import unittest
from random import *
from racionales import *

def IsNull(p):
    for i in p.keys():
        if p[i]!=0:
            return 0
    return 1        

def random_poly(integer=0):
    """Genera un polinomio al azar de grado <= 100, con coeficientes
    reales o enteros."""
    
    def coeff(x):
        if integer:
            return int(x)
        else:
            return x
        
    non_nil = randrange(21)
    d = dict([(randrange(101), Racional(coeff(randrange(-20,20)),
                                        coeff(choice(range(-30,0)+\
                                                     range(1,30)))))
              for k in range(non_nil)])
    return d

def escalar(x, p):
    """Multiplica el escalar 'x' por el polinomio p (diccionario)"""

    r = {}
    for k in p:
        r[k] = x*p[k]

    return r

def eq(d1, d2):
    e={}
    e.update(d1)
    e.update(d2)
    for i in e:
        e[i] = d1.get(i,0) - d2.get(i,0)
    return IsNull(e)


class Parte1TestCase(unittest.TestCase):

    def setUp(self):
        self.p = random_poly()
        self.q = random_poly()
        self.a = {0:3, 1:4, 2:5}
        self.b = {1:-3, 5:-10, 3:1}

    def testSumarNuloD(self):
        self.failUnless(eq(p.sumar(self.p, {}), self.p),
                        """Incorrecto: p+0 != p""")

    def testSumarNuloI(self):
        self.failUnless(eq(p.sumar({}, self.q), self.q),
                        "Incorrecto: 0+p != p")

    def testSumar(self):
        self.assert_(eq(p.sumar(self.a, self.b), {0:3, 1:1, 2:5, 3:1, 5:-10}),
                     """Incorrecta la suma""")

    def testMultiplicarCero(self):
        self.assert_(IsNull(p.multiplicar(self.p, {})) ,
                      'Incorrecto: p*{} !=0 ')  

    def testMultiplicarUno(self):
        self.assert_(eq(p.multiplicar(self.p, {0:1}), self.p),
                      'Incorrecto: p*{0:1} !=p')  

    def testMultiplicarGradoCero(self):
        self.assert_(eq(p.multiplicar({0:-3}, self.p), escalar(-3, self.p)),
                     'Incorrecto: {0:-3}*p != -3p')
        
    def testMultiplicarCuadrado(self):                  
        self.assert_(eq(p.multiplicar({1:1, 0:1},{1:1, 0:1}), {2:1, 1:2, 0:1}),
                       'Incorrecto: {1:1, 0:1}*{1:1,0:1} != {2:1, 1:2, 0:1}')

    def testMultiplicarMenosUno(self):
        self.assert_(eq(p.sumar(p.multiplicar({0:-1}, self.p), self.p), {0:0}),
                     'Incorrecto -1*p != -p')
        
    def testPotenciaCero(self):
        self.assert_(eq(p.potencia(self.p, 0), {0:1}),
                     'Incorrecto p**0 != 1')
        
    def testPotenciaUno(self):
        self.assert_(eq(p.potencia(self.p, 1), self.p),
                     'Incorrecto p**1 != p')
        
    def testPotenciaDos(self):
        self.assert_(eq(p.potencia(self.p, 2), p.multiplicar(self.p, self.p)),
                     'Incorrecto p**2 != p*p')
        
    def testPotenciaTres(self):
        self.assert_(eq(p.potencia(self.p, 3),
            p.multiplicar(p.multiplicar(self.p, self.p), self.p)),
                     'Incorrecto p**3 != p*p*p')

    def testEvaluarCero(self):
        self.assert_(p.evaluar(self.p, 0) == self.p.get(0, 0),
                     'Incorrecto: p(0) != p_0')

    def testEvaluarUno(self):
        self.assert_(p.evaluar(self.b, 1) == -12,
                     'Incorrecta la evaluacion en 1')

    def testEvaluarMenosUno(self):
        self.assert_(p.evaluar(self.a, -1) == 4,
                     'Incorrecta la evaluacion en -1')

    def testEvaluar(self):
        self.assert_(p.evaluar(self.b, 3) == -2412,
                     'Incorrecta la funcion evaluar')

    def testGradoNulo(self):
        self.assert_(p.grado({}) == -1,
                     'Incorrecto: gr(0) != -1')

    def testGradoNulo2(self):
        self.assert_(p.grado({1:0, 5:0, 100:0}) == -1,
                     'Incorrecto: gr(0) != -1')

    def testGradoCero(self):
        self.assert_(p.grado({0:1}) == 0,
                     'Incorrecto: gr(1) != 0')

    def testGradoCero2(self):
        self.assert_(p.grado({0:1, 1:0, 2:0}) == 0,
                     'Incorrecto: gr(1) != 0')
        
    def testGradoUno(self):
        self.assert_(p.grado({0:1, 1:5}) == 1,
                     'Incorrecto: gr(X) != 1')

    def testGradoUno2(self):
        self.assert_(p.grado({0:2, 1:3, 2:0}) == 1,
                     'Incorrecto: gr(X) != 1')

    def testGrado(self):
        self.assert_(p.grado(self.b) == 5,
                     'Incorrecta la funcion grado')
    
    def testGrado2(self):
        self.assert_(p.grado({80: 4, 1: 2, 100: 1, 5: 3}) == 100,
                     'Incorrecta la funcion grado')

    def testDividirUno(self):
        self.assert_(eq(p.dividir(self.p, {0:1}), self.p),
                     'Incorrecto: p/{0:1} != p')

    def testDividirSiMismo(self):
        self.assert_(eq(p.dividir(self.a, self.a), {0:1}),
                     'Incorrecto: p/p != {0:1}')

    def testDividirMenorGrado(self):
        self.assert_(eq(p.dividir({2:1},{3:2}), {0:0}),
                     'Incorrecto: {2:1}/{3:2} != {0:0}')

    def testDividir(self):
        self.assert_(eq(p.dividir(self.b, self.a),
                        {3:-2, 2:Racional(8,5), 1:Racional(3,25),
                         0:Racional(-132,125)}),
                     'Incorrecta la division')

    def testRestoCero(self):
        self.assert_(eq(p.resto(self.a, self.a), {0:0}),
                     'Incorrecto p % p != 0')

    def testRestoNoCero(self):
        self.assert_(eq(p.resto({0:1}, {1:1}), {0:1}),
                     'Incorrecto 1 % X != 1')

    def testRestoGrado(self):
        self.assert_(p.grado(p.resto(self.b, self.a)) < p.grado(self.a),
                     'Incorrecto: grado(p % q) >= grado(q)')
        
    def testDivisionEntera(self):
        self.assert_(eq(p.sumar(p.multiplicar(self.a,p.dividir(self.b,self.a)),
                                p.resto(self.b, self.a)), self.b),
                     'No se cumple la relacion q = p*(q/p) + q%p')
    

