PART 1: Structured Data & Objects

What is Structured Data?

Structured data organizes information into well-defined formats so programs can store, access, and manipulate it efficiently.

Key categories:

Type Description Ruby Example
Primitive Single values 42, "hello", true
Composite Groups of values Arrays, Hashes, Structs
Abstract Logical structure Stack, Queue, Tree

Structured data is the foundation of every data structure we study.


Objects – Attributes and Methods

An object bundles data (attributes) and behavior (methods) together.

class BankAccount
  # Attributes
  attr_accessor :owner, :balance

  def initialize(owner, balance = 0)
    @owner   = owner     # instance variable = attribute
    @balance = balance
  end

  # Methods (behavior)
  def deposit(amount)
    @balance += amount
  end

  def withdraw(amount)
    @balance -= amount if amount <= @balance
  end

  def to_s
    "#{@owner}: $#{@balance}"
  end
end

acct = BankAccount.new("Alice", 500)
acct.deposit(200)
puts acct   # => Alice: $700

Records and Fields

A record is a composite data structure with named fields — similar to a row in a database table.

In Ruby, use Struct for lightweight records:

# Define a record type with named fields
Student = Struct.new(:name, :id, :gpa)

# Create instances (records)
s1 = Student.new("Alice",  1001, 3.9)
s2 = Student.new("Bob",    1002, 3.4)
s3 = Student.new("Carol",  1003, 3.7)

puts s1.name  # => Alice
puts s1.gpa   # => 3.9

# Structs support iteration over fields
s1.each_pair { |field, val| puts "#{field}: #{val}" }
# name: Alice
# id: 1001
# gpa: 3.9

Key idea: Records map directly to objects with fixed attributes.


Memory Allocation

How does Ruby reserve space for data?

Stack Memory          Heap Memory
+------------+         +---------------------+
|  x = 5     | ------> |  Integer object: 5  |
|  s = "hi"  | ------> |  String object: "hi"|
|  arr = []  |-------> |  Array object: []   |
+------------+         +---------------------+
  Variables              Actual Objects
  (references)           (allocated data)
x   = 42          # stack holds reference; Fixnum on heap
str = "hello"     # stack ref → String object on heap
arr = [1, 2, 3]   # stack ref → Array object on heap

puts x.object_id    # unique heap address identifier
puts str.object_id
puts arr.object_id

In Ruby, almost everything lives on the heap. The stack holds references (pointers) to objects.


PART 2: Static vs Dynamic Structures

Static Memory Structures

Static structures have a fixed size determined at compile time or program start.

# Fixed-size array (static-style in concept)
DAYS_OF_WEEK = Array.new(7)   # allocated once, size = 7
DAYS_OF_WEEK[0] = "Monday"
DAYS_OF_WEEK[6] = "Sunday"

# Constants and fixed tables are classic static structures
PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29].freeze

# .freeze prevents modification — enforces static behavior
# PRIMES << 31  # => RuntimeError: can't modify frozen Array

Characteristics of static structures:

  • Size is fixed
  • Memory is predictable and fast to access
  • No overhead from memory management
  • Waste space if not fully used

Dynamic Structures – Heap Allocation

Dynamic structures grow and shrink at runtime on the heap.

# Ruby Array is fully dynamic
list = []               # starts empty

10.times { |i| list << i * 2 }   # grows automatically
puts list.inspect
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

list.delete_if { |x| x > 10 }    # shrinks automatically
puts list.inspect
# => [0, 2, 4, 6, 8, 10]

# Hash also grows dynamically
inventory = {}
inventory["apples"]  = 42
inventory["bananas"] = 17
inventory["mangoes"] = 5
puts inventory

Heap allocation means Ruby requests memory from the OS as needed and releases it when objects are no longer reachable.


Garbage Collection and Recycling

Ruby uses automatic garbage collection (GC) — you never call free().

# When an object loses all references, it becomes garbage
def create_garbage
  temp = "I am temporary"   # allocated on heap
  arr  = [1, 2, 3, 4, 5]   # allocated on heap
  # temp and arr go out of scope here → eligible for GC
end

create_garbage   # after this call, those objects are garbage

# Ruby uses a tri-color mark-and-sweep + generational GC
GC.start         # manually trigger (rarely needed)
puts GC.stat[:heap_live_slots]   # objects currently alive

How GC works: 1. Mark — trace all reachable objects from root references 2. Sweep — reclaim memory of unmarked (unreachable) objects 3. Compact (Ruby 3+) — defragment heap for better locality


PART 3: Linked Lists

Singly Linked Lists

Each node stores data + a pointer to the next node only.

class Node
  attr_accessor :data, :next_node
  def initialize(data)
    @data      = data
    @next_node = nil
  end
end

class SinglyLinkedList
  def initialize
    @head = nil
  end

  def prepend(data)
    node      = Node.new(data)
    node.next_node = @head
    @head     = node
  end

  def to_s
    result, cur = [], @head
    while cur
      result << cur.data
      cur = cur.next_node
    end
    result.join(" -> ") + " -> nil"
  end
end

list = SinglyLinkedList.new
[3, 2, 1].each { |v| list.prepend(v) }
puts list   # => 1 -> 2 -> 3 -> nil

Doubly Linked Lists

Each node has pointers to both next and previous nodes.

class DNode
  attr_accessor :data, :prev, :next_node
  def initialize(data)
    @data = data; @prev = nil; @next_node = nil
  end
end

class DoublyLinkedList
  def initialize
    @head = @tail = nil
  end

  def append(data)
    node = DNode.new(data)
    if @tail
      @tail.next_node = node
      node.prev       = @tail
    else
      @head = node
    end
    @tail = node
  end

  def traverse_backward
    cur = @tail
    while cur
      print "#{cur.data} "
      cur = cur.prev
    end
    puts
  end
end

dll = DoublyLinkedList.new
[10, 20, 30].each { |v| dll.append(v) }
dll.traverse_backward   # => 30 20 10

Linked List Operations

Insertion, Deletion, Traversal — the three core operations:

# INSERTION at position
def insert_after(target_data, new_data)
  cur = @head
  while cur
    if cur.data == target_data
      node           = Node.new(new_data)
      node.next_node = cur.next_node
      cur.next_node  = node
      return
    end
    cur = cur.next_node
  end
end

# DELETION by value  O(n)
def delete(data)
  return if @head.nil?
  if @head.data == data
    @head = @head.next_node; return
  end
  cur = @head
  while cur.next_node
    if cur.next_node.data == data
      cur.next_node = cur.next_node.next_node; return
    end
    cur = cur.next_node
  end
end

# TRAVERSAL  O(n)
def traverse
  cur = @head
  while cur
    print "#{cur.data} "
    cur = cur.next_node
  end
end

Arrays vs Hashes

# ARRAY — ordered, integer-indexed  O(1) access by index
fruits = ["apple", "banana", "cherry"]
puts fruits[0]          # => apple  (O(1))
puts fruits.length      # => 3
fruits.push("date")     # append   O(1) amortized

# HASH — key-value pairs, O(1) average lookup
ages = { "Alice" => 30, "Bob" => 25, "Carol" => 28 }
puts ages["Alice"]      # => 30   (O(1) avg)
ages["Dave"] = 22
ages.each { |name, age| puts "#{name} is #{age}" }
Feature Array Hash
Index type Integer (0-based) Any object
Access time O(1) by index O(1) avg by key
Order Maintained Maintained (Ruby 1.9+)
Use case Sequences Lookup tables / maps
Memory Compact Higher overhead

PART 4: Sorting Algorithms

Bubble Sort

Repeatedly swaps adjacent out-of-order elements. O(n²) — simple but slow.

def bubble_sort(arr)
  n = arr.length
  loop do
    swapped = false
    (n - 1).times do |i|
      if arr[i] > arr[i + 1]
        arr[i], arr[i + 1] = arr[i + 1], arr[i]   # swap
        swapped = true
      end
    end
    break unless swapped   # early exit if already sorted
  end
  arr
end

data = [64, 34, 25, 12, 22, 11, 90]
puts bubble_sort(data).inspect
# => [11, 12, 22, 25, 34, 64, 90]

Complexity: Time O(n²) best/worst | Space O(1)


Insertion Sort

Builds the sorted array one element at a time. O(n²) worst, O(n) best (nearly sorted).

def insertion_sort(arr)
  (1...arr.length).each do |i|
    key = arr[i]
    j   = i - 1
    while j >= 0 && arr[j] > key
      arr[j + 1] = arr[j]   # shift right
      j -= 1
    end
    arr[j + 1] = key          # insert
  end
  arr
end

data = [12, 11, 13, 5, 6]
puts insertion_sort(data).inspect
# => [5, 6, 11, 12, 13]

Best for: Small datasets or nearly sorted data.

Complexity: Time O(n²) worst | O(n) best | Space O(1)


Merge Sort

Divide-and-conquer. Split in half, sort each half, merge. O(n log n) guaranteed.

def merge_sort(arr)
  return arr if arr.length <= 1

  mid   = arr.length / 2
  left  = merge_sort(arr[0...mid])
  right = merge_sort(arr[mid..])

  merge(left, right)
end

def merge(left, right)
  result = []
  until left.empty? || right.empty?
    if left.first <= right.first
      result << left.shift
    else
      result << right.shift
    end
  end
  result + left + right
end

data = [38, 27, 43, 3, 9, 82, 10]
puts merge_sort(data).inspect
# => [3, 9, 10, 27, 38, 43, 82]

Quick Sort

Pick a pivot, partition around it, recurse. O(n log n) average, O(n²) worst.

def quicksort(arr)
  return arr if arr.length <= 1

  pivot = arr[arr.length / 2]         # choose middle as pivot
  left  = arr.select { |x| x < pivot }
  mid   = arr.select { |x| x == pivot }
  right = arr.select { |x| x > pivot }

  quicksort(left) + mid + quicksort(right)
end

data = [3, 6, 8, 10, 1, 2, 1]
puts quicksort(data).inspect
# => [1, 1, 2, 3, 6, 8, 10]
Algorithm Best Average Worst Space
Bubble O(n) O(n²) O(n²) O(1)
Insertion O(n) O(n²) O(n²) O(1)
Merge O(n log n) O(n log n) O(n log n) O(n)
Quick O(n log n) O(n log n) O(n²) O(log n)

PART 5: Stacks

Stacks – LIFO Structure

A stack is Last-In, First-Out. Think: stack of plates.

class Stack
  def initialize
    @data = []
  end

  def push(item)  = @data.push(item)
  def pop         = @data.pop
  def peek        = @data.last
  def empty?      = @data.empty?
  def size        = @data.size
  def to_s        = @data.inspect
end

s = Stack.new
s.push(10); s.push(20); s.push(30)
puts s          # => [10, 20, 30]
puts s.peek     # => 30  (top, not removed)
puts s.pop      # => 30  (removed)
puts s          # => [10, 20]

Use cases: Order reversal, undo operations, function call frames, expression evaluation.


Stacks – Order Reversal & Functions

Stacks reverse order and manage function call frames:

# Order reversal using a stack
def reverse_string(str)
  stack = Stack.new
  str.each_char { |c| stack.push(c) }
  result = ""
  result += stack.pop until stack.empty?
  result
end
puts reverse_string("hello")  # => "olleh"

# How the call stack works during function calls:
# Each call pushes a "frame" with local vars + return address
def factorial(n)          # frame pushed
  return 1 if n <= 1
  n * factorial(n - 1)    # new frame pushed recursively
end                       # frame popped on return

puts factorial(5)   # => 120
# Call stack at deepest point:
# [factorial(1), factorial(2), factorial(3), factorial(4), factorial(5)]

Recursion

Recursion uses the call stack implicitly — each call adds a new stack frame.

# Every recursive solution needs:
# 1. BASE CASE  — stops recursion
# 2. RECURSIVE CASE — moves toward base case

# Fibonacci with recursion
def fib(n)
  return n if n <= 1          # base case
  fib(n - 1) + fib(n - 2)    # recursive case
end

