{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Numeric.Units.Dimensional.FixedPoint
(
Dimensional,
Unit, Quantity, SQuantity,
Metricality(..),
Dimension (Dim),
type (*), type (/), type (^), NRoot, Recip,
Dimension' (Dim'), HasDimension(..), KnownDimension,
(*~), (/~),
(*), (/), (+), (-),
negate, abs,
expD, logD, sinD, cosD, tanD, asinD, acosD, atanD, sinhD, coshD, tanhD, asinhD, acoshD, atanhD, atan2D,
expVia, logVia, sinVia, cosVia, tanVia, asinVia, acosVia, atanVia, sinhVia, coshVia, tanhVia, asinhVia, acoshVia, atanhVia, atan2Via,
(*~~), (/~~), sum, mean,
rescale, rescaleFinite, rescaleD, rescaleVia, KnownVariant(dmap), changeRep, changeRepRound, changeRepApproximate,
DOne, DLength, DMass, DTime, DElectricCurrent, DThermodynamicTemperature, DAmountOfSubstance, DLuminousIntensity,
Dimensionless, Length, Mass, Time, ElectricCurrent, ThermodynamicTemperature, AmountOfSubstance, LuminousIntensity,
_0, epsilon,
_1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau,
siUnit, one, mkUnitR, mkUnitQ, mkUnitZ,
name, exactValue, weaken, strengthen, exactify,
type Q, type QScale, type Angle8, type Angle16, type Angle32
)
where
import Data.Bits
import Data.ExactPi
import qualified Data.ExactPi.TypeLevel as E
import Data.Int
import Data.Proxy
import qualified Data.Foldable as F
import Data.Ratio
import qualified GHC.TypeLits as N
import Numeric.Units.Dimensional.Coercion
import Numeric.Units.Dimensional.Internal
import Numeric.Units.Dimensional.Prelude hiding ((*~), (/~), (+), (-), recip, negate, abs, (*~~), (/~~), sum, mean, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau, changeRep)
import Numeric.Units.Dimensional.Variants hiding (type (*), type (/))
import qualified Numeric.Units.Dimensional.UnitNames as Name
import qualified Prelude as P
infixl 6 +, -
(+) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a
(+) = liftQ2 (P.+)
(-) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a
(-) = liftQ2 (P.-)
abs :: (Num a) => SQuantity s d a -> SQuantity s d a
abs = liftQ (P.abs)
negate :: (Num a) => SQuantity s d a -> SQuantity s d a
negate = liftQ (P.negate)
infixl 7 *~~, /~~
(*~~) :: (Functor f, RealFrac a, Integral b, E.MinCtxt s a) => f a -> Unit m d a -> f (SQuantity s d b)
xs *~~ u = fmap (*~ u) xs
(/~~) :: (Functor f, Real a, Fractional b, E.MinCtxt s b) => f (SQuantity s d a) -> Unit m d b -> f b
xs /~~ u = fmap (/~ u) xs
sum :: (Num a, F.Foldable f) => f (SQuantity s d a) -> SQuantity s d a
sum = F.foldr (+) _0
mean :: (Fractional a, F.Foldable f) => f (SQuantity s d a) -> SQuantity s d a
mean = reduce . F.foldr accumulate (_0, 0 :: Int)
where
reduce (s, n) = dmap (P./ fromIntegral n) s
accumulate val (accum, count) = (accum + val, count P.+ 1)
expD, logD, sinD, cosD, tanD, asinD, acosD, atanD, sinhD, coshD, tanhD, asinhD, acoshD, atanhD
:: (Integral a, Integral b, E.MinCtxt s1 Double, E.MinCtxt s2 Double) => SQuantity s1 DOne a -> SQuantity s2 DOne b
expD = expVia (Proxy :: Proxy P.Double)
logD = logVia (Proxy :: Proxy P.Double)
sinD = sinVia (Proxy :: Proxy P.Double)
cosD = cosVia (Proxy :: Proxy P.Double)
tanD = tanVia (Proxy :: Proxy P.Double)
asinD = asinVia (Proxy :: Proxy P.Double)
acosD = acosVia (Proxy :: Proxy P.Double)
atanD = atanVia (Proxy :: Proxy P.Double)
sinhD = sinhVia (Proxy :: Proxy P.Double)
coshD = coshVia (Proxy :: Proxy P.Double)
tanhD = tanhVia (Proxy :: Proxy P.Double)
asinhD = asinhVia (Proxy :: Proxy P.Double)
acoshD = acoshVia (Proxy :: Proxy P.Double)
atanhD = atanhVia (Proxy :: Proxy P.Double)
atan2D :: (Integral a, Integral b, E.MinCtxt s1 Double, E.MinCtxt s2 Double, E.MinCtxt s3 Double) => SQuantity s1 DOne a -> SQuantity s2 DOne a -> SQuantity s3 DOne b
atan2D = atan2Via (Proxy :: Proxy P.Double)
expVia, logVia, sinVia, cosVia, tanVia, asinVia, acosVia, atanVia, sinhVia, coshVia, tanhVia, asinhVia, acoshVia, atanhVia
:: (Integral a, RealFrac b, Floating b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c
expVia = liftDimensionlessVia P.exp
logVia = liftDimensionlessVia P.log
sinVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.sin
cosVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.cos
tanVia = liftDimensionlessPeriodicVia P.pi P.tan
asinVia = liftDimensionlessVia P.asin
acosVia = liftDimensionlessVia P.acos
atanVia = liftDimensionlessVia P.atan
sinhVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.sinh
coshVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.cosh
tanhVia = liftDimensionlessPeriodicVia P.pi P.tanh
asinhVia = liftDimensionlessVia P.asinh
acoshVia = liftDimensionlessVia P.acosh
atanhVia = liftDimensionlessVia P.atanh
atan2Via :: forall s1 s2 s3 a b c d.(Integral a, RealFloat b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b, E.MinCtxt s3 b, KnownDimension d) => Proxy b -> SQuantity s1 d a -> SQuantity s2 d a -> SQuantity s3 DOne c
atan2Via _ y x = (*~ siUnit) $ (P.atan2 :: b -> b -> b) (y /~ siUnit) (x /~ siUnit)
liftDimensionlessVia :: forall s1 s2 a b c.(Real a, RealFrac b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => (b -> b) -> Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c
liftDimensionlessVia f _ = (*~ siUnit) . (f :: b -> b) . (/~ siUnit)
liftDimensionlessPeriodicVia :: forall s1 s2 a b c.(Integral a, RealFrac b, Floating b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => ExactPi -> (forall d.Floating d => d -> d) -> Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c
liftDimensionlessPeriodicVia p f proxy | Just p'' <- p', p'' /= 0 = (liftDimensionlessVia f proxy) . dmap (`mod` p'')
| otherwise = liftDimensionlessVia f proxy
where
p' :: Maybe a
p' = fmap fromInteger . toExactInteger . P.recip . (P./ p) . E.exactPiVal $ (Proxy :: Proxy s1)
infixl 7 *~, /~
(*~) :: forall s m d a b.(RealFrac a, Integral b, E.MinCtxt s a) => a -> Unit m d a -> SQuantity s d b
x *~ (Unit _ _ y) = Quantity . round $ (x P.* y P./ s)
where
s = E.injMin (Proxy :: Proxy s)
(/~) :: forall s m d a b.(Real a, Fractional b, E.MinCtxt s b) => SQuantity s d a -> Unit m d b -> b
(Quantity x) /~ (Unit _ _ y) = ((realToFrac x) P.* s P./ y)
where
s = E.injMin (Proxy :: Proxy s)
rescale :: forall a b d s1 s2.(Integral a, Integral b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b
rescale | Just s' <- toExactInteger s = viaInteger (P.* s')
| Just s' <- toExactInteger (P.recip s) = viaInteger (`P.quot` s')
| Just q <- toExactRational s = viaInteger $ timesRational q
| otherwise = viaInteger $ \x -> fixedPoint (fmap (($ x) . timesRational) (rationalApproximations s))
where
s = (s1' P./ s2')
s1' = E.exactPiVal (Proxy :: Proxy s1)
s2' = E.exactPiVal (Proxy :: Proxy s2)
timesRational :: Rational -> Integer -> Integer
timesRational q = (`P.quot` denominator q) . (P.* numerator q)
rescaleFinite :: (Integral a, FiniteBits a, Integral b, FiniteBits b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b
rescaleFinite = rescale
rescaleVia :: forall a b c d s1 s2.(Integral a, RealFrac b, Floating b, Integral c, E.KnownExactPi s1, E.KnownExactPi s2) => Proxy b -> SQuantity s1 d a -> SQuantity s2 d c
rescaleVia _ = viaIntermediate (P.* s)
where
s = approximateValue (s1' P./ s2') :: b
s1' = E.exactPiVal $ (Proxy :: Proxy s1)
s2' = E.exactPiVal $ (Proxy :: Proxy s2)
rescaleD :: (Integral a, Integral b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b
rescaleD = rescaleVia (Proxy :: Proxy Double)
viaInteger :: (Integral a, Integral b) => (P.Integer -> P.Integer) -> SQuantity s1 d a -> SQuantity s2 d b
viaInteger f = Quantity . fromInteger . f . fromIntegral . unQuantity
viaIntermediate :: (Integral a, RealFrac b, Integral c) => (b -> b) -> SQuantity s1 d a -> SQuantity s2 d c
viaIntermediate f = Quantity . round . f . fromIntegral . unQuantity
fixedPoint :: (Eq a) => [a] -> a
fixedPoint [] = error "Fixed point of empty list."
fixedPoint [x] = x
fixedPoint (x1:x2:xs) | x1 == x2 = x1
| otherwise = fixedPoint (x2:xs)
changeRep :: forall v1 v2 d a b.
(KnownVariant v1, KnownVariant v2,
CompatibleVariants v1 v2,
E.MinCtxt (ScaleFactor v1 E./ ScaleFactor v2) b,
Real a, Fractional b)
=> Dimensional v1 d a -> Dimensional v2 d b
changeRep = liftD (P.* s) ((P.* s') . realToFrac) Name.weaken
where
p :: Proxy (ScaleFactor v1 E./ ScaleFactor v2)
p = Proxy
s = E.exactPiVal p
s' = E.injMin p
changeRepRound :: forall v1 v2 d a b.
(KnownVariant v1, KnownVariant v2,
CompatibleVariants v1 v2,
E.MinCtxt (ScaleFactor v1 E./ ScaleFactor v2) a,
RealFrac a, Integral b)
=> Dimensional v1 d a -> Dimensional v2 d b
changeRepRound = liftD (P.* s) (round . (P.* s')) Name.weaken
where
p :: Proxy (ScaleFactor v1 E./ ScaleFactor v2)
p = Proxy
s = E.exactPiVal p
s' = E.injMin p
_0 :: Num a => SQuantity s d a
_0 = Quantity 0
_1, _2, _3, _4, _5, _6, _7, _8, _9 :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a
_1 = rescale (epsilon :: SQuantity E.One DOne Integer)
_2 = rescale (epsilon :: SQuantity (E.ExactNatural 2) DOne Integer)
_3 = rescale (epsilon :: SQuantity (E.ExactNatural 3) DOne Integer)
_4 = rescale (epsilon :: SQuantity (E.ExactNatural 4) DOne Integer)
_5 = rescale (epsilon :: SQuantity (E.ExactNatural 5) DOne Integer)
_6 = rescale (epsilon :: SQuantity (E.ExactNatural 6) DOne Integer)
_7 = rescale (epsilon :: SQuantity (E.ExactNatural 7) DOne Integer)
_8 = rescale (epsilon :: SQuantity (E.ExactNatural 8) DOne Integer)
_9 = rescale (epsilon :: SQuantity (E.ExactNatural 9) DOne Integer)
pi :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a
pi = rescale (epsilon :: SQuantity E.Pi DOne Integer)
tau :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a
tau = rescale (epsilon :: SQuantity (E.ExactNatural 2 E.* E.Pi) DOne Integer)
epsilon :: (Integral a) => SQuantity s d a
epsilon = Quantity 1
type QScale n = (E.One E./ (E.ExactNatural (2 N.^ n)))
type Q n a = SQuantity (QScale n) DOne a
type Angle8 = SQuantity (E.Pi E.* (QScale 7)) DPlaneAngle Int8
type Angle16 = SQuantity (E.Pi E.* (QScale 15)) DPlaneAngle Int16
type Angle32 = SQuantity (E.Pi E.* (QScale 31)) DPlaneAngle Int32