--[[ Rational Arithmetic Data Representation Module (DeferGCD) H. Conrad Cunningham, Professor Computer and Information Science University of Mississippi Developed for CSci 450/503, Organization of Programming Languages, Fall 2016 This version of the data representation module represents a rational number using a two-element Lua array but defers computing the GCD until numer or denom is called. 1234567890123456789012345678901234567890123456789012345678901234567890 2016-09-11: Adapted from Haskell RationalDeferGCD module This work was based on a Haskell version that was itself adapted from section 2.1 of Abelson and Sussman's _Structure and Interpretation of Computer Programs_ (http://mitpress.mit.edu/sicp/). Note: The Haskell version has "showRat" in the "core' but it is moved the outer module in this Lua version. --]] -- UTILITY FUNCTIONS (not exported) -- Haskell -- signum' :: Int -> Int -- signum' n | n == 0 = 0 -- | n > 0 = 1 -- | n < 0 = -1 -- Assumes n is numeric local function signum(n) if n == 0 then return 0 elseif n > 0 then return 1 else return -1 end end -- Haskell -- 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) -- Assumes arguments are integers local function gcd(x,y) local function gcdaux(x,y) if y == 0 then return x else return gcdaux(y, x % y) -- tail recursive end end return gcdaux(math.abs(x), math.abs(y)) end -- RATIONAL ARITHMETIC OPERATORS -- Haskell -- module RationalCore -- (Rat, makeRat, numer, denom, showRat) -- where -- -- type Rat = (Int,Int) -- makeRat :: Int -> Int -> Rat -- makeRat x 0 = error ( "Cannot construct a rational number " -- ++ showRat (x,0) ++ "\n" ) -- makeRat 0 y = (0,1) -- makeRat x y = (x,y) -- Creates a new rational number, but does not -- check whether x and y are integers and y nonzero local function newRat(x,y) if x == 0 then return {0,1} else return {x,y} end end -- Checks whether arguments are integers for Lua before 5.3 local function makeRat(x,y) if type(x) == "number" and type(y) == "number" and x == math.floor(x) and y == math.floor(y) and y ~= 0 then return newRat(x,y) else error("Cannot construct rational number " .. tostring(x) .. "/" .. tostring(y) ) end end -- Haskell -- 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' local function numer(r) local x,y = r[1], r[2] local xx = signum(y) * x local yy = math.abs(y) local d = gcd(xx,yy) return xx / d end local function denom(r) local x,y = r[1], r[2] local xx = signum(y) * x local yy = math.abs(y) local d = gcd(xx,yy) return yy / d end -- Should newRat also be exported? return { makeRat = makeRat, numer = numer, denom = denom }