Set Warnings "-notation-overridden,-parsing".
Require Export Tactics.
Require Export Lists.

Check 3 = 3.

Check forall n m : nat, n + m = m + n.

Check 2 = 2.

Check forall n : nat, n = 2.

Check 3 = 4.

Theorem plus_2_2_is_4 :
  2 + 2 = 4.
Proof.
  reflexivity.
Qed.

Definition plus_fact : Prop := 2 + 2 = 4.
Check plus_fact.

Theorem plus_fact_is_true :
  plus_fact.
Proof.
  reflexivity.
Qed.

Definition is_three (n : nat) : Prop :=
  n = 3.

Check is_three.

Definition injective {A B} (f : A -> B) :=
  forall x y : A, f x = f y -> x = y.

Lemma succ_inj : injective S.
Proof.
  intros n m H.
  inversion H.
  reflexivity.
Qed.

Check @eq.

Example and_example : 3 + 4 = 7 /\ 2 * 2 = 4.
Proof.
  split.
  - reflexivity.
  - reflexivity.
Qed.

Lemma and_intro : forall A B : Prop,
  A -> B -> A /\ B.
Proof.
  intros A B HA HB.
  split.
  - apply HA.
  - apply HB.
Qed.

Example and_example' : 3 + 4 = 7 /\ 2 * 2 = 4.
Proof.
  apply and_intro.
  - reflexivity.
  - reflexivity.
Qed.

(* Exercise: 2 stars (and_exercise) *)
Example and_exercise_firsttry :
  forall n m : nat,
  n + m = 0 ->
  n = 0 /\ m = 0.
Proof.
  intros n m H.
  induction n, m.
  - auto.
  - auto.
  - split.
    + simpl in H. rewrite <- plus_n_O in H. apply H.
    + auto.
  - inversion H.
Qed.

Example and_exercise :
  forall n m : nat,
  n + m = 0 ->
  n = 0 /\ m = 0.
Proof.
  intros n m H.
  induction n.
  - auto.
  - inversion H.
Qed.

Lemma and_example2 : forall n m : nat,
  n = 0 /\ m = 0 ->
  n + m = 0.
Proof.
  intros.
  destruct H as [Hn Hm].
  rewrite Hn. apply Hm.
Qed.

Lemma and_example2' : forall n m : nat,
  n = 0 ->
  m = 0 ->
  n + m = 0.
Proof.
  intros.
  rewrite H. apply H0.
Qed.

Lemma and_example3 : forall n m : nat,
  n + m = 0 ->
  n * m = 0.
