packages feed

Eq-1.0: EqManips/Linker.hs

-- | This module will link variable names to
-- symbols.
module EqManips.Linker( DocString, LongDescr
                      , entityList
                      , metaFunctionList 
                      , unaryFunctions 
                      , multiParamsFunctions
                      , linkFormula
                      ) where

import Data.List
import Data.Maybe( fromMaybe )
import qualified Data.Map as Map

import EqManips.Types

-- | Linking formula doesn't change it's form,
-- so we can keep it
linkFormula :: Formula anyForm -> Formula anyForm
linkFormula (Formula a) = Formula $ link a

type DocString = String
type LongDescr = String

entityList :: [(String, (DocString, LongDescr, FormulaPrim))]
entityList =
    [ ("infinite", ("Represent the inifinity in this program."
                   , ""
                   , NumEntity Infinite))
    , ("pi", ( "The number Pi (=3.14159...)."
             , "When used, exact simplification can be used"
             , NumEntity Pi))
    , ("i", ( "The imaginary number, use it to describe complex numbers."
            , "i * i = -1"
            , complex (CInteger 0, CInteger 1)))
    ]

metaFunctionList :: [(String, (DocString, LongDescr, FormulaPrim -> FormulaPrim))]
metaFunctionList =
    [ ("Hold", ( "Avoid evaluating the expression passed as parameter."
               , ""
               , meta Hold))
    , ("Force", ( "Force the evaluation of sub-expression even if the enclosing"
                , ""
                , meta Force))
    , ("Expand", ( ""
                 , ""
                 , meta Expand))
    , ("Cleanup", ( "Make trivial simplification to the formula"
                  , "Simplify things like '1 * x' to 'x'."
                  , meta Cleanup))
    , ("Sort", ( ""
               , ""
               , meta Sort))
    ]

unaryFunctions :: [(String, (DocString, LongDescr, FormulaPrim -> FormulaPrim))]
unaryFunctions =
    [ ("ceil", ( ""
               , ""
               , unOp OpCeil))
    , ("floor", ( ""
                , ""
                , unOp OpFloor))
    , ("frac", ( ""
               , ""
               , unOp OpFrac))
    , ("sin", ( ""
              , ""
              , unOp OpSin))
    , ("sinh", ( ""
               , ""
               , unOp OpSinh))
    , ("asin", ( ""
               , ""
               , unOp OpASin))
    , ("asinh", ( ""
                , ""
                , unOp OpASinh))
    , ("cos", ( ""
              , ""
              , unOp OpCos))
    , ("cosh", ( ""
               , ""
               , unOp OpCosh))
    , ("acos", ( ""
               , ""
               , unOp OpACos))
    , ("acosh", ( ""
                , ""
                , unOp OpACosh))
    , ("tan", ( ""
              , ""
              , unOp OpTan))
    , ("tanh", ( ""
               , ""
               , unOp OpTanh))
    , ("atan", ( ""
               , ""
               , unOp OpATan))
    , ("atanh", ( ""
                , ""
                , unOp OpATanh))
    , ("abs", ( ""
              , ""
              , unOp OpAbs))
    , ("sqrt", ( ""
               , ""
               , unOp OpSqrt))
    , ("exp", ( ""
              , ""
              , unOp OpExp))
    , ("log", ( ""
              , ""
              , unOp OpLog))
    , ("ln", ( ""
             , ""
             , unOp OpLn))
    ]

unaryTranslations :: Map.Map String (FormulaPrim -> FormulaPrim)
unaryTranslations = Map.fromList
    [ (name, fun) | (name, (_,_,fun)) <- unaryFunctions ++ metaFunctionList ]

entityTranslation :: Map.Map String FormulaPrim
entityTranslation = Map.fromList [(name, val) | (name, (_,_,val)) <- entityList]

multiParametersFunction :: Map.Map String ([FormulaPrim] -> FormulaPrim)
multiParametersFunction = Map.fromList [(name, f) | (name, (_,_,_,f)) <- multiParamsFunctions]

multiParamsFunctions :: [ ( String
                          , (DocString, LongDescr, [(DocString,LongDescr)], [FormulaPrim] -> FormulaPrim))]
