import estructura
from lista import *

estructura.crear("arbol", "valor izq der")

#=P1==================================================================

num8 = arbol(8,None,None)
num1 = arbol(1,None,None)
num2 = arbol(2,None,None)
num9 = arbol(9,None,None)
num3 = arbol(3,None,None)

arbolEnunciado = arbol("+",arbol("-",arbol("+",num1,num2),num8) ,arbol("-",num9,num3)) 

#arbolCorrecto: Arbol -> bool
#nos dice si un arbol de expresiones estpa bien construido
#ej: arbolCorrecto(arbolEnunciado) entrega True
def arbolCorrecto(a):
    if (a.izq == None and a.der == None):
        #caso hoja / caso base
        if (type(a.valor == int) or type(a.valor == float)):
            return True
        else:
            #es cualquier otra cosa
            return False
    else:
        #estamos en un nodo interno
        if (a.valor == "+" or a.valor == "-"): #analisis de que sea la operación que queremos
            #es correcto
            if (a.izq != None and a.der != None):
                return (arbolCorrecto(a.izq) and arbolCorrecto(a.der))
            else:
                return False
        else:
            return False

assert arbolCorrecto(arbolEnunciado) == True
assert arbolCorrecto(arbol(num8,"+",8)) == False


#evaluarArbol: Arbol -> num
#entrega el número resultante de hacer todas las operaciones
#ejemplo: evaluarArbol(arbolEnunciado) da 1
def evaluarArbol(a):
    assert (arbolCorrecto(a) == True)
    #si llegamos aca, estamos perfecto, evaluemos

    if (a.izq != None and a.der != None):
        if a.valor == "+":
            return evaluarArbol(a.izq) + evaluarArbol(a.der)
        if a.valor == "-":
            return evaluarArbol(a.izq) - evaluarArbol(a.der)

    else:
        #caso hoja
        assert(a.izq == None and a.der == None)
        return a.valor


assert evaluarArbol(arbolEnunciado) == 1
assert evaluarArbol(num9) == 9

#=P2==================================================================

estructura.crear("entrenador","nombre id")

#arbol de ejemplo :D
a = arbol(valor=entrenador(nombre='pablo', id=3), izq=arbol(valor=entrenador(nombre='juan',id=1), izq=None, der=None),
          der=arbol(valor=entrenador(nombre='pedro', id=5), izq=arbol(valor=entrenador(nombre='marcita', id=4),
                                                                                          izq=None, der=None), der=None))
#agregar: ABB entrenador -> ABB
#agrega un elemento a un ABB, respetando los ordenes del ABB
#ejemplo: agregar(a,entrenador(matias,0)) da arbol(valor=entrenador(nombre='pablo', id=3), izq=arbol(valor=entrenador(nombre='juan', id=1),
#izq=arbol(valor=entrenador(nombre='matias', id=0), izq=None, der=None), der=None), der=arbol(valor=entrenador(nombre='pedro', id=5),
                                                                                             #izq=arbol(valor=entrenador(nombre='marcita', id=4),
                                                                                                       #izq=None, der=None), der=None))
def agregar(ABB,e):
    #caso base, ABB == None
    if ABB == None:
        return arbol(e, None,None)
    else:
        if (ABB.valor.id <= e.id):
            #pa la derecha
            return arbol(ABB.valor,ABB.izq,agregar(ABB.der,e))
        else:
            return arbol(ABB.valor,agregar(ABB.izq,e),ABB.der)
assert agregar(a,entrenador("matias",0)) == arbol(valor=entrenador(nombre='pablo', id=3), izq=arbol(valor=entrenador(nombre='juan', id=1),
                                                                                                    izq=arbol(valor=entrenador(nombre='matias', id=0),
                                                                                                              izq=None, der=None), der=None),
                                                  der=arbol(valor=entrenador(nombre='pedro', id=5), izq=arbol(valor=entrenador(nombre='marcita', id=4),
                                                                                                              izq=None, der=None), der=None))



#mostrarTotal: ABB-> None
#printea todos los elementos del arbol, en orden ascendiente
#ejemplo, al aplicar la función a el ABB a, printeará la info de juaun, pablo, pedro y marcita.
def mostrarTotal(ABB):
    if ABB==None:
        return
    mostrarTotal(ABB.izq)
    print("Nombre: ",ABB.valor.nombre,", id: ",ABB.valor.id)
    mostrarTotal(ABB.der)
    return