Proof.
  intros.
  assert (H' : n = 0 /\ m = 0).
  { induction n.
    - auto.
    - inversion H.
  }
  destruct H' as [Hn Hm].
  rewrite Hn. rewrite Hm.
  reflexivity.
Qed.

Lemma proj1 : forall P Q : Prop,
  P /\ Q -> P.
Proof.
  intros P Q [HP HQ].
  apply HP.
Qed.

(* Exercise: 1 star, optional (proj2) *)
Lemma proj2 : forall P Q : Prop,
  P /\ Q -> Q.
Proof.
  intros P Q [HP HQ].
  apply HQ.
Qed.

Theorem and_commut : forall P Q : Prop,
  P /\ Q -> Q /\ P.
Proof.
  intros P Q [HP HQ].
  split.
  - apply HQ.
  - apply HP.
Qed.

(* Exercise: 2 stars (and_assoc) *)
Theorem and_assoc : forall P Q R : Prop,
  P /\ (Q /\ R) -> (P /\ Q) /\ R.
Proof.
  intros P Q R [HP [HQ HR]].
  split.
  - split.
    + apply HP.
    + apply HQ.
  - apply HR.
Qed.

Check and.

Lemma or_example : forall n m : nat,
  n = 0 \/ m = 0 ->
  n * m = 0.
Proof.
  (* This pattern implicitly does case analysis on
    n = 0 \/ m = 0 *)
  intros n m [Hn | Hm].
  - rewrite Hn. reflexivity.
  - rewrite Hm. rewrite <- mult_n_O. reflexivity.
Qed.

Lemma or_intro : forall A B : Prop,
  A -> A \/ B.
Proof.
  intros A B HA.
  left.
  apply HA.
Qed.

Lemma zero_or_succ :
  forall n : nat, n = 0 \/ n = S (pred n).
Proof.
  intros.
  induction n.
  - left. reflexivity.
  - right. reflexivity.
Qed.

(** **** Exercise: 1 star (mult_eq_0)  *)
Lemma mult_eq_0 :
  forall n m, n * m = 0 -> n = 0 \/ m = 0.
Proof.
  intros.
  generalize dependent n.
  induction n.
  - intros Hm. left. reflexivity.
  - intros Hnm. induction m.
    + right. reflexivity.
    + left. inversion Hnm.
Qed.

(** **** Exercise: 1 star (or_commut)  *)
Theorem or_commut : forall P Q : Prop,
  P \/ Q  -> Q \/ P.
Proof.
  intros P Q [HP | HQ].
  - right. apply HP.
  - left. apply HQ.
Qed.

Module MyNot.

Definition not (P:Prop) := P -> False.

Notation "~ x" := (not x) : type_scope.

Check not.

End MyNot.

Theorem ex_falso_quadlibet : forall (P:Prop),
  False -> P.
Proof.
  intros P contra.
  destruct contra.
Qed.

(* Exercise: 2 stars, optional (not_implies_our_not) *)

Fact not_implies_our_not : forall (P:Prop),
  ~ P -> (forall (Q:Prop), P -> Q).
Proof.
  intros.
  destruct H.
  apply H0.
Qed.

Theorem zero_not_one : ~(0 = 1).
Proof.
  intros contra.
  inversion contra.
Qed.

Check (0 <> 1).

Theorem not_False :
  ~ False.
Proof.
  unfold not.
  intros H.
  destruct H.
Qed.

Theorem contradiction_implies_anything: forall P Q : Prop,
  (P /\ ~P) -> Q.
Proof.
  intros P Q [HP HNA].
  unfold not in HNA.
  apply HNA in HP.
  destruct HP.
Qed.

Theorem double_neg : forall P : Prop,
  P -> ~~P.
Proof.
  intros P HP.
  unfold not.
  intros G.
  apply G.
  apply HP.
Qed.

(* Exercise: 2 stars, recommended (contrapositive) *)
Theorem contrapositive : forall (P Q : Prop),
  (P -> Q) -> (~Q -> ~P).
Proof.
  intros P Q HPQ HQ HP.
  unfold not in HQ.
  apply HQ.
  apply HPQ.
  apply HP.
Qed.

(* Exercise: 1 star (not_both_true_and_false) *)
Theorem not_both_true_and_false : forall P : Prop,
  ~ (P /\ ~P).
Proof.
  intros P [HP HNP].
  apply HNP.
  apply HP.
Qed.

Theorem not_true_is_false : forall b : bool,
  b <> true -> b = false.
Proof.
  intros [] H.
  - apply ex_falso_quadlibet. unfold not in H. apply H. reflexivity.
  - reflexivity.
Qed.

Theorem not_true_is_false' : forall b : bool,
  b <> true -> b = false.
Proof.
  intros [] H.
  - unfold not in H.
    exfalso.
    apply H. reflexivity.
  - reflexivity.
Qed.

Lemma True_is_true : True.
Proof. apply I. Qed.

Module MyIff.

Definition iff (P Q : Prop) := (P -> Q) /\ (Q -> P).

Notation "P <-> Q" := (iff P Q)
                        (at level 95, no associativity)
                        : type_scope.

End MyIff.

Theorem iff_sym : forall P Q : Prop,
  (P <-> Q) -> (Q <-> P).
Proof.
  intros P Q [HP HQ].
  unfold iff.
  split.
  - apply HQ.
  - apply HP.
Qed.

Lemma not_true_iff_false : forall b,
  b <> true <-> b = false.
Proof.
  intros.
  unfold iff.
  split.
  - intros H. induction b.
    + exfalso. apply H. reflexivity.
    + reflexivity.
  - intros H. induction b.
    + inversion H.
    + unfold not. intros H'. inversion H'.
Qed.

Lemma or_intro_2 : forall A B : Prop,
  B -> A \/ B.
Proof.
  intros A B HB.
  right.
  apply HB.
Qed.

(* Exercise: 3 stars (or_distributes_over_and) *)

Theorem or_distributes_over_and : forall P Q R : Prop,
  P \/ (Q /\ R) <-> (P \/ Q) /\ (P \/ R).
Proof.
  intros P Q R.
  split.
  - intros [HP | [HQ HR]].
    + split.
      { - left. apply HP. }
      { - left. apply HP. }
    + split.
      { - right. apply HQ. }
      { - right. apply HR. }
  - intros [[HP | HQ] [HP' | HR]].
    + left. apply HP.
    + left. apply HP.
    + left. apply HP'.
    + right. split.
      { - apply HQ. }
      { - apply HR. }
Qed.

Require Import Coq.Setoids.Setoid.
Require Import Coq.Classes.RelationClasses.

Lemma mult_0 : forall n m,
  n * m = 0 <-> n = 0 \/ m = 0.
Proof.
  split.
  - apply mult_eq_0.
  - apply or_example.
Qed.

Lemma or_assoc : forall P Q R : Prop,
  P \/ (Q \/ R) <-> (P \/ Q) \/ R.
Proof.
  intros P Q R.
  split.
  - intros [H | [H | H]].
    + left. left. apply H.
    + left. right. apply H.
    + right. apply H.
  - intros [[H | H] | H].
    + left. apply H.
    + right. left. apply H.
    + right. right. apply H.
Qed.

Lemma mult_0_3 :
  forall n m p, n * m * p = 0 <-> n = 0 \/ m = 0 \/ p = 0.
Proof.
  intros n m p.
  rewrite mult_0. rewrite mult_0. rewrite or_assoc. reflexivity.
Qed.

Lemma apply_iff_example : forall n m : nat,
  n * m = 0 -> n = 0 \/ m = 0.
Proof.
  intros n m.
  apply mult_0.
Qed.

Lemma four_is_even : exists n : nat, 4 = n + n.
Proof.
  exists 2. reflexivity.
Qed.

Theorem exists_example_2 : forall n,
  (exists m, n = 4 + m) ->
  (exists o, n = 2 + o).
Proof.
  intros n [m Hm].
  exists (2 + m).
  apply Hm.
Qed.

(* Exercise: 1 star, recommend (dist_not_exists). *)
Theorem dist_not_exists : forall (X:Type) (P : X -> Prop),
  (forall x, P x) -> ~ (exists x, ~ P x).
Proof.
  unfold not.
  intros.
  destruct H0.
  apply H0.
  apply H.
Qed.

(* Exercise: 2 stars (dist_exists_or) *)
Theorem dist_exists_or : forall (X:Type) (P Q : X -> Prop),
  (exists x, P x \/ Q x) <-> (exists x, P x) \/ (exists x, Q x).
Proof.
  intros.
  split.
  - intros [x [HP | HQ]].
    + left. exists (x). apply HP.
    + right. exists (x). apply HQ.
  - intros [[x HP] | [x HQ]].
    + exists (x). left. apply HP.
    + exists (x). right. apply HQ.
Qed.

Fixpoint In {A : Type} (x : A) (l : list A) : Prop :=
  match l with
  | [] => False
  | x' :: l' => x' = x \/ In x l'
  end.

Example In_example_1 : In 4 [1; 2; 3; 4; 5].
Proof.
  simpl. right. right. right. left. reflexivity.
Qed.

Example In_example_2:
  forall n, In n [2; 4] ->
  exists n', n = 2 * n'.
Proof.
  simpl.
  intros n [H | [H | []]].
  - exists 1. rewrite <- H. reflexivity.
  - exists 2. rewrite <- H. reflexivity.
Qed.

Lemma In_map :
  forall (A B : Type) (f : A -> B) (l : list A) (x : A),
    In x l ->
    In (f x) (map f l).
Proof.
  intros. induction l.
  - apply H.
  - simpl. destruct H as [Hx | ].
    + left. rewrite Hx. reflexivity.
    + right. apply IHl. apply H.
Qed.

(* Exercise: 2 stars (In_map_iff) *)
Lemma In_map_iff :
  forall (A B : Type) (f : A -> B) (l : list A) (y : B),
    In y (map f l) <->
    exists x, f x = y /\ In x l.
Proof.
  intros A B f l y.
  split.
  - intros H. induction l.
      { - inversion H. }
      { - simpl. simpl in H. induction H as [Hx | E].
          + exists (x). split.
            * apply Hx.
            * left. reflexivity.
          + apply IHl in E. destruct E as [x' H1].
            exists (x'). split.
            * apply H1.
            * right. apply H1.
       }
   - intros [x H]. induction l.
    + destruct H. apply H0.
    + simpl. simpl in H. destruct H as [Hfx [Hx0 | Hxl]].
      * left. rewrite <- Hx0 in Hfx. apply Hfx.
      * right. apply IHl. split.
        { - apply Hfx. }
        { - apply Hxl. }
Qed.

Lemma app_r_nil : forall X (l : list X),
  [] ++ l = l.
Proof.
  intros. induction l.
  - reflexivity.
  - simpl. reflexivity.
Qed.

(* Exercise: 2 stars (In_app_iff) *)
Lemma In_app_iff : forall A l l' (a:A),
  In a (l++l') <-> In a l \/ In a l'.
Proof.
  intros A l l' a. split.
  - intros H. induction l.
    + simpl in H. right. apply H.
    + simpl in H. destruct H as [Hx | Ha].
      * simpl. left. left. apply Hx.
      * apply IHl in Ha. destruct Ha.
        { - simpl. left. right. apply H. }
        { - right. apply H. }
  - intros H. induction l.
    + simpl. destruct H.
      * induction H.
      * apply H.
    + destruct H as [H1 | H2].
      * simpl. simpl in H1. destruct H1.
        { - left. apply H. }
        { - right. apply IHl. left. apply H. }
      * simpl. right. apply IHl. right. apply H2.
Qed.

(* Exercise: 3 stars, recommended (All) *)
Fixpoint All {T : Type} (P : T -> Prop) (l : list T) : Prop :=
  match l with
  | [] => True
  | x :: l' => P x /\ All P l'
  end.

Lemma All_In :
  forall T (P : T -> Prop) (l : list T),
    (forall x, In x l -> P x) <-> All P l.
Proof.
  intros T P l.
  split.
  - intros H. induction l.
    + simpl. apply I.
    + simpl. split.
      * apply H. simpl. left. reflexivity.
      * apply IHl. intros. apply H. simpl. right. apply H0.
  - intros. induction l.
    + destruct H0.
    + simpl in H. simpl in H0.
      destruct H. destruct H0.
        * rewrite H0 in H. apply H.
        * apply IHl in H1.
          { - apply H1. }
          { - apply H0. }
Qed.

(* Exercise: 3 stars (combine_odd_even) *)
Definition combine_odd_even (Podd Peven : nat -> Prop) : nat -> Prop
  := fun (n : nat) =>
      match (oddb n) with
      | true => Podd n
      | false => Peven n
      end.

Lemma oddb_S : forall n,
  oddb (S n) = negb (oddb n).
Proof.
  intros. induction n.
  - simpl. reflexivity.
  - rewrite IHn. rewrite negb_involutive. reflexivity.
Qed.

Theorem not_true_is_false'' : forall b : bool,
  b <> true -> b = false.
Proof.
  intros [] H.
  - (* b = true *)
    unfold not in H.
    exfalso.
    apply H. reflexivity.
  - (* b = false *)
    reflexivity.
Qed.

Theorem combine_odd_even_intro :
  forall (Podd Peven : nat -> Prop) (n : nat),
         (oddb n = true -> Podd n) ->
         (oddb n = false -> Peven n) ->
         combine_odd_even Podd Peven n.
Proof.
  intros.
  induction n.
  - unfold combine_odd_even. simpl. apply H0. reflexivity.
  - unfold combine_odd_even. destruct (oddb (S n)).
      * apply H. reflexivity.
      * apply H0. reflexivity.
Qed.

Theorem combine_odd_even_elim_odd :
  forall (Podd Peven : nat -> Prop) (n : nat),
    combine_odd_even Podd Peven n ->
    oddb n = true ->
    Podd n.
Proof.
  intros. unfold combine_odd_even in H. destruct (oddb n).
  - apply H.
  - inversion H0.
Qed.

Theorem combine_odd_even_elim_even :
  forall (Podd Peven : nat -> Prop) (n : nat),
    combine_odd_even Podd Peven n ->
    oddb n = false ->
    Peven n.
Proof.
  intros. unfold combine_odd_even in H. destruct (oddb n).
  - inversion H0.
  - apply H.
Qed.

Check plus_comm.

Lemma plus_comm3 :
  forall x y z, x + (y + z) = (z + y) + x.
Proof.
  intros. rewrite plus_comm.
  assert (H: y + z = z + y).
  { - apply plus_comm. }
  rewrite H. reflexivity.
Qed.

Lemma plus_comm3_take2 :
  forall x y z, x + (y + z) = (z + y) + x.
Proof.
  intros.
  rewrite plus_comm.
  rewrite (plus_comm y z).
  reflexivity.
Qed.

Example lemma_application_ex :
  forall {n : nat} {ns : list nat},
    In n (map (fun m => m * 0) ns) ->
    n = 0.
Proof.
  intros.
  destruct (proj1 _ _ (In_map_iff _ _ _ _ _) H)
          as [m [Hm _]].
  rewrite mult_0_r in Hm. rewrite <- Hm. reflexivity.
Qed.

Example function_equality_ex1 : plus 3 = plus (pred 4).
Proof. reflexivity. Qed.

Example function_equality_ex2 :
  (fun x => plus x 1) = (fun x => plus 1 x).
Proof.
Abort.

Axiom function_extensionality : forall {X Y: Type}
                                       {f g : X -> Y},
  (forall (x:X), f x = g x) -> f = g.

Example function_equality_ex2 :
  (fun x => plus x 1) = (fun x => plus 1 x).
Proof.
  apply function_extensionality. intros x.
  apply plus_comm.
Qed.

Print Assumptions function_equality_ex2.

(* Exercise: 4 stars (tr_rev_correct) *)
Fixpoint rev_append {X} (l1 l2 : list X) : list X :=
  match l1 with
  | [] => l2
  | x :: l1' => rev_append l1' (x :: l2)
  end.

Definition tr_rev {X} (l : list X) : list X :=
  rev_append l [].

Compute app [1;2;3] [4;5;6].

Compute rev_append [1;2;3] [4;5;6].

Compute tr_rev [1;2;3].

Lemma rev_append_aux : forall X (l1 l2 : list X),
  rev_append l1 l2 = rev_append l1 [] ++ l2.
Proof.
  induction l1.
  - reflexivity.
  - intros l2. simpl.
    rewrite (IHl1 ([x])).
    rewrite (IHl1 (x::l2)).
    rewrite <- app_assoc. reflexivity.
Qed.

Lemma tr_rev_correct : forall X, @tr_rev X = @rev X.
Proof.
  intros.
  apply function_extensionality. induction x.
  - reflexivity.
  - unfold tr_rev. simpl. rewrite rev_append_aux. unfold tr_rev in IHx. rewrite IHx. reflexivity.
Qed.

Theorem evenb_double : forall k, evenb (double k) = true.
Proof.
  intros k.
  induction k.
  - reflexivity.
  - simpl. apply IHk.
Qed.

(*Exercise: 3 stars (evenb_double_conv) *)
Theorem evenb_double_conv : forall n,
  exists k, n = if evenb n then double k
                  else S (double k).
Proof.
  intros.
  induction n.
  - exists 0. reflexivity.
  - rewrite evenb_S. destruct IHn.
    destruct (evenb n).
    + simpl. exists x. rewrite <- H. reflexivity.
    + exists (S x). simpl. rewrite <- H. reflexivity.
Qed.

Theorem even_bool_prop : forall n,
  evenb n = true <-> exists k, n = double k.
Proof.
  intros n.
  split.
  - intros H. destruct (evenb_double_conv n) as [k Hk].
    exists k. rewrite H in Hk. apply Hk.
  - intros [k Hk]. rewrite Hk. apply evenb_double.
Qed.

Theorem beq_nat_true_iff : forall n1 n2 : nat,
  beq_nat n1 n2 = true <-> n1 = n2.
Proof.
  intros. split.
  - intros H. apply beq_nat_true. apply H.
  - intros H. rewrite H. symmetry. apply beq_nat_refl.
Qed.

Fail Definition is_even_prime n :=
  if n = 2 then true
  else false.

Example even_1000 : exists k, 1000 = double k.
Proof.
  exists 500. reflexivity. Qed.

Example even_1000' : evenb 1000 = true.
Proof. reflexivity. Qed.

Example even_1000'' : exists k, 1000 = double k.
Proof. apply even_bool_prop. reflexivity. Qed.

(* Exercise: 2 stars (logical_connectives) *)
Lemma andb_true_iff : forall b1 b2 : bool,
  b1 && b2 = true <-> b1 = true /\ b2 = true.
Proof.
  split.
  - intros H. split.
    + destruct b1.
      * reflexivity.
      * destruct H. unfold andb. reflexivity.
    + destruct b2.
      * reflexivity.
      * destruct H. unfold andb. destruct b1.
        { - reflexivity. }
        { - reflexivity. }
  - intros [H1 H2]. rewrite H1. apply H2.
Qed.

Lemma orb_true_iff : forall b1 b2,
  b1 || b2 = true <-> b1 = true \/ b2 = true.
Proof.
  split.
  - intros H. rewrite <- H. unfold orb. destruct b1.
    + left. reflexivity.
    + right. reflexivity.
  - intros [H1 | H2].
    + rewrite H1. reflexivity.
    + rewrite H2. destruct b1.
      * reflexivity.
      * reflexivity.
Qed.

Require Import NArith.

(* Exercise: 1 star (beq_nat_false_iff) *)
Theorem beq_nat_false_iff : forall x y : nat,
  beq_nat x y = false <-> x <> y.
Proof.
  intros x y.
  split.
  - unfold not. intros H G. rewrite <- G in H.
    rewrite <- beq_nat_refl in H. inversion H.
  - unfold not. intros. destruct (beq_nat x y) eqn:G.
    + exfalso. apply beq_nat_true in G. apply H. apply G.
    + reflexivity.
Qed.

(* Exercise: 3 stars (beq_list) *)
Fixpoint beq_list {A : Type} (beq : A -> A -> bool)
                  (l1 l2 : list A) : bool :=
  match l1, l2 with
  | [], [] => true
  | x :: l1', y :: l2' => beq x y && beq_list beq l1' l2'
  | _, _ => false
  end.

Lemma list_body_eq : forall X (x:X) (l1 l2 : list X),
  x :: l1 = x :: l2 ->
  l1 = l2.
Proof.
  intros.
  induction l1, l2.
  - reflexivity.
  - inversion H.
  - inversion H.
  - inversion H. rewrite <- H2. reflexivity.
Qed.

Lemma list_body_eq_inv : forall X (x:X) (l1 l2 : list X),
  l1 = l2 ->
  x :: l1 = x :: l2.
Proof.
  intros. rewrite H. reflexivity.
Qed.

Lemma beq_list_true_iff :
  forall A (beq : A -> A -> bool),
    (forall a1 a2, beq a1 a2 = true <-> a1 = a2) ->
    forall l1 l2, beq_list beq l1 l2 = true <-> l1 = l2.
Proof.
  intros A beq H.
  induction l1 as [| x l1'], l2 as [| y l2'].
  - simpl. split.
    + reflexivity.
    + reflexivity.
  - simpl. split.
    + intros G. inversion G.
    + intros G. inversion G.
  - simpl. split.
    + intros G. inversion G.
    + intros G. inversion G.
  - simpl. split.
    + intros G. rewrite andb_true_iff in G.
      destruct G. apply IHl1' in H1. apply H in H0.
      rewrite H0. rewrite H1. reflexivity.
    + intros G. rewrite andb_true_iff. split.
      * inversion G. apply H. reflexivity.
      * inversion G. apply IHl1' in H2.
        inversion G. rewrite H4 in H2. apply H2.
Qed.

(* Exercise: 2 stars, recommended (All_forallb) *)
Fixpoint forallb {X : Type} (test : X -> bool) (l : list X) : bool :=
  match l with
  | [] => true
  | x :: l' => andb (test x) (forallb test l')
  end.

Theorem forallb_true_iff : forall X test (l : list X),
  forallb test l = true <-> All (fun x => test x = true) l.
Proof.
  intros. split.
  - intros H. induction l.
    + simpl. apply I.
    + simpl. simpl in H. rewrite andb_true_iff in H. destruct H.  split.
      * apply H.
      * apply IHl. apply H0.
  - intros H. induction l.
    + reflexivity.
    + simpl. rewrite andb_true_iff. simpl in H. destruct H. split.
      * apply H.
      * apply IHl. apply H0.
Qed.

(* Classical vs. Constructive Logic *)

Definition excluded_middle := forall P : Prop,
  P \/ ~P.

Theorem restricted_excluded_middle : forall P b,
  (P <-> b = true) -> P \/ ~P.
Proof.
  intros. destruct b.
  - left. rewrite H. reflexivity.
  - right. rewrite H. intros contra. inversion contra.
Qed.

Theorem restricted_excluded_middle_eq : forall (n m : nat),
  n = m \/ n <> m.
Proof.
  intros n m.
  apply (restricted_excluded_middle (n = m) (beq_nat n m)).
  symmetry. apply beq_nat_true_iff.
Qed.

(* Exercise: 3 stars (excluded_middle_irrefutable) *)
Theorem excluded_middle_irrefutable: forall (P:Prop),
  ~ ~ (P \/ ~P).
Proof.
  intros. apply double_neg.
Abort.

(* Exercise:  3 stars, advanced (not_exists_dist) *)
Theorem not_exists_dist :
  excluded_middle ->
  forall (X:Type) (P : X -> Prop),
    ~ (exists x, ~ P x) -> (forall x, P x).
Proof.
  intros.


Require Import List.
Import ListNotations.
Set Implicit Arguments.
Require Import Arith.
Require Import Coq.Sorting.Sorted.
Require Import Coq.Sorting.Permutation.
Require Import Coq.Program.Combinators.
Require Import quicksort.

Inductive bt :=
| Leaf : bt
| Node : nat -> bt -> bt -> bt.

Inductive in_bt : nat -> bt -> Prop :=
| Curnode : forall n1 n2 tl tr, n1 = n2 -> in_bt n1 (Node n2 tl tr)
| Lefttree : forall n1 n2 tl tr, in_bt n1 tl -> in_bt n1 (Node n2 tl tr)
| Righttree : forall n1 n2 tl tr, in_bt n1 tr -> in_bt n1 (Node n2 tl tr).

Inductive bst : bt -> Prop :=
| BSTLeaf : bst Leaf
| BSTNode : forall n1 tl tr, (forall n2, in_bt n2 tl -> n2 < n1)
                        -> (forall n2, in_bt n2 tr -> n2 >= n1)
                        -> bst tl
                        -> bst tr
                        -> bst (Node n1 tl tr).

Fixpoint insert n1 t :=
  match t with
  | Leaf => Node n1 Leaf Leaf
  | Node n2 tl tr => if n1 <? n2
                    then Node n2 (insert n1 tl) tr
                    else Node n2 tl (insert n1 tr)
  end.

Lemma insert_in : forall t n, in_bt n (insert n t).
Proof.
  intros. induction t. compute. constructor. reflexivity.
  simpl. destruct (n <? n0); [ apply Lefttree | apply Righttree ]; assumption.
Qed.

Lemma insert_still_in : forall t n n1, in_bt n1 t -> in_bt n1 (insert n t).
Proof.
  intros. induction t. inversion H.
  simpl. destruct (n <? n0); inversion H.
  constructor. auto.
  apply Lefttree. apply IHt1. assumption.
  apply Righttree. assumption.

  constructor. auto.
  apply Lefttree. assumption.
  apply Righttree. apply IHt2. assumption.
Qed.

Lemma insert_inversion : forall t n n2, in_bt n2 (insert n t) -> in_bt n2 t \/  n2 = n.
Proof.
  intros. induction t.
  simpl in H. inversion H. right. assumption.
  inversion H2. inversion H2.

  simpl in *. destruct (n <? n0) eqn:res; inversion H.
  subst. left. constructor. reflexivity.
  assert (in_bt n2 t1 \/ n2 = n). apply IHt1. assumption.
  destruct H5. left. apply Lefttree. assumption.
  subst. right. reflexivity.
  subst. left. apply Righttree. assumption.

  subst. left. constructor. reflexivity.
  left. apply Lefttree. assumption.
  assert (in_bt n2 t2 \/ n2 = n). apply IHt2. assumption.
  destruct H5. subst. left. apply Righttree. assumption.
  right. assumption.
Qed.

Theorem insert_bst : forall t n, bst t -> bst (insert n t).
Proof.
  intros. induction H.
  compute. constructor; intros; try inversion H; try constructor.
  simpl. destruct (n <? n1) eqn:res.
  constructor. intros. apply insert_inversion in H3. destruct H3.
  apply H. assumption. rewrite <- H3 in res. apply Nat.ltb_lt in res.
  assumption. intros. apply H0. assumption. assumption. assumption.

  constructor. assumption. intros. apply insert_inversion in H3.
  destruct H3. apply H0. assumption. rewrite H3. apply Nat.ltb_nlt in res.
  apply not_gt in res. assumption. assumption. assumption.
Qed.

Definition insert_list l := List.fold_right insert Leaf l.

Lemma insert_list_bst : forall l, bst (insert_list l).
Proof.
  intros. induction l. compute. constructor.
  unfold insert_list in *. simpl. apply insert_bst. assumption.
Qed.

Fixpoint marshall t :=
  match t with
  | Leaf => []
  | Node a tl tr =>  (marshall tl) ++ (a :: (marshall tr))
  end.

Theorem in_marshall : forall x t, in_bt x t <-> In x (marshall t).
Proof.
  intros. split; intros.
  induction t. inversion H.
  simpl. apply in_app_iff. inversion H.
  subst. right. constructor. reflexivity.
  subst. left. apply IHt1. assumption.
  subst. right. apply in_cons. apply IHt2. assumption.

  induction t. inversion H. simpl in H. apply in_app_iff in H.
  destruct H. apply Lefttree. apply IHt1. assumption.
  simpl in H. destruct H. subst. constructor. reflexivity.
  apply Righttree. apply IHt2. assumption.
Qed.

Lemma lt_gt : forall n1 n2, n1 <= n2 <-> n2 >= n1.
Proof.
  intros. split;
  auto with arith.
Qed.

Theorem marshall_sorted : forall t, bst t -> LocallySorted le (marshall t).
Proof.
  intros. induction t. compute. constructor.
  simpl. inversion H. subst.
  assert (LocallySorted le (marshall t1)).
  apply IHt1. assumption.
  assert (LocallySorted le (marshall t2)).
  apply IHt2. assumption.
  apply app_lt_sorted. assumption. assumption.
  intros. assert (a < n). apply H3. apply in_marshall.
  assumption. auto with arith.
  intros. apply lt_gt. apply H4. apply in_marshall. assumption.
Qed.

Definition treesort l :=
  marshall (insert_list l).

Theorem treesort_sorted : forall l, LocallySorted le (treesort l).
Proof.
  intros. unfold treesort. apply marshall_sorted.
  apply insert_list_bst.
Qed.

Fixpoint tree_size t :=
  match t with
  | Leaf => 0
  | Node n tl tr => 1 + (tree_size tl) + (tree_size tr)
  end.

Lemma le_plus_l_elim : forall n m p, n + m <= p -> n <= p.
Proof.
  intros. induction m. rewrite plus_comm in H. simpl in H.
  assumption.
  apply IHm. rewrite plus_comm in H. inversion H.
  rewrite plus_comm. simpl. auto. constructor.
  rewrite plus_comm. apply le_Sn_le. simpl in H0.
  assumption.
Qed.

Lemma permutation_append : forall {A : Type} (l1 l2 l3 l4  : list A) a,
    Permutation l1 l2 -> Permutation l3 l4 ->
    Permutation (a :: (l1 ++ l3)) (l2 ++ (a :: l4)).
Proof.
  intros. apply Permutation_cons_app. apply Permutation_app.
  assumption. assumption.
Qed.

Lemma insert_permutation : forall a t,
    Permutation (a :: (marshall t))
                (marshall (insert a t)).
Proof.
  intros. remember (tree_size t) as n.
  assert ((tree_size t) <= n). rewrite Heqn. constructor.
  clear Heqn. generalize dependent t. induction n; intros.
  destruct t. auto.
  inversion H.

  destruct t. auto. simpl. destruct (a <? n0). simpl.
  assert (Permutation (a :: (marshall t1)) (marshall (insert a t1))).
  apply IHn. simpl in H. apply le_S_n in H. apply le_plus_l_elim in H.
  assumption. remember (n0 :: (marshall t2)) as l2.
  apply Permutation_app_tail with (tl := l2) in H0.
  simpl in H0. assumption.

  simpl.
  assert (Permutation (a :: marshall t2) (marshall (insert a t2))).
  apply IHn. simpl in H. apply le_S_n in H.
  rewrite plus_comm in H. apply le_plus_l_elim in H. assumption.
  remember (n0 :: marshall (insert a t2)) as x.
  remember (marshall t1) as y.
  assert
    (Permutation (a :: y ++ n0 :: (marshall t2)) (y ++ a :: n0 :: (marshall t2))).
  apply Permutation_cons_app. auto.
  apply Permutation_trans with (l' := (y ++ a :: n0 :: marshall t2)).
  assumption. rewrite Heqx. rewrite Heqy. apply Permutation_app.
  auto. assert (Permutation (a :: n0 :: marshall t2) (n0 :: a :: marshall t2)).
  apply perm_swap. apply Permutation_trans with (l' := (n0 :: a :: marshall t2)).
  assumption. constructor. assumption.
Qed.

Theorem treesort_permutation : forall l, Permutation l (treesort l).
Proof.
  intros. induction l. auto.
  unfold treesort in *. simpl.
  assert (Permutation (a :: marshall (insert_list l))
                      (marshall (insert a (insert_list l)))).
  apply insert_permutation.
  apply Permutation_trans with (l' := (a :: marshall (insert_list l))).
  constructor. assumption. assumption.
Qed.


From Hammer Require Import Hammer.

Require Import Rbase.
Require Import Rfunctions.
Local Open Scope R_scope.

Inductive Rlist : Type :=
| nil : Rlist
| cons : R -> Rlist -> Rlist.

Fixpoint In (x:R) (l:Rlist) : Prop :=
match l with
| nil => False
| cons a l' => x = a \/ In x l'
end.

Fixpoint Rlength (l:Rlist) : nat :=
match l with
| nil => 0%nat
| cons a l' => S (Rlength l')
end.

Fixpoint MaxRlist (l:Rlist) : R :=
match l with
| nil => 0
| cons a l1 =>
match l1 with
| nil => a
| cons a' l2 => Rmax a (MaxRlist l1)
end
end.

Fixpoint MinRlist (l:Rlist) : R :=
match l with
| nil => 1
| cons a l1 =>
match l1 with
| nil => a
| cons a' l2 => Rmin a (MinRlist l1)
end
end.

Lemma MaxRlist_P1 : forall (l:Rlist) (x:R), In x l -> x <= MaxRlist l.
Proof. hammer_hook "RList" "RList.MaxRlist_P1".
intros; induction  l as [| r l Hrecl].
simpl in H; elim H.
induction  l as [| r0 l Hrecl0].
simpl in H; elim H; intro.
simpl; right; assumption.
elim H0.
replace (MaxRlist (cons r (cons r0 l))) with (Rmax r (MaxRlist (cons r0 l))).
simpl in H; decompose [or] H.
rewrite H0; apply RmaxLess1.
unfold Rmax; case (Rle_dec r (MaxRlist (cons r0 l))); intro.
apply Hrecl; simpl; tauto.
apply Rle_trans with (MaxRlist (cons r0 l));
[ apply Hrecl; simpl; tauto | left; auto with real ].
unfold Rmax; case (Rle_dec r (MaxRlist (cons r0 l))); intro.
apply Hrecl; simpl; tauto.
apply Rle_trans with (MaxRlist (cons r0 l));
[ apply Hrecl; simpl; tauto | left; auto with real ].
reflexivity.
Qed.

Fixpoint AbsList (l:Rlist) (x:R) : Rlist :=
match l with
| nil => nil
| cons a l' => cons (Rabs (a - x) / 2) (AbsList l' x)
end.

Lemma MinRlist_P1 : forall (l:Rlist) (x:R), In x l -> MinRlist l <= x.
Proof. hammer_hook "RList" "RList.MinRlist_P1".
intros; induction  l as [| r l Hrecl].
simpl in H; elim H.
induction  l as [| r0 l Hrecl0].
simpl in H; elim H; intro.
simpl; right; symmetry ; assumption.
elim H0.
replace (MinRlist (cons r (cons r0 l))) with (Rmin r (MinRlist (cons r0 l))).
simpl in H; decompose [or] H.
rewrite H0; apply Rmin_l.
unfold Rmin; case (Rle_dec r (MinRlist (cons r0 l))); intro.
apply Rle_trans with (MinRlist (cons r0 l)).
assumption.
apply Hrecl; simpl; tauto.
apply Hrecl; simpl; tauto.
apply Rle_trans with (MinRlist (cons r0 l)).
apply Rmin_r.
apply Hrecl; simpl; tauto.
reflexivity.
Qed.

Lemma AbsList_P1 :
forall (l:Rlist) (x y:R), In y l -> In (Rabs (y - x) / 2) (AbsList l x).
Proof. hammer_hook "RList" "RList.AbsList_P1".
intros; induction  l as [| r l Hrecl].
elim H.
simpl; simpl in H; elim H; intro.
left; rewrite H0; reflexivity.
right; apply Hrecl; assumption.
Qed.

Lemma MinRlist_P2 :
forall l:Rlist, (forall y:R, In y l -> 0 < y) -> 0 < MinRlist l.
Proof. hammer_hook "RList" "RList.MinRlist_P2".
intros; induction  l as [| r l Hrecl].
apply Rlt_0_1.
induction  l as [| r0 l Hrecl0].
simpl; apply H; simpl; tauto.
replace (MinRlist (cons r (cons r0 l))) with (Rmin r (MinRlist (cons r0 l))).
unfold Rmin; case (Rle_dec r (MinRlist (cons r0 l))); intro.
apply H; simpl; tauto.
apply Hrecl; intros; apply H; simpl; simpl in H0; tauto.
reflexivity.
Qed.

Lemma AbsList_P2 :
forall (l:Rlist) (x y:R),
In y (AbsList l x) ->  exists z : R, In z l /\ y = Rabs (z - x) / 2.
Proof. hammer_hook "RList" "RList.AbsList_P2".
intros; induction  l as [| r l Hrecl].
elim H.
elim H; intro.
exists r; split.
simpl; tauto.
assumption.
assert (H1 := Hrecl H0); elim H1; intros; elim H2; clear H2; intros;
exists x0; simpl; simpl in H2; tauto.
Qed.

Lemma MaxRlist_P2 :
forall l:Rlist, (exists y : R, In y l) -> In (MaxRlist l) l.
Proof. hammer_hook "RList" "RList.MaxRlist_P2".
intros; induction  l as [| r l Hrecl].
simpl in H; elim H; trivial.
induction  l as [| r0 l Hrecl0].
simpl; left; reflexivity.
change (In (Rmax r (MaxRlist (cons r0 l))) (cons r (cons r0 l)));
unfold Rmax; case (Rle_dec r (MaxRlist (cons r0 l)));
intro.
right; apply Hrecl; exists r0; left; reflexivity.
left; reflexivity.
Qed.

Fixpoint pos_Rl (l:Rlist) (i:nat) : R :=
match l with
| nil => 0
| cons a l' => match i with
| O => a
| S i' => pos_Rl l' i'
end
end.

Lemma pos_Rl_P1 :
forall (l:Rlist) (a:R),
(0 < Rlength l)%nat ->
pos_Rl (cons a l) (Rlength l) = pos_Rl l (pred (Rlength l)).
Proof. hammer_hook "RList" "RList.pos_Rl_P1".
intros; induction  l as [| r l Hrecl];
[ elim (lt_n_O _ H)
| simpl; case (Rlength l); [ reflexivity | intro; reflexivity ] ].
Qed.

Lemma pos_Rl_P2 :
forall (l:Rlist) (x:R),
In x l <-> (exists i : nat, (i < Rlength l)%nat /\ x = pos_Rl l i).
Proof. hammer_hook "RList" "RList.pos_Rl_P2".
intros; induction  l as [| r l Hrecl].
split; intro;
[ elim H | elim H; intros; elim H0; intros; elim (lt_n_O _ H1) ].
split; intro.
elim H; intro.
exists 0%nat; split;
[ simpl; apply lt_O_Sn | simpl; apply H0 ].
elim Hrecl; intros; assert (H3 := H1 H0); elim H3; intros; elim H4; intros;
exists (S x0); split;
[ simpl; apply lt_n_S; assumption | simpl; assumption ].
elim H; intros; elim H0; intros; destruct (zerop x0) as [->|].
simpl in H2; left; assumption.
right; elim Hrecl; intros H4 H5; apply H5; assert (H6 : S (pred x0) = x0).
symmetry ; apply S_pred with 0%nat; assumption.
exists (pred x0); split;
[ simpl in H1; apply lt_S_n; rewrite H6; assumption
| rewrite <- H6 in H2; simpl in H2; assumption ].
Qed.

Lemma Rlist_P1 :
forall (l:Rlist) (P:R -> R -> Prop),
(forall x:R, In x l ->  exists y : R, P x y) ->
exists l' : Rlist,
Rlength l = Rlength l' /\
(forall i:nat, (i < Rlength l)%nat -> P (pos_Rl l i) (pos_Rl l' i)).
Proof. hammer_hook "RList" "RList.Rlist_P1".
intros; induction  l as [| r l Hrecl].
exists nil; intros; split;
[ reflexivity | intros; simpl in H0; elim (lt_n_O _ H0) ].
assert (H0 : In r (cons r l)).
simpl; left; reflexivity.
assert (H1 := H _ H0);
assert (H2 : forall x:R, In x l ->  exists y : R, P x y).
intros; apply H; simpl; right; assumption.
assert (H3 := Hrecl H2); elim H1; intros; elim H3; intros; exists (cons x x0);
intros; elim H5; clear H5; intros; split.
simpl; rewrite H5; reflexivity.
intros; destruct (zerop i) as [->|].
simpl; assumption.
assert (H9 : i = S (pred i)).
apply S_pred with 0%nat; assumption.
rewrite H9; simpl; apply H6; simpl in H7; apply lt_S_n; rewrite <- H9;
assumption.
Qed.

Definition ordered_Rlist (l:Rlist) : Prop :=
forall i:nat, (i < pred (Rlength l))%nat -> pos_Rl l i <= pos_Rl l (S i).

Fixpoint insert (l:Rlist) (x:R) : Rlist :=
match l with
| nil => cons x nil
| cons a l' =>
match Rle_dec a x with
| left _ => cons a (insert l' x)
| right _ => cons x l
end
end.

Fixpoint cons_Rlist (l k:Rlist) : Rlist :=
match l with
| nil => k
| cons a l' => cons a (cons_Rlist l' k)
end.

Fixpoint cons_ORlist (k l:Rlist) : Rlist :=
match k with
| nil => l
| cons a k' => cons_ORlist k' (insert l a)
end.

Fixpoint app_Rlist (l:Rlist) (f:R -> R) : Rlist :=
match l with
| nil => nil
| cons a l' => cons (f a) (app_Rlist l' f)
end.

Fixpoint mid_Rlist (l:Rlist) (x:R) : Rlist :=
match l with
| nil => nil
| cons a l' => cons ((x + a) / 2) (mid_Rlist l' a)
end.

Definition Rtail (l:Rlist) : Rlist :=
match l with
| nil => nil
| cons a l' => l'
end.

Definition FF (l:Rlist) (f:R -> R) : Rlist :=
match l with
| nil => nil
| cons a l' => app_Rlist (mid_Rlist l' a) f
end.

Lemma RList_P0 :
forall (l:Rlist) (a:R),
pos_Rl (insert l a) 0 = a \/ pos_Rl (insert l a) 0 = pos_Rl l 0.
Proof. hammer_hook "RList" "RList.RList_P0".
intros; induction  l as [| r l Hrecl];
[ left; reflexivity
| simpl; case (Rle_dec r a); intro;
[ right; reflexivity | left; reflexivity ] ].
Qed.

Lemma RList_P1 :
forall (l:Rlist) (a:R), ordered_Rlist l -> ordered_Rlist (insert l a).
Proof. hammer_hook "RList" "RList.RList_P1".
intros; induction  l as [| r l Hrecl].
simpl; unfold ordered_Rlist; intros; simpl in H0;
elim (lt_n_O _ H0).
simpl; case (Rle_dec r a); intro.
assert (H1 : ordered_Rlist l).
unfold ordered_Rlist; unfold ordered_Rlist in H; intros;
assert (H1 : (S i < pred (Rlength (cons r l)))%nat);
[ simpl; replace (Rlength l) with (S (pred (Rlength l)));
[ apply lt_n_S; assumption
| symmetry ; apply S_pred with 0%nat; apply neq_O_lt; red;
intro; rewrite <- H1 in H0; simpl in H0; elim (lt_n_O _ H0) ]
| apply (H _ H1) ].
assert (H2 := Hrecl H1); unfold ordered_Rlist; intros;
induction  i as [| i Hreci].
simpl; assert (H3 := RList_P0 l a); elim H3; intro.
rewrite H4; assumption.
induction  l as [| r1 l Hrecl0];
[ simpl; assumption
| rewrite H4; apply (H 0%nat); simpl; apply lt_O_Sn ].
simpl; apply H2; simpl in H0; apply lt_S_n;
replace (S (pred (Rlength (insert l a)))) with (Rlength (insert l a));
[ assumption
| apply S_pred with 0%nat; apply neq_O_lt; red; intro;
rewrite <- H3 in H0; elim (lt_n_O _ H0) ].
unfold ordered_Rlist; intros; induction  i as [| i Hreci];
[ simpl; auto with real
| change (pos_Rl (cons r l) i <= pos_Rl (cons r l) (S i)); apply H;
simpl in H0; simpl; apply (lt_S_n _ _ H0) ].
Qed.

Lemma RList_P2 :
forall l1 l2:Rlist, ordered_Rlist l2 -> ordered_Rlist (cons_ORlist l1 l2).
Proof. hammer_hook "RList" "RList.RList_P2".
simple induction l1;
[ intros; simpl; apply H
| intros; simpl; apply H; apply RList_P1; assumption ].
Qed.

Lemma RList_P3 :
forall (l:Rlist) (x:R),
In x l <-> (exists i : nat, x = pos_Rl l i /\ (i < Rlength l)%nat).
Proof. hammer_hook "RList" "RList.RList_P3".
intros; split; intro;
[ induction  l as [| r l Hrecl] | induction  l as [| r l Hrecl] ].
elim H.
elim H; intro;
[ exists 0%nat; split; [ apply H0 | simpl; apply lt_O_Sn ]
| elim (Hrecl H0); intros; elim H1; clear H1; intros; exists (S x0); split;
[ apply H1 | simpl; apply lt_n_S; assumption ] ].
elim H; intros; elim H0; intros; elim (lt_n_O _ H2).
simpl; elim H; intros; elim H0; clear H0; intros;
induction  x0 as [| x0 Hrecx0];
[ left; apply H0
| right; apply Hrecl; exists x0; split;
[ apply H0 | simpl in H1; apply lt_S_n; assumption ] ].
Qed.

Lemma RList_P4 :
forall (l1:Rlist) (a:R), ordered_Rlist (cons a l1) -> ordered_Rlist l1.
Proof. hammer_hook "RList" "RList.RList_P4".
intros; unfold ordered_Rlist; intros; apply (H (S i)); simpl;
replace (Rlength l1) with (S (pred (Rlength l1)));
[ apply lt_n_S; assumption
| symmetry ; apply S_pred with 0%nat; apply neq_O_lt; red;
intro; rewrite <- H1 in H0; elim (lt_n_O _ H0) ].
Qed.

Lemma RList_P5 :
forall (l:Rlist) (x:R), ordered_Rlist l -> In x l -> pos_Rl l 0 <= x.
Proof. hammer_hook "RList" "RList.RList_P5".
intros; induction  l as [| r l Hrecl];
[ elim H0
| simpl; elim H0; intro;
[ rewrite H1; right; reflexivity
| apply Rle_trans with (pos_Rl l 0);
[ apply (H 0%nat); simpl; induction  l as [| r0 l Hrecl0];
[ elim H1 | simpl; apply lt_O_Sn ]
| apply Hrecl; [ eapply RList_P4; apply H | assumption ] ] ] ].
Qed.

Lemma RList_P6 :
forall l:Rlist,
ordered_Rlist l <->
(forall i j:nat,
(i <= j)%nat -> (j < Rlength l)%nat -> pos_Rl l i <= pos_Rl l j).
Proof. hammer_hook "RList" "RList.RList_P6".
simple induction l; split; intro.
intros; right; reflexivity.
unfold ordered_Rlist; intros; simpl in H0; elim (lt_n_O _ H0).
intros; induction  i as [| i Hreci];
[ induction  j as [| j Hrecj];
[ right; reflexivity
| simpl; apply Rle_trans with (pos_Rl r0 0);
[ apply (H0 0%nat); simpl; simpl in H2; apply neq_O_lt;
red; intro; rewrite <- H3 in H2;
assert (H4 := lt_S_n _ _ H2); elim (lt_n_O _ H4)
| elim H; intros; apply H3;
[ apply RList_P4 with r; assumption
| apply le_O_n
| simpl in H2; apply lt_S_n; assumption ] ] ]
| induction  j as [| j Hrecj];
[ elim (le_Sn_O _ H1)
| simpl; elim H; intros; apply H3;
[ apply RList_P4 with r; assumption
| apply le_S_n; assumption
| simpl in H2; apply lt_S_n; assumption ] ] ].
unfold ordered_Rlist; intros; apply H0;
[ apply le_n_Sn | simpl; simpl in H1; apply lt_n_S; assumption ].
Qed.

Lemma RList_P7 :
forall (l:Rlist) (x:R),
ordered_Rlist l -> In x l -> x <= pos_Rl l (pred (Rlength l)).
Proof. hammer_hook "RList" "RList.RList_P7".
intros; assert (H1 := RList_P6 l); elim H1; intros H2 _; assert (H3 := H2 H);
clear H1 H2; assert (H1 := RList_P3 l x); elim H1;
clear H1; intros; assert (H4 := H1 H0); elim H4; clear H4;
intros; elim H4; clear H4; intros; rewrite H4;
assert (H6 : Rlength l = S (pred (Rlength l))).
apply S_pred with 0%nat; apply neq_O_lt; red; intro;
rewrite <- H6 in H5; elim (lt_n_O _ H5).
apply H3;
[ rewrite H6 in H5; apply lt_n_Sm_le; assumption
| apply lt_pred_n_n; apply neq_O_lt; red; intro; rewrite <- H7 in H5;
elim (lt_n_O _ H5) ].
Qed.

Lemma RList_P8 :
forall (l:Rlist) (a x:R), In x (insert l a) <-> x = a \/ In x l.
Proof. hammer_hook "RList" "RList.RList_P8".
simple induction l.
intros; split; intro; simpl in H; apply H.
intros; split; intro;
[ simpl in H0; generalize H0; case (Rle_dec r a); intros;
[ simpl in H1; elim H1; intro;
[ right; left; assumption
| elim (H a x); intros; elim (H3 H2); intro;
[ left; assumption | right; right; assumption ] ]
| simpl in H1; decompose [or] H1;
[ left; assumption
| right; left; assumption
| right; right; assumption ] ]
| simpl; case (Rle_dec r a); intro;
[ simpl in H0; decompose [or] H0;
[ right; elim (H a x); intros; apply H3; left
| left
| right; elim (H a x); intros; apply H3; right ]
| simpl in H0; decompose [or] H0; [ left | right; left | right; right ] ];
assumption ].
Qed.

Lemma RList_P9 :
forall (l1 l2:Rlist) (x:R), In x (cons_ORlist l1 l2) <-> In x l1 \/ In x l2.
Proof. hammer_hook "RList" "RList.RList_P9".
simple induction l1.
intros; split; intro;
[ simpl in H; right; assumption
| simpl; elim H; intro; [ elim H0 | assumption ] ].
intros; split.
simpl; intros; elim (H (insert l2 r) x); intros; assert (H3 := H1 H0);
elim H3; intro;
[ left; right; assumption
| elim (RList_P8 l2 r x); intros H5 _; assert (H6 := H5 H4); elim H6; intro;
[ left; left; assumption | right; assumption ] ].
intro; simpl; elim (H (insert l2 r) x); intros _ H1; apply H1;
elim H0; intro;
[ elim H2; intro;
[ right; elim (RList_P8 l2 r x); intros _ H4; apply H4; left; assumption
| left; assumption ]
| right; elim (RList_P8 l2 r x); intros _ H3; apply H3; right; assumption ].
Qed.

Lemma RList_P10 :
forall (l:Rlist) (a:R), Rlength (insert l a) = S (Rlength l).
Proof. hammer_hook "RList" "RList.RList_P10".
intros; induction  l as [| r l Hrecl];
[ reflexivity
| simpl; case (Rle_dec r a); intro;
[ simpl; rewrite Hrecl; reflexivity | reflexivity ] ].
Qed.

Lemma RList_P11 :
forall l1 l2:Rlist,
Rlength (cons_ORlist l1 l2) = (Rlength l1 + Rlength l2)%nat.
Proof. hammer_hook "RList" "RList.RList_P11".
simple induction l1;
[ intro; reflexivity
| intros; simpl; rewrite (H (insert l2 r)); rewrite RList_P10;
apply INR_eq; rewrite S_INR; do 2 rewrite plus_INR;
rewrite S_INR; ring ].
Qed.

Lemma RList_P12 :
forall (l:Rlist) (i:nat) (f:R -> R),
(i < Rlength l)%nat -> pos_Rl (app_Rlist l f) i = f (pos_Rl l i).
Proof. hammer_hook "RList" "RList.RList_P12".
simple induction l;
[ intros; elim (lt_n_O _ H)
| intros; induction  i as [| i Hreci];
[ reflexivity | simpl; apply H; apply lt_S_n; apply H0 ] ].
Qed.

Lemma RList_P13 :
forall (l:Rlist) (i:nat) (a:R),
(i < pred (Rlength l))%nat ->
pos_Rl (mid_Rlist l a) (S i) = (pos_Rl l i + pos_Rl l (S i)) / 2.
Proof. hammer_hook "RList" "RList.RList_P13".
simple induction l.
intros; simpl in H; elim (lt_n_O _ H).
simple induction r0.
intros; simpl in H0; elim (lt_n_O _ H0).
intros; simpl in H1; induction  i as [| i Hreci].
reflexivity.
change
(pos_Rl (mid_Rlist (cons r1 r2) r) (S i) =
(pos_Rl (cons r1 r2) i + pos_Rl (cons r1 r2) (S i)) / 2)
; apply H0; simpl; apply lt_S_n; assumption.
Qed.

Lemma RList_P14 : forall (l:Rlist) (a:R), Rlength (mid_Rlist l a) = Rlength l.
Proof. hammer_hook "RList" "RList.RList_P14".
simple induction l; intros;
[ reflexivity | simpl; rewrite (H r); reflexivity ].
Qed.

Lemma RList_P15 :
forall l1 l2:Rlist,
ordered_Rlist l1 ->
ordered_Rlist l2 ->
pos_Rl l1 0 = pos_Rl l2 0 -> pos_Rl (cons_ORlist l1 l2) 0 = pos_Rl l1 0.
Proof. hammer_hook "RList" "RList.RList_P15".
intros; apply Rle_antisym.
induction  l1 as [| r l1 Hrecl1];
[ simpl; simpl in H1; right; symmetry ; assumption
| elim (RList_P9 (cons r l1) l2 (pos_Rl (cons r l1) 0)); intros;
assert
(H4 :
In (pos_Rl (cons r l1) 0) (cons r l1) \/ In (pos_Rl (cons r l1) 0) l2);
[ left; left; reflexivity
| assert (H5 := H3 H4); apply RList_P5;
[ apply RList_P2; assumption | assumption ] ] ].
induction  l1 as [| r l1 Hrecl1];
[ simpl; simpl in H1; right; assumption
| assert
(H2 :
In (pos_Rl (cons_ORlist (cons r l1) l2) 0) (cons_ORlist (cons r l1) l2));
[ elim
(RList_P3 (cons_ORlist (cons r l1) l2)
(pos_Rl (cons_ORlist (cons r l1) l2) 0));
intros; apply H3; exists 0%nat; split;
[ reflexivity | rewrite RList_P11; simpl; apply lt_O_Sn ]
| elim (RList_P9 (cons r l1) l2 (pos_Rl (cons_ORlist (cons r l1) l2) 0));
intros; assert (H5 := H3 H2); elim H5; intro;
[ apply RList_P5; assumption
| rewrite H1; apply RList_P5; assumption ] ] ].
Qed.

Lemma RList_P16 :
forall l1 l2:Rlist,
ordered_Rlist l1 ->
ordered_Rlist l2 ->
pos_Rl l1 (pred (Rlength l1)) = pos_Rl l2 (pred (Rlength l2)) ->
pos_Rl (cons_ORlist l1 l2) (pred (Rlength (cons_ORlist l1 l2))) =
pos_Rl l1 (pred (Rlength l1)).
Proof. hammer_hook "RList" "RList.RList_P16".
intros; apply Rle_antisym.
induction  l1 as [| r l1 Hrecl1].
simpl; simpl in H1; right; symmetry ; assumption.
assert
(H2 :
In
(pos_Rl (cons_ORlist (cons r l1) l2)
(pred (Rlength (cons_ORlist (cons r l1) l2))))
(cons_ORlist (cons r l1) l2));
[ elim
(RList_P3 (cons_ORlist (cons r l1) l2)
(pos_Rl (cons_ORlist (cons r l1) l2)
(pred (Rlength (cons_ORlist (cons r l1) l2)))));
intros; apply H3; exists (pred (Rlength (cons_ORlist (cons r l1) l2)));
split; [ reflexivity | rewrite RList_P11; simpl; apply lt_n_Sn ]
| elim
(RList_P9 (cons r l1) l2
(pos_Rl (cons_ORlist (cons r l1) l2)
(pred (Rlength (cons_ORlist (cons r l1) l2)))));
intros; assert (H5 := H3 H2); elim H5; intro;
[ apply RList_P7; assumption | rewrite H1; apply RList_P7; assumption ] ].
induction  l1 as [| r l1 Hrecl1].
simpl; simpl in H1; right; assumption.
elim
(RList_P9 (cons r l1) l2 (pos_Rl (cons r l1) (pred (Rlength (cons r l1)))));
intros;
assert
(H4 :
In (pos_Rl (cons r l1) (pred (Rlength (cons r l1)))) (cons r l1) \/
In (pos_Rl (cons r l1) (pred (Rlength (cons r l1)))) l2);
[ left; change (In (pos_Rl (cons r l1) (Rlength l1)) (cons r l1));
elim (RList_P3 (cons r l1) (pos_Rl (cons r l1) (Rlength l1)));
intros; apply H5; exists (Rlength l1); split;
[ reflexivity | simpl; apply lt_n_Sn ]
| assert (H5 := H3 H4); apply RList_P7;
[ apply RList_P2; assumption
| elim
(RList_P9 (cons r l1) l2
(pos_Rl (cons r l1) (pred (Rlength (cons r l1)))));
intros; apply H7; left;
elim
(RList_P3 (cons r l1)
(pos_Rl (cons r l1) (pred (Rlength (cons r l1)))));
intros; apply H9; exists (pred (Rlength (cons r l1)));
split; [ reflexivity | simpl; apply lt_n_Sn ] ] ].
Qed.

Lemma RList_P17 :
forall (l1:Rlist) (x:R) (i:nat),
ordered_Rlist l1 ->
In x l1 ->
pos_Rl l1 i < x -> (i < pred (Rlength l1))%nat -> pos_Rl l1 (S i) <= x.
Proof. hammer_hook "RList" "RList.RList_P17".
simple induction l1.
intros; elim H0.
intros; induction  i as [| i Hreci].
simpl; elim H1; intro;
[ simpl in H2; rewrite H4 in H2; elim (Rlt_irrefl _ H2)
| apply RList_P5; [ apply RList_P4 with r; assumption | assumption ] ].
simpl; simpl in H2; elim H1; intro.
rewrite H4 in H2; assert (H5 : r <= pos_Rl r0 i);
[ apply Rle_trans with (pos_Rl r0 0);
[ apply (H0 0%nat); simpl; simpl in H3; apply neq_O_lt;
red; intro; rewrite <- H5 in H3; elim (lt_n_O _ H3)
| elim (RList_P6 r0); intros; apply H5;
[ apply RList_P4 with r; assumption
| apply le_O_n
| simpl in H3; apply lt_S_n; apply lt_trans with (Rlength r0);
[ apply H3 | apply lt_n_Sn ] ] ]
| elim (Rlt_irrefl _ (Rle_lt_trans _ _ _ H5 H2)) ].
apply H; try assumption;
[ apply RList_P4 with r; assumption
| simpl in H3; apply lt_S_n;
replace (S (pred (Rlength r0))) with (Rlength r0);
[ apply H3
| apply S_pred with 0%nat; apply neq_O_lt; red; intro;
rewrite <- H5 in H3; elim (lt_n_O _ H3) ] ].
Qed.

Lemma RList_P18 :
forall (l:Rlist) (f:R -> R), Rlength (app_Rlist l f) = Rlength l.
Proof. hammer_hook "RList" "RList.RList_P18".
simple induction l; intros;
[ reflexivity | simpl; rewrite H; reflexivity ].
Qed.

Lemma RList_P19 :
forall l:Rlist,
l <> nil ->  exists r : R, (exists r0 : Rlist, l = cons r r0).
Proof. hammer_hook "RList" "RList.RList_P19".
intros; induction  l as [| r l Hrecl];
[ elim H; reflexivity | exists r; exists l; reflexivity ].
Qed.

Lemma RList_P20 :
forall l:Rlist,
(2 <= Rlength l)%nat ->
exists r : R,
(exists r1 : R, (exists l' : Rlist, l = cons r (cons r1 l'))).
Proof. hammer_hook "RList" "RList.RList_P20".
intros; induction  l as [| r l Hrecl];
[ simpl in H; elim (le_Sn_O _ H)
| induction  l as [| r0 l Hrecl0];
[ simpl in H; elim (le_Sn_O _ (le_S_n _ _ H))
| exists r; exists r0; exists l; reflexivity ] ].
Qed.

