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

Require Export Induction.
Module NatList.

(* ################################################################# *)
(** * 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.
  destruct p as [m n]. 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.
  destruct p as [m n]. 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 nonzeros (l:natlist) : natlist :=
  match l with
  | nil => nil
  | h :: t =>
    match h with
    | O => nonzeros t
    | _ => h :: nonzeros t
    end
  end.

Example test_nonzeros:
  nonzeros [0;1;0;2;3;0;0] = [1;2;3].
Proof. reflexivity. Qed.

Fixpoint oddmembers (l:natlist) : natlist :=
  match l with
  | nil => nil
  | h :: t =>
    match oddb h with
    | true => h :: oddmembers t
    | false => oddmembers t
    end
  end.

Example test_oddmembers:
  oddmembers [0;1;0;2;3;0;0] = [1;3].
Proof. reflexivity. Qed.

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

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

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

Example test_countoddmembers3:
  countoddmembers nil = 0.
Proof. 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
  | nil, _ => l2
  | _, nil => l1
  | h1 :: t1, h2 :: t2 => h1 :: h2 :: alternate t1 t2
  end.

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

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

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

Example test_alternate4:
  alternate [] [20;30] = [20;30].
Proof. 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 :=
  match s with
  | nil => 0
  | h :: t =>
    match beq_nat v h with
    | true => 1 + count v t
    | false => count v t
    end
  end.

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

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

Example test_count2:              count 6 [1;2;3;1;4;1] = 0.
Proof. reflexivity. Qed.

(** 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 := app.

Example test_sum1:              count 1 (sum [1;2;3] [1;4;1]) = 3.
Proof. reflexivity. Qed.

Definition add (v:nat) (s:bag) : bag := cons v s.

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

Definition member (v:nat) (s:bag) : bool :=
  negb (beq_nat (count v s) 0).

Example test_member1:             member 1 [1;4;1] = true.
Proof. reflexivity. Qed.

Example test_member2:             member 2 [1;4;1] = false.
Proof. 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 =>
    match beq_nat h v with
    | true => t
    | false => h :: remove_one v t
    end
  end.

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

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

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

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

Fixpoint remove_all (v:nat) (s:bag) : bag :=
  match s with
  | nil => nil
  | h :: t =>
    match beq_nat h v with
    | true => remove_all v t
    | false => h :: remove_all v t
    end
  end.

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

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

Example test_remove_all3:  count 4 (remove_all 5 [2;1;4;5;1;4]) = 2.
Proof. reflexivity. Qed.

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

Fixpoint subset (s1:bag) (s2:bag) : bool :=
  match s1 with
  | nil => true
  | h1 :: t1 => member h1 s2 && subset t1 (remove_one h1 s2)
  end.

Example test_subset1:              subset [1;2] [2;1;4;1] = true.
Proof. reflexivity. Qed.
Example test_subset2:              subset [1;2;2] [2;1;4;1] = false.
Proof. 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.
*)

Theorem bag_theorem_count_add : forall (n : nat) (b : bag),
  count n (add n b) = 1 + count n b.
Proof.
  intros n b. simpl.
  replace (beq_nat n n) with true. reflexivity.
  induction n as [|n' IHn].
  - reflexivity. - simpl. rewrite <- IHn. reflexivity.
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.
  induction l as [|h t IH].
  - reflexivity.
  - simpl. rewrite IH. reflexivity.
Qed.

Theorem rev_app_distr: forall l1 l2 : natlist,
  rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof.
  induction l1 as [|h1 t1 IH].
  - intro l2. simpl. rewrite app_nil_r. reflexivity.
  - intro l2. simpl. rewrite IH. rewrite app_assoc.
    reflexivity.
Qed.

Theorem rev_involutive : forall l : natlist,
  rev (rev l) = l.
Proof.
  induction l as [|h1 t1 IH].
  - reflexivity.
  - simpl. rewrite rev_app_distr. rewrite IH. reflexivity.
Qed.

(** 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 l1 l2 l3 l4.
  replace ((l1 ++ l2) ++ l3) with (l1 ++ l2 ++ l3).
  rewrite app_assoc. rewrite app_assoc. reflexivity.
  rewrite app_assoc. reflexivity.
Qed.

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

Lemma nonzeros_app : forall l1 l2 : natlist,
  nonzeros (l1 ++ l2) = (nonzeros l1) ++ (nonzeros l2).
Proof.
  intros l1 l2. induction l1 as [|h1 t1 IH].
  - reflexivity.
  - destruct h1 as [|h1'].
    + simpl. rewrite IH. reflexivity.
    + simpl. rewrite IH. 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 :=
  match l1,l2 with
  | nil,nil => true
  | (h1 :: t1), (h2 :: t2) => beq_nat h1 h2 && beq_natlist t1 t2
  | _,_ => 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.

Theorem beq_natlist_refl : forall l:natlist,
  true = beq_natlist l l.
Proof.
  induction l as [|h t IH].
  - reflexivity.
  - simpl. rewrite IH. rewrite <- beq_nat_refl. reflexivity.
Qed.
(** [] *)

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

(** **** Exercise: 3 stars, advanced (bag_proofs)  *)
(** Here are a couple of little theorems to prove about your
    definitions about bags above. *)

Theorem count_member_nonzero : forall (s : bag),
  leb 1 (count 1 (1 :: s)) = true.
Proof.
  reflexivity.
Qed.

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

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.

Theorem remove_decreases_count: forall (s : bag),
  leb (count 0 (remove_one 0 s)) (count 0 s) = true.
Proof.
  induction s as [|h t IH].
  - reflexivity.
  - destruct h as [|h'].
    + simpl. rewrite ble_n_Sn. reflexivity.
    + simpl. rewrite IH. 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.  (You may
    find that the difficulty of the proof depends on how you defined
    [count]!) *)

Theorem bag_count_sum : forall (n : nat) (b1 b2 : bag),
  count n b1 + count n b2 = count n (sum b1 b2).
Proof.
  intros n b1 b2. induction b1 as [|h1 t1 IHb1].
  - reflexivity.
  - simpl. destruct (beq_nat n h1).
    + simpl. rewrite IHb1. reflexivity.
    + simpl. rewrite IHb1. reflexivity.
Qed.
(** [] *)

(** **** 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_injective : forall (l1 l2 : natlist),
  rev l1 = rev l2 -> l1 = l2.
Proof.
  intros l1 l2 H.
  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 :=
  match l with
  | nil => None
  | h :: _ => Some h
  end.

Example test_hd_error1 : hd_error [] = None.
Proof. reflexivity. Qed.

Example test_hd_error2 : hd_error [1] = Some 1.
Proof. reflexivity. Qed.

Example test_hd_error3 : hd_error [5;6] = Some 5.
Proof. reflexivity. Qed.
(** [] *)

(** **** 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.
  intros l default. induction l as [|h t IHl].
  - reflexivity.
  - reflexivity.
Qed.
(** [] *)

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.
  induction x. simpl. rewrite <- beq_nat_refl. reflexivity.
Qed.
(** [] *)

(** 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.
  simpl. intros d x.
  rewrite <- beq_id_refl. reflexivity.
Qed.
(** [] *)

(** **** 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.
  intros d x y o H. simpl. rewrite H. reflexivity.
Qed.
(** [] *)
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?  (Answer in English
    or the natural language of your choice.)

  "baz" has no element, because there isn't any "base constructor" availble.
*)
(** [] *)

(** $Date: 2017-09-06 10:45:52 -0400 (Wed, 06 Sep 2017) $ *)


(** * Sorting *)

(** In this module, we'll implement insertion sort, then state and
prove its correctness. As a warm-up, we'll first write a verified
insertion sort that operates over only lists of [nat]s. We'll then
generalize to an implementation that operates on polymorphic lists.

This module will only rely on the definitions introduced up to
MoreLogic. That said, feel free to use Coq tactics discussed later in
the text, such as auto.

*)

Require Export MoreLogic.

(** * Permutations *)

(** [perm] (5 pts): every sorting function should return a result that
is a permutation of its input.  We'll first define the permutation
relationship over lists. The dependency between permutations and the
type of data stored in a list is very loose, so we'll go ahead and
just define a permutation once, generalized over the type of data
stored in the lists that we permuate.

Please define a [Prop]osition [perm] paramaterized on:

(1) a type [T];

(2) two lists of type [list T].

For each type [T]:

(1) [perm T] should hold over the empty lists of type [T]:

(2) for each element [n] of type [T] and all lists [l], [l0], and [l1]
of [T] values, if [l] permutes to [l0 ++ l1], then [n :: l] permutes
to [l0 ++ n :: l1].

*)

(* DEFINE perm HERE *)

Inductive perm (T:Type): list T -> list T -> Prop:=
| perme : perm T [] []
| permp : forall (l l0 l1 :list T) (n: T), perm T l (l0++l1) -> perm T (n::l) (l0++n::l1).

(** [perm_refl (5 pts), perm_trans (20 pts)]: for each type [T], [perm
T] defines a binary relation over [list T] that is in fact an
equivalence relation. It'll help in some of our later proofs to use
the facts that [perm T] is reflexive and transitive, so please prove
these facts now. *)

Lemma perm_refl : forall T l, perm T l l.
Proof.
  intros.
  induction l.
  Case "perme".
      apply perme.
  Case "permp".
      apply permp with (l0:=[]) (l1:= l) (n:=x).
      simpl.
      assumption.
  Qed.

Lemma null_app: forall (T:Type) (l0 l1: list T),
[] = l0++l1 -> l0 = [] /\ l1=[].
Proof.
induction l0.
split.
reflexivity.
generalize dependent l1.
induction l1.
reflexivity.
intros.
inversion H.
intros.
inversion H.
Qed.

Lemma perm_trans :
  forall T l0 l1 l2, perm T l0 l1 -> perm T l1 l2 -> perm T l0 l2.
Proof.
intros.
generalize dependent l0.
induction H0; intros.
assumption.
inversion H0; subst.
apply null_app in H3.
destruct H3.
rewrite H1, H2.
simpl.
assumption.
rewrite <- H1 in H0.
apply permp with (n:=n0) in H0.
inversion H; subst.
admit.

  Qed.

(** * Sorting [list nat]'s

In this module, we'll define when one list of [list nat]'s is a
sorting of another, define insertion sort over [nat list]'s, and prove
that our definition returns a sorting of its input. *)

Module SortingNats.

(** ** Defining Sortedness

We'll now formulate under what conditions one [list nat] is a valid
sorting of another [list nat].  *)

(** [nat_sorted] (10 pts): please define a [Prop] named [nat_sorted]
over a single [list nat]. [nat_sorted] should hold under one of the
following conditions:

(1) [nat_sorted] holds for the empty list;

(2) for each [nat] [n], [nat_sorted] holds for [[n]];

(3) for all [nat]s [m] and [n] and each [list nat l], if [m <= n] and
[nat_sorted] holds for [n :: l], then [nat_sorted] holds for [m :: n
:: l].

Note that this definition formalizes only "local" sortedness, not
"strong" sortedness. *)

(* TODO: define nat_sorted here. *)

Inductive nat_sorted: list nat -> Prop:=
| natse : nat_sorted []
| natsn : forall (n:nat), nat_sorted [n]
| natsmn : forall (m n: nat) (l: list nat), m<= n -> nat_sorted (n::l) -> nat_sorted (m::n::l).

(** [is_nat_sorting] (5 pts): now, please formulate when one [list
nat] is a sorting of another [list nat].

For [nat list]'s [l0] and [l1], [l1] is a sorting of [l0] if both of
the following conditions hold:

(1) [l1] is a sorted [list nat];

(2) [l1] is a permutation of [l0]. *)

(* TODO: define is_nat_sorting here. *)

Definition is_nat_sorting (l0 l1:list nat): Prop:= nat_sorted l1 /\ perm nat l0 l1.

(** ** Defining insertion sort

We'll now define an implementation of insertion sort over [nat
list]'s. *)

(** [ble_nat_dec] (20 pts): one natural way to implement insertion
sort would be to use [ble_nat], which returns a [bool] that denotes if
its first input is less-than-or-equal-to the second. We could write an
implemenation using [ble_nat] that is correct, but its correctness
would be awkward to prove, because we would have to define, prove, and
use many simple lemmas for translating between the [bool] results of
[ble_nat] and the actual ordering over [ble_nat]'s inputs.

In the unit on evidence-carrying Booleans, we saw that a similar
awkwardness can be neatly sidestepped for reasoning about tests of
equality over [nat]s with [beq_nat] by writing and using a proof
[beq_nat_dec] that for all [nat]'s [m] and [n], either constructs a
proof that [m = n] or [m <> n].

To write and prove the correctness of our sorting function, we'll
write a similar proof [ble_nat_dec] that for all [nat]'s [m] and [n],
either constructs a proof that [n <= m] or that [m <= n].

The theorem is alrady stated. Please complete the proof.
*)

Theorem ble_nat_dec : forall (n m : nat), { n <= m } + { m <= n }.
Proof.
  intros n.
  induction n as [|n'].
  Case "n = 0".
    intros m.
    destruct m as [|m'].
    SCase "m = 0".
      left. reflexivity.
    SCase "m = S m'".
      left. apply O_le_n.
  Case "n = S n'".
    intros m.
    destruct m as [|m'].
    SCase "m = 0".
      right. apply O_le_n.
    SCase "m = S m'".
      destruct IHn' with (m := m') as [eq | neq].
      left. apply n_le_m__Sn_le_Sm. assumption.
      right. apply n_le_m__Sn_le_Sm. assumption.
Defined.

(** [nat_insert] (5 pts) takes:

(1) a [nat] [n];

(2) a sorted [nat list] [l].

[nat_insert] returns a sorted [list nat] with [n] inserted into [l].

Please define [nat_insert]. One natural definition is a [Fixpoint]
that only uses [ble_nat_dec].  *)

(* TODO: complete definition. *)
Fixpoint nat_insert n l : list nat:=
match l with
| [] => [n]
| x :: t1 => if ble_nat_dec n x
             then (n::x::t1)
             else (x::(nat_insert n t1))
end.

(** [nat_insert_sort] (5 pts) takes:

(1) a [list nat] [l]

[nat_insert_sort] returns a sorting of [l].

Please write [nat_insert_sort]. One natural definition is a [Fixpoint]
that only uses [nat_insert].  *)

(* TODO: complete definition *)
Fixpoint nat_insert_sort l : list nat:=
match l with
| [] => []
| [x] => [x]
| h::t1 => nat_insert h (nat_insert_sort t1)
end.

Example sort1 : nat_insert_sort [1;4;2;3;5] = [1;2;3;4;5].
Proof. simpl. reflexivity. Qed.

Example sort2 : nat_insert_sort [5;4;3;2;1] = [1;2;3;4;5].
Proof. reflexivity. Qed.

Example sort3 : nat_insert_sort [1;2;3;4;5] = [1;2;3;4;5].
Proof. reflexivity. Qed.

(** ** Proving correctness *)

(** [insert_sorted] (10 pts): we now have a complete definition of
insertion sort over [list nat]'s, [nat_insert_sort]; we want to prove
that it returns a sorting of its input.

One observation that helps to simplify the proof is that we can prove
that (1) [nat_insert_sort] produces a permutation and (2)
[nat_insert_sort] produces a sorted result completely
independently. Furthermore, the proofs of each sub-property can be
broken down into a key lemma about [insert], which is used in the
proof of the sub-property for [nat_insert_sort].

We'll start by proving that [nat_insert] takes each [nat] and sorted
list to a sorted list.  *)

Lemma blah: forall l n x,
x<=n -> nat_sorted (x::l) -> nat_sorted (nat_insert n (x::l)) -> nat_sorted (x::nat_insert n l).
Proof.
intros.
induction l.
simpl.
constructor.
assumption. constructor.
simpl.
destruct (ble_nat_dec n x0) eqn:B.
constructor. assumption. constructor. assumption.
inversion H0; subst. assumption.
constructor.
inversion H0; subst. assumption.

Admitted.

Lemma insert_sorted : forall l n, nat_sorted l -> nat_sorted (nat_insert n l).
Proof.
intros.
generalize dependent n.
induction l.
simpl.
constructor.
intros.
simpl.
destruct (ble_nat_dec n x) eqn:B.
constructor.
assumption. assumption.
inversion H; subst.
simpl.
constructor.
assumption. constructor.
inversion H; subst.
induction H.
simpl.
(*constructor.
simpl.
destruct (ble_nat_dec n n0) eqn:B.
constructor.
assumption. constructor.
constructor. assumption. constructor.
destruct (ble_nat_dec n m) eqn:B1.
simpl.
rewrite B1.
constructor. assumption.
constructor. auto.
assumption.
destruct (ble_nat_dec n n0) eqn:B.
simpl.
rewrite B1.
rewrite B.
simpl in IHnat_sorted.
rewrite B in IHnat_sorted.
constructor. assumption.
constructor. auto. assumption.
simpl.
rewrite B1.
rewrite B.

constructor. assumption.
induction l.
simpl. constructor. assumption. constructor.
simpl.*)

Admitted.

(** [insertion_sorted] (5 pts): now, prove that [nat_insert_sort]
produces a sorted result. One natural proof requires only about 10
lines, using [insert_sorted]. *)

Lemma insertion_sorted : forall l, nat_sorted (nat_insert_sort l).
Proof.
intros.
induction l.
simpl.
constructor.
simpl.
destruct l.
constructor.
apply insert_sorted.
assumption.
Qed.

(** [insert_perm] (10 pts): now, prove that [nat_insert] takes each
[nat] [n] and [list nat] [l] to a permutation of [n :: l]. One natural
proof uses [perm_refl]. *)

Lemma insert_perm : forall l n, perm nat (n:: l) (nat_insert n l).
Proof.
intros.
induction l.
simpl.
apply perm_refl.
simpl.
destruct (ble_nat_dec n x) eqn:B.
apply perm_refl.
inversion IHl; subst.
apply permp with (n:=x) in H0.
apply permp with (n:=n) in H0.
inversion H0; subst.
simpl.
destruct (ble_nat_dec n x) eqn:B1.
(*apply perm_refl.
rewrite <- H2.
apply permp.

rewrite l0 in B.
inversion B.
simpl in IHl.
inversion IHl; subst.
inversion H0.
inversion H.
apply perm_refl. *)
Admitted.

(** [insertion_perm] (10 pts): now, prove that [nat_insert_sort]
permutes its input. One natural proof is only ~15 lines, and uses
[perm_trans].  *)

Lemma insertion_perm : forall l, perm nat l (nat_insert_sort l).
Proof.
intros.
induction l.
constructor.
simpl.
destruct l.
simpl.
apply perm_refl.
inversion IHl; subst.
simpl.
Admitted.

(** [insertion_sorting] (5 pts): finally, prove that [nat_insert_sort]
returns a sorting of its input. One natural proof is extremely
straighforward and uses [insertion_sorted] and [insertion_perm]. *)

Theorem insertion_sorting :
  forall l, is_nat_sorting l (nat_insert_sort l).
Proof.
intros.
unfold is_nat_sorting.
split.
apply insertion_sorted.
apply insertion_perm.
Qed.

End SortingNats.

(** * Sorting Polymorphic [list]s

If you've completed all of the definitions up to this point, then
you've actually defined and proven the correctness of an
implementation of insertion sort for [nat list]'s.

However, sorting functions included in practical libraries are
typically polymorphic: they sort lists of arbitrary data of the same
type, as long as a user provides some function for ordering any two
elements of the type of values stored in the list.

In this next module, we'll define, then state and prove the
correctness of, sorting functions over polymorphic lists. If in
[SortingNats], you (1) defined [nat_insert] to only operate on
elements by applying [ble_nat_dec] and (2) wrote proofs that only
inspected values in a list by considering the result of applying
[ble_nat_dec] to the values, then your proofs from [SortingNats]
should go through after only superficial changes to various names. *)

Module SortingGen.

(** ** Defining sortedness

We'll now define sortedness over polymorphic lists.
*)

(** [binrel]: [binrel]'s will generalize the less-than-or-equal
ordering over [nat]'s used in the specfication of [nat_insert_sort].

For type [T], let [binrel T] be a binary relation over [T],
represented as a function that takes two values of type [T] and
returns a [Prop]: *)

Definition binrel (T : Type) := T -> T -> Prop.

(** [sorted] (5 pts): please generalize [nat_sorted] to be defined
over polymorphic lists.

For type [T], binary relation [ord] over [T], and list l of type [list
T], the [Prop]osition [sorted T ord l] should hold under conditions
directly analogous to those given for [nat_sorted].  *)

(* TODO: complete definition. *)

(** [is_sorting] (5 pts): please generalize [is_nat_sorting] to be
defined over polymorphic lists.

For type [T], binary relation [ord] over [T], and lists [l0] and [l1]
over values of type [T], the [Prop]osition [is_sorting T ord l0 l1]
should hold if all of the following conditions hold:

(1) [l1] is sorted under [T] and [ord];

(2) [l0] permutes to [l1].
*)

(* TODO: complete definition. *)

(** ** Defining insertion sort

We'll now define an insertion sort over polymorphic lists.  *)

(** [comparitor]: [comparitor]'s will generalize [ble_nat_dec] as used
in the definition of [nat_insert].

For type [T] and [ord] a binary relation over [T], let a comparitor
over [T] and [ord] be a function that for all elements [a] and [b] or
type [T], returns either evidence that the ordered pair [(a, b)] is in
[ord] or the pair [(b, a)] is in [ord]. *)

Definition comparitor { T } (ord: binrel T) :=
  forall (a b : T), { ord a b } + { ord b a }.

(** [insert] (5 pts): please generalize [nat_insert] to operate over
polymorphic lists.

[insert] should take:

(1) a type [T];

(2) a binary relation [ord] over [T];

(3) a comparitor [ble_dec] over [T] and [ord];

(4) an element [n] of type [T];

(5) a list [l] of [T] data.

If [l] is sorted, then [insert] returns a sorted list with [n]
inserted into [l].

You can define [insert] by essentialy replacing uses of [ble_nat_dec]
in [nat_insert] with uses of [ble_dec]. *)

(* TODO: complete definition. *)

(** [insert_sort] (5 pts): please generalize [nat_insert_sort] to
operate over polymorphic lists.

[insert_sort] sould take:

(1) a type [T];

(2) a binary relation [ord] over [T];

(3) a comparitor [ble_dec] over [T] and [ord];

(4) a list [l] of [T] values.

[insert_sort] should return a sorting of [l] under [T] and [ord].

You can define [insert_sort] by essentially replacing uses of
[nat_insert] in [nat_insert_sort] with [insert]. *)

(* TODO: complete definition. *)

(** ** Proving correctness *)

(** [insert_sorted] (5 pts): please generalize
[SortingNats.insert_sorted] for [insert], and prove the claim.

[insert_sorted] should claim that for each type [T], binary relation
[ord] over [T], and comparitor [ble_dec] over [T] and [ord], if [l] is
sorted under [T] and [ord], then [insert ble_dec n l] is sorted under
[T] and [ord].  *)

(* TODO: define and prove. *)

(** [insertion_sorted] (5 pts): please generalize
[SortingNats.insertion_sorted] for [insert_sort], and prove the claim.

[insertion_sorted] should claim that for each type [T], binary
relation [ord], comparitor [ble_dec] over [T] and [ord], and list [l] of
type [list T], [insert_sort T ord ble_dec l] is sorted.

The proof of insertion_sorted can be structured almost identically to
a proof of [SortingNats.insertion_sorted]. *)

(* TODO: define and prove. *)

(** [insert_perm] (5 pts): please generalize [SortingNats.insert_perm]
for [insert], and prove the claim.

[insert_perm] should claim that for each type [T], binary relation
[ord] over [T], comparitor [ble_dec] over [T] and [ord], element [n]
of type [T], and list [l] of [T] values, [n :: l] permutes to [insert
T ord ble_dec n l].

The proof can be structured almost identically to a clean proof of
[insert_perm].  *)

(* TODO: define and prove. *)

(** [insertion_perm] (5 pts): please generalize
[SortingNats.insertion_perm] to claim that [insert_sort] returns a
permutation of its input, and then prove the claim.

[insertion_perm] should claim that for each type [T], binary relation
[ord] over [T], comparitor [ble_dec] over [T] and [ord], and list [l] of
values of type [T], [l] permutes to [insert_sort ble_dec l]. *)

(* TODO: define and prove. *)

(** [insertion_sorting] (5 pts): please generalize
[SortNats.insertion_sorting] to claim the correctness of
[insert_sort], then prove the claim.

[insertion_sorting] should claim that for each type [T], binary
relation [ord] over [T], and comparitor [ble_dec] over [T] and [ord],
[insert_sort T ord ble_dec l] is a sorting of [l].

The proof can be structured very similarly to the proof of
[SortNats.insertion_sorting].  *)

(* TODO: define and prove. *)

(** It's now trivial to define [nat_insert_sort] as an application of
[insert_sort]: *)

(*Definition nat_insert_sort := insert_sort (SortingNats.ble_nat_dec).

(** We could also prove that the [nat_insert_sort] that we wrote by
hand is equivalent to the version produced as application of
[insert_sort]. I.e., we could prove: *)

Theorem eq_sorts :
  forall l, SortingNats.nat_insert_sort l = nat_insert_sort l. *)

(** However, assuming that the two programs were written with the same
structure, this proof wouldn't be terribly interesting. *)

(** Having defined and verified implementations of insertion sort, a
natural next step is to determine what is involved to prove other
favorite sorting algorithms, namely ones with tighter worst-case
performance bounds, such as [quicksort] and [mergesort].

These can be implemented and verified in Coq; however, defining
implementations that will even be accepted by the Coq compiler is
actually non-trivial. The complexity stems from the fact that the
natural definition of [mergesort] recurses on a split of its input
list, but the Coq compiler cannot determine that this definition of
[mergesort] performs well-founded recursion (compare to insertion
sort, which recurses on the tail of a list).

If you're interested in learning how to overcome this problem, Coq
provides a verified implementation of [mergesort] in its standard
library <https://coq.inria.fr/library/Coq.Sorting.Mergesort.html>. *)

End SortingGen.


(*
        #####################################################
        ###  PLEASE DO NOT DISTRIBUTE SOLUTIONS PUBLICLY  ###
        #####################################################
*)
(*
    You are only allowed to use these tactics:

    simpl, reflexivity, intros, rewrite, destruct, induction, apply, assert

    You are not allowed to use theorems outside this file *unless*
    explicitly recommended.
*)

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

(**

Show that 5 equals 5.

 *)
Theorem ex1:
  5 = 5.
Proof.
  simpl.
  reflexivity.
Qed.

(**

Show that equality for natural numbers is reflexive.

 *)
Theorem ex2:
  forall (x:nat), x = x.
Proof.
  intros x.
  simpl.
  reflexivity.
Qed.

(**

Show that [1 + n] equals the successor of [n].

 *)
Theorem ex3:
  forall n, 1 + n = S n.
Proof.
  intros n.
  assert (H: 1 + n = S n). {reflexivity. }
  simpl.
  reflexivity.
Qed.

(**

Show that if [x = y], then [y = x].

 *)
Theorem ex4:
  forall x (y:nat), x = y -> y = x.
Proof.
  intros x.
  intros y.
  intros x_eq_y.
  rewrite -> x_eq_y.
  simpl.
  reflexivity.
Qed.

(**

Show that if the result of the conjunction and the disjunction equal,
then both boolean values are equal.

 *)
Theorem ex5:
  forall (b c : bool), (andb b c = orb b c) -> b = c.
Proof.
  intros b c.
  destruct b.
- simpl.
  intros x.
  rewrite x.
  reflexivity.
- simpl.
  intros y.
  rewrite y.
  reflexivity.
Qed.

(**

In an addition, we can move the successor of the left-hand side to
the outer most.

 *)
Theorem ex6:
  forall n m, n + S m = S (n + m).
Proof.
  intros n m.
  induction n as [|n' IH].
  - reflexivity.
  - simpl.
    rewrite <- IH.
    reflexivity.
Qed.

(**

If two additions are equal, and the numbers to the left of each addition
are equal, then the numbers to the right of each addition must be equal.
To complete this exercise you will need to use the auxiliary
theorem: eq_add_S

 *)
Theorem ex7:
  forall x y n, n + x = n + y -> x = y.
Proof.
  intros x y n.
  intros nx_eq_ny.
  induction n as [ | i IH].
  - simpl in nx_eq_ny.
    rewrite -> nx_eq_ny.
    reflexivity.
  - simpl in nx_eq_ny.
    apply eq_add_S.
    rewrite <- IH.
    * reflexivity.
    * apply eq_add_S.
      rewrite <- nx_eq_ny.
      reflexivity.
Qed.

(**

Show that addition is commutative.
Hint: You might need to prove `x + 0 = x` and `S (y + x) = y + S x`
separately.

 *)
Theorem ex8:
  forall x y, x + y = y + x.
Proof.
Admitted.

(**

If two additions are equal, and the numbers to the right of each addition
are equal, then the numbers to the left of each addition must be equal.

Hint: Do not try to prove this theorem directly. You should be using
auxiliary results. You can use Admitted theorems.

 *)
Theorem ex9:
  forall x y n, x + n = y + n -> x = y.
Proof.

Admitted.


(* Software Foundations *)
(* Exercice ** plus_swap' *)
Lemma  plus_0_r: forall n: nat, n+0=n.
Proof.
    intros. induction n as [|n'].
    reflexivity.
    simpl. rewrite IHn'. reflexivity.
Qed.

Lemma plus_succ: forall n m: nat, n + S m = S (n + m).
Proof.
    intros. induction n as [|n'].
    simpl. reflexivity.
    simpl. rewrite IHn'. reflexivity.
Qed.

Lemma plus_comm: forall n m: nat, n+m = m +n.
Proof.
    intros. induction n as [|n'].
    simpl. rewrite plus_0_r. reflexivity.
    simpl. rewrite IHn'. rewrite plus_succ. reflexivity.
Qed.

Theorem plus_swap: forall n m p: nat, n + (m + p) = m + (n + p).
Proof.
    intros. rewrite plus_comm. induction m as [|m'].
    simpl. rewrite plus_comm. reflexivity.
    simpl. rewrite IHm'. reflexivity.
Qed.

(* using only the Lemma plus_succ *)
Theorem plus_swap': forall n m p: nat, n + (m + p) = m + (n + p).
Proof.
    intros. induction n as [|n']. induction m as [|m'].
    simpl. reflexivity.
    simpl. reflexivity.
    simpl. rewrite IHn'. (*remember (n'+p) as u*) rewrite plus_succ. reflexivity.
Qed.


Require Import List.
Require Import Bool.
Require Import Arith.

Require Extraction.
Require Import ExtrOcamlNatInt.

Definition filter_lte (a: nat) list := filter (fun n=> n <=? a) list.
Definition filter_gt (a: nat) list := filter (fun n=> negb(n <=? a)) list.

Fixpoint quicksort (l: list nat) :=
    match l with
      nil => nil
    | h :: t => let sorted_t := quicksort t in
                    filter_lte h sorted_t
                    ++ h :: filter_gt h sorted_t
    end.

Compute quicksort (nil).
Compute quicksort (1::2::3::6::1::2::1::0::nil).

Fixpoint length (l: list nat) : nat :=
    match l with
      nil => 0
    | a :: tl => 1 + (length (tl))
    end.

Theorem lengthIsCommutative: forall l m : list nat, length(l ++ m) = length(m ++ l).
Proof.
    induction l.
    intros.
    simpl.
    rewrite app_nil_r.
    reflexivity.
    intros.
    simpl.
    rewrite IHl.
    induction m.
    simpl.
    reflexivity.
    simpl.
    rewrite IHm.
    reflexivity.
Qed.

Theorem lengthIsDistributive: forall l j:(list nat), length (l ++ j) = length l + length j.
Proof.
    induction j.
    simpl.
    rewrite app_nil_r.
    firstorder.
    rewrite lengthIsCommutative.
    simpl.
    rewrite Nat.add_succ_r with (n:=length l) (m:=length j).
    rewrite lengthIsCommutative.
    rewrite IHj.
    firstorder.
Qed.

Theorem twoFilterLengthEq: forall n l, length(filter_lte n l) + length(filter_gt n l) = length(l).
Proof.
    intros.
    induction l.
    simpl.
    reflexivity.
    unfold filter_lte.
    assert ((fun n0 : nat => n0 <=? n) a = true \/ (fun n0 : nat => negb(n0 <=? n)) a = true).
    case (a <=? n).
    left; reflexivity.
    right; simpl; reflexivity.
    destruct H.
    simpl.
    rewrite H.
    simpl.
    assert (filter_lte n l = filter (fun n0 : nat => n0 <=? n) l).
    unfold filter_lte.
    firstorder.
    rewrite <- H0.
    rewrite IHl.
    firstorder.
    assert (filter_lte n (a::l) = filter (fun n0 : nat => n0 <=? n) (a::l)).
    unfold filter_lte.
    firstorder.
    rewrite <- H0.
    simpl.
    rewrite H.
    assert((a <=? n) = false).
    rewrite <- negb_involutive with (b:=(a <=? n)).
    rewrite H.
    firstorder.
    rewrite H1.
    simpl.
    rewrite Nat.add_succ_r with (n:=length(filter_lte n l)) (m:=(length (filter_gt n l))).
    rewrite IHl.
    firstorder.
Qed.

Theorem sortLengthUnchanged :
    forall l:(list nat),
    length l = length (quicksort l).
Proof.
    induction l.
    simpl.
    reflexivity.
    simpl.
    rewrite IHl.
    rewrite lengthIsDistributive.
    simpl.
    rewrite Nat.add_succ_r with
        (n:=length(filter_lte a (quicksort l)))
        (m:=(length (filter_gt a (quicksort l)))).
    rewrite twoFilterLengthEq.
    reflexivity.
Qed.

(* https://gist.github.com/adampalay/44b9ac3515469d92cb2f295e6179b8b9 *)

Extraction "quicksort.ml" quicksort.


From mathcomp Require Import ssreflect ssrnat.

Section natrualNumber.

Lemma add0nEqn (n : nat) : 0 + n = n.
Proof.
  by [].
Qed.

Lemma addn3Eq2n1 (n : nat) : n + 3 = 2 + n + 1.
Proof.
  rewrite addn1.
  rewrite add2n.
  rewrite addnC.
  by [].
Qed.

Fixpoint sum n := if n is m.+1 then sum m + n else 0.
(* n is m.+1 のところはいわゆるパターンマッチ？ *)

Lemma sumGauss (n : nat) : sum n * 2 = (n + 1) * n.
Proof.
  (* move: n; elim.
  move => //.
  move =>  n.
  move => IHn. *)
  elim: n => [// | n IHn]. (* induction *)
  rewrite mulnC.
  rewrite (_ : sum (n.+1) = n.+1 + (sum n)); last first. (* replace *)
  rewrite /=. (* simpl. *)
  by rewrite addnC.
  rewrite mulnDr.
  rewrite mulnC in IHn. (* コンテキストへの適用 *)
  rewrite IHn.
  rewrite 2!addn1. (* 2回適用 *)
  rewrite [_ * n]mulnC. (* パターンマッチ適用 *)
  rewrite -mulnDl. (* 右辺から左辺への書き換え *)
  by [].
Qed.

End natrualNumber.


Require Import Arith List Sorting Permutation.
Import ListNotations.
From mathcomp Require Import ssreflect ssrnat ssrbool.

Fixpoint insert (i:nat) (l:list nat) :=
  match l with
  | [] => [i]
  | h :: t => if i <= h then i :: h :: t else h :: insert i t
  end.

Fixpoint sort (l:list nat) : list nat :=
  match l with
  | [] => []
  | h :: t => insert h (sort t)
  end.

Example sort_pi :
  sort [3;1;4;1;5;9;2;6;5;3;5]
  = [1;1;2;3;3;4;5;5;5;6;9].
compute. reflexivity. Qed.

Inductive sorted : list nat -> Prop :=
| sorted_nil : sorted []
| sorted_1 : forall x, sorted [x]
| sorted_cons : forall x y l,
    x <= y -> sorted (y :: l) -> sorted (x :: y :: l).

Lemma insert_sorted:
  forall a l, sorted l -> sorted (insert a l).
Proof.
  intros a l S. induction S; simpl.
   -- apply sorted_1.
   -- SearchPattern (forall n m, _ (n < m) -> _ (n <= m)).
      case_eq (a <= x); intros H; apply sorted_cons; try apply sorted_1.
      apply H. apply ltnW. by rewrite leqNgt ltnS H.
   -- case_eq (a <= x); intros H1. apply sorted_cons. apply H1.
    apply sorted_cons. apply H. apply S.
    move: IHS. unfold insert. case_eq (a <= y); intros H2 IHS.
    apply sorted_cons. apply /ltnW. by rewrite leqNgt ltnS H1.
    apply IHS. fold insert. fold insert in IHS.
    apply sorted_cons. apply H. apply IHS.
Qed.

Theorem sort_sorted: forall l, sorted (sort l).
Proof.
  induction l.
   -- apply sorted_nil.
   -- apply (insert_sorted a _ IHl).
Qed.

Lemma insert_permute: forall x l, Permutation (x :: l) (insert x l).
Proof.
  fix insert_permute 2.
  intros x l. destruct l as [|a l'].
   -- apply Permutation_refl.
   -- simpl. case_eq (x <= a); intros H.
   ---- apply Permutation_refl.
   ---- apply (@perm_trans _ _ (a :: x :: l') _).
    apply perm_swap. apply perm_skip. apply insert_permute.
  Qed.

Theorem sort_perm : forall l, Permutation l (sort l).
Proof.
  induction l.
   -- apply Permutation_refl.
   -- simpl. apply (@perm_trans _ _ (a :: sort l) _).
    apply perm_skip, IHl.
    apply insert_permute.
Qed.

Definition is_a_sorting_algorithm (f: list nat -> list nat) :=
  forall al, Permutation al (f al) /\ sorted (f al).

Theorem insertion_sort_correct : is_a_sorting_algorithm sort.
Proof.
  intros l. apply conj. apply sort_perm. apply sort_sorted. Qed.


Require Import Omega.
Open Scope list_scope.

(* PAIR*)
Inductive is_even : nat -> Prop :=
| is_even_O : is_even 0
| is_even_S : forall n : nat , is_even n-> is_even (S (S n)).

Fixpoint even (n : nat) : Prop :=
  match n with
  | 0 => True
  | 1 => False
  | (S (S n)) => even n
end.

Functional Scheme even_ind := Induction for even Sort Prop.

Theorem even_sound :
 forall (n : nat) (v : Prop) , (even n) = True -> is_even n.
Proof.
  do 2 intro.
  functional induction (even n) using even_ind; intros.
  apply is_even_O.
  elimtype False; rewrite H; auto.
  apply is_even_S; apply IHP; assumption.
Qed.

(* FACTORIELLE *)

Inductive is_fact : nat -> nat -> Prop :=
| fact_0 :  is_fact 0 1
| fact_S : forall n : nat , forall s : nat , is_fact n s  -> is_fact (S n) (s * (S n)).

Fixpoint fact (n : nat) : nat :=
  match n with
 | 0 => 1
 | (S n) => (fact n * (S n))
 end.

Functional Scheme fact_ind := Induction for fact Sort Prop.

Theorem fact_sound :
  forall (n : nat) (r: nat) , (fact n ) = r -> is_fact  n r.
Proof.
  intro.
  functional induction (fact n) using fact_ind; intros.
  elim H.
  apply fact_0.
  elim H.
  apply fact_S.
  apply (IHn0 (fact n0)).
  reflexivity.
Qed.

(* TRI PAR INSERTION *)

Inductive is_perm : (list nat) -> (list nat) -> Prop :=
| is_perm_refl : forall l1 : (list nat) , is_perm l1 l1
| is_perm_transitive : forall l1 l2 l3 :(list nat) , is_perm l1 l2 -> is_perm l2 l3 -> is_perm l1 l3
| is_perm_sym : forall l1 l2 : (list nat) , is_perm l1 l2 -> is_perm l2 l1
| is_perm_cons : forall l1 l2 : (list nat) , forall a : nat , is_perm l1 l2 -> is_perm (a::l1) (a::l2)
| is_perm_a : forall l1 : (list nat) , forall a : nat , is_perm(a::l1)(l1++ a::nil).

Definition l1 := 1::2::3::nil.
Definition l2 := 3::2::1::nil.

Lemma undeuxtrois : is_perm l1 l2.
unfold l1.
unfold l2.
apply (is_perm_transitive (1::(2::(3::nil))) ((2::(3::nil))++(1::nil)) (3::(2::(1::nil)))).
apply is_perm_a.
simpl.
apply (is_perm_transitive (2::(3::(1::nil))) ((3::(1::nil))++(2::nil)) (3::(2::(1::nil)))).
apply is_perm_a.
simpl.
apply is_perm_cons.
apply (is_perm_transitive (1::(2::nil)) ((1::nil)++(2::nil)) (2::(1::nil))).
simpl.
apply is_perm_cons.
apply is_perm_refl.
apply is_perm_a.
Qed.

Inductive is_sort :(list nat) -> Prop :=
| is_sort_0 : is_sort nil
| is_sort_1 : forall n : nat , is_sort(n::nil)
| is_sort_N : forall n m : nat , forall l :(list nat) ,  n <= m -> is_sort(m::l) -> is_sort (n::m::l).

Lemma sort123 : is_sort l1.
unfold l1.
apply is_sort_N.
omega.
apply is_sort_N.
omega.
apply is_sort_1.
Qed.

Fixpoint Insert (x : nat) (l : (list nat)) : (list nat) :=
  match l with
 | (nil) => (x::nil)
 | h::t => match le_dec x h with
      | left _ => x::h::t
      | right _ => h::(Insert x t)
      end
end.

Fixpoint Sort (l : (list nat)) : (list nat) :=
 match l with
 | (nil) => (nil)
 | h::t => (Insert h (Sort t))
end.

(* Theorème intermediaire 1 -- Il sert à prouver le theoreme intermediaire 2 *)

Theorem inter1 : forall (a1 : nat) (a2 : nat) (l : (list nat)) , (is_perm (a1 :: a2 :: l) (a2 :: a1 :: l)).
Proof.
intro.
intro.
intro.
apply (is_perm_transitive  (a1 :: a2 :: l) ( a2 :: l ++ a1::nil ) (a2 :: a1 :: l)).
apply is_perm_a.
apply is_perm_cons.
apply is_perm_sym.
apply is_perm_a.
Qed.

(* Theoreme intermédiaire 2 *)
Theorem is_perm_insert :
forall (a : nat)  (l : (list nat)) , (is_perm (a::l) (Insert a l)).
Proof.
induction l;
simpl.
apply is_perm_a.
elim (le_dec a a0).
intros.
apply is_perm_refl.
intros.
apply (is_perm_transitive (a :: a0 :: l) (a0 :: a :: l) (a0 :: Insert a l)).
apply inter1.
apply is_perm_cons.
apply IHl.
Qed.

(* Theoreme intermédiaire 3 *)
Theorem is_sort_cons :
forall ( a : nat) (l : (list nat)) , (is_sort l) -> (is_sort (Insert a l)).
Proof.
induction l.
simpl.
intros.
apply is_sort_1.
intros.
simpl.
elim (le_dec a a0).
intros.
apply is_sort_N.
apply a1.
apply H.
intros.

Theorem Insert_Sort_Sound :
  forall (l : (list nat)) (l1 : (list nat)) ,(Sort l) = l1 -> (is_perm l l1) /\ (is_sort l1).
Proof.
induction l.
intros.
split.
simpl in H.
rewrite <- H.
apply is_perm_refl.
simpl in H.
rewrite <- H.
apply is_sort_0.
split.
rewrite <- H.
simpl.
apply (is_perm_transitive (a::l)  (a:: (Sort l)) (Insert a (Sort l))).
apply is_perm_cons.
apply IHl.
reflexivity.
apply is_perm_insert.


Require Import List.

(*Require Import CpdtTactics.*)

Set Implicit Arguments.

Inductive nat : Set :=
| O : nat
| S : nat -> nat.

Fixpoint plus (n m : nat) : nat :=
match n with
  | O => m
  | S n' => S (plus n' m)
end.

Theorem O_plus_n : forall n:nat, plus O n = n.
  intros.
  simpl.
  reflexivity.
Qed.

Theorem n_plus_O : forall n:nat, plus n O = n.
  induction n.
  simpl.
  reflexivity.
  simpl.
  rewrite IHn.
  reflexivity.
Qed.

Theorem plus_assoc_from_left : forall n m p : nat,
  plus (plus n m) p = plus n (plus m p).
  induction n.
  simpl.
  reflexivity.
  simpl.
  intros.
  rewrite IHn.
  reflexivity.
Qed.

Theorem plus_assoc_from_right : forall n m p : nat,
  plus n (plus m p) = plus (plus n m) p.
  induction n.
  simpl.
  reflexivity.
  intros.
  simpl.
  rewrite IHn.
  reflexivity.
Qed.

Lemma plus_succ_inside : forall n m : nat,
  S(plus n m) = plus n (S m).
  induction n.
  simpl.
  reflexivity.
  simpl.
  intros.
  rewrite IHn.
  reflexivity.
Qed.

Theorem plus_commutativity : forall n m : nat,
  plus n m = plus m n.

  induction n.
  simpl.
  intros.
  rewrite n_plus_O.
  reflexivity.
  simpl.
  intros.
  rewrite IHn.
  apply plus_succ_inside.
Qed.

Fixpoint mult (n m : nat) : nat :=
match n with
  | O => O
  | S n' => plus (mult n' m) m
end.

Definition I : nat := S O.

Theorem n_plus_I : forall n:nat, plus n I = S n.
  induction n.
  simpl.
  unfold I.
  reflexivity.
  simpl.
  rewrite IHn.
  reflexivity.
Qed.

Theorem I_mult_n : forall n:nat, mult I n = n.
  simpl.
  reflexivity.
Qed.

Theorem n_mult_I : forall n:nat, mult n I = n.
  induction n.
  simpl.
  reflexivity.
  simpl.
  rewrite IHn.
  simpl.
  apply n_plus_I.
Qed.

Lemma n_mult_O : forall n:nat, mult n O = O.
  induction n.
  simpl.
  reflexivity.
  simpl.
  rewrite IHn.
  simpl.
  reflexivity.
Qed.

Lemma O_mult_n : forall n:nat, mult O n = O.
  induction n.
  simpl.
  reflexivity.
  simpl.
  reflexivity.
Qed.

Theorem right_distributivity : forall n m p : nat,
  mult (plus n m) p = plus (mult n p) (mult m p).
  induction n.
  simpl.
  reflexivity.
  simpl.
  intros.
  rewrite IHn.
  rewrite plus_assoc_from_left.
  assert (plus (mult m p) p = plus p (mult m p)).
  rewrite plus_commutativity.
  reflexivity.
  rewrite H.
  rewrite plus_assoc_from_right.
  reflexivity.
Qed.

Theorem left_distributivity : forall n m p : nat,
  mult n (plus m p) = plus (mult n m) (mult n p).
  induction n.
  intros.
  simpl.
  reflexivity.
  intros.
  simpl.
  rewrite IHn.
  assert(plus (plus (mult n m) (mult n p)) (plus m p)
    = plus (mult n m) (plus (mult n p) (plus m p))).
  rewrite plus_assoc_from_left.
  reflexivity.
  rewrite H.
  assert(plus (mult n p) (plus m p) = plus (plus (mult n p) m) p).
  rewrite plus_assoc_from_right.
  reflexivity.
  rewrite H0.
  assert(plus (mult n p) m = plus m (mult n p)).
  rewrite plus_commutativity.
  reflexivity.
  rewrite H1.
  assert(plus (plus (mult n m) m) (plus (mult n p) p) = plus (mult n m) (plus m (plus (mult n p) p))).
  rewrite plus_assoc_from_left.
  reflexivity.
  rewrite H2.
  rewrite plus_assoc_from_left.
  reflexivity.
Qed.

Theorem mult_assoc_from_left: forall n m p :nat,
  mult (mult n m) p = mult n (mult m p).
  induction n.
  intros.
  simpl.
  reflexivity.
  intros.
  simpl.
  rewrite right_distributivity.
  rewrite IHn.
  reflexivity.
Qed.

Theorem mult_assoc_from_right: forall n m p :nat,
  mult n (mult m p) = mult (mult n m) p.
  induction n.
  intros.
  simpl.
  reflexivity.
  intros.
  simpl.
  rewrite IHn.
  rewrite right_distributivity.
  reflexivity.
Qed.

Theorem mult_commutativity: forall n m : nat,
  mult n m = mult m n.
  induction n.
  intro.
  simpl.
  rewrite n_mult_O.
  reflexivity.
  intro.
  simpl.
  rewrite IHn.
  assert(S n = plus n I).
  rewrite n_plus_I.
  reflexivity.
  rewrite H.
  rewrite left_distributivity.
  rewrite n_mult_I.
  reflexivity.
Qed.


Set Warnings "-notation-overriden,-parsing".

Require Export Logic.
Require Coq.omega.Omega.

(* CIC built from function types and inductive types *)

Inductive ev : nat -> Prop :=
  | ev_0 : ev 0
  | ev_SS : forall m:nat, ev m -> ev (S (S m)).

Theorem ev_4 : ev 4.
Proof. apply ev_SS,ev_SS,ev_0. Qed.

Theorem ev_4' : ev 4.
Proof. apply (ev_SS 2 (ev_SS 0 ev_0)). Qed.

Theorem ev_plus4 :
  forall n, ev n -> ev (4 + n).
Proof.
  intros n H. apply ev_SS, ev_SS. assumption.
Qed.

Definition double (n : nat) : nat :=
  plus n n.
Compute double 3.

Definition pred (n : nat) : nat :=
  match n with
  | 0 => 0
  | S n' => n'
  end.

Theorem ev_double : forall n, ev (double n).
Proof.
  intros n. unfold double. induction n.
  - apply ev_0.
  - simpl. rewrite <- plus_comm. simpl.
    apply ev_SS. apply IHn.
Qed.

Theorem ev_minus2 : forall n, ev n -> ev (pred (pred n)).
Proof.
  intros n E. inversion E as [| n' E'].
  - simpl. apply ev_0.
  - simpl. apply E'.
Qed.

Theorem ev_minus2' : forall n, ev n -> ev (pred (pred n)).
Proof.
  intros n E. destruct E.
  - simpl. apply ev_0.
  - simpl. apply E.
Qed.

Theorem evSS_ev : forall n, ev (S (S n)) -> ev n.
Proof.
  intros n H. inversion H as [| m h1 h2]. apply h1.
Qed.

Theorem one_not_even : not (ev 1).
Proof. intros H. inversion H. Qed.

Theorem SSSSev__even: forall n, ev (S (S (S (S n)))) -> ev n.
Proof.
  intros n H. inversion H as [| m H1 H2].
  apply evSS_ev in H1. apply H1.
Qed.

Theorem even5_nonsense : ev 5 -> 2 + 2 = 9.
Proof.
  intros H. simpl. inversion H as [| n' E H1].
  inversion E as [| n'' E' H2]. inversion E'.
Qed.

(*
Check eq.
Print eq_symm.
Theorem eq_symm :

Lemma ev_even_firsttry :  n,
  ev n -> exists k, n = double k.
Proof.
  intros n E. inversion E as [| n' E'].
  - exists 0. reflexivity.
  - assert (I : (exists k', double k' = n') ->
                (exists k, double k = S (S n'))).
    { intros [k' Hk']. rewrite <- Hk'. exists (S k').
      unfold double. simpl. rewrite -> plus_comm. simpl. reflexivity. }
  Search eq. rewrite -> eq_Symmetric.
*)

Lemma add_S : forall n m: nat, n = m -> S n = S m.
Proof. intros n m E. rewrite E. reflexivity. Qed.

Lemma remove_S : forall n m:nat, S n = S m -> n = m.
Proof. intros n m E. inversion E. reflexivity. Qed.

Lemma succ_even : forall k:nat, double (S k) = S (S (double k)).
Proof.
  intros k. unfold double.
  simpl. apply add_S. apply plus_comm.
Qed.

Lemma ev_even : forall n, ev n -> exists k, n = double k.
Proof.
  intros n E.
  induction E as [|n' E' IH].
  { exists 0. reflexivity. }
  { destruct IH as [k' Hk']. rewrite Hk'.
    exists (S k'). apply eq_sym. apply succ_even.
  }
Qed.

(* when to use indprop and when to use decidable definition? *)
(* maybe indprop more general? I think this is true *)

Lemma rev_SS : forall n, ev (S (S n)) -> ev n.
Proof.
  intros n H. inversion H. assumption.
Qed.

Lemma exists_half_to_ev : forall n, ((exists k, n = double k) -> ev n).
Proof.
  intros n [k H]. rewrite H. clear H. induction k.
  { apply ev_0. } { rewrite succ_even. apply ev_SS. apply IHk. }
Qed.

Theorem ev_even_iff : forall n,
 ev n <-> exists k, n = double k.
Proof.
  intros n. split.
  { apply ev_even. }
  { apply exists_half_to_ev. }
Qed.

(* pose v assert? *)
(* assert adds to subgoals,
   pose creates definition maybe *)
Theorem ev_sum : forall n m, ev n -> ev m -> ev (n + m).
Proof.
  intros n m En Em.
  induction En as [|n' _ IH].
  { simpl. apply Em. }
  { simpl. apply ev_SS. apply IH. }
Qed.

Inductive ev' : nat -> Prop :=
| ev'_0 : ev' 0
| ev'_2 : ev' 2
| ev'_sum : forall n m, ev' n -> ev' m -> ev' (n + m).

(* one disjuntive branch for each contructor *)

Theorem ev'_ev : forall n, ev' n <-> ev n.
Proof.
  intros n. split.
  { intros Ev'_n. induction Ev'_n as [| | m m' Ev'_m IHEv_m Ev'_m' IHEv_m'].
    { apply ev_0. }
    { apply ev_SS, ev_0. }
    { apply ev_sum. { apply IHEv_m. } { apply IHEv_m'. }}
  }
  { intros Ev_n. induction Ev_n as [| n' Ev_n' IHEv'_n'].
    { apply ev'_0. }
    { replace (S (S n')) with (2 + n').
      { apply ev'_sum. { apply ev'_2. } { apply IHEv'_n'. } }
      { reflexivity. }
    }
  }
Qed.

Theorem ev_ev__ev : forall n m,
  ev (n + m) -> ev n -> ev m.
Proof.
  intros n m Ev_nm Ev_n.
  induction Ev_n as [| n' Ev_n' IH].
  { simpl in Ev_nm; assumption. }
  { simpl in Ev_nm. apply rev_SS in Ev_nm.
    apply IH. apply Ev_nm.
  }
Qed.

(*
Theorem ev_plus_plus : forall n m p,
  ev (n + m) -> ev (n + p) -> ev (m + p).
Proof.
  intros n m p E_nm E_np.
  assert (H := forall n m : (ev n -> ev m)).
 *)


(* week_36c_mul.v *)
(* dIFP 2014-2015, Q1, Week 36 *)
(* Olivier Danvy <danvy@cs.au.dk> *)

(* ********** *)

Require Import Arith Bool.

Require Import unfold_tactic.

(* ********** *)

Notation "A === B" := (beq_nat A B) (at level 70, right associativity).

Definition unit_tests_for_multiplication (mul : nat -> nat -> nat) :=
  (mul 0 0 === 0)
  &&
  (mul 0 1 === 0)
  &&
  (mul 1 1 === 1)
  &&
  (mul 2 1 === 2)
  &&
  (mul 2 0 === 0)
  &&
  (mul 2 2 === 4)
  &&
  (mul 0 2 === 0)
  &&
  (mul 1 2 === 2).

(* Exercise 0: flesh out the unit tests above with more tests. *)

(* mult is the built-in multiplication in Coq (infix notation: * ): *)
Compute (unit_tests_for_multiplication mult).
(*
     = true
     : bool
*)

(* Exercise 1: why is there a space in the comment just above
   on the right of the infix notation for multiplication?

  Answer: It seems that when using the Notation ARG that the
  ARG is composed of tokens separated by spaces. Seeing that
  Notation "a*b" := (mult a b) will yield a parse error. So
  I would say it's because that the definition of the infix
  operator is Notation "a * b" that we write it like that.
  Though we can still utilize the infix operator without
  the spaces it was defined with seeing that
  Compute(3*4) yields a correct response of 12.
*)

(* ********** *)

Definition specification_of_multiplication (mul : nat -> nat -> nat) :=
  (forall j : nat,
    mul O j = 0)
  /\
  (forall i' j : nat,
    mul (S i') j = j + (mul i' j)).

(* ********** *)

(* For the following exercise,
   the following lemmas from the Arith library might come handy:
   plus_0_l, plus_0_r, plus_comm, and plus_assoc.
*)

Check plus_0_l.
Check mult_0_l.

(* Helper for later *)
(* Would we rather use unfold plus or the unfold_tactic with plus? *)
Proposition plus_1_S :
  forall n : nat,
    S n = plus 1 n.
Proof.
  intro n.
  (* unfold_tactic plus. *)
  unfold plus.
  reflexivity.
Qed.

(*
    show that 0 is left-absorbant for multiplication
    (aka mult_0_l in Arith)
*)

Proposition multiplication_bc_left :
  forall (mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall j : nat,
      mul 0 j = 0.
Proof.
  intro mult.
  intro S_mult.
  intro j.
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc _].
  rewrite -> (H_mult_bc j).
  reflexivity.
Qed.

(*
    show that 0 is right-absorbant for multiplication
    (aka mult_0_r in Arith)
*)

Proposition multiplication_bc_right :
  forall (mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall j : nat,
      mul j 0 = 0.
Proof.
  intro mult.
  intro S_mult.
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  intro j.

  induction j as [ | n' IHn'].

  (* Base case: *)
  apply (H_mult_bc 0).

  (* Induction case: *)
  rewrite -> (H_mult_ic n' 0).
  rewrite -> (plus_0_l (mult n' 0)).
  apply IHn'.
Qed.

(*
   show that 1 is left-neutral for multiplication
   (aka mult_1_l in Arith) x
*)

Proposition multiplication_1_neutral_left :
  forall (mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall j : nat,
      mul 1 j = j.
Proof.
  intro mult.
  intro S_mult.
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  intro j.
  rewrite -> (H_mult_ic 0 j).
  rewrite -> (H_mult_bc j).
  rewrite -> (plus_0_r j).
  reflexivity.
 Qed.

(* COULD HAVE BEEN DONE WITH INDUCTION! *)

(*
    show that 1 is right-neutral for multiplication
    (aka mult_1_r in Arith)
*)

Proposition multiplication_1_neutral_right :
  forall (mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall j : nat,
      mul j 1 = j.
Proof.
  intro mult.
  intro S_mult.
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  intro j.
  induction j as [ | n' IHn'].

  (* Base case: *)
  apply (H_mult_bc 1).

  (* Induction case: *)
  rewrite -> (H_mult_ic n' 1).
  Check(IHn').
  rewrite -> (IHn').
  symmetry.
  rewrite -> (plus_1_S n').
  reflexivity.
Qed.

(*
   show that multiplication is commutative
   (aka mult_comm in Arith)
*)

Check(mult_comm).

Proposition multiplication_ic_left :
  forall (mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall (x y : nat),
      y + mul x y = mul (S x) y.
Proof.
  intro mult.
  intro S_mult.
  intros x y.
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  rewrite -> (H_mult_ic x y).
  reflexivity.
Qed.

Proposition multiplication_ic_right :
  forall (mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall (x y : nat),
      x + mul x y = mul x (S y).
Proof.
  intro mult.
  intro S_mult.
  intros x y.
  assert (mul_s := S_mult).
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  induction x as [ | n' IHn'].

  (* Base case: *)
  rewrite -> (H_mult_bc).
  rewrite -> (multiplication_bc_left mult mul_s (S y)).
  rewrite -> (plus_0_l 0).
  reflexivity.

  (* Induction case: *)
  rewrite -> (H_mult_ic n' y).
  rewrite -> (H_mult_ic n' (S y)).
  rewrite <- (IHn').

  rewrite -> (plus_assoc (S n') y (mult n' y)).
  rewrite -> (plus_assoc (S y) n' (mult n' y)).

  rewrite -> (plus_1_S n').
  rewrite -> (plus_1_S y).

  (* There must be an easier way to align the rest *)
  rewrite -> (plus_comm (1 + n' + y) (mult n' y)).
  rewrite -> (plus_comm (1 + y + n') (mult n' y)).
  rewrite -> (plus_comm (1 + n') y).
  rewrite -> (plus_comm 1 n').
  rewrite -> (plus_assoc y n' 1).
  rewrite -> (plus_assoc (mult n' y) (y + n') 1).

  rewrite -> (plus_comm (1 + y) n').
  rewrite -> (plus_comm 1 y).
  rewrite -> (plus_assoc n' y 1).

  rewrite -> (plus_assoc (mult n' y) (n' + y) 1).
  rewrite -> (plus_comm n' y).
  reflexivity.
Qed.

Proposition multiplication_is_commutative :
  forall (mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall (x y : nat),
      mul x y = mul y x.
Proof.
  intro mult.
  intro S_mult.
  assert(mul_s := S_mult).
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  intros x y.
  induction x as [ | n' IHn'].

  (* Base case: *)

  rewrite -> (multiplication_bc_left mult mul_s y).
  rewrite -> (multiplication_bc_right mult mul_s y).
  reflexivity.

  (* Induction case: *)
  rewrite -> (H_mult_ic n' y).
  rewrite -> (IHn').
  rewrite -> (multiplication_ic_right mult mul_s y n').
  reflexivity.
Qed.

Check(mult_0_l).

(*
 * show that the specification of multiplication is unique
 *)

Proposition there_is_only_one_multiplication :
  forall mult1 mult2 : nat -> nat -> nat,
    specification_of_multiplication mult1 ->
    specification_of_multiplication mult2 ->
    forall x y : nat,
      mult1 x y = mult2 x y.
Proof.
  intros mult1 mult2 S_mult1 S_mult2 x y.
  induction x as [ | n' IHn'].
  rewrite -> (multiplication_bc_left mult1 S_mult1 y).
  rewrite -> (multiplication_bc_left mult2 S_mult2 y).
  reflexivity.

  rewrite <- (multiplication_ic_left mult1 S_mult1 n' y).
  rewrite <- (multiplication_ic_left mult2 S_mult2 n' y).
  rewrite -> (IHn').
  reflexivity.
Qed.

(*
   * implement multiplication,
     verify that your implementation passes the unit tests, and
     prove that your implementation satisfies the specification
*)

Fixpoint mult_v1 (x y : nat) : nat :=
  match x with
    | 0 => 0
    | S x' => y + (mult_v1 x' y)
  end.

Compute(unit_tests_for_multiplication mult_v1).

Lemma unfold_mult_v1_bc :
  forall (y: nat),
    mult_v1 0 y = 0.
Proof.
  unfold_tactic mult_v1.
Qed.

(* Is this how we would do it otherwise? *)
Lemma unfold_mult_v1_bc2 :
  forall (y: nat),
    mult_v1 0 y = 0.
Proof.
  intro y.
  unfold mult_v1.
  reflexivity.
Qed.

Lemma unfold_mult_v1_ic :
  forall (i' j : nat),
    mult_v1 (S i') j = j + mult_v1 i' j.
Proof.
  unfold_tactic mult_v1.
Qed.

Theorem mult_v1_satisfies_the_specification_of_multiplication :
  specification_of_multiplication mult_v1.
Proof.
  unfold specification_of_multiplication.
  split.

  apply unfold_mult_v1_bc.

  apply unfold_mult_v1_ic.

Qed.

(* Exercise:

   * show that 0 is left-absorbant for multiplication
     (aka mult_0_l in Arith) x

   * show that 0 is right-absorbant for multiplication
     (aka mult_0_r in Arith) x

   * show that 1 is left-neutral for multiplication
     (aka mult_1_l in Arith) x

   * show that 1 is right-neutral for multiplication
     (aka mult_1_r in Arith) x

   * show that multiplication is commutative
     (aka mult_comm in Arith)

   * show that the specification of multiplication is unique

   * implement multiplication,
     verify that your implementation passes the unit tests, and
     prove that your implementation satisfies the specification
*)

(*
    show that multiplication distributes over addition on the left
    (aka mult_plus_distr_l in Arith), and
*)

Check(mult_plus_distr_l).

Proposition multiplication_plus_distribution_left :
  forall(mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall(x y z : nat),
      mul x (y + z) = mul x y + mul x z.
Proof.
  intro mult.
  intro S_mult.
  intros x y z.
  assert(mul_s := S_mult).
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  induction x as [ | n' IHn'].
  rewrite -> (multiplication_bc_left mult mul_s (y + z)).
  rewrite -> (multiplication_bc_left mult mul_s y).
  rewrite -> (multiplication_bc_left mult mul_s z).
  rewrite -> (plus_0_l 0).
  reflexivity.

  rewrite -> (H_mult_ic n' (y + z)).
  rewrite -> (H_mult_ic n' y).
  rewrite -> (H_mult_ic n' z).
  rewrite -> (IHn').
  rewrite -> (plus_assoc (y + mult n' y) z (mult n' z)).
  rewrite -> (plus_assoc (y + z) (mult n' y) (mult n' z)).
  rewrite -> (plus_comm y (mult n' y)).
  Check(plus_assoc).
  rewrite <- (plus_assoc (mult n' y) y z).
  rewrite -> (plus_comm (mult n' y) (y + z)).
  reflexivity.
Qed.

(*
   show that multiplication is associative
   (aka mult_assoc in Arith),
*)

Proposition multiplication_is_associative :
  forall(mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall (x y z : nat),
      mul x (mul y z) = mul (mul x y) z.
Proof.
  intro mult.
  intro S_mult.
  assert(mul_s := S_mult).
  unfold specification_of_multiplication in S_mult.
  destruct S_mult as [H_mult_bc H_mult_ic].
  intros x y z.
  induction x as [ | n' IHn'].

  (* Base case: *)
  rewrite -> (multiplication_bc_left mult mul_s (mult y z)).
  rewrite -> (multiplication_bc_left mult mul_s y).
  rewrite -> (multiplication_bc_left mult mul_s z).
  reflexivity.

  (* Induction case: *)

  rewrite -> (H_mult_ic n' (mult y z)).
  rewrite -> (H_mult_ic n' y).
  rewrite -> (IHn').
  rewrite -> (multiplication_is_commutative mult mul_s (y + mult n' y) z).
  rewrite -> (multiplication_plus_distribution_left mult mul_s z y (mult n' y)).
  rewrite -> (multiplication_is_commutative mult mul_s (mult n' y) z).
  rewrite -> (multiplication_is_commutative mult mul_s y z).
  reflexivity.
Qed.

(*
    show that multiplication distributes over addition on the right
    (aka mult_plus_distr_r in Arith).
*)

Check(mult_plus_distr_r).

Proposition multiplication_plus_distribution_right :
  forall(mul : nat -> nat -> nat),
    specification_of_multiplication mul ->
    forall (x y z : nat),
      mul (x + y) z = mul x z + mul y z.
Proof.
  intro mult.
  intro S_mult.
  assert(mul_s := S_mult).
  intros x y z.
  rewrite -> (multiplication_is_commutative mult mul_s (x + y) z).
  rewrite -> (multiplication_is_commutative mult mul_s x z).
  rewrite -> (multiplication_is_commutative mult mul_s y z).
  apply (multiplication_plus_distribution_left mult mul_s z x y).
Qed.

(* ********** *)

(* Exercise for the over-achievers:

   In no particular order,

   * show that multiplication is associative
     (aka mult_assoc in Arith),

   * show that multiplication distributes over addition on the left
     (aka mult_plus_distr_l in Arith), and

   * show that multiplication distributes over addition on the right
     (aka mult_plus_distr_r in Arith).
*)

(* ********** *)

(* Exercise for the over-achievers with time on their hands:
   repeat the exercises above with our own implementation
   of the addition function.
   (You will first need to compile week_36b_add.v with coqc.)
*)

(*
Require Import week_36b_add.

Definition specification_of_multiplication' (mul : nat -> nat -> nat) :=
  (forall j : nat,
    mul O j = 0)
  /\
  (forall add : nat -> nat -> nat,
     specification_of_addition add ->
     forall i' j : nat,
       mul (S i') j = add j (mul i' j)).
*)

(* ********** *)

(* end of week_36c_mul.v *)


From mathcomp
Require Import ssreflect.

Require Classical_Prop.

Module LogicPrimer.

Import Classical_Prop.
Definition peirce_law := forall P Q: Prop, ((P -> Q) -> P) -> P.

(**
---------------------------------------------------------------------
Exercise [forall-distributivity]
---------------------------------------------------------------------

Formulate and prove the following theorem in Coq, which states the
distributivity of universal quantification with respect to implication:
\[
forall P Q,
  [(forall x, P(x) => Q(x)) => ((forall y, P(y)) => forall z, Q(z))]
\]

Be careful with the scoping of universally-quantified variables
and use parentheses to resolve ambiguities!
*)

Theorem all_imp_ist A (P Q: A -> Prop):
  (forall x: A, P x -> Q x) -> (forall y, P y) -> forall z, Q z.
Proof. by move=> H1 H2 z; apply: H1; apply: H2. Qed.

(**
---------------------------------------------------------------------
Exercise [Or-And distributivity]
---------------------------------------------------------------------
Prove the following theorems.
*)

Theorem or_distributes_over_and P Q R:
  P \/ (Q /\ R) <-> (P \/ Q) /\ (P \/ R).
Proof.
split.
- case; first by split; [left | left].
- by case=>q r; split; [right | right].
intuition.
Qed.

Theorem or_distributes_over_and_2 P Q R :
  (P \/ Q) /\ (P \/ R) -> P \/ (Q /\ R).
Proof.
case; case=>q; first by left.
case=>[p|r]; first by left.
right; split=>//.
Qed.

(**
---------------------------------------------------------------------
Exercise [Home-brewed existential quantification]
---------------------------------------------------------------------

Let us define our own version [my_ex] of the existential quantifier
using the SSReflect notation for constructors: *)

Inductive my_ex A (S: A -> Prop) : Prop := my_ex_intro x of S x.

(** You invited to prove the following goal, establishing the
equivalence of the two propositions. *)

Goal forall A (S: A -> Prop), my_ex A S <-> exists y: A, S y.
Proof.
move=> A S; split.
- by case=> x Hs; exists x.
by case=>y Hs; apply: my_ex_intro Hs.
Qed.

(**
Hint: the propositional equivalence [<->] is just a conjunction of
two implications, so proving it can be reduced to two separate goals
by means of [split] tactics.
*)

(**
---------------------------------------------------------------------
Exercise [Distributivity of existential quantification]
---------------------------------------------------------------------
Prove the following theorem.
*)

Theorem dist_exists_or (X : Type) (P Q : X -> Prop):
  (exists x, P x \/ Q x) <-> (exists x, P x) \/ (exists x, Q x).
Proof.
split; first by case=>x; case=>H; [left | right]; exists x=> //.
by case; case=>x H; exists x; [left | right].
Qed.

(**
---------------------------------------------------------------------
Exercise [Two equals three]
---------------------------------------------------------------------
Prove the following  theorem. Can you explain the proof?
*)

Theorem two_is_three A: (exists x : A, (forall R : A -> Prop, R x)) -> 2 = 3.
Proof.
by case=>x H; apply: H.
Qed.

(**
---------------------------------------------------------------------
Exercise [Dyslexic implication and contraposition]
---------------------------------------------------------------------

The "dyslexic" implication and contrapositions are the following
propositions.
*)

Definition dys_imp (P Q: Prop) := (P -> Q) -> (Q -> P).
Definition dys_contrap (P Q: Prop) := (P -> Q) -> (~P -> ~Q).

(**
These propositions are inhabited, as otherwise one, given a proof of
any of them, would be able to construct a proof of [False]. You are
invited to deomnstrate it by proving the following statements.
*)

Theorem di_false: (forall P Q: Prop, dys_imp P Q) -> False.
Proof. by move/(_ _ True); apply. Qed.

Theorem dc_false: (forall P Q: Prop, dys_contrap P Q) -> False.
Proof. by move=>H; apply: (H False True)=>//. Qed.

(**
---------------------------------------------------------------------
Exercise [Irrefutability of the excluded middle]
---------------------------------------------------------------------

Proof the following theorem that states that the assumption of the
falsehood of the excluded middle leads to inconsistencies, as is
allows one to derive [False].
*)

Theorem excluded_middle_irrefutable: forall (P : Prop), ~~(P \/ ~ P).
Proof.
move=>P H.
apply: (H); right=>p.
by apply: H; left.
Qed.

(**
---------------------------------------------------------------------
Exercise [Equivalence of classical logic axioms]
---------------------------------------------------------------------

Prove that the following five axioms of the classical are equivalent.

*)

Definition peirce := peirce_law.
Definition double_neg := forall P: Prop, ~ ~ P -> P.
Definition excluded_middle := forall P: Prop, P \/ ~P.
Definition de_morgan_not_and_not := forall P Q: Prop, ~ ( ~P /\ ~Q) -> P \/ Q.
Definition implies_to_or := forall P Q: Prop, (P -> Q) -> (~P \/ Q).

Lemma peirce_dn: peirce -> double_neg.
Proof. by move=>H P Hn; apply: (H _ False)=> /Hn. Qed.

Lemma dn_em : double_neg -> excluded_middle.
Proof.
rewrite /double_neg /excluded_middle=> Dn P.
apply: (Dn (P \/ ~ P))=>H1; apply: (H1).
by left; apply: (Dn)=> H2; apply: H1; right.
Qed.

Lemma em_dmnan: excluded_middle -> de_morgan_not_and_not.
Proof.
rewrite /excluded_middle /de_morgan_not_and_not=> H1 P Q H2.
suff: ~P -> Q.
- move=>H3. move: (H1 P); case=>//X; first by left.
  by right; apply: H3.
move=> Pn.
move: (H1 Q); case=>// Qn.
suff: False=>//; apply: H2; split=>//.
Qed.

Lemma dmnan_ito : de_morgan_not_and_not -> implies_to_or.
Proof.
rewrite /de_morgan_not_and_not /implies_to_or=> H1 P Q Hi.
suff: ~P \/ P.
case=>//; first by left.
- by move/ Hi; right.
move: (H1 (~P) P)=> H2; apply: H2; case=> Hp p.
suff: (P -> False) \/ False by case=>//.
by apply: H1; case.
Qed.

Lemma ito_peirce : implies_to_or -> peirce.
Proof.
rewrite /peirce /peirce_law /implies_to_or=> H1 P Q H2.
have X: P -> P by [].
move: (H1 P P) =>/(_ X); case=>{X}// Pn.
by apply: (H2)=>p.
Qed.

(**

Hint: Use [rewrite /d] tactics to unfold the definition of a value [d]
 and replace its name by its body. You can chain several unfoldings by
 writing [rewrite /d1 /d2 ...]  etc.

Hint: To facilitate the forward reasoning by contradiction, you can
 use the SSReflect tactic [suff: P], where [P] is an arbitrary
 proposition. The system will then require you to prove that [P]
 implies the goal _and_ [P] itself.

Hint: Stuck with a tricky proof? Use the Coq [admit] tactic as a
 "stub" for an unfinished proof of a goal, which, nevertheless will be
 considered completed by Coq. You can always get back to an admitted
 proof later.

*)

(**
---------------------------------------------------------------------
Exercise [Inifinitary de Morgan laws]
---------------------------------------------------------------------

Prove the following implication analogous to the Morgan law for
conjunctions  and disjunctions.

*)

Theorem not_forall_exists A (P : A -> Prop):
  (forall x: A, P x) -> ~(exists y: A, ~ P y).
Proof.
by move=>H G; case: G=>y G; apply: G; apply: H.
Qed.

(**

Then, prove that the assumption of the excluded middle axiom allows one to
establish the implication from the negation of (exists) to (forall).

*)

Theorem not_exists_forall :
  excluded_middle -> forall (X: Type) (P : X -> Prop),
    ~ (exists x, ~ P x) -> (forall x, P x).
Proof.
move=> Em X P H x; rewrite /excluded_middle in Em.
move: (Em (P x)); case=>// => H1.
by suff: False =>//; apply:H; exists x.
Qed.

End LogicPrimer.


From mathcomp Require Import ssreflect ssrbool.
From mathcomp Require ssrnat.

Section Orders.
  Context {T: Type} (leT: rel T).

  Class Reflexive := reflexivity: reflexive leT.
  Class Transitive := transitivity: transitive leT.
  Class Antisymmetric := antisymmetry: antisymmetric leT.
  Class Total := totality: total leT.

  Class PreOrder :=
    {
      PreOrder_Reflexive :> Reflexive;
      PreOrder_Transitive :> Transitive;
    }.

  Class PartialOrder :=
    {
      PartialOrder_PreOrder :> PreOrder;
      PartialOrder_Antisymmetric :> Antisymmetric;
    }.

  Class TotalPreOrder :=
    {
      TotalPreOrder_PreOrder :> PreOrder;
      TotalPreOrder_Total :> Total;
    }.
End Orders.

Arguments transitivity {T leT Transitive}.
Arguments antisymmetry {T leT Antisymmetric}.

Module Nat.
  Import ssrnat.

  Instance Leq: TotalPreOrder leq := {}.
  Proof.
    constructor.
    exact: leqnn.
    exact: leq_trans.
    exact: leq_total.
  Defined.
End Nat.