packages feed

Eq-1.0: EqManips/Algorithm/Eval/Polynomial.hs

module EqManips.Algorithm.Eval.Polynomial( polyEvalRules ) where

import Data.Either( partitionEithers )

import qualified EqManips.ErrorMessages as Err
import EqManips.Types
import EqManips.Polynome
import EqManips.EvaluationContext
import EqManips.Algorithm.Cleanup
import EqManips.Algorithm.Utils
import EqManips.Algorithm.Eval.Utils
import EqManips.Algorithm.Eval.Types

leftclean :: FormulaPrim -> EqContext (Either FormulaPrim a)
leftclean = left . unTagFormula . cleanup . Formula 

-- The two following rules can generate 0 in the polynomial
-- we have to clean them
-----------------------------------------------
----            '+'
-----------------------------------------------
add :: EvalOp
add (Poly _ p1) (Poly _ p2) = leftclean . poly $ p1 + p2
add v1 (Poly _ p) | isFormulaScalar v1 = leftclean . poly $ (PolyRest $ scalarToCoeff v1) + p
add (Poly _ p) v2 | isFormulaScalar v2 = leftclean . poly $ p + (PolyRest $ scalarToCoeff v2)
add (Variable v) (Poly _ p) = leftclean . poly $ Polynome v [(CoeffInt 1, PolyRest $ CoeffInt 1)] + p
add (Poly _ p) (Variable v) = left . poly $ p + Polynome v [(CoeffInt 1, PolyRest $ CoeffInt 1)]

add (BinOp _ OpPow [Variable v, degree]) (Poly _ p) 
    | isFormulaScalar degree = leftclean . poly $ Polynome v [(scalarToCoeff degree, PolyRest $ CoeffInt 1)] + p
add (Poly _ p) (BinOp _ OpPow [Variable v, degree]) 
    | isFormulaScalar degree = leftclean . poly $ p + Polynome v [(scalarToCoeff degree, PolyRest $ CoeffInt 1)]
add e e' = right (e, e')

-----------------------------------------------
----            '-'
-----------------------------------------------
sub :: EvalOp
#ifdef _DEBUG
sub leftArg@(Poly _ p1) rightArg@(Poly _ p2) = 
  addTrace ( "Polynome/Polynome '-'"
           , treeIfyFormula . Formula 
                            $ leftArg - rightArg) >>
#else
sub (Poly _ p1) (Poly _ p2) = 
#endif
    leftclean (poly $ p1 - p2)

sub v1 (Poly _ p) | isFormulaScalar v1 = leftclean . poly $ (PolyRest $ scalarToCoeff v1) - p
sub (Poly _ p) v2 | isFormulaScalar v2 = leftclean . poly $ p - (PolyRest $ scalarToCoeff v2)
sub (Variable v) (Poly _ p) = leftclean . poly $ Polynome v [(CoeffInt 1, PolyRest $ CoeffInt 1)] - p
sub (Poly _ p) (Variable v) = leftclean . poly $ p - Polynome v [(CoeffInt 1, PolyRest $ CoeffInt 1)]
sub (BinOp _ OpPow [Variable v, degree]) (Poly _ p) 
    | isFormulaScalar degree = leftclean . poly $ Polynome v [(scalarToCoeff degree, PolyRest $ CoeffInt 1)] - p
sub (Poly _ p) (BinOp _ OpPow [Variable v, degree]) 
    | isFormulaScalar degree = leftclean . poly $ p - Polynome v [(scalarToCoeff degree, PolyRest $ CoeffInt 1)]
sub e e' = right (e,e')

-----------------------------------------------
----            '*'
-----------------------------------------------
mul :: EvalOp
mul (Poly _ p1) (Poly _ p2) = left . poly $ p1 * p2
mul v1 (Poly _ p) | isFormulaScalar v1 = left . poly $ polyCoeffMap (scalarToCoeff v1 *) p
mul (Poly _ p) v2 | isFormulaScalar v2 = left . poly $ polyCoeffMap (* scalarToCoeff v2) p
mul (Variable v) (Poly _ p) = left . poly $ Polynome v [(CoeffInt 1, PolyRest $ CoeffInt 1)] * p
mul (Poly _ p) (Variable v) = left . poly $ p * Polynome v [(CoeffInt 1, PolyRest $ CoeffInt 1)]
mul (BinOp _ OpPow [Variable v, degree]) (Poly _ p) 
    | isFormulaScalar degree = left . poly $ Polynome v [(scalarToCoeff degree, PolyRest $ CoeffInt 1)] * p