Lemma RList_P21 : forall l l':Rlist, l = l' -> Rtail l = Rtail l'.
Proof. hammer_hook "RList" "RList.RList_P21".
intros; rewrite H; reflexivity.
Qed.

Lemma RList_P22 :
forall l1 l2:Rlist, l1 <> nil -> pos_Rl (cons_Rlist l1 l2) 0 = pos_Rl l1 0.
Proof. hammer_hook "RList" "RList.RList_P22".
simple induction l1; [ intros; elim H; reflexivity | intros; reflexivity ].
Qed.

Lemma RList_P23 :
forall l1 l2:Rlist,
Rlength (cons_Rlist l1 l2) = (Rlength l1 + Rlength l2)%nat.
Proof. hammer_hook "RList" "RList.RList_P23".
simple induction l1;
[ intro; reflexivity | intros; simpl; rewrite H; reflexivity ].
Qed.

Lemma RList_P24 :
forall l1 l2:Rlist,
l2 <> nil ->
pos_Rl (cons_Rlist l1 l2) (pred (Rlength (cons_Rlist l1 l2))) =
pos_Rl l2 (pred (Rlength l2)).
Proof. hammer_hook "RList" "RList.RList_P24".
simple induction l1.
intros; reflexivity.
intros; rewrite <- (H l2 H0); induction  l2 as [| r1 l2 Hrecl2].
elim H0; reflexivity.
do 2 rewrite RList_P23;
replace (Rlength (cons r r0) + Rlength (cons r1 l2))%nat with
(S (S (Rlength r0 + Rlength l2)));
[ replace (Rlength r0 + Rlength (cons r1 l2))%nat with
(S (Rlength r0 + Rlength l2));
[ reflexivity
| simpl; apply INR_eq; rewrite S_INR; do 2 rewrite plus_INR;
rewrite S_INR; ring ]
| simpl; apply INR_eq; do 3 rewrite S_INR; do 2 rewrite plus_INR;
rewrite S_INR; ring ].
Qed.

