Require Export List.
Export ListNotations.
Set Implicit Arguments.

From Coq Require Import ssreflect.

Require Import gen.
Require Import genT.
Require Import gen_tacs.
Require Import gen_seq.
Require Import List_lemmasT.
Require Import FunctionalExtensionality.
Require Import Lia.

(* The following definition is a generalization of the gen_ext property.
   As gen_ext, univ_gen_ext gives a precise definition of embedding between
   lists: l1 is embedded in l2 if all the elements in l1 appear in l2 in the
   same order as in l1. Some elements might be added in l2, under the condition
   that these elements satisfy a certain given property P.

   Effectively, univ_gen_ext allows to restrict the weakening of gen_ext_extra to
   elements of a type A satisfying some property P. *)


Inductive univ_gen_ext W (P : W Type) : relationT (list W) :=
  | univ_gen_ext_nil : univ_gen_ext P [] []
  | univ_gen_ext_cons : x l le, univ_gen_ext P l le univ_gen_ext P (x :: l) (x :: le)
  | univ_gen_ext_extra : x l le, P x univ_gen_ext P l le univ_gen_ext P l (x :: le)
  .

(* We can prove some general properties about univ_gen_ext. *)

(* univ_gen_ext is a reflexive relation. *)

Lemma univ_gen_ext_refl: A (l : list A) P, univ_gen_ext P l l.
Proof.
induction l.
- apply univ_gen_ext_nil.
- intro. apply univ_gen_ext_cons. apply IHl.
Qed.


(* It is also transitive. *)

Lemma univ_gen_ext_trans: A P (ys zs : list A),
  univ_gen_ext P ys zs
   xs, univ_gen_ext P xs ys univ_gen_ext P xs zs.
Proof. intros until 1. induction X. tauto.
intros. inversion . subst. apply univ_gen_ext_cons. firstorder.
subst. apply univ_gen_ext_extra. firstorder. apply IHX. assumption.
intros. firstorder. firstorder. apply univ_gen_ext_extra. firstorder. apply IHX.
assumption.
Qed.


(* The empty list can be embedded in any other list xs as long as the
   elements of that list are all satisfying P. *)


Lemma all_P_univ_gen_ext_nil: A P (xs : list A),
              ( x, InT x xs P x) (univ_gen_ext P [] xs).
Proof.
induction xs.
- intros. apply univ_gen_ext_nil.
- intros. apply univ_gen_ext_extra. apply X. apply InT_eq'. reflexivity.
  apply IHxs. intros. apply X. apply InT_cons. assumption.
Qed.


(* Reciprocally, if the empty list is embedded in another list then
   we know that all the elements of that list are satisfying P. *)


Lemma univ_gen_ext_nil_all_P: A P (xs : list A),
              (univ_gen_ext P [] xs) ( x, InT x xs P x).
Proof.
induction xs.
- intros. inversion .
- intros. remember (a :: xs) as . remember [] as . destruct X.
  * inversion .
  * inversion .
  * inversion . subst. pose (IHxs X). inversion .
    + subst. assumption.
    + apply . assumption.
Qed.


(* The next lemmas state that if a list xs is embedded in a list
   ys, then xs is also embedded in the list ys appended (on the
   right or left of it) with a list zs of which all elements are
   satsfying the property P. *)


Lemma univ_gen_ext_appL: A P (xs ys zs : list A),
  univ_gen_ext P xs ys
  ( z, InT z zs P z)
  univ_gen_ext P xs (zs ys).
Proof.
induction zs. tauto.
intros. simpl. apply univ_gen_ext_extra. apply . apply InT_eq.
apply IHzs. assumption. intros. apply . apply InT_cons. assumption.
Qed.


Lemma univ_gen_ext_appR: A P (xs ys zs : list A),
  univ_gen_ext P xs ys
  ( z, InT z zs P z)
  univ_gen_ext P xs (ys zs).
Proof.
intros. induction X.
- induction zs.
  * apply univ_gen_ext_nil.
  * apply univ_gen_ext_extra. apply . apply InT_eq. apply IHzs.
    intros. apply . apply InT_cons. assumption.
