content: -- Exercise set 4a:
  --
  --  * using type classes
  --  * working with lists
  --
  -- Type classes you'll need
  --  * Eq
  --  * Ord
  --  * Num
  --  * Fractional
  --
  -- Useful functions:
  --  * maximum
  --  * minimum
  --  * sort
  
  module Set4a where
  
  import Mooc.Todo
  import Data.List
  import Data.Ord
  import qualified Data.Map as Map
  import Data.Array
  
  ------------------------------------------------------------------------------
  -- Ex 1: implement the function allEqual which returns True if all
  -- values in the list are equal.
  --
  -- Examples:
  --   allEqual [] ==> True
  --   allEqual [1,2,3] ==> False
  --   allEqual [1,1,1] ==> True
  --
  -- PS. check out the error message you get with your implementation if
  -- you remove the Eq a => constraint from the type!
  
  allEqual :: Eq a => [a] -> Bool
  allEqual xs = todo
  
  ------------------------------------------------------------------------------
  -- Ex 2: implement the function distinct which returns True if all
  -- values in a list are different.
  --
  -- Hint: a certain function from the lecture material can make this
  -- really easy for you.
  --
  -- Examples:
  --   distinct [] ==> True
  --   distinct [1,1,2] ==> False
  --   distinct [1,2] ==> True
  
  distinct :: Eq a => [a] -> Bool
  distinct = todo
  
  ------------------------------------------------------------------------------
  -- Ex 3: implement the function middle that returns the middle value
  -- (not the smallest or the largest) out of its three arguments.
  --
  -- The function should work on all types in the Ord class. Give it a
  -- suitable type signature.
  --
  -- Examples:
  --   middle 'b' 'a' 'c'  ==> 'b'
  --   middle 1 7 3        ==> 3
  
  middle = todo
  
  ------------------------------------------------------------------------------
  -- Ex 4: return the range of an input list, that is, the difference
  -- between the smallest and the largest element.
  --
  -- Your function should work on all suitable types, like Float and
  -- Int. You'll need to add _class constraints_ to the type of range.
  --
  -- It's fine if your function doesn't work for empty inputs.
  --
  -- Examples:
  --   rangeOf [4,2,1,3]          ==> 3
  --   rangeOf [1.5,1.0,1.1,1.2]  ==> 0.5
  
  rangeOf :: [a] -> a
  rangeOf = todo
  
  ------------------------------------------------------------------------------
  -- Ex 5: given a (non-empty) list of (non-empty) lists, return the longest
  -- list. If there are multiple lists of the same length, return the list that
  -- has the smallest _first element_.
  --
  -- (If multiple lists have the same length and same first element,
  -- you can return any one of them.)
  --
  -- Give the function "longest" a suitable type.
  --
  -- Challenge: Can you solve this exercise without sorting the list of lists?
  --
  -- Examples:
  --   longest [[1,2,3],[4,5],[6]] ==> [1,2,3]
  --   longest ["bcd","def","ab"] ==> "bcd"
  
  longest = todo
  
  ------------------------------------------------------------------------------
  -- Ex 6: Implement the function incrementKey, that takes a list of
  -- (key,value) pairs, and adds 1 to all the values that have the given key.
  --
  -- You'll need to add _class constraints_ to the type of incrementKey
  -- to make the function work!
  --
  -- The function needs to be generic and handle all compatible types,
  -- see the examples.
  --
  -- Examples:
  --   incrementKey True [(True,1),(False,3),(True,4)] ==> [(True,2),(False,3),(True,5)]
  --   incrementKey 'a' [('a',3.4)] ==> [('a',4.4)]
  
  incrementKey :: k -> [(k,v)] -> [(k,v)]
  incrementKey = todo
  
  ------------------------------------------------------------------------------
  -- Ex 7: compute the average of a list of values of the Fractional
  -- class.
  --
  -- There is no need to handle the empty list case.
  --
  -- Hint! since Fractional is a subclass of Num, you have all
  -- arithmetic operations available
  --
  -- Hint! you can use the function fromIntegral to convert the list
  -- length to a Fractional
  
  average :: Fractional a => [a] -> a
  average xs = todo
  
  ------------------------------------------------------------------------------
  -- Ex 8: given a map from player name to score and two players, return
  -- the name of the player with more points. If the players are tied,
  -- return the name of the first player (that is, the name of the
  -- player who comes first in the argument list, player1).
  --
  -- If a player doesn't exist in the map, you can assume they have 0 points.
  --
  -- Hint: Map.findWithDefault can make this simpler
  --
  -- Examples:
  --   winner (Map.fromList [("Bob",3470),("Jane",2130),("Lisa",9448)]) "Jane" "Lisa"
  --     ==> "Lisa"
  --   winner (Map.fromList [("Mike",13607),("Bob",5899),("Lisa",5899)]) "Lisa" "Bob"
  --     ==> "Lisa"
  
  winner :: Map.Map String Int -> String -> String -> String
  winner scores player1 player2 = todo
  
  ------------------------------------------------------------------------------
  -- Ex 9: compute how many times each value in the list occurs. Return
  -- the frequencies as a Map from value to Int.
  --
  -- Challenge 1: try using Map.alter for this
  --
  -- Challenge 2: use foldr to process the list
  --
  -- Example:
  --   freqs [False,False,False,True]
  --     ==> Map.fromList [(False,3),(True,1)]
  
  freqs :: (Eq a, Ord a) => [a] -> Map.Map a Int
  freqs xs = todo
  
  ------------------------------------------------------------------------------
  -- Ex 10: recall the withdraw example from the course material. Write a
  -- similar function, transfer, that transfers money from one account
  -- to another.
  --
  -- However, the function should not perform the transfer if
  -- * the from account doesn't exist,
  -- * the to account doesn't exist,
  -- * the sum is negative,
  -- * or the from account doesn't have enough money.
  --
  -- Hint: there are many ways to implement this logic. Map.member or
  -- Map.notMember might help.
  --
  -- Examples:
  --   let bank = Map.fromList [("Bob",100),("Mike",50)]
  --   transfer "Bob" "Mike" 20 bank
  --     ==> fromList [("Bob",80),("Mike",70)]
  --   transfer "Bob" "Mike" 120 bank
  --     ==> fromList [("Bob",100),("Mike",50)]
  --   transfer "Bob" "Lisa" 20 bank
  --     ==> fromList [("Bob",100),("Mike",50)]
  --   transfer "Lisa" "Mike" 20 bank
  --     ==> fromList [("Bob",100),("Mike",50)]
  
  transfer :: String -> String -> Int -> Map.Map String Int -> Map.Map String Int
  transfer from to amount bank = todo
  
  ------------------------------------------------------------------------------
  -- Ex 11: given an Array and two indices, swap the elements in the indices.
  --
  -- Example:
  --   swap 2 3 (array (1,4) [(1,"one"),(2,"two"),(3,"three"),(4,"four")])
  --         ==> array (1,4) [(1,"one"),(2,"three"),(3,"two"),(4,"four")]
  
  swap :: Ix i => i -> i -> Array i a -> Array i a
  swap i j arr = todo
  
  ------------------------------------------------------------------------------
  -- Ex 12: given an Array, find the index of the largest element. You
  -- can assume the Array isn't empty.
  --
  -- You may assume that the largest element is unique.
  --
  -- Hint: check out Data.Array.indices or Data.Array.assocs
  
  maxIndex :: (Ix i, Ord a) => Array i a -> i
  maxIndex = todo
  