(** Generated by coq-of-ocaml *)
Require Import OCaml.OCaml.

Local Set Primitive Projections.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope type_scope.
Import ListNotations.

Unset Positivity Checking.
Unset Guard Checking.

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

Inductive natural : Set :=
| Zero : natural
| Succ : natural -> natural.

Fixpoint plus (plus_arg0 : natural) (plus_arg1 : natural) {struct plus_arg0}
  : natural :=
  match plus_arg0 with
  | Zero => plus_arg1
  | Succ n => Succ (plus n plus_arg1)
  end.

Fixpoint mult (mult_arg0 : natural) (mult_arg1 : natural) {struct mult_arg0}
  : natural :=
  match mult_arg0 with
  | Zero => Zero
  | Succ n => plus (mult n mult_arg1) mult_arg1
  end.

Definition synth (lf2 : natural) (lf1 : natural) : natural :=
  plus Zero (plus lf1 lf2).


(** Generated by coq-of-ocaml *)
Require Import OCaml.OCaml.

Local Set Primitive Projections.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope type_scope.
Import ListNotations.

Unset Positivity Checking.
Unset Guard Checking.

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

Inductive natural : Set :=
| Zero : natural
| Succ : natural -> natural.

Fixpoint plus (plus_arg0 : natural) (plus_arg1 : natural) {struct plus_arg0}
  : natural :=
  match plus_arg0 with
  | Zero => plus_arg1
  | Succ n => Succ (plus n plus_arg1)
  end.

Fixpoint mult (mult_arg0 : natural) (mult_arg1 : natural) {struct mult_arg0}
  : natural :=
  match mult_arg0 with
  | Zero => Zero
  | Succ n => plus (mult n mult_arg1) mult_arg1
  end.

Definition synth (y : natural) (x : natural) (lf1 : natural) : natural :=
  plus y (plus x lf1).


Require Import ZArith.
Require Import Setoid.

Local Open Scope Z_scope.

Add Parametric Relation : Z Z.le
reflexivity proved by Z.le_refl
transitivity proved by Z.le_trans as le.

Lemma eq_Zle : forall (x y : Z), x = y -> x <= y.
Proof.
intros ; subst ; reflexivity.
Qed.


(* This code is copyrighted by its authors; it is distributed under  *)
(* the terms of the LGPL license (see LICENSE and description files) *)