- simpl. apply univ_gen_ext_cons. assumption.
- simpl. apply univ_gen_ext_extra. assumption. assumption.
Qed.


(* The lemma univ_gen_ext_lem states that if a list x::Z is embedded in
   a list Y, then x must be appearing in Y, i.e. Y = Y1++x::Y2, and Y2 is
   is such that Z is embedded in Y2. Note that Y1 is here as it could well
   be that some elements were added via univ_gen_ext_extra before even
   treating x. *)


Lemma univ_gen_ext_lem: A (x : A) P Z Y,
  univ_gen_ext P (x :: Z) Y sigT (fun Y1 sigT (fun Y2
    prod (Y = x :: ) (prod (univ_gen_ext P Z )
      ( y, InT y P y)))).
Proof.
intros A x P Z. induction Y.
intro. inversion X. intro. inversion X ; subst.
exists []. exists Y. simpl. split. tauto. split. tauto. intros.
inversion .
apply IHY in . cD. subst.
exists (a :: ). exists . simpl. split.
auto. split. assumption. intros. remember (a :: ) as l. destruct .
- subst. inversion Heql. assumption.
- apply . inversion Heql. subst. assumption.
Qed.


(* The next two lemmas deal with the interaction between univ_gen_ext
   and append. In essence, they say that if the embedded (resp. embedding)
   list is of the shape X1++X2, then the embedding list (resp. embedded)
   is of the shape W1++W2 where W1 embedds (resp. is embedded in) X1 and
   W2 embedds (resp. is embedded in) X1. *)


Lemma univ_gen_ext_splitL: A P (Z2 Z1 Y : list A),
  univ_gen_ext P ( ) Y sigT (fun Y1 sigT (fun Y2
    prod (Y = ) (prod (univ_gen_ext P ) (univ_gen_ext P )))).
Proof.
intros A P . induction ; simpl.
- exists []. exists Y. simpl. split. trivial.
split. apply univ_gen_ext_nil. trivial.
- intro. intro. apply univ_gen_ext_lem in X. cD. subst.
apply in . cD. subst.
exists (X a :: ). exists .
split. rewrite app_comm_cons. rewrite app_assoc. trivial.
split. apply univ_gen_ext_appL. apply univ_gen_ext_cons. assumption.
2: assumption. assumption.
Qed.


Lemma univ_gen_ext_splitR: A P (Z2 Z1 Y : list A),
  univ_gen_ext P Y ( ) sigT (fun Y1 sigT (fun Y2
    prod (Y = ) (prod (univ_gen_ext P ) (univ_gen_ext P )))).
Proof.
intros V . induction ; simpl.
- exists []. exists Y. simpl. split. trivial.
  split. apply univ_gen_ext_nil. trivial.
- intro. intro. inversion X ; subst.
apply in . cD. subst.
exists (a :: ). exists . simpl. split. trivial.
split. apply univ_gen_ext_cons. assumption. assumption.
apply in . cD. subst.
exists . exists . split. trivial. split. apply univ_gen_ext_extra.
assumption. assumption. assumption.
Qed.


(* univ_gen_ext_combine claims that if Y1 is embedded in Z1 and Y2 is embedded
   in Z2, then we can combine the embedded lists in the shape of Y1++Y2 and combine
   the embedding lists in Z1++Z2 to preserve the relation of embedding between the
   newly created lists.*)


Lemma univ_gen_ext_combine: A P (Z2 Z1 Y2 Y1 : list A),
    univ_gen_ext P univ_gen_ext P univ_gen_ext P ( ) ( ).
Proof.
intros A P . induction .
- repeat rewrite app_nil_l. assumption.
- simpl. apply univ_gen_ext_cons. assumption.
- simpl. apply univ_gen_ext_extra. assumption. assumption.
Qed.


(* The next lemma states that if a::Y is embedded in a::Z, then we know
   for sure that Y is embedded in Z. Note that this is not direct as the
   a in the embedding list could have been added via univ_gen_ext_extra.
   Effectively, this lemma allows one to remove the identical heads of two
   lists univ_gen_ext and preserve that relation. *)


