{-# Language TemplateHaskell #-}
-- | For adding an uncertainty value to quantities with constraints.
module Language.Drasil.Chunk.UncertainQuantity (
  -- * Chunk Types
  UncertQ, UncertainChunk(..),
  -- * Constructors
  uq, uqc,
  uqcND, uncrtnChunk, uvc, uncrtnw) where
 
import Language.Drasil.Chunk.DefinedQuantity (dqdWr)
import Language.Drasil.Chunk.Constrained (ConstrConcept(..), ConstrainedChunk, cuc', cnstrw, cvc)
import Language.Drasil.Symbol
import Language.Drasil.Classes (NamedIdea(term), Idea(getA), Express(express),
  Definition(defn), ConceptDomain(cdom), Concept, Quantity,
  IsUnit, Constrained(constraints), HasReasVal(reasVal))
import Language.Drasil.Constraint (ConstraintE)
import Language.Drasil.Chunk.UnitDefn (MayHaveUnit(getUnit))
import Language.Drasil.Expr.Lang (Expr)
import Language.Drasil.Expr.Class (sy)
import Language.Drasil.NounPhrase.Core (NP)
import Language.Drasil.Space (Space, HasSpace(..))
import Language.Drasil.Uncertainty
import Language.Drasil.UID (HasUID(..))

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

{- The order of the following two implementations is the same as in Constrained -}

-- | UncertainChunk is a symbolic quantity with constraints, a typical value, and an uncertainty. 
-- Contains a 'ConstrainedChunk' and an 'Uncertainty'.
--
-- Ex. Measuring the length of a pendulum arm may be recorded with an uncertainty value.
data UncertainChunk  = UCh { UncertainChunk -> ConstrainedChunk
_conc :: ConstrainedChunk , UncertainChunk -> Uncertainty
_unc' :: Uncertainty }
makeLenses ''UncertainChunk

-- | Finds 'UID' of the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance HasUID            UncertainChunk where uid :: Lens' UncertainChunk UID
uid = Lens' UncertainChunk ConstrainedChunk
conc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasUID c => Lens' c UID
uid
-- | Equal if 'UID's are equal.
instance Eq                UncertainChunk where UncertainChunk
c1 == :: UncertainChunk -> UncertainChunk -> Bool
== UncertainChunk
c2 = (UncertainChunk
c1 forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid) forall a. Eq a => a -> a -> Bool
== (UncertainChunk
c2 forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid)
-- | Finds term ('NP') of the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance NamedIdea         UncertainChunk where term :: Lens' UncertainChunk NP
term = Lens' UncertainChunk ConstrainedChunk
conc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. NamedIdea c => Lens' c NP
term
-- | Finds the idea contained in the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance Idea              UncertainChunk where getA :: UncertainChunk -> Maybe String
getA (UCh ConstrainedChunk
n Uncertainty
_) = forall c. Idea c => c -> Maybe String
getA ConstrainedChunk
n
-- | Finds the 'Space' of the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance HasSpace          UncertainChunk where typ :: Getter UncertainChunk Space
typ = Lens' UncertainChunk ConstrainedChunk
conc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasSpace c => Getter c Space
typ
-- | Finds the 'Symbol' of the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance HasSymbol         UncertainChunk where symbol :: UncertainChunk -> Stage -> Symbol
symbol UncertainChunk
c = forall c. HasSymbol c => c -> Stage -> Symbol
symbol (UncertainChunk
cforall s a. s -> Getting a s a -> a
^.Lens' UncertainChunk ConstrainedChunk
conc)
-- | 'UncertainChunk's have a 'Quantity'.
instance Quantity          UncertainChunk where
-- | Finds the 'Constraint's of the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance Constrained       UncertainChunk where constraints :: Lens' UncertainChunk [ConstraintE]
constraints = Lens' UncertainChunk ConstrainedChunk
conc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. Constrained c => Lens' c [ConstraintE]
constraints
-- | Finds a reasonable value for the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance HasReasVal        UncertainChunk where reasVal :: Lens' UncertainChunk (Maybe Expr)
reasVal = Lens' UncertainChunk ConstrainedChunk
conc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasReasVal c => Lens' c (Maybe Expr)
reasVal
-- | Finds the uncertainty of an 'UncertainChunk'.
instance HasUncertainty    UncertainChunk where unc :: Lens' UncertainChunk Uncertainty
unc = Lens' UncertainChunk Uncertainty
unc'
-- | Finds units contained in the 'ConstrainedChunk' used to make the 'UncertainChunk'.
instance MayHaveUnit       UncertainChunk where getUnit :: UncertainChunk -> Maybe UnitDefn
getUnit = forall u. MayHaveUnit u => u -> Maybe UnitDefn
getUnit forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' UncertainChunk ConstrainedChunk
conc

{-- Constructors --}
-- | Smart constructor that can project to an 'UncertainChunk' (also given an 'Uncertainty').
uncrtnChunk :: (Quantity c, Constrained c, HasReasVal c, MayHaveUnit c) => 
  c -> Uncertainty -> UncertainChunk
uncrtnChunk :: forall c.
(Quantity c, Constrained c, HasReasVal c, MayHaveUnit c) =>
c -> Uncertainty -> UncertainChunk
uncrtnChunk c
q = ConstrainedChunk -> Uncertainty -> UncertainChunk
UCh (forall c.
(Quantity c, Constrained c, HasReasVal c, MayHaveUnit c) =>
c -> ConstrainedChunk
cnstrw c
q)

-- | Creates an uncertain variable chunk. Takes 'UID', term ('NP'),
-- 'Symbol', 'Space', 'Constrains', 'Expr', and 'Uncertainty'.
uvc :: String -> NP -> Symbol -> Space -> [ConstraintE] -> Expr -> Uncertainty -> UncertainChunk
uvc :: String
-> NP
-> Symbol
-> Space
-> [ConstraintE]
-> Expr
-> Uncertainty
-> UncertainChunk
uvc String
nam NP
trm Symbol
sym Space
space [ConstraintE]
cs Expr
val = forall c.
(Quantity c, Constrained c, HasReasVal c, MayHaveUnit c) =>
c -> Uncertainty -> UncertainChunk
uncrtnChunk (String
-> NP
-> Symbol
-> Space
-> [ConstraintE]
-> Maybe Expr
-> ConstrainedChunk
cvc String
nam NP
trm Symbol
sym Space
space [ConstraintE]
cs (forall a. a -> Maybe a
Just Expr
val))

-- | Projection function into an 'UncertainChunk' from 'UncertQ' or an 'UncertainChunk'.
uncrtnw :: (HasUncertainty c, Quantity c, Constrained c, HasReasVal c, MayHaveUnit c) => c -> UncertainChunk
uncrtnw :: forall c.
(HasUncertainty c, Quantity c, Constrained c, HasReasVal c,
 MayHaveUnit c) =>
c -> UncertainChunk
uncrtnw c
c = ConstrainedChunk -> Uncertainty -> UncertainChunk
UCh (forall c.
(Quantity c, Constrained c, HasReasVal c, MayHaveUnit c) =>
c -> ConstrainedChunk
cnstrw c
c) (c
c forall s a. s -> Getting a s a -> a
^. forall c. HasUncertainty c => Lens' c Uncertainty
unc)

-- | UncertQs are conceptual symbolic quantities with constraints and an 'Uncertainty'.
-- Contains a 'ConstrConcept' and an 'Uncertainty'.
--
-- Ex. Measuring the length of a pendulum arm may be recorded with an uncertainty value.
data UncertQ = UQ { UncertQ -> ConstrConcept
_coco :: ConstrConcept , UncertQ -> Uncertainty
_unc'' :: Uncertainty }
makeLenses ''UncertQ
  
-- | Equal if 'UID's are equal.
instance Eq             UncertQ where UncertQ
a == :: UncertQ -> UncertQ -> Bool
== UncertQ
b = (UncertQ
a forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid) forall a. Eq a => a -> a -> Bool
== (UncertQ
b forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid)
-- | Finds 'UID' of the 'ConstrConcept' used to make the 'UncertQ'.
instance HasUID         UncertQ where uid :: Lens' UncertQ UID
uid = Lens' UncertQ ConstrConcept
coco forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasUID c => Lens' c UID
uid
-- | Finds term ('NP') of the 'ConstrConcept' used to make the 'UncertQ'.
instance NamedIdea      UncertQ where term :: Lens' UncertQ NP
term = Lens' UncertQ ConstrConcept
coco forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. NamedIdea c => Lens' c NP
term
-- | Finds the idea contained in the 'ConstrConcept' used to make the 'UncertQ'.
instance Idea           UncertQ where getA :: UncertQ -> Maybe String
getA (UQ ConstrConcept
q Uncertainty
_) = forall c. Idea c => c -> Maybe String
getA ConstrConcept
q
-- | Finds the 'Space' of the 'ConstrConcept' used to make the 'UncertQ'.
instance HasSpace       UncertQ where typ :: Getter UncertQ Space
typ = Lens' UncertQ ConstrConcept
coco forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasSpace c => Getter c Space
typ
-- | Finds the 'Symbol' of the 'ConstrConcept' used to make the 'UncertQ'.
instance HasSymbol      UncertQ where symbol :: UncertQ -> Stage -> Symbol
symbol UncertQ
c = forall c. HasSymbol c => c -> Stage -> Symbol
symbol (UncertQ
cforall s a. s -> Getting a s a -> a
^.Lens' UncertQ ConstrConcept
coco)
-- | 'UncertQ's have a 'Quantity'.
instance Quantity       UncertQ where 
-- | Finds the uncertainty of an 'UncertQ'.
instance HasUncertainty UncertQ where unc :: Lens' UncertQ Uncertainty
unc = Lens' UncertQ Uncertainty
unc''
-- | Finds the 'Constraint's of a 'ConstrConcept' used to make the 'UncertQ'.
instance Constrained    UncertQ where constraints :: Lens' UncertQ [ConstraintE]
constraints = Lens' UncertQ ConstrConcept
coco forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. Constrained c => Lens' c [ConstraintE]
constraints
-- | Finds a reasonable value for the 'ConstrConcept' used to make the 'UncertQ'.
instance HasReasVal     UncertQ where reasVal :: Lens' UncertQ (Maybe Expr)
reasVal = Lens' UncertQ ConstrConcept
coco forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasReasVal c => Lens' c (Maybe Expr)
reasVal
-- | Finds definition of the 'ConstrConcept' used to make the 'UncertQ'.
instance Definition     UncertQ where defn :: Lens' UncertQ Sentence
defn = Lens' UncertQ ConstrConcept
coco forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. Definition c => Lens' c Sentence
defn
-- | Finds the domain contained in the 'ConstrConcept' used to make the 'UncertQ'.
instance ConceptDomain  UncertQ where cdom :: UncertQ -> [UID]
cdom = forall c. ConceptDomain c => c -> [UID]
cdom forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' UncertQ ConstrConcept
coco
-- | Finds the units of the 'ConstrConcept' used to make the 'UncertQ'.
instance MayHaveUnit    UncertQ where getUnit :: UncertQ -> Maybe UnitDefn
getUnit = forall u. MayHaveUnit u => u -> Maybe UnitDefn
getUnit forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' UncertQ ConstrConcept
coco
-- | Convert the symbol of the 'UncertQ' to a 'ModelExpr'.
instance Express        UncertQ where express :: UncertQ -> ModelExpr
express = forall r c. (ExprC r, HasUID c, HasSymbol c) => c -> r
sy

{-- Constructors --}
-- | Smart constructor that requires a 'Quantity', a percentage, and a typical value with an 'Uncertainty'.
uq :: (Quantity c, Constrained c, Concept c, HasReasVal c, MayHaveUnit c) =>
  c -> Uncertainty -> UncertQ
uq :: forall c.
(Quantity c, Constrained c, Concept c, HasReasVal c,
 MayHaveUnit c) =>
c -> Uncertainty -> UncertQ
uq c
q = ConstrConcept -> Uncertainty -> UncertQ
UQ (DefinedQuantityDict -> [ConstraintE] -> Maybe Expr -> ConstrConcept
ConstrConcept (forall c.
(Quantity c, Concept c, MayHaveUnit c) =>
c -> DefinedQuantityDict
dqdWr c
q) (c
q forall s a. s -> Getting a s a -> a
^. forall c. Constrained c => Lens' c [ConstraintE]
constraints) (c
q forall s a. s -> Getting a s a -> a
^. forall c. HasReasVal c => Lens' c (Maybe Expr)
reasVal))

--FIXME: this is kind of crazy and probably shouldn't be used!
-- | Uncertainty quantity ('uq') but with a constraint.
uqc :: (IsUnit u) => String -> NP -> String -> Symbol -> u -> Space
                -> [ConstraintE] -> Expr -> Uncertainty -> UncertQ
uqc :: forall u.
IsUnit u =>
String
-> NP
-> String
-> Symbol
-> u
-> Space
-> [ConstraintE]
-> Expr
-> Uncertainty
-> UncertQ
uqc String
nam NP
trm String
desc Symbol
sym u
un Space
space [ConstraintE]
cs Expr
val = forall c.
(Quantity c, Constrained c, Concept c, HasReasVal c,
 MayHaveUnit c) =>
c -> Uncertainty -> UncertQ
uq (forall u.
IsUnit u =>
String
-> NP
-> String
-> Symbol
-> u
-> Space
-> [ConstraintE]
-> Expr
-> ConstrConcept
cuc' String
nam NP
trm String
desc Symbol
sym u
un Space
space [ConstraintE]
cs Expr
val)

-- | Uncertainty quantity constraint ('uqc') without a description.
uqcND :: (IsUnit u) => String -> NP -> Symbol -> u -> Space -> [ConstraintE]
                  -> Expr -> Uncertainty -> UncertQ
uqcND :: forall u.
IsUnit u =>
String
-> NP
-> Symbol
-> u
-> Space
-> [ConstraintE]
-> Expr
-> Uncertainty
-> UncertQ
uqcND String
nam NP
trm Symbol
sym u
un Space
space [ConstraintE]
cs Expr
val = forall c.
(Quantity c, Constrained c, Concept c, HasReasVal c,
 MayHaveUnit c) =>
c -> Uncertainty -> UncertQ
uq (forall u.
IsUnit u =>
String
-> NP
-> String
-> Symbol
-> u
-> Space
-> [ConstraintE]
-> Expr
-> ConstrConcept
cuc' String
nam NP
trm String
"" Symbol
sym u
un Space
space [ConstraintE]
cs Expr
val)