import pygame
def main():
# Setup the properties of the game / screen
pygame.init()
SCREEN_WIDTH ,SCREEN_HEIGHT = 1280, 720
FPS = 60
screen = pygame.display.set_mode( (SCREEN_WIDTH, SCREEN_HEIGHT) )
pygame.display.set_caption('Tic Tac Toe')
clock = pygame.time.Clock()
running = True
clicked = False
# 0 = no winner, 1 = player1 won, 2 = player2 won, 3 = tie
winner = 0
restart = False
BG_COLOR = (0, 0, 0)
b = board(SCREEN_WIDTH, SCREEN_HEIGHT)
# Counts whose turn it is
# Even turn = Player1
# Odd turn = Player2
turn = 0
# Fill an array with 9 area objects, for each
# area of the board
#
# area 0.0 | area 0.1 | area 0.2
# _______________________________
# area 1.0 | area 1.1 | area 1.2
# _______________________________
# area 2.0 | area 2.1 | area 2.2
areas = []
for i in range(3):
row = []
for j in range(3):
row.append(area(
(b.get_board_posx() + (j * ( (b.get_board_size() / 3) +
(b.get_thickness() /2 ) ) ) ),
(b.get_board_posy() + (i * ( (b.get_board_size() / 3) +
(b.get_thickness() / 2 ) ) ) ),
b.get_field_size()
))
areas.append(row)
###########
# GAME LOOP
###########
while running:
# Abort condition
# If (x) is clicked, terminate program
# If clicked is True, the user clicked in this
# iteration of the game loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
clicked = True
if (not (winner == 0)):
restart = True
# If any key or mousebutton is pressed
# while game is over, game restarts
elif event.type == pygame.KEYDOWN:
if (not winner == 0):
restart = True
if event.key == pygame.K_ESCAPE:
running = False
# Setup screen
screen.fill(BG_COLOR)
b.draw_board(screen)
mouse_posx, mouse_posy = pygame.mouse.get_pos()
# Looping through every area on the board
# Most of the game logic happens here
for i in range(len(areas)):
for j in range(len(areas[i])):
# Draw already set X tokens
if (areas[i][j].get_state() == 2):
draw_x_token(
areas[i][j].get_posx(),
areas[i][j].get_posy(),
b.get_field_size(),
screen,
b.get_color()
)
# Draw already set O tokens
if (areas[i][j].get_state() == 3):
draw_o_token(
areas[i][j].get_posx(),
areas[i][j].get_posy(),
b.get_field_size(),
screen,
b.get_color()
)
# Checking if mouse is hovering over any
# of the areas
# If so, preview token that is about to be drawn
if(areas[i][j].is_in(mouse_posx, mouse_posy)):
# print('Mouse is in Area({}, {})'.format(i, j))
# Handling clicks (Draw tokens)
if clicked:
if (not(areas[i][j].get_state() == 2) and
not(areas[i][j].get_state() ==3) and winner == 0 ):
if (turn % 2 == 0):
draw_x_token(
areas[i][j].get_posx(),
areas[i][j].get_posy(),
b.get_field_size(),
screen,
b.get_color()
)
areas[i][j].set_state(2)
turn += 1
else:
draw_o_token(
areas[i][j].get_posx(),
areas[i][j].get_posy(),
b.get_field_size(),
screen,
b.get_color()
)
areas[i][j].set_state(3)
turn += 1
if ( (areas[i][j].get_state() == 0
or areas[i][j].get_state() == 1) and winner == 0 ):
# even turn: Player1
if (turn % 2 == 0):
draw_x_token(
areas[i][j].get_posx(),
areas[i][j].get_posy(),
b.get_field_size(),
screen,
b.get_color()
)
areas[i][j].set_state(1)
else:
draw_o_token(
areas[i][j].get_posx(),
areas[i][j].get_posy(),
b.get_field_size(),
screen,
b.get_color()
)
areas[i][j].set_state(1)
# If mouse is not hovering over an area
# and area is not already used
# Set state to inactive
else:
if (
not(areas[i][j].get_state() == 2) and
not(areas[i][j].get_state() ==3)
):
areas[i][j].set_state(0)
# Check if there is a winner
# after this round
win_cons = [
# Horizontal
[ areas[0][0], areas[0][1], areas[0][2] ],
[ areas[1][0], areas[1][1], areas[1][2] ],
[ areas[2][0], areas[2][1], areas[2][2] ],
# Vertical
[ areas[0][0], areas[1][0], areas[2][0] ],
[ areas[0][1], areas[1][1], areas[2][1] ],
[ areas[0][2], areas[1][2], areas[2][2] ],
# Diagonal
[ areas[0][2], areas[1][1], areas[2][0] ],
[ areas[0][0], areas[1][1], areas[2][2] ]
]
for i in range( len(win_cons) ):
if (
win_cons[i][0].get_state() ==
win_cons[i][1].get_state() ==
win_cons[i][2].get_state() ==
2
):
winner = 1
if (
win_cons[i][0].get_state() ==
win_cons[i][1].get_state() ==
win_cons[i][2].get_state() ==
3
):
winner = 2
# Check for tie
fields_filled = 0
for i in range(len(areas)):
for j in range(len(areas[i])):
if (
areas[i][j].get_state() == 2 or
areas[i][j].get_state() == 3
):
fields_filled += 1
if (fields_filled == 9 and winner == 0):
winner = 3
if ( not (winner == 0) ):
if winner == 1:
draw_text(screen, 'Player X has Won. Press any key to restart',
b.get_board_posx(),
b.get_board_posy() - b.get_board_size() * 0.08,
int( round(b.get_board_size() * 0.055) ) )
elif winner == 2:
draw_text(screen, 'Player O has Won. Press any key to restart',
b.get_board_posx(),
b.get_board_posy() - b.get_board_size() * 0.08,
int( round(b.get_board_size() * 0.055) ))
elif winner == 3:
draw_text(screen, 'You both win! Press any key to restart',
b.get_board_posx(),
b.get_board_posy() - b.get_board_size() * 0.08,
int( round(b.get_board_size() * 0.055) ) )
# Reset all changing variables
# and set area states to inactive
if restart:
restart = False
winner = 0
turn = 0
for i in range(len(areas)):
for j in range(len(areas[i])):
areas[i][j].set_state(0)
# Pygame stuff
pygame.display.update()
clock.tick(FPS)
clicked = False
##################
# END OF GAME LOOP
##################
# closing the pygame application
pygame.quit()
def draw_text(screen, text, posx, posy, size):
my_font = pygame.font.SysFont('Arial', size)
textsurface = my_font.render(text, False, (255, 255, 255))
screen.blit(textsurface, (posx, posy))
def draw_x_token(posx, posy, size, screen, color):
offset = size * 0.1
thickness = size * 0.1
points0 = [
(posx + offset, posy + offset),
(posx + offset + thickness, posy + offset),
(posx + size - offset, posy + size - offset),
(posx + size - (offset + thickness), posy + size - offset)]
pygame.draw.polygon(screen, color, points0, 0 )
points1 = [
( posx + size - (offset + thickness), posy + offset),
( posx + size - offset, posy + offset),
( posx + offset + thickness, posy + size - offset),
( posx + offset, posy + size - offset)]
pygame.draw.polygon(screen, color, points1, 0 )
def draw_o_token(posx, posy, size, screen, color):
offset = size * 0.1
thickness = size * 0.1
radius = size / 2 - offset
pygame.draw.circle(
screen,
color,
( int( round(posx + (size / 2), 0) ),
int( round(posy + (size / 2), 0) ) ),
int( round(radius) ),
int( round (thickness) ) )
class board:
def __init__(self, SCREEN_WIDTH, SCREEN_HEIGHT):
self.SCREEN_WIDTH = SCREEN_WIDTH
self.SCREEN_HEIGHT = SCREEN_HEIGHT
self.BOARD_WIDTH = self.BOARD_HEIGHT = 550
self.COLOR = (255, 255, 255)
self.THICKNESS = self.BOARD_WIDTH * 0.025
# The point (posx, posy) is at the top left position
# of the board
# Setting up posx and posy in a way
# that the board is always drawn in the centre
# of the pygame application/screen
self.posx = (self.SCREEN_WIDTH / 2) - (self.BOARD_WIDTH / 2)
self.posy = (self.SCREEN_HEIGHT / 2) - (self.BOARD_HEIGHT / 2)
# Printing positions of the 4 board lines
# for debug purposes
#print( 'Left vertical line {}, {}'.format(
#self.posx + (self.BOARD_WIDTH / 3) - (self.THICKNESS / 2),
#self.posy
#) )
#print( 'Right vertical line {}, {}'.format(
#self.posx + ( ( (self.BOARD_WIDTH / 3) - (self.THICKNESS / 2) ) * 2),
#self.posy
#))
#print( 'Upper horizontal line {}, {}'.format(
#self.posx,
#self.posy + (self.BOARD_HEIGHT / 3) - (self.THICKNESS / 2)
#) )
#print( 'Lower horizontal line {}, {}'.format(
#self.posx,
#self.posy + ( ( (self.BOARD_HEIGHT / 3) - (self.THICKNESS / 2) ) * 2)
#))
# Draws the game board in the middle of the screen
def draw_board(self, screen):
# Draw left vertical line
pygame.draw.rect(screen, self.COLOR, (
self.posx + (self.BOARD_WIDTH / 3) - (self.THICKNESS / 2),
self.posy, self.THICKNESS, self.BOARD_HEIGHT), 0)
# Draw right verical line
pygame.draw.rect(screen, self.COLOR, (
self.posx + ( (self.BOARD_WIDTH / 3) * 2 ),
self.posy, self.THICKNESS, self.BOARD_HEIGHT), 0)
# Draw upper horizontal line
pygame.draw.rect(screen, self.COLOR, (
self.posx,
self.posy + (self.BOARD_HEIGHT / 3) - (self.THICKNESS / 2),
self.BOARD_WIDTH, self.THICKNESS), 0)
# Draw lower horizontal line
pygame.draw.rect(screen, self.COLOR, (
self.posx,
self.posy + ( (self.BOARD_HEIGHT / 3) * 2),
self.BOARD_WIDTH, self.THICKNESS), 0)
# Getters to access board vars
def get_board_posx(self):
return self.posx
def get_board_posy(self):
return self.posy
def get_board_size(self):
return self.BOARD_WIDTH
def get_thickness(self):
return self.THICKNESS
def get_field_size(self):
return (self.BOARD_WIDTH / 3) - (self.THICKNESS / 2)
def get_color(self):
return self.COLOR
# The board is divided into 9 areas:
#
# area 0.0 | area 0.1 | area 0.2
# _______________________________
# area 1.0 | area 1.1 | area 1.2
# _______________________________
# area 2.0 | area 2.1 | area 2.2
class area:
active = False
def __init__(self, posx, posy, size):
self.posx = posx
self.posy = posy
# width and height of an area
self.size = size
# 0 = inactive
# 1 = mouse is hovering over area
# 2 = area is filled with an X
# 3 = area is filled with an O
self.state = 0
# Debug
#print(posx, posy, size)
def __repr__(self):
return 'Area({}, {})'.format(self.posx, self.posy)
# Function tests if Point(x, y) lies in the Area
# Typically used with mouseposition
def is_in(self, x, y):
if (
(x >= self.posx) and # Top left limit
(x <= (self.posx + self.size)) and # Top right limit
(y >= self.posy) and # Bottom left limit
(y <= (self.posy + self.size)) # Bottom right limit
):
return True
else:
return False
# only for debug
def print_pos(self):
print(self.posx, self.posy, self.size)
def get_posx(self):
return self.posx
def get_posy(self):
return self.posy
def get_active(self):
return self.active
def change_active(self):
self.active = not self.active
def get_state(self):
return self.state
def set_state(self, state):
if (state == 0 or
state == 1 or
state == 2 or
state == 3):
self.state = state
main()
aW1wb3J0IHB5Z2FtZQoKCgpkZWYgbWFpbigpOgoKICAgICMgU2V0dXAgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGdhbWUgLyBzY3JlZW4KICAgIHB5Z2FtZS5pbml0KCkKICAgIFNDUkVFTl9XSURUSCAsU0NSRUVOX0hFSUdIVCA9IDEyODAsIDcyMAogICAgRlBTID0gNjAKICAgIHNjcmVlbiA9IHB5Z2FtZS5kaXNwbGF5LnNldF9tb2RlKCAoU0NSRUVOX1dJRFRILCBTQ1JFRU5fSEVJR0hUKSApCiAgICBweWdhbWUuZGlzcGxheS5zZXRfY2FwdGlvbignVGljIFRhYyBUb2UnKQogICAgY2xvY2sgPSBweWdhbWUudGltZS5DbG9jaygpCiAgICBydW5uaW5nID0gVHJ1ZQogICAgY2xpY2tlZCA9IEZhbHNlCiAgICAjIDAgPSBubyB3aW5uZXIsIDEgPSBwbGF5ZXIxIHdvbiwgMiA9IHBsYXllcjIgd29uLCAzID0gdGllCiAgICB3aW5uZXIgPSAwCiAgICByZXN0YXJ0ID0gRmFsc2UKICAgIEJHX0NPTE9SID0gKDAsIDAsIDApCiAgICBiID0gYm9hcmQoU0NSRUVOX1dJRFRILCBTQ1JFRU5fSEVJR0hUKQoKICAgICMgQ291bnRzIHdob3NlIHR1cm4gaXQgaXMKICAgICMgRXZlbiB0dXJuID0gUGxheWVyMQogICAgIyBPZGQgdHVybiA9IFBsYXllcjIKICAgIHR1cm4gPSAwCgogICAgIyBGaWxsIGFuIGFycmF5IHdpdGggOSBhcmVhIG9iamVjdHMsIGZvciBlYWNoCiAgICAjIGFyZWEgb2YgdGhlIGJvYXJkCiAgICAjCiAgICAjIGFyZWEgMC4wIHwgYXJlYSAwLjEgfCBhcmVhIDAuMgogICAgIyBfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCiAgICAjIGFyZWEgMS4wIHwgYXJlYSAxLjEgfCBhcmVhIDEuMgogICAgIyBfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCiAgICAjIGFyZWEgMi4wIHwgYXJlYSAyLjEgfCBhcmVhIDIuMgogICAgYXJlYXMgPSBbXQogICAgZm9yIGkgaW4gcmFuZ2UoMyk6CiAgICAgICAgcm93ID0gW10KICAgICAgICBmb3IgaiBpbiByYW5nZSgzKToKICAgICAgICAgICAgcm93LmFwcGVuZChhcmVhKAogICAgICAgICAgICAoYi5nZXRfYm9hcmRfcG9zeCgpICsgKGogKiAoIChiLmdldF9ib2FyZF9zaXplKCkgLyAzKSArCiAgICAgICAgICAgIChiLmdldF90aGlja25lc3MoKSAvMiApICkgKSApLAogICAgICAgICAgICAoYi5nZXRfYm9hcmRfcG9zeSgpICsgKGkgKiAoIChiLmdldF9ib2FyZF9zaXplKCkgLyAzKSArCiAgICAgICAgICAgIChiLmdldF90aGlja25lc3MoKSAvIDIgKSApICkgKSwKICAgICAgICAgICAgYi5nZXRfZmllbGRfc2l6ZSgpCiAgICAgICAgICAgICkpCgogICAgICAgIGFyZWFzLmFwcGVuZChyb3cpCgoKICAgICMjIyMjIyMjIyMjCiAgICAjIEdBTUUgTE9PUAogICAgIyMjIyMjIyMjIyMKICAgIHdoaWxlIHJ1bm5pbmc6CgogICAgICAgICMgQWJvcnQgY29uZGl0aW9uCiAgICAgICAgIyBJZiAoeCkgaXMgY2xpY2tlZCwgdGVybWluYXRlIHByb2dyYW0KICAgICAgICAjIElmIGNsaWNrZWQgaXMgVHJ1ZSwgdGhlIHVzZXIgY2xpY2tlZCBpbiB0aGlzCiAgICAgICAgIyBpdGVyYXRpb24gb2YgdGhlIGdhbWUgbG9vcAogICAgICAgIGZvciBldmVudCBpbiBweWdhbWUuZXZlbnQuZ2V0KCk6CiAgICAgICAgICAgIGlmIGV2ZW50LnR5cGUgPT0gcHlnYW1lLlFVSVQ6CiAgICAgICAgICAgICAgICBydW5uaW5nID0gRmFsc2UKICAgICAgICAgICAgZWxpZiBldmVudC50eXBlID09IHB5Z2FtZS5NT1VTRUJVVFRPTkRPV046CiAgICAgICAgICAgICAgICBjbGlja2VkID0gVHJ1ZQogICAgICAgICAgICAgICAgaWYgKG5vdCAod2lubmVyID09IDApKToKICAgICAgICAgICAgICAgICAgICByZXN0YXJ0ID0gVHJ1ZQoKICAgICAgICAgICAgIyBJZiBhbnkga2V5IG9yIG1vdXNlYnV0dG9uIGlzIHByZXNzZWQKICAgICAgICAgICAgIyB3aGlsZSBnYW1lIGlzIG92ZXIsIGdhbWUgcmVzdGFydHMKICAgICAgICAgICAgZWxpZiBldmVudC50eXBlID09IHB5Z2FtZS5LRVlET1dOOgogICAgICAgICAgICAgICAgaWYgKG5vdCB3aW5uZXIgPT0gMCk6CiAgICAgICAgICAgICAgICAgICAgcmVzdGFydCA9IFRydWUKCiAgICAgICAgICAgICAgICBpZiBldmVudC5rZXkgPT0gcHlnYW1lLktfRVNDQVBFOgogICAgICAgICAgICAgICAgICAgIHJ1bm5pbmcgPSBGYWxzZQoKCgoKICAgICAgICAjIFNldHVwIHNjcmVlbgogICAgICAgIHNjcmVlbi5maWxsKEJHX0NPTE9SKQogICAgICAgIGIuZHJhd19ib2FyZChzY3JlZW4pCiAgICAgICAgbW91c2VfcG9zeCwgbW91c2VfcG9zeSA9IHB5Z2FtZS5tb3VzZS5nZXRfcG9zKCkKCgogICAgICAgICMgTG9vcGluZyB0aHJvdWdoIGV2ZXJ5IGFyZWEgb24gdGhlIGJvYXJkCiAgICAgICAgIyBNb3N0IG9mIHRoZSBnYW1lIGxvZ2ljIGhhcHBlbnMgaGVyZQogICAgICAgIGZvciBpIGluIHJhbmdlKGxlbihhcmVhcykpOgogICAgICAgICAgICBmb3IgaiBpbiByYW5nZShsZW4oYXJlYXNbaV0pKToKCiAgICAgICAgICAgICAgICAjIERyYXcgYWxyZWFkeSBzZXQgWCB0b2tlbnMKICAgICAgICAgICAgICAgIGlmIChhcmVhc1tpXVtqXS5nZXRfc3RhdGUoKSA9PSAyKToKICAgICAgICAgICAgICAgICAgICBkcmF3X3hfdG9rZW4oCiAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uZ2V0X3Bvc3goKSwKICAgICAgICAgICAgICAgICAgICBhcmVhc1tpXVtqXS5nZXRfcG9zeSgpLAogICAgICAgICAgICAgICAgICAgIGIuZ2V0X2ZpZWxkX3NpemUoKSwKICAgICAgICAgICAgICAgICAgICBzY3JlZW4sCiAgICAgICAgICAgICAgICAgICAgYi5nZXRfY29sb3IoKQogICAgICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgICAgICAjIERyYXcgYWxyZWFkeSBzZXQgTyB0b2tlbnMKICAgICAgICAgICAgICAgIGlmIChhcmVhc1tpXVtqXS5nZXRfc3RhdGUoKSA9PSAzKToKICAgICAgICAgICAgICAgICAgICBkcmF3X29fdG9rZW4oCiAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uZ2V0X3Bvc3goKSwKICAgICAgICAgICAgICAgICAgICBhcmVhc1tpXVtqXS5nZXRfcG9zeSgpLAogICAgICAgICAgICAgICAgICAgIGIuZ2V0X2ZpZWxkX3NpemUoKSwKICAgICAgICAgICAgICAgICAgICBzY3JlZW4sCiAgICAgICAgICAgICAgICAgICAgYi5nZXRfY29sb3IoKQogICAgICAgICAgICAgICAgICAgICkKCgogICAgICAgICAgICAgICAgIyBDaGVja2luZyBpZiBtb3VzZSBpcyBob3ZlcmluZyBvdmVyIGFueQogICAgICAgICAgICAgICAgIyBvZiB0aGUgYXJlYXMKICAgICAgICAgICAgICAgICMgSWYgc28sIHByZXZpZXcgdG9rZW4gdGhhdCBpcyBhYm91dCB0byBiZSBkcmF3bgogICAgICAgICAgICAgICAgaWYoYXJlYXNbaV1bal0uaXNfaW4obW91c2VfcG9zeCwgbW91c2VfcG9zeSkpOgogICAgICAgICAgICAgICAgICAgICMgcHJpbnQoJ01vdXNlIGlzIGluIEFyZWEoe30sIHt9KScuZm9ybWF0KGksIGopKQoKCiAgICAgICAgICAgICAgICAgICAgIyBIYW5kbGluZyBjbGlja3MgKERyYXcgdG9rZW5zKQogICAgICAgICAgICAgICAgICAgIGlmIGNsaWNrZWQ6CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChub3QoYXJlYXNbaV1bal0uZ2V0X3N0YXRlKCkgPT0gMikgYW5kCiAgICAgICAgICAgICAgICAgICAgICAgIG5vdChhcmVhc1tpXVtqXS5nZXRfc3RhdGUoKSA9PTMpIGFuZCB3aW5uZXIgPT0gMCApOgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0dXJuICUgMiA9PSAwKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcmF3X3hfdG9rZW4oCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uZ2V0X3Bvc3goKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmVhc1tpXVtqXS5nZXRfcG9zeSgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGIuZ2V0X2ZpZWxkX3NpemUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYi5nZXRfY29sb3IoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uc2V0X3N0YXRlKDIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHVybiArPSAxCgogICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcmF3X29fdG9rZW4oCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uZ2V0X3Bvc3goKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmVhc1tpXVtqXS5nZXRfcG9zeSgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGIuZ2V0X2ZpZWxkX3NpemUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYi5nZXRfY29sb3IoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uc2V0X3N0YXRlKDMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHVybiArPSAxCgoKCiAgICAgICAgICAgICAgICAgICAgaWYgKCAoYXJlYXNbaV1bal0uZ2V0X3N0YXRlKCkgPT0gMAogICAgICAgICAgICAgICAgICAgIG9yIGFyZWFzW2ldW2pdLmdldF9zdGF0ZSgpID09IDEpIGFuZCB3aW5uZXIgPT0gMCApOgoKICAgICAgICAgICAgICAgICAgICAgICAgIyBldmVuIHR1cm46IFBsYXllcjEKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHR1cm4gJSAyID09IDApOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgZHJhd194X3Rva2VuKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uZ2V0X3Bvc3goKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFyZWFzW2ldW2pdLmdldF9wb3N5KCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiLmdldF9maWVsZF9zaXplKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiLmdldF9jb2xvcigpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uc2V0X3N0YXRlKDEpCgogICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgZHJhd19vX3Rva2VuKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uZ2V0X3Bvc3goKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFyZWFzW2ldW2pdLmdldF9wb3N5KCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiLmdldF9maWVsZF9zaXplKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiLmdldF9jb2xvcigpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uc2V0X3N0YXRlKDEpCgoKCiAgICAgICAgICAgICAgICAjIElmIG1vdXNlIGlzIG5vdCBob3ZlcmluZyBvdmVyIGFuIGFyZWEKICAgICAgICAgICAgICAgICMgYW5kIGFyZWEgaXMgbm90IGFscmVhZHkgdXNlZAogICAgICAgICAgICAgICAgIyBTZXQgc3RhdGUgdG8gaW5hY3RpdmUKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgaWYgKAogICAgICAgICAgICAgICAgICAgIG5vdChhcmVhc1tpXVtqXS5nZXRfc3RhdGUoKSA9PSAyKSBhbmQKICAgICAgICAgICAgICAgICAgICBub3QoYXJlYXNbaV1bal0uZ2V0X3N0YXRlKCkgPT0zKQogICAgICAgICAgICAgICAgICAgICk6CiAgICAgICAgICAgICAgICAgICAgICAgIGFyZWFzW2ldW2pdLnNldF9zdGF0ZSgwKQoKCiAgICAgICAgIyBDaGVjayBpZiB0aGVyZSBpcyBhIHdpbm5lcgogICAgICAgICMgYWZ0ZXIgdGhpcyByb3VuZAogICAgICAgIHdpbl9jb25zID0gWwogICAgICAgICMgSG9yaXpvbnRhbAogICAgICAgIFsgYXJlYXNbMF1bMF0sIGFyZWFzWzBdWzFdLCBhcmVhc1swXVsyXSBdLAogICAgICAgIFsgYXJlYXNbMV1bMF0sIGFyZWFzWzFdWzFdLCBhcmVhc1sxXVsyXSBdLAogICAgICAgIFsgYXJlYXNbMl1bMF0sIGFyZWFzWzJdWzFdLCBhcmVhc1syXVsyXSBdLAogICAgICAgICMgVmVydGljYWwKICAgICAgICBbIGFyZWFzWzBdWzBdLCBhcmVhc1sxXVswXSwgYXJlYXNbMl1bMF0gXSwKICAgICAgICBbIGFyZWFzWzBdWzFdLCBhcmVhc1sxXVsxXSwgYXJlYXNbMl1bMV0gXSwKICAgICAgICBbIGFyZWFzWzBdWzJdLCBhcmVhc1sxXVsyXSwgYXJlYXNbMl1bMl0gXSwKICAgICAgICAjIERpYWdvbmFsCiAgICAgICAgWyBhcmVhc1swXVsyXSwgYXJlYXNbMV1bMV0sIGFyZWFzWzJdWzBdIF0sCiAgICAgICAgWyBhcmVhc1swXVswXSwgYXJlYXNbMV1bMV0sIGFyZWFzWzJdWzJdIF0KICAgICAgICBdCiAgICAgICAgZm9yIGkgaW4gcmFuZ2UoIGxlbih3aW5fY29ucykgKToKICAgICAgICAgICAgaWYgKAogICAgICAgICAgICB3aW5fY29uc1tpXVswXS5nZXRfc3RhdGUoKSA9PQogICAgICAgICAgICB3aW5fY29uc1tpXVsxXS5nZXRfc3RhdGUoKSA9PQogICAgICAgICAgICB3aW5fY29uc1tpXVsyXS5nZXRfc3RhdGUoKSA9PQogICAgICAgICAgICAyCiAgICAgICAgICAgICk6CiAgICAgICAgICAgICAgICB3aW5uZXIgPSAxCgogICAgICAgICAgICBpZiAoCiAgICAgICAgICAgIHdpbl9jb25zW2ldWzBdLmdldF9zdGF0ZSgpID09CiAgICAgICAgICAgIHdpbl9jb25zW2ldWzFdLmdldF9zdGF0ZSgpID09CiAgICAgICAgICAgIHdpbl9jb25zW2ldWzJdLmdldF9zdGF0ZSgpID09CiAgICAgICAgICAgIDMKICAgICAgICAgICAgKToKICAgICAgICAgICAgICAgIHdpbm5lciA9IDIKCiAgICAgICAgICAgICMgQ2hlY2sgZm9yIHRpZQogICAgICAgICAgICBmaWVsZHNfZmlsbGVkID0gMAogICAgICAgICAgICBmb3IgaSBpbiByYW5nZShsZW4oYXJlYXMpKToKICAgICAgICAgICAgICAgIGZvciBqIGluIHJhbmdlKGxlbihhcmVhc1tpXSkpOgogICAgICAgICAgICAgICAgICAgIGlmICgKICAgICAgICAgICAgICAgICAgICBhcmVhc1tpXVtqXS5nZXRfc3RhdGUoKSA9PSAyIG9yCiAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uZ2V0X3N0YXRlKCkgPT0gMwogICAgICAgICAgICAgICAgICAgICk6CiAgICAgICAgICAgICAgICAgICAgICAgIGZpZWxkc19maWxsZWQgKz0gMQoKICAgICAgICAgICAgaWYgKGZpZWxkc19maWxsZWQgPT0gOSBhbmQgd2lubmVyID09IDApOgogICAgICAgICAgICAgICAgd2lubmVyID0gMwoKICAgICAgICBpZiAoIG5vdCAod2lubmVyID09IDApICk6CgogICAgICAgICAgICBpZiB3aW5uZXIgPT0gMToKICAgICAgICAgICAgICAgIGRyYXdfdGV4dChzY3JlZW4sICdQbGF5ZXIgWCBoYXMgV29uLiBQcmVzcyBhbnkga2V5IHRvIHJlc3RhcnQnLAogICAgICAgICAgICAgICAgYi5nZXRfYm9hcmRfcG9zeCgpLAogICAgICAgICAgICAgICAgYi5nZXRfYm9hcmRfcG9zeSgpIC0gYi5nZXRfYm9hcmRfc2l6ZSgpICogMC4wOCwKICAgICAgICAgICAgICAgIGludCggcm91bmQoYi5nZXRfYm9hcmRfc2l6ZSgpICogMC4wNTUpICkgKQogICAgICAgICAgICBlbGlmIHdpbm5lciA9PSAyOgogICAgICAgICAgICAgICAgZHJhd190ZXh0KHNjcmVlbiwgJ1BsYXllciBPIGhhcyBXb24uIFByZXNzIGFueSBrZXkgdG8gcmVzdGFydCcsCiAgICAgICAgICAgICAgICBiLmdldF9ib2FyZF9wb3N4KCksCiAgICAgICAgICAgICAgICBiLmdldF9ib2FyZF9wb3N5KCkgLSBiLmdldF9ib2FyZF9zaXplKCkgKiAwLjA4LAogICAgICAgICAgICAgICAgaW50KCByb3VuZChiLmdldF9ib2FyZF9zaXplKCkgKiAwLjA1NSkgKSkKICAgICAgICAgICAgZWxpZiB3aW5uZXIgPT0gMzoKICAgICAgICAgICAgICAgIGRyYXdfdGV4dChzY3JlZW4sICdZb3UgYm90aCB3aW4hIFByZXNzIGFueSBrZXkgdG8gcmVzdGFydCcsCiAgICAgICAgICAgICAgICBiLmdldF9ib2FyZF9wb3N4KCksCiAgICAgICAgICAgICAgICBiLmdldF9ib2FyZF9wb3N5KCkgLSBiLmdldF9ib2FyZF9zaXplKCkgKiAwLjA4LAogICAgICAgICAgICAgICAgaW50KCByb3VuZChiLmdldF9ib2FyZF9zaXplKCkgKiAwLjA1NSkgKSApCgoKCiAgICAgICAgIyBSZXNldCBhbGwgY2hhbmdpbmcgdmFyaWFibGVzCiAgICAgICAgIyBhbmQgc2V0IGFyZWEgc3RhdGVzIHRvIGluYWN0aXZlCiAgICAgICAgaWYgcmVzdGFydDoKICAgICAgICAgICAgcmVzdGFydCA9IEZhbHNlCiAgICAgICAgICAgIHdpbm5lciA9IDAKICAgICAgICAgICAgdHVybiA9IDAKCiAgICAgICAgICAgIGZvciBpIGluIHJhbmdlKGxlbihhcmVhcykpOgogICAgICAgICAgICAgICAgZm9yIGogaW4gcmFuZ2UobGVuKGFyZWFzW2ldKSk6CiAgICAgICAgICAgICAgICAgICAgYXJlYXNbaV1bal0uc2V0X3N0YXRlKDApCgoKCiAgICAgICAgIyBQeWdhbWUgc3R1ZmYKICAgICAgICBweWdhbWUuZGlzcGxheS51cGRhdGUoKQogICAgICAgIGNsb2NrLnRpY2soRlBTKQogICAgICAgIGNsaWNrZWQgPSBGYWxzZQoKICAgICAgICAjIyMjIyMjIyMjIyMjIyMjIyMKICAgICAgICAjIEVORCBPRiBHQU1FIExPT1AKICAgICAgICAjIyMjIyMjIyMjIyMjIyMjIyMKCiAgICAjIGNsb3NpbmcgdGhlIHB5Z2FtZSBhcHBsaWNhdGlvbgogICAgcHlnYW1lLnF1aXQoKQoKCmRlZiBkcmF3X3RleHQoc2NyZWVuLCB0ZXh0LCBwb3N4LCBwb3N5LCBzaXplKToKICAgIG15X2ZvbnQgPSBweWdhbWUuZm9udC5TeXNGb250KCdBcmlhbCcsIHNpemUpCiAgICB0ZXh0c3VyZmFjZSA9IG15X2ZvbnQucmVuZGVyKHRleHQsIEZhbHNlLCAoMjU1LCAyNTUsIDI1NSkpCiAgICBzY3JlZW4uYmxpdCh0ZXh0c3VyZmFjZSwgKHBvc3gsIHBvc3kpKQoKZGVmIGRyYXdfeF90b2tlbihwb3N4LCBwb3N5LCBzaXplLCBzY3JlZW4sIGNvbG9yKToKICAgIG9mZnNldCA9IHNpemUgKiAwLjEKICAgIHRoaWNrbmVzcyA9IHNpemUgKiAwLjEKCiAgICBwb2ludHMwID0gWwogICAgKHBvc3ggKyBvZmZzZXQsIHBvc3kgKyBvZmZzZXQpLAogICAgKHBvc3ggKyBvZmZzZXQgKyB0aGlja25lc3MsIHBvc3kgKyBvZmZzZXQpLAogICAgKHBvc3ggKyBzaXplIC0gb2Zmc2V0LCBwb3N5ICsgc2l6ZSAtIG9mZnNldCksCiAgICAocG9zeCArIHNpemUgLSAob2Zmc2V0ICsgdGhpY2tuZXNzKSwgcG9zeSArIHNpemUgLSBvZmZzZXQpXQoKICAgIHB5Z2FtZS5kcmF3LnBvbHlnb24oc2NyZWVuLCBjb2xvciwgcG9pbnRzMCwgMCApCgogICAgcG9pbnRzMSA9IFsKICAgICggcG9zeCArICBzaXplIC0gKG9mZnNldCArIHRoaWNrbmVzcyksIHBvc3kgKyBvZmZzZXQpLAogICAgKCBwb3N4ICsgc2l6ZSAtIG9mZnNldCwgcG9zeSArIG9mZnNldCksCiAgICAoIHBvc3ggKyBvZmZzZXQgKyB0aGlja25lc3MsIHBvc3kgKyBzaXplIC0gb2Zmc2V0KSwKICAgICggcG9zeCArIG9mZnNldCwgcG9zeSArIHNpemUgLSBvZmZzZXQpXQoKICAgIHB5Z2FtZS5kcmF3LnBvbHlnb24oc2NyZWVuLCBjb2xvciwgcG9pbnRzMSwgMCApCgpkZWYgZHJhd19vX3Rva2VuKHBvc3gsIHBvc3ksIHNpemUsIHNjcmVlbiwgY29sb3IpOgogICAgb2Zmc2V0ID0gc2l6ZSAqIDAuMQogICAgdGhpY2tuZXNzID0gc2l6ZSAqIDAuMQogICAgcmFkaXVzID0gc2l6ZSAvIDIgLSBvZmZzZXQKCiAgICBweWdhbWUuZHJhdy5jaXJjbGUoCiAgICBzY3JlZW4sCiAgICBjb2xvciwKICAgICggaW50KCByb3VuZChwb3N4ICsgKHNpemUgLyAyKSwgMCkgKSwKICAgIGludCggcm91bmQocG9zeSArIChzaXplIC8gMiksIDApICkgKSwKICAgIGludCggcm91bmQocmFkaXVzKSApLAogICAgaW50KCByb3VuZCAodGhpY2tuZXNzKSApICkKCgpjbGFzcyBib2FyZDoKCiAgICBkZWYgX19pbml0X18oc2VsZiwgU0NSRUVOX1dJRFRILCBTQ1JFRU5fSEVJR0hUKToKICAgICAgICBzZWxmLlNDUkVFTl9XSURUSCA9IFNDUkVFTl9XSURUSAogICAgICAgIHNlbGYuU0NSRUVOX0hFSUdIVCA9IFNDUkVFTl9IRUlHSFQKCiAgICAgICAgc2VsZi5CT0FSRF9XSURUSCA9IHNlbGYuQk9BUkRfSEVJR0hUID0gNTUwCiAgICAgICAgc2VsZi5DT0xPUiA9ICgyNTUsIDI1NSwgMjU1KQogICAgICAgIHNlbGYuVEhJQ0tORVNTID0gc2VsZi5CT0FSRF9XSURUSCAqIDAuMDI1CgogICAgICAgICMgVGhlIHBvaW50IChwb3N4LCBwb3N5KSBpcyBhdCB0aGUgdG9wIGxlZnQgcG9zaXRpb24KICAgICAgICAjIG9mIHRoZSBib2FyZAogICAgICAgICMgU2V0dGluZyB1cCBwb3N4IGFuZCBwb3N5IGluIGEgd2F5CiAgICAgICAgIyB0aGF0IHRoZSBib2FyZCBpcyBhbHdheXMgZHJhd24gaW4gdGhlIGNlbnRyZQogICAgICAgICMgb2YgdGhlIHB5Z2FtZSBhcHBsaWNhdGlvbi9zY3JlZW4KICAgICAgICBzZWxmLnBvc3ggPSAoc2VsZi5TQ1JFRU5fV0lEVEggLyAyKSAtIChzZWxmLkJPQVJEX1dJRFRIIC8gMikKICAgICAgICBzZWxmLnBvc3kgPSAoc2VsZi5TQ1JFRU5fSEVJR0hUIC8gMikgLSAoc2VsZi5CT0FSRF9IRUlHSFQgLyAyKQoKICAgICAgICAjIFByaW50aW5nIHBvc2l0aW9ucyBvZiB0aGUgNCBib2FyZCBsaW5lcwogICAgICAgICMgZm9yIGRlYnVnIHB1cnBvc2VzCiAgICAgICAgI3ByaW50KCAnTGVmdCB2ZXJ0aWNhbCBsaW5lIHt9LCB7fScuZm9ybWF0KAogICAgICAgICNzZWxmLnBvc3ggKyAoc2VsZi5CT0FSRF9XSURUSCAvIDMpIC0gKHNlbGYuVEhJQ0tORVNTIC8gMiksCiAgICAgICAgI3NlbGYucG9zeQogICAgICAgICMpICkKCiAgICAgICAgI3ByaW50KCAnUmlnaHQgdmVydGljYWwgbGluZSB7fSwge30nLmZvcm1hdCgKICAgICAgICAjc2VsZi5wb3N4ICsgKCAoIChzZWxmLkJPQVJEX1dJRFRIIC8gMykgLSAoc2VsZi5USElDS05FU1MgLyAyKSApICogMiksCiAgICAgICAgI3NlbGYucG9zeQogICAgICAgICMpKQoKICAgICAgICAjcHJpbnQoICdVcHBlciBob3Jpem9udGFsIGxpbmUge30sIHt9Jy5mb3JtYXQoCiAgICAgICAgI3NlbGYucG9zeCwKICAgICAgICAjc2VsZi5wb3N5ICsgKHNlbGYuQk9BUkRfSEVJR0hUIC8gMykgLSAoc2VsZi5USElDS05FU1MgLyAyKQogICAgICAgICMpICkKCiAgICAgICAgI3ByaW50KCAnTG93ZXIgaG9yaXpvbnRhbCBsaW5lIHt9LCB7fScuZm9ybWF0KAogICAgICAgICNzZWxmLnBvc3gsCiAgICAgICAgI3NlbGYucG9zeSArICggKCAoc2VsZi5CT0FSRF9IRUlHSFQgLyAzKSAtIChzZWxmLlRISUNLTkVTUyAvIDIpICkgKiAyKQogICAgICAgICMpKQoKCiAgICAjIERyYXdzIHRoZSBnYW1lIGJvYXJkIGluIHRoZSBtaWRkbGUgb2YgdGhlIHNjcmVlbgogICAgZGVmIGRyYXdfYm9hcmQoc2VsZiwgc2NyZWVuKToKCiAgICAgICAgIyBEcmF3IGxlZnQgdmVydGljYWwgIGxpbmUKICAgICAgICBweWdhbWUuZHJhdy5yZWN0KHNjcmVlbiwgc2VsZi5DT0xPUiwgKAogICAgICAgIHNlbGYucG9zeCArIChzZWxmLkJPQVJEX1dJRFRIIC8gMykgLSAoc2VsZi5USElDS05FU1MgLyAyKSwKICAgICAgICBzZWxmLnBvc3ksIHNlbGYuVEhJQ0tORVNTLCBzZWxmLkJPQVJEX0hFSUdIVCksIDApCgogICAgICAgICMgRHJhdyByaWdodCB2ZXJpY2FsIGxpbmUKICAgICAgICBweWdhbWUuZHJhdy5yZWN0KHNjcmVlbiwgc2VsZi5DT0xPUiwgKAogICAgICAgIHNlbGYucG9zeCArICggKHNlbGYuQk9BUkRfV0lEVEggLyAzKSAqIDIgKSwKICAgICAgICBzZWxmLnBvc3ksIHNlbGYuVEhJQ0tORVNTLCBzZWxmLkJPQVJEX0hFSUdIVCksIDApCgogICAgICAgICMgRHJhdyB1cHBlciBob3Jpem9udGFsIGxpbmUKICAgICAgICBweWdhbWUuZHJhdy5yZWN0KHNjcmVlbiwgc2VsZi5DT0xPUiwgKAogICAgICAgIHNlbGYucG9zeCwKICAgICAgICBzZWxmLnBvc3kgKyAoc2VsZi5CT0FSRF9IRUlHSFQgLyAzKSAtIChzZWxmLlRISUNLTkVTUyAvIDIpLAogICAgICAgIHNlbGYuQk9BUkRfV0lEVEgsIHNlbGYuVEhJQ0tORVNTKSwgMCkKCiAgICAgICAgIyBEcmF3IGxvd2VyIGhvcml6b250YWwgbGluZQogICAgICAgIHB5Z2FtZS5kcmF3LnJlY3Qoc2NyZWVuLCBzZWxmLkNPTE9SLCAoCiAgICAgICAgc2VsZi5wb3N4LAogICAgICAgIHNlbGYucG9zeSArICggKHNlbGYuQk9BUkRfSEVJR0hUIC8gMykgKiAyKSwKICAgICAgICBzZWxmLkJPQVJEX1dJRFRILCBzZWxmLlRISUNLTkVTUyksIDApCgogICAgIyBHZXR0ZXJzIHRvIGFjY2VzcyBib2FyZCB2YXJzCiAgICBkZWYgZ2V0X2JvYXJkX3Bvc3goc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYucG9zeAogICAgZGVmIGdldF9ib2FyZF9wb3N5KHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLnBvc3kKICAgIGRlZiBnZXRfYm9hcmRfc2l6ZShzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5CT0FSRF9XSURUSAogICAgZGVmIGdldF90aGlja25lc3Moc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuVEhJQ0tORVNTCiAgICBkZWYgZ2V0X2ZpZWxkX3NpemUoc2VsZik6CiAgICAgICAgcmV0dXJuIChzZWxmLkJPQVJEX1dJRFRIIC8gMykgLSAoc2VsZi5USElDS05FU1MgLyAyKQogICAgZGVmIGdldF9jb2xvcihzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5DT0xPUgoKIyBUaGUgYm9hcmQgaXMgZGl2aWRlZCBpbnRvIDkgYXJlYXM6CiMKIyBhcmVhIDAuMCB8IGFyZWEgMC4xIHwgYXJlYSAwLjIKIyBfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCiMgYXJlYSAxLjAgfCBhcmVhIDEuMSB8IGFyZWEgMS4yCiMgX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwojIGFyZWEgMi4wIHwgYXJlYSAyLjEgfCBhcmVhIDIuMgpjbGFzcyBhcmVhOgogICAgYWN0aXZlID0gRmFsc2UKCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHBvc3gsIHBvc3ksIHNpemUpOgogICAgICAgIHNlbGYucG9zeCA9IHBvc3gKICAgICAgICBzZWxmLnBvc3kgPSBwb3N5CgogICAgICAgICMgd2lkdGggYW5kIGhlaWdodCBvZiBhbiBhcmVhCiAgICAgICAgc2VsZi5zaXplID0gc2l6ZQoKICAgICAgICAjIDAgPSBpbmFjdGl2ZQogICAgICAgICMgMSA9IG1vdXNlIGlzIGhvdmVyaW5nIG92ZXIgYXJlYQogICAgICAgICMgMiA9IGFyZWEgaXMgZmlsbGVkIHdpdGggYW4gWAogICAgICAgICMgMyA9IGFyZWEgaXMgZmlsbGVkIHdpdGggYW4gTwogICAgICAgIHNlbGYuc3RhdGUgPSAwCgogICAgICAgICMgRGVidWcKICAgICAgICAjcHJpbnQocG9zeCwgcG9zeSwgc2l6ZSkKCiAgICBkZWYgX19yZXByX18oc2VsZik6CiAgICAgICAgcmV0dXJuICdBcmVhKHt9LCB7fSknLmZvcm1hdChzZWxmLnBvc3gsIHNlbGYucG9zeSkKCiAgICAjIEZ1bmN0aW9uIHRlc3RzIGlmIFBvaW50KHgsIHkpIGxpZXMgaW4gdGhlIEFyZWEKICAgICMgVHlwaWNhbGx5IHVzZWQgd2l0aCBtb3VzZXBvc2l0aW9uCiAgICBkZWYgaXNfaW4oc2VsZiwgeCwgeSk6CiAgICAgICAgaWYgKAogICAgICAgICh4ID49IHNlbGYucG9zeCkgYW5kICAgICAgICAgICAgICAgICMgVG9wIGxlZnQgbGltaXQKICAgICAgICAoeCA8PSAoc2VsZi5wb3N4ICsgc2VsZi5zaXplKSkgYW5kICAjIFRvcCByaWdodCBsaW1pdAogICAgICAgICh5ID49IHNlbGYucG9zeSkgYW5kICAgICAgICAgICAgICAgICMgQm90dG9tIGxlZnQgbGltaXQKICAgICAgICAoeSA8PSAoc2VsZi5wb3N5ICsgc2VsZi5zaXplKSkgICAgICAjIEJvdHRvbSByaWdodCBsaW1pdAogICAgICAgICk6CiAgICAgICAgICAgIHJldHVybiBUcnVlCgogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJldHVybiBGYWxzZQoKICAgICMgb25seSBmb3IgZGVidWcKICAgIGRlZiBwcmludF9wb3Moc2VsZik6CiAgICAgICAgcHJpbnQoc2VsZi5wb3N4LCBzZWxmLnBvc3ksIHNlbGYuc2l6ZSkKCgoKICAgIGRlZiBnZXRfcG9zeChzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5wb3N4CgogICAgZGVmIGdldF9wb3N5KHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLnBvc3kKCgogICAgZGVmIGdldF9hY3RpdmUoc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuYWN0aXZlCgogICAgZGVmIGNoYW5nZV9hY3RpdmUoc2VsZik6CiAgICAgICAgc2VsZi5hY3RpdmUgPSBub3Qgc2VsZi5hY3RpdmUKCgogICAgZGVmIGdldF9zdGF0ZShzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5zdGF0ZQoKICAgIGRlZiBzZXRfc3RhdGUoc2VsZiwgc3RhdGUpOgogICAgICAgIGlmIChzdGF0ZSA9PSAwIG9yCiAgICAgICAgc3RhdGUgPT0gMSBvcgogICAgICAgIHN0YXRlID09IDIgb3IKICAgICAgICBzdGF0ZSA9PSAzKToKICAgICAgICAgICAgc2VsZi5zdGF0ZSA9IHN0YXRlCgoKCgoKCgptYWluKCkK