Lemma univ_gen_ext_same_hd: A P (Y Z : list A) (a : A),
    univ_gen_ext P (a::Y) (a::Z) univ_gen_ext P Y Z.
Proof.
intros A P Y Z a gen. inversion gen.
- assumption.
- subst. assert (H: univ_gen_ext P Y (a :: Y)).
  apply univ_gen_ext_extra. assumption. apply univ_gen_ext_refl.
  apply univ_gen_ext_trans with (ys:=(a :: Y)). assumption. assumption.
Qed.


(* The lemma univ_gen_ext_elem_deep considers a list l1 embedded in a
   list l2 in which an element a appears deeply. Essentially, the lemma
   makes a case distinction about a: either it has been added by univ_gen_ext_extra
   (this is the first case of the sum) or it was added by univ_gen_ext_cons
   in which case it has to appear somewhere in l1. *)


Lemma univ_gen_ext_elem_deep {A : Type} : P (l1 l2 l3 l4: list A) a,
          univ_gen_ext P
          ( = a :: )
            ((univ_gen_ext P ( ) * P a) +
            sigT (fun l5 sigT (fun l6
              (prod ( = a :: ) (prod (univ_gen_ext P ) (univ_gen_ext P )))))).
Proof.
intros. subst. rewrite cons_single in X. apply univ_gen_ext_splitR in X.
destruct X. destruct s. repeat destruct p. subst.
apply univ_gen_ext_splitR in . destruct . destruct s. repeat destruct p.
subst. inversion .
- right. exists x. exists (l ). split. auto.
  split. assumption. inversion X. subst. simpl. assumption.
- left. subst. inversion . subst. split. rewrite app_nil_l.
  apply univ_gen_ext_combine. assumption. assumption. assumption.
Qed.


(* The next lemma states that if a list l1 is embedded in another list,
   then one can add an element a however deep in the embedding list and
   keep the relation of embedding, as long as a satisfies P. *)


Lemma univ_gen_ext_add_elem_deep {A : Type} : P (l1 l2 l3: list A) a,
    univ_gen_ext P ( )
    P a univ_gen_ext P ( a :: ).
Proof.
intros. apply univ_gen_ext_splitR in X. destruct X. destruct s.
repeat destruct p. subst. apply univ_gen_ext_combine.
assumption. apply univ_gen_ext_extra. assumption. assumption.
Qed.


(* This last lemma claims that if l0 is embedded in l1 with a property P,
   then l0 is also embedded in l1 with the property Q. Obviously, all elements
   which were added in l1 via univ_gen_ext_extra satisfied the property P, and
   as Q is weaker than P we get that these elements aso satisfy Q. Thus we preserve
   the embedding relation. *)


Lemma univ_gen_ext_Q_weaker_than_P {A : Type} : P Q (l0 l1 : list A),
          ( a, P a Q a)
          (univ_gen_ext P )
          (univ_gen_ext Q ).
Proof.
intros. induction .
- apply univ_gen_ext_nil.
- apply univ_gen_ext_cons. assumption.
- apply univ_gen_ext_extra. apply X. assumption. assumption.
Qed.


Lemma univ_gen_ext_In {A : Type} : (l0 l1 : list A) a P, univ_gen_ext P
                                        In a In a .
Proof.
intros. induction X.
- subst. inversion H.
- subst. inversion H. subst. apply in_eq. subst. apply in_cons. apply IHX. assumption.
- apply in_cons. apply IHX. assumption.
Qed.


Lemma univ_gen_ext_smaller_length : {A: Type} (l1 l2 : list A) P,
                      univ_gen_ext P length length .
Proof.
intros. induction X.
- auto.
- simpl. apply le_n_S. assumption.
- simpl. lia.
Qed.


Lemma univ_gen_ext_same_length_id : {A: Type} (l1 l2 : list A) P,
              (length = length ) univ_gen_ext P = .
Proof.
intros A P H X. induction X.
- firstorder.
- simpl in H. inversion H. apply IHX in . subst. auto.
- simpl in H. exfalso. pose (univ_gen_ext_smaller_length X). lia.
Qed.


