{-# LANGUAGE TemplateHaskell #-}
module Language.Drasil.Chunk.DifferentialModel (
    -- * Chunk Type
    DifferentialModel,
    -- * Input Language
    ($*),
    -- * Constructors
    makeLinear
    ) where

import Control.Lens (makeLenses, (^.), view)
import Language.Drasil.Chunk.Concept (ConceptChunk, dccWDS)
import Language.Drasil.UID (HasUID(uid))
import Language.Drasil.Classes (Express(..),
  ConceptDomain(..), Definition(..), Idea(..), NamedIdea(..))
import Language.Drasil.ModelExpr.Lang (ModelExpr)
import Language.Drasil.NounPhrase.Core (NP)
import Language.Drasil.Sentence (Sentence)
import Language.Drasil.Expr.Lang (Expr(..))
import Language.Drasil.Chunk.Unital (UnitalChunk)
import Language.Drasil.ModelExpr.Class (ModelExprC(nthderiv, equiv))
import Language.Drasil.Expr.Class (mulRe, addRe, sy)
import Language.Drasil.Chunk.Constrained (ConstrConcept)
import Language.Drasil.Chunk.Quantity (qw)
import Language.Drasil.Literal.Class (exactDbl)

{-
  Input Language minic mathematic equation
  e.g. exactDbl 1 $* 1, 
  exactDbl 1 is coefficient term, 1 is the first derivative
-}

data CoeffDeriv = CD{
                      CoeffDeriv -> Expr
_coeff :: Expr,
                      CoeffDeriv -> Int
_degree :: Int
                    }
makeLenses ''CoeffDeriv

($*) :: Expr -> Int -> CoeffDeriv
$* :: Expr -> Int -> CoeffDeriv
($*) = Expr -> Int -> CoeffDeriv
CD

data DifferentialModel = Linear {
                                  DifferentialModel -> UnitalChunk
_indepVar :: UnitalChunk,
                                  DifferentialModel -> ConstrConcept
_depVar :: ConstrConcept,
                                  DifferentialModel -> [CoeffDeriv]
_coefficients :: [CoeffDeriv],
                                  DifferentialModel -> Expr
_constant :: Expr,
                                  DifferentialModel -> ConceptChunk
_conc :: ConceptChunk
                                }
makeLenses ''DifferentialModel

-- | Finds the 'UID' of the 'ConceptChunk' used to make the 'DifferentialModel'.
instance HasUID        DifferentialModel where uid :: (UID -> f UID) -> DifferentialModel -> f DifferentialModel
uid = (ConceptChunk -> f ConceptChunk)
-> DifferentialModel -> f DifferentialModel
Lens' DifferentialModel ConceptChunk
conc ((ConceptChunk -> f ConceptChunk)
 -> DifferentialModel -> f DifferentialModel)
-> ((UID -> f UID) -> ConceptChunk -> f ConceptChunk)
-> (UID -> f UID)
-> DifferentialModel
-> f DifferentialModel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UID -> f UID) -> ConceptChunk -> f ConceptChunk
forall c. HasUID c => Lens' c UID
uid
-- | Equal if 'UID's are equal.
instance Eq            DifferentialModel where a :: DifferentialModel
a == :: DifferentialModel -> DifferentialModel -> Bool
== b :: DifferentialModel
b = (DifferentialModel
a DifferentialModel -> Getting UID DifferentialModel UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID DifferentialModel UID
forall c. HasUID c => Lens' c UID
uid) UID -> UID -> Bool
forall a. Eq a => a -> a -> Bool
== (DifferentialModel
b DifferentialModel -> Getting UID DifferentialModel UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID DifferentialModel UID
forall c. HasUID c => Lens' c UID
uid)
-- | Finds the term ('NP') of the 'ConceptChunk' used to make the 'DifferentialModel'.
instance NamedIdea     DifferentialModel where term :: (NP -> f NP) -> DifferentialModel -> f DifferentialModel
term = (ConceptChunk -> f ConceptChunk)
-> DifferentialModel -> f DifferentialModel
Lens' DifferentialModel ConceptChunk
conc ((ConceptChunk -> f ConceptChunk)
 -> DifferentialModel -> f DifferentialModel)