multiParamsFunctions =
    [ ("Lambda", ( "Create an anonymous function"
                 , "An anonymous function is a function with no name which can be passed as parameter."
                 , [ ("Argument", "Variable to be bound when the lambda is called")
                   , ("Body", "Expression to be evaluated after argument binding.\n"
                            ++"The body is not evaluated during it's definition.")
                   ]
                 , lambdaBuilder )  )
    , ("derivate", ( "Make a partial differentiation"
                   , "Differentiate an expression for a variable given in parameter."
                   , [ ("Expression", "Expression to be differentiated, no evaluation occur at binding, unless it is in Force()")
                     , ("Variable", "Variable on which to perform partial differentiation. No evaluation done unless in Force()")
                     ]
                   , derivateBuilder
                   ))

    , ("sum", ( "Perform a sum of an expression"
              , "The sum bind a variable over a range and perform a sum. If the arguments below are not given, no calculation is performed."
              , [ ("Initial value", "An expression in the form x = something, to declare the start of iteration.")
                , ("End value", "An upper bound for iteration, must be a number for calculation to happen")
                , ("Expression", "Expression to be summed, can contain the variable bound by initial value.")
                ]
              , sumBuilder))
    , ("product", ( "Perform a product of an expression"
                , "The sum bind a variable over a range and perform a sum. If the arguments below are not given, no calculation is performed."
                , [ ("Initial value", "An expression in the form x = something, to declare the start of iteration.")
                  , ("End value", "An upper bound for iteration, must be a number for calculation to happen")
                  , ("Expression", "Expression to be summed, can contain the variable bound by initial value.")
                  ]
                , productBuilder ))
    , ("integrate", ( "Describe an integral"
                    , "For the moment, no calculation is performed. Just used for the format command"
                    , [ ("Initial Value", "Lower bound of the integral.")
                      , ("End Value", "Upper bound of the integral.")
                      , ("Expression", "The expression to be integrated.")
                      , ("Variable", "The dx of the integral, where x is any variable.")
                      ]
                    , integrateBuilder))
    , ("matrix", ( "Create a matrix"
                 , ""
                 , [("width", "Number of columns")
                   ,("height", "Number of lines of the matrix")
                   ,("...", "All the values")
                   ]
                 , matrixBuilder ))
    ]

lambdaBuilder :: [FormulaPrim] -> FormulaPrim
lambdaBuilder [arg, body] = meta LambdaBuild $ lambda [([arg], body)]
lambdaBuilder lst = app (Variable "Lambda") lst

derivateBuilder :: [FormulaPrim] -> FormulaPrim
derivateBuilder [what, var] = derivate what var
derivateBuilder lst = app (Variable "Derivate") lst


sumBuilder :: [FormulaPrim] -> FormulaPrim
sumBuilder [ini, end, what] = summ ini end what
sumBuilder [ini, what] = summ ini (Variable "") what
sumBuilder [what] = summ (Variable "") (Variable "") what
sumBuilder lst = app (Variable "sum") lst

productBuilder :: [FormulaPrim] -> FormulaPrim
productBuilder [ini, end, what] = productt ini end what
productBuilder [ini, what] = productt ini (Variable "") what
productBuilder [what] = productt (Variable "") (Variable "") what
productBuilder lst = app (Variable "product") lst

integrateBuilder :: [FormulaPrim] -> FormulaPrim
integrateBuilder [ini, end, what, dvar] = integrate ini end what dvar
integrateBuilder [ini, what, dvar] = integrate ini (Variable "") what dvar
integrateBuilder [what, dvar] = integrate (Variable "") (Variable "") what dvar
integrateBuilder lst = app (Variable "integrate") lst

matrixBuilder :: [FormulaPrim] -> FormulaPrim
matrixBuilder (CInteger n: CInteger m: exps)
    | fromEnum n * fromEnum m > length exps = error "The matrix has not enough expressions"
    | fromEnum n * fromEnum m < length exps = error "The matrix has too much expressions"
    | otherwise = matrix (fromEnum n) (fromEnum m) $ splitMatrix exps
        where splitMatrix  [] = []
              splitMatrix lst =
                let (matrixLine, matrixRest) = genericSplitAt n lst
                in matrixLine : splitMatrix matrixRest
matrixBuilder lst = app (Variable "matrix") lst

multivarLinker :: String -> [FormulaPrim] -> FormulaPrim
multivarLinker v flst =
    maybe (app (Variable v) $ linked) (\f -> f $ linked) 
    $ Map.lookup v multiParametersFunction
        where linked = map link flst

-- | Function associating variables to symbol.
link :: FormulaPrim -> FormulaPrim
link (App _ (Variable "block") [CInteger i1, CInteger i2, CInteger i3]) = 
    Block (fromEnum i1) (fromEnum i2) (fromEnum i3)

-- Transformations for operators
link p@(Poly _ _) = p
link v@(Variable varName) =
    fromMaybe v $ Map.lookup varName entityTranslation
link (App _ (Variable funName) [x]) = 
      maybe (multivarLinker funName [x]) (\f -> f $ linked)
    $ Map.lookup funName unaryTranslations
        where linked = link x

link (App _ (Variable v) flst) = multivarLinker v flst

-- General transformations
link (App _ f flst) = app (link f) $ map link flst
link (UnOp _ op f) = unOp op $ link f
link (BinOp _ op fs) = binOp op $ map link fs
link (Meta _ m fs) = meta m $ link fs
link a@(CFloat _) = a
link a@(CInteger _) = a
link a@(NumEntity _) = a
link a@(Block _ _ _) = a
link t@(Truth _) = t
link f@(Fraction _) = f
link (Complex _ (r,i)) = complex (link r, link i)
link (Lambda _ l) = lambda [ (map link fl, link f) | (fl, f) <- l]
link (Matrix _ n m ll) = matrix n m  [map link rows | rows <- ll]
link (Derivate _ a b) = derivate (link a) (link b)
link (Sum _ a b c) = summ (link a) (link b) (link c)
link (Product _ a b c) = productt (link a) (link b) (link c)
link (Integrate _ a b c d) = integrate (link a) (link b) (link c) (link d)
link (Indexes _ main lst) = indexes (link main) $ map link lst
link (List _ lst) = list $ map link lst