Lemma univ_gen_ext_not_In_delete : {A : Type} (l1 l2 : list A) P (a : A),
            ((In a ) False) univ_gen_ext P (a :: ) univ_gen_ext P .
Proof.
intros. remember (a :: ) as . induction X.
- inversion .
- inversion . subst. exfalso. apply H. apply in_eq.
- inversion . subst. assumption.
Qed.


Lemma InT_univ_gen_ext : {A : Type} (l1 l2 : list A) (P : A Type) (a : A),
          (InT a ) (univ_gen_ext P ) ((P a) + (InT a )).
Proof.
intros. induction .
- inversion X.
- inversion X. subst. right. apply InT_eq. apply in . destruct . auto.
  right. apply InT_cons. assumption.
- inversion X. subst. auto. apply in . assumption.
Qed.


(* univ_gen_ext simulates gen_ext: *)

Definition gen_ext {W : Type} := @univ_gen_ext W (fun x True).

Lemma gen_ext_refl: W (l : list W), gen_ext l l.
Proof. induction l. apply univ_gen_ext_nil. apply univ_gen_ext_cons. exact IHl. Qed.

Lemma gen_ext_appL: W (xs ys zs : list W),
 gen_ext xs ys gen_ext xs (zs ys).
Proof. induction zs. tauto. intros. apply univ_gen_ext_extra. tauto. tauto. Qed.

Lemma gen_ext_appR: W (xs ys zs : list W),
 gen_ext xs ys gen_ext xs (ys zs).
Proof. intros. induction X.
induction zs. simpl. apply univ_gen_ext_nil. simpl.
apply univ_gen_ext_extra. apply I. assumption.
simpl. apply univ_gen_ext_cons. assumption.
simpl. apply univ_gen_ext_extra. assumption. assumption. Qed.


Lemma gen_ext_nil_any: W (xs : list W),gen_ext [] xs.
Proof. induction xs. apply univ_gen_ext_nil. apply univ_gen_ext_extra. tauto. tauto. Qed.

Lemma gen_ext_trans: W (ys zs : list W),
 gen_ext ys zs xs, gen_ext xs ys gen_ext xs zs.
Proof. intros until 1. induction X. tauto.
intros. inversion . subst. apply univ_gen_ext_cons. firstorder.
subst. apply univ_gen_ext_extra. firstorder. firstorder.
intros. apply univ_gen_ext_extra. firstorder. firstorder. Qed.


Lemma gen_ext_app: W (ys zs : list W),
 gen_ext ys zs us vs, gen_ext us vs gen_ext (us ys) (vs zs).
Proof. intros. induction ; simpl. exact X.
apply univ_gen_ext_cons. assumption.
apply univ_gen_ext_extra. assumption. assumption. Qed.


Definition gen_ext_sameL W xs ys zs ge :=
  @gen_ext_app W ys zs ge xs xs (@gen_ext_refl W xs).
Definition gen_ext_sameR W xs ys zs ge :=
  @gen_ext_app W xs xs (@gen_ext_refl W xs) ys zs ge.

Lemma gen_ext_lem: A (x : A) Z Y,
 gen_ext (x :: Z) Y sigT (fun Y1 sigT (fun Y2
    prod (Y = x :: ) (gen_ext Z ))).
Proof. intros A x Z. induction Y.
intro. inversion X. intro. inversion X ; subst.
exists []. exists Y. simpl. tauto.
apply IHY in . cD. subst.
exists (a :: ). exists . simpl. tauto. Qed.


Lemma gen_ext_splitL: A Z2 Z1 Y,
 gen_ext ( : list A) Y sigT (fun Y1 sigT (fun Y2
    prod (Y = ) (prod (gen_ext ) (gen_ext )))).
Proof. intros A . induction ; simpl.
exists []. exists Y. simpl. split. trivial.
split. apply univ_gen_ext_nil. trivial.
intro. intro. apply gen_ext_lem in X. cD. subst.
apply in . cD. subst.
exists (X a :: ). exists .
split. rewrite app_comm_cons. rewrite app_assoc. trivial.
split. apply gen_ext_appL. apply univ_gen_ext_cons. assumption. assumption. Qed.


