# -*- coding: utf-8 -*-
'''
経緯線描画 (日時指定)
Ver 1.12 2013-05-30 update
'''
from Tkinter import *
from math import *
from numpy import *
from urllib import *
from HTMLParser import HTMLParser
import datetime
'''
経緯線描画
'''
class LatLong:
def __init__(self, P,B0,L0,r,width,height,astep,dstep,bg,fc,line,fpath):
self.root = Tk()
self.P = P
self.B0 = B0
self.L0 = L0
self.r = r
self.width = width
self.height = height
self.astep = astep
self.dstep = dstep
self.backcolor = bg
self.forecolor = fc
self.lineweight = line
self.fpath = fpath
self.c0=Canvas(self.root, width=width, height=height, bg=bg)
self.c0.pack()
# Z軸回転
def rotZ(self, a, d):
L = cos(radians(d)) * cos(radians(a - self.L0))
M = cos(radians(d)) * sin(radians(a - self.L0))
N = sin(radians(d))
return array([[L], [M], [N]])
# Y軸回転
def rotY(self, LMN):
mB0 = radians(self.B0) * -1
res = array([[]])
res = append(res, [[cos(mB0), 0, -1 * sin(mB0)]], axis=1)
res = append(res, [[0, 1, 0]], axis=0)
res = append(res, [[sin(mB0), 0, cos(mB0)]], axis=0)
return dot(res, LMN)
# X軸回転
def rotX(self, LMNd):
mP = radians(self.P) * -1
res = array([[]])
res = append(res, [[1, 0, 0]], axis=1)
res = append(res, [[0, cos(mP), sin(mP)]], axis=0)
res = append(res, [[0, -1 * sin(mP), cos(mP)]], axis=0)
return dot(res, LMNd)
# XYZ計算
def calXYZ(self, a, d):
LMN = self.rotZ(a,d)
LMNd = self.rotY(LMN)
xyz = self.rotX(LMNd)
return sum(xyz[0]),sum(xyz[1]),sum(xyz[2])
# 位置補正(XYZ)
def corrPos(self, x, y, z):
x = x
y = y * self.r + self.width / 2
z = self.height / 2 - z * self.r
return x,y,z
# 緯線
def Latitude(self):
lpos=[]
for delta in range(-90,90,self.dstep):
for alpha in range(0, 361, 5):
xyz = self.calXYZ(alpha, delta)
x,y,z = self.corrPos(xyz[0], xyz[1], xyz[2])
if x>=0:
lpos.append(y)
lpos.append(z)
else:
if len(lpos)>2:
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:]=[]
if len(lpos)>2:
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:]=[]
# 経線
def Longitude(self):
lpos=[]
for alpha in range(0,360,self.astep):
for delta in range(-90, 91, 5):
xyz = self.calXYZ(alpha, delta)
x,y,z = self.corrPos(xyz[0], xyz[1], xyz[2])
if x>=0:
lpos.append(y)
lpos.append(z)
if len(lpos)>2:
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:]=[]
# 外周描画
def dispOuter(self):
lpos = []
for pos in range(360):
y = cos(radians(pos))
z = sin(radians(pos))
x,y,z = self.corrPos(0, y, z)
lpos.append(y)
lpos.append(z)
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:] = []
# Save PostScript
def SaveImage(self):
self.c0.postscript(file=self.fpath, width=self.width, height=self.height)
# MainLoop
def mainloop(self):
self.root.mainloop()
'''
国立天文台よりPB0L0取得
'''
class KHTMLParser(HTMLParser):
hms = "00:00:00"
getfl = False
P = None
B0 = None
L0 = None
def handle_data(self, data):
data = data.strip(" \t\r\n")
if data and self.getfl:
if self.P and self.B0 and self.L0 is None: self.L0 = float(data)
if self.P and self.B0 is None: self.B0 = float(data)
if self.P is None: self.P = float(data)
if data.strip() == self.hms: self.getfl = True
def handle_starttag(self, tag, attrs):
pass
def handle_endtag(self, tag):
pass
'''
Main
'''
if __name__=='__main__':
'''
使用方法
クラス初期化
LatLong(P,B0,L0,r,width,height,astep,dstep,backcolor,forecolor,lineweight,fpath)
'''
# PB0L0取得
params = {'year':2013,'month':05,'day':20,'hour':10,'min':30,'sec':00}
r=urlopen('http://e...content-available-to-author-only...c.jp/cgi-bin/koyomi/cande/sun_spin.cgi',urlencode(params))
mp = KHTMLParser()
mp.hms = "%02d:%02d:%02d" % (int(params['hour']), int(params['min']), int(params['sec']))
mp.feed(r.read())
# LatLong起動
latlong = LatLong(mp.P,mp.B0,mp.L0,300, 800, 800, 15, 15, "black", "green", 2.0, r"~/Desktop/ball.ps")
latlong.Latitude()
latlong.Longitude()
latlong.dispOuter()
latlong.SaveImage()
latlong.mainloop()
# -*- coding: utf-8 -*-
'''
経緯線描画 (日時指定)
Ver 1.11 2013-05-29 update
'''
from Tkinter import *
from math import *
from numpy import *
from urllib import *
from HTMLParser import HTMLParser
import datetime
'''
経緯線描画
'''
class LatLong:
def __init__(self, P,B0,L0,r,width,height,astep,dstep,bg,fc,line,fpath):
self.root = Tk()
self.P = P
self.B0 = B0
self.L0 = L0
self.r = r
self.width = width
self.height = height
self.astep = astep
self.dstep = dstep
self.backcolor = bg
self.forecolor = fc
self.lineweight = line
self.fpath = fpath
self.c0=Canvas(self.root, width=width, height=height, bg=bg)
self.c0.pack()
# Z軸回転
def rotZ(self, a, d):
L = cos(radians(d)) * cos(radians(a - self.L0))
M = cos(radians(d)) * sin(radians(a - self.L0))
N = sin(radians(d))
return array([[L], [M], [N]])
# Y軸回転
def rotY(self, LMN):
mB0 = radians(self.B0) * -1
res = array([[]])
res = append(res, [[cos(mB0), 0, -1 * sin(mB0)]], axis=1)
res = append(res, [[0, 1, 0]], axis=0)
res = append(res, [[sin(mB0), 0, cos(mB0)]], axis=0)
return dot(res, LMN)
# X軸回転
def rotX(self, LMNd):
mP = radians(self.P) * -1
res = array([[]])
res = append(res, [[1, 0, 0]], axis=1)
res = append(res, [[0, cos(mP), sin(mP)]], axis=0)
res = append(res, [[0, -1 * sin(mP), cos(mP)]], axis=0)
return dot(res, LMNd)
# XYZ計算
def calXYZ(self, a, d):
LMN = self.rotZ(a,d)
LMNd = self.rotY(LMN)
xyz = self.rotX(LMNd)
return sum(xyz[0]),sum(xyz[1]),sum(xyz[2])
# 位置補正(XYZ)
def corrPos(self, x, y, z):
x = x
y = y * self.r + self.width / 2
z = self.height / 2 - z * self.r
return x,y,z
# 緯線
def Latitude(self):
lpos=[]
for delta in range(-90,90,self.dstep):
for alpha in range(0, 361, 5):
xyz = self.calXYZ(alpha, delta)
x,y,z = self.corrPos(xyz[0], xyz[1], xyz[2])
if x>=0:
lpos.append(y)
lpos.append(z)
else:
if len(lpos)>2:
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:]=[]
if len(lpos)>2:
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:]=[]
# 経線
def Longitude(self):
lpos=[]
for alpha in range(0,360,self.astep):
for delta in range(-90, 91, 5):
xyz = self.calXYZ(alpha, delta)
x,y,z = self.corrPos(xyz[0], xyz[1], xyz[2])
if x>=0:
lpos.append(y)
lpos.append(z)
if len(lpos)>2:
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:]=[]
# 外周描画
def dispOuter(self):
lpos = []
for pos in range(360):
y = cos(radians(pos))
z = sin(radians(pos))
x,y,z = self.corrPos(0, y, z)
lpos.append(y)
lpos.append(z)
self.c0.create_line(lpos,fill=self.forecolor,width=self.lineweight,smooth=True)
lpos[:] = []
# Save PostScript
def SaveImage(self):
self.c0.postscript(file=self.fpath, width=self.width, height=self.height)
# MainLoop
def mainloop(self):
self.root.mainloop()
'''
国立天文台よりPB0L0取得
'''
class KHTMLParser(HTMLParser):
hms = "00:00:00"
getfl = False
P = None
B0 = None
L0 = None
def handle_data(self, data):
data = data.strip(" \t\r\n")
if data and self.getfl:
if self.P and self.B0 and self.L0 is None: self.L0 = float(data)
if self.P and self.B0 is None: self.B0 = float(data)
if self.P is None: self.P = float(data)
if data.strip() == self.hms: self.getfl = True
def handle_starttag(self, tag, attrs):
pass
def handle_endtag(self, tag):
pass
'''
Main
'''
if __name__=='__main__':
'''
使用方法
クラス初期化
LatLong(P,B0,L0,r,width,height,astep,dstep,backcolor,forecolor,lineweight,fpath)
'''
# PB0L0取得
params = {'year':2013,'month':05,'day':20,'hour':10,'min':30,'sec':00, 'tsys':2}
r=urlopen('http://e...content-available-to-author-only...c.jp/cgi-bin/koyomi/cande/sun_spin.cgi',urlencode(params))
mp = KHTMLParser()
mp.hms = "%02d:%02d:%02d" % (int(params['hour']), int(params['min']), int(params['sec']))
mp.feed(r.read())
# LatLong起動
latlong = LatLong(mp.P,mp.B0,mp.L0,300, 800, 800, 15, 15, "black", "green", 2.0, r"~/Desktop/ball.ps")
latlong.Latitude()
latlong.Longitude()
latlong.dispOuter()
latlong.SaveImage()
latlong.mainloop()
IyAtKi0gY29kaW5nOiB1dGYtOCAtKi0KJycnCue1jOe3r+e3muaPj+eUuyAo5pel5pmC5oyH5a6aKQpWZXIgMS4xMiAyMDEzLTA1LTMwIHVwZGF0ZQonJycKZnJvbSBUa2ludGVyIGltcG9ydCAqCmZyb20gbWF0aCBpbXBvcnQgKgpmcm9tIG51bXB5IGltcG9ydCAqCmZyb20gdXJsbGliIGltcG9ydCAqCmZyb20gSFRNTFBhcnNlciBpbXBvcnQgSFRNTFBhcnNlcgppbXBvcnQgZGF0ZXRpbWUKCicnJwrntYznt6/nt5rmj4/nlLsKJycnCmNsYXNzIExhdExvbmc6CiAgICBkZWYgX19pbml0X18oc2VsZiwgUCxCMCxMMCxyLHdpZHRoLGhlaWdodCxhc3RlcCxkc3RlcCxiZyxmYyxsaW5lLGZwYXRoKToKICAgICAgICBzZWxmLnJvb3QgPSBUaygpCiAgICAgICAgc2VsZi5QID0gUAogICAgICAgIHNlbGYuQjAgPSBCMAogICAgICAgIHNlbGYuTDAgPSBMMAogICAgICAgIHNlbGYuciA9IHIKICAgICAgICBzZWxmLndpZHRoID0gd2lkdGgKICAgICAgICBzZWxmLmhlaWdodCA9IGhlaWdodAogICAgICAgIHNlbGYuYXN0ZXAgPSBhc3RlcAogICAgICAgIHNlbGYuZHN0ZXAgPSBkc3RlcAogICAgICAgIHNlbGYuYmFja2NvbG9yID0gYmcKICAgICAgICBzZWxmLmZvcmVjb2xvciA9IGZjCiAgICAgICAgc2VsZi5saW5ld2VpZ2h0ID0gbGluZQogICAgICAgIHNlbGYuZnBhdGggPSBmcGF0aAogICAgICAgIHNlbGYuYzA9Q2FudmFzKHNlbGYucm9vdCwgd2lkdGg9d2lkdGgsIGhlaWdodD1oZWlnaHQsIGJnPWJnKQogICAgICAgIHNlbGYuYzAucGFjaygpCgogICAgIyBa6Lu45Zue6LuiCiAgICBkZWYgcm90WihzZWxmLCBhLCBkKToKICAgICAgICBMID0gY29zKHJhZGlhbnMoZCkpICogY29zKHJhZGlhbnMoYSAtIHNlbGYuTDApKQogICAgICAgIE0gPSBjb3MocmFkaWFucyhkKSkgKiBzaW4ocmFkaWFucyhhIC0gc2VsZi5MMCkpCiAgICAgICAgTiA9IHNpbihyYWRpYW5zKGQpKQogICAgICAgIHJldHVybiBhcnJheShbW0xdLCBbTV0sIFtOXV0pCgogICAgIyBZ6Lu45Zue6LuiCiAgICBkZWYgcm90WShzZWxmLCBMTU4pOgogICAgICAgIG1CMCA9IHJhZGlhbnMoc2VsZi5CMCkgKiAtMQogICAgICAgIHJlcyA9IGFycmF5KFtbXV0pCiAgICAgICAgcmVzID0gYXBwZW5kKHJlcywgW1tjb3MobUIwKSwgMCwgLTEgKiBzaW4obUIwKV1dLCBheGlzPTEpCiAgICAgICAgcmVzID0gYXBwZW5kKHJlcywgW1swLCAxLCAwXV0sIGF4aXM9MCkKICAgICAgICByZXMgPSBhcHBlbmQocmVzLCBbW3NpbihtQjApLCAwLCBjb3MobUIwKV1dLCBheGlzPTApCiAgICAgICAgcmV0dXJuIGRvdChyZXMsIExNTikKICAgIAogICAgIyBY6Lu45Zue6LuiCiAgICBkZWYgcm90WChzZWxmLCBMTU5kKToKICAgICAgICBtUCA9IHJhZGlhbnMoc2VsZi5QKSAqIC0xCiAgICAgICAgcmVzID0gYXJyYXkoW1tdXSkKICAgICAgICByZXMgPSBhcHBlbmQocmVzLCBbWzEsIDAsIDBdXSwgYXhpcz0xKQogICAgICAgIHJlcyA9IGFwcGVuZChyZXMsIFtbMCwgY29zKG1QKSwgc2luKG1QKV1dLCBheGlzPTApCiAgICAgICAgcmVzID0gYXBwZW5kKHJlcywgW1swLCAtMSAqIHNpbihtUCksIGNvcyhtUCldXSwgYXhpcz0wKQogICAgICAgIHJldHVybiBkb3QocmVzLCBMTU5kKQoKICAgICMgWFla6KiI566XCiAgICBkZWYgY2FsWFlaKHNlbGYsIGEsIGQpOgogICAgICAgIExNTiA9IHNlbGYucm90WihhLGQpCiAgICAgICAgTE1OZCA9IHNlbGYucm90WShMTU4pCiAgICAgICAgeHl6ID0gc2VsZi5yb3RYKExNTmQpCgogICAgICAgIHJldHVybiBzdW0oeHl6WzBdKSxzdW0oeHl6WzFdKSxzdW0oeHl6WzJdKQoKICAgICMg5L2N572u6KOc5q2jKFhZWikKICAgIGRlZiBjb3JyUG9zKHNlbGYsIHgsIHksIHopOgogICAgICAgIHggPSB4CiAgICAgICAgeSA9IHkgKiBzZWxmLnIgKyBzZWxmLndpZHRoIC8gMgogICAgICAgIHogPSBzZWxmLmhlaWdodCAvIDIgLSB6ICogc2VsZi5yCiAgICAgICAgcmV0dXJuIHgseSx6CiAgICAgICAgCiAgICAj44CA57ev57eaCiAgICBkZWYgTGF0aXR1ZGUoc2VsZik6CiAgICAgICAgbHBvcz1bXQogICAgICAgIGZvciBkZWx0YSBpbiByYW5nZSgtOTAsOTAsc2VsZi5kc3RlcCk6CiAgICAgICAgICAgIGZvciBhbHBoYSBpbiByYW5nZSgwLCAzNjEsIDUpOgogICAgICAgICAgICAgICAgeHl6ID0gc2VsZi5jYWxYWVooYWxwaGEsIGRlbHRhKQogICAgICAgICAgICAgICAgeCx5LHogPSBzZWxmLmNvcnJQb3MoeHl6WzBdLCB4eXpbMV0sIHh5elsyXSkKICAgICAgICAgICAgICAgIGlmIHg+PTA6CiAgICAgICAgICAgICAgICAgICAgbHBvcy5hcHBlbmQoeSkKICAgICAgICAgICAgICAgICAgICBscG9zLmFwcGVuZCh6KQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBpZiBsZW4obHBvcyk+MjoKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5jMC5jcmVhdGVfbGluZShscG9zLGZpbGw9c2VsZi5mb3JlY29sb3Isd2lkdGg9c2VsZi5saW5ld2VpZ2h0LHNtb290aD1UcnVlKQogICAgICAgICAgICAgICAgICAgIGxwb3NbOl09W10KCiAgICAgICAgICAgIGlmIGxlbihscG9zKT4yOiAKICAgICAgICAgICAgICAgIHNlbGYuYzAuY3JlYXRlX2xpbmUobHBvcyxmaWxsPXNlbGYuZm9yZWNvbG9yLHdpZHRoPXNlbGYubGluZXdlaWdodCxzbW9vdGg9VHJ1ZSkKICAgICAgICAgICAgbHBvc1s6XT1bXQogICAgICAgIAogICAgIyDntYznt5oKICAgIGRlZiBMb25naXR1ZGUoc2VsZik6CiAgICAgICAgbHBvcz1bXQogICAgICAgIGZvciBhbHBoYSBpbiByYW5nZSgwLDM2MCxzZWxmLmFzdGVwKToKICAgICAgICAgICAgZm9yIGRlbHRhIGluIHJhbmdlKC05MCwgOTEsIDUpOgogICAgICAgICAgICAgICAgeHl6ID0gc2VsZi5jYWxYWVooYWxwaGEsIGRlbHRhKQogICAgICAgICAgICAgICAgeCx5LHogPSBzZWxmLmNvcnJQb3MoeHl6WzBdLCB4eXpbMV0sIHh5elsyXSkKICAgICAgICAgICAgICAgIGlmIHg+PTA6CiAgICAgICAgICAgICAgICAgICAgbHBvcy5hcHBlbmQoeSkKICAgICAgICAgICAgICAgICAgICBscG9zLmFwcGVuZCh6KQogICAgICAgICAgICBpZiBsZW4obHBvcyk+MjoKICAgICAgICAgICAgICAgIHNlbGYuYzAuY3JlYXRlX2xpbmUobHBvcyxmaWxsPXNlbGYuZm9yZWNvbG9yLHdpZHRoPXNlbGYubGluZXdlaWdodCxzbW9vdGg9VHJ1ZSkKICAgICAgICAgICAgbHBvc1s6XT1bXQoKICAgICMg5aSW5ZGo5o+P55S7CiAgICBkZWYgZGlzcE91dGVyKHNlbGYpOgogICAgICAgIGxwb3MgPSBbXQogICAgICAgIGZvciBwb3MgaW4gcmFuZ2UoMzYwKToKICAgICAgICAgICAgeSA9IGNvcyhyYWRpYW5zKHBvcykpCiAgICAgICAgICAgIHogPSBzaW4ocmFkaWFucyhwb3MpKQogICAgICAgICAgICB4LHkseiA9IHNlbGYuY29yclBvcygwLCB5LCB6KQoKICAgICAgICAgICAgbHBvcy5hcHBlbmQoeSkKICAgICAgICAgICAgbHBvcy5hcHBlbmQoeikKICAgICAgICBzZWxmLmMwLmNyZWF0ZV9saW5lKGxwb3MsZmlsbD1zZWxmLmZvcmVjb2xvcix3aWR0aD1zZWxmLmxpbmV3ZWlnaHQsc21vb3RoPVRydWUpCiAgICAgICAgbHBvc1s6XSA9IFtdCgogICAgIyBTYXZlIFBvc3RTY3JpcHQKICAgIGRlZiBTYXZlSW1hZ2Uoc2VsZik6CiAgICAgICAgc2VsZi5jMC5wb3N0c2NyaXB0KGZpbGU9c2VsZi5mcGF0aCwgd2lkdGg9c2VsZi53aWR0aCwgaGVpZ2h0PXNlbGYuaGVpZ2h0KQoKICAgICMgTWFpbkxvb3AKICAgIGRlZiBtYWlubG9vcChzZWxmKToKICAgICAgICBzZWxmLnJvb3QubWFpbmxvb3AoKQoKCicnJwrlm73nq4vlpKnmloflj7DjgojjgopQQjBMMOWPluW+lwonJycKY2xhc3MgS0hUTUxQYXJzZXIoSFRNTFBhcnNlcik6CiAgICBobXMgPSAiMDA6MDA6MDAiCiAgICBnZXRmbCA9IEZhbHNlCiAgICBQID0gTm9uZQogICAgQjAgPSBOb25lCiAgICBMMCA9IE5vbmUKICAgIAogICAgZGVmIGhhbmRsZV9kYXRhKHNlbGYsIGRhdGEpOgogICAgICAgIGRhdGEgPSBkYXRhLnN0cmlwKCIgIFx0XHJcbiIpCgogICAgICAgIGlmIGRhdGEgYW5kIHNlbGYuZ2V0Zmw6CiAgICAgICAgICAgIGlmIHNlbGYuUCBhbmQgc2VsZi5CMCBhbmQgc2VsZi5MMCBpcyBOb25lOiBzZWxmLkwwID0gZmxvYXQoZGF0YSkKICAgICAgICAgICAgaWYgc2VsZi5QIGFuZCBzZWxmLkIwIGlzIE5vbmU6IHNlbGYuQjAgPSBmbG9hdChkYXRhKQogICAgICAgICAgICBpZiBzZWxmLlAgaXMgTm9uZTogc2VsZi5QID0gZmxvYXQoZGF0YSkKICAgICAgICAgICAgCiAgICAgICAgaWYgZGF0YS5zdHJpcCgpID09IHNlbGYuaG1zOiBzZWxmLmdldGZsID0gVHJ1ZQoKICAgIGRlZiBoYW5kbGVfc3RhcnR0YWcoc2VsZiwgdGFnLCBhdHRycyk6CiAgICAgICAgcGFzcwoKICAgIGRlZiBoYW5kbGVfZW5kdGFnKHNlbGYsIHRhZyk6CiAgICAgICAgcGFzcwoKCicnJwpNYWluCicnJwppZiBfX25hbWVfXz09J19fbWFpbl9fJzoKICAgICcnJwogICAg5L2/55So5pa55rOVCgogICAg44Kv44Op44K55Yid5pyf5YyWCiAgICBMYXRMb25nKFAsQjAsTDAscix3aWR0aCxoZWlnaHQsYXN0ZXAsZHN0ZXAsYmFja2NvbG9yLGZvcmVjb2xvcixsaW5ld2VpZ2h0LGZwYXRoKQogICAgJycnCiAgICAjIFBCMEww5Y+W5b6XCiAgICBwYXJhbXMgPSB7J3llYXInOjIwMTMsJ21vbnRoJzowNSwnZGF5JzoyMCwnaG91cic6MTAsJ21pbic6MzAsJ3NlYyc6MDB9CgogICAgcj11cmxvcGVuKCdodHRwOi8vZS4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4uYy5qcC9jZ2ktYmluL2tveW9taS9jYW5kZS9zdW5fc3Bpbi5jZ2knLHVybGVuY29kZShwYXJhbXMpKQogICAgbXAgPSBLSFRNTFBhcnNlcigpCiAgICBtcC5obXMgPSAiJTAyZDolMDJkOiUwMmQiICUgKGludChwYXJhbXNbJ2hvdXInXSksIGludChwYXJhbXNbJ21pbiddKSwgaW50KHBhcmFtc1snc2VjJ10pKQogICAgbXAuZmVlZChyLnJlYWQoKSkKCiAgICAjIExhdExvbmfotbfli5UKICAgIGxhdGxvbmcgPSBMYXRMb25nKG1wLlAsbXAuQjAsbXAuTDAsMzAwLCA4MDAsIDgwMCwgMTUsIDE1LCAiYmxhY2siLCAiZ3JlZW4iLCAyLjAsIHIifi9EZXNrdG9wL2JhbGwucHMiKQogICAgCiAgICBsYXRsb25nLkxhdGl0dWRlKCkKICAgIGxhdGxvbmcuTG9uZ2l0dWRlKCkKICAgIGxhdGxvbmcuZGlzcE91dGVyKCkKICAgIGxhdGxvbmcuU2F2ZUltYWdlKCkKICAgIGxhdGxvbmcubWFpbmxvb3AoKQojIC0qLSBjb2Rpbmc6IHV0Zi04IC0qLQonJycK57WM57ev57ea5o+P55S7ICjml6XmmYLmjIflrpopClZlciAxLjExIDIwMTMtMDUtMjkgdXBkYXRlCicnJwpmcm9tIFRraW50ZXIgaW1wb3J0ICoKZnJvbSBtYXRoIGltcG9ydCAqCmZyb20gbnVtcHkgaW1wb3J0ICoKZnJvbSB1cmxsaWIgaW1wb3J0ICoKZnJvbSBIVE1MUGFyc2VyIGltcG9ydCBIVE1MUGFyc2VyCmltcG9ydCBkYXRldGltZQoKJycnCue1jOe3r+e3muaPj+eUuwonJycKY2xhc3MgTGF0TG9uZzoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBQLEIwLEwwLHIsd2lkdGgsaGVpZ2h0LGFzdGVwLGRzdGVwLGJnLGZjLGxpbmUsZnBhdGgpOgogICAgICAgIHNlbGYucm9vdCA9IFRrKCkKICAgICAgICBzZWxmLlAgPSBQCiAgICAgICAgc2VsZi5CMCA9IEIwCiAgICAgICAgc2VsZi5MMCA9IEwwCiAgICAgICAgc2VsZi5yID0gcgogICAgICAgIHNlbGYud2lkdGggPSB3aWR0aAogICAgICAgIHNlbGYuaGVpZ2h0ID0gaGVpZ2h0CiAgICAgICAgc2VsZi5hc3RlcCA9IGFzdGVwCiAgICAgICAgc2VsZi5kc3RlcCA9IGRzdGVwCiAgICAgICAgc2VsZi5iYWNrY29sb3IgPSBiZwogICAgICAgIHNlbGYuZm9yZWNvbG9yID0gZmMKICAgICAgICBzZWxmLmxpbmV3ZWlnaHQgPSBsaW5lCiAgICAgICAgc2VsZi5mcGF0aCA9IGZwYXRoCiAgICAgICAgc2VsZi5jMD1DYW52YXMoc2VsZi5yb290LCB3aWR0aD13aWR0aCwgaGVpZ2h0PWhlaWdodCwgYmc9YmcpCiAgICAgICAgc2VsZi5jMC5wYWNrKCkKCiAgICAjIFrou7jlm57ou6IKICAgIGRlZiByb3RaKHNlbGYsIGEsIGQpOgogICAgICAgIEwgPSBjb3MocmFkaWFucyhkKSkgKiBjb3MocmFkaWFucyhhIC0gc2VsZi5MMCkpCiAgICAgICAgTSA9IGNvcyhyYWRpYW5zKGQpKSAqIHNpbihyYWRpYW5zKGEgLSBzZWxmLkwwKSkKICAgICAgICBOID0gc2luKHJhZGlhbnMoZCkpCiAgICAgICAgcmV0dXJuIGFycmF5KFtbTF0sIFtNXSwgW05dXSkKCiAgICAjIFnou7jlm57ou6IKICAgIGRlZiByb3RZKHNlbGYsIExNTik6CiAgICAgICAgbUIwID0gcmFkaWFucyhzZWxmLkIwKSAqIC0xCiAgICAgICAgcmVzID0gYXJyYXkoW1tdXSkKICAgICAgICByZXMgPSBhcHBlbmQocmVzLCBbW2NvcyhtQjApLCAwLCAtMSAqIHNpbihtQjApXV0sIGF4aXM9MSkKICAgICAgICByZXMgPSBhcHBlbmQocmVzLCBbWzAsIDEsIDBdXSwgYXhpcz0wKQogICAgICAgIHJlcyA9IGFwcGVuZChyZXMsIFtbc2luKG1CMCksIDAsIGNvcyhtQjApXV0sIGF4aXM9MCkKICAgICAgICByZXR1cm4gZG90KHJlcywgTE1OKQogICAgCiAgICAjIFjou7jlm57ou6IKICAgIGRlZiByb3RYKHNlbGYsIExNTmQpOgogICAgICAgIG1QID0gcmFkaWFucyhzZWxmLlApICogLTEKICAgICAgICByZXMgPSBhcnJheShbW11dKQogICAgICAgIHJlcyA9IGFwcGVuZChyZXMsIFtbMSwgMCwgMF1dLCBheGlzPTEpCiAgICAgICAgcmVzID0gYXBwZW5kKHJlcywgW1swLCBjb3MobVApLCBzaW4obVApXV0sIGF4aXM9MCkKICAgICAgICByZXMgPSBhcHBlbmQocmVzLCBbWzAsIC0xICogc2luKG1QKSwgY29zKG1QKV1dLCBheGlzPTApCiAgICAgICAgcmV0dXJuIGRvdChyZXMsIExNTmQpCgogICAgIyBYWVroqIjnrpcKICAgIGRlZiBjYWxYWVooc2VsZiwgYSwgZCk6CiAgICAgICAgTE1OID0gc2VsZi5yb3RaKGEsZCkKICAgICAgICBMTU5kID0gc2VsZi5yb3RZKExNTikKICAgICAgICB4eXogPSBzZWxmLnJvdFgoTE1OZCkKCiAgICAgICAgcmV0dXJuIHN1bSh4eXpbMF0pLHN1bSh4eXpbMV0pLHN1bSh4eXpbMl0pCgogICAgIyDkvY3nva7oo5zmraMoWFlaKQogICAgZGVmIGNvcnJQb3Moc2VsZiwgeCwgeSwgeik6CiAgICAgICAgeCA9IHgKICAgICAgICB5ID0geSAqIHNlbGYuciArIHNlbGYud2lkdGggLyAyCiAgICAgICAgeiA9IHNlbGYuaGVpZ2h0IC8gMiAtIHogKiBzZWxmLnIKICAgICAgICByZXR1cm4geCx5LHoKICAgICAgICAKICAgICPjgIDnt6/nt5oKICAgIGRlZiBMYXRpdHVkZShzZWxmKToKICAgICAgICBscG9zPVtdCiAgICAgICAgZm9yIGRlbHRhIGluIHJhbmdlKC05MCw5MCxzZWxmLmRzdGVwKToKICAgICAgICAgICAgZm9yIGFscGhhIGluIHJhbmdlKDAsIDM2MSwgNSk6CiAgICAgICAgICAgICAgICB4eXogPSBzZWxmLmNhbFhZWihhbHBoYSwgZGVsdGEpCiAgICAgICAgICAgICAgICB4LHkseiA9IHNlbGYuY29yclBvcyh4eXpbMF0sIHh5elsxXSwgeHl6WzJdKQogICAgICAgICAgICAgICAgaWYgeD49MDoKICAgICAgICAgICAgICAgICAgICBscG9zLmFwcGVuZCh5KQogICAgICAgICAgICAgICAgICAgIGxwb3MuYXBwZW5kKHopCiAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgIGlmIGxlbihscG9zKT4yOgogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmMwLmNyZWF0ZV9saW5lKGxwb3MsZmlsbD1zZWxmLmZvcmVjb2xvcix3aWR0aD1zZWxmLmxpbmV3ZWlnaHQsc21vb3RoPVRydWUpCiAgICAgICAgICAgICAgICAgICAgbHBvc1s6XT1bXQoKICAgICAgICAgICAgaWYgbGVuKGxwb3MpPjI6IAogICAgICAgICAgICAgICAgc2VsZi5jMC5jcmVhdGVfbGluZShscG9zLGZpbGw9c2VsZi5mb3JlY29sb3Isd2lkdGg9c2VsZi5saW5ld2VpZ2h0LHNtb290aD1UcnVlKQogICAgICAgICAgICBscG9zWzpdPVtdCiAgICAgICAgCiAgICAjIOe1jOe3mgogICAgZGVmIExvbmdpdHVkZShzZWxmKToKICAgICAgICBscG9zPVtdCiAgICAgICAgZm9yIGFscGhhIGluIHJhbmdlKDAsMzYwLHNlbGYuYXN0ZXApOgogICAgICAgICAgICBmb3IgZGVsdGEgaW4gcmFuZ2UoLTkwLCA5MSwgNSk6CiAgICAgICAgICAgICAgICB4eXogPSBzZWxmLmNhbFhZWihhbHBoYSwgZGVsdGEpCiAgICAgICAgICAgICAgICB4LHkseiA9IHNlbGYuY29yclBvcyh4eXpbMF0sIHh5elsxXSwgeHl6WzJdKQogICAgICAgICAgICAgICAgaWYgeD49MDoKICAgICAgICAgICAgICAgICAgICBscG9zLmFwcGVuZCh5KQogICAgICAgICAgICAgICAgICAgIGxwb3MuYXBwZW5kKHopCiAgICAgICAgICAgIGlmIGxlbihscG9zKT4yOgogICAgICAgICAgICAgICAgc2VsZi5jMC5jcmVhdGVfbGluZShscG9zLGZpbGw9c2VsZi5mb3JlY29sb3Isd2lkdGg9c2VsZi5saW5ld2VpZ2h0LHNtb290aD1UcnVlKQogICAgICAgICAgICBscG9zWzpdPVtdCgogICAgIyDlpJblkajmj4/nlLsKICAgIGRlZiBkaXNwT3V0ZXIoc2VsZik6CiAgICAgICAgbHBvcyA9IFtdCiAgICAgICAgZm9yIHBvcyBpbiByYW5nZSgzNjApOgogICAgICAgICAgICB5ID0gY29zKHJhZGlhbnMocG9zKSkKICAgICAgICAgICAgeiA9IHNpbihyYWRpYW5zKHBvcykpCiAgICAgICAgICAgIHgseSx6ID0gc2VsZi5jb3JyUG9zKDAsIHksIHopCgogICAgICAgICAgICBscG9zLmFwcGVuZCh5KQogICAgICAgICAgICBscG9zLmFwcGVuZCh6KQogICAgICAgIHNlbGYuYzAuY3JlYXRlX2xpbmUobHBvcyxmaWxsPXNlbGYuZm9yZWNvbG9yLHdpZHRoPXNlbGYubGluZXdlaWdodCxzbW9vdGg9VHJ1ZSkKICAgICAgICBscG9zWzpdID0gW10KCiAgICAjIFNhdmUgUG9zdFNjcmlwdAogICAgZGVmIFNhdmVJbWFnZShzZWxmKToKICAgICAgICBzZWxmLmMwLnBvc3RzY3JpcHQoZmlsZT1zZWxmLmZwYXRoLCB3aWR0aD1zZWxmLndpZHRoLCBoZWlnaHQ9c2VsZi5oZWlnaHQpCgogICAgIyBNYWluTG9vcAogICAgZGVmIG1haW5sb29wKHNlbGYpOgogICAgICAgIHNlbGYucm9vdC5tYWlubG9vcCgpCgoKJycnCuWbveeri+WkqeaWh+WPsOOCiOOCilBCMEww5Y+W5b6XCicnJwpjbGFzcyBLSFRNTFBhcnNlcihIVE1MUGFyc2VyKToKICAgIGhtcyA9ICIwMDowMDowMCIKICAgIGdldGZsID0gRmFsc2UKICAgIFAgPSBOb25lCiAgICBCMCA9IE5vbmUKICAgIEwwID0gTm9uZQogICAgCiAgICBkZWYgaGFuZGxlX2RhdGEoc2VsZiwgZGF0YSk6CiAgICAgICAgZGF0YSA9IGRhdGEuc3RyaXAoIiAgXHRcclxuIikKCiAgICAgICAgaWYgZGF0YSBhbmQgc2VsZi5nZXRmbDoKICAgICAgICAgICAgaWYgc2VsZi5QIGFuZCBzZWxmLkIwIGFuZCBzZWxmLkwwIGlzIE5vbmU6IHNlbGYuTDAgPSBmbG9hdChkYXRhKQogICAgICAgICAgICBpZiBzZWxmLlAgYW5kIHNlbGYuQjAgaXMgTm9uZTogc2VsZi5CMCA9IGZsb2F0KGRhdGEpCiAgICAgICAgICAgIGlmIHNlbGYuUCBpcyBOb25lOiBzZWxmLlAgPSBmbG9hdChkYXRhKQogICAgICAgICAgICAKICAgICAgICBpZiBkYXRhLnN0cmlwKCkgPT0gc2VsZi5obXM6IHNlbGYuZ2V0ZmwgPSBUcnVlCgogICAgZGVmIGhhbmRsZV9zdGFydHRhZyhzZWxmLCB0YWcsIGF0dHJzKToKICAgICAgICBwYXNzCgogICAgZGVmIGhhbmRsZV9lbmR0YWcoc2VsZiwgdGFnKToKICAgICAgICBwYXNzCgoKJycnCk1haW4KJycnCmlmIF9fbmFtZV9fPT0nX19tYWluX18nOgogICAgJycnCiAgICDkvb/nlKjmlrnms5UKCiAgICDjgq/jg6njgrnliJ3mnJ/ljJYKICAgIExhdExvbmcoUCxCMCxMMCxyLHdpZHRoLGhlaWdodCxhc3RlcCxkc3RlcCxiYWNrY29sb3IsZm9yZWNvbG9yLGxpbmV3ZWlnaHQsZnBhdGgpCiAgICAnJycKICAgICMgUEIwTDDlj5blvpcKICAgIHBhcmFtcyA9IHsneWVhcic6MjAxMywnbW9udGgnOjA1LCdkYXknOjIwLCdob3VyJzoxMCwnbWluJzozMCwnc2VjJzowMCwgJ3RzeXMnOjJ9CgogICAgcj11cmxvcGVuKCdodHRwOi8vZS4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4uYy5qcC9jZ2ktYmluL2tveW9taS9jYW5kZS9zdW5fc3Bpbi5jZ2knLHVybGVuY29kZShwYXJhbXMpKQogICAgbXAgPSBLSFRNTFBhcnNlcigpCiAgICBtcC5obXMgPSAiJTAyZDolMDJkOiUwMmQiICUgKGludChwYXJhbXNbJ2hvdXInXSksIGludChwYXJhbXNbJ21pbiddKSwgaW50KHBhcmFtc1snc2VjJ10pKQogICAgbXAuZmVlZChyLnJlYWQoKSkKCiAgICAjIExhdExvbmfotbfli5UKICAgIGxhdGxvbmcgPSBMYXRMb25nKG1wLlAsbXAuQjAsbXAuTDAsMzAwLCA4MDAsIDgwMCwgMTUsIDE1LCAiYmxhY2siLCAiZ3JlZW4iLCAyLjAsIHIifi9EZXNrdG9wL2JhbGwucHMiKQogICAgCiAgICBsYXRsb25nLkxhdGl0dWRlKCkKICAgIGxhdGxvbmcuTG9uZ2l0dWRlKCkKICAgIGxhdGxvbmcuZGlzcE91dGVyKCkKICAgIGxhdGxvbmcuU2F2ZUltYWdlKCkKICAgIGxhdGxvbmcubWFpbmxvb3AoKQo=