Lemma RList_P25 :
forall l1 l2:Rlist,
ordered_Rlist l1 ->
ordered_Rlist l2 ->
pos_Rl l1 (pred (Rlength l1)) <= pos_Rl l2 0 ->
ordered_Rlist (cons_Rlist l1 l2).
Proof. hammer_hook "RList" "RList.RList_P25".
simple induction l1.
intros; simpl; assumption.
simple induction r0.
intros; simpl; simpl in H2; unfold ordered_Rlist; intros;
simpl in H3.
induction  i as [| i Hreci].
simpl; assumption.
change (pos_Rl l2 i <= pos_Rl l2 (S i)); apply (H1 i); apply lt_S_n;
replace (S (pred (Rlength l2))) with (Rlength l2);
[ assumption
| apply S_pred with 0%nat; apply neq_O_lt; red; intro;
rewrite <- H4 in H3; elim (lt_n_O _ H3) ].
intros; clear H; assert (H : ordered_Rlist (cons_Rlist (cons r1 r2) l2)).
apply H0; try assumption.
apply RList_P4 with r; assumption.
unfold ordered_Rlist; intros; simpl in H4;
induction  i as [| i Hreci].
simpl; apply (H1 0%nat); simpl; apply lt_O_Sn.
change
(pos_Rl (cons_Rlist (cons r1 r2) l2) i <=
pos_Rl (cons_Rlist (cons r1 r2) l2) (S i));
apply (H i); simpl; apply lt_S_n; assumption.
Qed.