Lemma gen_ext_splitR: A Z2 Z1 Y,
 gen_ext Y ( : list A) sigT (fun Y1 sigT (fun Y2
    prod (Y = ) (prod (gen_ext ) (gen_ext )))).
Proof. intros A . induction ; simpl.
exists []. exists Y. simpl. split. trivial.
split. apply univ_gen_ext_nil. trivial.
intro. intro. inversion X ; subst.
apply in . cD. subst.
exists (a :: ). exists . simpl. split. trivial.
split. apply univ_gen_ext_cons. assumption. assumption.
apply in . cD. subst.
exists . exists . split. trivial.
split. apply univ_gen_ext_extra. assumption. assumption. assumption. Qed.


Lemma gen_ext_combine: A (Z2 Z1 Y2 Y1 : list A),
   gen_ext gen_ext gen_ext ( ) ( ).
Proof.
intros A . induction .
- repeat rewrite app_nil_l. assumption.
- simpl. apply univ_gen_ext_cons. assumption.
- simpl. apply univ_gen_ext_extra. assumption. assumption.
Qed.


Lemma gen_ext_same_hd: A (Y Z : list A) a,
   gen_ext (a::Y) (a::Z) gen_ext Y Z.
Proof.
intros A Y Z a gen. inversion gen.
- assumption.
- subst. assert (H:gen_ext Y (a :: Y)).
  apply univ_gen_ext_extra. apply I. apply gen_ext_refl.
  apply gen_ext_trans with (ys:=(a :: Y)). assumption. assumption.
Qed.


Lemma gen_ext_single: A (l : list A), sing_empty l
   Y Z le,gen_ext (Z l Y) le sigT (fun Ze sigT (fun Ye
    prod (prod (gen_ext Z Ze) (gen_ext Y Ye)) (le = Ze l Ye))).
Proof. intros A l es Y. destruct es ; simpl ; intros.
apply gen_ext_splitL in X. cD. subst. firstorder.
apply gen_ext_splitL in X. cD.
apply gen_ext_lem in . cD. subst.
exists (X ). exists .
split. split. apply gen_ext_appR. assumption. assumption.
rewrite app_assoc. trivial. Qed.


Lemma gen_ext_one': A (x : A) (xs l : list A),
  xs = [x] gen_ext xs l InT x l.
Proof. intros. induction X.
- discriminate H.
- injection H as . subst. apply InT_eq.
- apply InT_cons. apply (IHX H). Qed.


Definition gen_ext_one A x l := @gen_ext_one' A x [x] l eq_refl.
(* and note result InT_split *)

Lemma InT_gen_ext A x l: InT x l gen_ext [x : A] l.
Proof. intros. apply InT_split in X. cD. subst.
apply gen_ext_appL. apply univ_gen_ext_cons. apply gen_ext_nil_any. Qed.


Lemma gen_ext_InT A (x : A) l m:gen_ext l m InT x l InT x m.
Proof. intro ge. induction ge. tauto.
intro. inversion X ; subst. apply InT_eq.
apply InT_cons. exact (IHge ).
intro. apply InT_cons. exact (IHge X). Qed.


Lemma gen_ext_diff' V X r :gen_ext X r (y : V) Y Z,
    r = (Y y :: Z) InT y X +gen_ext X (Y Z).
Proof. intro ge. induction ge.
- intros. list_eq_ncT. contradiction.
- intros. ; subst.
+ left. apply InT_eq.
+ erequire IHge. erequire IHge. erequire IHge. require IHge.
  reflexivity. destruct IHge.
 * left. apply InT_cons. assumption.
 * right. simpl. apply univ_gen_ext_cons. assumption.
- intros. ; subst.
+ right. simpl. assumption.
+ erequire IHge. erequire IHge. erequire IHge. require IHge.
  reflexivity. destruct IHge.
 * left. assumption.
 * right. simpl. apply univ_gen_ext_extra. apply I. assumption.
