--sumlist
sumlist :: (Num a) => [a] -> a
sumlist []    = 0
sumlist (h:t) = h+sumlist t

{-- Test it with:

sumlist [1,2,3,4]

--}

--map'
map' :: (a -> b) -> [a] -> [b]
map' _ []    = []
map' f (h:t) = f h:map' f t

{-- Test it with:

map' (\x -> x*x) [1,2,3,4]   -- displays [1,4,9,16]

--}


--foldr'
foldr' :: (a->b->b) -> b -> [a] -> b
foldr' _ u []    = u
foldr' f u (h:t) = f h (foldr' f u t)

{-- Test it with:

foldr' (+) 0 [1,2,3,4]   -- displays 10
foldr' (*) 1 [1,2,3,4]   -- displays 24

--}

--sumlist'
sumlist' :: (Num a) => [a] -> a
sumlist' = foldr' (+) 0

{-- Test it with:

sumlist' [1,2,3,4]

--}

map'' :: (a -> b) -> [a] -> [b]
map'' f = foldr (\h t -> (f h):t) []

{-- Test it with:

map'' (\x -> x*x) [1,2,3,4]   -- displays [1,4,9,16]

--}

map''' :: (a -> b) -> [a] -> [b]
map''' f = foldr ((:) . f) []

{-- Test it with:

map''' (\x -> x*x) [1,2,3,4]   -- displays [1,4,9,16]

--}


--foldl'
foldl' :: (a->b->b) -> b -> [a] -> b
foldl' _ u []    = u
foldl' f u (h:t) = foldl' f (f h u) t

{-- Test it with:

foldl' (+) 0 [1,2,3,4]   -- displays 10
foldl' (*) 1 [1,2,3,4]   -- displays 24

foldl' (\x y -> x:y) [] [1,2,3]
foldl' (:) [] [1,2,3]
--}

--foldlul
foldlul :: (b->a->b) -> b -> [a] -> b
foldlul _ u []    = u
foldlul f u (h:t) = foldlul f (f u h) t

{-- Test it with:

foldlul (+) 0 [1,2,3,4]   -- displays 10
foldlul (*) 1 [1,2,3,4]   -- displays 24

--}


-- filter'
filter' :: (a-> Bool) -> [a] -> [a]
filter' _ []    = []
filter' p (h:t) = if p h then h:filter' p t
                         else filter' p t

{-- Test it with:

filter' (\x -> x < 3) [1,2,3,4]   -- displays [1,2]

--}

-- filter'' as foldr' application
filter'' :: (a-> Bool) -> [a] -> [a]
filter'' p l = foldr' 
                 (\h t ->  if p h 
                           then h:t 
                           else t) [] l

{-- Test it with:

filter'' (\x -> x < 3) [1,2,3,4]   -- displays [1,2]

--}

-- filter''' as foldr' application after eta-reduction
filter''' :: (a-> Bool) -> [a] -> [a]
filter''' p = foldr' 
               (\h t ->  if p h 
                         then h:t 
                         else t) []

{-- Test it with:

filter''' (\x -> x < 3) [1,2,3,4]   -- displays [1,2]

--}

--map reduce
mapreduce :: (a->b)->([b]->c)->([a]->c)
mapreduce m r = \l -> r (map' m l)

sumsquares' :: (Num a) => [a] -> a
sumsquares' = mapreduce square sumlist where
                square = (\x -> x*x)
                sumlist = (foldr' (+) 0)

{-- Test it with:

sumsquares' [1,2,3,4]   -- displays 30

--}

sumsquares :: (Num a) => [a] -> a
sumsquares = mapreduce
                (\x -> x*x)
                (foldr' (+) 0)

{-- Test it with:

sumsquares [1,2,3,4]   -- displays 30

--}
