{-# LANGUAGE Safe #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Ratio
-- Copyright   :  (c) The University of Glasgow 2001
-- License     :  BSD-style (see the file libraries/base/LICENSE)
-- 
-- Maintainer  :  libraries@haskell.org
-- Stability   :  stable
-- Portability :  portable
--
-- Standard functions on rational numbers
--
-----------------------------------------------------------------------------

module Data.Ratio
    ( Ratio
    , Rational
    , (%)
    , numerator
    , denominator
    , approxRational

  ) where

import GHC.Real         -- The basic defns for Ratio

-- -----------------------------------------------------------------------------
-- approxRational

-- | 'approxRational', applied to two real fractional numbers @x@ and @epsilon@,
-- returns the simplest rational number within @epsilon@ of @x@.
-- A rational number @y@ is said to be /simpler/ than another @y'@ if
--
-- * @'abs' ('numerator' y) <= 'abs' ('numerator' y')@, and
--
-- * @'denominator' y <= 'denominator' y'@.
--
-- Any real interval contains a unique simplest rational;
-- in particular, note that @0\/1@ is the simplest rational of all.

-- Implementation details: Here, for simplicity, we assume a closed rational
-- interval.  If such an interval includes at least one whole number, then
-- the simplest rational is the absolutely least whole number.  Otherwise,
-- the bounds are of the form q%1 + r%d and q%1 + r'%d', where abs r < d
-- and abs r' < d', and the simplest rational is q%1 + the reciprocal of
-- the simplest rational between d'%r' and d%r.

approxRational :: (RealFrac a) => a -> a -> Rational
approxRational :: forall a. RealFrac a => a -> a -> Rational
approxRational a
rat a
eps =
    -- We convert rat and eps to rational *before* subtracting/adding since
    -- otherwise we may overflow. This was the cause of #14425.
    Rational -> Rational -> Rational
forall {a}. Real a => a -> a -> Rational
simplest (a -> Rational
forall a. Real a => a -> Rational
toRational a
rat Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
- a -> Rational
forall a. Real a => a -> Rational
toRational a
eps) (a -> Rational
forall a. Real a => a -> Rational
toRational a
rat Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
+ a -> Rational
forall a. Real a => a -> Rational
toRational a
eps)
  where
    simplest :: a -> a -> Rational
simplest a
x a
y
      | a
y a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
x      =  a -> a -> Rational
simplest a
y a
x
      | a
x a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
y     =  Rational
xr
      | a
x a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
0      =  Integer -> Integer -> Integer -> Integer -> Rational
forall {p}. Integral p => p -> p -> p -> p -> Ratio p
simplest' Integer
n Integer
d Integer
n' Integer
d'
      | a
y a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
0      =  - Integer -> Integer -> Integer -> Integer -> Rational
forall {p}. Integral p => p -> p -> p -> p -> Ratio p
simplest' (-Integer
n') Integer
d' (-Integer
n) Integer
d
      | Bool
otherwise  =  Integer
0 Integer -> Integer -> Rational
forall a. a -> a -> Ratio a
:% Integer
1
      where xr :: Rational
xr  = a -> Rational
forall a. Real a => a -> Rational
toRational a
x
            n :: Integer
n   = Rational -> Integer
forall a. Ratio a -> a
numerator Rational
xr
            d :: Integer
d   = Rational -> Integer
forall a. Ratio a -> a
denominator Rational
xr
            nd' :: Rational
nd' = a -> Rational
forall a. Real a => a -> Rational
toRational a
y
            n' :: Integer
n'  = Rational -> Integer
forall a. Ratio a -> a
numerator Rational
nd'
            d' :: Integer
d'  = Rational -> Integer
forall a. Ratio a -> a
denominator Rational
nd'

    simplest' :: p -> p -> p -> p -> Ratio p
simplest' p
n p
d p
n' p
d'       -- assumes 0 < n%d < n'%d'
      | p
r p -> p -> Bool
forall a. Eq a => a -> a -> Bool
== p
0     =  p
q p -> p -> Ratio p
forall a. a -> a -> Ratio a
:% p
1
      | p
q p -> p -> Bool
forall a. Eq a => a -> a -> Bool
/= p
q'    =  (p
qp -> p -> p
forall a. Num a => a -> a -> a
+p
1) p -> p -> Ratio p
forall a. a -> a -> Ratio a
:% p
1
      | Bool
otherwise  =  (p
qp -> p -> p
forall a. Num a => a -> a -> a
*p
n''p -> p -> p
forall a. Num a => a -> a -> a
+p
d'') p -> p -> Ratio p
forall a. a -> a -> Ratio a
:% p
n''
      where (p
q,p
r)      =  p -> p -> (p, p)
forall a. Integral a => a -> a -> (a, a)
quotRem p
n p
d
            (p
q',p
r')    =  p -> p -> (p, p)
forall a. Integral a => a -> a -> (a, a)
quotRem p
n' p
d'
            nd'' :: Ratio p
nd''       =  p -> p -> p -> p -> Ratio p
simplest' p
d' p
r' p
d p
r
            n'' :: p
n''        =  Ratio p -> p
forall a. Ratio a -> a
numerator Ratio p
nd''
            d'' :: p
d''        =  Ratio p -> p
forall a. Ratio a -> a
denominator Ratio p
nd''