{-# 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.
    forall {a}. Real a => a -> a -> Rational
simplest (forall a. Real a => a -> Rational
toRational a
rat forall a. Num a => a -> a -> a
- forall a. Real a => a -> Rational
toRational a
eps) (forall a. Real a => a -> Rational
toRational a
rat forall a. Num a => a -> a -> a
+ forall a. Real a => a -> Rational
toRational a
eps)
  where
    simplest :: a -> a -> Rational
simplest a
x a
y
      | a
y forall a. Ord a => a -> a -> Bool
< a
x      =  a -> a -> Rational
simplest a
y a
x
      | a
x forall a. Eq a => a -> a -> Bool
== a
y     =  Rational
xr
      | a
x forall a. Ord a => a -> a -> Bool
> a
0      =  forall {a}. Integral a => a -> a -> a -> a -> Ratio a
simplest' Integer
n Integer
d Integer
n' Integer
d'
      | a
y forall a. Ord a => a -> a -> Bool
< a
0      =  - forall {a}. Integral a => a -> a -> a -> a -> Ratio a
simplest' (-Integer
n') Integer
d' (-Integer
n) Integer
d
      | Bool
otherwise  =  Integer
0 forall a. a -> a -> Ratio a
:% Integer
1
      where xr :: Rational
xr  = forall a. Real a => a -> Rational
toRational a
x
            n :: Integer
n   = forall a. Ratio a -> a
numerator Rational
xr
            d :: Integer
d   = forall a. Ratio a -> a
denominator Rational
xr
            nd' :: Rational
nd' = forall a. Real a => a -> Rational
toRational a
y
            n' :: Integer
n'  = forall a. Ratio a -> a
numerator Rational
nd'
            d' :: Integer
d'  = forall a. Ratio a -> a
denominator Rational
nd'

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