{-# Language TemplateHaskell #-}
-- | For defining units built from a concept.
module Language.Drasil.Chunk.UnitDefn (
  -- * Classes
  MayHaveUnit(getUnit),
  IsUnit(getUnits),
  -- * Chunk Type
  UnitDefn(..),
  -- * Constructors
  baseUnit, derivedUnit, derivedUnitScale, derivedUnitShift,
  compoundUnit, compoundUnitScale, compoundUnitShift,
  -- * Unit Combinators ('UnitEquation's)
  (^:), (/:), (*:), (*$), (/$), (^$),
  -- * Helpers
  fromUDefn, getCu, compUnitDefn, unitSymbol
) where

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

import Drasil.Database (HasChunkRefs(..), UID, HasUID(..), mkUid, nsUid)

import Language.Drasil.Chunk.Concept (ConceptChunk, cncpt''')
import Language.Drasil.Sentence (Sentence(..))
import Language.Drasil.Classes (NamedIdea(term), Idea(getA),
  Definition(defn), HasUnitSymbol(usymb), IsUnit(udefn, getUnits))
import Language.Drasil.NaturalLanguage.English.NounPhrase (NP)
import Language.Drasil.Symbol (Symbol)
import Language.Drasil.UnitLang (USymb(US), UDefn(UScale, USynonym, UShift),
  compUSymb, fromUDefn, getUSymb, getDefn, UnitSymbol(BaseSI, DerivedSI, Defined))

-- | For defining units.
-- It has a 'ConceptChunk' (that defines what kind of unit it is),
-- a unit symbol, maybe another (when it is a synonym),
-- perhaps a definition, and a list of 'UID' of the units that make up
-- the definition.
--
-- Ex. Meter is a unit of length defined by the symbol (m).
data UnitDefn = UD { UnitDefn -> ConceptChunk
_vc :: ConceptChunk
                   , UnitDefn -> UnitSymbol
_cas :: UnitSymbol
                   , UnitDefn -> [UID]
_cu :: [UID] }
makeLenses ''UnitDefn

instance HasChunkRefs UnitDefn where
  chunkRefs :: UnitDefn -> Set UID
chunkRefs UnitDefn
ud = ConceptChunk -> Set UID
forall a. HasChunkRefs a => a -> Set UID
chunkRefs (UnitDefn
ud UnitDefn
-> Getting ConceptChunk UnitDefn ConceptChunk -> ConceptChunk
forall s a. s -> Getting a s a -> a
^. Getting ConceptChunk UnitDefn ConceptChunk
Lens' UnitDefn ConceptChunk
vc)
  {-# INLINABLE chunkRefs #-}

-- | Finds 'UID' of the 'ConceptChunk' used to make the 'UnitDefn'.
instance HasUID        UnitDefn where uid :: Getter UnitDefn UID
uid = (ConceptChunk -> f ConceptChunk) -> UnitDefn -> f UnitDefn
Lens' UnitDefn ConceptChunk
vc ((ConceptChunk -> f ConceptChunk) -> UnitDefn -> f UnitDefn)
-> ((UID -> f UID) -> ConceptChunk -> f ConceptChunk)
-> (UID -> f UID)
-> UnitDefn
-> f UnitDefn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UID -> f UID) -> ConceptChunk -> f ConceptChunk
forall c. HasUID c => Getter c UID
Getter ConceptChunk UID
uid
-- | Finds term ('NP') of the 'ConceptChunk' used to make the 'UnitDefn'.
instance NamedIdea     UnitDefn where term :: Lens' UnitDefn NP
term   = (ConceptChunk -> f ConceptChunk) -> UnitDefn -> f UnitDefn
Lens' UnitDefn ConceptChunk
vc ((ConceptChunk -> f ConceptChunk) -> UnitDefn -> f UnitDefn)
-> ((NP -> f NP) -> ConceptChunk -> f ConceptChunk)
-> (NP -> f NP)
-> UnitDefn
-> f UnitDefn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NP -> f NP) -> ConceptChunk -> f ConceptChunk
forall c. NamedIdea c => Lens' c NP
Lens' ConceptChunk NP
term
-- | Finds the idea contained in the 'ConceptChunk' used to make the 'UnitDefn'.
instance Idea          UnitDefn where getA :: UnitDefn -> Maybe String
getA UnitDefn
c = ConceptChunk -> Maybe String
forall c. Idea c => c -> Maybe String
getA (UnitDefn
c UnitDefn
-> Getting ConceptChunk UnitDefn ConceptChunk -> ConceptChunk
forall s a. s -> Getting a s a -> a
^. Getting ConceptChunk UnitDefn ConceptChunk
Lens' UnitDefn ConceptChunk
vc)
-- | Finds definition of the 'ConceptChunk' used to make the 'UnitDefn'.
instance Definition    UnitDefn where defn :: Lens' UnitDefn Sentence
defn = (ConceptChunk -> f ConceptChunk) -> UnitDefn -> f UnitDefn
Lens' UnitDefn ConceptChunk
vc ((ConceptChunk -> f ConceptChunk) -> UnitDefn -> f UnitDefn)
-> ((Sentence -> f Sentence) -> ConceptChunk -> f ConceptChunk)
-> (Sentence -> f Sentence)
-> UnitDefn
-> f UnitDefn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Sentence -> f Sentence) -> ConceptChunk -> f ConceptChunk
forall c. Definition c => Lens' c Sentence
Lens' ConceptChunk Sentence
defn
-- | Equal if 'Symbol's are equal.
instance Eq            UnitDefn where UnitDefn
a == :: UnitDefn -> UnitDefn -> Bool
== UnitDefn
b = UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
a USymb -> USymb -> Bool
forall a. Eq a => a -> a -> Bool
== UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
b
-- | Finds unit symbol of the 'ConceptChunk' used to make the 'UnitDefn'.
instance HasUnitSymbol UnitDefn where usymb :: UnitDefn -> USymb
usymb = UnitSymbol -> USymb
getUSymb (UnitSymbol -> USymb)
-> (UnitDefn -> UnitSymbol) -> UnitDefn -> USymb
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting UnitSymbol UnitDefn UnitSymbol -> UnitDefn -> UnitSymbol
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting UnitSymbol UnitDefn UnitSymbol
Lens' UnitDefn UnitSymbol
cas
-- | Gets the UnitDefn and contributing units.
instance IsUnit        UnitDefn where
  udefn :: UnitDefn -> Maybe UDefn
udefn = UnitSymbol -> Maybe UDefn
getDefn (UnitSymbol -> Maybe UDefn)
-> (UnitDefn -> UnitSymbol) -> UnitDefn -> Maybe UDefn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting UnitSymbol UnitDefn UnitSymbol -> UnitDefn -> UnitSymbol
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting UnitSymbol UnitDefn UnitSymbol
Lens' UnitDefn UnitSymbol
cas  -- Finds unit definition of UnitDefn.
  getUnits :: UnitDefn -> [UID]
getUnits = Getting [UID] UnitDefn [UID] -> UnitDefn -> [UID]
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting [UID] UnitDefn [UID]
Lens' UnitDefn [UID]
cu  -- Finds list of contributing units through UIDs from a UnitDefn.

-- | Types may contain a unit ('UnitDefn').
class MayHaveUnit u where
   getUnit :: u -> Maybe UnitDefn

-- | Takes a contributing unit (['UID']) and a symbol ('USymb').
data UnitEquation = UE {UnitEquation -> [UID]
_contributingUnit :: [UID]
                       , UnitEquation -> USymb
_us :: USymb}
makeLenses ''UnitEquation
instance HasUnitSymbol UnitEquation where usymb :: UnitEquation -> USymb
usymb UnitEquation
u = UnitEquation
u UnitEquation -> Getting USymb UnitEquation USymb -> USymb
forall s a. s -> Getting a s a -> a
^. Getting USymb UnitEquation USymb
Lens' UnitEquation USymb
us
-- ^ Finds the unit symbol ('USymb') for a 'UnitEquation'.

-- | Extract the 'UnitSymbol' of a 'UnitDefn'.
unitSymbol :: UnitDefn -> UnitSymbol
unitSymbol :: UnitDefn -> UnitSymbol
unitSymbol = (UnitDefn -> Getting UnitSymbol UnitDefn UnitSymbol -> UnitSymbol
forall s a. s -> Getting a s a -> a
^. Getting UnitSymbol UnitDefn UnitSymbol
Lens' UnitDefn UnitSymbol
cas)

-- | Get a list of 'UID' of the units that make up the 'UnitEquation'.
getCu :: UnitEquation -> [UID]
getCu :: UnitEquation -> [UID]
getCu = Getting [UID] UnitEquation [UID] -> UnitEquation -> [UID]
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting [UID] UnitEquation [UID]
Lens' UnitEquation [UID]
contributingUnit

-- | Create a base unit (one not defined in terms of any other unit, e.g. m, kg)
-- from a 'UID' string, term, definition, and its symbol.
baseUnit :: String -> NP -> String -> Symbol -> UnitDefn
baseUnit :: String -> NP -> String -> Symbol -> UnitDefn
baseUnit String
idStr NP
trm String
dsc Symbol
sym =
  ConceptChunk -> UnitSymbol -> [UID] -> UnitDefn
UD (UID -> NP -> Sentence -> ConceptChunk
cncpt''' (String -> UID
unitUid String
idStr) NP
trm (String -> Sentence
S String
dsc)) (USymb -> UnitSymbol
BaseSI (USymb -> UnitSymbol) -> USymb -> UnitSymbol
forall a b. (a -> b) -> a -> b
$ [(Symbol, Integer)] -> USymb
US [(Symbol
sym, Integer
1)]) []

-- | Create a derived unit (a combination of other units that has its own
-- special symbol, e.g. N) from a 'UID' string, term, definition, its symbol,
-- and the unit equation it is defined by.
derivedUnit :: String -> NP -> String -> Symbol -> UnitEquation -> UnitDefn
derivedUnit :: String -> NP -> String -> Symbol -> UnitEquation -> UnitDefn
derivedUnit String
idStr NP
trm String
dsc Symbol
sym UnitEquation
ueq =
  ConceptChunk -> UnitSymbol -> [UID] -> UnitDefn
UD (UID -> NP -> Sentence -> ConceptChunk
cncpt''' (String -> UID
unitUid String
idStr) NP
trm (String -> Sentence
S String
dsc))
     (USymb -> USymb -> UDefn -> UnitSymbol
DerivedSI ([(Symbol, Integer)] -> USymb
US [(Symbol
sym,Integer
1)]) (UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
ueq) (USymb -> UDefn
USynonym (USymb -> UDefn) -> USymb -> UDefn
forall a b. (a -> b) -> a -> b
$ UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
ueq))
     (UnitEquation -> [UID]
getCu UnitEquation
ueq)

-- | Create a derived unit that is a scaled version of another unit
-- from a 'UID' string, term, definition, its symbol, the scaling factor,
-- and the unit being scaled.
derivedUnitScale :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
derivedUnitScale :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
derivedUnitScale String
idStr NP
trm String
dsc Symbol
sym Double
factor UnitDefn
base =
  ConceptChunk -> UnitSymbol -> [UID] -> UnitDefn
UD (UID -> NP -> Sentence -> ConceptChunk
cncpt''' (String -> UID
unitUid String
idStr) NP
trm (String -> Sentence
S String
dsc))
     (USymb -> USymb -> UDefn -> UnitSymbol
DerivedSI ([(Symbol, Integer)] -> USymb
US [(Symbol
sym, Integer
1)]) (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
base) (Double -> USymb -> UDefn
UScale Double
factor (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
base)))
     [UnitDefn
base UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid]

-- | Create a derived unit that is a shifted version of another unit
-- (e.g. centigrade = kelvin - 273.15) from a 'UID' string, term, definition,
-- its symbol, the shift factor, and the unit being shifted.
derivedUnitShift :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
derivedUnitShift :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
derivedUnitShift String
idStr NP
trm String
dsc Symbol
sym Double
factor UnitDefn
base =
  ConceptChunk -> UnitSymbol -> [UID] -> UnitDefn
UD (UID -> NP -> Sentence -> ConceptChunk
cncpt''' (String -> UID
unitUid String
idStr) NP
trm (String -> Sentence
S String
dsc))
     (USymb -> USymb -> UDefn -> UnitSymbol
DerivedSI ([(Symbol, Integer)] -> USymb
US [(Symbol
sym, Integer
1)]) (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
base) (Double -> USymb -> UDefn
UShift Double
factor (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
base)))
     [UnitDefn
base UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid]

-- | Create a compound unit (a combination of other units without its own
-- special symbol, e.g. m/s) from a 'UID' string, term, definition, and the
-- unit equation it is defined by.
compoundUnit :: String -> NP -> String -> UnitEquation -> UnitDefn
compoundUnit :: String -> NP -> String -> UnitEquation -> UnitDefn
compoundUnit String
idStr NP
trm String
dsc UnitEquation
ueq =
  ConceptChunk -> UnitSymbol -> [UID] -> UnitDefn
UD (UID -> NP -> Sentence -> ConceptChunk
cncpt''' (String -> UID
unitUid String
idStr) NP
trm (String -> Sentence
S String
dsc))
     (USymb -> UDefn -> UnitSymbol
Defined (UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
ueq) (USymb -> UDefn
USynonym (USymb -> UDefn) -> USymb -> UDefn
forall a b. (a -> b) -> a -> b
$ UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
ueq))
     (UnitEquation -> [UID]
getCu UnitEquation
ueq)

-- | Create a defined unit that is a scaled version of another unit
-- (e.g. millimetre = 0.001 × metre) from a 'UID' string, term, definition,
-- its symbol, the scaling factor, and the unit being scaled.
compoundUnitScale :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
compoundUnitScale :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
compoundUnitScale String
idStr NP
trm String
dsc Symbol
sym Double
factor UnitDefn
base =
  ConceptChunk -> UnitSymbol -> [UID] -> UnitDefn
UD (UID -> NP -> Sentence -> ConceptChunk
cncpt''' (String -> UID
unitUid String
idStr) NP
trm (String -> Sentence
S String
dsc))
     (USymb -> UDefn -> UnitSymbol
Defined ([(Symbol, Integer)] -> USymb
US [(Symbol
sym, Integer
1)]) (Double -> USymb -> UDefn
UScale Double
factor (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
base)))
     [UnitDefn
base UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid]

-- | Create a defined unit that is a shifted version of another unit
-- from a 'UID' string, term, definition, its symbol, the shift factor,
-- and the unit being shifted.
compoundUnitShift :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
compoundUnitShift :: String -> NP -> String -> Symbol -> Double -> UnitDefn -> UnitDefn
compoundUnitShift String
idStr NP
trm String
dsc Symbol
sym Double
factor UnitDefn
base =
  ConceptChunk -> UnitSymbol -> [UID] -> UnitDefn
UD (UID -> NP -> Sentence -> ConceptChunk
cncpt''' (String -> UID
unitUid String
idStr) NP
trm (String -> Sentence
S String
dsc))
     (USymb -> UDefn -> UnitSymbol
Defined ([(Symbol, Integer)] -> USymb
US [(Symbol
sym, Integer
1)]) (Double -> USymb -> UDefn
UShift Double
factor (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
base)))
     [UnitDefn
base UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid]

-- | Build the 'UID' of a unit from its name, in the "unit" namespace.
unitUid :: String -> UID
unitUid :: String -> UID
unitUid = String -> UID -> UID
nsUid String
"unit" (UID -> UID) -> (String -> UID) -> String -> UID
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> UID
mkUid

---------------------------------------------------------
--- These conveniences go here, because we need the class
-- | Combinator for raising a unit to a power.
(^:) :: UnitDefn -> Integer -> UnitEquation
UnitDefn
u ^: :: UnitDefn -> Integer -> UnitEquation
^: Integer
i = [UID] -> USymb -> UnitEquation
UE [UnitDefn
u UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid] (USymb -> USymb
upow (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
u))
  where
    upow :: USymb -> USymb
upow (US [(Symbol, Integer)]
l) = [(Symbol, Integer)] -> USymb
US ([(Symbol, Integer)] -> USymb) -> [(Symbol, Integer)] -> USymb
forall a b. (a -> b) -> a -> b
$ (Integer -> Integer) -> (Symbol, Integer) -> (Symbol, Integer)
forall b c d. (b -> c) -> (d, b) -> (d, c)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second (Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
i) ((Symbol, Integer) -> (Symbol, Integer))
-> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Symbol, Integer)]
l

