Set Implicit Arguments.
Require Import Relations List Sorted Arith.
From hydras Require Import Restriction.
Require Import Lia.

(* begin snippet tDef *)
Definition t (A:Type) := list (A * nat).
(* end snippet tDef *)

(* begin snippet AGiven *)

Section A_given.

  Variable A: Type.
  Variable LtA : relation A.
  (* end snippet AGiven *)

  (* begin snippet lexpowerDef *)

  Inductive lexpower: relation (t A) :=
    lex1: forall a n l,  lexpower nil ((a,n)::l)
  | lex2: forall a n p l l',  n < p -> lexpower ((a,n)::l) ((a,p)::l')
  | lex3: forall a b n p l l',  LtA a b -> lexpower ((a,n)::l) ((b,p)::l')
  | lex4: forall a n l l',  lexpower l l' -> lexpower ((a,n)::l) ((a,n)::l').

End A_given.
(* end snippet lexpowerDef *)

(* begin snippet notWfa:: no-out *)
Section Counter_Example.
  Let R := lexpower  lt.
  Hypothesis Hwf : well_founded R.

  Definition seq (n:nat) := repeat (0,0) n ++ ((2,0)::nil).

  Lemma decr_seq : forall n, R (seq (S n)) (seq n).
  (* end snippet notWfa *)
  Proof.
    induction n.
    -  constructor 3; auto with arith.
    - constructor 4; apply IHn.
  Qed.

  Lemma not_acc : forall a b, R a b -> ~ Acc R a -> ~ Acc R b.
  Proof.
    intros a b H H0 H1; absurd (Acc R a); auto.
  Qed.

  (* begin snippet notWfb:: no-out *)
  Let is_in_seq l := exists i, l = seq i.

  Lemma is_in_seq_not_Acc : forall x,  is_in_seq x -> ~ Acc R x.
  Proof.
    intro x; pattern x; apply well_founded_ind with R; [assumption | ].
    (* ... *)
    (* end snippet notWfb *)
    clear x; intros x IHx [i Hi]; subst x.
    specialize (IHx _  (decr_seq i)); destruct IHx.
    exists (S i);auto.
    apply Hwf.
  Qed.

  (* begin snippet notWfc:: no-out *)
  Lemma contrad: False.
  Proof.
    apply (@is_in_seq_not_Acc (seq 0)).
    exists 0; trivial.
    apply Hwf.
  Qed.

End Counter_Example.
(* end snippet notWfc *)

(** Lists in normal form *)

(* begin snippet lexnfDef *)
Definition lexnf {A: Type}(ltA : relation A) (l: t A)
  := LocallySorted (Basics.flip ltA) (map fst l).
(* end snippet lexnfDef *)

(* begin snippet lexltDef *)
Definition lexlt {A}(ltA : relation A) :=
  restrict (lexnf ltA) (lexpower ltA).
(* end snippet lexltDef *)

(* begin snippet bigProofa:: no-out *)
Section ProofOfLexwf.
  Variables (A: Type)
            (ltA : relation A).
  Hypothesis HwfA : well_founded ltA.

  #[local] Notation NF := (lexnf ltA).
  #[local] Notation LT := (lexlt ltA).
  (* end snippet bigProofa *)

  (* begin snippet theStatement:: no-out *)
  Theorem lexwf:
    forall l,  NF l -> Acc LT l.
  (* end snippet theStatement *)

  (* begin snippet BadProof:: no-out *)
  Proof.
    induction l.
    - split; destruct 1 as [_ [H1 _]]. inversion H1.
      (* end snippet BadProof *)
     (* begin snippet BadProofb:: -.h#A -.h#ltA -.h#A -.h#H  *)
    - (* .no-out *) split; split; intros; destruct a as [a n].
      (* end snippet BadProofb *)
      (* begin snippet BadProofc *)
  Abort.
  (* end snippet BadProofc *)

  (* begin snippet NFInv1:: no-out *)
  Lemma NF_inv1 : forall a n l, NF ((a,n)::l) -> NF l.
  (* end snippet NFInv1 *)
  Proof.
    destruct l.
    - constructor.
    - inversion_clear 1; assumption.
  Qed.

  (* begin snippet NFInv2:: no-out *)
  Lemma NF_inv2 : forall a n b p l,  NF((a,n)::(b,p)::l) -> ltA b a.
    (* end snippet NFInv2 *)
    inversion_clear 1. assumption.
  Qed.

  (* begin snippet LTInv:: no-out *)
  Lemma LT_inv : forall a n l l',
      LT l' ((a,n)::l) ->
      l' = nil \/
      (exists b p l'', l'= ((b,p)::l'') /\ ltA b a) \/
      (exists l'',  l'=(a,n)::l'' /\ LT l'' l) \/
      (exists  p l'', l'= ((a,p)::l'') /\ p < n).
  (* end snippet LTInv *)
  Proof.
    destruct 1.
    destruct l'.
    - now left.
    - right.
      destruct H0 as [H0 H1]; inversion H0.
      + subst; right; right; exists n0, l'; split; auto.
      + subst; left; exists a0, n0, l'; split; auto.
      + subst; right;left; exists l'; split; auto.
        split; auto.
        eapply NF_inv1 ; eauto.
        split; auto.
        eapply NF_inv1 ; eauto.
  Qed.

  (* begin snippet AccsDef *)
  Let Accs (a:A) := forall n l, NF ((a,n)::l) ->
                                Acc LT ((a,n)::l).
  (* end snippet AccsDef *)

  (* begin snippet AccNil:: no-out *)
  Lemma Acc_nil : Acc LT nil.
  Proof.
    split; destruct 1 as [_ [H _]]; inversion H.
  Qed.
  (* end snippet AccNil *)

  (* begin snippet LAccsa *)
  Lemma Accs_all: forall a:A, Accs a. (* .no-out *)
  Proof. (* .no-out *)
    unfold Accs; intros a; pattern a;
      eapply  well_founded_induction with (R:= ltA); [assumption|];
        clear a ; intros a  IHa.
    (* end snippet LAccsa *)

    (** let us prepare an induction on l *)
    (* begin snippet Laccsb:: no-out *)
    assert (Hl: forall n l, NF ((a,n)::l) -> Acc LT l).
    (* we skip the proof of Hl ... *)
    (* end snippet Laccsb *)
    {
      destruct l.
      - intro; apply Acc_nil.
      - destruct p as [a0 n0]; intro H; apply IHa.
        + now inversion_clear H.
        + eapply NF_inv1; eauto.
          (* begin snippet Laccsg:: no-in unfold -.h#* .h#IHa  .h#Hl *)
    }
    (* end snippet Laccsg *)
    (* begin snippet Laccsc:: no-out  *)
    intro n; pattern n; apply (well_founded_induction lt_wf).
    (* end snippet Laccsc *)
    (* begin snippet Laccsd:: -.h#A -.h#ltA -.h#Accs -.h#HwfA  *)
    clear n; intros n Hn l H0.
    (* end snippet Laccsd *)

    (* begin snippet Laccse:: -.h#A -.h#ltA -.h#HwfA -.h#Accs -.h#Hl  *)
    assert (H1: Acc LT l) by (eapply Hl; eauto);
      revert H0; pattern l; eapply Acc_ind with LT; [| eauto];
        intros x0 H0 H2 H3.
    (* end snippet Laccse *)
    (* begin snippet Laccsf:: -.h#* .h#IHa .h#Hn .h#H0 .h#H2 .h#H2 .h#H3 .h#H4 *)
    split; intros y H4.
    destruct (LT_inv H4) as [H5 | [H6 | [H7 | H8]]]. (* .no-out *)
    (* end snippet Laccsf *)
    (* begin snippet case1:: -.h#* .h#H5  *)
    + (* .no-in .unfold *) subst; apply Acc_nil. (* .no-out *)
    (* end snippet case1 *)

    + (* begin snippet case2:: -.h#* .h#IHa .h#H7  .h#H4 *)
      destruct H6 as [b [p [l'' [H6 H7]]]];  subst y. (* .no-in .unfold *)
      apply IHa;  auto.
      now destruct H4.
    (* end snippet case2 *)
    + (* begin snippet case3:: -.h#*  .h#H7  .h#H4 .h#H6 .h#H2 *)
      destruct H7 as [l'' [H6 H7]];  subst. (* .no-in .unfold *)
      apply H2; auto.
      now destruct H4.
    (* end snippet case3 *)
    + (* begin snippet case4:: -.h#*  .h#H7  .h#Hn .h#H4 .h#H6  *)
      destruct H8 as [p [l'' [H6 H7]]]; subst; apply Hn; auto.
      (* end snippet case4 *)
      now destruct H4.
  Qed.

(* begin snippet NFAcc:: no-out  *)
  Lemma NF_Acc : forall l: t A, NF l -> Acc LT l.
  Proof.
    destruct l.
    -  intro; apply Acc_nil.
    -  destruct p; intro;  now apply Accs_all.
  Qed.

End ProofOfLexwf.
(* end snippet NFAcc  *)

(* begin snippet lexwf:: no-out  *)
Theorem lexwf {A}( ltA : relation A) :
  well_founded ltA ->
  forall l,  lexnf ltA l -> Acc (lexlt ltA) l. (* .no-out *)
Proof. apply NF_Acc.  Qed.
(* end snippet lexwf  *)

(* begin snippet Examples:: no-out *)

Example Ex1 : lexpower  lt  ((2,7)::nil) ((3,0):: nil).
Proof.
  constructor 3; auto with arith.
Qed.

Example Ex2 : lexpower  lt  ((2,7)::(1,0)::(0,33)::nil) ((2,7)::(1,6)::nil).
Proof.
  constructor 4.
  constructor 2; auto with arith.
Qed.

Example Ex3 : lexnf lt ((2,7)::(1,0)::(0,33)::nil).
Proof.
  repeat constructor.
Qed.

(* end snippet Examples *)

(* begin snippet Impossibility1:: no-out *)
Section Impossibility1.
  Variable m : t nat -> nat.
  Hypothesis mDecr : forall l l': t nat,  lexlt lt l l' -> m l <  m l'.

  Definition iota (n:nat) := (0, n)::nil.
  Let x := m ((1,0)::nil).
  Let y := m (iota x).

  Fact F1 : y < x.
  (* end snippet Impossibility1 *)
  Proof.
    apply mDecr; unfold x,iota; cbn.
    split.
    - red; constructor.
    - split.
      + constructor; auto with arith.
      + red; constructor.
  Qed.
(* begin snippet Impossibility1a:: no-out *)
  Fact F2 : x <= y.
  (* end snippet Impossibility1a *)
  Proof.
    unfold y in *; clear y; induction x.
    - auto with arith.
    -  assert (m (iota n) < m (iota (S n))).
       { apply mDecr; unfold iota; simpl.
         split.
         - constructor.
         - split.
           + constructor 2; auto with arith.
           + constructor.
       }
       lia.
  Qed.

  (* begin snippet Impossibility1b:: no-out *)
  Lemma impossible_nat : False.
  Proof.  generalize F1, F2; lia.   Qed.

End Impossibility1.
(* end snippet Impossibility1b *)


Require Export ProjectiveGeometry.Dev.projective_plane_axioms.
Require Export ProjectiveGeometry.Dev.fano_plane.

(** Fano's plane **)
(** also known as PG(2,2). **)

(** To show that our axiom system is consistent we build a finite model. **)

Section s_fanoPlaneModel.

(** We define point and line by an inductive type representing the seven possibilities. **)
(** We can not use directly the inductive type for a technical reason related to Coq's implementation. **)

Inductive ind_Point : Set := A | B | C | D | E | F | G.
Inductive ind_line : Set := ABF | BCD | CAE | ADG | BEG | CFG | DEF.

Definition Point : Set := ind_Point.
Definition Line : Set := ind_line.

Definition Incid_bool : Point -> Line -> bool := fun P l =>
 match P with
| A =>
    match l with
    | ABF => true
    | BCD => false
    | CAE => true
    | ADG => true
    | BEG => false
    | CFG => false
    | DEF => false
    end
| B =>
    match l with
    | ABF => true
    | BCD => true
    | CAE => false
    | ADG => false
    | BEG => true
    | CFG => false
    | DEF => false
    end
| C =>
    match l with
    | ABF => false
    | BCD => true
    | CAE => true
    | ADG => false
    | BEG => false
    | CFG => true
    | DEF => false
    end
| D =>
    match l with
    | ABF => false
    | BCD => true
    | CAE => false
    | ADG => true
    | BEG => false
    | CFG => false
    | DEF => true
    end
| E =>
    match l with
    | ABF => false
    | BCD => false
    | CAE => true
    | ADG => false
    | BEG => true
    | CFG => false
    | DEF => true
    end
| F =>
    match l with
    | ABF => true
    | BCD => false
    | CAE => false
    | ADG => false
    | BEG => false
    | CFG => true
    | DEF => true
    end
| G =>
    match l with
    | ABF => false
    | BCD => false
    | CAE => false
    | ADG => true
    | BEG => true
    | CFG => true
    | DEF => false
    end
end.

Definition Incid : Point -> Line -> Prop := fun P L => (Incid_bool P L = true).

Hint Unfold Incid Incid_bool.

Lemma incid_dec : forall (A:Point) (l:Line), {Incid A l} + {~Incid A l}.
Proof.
intros.
unfold Incid.
elim l;elim A0;unfold Incid_bool;simpl;auto;right;discriminate.
Qed.

Ltac solve_ex L := solve [exists L;auto].

(** A tactic which tries all possible lines **)
Ltac solve_ex_l := first [solve_ex ABF
     |  solve_ex BCD
     |  solve_ex CAE
     |  solve_ex ADG
     |  solve_ex BEG
     |  solve_ex CFG
     |  solve_ex DEF
 ].

(** A tactic which tries all possible points **)
Ltac solve_ex_p := first [
        solve_ex A
     |  solve_ex B
     |  solve_ex C
     |  solve_ex D
     |  solve_ex E
     |  solve_ex F
     |  solve_ex G
 ].

(** A1 : any two points lie on a unique line **)
Lemma a1_exist : forall ( A B :Point) ,{l:Line | Incid A l /\ Incid B l}.
Proof.
intros.
elim A0;elim B0;solve_ex_l.
Qed.

Lemma degen_point: forall A:Point, forall P: Prop, ~A=A -> P.
Proof.
intuition.
Qed.

Lemma degen_line: forall A:Line, forall P: Prop, A<>A -> P.
Proof.
intuition.
Qed.

Ltac remove_degen := match goal with
| H: ~ ?A=?A |- ?G => apply (degen_point A G H)
| H: ?A<>?A |- ?G => apply (degen_line A G H)
end.

Lemma uniqueness : forall A B :Point, forall l m : Line,
 Incid A l -> Incid B l  -> Incid A m -> Incid B m -> A=B\/l=m.
Proof.
intros P Q l m H1 H2 H3 H4.
induction P; induction Q; try (left;reflexivity);
induction l; try discriminate;
induction m; try discriminate; try (left; reflexivity) ; try (right; reflexivity); try remove_degen.
Qed.

Lemma a1_unique:forall (A B :Point)(l1 l2:Line),
  ~A=B -> Incid A l1 -> Incid B l1 -> Incid A l2 -> Incid B l2 -> l1=l2.
Proof.
intros X Y l1 l2 HXY H1 H2 H3 H4.
induction X;induction Y; try remove_degen;
induction l1;try discriminate; induction l2; discriminate || reflexivity.
Qed.

Lemma a2_unique : forall(l1 l2 :Line)(A B :Point),
  ~l1=l2 -> Incid A l1 -> Incid A l2 -> Incid B l1 -> Incid B l2 -> A=B.
Proof.
intros l1 l2 X Y H H1 H2 H3 H4.
 induction X;induction Y;try reflexivity;
 induction l1;try discriminate;
 induction l2;try discriminate;try remove_degen.
Qed.

(** A2 : any two lines meet in a unique point **)
Lemma a2_exist : forall (l1 l2:Line), {A:Point | Incid A l1 /\ Incid A l2}.
Proof.
intros.
induction l1;induction l2;
solve_ex_p.
Qed.

(** A3 : there exist four points with no three collinear **)
Lemma a3 : {A:Point & {B :Point & {C:Point & {D :Point |
  (forall l :Line, ~A = B /\ ~A = C /\ ~A = D /\ ~B = C /\ ~B = D /\ ~C = D /\
    (Incid A l /\ Incid B l -> ~Incid C l /\ ~Incid D l)
    /\ (Incid A l /\ Incid C l -> ~Incid B l /\ ~Incid D l)
    /\  (Incid A l /\ Incid D l -> ~Incid C l /\ ~Incid B l)
    /\  (Incid C l /\ Incid B l -> ~Incid A l /\ ~Incid D l)
    /\ (Incid D l /\ Incid B l -> ~Incid C l /\ ~Incid A l)
    /\  (Incid C l /\ Incid D l -> ~Incid B l /\ ~Incid A l))}}}}.
Proof.
exists A.
exists B.
exists C.
exists G.
intros.
split.
intuition;discriminate.
elim l;unfold Incid, Incid_bool;intuition;discriminate.
Qed.

Instance ObjectPointFano : ObjectPoint := {
Point := Point
}.

Instance ProjectiveStructureFano : ProjectiveStructure ObjectPointFano := {
Line := Line;
Incid := Incid;
incid_dec := incid_dec
}.

Instance ProjectiveStructureLEFano : ProjectiveStructureLE ProjectiveStructureFano := {
a1_exist := a1_exist
}.

Instance ProjectiveStructureLEUFano : ProjectiveStructureLEU ProjectiveStructureLEFano := {
uniqueness := uniqueness
}.

Instance PreProjectivePlaneFano : PreProjectivePlane ProjectiveStructureLEUFano := {
a2_exist := a2_exist
}.

Instance ProjectivePlaneFano : ProjectivePlane PreProjectivePlaneFano := {
a3 := a3
}.

End s_fanoPlaneModel.

Module fano_plane_inst : fano_plane

with Definition Point:= Point
with Definition A:= A
with Definition B:= B
with Definition C:= C
with Definition D:= D
with Definition E:= E
with Definition F:= F
with Definition G := G

with Definition Line:= Line

with Definition ABF := ABF
with Definition BCD := BCD
with Definition CAE := CAE
with Definition ADG := ADG
with Definition BEG := BEG
with Definition CFG := CFG
with Definition DEF := DEF
with Definition Incid := Incid
.

Definition Point:= Point.

Definition A:= A.
Definition B:= B.
Definition C:= C.
Definition D:= D.
Definition E:= E.
Definition F:= F.
Definition G := G.

Definition Line:= Line.

Definition ABF := ABF.
Definition BCD := BCD.
Definition CAE := CAE.
Definition ADG := ADG.
Definition BEG := BEG.
Definition CFG := CFG.
Definition DEF := DEF.

Definition Incid := Incid.

Lemma is_only_7_pts : forall P:Point, {P=A}+{P=B}+{P=C}+{P=D}+{P=E}+{P=F}+{P=G}.
Proof.
intros.
elim P;
intuition.
Qed.

Lemma is_only_7_lines : forall P:Line, {P=ABF}+{P=BCD}+{P=CAE}+{P=ADG}+{P=BEG}+{P=CFG}+{P=DEF}.
Proof.
intros.
elim P;
intuition.
Qed.

Definition is_fano_plane A B C D E F G ABF BCD CAE ADG BEG CFG DEF :=
(~A=B /\ ~A=C /\ ~A=D /\ ~A=E /\ ~A=F /\ ~A=G /\
~B=C /\ ~B=D /\ ~B=E /\ ~B=F /\ ~B=G /\
~C=D /\ ~C=E /\ ~C=F /\ ~C=G /\
~D=E /\ ~D=F /\ ~D=G /\
~E=F /\ ~E=G /\
~F=G) /\
(ABF<>BCD /\ ABF <>CAE /\ ABF <>ADG /\ ABF<>BEG /\ ABF<>CFG /\ ABF<>DEF /\
BCD<>CAE /\ BCD<>ADG /\ BCD<>BEG /\ BCD<>CFG /\ BCD<>DEF /\
CAE<>ADG /\ CAE<>BEG /\ CAE<>CFG /\ CAE<>DEF /\
ADG<>BEG /\ ADG<>CFG /\ADG<>DEF /\
BEG<>CFG /\ BEG<>DEF /\
CFG<>DEF )/\

( Incid A ABF /\ Incid B ABF /\ ~ Incid C ABF /\ ~ Incid D ABF /\ ~ Incid E ABF /\ Incid F ABF /\ ~ Incid G  ABF /\
 ~ Incid A BCD /\ Incid B BCD /\ Incid C BCD /\ Incid D BCD /\ ~ Incid E BCD /\ ~ Incid F BCD /\ ~ Incid G  BCD /\
 Incid A CAE /\ ~ Incid B CAE /\ Incid C CAE /\ ~ Incid D CAE /\ Incid E CAE /\ ~ Incid F CAE /\ ~ Incid G  CAE /\
 Incid A ADG /\ ~ Incid B ADG /\ ~ Incid C ADG /\ Incid D ADG /\ ~ Incid E ADG /\ ~ Incid F ADG /\ Incid G  ADG /\
 ~ Incid A BEG /\ Incid B BEG /\ ~ Incid C BEG /\ ~ Incid D BEG /\  Incid E BEG /\ ~ Incid F BEG /\ Incid G  BEG /\
 ~ Incid A CFG /\ ~ Incid B CFG /\ Incid C CFG /\ ~ Incid D CFG /\ ~ Incid E CFG /\ Incid F CFG /\ Incid G  CFG /\
 ~ Incid A DEF /\ ~ Incid B DEF /\ ~ Incid C DEF /\ Incid D DEF /\ Incid E DEF /\ Incid F DEF /\ ~ Incid G  DEF).

Lemma is_fano_plane_inst :  is_fano_plane A B C D E F G ABF BCD CAE ADG BEG CFG DEF.
Proof.
unfold is_fano_plane.
repeat split; try discriminate.
Qed.

End fano_plane_inst.

Module Import Desargues := Desargues fano_plane_inst.

Definition on_line := fun A B C l => Incid A l /\ Incid B l /\ Incid C l.
Definition collinear A B C :=  exists l, Incid A l /\ Incid B l /\ Incid C l.

Theorem Desargues_fano :
forall O P Q R P' Q' R' alpha beta gamma lP lQ lR lPQ lPR lQR lP'Q' lP'R' lQ'R',
((on_line P Q gamma lPQ) /\ (on_line P' Q' gamma lP'Q')) /\
((on_line P R beta lPR) /\ (on_line P' R' beta lP'R')) /\
((on_line Q R alpha lQR) /\ (on_line Q' R' alpha lQ'R')) /\
((on_line O P P' lP) /\ (on_line O Q Q' lQ) /\(on_line O R R' lR)) /\
~collinear O P Q /\  ~collinear O P R /\ ~collinear O Q R /\
~collinear P Q R /\ ~collinear P' Q' R' /\ ((P<>P')\/(Q<>Q')\/(R<>R')) ->
collinear alpha beta gamma.
Proof.
intros.
unfold on_line in *; decompose [and] H;clear H.
assert (T:=Desargues.Desargues).
unfold M2.collinear, M2.on_line in *.
unfold fano_plane_inst.Incid,  fano_plane_inst.Line, fano_plane_inst.Point
in *.
apply (T O P Q R P' Q' R' alpha beta gamma lP lQ lR lPQ lPR lQR lP'Q' lP'R' lQ'R').
repeat split;auto.
Qed.


(** * MoreCoq: More About Coq's Tactics *)

Require Export Poly.

(** This chapter introduces several more proof strategies and
    tactics that, together, allow us to prove theorems about the
    functional programs we have been writing. In particular, we'll
    reason about functions that work with natural numbers and lists.

    In particular, we will see:
    - how to use auxiliary lemmas, in both forwards and backwards reasoning;
    - how to reason about data constructors, which are injective and disjoint;
    - how to create a strong induction hypotheses (and when
      strengthening is required); and
    - how to reason by case analysis.
 *)

(* ###################################################### *)
(** * 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 H H0.
  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 perform simplification 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.
  induction l.
    - rewrite H. symmetry. apply rev_involutive.
    - simpl. symmetry. rewrite snoc_rev. rewrite H. 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?
  (* FILL IN HERE *)
*)
(** [] *)

(* ###################################################### *)
(** * 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 n m o p H H0.
  apply trans_eq with m. 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 X x y z l j H H0.
  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 X x y z l j H H0.
  inversion H0.
  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.

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

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

Theorem beq_nat_0_r : forall n,
   beq_nat n 0 = true -> n = 0.
Proof.
  intros n H.
  apply beq_nat_0_l.
  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].

    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'].
    (* Hint: use the plus_n_Sm lemma *)
    - intros m H. SearchAbout nat. inversion H. destruct m.
      + reflexivity.
      + rewrite <- plus_n_Sm in H1.
        rewrite H1.
Admitted.

(* ###################################################### *)
(** * 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 (left as an exercise) 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.
  induction n.
  - destruct m.
    + reflexivity.
    + intros H. inversion H.
  - intros. destruct m.
    + inversion H.
    + simpl in 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. [] *)

(** Here's another illustration of [inversion] and using an
    appropriately general induction hypothesis.  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.

(** It might be tempting to start proving the above theorem
    by introducing [n] and [eq] at the outset.  However, this leads
    to an induction hypothesis that is not strong enough.  Compare
    the above to the following (aborted) attempt: *)

Theorem length_snoc_bad : forall (X : Type) (v : X)
                              (l : list X) (n : nat),
     length l = n ->
     length (snoc l v) = S n.
Proof.
  intros X v l n eq. induction l as [| v' l'].

  Case "l = []".
    rewrite <- eq. reflexivity.

  Case "l = v' :: l'".
    simpl. destruct n as [| n'].
    SCase "n = 0". inversion eq.
    SCase "n = S n'".
      apply f_equal. Abort. (* apply IHl'. *) (* The IH doesn't apply! *)

(** As in the double examples, the problem is that by
    introducing [n] before doing induction on [l], the induction
    hypothesis is specialized to one particular natural number, namely
    [n].  In the induction case, however, we need to be able to use
    the induction hypothesis on some other natural number [n'].
    Retaining the more general form of the induction hypothesis thus
    gives us more flexibility.

    In general, a good rule of thumb is to make the induction hypothesis
    as general as possible. *)

(** **** 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 n X l H.
  generalize dependent n.
  induction l as [|h t].
    - intros. simpl. reflexivity.
    - intros. simpl in H. destruct n.
      + inversion H.
      + inversion H. apply IHt in H1. apply IHt. rewrite <- pred_Sn. 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 n X v l.
  generalize dependent n.
  induction l.
    - intros. simpl. rewrite <- H. simpl. reflexivity.
    - intros. destruct n.
      + inversion H.
      + simpl in H. inversion H. rewrite H1. simpl. apply IHl in H1.
        rewrite H1. reflexivity.
Qed.

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

Lemma length_S: forall (X: Type) (x : X) (l: list X), length (x :: l) = S (length l).
Proof. intros. simpl. reflexivity. Qed.

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.
  induction l1.
    - intros. simpl. destruct l2.
      + simpl. rewrite <- H. simpl. reflexivity.
      + rewrite nil_app in H.
        rewrite length_S in H. apply H.
    - intros. simpl in H.
Admitted.

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

Theorem app_length_twice : forall (X:Type) (n:nat) (l:list X),
     length l = n ->
     length (l ++ l) = n + n.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Exercise: 3 stars, optional (double_induction)  *)
(** Prove the following principle of induction over two naturals. *)

Theorem double_induction: forall (P : nat -> nat -> Prop),
  P 0 0 ->
  (forall m, P m 0 -> P (S m) 0) ->
  (forall n, P 0 n -> P 0 (S n)) ->
  (forall m n, P m n -> P (S m) (S n)) ->
  forall m n, P m n.
Proof.
  intros P H H0 H1 H2.
  generalize dependent H.
  induction m as [|m'].
    - intros. induction n.
      + apply H.
      + apply H1, IHn.
    - intros. destruct n; [ apply H0 | apply H2]; apply IHm'.
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 X x1 x2 k1 k2 f.
  unfold override. destruct (beq_nat k1 k2).
    - reflexivity.
    - destruct k2; 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.
  induction l.
    - intros. inversion H. reflexivity.
    - destruct l1.
      + intros.
Admitted.

(** 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 f b.
  destruct (f b) eqn:H.
    - destruct b.
      + rewrite H. apply H.
      + destruct (f true) eqn:H'.
        * apply H'.
        * rewrite H. reflexivity.
    - destruct b.
      + destruct (f false) eqn:H'.
        * rewrite H. reflexivity.
        * rewrite H'. reflexivity.
      + rewrite H. apply H.
Qed.

Print bool_fn_applied_thrice.

(** **** 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 X x1 k1 k2 f H.
  unfold override.
  destruct (beq_nat k1 k2) eqn:HH.
    - apply beq_nat_true in HH. rewrite <- HH, H. reflexivity.
    - reflexivity.
Qed.

Print override_same.

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

(** **** Exercise: 3 stars (beq_nat_sym)  *)
Theorem beq_nat_sym : forall (n m : nat),
  beq_nat n m = beq_nat m n.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** **** 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.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** **** 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]? When is this property true?

    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 :=
(* FILL IN HERE *) admit.

Theorem split_combine : split_combine_statement.
Proof.
(* FILL IN HERE *) Admitted.

(** [] *)

(** **** 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.
  (* FILL IN HERE *) 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. Admitted.
(** [] *)

(** **** 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 theorem [existsb_existsb'] that [existsb'] and [existsb] have
    the same behavior.
*)


(**********************************************************************)
(* Equations                                                          *)
(* Copyright (c) 2009-2016 Matthieu Sozeau <matthieu.sozeau@inria.fr> *)
(**********************************************************************)
(* This file is distributed under the terms of the                    *)
(* GNU Lesser General Public License Version 2.1                      *)
(**********************************************************************)

(** An example development of the [fin] datatype using [equations]. *)

Require Import Coq.Program.Program Equations.Equations.

(** [fin n] is the type of naturals smaller than [n]. *)

Inductive fin : nat -> Set :=
| fz : forall {n}, fin (S n)
| fs : forall {n}, fin n -> fin (S n).

(** We can inject it into [nat]. *)

Equations(nocomp) fog {n} (f : fin n) : nat :=
fog {n:=?(S n)} (fz n) := 0 ;
fog (fs n f) := S (fog f).

(** The injection preserves the number: *)
Require Import FunctionalInduction.

Lemma fog_inj {n} (f : fin n) : fog f < n.
Proof with auto with arith. intros.
  depind f; simp fog...
Qed.

(** Of course it has an inverse. *)

Equations(nocomp) gof n : fin (S n) :=
gof O := fz ;
gof (S n) := fs (gof n).

Lemma fog_gof n : fog (gof n) = n.
Proof with auto with arith. intros.
  funind (gof n) gofn; simp fog gof...
Qed.

(** Let's do some arithmetic on [fin] *)

(* Equations_nocomp fin_plus {n m} (x : fin n) (y : fin m) : fin (n + m) := *)
(* fin_plus ?(S n) ?(S m) (fz n) (fz m) := fz ; *)
(* fin_plus ?(S n) ?(S m) (fs n x) y := fs (fin_plus x y) ; *)
(* fin_plus ?(S n) ?(S m) (fz n) (fs m y) := fs (fin_plus fz y).  *)

(** Won't pass the guardness check which diverges anyway. *)

Inductive finle : forall (n : nat) (x : fin n) (y : fin n), Prop :=
| leqz : forall {n j}, finle (S n) fz j
| leqs : forall {n i j}, finle n i j -> finle (S n) (fs i) (fs j).

Scheme finle_ind_dep := Induction for finle Sort Prop.

Instance finle_ind_pack n x y : DependentEliminationPackage (finle n x y) :=
  { elim_type := _ ; elim := finle_ind_dep }.

Arguments finle {n}.

Require Vectors.Vector.
Arguments Vector.nil {A}.
Arguments Vector.cons {A} _ {n}.
Notation vnil := Vector.nil.
Notation vcons := Vector.cons.

Equations(nocomp) nth {A} {n} (v : Vector.t A n) (f : fin n) : A :=
nth (vcons a _ v) fz := a ;
nth (vcons a _ v) (fs n f) := nth v f.

Equations(nocomp) tabulate {A} {n} (f : fin n -> A) : Vector.t A n :=
tabulate {n:=O} f := vnil ;
tabulate {n:=(S n)} f := vcons (f fz) (tabulate (f ∘ fs)).

(** NoConfusion For [fin]. *)

Derive NoConfusion for fin.

(** [Below] recursor for [fin]. *)

Equations(nocomp noind) Below_fin (P : forall n, fin n -> Type) {n} (v : fin n) : Type :=
Below_fin P fz := unit ;
Below_fin P (fs n f) := (P n f * Below_fin P f)%type.

Hint Rewrite Below_fin_equation_2 (* Below_fin_equation_3 *) : Below.

Equations(nocomp noeqns noind) below_fin (P : forall n, fin n -> Type)
  (step : forall n (v : fin n), Below_fin P v -> P n v)
  {n} (v : fin n) : Below_fin P v :=
below_fin P step fz := tt ;
below_fin P step (fs n f) :=
  let bf := below_fin P step f in
    (step n f bf, bf).

Global Opaque Below_fin.

Definition rec_fin (P : forall n, fin n -> Type) {n} v
  (step : forall n (v : fin n), Below_fin P v -> P n v) : P n v :=
  step n v (below_fin P step v).

Import Equations.Below.

Instance fin_Recursor n : Recursor (fin n) :=
  { rec_type := fun v => forall (P : forall n, fin n -> Type) step, P n v;
    rec := fun v P step => rec_fin P v step }.


(***************************************************************************
* Safety for STLC in Wright & Felleisen style - Definitions                *
* Arthur Chargueraud, July 2007                                            *
***************************************************************************)

Set Implicit Arguments.
Require Import LibLN.

(** Grammar of types. *)

Inductive typ : Set :=
  | typ_var   : var -> typ
  | typ_arrow : typ -> typ -> typ.

(** Grammar of pre-terms. *)

Inductive trm : Set :=
  | trm_bvar : nat -> trm
  | trm_fvar : var -> trm
  | trm_abs  : trm -> trm
  | trm_app  : trm -> trm -> trm.

(** Opening up abstractions *)

Fixpoint open_rec (k : nat) (u : trm) (t : trm) {struct t} : trm :=
  match t with
  | trm_bvar i    => If k = i then u else (trm_bvar i)
  | trm_fvar x    => trm_fvar x
  | trm_abs t1    => trm_abs (open_rec (S k) u t1)
  | trm_app t1 t2 => trm_app (open_rec k u t1) (open_rec k u t2)
  end.

Definition open t u := open_rec 0 u t.

Notation "{ k ~> u } t" := (open_rec k u t) (at level 67).
Notation "t ^^ u" := (open t u) (at level 67).
Notation "t ^ x" := (open t (trm_fvar x)).

(** Terms are locally-closed pre-terms *)

Inductive term : trm -> Prop :=
  | term_var : forall x,
      term (trm_fvar x)
  | term_abs : forall L t1,
      (forall x, x \notin L -> term (t1 ^ x)) ->
      term (trm_abs t1)
  | term_app : forall t1 t2,
      term t1 ->
      term t2 ->
      term (trm_app t1 t2).

(** Environment is an associative list mapping variables to types. *)

Definition env := LibEnv.env typ.

(** Typing relation *)

Reserved Notation "E |= t ~: T" (at level 69).

Inductive typing : env -> trm -> typ -> Prop :=
  | typing_var : forall E x T,
      ok E ->
      binds x T E ->
      E |= (trm_fvar x) ~: T
  | typing_abs : forall L E U T t1,
      (forall x, x \notin L ->
        (E & x ~ U) |= t1 ^ x ~: T) ->
      E |= (trm_abs t1) ~: (typ_arrow U T)
  | typing_app : forall S T E t1 t2,
      E |= t1 ~: (typ_arrow S T) ->
      E |= t2 ~: S ->
      E |= (trm_app t1 t2) ~: T

where "E |= t ~: T" := (typing E t T).

(** Definition of values (only abstractions are values) *)

Inductive value : trm -> Prop :=
  | value_abs : forall t1,
      term (trm_abs t1) -> value (trm_abs t1).

(** Reduction contexts *)

Inductive ctx : Set :=
  | ctx_hole : ctx
  | ctx_app_1 : forall (C : ctx) t2,
      term t2 -> ctx
  | ctx_app_2 : forall t1 (C : ctx),
      value t1 -> ctx.

Fixpoint ctx_of (C : ctx) (t : trm) {struct C} : trm :=
  match C with
  | ctx_hole         => t
  | ctx_app_1 C t2 _ => trm_app (ctx_of C t) t2
  | ctx_app_2 t1 C _ => trm_app t1 (ctx_of C t)
  end.

(** Reduction relation - one step in call-by-value *)

Inductive red : trm -> trm -> Prop :=
  | red_beta : forall t1 t2,
      term (trm_abs t1) ->
      value t2 ->
      red (trm_app (trm_abs t1) t2) (t1 ^^ t2)
  | red_ctx : forall C t t',
      red t t' ->
      red (ctx_of C t) (ctx_of C t').

Notation "t --> t'" := (red t t') (at level 68).

(** Goal is to prove preservation and progress *)

Definition preservation := forall E t t' T,
  E |= t ~: T ->
  t --> t' ->
  E |= t' ~: T.

Definition progress := forall t T,
  empty |= t ~: T ->
     value t
  \/ exists t', t --> t'.


(**********************************************************************

 Categories enriched over posets

 In this file, we study categories enriched over posets. We provide
 an elementary definition for both enrichments of categories and of
 functors and we prove the equivalence of these notions with the
 general notion of enrichments via the cartesian monoidal category of
 posets.

 Enrichments of posets for categories means that every hom-set is
 equipped with the structure of a poset and that composition is
 monotone, while enrichment for functors means that the action on
 morphisms is monotone.

 Contents
 1. The monoidal category is faithful
 2. Elementary definition of poset enrichments
 3. Equivalence of enrichments with the elementary definition
 4. Elementary definition of poset enriched functors
 5. Equivalence of functor enrichments with elementary definition

 **********************************************************************)
Require Import UniMath.Foundations.All.
Require Import UniMath.MoreFoundations.All.
Require Import UniMath.Combinatorics.Posets.
Require Import UniMath.CategoryTheory.Core.Categories.
Require Import UniMath.CategoryTheory.Core.Isos.
Require Import UniMath.CategoryTheory.Core.Functors.
Require Import UniMath.CategoryTheory.Core.NaturalTransformations.
Require Import UniMath.CategoryTheory.Core.Univalence.
Require Import UniMath.CategoryTheory.DisplayedCats.Examples.CategoryOfPosets.
Require Import UniMath.CategoryTheory.Monoidal.WhiskeredBifunctors.
Require Import UniMath.CategoryTheory.Monoidal.Categories.
Require Import UniMath.CategoryTheory.Monoidal.Structure.Cartesian.
Require Import UniMath.CategoryTheory.Monoidal.Examples.PosetsMonoidal.
Require Import UniMath.CategoryTheory.EnrichedCats.Enrichment.
Require Import UniMath.CategoryTheory.EnrichedCats.EnrichmentFunctor.

Local Open Scope cat.

(**
 1. The monoidal category is faithful
 *)
Definition poset_faithful_moncat
  : faithful_moncat poset_monoidal_cat.
Proof.
  intros R₁ R₂ f g p.
  use eq_monotone_function.
  intro x.
  assert (is_monotone unit_PartialOrder (pr2 R₁) (λ _, x)) as H.
  {
    intros w₁ w₂ q.
    apply refl_PartialOrder.
  }
  exact (eqtohomot (maponpaths pr1 (p ((λ _, x) ,, H))) tt).
Qed.

(**
 2. Elementary definition of poset enrichments
 *)
Definition poset_enrichment_data
           (C : category)
  : UU
  := ∏ (x y : C), PartialOrder (homset x y).

Definition poset_enrichment_laws
           {C : category}
           (PEC : poset_enrichment_data C)
  : UU
  := (∏ (x y z : C)
        (f₁ f₂ : x --> y)
        (g : y --> z)
        (p : PEC x y f₁ f₂),
      PEC x z (f₁ · g) (f₂ · g))
     ×
     (∏ (x y z : C)
        (f : x --> y)
        (g₁ g₂ : y --> z)
        (p : PEC y z g₁ g₂),
      PEC x z (f · g₁) (f · g₂)).

Proposition isaprop_poset_enrichment_laws
            {C : category}
            (PEC : poset_enrichment_data C)
  : isaprop (poset_enrichment_laws PEC).
Proof.
  use isapropdirprod ; (repeat (use impred ; intro)) ; apply propproperty.
Qed.

Definition poset_enrichment
           (C : category)
  : UU
  := ∑ (PEC : poset_enrichment_data C), poset_enrichment_laws PEC.

Definition poset_enrichment_hom_poset
           {C : category}
           (PEC : poset_enrichment C)
           (x y : C)
  : PartialOrder (x --> y ,, homset_property C x y)
  := pr1 PEC x y.

Coercion poset_enrichment_hom_poset : poset_enrichment >-> Funclass.

Proposition poset_enrichment_comp_l
            {C : category}
            (PEC : poset_enrichment C)
            {x y z : C}
            {f₁ f₂ : x --> y}
            (g : y --> z)
            (p : PEC x y f₁ f₂)
  : PEC x z (f₁ · g) (f₂ · g).
Proof.
  exact (pr12 PEC x y z f₁ f₂ g p).
Qed.

Proposition poset_enrichment_comp_r
            {C : category}
            (PEC : poset_enrichment C)
            {x y z : C}
            (f : x --> y)
            {g₁ g₂ : y --> z}
            (p : PEC y z g₁ g₂)
  : PEC x z (f · g₁) (f · g₂).
Proof.
  exact (pr22 PEC x y z f g₁ g₂ p).
Qed.

(**
 3. Equivalence of enrichments with the elementary definition
 *)
Section MakePosetEnrichment.
  Context (C : category)
          (PEC : poset_enrichment C).

  Definition make_enrichment_over_poset_data
    : enrichment_data C poset_monoidal_cat.
  Proof.
    simple refine (_ ,, _ ,, _ ,, _ ,, _).
    - exact (λ x y, _ ,, PEC x y).
    - refine (λ x, (λ _, identity _) ,, _).
      abstract
        (cbn ; intros t₁ t₂ p ;
         apply refl_PartialOrder).
    - simple refine (λ x y z, _ ,, _) ; cbn in *.
      + exact (λ fg, pr2 fg · pr1 fg).
      + abstract
          (intros fg₁ fg₂ p ; cbn in * ;
           exact (trans_PartialOrder
                    _
                    (poset_enrichment_comp_l PEC _ (pr2 p))
                    (poset_enrichment_comp_r PEC _ (pr1 p)))).
    - refine (λ x y f, (λ _, f) ,, _).
      abstract
        (intros t₁ t₂ p ; cbn in * ;
         apply refl_PartialOrder).
    - exact (λ x y f, pr1 f tt).
  Defined.

  Proposition make_enrichment_over_poset_laws
    : enrichment_laws make_enrichment_over_poset_data.
  Proof.
    repeat split.
    - intros x y.
      use eq_monotone_function.
      intro a ; cbn.
      rewrite id_right.
      apply idpath.
    - intros x y.
      use eq_monotone_function.
      intro a ; cbn.
      rewrite id_left.
      apply idpath.
    - intros w x y z.
      use eq_monotone_function.
      intro a ; cbn.
      rewrite assoc.
      apply idpath.
    - intros x y f.
      use eq_monotone_function.
      intro a ; cbn in *.
      apply maponpaths.
      apply isapropunit.
  Qed.

  Definition make_enrichment_over_poset
    : enrichment C poset_monoidal_cat
    := make_enrichment_over_poset_data ,, make_enrichment_over_poset_laws.
End MakePosetEnrichment.

Section FromPosetEnrichment.
  Context (C : category)
          (E : enrichment C poset_monoidal_cat).

  Definition make_poset_enrichment_rel
             (x y : C)
    : hrel (x --> y).
  Proof.
    refine (λ f g, _).
    use make_hProp.
    - exact (pr12 (E ⦃ x , y ⦄)
                  (pr1 (enriched_from_arr E f) tt)
                  (pr1 (enriched_from_arr E g) tt)).
    - apply (pr12 (E ⦃ x , y ⦄)).
  Defined.

  Definition make_poset_enrichment_data
    : poset_enrichment_data C.
  Proof.
    intros x y.
    simple refine (_ ,, ((_ ,, _) ,, _)).
    - exact (make_poset_enrichment_rel x y).
    - intros f g h p q.
      exact (trans_PartialOrder (pr2 (E ⦃ x , y ⦄)) p q).
    - intros f.
      exact (refl_PartialOrder (pr2 (E ⦃ x , y ⦄)) _).
    - cbn.
      intros f g p q.
      rewrite <- (enriched_to_from_arr E f).
      rewrite <- (enriched_to_from_arr E g).
      apply maponpaths.
      use eq_monotone_function.
      intro w.
      induction w.
      exact (antisymm_PartialOrder (pr2 (E ⦃ x , y ⦄)) p q).
  Defined.

  Proposition make_poset_enrichment_laws
    : poset_enrichment_laws make_poset_enrichment_data.
  Proof.
    repeat split.
    - intros x y z f₁ f₂ g p ; cbn.
      rewrite !enriched_from_arr_comp ; cbn.
      pose (Ef₁ := enriched_from_arr E f₁ : monotone_function _ _).
      pose (Ef₂ := enriched_from_arr E f₂ : monotone_function _ _).
      pose (Eg := enriched_from_arr E g : monotone_function _ _).
      use (pr2 (enriched_comp E x y z) (Eg tt ,, Ef₁ tt) (Eg tt ,, Ef₂ tt)).
      split.
      + apply refl_PartialOrder.
      + exact p.
    - intros x y z f g₁ g₂ p ;cbn.
      rewrite !enriched_from_arr_comp ; cbn.
      pose (Ef := enriched_from_arr E f : monotone_function _ _).
      pose (Eg₁ := enriched_from_arr E g₁ : monotone_function _ _).
      pose (Eg₂ := enriched_from_arr E g₂ : monotone_function _ _).
      use (pr2 (enriched_comp E x y z) (Eg₁ tt ,, Ef tt) (Eg₂ tt ,, Ef tt)).
      split.
      + exact p.
      + apply refl_PartialOrder.
  Qed.

  Definition make_poset_enrichment
    : poset_enrichment C
    := make_poset_enrichment_data ,, make_poset_enrichment_laws.
End FromPosetEnrichment.

Section EnrichmentOverPosetInverse.
  Context {C : category}
          (E : enrichment C poset_monoidal_cat).

  Definition enrichment_over_poset_weq_poset_enrichment_inv_iso
             (x y : C)
    : z_iso
        ((pr11 (make_enrichment_over_poset C (make_poset_enrichment C E))) x y)
        (E ⦃ x , y ⦄).
  Proof.
    use make_z_iso ; cbn.
    - simple refine (_ ,, _).
      + exact (λ f, pr1 (enriched_from_arr E f) tt).
      + abstract
          (cbn ;
           intros f g p ;
           apply p).
    - simple refine (_ ,, _).
      + refine (λ f, enriched_to_arr E _).
        simple refine (_ ,, _).
        * exact (λ _, f).
        * abstract
            (cbn ;
             intros t₁ t₂ p ;
             apply refl_PartialOrder).
      + abstract
          (intros f₁ f₂ p ; cbn ;
           rewrite !enriched_from_to_arr ; cbn ;
           exact p).
    - split.
      + abstract
          (use eq_monotone_function ;
           intro f ; cbn in * ;
           refine (_ @ enriched_to_from_arr E f) ;
           apply maponpaths ;
           use eq_monotone_function ;
           intro t ; cbn ;
           apply maponpaths ;
           apply isapropunit).
      + abstract
          (use eq_monotone_function ;
           intro f ; cbn in * ;
           assert (is_monotone unit_PartialOrder (pr2 (E ⦃ x, y ⦄)) (λ _ : unit, f)) as H ;
           [ intros t₁ t₂ p ;
             apply refl_PartialOrder
           | ] ;
           refine (_ @ eqtohomot (maponpaths pr1 (enriched_from_to_arr E (_ ,, H))) tt) ;
           cbn ;
           apply maponpaths_2 ;
           do 3 apply maponpaths ;
           apply isaprop_is_monotone).
  Defined.

  Definition enrichment_over_poset_weq_poset_enrichment_inv_1
    : make_enrichment_over_poset C (make_poset_enrichment C E) = E.
  Proof.
    use subtypePath.
    {
      intro.
      apply isaprop_enrichment_laws.
    }
    use (invweq (total2_paths_equiv _ _ _)).
    use (invmap (enrichment_data_hom_path _ _ _)).
    {
      exact is_univalent_category_of_posets.
    }
    simple refine (_ ,, _ ,, _ ,, _ ,, _).
    - exact enrichment_over_poset_weq_poset_enrichment_inv_iso.
    - abstract
        (intro x ;
         use eq_monotone_function ;
         intro w ; cbn ;
         rewrite enriched_from_arr_id ;
         apply maponpaths ;
         apply isapropunit).
    - abstract
        (intros x y z ;
         use eq_monotone_function ;
         intro w ; cbn ;
         rewrite enriched_from_arr_comp ; cbn ;
         apply idpath).
    - abstract
        (intros x y f ;
         use eq_monotone_function ;
         intro w ; cbn ;
         apply maponpaths ;
         apply isapropunit).
    - abstract
        (intros x y f ;
         cbn ;
         refine (!_) ;
         refine (_ @ enriched_to_from_arr E (pr1 f tt)) ;
         apply maponpaths ;
         use eq_monotone_function ;
         intro w ; cbn ;
         induction w ;
         apply idpath).
  Defined.
End EnrichmentOverPosetInverse.

Definition enrichment_over_poset_weq_poset_enrichment_inv_2
           {C : category}
           (E : poset_enrichment C)
  : make_poset_enrichment C (make_enrichment_over_poset C E) = E.
Proof.
  use subtypePath.
  {
    intro.
    apply isaprop_poset_enrichment_laws.
  }
  use funextsec ; intro x.
  use funextsec ; intro y.
  use subtypePath.
  {
    intro.
    apply isaprop_isPartialOrder.
  }
  apply idpath.
Qed.

Definition enrichment_over_poset_weq_poset_enrichment
           (C : category)
  : enrichment C poset_monoidal_cat ≃ poset_enrichment C.
Proof.
  use weq_iso.
  - exact (make_poset_enrichment C).
  - exact (make_enrichment_over_poset C).
  - exact enrichment_over_poset_weq_poset_enrichment_inv_1.
  - exact enrichment_over_poset_weq_poset_enrichment_inv_2.
Defined.

(**
 4. Elementary definition of poset enriched functors
 *)
Definition functor_poset_enrichment
           {C₁ C₂ : category}
           (P₁ : poset_enrichment C₁)
           (P₂ : poset_enrichment C₂)
           (F : C₁ ⟶ C₂)
  : UU
  := ∏ (x y : C₁)
       (f g : x --> y),
     P₁ x y f g → P₂ (F x) (F y) (#F f) (#F g).

(**
 5. Equivalence of functor enrichments with elementary definition
 *)
Definition make_functor_poset_enrichment
           {C₁ C₂ : category}
           (P₁ : poset_enrichment C₁)
           (P₂ : poset_enrichment C₂)
           (F : C₁ ⟶ C₂)
           (HF : functor_enrichment
                   F
                   (make_enrichment_over_poset C₁ P₁)
                   (make_enrichment_over_poset C₂ P₂))
  : functor_poset_enrichment P₁ P₂ F.
Proof.
  intros x y f g p.
  pose (eqtohomot (maponpaths pr1 (functor_enrichment_from_arr HF f)) tt) as pf.
  pose (eqtohomot (maponpaths pr1 (functor_enrichment_from_arr HF g)) tt) as pg.
  cbn in pf, pg.
  rewrite pf, pg.
  exact (pr2 (HF x y) f g p).
Qed.

Definition make_functor_enrichment_over_poset
           {C₁ C₂ : category}
           (P₁ : poset_enrichment C₁)
           (P₂ : poset_enrichment C₂)
           (F : C₁ ⟶ C₂)
           (HF : functor_poset_enrichment P₁ P₂ F)
  : functor_enrichment
      F
      (make_enrichment_over_poset C₁ P₁)
      (make_enrichment_over_poset C₂ P₂).
Proof.
  simple refine (_ ,, _).
  - refine (λ x y, (λ f, #F f) ,, λ f g p, _).
    exact (HF x y f g p).
  - repeat split.
    + abstract
        (intros x ;
         use eq_monotone_function ;
         intro w ; cbn ;
         apply functor_id).
    + abstract
        (intros x y z ;
         use eq_monotone_function ;
         intro w ; cbn ;
         apply functor_comp).
    + abstract
        (intros x y z ;
         use eq_monotone_function ;
         intro w ; cbn ;
         apply idpath).
Defined.

Definition functor_enrichment_over_poset_weq_poset_enrichment
           {C₁ C₂ : category}
           (P₁ : poset_enrichment C₁)
           (P₂ : poset_enrichment C₂)
           (F : C₁ ⟶ C₂)
  : functor_enrichment
      F
      (make_enrichment_over_poset C₁ P₁)
      (make_enrichment_over_poset C₂ P₂)
    ≃
    functor_poset_enrichment P₁ P₂ F.
Proof.
  use weq_iso.
  - exact (make_functor_poset_enrichment P₁ P₂ F).
  - exact (make_functor_enrichment_over_poset P₁ P₂ F).
  - abstract
      (intro EF ;
       use subtypePath ; [ intro ; apply isaprop_is_functor_enrichment | ] ;
       use funextsec ; intro x ;
       use funextsec ; intro y ;
       use eq_monotone_function ;
       intro f ;
       cbn ;
       exact (eqtohomot (maponpaths pr1 (functor_enrichment_from_arr EF f)) tt)).
  - abstract
      (intros EF ;
       use funextsec ; intro x ;
       use funextsec ; intro y ;
       use funextsec ; intro f ;
       use funextsec ; intro g ;
       use funextsec ; intro p ;
       apply propproperty).
Defined.


Load LFindLoad.
From lfind Require Import LFind.
Unset Printing Notations.
Set Printing Implicit.

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

Inductive lst : Type := Cons : natural -> lst -> lst |  Nil : lst.

Inductive tree : Type := Node : natural -> tree -> tree -> tree |  Leaf : tree.

Inductive Pair : Type := mkpair : natural -> natural -> Pair
with Zlst : Type := zcons : Pair -> Zlst -> Zlst |  znil : Zlst.

Fixpoint drop (drop_arg0 : natural) (drop_arg1 : lst) : lst
           := match drop_arg0, drop_arg1 with
              | x, Nil => Nil
              | Zero, x => x
              | Succ x, Cons y z => drop x z
              end.

Theorem drop_Nil: forall (x: natural), drop x Nil = Nil.
Proof.
  induction x ; simpl; reflexivity.
Qed.

Theorem drop_Cons: forall (x n: natural) (l: lst), drop (Succ x) (Cons n l) = drop x l.
  induction l; induction x; simpl; reflexivity.
Qed.

Theorem drop_Cons_assoc: forall (x1 x2 x3: natural) (l: lst),
    drop x1 (drop x2 (Cons x3 l)) = drop x2 (drop x1 (Cons x3 l)).
Proof.
  induction x1; induction x2; try (simpl; reflexivity).
  + induction l.
    * rewrite 2 drop_Cons. rewrite <- IHx1.
      rewrite IHx2. rewrite 2 drop_Cons.
      induction l.
      - rewrite IHx1. reflexivity.
      - rewrite 3 drop_Nil. reflexivity.
    * simpl. lfind.  reflexivity.
Admitted.

Theorem drop_assoc : forall (x : natural) (y : natural) (z : lst), eq (drop x (drop y z)) (drop y (drop x z)).
Proof.
  induction z.
  + rewrite 2 drop_Cons_assoc. reflexivity.
  + rewrite 3 drop_Nil. reflexivity.
Qed.

Theorem theorem0 : forall (x : natural) (y : natural) (w : natural) (z : lst), eq (drop w (drop x (drop y z))) (drop y (drop x (drop w z))).
Proof.
  intros.
  rewrite (drop_assoc w x).
  rewrite (drop_assoc w y).
  rewrite (drop_assoc x y).
  reflexivity.
Qed.


Require Export Category.
Require Import Common.

Set Universe Polymorphism.
Set Implicit Arguments.
Generalizable All Variables.
Set Asymmetric Patterns.

Local Open Scope category_scope.
Local Open Scope morphism_scope.

Definition UniqueUpToUniqueIsomorphism (C : PreCategory) (P : C -> Type) :=
  forall x (_ : P x) x' (_ : P x'),
    { c : Contr (Morphism C x x')
    | IsIsomorphism (center (Morphism C x x')) }.

(** A terminal object is an object with a unique morphism from every
    other object. *)
Notation IsTerminalObject C x :=
  (forall x' : Object C, Contr (Morphism C x' x)).

Record TerminalObject (C : PreCategory) :=
  {
    TerminalObject_Object :> C;
    TerminalObject_IsTerminalObject :> IsTerminalObject C TerminalObject_Object
  }.

Existing Instance TerminalObject_IsTerminalObject.

(** An initial object is an object with a unique morphism from every
    other object. *)
Notation IsInitialObject C x :=
  (forall x' : Object C, Contr (Morphism C x x')).

Record InitialObject (C : PreCategory) :=
  {
    InitialObject_Object :> C;
    InitialObject_IsInitialObject :> IsInitialObject C InitialObject_Object
  }.

Existing Instance InitialObject_IsInitialObject.

Arguments UniqueUpToUniqueIsomorphism [C] P.

Section CategoryObjectsTheorems.
  Variable C : PreCategory.

  Ltac unique :=
   repeat first [ intro
                 | exists _
                 | exists (center (Morphism C _ _))
                 | etransitivity; [ symmetry | ]; apply contr
                 ].

  (** The terminal object is unique up to unique isomorphism. *)
  Theorem TerminalObjectUnique
  : UniqueUpToUniqueIsomorphism (fun x => IsTerminalObject C x).
  Proof.
    unique.
  Qed.

  (** The initial object is unique up to unique isomorphism. *)
  Theorem InitialObjectUnique
  : UniqueUpToUniqueIsomorphism (fun x => IsInitialObject C x).
  Proof.
    unique.
  Qed.
End CategoryObjectsTheorems.


Theorem LogicBot_10_14_2021 : (forall a b:Prop, ~((a /\ (b <-> b)) <-> ~a)).
intros A B H.
destruct H.
assert (a:A).
apply H0.
intro a.
apply H.
split.
apply a.
apply iff_refl.
apply a.
apply H.
split.
apply a.
apply iff_refl.
apply a.
Optimize Proof.
Defined.

Print LogicBot_10_14_2021.
(*
fun (A B : Prop) (H : A /\ (B <-> B) <-> ~ A) =>
match H with
| conj H0 H1 =>
    let a : A :=
      let H2 : ~ A -> A := fun H2 : ~ A => match H1 H2 with
                                           | conj x _ => x
                                           end in
      H2 (fun a : A => H0 (conj a (iff_refl B)) a) in
    H0 (conj a (iff_refl B)) a
end
*)


Inductive Ord : Set :=
|  zero : Ord
|  succ : Ord -> Ord
| limit : (nat->Ord)->Ord.

Fixpoint nat2ord (n:nat) : Ord :=
 match n with 0 => zero
            | S p => succ (nat2ord p)
 end.

Fixpoint plus (o1 o2:Ord){struct o2} :=
  match o2 with zero => o1
              | succ o2' => succ (plus o1  o2')
              | limit f => limit (fun n => plus o1 (f n))
  end.
Notation  "o1 + o2" := (plus o1 o2):o_scope.
Open Scope o_scope.

Coercion nat2ord : nat >-> Ord.

Definition omega := limit (fun n => n).

Check (succ 23).
Check (omega = 2+omega).

Check (succ (3+5)).

Eval compute in (succ (3+5)).


Require Import Helix.Util.VecUtil.
Require Import Helix.Util.VecSetoid.
Require Import Helix.Util.Misc.
Require Import Helix.HCOL.CarrierType.

Require Import Helix.HCOL.HCOL.
Require Import Helix.HCOL.HCOLImpl.
Require Import Helix.HCOL.THCOL.
Require Import Helix.HCOL.THCOLImpl.

Require Import Coq.Arith.Arith.
Require Import Coq.Arith.Compare_dec.
Require Import Coq.Arith.Peano_dec.
Require Import Coq.Program.Program.

Require Import Helix.Tactics.HelixTactics.

Require Import Coq.Logic.FunctionalExtensionality.

(* CoRN MathClasses *)
Require Import MathClasses.interfaces.abstract_algebra MathClasses.interfaces.orders.
Require Import MathClasses.orders.minmax MathClasses.orders.orders MathClasses.orders.rings.
Require Import MathClasses.theory.rings.

Import VectorNotations.
Open Scope vector_scope.

Section HCOLBreakdown.

  Context `{CAPROPS: CarrierProperties}.
  Add Ring RingA: (stdlib_ring_theory CarrierA).

  Lemma Vmap2Indexed_to_VMap2 `{Setoid A} {n} {a b: vector A n}
        (f:A->A->A)
  :
    Vmap2 f a b = Vmap2Indexed (IgnoreIndex2 f) a b.
  Proof.
    vec_index_equiv i ip.
    rewrite Vnth_Vmap2Indexed.
    rewrite Vnth_map2.
    reflexivity.
  Qed.

  Theorem breakdown_ScalarProd: forall (n:nat) (a v: avector n),
      ScalarProd (a,v) =
      ((Reduction (+) 0) ∘ (BinOp (IgnoreIndex2 mult))) (a,v).
  Proof.
    intros n a v.
    unfold compose, BinOp, Reduction, ScalarProd.
    rewrite Vmap2Indexed_to_VMap2.
    reflexivity.
  Qed.

  Fact breakdown_OScalarProd: forall {h:nat},
      HScalarProd (h:=h)
      =
      ((HReduction  (+) 0) ∘ (HBinOp (IgnoreIndex2 mult))).
  Proof.
    intros h.
    apply HOperator_functional_extensionality; intros v.
    unfold HScalarProd, HReduction, HBinOp.
    unfold vector2pair, compose, Lst, Vectorize.
    apply Vcons_single_elim.
    destruct (Vbreak v).
    apply breakdown_ScalarProd.
  Qed.

  Theorem breakdown_EvalPolynomial: forall (n:nat) (a: avector (S n)) (v: CarrierA),
      EvalPolynomial a v = (
        ScalarProd ∘ (pair a) ∘ (MonomialEnumerator n)
      ) v.
  Proof.
    intros n a v.
    unfold compose.
    induction n.
    - simpl (MonomialEnumerator 0 v).
      rewrite EvalPolynomial_reduce.
      dep_destruct (Vtail a).
      simpl.
      ring.

    - rewrite EvalPolynomial_reduce, MonomialEnumerator_cons, ScalarProd_reduce.
      unfold Ptail.
      rewrite ScalarProd_comm.

      Opaque Scale ScalarProd.
      simpl.
      Transparent Scale ScalarProd.

      rewrite ScalarProduct_hd_descale, IHn, mult_1_r, ScalarProd_comm.
      reflexivity.
  Qed.

  Fact breakdown_OEvalPolynomial: forall (n:nat) (a: avector (S n)),
      HEvalPolynomial a =
      (HScalarProd ∘
                   ((HPrepend  a) ∘
                                  (HMonomialEnumerator n))).
  Proof.
    intros n a.
    apply HOperator_functional_extensionality; intros v.
    unfold HEvalPolynomial, HScalarProd, HPrepend, HMonomialEnumerator.
    unfold vector2pair, compose, Lst, Scalarize.
    rewrite Vcons_single_elim, Vbreak_app.
    apply breakdown_EvalPolynomial.
  Qed.

  Theorem breakdown_TInfinityNorm: forall (n:nat) (v:avector n),
      InfinityNorm (n:=n) v = ((Reduction max 0) ∘ (HPointwise (IgnoreIndex abs))) v.
  Proof.
    intros n v.
    unfold InfinityNorm, Reduction, compose, IgnoreIndex, HPointwise.
    rewrite Vmap_as_Vbuild.
    reflexivity.
  Qed.

  Fact breakdown_OTInfinityNorm:  forall (n:nat),
      HInfinityNorm =
      (HReduction max 0 (i:=n) ∘ (HPointwise (IgnoreIndex abs))).
  Proof.
    intros n.
    apply HOperator_functional_extensionality; intros v.
    apply Vcons_single_elim.
    apply breakdown_TInfinityNorm.
  Qed.

  Theorem breakdown_MonomialEnumerator:
    forall (n:nat) (x: CarrierA),
      MonomialEnumerator n x = Induction (S n) (.*.) 1 x.
  Proof.
    intros n x.
    induction n.
    - reflexivity.
    - rewrite MonomialEnumerator_cons.
      rewrite_clear IHn.
      symmetry.
      rewrite Induction_cons.
      unfold Scale.
      f_equiv.
      setoid_replace (fun x0 : CarrierA => mult x0 x) with (mult x).
      reflexivity.
      +
        intros a b E.
        rewrite E.
        apply mult_comm.
  Qed.

  Fact breakdown_OMonomialEnumerator:
    forall (n:nat),
      HMonomialEnumerator n =
      HInduction (S n) (.*.) 1.
  Proof.
    intros n.
    apply HOperator_functional_extensionality; intros v.
    apply breakdown_MonomialEnumerator.
  Qed.

  Theorem breakdown_ChebyshevDistance:  forall (n:nat) (ab: (avector n)*(avector n)),
      ChebyshevDistance ab = (InfinityNorm ∘ VMinus) ab.
  Proof.
    intros.
    unfold compose, ChebyshevDistance, VMinus.
    destruct ab.
    reflexivity.
  Qed.

  Fact breakdown_OChebyshevDistance:  forall (n:nat),
      HChebyshevDistance n = (HInfinityNorm ∘ HVMinus).
  Proof.
    intros n.
    apply HOperator_functional_extensionality; intros v.
    unfold Lst, compose.
    apply Vcons_single_elim.
    apply breakdown_ChebyshevDistance.
  Qed.

  Theorem breakdown_VMinus:  forall (n:nat) (ab: (avector n)*(avector n)),
      VMinus ab = BinOp (IgnoreIndex2 sub) ab.
  Proof.
    intros.
    unfold VMinus, BinOp.
    break_let.
    unfold sub.
    rewrite Vmap2Indexed_to_VMap2.
    reflexivity.
  Qed.

  Fact breakdown_OVMinus:  forall (n:nat),
      HVMinus = HBinOp (o:=n) (IgnoreIndex2 sub).
  Proof.
    intros n.
    apply HOperator_functional_extensionality; intros v.
    unfold HVMinus.
    unfold vector2pair.
    apply breakdown_VMinus.
  Qed.

  Fact breakdown_OTLess_Base: forall
      {i1 i2 o}
      `{o1pf: !HOperator (o1: avector i1 -> avector o)}
      `{o2pf: !HOperator (o2: avector i2 -> avector o)},
      HTLess o1 o2 = (HBinOp (IgnoreIndex2 Zless) ∘ HCross o1 o2).
  Proof.
    intros i1 i2 o o1 po1 o2 po2.
    apply HOperator_functional_extensionality; intros v.
    unfold HTLess, HBinOp, HCross.
    unfold compose, BinOp.
    cbn.
    rewrite vp2pv.
    repeat break_let.
    unfold vector2pair in Heqp.
    rewrite Heqp in Heqp1.
    tuple_inversion.
    tuple_inversion.
    rewrite Vmap2Indexed_to_VMap2.
    reflexivity.
  Qed.

End HCOLBreakdown.