content: module Set16a where
  
  import Mooc.Todo
  import Test.QuickCheck
  
  import Data.List
  
  ------------------------------------------------------------------------------
  -- Ex 1: Write a Property that checks that a given list is sorted (in
  -- ascending order)
  --
  -- Examples:
  --  *Set16a> quickCheck (isSorted [1,2,3])
  --  +++ OK, passed 1 test.
  --  *Set16a> quickCheck (isSorted [1,3,2])
  --  *** Failed! Falsified (after 1 test):
  --  *Set16a> quickCheck (isSorted [])
  --  +++ OK, passed 1 test.
  
  isSorted :: (Show a, Ord a) => [a] -> Property
  isSorted = todo
  
  ------------------------------------------------------------------------------
  -- Ex 2: In this and the following exercises, we'll build a suite of
  -- tests for testing a function
  --
  --   frequencies :: Eq a => [a] -> [(a,Int)]
  --
  -- That counts how many times each element occurs in a list.
  --
  -- The tests will be properties that check that a given input (of type
  -- [a]) and output (of type [(a,Int)]) have a certain relationship.
  --
  -- For this exercise, implement a Property that checks that the sum
  -- all the Ints in the output is the length of the input.
  --
  -- Examples:
  -- (the exact output of the test doesn't matter, only whether it passes)
  --
  --  *Set16a> quickCheck (sumIsLength "abb" [('a',1),('b',2)])
  --  +++ OK, passed 1 test.
  --  *Set16a> quickCheck (sumIsLength "abb" [('a',1),('x',3),('z',-1)])
  --  +++ OK, passed 1 test.
  --  *Set16a> quickCheck (sumIsLength "abb" [('a',1),('b',1)])
  --  *** Failed! Falsified (after 1 test):
  --  3 /= 2
  --  *Set16a> quickCheck (sumIsLength "" [])
  --  +++ OK, passed 1 test.
  --  *Set16a> quickCheck (sumIsLength [4,5,6,4,5,4] (freq1 [4,5,6,4,5,4]))
  --  +++ OK, passed 1 test.
  
  sumIsLength :: Show a => [a] -> [(a,Int)] -> Property
  sumIsLength input output = todo
  
  -- This is a function that passes the sumIsLength test but is wrong
  freq1 :: Eq a => [a] -> [(a,Int)]
  freq1 [] = []
  freq1 [x] = [(x,1)]
  freq1 (x:y:xs) = [(x,1),(y,length xs + 1)]
  
  ------------------------------------------------------------------------------
  -- Ex 3: Implement a Property that takes an arbitrary element from the
  -- input, and checks that it occurs in the output.
  --
  -- You can assume that the input is nonempty.
  --
  -- Hint: Use forAll to pick an element from the input.
  --
  -- Examples:
  --  *Set16a> quickCheck (inputInOutput "abb" [('a',1),('b',2)])
  --  +++ OK, passed 100 tests.
  --  *Set16a> quickCheck (inputInOutput "abb" [('a',1),('x',3),('z',-1)])
  --  *** Failed! Falsified (after 1 test):
  --  'b'
  --  *Set16a> quickCheck (inputInOutput [4,5,6,4,5,4] (freq1 [4,5,6,4,5,4]))
  --  *** Failed! Falsified (after 3 tests):
  --  6
  --  *Set16a> quickCheck (inputInOutput [4,5,6,4,5,4] (freq2 [4,5,6,4,5,4]))
  --  +++ OK, passed 100 tests.
  
  inputInOutput :: (Show a, Eq a) => [a] -> [(a,Int)] -> Property
  inputInOutput input output = todo
  
  -- This function passes both the sumIsLength and inputInOutput tests
  freq2 :: Eq a => [a] -> [(a,Int)]
  freq2 xs = map (\x -> (x,1)) xs
  
  ------------------------------------------------------------------------------
  -- Ex 4: Implement a Property that takes a pair (x,n) from the
  -- output, and checks that x occurs n times in the input.
  --
  -- You can assume that the output is nonempty.
  --
  -- Examples:
  --  *Set16a> quickCheck (outputInInput "abb" [('a',1)])
  --  +++ OK, passed 100 tests.
  --  *Set16a> quickCheck (outputInInput "abb" [('a',1),('x',3)])
  --  *** Failed! Falsified (after 1 test):
  --  ('x',3)
  --  0 /= 3
  --  *Set16a> quickCheck (outputInInput "abb" [('a',1),('b',3)])
  --  *** Failed! Falsified (after 3 tests):
  --  ('b',3)
  --  2 /= 3
  --  *Set16a> quickCheck (outputInInput [4,5,6,4,5,4] (freq2 [4,5,6,4,5,4]))
  --  *** Failed! Falsified (after 1 test):
  --  (4,1)
  --  3 /= 1
  --  *Set16a> quickCheck (outputInInput [4,5,6,4,5,4] (freq3 [4,5,6,4,5,4]))
  --  +++ OK, passed 100 tests.
  
  outputInInput :: (Show a, Eq a) => [a] -> [(a,Int)] -> Property
  outputInInput input output = todo
  
  -- This function passes the outputInInput test but not the others
  freq3 :: Eq a => [a] -> [(a,Int)]
  freq3 [] = []
  freq3 (x:xs) = [(x,1 + length (filter (==x) xs))]
  
  ------------------------------------------------------------------------------
  -- Ex 5: Implement a Property that takes a candidate function freq, a
  -- NonEmptyList Char input, and checks that all the three properties
  -- (sumIsLength, inputInOutput and outputInInput) hold between the
  -- input and the output from freq.
  --
  -- Hint: Use conjoin or (.&&.) to "and" together properties.
  --
  -- Examples:
  --  *Set16a> quickCheck (frequenciesProp freq1)
  --  *** Failed! Falsified (after 5 tests and 5 shrinks):
  --  ...
  --  *Set16a> quickCheck (frequenciesProp freq2)
  --  *** Failed! Falsified (after 20 tests and 2 shrinks):
  --  ...
  --  *Set16a> quickCheck (frequenciesProp freq3)
  --  *** Failed! Falsified (after 1 test and 2 shrinks):
  --  ...
  --  *Set16a> quickCheck (frequenciesProp frequencies)
  --  +++ OK, passed 100 tests.
  
  frequenciesProp :: ([Char] -> [(Char,Int)]) -> NonEmptyList Char -> Property
  frequenciesProp freq input = todo
  
  frequencies :: Eq a => [a] -> [(a,Int)]
  frequencies [] = []
  frequencies (x:ys) = (x, length xs) : frequencies others
    where (xs,others) = partition (==x) (x:ys)
  
  ------------------------------------------------------------------------------
  -- Ex 6: Write a generator for lists that have these properties:
  -- * length 3-5
  -- * elements are numbers from 0 to 10
  -- * the list is sorted
  --
  -- Hints: Remember the generators `elements` and `choose`, but also
  -- check out `vectorOf`, and don't be afraid to use `sort`.
  --
  -- Example:
  --  *Set16a> sample genList
  --  [0,1,2,8]
  --  [3,5,7,8,10]
  --  [6,7,9]
  --  [2,2,3]
  --  [0,5,7,10]
  --  [5,8,10]
  --  [1,6,8]
  --  [1,5,8,9]
  --  [2,3,4,6,8]
  --  [0,1,8]
  --  [2,4,10]
  
  genList :: Gen [Int]
  genList = todo
  
  ------------------------------------------------------------------------------
  -- Ex 7: Here are the datatypes Arg and Expression from Set 15. Write
  -- Arbitrary instances for Expression and Arg such that:
  --
  -- * All combinations of Plus, Minus, Number, Variable are produced
  -- * All numbers are in the range 0-10
  -- * All variables are one of a,b,c,x,y,z
  --
  -- You don't need to implement shrink, just arbitrary.
  --
  -- Hint: use
  --   oneof :: [Gen a] -> Gen a
  -- to randomly pick from a set of generators
  --
  -- Examples:
  --  *Set16a> sample (arbitrary :: Gen Arg)
  --  Number 3
  --  Variable 'c'
  --  Variable 'z'
  --  Number 5
  --  Number 6
  --  Variable 'x'
  --  *Set16a> sample (arbitrary :: Gen Expression)
  --  Minus (Variable 'b') (Variable 'c')
  --  Plus (Variable 'c') (Number 0)
  --  Minus (Number 0) (Variable 'c')
  --  Minus (Number 0) (Number 7)
  --  Minus (Number 8) (Number 5)
  
  data Arg = Number Int | Variable Char
    deriving (Show, Eq)
  
  data Expression = Plus Arg Arg | Minus Arg Arg
    deriving (Show, Eq)
  
  instance Arbitrary Arg where
    arbitrary = todo
  
  instance Arbitrary Expression where
    arbitrary = todo
  