-- | Combinator for dividing one unit by another.
(/:) :: UnitDefn -> UnitDefn -> UnitEquation
UnitDefn
u1 /: :: UnitDefn -> UnitDefn -> UnitEquation
/: UnitDefn
u2 = let US [(Symbol, Integer)]
l1 = UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
u1
               US [(Symbol, Integer)]
l2 = UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
u2 in
  [UID] -> USymb -> UnitEquation
UE [UnitDefn
u1 UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid, UnitDefn
u2 UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid] ([(Symbol, Integer)] -> USymb
US ([(Symbol, Integer)] -> USymb) -> [(Symbol, Integer)] -> USymb
forall a b. (a -> b) -> a -> b
$ [(Symbol, Integer)]
l1 [(Symbol, Integer)] -> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall a. Semigroup a => a -> a -> a
<> ((Symbol, Integer) -> (Symbol, Integer))
-> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Integer -> Integer) -> (Symbol, Integer) -> (Symbol, Integer)
forall b c d. (b -> c) -> (d, b) -> (d, c)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second Integer -> Integer
forall a. Num a => a -> a
negate) [(Symbol, Integer)]
l2)

-- | Combinator for multiplying two units together.
(*:) :: UnitDefn -> UnitDefn -> UnitEquation
UnitDefn
u1 *: :: UnitDefn -> UnitDefn -> UnitEquation
*: UnitDefn
u2 = let US [(Symbol, Integer)]
l1 = UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
u1
               US [(Symbol, Integer)]
