module Numeric.Field.Fraction
( Fraction
, numerator
, denominator
, Ratio
, (%)
, lcm
) where
import Data.Proxy
import Numeric.Additive.Class
import Numeric.Additive.Group
import Numeric.Algebra.Class
import Numeric.Algebra.Commutative
import Numeric.Algebra.Division
import Numeric.Algebra.Unital
import Numeric.Decidable.Units
import Numeric.Decidable.Zero
import Numeric.Domain.Euclidean
import Numeric.Natural
import Numeric.Rig.Characteristic
import Numeric.Rig.Class
import Numeric.Ring.Class
import Numeric.Semiring.Integral
import Prelude hiding (Integral (..), Num (..), gcd, lcm)
data Fraction d = Fraction !d !d
type Ratio = Fraction
lcm :: Euclidean r => r -> r -> r
lcm p q = p * q `quot` gcd p q
instance (Eq d, Show d, Unital d) => Show (Fraction d) where
showsPrec d (Fraction p q)
| q == one = showsPrec d p
| otherwise = showParen (d > 5) $ showsPrec 6 p . showString " / " . showsPrec 6 q
infixl 7 %
(%) :: Euclidean d => d -> d -> Fraction d
a % b = let (ua, a') = splitUnit a
(ub, b') = splitUnit b
Just ub' = recipUnit ub
r = gcd a' b'
in Fraction (ua * ub' * a' `quot` r) (b' `quot` r)
numerator :: Fraction t -> t
numerator (Fraction q _) = q
denominator :: Fraction t -> t
denominator (Fraction _ p) = p
instance Euclidean d => IntegralSemiring (Fraction d)
instance (Eq d, Multiplicative d) => Eq (Fraction d) where
Fraction p q == Fraction s t = p*t == q*s
instance (Ord d, Multiplicative d) => Ord (Fraction d) where
compare (Fraction p q) (Fraction p' q') = compare (p*q') (p'*q)
instance Euclidean d => Division (Fraction d) where
recip (Fraction p q) | isZero p = error "Ratio has zero denominator!"
| otherwise = let (recipUnit -> Just u, p') = splitUnit p
in Fraction (q * u) p'
Fraction p q / Fraction s t = (p*t) % (q*s)
instance (Commutative d, Euclidean d) => Commutative (Fraction d)
instance Euclidean d => DecidableZero (Fraction d) where
isZero (Fraction p _) = isZero p
instance Euclidean d => DecidableUnits (Fraction d) where
isUnit (Fraction p _) = not $ isZero p
recipUnit (Fraction p q) | isZero p = Nothing
| otherwise = Just (Fraction q p)
instance Euclidean d => Ring (Fraction d)
instance Euclidean d => Abelian (Fraction d)
instance Euclidean d => Semiring (Fraction d)
instance Euclidean d => Group (Fraction d) where
negate (Fraction p q) = Fraction (negate p) q
Fraction p q Fraction p' q' = (p*q'p'*q) % (q*q')
instance Euclidean d => Monoidal (Fraction d) where
zero = Fraction zero one
instance Euclidean d => LeftModule Integer (Fraction d) where
n .* Fraction p r = (n .* p) % r
instance Euclidean d => RightModule Integer (Fraction d) where
Fraction p r *. n = (p *. n) % r
instance Euclidean d => LeftModule Natural (Fraction d) where
n .* Fraction p r = (n .* p) % r
instance Euclidean d => RightModule Natural (Fraction d) where
Fraction p r *. n = (p *. n) % r
instance Euclidean d => Additive (Fraction d) where
Fraction p q + Fraction s t =
let u = gcd q t
in Fraction (p * t `quot` u + s*q`quot`u) (q*t`quot`u)
instance Euclidean d => Unital (Fraction d) where
one = Fraction one one
instance Euclidean d => Multiplicative (Fraction d) where
Fraction p q * Fraction s t = (p*s) % (q*t)
instance Euclidean d => Rig (Fraction d)
instance (Characteristic d, Euclidean d) => Characteristic (Fraction d) where
char _ = char (Proxy :: Proxy d)