ref: cf20fb934e9af13b64c2365cbf95d974f30b5c9d
dir: /lib/Data/Ratio.hs/
module Data.Ratio(
Ratio, Rational,
(%),
numerator, denominator,
approxRational,
rationalInfinity,
rationalNaN,
rationalMinusZero,
) where
import Prelude() -- do not import Prelude
import Data.Bool
import Data.Eq
import Data.Fractional
import Data.Function
import Data.Integer
import Data.Integral
import Data.Num
import Data.Ord
import Data.Ratio_Type
import Data.Real
import Data.RealFrac
import Text.Show
{- in Data.Ratio_Type
data Ratio a = !a :% !a
-}
-- The value x :% y represents the rational number x/y.
-- The y is always >= 0, and the number is in reduced for,
-- i.e., there are no common factor > 1 for x and y.
-- In addition to the ordinary rationals, we use the "wheel"
-- extension of the rationals.
-- This means that the / operation is total. In particular
-- x/0 == 1/0 == rationalInfinity, when x/=0
-- 0/0 == rationalNaN (often called perp for wheels).
-- Wheels obey most of the usual algebraic laws for rationals,
-- but the distributive law has to be augmented to work for all
-- numbers.
-- (x + y) * z + 0*z == x*z + y*z
-- When z is a normal rational number the is clearly the same
-- as the usual distributive law.
--
-- NOTE. Experimentally, we extend this with:
-- x/0 == 1/0 when x > 0
-- x/0 == -1/0 when x < 0
instance forall a . Eq a => Eq (Ratio a) where
(x :% y) == (x' :% y') = x == x' && y == y'
instance forall a . (Integral a, Ord a) => Ord (Ratio a) where
(x :% y) <= (x' :% y') = x * y' <= x' * y
(x :% y) < (x' :% y') = x * y' < x' * y
(x :% y) >= (x' :% y') = x * y' >= x' * y
(x :% y) > (x' :% y') = x * y' > x' * y
instance forall a . (Integral a, Ord a) => Num (Ratio a) where
(x:%y) + (x':%y') = reduce (x*y' + x'*y) (y*y')
(x:%y) - (x':%y') = reduce (x*y' - x'*y) (y*y')
(x:%y) * (x':%y') = reduce (x * x') (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 forall a . (Integral a, Ord a) => Fractional (Ratio a) where
(x:%y) / (x':%y') = reduce (x*y') (y*x')
recip (x:%y)
| x < 0 = (-y) :% (-x)
| otherwise = y :% x
fromRational (x:%y) = fromInteger x % fromInteger y
instance forall a . (Show a) => Show (Ratio a) where
showsPrec p (x:%y) = showParen (p > 7) $
showsPrec 8 x .
showString " % " .
showsPrec 8 y
instance forall a . (Integral a, Ord a) => Real (Ratio a) where
toRational (x :% y) = toInteger x :% toInteger y
rationalInfinity :: Rational
rationalInfinity = 1 :% 0
rationalNaN :: Rational
rationalNaN = 0 :% 0
rationalMinusZero :: Rational
rationalMinusZero = 0 :% (-1)
infixl 7 %
(%) :: forall a . (Integral a, Ord a) => a -> a -> Ratio a
x % y = reduce x y
reduce :: forall a . (Ord a, Integral a) => a -> a -> Ratio a
reduce x y | y > 0 = let d = gcd x y in (x `quot` d) :% (y `quot` d)
| y < 0 = reduce (- x) (- y)
| otherwise = signum x :% 0
numerator :: forall a . Ratio a -> a
numerator (x :% _) = x
denominator :: forall a . Ratio a -> a
denominator (_ :% y) = y
instance (Ord a, Integral a) => RealFrac (Ratio a) where
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
approxRational :: (RealFrac a) => a -> a -> Rational
approxRational rat eps =
simplest (toRational rat - toRational eps) (toRational rat + toRational eps)
where
simplest x y
| y < x = simplest y x
| x == y = xr
| x > 0 = simplest' n d n' d'
| y < 0 = - simplest' (-n') d' (-n) d
| otherwise = 0 :% 1
where xr = toRational x
n = numerator xr
d = denominator xr
nd' = toRational y
n' = numerator nd'
d' = denominator nd'
simplest' n d n' d' -- assumes 0 < n%d < n'%d'
| r == 0 = q :% 1
| q /= q' = (q+1) :% 1
| otherwise = (q*n''+d'') :% n''
where (q,r) = quotRem n d
(q',r') = quotRem n' d'
nd'' = simplest' d' r' d r
n'' = numerator nd''
d'' = denominator nd''