module Week4 where

import Test.QuickCheck
import System.Directory
import Data.Maybe

-------------------------------------------------------------------------
-- 1. Properties of the look function

type Table a b = [(a,b)]

look :: Eq a => a -> Table a b -> Maybe b
look x []           = Nothing
look x ((x',y):xys)
  | x == x'         = Just y
  | otherwise       = look x xys

prop_LookJust :: Integer -> [(Integer,Integer)] -> Property
prop_LookJust x xys =
  look_x /= Nothing ==>
    (x,y) `elem` xys
 where
  look_x = look x xys
  Just y = look_x

prop_Look :: Integer -> [(Integer,Integer)] -> Bool
prop_Look x xys =
  examine (look x xys)
 where
  examine Nothing  = x `notElem` [ x | (x,_) <- xys ]
  examine (Just y) = (x,y) `elem` xys

prop_LookSucceed :: Integer -> [(Integer,Integer)] -> Property
prop_LookSucceed k tab =
  k `elem` [k' | (k',_) <- tab] ==> isJust (look k tab)

-------------------------------------------------------------------------
-- 2. Properties of the prefixOf function

prefixOf :: Eq a => [a] -> [a] -> Bool
prefixOf [] _          = True
prefixOf _  []         = False
prefixOf (x:xs) (y:ys) = x == y && prefixOf xs ys

prop_prefixOfSelf :: Int -> String -> Bool
prop_prefixOfSelf n s1 = take n s1 `prefixOf` s1

prop_prefixOfAlt :: String -> String -> Property
prop_prefixOfAlt s1 s2 =
    collectLength $
      collect pref $
        pref == (take (length s1) s2 == s1)
  where
    pref = s1 `prefixOf` s2
    collectLength p = if pref then collect (length s1) p else p
  -- The above property shows that (s1 `prefixOf` s2) is false in >90% of the
  -- tests, *and* that whenever it is true, the length of s1 is usually 0!

prop_prefixOfBetter :: String -> String -> Bool
prop_prefixOfBetter s1 s2 = s1 `prefixOf` (s1++s2)

-------------------------------------------------------------------------
-- 3. Monadic helper functions

{-
sequence_ :: Monad m => [m ()] -> m ()
sequence_ []     = do return ()
sequence_ (m:ms) = do m
                      sequence_ ms

-- or: (less efficient)

sequence_ xs = do sequence xs
                  return ()
-}

onlyIf :: Monad m => Bool -> m () -> m ()
onlyIf True  m = m
onlyIf False _ = return ()

-------------------------------------------------------------------------
-- 4. The Number Game

game :: IO ()
game =
 do putStrLn "Think of a number 1 -- 100."
    play 1 100
    putStrLn "I won!"

play :: Int -> Int -> IO ()
play a b =
  do putStr ("Is it " ++ show guess ++ "? ")
     s <- getLine
     case s of
       "higher" -> play (guess+1) b
       "lower"  -> play a (guess-1)
       _        -> return ()
 where
  guess = (a+b) `div` 2

-------------------------------------------------------------------------
-- 5. A Backup Script

backup :: IO ()
backup =
  do files <- getDirectoryContents "."
     createDirectory "backup"
     sequence_
       [ copyFile' file ("backup/" ++ file)
       | file <- files
       ]

-- copyFile' file file' copies file to file', if file is a real file
-- (and not a directory)
copyFile' :: FilePath -> FilePath -> IO ()
copyFile' file file' =
  do isFile <- doesFileExist file
     if isFile
       then copyFile file file'
       else return ()