-> ((NP -> f NP) -> ConceptChunk -> f ConceptChunk)
-> (NP -> f NP)
-> DifferentialModel
-> f DifferentialModel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NP -> f NP) -> ConceptChunk -> f ConceptChunk
forall c. NamedIdea c => Lens' c NP
term
-- | Finds the idea contained in the 'ConceptChunk' used to make the 'DifferentialModel'.
instance Idea          DifferentialModel where getA :: DifferentialModel -> Maybe String
getA = ConceptChunk -> Maybe String
forall c. Idea c => c -> Maybe String
getA (ConceptChunk -> Maybe String)
-> (DifferentialModel -> ConceptChunk)
-> DifferentialModel
-> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting ConceptChunk DifferentialModel ConceptChunk
-> DifferentialModel -> ConceptChunk
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting ConceptChunk DifferentialModel ConceptChunk
Lens' DifferentialModel ConceptChunk
conc
-- | Finds the definition contained in the 'ConceptChunk' used to make the 'DifferentialModel'.
instance Definition    DifferentialModel where defn :: (Sentence -> f Sentence)
-> DifferentialModel -> f DifferentialModel
defn = (ConceptChunk -> f ConceptChunk)
-> DifferentialModel -> f DifferentialModel
Lens' DifferentialModel ConceptChunk
conc ((ConceptChunk -> f ConceptChunk)
 -> DifferentialModel -> f DifferentialModel)
