content: -- Welcome to the first exercise set of part 2 of the Haskell Mooc!
  -- Edit this file according to the instructions, and check your
  -- answers with
  --
  --   stack runhaskell Set9aTest.hs
  --
  -- You can also play around with your answers in GHCi with
  --
  --   stack ghci Set9a.hs
  
  module Set9a where
  
  import Data.Char
  import Data.List
  import Data.Ord
  
  import Mooc.Todo
  
  ------------------------------------------------------------------------------
  -- Ex 1: Implement a function workload that takes in the number of
  -- exercises a student has to finish, and another number that counts
  -- the number of hours each exercise takes.
  --
  -- If the total number of hours needed for all exercises is over 100,
  -- return "Holy moly!" if it is under 10, return "Piece of cake!".
  -- Otherwise return "Ok."
  
  workload :: Int -> Int -> String
  workload nExercises hoursPerExercise = todo
  
  ------------------------------------------------------------------------------
  -- Ex 2: Implement the function echo that builds a string like this:
  --
  --   echo "hello!" ==> "hello!, ello!, llo!, lo!, o!, !, "
  --   echo "ECHO" ==> "ECHO, CHO, HO, O, "
  --   echo "X" ==> "X, "
  --   echo "" ==> ""
  --
  -- Hint: use recursion
  
  echo :: String -> String
  echo = todo
  
  ------------------------------------------------------------------------------
  -- Ex 3: A country issues some banknotes. The banknotes have a serial
  -- number that can be used to check if the banknote is valid. For a
  -- banknote to be valid, either
  --  * the third and fifth digits need to be the same
  --  * or the fourth and sixth digits need to be the same
  --
  -- Given a list of bank note serial numbers (strings), count how many
  -- are valid.
  
  countValid :: [String] -> Int
  countValid = todo
  
  ------------------------------------------------------------------------------
  -- Ex 4: Find the first element that repeats two or more times _in a
  -- row_ in the input list. Return a Nothing value if no element repeats.
  --
  -- Examples:
  --   repeated [1,2,3] ==> Nothing
  --   repeated [1,2,2,3,3] ==> Just 2
  --   repeated [1,2,1,2,3,3] ==> Just 3
  
  repeated :: Eq a => [a] -> Maybe a
  repeated = todo
  
  ------------------------------------------------------------------------------
  -- Ex 5: A laboratory has been collecting measurements. Some of the
  -- measurements have failed, so the lab is using the type
  --   Either String Int
  -- to track the measurements. A Left value represents a failed measurement,
  -- while a Right value represents a successful one.
  --
  -- Compute the sum of all successful measurements. If there are
  -- successful measurements, return the sum wrapped in a Right, but if
  -- there are none, return Left "no data".
  --
  -- Examples:
  --   sumSuccess [Right 1, Left "it was a snake!", Right 3]
  --     ==> Right 4
  --   sumSuccess [Left "lab blew up", Left "I was sick"]
  --     ==> Left "no data"
  --   sumSuccess []
  --     ==> Left "no data"
  
  sumSuccess :: [Either String Int] -> Either String Int
  sumSuccess = todo
  
  ------------------------------------------------------------------------------
  -- Ex 6: A combination lock can either be open or closed. The lock
  -- also remembers a code. A closed lock can only be opened with the
  -- right code. The code of an open lock can be changed.
  --
  -- Implement a datatype Lock and the functions isOpen, open, lock,
  -- changeCode and the constant aLock as instructed below.
  --
  -- Examples:
  --   isOpen aLock ==> False
  --   isOpen (lock aLock) ==> False
  --   isOpen (open "1234" aLock) ==> True
  --   isOpen (lock (open "1234" aLock)) ==> False
  --   isOpen (open "1235" aLock) ==> False
  --   isOpen (lock (open "1235" aLock)) ==> False
  --   isOpen (open "1234" (changeCode "0000" aLock)) ==> True
  --   isOpen (open "0000" (changeCode "0000" aLock)) ==> False
  --   isOpen (open "0000" (lock (changeCode "0000" (open "1234" aLock)))) ==> True
  --   isOpen (open "1234" (lock (changeCode "0000" (open "1234" aLock)))) ==> False
  
  data Lock = LockUndefined
    deriving Show
  
  -- aLock should be a locked lock with the code "1234"
  aLock :: Lock
  aLock = todo
  
  -- isOpen returns True if the lock is open
  isOpen :: Lock -> Bool
  isOpen = todo
  
  -- open tries to open the lock with the given code. If the code is
  -- wrong, nothing happens.
  open :: String -> Lock -> Lock
  open = todo
  
  -- lock closes a lock. If the lock is already closed, nothing happens.
  lock :: Lock -> Lock
  lock = todo
  
  -- changeCode changes the code of an open lock. If the lock is closed,
  -- nothing happens.
  changeCode :: String -> Lock -> Lock
  changeCode = todo
  
  ------------------------------------------------------------------------------
  -- Ex 7: Here's a type Text that just wraps a String. Implement an Eq
  -- instance for Text that ignores all white space (space characters
  -- and line returns).
  --
  -- Hint: Data.Char.isSpace
  --
  -- Examples
  --   Text "abc"  == Text "abc"      ==> True
  --   Text "a bc" == Text "ab  c\n"  ==> True
  --   Text "abc"  == Text "abcd"     ==> False
  --   Text "a bc" == Text "ab  d\n"  ==> False
  
  data Text = Text String
    deriving Show
  
  
  ------------------------------------------------------------------------------
  -- Ex 8: We can represent functions or mappings as lists of pairs.
  -- For example the list [("bob",13),("mary",8)] means that "bob" maps
  -- to 13 and "mary" maps to 8.
  --
  -- Implement _composition_ for mappings like this. You compose two
  -- mappings by looking up each result of the first mapping in the
  -- second mapping.
  --
  -- You may assume there are no repeated first elements of tuples in
  -- the argument lists, that is.
  --
  -- The ordering of the output doesn't matter.
  --
  -- Hint: remember the function `lookup` from Prelude?
  --
  -- Note! The order of arguments to `compose` is the other way around
  -- compared to e.g. (.): `compose f g` should apply `f` first, then
  -- `g`, but `f.g` applies `g` first, then `f`.
  --
  -- Examples:
  --   composing two mappings of size 1:
  --     compose [("a",1)] [(1,True)]
  --       ==> [("a",True)]
  --   nonmatching mappings get ignored:
  --     compose [("a",1),("b",2)] [(3,False),(4,True)]
  --       ==> []
  --   a more complex example: note how "omicron" and "c" are ignored
  --     compose [("a","alpha"),("b","beta"),("c","gamma")] [("alpha",1),("beta",2),("omicron",15)]
  --       ==> [("a",1),("b",2)]
  
  compose :: (Eq a, Eq b) => [(a,b)] -> [(b,c)] -> [(a,c)]
  compose = todo
  
  ------------------------------------------------------------------------------
  -- Ex 9: Reorder a list using a list of indices.
  --
  -- You are given a list of indices (numbers from 0 to n) and an input
  -- list (of length n). Each index in the index list tells you where to
  -- place the corresponding element from the input list in the output
  -- list.
  --
  -- For example, if the 3rd element of the index list is 7, and the 3rd
  -- element of the input list is 'a', the output list should have 'a'
  -- at index 7.
  --
  -- (The index lists discussed in this exercise correspond to permutations in
  -- math. In fact, permutations can be multiplied which is a special case of
  -- the compose function in the previous exercise. For more information on
  -- permutations, see https://en.wikipedia.org/wiki/Permutation)
  --
  -- Examples:
  --   permute [0,1] [True, False] ==> [True, False]
  --   permute [1,0] [True, False] ==> [False, True]
  --   permute [0,1,2,3] "hask" ==> "hask"
  --   permute [2,0,1,3] "hask" ==> "ashk"
  --   permute [1,2,3,0] "hask" ==> "khas"
  --   permute [2, 1, 0] (permute [2, 1, 0] "foo") ==> "foo"
  --   permute [1, 0, 2] (permute [0, 2, 1] [9,3,5]) ==> [5,9,3]
  --   permute [0, 2, 1] (permute [1, 0, 2] [9,3,5]) ==> [3,5,9]
  --   permute ([1, 0, 2] `multiply` [0, 2, 1]) [9,3,5] ==> [5,9,3]
  --   permute ([0, 2, 1] `multiply` [1, 0, 2]) [9,3,5] ==> [3,5,9]
  
  -- A type alias for index lists.
  type Permutation = [Int]
  
  -- Permuting a list with the identity permutation should change nothing.
  identity :: Int -> Permutation
  identity n = [0 .. n - 1]
  
  -- This function shows how permutations can be composed. Do not edit this
  -- function.
  multiply :: Permutation -> Permutation -> Permutation
  multiply p q = map (\i -> p !! (q !! i)) (identity (length p))
  
  permute :: Permutation -> [a] -> [a]
  permute = todo
  