{-  Exploring Languages with Interpreters and Functional Programming
    Chapter 7 & 12: Rational Arithmetic Core Data Representation
    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' and 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:
using two relatively prime integers (Int).

-}

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

-- Core

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 _ = zeroRat
makeRat x y = (x' `div` d, y' `div` d) 
    where x' = (signum' y) * x 
          y' = abs' y 
          d  = gcd' x' y'

numer, denom :: Rat -> Int
numer (x,_) = x
denom (_,y) = 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)

