What are the characters of optimization problem?

In general, an optimization problem has two parts:

  1. An objective function that is to be maximized or minimized. For example, the airfare1 between Boston and Istanbul.
  2. A set of constraints (possibly empty) that must be honored2. For example, an upper bound3 on the travel time.

What’s the main things to take away from this chapter?

12.1 Knapsack4 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 loot5 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.

Value Weight Value/Weight
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)

How to formalize the 0/1 knapsack problem as an instance of a classic optimization problem?

The 0/1 knapsack problem can be formalized as follows:

  1. Each item is represented by a pair, <value, weight>.
  2. The knapsack can accommodate items with a total weight of no more than w.
  3. A vector, I, of length n, represents the set of available items. Each element of the vector is an item.
  4. A vector, V, of length n, is used to indicate whether or not each item is taken by the burglar. If V[i] = 1, item I[i] is taken. If V[i] = 0, item I[i] is not taken.
  5. Find a V that maximizes

\[ \sum_{i=0}^{n-1} V[i]^{*} I[i] . \text { value } \]

​ subject to the constraint that \[ \sum_{i=0}^{n-1} V[i] * I[i] . \text {weight} \leq w \]

How to implement the 0/1 knapsack problem’s formalization which is inherently exponential in the number of items in a straightforward way?

def get_binary_rep(positive_integer: int, num_bi_digits: int):
    """
    Get int n's binary representation in a fix binary digits with leading zeros.
    :param positive_integer: int, a non-negative decimal integer to be turned into binary number.
    :param num_bi_digits: int, a non-negative decimal integer to set a fixed length of the
    resulting binary number digits. It should be big enough.
    :return: a string of '0's and '1's of length numDigits that is a binary representation
    of n.
    """
    binary_str = ''
    while positive_integer > 0:
        binary_str = str(positive_integer % 2) + binary_str
        positive_integer = positive_integer // 2
    if len(binary_str) > num_bi_digits:
        raise ValueError('not enough digits')
    for i in range(num_bi_digits- len(binary_str)):
        binary_str = '0' + binary_str
    return binary_str

def gen_power_set (ori_list): # O(2**len(ori_list)) * O(len (ori_list))
    """
    Generate the power set of ori_list, including all subsets of the set of
     ori_list's items.
    E.g., if ori_list is [1, 2] it will return a list with elements
    [], [1], [2], and [1,2].
    Recall that every set is a subset of itself and the empty set is a subset of every set.
    So the length of the power set is 2**len(ori_list).
    :param ori_list: list
    :return: a list of lists that contains all possible combinations of
    the elements of ori_list.
    """
    power_set = []
    for i in range (0, 2**len(ori_list)): # O(2**len(ori_list))
        bin_str = get_binary_rep(i, len(ori_list))
        subset = []
        for j in range(len(ori_list)): # O(len(ori_list))
           if bin_str[j] == '1':
              subset.append(ori_list[j])
        power_set.append(subset)
    return power_set