(* Contribution to the Coq Library   V6.3 (July 1999)                    *)
(****************************************************************************)
(*                                                                          *)
(*                                                                          *)
(*                   Solange Coupet-Grimal & Line Jakubiec                  *)
(*                                                                          *)
(*                                                                          *)
(*              Laboratoire d'Informatique de Marseille                     *)
(*               CMI-Technopole de Chateau-Gombert                          *)
(*                   39, Rue F. Joliot Curie                                *)
(*                   13453 MARSEILLE Cedex 13                               *)
(*           e-mail:{Solange.Coupet,Line.Jakubiec}@lim.univ-mrs.fr          *)
(*                                                                          *)
(*                                                                          *)
(*                                Coq V5.10                                 *)
(*                              May 30th 1996                               *)
(*                                                                          *)
(****************************************************************************)
(*                                Numerals.v                                *)
(****************************************************************************)

Require Export Dependent_lists.
Require Export Lib_Arith.

Definition Inj (A : Set) (P : A -> Prop) (e : {x : A | P x}) :=
  let (a, _) return A := e in a.

Fixpoint exp (e x : nat) {struct x} : nat :=
  match x with
  | O => 1
  | S p => e * exp e p
  end.

Section Numerals.

  Definition BT := {b : nat | 0 < b}.
  Variable BASE : BT.
  Definition base := Inj nat (fun b : nat => 0 < b) BASE.
  Definition digit := {x : nat | x < base}.
  Definition val : digit -> nat := Inj nat (fun x : nat => x < base).
  Definition num := list digit.
  Definition inf (n : nat) := {x : nat | x < n}.
  Definition val_inf (n : nat) : inf n -> nat :=
    Inj nat (fun x : nat => x < n).
  Let Cons := cons digit.
  Let Nil := nil digit.

  Fixpoint Val (n : nat) (X : num n) {struct X} : nat :=
    match X with
    | nil => 0
    | cons p xp X' => val xp * exp base p + Val p X'
    end.

  Lemma Val_val : forall x : digit, Val 1 (Cons 0 x Nil) = val x.
  simpl in |- *.
  intros x.
  rewrite (mult_1_r (val x)).
  auto.
  Qed.

  Lemma upper_bound : forall (n : nat) (X : num n), Val n X < exp base n.
  intros n X; elim X.
  auto.
  intros n0 y l H_rec.
  simpl in |- *.
  apply lt_le_trans with (pred base * exp_n base n0 + exp_n base n0).
  apply le_lt_plus_mult.
  elim y.
  intros x H_y.
  simpl in |- *.
  auto. (*lt_le_pred*)
  trivial.
  replace (pred base) with (base - 1). (*pred_minus*)
  rewrite mult_minus_distr_r.
  simpl in |- *.
  elim plus_n_O.
  elim exp_n_plus_p1.
  elim plus_Snm_nSm; simpl in |- *; elim plus_n_O.
  elim plus_comm.
  elim le_plus_minus.
  apply le_mult_cst; auto.
  apply le_exp_n_mult.
  unfold base in |- *; case BASE; auto.
  auto.
  Qed.

  Definition val_bound (n : nat) (X : num n) : inf (exp base n) :=
    exist (fun p : nat => p < exp base n) (Val n X) (upper_bound n X).

  Lemma comp_dif :
   forall (n : nat) (x y : digit) (X Y : num n),
   val x < val y -> Val (S n) (Cons n x X) < Val (S n) (Cons n y Y).
  simpl in |- *; intros.
  elim H.
  apply same_quotient_order; auto; apply upper_bound.
  intros; apply same_quotient_order; auto; apply upper_bound.
  Qed.

  Lemma comp_eq_most :
   forall (n : nat) (x y : digit) (X Y : num n),
   val x = val y ->
   Val n X < Val n Y -> Val (S n) (Cons n x X) < Val (S n) (Cons n y Y).
  intros n x y X Y e H.
  simpl in |- *.
  rewrite e.
  apply plus_lt_compat_l; auto.
  Qed.

  Lemma com_eq :
   forall (n : nat) (x y : digit) (X Y : num n),
   val x = val y ->
   Val n X = Val n Y -> Val (S n) (Cons n x X) = Val (S n) (Cons n y Y).
  simpl in |- *.
  intros n x y X Y He HE.
  rewrite He; rewrite HE; auto.
  Qed.

End Numerals.


Require Import List.
Import ListNotations.

Inductive alpha := M | I | U.

Definition word := list alpha.

Inductive lang : word -> Prop :=
| axiom : lang [M;I]
| rule1 x : lang (x ++ [I]) -> lang (x ++ [I;U])
| rule2 x : lang ([M] ++ x) -> lang ([M] ++ x ++ x)
| rule3 x y : lang (x ++ [I;I;I] ++ y) -> lang (x ++ [U] ++ y)
| rule4 x y : lang (x ++ [U;U] ++ y) -> lang (x ++ y).

Check List.hd.
Check List.hd_error.

Lemma startM (w:word) : lang w -> (List.hd_error w) = Some M.
Proof.
  intro.
  induction H; trivial.
  unfold List.hd_error.
  destruct x; simpl in *; trivial.
  - destruct x; simpl in *.
    + discriminate.
    + apply IHlang.
  - destruct x; simpl in *.
    + discriminate.
    + apply IHlang.
Qed.

Lemma startMexists (w:word): lang w -> exists t, w=M::t.
Proof.
  intro.
  apply startM in H.
  destruct w.
  unfold hd_error in *.
  discriminate.
  simpl in *.
  injection H as ->.
  exists w.
  trivial.
Qed.

Inductive Z3 := Z0 | Z1 | Z2.

Definition succ (n:Z3) :=
  match n with
  |Z0 => Z1
  |Z1 => Z2
  |Z2 => Z0
  end
.

Definition add (x y:Z3) :=
  match x with
  |Z0 => y
  |Z1 => succ y
  |Z2 => succ (succ y)
  end
.

Lemma add_comm (x y:Z3) : add x y = add y x.
Proof.
  destruct x,y; simpl; trivial.
Qed.

Lemma add_assoc (x y z:Z3) : add x (add y z) = add (add x y) z.
Proof.
  destruct x,y,z; simpl; trivial.
Qed.

Lemma add_Z0 (x:Z3) : add x Z0 = x.
Proof.
  destruct x; simpl; trivial.
Qed.

Lemma notZ0 (z:Z3) : z<>Z0 -> add z z <> Z0.
Proof.
  destruct z; simpl; trivial; discriminate.
Qed.

Fixpoint occurI3 (w:word) :=
  match w with
  |[] => Z0
  |I::t => add Z1 (occurI3 t)
  |_::t => occurI3 t
  end
.

Lemma occurI3Nil : occurI3 [] = Z0.
Proof.
  unfold occurI3.
  trivial.
Qed.

Lemma add_succ (x y:Z3) : succ (add x y) = add (succ x) y.
Proof.
  destruct x,y; trivial.
Qed.

Lemma conc_occurI3 (v w:word) : occurI3 (v ++ w) = add (occurI3 v) (occurI3 w).
Proof.
  destruct v,w; simpl; trivial;destruct a; simpl.
Qed.

Lemma distr_occurI3 (v w:word) : occurI3 (v ++ w) = add (occurI3 v) (occurI3 w).
Proof.
  induction v; simpl.
  - trivial.
  - destruct a.
    + apply IHv.
    + rewrite <- add_succ.
    rewrite occurI3Nil. rewrite add_Z0.


From Hammer Require Import Hammer.

Require Export Relation_Definitions.
From ZornsLemma Require Import Relation_Definitions_Implicit.
Require Import Classical.
Require Import Arith.

Record DirectedSet := {
DS_set : Type;
DS_ord : relation DS_set;
DS_ord_cond : preorder DS_ord;
DS_join_cond : forall i j:DS_set, exists k:DS_set,
DS_ord i k /\ DS_ord j k
}.

Arguments DS_ord {d}.
Arguments DS_ord_cond {d}.
Arguments DS_join_cond {d}.

Section for_large.

Variable I : DirectedSet.

Definition eventually (P : DS_set I -> Prop) : Prop :=
exists i:DS_set I, forall j:DS_set I,
DS_ord i j -> P j.

Lemma eventually_and: forall (P Q: DS_set I -> Prop),
eventually P -> eventually Q ->
eventually (fun i:DS_set I => P i /\ Q i).
Proof. hammer_hook "DirectedSets" "DirectedSets.eventually_and".
intros.
destruct H.
destruct H0.
destruct (DS_join_cond x x0) as [? [? ?]].
exists x1.
intros; split.
apply H.
apply preord_trans with x1; trivial.
apply DS_ord_cond.
apply H0.
apply preord_trans with x1; trivial.
apply DS_ord_cond.
Qed.

Lemma eventually_impl_base: forall (P Q: DS_set I -> Prop),
(forall i:DS_set I, P i -> Q i) ->
eventually P -> eventually Q.
Proof. hammer_hook "DirectedSets" "DirectedSets.eventually_impl_base".
intros.
destruct H0.
exists x.
intros.
auto.
Qed.

Lemma eventually_impl: forall (P Q: DS_set I -> Prop),
eventually P -> eventually (fun i:DS_set I => P i -> Q i) ->
eventually Q.
Proof. hammer_hook "DirectedSets" "DirectedSets.eventually_impl".
intros.
apply eventually_impl_base with (P := fun (i:DS_set I) =>
P i /\ (P i -> Q i)).
tauto.
apply eventually_and; assumption.
Qed.

Definition exists_arbitrarily_large (P: DS_set I -> Prop) :=
forall i:DS_set I, exists j:DS_set I,
DS_ord i j /\ P j.

Lemma not_eal_eventually_not: forall (P: DS_set I -> Prop),
~ exists_arbitrarily_large P ->
eventually (fun i:DS_set I => ~ P i).
Proof. hammer_hook "DirectedSets" "DirectedSets.not_eal_eventually_not".
intros.
apply not_all_ex_not in H.
destruct H as [i].
exists i.
intros.
intro.
contradiction H.
exists j; split; trivial.
Qed.

Lemma not_eventually_eal_not: forall (P: DS_set I -> Prop),
~ eventually P ->
exists_arbitrarily_large (fun i:DS_set I => ~ P i).
Proof. hammer_hook "DirectedSets" "DirectedSets.not_eventually_eal_not".
intros.
red; intros.
apply NNPP; intro.
contradiction H.
exists i.
intros.
apply NNPP; intro.
contradiction H0.
exists j; split; trivial.
Qed.

End for_large.

Arguments eventually {I}.
Arguments eventually_and {I}.
Arguments eventually_impl_base {I}.
Arguments eventually_impl {I}.
Arguments exists_arbitrarily_large {I}.
Arguments not_eal_eventually_not {I}.
Arguments not_eventually_eal_not {I}.

Notation "'for' 'large' i : I , p" :=
(eventually (fun i:I => p))
(at level 200, i ident, right associativity).

Notation "'exists' 'arbitrarily' 'large' i : I , p" :=
(exists_arbitrarily_large (fun i:I => p))
(at level 200, i ident, right associativity).

Section nat_DS.

Definition nat_DS : DirectedSet.
refine (Build_DirectedSet nat le _ _).
constructor; red; intros; auto with arith.
apply le_trans with y; assumption.
intros.
case (lt_eq_lt_dec i j).
exists j.
destruct s; auto with arith.
destruct e; auto with arith.
exists i; auto with arith.
Defined.

End nat_DS.


(** Sequences of transitions. *)

Require Import Classical.

Set Implicit Arguments.

Section SEQUENCES.

Variable A: Type.
Variable R: A -> A -> Prop.

(** Zero, one or several transitions: reflexive, transitive closure of [R]. *)

Inductive star: A -> A -> Prop :=
  | star_refl: forall a,
      star a a
  | star_step: forall a b c,
      R a b -> star b c -> star a c.

Lemma star_one:
  forall (a b: A), R a b -> star a b.
Proof.
  intros. econstructor; eauto. constructor.
Qed.

Lemma star_trans:
  forall (a b: A), star a b -> forall c, star b c -> star a c.
Proof.
  induction 1; intros. auto. econstructor; eauto.
Qed.

(** One or several transitions: transitive closure of [R]. *)

Inductive plus: A -> A -> Prop :=
  | plus_left: forall a b c,
      R a b -> star b c -> plus a c.

Lemma plus_one:
  forall a b, R a b -> plus a b.
Proof.
  intros. apply plus_left with b. auto. apply star_refl.
Qed.

Lemma star_plus:
  forall a b c, star a b -> plus b c -> plus a c.
Proof.
  intros. inversion H0. inversion H. econstructor; eauto.
  econstructor; eauto. eapply star_trans; eauto. econstructor; eauto.
Qed.

Lemma plus_right:
  forall a b c, star a b -> R b c -> plus a c.
Proof.
  intros. eapply star_plus; eauto. apply plus_one; auto.
Qed.

(** Infinitely many transitions. *)

CoInductive infseq: A -> Prop :=
  | infseq_step: forall a b,
      R a b -> infseq b -> infseq a.

(** Coinduction principles to show the existence of infinite sequences. *)

Lemma infseq_coinduction_principle:
  forall (X: A -> Prop),
  (forall a, X a -> exists b, R a b /\ X b) ->
  forall a, X a -> infseq a.
Proof.
  intros X P. cofix COINDHYP; intros.
  destruct (P a H) as [b [U V]]. apply infseq_step with b; auto.
Qed.

Lemma infseq_coinduction_principle_2:
  forall (X: A -> Prop),
  (forall a, X a -> exists b, plus a b /\ X b) ->
  forall a, X a -> infseq a.
Proof.
  intros.
  apply infseq_coinduction_principle with
    (X := fun a => exists b, star a b /\ X b).
  intros.
  destruct H1 as [b [STAR Xb]]. inversion STAR; subst.
  destruct (H b Xb) as [c [PLUS Xc]]. inversion PLUS; subst.
  exists b0; split. auto. exists c; auto.
  exists b0; split. auto. exists b; auto.

  exists a; split. apply star_refl. auto.
Qed.

(** An example using [infseq_coinduction_principle]. *)

Lemma infseq_alternate_characterization:
  forall a,
  (forall b, star a b -> exists c, R b c) ->
  infseq a.
Proof.
  apply infseq_coinduction_principle.
  intros. destruct (H a) as [b Rb]. constructor.
  exists b; split; auto.
  intros. apply H. econstructor; eauto.
Qed.

(** A sequence is either infinite or stops on an irreducible term. *)

Definition irred (a: A) : Prop := forall b, ~(R a b).

Lemma infseq_or_finseq:
  forall a, infseq a \/ exists b, star a b /\ irred b.
Proof.
  intros.
  destruct (classic (forall b, star a b -> exists c, R b c)).
  left. apply infseq_alternate_characterization; auto.
  right.
  generalize (not_all_ex_not _ _ H). intros [b P].
  generalize (imply_to_and _ _ P). intros [U V].
  exists b; split. auto.
  red; intros; red; intros. elim V. exists b0; auto.
Qed.

(** Additional properties for deterministic transition relations. *)

Hypothesis R_determ:
  forall a b c, R a b -> R a c -> b = c.

(** Uniqueness of transition sequences. *)

Lemma star_star_inv:
  forall a b, star a b -> forall c, star a c -> star b c \/ star c b.
Proof.
  induction 1; intros.
  auto.
  inversion H1; subst. right. eapply star_step; eauto.
  assert (b = b0). eapply R_determ; eauto. subst b0.
  apply IHstar; auto.
Qed.

Lemma finseq_unique:
  forall a b b',
  star a b -> irred b ->
  star a b' -> irred b' ->
  b = b'.
Proof.
  intros. destruct (star_star_inv H H1).
  inversion H3; subst. auto. elim (H0 _ H4).
  inversion H3; subst. auto. elim (H2 _ H4).
Qed.

Lemma infseq_star_inv:
  forall a b, star a b -> infseq a -> infseq b.
Proof.
  induction 1; intros. auto.
  inversion H1; subst. assert (b = b0). eapply R_determ; eauto. subst b0.
  apply IHstar. auto.
Qed.

Lemma infseq_finseq_excl:
  forall a b,
  star a b -> irred b -> infseq a -> False.
Proof.
  intros.
  assert (infseq b). eapply infseq_star_inv; eauto.
  inversion H2. elim (H0 b0); auto.
Qed.

End SEQUENCES.


Require Export D.

(** 3 stars, advanced (beautiful__gorgeous)  *)

Lemma plus_assoc: forall a b c,
  (a + b) + c = a + (b + c).
Proof. intros. induction a.
  Case "a = 0". reflexivity.
  Case "a = S a". simpl. rewrite -> IHa. reflexivity. Qed.

Theorem gorgeous_sum : forall n m,
  gorgeous n -> gorgeous m -> gorgeous (n + m).
Proof.
  intros n m Hn Hm. induction Hn.
  Case "n = 0". simpl. apply Hm.
  Case "n = 3 + n0". rewrite -> plus_assoc. apply g_plus3. apply IHHn.
  Case "n = 5 + n0". rewrite -> plus_assoc. apply g_plus5. apply IHHn.
  Qed.

Theorem beautiful__gorgeous : forall n, beautiful n -> gorgeous n.
Proof.
  intros n Hb. induction Hb.
  Case "n = 0". apply g_0.
  Case "n = 3". apply g_plus3. apply g_0.
  Case "n = 5". apply g_plus5. apply g_0.
  Case "n = n + m". apply gorgeous_sum.  apply IHHb1. apply IHHb2.
  Qed.


Require Import Nsize.
Require Import Nsplit.
Require Import Nshift.
Require Import Nminus.
Require Import NArith.
Require Import Div2.
Require Import Max.
Require Import NArithRing.
Require Import Compare.

Tactic Notation "ringreplace" constr (a) "with" constr (b) :=
replace a with b ; [idtac | solve [ ring ]].

Local Open Scope N_scope.

Definition KaratsubaStop : nat := 40%nat.

Fixpoint KaratsubaMultF (i:nat) (a b:N) {struct i} : N :=
 let (a', b') := match (Ncompare a b) with
                 | Lt => (b, a)
                 | _ => (a, b)
                 end in
 let n:=(div2 (Nsize a')) in
 match i, le KaratsubaStop n with
 | (S i'), true => let (a1,a2):= NsplitAt n a' in
                   let (b1,b2) := NsplitAt n b' in
                   let x := KaratsubaMultF i' a2 b2 in
                   let y := KaratsubaMultF i' a1 b1 in
                   let z := KaratsubaMultF i' (Nplus a1 a2) (Nplus b1 b2)
                   in x + (Nshift n (Nminus z (y+x))) + (Nshift (2*n) y)
 | _, _ => (Nmult a' b')
end.

Definition KaratsubaMult a b := KaratsubaMultF (max (Nsize a) (Nsize b)) a b.

Lemma KaratsubaMultCorrect1 : forall (a b c d:N),
(Nminus ((a + b) * (c + d)) (a * c + b * d))=(b*c + a*d).
Proof.
intros.
ringreplace ((a + b) * (c + d)) with ((a*c + b*d) + (b * c + a * d)).
apply Nminus_plus.
Qed.

Theorem KaratsubaMultCorrect : forall a b, KaratsubaMult a b = a * b.
Proof.
intros a b.
unfold KaratsubaMult.
generalize (max (Nsize a) (Nsize b)).
intros i.
generalize a b.
clear a b.
induction i; intros a b.
simpl.
destruct (a ?= b); ring.
Opaque KaratsubaStop.
simpl.
set (p:=match a ?= b with
     | Eq => (a, b)
     | Lt => (b, a)
     | Gt => (a, b)
     end).
replace (a*b) with ((fst p)*(snd p)).
destruct p.
simpl.
Transparent KaratsubaStop.
destruct (le KaratsubaStop (div2 (Nsize n))).
transitivity ((let (a0, b0) := NsplitAt (div2 (Nsize n)) n in
 b0 + Nshift (div2 (Nsize n)) a0) *
(let (a0, b0) := NsplitAt (div2 (Nsize n)) n0 in
 b0 + Nshift (div2 (Nsize n)) a0)).
destruct (NsplitAt (div2 (Nsize n)) n).
destruct (NsplitAt (div2 (Nsize n)) n0).
repeat rewrite IHi.
rewrite KaratsubaMultCorrect1.
autorewrite with NshiftExpand.
change (Npos (Pshift.Pow2 0)) with 1.
ring.
rewrite (NsplitAtSum (div2 (Nsize n)) n).
rewrite (NsplitAtSum (div2 (Nsize n)) n0).
reflexivity.
reflexivity.
destruct (a?=b); try reflexivity.
apply Nmult_comm.
Qed.


(** * MoreCoq: More About Coq *)

Require Export Poly.

(** This chapter introduces several more Coq tactics that,
    together, allow us to prove many more theorems about the
    functional programs we are writing. *)

(* ###################################################### *)
(** * The [apply] Tactic *)

(** We often encounter situations where the goal to be proved is
    exactly the same as some hypothesis in the context or some
    previously proved lemma. *)

Theorem silly1 : forall (n m o p : nat),
     n = m  ->
     [n;o] = [n;p] ->
     [n;o] = [m;p].
Proof.
  intros n m o p eq1 eq2.
  rewrite <- eq1.
  (* At this point, we could finish with
     "[rewrite -> eq2. reflexivity.]" as we have
     done several times above. But we can achieve the
     same effect in a single step by using the
     [apply] tactic instead: *)
  apply eq2.  Qed.

(** The [apply] tactic also works with _conditional_ hypotheses
    and lemmas: if the statement being applied is an implication, then
    the premises of this implication will be added to the list of
    subgoals needing to be proved. *)

Theorem silly2 : forall (n m o p : nat),
     n = m  ->
     (forall (q r : nat), q = r -> [q;o] = [r;p]) ->
     [n;o] = [m;p].
Proof.
  intros n m o p eq1 eq2.
  apply eq2. apply eq1.  Qed.

(** You may find it instructive to experiment with this proof
    and see if there is a way to complete it using just [rewrite]
    instead of [apply]. *)

(** Typically, when we use [apply H], the statement [H] will
    begin with a [forall] binding some _universal variables_.  When
    Coq matches the current goal against the conclusion of [H], it
    will try to find appropriate values for these variables.  For
    example, when we do [apply eq2] in the following proof, the
    universal variable [q] in [eq2] gets instantiated with [n] and [r]
    gets instantiated with [m]. *)

Theorem silly2a : forall (n m : nat),
     (n,n) = (m,m)  ->
     (forall (q r : nat), (q,q) = (r,r) -> [q] = [r]) ->
     [n] = [m].
Proof.
  intros n m eq1 eq2.
  apply eq2. apply eq1.  Qed.

(** **** Exercise: 2 stars, optional (silly_ex) *)
(** Complete the following proof without using [simpl]. *)

Theorem silly_ex :
     (forall n, evenb n = true -> oddb (S n) = true) ->
     evenb 3 = true ->
     oddb 4 = true.
Proof.
  intros. apply H. apply H0. Qed.

(** To use the [apply] tactic, the (conclusion of the) fact
    being applied must match the goal _exactly_ -- for example, [apply]
    will not work if the left and right sides of the equality are
    swapped. *)

Theorem silly3_firsttry : forall (n : nat),
     true = beq_nat n 5  ->
     beq_nat (S (S n)) 7 = true.
Proof.
  intros n H.
  simpl.
  (* Here we cannot use [apply] directly *)
Abort.

(** In this case we can use the [symmetry] tactic, which switches the
    left and right sides of an equality in the goal. *)

Theorem silly3 : forall (n : nat),
     true = beq_nat n 5  ->
     beq_nat (S (S n)) 7 = true.
Proof.
  intros n H.
  symmetry.
  simpl. (* Actually, this [simpl] is unnecessary, since
            [apply] will do a [simpl] step first. *)
  apply H.  Qed.

(** **** Exercise: 3 stars (apply_exercise1) *)
(** Hint: you can use [apply] with previously defined lemmas, not
    just hypotheses in the context.  Remember that [SearchAbout] is
    your friend. *)

Theorem rev_exercise1 : forall (l l' : list nat),
     l = rev l' ->
     l' = rev l.
Proof.
  intros. rewrite -> H.
  symmetry. apply rev_involutive. Qed.

(** **** Exercise: 1 star, optional (apply_rewrite) *)
(** Briefly explain the difference between the tactics [apply] and
    [rewrite].  Are there situations where both can usefully be
    applied?

*)
(** [] *)

(* ###################################################### *)
(** * The [apply ... with ...] Tactic *)

(** The following silly example uses two rewrites in a row to
    get from [[a,b]] to [[e,f]]. *)

Example trans_eq_example : forall (a b c d e f : nat),
     [a;b] = [c;d] ->
     [c;d] = [e;f] ->
     [a;b] = [e;f].
Proof.
  intros a b c d e f eq1 eq2.
  rewrite -> eq1. rewrite -> eq2. reflexivity.  Qed.

(** Since this is a common pattern, we might
    abstract it out as a lemma recording once and for all
    the fact that equality is transitive. *)

Theorem trans_eq : forall (X:Type) (n m o : X),
  n = m -> m = o -> n = o.
Proof.
  intros X n m o eq1 eq2. rewrite -> eq1. rewrite -> eq2.
  reflexivity.  Qed.

(** Now, we should be able to use [trans_eq] to
    prove the above example.  However, to do this we need
    a slight refinement of the [apply] tactic. *)

Example trans_eq_example' : forall (a b c d e f : nat),
     [a;b] = [c;d] ->
     [c;d] = [e;f] ->
     [a;b] = [e;f].
Proof.
  intros a b c d e f eq1 eq2.
  (* If we simply tell Coq [apply trans_eq] at this point,
     it can tell (by matching the goal against the
     conclusion of the lemma) that it should instantiate [X]
     with [[nat]], [n] with [[a,b]], and [o] with [[e,f]].
     However, the matching process doesn't determine an
     instantiation for [m]: we have to supply one explicitly
     by adding [with (m:=[c,d])] to the invocation of
     [apply]. *)
  apply trans_eq with (m:=[c;d]). apply eq1. apply eq2.   Qed.

(**  Actually, we usually don't have to include the name [m]
    in the [with] clause; Coq is often smart enough to
    figure out which instantiation we're giving. We could
    instead write: [apply trans_eq with [c,d]]. *)

(** **** Exercise: 3 stars, optional (apply_with_exercise) *)
Example trans_eq_exercise : forall (n m o p : nat),
     m = (minustwo o) ->
     (n + p) = m ->
     (n + p) = (minustwo o).
Proof.
  intros. eapply trans_eq. apply H0. apply H. Qed.

(* ###################################################### *)
(** * The [inversion] tactic *)

(** Recall the definition of natural numbers:
     Inductive nat : Type :=
       | O : nat
       | S : nat -> nat.
    It is clear from this definition that every number has one of two
    forms: either it is the constructor [O] or it is built by applying
    the constructor [S] to another number.  But there is more here than
    meets the eye: implicit in the definition (and in our informal
    understanding of how datatype declarations work in other
    programming languages) are two other facts:

    - The constructor [S] is _injective_.  That is, the only way we can
      have [S n = S m] is if [n = m].

    - The constructors [O] and [S] are _disjoint_.  That is, [O] is not
      equal to [S n] for any [n]. *)

(** Similar principles apply to all inductively defined types: all
    constructors are injective, and the values built from distinct
    constructors are never equal.  For lists, the [cons] constructor is
    injective and [nil] is different from every non-empty list.  For
    booleans, [true] and [false] are unequal.  (Since neither [true]
    nor [false] take any arguments, their injectivity is not an issue.) *)

(** Coq provides a tactic called [inversion] that allows us to exploit
    these principles in proofs.

    The [inversion] tactic is used like this.  Suppose [H] is a
    hypothesis in the context (or a previously proven lemma) of the
    form
      c a1 a2 ... an = d b1 b2 ... bm
    for some constructors [c] and [d] and arguments [a1 ... an] and
    [b1 ... bm].  Then [inversion H] instructs Coq to "invert" this
    equality to extract the information it contains about these terms:

    - If [c] and [d] are the same constructor, then we know, by the
      injectivity of this constructor, that [a1 = b1], [a2 = b2],
      etc.; [inversion H] adds these facts to the context, and tries
      to use them to rewrite the goal.

    - If [c] and [d] are different constructors, then the hypothesis
      [H] is contradictory.  That is, a false assumption has crept
      into the context, and this means that any goal whatsoever is
      provable!  In this case, [inversion H] marks the current goal as
      completed and pops it off the goal stack. *)

(** The [inversion] tactic is probably easier to understand by
    seeing it in action than from general descriptions like the above.
    Below you will find example theorems that demonstrate the use of
    [inversion] and exercises to test your understanding. *)

Theorem eq_add_S : forall (n m : nat),
     S n = S m ->
     n = m.
Proof.
  intros n m eq. inversion eq. reflexivity.  Qed.

Theorem silly4 : forall (n m : nat),
     [n] = [m] ->
     n = m.
Proof.
  intros n o eq. inversion eq. reflexivity.  Qed.

(** As a convenience, the [inversion] tactic can also
    destruct equalities between complex values, binding
    multiple variables as it goes. *)

Theorem silly5 : forall (n m o : nat),
     [n;m] = [o;o] ->
     [n] = [m].
Proof.
  intros n m o eq. inversion eq. reflexivity. Qed.

(** **** Exercise: 1 star (sillyex1) *)
Example sillyex1 : forall (X : Type) (x y z : X) (l j : list X),
     x :: y :: l = z :: j ->
     y :: l = x :: j ->
     x = y.
Proof.
  intros. inversion H0. reflexivity. Qed.

Theorem silly6 : forall (n : nat),
     S n = O ->
     2 + 2 = 5.
Proof.
  intros n contra. inversion contra. Qed.

Theorem silly7 : forall (n m : nat),
     false = true ->
     [n] = [m].
Proof.
  intros n m contra. inversion contra.  Qed.

(** **** Exercise: 1 star (sillyex2) *)
Example sillyex2 : forall (X : Type) (x y z : X) (l j : list X),
     x :: y :: l = [] ->
     y :: l = z :: j ->
     x = z.
Proof.
  intros. inversion H. Qed.

(** While the injectivity of constructors allows us to reason
    [forall (n m : nat), S n = S m -> n = m], the reverse direction of
    the implication is an instance of a more general fact about
    constructors and functions, which we will often find useful: *)

Theorem f_equal : forall (A B : Type) (f: A -> B) (x y: A),
    x = y -> f x = f y.
Proof. intros A B f x y eq. rewrite eq.  reflexivity.  Qed.

(** Here's another illustration of [inversion].  This is a slightly
    roundabout way of stating a fact that we have already proved
    above.  The extra equalities force us to do a little more
    equational reasoning and exercise some of the tactics we've seen
    recently. *)

Theorem length_snoc' : forall (X : Type) (v : X)
                              (l : list X) (n : nat),
     length l = n ->
     length (snoc l v) = S n.
Proof.
  intros X v l. induction l as [| v' l'].
  Case "l = []". intros n eq. rewrite <- eq. reflexivity.
  Case "l = v' :: l'". intros n eq. simpl. destruct n as [| n'].
    SCase "n = 0". inversion eq.
    SCase "n = S n'".
      apply f_equal. apply IHl'. inversion eq. reflexivity. Qed.

(** **** Exercise: 2 stars, optional (practice) *)
(** A couple more nontrivial but not-too-complicated proofs to work
    together in class, or for you to work as exercises.  They may
    involve applying lemmas from earlier lectures or homeworks. *)

Theorem beq_nat_0_l : forall n,
   beq_nat 0 n = true -> n = 0.
Proof.
  intros. destruct n. reflexivity. simpl in H. inversion H. Qed.

Theorem beq_nat_0_r : forall n,
   beq_nat n 0 = true -> n = 0.
Proof.
  intros.
  destruct n. reflexivity. inversion H. Qed.

(* ###################################################### *)
(** * Using Tactics on Hypotheses *)

(** By default, most tactics work on the goal formula and leave
    the context unchanged.  However, most tactics also have a variant
    that performs a similar operation on a statement in the context.

    For example, the tactic [simpl in H] performs simplification in
    the hypothesis named [H] in the context. *)

Theorem S_inj : forall (n m : nat) (b : bool),
     beq_nat (S n) (S m) = b  ->
     beq_nat n m = b.
Proof.
  intros n m b H. simpl in H. apply H.  Qed.

(** Similarly, the tactic [apply L in H] matches some
    conditional statement [L] (of the form [L1 -> L2], say) against a
    hypothesis [H] in the context.  However, unlike ordinary
    [apply] (which rewrites a goal matching [L2] into a subgoal [L1]),
    [apply L in H] matches [H] against [L1] and, if successful,
    replaces it with [L2].

    In other words, [apply L in H] gives us a form of "forward
    reasoning" -- from [L1 -> L2] and a hypothesis matching [L1], it
    gives us a hypothesis matching [L2].  By contrast, [apply L] is
    "backward reasoning" -- it says that if we know [L1->L2] and we
    are trying to prove [L2], it suffices to prove [L1].

    -->deduction theorem

    Here is a variant of a proof from above, using forward reasoning
    throughout instead of backward reasoning. *)

Theorem silly3' : forall (n : nat),
  (beq_nat n 5 = true -> beq_nat (S (S n)) 7 = true) ->
     true = beq_nat n 5  ->
     true = beq_nat (S (S n)) 7.
Proof.
  intros n eq H.
  symmetry in H. apply eq in H. symmetry in H.
  apply H.  Qed.

(** Forward reasoning starts from what is _given_ (premises,
    previously proven theorems) and iteratively draws conclusions from
    them until the goal is reached.  Backward reasoning starts from
    the _goal_, and iteratively reasons about what would imply the
    goal, until premises or previously proven theorems are reached.
    If you've seen informal proofs before (for example, in a math or
    computer science class), they probably used forward reasoning.  In
    general, Coq tends to favor backward reasoning, but in some
    situations the forward style can be easier to use or to think
    about.  *)

(** **** Exercise: 3 stars (plus_n_n_injective) *)
(** Practice using "in" variants in this exercise. *)

Theorem plus_n_n_injective : forall n m,
     n + n = m + m ->
     n = m.
Proof.
  intros n. induction n as [|n'].
  simpl. destruct m. intros. reflexivity.
  intros. inversion H.
  simpl. intros. destruct m. inversion H.
  simpl in H. rewrite <- plus_n_Sm in H. rewrite <- plus_n_Sm in H.
  inversion H. apply IHn' in H1. apply f_equal with (f := S) in H1.
  apply H1.
  Qed.

(* ###################################################### *)
(** * Varying the Induction Hypothesis *)

(** Sometimes it is important to control the exact form of the
    induction hypothesis when carrying out inductive proofs in Coq.
    In particular, we need to be careful about which of the
    assumptions we move (using [intros]) from the goal to the context
    before invoking the [induction] tactic.  For example, suppose
    we want to show that the [double] function is injective -- i.e.,
    that it always maps different arguments to different results:
    Theorem double_injective: forall n m, double n = double m -> n = m.
    The way we _start_ this proof is a little bit delicate: if we
    begin it with
      intros n. induction n.
]]
    all is well.  But if we begin it with
      intros n m. induction n.
    we get stuck in the middle of the inductive case... *)

Theorem double_injective_FAILED : forall n m,
     double n = double m ->
     n = m.
Proof.
  intros n m. induction n as [| n'].
  Case "n = O". simpl. intros eq. destruct m as [| m'].
    SCase "m = O". reflexivity.
    SCase "m = S m'". inversion eq.
  Case "n = S n'". intros eq. destruct m as [| m'].
    SCase "m = O". inversion eq.
    SCase "m = S m'".  apply f_equal.
      (* Here we are stuck.  The induction hypothesis, [IHn'], does
         not give us [n' = m'] -- there is an extra [S] in the
         way -- so the goal is not provable. *)
      Abort.

(** What went wrong? *)

(** The problem is that, at the point we invoke the induction
    hypothesis, we have already introduced [m] into the context --
    intuitively, we have told Coq, "Let's consider some particular
    [n] and [m]..." and we now have to prove that, if [double n =
    double m] for _this particular_ [n] and [m], then [n = m].

    The next tactic, [induction n] says to Coq: We are going to show
    the goal by induction on [n].  That is, we are going to prove that
    the proposition

      - [P n]  =  "if [double n = double m], then [n = m]"

    holds for all [n] by showing

      - [P O]

         (i.e., "if [double O = double m] then [O = m]")

      - [P n -> P (S n)]

        (i.e., "if [double n = double m] then [n = m]" implies "if
        [double (S n) = double m] then [S n = m]").

    If we look closely at the second statement, it is saying something
    rather strange: it says that, for a _particular_ [m], if we know

      - "if [double n = double m] then [n = m]"

    then we can prove

       - "if [double (S n) = double m] then [S n = m]".

    To see why this is strange, let's think of a particular [m] --
    say, [5].  The statement is then saying that, if we know

      - [Q] = "if [double n = 10] then [n = 5]"

    then we can prove

      - [R] = "if [double (S n) = 10] then [S n = 5]".

    But knowing [Q] doesn't give us any help with proving [R]!  (If we
    tried to prove [R] from [Q], we would say something like "Suppose
    [double (S n) = 10]..." but then we'd be stuck: knowing that
    [double (S n)] is [10] tells us nothing about whether [double n]
    is [10], so [Q] is useless at this point.) *)

(** To summarize: Trying to carry out this proof by induction on [n]
    when [m] is already in the context doesn't work because we are
    trying to prove a relation involving _every_ [n] but just a
    _single_ [m]. *)

(** The good proof of [double_injective] leaves [m] in the goal
    statement at the point where the [induction] tactic is invoked on
    [n]: *)

Theorem double_injective : forall n m,
     double n = double m ->
     n = m.
Proof.
  intros n. induction n as [| n'].
  Case "n = O". simpl. intros m eq. destruct m as [| m'].
    SCase "m = O". reflexivity.
    SCase "m = S m'". inversion eq.
  Case "n = S n'".
    (* Notice that both the goal and the induction
       hypothesis have changed: the goal asks us to prove
       something more general (i.e., to prove the
       statement for _every_ [m]), but the IH is
       correspondingly more flexible, allowing us to
       choose any [m] we like when we apply the IH.  *)
    intros m eq.
    (* Now we choose a particular [m] and introduce the
       assumption that [double n = double m].  Since we
       are doing a case analysis on [n], we need a case
       analysis on [m] to keep the two "in sync." *)
    destruct m as [| m'].
    SCase "m = O".
      (* The 0 case is trivial *)
      inversion eq.
    SCase "m = S m'".
      apply f_equal.
      (* At this point, since we are in the second
         branch of the [destruct m], the [m'] mentioned
         in the context at this point is actually the
         predecessor of the one we started out talking
         about.  Since we are also in the [S] branch of
         the induction, this is perfect: if we
         instantiate the generic [m] in the IH with the
         [m'] that we are talking about right now (this
         instantiation is performed automatically by
         [apply]), then [IHn'] gives us exactly what we
         need to finish the proof. *)
      apply IHn'. inversion eq. reflexivity. Qed.

(** What this teaches us is that we need to be careful about using
    induction to try to prove something too specific: If we're proving
    a property of [n] and [m] by induction on [n], we may need to
    leave [m] generic. *)

(** The proof of this theorem has to be treated similarly: *)

(** **** Exercise: 2 stars (beq_nat_true) *)
Theorem beq_nat_true : forall n m,
    beq_nat n m = true -> n = m.
Proof.
  intros n. induction n as [|n'].
  intros. destruct m. reflexivity. inversion H.
  intros. destruct m. inversion H.
  apply IHn' in H. rewrite -> H. reflexivity.
  Qed.

(** **** Exercise: 2 stars, advanced (beq_nat_true_informal) *)
(** Give a careful informal proof of [beq_nat_true], being as explicit
    as possible about quantifiers. *)

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

(** The strategy of doing fewer [intros] before an [induction] doesn't
    always work directly; sometimes a little _rearrangement_ of
    quantified variables is needed.  Suppose, for example, that we
    wanted to prove [double_injective] by induction on [m] instead of
    [n]. *)

Theorem double_injective_take2_FAILED : forall n m,
     double n = double m ->
     n = m.
Proof.
  intros n m. induction m as [| m'].
  Case "m = O". simpl. intros eq. destruct n as [| n'].
    SCase "n = O". reflexivity.
    SCase "n = S n'". inversion eq.
  Case "m = S m'". intros eq. destruct n as [| n'].
    SCase "n = O". inversion eq.
    SCase "n = S n'".  apply f_equal.
        (* Stuck again here, just like before. *)
Abort.

(** The problem is that, to do induction on [m], we must first
    introduce [n].  (If we simply say [induction m] without
    introducing anything first, Coq will automatically introduce
    [n] for us!)   *)

(** What can we do about this?  One possibility is to rewrite the
    statement of the lemma so that [m] is quantified before [n].  This
    will work, but it's not nice: We don't want to have to mangle the
    statements of lemmas to fit the needs of a particular strategy for
    proving them -- we want to state them in the most clear and
    natural way. *)

(**  What we can do instead is to first introduce all the
    quantified variables and then _re-generalize_ one or more of
    them, taking them out of the context and putting them back at
    the beginning of the goal.  The [generalize dependent] tactic
    does this. *)

Theorem double_injective_take2 : forall n m,
     double n = double m ->
     n = m.
Proof.
  intros n m.
  (* [n] and [m] are both in the context *)
  generalize dependent n.
  (* Now [n] is back in the goal and we can do induction on
     [m] and get a sufficiently general IH. *)
  induction m as [| m'].
  Case "m = O". simpl. intros n eq. destruct n as [| n'].
    SCase "n = O". reflexivity.
    SCase "n = S n'". inversion eq.
  Case "m = S m'". intros n eq. destruct n as [| n'].
    SCase "n = O". inversion eq.
    SCase "n = S n'". apply f_equal.
      apply IHm'. inversion eq. reflexivity. Qed.

(** Let's look at an informal proof of this theorem.  Note that
    the proposition we prove by induction leaves [n] quantified,
    corresponding to the use of generalize dependent in our formal
    proof.

_Theorem_: For any nats [n] and [m], if [double n = double m], then
  [n = m].

_Proof_: Let [m] be a [nat]. We prove by induction on [m] that, for
  any [n], if [double n = double m] then [n = m].

  - First, suppose [m = 0], and suppose [n] is a number such
    that [double n = double m].  We must show that [n = 0].

    Since [m = 0], by the definition of [double] we have [double n =
    0].  There are two cases to consider for [n].  If [n = 0] we are
    done, since this is what we wanted to show.  Otherwise, if [n = S
    n'] for some [n'], we derive a contradiction: by the definition of
    [double] we would have [double n = S (S (double n'))], but this
    contradicts the assumption that [double n = 0].

  - Otherwise, suppose [m = S m'] and that [n] is again a number such
    that [double n = double m].  We must show that [n = S m'], with
    the induction hypothesis that for every number [s], if [double s =
    double m'] then [s = m'].

    By the fact that [m = S m'] and the definition of [double], we
    have [double n = S (S (double m'))].  There are two cases to
    consider for [n].

    If [n = 0], then by definition [double n = 0], a contradiction.
    Thus, we may assume that [n = S n'] for some [n'], and again by
    the definition of [double] we have [S (S (double n')) = S (S
    (double m'))], which implies by inversion that [double n' = double
    m'].

    Instantiating the induction hypothesis with [n'] thus allows us to
    conclude that [n' = m'], and it follows immediately that [S n' = S
    m'].  Since [S n' = n] and [S m' = m], this is just what we wanted
    to show. [] *)

(** **** Exercise: 3 stars (gen_dep_practice) *)

(** Prove this by induction on [l]. *)

Theorem index_after_last: forall (n : nat) (X : Type) (l : list X),
     length l = n ->
     index n l = None.
Proof.
  intros. generalize dependent n.
  induction l as [|hd tl]. simpl. intros. reflexivity.
  simpl. destruct n. simpl. intros. inversion H.
  simpl. intros. inversion H. rewrite -> H1. apply IHtl in H1.
  rewrite -> H1. reflexivity. Qed.

(** **** Exercise: 3 stars, advanced, optional (index_after_last_informal) *)
(** Write an informal proof corresponding to your Coq proof
    of [index_after_last]:

     _Theorem_: For all sets [X], lists [l : list X], and numbers
      [n], if [length l = n] then [index n l = None].

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

(** **** Exercise: 3 stars, optional (gen_dep_practice_more) *)
(** Prove this by induction on [l]. *)

Theorem length_snoc''' : forall (n : nat) (X : Type)
                              (v : X) (l : list X),
     length l = n ->
     length (snoc l v) = S n.
Proof.
  intros. generalize dependent n. induction l as [| h t].
  {intros. rewrite <- H. simpl. reflexivity. }
  {intros n eq. simpl. apply f_equal. destruct n. inversion eq.
   apply IHt. simpl in eq. inversion eq. reflexivity. }
Qed.

(** **** Exercise: 3 stars, optional (app_length_cons) *)
(** Prove this by induction on [l1], without using [app_length]. *)

Theorem app_length_cons : forall (X : Type) (l1 l2 : list X)
                                  (x : X) (n : nat),
     length (l1 ++ (x :: l2)) = n ->
     S (length (l1 ++ l2)) = n.
Proof.
  intros X l1 l2 x.
  induction l1 as [| hd tl].
  {simpl. intros. rewrite -> H. reflexivity. }
  {simpl. destruct n.
   {simpl. intros. inversion H. }
   {intros. inversion H. apply f_equal. apply IHtl in H1.
    inversion H. rewrite -> H1.  rewrite -> H2.
    reflexivity. }
     }
Qed.

(** **** Exercise: 4 stars, optional (app_length_twice) *)
(** Prove this by induction on [l], without using app_length. *)

Lemma app_length_twice_lemma : forall (X : Type) (l1 l2 :  list X) (x : X) (n : nat),
                                 length (l1 ++ l2) = n -> length(l1 ++ x :: l2) = S n.
Proof.
  intros X l1.
  induction l1 as [|hd tl].
  {intros. simpl in *. apply f_equal. apply H. }
  {intros. destruct n. inversion H.
   simpl in *. apply f_equal. apply IHtl. inversion H. reflexivity. }
  Qed.

Theorem app_length_twice : forall (X:Type) (n:nat) (l:list X),
     length l = n ->
     length (l ++ l) = n + n.
Proof.
  intros.
  generalize dependent n.
  induction l as [|hd tl].
  {intros. simpl in *. destruct n.
   {reflexivity. }
   {inversion H. }
  }
  {intros. simpl. destruct n.
   {inversion H. }
   {simpl in *. inversion H. apply f_equal.
    apply IHtl in H1. apply app_length_twice_lemma with (x := hd) in H1 .
    rewrite -> H1. rewrite <- plus_n_Sm. inversion H. reflexivity. }
  }
  Qed.

(* ###################################################### *)
(** * Using [destruct] on Compound Expressions *)

(** We have seen many examples where the [destruct] tactic is
    used to perform case analysis of the value of some variable.  But
    sometimes we need to reason by cases on the result of some
    _expression_.  We can also do this with [destruct].

    Here are some examples: *)

Definition sillyfun (n : nat) : bool :=
  if beq_nat n 3 then false
  else if beq_nat n 5 then false
  else false.

Theorem sillyfun_false : forall (n : nat),
  sillyfun n = false.
Proof.
  intros n. unfold sillyfun.
  destruct (beq_nat n 3).
    Case "beq_nat n 3 = true". reflexivity.
    Case "beq_nat n 3 = false". destruct (beq_nat n 5).
      SCase "beq_nat n 5 = true". reflexivity.
      SCase "beq_nat n 5 = false". reflexivity.  Qed.

(** After unfolding [sillyfun] in the above proof, we find that
    we are stuck on [if (beq_nat n 3) then ... else ...].  Well,
    either [n] is equal to [3] or it isn't, so we use [destruct
    (beq_nat n 3)] to let us reason about the two cases.

    In general, the [destruct] tactic can be used to perform case
    analysis of the results of arbitrary computations.  If [e] is an
    expression whose type is some inductively defined type [T], then,
    for each constructor [c] of [T], [destruct e] generates a subgoal
    in which all occurrences of [e] (in the goal and in the context)
    are replaced by [c].

*)

(** **** Exercise: 1 star (override_shadow) *)
Theorem override_shadow : forall (X:Type) x1 x2 k1 k2 (f : nat->X),
  (override (override f k1 x2) k1 x1) k2 = (override f k1 x1) k2.
Proof.
  intros.
  unfold override. destruct (beq_nat k1 k2).
  {reflexivity. }
  {reflexivity. }
Qed.

(** **** Exercise: 3 stars, optional (combine_split) *)
(** Complete the proof below *)

Theorem combine_split : forall X Y (l : list (X * Y)) l1 l2,
  split l = (l1, l2) ->
  combine l1 l2 = l.
Proof.
  intros X Y l. induction l as [|[h1 h2] t]. intros. simpl in *. inversion H. reflexivity.
  intros. inversion H. simpl. apply f_equal. apply IHt. destruct (split t).
  simpl. reflexivity.
  Qed.

(** Sometimes, doing a [destruct] on a compound expression (a
    non-variable) will erase information we need to complete a proof. *)
(** For example, suppose
    we define a function [sillyfun1] like this: *)

Definition sillyfun1 (n : nat) : bool :=
  if beq_nat n 3 then true
  else if beq_nat n 5 then true
  else false.

(** And suppose that we want to convince Coq of the rather
    obvious observation that [sillyfun1 n] yields [true] only when [n]
    is odd.  By analogy with the proofs we did with [sillyfun] above,
    it is natural to start the proof like this: *)

Theorem sillyfun1_odd_FAILED : forall (n : nat),
     sillyfun1 n = true ->
     oddb n = true.
Proof.
  intros n eq. unfold sillyfun1 in eq.
  destruct (beq_nat n 3).
  (* stuck... *)
Abort.

(** We get stuck at this point because the context does not
    contain enough information to prove the goal!  The problem is that
    the substitution peformed by [destruct] is too brutal -- it threw
    away every occurrence of [beq_nat n 3], but we need to keep some
    memory of this expression and how it was destructed, because we
    need to be able to reason that since, in this branch of the case
    analysis, [beq_nat n 3 = true], it must be that [n = 3], from
    which it follows that [n] is odd.

    What we would really like is to substitute away all existing
    occurences of [beq_nat n 3], but at the same time add an equation
    to the context that records which case we are in.  The [eqn:]
    qualifier allows us to introduce such an equation (with whatever
    name we choose). *)

Theorem sillyfun1_odd : forall (n : nat),
     sillyfun1 n = true ->
     oddb n = true.
Proof.
  intros n eq. unfold sillyfun1 in eq.
  destruct (beq_nat n 3) eqn:Heqe3.
  (* Now we have the same state as at the point where we got stuck
    above, except that the context contains an extra equality
    assumption, which is exactly what we need to make progress. *)
    Case "e3 = true". apply beq_nat_true in Heqe3.
      rewrite -> Heqe3. reflexivity.
    Case "e3 = false".
     (* When we come to the second equality test in the body of the
       function we are reasoning about, we can use [eqn:] again in the
       same way, allow us to finish the proof. *)
      destruct (beq_nat n 5) eqn:Heqe5.
        SCase "e5 = true".
          apply beq_nat_true in Heqe5.
          rewrite -> Heqe5. reflexivity.
        SCase "e5 = false". inversion eq.  Qed.

(** **** Exercise: 2 stars (destruct_eqn_practice) *)
Theorem bool_fn_applied_thrice :
  forall (f : bool -> bool) (b : bool),
  f (f (f b)) = f b.
Proof.
  intros.
  destruct (f b) eqn:eqFB.
  {destruct (f true) eqn:eqFT.
   {rewrite -> eqFT. reflexivity. }
   {destruct (f false) eqn :eqFF.
    {reflexivity. }
    {destruct b. rewrite -> eqFB in eqFT. inversion eqFT.
     rewrite -> eqFB in eqFF. inversion eqFF. }
   }
  }
  {destruct (f false) eqn : eqFF.
   {destruct (f true) eqn : eqFT.
    {destruct b. rewrite -> eqFB in eqFT. inversion eqFT.
     rewrite ->eqFB in eqFF. inversion eqFF. }
   {destruct b. rewrite -> eqFB in eqFT. reflexivity. reflexivity. }
   }
  {rewrite -> eqFF. reflexivity. }
  }
Qed.

(** **** Exercise: 2 stars (override_same) *)
Theorem override_same : forall (X:Type) x1 k1 k2 (f : nat->X),
  f k1 = x1 ->
  (override f k1 x1) k2 = f k2.
Proof.
  intros.
  unfold override. destruct (beq_nat k1 k2) eqn : H1.
  rewrite <- H. apply f_equal. apply beq_nat_true in H1.
  rewrite -> H1. reflexivity.
  reflexivity.
  Qed.

(* ################################################################## *)
(** * Review *)

(** We've now seen a bunch of Coq's fundamental tactics.  We'll
    introduce a few more as we go along through the coming lectures,
    and later in the course we'll introduce some more powerful
    _automation_ tactics that make Coq do more of the low-level work
    in many cases.  But basically we've got what we need to get work
    done.

    Here are the ones we've seen:

      - [intros]:
        move hypotheses/variables from goal to context

      - [reflexivity]:
        finish the proof (when the goal looks like [e = e])

      - [apply]:
        prove goal using a hypothesis, lemma, or constructor

      - [apply... in H]:
        apply a hypothesis, lemma, or constructor to a hypothesis in
        the context (forward reasoning)

      - [apply... with...]:
        explicitly specify values for variables that cannot be
        determined by pattern matching

      - [simpl]:
        simplify computations in the goal

      - [simpl in H]:
        ... or a hypothesis

      - [rewrite]:
        use an equality hypothesis (or lemma) to rewrite the goal

      - [rewrite ... in H]:
        ... or a hypothesis

      - [symmetry]:
        changes a goal of the form [t=u] into [u=t]

      - [symmetry in H]:
        changes a hypothesis of the form [t=u] into [u=t]

      - [unfold]:
        replace a defined constant by its right-hand side in the goal

      - [unfold... in H]:
        ... or a hypothesis

      - [destruct... as...]:
        case analysis on values of inductively defined types

      - [destruct... eqn:...]:
        specify the name of an equation to be added to the context,
        recording the result of the case analysis

      - [induction... as...]:
        induction on values of inductively defined types

      - [inversion]:
        reason by injectivity and distinctness of constructors

      - [assert (e) as H]:
        introduce a "local lemma" [e] and call it [H]

      - [generalize dependent x]:
        move the variable [x] (and anything else that depends on it)
        from the context back to an explicit hypothesis in the goal
        formula
*)

(* ###################################################### *)
(** * Additional Exercises *)

Theorem XEqX : forall n, beq_nat n n = true.
Proof.
  intros. induction n.
  reflexivity. simpl. rewrite -> IHn.
  reflexivity. Qed.

(** **** Exercise: 3 stars (beq_nat_sym) *)
Theorem beq_nat_sym : forall (n m : nat),
  beq_nat n m = beq_nat m n.
Proof.
  intros n.
  induction n.
  intros. destruct m. reflexivity. simpl. reflexivity.
  intros. destruct m. simpl. reflexivity. simpl.
  apply IHn. Qed.

(** **** Exercise: 3 stars, advanced, optional (beq_nat_sym_informal) *)
(** Give an informal proof of this lemma that corresponds to your
    formal proof above:

   Theorem: For any [nat]s [n] [m], [beq_nat n m = beq_nat m n].

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

(** **** Exercise: 3 stars, optional (beq_nat_trans) *)
Theorem beq_nat_trans : forall n m p,
  beq_nat n m = true ->
  beq_nat m p = true ->
  beq_nat n p = true.
Proof.
  intros. apply beq_nat_true in H. apply beq_nat_true in H0.
  rewrite -> H. rewrite -> H0. apply XEqX.
  Qed.

(** **** Exercise: 3 stars, advanced (split_combine) *)
(** We have just proven that for all lists of pairs, [combine] is the
    inverse of [split].  How would you formalize the statement that
    [split] is the inverse of [combine]?

    Complete the definition of [split_combine_statement] below with a
    property that states that [split] is the inverse of
    [combine]. Then, prove that the property holds. (Be sure to leave
    your induction hypothesis general by not doing [intros] on more
    things than necessary.  Hint: what property do you need of [l1]
    and [l2] for [split] [combine l1 l2 = (l1,l2)] to be true?)  *)

Definition split_combine_statement : Prop :=
  forall (X Y : Type) (l1 : list X) (l2 : list Y) (l : list (X * Y)),
    length l1 = length l2 -> combine l1 l2 = l -> split l = (l1, l2).

Theorem split_combine : split_combine_statement.
Proof.
  intros X Y. induction l1.
  simpl. intros. destruct l2.
  rewrite <- H0.  reflexivity. inversion H.
  Case "l1 = cons hd tl".
  intros. destruct l2. inversion H.
  inversion H. simpl in H0. destruct l. inversion H0.
  inversion H0.  apply IHl1 with (l := l) in H2.
  rewrite -> H4. simpl. rewrite -> H2. reflexivity.
  apply H4.
  Qed.

(** **** Exercise: 3 stars (override_permute) *)
Theorem override_permute : forall (X:Type) x1 x2 k1 k2 k3 (f : nat->X),
  beq_nat k2 k1 = false ->
  (override (override f k2 x2) k1 x1) k3 = (override (override f k1 x1) k2 x2) k3.
Proof.
  intros. unfold override.
  rewrite -> beq_nat_sym in H. Admitted.

(** **** Exercise: 3 stars, advanced (filter_exercise) *)
(** This one is a bit challenging.  Pay attention to the form of your IH. *)

Theorem filter_exercise : forall (X : Type) (test : X -> bool)
                             (x : X) (l lf : list X),
     filter test l = x :: lf ->
     test x = true.
Proof.
  intros. generalize dependent x. generalize dependent lf.
  induction l.
  intros. simpl in H. inversion H.
  intros. simpl in *. destruct (test x) eqn : H1. inversion H.
  rewrite <- H2. apply H1. apply IHl with (lf := lf). apply H.
  Qed.

(** **** Exercise: 4 stars, advanced (forall_exists_challenge) *)
(** Define two recursive [Fixpoints], [forallb] and [existsb].  The
    first checks whether every element in a list satisfies a given
    predicate:
      forallb oddb [1;3;5;7;9] = true

      forallb negb [false;false] = true

      forallb evenb [0;2;4;5] = false

      forallb (beq_nat 5) [] = true
    The second checks whether there exists an element in the list that
    satisfies a given predicate:
      existsb (beq_nat 5) [0;2;3;6] = false

      existsb (andb true) [true;true;false] = true

      existsb oddb [1;0;0;0;0;3] = true

      existsb evenb [] = false
    Next, define a _nonrecursive_ version of [existsb] -- call it
    [existsb'] -- using [forallb] and [negb].

    Prove that [existsb'] and [existsb] have the same behavior.
*)

Fixpoint forallb {X : Type} (p : X -> bool) (l : list X) : bool :=
  match l with
      |nil => true
      |x::xs => if p x
                then forallb p xs
                else false
  end.

Fixpoint existsb {X : Type} (p : X -> bool) (l : list X) : bool :=
  match l with
      |nil => false
      |x::xs => if p x
                then true
                else existsb p xs
  end.

Definition existsb' {X:Type} (p:X -> bool) (l : list X) : bool :=
  negb (forallb (fun x => negb(p x)) l).

Theorem ExistsBCorrect : forall (X : Type) (p : X -> bool) (l : list X),
                           existsb p l = existsb' p l.
Proof.
  intros.
  unfold existsb'.
  induction l. simpl. reflexivity.
  simpl. destruct (p x) eqn : H. simpl. reflexivity.
  simpl. apply IHl. Qed.

(* $Date: 2013-07-17 16:19:11 -0400 (Wed, 17 Jul 2013) $ *)


Require Import List.
Require Import Arith.

(** Not actually an exersize I was just checking out how match works*)
Fixpoint odd n :=
  match n with
    0 => False
  | 1 => True
  | S (S p) => odd p
  end.

(** Range Exersize*)
Fixpoint range (n:nat) :=
  match n with
    0 => nil
  | S p => (range p) ++ (p :: nil)
  end.

Compute range 5.

(** Sorting Exersize*)
Require Import Bool.

Definition endIsSorted (l : list nat) :=
  (orb ((length l) <? 2) ((nth 0 l 0) <=? (nth 1 l 0))).

Fixpoint isSorted (l: list nat) :=
  match l with
  | nil => true
  | _ :: tail => (andb (endIsSorted l) (isSorted tail))
  end.

Compute isSorted (1::2::3::nil).
Compute isSorted (1::4::3::nil).

(*Counting occurances in list Exersize*)

Fixpoint countOccurances (l : list nat) (n : nat) : nat :=
  match l with
  | nil => 0
  | head :: tail => if head =? n then
      1 + (countOccurances tail n) else (countOccurances tail n)
  end.

Compute countOccurances (1::2::1::4::1::4::nil) 1.
Compute countOccurances (1::2::1::4::1::4::nil) 4.


(* Contribution to the Coq Library   V6.3 (July 1999)                    *)

(****************************************************************************)
(*                                                                          *)
(*                           Group Theory in Coq                            *)
(*                                                                          *)
(*                                                                          *)
(*                                Coq V5.10                                 *)
(*									    *)
(*			         Gilles Kahn 				    *)
(*									    *)
(*                                  INRIA                                   *)
(*                             Sophia-Antipolis                             *)
(*									    *)
(*				 January 1996				    *)
(*                                                                          *)
(****************************************************************************)

Require Import Ensembles.
Require Import Laws.
Section group_definition.
Variable U : Type.

Record Group : Type := group
  {G_ : Ensemble U;
   star_ : U -> U -> U;
   inv_ : U -> U;
   e_ : U;
   G0_ : endo_operation U G_ star_;
   G1_ : associative U star_;
   G2a_ : In U G_ e_;
   G2b_ : left_neutral U star_ e_;
   G2c_ : right_neutral U star_ e_;
   G3a_ : endo_function U G_ inv_;
   G3b_ : right_inverse U star_ inv_ e_;
   G3c_ : left_inverse U star_ inv_ e_}.

Inductive subgroup (g1 g2 : Group) : Prop :=
    Definition_of_subgroup :
      Included U (G_ g1) (G_ g2) -> star_ g1 = star_ g2 -> subgroup g1 g2.

Definition Setsubgroup (E : Ensemble U) (Gr : Group) : Prop :=
  ex (fun g : Group => subgroup g Gr /\ G_ g = E).
End group_definition.


Require Export Arith.
Require Export ZArithRing.
Require Export Omega.

Theorem Frobenius_3_8 :
  forall n:nat,
    8 <= n -> (exists p:nat, (exists q:nat, n = 3 * p + 5 * q)).
Proof.
 intros n Hle; elim Hle.
 exists 1; exists 1; ring.
 intros n' Hle' [p' [q']].
 case q'.
 intros Heq.
 assert (H3lep': 3 <= p').
 omega.
 exists (p' - 3); exists 2.
 rewrite Heq.
 replace (3*(p'-3)+5*2) with (S (3*3+3*(p'-3))).
 rewrite <- mult_plus_distr_l.
 rewrite le_plus_minus_r; auto.
 ring.
 intros q'' Heq; exists (p'+2); exists q''; rewrite Heq.
 ring.
Qed.


Require Import Setoid.
Require Import ZArith.
Require Import Setoid.

Definition Rel (A:Type) := A -> A -> Prop.

Inductive refltrans {A:Type} (R: Rel A) : A -> A -> Prop :=
| refl: forall a, (refltrans R) a a
| rtrans: forall a b c, R a b -> refltrans R b c -> refltrans R a c.

Definition Z_prop {A:Type} (R: Rel A) := exists f:A -> A, forall a b, R a b -> ((refltrans R) b (f a) /\ (refltrans R) (f a) (f b)).

Definition f_is_Z {A:Type} (R: Rel A) (f: A -> A) := forall a b, R a b -> ((refltrans R)  b (f a) /\ (refltrans R) (f a) (f b)).

Definition f_is_weak_Z {A} (R R': Rel A) (f: A -> A) := forall a b, R a b -> ((refltrans R') b (f a) /\ (refltrans R') (f a) (f b)).

Definition comp {A} (f1 f2: A -> A) := fun x:A => f1 (f2 x).
Notation "f1 # f2" := (comp f1 f2) (at level 40).

Inductive union {A} (red1 red2: Rel A) : Rel A :=
| union_left: forall a b, red1 a b -> union red1 red2 a b
| union_right: forall a b, red2 a b -> union red1 red2 a b.
Notation "R1 !_! R2" := (union R1 R2) (at level 40).

Definition Z_comp {A:Type} (R :Rel A) := exists (R1 R2: Rel A) (f1 f2: A -> A), (forall x y, R x y <-> (R1 !_! R2) x y) /\ f_is_Z R1 f1 /\ (forall a b, R1 a b -> (refltrans R) ((f2 # f1) a) ((f2 # f1) b)) /\ (forall a b, b = f1 a -> (refltrans R) b (f2 b)) /\ (f_is_weak_Z R2 R (f2 # f1)).

Definition Z_comp_eq {A:Type} (R :Rel A) := exists (R1 R2: Rel A) (f1 f2: A -> A), (forall x y, R x y <-> (R1 !_! R2) x y) /\ (forall a b, R1 a b -> (f1 a) = (f1 b)) /\ (forall a, (refltrans R1) a (f1 a)) /\ (forall b a, a = f1 b -> (refltrans R) a (f2 a)) /\ (f_is_weak_Z R2 R (f2 # f1)).

Inductive trans {A} (red: Rel A) : Rel A :=
| singl: forall a b,  red a b -> trans red a b
| transit: forall b a c,  red a b -> trans red b c -> trans red a c.

Arguments transit {A} {red} _ _ _ _ _ .

Lemma trans_composition {A} (R: Rel A):
  forall t u v, trans R t u -> trans R u v -> trans R t v.
Proof.
  intros t u v H1 H2. induction H1.
  - apply transit with b; assumption.
  - apply transit with b.
    + assumption.
    + apply IHtrans; assumption.
Qed.

Lemma refltrans_composition {A} (R: Rel A): forall t u v, refltrans R t u -> refltrans R u v -> refltrans R t v.
Proof.
  intros t u v.
  intros H1 H2.
  induction H1.
  - assumption.
  - apply rtrans with b.
    + assumption.
    + apply IHrefltrans; assumption.
Qed.

Lemma union_or {A}: forall (r1 r2: Rel A) (a b: A), (r1 !_! r2) a b <-> (r1 a b) \/ (r2 a b).
Proof.
  intros r1 r2 a b; split.
  - intro Hunion.
    inversion Hunion; subst.
    + left; assumption.
    + right; assumption.
  - intro Hunion.
    inversion Hunion.
    + apply union_left; assumption.
    + apply union_right; assumption.
Qed.

Lemma refltrans_union {A:Type}: forall (R R' :Rel A) (a b: A), refltrans R a b -> refltrans (R !_! R') a b.
Proof.
  intros R R' a b Hrefl.
  induction Hrefl.
  - apply refl.
  - apply rtrans with b.
    + apply union_left. assumption.
    + assumption.
Qed.

Lemma refltrans_union_equiv {A}: forall (R R1 R2 : Rel A), (forall (x y : A), (R x y <-> (R1 !_! R2) x y)) -> forall (x y: A), refltrans (R1 !_! R2) x y -> refltrans R x y.
Proof.
  intros.
  induction H0.
  + apply refl.
  + apply rtrans with b.
    - apply H. assumption.
    - assumption.
Qed.

Theorem Z_comp_implies_Z_prop {A:Type}: forall (R :Rel A), Z_comp R -> Z_prop R.
Proof.
  intros R H.
  unfold Z_prop. unfold Z_comp in H. destruct H as
  [ R1 [ R2 [f1 [f2 [Hunion [H1 [H2 [H3 H4]]]]]]]].
  exists (f2 # f1).
  intros a b HR.
  apply Hunion in HR. inversion HR; subst. clear HR.
  - split.
    + apply refltrans_composition with (f1 a).
      * apply H1 in H.
        destruct H as [Hb Hf].
        apply (refltrans_union R1 R2) in Hb.
        apply refltrans_union_equiv with R1 R2.
        ** apply Hunion.
        ** apply Hb.
      * apply H3 with a. reflexivity.
    + apply H2. assumption.
  - apply H4. assumption.
Qed.

Lemma Z_comp_eq_implies_Z_comp {A:Type}: forall (R : Rel A), Z_comp_eq R -> Z_comp R.
Proof.
  intros R Heq. unfold Z_comp_eq in Heq.
  destruct Heq as [R1 [R2 [f1 [f2 [Hunion [H1 [H2 [H3 H4]]]]]]]].
  unfold Z_comp.
  exists R1, R2, f1, f2.
  split.
  - assumption.
  - split.
    + unfold f_is_Z.
      intros a b H; split.
      * apply H1 in H. rewrite H. apply H2.
      * apply H1 in H. rewrite H. apply refl.
    + split.
      * intros a b H.
        unfold comp.
        apply H1 in H.
        rewrite H.
        apply refl.
      * split; assumption.
Qed.

Corollary Z_comp_eq_implies_Z_prop {A:Type}: forall (R : Rel A), Z_comp_eq R -> Z_prop R.
Proof.
  intros.
  apply Z_comp_eq_implies_Z_comp in H.
  apply Z_comp_implies_Z_prop.
  assumption.
Qed.


(** Generated by coq-of-ocaml *)
Require Import OCaml.OCaml.

Local Set Primitive Projections.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope type_scope.
Import ListNotations.

Unset Positivity Checking.
Unset Guard Checking.

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

Inductive natural : Set :=
| Zero : natural
| Succ : natural -> natural.

Fixpoint plus (plus_arg0 : natural) (plus_arg1 : natural) {struct plus_arg0}
  : natural :=
  match plus_arg0 with
  | Zero => plus_arg1
  | Succ n => Succ (plus n plus_arg1)
  end.

Fixpoint mult (mult_arg0 : natural) (mult_arg1 : natural) {struct mult_arg0}
  : natural :=
  match mult_arg0 with
  | Zero => Zero
  | Succ n => plus (mult n mult_arg1) mult_arg1
  end.

Definition synth (lf2 : natural) (x : natural) : natural :=
  mult lf2 (plus Zero x).


(** Generated by coq-of-ocaml *)
Require Import OCaml.OCaml.

Local Set Primitive Projections.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope type_scope.
Import ListNotations.

Unset Positivity Checking.
Unset Guard Checking.

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

Inductive natural : Set :=
| Zero : natural
| Succ : natural -> natural.

Fixpoint plus (plus_arg0 : natural) (plus_arg1 : natural) {struct plus_arg0}
  : natural :=
  match plus_arg0 with
  | Zero => plus_arg1
  | Succ n => Succ (plus n plus_arg1)
  end.

Fixpoint mult (mult_arg0 : natural) (mult_arg1 : natural) {struct mult_arg0}
  : natural :=
  match mult_arg0 with
  | Zero => Zero
  | Succ n => plus (mult n mult_arg1) mult_arg1
  end.

Definition synth (lf3 : natural) (lf2 : natural) : natural :=
  plus Zero (plus lf2 lf3).


(** Generated by coq-of-ocaml *)
Require Import OCaml.OCaml.

Local Set Primitive Projections.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope type_scope.
Import ListNotations.

Unset Positivity Checking.
Unset Guard Checking.

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

Inductive natural : Set :=
| Zero : natural
| Succ : natural -> natural.

Fixpoint plus (plus_arg0 : natural) (plus_arg1 : natural) {struct plus_arg0}
  : natural :=
  match plus_arg0 with
  | Zero => plus_arg1
  | Succ n => Succ (plus n plus_arg1)
  end.

Fixpoint mult (mult_arg0 : natural) (mult_arg1 : natural) {struct mult_arg0}
  : natural :=
  match mult_arg0 with
  | Zero => Zero
  | Succ n => plus (mult n mult_arg1) mult_arg1
  end.

Definition synth (lf1 : natural) (lf2 : natural) : natural :=
  plus Zero (plus lf2 lf1).


Require Import
  Coq.Lists.List Coq.Lists.SetoidList MathClasses.implementations.list
  MathClasses.interfaces.abstract_algebra MathClasses.interfaces.finite_sets MathClasses.interfaces.orders
  MathClasses.theory.lattices MathClasses.orders.lattices.

(*
We define finite sets as unordered lists. This implementation is slow,
but quite convenient as a reference implementation to lift properties to
arbitrary finite set instances.
*)
Instance listset A `{Equiv A} : SetType A | 30 := sig (NoDupA (=)).

Section listset.
Context `{Setoid A} `{∀ a₁ a₂ : A, Decision (a₁ = a₂)}.

Instance listset_in_raw: Contains A (list A) := InA (=).
Instance listset_equiv_raw: Equiv (list A) := equivlistA (=).
Instance: Setoid (list A) := {}.

Instance listset_empty_raw: Bottom (list A) := [].
Instance listset_join_raw: Join (list A) := @app A.
Instance: BoundedJoinSemiLattice (list A).
Proof.
  split. split. split. split. apply _.
       repeat intro. now apply equivlistA_app_ass.
      apply _.
     repeat intro. now apply equivlistA_app_nil_l.
    repeat intro. now apply equivlistA_app_nil_r.
   repeat intro. now apply equivlistA_app_comm.
  repeat intro. now apply equivlistA_app_idem.
Qed.

Global Instance listset_to_list: Cast (set_type A) (list A) := @proj1_sig _ _.
Global Instance listset_in: SetContains A := λ x l, x ∈ 'l.
Global Instance listset_le: SetLe A := λ l k, ∀ x, x ∈ l → x ∈ k.
Global Instance listset_equiv: SetEquiv A := λ l k, ∀ x, x ∈ l ↔ x ∈ k.

Instance: Setoid (set_type A).
Proof. now apply (setoids.projected_setoid listset_to_list). Qed.

Global Instance: Setoid_Morphism listset_to_list.
Proof. firstorder. Qed.
Global Instance: Injective listset_to_list.
Proof. firstorder. Qed.

Global Instance: Proper ((=) ==> (=) ==> iff) listset_in.
Proof.
  intros x y E1 l k E2.
  transitivity (listset_in x k). easy.
  unfold listset_in. now rewrite E1.
Qed.

Fixpoint listset_add_raw (x : A) (l : list A) : list A :=
  match l with
  | [] => [x]
  | y :: l => y :: if decide_rel (=) x y then l else listset_add_raw x l
  end.

Lemma listset_add_raw_cons l x :
  x :: l = listset_add_raw x l.
Proof.
  induction l; simpl; try reflexivity.
  case (decide_rel _); intros E.
   now rewrite E, equivlistA_double_head.
  now rewrite equivlistA_permute_heads, IHl.
Qed.

Lemma listset_add_raw_InA (l : list A) (x y : A) :
  y ∈ listset_add_raw x l → y = x ∨ y ∈ l.
Proof.
  unfold contains, listset_in_raw. induction l; simpl.
   intros E. inversion_clear E; auto.
  case (decide_rel _); auto; intros E1 E2.
  inversion_clear E2; intuition.
Qed.

Lemma listset_add_raw_NoDupA (l : list A) (x : A) :
  NoDupA (=) l → NoDupA (=) (listset_add_raw x l).
Proof.
  intros Pl. induction l; simpl.
   now apply NoDupA_singleton.
  case (decide_rel _); intros E1; auto.
  inversion_clear Pl.
  apply NoDupA_cons; auto.
  intros E2. destruct (listset_add_raw_InA _ _ _ E2); intuition.
Qed.

Global Program Instance listset_empty: EmptySet A := [].
Global Program Instance listset_singleton: SetSingleton A := λ x, [x].
Next Obligation. now apply NoDupA_singleton. Qed.
Global Program Instance listset_join: SetJoin A := λ l k, fold_right listset_add_raw (`k) (`l)↾_.
Next Obligation.
  destruct l as [l Pl], k as [k Pk].
  induction l; intros; simpl in *; auto.
  apply listset_add_raw_NoDupA, IHl. now inversion Pl.
Qed.

Instance: Setoid_Morphism listset_singleton.
Proof.
  split; try apply _. intros ? ? E.
  apply (injective listset_to_list). change ([x] = [y]). now rewrite E.
Qed.

Lemma listset_to_list_preserves_join l k :
  listset_to_list (l ⊔ k) = listset_to_list l ⊔ listset_to_list k.
Proof.
  destruct l as [l Pl], k as [k Pk].
  unfold join, listset_join, listset_join_raw. simpl. clear Pk Pl.
  induction l; simpl; intros; [easy|].
  now rewrite <-IHl, listset_add_raw_cons.
Qed.

Instance: BoundedJoinSemiLattice (set_type A).
Proof.
  apply (projected_bounded_sl listset_to_list).
   intros. now apply listset_to_list_preserves_join.
  reflexivity.
Qed.

Lemma listset_in_join l k x : x ∈ l ⊔ k ↔ x ∈ l ∨ x ∈ k.
Proof.
  unfold contains, listset_in_raw, listset_in.
  rewrite listset_to_list_preserves_join.
  now apply InA_app_iff.
Qed.

Instance: JoinSemiLatticeOrder listset_le.
Proof.
  apply alt_Build_JoinSemiLatticeOrder. intros l k.
  unfold le, listset_le, equiv, listset_equiv.
  setoid_rewrite listset_in_join. firstorder auto.
Qed.

Lemma listset_induction (P : set_type A → Prop) `{proper : !Proper ((=) ==> iff) P} :
  P ∅ → (∀ x l, x ∉ l → P l → P ({{ x }} ⊔ l)) → ∀ l, P l.
Proof.
  intros Pempty Padd.
  intros [l Pl]. induction l as [|x l].
   apply proper with ∅; firstorder.
  inversion_clear Pl as [|??? Pl'].
  apply proper with ({{ x }} ⊔ l↾Pl'); auto.
  intros z. change (z ∈ x :: l ↔ z ∈ listset_add_raw x l).
  now rewrite listset_add_raw_cons.
Qed.

Fixpoint listset_extend_raw `{Bottom B} `{Join B} (f : A → B) (l : list A) : B :=
  match l with
  | [] => ⊥
  | x :: l => f x ⊔ listset_extend_raw f l
  end.

Global Instance list_extend: FSetExtend A := λ _ _ _ f l, listset_extend_raw f (`l).

Section listset_extend.
  Context `{BoundedJoinSemiLattice B} `{!Setoid_Morphism (f : A → B)}.

  Lemma listset_extend_raw_permute (l k : list A) :
    PermutationA (=) l k → listset_extend_raw f l = listset_extend_raw f k.
  Proof.
    induction 1; simpl.
       reflexivity.
      apply sg_op_proper. now apply sm_proper. easy.
     now rewrite !associativity, (commutativity (f _)).
    etransitivity; eassumption.
  Qed.

  Instance list_extend_proper: Proper (equiv ==> equiv) (fset_extend f).
  Proof.
    intros [??][??] ?.
    apply listset_extend_raw_permute. now apply NoDupA_equivlistA_PermutationA.
  Qed.

  Lemma list_extend_empty:
    fset_extend f ∅ = ⊥.
  Proof. reflexivity. Qed.

  Lemma list_extend_add x l :
    fset_extend f ({{x}} ⊔ l) = f x ⊔ fset_extend f l.
  Proof.
    destruct l as [l Pl]. unfold fset_extend, list_extend. simpl. clear Pl.
    induction l; simpl; [easy|].
    case (decide_rel _); intros E.
     now rewrite E, associativity, (idempotency (&) _).
    now rewrite IHl, 2!associativity, (commutativity (f _)).
  Qed.

  Instance list_extend_mor:
    BoundedJoinSemiLattice_Morphism (fset_extend f).
  Proof.
    repeat (split; try apply _).
     intros l k. change (fset_extend f (l ⊔ k) = fset_extend f l ⊔ fset_extend f k).
     pattern l. apply listset_induction; clear l.
       solve_proper.
      now rewrite list_extend_empty, 2!left_identity.
     intros x l E1 E2.
     now rewrite <-associativity, 2!list_extend_add, E2, associativity.
    reflexivity.
  Qed.
End listset_extend.

Local Existing Instance list_extend_mor.

Global Instance: FSet A.
Proof.
  split; try apply _.
   intros B ???? f ? x y E.
   unfold compose, fset_extend, list_extend. simpl.
   now rewrite E, right_identity.
  intros B ??? f ? h ? E1 k l E2.
  pose proof (bounded_join_slmor_b (f:=h)).
  rewrite E2. clear k E2. pattern l.
  apply listset_induction; clear l.
    solve_proper.
   now rewrite preserves_bottom.
  intros x l E2 E3. rewrite list_extend_add, preserves_join, E3.
  apply sg_op_proper; [|easy]. symmetry. now apply E1.
Qed.

Instance: FSetContainsSpec A.
Proof.
  split; try apply _. unfold le, listset_le.
  intros x X; split; intros E1.
   intros z E2. inversion_clear E2 as [?? E3|?? E3].
    now rewrite E3.
   now inversion E3.
  apply E1. now rapply InA_cons_hd.
Qed.

Instance listset_in_raw_dec: ∀ x (l : list A), Decision (x ∈ l) := λ x l, InA_dec (decide_rel (=)) x l.
Global Instance listset_in_dec: ∀ x (l : set_type A), Decision (x ∈ l) := λ x l, InA_dec (decide_rel (=)) x ('l).

Instance listset_meet_raw: Meet (list A) :=
  fix listset_meet_raw l k :=
    match l with
    | [] => []
    | x :: l => if decide_rel (∈) x k then x :: listset_meet_raw l k else listset_meet_raw l k
    end.

Lemma listset_in_meet_raw l k x :
  x ∈ l ⊓ k ↔ x ∈ l ∧ x ∈ k.
Proof.
  unfold meet, contains, listset_in_raw. split.
   intros E; split; revert E.
    induction l; simpl.
     intuition.
    case (decide_rel _); intros ? E; intuition.
    inversion_clear E; intuition.
   induction l; simpl.
    intros E1; inversion E1.
   case (decide_rel _); intros ? E1; intuition.
   inversion_clear E1 as [?? E2|]; auto. now rewrite E2.
  intros [E1 E2]. induction l; simpl; [easy|].
  case (decide_rel _); intros E3.
   inversion_clear E1; intuition.
  inversion_clear E1 as [?? E4|]; intuition.
  destruct E3. now rewrite <-E4.
Qed.

Lemma listset_meet_raw_NoDupA (l k : list A) :
  NoDupA (=) l → NoDupA (=) (l ⊓ k).
Proof.
  unfold meet. intros Pl. induction l; simpl; auto.
  inversion_clear Pl as [|? ? E1].
  case (decide_rel _); intros; auto.
  apply NoDupA_cons; auto.
  intros E2. destruct E1. now apply (listset_in_meet_raw l k _).
Qed.

Global Program Instance listset_meet: SetMeet A := λ l k, listset_meet_raw l k.
Next Obligation. apply listset_meet_raw_NoDupA. now destruct l. Qed.

Instance listset_diff_raw: Difference (list A) :=
  fix listset_diff_raw l k :=
    match l with
    | [] => []
    | x :: l => if decide_rel (∈) x k then listset_diff_raw l k else x :: listset_diff_raw l k
    end.

Lemma listset_in_diff_raw l k x :
  x ∈ l ∖ k ↔ x ∈ l ∧ x ∉ k.
Proof.
  unfold difference, contains, listset_in_raw. split.
   intros E; split; revert E.
    induction l; simpl.
     intuition.
    case (decide_rel _); intros ? E; intuition.
    inversion_clear E; intuition.
   induction l; simpl.
    intros E1; inversion E1.
   case (decide_rel _); intros ? E1.
    intuition.
   inversion_clear E1 as [?? E2|]; auto. now rewrite E2.
  intros [E1 E2]. induction l; simpl; [easy|].
  case (decide_rel _); intros E3.
   inversion_clear E1 as [?? E4|]; intuition.
   destruct E2. now rewrite E4.
  inversion_clear E1; intuition.
Qed.

Lemma listset_diff_raw_NoDupA (l k : list A) :
  NoDupA (=) l → NoDupA (=) (l ∖ k).
Proof.
  unfold difference. intros Pl. induction l; simpl; auto.
  inversion_clear Pl as [|? ? E1].
  case (decide_rel _); intros; auto.
  apply NoDupA_cons; auto.
  intros E2. destruct E1. now apply (listset_in_diff_raw l k _).
Qed.

Global Program Instance listset_diff: SetDifference A := λ l k, listset_diff_raw l k.
Next Obligation. apply listset_diff_raw_NoDupA. now destruct l. Qed.

Global Instance: FullFSet A | 30.
Proof.
  split; try apply _.
   intros [??] [??]. now rapply listset_in_meet_raw.
  intros [??] [??]. now rapply listset_in_diff_raw.
Qed.
End listset.


Section ExerciseTwo.

Variable X : Set.
Variable P Q R : X -> Prop.

Lemma Ex2A : (exists x, (P x) /\ (Q x)) -> (exists x, (P x)) /\ (exists x, (Q x)).
intros H.
split.
  destruct H as [x H1].
  destruct H1 as [H2 H3].
  exists x.
  exact H2.
  destruct H as [x H1].
  destruct H1 as [H2 H3].
  exists x.
  exact H3.
Qed.

Variable A : X -> X -> Prop.

Lemma Ex2B : (exists x, forall y, (A x y)) -> (forall y, exists x, (A x y)).
intros H.
destruct H as [x H2].
intro H3.
exists x.
apply H2.
Qed.

Lemma Ex2C : (exists x, (P x)) -> (forall x y, (P x) -> (Q y)) -> (forall y, (Q y)).
intros H1 H2.
intros H3.
elim H1.
intros H4.
apply H2.
Qed.

Lemma Ex2D : (forall x, (Q x) -> (R x)) -> (exists x, (P x) /\ (Q x)) -> (exists x, (P x) /\ (R x)).
intros H1 H2.
destruct H2 as [x H3].
exists x.
destruct H3 as [H4 H5].
split.
  exact H4.
  apply H1.
  exact H5.
Qed.

Lemma Ex2E : (forall x, (P x) -> (Q x)) -> (exists x, (P x)) -> (exists y, (Q y)).
Proof.
intros H1 H2.
destruct H2 as [x H3].
exists x.
apply H1.
exact H3.
Qed.

Lemma Ex2F : (exists x, (P x)) \/ (exists x, (Q x)) <-> (exists x, (P x) \/ (Q x)).
Proof.
split.
  (* left to right *)
  intros H1.
  destruct H1 as [H2 | H3].
  destruct H2 as [x H4].
  exists x.
  left; exact H4.
  destruct H3 as [x H5].
  exists x.
  right; exact H5.
  (* right to left *)
  intros H1.
  destruct H1 as [x H2].
  destruct H2 as [H3 | H4].
  left.
  exists x.
  exact H3.
  right.
  exists x.
  exact H4.
Qed.


Require Import Basics.
Require Import Types.
Require Import Diagrams.Graph.
Require Import Diagrams.Diagram.

(** We define here the Graph ∫D, also denoted G·D *)

Definition integral {G : Graph} (D : Diagram G) : Graph.
Proof.
  serapply Build_Graph.
  + exact {i : G & D i}.
  + intros i j.
    exact {g : G i.1 j.1 & D _f g i.2 = j.2}.
Defined.

(** Then, a dependent diagram E over D is just a diagram over ∫D. *)

Definition DDiagram {G : Graph} (D : Diagram G)
  := Diagram (integral D).

(** Given a dependent diagram, we c.an recover a diagram over G by considering the Σ types. *)

  Definition diagram_sigma {G : Graph} {D : Diagram G} (E : DDiagram D)
    : Diagram G.
  Proof.
    serapply Build_Diagram.
    - intro i.
      exact {x : D i & E (i; x)}.
    - intros i j g x. simpl in *.
      exists (D _f g x.1).
      exact (@arr _ E (i; x.1) (j; D _f g x.1) (g; idpath) x.2).
  Defined.

  (** A dependent diagram is said equifibered if all its fibers are equivalences. *)

  Class Equifibered {G : Graph} {D : Diagram G} (E : DDiagram D) := {
    isequifibered i j (g : G i j) (x : D i)
      :> IsEquiv (@arr _ E (i; x) (j; D _f g x) (g; idpath));
  }.