(** * Lists: Working with Structured Data *)

Require Export Induction.
Module NatList.

(* Problem examples *)
(* filterList in terms of a fold does not satisfy any proofs *)
(* but in terms of pattern matching it does *)
(* Coq could not determine that subset was reducing in size when in a fold *)
(* nonzeros when expressed with a filter became hard to prove *)
(* same with remove decrease count, if count is expressed via a fold it becomes very hard to prove. *)

(* ################################################################# *)
(** * Pairs of Numbers *)

(** In an [Inductive] type definition, each constructor can take
    any number of arguments -- none (as with [true] and [O]), one (as
    with [S]), or more than one, as here: *)

Inductive natprod : Type :=
| pair : nat -> nat -> natprod.

(** This declaration can be read: "There is just one way to
    construct a pair of numbers: by applying the constructor [pair] to
    two arguments of type [nat]." *)

Check (pair 3 5).

(** Here are two simple functions for extracting the first and
    second components of a pair.  The definitions also illustrate how
    to do pattern matching on two-argument constructors. *)

Definition fst (p : natprod) : nat :=
  match p with
  | pair x y => x
  end.

Definition snd (p : natprod) : nat :=
  match p with
  | pair x y => y
  end.

Compute (fst (pair 3 5)).
(* ===> 3 *)

(** Since pairs are used quite a bit, it is nice to be able to
    write them with the standard mathematical notation [(x,y)] instead
    of [pair x y].  We can tell Coq to allow this with a [Notation]
    declaration. *)

Notation "( x , y )" := (pair x y).

(** The new pair notation can be used both in expressions and in
    pattern matches (indeed, we've actually seen this already in the
    [Basics] chapter, in the definition of the [minus] function --
    this works because the pair notation is also provided as part of
    the standard library): *)

Compute (fst (3,5)).

Definition fst' (p : natprod) : nat :=
  match p with
  | (x,y) => x
  end.

Definition snd' (p : natprod) : nat :=
  match p with
  | (x,y) => y
  end.

Definition swap_pair (p : natprod) : natprod :=
  match p with
  | (x,y) => (y,x)
  end.

(** Let's try to prove a few simple facts about pairs.

    If we state things in a particular (and slightly peculiar) way, we
    can complete proofs with just reflexivity (and its built-in
    simplification): *)

Theorem surjective_pairing' : forall (n m : nat),
  (n,m) = (fst (n,m), snd (n,m)).
Proof.
  reflexivity.  Qed.

(** But [reflexivity] is not enough if we state the lemma in a more
    natural way: *)

Theorem surjective_pairing_stuck : forall (p : natprod),
  p = (fst p, snd p).
Proof.
  simpl. (* Doesn't reduce anything! *)
Abort.

(** We have to expose the structure of [p] so that [simpl] can
    perform the pattern match in [fst] and [snd].  We can do this with
    [destruct]. *)

Theorem surjective_pairing : forall (p : natprod),
  p = (fst p, snd p).
Proof.
  intros p.  destruct p as [n m].  simpl.  reflexivity.  Qed.

(** Notice that, unlike its behavior with [nat]s, [destruct]
    generates just one subgoal here.  That's because [natprod]s can
    only be constructed in one way. *)

(** **** Exercise: 1 star (snd_fst_is_swap)  *)
Theorem snd_fst_is_swap : forall (p : natprod),
  (snd p, fst p) = swap_pair p.
Proof.
  intros p. destruct p as [n m]. simpl. reflexivity. Qed.
(** [] *)

(** **** Exercise: 1 star, optional (fst_swap_is_snd)  *)
Theorem fst_swap_is_snd : forall (p : natprod),
  fst (swap_pair p) = snd p.
Proof.
  intros p. destruct p as [n m]. simpl. reflexivity. Qed.
(** [] *)

(* ################################################################# *)
(** * Lists of Numbers *)

(** Generalizing the definition of pairs, we can describe the
    type of _lists_ of numbers like this: "A list is either the empty
    list or else a pair of a number and another list." *)

Inductive natlist : Type :=
  | nil  : natlist
  | cons : nat -> natlist -> natlist.

(** For example, here is a three-element list: *)

Definition mylist := cons 1 (cons 2 (cons 3 nil)).

(** As with pairs, it is more convenient to write lists in
    familiar programming notation.  The following declarations
    allow us to use [::] as an infix [cons] operator and square
    brackets as an "outfix" notation for constructing lists. *)

Notation "x :: l" := (cons x l)
                     (at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).

(** It is not necessary to understand the details of these
    declarations, but in case you are interested, here is roughly
    what's going on.  The [right associativity] annotation tells Coq
    how to parenthesize expressions involving several uses of [::] so
    that, for example, the next three declarations mean exactly the
    same thing: *)

Definition mylist1 := 1 :: (2 :: (3 :: nil)).
Definition mylist2 := 1 :: 2 :: 3 :: nil.
Definition mylist3 := [1;2;3].

(** The [at level 60] part tells Coq how to parenthesize
    expressions that involve both [::] and some other infix operator.
    For example, since we defined [+] as infix notation for the [plus]
    function at level 50,

  Notation "x + y" := (plus x y)
                      (at level 50, left associativity).

    the [+] operator will bind tighter than [::], so [1 + 2 :: [3]]
    will be parsed, as we'd expect, as [(1 + 2) :: [3]] rather than [1
    + (2 :: [3])].

    (Expressions like "[1 + 2 :: [3]]" can be a little confusing when
    you read them in a [.v] file.  The inner brackets, around 3, indicate
    a list, but the outer brackets, which are invisible in the HTML
    rendering, are there to instruct the "coqdoc" tool that the bracketed
    part should be displayed as Coq code rather than running text.)

    The second and third [Notation] declarations above introduce the
    standard square-bracket notation for lists; the right-hand side of
    the third one illustrates Coq's syntax for declaring n-ary
    notations and translating them to nested sequences of binary
    constructors. *)

(* ----------------------------------------------------------------- *)
(** *** Repeat *)

(** A number of functions are useful for manipulating lists.
    For example, the [repeat] function takes a number [n] and a
    [count] and returns a list of length [count] where every element
    is [n]. *)

Fixpoint repeat (n count : nat) : natlist :=
  match count with
  | O => nil
  | S count' => n :: (repeat n count')
  end.

(* ----------------------------------------------------------------- *)
(** *** Length *)

(** The [length] function calculates the length of a list. *)

Fixpoint length (l:natlist) : nat :=
  match l with
  | nil => O
  | h :: t => S (length t)
  end.

(* ----------------------------------------------------------------- *)
(** *** Append *)

(** The [app] function concatenates (appends) two lists. *)

Fixpoint app (l1 l2 : natlist) : natlist :=
  match l1 with
  | nil    => l2
  | h :: t => h :: (app t l2)
  end.

(** Actually, [app] will be used a lot in some parts of what
    follows, so it is convenient to have an infix operator for it. *)

Notation "x ++ y" := (app x y)
                     (right associativity, at level 60).

Example test_app1:             [1;2;3] ++ [4;5] = [1;2;3;4;5].
Proof. reflexivity.  Qed.
Example test_app2:             nil ++ [4;5] = [4;5].
Proof. reflexivity.  Qed.
Example test_app3:             [1;2;3] ++ nil = [1;2;3].
Proof. reflexivity.  Qed.

(* ----------------------------------------------------------------- *)
(** *** Head (with default) and Tail *)

(** Here are two smaller examples of programming with lists.
    The [hd] function returns the first element (the "head") of the
    list, while [tl] returns everything but the first
    element (the "tail").
    Of course, the empty list has no first element, so we
    must pass a default value to be returned in that case.  *)

Definition hd (default:nat) (l:natlist) : nat :=
  match l with
  | nil => default
  | h :: t => h
  end.

Definition tl (l:natlist) : natlist :=
  match l with
  | nil => nil
  | h :: t => t
  end.

Example test_hd1:             hd 0 [1;2;3] = 1.
Proof. reflexivity.  Qed.
Example test_hd2:             hd 0 [] = 0.
Proof. reflexivity.  Qed.
Example test_tl:              tl [1;2;3] = [2;3].
Proof. reflexivity.  Qed.

(* ----------------------------------------------------------------- *)
(** *** Exercises *)

(** **** Exercise: 2 stars, recommended (list_funs)  *)
(** Complete the definitions of [nonzeros], [oddmembers] and
    [countoddmembers] below. Have a look at the tests to understand
    what these functions should do. *)

Fixpoint foldList {b : Type} (f : b -> nat -> b) (acc : b)  (l : natlist) : b :=
  match l with
  | nil => acc
  | h :: t => foldList f (f acc h) t
  end.

Arguments foldList [b].

Definition mapList (f : nat -> nat) (l : natlist) : natlist :=
  let
    myf := fun acc n => f n :: acc
  in
  foldList myf [] l.

(* Why doesn't this work? *)
Definition filterList' (f : nat -> bool) (l : natlist) : natlist :=
  let
    myf := fun acc n => if f n
                     then n :: acc
                     else acc
  in
  foldList myf [] l.

(* But this does? *)
Fixpoint filterList (f : nat -> bool) (l : natlist) : natlist :=
  match l with
  | nil => nil
  | h :: t => if f h
             then h :: filterList f t
             else filterList f t
  end.

Fixpoint nonzeros' (l:natlist) : natlist :=
  match l with
  | nil => nil
  | 0 :: t => nonzeros' t
  | h :: t => h :: nonzeros' t
  end.

Definition isZero (n : nat) : bool :=
  match n with
    | 0 => true
    | _ => false
  end.

Fixpoint nonzeros (l:natlist) : natlist := filterList (fun x => negb (isZero x)) l.

Example test_nonzeros:
  nonzeros' [0;1;0;2;3;0;0] = [1;2;3].
Proof. simpl. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.test_nonzeros *)

Fixpoint oddmembers (l:natlist) : natlist := filterList (fun a => negb (evenb a)) l.
  (* match l with *)
  (* | nil => nil *)
  (* | h :: t => if (negB (even)) *)

Example test_oddmembers:
  oddmembers [0;1;0;2;3;0;0] = [1;3].
Proof. simpl. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.test_oddmembers *)

Definition countoddmembers (l:natlist) : nat := length (oddmembers l).

Example test_countoddmembers1:
  countoddmembers [1;0;3;1;4;5] = 4.
Proof. simpl. reflexivity. Qed.

Example test_countoddmembers2:
  countoddmembers [0;2;4] = 0.
Proof. simpl. reflexivity. Qed.

Example test_countoddmembers3:
  countoddmembers nil = 0.
Proof. simpl. reflexivity. Qed.
(** [] *)

(** **** Exercise: 3 stars, advanced (alternate)  *)
(** Complete the definition of [alternate], which "zips up" two lists
    into one, alternating between elements taken from the first list
    and elements from the second.  See the tests below for more
    specific examples.

    Note: one natural and elegant way of writing [alternate] will fail
    to satisfy Coq's requirement that all [Fixpoint] definitions be
    "obviously terminating."  If you find yourself in this rut, look
    for a slightly more verbose solution that considers elements of
    both lists at the same time.  (One possible solution requires
    defining a new kind of pairs, but this is not the only way.)  *)

Fixpoint alternate (l1 l2 : natlist) : natlist :=
  match l1, l2 with
  | h1 :: t1, h2 :: t2 => h1 :: h2 :: alternate t1 t2
  | l1', nil => l1'
  | nil, l2' => l2'
  end.

Example test_alternate1:
  alternate [1;2;3] [4;5;6] = [1;4;2;5;3;6].
Proof. simpl. reflexivity. Qed.

Example test_alternate2:
  alternate [1] [4;5;6] = [1;4;5;6].
Proof. simpl. reflexivity. Qed.

Example test_alternate3:
  alternate [1;2;3] [4] = [1;4;2;3].
Proof. simpl. reflexivity. Qed.

Example test_alternate4:
  alternate [] [20;30] = [20;30].
Proof. simpl. reflexivity. Qed.

(** [] *)

(* ----------------------------------------------------------------- *)
(** *** Bags via Lists *)

(** A [bag] (or [multiset]) is like a set, except that each element
    can appear multiple times rather than just once.  One possible
    implementation is to represent a bag of numbers as a list. *)

Definition bag := natlist.

(** **** Exercise: 3 stars, recommended (bag_functions)  *)
(** Complete the following definitions for the functions
    [count], [sum], [add], and [member] for bags. *)

Fixpoint count (v:nat) (s:bag) : nat := length (filterList (beq_nat v) s).

(** All these proofs can be done just by [reflexivity]. *)

Example test_count1:              count 1 [1;2;3;1;4;1] = 3.
Proof. simpl. reflexivity. Qed.

Example test_count2:              count 6 [1;2;3;1;4;1] = 0.
Proof. simpl. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.test_count2 *)

(** Multiset [sum] is similar to set [union]: [sum a b] contains all
    the elements of [a] and of [b].  (Mathematicians usually define
    [union] on multisets a little bit differently -- using max instead
    of sum -- which is why we don't use that name for this operation.)
    For [sum] we're giving you a header that does not give explicit
    names to the arguments.  Moreover, it uses the keyword
    [Definition] instead of [Fixpoint], so even if you had names for
    the arguments, you wouldn't be able to process them recursively.
    The point of stating the question this way is to encourage you to
    think about whether [sum] can be implemented in another way --
    perhaps by using functions that have already been defined.  *)

Definition sum : bag -> bag -> bag :=
  alternate.

Example test_sum1:              count 1 (sum [1;2;3] [1;4;1]) = 3.
Proof. simpl. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.test_sum1 *)

Definition add (v:nat) (s:bag) : bag := app [v] s.

Example test_add1:                count 1 (add 1 [1;4;1]) = 3.
Proof. simpl. reflexivity. Qed.

Example test_add2:                count 5 (add 1 [1;4;1]) = 0.
Proof. simpl. reflexivity. Qed.

(* GRADE_THEOREM 0.5: NatList.test_add1 *)
(* GRADE_THEOREM 0.5: NatList.test_add2 *)

(* again, what is wrong with the fold definition *)
Definition member' (v:nat) (s:bag) : bool :=
  foldList (fun acc n => beq_nat n v || acc) false s.

Definition isNil (l :natlist) : bool :=
  match l with
  | nil => true
  | _ => false
  end.

Definition member (v:nat) (s:bag) : bool := negb (isNil (filterList (beq_nat v) s)).

Example test_member1:             member 1 [1;4;1] = true.
Proof. simpl. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.test_member1 *)
(* GRADE_THEOREM 0.5: NatList.test_member2 *)

