from re import compile
from ast import literal_eval
from functools import reduce
from collections import Iterator, deque, namedtuple
Location = namedtuple( 'Location' , 'start, end, filename, first_line' )
class StructMixIn:
infix = False
closed = False
indented = False
def after( self , other) :
self .location = Location(
other.location .end ,
other.location .end ,
other.location .filename ,
other.location .first_line
)
return self
def at( self , stream, start) :
self .location = Location(
stream.position ( start) ,
stream.position ( stream.offset ) ,
stream.filename ,
stream.line ( start)
)
return self
class Expression ( list , StructMixIn) :
def __repr__ ( self ) :
return ( '({})' if self .closed else '{}' ) .format (
' ' .join ( map ( repr , self [ 1 :] ) ) if self [ 0 ] == '' else
'{1}{0}{2}' .format ( *self ) if len ( self ) == 3 and self [ 0 ] == '.' else
'{1} {0} {2}' .format ( *self ) if len ( self ) == 3 else
'{1} {0}' .format ( *self ) if len ( self ) == 2 else
'({}) {}' .format ( self [ 0 ] , ' ' .join ( map ( repr , self [ 1 :] ) ) )
)
class Link ( str , StructMixIn) :
def __new__ ( cls, data, infix= False ) :
obj = str .__new__ ( cls, data)
obj.infix = bool ( infix)
return obj
def __repr__ ( self ) :
return self
class Constant ( StructMixIn) :
def __init__ ( self , value) :
super ( ) .__init__ ( )
self .value = value
def __repr__ ( self ) :
return repr ( self .value )
class Internal ( Constant) :
pass
# In all comments below, `R` and `Q` are infix links, while lowercase letters
# are arbitrary expressions.
has_priority = ( lambda f: lambda a, b: f( a) [ 0 ] > f( b) [ 1 ] ) ( lambda m, g= {
# `a R b Q c` <=> `a R (b Q c)` if left binding strength of `Q`
# is higher than right binding strength of `R`.
'' : ( -2 , -2 ) , # call with an argument
'^' : ( -3 , -4 ) , # exponentiation
'*' : ( -5 , -5 ) , # multiplication
'/' : ( -5 , -5 ) , # fp division
'//' : ( -5 , -5 ) , # int division
'%' : ( -5 , -5 ) , # modulus
'+' : ( -6 , -6 ) , # addition
'-' : ( -6 , -6 ) , # subtraction
} .get : g( m, ( -7 , -7 ) ) ) # Default
# If `unassoc(R)`: `a R b R c` <=> `Expression [ Link R, Link a, Link b, Link c]`
# Otherwise: `a R b R c` <=> `Expression [ Link R, Expression [...], Link c]`
# (This doesn't apply to right-fixed links.)
unassoc = { ',' , '..' , '::' , '' , '\n ' } .__contains__
# These operators have no right-hand statement part in any case.
unary = { '!' } .__contains__
# infixl :: (State, StructMixIn, Link, StructMixIn) -> StructMixIn
#
# Handle an infix expression given its all three parts.
#
# If that particular expression has no right-hand statement part,
# `rhs` will be pushed to the object queue.
#
def infixl( stream, lhs, op, rhs) :
break_ = False
postfixl = lambda : ( lhs if op in '\n ' else infixl_insert_rhs( lhs, op, None ) )
while rhs == '\n ' and not unary( op) :
break_, rhs = rhs, next( stream)
if isinstance ( rhs, Internal) or unary( op) : # `(a R)`
stream.appendleft ( rhs)
return postfixl( )
if break_ and op == '' and rhs.indented :
# a
# b <=> a b (c d)
# c d
return reduce (
lambda lhs, rhs: infixl_insert_rhs( lhs, op, rhs) ,
rhs[ 1 :] if isinstance ( rhs, Expression) and rhs[ 0 ] == '\n ' else [ rhs] , lhs
)
if break_ and op != '\n ' and not rhs.indented : # `a R`
return infixl( stream, postfixl( ) , break_, rhs)
if not rhs.closed and rhs.infix and ( op == '' or has_priority( op, rhs) ) :
# `a R Q b` <=> `(a R) Q b` if R is prioritized over Q or R is empty.
return infixl( stream, postfixl( ) , rhs, next( stream) )
return infixl_insert_rhs( lhs, op, rhs)
# infixl_insert_rhs :: (StructMixIn, Link, StructMixIn) -> StructMixIn
#
# Recursively descends into `root` if infix precedence rules allow it to,
# otherwise simply creates and returns a new infix expression AST node.
#
def infixl_insert_rhs( root, op, rhs) :
if isinstance ( root, Expression) and not root.closed :
if has_priority( op, root[ 0 ] ) :
# `a R b Q c` <=> `a R (b Q c)` if Q is prioritized over R
root.append ( infixl_insert_rhs( root.pop ( ) , op, rhs) )
return root
elif op == root[ 0 ] and unassoc( op) and rhs is not None :
root.append ( rhs) # `a R b R c` <=> `R a b c`
return root
# `R` <=> `Link R`
# `R rhs` <=> `Expression [ Link '', Link R, Link rhs ]`
# `lhs R` <=> `Expression [ Link R, Link lhs ]`
# `lhs R rhs` <=> `Expression [ Link R, Link lhs, Link rhs ]`
e = Expression( [ op, root] if rhs is None else [ op, root, rhs] )
e.closed = rhs is None
e.location = Location(
root.location .start ,
e[ -( not e.closed ) ] .location .end ,
root.location .filename ,
root.location .first_line
)
return e
def indent( stream, token ) :
indent = len ( token .group ( ) )
if indent > stream.indent [ -1 ] :
stream.indent .append ( indent)
block = do( stream, token , { '' } , preserve_close_state= True )
block.indented = True
return block
while indent != stream.indent [ -1 ] :
stream.indent .pop ( ) < 0 and stream.error ( 'no matching indentation level' , token .start ( ) )
stream.appendleft ( Link( '\n ' , True ) .at ( stream, token .end ( ) ) )
stream.appendleft ( Internal( '' ) .at ( stream, token .end ( ) ) )
def whitespace( stream, token ) : pass
def number( stream, token ) :
return Constant( literal_eval( token .group ( ) ) )
def string ( stream, token ) :
g = token .group ( 2 ) * ( 4 - len ( token .group ( 2 ) ) )
q = '' .join ( sorted ( set ( token .group ( 1 ) ) ) )
return Constant( literal_eval( '' .join ( [ q, g, token .group ( 3 ) , g] ) ) )
def link( stream, token , infixn= { 'if' , 'else' , 'or' , 'and' , 'in' , 'is' , 'where' } ) :
infix = token .group ( 2 ) or token .group ( 3 ) or token .group ( 4 )
return Link( infix or token .group ( ) , infix or ( token .group ( ) in infixn) )
def do( stream, token , ends= { ')' } , preserve_close_state= False ) :
object = Constant( None ) .at ( stream, token .end ( ) )
can_join = False
for item in stream:
if isinstance ( item, Internal) :
item.value in ends or stream.error (
'unexpected block delimiter' if item.value else
'unexpected EOF' if stream.offset >= len ( stream.buffer ) else
'unexpected dedent' , item.location .start [ 0 ]
)
break
elif can_join:
# Two objects in a row should be joined with an empty infix link.
object = infixl( stream, object , Link( '' , True ) .after ( object ) , item)
# Ignore line feeds directly following an opening parentheses.
elif item != '\n ' :
object , can_join = item, True
object .closed |= not preserve_close_state
return object
def end( stream, token ) :
return Internal( token .group ( ) )
def string_err( stream, token ) :
stream.error ( 'unexpected EOF while reading a string literal' , token .start ( ) )
def error( stream, token ) :
stream.error ( 'invalid input' , token .start ( ) )
def R( func, expr) :
return func, compile ( expr) .match
class it ( Iterator, deque) :
tokens = [ R( indent, r' *' ) ] , [
R( whitespace, r'[^\S \n ]+|\s *#.*' )
, R( number, r'(?i)[+-]?(?:0b[0-1]+|0o[0-7]+|0x[0-9a-f]+|\d +(?:\. \d +)?(?:e[+-]?\d +)?j?)' )
, R( string , r'(?s)([br]*)(\' \' \' |"""|"|\' )((?:\\ ?.)*?)\2 ' )
, R( link, r"(\w +'*|\* +(?=:)|([!$%&*+\- -/:<-@\\ ^|~;]+|,+))|\s *(\n )|`(\w +'*)`" )
, R( do, r'\( ' )
, R( end, r'\) |$' )
, R( string_err, r'''['"]''' )
, R( error, r'.' )
]
def __new__ ( cls, data, filename= '<string>' ) :
self = super ( it, cls) .__new__ ( cls)
self .filename = filename
self .buffer = data
self .offset = 0
self .indent = deque( [ -1 ] )
self .nextset = self .tokens [ 0 ]
return next( self )
def __next__( self ) :
while not self :
f, match = next( ( f, match)
for f, re in self .nextset
for match in [ re ( self .buffer , self .offset ) ] if match
)
self .offset = match.end ( )
self .nextset = self .tokens [ not match.group ( ) .endswith ( '\n ' ) ]
q = f( self , match)
if q is not None :
return q.at ( self , match.start ( ) )
return self .popleft ( )
# position :: int -> (int, int, int)
#
# Given a character offset, get an (offset, lineno, charno) triple.
#
def position( self , offset) :
return ( offset,
1 + self .buffer .count ( '\n ' , 0 , offset) ,
offset - self .buffer .rfind ( '\n ' , 0 , offset) )
# error :: (str, int) -> _|_
#
# Raise a `SyntaxError` at a given offset.
#
def error( self , description, at) :
_, lineno, charno = self .position ( at)
raise SyntaxError ( description, ( self .filename , lineno, charno, self .line ( at) ) )
# line :: int -> str
#
# Get a line which contains the character at a given offset.
#
def line( self , offset) :
return self .buffer [
self .buffer .rfind ( '\n ' , 0 , offset) + 1 or None :
self .buffer . find ( '\n ' , offset) + 1 or None
]
##########################
import fractions as fr
import operator as op
ns = { '' : lambda f, *xs: f( *xs) , '^' : op.pow , '*' : op.mul , '/' : fr.Fraction , '//' : op.truediv , '%' : op.mod , '+' : op.add , '-' : op.sub }
def eval ( x) :
if isinstance ( x, Link) : return ns[ x]
if isinstance ( x, Constant) : return x.value
if isinstance ( x, Expression) : return call( *x)
raise TypeError ( 'invalid AST node' , x)
def call( f, *args, rightbind= False ) :
return f if not args else call( *args, rightbind= True ) if f.infix and f == '' \
else ( lambda x: eval ( f) ( x, *map ( eval , args) ) ) if f.infix and not f.closed and rightbind \
else ( lambda x: eval ( f) ( eval ( args[ 0 ] ) , x) ) if f.infix and not f.closed and len ( args) == 1 \
else eval ( f) ( *map ( eval , args) )
############################
import sys
while True :
try : print ( eval ( it( input ( '> ' ) ) ) )
except EOFError : raise SystemExit
except SystemExit : raise
except BaseException as e: sys .excepthook ( type ( e) , e, e.__traceback__)
ZnJvbSByZSAgICAgICAgICBpbXBvcnQgY29tcGlsZQpmcm9tIGFzdCAgICAgICAgIGltcG9ydCBsaXRlcmFsX2V2YWwKZnJvbSBmdW5jdG9vbHMgICBpbXBvcnQgcmVkdWNlCmZyb20gY29sbGVjdGlvbnMgaW1wb3J0IEl0ZXJhdG9yLCBkZXF1ZSwgbmFtZWR0dXBsZQoKTG9jYXRpb24gPSBuYW1lZHR1cGxlKCdMb2NhdGlvbicsICdzdGFydCwgZW5kLCBmaWxlbmFtZSwgZmlyc3RfbGluZScpCgoKY2xhc3MgU3RydWN0TWl4SW46CgogICAgaW5maXggICAgICAgPSBGYWxzZQogICAgY2xvc2VkICAgICAgPSBGYWxzZQogICAgaW5kZW50ZWQgICAgPSBGYWxzZQoKICAgIGRlZiBhZnRlcihzZWxmLCBvdGhlcik6CgogICAgICAgIHNlbGYubG9jYXRpb24gPSBMb2NhdGlvbigKICAgICAgICAgICAgb3RoZXIubG9jYXRpb24uZW5kLAogICAgICAgICAgICBvdGhlci5sb2NhdGlvbi5lbmQsCiAgICAgICAgICAgIG90aGVyLmxvY2F0aW9uLmZpbGVuYW1lLAogICAgICAgICAgICBvdGhlci5sb2NhdGlvbi5maXJzdF9saW5lCiAgICAgICAgKQogICAgICAgIHJldHVybiBzZWxmCgogICAgZGVmIGF0KHNlbGYsIHN0cmVhbSwgc3RhcnQpOgoKICAgICAgICBzZWxmLmxvY2F0aW9uID0gTG9jYXRpb24oCiAgICAgICAgICAgIHN0cmVhbS5wb3NpdGlvbihzdGFydCksCiAgICAgICAgICAgIHN0cmVhbS5wb3NpdGlvbihzdHJlYW0ub2Zmc2V0KSwKICAgICAgICAgICAgc3RyZWFtLmZpbGVuYW1lLAogICAgICAgICAgICBzdHJlYW0ubGluZShzdGFydCkKICAgICAgICApCiAgICAgICAgcmV0dXJuIHNlbGYKCgpjbGFzcyBFeHByZXNzaW9uIChsaXN0LCBTdHJ1Y3RNaXhJbik6CgogICAgZGVmIF9fcmVwcl9fKHNlbGYpOgoKICAgICAgICByZXR1cm4gKCcoe30pJyBpZiBzZWxmLmNsb3NlZCBlbHNlICd7fScpLmZvcm1hdCgKICAgICAgICAgICAgJyAnLmpvaW4obWFwKHJlcHIsIHNlbGZbMTpdKSkgaWYgc2VsZlswXSA9PSAnJyBlbHNlCiAgICAgICAgICAgICd7MX17MH17Mn0nICAuZm9ybWF0KCpzZWxmKSBpZiBsZW4oc2VsZikgPT0gMyBhbmQgc2VsZlswXSA9PSAnLicgZWxzZQogICAgICAgICAgICAnezF9IHswfSB7Mn0nLmZvcm1hdCgqc2VsZikgaWYgbGVuKHNlbGYpID09IDMgZWxzZQogICAgICAgICAgICAnezF9IHswfScgICAgLmZvcm1hdCgqc2VsZikgaWYgbGVuKHNlbGYpID09IDIgZWxzZQogICAgICAgICAgICAnKHt9KSB7fScuZm9ybWF0KHNlbGZbMF0sICcgJy5qb2luKG1hcChyZXByLCBzZWxmWzE6XSkpKQogICAgICAgICkKCgpjbGFzcyBMaW5rIChzdHIsIFN0cnVjdE1peEluKToKCiAgICBkZWYgX19uZXdfXyhjbHMsIGRhdGEsIGluZml4PUZhbHNlKToKCiAgICAgICAgb2JqID0gc3RyLl9fbmV3X18oY2xzLCBkYXRhKQogICAgICAgIG9iai5pbmZpeCA9IGJvb2woaW5maXgpCiAgICAgICAgcmV0dXJuIG9iagoKICAgIGRlZiBfX3JlcHJfXyhzZWxmKToKCiAgICAgICAgcmV0dXJuIHNlbGYKCgpjbGFzcyBDb25zdGFudCAoU3RydWN0TWl4SW4pOgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCB2YWx1ZSk6CgogICAgICAgIHN1cGVyKCkuX19pbml0X18oKQogICAgICAgIHNlbGYudmFsdWUgPSB2YWx1ZQoKICAgIGRlZiBfX3JlcHJfXyhzZWxmKToKCiAgICAgICAgcmV0dXJuIHJlcHIoc2VsZi52YWx1ZSkKCgpjbGFzcyBJbnRlcm5hbCAoQ29uc3RhbnQpOgoKICAgIHBhc3MKCiMgSW4gYWxsIGNvbW1lbnRzIGJlbG93LCBgUmAgYW5kIGBRYCBhcmUgaW5maXggbGlua3MsIHdoaWxlIGxvd2VyY2FzZSBsZXR0ZXJzCiMgYXJlIGFyYml0cmFyeSBleHByZXNzaW9ucy4KaGFzX3ByaW9yaXR5ID0gKGxhbWJkYSBmOiBsYW1iZGEgYSwgYjogZihhKVswXSA+IGYoYilbMV0pKGxhbWJkYSBtLCBnPXsKICAgICMgYGEgUiBiIFEgY2AgPD0+IGBhIFIgKGIgUSBjKWAgaWYgbGVmdCBiaW5kaW5nIHN0cmVuZ3RoIG9mIGBRYAogICAgIyBpcyBoaWdoZXIgdGhhbiByaWdodCBiaW5kaW5nIHN0cmVuZ3RoIG9mIGBSYC4KICAgICcnOiAgICAgICgtMiwgIC0yKSwgICAjIGNhbGwgd2l0aCBhbiBhcmd1bWVudAogICAgJ14nOiAgICAgKC0zLCAgLTQpLCAgICMgZXhwb25lbnRpYXRpb24KICAgICcqJzogICAgICgtNSwgIC01KSwgICAjIG11bHRpcGxpY2F0aW9uCiAgICAnLyc6ICAgICAoLTUsICAtNSksICAgIyBmcCBkaXZpc2lvbgogICAgJy8vJzogICAgKC01LCAgLTUpLCAgICMgaW50IGRpdmlzaW9uCiAgICAnJSc6ICAgICAoLTUsICAtNSksICAgIyBtb2R1bHVzCiAgICAnKyc6ICAgICAoLTYsICAtNiksICAgIyBhZGRpdGlvbgogICAgJy0nOiAgICAgKC02LCAgLTYpLCAgICMgc3VidHJhY3Rpb24KfS5nZXQ6IGcobSwgKC03LCAtNykpKSAgIyBEZWZhdWx0CgoKIyBJZiBgdW5hc3NvYyhSKWA6IGBhIFIgYiBSIGNgIDw9PiBgRXhwcmVzc2lvbiBbIExpbmsgUiwgTGluayBhLCBMaW5rIGIsIExpbmsgY11gCiMgT3RoZXJ3aXNlOiAgICAgICBgYSBSIGIgUiBjYCA8PT4gYEV4cHJlc3Npb24gWyBMaW5rIFIsIEV4cHJlc3Npb24gWy4uLl0sIExpbmsgY11gCiMgKFRoaXMgZG9lc24ndCBhcHBseSB0byByaWdodC1maXhlZCBsaW5rcy4pCnVuYXNzb2MgPSB7JywnLCAnLi4nLCAnOjonLCAnJywgJ1xuJ30uX19jb250YWluc19fCgojIFRoZXNlIG9wZXJhdG9ycyBoYXZlIG5vIHJpZ2h0LWhhbmQgc3RhdGVtZW50IHBhcnQgaW4gYW55IGNhc2UuCnVuYXJ5ID0geychJ30uX19jb250YWluc19fCgoKIyBpbmZpeGwgOjogKFN0YXRlLCBTdHJ1Y3RNaXhJbiwgTGluaywgU3RydWN0TWl4SW4pIC0+IFN0cnVjdE1peEluCiMKIyBIYW5kbGUgYW4gaW5maXggZXhwcmVzc2lvbiBnaXZlbiBpdHMgYWxsIHRocmVlIHBhcnRzLgojCiMgSWYgdGhhdCBwYXJ0aWN1bGFyIGV4cHJlc3Npb24gaGFzIG5vIHJpZ2h0LWhhbmQgc3RhdGVtZW50IHBhcnQsCiMgYHJoc2Agd2lsbCBiZSBwdXNoZWQgdG8gdGhlIG9iamVjdCBxdWV1ZS4KIwpkZWYgaW5maXhsKHN0cmVhbSwgbGhzLCBvcCwgcmhzKToKCiAgICBicmVha18gICA9IEZhbHNlCiAgICBwb3N0Zml4bCA9IGxhbWJkYTogKGxocyBpZiBvcCBpbiAnXG4gJyBlbHNlIGluZml4bF9pbnNlcnRfcmhzKGxocywgb3AsIE5vbmUpKQoKICAgIHdoaWxlIHJocyA9PSAnXG4nIGFuZCBub3QgdW5hcnkob3ApOgoKICAgICAgICBicmVha18sIHJocyA9IHJocywgbmV4dChzdHJlYW0pCgogICAgaWYgaXNpbnN0YW5jZShyaHMsIEludGVybmFsKSBvciB1bmFyeShvcCk6ICAjIGAoYSBSKWAKCiAgICAgICAgc3RyZWFtLmFwcGVuZGxlZnQocmhzKQogICAgICAgIHJldHVybiBwb3N0Zml4bCgpCgogICAgaWYgYnJlYWtfIGFuZCBvcCA9PSAnJyBhbmQgcmhzLmluZGVudGVkOgoKICAgICAgICAjIGEKICAgICAgICAjICAgYiAgICAgICAgIDw9PiAgYSBiIChjIGQpCiAgICAgICAgIyAgIGMgZAogICAgICAgIHJldHVybiByZWR1Y2UoCiAgICAgICAgICAgIGxhbWJkYSBsaHMsIHJoczogaW5maXhsX2luc2VydF9yaHMobGhzLCBvcCwgcmhzKSwKICAgICAgICAgICAgcmhzWzE6XSBpZiBpc2luc3RhbmNlKHJocywgRXhwcmVzc2lvbikgYW5kIHJoc1swXSA9PSAnXG4nIGVsc2UgW3Joc10sIGxocwogICAgICAgICkKCiAgICBpZiBicmVha18gYW5kIG9wICE9ICdcbicgYW5kIG5vdCByaHMuaW5kZW50ZWQ6ICAjIGBhIFJgCgogICAgICAgIHJldHVybiBpbmZpeGwoc3RyZWFtLCBwb3N0Zml4bCgpLCBicmVha18sIHJocykKCiAgICBpZiBub3QgcmhzLmNsb3NlZCBhbmQgcmhzLmluZml4IGFuZCAob3AgPT0gJycgb3IgaGFzX3ByaW9yaXR5KG9wLCByaHMpKToKCiAgICAgICAgIyBgYSBSIFEgYmAgPD0+IGAoYSBSKSBRIGJgIGlmIFIgaXMgcHJpb3JpdGl6ZWQgb3ZlciBRIG9yIFIgaXMgZW1wdHkuCiAgICAgICAgcmV0dXJuIGluZml4bChzdHJlYW0sIHBvc3RmaXhsKCksIHJocywgbmV4dChzdHJlYW0pKQoKICAgIHJldHVybiBpbmZpeGxfaW5zZXJ0X3JocyhsaHMsIG9wLCByaHMpCgoKIyBpbmZpeGxfaW5zZXJ0X3JocyA6OiAoU3RydWN0TWl4SW4sIExpbmssIFN0cnVjdE1peEluKSAtPiBTdHJ1Y3RNaXhJbgojCiMgUmVjdXJzaXZlbHkgZGVzY2VuZHMgaW50byBgcm9vdGAgaWYgaW5maXggcHJlY2VkZW5jZSBydWxlcyBhbGxvdyBpdCB0bywKIyBvdGhlcndpc2Ugc2ltcGx5IGNyZWF0ZXMgYW5kIHJldHVybnMgYSBuZXcgaW5maXggZXhwcmVzc2lvbiBBU1Qgbm9kZS4KIwpkZWYgaW5maXhsX2luc2VydF9yaHMocm9vdCwgb3AsIHJocyk6CgogICAgaWYgaXNpbnN0YW5jZShyb290LCBFeHByZXNzaW9uKSBhbmQgbm90IHJvb3QuY2xvc2VkOgoKICAgICAgICBpZiBoYXNfcHJpb3JpdHkob3AsIHJvb3RbMF0pOgoKICAgICAgICAgICAgIyBgYSBSIGIgUSBjYCA8PT4gYGEgUiAoYiBRIGMpYCBpZiBRIGlzIHByaW9yaXRpemVkIG92ZXIgUgogICAgICAgICAgICByb290LmFwcGVuZChpbmZpeGxfaW5zZXJ0X3Jocyhyb290LnBvcCgpLCBvcCwgcmhzKSkKICAgICAgICAgICAgcmV0dXJuIHJvb3QKCiAgICAgICAgZWxpZiBvcCA9PSByb290WzBdIGFuZCB1bmFzc29jKG9wKSBhbmQgcmhzIGlzIG5vdCBOb25lOgoKICAgICAgICAgICAgcm9vdC5hcHBlbmQocmhzKSAgIyBgYSBSIGIgUiBjYCA8PT4gYFIgYSBiIGNgCiAgICAgICAgICAgIHJldHVybiByb290CgogICAgIyBgUmAgICAgICAgICA8PT4gYExpbmsgUmAKICAgICMgYFIgcmhzYCAgICAgPD0+IGBFeHByZXNzaW9uIFsgTGluayAnJywgTGluayBSLCBMaW5rIHJocyBdYAogICAgIyBgbGhzIFJgICAgICA8PT4gYEV4cHJlc3Npb24gWyBMaW5rIFIsIExpbmsgbGhzIF1gCiAgICAjIGBsaHMgUiByaHNgIDw9PiBgRXhwcmVzc2lvbiBbIExpbmsgUiwgTGluayBsaHMsIExpbmsgcmhzIF1gCiAgICBlID0gRXhwcmVzc2lvbihbb3AsIHJvb3RdIGlmIHJocyBpcyBOb25lIGVsc2UgW29wLCByb290LCByaHNdKQogICAgZS5jbG9zZWQgPSByaHMgaXMgTm9uZQogICAgZS5sb2NhdGlvbiA9IExvY2F0aW9uKAogICAgICAgIHJvb3QubG9jYXRpb24uc3RhcnQsCiAgICAgICAgZVstKG5vdCBlLmNsb3NlZCldLmxvY2F0aW9uLmVuZCwKICAgICAgICByb290LmxvY2F0aW9uLmZpbGVuYW1lLAogICAgICAgIHJvb3QubG9jYXRpb24uZmlyc3RfbGluZQogICAgKQogICAgcmV0dXJuIGUKCgpkZWYgaW5kZW50KHN0cmVhbSwgdG9rZW4pOgoKICAgIGluZGVudCA9IGxlbih0b2tlbi5ncm91cCgpKQoKICAgIGlmIGluZGVudCA+IHN0cmVhbS5pbmRlbnRbLTFdOgoKICAgICAgICBzdHJlYW0uaW5kZW50LmFwcGVuZChpbmRlbnQpCiAgICAgICAgYmxvY2sgPSBkbyhzdHJlYW0sIHRva2VuLCB7Jyd9LCBwcmVzZXJ2ZV9jbG9zZV9zdGF0ZT1UcnVlKQogICAgICAgIGJsb2NrLmluZGVudGVkID0gVHJ1ZQogICAgICAgIHJldHVybiBibG9jawoKICAgIHdoaWxlIGluZGVudCAhPSBzdHJlYW0uaW5kZW50Wy0xXToKCiAgICAgICAgc3RyZWFtLmluZGVudC5wb3AoKSA8IDAgYW5kIHN0cmVhbS5lcnJvcignbm8gbWF0Y2hpbmcgaW5kZW50YXRpb24gbGV2ZWwnLCB0b2tlbi5zdGFydCgpKQogICAgICAgIHN0cmVhbS5hcHBlbmRsZWZ0KExpbmsoJ1xuJywgVHJ1ZSkuYXQoc3RyZWFtLCB0b2tlbi5lbmQoKSkpCiAgICAgICAgc3RyZWFtLmFwcGVuZGxlZnQoSW50ZXJuYWwoJycpLmF0KHN0cmVhbSwgdG9rZW4uZW5kKCkpKQoKCmRlZiB3aGl0ZXNwYWNlKHN0cmVhbSwgdG9rZW4pOiBwYXNzCgoKZGVmIG51bWJlcihzdHJlYW0sIHRva2VuKToKCiAgICByZXR1cm4gQ29uc3RhbnQobGl0ZXJhbF9ldmFsKHRva2VuLmdyb3VwKCkpKQoKCmRlZiBzdHJpbmcoc3RyZWFtLCB0b2tlbik6CgogICAgZyA9IHRva2VuLmdyb3VwKDIpICogKDQgLSBsZW4odG9rZW4uZ3JvdXAoMikpKQogICAgcSA9ICcnLmpvaW4oc29ydGVkKHNldCh0b2tlbi5ncm91cCgxKSkpKQogICAgcmV0dXJuIENvbnN0YW50KGxpdGVyYWxfZXZhbCgnJy5qb2luKFtxLCBnLCB0b2tlbi5ncm91cCgzKSwgZ10pKSkKCgpkZWYgbGluayhzdHJlYW0sIHRva2VuLCBpbmZpeG49eydpZicsICdlbHNlJywgJ29yJywgJ2FuZCcsICdpbicsICdpcycsICd3aGVyZSd9KToKCiAgICBpbmZpeCA9IHRva2VuLmdyb3VwKDIpIG9yIHRva2VuLmdyb3VwKDMpIG9yIHRva2VuLmdyb3VwKDQpCiAgICByZXR1cm4gTGluayhpbmZpeCBvciB0b2tlbi5ncm91cCgpLCBpbmZpeCBvciAodG9rZW4uZ3JvdXAoKSBpbiBpbmZpeG4pKQoKCmRlZiBkbyhzdHJlYW0sIHRva2VuLCBlbmRzPXsnKSd9LCBwcmVzZXJ2ZV9jbG9zZV9zdGF0ZT1GYWxzZSk6CgogICAgb2JqZWN0ICAgPSBDb25zdGFudChOb25lKS5hdChzdHJlYW0sIHRva2VuLmVuZCgpKQogICAgY2FuX2pvaW4gPSBGYWxzZQoKICAgIGZvciBpdGVtIGluIHN0cmVhbToKCiAgICAgICAgaWYgaXNpbnN0YW5jZShpdGVtLCBJbnRlcm5hbCk6CgogICAgICAgICAgICBpdGVtLnZhbHVlIGluIGVuZHMgb3Igc3RyZWFtLmVycm9yKAogICAgICAgICAgICAgICd1bmV4cGVjdGVkIGJsb2NrIGRlbGltaXRlcicgaWYgaXRlbS52YWx1ZSBlbHNlCiAgICAgICAgICAgICAgJ3VuZXhwZWN0ZWQgRU9GJyAgICAgICAgICAgICBpZiBzdHJlYW0ub2Zmc2V0ID49IGxlbihzdHJlYW0uYnVmZmVyKSBlbHNlCiAgICAgICAgICAgICAgJ3VuZXhwZWN0ZWQgZGVkZW50JywgaXRlbS5sb2NhdGlvbi5zdGFydFswXQogICAgICAgICAgICApCgogICAgICAgICAgICBicmVhawoKICAgICAgICBlbGlmIGNhbl9qb2luOgoKICAgICAgICAgICAgIyBUd28gb2JqZWN0cyBpbiBhIHJvdyBzaG91bGQgYmUgam9pbmVkIHdpdGggYW4gZW1wdHkgaW5maXggbGluay4KICAgICAgICAgICAgb2JqZWN0ID0gaW5maXhsKHN0cmVhbSwgb2JqZWN0LCBMaW5rKCcnLCBUcnVlKS5hZnRlcihvYmplY3QpLCBpdGVtKQoKICAgICAgICAjIElnbm9yZSBsaW5lIGZlZWRzIGRpcmVjdGx5IGZvbGxvd2luZyBhbiBvcGVuaW5nIHBhcmVudGhlc2VzLgogICAgICAgIGVsaWYgaXRlbSAhPSAnXG4nOgoKICAgICAgICAgICAgb2JqZWN0LCBjYW5fam9pbiA9IGl0ZW0sIFRydWUKCiAgICBvYmplY3QuY2xvc2VkIHw9IG5vdCBwcmVzZXJ2ZV9jbG9zZV9zdGF0ZQogICAgcmV0dXJuIG9iamVjdAoKCmRlZiBlbmQoc3RyZWFtLCB0b2tlbik6CgogICAgcmV0dXJuIEludGVybmFsKHRva2VuLmdyb3VwKCkpCgoKZGVmIHN0cmluZ19lcnIoc3RyZWFtLCB0b2tlbik6CgogICAgc3RyZWFtLmVycm9yKCd1bmV4cGVjdGVkIEVPRiB3aGlsZSByZWFkaW5nIGEgc3RyaW5nIGxpdGVyYWwnLCB0b2tlbi5zdGFydCgpKQoKCmRlZiBlcnJvcihzdHJlYW0sIHRva2VuKToKCiAgICBzdHJlYW0uZXJyb3IoJ2ludmFsaWQgaW5wdXQnLCB0b2tlbi5zdGFydCgpKQoKCmRlZiBSKGZ1bmMsIGV4cHIpOgoKICAgIHJldHVybiBmdW5jLCBjb21waWxlKGV4cHIpLm1hdGNoCgoKY2xhc3MgaXQgKEl0ZXJhdG9yLCBkZXF1ZSk6CgogICAgdG9rZW5zID0gW1IoaW5kZW50LCByJyAqJyldLCBbCiAgICAgICAgUih3aGl0ZXNwYWNlLCByJ1teXFNcbl0rfFxzKiMuKicpCiAgICAgICwgUihudW1iZXIsICAgICByJyg/aSlbKy1dPyg/OjBiWzAtMV0rfDBvWzAtN10rfDB4WzAtOWEtZl0rfFxkKyg/OlwuXGQrKT8oPzplWystXT9cZCspP2o/KScpCiAgICAgICwgUihzdHJpbmcsICAgICByJyg/cykoW2JyXSopKFwnXCdcJ3wiIiJ8InxcJykoKD86XFw/LikqPylcMicpCiAgICAgICwgUihsaW5rLCAgICAgICByIihcdysnKnxcKisoPz06KXwoWyEkJSYqK1wtLS86PC1AXFxefH47XSt8LCspKXxccyooXG4pfGAoXHcrJyopYCIpCiAgICAgICwgUihkbywgICAgICAgICByJ1woJykKICAgICAgLCBSKGVuZCwgICAgICAgIHInXCl8JCcpCiAgICAgICwgUihzdHJpbmdfZXJyLCByJycnWyciXScnJykKICAgICAgLCBSKGVycm9yLCAgICAgIHInLicpCiAgICBdCgogICAgZGVmIF9fbmV3X18oY2xzLCBkYXRhLCBmaWxlbmFtZT0nPHN0cmluZz4nKToKCiAgICAgICAgc2VsZiA9IHN1cGVyKGl0LCBjbHMpLl9fbmV3X18oY2xzKQogICAgICAgIHNlbGYuZmlsZW5hbWUgPSBmaWxlbmFtZQogICAgICAgIHNlbGYuYnVmZmVyICAgPSBkYXRhCiAgICAgICAgc2VsZi5vZmZzZXQgICA9IDAKICAgICAgICBzZWxmLmluZGVudCAgID0gZGVxdWUoWy0xXSkKICAgICAgICBzZWxmLm5leHRzZXQgID0gc2VsZi50b2tlbnNbMF0KICAgICAgICByZXR1cm4gbmV4dChzZWxmKQoKICAgIGRlZiBfX25leHRfXyhzZWxmKToKCiAgICAgICAgd2hpbGUgbm90IHNlbGY6CgogICAgICAgICAgICBmLCBtYXRjaCA9IG5leHQoKGYsIG1hdGNoKQogICAgICAgICAgICAgICAgZm9yIGYsIHJlIGluIHNlbGYubmV4dHNldAogICAgICAgICAgICAgICAgZm9yIG1hdGNoIGluIFtyZShzZWxmLmJ1ZmZlciwgc2VsZi5vZmZzZXQpXSBpZiBtYXRjaAogICAgICAgICAgICApCiAgICAgICAgICAgIHNlbGYub2Zmc2V0ICA9IG1hdGNoLmVuZCgpCiAgICAgICAgICAgIHNlbGYubmV4dHNldCA9IHNlbGYudG9rZW5zW25vdCBtYXRjaC5ncm91cCgpLmVuZHN3aXRoKCdcbicpXQogICAgICAgICAgICBxID0gZihzZWxmLCBtYXRjaCkKCiAgICAgICAgICAgIGlmIHEgaXMgbm90IE5vbmU6CgogICAgICAgICAgICAgICAgcmV0dXJuIHEuYXQoc2VsZiwgbWF0Y2guc3RhcnQoKSkKCiAgICAgICAgcmV0dXJuIHNlbGYucG9wbGVmdCgpCgogICAgIyBwb3NpdGlvbiA6OiBpbnQgLT4gKGludCwgaW50LCBpbnQpCiAgICAjCiAgICAjIEdpdmVuIGEgY2hhcmFjdGVyIG9mZnNldCwgZ2V0IGFuIChvZmZzZXQsIGxpbmVubywgY2hhcm5vKSB0cmlwbGUuCiAgICAjCiAgICBkZWYgcG9zaXRpb24oc2VsZiwgb2Zmc2V0KToKCiAgICAgICAgcmV0dXJuIChvZmZzZXQsCiAgICAgICAgICAgIDEgICAgICArIHNlbGYuYnVmZmVyLmNvdW50KCdcbicsIDAsIG9mZnNldCksCiAgICAgICAgICAgIG9mZnNldCAtIHNlbGYuYnVmZmVyLnJmaW5kKCdcbicsIDAsIG9mZnNldCkpCgogICAgIyBlcnJvciA6OiAoc3RyLCBpbnQpIC0+IF98XwogICAgIwogICAgIyBSYWlzZSBhIGBTeW50YXhFcnJvcmAgYXQgYSBnaXZlbiBvZmZzZXQuCiAgICAjCiAgICBkZWYgZXJyb3Ioc2VsZiwgZGVzY3JpcHRpb24sIGF0KToKCiAgICAgICAgXywgbGluZW5vLCBjaGFybm8gPSBzZWxmLnBvc2l0aW9uKGF0KQogICAgICAgIHJhaXNlIFN5bnRheEVycm9yKGRlc2NyaXB0aW9uLCAoc2VsZi5maWxlbmFtZSwgbGluZW5vLCBjaGFybm8sIHNlbGYubGluZShhdCkpKQoKICAgICMgbGluZSA6OiBpbnQgLT4gc3RyCiAgICAjCiAgICAjIEdldCBhIGxpbmUgd2hpY2ggY29udGFpbnMgdGhlIGNoYXJhY3RlciBhdCBhIGdpdmVuIG9mZnNldC4KICAgICMKICAgIGRlZiBsaW5lKHNlbGYsIG9mZnNldCk6CgogICAgICAgIHJldHVybiBzZWxmLmJ1ZmZlclsKICAgICAgICAgICAgc2VsZi5idWZmZXIucmZpbmQoJ1xuJywgMCwgb2Zmc2V0KSArIDEgb3IgTm9uZToKICAgICAgICAgICAgc2VsZi5idWZmZXIuIGZpbmQoJ1xuJywgICAgb2Zmc2V0KSArIDEgb3IgTm9uZQogICAgICAgIF0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgppbXBvcnQgZnJhY3Rpb25zIGFzIGZyCmltcG9ydCBvcGVyYXRvciAgYXMgb3AKCm5zID0geycnOiBsYW1iZGEgZiwgKnhzOiBmKCp4cyksICdeJzogb3AucG93LCAnKic6IG9wLm11bCwgJy8nOiBmci5GcmFjdGlvbiwgJy8vJzogb3AudHJ1ZWRpdiwgJyUnOiBvcC5tb2QsICcrJzogb3AuYWRkLCAnLSc6IG9wLnN1Yn0KCgpkZWYgZXZhbCh4KToKICAgIGlmIGlzaW5zdGFuY2UoeCwgTGluayk6ICAgICAgIHJldHVybiBuc1t4XQogICAgaWYgaXNpbnN0YW5jZSh4LCBDb25zdGFudCk6ICAgcmV0dXJuIHgudmFsdWUKICAgIGlmIGlzaW5zdGFuY2UoeCwgRXhwcmVzc2lvbik6IHJldHVybiBjYWxsKCp4KQogICAgcmFpc2UgVHlwZUVycm9yKCdpbnZhbGlkIEFTVCBub2RlJywgeCkKCgpkZWYgY2FsbChmLCAqYXJncywgcmlnaHRiaW5kPUZhbHNlKToKICAgIHJldHVybiBmIGlmIG5vdCBhcmdzIGVsc2UgY2FsbCgqYXJncywgcmlnaHRiaW5kPVRydWUpIGlmIGYuaW5maXggYW5kIGYgPT0gJycgXAogICAgICBlbHNlIChsYW1iZGEgeDogZXZhbChmKSh4LCAqbWFwKGV2YWwsIGFyZ3MpKSkgaWYgZi5pbmZpeCBhbmQgbm90IGYuY2xvc2VkIGFuZCByaWdodGJpbmQgICAgICBcCiAgICAgIGVsc2UgKGxhbWJkYSB4OiBldmFsKGYpKGV2YWwoYXJnc1swXSksIHgpICAgKSBpZiBmLmluZml4IGFuZCBub3QgZi5jbG9zZWQgYW5kIGxlbihhcmdzKSA9PSAxIFwKICAgICAgZWxzZSBldmFsKGYpKCptYXAoZXZhbCwgYXJncykpCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKaW1wb3J0IHN5cwoKd2hpbGUgVHJ1ZToKICAgIHRyeTogcHJpbnQoZXZhbChpdChpbnB1dCgnPiAnKSkpKQogICAgZXhjZXB0IEVPRkVycm9yOiAgIHJhaXNlIFN5c3RlbUV4aXQKICAgIGV4Y2VwdCBTeXN0ZW1FeGl0OiByYWlzZQogICAgZXhjZXB0IEJhc2VFeGNlcHRpb24gYXMgZTogc3lzLmV4Y2VwdGhvb2sodHlwZShlKSwgZSwgZS5fX3RyYWNlYmFja19fKQ==