{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
#if MIN_VERSION_base(4,12,0)
{-# LANGUAGE NoStarIsType #-}
#endif
module Data.TypeNums.Arithmetic.Internal (
AddK
, SubK
, MulK
, IntDivK
, ExpK
, NegK
, Abs
, Negate
, Recip
, Simplify
, Truncate
, Floor
, Ceiling
, Add
, Sub
, Mul
, RatDiv
, DivMod
, QuotRem
, Div
, Mod
, Quot
, Rem
, GCD
, LCM
, Exp
, IntLog
) where
import Data.Type.Bool (If)
import qualified Data.Type.Equality as DTE
import Data.TypeNums.Ints
import Data.TypeNums.Rats.Type
import GHC.TypeLits(Nat)
import qualified GHC.TypeLits as G
type family AddK k1 k2 where
AddK Rat _ = Rat
AddK _ Rat = Rat
AddK Nat Nat = Nat
AddK Nat TInt = TInt
AddK TInt Nat = TInt
AddK TInt TInt = TInt
type family SubK k1 k2 where
SubK Rat _ = Rat
SubK _ Rat = Rat
SubK Nat Nat = Nat
SubK Nat TInt = TInt
SubK TInt Nat = TInt
SubK TInt TInt = TInt
type family MulK k1 k2 where
MulK k k = k
MulK Rat _ = Rat
MulK _ Rat = Rat
MulK Nat TInt = TInt
MulK TInt Nat = TInt
type family IntDivK k where
IntDivK Nat = Nat
IntDivK TInt = TInt
type family ExpK k1 k2 where
ExpK Nat Nat = Nat
ExpK TInt Nat = TInt
ExpK Rat Nat = Rat
ExpK _ TInt = Rat
type family NegK k where
NegK Nat = TInt
NegK TInt = TInt
NegK Rat = Rat
type family Add (x :: k1) (y :: k2) :: AddK k1 k2 where
Add (x :: Nat) (y :: Nat) = x G.+ y
Add ('Pos x) ('Pos y) = 'Pos (x G.+ y)
Add ('Neg x) ('Pos y) = If (x G.<=? y)
('Pos (y G.- x))
('Neg (x G.- y))
Add ('Pos x) ('Neg y) = If (y G.<=? x)
('Pos (x G.- y))
('Neg (y G.- x))
Add ('Neg x) ('Neg y) = 'Neg (x G.+ y)
Add ('Pos x) (y :: Nat) = 'Pos (x G.+ y)
Add ('Neg x) (y :: Nat) = If (x G.<=? y)
('Pos (y G.- x))
('Neg (x G.- y))
Add (x :: Nat) ('Pos y) = 'Pos (x G.+ y)
Add (x :: Nat) ('Neg y) = If (y G.<=? x)
('Pos (x G.- y))
('Neg (y G.- x))
Add (x :: Rat) (y :: Rat) = Simplify (AddRat x y)
Add (x :: Rat) y = Simplify (AddRat x (y ':% 1))
Add x (y :: Rat) = Simplify (AddRat (x ':% 1) y)
type family AddRat (x :: Rat) (y :: Rat) :: Rat where
AddRat (n1 ':% d1) (n2 ':% d2) = (Add (Mul n1 d2) (Mul n2 d1)) ':% (Mul d1 d2)
type family Sub (x :: k1) (y :: k2) :: SubK k1 k2 where
Sub (x :: Nat) (y :: Nat) = x G.- y
Sub ('Pos x) ('Pos y) = Add x ('Neg y)
Sub ('Neg x) ('Pos y) = Add ('Neg x) ('Neg y)
Sub ('Pos x) ('Neg y) = Add ('Pos x) ('Pos y)
Sub ('Neg x) ('Neg y) = Add ('Neg x) ('Pos y)
Sub ('Pos x) (y :: Nat) = Add ('Pos x) ('Neg y)
Sub ('Neg x) (y :: Nat) = Add ('Neg x) ('Neg y)
Sub (x :: Nat) ('Pos y) = Add x ('Neg y)
Sub (x :: Nat) ('Neg y) = Add x ('Pos y)
Sub (x :: Rat) (y :: Rat) = Simplify (SubRat x y)
Sub (x :: Rat) y = Simplify (SubRat x (y ':% 1))
Sub x (y :: Rat) = Simplify (SubRat (x ':% 1) y)
type family SubRat (x :: Rat) (y :: Rat) :: Rat where
SubRat (n1 ':% d1) (n2 ':% d2) = (Sub (Mul n1 ('Pos d2)) (Mul n2 ('Pos d1))) ':% (Mul d1 d2)
type family Mul (x :: k1) (y :: k2) :: MulK k1 k2 where
Mul (x :: Nat) (y :: Nat) = x G.* y
Mul ('Pos x) ('Pos y) = 'Pos (x G.* y)
Mul ('Neg x) ('Pos y) = 'Neg (x G.* y)
Mul ('Pos x) ('Neg y) = 'Neg (x G.* y)
Mul ('Neg x) ('Neg y) = 'Pos (x G.* y)
Mul ('Pos x) (y :: Nat) = 'Pos (x G.* y)
Mul ('Neg x) (y :: Nat) = 'Neg (x G.* y)
Mul (x :: Nat) ('Pos y) = 'Pos (x G.* y)
Mul (x :: Nat) ('Neg y) = 'Neg (x G.* y)
Mul (x :: Rat) (y :: Rat) = Simplify (MulRat x y)
Mul (x :: Rat) y = Simplify (MulRat x (y ':% 1))
Mul x (y :: Rat) = Simplify (MulRat (x ':% 1) y)
type family MulRat (x :: Rat) (y :: Rat) :: Rat where
MulRat (n1 ':% d1) (n2 ':% d2) = (Mul n1 n2) ':% (Mul d1 d2)
type family Recip (x :: k) :: Rat where
Recip (x :: Nat) = 'Pos 1 ':% x
Recip ('Pos x) = 'Pos 1 ':% x
Recip ('Neg x) = 'Neg 1 ':% x
Recip ('Pos x ':% y) = 'Pos y ':% x
Recip ('Neg x ':% y) = 'Neg y ':% x
Recip ((x :: Nat) ':% y) = 'Pos y ':% x
type family RatDiv (x :: k1) (y :: k2) :: Rat where
RatDiv (x :: Nat) (y :: Nat) = Simplify (x ':% y)
RatDiv (x :: TInt) (y :: Nat) = Simplify (x ':% y)
RatDiv (x :: Nat) ('Pos y) = Simplify (x ':% y)
RatDiv (x :: Nat) ('Neg y) = Simplify (('Neg x) ':% y)
RatDiv (x :: TInt) ('Neg y) = Simplify ((Negate x) ':% y)
RatDiv (x :: TInt) ('Pos y) = Simplify (x ':% y)
RatDiv (x :: Rat) (y :: Rat) = Mul x (Recip y)
RatDiv x (y :: Rat) = Mul x (Recip y)
RatDiv (x :: Rat) y = Mul x (RatDiv 1 y)
type family Negate (x :: k) :: NegK k where
Negate (x :: Nat) = Negate ('Pos x)
Negate ('Pos 0) = 'Pos 0
Negate ('Neg 0) = 'Pos 0
Negate ('Pos x) = 'Neg x
Negate ('Neg x) = 'Pos x
Negate (x ':% y) = (Negate x) ':% y
infixl 7 `Div`, `Mod`, `Quot`, `Rem`
type family DivMod (x :: k) (y :: Nat) :: (IntDivK k, IntDivK k) where
DivMod _ 0 = G.TypeError ('G.Text "Divisor must not be 0")
DivMod (x :: Nat) y = UnPos (DivModAux ('Pos x) y ('Pos 0))
DivMod ('Pos x) y = DivModAux ('Pos x) y ('Pos 0)
DivMod ('Neg x) y = DivModNegFixup (DivModAux ('Pos x) y ('Pos 0)) y
type family QuotRem (x :: k) (y :: Nat) :: (IntDivK k, IntDivK k) where
QuotRem ('Neg x) y = QuotRemFixup (DivMod ('Neg x) y) y
QuotRem x y = DivMod x y
type family Div (x :: k) (y :: Nat) :: IntDivK k where
Div x y = Fst (DivMod x y)
type family Mod (x :: k) (y :: Nat) :: IntDivK k where
Mod x y = Snd (DivMod x y)
type family DivModNegFixup (x :: (TInt, TInt)) (y :: Nat) :: (TInt, TInt) where
DivModNegFixup '(a, 'Pos 0) y = '( Negate a, 'Pos 0 )
DivModNegFixup '(a, 'Neg 0) y = '( Negate a, 'Pos 0 )
DivModNegFixup '(a, b) y = '( Negate (Add 1 a), Sub y b )
type family QuotRemFixup (x :: (TInt, TInt)) (y :: Nat) :: (TInt, TInt) where
QuotRemFixup '(d, 'Pos 0) y = '( d, 'Pos 0 )
QuotRemFixup '(d, 'Neg 0) y = '( d, 'Pos 0 )
QuotRemFixup '(d,m) y = '(Add 1 d, Sub m y)
type family Quot (x :: k) (y :: Nat) :: IntDivK k where
Quot x y = Fst (QuotRem x y)
type family Rem (x :: k) (y :: Nat) :: IntDivK k where
Rem x y = Snd (QuotRem x y)
type family DivModAux (x :: TInt) (y :: Nat) (a :: TInt) :: (TInt, TInt) where
DivModAux ('Pos x) y a = DivModAux (Sub ('Pos x) y) y (Add 1 a)
DivModAux ('Neg x) y a = '(Sub a 1, Sub y ('Pos x))
type family UnPos (x :: k1) :: k2 where
UnPos ('Pos x) = x
UnPos '( 'Pos x, 'Pos y) = '(x, y)
type family Fst (x :: (k1, k2)) :: k1 where
Fst '(x,y) = x
type family Snd (x :: (k1, k2)) :: k2 where
Snd '(x,y) = y
type family Abs (x :: k) :: k where
Abs (x :: Nat) = x
Abs ('Pos x) = 'Pos x
Abs ('Neg x) = 'Pos x
Abs (x ':% y) = Simplify (Abs x ':% y)
type family GCD (x :: k1) (y :: k2) :: Nat where
GCD (x :: Nat) (y :: Nat) = GCDAux x y
GCD (x :: Nat) (y :: TInt) = GCDAux x (UnPos (Abs y))
GCD (x :: TInt) (y :: Nat) = GCDAux (UnPos (Abs x)) y
GCD (x :: TInt) (y :: TInt) = GCDAux (UnPos (Abs x)) (UnPos (Abs y))
type family GCDAux (x :: Nat) (y :: Nat) :: Nat where
GCDAux x 0 = x
GCDAux x y = GCDAux y (Rem x y)
type family LCM (x :: k1) (y :: k2) :: Nat where
LCM (x :: Nat) (y :: Nat) = Mul (Div (Abs x) (GCD x y)) (Abs y)
LCM (x :: Nat) (y :: TInt) = UnPos (Mul (Div (Abs x) (GCD x y)) (Abs y))
LCM (x :: TInt) (y :: Nat) = UnPos (Mul (Div (Abs x) (GCD x y)) (Abs y))
LCM (x :: TInt) (y :: TInt) = UnPos (Mul (Div (Abs x) (GCD x y)) (Abs y))
type family Simplify (x :: Rat) :: Rat where
Simplify ((x :: Nat) ':% y) = (Quot ('Pos x) (GCD x y)) ':% (Quot y (GCD x y))
Simplify ((x :: TInt) ':% y) = (Quot x (GCD x y)) ':% (Quot y (GCD x y))
type family Exp (x :: k1) (y :: k2) :: ExpK k1 k2 where
Exp (x :: Nat) (y :: Nat) = x G.^ y
Exp ('Pos x) (y :: Nat) = 'Pos (x G.^ y)
Exp ('Neg x) (y :: Nat) = ExpAux ('Pos 1) ('Neg x) y
Exp (x :: Rat) (y :: Nat) = Simplify (ExpAux (1 ':% 1) x y)
Exp (x :: Rat) ('Pos y) = Simplify (ExpAux (1 ':% 1) x y)
Exp (x :: Rat) ('Neg y) = Simplify (Recip (ExpAux (1 ':% 1) x y))
Exp x ('Pos y) = Simplify (ExpAux (1 ':% 1) (x ':% 1) y)
Exp x ('Neg y) = Simplify (Recip (ExpAux (1 ':% 1) (x ':% 1) y))
type family ExpAux (acc :: k1) (x :: k1) (y :: Nat) :: k1 where
ExpAux acc _ 0 = acc
ExpAux acc x y = ExpAux (Mul x acc) x (Sub y 1)
type family Truncate (x :: k) :: TInt where
Truncate (x :: Nat) = 'Pos x
Truncate (x :: TInt) = x
Truncate (x ':% 0) = G.TypeError ('G.Text ("The denominator must not be 0"))
Truncate ((x :: Nat) ':% y) = 'Pos (Quot x y)
Truncate ((x :: TInt) ':% y) = Quot x y
type family Floor (x :: k) :: TInt where
Floor (x :: Nat) = 'Pos x
Floor (x :: TInt) = x
Floor (x ':% 0) = G.TypeError ('G.Text ("The denominator must not be 0"))
Floor ((x :: Nat) ':% y) = 'Pos (Div x y)
Floor ((x :: TInt) ':% y) = Div x y
type family Ceiling (x :: k) :: TInt where
Ceiling (x :: Nat) = 'Pos x
Ceiling (x :: TInt) = x
Ceiling (x ':% 0) = G.TypeError ('G.Text ("The denominator must not be 0"))
Ceiling (x ':% y) = Add 1 (Floor (Sub x 1 ':% y))
type family IntLog (n :: Nat) (x :: k) :: TInt where
IntLog 0 _ = G.TypeError ('G.Text "Invalid IntLog base: 0")
IntLog 1 _ = G.TypeError ('G.Text "Invalid IntLog base: 1")
IntLog _ 0 = G.TypeError ('G.Text "IntLog n 0 is infinite")
IntLog _ ('Pos 0) = G.TypeError ('G.Text "IntLog n 0 is infinite")
IntLog _ ('Neg 0) = G.TypeError ('G.Text "IntLog n 0 is infinite")
IntLog _ (0 ':% _) = G.TypeError ('G.Text "IntLog n 0 is infinite")
IntLog _ (_ ':% 0) = G.TypeError ('G.Text "IntLog parameter has zero denominator")
IntLog _ ('Neg _) = G.TypeError ('G.Text "IntLog of a negative does not exist")
IntLog n (x :: Nat) = IntLog n ('Pos x)
IntLog n ('Pos x) = IntLogAux n ('Pos x)
IntLog n (x :: Rat) =
If (Floor x DTE.== 'Pos 0)
(NegLogFudge n x (Negate (IntLogAux n (Floor (Recip x)))))
(IntLog n (Floor x))
type family IntLogAux (n :: Nat) (x :: TInt) :: TInt where
IntLogAux n ('Pos 0) = 'Neg 1
IntLogAux n ('Pos 1) = 'Pos 0
IntLogAux n ('Pos x) = Add 1 (IntLogAux n (Div ('Pos x) n))
type family NegLogFudge (n :: Nat) (x :: Rat) (lg :: TInt) where
NegLogFudge n x lg =
If (Simplify (Exp n lg) DTE.== Simplify x)
lg
(Sub lg 1)