# life.py
# Glenn A. Richard
# Mechanical MOOC MIT OCW 6.189
# Project 2 Conway's Game of Life
# Currently set to create a Gosper Glider Gun
# Use Python 2.7
# July 24, 2013
# Modified July 30, 2013
## Code template written by Sarina Canelake & Kelly Casteel, August 2010
## Revised January 2011
from graphics import *
import random
############################################################
# GLOBAL VARIABLES
############################################################
BLOCK_SIZE = 16
BLOCK_OUTLINE_WIDTH = 2
BOARD_WIDTH = 50
BOARD_HEIGHT = 50
COLOR = "red" # I added this.
DELAY = 200
neighbor_test_blocklist = [ ( 0 , 0 ) , ( 1 , 1 ) ]
blinker_blocklist = [ ( 2 , 2 ) , ( 2 , 3 ) , ( 2 , 4 ) ]
toad_blocklist = [ ( 4 , 4 ) , ( 3 , 5 ) , ( 3 , 6 ) , ( 5 , 7 ) , ( 6 , 5 ) , ( 6 , 6 ) ]
beacon_blocklist = [ ( 2 , 3 ) , ( 2 , 4 ) , ( 3 , 3 ) , ( 3 , 4 ) , ( 4 , 5 ) , ( 4 , 6 ) , ( 5 , 5 ) , ( 5 , 6 ) ]
glider_blocklist = [ ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 1 ) , ( 3 , 2 ) , ( 3 , 3 ) ]
pulsar_blocklist = [ ( 2 , 4 ) , ( 2 , 5 ) , ( 2 , 6 ) , ( 4 , 2 ) , ( 4 , 7 ) , ( 5 , 2 ) , ( 5 , 7 ) ,
( 6 , 2 ) , ( 6 , 7 ) , ( 7 , 4 ) , ( 7 , 5 ) , ( 7 , 6 ) ]
gosper_glider_gun_blocklist = [ ( 1 , 5 ) , ( 1 , 6 ) , ( 2 , 5 ) , ( 2 , 6 ) , ( 11 , 5 ) , ( 11 , 6 ) , ( 11 , 7 ) , ( 12 , 4 ) , ( 12 , 8 ) , ( 13 , 3 ) , ( 13 , 9 ) , ( 14 , 3 ) , ( 14 , 9 ) , ( 15 , 6 ) , ( 16 , 4 ) , ( 16 , 8 ) , ( 17 , 5 ) , ( 17 , 6 ) , ( 17 , 7 ) , ( 18 , 6 ) , ( 21 , 3 ) , ( 21 , 4 ) , ( 21 , 5 ) , ( 22 , 3 ) , ( 22 , 4 ) , ( 22 , 5 ) , ( 23 , 2 ) , ( 23 , 6 ) , ( 25 , 1 ) , ( 25 , 2 ) , ( 25 , 6 ) , ( 25 , 7 ) , ( 35 , 3 ) , ( 35 , 4 ) , ( 36 , 3 ) , ( 36 , 4 ) ]
# for diehard, make board at least 25x25, might need to change block size
diehard_blocklist = [ ( 5 , 7 ) , ( 6 , 7 ) , ( 6 , 8 ) , ( 10 , 8 ) , ( 11 , 8 ) , ( 12 , 8 ) , ( 11 , 6 ) ]
# list of blocklists
blocklist_list = [ glider_blocklist, gosper_glider_gun_blocklist]
# blocklist_list = [toad_blocklist, beacon_blocklist, glider_blocklist, pulsar_blocklist, diehard_blocklist, gosper_glider_gun_blocklist]
############################################################
# TEST CODE (don't worry about understanding this section)
############################################################
def test_neighbors( board) :
'''
Code to test the board.get_block_neighbor function
'''
for block in board.block_list .values ( ) :
neighbors = board.get_block_neighbors ( block)
ncoords = [ neighbor.get_coords ( ) for neighbor in neighbors]
if block.get_coords ( ) == ( 0 , 0 ) :
zeroneighs = [ ( 0 , 1 ) , ( 1 , 1 ) , ( 1 , 0 ) ]
for n in ncoords:
if n not in zeroneighs:
print "Testing block at (0,0)"
print "Got" , ncoords
print "Expected" , zeroneighs
return False
for neighbor in neighbors:
if neighbor.get_coords ( ) == ( 1 , 1 ) :
if neighbor.is_live ( ) == False :
print "Testing block at (0, 0)..."
print "My neighbor at (1, 1) should be live; it is not."
print "Did you return my actual neighbors, or create new copies of them?"
print "FAIL: get_block_neighbors() should NOT return new Blocks!"
return False
elif block.get_coords ( ) == ( 1 , 1 ) :
oneneighs = [ ( 0 , 0 ) , ( 0 , 1 ) , ( 0 , 2 ) , ( 1 , 0 ) , ( 1 , 2 ) , ( 2 , 0 ) , ( 2 , 1 ) , ( 2 , 2 ) ]
for n in ncoords:
if n not in oneneighs:
print "Testing block at (1,1)"
print "Got" , ncoords
print "Expected" , oneneighs
return False
for n in oneneighs:
if n not in ncoords:
print "Testing block at (1,1)"
print "Got" , ncoords
print "Expected" , oneneighs
return False
print "Passed neighbor test"
return True
############################################################
# BLOCK CLASS (Read through and understand this part!)
############################################################
class Block( Rectangle) :
''' Block class:
Implement a block for a tetris piece
Attributes: x - type: int
y - type: int
specify the position on the board
in terms of the square grid
'''
def __init__ ( self , pos, color) :
'''
pos: a Point object specifing the (x, y) square of the Block (NOT in pixels!)
color: a string specifing the color of the block (eg 'blue' or 'purple')
'''
self .x = pos.x
self .y = pos.y
p1 = Point( pos.x *BLOCK_SIZE,
pos.y *BLOCK_SIZE)
p2 = Point( p1.x + BLOCK_SIZE, p1.y + BLOCK_SIZE)
Rectangle.__init__ ( self , p1, p2)
self .setWidth ( BLOCK_OUTLINE_WIDTH)
self .setFill ( color)
self .status = 'dead'
self .new_status = 'None'
def get_coords( self ) :
return ( self .x , self .y )
def set_live( self , canvas) :
'''
Sets the block status to 'live' and draws it on the grid.
Be sure to do this on the canvas!
'''
if self .status == 'dead' :
self .status = 'live'
self .draw ( canvas)
def set_dead( self ) :
'''
Sets the block status to 'dead' and undraws it from the grid.
'''
if self .status == 'live' :
self .status = 'dead'
self .undraw ( )
def is_live( self ) :
'''
Returns True if the block is currently 'live'. Returns False otherwise.
'''
if self .status == 'live' :
return True
return False
def reset_status( self , canvas) :
'''
Sets the new_status to be the current status
'''
if self .new_status == 'dead' :
self .set_dead ( )
elif self .new_status == 'live' :
self .set_live ( canvas)
###########################################################
# BOARD CLASS (Read through and understand this part!)
# Print out and turn in this section.
# Name:
# Recitation:
###########################################################
class Board( object ) :
''' Board class: it represents the Game of Life board
Attributes: width - type:int - width of the board in squares
height - type:int - height of the board in squares
canvas - type:CanvasFrame - where the blocks will be drawn
block_list - type:Dictionary - stores the blocks for a given position
'''
def __init__ ( self , win, width, height) :
self .width = width
self .height = height
self .win = win
# self.delay is the number of ms between each simulation. Change to be
# shorter or longer if you wish!
self .delay = DELAY
# create a canvas to draw the blocks on
self .canvas = CanvasFrame( win, self .width * BLOCK_SIZE,
self .height * BLOCK_SIZE)
self .canvas .setBackground ( 'white' )
# initialize grid lines
for x in range ( 1 , self .width ) :
self .draw_gridline ( Point( x, 0 ) , Point( x, self .height ) )
for y in range ( 1 , self .height ) :
self .draw_gridline ( Point( 0 , y) , Point( self .width , y) )
# For each square on the board, we need to initialize
# a block and store that block in a data structure. A
# dictionary (self.block_list) that has key:value pairs of
# (x,y):Block will be useful here.
self .block_list = { }
####### YOUR CODE HERE ######
## BLOCK_SIZE = 40
## BLOCK_OUTLINE_WIDTH = 2
## BOARD_WIDTH = 12
## BOARD_HEIGHT = 12
## # raise Exception("__init__ not implemented")
for x in range ( BOARD_WIDTH) :
for y in range ( BOARD_HEIGHT) :
self .block_list [ x, y] = Block( Point( x, y) , COLOR)
def draw_gridline( self , startp, endp) :
''' Parameters: startp - a Point of where to start the gridline
endp - a Point of where to end the gridline
Draws two straight 1 pixel lines next to each other, to create
a nice looking grid on the canvas.
'''
line = Line( Point( startp.x *BLOCK_SIZE, startp.y *BLOCK_SIZE) , \
Point( endp.x *BLOCK_SIZE, endp.y *BLOCK_SIZE) )
line.draw ( self .canvas )
line = Line( Point( startp.x *BLOCK_SIZE-1 , startp.y *BLOCK_SIZE-1 ) , \
Point( endp.x *BLOCK_SIZE-1 , endp.y *BLOCK_SIZE-1 ) )
line.draw ( self .canvas )
def random_seed( self , percentage) :
''' Parameters: percentage - a number between 0 and 1 representing the
percentage of the board to be filled with
blocks
This method activates the specified percentage of blocks randomly.
'''
for block in self .block_list .values ( ) :
if random .random ( ) < percentage:
block.set_live ( self .canvas )
def seed( self , block_coords, offset_x_y = ( 0 , 0 ) ) :
'''
Seeds the board with a certain configuration.
Takes in a list of (x, y) tuples representing block coordinates,
and activates the blocks corresponding to those coordinates.
'''
#### YOUR CODE HERE #####
# raise Exception("seed not implemented")
for coord in block_coords:
coord = ( coord[ 0 ] + offset_x_y[ 0 ] , coord[ 1 ] + offset_x_y[ 1 ] )
self .block_list [ coord] .set_live ( self .canvas )
def get_block_neighbors( self , block) :
'''
Given a Block object, returns a list of neighboring blocks.
Should not return itself in the list.
'''
#### YOUR CODE HERE #####
#### Think about edge conditions!
# raise Exception("get_block_neighbors not implemented")
x = block.x
y = block.y
neighborCoordList = [ ]
for col in range ( x - 1 , x + 2 ) :
for row in range ( y - 1 , y + 2 ) :
if 0 <= col < BOARD_WIDTH and 0 <= row < BOARD_HEIGHT:
neighborCoordList.append ( ( col, row) )
neighborCoordList.remove ( ( x, y) )
neighborList = [ ]
for coords in neighborCoordList:
neighborList.append ( self .block_list [ coords] )
return neighborList
def simulate( self ) :
'''
Executes one turn of Conways Game of Life using the rules
listed in the handout. Best approached in a two-step strategy:
1. Calculate the new_status of each block by looking at the
status of its neighbors.
2. Set blocks to 'live' if their new_status is 'live' and their
status is 'dead'. Similarly, set blocks to 'dead' if their
new_status is 'dead' and their status is 'live'. Then, remember
to call reset_status(self.canvas) on each block.
'''
#### YOUR CODE HERE #####
for col in range ( BOARD_WIDTH) :
for row in range ( BOARD_HEIGHT) :
block = self .block_list [ col, row]
n = self .count_live_neighbors ( block)
if block.is_live ( ) :
if 2 <= n <= 3 :
block.new_status = "live"
else :
block.new_status = "dead"
else :
if n == 3 :
block.new_status = "live"
else :
block.new_status = "dead"
for col in range ( BOARD_WIDTH) :
for row in range ( BOARD_HEIGHT) :
block = self .block_list [ col, row]
block.reset_status ( self .canvas )
# raise Exception("simulate not implemented")
# My helper methods below
def count_live_neighbors( self , block) :
count = 0
neighbors = self .get_block_neighbors ( block)
for n in neighbors:
if n.is_live ( ) :
count += 1
return count
# My helper methods above
def animate( self ) :
'''
Animates the Game of Life, calling "simulate"
once every second
'''
self .simulate ( )
self .win .after ( self .delay , self .animate )
################################################################
# RUNNING THE SIMULATION
################################################################
if __name__ == '__main__' :
# Initalize board
win = Window( "Conway's Game of Life" )
board = Board( win, BOARD_WIDTH, BOARD_HEIGHT)
## PART 1: Make sure that the board __init__ method works
# board.random_seed(.15)
## PART 2: Make sure board.seed works. Comment random_seed above and uncomment
## one of the seed methods below
# board.seed(toad_blocklist)
## PART 3: Test that neighbors work by commenting the above and uncommenting
## the following two lines:
# board.seed(neighbor_test_blocklist)
# test_neighbors(board)
## PART 4: Test that simulate() works by uncommenting the next two lines:
# board.seed(blocklist_list[random.randint(0, len(blocklist_list) - 1)])
board.seed ( gosper_glider_gun_blocklist)
# win.after(2000, board.simulate)
## PART 5: Try animating! Comment out win.after(2000, board.simulate) above, and
## uncomment win.after below.
win.after ( 2000 , board.animate )
## Yay, you're done! Try seeding with different blocklists (a few are provided at the top of this file!)
win.mainloop ( )
IyBsaWZlLnB5CiMgR2xlbm4gQS4gUmljaGFyZAojIE1lY2hhbmljYWwgTU9PQyBNSVQgT0NXIDYuMTg5CiMgUHJvamVjdCAyIENvbndheSdzIEdhbWUgb2YgTGlmZQojIEN1cnJlbnRseSBzZXQgdG8gY3JlYXRlIGEgR29zcGVyIEdsaWRlciBHdW4KIyBVc2UgUHl0aG9uIDIuNwojIEp1bHkgMjQsIDIwMTMKIyBNb2RpZmllZCBKdWx5IDMwLCAyMDEzCiMjIENvZGUgdGVtcGxhdGUgd3JpdHRlbiBieSBTYXJpbmEgQ2FuZWxha2UgJiBLZWxseSBDYXN0ZWVsLCBBdWd1c3QgMjAxMAojIyBSZXZpc2VkIEphbnVhcnkgMjAxMQpmcm9tIGdyYXBoaWNzIGltcG9ydCAqCmltcG9ydCByYW5kb20KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEdMT0JBTCBWQVJJQUJMRVMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgICAKQkxPQ0tfU0laRSA9IDE2CkJMT0NLX09VVExJTkVfV0lEVEggPSAyCkJPQVJEX1dJRFRIID0gNTAKQk9BUkRfSEVJR0hUID0gNTAKQ09MT1IgPSAicmVkIiAjIEkgYWRkZWQgdGhpcy4KREVMQVkgPSAyMDAKCm5laWdoYm9yX3Rlc3RfYmxvY2tsaXN0ID0gWygwLDApLCAoMSwxKV0KYmxpbmtlcl9ibG9ja2xpc3QgPSBbKDIsIDIpLCAoMiwgMyksICgyLCA0KV0KdG9hZF9ibG9ja2xpc3QgPSBbKDQsNCksICgzLDUpLCAoMyw2KSwgKDUsNyksICg2LDUpLCAoNiw2KV0KYmVhY29uX2Jsb2NrbGlzdCA9IFsoMiwzKSwgKDIsNCksICgzLDMpLCAoMyw0KSwgKDQsNSksICg0LDYpLCAoNSw1KSwgKDUsNildCmdsaWRlcl9ibG9ja2xpc3QgPSBbKDEsMiksICgyLDMpLCAoMywxKSwgKDMsMiksICgzLDMpXQpwdWxzYXJfYmxvY2tsaXN0ID0gWygyLDQpLCAoMiw1KSwgKDIsNiksICg0LDIpLCAoNCw3KSwgKDUsMiksICg1LDcpLAogICAgICAgICAgICAgICAgICAgICg2LDIpLCAoNiw3KSwgKDcsNCksICg3LDUpLCAoNyw2KV0KZ29zcGVyX2dsaWRlcl9ndW5fYmxvY2tsaXN0ID0gWygxLDUpLCAoMSw2KSwgKDIsNSksICgyLDYpLCAoMTEsNSksICgxMSw2KSwgKDExLDcpLCAoMTIsNCksICgxMiw4KSwgKDEzLDMpLCAoMTMsOSksICgxNCwzKSwgKDE0LDkpLCAoMTUsNiksICgxNiw0KSwgKDE2LDgpLCAoMTcsNSksICgxNyw2KSwgKDE3LDcpLCAoMTgsNiksICgyMSwzKSwgKDIxLDQpLCAoMjEsNSksICgyMiwzKSwgKDIyLDQpLCAoMjIsNSksICgyMywyKSwgKDIzLDYpLCAoMjUsMSksICgyNSwyKSwgKDI1LDYpLCAoMjUsNyksICgzNSwzKSwgKDM1LDQpLCAoMzYsMyksICgzNiw0KV0KIyBmb3IgZGllaGFyZCwgbWFrZSBib2FyZCBhdCBsZWFzdCAyNXgyNSwgbWlnaHQgbmVlZCB0byBjaGFuZ2UgYmxvY2sgc2l6ZQpkaWVoYXJkX2Jsb2NrbGlzdCA9IFsoNSw3KSwgKDYsNyksICg2LDgpLCAoMTAsOCksICgxMSw4KSwgKDEyLDgpLCAoMTEsNildCiMgbGlzdCBvZiBibG9ja2xpc3RzCmJsb2NrbGlzdF9saXN0ID0gW2dsaWRlcl9ibG9ja2xpc3QsIGdvc3Blcl9nbGlkZXJfZ3VuX2Jsb2NrbGlzdF0KCiMgYmxvY2tsaXN0X2xpc3QgPSBbdG9hZF9ibG9ja2xpc3QsIGJlYWNvbl9ibG9ja2xpc3QsIGdsaWRlcl9ibG9ja2xpc3QsIHB1bHNhcl9ibG9ja2xpc3QsIGRpZWhhcmRfYmxvY2tsaXN0LCBnb3NwZXJfZ2xpZGVyX2d1bl9ibG9ja2xpc3RdCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFRFU1QgQ09ERSAoZG9uJ3Qgd29ycnkgYWJvdXQgdW5kZXJzdGFuZGluZyB0aGlzIHNlY3Rpb24pCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKZGVmIHRlc3RfbmVpZ2hib3JzKGJvYXJkKToKICAgICcnJwogICAgQ29kZSB0byB0ZXN0IHRoZSBib2FyZC5nZXRfYmxvY2tfbmVpZ2hib3IgZnVuY3Rpb24KICAgICcnJwogICAgZm9yIGJsb2NrIGluIGJvYXJkLmJsb2NrX2xpc3QudmFsdWVzKCk6CiAgICAgICAgbmVpZ2hib3JzID0gYm9hcmQuZ2V0X2Jsb2NrX25laWdoYm9ycyhibG9jaykKICAgICAgICBuY29vcmRzID0gW25laWdoYm9yLmdldF9jb29yZHMoKSBmb3IgbmVpZ2hib3IgaW4gbmVpZ2hib3JzXQogICAgICAgIGlmIGJsb2NrLmdldF9jb29yZHMoKSA9PSAoMCwwKToKICAgICAgICAgICAgemVyb25laWdocyA9IFsoMCwxKSwgKDEsMSksICgxLDApXQogICAgICAgICAgICBmb3IgbiBpbiBuY29vcmRzOgogICAgICAgICAgICAgICAgaWYgbiBub3QgaW4gemVyb25laWdoczoKICAgICAgICAgICAgICAgICAgICBwcmludCAiVGVzdGluZyBibG9jayBhdCAoMCwwKSIKICAgICAgICAgICAgICAgICAgICBwcmludCAiR290IiwgbmNvb3JkcwogICAgICAgICAgICAgICAgICAgIHByaW50ICJFeHBlY3RlZCIsIHplcm9uZWlnaHMKICAgICAgICAgICAgICAgICAgICByZXR1cm4gRmFsc2UKCiAgICAgICAgICAgIGZvciBuZWlnaGJvciBpbiBuZWlnaGJvcnM6CiAgICAgICAgICAgICAgICBpZiBuZWlnaGJvci5nZXRfY29vcmRzKCkgPT0gKDEsIDEpOgogICAgICAgICAgICAgICAgICAgIGlmIG5laWdoYm9yLmlzX2xpdmUoKSA9PSBGYWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgcHJpbnQgIlRlc3RpbmcgYmxvY2sgYXQgKDAsIDApLi4uIgogICAgICAgICAgICAgICAgICAgICAgICBwcmludCAiTXkgbmVpZ2hib3IgYXQgKDEsIDEpIHNob3VsZCBiZSBsaXZlOyBpdCBpcyBub3QuIgogICAgICAgICAgICAgICAgICAgICAgICBwcmludCAiRGlkIHlvdSByZXR1cm4gbXkgYWN0dWFsIG5laWdoYm9ycywgb3IgY3JlYXRlIG5ldyBjb3BpZXMgb2YgdGhlbT8iCiAgICAgICAgICAgICAgICAgICAgICAgIHByaW50ICJGQUlMOiBnZXRfYmxvY2tfbmVpZ2hib3JzKCkgc2hvdWxkIE5PVCByZXR1cm4gbmV3IEJsb2NrcyEiCiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBGYWxzZQoKICAgICAgICBlbGlmIGJsb2NrLmdldF9jb29yZHMoKSA9PSAoMSwxKToKICAgICAgICAgICAgb25lbmVpZ2hzID0gWygwLDApLCAoMCwxKSwgKDAsMiksICgxLDApLCAoMSwyKSwgKDIsMCksICgyLDEpLCgyLDIpXQogICAgICAgICAgICBmb3IgbiBpbiBuY29vcmRzOgogICAgICAgICAgICAgICAgaWYgbiBub3QgaW4gb25lbmVpZ2hzOgogICAgICAgICAgICAgICAgICAgIHByaW50ICJUZXN0aW5nIGJsb2NrIGF0ICgxLDEpIgogICAgICAgICAgICAgICAgICAgIHByaW50ICJHb3QiLCBuY29vcmRzCiAgICAgICAgICAgICAgICAgICAgcHJpbnQgIkV4cGVjdGVkIiwgb25lbmVpZ2hzCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIEZhbHNlCiAgICAgICAgICAgIGZvciBuIGluIG9uZW5laWdoczoKICAgICAgICAgICAgICAgIGlmIG4gbm90IGluIG5jb29yZHM6CiAgICAgICAgICAgICAgICAgICAgcHJpbnQgIlRlc3RpbmcgYmxvY2sgYXQgKDEsMSkiCiAgICAgICAgICAgICAgICAgICAgcHJpbnQgIkdvdCIsIG5jb29yZHMKICAgICAgICAgICAgICAgICAgICBwcmludCAiRXhwZWN0ZWQiLCBvbmVuZWlnaHMKICAgICAgICAgICAgICAgICAgICByZXR1cm4gRmFsc2UKICAgIHByaW50ICJQYXNzZWQgbmVpZ2hib3IgdGVzdCIKICAgIHJldHVybiBUcnVlCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQkxPQ0sgQ0xBU1MgKFJlYWQgdGhyb3VnaCBhbmQgdW5kZXJzdGFuZCB0aGlzIHBhcnQhKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmNsYXNzIEJsb2NrKFJlY3RhbmdsZSk6CiAgICAnJycgQmxvY2sgY2xhc3M6CiAgICAgICAgSW1wbGVtZW50IGEgYmxvY2sgZm9yIGEgdGV0cmlzIHBpZWNlCiAgICAgICAgQXR0cmlidXRlczogeCAtIHR5cGU6IGludAogICAgICAgICAgICAgICAgICAgIHkgLSB0eXBlOiBpbnQKICAgICAgICBzcGVjaWZ5IHRoZSBwb3NpdGlvbiBvbiB0aGUgYm9hcmQKICAgICAgICBpbiB0ZXJtcyBvZiB0aGUgc3F1YXJlIGdyaWQKICAgICcnJwoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBwb3MsIGNvbG9yKToKICAgICAgICAnJycKICAgICAgICBwb3M6IGEgUG9pbnQgb2JqZWN0IHNwZWNpZmluZyB0aGUgKHgsIHkpIHNxdWFyZSBvZiB0aGUgQmxvY2sgKE5PVCBpbiBwaXhlbHMhKQogICAgICAgIGNvbG9yOiBhIHN0cmluZyBzcGVjaWZpbmcgdGhlIGNvbG9yIG9mIHRoZSBibG9jayAoZWcgJ2JsdWUnIG9yICdwdXJwbGUnKQogICAgICAgICcnJwogICAgICAgIHNlbGYueCA9IHBvcy54CiAgICAgICAgc2VsZi55ID0gcG9zLnkKICAgICAgICAKICAgICAgICBwMSA9IFBvaW50KHBvcy54KkJMT0NLX1NJWkUsCiAgICAgICAgICAgICAgICAgICBwb3MueSpCTE9DS19TSVpFKQogICAgICAgIHAyID0gUG9pbnQocDEueCArIEJMT0NLX1NJWkUsIHAxLnkgKyBCTE9DS19TSVpFKQoKICAgICAgICBSZWN0YW5nbGUuX19pbml0X18oc2VsZiwgcDEsIHAyKQogICAgICAgIHNlbGYuc2V0V2lkdGgoQkxPQ0tfT1VUTElORV9XSURUSCkKICAgICAgICBzZWxmLnNldEZpbGwoY29sb3IpCiAgICAgICAgc2VsZi5zdGF0dXMgPSAnZGVhZCcKICAgICAgICBzZWxmLm5ld19zdGF0dXMgPSAnTm9uZScKICAgICAgICAKICAgIGRlZiBnZXRfY29vcmRzKHNlbGYpOgogICAgICAgIHJldHVybiAoc2VsZi54LCBzZWxmLnkpCgogICAgZGVmIHNldF9saXZlKHNlbGYsIGNhbnZhcyk6CiAgICAgICAgJycnCiAgICAgICAgU2V0cyB0aGUgYmxvY2sgc3RhdHVzIHRvICdsaXZlJyBhbmQgZHJhd3MgaXQgb24gdGhlIGdyaWQuCiAgICAgICAgQmUgc3VyZSB0byBkbyB0aGlzIG9uIHRoZSBjYW52YXMhCiAgICAgICAgJycnCiAgICAgICAgaWYgc2VsZi5zdGF0dXM9PSdkZWFkJzoKICAgICAgICAgIHNlbGYuc3RhdHVzID0gJ2xpdmUnCiAgICAgICAgICBzZWxmLmRyYXcoY2FudmFzKQoKICAgIGRlZiBzZXRfZGVhZChzZWxmKToKICAgICAgICAnJycKICAgICAgICBTZXRzIHRoZSBibG9jayBzdGF0dXMgdG8gJ2RlYWQnIGFuZCB1bmRyYXdzIGl0IGZyb20gdGhlIGdyaWQuCiAgICAgICAgJycnCiAgICAgICAgaWYgc2VsZi5zdGF0dXM9PSdsaXZlJzoKICAgICAgICAgIHNlbGYuc3RhdHVzID0gJ2RlYWQnCiAgICAgICAgICBzZWxmLnVuZHJhdygpCgogICAgZGVmIGlzX2xpdmUoc2VsZik6CiAgICAgICAgJycnCiAgICAgICAgUmV0dXJucyBUcnVlIGlmIHRoZSBibG9jayBpcyBjdXJyZW50bHkgJ2xpdmUnLiBSZXR1cm5zIEZhbHNlIG90aGVyd2lzZS4KICAgICAgICAnJycKICAgICAgICBpZiBzZWxmLnN0YXR1cyA9PSAnbGl2ZSc6CiAgICAgICAgICAgIHJldHVybiBUcnVlCiAgICAgICAgcmV0dXJuIEZhbHNlCgogICAgZGVmIHJlc2V0X3N0YXR1cyhzZWxmLCBjYW52YXMpOgogICAgICAgICcnJwogICAgICAgIFNldHMgdGhlIG5ld19zdGF0dXMgdG8gYmUgdGhlIGN1cnJlbnQgc3RhdHVzCiAgICAgICAgJycnCiAgICAgICAgaWYgc2VsZi5uZXdfc3RhdHVzPT0nZGVhZCc6CiAgICAgICAgICAgIHNlbGYuc2V0X2RlYWQoKQogICAgICAgIGVsaWYgc2VsZi5uZXdfc3RhdHVzPT0nbGl2ZSc6CiAgICAgICAgICAgIHNlbGYuc2V0X2xpdmUoY2FudmFzKQogICAgICAgIAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBCT0FSRCBDTEFTUyAoUmVhZCB0aHJvdWdoIGFuZCB1bmRlcnN0YW5kIHRoaXMgcGFydCEpCiMgUHJpbnQgb3V0IGFuZCB0dXJuIGluIHRoaXMgc2VjdGlvbi4KIyBOYW1lOgojIFJlY2l0YXRpb246CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpjbGFzcyBCb2FyZChvYmplY3QpOgogICAgJycnIEJvYXJkIGNsYXNzOiBpdCByZXByZXNlbnRzIHRoZSBHYW1lIG9mIExpZmUgYm9hcmQKCiAgICAgICAgQXR0cmlidXRlczogd2lkdGggLSB0eXBlOmludCAtIHdpZHRoIG9mIHRoZSBib2FyZCBpbiBzcXVhcmVzCiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0IC0gdHlwZTppbnQgLSBoZWlnaHQgb2YgdGhlIGJvYXJkIGluIHNxdWFyZXMKICAgICAgICAgICAgICAgICAgICBjYW52YXMgLSB0eXBlOkNhbnZhc0ZyYW1lIC0gd2hlcmUgdGhlIGJsb2NrcyB3aWxsIGJlIGRyYXduCiAgICAgICAgICAgICAgICAgICAgYmxvY2tfbGlzdCAtIHR5cGU6RGljdGlvbmFyeSAtIHN0b3JlcyB0aGUgYmxvY2tzIGZvciBhIGdpdmVuIHBvc2l0aW9uCiAgICAnJycKICAgIAogICAgZGVmIF9faW5pdF9fKHNlbGYsIHdpbiwgd2lkdGgsIGhlaWdodCk6CiAgICAgICAgc2VsZi53aWR0aCA9IHdpZHRoCiAgICAgICAgc2VsZi5oZWlnaHQgPSBoZWlnaHQKICAgICAgICBzZWxmLndpbiA9IHdpbgogICAgICAgICMgc2VsZi5kZWxheSBpcyB0aGUgbnVtYmVyIG9mIG1zIGJldHdlZW4gZWFjaCBzaW11bGF0aW9uLiBDaGFuZ2UgdG8gYmUKICAgICAgICAjIHNob3J0ZXIgb3IgbG9uZ2VyIGlmIHlvdSB3aXNoIQogICAgICAgIHNlbGYuZGVsYXkgPSBERUxBWQoKICAgICAgICAjIGNyZWF0ZSBhIGNhbnZhcyB0byBkcmF3IHRoZSBibG9ja3Mgb24KICAgICAgICBzZWxmLmNhbnZhcyA9IENhbnZhc0ZyYW1lKHdpbiwgc2VsZi53aWR0aCAqIEJMT0NLX1NJWkUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuaGVpZ2h0ICogQkxPQ0tfU0laRSkKICAgICAgICBzZWxmLmNhbnZhcy5zZXRCYWNrZ3JvdW5kKCd3aGl0ZScpCgogICAgICAgICMgaW5pdGlhbGl6ZSBncmlkIGxpbmVzCiAgICAgICAgZm9yIHggaW4gcmFuZ2UoMSxzZWxmLndpZHRoKToKICAgICAgICAgICAgc2VsZi5kcmF3X2dyaWRsaW5lKFBvaW50KHgsIDApLCBQb2ludCh4LCBzZWxmLmhlaWdodCkpCgogICAgICAgIGZvciB5IGluIHJhbmdlKDEsc2VsZi5oZWlnaHQpOgogICAgICAgICAgICBzZWxmLmRyYXdfZ3JpZGxpbmUoUG9pbnQoMCwgeSksIFBvaW50KHNlbGYud2lkdGgsIHkpKQoKICAgICAgICAjIEZvciBlYWNoIHNxdWFyZSBvbiB0aGUgYm9hcmQsIHdlIG5lZWQgdG8gaW5pdGlhbGl6ZQogICAgICAgICMgYSBibG9jayBhbmQgc3RvcmUgdGhhdCBibG9jayBpbiBhIGRhdGEgc3RydWN0dXJlLiBBCiAgICAgICAgIyBkaWN0aW9uYXJ5IChzZWxmLmJsb2NrX2xpc3QpIHRoYXQgaGFzIGtleTp2YWx1ZSBwYWlycyBvZgogICAgICAgICMgKHgseSk6QmxvY2sgd2lsbCBiZSB1c2VmdWwgaGVyZS4KICAgICAgICBzZWxmLmJsb2NrX2xpc3QgPSB7fQoKICAgICAgICAjIyMjIyMjIFlPVVIgQ09ERSBIRVJFICMjIyMjIwojIyAgICAgICAgQkxPQ0tfU0laRSA9IDQwCiMjICAgICAgICBCTE9DS19PVVRMSU5FX1dJRFRIID0gMgojIyAgICAgICAgQk9BUkRfV0lEVEggPSAxMgojIyAgICAgICAgQk9BUkRfSEVJR0hUID0gMTIKIyMgICAgICAgICMgcmFpc2UgRXhjZXB0aW9uKCJfX2luaXRfXyBub3QgaW1wbGVtZW50ZWQiKQogICAgICAgIGZvciB4IGluIHJhbmdlKEJPQVJEX1dJRFRIKToKICAgICAgICAgICAgZm9yIHkgaW4gcmFuZ2UoQk9BUkRfSEVJR0hUKToKICAgICAgICAgICAgICAgIHNlbGYuYmxvY2tfbGlzdFt4LCB5XSA9IEJsb2NrKFBvaW50KHgsIHkpLCBDT0xPUikKCgogICAgZGVmIGRyYXdfZ3JpZGxpbmUoc2VsZiwgc3RhcnRwLCBlbmRwKToKICAgICAgICAnJycgUGFyYW1ldGVyczogc3RhcnRwIC0gYSBQb2ludCBvZiB3aGVyZSB0byBzdGFydCB0aGUgZ3JpZGxpbmUKICAgICAgICAgICAgICAgICAgICAgICAgZW5kcCAtIGEgUG9pbnQgb2Ygd2hlcmUgdG8gZW5kIHRoZSBncmlkbGluZQogICAgICAgICAgICBEcmF3cyB0d28gc3RyYWlnaHQgMSBwaXhlbCBsaW5lcyBuZXh0IHRvIGVhY2ggb3RoZXIsIHRvIGNyZWF0ZQogICAgICAgICAgICBhIG5pY2UgbG9va2luZyBncmlkIG9uIHRoZSBjYW52YXMuCiAgICAgICAgJycnCiAgICAgICAgbGluZSA9IExpbmUoUG9pbnQoc3RhcnRwLngqQkxPQ0tfU0laRSwgc3RhcnRwLnkqQkxPQ0tfU0laRSksIFwKICAgICAgICAgICAgICAgICAgICBQb2ludChlbmRwLngqQkxPQ0tfU0laRSwgZW5kcC55KkJMT0NLX1NJWkUpKQogICAgICAgIGxpbmUuZHJhdyhzZWxmLmNhbnZhcykKICAgICAgICAKICAgICAgICBsaW5lID0gTGluZShQb2ludChzdGFydHAueCpCTE9DS19TSVpFLTEsIHN0YXJ0cC55KkJMT0NLX1NJWkUtMSksIFwKICAgICAgICAgICAgICAgICAgICBQb2ludChlbmRwLngqQkxPQ0tfU0laRS0xLCBlbmRwLnkqQkxPQ0tfU0laRS0xKSkKICAgICAgICBsaW5lLmRyYXcoc2VsZi5jYW52YXMpCgoKICAgIGRlZiByYW5kb21fc2VlZChzZWxmLCBwZXJjZW50YWdlKToKICAgICAgICAnJycgUGFyYW1ldGVyczogcGVyY2VudGFnZSAtIGEgbnVtYmVyIGJldHdlZW4gMCBhbmQgMSByZXByZXNlbnRpbmcgdGhlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjZW50YWdlIG9mIHRoZSBib2FyZCB0byBiZSBmaWxsZWQgd2l0aAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzCiAgICAgICAgICAgIFRoaXMgbWV0aG9kIGFjdGl2YXRlcyB0aGUgc3BlY2lmaWVkIHBlcmNlbnRhZ2Ugb2YgYmxvY2tzIHJhbmRvbWx5LgogICAgICAgICcnJwogICAgICAgIGZvciBibG9jayBpbiBzZWxmLmJsb2NrX2xpc3QudmFsdWVzKCk6CiAgICAgICAgICAgIGlmIHJhbmRvbS5yYW5kb20oKSA8IHBlcmNlbnRhZ2U6CiAgICAgICAgICAgICAgICBibG9jay5zZXRfbGl2ZShzZWxmLmNhbnZhcykKCiAgICBkZWYgc2VlZChzZWxmLCBibG9ja19jb29yZHMsIG9mZnNldF94X3kgPSAoMCwgMCkpOgogICAgICAgICcnJwogICAgICAgIFNlZWRzIHRoZSBib2FyZCB3aXRoIGEgY2VydGFpbiBjb25maWd1cmF0aW9uLgogICAgICAgIFRha2VzIGluIGEgbGlzdCBvZiAoeCwgeSkgdHVwbGVzIHJlcHJlc2VudGluZyBibG9jayBjb29yZGluYXRlcywKICAgICAgICBhbmQgYWN0aXZhdGVzIHRoZSBibG9ja3MgY29ycmVzcG9uZGluZyB0byB0aG9zZSBjb29yZGluYXRlcy4KICAgICAgICAnJycKCiAgICAgICAgIyMjIyBZT1VSIENPREUgSEVSRSAjIyMjIwogICAgICAgICMgcmFpc2UgRXhjZXB0aW9uKCJzZWVkIG5vdCBpbXBsZW1lbnRlZCIpCiAgICAgICAgZm9yIGNvb3JkIGluIGJsb2NrX2Nvb3JkczoKICAgICAgICAgICAgY29vcmQgPSAoY29vcmRbMF0gKyBvZmZzZXRfeF95WzBdLCBjb29yZFsxXSArIG9mZnNldF94X3lbMV0pCiAgICAgICAgICAgIHNlbGYuYmxvY2tfbGlzdFtjb29yZF0uc2V0X2xpdmUoc2VsZi5jYW52YXMpCgoKICAgIGRlZiBnZXRfYmxvY2tfbmVpZ2hib3JzKHNlbGYsIGJsb2NrKToKICAgICAgICAnJycKICAgICAgICBHaXZlbiBhIEJsb2NrIG9iamVjdCwgcmV0dXJucyBhIGxpc3Qgb2YgbmVpZ2hib3JpbmcgYmxvY2tzLgogICAgICAgIFNob3VsZCBub3QgcmV0dXJuIGl0c2VsZiBpbiB0aGUgbGlzdC4KICAgICAgICAnJycKICAgICAgICAjIyMjIFlPVVIgQ09ERSBIRVJFICMjIyMjCiAgICAgICAgIyMjIyBUaGluayBhYm91dCBlZGdlIGNvbmRpdGlvbnMhCiAgICAgICAgIyByYWlzZSBFeGNlcHRpb24oImdldF9ibG9ja19uZWlnaGJvcnMgbm90IGltcGxlbWVudGVkIikKICAgICAgICB4ID0gYmxvY2sueAogICAgICAgIHkgPSBibG9jay55CiAgICAgICAgbmVpZ2hib3JDb29yZExpc3QgPSBbXQogICAgICAgIGZvciBjb2wgaW4gcmFuZ2UoeCAtIDEsIHggKyAyKToKICAgICAgICAgICAgZm9yIHJvdyBpbiByYW5nZSh5IC0gMSwgeSArIDIpOgogICAgICAgICAgICAgICAgaWYgMCA8PSBjb2wgPCBCT0FSRF9XSURUSCBhbmQgMCA8PSByb3cgPCBCT0FSRF9IRUlHSFQ6CiAgICAgICAgICAgICAgICAgICAgbmVpZ2hib3JDb29yZExpc3QuYXBwZW5kKChjb2wsIHJvdykpCiAgICAgICAgbmVpZ2hib3JDb29yZExpc3QucmVtb3ZlKCh4LCB5KSkKICAgICAgICBuZWlnaGJvckxpc3QgPSBbXQogICAgICAgIGZvciBjb29yZHMgaW4gbmVpZ2hib3JDb29yZExpc3Q6CiAgICAgICAgICAgIG5laWdoYm9yTGlzdC5hcHBlbmQoc2VsZi5ibG9ja19saXN0W2Nvb3Jkc10pCiAgICAgICAgcmV0dXJuIG5laWdoYm9yTGlzdAoKICAgIGRlZiBzaW11bGF0ZShzZWxmKToKICAgICAgICAnJycKICAgICAgICBFeGVjdXRlcyBvbmUgdHVybiBvZiBDb253YXlzIEdhbWUgb2YgTGlmZSB1c2luZyB0aGUgcnVsZXMKICAgICAgICBsaXN0ZWQgaW4gdGhlIGhhbmRvdXQuIEJlc3QgYXBwcm9hY2hlZCBpbiBhIHR3by1zdGVwIHN0cmF0ZWd5OgogICAgICAgIAogICAgICAgIDEuIENhbGN1bGF0ZSB0aGUgbmV3X3N0YXR1cyBvZiBlYWNoIGJsb2NrIGJ5IGxvb2tpbmcgYXQgdGhlCiAgICAgICAgICAgc3RhdHVzIG9mIGl0cyBuZWlnaGJvcnMuCgogICAgICAgIDIuIFNldCBibG9ja3MgdG8gJ2xpdmUnIGlmIHRoZWlyIG5ld19zdGF0dXMgaXMgJ2xpdmUnIGFuZCB0aGVpcgogICAgICAgICAgIHN0YXR1cyBpcyAnZGVhZCcuIFNpbWlsYXJseSwgc2V0IGJsb2NrcyB0byAnZGVhZCcgaWYgdGhlaXIKICAgICAgICAgICBuZXdfc3RhdHVzIGlzICdkZWFkJyBhbmQgdGhlaXIgc3RhdHVzIGlzICdsaXZlJy4gVGhlbiwgcmVtZW1iZXIKICAgICAgICAgICB0byBjYWxsIHJlc2V0X3N0YXR1cyhzZWxmLmNhbnZhcykgb24gZWFjaCBibG9jay4KICAgICAgICAnJycKCiAgICAgICAgIyMjIyBZT1VSIENPREUgSEVSRSAjIyMjIwogICAgICAgIGZvciBjb2wgaW4gcmFuZ2UoQk9BUkRfV0lEVEgpOgogICAgICAgICAgICBmb3Igcm93IGluIHJhbmdlKEJPQVJEX0hFSUdIVCk6CiAgICAgICAgICAgICAgICBibG9jayA9IHNlbGYuYmxvY2tfbGlzdFtjb2wsIHJvd10KICAgICAgICAgICAgICAgIG4gPSBzZWxmLmNvdW50X2xpdmVfbmVpZ2hib3JzKGJsb2NrKQogICAgICAgICAgICAgICAgaWYgYmxvY2suaXNfbGl2ZSgpOgogICAgICAgICAgICAgICAgICAgIGlmIDIgPD0gbiA8PSAzOgogICAgICAgICAgICAgICAgICAgICAgICBibG9jay5uZXdfc3RhdHVzID0gImxpdmUiCiAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2submV3X3N0YXR1cyA9ICJkZWFkIgogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBpZiBuID09IDM6CiAgICAgICAgICAgICAgICAgICAgICAgIGJsb2NrLm5ld19zdGF0dXMgPSAibGl2ZSIKICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICBibG9jay5uZXdfc3RhdHVzID0gImRlYWQiCiAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgZm9yIGNvbCBpbiByYW5nZShCT0FSRF9XSURUSCk6CiAgICAgICAgICAgIGZvciByb3cgaW4gcmFuZ2UoQk9BUkRfSEVJR0hUKToKICAgICAgICAgICAgICAgIGJsb2NrID0gc2VsZi5ibG9ja19saXN0W2NvbCwgcm93XQogICAgICAgICAgICAgICAgYmxvY2sucmVzZXRfc3RhdHVzKHNlbGYuY2FudmFzKQogICAgICAgICMgcmFpc2UgRXhjZXB0aW9uKCJzaW11bGF0ZSBub3QgaW1wbGVtZW50ZWQiKQogICAgIyBNeSBoZWxwZXIgbWV0aG9kcyBiZWxvdwogICAgZGVmIGNvdW50X2xpdmVfbmVpZ2hib3JzKHNlbGYsIGJsb2NrKToKICAgICAgICBjb3VudCA9IDAKICAgICAgICBuZWlnaGJvcnMgPSBzZWxmLmdldF9ibG9ja19uZWlnaGJvcnMoYmxvY2spCiAgICAgICAgZm9yIG4gaW4gbmVpZ2hib3JzOgogICAgICAgICAgICBpZiBuLmlzX2xpdmUoKToKICAgICAgICAgICAgICAgIGNvdW50ICs9IDEKICAgICAgICByZXR1cm4gY291bnQKICAgICMgTXkgaGVscGVyIG1ldGhvZHMgYWJvdmUKCiAgICAgICAgCgogICAgZGVmIGFuaW1hdGUoc2VsZik6CiAgICAgICAgJycnCiAgICAgICAgQW5pbWF0ZXMgdGhlIEdhbWUgb2YgTGlmZSwgY2FsbGluZyAic2ltdWxhdGUiCiAgICAgICAgb25jZSBldmVyeSBzZWNvbmQKICAgICAgICAnJycKICAgICAgICBzZWxmLnNpbXVsYXRlKCkKICAgICAgICBzZWxmLndpbi5hZnRlcihzZWxmLmRlbGF5LCBzZWxmLmFuaW1hdGUpCgoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBSVU5OSU5HIFRIRSBTSU1VTEFUSU9OCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6ICAgIAogICAgIyBJbml0YWxpemUgYm9hcmQKICAgIHdpbiA9IFdpbmRvdygiQ29ud2F5J3MgR2FtZSBvZiBMaWZlIikKICAgIGJvYXJkID0gQm9hcmQod2luLCBCT0FSRF9XSURUSCwgQk9BUkRfSEVJR0hUKQoKICAgICMjIFBBUlQgMTogTWFrZSBzdXJlIHRoYXQgdGhlIGJvYXJkIF9faW5pdF9fIG1ldGhvZCB3b3JrcyAgICAKICAgICMgYm9hcmQucmFuZG9tX3NlZWQoLjE1KQoKICAgICMjIFBBUlQgMjogTWFrZSBzdXJlIGJvYXJkLnNlZWQgd29ya3MuIENvbW1lbnQgcmFuZG9tX3NlZWQgYWJvdmUgYW5kIHVuY29tbWVudAogICAgIyMgIG9uZSBvZiB0aGUgc2VlZCBtZXRob2RzIGJlbG93CiAgICAjIGJvYXJkLnNlZWQodG9hZF9ibG9ja2xpc3QpCgogICAgIyMgUEFSVCAzOiBUZXN0IHRoYXQgbmVpZ2hib3JzIHdvcmsgYnkgY29tbWVudGluZyB0aGUgYWJvdmUgYW5kIHVuY29tbWVudGluZwogICAgIyMgdGhlIGZvbGxvd2luZyB0d28gbGluZXM6CiAgICAjIGJvYXJkLnNlZWQobmVpZ2hib3JfdGVzdF9ibG9ja2xpc3QpCiAgICAjIHRlc3RfbmVpZ2hib3JzKGJvYXJkKQoKCiAgICAjIyBQQVJUIDQ6IFRlc3QgdGhhdCBzaW11bGF0ZSgpIHdvcmtzIGJ5IHVuY29tbWVudGluZyB0aGUgbmV4dCB0d28gbGluZXM6CiAgICAKICAgICMgYm9hcmQuc2VlZChibG9ja2xpc3RfbGlzdFtyYW5kb20ucmFuZGludCgwLCBsZW4oYmxvY2tsaXN0X2xpc3QpIC0gMSldKQogICAgYm9hcmQuc2VlZChnb3NwZXJfZ2xpZGVyX2d1bl9ibG9ja2xpc3QpCiAgICAKICAgICMgd2luLmFmdGVyKDIwMDAsIGJvYXJkLnNpbXVsYXRlKQoKICAgICMjIFBBUlQgNTogVHJ5IGFuaW1hdGluZyEgQ29tbWVudCBvdXQgd2luLmFmdGVyKDIwMDAsIGJvYXJkLnNpbXVsYXRlKSBhYm92ZSwgYW5kCiAgICAjIyB1bmNvbW1lbnQgd2luLmFmdGVyIGJlbG93LgogICAgd2luLmFmdGVyKDIwMDAsIGJvYXJkLmFuaW1hdGUpCgogICAgIyMgWWF5LCB5b3UncmUgZG9uZSEgVHJ5IHNlZWRpbmcgd2l0aCBkaWZmZXJlbnQgYmxvY2tsaXN0cyAoYSBmZXcgYXJlIHByb3ZpZGVkIGF0IHRoZSB0b3Agb2YgdGhpcyBmaWxlISkKICAgIAogICAgd2luLm1haW5sb29wKCkKICAgICAgICAgICAgICAgIAo=