{-# LANGUAGE MultiParamTypeClasses #-} module Algebra.Morphism.Ratio where import Algebra.Classes import Prelude (Ord(..), Eq(..),Integer,Show(..), error, otherwise, (.), Int, ($)) import Text.Show (showParen, showString) import qualified Data.Ratio ------------------------------------------------------------------------ -- Divide by zero and arithmetic overflow ------------------------------------------------------------------------ -- We put them here because they are needed relatively early -- in the libraries before the Exception type has been defined yet. {-# NOINLINE divZeroError #-} divZeroError :: a divZeroError = error "division by zero" {-# NOINLINE ratioZeroDenominatorError #-} ratioZeroDenominatorError :: a ratioZeroDenominatorError = error "ratioZeroDenomException" {-# NOINLINE overflowError #-} overflowError :: a overflowError = error "overflowException" {-# NOINLINE underflowError #-} underflowError :: a underflowError = error "underflowException" data Ratio a = !a :% !a deriving Eq -- ^ @since 2.01 type Rational = Ratio Integer -------------------------------------------------------------- -- Instances for @Ratio@ -------------------------------------------------------------- -- | @since 2.0.1 instance (Integral a) => Ord (Ratio a) where {-# SPECIALIZE instance Ord Rational #-} (x:%y) <= (x':%y') = x * y' <= x' * y (x:%y) < (x':%y') = x * y' < x' * y -- | @since 2.0.1 instance EuclideanDomain a => Additive (Ratio a) where zero = zero :% one (x:%y) + (x':%y') = reduce (x*y' + x'*y) (y*y') instance EuclideanDomain a => Multiplicative (Ratio a) where one = one :% one (x:%y) * (x':%y') = reduce (x * x') (y * y') instance EuclideanDomain a => Group (Ratio a) where (x:%y) - (x':%y') = reduce (x*y' - x'*y) (y*y') negate (x:%y) = (negate x) :% y -- abs (x:%y) = abs x :% y -- signum (x:%_) = signum x :% 1 -- fromInteger x = fromInteger x :% 1 instance EuclideanDomain a => AbelianAdditive (Ratio a) instance EuclideanDomain a => Ring (Ratio a) instance EuclideanDomain a => Scalable (Ratio a) (Ratio a) where (*^) = (*) -- | @since 2.0.1 instance (EuclideanDomain a) => Division (Ratio a) where {-# SPECIALIZE instance Division Rational #-} (x:%y) / (x':%y') = (x*y') % (y*x') -- recip (x:%y) -- | isZero x = ratioZeroDenominatorError -- | x < 0 = negate y :% negate x -- | otherwise = y :% x instance EuclideanDomain a => Field (Ratio a) where fromRational x = fromInteger (Data.Ratio.numerator x) % fromInteger (Data.Ratio.denominator x) -- | @since 2.0.1 -- instance (Integral a) => Real (Ratio a) where -- {-# SPECIALIZE instance Real Rational #-} -- toRational (x:%y) = toInteger x :% toInteger y -- -- | @since 2.0.1 -- instance (Integral a) => RealFrac (Ratio a) where -- {-# SPECIALIZE instance RealFrac Rational #-} -- properFraction (x:%y) = (fromInteger (toInteger q), r:%y) -- where (q,r) = quotRem x y -- round r = -- let -- (n, f) = properFraction r -- x = if r < 0 then -1 else 1 -- in -- case (compare (abs f) 0.5, odd n) of -- (LT, _) -> n -- (EQ, False) -> n -- (EQ, True) -> n + x -- (GT, _) -> n + x -- | @since 2.0.1 instance (Show a) => Show (Ratio a) where {-# SPECIALIZE instance Show Rational #-} showsPrec p (x:%y) = showParen (p > ratioPrec) $ showsPrec ratioPrec1 x . showString " % " . showsPrec ratioPrec1 y -- | 'reduce' is a subsidiary function used only in this module. -- It normalises a ratio by dividing both numerator and denominator by -- their greatest common divisor. reduce :: (EuclideanDomain a) => a -> a -> Ratio a {-# SPECIALISE reduce :: Integer -> Integer -> Rational #-} reduce x y | isZero y = ratioZeroDenominatorError | otherwise = (x `quot` d) :% (y `quot` d) where d = gcd x y (%) :: EuclideanDomain a => a -> a -> Ratio a x % y = reduce (x * sign) a where (a,sign) = normalize y ratioPrec, ratioPrec1 :: Int ratioPrec = 7 -- Precedence of ':%' constructor ratioPrec1 = ratioPrec + 1