class Parte2y3TestCase(unittest.TestCase):

    def setUp(self):
        self.p = random_poly()
        self.q = random_poly()
        self.a = {0:3, 1:4, 2:5}
        self.b = {1:-3, 5:-10, 3:1}
        self.polip = p.Polinomio(self.p)
        self.poliq = p.Polinomio(self.q)
        self.polia = p.Polinomio(self.a)
        self.polib = p.Polinomio(self.b)

    def testPoliGrado(self):
        self.assert_(self.polia.grado() == 2,
                     'Incorrecto el metodo grado')

    def testPoliGrado2(self):
        self.assert_(p.Polinomio({3:1, 100:0, 50:0}).grado() == 3,
                     'Incorrecto el metodo grado')

    def testPoliGradoNulo(self):
        self.assert_(p.Polinomio({0:0}).grado() == -1,
                     'Incorrecto 0.grado() != -1')

    def testPoliGradoNulo2(self):
        self.assert_(p.Polinomio({1:0, 5:0, 100:0}).grado() == -1,
                     'Incorrecto 0.grado() != -1')

    def testPoliGradoCero(self):
        self.assert_(p.Polinomio({0:1}).grado() == 0,
                     'Incorrecto 1.grado() != 0')

    def testPoliGradoCero2(self):
        self.assert_(p.Polinomio({0:1, 1:0, 2:0}).grado() == 0,
                     'Incorrecto 1.grado() != 0')
        
    def testPoliGradoUno(self):
        self.assert_(p.Polinomio({0:1, 1:5}).grado() == 1,
                     'Incorrecto X.grado() != 1')

    def testPolinomioGrado(self):
        self.assert_(self.polib.grado() == 5,
                     'Incorrecta la funcion grado')
    
    def testPoliGrado2(self):
        self.assert_(p.Polinomio({80: 4, 1: 2, 100: 1, 5: 3}).grado() == 100,
                     'Incorrecta la funcion grado')

    def testPoliEqual(self):
        self.assert_(self.polip == self.polip,
                     'Incorrecta la igualdad en polinomios')

    def testPoliEqualEntero(self):
        self.assert_(p.Polinomio({0:1}) == 1,
                     'Incorrecto: (polinomio 1) != 1')
        
    def testPoliNotEqual(self):
        self.assert_(not self.polip != self.polip,
                     "Incorrecta el 'distinto' en polinomios")
        
    def testPoliSumarCero(self):
        self.assert_(self.polip + p.Polinomio({0:0}) == self.polip,
                     'Incorrecta la suma con el polinomio nulo: p + 0 != p')

    def testPoliSumarCero2(self):
        self.assert_(self.polip + 0 == self.polip,
                     'Incorrecta la suma con el entero 0: p + 0 != p')

    def testPoliSumarCeroI(self):
        self.assert_(p.Polinomio({0:0}) + self.polip == self.polip,
                     'Incorrecta la suma por izquierda con el polinomio nulo: 0 + p != p')

    def testPoliSumarCero2I(self):
        self.assert_(0 + self.polip == self.polip,
                     'Incorrecta la suma por izquierda con el entero 0: 0 + p != p')

    def testPoliSumar(self):
        self.assert_(self.polia + self.polib == p.Polinomio({0:3, 1:1, 2:5, 3:1, 5:-10}),
                     'Incorrecta la suma de polinomios')

    def testPoliRestarCero(self):
        self.assert_(self.polia - p.Polinomio({0:0}) == self.polia,
                     'Incorrecto: p - 0 != p')

    def testPoliRestarEnteroCero(self):
        self.assert_(self.polia - 0 == self.polia,
                     'Incorrecto: p - (racional 0) != p')

    def testPoliRestarCeroI(self):
        self.assert_(p.Polinomio({0:0}) - self.polip == (-1)*self.polip,
                     'Incorrecto: 0 - p != -p')

    def testPoliRestarEnteroCeroI(self):
        self.assert_(0 - self.polip == (-1)*self.polip,
                     'Incorrecto: (racional 0) - p != -p')

    def testPoliRestarSiMismo(self):
        self.assert_(self.polip - self.polip == p.Polinomio({0:0}),
                     'Incorrecto: p - p != 0')

    def testPoliMultiplicarUno(self):
        self.assert_(self.polip*p.Polinomio({0:1}) == self.polip,
                     'Incorrecta el producto con el polinomio uno: p * 1 != p')

    def testPoliMultiplicarUno(self):
        self.assert_(self.polip * 1 == self.polip,
                     'Incorrecta el producto con el entero 1: p * 1 != p')

    def testPoliMultiplicarUnoI(self):
        self.assert_(p.Polinomio({0:1}) * self.polip == self.polip,
                     'Incorrecta la multiplicacion por izquierda con el polinomio uno: 1 * p != p')

    def testPoliMultiplicarUno2I(self):
        self.assert_(1 * self.polip == self.polip,
                     'Incorrecta la multiplicacion por izquierda con el entero 1: 1 * p != p')

    def testPoliMultiplicacion(self):
        self.assert_(self.polia * p.Polinomio({2:1,0:1}) == p.Polinomio({0:3,1:4,2:8,3:4,4:5}),
                     'Incorrecta la multiplicacion de polinomios')

    def testPoliPotenciaCero(self):
        self.assert_(self.polia ** 0 == p.Polinomio({0:1}),
                     'Incorrecto p**0 != 1')
        
    def testPoliPotenciaUno(self):
        self.assert_(self.polia ** 1 == self.polia,
                     'Incorrecto p**1 != p')
        
    def testPoliPotenciaDos(self):
        self.assert_(self.polia ** 2 == self.polia * self.polia,
                     'Incorrecto p**2 != p*p')
        
    def testPoliPotenciaTres(self):
        self.assert_(self.polia ** 3 == self.polia * self.polia * self.polia,
                     'Incorrecto p**3 != p*p*p')

    def testPoliDividirUno(self):
        self.assert_(self.polia/p.Polinomio({0:1}) == self.polia,
                     'Incorrecto: p/1 != p')

    def testPoliDividirEnteroUno(self):
        self.assert_(self.polia/1 == self.polia,
                     'Incorrecto: p/(racional 1) != p')

    def testPoliDividirSiMismo(self):
        self.assert_(self.polia/self.polia == p.Polinomio({0:1}),
                     'Incorrecto: p/p != 1')

    def testPoliModCero(self):
        self.assert_(self.polia % self.polia == p.Polinomio({0:0}),
                     'Incorrecto: p % p != 0')

    def testPoliModNoCero(self):
        self.assert_(p.Polinomio({0:1}) % p.Polinomio({1:1}) == p.Polinomio({0:1}),
                     'Incorrecto: 1 % X != 1')

    def testPoliModEntero(self):
        self.assert_(1 % p.Polinomio({1:1}) == 1,
                     'Incorrecto: (racional 1) % X != (racional 1)')

    def testPoliModEntero2(self):
        self.assert_(p.Polinomio({1:1}) %  3 == 0,
                     'Incorrecto: X % (racional 3) != 0')

    def testPoliDivisionEntera(self):
        self.assert_(self.polia * (self.polib/self.polia) + self.polib%self.polia == self.polib,
                     'No se cumple p*(q/p) + q%p == q')
        
    def testPoliEvaluar(self):
        self.assert_(self.polia(0) == 3,
                     'Incorrecto: p(0) != p_0')

    def testPoliEvaluarUno(self):
        self.assert_(self.polib(1) == -12,
                     'Incorrecto: p(1)')

    def testPoliGetitem(self):
        self.assert_(self.polib[0] == self.polib(0),
                     'Incorrecto p[0]')

    def testPoliGetitem2(self):
        self.assert_(self.polib[1] == -3,
                     'Incorrecto p[1]')

    def testPoliGetitem3(self):
        self.assert_(self.polib[50] == 0,
                     'Incorrecto p[50]')
        
    def testMcdSiMismo(self):
        self.assert_(p.mcd(self.polia, self.polia) == self.polia,
                     'Incorrecto mcd(p, p) != p')

    def testMcdSiMismo2(self):
        self.assert_(p.mcd(self.polia, 5*self.polia).grado() == self.polia.grado(),
                     'Incorrecto grado(mcd(p,p)) != grado(p)')

    def testMcd1(self):
        self.assert_(self.polia % p.mcd(self.polia, self.polib) == 0,
                     'Incorrecto mcd(p,q) no divide a p')

    def testMcd2(self):
        self.assert_(self.polib % p.mcd(self.polia, self.polib) == 0,
                     'Incorrecto mcd(p,q) no divide a q')

    def testRaices(self):
        self.assert_(p.raices_racionales(p.Polinomio({2:1,1:-2,0:1})) == [1],
                     'Incorrecta las raices de X^2-2X+1')

    def testRaices2(self):
        l = p.raices_racionales(p.Polinomio({2:2,1:5,0:-3}))
        self.assert_(len(l) == 2 and -3 in l and Racional(1,2) in l,
                     'Incorrectas las raices de 2X^2+5X-3')

    def testRaices3(self):
        l = p.raices_racionales(p.Polinomio({3:2,2:5,1:-3}))
        self.assert_(len(l) == 3 and 0 in l and Racional(1,2) in l and -3 in l,
                     'Incorrectas las raices de 2X^3+5X^2-3X')

    def testRaicesCoefNoEnteros(self):
        self.failUnlessRaises(Exception,
                              lambda: p.raices_racionales(p.Polinomio({3:Racional(1,2),0:1})),
                              'raices_racionales no detecta coeficientes racionales')
        
                              
def verificar():
    global p
    try:
        import polinomios
        reload(polinomios)
        p = polinomios
    except ImportError:
        print """No se encontro el modulo 'polinomios.py'."""
        return

    loader = unittest.TestLoader()
    test1 = loader.loadTestsFromTestCase(Parte1TestCase)
    test2y3 = loader.loadTestsFromTestCase(Parte2y3TestCase)
    runner = unittest.TextTestRunner()
    runner.run(test1)
    runner.run(test2y3)
    
if __name__ == '__main__':
    verificar()
