import re
from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
from random import randint
import pickle
import time
class Dict( ) :
def __init__ ( self ) :
self .mydict = { }
self .currentdict = [ ]
self .current_word = ''
self .current_answer = [ ]
self .life = 0
self .start_time = 0
self .finish_time = 0
self .my_time = 0
def add_item( self , event, enword, ruwords, entry1, entry2) :
en = enword.strip ( )
if re .match ( r'\b [a-z]+\b ' , enword) and re .match ( r'^([А-Яа-я]+\s *,{1}\s *)+([А-Яа-яё]+){1}$|^[А-Яа-яё]+$' , ruwords) :
ru = [ i.strip ( ) for i in ruwords.split ( ',' ) ]
it = { en : ru}
self .mydict .update ( it)
entry1.delete ( 0 , END)
entry2.delete ( 0 , END)
def save_dict( self ) :
name = asksaveasfilename( )
f = open ( name, 'wb' )
pickle .dump ( self .mydict , f)
self .mydict = { }
f.close ( )
def open_dict( self ) :
try :
name = askopenfilename( )
f = open ( name, 'rb' )
self .mydict = pickle .load ( f)
self .currentdict = [ i for i in self .mydict ]
self .life = round ( len ( self .mydict ) * 0.25 ) +1
if mtime.get ( ) == 0 :
self .my_time = len ( self .mydict ) * 9000
else :
self .my_time = len ( self .mydict ) * mtime.get ( )
f.close ( )
except : pass
def get_answer( self , event) :
self .current_answer = ruword.get ( )
if len ( self .current_answer ) == 0 :
pass
else :
try :
right_answer = self .mydict .get ( self .current_word )
if self .current_answer in right_answer:
showrighta.configure ( text= '' )
showrightb.configure ( text= '' )
ruword.delete ( 0 , END)
result.configure ( text= 'Верно' , fg= 'green' )
word = self .currentdict .pop ( randint( 0 , len ( self .currentdict ) -1 ) )
self .current_word = word
enword.delete ( 0 , END)
enword.insert ( END, word)
else :
self .life -= 1
ruword.delete ( 0 , END)
result.configure ( text= 'Неправильно.' , fg= 'red' )
showrighta.configure ( text= 'Возможные варианты: ' )
showrightb.configure ( text= ', ' .join ( self .mydict .get ( self .current_word ) ) .strip ( ', ' ) )
word = self .currentdict .pop ( randint( 0 , len ( self .currentdict ) -1 ) )
self .current_word = word
enword.delete ( 0 , END)
enword.insert ( END, word)
except ValueError :
self .finish_time = round ( time .time ( ) )
ruword.delete ( 0 , END)
ruword.insert ( END, 'Конец словаря' )
if self .life > 0 and self .my_time - ( self .finish_time - self .start_time ) > 0 :
finaltext.configure ( text= 'Отлично! Словарь успешно закреплён.' , fg= 'gold' , bg= 'gray' )
else :
finaltext.configure ( text= 'Вы недостаточно хорошо знаете эти слова.\n Попробуйте снова в другой раз.' , fg= 'blue' , bg= 'red' )
bstudy.configure ( state= 'normal' )
result.configure ( text= '' )
showrighta.configure ( text= '' )
showrightb.configure ( text= '' )
def start( self ) :
finaltext.configure ( text= '' )
if len ( self .currentdict ) < 1 :
self .currentdict = [ i for i in self .mydict ]
word = self .currentdict .pop ( randint( 0 , len ( self .currentdict ) -1 ) )
self .current_word = word
ruword.delete ( 0 , END)
enword.delete ( 0 , END)
enword.insert ( END, word)
bstudy.configure ( state= 'disabled' )
self .start_time = round ( time .time ( ) )
def restart( self ) :
self .start_time = 0
self .finish_time = 0
self .currentdict = [ i for i in self .mydict ]
self .life = round ( len ( self .mydict ) * 0.25 ) +1
self .start ( )
def create_new( self ) :
self .mydict = { }
newin = Toplevel( mywin)
newin.title ( 'Создание словаря' )
newin.geometry ( '600x240' )
newin.resizable ( 0 , 0 )
enterenword = Entry( newin, width= 36 )
enterruswords = Entry( newin, width= 36 )
en = Label( newin, text = 'English word:\n (for example: "script")' , font= 12 )
ru = Label( newin, text = 'Русский(е) эквивалент(ы):\n (например: "сценарий, почерк,\n подлинник")' , font = 12 )
enterruswords.bind ( '<Return>' , lambda event= '<Return>' : self .add_item ( '<Return>' , enterenword.get ( ) , enterruswords.get ( ) , enterenword, enterruswords) )
en.place ( x= 5 , y= 10 )
enterenword.place ( x= 255 , y= 10 )
ru.place ( x= 5 , y= 60 )
enterruswords.place ( x= 255 , y= 60 )
saveme = Button( newin, text= 'Сохранить' , bg= '#9ACEEB' , height= 4 , width= 25 , command= d.save_dict )
add_it = Button( newin, text= 'Добавить' , bg= '#9ACEEB' , height= 2 , width= 8 , command= lambda event= '<Button-1>' : self .add_item ( '<Button-1>' , enterenword.get ( ) , enterruswords.get ( ) , enterenword, enterruswords) )
add_it.place ( x= 500 , y= 50 )
saveme.place ( x= 200 , y= 160 )
def myhelp( ) :
showinfo( title= 'qwertyнекуцй' ,
message= '''Кнопка "Повторить" позволяет ПОВТОРНО упражняться с одним и тем же словарём.\n
Открыв новый словарь, используйте кнопку "Go!"\n
Указав кол-во секунд, вы ограничите допустимое время тренировки кол-вом секунд, умноженным на кол-во слов в вашем словаре\n
Создавая словарь, вводите слова без кавычек, разделяя русские эквиваленты английского слова запятыми.\n
Добавляйте каждую новую позицию в словарь, испольуя кнопку "Добавить", после чего сохраните файл.''' )
d = Dict( )
#
mywin = Tk( )
mywin.resizable ( 0 , 0 )
mywin.geometry ( '900x450' )
mywin.title ( 'en-ru trainer' )
mtime = IntVar( )
chtime = [ 2 , 4 , 6 , 8 , 10 ]
choosetime = OptionMenu( mywin, mtime, *chtime)
result = Label( mywin, text= '' , font = 16 )
showrighta = Label( mywin, text= '' , font= 14 , fg= 'orange' )
showrightb = Label( mywin, text= '' , font= 16 , fg= 'green' )
timetext = Label( text= 'Тренировка на время выключена\n (включить, указав кол-во секунд)' )
enword = Entry( mywin, width= 40 )
ruword = Entry( mywin, width= 40 )
bstudy = Button( mywin, text= 'Go!' , command = d.start , height= 5 , width= 12 , bg= '#ED760E' , fg= 'white' , font= 18 )
bopenfile = Button( text= 'Открыть словарь...' , command= d.open_dict , height= 4 , width= 16 , bg= '#9ACEEB' )
bcreatenewdict = Button( text= 'Создать новый словарь...' , command= d.create_new , bg= '#9ACEEB' )
restart = Button( text= 'Повторить' , command= d.restart , height= 4 , width= 16 , bg= '#9ACEEB' )
ruword.bind ( '<Return>' , d.get_answer )
finaltext = Label( text= '' , font = 14 )
enblahblah = Label( text= 'Английское слово: ' , font= 14 )
rublahblah = Label( text= 'Перевод: ' , font= 14 )
mymenu = Menu( mywin)
mywin.config ( menu= mymenu)
mmain = Menu( mymenu)
mymenu.add_cascade ( label= 'Справка' , menu= mmain)
mmain.add_command ( label= 'Help' , command= Dict.myhelp )
enblahblah.place ( x= 12 , y= 100 )
rublahblah.place ( x= 12 , y= 150 )
enword.place ( x= 200 , y= 100 )
ruword.place ( x= 200 , y= 150 )
bstudy.place ( x= 365 , y= 200 )
bopenfile.place ( x= 700 , y= 50 )
restart.place ( x= 700 , y= 160 )
timetext.place ( x= 680 , y= 270 )
choosetime.place ( x= 755 , y= 310 )
result.place ( x= 300 , y= 35 )
showrighta.place ( x= 80 , y= 70 )
showrightb.place ( x= 250 , y= 70 )
finaltext.place ( x= 110 , y= 330 )
bcreatenewdict.place ( x= 30 , y= 388 )
#
mywin = mainloop( )
aW1wb3J0IHJlCmZyb20gdGtpbnRlciBpbXBvcnQgKgpmcm9tIHRraW50ZXIuZmlsZWRpYWxvZyBpbXBvcnQgKgpmcm9tIHRraW50ZXIubWVzc2FnZWJveCBpbXBvcnQgKgpmcm9tIHJhbmRvbSBpbXBvcnQgcmFuZGludAppbXBvcnQgcGlja2xlCmltcG9ydCB0aW1lCgpjbGFzcyBEaWN0KCk6CiAgICBkZWYgX19pbml0X18oc2VsZik6CiAgICAgICAgc2VsZi5teWRpY3QgPSB7fQogICAgICAgIHNlbGYuY3VycmVudGRpY3QgPSBbXQogICAgICAgIHNlbGYuY3VycmVudF93b3JkID0gJycKICAgICAgICBzZWxmLmN1cnJlbnRfYW5zd2VyID0gW10KICAgICAgICBzZWxmLmxpZmUgPSAwCiAgICAgICAgc2VsZi5zdGFydF90aW1lID0gMAogICAgICAgIHNlbGYuZmluaXNoX3RpbWUgPSAwCiAgICAgICAgc2VsZi5teV90aW1lID0gMAogICAgICAgIAogICAgZGVmIGFkZF9pdGVtKHNlbGYsZXZlbnQsIGVud29yZCxydXdvcmRzLGVudHJ5MSxlbnRyeTIpOgogICAgICAgIGVuID0gZW53b3JkLnN0cmlwKCkKICAgICAgICBpZiByZS5tYXRjaChyJ1xiW2Etel0rXGInLCBlbndvcmQpIGFuZCByZS5tYXRjaChyJ14oW9CQLdCv0LAt0Y9dK1xzKix7MX1ccyopKyhb0JAt0K/QsC3Rj9GRXSspezF9JHxeW9CQLdCv0LAt0Y/RkV0rJCcscnV3b3Jkcyk6CiAgICAgICAgICAgIHJ1ID0gW2kuc3RyaXAoKSBmb3IgaSBpbiBydXdvcmRzLnNwbGl0KCcsJyldCiAgICAgICAgICAgIGl0ID0ge2VuIDogcnV9CiAgICAgICAgICAgIHNlbGYubXlkaWN0LnVwZGF0ZShpdCkKICAgICAgICAgICAgZW50cnkxLmRlbGV0ZSgwLEVORCkKICAgICAgICAgICAgZW50cnkyLmRlbGV0ZSgwLEVORCkKCiAgICBkZWYgc2F2ZV9kaWN0KHNlbGYpOgogICAgICAgIG5hbWUgPSBhc2tzYXZlYXNmaWxlbmFtZSgpCiAgICAgICAgZiA9IG9wZW4obmFtZSwgJ3diJykKICAgICAgICBwaWNrbGUuZHVtcChzZWxmLm15ZGljdCxmKQogICAgICAgIHNlbGYubXlkaWN0ID0ge30KICAgICAgICBmLmNsb3NlKCkKCiAgICBkZWYgb3Blbl9kaWN0KHNlbGYpOgogICAgICAgIHRyeToKICAgICAgICAgICAgbmFtZSA9IGFza29wZW5maWxlbmFtZSgpCiAgICAgICAgICAgIGYgPSBvcGVuKG5hbWUsJ3JiJykKICAgICAgICAgICAgc2VsZi5teWRpY3QgPSBwaWNrbGUubG9hZChmKQogICAgICAgICAgICBzZWxmLmN1cnJlbnRkaWN0ID0gW2kgZm9yIGkgaW4gc2VsZi5teWRpY3RdCiAgICAgICAgICAgIHNlbGYubGlmZSA9IHJvdW5kKGxlbihzZWxmLm15ZGljdCkgKiAwLjI1KSsxCiAgICAgICAgICAgIGlmIG10aW1lLmdldCgpID09IDA6CiAgICAgICAgICAgICAgICBzZWxmLm15X3RpbWUgPSBsZW4oc2VsZi5teWRpY3QpICogOTAwMAogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgc2VsZi5teV90aW1lID0gbGVuKHNlbGYubXlkaWN0KSAqIG10aW1lLmdldCgpCiAgICAgICAgICAgIGYuY2xvc2UoKQogICAgICAgIGV4Y2VwdDogcGFzcwoKICAgIGRlZiBnZXRfYW5zd2VyKHNlbGYsZXZlbnQpOgogICAgICAgIHNlbGYuY3VycmVudF9hbnN3ZXIgPSBydXdvcmQuZ2V0KCkKICAgICAgICBpZiBsZW4oc2VsZi5jdXJyZW50X2Fuc3dlcikgPT0gMDoKICAgICAgICAgICAgcGFzcwogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHJpZ2h0X2Fuc3dlciA9IHNlbGYubXlkaWN0LmdldChzZWxmLmN1cnJlbnRfd29yZCkKICAgICAgICAgICAgICAgIGlmIHNlbGYuY3VycmVudF9hbnN3ZXIgaW4gcmlnaHRfYW5zd2VyOgogICAgICAgICAgICAgICAgICAgIHNob3dyaWdodGEuY29uZmlndXJlKHRleHQ9JycpCiAgICAgICAgICAgICAgICAgICAgc2hvd3JpZ2h0Yi5jb25maWd1cmUodGV4dD0nJykKICAgICAgICAgICAgICAgICAgICBydXdvcmQuZGVsZXRlKDAsRU5EKQogICAgICAgICAgICAgICAgICAgIHJlc3VsdC5jb25maWd1cmUodGV4dD0gJ9CS0LXRgNC90L4nLGZnPSdncmVlbicpCiAgICAgICAgICAgICAgICAgICAgd29yZCA9IHNlbGYuY3VycmVudGRpY3QucG9wKHJhbmRpbnQoMCxsZW4oc2VsZi5jdXJyZW50ZGljdCktMSkpCiAgICAgICAgICAgICAgICAgICAgc2VsZi5jdXJyZW50X3dvcmQgPSB3b3JkCiAgICAgICAgICAgICAgICAgICAgZW53b3JkLmRlbGV0ZSgwLEVORCkKICAgICAgICAgICAgICAgICAgICBlbndvcmQuaW5zZXJ0KEVORCx3b3JkKQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBzZWxmLmxpZmUtPTEKICAgICAgICAgICAgICAgICAgICBydXdvcmQuZGVsZXRlKDAsRU5EKQogICAgICAgICAgICAgICAgICAgIHJlc3VsdC5jb25maWd1cmUodGV4dD0gJ9Cd0LXQv9GA0LDQstC40LvRjNC90L4uJyxmZz0ncmVkJykKICAgICAgICAgICAgICAgICAgICBzaG93cmlnaHRhLmNvbmZpZ3VyZSh0ZXh0PSfQktC+0LfQvNC+0LbQvdGL0LUg0LLQsNGA0LjQsNC90YLRizogJykKICAgICAgICAgICAgICAgICAgICBzaG93cmlnaHRiLmNvbmZpZ3VyZSh0ZXh0PSAnLCAnLmpvaW4oc2VsZi5teWRpY3QuZ2V0KHNlbGYuY3VycmVudF93b3JkKSkuc3RyaXAoJywgJykpCiAgICAgICAgICAgICAgICAgICAgd29yZCA9IHNlbGYuY3VycmVudGRpY3QucG9wKHJhbmRpbnQoMCxsZW4oc2VsZi5jdXJyZW50ZGljdCktMSkpCiAgICAgICAgICAgICAgICAgICAgc2VsZi5jdXJyZW50X3dvcmQgPSB3b3JkCiAgICAgICAgICAgICAgICAgICAgZW53b3JkLmRlbGV0ZSgwLEVORCkKICAgICAgICAgICAgICAgICAgICBlbndvcmQuaW5zZXJ0KEVORCx3b3JkKQogICAgICAgICAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICAgICAgICAgIHNlbGYuZmluaXNoX3RpbWUgPSByb3VuZCh0aW1lLnRpbWUoKSkKICAgICAgICAgICAgICAgIHJ1d29yZC5kZWxldGUoMCxFTkQpCiAgICAgICAgICAgICAgICBydXdvcmQuaW5zZXJ0KEVORCwgJ9Ca0L7QvdC10YYg0YHQu9C+0LLQsNGA0Y8nKQogICAgICAgICAgICAgICAgaWYgc2VsZi5saWZlID4gMCBhbmQgc2VsZi5teV90aW1lIC0gKHNlbGYuZmluaXNoX3RpbWUgLSBzZWxmLnN0YXJ0X3RpbWUpID4gMDoKICAgICAgICAgICAgICAgICAgICBmaW5hbHRleHQuY29uZmlndXJlKHRleHQ9ICfQntGC0LvQuNGH0L3QviEg0KHQu9C+0LLQsNGA0Ywg0YPRgdC/0LXRiNC90L4g0LfQsNC60YDQtdC/0LvRkdC9LicsZmc9J2dvbGQnLGJnPSdncmF5JykKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgZmluYWx0ZXh0LmNvbmZpZ3VyZSh0ZXh0PSAn0JLRiyDQvdC10LTQvtGB0YLQsNGC0L7Rh9C90L4g0YXQvtGA0L7RiNC+INC30L3QsNC10YLQtSDRjdGC0Lgg0YHQu9C+0LLQsC5cbiDQn9C+0L/RgNC+0LHRg9C50YLQtSDRgdC90L7QstCwINCyINC00YDRg9Cz0L7QuSDRgNCw0LcuJyxmZz0nYmx1ZScsYmc9J3JlZCcpCiAgICAgICAgICAgICAgICBic3R1ZHkuY29uZmlndXJlKHN0YXRlPSdub3JtYWwnKQogICAgICAgICAgICAgICAgcmVzdWx0LmNvbmZpZ3VyZSh0ZXh0PScnKQogICAgICAgICAgICAgICAgc2hvd3JpZ2h0YS5jb25maWd1cmUodGV4dD0nJykKICAgICAgICAgICAgICAgIHNob3dyaWdodGIuY29uZmlndXJlKHRleHQ9JycpCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgZGVmIHN0YXJ0KHNlbGYpOgogICAgICAgIGZpbmFsdGV4dC5jb25maWd1cmUodGV4dD0gJycpCiAgICAgICAgaWYgbGVuKHNlbGYuY3VycmVudGRpY3QpIDwgMToKICAgICAgICAgICAgc2VsZi5jdXJyZW50ZGljdCA9IFtpIGZvciBpIGluIHNlbGYubXlkaWN0XQogICAgICAgIHdvcmQgPSBzZWxmLmN1cnJlbnRkaWN0LnBvcChyYW5kaW50KDAsbGVuKHNlbGYuY3VycmVudGRpY3QpLTEpKQogICAgICAgIHNlbGYuY3VycmVudF93b3JkID0gd29yZAogICAgICAgIHJ1d29yZC5kZWxldGUoMCxFTkQpCiAgICAgICAgZW53b3JkLmRlbGV0ZSgwLEVORCkKICAgICAgICBlbndvcmQuaW5zZXJ0KEVORCx3b3JkKQogICAgICAgIGJzdHVkeS5jb25maWd1cmUoc3RhdGU9J2Rpc2FibGVkJykKICAgICAgICBzZWxmLnN0YXJ0X3RpbWUgPSByb3VuZCh0aW1lLnRpbWUoKSkKCiAgICBkZWYgcmVzdGFydChzZWxmKToKICAgICAgICBzZWxmLnN0YXJ0X3RpbWUgPSAwCiAgICAgICAgc2VsZi5maW5pc2hfdGltZSA9IDAKICAgICAgICBzZWxmLmN1cnJlbnRkaWN0ID0gW2kgZm9yIGkgaW4gc2VsZi5teWRpY3RdCiAgICAgICAgc2VsZi5saWZlID0gcm91bmQobGVuKHNlbGYubXlkaWN0KSAqIDAuMjUpKzEKICAgICAgICBzZWxmLnN0YXJ0KCkKICAgICAgICAKICAgIGRlZiBjcmVhdGVfbmV3KHNlbGYpOgogICAgICAgIHNlbGYubXlkaWN0ID0ge30KICAgICAgICBuZXdpbiA9IFRvcGxldmVsKG15d2luKQogICAgICAgIG5ld2luLnRpdGxlKCfQodC+0LfQtNCw0L3QuNC1INGB0LvQvtCy0LDRgNGPJykKICAgICAgICBuZXdpbi5nZW9tZXRyeSgnNjAweDI0MCcpCiAgICAgICAgbmV3aW4ucmVzaXphYmxlKDAsMCkKICAgICAgICBlbnRlcmVud29yZCA9IEVudHJ5KG5ld2luLHdpZHRoPTM2KQogICAgICAgIGVudGVycnVzd29yZHMgPSBFbnRyeShuZXdpbix3aWR0aD0zNikKICAgICAgICBlbiA9IExhYmVsKG5ld2luLHRleHQgPSAnRW5nbGlzaCB3b3JkOlxuIChmb3IgZXhhbXBsZTogInNjcmlwdCIpJyxmb250PTEyKQogICAgICAgIHJ1ID0gTGFiZWwobmV3aW4sIHRleHQgPSAn0KDRg9GB0YHQutC40Lko0LUpINGN0LrQstC40LLQsNC70LXQvdGCKNGLKTpcbiAo0L3QsNC/0YDQuNC80LXRgDogItGB0YbQtdC90LDRgNC40LksINC/0L7Rh9C10YDQuixcbtC/0L7QtNC70LjQvdC90LjQuiIpJywgZm9udCA9IDEyKQogICAgICAgIGVudGVycnVzd29yZHMuYmluZCgnPFJldHVybj4nLCBsYW1iZGEgZXZlbnQ9JzxSZXR1cm4+JyA6IHNlbGYuYWRkX2l0ZW0oJzxSZXR1cm4+JywgZW50ZXJlbndvcmQuZ2V0KCksIGVudGVycnVzd29yZHMuZ2V0KCksZW50ZXJlbndvcmQsZW50ZXJydXN3b3JkcykpCiAgICAgICAgZW4ucGxhY2UoeD01LHk9MTApCiAgICAgICAgZW50ZXJlbndvcmQucGxhY2UoeD0yNTUseT0xMCkKICAgICAgICBydS5wbGFjZSh4PTUseT02MCkKICAgICAgICBlbnRlcnJ1c3dvcmRzLnBsYWNlKHg9MjU1LHk9NjApCiAgICAgICAgc2F2ZW1lID0gQnV0dG9uKG5ld2luLHRleHQ9J9Ch0L7RhdGA0LDQvdC40YLRjCcsYmc9JyM5QUNFRUInLGhlaWdodD00LHdpZHRoPTI1LGNvbW1hbmQ9ZC5zYXZlX2RpY3QpCiAgICAgICAgYWRkX2l0ID0gQnV0dG9uKG5ld2luLHRleHQ9J9CU0L7QsdCw0LLQuNGC0YwnLGJnPScjOUFDRUVCJyxoZWlnaHQ9Mix3aWR0aD04LCBjb21tYW5kPSBsYW1iZGEgZXZlbnQ9JzxCdXR0b24tMT4nIDogc2VsZi5hZGRfaXRlbSgnPEJ1dHRvbi0xPicsIGVudGVyZW53b3JkLmdldCgpLCBlbnRlcnJ1c3dvcmRzLmdldCgpLGVudGVyZW53b3JkLGVudGVycnVzd29yZHMpKQogICAgICAgIGFkZF9pdC5wbGFjZSh4PTUwMCx5PTUwKQogICAgICAgIHNhdmVtZS5wbGFjZSh4PTIwMCx5PTE2MCkKCiAgICBkZWYgbXloZWxwKCk6CiAgICAgICAgc2hvd2luZm8odGl0bGU9J3F3ZXJ0edC90LXQutGD0YbQuScsCiAgICAgICAgICAgICAgICAgbWVzc2FnZT0nJyfQmtC90L7Qv9C60LAgItCf0L7QstGC0L7RgNC40YLRjCIg0L/QvtC30LLQvtC70Y/QtdGCINCf0J7QktCi0J7QoNCd0J4g0YPQv9GA0LDQttC90Y/RgtGM0YHRjyAg0YEg0L7QtNC90LjQvCDQuCDRgtC10Lwg0LbQtSDRgdC70L7QstCw0YDRkdC8LlxuCtCe0YLQutGA0YvQsiDQvdC+0LLRi9C5INGB0LvQvtCy0LDRgNGMLCDQuNGB0L/QvtC70YzQt9GD0LnRgtC1INC60L3QvtC/0LrRgyAiR28hIlxuCtCj0LrQsNC30LDQsiDQutC+0Lst0LLQviDRgdC10LrRg9C90LQsINCy0Ysg0L7Qs9GA0LDQvdC40YfQuNGC0LUg0LTQvtC/0YPRgdGC0LjQvNC+0LUg0LLRgNC10LzRjyDRgtGA0LXQvdC40YDQvtCy0LrQuCDQutC+0Lst0LLQvtC8INGB0LXQutGD0L3QtCwg0YPQvNC90L7QttC10L3QvdGL0Lwg0L3QsCDQutC+0Lst0LLQviDRgdC70L7QsiDQsiDQstCw0YjQtdC8INGB0LvQvtCy0LDRgNC1XG4K0KHQvtC30LTQsNCy0LDRjyDRgdC70L7QstCw0YDRjCwg0LLQstC+0LTQuNGC0LUg0YHQu9C+0LLQsCDQsdC10Lcg0LrQsNCy0YvRh9C10LosINGA0LDQt9C00LXQu9GP0Y8g0YDRg9GB0YHQutC40LUg0Y3QutCy0LjQstCw0LvQtdC90YLRiyDQsNC90LPQu9C40LnRgdC60L7Qs9C+INGB0LvQvtCy0LAg0LfQsNC/0Y/RgtGL0LzQuC5cbgrQlNC+0LHQsNCy0LvRj9C50YLQtSDQutCw0LbQtNGD0Y4g0L3QvtCy0YPRjiDQv9C+0LfQuNGG0LjRjiDQsiDRgdC70L7QstCw0YDRjCwg0LjRgdC/0L7Qu9GM0YPRjyDQutC90L7Qv9C60YMgItCU0L7QsdCw0LLQuNGC0YwiLCDQv9C+0YHQu9C1INGH0LXQs9C+INGB0L7RhdGA0LDQvdC40YLQtSDRhNCw0LnQuy4nJycpCmQgPSBEaWN0KCkKIwpteXdpbiA9IFRrKCkKbXl3aW4ucmVzaXphYmxlKDAsMCkKbXl3aW4uZ2VvbWV0cnkoJzkwMHg0NTAnKQpteXdpbi50aXRsZSgnZW4tcnUgdHJhaW5lcicpCm10aW1lID0gSW50VmFyKCkKY2h0aW1lID0gWzIsNCw2LDgsMTBdCmNob29zZXRpbWUgPSBPcHRpb25NZW51KG15d2luLCBtdGltZSwgKmNodGltZSkKcmVzdWx0ID0gTGFiZWwobXl3aW4sdGV4dD0nJyxmb250ID0gMTYpCnNob3dyaWdodGEgPSBMYWJlbChteXdpbiwgdGV4dD0nJyxmb250PTE0LCBmZz0nb3JhbmdlJykKc2hvd3JpZ2h0YiA9IExhYmVsKG15d2luLCB0ZXh0PScnLGZvbnQ9MTYsIGZnPSdncmVlbicpCgp0aW1ldGV4dCA9IExhYmVsKHRleHQ9J9Ci0YDQtdC90LjRgNC+0LLQutCwINC90LAg0LLRgNC10LzRjyDQstGL0LrQu9GO0YfQtdC90LBcbijQstC60LvRjtGH0LjRgtGMLCDRg9C60LDQt9Cw0LIg0LrQvtC7LdCy0L4g0YHQtdC60YPQvdC0KScpCmVud29yZCA9IEVudHJ5KG15d2luLHdpZHRoPTQwKQpydXdvcmQgPSBFbnRyeShteXdpbix3aWR0aD00MCkKYnN0dWR5ID0gQnV0dG9uKG15d2luLHRleHQ9J0dvIScsY29tbWFuZCA9IGQuc3RhcnQsaGVpZ2h0PTUsd2lkdGg9MTIsYmc9JyNFRDc2MEUnLGZnPSd3aGl0ZScsZm9udD0xOCkKYm9wZW5maWxlID0gQnV0dG9uKHRleHQ9J9Ce0YLQutGA0YvRgtGMINGB0LvQvtCy0LDRgNGMLi4uJywgY29tbWFuZD0gZC5vcGVuX2RpY3QsaGVpZ2h0PTQsd2lkdGg9MTYsYmc9JyM5QUNFRUInKQpiY3JlYXRlbmV3ZGljdCA9IEJ1dHRvbih0ZXh0PSfQodC+0LfQtNCw0YLRjCDQvdC+0LLRi9C5INGB0LvQvtCy0LDRgNGMLi4uJyxjb21tYW5kPSBkLmNyZWF0ZV9uZXcsYmc9JyM5QUNFRUInKQpyZXN0YXJ0ID0gQnV0dG9uKHRleHQ9J9Cf0L7QstGC0L7RgNC40YLRjCcsIGNvbW1hbmQ9ZC5yZXN0YXJ0LGhlaWdodD00LHdpZHRoPTE2LGJnPScjOUFDRUVCJykKcnV3b3JkLmJpbmQoJzxSZXR1cm4+JyxkLmdldF9hbnN3ZXIpCmZpbmFsdGV4dCA9IExhYmVsKHRleHQ9JycsZm9udCA9IDE0KQplbmJsYWhibGFoID0gTGFiZWwodGV4dD0n0JDQvdCz0LvQuNC50YHQutC+0LUg0YHQu9C+0LLQvjogJyxmb250PTE0KQpydWJsYWhibGFoID0gTGFiZWwodGV4dD0n0J/QtdGA0LXQstC+0LQ6ICcsZm9udD0xNCkKbXltZW51ID0gTWVudShteXdpbikKbXl3aW4uY29uZmlnKG1lbnU9bXltZW51KQptbWFpbiA9IE1lbnUobXltZW51KQpteW1lbnUuYWRkX2Nhc2NhZGUobGFiZWw9J9Ch0L/RgNCw0LLQutCwJyxtZW51PW1tYWluKQptbWFpbi5hZGRfY29tbWFuZChsYWJlbD0nSGVscCcsIGNvbW1hbmQ9RGljdC5teWhlbHApCmVuYmxhaGJsYWgucGxhY2UoeD0xMix5PTEwMCkKcnVibGFoYmxhaC5wbGFjZSh4PTEyLHk9MTUwKQplbndvcmQucGxhY2UoeD0yMDAseT0xMDApCnJ1d29yZC5wbGFjZSh4PTIwMCx5PTE1MCkKYnN0dWR5LnBsYWNlKHg9MzY1LHk9MjAwKQpib3BlbmZpbGUucGxhY2UoeD03MDAseT01MCkKcmVzdGFydC5wbGFjZSh4PTcwMCx5PTE2MCkKdGltZXRleHQucGxhY2UoeD02ODAseT0yNzApCmNob29zZXRpbWUucGxhY2UoeD03NTUseT0zMTApCnJlc3VsdC5wbGFjZSh4PTMwMCx5PTM1KQpzaG93cmlnaHRhLnBsYWNlKHg9ODAseT03MCkKc2hvd3JpZ2h0Yi5wbGFjZSh4PSAyNTAseT03MCkKZmluYWx0ZXh0LnBsYWNlKHg9MTEwLCB5PTMzMCkKYmNyZWF0ZW5ld2RpY3QucGxhY2UoeD0zMCx5PTM4OCkKIwpteXdpbiA9IG1haW5sb29wKCkK