(** * Poly: Polymorphism and Higher-Order Functions *)
Set Warnings "-extraction-opaque-accessed,-extraction".
Set Warnings "-notation-overridden,-parsing".
(* Require Export Lists. *)
(* Inductive list (X:Type) : Type := | nil : list X | cons : X -> list X -> list X. Derive Arbitrary for list. Derive Show for list. Instance list_eq (X : Type) (H : forall (a b : X), Dec (a = b)) (x y : list X) : Dec (x = y). constructor. unfold ssrbool.decidable. repeat (decide equality). apply H. Defined. *)
Fixpoint repeat (X : Type) (x : X) (count : nat) : list X :=
match count with
| 0 => nil
| S count' => cons x (repeat X x count')
end.
(** As with [nil] and [cons], we can use [repeat] by applying it first to a type and then to its list argument: *)
QuickChick (
(repeat nat 4 2 = cons 4 (cons 4 (nil)))? ).
Arguments repeat {X} x count.
Notation "x :: y" := (cons x y)
(at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y []) ..).
Notation "x ++ y" := (app x y)
(at level 60, right associativity).
Fixpoint combine {X Y : Type} (lx : list X) (ly : list Y)
: list (X*Y) :=
match lx, ly with
| [], _ => []
| _, [] => []
| x :: tx, y :: ty => (x, y) :: (combine tx ty)
end.
(** **** Exercise: 2 stars, recommended (split)  *)
(** The function [split] is the right inverse of [combine]: it takes a list of pairs and returns a pair of lists.  In many functional languages, it is called [unzip].  Fill in the definition of [split] below.  Make sure it passes the given unit test. *)
Fixpoint split {X Y : Type} (l : list (X*Y))
: (list X) * (list Y) := ([],[]).
Definition split_combineP l1 l2 :=
(split (combine l1 l2) = (l1,l2))?.
QuickChick (expectFailure split_combineP).
Fixpoint filter {X:Type} (test: X->bool) (l:list X)
: (list X) :=
match l with
| []     => []
| h :: t => if test h then h :: (filter test t)
else       filter test t
end.
(** **** Exercise: 3 stars (partition)  *)
(** Use [filter] to write a Coq function [partition]:  partition : forall X : Type, (X -> bool) -> list X -> list X * list X  Given a set [X], a test function of type [X -> bool] and a [list X], [partition] should return a pair of lists.  The first member of the pair is the sublist of the original list containing the elements that satisfy the test, and the second is the sublist containing those that fail the test.  The order of elements in the two sublists should be the same as their order in the original list. *)
Definition partition {X : Type}
(test : X -> bool)
(l : list X)
: list X * list X
(* REPLACE THIS LINE WITH ":= _your_definition_ ." *) := ([],[]).  (* QuickCheck (fun {X:Type} (l: list X) => checker false). ==> Error: Conversion test raised an anomaly *)
Open Scope string.
Instance show_natfun : Show (nat -> bool) :=
{|
show f := "{"
++ "0 |-> " ++ (show (f 0) )
++ ", 1 |-> " ++ (show (f 1) )
++ ", 2 |-> " ++ (show (f 2) )
++ ", 3 |-> " ++ (show (f 3) )
++ ", 4 |-> " ++ (show (f 4))
++ "}"
|}.
QuickCheck (fun (test: nat -> bool) (l: list nat) => checker false).
Fixpoint map {X Y:Type} (f:X->Y) (l:list X) : (list Y) :=
match l with
| []     => []
| h :: t => (f h) :: (map f t)
end.

(* Specifications: *)
Theorem map_rev : forall (X Y : Type) (f : X -> Y) (l : list X),
map f (rev l) = rev (map f l).