Example test_member2:             member 2 [1;4;1] = false.
Proof. simpl. reflexivity. Qed.
(** [] *)

(** **** Exercise: 3 stars, optional (bag_more_functions)  *)
(** Here are some more [bag] functions for you to practice with. *)

(** When [remove_one] is applied to a bag without the number to remove,
   it should return the same bag unchanged. *)

Fixpoint remove_one (v:nat) (s:bag) : bag :=
  match s with
  | nil => nil
  | h :: t => if (beq_nat h v)
             then t
             else h :: remove_one v t
  end.

Example test_remove_one1:
  count 5 (remove_one 5 [2;1;5;4;1]) = 0.
Proof. simpl. reflexivity. Qed.

Example test_remove_one2:
  count 5 (remove_one 5 [2;1;4;1]) = 0.
Proof. simpl. reflexivity. Qed.

Example test_remove_one3:
  count 4 (remove_one 5 [2;1;4;5;1;4]) = 2.
Proof. simpl. reflexivity. Qed.

Example test_remove_one4:
  count 5 (remove_one 5 [2;1;5;4;5;1;4]) = 1.
Proof. simpl. reflexivity. Qed.

Fixpoint remove_all (v:nat) (s:bag) : bag := filterList (fun x => negb (beq_nat v x)) s.

Example test_remove_all1:  count 5 (remove_all 5 [2;1;5;4;1]) = 0.
Proof. simpl. reflexivity. Qed.
Example test_remove_all2:  count 5 (remove_all 5 [2;1;4;1]) = 0.
Proof. simpl. reflexivity. Qed.
Example test_remove_all3:  count 4 (remove_all 5 [2;1;4;5;1;4]) = 2.
Proof. simpl. reflexivity. Qed.
Example test_remove_all4:  count 5 (remove_all 5 [2;1;5;4;5;1;4;5;1;4]) = 0.
Proof. simpl. reflexivity. Qed.

