-- | Polynomial expressions (used for parsing)

{-# LANGUAGE BangPatterns, DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
module Math.Singular.Factory.Expr where

--------------------------------------------------------------------------------

import Data.Char
import Data.List

import Control.Applicative
import Control.Monad

import Data.Text.Lazy ( Text )
import qualified Data.Text.Lazy      as T
import qualified Data.Text.Lazy.Read as T

import Math.Singular.Factory.Internal.DList as DList

--------------------------------------------------------------------------------
-- * Types

data Sign
  = Plus
  | Minus
  deriving (Eq,Ord,Show)

negateIfMinus :: Num a => Sign -> a -> a
negateIfMinus Plus  = id
negateIfMinus Minus = negate

--------------------------------------------------------------------------------

-- | Monomials
newtype Monom var
  = Monom [(var,Int)]
  deriving (Eq,Ord,Show,Functor,Foldable,Traversable)

-- | A monomial multiplied by a constant
data Term coeff var
  = Term !coeff !(Monom var)
  deriving (Eq,Ord,Show,Functor,Foldable,Traversable)

-- | Polynomials as linear combination of monomials
newtype GenPoly coeff var
  = GenPoly [Term coeff var]
  deriving (Eq,Ord,Show,Functor,Foldable,Traversable)

-- | Polynomial expressions
data Expr v
  = VarE !v
  | KstE !Integer
  | NegE (Expr v)
  | LinE [(Sign,Expr v)]
  | MulE [Expr v]
  | PowE (Expr v) !Int
  deriving (Eq,Ord,Show,Functor,Foldable,Traversable)

--------------------------------------------------------------------------------
-- * Evaluation to @Num@

evalSign :: Num c => Sign -> c -> c
evalSign Plus  = id
evalSign Minus = negate

evalMonom :: Num c => (var -> c) -> Monom var -> c
evalMonom f (Monom list) = product (map g list) where
  g (var,expo) = (f var)^expo

evalTerm :: Num c => (coeff -> c) -> (var -> c) -> Term coeff var -> c
evalTerm f g (Term coeff monom) = f coeff * evalMonom g monom

evalGenPoly :: Num c => (coeff -> c) -> (var -> c) -> GenPoly coeff var -> c
evalGenPoly f g (GenPoly terms) = sum (map (evalTerm f g) terms)

evalExpr :: Num c => (var -> c) -> Expr var -> c
evalExpr evalVar = go where
  go expr = case expr of
    VarE v   -> evalVar v
    KstE k   -> fromInteger k
    NegE e   -> negate (go e)
    LinE xs  -> sum [ evalSign pm (go x) | (pm,x) <- xs ]
    MulE xs  -> product (map go xs)
    PowE e k -> (go e)^k

--------------------------------------------------------------------------------

{-

prettExpr :: Expr -> String
prettExpr expr = DList.toList (prettPrecExpr 0 expr)

prettyPrecExpr :: Int -> Expr -> DList Char
prettyPrecExpr = go where

  chr c = DList.singleton c
  str s = DList.append s

  go !prec !expr = case expr of
    VarE !Var
    KstE !Integer
    NegE Expr
    LinE [(Sign,Expr)]
    MulE [Expr]
    PowE Expr !Int

-}

--------------------------------------------------------------------------------