packages feed

Eq-1.1: EqManips/Algorithm/StackVM/Stack.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
module EqManips.Algorithm.StackVM.Stack( compileExpression
                                       , evalProgram 
                                       , ValueType
                                       ) where

import Control.Applicative
import Data.List( foldl' )

import EqManips.Types
import EqManips.Polynome
import EqManips.Algorithm.Cleanup( cleanupFormulaPrim )

type ValueType = Double

data StackOperand =
      Add | Sub | Mul | Div
    | Pow | Negate | Abs | Sqrt
    | Sin | Sinh | ASin | ASinh
    | Cos | Cosh | ACos | ACosh
    | Tan | Tanh | ATan | ATanh
    | Ln | Log | Exp
    | Ceil | Floor | Frac
    | LoadX
    | LoadY
    | LoadConst ValueType
    deriving Show

type CompiledExpression = [StackOperand]

type MachineWorld = [ValueType]

-- | bla
evalProgram :: CompiledExpression -> ValueType -> ValueType
            -> ValueType
evalProgram program x y = head $ foldl' (evalOperation x y) [] program

-- | Main eval function.
evalOperation :: ValueType -> ValueType -> MachineWorld
              -> StackOperand
              -> MachineWorld
evalOperation _ _ rest (LoadConst v) = v : rest
evalOperation x _ rest LoadX = x : rest
evalOperation _ y rest LoadY = y : rest

evalOperation _ _ (v1:v2:rest) Add = (v2 + v1) : rest
evalOperation _ _ (v1:v2:rest) Sub = (v2 - v1) : rest
evalOperation _ _ (v1:v2:rest) Mul = (v2 * v1) : rest
evalOperation _ _ (v1:v2:rest) Div = (v2 / v1) : rest
evalOperation _ _ (v1:v2:rest) Pow = (v2 ** v1) : rest

evalOperation _ _ (v1:rest) Negate = (-v1) : rest
evalOperation _ _ (v1:rest) Abs = (-v1) : rest
evalOperation _ _ (v1:rest) Sqrt = sqrt v1 : rest
evalOperation _ _ (v1:rest) Sin  = sin  v1 : rest
evalOperation _ _ (v1:rest) Sinh  = sinh  v1 : rest
evalOperation _ _ (v1:rest) ASin  = asin  v1 : rest
evalOperation _ _ (v1:rest) ASinh = asinh v1 : rest
evalOperation _ _ (v1:rest) Cos  = cos  v1 : rest
evalOperation _ _ (v1:rest) Cosh  = cosh  v1 : rest
evalOperation _ _ (v1:rest) ACos  = acos  v1 : rest
evalOperation _ _ (v1:rest) ACosh = acosh v1 : rest
evalOperation _ _ (v1:rest) Tan  = tan  v1 : rest
evalOperation _ _ (v1:rest) Tanh  = tanh  v1 : rest
evalOperation _ _ (v1:rest) ATan  = atan  v1 : rest
evalOperation _ _ (v1:rest) ATanh = atanh v1 : rest
evalOperation _ _ (v1:rest) Ln  = log  v1 : rest
evalOperation _ _ (v1:rest) Log  = (log v1 / log 10) : rest
evalOperation _ _ (v1:rest) Exp = exp v1 : rest

evalOperation _ _ (v1:rest) Ceil = (fromInteger $ ceiling v1) : rest
evalOperation _ _ (v1:rest) Floor = (fromInteger $ floor v1) : rest
evalOperation _ _ (v1:rest) Frac = v' : rest
    where (_, v') = properFraction v1 :: (Int,Double)

evalOperation _ _ [] _ = error "Stack VM : empty stack."
evalOperation _ _ _ _ = error "Stack VM : stack underflow"


stackOpOfBinop :: BinOperator -> Maybe StackOperand
stackOpOfBinop OpAdd = Just Add  
stackOpOfBinop OpSub = Just Sub 
stackOpOfBinop OpMul = Just Mul 
stackOpOfBinop OpDiv = Just Div 
stackOpOfBinop OpPow = Just Pow 
stackOpOfBinop _ = Nothing

stackOpOfUnop :: UnOperator -> StackOperand
stackOpOfUnop OpNegate = Negate 
stackOpOfUnop OpAbs = Abs 
stackOpOfUnop OpSqrt = Sqrt
stackOpOfUnop OpSin = Sin 
stackOpOfUnop OpSinh = Sinh 
stackOpOfUnop OpASin = ASin 
stackOpOfUnop OpASinh = ASinh
stackOpOfUnop OpCos = Cos 
stackOpOfUnop OpCosh = Cosh 
stackOpOfUnop OpACos = ACos 
stackOpOfUnop OpACosh = ACosh
stackOpOfUnop OpTan = Tan 
stackOpOfUnop OpTanh = Tanh 
stackOpOfUnop OpATan = ATan 
stackOpOfUnop OpATanh = ATanh
stackOpOfUnop OpLn = Ln 
stackOpOfUnop OpLog = Log 
stackOpOfUnop OpExp = Exp
stackOpOfUnop OpFactorial =
    error "Cannot be compiled"
stackOpOfUnop OpCeil = Ceil 
stackOpOfUnop OpFloor = Floor 
stackOpOfUnop OpFrac = Frac

-- | Convert a polynome into a formula to provide the minimal
-- formula in term of multiplication need.
convertPolynomeToEvalFormula :: Polynome -> Maybe FormulaPrim
convertPolynomeToEvalFormula (PolyRest c) = Just $ coefToFormula c
convertPolynomeToEvalFormula (Polynome [var] polyCoeffs) 
    | var == 'x' || var == 'y' = do
      firstTransfo <- convertPolynomeToEvalFormula firstSub
      (lastCoeff, lastFormu) <-
                 foldl' prefCoeff (Just (firstCoeff, firstTransfo)) restCoeff
      pure . cleanupFormulaPrim $ lastFormu * fvar ** coefToFormula lastCoeff
        where ((firstCoeff,firstSub):restCoeff) = reverse polyCoeffs
              fvar = Variable [var]

              multCoeff :: FormulaPrim -> PolyCoeff -> PolyCoeff -> FormulaPrim
                        -> (PolyCoeff, FormulaPrim)
              multCoeff rez _             0 subFormu = (0        , rez + subFormu)
              multCoeff rez 0         coeff subFormu = (coeff - 1, rez * fcoeff * fvar * subFormu)
                where fcoeff = coefToFormula coeff
              multCoeff rez prevCoeff coeff subFormu =
                  (coeff, (rez * fvar ** thisCoeff + 1) * subFormu)
                where thisCoeff = coefToFormula $ prevCoeff - coeff

              prefCoeff :: Maybe (PolyCoeff, FormulaPrim) -> (PolyCoeff, Polynome)
                        -> Maybe (PolyCoeff, FormulaPrim)
              prefCoeff Nothing                            _ = Nothing
              prefCoeff (Just (prevCoeff, rez)) (coeff, sub) = do
                  multCoeff rez prevCoeff coeff <$> convertPolynomeToEvalFormula sub
              

convertPolynomeToEvalFormula (Polynome _ _) = Nothing

compileExpression :: FormulaPrim -> Either String CompiledExpression
compileExpression (Poly _ p) =
    maybe (Left "Wrong variable name in expression") compileExpression
          $ convertPolynomeToEvalFormula p

compileExpression (Variable "x") = Right [LoadX]
compileExpression (Variable "y") = Right [LoadY]
compileExpression (NumEntity Pi) = Right [LoadConst pi]
compileExpression (NumEntity _) = 
    Left "Can't compile numeric entity"
compileExpression (Variable v) =
    Left $ "Can't compile expression with unbound variable ("
                ++ v ++ ")"
compileExpression (CInteger i) = Right [LoadConst $ fromInteger i]
compileExpression (CFloat f) = Right [LoadConst f]
compileExpression (Fraction f) = Right [LoadConst $ fromRational f]
compileExpression (UnOp _ OpFactorial _) =
    Left "Cannot compile factorial expression"
compileExpression (UnOp _ op sub) =
    (++ [stackOpOfUnop op]) <$> compileExpression sub

compileExpression (BinOp _ op formulas) =
  case stackOpOfBinop op of
    Just stackOp -> case mapM compileExpression formulas of
        Left err -> Left err
        Right [] -> Left "Stack VM : Empty binop"
        Right [x] -> Right x
        Right (x:xs) ->
            Right $ x ++ foldr (\lst acc -> lst ++ (stackOp : acc)) [] xs
    Nothing -> Left "Error non continuous operators used"
compileExpression (App _ _ _) =
    Left "No function call allowed in compiled expression."
compileExpression (Sum _ _ _ _) =
    Left "No sum allowed."
compileExpression (Product _ _ _ _) =
    Left "No product allowed."
compileExpression (Indexes _ _ _) =
    Left "No indexes allowed in compiled exprression."
compileExpression (List _ _) =
    Left "No lists allowed in compiled exprression."
compileExpression (Complex _ _) =
    Left "No complex arithmetic allowed in compiled expression."
compileExpression (Lambda _ _) = 
    Left "No lambda allowed in compiled expression."
compileExpression (Matrix _ _ _ _) = 
    Left "No matrix allowed in compiled expression."
compileExpression (Truth _) = 
    Left "No boolean expression allowed for compilation."
compileExpression (Derivate _ _ _) = 
    Left "No derivation allowed in compilation."
compileExpression (Integrate _ _ _ _ _) = 
    Left "No integration allowed in compilation."
compileExpression (Block _ _ _) = 
    Left "There is some errors in expressions."
compileExpression (Meta _ _ _) =
    Left "No meta operations allowed in compilation."