content: -- Exercise set 7
  
  module Set7 where
  
  import Mooc.Todo
  import Data.List
  import Data.List.NonEmpty (NonEmpty ((:|)))
  import Data.Monoid
  import Data.Semigroup
  
  ------------------------------------------------------------------------------
  -- Ex 1: you'll find below the types Time, Distance and Velocity,
  -- which represent time, distance and velocity in seconds, meters and
  -- meters per second.
  --
  -- Implement the functions below.
  
  data Distance = Distance Double
    deriving (Show,Eq)
  
  data Time = Time Double
    deriving (Show,Eq)
  
  data Velocity = Velocity Double
    deriving (Show,Eq)
  
  -- velocity computes a velocity given a distance and a time
  velocity :: Distance -> Time -> Velocity
  velocity = todo
  
  -- travel computes a distance given a velocity and a time
  travel :: Velocity -> Time -> Distance
  travel = todo
  
  ------------------------------------------------------------------------------
  -- Ex 2: let's implement a simple Set datatype. A Set is a list of
  -- unique elements. The set is always kept ordered.
  --
  -- Implement the functions below. You might need to add class
  -- constraints to the functions' types.
  --
  -- Examples:
  --   member 'a' (Set ['a','b','c'])  ==>  True
  --   add 2 (add 3 (add 1 emptySet))  ==>  Set [1,2,3]
  --   add 1 (add 1 emptySet)  ==>  Set [1]
  
  data Set a = Set [a]
    deriving (Show,Eq)
  
  -- emptySet is a set with no elements
  emptySet :: Set a
  emptySet = todo
  
  -- member tests if an element is in a set
  member :: Eq a => a -> Set a -> Bool
  member = todo
  
  -- add a member to a set
  add :: a -> Set a -> Set a
  add = todo
  
  ------------------------------------------------------------------------------
  -- Ex 3: a state machine for baking a cake. The type Event represents
  -- things that can happen while baking a cake. The type State is meant
  -- to represent the states a cake can be in.
  --
  -- Your job is to
  --
  --  * add new states to the State type
  --  * and implement the step function
  --
  -- so that they have the following behaviour:
  --
  --  * Baking starts in the Start state
  --  * A successful cake (reperesented by the Finished value) is baked
  --    by first adding eggs, then adding flour and sugar (flour and
  --    sugar can be added in which ever order), then mixing, and
  --    finally baking.
  --  * If the order of Events differs from this, the result is an Error cake.
  --    No Events can save an Error cake.
  --  * Once a cake is Finished, it stays Finished even if additional Events happen.
  --
  -- The function bake just calls step repeatedly. It's used for the
  -- examples below. Don't modify it.
  --
  -- Examples:
  --   bake [AddEggs,AddFlour,AddSugar,Mix,Bake]  ==>  Finished
  --   bake [AddEggs,AddFlour,AddSugar,Mix,Bake,AddSugar,Mix]  ==> Finished
  --   bake [AddFlour]  ==>  Error
  --   bake [AddEggs,AddFlour,Mix]  ==>  Error
  
  data Event = AddEggs | AddFlour | AddSugar | Mix | Bake
    deriving (Eq,Show)
  
  data State = Start | Error | Finished
    deriving (Eq,Show)
  
  step = todo
  
  -- do not edit this
  bake :: [Event] -> State
  bake events = go Start events
    where go state [] = state
          go state (e:es) = go (step state e) es
  
  ------------------------------------------------------------------------------
  -- Ex 4: remember how the average function from Set4 couldn't really
  -- work on empty lists? Now we can reimplement average for NonEmpty
  -- lists and avoid the edge case.
  --
  -- PS. The Data.List.NonEmpty type has been imported for you
  --
  -- Examples:
  --   average (1.0 :| [])  ==>  1.0
  --   average (1.0 :| [2.0,3.0])  ==>  2.0
  
  average :: Fractional a => NonEmpty a -> a
  average = todo
  
  ------------------------------------------------------------------------------
  -- Ex 5: reverse a NonEmpty list.
  --
  -- PS. The Data.List.NonEmpty type has been imported for you
  
  reverseNonEmpty :: NonEmpty a -> NonEmpty a
  reverseNonEmpty = todo
  
  ------------------------------------------------------------------------------
  -- Ex 6: implement Semigroup instances for the Distance, Time and
  -- Velocity types from exercise 1. The instances should perform
  -- addition.
  --
  -- When you've defined the instances you can do things like this:
  --
  -- velocity (Distance 50 <> Distance 10) (Time 1 <> Time 2)
  --    ==> Velocity 20
  
  
  ------------------------------------------------------------------------------
  -- Ex 7: implement a Monoid instance for the Set type from exercise 2.
  -- The (<>) operation should be the union of sets.
  --
  -- What's the right definition for mempty?
  --
  -- What are the class constraints for the instances?
  
  
  ------------------------------------------------------------------------------
  -- Ex 8: below you'll find two different ways of representing
  -- calculator operations. The type Operation1 is a closed abstraction,
  -- while the class Operation2 is an open abstraction.
  --
  -- Your task is to add:
  --  * a multiplication case to Operation1 and Operation2
  --    (named Multiply1 and Multiply2, respectively)
  --  * functions show1 and show2 that render values of
  --    Operation1 and Operation2 to strings
  --
  -- Examples:
  --   compute1 (Multiply1 2 3) ==> 6
  --   compute2 (Multiply2 2 3) ==> 6
  --   show1 (Add1 2 3) ==> "2+3"
  --   show1 (Multiply1 4 5) ==> "4*5"
  --   show2 (Subtract2 2 3) ==> "2-3"
  --   show2 (Multiply2 4 5) ==> "4*5"
  
  data Operation1 = Add1 Int Int
                  | Subtract1 Int Int
    deriving Show
  
  compute1 :: Operation1 -> Int
  compute1 (Add1 i j) = i+j
  compute1 (Subtract1 i j) = i-j
  
  show1 :: Operation1 -> String
  show1 = todo
  
  data Add2 = Add2 Int Int
    deriving Show
  data Subtract2 = Subtract2 Int Int
    deriving Show
  
  class Operation2 op where
    compute2 :: op -> Int
  
  instance Operation2 Add2 where
    compute2 (Add2 i j) = i+j
  
  instance Operation2 Subtract2 where
    compute2 (Subtract2 i j) = i-j
  
  
  ------------------------------------------------------------------------------
  -- Ex 9: validating passwords. Below you'll find a type
  -- PasswordRequirement describing possible requirements for passwords.
  --
  -- Implement the function passwordAllowed that checks whether a
  -- password is allowed.
  --
  -- Examples:
  --   passwordAllowed "short" (MinimumLength 8) ==> False
  --   passwordAllowed "veryLongPassword" (MinimumLength 8) ==> True
  --   passwordAllowed "password" (ContainsSome "0123456789") ==> False
  --   passwordAllowed "p4ssword" (ContainsSome "0123456789") ==> True
  --   passwordAllowed "password" (DoesNotContain "0123456789") ==> True
  --   passwordAllowed "p4ssword" (DoesNotContain "0123456789") ==> False
  --   passwordAllowed "p4ssword" (And (ContainsSome "1234") (MinimumLength 5)) ==> True
  --   passwordAllowed "p4ss" (And (ContainsSome "1234") (MinimumLength 5)) ==> False
  --   passwordAllowed "p4ss" (Or (ContainsSome "1234") (MinimumLength 5)) ==> True
  
  data PasswordRequirement =
    MinimumLength Int
    | ContainsSome String    -- contains at least one of given characters
    | DoesNotContain String  -- does not contain any of the given characters
    | And PasswordRequirement PasswordRequirement -- and'ing two requirements
    | Or PasswordRequirement PasswordRequirement  -- or'ing
    deriving Show
  
  passwordAllowed :: String -> PasswordRequirement -> Bool
  passwordAllowed = todo
  
  ------------------------------------------------------------------------------
  -- Ex 10: a DSL for simple arithmetic expressions with addition and
  -- multiplication. Define the type Arithmetic so that it can express
  -- expressions like this. Define the functions literal and operation
  -- for creating Arithmetic values.
  --
  -- Define two interpreters for Arithmetic: evaluate should compute the
  -- expression, and render should show the expression as a string.
  --
  -- Examples:
  --   evaluate (literal 3) ==> 3
  --   render   (literal 3) ==> "3"
  --   evaluate (operation "+" (literal 3) (literal 4)) ==> 7
  --   render   (operation "+" (literal 3) (literal 4)) ==> "(3+4)"
  --   evaluate (operation "*" (literal 3) (operation "+" (literal 1) (literal 1)))
  --     ==> 6
  --   render   (operation "*" (literal 3) (operation "+" (literal 1) (literal 1)))
  --     ==> "(3*(1+1))"
  --
  
  data Arithmetic = Todo
    deriving Show
  
  literal :: Integer -> Arithmetic
  literal = todo
  
  operation :: String -> Arithmetic -> Arithmetic -> Arithmetic
  operation = todo
  
  evaluate :: Arithmetic -> Integer
  evaluate = todo
  
  render :: Arithmetic -> String
  render = todo
  