Lemma RList_P26 :
forall (l1 l2:Rlist) (i:nat),
(i < Rlength l1)%nat -> pos_Rl (cons_Rlist l1 l2) i = pos_Rl l1 i.
Proof. hammer_hook "RList" "RList.RList_P26".
simple induction l1.
intros; elim (lt_n_O _ H).
intros; induction  i as [| i Hreci].
apply RList_P22; discriminate.
apply (H l2 i); simpl in H0; apply lt_S_n; assumption.
Qed.

Lemma RList_P27 :
forall l1 l2 l3:Rlist,
cons_Rlist l1 (cons_Rlist l2 l3) = cons_Rlist (cons_Rlist l1 l2) l3.
Proof. hammer_hook "RList" "RList.RList_P27".
simple induction l1; intros;
[ reflexivity | simpl; rewrite (H l2 l3); reflexivity ].
Qed.

Lemma RList_P28 : forall l:Rlist, cons_Rlist l nil = l.
Proof. hammer_hook "RList" "RList.RList_P28".
simple induction l;
[ reflexivity | intros; simpl; rewrite H; reflexivity ].
Qed.

Lemma RList_P29 :
forall (l2 l1:Rlist) (i:nat),
(Rlength l1 <= i)%nat ->
(i < Rlength (cons_Rlist l1 l2))%nat ->
pos_Rl (cons_Rlist l1 l2) i = pos_Rl l2 (i - Rlength l1).
Proof. hammer_hook "RList" "RList.RList_P29".
simple induction l2.
intros; rewrite RList_P28 in H0; elim (lt_irrefl _ (le_lt_trans _ _ _ H H0)).
intros;
replace (cons_Rlist l1 (cons r r0)) with
(cons_Rlist (cons_Rlist l1 (cons r nil)) r0).
inversion H0.
rewrite <- minus_n_n; simpl; rewrite RList_P26.
clear l2 r0 H i H0 H1 H2; induction  l1 as [| r0 l1 Hrecl1].
reflexivity.
simpl; assumption.
rewrite RList_P23; rewrite plus_comm; simpl; apply lt_n_Sn.
replace (S m - Rlength l1)%nat with (S (S m - S (Rlength l1))).
rewrite H3; simpl;
replace (S (Rlength l1)) with (Rlength (cons_Rlist l1 (cons r nil))).
apply (H (cons_Rlist l1 (cons r nil)) i).
rewrite RList_P23; rewrite plus_comm; simpl; rewrite <- H3;
apply le_n_S; assumption.
repeat rewrite RList_P23; simpl; rewrite RList_P23 in H1;
rewrite plus_comm in H1; simpl in H1; rewrite (plus_comm (Rlength l1));
simpl; rewrite plus_comm; apply H1.
rewrite RList_P23; rewrite plus_comm; reflexivity.
change (S (m - Rlength l1) = (S m - Rlength l1)%nat);
apply minus_Sn_m; assumption.
replace (cons r r0) with (cons_Rlist (cons r nil) r0);
[ symmetry ; apply RList_P27 | reflexivity ].
Qed.


