# Examen final (parcial 1) ALS (c) 2019 Baltasar MIT License <jbgarcia@uvigo.es>
"""
1. (2 pts.) Implemente la función calcula_diferencia(s1, s2). Esta función recorre comparando ambas cadenas. Para cada carácter distinto, convierte el carácter a su código correspondiente en minúsculas y resta ambos. Si las dos cadenas tienen longitud distinta, se le suma por cada carácter de diferencia la existente entre ‘z’ y ‘a’, más 10.
print(calcula_diferencia(“radia”, “radio”)) # delta ‘o’ a ‘a’ == 14
print(calcula_diferencia(“radia”, “rada”)) # delta ‘i’ a ‘a’ + delta ‘a’ a ‘z’ + 10 == 43
2. (4 pts.) Cree la clase Arbol para estructuras en árboles. Esta clase admite dos valores en el constructor: el nodo raíz, y una lista con los subnodos, que pueden tener un valor cualquiera, u otro objeto Arbol. Tendrá también las propiedades de solo lectura raiz, que devolverán el nodo raíz y subnodos, que devolverá una nueva lista con los subnodos. La función len() será capaz de actuar sobre objetos de esta clase, devolviendo el número de elementos en la lista de subnodos. El operador [] será aplicable, de manera que se devolverá el elemento i-ésimo de la lista de subnodos. Debe ser posible convertir la información de un árbol a cadena, en el formato raiz(subnodo1, subnodo2, ...). Crea el método estático combina(a1, a2), que devolverá un nuevo árbol cuya raíz es la raíz de a1 concatenada con la de a2, y sus subnodos los subnodos de a1 y a2. Así, la combinación de Arbol(“a”, [Arbol(“b”, [“c”, “d”]), “e”]), y Arbol(“a”, [“f”, “g”]), devolvería el árbol Arbol(“aa”, [Arbol(“b”, [“c”, “d”]), “e”, “f”, “g”]).
3. (4 pts.) Cree la clase Alumno, cuyos objetos se construyen con los datos: dni y nombre, ambos texto. De esta clase derivan la clase Erasmus, que incorpora su país de origen. La clase Nota se construye con un objeto alumno y una nota (float). La clase Asignatura se construye con su nombre, el nombre del profesor, y el año. La clase Calificaciones se construye con el nombre del examen (p.ej. “parcial 1”), un objeto asignatura, y una lista de objetos Nota. Cree propiedades de solo lectura para todos los atributos. Cree métodos que permitan convertir la información a texto en todas las clases.
alumno1 = Alumno("Baltasar", "1X")
alumno2 = Erasmus("Wally", "X2X", "Noselandia")
nota1 = Nota(alumno1, 9.5)
nota2 = Nota(alumno2, 5.752)
asignatura = Asignatura("ALS", "Baltasar", 2019)
parcial1 = Calificaciones("parcial 1", self.asignatura, [self.nota1, self.nota2])
print(parcial1)
ALS
parcial 1
1X - Baltasar: 9.50
X2X - Wally (Noselandia): 5.75
"""
def calcula_diferencia( s1, s2) :
"""Calcula la diferencia entre dos textos.
- Para cada carácter distinto, convierte el carácter a su código correspondiente en minúsculas y resta ambos.
- Si las dos cadenas tienen longitud distinta, se le suma por cada carácter de diferencia la existente entre ‘z’ y ‘a’, más 10.
:param s: La cadena a rotar.
:param n: El num. de veces a rotar.
"""
def diferencia_caracteres( c1, c2) :
val1 = ord ( c1[ 0 ] .lower ( ) )
val2 = ord ( c2[ 0 ] .lower ( ) )
return abs ( val1 - val2)
def diferencia_cadenas( s1, s2) :
val1 = abs ( ord ( 'z' ) - ord ( 'a' ) ) + 10
return abs ( len ( s1) - len ( s2) ) * val1
s1 = s1.strip ( ) .lower ( )
s2 = s2.strip ( ) .lower ( )
len_comun = min ( len ( s1) , len ( s2) )
toret = diferencia_cadenas( s1, s2)
for i in range ( len_comun) :
toret += diferencia_caracteres( s1[ i] , s2[ i] )
return toret
class Arbol:
"""Representa un arbol."""
def __init__ ( self , raiz, subnodos) :
self ._raiz = raiz
self ._subnodos = list ( subnodos)
@ property
def raiz( self ) :
return self ._raiz
@ property
def subnodos( self ) :
return list ( self ._subnodos)
def __len__ ( self ) :
return len ( self ._subnodos)
def __getitem__ ( self , item) :
return self ._subnodos[ item]
def __str__ ( self ) :
return str .format ( "{}({})" , str ( self .raiz ) , ", " .join ( [ str ( x) for x in self ._subnodos] ) )
@ staticmethod
def combina( a1, a2) :
return Arbol( a1.raiz + a2.raiz , a1.subnodos + a2.subnodos )
class Alumno:
""" Un alumno de una asignatura. """
def __init__ ( self , nombre, dni) :
self ._nombre = nombre
self ._dni = dni
@ property
def dni( self ) :
return self ._dni
@ property
def nombre( self ) :
return self ._nombre
def __str__ ( self ) :
return str .format ( "{} - {}" , self .dni , self .nombre )
class Erasmus( Alumno) :
""" Alumno proveniente del extranjero. """
def __init__ ( self , nombre, dni, pais) :
super ( ) .__init__ ( nombre, dni)
self ._pais = pais
@ property
def pais( self ) :
return self ._pais
def __str__ ( self ) :
return str .format ( "{} ({})" , super ( ) .__str__ ( ) , self .pais )
class Nota:
""" Nota de un alumno. """
def __init__ ( self , alumno, nota) :
self ._alumno = alumno
self ._nota = nota
@ property
def alumno( self ) :
return self ._alumno
@ property
def nota( self ) :
return self ._nota
def __str__ ( self ) :
return str .format ( "{}: {:5.2f}" , str ( self .alumno ) , self .nota )
class Asignatura:
def __init__ ( self , nombre, nombre_profesor, anno) :
self ._nombre = nombre
self ._nombre_profesor = nombre_profesor
self ._anno = anno
@ property
def nombre( self ) :
return self ._nombre
@ property
def nombre_profesor( self ) :
return self ._nombre_profesor
@ property
def anno( self ) :
return self ._anno
def __str__ ( self ) :
return str .format ( "{} {} ({})" , self .nombre , self .nombre_profesor , self .anno )
class Calificaciones:
def __init__ ( self , nombre_examen, asignatura, lista_notas) :
self ._nombre_examen = nombre_examen
self ._asignatura = asignatura
self ._lista_notas = lista_notas
@ property
def nombre_examen( self ) :
return self ._nombre_examen
@ property
def asignatura( self ) :
return self ._asignatura
@ property
def notas( self ) :
return list ( self ._lista_notas)
def __len__ ( self ) :
return len ( self ._lista_notas)
def __getitem__ ( self , item) :
return self ._lista_notas[ item]
def __str__ ( self ) :
return str .format ( "{}\n {}\n {}\n " ,
self .asignatura .nombre ,
self .nombre_examen ,
"\n " .join ( [ str ( nota) for nota in self ._lista_notas] ) )
import unittest
class TestCalculaDiferencia( unittest .TestCase ) :
def setUp( self ) :
self .diferencia_caracter_extra = abs ( ord ( 'a' ) - ord ( 'z' ) ) + 10
def testIt( self ) :
cads1 = [ "radia" , "radia" ]
cads2 = [ "radio" , "rada" ]
resultados = [ abs ( ord ( 'o' ) - ord ( 'a' ) ) ,
abs ( ord ( 'i' ) - ord ( 'a' ) )
+ self .diferencia_caracter_extra ]
for i, resultado in enumerate ( resultados) :
self .assertEqual ( resultado, calcula_diferencia( cads1[ i] , cads2[ i] ) )
class TestArbol( unittest .TestCase ) :
def setUp( self ) :
self .a1 = Arbol( "a" , [ Arbol( "b" , [ "c" , "d" ] ) , "e" ] )
self .a2 = Arbol( "a" , [ "f" , "g" ] )
def testArbol1( self ) :
sub_a3 = self .a1 [ 0 ]
self .assertEqual ( 2 , len ( sub_a3) )
self .assertEqual ( 'b' , sub_a3.raiz )
self .assertEqual ( 'c' , sub_a3[ 0 ] )
self .assertEqual ( 'd' , sub_a3[ 1 ] )
self .assertEqual ( "b(c, d)" , str ( sub_a3) )
self .assertEqual ( 2 , len ( self .a1 ) )
self .assertEqual ( 'a' , self .a1 .raiz )
self .assertEqual ( sub_a3, self .a1 [ 0 ] )
self .assertEqual ( 'e' , self .a1 [ 1 ] )
self .assertEqual ( "a(b(c, d), e)" , str ( self .a1 ) )
def testArbol2( self ) :
self .assertEqual ( 2 , len ( self .a2 ) )
self .assertEqual ( 'a' , self .a2 .raiz )
self .assertEqual ( 'f' , self .a2 [ 0 ] )
self .assertEqual ( 'g' , self .a2 [ 1 ] )
self .assertEqual ( "a(f, g)" , str ( self .a2 ) )
def testCombina( self ) :
a4 = Arbol.combina ( self .a1 , self .a2 )
self .assertEqual ( 4 , len ( a4) )
self .assertEqual ( self .a1 .raiz + self .a2 .raiz , a4.raiz )
self .assertEqual ( self .a1 [ 0 ] , a4[ 0 ] )
self .assertEqual ( 'e' , a4[ 1 ] )
self .assertEqual ( 'f' , a4[ 2 ] )
self .assertEqual ( 'g' , a4[ 3 ] )
self .assertEqual ( "aa(b(c, d), e, f, g)" , str ( a4) )
class TestNotasAsignatura( unittest .TestCase ) :
def setUp( self ) :
self .alumno1 = Alumno( "Baltasar" , "1X" )
self .alumno2 = Erasmus( "Wally" , "X2X" , "Noselandia" )
self .nota1 = Nota( self .alumno1 , 9.5 )
self .nota2 = Nota( self .alumno2 , 5.752 )
self .asignatura = Asignatura( "ALS" , "Baltasar" , 2019 )
self .parcial1 = Calificaciones( "parcial 1" ,
self .asignatura ,
[ self .nota1 , self .nota2 ] )
def testAlumno1( self ) :
self .assertEqual ( "Baltasar" , self .alumno1 .nombre )
self .assertEqual ( "1X" , self .alumno1 .dni )
self .assertEqual ( "1X - Baltasar" , str ( self .alumno1 ) )
def testAlumno2( self ) :
self .assertEqual ( "Wally" , self .alumno2 .nombre )
self .assertEqual ( "X2X" , self .alumno2 .dni )
self .assertEqual ( "Noselandia" , self .alumno2 .pais )
self .assertEqual ( "X2X - Wally (Noselandia)" , str ( self .alumno2 ) )
def testNota1( self ) :
self .assertEqual ( self .alumno1 , self .nota1 .alumno )
self .assertEqual ( 9.5 , self .nota1 .nota )
self .assertEqual ( "1X - Baltasar: 9.50" , str ( self .nota1 ) )
def testNota2( self ) :
self .assertEqual ( self .alumno2 , self .nota2 .alumno )
self .assertEqual ( 5.752 , self .nota2 .nota )
self .assertEqual ( "X2X - Wally (Noselandia): 5.75" , str ( self .nota2 ) )
def testAsignatura( self ) :
self .assertEqual ( "ALS" , self .asignatura .nombre )
self .assertEqual ( "Baltasar" , self .asignatura .nombre_profesor )
self .assertEqual ( 2019 , self .asignatura .anno )
def testCalificaciones( self ) :
text = ( self .parcial1 .asignatura .nombre
+ "\n " + self .parcial1 .nombre_examen
+ "\n " + "\n " .join ( [ str ( x) for x in self .parcial1 .notas ] )
+ "\n " )
self .assertEqual ( "parcial 1" , self .parcial1 .nombre_examen )
self .assertEqual ( self .asignatura , self .parcial1 .asignatura )
self .assertEqual ( 2 , len ( self .parcial1 ) )
self .assertEqual ( [ self .nota1 , self .nota2 ] , self .parcial1 .notas )
self .assertEqual ( self .nota1 , self .parcial1 [ 0 ] )
self .assertEqual ( self .nota2 , self .parcial1 [ 1 ] )
self .assertEqual ( text, str ( self .parcial1 ) )
if __name__ == "__main__" :
unittest .main ( )
IyBFeGFtZW4gZmluYWwgKHBhcmNpYWwgMSkgQUxTIChjKSAyMDE5IEJhbHRhc2FyIE1JVCBMaWNlbnNlIDxqYmdhcmNpYUB1dmlnby5lcz4KCiIiIgogICAgMS4gKDIgcHRzLikgSW1wbGVtZW50ZSBsYSBmdW5jacOzbiBjYWxjdWxhX2RpZmVyZW5jaWEoczEsIHMyKS4gRXN0YSBmdW5jacOzbiByZWNvcnJlIGNvbXBhcmFuZG8gYW1iYXMgY2FkZW5hcy4gUGFyYSBjYWRhIGNhcsOhY3RlciBkaXN0aW50bywgY29udmllcnRlIGVsIGNhcsOhY3RlciBhIHN1IGPDs2RpZ28gY29ycmVzcG9uZGllbnRlIGVuIG1pbsO6c2N1bGFzIHkgcmVzdGEgYW1ib3MuIFNpIGxhcyBkb3MgY2FkZW5hcyB0aWVuZW4gbG9uZ2l0dWQgZGlzdGludGEsIHNlIGxlIHN1bWEgcG9yIGNhZGEgY2Fyw6FjdGVyIGRlIGRpZmVyZW5jaWEgbGEgZXhpc3RlbnRlIGVudHJlIOKAmHrigJkgeSDigJhh4oCZLCBtw6FzIDEwLgoKcHJpbnQoY2FsY3VsYV9kaWZlcmVuY2lhKOKAnHJhZGlh4oCdLCDigJxyYWRpb+KAnSkpCSMgZGVsdGEg4oCYb+KAmSBhIOKAmGHigJkgPT0gMTQKcHJpbnQoY2FsY3VsYV9kaWZlcmVuY2lhKOKAnHJhZGlh4oCdLCDigJxyYWRh4oCdKSkJIyBkZWx0YSDigJhp4oCZIGEg4oCYYeKAmSArIGRlbHRhIOKAmGHigJkgYSDigJh64oCZICsgMTAgPT0gNDMKCgogICAgMi4gKDQgcHRzLikgQ3JlZSBsYSBjbGFzZSBBcmJvbCBwYXJhIGVzdHJ1Y3R1cmFzIGVuIMOhcmJvbGVzLiBFc3RhIGNsYXNlIGFkbWl0ZSBkb3MgdmFsb3JlcyBlbiBlbCBjb25zdHJ1Y3RvcjogZWwgbm9kbyByYcOteiwgeSB1bmEgbGlzdGEgY29uIGxvcyBzdWJub2RvcywgcXVlIHB1ZWRlbiB0ZW5lciB1biB2YWxvciBjdWFscXVpZXJhLCB1IG90cm8gb2JqZXRvIEFyYm9sLiBUZW5kcsOhIHRhbWJpw6luIGxhcyBwcm9waWVkYWRlcyBkZSBzb2xvIGxlY3R1cmEgcmFpeiwgcXVlIGRldm9sdmVyw6FuIGVsIG5vZG8gcmHDrXogeSBzdWJub2RvcywgcXVlIGRldm9sdmVyw6EgdW5hIG51ZXZhIGxpc3RhIGNvbiBsb3Mgc3Vibm9kb3MuIExhIGZ1bmNpw7NuIGxlbigpIHNlcsOhIGNhcGF6IGRlIGFjdHVhciBzb2JyZSBvYmpldG9zIGRlIGVzdGEgY2xhc2UsIGRldm9sdmllbmRvIGVsIG7Dum1lcm8gZGUgZWxlbWVudG9zIGVuIGxhIGxpc3RhIGRlIHN1Ym5vZG9zLiBFbCBvcGVyYWRvciBbXSBzZXLDoSBhcGxpY2FibGUsIGRlIG1hbmVyYSBxdWUgc2UgZGV2b2x2ZXLDoSBlbCBlbGVtZW50byBpLcOpc2ltbyBkZSBsYSBsaXN0YSBkZSBzdWJub2Rvcy4gRGViZSBzZXIgcG9zaWJsZSBjb252ZXJ0aXIgbGEgaW5mb3JtYWNpw7NuIGRlIHVuIMOhcmJvbCBhIGNhZGVuYSwgZW4gZWwgZm9ybWF0byByYWl6KHN1Ym5vZG8xLCBzdWJub2RvMiwgLi4uKS4gQ3JlYSBlbCBtw6l0b2RvIGVzdMOhdGljbyBjb21iaW5hKGExLCBhMiksIHF1ZSBkZXZvbHZlcsOhIHVuIG51ZXZvIMOhcmJvbCBjdXlhIHJhw616IGVzIGxhIHJhw616IGRlIGExIGNvbmNhdGVuYWRhIGNvbiBsYSBkZSBhMiwgeSBzdXMgc3Vibm9kb3MgbG9zIHN1Ym5vZG9zIGRlIGExIHkgYTIuIEFzw60sIGxhIGNvbWJpbmFjacOzbiBkZSBBcmJvbCjigJxh4oCdLCBbQXJib2wo4oCcYuKAnSwgW+KAnGPigJ0sIOKAnGTigJ1dKSwg4oCcZeKAnV0pLCB5ICBBcmJvbCjigJxh4oCdLCBb4oCcZuKAnSwg4oCcZ+KAnV0pLCBkZXZvbHZlcsOtYSBlbCDDoXJib2wgQXJib2wo4oCcYWHigJ0sIFtBcmJvbCjigJxi4oCdLCBb4oCcY+KAnSwg4oCcZOKAnV0pLCDigJxl4oCdLCDigJxm4oCdLCDigJxn4oCdXSkuCgogICAgMy4gKDQgcHRzLikgQ3JlZSBsYSBjbGFzZSBBbHVtbm8sIGN1eW9zIG9iamV0b3Mgc2UgY29uc3RydXllbiBjb24gbG9zIGRhdG9zOiBkbmkgeSBub21icmUsIGFtYm9zIHRleHRvLiBEZSBlc3RhIGNsYXNlIGRlcml2YW4gbGEgY2xhc2UgRXJhc211cywgcXVlIGluY29ycG9yYSBzdSBwYcOtcyBkZSBvcmlnZW4uIExhIGNsYXNlIE5vdGEgc2UgY29uc3RydXllIGNvbiB1biBvYmpldG8gYWx1bW5vIHkgdW5hIG5vdGEgKGZsb2F0KS4gTGEgY2xhc2UgQXNpZ25hdHVyYSBzZSBjb25zdHJ1eWUgY29uIHN1IG5vbWJyZSwgZWwgbm9tYnJlIGRlbCBwcm9mZXNvciwgeSBlbCBhw7FvLiBMYSBjbGFzZSBDYWxpZmljYWNpb25lcyBzZSBjb25zdHJ1eWUgY29uIGVsIG5vbWJyZSBkZWwgZXhhbWVuIChwLmVqLiDigJxwYXJjaWFsIDHigJ0pLCB1biBvYmpldG8gYXNpZ25hdHVyYSwgeSB1bmEgbGlzdGEgZGUgb2JqZXRvcyBOb3RhLiBDcmVlIHByb3BpZWRhZGVzIGRlIHNvbG8gbGVjdHVyYSBwYXJhIHRvZG9zIGxvcyBhdHJpYnV0b3MuIENyZWUgbcOpdG9kb3MgcXVlIHBlcm1pdGFuIGNvbnZlcnRpciBsYSBpbmZvcm1hY2nDs24gYSB0ZXh0byBlbiB0b2RhcyBsYXMgY2xhc2VzLgoKYWx1bW5vMSA9IEFsdW1ubygiQmFsdGFzYXIiLCAiMVgiKQphbHVtbm8yID0gRXJhc211cygiV2FsbHkiLCAiWDJYIiwgIk5vc2VsYW5kaWEiKQpub3RhMSA9IE5vdGEoYWx1bW5vMSwgOS41KQpub3RhMiA9IE5vdGEoYWx1bW5vMiwgNS43NTIpCmFzaWduYXR1cmEgPSBBc2lnbmF0dXJhKCJBTFMiLCAiQmFsdGFzYXIiLCAyMDE5KQpwYXJjaWFsMSA9IENhbGlmaWNhY2lvbmVzKCJwYXJjaWFsIDEiLCBzZWxmLmFzaWduYXR1cmEsIFtzZWxmLm5vdGExLCBzZWxmLm5vdGEyXSkKcHJpbnQocGFyY2lhbDEpCgpBTFMKcGFyY2lhbCAxCjFYIC0gQmFsdGFzYXI6ICA5LjUwClgyWCAtIFdhbGx5IChOb3NlbGFuZGlhKTogIDUuNzUKIiIiCgpkZWYgY2FsY3VsYV9kaWZlcmVuY2lhKHMxLCBzMik6CiAgICAiIiJDYWxjdWxhIGxhIGRpZmVyZW5jaWEgZW50cmUgZG9zIHRleHRvcy4KICAgICAgICAtIFBhcmEgY2FkYSBjYXLDoWN0ZXIgZGlzdGludG8sIGNvbnZpZXJ0ZSBlbCBjYXLDoWN0ZXIgYSBzdSBjw7NkaWdvIGNvcnJlc3BvbmRpZW50ZSBlbiBtaW7DunNjdWxhcyB5IHJlc3RhIGFtYm9zLgogICAgICAgIC0gU2kgbGFzIGRvcyBjYWRlbmFzIHRpZW5lbiBsb25naXR1ZCBkaXN0aW50YSwgc2UgbGUgc3VtYSBwb3IgY2FkYSBjYXLDoWN0ZXIgZGUgZGlmZXJlbmNpYSBsYSBleGlzdGVudGUgZW50cmUg4oCYeuKAmSB5IOKAmGHigJksIG3DoXMgMTAuCiAgICAKICAgICAgICA6cGFyYW0gczogTGEgY2FkZW5hIGEgcm90YXIuCiAgICAgICAgOnBhcmFtIG46IEVsIG51bS4gZGUgdmVjZXMgYSByb3Rhci4KICAgICIiIgogICAgCiAgICBkZWYgZGlmZXJlbmNpYV9jYXJhY3RlcmVzKGMxLCBjMik6CiAgICAgICAgdmFsMSA9IG9yZChjMVswXS5sb3dlcigpKQogICAgICAgIHZhbDIgPSBvcmQoYzJbMF0ubG93ZXIoKSkKICAgICAgICAKICAgICAgICByZXR1cm4gYWJzKHZhbDEgLSB2YWwyKQogICAgCiAgICBkZWYgZGlmZXJlbmNpYV9jYWRlbmFzKHMxLCBzMik6CiAgICAgICAgdmFsMSA9IGFicyhvcmQoJ3onKSAtIG9yZCgnYScpKSArIDEwCiAgICAgICAgCiAgICAgICAgcmV0dXJuIGFicyhsZW4oczEpIC0gbGVuKHMyKSkgKiB2YWwxCiAgICAKICAgIHMxID0gczEuc3RyaXAoKS5sb3dlcigpCiAgICBzMiA9IHMyLnN0cmlwKCkubG93ZXIoKQogICAgbGVuX2NvbXVuID0gbWluKGxlbihzMSksIGxlbihzMikpCiAgICB0b3JldCA9IGRpZmVyZW5jaWFfY2FkZW5hcyhzMSwgczIpCiAgICAKICAgIGZvciBpIGluIHJhbmdlKGxlbl9jb211bik6CiAgICAgICAgdG9yZXQgKz0gZGlmZXJlbmNpYV9jYXJhY3RlcmVzKHMxW2ldLCBzMltpXSkKCiAgICByZXR1cm4gdG9yZXQKCgpjbGFzcyBBcmJvbDoKICAgICIiIlJlcHJlc2VudGEgdW4gYXJib2wuIiIiCiAgICAKICAgIGRlZiBfX2luaXRfXyhzZWxmLCByYWl6LCBzdWJub2Rvcyk6CiAgICAgICAgc2VsZi5fcmFpeiA9IHJhaXoKICAgICAgICBzZWxmLl9zdWJub2RvcyA9IGxpc3Qoc3Vibm9kb3MpCiAgICAgICAgCiAgICBAcHJvcGVydHkKICAgIGRlZiByYWl6KHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLl9yYWl6CiAgICAKICAgIEBwcm9wZXJ0eQogICAgZGVmIHN1Ym5vZG9zKHNlbGYpOgogICAgICAgIHJldHVybiBsaXN0KHNlbGYuX3N1Ym5vZG9zKQogICAgCiAgICBkZWYgX19sZW5fXyhzZWxmKToKICAgICAgICByZXR1cm4gbGVuKHNlbGYuX3N1Ym5vZG9zKQogICAgCiAgICBkZWYgX19nZXRpdGVtX18oc2VsZiwgaXRlbSk6CiAgICAgICAgcmV0dXJuIHNlbGYuX3N1Ym5vZG9zW2l0ZW1dCiAgICAKICAgIGRlZiBfX3N0cl9fKHNlbGYpOgogICAgICAgIHJldHVybiBzdHIuZm9ybWF0KCJ7fSh7fSkiLCBzdHIoc2VsZi5yYWl6KSwgIiwgIi5qb2luKFtzdHIoeCkgZm9yIHggaW4gc2VsZi5fc3Vibm9kb3NdKSkKICAgIAogICAgQHN0YXRpY21ldGhvZAogICAgZGVmIGNvbWJpbmEoYTEsIGEyKToKICAgICAgICByZXR1cm4gQXJib2woYTEucmFpeiArIGEyLnJhaXosIGExLnN1Ym5vZG9zICsgYTIuc3Vibm9kb3MpCgoKY2xhc3MgQWx1bW5vOgogICAgIiIiIFVuIGFsdW1ubyBkZSB1bmEgYXNpZ25hdHVyYS4gIiIiCiAgICBkZWYgX19pbml0X18oc2VsZiwgbm9tYnJlLCBkbmkpOgogICAgICAgIHNlbGYuX25vbWJyZSA9IG5vbWJyZQogICAgICAgIHNlbGYuX2RuaSA9IGRuaQogICAgICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgZG5pKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLl9kbmkKICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgbm9tYnJlKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLl9ub21icmUKICAgIAogICAgZGVmIF9fc3RyX18oc2VsZik6CiAgICAgICAgcmV0dXJuIHN0ci5mb3JtYXQoInt9IC0ge30iLCBzZWxmLmRuaSwgc2VsZi5ub21icmUpCgoKY2xhc3MgRXJhc211cyhBbHVtbm8pOgogICAgIiIiIEFsdW1ubyBwcm92ZW5pZW50ZSBkZWwgZXh0cmFuamVyby4gIiIiCiAgICBkZWYgX19pbml0X18oc2VsZiwgbm9tYnJlLCBkbmksIHBhaXMpOgogICAgICAgIHN1cGVyKCkuX19pbml0X18obm9tYnJlLCBkbmkpCiAgICAgICAgc2VsZi5fcGFpcyA9IHBhaXMKICAgICAgICAKICAgIEBwcm9wZXJ0eQogICAgZGVmIHBhaXMoc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuX3BhaXMKICAgIAogICAgZGVmIF9fc3RyX18oc2VsZik6CiAgICAgICAgcmV0dXJuIHN0ci5mb3JtYXQoInt9ICh7fSkiLCBzdXBlcigpLl9fc3RyX18oKSwgc2VsZi5wYWlzKQoKCmNsYXNzIE5vdGE6CiAgICAiIiIgTm90YSBkZSB1biBhbHVtbm8uICIiIgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGFsdW1ubywgbm90YSk6CiAgICAgICAgc2VsZi5fYWx1bW5vID0gYWx1bW5vCiAgICAgICAgc2VsZi5fbm90YSA9IG5vdGEKICAgICAgICAKICAgIEBwcm9wZXJ0eQogICAgZGVmIGFsdW1ubyhzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5fYWx1bW5vCiAgICAKICAgIEBwcm9wZXJ0eQogICAgZGVmIG5vdGEoc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuX25vdGEKICAgIAogICAgZGVmIF9fc3RyX18oc2VsZik6CiAgICAgICAgcmV0dXJuIHN0ci5mb3JtYXQoInt9OiB7OjUuMmZ9Iiwgc3RyKHNlbGYuYWx1bW5vKSwgc2VsZi5ub3RhKQogICAgCgpjbGFzcyBBc2lnbmF0dXJhOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIG5vbWJyZSwgbm9tYnJlX3Byb2Zlc29yLCBhbm5vKToKICAgICAgICBzZWxmLl9ub21icmUgPSBub21icmUKICAgICAgICBzZWxmLl9ub21icmVfcHJvZmVzb3IgPSBub21icmVfcHJvZmVzb3IKICAgICAgICBzZWxmLl9hbm5vID0gYW5ubwogICAgICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgbm9tYnJlKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLl9ub21icmUKICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgbm9tYnJlX3Byb2Zlc29yKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLl9ub21icmVfcHJvZmVzb3IKICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgYW5ubyhzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5fYW5ubwogICAgCiAgICBkZWYgX19zdHJfXyhzZWxmKToKICAgICAgICByZXR1cm4gc3RyLmZvcm1hdCggInt9IHt9ICh7fSkiLCBzZWxmLm5vbWJyZSwgc2VsZi5ub21icmVfcHJvZmVzb3IsIHNlbGYuYW5ubykKCgpjbGFzcyBDYWxpZmljYWNpb25lczoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBub21icmVfZXhhbWVuLCBhc2lnbmF0dXJhLCBsaXN0YV9ub3Rhcyk6CiAgICAgICAgc2VsZi5fbm9tYnJlX2V4YW1lbiA9IG5vbWJyZV9leGFtZW4KICAgICAgICBzZWxmLl9hc2lnbmF0dXJhID0gYXNpZ25hdHVyYQogICAgICAgIHNlbGYuX2xpc3RhX25vdGFzID0gbGlzdGFfbm90YXMKICAgICAgICAKICAgIEBwcm9wZXJ0eQogICAgZGVmIG5vbWJyZV9leGFtZW4oc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuX25vbWJyZV9leGFtZW4KICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgYXNpZ25hdHVyYShzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5fYXNpZ25hdHVyYQogICAgCiAgICBAcHJvcGVydHkKICAgIGRlZiBub3RhcyhzZWxmKToKICAgICAgICByZXR1cm4gbGlzdChzZWxmLl9saXN0YV9ub3RhcykKICAgICAgICAKICAgIGRlZiBfX2xlbl9fKHNlbGYpOgogICAgICAgIHJldHVybiBsZW4oc2VsZi5fbGlzdGFfbm90YXMpCiAgICAgICAgCiAgICBkZWYgX19nZXRpdGVtX18oc2VsZiwgaXRlbSk6CiAgICAgICAgcmV0dXJuIHNlbGYuX2xpc3RhX25vdGFzW2l0ZW1dCiAgICAKICAgIGRlZiBfX3N0cl9fKHNlbGYpOgogICAgICAgIHJldHVybiBzdHIuZm9ybWF0KCJ7fVxue31cbnt9XG4iLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuYXNpZ25hdHVyYS5ub21icmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5ub21icmVfZXhhbWVuLAogICAgICAgICAgICAgICAgICAgICAgICAgICJcbiIuam9pbihbc3RyKG5vdGEpIGZvciBub3RhIGluIHNlbGYuX2xpc3RhX25vdGFzXSkpCgoKaW1wb3J0IHVuaXR0ZXN0IAoKCmNsYXNzIFRlc3RDYWxjdWxhRGlmZXJlbmNpYSh1bml0dGVzdC5UZXN0Q2FzZSk6CiAgICBkZWYgc2V0VXAoc2VsZik6CiAgICAgICAgc2VsZi5kaWZlcmVuY2lhX2NhcmFjdGVyX2V4dHJhID0gYWJzKG9yZCgnYScpIC0gb3JkKCd6JykpICsgMTAKICAgICAgICAKICAgIGRlZiB0ZXN0SXQoc2VsZik6CiAgICAgICAgY2FkczEgPSBbInJhZGlhIiwgInJhZGlhIl0KICAgICAgICBjYWRzMiA9IFsicmFkaW8iLCAicmFkYSJdCiAgICAgICAgcmVzdWx0YWRvcyA9IFthYnMob3JkKCdvJykgLSBvcmQoJ2EnKSksCiAgICAgICAgICAgICAgICAgICAgICBhYnMob3JkKCdpJykgLSBvcmQoJ2EnKSkKICAgICAgICAgICAgICAgICAgICAgICAgKyBzZWxmLmRpZmVyZW5jaWFfY2FyYWN0ZXJfZXh0cmFdCiAgICAgICAgCiAgICAgICAgZm9yIGksIHJlc3VsdGFkbyBpbiBlbnVtZXJhdGUocmVzdWx0YWRvcyk6CiAgICAgICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwocmVzdWx0YWRvLCBjYWxjdWxhX2RpZmVyZW5jaWEoY2FkczFbaV0sIGNhZHMyW2ldKSkKICAgICAgICAgICAgCgoKY2xhc3MgVGVzdEFyYm9sKHVuaXR0ZXN0LlRlc3RDYXNlKToKICAgIGRlZiBzZXRVcChzZWxmKToKICAgICAgICBzZWxmLmExID0gQXJib2woImEiLCBbQXJib2woImIiLCBbImMiLCAiZCJdKSwgImUiXSkKICAgICAgICBzZWxmLmEyID0gQXJib2woImEiLCBbImYiLCAiZyJdKQogICAgCiAgICBkZWYgdGVzdEFyYm9sMShzZWxmKToKICAgICAgICBzdWJfYTMgPSBzZWxmLmExWzBdCiAgICAgICAgCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgyLCBsZW4oc3ViX2EzKSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCdiJywgc3ViX2EzLnJhaXopCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgnYycsIHN1Yl9hM1swXSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCdkJywgc3ViX2EzWzFdKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoImIoYywgZCkiLCBzdHIoc3ViX2EzKSkKICAgICAgICAKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKDIsIGxlbihzZWxmLmExKSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCdhJywgc2VsZi5hMS5yYWl6KQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoc3ViX2EzLCBzZWxmLmExWzBdKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoJ2UnLCBzZWxmLmExWzFdKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoImEoYihjLCBkKSwgZSkiLCBzdHIoc2VsZi5hMSkpCiAgICAgICAgCiAgICBkZWYgdGVzdEFyYm9sMihzZWxmKToKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKDIsIGxlbihzZWxmLmEyKSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCdhJywgc2VsZi5hMi5yYWl6KQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoJ2YnLCBzZWxmLmEyWzBdKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoJ2cnLCBzZWxmLmEyWzFdKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoImEoZiwgZykiLCBzdHIoc2VsZi5hMikpCiAgICAKICAgIGRlZiB0ZXN0Q29tYmluYShzZWxmKToKICAgICAgICBhNCA9IEFyYm9sLmNvbWJpbmEoc2VsZi5hMSwgc2VsZi5hMikKICAgICAgICAKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKDQsIGxlbihhNCkpCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbChzZWxmLmExLnJhaXogKyBzZWxmLmEyLnJhaXosIGE0LnJhaXopCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbChzZWxmLmExWzBdLCBhNFswXSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCdlJywgYTRbMV0pCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgnZicsIGE0WzJdKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoJ2cnLCBhNFszXSkgICAgICAgIAogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoImFhKGIoYywgZCksIGUsIGYsIGcpIiwgc3RyKGE0KSkKICAgICAKICAgICAKY2xhc3MgVGVzdE5vdGFzQXNpZ25hdHVyYSh1bml0dGVzdC5UZXN0Q2FzZSk6CiAgICBkZWYgc2V0VXAoc2VsZik6CiAgICAgICAgc2VsZi5hbHVtbm8xID0gQWx1bW5vKCJCYWx0YXNhciIsICIxWCIpCiAgICAgICAgc2VsZi5hbHVtbm8yID0gRXJhc211cygiV2FsbHkiLCAiWDJYIiwgIk5vc2VsYW5kaWEiKQogICAgICAgIHNlbGYubm90YTEgPSBOb3RhKHNlbGYuYWx1bW5vMSwgOS41KQogICAgICAgIHNlbGYubm90YTIgPSBOb3RhKHNlbGYuYWx1bW5vMiwgNS43NTIpCiAgICAgICAgc2VsZi5hc2lnbmF0dXJhID0gQXNpZ25hdHVyYSgiQUxTIiwgIkJhbHRhc2FyIiwgMjAxOSkKICAgICAgICBzZWxmLnBhcmNpYWwxID0gQ2FsaWZpY2FjaW9uZXMoInBhcmNpYWwgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuYXNpZ25hdHVyYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW3NlbGYubm90YTEsIHNlbGYubm90YTJdKQogICAgCiAgICBkZWYgdGVzdEFsdW1ubzEoc2VsZik6CiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgiQmFsdGFzYXIiLCBzZWxmLmFsdW1ubzEubm9tYnJlKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoIjFYIiwgc2VsZi5hbHVtbm8xLmRuaSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCIxWCAtIEJhbHRhc2FyIiwgc3RyKHNlbGYuYWx1bW5vMSkpCiAgICAgICAgCiAgICBkZWYgdGVzdEFsdW1ubzIoc2VsZik6CiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgiV2FsbHkiLCBzZWxmLmFsdW1ubzIubm9tYnJlKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoIlgyWCIsIHNlbGYuYWx1bW5vMi5kbmkpCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgiTm9zZWxhbmRpYSIsIHNlbGYuYWx1bW5vMi5wYWlzKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoIlgyWCAtIFdhbGx5IChOb3NlbGFuZGlhKSIsIHN0cihzZWxmLmFsdW1ubzIpKQogICAgCiAgICBkZWYgdGVzdE5vdGExKHNlbGYpOgogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoc2VsZi5hbHVtbm8xLCBzZWxmLm5vdGExLmFsdW1ubykKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKDkuNSwgc2VsZi5ub3RhMS5ub3RhKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoIjFYIC0gQmFsdGFzYXI6ICA5LjUwIiwgc3RyKHNlbGYubm90YTEpKQogICAgICAgIAogICAgZGVmIHRlc3ROb3RhMihzZWxmKToKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKHNlbGYuYWx1bW5vMiwgc2VsZi5ub3RhMi5hbHVtbm8pCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCg1Ljc1Miwgc2VsZi5ub3RhMi5ub3RhKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoIlgyWCAtIFdhbGx5IChOb3NlbGFuZGlhKTogIDUuNzUiLCBzdHIoc2VsZi5ub3RhMikpCiAgICAgICAgCiAgICBkZWYgdGVzdEFzaWduYXR1cmEoc2VsZik6CiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgiQUxTIiwgc2VsZi5hc2lnbmF0dXJhLm5vbWJyZSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCJCYWx0YXNhciIsIHNlbGYuYXNpZ25hdHVyYS5ub21icmVfcHJvZmVzb3IpCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgyMDE5LCBzZWxmLmFzaWduYXR1cmEuYW5ubykKICAgICAgICAKICAgIGRlZiB0ZXN0Q2FsaWZpY2FjaW9uZXMoc2VsZik6CiAgICAgICAgdGV4dCA9IChzZWxmLnBhcmNpYWwxLmFzaWduYXR1cmEubm9tYnJlCiAgICAgICAgICAgICsgIlxuIiArIHNlbGYucGFyY2lhbDEubm9tYnJlX2V4YW1lbgogICAgICAgICAgICArICJcbiIgKyAiXG4iLmpvaW4oW3N0cih4KSBmb3IgeCBpbiBzZWxmLnBhcmNpYWwxLm5vdGFzXSkKICAgICAgICAgICAgKyAiXG4iKQogICAgICAgIAogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoInBhcmNpYWwgMSIsIHNlbGYucGFyY2lhbDEubm9tYnJlX2V4YW1lbikKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKHNlbGYuYXNpZ25hdHVyYSwgc2VsZi5wYXJjaWFsMS5hc2lnbmF0dXJhKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoMiwgbGVuKHNlbGYucGFyY2lhbDEpKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoW3NlbGYubm90YTEsIHNlbGYubm90YTJdLCBzZWxmLnBhcmNpYWwxLm5vdGFzKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoc2VsZi5ub3RhMSwgc2VsZi5wYXJjaWFsMVswXSkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKHNlbGYubm90YTIsIHNlbGYucGFyY2lhbDFbMV0pCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCh0ZXh0LCBzdHIoc2VsZi5wYXJjaWFsMSkpCgoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIHVuaXR0ZXN0Lm1haW4oKQo=