{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
#if ( __GLASGOW_HASKELL__ >= 806 )
{-# LANGUAGE NoStarIsType #-}
#endif
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Time.Rational
( Rat (..)
, type (:%)
, type (%)
, type (*)
, type (/)
, MulK
, DivK
, Gcd
, Normalize
, DivRat
, type (>=%)
, RatioNat
, KnownRat (..)
, withRuntimeDivRat
, KnownDivRat
) where
import Data.Kind (Type)
import Data.Proxy (Proxy (..))
import GHC.Natural (Natural)
import GHC.Real (Ratio ((:%)))
import GHC.TypeNats (Div, Mod, type (<=?), KnownNat, Nat, natVal)
import qualified GHC.TypeNats
import Unsafe.Coerce (unsafeCoerce)
data Rat = Nat ::% Nat
type family MulK (k1 :: Type) (k2 :: Type) :: Type
type instance MulK Nat Nat = Nat
type instance MulK Rat Rat = Rat
type instance MulK Rat Nat = Rat
type instance MulK Nat Rat = Rat
type family DivK (k1 :: Type) (k2 :: Type) :: Type
type instance DivK Nat Nat = Rat
type instance DivK Rat Rat = Rat
type instance DivK Rat Nat = Rat
type instance DivK Nat Rat = Rat
type family (*) (a :: k1) (b :: k2) :: MulK k1 k2
type instance (a :: Nat) * (b :: Nat) = (GHC.TypeNats.*) a b
type instance (a :: Rat) * (b :: Rat) = MulRat a b
type instance (a :: Rat) * (b :: Nat) = MulNatRat b a
type instance (a :: Nat) * (b :: Rat) = MulNatRat a b
type family (/) (a :: k1) (b :: k2) :: DivK k1 k2
type instance (a :: Nat) / (b :: Nat) = a % b
type instance (a :: Rat) / (b :: Rat) = DivRat a b
type instance (a :: Rat) / (b :: Nat) = DivRatNat a b
type instance (a :: Nat) / (b :: Rat) = DivRat (a :% 1) b
type (:%) = '(::%)
type family (m :: Nat) % (n :: Nat) :: Rat where
a % b = Normalize (a :% b)
infixl 7 %
type family DivRat (m :: Rat) (n :: Rat) :: Rat where
DivRat (a :% b) (c :% d) = (a * d) % (b * c)
type family MulRat (m :: Rat) (n :: Rat) :: Rat where
MulRat (a :% b) (c :% d) = (a * c) % (b * d)
type family MulNatRat (n :: Nat) (r :: Rat) :: Rat where
MulNatRat x (a :% b) = (x * a) % b
type family DivRatNat (r :: Rat) (n :: Nat) :: Rat where
DivRatNat (a :% b) x = a % (b * x)
type family Gcd (m :: Nat) (n :: Nat) :: Nat where
Gcd a 0 = a
Gcd a b = Gcd b (a `Mod` b)
type family Normalize (r :: Rat) :: Rat where
Normalize (a :% b) = (a `Div` Gcd a b) :% (b `Div` Gcd a b)
infix 4 >=%
type family (m :: Rat) >=% (n :: Rat) :: Bool where
(a :% b) >=% (c :% d) = c * b <=? a * d
type RatioNat = Ratio Natural
class KnownRat (r :: Rat) where
ratVal :: RatioNat
instance (KnownNat a, KnownNat b) => KnownRat (a :% b) where
ratVal :: RatioNat
ratVal = Proxy a -> Natural
forall (n :: Nat) (proxy :: Nat -> Type).
KnownNat n =>
proxy n -> Natural
natVal (Proxy a
forall k (t :: k). Proxy t
Proxy @a) Natural -> Natural -> RatioNat
forall a. a -> a -> Ratio a
:% Proxy b -> Natural
forall (n :: Nat) (proxy :: Nat -> Type).
KnownNat n =>
proxy n -> Natural
natVal (Proxy b
forall k (t :: k). Proxy t
Proxy @b)
newtype KnownRatDict (unit :: Rat) r = MkKnownRatDict (KnownRat unit => r)
giftRat :: forall (unit :: Rat) r . (KnownRat unit => r) -> RatioNat -> r
giftRat :: (KnownRat unit => r) -> RatioNat -> r
giftRat KnownRat unit => r
given = KnownRatDict unit r -> RatioNat -> r
forall a b. a -> b
unsafeCoerce ((KnownRat unit => r) -> KnownRatDict unit r
forall (unit :: Rat) r. (KnownRat unit => r) -> KnownRatDict unit r
MkKnownRatDict KnownRat unit => r
given :: KnownRatDict unit r)
{-# INLINE giftRat #-}
withRuntimeDivRat :: forall (a :: Rat) (b :: Rat) r . (KnownRat a, KnownRat b) => (KnownRat (a / b) => r) -> r
withRuntimeDivRat :: (KnownRat (a / b) => r) -> r
withRuntimeDivRat KnownRat (a / b) => r
r = (KnownRat (a / b) => r) -> RatioNat -> r
forall (unit :: Rat) r. (KnownRat unit => r) -> RatioNat -> r
giftRat @(a / b) KnownRat (a / b) => r
r (KnownRat a => RatioNat
forall (r :: Rat). KnownRat r => RatioNat
ratVal @a RatioNat -> RatioNat -> RatioNat
forall a. Fractional a => a -> a -> a
/ KnownRat b => RatioNat
forall (r :: Rat). KnownRat r => RatioNat
ratVal @b)
{-# INLINE withRuntimeDivRat #-}
type KnownDivRat a b = ( KnownRat a
, KnownRat b
, KnownRat (a / b)
)