(* Source langauge *)

Require Import Bool Arith List Cpdt.CpdtTactics.
Set Implicit Arguments.
Set Asymmetric Patterns.

Inductive binop : Set := Plus | Times.

Inductive exp : Set :=
| Const : nat -> exp
| Binop : binop -> exp -> exp -> exp.

Definition binopDenote (b: binop) : nat -> nat -> nat :=
  match b with
  | Plus => plus
  | Times => mult
  end.

Fixpoint expDenote(e: exp): nat :=
  match e with
  | Const n => n
  | Binop b e1 e2 => (binopDenote b) (expDenote e1) (expDenote e2)
  end.

(* Examples *)

Eval simpl in expDenote (Const 42).

Eval simpl in expDenote (Binop Plus (Const 2) (Const 2)).

Eval simpl in expDenote (Binop Times (Binop Plus (Const 2) (Const 2)) (Const 7)).

(* Target Language *)

Inductive instr : Set :=
| iConst : nat -> instr
|iBinop : binop -> instr.

Definition prog := list instr.
Definition stack := list nat.

Definition instrDenote (i: instr) (s: stack) : option stack :=
  match i with
  | iConst n => Some (n :: s)
  | iBinop b =>
    match s with
    | arg1 :: arg2 :: s' => Some ((binopDenote b) arg1 arg2 :: s')
    | _ => None
    end
  end.

Fixpoint progDenote(p: prog) (s: stack) : option stack :=
  match p with
  | nil => Some s
  | i :: p' =>
    match instrDenote i s with
    | None => None
    | Some s' => progDenote p' s'
    end
  end.

(* Translation *)

Fixpoint compile (e: exp) : prog :=
  match e with
  | Const n => iConst n :: nil
  | Binop b e1 e2 => compile e2 ++ compile e1 ++ iBinop b :: nil
  end.

Eval simpl in compile (Const 42).
Eval simpl in compile (Binop Plus (Const 2) (Const 2)).
Eval simpl in compile (Binop Times (Binop Plus (Const 2) (Const 2)) (Const 7)).

Eval simpl in progDenote (compile (Const 42)) nil.
Eval simpl in progDenote (compile (Binop Plus (Const 2) (Const 2))) nil.
Eval simpl in progDenote (compile (Binop Times (Binop Plus (Const 2) (Const 2)) (Const 7))) nil.

(* Correctness proof *)

Theorem compile_correct : forall e , progDenote (compile e) nil = Some (expDenote e :: nil).

Abort.

(* Trick: Strengtening the induction hypothesis *)

Lemma compile_correct' : forall e p s ,
    progDenote (compile e ++ p) s = progDenote p (expDenote e :: s).

(* First subgoal *)
induction e.
intros.
unfold compile.
unfold expDenote.
unfold progDenote at 1.
simpl.
fold progDenote.
reflexivity.

intros.
unfold compile.
fold compile.
unfold expDenote.
fold expDenote.

Check app_assoc_reverse.
SearchRewrite ((_ ++ _) ++ _).

rewrite app_assoc_reverse.
rewrite IHe2.

rewrite app_assoc_reverse.
rewrite IHe1.

unfold progDenote at 1.
simpl.
fold progDenote.
reflexivity.

Abort.

(* Automatic reasoning *)

Lemma compile_correct'' : forall e s p, progDenote (compile e ++ p) s =
  progDenote p (expDenote e :: s).
  induction e; crush.
Qed.

Theorem compile_correct : forall e , progDenote (compile e) nil = Some (expDenote e :: nil).

intros.

Check app_nil_end.

rewrite (app_nil_end (compile e)).
rewrite compile_correct''.
reflexivity.
Qed.


Require Import List QArith Qreals.

Local Open Scope Q.

Import ListNotations.
Notation Qmin := Qminmax.Qmin.

Fixpoint Qmin_list l : Q :=
  match l with
  | nil => 0
  | a :: l0 => match l0 with
            | nil => a
            | _ => Qmin a (Qmin_list l0)
            end
  end.

Lemma Qmin_list_cons a b l :
  Qmin_list (a :: b :: l) = Qmin a (Qmin_list (b :: l)).
