Require Import Undecidability.IntersectionTypes.CD.
Require Import Undecidability.IntersectionTypes.Util.CD_facts.
Require Undecidability.IntersectionTypes.Util.CD_sn.
Require Import Undecidability.LambdaCalculus.Util.term_facts.
Require Import PeanoNat Relations List Lia.
Import ListNotations TermNotations.
Import L (term, var, app, lam).
Import Lambda.
Require Import ssreflect.
Set Default Goal Selector "!".
Unset Implicit Arguments.
Lemma Forall_exists_Forall2 {X Y : Type} (P : X -> Y -> Prop) l : Forall (fun x => exists (y : Y), P x y) l ->
exists l', Forall2 P l l'.
Proof.
elim.
- by exists nil.
- move=> ?? [y ?] _ [l' ?]. exists (y :: l'). by constructor.
Qed.
Inductive typable (M : term): Prop :=
typable_intro Gamma t : type_assignment Gamma M t -> typable M.
#[local] Notation is_nonzero := (fun n : nat => match n with 0 => False | S _ => True end).
Definition has_var_zero M := not (allfv is_nonzero M).
Inductive stepI : term -> term -> Prop :=
| stepIRed s t : has_var_zero s -> stepI (app (lam s) t) (subst (scons t var) s)
| stepILam s s' : stepI s s' -> stepI (lam s) (lam s')
| stepIAppL s s' t : stepI s s' -> stepI (app s t) (app s' t)
| stepIAppR s t t' : stepI t t' -> stepI (app s t) (app s t').
Inductive stepK : term -> term -> Prop :=
| stepKRed s t : normal_form t -> stepK (app (lam (ren S s)) t) s
| stepKLam s s' : stepK s s' -> stepK (lam s) (lam s')
| stepKNAppR s t t' : neutral (fun _ => True) s -> stepK t t' -> stepK (app s t) (app s t')
| stepKLAppR s t t' : stepK t t' -> stepK (app (lam (ren S s)) t) (app (lam (ren S s)) t')
| stepKAAppL s1 s2 s' t : stepK (app s1 s2) s' -> stepK (app (app s1 s2) t) (app s' t).
#[local] Notation step' M N := (stepI M N \/ stepK M N).
#[local] Notation steps' M N := (clos_refl_trans term (fun M N => step' M N) M N).
Fixpoint merge (Gamma1 Gamma2 : list ty) :=
match Gamma1 with
| nil => Gamma2
| (s1, phi1) :: Gamma1 =>
match Gamma2 with
| nil => (s1, phi1) :: Gamma1
| (s2, phi2) :: Gamma2 => (s1, phi1 ++ (s2 :: phi2)) :: merge Gamma1 Gamma2
end
end.
Lemma type_assignment_mergeL Delta Gamma M t :
type_assignment Gamma M t -> type_assignment (merge Delta Gamma) M t.
Proof.
apply: type_assignment_weaken.
elim: Delta Gamma.
- move=> /= > -> ?. by do 3 econstructor.
- move=> [??] > IH [|[??] ?] /=.
+ by case.
+ move=> [|?] /=.
* move=> > [] *. subst. do 3 econstructor; [done|].
right. apply /in_app_iff. by right.
* by apply: IH.
Qed.
Lemma type_assignment_mergeR Delta Gamma M t :
type_assignment Gamma M t -> type_assignment (merge Gamma Delta) M t.
Proof.
apply: type_assignment_weaken.
elim: Gamma Delta.
- by move=> ? [|].
- move=> [??] > IH [|[??] ?] /=.
+ move=> [|?] /=.
* move=> > [] *. subst. by do 3 econstructor.
* move=> *. by do 3 econstructor; eassumption.
+ move=> [|?] /=.
* move=> > [] *. subst. do 3 econstructor; [done|].
rewrite in_app_iff. tauto.
* by apply: IH.
Qed.
Lemma neutral_typableE M : neutral typable M -> forall t, exists Gamma, type_assignment Gamma M t.
Proof.
elim.
- move=> x t. exists (repeat (t, nil) (S x)). econstructor.
+ apply: nth_error_repeat. lia.
+ by left.
- move=> > _ IH [] Gamma s ? t.
move: (IH (arr s nil t)) => - [Gamma'] ?.
exists (merge Gamma Gamma'). econstructor.
+ apply: type_assignment_mergeL. by eassumption.
+ by apply: type_assignment_mergeR.
+ done.
Qed.
Lemma neutral_typableI P M : neutral P M -> typable M -> neutral typable M.
Proof.
elim.
- by constructor.
- move=> > _ IH _ [] > /type_assignmentE [] *.
constructor; [apply: IH|]; apply: typable_intro; eassumption.
Qed.
Lemma typable_lam M : typable M -> typable (lam M).
Proof.
move=> [] [|[??] ?] ?.
+ move=> /(type_assignment_mergeL [(atom 0, nil)]) ?.
do 2 econstructor. by eassumption.
+ move=> *. do 2 econstructor. by eassumption.
Qed.
Lemma nf_typable M : normal_form M -> typable M.
Proof.
elim.
- move=> x. apply: (typable_intro _ (repeat ((atom 0), nil) (S x)) (atom 0)).
econstructor; [|by left]. apply: nth_error_repeat. lia.
- move=> > ?. by apply: typable_lam.
- move=> x N ??.
have /neutral_typableE : neutral typable (app (var x) N) by do ? constructor.
move=> /(_ (atom 0)) [?] ?. econstructor. by eassumption.
- move=> > /normal_form_app_neutral /neutral_typableI /[apply].
move=> /neutral_typableE H _ [] Gamma t ?.
move: (H (arr t nil (atom 0))) => [Gamma'] ?.
apply: typable_intro. econstructor.
+ apply: (type_assignment_mergeL Gamma Gamma'). by eassumption.
+ by apply: type_assignment_mergeR.
+ done.
Qed.
Lemma arg_K_typable M N Gamma s phi t :
type_assignment Gamma (lam (ren S M)) (arr s phi t) ->
typable N ->
exists Gamma', type_assignment Gamma' (app (lam (ren S M)) N) t.
Proof.
move=> /type_assignmentE ? [] Gamma' s' ?.
exists (merge Gamma Gamma').
econstructor; [|by apply: type_assignment_mergeL; eassumption|by apply: Forall_nil].
apply: type_assignment_mergeR. constructor.
apply: type_assignment_fv; [eassumption|].
apply: allfv_ren. apply: allfv_trivial.
move=> >. by apply.
Qed.
Lemma stepK_expansion M N Gamma t : stepK M N -> type_assignment Gamma N t ->
match M with
| var _ => exists Gamma', type_assignment Gamma' M t
| app _ _ => exists Gamma', type_assignment Gamma' M t
| lam _ => typable M
end.
Proof.
have Hty : forall M t,
match M with
| var _ => exists Gamma', type_assignment Gamma' M t
| app _ _ => exists Gamma', type_assignment Gamma' M t
| lam _ => typable M
end -> typable M.
{ by move=> [] > => [[? /typable_intro]|[? /typable_intro]|]. }
move=> H. elim: H Gamma t.
- move=> > /nf_typable /arg_K_typable H.
move=> Gamma t /(type_assignment_ren _ ((atom 0, nil):: Gamma) S) H'.
apply: H. constructor. apply: H'. by case.
- move=> > ? IH Gamma [] > /type_assignmentE; [done|].
move=> /IH /Hty. by apply: typable_lam.
- move=> > ?? IH Gamma t /type_assignmentE [] >.
move=> ? /IH /Hty {}IH ?.
apply: neutral_typableE. constructor; [|done].
apply: neutral_typableI; [|econstructor]; eassumption.
- move=> > ? IH Gamma t /type_assignmentE [] >.
move=> ? /IH /Hty {}IH ?. by apply: arg_K_typable; eassumption.
- move=> > ? IH Gamma t /type_assignmentE [] >.
move=> /IH [?] ?? H. eexists (merge Gamma _). econstructor.
+ apply: type_assignment_mergeL. by eassumption.
+ by apply: type_assignment_mergeR.
+ apply: Forall_impl H => *. by apply: type_assignment_mergeR.
Qed.
Lemma type_assignment_allfv_substE Gamma1 Gamma2 M N s phi t :
allfv (Nat.iter (length Gamma1) (scons True) is_nonzero) M ->
type_assignment (Gamma1 ++ Gamma2)
(subst
(Nat.iter (length Gamma1)
(fun sigma => scons (var 0) (fun x => ren S (sigma x))) (scons N var)) M) t ->
type_assignment (Gamma1 ++ (s, phi) :: Gamma2) M t.
Proof.
move=> HM /(type_assignment_ren_fv _ (Gamma1 ++ (s, phi) :: Gamma2)
(fun n => match (length Gamma1) - n with 0 => S n | _ => n end)).
rewrite !simpl_term.
have : forall (A B C : Prop), A -> (B -> C) -> (A -> B) -> C by auto.
apply.
{ apply: allfv_subst. apply: allfv_impl HM.
elim: (Gamma1).
- by move=> [|x] /=.
- move=> ?? IH [|x] /=; [done|].
move=> /IH H'. apply: allfv_ren.
apply: allfv_impl H'.
move=> x' /= {}IH > /IH <-.
by case: (_ - _). }
congr type_assignment. rewrite -[RHS]subst_var_term.
apply: ext_allfv_subst_term.
apply: allfv_impl HM.
elim: (length Gamma1). { by case. }
move=> x IH [|?] /=; [done|].
move=> /IH. rewrite !simpl_term /=.
move: (Nat.iter _ _ _ _) => [? []|??|?] /=; [|done..].
move=> <-. congr var. by case: (_ - _).
Qed.
Lemma stepISubst_expansion M N Gamma t : has_var_zero M ->
type_assignment Gamma (subst (scons N var) M) t -> type_assignment Gamma (app (lam M) N) t.
Proof.
suff H : forall Gamma1 Gamma2,
not (allfv (Nat.iter (length Gamma1) (scons True) is_nonzero) M) ->
type_assignment (Gamma1 ++ Gamma2) (subst (Nat.iter (length Gamma1) (fun sigma => scons (var 0) (funcomp (ren S) sigma)) (scons N var)) M) t ->
exists s_phi,
type_assignment (Gamma1 ++ s_phi :: Gamma2) M t /\
type_assignment Gamma2 N (fst s_phi) /\
Forall (type_assignment Gamma2 N) (snd s_phi).
{ move=> HM /(H nil) [|[? ?]].
- move=> H'. apply: HM. apply: allfv_impl H'. by case.
- move=> [?] [??]. econstructor; [|eassumption..].
by constructor. }
move=> Gamma1 Gamma2. elim: M Gamma1 t.
- move=> y Gamma1 t /= H.
have <- : length Gamma1 = y.
{ elim: Gamma1 y H=> /=; [by case|].
move=> ?? IH [|y] /=; [done|].
by move=> /IH ->. }
move=> HN. exists (t, nil). clear H.
split; [|split; [|done]].
+ econstructor; [|by left].
by rewrite nth_error_app2 ?Nat.sub_diag; [lia|].
+ elim: Gamma1 HN; [done|].
move=> ? Gamma1 IH /= H. apply: IH.
move: H=> /(type_assignment_ren_fv _ (Gamma1 ++ Gamma2) Nat.pred).
rewrite !simpl_term. apply. apply: allfv_ren.
by apply: allfv_trivial=> - [|?] /=.
- move=> M1 + M2 + Gamma1 t.
move=> /(_ Gamma1) + /(_ Gamma1).
set P := (Nat.iter (length Gamma1) (scons True) is_nonzero).
move=> IH1 IH2 H /=.
have HP : forall n, Decidable.decidable (P n).
{ rewrite /P /Decidable.decidable /=.
elim: (Gamma1) => /=.
- case; by auto.
- move=> ?? IH [|?] /=; by auto. }
move=> /type_assignmentE [] s' phi'.
have := allfv_dec _ M2 HP.
have := allfv_dec _ M1 HP.
move=> [|] HM1 [|] HM2.
+ move: H=> /=. tauto.
+ move: HM1 => /type_assignment_allfv_substE /[apply] H'M1.
move=> /(IH2 _ HM2) [[s2 phi2]] [?] [??].
move=> /(@Forall_impl _ _ _ (fun t => IH2 t HM2)).
move=> /Forall_exists_Forall2 [tys] Hphi'.
exists (s2, (phi2 ++ concat (map (fun '(s', phi') => s' :: phi') tys))).
split; [|split].
* econstructor.
** by apply: H'M1.
** apply: type_assignment_weaken_assumption; [|by eassumption].
move=> ?. do ? rewrite /= in_app_iff; tauto.
** move: Hphi'. elim; [done|].
move=> ? [??] > /= [?] [??] _ H'. constructor.
*** apply: type_assignment_weaken_assumption; [|by eassumption].
move=> ?. do ? rewrite /= in_app_iff; tauto.
*** apply: Forall_impl H' => ?. apply: type_assignment_weaken_assumption.
move=> ?. do ? rewrite /= in_app_iff; tauto.
* by eassumption.
* apply /Forall_app. split; [by eassumption|].
apply /Forall_concat /Forall_map. elim: Hphi'; [done|].
move=> ? [??] > [_] /= [??] *. by do ? constructor.
+ move=> /(IH1 _ HM1) [[s phi]] [?] [??] H1M2 H2M2.
exists (s, phi). split; [|by split].
econstructor.
* by eassumption.
* by apply: type_assignment_allfv_substE; eassumption.
* apply: Forall_impl H2M2 => ?. by apply: type_assignment_allfv_substE.
+ move=> /(IH1 _ HM1) [[s1 phi1]] [?] [??].
move=> /(IH2 _ HM2) [[s2 phi2]] [?] [??].
move=> /(@Forall_impl _ _ _ (fun t => IH2 t HM2)).
move=> /Forall_exists_Forall2 [tys] Hphi'.
exists (s1, (phi1 ++ s2 :: phi2 ++ concat (map (fun '(s', phi') => s' :: phi') tys))).
split; [|split].
* econstructor.
** apply: type_assignment_weaken_assumption; [|by eassumption].
move=> ?. do ? rewrite /= in_app_iff; tauto.
** apply: type_assignment_weaken_assumption; [|by eassumption].
move=> ?. do ? rewrite /= in_app_iff; tauto.
** move: Hphi'. elim; [done|].
move=> ? [??] > [?] [??] _ H'. constructor.
*** apply: type_assignment_weaken_assumption; [|by eassumption].
move=> ?. do ? rewrite /= in_app_iff; tauto.
*** apply: Forall_impl H' => ?.
apply: type_assignment_weaken_assumption.
move=> ?. do ? rewrite /= in_app_iff; tauto.
* eassumption.
* apply /Forall_app. split; [done|].
apply: Forall_cons; [done|].
apply /Forall_app. split; [done|].
elim: Hphi'; [done|].
move=> ? [??] > /= [?] [??] _ ?.
by constructor; [|apply /Forall_app].
- move=> M IH Gamma1 [?|s' phi' t] /= HM /type_assignmentE; [done|].
move=> /(IH ((s', phi') :: Gamma1)) {}IH.
move: IH => [|[s phi]].
{ move=> H. apply: HM. apply: allfv_impl H. by case. }
move=> [?] [??]. exists (s, phi). by do 2 constructor.
Qed.
Lemma stepI_expansion M N Gamma t : stepI M N -> type_assignment Gamma N t ->
type_assignment Gamma M t.
Proof.
move=> H. elim: H Gamma t.
- move=> *. by apply: stepISubst_expansion.
- move=> > _ IH ? [|??] > /type_assignmentE ?; [done|].
constructor. by apply: IH.
- move=> > _ IH > /type_assignmentE [] *.
econstructor; [|eassumption..].
apply: IH. by eassumption.
- move=> > _ IH > /type_assignmentE [] > ?? H'. econstructor.
+ by eassumption.
+ by apply: IH.
+ apply: Forall_impl H' => ?. by apply: IH.
Qed.
Lemma step'_step M N : step' M N -> step M N.
Proof.
case.
- elim; by auto using step.
- elim; [|by auto using step..].
move=> > _. apply: stepSubst'. by rewrite !simpl_term.
Qed.
Lemma has_var_zero_dec M : has_var_zero M \/ exists M', M = ren S M'.
Proof.
case: (@allfv_dec (fun n => match n with 0 => False | _ => True end) M).
- move=> [|?]; tauto.
- move=> H. right. exists (ren Nat.pred M). rewrite simpl_term.
rewrite -[LHS]ren_id_term. apply: ext_allfv_ren_term.
by apply: allfv_impl H => - [|?].
- move=> ?. by left.
Qed.
Lemma step_step' M N : step M N -> exists N', step' M N'.
Proof.
elim: M N.
- by move=> > /stepE.
- move=> [?|M1 M2|M] IHM N IHN ?.
+ move=> /stepE [|[|]] [?] [] => [|/stepE|]; [done..|].
move=> /IHN [?] [] ? _.
* eexists. left. apply: stepIAppR. by eassumption.
* eexists. right. by apply: stepKNAppR; [constructor|eassumption].
+ have [[?]|H] := step_dec (app M1 M2).
* move=> /IHM [?] []; by eauto using stepK, stepI.
* move=> /stepE [|[|]] [?] [] => [|/H|]; [done..|].
move=> /IHN [?] []; [by eauto using stepK, stepI|].
move=> ? _. eexists. right. apply: stepKNAppR; [|by eassumption].
move: H => /not_step_normal_form /normal_form_app_neutral.
by apply: neutral_impl.
+ have [?|[? ->]] := has_var_zero_dec M.
{ by eauto using stepK, stepI. }
have [[?]|/not_step_normal_form ?] := step_dec N.
* move=> /IHN [?] []; by eauto using stepK, stepI.
* by eauto using stepK, stepI.
- move=> ? IH ? /stepE [?] [/IH [? [?|?]]].
+ by eauto using stepK, stepI.
+ by eauto using stepK, stepI.
Qed.
Lemma step'_or_nf M : (exists N, step' M N) \/ (normal_form M).
Proof.
by move: (step_dec M) => [[? /step_step']|/not_step_normal_form]; auto.
Qed.
Lemma sn_step'_nf M : sn M -> exists N, steps' M N /\ normal_form N.
Proof.
elim=> {}M _ IH. case: (step'_or_nf M).
- move=> [?] /[dup] /step'_step /IH [?] [??] ?.
eexists. split; [|by eassumption].
apply: rt_trans; [apply: rt_step|]; by eassumption.
- move=> ?. eexists. by split; [apply: rt_refl|].
Qed.
Lemma subject_expansion M N : step' M N -> typable N -> typable M.
Proof.
case.
+ by move=> /stepI_expansion H [] > /H /typable_intro.
+ move=> /stepK_expansion H [] > /H{H}.
by case: M => > => [[? /typable_intro]|[? /typable_intro]|].
Qed.
Lemma sn_type_assignment M : sn M -> exists Gamma t, type_assignment Gamma M t.
Proof.
move=> /sn_step'_nf [N] [/clos_rt_rt1n_iff]. elim.
- move=> ? /nf_typable [] *. do 2 eexists. by eassumption.
- move=> > /subject_expansion H _ /[apply].
move=> [?] [?] /typable_intro /H [] *. do 2 eexists. by eassumption.
Qed.
Theorem wn_step'_sn_step (M N : term) : steps' M N -> normal_form N -> sn M.
Proof.
move=> /clos_rt_rt1n_iff HMN HN.
suff: typable M.
{ move=> [] >. by apply: CD_sn.strong_normalization. }
elim: HMN HN.
- by apply: nf_typable.
- move=> > /subject_expansion. by auto.
Qed.
Require Import Undecidability.Synthetic.Definitions.
Theorem reduction : SNclosed ⪯ CD_TYP.
Proof.
exists (fun '(exist _ M _) => M).
move=> [t Ht] /=. split.
- move=> ?. by apply: sn_type_assignment.
- move=> [?] [?]. by apply: CD_sn.strong_normalization.
Qed.