module Numeric.Field.Fraction
( Fraction
, numerator
, denominator
, Ratio
, (%)
) 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.Algebra.Unital.UnitNormalForm
import Numeric.Decidable.Associates
import Numeric.Decidable.Units
import Numeric.Decidable.Zero
import Numeric.Domain.Euclidean
import Numeric.Domain.GCD
import Numeric.Domain.Integral
import Numeric.Domain.PID
import Numeric.Domain.UFD
import Numeric.Natural
import Numeric.Rig.Characteristic
import Numeric.Rig.Class
import Numeric.Ring.Class
import Numeric.Semiring.ZeroProduct
import Prelude hiding (Integral (..), Num (..), gcd, lcm)
data Fraction d = Fraction !d !d
type Ratio = Fraction
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 %
(%) :: (GCDDomain d) => d -> d -> Fraction d
a % b | isZero b = error "Divide by zero"
| otherwise = let (ua, a') = splitUnit a
(ub, b') = splitUnit b
Just ub' = recipUnit ub
(a'',b'') = reduceFraction a' b' in
Fraction (ua * ub' * a'') (b'')
numerator :: Fraction t -> t
numerator (Fraction q _) = q
denominator :: Fraction t -> t
denominator (Fraction _ p) = p
instance (GCDDomain d) => ZeroProductSemiring (Fraction d)
instance (Eq d, GCDDomain d) => Eq (Fraction d) where
Fraction p q == Fraction s t = p*t == q*s
instance (Ord d, GCDDomain d) => Ord (Fraction d) where
compare (Fraction p q) (Fraction p' q') = compare (p*q') (p'*q)
instance (GCDDomain d) => Division (Fraction d) where
recip (Fraction p q)
| isZero p = error "Divide by zero"
| otherwise = let (recipUnit -> Just u, p') = splitUnit p in
Fraction (q * u) p'
Fraction p q / Fraction s t = (p*t) % (q*s)
instance (GCDDomain d) => Commutative (Fraction d)
instance (GCDDomain d) => DecidableZero (Fraction d) where
isZero (Fraction p _) = isZero p
instance (GCDDomain d) => DecidableUnits (Fraction d) where
isUnit (Fraction p _) = not $ isZero p
recipUnit (Fraction p q) | isZero p = Nothing
| otherwise = Just (Fraction q p)
instance (GCDDomain d) => DecidableAssociates (Fraction d) where
isAssociate a b = not (isZero a || isZero b)
instance (GCDDomain d) => Ring (Fraction d)
instance (GCDDomain d) => Abelian (Fraction d)
instance (GCDDomain d) => Semiring (Fraction d)
instance (GCDDomain 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 (GCDDomain d) => Monoidal (Fraction d) where
zero = Fraction zero one
instance (GCDDomain d) => LeftModule Integer (Fraction d) where
n .* Fraction p r = (n .* p) % r
instance (GCDDomain d) => RightModule Integer (Fraction d) where
Fraction p r *. n = (p *. n) % r
instance (GCDDomain d) => LeftModule Natural (Fraction d) where
n .* Fraction p r = (n .* p) % r
instance (GCDDomain d) => RightModule Natural (Fraction d) where
Fraction p r *. n = (p *. n) % r
instance (GCDDomain d) => Additive (Fraction d) where
Fraction p q + Fraction s t =
let n = p*t + s*q
d = q*t
(n',d') = reduceFraction n d
in Fraction n' d'
instance (GCDDomain d) => Unital (Fraction d) where
one = Fraction one one
instance (GCDDomain d) => Multiplicative (Fraction d) where
Fraction p q * Fraction s t = (p*s) % (q*t)
instance (GCDDomain d) => Rig (Fraction d)
instance (Characteristic d, GCDDomain d) => Characteristic (Fraction d) where
char _ = char (Proxy :: Proxy d)
instance (GCDDomain d) => UnitNormalForm (Fraction d)
instance (GCDDomain d) => IntegralDomain (Fraction d)
instance (GCDDomain d) => GCDDomain (Fraction d)
instance (GCDDomain d) => UFD (Fraction d)
instance (GCDDomain d) => PID (Fraction d)
instance (GCDDomain d) => Euclidean (Fraction d)