-> ((Sentence -> f Sentence) -> ConceptChunk -> f ConceptChunk)
-> (Sentence -> f Sentence)
-> DifferentialModel
-> f DifferentialModel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Sentence -> f Sentence) -> ConceptChunk -> f ConceptChunk
forall c. Definition c => Lens' c Sentence
defn
instance ConceptDomain DifferentialModel where cdom :: DifferentialModel -> [UID]
cdom = ConceptChunk -> [UID]
forall c. ConceptDomain c => c -> [UID]
cdom (ConceptChunk -> [UID])
-> (DifferentialModel -> ConceptChunk)
-> DifferentialModel
-> [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting ConceptChunk DifferentialModel ConceptChunk
-> DifferentialModel -> ConceptChunk
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting ConceptChunk DifferentialModel ConceptChunk
Lens' DifferentialModel ConceptChunk
conc
-- | Finds the domain of the 'ConceptChunk' used to make the 'DifferentialModel'.
-- | Convert the 'DifferentialModel' into the model expression language.
-- | Set Canonical form of ODE to Zero, e.g. ax0 + bx1 + cx2 + .... + c = 0
instance Express       DifferentialModel where express :: DifferentialModel -> ModelExpr
express = DifferentialModel -> ModelExpr
formStdODE

-- | Construct a Canonical form of ODE, e.g. ax0 + bx1 + cx2 + .... + c
-- | x0 is the highest order, x1 is the second higest order, and so on. The c is the constant.
formStdODE :: DifferentialModel -> ModelExpr
formStdODE :: DifferentialModel -> ModelExpr
formStdODE d :: DifferentialModel
d = [ModelExpr] -> ModelExpr
forall r. ModelExprC r => [r] -> r
equiv ([ModelExpr] -> ModelExpr) -> [ModelExpr] -> ModelExpr
forall a b. (a -> b) -> a -> b
$ (DifferentialModel -> ModelExpr
addCoes DifferentialModel
d ModelExpr -> ModelExpr -> ModelExpr
forall r. ExprC r => r -> r -> r
`addRe` Expr -> ModelExpr
forall c. Express c => c -> ModelExpr
express (DifferentialModel
d DifferentialModel -> Getting Expr DifferentialModel Expr -> Expr
forall s a. s -> Getting a s a -> a
^. Getting Expr DifferentialModel Expr
Lens' DifferentialModel Expr
constant)) ModelExpr -> [ModelExpr] -> [ModelExpr]
forall a. a -> [a] -> [a]
: [Integer -> ModelExpr
forall r. LiteralC r => Integer -> r
exactDbl 0]

-- | Construct a form of ODE with constant on the rhs
-- formConODE :: DifferentialModel -> ModelExpr
-- formConODE d = equiv $ addCoes d : [express (d ^. constant)]

-- | Add coefficients together by restructuring each CoeffDeriv
addCoes :: DifferentialModel -> ModelExpr
addCoes :: DifferentialModel -> ModelExpr
addCoes d :: DifferentialModel
d = (ModelExpr -> ModelExpr -> ModelExpr) -> [ModelExpr] -> ModelExpr
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 ModelExpr -> ModelExpr -> ModelExpr
forall r. ExprC r => r -> r -> r
addRe ([ModelExpr] -> ModelExpr) -> [ModelExpr] -> ModelExpr
forall a b. (a -> b) -> a -> b
$
            (CoeffDeriv -> ModelExpr) -> [CoeffDeriv] -> [ModelExpr]
forall a b. (a -> b) -> [a] -> [b]
map(\x :: CoeffDeriv
x ->
                  Expr -> ModelExpr
forall c. Express c => c -> ModelExpr
express (CoeffDeriv
x CoeffDeriv -> Getting Expr CoeffDeriv Expr -> Expr
forall s a. s -> Getting a s a -> a
^. Getting Expr CoeffDeriv Expr
Lens' CoeffDeriv Expr
coeff)
                  ModelExpr -> ModelExpr -> ModelExpr
forall r. ExprC r => r -> r -> r
`mulRe`
                  Integer -> ModelExpr -> UnitalChunk -> ModelExpr
forall r c.
(ModelExprC r, HasUID c, HasSymbol c) =>
Integer -> r -> c -> r
nthderiv
                    (Int -> Integer
forall a. Integral a => a -> Integer
toInteger (CoeffDeriv
x CoeffDeriv -> Getting Int CoeffDeriv Int -> Int
forall s a. s -> Getting a s a -> a
^. Getting Int CoeffDeriv Int
Lens' CoeffDeriv Int
degree))
                    (QuantityDict -> ModelExpr
forall r c. (ExprC r, HasUID c, HasSymbol c) => c -> r
sy (ConstrConcept -> QuantityDict
forall q. (Quantity q, MayHaveUnit q) => q -> QuantityDict
qw (DifferentialModel
d DifferentialModel
-> Getting ConstrConcept DifferentialModel ConstrConcept
-> ConstrConcept
forall s a. s -> Getting a s a -> a
^. Getting ConstrConcept DifferentialModel ConstrConcept
Lens' DifferentialModel ConstrConcept
depVar)))
                    (DifferentialModel
d DifferentialModel
-> Getting UnitalChunk DifferentialModel UnitalChunk -> UnitalChunk
forall s a. s -> Getting a s a -> a
^. Getting UnitalChunk DifferentialModel UnitalChunk
Lens' DifferentialModel UnitalChunk
indepVar)
               )
               (DifferentialModel
d DifferentialModel
-> Getting [CoeffDeriv] DifferentialModel [CoeffDeriv]
-> [CoeffDeriv]
forall s a. s -> Getting a s a -> a
^. Getting [CoeffDeriv] DifferentialModel [CoeffDeriv]
Lens' DifferentialModel [CoeffDeriv]
coefficients)

-- | Create a 'DifferentialModel' from a given indepVar ('UnitalChunk'), DepVar ('ModelExpr'),
-- | Coefficients ('[Expr]'), Constant ('Expr'), UID ('String'), term ('NP'), definition ('Sentence').
makeLinear :: UnitalChunk -> ConstrConcept -> [CoeffDeriv] -> Expr -> String -> NP -> Sentence -> DifferentialModel
makeLinear :: UnitalChunk
-> ConstrConcept
-> [CoeffDeriv]
-> Expr
-> String
-> NP
-> Sentence
-> DifferentialModel
makeLinear dmIndepVar :: UnitalChunk
dmIndepVar dmDepVar :: ConstrConcept
dmDepVar dmCoeff :: [CoeffDeriv]
dmCoeff dmConst :: Expr
dmConst dmID :: String
dmID dmTerm :: NP
dmTerm dmDefn :: Sentence
dmDefn =
  UnitalChunk
-> ConstrConcept
-> [CoeffDeriv]
-> Expr
-> ConceptChunk
-> DifferentialModel
Linear UnitalChunk
dmIndepVar ConstrConcept
dmDepVar [CoeffDeriv]
dmCoeff Expr
dmConst (String -> NP -> Sentence -> ConceptChunk
dccWDS String
dmID NP
dmTerm Sentence
dmDefn)