l2 = UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
u2 in
  [UID] -> USymb -> UnitEquation
UE [UnitDefn
u1 UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid, UnitDefn
u2 UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid] ([(Symbol, Integer)] -> USymb
US ([(Symbol, Integer)] -> USymb) -> [(Symbol, Integer)] -> USymb
forall a b. (a -> b) -> a -> b
$ [(Symbol, Integer)]
l1 [(Symbol, Integer)] -> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall a. Semigroup a => a -> a -> a
<> [(Symbol, Integer)]
l2)

-- | Combinator for multiplying a unit and a symbol.
(*$) :: UnitDefn -> UnitEquation -> UnitEquation
UnitDefn
u1 *$ :: UnitDefn -> UnitEquation -> UnitEquation
*$ UnitEquation
u2 = let US [(Symbol, Integer)]
l1 = UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
u1
               US [(Symbol, Integer)]
l2 = UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
u2 in
  [UID] -> USymb -> UnitEquation
UE (UnitDefn
u1 UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid UID -> [UID] -> [UID]
forall a. a -> [a] -> [a]
: UnitEquation -> [UID]
getCu UnitEquation
u2) ([(Symbol, Integer)] -> USymb
US ([(Symbol, Integer)] -> USymb) -> [(Symbol, Integer)] -> USymb
forall a b. (a -> b) -> a -> b
$ [(Symbol, Integer)]
l1 [(Symbol, Integer)] -> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall a. Semigroup a => a -> a -> a
<> [(Symbol, Integer)]
l2)