Qed.


Definition gen_ext_diff V X y Y Z ge := @gen_ext_diff' V X _ ge y Y Z eq_refl.

Lemma gen_ext_delet_hd {A : Type} : (l1 l2 l3: list A) (a : A),
       gen_ext ( = a :: ) gen_ext .
Proof.
intros a gen. generalize dependent . induction gen.
- intros E. inversion E.
- intros E. inversion E. destruct .
  * subst. apply gen_ext_nil_any.
  * inversion E. subst. apply univ_gen_ext_extra. apply I. assumption.
- intros E. apply univ_gen_ext_extra. apply I. apply IHgen. assumption.
Qed.


Lemma gen_ext_elem_deep {A : Type} : (l1 l2 l3 l4: list A) (a : A),
         gen_ext
          ( = a :: )
            ((gen_ext ( )) +
            sigT (fun l5 sigT (fun l6
              (prod ( = a :: ) (prod (gen_ext ) (gen_ext )))))).
Proof.
intros a gen. generalize dependent a. generalize dependent .
generalize dependent . induction gen.
- intros a E. destruct . inversion E. inversion E.
- intros a E. destruct .
  * inversion E. simpl in E. destruct .
    + right. exists []. exists []. split. simpl. destruct l.
      reflexivity. exfalso. subst. inversion gen. split.
      apply univ_gen_ext_nil. apply univ_gen_ext_nil.
    + pose (IHgen [] ). simpl in s. pose (s ). destruct .
      { right. exists []. exists l. split. reflexivity. split.
        apply univ_gen_ext_nil. apply univ_gen_ext_extra. apply I. assumption. }
      { repeat destruct . repeat destruct p. subst. right.
        exists []. exists ( :: ). split. reflexivity.
        split. apply univ_gen_ext_nil. destruct . simpl. apply univ_gen_ext_cons. assumption.
        inversion g. }
  * inversion E. pose (IHgen a ). destruct s.
    + left. apply univ_gen_ext_cons. assumption.
    + repeat destruct s. repeat destruct p. subst. right. exists ( :: ).
      exists . split. reflexivity. split. apply univ_gen_ext_cons. assumption.
      assumption.
- intros a E. destruct .
  * simpl in E. inversion E. subst. left. simpl. assumption.
  * inversion E. pose (IHgen a ). destruct s.
    + left. apply univ_gen_ext_extra. apply I. assumption.
    + repeat destruct s. repeat destruct . subst. right.
      exists . exists . split. reflexivity. split.
      apply univ_gen_ext_extra. apply I. assumption. assumption.
Qed.


Lemma gen_ext_add_elem_deep {A : Type} : (l1 l2 l3: list A) (a : A),
   gen_ext ( ) gen_ext ( a :: ).
Proof.
induction .
- intros. apply gen_ext_nil_any.
- induction .
  * intros gen. simpl. simpl in gen. apply univ_gen_ext_extra. apply I. assumption.
  * intros gen. remember (( :: ) ) as . remember (a :: ) as . destruct gen.
    + apply gen_ext_nil_any.
    + inversion . simpl. subst. apply univ_gen_ext_cons.
      inversion . apply . rewrite . assumption.
    + inversion . subst. simpl. apply univ_gen_ext_extra. apply I.
      apply . assumption.
Qed.


Ltac solve_gen_ext :=
  repeat (apply gen_ext_appR ||
      apply gen_ext_nil_any || apply gen_ext_refl ||
      apply gen_ext_sameL || apply gen_ext_appL ||
      apply univ_gen_ext_cons || apply univ_gen_ext_extra).

Ltac solve_univ_gen_ext :=
  repeat (apply univ_gen_ext_appR || apply univ_gen_ext_refl ||
      apply univ_gen_ext_appL || apply univ_gen_ext_cons ||
      apply univ_gen_ext_extra).

(* univ_gen_ext simulates rest_gen_ext. The latter allows to restrict
   the elements you can add to the embedding list to elements of a specific
   list l. *)


Definition rest_gen_ext {W : Type} l := univ_gen_ext (fun x (@InT W x l)).