#como esta función printea, no tiene receta de diseño.


#buscarMayor: ABB -> elemento (particularmente entrenador en este caso)
#encuentra el mayor elemento de un ABB
#ejemplo buscarMayor(a) entrega entrenador("pedro",5)
def buscarMayor(ABB):
    if ABB == None:
        return None
    elif (ABB.izq == None and ABB.der == None):
        return ABB.valor
    elif (ABB.izq != None and ABB.der == None):
        return ABB.valor
    elif (ABB.izq == None and ABB.der != None):
        return buscarMayor(ABB.der)
    else:
        return buscarMayor(ABB.der)

assert buscarMayor(a) ==entrenador("pedro",5)


#borrar: ABB, num -> ABB
#elimina un elemento del ABB
#ejemplo borrar(a,3) da arbol(valor=entrenador(nombre='juan', id=1),
#izq=None, der=arbol(valor=entrenador(nombre='pedro', id=5), izq=arbol(valor=entrenador(nombre='marcita', id=4), izq=None, der=None), der=None))
def borrar(ABB,idd):
    if ABB == None:
        return None
    else:
        #aca al menos tenemos un nodo
        if ABB.valor.id > idd:
            return arbol(ABB.valor, borrar(ABB.izq,idd)         , ABB.der)
            #para la izquierda
        elif ABB.valor.id < idd:
            return arbol(ABB.valor,ABB.izq, borrar(ABB.der,idd))
            #para la derecha
        else:
            #muchos casos uuuu
            #justo es en ese nodo!

            #caso ambos lados son vacios
            if (ABB.izq == None and ABB.der == None):
                return None
            #caso izq vacio
            elif (ABB.izq == None and ABB.der != None):
                return ABB.der
            #caso der vacio
            elif (ABB.izq != None and ABB.der == None):
                return ABB.izq
            #caso hay para ambos lados
            else:
                mayor = buscarMayor(ABB.izq)
                izq_actualizada= borrar(ABB.izq,mayor.id)
                return arbol(mayor, izq_actualizada,ABB.der)

assert borrar(a,3) == arbol(valor=entrenador(nombre='juan', id=1), izq=None, der=arbol(valor=entrenador(nombre='pedro',
                                                                                         id=5), izq=arbol(valor=entrenador(nombre='marcita', id=4), izq=None,
                                                                                                          der=None), der=None))

#=P3==================================================================
def mapa(f,unaLista):
    if vacia(unaLista):
        return listaVacia
    else :
        return crearLista(f(cabeza(unaLista)),mapa(f,cola(unaLista)))

#lista de ejemplo
lista_juguete = lista( entrenador("pablo",2) ,    lista(entrenador("pepe",10)   ,lista(entrenador("laura",50),None)   )    )

#a)
#función auxiliar parte a
#add1: entrenador -> entrenador
#aumenta en 1 el id del entrenador
#ejemplo add1(entrenador("pablo",2)) da entrenador("pablo",3)
def add1(e):
    return entrenador(e.nombre,e.id + 1)

assert add1(entrenador("pablo",2)) == entrenador("pablo",3)
assert add1(entrenador("pablo",0)) == entrenador("pablo",1)

#ahora, aplicamos mapa como siempre nomas
lista2 = mapa(add1,lista_juguete)

#b)
#mapaPlus: función lista num str -> lista
#aplica la funcion a cada elemento de la lista con las dos variables extra
def mapaPlus(f,lis,x,y):
    if lis == None:
        return None
    else :
        return lista(f(cabeza(lis),x,y),(mapaPlus(f,cola(lis),x,y)))


#c)
#addxy: entrenador x y -> entrenador
#aumenta un valor x al id y agrega el string y al final de la palabra
def addxy(e,x,y):
    return entrenador(e.nombre+y,e.id+x)

assert addxy(entrenador("claudia",2), 3, "+") == entrenador("claudia+",5)

assert  mapaPlus(addxy,lista2,2,"aa") == lista(valor=entrenador(nombre='pabloaa', id=5), siguiente=lista(valor=entrenador(nombre='pepeaa', id=13),
                                                                            siguiente=lista(valor=entrenador(nombre='lauraaa', id=53), siguiente=None)))

    
lista3 = mapaPlus(addxy,lista_juguete,3,"_")









