Require Import List Lia.
Import ListNotations.
Require Import Undecidability.SystemF.SysF Undecidability.SystemF.Autosubst.syntax Undecidability.SystemF.Autosubst.unscoped.
Import UnscopedNotations.
From Undecidability.SystemF.Util Require Import Facts poly_type_facts pure_term_facts.
Require Import ssreflect ssrbool ssrfun.
Set Default Goal Selector "!".
Lemma subst_term_up_term_term_var {σ t}: subst_term σ (up_term_term var) t = subst_term σ var t.
Proof. apply: ext_term; [done | by case]. Qed.
Lemma subst_term_up_term_poly_type {σ τ t}: subst_term (up_term_poly_type σ) τ t = subst_term σ τ t.
Proof.
apply: ext_term; last done.
move=> ?. by rewrite /up_term_poly_type /funcomp /= ren_poly_type_id.
Qed.
Lemma subst_term_up_poly_type_term_var {σ t}: subst_term σ (up_poly_type_term var) t = subst_term σ var t.
Proof. apply: ext_term; [done | by case]. Qed.
Lemma subst_term_poly_var_var {P} : subst_term poly_var var P = P.
Proof. by apply: idSubst_term. Qed.
Lemma subst_term_ren_term {σ σ' ξ ξ' P} : subst_term σ σ' (ren_term ξ ξ' P) = subst_term (ξ >> σ) (ξ' >> σ') P.
Proof. by apply: compRenSubst_term. Qed.
Definition term_norm := (@subst_term_up_term_term_var, @subst_term_up_term_poly_type, @subst_term_up_poly_type_term_var, @subst_term_ren_term).
Inductive head_form : term -> Prop :=
| head_form_var {x} : head_form (var x)
| head_form_app {P Q} : head_form P -> normal_form Q -> head_form (app P Q)
| head_form_ty_app {P t} : head_form P -> head_form (ty_app P t)
with normal_form : term -> Prop :=
| normal_form_head_form {P} : head_form P -> normal_form P
| normal_form_abs {s P} : normal_form P -> normal_form (abs s P)
| normal_form_ty_abs {P} : normal_form P -> normal_form (ty_abs P).
Scheme normal_form_ind' := Induction for normal_form Sort Prop
with head_form_ind' := Induction for head_form Sort Prop.
Fact normal_form_ren_term {ζ ξ P} :
normal_form P -> normal_form (ren_term ζ ξ P).
Proof.
move=> H. move: P H ζ ξ.
apply: (normal_form_ind'
(fun P _ => forall ζ ξ, normal_form (ren_term ζ ξ P))
(fun P _ => forall ζ ξ, head_form (ren_term ζ ξ P)));
by eauto using normal_form, head_form.
Qed.
Fact head_form_ren_term {ζ ξ P} :
head_form P -> head_form (ren_term ζ ξ P).
Proof.
move=> H. move: P H ζ ξ.
apply: (head_form_ind'
(fun P _ => forall ζ ξ, normal_form (ren_term ζ ξ P))
(fun P _ => forall ζ ξ, head_form (ren_term ζ ξ P)));
by eauto using normal_form, head_form.
Qed.
Fixpoint erase (P: term) : pure_term :=
match P with
| var x => pure_var x
| app P Q => pure_app (erase P) (erase Q)
| abs _ P => pure_abs (erase P)
| ty_app P _ => erase P
| ty_abs P => erase P
end.
Fact erase_subst_term_var {P σ} : erase (subst_term σ var P) = erase P.
Proof.
elim: P σ; move=> /= *; rewrite ?subst_term_up_term_term_var ?subst_term_up_poly_type_term_var; by congruence.
Qed.
Definition many_app (P: term) (Qs: list term) :=
fold_left app Qs P.
Definition many_ty_app (P: term) (ts: list poly_type) :=
fold_left ty_app ts P.
Definition many_ty_abs (n: nat) (P: term) :=
Nat.iter n ty_abs P.
Fact many_app_app {P Qs Qs'} : many_app P (Qs ++ Qs') = many_app (many_app P Qs) Qs'.
Proof. by rewrite /many_app fold_left_app. Qed.
Fact many_ty_app_app {P ts ts'} : many_ty_app P (ts ++ ts') = many_ty_app (many_ty_app P ts) ts'.
Proof. by rewrite /many_ty_app fold_left_app. Qed.
Definition many_pure_app (M: pure_term) (Ns: list pure_term) :=
fold_left pure_app Ns M.
Fact many_pure_app_app {M Ns Ns'} :
many_pure_app M (Ns ++ Ns') = many_pure_app (many_pure_app M Ns) Ns'.
Proof. by rewrite /many_pure_app fold_left_app. Qed.
Definition many_pure_term_abs (n: nat) (M: pure_term) :=
Nat.iter n pure_abs M.
Inductive argument : Type :=
| argument_term : term -> argument
| argument_poly_type : poly_type -> argument.
Definition many_argument_app (P: term) (As: list argument) :=
fold_left
(fun P A =>
match A with
| argument_term Q => app P Q
| argument_poly_type t => ty_app P t
end)
As P.
Fact many_argument_app_app {P As As'} : many_argument_app P (As ++ As') = many_argument_app (many_argument_app P As) As'.
Proof. by rewrite /many_argument_app fold_left_app. Qed.
Lemma many_argument_appI {P} : head_form P ->
exists x As, P = many_argument_app (var x) As /\ Forall (fun A => if A is argument_term Q then normal_form Q else True) As.
Proof.
elim.
- move=> x. by exists x, [].
- move=> {}P Q _ [x] [As] [-> HAs] ?. exists x, (As ++ [argument_term Q]).
constructor; first by rewrite many_argument_app_app.
rewrite Forall_app. constructor; [done | by constructor].
- move=> {}P t _ [x] [As] [-> HAs]. exists x, (As ++ [argument_poly_type t]).
constructor; first by rewrite many_argument_app_app.
rewrite Forall_app. constructor; [done | by constructor].
Qed.
Fact erase_many_ty_abs {n P} : erase (many_ty_abs n P) = erase P.
Proof. elim: n => /= *; by congruence. Qed.
Fact erase_many_ty_app {ts P} : erase (many_ty_app P ts) = erase P.
Proof. elim: ts P; [done | by move=> > + ? /= => ->]. Qed.
Lemma erase_ren_term_id {ξ P} : erase (ren_term ξ id P) = erase P.
Proof.
elim: P ξ.
- done.
- by move=> ? + ? + ? /= => -> ->.
- move=> ? ? IH ? /=.
under extRen_term => [? | ?] do [| rewrite /upRen_term_term up_ren_id].
by rewrite IH.
- by move=> ? + ? ? /= => ->.
- by move=> ? + ? /= => ->.
Qed.
Fixpoint term_size (P: term) :=
match P with
| var _ => 1
| app P Q => 1 + term_size P + term_size Q
| abs _ P => 1 + term_size P
| ty_app P _ => 1 + term_size P
| ty_abs P => 1 + term_size P
end.
Lemma term_size_pos (P: term) : term_size P = S (term_size P - 1).
Proof. case: P => /= *; by lia. Qed.
Fact term_size_ren_term {ζ ξ P} : term_size (ren_term ζ ξ P) = term_size P.
Proof. elim: P ζ ξ => /=; by congruence. Qed.
Lemma term_size_many_app_le P Qs : term_size P <= term_size (many_app P Qs).
Proof.
elim: Qs P; first by move=> /=; lia.
move=> Q Qs + P => /(_ (app P Q)) /=. by lia.
Qed.
Lemma many_argument_app_map_argument_poly_type {P ts} :
many_argument_app P (map argument_poly_type ts) = many_ty_app P ts.
Proof. elim: ts P; [done | move=> /= *; by congruence]. Qed.
Lemma many_argument_app_map_argument_term {P Qs} :
many_argument_app P (map argument_term Qs) = many_app P Qs.
Proof. elim: Qs P; [done | move=> /= *; by congruence]. Qed.
Lemma normal_form_many_app {P Qs} : normal_form (many_app P Qs) -> normal_form P /\ Forall normal_form Qs.
Proof.
elim /rev_ind: Qs P; first by (move=> *; constructor).
move=> Q Qs IH P. rewrite many_app_app /=.
move H1P': (app _ _) => P' H2P'. case: H2P' H1P'; [|done|done].
move=> ? []; [done| |done]. move=> ? ? /normal_form_head_form + + [? ?]. subst.
move=> /IH [? ?] ?. constructor; first done.
apply /Forall_appP. constructor; first done.
by constructor.
Qed.
Fixpoint allfv_term (p: nat -> Prop) (P: term) :=
match P with
| var x => p x
| app P Q => allfv_term p P /\ allfv_term p Q
| abs _ P => allfv_term (scons True p) P
| ty_app P _ => allfv_term p P
| ty_abs P => allfv_term p P
end.
Lemma allfv_term_impl {p1 p2: nat -> Prop} {P}:
(forall x, p1 x -> p2 x) -> allfv_term p1 P -> allfv_term p2 P.
Proof.
elim: P p1 p2.
- move=> >. by apply.
- by move=> ? IH1 ? IH2 > /= /copy [/IH1 {}IH1 /IH2 {}IH2] [/IH1 ? /IH2 ?].
- move=> > IH > H /=. apply: IH. by case.
- move=> > IH > H /=. by apply: IH.
- move=> > IH > H /=. by apply: IH.
Qed.
Lemma allfv_pure_term_erase {p P}:
allfv_pure_term p (erase P) -> allfv_term p P.
Proof.
elim: P p; [ done | | | done | done ].
- by move=> ? IH1 ? IH2 > /= [/IH1 ? /IH2 ?].
- by move=> > IH > /= /IH.
Qed.
Lemma erase_ren_term {ξ σ P} : erase (ren_term σ ξ P) = ren_pure_term ξ (erase P).
Proof.
elim: P ξ σ.
- done.
- by move=> ? + ? + ? ? /= => -> ->.
- by move=> ? ? + ? ? /= => ->.
- done.
- by move=> ? + ? ? /= => ->.
Qed.