{-  Exploring Languages with Interpreters and Functional Programming
    Chapter 7 & 12: Rational Arithmetic Deferred GCD Data Rep
    Copyright (C) 2018, H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2016-07-??: Based on SICP 2.1 & my earler Haskell, Lua versions
2017-02-03: Added comments
2018-06-12: Added zeroRat
2018-06-23: Used "otherwise" clauses on signum', abs',
            Avoided call of showRat in makeRat error message
2018-07-04: Updated to be compatible with testing modules;
            Added copyright notice
2018-07-19: Corrected signature of zeroRat, updated for Chapter 12

This module "implements" the abstract interface RationalRep. It
encapsulates one possible data representation for rational numbers:
deferring computation of GCD until component accessed.

-}

module RationalDeferGCD
  ( Rat, zeroRat, makeRat, numer, denom, showRat )
where

-- Core functionality for Deferred GCD data representation

type Rat = (Int,Int)

zeroRat :: Rat
zeroRat = (0,1)

makeRat :: Int -> Int -> Rat
makeRat x 0 = error ( "Cannot construct a rational number "
                       ++ show x ++ "/0" )
makeRat 0 y = zeroRat 
makeRat x y = (x,y)

numer, denom :: Rat -> Int
numer (x,y) = x' `div` d
    where x' = (signum' y) * x 
          y' = abs' y 
          d  = gcd' x' y'

denom (x,y) = y' `div` d
    where x' = (signum' y) * x
          y' = abs' y 
          d  = gcd' x' y'

showRat :: Rat -> String
showRat r = show (numer r) ++ "/" ++ show (denom r)

--- Utilities

signum' :: Int -> Int
signum' n | n == 0    =  0 
          | n > 0     =  1 
          | otherwise = -1
            
abs' :: Int -> Int 
abs' n | n >= 0    =  n 
       | otherwise = -n 
 
gcd' :: Int -> Int -> Int 
gcd' x y = gcd'' (abs' x) (abs' y) 
         where gcd'' x 0 = x 
               gcd'' x y = gcd'' y (x `rem` y)

