# -*- coding: utf-8 -*-
from collections import defaultdict
from itertools import product
from PIL import Image, ImageFont, ImageDraw
FALL = '|'
RIVER = '~'
POOL = 'N'
SOURCE = 'x'
SOURCE_AND_FALL = '*'
SOURCE_AND_RIVER = 'X'
SOURCE_AND_POOL = '%'
BLOCK = '#'
LRAMP = '/'
RRAMP = '\\ '
SPACE = ' '
LRAMP_AND_FALL = 'd'
RRAMP_AND_FALL = 'b'
LRAMP_AND_RIVER = '}'
RRAMP_AND_RIVER = '{'
LRAMP_AND_POOL = ']'
RRAMP_AND_POOL = '['
HERE = ( 0 , 0 )
DOWN = ( 1 , 0 )
LEFT = ( 0 , -1 )
RIGHT = ( 0 , 1 )
UP = ( -1 , 0 )
ORDINALS = [ DOWN, LEFT, RIGHT, UP]
gridStr = """\
#x#
# #
# #
# #
# #
###"""
gridStr = """\
# x #
# #
# #
# #
# #
#####"""
gridStr = """\
x #
# # #
# # #
# # #
# # #
# # #
# # #
# # #
# # #
# # #
# #
######"""
gridStr = """\
x
# /
#####"""
gridStr = """\
x #
# # #
# # #
# # #
# #
######"""
gridStr = """\
x
# # #
# # #
# # #
# # #
# # #
#######################"""
gridStr = """\
x
# /
#####
# #
# # /
###\/ #"""
gridStr = """\
x
# #
# #
# #
#### #####
#### #####
#### #####
##########"""
FRAMES = 100
# The capacity of a ramp cell.
HALF_CAPACITY = 50
# The capacity of an empty cell.
MAX_CAPACITY = 2 *HALF_CAPACITY
# The rate of flow in from a source.
SOURCE_RATE = HALF_CAPACITY
# The maximum rate of flow between two cells.
MAX_FLOW = MAX_CAPACITY
# The ratio extra a cell can hold if the cell above is full (assuming both have same capacity)
COMPRESSABILITY = 0.25
SPACE_THRESHHOLD = HALF_CAPACITY / 2
RIVER_THRESHHOLD = HALF_CAPACITY + HALF_CAPACITY / 2
def isSource( cell) :
return cell in [ SOURCE, SOURCE_AND_FALL, SOURCE_AND_RIVER, SOURCE_AND_POOL]
def isLRamp( cell) :
return cell in [ LRAMP, LRAMP_AND_FALL, LRAMP_AND_RIVER, LRAMP_AND_POOL]
def isRRamp( cell) :
return cell in [ RRAMP, RRAMP_AND_FALL, RRAMP_AND_RIVER, RRAMP_AND_POOL]
def isRamp( cell) :
return isLRamp( cell) or isRRamp( cell)
def findBackground( cell) :
if cell in [ SPACE, FALL, RIVER, POOL, SOURCE] :
return SPACE
elif cell in [ BLOCK] :
return BLOCK
elif isLRamp( cell) :
return LRAMP
elif isRRamp( cell) :
return RRAMP
raise Exception
class WaterType:
FALL = 1
SETTLED = 2
class Cell:
def __init__ ( self , background, isSource) :
self .background = background
self .isSource = isSource
self .water = 0
self .waterType = WaterType.FALL
def getCapacity( self ) :
return ( 0 if self .background == BLOCK else ( HALF_CAPACITY if isRamp( self .background ) else MAX_CAPACITY) )
def copyCell( self , cell) :
self .background = cell.background
self .isSource = cell.isSource
# Include any water already here
self .water += cell.water
self .waterType = cell.waterType
def updateType( self , cellBelow) :
if self .water == 0 :
self .waterType = WaterType.SETTLED
elif self .water == self .getCapacity ( ) :
# If a completely saturated block of water is falling it should still look full.
self .waterType = WaterType.SETTLED
elif cellBelow.water < cellBelow.getCapacity ( ) * 0.9 :
self .waterType = WaterType.FALL
else :
self .waterType = WaterType.SETTLED
def __str__ ( self ) :
if self .background == SPACE and not self .isSource and self .water <= SPACE_THRESHHOLD:
return SPACE
elif self .background == SPACE and not self .isSource and self .waterType == WaterType.FALL :
return FALL
elif self .background == SPACE and not self .isSource and self .water <= RIVER_THRESHHOLD:
return RIVER
elif self .background == SPACE and not self .isSource :
return POOL
elif self .background == SPACE and self .water <= SPACE_THRESHHOLD:
return SOURCE
elif self .background == SPACE and self .waterType == WaterType.FALL :
return SOURCE_AND_FALL
elif self .background == SPACE and self .water <= RIVER_THRESHHOLD:
return SOURCE_AND_RIVER
elif self .background == SPACE:
return SOURCE_AND_POOL
elif self .background == BLOCK:
return BLOCK
elif isRamp( self .background ) and self .isSource :
raise Exception
elif self .background == LRAMP and self .water <= SPACE_THRESHHOLD / 2 :
return LRAMP
elif self .background == LRAMP and self .waterType == WaterType.FALL :
return LRAMP_AND_FALL
elif self .background == LRAMP and self .water <= RIVER_THRESHHOLD / 2 :
return LRAMP_AND_RIVER
elif self .background == LRAMP:
return LRAMP_AND_POOL
elif self .water <= SPACE_THRESHHOLD / 2 :
return RRAMP
elif self .waterType == WaterType.FALL :
return RRAMP_AND_FALL
elif self .water <= RIVER_THRESHHOLD / 2 :
return RRAMP_AND_RIVER
else :
return RRAMP_AND_POOL
gridCells = map ( lambda x: list ( x) , gridStr.split ( '\n ' ) )
height = len ( gridCells)
width = len ( gridCells[ 0 ] )
grid = defaultdict( lambda : Cell( SPACE, False ) )
for i in range ( height) :
for j in range ( width) :
grid[ i, j] = Cell( findBackground( gridCells[ i] [ j] ) , isSource( gridCells[ i] [ j] ) )
def p( coords, offset) :
"""Add two tuples together.
>>> p((2, 3), (-1, 0))
(1, 3)
"""
return tuple ( map ( sum , zip ( coords, offset) ) )
def adjacent( i, j, includeUp) :
ret = [ ]
for offset in ( ORDINALS if includeUp else ORDINALS[ :-1 ] ) :
ret.append ( p( ( i, j) , offset) )
return ret
CONNECTION_TO_SHAPES = { DOWN: [ SPACE, LRAMP, RRAMP] , LEFT: [ SPACE, RRAMP] , RIGHT: [ SPACE, LRAMP] , UP: [ SPACE] }
def adjoint( grid, i, j, includeUp, includeDown) :
"""Find all cells connected to this one (if this is a block then nothing connects to it).
>>> grid = defaultdict(lambda : Cell(SPACE, False))
>>> adjoint(grid, 10, 1, True, True)
{(0, 1): (10, 2), (0, -1): (10, 0), (1, 0): (11, 1), (-1, 0): (9, 1)}
>>> adjoint(grid, 10, 1, False, True)
{(0, 1): (10, 2), (0, -1): (10, 0), (1, 0): (11, 1)}
>>> adjoint(grid, 10, 1, False, False)
{(0, 1): (10, 2), (0, -1): (10, 0)}
>>> grid[10, 1].background = LRAMP
>>> adjoint(grid, 10, 1, True, True)
{(0, -1): (10, 0), (-1, 0): (9, 1)}
>>> grid[10, 0].background = LRAMP
>>> grid[9, 1].background = RRAMP
>>> adjoint(grid, 10, 1, True, True)
{}
"""
ret = { }
cell = grid[ i, j]
if cell.background == BLOCK:
adj = [ ]
elif cell.background == LRAMP:
adj = [ LEFT, UP]
elif cell.background == RRAMP:
adj = [ RIGHT, UP]
elif cell.background == SPACE:
adj = list ( ORDINALS)
if not includeUp and UP in adj:
adj.remove ( UP)
if not includeDown and DOWN in adj:
adj.remove ( DOWN)
for offset in adj:
neighbourCoords = p( ( i, j) , offset)
neighbour = grid[ neighbourCoords]
if neighbour.background in CONNECTION_TO_SHAPES[ offset] :
ret[ offset] = neighbourCoords
return ret
def makeFrame( textLines, t) :
img = Image.new ( 'RGB' , ( 200 , 200 ) )
draw = ImageDraw.Draw ( img)
font = ImageFont.truetype ( 'FreeMono.ttf' , 14 , encoding= 'unic' )
y = 0
for line in textLines:
draw.text ( ( 0 , y) , line, ( 255 , 255 , 255 ) , font= font)
y += 20
img.save ( 'frame_%04d.png' %t)
def displayGrid( grid, t) :
print
textLines = [ ]
for i in range ( height) :
row = ''
for j in range ( width) :
grid[ i, j] .updateType ( grid[ i+1 , j] )
row += str ( grid[ i, j] )
row += ' '
for j in range ( width) :
row += ( '%x' %( grid[ i, j] .water / 10 ) if grid[ i, j] .water < 160 else '+' )
textLines.append ( row)
text = '\n ' .join ( textLines)
print text
makeFrame( textLines, t)
def constrain( n, minN, maxN) :
if n < minN:
return minN
elif n > maxN:
return maxN
return n
def updateFlowDown( flow, contents, capacities) :
flowDown = constrain( capacities[ DOWN] - contents[ DOWN] , 0 , contents[ HERE] )
flow[ DOWN] += flowDown
contents[ HERE] -= flowDown
def updateFlowAcross( flow, contents, capacities) :
"""Update flow across and a bit down to simulate pressure at this level."""
relevantCapacity = { }
for ordinal in [ LEFT, HERE, RIGHT] :
relevantCapacity[ ordinal] = capacities[ ordinal]
relevantCapacity[ DOWN] = capacities[ DOWN] * COMPRESSABILITY
relevantContents = { }
for ordinal in [ LEFT, HERE, RIGHT] :
relevantContents[ ordinal] = constrain( contents[ ordinal] , 0 , capacities[ ordinal] )
relevantContents[ DOWN] = max ( contents[ DOWN] - capacities[ DOWN] , 0 )
idealFlowOut = 0
for ordinal in [ LEFT, RIGHT, DOWN] :
idealFlowOut += relevantCapacity[ ordinal] - relevantContents[ ordinal]
if idealFlowOut <= contents[ HERE] :
for ordinal in [ LEFT, RIGHT, DOWN] :
amount = max ( relevantCapacity[ ordinal] - relevantContents[ ordinal] , 0 )
flow[ ordinal] += amount
contents[ HERE] -= amount
else :
# Add in flow to HERE before splitting between destinations
idealFlowOut += relevantCapacity[ HERE] - relevantContents[ HERE]
for ordinal in [ LEFT, RIGHT, DOWN] :
amount = max ( relevantCapacity[ ordinal] - relevantContents[ ordinal] , 0 ) * relevantContents[ HERE] / idealFlowOut
flow[ ordinal] += amount
contents[ HERE] -= amount
def updateFlowHere( flow, contents, capacities) :
"""Create a 'flow' to the current cell. Any remaining can be pushed upwards."""
constrained = constrain( contents[ HERE] , 0 , capacities[ HERE] )
# The water will not actually leave the cell, so no need to add an explicit flow
#flow[HERE] += constrained
contents[ HERE] -= constrained
def updateFlowUp( flow, contents, capacities) :
"""Update flow up and a bit across and down to simulate pressure at the level above."""
relevantCapacity = { }
# Magic constant here: 1.4 seems to work best!
relevantCapacity[ UP] = capacities[ UP] * 1.4
for ordinal in [ LEFT, HERE, RIGHT] :
relevantCapacity[ ordinal] = capacities[ ordinal] * COMPRESSABILITY
relevantCapacity[ DOWN] = capacities[ DOWN] * COMPRESSABILITY * ( 1 + COMPRESSABILITY)
relevantContents = { }
relevantContents[ UP] = contents[ UP]
relevantContents[ HERE] = contents[ HERE]
for ordinal in [ LEFT, RIGHT] :
relevantContents[ ordinal] = max ( contents[ ordinal] - capacities[ ordinal] , 0 )
relevantContents[ DOWN] = max ( contents[ DOWN] - capacities[ DOWN] * ( 1 + COMPRESSABILITY) , 0 )
idealFlowOut = 0
for ordinal in [ UP, LEFT, RIGHT, DOWN] :
idealFlowOut += relevantCapacity[ ordinal] - relevantContents[ ordinal]
if idealFlowOut <= contents[ HERE] :
for ordinal in [ UP, LEFT, RIGHT, DOWN] :
amount = max ( relevantCapacity[ ordinal] - relevantContents[ ordinal] , 0 )
flow[ ordinal] += amount
contents[ HERE] -= amount
else :
# Add in flow to HERE before splitting between destinations
idealFlowOut += relevantCapacity[ HERE] - relevantContents[ HERE]
for ordinal in [ UP, LEFT, RIGHT, DOWN] :
amount = max ( relevantCapacity[ ordinal] - relevantContents[ ordinal] , 0 ) * relevantContents[ HERE] / idealFlowOut
flow[ ordinal] += amount
contents[ HERE] -= amount
def main( grid) :
t = 0
while t < FRAMES:
displayGrid( grid, t)
newGrid = defaultdict( lambda : Cell( SPACE, False ) )
for i, j in product( range ( height) , range ( width) ) :
cell = grid[ i, j]
if cell.isSource :
cell.water += SOURCE_RATE
newCell = newGrid[ i, j]
newCell.copyCell ( cell)
flow = defaultdict( int )
if cell.water <= 0 :
continue
neighbourhood = adjoint( grid, i, j, True , True )
capacities = { HERE: cell.getCapacity ( ) }
contents = { HERE: cell.water }
for ordinal in ORDINALS:
if ordinal in neighbourhood.keys ( ) :
capacities[ ordinal] = grid[ neighbourhood[ ordinal] ] .getCapacity ( )
contents[ ordinal] = grid[ neighbourhood[ ordinal] ] .water
else :
capacities[ ordinal] = 0
contents[ ordinal] = 0
# Down
updateFlowDown( flow, contents, capacities)
if contents[ HERE] > 0 :
# Left and Right
updateFlowAcross( flow, contents, capacities)
if contents[ HERE] > 0 :
# Here
updateFlowHere( flow, contents, capacities)
if contents[ HERE] > 0 :
# Up
#print 'Before %f'%contents[HERE]
updateFlowUp( flow, contents, capacities)
#print 'Up from %d %d: %s'%(i, j, flow)
#print 'After %f'%contents[HERE]
for ordinal, amount in flow.items ( ) :
if amount == 0 :
continue
if ordinal not in neighbourhood.keys ( ) :
print '%s not in %s when trying to move %f' %( ordinal, neighbourhood.keys ( ) , amount)
continue
# Smooth the movement by only flowing half the amount left and right
if ordinal in [ LEFT, RIGHT] :
amount = int ( amount / 2 )
newGrid[ neighbourhood[ ordinal] ] .water += amount
newCell.water -= amount
grid = newGrid
t += 1
if __name__ == '__main__' :
import doctest
# Doesn't test inner functions!
failures, passes = doctest .testmod ( raise_on_error= False )
if failures == 0 :
main( grid)
else :
print 'Stopping with %d failures' %failures
IyAtKi0gY29kaW5nOiB1dGYtOCAtKi0KZnJvbSBjb2xsZWN0aW9ucyBpbXBvcnQgZGVmYXVsdGRpY3QKZnJvbSBpdGVydG9vbHMgaW1wb3J0IHByb2R1Y3QKZnJvbSBQSUwgaW1wb3J0IEltYWdlLCBJbWFnZUZvbnQsIEltYWdlRHJhdwoKRkFMTCA9ICd8JwpSSVZFUiA9ICd+JwpQT09MID0gJ04nCiAKU09VUkNFID0gJ3gnClNPVVJDRV9BTkRfRkFMTCA9ICcqJwpTT1VSQ0VfQU5EX1JJVkVSID0gJ1gnClNPVVJDRV9BTkRfUE9PTCA9ICclJwogCkJMT0NLID0gJyMnCkxSQU1QID0gJy8nClJSQU1QID0gJ1xcJwpTUEFDRSA9ICcgJwogCkxSQU1QX0FORF9GQUxMID0gJ2QnClJSQU1QX0FORF9GQUxMID0gJ2InCkxSQU1QX0FORF9SSVZFUiA9ICd9JwpSUkFNUF9BTkRfUklWRVIgPSAneycKTFJBTVBfQU5EX1BPT0wgPSAnXScKUlJBTVBfQU5EX1BPT0wgPSAnWycKCkhFUkUgPSAoMCwwKQpET1dOID0gKDEsMCkKTEVGVCA9ICgwLC0xKQpSSUdIVCA9ICgwLDEpClVQID0gKC0xLDApCk9SRElOQUxTID0gW0RPV04sTEVGVCxSSUdIVCxVUF0KCmdyaWRTdHIgPSAiIiJcCiN4IwojICMKIyAjCiMgIwojICMKIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiMgeCAjCiMgICAjCiMgICAjCiMgICAjCiMgICAjCiMjIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiB4ICMgIAojICAjICMKIyAgIyAjCiMgICMgIwojICAjICMKIyAgIyAjCiMgICMgIwojICAjICMKIyAgIyAjCiMgICMgIwojICAgICMKIyMjIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiAgeCAgCiMgIC8gCiMjIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiB4ICMgIAojICAjICMKIyAgIyAjCiMgICMgIwojICAgICMKIyMjIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiAgICAgICAgICAgICAgICAgICAgeCAgCiAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgCiMgICAgICAgICAgICAgICAgICAgIyAjCiMgICAgICAgICAgICAgICAgICAgIyAjCiMgICAgICAgICAgICAgICAgICAgIyAjCiMgICAgICAgICAgICAgICAgICAgIyAjCiMgICAgICAgICAgICAgICAgICAgIyAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiAgeCAgIAojICAgLyAKIyMjIyMgCiMgICAjIAojICMgIC8KIyMjXC8jIiIiCmdyaWRTdHIgPSAiIiJcCiAgIHggICAgICAKIyAgICAgICAgIwojICAgICAgICAjCiMgICAgICAgICMKIyMjIyAjIyMjIwojIyMjICMjIyMjCiMjIyMgIyMjIyMKIyMjIyMjIyMjIyIiIgoKRlJBTUVTID0gMTAwCgojIFRoZSBjYXBhY2l0eSBvZiBhIHJhbXAgY2VsbC4KSEFMRl9DQVBBQ0lUWSA9IDUwCiMgVGhlIGNhcGFjaXR5IG9mIGFuIGVtcHR5IGNlbGwuCk1BWF9DQVBBQ0lUWSA9IDIqSEFMRl9DQVBBQ0lUWQojIFRoZSByYXRlIG9mIGZsb3cgaW4gZnJvbSBhIHNvdXJjZS4KU09VUkNFX1JBVEUgPSBIQUxGX0NBUEFDSVRZCiMgVGhlIG1heGltdW0gcmF0ZSBvZiBmbG93IGJldHdlZW4gdHdvIGNlbGxzLgpNQVhfRkxPVyA9IE1BWF9DQVBBQ0lUWQojIFRoZSByYXRpbyBleHRyYSBhIGNlbGwgY2FuIGhvbGQgaWYgdGhlIGNlbGwgYWJvdmUgaXMgZnVsbCAoYXNzdW1pbmcgYm90aCBoYXZlIHNhbWUgY2FwYWNpdHkpCkNPTVBSRVNTQUJJTElUWSA9IDAuMjUKClNQQUNFX1RIUkVTSEhPTEQgPSBIQUxGX0NBUEFDSVRZIC8gMgpSSVZFUl9USFJFU0hIT0xEID0gSEFMRl9DQVBBQ0lUWSArIEhBTEZfQ0FQQUNJVFkgLyAyCgpkZWYgaXNTb3VyY2UoY2VsbCk6CiAgICByZXR1cm4gY2VsbCBpbiBbU09VUkNFLCBTT1VSQ0VfQU5EX0ZBTEwsIFNPVVJDRV9BTkRfUklWRVIsIFNPVVJDRV9BTkRfUE9PTF0KCmRlZiBpc0xSYW1wKGNlbGwpOgogICAgcmV0dXJuIGNlbGwgaW4gW0xSQU1QLCBMUkFNUF9BTkRfRkFMTCwgTFJBTVBfQU5EX1JJVkVSLCBMUkFNUF9BTkRfUE9PTF0KZGVmIGlzUlJhbXAoY2VsbCk6CiAgICByZXR1cm4gY2VsbCBpbiBbUlJBTVAsIFJSQU1QX0FORF9GQUxMLCBSUkFNUF9BTkRfUklWRVIsIFJSQU1QX0FORF9QT09MXQpkZWYgaXNSYW1wKGNlbGwpOgogICAgcmV0dXJuIGlzTFJhbXAoY2VsbCkgb3IgaXNSUmFtcChjZWxsKQoKZGVmIGZpbmRCYWNrZ3JvdW5kKGNlbGwpOgogICAgaWYgY2VsbCBpbiBbU1BBQ0UsIEZBTEwsIFJJVkVSLCBQT09MLCBTT1VSQ0VdOgogICAgICAgIHJldHVybiBTUEFDRQogICAgZWxpZiBjZWxsIGluIFtCTE9DS106CiAgICAgICAgcmV0dXJuIEJMT0NLCiAgICBlbGlmIGlzTFJhbXAoY2VsbCk6CiAgICAgICAgcmV0dXJuIExSQU1QCiAgICBlbGlmIGlzUlJhbXAoY2VsbCk6CiAgICAgICAgcmV0dXJuIFJSQU1QCiAgICByYWlzZSBFeGNlcHRpb24KCmNsYXNzIFdhdGVyVHlwZToKICAgIEZBTEwgPSAxCiAgICBTRVRUTEVEID0gMgoKY2xhc3MgQ2VsbDoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBiYWNrZ3JvdW5kLCBpc1NvdXJjZSk6CiAgICAgICAgc2VsZi5iYWNrZ3JvdW5kID0gYmFja2dyb3VuZAogICAgICAgIHNlbGYuaXNTb3VyY2UgPSBpc1NvdXJjZQogICAgICAgIHNlbGYud2F0ZXIgPSAwCiAgICAgICAgc2VsZi53YXRlclR5cGUgPSBXYXRlclR5cGUuRkFMTAogICAgZGVmIGdldENhcGFjaXR5KHNlbGYpOgogICAgICAgIHJldHVybiAoMCBpZiBzZWxmLmJhY2tncm91bmQgPT0gQkxPQ0sgZWxzZSAoSEFMRl9DQVBBQ0lUWSBpZiBpc1JhbXAoc2VsZi5iYWNrZ3JvdW5kKSBlbHNlIE1BWF9DQVBBQ0lUWSkpCiAgICBkZWYgY29weUNlbGwoc2VsZiwgY2VsbCk6CiAgICAgICAgc2VsZi5iYWNrZ3JvdW5kID0gY2VsbC5iYWNrZ3JvdW5kCiAgICAgICAgc2VsZi5pc1NvdXJjZSA9IGNlbGwuaXNTb3VyY2UKICAgICAgICAjIEluY2x1ZGUgYW55IHdhdGVyIGFscmVhZHkgaGVyZQogICAgICAgIHNlbGYud2F0ZXIgKz0gY2VsbC53YXRlcgogICAgICAgIHNlbGYud2F0ZXJUeXBlID0gY2VsbC53YXRlclR5cGUKICAgIGRlZiB1cGRhdGVUeXBlKHNlbGYsIGNlbGxCZWxvdyk6CiAgICAgICAgaWYgc2VsZi53YXRlciA9PSAwOgogICAgICAgICAgICBzZWxmLndhdGVyVHlwZSA9IFdhdGVyVHlwZS5TRVRUTEVECiAgICAgICAgZWxpZiBzZWxmLndhdGVyID09IHNlbGYuZ2V0Q2FwYWNpdHkoKToKICAgICAgICAgICAgIyBJZiBhIGNvbXBsZXRlbHkgc2F0dXJhdGVkIGJsb2NrIG9mIHdhdGVyIGlzIGZhbGxpbmcgaXQgc2hvdWxkIHN0aWxsIGxvb2sgZnVsbC4KICAgICAgICAgICAgc2VsZi53YXRlclR5cGUgPSBXYXRlclR5cGUuU0VUVExFRAogICAgICAgIGVsaWYgY2VsbEJlbG93LndhdGVyIDwgY2VsbEJlbG93LmdldENhcGFjaXR5KCkgKiAwLjk6CiAgICAgICAgICAgIHNlbGYud2F0ZXJUeXBlID0gV2F0ZXJUeXBlLkZBTEwKICAgICAgICBlbHNlOgogICAgICAgICAgICBzZWxmLndhdGVyVHlwZSA9IFdhdGVyVHlwZS5TRVRUTEVECiAgICBkZWYgX19zdHJfXyhzZWxmKToKICAgICAgICBpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIG5vdCBzZWxmLmlzU291cmNlIGFuZCBzZWxmLndhdGVyIDw9IFNQQUNFX1RIUkVTSEhPTEQ6CiAgICAgICAgICAgIHJldHVybiBTUEFDRQogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IFNQQUNFIGFuZCBub3Qgc2VsZi5pc1NvdXJjZSBhbmQgc2VsZi53YXRlclR5cGUgPT0gV2F0ZXJUeXBlLkZBTEw6CiAgICAgICAgICAgIHJldHVybiBGQUxMCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIG5vdCBzZWxmLmlzU291cmNlIGFuZCBzZWxmLndhdGVyIDw9IFJJVkVSX1RIUkVTSEhPTEQ6CiAgICAgICAgICAgIHJldHVybiBSSVZFUgogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IFNQQUNFIGFuZCBub3Qgc2VsZi5pc1NvdXJjZToKICAgICAgICAgICAgcmV0dXJuIFBPT0wKICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBTUEFDRSBhbmQgc2VsZi53YXRlciA8PSBTUEFDRV9USFJFU0hIT0xEOgogICAgICAgICAgICByZXR1cm4gU09VUkNFCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIHNlbGYud2F0ZXJUeXBlID09IFdhdGVyVHlwZS5GQUxMOgogICAgICAgICAgICByZXR1cm4gU09VUkNFX0FORF9GQUxMCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIHNlbGYud2F0ZXIgPD0gUklWRVJfVEhSRVNISE9MRDoKICAgICAgICAgICAgcmV0dXJuIFNPVVJDRV9BTkRfUklWRVIKICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBTUEFDRToKICAgICAgICAgICAgcmV0dXJuIFNPVVJDRV9BTkRfUE9PTAogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IEJMT0NLOgogICAgICAgICAgICByZXR1cm4gQkxPQ0sKICAgICAgICBlbGlmIGlzUmFtcChzZWxmLmJhY2tncm91bmQpIGFuZCBzZWxmLmlzU291cmNlOgogICAgICAgICAgICByYWlzZSBFeGNlcHRpb24KICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBMUkFNUCBhbmQgc2VsZi53YXRlciA8PSBTUEFDRV9USFJFU0hIT0xEIC8gMjoKICAgICAgICAgICAgcmV0dXJuIExSQU1QCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gTFJBTVAgYW5kIHNlbGYud2F0ZXJUeXBlID09IFdhdGVyVHlwZS5GQUxMOgogICAgICAgICAgICByZXR1cm4gTFJBTVBfQU5EX0ZBTEwKICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBMUkFNUCBhbmQgc2VsZi53YXRlciA8PSBSSVZFUl9USFJFU0hIT0xEIC8gMjoKICAgICAgICAgICAgcmV0dXJuIExSQU1QX0FORF9SSVZFUgogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IExSQU1QOgogICAgICAgICAgICByZXR1cm4gTFJBTVBfQU5EX1BPT0wKICAgICAgICBlbGlmIHNlbGYud2F0ZXIgPD0gU1BBQ0VfVEhSRVNISE9MRCAvIDI6CiAgICAgICAgICAgIHJldHVybiBSUkFNUAogICAgICAgIGVsaWYgc2VsZi53YXRlclR5cGUgPT0gV2F0ZXJUeXBlLkZBTEw6CiAgICAgICAgICAgIHJldHVybiBSUkFNUF9BTkRfRkFMTAogICAgICAgIGVsaWYgc2VsZi53YXRlciA8PSBSSVZFUl9USFJFU0hIT0xEIC8gMjoKICAgICAgICAgICAgcmV0dXJuIFJSQU1QX0FORF9SSVZFUgogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJldHVybiBSUkFNUF9BTkRfUE9PTAoKZ3JpZENlbGxzID0gbWFwKGxhbWJkYSB4OiBsaXN0KHgpLCBncmlkU3RyLnNwbGl0KCdcbicpKQpoZWlnaHQgPSBsZW4oZ3JpZENlbGxzKQp3aWR0aCA9IGxlbihncmlkQ2VsbHNbMF0pCmdyaWQgPSBkZWZhdWx0ZGljdChsYW1iZGEgOiBDZWxsKFNQQUNFLCBGYWxzZSkpCmZvciBpIGluIHJhbmdlKGhlaWdodCk6CiAgICBmb3IgaiBpbiByYW5nZSh3aWR0aCk6CiAgICAgICAgZ3JpZFtpLGpdID0gQ2VsbChmaW5kQmFja2dyb3VuZChncmlkQ2VsbHNbaV1bal0pLCBpc1NvdXJjZShncmlkQ2VsbHNbaV1bal0pKQoKZGVmIHAoY29vcmRzLCBvZmZzZXQpOgogICAgIiIiQWRkIHR3byB0dXBsZXMgdG9nZXRoZXIuCiAgICAKICAgID4+PiBwKCgyLCAzKSwgKC0xLCAwKSkKICAgICgxLCAzKQogICAgIiIiCiAgICByZXR1cm4gdHVwbGUobWFwKHN1bSwgemlwKGNvb3Jkcywgb2Zmc2V0KSkpCgpkZWYgYWRqYWNlbnQoaSxqLGluY2x1ZGVVcCk6CiAgICByZXQgPSBbXQogICAgZm9yIG9mZnNldCBpbiAoT1JESU5BTFMgaWYgaW5jbHVkZVVwIGVsc2UgT1JESU5BTFNbOi0xXSk6CiAgICAgICAgcmV0LmFwcGVuZChwKChpLGopLCBvZmZzZXQpKQogICAgcmV0dXJuIHJldAoKQ09OTkVDVElPTl9UT19TSEFQRVMgPSB7RE9XTjogW1NQQUNFLCBMUkFNUCwgUlJBTVBdLCBMRUZUOiBbU1BBQ0UsIFJSQU1QXSwgUklHSFQ6IFtTUEFDRSwgTFJBTVBdLCBVUDogW1NQQUNFXX0KCmRlZiBhZGpvaW50KGdyaWQsIGksIGosIGluY2x1ZGVVcCwgaW5jbHVkZURvd24pOgogICAgIiIiRmluZCBhbGwgY2VsbHMgY29ubmVjdGVkIHRvIHRoaXMgb25lIChpZiB0aGlzIGlzIGEgYmxvY2sgdGhlbiBub3RoaW5nIGNvbm5lY3RzIHRvIGl0KS4KICAgIAogICAgPj4+IGdyaWQgPSBkZWZhdWx0ZGljdChsYW1iZGEgOiBDZWxsKFNQQUNFLCBGYWxzZSkpCiAgICA+Pj4gYWRqb2ludChncmlkLCAxMCwgMSwgVHJ1ZSwgVHJ1ZSkKICAgIHsoMCwgMSk6ICgxMCwgMiksICgwLCAtMSk6ICgxMCwgMCksICgxLCAwKTogKDExLCAxKSwgKC0xLCAwKTogKDksIDEpfQogICAgCiAgICA+Pj4gYWRqb2ludChncmlkLCAxMCwgMSwgRmFsc2UsIFRydWUpCiAgICB7KDAsIDEpOiAoMTAsIDIpLCAoMCwgLTEpOiAoMTAsIDApLCAoMSwgMCk6ICgxMSwgMSl9CiAgICAKICAgID4+PiBhZGpvaW50KGdyaWQsIDEwLCAxLCBGYWxzZSwgRmFsc2UpCiAgICB7KDAsIDEpOiAoMTAsIDIpLCAoMCwgLTEpOiAoMTAsIDApfQogICAgCiAgICA+Pj4gZ3JpZFsxMCwgMV0uYmFja2dyb3VuZCA9IExSQU1QCiAgICA+Pj4gYWRqb2ludChncmlkLCAxMCwgMSwgVHJ1ZSwgVHJ1ZSkKICAgIHsoMCwgLTEpOiAoMTAsIDApLCAoLTEsIDApOiAoOSwgMSl9CiAgICAKICAgID4+PiBncmlkWzEwLCAwXS5iYWNrZ3JvdW5kID0gTFJBTVAKICAgID4+PiBncmlkWzksIDFdLmJhY2tncm91bmQgPSBSUkFNUAogICAgPj4+IGFkam9pbnQoZ3JpZCwgMTAsIDEsIFRydWUsIFRydWUpCiAgICB7fQogICAgIiIiCiAgICByZXQgPSB7fQogICAgY2VsbCA9IGdyaWRbaSwgal0KICAgIGlmIGNlbGwuYmFja2dyb3VuZCA9PSBCTE9DSzoKICAgICAgICBhZGogPSBbXQogICAgZWxpZiBjZWxsLmJhY2tncm91bmQgPT0gTFJBTVA6CiAgICAgICAgYWRqID0gW0xFRlQsIFVQXQogICAgZWxpZiBjZWxsLmJhY2tncm91bmQgPT0gUlJBTVA6CiAgICAgICAgYWRqID0gW1JJR0hULCBVUF0KICAgIGVsaWYgY2VsbC5iYWNrZ3JvdW5kID09IFNQQUNFOgogICAgICAgIGFkaiA9IGxpc3QoT1JESU5BTFMpCiAgICBpZiBub3QgaW5jbHVkZVVwIGFuZCBVUCBpbiBhZGo6CiAgICAgICAgYWRqLnJlbW92ZShVUCkKICAgIGlmIG5vdCBpbmNsdWRlRG93biBhbmQgRE9XTiBpbiBhZGo6CiAgICAgICAgYWRqLnJlbW92ZShET1dOKQogICAgZm9yIG9mZnNldCBpbiBhZGo6CiAgICAgICAgbmVpZ2hib3VyQ29vcmRzID0gcCgoaSxqKSwgb2Zmc2V0KQogICAgICAgIG5laWdoYm91ciA9IGdyaWRbbmVpZ2hib3VyQ29vcmRzXQogICAgICAgIGlmIG5laWdoYm91ci5iYWNrZ3JvdW5kIGluIENPTk5FQ1RJT05fVE9fU0hBUEVTW29mZnNldF06CiAgICAgICAgICAgIHJldFtvZmZzZXRdID0gbmVpZ2hib3VyQ29vcmRzCiAgICByZXR1cm4gcmV0CgpkZWYgbWFrZUZyYW1lKHRleHRMaW5lcywgdCk6CiAgICBpbWcgPSBJbWFnZS5uZXcoJ1JHQicsICgyMDAsIDIwMCkpCiAgICBkcmF3ID0gSW1hZ2VEcmF3LkRyYXcoaW1nKQogICAgZm9udCA9IEltYWdlRm9udC50cnVldHlwZSgnRnJlZU1vbm8udHRmJywgMTQsIGVuY29kaW5nPSd1bmljJykKICAgIHkgPSAwCiAgICBmb3IgbGluZSBpbiB0ZXh0TGluZXM6CiAgICAgICAgZHJhdy50ZXh0KCgwLCB5KSwgbGluZSwgKDI1NSwyNTUsMjU1KSwgZm9udD1mb250KQogICAgICAgIHkgKz0gMjAKICAgIGltZy5zYXZlKCdmcmFtZV8lMDRkLnBuZycldCkKCmRlZiBkaXNwbGF5R3JpZChncmlkLCB0KToKICAgIHByaW50CiAgICB0ZXh0TGluZXMgPSBbXQogICAgZm9yIGkgaW4gcmFuZ2UoaGVpZ2h0KToKICAgICAgICByb3cgPSAnJwogICAgICAgIGZvciBqIGluIHJhbmdlKHdpZHRoKToKICAgICAgICAgICAgZ3JpZFtpLGpdLnVwZGF0ZVR5cGUoZ3JpZFtpKzEsal0pCiAgICAgICAgICAgIHJvdyArPSBzdHIoZ3JpZFtpLGpdKQogICAgICAgIHJvdyArPSAnICcKICAgICAgICBmb3IgaiBpbiByYW5nZSh3aWR0aCk6CiAgICAgICAgICAgIHJvdyArPSAoJyV4JyUoZ3JpZFtpLGpdLndhdGVyIC8gMTApIGlmIGdyaWRbaSxqXS53YXRlciA8IDE2MCBlbHNlICcrJykKICAgICAgICB0ZXh0TGluZXMuYXBwZW5kKHJvdykKICAgIHRleHQgPSAnXG4nLmpvaW4odGV4dExpbmVzKQogICAgcHJpbnQgdGV4dAogICAgbWFrZUZyYW1lKHRleHRMaW5lcywgdCkKCmRlZiBjb25zdHJhaW4obiwgbWluTiwgbWF4Tik6CiAgICBpZiBuIDwgbWluTjoKICAgICAgICByZXR1cm4gbWluTgogICAgZWxpZiBuID4gbWF4TjoKICAgICAgICByZXR1cm4gbWF4TgogICAgcmV0dXJuIG4KCmRlZiB1cGRhdGVGbG93RG93bihmbG93LCBjb250ZW50cywgY2FwYWNpdGllcyk6CiAgICBmbG93RG93biA9IGNvbnN0cmFpbihjYXBhY2l0aWVzW0RPV05dIC0gY29udGVudHNbRE9XTl0sIDAsIGNvbnRlbnRzW0hFUkVdKQogICAgZmxvd1tET1dOXSArPSBmbG93RG93bgogICAgY29udGVudHNbSEVSRV0gLT0gZmxvd0Rvd24KCmRlZiB1cGRhdGVGbG93QWNyb3NzKGZsb3csIGNvbnRlbnRzLCBjYXBhY2l0aWVzKToKICAgICIiIlVwZGF0ZSBmbG93IGFjcm9zcyBhbmQgYSBiaXQgZG93biB0byBzaW11bGF0ZSBwcmVzc3VyZSBhdCB0aGlzIGxldmVsLiIiIgogICAgcmVsZXZhbnRDYXBhY2l0eSA9IHt9CiAgICBmb3Igb3JkaW5hbCBpbiBbTEVGVCwgSEVSRSwgUklHSFRdOgogICAgICAgIHJlbGV2YW50Q2FwYWNpdHlbb3JkaW5hbF0gPSBjYXBhY2l0aWVzW29yZGluYWxdCiAgICByZWxldmFudENhcGFjaXR5W0RPV05dID0gY2FwYWNpdGllc1tET1dOXSAqIENPTVBSRVNTQUJJTElUWQogICAgcmVsZXZhbnRDb250ZW50cyA9IHt9CiAgICBmb3Igb3JkaW5hbCBpbiBbTEVGVCwgSEVSRSwgUklHSFRdOgogICAgICAgIHJlbGV2YW50Q29udGVudHNbb3JkaW5hbF0gPSBjb25zdHJhaW4oY29udGVudHNbb3JkaW5hbF0sIDAsIGNhcGFjaXRpZXNbb3JkaW5hbF0pCiAgICByZWxldmFudENvbnRlbnRzW0RPV05dID0gbWF4KGNvbnRlbnRzW0RPV05dIC0gY2FwYWNpdGllc1tET1dOXSwgMCkKCiAgICBpZGVhbEZsb3dPdXQgPSAwCiAgICBmb3Igb3JkaW5hbCBpbiBbTEVGVCwgUklHSFQsIERPV05dOgogICAgICAgIGlkZWFsRmxvd091dCArPSByZWxldmFudENhcGFjaXR5W29yZGluYWxdIC0gcmVsZXZhbnRDb250ZW50c1tvcmRpbmFsXQogICAgCiAgICBpZiBpZGVhbEZsb3dPdXQgPD0gY29udGVudHNbSEVSRV06CiAgICAgICAgZm9yIG9yZGluYWwgaW4gW0xFRlQsIFJJR0hULCBET1dOXToKICAgICAgICAgICAgYW1vdW50ID0gbWF4KHJlbGV2YW50Q2FwYWNpdHlbb3JkaW5hbF0gLSByZWxldmFudENvbnRlbnRzW29yZGluYWxdLCAwKQogICAgICAgICAgICBmbG93W29yZGluYWxdICs9IGFtb3VudAogICAgICAgICAgICBjb250ZW50c1tIRVJFXSAtPSBhbW91bnQKICAgIGVsc2U6CiAgICAgICAgIyBBZGQgaW4gZmxvdyB0byBIRVJFIGJlZm9yZSBzcGxpdHRpbmcgYmV0d2VlbiBkZXN0aW5hdGlvbnMKICAgICAgICBpZGVhbEZsb3dPdXQgKz0gcmVsZXZhbnRDYXBhY2l0eVtIRVJFXSAtIHJlbGV2YW50Q29udGVudHNbSEVSRV0KICAgICAgICBmb3Igb3JkaW5hbCBpbiBbTEVGVCwgUklHSFQsIERPV05dOgogICAgICAgICAgICBhbW91bnQgPSBtYXgocmVsZXZhbnRDYXBhY2l0eVtvcmRpbmFsXSAtIHJlbGV2YW50Q29udGVudHNbb3JkaW5hbF0sIDApICogcmVsZXZhbnRDb250ZW50c1tIRVJFXSAvIGlkZWFsRmxvd091dAogICAgICAgICAgICBmbG93W29yZGluYWxdICs9IGFtb3VudAogICAgICAgICAgICBjb250ZW50c1tIRVJFXSAtPSBhbW91bnQKCmRlZiB1cGRhdGVGbG93SGVyZShmbG93LCBjb250ZW50cywgY2FwYWNpdGllcyk6CiAgICAiIiJDcmVhdGUgYSAnZmxvdycgdG8gdGhlIGN1cnJlbnQgY2VsbC4gQW55IHJlbWFpbmluZyBjYW4gYmUgcHVzaGVkIHVwd2FyZHMuIiIiCiAgICBjb25zdHJhaW5lZCA9IGNvbnN0cmFpbihjb250ZW50c1tIRVJFXSwgMCwgY2FwYWNpdGllc1tIRVJFXSkKICAgICMgVGhlIHdhdGVyIHdpbGwgbm90IGFjdHVhbGx5IGxlYXZlIHRoZSBjZWxsLCBzbyBubyBuZWVkIHRvIGFkZCBhbiBleHBsaWNpdCBmbG93CiAgICAjZmxvd1tIRVJFXSArPSBjb25zdHJhaW5lZAogICAgY29udGVudHNbSEVSRV0gLT0gY29uc3RyYWluZWQKCmRlZiB1cGRhdGVGbG93VXAoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpOgogICAgIiIiVXBkYXRlIGZsb3cgdXAgYW5kIGEgYml0IGFjcm9zcyBhbmQgZG93biB0byBzaW11bGF0ZSBwcmVzc3VyZSBhdCB0aGUgbGV2ZWwgYWJvdmUuIiIiCiAgICByZWxldmFudENhcGFjaXR5ID0ge30KICAgICMgTWFnaWMgY29uc3RhbnQgaGVyZTogMS40IHNlZW1zIHRvIHdvcmsgYmVzdCEKICAgIHJlbGV2YW50Q2FwYWNpdHlbVVBdID0gY2FwYWNpdGllc1tVUF0gKiAxLjQKICAgIGZvciBvcmRpbmFsIGluIFtMRUZULCBIRVJFLCBSSUdIVF06CiAgICAgICAgcmVsZXZhbnRDYXBhY2l0eVtvcmRpbmFsXSA9IGNhcGFjaXRpZXNbb3JkaW5hbF0gKiBDT01QUkVTU0FCSUxJVFkKICAgIHJlbGV2YW50Q2FwYWNpdHlbRE9XTl0gPSBjYXBhY2l0aWVzW0RPV05dICogQ09NUFJFU1NBQklMSVRZICogKDEgKyBDT01QUkVTU0FCSUxJVFkpCiAgICByZWxldmFudENvbnRlbnRzID0ge30KICAgIHJlbGV2YW50Q29udGVudHNbVVBdID0gY29udGVudHNbVVBdCiAgICByZWxldmFudENvbnRlbnRzW0hFUkVdID0gY29udGVudHNbSEVSRV0KICAgIGZvciBvcmRpbmFsIGluIFtMRUZULCBSSUdIVF06CiAgICAgICAgcmVsZXZhbnRDb250ZW50c1tvcmRpbmFsXSA9IG1heChjb250ZW50c1tvcmRpbmFsXSAtIGNhcGFjaXRpZXNbb3JkaW5hbF0sIDApCiAgICByZWxldmFudENvbnRlbnRzW0RPV05dID0gbWF4KGNvbnRlbnRzW0RPV05dIC0gY2FwYWNpdGllc1tET1dOXSAqICgxICsgQ09NUFJFU1NBQklMSVRZKSwgMCkKCiAgICBpZGVhbEZsb3dPdXQgPSAwCiAgICBmb3Igb3JkaW5hbCBpbiBbVVAsIExFRlQsIFJJR0hULCBET1dOXToKICAgICAgICBpZGVhbEZsb3dPdXQgKz0gcmVsZXZhbnRDYXBhY2l0eVtvcmRpbmFsXSAtIHJlbGV2YW50Q29udGVudHNbb3JkaW5hbF0KICAgIAogICAgaWYgaWRlYWxGbG93T3V0IDw9IGNvbnRlbnRzW0hFUkVdOgogICAgICAgIGZvciBvcmRpbmFsIGluIFtVUCwgTEVGVCwgUklHSFQsIERPV05dOgogICAgICAgICAgICBhbW91bnQgPSBtYXgocmVsZXZhbnRDYXBhY2l0eVtvcmRpbmFsXSAtIHJlbGV2YW50Q29udGVudHNbb3JkaW5hbF0sIDApCiAgICAgICAgICAgIGZsb3dbb3JkaW5hbF0gKz0gYW1vdW50CiAgICAgICAgICAgIGNvbnRlbnRzW0hFUkVdIC09IGFtb3VudAogICAgZWxzZToKICAgICAgICAjIEFkZCBpbiBmbG93IHRvIEhFUkUgYmVmb3JlIHNwbGl0dGluZyBiZXR3ZWVuIGRlc3RpbmF0aW9ucwogICAgICAgIGlkZWFsRmxvd091dCArPSByZWxldmFudENhcGFjaXR5W0hFUkVdIC0gcmVsZXZhbnRDb250ZW50c1tIRVJFXQogICAgICAgIGZvciBvcmRpbmFsIGluIFtVUCwgTEVGVCwgUklHSFQsIERPV05dOgogICAgICAgICAgICBhbW91bnQgPSBtYXgocmVsZXZhbnRDYXBhY2l0eVtvcmRpbmFsXSAtIHJlbGV2YW50Q29udGVudHNbb3JkaW5hbF0sIDApICogcmVsZXZhbnRDb250ZW50c1tIRVJFXSAvIGlkZWFsRmxvd091dAogICAgICAgICAgICBmbG93W29yZGluYWxdICs9IGFtb3VudAogICAgICAgICAgICBjb250ZW50c1tIRVJFXSAtPSBhbW91bnQKCmRlZiBtYWluKGdyaWQpOgogICAgdCA9IDAKICAgIHdoaWxlIHQgPCBGUkFNRVM6CiAgICAgICAgZGlzcGxheUdyaWQoZ3JpZCwgdCkKICAgICAgICBuZXdHcmlkID0gZGVmYXVsdGRpY3QobGFtYmRhIDogQ2VsbChTUEFDRSwgRmFsc2UpKQogICAgICAgIGZvciBpLCBqIGluIHByb2R1Y3QocmFuZ2UoaGVpZ2h0KSwgcmFuZ2Uod2lkdGgpKToKICAgICAgICAgICAgY2VsbCA9IGdyaWRbaSxqXQogICAgICAgICAgICBpZiBjZWxsLmlzU291cmNlOgogICAgICAgICAgICAgICAgY2VsbC53YXRlciArPSBTT1VSQ0VfUkFURQogICAgICAgICAgICBuZXdDZWxsID0gbmV3R3JpZFtpLGpdCiAgICAgICAgICAgIG5ld0NlbGwuY29weUNlbGwoY2VsbCkKICAgICAgICAgICAgZmxvdyA9IGRlZmF1bHRkaWN0KGludCkKICAgICAgICAgICAgCiAgICAgICAgICAgIGlmIGNlbGwud2F0ZXIgPD0gMDoKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIAogICAgICAgICAgICBuZWlnaGJvdXJob29kID0gYWRqb2ludChncmlkLCBpLCBqLCBUcnVlLCBUcnVlKQoKICAgICAgICAgICAgY2FwYWNpdGllcyA9IHtIRVJFOiBjZWxsLmdldENhcGFjaXR5KCl9CiAgICAgICAgICAgIGNvbnRlbnRzID0ge0hFUkU6IGNlbGwud2F0ZXJ9CiAgICAgICAgICAgIGZvciBvcmRpbmFsIGluIE9SRElOQUxTOgogICAgICAgICAgICAgICAgaWYgb3JkaW5hbCBpbiBuZWlnaGJvdXJob29kLmtleXMoKToKICAgICAgICAgICAgICAgICAgICBjYXBhY2l0aWVzW29yZGluYWxdID0gZ3JpZFtuZWlnaGJvdXJob29kW29yZGluYWxdXS5nZXRDYXBhY2l0eSgpCiAgICAgICAgICAgICAgICAgICAgY29udGVudHNbb3JkaW5hbF0gPSBncmlkW25laWdoYm91cmhvb2Rbb3JkaW5hbF1dLndhdGVyCiAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgIGNhcGFjaXRpZXNbb3JkaW5hbF0gPSAwCiAgICAgICAgICAgICAgICAgICAgY29udGVudHNbb3JkaW5hbF0gPSAwCiAgICAgICAgICAgIAogICAgICAgICAgICAjIERvd24KICAgICAgICAgICAgdXBkYXRlRmxvd0Rvd24oZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgIGlmIGNvbnRlbnRzW0hFUkVdID4gMDoKICAgICAgICAgICAgICAgICMgTGVmdCBhbmQgUmlnaHQKICAgICAgICAgICAgICAgIHVwZGF0ZUZsb3dBY3Jvc3MoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgICAgICBpZiBjb250ZW50c1tIRVJFXSA+IDA6CiAgICAgICAgICAgICAgICAgICAgIyBIZXJlCiAgICAgICAgICAgICAgICAgICAgdXBkYXRlRmxvd0hlcmUoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgICAgICAgICAgaWYgY29udGVudHNbSEVSRV0gPiAwOgogICAgICAgICAgICAgICAgICAgICAgICAjIFVwCiAgICAgICAgICAgICAgICAgICAgICAgICNwcmludCAnQmVmb3JlICVmJyVjb250ZW50c1tIRVJFXQogICAgICAgICAgICAgICAgICAgICAgICB1cGRhdGVGbG93VXAoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgICAgICAgICAgICAgICNwcmludCAnVXAgZnJvbSAlZCAlZDogJXMnJShpLCBqLCBmbG93KQogICAgICAgICAgICAgICAgICAgICAgICAjcHJpbnQgJ0FmdGVyICVmJyVjb250ZW50c1tIRVJFXQogICAgICAgICAgICBmb3Igb3JkaW5hbCwgYW1vdW50IGluIGZsb3cuaXRlbXMoKToKICAgICAgICAgICAgICAgIGlmIGFtb3VudCA9PSAwOgogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICBpZiBvcmRpbmFsIG5vdCBpbiBuZWlnaGJvdXJob29kLmtleXMoKToKICAgICAgICAgICAgICAgICAgICBwcmludCAnJXMgbm90IGluICVzIHdoZW4gdHJ5aW5nIHRvIG1vdmUgJWYnJShvcmRpbmFsLCBuZWlnaGJvdXJob29kLmtleXMoKSwgYW1vdW50KQogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICAjIFNtb290aCB0aGUgbW92ZW1lbnQgYnkgb25seSBmbG93aW5nIGhhbGYgdGhlIGFtb3VudCBsZWZ0IGFuZCByaWdodAogICAgICAgICAgICAgICAgaWYgb3JkaW5hbCBpbiBbTEVGVCwgUklHSFRdOgogICAgICAgICAgICAgICAgICAgIGFtb3VudCA9IGludChhbW91bnQgLyAyKQogICAgICAgICAgICAgICAgbmV3R3JpZFtuZWlnaGJvdXJob29kW29yZGluYWxdXS53YXRlciArPSBhbW91bnQKICAgICAgICAgICAgICAgIG5ld0NlbGwud2F0ZXIgLT0gYW1vdW50CgogICAgICAgIGdyaWQgPSBuZXdHcmlkCiAgICAgICAgCiAgICAgICAgdCArPSAxCgppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgogICAgaW1wb3J0IGRvY3Rlc3QKICAgICMgRG9lc24ndCB0ZXN0IGlubmVyIGZ1bmN0aW9ucyEKICAgIGZhaWx1cmVzLCBwYXNzZXMgPSBkb2N0ZXN0LnRlc3Rtb2QocmFpc2Vfb25fZXJyb3I9RmFsc2UpCiAgICBpZiBmYWlsdXJlcyA9PSAwOgogICAgICAgIG1haW4oZ3JpZCkKICAgIGVsc2U6CiAgICAgICAgcHJpbnQgJ1N0b3BwaW5nIHdpdGggJWQgZmFpbHVyZXMnJWZhaWx1cmVzCgoKCgoKCgoKCgoKCgoK