puts (0..7).map { |i| fib(i) }.inspect
# => [0, 1, 1, 2, 3, 5, 8, 13]

# Tower of Hanoi — classic recursion example
def hanoi(n, from, to, via)
  if n == 1
    puts "Move disk 1: #{from} → #{to}"
    return
  end
  hanoi(n - 1, from, via, to)
  puts "Move disk #{n}: #{from} → #{to}"
  hanoi(n - 1, via, to, from)
end

hanoi(3, "A", "C", "B")

PART 6: Queues

Queues – FIFO Structure

A queue is First-In, First-Out. Think: checkout line.

class Queue
  def initialize
    @data = []
  end

  def enqueue(item) = @data.push(item)      # add to back
  def dequeue       = @data.shift           # remove from front
  def front         = @data.first
  def empty?        = @data.empty?
  def size          = @data.size
  def to_s          = @data.inspect
end

q = Queue.new
q.enqueue("Alice"); q.enqueue("Bob"); q.enqueue("Carol")
puts q            # => ["Alice", "Bob", "Carol"]
puts q.dequeue    # => "Alice"
puts q            # => ["Bob", "Carol"]

Use cases: Task scheduling, BFS traversal, print spoolers, buffering.


Circular Queue

A circular queue reuses empty slots by wrapping the tail pointer around.

class CircularQueue
  def initialize(capacity)
    @cap   = capacity
    @data  = Array.new(capacity)
    @head  = 0
    @tail  = 0
    @size  = 0
  end

  def enqueue(item)
    raise "Queue full" if @size == @cap
    @data[@tail] = item
    @tail = (@tail + 1) % @cap   # wrap around!
    @size += 1
  end

  def dequeue
    raise "Queue empty" if @size == 0
    item  = @data[@head]
    @head = (@head + 1) % @cap   # wrap around!
    @size -= 1
    item
  end
end

cq = CircularQueue.new(3)
cq.enqueue(1); cq.enqueue(2); cq.enqueue(3)
puts cq.dequeue   # => 1
cq.enqueue(4)     # reuses slot 0

Priority Queue

Elements are dequeued in priority order, not arrival order.

class PriorityQueue
  def initialize
    @data = []   # store [priority, item] pairs
  end

  def enqueue(item, priority)
    @data << [priority, item]
    @data.sort_by! { |p, _| -p }  # highest priority first
  end

  def dequeue
    @data.shift&.last
  end

  def peek
    @data.first&.last
  end
end

pq = PriorityQueue.new
pq.enqueue("low task",      1)
pq.enqueue("critical task", 10)
pq.enqueue("normal task",   5)

puts pq.dequeue   # => "critical task"   (priority 10)
puts pq.dequeue   # => "normal task"     (priority 5)
puts pq.dequeue   # => "low task"        (priority 1)

PART 7: Trees

Binary Trees

A binary tree has nodes with at most two children (left and right).

class TreeNode
  attr_accessor :val, :left, :right
  def initialize(val)
    @val = val; @left = nil; @right = nil
  end
end

# Build a Binary Search Tree (BST)
class BST
  def insert(root, val)
    return TreeNode.new(val) if root.nil?
    if val < root.val
      root.left  = insert(root.left,  val)
    else
      root.right = insert(root.right, val)
    end
    root
  end

  def inorder(root)   # Left → Root → Right (sorted output)
    return if root.nil?
    inorder(root.left)
    print "#{root.val} "
    inorder(root.right)
  end
end

bst  = BST.new
root = nil
[5, 3, 7, 1, 4, 6, 8].each { |v| root = bst.insert(root, v) }
bst.inorder(root)   # => 1 3 4 5 6 7 8

Math Expression Tree

Expression trees represent mathematical expressions structurally.

class ExprNode
  attr_accessor :val, :left, :right
  def initialize(val, left = nil, right = nil)
    @val = val; @left = left; @right = right
  end
end

# Tree for: (3 + 4) * (5 - 2)
#        *
#       / \
#      +   -
#     / \ / \
#    3  4 5  2

tree = ExprNode.new("*",
         ExprNode.new("+", ExprNode.new(3), ExprNode.new(4)),
         ExprNode.new("-", ExprNode.new(5), ExprNode.new(2)))

def evaluate(node)
  return node.val if node.left.nil?   # leaf = number
  l = evaluate(node.left)
  r = evaluate(node.right)
  case node.val
  when "+" then l + r
  when "-" then l - r
  when "*" then l * r
  when "/" then l / r.to_f
  end
end

puts evaluate(tree)   # => 21  [ (3+4)*(5-2) = 7*3 ]

B+ Trees

B+ Trees are balanced multi-way trees used in databases and file systems.

Structure of a B+ Tree (order 3):

Internal nodes: [ 10 | 20 ]
               /     |      \
         [5|7]    [12|17]   [22|30]
          ↓          ↓          ↓
       Leaf nodes (linked) → faster range queries

Key properties:
  • All data lives in LEAF nodes
  • Internal nodes are index-only
  • Leaf nodes are linked (→) for range scans
  • Guaranteed O(log n) search, insert, delete
# Conceptual B+ Tree search in Ruby
class BPlusNode
  attr_accessor :keys, :children, :is_leaf, :next_leaf
  def initialize(is_leaf = false)
    @keys = []; @children = []; @is_leaf = is_leaf
    @next_leaf = nil
  end
end

# B+ Trees are used in: MySQL InnoDB, PostgreSQL indexes,
# file systems (NTFS, ext4), and many NoSQL stores.
puts "B+ Tree ensures O(log n) operations regardless of size"

PART 8: Graphs

Graphs – Mazes

A graph is a set of nodes (vertices) connected by edges. Mazes are graphs!

# Represent a maze as an adjacency list
maze = {
  "start" => ["A", "B"],
  "A"     => ["C"],
  "B"     => ["C", "D"],
  "C"     => ["end"],
  "D"     => ["end"],
  "end"   => []
}

# BFS to find shortest path through maze
def bfs(graph, start, goal)
  queue   = [[start, [start]]]
  visited = {}
  until queue.empty?
    node, path = queue.shift
    return path if node == goal
    next if visited[node]
    visited[node] = true
    graph[node].each { |n| queue << [n, path + [n]] }
  end
  nil
end

path = bfs(maze, "start", "end")
puts path.join(" → ")   # => start → A → C → end

Transition Matrix (Adjacency Matrix)

A transition matrix represents graph edges as a 2D grid.

# Graph:  A→B, A→C, B→C, C→D
#         0=A, 1=B, 2=C, 3=D
matrix = [
  [0, 1, 1, 0],   # A connects to B, C
  [0, 0, 1, 0],   # B connects to C
  [0, 0, 0, 1],   # C connects to D
  [0, 0, 0, 0]    # D: no outgoing edges
]

LABELS = %w[A B C D]

# Print adjacency
matrix.each_with_index do |row, i|
  row.each_with_index do |val, j|
    puts "#{LABELS[i]} → #{LABELS[j]}" if val == 1
  end
end

# Check if path A→C exists: matrix[0][2]
puts "A→C edge exists? #{matrix[0][2] == 1}"   # => true
# Check if path B→A exists: matrix[1][0]
puts "B→A edge exists? #{matrix[1][0] == 1}"   # => false

Dijkstra’s Algorithm

Finds the shortest path in a weighted graph with non-negative weights.

def dijkstra(graph, start)
  dist    = Hash.new(Float::INFINITY)
  dist[start] = 0
  visited = {}
  pq      = [[0, start]]   # [cost, node]

  until pq.empty?
    cost, node = pq.min_by { |c, _| c }
    pq.delete([cost, node])
    next if visited[node]
    visited[node] = true

    (graph[node] || []).each do |neighbor, weight|
      new_cost = cost + weight
      if new_cost < dist[neighbor]
        dist[neighbor] = new_cost
        pq << [new_cost, neighbor]
      end
    end
  end
  dist
end

graph = {
  "A" => [["B", 1], ["C", 4]],
  "B" => [["C", 2], ["D", 5]],
  "C" => [["D", 1]],
  "D" => []
}
puts dijkstra(graph, "A").inspect
# => {"A"=>0, "B"=>1, "C"=>3, "D"=>4}

Flow Analysis (Max-Flow)

Flow networks model capacity-constrained systems (networks, pipelines, traffic).

# Ford-Fulkerson Max-Flow (BFS-based Edmonds-Karp)
def bfs_path(capacity, source, sink, parent)
  visited = { source => true }
  queue   = [source]
  until queue.empty?
    u = queue.shift
    capacity[u]&.each do |v, cap|
      if !visited[v] && cap > 0
        visited[v] = true
        parent[v]  = u
        return true if v == sink
        queue << v
      end
    end
  end
  false
end

# Key concepts for flow analysis:
# • Source node (supply) → Sink node (demand)
# • Each edge has a capacity constraint
# • Max-Flow = Min-Cut (Ford-Fulkerson theorem)
# • Applications: network routing, matching, scheduling

puts "Max-Flow applications:"
puts "  • Internet traffic routing"
puts "  • Supply chain optimization"
puts "  • Bipartite matching (job assignments)"
puts "  • Image segmentation"

EXAM SUMMARY

Quick Reference – Complexity Cheat Sheet

Structure / Algorithm Access Search Insert Delete Space
Array (static) O(1) O(n) O(n) O(n) O(n)
Hash / Dictionary O(1) O(1) O(1) O(1) O(n)
Singly Linked List O(n) O(n) O(1)† O(1)† O(n)
Doubly Linked List O(n) O(n) O(1)† O(1)† O(n)
Stack (push/pop) O(n) O(n) O(1) O(1) O(n)
Queue (enq/deq) O(n) O(n) O(1) O(1) O(n)
BST (balanced) O(log n) O(log n) O(log n) O(log n) O(n)
B+ Tree O(log n) O(log n) O(log n) O(log n) O(n)
Bubble Sort O(1) worst O(n²)
Merge Sort O(n) always O(n log n)
Quick Sort O(log n) avg O(n log n)

† With direct reference to node


Key Concepts to Remember

Memory: - Stack = references/primitives | Heap = objects - GC marks reachable objects, sweeps the rest

Lists: Singly (forward only) vs Doubly (forward + backward)

Trees: - BST: left < root < right → inorder gives sorted output - B+ Tree: all data in leaves, leaves linked for range queries - Expression Tree: post-order traversal = evaluate

Graphs: - Adjacency List: sparse graphs (less memory) - Adjacency Matrix: dense graphs (O(1) edge lookup) - Dijkstra: greedy shortest path (non-negative weights only)

Stacks: LIFO → reversal, recursion, function calls

Queues: FIFO → BFS, scheduling | Circular → reuse space | Priority → ordered service

Good luck on your final exam!

