{- Exercise 2

Function  | Recursion | Linear recursion | Tail recursion | Guarded recursion |
-------------------------------------------------------------------------------
pow2      |    yes    |       yes        |       no       |        no         |
factAux   |           |                  |                |                   |
factorial |           |                  |                |                   |
init      |           |                  |                |                   |
binom     |           |                  |                |                   |
negList   |           |                  |                |                   |

-}

{- Exercise 3 -}
applyIndefinitely :: (a -> a) -> a -> [a]
applyIndefinitely f x = undefined

takeUntil :: (a -> Bool) -> [a] -> [a]
takeUntil p xs = undefined

what :: ([a], [a]) -> ([a], [a])
what = undefined

rev :: [a] -> [a]
rev = fst . head . dropWhile (not . null . snd) . applyIndefinitely what . (,) []

prefixes :: [a] -> [[a]]
prefixes = undefined


{- Tests -}
tests = do
    check "applyIndefinitely1" (show [1,2,3,4]) (take 4 $ applyIndefinitely (+1) 1)
    check "applyIndefinitely2" (show [[],[0],[0,0],[0,0,0]]) (take 4 $ applyIndefinitely (++[0]) [])
    checkAll "takeUntil1" [(> 5),(< 5),(== 5)]
        (\p -> show $ takeWhile (not . p) [1..10] ++ [head $ dropWhile (not . p) [1..10]])
        (\p -> takeUntil p [1..10])
    check "takeUntil2" (show "") (takeUntil (=='a') [])
    checkAll "rev" ["hello","aha",[]] (show . reverse) (rev)
    check "prefixes1" (show [[],[1],[1,2],[1,2,3]]) (take 4 $ prefixes [1..])
    check "prefixes2" (show [""]) (prefixes "")
    check "prefixes3" (show [[],["a"],["a","b"],["a","b","c"]]) (prefixes ["a","b","c"])

check name e c = do
    putStr ("*** " ++ name ++ ": ")
    if show c == e then putStrLn "OK"
    else putStrLn ("ERROR; expected '" ++ e ++ "', but found '" ++ show c ++ "'")

checkAll name xs e c = do
    putStr ("*** " ++ name ++ ": ")
    let errors = filter (\x -> show (c x) /= e x) xs
    if null errors then putStrLn "OK"
    else do
        let x = head errors
        putStrLn ("ERROR; expexted '" ++ e x ++ "', but found '" ++ show (c x) ++ "'")