(** * HoareList: Hoare Logic with Lists *)

Require Export SfLib.

(* ####################################################### *)
(** * Imp Programs with Lists *)

(** There are only so many numeric functions with interesting
    properties that have simple proofs.  (Of course, there are lots of
    interesting functions on numbers and they have many interesting
    properties -- this is the whole field of number theory! -- but
    proving these properties often requires developing a lot of
    supporting lemmas.)  In order to able to write a few more programs
    to reason about, we introduce here an extended version of Imp
    where variables can range over both numbers and lists of numbers.
    The basic operations are extended to also include taking the head
    and tail of lists, and testing lists for nonemptyness.

    To do this, we only need to change the definitions of [state],
    [aexp], [aeval], [bexp], and [beval]. The definitions of [com] and
    [ceval] can be reused verbatim, although we need to copy-and-paste
    them in the context of the new definitions.

    We start by repeating some material from chapter [Imp]. *)

(** ** Repeated Definitions *)

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

Definition beq_id id1 id2 :=
  match (id1, id2) with
    (Id n1, Id n2) => beq_nat n1 n2
  end.

Theorem beq_id_refl : forall i,
  true = beq_id i i.
Proof.
  intros. destruct i.
  apply beq_nat_refl.  Qed.

Theorem beq_id_eq : forall i1 i2,
  true = beq_id i1 i2 -> i1 = i2.
Proof.
  intros i1 i2 H.
  destruct i1. destruct i2.
  apply beq_nat_eq in H. subst.
  reflexivity.  Qed.

Theorem beq_id_false_not_eq : forall i1 i2,
  beq_id i1 i2 = false -> i1 <> i2.
Proof.
  intros i1 i2 H.
  destruct i1. destruct i2.
  apply beq_nat_false in H.
  intros C. apply H. inversion C. reflexivity.  Qed.

Theorem not_eq_beq_id_false : forall i1 i2,
  i1 <> i2 -> beq_id i1 i2 = false.
Proof.
  intros i1 i2 H.
  destruct i1. destruct i2.
  assert (n <> n0).
    intros C. subst. apply H. reflexivity.
  apply not_eq_beq_false. assumption.  Qed.

Definition X : id := Id 0.
Definition Y : id := Id 1.
Definition Z : id := Id 2.

(** ** Extensions *)

(** Now we come to the key changes.

    Rather than evaluating to a [nat], an [aexp] in our new language
    will evaluate to a _value_ -- an element of type [val] -- which
    can be either a [nat] or a list of [nat]s.

    Similarly, [state]s will now map identifiers to [val]s rather than
    [nat]s, so that we can store lists in mutable variables. *)

Inductive val : Type :=
| VNat : nat -> val
| VList : list nat -> val.

Definition state := id -> val.

Definition empty_state : state := fun _ => VNat 0.
Definition update (st : state) (X:id) (v : val) : state :=
  fun X' => if beq_id X X' then v else st X'.

(** Imp does not have a static type system, so nothing prevents the
    programmer from e.g. adding two lists or taking the head of a
    number. We have to decide what to do in such nonsensical
    situations.

    We adopt a simple solution: if an arithmetic function is given a
    list as an argument we treat the list as if it was the number
    [0]. Similarly, if a list function is given a number as an
    argument we treat the number as if it was [nil].  (Cf. Javascript,
    where adding [3] to the empty list evaluates to [3]...)

    The two functions [asnat] and [aslist] interpret [val]s in a
    numeric or a list context; [aeval] calls these whenever it
    evaluates an arithmetic or a list operation.*)

Definition asnat (v : val) : nat :=
  match v with
  | VNat n => n
  | VList _ => 0
  end.

Definition aslist (v : val) : list nat :=
  match v with
  | VNat n => []
  | VList xs => xs
  end.

(** Now we fill in the definitions of abstract syntax and
    evaluation functions for arithmetic and boolean expressions. *)

Inductive aexp : Type :=
  | ANum : nat -> aexp
  | AId : id -> aexp
  | APlus : aexp -> aexp -> aexp
  | AMinus : aexp -> aexp -> aexp
  | AMult : aexp -> aexp -> aexp
  (* Four new cases: *)
  | AHead : aexp -> aexp
  | ATail : aexp -> aexp
  | ACons : aexp -> aexp -> aexp
  | ANil  : aexp.

Tactic Notation "aexp_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "ANum" | Case_aux c "AId" | Case_aux c "APlus"
  | Case_aux c "AMinus" | Case_aux c "AMult"
  | Case_aux c "AHead" | Case_aux c "ATail"
  | Case_aux c "ACons" | Case_aux c "ANil" ].

Definition tail (l : list nat) :=
  match l with
  | x::xs => xs
  | [] => []
  end.

Definition head (l : list nat) :=
  match l with
  | x::xs => x
  | [] => 0
  end.

Fixpoint aeval (st : state) (e : aexp) : val :=
  match e with
  | ANum n => VNat n
  | AId i => st i
  | APlus a1 a2   => VNat (asnat (aeval st a1) + asnat (aeval st a2))
  | AMinus a1 a2  => VNat (asnat (aeval st a1) - asnat (aeval st a2))
  | AMult a1 a2   => VNat (asnat (aeval st a1) * asnat (aeval st a2))
  (* Four new cases: *)
  | ATail a => VList (tail (aslist (aeval st a)))
  | AHead a => VNat (head (aslist (aeval st a)))
  | ACons a1 a2 => VList (asnat (aeval st a1) :: aslist (aeval st a2))
  | ANil => VList []
  end.

(** We extend [bexp]s with an operation to test if a list is nonempty
    and adapt [beval] acordingly. *)

Inductive bexp : Type :=
  | BTrue : bexp
  | BFalse : bexp
  | BEq : aexp -> aexp -> bexp
  | BLe : aexp -> aexp -> bexp
  | BNot : bexp -> bexp
  | BAnd : bexp -> bexp -> bexp
  (* New case: *)
  | BIsCons : aexp -> bexp.

Tactic Notation "bexp_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "BTrue" | Case_aux c "BFalse" | Case_aux c "BEq"
  | Case_aux c "BLe" | Case_aux c "BNot" | Case_aux c "BAnd"
  | Case_aux c "BIsCons" ].

Fixpoint beval (st : state) (e : bexp) : bool :=
  match e with
  | BTrue       => true
  | BFalse      => false
  | BEq a1 a2   => beq_nat (asnat (aeval st a1)) (asnat (aeval st a2))
  | BLe a1 a2   => ble_nat (asnat (aeval st a1)) (asnat (aeval st a2))
  | BNot b1     => negb (beval st b1)
  | BAnd b1 b2  => andb (beval st b1) (beval st b2)
  (* New case: *)
  | BIsCons a    => match aslist (aeval st a) with
                     | _::_ => true
                     | [] => false
                   end
  end.

(** ** Repeated Definitions *)

(** Now we need to repeat a little bit of low-level work from Imp.v,
    plus the definitions of [com] and [ceval].  There are no
    interesting changes -- it's just a matter of repeating the same
    definitions, lemmas, and proofs in the context of the new
    definitions of arithmetic and boolean expressions.

    (Is all this cutting and pasting really necessary?  No: Coq
    includes a powerful module system that we could use to abstract
    the repeated definitions with respect to the varying parts.  But
    explaining how it works would distract us from the topic at hand.)
 *)

Theorem update_eq : forall n V st,
  (update st V n) V = n.
Proof.
  intros n V st.
  unfold update.
  rewrite <- beq_id_refl.
  reflexivity.
Qed.

Theorem update_neq : forall V2 V1 n st,
  beq_id V2 V1 = false ->
  (update st V2 n) V1 = (st V1).
Proof.
  intros V2 V1 n st Hneq.
  unfold update.
  rewrite -> Hneq.
  reflexivity. Qed.

Theorem update_shadow : forall x1 x2 k1 k2 (f : state),
   (update  (update f k2 x1) k2 x2) k1 = (update f k2 x2) k1.
Proof.
  intros x1 x2 k1 k2 f.
  unfold update.
  destruct (beq_id k2 k1); reflexivity.  Qed.

Theorem update_same : forall x1 k1 k2 (f : state),
  f k1 = x1 ->
  (update f k1 x1) k2 = f k2.
Proof.
  intros x1 k1 k2 f Heq.
  unfold update. subst.
  remember (beq_id k1 k2) as b.
  destruct b.
  Case "true".
    apply beq_id_eq in Heqb. subst. reflexivity.
  Case "false".
    reflexivity.  Qed.

Theorem update_permute : forall x1 x2 k1 k2 k3 f,
  beq_id k2 k1 = false ->
  (update (update f k2 x1) k1 x2) k3 = (update (update f k1 x2) k2 x1) k3.
Proof.
  intros x1 x2 k1 k2 k3 f H.
  unfold update.
  remember (beq_id k1 k3) as b13.
  remember (beq_id k2 k3) as b23.
  apply beq_id_false_not_eq in H.
  destruct b13; try reflexivity.
  Case "true".
    destruct b23; try reflexivity.
    SCase "true".
      apply beq_id_eq in Heqb13.
      apply beq_id_eq in Heqb23.
      subst. apply ex_falso_quodlibet. apply H. reflexivity.  Qed.

(** We can keep exactly the same old definitions of [com] and
    [ceval]. *)

Inductive com : Type :=
  | CSkip : com
  | CAss : id -> aexp -> com
  | CSeq : com -> com -> com
  | CIf : bexp -> com -> com -> com
  | CWhile : bexp -> com -> com.

Tactic Notation "com_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";"
  | Case_aux c "IFB" | Case_aux c "WHILE" ].

Notation "'SKIP'" :=
  CSkip.
Notation "X '::=' a" :=
  (CAss X a) (at level 60).
Notation "c1 ; c2" :=
  (CSeq c1 c2) (at level 80, right associativity).
Notation "'WHILE' b 'DO' c 'END'" :=
  (CWhile b c) (at level 80, right associativity).
Notation "'IFB' e1 'THEN' e2 'ELSE' e3 'FI'" :=
  (CIf e1 e2 e3) (at level 80, right associativity).

Reserved Notation "c1 '/' st '||' st'" (at level 40, st at level 39).

Inductive ceval : state -> com -> state -> Prop :=
  | E_Skip : forall st,
      SKIP / st || st
  | E_Asgn  : forall st a1 n X,
      aeval st a1 = n ->
      (X ::= a1) / st || (update st X n)
  | E_Seq : forall c1 c2 st st' st'',
      c1 / st  || st' ->
      c2 / st' || st'' ->
      (c1 ; c2) / st || st''
  | E_IfTrue : forall st st' b1 c1 c2,
      beval st b1 = true ->
      c1 / st || st' ->
      (IFB b1 THEN c1 ELSE c2 FI) / st || st'
  | E_IfFalse : forall st st' b1 c1 c2,
      beval st b1 = false ->
      c2 / st || st' ->
      (IFB b1 THEN c1 ELSE c2 FI) / st || st'
  | E_WhileEnd : forall b1 st c1,
      beval st b1 = false ->
      (WHILE b1 DO c1 END) / st || st
  | E_WhileLoop : forall st st' st'' b1 c1,
      beval st b1 = true ->
      c1 / st || st' ->
      (WHILE b1 DO c1 END) / st' || st'' ->
      (WHILE b1 DO c1 END) / st || st''

  where "c1 '/' st '||' st'" := (ceval st c1 st').

Tactic Notation "ceval_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "E_Skip" | Case_aux c "E_Asgn" | Case_aux c "E_Seq"
  | Case_aux c "E_IfTrue" | Case_aux c "E_IfFalse"
  | Case_aux c "E_WhileEnd" | Case_aux c "E_WhileLoop" ].

