import math
#import matplotlib.pyplot
#import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
from matplotlib.colors import colorConverter
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
import mpl_toolkits.mplot3d.art3d as art3d
#import numpy as np
import matplotlib.pyplot as plt
#from numpy import *
from pylab import *
#import pylab
class vec(object):
def __init__(self, x, y, z):
self.vec=(x, y, z)
def unit(self, vec):
dist=math.sqrt(vec[0]**2+vec[1]**2+vec[2]**2)
unitvec=(vec[0]/dist,vec[1]/dist,vec[2]/dist)
return unitvec
def add(self, vector1, vector2):
sum=(vector1[0]+vector2[0], vector1[1]+vector2[1], vector1[2]+vector2[2])
return sum
def addfour(self, vec1, vec2, vec3, vec4):
comebine1=self.add(vec1,vec2)
comebine2=self.add(vec3,vec4)
outcome=self.add(comebine1, comebine2)
return outcome
def smultiply(self,scalar, vec):
product=(scalar*vec[0],scalar*vec[1],scalar*vec[2])
return product
def innerproduct(self, vec1, vec2):
result=(vec1[0]*vec2[0]+vec1[1]*vec2[1]+vec1[2]*vec2[2])
return result
def crossproduct(self, vec1, vec2):
res=(vec1[1]*vec2[2]-vec1[2]*vec2[1], vec1[2]*vec2[0]-vec1[0]*vec2[2], vec1[0]*vec2[1]-vec1[1]*vec2[0])
return res
def function(self, vec1, vec2, i):
coef1=c1*inner(vec2, vec2)
term1=self.smultiply(coef1, vec1)
term2=self.smultiply(c2,self.crossproduct(vec2,vec1))
term3=self.smultiply(c3*(1+c5*math.sin(5000*i*h)),(0,0,1))
term4=self.smultiply(c4*(1+c5*math.sin(5000*i*h))*vec1[2],vec1)
sumoffour=self.addfour(term1,term2,term3,term4)
out=self.smultiply(h, sumoffour)
return out
def rungekutta1(self,vec1,vec):
k11=self.smultiply(h,vec)
k11_half=self.smultiply(.5, k11)
y12=self.add(vec, k11_half)
k12=self.smultiply(h,y12)
k12_half=self.smultiply(.5, k12)
y13=self.add(vec, k12_half)
k13=self.smultiply(h,y13)
y14=self.add(vec, k13)
k14=self.smultiply(h,y14)
k12_double=self.smultiply(2.0, k12)
k13_double=self.smultiply(2.0, k13)
k1s=self.addfour(k11, k12_double, k13_double, k14)
n_ununit=self.add(vec1, self.smultiply(1.0/6, k1s))
n_next=self.unit(n_ununit)
return n_next
def rungekutta2(self, vec1, vec2, i):
k21=self.function(vec1, vec2, i)
n22=self.add(vec1,self.smultiply(.5*h,(1,1,1)))
d_n22=self.add(vec2,self.smultiply(.5,k21))
k22=self.function(n22,d_n22, i)
n23=self.add(vec1,self.smultiply(.5*h,(1,1,1)))
d_n23=self.add(vec2,self.smultiply(.5,k22))
k23=self.function(n23,d_n23, i)
n24=self.add(vec1,self.smultiply(h,(1,1,1)))
d_n24=self.add(vec2,k23)
k24=self.function(n24,d_n24, i)
k22_double=self.smultiply(2.0,k22)
k23_double=self.smultiply(2.0,k23)
k2s=self.addfour(k21, k22_double, k23_double, k24)
d_n_ununit=self.add(vec2, self.smultiply(1.0/6, k2s))
d_n_next=self.unit(d_n_ununit)
return d_n_next
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
FancyArrowPatch.draw(self, renderer)
def drawbody(x, y, z):
u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
x1=.4*np.cos(u)*np.sin(v)
y1=.4*np.sin(u)*np.sin(v)
z1=.1*np.cos(v)
theta = math.acos(math.sqrt(y**2+ z**2)/math.sqrt(x**2 + y**2 +z**2))
x2 = x1*math.cos(theta) + z1*math.sin(theta)
y2 = y1
z2 = -x1*math.sin(theta) + z1*math.cos(theta)
if theta <0:
theta1 = math.acos(z/math.sqrt(y**2+z**2))
else:
theta1=-math.acos(z/math.sqrt(y**2+z**2))
x3 = x2
y3 = y2*math.cos(theta1) - z2*math.sin(theta1)
z3 = y2*math.sin(theta1) + z2*math.cos(theta1)
ax.plot_wireframe(x3+x/2, y3+y/2, z3+z/2, color="#254117")
ax.scatter([x3+x/2],[y3+y/2],[0], marker='_', color='black')
matplotlib.pyplot.show()
def drawgroundandlabelaxis(z, n_o):
rectangle = Rectangle((-1,-1),2,2, color='grey')
ax.add_patch(rectangle)
art3d.pathpatch_2d_to_3d(rectangle, z=z- n_o)
ax.set_xlabel('X-axis')
ax.set_ylabel('Y-axis')
ax.set_zlabel('Z-axis')
matplotlib.pyplot.show()
def plotvel(dx, dy, dz, x, y, z):
a = Arrow3D([x,dx+x],[y,dy+y],[z,dz+z], mutation_scale=20, lw=1, arrowstyle="-|>", color='r')
ax.add_artist(a)
plt.show()
#ax.scatter(dx, dy, dz, c='#330099')
matplotlib.pyplot.show()
def plotpos(x ,y ,z, n_o, w):
ax.cla()
#drawbody(x,y,z)
line = ax.plot([x,0],[y,0],[z,z- n_o],color='#000066', marker= 'o')
line = ax.plot([0,0],[0,0],[1.1,-.5],color='g')
ax.scatter([0,0],[0,0],[0,1], marker='8', color='g')
drawgroundandlabelaxis(z, n_o)
matplotlib.pyplot.show()
def plotthetaphi(x,y,z):
phi= math.acos(z/math.sqrt(x**2+y**2+z**2))
theta = math.acos(x/math.sqrt(x**2 + y**2))
plot(theta, phi,'.',color='red')
def drawstaticplot(m, n, d_n, n_o):
counter=0
for i in range(0,m):
n=vector.rungekutta1(n, d_n)
d_n=vector.rungekutta2(n, d_n, i)
x1 = n[0]
y1 = n[1]
z1 = n[2]
if i%20==0:
xarray.append(x1)
yarray.append(y1)
zarray.append(z1)
x_m = xarray[::20]
y_m = yarray[::20]
z_m = zarray[::20]
for j in range(0,6):
print x_m[j], y_m[j], z_m[j]
# if (((zarray[j]-n_o)>0) and ((zarray[j+1]-n_o)<0)): #or (((zarray[j]-n_o)<0) and ((zarray[j+20]-n_o)>0)):
# counter= counter +1
# print zarray[j]-n_o,counter
# plotthetaphi(xarray[j],yarray[j],zarray[j])
#ax.plot([xarray[j],xarray[j+20]],[yarray[j],yarray[j+20]],[zarray[j],zarray[j+20]],color='#0000FF' ,marker='.')
#ax.plot([xarray[j],0],[yarray[j],0],[zarray[j],zarray[j]-n_o],color='#0000FF',marker='.')
#matplotlib.pyplot.show()
def setboundary():
ax.set_xlim3d(-1,1)
ax.set_ylim3d(-1,1)
ax.set_zlim3d(0,1)
def drawdynamicplot(m,n,d_n, n_o):
for i in range(0,m):
n=vector.rungekutta1(n, d_n)
d_n=vector.rungekutta2(n, d_n, i)
setboundary()
if i%20 == 0:
print "-------------" ,i , "----------------"
print n[0]
plotpos(n[0],n[1],n[2], n_o, i)
plotvel(d_n[0],d_n[1],d_n[2],n[0],n[1],n[2])
#if i%320 ==0:
#plotthetaphi(n[0],n[1],n[2])
def realtimeconnectline(m,n,d_n):
for i in range(0,m):
n=vector.rungekutta1(n, d_n)
d_n=vector.rungekutta2(n, d_n, i)
x1=n[0]
y1=n[1]
z1=n[2]
print x1,y1,z1
if i%10 == 0:
x1=n[0]
y1=n[1]
z1=n[2]
if i%20==0:
x2=n[0]
y2=n[1]
z2=n[2]
ax.plot([x1,x2],[y1,y2],[z1,z2],color = 'blue',marker='.')
matplotlib.pyplot.show()
#fig = plt.figure()
#ax = fig.add_subplot(111, projection='3d')
c1=-1.0
c2=6.6
c3=-5.4
c4=-c3
c5=1
#[c1,c2,c3,c4,c5]=[-1.0,-2.4,-3.67,-c3,0]
h=.005
vector=vec(1,2,3)
allone=vec(1.0,1.0,1.0)
n=(0.0,.5,.5)
d_n=(5.0,5.0,0.0)
ion()
n=vector.unit(n)
#d_n = vec.unit(vec, d_n)
m=5000
xarray=[]
yarray=[]
zarray=[]
drawstaticplot(m,n, d_n, n[2])
#drawdynamicplot(m,n, d_n, n[2])
#realtimeconnectline(m,n,d_n)
aW1wb3J0IG1hdGgKI2ltcG9ydCBtYXRwbG90bGliLnB5cGxvdAojaW1wb3J0IG1hdHBsb3RsaWIgYXMgbXBsCmZyb20gbXBsX3Rvb2xraXRzLm1wbG90M2QgaW1wb3J0IEF4ZXMzRApmcm9tIG1hdHBsb3RsaWIuY29sbGVjdGlvbnMgaW1wb3J0IFBvbHlDb2xsZWN0aW9uCmZyb20gbWF0cGxvdGxpYi5jb2xvcnMgaW1wb3J0IGNvbG9yQ29udmVydGVyCmZyb20gbWF0cGxvdGxpYi5wYXRjaGVzIGltcG9ydCBGYW5jeUFycm93UGF0Y2gKZnJvbSBtcGxfdG9vbGtpdHMubXBsb3QzZCBpbXBvcnQgcHJvajNkCmltcG9ydCBtcGxfdG9vbGtpdHMubXBsb3QzZC5hcnQzZCBhcyBhcnQzZAojaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKI2Zyb20gbnVtcHkgaW1wb3J0ICoKZnJvbSBweWxhYiBpbXBvcnQgKgojaW1wb3J0IHB5bGFiCgoKY2xhc3MgdmVjKG9iamVjdCk6CiAgICBkZWYgX19pbml0X18oc2VsZiwgeCwgeSwgeik6CiAgICAgICAgc2VsZi52ZWM9KHgsIHksIHopCiAgICAgICAgCiAgICBkZWYgdW5pdChzZWxmLCB2ZWMpOgogICAgICAgIGRpc3Q9bWF0aC5zcXJ0KHZlY1swXSoqMit2ZWNbMV0qKjIrdmVjWzJdKioyKQogICAgICAgIHVuaXR2ZWM9KHZlY1swXS9kaXN0LHZlY1sxXS9kaXN0LHZlY1syXS9kaXN0KQogICAgICAgIHJldHVybiB1bml0dmVjCiAgICAKICAgIGRlZiBhZGQoc2VsZiwgdmVjdG9yMSwgdmVjdG9yMik6CiAgICAgICAgc3VtPSh2ZWN0b3IxWzBdK3ZlY3RvcjJbMF0sIHZlY3RvcjFbMV0rdmVjdG9yMlsxXSwgdmVjdG9yMVsyXSt2ZWN0b3IyWzJdKQogICAgICAgIHJldHVybiBzdW0KICAgIAogICAgZGVmIGFkZGZvdXIoc2VsZiwgdmVjMSwgdmVjMiwgdmVjMywgdmVjNCk6CiAgICAgICAgY29tZWJpbmUxPXNlbGYuYWRkKHZlYzEsdmVjMikKICAgICAgICBjb21lYmluZTI9c2VsZi5hZGQodmVjMyx2ZWM0KQogICAgICAgIG91dGNvbWU9c2VsZi5hZGQoY29tZWJpbmUxLCBjb21lYmluZTIpCiAgICAgICAgcmV0dXJuIG91dGNvbWUKICAgIAogICAgZGVmIHNtdWx0aXBseShzZWxmLHNjYWxhciwgdmVjKToKICAgICAgICBwcm9kdWN0PShzY2FsYXIqdmVjWzBdLHNjYWxhcip2ZWNbMV0sc2NhbGFyKnZlY1syXSkKICAgICAgICByZXR1cm4gcHJvZHVjdAogICAgCiAgICBkZWYgaW5uZXJwcm9kdWN0KHNlbGYsIHZlYzEsIHZlYzIpOgogICAgICAgIHJlc3VsdD0odmVjMVswXSp2ZWMyWzBdK3ZlYzFbMV0qdmVjMlsxXSt2ZWMxWzJdKnZlYzJbMl0pCiAgICAgICAgcmV0dXJuIHJlc3VsdAogICAgCiAgICBkZWYgY3Jvc3Nwcm9kdWN0KHNlbGYsIHZlYzEsIHZlYzIpOgogICAgICAgIHJlcz0odmVjMVsxXSp2ZWMyWzJdLXZlYzFbMl0qdmVjMlsxXSwgdmVjMVsyXSp2ZWMyWzBdLXZlYzFbMF0qdmVjMlsyXSwgdmVjMVswXSp2ZWMyWzFdLXZlYzFbMV0qdmVjMlswXSkKICAgICAgICByZXR1cm4gcmVzCiAgICAKICAgIGRlZiBmdW5jdGlvbihzZWxmLCB2ZWMxLCB2ZWMyLCBpKToKICAgICAgICBjb2VmMT1jMSppbm5lcih2ZWMyLCB2ZWMyKQogICAgICAgIHRlcm0xPXNlbGYuc211bHRpcGx5KGNvZWYxLCB2ZWMxKQogICAgICAgIHRlcm0yPXNlbGYuc211bHRpcGx5KGMyLHNlbGYuY3Jvc3Nwcm9kdWN0KHZlYzIsdmVjMSkpCiAgICAgICAgdGVybTM9c2VsZi5zbXVsdGlwbHkoYzMqKDErYzUqbWF0aC5zaW4oNTAwMCppKmgpKSwoMCwwLDEpKQogICAgICAgIHRlcm00PXNlbGYuc211bHRpcGx5KGM0KigxK2M1Km1hdGguc2luKDUwMDAqaSpoKSkqdmVjMVsyXSx2ZWMxKQogICAgICAgIHN1bW9mZm91cj1zZWxmLmFkZGZvdXIodGVybTEsdGVybTIsdGVybTMsdGVybTQpCiAgICAgICAgb3V0PXNlbGYuc211bHRpcGx5KGgsIHN1bW9mZm91cikKICAgICAgICByZXR1cm4gb3V0CiAgICBkZWYgcnVuZ2VrdXR0YTEoc2VsZix2ZWMxLHZlYyk6CiAgICAgICAgazExPXNlbGYuc211bHRpcGx5KGgsdmVjKQogICAgICAgIGsxMV9oYWxmPXNlbGYuc211bHRpcGx5KC41LCBrMTEpCiAgICAgICAgeTEyPXNlbGYuYWRkKHZlYywgazExX2hhbGYpCiAgICAgICAgazEyPXNlbGYuc211bHRpcGx5KGgseTEyKQogICAgICAgIGsxMl9oYWxmPXNlbGYuc211bHRpcGx5KC41LCBrMTIpCiAgICAgICAgeTEzPXNlbGYuYWRkKHZlYywgazEyX2hhbGYpCiAgICAgICAgazEzPXNlbGYuc211bHRpcGx5KGgseTEzKQogICAgICAgIHkxND1zZWxmLmFkZCh2ZWMsIGsxMykKICAgICAgICBrMTQ9c2VsZi5zbXVsdGlwbHkoaCx5MTQpCiAgICAgICAgazEyX2RvdWJsZT1zZWxmLnNtdWx0aXBseSgyLjAsIGsxMikKICAgICAgICBrMTNfZG91YmxlPXNlbGYuc211bHRpcGx5KDIuMCwgazEzKQogICAgICAgIGsxcz1zZWxmLmFkZGZvdXIoazExLCBrMTJfZG91YmxlLCBrMTNfZG91YmxlLCBrMTQpCiAgICAgICAgbl91bnVuaXQ9c2VsZi5hZGQodmVjMSwgc2VsZi5zbXVsdGlwbHkoMS4wLzYsIGsxcykpCiAgICAgICAgbl9uZXh0PXNlbGYudW5pdChuX3VudW5pdCkKICAgICAgICByZXR1cm4gbl9uZXh0CiAgICBkZWYgcnVuZ2VrdXR0YTIoc2VsZiwgdmVjMSwgdmVjMiwgaSk6CiAgICAgICAgazIxPXNlbGYuZnVuY3Rpb24odmVjMSwgdmVjMiwgaSkKICAgICAgICBuMjI9c2VsZi5hZGQodmVjMSxzZWxmLnNtdWx0aXBseSguNSpoLCgxLDEsMSkpKQogICAgICAgIGRfbjIyPXNlbGYuYWRkKHZlYzIsc2VsZi5zbXVsdGlwbHkoLjUsazIxKSkKICAgICAgICBrMjI9c2VsZi5mdW5jdGlvbihuMjIsZF9uMjIsIGkpCiAgICAgICAgbjIzPXNlbGYuYWRkKHZlYzEsc2VsZi5zbXVsdGlwbHkoLjUqaCwoMSwxLDEpKSkKICAgICAgICBkX24yMz1zZWxmLmFkZCh2ZWMyLHNlbGYuc211bHRpcGx5KC41LGsyMikpCiAgICAgICAgazIzPXNlbGYuZnVuY3Rpb24objIzLGRfbjIzLCBpKQogICAgICAgIG4yND1zZWxmLmFkZCh2ZWMxLHNlbGYuc211bHRpcGx5KGgsKDEsMSwxKSkpCiAgICAgICAgZF9uMjQ9c2VsZi5hZGQodmVjMixrMjMpCiAgICAgICAgazI0PXNlbGYuZnVuY3Rpb24objI0LGRfbjI0LCBpKQogICAgICAgIGsyMl9kb3VibGU9c2VsZi5zbXVsdGlwbHkoMi4wLGsyMikKICAgICAgICBrMjNfZG91YmxlPXNlbGYuc211bHRpcGx5KDIuMCxrMjMpCiAgICAgICAgazJzPXNlbGYuYWRkZm91cihrMjEsIGsyMl9kb3VibGUsIGsyM19kb3VibGUsIGsyNCkKICAgICAgICBkX25fdW51bml0PXNlbGYuYWRkKHZlYzIsIHNlbGYuc211bHRpcGx5KDEuMC82LCBrMnMpKQogICAgICAgIGRfbl9uZXh0PXNlbGYudW5pdChkX25fdW51bml0KQogICAgICAgIHJldHVybiBkX25fbmV4dAoKY2xhc3MgQXJyb3czRChGYW5jeUFycm93UGF0Y2gpOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHhzLCB5cywgenMsICphcmdzLCAqKmt3YXJncyk6CiAgICAgICAgRmFuY3lBcnJvd1BhdGNoLl9faW5pdF9fKHNlbGYsICgwLDApLCAoMCwwKSwgKmFyZ3MsICoqa3dhcmdzKQogICAgICAgIHNlbGYuX3ZlcnRzM2QgPSB4cywgeXMsIHpzCgogICAgZGVmIGRyYXcoc2VsZiwgcmVuZGVyZXIpOgogICAgICAgIHhzM2QsIHlzM2QsIHpzM2QgPSBzZWxmLl92ZXJ0czNkCiAgICAgICAgeHMsIHlzLCB6cyA9IHByb2ozZC5wcm9qX3RyYW5zZm9ybSh4czNkLCB5czNkLCB6czNkLCByZW5kZXJlci5NKQogICAgICAgIHNlbGYuc2V0X3Bvc2l0aW9ucygoeHNbMF0seXNbMF0pLCh4c1sxXSx5c1sxXSkpCiAgICAgICAgRmFuY3lBcnJvd1BhdGNoLmRyYXcoc2VsZiwgcmVuZGVyZXIpCgpkZWYgZHJhd2JvZHkoeCwgeSwgeik6CiAgICAgICAgdSwgdiA9IG5wLm1ncmlkWzA6MipucC5waToyMGosIDA6bnAucGk6MTBqXQogICAgICAgIHgxPS40Km5wLmNvcyh1KSpucC5zaW4odikKICAgICAgICB5MT0uNCpucC5zaW4odSkqbnAuc2luKHYpCiAgICAgICAgejE9LjEqbnAuY29zKHYpCiAgICAgICAgdGhldGEgPSBtYXRoLmFjb3MobWF0aC5zcXJ0KHkqKjIrIHoqKjIpL21hdGguc3FydCh4KioyICsgeSoqMiAreioqMikpCiAgICAgICAgeDIgPSB4MSptYXRoLmNvcyh0aGV0YSkgKyB6MSptYXRoLnNpbih0aGV0YSkKICAgICAgICB5MiA9IHkxCiAgICAgICAgejIgPSAteDEqbWF0aC5zaW4odGhldGEpICsgejEqbWF0aC5jb3ModGhldGEpCiAgICAgICAgaWYgdGhldGEgPDA6CiAgICAgICAgICAgIHRoZXRhMSA9IG1hdGguYWNvcyh6L21hdGguc3FydCh5KioyK3oqKjIpKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHRoZXRhMT0tbWF0aC5hY29zKHovbWF0aC5zcXJ0KHkqKjIreioqMikpCiAgICAgICAgICAgIHgzID0geDIKICAgICAgICAgICAgeTMgPSB5MiptYXRoLmNvcyh0aGV0YTEpIC0gejIqbWF0aC5zaW4odGhldGExKQogICAgICAgICAgICB6MyA9IHkyKm1hdGguc2luKHRoZXRhMSkgKyB6MiptYXRoLmNvcyh0aGV0YTEpCiAgICAgICAgYXgucGxvdF93aXJlZnJhbWUoeDMreC8yLCB5Myt5LzIsIHozK3ovMiwgY29sb3I9IiMyNTQxMTciKSAgICAgICAgICAgCiAgICAgICAgYXguc2NhdHRlcihbeDMreC8yXSxbeTMreS8yXSxbMF0sIG1hcmtlcj0nXycsIGNvbG9yPSdibGFjaycpCiAgICAgICAgbWF0cGxvdGxpYi5weXBsb3Quc2hvdygpCgpkZWYgZHJhd2dyb3VuZGFuZGxhYmVsYXhpcyh6LCBuX28pOgogICAgICAgIHJlY3RhbmdsZSA9IFJlY3RhbmdsZSgoLTEsLTEpLDIsMiwgY29sb3I9J2dyZXknKQogICAgICAgIGF4LmFkZF9wYXRjaChyZWN0YW5nbGUpCiAgICAgICAgYXJ0M2QucGF0aHBhdGNoXzJkX3RvXzNkKHJlY3RhbmdsZSwgej16LSBuX28pCiAgICAgICAgYXguc2V0X3hsYWJlbCgnWC1heGlzJykKICAgICAgICBheC5zZXRfeWxhYmVsKCdZLWF4aXMnKQogICAgICAgIGF4LnNldF96bGFiZWwoJ1otYXhpcycpICAgICAKICAgICAgICBtYXRwbG90bGliLnB5cGxvdC5zaG93KCkKCmRlZiBwbG90dmVsKGR4LCBkeSwgZHosIHgsIHksIHopOgogICAgICAgIGEgPSBBcnJvdzNEKFt4LGR4K3hdLFt5LGR5K3ldLFt6LGR6K3pdLCBtdXRhdGlvbl9zY2FsZT0yMCwgbHc9MSwgYXJyb3dzdHlsZT0iLXw+IiwgY29sb3I9J3InKQogICAgICAgIGF4LmFkZF9hcnRpc3QoYSkKICAgICAgICBwbHQuc2hvdygpCiAgICAgICAgI2F4LnNjYXR0ZXIoZHgsIGR5LCBkeiwgYz0nIzMzMDA5OScpCiAgICAgICAgbWF0cGxvdGxpYi5weXBsb3Quc2hvdygpCiAgICAKZGVmIHBsb3Rwb3MoeCAseSAseiwgbl9vLCB3KToKICAgICAgICBheC5jbGEoKQogICAgICAgICNkcmF3Ym9keSh4LHkseikKICAgICAgICBsaW5lID0gYXgucGxvdChbeCwwXSxbeSwwXSxbeix6LSBuX29dLGNvbG9yPScjMDAwMDY2JywgbWFya2VyPSAnbycpCiAgICAgICAgbGluZSA9IGF4LnBsb3QoWzAsMF0sWzAsMF0sWzEuMSwtLjVdLGNvbG9yPSdnJykKICAgICAgICBheC5zY2F0dGVyKFswLDBdLFswLDBdLFswLDFdLCBtYXJrZXI9JzgnLCBjb2xvcj0nZycpCiAgICAgICAgZHJhd2dyb3VuZGFuZGxhYmVsYXhpcyh6LCBuX28pCiAgICAgICAgbWF0cGxvdGxpYi5weXBsb3Quc2hvdygpCgpkZWYgcGxvdHRoZXRhcGhpKHgseSx6KToKICAgIHBoaT0gbWF0aC5hY29zKHovbWF0aC5zcXJ0KHgqKjIreSoqMit6KioyKSkKICAgIHRoZXRhID0gbWF0aC5hY29zKHgvbWF0aC5zcXJ0KHgqKjIgKyB5KioyKSkKICAgIHBsb3QodGhldGEsIHBoaSwnLicsY29sb3I9J3JlZCcpCiAgICAKCiAgICAKZGVmIGRyYXdzdGF0aWNwbG90KG0sIG4sIGRfbiwgbl9vKToKICAgIGNvdW50ZXI9MAogICAgZm9yIGkgaW4gcmFuZ2UoMCxtKToKICAgICAgICBuPXZlY3Rvci5ydW5nZWt1dHRhMShuLCBkX24pCiAgICAgICAgZF9uPXZlY3Rvci5ydW5nZWt1dHRhMihuLCBkX24sIGkpCiAgICAgICAgeDEgPSBuWzBdCiAgICAgICAgeTEgPSBuWzFdCiAgICAgICAgejEgPSBuWzJdCiAgICAgICAgaWYgaSUyMD09MDoKICAgICAgICAgICAgeGFycmF5LmFwcGVuZCh4MSkKICAgICAgICAgICAgeWFycmF5LmFwcGVuZCh5MSkKICAgICAgICAgICAgemFycmF5LmFwcGVuZCh6MSkKICAgICAgICB4X20gPSB4YXJyYXlbOjoyMF0KICAgICAgICB5X20gPSB5YXJyYXlbOjoyMF0KICAgICAgICB6X20gPSB6YXJyYXlbOjoyMF0KICAgICAgICAKICAgIGZvciBqIGluIHJhbmdlKDAsNik6CiAgICAgICAgcHJpbnQgeF9tW2pdLCB5X21bal0sIHpfbVtqXQogICAgIyAgICBpZiAoKCh6YXJyYXlbal0tbl9vKT4wKSBhbmQgKCh6YXJyYXlbaisxXS1uX28pPDApKTogI29yICgoKHphcnJheVtqXS1uX28pPDApIGFuZCAoKHphcnJheVtqKzIwXS1uX28pPjApKToKICAgICMgICAgICAgIGNvdW50ZXI9IGNvdW50ZXIgKzEKICAgICMgICAgICAgIHByaW50IHphcnJheVtqXS1uX28sY291bnRlcgogICAgIyAgICAgICAgcGxvdHRoZXRhcGhpKHhhcnJheVtqXSx5YXJyYXlbal0semFycmF5W2pdKQogICAgICAgICAgICAKICAgICAgICAgICAgI2F4LnBsb3QoW3hhcnJheVtqXSx4YXJyYXlbaisyMF1dLFt5YXJyYXlbal0seWFycmF5W2orMjBdXSxbemFycmF5W2pdLHphcnJheVtqKzIwXV0sY29sb3I9JyMwMDAwRkYnICxtYXJrZXI9Jy4nKQogICAgICAgICAgICAjYXgucGxvdChbeGFycmF5W2pdLDBdLFt5YXJyYXlbal0sMF0sW3phcnJheVtqXSx6YXJyYXlbal0tbl9vXSxjb2xvcj0nIzAwMDBGRicsbWFya2VyPScuJykKICAgICAgICAgICAgI21hdHBsb3RsaWIucHlwbG90LnNob3coKQoKCgogICAgCmRlZiBzZXRib3VuZGFyeSgpOgogICAgYXguc2V0X3hsaW0zZCgtMSwxKQogICAgYXguc2V0X3lsaW0zZCgtMSwxKQogICAgYXguc2V0X3psaW0zZCgwLDEpCiAgICAKCmRlZiBkcmF3ZHluYW1pY3Bsb3QobSxuLGRfbiwgbl9vKToKICAgIGZvciBpIGluIHJhbmdlKDAsbSk6ICAgICAgICAKICAgICAgICAgICAgbj12ZWN0b3IucnVuZ2VrdXR0YTEobiwgZF9uKQogICAgICAgICAgICBkX249dmVjdG9yLnJ1bmdla3V0dGEyKG4sIGRfbiwgaSkKICAgICAgICAgICAgc2V0Ym91bmRhcnkoKQogICAgICAgICAgICBpZiBpJTIwID09IDA6CiAgICAgICAgICAgICAgICBwcmludCAiLS0tLS0tLS0tLS0tLSIgLGkgLCAiLS0tLS0tLS0tLS0tLS0tLSIKICAgICAgICAgICAgICAgIHByaW50IG5bMF0gICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICBwbG90cG9zKG5bMF0sblsxXSxuWzJdLCBuX28sIGkpCiAgICAgICAgICAgICAgICBwbG90dmVsKGRfblswXSxkX25bMV0sZF9uWzJdLG5bMF0sblsxXSxuWzJdKQogICAgICAgICAgICAgICAgI2lmIGklMzIwID09MDogICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICNwbG90dGhldGFwaGkoblswXSxuWzFdLG5bMl0pCiAgICAgICAgICAgICAgICAKZGVmIHJlYWx0aW1lY29ubmVjdGxpbmUobSxuLGRfbik6CiAgICBmb3IgaSBpbiByYW5nZSgwLG0pOgogICAgICAgIG49dmVjdG9yLnJ1bmdla3V0dGExKG4sIGRfbikKICAgICAgICBkX249dmVjdG9yLnJ1bmdla3V0dGEyKG4sIGRfbiwgaSkKICAgICAgICB4MT1uWzBdCiAgICAgICAgeTE9blsxXQogICAgICAgIHoxPW5bMl0KICAgICAgICBwcmludCB4MSx5MSx6MQogICAgICAgIGlmIGklMTAgPT0gMDoKICAgICAgICAgICAgICAgIHgxPW5bMF0KICAgICAgICAgICAgICAgIHkxPW5bMV0KICAgICAgICAgICAgICAgIHoxPW5bMl0KICAgICAgICAgICAgICAgIGlmIGklMjA9PTA6CiAgICAgICAgICAgICAgICAgICAgeDI9blswXQogICAgICAgICAgICAgICAgICAgIHkyPW5bMV0KICAgICAgICAgICAgICAgICAgICB6Mj1uWzJdCiAgICAgICAgICAgICAgICBheC5wbG90KFt4MSx4Ml0sW3kxLHkyXSxbejEsejJdLGNvbG9yID0gJ2JsdWUnLG1hcmtlcj0nLicpCiAgICAgICAgICAgICAgICBtYXRwbG90bGliLnB5cGxvdC5zaG93KCkKICAgICAgICAgICAgICAgIAoKCiAgICAgICAgICAgICAgICAKI2ZpZyA9IHBsdC5maWd1cmUoKQojYXggPSBmaWcuYWRkX3N1YnBsb3QoMTExLCBwcm9qZWN0aW9uPSczZCcpCgpjMT0tMS4wCmMyPTYuNgpjMz0tNS40CmM0PS1jMwpjNT0xCiNbYzEsYzIsYzMsYzQsYzVdPVstMS4wLC0yLjQsLTMuNjcsLWMzLDBdCmg9LjAwNQp2ZWN0b3I9dmVjKDEsMiwzKQphbGxvbmU9dmVjKDEuMCwxLjAsMS4wKQpuPSgwLjAsLjUsLjUpCmRfbj0oNS4wLDUuMCwwLjApCmlvbigpCm49dmVjdG9yLnVuaXQobikKI2RfbiA9IHZlYy51bml0KHZlYywgZF9uKQoKbT01MDAwCnhhcnJheT1bXQp5YXJyYXk9W10KemFycmF5PVtdCgpkcmF3c3RhdGljcGxvdChtLG4sIGRfbiwgblsyXSkKI2RyYXdkeW5hbWljcGxvdChtLG4sIGRfbiwgblsyXSkKI3JlYWx0aW1lY29ubmVjdGxpbmUobSxuLGRfbikKCg==