(* damn, coq cannot determine the decreasing terms *)
(* Fixpoint subset' (s1:bag) (s2:bag) : bool := *)
(*   foldList (fun acc a => *)
(*               member a s2 && subset' (tl s1) (tl s2)) true s1. *)

Fixpoint subset (s1:bag) (s2:bag) : bool :=
  match s1 with
  | nil => true
  | h :: t => if member h s2
             then subset t (remove_one h s2)
             else false
  end.

Example test_subset1:              subset [1;2] [2;1;4;1] = true.
Proof. simpl. reflexivity. Qed.

Example test_subset2:              subset [1;2;2] [2;1;4;1] = false.
Proof. simpl. reflexivity. Qed.

Example test_subset3:              subset [2;1;4;1] [2;1;4;1] = true.
Proof. simpl. reflexivity. Qed.

(** [] *)

(** **** Exercise: 3 stars, recommended (bag_theorem)  *)
(** Write down an interesting theorem [bag_theorem] about bags
    involving the functions [count] and [add], and prove it.  Note
    that, since this problem is somewhat open-ended, it's possible
    that you may come up with a theorem which is true, but whose proof
    requires techniques you haven't learned yet.  Feel free to ask for
    help if you get stuck! *)

(*
Theorem bag_theorem : ...
Proof.
  ...
Qed.
*)

(** [] *)

(* ################################################################# *)
(** * Reasoning About Lists *)

(** As with numbers, simple facts about list-processing
    functions can sometimes be proved entirely by simplification.  For
    example, the simplification performed by [reflexivity] is enough
    for this theorem... *)

Theorem nil_app : forall l:natlist,
  [] ++ l = l.
Proof. reflexivity. Qed.

(** ...because the [[]] is substituted into the
    "scrutinee" (the expression whose value is being "scrutinized" by
    the match) in the definition of [app], allowing the match itself
    to be simplified. *)

(** Also, as with numbers, it is sometimes helpful to perform case
    analysis on the possible shapes (empty or non-empty) of an unknown
    list. *)

Theorem tl_length_pred : forall l:natlist,
  pred (length l) = length (tl l).
Proof.
  intros l. destruct l as [| n l'].
  - (* l = nil *)
    reflexivity.
  - (* l = cons n l' *)
    reflexivity.  Qed.

(** Here, the [nil] case works because we've chosen to define
    [tl nil = nil]. Notice that the [as] annotation on the [destruct]
    tactic here introduces two names, [n] and [l'], corresponding to
    the fact that the [cons] constructor for lists takes two
    arguments (the head and tail of the list it is constructing). *)

(** Usually, though, interesting theorems about lists require
    induction for their proofs. *)

(* ----------------------------------------------------------------- *)
(** *** Micro-Sermon *)

(** Simply reading example proof scripts will not get you very far!
    It is important to work through the details of each one, using Coq
    and thinking about what each step achieves.  Otherwise it is more
    or less guaranteed that the exercises will make no sense when you
    get to them.  'Nuff said. *)

(* ================================================================= *)
(** ** Induction on Lists *)

(** Proofs by induction over datatypes like [natlist] are a
    little less familiar than standard natural number induction, but
    the idea is equally simple.  Each [Inductive] declaration defines
    a set of data values that can be built up using the declared
    constructors: a boolean can be either [true] or [false]; a number
    can be either [O] or [S] applied to another number; a list can be
    either [nil] or [cons] applied to a number and a list.

    Moreover, applications of the declared constructors to one another
    are the _only_ possible shapes that elements of an inductively
    defined set can have, and this fact directly gives rise to a way
    of reasoning about inductively defined sets: a number is either
    [O] or else it is [S] applied to some _smaller_ number; a list is
    either [nil] or else it is [cons] applied to some number and some
    _smaller_ list; etc. So, if we have in mind some proposition [P]
    that mentions a list [l] and we want to argue that [P] holds for
    _all_ lists, we can reason as follows:

      - First, show that [P] is true of [l] when [l] is [nil].

      - Then show that [P] is true of [l] when [l] is [cons n l'] for
        some number [n] and some smaller list [l'], assuming that [P]
        is true for [l'].

    Since larger lists can only be built up from smaller ones,
    eventually reaching [nil], these two arguments together establish
    the truth of [P] for all lists [l].  Here's a concrete example: *)

Theorem app_assoc : forall l1 l2 l3 : natlist,
  (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
  intros l1 l2 l3. induction l1 as [| n l1' IHl1'].
  - (* l1 = nil *)
    reflexivity.
  - (* l1 = cons n l1' *)
    simpl. rewrite -> IHl1'. reflexivity.  Qed.

(** Notice that, as when doing induction on natural numbers, the
    [as...] clause provided to the [induction] tactic gives a name to
    the induction hypothesis corresponding to the smaller list [l1']
    in the [cons] case. Once again, this Coq proof is not especially
    illuminating as a static written document -- it is easy to see
    what's going on if you are reading the proof in an interactive Coq
    session and you can see the current goal and context at each
    point, but this state is not visible in the written-down parts of
    the Coq proof.  So a natural-language proof -- one written for
    human readers -- will need to include more explicit signposts; in
    particular, it will help the reader stay oriented if we remind
    them exactly what the induction hypothesis is in the second
    case. *)

(** For comparison, here is an informal proof of the same theorem. *)

(** _Theorem_: For all lists [l1], [l2], and [l3],
   [(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3)].

   _Proof_: By induction on [l1].

   - First, suppose [l1 = []].  We must show

       ([] ++ l2) ++ l3 = [] ++ (l2 ++ l3),

     which follows directly from the definition of [++].

   - Next, suppose [l1 = n::l1'], with

       (l1' ++ l2) ++ l3 = l1' ++ (l2 ++ l3)

     (the induction hypothesis). We must show

       ((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ (l2 ++ l3).

     By the definition of [++], this follows from

       n :: ((l1' ++ l2) ++ l3) = n :: (l1' ++ (l2 ++ l3)),

     which is immediate from the induction hypothesis.  [] *)

(* ----------------------------------------------------------------- *)
(** *** Reversing a List *)

(** For a slightly more involved example of inductive proof over
    lists, suppose we use [app] to define a list-reversing function
    [rev]: *)

Fixpoint rev (l:natlist) : natlist :=
  match l with
  | nil    => nil
  | h :: t => rev t ++ [h]
  end.

Example test_rev1:            rev [1;2;3] = [3;2;1].
Proof. reflexivity.  Qed.
Example test_rev2:            rev nil = nil.
Proof. reflexivity.  Qed.

(* ----------------------------------------------------------------- *)
(** *** Properties of [rev] *)

(** Now let's prove some theorems about our newly defined [rev].
    For something a bit more challenging than what we've seen, let's
    prove that reversing a list does not change its length.  Our first
    attempt gets stuck in the successor case... *)

Theorem rev_length_firsttry : forall l : natlist,
  length (rev l) = length l.
Proof.
  intros l. induction l as [| n l' IHl'].
  - (* l = [] *)
    reflexivity.
  - (* l = n :: l' *)
    (* This is the tricky case.  Let's begin as usual
       by simplifying. *)
    simpl.
    (* Now we seem to be stuck: the goal is an equality
       involving [++], but we don't have any useful equations
       in either the immediate context or in the global
       environment!  We can make a little progress by using
       the IH to rewrite the goal... *)
    rewrite <- IHl'.
    (* ... but now we can't go any further. *)
Abort.

(** So let's take the equation relating [++] and [length] that
    would have enabled us to make progress and prove it as a separate
    lemma. *)

Theorem app_length : forall l1 l2 : natlist,
  length (l1 ++ l2) = (length l1) + (length l2).
Proof.
  (* WORKED IN CLASS *)
  intros l1 l2. induction l1 as [| n l1' IHl1'].
  - (* l1 = nil *)
    reflexivity.
  - (* l1 = cons *)
    simpl. rewrite -> IHl1'. reflexivity.  Qed.

(** Note that, to make the lemma as general as possible, we
    quantify over _all_ [natlist]s, not just those that result from an
    application of [rev].  This should seem natural, because the truth
    of the goal clearly doesn't depend on the list having been
    reversed.  Moreover, it is easier to prove the more general
    property. *)

(** Now we can complete the original proof. *)

Theorem rev_length : forall l : natlist,
  length (rev l) = length l.
Proof.
  intros l. induction l as [| n l' IHl'].
  - (* l = nil *)
    reflexivity.
  - (* l = cons *)
    simpl. rewrite -> app_length, plus_comm.
    simpl. rewrite -> IHl'. reflexivity.  Qed.

(** For comparison, here are informal proofs of these two theorems:

    _Theorem_: For all lists [l1] and [l2],
       [length (l1 ++ l2) = length l1 + length l2].

    _Proof_: By induction on [l1].

    - First, suppose [l1 = []].  We must show

        length ([] ++ l2) = length [] + length l2,

      which follows directly from the definitions of
      [length] and [++].

    - Next, suppose [l1 = n::l1'], with

        length (l1' ++ l2) = length l1' + length l2.

      We must show

        length ((n::l1') ++ l2) = length (n::l1') + length l2).

      This follows directly from the definitions of [length] and [++]
      together with the induction hypothesis. [] *)

(** _Theorem_: For all lists [l], [length (rev l) = length l].

    _Proof_: By induction on [l].

      - First, suppose [l = []].  We must show

          length (rev []) = length [],

        which follows directly from the definitions of [length]
        and [rev].

      - Next, suppose [l = n::l'], with

          length (rev l') = length l'.

        We must show

          length (rev (n :: l')) = length (n :: l').

        By the definition of [rev], this follows from

          length ((rev l') ++ [n]) = S (length l')

        which, by the previous lemma, is the same as

          length (rev l') + length [n] = S (length l').

        This follows directly from the induction hypothesis and the
        definition of [length]. [] *)

(** The style of these proofs is rather longwinded and pedantic.
    After the first few, we might find it easier to follow proofs that
    give fewer details (which can easily work out in our own minds or
    on scratch paper if necessary) and just highlight the non-obvious
    steps.  In this more compressed style, the above proof might look
    like this: *)

(** _Theorem_:
     For all lists [l], [length (rev l) = length l].

    _Proof_: First, observe that [length (l ++ [n]) = S (length l)]
     for any [l] (this follows by a straightforward induction on [l]).
     The main property again follows by induction on [l], using the
     observation together with the induction hypothesis in the case
     where [l = n'::l']. [] *)

(** Which style is preferable in a given situation depends on
    the sophistication of the expected audience and how similar the
    proof at hand is to ones that the audience will already be
    familiar with.  The more pedantic style is a good default for our
    present purposes. *)

(* ================================================================= *)
(** ** [Search] *)

(** We've seen that proofs can make use of other theorems we've
    already proved, e.g., using [rewrite].  But in order to refer to a
    theorem, we need to know its name!  Indeed, it is often hard even
    to remember what theorems have been proven, much less what they
    are called.

    Coq's [Search] command is quite helpful with this.  Typing
    [Search foo] will cause Coq to display a list of all theorems
    involving [foo].  For example, try uncommenting the following line
    to see a list of theorems that we have proved about [rev]: *)

(*  Search rev. *)

(** Keep [Search] in mind as you do the following exercises and
    throughout the rest of the book; it can save you a lot of time!

    If you are using ProofGeneral, you can run [Search] with [C-c
    C-a C-a]. Pasting its response into your buffer can be
    accomplished with [C-c C-;]. *)

(* ================================================================= *)
(** ** List Exercises, Part 1 *)

(** **** Exercise: 3 stars (list_exercises)  *)
(** More practice with lists: *)

Theorem app_nil_r : forall l : natlist,
  l ++ [] = l.
Proof.
  intros l. simpl. induction l as [].
  - (* nil case *) reflexivity.
  - (* cons case *) simpl. rewrite -> IHl. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.app_nil_r *)

Theorem rev_app_distr: forall l1 l2 : natlist,
  rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof.
  intros. induction l1  as [].
  - (* nil case *) simpl. rewrite -> app_nil_r. reflexivity.
  - (* cons *) simpl. rewrite -> IHl1, app_assoc. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.rev_app_distr *)

Theorem rev_involutive : forall l : natlist,
  rev (rev l) = l.
Proof.
  intros l. induction l as [| n l' IHl'].

  - (* nil *) simpl. reflexivity.
  - (* cons *) simpl. rewrite -> rev_app_distr, IHl'. simpl. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.rev_involutive *)

(** There is a short solution to the next one.  If you find yourself
    getting tangled up, step back and try to look for a simpler
    way. *)

Theorem app_assoc4 : forall l1 l2 l3 l4 : natlist,
  l1 ++ (l2 ++ (l3 ++ l4)) = ((l1 ++ l2) ++ l3) ++ l4.
Proof.
  intros. rewrite -> app_assoc, app_assoc. reflexivity. Qed.
(* GRADE_THEOREM 0.5: NatList.app_assoc4 *)

(** An exercise about your implementation of [nonzeros]: *)

(* with my fold implementation this became hard to prove *)
Lemma nonzeros_app : forall l1 l2 : natlist,
  nonzeros' (l1 ++ l2) = (nonzeros' l1) ++ (nonzeros' l2).
Proof.
  intros.  induction l1  as [].
  - simpl. reflexivity.
  - destruct n as [| m].
    + simpl. rewrite -> IHl1. reflexivity.
    + simpl. rewrite -> IHl1. reflexivity. Qed.
(** [] *)

(** **** Exercise: 2 stars (beq_natlist)  *)
(** Fill in the definition of [beq_natlist], which compares
    lists of numbers for equality.  Prove that [beq_natlist l l]
    yields [true] for every list [l]. *)

Fixpoint beq_natlist' (l1 l2 : natlist) : bool := subset l1 l2 && subset l2 l1.
Fixpoint beq_natlist (l1 l2 : natlist) : bool :=
  match l1, l2 with
  | h1 :: t1, h2 :: t2 => beq_nat h1 h2 && beq_natlist t1 t2
  | nil     , nil      => true
  | _       , _        => false
  end.

Example test_beq_natlist1 :
  (beq_natlist nil nil = true).
Proof. reflexivity.  Qed.

Example test_beq_natlist2 :
  beq_natlist [1;2;3] [1;2;3] = true.
Proof. reflexivity.  Qed.

Example test_beq_natlist3 :
  beq_natlist [1;2;3] [1;2;4] = false.
Proof. reflexivity.  Qed.

Lemma beq_nat_eq : forall (n:nat),
    true = beq_nat n n.
Proof.
  intros. induction n as [].
  - simpl. reflexivity.
  - simpl. rewrite <- IHn. reflexivity. Qed.

Theorem beq_natlist_refl : forall l:natlist,
  true = beq_natlist l l.
Proof.
  intros. induction l as [].
  - reflexivity.
  - simpl. rewrite <- IHl. rewrite <- beq_nat_eq. simpl. reflexivity.
Qed.
(** [] *)

(* ================================================================= *)
(** ** List Exercises, Part 2 *)

(** Here are a couple of little theorems to prove about your
    definitions about bags above. *)

(** **** Exercise: 1 star (count_member_nonzero)  *)
Theorem count_member_nonzero : forall (s : bag),
  leb 1 (count 1 (1 :: s)) = true.
Proof.
  intros. simpl. reflexivity. Qed.
(** [] *)

(** The following lemma about [leb] might help you in the next exercise. *)

Theorem ble_n_Sn : forall n,
  leb n (S n) = true.
Proof.
  intros n. induction n as [| n' IHn'].
  - (* 0 *)
    simpl.  reflexivity.
  - (* S n' *)
    simpl.  rewrite IHn'.  reflexivity.  Qed.

(** **** Exercise: 3 stars, advanced (remove_decreases_count)  *)
Fixpoint count'' (v:nat) (s:bag) : nat :=
  match s with
  | nil => 0
  | h :: t => (if beq_nat v h then 1 else 0) + count'' v t
  end.

Fixpoint remove_one' (v:nat) (s:bag) : bag :=
    match s with
      | nil => nil
      | x :: xs => match (beq_nat x v) with
                     | true => xs
                     | false => x:: (remove_one' v xs)
                  end
    end.

Theorem remove_decreases_count: forall (s : bag),
  leb (count'' 0 (remove_one' 0 s)) (count'' 0 s) = true.
Proof.
  intros. induction s as [].
  - simpl. reflexivity.
  - destruct n as [| n'].
    + rewrite -> ble_n_Sn. reflexivity.
    + simpl. rewrite -> IHs. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 3 stars, optional (bag_count_sum)  *)
(** Write down an interesting theorem [bag_count_sum] about bags
    involving the functions [count] and [sum], and prove it using
    Coq.  (You may find that the difficulty of the proof depends on
    how you defined [count]!) *)
(* Ehhhhh no thanks *)
(** [] *)

(** **** Exercise: 4 stars, advanced (rev_injective)  *)
(** Prove that the [rev] function is injective -- that is,

    forall (l1 l2 : natlist), rev l1 = rev l2 -> l1 = l2.

    (There is a hard way and an easy way to do this.) *)

Theorem rev_inj: forall (l1 l2 : natlist),
    rev l1 = rev l2 -> l1 = l2.
Proof.
  intros.
  rewrite <- rev_involutive.
  rewrite <- H.
  rewrite -> rev_involutive.
  reflexivity. Qed.
(** [] *)

(* ################################################################# *)
(** * Options *)

(** Suppose we want to write a function that returns the [n]th
    element of some list.  If we give it type [nat -> natlist -> nat],
    then we'll have to choose some number to return when the list is
    too short... *)

Fixpoint nth_bad (l:natlist) (n:nat) : nat :=
  match l with
  | nil => 42  (* arbitrary! *)
  | a :: l' => match beq_nat n O with
               | true => a
               | false => nth_bad l' (pred n)
               end
  end.

(** This solution is not so good: If [nth_bad] returns [42], we
    can't tell whether that value actually appears on the input
    without further processing. A better alternative is to change the
    return type of [nth_bad] to include an error value as a possible
    outcome. We call this type [natoption]. *)

Inductive natoption : Type :=
  | Some : nat -> natoption
  | None : natoption.

(** We can then change the above definition of [nth_bad] to
    return [None] when the list is too short and [Some a] when the
    list has enough members and [a] appears at position [n]. We call
    this new function [nth_error] to indicate that it may result in an
    error. *)

Fixpoint nth_error (l:natlist) (n:nat) : natoption :=
  match l with
  | nil => None
  | a :: l' => match beq_nat n O with
               | true => Some a
               | false => nth_error l' (pred n)
               end
  end.

Example test_nth_error1 : nth_error [4;5;6;7] 0 = Some 4.
Proof. reflexivity. Qed.
Example test_nth_error2 : nth_error [4;5;6;7] 3 = Some 7.
Proof. reflexivity. Qed.
Example test_nth_error3 : nth_error [4;5;6;7] 9 = None.
Proof. reflexivity. Qed.

(** (In the HTML version, the boilerplate proofs of these
    examples are elided.  Click on a box if you want to see one.)

    This example is also an opportunity to introduce one more small
    feature of Coq's programming language: conditional
    expressions... *)

Fixpoint nth_error' (l:natlist) (n:nat) : natoption :=
  match l with
  | nil => None
  | a :: l' => if beq_nat n O then Some a
               else nth_error' l' (pred n)
  end.

(** Coq's conditionals are exactly like those found in any other
    language, with one small generalization.  Since the boolean type
    is not built in, Coq actually supports conditional expressions over
    _any_ inductively defined type with exactly two constructors.  The
    guard is considered true if it evaluates to the first constructor
    in the [Inductive] definition and false if it evaluates to the
    second. *)

(** The function below pulls the [nat] out of a [natoption], returning
    a supplied default in the [None] case. *)

Definition option_elim (d : nat) (o : natoption) : nat :=
  match o with
  | Some n' => n'
  | None => d
  end.

(** **** Exercise: 2 stars (hd_error)  *)
(** Using the same idea, fix the [hd] function from earlier so we don't
    have to pass a default element for the [nil] case.  *)

Definition hd_error (l : natlist) : natoption
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.

Example test_hd_error1 : hd_error [] = None.
 (* FILL IN HERE *) Admitted.

Example test_hd_error2 : hd_error [1] = Some 1.
 (* FILL IN HERE *) Admitted.

Example test_hd_error3 : hd_error [5;6] = Some 5.
 (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Exercise: 1 star, optional (option_elim_hd)  *)
(** This exercise relates your new [hd_error] to the old [hd]. *)

Theorem option_elim_hd : forall (l:natlist) (default:nat),
  hd default l = option_elim default (hd_error l).
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

End NatList.

(* ################################################################# *)
(** * Partial Maps *)

(** As a final illustration of how data structures can be defined in
    Coq, here is a simple _partial map_ data type, analogous to the
    map or dictionary data structures found in most programming
    languages. *)

(** First, we define a new inductive datatype [id] to serve as the
    "keys" of our partial maps. *)

Inductive id : Type :=
  | Id : nat -> id.

(** Internally, an [id] is just a number.  Introducing a separate type
    by wrapping each nat with the tag [Id] makes definitions more
    readable and gives us the flexibility to change representations
    later if we wish. *)

(** We'll also need an equality test for [id]s: *)

Definition beq_id (x1 x2 : id) :=
  match x1, x2 with
  | Id n1, Id n2 => beq_nat n1 n2
  end.

(** **** Exercise: 1 star (beq_id_refl)  *)
Theorem beq_id_refl : forall x, true = beq_id x x.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** Now we define the type of partial maps: *)

Module PartialMap.
Export NatList.

Inductive partial_map : Type :=
  | empty  : partial_map
  | record : id -> nat -> partial_map -> partial_map.

(** This declaration can be read: "There are two ways to construct a
    [partial_map]: either using the constructor [empty] to represent an
    empty partial map, or by applying the constructor [record] to
    a key, a value, and an existing [partial_map] to construct a
    [partial_map] with an additional key-to-value mapping." *)

(** The [update] function overrides the entry for a given key in a
    partial map (or adds a new entry if the given key is not already
    present). *)

Definition update (d : partial_map)
                  (x : id) (value : nat)
                  : partial_map :=
  record x value d.

(** Last, the [find] function searches a [partial_map] for a given
    key.  It returns [None] if the key was not found and [Some val] if
    the key was associated with [val]. If the same key is mapped to
    multiple values, [find] will return the first one it
    encounters. *)

Fixpoint find (x : id) (d : partial_map) : natoption :=
  match d with
  | empty         => None
  | record y v d' => if beq_id x y
                     then Some v
                     else find x d'
  end.

(** **** Exercise: 1 star (update_eq)  *)
Theorem update_eq :
  forall (d : partial_map) (x : id) (v: nat),
    find x (update d x v) = Some v.
Proof.
 (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Exercise: 1 star (update_neq)  *)
Theorem update_neq :
  forall (d : partial_map) (x y : id) (o: nat),
    beq_id x y = false -> find x (update d y o) = find x d.
Proof.
 (* FILL IN HERE *) Admitted.
(** [] *)
End PartialMap.

(** **** Exercise: 2 stars (baz_num_elts)  *)
(** Consider the following inductive definition: *)

Inductive baz : Type :=
  | Baz1 : baz -> baz
  | Baz2 : baz -> bool -> baz.

(** How _many_ elements does the type [baz] have?
    (Explain your answer in words, preferrably English.) *)

(* FILL IN HERE *)
(** [] *)


Theorem biconditional_elimination :
  forall (p q : Prop), (p <-> q) <-> ((p -> q) /\ (q -> p)).
Proof.
  intros p q.
  split.

  intro H.
  destruct H as [H G].
  split.

  apply H.
  apply G.

  intro H.
  destruct H as [H G].
  split.

  apply H.
  apply G.
Qed.

Theorem non_contradiction :
  forall (p : Prop), (p /\ ~p) <-> False.
Proof.
  intro p.
  split.

  intro H.
  destruct H as [H G].

  absurd p.
  apply G.
  apply H.

  intro H.
  contradict H.
Qed.

Theorem commutativity_and :
  forall (p q : Prop), p /\ q <-> q /\ p.
Proof.
  intros p q.
  split.

  intro H.
  destruct H as [H G].
  split.

  apply G.
  apply H.

  intro H.
  destruct H as [H G].
  split.

  apply G.
  apply H.
Qed.

Theorem commutativity_or :
  forall (p q : Prop), p \/ q <-> q \/ p.
Proof.
  intros p q.
  split.

  intro H.
  destruct H as [H | H].

  right.
  apply H.

  left.
  apply H.

  intro H.
  destruct H as [H | H].

  right.
  apply H.

  left.
  apply H.
Qed.

Theorem associativity_and :
  forall (p q r : Prop), p /\ (q /\ r) <-> (p /\ q) /\ r.
Proof.
  intros p q r.
  split.

  intro H.
  destruct H as [H G].
  destruct G as [G I].
  split.
  split.

  apply H.
  apply G.
  apply I.

  intro H.
  destruct H as [H G].
  destruct H as [H I].

  split.
  apply H.

  split.
  apply I.

  apply G.
Qed.

Theorem associativity_or :
  forall (p q r : Prop), p \/ (q \/ r) <-> (p \/ q) \/ r.
Proof.
  intros p q r.
  split.

  intro H.
  destruct H as [H | H].

  left.
  left.
  apply H.

  destruct H as [H | H].
  left.
  right.
  apply H.

  right.
  apply H.

  intro H.
  destruct H as [H | H].
  destruct H as [H | H].

  left.
  apply H.

  right.
  left.
  apply H.

  right.
  right.
  apply H.
Qed.

Theorem idempotency_and :
  forall (p : Prop), p /\ p <-> p.
Proof.
  intro p.
  split.

  intro H.
  destruct H as [H _].

  apply H.

  intro H.
  split.

  apply H.
  apply H.
Qed.

Theorem idempotency_or :
  forall (p : Prop), p \/ p <-> p.
Proof.
  intro p.
  split.

  intro H.
  destruct H as [H | G].

  apply H.
  apply G.

  intro H.
  left.

  apply H.
Qed.

Theorem absorption_and :
  forall (p q : Prop), (p /\ (p \/ q)) <-> p.
Proof.
  intros p q.
  split.

  intro H.
  destruct H as [H _].
  apply H.

  intro H.
  split.

  apply H.

  left.
  apply H.
Qed.


Require Import ZArith.
Section section_for_chapter_6.
Inductive month : Set :=
  | January : month
  | February : month
  | March : month
  | April : month
  | May : month
  | June : month
  | July : month
  | August : month
  | September : month
  | October : month
  | November : month
  | December : month.

(* 6.1 p127 *)
Inductive season : Set :=
  | Spring : season
  | Summer : season
  | Autumn : season
  | Winter : season.

(* 6.2 p127 *)
Check bool_ind.
Check bool_rec.

(* 6.3 p128 *)
Theorem bool_equal : forall b:bool, b = true \/ b = false.
Proof.
intros.
pattern b.
apply bool_ind;[left; reflexivity | right; reflexivity].
Qed.

Theorem bool_equal' : forall b:bool, b = true \/ b = false.
Proof(bool_ind
  (fun b => b = true \/ b = false)
  (or_introl (eq_refl true))
  (or_intror (eq_refl false))).

(* 6.4 p132 *)
Check month_rec.

Definition mtos := month_rect (fun m:month => season)
  Winter Winter Winter
  Spring Spring Spring
  Summer Summer Summer
  Autumn Autumn Autumn.

(* 6.5 p132 *)

Definition month_length (leap:bool) :=
  month_rec (fun m:month => nat)
  31 (if leap then 29 else 28) 31 30 31 30 31 31 30 31 30 31.

Fixpoint nat_even (n:nat) : bool :=
  match n with
    | O => true
    | (S O) => false
    | (S (S m)) => nat_even m
  end.

Definition month_length_even (leap:bool) (m:month):=
  if nat_even (month_length leap m) then true else false.

Eval compute in month_length_even true February.
Eval compute in month_length_even false February.

(* 6.6 p132 *)

Definition bool_not (b:bool) : bool := if b then false else true.

Definition bool_xor (b b':bool) : bool := if b then bool_not b' else b'.

Definition bool_and (b b':bool) : bool := if b then b' else false.

Definition bool_or (b b':bool) := if b then true else b'.

Definition bool_eq (b b':bool) := if b then b' else bool_not b'.

Theorem bool_xor_not_eq :
 forall b1 b2:bool, bool_xor b1 b2 = bool_not (bool_eq b1 b2).
Proof.
intros.
case b1. simpl; trivial.
case b2. simpl; trivial. simpl; trivial.
Qed.

Theorem bool_not_and :
 forall b1 b2:bool,
   bool_not (bool_and b1 b2) = bool_or (bool_not b1) (bool_not b2).
Proof.
intros.
case b1. simpl; trivial.
case b2. simpl; trivial. simpl; trivial.
Qed.

Theorem bool_not_not : forall b:bool, bool_not (bool_not b) = b.
Proof.
intros.
case b. simpl; trivial. simpl; trivial.
Qed.

Theorem bool_ex_middle : forall b:bool, bool_or b (bool_not b) = true.
Proof.
intros.
case b. simpl; trivial. simpl; trivial.
Qed.

Theorem bool_eq_reflect : forall b1 b2:bool, bool_eq b1 b2 = true -> b1 = b2.
Proof.
intros b1 b2.
case b1. simpl. intros. rewrite H. trivial.
case b2. simpl; trivial. simpl; trivial.
Qed.

Theorem bool_eq_reflect' : forall b1 b2:bool, bool_eq b1 b2 = true -> b1 = b2.
Proof.
  intros b1 b2. case b1; case b2; simpl; trivial.
  discriminate.
Qed.

Theorem bool_eq_reflect2 : forall b1 b2:bool, b1 = b2 -> bool_eq b1 b2 = true.
Proof.
intros b1 b2.
case b1. simpl. intros. rewrite H. trivial.
case b2. simpl; trivial. simpl; trivial.
Qed.

Theorem bool_eq_reflect2' : forall b1 b2:bool, b1 = b2 -> bool_eq b1 b2 = true.
Proof.
  intros b1 b2. case b1; case b2; simpl; trivial.
  discriminate.
Qed.

Theorem bool_not_or :
 forall b1 b2:bool,
   bool_not (bool_or b1 b2) = bool_and (bool_not b1) (bool_not b2).
Proof.
intros b1 b2.
case b1; case b2; simpl; trivial.
Qed.

Theorem bool_distr :
 forall b1 b2 b3:bool,
   bool_or (bool_and b1 b3) (bool_and b2 b3) = bool_and (bool_or b1 b2) b3.
Proof.
intros b1 b2 b3.
case b1; case b2; case b3; simpl; trivial.
Qed.

(* 6.7 p133 *)

Record plane : Set := point {abscissa : Z; ordinate : Z}.

Check plane_rec.

(* 6.8 p133*)

Definition manhattan_dist (p1 p2 : plane) : Z :=
 Z.add (Z.abs (abscissa p1 - abscissa p2)) (Z.abs (ordinate p1 - ordinate p2)).

(* 6.9 p135 *)

Inductive vehicle : Set :=
  | bicycle : nat -> vehicle
  | motorized : nat -> nat -> vehicle.

Check vehicle_rec.

Definition nb_seats := vehicle_rec (fun v => nat) (fun x => 2) (fun x n => n).

Eval compute in nb_seats (bicycle 5).

Eval compute in nb_seats (motorized 2 3).

(* 6.10 p140 *)

Definition is_Jan := month_rect (fun m => bool)
  true false false false
  false false false false
  false false false false.

Eval compute in is_Jan January.
Eval compute in is_Jan February.

(* 6.11 p140 *)

Check bool_rect.

Theorem neq_tf : true <> false.
Proof.
unfold not.
discriminate.
Qed.

Section proof_of_bool_discr.

 Let is_true (b:bool) : Prop :=
   match b with
   | true => True
   | _ => False
   end.

 Theorem bool_discr : true <> false.
 Proof.
  intro e.
  change (is_true false).
  rewrite <- e. simpl. trivial.
 Qed.

End proof_of_bool_discr.

Print bool_discr.

(* 6.12 p140 *)

Theorem neq_bm : forall (x y z:nat), bicycle x <> motorized y z.
Proof.
unfold not.
discriminate.
Qed.

Section proof_of_vehicle_discr.

 Let is_bicycle (v:vehicle) : Prop :=
   match v with
   | (bicycle x) => True
   | _ => False
   end.

 Theorem vehicle_discr : forall (x y z:nat), bicycle x <> motorized y z.
 Proof.
  intros.
  unfold not.
  intros.
  change (is_bicycle (motorized y z)).
  rewrite <- H. simpl. trivial.
 Qed.

End proof_of_vehicle_discr.

(* 6.13 p143 *)
(* this part includes a proof of false *)
(*
Require Import Arith.
Record RatPlus : Set := mkRat
  {top : nat; bottom : nat; bottom_condition : bottom <> 0}.

Axiom
  eq_RatPlus :
    forall r r':RatPlus, top r * bottom r' = top r' * bottom r -> r = r'.

Theorem rp1 : forall (x0 y0 x1 y1:nat) (p0:y0<>0) (p1:y1<>0), mkRat x0 y0 p0 = mkRat x1 y1 p1 -> x0 = x1.
Proof.
intros.
injection H.
trivial.
Qed.

SearchPattern (_ <> _).

Definition n0 := mkRat 2 6 (not_eq_sym (O_S 5)).

Definition n1 := mkRat 1 3 (not_eq_sym (O_S 2)).

Theorem rp2 : mkRat 2 6 (not_eq_sym (O_S 5)) = mkRat 1 3 (not_eq_sym (O_S 2)) -> False.
Proof.
intros.
discriminate.
Qed.

Theorem rp3 : mkRat 2 6 (not_eq_sym (O_S 5)) = mkRat 1 3 (not_eq_sym (O_S 2)).
Proof.
apply eq_RatPlus.
compute.
trivial.
Qed.

Theorem proof_false : False.
Proof.
apply rp2.
exact rp3.
Qed.
*)

(* 6.14 p152 *)
(* 6.15 p152 *)
Definition lt3 (n:nat) : bool :=
  match n with
    | O => true
    | S O => true
    | (S (S O)) => true
    | _ => false
  end.

(* 6.16 p152 *)
Fixpoint add' (n m:nat) : nat :=
  match m with
    | O => n
    | S a => S (add' n a)
  end.

(* 6.17 p152 *)
Fixpoint sum_f (n:nat) (f:nat->Z) : Z :=
  match n with
    | O => f O
    | S a => Z.add (f (S a)) (sum_f a f)
  end.

(* 6.18 p152 *)
Fixpoint two_power (n:nat) : nat :=
  match n with
    | O => S O
    | S a => (two_power a) + (two_power a)
  end.

Eval compute in two_power 3.

(* 6.19 p154 *)

Open Scope Z_scope.

Print Z.

Print positive.

Check Zpos (xO (xO (xO (xI (xO (xI (xI (xI (xI xH))))))))).
Check Zpos (xI (xO (xO (xI xH)))).
Check Zpos (xO (xO (xO (xO (xO (xO (xO (xO (xO xH))))))))).

(* 6.20 p154 *)

Definition pos_even_bool (n:positive) : bool :=
  match n with
    | (xO _) => true
    | _ => false
  end.

(* 6.21 p154 *)

Definition pos_div4 (n:positive) : Z :=
  match n with
    | (xO (xO x)) => Zpos x
    | (xO (xI x)) => Zpos x
    | (xI (xO x)) => Zpos x
    | (xI (xI x)) => Zpos x
    | _ => Z0
  end.

(* 6.22 p154 *)

Open Scope positive_scope.

Fixpoint pos_succ (n : positive) : positive :=
  match n with
    | xH => (xO xH)
    | xO x => xI x
    | xI x => xO (pos_succ x)
  end.

Fixpoint pos_add (a b : positive) : positive :=
  match (a, b) with
    | (xH, b) => pos_succ b
    | (a, xH) => pos_succ a
    | ((xO a'), (xO b')) => xO (pos_add a' b')
    | ((xI a'), (xO b')) => xI (pos_add a' b')
    | ((xO a'), (xI b')) => xI (pos_add a' b')
    | ((xI a'), (xI b')) => xO (pos_succ (pos_add a' b'))
  end.

Eval compute in pos_succ xH.
Eval compute in (pos_succ (xI (xI (xI (xI xH))))).
Eval compute in (pos_add (xI (xI (xI (xI xH)))) (xI (xI (xI (xI xH))))).

Search positive.

Eval compute in shift (5) (xI xH).

Fixpoint pos_mult (a b : positive) : positive :=
  match (a, b) with
    | (xH, b) => b
    | (a, xH) => a
    | ((xO a'), (xO b')) => xO (xO (pos_mult a' b'))
    | ((xI a'), (xO b')) => pos_add (xO (xO (pos_mult a' b'))) b
    | ((xO a'), (xI b')) => pos_add (xO (xO (pos_mult a' b'))) a
    | ((xI a'), (xI b')) => pos_add (xO (xO (pos_mult a' b'))) (pos_add (xO a') b)
  end.

Eval compute in (pos_mult (xI (xI (xI (xI xH)))) (xI (xI (xI (xI xH))))).
Eval compute in (pos_mult (xI (xO (xI (xO xH)))) (xI (xO (xI (xO xH))))).

Print Z.

Definition Z_mult (a b : Z) : Z :=
  match (a, b) with
    | (Z0, b) => Z0
    | (a, Z0) => Z0
    | (Zpos x, Zpos y) => Zpos (pos_mult x y)
    | (Zpos x, Zneg y) => Zneg (pos_mult x y)
    | (Zneg x, Zpos y) => Zneg (pos_mult x y)
    | (Zneg x, Zneg y) => Zpos (pos_mult x y)
  end.

(* 6.23 p154 *)

Inductive L : Set :=
  | L_true : L
  | L_false : L
  | L_disj : L -> L -> L
  | L_conj : L -> L -> L
  | L_impl : L -> L -> L
  | L_not : L -> L.

(* 6.24 p155 *)

Inductive F: Set :=
 | F_one : F
 | F_n : F -> F
 | F_d : F -> F.

(* 6.25 p155 *)

Inductive Z_btree : Set :=
 | Z_leaf : Z_btree
 | Z_bnode : Z->Z_btree->Z_btree->Z_btree.

Search bool.

Fixpoint value_present (z:Z) (t:Z_btree) : bool :=
  match t with
    | Z_leaf => false
    | (Z_bnode x' t1 t2) => if (Zeq_bool x' z) then true else orb (value_present z t1) (value_present z t2)
  end.

(* 6.26 p155 *)

Fixpoint power (z:Z) (n:nat) : Z :=
  match n with
    | O => 1%Z
    | (S x) => Z_mult z (power z x)
  end.

Eval compute in power 3 10.

Fixpoint discrete_log (n:positive) : nat :=
  match n with
    | xH => O
    | (xI x) => S (discrete_log x)
    | (xO x) => S (discrete_log x)
  end.

Eval compute in discrete_log (xI (xI (xI (xI xH)))).

(* 6.27 p156 *)

Inductive Z_fbtree : Set :=
  | Z_fleaf : Z_fbtree
  | Z_fnode : Z -> (bool -> Z_fbtree) -> Z_fbtree.

Definition right_son (t:Z_btree) : Z_btree :=
  match t with
  | Z_leaf => Z_leaf
  | Z_bnode a t1 t2 => t2
  end.

Definition fright_son (t:Z_fbtree) : Z_fbtree :=
  match t with
  | Z_fleaf => Z_fleaf
  | Z_fnode a f => f false
  end.

Fixpoint fsum_all_values (t:Z_fbtree) : Z :=
  (match t with
  | Z_fleaf => 0
  | Z_fnode v f =>
    v + fsum_all_values (f true) + fsum_all_values (f false)
  end)%Z.

Check Z.eq_dec.

Fixpoint fzero_present (t:Z_fbtree) : bool :=
  match t with
  | Z_fleaf => false
  | Z_fnode v f => if (Z.eq_dec v 0%Z) then true else orb (fzero_present (f true)) (fzero_present (f false))
  end.

(* 6.28 p157 *)

Inductive Z_inf_branch_tree : Set :=
  | Z_inf_leaf : Z_inf_branch_tree
  | Z_inf_node : Z -> (nat->Z_inf_branch_tree)->Z_inf_branch_tree.

(* Error: Cannot guess decreasing argument of fix. *)
(*
Fixpoint izero_present (n:nat) (t:Z_inf_branch_tree) : bool :=
  match (t, n) with
  | (Z_inf_leaf, _) => false
  | (Z_inf_node 0%Z f, _) => true
  | (Z_inf_node _ f, 0%nat) => izero_present n (f 0%nat)
  | (Z_inf_node _ f, (S m)) => orb (izero_present n (f m)) (izero_present m t)
  end.
*)

Fixpoint any_true (n:nat) (f:nat->bool):bool :=
  match n with
  | 0%nat => f 0%nat
  | S m => orb (f (S m)) (any_true m f)
  end.

Fixpoint izero_present (n:nat) (t:Z_inf_branch_tree) : bool :=
  match t with
  | Z_inf_leaf => false
  | Z_inf_node v f =>
    match v with
    | 0%Z => true
    | _ => any_true n (fun x => izero_present n (f x))
    end
  end.

(* 6.29 p158 *)

Open Scope nat_scope.

Theorem plus_n_O : forall (n:nat), n = n + 0.
Proof.
intro n.
elim n.
simpl.
reflexivity.
intros.
simpl.
elim H.
reflexivity.
Qed.

(* 6.30 p158 *)

Fixpoint f1 (bt:Z_btree) : Z_fbtree :=
  match bt with
  | Z_leaf => Z_fleaf
  | Z_bnode v t1 t2 => Z_fnode v (fun b => if b then (f1 t1) else (f1 t2))
  end.

Fixpoint f2 (ft:Z_fbtree) : Z_btree :=
  match ft with
  | Z_fleaf => Z_leaf
  | Z_fnode v f => Z_bnode v (f2 (f true)) (f2 (f false))
  end.

Theorem f2_f1 : forall t: Z_btree, f2 (f1 t) = t.
Proof.
intros.
elim t.
simpl.
trivial.
intros.
simpl.
rewrite H.
rewrite H0.
trivial.
Qed.

Require Import Logic.FunctionalExtensionality.

Theorem f1_f2 : forall t: Z_fbtree, f1 (f2 t) = t.
Proof.
intros.
induction t.
simpl; trivial.
simpl. rewrite H. rewrite H.
rewrite <- (functional_extensionality (fun b:bool => if b then z0 true else z0 false) z0).
trivial.
intros. elim x; trivial.
Qed.

(* 6.31 p158 *)

Fixpoint mult2 (n:nat) : nat :=
  match n with
  | O => O
  | (S p) => (S (S (mult2 p)))
  end.

Lemma mult2_double : forall n:nat, mult2 n = n + n.
Proof.
intros.
elim n.
simpl. trivial.
intros.
simpl. rewrite H. rewrite plus_n_Sm. trivial.
Qed.

(* 6.32 p158 *)

Fixpoint sum_n (n:nat) : nat :=
  match n with
  | O => O
  | S p => S p + sum_n p
  end.

Require Import ArithRing.

Theorem sum_closed_form : forall n:nat, 2 * sum_n n = n * S n.
Proof.
intro n.
induction n.
simpl. trivial.
simpl (sum_n (S n)).
ring_simplify.
rewrite IHn.
ring_simplify.
trivial.
Qed.

Theorem sum_closed_form' : forall n:nat, 2 * sum_n n = n * S n.
Proof.
induction n; trivial.
unfold sum_n; fold sum_n.
rewrite mult_plus_distr_l.
rewrite IHn.
simpl.
f_equal.
rewrite <- plus_n_O.
repeat rewrite mult_succ_r.
repeat rewrite plus_n_Sm.
rewrite plus_assoc_reverse.
f_equal.
rewrite <- plus_comm.
trivial.
Qed.

Print sum_closed_form'.

(* 6.33 p159 *)

Require Import Omega.

Theorem sum_n_le_n : forall n:nat, n <= sum_n n.
Proof.
intro n.
assert ((2 * n) <= (2 * sum_n n)).
rewrite sum_closed_form.
ring_simplify.
induction n.
simpl; omega.
ring_simplify. omega.
omega.
Qed.

Print sum_n_le_n.

Theorem sum_n_le_n' : forall n:nat, n <= sum_n n.
Proof.
 simple induction n.
 auto with arith.
 intros n1 Hn0. simpl.
 auto with arith.
Qed.

Print sum_n_le_n'.

(* 6.34 p161 *)
Require Import List.
Definition two_first (A:Set)(l:list A) : list A :=
 match l with
 | a :: b :: l' => a :: b :: nil
 | _ => (nil (A:=A))
 end.

(* 6.35 p161 *)

Fixpoint take (A:Set)(n:nat)(l:list A): list A :=
   match (n,l) with
   | (0, l') => nil
   | (S n', nil) => nil
   | (S n', a::l') =>
     match take _ n' l' with
     | nil => nil
     | l'' => a :: l''
     end
  end.

(* 6.36 p161 *)

Fixpoint sum_list (l:list nat) : nat :=
  match l with
  | nil => 0
  | a::l' => a + sum_list l'
  end.

(* 6.37 p161 *)

Fixpoint ones (n:nat) : list nat :=
  match n with
  | O => nil
  | (S m) => 1 :: ones m
  end.

(* 6.38 p162 *)

Fixpoint natlist (n:nat) : list nat :=
  (fix f (m:nat):list nat :=
    match m with
    | O => nil
    | (S x) => (n - x)::(f x)
    end) n.

Eval compute in natlist 5.

(* 6.39 p163 *)

Fixpoint nth_option (A:Set)(n:nat)(l:list A) {struct l}: option A :=
  match n, l with
  | O, cons a tl =>  Some a
  | S p, cons a tl => nth_option A p tl
  | _, nil => None
  end.

Fixpoint nth_option' (A:Set)(n:nat)(l:list A) {struct n}: option A :=
  match n, l with
  | O, cons a tl =>  Some a
  | O, nil => None
  | S p, cons a tl => nth_option' A p tl
  | S p, nil => None
  end.

(* 6.40 p163 *)

Lemma nth_length : forall (A:Set)(n:nat)(l:list A), nth_option A n l = None <-> length l <= n.
Proof.
simple induction n.
destruct l.
simpl; split; auto.
simpl; split; [discriminate | inversion 1].
destruct l; split; simpl; auto with arith.
case (H l); auto with arith.
case (H l); auto with arith.
Qed.

(* 6.41 p163 *)

Fixpoint first_in_list (A:Set)(f:A->bool)(l:list A) : option A :=
  match l with
  | nil => None
  | a::l' => if f a then Some a else first_in_list A f l'
  end.

(* 6.42 p163 *)

Fixpoint split (A B: Set)(l: list (A * B)) : (list A)*(list B) :=
  match l with
  | nil => (nil, nil)
  | (a,b)::l' => let ll := (split _ _ l') in (a::(fst ll), b::(snd ll))
  end.

Fixpoint combine (A B: Set)(l1 : list A)(l2 :list B): list (A*B):=
  match l1, l2 with
  | nil, _ => nil
  | _, nil => nil
  | a::l1', b::l2' => (a,b)::(combine _ _ l1' l2')
  end.

(*copy from the answer*)
Theorem combine_of_split : forall (A B:Set) (l:list (A*B)),
   let ( l1,l2) :=  (split _ _ l)
   in combine _ _ l1 l2 = l.
Proof.
 simple induction l; simpl; auto.
 intros p l0; case (split A B l0); simpl; auto.
 case p; simpl; auto.
 destruct 1; auto.
Qed.

Print combine_of_split.

(* 6.43 p163 *)

Inductive btree (A:Set) :Set :=
  | bleaf : btree A
  | bnode : A ->btree A->btree A->btree A.

Fixpoint Z_btree_to_btree (t:Z_btree) : btree Z :=
  match t with
  | Z_leaf => bleaf Z
  | Z_bnode x t1 t2 =>
      bnode Z x (Z_btree_to_btree t1) (Z_btree_to_btree t2)
  end.

Fixpoint btree_to_Z_btree (t:btree Z) : Z_btree :=
  match t with
  | bleaf => Z_leaf
  | bnode x t1 t2 =>
      Z_bnode x (btree_to_Z_btree t1) (btree_to_Z_btree t2)
  end.

Theorem btree_to_Z_inv : forall t, Z_btree_to_btree (btree_to_Z_btree t) = t.
Proof.
intros.
induction t.
simpl.
trivial.
simpl.
rewrite IHt1.
rewrite IHt2.
trivial.
Qed.

Theorem Z_btree_to_inv : forall t, btree_to_Z_btree (Z_btree_to_btree t) = t.
Proof.
intros.
induction t; simpl; trivial.
simpl; rewrite IHt1; rewrite IHt2; trivial.
Qed.

(* 6.44 p164 *)

Fixpoint F_to_PQ (f:F) : nat * nat :=
  match f with
  | F_one => (1, 1)
  | F_n g => let (p,q) := F_to_PQ g in (p+q,q)
  | F_d g => let (p,q) := F_to_PQ g in (p,p+q)
  end.

Eval compute in F_to_PQ (F_one).

Eval compute in F_to_PQ (F_n F_one).

Eval compute in F_to_PQ (F_d (F_n F_one)).

(* 6.45 p164 *)

Inductive cmp : Set :=
  | Less : cmp
  | Equal : cmp
  | Greater : cmp.

Fixpoint three_way_compare (a b:nat) : cmp :=
  match a, b with
  | O, O => Equal
  | S _, O => Greater
  | O, S _ => Less
  | S n, S m => three_way_compare n m
  end.

Fixpoint update_primes (k:nat) (l:list (nat*nat)) : (list (nat*nat))*bool :=
  match l with
  | nil => (nil, false)
  | (p,m)::l' => let (a, b) := update_primes k l' in
    match three_way_compare k m with
    | Less => ((p, m)::a, b)
    | Equal => ((p, m+p)::a, true)
    | Greater => ((p, m+p)::a, b)
    end
  end.

Fixpoint prime_sieve (n:nat) : (list (nat*nat)) :=
  match n with
  | O => nil
  | S O => nil
  | S m => let (a, b) := update_primes (S m) (prime_sieve m) in
    if b then a else (S m, 2 * (S m))::a
  end.

Eval compute in prime_sieve 100.

(* It is so hard to prove the soundness and completeness! *)

(* 6.46 p167 *)

Inductive htree (A:Set) : nat -> Set :=
  | hleaf : A -> (htree A O)
  | hnode : forall n:nat, A -> htree A n -> htree A n -> htree A (S n).

Fixpoint invert (A:Set)(n:nat)(t:htree A n) : htree A n :=
  match t with
  | hleaf v => hleaf A v
  | hnode p v t1 t2 =>
    hnode A p v (invert A p t2) (invert A p t1)
  end.

(*copy from the answer*)

Definition first_of_htree :
  forall (A:Set) (n:nat), htree A n -> htree A (S n) -> htree A n.
 intros A n v t.

 generalize v.
 change (htree A (pred (S n)) -> htree A (pred (S n))).
 case t.
 intros x v'; exact v'.
 intros p x t1 t2 v'; exact t1.
Defined.

Print first_of_htree.

Eval compute in (pred O).

Theorem injection_first_htree :
 forall (n:nat) (t1 t2 t3 t4:htree nat n),
   hnode nat n 0 t1 t2 = hnode nat n 0 t3 t4 -> t1 = t3.
 intros n t1 t2 t3 t4 h.
 change
  (first_of_htree nat n t1 (hnode nat n 0 t1 t2) =
   first_of_htree nat n t1 (hnode nat n 0 t3 t4)).
 rewrite h.
 reflexivity.
Qed.

(* 6.47 p167 *)

Fixpoint make_htree (n:nat) : htree Z n :=
  match n with
  | O => hleaf Z 0%Z
  | (S m) => hnode Z m 0%Z (make_htree m) (make_htree m)
  end.

(* 6.48 p167 *)

Inductive binary_word : nat -> Set :=
  | empty_binary_word :  binary_word 0
  | cons_binary_word :
    forall p:nat, bool -> binary_word p -> binary_word (S p).

Fixpoint binary_word_concat (n:nat)(w1:binary_word n)(m:nat)(w2:binary_word m) : binary_word (n+m) :=
  match w1 with
  | empty_binary_word => w2
  | cons_binary_word q b w1' =>
    cons_binary_word (q+m) b (binary_word_concat q w1' m w2)
 end.

(* 6.49 p167 *)
Fixpoint binary_word_or (l : nat) (wl wr : binary_word l) : binary_word l.
  destruct wl.
  exact wr.
  inversion wr.
  exact(cons_binary_word p (orb b H0) (binary_word_or p wl H1)).
Defined.

Definition bw1:= cons_binary_word 1 false (cons_binary_word 0 true empty_binary_word).
Definition bw2:= cons_binary_word 1 true (cons_binary_word 0 false empty_binary_word).

Eval compute in binary_word_or 2 bw1 bw2.

Print binary_word_or.

Eval compute in eq_rec 2 binary_word bw1 2 eq_refl.

(* lolisa's solution *)

Fixpoint binary_word_or1 (l : nat) (wl wr : binary_word l) : binary_word l.
  refine(
    match wl in binary_word n return binary_word n -> binary_word n with
    | empty_binary_word => (fun x => x)
    | cons_binary_word ll lb lw =>
        (fun wr' : binary_word (S ll) => match wr' with
        | empty_binary_word => (fun p : False => wr')
        | cons_binary_word rl rb rw =>
            (fun p : ll = rl =>
              cons_binary_word
              rl
              (bool_or lb rb)
              (binary_word_or rl (eq_rec ll binary_word lw rl p) rw))
        end _)
    end wr).
    reflexivity.
Defined.

Print binary_word_or1.

Fixpoint binary_word_or2 (n:nat)(w1 w2:binary_word n) : binary_word n.
  refine(
  match w1 in binary_word p return binary_word p -> binary_word p with
  | empty_binary_word => (fun x => x)
  | cons_binary_word q1 b1 w1' => (fun w3 =>
    match w3 with
    | empty_binary_word => w3
    | cons_binary_word q2 b2 w2' => (fun P:q1=q2 =>
      cons_binary_word q2 (orb b1 b2) (binary_word_or q2 (eq_rec q1 binary_word w1' q2 P) w2'))
    end eq_refl
  )
  end w2).
Qed.

Print binary_word_or2.

(* 6.50 p167 *)

(* copy from answer *)

Fixpoint bool_nat_type (n:nat) : Set :=
  match n with 0 => nat | 1 => bool | S (S n) => bool_nat_type n end.

Fixpoint bool_nat_fun_aux (n:nat) :  bool_nat_type n -> bool_nat_type n :=
 match n return bool_nat_type n -> bool_nat_type n with
   0 => S | 1 => negb | S (S n) => bool_nat_fun_aux n
 end.

Print bool_nat_fun_aux.

Fixpoint bool_nat_fun (n:nat) : bool_nat_type n :=
 match n return bool_nat_type n with
   0 => 0
 | 1 => true
 | S (S n) => bool_nat_fun_aux n (bool_nat_fun n)
 end.

Eval compute in bool_nat_fun_aux 6 (bool_nat_fun 6).

Eval compute in (bool_nat_fun 7).

Eval compute in (bool_nat_fun 9).

Eval compute in (bool_nat_fun 7 : bool).

Eval compute in (bool_nat_fun 6 : nat).

(* 6.51 p169 *)

Check Empty_set.

Theorem all_equal : forall x y : Empty_set, x = y.
Proof.
destruct x.
Qed.

Theorem all_diff : forall x y : Empty_set, x <> y.
Proof.
destruct x.
Qed.

End section_for_chapter_6.


(********************** Begin: FRAP Preamble **********************)
Require Import Frap.

Inductive arith : Set :=
| Const (n : nat)
| Var (x : var)
| Plus (e1 e2 : arith)
| Minus (e1 e2 : arith)
| Times (e1 e2 : arith).

Example ex1 := Const 42.
Example ex2 := Plus (Var "y") (Times (Var "x") (Const 3)).

Definition valuation := fmap var nat.

Fixpoint interp (e : arith) (v : valuation) : nat :=
  match e with
  | Const n => n
  | Var x =>
    match v $? x with
    | None => 0
    | Some n => n
    end
  | Plus e1 e2 => interp e1 v + interp e2 v
  | Minus e1 e2 => interp e1 v - interp e2 v
  | Times e1 e2 => interp e1 v * interp e2 v
  end.

Definition valuation0 : valuation :=
  $0 $+ ("x", 17) $+ ("y", 3).

Theorem interp_ex1 : interp ex1 valuation0 = 42.
Proof.
  simplify.
  equality.
Qed.

Theorem interp_ex2 : interp ex2 valuation0 = 54.
Proof.
  unfold valuation0.
  simplify.
  equality.
Qed.

Fixpoint commuter (e : arith) : arith :=
  match e with
  | Const _ => e
  | Var _ => e
  | Plus e1 e2 => Plus (commuter e2) (commuter e1)
  | Minus e1 e2 => Minus (commuter e1) (commuter e2)
  | Times e1 e2 => Times (commuter e2) (commuter e1)
  end.
(********************** End: FRAP Preamble **********************)

(********************** Begin: Studio talk **********************)
Theorem commuter_ok : forall v e, interp (commuter e) v = interp e v.
Proof.
  intros.
  induct e; simplify.
  - equality.
  - equality.
  - linear_arithmetic.
  - equality.
  - rewrite IHe1, IHe2.
    remember (interp e2 v) as a.
    remember (interp e1 v) as b.
    (* ring.              [> ring knows a*b = b*a <] *)
    (* Show Proof.        [> 259 line proof <] *)
    linear_arithmetic. (* why does linear_arithmetic work here? :shrug: *)
    (* Show Proof.        [> 333 line proof <] *)
    Locate linear_arithmetic.
Qed.
(* Ring is just a decision procedure; as long as your datatype has proven its
*  axioms, you can use proofs about rings for free. *)

(* Note: linear_arithmetic is not equivalent to ring *)
(* Recall: ring can only prove equalities, l_a can prove both equalities and
*  inequalities. *)
(********************** End: Studio Talk **********************)

(********************** Begin: my solution **********************)
Fixpoint doSomeArithmetic (e : arith) : arith :=
  match e with
  | Const _ => e
  | Var _ => e
  | Plus (Const n1) (Const n2) => Const (n1 + n2)
  | Plus e1 e2 => Plus (doSomeArithmetic e1) (doSomeArithmetic e2)
  | Minus e1 e2 => Minus (doSomeArithmetic e1) (doSomeArithmetic e2)
  | Times (Const n1) (Const n2) => Const (n1 * n2)
  | Times e1 e2 => Times (doSomeArithmetic e1) (doSomeArithmetic e2)
  end.

(* Just case bashin' *)
Theorem doSomeArithmetic_ok : forall e v, interp (doSomeArithmetic e) v = interp e v.
Proof.
  induct e; simplify.
  - equality.
  - equality.
  - cases e1; simplify; try equality.
    cases e2; simplify; equality.
  - cases e1; simplify; equality.
  - cases e1; simplify; try equality.
    cases e2; simplify; equality.
Qed.
(********************** End: my solution **********************)

(********************** Begin: FRAP Stuff **********************)
Inductive instruction :=
| PushConst (n : nat)
| PushVar (x : var)
| Add
| Subtract
| Multiply.

Definition run1 (i : instruction) (v : valuation) (stack : list nat) : list nat :=
  match i with
  | PushConst n => n :: stack
  | PushVar x => (match v $? x with
                  | None => 0
                  | Some n => n
                  end) :: stack
  | Add =>
    match stack with
    | arg2 :: arg1 :: stack' => arg1 + arg2 :: stack'
    | _ => stack
    end
  | Subtract =>
    match stack with
    | arg2 :: arg1 :: stack' => arg1 - arg2 :: stack'
    | _ => stack
    end
  | Multiply =>
    match stack with
    | arg2 :: arg1 :: stack' => arg1 * arg2 :: stack'
    | _ => stack
    end
  end.

Fixpoint run (is : list instruction) (v : valuation) (stack : list nat) : list nat :=
  match is with
  | nil => stack
  | i :: is' => run is' v (run1 i v stack)
  end.

Fixpoint compile (e : arith) : list instruction :=
  match e with
  | Const n => PushConst n :: nil
  | Var x => PushVar x :: nil
  | Plus e1 e2 => compile e1 ++ compile e2 ++ Add :: nil
  | Minus e1 e2 => compile e1 ++ compile e2 ++ Subtract :: nil
  | Times e1 e2 => compile e1 ++ compile e2 ++ Multiply :: nil
  end.
(********************** End: FRAP Stuff **********************)

(********************** Begin: Studio Talk **********************)
Theorem compile_ok' : forall e v, run (compile e) v nil = interp e v :: nil.
Proof.
  induct e; intros; simplify; auto.
  - try rewrite IHe1. (* this hypothesis is not syntactically equivalent to the current goal
                         in particular, it only hypothesizes running a single compilation
                         we NEED a stronger induction hypothesis! hence compile_ok_stronger *)
Admitted.

(* so lets do this for all instruction lists, not just single compiles *)
Theorem compile_ok_stronger :
  forall e v is,
    run (compile e ++ is) v nil = run is v (interp e v :: nil).
Proof.
  induct e; intros; simplify; auto.
  - try rewrite IHe1. (* still doesn't work, BUT just a silly nesting reason. *)
    Search ((_ ++ _) ++ _ = _ ++ (_ ++ _)). (* so let's find what we need *)
    rewrite app_assoc_reverse.
    rewrite IHe1. (* great! *)
    rewrite app_assoc_reverse.
    try rewrite IHe2.
    (* BUT this doesn't work for the second side! the IH works
       for empty instruction stacks but what we need to prove is no longer
       empty! there's an `interp e1 v` sitting in there. So this theorem statement
       is NOT inductive. *)
Admitted.

(* so lets make sure we have a statement for ALL stacks, not just nil *)
Theorem compile_ok_even_stronger :
  forall e v is stack,
    run (compile e ++ is) v stack = run is v (interp e v :: stack).
Proof.
  induct e; intros; simplify; auto.
  - rewrite <- app_assoc. (* same thing as rewrite app_assoc_reverse. *)
    rewrite IHe1.
    rewrite <- app_assoc.
    rewrite IHe2. (* Notice IHe2 is now strong enough for the goal! *)
    simplify. reflexivity.
  (* the other cases are identical *)
  - rewrite <- app_assoc. rewrite IHe1.
    rewrite <- app_assoc. rewrite IHe2. simplify. reflexivity.
  - rewrite <- app_assoc. rewrite IHe1.
    rewrite <- app_assoc. rewrite IHe2. simplify. reflexivity.
Qed.

(* Finding the right inductive hypothesis is an art. It needs to be weak enough
   to prove base cases but strong enough to induct. *)

Theorem compile_ok : forall e v, run (compile e) v nil = interp e v :: nil.
Proof.
  intros.
  try apply compile_ok_even_stronger. (* doesn't work directly *)
  SearchRewrite (_ ++ nil).
  rewrite (app_nil_end (compile e)). (* pass (compile e ++ []) instead of (compile e) *)
  apply compile_ok_even_stronger.
Qed.

(********************** End: Studio Talk **********************)


(*
Noel-Lardin Thomas
Turki Sanekli Hedi
*)

(**********************************************************
*                         Partie 1                        *
***********************************************************)

(* -------------- Lambda calcul simplement typé ----------------*)
Section type_booleen.
Variable T: Set.
Variable E F:T.
Definition cbool := T -> T -> T.
Definition cnat := (T->T) -> T->T.

(* # 1 #*)
(* Définition des Booléens *)
Definition ctr : cbool := fun x y => x. (* TRUE *)
Definition cfa : cbool := fun x y => y. (* FALSE *)
Compute ctr E F.
Compute cfa E F.

(* # 2 #*)
(* Définition de la contion if *)
Definition cif : cbool -> T -> T -> T := fun c : cbool => fun x y => c x y.
Compute (cif ctr E F).
Compute (cif cfa E F).

(* Définition de la négation *)
Definition cnot : cbool->cbool := fun b : cbool => fun x y => cif b y x.
Compute(cnot ctr).
Compute(cnot cfa).

(* Définition de la contion and *)
Definition cand : cbool-> cbool->cbool := fun a b : cbool=> fun x y : T => a (b x y) y.
Compute (cand ctr cfa).
Compute (cand ctr ctr).
Compute (cand cfa cfa).
Compute (cand cfa ctr).

(* Définition de la contion or *)
Definition cor : cbool->cbool->cbool := fun a b : cbool=> fun x y : T => a x (b x y).
Compute (cor ctr cfa).
Compute (cor ctr ctr).
Compute (cor cfa cfa).
Compute (cor cfa ctr).

(* # 2 # *)
Section type_entier.

(* Définition des constantes *)
Definition c0 : cnat := fun f x => x.
Definition c1 : cnat := fun f x => f x.
Definition c2 : cnat := fun f x => f (f x).
Definition c3 : cnat := fun f x => f (f (f x)).
Compute (c0).
Compute (c1).
Compute (c2).
Compute (c3).

(* Définition de l'opération successeur *)
Definition csucc : cnat-> cnat := fun n => fun f => fun x => f(n f x).
Compute (csucc c0).
Compute (csucc c1).
Compute (csucc c2).
Compute (csucc c3).
Compute (csucc (csucc c3)).

(* Définition de l'opération addition *)
Definition cadd : cnat->cnat->cnat := fun n m f x => n f ( m f x ).
Compute(cadd c1 c2).
Compute (cadd c3 (csucc c3)).
Compute (cadd (csucc c2) (csucc c3)).

(* Définition de l'opération multiplication *)
Definition cmult: cnat->cnat->cnat := fun n m f => n ( m f ).
Compute(cmult c2 c3).
Compute (cmult (cadd c2 c3) (csucc c1)).

(* Définition du test à 0 *)
Definition ceq0: cnat->cbool := fun n => fun x y => n (fun z => y) x.
Compute (ceq0 c0).
Compute (ceq0 c1).
Compute (ceq0 (cmult c0 (cadd c3 (cmult c2 c3)))). (* Multiplication par 0 *)
Compute (ceq0 (cmult c3 (cadd c3 (cmult c2 c3)))).

End type_entier.
End type_booleen.