-- Informatics 1 Functional Programming
-- December 2012
-- SITTING 1 (09:30 - 11:30)

import Test.QuickCheck( quickCheck, 
                        Arbitrary( arbitrary ),
                        oneof, elements, sized  )
import Control.Monad -- defines liftM, liftM2, used below

-- Question 1

-- 1a

f :: Int -> [Int] -> [Int]
f =  undefined

-- 1b

g :: Int -> [Int] -> [Int]
g =  undefined

-- Question 2

-- 2a

p :: [Int] -> Bool
p ns = and [ even n | n <- ns, n >= 10, n <= 100 ]

test2a =
  p [1,12,153,84,64,9] == True &&
  p [1,12,153,83,9] == False &&
  p [] == True &&
  p [1,151] == True

-- 2b

q :: [Int] -> Bool
q []     = True
q (n:ns) | n >= 10 && n <= 100 = even n && q ns
         | otherwise           = q ns

test2b =
  q [1,12,153,84,64,9] == True &&
  q [1,12,153,83,9] == False &&
  q [] == True &&
  q [1,151] == True

-- 2c

r :: [Int] -> Bool
r ns =  foldr (&&) True ( map even ( filter (<= 100) ( filter (>= 10) ns )))

test2c =
  r [1,12,153,84,64,9] == True &&
  r [1,12,153,83,9] == False &&
  r [] == True &&
  r [1,151] == True

prop2 ns = p ns == q ns && q ns == r ns


-- Question 3

data Prop = X
          | F
          | T
          | Not Prop
          | Prop :|: Prop
          deriving (Eq, Ord)

-- turns a Prop into a string approximating mathematical notation

showProp :: Prop -> String
showProp X          =  "X"
showProp F          =  "F"
showProp T          =  "T"
showProp (Not p)    =  "(~" ++ showProp p ++ ")"
showProp (p :|: q)  =  "(" ++ showProp p ++ "|" ++ showProp q ++ ")"

-- For QuickCheck

instance Show Prop where
    show  =  showProp

instance Arbitrary Prop where
    arbitrary  =  sized prop
        where
          prop n | n <= 0     =  atom
                 | otherwise  =  oneof [ atom
                                       , liftM Not subform
                                       , liftM2 (:|:) subform subform
                                       ]
                 where
                   atom = oneof [elements [X,F,T]]
                   subform  =  prop (n `div` 2)

-- 3a

eval :: Prop -> Bool -> Bool
eval X v = v
eval F v = False
eval T v = True
eval (Not p) v = not (eval p v)
eval (p :|: q) v = (eval p v) || (eval q v)


test3a =
  eval (Not T) True == False &&
  eval (Not X) False == True &&
  eval (Not X :|: Not (Not X)) True == True &&
  eval (Not X :|: Not (Not X)) False == True &&
  eval (Not (Not X :|: F)) True == True &&
  eval (Not (Not X :|: F)) False == False

-- 3b

simplify :: Prop -> Prop
simplify =  undefined
-- simplify X = undefined
-- simplify F = undefined
-- simplify T = undefined
-- simplify (Not p) =  undefined
-- simplify (p :|: q) = undefined
