(* Add LoadPath "../tense-lns". *)

Require Import List.
Require Import existsT.
Require Import gen_tacs.
Require Import gen.
Require Import List_lemmasT.

Set Implicit Arguments.
Import ListNotations.

(* Contains definitions and lemmas for swapped swapped_spec and swapped_gen, all used for contraction. *)

Inductive swapped T: list T -> list T -> Type :=
| swapped_I : forall (X Y A B C D : list T),
    X = (A ++ B ++ C ++ D) -> Y = (A ++ C ++ B ++ D) -> swapped X Y.

Lemma swapped_I': forall T (A B C D : list T),
  swapped (A ++ B ++ C ++ D) (A ++ C ++ B ++ D).
Proof. intros. eapply swapped_I ; reflexivity. Qed.

Lemma swapped_same: forall T X, swapped X (X : list T).
Proof. intros. apply (swapped_I [] [] [] X) ; simpl ; reflexivity. Qed.

Lemma swapped_L: forall T X Y Z,
  swapped X (Y : list T) -> swapped (Z ++ X) (Z ++ Y).
Proof. intros *; intros X0. inversion X0. subst.
  rewrite !(app_assoc Z). apply swapped_I'. Qed.

Lemma swapped_R: forall T X Y Z,
  swapped X (Y : list T) -> swapped (X ++ Z) (Y ++ Z).
Proof. intros *; intros X0. destruct X0. subst. rewrite <- !app_assoc. apply swapped_I'. Qed.

Lemma swapped_cons: forall T (x : T) Y Z,
  swapped Y Z -> swapped (x :: Y) (x :: Z).
Proof.
  intros *; intros H. destruct H.
  subst. repeat rewrite app_comm_cons.
  apply swapped_I'.
Qed.

Definition swapped_single T (x : T) := swapped_cons x (swapped_same []).

Lemma swapped_nilLE T Y: @swapped T [] Y -> Y = [].
Proof. intro sw. inversion sw. subst.
repeat (list_eq_ncT ; cD ; subst) ;
simpl ; rewrite ?app_nil_r ; try reflexivity. Qed.

Lemma swapped_nilRE T Y: @swapped T Y [] -> Y = [].
Proof. intro sw. inversion sw. subst.
repeat (list_eq_ncT ; cD ; subst) ;
simpl ; rewrite ?app_nil_r ; try reflexivity. Qed.

Lemma swapped_singleLE T (x : T) Y: swapped [x] Y -> Y = [x].
Proof. intro sw. inversion sw. subst.
acacD'T2 ; subst ; repeat (list_eq_ncT ; cD ; subst) ;
simpl ; rewrite ?app_nil_r ; try reflexivity. Qed.

Lemma swapped_singleRE T (x : T) Y: swapped Y [x] -> Y = [x].
Proof. intro sw. inversion sw. subst.
acacD'T2 ; subst ; repeat (list_eq_ncT ; cD ; subst) ;
simpl ; rewrite ?app_nil_r ; try reflexivity. Qed.

Lemma swapped_simple: forall T U V X Y,
  U = X ++ Y -> V = Y ++ X -> swapped U (V : list T).
Proof. intros. subst.
  apply (swapped_I [] X Y []) ; simpl ; rewrite app_nil_r ; reflexivity. Qed.

Lemma swapped_simple': forall T X Y, swapped (X ++ Y : list T) (Y ++ X).
Proof. intros. eapply swapped_simple ; reflexivity. Qed.

Lemma swapped_simpleL: forall T X Y Z,
  Y = Z -> swapped (X ++ Y) (Z ++ X : list T).
Proof. intros. subst. apply swapped_simple'. Qed.

Lemma swapped_simpleR: forall T X Y Z,
  Y = Z -> swapped (Y ++ X) (X ++ Z : list T).
Proof. intros. subst. apply swapped_simple'. Qed.

Lemma swapped_comm : forall {T} (A B : list T),
    swapped A B ->
    swapped B A.
Proof.
  intros T A B H.
  inversion H. subst.
  eapply swapped_I'.
Qed.

Definition single X (a : X) := [a].

Lemma cons_app_single X (a : X) xs : a :: xs = single a ++ xs.
Proof. unfold single. simpl. reflexivity. Qed.

Lemma single_eq X a : [a : X] = single a.
Proof. unfold single. reflexivity. Qed.

(* note some of the complexity of swap_tac involving cons
  may be avoided by rewriting with cons_app_single and single_eq *)


Lemma swapped_Rc2 T A H B C:
  swapped C (H ++ [A : T]) -> swapped (C ++ B) (H ++ A :: B).
Proof. intros sw. eapply swapped_R in sw. revert sw.
rewrite <- app_assoc. simpl. intro. eassumption. Qed.

Lemma swapped_Rc1 T A H B C:
  swapped (H ++ [A : T]) C -> swapped (H ++ A :: B) (C ++ B).
Proof. intros sw. eapply swapped_R in sw. revert sw.
rewrite <- app_assoc. simpl. intro. eassumption. Qed.