def choose_best(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 and items_val > best_val:
            best_val = items_val
            best_set = items
    return best_set, best_val

def test_best(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(pset, max_weight, Item.getValue, Item.getWeight)
    print('Total value of items taken is', val)
    for item in taken:
        print(item)

Run test_best script.

test_best(max_weight=20)

This result is different from the result of density.

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. 【吴军老师写好算法后,工程主管要他做优化,省机器就是算钱。】

```


  1. airfare: the price to be paid by an aircraft passenger for a particular journey: save a bundle in airfare by flying standby.

  2. honor_v.t.: make sure the franchisees honour the terms of the contract: fulfil, observe, keep, discharge, implement, perform, execute, effect, obey, heed, follow, carry out, carry through, keep to, abide by, adhere to, comply with, conform to, act in accordance with, be true to, be faithful to, live up to; rare effectuate. ANTONYMS disobey.

  3. Syn. – Limit; bound; border; term; termination; barrier; verge; confines; precinct. Bound, Boundary. Boundary, in its original and strictest sense, is a visible object or mark indicating a limit. Bound is the limit itself. But in ordinary usage the two words are made interchangeable.

  4. knapsack: noun RUCKSACK, BACKPACK, haversack, pack, kitbag, duffel bag, satchel, shoulder bag; British holdall.

  5. loot: Plunder; booty; especially, the booty taken in a conquered or sacked city.

LS0tCnRpdGxlOiAxMiBLTkFQU0FDSyBBTkQgR1JBUEggT1BUSU1JWkFUSU9OIFBST0JMRU1TCnN1YnRpdGxlOiAKYXV0aG9yOiBKb2huIFF1CmRhdGU6IDIwMjAtMDYtMTMKc2x1ZzogCnRhZ3M6Ci0gCmNhdGVnb3JpZXM6Ci0gbWl0NjAwCnR5cG9yYS1yb290LXVybDogLi4vLi4vc3RhdGljCnNob3dfdG9jOiB5ZXMKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKIyMjIFdoYXQgYXJlIHRoZSBjaGFyYWN0ZXJzIG9mIG9wdGltaXphdGlvbiBwcm9ibGVtPwoKSW4gZ2VuZXJhbCwgYW4gb3B0aW1pemF0aW9uIHByb2JsZW0gaGFzIHR3byBwYXJ0czoKCjEuIEFuIG9iamVjdGl2ZSBmdW5jdGlvbiB0aGF0IGlzIHRvIGJlIG1heGltaXplZCBvciBtaW5pbWl6ZWQuIEZvciBleGFtcGxlLCB0aGUgYWlyZmFyZVteYWlyZmFyZV0gYmV0d2VlbiBCb3N0b24gYW5kIElzdGFuYnVsLgoxLiBBIHNldCBvZiBjb25zdHJhaW50cyAocG9zc2libHkgZW1wdHkpIHRoYXQgbXVzdCBiZSBob25vcmVkW15ob25vcl92LnQuXS4gRm9yIGV4YW1wbGUsIGFuIHVwcGVyIGJvdW5kW15ib3VuZF9ib3VuZGFyeV9TeW5dIG9uIHRoZSB0cmF2ZWwgdGltZS4KClteaG9ub3Jfdi50Ll06ICoqaG9ub3Jfdi50LioqOiAqbWFrZSBzdXJlIHRoZSBmcmFuY2hpc2VlcyBob25vdXIgdGhlIHRlcm1zIG9mIHRoZSBjb250cmFjdCo6IGZ1bGZpbCwgb2JzZXJ2ZSwga2VlcCwgZGlzY2hhcmdlLCBpbXBsZW1lbnQsIHBlcmZvcm0sIGV4ZWN1dGUsIGVmZmVjdCwgb2JleSwgaGVlZCwgZm9sbG93LCBjYXJyeSBvdXQsIGNhcnJ5IHRocm91Z2gsIGtlZXAgdG8sIGFiaWRlIGJ5LCBhZGhlcmUgdG8sIGNvbXBseSB3aXRoLCBjb25mb3JtIHRvLCBhY3QgaW4gYWNjb3JkYW5jZSB3aXRoLCBiZSB0cnVlIHRvLCBiZSBmYWl0aGZ1bCB0bywgbGl2ZSB1cCB0bzsgKnJhcmUqIGVmZmVjdHVhdGUuIEFOVE9OWU1TIGRpc29iZXkuClteYWlyZmFyZV06ICoqYWlyZmFyZSoqOiB0aGUgcHJpY2UgdG8gYmUgcGFpZCBieSBhbiBhaXJjcmFmdCBwYXNzZW5nZXIgZm9yIGEgcGFydGljdWxhciBqb3VybmV5OiAqc2F2ZSBhIGJ1bmRsZSBpbiBhaXJmYXJlIGJ5IGZseWluZyBzdGFuZGJ5Ki4KW15ib3VuZF9ib3VuZGFyeV9TeW5dOiAqKlN5bi4qKiDigJMgTGltaXQ7IGJvdW5kOyBib3JkZXI7IHRlcm07IHRlcm1pbmF0aW9uOyBiYXJyaWVyOyB2ZXJnZTsgY29uZmluZXM7IHByZWNpbmN0LiBCb3VuZCwgQm91bmRhcnkuICpCb3VuZGFyeSosIGluIGl0cyBvcmlnaW5hbCBhbmQgc3RyaWN0ZXN0IHNlbnNlLCBpcyBhIHZpc2libGUgb2JqZWN0IG9yIG1hcmsgaW5kaWNhdGluZyBhIGxpbWl0LiAqQm91bmQqIGlzIHRoZSBsaW1pdCBpdHNlbGYuIEJ1dCBpbiBvcmRpbmFyeSB1c2FnZSB0aGUgdHdvIHdvcmRzIGFyZSBtYWRlIGludGVyY2hhbmdlYWJsZS4KCgoKIyMjIFdoYXQncyB0aGUgbWFpbiB0aGluZ3MgdG8gdGFrZSBhd2F5IGZyb20gdGhpcyBjaGFwdGVyPwoKLSBNYW55IHByb2JsZW1zIG9mIHJlYWwgaW1wb3J0YW5jZSBjYW4gYmUgZm9ybXVsYXRlZCBpbiBhIHNpbXBsZSB3YXkgdGhhdCBsZWFkcyBuYXR1cmFsbHkgdG8gYSBjb21wdXRhdGlvbmFsIHNvbHV0aW9uLgotIFJlZHVjaW5nIGEgc2VlbWluZ2x5IG5ldyBwcm9ibGVtIHRvIGFuIGluc3RhbmNlIG9mIGEgd2VsbC1rbm93biBwcm9ibGVtIGFsbG93cyBvbmUgdG8gdXNlIHByZWV4aXN0aW5nIHNvbHV0aW9ucy4KLSBLbmFwc2FjayBwcm9ibGVtcyBhbmQgZ3JhcGggcHJvYmxlbXMgYXJlIGNsYXNzZXMgb2YgcHJvYmxlbXMgdG8gd2hpY2ggb3RoZXIgcHJvYmxlbXMgY2FuIG9mdGVuIGJlIHJlZHVjZWQuCi0gRXhoYXVzdGl2ZSBlbnVtZXJhdGlvbiBhbGdvcml0aG1zIHByb3ZpZGUgYSBzaW1wbGUsIGJ1dCBvZnRlbiBjb21wdXRhdGlvbmFsbHkgaW50cmFjdGFibGUsIHdheSB0byBzZWFyY2ggZm9yIG9wdGltYWwgc29sdXRpb25zLgotIEEgZ3JlZWR5IGFsZ29yaXRobSBpcyBvZnRlbiBhIHByYWN0aWNhbCBhcHByb2FjaCB0byBmaW5kaW5nIGEgcHJldHR5IGdvb2QsIGJ1dCBub3QgYWx3YXlzIG9wdGltYWwsIHNvbHV0aW9uIHRvIGFuIG9wdGltaXphdGlvbiBwcm9ibGVtLgoKIyMgMTIuMSBLbmFwc2Fja1tea25hcHNhY2tdIFByb2JsZW1zCgpbXmtuYXBzYWNrXTogKiprbmFwc2FjayoqOiBub3VuIFJVQ0tTQUNLLCBCQUNLUEFDSywgaGF2ZXJzYWNrLCBwYWNrLCBraXRiYWcsIGR1ZmZlbCBiYWcsIHNhdGNoZWwsIHNob3VsZGVyIGJhZzsgKkJyaXRpc2gqIGhvbGRhbGwuCgojIyMgV2h5IGl0IGlzIG5vdCBlYXN5IGJlaW5nIGEgYnVyZ2xhcj8KCkluIGFkZGl0aW9uIHRvIHRoZSBvYnZpb3VzIHByb2JsZW1zIChtYWtpbmcgc3VyZSB0aGF0IGEgaG9tZSBpcyBlbXB0eSwgcGlja2luZyBsb2NrcywgY2lyY3VtdmVudGluZyBhbGFybXMsIGRlYWxpbmcgd2l0aCBldGhpY2FsIHF1YW5kYXJpZXMsIGV0Yy4pLCBhIGJ1cmdsYXIgaGFzIHRvIGRlY2lkZSB3aGF0IHRvIHN0ZWFsLiBUaGUgcHJvYmxlbSBpcyB0aGF0IG1vc3QgaG9tZXMgY29udGFpbiBtb3JlIHRoaW5ncyBvZiB2YWx1ZSB0aGFuIHRoZSBhdmVyYWdlIGJ1cmdsYXIgY2FuIGNhcnJ5IGF3YXkuIFdoYXTigJlzIGEgcG9vciBidXJnbGFyIHRvIGRvPyBIZSBuZWVkcyB0byBmaW5kIHRoZSBzZXQgb2YgdGhpbmdzIHRoYXQgcHJvdmlkZXMgdGhlIG1vc3QgdmFsdWUgd2l0aG91dCBleGNlZWRpbmcgaGlzIGNhcnJ5aW5nIGNhcGFjaXR5LiBTdXBwb3NlIGZvciBleGFtcGxlLCBhIGJ1cmdsYXIgd2hvIGhhcyBhIGtuYXBzYWNrIHRoYXQgY2FuIGhvbGQgYXQgbW9zdCAyMCBwb3VuZHMgb2YgbG9vdFtebG9vdF0gYnJlYWtzIGludG8gYSBob3VzZSBhbmQgZmluZHMgdGhlIGl0ZW1zIGluIEZpZ3VyZSAxMi4xLiBDbGVhcmx5LCBoZSB3aWxsIG5vdCBiZSBhYmxlIHRvIGZpdCBpdCBhbGwgaW4gaGlzIGtuYXBzYWNrLCBzbyBoZSBuZWVkcyB0byBkZWNpZGUgd2hhdCB0byB0YWtlIGFuZCB3aGF0IHRvIGxlYXZlIGJlaGluZC4KCnwgICAgICAgICAgfCBWYWx1ZSB8IFdlaWdodCB8IFZhbHVlL1dlaWdodCB8CnwgOi0tLS0tLTogfCA6LS0tOiB8IDotLS0tOiB8IDotLS0tLS0tLS0tOiB8CnwgIENsb2NrICAgfCAgMTc1ICB8ICAgMTAgICB8ICAgICAxNy41ICAgICB8CnwgUGFpbnRpbmcgfCAgOTAgICB8ICAgOSAgICB8ICAgICAgMTAgICAgICB8CnwgIFJhZGlvICAgfCAgMjAgICB8ICAgNCAgICB8ICAgICAgNSAgICAgICB8CnwgICBWYXNlICAgfCAgNTAgICB8ICAgMiAgICB8ICAgICAgMjUgICAgICB8CnwgICBCb29rICAgfCAgMTAgICB8ICAgMSAgICB8ICAgICAgMTAgICAgICB8CnwgQ29tcHV0ZXIgfCAgMjAwICB8ICAgMjAgICB8ICAgICAgMTAgICAgICB8Cgo8Y2VudGVyPkZpZ3VyZSAxMi4xIFRhYmxlIG9mIGl0ZW1zPC9jZW50ZXI+CgpbXmxvb3RdOiAqKmxvb3QqKjogUGx1bmRlcjsgYm9vdHk7IGVzcGVjaWFsbHksIHRoZSBib290eSB0YWtlbiBpbiBhIGNvbnF1ZXJlZCBvciBzYWNrZWQgY2l0eS4KCiMjIyBXaGF0IGlzIHRoZSBiYXNpYyBjb25jZXB0IGJlaGluZCBncmVlZHkgYWxnb3JpdGhtPwoKVGhlIHRoaWVmIHdvdWxkIGNob29zZSB0aGUgYmVzdCBpdGVtIGZpcnN0LCB0aGVuIHRoZSBuZXh0IGJlc3QsIGFuZCBjb250aW51ZSB1bnRpbCBoZSByZWFjaGVkIGhpcyBsaW1pdC4gT2YgY291cnNlLCBiZWZvcmUgZG9pbmcgdGhpcywgdGhlIHRoaWVmIHdvdWxkIGhhdmUgdG8gZGVjaWRlIHdoYXQg4oCcYmVzdOKAnSBzaG91bGQgbWVhbi4gSXMgdGhlIGJlc3QgaXRlbSB0aGUgbW9zdCB2YWx1YWJsZSwgdGhlIGxlYXN0IGhlYXZ5LCBvciBtYXliZSB0aGUgaXRlbSB3aXRoIHRoZSBoaWdoZXN0IHZhbHVlLXRvLXdlaWdodCByYXRpbz8KCkRvbid0IHVuZGVyIGxvb2sgYSBncmVlZHkgYWxnb3JpdGhtLiBJdCBpcyBhbiBhbGdvcml0aG06CgotIEl0IHNldHMgdGhlIHJ1bGVyIHRvIGNvbXBhcmU6IHRoZSBiZXN0LgotIEl0IHNob3dzIHRoZSBvcmRlciBvZiBhY3Rpb25zOiBvbmUgYnkgb25lLgotIEl0IHB1dHMgY2xlYXIgYm91bmRhcnk6IGNhcnJ5IHdlaWdodC4KCiMjIyBXaGF0IGlzIHRoZSBpbXBsZW1lbnRhdGlvbiBvZiB0aHJlZSAiYmVzdCIgZ3JlZWR5IGFsZ29yaXRobT8KCmBgYHtweXRob259CmNsYXNzIEl0ZW0gKG9iamVjdCk6CiAgICAiIiIKICAgIENsYXNzIG9mIGxvb3QgaXRlbSwgaW5jbHVkaW5nIHRocmVlIGF0dHJpYnV0ZXM6IDxuYW1lLCB2YWx1ZSwgYW5kIHdlaWdodD4uCiAgICAiIiIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgbiwgdiwgdyk6CiAgICAgICAgIiIiCiAgICAgICAgSW5pdCBhIGluc3RhbmNlIG9mIEl0ZW0gd2l0aCBuYW1lLCB2YWx1ZSBhbmQgd2VpZ2h0LgogICAgICAgIDpwYXJhbSBuOiBzdHJpbmcsIG5hbWUgb2YgaXRlbQogICAgICAgIDpwYXJhbSB2OiBmbG9hdCwgdmFsdWUgb2YgaXRlbQogICAgICAgIDpwYXJhbSB3OiBmbG9hdCwgdmFsdWUgb2YgaXRlbQogICAgICAgICIiIgogICAgICAgIHNlbGYubmFtZSA9IG4KICAgICAgICBzZWxmLnZhbHVlID0gdgogICAgICAgIHNlbGYud2VpZ2h0ID0gdwoKICAgIGRlZiBnZXROYW1lIChzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5uYW1lCgogICAgZGVmIGdldFZhbHVlIChzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi52YWx1ZQoKICAgIGRlZiBnZXRXZWlnaHQgKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLndlaWdodAoKICAgIGRlZiBfX3N0cl9fKHNlbGYpOgogICAgICAgIHJlc3VsdCA9ICc8JyArIHNlbGYubmFtZSArICcsICcgKyBzdHIgKHNlbGYudmFsdWUpXAogICAgICAgICAgICAgICAgICsgJywgJyArIHN0ciAoc2VsZi53ZWlnaHQpICsgJz4nCiAgICAgICAgcmV0dXJuIHJlc3VsdAoKCmRlZiB2YWx1ZSAoaXRlbSk6CiAgICAiIiIKICAgIE1hcCAiaXRlbSIgaW50byBpdHMgdmFsdWUuCiAgICA6cGFyYW0gaXRlbTogYW4gSXRlbSB0eXBlIGl0ZW0KICAgIDpyZXR1cm46IHRoaXMgaXRlbSdzIHZhbHVlCiAgICAiIiIKICAgIHJldHVybiBpdGVtLmdldFZhbHVlICgpCgoKZGVmIHdlaWdodEludmVyc2UgKGl0ZW0pOgogICAgIiIiCiAgICBNYXAgIml0ZW0iIGludG8gaXRzIHdlaWdodCdzIGludmVyc2UgdmFsdWUuCiAgICA6cGFyYW0gaXRlbTogYW4gSXRlbSB0eXBlIGl0ZW0KICAgIDpyZXR1cm46IHRoaXMgaXRlbSdzIDEvd2VpZ2h0CiAgICAiIiIKICAgIHJldHVybiAxLjAvaXRlbS5nZXRXZWlnaHQgKCkKCgpkZWYgZGVuc2l0eSAoaXRlbSk6CiAgICAiIiIKICAgIE1hcCAiaXRlbSIgaW50byBpdHMgZGVuc2l0eSh2YWx1ZS93ZWlnaHQpLgogICAgOnBhcmFtIGl0ZW06IGFuIEl0ZW0gdHlwZSBpdGVtCiAgICA6cmV0dXJuOiB0aGlzIGl0ZW0ncyB2YWx1ZS93ZWlnaHQKICAgICIiIgogICAgcmV0dXJuIGl0ZW0uZ2V0VmFsdWUgKCkvaXRlbS5nZXRXZWlnaHQgKCkKCgpkZWYgZ3JlZWR5IChpdGVtcywgbWF4V2VpZ2h0LCBrZXlGdW5jdGlvbik6CiAgICAiIiIKICAgIEdyZWVkeSBpcyB0aGUgY29yZSBwcm9jZXNzIG9mIGdyZWVkeSBhbGdvcml0aG0uIEFjY29yZGluZyB0byB0aGUga2V5RnVuY3Rpb24sCiAgICBpdCBwdXRzIHRoZSBiZXN0IGl0ZW1zIGludG8ga25hcHNhY2sgdW50aWwgaXQgaXMgZnVsbC4KICAgIDpwYXJhbSBpdGVtczogbGlzdCwgb2YgaW5zdGFuY2VzIG9mIEl0ZW0uCiAgICA6cGFyYW0gbWF4V2VpZ2h0OiBmbG9hdCA+PSAwLCB0aGUgbWF4aW11bSB3ZWlnaHQgYSBrbmFwc2FjayBjYW4gY29udGFpbi4KICAgIDpwYXJhbSBrZXlGdW5jdGlvbjogZnVuY3Rpb24sCiAgICBtYXBzIGFuIGl0ZW0gaW4gYSBsaXN0IHRvIGFuIGNvbXBhcmFibGUgdmFsdWUgYWNjb3JkaW5nbHksCiAgICBhcyB0aGUgc2Vjb25kIHBhcmFtZXRlciBvZiB0aGUgYHNvcnRlZGAgZnVuY3Rpb24KICAgIDpyZXR1cm46IGEgbGlzdCBvZiB0YWtlbiBpdGVtcywgYW5kIGEgZmxvYXQgb2YgdGhlaXIgdG90YWwgdmFsdWUKICAgICIiIgogICAgIyBVc2UgIndoYXQgaXMgbXkgYmVzdCIgYXMgdGhlIGtleSBmdW5jdGlvbiB0byBzb3J0IGEgY29waWVkIGxpc3Qgb2YgdGhlIGl0ZW1zLgogICAgIyBOb3RlIHRoZSBgc29ydGVkYCdzIGFsZ29yaXRobSBlZmZpY2llbmN5IGlzIGluIE8obiAqIGxvZyhuKSkuCiAgICBpdGVtc0NvcHkgPSBzb3J0ZWQgKGl0ZW1zLCBrZXkgPSBrZXlGdW5jdGlvbiwgcmV2ZXJzZSA9IFRydWUpCiAgICByZXN1bHQgPSBbXQogICAgdG90YWxWYWx1ZSwgdG90YWxXZWlnaHQgPSAwLjAsIDAuMAogICAgIyBUcnkgZWFjaCBpdGVtIGluIHRoZSBzb3J0ZWQgaXRlbXMgbGlzdCBkZXNjZW5kaW5nbHkuCiAgICBmb3IgaSBpbiByYW5nZSAobGVuIChpdGVtc0NvcHkpKToKICAgICAgICAjIENoZWNrIHdoZXRoZXIgdGhlIG5leHQgYmlnZ2VzdCBvbmUgY291bGQgYmUgdGFrZW4gaW50byBrbmFwc2Fjay4KICAgICAgICBpZiAodG90YWxXZWlnaHQgKyBpdGVtc0NvcHlbaV0uZ2V0V2VpZ2h0ICgpKSA8PSBtYXhXZWlnaHQ6CiAgICAgICAgICAgIHJlc3VsdC5hcHBlbmQgKGl0ZW1zQ29weVtpXSkKICAgICAgICAgICAgIyBJZiBuZXcgaXRlbSBpcyB0YWtlbiwgdXBkYXRlIHRoZSB0b3RhbCB3ZWlnaHQgYW5kIHZhbHVlIG9mIHJlc3VsdAogICAgICAgICAgICB0b3RhbFdlaWdodCArPSBpdGVtc0NvcHlbaV0uZ2V0V2VpZ2h0ICgpCiAgICAgICAgICAgIHRvdGFsVmFsdWUgKz0gaXRlbXNDb3B5W2ldLmdldFZhbHVlICgpCiAgICByZXR1cm4gcmVzdWx0LCB0b3RhbFZhbHVlCgoKZGVmIGJ1aWxkX2l0ZW1zICgpOgogICAgIiIiCiAgICBCdWlsZCBhIGxpc3Qgb2YgSXRlbXMgaW5zdGFuY2VzLgogICAgOnJldHVybjogYSBsaXN0IG9mIEl0ZW1zIGluc3RhbmNlcy4KICAgICIiIgogICAgbmFtZXMgPSBbJ2Nsb2NrJywncGFpbnRpbmcnLCdyYWRpbycsJ3Zhc2UnLCdib29rJywnY29tcHV0ZXInXQogICAgdmFsdWVzID0gWzE3NSw5MCwyMCw1MCwxMCwyMDBdCiAgICB3ZWlnaHRzID0gWzEwLDksNCwyLDEsMjBdCiAgICBpdGVtcyA9IFtdCiAgICBmb3IgaSBpbiByYW5nZSAobGVuKHZhbHVlcykpOgogICAgICAgIGl0ZW1zLmFwcGVuZCAoSXRlbShuYW1lc1tpXSwgdmFsdWVzW2ldLCB3ZWlnaHRzW2ldKSkKICAgIHJldHVybiBpdGVtcwoKCmRlZiB0ZXN0X2dyZWVkeSAoaXRlbXMsIG1heFdlaWdodCwga2V5RnVuY3Rpb24pOgogICAgIiIiCiAgICBDYWxjdWxhdGUgZm9yIG9uZSBzb3J0IG9mIGdyZWVkeSBzdHJhdGVneSwgYW5kIHByaW50IGl0cyByZXN1bHQuCiAgICA6cGFyYW0gaXRlbXM6IGEgbGlzdCBvZiBJdGVtcyBpbnN0YW5jZXMuCiAgICA6cGFyYW0gbWF4V2VpZ2h0OiBmbG9hdCwgYXMgdGhlIG1heGltdW0gd2VpZ2h0IGEga25hcHNhY2sgY2FuIGhvbGQuCiAgICA6cGFyYW0ga2V5RnVuY3Rpb246IGZ1bmN0aW9uLAogICAgbWFwcyBhbiBpdGVtIGluIGEgbGlzdCB0byBhbiBjb21wYXJhYmxlIHZhbHVlIGFjY29yZGluZ2x5LAogICAgYXMgdGhlIHNlY29uZCBwYXJhbWV0ZXIgb2YgdGhlIGBzb3J0ZWRgIGZ1bmN0aW9uCiAgICA6cmV0dXJuOiBOb25lLiBQcmludCBvdXQgdG90YWwgdmFsdWUgYW5kIGl0ZW1zIGluIHRha2VuIGxvb3QuCiAgICAiIiIKICAgIHRha2VuLCB2YWwgPSBncmVlZHkgKGl0ZW1zLCBtYXhXZWlnaHQsIGtleUZ1bmN0aW9uKQogICAgcHJpbnQgKCdUb3RhbCB2YWx1ZSBvZiBpdGVtcyB0YWtlbiBpcycsIHZhbCkKICAgIGZvciBpdGVtIGluIHRha2VuOgogICAgICAgIHByaW50ICgnICcsIGl0ZW0pCgoKZGVmIHRlc3RfZGlmZl9ncmVlZHlzIChtYXhXZWlnaHQgPSAyMCk6CiAgICAiIiIKICAgIFByaW50IG91dCB0aGUgcmVzdWx0cyBvZiBkaWZmZXJlbnQgc29ydHMgb2YgZ3JlZWR5IHN0cmF0ZWd5LgogICAgOnBhcmFtIG1heFdlaWdodDogZmxvYXQsIGFzIHRoZSBtYXhpbXVtIHdlaWdodCBhIGtuYXBzYWNrIGNhbiBob2xkLAogICAgd2l0aCBkZWZhdWx0IHZhbHVlIDIwLjAuCiAgICA6cmV0dXJuOiBOb25lLiBQcmludCBvdXQgdG90YWwgdmFsdWUgYW5kIGl0ZW1zIGluIHRha2VuIGxvb3Qgb2YgdGhyZWUgc29ydHMgb2Ygc3RyYXRlZ3kuCiAgICAiIiIKICAgIGl0ZW1zID0gYnVpbGRfaXRlbXMgKCkKICAgICMgR3JlZWR5IGJ5IHZhbHVlLgogICAgcHJpbnQgKCdVc2UgZ3JlZWR5IGJ5IHZhbHVlIHRvIGZpbGwga25hcHNhY2sgb2Ygc2l6ZScsIG1heFdlaWdodCkKICAgIHRlc3RfZ3JlZWR5IChpdGVtcywgbWF4V2VpZ2h0LCB2YWx1ZSkKICAgICMgR3JlZWR5IGJ5IHdlaWdodC4KICAgIHByaW50ICgnXG5Vc2UgZ3JlZWR5IGJ5IHdlaWdodCB0byBmaWxsIGtuYXBzYWNrIG9mIHNpemUnLAogICAgICAgICAgbWF4V2VpZ2h0KQogICAgdGVzdF9ncmVlZHkgKGl0ZW1zLCBtYXhXZWlnaHQsIHdlaWdodEludmVyc2UpCiAgICAjIEdyZWVkeSBieSBkZW5zaXR5LgogICAgcHJpbnQgKCdcblVzZSBncmVlZHkgYnkgZGVuc2l0eSB0byBmaWxsIGtuYXBzYWNrIG9mIHNpemUnLAogICAgICAgICAgbWF4V2VpZ2h0KQogICAgdGVzdF9ncmVlZHkgKGl0ZW1zLCBtYXhXZWlnaHQsIGRlbnNpdHkpCmBgYAoKSGVyZSB3ZSBydW4gaXQuCgpgYGB7cHl0aG9ufQp0ZXN0X2RpZmZfZ3JlZWR5cyhtYXhXZWlnaHQ9MjAuMCkKYGBgCgojIyMgSG93IHRvIGZvcm1hbGl6ZSB0aGUgMC8xIGtuYXBzYWNrIHByb2JsZW0gYXMgYW4gaW5zdGFuY2Ugb2YgYSBjbGFzc2ljIG9wdGltaXphdGlvbiBwcm9ibGVtPwoKVGhlIDAvMSBrbmFwc2FjayBwcm9ibGVtIGNhbiBiZSBmb3JtYWxpemVkIGFzIGZvbGxvd3M6CgoxLiBFYWNoIGl0ZW0gaXMgcmVwcmVzZW50ZWQgYnkgYSBwYWlyLCA8dmFsdWUsIHdlaWdodD4uCjEuIFRoZSBrbmFwc2FjayBjYW4gYWNjb21tb2RhdGUgaXRlbXMgd2l0aCBhIHRvdGFsIHdlaWdodCBvZiBubyBtb3JlIHRoYW4gdy4KMS4gQSB2ZWN0b3IsIEksIG9mIGxlbmd0aCBuLCByZXByZXNlbnRzIHRoZSBzZXQgb2YgYXZhaWxhYmxlIGl0ZW1zLiBFYWNoIGVsZW1lbnQgb2YgdGhlIHZlY3RvciBpcyBhbiBpdGVtLgoxLiBBIHZlY3RvciwgViwgb2YgbGVuZ3RoIG4sIGlzIHVzZWQgdG8gaW5kaWNhdGUgd2hldGhlciBvciBub3QgZWFjaCBpdGVtIGlzIHRha2VuIGJ5IHRoZSBidXJnbGFyLiBJZiBWW2ldID0gMSwgaXRlbSBJW2ldIGlzIHRha2VuLiBJZiBWW2ldID0gMCwgaXRlbSBJW2ldIGlzIG5vdCB0YWtlbi4KMS4gRmluZCBhIFYgdGhhdCBtYXhpbWl6ZXMKCiQkClxzdW1fe2k9MH1ee24tMX0gVltpXV57Kn0gSVtpXSAuIFx0ZXh0IHsgdmFsdWUgfQokJAoK4oCLCQlzdWJqZWN0IHRvIHRoZSBjb25zdHJhaW50IHRoYXQKJCQKXHN1bV97aT0wfV57bi0xfSBWW2ldICogSVtpXSAuIFx0ZXh0IHt3ZWlnaHR9IFxsZXEgdwokJAoKIyMjIEhvdyB0byBpbXBsZW1lbnQgdGhlIDAvMSBrbmFwc2FjayBwcm9ibGVtJ3MgZm9ybWFsaXphdGlvbiB3aGljaCBpcyBpbmhlcmVudGx5IGV4cG9uZW50aWFsIGluIHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gYSBzdHJhaWdodGZvcndhcmQgd2F5PwoKYGBge3B5dGhvbn0KZGVmIGdldF9iaW5hcnlfcmVwKHBvc2l0aXZlX2ludGVnZXI6IGludCwgbnVtX2JpX2RpZ2l0czogaW50KToKICAgICIiIgogICAgR2V0IGludCBuJ3MgYmluYXJ5IHJlcHJlc2VudGF0aW9uIGluIGEgZml4IGJpbmFyeSBkaWdpdHMgd2l0aCBsZWFkaW5nIHplcm9zLgogICAgOnBhcmFtIHBvc2l0aXZlX2ludGVnZXI6IGludCwgYSBub24tbmVnYXRpdmUgZGVjaW1hbCBpbnRlZ2VyIHRvIGJlIHR1cm5lZCBpbnRvIGJpbmFyeSBudW1iZXIuCiAgICA6cGFyYW0gbnVtX2JpX2RpZ2l0czogaW50LCBhIG5vbi1uZWdhdGl2ZSBkZWNpbWFsIGludGVnZXIgdG8gc2V0IGEgZml4ZWQgbGVuZ3RoIG9mIHRoZQogICAgcmVzdWx0aW5nIGJpbmFyeSBudW1iZXIgZGlnaXRzLiBJdCBzaG91bGQgYmUgYmlnIGVub3VnaC4KICAgIDpyZXR1cm46IGEgc3RyaW5nIG9mICcwJ3MgYW5kICcxJ3Mgb2YgbGVuZ3RoIG51bURpZ2l0cyB0aGF0IGlzIGEgYmluYXJ5IHJlcHJlc2VudGF0aW9uCiAgICBvZiBuLgogICAgIiIiCiAgICBiaW5hcnlfc3RyID0gJycKICAgIHdoaWxlIHBvc2l0aXZlX2ludGVnZXIgPiAwOgogICAgICAgIGJpbmFyeV9zdHIgPSBzdHIocG9zaXRpdmVfaW50ZWdlciAlIDIpICsgYmluYXJ5X3N0cgogICAgICAgIHBvc2l0aXZlX2ludGVnZXIgPSBwb3NpdGl2ZV9pbnRlZ2VyIC8vIDIKICAgIGlmIGxlbihiaW5hcnlfc3RyKSA+IG51bV9iaV9kaWdpdHM6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcignbm90IGVub3VnaCBkaWdpdHMnKQogICAgZm9yIGkgaW4gcmFuZ2UobnVtX2JpX2RpZ2l0cy0gbGVuKGJpbmFyeV9zdHIpKToKICAgICAgICBiaW5hcnlfc3RyID0gJzAnICsgYmluYXJ5X3N0cgogICAgcmV0dXJuIGJpbmFyeV9zdHIKCmRlZiBnZW5fcG93ZXJfc2V0IChvcmlfbGlzdCk6ICMgTygyKipsZW4ob3JpX2xpc3QpKSAqIE8obGVuIChvcmlfbGlzdCkpCiAgICAiIiIKICAgIEdlbmVyYXRlIHRoZSBwb3dlciBzZXQgb2Ygb3JpX2xpc3QsIGluY2x1ZGluZyBhbGwgc3Vic2V0cyBvZiB0aGUgc2V0IG9mCiAgICAgb3JpX2xpc3QncyBpdGVtcy4KICAgIEUuZy4sIGlmIG9yaV9saXN0IGlzIFsxLCAyXSBpdCB3aWxsIHJldHVybiBhIGxpc3Qgd2l0aCBlbGVtZW50cwogICAgW10sIFsxXSwgWzJdLCBhbmQgWzEsMl0uCiAgICBSZWNhbGwgdGhhdCBldmVyeSBzZXQgaXMgYSBzdWJzZXQgb2YgaXRzZWxmIGFuZCB0aGUgZW1wdHkgc2V0IGlzIGEgc3Vic2V0IG9mIGV2ZXJ5IHNldC4KICAgIFNvIHRoZSBsZW5ndGggb2YgdGhlIHBvd2VyIHNldCBpcyAyKipsZW4ob3JpX2xpc3QpLgogICAgOnBhcmFtIG9yaV9saXN0OiBsaXN0CiAgICA6cmV0dXJuOiBhIGxpc3Qgb2YgbGlzdHMgdGhhdCBjb250YWlucyBhbGwgcG9zc2libGUgY29tYmluYXRpb25zIG9mCiAgICB0aGUgZWxlbWVudHMgb2Ygb3JpX2xpc3QuCiAgICAiIiIKICAgIHBvd2VyX3NldCA9IFtdCiAgICBmb3IgaSBpbiByYW5nZSAoMCwgMioqbGVuKG9yaV9saXN0KSk6ICMgTygyKipsZW4ob3JpX2xpc3QpKQogICAgICAgIGJpbl9zdHIgPSBnZXRfYmluYXJ5X3JlcChpLCBsZW4ob3JpX2xpc3QpKQogICAgICAgIHN1YnNldCA9IFtdCiAgICAgICAgZm9yIGogaW4gcmFuZ2UobGVuKG9yaV9saXN0KSk6ICMgTyhsZW4ob3JpX2xpc3QpKQogICAgICAgICAgIGlmIGJpbl9zdHJbal0gPT0gJzEnOgogICAgICAgICAgICAgIHN1YnNldC5hcHBlbmQob3JpX2xpc3Rbal0pCiAgICAgICAgcG93ZXJfc2V0LmFwcGVuZChzdWJzZXQpCiAgICByZXR1cm4gcG93ZXJfc2V0CgpkZWYgY2hvb3NlX2Jlc3QocHNldCwgbWF4X3dlaWdodCwgZ2V0X3ZhbCwgZ2V0X3dlaWdodCk6CiAgICAiIiIKICAgIENob29zZSB0aGUgYmVzdCBzZXQgb2YgaXRlbXMgZnJvbSB0aGUgcG93ZXIgc2V0IG9mIG9yaWdpbmFsIGxpc3QsCiAgICB3aXRoaW4gdGhlIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2Fjay4KICAgIDpwYXJhbSBwc2V0OiBsaXN0IG9mIGxpc3RzLCB0aGF0IGNvbnRhaW5zIGFsbCBwb3NzaWJsZSBjb21iaW5hdGlvbnMgb2YKICAgIHRoZSBlbGVtZW50cyBvZiBvcmlfbGlzdC4KICAgIDpwYXJhbSBtYXhfd2VpZ2h0OiBmbG9hdCBvciBpbnQsIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2Fjay4KICAgIDpwYXJhbSBnZXRfdmFsOiBmdW5jdGlvbiBvciBtZXRob2QsIG1hcCBhbiBpdGVtIGluIHBzZXQncyBsaXN0IGludG8gaXRzCiAgICBjb21wYXJhYmxlIHZhbHVlCiAgICA6cGFyYW0gZ2V0X3dlaWdodDogZnVuY3Rpb24gb3IgbWV0aG9kLCBtYXAgYW4gaXRlbSBpbiBwc2V0J3MgbGlzdCBpbnRvIGl0cwogICAgY29tcGFyYWJsZSB3ZWlnaHQKICAgIDpyZXR1cm46IGEgYmVzdCBzZXQgb2YgaXRlbXMsIGFuZCB0aGlzIGJlc3Qgc2V0J3MgdG90YWwgdmFsdWUKICAgICIiIgogICAgYmVzdF92YWwgPSAwLjAKICAgIGJlc3Rfc2V0ID0gTm9uZQogICAgZm9yIGl0ZW1zIGluIHBzZXQ6CiAgICAgICAgaXRlbXNfdmFsID0gMC4wCiAgICAgICAgaXRlbXNfd2VpZ2h0ID0gMC4wCiAgICAgICAgZm9yIGl0ZW0gaW4gaXRlbXM6CiAgICAgICAgICAgIGl0ZW1zX3ZhbCArPSBnZXRfdmFsKGl0ZW0pCiAgICAgICAgICAgIGl0ZW1zX3dlaWdodCArPSBnZXRfd2VpZ2h0KGl0ZW0pCiAgICAgICAgaWYgaXRlbXNfd2VpZ2h0IDw9IG1heF93ZWlnaHQgYW5kIGl0ZW1zX3ZhbCA+IGJlc3RfdmFsOgogICAgICAgICAgICBiZXN0X3ZhbCA9IGl0ZW1zX3ZhbAogICAgICAgICAgICBiZXN0X3NldCA9IGl0ZW1zCiAgICByZXR1cm4gYmVzdF9zZXQsIGJlc3RfdmFsCgpkZWYgdGVzdF9iZXN0KG1heF93ZWlnaHQgPSAyMCk6CiAgICAiIiIKICAgIFJ1biB0aGUgY2hvb3NlX2Jlc3QgZnVuY3Rpb24sIGFuZCBwcmludCBpdHMgcmVzdWx0IGluIHByb3BlciBmb3JtYXQuCiAgICA6cGFyYW0gbWF4X3dlaWdodDogZmxvYXQgb3IgaW50LCBtYXhpbXVtIHdlaWdodCBidWRnZXQgb2Yga25hcHNhY2ssIGRlZmF1bHQgMjAuCiAgICA6cmV0dXJuOiBOb25lLCBwcmludCBvdXQgdGhlIGl0ZW1zIHRha2VuIGFuZCB0aGVpciB0b3RhbCB2YWx1ZS4KICAgICIiIgogICAgaXRlbXMgPSBidWlsZF9pdGVtcygpCiAgICBwc2V0ID0gZ2VuX3Bvd2VyX3NldChpdGVtcykKICAgIHRha2VuLCB2YWwgPSBjaG9vc2VfYmVzdChwc2V0LCBtYXhfd2VpZ2h0LCBJdGVtLmdldFZhbHVlLCBJdGVtLmdldFdlaWdodCkKICAgIHByaW50KCdUb3RhbCB2YWx1ZSBvZiBpdGVtcyB0YWtlbiBpcycsIHZhbCkKICAgIGZvciBpdGVtIGluIHRha2VuOgogICAgICAgIHByaW50KGl0ZW0pCmBgYAoKUnVuIGB0ZXN0X2Jlc3RgIHNjcmlwdC4KCmBgYHtweXRob259CnRlc3RfYmVzdChtYXhfd2VpZ2h0PTIwKQpgYGAKClRoaXMgcmVzdWx0IGlzIGRpZmZlcmVudCBmcm9tIHRoZSByZXN1bHQgb2YgZGVuc2l0eS4KCiMjIyBDb3VsZCB0aGlzIHN0cmFpZ2h0Zm9yd2FyZCB3YXkgYmUgc3BlZWRlZCB1cD8KCjAxLlRoZSBgZ2VuX3Bvd2VyX3NldGAgY291bGQgaGF2ZSBoYWQgdGhlIGhlYWRlcgoKYGBgcHl0aG9uCmRlZiBnZW5Qb3dlcnNldChpdGVtcywgY29uc3RyYWludCwgZ2V0VmFsLCBnZXRXZWlnaHQpCmBgYAogIAlhbmQgcmV0dXJuZWQgb25seSB0aG9zZSBjb21iaW5hdGlvbnMgdGhhdCBtZWV0IHRoZSB3ZWlnaHQgY29uc3RyYWludC4KCk9yCgowMi5UaGUgYGNob29zZV9iZXN0YCBjb3VsZCBleGl0IHRoZSBpbm5lciBsb29wIGFzIHNvb24gYXMgdGhlIHdlaWdodCBjb25zdHJhaW50IGlzIGV4Y2VlZGVkLgoKYGBge3B5dGhvbn0KIyBkZWYgZ2VuX3Bvd2VyX3NldCAob3JpX2xpc3QpOiAjIE8oMioqbGVuKG9yaV9saXN0KSkgKiBPKGxlbiAob3JpX2xpc3QpKQpkZWYgZ2VuX3Bvd2VyX3NldF93aXRoaW5fY29uc3RyYWludChpdGVtcywgY29uc3RyYWludCwgZ2V0V2VpZ2h0KToKICAgICIiIgogICAgR2VuZXJhdGUgdGhlIHBvd2VyIHNldCBvZiBpdGVtcyB3aGljaCBtZWV0IHRoZSB3ZWlnaHQgY29uc3RyYWludC4KICAgIDpwYXJhbSBpdGVtczogbGlzdCwgaW5zdGFuY2VzIG9mIEl0ZW1zLgogICAgOnBhcmFtIGNvbnN0cmFpbnQ6IGZsb2F0LCBtYXhpbXVtIHdlaWdodCBvZiBrbmFwc2Fjay4KICAgIDpwYXJhbSBnZXRXZWlnaHQ6IG1ldGhvZCwgZ2V0IHdlaWdodCBvZiBhbiBJdGVtLgogICAgOnJldHVybjogYSBsaXN0IG9mIGxpc3RzIHRoYXQgY29udGFpbnMgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZgogICAgdGhlIGVsZW1lbnRzIHRoYXQgbWVldCB0aGUgd2VpZ2h0IGNvbnN0cmFpbnQuCiAgICAiIiIKICAgIHBvd2VyX3NldF93aXRoaW5fY29uc3RyYWludCA9IFtdCiAgICBmb3IgaSBpbiByYW5nZSAoMCwgMioqbGVuKGl0ZW1zKSk6ICMgTygyKipsZW4ob3JpX2xpc3QpKQogICAgICAgIGJpbl9zdHIgPSBnZXRfYmluYXJ5X3JlcChpLCBsZW4oaXRlbXMpKQogICAgICAgIHN1YnNldCA9IFtdCiAgICAgICAgc3Vic2V0X3RvdGFsX3dlaWdodCA9IDAKICAgICAgICBmb3IgaiBpbiByYW5nZShsZW4oaXRlbXMpKTogIyBPKGxlbihvcmlfbGlzdCkpCiAgICAgICAgICAgIGlmIGJpbl9zdHJbal0gPT0gJzEnOgogICAgICAgICAgICAgICAgc3Vic2V0LmFwcGVuZChpdGVtc1tqXSkKICAgICAgICAgICAgICAgIHN1YnNldF90b3RhbF93ZWlnaHQgKz0gaXRlbXNbal0uZ2V0V2VpZ2h0KCkKICAgICAgICBpZiBzdWJzZXRfdG90YWxfd2VpZ2h0IDw9IGNvbnN0cmFpbnQ6CiAgICAgICAgICAgIHBvd2VyX3NldF93aXRoaW5fY29uc3RyYWludC5hcHBlbmQoc3Vic2V0KQogICAgcmV0dXJuIHBvd2VyX3NldF93aXRoaW5fY29uc3RyYWludAoKIyBkZWYgY2hvb3NlX2Jlc3QocHNldCwgbWF4X3dlaWdodCwgZ2V0X3ZhbCwgZ2V0X3dlaWdodCk6CmRlZiBjaG9vc2VfYmVzdF9lZmZpY2llbnRseShwc2V0LCBtYXhfd2VpZ2h0LCBnZXRfdmFsLCBnZXRfd2VpZ2h0KToKICAgICIiIgogICAgQ2hvb3NlIHRoZSBiZXN0IHNldCBvZiBpdGVtcyBmcm9tIHRoZSBwb3dlciBzZXQgb2Ygb3JpZ2luYWwgbGlzdCwKICAgIHdpdGhpbiB0aGUgbWF4aW11bSB3ZWlnaHQgYnVkZ2V0IG9mIGtuYXBzYWNrLgogICAgOnBhcmFtIHBzZXQ6IGxpc3Qgb2YgbGlzdHMsIHRoYXQgY29udGFpbnMgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZgogICAgdGhlIGVsZW1lbnRzIG9mIG9yaV9saXN0LgogICAgOnBhcmFtIG1heF93ZWlnaHQ6IGZsb2F0IG9yIGludCwgbWF4aW11bSB3ZWlnaHQgYnVkZ2V0IG9mIGtuYXBzYWNrLgogICAgOnBhcmFtIGdldF92YWw6IGZ1bmN0aW9uIG9yIG1ldGhvZCwgbWFwIGFuIGl0ZW0gaW4gcHNldCdzIGxpc3QgaW50byBpdHMKICAgIGNvbXBhcmFibGUgdmFsdWUKICAgIDpwYXJhbSBnZXRfd2VpZ2h0OiBmdW5jdGlvbiBvciBtZXRob2QsIG1hcCBhbiBpdGVtIGluIHBzZXQncyBsaXN0IGludG8gaXRzCiAgICBjb21wYXJhYmxlIHdlaWdodAogICAgOnJldHVybjogYSBiZXN0IHNldCBvZiBpdGVtcywgYW5kIHRoaXMgYmVzdCBzZXQncyB0b3RhbCB2YWx1ZQogICAgIiIiCiAgICBiZXN0X3ZhbCA9IDAuMAogICAgYmVzdF9zZXQgPSBOb25lCiAgICBmb3IgaXRlbXMgaW4gcHNldDoKICAgICAgICBpdGVtc192YWwgPSAwLjAKICAgICAgICBpdGVtc193ZWlnaHQgPSAwLjAKICAgICAgICBmb3IgaXRlbSBpbiBpdGVtczoKICAgICAgICAgICAgaXRlbXNfdmFsICs9IGdldF92YWwoaXRlbSkKICAgICAgICAgICAgaXRlbXNfd2VpZ2h0ICs9IGdldF93ZWlnaHQoaXRlbSkKICAgICAgICAgICAgaWYgaXRlbXNfd2VpZ2h0ID4gbWF4X3dlaWdodDoKICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgaWYgaXRlbXNfd2VpZ2h0IDw9IG1heF93ZWlnaHQgYW5kIGl0ZW1zX3ZhbCA+IGJlc3RfdmFsOgogICAgICAgICAgICBiZXN0X3ZhbCA9IGl0ZW1zX3ZhbAogICAgICAgICAgICBiZXN0X3NldCA9IGl0ZW1zCiAgICByZXR1cm4gYmVzdF9zZXQsIGJlc3RfdmFsCgojIGRlZiB0ZXN0X2Jlc3QobWF4X3dlaWdodCA9IDIwKToKZGVmIHRlc3RfYmVzdF9vcHRfMDEobWF4X3dlaWdodCA9IDIwKToKICAgICIiIgogICAgUnVuIHRoZSBjaG9vc2VfYmVzdCBmdW5jdGlvbiwgYW5kIHByaW50IGl0cyByZXN1bHQgaW4gcHJvcGVyIGZvcm1hdC4KICAgIDpwYXJhbSBtYXhfd2VpZ2h0OiBmbG9hdCBvciBpbnQsIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2FjaywgZGVmYXVsdCAyMC4KICAgIDpyZXR1cm46IE5vbmUsIHByaW50IG91dCB0aGUgaXRlbXMgdGFrZW4gYW5kIHRoZWlyIHRvdGFsIHZhbHVlLgogICAgIiIiCiAgICBpdGVtcyA9IGJ1aWxkX2l0ZW1zKCkKICAgIHBzZXRfd2l0aGluX2NvbnN0cmFpbnQgPSBnZW5fcG93ZXJfc2V0X3dpdGhpbl9jb25zdHJhaW50KGl0ZW1zLCBjb25zdHJhaW50PW1heF93ZWlnaHQsIGdldFdlaWdodD1JdGVtLmdldFdlaWdodCkKICAgIHRha2VuLCB2YWwgPSBjaG9vc2VfYmVzdChwc2V0X3dpdGhpbl9jb25zdHJhaW50LCBtYXhfd2VpZ2h0LCBJdGVtLmdldFZhbHVlLCBJdGVtLmdldFdlaWdodCkKICAgIHByaW50KCdUb3RhbCB2YWx1ZSBvZiBpdGVtcyB0YWtlbiBpcycsIHZhbCkKICAgIGZvciBpdGVtIGluIHRha2VuOgogICAgICAgIHByaW50KGl0ZW0pCgojIGRlZiB0ZXN0X2Jlc3QobWF4X3dlaWdodCA9IDIwKToKZGVmIHRlc3RfYmVzdF9vcHRfMDIobWF4X3dlaWdodCA9IDIwKToKICAgICIiIgogICAgUnVuIHRoZSBjaG9vc2VfYmVzdCBmdW5jdGlvbiwgYW5kIHByaW50IGl0cyByZXN1bHQgaW4gcHJvcGVyIGZvcm1hdC4KICAgIDpwYXJhbSBtYXhfd2VpZ2h0OiBmbG9hdCBvciBpbnQsIG1heGltdW0gd2VpZ2h0IGJ1ZGdldCBvZiBrbmFwc2FjaywgZGVmYXVsdCAyMC4KICAgIDpyZXR1cm46IE5vbmUsIHByaW50IG91dCB0aGUgaXRlbXMgdGFrZW4gYW5kIHRoZWlyIHRvdGFsIHZhbHVlLgogICAgIiIiCiAgICBpdGVtcyA9IGJ1aWxkX2l0ZW1zKCkKICAgIHBzZXQgPSBnZW5fcG93ZXJfc2V0KGl0ZW1zKQogICAgdGFrZW4sIHZhbCA9IGNob29zZV9iZXN0X2VmZmljaWVudGx5KHBzZXQsIG1heF93ZWlnaHQsIEl0ZW0uZ2V0VmFsdWUsIEl0ZW0uZ2V0V2VpZ2h0KQogICAgcHJpbnQoJ1RvdGFsIHZhbHVlIG9mIGl0ZW1zIHRha2VuIGlzJywgdmFsKQogICAgZm9yIGl0ZW0gaW4gdGFrZW46CiAgICAgICAgcHJpbnQoaXRlbSkKYGBgCgoKYGBge3B5dGhvbn0KdGVzdF9iZXN0X29wdF8wMShtYXhfd2VpZ2h0PTIwKQpgYGAKCgpgYGB7cHl0aG9ufQp0ZXN0X2Jlc3Rfb3B0XzAyKG1heF93ZWlnaHQ9MjApCmBgYAoKV2hpbGUgdGhlc2Uga2luZHMgb2Ygb3B0aW1pemF0aW9ucyBhcmUgb2Z0ZW4gd29ydGggZG9pbmcsIHRoZXkgZG9u4oCZdCBhZGRyZXNzIHRoZSBmdW5kYW1lbnRhbCBpc3N1ZS4g44CQ5ZC05Yab6ICB5biI5YaZ5aW9566X5rOV5ZCO77yM5bel56iL5Li7566h6KaB5LuW5YGa5LyY5YyW77yM55yB5py65Zmo5bCx5piv566X6ZKx44CC44CRCgpgYGAK