{-  Factorial Functions -- Possible Interactive Session
    H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2017-08-27: Created from Fact.hs and notes for use in demo
2018-08-18: Updated for 2018 ELIFP textbook

-}

fact1 :: Int -> Int 
fact1 n = if n == 0 then 
              1 
          else
              n * fact1 (n-1)
 
fact2 :: Int -> Int
fact2 n | n == 0    = 1
fact2 n | otherwise = fact2 (n-1) * n

fact3 :: Int -> Int
fact3 0 = 1
fact3 n = fact3 (n-1) * n

fact4 :: Int -> Int
fact4 n
    | n == 0 = 1
    | n > 0  = fact4 (n-1) * n
 
fact4' :: Int -> Int 
fact4' n 
  | n == 0 =  1 
  | n >= 1 =  n * fact4' (n-1)
  | otherwise = error "fact4' called with negative argument"

fact4'' :: Int -> Int 
fact4'' 0          =  1 
fact4'' n | n >= 1 =  n * fact4'' (n-1)
fact4'' n          = error "fact4'' called with negative argument"

fact5 :: Int -> Int
fact5 n = product [1..n]

fact6 :: Int -> Int 
fact6 n = factIter n 1 

factIter :: Int -> Int -> Int 
factIter 0 r         = r 
factIter n r | n > 0 = factIter (n-1) (n*r) 

fact7 :: Int -> Maybe Int
fact7 n | n == 0    = Just 1
fact7 n | n > 0     = fmap (*n) (fact7 (n-1))
fact7 n | otherwise = Nothing

fact8 :: Integer -> Integer
fact8 n
  | n == 0 =  1 
  | n >= 1 =  n * fact8 (n-1)

fact7 :: Int -> Double
fact7 n = fromInteger (toInteger (product [1..n]))

fact8 :: Int -> Maybe Int
fact8 n | n == 0    = Just 1
fact8 n | n > 0     = fmap (*n) (fact8 (n-1))
fact8 n | otherwise = Nothing

fact9 :: Integer -> Double
fact9 n = fromInteger (product [1..n])

fact10 :: Integer -> Integer
fact10 n = foldl (*) 1 [1..n]

fact11 :: Integer -> Integer
fact11 n = foldr (*) 1 [1..n]