Lemma swapped_ca1 T A H: swapped (A :: H) (H ++ [A : T]).
Proof. pose (swapped_simple' [A] H). simpl in s. exact s. Qed.

Lemma swapped_ca2 T A H: swapped (H ++ [A : T]) (A :: H).
Proof. pose (swapped_simple' H [A]). simpl in s. exact s. Qed.

Lemma swapped_map_ex T U (f : T -> U) xs ys:
  swapped (map f xs) ys -> sigT2 (swapped xs) (fun zs => ys = map f zs).
Proof. intro sw. inversion sw. subst. clear sw.
repeat (match goal with | [ H : _ |- _ ] => eapply map_app_ex in H ; cD end).
subst. eexists. apply swapped_I'. rewrite !map_app. reflexivity. Qed.

Lemma swapped_map T U (f : T -> U) xs ys:
  swapped xs ys -> swapped (map f xs) (map f ys).
Proof. intro sw. inversion sw. subst. clear sw.
rewrite !map_app. apply swapped_I'. Qed.

(* Sequences of swaps of length n+1. *)
Inductive swapped_spec {T} : nat -> list T -> list T -> Type :=
  swapped_spec_I X Y : swapped X Y -> swapped_spec 0 X Y
| swapped_spec_step n A B C :
    swapped_spec n A B -> swapped B C -> swapped_spec (S n) A C.

Lemma swapped_spec_refl : forall {T} n (A : list T),
    swapped_spec n A A.
Proof.
  induction n; intros. econstructor. apply swapped_same.
  econstructor. apply IHn.
  apply swapped_same.
Qed.

Lemma swapped_app_L : forall {T} n (l A B : list T),
    swapped_spec n A B ->
    swapped_spec n (l ++ A) (l ++ B).
Proof.
  induction n; intros *; intros Hswap.
  constructor. apply swapped_L. inversion Hswap. auto.
  inversion Hswap as [ | ? ? ? ? X X0]. subst.
  econstructor. eapply IHn. exact X.
  apply swapped_L. assumption.
Qed.

Lemma swapped_spec_trans : forall {T} n1 n2 (l1 l2 l3 : list T),
    swapped_spec n1 l1 l2 ->
    swapped_spec n2 l2 l3 ->
    existsT2 m, swapped_spec m l1 l3.
Proof.
  induction n2; intros *; intros H1 H2.
  inversion H2. subst. exists (S n1).
  econstructor. apply H1. assumption.
  inversion H2 as [ | ? ? ? ? X X0]. subst.
  eapply IHn2 in H1. destruct H1 as [m H1].
  exists (S m). econstructor.
  apply H1. apply X0. apply X.
Qed.

Lemma swapped_spec_trans_exact : forall {T} n1 n2 (l1 l2 l3 : list T),
    swapped_spec n1 l1 l2 ->
    swapped_spec n2 l2 l3 ->
    swapped_spec (S (n1 + n2)) l1 l3.
Proof.
  induction n2; intros *; intros H1 H2.
  inversion H2 as [? ? X | ]. subst. rewrite PeanoNat.Nat.add_0_r.
  econstructor. apply H1. apply X.
  inversion H2 as [| ? ? ? ? X X0]. subst.
  eapply IHn2 in H1. simpl. econstructor.
  rewrite <- PeanoNat.Nat.add_succ_comm.
  apply H1. apply X0. assumption.
Qed.

Lemma swapped_spec_comm : forall {T} n (A B : list T),
    swapped_spec n A B ->
    swapped_spec n B A.
Proof.
  induction n; intros *; intros H.
  constructor. inversion H as [? ? X | ]. subst.
  eapply swapped_comm. assumption.
  inversion H as [ | ? ? ? ? X X0]. subst.
  eapply swapped_comm in X0.
  eapply swapped_spec_I in X0.
  apply IHn in X.
  apply (@swapped_spec_trans_exact T _ _ _ _ _ X0 X).
Qed.

Lemma swapped_spec_conv : forall {T} n m (A B : list T),
  n = m ->
  swapped_spec n A B ->
  swapped_spec m A B.
Proof. intros. subst. auto. Qed.

Lemma swapped_app_mid_L : forall {T} n (A B C D E : list T),
    swapped_spec n (A ++ B ++ C ++ D) E ->
    swapped_spec (S n) (A ++ C ++ B ++ D) E.
Proof.
  intros *; intros Hswap.
  assert (S n = S (0 + n)) as Hass.
  reflexivity.
  eapply swapped_spec_conv. symmetry. apply Hass.
  eapply swapped_spec_trans_exact.
  constructor. apply swapped_I'.
  apply Hswap.
Qed.

Lemma swapped_app_mid_R : forall {T} n (A B C D E : list T),
    swapped_spec n E (A ++ B ++ C ++ D) ->
    swapped_spec (S n) E (A ++ C ++ B ++ D).
Proof.
  intros *; intros H.
  eapply swapped_spec_comm in H.
  eapply swapped_spec_comm.
  eapply swapped_app_mid_L.
  apply H.
Qed.

Lemma swapped_spec_front_mid : forall {T} n (A B C D : list T),
    swapped_spec n B (C ++ D) ->
    existsT2 m, swapped_spec m (A ++ B) (C ++ A ++ D).
Proof.
  intros T n A B C D Hswap.
  eapply swapped_app_L in Hswap.
  eapply swapped_spec_trans. exact Hswap.
  rewrite <- app_nil_l.
  eapply swapped_app_mid_R.
  apply swapped_spec_refl.
  Unshelve. apply 0.
Qed.

Lemma swapped__n_mid : forall {T} m (l Gam1 Gam2 Gam1' Gam2' : list T),
    swapped_spec m (Gam1 ++ Gam2) (Gam1' ++ Gam2') ->
    existsT2 n, swapped_spec n (Gam1 ++ l ++ Gam2) (Gam1' ++ l ++ Gam2').
Proof.
  intros *.
  intros H. eapply swapped_app_L in H.
  rewrite <- app_nil_l in H.
  eexists.
  replace (Gam1 ++ l ++ Gam2) with (nil ++ Gam1 ++ l ++ Gam2).
  replace (Gam1' ++ l ++ Gam2') with (nil ++ Gam1' ++ l ++ Gam2').
  eapply swapped_app_mid_R.
  eapply swapped_app_mid_L.
  eapply H. all : reflexivity.
Qed.

(* Sequences of swaps, length implicit. *)
Inductive swapped_gen {T} Gam Delt :=
  swapped_gen_I : (existsT2 n, @swapped_spec T n Gam Delt) -> swapped_gen Gam Delt.

Lemma swapped_gen_front_mid : forall {T} (A B C D : list T),
    swapped_gen B (C ++ D) ->
    swapped_gen (A ++ B) (C ++ A ++ D).
Proof.
  intros T A B C D Hswap. inversion Hswap as [Hs].
  destruct Hs as [n Hs].
  constructor.
  eapply swapped_spec_front_mid. exact Hs.
Qed.

Lemma swapped_spec_opp : forall {T} n (A B C : list T),
    swapped_spec n B C ->
    swapped A B ->
    swapped_spec (S n) A C.
Proof.
  intros *; intros H1 H2.
  eapply swapped_spec_I in H2.
  eapply swapped_spec_trans_exact in H1.
  2 : eapply H2. auto.
Qed.

Lemma swapped__gen : forall {T} (A B : list T),
  swapped A B -> swapped_gen A B.
Proof.
  intros T A B H. constructor.
  exists 0. constructor. exact H.
Qed.

Lemma swapped_gen_app_L : forall {T} (l A B : list T),
    swapped_gen A B ->
    swapped_gen (l ++ A) (l ++ B).
Proof.
  intros T l A B H. inversion H as [H2].
  destruct H2 as [n H2]. constructor.
  eapply swapped_app_L in H2. exists n. exact H2.
Qed.

Lemma swapped_gen_refl : forall {T} (A : list T),
    swapped_gen A A.
Proof.
  intros T A. constructor.
  exists 0. apply swapped_spec_refl.
Qed.

(* tactics to identify swapped lists, where one of swap is single list *)

Ltac show_swapped_1 :=
  list_assoc_r' ;
  try (eapply arg_cong_imp ; [> list_assoc_l' ; reflexivity | ] ;
    apply swapped_simpleL ; list_eq_assoc) ;
  try (eapply arg1_cong_imp ; [> list_assoc_l' ; reflexivity | ] ;
    apply swapped_simpleR ; list_eq_assoc).

Ltac show_swapped_1_ns :=
  list_assoc_r ; (* not the ssreflext version *)
  try (eapply arg_cong_imp ; [> list_assoc_l' ; reflexivity | ] ;
    apply swapped_simpleL ; list_eq_assoc) ;
  try (eapply arg1_cong_imp ; [> list_assoc_l' ; reflexivity | ] ;
    apply swapped_simpleR ; list_eq_assoc).

(* this should work wherever swap_tac does, but trying to use it in place of
  swap_tac produces occasional failures - why?? - to investigate *)

Ltac swap_tac_Rc :=
  list_assoc_r ; try (apply swapped_same) ;
    repeat (apply swapped_L || apply swapped_cons) ;
    list_assoc_l ;
    repeat (apply swapped_R || apply swapped_Rc1 || apply swapped_Rc2) ;
    (show_swapped_1 || apply swapped_ca2 || apply swapped_ca1).

Ltac swap_tac :=
  list_assoc_r ; try (apply swapped_same) ;
    repeat (apply swapped_L || apply swapped_cons) ;
    list_assoc_l ; repeat (apply swapped_R) ; show_swapped_1.

Ltac swap_tac_ns :=
  list_assoc_r ; try (apply swapped_same) ;
    repeat (apply swapped_L || apply swapped_cons) ;
    list_assoc_l ; repeat (apply swapped_R) ; show_swapped_1_ns.

Goal forall T A B C D, swapped (A ++ B ++ C ++ D : list T) (D ++ A ++ B ++ C).
Proof. intros. show_swapped_1. Qed.

Goal forall T A B C D, swapped (D ++ A ++ B ++ C) (A ++ B ++ C ++ D : list T).
Proof. intros. show_swapped_1. Qed.