-- |
-- Module      : PolOperaciones
-- Description : Operaciones con el TAD de los polinomios.
-- License     : Creative Commons
-- Maintainer  : José A. Alonso
-- 
-- En este módulo se definen operaciones con el TAD (tipo abstracto de
-- datos) de los polinomios estudiado en el 
-- <http://bit.ly/1WYZJuC tema 21> del curso. 
-- 
-- En los ejemplos se usarán los siguientes polinomios:
--
-- > ejPol1, ejPol2, ejPol3, ejTerm:: Polinomio Int
-- > ejPol1 = consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero))
-- > ejPol2 = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero))
-- > ejPol3 = consPol 4 6 (consPol 1 2 polCero)
-- > ejTerm = consPol 1 4 polCero

module I1M.PolOperaciones
    ( module I1M.Pol
    -- * Funciones sobre términos
    , creaTermino
    , termLider
    -- * Suma de polinomios
    , sumaPol
    -- * Producto de polinomios
    , multPorTerm 
    , multPol 
    , polUnidad
    -- * Valor de un polinomio en un punto
    , valor
    -- * Verificación de raices de polinomios
    , esRaiz
    -- * Derivación de polinomios
    , derivada 
    -- * Resta de polinomios
    , restaPol) 
    where

-- ---------------------------------------------------------------------
-- Importación de librerías                                           --
-- ---------------------------------------------------------------------

import I1M.Pol

-- ---------------------------------------------------------------------
-- Ejemplos                                                           --
-- ---------------------------------------------------------------------

-- Ejemplos de polinomios con coeficientes enteros:
ejPol1, ejPol2, ejPol3, ejTerm:: Polinomio Int
ejPol1 = consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero))
ejPol2 = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero))
ejPol3 = consPol 4 6 (consPol 1 2 polCero)
ejTerm = consPol 1 4 polCero

-- ---------------------------------------------------------------------
-- Funciones sobre términos
-- ---------------------------------------------------------------------

-- | (creaTermino n a) es el término a*x^n. Por ejemplo, 
-- 
-- > creaTermino 2 5  ==  5*x^2
creaTermino:: (Num t, Eq t) => Int -> t -> Polinomio t
creaTermino n a = consPol n a polCero

-- | (termLider p) es el término líder del polinomio p. Por ejemplo,
-- 
-- > ejPol2            ==  x^5 + 5*x^2 + 4*x
-- > termLider ejPol2  ==  x^5
termLider:: (Num t, Eq t) => Polinomio t -> Polinomio t
termLider p = creaTermino (grado p) (coefLider p)

-- ---------------------------------------------------------------------
-- Suma de polinomios                                                 --
-- ---------------------------------------------------------------------

-- | (sumaPol p q) es la suma de los polinomios p y q. Por ejemplo, 
-- 
-- > ejPol1                 ==  3*x^4 + -5*x^2 + 3
-- > ejPol2                 ==  x^5 + 5*x^2 + 4*x
-- > sumaPol ejPol1 ejPol2  ==  x^5 + 3*x^4 + 4*x + 3
sumaPol:: (Num a, Eq a) => Polinomio a -> Polinomio a -> Polinomio a
sumaPol p q 
    | esPolCero p = q
    | esPolCero q = p
    | n1 > n2      = consPol n1 a1 (sumaPol r1 q)
    | n1 < n2      = consPol n2 a2 (sumaPol p r2)
    | otherwise    = consPol n1 (a1+a2) (sumaPol r1 r2)
    where n1 = grado p
          a1 = coefLider p
          r1 = restoPol p
          n2 = grado q
          a2 = coefLider q
          r2 = restoPol q

-- ---------------------------------------------------------------------
-- Producto de polinomios                                             --
-- ---------------------------------------------------------------------

