from threading import Thread # потоки
import msvcrt # библиотека неприрывно считывающая нажатие клавиш в консоли
#a = msvcrt.getch() # получает клавишу формат: b'a' - a
#str(a)[2]
import random
import os
import math
import time
import copy
# PixelDisplay(width,height) поле для рисования
# Clear() очистить
# ShowDisplay() обновить
# SetPixel(x,y) нарисовать пиксель
# ClearPixel(x,y) удалить пиксель
# DrawLine(x1,y1,x2,y2) нарисовать линию
# DrawCircul(x,y,r) нарисовать окружность
# DrawPoligon(x1,y1,z1,x2,y2,z2,x3,y3,z3) нарисовать трехмерный полигон
class PixelDisplay:
h = 10 # 3d projection value
def __init__(self,width,height):
self.width = width
self.height = height
self.PixelArray = self.CreateDisplay()
self.cls = copy.deepcopy(self.PixelArray)
def CreateDisplay(self):
Y = []
X = []
for i in range(self.height):
for j in range(self.width):
X.append(" ")
Y.append(X)
X = []
return Y
def Clear(self):
self.PixelArray = []
self.PixelArray = copy.deepcopy(self.cls)
time.sleep(0.004)
os.system("cls")
time.sleep(0.004)
def ShowDisplay(self):
st = ""
for i in range(self.height):
for j in range(self.width):
st += self.PixelArray[i][j]
print(st)
st = ""
def SetPixel(self,x,y,pix="*"):
if (y < self.height and x <self.width and y >= 0 and x>= 0):
self.PixelArray[y][x] = pix
def ClearPixel (self,x,y):
if (y < self.height and x <self.width and y >= 0 and x>= 0):
self.PixelArray[y][x] = " "
def DrawLine(self,x1,y1,x2,y2,pix="*"):
if (abs(x2-x1)>=abs(y2-y1)):
if (x2 != x1):
tan = (y2-y1)/(x2-x1)
if (x2>x1):
for x in range(x1,x2):
y = tan*(x-x1)+y1
self.SetPixel(x,int(y),pix)
elif (x1>x2):
for x in range(x2,x1):
y = tan*(x-x1)+y1
self.SetPixel(x,int(y),pix)
elif (x1 == x2):
if (y2 > y1):
for y in range(y1,y2):
self.SetPixel(x1,y,pix)
elif (y1 > y2):
for y in range(y2,y1):
self.SetPixel(x1,y,pix)
else:
self.SetPixel(x1,y1,pix)
if (abs(x2-x1)< abs(y2-y1)):
if (y2 != y1):
tan = (x2-x1)/(y2-y1)
if (y2>y1):
for y in range(y1,y2):
x = tan*(y-y1)+x1
self.SetPixel(int(x),y,pix)
elif (y1>y2):
for y in range(y2,y1):
x = tan*(y-y1)+x1
self.SetPixel(int(x),y,pix)
elif (y1 == y2):
if (x2 > x1):
for x in range(x1,x2):
self.SetPixel(x,y1,pix)
elif (x1 > x2):
for x in range(x2,x1):
self.SetPixel(x,y1,pix)
else:
self.SetPixel(x1,y1,pix)
def DrawCircul(self,x,y,r,pix="*"):
for i in range(360):
_x = int(x + r*math.cos(i*3.1415926/180))
_y = int(y + r*math.sin(i*3.1415926/180))
self.SetPixel(_x,_y,pix)
def Pixel3d(self,x,y,z):
if (z > 0):
_x = int((x/z)*self.h)
_y = int((y/z)*self.h)
return [_x, _y]
else:
print("error")
def DrawPoligon(self,x1,y1,z1,x2,y2,z2,x3,y3,z3,pix="*"):
v1 = [x2-x1,y2-y1,z2-z1]
v2 = [x3-x2,y3-y2,z3-z2]
n = [v1[1]*v2[2]-v1[2]*v2[1],v1[2]*v2[0]-v1[0]*v2[2],v1[0]*v2[1]-v1[1]*v2[0]]
if (n[2]*1< 0):
P1 = self.Pixel3d(x1,y1,z1)
P2 = self.Pixel3d(x2,y2,z2)
P3 = self.Pixel3d(x3,y3,z3)
x1,y1 = P1[0],P1[1]
x2,y2 = P2[0],P2[1]
x3,y3 = P3[0],P3[1]
self.DrawLine(x1,y1,x2,y2,pix)
self.DrawLine(x2,y2,x3,y3,pix)
self.DrawLine(x3,y3,x1,y1,pix)
class Sprite:
# format
# A = [
#[[" ","*"," "],[" ","*"," "],[" ","*"," "]],
#[[" ","*"," "],["*","*","*"],[" ","*"," "]]
#]
def __init__(self,A,x,y,display):
self.x = x
self.y = y
self.display = display
self.img = A
self.width = len(self.img[0][0])
self.height = len(self.img[0])
self.shoot = len(self.img)
self.currentShoot = 0
self.collisionShoot = self.currentShoot
# отобразить спрайт и переключить на новый кадр
def Draw(self):
# i - y
# j - x
for i in range(len(self.img[self.currentShoot])):
for j in range(len(self.img[self.currentShoot][0])):
self.display.SetPixel(self.x+j,self.y+i,self.img[self.currentShoot][i][j])
self.collisionShoot = self.currentShoot
self.currentShoot += 1
if (self.currentShoot >= len(self.img)):
self.currentShoot = 0
# отобразить конкретный кадр (OK)
def DrawShoot(self,Shoot):
# i - y
# j - x
for i in range(len(self.img[Shoot])):
for j in range(len(self.img[Shoot][0])):
self.display.SetPixel(self.x+j,self.y+i,self.img[Shoot][i][j])
self.collisionShoot = Shoot
self.currentShoot = Shoot
self.width = len(self.img[Shoot][0])
self.height = len(self.img[Shoot])
# проверка попадания
def CheckCollision(self,x,y):
x = x - self.x
y = y - self.y
if (abs(x) < self.width and abs(y) < self.height):
if (self.img[self.collisionShoot][y][x] != " "):
return True
else:
return False
else:
return False
# обновить координаты
def updateXY(self,x,y):
self.x = x
self.y = y
class Player:
def __init__(self,playerSprite,x,y,display):
self.x = x
self.y = y
self.sprite = Sprite(playerSprite,x,y,display)
def move_x(self,value):
if (value==-1):
self.x-=1
if (value==1):
self.x+=1
self.sprite.updateXY(self.x,self.y)
def move_y(self,value):
if (value==-1):
self.y-=1
if (value==1):
self.y+=1
self.sprite.updateXY(self.x,self.y)
class bullet:
def __init__(self,bulletSprite,x,y,display,vx,vy,dist):
self.x0 = x
self.y0 = y
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.dist = dist
self.sprite = Sprite(bulletSprite,x,y,display)
def move(self):
self.x = self.x + self.vx
self.y = self.y + self.vy
self.sprite.updateXY(self.x,self.y)
def check(self):
vector = pow(pow(self.x-self.x0,2)+pow(self.y-self.y0,2),0.5)
if (vector < self.dist):
return True
else:
return False
class Game:
def __init__(self):
self.A = [[[" ","*"," "],["*","*","*"],[" ","*"," "],["*"," ","*"]],
[["*"," ","*"," "],[" ","*","*","*"],["*"," ","*"," "]],
[["*"," ","*"],[" ","*"," "],["*","*","*"],[" ","*"," "]],
[[" ","*"," ","*"],["*","*","*"," "],[" ","*"," ","*"]]]
self.plShootNum = 0
self.pl2ShootNum = 0
self.bullets = []
self.bulletsLen = 0
self.bulletsIndexes = []
self.key = "b'm'"
self.GameScreen = PixelDisplay(100,70)
self.player = Player(self.A,10,25,self.GameScreen)
self.player2 = Player(self.A,25,25,self.GameScreen)
self.player2Action = True
self.playerScore = 0
self.player2Score = 0
# рендер игры
def gameLoop(self):
while (True):
# действия игрока2
vect = [(self.player2.x),(self.player2.y)]
vect1 = [-15+self.player.x,0+self.player.y]
vect2 = [15+self.player.x,0+self.player.y]
vect3 = [0+self.player.x,15+self.player.y]
vect4 = [0+self.player.x,-15+self.player.y]
v = [vect1,vect2,vect3,vect4]
dist = [0,0,0,0]
index = 0
while (self.player2Action):
for i in range(4):
dist[i] = pow(pow(v[i][0]-vect[0],2)+pow(v[i][1]-vect[1],2),0.5)
min_d = dist[0]
for i in range(4):
if dist[i] < min_d:
index = i
target = v[index]
if (vect[0]<target[0]):
self.pl2ShootNum = 1
self.player2.move_x(1)
self.player2Action = False
continue
if (vect[0]>target[0]):
self.pl2ShootNum = 3
self.player2.move_x(-1)
self.player2Action = False
continue
if (vect[1]<target[1]):
self.pl2ShootNum = 2
self.player2.move_y(1)
self.player2Action = False
continue
if (vect[1]>target[1]):
self.pl2ShootNum = 0
self.player2.move_y(-1)
self.player2Action = False
continue
if index == 1:
self.bullets.append(bullet([[['"']]],self.player2.x-5,self.player2.y+1,self.GameScreen,-1,0,20))
self.bulletsLen = len(self.bullets)
self.pl2ShootNum = 3
if index == 0:
self.bullets.append(bullet([[['"']]],self.player2.x+5,self.player2.y+1,self.GameScreen,1,0,20))
self.bulletsLen = len(self.bullets)
self.pl2ShootNum = 1
if index == 2:
self.bullets.append(bullet([[['"']]],self.player2.x+1,self.player2.y-4,self.GameScreen,0,-1,20))
self.bulletsLen = len(self.bullets)
self.pl2ShootNum = 0
if index == 3:
self.bullets.append(bullet([[['"']]],self.player2.x+1,self.player2.y+4,self.GameScreen,0,1,20))
self.bulletsLen = len(self.bullets)
self.pl2ShootNum = 2
self.player2Action = False
self.player2Action = True
# обработка кнопок
if (str(self.key)[2]=='a'):
self.plShootNum = 3
self.player.move_x(-1)
self.key = "b'm'"
if (str(self.key)[2]=='d'):
self.plShootNum = 1
self.player.move_x(1)
self.key = "b'm'"
if (str(self.key)[2]=='w'):
self.plShootNum = 0
self.player.move_y(-1)
self.key = "b'm'"
if (str(self.key)[2]=='s'):
self.plShootNum = 2
self.player.move_y(1)
self.key = "b'm'"
# пули
if (str(self.key)[2]=='f'):
if self.plShootNum == 0:
self.bullets.append(bullet([[['"']]],self.player.x+1,self.player.y-4,self.GameScreen,0,-1,20))
elif self.plShootNum == 2:
self.bullets.append(bullet([[['"']]],self.player.x+1,self.player.y+4,self.GameScreen,0,1,20))
elif self.plShootNum == 1:
vect = [1,0]
self.bullets.append(bullet([[['"']]],self.player.x+5,self.player.y+1,self.GameScreen,1,0,20))
elif self.plShootNum == 3:
vect = [-1,0]
self.bullets.append(bullet([[['"']]],self.player.x-5,self.player.y+1,self.GameScreen,-1,0,20))
self.bulletsLen = len(self.bullets)
self.key = "b'm'"
# код
# пули
for i in range(self.bulletsLen):
if (self.bullets[i].check()):
self.bullets[i].move()
else:
self.bulletsIndexes.append(i)
for i in self.bulletsIndexes:
self.bullets.pop(i)
self.bulletsLen -= 1
self.bulletsIndexes = []
# пули конец
self.GameScreen.Clear()
self.player.sprite.DrawShoot(self.plShootNum)
self.player2.sprite.DrawShoot(self.pl2ShootNum)
for i in range(self.bulletsLen):
self.bullets[i].sprite.DrawShoot(0)
self.GameScreen.ShowDisplay()
# попадание по игроку
for b in self.bullets:
if (self.player.sprite.CheckCollision(b.x,b.y)):
self.player = Player(self.A,random.randint(1,99),random.randint(1,69),self.GameScreen)
self.bullets = []
self.bulletsLen = 0
self.player2Score += 1
if (self.player2.sprite.CheckCollision(b.x,b.y)):
self.player2 = Player(self.A,random.randint(1,99),random.randint(1,69),self.GameScreen)
self.bullets = []
self.bulletsLen = 0
self.playerScore += 1
print("Player Score:",self.playerScore,"Computer Score:",self.player2Score)
# считка клавиш
def getKey(self):
while (True):
self.key = msvcrt.getch()
game = Game()
thread1 = Thread(target=game.gameLoop)
thread2 = Thread(target=game.getKey)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
ZnJvbSB0aHJlYWRpbmcgaW1wb3J0IFRocmVhZCAjINC/0L7RgtC+0LrQuAppbXBvcnQgbXN2Y3J0ICMg0LHQuNCx0LvQuNC+0YLQtdC60LAg0L3QtdC/0YDQuNGA0YvQstC90L4g0YHRh9C40YLRi9Cy0LDRjtGJ0LDRjyDQvdCw0LbQsNGC0LjQtSDQutC70LDQstC40Ygg0LIg0LrQvtC90YHQvtC70LgKI2EgPSBtc3ZjcnQuZ2V0Y2goKSAjINC/0L7Qu9GD0YfQsNC10YIg0LrQu9Cw0LLQuNGI0YMg0YTQvtGA0LzQsNGCOiBiJ2EnIC0gYQojc3RyKGEpWzJdCmltcG9ydCByYW5kb20KaW1wb3J0IG9zCmltcG9ydCBtYXRoCmltcG9ydCB0aW1lCmltcG9ydCBjb3B5CgojIFBpeGVsRGlzcGxheSh3aWR0aCxoZWlnaHQpINC/0L7Qu9C1INC00LvRjyDRgNC40YHQvtCy0LDQvdC40Y8KIyBDbGVhcigpINC+0YfQuNGB0YLQuNGC0YwKIyBTaG93RGlzcGxheSgpINC+0LHQvdC+0LLQuNGC0YwKIyBTZXRQaXhlbCh4LHkpINC90LDRgNC40YHQvtCy0LDRgtGMINC/0LjQutGB0LXQu9GMCiMgQ2xlYXJQaXhlbCh4LHkpINGD0LTQsNC70LjRgtGMINC/0LjQutGB0LXQu9GMCiMgRHJhd0xpbmUoeDEseTEseDIseTIpINC90LDRgNC40YHQvtCy0LDRgtGMINC70LjQvdC40Y4KIyBEcmF3Q2lyY3VsKHgseSxyKSDQvdCw0YDQuNGB0L7QstCw0YLRjCDQvtC60YDRg9C20L3QvtGB0YLRjAojIERyYXdQb2xpZ29uKHgxLHkxLHoxLHgyLHkyLHoyLHgzLHkzLHozKSDQvdCw0YDQuNGB0L7QstCw0YLRjCDRgtGA0LXRhdC80LXRgNC90YvQuSDQv9C+0LvQuNCz0L7QvQoKY2xhc3MgUGl4ZWxEaXNwbGF5OgogICAgaCA9IDEwICMgM2QgcHJvamVjdGlvbiB2YWx1ZQogICAgZGVmIF9faW5pdF9fKHNlbGYsd2lkdGgsaGVpZ2h0KToKICAgICAgICBzZWxmLndpZHRoID0gd2lkdGgKICAgICAgICBzZWxmLmhlaWdodCA9IGhlaWdodAogICAgICAgIHNlbGYuUGl4ZWxBcnJheSA9IHNlbGYuQ3JlYXRlRGlzcGxheSgpCiAgICAgICAgc2VsZi5jbHMgPSBjb3B5LmRlZXBjb3B5KHNlbGYuUGl4ZWxBcnJheSkKICAgIGRlZiBDcmVhdGVEaXNwbGF5KHNlbGYpOgogICAgICAgIFkgPSBbXQogICAgICAgIFggPSBbXQogICAgICAgIGZvciBpIGluIHJhbmdlKHNlbGYuaGVpZ2h0KToKICAgICAgICAgICAgZm9yIGogaW4gcmFuZ2Uoc2VsZi53aWR0aCk6CiAgICAgICAgICAgICAgICBYLmFwcGVuZCgiICIpCiAgICAgICAgICAgIFkuYXBwZW5kKFgpCiAgICAgICAgICAgIFggPSBbXQogICAgICAgIHJldHVybiBZCiAgICBkZWYgQ2xlYXIoc2VsZik6CiAgICAgICAgc2VsZi5QaXhlbEFycmF5ID0gW10KICAgICAgICBzZWxmLlBpeGVsQXJyYXkgPSBjb3B5LmRlZXBjb3B5KHNlbGYuY2xzKQogICAgICAgIHRpbWUuc2xlZXAoMC4wMDQpCiAgICAgICAgb3Muc3lzdGVtKCJjbHMiKQogICAgICAgIHRpbWUuc2xlZXAoMC4wMDQpCiAgICBkZWYgU2hvd0Rpc3BsYXkoc2VsZik6CiAgICAgICAgc3QgPSAiIgogICAgICAgIGZvciBpIGluIHJhbmdlKHNlbGYuaGVpZ2h0KToKICAgICAgICAgICAgZm9yIGogaW4gcmFuZ2Uoc2VsZi53aWR0aCk6CiAgICAgICAgICAgICAgICBzdCArPSBzZWxmLlBpeGVsQXJyYXlbaV1bal0KICAgICAgICAgICAgcHJpbnQoc3QpCiAgICAgICAgICAgIHN0ID0gIiIKICAgIGRlZiBTZXRQaXhlbChzZWxmLHgseSxwaXg9IioiKToKICAgICAgICBpZiAoeSA8IHNlbGYuaGVpZ2h0IGFuZCB4IDxzZWxmLndpZHRoIGFuZCB5ID49IDAgYW5kIHg+PSAwKToKICAgICAgICAgICAgc2VsZi5QaXhlbEFycmF5W3ldW3hdID0gcGl4CiAgICBkZWYgQ2xlYXJQaXhlbCAoc2VsZix4LHkpOgogICAgICAgIGlmICh5IDwgc2VsZi5oZWlnaHQgYW5kIHggPHNlbGYud2lkdGggYW5kIHkgPj0gMCBhbmQgeD49IDApOgogICAgICAgICAgICBzZWxmLlBpeGVsQXJyYXlbeV1beF0gPSAiICIKICAgIGRlZiBEcmF3TGluZShzZWxmLHgxLHkxLHgyLHkyLHBpeD0iKiIpOgogICAgICAgIGlmIChhYnMoeDIteDEpPj1hYnMoeTIteTEpKToKICAgICAgICAgICAgaWYgKHgyICE9IHgxKToKICAgICAgICAgICAgICAgIHRhbiA9ICh5Mi15MSkvKHgyLXgxKQogICAgICAgICAgICBpZiAoeDI+eDEpOgogICAgICAgICAgICAgICAgZm9yIHggaW4gcmFuZ2UoeDEseDIpOgogICAgICAgICAgICAgICAgICAgIHkgPSB0YW4qKHgteDEpK3kxCiAgICAgICAgICAgICAgICAgICAgc2VsZi5TZXRQaXhlbCh4LGludCh5KSxwaXgpCiAgICAgICAgICAgIGVsaWYgKHgxPngyKToKICAgICAgICAgICAgICAgIGZvciB4IGluIHJhbmdlKHgyLHgxKToKICAgICAgICAgICAgICAgICAgICB5ID0gdGFuKih4LXgxKSt5MQogICAgICAgICAgICAgICAgICAgIHNlbGYuU2V0UGl4ZWwoeCxpbnQoeSkscGl4KQogICAgICAgICAgICBlbGlmICh4MSA9PSB4Mik6CiAgICAgICAgICAgICAgICBpZiAoeTIgPiB5MSk6CiAgICAgICAgICAgICAgICAgICAgZm9yIHkgaW4gcmFuZ2UoeTEseTIpOgogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLlNldFBpeGVsKHgxLHkscGl4KQogICAgICAgICAgICAgICAgZWxpZiAoeTEgPiB5Mik6CiAgICAgICAgICAgICAgICAgICAgZm9yIHkgaW4gcmFuZ2UoeTIseTEpOgogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLlNldFBpeGVsKHgxLHkscGl4KQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBzZWxmLlNldFBpeGVsKHgxLHkxLHBpeCkKICAgICAgICBpZiAoYWJzKHgyLXgxKTwgYWJzKHkyLXkxKSk6CiAgICAgICAgICAgIGlmICh5MiAhPSB5MSk6CiAgICAgICAgICAgICAgICB0YW4gPSAoeDIteDEpLyh5Mi15MSkKICAgICAgICAgICAgaWYgKHkyPnkxKToKICAgICAgICAgICAgICAgIGZvciB5IGluIHJhbmdlKHkxLHkyKToKICAgICAgICAgICAgICAgICAgICB4ID0gdGFuKih5LXkxKSt4MQogICAgICAgICAgICAgICAgICAgIHNlbGYuU2V0UGl4ZWwoaW50KHgpLHkscGl4KQogICAgICAgICAgICBlbGlmICh5MT55Mik6CiAgICAgICAgICAgICAgICBmb3IgeSBpbiByYW5nZSh5Mix5MSk6CiAgICAgICAgICAgICAgICAgICAgeCA9IHRhbiooeS15MSkreDEKICAgICAgICAgICAgICAgICAgICBzZWxmLlNldFBpeGVsKGludCh4KSx5LHBpeCkKICAgICAgICAgICAgZWxpZiAoeTEgPT0geTIpOgogICAgICAgICAgICAgICAgaWYgKHgyID4geDEpOgogICAgICAgICAgICAgICAgICAgIGZvciB4IGluIHJhbmdlKHgxLHgyKToKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5TZXRQaXhlbCh4LHkxLHBpeCkKICAgICAgICAgICAgICAgIGVsaWYgKHgxID4geDIpOgogICAgICAgICAgICAgICAgICAgIGZvciB4IGluIHJhbmdlKHgyLHgxKToKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5TZXRQaXhlbCh4LHkxLHBpeCkKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5TZXRQaXhlbCh4MSx5MSxwaXgpCiAgICBkZWYgRHJhd0NpcmN1bChzZWxmLHgseSxyLHBpeD0iKiIpOgogICAgICAgIGZvciBpIGluIHJhbmdlKDM2MCk6CiAgICAgICAgICAgIF94ID0gaW50KHggKyByKm1hdGguY29zKGkqMy4xNDE1OTI2LzE4MCkpCiAgICAgICAgICAgIF95ID0gaW50KHkgKyByKm1hdGguc2luKGkqMy4xNDE1OTI2LzE4MCkpCiAgICAgICAgICAgIHNlbGYuU2V0UGl4ZWwoX3gsX3kscGl4KQogICAgZGVmIFBpeGVsM2Qoc2VsZix4LHkseik6CiAgICAgICAgaWYgKHogPiAwKToKICAgICAgICAgICAgX3ggPSBpbnQoKHgveikqc2VsZi5oKQogICAgICAgICAgICBfeSA9IGludCgoeS96KSpzZWxmLmgpCiAgICAgICAgICAgIHJldHVybiBbX3gsIF95XQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHByaW50KCJlcnJvciIpCiAgICBkZWYgRHJhd1BvbGlnb24oc2VsZix4MSx5MSx6MSx4Mix5Mix6Mix4Myx5Myx6MyxwaXg9IioiKToKICAgICAgICB2MSA9IFt4Mi14MSx5Mi15MSx6Mi16MV0KICAgICAgICB2MiA9IFt4My14Mix5My15Mix6My16Ml0KICAgICAgICBuID0gW3YxWzFdKnYyWzJdLXYxWzJdKnYyWzFdLHYxWzJdKnYyWzBdLXYxWzBdKnYyWzJdLHYxWzBdKnYyWzFdLXYxWzFdKnYyWzBdXQogICAgICAgIGlmIChuWzJdKjE8IDApOgogICAgICAgICAgICBQMSA9IHNlbGYuUGl4ZWwzZCh4MSx5MSx6MSkKICAgICAgICAgICAgUDIgPSBzZWxmLlBpeGVsM2QoeDIseTIsejIpCiAgICAgICAgICAgIFAzID0gc2VsZi5QaXhlbDNkKHgzLHkzLHozKQogICAgICAgICAgICB4MSx5MSA9IFAxWzBdLFAxWzFdCiAgICAgICAgICAgIHgyLHkyID0gUDJbMF0sUDJbMV0KICAgICAgICAgICAgeDMseTMgPSBQM1swXSxQM1sxXQogICAgICAgICAgICBzZWxmLkRyYXdMaW5lKHgxLHkxLHgyLHkyLHBpeCkKICAgICAgICAgICAgc2VsZi5EcmF3TGluZSh4Mix5Mix4Myx5MyxwaXgpCiAgICAgICAgICAgIHNlbGYuRHJhd0xpbmUoeDMseTMseDEseTEscGl4KQoKY2xhc3MgU3ByaXRlOgogICAgIyBmb3JtYXQKICAgICMgQSA9IFsKICAgICNbWyIgIiwiKiIsIiAiXSxbIiAiLCIqIiwiICJdLFsiICIsIioiLCIgIl1dLAogICAgI1tbIiAiLCIqIiwiICJdLFsiKiIsIioiLCIqIl0sWyIgIiwiKiIsIiAiXV0KICAgICNdCiAgICBkZWYgX19pbml0X18oc2VsZixBLHgseSxkaXNwbGF5KToKICAgICAgICBzZWxmLnggPSB4CiAgICAgICAgc2VsZi55ID0geQogICAgICAgIHNlbGYuZGlzcGxheSA9IGRpc3BsYXkKICAgICAgICBzZWxmLmltZyA9IEEKICAgICAgICBzZWxmLndpZHRoID0gbGVuKHNlbGYuaW1nWzBdWzBdKQogICAgICAgIHNlbGYuaGVpZ2h0ID0gbGVuKHNlbGYuaW1nWzBdKQogICAgICAgIHNlbGYuc2hvb3QgPSBsZW4oc2VsZi5pbWcpCiAgICAgICAgc2VsZi5jdXJyZW50U2hvb3QgPSAwCiAgICAgICAgc2VsZi5jb2xsaXNpb25TaG9vdCA9IHNlbGYuY3VycmVudFNob290CiAgICAjINC+0YLQvtCx0YDQsNC30LjRgtGMINGB0L/RgNCw0LnRgiDQuCDQv9C10YDQtdC60LvRjtGH0LjRgtGMINC90LAg0L3QvtCy0YvQuSDQutCw0LTRgAogICAgZGVmIERyYXcoc2VsZik6CiAgICAgICAgIyBpIC0geQogICAgICAgICMgaiAtIHgKICAgICAgICBmb3IgaSBpbiByYW5nZShsZW4oc2VsZi5pbWdbc2VsZi5jdXJyZW50U2hvb3RdKSk6CiAgICAgICAgICAgIGZvciBqIGluIHJhbmdlKGxlbihzZWxmLmltZ1tzZWxmLmN1cnJlbnRTaG9vdF1bMF0pKToKICAgICAgICAgICAgICAgIHNlbGYuZGlzcGxheS5TZXRQaXhlbChzZWxmLngraixzZWxmLnkraSxzZWxmLmltZ1tzZWxmLmN1cnJlbnRTaG9vdF1baV1bal0pCiAgICAgICAgc2VsZi5jb2xsaXNpb25TaG9vdCA9IHNlbGYuY3VycmVudFNob290CiAgICAgICAgc2VsZi5jdXJyZW50U2hvb3QgKz0gMQogICAgICAgIGlmIChzZWxmLmN1cnJlbnRTaG9vdCA+PSBsZW4oc2VsZi5pbWcpKToKICAgICAgICAgICAgc2VsZi5jdXJyZW50U2hvb3QgPSAwCiAgICAjINC+0YLQvtCx0YDQsNC30LjRgtGMINC60L7QvdC60YDQtdGC0L3Ri9C5INC60LDQtNGAIChPSykKICAgIGRlZiBEcmF3U2hvb3Qoc2VsZixTaG9vdCk6CiAgICAgICAgIyBpIC0geQogICAgICAgICMgaiAtIHgKICAgICAgICBmb3IgaSBpbiByYW5nZShsZW4oc2VsZi5pbWdbU2hvb3RdKSk6CiAgICAgICAgICAgIGZvciBqIGluIHJhbmdlKGxlbihzZWxmLmltZ1tTaG9vdF1bMF0pKToKICAgICAgICAgICAgICAgIHNlbGYuZGlzcGxheS5TZXRQaXhlbChzZWxmLngraixzZWxmLnkraSxzZWxmLmltZ1tTaG9vdF1baV1bal0pCiAgICAgICAgc2VsZi5jb2xsaXNpb25TaG9vdCA9IFNob290CiAgICAgICAgc2VsZi5jdXJyZW50U2hvb3QgPSBTaG9vdAogICAgICAgIHNlbGYud2lkdGggPSBsZW4oc2VsZi5pbWdbU2hvb3RdWzBdKQogICAgICAgIHNlbGYuaGVpZ2h0ID0gbGVuKHNlbGYuaW1nW1Nob290XSkKICAgICMg0L/RgNC+0LLQtdGA0LrQsCDQv9C+0L/QsNC00LDQvdC40Y8KICAgIGRlZiBDaGVja0NvbGxpc2lvbihzZWxmLHgseSk6CiAgICAgICAgeCA9IHggLSBzZWxmLngKICAgICAgICB5ID0geSAtIHNlbGYueQogICAgICAgIGlmIChhYnMoeCkgPCBzZWxmLndpZHRoIGFuZCBhYnMoeSkgPCBzZWxmLmhlaWdodCk6CiAgICAgICAgICAgIGlmIChzZWxmLmltZ1tzZWxmLmNvbGxpc2lvblNob290XVt5XVt4XSAhPSAiICIpOgogICAgICAgICAgICAgICAgcmV0dXJuIFRydWUKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIHJldHVybiBGYWxzZQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJldHVybiBGYWxzZSAKICAgICMg0L7QsdC90L7QstC40YLRjCDQutC+0L7RgNC00LjQvdCw0YLRiwogICAgZGVmIHVwZGF0ZVhZKHNlbGYseCx5KToKICAgICAgICBzZWxmLnggPSB4CiAgICAgICAgc2VsZi55ID0geQoKY2xhc3MgUGxheWVyOgogICAgZGVmIF9faW5pdF9fKHNlbGYscGxheWVyU3ByaXRlLHgseSxkaXNwbGF5KToKICAgICAgICBzZWxmLnggPSB4CiAgICAgICAgc2VsZi55ID0geQogICAgICAgIHNlbGYuc3ByaXRlID0gU3ByaXRlKHBsYXllclNwcml0ZSx4LHksZGlzcGxheSkKICAgIGRlZiBtb3ZlX3goc2VsZix2YWx1ZSk6CiAgICAgICAgaWYgKHZhbHVlPT0tMSk6CiAgICAgICAgICAgIHNlbGYueC09MQogICAgICAgIGlmICh2YWx1ZT09MSk6CiAgICAgICAgICAgIHNlbGYueCs9MQogICAgICAgIHNlbGYuc3ByaXRlLnVwZGF0ZVhZKHNlbGYueCxzZWxmLnkpCiAgICBkZWYgbW92ZV95KHNlbGYsdmFsdWUpOgogICAgICAgIGlmICh2YWx1ZT09LTEpOgogICAgICAgICAgICBzZWxmLnktPTEKICAgICAgICBpZiAodmFsdWU9PTEpOgogICAgICAgICAgICBzZWxmLnkrPTEKICAgICAgICBzZWxmLnNwcml0ZS51cGRhdGVYWShzZWxmLngsc2VsZi55KQoKY2xhc3MgYnVsbGV0OgogICAgZGVmIF9faW5pdF9fKHNlbGYsYnVsbGV0U3ByaXRlLHgseSxkaXNwbGF5LHZ4LHZ5LGRpc3QpOgogICAgICAgIHNlbGYueDAgPSB4CiAgICAgICAgc2VsZi55MCA9IHkKICAgICAgICBzZWxmLnggPSB4CiAgICAgICAgc2VsZi55ID0geQogICAgICAgIHNlbGYudnggPSB2eAogICAgICAgIHNlbGYudnkgPSB2eQogICAgICAgIHNlbGYuZGlzdCA9IGRpc3QKICAgICAgICBzZWxmLnNwcml0ZSA9IFNwcml0ZShidWxsZXRTcHJpdGUseCx5LGRpc3BsYXkpCiAgICBkZWYgbW92ZShzZWxmKToKICAgICAgICBzZWxmLnggPSBzZWxmLnggKyBzZWxmLnZ4CiAgICAgICAgc2VsZi55ID0gc2VsZi55ICsgc2VsZi52eQogICAgICAgIHNlbGYuc3ByaXRlLnVwZGF0ZVhZKHNlbGYueCxzZWxmLnkpCiAgICBkZWYgY2hlY2soc2VsZik6CiAgICAgICAgdmVjdG9yID0gcG93KHBvdyhzZWxmLngtc2VsZi54MCwyKStwb3coc2VsZi55LXNlbGYueTAsMiksMC41KQogICAgICAgIGlmICh2ZWN0b3IgPCBzZWxmLmRpc3QpOgogICAgICAgICAgICByZXR1cm4gVHJ1ZQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJldHVybiBGYWxzZQoKY2xhc3MgR2FtZToKICAgIGRlZiBfX2luaXRfXyhzZWxmKToKICAgICAgICBzZWxmLkEgPSBbW1siICIsIioiLCIgIl0sWyIqIiwiKiIsIioiXSxbIiAiLCIqIiwiICJdLFsiKiIsIiAiLCIqIl1dLAogICAgICAgICAgICAgW1siKiIsIiAiLCIqIiwiICJdLFsiICIsIioiLCIqIiwiKiJdLFsiKiIsIiAiLCIqIiwiICJdXSwKICAgICAgICAgICAgIFtbIioiLCIgIiwiKiJdLFsiICIsIioiLCIgIl0sWyIqIiwiKiIsIioiXSxbIiAiLCIqIiwiICJdXSwKICAgICAgICAgICAgIFtbIiAiLCIqIiwiICIsIioiXSxbIioiLCIqIiwiKiIsIiAiXSxbIiAiLCIqIiwiICIsIioiXV1dCiAgICAgICAgc2VsZi5wbFNob290TnVtID0gMAogICAgICAgIHNlbGYucGwyU2hvb3ROdW0gPSAwCiAgICAgICAgc2VsZi5idWxsZXRzID0gW10KICAgICAgICBzZWxmLmJ1bGxldHNMZW4gPSAwCiAgICAgICAgc2VsZi5idWxsZXRzSW5kZXhlcyA9IFtdCiAgICAgICAgc2VsZi5rZXkgPSAiYidtJyIKICAgICAgICBzZWxmLkdhbWVTY3JlZW4gPSBQaXhlbERpc3BsYXkoMTAwLDcwKQogICAgICAgIHNlbGYucGxheWVyID0gUGxheWVyKHNlbGYuQSwxMCwyNSxzZWxmLkdhbWVTY3JlZW4pCiAgICAgICAgc2VsZi5wbGF5ZXIyID0gUGxheWVyKHNlbGYuQSwyNSwyNSxzZWxmLkdhbWVTY3JlZW4pCiAgICAgICAgc2VsZi5wbGF5ZXIyQWN0aW9uID0gVHJ1ZQogICAgICAgIHNlbGYucGxheWVyU2NvcmUgPSAwCiAgICAgICAgc2VsZi5wbGF5ZXIyU2NvcmUgPSAwCiAgICAjINGA0LXQvdC00LXRgCDQuNCz0YDRiwogICAgZGVmIGdhbWVMb29wKHNlbGYpOgogICAgICAgIHdoaWxlIChUcnVlKToKICAgICAgICAgICAgIyDQtNC10LnRgdGC0LLQuNGPINC40LPRgNC+0LrQsDIKICAgICAgICAgICAgdmVjdCA9IFsoc2VsZi5wbGF5ZXIyLngpLChzZWxmLnBsYXllcjIueSldCiAgICAgICAgICAgIHZlY3QxID0gWy0xNStzZWxmLnBsYXllci54LDArc2VsZi5wbGF5ZXIueV0KICAgICAgICAgICAgdmVjdDIgPSBbMTUrc2VsZi5wbGF5ZXIueCwwK3NlbGYucGxheWVyLnldCiAgICAgICAgICAgIHZlY3QzID0gWzArc2VsZi5wbGF5ZXIueCwxNStzZWxmLnBsYXllci55XQogICAgICAgICAgICB2ZWN0NCA9IFswK3NlbGYucGxheWVyLngsLTE1K3NlbGYucGxheWVyLnldCiAgICAgICAgICAgIHYgPSBbdmVjdDEsdmVjdDIsdmVjdDMsdmVjdDRdCiAgICAgICAgICAgIGRpc3QgPSBbMCwwLDAsMF0KICAgICAgICAgICAgaW5kZXggPSAwCiAgICAgICAgICAgIHdoaWxlIChzZWxmLnBsYXllcjJBY3Rpb24pOgogICAgICAgICAgICAgICAgZm9yIGkgaW4gcmFuZ2UoNCk6CiAgICAgICAgICAgICAgICAgICAgZGlzdFtpXSA9IHBvdyhwb3codltpXVswXS12ZWN0WzBdLDIpK3Bvdyh2W2ldWzFdLXZlY3RbMV0sMiksMC41KQogICAgICAgICAgICAgICAgbWluX2QgPSBkaXN0WzBdCiAgICAgICAgICAgICAgICBmb3IgaSBpbiByYW5nZSg0KToKICAgICAgICAgICAgICAgICAgICBpZiBkaXN0W2ldIDwgbWluX2Q6CiAgICAgICAgICAgICAgICAgICAgICAgIGluZGV4ID0gaQogICAgICAgICAgICAgICAgdGFyZ2V0ID0gdltpbmRleF0KICAgICAgICAgICAgICAgIGlmICh2ZWN0WzBdPHRhcmdldFswXSk6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbDJTaG9vdE51bSA9IDEKICAgICAgICAgICAgICAgICAgICBzZWxmLnBsYXllcjIubW92ZV94KDEpCiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXIyQWN0aW9uID0gRmFsc2UKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICAgICAgaWYgKHZlY3RbMF0+dGFyZ2V0WzBdKToKICAgICAgICAgICAgICAgICAgICBzZWxmLnBsMlNob290TnVtID0gMwogICAgICAgICAgICAgICAgICAgIHNlbGYucGxheWVyMi5tb3ZlX3goLTEpCiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXIyQWN0aW9uID0gRmFsc2UKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICAgICAgaWYgKHZlY3RbMV08dGFyZ2V0WzFdKToKICAgICAgICAgICAgICAgICAgICBzZWxmLnBsMlNob290TnVtID0gMgogICAgICAgICAgICAgICAgICAgIHNlbGYucGxheWVyMi5tb3ZlX3koMSkKICAgICAgICAgICAgICAgICAgICBzZWxmLnBsYXllcjJBY3Rpb24gPSBGYWxzZQogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICBpZiAodmVjdFsxXT50YXJnZXRbMV0pOgogICAgICAgICAgICAgICAgICAgIHNlbGYucGwyU2hvb3ROdW0gPSAwCiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXIyLm1vdmVfeSgtMSkKICAgICAgICAgICAgICAgICAgICBzZWxmLnBsYXllcjJBY3Rpb24gPSBGYWxzZQogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICBpZiBpbmRleCA9PSAxOgogICAgICAgICAgICAgICAgICAgIHNlbGYuYnVsbGV0cy5hcHBlbmQoYnVsbGV0KFtbWyciJ11dXSxzZWxmLnBsYXllcjIueC01LHNlbGYucGxheWVyMi55KzEsc2VsZi5HYW1lU2NyZWVuLC0xLDAsMjApKQogICAgICAgICAgICAgICAgICAgIHNlbGYuYnVsbGV0c0xlbiA9IGxlbihzZWxmLmJ1bGxldHMpCiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbDJTaG9vdE51bSA9IDMKICAgICAgICAgICAgICAgIGlmIGluZGV4ID09IDA6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5idWxsZXRzLmFwcGVuZChidWxsZXQoW1tbJyInXV1dLHNlbGYucGxheWVyMi54KzUsc2VsZi5wbGF5ZXIyLnkrMSxzZWxmLkdhbWVTY3JlZW4sMSwwLDIwKSkKICAgICAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHNMZW4gPSBsZW4oc2VsZi5idWxsZXRzKQogICAgICAgICAgICAgICAgICAgIHNlbGYucGwyU2hvb3ROdW0gPSAxCiAgICAgICAgICAgICAgICBpZiBpbmRleCA9PSAyOgogICAgICAgICAgICAgICAgICAgIHNlbGYuYnVsbGV0cy5hcHBlbmQoYnVsbGV0KFtbWyciJ11dXSxzZWxmLnBsYXllcjIueCsxLHNlbGYucGxheWVyMi55LTQsc2VsZi5HYW1lU2NyZWVuLDAsLTEsMjApKQogICAgICAgICAgICAgICAgICAgIHNlbGYuYnVsbGV0c0xlbiA9IGxlbihzZWxmLmJ1bGxldHMpCiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbDJTaG9vdE51bSA9IDAKICAgICAgICAgICAgICAgIGlmIGluZGV4ID09IDM6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5idWxsZXRzLmFwcGVuZChidWxsZXQoW1tbJyInXV1dLHNlbGYucGxheWVyMi54KzEsc2VsZi5wbGF5ZXIyLnkrNCxzZWxmLkdhbWVTY3JlZW4sMCwxLDIwKSkKICAgICAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHNMZW4gPSBsZW4oc2VsZi5idWxsZXRzKQogICAgICAgICAgICAgICAgICAgIHNlbGYucGwyU2hvb3ROdW0gPSAyCiAgICAgICAgICAgICAgICBzZWxmLnBsYXllcjJBY3Rpb24gPSBGYWxzZQogICAgICAgICAgICBzZWxmLnBsYXllcjJBY3Rpb24gPSBUcnVlCiAgICAgICAgICAgICMg0L7QsdGA0LDQsdC+0YLQutCwINC60L3QvtC/0L7QugogICAgICAgICAgICBpZiAoc3RyKHNlbGYua2V5KVsyXT09J2EnKToKICAgICAgICAgICAgICAgIHNlbGYucGxTaG9vdE51bSA9IDMKICAgICAgICAgICAgICAgIHNlbGYucGxheWVyLm1vdmVfeCgtMSkKICAgICAgICAgICAgICAgIHNlbGYua2V5ID0gImInbSciCiAgICAgICAgICAgIGlmIChzdHIoc2VsZi5rZXkpWzJdPT0nZCcpOgogICAgICAgICAgICAgICAgc2VsZi5wbFNob290TnVtID0gMQogICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXIubW92ZV94KDEpCiAgICAgICAgICAgICAgICBzZWxmLmtleSA9ICJiJ20nIgogICAgICAgICAgICBpZiAoc3RyKHNlbGYua2V5KVsyXT09J3cnKToKICAgICAgICAgICAgICAgIHNlbGYucGxTaG9vdE51bSA9IDAKICAgICAgICAgICAgICAgIHNlbGYucGxheWVyLm1vdmVfeSgtMSkKICAgICAgICAgICAgICAgIHNlbGYua2V5ID0gImInbSciCiAgICAgICAgICAgIGlmIChzdHIoc2VsZi5rZXkpWzJdPT0ncycpOgogICAgICAgICAgICAgICAgc2VsZi5wbFNob290TnVtID0gMgogICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXIubW92ZV95KDEpCiAgICAgICAgICAgICAgICBzZWxmLmtleSA9ICJiJ20nIgogICAgICAgICAgICAjINC/0YPQu9C4CiAgICAgICAgICAgIGlmIChzdHIoc2VsZi5rZXkpWzJdPT0nZicpOgogICAgICAgICAgICAgICAgaWYgc2VsZi5wbFNob290TnVtID09IDA6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5idWxsZXRzLmFwcGVuZChidWxsZXQoW1tbJyInXV1dLHNlbGYucGxheWVyLngrMSxzZWxmLnBsYXllci55LTQsc2VsZi5HYW1lU2NyZWVuLDAsLTEsMjApKQogICAgICAgICAgICAgICAgZWxpZiBzZWxmLnBsU2hvb3ROdW0gPT0gMjoKICAgICAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHMuYXBwZW5kKGJ1bGxldChbW1snIiddXV0sc2VsZi5wbGF5ZXIueCsxLHNlbGYucGxheWVyLnkrNCxzZWxmLkdhbWVTY3JlZW4sMCwxLDIwKSkKICAgICAgICAgICAgICAgIGVsaWYgc2VsZi5wbFNob290TnVtID09IDE6CiAgICAgICAgICAgICAgICAgICAgdmVjdCA9IFsxLDBdCiAgICAgICAgICAgICAgICAgICAgc2VsZi5idWxsZXRzLmFwcGVuZChidWxsZXQoW1tbJyInXV1dLHNlbGYucGxheWVyLngrNSxzZWxmLnBsYXllci55KzEsc2VsZi5HYW1lU2NyZWVuLDEsMCwyMCkpCiAgICAgICAgICAgICAgICBlbGlmIHNlbGYucGxTaG9vdE51bSA9PSAzOgogICAgICAgICAgICAgICAgICAgIHZlY3QgPSBbLTEsMF0KICAgICAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHMuYXBwZW5kKGJ1bGxldChbW1snIiddXV0sc2VsZi5wbGF5ZXIueC01LHNlbGYucGxheWVyLnkrMSxzZWxmLkdhbWVTY3JlZW4sLTEsMCwyMCkpCiAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHNMZW4gPSBsZW4oc2VsZi5idWxsZXRzKQogICAgICAgICAgICAgICAgc2VsZi5rZXkgPSAiYidtJyIKICAgICAgICAgICAgIyDQutC+0LQKICAgICAgICAgICAgIyDQv9GD0LvQuAogICAgICAgICAgICBmb3IgaSBpbiByYW5nZShzZWxmLmJ1bGxldHNMZW4pOgogICAgICAgICAgICAgICAgaWYgKHNlbGYuYnVsbGV0c1tpXS5jaGVjaygpKToKICAgICAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHNbaV0ubW92ZSgpCiAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgIHNlbGYuYnVsbGV0c0luZGV4ZXMuYXBwZW5kKGkpCiAgICAgICAgICAgIGZvciBpIGluIHNlbGYuYnVsbGV0c0luZGV4ZXM6CiAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHMucG9wKGkpCiAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHNMZW4gLT0gMQogICAgICAgICAgICBzZWxmLmJ1bGxldHNJbmRleGVzID0gW10KICAgICAgICAgICAgIyDQv9GD0LvQuCDQutC+0L3QtdGGCiAgICAgICAgICAgIHNlbGYuR2FtZVNjcmVlbi5DbGVhcigpCiAgICAgICAgICAgIHNlbGYucGxheWVyLnNwcml0ZS5EcmF3U2hvb3Qoc2VsZi5wbFNob290TnVtKQogICAgICAgICAgICBzZWxmLnBsYXllcjIuc3ByaXRlLkRyYXdTaG9vdChzZWxmLnBsMlNob290TnVtKQogICAgICAgICAgICBmb3IgaSBpbiByYW5nZShzZWxmLmJ1bGxldHNMZW4pOgogICAgICAgICAgICAgICAgc2VsZi5idWxsZXRzW2ldLnNwcml0ZS5EcmF3U2hvb3QoMCkKICAgICAgICAgICAgc2VsZi5HYW1lU2NyZWVuLlNob3dEaXNwbGF5KCkKICAgICAgICAgICAgIyDQv9C+0L/QsNC00LDQvdC40LUg0L/QviDQuNCz0YDQvtC60YMKICAgICAgICAgICAgZm9yIGIgaW4gc2VsZi5idWxsZXRzOgogICAgICAgICAgICAgICAgaWYgKHNlbGYucGxheWVyLnNwcml0ZS5DaGVja0NvbGxpc2lvbihiLngsYi55KSk6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXIgPSBQbGF5ZXIoc2VsZi5BLHJhbmRvbS5yYW5kaW50KDEsOTkpLHJhbmRvbS5yYW5kaW50KDEsNjkpLHNlbGYuR2FtZVNjcmVlbikKICAgICAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHMgPSBbXQogICAgICAgICAgICAgICAgICAgIHNlbGYuYnVsbGV0c0xlbiA9IDAKICAgICAgICAgICAgICAgICAgICBzZWxmLnBsYXllcjJTY29yZSArPSAxCiAgICAgICAgICAgICAgICBpZiAoc2VsZi5wbGF5ZXIyLnNwcml0ZS5DaGVja0NvbGxpc2lvbihiLngsYi55KSk6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXIyID0gUGxheWVyKHNlbGYuQSxyYW5kb20ucmFuZGludCgxLDk5KSxyYW5kb20ucmFuZGludCgxLDY5KSxzZWxmLkdhbWVTY3JlZW4pCiAgICAgICAgICAgICAgICAgICAgc2VsZi5idWxsZXRzID0gW10KICAgICAgICAgICAgICAgICAgICBzZWxmLmJ1bGxldHNMZW4gPSAwCiAgICAgICAgICAgICAgICAgICAgc2VsZi5wbGF5ZXJTY29yZSArPSAxCiAgICAgICAgICAgIHByaW50KCJQbGF5ZXIgU2NvcmU6IixzZWxmLnBsYXllclNjb3JlLCJDb21wdXRlciBTY29yZToiLHNlbGYucGxheWVyMlNjb3JlKQogICAgIyDRgdGH0LjRgtC60LAg0LrQu9Cw0LLQuNGICiAgICBkZWYgZ2V0S2V5KHNlbGYpOgogICAgICAgIHdoaWxlIChUcnVlKToKICAgICAgICAgICAgc2VsZi5rZXkgPSBtc3ZjcnQuZ2V0Y2goKQoKZ2FtZSA9IEdhbWUoKQoKdGhyZWFkMSA9IFRocmVhZCh0YXJnZXQ9Z2FtZS5nYW1lTG9vcCkKdGhyZWFkMiA9IFRocmVhZCh0YXJnZXQ9Z2FtZS5nZXRLZXkpCgp0aHJlYWQxLnN0YXJ0KCkKdGhyZWFkMi5zdGFydCgpCnRocmVhZDEuam9pbigpCnRocmVhZDIuam9pbigpCg==