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:
| 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}" }
| 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]
| 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
| 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==