-- | (multPorTerm t p) es el producto del término t por el polinomio
-- p. Por ejemplo,
-- 
-- > ejTerm                     ==  4*x
-- > ejPol2                     ==  x^5 + 5*x^2 + 4*x
-- > multPorTerm ejTerm ejPol2  ==  4*x^6 + 20*x^3 + 16*x^2
multPorTerm :: (Num t, Eq t) => Polinomio t -> Polinomio t -> Polinomio t
multPorTerm term pol 
    | esPolCero pol = polCero
    | otherwise     = consPol (n+m) (a*b) (multPorTerm term r)
    where n = grado term
          a = coefLider term
          m = grado pol
          b = coefLider pol
          r = restoPol pol    

-- | (multPol p q) es el producto de los polinomios p y q. Por
-- ejemplo,
-- 
-- > ghci> ejPol1
-- > 3*x^4 + -5*x^2 + 3
-- > ghci> ejPol2
-- > x^5 + 5*x^2 + 4*x
-- > ghci> multPol ejPol1 ejPol2
-- > 3*x^9 + -5*x^7 + 15*x^6 + 15*x^5 + -25*x^4 + -20*x^3 + 15*x^2 + 12*x
multPol :: (Num a, Eq a) => Polinomio a -> Polinomio a -> Polinomio a
multPol p q
    | esPolCero p = polCero
    | otherwise    = sumaPol (multPorTerm (termLider p) q)
                             (multPol (restoPol p) q)

-- | polUnidad es el polinomio unidad. Por ejemplo, 
-- 
-- > ghci> polUnidad
-- > 1
polUnidad:: (Num t, Eq t) => Polinomio t
polUnidad = consPol 0 1 polCero

-- ---------------------------------------------------------------------
-- Valor de un polinomio en un punto                                  --
-- ---------------------------------------------------------------------

-- | (valor p c) es el valor del polinomio p al sustituir su variable por
-- c. Por ejemplo, 
-- 
-- > ejPol1             ==  3*x^4 + -5*x^2 + 3
-- > valor ejPol1 0     ==  3
-- > valor ejPol1 1     ==  1
-- > valor ejPol1 (-2)  ==  31
valor:: (Num a, Eq a) => Polinomio a -> a -> a
valor p c 
    | esPolCero p = 0
    | otherwise   =  b*c^n + valor r c
    where n = grado p
          b = coefLider p
          r = restoPol p

-- ---------------------------------------------------------------------
-- Verificación de raices de polinomios                               --
-- ---------------------------------------------------------------------

-- | (esRaiz c p) se verifica si c es una raiz del polinomio p. Por
-- ejemplo, 
-- 
-- > ejPol3           ==  6*x^4 + 2*x
-- > esRaiz 1 ejPol3  ==  False
-- > esRaiz 0 ejPol3  ==  True
esRaiz:: (Num a, Eq a) => a -> Polinomio a -> Bool
esRaiz c p = valor p c == 0

-- ---------------------------------------------------------------------
-- Derivación de polinomios                                           --
-- ---------------------------------------------------------------------

-- | (derivada p) es la derivada del polinomio p. Por ejemplo, 
-- 
-- > ejPol2           ==  x^5 + 5*x^2 + 4*x
-- > derivada ejPol2  ==  5*x^4 + 10*x + 4
derivada :: (Eq a, Num a) => Polinomio a -> Polinomio a
derivada p 
    | n == 0     = polCero
    | otherwise  = consPol (n-1) (b * fromIntegral n) (derivada r)
    where n = grado p
          b = coefLider p
          r = restoPol p

-- ---------------------------------------------------------------------
-- Resta de polinomios                                                --
-- ---------------------------------------------------------------------

-- | (resta p q) es el polinomio obtenido restándole a p el q. Por
-- ejemplo, 
-- 
-- > ejPol1                  ==  3*x^4 + -5*x^2 + 3
-- > ejPol2                  ==  x^5 + 5*x^2 + 4*x
-- > restaPol ejPol1 ejPol2  ==  -1*x^5 + 3*x^4 + -10*x^2 + -4*x + 3
restaPol :: (Num a, Eq a) => Polinomio a -> Polinomio a -> Polinomio a
restaPol p q  = 
    sumaPol p (multPorTerm (creaTermino 0 (-1)) q)