-- Solutions to class exam
-- 1.
type Item = String
type Quantity = Int
type Price = Int
type Entry = (Item, Quantity, Price)
-- 1a.
important :: Entry -> Bool
important (i, q, p) = q > 100 || p > 1000 || q*p > 50000
-- 1b.
showEntry :: Entry -> String
showEntry (i, q, p) = i ++ star ++ ": " ++ show q ++ "@" ++ showPrice p
where star = if important (i,q,p) then "(*)" else ""
showPrice p | n == 1 = "0.0" ++ s
| n == 2 = "0." ++ s
| n >= 3 = take (n-2) s ++ "." ++ drop (n-2) s
where s = show p
n = length s
x1b = showEntry ("Paper clip", 5000, 10) == "Paper clip(*): 5000@0.10"
&& showEntry ("Notebook", 3, 900) == "Notebook: 3@9.00"
-- 1c.
showEntries :: [Entry] -> String
showEntries es = unlines [ showEntry e | e <- es ]
x1c = showEntries [("Paper clip", 5000, 10), ("Notebook", 3, 900)] ==
"Paper clip(*): 5000@0.10\nNotebook: 3@9.00\n"
-- 2.
type Pounds = Float
-- 2a.
lo = 2010
hi = 31400
tax :: Pounds -> Pounds
tax x | 0 <= x && x <= lo = 0.10*x
| lo < x && x <= hi = 0.22*(x-lo) + tax lo
| hi < x = 0.40*(x-hi) + tax hi
x2a = tax 20000 == 4158.80 && tax 40000 == 10106.80
-- alternative solution
tax' :: Pounds -> Pounds
tax' x = 0.10*(0 `max` x `min` lo)
+ 0.22*((lo `max` x `min` hi) - lo)
+ 0.40*((hi `max` x) - hi)
x2a' = tax' 20000 == 4158.80 && tax' 40000 == 10106.80
-- 2b.
highTax :: [Pounds] -> Pounds
highTax xs = sum [ tax x | x <- xs, x >= hi ]
x2b = highTax [40000, 20000, 40000] == 20213.60
-- 2c.
highTax' :: [Pounds] -> Pounds
highTax' [] = 0
highTax' (x:xs) | x >= hi = tax x + highTax' xs
| otherwise = highTax' xs
x2c = highTax' [40000, 20000, 40000] == 20213.60
-- 3.
-- 3a.
nextRow :: [Int] -> [Int]
nextRow xs = [ x+y | (x,y) <- zip ([0]++xs) (xs++[0]) ]
x3a = nextRow [1,3,3,1] == [1,4,6,4,1]
-- alternative solution
nextRow' :: [Int] -> [Int]
nextRow' xs = zipWith (+) ([0]++xs) (xs++[0])
x3a' = nextRow' [1,3,3,1] == [1,4,6,4,1]
-- alternative solution
nextRow'' :: [Int] -> [Int]
nextRow'' xs = adjacentsum ([0]++xs++[0])
where adjacentsum [0] = []
adjacentsum (x:y:ys) = x+y : adjacentsum (y:ys)
x3a'' = nextRow'' [1,3,3,1] == [1,4,6,4,1]
-- 3b.
pascal :: Int -> [[Int]]
pascal n = loop n [1]
where
loop 1 xs = [xs]
loop n xs | n > 1 = xs : loop (n-1) (nextRow xs)
x3b = pascal 6 == [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1],[1,5,10,10,5,1]]
-- alternative solution
pascal' :: Int -> [[Int]]
pascal' n = take n (iterate nextRow [1])
x3b' = pascal 6 == [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1],[1,5,10,10,5,1]]
-- 1c.
centre :: Int -> String -> String
centre w s | n <= w = replicate h ' ' ++ s
where n = length s
h = (w - n) `div` 2
showLine :: Int -> [Int] -> String
showLine w xs = centre w (unwords [ show x | x <- xs ])
x3c = showLine 20 [1,4,6,4,1] == " 1 4 6 4 1"
-- 1d.
showPascal :: Int -> Int -> String
showPascal w n = unlines [ showLine w xs | xs <- pascal n ]
x3d = showPascal 20 6 ==
" 1\n"
++ " 1 1\n"
++ " 1 2 1\n"
++ " 1 3 3 1\n"
++ " 1 4 6 4 1\n"
++ " 1 5 10 10 5 1\n"