Proof. reflexivity. Qed.

Lemma Qmin_list_snoc a b l :
  Qmin_list (a :: l ++ [b]) == Qmin (Qmin_list (a :: l)) b.
Proof.
  revert a; induction l; intros.
  - reflexivity.
  - rewrite <- app_comm_cons, Qmin_list_cons. rewrite IHl.
    rewrite Qmin_list_cons. apply Qminmax.Q.min_assoc.
Qed.

Lemma Qmin_list_cons_cons a b l :
  Qmin_list (a :: b :: l) == Qmin_list (b :: a :: l).
Proof.
  remember (rev l).
  revert l Heql0; induction l0; intros.
  - apply (f_equal (@rev Q)) in Heql0.
    rewrite rev_involutive in Heql0. simpl in Heql0. subst.
    apply Qminmax.Q.min_comm.
  - apply (f_equal (@rev Q)) in Heql0.
    rewrite rev_involutive in Heql0. simpl in Heql0. subst.
    rewrite !app_comm_cons.
    setoid_rewrite Qmin_list_snoc.
    rewrite IHl0.
    reflexivity.
    symmetry; apply rev_involutive.
Qed.

Lemma Qmin_list_spec l :
  forall a, In a l -> Qmin_list l <= a.
Proof.
  induction l; intros.
  - destruct H.
  - destruct H.
    + subst. destruct l.
      * apply Qle_refl.
      * rewrite Qmin_list_cons.
        apply Qminmax.Q.le_min_l.
    + destruct l.
      * destruct H.
      * rewrite Qmin_list_cons.
        eapply Qle_trans.
        apply Qminmax.Q.le_min_r.
        apply IHl. assumption. Qed.

Lemma Qmin_list_spec2 l (H : ~ (l = nil)) :
  exists a, In a l /\ Qmin_list l == a.
Proof.
  induction l.
  - contradiction.
  -
    destruct l.
    + simpl. exists a. split. left; reflexivity. reflexivity.
    + assert (q :: l <> nil) by congruence.
      apply IHl in H0. destruct H0 as [x []].
      destruct (Qminmax.Q.min_dec a x).
      * exists a. split. left; reflexivity. rewrite Qmin_list_cons. rewrite H1. assumption.
      * exists x. split. right; assumption. rewrite Qmin_list_cons. rewrite H1. assumption. Qed.

Lemma Qmin_list_app l k : l <> [] -> k <> [] ->
  Qmin_list (l ++ k) == Qmin (Qmin_list l) (Qmin_list k).
Proof.
  destruct k; destruct l; try congruence; intros.
  revert q k H0; induction l; intros.
  - reflexivity.
  -
    rewrite <- app_comm_cons.
    rewrite <- app_comm_cons.
    rewrite Qmin_list_cons_cons.
    rewrite Qmin_list_cons.
    setoid_rewrite IHl; try congruence.
    rewrite Qmin_list_cons_cons.
    rewrite Qmin_list_cons.
    rewrite Qminmax.Q.min_assoc. reflexivity.
Qed.

Instance Qmin_list_single_proper : Proper (Qeq ==> Qeq) (fun q => Qmin_list [q]).
Proof.
  do 2 red; intros. apply H.
Qed.

Instance Qmin_list_proper : Proper (eq ==> Qeq) Qmin_list.
Proof.
  do 2 red; intros. rewrite H. reflexivity.
Qed.

Lemma Qmin_list_single q : Qmin_list [q] = q.
Proof. reflexivity. Qed.

Inductive Qlist_eq : list Q -> list Q -> Prop :=
| Qlist_eq_nil : Qlist_eq [] []
| Qlist_eq_cons x y l k : x == y -> Qlist_eq l k -> Qlist_eq (x :: l) (y :: k).
Local Notation "l == k" := (Qlist_eq l k).

Require Import Classes.RelationClasses.

Instance Qlist_eq_refl : Reflexive Qlist_eq.
Proof. red; intros; induction x; constructor; [reflexivity|assumption]. Qed.
Instance Qlist_eq_sym : Symmetric Qlist_eq.
Proof. red; intros. induction H; [reflexivity|constructor; [symmetry|]; assumption]. Qed.
Instance Qlist_eq_trans : Transitive Qlist_eq.
Proof. red. intros x. induction x; intros.
       - inversion H; subst; assumption.
       - destruct z.
         + inversion H0; subst; assumption.
         + inversion H. subst. inversion H0. subst. constructor.
           etransitivity. eassumption. assumption.
           apply IHx with (y:=k); assumption. Qed.

Instance cons_Qlist_eq_proper : Proper (Qeq ==> Qlist_eq ==> Qlist_eq) cons.
Proof.
  red; red; intros.
  red; intros. constructor; assumption. Qed.

Instance app_Qlist_eq_proper : Proper (Qlist_eq ==> Qlist_eq ==> Qlist_eq) (@app Q).
Proof.
  red; red; intros.
  red; intros. induction H.
  - assumption.
  - rewrite <- !app_comm_cons. constructor. assumption. assumption. Qed.

Instance Qmin_list_proper2 : Proper (Qlist_eq ==> Qeq) Qmin_list.
Proof.
  do 2 red. intros.
  destruct H.
  - reflexivity.
  - induction H0.
    + assumption.
    + do 2 rewrite Qmin_list_cons_cons, Qmin_list_cons.
      setoid_rewrite IHQlist_eq.
      setoid_rewrite H0. reflexivity. Qed.


(* Definitions that are used by ott-generated output (when using non-expanded lists) *)

Require Import Bool.
Require Import List.
Set Implicit Arguments.

Section list_predicates.
Variable (A : Type).

(* Test whether a predicate [p] holds for every element of a list [l]. *)
Definition forall_list (p:A->bool) (l:list A) :=
  fold_left (fun b (z:A) => b && p z) l true.

(* Test whether a predicate [p] holds for some element of a list [l]. *)
Definition exists_list (p:A->bool) (l:list A) :=
  fold_left (fun b (z:A) => b || p z) l false.

(* Assert that a property holds for every element of a list *)
Inductive Forall_list (P:A->Prop) : list A -> Prop :=
  | Forall_nil : Forall_list P nil
  | Forall_cons :
    forall x l, P x -> Forall_list P l -> Forall_list P (x::l).
(* Assert that a property holds for some element of a list *)
Inductive Exists_list (P:A->Prop) : list A -> Prop :=
  | Exists_head : forall x l, P x -> Exists_list P (x::l)
  | Exists_tail : forall x l, Exists_list P l -> Exists_list P (x::l).

End list_predicates.
Hint Constructors Forall_list Exists_list : core.

Section list_mem.
(* Functions about membership in a list, with equality between a list
   element and a potential member being decided by [eq_dec]. *)
Variable (A : Type).
Variable (eq_dec : forall (a b:A), {a=b} + {a<>b}).

(* Test whether [x] appears in [l]. *)
Fixpoint list_mem (x:A) (l:list A) {struct l} : bool :=
  match l with
  | nil => false
  | cons h t => if eq_dec h x then true else list_mem x t
end.

(* Remove any element of [l1] that is present in [l2]. *)
Fixpoint list_minus (l1 l2:list A) {struct l1} : list A :=
  match l1 with
  | nil => nil
  | cons h t =>
    if (list_mem h l2) then list_minus t l2 else cons h (list_minus t l2)
end.
End list_mem.

Section Flat_map_definition.
Variables (A B : Type).
Variable (f : A -> list B).
(* This definition is almost the same as the one in the standard library of
   Coq V8.0 or V8.1. The difference is that this version has the shape
    fun A B f => (fix flat_map l := _)
   while the standard library has
    fun A B => (fix flat_map f l := _)
   Our version has the advantage of making recursive definitions such as
    fix foo x := match x with ... | List xs => flat_map foo xs end
   well-founded.
 *)
Fixpoint flat_map (l:list A) {struct l} : list B :=
  match l with
    | nil => nil
    | cons x t => (f x) ++ (flat_map t)
  end.
End Flat_map_definition.

(* Provide helper lemmas for {{coq-equality}} homs. *)
Require Export Ott.ott_list_eq_dec.


Require Export D.

(** 3 star (even__ev)
    Note that proving [even__ev] directly is hard.
    You might find it easier to prove [even__ev_strong] first
    and then prove [even__ev] using it.
*)

Lemma even__ev_weak: forall n: nat,
  (even n -> ev n) /\ (even (S n) -> ev (S n)).
Proof.
  induction n.
  Case "n = 0". split. intro H. apply ev_0. intro Hcontra. inversion Hcontra.
  Case "n = S n". inversion IHn. split. apply H0.
    intro HSS. apply ev_SS. unfold even in HSS. simpl in HSS. apply H. apply HSS. Qed.

Lemma even__ev_strong: forall n : nat,
  (even (pred n) -> ev (pred n)) /\ (even n -> ev n).
Proof.
  induction n.
  Case "n = 0". simpl. split. intro H. apply ev_0. intro H. apply ev_0.
  Case "n = S n". simpl. apply even__ev_weak. Qed.

Theorem even__ev: forall n : nat,
  even n -> ev n.
Proof.
  intro. apply even__ev_strong.
  Qed.


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

(* $Date: 2012-09-08 20:51:57 -0400 (Sat, 08 Sep 2012) $ *)

(** The next line imports all of our definitions from the
    previous chapter. *)

Require Export Basics.

(** For it to work, you need to use [coqc] to compile [Basics.v]
    into [Basics.vo].  (This is like making a .class file from a .java
    file, or a .o file from a .c file.)

    Here are two ways to compile your code:

     - CoqIDE:

         Open [Basics.v].
         In the "Compile" menu, click on "Compile Buffer".

     - Command line:

         Run [coqc Basics.v]

    In this file, we again use the [Module] feature to wrap all of the
    definitions for pairs and lists of numbers in a module so that,
    later, we can reuse the same names for improved (generic) versions
    of the same operations. *)

Module NatList.

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

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

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]." *)

(** We can construct an element of [natprod] like this: *)

Eval simpl in (pair 3 5).

(** Here are two simple function definitions 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.

Eval simpl in (fst (pair 3 5)).

(** 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 notation can be used both in expressions and in
    pattern matches (indeed, we've seen it already in the previous
    chapter -- this notation is provided as part of the standard
    library): *)

Eval simpl in (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 and prove a few simple facts about pairs.  If we
    state the lemmas in a particular (and slightly peculiar) way, we
    can prove them 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! *)
Admitted.

(** 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].

    Notice that, unlike for [nat]s, [destruct] doesn't generate an
    extra subgoal here.  That's because [natprod]s can only be
    constructed in one way.  *)

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

(** Notice that Coq allows us to use the notation we introduced
    for pairs in the "[as]..." pattern that tells it what variables to
    bind. *)

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

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

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

(** Generalizing the definition of pairs a little, 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 two 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 fully understand 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])].

   (By the way, it's worth noting in passing that 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 are there to instruct the "coqdoc" tool that the bracketed
   part should be displayed as Coq code rather than running text.
   These brackets don't appear in the generated HTML.)

   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. *)

