module Lecture02A where 
-- Functions over Lists; Classes and Polymorphism; 
-- more testing
-- David Sands 2011-10-31

import Data.List(sort)
import Test.QuickCheck

-- hide some Prelude functions so we 
-- can redefine them
import Prelude hiding ((++),reverse,drop,take) 

import qualified Prelude as P((++),reverse,drop,take) 
-- use the original versions a P.++, P.reverse etc
{-
Plan:
  Lists; recursive definitions, polymorphism, 
  classes, quickCheck
  What are lists? 
-}

-- example list functions from Prelude
-- append
(++) :: [a] -> [a] -> [a]
[] ++ ys     = ys
(x:xs) ++ ys =  x:(xs ++ ys)

-- reverse
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
-- (inefficient version)

rev xs = revinto [] xs
  where revinto a [] = a
        revinto a (y:ys) = revinto (y:a) ys

-- take, drop
drop,take :: Int -> [a] -> [a]
take n _ | n <= 0 = []
take _ []         = []
take n (x:xs)     = x:take (n-1) xs

drop n xs | n <= 0 = xs
drop _ []          = []
drop n (x:xs)      = drop (n-1) xs

-- simple property of take? (==>) 
prop_take n xs = n >= 0 ==> 
                 collect (n > length xs) $
                 length (take n xs) <= n 

prop_takedrop n xs =  classify (n <= 0 || n > length xs) "extreme" $
                      take n xs ++ drop n xs == xs
  where types = xs :: [Bool]

-- connecting take and drop?
                
qsort [] = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort bigger
  where smaller = [y | y <- xs, y < x]
        bigger  = [z | z <- xs, z >= x]

prop_qsort xs = sort xs == qsort xs 
  where types = xs :: [Bool]

isort [] = []
isort (x:xs) = insert x (isort xs)

insert :: Ord a => a -> [a] -> [a]
insert x [] = [x]
insert x (y:ys) | x <= y = x:y:ys
insert x (y:ys)          = y:insert x ys

prop_insert x xs = insert x (sort xs) == sort (x:xs)
  -- too similar to definition


-- Time permitting: 
-- Higher-order functions
-- filter map and foldr