{-# LANGUAGE TemplateHaskell #-}
module Language.Drasil.Chunk.CodeDefinition (
  CodeDefinition, DefinitionType(..), qtoc, qtov, odeDef, auxExprs, defType,
) where

import Language.Drasil
import Language.Drasil.Chunk.Code (quantvar, quantfunc)
import Language.Drasil.CodeExpr.Development (expr, CanGenCode(..))
import Language.Drasil.Data.ODEInfo (ODEInfo(..), ODEOptions(..))

import Control.Lens ((^.), makeLenses, view)

-- | The definition may be specialized to use ODEs.
data DefinitionType = Definition | ODE

-- | A chunk for pairing a mathematical definition with a 'CodeChunk'.
data CodeDefinition = CD { CodeDefinition -> CodeChunk
_cchunk   :: CodeChunk
                         , CodeDefinition -> CodeExpr
_def      :: CodeExpr
                         , CodeDefinition -> [CodeExpr]
_auxExprs :: [CodeExpr]
                         , CodeDefinition -> DefinitionType
_defType  :: DefinitionType
                         }
makeLenses ''CodeDefinition

-- | Finds the 'UID' of the 'CodeChunk' used to make the 'CodeDefinition'.
instance HasUID           CodeDefinition where uid :: Getter CodeDefinition UID
uid = (CodeChunk -> f CodeChunk) -> CodeDefinition -> f CodeDefinition
Lens' CodeDefinition CodeChunk
cchunk ((CodeChunk -> f CodeChunk) -> CodeDefinition -> f CodeDefinition)
-> ((UID -> f UID) -> CodeChunk -> f CodeChunk)
-> (UID -> f UID)
-> CodeDefinition
-> f CodeDefinition
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UID -> f UID) -> CodeChunk -> f CodeChunk
forall c. HasUID c => Getter c UID
Getter CodeChunk UID
uid
-- | Finds the term ('NP') of the 'CodeChunk' used to make the 'CodeDefinition'.
instance NamedIdea        CodeDefinition where term :: Lens' CodeDefinition NP
term = (CodeChunk -> f CodeChunk) -> CodeDefinition -> f CodeDefinition
Lens' CodeDefinition CodeChunk
cchunk ((CodeChunk -> f CodeChunk) -> CodeDefinition -> f CodeDefinition)
-> ((NP -> f NP) -> CodeChunk -> f CodeChunk)
-> (NP -> f NP)
-> CodeDefinition
-> f CodeDefinition
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NP -> f NP) -> CodeChunk -> f CodeChunk
forall c. NamedIdea c => Lens' c NP
Lens' CodeChunk NP
term
-- | Finds the idea contained in the 'CodeChunk' used to make the 'CodeDefinition'.
instance Idea             CodeDefinition where getA :: CodeDefinition -> Maybe String
getA = CodeChunk -> Maybe String
forall c. Idea c => c -> Maybe String
getA (CodeChunk -> Maybe String)
-> (CodeDefinition -> CodeChunk) -> CodeDefinition -> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting CodeChunk CodeDefinition CodeChunk
-> CodeDefinition -> CodeChunk
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting CodeChunk CodeDefinition CodeChunk
Lens' CodeDefinition CodeChunk
cchunk
-- | Finds the 'Space' of the 'CodeChunk' used to make the 'CodeDefinition'.
instance HasSpace         CodeDefinition where typ :: Getter CodeDefinition Space
typ = (CodeChunk -> f CodeChunk) -> CodeDefinition -> f CodeDefinition
Lens' CodeDefinition CodeChunk
cchunk ((CodeChunk -> f CodeChunk) -> CodeDefinition -> f CodeDefinition)
-> ((Space -> f Space) -> CodeChunk -> f CodeChunk)
-> (Space -> f Space)
-> CodeDefinition
-> f CodeDefinition
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Space -> f Space) -> CodeChunk -> f CodeChunk
forall c. HasSpace c => Getter c Space
Getter CodeChunk Space
typ
-- | Finds the 'Stage' dependent 'Symbol' of the 'CodeChunk' used to make the 'CodeDefinition'.
instance HasSymbol        CodeDefinition where symbol :: CodeDefinition -> Stage -> Symbol
symbol CodeDefinition
c = CodeChunk -> Stage -> Symbol
forall c. HasSymbol c => c -> Stage -> Symbol
symbol (CodeDefinition
c CodeDefinition
-> Getting CodeChunk CodeDefinition CodeChunk -> CodeChunk
forall s a. s -> Getting a s a -> a
^. Getting CodeChunk CodeDefinition CodeChunk
Lens' CodeDefinition CodeChunk
cchunk)
-- | 'CodeDefinition's have a 'Quantity'.
instance Quantity         CodeDefinition
-- | Finds the code name of a 'CodeDefinition'.
-- 'Function' 'CodeDefinition's are named with the function prefix to distinguish
-- them from the corresponding variable version.
instance CodeIdea         CodeDefinition where
  codeName :: CodeDefinition -> String
