{-# LANGUAGE MultiParamTypeClasses, NoImplicitPrelude, RebindableSyntax #-} {-# LANGUAGE ScopedTypeVariables, ViewPatterns #-} 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) -- | Fraction field @k(D)@ of 'GCDDomain' domain @D@. data Fraction d = Fraction !d !d -- Invariants: r == Fraction p q -- ==> leadingUnit q == one && q /= 0 -- && isUnit (gcd p q) -- | Convenient synonym for 'Fraction'. 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 {-# INLINE numerator #-} denominator :: Fraction t -> t denominator (Fraction _ p) = p {-# INLINE denominator #-} 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 {-# INLINE (==) #-} instance (Ord d, GCDDomain d) => Ord (Fraction d) where compare (Fraction p q) (Fraction p' q') = compare (p*q') (p'*q) {-# INLINE compare #-} 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) {-# INLINE recip #-} {-# INLINE (/) #-} instance (GCDDomain d) => Commutative (Fraction d) instance (GCDDomain d) => DecidableZero (Fraction d) where isZero (Fraction p _) = isZero p {-# INLINE isZero #-} instance (GCDDomain d) => DecidableUnits (Fraction d) where isUnit (Fraction p _) = not $ isZero p {-# INLINE isUnit #-} recipUnit (Fraction p q) | isZero p = Nothing | otherwise = Just (Fraction q p) {-# INLINE recipUnit #-} 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 {-# INLINE zero #-} instance (GCDDomain d) => LeftModule Integer (Fraction d) where n .* Fraction p r = (n .* p) % r {-# INLINE (.*) #-} instance (GCDDomain d) => RightModule Integer (Fraction d) where Fraction p r *. n = (p *. n) % r {-# INLINE (*.) #-} instance (GCDDomain d) => LeftModule Natural (Fraction d) where n .* Fraction p r = (n .* p) % r {-# INLINE (.*) #-} instance (GCDDomain d) => RightModule Natural (Fraction d) where Fraction p r *. n = (p *. n) % r {-# INLINE (*.) #-} 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' {-# INLINE (+) #-} instance (GCDDomain d) => Unital (Fraction d) where one = Fraction one one {-# INLINE 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)