--------------------------------------------------------------------------
-- 	Haskell: The Craft of Functional Programming
-- 	Simon Thompson
-- 	(c) Addison-Wesley, 1999.
-- 
-- 	Chapter 4
--------------------------------------------------------------------------

module Chapter4 where

-- Designing a program in Haskell
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

maxThree :: Int -> Int -> Int -> Int
maxThree x y z = (x `max` y) `max` z

middleNumber :: Int -> Int -> Int -> Int
middleNumber x y z
  | between y x z      = x
  | between x y z      = y
  | otherwise          = z

-- What follows here is a dummy definition of between; you need to replace this
-- with a proper definition for the function middleNumber to work.

between ::  Int -> Int -> Int -> Bool
between = between

-- Primitive recursion over Int
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

-- The factorial of n is 1*2*...*(n-1)*n, so that factorial of four is 24.
-- It is often written n!

fac :: Int -> Int
fac n
  | n==0        = 1
  | n>0         = fac (n-1) * n
  | otherwise   = error "fac only defined on natural numbers"

--                                      n
-- Raising two to a power: power2 n is 2  in mathematical notation.

power2 :: Int -> Int
power2 n
  | n==0        = 1
  | n>0         = 2 * power2 (n-1)

-- The sum of the factorials up to a particular value, 0! + 1! + ... n!.

sumFacs :: Int -> Int
sumFacs n
  | n==0        = 1
  | n>0         = sumFacs (n-1) + fac n  

-- The sum of the values of a function up to a particular value: 
-- 	f 0 + f 1 + ... f n
-- from which you can reconstruct sumFacs: sumFacs n = sumFun fac n

sumFun :: (Int -> Int) -> Int -> Int
sumFun f n
  | n==0        = f 0
  | n>0         = sumFun f (n-1) + f n  

-- The maximum number of regions into which n lines can cut a plane.

regions :: Int -> Int 
regions n
  | n==0        = 1
  | n>0         = regions (n-1) + n

-- The Fibonacci numbers 0, 1, 1, 2, 3, 5, ..., u, v, u+v, ...

fib :: Int -> Int
fib n 
  | n==0        = 0
  | n==1        = 1
  | n>1         = fib (n-2) + fib (n-1)

-- Division of integers

remainder :: Int -> Int -> Int
remainder m n 
  | m<n         = m
  | otherwise   = remainder (m-n) n

divide    :: Int -> Int -> Int
divide m n
  | m<n         = 0
  | otherwise   = 1 + divide (m-n) n

-- Testing
-- ^^^^^^^

-- Does this function calculate the maximum of three numbers?

mysteryMax :: Int -> Int -> Int -> Int
mysteryMax x y z
  | x > y && x > z      = x
  | y > x && y > z      = y
  | otherwise           = z