codeName (CD c :: CodeChunk
c@(CodeC QuantityDict
_ VarOrFunc
Var) CodeExpr
_ [CodeExpr]
_ DefinitionType
_) = CodeChunk -> String
forall c. CodeIdea c => c -> String
codeName CodeChunk
c
  codeName (CD c :: CodeChunk
c@(CodeC QuantityDict
_ VarOrFunc
Func) CodeExpr
_ [CodeExpr]
_ DefinitionType
_) = String
funcPrefix String -> String -> String
forall a. [a] -> [a] -> [a]
++ CodeChunk -> String
forall c. CodeIdea c => c -> String
codeName CodeChunk
c
  codeChunk :: CodeDefinition -> CodeChunk
codeChunk = Getting CodeChunk CodeDefinition CodeChunk
-> CodeDefinition -> CodeChunk
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting CodeChunk CodeDefinition CodeChunk
Lens' CodeDefinition CodeChunk
cchunk
-- | Equal if 'UID's are equal.
instance Eq               CodeDefinition where CodeDefinition
c1 == :: CodeDefinition -> CodeDefinition -> Bool
== CodeDefinition
c2 = (CodeDefinition
c1 CodeDefinition -> Getting UID CodeDefinition UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID CodeDefinition UID
forall c. HasUID c => Getter c UID
Getter CodeDefinition UID
uid) UID -> UID -> Bool
forall a. Eq a => a -> a -> Bool
== (CodeDefinition
c2 CodeDefinition -> Getting UID CodeDefinition UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID CodeDefinition UID
forall c. HasUID c => Getter c UID
Getter CodeDefinition UID
uid)
-- | Finds the units of the 'CodeChunk' used to make the 'CodeDefinition'.
instance MayHaveUnit      CodeDefinition where getUnit :: CodeDefinition -> Maybe UnitDefn
getUnit = CodeChunk -> Maybe UnitDefn
forall u. MayHaveUnit u => u -> Maybe UnitDefn
getUnit (CodeChunk -> Maybe UnitDefn)
-> (CodeDefinition -> CodeChunk)
-> CodeDefinition
-> Maybe UnitDefn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting CodeChunk CodeDefinition CodeChunk
-> CodeDefinition -> CodeChunk
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting CodeChunk CodeDefinition CodeChunk
Lens' CodeDefinition CodeChunk
cchunk
-- | Finds the defining expression of a CodeDefinition.
instance DefiningCodeExpr CodeDefinition where codeExpr :: Lens' CodeDefinition CodeExpr
codeExpr = (CodeExpr -> f CodeExpr) -> CodeDefinition -> f CodeDefinition
Lens' CodeDefinition CodeExpr
def

-- NOTE: We'll eventually want extra smart constructors that allow for custom
--       CodeExprs inputs.

-- TODO: These below 3 functions that generate ``CodeDefinitions'' should be generalized
--       It _might_ be good to create make a ``CanGenCodeDefinition''-like typeclass

-- | Constructs a 'CodeDefinition' where the underlying 'CodeChunk' is for a function.
qtoc :: (Quantity (q Expr), MayHaveUnit (q Expr), DefiningExpr q) => q Expr -> CodeDefinition
qtoc :: forall (q :: * -> *).
(Quantity (q Expr), MayHaveUnit (q Expr), DefiningExpr q) =>
q Expr -> CodeDefinition
qtoc q Expr
q = CodeChunk
-> CodeExpr -> [CodeExpr] -> DefinitionType -> CodeDefinition
CD (CodeFuncChunk -> CodeChunk
forall c. CodeIdea c => c -> CodeChunk
codeChunk (CodeFuncChunk -> CodeChunk) -> CodeFuncChunk -> CodeChunk
forall a b. (a -> b) -> a -> b
$ q Expr -> CodeFuncChunk
forall c. (Quantity c, MayHaveUnit c) => c -> CodeFuncChunk
quantfunc q Expr
q) (Expr -> CodeExpr
expr (Expr -> CodeExpr) -> Expr -> CodeExpr
forall a b. (a -> b) -> a -> b
$ q Expr
q q Expr -> Getting Expr (q Expr) Expr -> Expr
forall s a. s -> Getting a s a -> a
^. Getting Expr (q Expr) Expr
forall e. Lens' (q e) e
forall (c :: * -> *) e. DefiningExpr c => Lens' (c e) e
defnExpr) [] DefinitionType
Definition