(** 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.

(** 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.

(** The [app] ("append") function concatenates 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.

(** Here are two smaller examples of programming with lists.
    The [hd] function returns the first element (the "head") of the
    list, while [tail] returns everything but the first
    element.  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 tail (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_tail:            tail [1,2,3] = [2,3].
Proof. reflexivity.  Qed.

(** **** Exercise: 2 stars, recommended (list_funs) *)
(** Complete the definitions of [nonzeros], [oddmembers] and
    [countoddmembers] below.  *)

Fixpoint nonzeros (l:natlist) : natlist :=
  match l with
  | nil => nil
  | O :: t => nonzeros t
  | n :: t => n :: nonzeros t
  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 => if oddb h
    then h :: oddmembers t
    else oddmembers t
  end.

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

Fixpoint countoddmembers (l:natlist) : nat :=
  match l with
  | nil => 0
  | h :: t => if oddb h
    then 1 + countoddmembers t
    else countoddmembers t
  end.

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, recommended (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, but each element can appear
    multiple times instead of just once.  One reasonable
    implementation of bags 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 => if beq_nat h v
    then 1 + count v t
    else count v t
  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, which
    is why we don't use that name for this operation.)
    For [sum] we're giving you a header that does not give explicit
    names to the arguments.  Moreover, it uses the keyword
    [Definition] instead of [Fixpoint], so even if you had names for
    the arguments, you wouldn't be able to process them recursively.
    The point of stating the question this way is to encourage you to
    think about whether [sum] can be implemented in another way --
    perhaps by using functions that have already been defined.  *)

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

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

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

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. *)

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

Example test_remove_one1:         count 5 (remove_one 5 [2,1,5,4,1]) = 0.
Proof. 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 => if beq_nat h v
    then remove_all v t
    else h :: remove_all v t
  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 :=
  admit.

Example test_subset1:              subset [1,2] [2,1,4,1] = true.
Admitted.
Example test_subset2:              subset [1,2,2] [2,1,4,1] = false.
Admitted.
(** [] *)

(** **** Exercise: 3 stars, recommended (bag_theorem) *)
(** Write down an interesting 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 : forall (n : nat) (s : bag),
  count n (add n s) = S (count n s).
Proof.
  Admitted.

(** [] *)

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

(** Just 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 match position
    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 (tail l).
Proof.
  intros l. destruct l as [| n l'].
  Case "l = nil".
    reflexivity.
  Case "l = cons n l'".
    reflexivity.  Qed.

(** Here, the [nil] case works because we've chosen to define
    [tail 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 proofs will not get you very far!  It is
    very important to work through the details of each one, using Coq
    and thinking about what each step of the proof achieves.
    Otherwise it is more or less guaranteed that the exercises will
    make no sense. *)

(* ###################################################### *)
(** ** Induction on Lists *)

(** Proofs by induction over datatypes like [natlist] are
    perhaps a little less familiar than standard natural number
    induction, but the basic idea is equally simple.  Each [Inductive]
    declaration defines a set of data values that can be built up from
    the declared constructors: a boolean can be either [true] or
    [false]; a number can be either [O] or [S] applied to a 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 things together establish the
    truth of [P] for all lists [l].  Here's a concrete example: *)

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

(** 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.  *)

(** _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.  []

  Here is an exercise to be worked together in class: *)

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'].
  Case "l1 = nil".
    reflexivity.
  Case "l1 = cons".
    simpl. rewrite -> IHl1'. reflexivity.  Qed.

(** For a slightly more involved example of an inductive proof
    over lists, suppose we define a "cons on the right" function
    [snoc] like this... *)

Fixpoint snoc (l:natlist) (v:nat) : natlist :=
  match l with
  | nil    => [v]
  | h :: t => h :: (snoc t v)
  end.

(** ... and use it to define a list-reversing function [rev]
    like this: *)

Fixpoint rev (l:natlist) : natlist :=
  match l with
  | nil    => nil
  | h :: t => snoc (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.

(** Now let's prove some more list theorems using our newly
    defined [snoc] and [rev].  For something a little more challenging
    than the inductive proofs we've seen so far, let's prove that
    reversing a list does not change its length.  Our first attempt at
    this proof 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'].
  Case "l = []".
    reflexivity.
  Case "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
       [snoc], but we don't have any equations in either the
       immediate context or the global environment that have
       anything to do with [snoc]!

       We can make a little progress by using the IH to rewrite the
       goal... *)
    rewrite <- IHl'.
    (* ... but now we can't go any further. *)
Admitted.

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

Theorem length_snoc : forall n : nat, forall l : natlist,
  length (snoc l n) = S (length l).
Proof.
  intros n l. induction l as [| n' l'].
  Case "l = nil".
    reflexivity.
  Case "l = cons n' l'".
    simpl. rewrite -> IHl'. reflexivity.  Qed.

(** 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'].
  Case "l = nil".
    reflexivity.
  Case "l = cons".
    simpl. rewrite -> length_snoc.
    rewrite -> IHl'. reflexivity.  Qed.

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

    _Theorem_: For all numbers [n] and lists [l],
       [length (snoc l n) = S (length l)].

    _Proof_: By induction on [l].

    - First, suppose [l = []].  We must show
        length (snoc [] n) = S (length []),
      which follows directly from the definitions of
      [length] and [snoc].

    - Next, suppose [l = n'::l'], with
        length (snoc l' n) = S (length l').
      We must show
        length (snoc (n' :: l') n) = S (length (n' :: l')).
      By the definitions of [length] and [snoc], this
      follows from
        S (length (snoc l' n)) = S (S (length l')),
]]
      which is immediate from 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 (snoc (rev l') n) = S (length l')
        which, by the previous lemma, is the same as
          S (length (rev l')) = S (length l').
        This is immediate from the induction hypothesis. [] *)

(** Obviously, the style of these proofs is rather longwinded
    and pedantic.  After the first few, we might find it easier to
    follow proofs that give a little less detail overall (since we can
    easily work them 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 more like this: *)

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

    _Proof_: First, observe that
       length (snoc l n) = S (length l)
     for any [l].  This follows by a straightforward induction on [l].
     The main property now follows by another straightforward
     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 on 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
    present purposes. *)

(* ###################################################### *)
(** ** [SearchAbout] *)

(** We've seen that proofs can make use of other theorems we've
    already proved, using [rewrite], and later we will see other ways
    of reusing previous theorems.  But in order to refer to a theorem,
    we need to know its name, and remembering the names of all the
    theorems we might ever want to use can become quite difficult!  It
    is often hard even to remember what theorems have been proven,
    much less what they are named.

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

(* SearchAbout rev. *)

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

(** Also, if you are using ProofGeneral, you can run [SearchAbout]
    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, recommended (list_exercises) *)
(** More practice with lists. *)

Theorem app_nil_end : forall l : natlist,
  l ++ [] = l.
Proof.
  intros l. induction l as [| n l'].
  Case "l = []". reflexivity.
  Case "l = n :: l'". simpl. rewrite -> IHl'. reflexivity.  Qed.

Theorem rev_snoc : forall l : natlist, forall n : nat,
  rev (snoc l n) = n :: (rev l).
Proof.
  intros l n. induction l.
  reflexivity.
  simpl. rewrite -> IHl. reflexivity.  Qed.

Theorem rev_involutive : forall l : natlist,
  rev (rev l) = l.
Proof.
  intros l. induction l as [| n l'].
  Case "l = []". reflexivity.
  Case "l = n : l'".
    simpl.
    rewrite -> rev_snoc.
    rewrite -> IHl'.
    reflexivity.  Qed.

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

Theorem app_ass4 : forall l1 l2 l3 l4 : natlist,
  l1 ++ (l2 ++ (l3 ++ l4)) = ((l1 ++ l2) ++ l3) ++ l4.
Proof.
  intros l1 l2 l3 l4.
  rewrite -> app_ass. rewrite -> app_ass.
  reflexivity.  Qed.

Theorem snoc_append : forall (l:natlist) (n:nat),
  snoc l n = l ++ [n].
Proof.
  intros l n. induction l.
  reflexivity.
  simpl. rewrite -> IHl. reflexivity.  Qed.

Theorem app_nil : forall l : natlist,
  l ++ [] = l.
Proof.
  intros l. induction l.
  reflexivity.
  simpl. rewrite -> IHl. reflexivity.  Qed.

Theorem distr_rev : forall l1 l2 : natlist,
  rev (l1 ++ l2) = (rev l2) ++ (rev l1).
Proof.
  intros l1 l2. induction l1.
  simpl. rewrite -> app_nil. reflexivity.
  simpl.
  rewrite -> IHl1.
  rewrite -> snoc_append.
  rewrite -> snoc_append.
  rewrite -> app_ass.
  reflexivity.  Qed.

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

Lemma nonzeros_length : forall l1 l2 : natlist,
  nonzeros (l1 ++ l2) = (nonzeros l1) ++ (nonzeros l2).
Proof.
  intros l1 l2. induction l1.
    simpl. reflexivity.
    destruct n.
      simpl. rewrite -> IHl1. reflexivity.
      simpl. rewrite -> IHl1. reflexivity.  Qed.

(** [] *)

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

(** **** Exercise: 2 stars, recommended (list_design) *)
(** Design exercise:
     - Write down a non-trivial theorem involving [cons]
       ([::]), [snoc], and [append] ([++]).
     - Prove it. *)

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

(** **** Exercise: 2 stars, optional (bag_proofs) *)
(** If you did the optional exercise about bags above, here are a
    couple of little theorems to prove about your definitions. *)

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

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

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

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

(** **** Exercise: 3 stars, optional (bag_count_sum) *)
(** Write down an interesting theorem about bags involving the
    functions [count] and [sum], and prove it.*)

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

(** **** Exercise: 4 stars, optional (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 solve this exercise.
*)

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 *)

(** Here is another type definition that is often useful in
    day-to-day programming: *)

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

(** One use of [natoption] is as a way of returning "error
    codes" from functions.  For example, 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 return some
    number when the list is too short! *)

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

(** On the other hand, if we give it type [nat -> natlist ->
    natoption], then we can return [None] when the list is too short
    and [Some a] when the list has enough members and [a] appears at
    position [n]. *)

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

Example test_index1 :    index 0 [4,5,6,7]  = Some 4.
Proof. reflexivity.  Qed.
Example test_index2 :    index 3 [4,5,6,7]  = Some 7.
Proof. reflexivity.  Qed.
Example test_index3 :    index 10 [4,5,6,7] = None.
Proof. reflexivity.  Qed.

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

Fixpoint index' (n:nat) (l:natlist) : natoption :=
  match l with
  | nil => None
  | a :: l' => if beq_nat n O then Some a else index' (pred n) l'
  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 allows 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_opt) *)
(** 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_opt (l : natlist) : natoption :=
  match l with
  | nil    => None
  | h :: _ => Some h
  end.

Example test_hd_opt1 : hd_opt [] = None.
Proof. reflexivity.  Qed.

Example test_hd_opt2 : hd_opt [1] = Some 1.
Proof. reflexivity.  Qed.

Example test_hd_opt3 : hd_opt [5,6] = Some 5.
Proof. reflexivity.  Qed.
(** [] *)

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

Theorem option_elim_hd : forall (l:natlist) (default:nat),
  hd default l = option_elim default (hd_opt l).
Proof.
  intros l default. destruct l.
    reflexivity.
    simpl. reflexivity.  Qed.
(** [] *)

(** **** Exercise: 2 stars, recommended (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
  | nil, _   => false
  | _, nil   => false
  | h1 :: t1, h2 :: t2 => andb (beq_nat h1 h2) (beq_natlist t1 t2)
  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.
  intros l. induction l as [| h l'].
    Case "l = []". reflexivity.
    Case "l = cons".
      simpl.
      rewrite <- IHl'.
      rewrite <- beq_nat_refl.
      simpl. reflexivity.  Qed.

(** [] *)

(* ###################################################### *)
(** * Extended Exercise: Dictionaries *)

(** As a final illustration of how fundamental data structures
    can be defined in Coq, here is the declaration of a simple
    [dictionary] data type, using numbers for both the keys and the
    values stored under these keys.  (That is, a dictionary represents
    a finite map from numbers to numbers.) *)

Module Dictionary.

Inductive dictionary : Type :=
  | empty  : dictionary
  | record : nat -> nat -> dictionary -> dictionary.

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

Definition insert (key value : nat) (d : dictionary) : dictionary :=
  (record key value d).

(** Here is a function [find] that searches a [dictionary] for a
    given key.  It evaluates evaluates to [None] if the key was not
    found and [Some val] if the key was mapped to [val] in the
    dictionary. If the same key is mapped to multiple values, [find]
    will return the first one it finds. *)

Fixpoint find (key : nat) (d : dictionary) : natoption :=
  match d with
  | empty         => None
  | record k v d' => if (beq_nat key k) then (Some v) else (find key d')
  end.

(** **** Exercise: 1 star (dictionary_invariant1) *)
(** Complete the following proof. *)

Theorem dictionary_invariant1 : forall (d : dictionary) (k v: nat),
  (find k (insert k v d)) = Some v.
Proof.
  intros d k v.
  simpl.
  rewrite <- beq_nat_refl.
  reflexivity.  Qed.

(** [] *)

(** **** Exercise: 1 star (dictionary_invariant2) *)
(** Complete the following proof. *)

Theorem dictionary_invariant2 : forall (d : dictionary) (m n o: nat),
  (beq_nat m n) = false -> (find m d) = (find m (insert n o d)).
Proof.
  intros d m n o.
  intros H.
  simpl.
  rewrite -> H.
  reflexivity.  Qed.
(** [] *)

End Dictionary.

End NatList.