idris2-ratio/src/Data/Ratio.idr
2022-09-03 19:39:56 -04:00

91 lines
2 KiB
Idris

module Data.Ratio
infix 10 //
export
record Ratio a where
constructor MkRatio
nm, dn : a
public export
Rational : Type
Rational = Ratio Integer
gcd : (Eq a, Integral a) => a -> a -> a
gcd x y =
if x == 0 then y
else if y == 0 then x
else gcd y (x `mod` y)
export
reduce : (Eq a, Integral a) => Ratio a -> Ratio a
reduce (MkRatio n d) =
let g = gcd n d in MkRatio (n `div` g) (d `div` g)
export partial
(//) : (Eq a, Integral a) => a -> a -> Ratio a
n // d = case d /= 0 of
True => reduce $ MkRatio n d
namespace Ratio
export %inline
numer : Ratio a -> a
numer = nm
export %inline
(.numer) : Ratio a -> a
(.numer) = nm
export %inline
denom : Ratio a -> a
denom = dn
export %inline
(.denom) : Ratio a -> a
(.denom) = dn
export
Eq a => Eq (Ratio a) where
MkRatio n d == MkRatio m b = n == m && d == b
export
(Ord a, Num a) => Ord (Ratio a) where
compare (MkRatio n d) (MkRatio m b) =
flipIfNeg (b*d) $ compare (n*b) (m*d)
where
flipIfNeg : a -> Ordering -> Ordering
flipIfNeg x EQ = EQ
flipIfNeg x LT = if x >= 0 then LT else GT
flipIfNeg x GT = if x >= 0 then GT else LT
export
Show a => Show (Ratio a) where
showPrec p (MkRatio n d) =
let p' = User 10
in showParens (p >= p') (showPrec p' n ++ " // " ++ showPrec p' d)
export
(Eq a, Integral a) => Num (Ratio a) where
MkRatio n d + MkRatio m b = reduce $ MkRatio (n*b + m*d) (d*b)
MkRatio n d * MkRatio m b = reduce $ MkRatio (n*m) (d*b)
fromInteger x = MkRatio (fromInteger x) 1
export
(Eq a, Integral a, Neg a) => Neg (Ratio a) where
negate (MkRatio n d) = MkRatio (-n) d
MkRatio n d - MkRatio m b = reduce $ MkRatio (n*b - m*d) (d*b)
export
(Eq a, Integral a, Abs a) => Abs (Ratio a) where
abs (MkRatio n d) = MkRatio (abs n) (abs d)
export
(Eq a, Integral a) => Fractional (Ratio a) where
recip (MkRatio n d) = case n /= 0 of True => MkRatio d n
MkRatio n d / MkRatio m b = case m /= 0 of
True => reduce $ MkRatio (n*b) (m*d)