-- | Combinator for dividing a unit and a symbol.
(/$) :: UnitDefn -> UnitEquation -> UnitEquation
UnitDefn
u1 /$ :: UnitDefn -> UnitEquation -> UnitEquation
/$ UnitEquation
u2 = let US [(Symbol, Integer)]
l1 = UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
u1
               US [(Symbol, Integer)]
l2 = UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
u2 in
  [UID] -> USymb -> UnitEquation
UE (UnitDefn
u1 UnitDefn -> Getting UID UnitDefn UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID UnitDefn UID
forall c. HasUID c => Getter c UID
Getter UnitDefn UID
uid UID -> [UID] -> [UID]
forall a. a -> [a] -> [a]
: UnitEquation -> [UID]
getCu UnitEquation
u2) ([(Symbol, Integer)] -> USymb
US ([(Symbol, Integer)] -> USymb) -> [(Symbol, Integer)] -> USymb
forall a b. (a -> b) -> a -> b
$ [(Symbol, Integer)]
l1 [(Symbol, Integer)] -> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall a. Semigroup a => a -> a -> a
<> ((Symbol, Integer) -> (Symbol, Integer))
-> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Integer -> Integer) -> (Symbol, Integer) -> (Symbol, Integer)
forall b c d. (b -> c) -> (d, b) -> (d, c)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second Integer -> Integer
forall a. Num a => a -> a
negate) [(Symbol, Integer)]
l2)

