module PA4 where
import Test.HUnit

runTests = runTestTT tests -- use runTests to run all the tests at once
  where tests = test (part1tests ++ part2tests ++ part3tests)

-- Part 1 ----------------------------------------------------------------------

type Set t = [t]
type Relation t1 t2 = Set (t1,t2)
type Function t1 t2 = t1 -> t2
-- These type synonyms are used in the type signatures only to clarify intent.
 
contains :: (Eq t) => Set t -> Set t -> Bool
a `contains` b = undefined

cross :: Set t1 -> Set t2 -> Relation t1 t2
a `cross` b = undefined

inverse :: Relation t1 t2 -> Relation t2 t1
inverse = undefined

asRelationOn :: Function t1 t2 -> Set t1 -> Relation t1 t2
f `asRelationOn` a = undefined

part1tests =
  [ naturals `contains` [50,50,31337] ~?= True
  , [50,50,31337] `contains` naturals ~?= False
  , [0,1] `cross` [2,3] ~?= [(0,2),(0,3),(1,2),(1,3)]
  , id `asRelationOn` [1,2,3] ~?= [(1,1),(2,2),(3,3)]
  , id `asRelationOn` naturals `contains` (id `asRelationOn` [21..42]) ~?= True
  , length `asRelationOn` ["a","bc"] ~?= [("a",1),("bc",2)]
  , (21,42) `elem` ((*2) `asRelationOn` naturals) ~?= True
  , (42,21) `elem` inverse ((*2) `asRelationOn` naturals) ~?= True
  ] where
    naturals = [0..]

-- Part 2 ----------------------------------------------------------------------

divides :: (Integral t) => t -> t -> Bool
divides m n = undefined

divisors :: (Integral t) => t -> [t]
divisors n = undefined

isPrime :: (Integral t) => t -> Bool
isPrime n = undefined

primes :: [Integer]
primes = undefined

part2tests =
  [ divides 2 4 ~?= True
  , divisors 3 ~?= [1,3]
  , isPrime 1 ~?= False
  , primes !! 3 ~?= 7
  ]

-- Part 3 ----------------------------------------------------------------------

data BinaryTree bt lt = Branch bt (BinaryTree bt lt) (BinaryTree bt lt)
                      | Leaf lt deriving (Show, Eq)
-- Type variable names have been chosen as abbreviations:
-- bt stands for "branch type", lt for "leaf type", rt for "result type".
type TreeFun bt lt rt = BinaryTree bt lt -> rt -- from a tree to a result
type BranchFun bt rt = bt -> rt -> rt -> rt -- from a branch to a result
type LeafFun lt rt = lt -> rt -- from a leaf

data BinOp = Add | Sub | Mult
instance Show BinOp where
  show Add = "+"
  show Sub = "-"
  show Mult = "*"

getFunction :: BranchFun BinOp Integer
getFunction binop = undefined

showParenPre :: (Show bt) => BranchFun bt String
showParenPre op left right = undefined

showPost :: (Show bt) => BranchFun bt String
showPost op left right = undefined

treeApply :: BranchFun bt rt -> LeafFun lt rt -> TreeFun bt lt rt
treeApply bf lf tree = undefined

-- These functions are already defined to use the ones you define above.
showScheme :: (Show bt, Show lt) => BinaryTree bt lt -> String
showScheme = treeApply showParenPre show

showRPN :: (Show bt, Show lt) => BinaryTree bt lt -> String
showRPN = treeApply showPost show

type AE = BinaryTree BinOp Integer
eval :: AE -> Integer
eval = treeApply getFunction id

part3tests =
  [ eval expr ~?= 5
  , showScheme expr ~?= "(- (* 3 2) 1)"
  , showRPN expr ~?= "3 2 * 1 -"
  ] where
    expr = Branch Sub (Branch Mult (Leaf 3) (Leaf 2)) (Leaf 1)