mul (Poly _ p) (BinOp _ OpPow [Variable v, degree]) 
    | isFormulaScalar degree = left . poly $ p * Polynome v [(scalarToCoeff degree, PolyRest $ CoeffInt 1)]
mul e e' = right (e, e')

-----------------------------------------------
----        '/'
-----------------------------------------------
-- | Handle the division operator. Nicely handle the case
-- of division by 0.
division :: EvalOp
division v1 (Poly _ p) | isFormulaScalar v1 = left . poly $ polyCoeffMap (scalarToCoeff v1 /) p
division (Poly _ p) v2 | isFormulaScalar v2 = left . poly $ polyCoeffMap (/ scalarToCoeff v2) p
division p1@(Poly _ p) p2f@(Poly _ p2) = 
    let unconstruct = unTagFormula  . cleanupRules . Formula . polyAsFormula
    in case syntheticDiv p p2 of
        (Nothing, Nothing) -> right (p1, p2f)
        (Nothing, Just _) -> right (p1, p2f)
        (Just quotient, Nothing) -> left $ unconstruct quotient
        (Just quotient, Just rest) -> left $ unconstruct quotient
                                           + ( unconstruct rest 
                                             / unconstruct p2)
division f1 f2 = right (f1, f2)

-- | If a polynome's variable is bound, replace it by the real
-- the value.
substitutePolynome :: EvalFun -> Polynome -> Formula ListForm -> EqContext FormulaPrim
substitutePolynome _ (PolyRest _) _ = error Err.polynome_no_coeff_substitution 
substitutePolynome evaluator (Polynome _var coefs) (Formula subst) =
    evaluator $ binOp OpAdd added
        where added = [formulize subPoly * (subst ** coefToFormula degree) | (degree, subPoly) <- coefs]
              formulize (PolyRest coeff) = coefToFormula coeff
              formulize normalPolynome = poly normalPolynome

checkPolynomeBinding :: EvalFun -> Polynome -> EqContext (Either Polynome FormulaPrim)
checkPolynomeBinding _           p@(PolyRest _) = return $ Left p
checkPolynomeBinding evaluator pol@(Polynome var coefList) = do
    varBound <- symbolLookup var
    case varBound of
         Just bound ->
             substitutePolynome evaluator pol bound >>= (return . Right)
         Nothing -> do
            subs <- mapM (\(coeff,p) -> do
                subPoly <- checkPolynomeBinding evaluator p
                case subPoly of
                     Left filteredPoly -> return . Left $ (coeff, filteredPoly)
                     Right formu -> return . Right $
                         formu * poly (Polynome var [( coeff
                                                     , PolyRest $ CoeffInt 1)])
                ) coefList
            case  partitionEithers subs of
                ([], []) -> error "Impossible case"
                ([], formulas) ->
                    return . Right $ binOp OpAdd formulas
                (polys, []) ->
                    return . Left $ Polynome var polys
                (polys, formulas) ->
                    return . Right .  binOp OpAdd
                        $ poly (Polynome var polys) : formulas
                        

-----------------------------------------------
----        General evaluation
-----------------------------------------------
-- | General evaluation/reduction function
polyEvalRules :: EvalFun -> EvalFun
polyEvalRules _ (BinOp _ OpAdd fs) = binEval OpAdd add add fs
polyEvalRules _ (BinOp _ OpSub fs) = binEval OpSub sub add fs
polyEvalRules _ (BinOp _ OpMul fs) = binEval OpMul mul mul fs
polyEvalRules _ (BinOp _ OpDiv fs) = binEval OpDiv division mul fs
polyEvalRules evaluator (Poly _ pol@(Polynome _ _)) = do
    checkPolynomeBinding evaluator pol 
    >>= either (return . poly) return
polyEvalRules _ end = return end