import estructura



# Abstraccion Funcional



# Datos compuestos



# escuela: nombre(str) municipalidad(str) numero(int) telefono(str) alumnos(int)

estructura.crear("escuela", "nombre municipalidad numero telefono alumnos")



# alumno: nombre(str) promedio(float)

estructura.crear("alumno", "nombre promedio")



# actualizarTelefonos: lista(escuela) - > lista(escuela)

# actualiza telefonos de la lista de escuelas

# ejemplo: propuesto

def actualizarTelefonos(unaLista):

    if vacia(unaLista):

        return listaVacia

    else:

        return crearLista(cambiarTelefono(cabeza(unaLista)), cola(unaLista))



# Test propuesto



# cambiarTelefono: escuela -> escuela

# actualiza telefono a una Escuela

# ejemplo: cambiarTelefono(escuela("Instituto Internacional", "Santiago", "123", "5551122", "1200"))

# devuelve escuela("Instituto Internacional", "Santiago", "123", "5551122", "1200")

def cambiarTelefono(unaEscuela):

    return escuela(unaEscuela.nombre, unaEscuela.municipalidad, unaEscuela.numero, "2" + unaEscuela.telefono, unaEscuela.alumnos)



# Test

assert cambiarTelefono(escuela("Instituto Internacional", "Santiago", "123", "5551122", "1200")) == escuela("Instituto Internacional", "Santiago", "123", "25551122", "1200")



# actualizarPromedios: lista(alumno) -> lista(alumno)

# actualiza promedios de la lista de alumnos en 5%

# ejemplo: propuesto

def actualizarPromedios(unaLista):

    if vacia(unaLista):

        return listaVacia

    else:

        return crearLista(cambiarPromedio(cabeza(unaLista)), cola(unaLista))



# Test propuesto



# cambiarPromedio: alumno -> alumno

# actualiza promedio a un alumno

# ejemplo: cambiarPromedio(alumno("Juan Perez", 6.0)) devuelve alumno("Juan Perez", 6.3)

def cambiarPromedio(unAlumno):

    nuevoPromedio = unAlumno.promedio * 1.05

    if nuevoPromedio > 7.0:

        nuevoPromedio = 7.0

    return alumno(unAlumno.nombre, nuevoPromedio)



# Test

from cerca import cerca

alum = alumno("Juan Perez", 6.0)

alumMod = cambiarPromedio(alum)

assert alumMod.nombre == alum.nombre and cerca(alumMod.promedio, 6.3, 0.00001)



# mapa : (X -> Y) lista(X) -> lista(Y)

def mapa(f, unaLista):

    if vacia(unaLista):

        return listaVacia

    else:

        return crearLista(f(cabeza(unaLista)), mapa(cola(unaLista)))



# actualizarTelefonosAbstracto: lista(escuela) -> lista(escuela)

def actualizarTelefonosAbstracto(unaLista):

    return mapa(cambiarTelefono, unaLista)



# actualizarPromediosAbstracto: lista(alumno) -> lista(alumno)

def actualizarPromediosAbstracto(unaLista):

    return mapa(cambiarPromedio, unaLista)



# Arboles



# nodo: valor(int) izq(nodo) der(nodo)

estructura.crear("nodo", "valor izq der")



nodoVacio = None



# tamanoArbol: nodo -> int

# devuelve numero de nodos en el arbol

# ejemplo: tamanoArbol(nodo(1, nodoVacio, nodoVacio)) devuelve 1

def tamanoArbol(raiz):

    if raiz == nodoVacio:

        return 0

    else:

        return 1 + tamanoArbol(raiz.izq) + tamanoArbol(raiz.der)



# Test

assert tamanoArbol(nodo(1, nodoVacio, nodoVacio)) == 1



# alturaArbol: nodo -> int

# devuelve la altura de un arbol

# ejemplo: alturaArbol(nodo(1, nodoVacio, nodoVacio)) devuelve 0

def alturaArbol(raiz):

    if raiz == nodoVacio:

        return -1

    else:

        return 1 + max(alturaArbol(raiz.izq), alturaArbol(raiz.der))



# Test

assert alturaArbol(nodo(1, nodoVacio, nodoVacio)) == 0



# esBalanceado: nodo -> bool

# devuelve True si arbol es balanceado

# ejemplo: esBalanceado(nodo(1, nodoVacio, nodoVacio)) devuelve True

def esBalanceado(raiz):

    if raiz == nodoVacio:

        return True

    else:

        return esBalanceado(raiz.izq) and esBalanceado(raiz.der) and abs(alturaArbol(raiz.izq) - alturaArbol(raiz.der)) <= 1



# Test

assert esBalanceado(nodo(1, nodoVacio, nodoVacio)) == True

