module Numeric.Discretized where
import Control.Applicative
import Control.DeepSeq (NFData(..))
import Data.Aeson (FromJSON,ToJSON)
import Data.Binary (Binary)
import Data.Hashable (Hashable)
import Data.Proxy
import Data.Ratio
import Data.Serialize (Serialize)
import Data.Vector.Unboxed.Deriving
import Debug.Trace
import GHC.Generics
import GHC.Real (Ratio(..))
import GHC.TypeLits
import Algebra.Structure.Semiring
import Numeric.Limits
data RatioTy a = RTyExp a | RTyId a | RTyLn a | RTyPlus (RatioTy a) (RatioTy a) | RTyTimes (RatioTy a) (RatioTy a) | Unknown
class RatioTyConstant a where
ratioTyConstant ∷ Proxy a → Ratio Integer
instance (KnownNat k) ⇒ RatioTyConstant (RTyExp (k∷Nat)) where
{-# Inline ratioTyConstant #-}
ratioTyConstant Proxy = let n = natVal @k Proxy in toRational (exp $ fromInteger n)
instance (KnownNat k) ⇒ RatioTyConstant (RTyId (k∷Nat)) where
{-# Inline ratioTyConstant #-}
ratioTyConstant Proxy = let n = natVal @k Proxy in toRational n
instance (KnownNat k) ⇒ RatioTyConstant (RTyLn (k∷Nat)) where
{-# Inline ratioTyConstant #-}
ratioTyConstant Proxy = let n = natVal @k Proxy in toRational (log $ fromInteger n)
instance (RatioTyConstant a, RatioTyConstant b) ⇒ RatioTyConstant (RTyPlus (a∷RatioTy k) (b∷RatioTy k)) where
{-# Inline ratioTyConstant #-}
ratioTyConstant Proxy = ratioTyConstant @a Proxy + ratioTyConstant @b Proxy
instance (RatioTyConstant a, RatioTyConstant b) ⇒ RatioTyConstant (RTyTimes (a∷RatioTy k) (b∷RatioTy k)) where
{-# Inline ratioTyConstant #-}
ratioTyConstant Proxy = ratioTyConstant @a Proxy * ratioTyConstant @b Proxy
newtype Discretized (b ∷ k) = Discretized { getDiscretized ∷ Int }
deriving (Eq,Ord,Generic,Show,Read)
derivingUnbox "Discretized"
[t| forall t . Discretized t → Int |] [| getDiscretized |] [| Discretized |]
instance NFData (Discretized t) where
rnf (Discretized k) = rnf k
{-# Inline rnf #-}
instance Binary (Discretized t)
instance Serialize (Discretized t)
instance FromJSON (Discretized t)
instance ToJSON (Discretized t)
instance Hashable (Discretized t)
instance Num (Discretized Unknown) where
Discretized x + Discretized y = Discretized $ x+y
Discretized x - Discretized y = Discretized $ x-y
(*) = error "Discretized Unknown does not admit (*)"
abs (Discretized x) = Discretized $ abs x
signum (Discretized x) = Discretized $ signum x
fromInteger = error "Discretized Unknown does not admit fromInteger"
{-# Inline (+) #-}
{-# Inline (-) #-}
{-# Inline abs #-}
{-# Inline signum #-}
instance (KnownNat u, KnownNat l) ⇒ Num (Discretized ((u∷Nat) :% (l∷Nat))) where
{-# Inline (+) #-}
Discretized x + Discretized y = Discretized (x+y)
{-# Inline (-) #-}
Discretized x - Discretized y = Discretized (x-y)
{-# Inline (*) #-}
Discretized x * Discretized y =
let u = fromInteger $ natVal @u Proxy
l = fromInteger $ natVal @l Proxy
in Discretized $ (x*y*u) `div` l
{-# Inline abs #-}
abs (Discretized x) = Discretized (abs x)
{-# Inline signum #-}
signum (Discretized x) = Discretized $ signum x
{-# Inline fromInteger #-}
fromInteger x =
let u = fromInteger $ natVal @u Proxy
l = fromInteger $ natVal @l Proxy
in Discretized $ (fromInteger x*u) `div` l
instance Enum (Discretized b) where
toEnum = Discretized
{-# Inline toEnum #-}
fromEnum = getDiscretized
{-# Inline fromEnum #-}
instance (KnownNat u, KnownNat l) ⇒ Fractional (Discretized ((u∷Nat) :% (l∷Nat))) where
{-# Inline (/) #-}
Discretized x / Discretized y =
let u = fromInteger $ natVal @u Proxy
l = fromInteger $ natVal @l Proxy
in Discretized $ (x * l) `div` (y * u)
{-# Inline recip #-}
recip (Discretized x) =
let u = fromInteger $ natVal @u Proxy
l = fromInteger $ natVal @l Proxy
in error "need to find approximately ok transformation"
{-# Inline fromRational #-}
fromRational (a :% b) =
let u = natVal @u Proxy
l = natVal @l Proxy
in Discretized . fromInteger $ (a * l) `div` (b * u)
instance (KnownNat u, KnownNat l) ⇒ Real (Discretized ((u∷Nat) :% (l∷Nat))) where
{-# Inline toRational #-}
toRational (Discretized d) =
let u = natVal @u Proxy
l = natVal @l Proxy
in (fromIntegral d * u) % l
instance (Num (Discretized k)) ⇒ Semiring (Discretized k) where
plus = (+)
times = (*)
zero = 0
one = 1
{-# Inline plus #-}
{-# Inline times #-}
{-# Inline zero #-}
{-# Inline one #-}
instance (NumericLimits (Discretized t)) where
minFinite = Discretized minFinite
{-# Inline minFinite #-}
maxFinite = Discretized maxFinite
{-# Inline maxFinite #-}
discretizeRatio ∷ forall a u l . (Real a, KnownNat u, KnownNat l) ⇒ a → Discretized ((u∷Nat) :% (l∷Nat))
{-# Inline discretizeRatio #-}
discretizeRatio a =
let u = natVal @u Proxy
l = natVal @l Proxy
k = toRational a
in Discretized . fromIntegral $ numerator k * l `div` (denominator k * u)