12.1 Knapsack Problems
Why it is not easy being a burglar?
In addition to the obvious problems (making sure that a home is empty, picking locks, circumventing alarms, dealing with ethical quandaries, etc.), a burglar has to decide what to steal. The problem is that most homes contain more things of value than the average burglar can carry away. What’s a poor burglar to do? He needs to find the set of things that provides the most value without exceeding his carrying capacity. Suppose for example, a burglar who has a knapsack that can hold at most 20 pounds of loot breaks into a house and finds the items in Figure 12.1. Clearly, he will not be able to fit it all in his knapsack, so he needs to decide what to take and what to leave behind.
Clock |
175 |
10 |
17.5 |
Painting |
90 |
9 |
10 |
Radio |
20 |
4 |
5 |
Vase |
50 |
2 |
25 |
Book |
10 |
1 |
10 |
Computer |
200 |
20 |
10 |
Figure 12.1 Table of items
What is the basic concept behind greedy algorithm?
The thief would choose the best item first, then the next best, and continue until he reached his limit. Of course, before doing this, the thief would have to decide what “best” should mean. Is the best item the most valuable, the least heavy, or maybe the item with the highest value-to-weight ratio?
Don’t under look a greedy algorithm. It is an algorithm:
- It sets the ruler to compare: the best.
- It shows the order of actions: one by one.
- It puts clear boundary: carry weight.
What is the implementation of three “best” greedy algorithm?
class Item (object):
"""
Class of loot item, including three attributes: <name, value, and weight>.
"""
def __init__(self, n, v, w):
"""
Init a instance of Item with name, value and weight.
:param n: string, name of item
:param v: float, value of item
:param w: float, value of item
"""
self.name = n
self.value = v
self.weight = w
def getName (self):
return self.name
def getValue (self):
return self.value
def getWeight (self):
return self.weight
def __str__(self):
result = '<' + self.name + ', ' + str (self.value)\
+ ', ' + str (self.weight) + '>'
return result
def value (item):
"""
Map "item" into its value.
:param item: an Item type item
:return: this item's value
"""
return item.getValue ()
def weightInverse (item):
"""
Map "item" into its weight's inverse value.
:param item: an Item type item
:return: this item's 1/weight
"""
return 1.0/item.getWeight ()
def density (item):
"""
Map "item" into its density(value/weight).
:param item: an Item type item
:return: this item's value/weight
"""
return item.getValue ()/item.getWeight ()
def greedy (items, maxWeight, keyFunction):
"""
Greedy is the core process of greedy algorithm. According to the keyFunction,
it puts the best items into knapsack until it is full.
:param items: list, of instances of Item.
:param maxWeight: float >= 0, the maximum weight a knapsack can contain.
:param keyFunction: function,
maps an item in a list to an comparable value accordingly,
as the second parameter of the `sorted` function
:return: a list of taken items, and a float of their total value
"""
# Use "what is my best" as the key function to sort a copied list of the items.
# Note the `sorted`'s algorithm efficiency is in O(n * log(n)).
itemsCopy = sorted (items, key = keyFunction, reverse = True)
result = []
totalValue, totalWeight = 0.0, 0.0
# Try each item in the sorted items list descendingly.
for i in range (len (itemsCopy)):
# Check whether the next biggest one could be taken into knapsack.
if (totalWeight + itemsCopy[i].getWeight ()) <= maxWeight:
result.append (itemsCopy[i])
# If new item is taken, update the total weight and value of result
totalWeight += itemsCopy[i].getWeight ()
totalValue += itemsCopy[i].getValue ()
return result, totalValue
def build_items ():
"""
Build a list of Items instances.
:return: a list of Items instances.
"""
names = ['clock','painting','radio','vase','book','computer']
values = [175,90,20,50,10,200]
weights = [10,9,4,2,1,20]
items = []
for i in range (len(values)):
items.append (Item(names[i], values[i], weights[i]))
return items
def test_greedy (items, maxWeight, keyFunction):
"""
Calculate for one sort of greedy strategy, and print its result.
:param items: a list of Items instances.
:param maxWeight: float, as the maximum weight a knapsack can hold.
:param keyFunction: function,
maps an item in a list to an comparable value accordingly,
as the second parameter of the `sorted` function
:return: None. Print out total value and items in taken loot.
"""
taken, val = greedy (items, maxWeight, keyFunction)
print ('Total value of items taken is', val)
for item in taken:
print (' ', item)
def test_diff_greedys (maxWeight = 20):
"""
Print out the results of different sorts of greedy strategy.
:param maxWeight: float, as the maximum weight a knapsack can hold,
with default value 20.0.
:return: None. Print out total value and items in taken loot of three sorts of strategy.
"""
items = build_items ()
# Greedy by value.
print ('Use greedy by value to fill knapsack of size', maxWeight)
test_greedy (items, maxWeight, value)
# Greedy by weight.
print ('\nUse greedy by weight to fill knapsack of size',
maxWeight)
test_greedy (items, maxWeight, weightInverse)
# Greedy by density.
print ('\nUse greedy by density to fill knapsack of size',
maxWeight)
test_greedy (items, maxWeight, density)
Here we run it.
test_diff_greedys(maxWeight=20.0)
Could this straightforward way be speeded up?
01.The gen_power_set
could have had the header
def genPowerset(items, constraint, getVal, getWeight)
and returned only those combinations that meet the weight constraint.
Or
02.The choose_best
could exit the inner loop as soon as the weight constraint is exceeded.
# def gen_power_set (ori_list): # O(2**len(ori_list)) * O(len (ori_list))
def gen_power_set_within_constraint(items, constraint, getWeight):
"""
Generate the power set of items which meet the weight constraint.
:param items: list, instances of Items.
:param constraint: float, maximum weight of knapsack.
:param getWeight: method, get weight of an Item.
:return: a list of lists that contains all possible combinations of
the elements that meet the weight constraint.
"""
power_set_within_constraint = []
for i in range (0, 2**len(items)): # O(2**len(ori_list))
bin_str = get_binary_rep(i, len(items))
subset = []
subset_total_weight = 0
for j in range(len(items)): # O(len(ori_list))
if bin_str[j] == '1':
subset.append(items[j])
subset_total_weight += items[j].getWeight()
if subset_total_weight <= constraint:
power_set_within_constraint.append(subset)
return power_set_within_constraint
# def choose_best(pset, max_weight, get_val, get_weight):
def choose_best_efficiently(pset, max_weight, get_val, get_weight):
"""
Choose the best set of items from the power set of original list,
within the maximum weight budget of knapsack.
:param pset: list of lists, that contains all possible combinations of
the elements of ori_list.
:param max_weight: float or int, maximum weight budget of knapsack.
:param get_val: function or method, map an item in pset's list into its
comparable value
:param get_weight: function or method, map an item in pset's list into its
comparable weight
:return: a best set of items, and this best set's total value
"""
best_val = 0.0
best_set = None
for items in pset:
items_val = 0.0
items_weight = 0.0
for item in items:
items_val += get_val(item)
items_weight += get_weight(item)
if items_weight > max_weight:
break
if items_weight <= max_weight and items_val > best_val:
best_val = items_val
best_set = items
return best_set, best_val
# def test_best(max_weight = 20):
def test_best_opt_01(max_weight = 20):
"""
Run the choose_best function, and print its result in proper format.
:param max_weight: float or int, maximum weight budget of knapsack, default 20.
:return: None, print out the items taken and their total value.
"""
items = build_items()
pset_within_constraint = gen_power_set_within_constraint(items, constraint=max_weight, getWeight=Item.getWeight)
taken, val = choose_best(pset_within_constraint, max_weight, Item.getValue, Item.getWeight)
print('Total value of items taken is', val)
for item in taken:
print(item)
# def test_best(max_weight = 20):
def test_best_opt_02(max_weight = 20):
"""
Run the choose_best function, and print its result in proper format.
:param max_weight: float or int, maximum weight budget of knapsack, default 20.
:return: None, print out the items taken and their total value.
"""
items = build_items()
pset = gen_power_set(items)
taken, val = choose_best_efficiently(pset, max_weight, Item.getValue, Item.getWeight)
print('Total value of items taken is', val)
for item in taken:
print(item)
test_best_opt_01(max_weight=20)
test_best_opt_02(max_weight=20)
While these kinds of optimizations are often worth doing, they don’t address the fundamental issue. 【吴军老师写好算法后,工程主管要他做优化,省机器就是算钱。】
```
LS0tCnRpdGxlOiAxMiBLTkFQU0FDSyBBTkQgR1JBUEggT1BUSU1JWkFUSU9OIFBST0JMRU1TCnN1YnRpdGxlOiAKYXV0aG9yOiBKb2huIFF1CmRhdGU6IDIwMjAtMDYtMTMKc2x1ZzogCnRhZ3M6Ci0gCmNhdGVnb3JpZXM6Ci0gbWl0NjAwCnR5cG9yYS1yb290LXVybDogLi4vLi4vc3RhdGljCnNob3dfdG9jOiB5ZXMKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKIyMjIFdoYXQgYXJlIHRoZSBjaGFyYWN0ZXJzIG9mIG9wdGltaXphdGlvbiBwcm9ibGVtPwoKSW4gZ2VuZXJhbCwgYW4gb3B0aW1pemF0aW9uIHByb2JsZW0gaGFzIHR3byBwYXJ0czoKCjEuIEFuIG9iamVjdGl2ZSBmdW5jdGlvbiB0aGF0IGlzIHRvIGJlIG1heGltaXplZCBvciBtaW5pbWl6ZWQuIEZvciBleGFtcGxlLCB0aGUgYWlyZmFyZVteYWlyZmFyZV0gYmV0d2VlbiBCb3N0b24gYW5kIElzdGFuYnVsLgoxLiBBIHNldCBvZiBjb25zdHJhaW50cyAocG9zc2libHkgZW1wdHkpIHRoYXQgbXVzdCBiZSBob25vcmVkW15ob25vcl92LnQuXS4gRm9yIGV4YW1wbGUsIGFuIHVwcGVyIGJvdW5kW15ib3VuZF9ib3VuZGFyeV9TeW5dIG9uIHRoZSB0cmF2ZWwgdGltZS4KClteaG9ub3Jfdi50Ll06ICoqaG9ub3Jfdi50LioqOiAqbWFrZSBzdXJlIHRoZSBmcmFuY2hpc2VlcyBob25vdXIgdGhlIHRlcm1zIG9mIHRoZSBjb250cmFjdCo6IGZ1bGZpbCwgb2JzZXJ2ZSwga2VlcCwgZGlzY2hhcmdlLCBpbXBsZW1lbnQsIHBlcmZvcm0sIGV4ZWN1dGUsIGVmZmVjdCwgb2JleSwgaGVlZCwgZm9sbG93LCBjYXJyeSBvdXQsIGNhcnJ5IHRocm91Z2gsIGtlZXAgdG8sIGFiaWRlIGJ5LCBhZGhlcmUgdG8sIGNvbXBseSB3aXRoLCBjb25mb3JtIHRvLCBhY3QgaW4gYWNjb3JkYW5jZSB3aXRoLCBiZSB0cnVlIHRvLCBiZSBmYWl0aGZ1bCB0bywgbGl2ZSB1cCB0bzsgKnJhcmUqIGVmZmVjdHVhdGUuIEFOVE9OWU1TIGRpc29iZXkuClteYWlyZmFyZV06ICoqYWlyZmFyZSoqOiB0aGUgcHJpY2UgdG8gYmUgcGFpZCBieSBhbiBhaXJjcmFmdCBwYXNzZW5nZXIgZm9yIGEgcGFydGljdWxhciBqb3VybmV5OiAqc2F2ZSBhIGJ1bmRsZSBpbiBhaXJmYXJlIGJ5IGZseWluZyBzdGFuZGJ5Ki4KW15ib3VuZF9ib3VuZGFyeV9TeW5dOiAqKlN5bi4qKiDigJMgTGltaXQ7IGJvdW5kOyBib3JkZXI7IHRlcm07IHRlcm1pbmF0aW9uOyBiYXJyaWVyOyB2ZXJnZTsgY29uZmluZXM7IHByZWNpbmN0LiBCb3VuZCwgQm91bmRhcnkuICpCb3VuZGFyeSosIGluIGl0cyBvcmlnaW5hbCBhbmQgc3RyaWN0ZXN0IHNlbnNlLCBpcyBhIHZpc2libGUgb2JqZWN0IG9yIG1hcmsgaW5kaWNhdGluZyBhIGxpbWl0LiAqQm91bmQqIGlzIHRoZSBsaW1pdCBpdHNlbGYuIEJ1dCBpbiBvcmRpbmFyeSB1c2FnZSB0aGUgdHdvIHdvcmRzIGFyZSBtYWRlIGludGVyY2hhbmdlYWJsZS4KCgoKIyMjIFdoYXQncyB0aGUgbWFpbiB0aGluZ3MgdG8gdGFrZSBhd2F5IGZyb20gdGhpcyBjaGFwdGVyPwoKLSBNYW55IHByb2JsZW1zIG9mIHJlYWwgaW1wb3J0YW5jZSBjYW4gYmUgZm9ybXVsYXRlZCBpbiBhIHNpbXBsZSB3YXkgdGhhdCBsZWFkcyBuYXR1cmFsbHkgdG8gYSBjb21wdXRhdGlvbmFsIHNvbHV0aW9uLgotIFJlZHVjaW5nIGEgc2VlbWluZ2x5IG5ldyBwcm9ibGVtIHRvIGFuIGluc3RhbmNlIG9mIGEgd2VsbC1rbm93biBwcm9ibGVtIGFsbG93cyBvbmUgdG8gdXNlIHByZWV4aXN0aW5nIHNvbHV0aW9ucy4KLSBLbmFwc2FjayBwcm9ibGVtcyBhbmQgZ3JhcGggcHJvYmxlbXMgYXJlIGNsYXNzZXMgb2YgcHJvYmxlbXMgdG8gd2hpY2ggb3RoZXIgcHJvYmxlbXMgY2FuIG9mdGVuIGJlIHJlZHVjZWQuCi0gRXhoYXVzdGl2ZSBlbnVtZXJhdGlvbiBhbGdvcml0aG1zIHByb3ZpZGUgYSBzaW1wbGUsIGJ1dCBvZnRlbiBjb21wdXRhdGlvbmFsbHkgaW50cmFjdGFibGUsIHdheSB0byBzZWFyY2ggZm9yIG9wdGltYWwgc29sdXRpb25zLgotIEEgZ3JlZWR5IGFsZ29yaXRobSBpcyBvZnRlbiBhIHByYWN0aWNhbCBhcHByb2FjaCB0byBmaW5kaW5nIGEgcHJldHR5IGdvb2QsIGJ1dCBub3QgYWx3YXlzIG9wdGltYWwsIHNvbHV0aW9uIHRvIGFuIG9wdGltaXphdGlvbiBwcm9ibGVtLgoKIyMgMTIuMSBLbmFwc2Fja1tea25hcHNhY2tdIFByb2JsZW1zCgpbXmtuYXBzYWNrXTogKiprbmFwc2FjayoqOiBub3VuIFJVQ0tTQUNLLCBCQUNLUEFDSywgaGF2ZXJzYWNrLCBwYWNrLCBraXRiYWcsIGR1ZmZlbCBiYWcsIHNhdGNoZWwsIHNob3VsZGVyIGJhZzsgKkJyaXRpc2gqIGhvbGRhbGwuCgojIyMgV2h5IGl0IGlzIG5vdCBlYXN5IGJlaW5nIGEgYnVyZ2xhcj8KCkluIGFkZGl0aW9uIHRvIHRoZSBvYnZpb3VzIHByb2JsZW1zIChtYWtpbmcgc3VyZSB0aGF0IGEgaG9tZSBpcyBlbXB0eSwgcGlja2luZyBsb2NrcywgY2lyY3VtdmVudGluZyBhbGFybXMsIGRlYWxpbmcgd2l0aCBldGhpY2FsIHF1YW5kYXJpZXMsIGV0Yy4pLCBhIGJ1cmdsYXIgaGFzIHRvIGRlY2lkZSB3aGF0IHRvIHN0ZWFsLiBUaGUgcHJvYmxlbSBpcyB0aGF0IG1vc3QgaG9tZXMgY29udGFpbiBtb3JlIHRoaW5ncyBvZiB2YWx1ZSB0aGFuIHRoZSBhdmVyYWdlIGJ1cmdsYXIgY2FuIGNhcnJ5IGF3YXkuIFdoYXTigJlzIGEgcG9vciBidXJnbGFyIHRvIGRvPyBIZSBuZWVkcyB0byBmaW5kIHRoZSBzZXQgb2YgdGhpbmdzIHRoYXQgcHJvdmlkZXMgdGhlIG1vc3QgdmFsdWUgd2l0aG91dCBleGNlZWRpbmcgaGlzIGNhcnJ5aW5nIGNhcGFjaXR5LiBTdXBwb3NlIGZvciBleGFtcGxlLCBhIGJ1cmdsYXIgd2hvIGhhcyBhIGtuYXBzYWNrIHRoYXQgY2FuIGhvbGQgYXQgbW9zdCAyMCBwb3VuZHMgb2YgbG9vdFtebG9vdF0gYnJlYWtzIGludG8gYSBob3VzZSBhbmQgZmluZHMgdGhlIGl0ZW1zIGluIEZpZ3VyZSAxMi4xLiBDbGVhcmx5LCBoZSB3aWxsIG5vdCBiZSBhYmxlIHRvIGZpdCBpdCBhbGwgaW4gaGlzIGtuYXBzYWNrLCBzbyBoZSBuZWVkcyB0byBkZWNpZGUgd2hhdCB0byB0YWtlIGFuZCB3aGF0IHRvIGxlYXZlIGJlaGluZC4KCnwgICAgICAgICAgfCBWYWx1ZSB8IFdlaWdodCB8IFZhbHVlL1dlaWdodCB8CnwgOi0tLS0tLTogfCA6LS0tOiB8IDotLS0tOiB8IDotLS0tLS0tLS0tOiB8CnwgIENsb2NrICAgfCAgMTc1ICB8ICAgMTAgICB8ICAgICAxNy41ICAgICB8CnwgUGFpbnRpbmcgfCAgOTAgICB8ICAgOSAgICB8ICAgICAgMTAgICAgICB8CnwgIFJhZGlvICAgfCAgMjAgICB8ICAgNCAgICB8ICAgICAgNSAgICAgICB8CnwgICBWYXNlICAgfCAgNTAgICB8ICAgMiAgICB8ICAgICAgMjUgICAgICB8CnwgICBCb29rICAgfCAgMTAgICB8ICAgMSAgICB8ICAgICAgMTAgICAgICB8CnwgQ29tcHV0ZXIgfCAgMjAwICB8ICAgMjAgICB8ICAgICAgMTAgICAgICB8Cgo8Y2VudGVyPkZpZ3VyZSAxMi4xIFRhYmxlIG9mIGl0ZW1zPC9jZW50ZXI+CgpbXmxvb3RdOiAqKmxvb3QqKjogUGx1bmRlcjsgYm9vdHk7IGVzcGVjaWFsbHksIHRoZSBib290eSB0YWtlbiBpbiBhIGNvbnF1ZXJlZCBvciBzYWNrZWQgY2l0eS4KCiMjIyBXaGF0IGlzIHRoZSBiYXNpYyBjb25jZXB0IGJlaGluZCBncmVlZHkgYWxnb3JpdGhtPwoKVGhlIHRoaWVmIHdvdWxkIGNob29zZSB0aGUgYmVzdCBpdGVtIGZpcnN0LCB0aGVuIHRoZSBuZXh0IGJlc3QsIGFuZCBjb250aW51ZSB1bnRpbCBoZSByZWFjaGVkIGhpcyBsaW1pdC4gT2YgY291cnNlLCBiZWZvcmUgZG9pbmcgdGhpcywgdGhlIHRoaWVmIHdvdWxkIGhhdmUgdG8gZGVjaWRlIHdoYXQg4oCcYmVzdOKAnSBzaG91bGQgbWVhbi4gSXMgdGhlIGJlc3QgaXRlbSB0aGUgbW9zdCB2YWx1YWJsZSwgdGhlIGxlYXN0IGhlYXZ5LCBvciBtYXliZSB0aGUgaXRlbSB3aXRoIHRoZSBoaWdoZXN0IHZhbHVlLXRvLXdlaWdodCByYXRpbz8KCkRvbid0IHVuZGVyIGxvb2sgYSBncmVlZHkgYWxnb3JpdGhtLiBJdCBpcyBhbiBhbGdvcml0aG06CgotIEl0IHNldHMgdGhlIHJ1bGVyIHRvIGNvbXBhcmU6IHRoZSBiZXN0LgotIEl0IHNob3dzIHRoZSBvcmRlciBvZiBhY3Rpb25zOiBvbmUgYnkgb25lLgotIEl0IHB1dHMgY2xlYXIgYm91bmRhcnk6IGNhcnJ5IHdlaWdodC4KCiMjIyBXaGF0IGlzIHRoZSBpbXBsZW1lbnRhdGlvbiBvZiB0aHJlZSAiYmVzdCIgZ3JlZWR5IGFsZ29yaXRobT8KCmBgYHtweXRob259CmNsYXNzIEl0ZW0gKG9iamVjdCk6CiAgICAiIiIKICAgIENsYXNzIG9mIGxvb3QgaXRlbSwgaW5jbHVkaW5nIHRocmVlIGF0dHJpYnV0ZXM6IDxuYW1lLCB2YWx1ZSwgYW5kIHdlaWdodD4uCiAgICAiIiIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgbiwgdiwgdyk6CiAgICAgICAgIiIiCiAgICAgICAgSW5pdCBhIGluc3RhbmNlIG9mIEl0ZW0gd2l0aCBuYW1lLCB2YWx1ZSBhbmQgd2VpZ2h0LgogICAgICAgIDpwYXJhbSBuOiBzdHJpbmcsIG5hbWUgb2YgaXRlbQogICAgICAgIDpwYXJhbSB2OiBmbG9hdCwgdmFsdWUgb2YgaXRlbQogICAgICAgIDpwYXJhbSB3OiBmbG9hdCwgdmFsdWUgb2YgaXRlbQogICAgICAgICIiIgogICAgICAgIHNlbGYubmFtZSA9IG4KICAgICAgICBzZWxmLnZhbHVlID0gdgogICAgICAgIHNlbGYud2VpZ2h0ID0gdwoKICAgIGRlZiBnZXROYW1lIChzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5uYW1lCgogICAgZGVmIGdldFZhbHVlIChzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi52YWx1ZQoKICAgIGRlZiBnZXRXZWlnaHQgKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLndlaWdodAoKICAgIGRlZiBfX3N0cl9fKHNlbGYpOgogICAgICAgIHJlc3VsdCA9ICc8JyArIHNlbGYubmFtZSArICcsICcgKyBzdHIgKHNlbGYudmFsdWUpXAogICAgICAgICAgICAgICAgICsgJywgJyArIHN0ciAoc2VsZi53ZWlnaHQpICsgJz4nCiAgICAgICAgcmV0dXJuIHJlc3VsdAoKCmRlZiB2YWx1ZSAoaXRlbSk6CiAgICAiIiIKICAgIE1hcCAiaXRlbSIgaW50byBpdHMgdmFsdWUuCiAgICA6cGFyYW0gaXRlbTogYW4gSXRlbSB0eXBlIGl0ZW0KICAgIDpyZXR1cm46IHRoaXMgaXRlbSdzIHZhbHVlCiAgICAiIiIKICAgIHJldHVybiBpdGVtLmdldFZhbHVlICgpCgoKZGVmIHdlaWdodEludmVyc2UgKGl0ZW0pOgogICAgIiIiCiAgICBNYXAgIml0ZW0iIGludG8gaXRzIHdlaWdodCdzIGludmVyc2UgdmFsdWUuCiAgICA6cGFyYW0gaXRlbTogYW4gSXRlbSB0eXBlIGl0ZW0KICAgIDpyZXR1cm46IHRoaXMgaXRlbSdzIDEvd2VpZ2h0CiAgICAiIiIKICAgIHJldHVybiAxLjAvaXRlbS5nZXRXZWlnaHQgKCkKCgpkZWYgZGVuc2l0eSAoaXRlbSk6CiAgICAiIiIKICAgIE1hcCAiaXRlbSIgaW50byBpdHMgZGVuc2l0eSh2YWx1ZS93ZWlnaHQpLgogICAgOnBhcmFtIGl0ZW06IGFuIEl0ZW0gdHlwZSBpdGVtCiAgICA6cmV0dXJuOiB0aGlzIGl0ZW0ncyB2YWx1ZS93ZWlnaHQKICAgICIiIgogICAgcmV0dXJuIGl0ZW0uZ2V0VmFsdWUgKCkvaXRlbS5nZXRXZWlnaHQgKCkKCgpkZWYgZ3JlZWR5IChpdGVtcywgbWF4V2VpZ2h0LCBrZXlGdW5jdGlvbik6CiAgICAiIiIKICAgIEdyZWVkeSBpcyB0aGUgY29yZSBwcm9jZXNzIG9mIGdyZWVkeSBhbGdvcml0aG0uIEFjY29yZGluZyB0byB0aGUga2V5RnVuY3Rpb24sCiAgICBpdCBwdXRzIHRoZSBiZXN0IGl0ZW1zIGludG8ga25hcHNhY2sgdW50aWwgaXQgaXMgZnVsbC4KICAgIDpwYXJhbSBpdGVtczogbGlzdCwgb2YgaW5zdGFuY2VzIG9mIEl0ZW0uCiAgICA6cGFyYW0gbWF4V2VpZ2h0OiBmbG9hdCA+PSAwLCB0aGUgbWF4aW11bSB3ZWlnaHQgYSBrbmFwc2FjayBjYW4gY29udGFpbi4KICAgIDpwYXJhbSBrZXlGdW5jdGlvbjogZnVuY3Rpb24sCiAgICBtYXBzIGFuIGl0ZW0gaW4gYSBsaXN0IHRvIGFuIGNvbXBhcmFibGUgdmFsdWUgYWNjb3JkaW5nbHksCiAgICBhcyB0aGUgc2Vjb25kIHBhcmFtZXRlciBvZiB0aGUgYHNvcnRlZGAgZnVuY3Rpb24KICAgIDpyZXR1cm46IGEgbGlzdCBvZiB0YWtlbiBpdGVtcywgYW5kIGEgZmxvYXQgb2YgdGhlaXIgdG90YWwgdmFsdWUKICAgICIiIgogICAgIyBVc2UgIndoYXQgaXMgbXkgYmVzdCIgYXMgdGhlIGtleSBmdW5jdGlvbiB0byBzb3J0IGEgY29waWVkIGxpc3Qgb2YgdGhlIGl0ZW1zLgogICAgIyBOb3RlIHRoZSBgc29ydGVkYCdzIGFsZ29yaXRobSBlZmZpY2llbmN5IGlzIGluIE8obiAqIGxvZyhuKSkuCiAgICBpdGVtc0NvcHkgPSBzb3J0ZWQgKGl0ZW1zLCBrZXkgPSBrZXlGdW5jdGlvbiwgcmV2ZXJzZSA9IFRydWUpCiAgICByZXN1bHQgPSBbXQogICAgdG90YWxWYWx1ZSwgdG90YWxXZWlnaHQgPSAwLjAsIDAuMAogICAgIyBUcnkgZWFjaCBpdGVtIGluIHRoZSBzb3J0ZWQgaXRlbXMgbGlzdCBkZXNjZW5kaW5nbHkuCiAgICBmb3IgaSBpbiByYW5nZSAobGVuIChpdGVtc0NvcHkpKToKICAgICAgICAjIENoZWNrIHdoZXRoZXIgdGhlIG5leHQgYmlnZ2VzdCBvbmUgY291bGQgYmUgdGFrZW4gaW50byBrbmFwc2Fjay4KICAgICAgICBpZiAodG90YWxXZWlnaHQgKyBpdGVtc0NvcHlbaV0uZ2V0V2VpZ2h0ICgpKSA8PSBtYXhXZWlnaHQ6CiAgICAgICAgICAgIHJlc3VsdC5hcHBlbmQgKGl0ZW1zQ29weVtpXSkKICAgICAgICAgICAgIyBJZiBuZXcgaXRlbSBpcyB0YWtlbiwgdXBkYXRlIHRoZSB0b3RhbCB3ZWlnaHQgYW5kIHZhbHVlIG9mIHJlc3VsdAogICAgICAgICAgICB0b3RhbFdlaWdodCArPSBpdGVtc0NvcHlbaV0uZ2V0V2VpZ2h0ICgpCiAgICAgICAgICAgIHRvdGFsVmFsdWUgKz0gaXRlbXNDb3B5W2ldLmdldFZhbHVlICgpCiAgICByZXR1cm4gcmVzdWx0LCB0b3RhbFZhbHVlCgoKZGVmIGJ1aWxkX2l0ZW1zICgpOgogICAgIiIiCiAgICBCdWlsZCBhIGxpc3Qgb2YgSXRlbXMgaW5zdGFuY2VzLgogICAgOnJldHVybjogYSBsaXN0IG9mIEl0ZW1zIGluc3RhbmNlcy4KICAgICIiIgogICAgbmFtZXMgPSBbJ2Nsb2NrJywncGFpbnRpbmcnLCdyYWRpbycsJ3Zhc2UnLCdib29rJywnY29tcHV0ZXInXQogICAgdmFsdWVzID0gWzE3NSw5MCwyMCw1MCwxMCwyMDBdCiAgICB3ZWlnaHRzID0gWzEwLDksNCwyLDEsMjBdCiAgICBpdGVtcyA9IFtdCiAgICBmb3IgaSBpbiByYW5nZSAobGVuKHZhbHVlcykpOgogICAgICAgIGl0ZW1zLmFwcGVuZCAoSXRlbShuYW1lc1tpXSwgdmFsdWVzW2ldLCB3ZWlnaHRzW2ldKSkKICAgIHJldHVybiBpdGVtcwoKCmRlZiB0ZXN0X2dyZWVkeSAoaXRlbXMsIG1heFdlaWdodCwga2V5RnVuY3Rpb24pOgogICAgIiIiCiAgICBDYWxjdWxhdGUgZm9yIG9uZSBzb3J0IG9mIGdyZWVkeSBzdHJhdGVneSwgYW5kIHByaW50IGl0cyByZXN1bHQuCiAgICA6cGFyYW0gaXRlbXM6IGEgbGlzdCBvZiBJdGVtcyBpbnN0YW5jZXMuCiAgICA6cGFyYW0gbWF4V2VpZ2h0OiBmbG9hdCwgYXMgdGhlIG1heGltdW0gd2VpZ2h0IGEga25hcHNhY2sgY2FuIGhvbGQuCiAgICA6cGFyYW0ga2V5RnVuY3Rpb246IGZ1bmN0aW9uLAogICAgbWFwcyBhbiBpdGVtIGluIGEgbGlzdCB0byBhbiBjb21wYXJhYmxlIHZhbHVlIGFjY29yZGluZ2x5LAogICAgYXMgdGhlIHNlY29uZCBwYXJhbWV0ZXIgb2YgdGhlIGBzb3J0ZWRgIGZ1bmN0aW9uCiAgICA6cmV0dXJuOiBOb25lLiBQcmludCBvdXQgdG90YWwgdmFsdWUgYW5kIGl0ZW1zIGluIHRha2VuIGxvb3QuCiAgICAiIiIKICAgIHRha2VuLCB2YWwgPSBncmVlZHkgKGl0ZW1zLCBtYXhXZWlnaHQsIGtleUZ1bmN0aW9uKQogICAgcHJpbnQgKCdUb3RhbCB2YWx1ZSBvZiBpdGVtcyB0YWtlbiBpcycsIHZhbCkKICAgIGZvciBpdGVtIGluIHRha2VuOgogICAgICAgIHByaW50ICgnICcsIGl0ZW0pCgoKZGVmIHRlc3RfZGlmZl9ncmVlZHlzIChtYXhXZWlnaHQgPSAyMCk6CiAgICAiIiIKICAgIFByaW50IG91dCB0aGUgcmVzdWx0cyBvZiBkaWZmZXJlbnQgc29ydHMgb2YgZ3JlZWR5IHN0cmF0ZWd5LgogICAgOnBhcmFtIG1heFdlaWdodDogZmxvYXQsIGFzIHRoZSBtYXhpbXVtIHdlaWdodCBhIGtuYXBzYWNrIGNhbiBob2xkLAogICAgd2l0aCBkZWZhdWx0IHZhbHVlIDIwLjAuCiAgICA6cmV0dXJuOiBOb25lLiBQcmludCBvdXQgdG90YWwgdmFsdWUgYW5kIGl0ZW1zIGluIHRha2VuIGxvb3Qgb2YgdGhyZWUgc29ydHMgb2Ygc3RyYXRlZ3kuCiAgICAiIiIKICAgIGl0ZW1zID0gYnVpbGRfaXRlbXMgKCkKICAgICMgR3JlZWR5IGJ5IHZhbHVlLgogICAgcHJpbnQgKCdVc2UgZ3JlZWR5IGJ5IHZhbHVlIHRvIGZpbGwga25hcHNhY2sgb2Ygc2l6ZScsIG1heFdlaWdodCkKICAgIHRlc3RfZ3JlZWR5IChpdGVtcywgbWF4V2VpZ2h0LCB2YWx1ZSkKICAgICMgR3JlZWR5IGJ5IHdlaWdodC4KICAgIHByaW50ICgnXG5Vc2UgZ3JlZWR5IGJ5IHdlaWdodCB0byBmaWxsIGtuYXBzYWNrIG9mIHNpemUnLAogICAgICAgICAgbWF4V2VpZ2h0KQogICAgdGVzdF9ncmVlZHkgKGl0ZW1zLCBtYXhXZWlnaHQsIHdlaWdodEludmVyc2UpCiAgICAjIEdyZWVkeSBieSBkZW5zaXR5LgogICAgcHJpbnQgKCdcblVzZSBncmVlZHkgYnkgZGVuc2l0eSB0byBmaWxsIGtuYXBzYWNrIG9mIHNpemUnLAogICAgICAgICAgbWF4V2VpZ2h0KQogICAgdGVzdF9ncmVlZHkgKGl0ZW1zLCBtYXhXZWlnaHQsIGRlbnNpdHkpCmBgYAoKSGVyZSB3ZSBydW4gaXQuCgpgYGB7cHl0aG9ufQp0ZXN0X2RpZmZfZ3JlZWR5cyhtYXhXZWlnaHQ9MjAuMCkKYGBgCgojIyMgSG93IHRvIGZvcm1hbGl6ZSB0aGUgMC8xIGtuYXBzYWNrIHByb2JsZW0gYXMgYW4gaW5zdGFuY2Ugb2YgYSBjbGFzc2ljIG9wdGltaXphdGlvbiBwcm9ibGVtPwoKVGhlIDAvMSBrbmFwc2FjayBwcm9ibGVtIGNhbiBiZSBmb3JtYWxpemVkIGFzIGZvbGxvd3M6CgoxLiBFYWNoIGl0ZW0gaXMgcmVwcmVzZW50ZWQgYnkgYSBwYWlyLCA8dmFsdWUsIHdlaWdodD4uCjEuIFRoZSBrbmFwc2FjayBjYW4gYWNjb21tb2RhdGUgaXRlbXMgd2l0aCBhIHRvdGFsIHdlaWdodCBvZiBubyBtb3JlIHRoYW4gdy4KMS4gQSB2ZWN0b3IsIEksIG9mIGxlbmd0aCBuLCByZXByZXNlbnRzIHRoZSBzZXQgb2YgYXZhaWxhYmxlIGl0ZW1zLiBFYWNoIGVsZW1lbnQgb2YgdGhlIHZlY3RvciBpcyBhbiBpdGVtLgoxLiBBIHZlY3RvciwgViwgb2YgbGVuZ3RoIG4sIGlzIHVzZWQgdG8gaW5kaWNhdGUgd2hldGhlciBvciBub3QgZWFjaCBpdGVtIGlzIHRha2VuIGJ5IHRoZSBidXJnbGFyLiBJZiBWW2ldID0gMSwgaXRlbSBJW2ldIGlzIHRha2VuLiBJZiBWW2ldID0gMCwgaXRlbSBJW2ldIGlzIG5vdCB0YWtlbi4KMS4gRmluZCBhIFYgdGhhdCBtYXhpbWl6ZXMKCiQkClxzdW1fe2k9MH1ee24tMX0gVltpXV57Kn0gSVtpXSAuIFx0ZXh0IHsgdmFsdWUgfQokJAoK4oCLCQlzdWJqZWN0IHRvIHRoZSBjb25zdHJhaW50IHRoYXQKJCQKXHN1bV97aT0wfV57bi0xfSBWW2ldICogSVtpXSAuIFx0ZXh0IHt3ZWlnaHR9IFxsZXEgdwokJAoKIyMjIEhvdyB0byBpbXBsZW1lbnQgdGhlIDAvMSBrbmFwc2FjayBwcm9ibGVtJ3MgZm9ybWFsaXphdGlvbiB3aGljaCBpcyBpbmhlcmVudGx5IGV4cG9uZW50aWFsIGluIHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gYSBzdHJhaWdodGZvcndhcmQgd2F5PwoKYGBge3B5dGhvbn0KZGVmIGdldF9iaW5hcnlfcmVwKHBvc2l0aXZlX2ludGVnZXI6IGludCwgbnVtX2JpX2RpZ2l0czogaW50KToKICAgICIiIgogICAgR2V0IGludCBuJ3MgYmluYXJ5IHJlcHJlc2VudGF0aW9uIGluIGEgZml4IGJpbmFyeSBkaWdpdHMgd2l0aCBsZWFkaW5nIHplcm9zLgogICAgOnBhcmFtIHBvc2l0aXZlX2ludGVnZXI6IGludCwgYSBub24tbmVnYXRpdmUgZGVjaW1hbCBpbnRlZ2VyIHRvIGJlIHR1cm5lZCBpbnRvIGJpbmFyeSBudW1iZXIuCiAgICA6cGFyYW0gbnVtX2JpX2RpZ2l0czogaW50LCBhIG5vbi1uZWdhdGl2ZSBkZWNpbWFsIGludGVnZXIgdG8gc2V0IGEgZml4ZWQgbGVuZ3RoIG9mIHRoZQogICAgcmVzdWx0aW5nIGJpbmFyeSBudW1iZXIgZGlnaXRzLiBJdCBzaG91bGQgYmUgYmlnIGVub3VnaC4KICAgIDpyZXR1cm46IGEgc3RyaW5nIG9mICcwJ3MgYW5kICcxJ3Mgb2YgbGVuZ3RoIG51bURpZ2l0cyB0aGF0IGlzIGEgYmluYXJ5IHJlcHJlc2VudGF0aW9uCiAgICBvZiBuLgogICAgIiIiCiAgICBiaW5hcnlfc3RyID0gJycKICAgIHdoaWxlIHBvc2l0aXZlX2ludGVnZXIgPiAwOgogICAgICAgIGJpbmFyeV9zdHIgPSBzdHIocG9zaXRpdmVfaW50ZWdlciAlIDIpICsgYmluYXJ5X3N0cgogICAgICAgIHBvc2l0aXZlX2ludGVnZXIgPSBwb3NpdGl2ZV9pbnRlZ2VyIC8vIDIKICAgIGlmIGxlbihiaW5hcnlfc3RyKSA+IG51bV9iaV9kaWdpdHM6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcignbm90IGVub3VnaCBkaWdpdHMnKQogICAgZm9yIGkgaW4gcmFuZ2UobnVtX2JpX2RpZ2l0cy0gbGVuKGJpbmFyeV9zdHIpKToKICAgICAgICBiaW5hcnlfc3RyID0gJzAnICsgYmluYXJ5X3N0cgogICAgcmV0dXJuIGJpbmFyeV9zdHIKCmRlZiBnZW5fcG93ZXJfc2V0IChvcmlfbGlzdCk6ICMgTygyKipsZW4ob3JpX2xpc3QpKSAqIE8obGVuIChvcmlfbGlzdCkpCiAgICAiIiIKICAgIEdlbmVyYXRlIHRoZSBwb3dlciBzZXQgb2Ygb3JpX2xpc3QsIGluY2x1ZGluZyBhbGwgc3Vic2V0cyBvZiB0aGUgc2V0IG9mCiAgICAgb3JpX2xpc3QncyBpdGVtcy4KICAgIEUuZy4sIGlmIG9yaV9saXN0IGlzIFsxLCAyXSBpdCB3aWxsIHJldHVybiBhIGxpc3Qgd2l0aCBlbGVtZW50cwogICAgW10sIFsxXSwgWzJdLCBhbmQgWzEsMl0uCiAgICBSZWNhbGwgdGhhdCBldmVyeSBzZXQgaXMgYSBzdWJzZXQgb2YgaXRzZWxmIGFuZCB0aGUgZW1wdHkgc2V0IGlzIGEgc3Vic2V0IG9mIGV2ZXJ5IHNldC4KICAgIFNvIHRoZSBsZW5ndGggb2YgdGhlIHBvd2VyIHNldCBpcyAyKipsZW4ob3JpX2xpc3QpLgogICAgOnBhcmFtIG9yaV9saXN0OiBsaXN0CiAgICA6cmV0dXJuOiBhIGxpc3Qgb2YgbGlzdHMgdGhhdCBjb250YWlucyBhbGwgcG9zc2libGUgY29tYmluYXRpb25zIG9mCiAgICB0aGUgZWxlbWVudHMgb2Ygb3JpX2xpc3QuCiAgICAiIiIKICAgIHBvd2VyX3NldCA9IFtdCiAgICBmb3IgaSBpbiByYW5nZSAoMCwgMioqbGVuKG9yaV9saXN0KSk6ICMgTygyKipsZW4ob3JpX2xpc3QpKQogICAgICAgIGJpbl9zdHIgPSBnZXRfYmluYXJ5X3JlcChpLCBsZW4ob3JpX2xpc3QpKQogICAgICAgIHN1YnNldCA9IFtdCiAgICAgICAgZm9yIGogaW4gcmFuZ2UobGVuKG9yaV9saXN0KSk6ICMgTyhsZW4ob3JpX2xpc3QpKQogICAgICAgICAgIGlmIGJpbl9zdHJbal0gPT0gJzEnOgogICAgICAgICAgICAgIHN1YnNldC5hcHBlbmQob3JpX2xpc3Rbal0pCiAgICAgICAgcG93ZXJfc2V0LmFwcGVuZChzdWJzZXQpCiAgICByZXR1cm4gcG93ZXJfc2V0CgpkZWYgY2hvb3NlX2Jlc3QocHNldCwgbWF4X3dlaWdodCwgZ2V0X3ZhbCwgZ2V0X3dlaWdodCk6CiAgICAiIiIKICAgIENob29zZSB0aGUgYmVzdCBzZXQgb2YgaXRlbXMgZnJvbSB0aGUgcG93ZXIgc2V0IG9mIG9yaWdpbmFsIGxpc3QsCiAgICB3aXRoaW4gdGhlIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2Fjay4KICAgIDpwYXJhbSBwc2V0OiBsaXN0IG9mIGxpc3RzLCB0aGF0IGNvbnRhaW5zIGFsbCBwb3NzaWJsZSBjb21iaW5hdGlvbnMgb2YKICAgIHRoZSBlbGVtZW50cyBvZiBvcmlfbGlzdC4KICAgIDpwYXJhbSBtYXhfd2VpZ2h0OiBmbG9hdCBvciBpbnQsIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2Fjay4KICAgIDpwYXJhbSBnZXRfdmFsOiBmdW5jdGlvbiBvciBtZXRob2QsIG1hcCBhbiBpdGVtIGluIHBzZXQncyBsaXN0IGludG8gaXRzCiAgICBjb21wYXJhYmxlIHZhbHVlCiAgICA6cGFyYW0gZ2V0X3dlaWdodDogZnVuY3Rpb24gb3IgbWV0aG9kLCBtYXAgYW4gaXRlbSBpbiBwc2V0J3MgbGlzdCBpbnRvIGl0cwogICAgY29tcGFyYWJsZSB3ZWlnaHQKICAgIDpyZXR1cm46IGEgYmVzdCBzZXQgb2YgaXRlbXMsIGFuZCB0aGlzIGJlc3Qgc2V0J3MgdG90YWwgdmFsdWUKICAgICIiIgogICAgYmVzdF92YWwgPSAwLjAKICAgIGJlc3Rfc2V0ID0gTm9uZQogICAgZm9yIGl0ZW1zIGluIHBzZXQ6CiAgICAgICAgaXRlbXNfdmFsID0gMC4wCiAgICAgICAgaXRlbXNfd2VpZ2h0ID0gMC4wCiAgICAgICAgZm9yIGl0ZW0gaW4gaXRlbXM6CiAgICAgICAgICAgIGl0ZW1zX3ZhbCArPSBnZXRfdmFsKGl0ZW0pCiAgICAgICAgICAgIGl0ZW1zX3dlaWdodCArPSBnZXRfd2VpZ2h0KGl0ZW0pCiAgICAgICAgaWYgaXRlbXNfd2VpZ2h0IDw9IG1heF93ZWlnaHQgYW5kIGl0ZW1zX3ZhbCA+IGJlc3RfdmFsOgogICAgICAgICAgICBiZXN0X3ZhbCA9IGl0ZW1zX3ZhbAogICAgICAgICAgICBiZXN0X3NldCA9IGl0ZW1zCiAgICByZXR1cm4gYmVzdF9zZXQsIGJlc3RfdmFsCgpkZWYgdGVzdF9iZXN0KG1heF93ZWlnaHQgPSAyMCk6CiAgICAiIiIKICAgIFJ1biB0aGUgY2hvb3NlX2Jlc3QgZnVuY3Rpb24sIGFuZCBwcmludCBpdHMgcmVzdWx0IGluIHByb3BlciBmb3JtYXQuCiAgICA6cGFyYW0gbWF4X3dlaWdodDogZmxvYXQgb3IgaW50LCBtYXhpbXVtIHdlaWdodCBidWRnZXQgb2Yga25hcHNhY2ssIGRlZmF1bHQgMjAuCiAgICA6cmV0dXJuOiBOb25lLCBwcmludCBvdXQgdGhlIGl0ZW1zIHRha2VuIGFuZCB0aGVpciB0b3RhbCB2YWx1ZS4KICAgICIiIgogICAgaXRlbXMgPSBidWlsZF9pdGVtcygpCiAgICBwc2V0ID0gZ2VuX3Bvd2VyX3NldChpdGVtcykKICAgIHRha2VuLCB2YWwgPSBjaG9vc2VfYmVzdChwc2V0LCBtYXhfd2VpZ2h0LCBJdGVtLmdldFZhbHVlLCBJdGVtLmdldFdlaWdodCkKICAgIHByaW50KCdUb3RhbCB2YWx1ZSBvZiBpdGVtcyB0YWtlbiBpcycsIHZhbCkKICAgIGZvciBpdGVtIGluIHRha2VuOgogICAgICAgIHByaW50KGl0ZW0pCmBgYAoKUnVuIGB0ZXN0X2Jlc3RgIHNjcmlwdC4KCmBgYHtweXRob259CnRlc3RfYmVzdChtYXhfd2VpZ2h0PTIwKQpgYGAKClRoaXMgcmVzdWx0IGlzIGRpZmZlcmVudCBmcm9tIHRoZSByZXN1bHQgb2YgZGVuc2l0eS4KCiMjIyBDb3VsZCB0aGlzIHN0cmFpZ2h0Zm9yd2FyZCB3YXkgYmUgc3BlZWRlZCB1cD8KCjAxLlRoZSBgZ2VuX3Bvd2VyX3NldGAgY291bGQgaGF2ZSBoYWQgdGhlIGhlYWRlcgoKYGBgcHl0aG9uCmRlZiBnZW5Qb3dlcnNldChpdGVtcywgY29uc3RyYWludCwgZ2V0VmFsLCBnZXRXZWlnaHQpCmBgYAogIAlhbmQgcmV0dXJuZWQgb25seSB0aG9zZSBjb21iaW5hdGlvbnMgdGhhdCBtZWV0IHRoZSB3ZWlnaHQgY29uc3RyYWludC4KCk9yCgowMi5UaGUgYGNob29zZV9iZXN0YCBjb3VsZCBleGl0IHRoZSBpbm5lciBsb29wIGFzIHNvb24gYXMgdGhlIHdlaWdodCBjb25zdHJhaW50IGlzIGV4Y2VlZGVkLgoKYGBge3B5dGhvbn0KIyBkZWYgZ2VuX3Bvd2VyX3NldCAob3JpX2xpc3QpOiAjIE8oMioqbGVuKG9yaV9saXN0KSkgKiBPKGxlbiAob3JpX2xpc3QpKQpkZWYgZ2VuX3Bvd2VyX3NldF93aXRoaW5fY29uc3RyYWludChpdGVtcywgY29uc3RyYWludCwgZ2V0V2VpZ2h0KToKICAgICIiIgogICAgR2VuZXJhdGUgdGhlIHBvd2VyIHNldCBvZiBpdGVtcyB3aGljaCBtZWV0IHRoZSB3ZWlnaHQgY29uc3RyYWludC4KICAgIDpwYXJhbSBpdGVtczogbGlzdCwgaW5zdGFuY2VzIG9mIEl0ZW1zLgogICAgOnBhcmFtIGNvbnN0cmFpbnQ6IGZsb2F0LCBtYXhpbXVtIHdlaWdodCBvZiBrbmFwc2Fjay4KICAgIDpwYXJhbSBnZXRXZWlnaHQ6IG1ldGhvZCwgZ2V0IHdlaWdodCBvZiBhbiBJdGVtLgogICAgOnJldHVybjogYSBsaXN0IG9mIGxpc3RzIHRoYXQgY29udGFpbnMgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZgogICAgdGhlIGVsZW1lbnRzIHRoYXQgbWVldCB0aGUgd2VpZ2h0IGNvbnN0cmFpbnQuCiAgICAiIiIKICAgIHBvd2VyX3NldF93aXRoaW5fY29uc3RyYWludCA9IFtdCiAgICBmb3IgaSBpbiByYW5nZSAoMCwgMioqbGVuKGl0ZW1zKSk6ICMgTygyKipsZW4ob3JpX2xpc3QpKQogICAgICAgIGJpbl9zdHIgPSBnZXRfYmluYXJ5X3JlcChpLCBsZW4oaXRlbXMpKQogICAgICAgIHN1YnNldCA9IFtdCiAgICAgICAgc3Vic2V0X3RvdGFsX3dlaWdodCA9IDAKICAgICAgICBmb3IgaiBpbiByYW5nZShsZW4oaXRlbXMpKTogIyBPKGxlbihvcmlfbGlzdCkpCiAgICAgICAgICAgIGlmIGJpbl9zdHJbal0gPT0gJzEnOgogICAgICAgICAgICAgICAgc3Vic2V0LmFwcGVuZChpdGVtc1tqXSkKICAgICAgICAgICAgICAgIHN1YnNldF90b3RhbF93ZWlnaHQgKz0gaXRlbXNbal0uZ2V0V2VpZ2h0KCkKICAgICAgICBpZiBzdWJzZXRfdG90YWxfd2VpZ2h0IDw9IGNvbnN0cmFpbnQ6CiAgICAgICAgICAgIHBvd2VyX3NldF93aXRoaW5fY29uc3RyYWludC5hcHBlbmQoc3Vic2V0KQogICAgcmV0dXJuIHBvd2VyX3NldF93aXRoaW5fY29uc3RyYWludAoKIyBkZWYgY2hvb3NlX2Jlc3QocHNldCwgbWF4X3dlaWdodCwgZ2V0X3ZhbCwgZ2V0X3dlaWdodCk6CmRlZiBjaG9vc2VfYmVzdF9lZmZpY2llbnRseShwc2V0LCBtYXhfd2VpZ2h0LCBnZXRfdmFsLCBnZXRfd2VpZ2h0KToKICAgICIiIgogICAgQ2hvb3NlIHRoZSBiZXN0IHNldCBvZiBpdGVtcyBmcm9tIHRoZSBwb3dlciBzZXQgb2Ygb3JpZ2luYWwgbGlzdCwKICAgIHdpdGhpbiB0aGUgbWF4aW11bSB3ZWlnaHQgYnVkZ2V0IG9mIGtuYXBzYWNrLgogICAgOnBhcmFtIHBzZXQ6IGxpc3Qgb2YgbGlzdHMsIHRoYXQgY29udGFpbnMgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZgogICAgdGhlIGVsZW1lbnRzIG9mIG9yaV9saXN0LgogICAgOnBhcmFtIG1heF93ZWlnaHQ6IGZsb2F0IG9yIGludCwgbWF4aW11bSB3ZWlnaHQgYnVkZ2V0IG9mIGtuYXBzYWNrLgogICAgOnBhcmFtIGdldF92YWw6IGZ1bmN0aW9uIG9yIG1ldGhvZCwgbWFwIGFuIGl0ZW0gaW4gcHNldCdzIGxpc3QgaW50byBpdHMKICAgIGNvbXBhcmFibGUgdmFsdWUKICAgIDpwYXJhbSBnZXRfd2VpZ2h0OiBmdW5jdGlvbiBvciBtZXRob2QsIG1hcCBhbiBpdGVtIGluIHBzZXQncyBsaXN0IGludG8gaXRzCiAgICBjb21wYXJhYmxlIHdlaWdodAogICAgOnJldHVybjogYSBiZXN0IHNldCBvZiBpdGVtcywgYW5kIHRoaXMgYmVzdCBzZXQncyB0b3RhbCB2YWx1ZQogICAgIiIiCiAgICBiZXN0X3ZhbCA9IDAuMAogICAgYmVzdF9zZXQgPSBOb25lCiAgICBmb3IgaXRlbXMgaW4gcHNldDoKICAgICAgICBpdGVtc192YWwgPSAwLjAKICAgICAgICBpdGVtc193ZWlnaHQgPSAwLjAKICAgICAgICBmb3IgaXRlbSBpbiBpdGVtczoKICAgICAgICAgICAgaXRlbXNfdmFsICs9IGdldF92YWwoaXRlbSkKICAgICAgICAgICAgaXRlbXNfd2VpZ2h0ICs9IGdldF93ZWlnaHQoaXRlbSkKICAgICAgICAgICAgaWYgaXRlbXNfd2VpZ2h0ID4gbWF4X3dlaWdodDoKICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgaWYgaXRlbXNfd2VpZ2h0IDw9IG1heF93ZWlnaHQgYW5kIGl0ZW1zX3ZhbCA+IGJlc3RfdmFsOgogICAgICAgICAgICBiZXN0X3ZhbCA9IGl0ZW1zX3ZhbAogICAgICAgICAgICBiZXN0X3NldCA9IGl0ZW1zCiAgICByZXR1cm4gYmVzdF9zZXQsIGJlc3RfdmFsCgojIGRlZiB0ZXN0X2Jlc3QobWF4X3dlaWdodCA9IDIwKToKZGVmIHRlc3RfYmVzdF9vcHRfMDEobWF4X3dlaWdodCA9IDIwKToKICAgICIiIgogICAgUnVuIHRoZSBjaG9vc2VfYmVzdCBmdW5jdGlvbiwgYW5kIHByaW50IGl0cyByZXN1bHQgaW4gcHJvcGVyIGZvcm1hdC4KICAgIDpwYXJhbSBtYXhfd2VpZ2h0OiBmbG9hdCBvciBpbnQsIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2FjaywgZGVmYXVsdCAyMC4KICAgIDpyZXR1cm46IE5vbmUsIHByaW50IG91dCB0aGUgaXRlbXMgdGFrZW4gYW5kIHRoZWlyIHRvdGFsIHZhbHVlLgogICAgIiIiCiAgICBpdGVtcyA9IGJ1aWxkX2l0ZW1zKCkKICAgIHBzZXRfd2l0aGluX2NvbnN0cmFpbnQgPSBnZW5fcG93ZXJfc2V0X3dpdGhpbl9jb25zdHJhaW50KGl0ZW1zLCBjb25zdHJhaW50PW1heF93ZWlnaHQsIGdldFdlaWdodD1JdGVtLmdldFdlaWdodCkKICAgIHRha2VuLCB2YWwgPSBjaG9vc2VfYmVzdChwc2V0X3dpdGhpbl9jb25zdHJhaW50LCBtYXhfd2VpZ2h0LCBJdGVtLmdldFZhbHVlLCBJdGVtLmdldFdlaWdodCkKICAgIHByaW50KCdUb3RhbCB2YWx1ZSBvZiBpdGVtcyB0YWtlbiBpcycsIHZhbCkKICAgIGZvciBpdGVtIGluIHRha2VuOgogICAgICAgIHByaW50KGl0ZW0pCgojIGRlZiB0ZXN0X2Jlc3QobWF4X3dlaWdodCA9IDIwKToKZGVmIHRlc3RfYmVzdF9vcHRfMDIobWF4X3dlaWdodCA9IDIwKToKICAgICIiIgogICAgUnVuIHRoZSBjaG9vc2VfYmVzdCBmdW5jdGlvbiwgYW5kIHByaW50IGl0cyByZXN1bHQgaW4gcHJvcGVyIGZvcm1hdC4KICAgIDpwYXJhbSBtYXhfd2VpZ2h0OiBmbG9hdCBvciBpbnQsIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2FjaywgZGVmYXVsdCAyMC4KICAgIDpyZXR1cm46IE5vbmUsIHByaW50IG91dCB0aGUgaXRlbXMgdGFrZW4gYW5kIHRoZWlyIHRvdGFsIHZhbHVlLgogICAgIiIiCiAgICBpdGVtcyA9IGJ1aWxkX2l0ZW1zKCkKICAgIHBzZXQgPSBnZW5fcG93ZXJfc2V0KGl0ZW1zKQogICAgdGFrZW4sIHZhbCA9IGNob29zZV9iZXN0X2VmZmljaWVudGx5KHBzZXQsIG1heF93ZWlnaHQsIEl0ZW0uZ2V0VmFsdWUsIEl0ZW0uZ2V0V2VpZ2h0KQogICAgcHJpbnQoJ1RvdGFsIHZhbHVlIG9mIGl0ZW1zIHRha2VuIGlzJywgdmFsKQogICAgZm9yIGl0ZW0gaW4gdGFrZW46CiAgICAgICAgcHJpbnQoaXRlbSkKYGBgCgoKYGBge3B5dGhvbn0KdGVzdF9iZXN0X29wdF8wMShtYXhfd2VpZ2h0PTIwKQpgYGAKCgpgYGB7cHl0aG9ufQp0ZXN0X2Jlc3Rfb3B0XzAyKG1heF93ZWlnaHQ9MjApCmBgYAoKV2hpbGUgdGhlc2Uga2luZHMgb2Ygb3B0aW1pemF0aW9ucyBhcmUgb2Z0ZW4gd29ydGggZG9pbmcsIHRoZXkgZG9u4oCZdCBhZGRyZXNzIHRoZSBmdW5kYW1lbnRhbCBpc3N1ZS4g44CQ5ZC05Yab6ICB5biI5YaZ5aW9566X5rOV5ZCO77yM5bel56iL5Li7566h6KaB5LuW5YGa5LyY5YyW77yM55yB5py65Zmo5bCx5piv566X6ZKx44CC44CRCgpgYGAK