module Data.Quantities.Convert where
import Data.List (sort)
import qualified Data.Map as M
import Data.Quantities.Data
unityQuant :: Definitions -> Quantity
unityQuant d = Quantity 1 (CompoundUnit d [])
convert :: Quantity -> CompoundUnit -> Either QuantityError Quantity
convert q us
| hq /= hus = Left $ DifferentDefinitionsError (units q) us
| otherwise = convert' (defs us) q us
where hq = defStringHash (defs' q)
hus = defStringHash (defs us)
convertBase :: Quantity -> Quantity
convertBase x = convertBase' (defs' x) x
convert' :: Definitions -> Quantity -> CompoundUnit -> Either QuantityError Quantity
convert' d q us'
| dimq /= dimus = Left $ DimensionalityError (CompoundUnit d dimq) (CompoundUnit d dimus)
| otherwise = Right $ Quantity (mb/mb') us'
where mb = magnitude $ convertBase' d q
mb' = magnitude $ toBase d (sUnits us')
dimq = dimensionality' d (units' q)
dimus = dimensionality' d (sUnits us')
convertBase' :: Definitions -> Quantity -> Quantity
convertBase' d (Quantity m us) = Quantity (m*mb) ub
where (Quantity mb ub) = toBase d (sUnits us)
toBase :: Definitions -> [SimpleUnit] -> Quantity
toBase d = foldr (multiplyQuants . simpleToBase d) (unityQuant d)
simpleToBase :: Definitions -> SimpleUnit -> Quantity
simpleToBase d (SimpleUnit sym pre pow) = Quantity m (CompoundUnit d us)
where (m', u') = bases d M.! sym
us = map (\(SimpleUnit s p pow') -> SimpleUnit s p (pow*pow')) u'
m = (m' * (prefixValues d M.! pre)) ** pow
dimensionality :: Quantity -> CompoundUnit
dimensionality q = CompoundUnit (defs' q) dimUnits
where dimUnits = dimensionality' (defs' q) (units' q)
dimensionality' :: Definitions -> [SimpleUnit] -> [SimpleUnit]
dimensionality' d us = sort $ map dim ub
where ub = units' $ toBase d us
dim (SimpleUnit sym _ pow) = SimpleUnit (unitTypes d M.! sym) "" pow
addQuants :: Quantity -> Quantity -> Either QuantityError Quantity
addQuants = linearQuants (+)
subtractQuants :: Quantity -> Quantity -> Either QuantityError Quantity
subtractQuants = linearQuants ()
linearQuants :: (Double -> Double -> Double) -> Quantity -> Quantity
-> Either QuantityError Quantity
linearQuants f (Quantity m1 u1) q2 = case q of
(Right q') -> Right $ Quantity (f m1 (magnitude q')) u1
(Left err) -> Left err
where q = convert q2 u1