# -*- 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 = """\
# #
#xx#
# # #
# # #
# # #
# # #
# # #
# # #
# # #
# # #
# # #
# #
######"""
gridStr = """\
x #
# # #
# # #
# # #
# #
######"""
gridStr = """\
x
# #
# #
# #
#### #####
#### #####
#### #####
##########"""
gridStr = """\
x
# # #
# # #
# # #
# # #
# # #
#######################"""
gridStr = """\
xx //##\\ \\
\ x /
\ /
# /\ \/ ###
# / \ # #
# / / # #
# / / # #
# / \ / \ #
#######################"""
FRAMES = 150
# 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' , ( 400 , 200 ) )
draw = ImageDraw.Draw ( img)
font = ImageFont.truetype ( '/usr/share/fonts/truetype/freefont/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 updateFlow( flow, relevantContents, relevantCapacity) :
"""Update the flow given some contents to split between some directions in ratio with the capacities."""
totalCapacity = sum ( relevantCapacity.values ( ) )
totalContents = sum ( relevantContents.values ( ) )
target = dict ( ( ordinal, capacity * totalContents / totalCapacity) for ( ordinal, capacity) in relevantCapacity.items ( ) )
targetFlow = dict ( ( ordinal, max ( target[ ordinal] - relevantContents[ ordinal] , 0 ) ) for ordinal in target.keys ( ) )
connectedOrdinals = set ( ORDINALS) .intersection ( targetFlow.keys ( ) )
totalTargetOutFlow = sum ( targetFlow[ ordinal] for ordinal in connectedOrdinals)
if totalTargetOutFlow <= 0 :
return 0
actualOutFlow = min ( totalTargetOutFlow, relevantContents[ HERE] )
totalFlowed = 0
for ordinal in connectedOrdinals:
amount = int ( targetFlow[ ordinal] * actualOutFlow / totalTargetOutFlow)
flow[ ordinal] += amount
totalFlowed += amount
return totalFlowed
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 )
totalFlowed = updateFlow( flow, relevantContents, relevantCapacity)
contents[ HERE] -= totalFlowed
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] * 0.55
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 )
totalFlowed = updateFlow( flow, relevantContents, relevantCapacity)
contents[ HERE] -= totalFlowed
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+JwpQT09MID0gJ04nCiAKU09VUkNFID0gJ3gnClNPVVJDRV9BTkRfRkFMTCA9ICcqJwpTT1VSQ0VfQU5EX1JJVkVSID0gJ1gnClNPVVJDRV9BTkRfUE9PTCA9ICclJwogCkJMT0NLID0gJyMnCkxSQU1QID0gJy8nClJSQU1QID0gJ1xcJwpTUEFDRSA9ICcgJwogCkxSQU1QX0FORF9GQUxMID0gJ2QnClJSQU1QX0FORF9GQUxMID0gJ2InCkxSQU1QX0FORF9SSVZFUiA9ICd9JwpSUkFNUF9BTkRfUklWRVIgPSAneycKTFJBTVBfQU5EX1BPT0wgPSAnXScKUlJBTVBfQU5EX1BPT0wgPSAnWycKCkhFUkUgPSAoMCwwKQpET1dOID0gKDEsMCkKTEVGVCA9ICgwLC0xKQpSSUdIVCA9ICgwLDEpClVQID0gKC0xLDApCk9SRElOQUxTID0gW0RPV04sTEVGVCxSSUdIVCxVUF0KCmdyaWRTdHIgPSAiIiJcCiN4IwojICMKIyAjCiMgIwojICMKIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiMgeCAjCiMgICAjCiMgICAjCiMgICAjCiMgICAjCiMjIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiAgeCAgCiMgIC8gCiMjIyMjIiIiCmdyaWRTdHIgPSAiIiJcCiAgeCAgIAojICAgLyAKIyMjIyMgCiMgICAjIAojICMgIC8KIyMjXC8jIiIiCmdyaWRTdHIgPSAiIiJcCiMgICMgIAojeHgjICAKIyAgIyAjCiMgICMgIwojICAjICMKIyAgIyAjCiMgICMgIwojICAjICMKIyAgIyAjCiMgICMgIwojICAjICMKIyAgICAjCiMjIyMjIyIiIgpncmlkU3RyID0gIiIiXAogeCAjICAKIyAgIyAjCiMgICMgIwojICAjICMKIyAgICAjCiMjIyMjIyIiIgpncmlkU3RyID0gIiIiXAogICB4ICAgICAgCiMgICAgICAgICMKIyAgICAgICAgIwojICAgICAgICAjCiMjIyMgIyMjIyMKIyMjIyAjIyMjIwojIyMjICMjIyMjCiMjIyMjIyMjIyMiIiIKZ3JpZFN0ciA9ICIiIlwKICAgICAgICAgICAgICAgICAgICB4ICAKICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAKIyAgICAgICAgICAgICAgICAgICAjICMKIyAgICAgICAgICAgICAgICAgICAjICMKIyAgICAgICAgICAgICAgICAgICAjICMKIyAgICAgICAgICAgICAgICAgICAjICMKIyAgICAgICAgICAgICAgICAgICAjICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMiIiIKZ3JpZFN0ciA9ICIiIlwKICAgIHh4ICAgICAgICAvLyMjXFxcXCAgIAogICAgICAgICAgICAgIFwgIHggLyAgIAogICAgICAgICAgICAgICBcICAvICAgIAojICAgL1wgICAgICAgICAgXC8gICMjIwojICAvICBcICAgICAgICAgICAgICMgIwojICAgICAgICAvICAgLyAgICAgICMgIwojICAgICAgIC8gICAvICAgICAgICMgIwojICAgICAgLyBcIC8gIFwgICAgICAgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyIiIgoKRlJBTUVTID0gMTUwCgojIFRoZSBjYXBhY2l0eSBvZiBhIHJhbXAgY2VsbC4KSEFMRl9DQVBBQ0lUWSA9IDUwCiMgVGhlIGNhcGFjaXR5IG9mIGFuIGVtcHR5IGNlbGwuCk1BWF9DQVBBQ0lUWSA9IDIqSEFMRl9DQVBBQ0lUWQojIFRoZSByYXRlIG9mIGZsb3cgaW4gZnJvbSBhIHNvdXJjZS4KU09VUkNFX1JBVEUgPSBIQUxGX0NBUEFDSVRZCiMgVGhlIG1heGltdW0gcmF0ZSBvZiBmbG93IGJldHdlZW4gdHdvIGNlbGxzLgpNQVhfRkxPVyA9IE1BWF9DQVBBQ0lUWQojIFRoZSByYXRpbyBleHRyYSBhIGNlbGwgY2FuIGhvbGQgaWYgdGhlIGNlbGwgYWJvdmUgaXMgZnVsbCAoYXNzdW1pbmcgYm90aCBoYXZlIHNhbWUgY2FwYWNpdHkpCkNPTVBSRVNTQUJJTElUWSA9IDAuMjUKClNQQUNFX1RIUkVTSEhPTEQgPSBIQUxGX0NBUEFDSVRZIC8gMgpSSVZFUl9USFJFU0hIT0xEID0gSEFMRl9DQVBBQ0lUWSArIEhBTEZfQ0FQQUNJVFkgLyAyCgpkZWYgaXNTb3VyY2UoY2VsbCk6CiAgICByZXR1cm4gY2VsbCBpbiBbU09VUkNFLCBTT1VSQ0VfQU5EX0ZBTEwsIFNPVVJDRV9BTkRfUklWRVIsIFNPVVJDRV9BTkRfUE9PTF0KCmRlZiBpc0xSYW1wKGNlbGwpOgogICAgcmV0dXJuIGNlbGwgaW4gW0xSQU1QLCBMUkFNUF9BTkRfRkFMTCwgTFJBTVBfQU5EX1JJVkVSLCBMUkFNUF9BTkRfUE9PTF0KZGVmIGlzUlJhbXAoY2VsbCk6CiAgICByZXR1cm4gY2VsbCBpbiBbUlJBTVAsIFJSQU1QX0FORF9GQUxMLCBSUkFNUF9BTkRfUklWRVIsIFJSQU1QX0FORF9QT09MXQpkZWYgaXNSYW1wKGNlbGwpOgogICAgcmV0dXJuIGlzTFJhbXAoY2VsbCkgb3IgaXNSUmFtcChjZWxsKQoKZGVmIGZpbmRCYWNrZ3JvdW5kKGNlbGwpOgogICAgaWYgY2VsbCBpbiBbU1BBQ0UsIEZBTEwsIFJJVkVSLCBQT09MLCBTT1VSQ0VdOgogICAgICAgIHJldHVybiBTUEFDRQogICAgZWxpZiBjZWxsIGluIFtCTE9DS106CiAgICAgICAgcmV0dXJuIEJMT0NLCiAgICBlbGlmIGlzTFJhbXAoY2VsbCk6CiAgICAgICAgcmV0dXJuIExSQU1QCiAgICBlbGlmIGlzUlJhbXAoY2VsbCk6CiAgICAgICAgcmV0dXJuIFJSQU1QCiAgICByYWlzZSBFeGNlcHRpb24KCmNsYXNzIFdhdGVyVHlwZToKICAgIEZBTEwgPSAxCiAgICBTRVRUTEVEID0gMgoKY2xhc3MgQ2VsbDoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBiYWNrZ3JvdW5kLCBpc1NvdXJjZSk6CiAgICAgICAgc2VsZi5iYWNrZ3JvdW5kID0gYmFja2dyb3VuZAogICAgICAgIHNlbGYuaXNTb3VyY2UgPSBpc1NvdXJjZQogICAgICAgIHNlbGYud2F0ZXIgPSAwCiAgICAgICAgc2VsZi53YXRlclR5cGUgPSBXYXRlclR5cGUuRkFMTAogICAgZGVmIGdldENhcGFjaXR5KHNlbGYpOgogICAgICAgIHJldHVybiAoMCBpZiBzZWxmLmJhY2tncm91bmQgPT0gQkxPQ0sgZWxzZSAoSEFMRl9DQVBBQ0lUWSBpZiBpc1JhbXAoc2VsZi5iYWNrZ3JvdW5kKSBlbHNlIE1BWF9DQVBBQ0lUWSkpCiAgICBkZWYgY29weUNlbGwoc2VsZiwgY2VsbCk6CiAgICAgICAgc2VsZi5iYWNrZ3JvdW5kID0gY2VsbC5iYWNrZ3JvdW5kCiAgICAgICAgc2VsZi5pc1NvdXJjZSA9IGNlbGwuaXNTb3VyY2UKICAgICAgICAjIEluY2x1ZGUgYW55IHdhdGVyIGFscmVhZHkgaGVyZQogICAgICAgIHNlbGYud2F0ZXIgKz0gY2VsbC53YXRlcgogICAgICAgIHNlbGYud2F0ZXJUeXBlID0gY2VsbC53YXRlclR5cGUKICAgIGRlZiB1cGRhdGVUeXBlKHNlbGYsIGNlbGxCZWxvdyk6CiAgICAgICAgaWYgc2VsZi53YXRlciA9PSAwOgogICAgICAgICAgICBzZWxmLndhdGVyVHlwZSA9IFdhdGVyVHlwZS5TRVRUTEVECiAgICAgICAgZWxpZiBzZWxmLndhdGVyID09IHNlbGYuZ2V0Q2FwYWNpdHkoKToKICAgICAgICAgICAgIyBJZiBhIGNvbXBsZXRlbHkgc2F0dXJhdGVkIGJsb2NrIG9mIHdhdGVyIGlzIGZhbGxpbmcgaXQgc2hvdWxkIHN0aWxsIGxvb2sgZnVsbC4KICAgICAgICAgICAgc2VsZi53YXRlclR5cGUgPSBXYXRlclR5cGUuU0VUVExFRAogICAgICAgIGVsaWYgY2VsbEJlbG93LndhdGVyIDwgY2VsbEJlbG93LmdldENhcGFjaXR5KCkgKiAwLjk6CiAgICAgICAgICAgIHNlbGYud2F0ZXJUeXBlID0gV2F0ZXJUeXBlLkZBTEwKICAgICAgICBlbHNlOgogICAgICAgICAgICBzZWxmLndhdGVyVHlwZSA9IFdhdGVyVHlwZS5TRVRUTEVECiAgICBkZWYgX19zdHJfXyhzZWxmKToKICAgICAgICBpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIG5vdCBzZWxmLmlzU291cmNlIGFuZCBzZWxmLndhdGVyIDw9IFNQQUNFX1RIUkVTSEhPTEQ6CiAgICAgICAgICAgIHJldHVybiBTUEFDRQogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IFNQQUNFIGFuZCBub3Qgc2VsZi5pc1NvdXJjZSBhbmQgc2VsZi53YXRlclR5cGUgPT0gV2F0ZXJUeXBlLkZBTEw6CiAgICAgICAgICAgIHJldHVybiBGQUxMCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIG5vdCBzZWxmLmlzU291cmNlIGFuZCBzZWxmLndhdGVyIDw9IFJJVkVSX1RIUkVTSEhPTEQ6CiAgICAgICAgICAgIHJldHVybiBSSVZFUgogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IFNQQUNFIGFuZCBub3Qgc2VsZi5pc1NvdXJjZToKICAgICAgICAgICAgcmV0dXJuIFBPT0wKICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBTUEFDRSBhbmQgc2VsZi53YXRlciA8PSBTUEFDRV9USFJFU0hIT0xEOgogICAgICAgICAgICByZXR1cm4gU09VUkNFCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIHNlbGYud2F0ZXJUeXBlID09IFdhdGVyVHlwZS5GQUxMOgogICAgICAgICAgICByZXR1cm4gU09VUkNFX0FORF9GQUxMCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gU1BBQ0UgYW5kIHNlbGYud2F0ZXIgPD0gUklWRVJfVEhSRVNISE9MRDoKICAgICAgICAgICAgcmV0dXJuIFNPVVJDRV9BTkRfUklWRVIKICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBTUEFDRToKICAgICAgICAgICAgcmV0dXJuIFNPVVJDRV9BTkRfUE9PTAogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IEJMT0NLOgogICAgICAgICAgICByZXR1cm4gQkxPQ0sKICAgICAgICBlbGlmIGlzUmFtcChzZWxmLmJhY2tncm91bmQpIGFuZCBzZWxmLmlzU291cmNlOgogICAgICAgICAgICByYWlzZSBFeGNlcHRpb24KICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBMUkFNUCBhbmQgc2VsZi53YXRlciA8PSBTUEFDRV9USFJFU0hIT0xEIC8gMjoKICAgICAgICAgICAgcmV0dXJuIExSQU1QCiAgICAgICAgZWxpZiBzZWxmLmJhY2tncm91bmQgPT0gTFJBTVAgYW5kIHNlbGYud2F0ZXJUeXBlID09IFdhdGVyVHlwZS5GQUxMOgogICAgICAgICAgICByZXR1cm4gTFJBTVBfQU5EX0ZBTEwKICAgICAgICBlbGlmIHNlbGYuYmFja2dyb3VuZCA9PSBMUkFNUCBhbmQgc2VsZi53YXRlciA8PSBSSVZFUl9USFJFU0hIT0xEIC8gMjoKICAgICAgICAgICAgcmV0dXJuIExSQU1QX0FORF9SSVZFUgogICAgICAgIGVsaWYgc2VsZi5iYWNrZ3JvdW5kID09IExSQU1QOgogICAgICAgICAgICByZXR1cm4gTFJBTVBfQU5EX1BPT0wKICAgICAgICBlbGlmIHNlbGYud2F0ZXIgPD0gU1BBQ0VfVEhSRVNISE9MRCAvIDI6CiAgICAgICAgICAgIHJldHVybiBSUkFNUAogICAgICAgIGVsaWYgc2VsZi53YXRlclR5cGUgPT0gV2F0ZXJUeXBlLkZBTEw6CiAgICAgICAgICAgIHJldHVybiBSUkFNUF9BTkRfRkFMTAogICAgICAgIGVsaWYgc2VsZi53YXRlciA8PSBSSVZFUl9USFJFU0hIT0xEIC8gMjoKICAgICAgICAgICAgcmV0dXJuIFJSQU1QX0FORF9SSVZFUgogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJldHVybiBSUkFNUF9BTkRfUE9PTAoKZ3JpZENlbGxzID0gbWFwKGxhbWJkYSB4OiBsaXN0KHgpLCBncmlkU3RyLnNwbGl0KCdcbicpKQpoZWlnaHQgPSBsZW4oZ3JpZENlbGxzKQp3aWR0aCA9IGxlbihncmlkQ2VsbHNbMF0pCmdyaWQgPSBkZWZhdWx0ZGljdChsYW1iZGEgOiBDZWxsKFNQQUNFLCBGYWxzZSkpCmZvciBpIGluIHJhbmdlKGhlaWdodCk6CiAgICBmb3IgaiBpbiByYW5nZSh3aWR0aCk6CiAgICAgICAgZ3JpZFtpLGpdID0gQ2VsbChmaW5kQmFja2dyb3VuZChncmlkQ2VsbHNbaV1bal0pLCBpc1NvdXJjZShncmlkQ2VsbHNbaV1bal0pKQoKZGVmIHAoY29vcmRzLCBvZmZzZXQpOgogICAgIiIiQWRkIHR3byB0dXBsZXMgdG9nZXRoZXIuCiAgICAKICAgID4+PiBwKCgyLCAzKSwgKC0xLCAwKSkKICAgICgxLCAzKQogICAgIiIiCiAgICByZXR1cm4gdHVwbGUobWFwKHN1bSwgemlwKGNvb3Jkcywgb2Zmc2V0KSkpCgpkZWYgYWRqYWNlbnQoaSxqLGluY2x1ZGVVcCk6CiAgICByZXQgPSBbXQogICAgZm9yIG9mZnNldCBpbiAoT1JESU5BTFMgaWYgaW5jbHVkZVVwIGVsc2UgT1JESU5BTFNbOi0xXSk6CiAgICAgICAgcmV0LmFwcGVuZChwKChpLGopLCBvZmZzZXQpKQogICAgcmV0dXJuIHJldAoKQ09OTkVDVElPTl9UT19TSEFQRVMgPSB7RE9XTjogW1NQQUNFLCBMUkFNUCwgUlJBTVBdLCBMRUZUOiBbU1BBQ0UsIFJSQU1QXSwgUklHSFQ6IFtTUEFDRSwgTFJBTVBdLCBVUDogW1NQQUNFXX0KCmRlZiBhZGpvaW50KGdyaWQsIGksIGosIGluY2x1ZGVVcCwgaW5jbHVkZURvd24pOgogICAgIiIiRmluZCBhbGwgY2VsbHMgY29ubmVjdGVkIHRvIHRoaXMgb25lIChpZiB0aGlzIGlzIGEgYmxvY2sgdGhlbiBub3RoaW5nIGNvbm5lY3RzIHRvIGl0KS4KICAgIAogICAgPj4+IGdyaWQgPSBkZWZhdWx0ZGljdChsYW1iZGEgOiBDZWxsKFNQQUNFLCBGYWxzZSkpCiAgICA+Pj4gYWRqb2ludChncmlkLCAxMCwgMSwgVHJ1ZSwgVHJ1ZSkKICAgIHsoMCwgMSk6ICgxMCwgMiksICgwLCAtMSk6ICgxMCwgMCksICgxLCAwKTogKDExLCAxKSwgKC0xLCAwKTogKDksIDEpfQogICAgCiAgICA+Pj4gYWRqb2ludChncmlkLCAxMCwgMSwgRmFsc2UsIFRydWUpCiAgICB7KDAsIDEpOiAoMTAsIDIpLCAoMCwgLTEpOiAoMTAsIDApLCAoMSwgMCk6ICgxMSwgMSl9CiAgICAKICAgID4+PiBhZGpvaW50KGdyaWQsIDEwLCAxLCBGYWxzZSwgRmFsc2UpCiAgICB7KDAsIDEpOiAoMTAsIDIpLCAoMCwgLTEpOiAoMTAsIDApfQogICAgCiAgICA+Pj4gZ3JpZFsxMCwgMV0uYmFja2dyb3VuZCA9IExSQU1QCiAgICA+Pj4gYWRqb2ludChncmlkLCAxMCwgMSwgVHJ1ZSwgVHJ1ZSkKICAgIHsoMCwgLTEpOiAoMTAsIDApLCAoLTEsIDApOiAoOSwgMSl9CiAgICAKICAgID4+PiBncmlkWzEwLCAwXS5iYWNrZ3JvdW5kID0gTFJBTVAKICAgID4+PiBncmlkWzksIDFdLmJhY2tncm91bmQgPSBSUkFNUAogICAgPj4+IGFkam9pbnQoZ3JpZCwgMTAsIDEsIFRydWUsIFRydWUpCiAgICB7fQogICAgIiIiCiAgICByZXQgPSB7fQogICAgY2VsbCA9IGdyaWRbaSwgal0KICAgIGlmIGNlbGwuYmFja2dyb3VuZCA9PSBCTE9DSzoKICAgICAgICBhZGogPSBbXQogICAgZWxpZiBjZWxsLmJhY2tncm91bmQgPT0gTFJBTVA6CiAgICAgICAgYWRqID0gW0xFRlQsIFVQXQogICAgZWxpZiBjZWxsLmJhY2tncm91bmQgPT0gUlJBTVA6CiAgICAgICAgYWRqID0gW1JJR0hULCBVUF0KICAgIGVsaWYgY2VsbC5iYWNrZ3JvdW5kID09IFNQQUNFOgogICAgICAgIGFkaiA9IGxpc3QoT1JESU5BTFMpCiAgICBpZiBub3QgaW5jbHVkZVVwIGFuZCBVUCBpbiBhZGo6CiAgICAgICAgYWRqLnJlbW92ZShVUCkKICAgIGlmIG5vdCBpbmNsdWRlRG93biBhbmQgRE9XTiBpbiBhZGo6CiAgICAgICAgYWRqLnJlbW92ZShET1dOKQogICAgZm9yIG9mZnNldCBpbiBhZGo6CiAgICAgICAgbmVpZ2hib3VyQ29vcmRzID0gcCgoaSxqKSwgb2Zmc2V0KQogICAgICAgIG5laWdoYm91ciA9IGdyaWRbbmVpZ2hib3VyQ29vcmRzXQogICAgICAgIGlmIG5laWdoYm91ci5iYWNrZ3JvdW5kIGluIENPTk5FQ1RJT05fVE9fU0hBUEVTW29mZnNldF06CiAgICAgICAgICAgIHJldFtvZmZzZXRdID0gbmVpZ2hib3VyQ29vcmRzCiAgICByZXR1cm4gcmV0CgpkZWYgbWFrZUZyYW1lKHRleHRMaW5lcywgdCk6CiAgICBpbWcgPSBJbWFnZS5uZXcoJ1JHQicsICg0MDAsIDIwMCkpCiAgICBkcmF3ID0gSW1hZ2VEcmF3LkRyYXcoaW1nKQogICAgZm9udCA9IEltYWdlRm9udC50cnVldHlwZSgnL3Vzci9zaGFyZS9mb250cy90cnVldHlwZS9mcmVlZm9udC9GcmVlTW9uby50dGYnLCAxNCwgZW5jb2Rpbmc9J3VuaWMnKQogICAgeSA9IDAKICAgIGZvciBsaW5lIGluIHRleHRMaW5lczoKICAgICAgICBkcmF3LnRleHQoKDAsIHkpLCBsaW5lLCAoMjU1LDI1NSwyNTUpLCBmb250PWZvbnQpCiAgICAgICAgeSArPSAyMAogICAgaW1nLnNhdmUoJ2ZyYW1lXyUwNGQucG5nJyV0KQoKZGVmIGRpc3BsYXlHcmlkKGdyaWQsIHQpOgogICAgcHJpbnQKICAgIHRleHRMaW5lcyA9IFtdCiAgICBmb3IgaSBpbiByYW5nZShoZWlnaHQpOgogICAgICAgIHJvdyA9ICcnCiAgICAgICAgZm9yIGogaW4gcmFuZ2Uod2lkdGgpOgogICAgICAgICAgICBncmlkW2ksal0udXBkYXRlVHlwZShncmlkW2krMSxqXSkKICAgICAgICAgICAgcm93ICs9IHN0cihncmlkW2ksal0pCiAgICAgICAgcm93ICs9ICcgJwogICAgICAgIGZvciBqIGluIHJhbmdlKHdpZHRoKToKICAgICAgICAgICAgcm93ICs9ICgnJXgnJShncmlkW2ksal0ud2F0ZXIgLyAxMCkgaWYgZ3JpZFtpLGpdLndhdGVyIDwgMTYwIGVsc2UgJysnKQogICAgICAgIHRleHRMaW5lcy5hcHBlbmQocm93KQogICAgdGV4dCA9ICdcbicuam9pbih0ZXh0TGluZXMpCiAgICBwcmludCB0ZXh0CiAgICBtYWtlRnJhbWUodGV4dExpbmVzLCB0KQoKZGVmIGNvbnN0cmFpbihuLCBtaW5OLCBtYXhOKToKICAgIGlmIG4gPCBtaW5OOgogICAgICAgIHJldHVybiBtaW5OCiAgICBlbGlmIG4gPiBtYXhOOgogICAgICAgIHJldHVybiBtYXhOCiAgICByZXR1cm4gbgoKZGVmIHVwZGF0ZUZsb3coZmxvdywgcmVsZXZhbnRDb250ZW50cywgcmVsZXZhbnRDYXBhY2l0eSk6CiAgICAiIiJVcGRhdGUgdGhlIGZsb3cgZ2l2ZW4gc29tZSBjb250ZW50cyB0byBzcGxpdCBiZXR3ZWVuIHNvbWUgZGlyZWN0aW9ucyBpbiByYXRpbyB3aXRoIHRoZSBjYXBhY2l0aWVzLiIiIgogICAgdG90YWxDYXBhY2l0eSA9IHN1bShyZWxldmFudENhcGFjaXR5LnZhbHVlcygpKQogICAgdG90YWxDb250ZW50cyA9IHN1bShyZWxldmFudENvbnRlbnRzLnZhbHVlcygpKQogICAgdGFyZ2V0ID0gZGljdCgob3JkaW5hbCwgY2FwYWNpdHkgKiB0b3RhbENvbnRlbnRzIC8gdG90YWxDYXBhY2l0eSkgZm9yIChvcmRpbmFsLCBjYXBhY2l0eSkgaW4gcmVsZXZhbnRDYXBhY2l0eS5pdGVtcygpKQogICAgdGFyZ2V0RmxvdyA9IGRpY3QoKG9yZGluYWwsIG1heCh0YXJnZXRbb3JkaW5hbF0gLSByZWxldmFudENvbnRlbnRzW29yZGluYWxdLCAwKSkgZm9yIG9yZGluYWwgaW4gdGFyZ2V0LmtleXMoKSkKICAgIGNvbm5lY3RlZE9yZGluYWxzID0gc2V0KE9SRElOQUxTKS5pbnRlcnNlY3Rpb24odGFyZ2V0Rmxvdy5rZXlzKCkpCiAgICB0b3RhbFRhcmdldE91dEZsb3cgPSBzdW0odGFyZ2V0Rmxvd1tvcmRpbmFsXSBmb3Igb3JkaW5hbCBpbiBjb25uZWN0ZWRPcmRpbmFscykKICAgIGlmIHRvdGFsVGFyZ2V0T3V0RmxvdyA8PSAwOgogICAgICAgIHJldHVybiAwCiAgICBhY3R1YWxPdXRGbG93ID0gbWluKHRvdGFsVGFyZ2V0T3V0RmxvdywgcmVsZXZhbnRDb250ZW50c1tIRVJFXSkKICAgIHRvdGFsRmxvd2VkID0gMAogICAgZm9yIG9yZGluYWwgaW4gY29ubmVjdGVkT3JkaW5hbHM6CiAgICAgICAgYW1vdW50ID0gaW50KHRhcmdldEZsb3dbb3JkaW5hbF0gKiBhY3R1YWxPdXRGbG93IC8gdG90YWxUYXJnZXRPdXRGbG93KQogICAgICAgIGZsb3dbb3JkaW5hbF0gKz0gYW1vdW50CiAgICAgICAgdG90YWxGbG93ZWQgKz0gYW1vdW50CiAgICByZXR1cm4gdG90YWxGbG93ZWQKCmRlZiB1cGRhdGVGbG93RG93bihmbG93LCBjb250ZW50cywgY2FwYWNpdGllcyk6CiAgICBmbG93RG93biA9IGNvbnN0cmFpbihjYXBhY2l0aWVzW0RPV05dIC0gY29udGVudHNbRE9XTl0sIDAsIGNvbnRlbnRzW0hFUkVdKQogICAgZmxvd1tET1dOXSArPSBmbG93RG93bgogICAgY29udGVudHNbSEVSRV0gLT0gZmxvd0Rvd24KCmRlZiB1cGRhdGVGbG93QWNyb3NzKGZsb3csIGNvbnRlbnRzLCBjYXBhY2l0aWVzKToKICAgICIiIlVwZGF0ZSBmbG93IGFjcm9zcyBhbmQgYSBiaXQgZG93biB0byBzaW11bGF0ZSBwcmVzc3VyZSBhdCB0aGlzIGxldmVsLiIiIgogICAgcmVsZXZhbnRDYXBhY2l0eSA9IHt9CiAgICBmb3Igb3JkaW5hbCBpbiBbTEVGVCwgSEVSRSwgUklHSFRdOgogICAgICAgIHJlbGV2YW50Q2FwYWNpdHlbb3JkaW5hbF0gPSBjYXBhY2l0aWVzW29yZGluYWxdCiAgICByZWxldmFudENhcGFjaXR5W0RPV05dID0gY2FwYWNpdGllc1tET1dOXSAqIENPTVBSRVNTQUJJTElUWQogICAgcmVsZXZhbnRDb250ZW50cyA9IHt9CiAgICBmb3Igb3JkaW5hbCBpbiBbTEVGVCwgSEVSRSwgUklHSFRdOgogICAgICAgIHJlbGV2YW50Q29udGVudHNbb3JkaW5hbF0gPSBjb25zdHJhaW4oY29udGVudHNbb3JkaW5hbF0sIDAsIGNhcGFjaXRpZXNbb3JkaW5hbF0pCiAgICByZWxldmFudENvbnRlbnRzW0RPV05dID0gbWF4KGNvbnRlbnRzW0RPV05dIC0gY2FwYWNpdGllc1tET1dOXSwgMCkKCiAgICB0b3RhbEZsb3dlZCA9IHVwZGF0ZUZsb3coZmxvdywgcmVsZXZhbnRDb250ZW50cywgcmVsZXZhbnRDYXBhY2l0eSkKICAgIGNvbnRlbnRzW0hFUkVdIC09IHRvdGFsRmxvd2VkCgpkZWYgdXBkYXRlRmxvd0hlcmUoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpOgogICAgIiIiQ3JlYXRlIGEgJ2Zsb3cnIHRvIHRoZSBjdXJyZW50IGNlbGwuIEFueSByZW1haW5pbmcgY2FuIGJlIHB1c2hlZCB1cHdhcmRzLiIiIgogICAgY29uc3RyYWluZWQgPSBjb25zdHJhaW4oY29udGVudHNbSEVSRV0sIDAsIGNhcGFjaXRpZXNbSEVSRV0pCiAgICAjIFRoZSB3YXRlciB3aWxsIG5vdCBhY3R1YWxseSBsZWF2ZSB0aGUgY2VsbCwgc28gbm8gbmVlZCB0byBhZGQgYW4gZXhwbGljaXQgZmxvdwogICAgI2Zsb3dbSEVSRV0gKz0gY29uc3RyYWluZWQKICAgIGNvbnRlbnRzW0hFUkVdIC09IGNvbnN0cmFpbmVkCgpkZWYgdXBkYXRlRmxvd1VwKGZsb3csIGNvbnRlbnRzLCBjYXBhY2l0aWVzKToKICAgICIiIlVwZGF0ZSBmbG93IHVwIGFuZCBhIGJpdCBhY3Jvc3MgYW5kIGRvd24gdG8gc2ltdWxhdGUgcHJlc3N1cmUgYXQgdGhlIGxldmVsIGFib3ZlLiIiIgogICAgcmVsZXZhbnRDYXBhY2l0eSA9IHt9CiAgICAjIE1hZ2ljIGNvbnN0YW50IGhlcmU6IDEuNCBzZWVtcyB0byB3b3JrIGJlc3QhCiAgICByZWxldmFudENhcGFjaXR5W1VQXSA9IGNhcGFjaXRpZXNbVVBdICogMC41NQogICAgZm9yIG9yZGluYWwgaW4gW0xFRlQsIEhFUkUsIFJJR0hUXToKICAgICAgICByZWxldmFudENhcGFjaXR5W29yZGluYWxdID0gY2FwYWNpdGllc1tvcmRpbmFsXSAqIENPTVBSRVNTQUJJTElUWQogICAgcmVsZXZhbnRDYXBhY2l0eVtET1dOXSA9IGNhcGFjaXRpZXNbRE9XTl0gKiBDT01QUkVTU0FCSUxJVFkgKiAoMSArIENPTVBSRVNTQUJJTElUWSkKICAgIHJlbGV2YW50Q29udGVudHMgPSB7fQogICAgcmVsZXZhbnRDb250ZW50c1tVUF0gPSBjb250ZW50c1tVUF0KICAgIHJlbGV2YW50Q29udGVudHNbSEVSRV0gPSBjb250ZW50c1tIRVJFXQogICAgZm9yIG9yZGluYWwgaW4gW0xFRlQsIFJJR0hUXToKICAgICAgICByZWxldmFudENvbnRlbnRzW29yZGluYWxdID0gbWF4KGNvbnRlbnRzW29yZGluYWxdIC0gY2FwYWNpdGllc1tvcmRpbmFsXSwgMCkKICAgIHJlbGV2YW50Q29udGVudHNbRE9XTl0gPSBtYXgoY29udGVudHNbRE9XTl0gLSBjYXBhY2l0aWVzW0RPV05dICogKDEgKyBDT01QUkVTU0FCSUxJVFkpLCAwKQoKICAgIHRvdGFsRmxvd2VkID0gdXBkYXRlRmxvdyhmbG93LCByZWxldmFudENvbnRlbnRzLCByZWxldmFudENhcGFjaXR5KQogICAgY29udGVudHNbSEVSRV0gLT0gdG90YWxGbG93ZWQKCmRlZiBtYWluKGdyaWQpOgogICAgdCA9IDAKICAgIHdoaWxlIHQgPCBGUkFNRVM6CiAgICAgICAgZGlzcGxheUdyaWQoZ3JpZCwgdCkKICAgICAgICBuZXdHcmlkID0gZGVmYXVsdGRpY3QobGFtYmRhIDogQ2VsbChTUEFDRSwgRmFsc2UpKQogICAgICAgIGZvciBpLCBqIGluIHByb2R1Y3QocmFuZ2UoaGVpZ2h0KSwgcmFuZ2Uod2lkdGgpKToKICAgICAgICAgICAgY2VsbCA9IGdyaWRbaSxqXQogICAgICAgICAgICBpZiBjZWxsLmlzU291cmNlOgogICAgICAgICAgICAgICAgY2VsbC53YXRlciArPSBTT1VSQ0VfUkFURQogICAgICAgICAgICBuZXdDZWxsID0gbmV3R3JpZFtpLGpdCiAgICAgICAgICAgIG5ld0NlbGwuY29weUNlbGwoY2VsbCkKICAgICAgICAgICAgZmxvdyA9IGRlZmF1bHRkaWN0KGludCkKICAgICAgICAgICAgCiAgICAgICAgICAgIGlmIGNlbGwud2F0ZXIgPD0gMDoKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIAogICAgICAgICAgICBuZWlnaGJvdXJob29kID0gYWRqb2ludChncmlkLCBpLCBqLCBUcnVlLCBUcnVlKQoKICAgICAgICAgICAgY2FwYWNpdGllcyA9IHtIRVJFOiBjZWxsLmdldENhcGFjaXR5KCl9CiAgICAgICAgICAgIGNvbnRlbnRzID0ge0hFUkU6IGNlbGwud2F0ZXJ9CiAgICAgICAgICAgIGZvciBvcmRpbmFsIGluIE9SRElOQUxTOgogICAgICAgICAgICAgICAgaWYgb3JkaW5hbCBpbiBuZWlnaGJvdXJob29kLmtleXMoKToKICAgICAgICAgICAgICAgICAgICBjYXBhY2l0aWVzW29yZGluYWxdID0gZ3JpZFtuZWlnaGJvdXJob29kW29yZGluYWxdXS5nZXRDYXBhY2l0eSgpCiAgICAgICAgICAgICAgICAgICAgY29udGVudHNbb3JkaW5hbF0gPSBncmlkW25laWdoYm91cmhvb2Rbb3JkaW5hbF1dLndhdGVyCiAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgIGNhcGFjaXRpZXNbb3JkaW5hbF0gPSAwCiAgICAgICAgICAgICAgICAgICAgY29udGVudHNbb3JkaW5hbF0gPSAwCiAgICAgICAgICAgIAogICAgICAgICAgICAjIERvd24KICAgICAgICAgICAgdXBkYXRlRmxvd0Rvd24oZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgIGlmIGNvbnRlbnRzW0hFUkVdID4gMDoKICAgICAgICAgICAgICAgICMgTGVmdCBhbmQgUmlnaHQKICAgICAgICAgICAgICAgIHVwZGF0ZUZsb3dBY3Jvc3MoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgICAgICBpZiBjb250ZW50c1tIRVJFXSA+IDA6CiAgICAgICAgICAgICAgICAgICAgIyBIZXJlCiAgICAgICAgICAgICAgICAgICAgdXBkYXRlRmxvd0hlcmUoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgICAgICAgICAgaWYgY29udGVudHNbSEVSRV0gPiAwOgogICAgICAgICAgICAgICAgICAgICAgICAjIFVwCiAgICAgICAgICAgICAgICAgICAgICAgICNwcmludCAnQmVmb3JlICVmJyVjb250ZW50c1tIRVJFXQogICAgICAgICAgICAgICAgICAgICAgICB1cGRhdGVGbG93VXAoZmxvdywgY29udGVudHMsIGNhcGFjaXRpZXMpCiAgICAgICAgICAgICAgICAgICAgICAgICNwcmludCAnVXAgZnJvbSAlZCAlZDogJXMnJShpLCBqLCBmbG93KQogICAgICAgICAgICAgICAgICAgICAgICAjcHJpbnQgJ0FmdGVyICVmJyVjb250ZW50c1tIRVJFXQogICAgICAgICAgICBmb3Igb3JkaW5hbCwgYW1vdW50IGluIGZsb3cuaXRlbXMoKToKICAgICAgICAgICAgICAgIGlmIGFtb3VudCA9PSAwOgogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICBpZiBvcmRpbmFsIG5vdCBpbiBuZWlnaGJvdXJob29kLmtleXMoKToKICAgICAgICAgICAgICAgICAgICBwcmludCAnJXMgbm90IGluICVzIHdoZW4gdHJ5aW5nIHRvIG1vdmUgJWYnJShvcmRpbmFsLCBuZWlnaGJvdXJob29kLmtleXMoKSwgYW1vdW50KQogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICAjIFNtb290aCB0aGUgbW92ZW1lbnQgYnkgb25seSBmbG93aW5nIGhhbGYgdGhlIGFtb3VudCBsZWZ0IGFuZCByaWdodAogICAgICAgICAgICAgICAgI2lmIG9yZGluYWwgaW4gW0xFRlQsIFJJR0hUXToKICAgICAgICAgICAgICAgICMgICAgYW1vdW50ID0gaW50KGFtb3VudCAvIDIpCiAgICAgICAgICAgICAgICBuZXdHcmlkW25laWdoYm91cmhvb2Rbb3JkaW5hbF1dLndhdGVyICs9IGFtb3VudAogICAgICAgICAgICAgICAgbmV3Q2VsbC53YXRlciAtPSBhbW91bnQKCiAgICAgICAgZ3JpZCA9IG5ld0dyaWQKICAgICAgICAKICAgICAgICB0ICs9IDEKCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6CiAgICBpbXBvcnQgZG9jdGVzdAogICAgIyBEb2Vzbid0IHRlc3QgaW5uZXIgZnVuY3Rpb25zIQogICAgZmFpbHVyZXMsIHBhc3NlcyA9IGRvY3Rlc3QudGVzdG1vZChyYWlzZV9vbl9lcnJvcj1GYWxzZSkKICAgIGlmIGZhaWx1cmVzID09IDA6CiAgICAgICAgbWFpbihncmlkKQogICAgZWxzZToKICAgICAgICBwcmludCAnU3RvcHBpbmcgd2l0aCAlZCBmYWlsdXJlcyclZmFpbHVyZXMKCgoKCgoKCgoKCgoKCgo=