LS0tDQp0aXRsZTogIkRhdGEgU3RydWN0dXJlcyAmIEFsZ29yaXRobXMiDQphdXRob3I6ICJSIEJhdHppbmdlciINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgb3V0cHV0OiBodG1sX25vdGVib29rDQpsaW5rLWNpdGF0aW9uczogVFJVRQ0Kc3VidGl0bGU6IENvbXByZWhlbnNpdmUgRmluYWwgRXhhbSBSZXZpZXcNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQotLS0NCg0KIyBQQVJUIDE6IFN0cnVjdHVyZWQgRGF0YSAmIE9iamVjdHMgey5zZWN0aW9uLXRpdGxlfQ0KDQojIyBXaGF0IGlzIFN0cnVjdHVyZWQgRGF0YT8NCg0KKipTdHJ1Y3R1cmVkIGRhdGEqKiBvcmdhbml6ZXMgaW5mb3JtYXRpb24gaW50byB3ZWxsLWRlZmluZWQgZm9ybWF0cyBzbyBwcm9ncmFtcyBjYW4gc3RvcmUsIGFjY2VzcywgYW5kIG1hbmlwdWxhdGUgaXQgZWZmaWNpZW50bHkuDQoNCktleSBjYXRlZ29yaWVzOg0KDQp8IFR5cGUgfCBEZXNjcmlwdGlvbiB8IFJ1YnkgRXhhbXBsZSB8DQp8LS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS18DQp8ICoqUHJpbWl0aXZlKiogfCBTaW5nbGUgdmFsdWVzIHwgYDQyYCwgYCJoZWxsbyJgLCBgdHJ1ZWAgfA0KfCAqKkNvbXBvc2l0ZSoqIHwgR3JvdXBzIG9mIHZhbHVlcyB8IEFycmF5cywgSGFzaGVzLCBTdHJ1Y3RzIHwNCnwgKipBYnN0cmFjdCoqIHwgTG9naWNhbCBzdHJ1Y3R1cmUgfCBTdGFjaywgUXVldWUsIFRyZWUgfA0KDQo+IFN0cnVjdHVyZWQgZGF0YSBpcyB0aGUgZm91bmRhdGlvbiBvZiBldmVyeSBkYXRhIHN0cnVjdHVyZSB3ZSBzdHVkeS4NCg0KLS0tDQoNCiMjIE9iamVjdHMg4oCTIEF0dHJpYnV0ZXMgYW5kIE1ldGhvZHMNCg0KQW4gKipvYmplY3QqKiBidW5kbGVzICoqZGF0YSoqIChhdHRyaWJ1dGVzKSBhbmQgKipiZWhhdmlvcioqIChtZXRob2RzKSB0b2dldGhlci4NCg0KYGBgcnVieQ0KY2xhc3MgQmFua0FjY291bnQNCiAgIyBBdHRyaWJ1dGVzDQogIGF0dHJfYWNjZXNzb3IgOm93bmVyLCA6YmFsYW5jZQ0KDQogIGRlZiBpbml0aWFsaXplKG93bmVyLCBiYWxhbmNlID0gMCkNCiAgICBAb3duZXIgICA9IG93bmVyICAgICAjIGluc3RhbmNlIHZhcmlhYmxlID0gYXR0cmlidXRlDQogICAgQGJhbGFuY2UgPSBiYWxhbmNlDQogIGVuZA0KDQogICMgTWV0aG9kcyAoYmVoYXZpb3IpDQogIGRlZiBkZXBvc2l0KGFtb3VudCkNCiAgICBAYmFsYW5jZSArPSBhbW91bnQNCiAgZW5kDQoNCiAgZGVmIHdpdGhkcmF3KGFtb3VudCkNCiAgICBAYmFsYW5jZSAtPSBhbW91bnQgaWYgYW1vdW50IDw9IEBiYWxhbmNlDQogIGVuZA0KDQogIGRlZiB0b19zDQogICAgIiN7QG93bmVyfTogJCN7QGJhbGFuY2V9Ig0KICBlbmQNCmVuZA0KDQphY2N0ID0gQmFua0FjY291bnQubmV3KCJBbGljZSIsIDUwMCkNCmFjY3QuZGVwb3NpdCgyMDApDQpwdXRzIGFjY3QgICAjID0+IEFsaWNlOiAkNzAwDQpgYGANCg0KLS0tDQoNCiMjIFJlY29yZHMgYW5kIEZpZWxkcw0KDQpBICoqcmVjb3JkKiogaXMgYSBjb21wb3NpdGUgZGF0YSBzdHJ1Y3R1cmUgd2l0aCBuYW1lZCAqKmZpZWxkcyoqIOKAlCBzaW1pbGFyIHRvIGEgcm93IGluIGEgZGF0YWJhc2UgdGFibGUuDQoNCkluIFJ1YnksIHVzZSBgU3RydWN0YCBmb3IgbGlnaHR3ZWlnaHQgcmVjb3JkczoNCg0KYGBgcnVieQ0KIyBEZWZpbmUgYSByZWNvcmQgdHlwZSB3aXRoIG5hbWVkIGZpZWxkcw0KU3R1ZGVudCA9IFN0cnVjdC5uZXcoOm5hbWUsIDppZCwgOmdwYSkNCg0KIyBDcmVhdGUgaW5zdGFuY2VzIChyZWNvcmRzKQ0KczEgPSBTdHVkZW50Lm5ldygiQWxpY2UiLCAgMTAwMSwgMy45KQ0KczIgPSBTdHVkZW50Lm5ldygiQm9iIiwgICAgMTAwMiwgMy40KQ0KczMgPSBTdHVkZW50Lm5ldygiQ2Fyb2wiLCAgMTAwMywgMy43KQ0KDQpwdXRzIHMxLm5hbWUgICMgPT4gQWxpY2UNCnB1dHMgczEuZ3BhICAgIyA9PiAzLjkNCg0KIyBTdHJ1Y3RzIHN1cHBvcnQgaXRlcmF0aW9uIG92ZXIgZmllbGRzDQpzMS5lYWNoX3BhaXIgeyB8ZmllbGQsIHZhbHwgcHV0cyAiI3tmaWVsZH06ICN7dmFsfSIgfQ0KIyBuYW1lOiBBbGljZQ0KIyBpZDogMTAwMQ0KIyBncGE6IDMuOQ0KYGBgDQoNCj4gKipLZXkgaWRlYToqKiBSZWNvcmRzIG1hcCBkaXJlY3RseSB0byBvYmplY3RzIHdpdGggZml4ZWQgYXR0cmlidXRlcy4NCg0KLS0tDQoNCiMjIE1lbW9yeSBBbGxvY2F0aW9uDQoNCioqSG93IGRvZXMgUnVieSByZXNlcnZlIHNwYWNlIGZvciBkYXRhPyoqDQoNCmBgYA0KU3RhY2sgTWVtb3J5ICAgICAgICAgIEhlYXAgTWVtb3J5DQorLS0tLS0tLS0tLS0tKyAgICAgICAgICstLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICB4ID0gNSAgICAgfCAtLS0tLS0+IHwgIEludGVnZXIgb2JqZWN0OiA1ICB8DQp8ICBzID0gImhpIiAgfCAtLS0tLS0+IHwgIFN0cmluZyBvYmplY3Q6ICJoaSJ8DQp8ICBhcnIgPSBbXSAgfC0tLS0tLS0+IHwgIEFycmF5IG9iamVjdDogW10gICB8DQorLS0tLS0tLS0tLS0tKyAgICAgICAgICstLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQogIFZhcmlhYmxlcyAgICAgICAgICAgICAgQWN0dWFsIE9iamVjdHMNCiAgKHJlZmVyZW5jZXMpICAgICAgICAgICAoYWxsb2NhdGVkIGRhdGEpDQpgYGANCg0KYGBgcnVieQ0KeCAgID0gNDIgICAgICAgICAgIyBzdGFjayBob2xkcyByZWZlcmVuY2U7IEZpeG51bSBvbiBoZWFwDQpzdHIgPSAiaGVsbG8iICAgICAjIHN0YWNrIHJlZiDihpIgU3RyaW5nIG9iamVjdCBvbiBoZWFwDQphcnIgPSBbMSwgMiwgM10gICAjIHN0YWNrIHJlZiDihpIgQXJyYXkgb2JqZWN0IG9uIGhlYXANCg0KcHV0cyB4Lm9iamVjdF9pZCAgICAjIHVuaXF1ZSBoZWFwIGFkZHJlc3MgaWRlbnRpZmllcg0KcHV0cyBzdHIub2JqZWN0X2lkDQpwdXRzIGFyci5vYmplY3RfaWQNCmBgYA0KDQo+IEluIFJ1YnksICoqYWxtb3N0IGV2ZXJ5dGhpbmcgbGl2ZXMgb24gdGhlIGhlYXAqKi4gVGhlIHN0YWNrIGhvbGRzICpyZWZlcmVuY2VzKiAocG9pbnRlcnMpIHRvIG9iamVjdHMuDQoNCi0tLQ0KDQojIFBBUlQgMjogU3RhdGljIHZzIER5bmFtaWMgU3RydWN0dXJlcyB7LnNlY3Rpb24tdGl0bGV9DQoNCiMjIFN0YXRpYyBNZW1vcnkgU3RydWN0dXJlcw0KDQoqKlN0YXRpYyoqIHN0cnVjdHVyZXMgaGF2ZSBhICoqZml4ZWQgc2l6ZSoqIGRldGVybWluZWQgYXQgY29tcGlsZSB0aW1lIG9yIHByb2dyYW0gc3RhcnQuDQoNCmBgYHJ1YnkNCiMgRml4ZWQtc2l6ZSBhcnJheSAoc3RhdGljLXN0eWxlIGluIGNvbmNlcHQpDQpEQVlTX09GX1dFRUsgPSBBcnJheS5uZXcoNykgICAjIGFsbG9jYXRlZCBvbmNlLCBzaXplID0gNw0KREFZU19PRl9XRUVLWzBdID0gIk1vbmRheSINCkRBWVNfT0ZfV0VFS1s2XSA9ICJTdW5kYXkiDQoNCiMgQ29uc3RhbnRzIGFuZCBmaXhlZCB0YWJsZXMgYXJlIGNsYXNzaWMgc3RhdGljIHN0cnVjdHVyZXMNClBSSU1FUyA9IFsyLCAzLCA1LCA3LCAxMSwgMTMsIDE3LCAxOSwgMjMsIDI5XS5mcmVlemUNCg0KIyAuZnJlZXplIHByZXZlbnRzIG1vZGlmaWNhdGlvbiDigJQgZW5mb3JjZXMgc3RhdGljIGJlaGF2aW9yDQojIFBSSU1FUyA8PCAzMSAgIyA9PiBSdW50aW1lRXJyb3I6IGNhbid0IG1vZGlmeSBmcm96ZW4gQXJyYXkNCmBgYA0KDQoqKkNoYXJhY3RlcmlzdGljcyBvZiBzdGF0aWMgc3RydWN0dXJlczoqKg0KDQotIFNpemUgaXMgZml4ZWQNCi0gTWVtb3J5IGlzIHByZWRpY3RhYmxlIGFuZCBmYXN0IHRvIGFjY2Vzcw0KLSBObyBvdmVyaGVhZCBmcm9tIG1lbW9yeSBtYW5hZ2VtZW50DQotIFdhc3RlIHNwYWNlIGlmIG5vdCBmdWxseSB1c2VkDQoNCi0tLQ0KDQojIyBEeW5hbWljIFN0cnVjdHVyZXMg4oCTIEhlYXAgQWxsb2NhdGlvbg0KDQoqKkR5bmFtaWMqKiBzdHJ1Y3R1cmVzIGdyb3cgYW5kIHNocmluayBhdCAqKnJ1bnRpbWUqKiBvbiB0aGUgKipoZWFwKiouDQoNCmBgYHJ1YnkNCiMgUnVieSBBcnJheSBpcyBmdWxseSBkeW5hbWljDQpsaXN0ID0gW10gICAgICAgICAgICAgICAjIHN0YXJ0cyBlbXB0eQ0KDQoxMC50aW1lcyB7IHxpfCBsaXN0IDw8IGkgKiAyIH0gICAjIGdyb3dzIGF1dG9tYXRpY2FsbHkNCnB1dHMgbGlzdC5pbnNwZWN0DQojID0+IFswLCAyLCA0LCA2LCA4LCAxMCwgMTIsIDE0LCAxNiwgMThdDQoNCmxpc3QuZGVsZXRlX2lmIHsgfHh8IHggPiAxMCB9ICAgICMgc2hyaW5rcyBhdXRvbWF0aWNhbGx5DQpwdXRzIGxpc3QuaW5zcGVjdA0KIyA9PiBbMCwgMiwgNCwgNiwgOCwgMTBdDQoNCiMgSGFzaCBhbHNvIGdyb3dzIGR5bmFtaWNhbGx5DQppbnZlbnRvcnkgPSB7fQ0KaW52ZW50b3J5WyJhcHBsZXMiXSAgPSA0Mg0KaW52ZW50b3J5WyJiYW5hbmFzIl0gPSAxNw0KaW52ZW50b3J5WyJtYW5nb2VzIl0gPSA1DQpwdXRzIGludmVudG9yeQ0KYGBgDQoNCj4gKipIZWFwIGFsbG9jYXRpb24qKiBtZWFucyBSdWJ5IHJlcXVlc3RzIG1lbW9yeSBmcm9tIHRoZSBPUyBhcyBuZWVkZWQgYW5kIHJlbGVhc2VzIGl0IHdoZW4gb2JqZWN0cyBhcmUgbm8gbG9uZ2VyIHJlYWNoYWJsZS4NCg0KLS0tDQoNCiMjIEdhcmJhZ2UgQ29sbGVjdGlvbiBhbmQgUmVjeWNsaW5nDQoNClJ1YnkgdXNlcyAqKmF1dG9tYXRpYyBnYXJiYWdlIGNvbGxlY3Rpb24gKEdDKSoqIOKAlCB5b3UgbmV2ZXIgY2FsbCBgZnJlZSgpYC4NCg0KYGBgcnVieQ0KIyBXaGVuIGFuIG9iamVjdCBsb3NlcyBhbGwgcmVmZXJlbmNlcywgaXQgYmVjb21lcyBnYXJiYWdlDQpkZWYgY3JlYXRlX2dhcmJhZ2UNCiAgdGVtcCA9ICJJIGFtIHRlbXBvcmFyeSIgICAjIGFsbG9jYXRlZCBvbiBoZWFwDQogIGFyciAgPSBbMSwgMiwgMywgNCwgNV0gICAjIGFsbG9jYXRlZCBvbiBoZWFwDQogICMgdGVtcCBhbmQgYXJyIGdvIG91dCBvZiBzY29wZSBoZXJlIOKGkiBlbGlnaWJsZSBmb3IgR0MNCmVuZA0KDQpjcmVhdGVfZ2FyYmFnZSAgICMgYWZ0ZXIgdGhpcyBjYWxsLCB0aG9zZSBvYmplY3RzIGFyZSBnYXJiYWdlDQoNCiMgUnVieSB1c2VzIGEgdHJpLWNvbG9yIG1hcmstYW5kLXN3ZWVwICsgZ2VuZXJhdGlvbmFsIEdDDQpHQy5zdGFydCAgICAgICAgICMgbWFudWFsbHkgdHJpZ2dlciAocmFyZWx5IG5lZWRlZCkNCnB1dHMgR0Muc3RhdFs6aGVhcF9saXZlX3Nsb3RzXSAgICMgb2JqZWN0cyBjdXJyZW50bHkgYWxpdmUNCmBgYA0KDQoqKkhvdyBHQyB3b3JrczoqKg0KMS4gKipNYXJrKiog4oCUIHRyYWNlIGFsbCByZWFjaGFibGUgb2JqZWN0cyBmcm9tIHJvb3QgcmVmZXJlbmNlcw0KMi4gKipTd2VlcCoqIOKAlCByZWNsYWltIG1lbW9yeSBvZiB1bm1hcmtlZCAodW5yZWFjaGFibGUpIG9iamVjdHMNCjMuICoqQ29tcGFjdCoqICooUnVieSAzKykqIOKAlCBkZWZyYWdtZW50IGhlYXAgZm9yIGJldHRlciBsb2NhbGl0eQ0KDQotLS0NCg0KIyBQQVJUIDM6IExpbmtlZCBMaXN0cyB7LnNlY3Rpb24tdGl0bGV9DQoNCiMjIFNpbmdseSBMaW5rZWQgTGlzdHMNCg0KRWFjaCAqKm5vZGUqKiBzdG9yZXMgZGF0YSArIGEgcG9pbnRlciB0byB0aGUgKipuZXh0Kiogbm9kZSBvbmx5Lg0KDQpgYGBydWJ5DQpjbGFzcyBOb2RlDQogIGF0dHJfYWNjZXNzb3IgOmRhdGEsIDpuZXh0X25vZGUNCiAgZGVmIGluaXRpYWxpemUoZGF0YSkNCiAgICBAZGF0YSAgICAgID0gZGF0YQ0KICAgIEBuZXh0X25vZGUgPSBuaWwNCiAgZW5kDQplbmQNCg0KY2xhc3MgU2luZ2x5TGlua2VkTGlzdA0KICBkZWYgaW5pdGlhbGl6ZQ0KICAgIEBoZWFkID0gbmlsDQogIGVuZA0KDQogIGRlZiBwcmVwZW5kKGRhdGEpDQogICAgbm9kZSAgICAgID0gTm9kZS5uZXcoZGF0YSkNCiAgICBub2RlLm5leHRfbm9kZSA9IEBoZWFkDQogICAgQGhlYWQgICAgID0gbm9kZQ0KICBlbmQNCg0KICBkZWYgdG9fcw0KICAgIHJlc3VsdCwgY3VyID0gW10sIEBoZWFkDQogICAgd2hpbGUgY3VyDQogICAgICByZXN1bHQgPDwgY3VyLmRhdGENCiAgICAgIGN1ciA9IGN1ci5uZXh0X25vZGUNCiAgICBlbmQNCiAgICByZXN1bHQuam9pbigiIC0+ICIpICsgIiAtPiBuaWwiDQogIGVuZA0KZW5kDQoNCmxpc3QgPSBTaW5nbHlMaW5rZWRMaXN0Lm5ldw0KWzMsIDIsIDFdLmVhY2ggeyB8dnwgbGlzdC5wcmVwZW5kKHYpIH0NCnB1dHMgbGlzdCAgICMgPT4gMSAtPiAyIC0+IDMgLT4gbmlsDQpgYGANCg0KLS0tDQoNCiMjIERvdWJseSBMaW5rZWQgTGlzdHMNCg0KRWFjaCBub2RlIGhhcyBwb2ludGVycyB0byAqKmJvdGgqKiBuZXh0IGFuZCBwcmV2aW91cyBub2Rlcy4NCg0KYGBgcnVieQ0KY2xhc3MgRE5vZGUNCiAgYXR0cl9hY2Nlc3NvciA6ZGF0YSwgOnByZXYsIDpuZXh0X25vZGUNCiAgZGVmIGluaXRpYWxpemUoZGF0YSkNCiAgICBAZGF0YSA9IGRhdGE7IEBwcmV2ID0gbmlsOyBAbmV4dF9ub2RlID0gbmlsDQogIGVuZA0KZW5kDQoNCmNsYXNzIERvdWJseUxpbmtlZExpc3QNCiAgZGVmIGluaXRpYWxpemUNCiAgICBAaGVhZCA9IEB0YWlsID0gbmlsDQogIGVuZA0KDQogIGRlZiBhcHBlbmQoZGF0YSkNCiAgICBub2RlID0gRE5vZGUubmV3KGRhdGEpDQogICAgaWYgQHRhaWwNCiAgICAgIEB0YWlsLm5leHRfbm9kZSA9IG5vZGUNCiAgICAgIG5vZGUucHJldiAgICAgICA9IEB0YWlsDQogICAgZWxzZQ0KICAgICAgQGhlYWQgPSBub2RlDQogICAgZW5kDQogICAgQHRhaWwgPSBub2RlDQogIGVuZA0KDQogIGRlZiB0cmF2ZXJzZV9iYWNrd2FyZA0KICAgIGN1ciA9IEB0YWlsDQogICAgd2hpbGUgY3VyDQogICAgICBwcmludCAiI3tjdXIuZGF0YX0gIg0KICAgICAgY3VyID0gY3VyLnByZXYNCiAgICBlbmQNCiAgICBwdXRzDQogIGVuZA0KZW5kDQoNCmRsbCA9IERvdWJseUxpbmtlZExpc3QubmV3DQpbMTAsIDIwLCAzMF0uZWFjaCB7IHx2fCBkbGwuYXBwZW5kKHYpIH0NCmRsbC50cmF2ZXJzZV9iYWNrd2FyZCAgICMgPT4gMzAgMjAgMTANCmBgYA0KDQotLS0NCg0KIyMgTGlua2VkIExpc3QgT3BlcmF0aW9ucw0KDQoqKkluc2VydGlvbiwgRGVsZXRpb24sIFRyYXZlcnNhbCoqIOKAlCB0aGUgdGhyZWUgY29yZSBvcGVyYXRpb25zOg0KDQpgYGBydWJ5DQojIElOU0VSVElPTiBhdCBwb3NpdGlvbg0KZGVmIGluc2VydF9hZnRlcih0YXJnZXRfZGF0YSwgbmV3X2RhdGEpDQogIGN1ciA9IEBoZWFkDQogIHdoaWxlIGN1cg0KICAgIGlmIGN1ci5kYXRhID09IHRhcmdldF9kYXRhDQogICAgICBub2RlICAgICAgICAgICA9IE5vZGUubmV3KG5ld19kYXRhKQ0KICAgICAgbm9kZS5uZXh0X25vZGUgPSBjdXIubmV4dF9ub2RlDQogICAgICBjdXIubmV4dF9ub2RlICA9IG5vZGUNCiAgICAgIHJldHVybg0KICAgIGVuZA0KICAgIGN1ciA9IGN1ci5uZXh0X25vZGUNCiAgZW5kDQplbmQNCg0KIyBERUxFVElPTiBieSB2YWx1ZSAgTyhuKQ0KZGVmIGRlbGV0ZShkYXRhKQ0KICByZXR1cm4gaWYgQGhlYWQubmlsPw0KICBpZiBAaGVhZC5kYXRhID09IGRhdGENCiAgICBAaGVhZCA9IEBoZWFkLm5leHRfbm9kZTsgcmV0dXJuDQogIGVuZA0KICBjdXIgPSBAaGVhZA0KICB3aGlsZSBjdXIubmV4dF9ub2RlDQogICAgaWYgY3VyLm5leHRfbm9kZS5kYXRhID09IGRhdGENCiAgICAgIGN1ci5uZXh0X25vZGUgPSBjdXIubmV4dF9ub2RlLm5leHRfbm9kZTsgcmV0dXJuDQogICAgZW5kDQogICAgY3VyID0gY3VyLm5leHRfbm9kZQ0KICBlbmQNCmVuZA0KDQojIFRSQVZFUlNBTCAgTyhuKQ0KZGVmIHRyYXZlcnNlDQogIGN1ciA9IEBoZWFkDQogIHdoaWxlIGN1cg0KICAgIHByaW50ICIje2N1ci5kYXRhfSAiDQogICAgY3VyID0gY3VyLm5leHRfbm9kZQ0KICBlbmQNCmVuZA0KYGBgDQoNCi0tLQ0KDQojIyBBcnJheXMgdnMgSGFzaGVzDQoNCmBgYHJ1YnkNCiMgQVJSQVkg4oCUIG9yZGVyZWQsIGludGVnZXItaW5kZXhlZCAgTygxKSBhY2Nlc3MgYnkgaW5kZXgNCmZydWl0cyA9IFsiYXBwbGUiLCAiYmFuYW5hIiwgImNoZXJyeSJdDQpwdXRzIGZydWl0c1swXSAgICAgICAgICAjID0+IGFwcGxlICAoTygxKSkNCnB1dHMgZnJ1aXRzLmxlbmd0aCAgICAgICMgPT4gMw0KZnJ1aXRzLnB1c2goImRhdGUiKSAgICAgIyBhcHBlbmQgICBPKDEpIGFtb3J0aXplZA0KDQojIEhBU0gg4oCUIGtleS12YWx1ZSBwYWlycywgTygxKSBhdmVyYWdlIGxvb2t1cA0KYWdlcyA9IHsgIkFsaWNlIiA9PiAzMCwgIkJvYiIgPT4gMjUsICJDYXJvbCIgPT4gMjggfQ0KcHV0cyBhZ2VzWyJBbGljZSJdICAgICAgIyA9PiAzMCAgIChPKDEpIGF2ZykNCmFnZXNbIkRhdmUiXSA9IDIyDQphZ2VzLmVhY2ggeyB8bmFtZSwgYWdlfCBwdXRzICIje25hbWV9IGlzICN7YWdlfSIgfQ0KYGBgDQoNCnwgRmVhdHVyZSB8IEFycmF5IHwgSGFzaCB8DQp8LS0tLS0tLS0tfC0tLS0tLS18LS0tLS0tfA0KfCBJbmRleCB0eXBlIHwgSW50ZWdlciAoMC1iYXNlZCkgfCBBbnkgb2JqZWN0IHwNCnwgQWNjZXNzIHRpbWUgfCBPKDEpIGJ5IGluZGV4IHwgTygxKSBhdmcgYnkga2V5IHwNCnwgT3JkZXIgfCBNYWludGFpbmVkIHwgTWFpbnRhaW5lZCAoUnVieSAxLjkrKSB8DQp8IFVzZSBjYXNlIHwgU2VxdWVuY2VzIHwgTG9va3VwIHRhYmxlcyAvIG1hcHMgfA0KfCBNZW1vcnkgfCBDb21wYWN0IHwgSGlnaGVyIG92ZXJoZWFkIHwNCg0KLS0tDQoNCiMgUEFSVCA0OiBTb3J0aW5nIEFsZ29yaXRobXMgey5zZWN0aW9uLXRpdGxlfQ0KDQojIyBCdWJibGUgU29ydA0KDQpSZXBlYXRlZGx5IHN3YXBzIGFkamFjZW50IG91dC1vZi1vcmRlciBlbGVtZW50cy4gKipPKG7CsikqKiDigJQgc2ltcGxlIGJ1dCBzbG93Lg0KDQpgYGBydWJ5DQpkZWYgYnViYmxlX3NvcnQoYXJyKQ0KICBuID0gYXJyLmxlbmd0aA0KICBsb29wIGRvDQogICAgc3dhcHBlZCA9IGZhbHNlDQogICAgKG4gLSAxKS50aW1lcyBkbyB8aXwNCiAgICAgIGlmIGFycltpXSA+IGFycltpICsgMV0NCiAgICAgICAgYXJyW2ldLCBhcnJbaSArIDFdID0gYXJyW2kgKyAxXSwgYXJyW2ldICAgIyBzd2FwDQogICAgICAgIHN3YXBwZWQgPSB0cnVlDQogICAgICBlbmQNCiAgICBlbmQNCiAgICBicmVhayB1bmxlc3Mgc3dhcHBlZCAgICMgZWFybHkgZXhpdCBpZiBhbHJlYWR5IHNvcnRlZA0KICBlbmQNCiAgYXJyDQplbmQNCg0KZGF0YSA9IFs2NCwgMzQsIDI1LCAxMiwgMjIsIDExLCA5MF0NCnB1dHMgYnViYmxlX3NvcnQoZGF0YSkuaW5zcGVjdA0KIyA9PiBbMTEsIDEyLCAyMiwgMjUsIDM0LCA2NCwgOTBdDQpgYGANCg0KKipDb21wbGV4aXR5OioqIFRpbWUgTyhuwrIpIGJlc3Qvd29yc3QgfCBTcGFjZSBPKDEpDQoNCi0tLQ0KDQojIyBJbnNlcnRpb24gU29ydA0KDQpCdWlsZHMgdGhlIHNvcnRlZCBhcnJheSBvbmUgZWxlbWVudCBhdCBhIHRpbWUuICoqTyhuwrIpKiogd29yc3QsICoqTyhuKSoqIGJlc3QgKG5lYXJseSBzb3J0ZWQpLg0KDQpgYGBydWJ5DQpkZWYgaW5zZXJ0aW9uX3NvcnQoYXJyKQ0KICAoMS4uLmFyci5sZW5ndGgpLmVhY2ggZG8gfGl8DQogICAga2V5ID0gYXJyW2ldDQogICAgaiAgID0gaSAtIDENCiAgICB3aGlsZSBqID49IDAgJiYgYXJyW2pdID4ga2V5DQogICAgICBhcnJbaiArIDFdID0gYXJyW2pdICAgIyBzaGlmdCByaWdodA0KICAgICAgaiAtPSAxDQogICAgZW5kDQogICAgYXJyW2ogKyAxXSA9IGtleSAgICAgICAgICAjIGluc2VydA0KICBlbmQNCiAgYXJyDQplbmQNCg0KZGF0YSA9IFsxMiwgMTEsIDEzLCA1LCA2XQ0KcHV0cyBpbnNlcnRpb25fc29ydChkYXRhKS5pbnNwZWN0DQojID0+IFs1LCA2LCAxMSwgMTIsIDEzXQ0KYGBgDQoNCioqQmVzdCBmb3I6KiogU21hbGwgZGF0YXNldHMgb3IgbmVhcmx5IHNvcnRlZCBkYXRhLg0KDQoqKkNvbXBsZXhpdHk6KiogVGltZSBPKG7Csikgd29yc3QgfCBPKG4pIGJlc3QgfCBTcGFjZSBPKDEpDQoNCi0tLQ0KDQojIyBNZXJnZSBTb3J0DQoNCkRpdmlkZS1hbmQtY29ucXVlci4gU3BsaXQgaW4gaGFsZiwgc29ydCBlYWNoIGhhbGYsIG1lcmdlLiAqKk8obiBsb2cgbikqKiBndWFyYW50ZWVkLg0KDQpgYGBydWJ5DQpkZWYgbWVyZ2Vfc29ydChhcnIpDQogIHJldHVybiBhcnIgaWYgYXJyLmxlbmd0aCA8PSAxDQoNCiAgbWlkICAgPSBhcnIubGVuZ3RoIC8gMg0KICBsZWZ0ICA9IG1lcmdlX3NvcnQoYXJyWzAuLi5taWRdKQ0KICByaWdodCA9IG1lcmdlX3NvcnQoYXJyW21pZC4uXSkNCg0KICBtZXJnZShsZWZ0LCByaWdodCkNCmVuZA0KDQpkZWYgbWVyZ2UobGVmdCwgcmlnaHQpDQogIHJlc3VsdCA9IFtdDQogIHVudGlsIGxlZnQuZW1wdHk/IHx8IHJpZ2h0LmVtcHR5Pw0KICAgIGlmIGxlZnQuZmlyc3QgPD0gcmlnaHQuZmlyc3QNCiAgICAgIHJlc3VsdCA8PCBsZWZ0LnNoaWZ0DQogICAgZWxzZQ0KICAgICAgcmVzdWx0IDw8IHJpZ2h0LnNoaWZ0DQogICAgZW5kDQogIGVuZA0KICByZXN1bHQgKyBsZWZ0ICsgcmlnaHQNCmVuZA0KDQpkYXRhID0gWzM4LCAyNywgNDMsIDMsIDksIDgyLCAxMF0NCnB1dHMgbWVyZ2Vfc29ydChkYXRhKS5pbnNwZWN0DQojID0+IFszLCA5LCAxMCwgMjcsIDM4LCA0MywgODJdDQpgYGANCg0KLS0tDQoNCiMjIFF1aWNrIFNvcnQNCg0KUGljayBhIHBpdm90LCBwYXJ0aXRpb24gYXJvdW5kIGl0LCByZWN1cnNlLiAqKk8obiBsb2cgbikqKiBhdmVyYWdlLCAqKk8obsKyKSoqIHdvcnN0Lg0KDQpgYGBydWJ5DQpkZWYgcXVpY2tzb3J0KGFycikNCiAgcmV0dXJuIGFyciBpZiBhcnIubGVuZ3RoIDw9IDENCg0KICBwaXZvdCA9IGFyclthcnIubGVuZ3RoIC8gMl0gICAgICAgICAjIGNob29zZSBtaWRkbGUgYXMgcGl2b3QNCiAgbGVmdCAgPSBhcnIuc2VsZWN0IHsgfHh8IHggPCBwaXZvdCB9DQogIG1pZCAgID0gYXJyLnNlbGVjdCB7IHx4fCB4ID09IHBpdm90IH0NCiAgcmlnaHQgPSBhcnIuc2VsZWN0IHsgfHh8IHggPiBwaXZvdCB9DQoNCiAgcXVpY2tzb3J0KGxlZnQpICsgbWlkICsgcXVpY2tzb3J0KHJpZ2h0KQ0KZW5kDQoNCmRhdGEgPSBbMywgNiwgOCwgMTAsIDEsIDIsIDFdDQpwdXRzIHF1aWNrc29ydChkYXRhKS5pbnNwZWN0DQojID0+IFsxLCAxLCAyLCAzLCA2LCA4LCAxMF0NCmBgYA0KDQp8IEFsZ29yaXRobSB8IEJlc3QgfCBBdmVyYWdlIHwgV29yc3QgfCBTcGFjZSB8DQp8LS0tLS0tLS0tLS18LS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tfC0tLS0tLS18DQp8IEJ1YmJsZSAgICB8IE8obikgfCBPKG7CsikgfCBPKG7CsikgfCBPKDEpIHwNCnwgSW5zZXJ0aW9uIHwgTyhuKSB8IE8obsKyKSB8IE8obsKyKSB8IE8oMSkgfA0KfCBNZXJnZSAgICAgfCBPKG4gbG9nIG4pIHwgTyhuIGxvZyBuKSB8IE8obiBsb2cgbikgfCBPKG4pIHwNCnwgUXVpY2sgICAgIHwgTyhuIGxvZyBuKSB8IE8obiBsb2cgbikgfCBPKG7CsikgfCBPKGxvZyBuKSB8DQoNCi0tLQ0KDQojIFBBUlQgNTogU3RhY2tzIHsuc2VjdGlvbi10aXRsZX0NCg0KIyMgU3RhY2tzIOKAkyBMSUZPIFN0cnVjdHVyZQ0KDQpBICoqc3RhY2sqKiBpcyBMYXN0LUluLCBGaXJzdC1PdXQuIFRoaW5rOiBzdGFjayBvZiBwbGF0ZXMuDQoNCmBgYHJ1YnkNCmNsYXNzIFN0YWNrDQogIGRlZiBpbml0aWFsaXplDQogICAgQGRhdGEgPSBbXQ0KICBlbmQNCg0KICBkZWYgcHVzaChpdGVtKSAgPSBAZGF0YS5wdXNoKGl0ZW0pDQogIGRlZiBwb3AgICAgICAgICA9IEBkYXRhLnBvcA0KICBkZWYgcGVlayAgICAgICAgPSBAZGF0YS5sYXN0DQogIGRlZiBlbXB0eT8gICAgICA9IEBkYXRhLmVtcHR5Pw0KICBkZWYgc2l6ZSAgICAgICAgPSBAZGF0YS5zaXplDQogIGRlZiB0b19zICAgICAgICA9IEBkYXRhLmluc3BlY3QNCmVuZA0KDQpzID0gU3RhY2submV3DQpzLnB1c2goMTApOyBzLnB1c2goMjApOyBzLnB1c2goMzApDQpwdXRzIHMgICAgICAgICAgIyA9PiBbMTAsIDIwLCAzMF0NCnB1dHMgcy5wZWVrICAgICAjID0+IDMwICAodG9wLCBub3QgcmVtb3ZlZCkNCnB1dHMgcy5wb3AgICAgICAjID0+IDMwICAocmVtb3ZlZCkNCnB1dHMgcyAgICAgICAgICAjID0+IFsxMCwgMjBdDQpgYGANCg0KKipVc2UgY2FzZXM6KiogT3JkZXIgcmV2ZXJzYWwsIHVuZG8gb3BlcmF0aW9ucywgZnVuY3Rpb24gY2FsbCBmcmFtZXMsIGV4cHJlc3Npb24gZXZhbHVhdGlvbi4NCg0KLS0tDQoNCiMjIFN0YWNrcyDigJMgT3JkZXIgUmV2ZXJzYWwgJiBGdW5jdGlvbnMNCg0KU3RhY2tzICoqcmV2ZXJzZSBvcmRlcioqIGFuZCBtYW5hZ2UgKipmdW5jdGlvbiBjYWxsIGZyYW1lcyoqOg0KDQpgYGBydWJ5DQojIE9yZGVyIHJldmVyc2FsIHVzaW5nIGEgc3RhY2sNCmRlZiByZXZlcnNlX3N0cmluZyhzdHIpDQogIHN0YWNrID0gU3RhY2submV3DQogIHN0ci5lYWNoX2NoYXIgeyB8Y3wgc3RhY2sucHVzaChjKSB9DQogIHJlc3VsdCA9ICIiDQogIHJlc3VsdCArPSBzdGFjay5wb3AgdW50aWwgc3RhY2suZW1wdHk/DQogIHJlc3VsdA0KZW5kDQpwdXRzIHJldmVyc2Vfc3RyaW5nKCJoZWxsbyIpICAjID0+ICJvbGxlaCINCg0KIyBIb3cgdGhlIGNhbGwgc3RhY2sgd29ya3MgZHVyaW5nIGZ1bmN0aW9uIGNhbGxzOg0KIyBFYWNoIGNhbGwgcHVzaGVzIGEgImZyYW1lIiB3aXRoIGxvY2FsIHZhcnMgKyByZXR1cm4gYWRkcmVzcw0KZGVmIGZhY3RvcmlhbChuKSAgICAgICAgICAjIGZyYW1lIHB1c2hlZA0KICByZXR1cm4gMSBpZiBuIDw9IDENCiAgbiAqIGZhY3RvcmlhbChuIC0gMSkgICAgIyBuZXcgZnJhbWUgcHVzaGVkIHJlY3Vyc2l2ZWx5DQplbmQgICAgICAgICAgICAgICAgICAgICAgICMgZnJhbWUgcG9wcGVkIG9uIHJldHVybg0KDQpwdXRzIGZhY3RvcmlhbCg1KSAgICMgPT4gMTIwDQojIENhbGwgc3RhY2sgYXQgZGVlcGVzdCBwb2ludDoNCiMgW2ZhY3RvcmlhbCgxKSwgZmFjdG9yaWFsKDIpLCBmYWN0b3JpYWwoMyksIGZhY3RvcmlhbCg0KSwgZmFjdG9yaWFsKDUpXQ0KYGBgDQoNCi0tLQ0KDQojIyBSZWN1cnNpb24NCg0KKipSZWN1cnNpb24qKiB1c2VzIHRoZSBjYWxsIHN0YWNrIGltcGxpY2l0bHkg4oCUIGVhY2ggY2FsbCBhZGRzIGEgbmV3IHN0YWNrIGZyYW1lLg0KDQpgYGBydWJ5DQojIEV2ZXJ5IHJlY3Vyc2l2ZSBzb2x1dGlvbiBuZWVkczoNCiMgMS4gQkFTRSBDQVNFICDigJQgc3RvcHMgcmVjdXJzaW9uDQojIDIuIFJFQ1VSU0lWRSBDQVNFIOKAlCBtb3ZlcyB0b3dhcmQgYmFzZSBjYXNlDQoNCiMgRmlib25hY2NpIHdpdGggcmVjdXJzaW9uDQpkZWYgZmliKG4pDQogIHJldHVybiBuIGlmIG4gPD0gMSAgICAgICAgICAjIGJhc2UgY2FzZQ0KICBmaWIobiAtIDEpICsgZmliKG4gLSAyKSAgICAjIHJlY3Vyc2l2ZSBjYXNlDQplbmQNCg0KcHV0cyAoMC4uNykubWFwIHsgfGl8IGZpYihpKSB9Lmluc3BlY3QNCiMgPT4gWzAsIDEsIDEsIDIsIDMsIDUsIDgsIDEzXQ0KDQojIFRvd2VyIG9mIEhhbm9pIOKAlCBjbGFzc2ljIHJlY3Vyc2lvbiBleGFtcGxlDQpkZWYgaGFub2kobiwgZnJvbSwgdG8sIHZpYSkNCiAgaWYgbiA9PSAxDQogICAgcHV0cyAiTW92ZSBkaXNrIDE6ICN7ZnJvbX0g4oaSICN7dG99Ig0KICAgIHJldHVybg0KICBlbmQNCiAgaGFub2kobiAtIDEsIGZyb20sIHZpYSwgdG8pDQogIHB1dHMgIk1vdmUgZGlzayAje259OiAje2Zyb219IOKGkiAje3RvfSINCiAgaGFub2kobiAtIDEsIHZpYSwgdG8sIGZyb20pDQplbmQNCg0KaGFub2koMywgIkEiLCAiQyIsICJCIikNCmBgYA0KDQotLS0NCg0KIyBQQVJUIDY6IFF1ZXVlcyB7LnNlY3Rpb24tdGl0bGV9DQoNCiMjIFF1ZXVlcyDigJMgRklGTyBTdHJ1Y3R1cmUNCg0KQSAqKnF1ZXVlKiogaXMgRmlyc3QtSW4sIEZpcnN0LU91dC4gVGhpbms6IGNoZWNrb3V0IGxpbmUuDQoNCmBgYHJ1YnkNCmNsYXNzIFF1ZXVlDQogIGRlZiBpbml0aWFsaXplDQogICAgQGRhdGEgPSBbXQ0KICBlbmQNCg0KICBkZWYgZW5xdWV1ZShpdGVtKSA9IEBkYXRhLnB1c2goaXRlbSkgICAgICAjIGFkZCB0byBiYWNrDQogIGRlZiBkZXF1ZXVlICAgICAgID0gQGRhdGEuc2hpZnQgICAgICAgICAgICMgcmVtb3ZlIGZyb20gZnJvbnQNCiAgZGVmIGZyb250ICAgICAgICAgPSBAZGF0YS5maXJzdA0KICBkZWYgZW1wdHk/ICAgICAgICA9IEBkYXRhLmVtcHR5Pw0KICBkZWYgc2l6ZSAgICAgICAgICA9IEBkYXRhLnNpemUNCiAgZGVmIHRvX3MgICAgICAgICAgPSBAZGF0YS5pbnNwZWN0DQplbmQNCg0KcSA9IFF1ZXVlLm5ldw0KcS5lbnF1ZXVlKCJBbGljZSIpOyBxLmVucXVldWUoIkJvYiIpOyBxLmVucXVldWUoIkNhcm9sIikNCnB1dHMgcSAgICAgICAgICAgICMgPT4gWyJBbGljZSIsICJCb2IiLCAiQ2Fyb2wiXQ0KcHV0cyBxLmRlcXVldWUgICAgIyA9PiAiQWxpY2UiDQpwdXRzIHEgICAgICAgICAgICAjID0+IFsiQm9iIiwgIkNhcm9sIl0NCmBgYA0KDQoqKlVzZSBjYXNlczoqKiBUYXNrIHNjaGVkdWxpbmcsIEJGUyB0cmF2ZXJzYWwsIHByaW50IHNwb29sZXJzLCBidWZmZXJpbmcuDQoNCi0tLQ0KDQojIyBDaXJjdWxhciBRdWV1ZQ0KDQpBICoqY2lyY3VsYXIgcXVldWUqKiByZXVzZXMgZW1wdHkgc2xvdHMgYnkgd3JhcHBpbmcgdGhlIHRhaWwgcG9pbnRlciBhcm91bmQuDQoNCmBgYHJ1YnkNCmNsYXNzIENpcmN1bGFyUXVldWUNCiAgZGVmIGluaXRpYWxpemUoY2FwYWNpdHkpDQogICAgQGNhcCAgID0gY2FwYWNpdHkNCiAgICBAZGF0YSAgPSBBcnJheS5uZXcoY2FwYWNpdHkpDQogICAgQGhlYWQgID0gMA0KICAgIEB0YWlsICA9IDANCiAgICBAc2l6ZSAgPSAwDQogIGVuZA0KDQogIGRlZiBlbnF1ZXVlKGl0ZW0pDQogICAgcmFpc2UgIlF1ZXVlIGZ1bGwiIGlmIEBzaXplID09IEBjYXANCiAgICBAZGF0YVtAdGFpbF0gPSBpdGVtDQogICAgQHRhaWwgPSAoQHRhaWwgKyAxKSAlIEBjYXAgICAjIHdyYXAgYXJvdW5kIQ0KICAgIEBzaXplICs9IDENCiAgZW5kDQoNCiAgZGVmIGRlcXVldWUNCiAgICByYWlzZSAiUXVldWUgZW1wdHkiIGlmIEBzaXplID09IDANCiAgICBpdGVtICA9IEBkYXRhW0BoZWFkXQ0KICAgIEBoZWFkID0gKEBoZWFkICsgMSkgJSBAY2FwICAgIyB3cmFwIGFyb3VuZCENCiAgICBAc2l6ZSAtPSAxDQogICAgaXRlbQ0KICBlbmQNCmVuZA0KDQpjcSA9IENpcmN1bGFyUXVldWUubmV3KDMpDQpjcS5lbnF1ZXVlKDEpOyBjcS5lbnF1ZXVlKDIpOyBjcS5lbnF1ZXVlKDMpDQpwdXRzIGNxLmRlcXVldWUgICAjID0+IDENCmNxLmVucXVldWUoNCkgICAgICMgcmV1c2VzIHNsb3QgMA0KYGBgDQoNCi0tLQ0KDQojIyBQcmlvcml0eSBRdWV1ZQ0KDQpFbGVtZW50cyBhcmUgZGVxdWV1ZWQgaW4gKipwcmlvcml0eSBvcmRlcioqLCBub3QgYXJyaXZhbCBvcmRlci4NCg0KYGBgcnVieQ0KY2xhc3MgUHJpb3JpdHlRdWV1ZQ0KICBkZWYgaW5pdGlhbGl6ZQ0KICAgIEBkYXRhID0gW10gICAjIHN0b3JlIFtwcmlvcml0eSwgaXRlbV0gcGFpcnMNCiAgZW5kDQoNCiAgZGVmIGVucXVldWUoaXRlbSwgcHJpb3JpdHkpDQogICAgQGRhdGEgPDwgW3ByaW9yaXR5LCBpdGVtXQ0KICAgIEBkYXRhLnNvcnRfYnkhIHsgfHAsIF98IC1wIH0gICMgaGlnaGVzdCBwcmlvcml0eSBmaXJzdA0KICBlbmQNCg0KICBkZWYgZGVxdWV1ZQ0KICAgIEBkYXRhLnNoaWZ0Ji5sYXN0DQogIGVuZA0KDQogIGRlZiBwZWVrDQogICAgQGRhdGEuZmlyc3QmLmxhc3QNCiAgZW5kDQplbmQNCg0KcHEgPSBQcmlvcml0eVF1ZXVlLm5ldw0KcHEuZW5xdWV1ZSgibG93IHRhc2siLCAgICAgIDEpDQpwcS5lbnF1ZXVlKCJjcml0aWNhbCB0YXNrIiwgMTApDQpwcS5lbnF1ZXVlKCJub3JtYWwgdGFzayIsICAgNSkNCg0KcHV0cyBwcS5kZXF1ZXVlICAgIyA9PiAiY3JpdGljYWwgdGFzayIgICAocHJpb3JpdHkgMTApDQpwdXRzIHBxLmRlcXVldWUgICAjID0+ICJub3JtYWwgdGFzayIgICAgIChwcmlvcml0eSA1KQ0KcHV0cyBwcS5kZXF1ZXVlICAgIyA9PiAibG93IHRhc2siICAgICAgICAocHJpb3JpdHkgMSkNCmBgYA0KDQotLS0NCg0KIyBQQVJUIDc6IFRyZWVzIHsuc2VjdGlvbi10aXRsZX0NCg0KIyMgQmluYXJ5IFRyZWVzDQoNCkEgKipiaW5hcnkgdHJlZSoqIGhhcyBub2RlcyB3aXRoIGF0IG1vc3QgKip0d28gY2hpbGRyZW4qKiAobGVmdCBhbmQgcmlnaHQpLg0KDQpgYGBydWJ5DQpjbGFzcyBUcmVlTm9kZQ0KICBhdHRyX2FjY2Vzc29yIDp2YWwsIDpsZWZ0LCA6cmlnaHQNCiAgZGVmIGluaXRpYWxpemUodmFsKQ0KICAgIEB2YWwgPSB2YWw7IEBsZWZ0ID0gbmlsOyBAcmlnaHQgPSBuaWwNCiAgZW5kDQplbmQNCg0KIyBCdWlsZCBhIEJpbmFyeSBTZWFyY2ggVHJlZSAoQlNUKQ0KY2xhc3MgQlNUDQogIGRlZiBpbnNlcnQocm9vdCwgdmFsKQ0KICAgIHJldHVybiBUcmVlTm9kZS5uZXcodmFsKSBpZiByb290Lm5pbD8NCiAgICBpZiB2YWwgPCByb290LnZhbA0KICAgICAgcm9vdC5sZWZ0ICA9IGluc2VydChyb290LmxlZnQsICB2YWwpDQogICAgZWxzZQ0KICAgICAgcm9vdC5yaWdodCA9IGluc2VydChyb290LnJpZ2h0LCB2YWwpDQogICAgZW5kDQogICAgcm9vdA0KICBlbmQNCg0KICBkZWYgaW5vcmRlcihyb290KSAgICMgTGVmdCDihpIgUm9vdCDihpIgUmlnaHQgKHNvcnRlZCBvdXRwdXQpDQogICAgcmV0dXJuIGlmIHJvb3QubmlsPw0KICAgIGlub3JkZXIocm9vdC5sZWZ0KQ0KICAgIHByaW50ICIje3Jvb3QudmFsfSAiDQogICAgaW5vcmRlcihyb290LnJpZ2h0KQ0KICBlbmQNCmVuZA0KDQpic3QgID0gQlNULm5ldw0Kcm9vdCA9IG5pbA0KWzUsIDMsIDcsIDEsIDQsIDYsIDhdLmVhY2ggeyB8dnwgcm9vdCA9IGJzdC5pbnNlcnQocm9vdCwgdikgfQ0KYnN0Lmlub3JkZXIocm9vdCkgICAjID0+IDEgMyA0IDUgNiA3IDgNCmBgYA0KDQotLS0NCg0KIyMgTWF0aCBFeHByZXNzaW9uIFRyZWUNCg0KRXhwcmVzc2lvbiB0cmVlcyByZXByZXNlbnQgKiptYXRoZW1hdGljYWwgZXhwcmVzc2lvbnMqKiBzdHJ1Y3R1cmFsbHkuDQoNCmBgYHJ1YnkNCmNsYXNzIEV4cHJOb2RlDQogIGF0dHJfYWNjZXNzb3IgOnZhbCwgOmxlZnQsIDpyaWdodA0KICBkZWYgaW5pdGlhbGl6ZSh2YWwsIGxlZnQgPSBuaWwsIHJpZ2h0ID0gbmlsKQ0KICAgIEB2YWwgPSB2YWw7IEBsZWZ0ID0gbGVmdDsgQHJpZ2h0ID0gcmlnaHQNCiAgZW5kDQplbmQNCg0KIyBUcmVlIGZvcjogKDMgKyA0KSAqICg1IC0gMikNCiMgICAgICAgICoNCiMgICAgICAgLyBcDQojICAgICAgKyAgIC0NCiMgICAgIC8gXCAvIFwNCiMgICAgMyAgNCA1ICAyDQoNCnRyZWUgPSBFeHByTm9kZS5uZXcoIioiLA0KICAgICAgICAgRXhwck5vZGUubmV3KCIrIiwgRXhwck5vZGUubmV3KDMpLCBFeHByTm9kZS5uZXcoNCkpLA0KICAgICAgICAgRXhwck5vZGUubmV3KCItIiwgRXhwck5vZGUubmV3KDUpLCBFeHByTm9kZS5uZXcoMikpKQ0KDQpkZWYgZXZhbHVhdGUobm9kZSkNCiAgcmV0dXJuIG5vZGUudmFsIGlmIG5vZGUubGVmdC5uaWw/ICAgIyBsZWFmID0gbnVtYmVyDQogIGwgPSBldmFsdWF0ZShub2RlLmxlZnQpDQogIHIgPSBldmFsdWF0ZShub2RlLnJpZ2h0KQ0KICBjYXNlIG5vZGUudmFsDQogIHdoZW4gIisiIHRoZW4gbCArIHINCiAgd2hlbiAiLSIgdGhlbiBsIC0gcg0KICB3aGVuICIqIiB0aGVuIGwgKiByDQogIHdoZW4gIi8iIHRoZW4gbCAvIHIudG9fZg0KICBlbmQNCmVuZA0KDQpwdXRzIGV2YWx1YXRlKHRyZWUpICAgIyA9PiAyMSAgWyAoMys0KSooNS0yKSA9IDcqMyBdDQpgYGANCg0KLS0tDQoNCiMjIEIrIFRyZWVzDQoNCioqQisgVHJlZXMqKiBhcmUgYmFsYW5jZWQgbXVsdGktd2F5IHRyZWVzIHVzZWQgaW4gZGF0YWJhc2VzIGFuZCBmaWxlIHN5c3RlbXMuDQoNCmBgYA0KU3RydWN0dXJlIG9mIGEgQisgVHJlZSAob3JkZXIgMyk6DQoNCkludGVybmFsIG5vZGVzOiBbIDEwIHwgMjAgXQ0KICAgICAgICAgICAgICAgLyAgICAgfCAgICAgIFwNCiAgICAgICAgIFs1fDddICAgIFsxMnwxN10gICBbMjJ8MzBdDQogICAgICAgICAg4oaTICAgICAgICAgIOKGkyAgICAgICAgICDihpMNCiAgICAgICBMZWFmIG5vZGVzIChsaW5rZWQpIOKGkiBmYXN0ZXIgcmFuZ2UgcXVlcmllcw0KDQpLZXkgcHJvcGVydGllczoNCiAg4oCiIEFsbCBkYXRhIGxpdmVzIGluIExFQUYgbm9kZXMNCiAg4oCiIEludGVybmFsIG5vZGVzIGFyZSBpbmRleC1vbmx5DQogIOKAoiBMZWFmIG5vZGVzIGFyZSBsaW5rZWQgKOKGkikgZm9yIHJhbmdlIHNjYW5zDQogIOKAoiBHdWFyYW50ZWVkIE8obG9nIG4pIHNlYXJjaCwgaW5zZXJ0LCBkZWxldGUNCmBgYA0KDQpgYGBydWJ5DQojIENvbmNlcHR1YWwgQisgVHJlZSBzZWFyY2ggaW4gUnVieQ0KY2xhc3MgQlBsdXNOb2RlDQogIGF0dHJfYWNjZXNzb3IgOmtleXMsIDpjaGlsZHJlbiwgOmlzX2xlYWYsIDpuZXh0X2xlYWYNCiAgZGVmIGluaXRpYWxpemUoaXNfbGVhZiA9IGZhbHNlKQ0KICAgIEBrZXlzID0gW107IEBjaGlsZHJlbiA9IFtdOyBAaXNfbGVhZiA9IGlzX2xlYWYNCiAgICBAbmV4dF9sZWFmID0gbmlsDQogIGVuZA0KZW5kDQoNCiMgQisgVHJlZXMgYXJlIHVzZWQgaW46IE15U1FMIElubm9EQiwgUG9zdGdyZVNRTCBpbmRleGVzLA0KIyBmaWxlIHN5c3RlbXMgKE5URlMsIGV4dDQpLCBhbmQgbWFueSBOb1NRTCBzdG9yZXMuDQpwdXRzICJCKyBUcmVlIGVuc3VyZXMgTyhsb2cgbikgb3BlcmF0aW9ucyByZWdhcmRsZXNzIG9mIHNpemUiDQpgYGANCg0KLS0tDQoNCiMgUEFSVCA4OiBHcmFwaHMgey5zZWN0aW9uLXRpdGxlfQ0KDQojIyBHcmFwaHMg4oCTIE1hemVzDQoNCkEgKipncmFwaCoqIGlzIGEgc2V0IG9mICoqbm9kZXMgKHZlcnRpY2VzKSoqIGNvbm5lY3RlZCBieSAqKmVkZ2VzKiouIE1hemVzIGFyZSBncmFwaHMhDQoNCmBgYHJ1YnkNCiMgUmVwcmVzZW50IGEgbWF6ZSBhcyBhbiBhZGphY2VuY3kgbGlzdA0KbWF6ZSA9IHsNCiAgInN0YXJ0IiA9PiBbIkEiLCAiQiJdLA0KICAiQSIgICAgID0+IFsiQyJdLA0KICAiQiIgICAgID0+IFsiQyIsICJEIl0sDQogICJDIiAgICAgPT4gWyJlbmQiXSwNCiAgIkQiICAgICA9PiBbImVuZCJdLA0KICAiZW5kIiAgID0+IFtdDQp9DQoNCiMgQkZTIHRvIGZpbmQgc2hvcnRlc3QgcGF0aCB0aHJvdWdoIG1hemUNCmRlZiBiZnMoZ3JhcGgsIHN0YXJ0LCBnb2FsKQ0KICBxdWV1ZSAgID0gW1tzdGFydCwgW3N0YXJ0XV1dDQogIHZpc2l0ZWQgPSB7fQ0KICB1bnRpbCBxdWV1ZS5lbXB0eT8NCiAgICBub2RlLCBwYXRoID0gcXVldWUuc2hpZnQNCiAgICByZXR1cm4gcGF0aCBpZiBub2RlID09IGdvYWwNCiAgICBuZXh0IGlmIHZpc2l0ZWRbbm9kZV0NCiAgICB2aXNpdGVkW25vZGVdID0gdHJ1ZQ0KICAgIGdyYXBoW25vZGVdLmVhY2ggeyB8bnwgcXVldWUgPDwgW24sIHBhdGggKyBbbl1dIH0NCiAgZW5kDQogIG5pbA0KZW5kDQoNCnBhdGggPSBiZnMobWF6ZSwgInN0YXJ0IiwgImVuZCIpDQpwdXRzIHBhdGguam9pbigiIOKGkiAiKSAgICMgPT4gc3RhcnQg4oaSIEEg4oaSIEMg4oaSIGVuZA0KYGBgDQoNCi0tLQ0KDQojIyBUcmFuc2l0aW9uIE1hdHJpeCAoQWRqYWNlbmN5IE1hdHJpeCkNCg0KQSAqKnRyYW5zaXRpb24gbWF0cml4KiogcmVwcmVzZW50cyBncmFwaCBlZGdlcyBhcyBhIDJEIGdyaWQuDQoNCmBgYHJ1YnkNCiMgR3JhcGg6ICBB4oaSQiwgQeKGkkMsIELihpJDLCBD4oaSRA0KIyAgICAgICAgIDA9QSwgMT1CLCAyPUMsIDM9RA0KbWF0cml4ID0gWw0KICBbMCwgMSwgMSwgMF0sICAgIyBBIGNvbm5lY3RzIHRvIEIsIEMNCiAgWzAsIDAsIDEsIDBdLCAgICMgQiBjb25uZWN0cyB0byBDDQogIFswLCAwLCAwLCAxXSwgICAjIEMgY29ubmVjdHMgdG8gRA0KICBbMCwgMCwgMCwgMF0gICAgIyBEOiBubyBvdXRnb2luZyBlZGdlcw0KXQ0KDQpMQUJFTFMgPSAld1tBIEIgQyBEXQ0KDQojIFByaW50IGFkamFjZW5jeQ0KbWF0cml4LmVhY2hfd2l0aF9pbmRleCBkbyB8cm93LCBpfA0KICByb3cuZWFjaF93aXRoX2luZGV4IGRvIHx2YWwsIGp8DQogICAgcHV0cyAiI3tMQUJFTFNbaV19IOKGkiAje0xBQkVMU1tqXX0iIGlmIHZhbCA9PSAxDQogIGVuZA0KZW5kDQoNCiMgQ2hlY2sgaWYgcGF0aCBB4oaSQyBleGlzdHM6IG1hdHJpeFswXVsyXQ0KcHV0cyAiQeKGkkMgZWRnZSBleGlzdHM/ICN7bWF0cml4WzBdWzJdID09IDF9IiAgICMgPT4gdHJ1ZQ0KIyBDaGVjayBpZiBwYXRoIELihpJBIGV4aXN0czogbWF0cml4WzFdWzBdDQpwdXRzICJC4oaSQSBlZGdlIGV4aXN0cz8gI3ttYXRyaXhbMV1bMF0gPT0gMX0iICAgIyA9PiBmYWxzZQ0KYGBgDQoNCi0tLQ0KDQojIyBEaWprc3RyYSdzIEFsZ29yaXRobQ0KDQpGaW5kcyB0aGUgKipzaG9ydGVzdCBwYXRoKiogaW4gYSB3ZWlnaHRlZCBncmFwaCB3aXRoIG5vbi1uZWdhdGl2ZSB3ZWlnaHRzLg0KDQpgYGBydWJ5DQpkZWYgZGlqa3N0cmEoZ3JhcGgsIHN0YXJ0KQ0KICBkaXN0ICAgID0gSGFzaC5uZXcoRmxvYXQ6OklORklOSVRZKQ0KICBkaXN0W3N0YXJ0XSA9IDANCiAgdmlzaXRlZCA9IHt9DQogIHBxICAgICAgPSBbWzAsIHN0YXJ0XV0gICAjIFtjb3N0LCBub2RlXQ0KDQogIHVudGlsIHBxLmVtcHR5Pw0KICAgIGNvc3QsIG5vZGUgPSBwcS5taW5fYnkgeyB8YywgX3wgYyB9DQogICAgcHEuZGVsZXRlKFtjb3N0LCBub2RlXSkNCiAgICBuZXh0IGlmIHZpc2l0ZWRbbm9kZV0NCiAgICB2aXNpdGVkW25vZGVdID0gdHJ1ZQ0KDQogICAgKGdyYXBoW25vZGVdIHx8IFtdKS5lYWNoIGRvIHxuZWlnaGJvciwgd2VpZ2h0fA0KICAgICAgbmV3X2Nvc3QgPSBjb3N0ICsgd2VpZ2h0DQogICAgICBpZiBuZXdfY29zdCA8IGRpc3RbbmVpZ2hib3JdDQogICAgICAgIGRpc3RbbmVpZ2hib3JdID0gbmV3X2Nvc3QNCiAgICAgICAgcHEgPDwgW25ld19jb3N0LCBuZWlnaGJvcl0NCiAgICAgIGVuZA0KICAgIGVuZA0KICBlbmQNCiAgZGlzdA0KZW5kDQoNCmdyYXBoID0gew0KICAiQSIgPT4gW1siQiIsIDFdLCBbIkMiLCA0XV0sDQogICJCIiA9PiBbWyJDIiwgMl0sIFsiRCIsIDVdXSwNCiAgIkMiID0+IFtbIkQiLCAxXV0sDQogICJEIiA9PiBbXQ0KfQ0KcHV0cyBkaWprc3RyYShncmFwaCwgIkEiKS5pbnNwZWN0DQojID0+IHsiQSI9PjAsICJCIj0+MSwgIkMiPT4zLCAiRCI9PjR9DQpgYGANCg0KLS0tDQoNCiMjIEZsb3cgQW5hbHlzaXMgKE1heC1GbG93KQ0KDQoqKkZsb3cgbmV0d29ya3MqKiBtb2RlbCBjYXBhY2l0eS1jb25zdHJhaW5lZCBzeXN0ZW1zIChuZXR3b3JrcywgcGlwZWxpbmVzLCB0cmFmZmljKS4NCg0KYGBgcnVieQ0KIyBGb3JkLUZ1bGtlcnNvbiBNYXgtRmxvdyAoQkZTLWJhc2VkIEVkbW9uZHMtS2FycCkNCmRlZiBiZnNfcGF0aChjYXBhY2l0eSwgc291cmNlLCBzaW5rLCBwYXJlbnQpDQogIHZpc2l0ZWQgPSB7IHNvdXJjZSA9PiB0cnVlIH0NCiAgcXVldWUgICA9IFtzb3VyY2VdDQogIHVudGlsIHF1ZXVlLmVtcHR5Pw0KICAgIHUgPSBxdWV1ZS5zaGlmdA0KICAgIGNhcGFjaXR5W3VdJi5lYWNoIGRvIHx2LCBjYXB8DQogICAgICBpZiAhdmlzaXRlZFt2XSAmJiBjYXAgPiAwDQogICAgICAgIHZpc2l0ZWRbdl0gPSB0cnVlDQogICAgICAgIHBhcmVudFt2XSAgPSB1DQogICAgICAgIHJldHVybiB0cnVlIGlmIHYgPT0gc2luaw0KICAgICAgICBxdWV1ZSA8PCB2DQogICAgICBlbmQNCiAgICBlbmQNCiAgZW5kDQogIGZhbHNlDQplbmQNCg0KIyBLZXkgY29uY2VwdHMgZm9yIGZsb3cgYW5hbHlzaXM6DQojIOKAoiBTb3VyY2Ugbm9kZSAoc3VwcGx5KSDihpIgU2luayBub2RlIChkZW1hbmQpDQojIOKAoiBFYWNoIGVkZ2UgaGFzIGEgY2FwYWNpdHkgY29uc3RyYWludA0KIyDigKIgTWF4LUZsb3cgPSBNaW4tQ3V0IChGb3JkLUZ1bGtlcnNvbiB0aGVvcmVtKQ0KIyDigKIgQXBwbGljYXRpb25zOiBuZXR3b3JrIHJvdXRpbmcsIG1hdGNoaW5nLCBzY2hlZHVsaW5nDQoNCnB1dHMgIk1heC1GbG93IGFwcGxpY2F0aW9uczoiDQpwdXRzICIgIOKAoiBJbnRlcm5ldCB0cmFmZmljIHJvdXRpbmciDQpwdXRzICIgIOKAoiBTdXBwbHkgY2hhaW4gb3B0aW1pemF0aW9uIg0KcHV0cyAiICDigKIgQmlwYXJ0aXRlIG1hdGNoaW5nIChqb2IgYXNzaWdubWVudHMpIg0KcHV0cyAiICDigKIgSW1hZ2Ugc2VnbWVudGF0aW9uIg0KYGBgDQoNCi0tLQ0KDQojIEVYQU0gU1VNTUFSWSB7LnNlY3Rpb24tdGl0bGV9DQoNCiMjIFF1aWNrIFJlZmVyZW5jZSDigJMgQ29tcGxleGl0eSBDaGVhdCBTaGVldA0KDQp8IFN0cnVjdHVyZSAvIEFsZ29yaXRobSB8IEFjY2VzcyB8IFNlYXJjaCB8IEluc2VydCB8IERlbGV0ZSB8IFNwYWNlIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tfA0KfCBBcnJheSAoc3RhdGljKSAgICAgICAgfCBPKDEpICAgfCBPKG4pICAgfCBPKG4pICAgfCBPKG4pICAgfCBPKG4pICB8DQp8IEhhc2ggLyBEaWN0aW9uYXJ5ICAgICB8IE8oMSkgICB8IE8oMSkgICB8IE8oMSkgICB8IE8oMSkgICB8IE8obikgIHwNCnwgU2luZ2x5IExpbmtlZCBMaXN0ICAgIHwgTyhuKSAgIHwgTyhuKSAgIHwgTygxKeKAoCAgfCBPKDEp4oCgICB8IE8obikgIHwNCnwgRG91Ymx5IExpbmtlZCBMaXN0ICAgIHwgTyhuKSAgIHwgTyhuKSAgIHwgTygxKeKAoCAgfCBPKDEp4oCgICB8IE8obikgIHwNCnwgU3RhY2sgKHB1c2gvcG9wKSAgICAgIHwgTyhuKSAgIHwgTyhuKSAgIHwgTygxKSAgIHwgTygxKSAgIHwgTyhuKSAgfA0KfCBRdWV1ZSAoZW5xL2RlcSkgICAgICAgfCBPKG4pICAgfCBPKG4pICAgfCBPKDEpICAgfCBPKDEpICAgfCBPKG4pICB8DQp8IEJTVCAoYmFsYW5jZWQpICAgICAgICB8IE8obG9nIG4pIHwgTyhsb2cgbikgfCBPKGxvZyBuKSB8IE8obG9nIG4pIHwgTyhuKSB8DQp8IEIrIFRyZWUgICAgICAgICAgICAgICB8IE8obG9nIG4pIHwgTyhsb2cgbikgfCBPKGxvZyBuKSB8IE8obG9nIG4pIHwgTyhuKSB8DQp8IEJ1YmJsZSBTb3J0ICAgICAgICAgICB8IOKAlCAgICAgIHwg4oCUICAgICAgfCDigJQgICAgICB8IOKAlCAgICAgIHwgTygxKSB3b3JzdCBPKG7CsikgfA0KfCBNZXJnZSBTb3J0ICAgICAgICAgICAgfCDigJQgICAgICB8IOKAlCAgICAgIHwg4oCUICAgICAgfCDigJQgICAgICB8IE8obikgYWx3YXlzIE8obiBsb2cgbikgfA0KfCBRdWljayBTb3J0ICAgICAgICAgICAgfCDigJQgICAgICB8IOKAlCAgICAgIHwg4oCUICAgICAgfCDigJQgICAgICB8IE8obG9nIG4pIGF2ZyBPKG4gbG9nIG4pIHwNCg0K4oCgIFdpdGggZGlyZWN0IHJlZmVyZW5jZSB0byBub2RlDQoNCi0tLQ0KDQojIyBLZXkgQ29uY2VwdHMgdG8gUmVtZW1iZXINCg0KKipNZW1vcnk6KioNCi0gU3RhY2sgPSByZWZlcmVuY2VzL3ByaW1pdGl2ZXMgfCBIZWFwID0gb2JqZWN0cw0KLSBHQyBtYXJrcyByZWFjaGFibGUgb2JqZWN0cywgc3dlZXBzIHRoZSByZXN0DQoNCioqTGlzdHM6KiogU2luZ2x5IChmb3J3YXJkIG9ubHkpIHZzIERvdWJseSAoZm9yd2FyZCArIGJhY2t3YXJkKQ0KDQoqKlRyZWVzOioqDQotIEJTVDogbGVmdCA8IHJvb3QgPCByaWdodCDihpIgaW5vcmRlciBnaXZlcyBzb3J0ZWQgb3V0cHV0DQotIEIrIFRyZWU6IGFsbCBkYXRhIGluIGxlYXZlcywgbGVhdmVzIGxpbmtlZCBmb3IgcmFuZ2UgcXVlcmllcw0KLSBFeHByZXNzaW9uIFRyZWU6IHBvc3Qtb3JkZXIgdHJhdmVyc2FsID0gZXZhbHVhdGUNCg0KKipHcmFwaHM6KioNCi0gQWRqYWNlbmN5IExpc3Q6IHNwYXJzZSBncmFwaHMgKGxlc3MgbWVtb3J5KQ0KLSBBZGphY2VuY3kgTWF0cml4OiBkZW5zZSBncmFwaHMgKE8oMSkgZWRnZSBsb29rdXApDQotIERpamtzdHJhOiBncmVlZHkgc2hvcnRlc3QgcGF0aCAobm9uLW5lZ2F0aXZlIHdlaWdodHMgb25seSkNCg0KKipTdGFja3M6KiogTElGTyDihpIgcmV2ZXJzYWwsIHJlY3Vyc2lvbiwgZnVuY3Rpb24gY2FsbHMNCg0KKipRdWV1ZXM6KiogRklGTyDihpIgQkZTLCBzY2hlZHVsaW5nIHwgQ2lyY3VsYXIg4oaSIHJldXNlIHNwYWNlIHwgUHJpb3JpdHkg4oaSIG9yZGVyZWQgc2VydmljZQ0KDQo+ICoqR29vZCBsdWNrIG9uIHlvdXIgZmluYWwgZXhhbSEqKiANCg==