(* ####################################################### *)
(** * Hoare Rules *)

(** We copy verbatim the Hoare rules from Hoare.v. *)
(**
             ------------------------------ (hoare_asgn)
             {{assn_sub X a Q}} X::=a {{Q}}

             --------------------  (hoare_skip)
             {{ P }} SKIP {{ P }}

               {{ P }} c1 {{ Q }}
               {{ Q }} c2 {{ R }}
              ---------------------  (hoare_seq)
              {{ P }} c1;c2 {{ R }}

              {{P /\  b}} c1 {{Q}}
              {{P /\ ~b}} c2 {{Q}}
      ------------------------------------  (hoare_if)
      {{P}} IFB b THEN c1 ELSE c2 FI {{Q}}

               {{P /\ b}} c {{P}}
        -----------------------------------  (hoare_while)
        {{P}} WHILE b DO c END {{P /\ ~b}}

                {{P'}} c {{Q'}}
                   P ~~> P'
                   Q' ~~> Q
         -----------------------------   (hoare_consequence)
                {{P}} c {{Q}}
*)

Definition Assertion := state -> Prop.

Definition hoare_triple (P:Assertion) (c:com) (Q:Assertion) : Prop :=
  forall st st',
       c / st || st'  ->
       P st  ->
       Q st'.

Notation "{{ P }}  c  {{ Q }}" := (hoare_triple P c Q)
                                  (at level 90, c at next level)
                                  : hoare_spec_scope.
Open Scope hoare_spec_scope.

Definition assn_sub X a Q : Assertion :=
  fun (st : state) =>
    Q (update st X (aeval st a)).

Theorem hoare_asgn : forall Q X a,
  {{assn_sub X a Q}} (X ::= a) {{Q}}.
Proof.
  unfold hoare_triple.
  intros Q X a st st' HE HQ.
  inversion HE. subst.
  unfold assn_sub in HQ. assumption.  Qed.

Theorem hoare_skip : forall P,
     {{P}} SKIP {{P}}.
Proof.
  intros P st st' H HP. inversion H. subst.
  assumption.  Qed.

Theorem hoare_seq : forall P Q R c1 c2,
     {{Q}} c2 {{R}} ->
     {{P}} c1 {{Q}} ->
     {{P}} c1;c2 {{R}}.
Proof.
  intros P Q R c1 c2 H1 H2 st st' H12 Pre.
  inversion H12; subst.
  apply (H1 st'0 st'); try assumption.
  apply (H2 st st'0); assumption. Qed.

Definition bassn b : Assertion :=
  fun st => (beval st b = true).

Lemma bexp_eval_true : forall b st,
  beval st b = true -> (bassn b) st.
Proof.
  intros b st Hbe.
  unfold bassn. assumption.  Qed.

Lemma bexp_eval_false : forall b st,
  beval st b = false -> ~ ((bassn b) st).
Proof.
  intros b st Hbe contra.
  unfold bassn in contra.
  rewrite -> contra in Hbe. inversion Hbe.  Qed.

Theorem hoare_if : forall P Q b c1 c2,
  {{fun st => P st /\ bassn b st}} c1 {{Q}} ->
  {{fun st => P st /\ ~(bassn b st)}} c2 {{Q}} ->
  {{P}} (IFB b THEN c1 ELSE c2 FI) {{Q}}.
Proof.
  intros P Q b c1 c2 HTrue HFalse st st' HE HP.
  inversion HE; subst.
  Case "b is true".
    apply (HTrue st st').
      assumption.
      split. assumption.
             apply bexp_eval_true. assumption.
  Case "b is false".
    apply (HFalse st st').
      assumption.
      split. assumption.
             apply bexp_eval_false. assumption. Qed.

Lemma hoare_while : forall P b c,
  {{fun st => P st /\ bassn b st}} c {{P}} ->
  {{P}} WHILE b DO c END {{fun st => P st /\ ~ (bassn b st)}}.
Proof.
  intros P b c Hhoare st st' He HP.
  (* Like we've seen before, we need to reason by induction
     on He, because, in the "keep looping" case, its hypotheses
     talk about the whole loop instead of just c *)
  remember (WHILE b DO c END) as wcom.
  ceval_cases (induction He) Case; try (inversion Heqwcom); subst.

  Case "E_WhileEnd".
    split. assumption. apply bexp_eval_false.  assumption.

  Case "E_WhileLoop".
    apply IHHe2.  reflexivity.
    apply (Hhoare st st'); try assumption.
      split. assumption. apply bexp_eval_true. assumption.  Qed.

Definition assert_implies (P Q : Assertion) : Prop :=
  forall st, P st -> Q st.

Notation "P ~~> Q" := (assert_implies P Q) (at level 80).
Notation "P <~~> Q" := (P ~~> Q /\ Q ~~> P) (at level 80).

Theorem hoare_consequence_pre : forall (P P' Q : Assertion) c,
  {{P'}} c {{Q}} ->
  P ~~> P' ->
  {{P}} c {{Q}}.
Proof.
  intros P P' Q c Hhoare Himp.
  intros st st' Hc HP. apply (Hhoare st st').
  assumption. apply Himp. assumption. Qed.

Theorem hoare_consequence_post : forall (P Q Q' : Assertion) c,
  {{P}} c {{Q'}} ->
  Q' ~~> Q ->
  {{P}} c {{Q}}.
Proof.
  intros P Q Q' c Hhoare Himp.
  intros st st' Hc HP.
  apply Himp.
  apply (Hhoare st st').
  assumption. assumption. Qed.

Theorem hoare_consequence : forall (P P' Q Q' : Assertion) c,
  {{P'}} c {{Q'}} ->
  P ~~> P' ->
  Q' ~~> Q ->
  {{P}} c {{Q}}.
Proof.
  intros P P' Q Q' c Hht HPP' HQ'Q.
  intros st st' Hc HP.
  apply HQ'Q. apply (Hht st st'). assumption.
  apply HPP'. assumption. Qed.

(* ####################################################### *)
(** ** Reasoning About Programs with Lists *)

(** Now let's look at a formal Hoare Logic proof for a program that
    works with lists.  We will verify the following program, which
    checks if the number [Y] occurs in the list [X], and if so sets
    [Z] to [1]. *)

Definition list_member :=
  WHILE BIsCons (AId X) DO
    IFB (BEq (AId Y) (AHead (AId X))) THEN
      Z ::= (ANum 1)
    ELSE
      SKIP
    FI;
    X ::= ATail (AId X)
  END.

(** The informal proof looks like this:
    {{ X = l /\ Y = n /\ Z = 0 }} =>
    {{ Y = n /\ exists p, p ++ X = l /\ (Z = 1 <-> appears_in n p) }}
  WHILE (BIsCons X)
  DO
      {{ Y = n /\ (exists p, p ++ X = l /\ (Z = 1 <-> appears_in n p))
               /\ (BIsCons X) }}
    IFB (Y == head X) THEN
        {{ Y = n
           /\ (exists p, p ++ X = l /\ (Z = 1 <-> appears_in n p))
           /\ (BIsCons X)
           /\ Y == AHead X }} =>
        {{ Y = n /\ (exists p, p ++ tail X = l
                    /\ (1 = 1 <-> appears_in n p)) }}
      Z ::= 1
        {{ Y = n
           /\ (exists p, p ++ tail X = l /\ (Z = 1 <-> appears_in n p)) }}
    ELSE
        {{ Y = n
           /\ (exists p, p ++ X = l /\ (Z = 1 <-> appears_in n p))
           /\ (BIsCons X)
           /\ ~ (Y == head X) }} =>
        {{ Y = n
           /\ (exists p, p ++ tail X = l /\ (Z = 1 <-> appears_in n p)) }}
      SKIP
        {{ Y = n
           /\ (exists p, p ++ tail X = l /\ (Z = 1 <-> appears_in n p)) }}
    FI;
       {{ Y = n
           /\ (exists p, p ++ tail X = l /\ (Z = 1 <-> appears_in n p)) }}
    X ::= ATail X
        {{ Y = n
           /\ (exists p, p ++ X = l /\ (Z = 1 <-> appears_in n p)) }}
  END
     {{ Y = n
        /\ (exists p, p ++ X = l /\ (Z = 1 <-> appears_in n p))
        /\ ~ (BIsCons X) }} =>
     {{ Z = 1 <-> appears_in n l }}

  The only interesting part of the proof is the choice of loop invariant:
      exists p, p ++ X = l /\ (Z = 1 <-> appears_in n p)
  This states that at each iteration of the loop, the original list
  [l] is equal to the append of the current value of [X] and some
  other list [p] which is not the value of any variable in the
  program, but keeps track of enough information from the original
  state to make the proof go through. (Such a [p] is sometimes called
  a "ghost variable"). *)

(** In order to show that such a list [p] exists, in each iteration we
    add the head of [X] to the _end_ of [p]. This needs the function
    [snoc], from Poly.v. *)

Fixpoint snoc {X:Type} (l:list X) (v:X) : (list X) :=
  match l with
  | nil      =>  [ v ]
  | cons h t => h :: (snoc t v)
  end.

(** The main proof uses several lemmas about [snoc] and [++]. *)

Lemma snoc_equation : forall (A : Type) (h:A) (x y : list A),
  snoc x h ++ y = x ++ h :: y.
Proof.
  intros A h x y.
  induction x.
    Case "x = []". reflexivity.
    Case "x = cons". simpl. rewrite IHx. reflexivity.
Qed.

Lemma appears_in_snoc1 : forall a l,
  appears_in a (snoc l a).
Proof.
  induction l.
    Case "l = []". apply ai_here.
    Case "l = cons". simpl. apply ai_later. apply IHl.
Qed.

Lemma appears_in_snoc2 : forall a b l,
  appears_in a l ->
  appears_in a (snoc l b).
Proof.
  induction l; intros H; inversion H; subst; simpl.
    Case "l = []". apply ai_here.
    Case "l = cons". apply ai_later. apply IHl. assumption.
Qed.

Lemma appears_in_snoc3 : forall a b l,
   appears_in a (snoc l b) ->
   (appears_in a l \/ a = b).
Proof.
   induction l; intros H.
   Case "l = []". inversion H.
     SCase "ai_here". right. reflexivity.
     SCase "ai_later". left. assumption.
   Case "l = cons". inversion H; subst.
     SCase "ai_here". left. apply ai_here.
     SCase "ai_later". destruct (IHl H1).
       left. apply ai_later. assumption.
       right. assumption.
Qed.

Lemma append_singleton_equation : forall (x : nat) l l',
  (l ++ [x]) ++ l' = l ++ x :: l'.
Proof.
  intros x l l'.
  induction l.
    reflexivity.
    simpl. rewrite IHl. reflexivity.
Qed.

Lemma append_nil : forall (A : Type) (l : list A),
  l ++ [] = l.
Proof.
  induction l.
    reflexivity.
    simpl. rewrite IHl. reflexivity.
Qed.

Theorem list_member_correct : forall l n,
  {{ fun st => aslist (st X) = l /\ asnat (st Y) = n /\ asnat (st Z) = 0 }}
  list_member
  {{ fun st => asnat (st Z) = 1 <-> appears_in n l }}.
Proof.
  intros l n.
  eapply hoare_consequence.
  apply hoare_while with (P := fun st =>
     asnat (st Y) = n
     /\ exists p, p ++ aslist (st X) = l
                  /\ (asnat (st Z) = 1 <-> appears_in n p)).
    (* The loop body preserves the invariant: *)
    eapply hoare_seq.
    apply hoare_asgn.
    apply hoare_if.
    Case "If taken".
      eapply hoare_consequence_pre.
      apply hoare_asgn.
      intros st [[[H1 [p [H2 H3]]] H9] H10].
      unfold assn_sub. split.
      (* (st Y) is still n *)
        rewrite update_neq; try reflexivity.
        rewrite update_neq; try reflexivity.
        assumption.
      (* and the interesting part of the invariant is preserved: *)
        (* X has to be a cons *)
        remember (aslist (st X)) as x.
        destruct x as [|h x'].
          unfold bassn in H9. unfold beval in H9. unfold aeval in H9.
          rewrite <- Heqx in H9. inversion H9.

          exists (snoc p h).
          rewrite update_eq.
          unfold aeval. rewrite update_neq; try reflexivity.
          rewrite <- Heqx.
          split.
            rewrite snoc_equation. assumption.

            rewrite update_neq; try reflexivity.
            rewrite update_eq.
            split.
              simpl.
              unfold bassn in H10. unfold beval in H10.
              unfold aeval in H10. rewrite H1 in H10.
              rewrite <- Heqx in H10. simpl in H10.
              rewrite (beq_nat_true _ _ H10).
              intros. apply appears_in_snoc1.

              intros. reflexivity.
    Case "If not taken".
      eapply hoare_consequence_pre. apply hoare_skip.
      unfold assn_sub.
      intros st [[[H1 [p [H2 H3]]] H9] H10].
      split.
      (* (st Y) is still n *)
        rewrite update_neq; try reflexivity.
        assumption.
      (* and the interesting part of the invariant is preserved: *)
        (* X has to be a cons *)
        remember (aslist (st X)) as x.
        destruct x as [|h x'].
          unfold bassn in H9. unfold beval in H9. unfold aeval in H9.
          rewrite <- Heqx in H9. inversion H9.

          exists (snoc p h).
          split.
            rewrite update_eq.
            unfold aeval. rewrite <- Heqx.
            rewrite snoc_equation. assumption.

            rewrite update_neq; try reflexivity.
            split.
              intros. apply appears_in_snoc2. apply H3. assumption.

              intros.  destruct (appears_in_snoc3 _ _ _ H).
              SCase "later".
                inversion H3 as [_ H3'].
                apply H3'. assumption.
              SCase "here (absurd)".
                subst.
                unfold bassn, beval, aeval in H10.
                rewrite not_true_iff_false in H10.
                apply beq_nat_false in H10.
                rewrite <- Heqx in H10. simpl in H10.
                apply ex_falso_quodlibet. apply H10. assumption.
  (* The invariant holds at the start of the loop: *)
  intros st [H1 [H2 H3]].
  rewrite H1. rewrite H2. rewrite H3.
  split.
    reflexivity.
    exists []. split.
      reflexivity.
      split; intros H; inversion H.
  (* At the end of the loop the invariant implies the right thing. *)
  simpl.   intros st [[H1 [p [H2 H3]]] H5].
  (* x must be [] *)
  unfold bassn in H5. unfold beval in H5. unfold aeval in H5.
  destruct (aslist (st X)) as [|h x'].
    rewrite append_nil in H2.
    rewrite <- H2.
    assumption.

    apply ex_falso_quodlibet. apply H5. reflexivity.
Qed.

(** **** Exercise: 3 stars (list_sum) *)
(** Here is a direct definition of the sum of the elements of a list,
    and an Imp program that computes the sum. *)

Definition sum l := fold_right plus 0 l.

Definition sum_program :=
  Y ::= ANum 0;
  WHILE (BIsCons (AId X)) DO
    Y ::= APlus (AId Y) (AHead (AId X)) ;
    X ::= ATail (AId X)
  END.

(** Provide an _informal_ proof of the following specification of
    [sum_program] in the form of a decorated version of the
    program. *)

Definition sum_program_spec := forall l,
  {{ fun st => aslist (st X) = l }}
  sum_program
  {{ fun st => asnat (st Y) = sum l }}.

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

(** **** Exercise: 4 stars (list_reverse) *)
(** Recall the function [rev] from Poly.v, for reversing lists. *)

Fixpoint rev {X:Type} (l:list X) : list X :=
  match l with
  | nil      => []
  | cons h t => snoc (rev t) h
  end.

(** Write an Imp program [list_reverse_program] that reverses
    lists. Formally prove that it satisfies the following
    specification:
    forall l : list nat,
      {{ X =  l /\ Y = nil }}
      list_reverse_program
      {{ Y = rev l }}.
    You may find the lemmas [append_nil] and [rev_equation] useful.
*)

Lemma rev_equation : forall (A : Type) (h : A) (x y : list A),
  rev (h :: x) ++ y = rev x ++ h :: y.
Proof.
  intros. simpl. apply snoc_equation.
Qed.

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

(** Finally, for a bigger example, let's redo the proof of
    [list_member_correct] from above using our new tools.

    Notice that the [verify] tactic leaves subgoals for each
    "interesting" use of [hoare_consequence] -- that is, for each [=>]
    that occurs in the decorated program, except for the ones that can
    be eliminated by repeated application of a few simple automated
    tactics. Each of these implications relies on a fact about lists,
    for example that [l ++ [] = l]. In other words, the Hoare logic
    infrastructure has taken care of the boilerplate reasoning about
    the execution of imperative programs, while the user has to prove
    lemmas that are specific to the problem domain (e.g. lists or
    numbers). *)

(* ####################################################### *)
(** * Formal Decorated Programs *)

(** Again, the definitions are copied verbatim from Hoare.v *)

Inductive dcom : Type :=
  | DCSkip :  Assertion -> dcom
  | DCSeq : dcom -> dcom -> dcom
  | DCAsgn : id -> aexp ->  Assertion -> dcom
  | DCIf : bexp ->  Assertion -> dcom ->  Assertion -> dcom
           -> Assertion-> dcom
  | DCWhile : bexp -> Assertion -> dcom -> Assertion -> dcom
  | DCPre : Assertion -> dcom -> dcom
  | DCPost : dcom -> Assertion -> dcom.

Tactic Notation "dcom_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "Skip" | Case_aux c "Seq" | Case_aux c "Asgn"
  | Case_aux c "If" | Case_aux c "While"
  | Case_aux c "Pre" | Case_aux c "Post" ].

Notation "'SKIP' {{ P }}"
      := (DCSkip P)
      (at level 10) : dcom_scope.
Notation "l '::=' a {{ P }}"
      := (DCAsgn l a P)
      (at level 60, a at next level) : dcom_scope.
Notation "'WHILE' b 'DO' {{ Pbody }} d 'END' {{ Ppost }}"
      := (DCWhile b Pbody d Ppost)
      (at level 80, right associativity) : dcom_scope.
Notation "'IFB' b 'THEN' {{ P }} d 'ELSE' {{ P' }} d' 'FI' {{ Q }}"
      := (DCIf b P d P' d' Q)
      (at level 80, right associativity)  : dcom_scope.
Notation "'=>' {{ P }} d"
      := (DCPre P d)
      (at level 90, right associativity)  : dcom_scope.
Notation "{{ P }} d"
      := (DCPre P d)
      (at level 90)  : dcom_scope.
Notation "d '=>' {{ P }}"
      := (DCPost d P)
      (at level 91, right associativity)  : dcom_scope.
Notation " d ; d' "
      := (DCSeq d d')
      (at level 80, right associativity)  : dcom_scope.

Delimit Scope dcom_scope with dcom.

Example dec_while : dcom := (
  {{ fun st => True }}
  WHILE (BNot (BEq (AId X) (ANum 0)))
  DO
    {{ fun st => True /\ bassn (BNot (BEq (AId X) (ANum 0))) st}}
    X ::= (AMinus (AId X) (ANum 1))
    {{ fun _ => True }}
  END
  {{ fun st => True /\ ~bassn (BNot (BEq (AId X) (ANum 0))) st}} =>
  {{ fun st => asnat (st X) = 0 }}
) % dcom.

Fixpoint extract (d:dcom) : com :=
  match d with
  | DCSkip _           => SKIP
  | DCSeq d1 d2        => (extract d1 ; extract d2)
  | DCAsgn X a _       => X ::= a
  | DCIf b _ d1 _ d2 _ => IFB b THEN extract d1 ELSE extract d2 FI
  | DCWhile b _ d _    => WHILE b DO extract d END
  | DCPre _ d          => extract d
  | DCPost d _         => extract d
  end.

Fixpoint post (d:dcom) : Assertion :=
  match d with
  | DCSkip P                => P
  | DCSeq d1 d2             => post d2
  | DCAsgn X a Q            => Q
  | DCIf  _ _ d1 _ d2 Q     => Q
  | DCWhile b Pbody c Ppost => Ppost
  | DCPre _ d               => post d
  | DCPost c Q              => Q
  end.

Fixpoint pre (d:dcom) : Assertion :=
  match d with
  | DCSkip P                => fun st => True
  | DCSeq c1 c2             => pre c1
  | DCAsgn X a Q            => fun st => True
  | DCIf  _ _ t _ e _       => fun st => True
  | DCWhile b Pbody c Ppost => fun st => True
  | DCPre P c               => P
  | DCPost c Q              => pre c
  end.

Definition dec_correct (d:dcom) :=
  {{pre d}} (extract d) {{post d}}.

Fixpoint verification_conditions (P : Assertion) (d:dcom) : Prop :=
  match d with
  | DCSkip Q          =>
      (P ~~> Q)
  | DCSeq d1 d2      =>
      verification_conditions P d1
      /\ verification_conditions (post d1) d2
  | DCAsgn X a Q      =>
      (P ~~> assn_sub X a Q)
  | DCIf b P1 d1 P2 d2 Q =>
      ((fun st => P st /\ bassn b st) ~~> P1)
      /\ ((fun st => P st /\ ~ (bassn b st)) ~~> P2)
      /\ (Q = post d1) /\ (Q = post d2)
      /\ verification_conditions P1 d1
      /\ verification_conditions P2 d2
  | DCWhile b Pbody d Ppost      =>
      (* post d is the loop invariant and the initial precondition *)
      (P ~~> post d)
      /\ (Pbody = (fun st => post d st /\ bassn b st))
      /\ (Ppost = (fun st => post d st /\ ~(bassn b st)))
      /\ verification_conditions Pbody d
  | DCPre P' d         =>
      (P ~~> P') /\ verification_conditions P' d
  | DCPost d Q        =>
      verification_conditions P d /\ (post d ~~> Q)
  end.

Theorem verification_correct : forall d P,
  verification_conditions P d -> {{P}} (extract d) {{post d}}.
Proof.
  dcom_cases (induction d) Case; intros P H; simpl in *.
  Case "Skip".
    eapply hoare_consequence_pre.
      apply hoare_skip.
      assumption.
  Case "Seq".
    inversion H as [H1 H2]. clear H.
    eapply hoare_seq.
      apply IHd2. apply H2.
      apply IHd1. apply H1.
  Case "Asgn".
    eapply hoare_consequence_pre.
      apply hoare_asgn.
      assumption.
  Case "If".
    inversion H as [HPre1 [HPre2 [Hd1 [Hd2 [HThen HElse]]]]]; clear H.
    subst.
    apply hoare_if.
      eapply hoare_consequence_pre. apply IHd1. eassumption. assumption.
      rewrite Hd2.
      eapply hoare_consequence_pre. apply IHd2. eassumption. assumption.
  Case "While".
    inversion H as [Hpre [Hbody [Hpost Hd]]]; subst; clear H.
    eapply hoare_consequence_pre.
    apply hoare_while with (P := post d).
      apply IHd. apply Hd.
      assumption.
  Case "Pre".
    inversion H as [HP Hd]; clear H.
    eapply hoare_consequence_pre. apply IHd. apply Hd. assumption.
  Case "Post".
    inversion H as [Hd HQ]; clear H.
    eapply hoare_consequence_post. apply IHd. apply Hd. assumption.
Qed.

Tactic Notation "verify" :=
  try apply verification_correct;
  repeat split;
  simpl; unfold assert_implies;
  unfold bassn in *; unfold beval in *; unfold aeval in *;
  unfold assn_sub; intros;
  repeat rewrite update_eq;
  repeat (rewrite update_neq; [| reflexivity]);
  simpl in *;
  repeat match goal with [H : _ /\ _ |- _] => destruct H end;
  repeat rewrite not_true_iff_false in *;
  repeat rewrite not_false_iff_true in *;
  repeat rewrite negb_true_iff in *;
  repeat rewrite negb_false_iff in *;
  repeat rewrite beq_nat_true_iff in *;
  repeat rewrite beq_nat_false_iff in *;
  try eauto; try omega.

(** Finally, for a bigger example, let's redo the proof of
    [list_member_correct] from above using our new tools.

    Notice that the [verify] tactic leaves subgoals for each
    "interesting" use of [hoare_consequence] -- that is, for each [=>]
    that occurs in the decorated program, except for the ones that can
    be eliminated by repeated application of a few simple automated
    tactics. Each of these implications relies on a fact about lists,
    for example that [l ++ [] = l]. In other words, the Hoare logic
    infrastructure has taken care of the boilerplate reasoning about
    the execution of imperative programs, while the user has to prove
    lemmas that are specific to the problem domain (e.g. lists or
    numbers). *)

Definition list_member_dec (n : nat) (l : list nat) : dcom := (
    {{ fun st => aslist (st X) = l /\ asnat (st Y) = n /\ asnat (st Z) = 0 }}
  WHILE BIsCons (AId X)
  DO {{ fun st => (asnat (st Y) = n
               /\ (exists p, p ++ aslist (st X) = l
               /\ (asnat (st Z) = 1 <-> appears_in n p)))
               /\ bassn (BIsCons (AId X)) st }}
    IFB (BEq (AId Y) (AHead (AId X))) THEN
        {{ fun st =>
          ((asnat (st Y) = n
            /\ (exists p, p ++ aslist (st X) = l
                /\ (asnat (st Z) = 1 <-> appears_in n p)))
          /\ bassn (BIsCons (AId X)) st)
          /\ bassn (BEq (AId Y) (AHead (AId X))) st }}
        =>
        {{ fun st =>
            asnat (st Y) = n
            /\ (exists p, p ++ tail (aslist (st X)) = l
                 /\ (1 = 1 <-> appears_in n p)) }}
      Z ::= ANum 1
        {{ fun st => asnat (st Y) = n
             /\ (exists p, p ++ tail (aslist (st X)) = l
                  /\ (asnat (st Z) = 1 <-> appears_in n p)) }}
   ELSE
        {{ fun st =>
          ((asnat (st Y) = n
            /\ (exists p, p ++ aslist (st X) = l
                  /\ (asnat (st Z) = 1 <-> appears_in n p)))
          /\ bassn (BIsCons (AId X)) st)
          /\ ~bassn (BEq (AId Y) (AHead (AId X))) st }}
        =>
        {{ fun st =>
          asnat (st Y) = n
          /\ (exists p, p ++ tail (aslist (st X)) = l
               /\ (asnat (st Z) = 1 <-> appears_in n p)) }}
      SKIP
        {{ fun st => asnat (st Y) = n
            /\ (exists p, p ++ tail (aslist (st X)) = l
                 /\ (asnat (st Z) = 1 <-> appears_in n p)) }}
   FI
     {{ fun st => asnat (st Y) = n
        /\ (exists p, p ++ tail (aslist (st X)) = l
          /\ (asnat (st Z) = 1 <-> appears_in n p)) }}
   ;
   X ::= (ATail (AId X))
     {{ fun st  =>
         asnat (st Y) = n /\
         (exists p : list nat, p ++ aslist (st X) = l
           /\ (asnat (st Z) = 1 <-> appears_in n p)) }}
  END
   {{ fun st =>
     (asnat (st Y) = n
       /\ (exists p, p ++ aslist (st X) = l
            /\ (asnat (st Z) = 1 <-> appears_in n p)))
     /\ ~bassn (BIsCons (AId X)) st }}
   =>
   {{ fun st => asnat (st Z) = 1 <-> appears_in n l }}
) %dcom.

Theorem list_member_correct' : forall n l,
  dec_correct (list_member_dec n l).
Proof.
  intros n l. verify.
  Case "The loop precondition holds.".
    exists []. simpl. split.
      rewrite H. reflexivity.
      rewrite H1. split; intro Hc; inversion Hc.
  Case "IF taken".
    destruct H2 as [p [H3 H4]].
    (* We know X is non-nil. *)
    remember (aslist (st X)) as x.
    destruct x as [|h x'].
      inversion H1.
      exists (snoc p h).
      simpl. split.
         rewrite snoc_equation. assumption.
         split.
           rewrite H in H0.
           simpl in H0. rewrite H0.
           intros _. apply appears_in_snoc1.
           intros _. reflexivity.
  Case "If not taken".
    destruct H2 as [p [H3 H4]].
    (* We know X is non-nil. *)
    remember (aslist (st X)) as x.
    destruct x as [|h x'].
      inversion H1.
      exists (snoc p h).
      split. simpl.
        rewrite snoc_equation. assumption.
        split.
          intros. apply appears_in_snoc2. apply H4. assumption.
          intros Hai.  destruct (appears_in_snoc3 _ _ _ Hai).
          SCase "later". apply H4. assumption.
          SCase "here (absurd)".
            subst. simpl in H0.
            apply ex_falso_quodlibet. apply H0. assumption.
  Case "Loop postcondition implies desired conclusion (->)".
    destruct H2 as [p [H3 H4]].
    destruct (aslist (st X)) as [|h x'].
       rewrite append_nil in H3. subst. apply H4. assumption.
       inversion H1.
  Case "loop postcondition implies desired conclusion (<-)".
    destruct H2 as [p [H3 H4]].
    destruct (aslist (st X)) as [|h x'].
       rewrite append_nil in H3. subst. apply H4. assumption.
       inversion H1.
Qed.


Require Export Coq.Lists.List.
Require Export Coq.ZArith.BinInt.
Require Export Coq.ZArith.Zcomplements.
Require Export Coq.Sorting.Permutation.
Require Export Coq.micromega.Lia.
Require Export VST.zlist.Zlist.
Require Export Poulet4.Utils.Utils.
Open Scope Z_scope.

Definition map2 {A B C} (f : A -> B -> C) (al : list A) (bl : list B) : list C :=
  map (uncurry f) (combine al bl).

Lemma fold_andb_false: forall l, fold_left andb l false = false.
Proof. induction l; simpl; intros; auto. Qed.

Lemma fold_andb_cons: forall l b, fold_andb (b :: l) = (b && fold_andb l)%bool.
Proof.
  intros. unfold fold_andb. simpl. destruct b; simpl; auto.
  apply fold_andb_false.
Qed.

Lemma fold_andb_true: forall l, fold_andb l = true <-> (forall i, 0 <= i < Zlength l -> Znth i l = true).
Proof.
  induction l; split; intros; simpl in *; auto.
  - list_solve.
  - rewrite fold_andb_cons in H. apply andb_prop in H. destruct H. subst.
    rewrite IHl in H1. list_simplify. apply H1. lia.
  - rewrite fold_andb_cons. cut (a = true).
    + intros. subst. simpl. apply IHl. intros.
      assert (0 <= i + 1 < Zlength (true :: l)) by list_solve.
      apply H in H1. rewrite Znth_pos_cons in H1 by lia. rewrite <- H1. f_equal. lia.
    + assert (0 <= 0 < Zlength (a :: l)) by list_solve. apply H in H0.
      now rewrite Znth_0_cons in H0.
Qed.

Lemma forallb_fold_andb {A : Type}: forall (f: A -> bool) l, forallb f l = fold_andb (map f l).
Proof.
  intros. induction l; simpl; auto. rewrite IHl, fold_andb_cons. auto.
Qed.

Lemma fold_orb_cons: forall l b, fold_orb (b :: l) = (b || fold_orb l)%bool.
Proof.
  intros. unfold fold_orb. simpl. destruct b; simpl; auto.
  induction l; simpl; intros; auto.
Qed.

Lemma existsb_fold_orb {A: Type}: forall (f: A -> bool) l, existsb f l = fold_orb (map f l).
Proof.
  intros. induction l; simpl; auto. rewrite IHl, fold_orb_cons. auto.
Qed.

Lemma fold_orb_false: forall l, fold_orb (false :: l) = fold_orb l.
Proof. intros. rewrite fold_orb_cons. now simpl. Qed.

Lemma fold_orb_perm: forall l1 l2, Permutation l1 l2 -> fold_orb l1 = fold_orb l2.
Proof.
  intros. induction H; simpl; auto; try rewrite !fold_orb_cons.
  - now f_equal.
  - rewrite !Bool.orb_assoc, (Bool.orb_comm x y). auto.
  - rewrite IHPermutation1. auto.
Qed.

Lemma map2_cons {A B C}: forall (f : A -> B -> C) a al b bl,
    map2 f (a :: al) (b :: bl) = f a b :: map2 f al bl.
Proof. intros. unfold map2. simpl. auto. Qed.

Lemma Zlength_map2 : forall {A B C} (f : A -> B -> C) al bl,
  Zlength (map2 f al bl) = Z.min (Zlength al) (Zlength bl).
Proof.
  unfold map2. induction al; intros; destruct bl.
  - auto.
  - simpl. list_solve.
  - simpl. list_solve.
  - simpl. specialize (IHal bl). list_solve.
Qed.

Lemma Znth_map2 : forall {A B C} {da : Inhabitant A} {db : Inhabitant B} {dc : Inhabitant C}
    (f : A -> B -> C) al bl i,
  Zlength al = Zlength bl ->
  0 <= i < Zlength al ->
  Znth i (map2 f al bl) = f (Znth i al) (Znth i bl).
Proof.
  unfold map2. induction al; intros; destruct bl.
  - list_solve.
  - simpl. list_solve.
  - simpl. list_solve.
  - simpl. specialize (IHal bl (i-1)).
    destruct (Z.eq_dec i 0).
    + list_solve.
    + list_simplify.
      apply IHal; list_solve.
Qed.

#[export] Hint Rewrite @Zlength_map2 using Zlength_solve : Zlength.
#[export] Hint Rewrite @Znth_map2 using Zlength_solve : Znth.

Lemma Forall2_forall_range2 : forall {A B} {da : Inhabitant A} {db : Inhabitant B}
    (P : A -> B -> Prop) al bl,
  Forall2 P al bl <-> Zlength al = Zlength bl /\ forall_range2 0 (Zlength al) 0 al bl P.
Proof.
  intros. split; intros.
  - induction H.
    + unfold forall_range2, forall_i; split; intros; list_solve.
    + destruct IHForall2.
      unfold forall_range2, forall_i; split; intros; list_solve.
  - destruct H. generalize dependent bl; induction al; intros.
    + assert (bl = nil) by list_solve. subst; auto.
    + destruct bl; only 1 : list_solve.
      constructor.
      { specialize (H0 0 ltac:(list_solve)). simpl in H0. list_solve. }
      { apply IHal; list_solve. }
Qed.

Lemma Zlength_fold_right {A B}: forall (f: B -> list A -> list A) init l,
    (forall b la, Zlength (f b la) = Zlength la) ->
    Zlength (fold_right f init l) = Zlength init.
Proof.
  intros. revert l init. induction l; intros; simpl; auto.
  rewrite H. easy.
Qed.

Lemma Forall_wrap {A: Type}: forall (P: A -> Prop) (l: list A) i len,
    len = Zlength l -> 0 <= i < len -> Forall P l <-> Forall P (sublist i (i + len) (l ++ l)).
Proof.
  intros. rewrite <- (sublist_rejoin i len) by list_solve. rewrite Forall_app.
  rewrite (sublist_app1 A i len) by lia. rewrite (sublist_app2 len (i + len)) by lia.
  replace (len - Zlength l) with 0 by lia. replace (i + len - Zlength l) with i by lia.
  now rewrite and_comm, <- Forall_app, sublist_rejoin, sublist_same by lia.
Qed.

Definition Zsum (l: list Z) := fold_left Z.add l 0.

Lemma Zsum_cons: forall l b, Zsum (b :: l) = b + Zsum l.
Proof.
  unfold Zsum. simpl. induction l; intros; simpl; try lia.
  rewrite IHl, (IHl a). lia.
Qed.

Lemma Zsum_perm: forall l1 l2, Permutation l1 l2 -> Zsum l1 = Zsum l2.
Proof. intros. induction H; simpl; auto; try rewrite !Zsum_cons; lia. Qed.

Lemma Zsum_app: forall l1 l2, Zsum (l1 ++ l2) = Zsum l1 + Zsum l2.
Proof. induction l1; intros; simpl; auto. rewrite !Zsum_cons. rewrite IHl1. lia. Qed.

Definition list_min (l : list Z) : Z :=
  match l with
  | nil => 0
  | x :: l' => fold_left Z.min l' x
  end.

Lemma list_min_map_add: forall {A: Type} (f: A -> Z) (z: Z) (l: list A),
    0 < Zlength l ->
    list_min (map (fun a => f a + z) l) = list_min (map f l) + z.
Proof.
  intros. destruct l as [| a l]. 1: list_solve. simpl. clear H.
  generalize (f a) as init. clear a. induction l; intros; simpl; auto.
  rewrite Z.add_min_distr_r. apply IHl.
Qed.


Require Import SfLib.
Require Import util.
Require Import STLC_types.

(* term definition *)
Unset Elimination Schemes.
Inductive term : Type :=
| TVar : id -> term
| TApp : term -> term -> term
| TLambda : id -> type -> term -> term
| TTrue : term
| TFalse : term
| TIf : term -> term -> term -> term
| TNum : nat -> term
| TPlus : term -> term -> term
| TEqNat : term -> term -> term
| TLiteral : list id -> list term -> term
| TAccess : term -> id -> term.
Set Elimination Schemes.

(* as with types, the default induction hypothesis is broken here;
   this definition is thus necessary *)
Definition term_ind := fun (P : term -> Prop) (f : forall i : id, P (TVar i))
  (f0 : forall t : term, P t -> forall t0 : term, P t0 -> P (TApp t t0))
  (f1 : forall (i : id) (t : type) (t0 : term), P t0 -> P (TLambda i t t0))
  (f2 : P TTrue) (f3 : P TFalse)
  (f4 : forall t : term,
        P t ->
        forall t0 : term, P t0 -> forall t1 : term, P t1 -> P (TIf t t0 t1))
  (f5 : forall n : nat, P (TNum n))
  (f6 : forall t : term, P t -> forall t0 : term, P t0 -> P (TPlus t t0))
  (f7 : forall t : term, P t -> forall t0 : term, P t0 -> P (TEqNat t t0))
  (f8 : forall li lv, Forall P lv -> P (TLiteral li lv))
  (f9 : forall t : term, P t -> forall i : id, P (TAccess t i)) =>
fix F (t : term) : P t :=
  match t as t0 return (P t0) with
  | TVar i => f i
  | TApp t0 t1 => f0 t0 (F t0) t1 (F t1)
  | TLambda i t0 t1 => f1 i t0 t1 (F t1)
  | TTrue => f2
  | TFalse => f3
  | TIf t0 t1 t2 => f4 t0 (F t0) t1 (F t1) t2 (F t2)
  | TNum n => f5 n
  | TPlus t0 t1 => f6 t0 (F t0) t1 (F t1)
  | TEqNat t0 t1 => f7 t0 (F t0) t1 (F t1)
  | TLiteral li lv => f8 li lv ((fix forall_rec (lv' : list term)
                                 : Forall P lv' :=
                                   match lv' with
                                     | nil => Forall_nil P
                                     | v :: rest =>
                                       Forall_cons v (F v) (forall_rec rest)
                                   end)
                                  lv)
  | TAccess t0 i => f9 t0 (F t0) i
  end.

Notation "\ x : t1 . y" := (TLambda x t1 y) (at level 50).

Tactic Notation "term_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "TVar"    | Case_aux c "TApp"
  | Case_aux c "TLambda" | Case_aux c "TTrue"
  | Case_aux c "TFalse"  | Case_aux c "TIf"
  | Case_aux c "TNum"    | Case_aux c "TPlus"
  | Case_aux c "TEqNat"  | Case_aux c "TLiteral"
  | Case_aux c "TAccess"].

Hint Constructors term.

(* value definition *)
Unset Elimination Schemes.
Inductive value : term -> Prop :=
| v_abs : forall x T t, value (TLambda x T t)
| v_true : value TTrue
| v_false : value TFalse
| v_nat : forall n, value (TNum n)
| v_literal : forall li lv, (Forall value lv) -> value (TLiteral li lv).
Set Elimination Schemes.

(* again, the default induction hypothesis is insufficient.
   This occurs essentially wherever Record types or literal terms appear *)
Definition value_ind := fun (P : term -> Prop)
  (f : forall (x : id) (T : type) (t : term), P (TLambda x T t))
  (f0 : P TTrue)
  (f1 : P TFalse)
  (f2 : forall n : nat, P (TNum n))
  (f3 : forall li lv,
        Forall P lv -> P (TLiteral li lv)) =>
  fix F (t : term) (v : value t) {struct v} : P t :=
  match v in (value t0) return (P t0) with
  | v_abs x T t0 => f x T t0
  | v_true => f0
  | v_false => f1
  | v_nat n => f2 n
  | v_literal li lv f5 => f3 li lv ((fix forallrec l (f5 : Forall value l) : Forall P l :=
     match f5 with
     | Forall_nil => Forall_nil P
     | Forall_cons t ts vt tvs => Forall_cons t (F t vt) (forallrec ts tvs)
     end) lv f5)
  end.

Tactic Notation "value_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "v_abs" | Case_aux c "v_true"
  | Case_aux c "v_false" | Case_aux c "v_nat"
  | Case_aux c "v_literal" ].

Hint Constructors value.

(* substitution semantics for evaluation *)
Fixpoint subst (x : id) (s : term) (t : term) : term :=
  match t with
    | TVar x' =>
      if beq_id x x' then s else t
    | TLambda x' T t1 =>
      TLambda x' T (if beq_id x x' then t1 else (subst x s t1))
    | TApp lambda arg =>
      TApp (subst x s lambda) (subst x s arg)
    | TTrue => TTrue
    | TFalse => TFalse
    | TIf c t e => TIf (subst x s c) (subst x s t) (subst x s e)
    | TNum n => TNum n
    | TPlus l r => TPlus (subst x s l) (subst x s r)
    | TEqNat l r => TEqNat (subst x s l) (subst x s r)
    | TLiteral li lv => TLiteral li (map (subst x s) lv)
    | TAccess r i => TAccess (subst x s r) i
  end.

Notation "'[' x ':=' s ']' t" := (subst x s t) (at level 20).

(* definitions and lemmas for/about looking up terms in literals *)
Fixpoint lookup' (i : id) (l : list (id * term)) : option term :=
  match l with
    | nil => None
    | (i',v) :: l' => if beq_id i i'
                      then Some v
                      else lookup' i l'
  end.

Definition lookup i li lv :=
  lookup' i (combine li lv).

Lemma lookup_in_pair : forall i v li lv,
                    Uniq li -> lookup i li lv = Some v -> In (i,v) (combine li lv).
Proof with auto.
  intros i v li. generalize dependent i. generalize dependent v.
  induction li; intros; try solve by inversion.
    Case "inductive". destruct lv; try solve by inversion.  simpl in *.
      unfold lookup in H0. simpl in H0. remember (beq_id i a). destruct b.
      SCase "i = a". apply beq_id_eq in Heqb. inversion H0. subst...
      SCase "i != a". inversion H; subst. unfold lookup in IHli. right.
        apply (IHli v i lv H4 H0)...
Qed.

Lemma lookup_in : forall i v li lv,
                    Uniq li -> lookup i li lv = Some v -> In i li.
Proof with auto.
  intros. apply in_combine_l with (l' := lv) (y := v). apply lookup_in_pair...
Qed.

Lemma in_lookup : forall i v li lv, Uniq li -> In (i,v) (combine li lv) -> lookup i li lv = Some v.
Proof with auto. intros i v li. generalize dependent i. generalize dependent v.
  induction li; intros.
  Case "base". simpl in *. contradiction.
  Case "inductive". destruct lv. inversion H0. simpl in *. destruct H0.
    SCase "(a,t) = (i,v)". inversion H0. subst. unfold lookup. simpl. rewrite <- beq_id_refl...
    SCase "In (i,v) (combine li lv)". inversion H; subst. apply IHli in H0...
      unfold lookup in *. simpl in *. remember (beq_id i a). destruct b...
        SSCase "i=a". apply beq_id_eq in Heqb. subst. contradict H3.
          apply lookup_in in H0...
Qed.


Require Import List.
Import ListNotations.

From Verbatim Require Import ltac.

Module Trie.

  Inductive Trie {Target : Type} : Type :=
  | Leaf
  | Branch (t : option Target) (t0 t1 : Trie).

  Fixpoint set_Trie {Target : Type} (T : Trie) (key : list bool) (value : Target) : Trie :=
    match key with
    | [] =>
      match T with
      | Leaf => Branch (Some value) Leaf Leaf
      | Branch _ t0 t1 => Branch (Some value) t0 t1
      end
    | b :: bs =>
      match T with
      | Leaf =>
        if b then Branch None (set_Trie Leaf bs value) Leaf
        else Branch None Leaf (set_Trie Leaf bs value)
      | Branch t t0 t1 =>
        if b then Branch t (set_Trie t0 bs value) t1
        else Branch t t0 (set_Trie t1 bs value)
      end
    end.

  Fixpoint get_Trie {Target : Type} (T : Trie) (key : list bool) : option Target :=
    match T with
    | Leaf => None
    | Branch t t0 t1 =>
      match key with
      | [] => t
      | b :: bs =>
        if b then get_Trie t0 bs
        else get_Trie t1 bs
      end
    end.

  Lemma get_set : forall {Target : Type} key T (value : Target),
      get_Trie (set_Trie T key value) key = Some value.
  Proof.
    induction key; intros.
    - sis. repeat dm.
    - sis. repeat dm; sis; apply IHkey.
  Qed.

  Lemma get_set_moot : forall {Target : Type} k0 k1 (v : Target) T,
      k0 <> k1
      -> get_Trie (set_Trie T k0 v) k1 = get_Trie T k1.
  Proof.
    induction k0; destruct k1; intros.
    - contradiction.
    - clear H. sis. repeat dm. sis. repeat dm.
    - clear H. sis. repeat dm.
    - sis. repeat dm; sis; repeat dm; apply IHk0; intros C; destruct H; rewrite C; auto.
  Qed.

End Trie.


Parameter bound : Type.

(* Saturating *)
Parameter succ : bound -> bound.
Parameter pred : bound -> bound.

Parameter le : bound -> bound -> Prop.
Parameter le_dec : forall x y, {le x y} + {~ le x y}.
Parameter le_refl : forall x, le x x.
Parameter le_trans : forall x y z, le x y -> le y z -> le x z.
Parameter lt_le_incl : forall x y, ~ le y x -> le x y.
Parameter lt_le_succ : forall x y, ~ le y x -> le (succ x) y.
Parameter lt_le_pred : forall x y, ~ le y x -> le x (pred y).

Definition leb x y := if le_dec x y then true else false.

Declare Scope bound_scope.
Bind Scope bound_scope with bound.
Open Scope bound_scope.

Local Notation "x >= y" := (le y x) (at level 70) : bound_scope.
Local Notation "x <= y" := (le x y) (at level 70) : bound_scope.
Local Notation "x > y" := (~ le x y) (at level 70) : bound_scope.
Local Notation "x < y" := (~ le y x) (at level 70) : bound_scope.
Local Notation "x <= y <= z" := (le x y /\ le y z) (at level 70, y at next level) : bound_scope.

Local Notation "x >=? y" := (leb y x) (at level 70) : bound_scope.
Local Notation "x <=? y" := (leb x y) (at level 70) : bound_scope.
Local Notation "x >? y" := (negb (leb x y)) (at level 70) : bound_scope.
Local Notation "x <? y" := (negb (leb y x)) (at level 70) : bound_scope.
Local Notation "x <=? y <=? z" := (andb (leb x y) (leb y z)) (at level 70, y at next level) : bound_scope.

Definition lt_dec : forall x y, {x < y} + {y <= x}.
Proof. intros. destruct (le_dec y x). now right. now left. Defined.

Definition min x y := if x <=? y then x else y.
Definition max x y := if y <=? x then x else y.

Lemma min_refl : forall x, min x x = x.
Proof.
  unfold min, leb. intros. now destruct (le_dec x x). Qed.
Lemma max_refl : forall x, max x x = x.
Proof.
  unfold max, leb. intros. now destruct (le_dec x x). Qed.

Lemma min_trans : forall x y z,
  y <= x -> min y z <= x.
Proof.
  unfold min, leb. intros. destruct (le_dec y z). assumption.
  apply lt_le_incl in n. apply (le_trans _ _ _ n H). Qed.

Lemma max_trans : forall x y z,
  x <= y -> x <= max y z.
Proof.
  unfold max, leb. intros. destruct (le_dec z y). assumption.
  apply lt_le_incl in n. apply (le_trans _ _ _ H n). Qed.

Lemma min_max_trans : forall w x y z,
  w <= x -> y <= z -> min w y <= max x z.
Proof.
  intros. now apply min_trans, max_trans. Qed.

Record interval := Interval {
  lo: bound;
  hi: bound;
  ordered: le lo hi;
}.

Definition singleton (x : bound) : interval :=
  Interval x x (le_refl _).

Definition is_contiguous (i1 i2 : interval) : bool :=
  max i1.(lo) i2.(lo) <=? succ (min i1.(hi) i2.(hi)).

Definition is_intersection_empty (i1 i2 : interval) : bool :=
  max i1.(lo) i2.(lo) >? min i1.(hi) i2.(hi).

Definition is_subset (i1 i2 : interval) : bool :=
  (i2.(lo) <=? i1.(lo) <=? i2.(hi)) && (i2.(lo) <=? i1.(hi) <=? i2.(hi)).

Definition union (i1 i2 : interval) : option interval :=
  if is_contiguous i1 i2 then
    Some (Interval (min i1.(lo) i2.(lo))
                   (max i1.(hi) i2.(hi))
                   (min_max_trans _ _ _ _ i1.(ordered) i2.(ordered))) else None.

Definition intersect (i1 i2 : interval) : option interval :=
  let lo3 := max i1.(lo) i2.(lo) in
  let hi3 := min i1.(hi) i2.(hi) in
  match le_dec lo3 hi3 with
  | left pf => Some (Interval lo3 hi3 pf)
  | right _ => None
  end.

Lemma intersect_not_empty : forall i1 i2,
  (exists i3, intersect i1 i2 = Some i3) <-> is_intersection_empty i1 i2 = false.
Proof.
  unfold intersect, is_intersection_empty, leb. intros [] []. cbn.
  split.
  - intros [[]]. now destruct (le_dec (max lo0 lo1) (min hi0 hi1)).
  - intros. destruct (le_dec (max lo0 lo1) (min hi0 hi1)).
    now eexists. discriminate. Qed.

Lemma intersect_is_empty : forall i1 i2,
  intersect i1 i2 = None <-> is_intersection_empty i1 i2 = true.
Proof.
  unfold intersect, is_intersection_empty, leb. intros [] []. cbn.
  now destruct (le_dec (max lo0 lo1) (min hi0 hi1)). Qed.

Definition difference (i1 i2 : interval) : option interval * option interval :=
  match lt_dec i1.(lo) i2.(lo), lt_dec i2.(hi) i1.(hi) with
  | left pflo, left pfhi =>
      (Some (Interval i1.(lo) (pred i2.(lo)) (lt_le_pred _ _ pflo)),
       Some (Interval (succ i2.(hi)) i1.(hi) (lt_le_succ _ _ pfhi)))
  | left pflo, right _ =>
      (Some (Interval i1.(lo) (pred i2.(lo)) (lt_le_pred _ _ pflo)), None)
  | right _, left pfhi =>
      (None, Some (Interval (succ i2.(hi)) i1.(hi) (lt_le_succ _ _ pfhi)))
  | right _, right _ => (None, None) (* i1 is a subset of i2 *)
  end.

Lemma subset_difference_is_empty : forall i1 i2,
  is_subset i1 i2 = true <-> difference i1 i2 = (None, None).
Admitted.

Definition interval_set := list interval.


(** rk-singleton : The rank of a point is always greater than one  **)
Lemma rk_singleton_ge : forall P, rk (P :: nil) >= 1.
Proof.
intros.
assert(HH := rk_points);use HH.
(*destruct (is_only_13_pts P) as [[[[[[[[[[[[HA | HB] | HC] | HD] | HE ] | HF ] | HG] | HH] |  HI ] | HJ] | HK] | HL] | HM]; subst.*)

case_clear_2 P ;intuition.
Qed.

Lemma rk_singleton_1 : forall A, rk(A :: nil) <= 1.
Proof.
intros.
apply matroid1_b_useful.
intuition.
Qed.

Lemma rk_couple_ge_alt : forall P Q, rk(P :: Q :: nil) = 2 -> rk(P :: Q :: nil) >=2.
Proof.
intuition.
Qed.

(** rk-couple : The rank of a two distinct points is always greater than one  **)
Lemma rk_couple_ge : forall P Q, ~ P = Q -> rk(P :: Q :: nil) >= 2.
Proof.
intros.
assert(HH := rk_distinct_points);use HH.
apply rk_couple_ge_alt.
case_clear_2 P;case_clear_2 Q;try equal_degens;try assumption;rewrite couple_equal;assumption.
Qed.

Lemma rk_couple_2 : forall P Q, rk(P :: Q :: nil) <= 2.
Proof.
intros.
apply matroid1_b_useful.
intuition.
Qed.

Lemma rk_couple : forall P Q : Point,~ P = Q -> rk(P :: Q :: nil) = 2.
Proof.
intros.
assert(HH := rk_couple_2 P Q).
assert(HH0 := rk_couple_ge P Q H).
omega.
Qed.

Lemma couple_rk1 : forall P Q, rk(P :: Q :: nil) = 2 -> ~ P = Q.
Proof.
intros.
intro.
rewrite H0 in H.
assert(HH : equivlist (Q :: Q :: nil) (Q :: nil));[my_inO|].
rewrite HH in H.
assert(HH0 := rk_singleton_1 Q).
omega.
Qed.

Lemma quadruple_rk2_to_triple_rk2 : forall P Q R S : Point,
rk (P :: Q :: nil) = 2 -> rk(P :: Q ::  R :: S :: nil) = 2 -> rk(P :: Q :: R :: nil) = 2.
Proof.
intros.
assert(HH : incl (P :: Q :: R :: nil) (P :: Q :: R :: S :: nil));[my_inO|].
assert(HH0 := matroid2 (P :: Q :: R :: nil) (P :: Q :: R :: S :: nil) HH).
assert(HH1bis := couple_rk1 P Q H).
assert(HH1 := rk_couple P Q HH1bis).
assert(HH2 : incl (P :: Q :: nil) (P :: Q :: R :: nil));[my_inO|].
assert(HH3 := matroid2 (P :: Q :: nil) (P :: Q :: R :: nil) HH2).
omega.
Qed.

Ltac rk_triple_to_quadruple_bis :=
match goal with
| H0 : rk(?A :: ?B :: ?C :: ?D :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis |assumption]);assumption
| H0 : rk(?A :: ?B :: ?D :: ?C :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_1 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?A :: ?C :: ?B :: ?D :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_2 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?A :: ?C :: ?D :: ?B :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_3 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?A :: ?D :: ?B :: ?C :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_4 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?A :: ?D :: ?C :: ?B :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_5 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?B :: ?A :: ?C :: ?D :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_6 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?B :: ?A :: ?D :: ?C :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_7 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?B :: ?C :: ?A :: ?D :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_8 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?B :: ?C :: ?D :: ?A :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_9 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?B :: ?D :: ?A :: ?C :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_10 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?B :: ?D :: ?C :: ?A :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_11 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?C :: ?A :: ?B :: ?D :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_12 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?C :: ?A :: ?D :: ?B :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_13 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?C :: ?B :: ?A :: ?D :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_14 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?C :: ?B :: ?D :: ?A :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_15 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?C :: ?D :: ?A :: ?B :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_16 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?C :: ?D :: ?B :: ?A :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_17 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?D :: ?A :: ?B :: ?C :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_18 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?D :: ?A :: ?C :: ?B :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_19 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?D :: ?B :: ?A :: ?C :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_20 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?D :: ?B :: ?C :: ?A :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_21 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?D :: ?C :: ?A :: ?B :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_22 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
| H0 : rk(?D :: ?C :: ?B :: ?A :: nil) = 2 |- rk(?A :: ?B :: ?C :: nil) = 2 =>
  rewrite <-quadruple_equal_23 in H0;let HH := fresh in assert(HH : rk(A :: B :: C :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 A B C D);solve [rk_couple_triple_bis_bis|assumption]);assumption
end.
(*
Ltac rk_triple_to_quadruple_bis_bis :=
match goal with
| H : _ |- rk(?A :: ?B :: ?C :: nil) = 2 => solve[rk_triple_to_quadruple_bis A B C]
end.
*)
Lemma triple_rk2_1 : forall P R, rk(P :: R :: nil) = 2 -> rk(P :: P :: R :: nil) = 2.
Proof.
intros.
assert(HH : equivlist (P :: P :: R :: nil) (P :: R :: nil));[my_inO|];rewrite HH;intuition.
Qed.

Lemma triple_rk2_2 : forall P R, rk(P :: R :: nil) = 2 -> rk(P :: R :: P :: nil) = 2.
Proof.
intros.
assert(HH : equivlist (P :: R :: P :: nil) (P :: R :: nil));[my_inO|];rewrite HH;intuition.
Qed.

Lemma triple_rk2_3 : forall P R, rk(P :: R :: nil) = 2 -> rk(R :: P :: P :: nil) = 2.
Proof.
intros.
assert(HH : equivlist (R :: P :: P :: nil) (P :: R :: nil));[my_inO|];rewrite HH;intuition.
Qed.

Ltac degens_rk2 :=
match goal with
| H : _ |- rk(?P :: ?P :: ?R :: nil) = 2 => (*try*) solve[apply triple_rk2_1;rk_couple_triple_bis_bis]
| H : _ |- rk(?P :: ?R :: ?P :: nil) = 2 => (*try*) solve[apply triple_rk2_2;rk_couple_triple_bis_bis]
| H : _ |- rk(?R :: ?P :: ?P :: nil) = 2 => (*try*) solve[apply triple_rk2_3;rk_couple_triple_bis_bis]
end.

Ltac degens_rk2' :=
  solve[ first [apply triple_rk2_1 | apply triple_rk2_2 | apply triple_rk2_3];rk_couple_triple_bis_bis].

Ltac solve_ex_1 L := solve [exists L; repeat split;solve [ assumption |  degens_rk2' | rk_couple_triple_bis_bis | rk_triple_to_quadruple_bis]].

(*Ltac solve_ex_p_1 := solve_ex_p solve_ex_1.  first [
        solve_ex_1 A
     |  solve_ex_1 B
     |  solve_ex_1 C
     |  solve_ex_1 D
     |  solve_ex_1 E
     |  solve_ex_1 F
     |  solve_ex_1 G
     |  solve_ex_1 H
     |  solve_ex_1 I
     |  solve_ex_1 J
     |  solve_ex_1 K
     |  solve_ex_1 L
     |  solve_ex_1 M
 ].*)
Ltac solve_ex_p_1 := first [
 solve_ex_1 P0 |  solve_ex_1 P1 |  solve_ex_1 P2 |  solve_ex_1 P3 |  solve_ex_1 P4 |  solve_ex_1 P5 |  solve_ex_1 P6 |  solve_ex_1 P7 |  solve_ex_1 P8 |  solve_ex_1 P9 |  solve_ex_1 P10 |  solve_ex_1 P11 |  solve_ex_1 P12 |  solve_ex_1 P13 |  solve_ex_1 P14 |  solve_ex_1 P15 |  solve_ex_1 P16 |  solve_ex_1 P17 |  solve_ex_1 P18 |  solve_ex_1 P19 |  solve_ex_1 P20 |  solve_ex_1 P21 |  solve_ex_1 P22 |  solve_ex_1 P23 |  solve_ex_1 P24 |  solve_ex_1 P25 |  solve_ex_1 P26 |  solve_ex_1 P27 |  solve_ex_1 P28 |  solve_ex_1 P29 |  solve_ex_1 P30 ].

Ltac rk_three_points_simplify P Q :=
match goal with
| H0 : rk(P :: Q :: ?R :: ?X :: nil) = 2 |- _ => let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [ assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(P :: ?R :: Q :: ?X :: nil) = 2 |- _ => rewrite <-quadruple_equal_2 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(P :: ?R :: ?X :: Q :: nil) = 2 |- _ => rewrite <-quadruple_equal_3 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(Q :: P :: ?R :: ?X :: nil) = 2 |- _ => rewrite <-quadruple_equal_6 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(Q :: ?R :: P :: ?X :: nil) = 2 |- _ => rewrite <-quadruple_equal_8 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(Q :: ?R :: ?X :: P :: nil) = 2 |- _ => rewrite <-quadruple_equal_9 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(?R :: P :: Q :: ?X :: nil) = 2 |- _ => rewrite <-quadruple_equal_12 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(?R :: P :: ?X :: Q :: nil) = 2 |- _ => rewrite <-quadruple_equal_13 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(?R :: Q :: P :: ?X :: nil) = 2 |- _ => rewrite <-quadruple_equal_14 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(?R :: Q :: ?X :: P :: nil) = 2 |- _ => rewrite <-quadruple_equal_15 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(?R :: ?X :: P :: Q :: nil) = 2 |- _ => rewrite <-quadruple_equal_16 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
| H0 : rk(?R :: ?X :: Q :: P :: nil) = 2 |- _ => rewrite <-quadruple_equal_17 in H0;let HH := fresh in assert(HH : rk(P :: Q :: R :: nil) = 2) by (apply (quadruple_rk2_to_triple_rk2 P Q R X);solve [assumption|rk_couple_triple_bis_bis]);solve_ex_1 R
end.

Ltac rk_three_points_simplify_bis :=
match goal with
| H : _ |- exists R, rk (?P :: ?P :: _ :: nil) = 2 /\ _ /\ _ => solve_ex_p_1
| H : _ |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ => rk_three_points_simplify P Q
end.

Ltac my_inA :=
  intuition;unfold incl in *;unfold equivlist in *; simpl;
  repeat match goal with
  |[H : _ |- _] => progress intros
  |[H : _ |- _] => progress intro
  |[H : _ |- _] => progress intuition
  |[H : _ |- _] => split;intuition
  |[H : In _ (?P ::  _ ) |- _] => inversion H;clear H
  |[H : _ = _ |- _] => rewrite <-H
  |[H : In _ nil |- _] => inversion H
         end.

Lemma all_distinct_scheme : forall P Q T1 T2 T3 T4,
    rk (P :: Q :: T1 :: T2 :: T3 :: T4::nil)=2 ->
    rk (P::Q::nil)=2 -> rk (P::T1::nil)=2 -> rk (P::T2::nil)=2 -> rk (P::T3::nil)=2 -> rk (P::T4::nil)=2 ->
    rk (Q::T1::nil)=2 -> rk (Q::T2::nil)=2 -> rk (Q::T3::nil)=2 -> rk (Q::T4::nil)=2 ->
    rk (T1::T2::nil)=2 -> rk (T1::T3::nil)=2 -> rk (T1::T4::nil)=2 ->
    rk (T2::T3::nil)=2 -> rk (T2::T4::nil)=2 ->
    rk (T3::T4::nil)=2 ->
    rk (P::Q::T1::nil)=2/\ rk (Q :: T1 :: nil) = 2 /\ rk (P :: T1 :: nil) = 2.
Proof.
  intros.
  split.
  assert (rk (P :: Q :: T1 :: nil) <= 2 /\ 2 <= rk (P :: Q :: T1 :: nil)).

  split.
  rewrite <- H.
  apply matroid2.
  my_inA.
  rewrite <- H0.
  apply matroid2.
  my_inA.
  omega.
  split; assumption.
Qed.

Lemma rk_sym12 : forall A B U, rk(A::B::U)=rk(B::A::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym13 : forall A B C U, rk(A::B::C::U)=rk(C::B::A::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym14 : forall A B C D U, rk(A::B::C::D::U)=rk(D::B::C::A::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym15 : forall A B C D E U, rk(A::B::C::D::E::U)=rk(E::B::C::D::A::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym16 : forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(F::B::C::D::E::A::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym23 : forall A B C U, rk(A::B::C::U)=rk(A::C::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym24 : forall A B C D U, rk(A::B::C::D::U)=rk(A::D::C::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym25 : forall A B C D E U, rk(A::B::C::D::E::U)=rk(A::E::C::D::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym26 : forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(A::F::C::D::E::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym34 : forall A B C D U, rk(A::B::C::D::U)=rk(A::B::D::C::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym35 : forall A B C D E U, rk(A::B::C::D::E::U)=rk(A::B::E::D::C::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym36 : forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(A::B::F::D::E::C::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym45 : forall A B C D E U, rk(A::B::C::D::E::U)=rk(A::B::C::E::D::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym46 : forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(A::B::C::F::E::D::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_sym56 : forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(A::B::C::D::F::E::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_13_12 : forall A B C U, rk(A::B::C::U) = rk (A::C::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_14_12 : forall A B C D U, rk(A::B::C::D::U) = rk (A::D::C::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_15_12 : forall A B C D E U, rk(A::B::C::D::E::U) = rk (A::E::C::D::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_16_12 : forall A B C D E F U, rk(A::B::C::D::E::F::U) = rk (A::F::C::D::E::B::U).
Proof.
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_23_12 : forall A B C U, rk(A::B::C::U)= rk(B::C::A::U).
Proof.
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_24_12 : forall A B C D U, rk(A::B::C::D::U)= rk(B::D::A::C::U).
Proof.
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_25_12 : forall A B C D E U, rk(A::B::C::D::E::U)= rk(B::E::A::C::D:: U).
Proof.
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_26_12 : forall A B C D E F U, rk(A::B::C::D::E::F::U)= rk(B::F::A::C::D::E::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_34_12 : forall A B C D U, rk(A::B::C::D::U)=rk(C::D::A::B::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_35_12 : forall A B C D E U, rk(A::B::C::D::E::U)=rk(C::E::A::B::D::U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_36_12 : forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(C::F::A::B::D::E:: U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_45_12 :forall A B C D E U, rk(A::B::C::D::E::U)=rk(D::E::A::B::C:: U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_46_12 :forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(D::F::A::B::C::E:: U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Lemma rk_56_12 :forall A B C D E F U, rk(A::B::C::D::E::F::U)=rk(E::F::A::B::C::D:: U).
Proof.
  intros; apply rk_compat; unfold equivlist; split; my_inA.
Qed.

Ltac my_rk_three_points_simplify_bis :=
match goal with
| H : _ |- exists R, rk (?P :: ?P :: _ :: nil) = 2 /\ _ /\ _ => solve_ex_p_1
(*1*)
| H : rk(?P::?Q::?T1::?T2::?T3::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?P::?T1::?Q::?T2::?T3::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
  rewrite rk_13_12 in H;
  solve [
      exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
             solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?P::?T1::?T2::?Q::?T3::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
  rewrite rk_14_12 in H;
  solve [
      exists T2 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
             solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?P::?T1::?T2::?T3::?Q::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
  rewrite rk_15_12 in H;
  solve [
      exists T2 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
             solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?P::_::?T2::_::_::?Q::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   rewrite rk_16_12 in H;
  solve [
      exists T2 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
             solve [assumption | rk_couple_triple_bis_bis ] ]

(*2*)
| H : rk(?T1::?P::?Q::?T2::?T3::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
  rewrite rk_23_12 in H ; solve [
      exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
             solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?T1::?P::?T2::?Q::?T3::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
    rewrite rk_24_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?T1::?P::?T2::?T3::?Q::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   rewrite rk_25_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?T1::?P::?T2::?T3::?T4::?Q::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   rewrite rk_26_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]
(*3*)
| H : rk(?T1::?T2::?P::?Q::?T3::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   rewrite rk_34_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?T1::?T2::?P::?T3::?Q::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   rewrite rk_35_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?T1::?T2::?P::?T3::?T4::?Q::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   rewrite rk_36_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]

(*4*)
| H : rk(?T1::?T2::?T3::?P::?Q::?T4::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
   rewrite rk_45_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]

| H : rk(?T1::?T2::?T3::?P::?T4::?Q::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
  rewrite rk_46_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]
(*5*)
| H : rk(?T1::?T2::?T3::?T4::?P::?Q::nil)=2 |- exists R, rk (?P :: ?Q :: R :: nil) = 2 /\ _ /\ _ =>
  rewrite rk_56_12 in H; solve [
       exists T1 ; apply (all_distinct_scheme _ _ _ _ _ _ H);
              solve [assumption | rk_couple_triple_bis_bis ] ]
 (* reverse *)
(*1*)
| H : rk(?P::?Q::?T1::?T2::?T3::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H ; rewrite rk_sym12 in H; my_rk_three_points_simplify_bis

| H : rk(?P::?T1::?Q::?T2::?T3::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
    idtac H ; rewrite rk_sym13 in H; my_rk_three_points_simplify_bis

| H : rk(?P::?T1::?T2::?Q::?T3::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H ; rewrite rk_sym14 in H; my_rk_three_points_simplify_bis
| H : rk(?P::?T1::?T2::?T3::?Q::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym15 in H; my_rk_three_points_simplify_bis

| H : rk(?P::?T1::?T2::?T3::?T4::?Q::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym16 in H; my_rk_three_points_simplify_bis

(*2*)
| H : rk(?T1::?P::?Q::?T2::?T3::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym23 in H; my_rk_three_points_simplify_bis

| H : rk(?T1::?P::?T2::?Q::?T3::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym24 in H; my_rk_three_points_simplify_bis

| H : rk(?T1::?P::?T2::?T3::?Q::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym25 in H; my_rk_three_points_simplify_bis

| H : rk(?T1::?P::?T2::?T3::?T4::?Q::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym26 in H; my_rk_three_points_simplify_bis
(*3*)
| H : rk(?T1::?T2::?P::?Q::?T3::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym34 in H; my_rk_three_points_simplify_bis

| H : rk(?T1::?T2::?P::?T3::?Q::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym35 in H; my_rk_three_points_simplify_bis

| H : rk(?T1::?T2::?P::?T3::?T4::?Q::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym36 in H; my_rk_three_points_simplify_bis

(*4*)
| H : rk(?T1::?T2::?T3::?P::?Q::?T4::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym45 in H; my_rk_three_points_simplify_bis

| H : rk(?T1::?T2::?T3::?P::?T4::?Q::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym46 in H; my_rk_three_points_simplify_bis
(*5*)
| H : rk(?T1::?T2::?T3::?T4::?P::?Q::nil)=2 |- exists R, rk (?Q :: ?P :: R :: nil) = 2 /\ _ /\ _ =>
  idtac H; rewrite rk_sym56 in H; my_rk_three_points_simplify_bis

end.

(** rk-three_point_on_lines : Each lines contains at least three points **)
Lemma rk_three_points_on_lines : forall P Q, exists R,
rk (P :: Q :: R :: nil) = 2 /\ rk (Q :: R :: nil) = 2 /\ rk (P :: R :: nil) = 2.
Proof.
intros.
assert(HH := rk_distinct_points);assert(HH0 := rk_lines);use HH;use HH0.
time (case_clear_2 P;case_clear_2 Q ; my_rk_three_points_simplify_bis).
Time Qed.

(* proofs are completed up to here *)

Ltac rk_inter_simplify (*P Q R S*) X X' Y Y' :=
match goal with
| H : _ |- exists J, rk (_ :: Y :: _ :: nil) = 2 /\ rk (_ :: _ :: _ :: nil) = 2 => solve_ex_1 Y
| H : _ |- exists J, rk (Y :: _ :: _ :: nil) = 2 /\ rk (_ :: _ :: _ :: nil) = 2 => solve_ex_1 Y
| H : _ |- exists J, rk (_ :: Y' :: _ :: nil) = 2 /\ rk (_ :: _ :: _ :: nil) = 2 => solve_ex_1 Y'
| H : _ |- exists J, rk (Y' :: _ :: _ :: nil) = 2 /\ rk (_ :: _ :: _ :: nil) = 2 =>  solve_ex_1 Y'
| H : _ |- exists J, rk (_ :: _ :: _ :: nil) = 2 /\ rk (_ :: X :: _ :: nil) = 2 => solve_ex_1 X
| H : _ |- exists J, rk (_ :: _ :: _ :: nil) = 2 /\ rk (X :: _ :: _ :: nil) = 2 =>  solve_ex_1 X
| H : _ |- exists J, rk (_ :: _ :: _ :: nil) = 2 /\ rk (_ :: X' :: _ :: nil) = 2 => solve_ex_1 X'
| H : _ |- exists J, rk (_ :: _ :: _ :: nil) = 2 /\ rk (X' :: _ :: _ :: nil) = 2 => solve_ex_1 X'
| H : _ |- exists J, rk (_ :: _ :: _ :: nil) = 2 /\ rk (_ :: _ :: _ :: nil) = 2 => solve [solve_ex_1 X | solve_ex_1 X']
end.

Ltac rk_inter_simplify_bis P Q R S X X' :=
match goal with
| H : rk(R :: S :: ?Y :: ?Y' :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(R :: ?Y :: S :: ?Y' :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(R :: ?Y :: ?Y' :: S :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(S :: R :: ?Y :: ?Y' :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(S :: ?Y :: R :: ?Y' :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(S :: ?Y :: ?Y' :: R :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(?Y :: R :: S :: ?Y' :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(?Y :: R :: ?Y' :: S :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(?Y :: S :: R :: ?Y' :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(?Y :: S :: ?Y' :: R :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(?Y :: ?Y' :: R :: S :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
| H : rk(?Y :: ?Y' :: S :: R :: nil) = 2 |- _ => rk_inter_simplify (*P Q R S*) X X' Y Y'
end.

Ltac rk_inter_simplify_bis_bis_bis :=
match goal with
| H : _ |- exists J, rk (?P :: ?P :: _ :: nil) = 2 /\ rk (?P :: ?P :: _ :: nil) = 2 => solve_ex_p_1
| H : _ |- exists J, rk (?P :: ?P :: _ :: nil) = 2 /\ rk (?Q :: ?Q :: _ :: nil) = 2 => solve_ex_p_1

| H : _ |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?P :: ?P :: _ :: nil) = 2 => solve_ex_1 P
| H : _ |- exists J, rk (?Q :: ?P :: _ :: nil) = 2 /\ rk (?Q :: ?Q :: _ :: nil) = 2 => solve_ex_1 P
| H : _ |- exists J, rk (?Q :: ?Q :: _ :: nil) = 2 /\ rk (?P :: ?Q :: _ :: nil) = 2 => solve_ex_1 P
| H : _ |- exists J, rk (?Q :: ?Q :: _ :: nil) = 2 /\ rk (?Q :: ?P :: _ :: nil) = 2 => solve_ex_1 P

| H : _ |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?R :: _ :: nil) = 2 => solve_ex_1 P
| H : _ |- exists J, rk (?R :: ?R :: _ :: nil) = 2 /\ rk (?P :: ?Q :: _ :: nil) = 2 => solve_ex_1 P

| H : _ |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?P :: _ :: nil) = 2 => solve_ex_1 P
| H : _ |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?P :: ?R :: _ :: nil) = 2 => solve_ex_1 P
| H : _ |- exists J, rk (?Q :: ?P :: _ :: nil) = 2 /\ rk (?R :: ?P :: _ :: nil) = 2 => solve_ex_1 P
| H : _ |- exists J, rk (?Q :: ?P :: _ :: nil) = 2 /\ rk (?P :: ?R :: _ :: nil) = 2 => solve_ex_1 P

| H : rk(?P :: ?Q :: ?X :: ?X' :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?P :: ?X :: ?Q :: ?X' :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?P :: ?X :: ?X' :: ?Q :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?Q :: ?P :: ?X :: ?X' :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?Q :: ?X :: ?P :: ?X' :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?Q :: ?X :: ?X' :: ?P :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?X :: ?P :: ?Q :: ?X' :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?X :: ?P :: ?X' :: ?Q :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?X :: ?Q :: ?P :: ?X' :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?X :: ?Q :: ?X' :: ?P :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?X :: ?X' :: ?P :: ?Q :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
| H : rk(?X :: ?X' :: ?Q :: ?P :: nil) = 2 |- exists J, rk (?P :: ?Q :: _ :: nil) = 2 /\ rk (?R :: ?S :: _ :: nil) = 2 => rk_inter_simplify_bis P Q R S X X'
end.

(** rk-inter : Two lines always intersect in the plane **)
Lemma rk_inter : forall P Q R S, exists J, rk (P :: Q :: J :: nil) = 2 /\ rk (R :: S :: J :: nil) = 2.
Proof.
intros.
assert(HH := rk_distinct_points);assert(HH0 := rk_lines);use HH;use HH0.

 time (case_clear_2 P;case_clear_2 Q;
 abstract (case_clear_2 R;case_clear_2 S; rk_inter_simplify_bis_bis_bis)).

Qed.

(** rk-lower_dim : There exist three points which are not collinear **)
Lemma rk_lower_dim : exists P0 P1 P2, rk( P0 :: P1 :: P2 :: nil) >=3.
Proof.
intros.
assert(HH := rk_planes);use HH.
exists A;exists B;exists E;intuition.
Qed.

End s_fanoPlaneModelRkPG25.

(* Local Variables: *)
(* coq-prog-name: "/Users/magaud/.opam/4.06.0/bin/coqtop" *)
(* coq-load-path: (("/Users/magaud/containers/theories" "Containers") ("/Users/magaud/galapagos/dev/trunk/ProjectiveGeometry/Dev" "ProjectiveGeometry.Dev")) *)
(* suffixes: .v *)
(* End: *)


(** This file collects theorems, definitions, tactics, related to propositions
with a decidable equality. Such propositions are collected by the [Decision]
type class. *)
From stdpp Require Export proof_irrel.
From stdpp Require Import options.

(* Pick up extra assumptions from section parameters. *)
Set Default Proof Using "Type*".

Lemma dec_stable `{Decision P} : ¬¬P → P.
Proof. firstorder. Qed.

Lemma Is_true_reflect (b : bool) : reflect b b.
Proof. destruct b; [left; constructor | right; intros []]. Qed.
Instance: Inj (=) (↔) Is_true.
Proof. intros [] []; simpl; intuition. Qed.

Lemma decide_True {A} `{Decision P} (x y : A) :
  P → (if decide P then x else y) = x.
Proof. destruct (decide P); tauto. Qed.
Lemma decide_False {A} `{Decision P} (x y : A) :
  ¬P → (if decide P then x else y) = y.
Proof. destruct (decide P); tauto. Qed.
Lemma decide_iff {A} P Q `{Decision P, Decision Q} (x y : A) :
  (P ↔ Q) → (if decide P then x else y) = (if decide Q then x else y).
Proof. intros [??]. destruct (decide P), (decide Q); tauto. Qed.

Lemma decide_left `{Decision P, !ProofIrrel P} (HP : P) : decide P = left HP.
Proof. destruct (decide P); [|contradiction]. f_equal. apply proof_irrel. Qed.
Lemma decide_right `{Decision P, !ProofIrrel (¬ P)} (HP : ¬ P) : decide P = right HP.
Proof. destruct (decide P); [contradiction|]. f_equal. apply proof_irrel. Qed.

(** The tactic [destruct_decide] destructs a sumbool [dec]. If one of the
components is double negated, it will try to remove the double negation. *)
Tactic Notation "destruct_decide" constr(dec) "as" ident(H) :=
  destruct dec as [H|H];
  try match type of H with
  | ¬¬_ => apply dec_stable in H
  end.
Tactic Notation "destruct_decide" constr(dec) :=
  let H := fresh in destruct_decide dec as H.

(** The tactic [case_decide] performs case analysis on an arbitrary occurrence
of [decide] or [decide_rel] in the conclusion or hypotheses. *)
Tactic Notation "case_decide" "as" ident(Hd) :=
  match goal with
  | H : context [@decide ?P ?dec] |- _ =>
    destruct_decide (@decide P dec) as Hd
  | H : context [@decide_rel _ _ ?R ?x ?y ?dec] |- _ =>
    destruct_decide (@decide_rel _ _ R x y dec) as Hd
  | |- context [@decide ?P ?dec] =>
    destruct_decide (@decide P dec) as Hd
  | |- context [@decide_rel _ _ ?R ?x ?y ?dec] =>
    destruct_decide (@decide_rel _ _ R x y dec) as Hd
  end.
Tactic Notation "case_decide" :=
  let H := fresh in case_decide as H.

(** The tactic [solve_decision] uses Coq's [decide equality] tactic together
with instance resolution to automatically generate decision procedures. *)
Ltac solve_trivial_decision :=
  match goal with
  | |- Decision (?P) => apply _
  | |- sumbool ?P (¬?P) => change (Decision P); apply _
  end.
Ltac solve_decision :=
  unfold EqDecision; intros; first
    [ solve_trivial_decision
    | unfold Decision; decide equality; solve_trivial_decision ].

(** The following combinators are useful to create Decision proofs in
combination with the [refine] tactic. *)
Notation swap_if S := (match S with left H => right H | right H => left H end).
Notation cast_if S := (if S then left _ else right _).
Notation cast_if_and S1 S2 := (if S1 then cast_if S2 else right _).
Notation cast_if_and3 S1 S2 S3 := (if S1 then cast_if_and S2 S3 else right _).
Notation cast_if_and4 S1 S2 S3 S4 :=
  (if S1 then cast_if_and3 S2 S3 S4 else right _).
Notation cast_if_and5 S1 S2 S3 S4 S5 :=
  (if S1 then cast_if_and4 S2 S3 S4 S5 else right _).
Notation cast_if_and6 S1 S2 S3 S4 S5 S6 :=
  (if S1 then cast_if_and5 S2 S3 S4 S5 S6 else right _).
Notation cast_if_or S1 S2 := (if S1 then left _ else cast_if S2).
Notation cast_if_or3 S1 S2 S3 := (if S1 then left _ else cast_if_or S2 S3).
Notation cast_if_not_or S1 S2 := (if S1 then cast_if S2 else left _).
Notation cast_if_not S := (if S then right _ else left _).

(** We can convert decidable propositions to booleans. *)
Definition bool_decide (P : Prop) {dec : Decision P} : bool :=
  if dec then true else false.

Lemma bool_decide_reflect P `{dec : Decision P} : reflect P (bool_decide P).
Proof. unfold bool_decide. destruct dec; [left|right]; assumption. Qed.

Lemma bool_decide_decide P `{!Decision P} :
  bool_decide P = if decide P then true else false.
Proof. reflexivity. Qed.
Lemma decide_bool_decide P {Hdec: Decision P} {X : Type} (x1 x2 : X):
  (if decide P then x1 else x2) = (if bool_decide P then x1 else x2).
Proof. unfold bool_decide, decide. destruct Hdec; reflexivity. Qed.

Tactic Notation "case_bool_decide" "as" ident (Hd) :=
  match goal with
  | H : context [@bool_decide ?P ?dec] |- _ =>
    destruct_decide (@bool_decide_reflect P dec) as Hd
  | |- context [@bool_decide ?P ?dec] =>
    destruct_decide (@bool_decide_reflect P dec) as Hd
  end.
Tactic Notation "case_bool_decide" :=
  let H := fresh in case_bool_decide as H.

Lemma bool_decide_spec (P : Prop) {dec : Decision P} : bool_decide P ↔ P.
Proof. unfold bool_decide. destruct dec; simpl; tauto. Qed.
Lemma bool_decide_unpack (P : Prop) {dec : Decision P} : bool_decide P → P.
Proof. rewrite bool_decide_spec; trivial. Qed.
Lemma bool_decide_pack (P : Prop) {dec : Decision P} : P → bool_decide P.
Proof. rewrite bool_decide_spec; trivial. Qed.
Hint Resolve bool_decide_pack : core.

Lemma bool_decide_eq_true (P : Prop) `{Decision P} : bool_decide P = true ↔ P.
Proof. case_bool_decide; intuition discriminate. Qed.
Lemma bool_decide_eq_false (P : Prop) `{Decision P} : bool_decide P = false ↔ ¬P.
Proof. case_bool_decide; intuition discriminate. Qed.
Lemma bool_decide_iff (P Q : Prop) `{Decision P, Decision Q} :
  (P ↔ Q) → bool_decide P = bool_decide Q.
Proof. repeat case_bool_decide; tauto. Qed.

Lemma bool_decide_eq_true_1 P `{!Decision P}: bool_decide P = true → P.
Proof. apply bool_decide_eq_true. Qed.
Lemma bool_decide_eq_true_2 P `{!Decision P}: P → bool_decide P = true.
Proof. apply bool_decide_eq_true. Qed.

Lemma bool_decide_eq_false_1 P `{!Decision P}: bool_decide P = false → ¬P.
Proof. apply bool_decide_eq_false. Qed.
Lemma bool_decide_eq_false_2 P `{!Decision P}: ¬P → bool_decide P = false.
Proof. apply bool_decide_eq_false. Qed.

(** Backwards compatibility notations. *)
Notation bool_decide_true := bool_decide_eq_true_2.
Notation bool_decide_false := bool_decide_eq_false_2.

(** * Decidable Sigma types *)
(** Leibniz equality on Sigma types requires the equipped proofs to be
equal as Coq does not support proof irrelevance. For decidable we
propositions we define the type [dsig P] whose Leibniz equality is proof
irrelevant. That is [∀ x y : dsig P, x = y ↔ `x = `y]. *)
Definition dsig `(P : A → Prop) `{∀ x : A, Decision (P x)} :=
  { x | bool_decide (P x) }.

Definition proj2_dsig `{∀ x : A, Decision (P x)} (x : dsig P) : P (`x) :=
  bool_decide_unpack _ (proj2_sig x).
Definition dexist `{∀ x : A, Decision (P x)} (x : A) (p : P x) : dsig P :=
  x↾bool_decide_pack _ p.
Lemma dsig_eq `(P : A → Prop) `{∀ x, Decision (P x)}
  (x y : dsig P) : x = y ↔ `x = `y.
Proof. apply (sig_eq_pi _). Qed.
Lemma dexists_proj1 `(P : A → Prop) `{∀ x, Decision (P x)} (x : dsig P) p :
  dexist (`x) p = x.
Proof. apply dsig_eq; reflexivity. Qed.

(** * Instances of [Decision] *)
(** Instances of [Decision] for operators of propositional logic. *)
Instance True_dec: Decision True := left I.
Instance False_dec: Decision False := right (False_rect False).
Instance Is_true_dec b : Decision (Is_true b).
Proof. destruct b; simpl; apply _. Defined.

Section prop_dec.
  Context `(P_dec : Decision P) `(Q_dec : Decision Q).

  Global Instance not_dec: Decision (¬P).
  Proof. refine (cast_if_not P_dec); intuition. Defined.
  Global Instance and_dec: Decision (P ∧ Q).
  Proof. refine (cast_if_and P_dec Q_dec); intuition. Defined.
  Global Instance or_dec: Decision (P ∨ Q).
  Proof. refine (cast_if_or P_dec Q_dec); intuition. Defined.
  Global Instance impl_dec: Decision (P → Q).
  Proof. refine (if P_dec then cast_if Q_dec else left _); intuition. Defined.
End prop_dec.
Instance iff_dec `(P_dec : Decision P) `(Q_dec : Decision Q) :
  Decision (P ↔ Q) := and_dec _ _.

(** Instances of [Decision] for common data types. *)
Instance bool_eq_dec : EqDecision bool.
Proof. solve_decision. Defined.
Instance unit_eq_dec : EqDecision unit.
Proof. solve_decision. Defined.
Instance Empty_set_eq_dec : EqDecision Empty_set.
Proof. solve_decision. Defined.
Instance prod_eq_dec `{EqDecision A, EqDecision B} : EqDecision (A * B).
Proof. solve_decision. Defined.
Instance sum_eq_dec `{EqDecision A, EqDecision B} : EqDecision (A + B).
Proof. solve_decision. Defined.

Instance curry_dec `(P_dec : ∀ (x : A) (y : B), Decision (P x y)) p :
    Decision (curry P p) :=
  match p as p return Decision (curry P p) with
  | (x,y) => P_dec x y
  end.

Instance sig_eq_dec `(P : A → Prop) `{∀ x, ProofIrrel (P x), EqDecision A} :
  EqDecision (sig P).
Proof.
 refine (λ x y, cast_if (decide (`x = `y))); rewrite sig_eq_pi; trivial.
Defined.

(** Some laws for decidable propositions *)
Lemma not_and_l {P Q : Prop} `{Decision P} : ¬(P ∧ Q) ↔ ¬P ∨ ¬Q.
Proof. destruct (decide P); tauto. Qed.
Lemma not_and_r {P Q : Prop} `{Decision Q} : ¬(P ∧ Q) ↔ ¬P ∨ ¬Q.
Proof. destruct (decide Q); tauto. Qed.
Lemma not_and_l_alt {P Q : Prop} `{Decision P} : ¬(P ∧ Q) ↔ ¬P ∨ (¬Q ∧ P).
Proof. destruct (decide P); tauto. Qed.
Lemma not_and_r_alt {P Q : Prop} `{Decision Q} : ¬(P ∧ Q) ↔ (¬P ∧ Q) ∨ ¬Q.
Proof. destruct (decide Q); tauto. Qed.

Program Definition inj_eq_dec `{EqDecision A} {B} (f : B → A)
  `{!Inj (=) (=) f} : EqDecision B := λ x y, cast_if (decide (f x = f y)).
Solve Obligations with firstorder congruence.

(** * Instances of [RelDecision] *)
Definition flip_dec {A} (R : relation A) `{!RelDecision R} :
  RelDecision (flip R) := λ x y, decide_rel R y x.
(** We do not declare this as an actual instance since Coq can unify [flip ?R]
with any relation. Coq's standard library is carrying out the same approach for
the [Reflexive], [Transitive], etc, instance of [flip]. *)
Hint Extern 3 (RelDecision (flip _)) => apply flip_dec : typeclass_instances.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.montgomery32_2e216m2e108m1_7limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition opp :
  { opp : feBW_small -> feBW_small
  | forall a, phiM_small (opp a) = F.opp (phiM_small a) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_opp ().
  Show Ltac Profile.
Time Defined.

Print Assumptions opp.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.montgomery32_2e150m3_5limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition mul :
  { mul : feBW_small -> feBW_small -> feBW_small
  | forall a b, phiM_small (mul a b) = F.mul (phiM_small a) (phiM_small b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_mul ().
  Show Ltac Profile.
Time Defined.

Print Assumptions mul.