(* Lemma Par_Abs_inversion_Irrel_2: forall G D a b, Par G D (a_UAbs Irrel a) b -> (exists a', b = (a_UAbs Irrel a') /\ Par G D (open_tm_wrt_tm a (a_Bullet)) (open_tm_wrt_tm a' (a_Bullet))) \/ (exists a', Par G D a' b /\ (forall x, x `notin`  fv_tm_tm_tm a -> open_tm_wrt_tm a (a_Var_f x) = a_App a' Irrel a_Bullet)).   Lemma Par_CAbs_inversion : forall G D a b, Par G D (a_UCAbs a) b -> (exists a', b = (a_UCAbs a') /\ forall c, c `notin` fv_co_co_tm a \u fv_co_co_tm a' -> Par G D (open_tm_wrt_co a (g_Var_f c)) (open_tm_wrt_co a' (g_Var_f c))) \/ (exists a', Par G D a' b /\ (forall c, c `notin`  fv_co_co_tm a -> open_tm_wrt_co a (g_Var_f c) = a_CApp a' g_Triv)).   Lemma copen2 : forall c (b: co) S D a a', lc_co b -> c `notin` fv_co_co_tm a' \u fv_co_co_tm a -> Par S D (open_tm_wrt_co a (g_Var_f c)) (open_tm_wrt_co a' (g_Var_f c)) -> Par S D (open_tm_wrt_co a b) (open_tm_wrt_co a' b).  (* -------------------------------------------------------------------------------- *)
Ltac try_refl :=
try match goal with
| [ P2 : Par _ _ _ ?b |- _ ] =>
exists b; assert (lc_tm b); try eapply Par_lc2; eauto; try split; eauto; fail
end.
(* Ltac invert_syntactic_equality := match goal with | [ H : a_UAbs _ _ = a_UAbs _ _ |- _ ] => inversion H; subst; clear H | [ H : a_Pi _ _ _ = a_Pi _ _ _ |- _ ] => inversion H; subst; clear H | [ H : a_UCAbs _ = a_UCAbs _ |- _ ] => inversion H; subst; clear H | [ H : a_App _ _ _ = _ _ _ |- _ ] => inversion H; subst; clear H | [ H : a_CApp _ _  = a_CApp _ _ |- _ ] => inversion H; subst; clear H | [ H : a_Fam _ = a_Fam _ |- _ ] => inversion H; subst; clear H | [ H : a_CPi _ _ = a_CPi _ _ |- _ ] => inversion H; subst; clear H end. *)
Ltac invert_equality :=
match goal with
| [ H : _ = _ |- _ ] =>
inversion H
end.
Ltac try_refl_left :=
try match goal with
| [ P2 : Par _ _ ?b ?b |- exists cc:tm, Par ?S ?D ?b cc /\ Par ?S ?D ?a2 cc ] =>
exists a2; assert (lc_tm a2); try eapply Par_lc2; eauto; try split; eauto; fail
end.
Ltac try_refl_right :=
try match goal with
| [ P2 : Par _ _ ?b ?b |- exists cc:tm, Par ?S ?D ?a2 cc /\ Par ?S ?D ?b cc ] =>
exists a2; assert (lc_tm a2); try eapply Par_lc2; eauto; try split; eauto; fail
end.
Ltac invert_erased :=
match goal with
| [ H : erased_tm ?a |- _ ] => inversion H; subst; clear H
end.
(* Find prove that the result of Par is erased, and then invert that *)
Ltac invert_erased_tm b :=
let h0 := fresh in
match goal with
[ h : Par ?G ?D ?a b, h1: erased_tm ?a |- _ ] =>
assert (h0 : erased_tm b);
[ eapply (Par_erased_tm h); eauto | inversion h0; subst]
end.
(*    If we know that (a ^ x) == (App b rho x), replace (a ^ b0) with (App b rho b0) The tactic only succeeds if there is only 1 equality in the context. *)
Ltac eta_expand x :=
let h1 := fresh in
match goal with
| [ H18 : ∀ x : atom,
x `notin` ?L0
→ open_tm_wrt_tm ?a (a_Var_f x) = a_App ?b0 ?rho (a_Var_f x)
|- _ ] =>
pick fresh x for (L0 \u  fv_tm_tm_tm a \u fv_tm_tm_tm b0);
move: (H18 x ltac:(auto)) => h1; clear H18;
rewrite (@tm_subst_tm_tm_intro x a); auto; rewrite h1;
simpl; destruct (@eq_dec tmvar _ x x); try done;
rewrite tm_subst_tm_tm_fresh_eq; auto
| [ H18 : ∀ x : atom,
x `notin` ?L0
→ open_tm_wrt_tm ?a (a_Var_f x) = a_App ?b0 ?rho a_Bullet
|- _ ] =>
pick fresh x for (L0 \u  fv_tm_tm_tm a \u fv_tm_tm_tm b0);
move: (H18 x ltac:(auto)) => h1; clear H18;
rewrite (@tm_subst_tm_tm_intro x a); auto; rewrite h1;
simpl; destruct (@eq_dec tmvar _ x x); try done;
rewrite tm_subst_tm_tm_fresh_eq; auto
| [ H18 : ∀ x : atom,
x `notin` ?L0
→ open_tm_wrt_co ?a (g_Var_f x) = a_CApp ?b0 g_Triv
|- _ ] =>
pick fresh x for (L0 \u  fv_co_co_tm a \u fv_co_co_tm b0);
move: (H18 x ltac:(auto)) => h1; clear H18;
rewrite (@co_subst_co_tm_intro x a); auto; rewrite h1;
simpl; destruct (@eq_dec tmvar _ x x); try done;
rewrite co_subst_co_tm_fresh_eq; auto
end.
(* Rewrite a goal of the form ... (a'0 ^ b'0) ... To ... (b . b'0) ... and try to solve it, give eta-assn Y2 *)
Ltac eta_case a'0 Y2 :=
let x:= fresh in
pick fresh x;
rewrite (tm_subst_tm_tm_intro x a'0); auto;
rewrite Y2; auto; simpl;
rewrite (tm_subst_tm_tm_fresh_eq); auto;
destruct eq_dec; try done;
eauto; clear x.
Ltac invert_lc :=
match goal with
| [ H : lc_tm ?a |- _ ] => inversion H; subst; clear H
end.
Ltac use_size_induction a ac Par1 Par2 :=
match goal with
| [   IH : forall y: nat, ?T,
H2 : Good ?G ?D,
H3 : erased_tm a,
H : Par ?G ?D a ?b0,
H4 : Par ?G ?D a ?b1 |- _ ] =>
move: (@IH (size_tm a) ltac:(omega) a ltac:(auto) _ _ _ H2 H3 H _ H4) => [ ac [Par1 Par2]]
end.
Ltac use_size_induction_open a0 x ac Par1 Par2 :=
let h0 := fresh in
let h1 := fresh in
let h2 := fresh in
let EQ1 := fresh in
let EQ2 := fresh in
match goal with
| [  H : ∀ x : atom,
x `notin` ?L
→ Par ?S ?D (?open_tm_wrt_tm a0 (?a_Var_f x)) ?b,
H4: ∀ x : atom,
x `notin` ?L0
→ Par ?S ?D (?open_tm_wrt_tm a0 (?a_Var_f x)) ?c,
H1 : ∀ x : atom, x `notin` ?L1 →
erased_tm (?open_tm_wrt_tm a0 (?a_Var_f x)) |- _ ] =>
move: (H x ltac:(auto)) => h0; clear H;
move: (H4 x ltac:(auto)) => h1; clear H4;
move: (H1 x ltac:(auto)) => h2; clear H1;
move: (size_tm_open_tm_wrt_tm_var a0 x) => EQ1;
move: (size_tm_open_tm_wrt_co_var a0 x) => EQ2;
use_size_induction (open_tm_wrt_tm a0 (a_Var_f x)) ac Par1 Par2;
clear h0; clear h1; clear h2; clear EQ1; clear EQ2
end.
(* Ltac use_induction a ac Par1 Par2 := match goal with | [ IHPar1 : Good ?G ?D -> erased_tm a → ∀ a2 : tm, Par ?G ?D ?a a2 → ?T , H2 : Good ?G ?D, H3 : erased_tm a, H4 : Par ?G ?D a ?b |- _ ] => destruct (IHPar1 H2 H3 _ H4) as [ac [Par1 Par2]] end.  Ltac use_induction_on a b ac Par1 Par2 := match goal with | [ IHPar1 : Good ?G ?D -> erased_tm a → ∀ a2 : tm, Par ?G ?D ?a a2 → ?T , H2 : Good ?G ?D, H3 : erased_tm a, H4 : Par ?G ?D a b |- _ ] => destruct (IHPar1 H2 H3 _ H4) as [ac [Par1 Par2]] end. *)
Ltac par_erased_open x J Par4 :=
let K := fresh in
let KK := fresh in
let h0 := fresh in
match goal with
| [H13 : ∀ x : atom, x `notin` ?L →
Par ?G ?D (open_tm_wrt_tm ?a (a_Var_f x)) ?b,
H4 : ∀ x : atom, x `notin` ?L1 → erased_tm  (open_tm_wrt_tm ?a (a_Var_f x))
|- _ ] =>
have: x `notin` L; auto => h0;
pose K:= H13 x h0; clearbody K; clear h0;
have: x `notin` L1; auto => h0;
pose KK := H4 x h0; clearbody KK;
pose J := subst3 x Par4 KK K;
clearbody J;
repeat rewrite tm_subst_tm_tm_open_tm_wrt_tm in J; [auto;
simpl in J;
destruct eq_dec; try congruence;
repeat rewrite tm_subst_tm_tm_fresh_eq in J; auto
| try apply (Par_lc2 Par4); auto
| apply (Par_lc1 Par4); auto]
end.
Ltac finish_open_co a'0 :=
let K := fresh in
let J := fresh in
let h0 := fresh in
match goal with
| H12 : forall c, c `notin` ?L -> Par ?G ?D (open_tm_wrt_co a'0 (g_Var_f c)) (open_tm_wrt_co ?b (g_Var_f c)) |- _ =>
pick_fresh c;
have: c `notin` L; auto => h0;
pose K := H12 c h0; clearbody K;
pose J := subst4 c lc_g_Triv K;
clearbody J;
repeat rewrite co_subst_co_tm_open_tm_wrt_co in J; eauto;
simpl in J;
destruct eq_dec; try congruence;
repeat rewrite co_subst_co_tm_fresh_eq in J; eauto with lc
end.

(* Specifications: *)
Lemma open_tm_wrt_tm_bullet_var_eq: forall a x,
x `notin` fv_tm_tm_tm (open_tm_wrt_tm a (a_Var_f x)) ->
open_tm_wrt_tm a (a_Bullet) = open_tm_wrt_tm a (a_Var_f x).
Lemma open_tm_wrt_tm_inj_irrel: forall(a2 a1 : tm) (x1 : atom),
x1 `notin` fv_tm_tm_tm (open_tm_wrt_tm a2 (a_Var_f x1))
-> x1 `notin` fv_tm_tm_tm (open_tm_wrt_tm a1 (a_Var_f x1))
-> open_tm_wrt_tm a2 a_Bullet = open_tm_wrt_tm a1 (a_Var_f x1)
-> a2 = a1.
Lemma open_tm_wrt_co_triv_var_eq: forall a c,
c `notin` fv_co_co_tm (open_tm_wrt_co a (g_Var_f c)) ->
open_tm_wrt_co a g_Triv = open_tm_wrt_co a (g_Var_f c).
Lemma open_tm_wrt_co_inj: forall(a2 a1 : tm) (c : atom),
c `notin` fv_co_co_tm (open_tm_wrt_co a2 (g_Var_f c))
-> c `notin` fv_co_co_tm (open_tm_wrt_co a1 (g_Var_f c))
-> open_tm_wrt_co a2 g_Triv = open_tm_wrt_co a1 (g_Var_f c)
-> a2 = a1.
Lemma erased_fv_co: forall a x, erased_tm a -> x `notin` fv_co_co_tm a.
Lemma confluence_size : forall n a, size_tm a <= n ->  forall S D a1, Good S D -> erased_tm a -> Par S D a a1 -> forall a2, Par S D a a2 -> exists b, Par S D a1 b /\ Par S D a2 b.
(* Extra tactic? There's the same one above confluenze size.  Ltac use_size_induction b ac Par1 Par2 := match goal with | [   IH : forall y: nat, ?T, H2 : Good S D, H3 : erased_tm b, H : Par S D b ?b0, H4 : Par S D b ?b1 |- _ ] => move: (@IH (size_tm b) ltac:(omega) b ltac:(auto) _ _ _ H2 H3 H _ H4) => [ ac [Par1 Par2]] end. *)
Lemma confluence : forall S D a a1, Good S D -> erased_tm a -> Par S D a a1 -> forall a2, Par S D a a2 -> exists b, Par S D a1 b /\ Par S D a2 b.
(* ---------------------------------------------------------------------- *)
Lemma multipar_Star : forall S D A B, multipar S D A B -> A = a_Star -> B = a_Star.
Lemma multipar_Bullet : forall S D B, multipar S D a_Bullet B -> B = a_Bullet.
Inductive Path_consistent : const -> tm -> tm -> Prop :=
PC_Const : forall T, Path_consistent T (a_Const T) (a_Const T)
| PC_App   : forall T a1 a2 b1 b2,
erased_tm a2 -> erased_tm b2 ->
Path_consistent T a1 b1 ->
Path_consistent T (a_App a1 Rel a2) (a_App b1 Rel b2)
| PC_AppIrrel : forall T a1 b1,
Path_consistent T a1 b1 ->
Path_consistent T (a_App a1 Irrel a_Bullet) (a_App b1 Irrel a_Bullet)
| PC_CApp  : forall T a1 b1,
Path_consistent T a1 b1 ->
Path_consistent T (a_CApp a1 g_Triv) (a_CApp b1 g_Triv).