-- | Constructs a 'CodeDefinition' where the underlying 'CodeChunk' is for a variable.
qtov :: CanGenCode e => QDefinition e -> CodeDefinition
qtov :: forall e. CanGenCode e => QDefinition e -> CodeDefinition
qtov QDefinition e
q = CodeChunk
-> CodeExpr -> [CodeExpr] -> DefinitionType -> CodeDefinition
CD (CodeVarChunk -> CodeChunk
forall c. CodeIdea c => c -> CodeChunk
codeChunk (CodeVarChunk -> CodeChunk) -> CodeVarChunk -> CodeChunk
forall a b. (a -> b) -> a -> b
$ QDefinition e -> CodeVarChunk
forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar QDefinition e
q) (e -> CodeExpr
forall e. CanGenCode e => e -> CodeExpr
toCodeExpr (e -> CodeExpr) -> e -> CodeExpr
forall a b. (a -> b) -> a -> b
$ QDefinition e
q QDefinition e -> Getting e (QDefinition e) e -> e
forall s a. s -> Getting a s a -> a
^. Getting e (QDefinition e) e
forall e. Lens' (QDefinition e) e
forall (c :: * -> *) e. DefiningExpr c => Lens' (c e) e
defnExpr) [] DefinitionType
Definition

-- | Constructs a 'CodeDefinition' for an ODE.
odeDef :: ODEInfo -> CodeDefinition
odeDef :: ODEInfo -> CodeDefinition
odeDef ODEInfo
info = CodeChunk
-> CodeExpr -> [CodeExpr] -> DefinitionType -> CodeDefinition
CD
  (CodeFuncChunk -> CodeChunk
forall c. CodeIdea c => c -> CodeChunk
codeChunk (CodeFuncChunk -> CodeChunk) -> CodeFuncChunk -> CodeChunk
forall a b. (a -> b) -> a -> b
$ CodeVarChunk -> CodeFuncChunk
forall c. (Quantity c, MayHaveUnit c) => c -> CodeFuncChunk
quantfunc (CodeVarChunk -> CodeFuncChunk) -> CodeVarChunk -> CodeFuncChunk
forall a b. (a -> b) -> a -> b
$ ODEInfo -> CodeVarChunk
depVar ODEInfo
info)
  ([[CodeExpr]] -> CodeExpr
forall r. ExprC r => [[r]] -> r
matrix [ODEInfo -> [CodeExpr]
odeSyst ODEInfo
info])
  ([[CodeExpr]] -> CodeExpr
forall r. ExprC r => [[r]] -> r
matrix [ODEInfo -> [CodeExpr]
initVal ODEInfo
info]CodeExpr -> [CodeExpr] -> [CodeExpr]
forall a. a -> [a] -> [a]
:
    ((ODEInfo -> CodeExpr) -> CodeExpr)
-> [ODEInfo -> CodeExpr] -> [CodeExpr]
forall a b. (a -> b) -> [a] -> [b]
map ((ODEInfo -> CodeExpr) -> ODEInfo -> CodeExpr
forall a b. (a -> b) -> a -> b
$ ODEInfo
info) [ODEInfo -> CodeExpr
tInit, ODEInfo -> CodeExpr
tFinal, ODEOptions -> CodeExpr
absTol (ODEOptions -> CodeExpr)
-> (ODEInfo -> ODEOptions) -> ODEInfo -> CodeExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ODEInfo -> ODEOptions
odeOpts, ODEOptions -> CodeExpr
relTol (ODEOptions -> CodeExpr)
-> (ODEInfo -> ODEOptions) -> ODEInfo -> CodeExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ODEInfo -> ODEOptions
odeOpts, ODEOptions -> CodeExpr
stepSize (ODEOptions -> CodeExpr)
-> (ODEInfo -> ODEOptions) -> ODEInfo -> CodeExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ODEInfo -> ODEOptions
odeOpts])
  DefinitionType
ODE