-- | Combinator for mulitiplying two unit equations.
(^$) :: UnitEquation -> UnitEquation -> UnitEquation
UnitEquation
u1 ^$ :: UnitEquation -> UnitEquation -> UnitEquation
^$ UnitEquation
u2 = let US [(Symbol, Integer)]
l1 = UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
u1
               US [(Symbol, Integer)]
l2 = UnitEquation -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitEquation
u2 in
  [UID] -> USymb -> UnitEquation
UE (UnitEquation -> [UID]
getCu UnitEquation
u1 [UID] -> [UID] -> [UID]
forall a. Semigroup a => a -> a -> a
<> UnitEquation -> [UID]
getCu UnitEquation
u2) ([(Symbol, Integer)] -> USymb
US ([(Symbol, Integer)] -> USymb) -> [(Symbol, Integer)] -> USymb
forall a b. (a -> b) -> a -> b
$ [(Symbol, Integer)]
l1 [(Symbol, Integer)] -> [(Symbol, Integer)] -> [(Symbol, Integer)]
forall a. Semigroup a => a -> a -> a
<> [(Symbol, Integer)]
l2)

-- | We don't want an Ord on units, but this still allows us to compare them.
compUnitDefn :: UnitDefn -> UnitDefn -> Ordering
compUnitDefn :: UnitDefn -> UnitDefn -> Ordering
compUnitDefn UnitDefn
a UnitDefn
b = USymb -> USymb -> Ordering
compUSymb (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
a) (UnitDefn -> USymb
forall u. HasUnitSymbol u => u -> USymb
usymb UnitDefn
b)