Typing and Reduction Relation in PCF2


From Coq Require Import Lia List Init.Nat PeanoNat.
Require Import PCF2.preliminaries.
From PCF2.external.Synthetic Require Import Definitions DecidabilityFacts.
From PCF2.Autosubst Require Import unscoped pcf_2.
Set Default Goal Selector "!".

Infix "⟶" := Fun (at level 30, right associativity).

Inductive step: tm -> tm -> Prop :=
| Beta e e': step (app (lam e) e') (Subst_tm (scons e' ids) e)
| AppL e1 e1' e2: step e1 e1' -> step (app e1 e2) (app e1' e2)
| AppR e1 e2 e2': step e2 e2' -> step (app e1 e2) (app e1 e2')
| Lam e e': step e e' -> step (lam e) (lam e')
| If1 e1 e1' e2 e3: step e1 e1' -> step (trny e1 e2 e3) (trny e1' e2 e3)
| If2 e1 e2 e2' e3: step e2 e2' -> step (trny e1 e2 e3) (trny e1 e2' e3)
| If3 e1 e2 e3 e3': step e3 e3' -> step (trny e1 e2 e3) (trny e1 e2 e3')
| IfT e1 e2: step (trny tt e1 e2) e1
| IfF e1 e2: step (trny ff e1 e2) e2
| IfB e1 e2: step (trny bot e1 e2) bot
| If_nested e1 e2 e3 e4 e5: step (trny (trny e1 e2 e3) e4 e5) (trny e1 (trny e2 e4 e5) (trny e3 e4 e5)).

Notation " s ≻ t" := (step s t) (at level 50, t at next level).

Definition context: Type := list(ty).

Inductive typed: context -> tm -> ty -> Prop :=
| tt_typed Γ: typed Γ tt Base
| ff_typed Γ: typed Γ ff Base
| bot_typed Γ: typed Γ bot Base
| var_typed n A Γ: nth_error Γ n = Some A -> typed Γ (var_tm n) A
| fun_typed e typ1 typ2 Γ: typed (cons typ1 Γ) e typ2 -> typed Γ (lam e) (Fun typ1 typ2)
| app_typed e1 e2 typ1 typ2 Γ: typed Γ e1 (Fun typ1 typ2) -> typed Γ e2 typ1 -> typed Γ (app e1 e2) typ2
| trny_typed e1 e2 e3 Γ: typed Γ e1 Base -> typed Γ e2 Base -> typed Γ e3 Base -> typed Γ (trny e1 e2 e3) Base.

Notation "Γ ⊢ s : A" := (typed Γ s A) (at level 50, s, A at next level).

Require Import Coq.Relations.Relation_Operators.
Hint Constructors clos_refl_trans typed step : core.

Definition steps := clos_refl_trans tm step.
Hint Unfold steps : core.

Notation " s ≻* t" := (steps s t) (at level 50, t at next level).

Definition is_bval e := match e with
                      |tt => true
                      |ff => true
                      |bot => true
                      |_ => false
                      end.

Definition evaluates_to_base s s' := s ≻* s' /\ is_bval s' = true.

Notation " s ⇓ t" := (evaluates_to_base s t) (at level 50, t at next level).

Definition subst_well_typed (Γ': context) (Γ: context) (σ: nat -> tm):=
    forall n T, nth_error Γ n = Some T -> Γ' (σ n): T.

Definition ren_well_typed (Γ': context) (Γ: context) (r: nat -> nat):=
  forall n T, nth_error Γ n = Some T -> nth_error Γ' (r n) = Some T.

Notation " Γ' ⊢ σ [ Γ ]" := (subst_well_typed Γ' Γ σ) (at level 50, Γ, σ at next level).

Axiom weak_normalisation_comp:
  forall e, nil e: Base -> sig (fun v => e v).

Axiom church_rosser:
  forall s t u, s ≻* t -> s ≻* u -> exists w, t ≻* w /\ u ≻* w.

Definition embed (b: bool) :=
  match b with
  |true => tt
  |false => ff
  end.

Require Import Setoid Morphisms.

Instance steps_PreOrder : PreOrder steps.
Proof.
  split.
  - intros x. eapply rt_refl.
  - intros x y z. eapply rt_trans.
Qed.

Lemma app_left s s' t:
  s ≻* s' -> app s t ≻* app s' t.
Proof.
  induction 1; eauto.
Qed.

Lemma app_right s t t':
  t ≻* t' -> app s t ≻* app s t'.
Proof.
  induction 1; eauto.
Qed.

Instance app_proper : Proper (steps ==> steps ==> steps) app.
Proof.
  intros a b H1 c d H2.
  eapply rt_trans.
  - eapply app_left; eauto.
  - now apply app_right.
Qed.

Lemma if_cond s s' t u:
  s ≻* s' -> trny s t u ≻* trny s' t u.
Proof.
  induction 1; eauto.
Qed.

Lemma if_trm1 s t t' u:
  t ≻* t' -> trny s t u ≻* trny s t' u.
Proof.
  induction 1; eauto.
Qed.

Lemma if_trm2 s t u u':
  u ≻* u' -> trny s t u ≻* trny s t u'.
Proof.
  induction 1; eauto.
Qed.

Instance if_proper : Proper (steps ==> steps ==> steps ==> steps) trny.
Proof.
  intros a b H1 c d H2 e f H3.
  eapply rt_trans.
  - eapply if_cond; eauto.
  - eapply rt_trans.
    + eapply if_trm1; eauto.
    + now apply if_trm2.
Qed.

Instance lam_proper : Proper (steps ==> steps) lam.
Proof.
  intros a b H.
  induction H; eauto.
Qed.

Instance step_steps : subrelation step (steps).
Proof.
  intros ? ? ?. econstructor.
  eauto.
Qed.

Instance evaluates_steps : subrelation evaluates_to_base (steps).
Proof.
  firstorder.
Qed.

Lemma steps_1n s t u:
  s ≻* t -> t ≻* u -> s ≻* u.
Proof.
  intros H1 H2. now rewrite H1.
Qed.

Lemma steps_n1 s t u:
  s ≻* t -> t u -> s ≻* u.
Proof.
  intros H1 H2. now rewrite <- H2.
Qed.

Lemma step_eval s t v:
  s ≻* t -> t v -> s v.
Proof.
  intros; firstorder; eauto.
Qed.

Lemma val_no_step v t:
  is_bval v = true -> ~ v t.
Proof.
  intros H H'. destruct v; try easy; inv H'.
Qed.

Lemma base_eval t:
  nil t: Base -> (t tt) + (t ff) + (t bot).
Proof.
  intros typed_t. destruct (weak_normalisation_comp t typed_t).
  destruct x; try firstorder; discriminate.
Qed.

Lemma bval_steps_char v t:
  is_bval v = true -> v ≻* t -> t = v.
Proof.
  intros H H1. induction H1.
  2: easy.
  - destruct x; inv H0; discriminate.
  - rewrite IHclos_refl_trans2, IHclos_refl_trans1; try easy.
    now rewrite IHclos_refl_trans1.
Qed.

Lemma eval_step s t v:
  s ≻* t -> s v -> t v.
Proof.
  intros H H1; firstorder.
  destruct (church_rosser s t v); eauto.
  destruct H2. erewrite (bval_steps_char v x) in H2; eauto.
Qed.

Definition closed (s: tm) := allfv_tm (fun _ => False) s.

Lemma typed_free_var Γ s A:
  Γ s: A -> allfv_tm (fun n => exists T, nth_error Γ n = Some T) s.
Proof.
  intros typed_s. induction typed_s; firstorder.
  apply (allfvImpl_tm (fun n : nat => exists T : ty, nth_error (typ1 :: Γ) n = Some T)); eauto.
  intros n H. destruct n; eauto.
  unfold upAllfv_tm_tm. unfold up_allfv; now cbn.
Qed.

Lemma typed_empty_closed s A:
  nil s: A -> closed s.
Proof.
  intros typed_s.
  assert (H: allfv_tm (fun n => exists T, nth_error nil n = Some T) s) by exact (typed_free_var nil s A typed_s).
  unfold closed. apply (allfvImpl_tm (fun n : nat => exists T: ty, nth_error nil n = Some T)); eauto.
  intros n H1. destruct n, H1; discriminate.
Qed.

Lemma type_weakening Γ Γ' A s:
  Γ s : A -> (Γ ++ Γ') s : A.
Proof.
  intros typed_Γ. induction typed_Γ; eauto.
  econstructor. induction n in Γ, H |- *.
  - destruct Γ; cbn in *; try discriminate; eauto.
  - destruct Γ.
    + discriminate.
    + cbn in *. apply IHn; eauto.
Qed.

Lemma evaluates_to_same s v v':
  s v -> s v' -> v = v'.
Proof.
  intros [H1 H2] [H3 H4].
  assert (H5: exists w, v ≻* w /\ v' ≻* w).
  - apply (church_rosser s); eauto.
  - destruct H5 as [x [H5 H6]].
    assert (H7: x = v) by now apply bval_steps_char.
    assert (H8: x = v') by now apply bval_steps_char.
    now subst.
Qed.

Lemma ren_preserves' Γ Γ' r e A:
  ren_well_typed Γ' Γ r -> Γ e : A -> Γ' (Ren_tm r e) : A.
Proof.
  intros ren_wt typed_e. induction typed_e in r, Γ', ren_wt |- *; cbn; eauto.
  econstructor. eapply IHtyped_e. intros n T H.
  destruct n; cbn in *.
  + assumption.
  + now eapply ren_wt in H.
Qed.

Lemma ren_preserves Γ Γ' r e A:
  Γ' core.funcomp var_tm r [ Γ ] -> Γ e : A -> Γ' (Ren_tm r e) : A.
Proof.
  intros subst_wt typed_e. induction typed_e in r, Γ', subst_wt |- *; cbn; eauto.
  econstructor. eapply IHtyped_e.
  econstructor. destruct n; cbn in *.
  + assumption.
  + eapply subst_wt in H. now inversion H.
Qed.

Lemma subst_preserves Γ Γ' σ e A:
  Γ' σ [ Γ ] -> Γ e : A -> Γ' (Subst_tm σ e) : A.
Proof.
  intros subst_wt typed_e. induction typed_e in σ, Γ', subst_wt |- *; cbn; eauto.
  econstructor. eapply IHtyped_e. unfold up_tm_tm.
  intros n T H. destruct n; cbn; eauto.
  unfold core.funcomp.
  eapply ren_preserves.
  2:{ eapply subst_wt. cbn in *. assumption. }
  econstructor. cbn. assumption.
Qed.
Hint Resolve subst_preserves : core.

Lemma type_preservation_step Γ A s s':
  Γ s: A -> s s' -> Γ s': A.
Proof.
  intros H step_s. induction step_s in A, Γ, H |-*; inv H; try firstorder.
  2, 3: econstructor; eauto.
  - inv H3. assert (H: Γ (scons e' ids)[typ1 :: Γ]).
    + intros n T H. destruct n.
      * cbn in *. injection H. now intros ?; subst.
      * cbn in *. asimpl. now constructor.
    + eapply subst_preserves; eauto.
  - inv H4. econstructor; eauto.
Qed.

Lemma type_preservation Γ A s s':
  Γ s: A -> s ≻* s' -> Γ s': A.
Proof.
  intros typed_s steps.
  induction steps; eauto.
  eapply type_preservation_step; eauto.
Qed.

Lemma allfv_tm_dec:
  forall p, decidable p -> decidable (allfv_tm p).
Proof.
  intros p H. rewrite decidable_iff. rewrite decidable_iff in H. destruct H as [H]. constructor. intro trm.
  induction trm in p, H |- *; cbn; firstorder.
  destruct (IHtrm (upAllfv_tm_tm p)). 2,3: firstorder.
  intros []; firstorder.
Qed.

Lemma closed_dec:
  decidable closed.
Proof.
  unfold closed. eapply allfv_tm_dec. exists (fun _ => false).
  intros _. now firstorder.
Qed.


Lemma subst_charact_typed σ1 σ2 e Γ A:
  Γ e: A -> (forall n T, nth_error Γ n = Some T -> σ1 n = σ2 n) -> Subst_tm σ1 e = Subst_tm σ2 e.
Proof.
  intros typed_e eq.
  induction typed_e in σ1, σ2, eq |- *.
  1-4: eauto.
  - cbn. erewrite IHtyped_e. 1: reflexivity. intros n T H. destruct n; eauto. cbn. unfold core.funcomp.
    assert (σ1 n = σ2 n).
    + eapply eq. eauto.
    + rewrite H0; easy.
  - cbn. erewrite IHtyped_e1, IHtyped_e2. 1: reflexivity. all: eauto.
  - cbn. erewrite IHtyped_e1, IHtyped_e2, IHtyped_e3. 1: reflexivity. all: eauto.
Qed.

Require Import Lia.

Definition shifted_subst σ N :=
  fun n => match (S n - N) with
  |O => var_tm n
  |S _ => (iter N (fun s => up_tm_tm s) σ) n
  end.

Lemma subst_charact σ1 σ2 s:
  (forall n, σ1 n = σ2 n) -> Subst_tm σ1 s = Subst_tm σ2 s.
Proof.
  intros H. induction s in σ1, σ2, H |- *; cbn; eauto.
  - erewrite IHs1, IHs2, IHs3; eauto.
  - erewrite IHs1, IHs2; eauto.
  - erewrite IHs; eauto. intros n. destruct n; eauto.
    cbn. unfold core.funcomp. now rewrite H.
Qed.

Lemma shifted_subst_char σ N e:
  Subst_tm (shifted_subst σ N) e = Subst_tm (iter N (fun s => up_tm_tm s) σ) e.
Proof.
  eapply subst_charact.
  intros n. induction N in σ, n |- *; eauto. rewrite iter_succ_l.
  unfold shifted_subst in *. destruct (S n - S N) eqn: eq.
  2: easy.
  unfold shifted_subst. destruct n; cbn; eauto. unfold core.funcomp.
  erewrite <- IHN. destruct (S n - N) eqn: eq'; eauto. lia.
Qed.

Lemma fv_subst e N σ:
  allfv_tm (fun n => n < N) e -> Subst_tm (shifted_subst σ N) e = e.
Proof.
  intros H. induction e in N, H |- *; firstorder.
  - cbn in *. unfold shifted_subst. destruct (S n - N) eqn: e; eauto. lia.
  - cbn in *. now rewrite IHe1, IHe2, IHe3.
  - cbn in *. now rewrite IHe1, IHe2.
  - erewrite shifted_subst_char. specialize (IHe (N + 1)). rewrite shifted_subst_char in IHe. cbn in *. enough (up_tm_tm (iter N (fun s => up_tm_tm s) σ) = iter (N + 1) (fun s => up_tm_tm s) σ).
    + rewrite H1, IHe.
      1: easy.
      eapply (allfvImpl_tm (upAllfv_tm_tm (fun n : nat => n < N))); eauto.
      * intros n H2. destruct n; cbn in H2; lia.
    + assert (eq: N + 1 = S N) by lia. now rewrite eq.
Qed.

Lemma closed_subst e σ:
  closed e -> Subst_tm σ e = e.
Proof.
  intros H. assert (allfv_tm (fun n => n < 0) e -> Subst_tm (shifted_subst σ 0) e = e) by apply fv_subst.
  enough (Subst_tm (shifted_subst σ 0) e = Subst_tm σ e).
  - rewrite <- H1. eapply H0.
    eapply (allfvImpl_tm (fun n => False)); firstorder.
  - eapply subst_charact. intros n. unfold shifted_subst.
    destruct (S n - 0) eqn: eq; eauto. lia.
Qed.

Lemma ren_subst e f:
  ren_tm f e = Subst_tm (fun n => var_tm (f n)) e.
Proof.
  induction e in f |- *; cbn; eauto.
  - now rewrite IHe1, IHe2, IHe3.
  - now rewrite IHe1, IHe2.
  - rewrite IHe. enough (H: Subst_tm (fun n : nat => var_tm (upRen_tm_tm f n)) e = Subst_tm (up_tm_tm (fun n : nat => var_tm (f n))) e) by now rewrite H.
    eapply subst_charact. intros [|n]; cbn; eauto.
Qed.

Lemma closed_ren e f:
  closed e -> ren_tm f e = e.
Proof.
  rewrite ren_subst.
  eapply closed_subst.
Qed.

Lemma subst_concat Γ T A a σ1 σ2 e:
  (cons T Γ) e: A -> nil a: T -> nil σ2 [Γ] -> Subst_tm (scons a σ1) (Subst_tm (up_tm_tm σ2) e) = Subst_tm (scons a σ2) e.
Proof.
  intros. asimpl. eapply subst_charact_typed; eauto.
  intros [] ? ?; cbn in *; eauto.
  asimpl. unfold core.funcomp.
  erewrite closed_subst; eauto using typed_empty_closed.
Qed.

Lemma subst_shift T Γ A σ t:
(T :: Γ) t : A -> nil σ [(T :: Γ)] -> Subst_tm (fun n => σ (S n)) (Subst_tm (scons (σ 0) (fun n => var_tm n)) t) = Subst_tm σ t.
Proof.
intros typed_t subst_wt. asimpl. eapply subst_charact.
intros n. destruct n; eauto. cbn. erewrite closed_subst; eauto. eapply typed_empty_closed; eapply subst_wt; now cbn.
Qed.

Lemma funcomp_charact X Y Z (f f': X -> Y) (g g': Y -> Z) (x: X):
  g (f x) = g' (f' x) -> core.funcomp g f x = core.funcomp g' f' x.
  firstorder.
Qed.

Lemma subst_preserves_step σ e e':
  e e' -> (Subst_tm σ e) (Subst_tm σ e').
Proof.
  intros step_e.
  induction step_e in σ |- *; cbn.
   {enough (H: Subst_tm σ (Subst_tm (scons e' ids) e) = Subst_tm (scons (Subst_tm σ e') (fun n => var_tm n)) (Subst_tm (up_tm_tm σ) e)).
    + rewrite H. econstructor.
    + asimpl. eapply subst_charact. intros [ |n]; eauto. now asimpl. }
  all: now econstructor.
Qed.

Lemma subst_closes Γ σ e A:
  nil σ [Γ] -> Γ e: A -> closed (Subst_tm σ e).
Proof.
  intros. eapply typed_empty_closed. eapply subst_preserves; eauto.
Qed.

Lemma subst_well_typed_nil_nil σ:
  nil σ [nil].
Proof.
  red. intros n T H. destruct n; easy.
Qed.

Lemma subst_well_typed_singleton T t Γ σ:
  Γ t: T -> Γ scons t σ [cons T nil].
Proof.
  intros typed_t n A H.
  destruct n; cbn in *; eauto.
  - injection H. now intros ->.
  - destruct n; easy.
Qed.

Lemma subst_map_closed_list (σ: nat -> tm) (l: list tm):
  Forall (fun t => closed t) l -> map (Subst_tm σ) l = l.
Proof.
  intros H. induction l.
  1: easy.
  cbn. rewrite Forall_cons_iff in H. rewrite IHl. 2: easy.
  rewrite closed_subst; easy.
Qed.

Lemma iter_ren_closed f t n:
  closed t -> iter n (ren_tm f) t = t.
Proof.
  intros closed_t.
  induction n.
  1: easy. rewrite iter_succ_l.
  rewrite IHn. now rewrite closed_ren.
Qed.

Lemma up_tm_tm_iter1 n m σ:
  m < n -> iter n Up_tm_tm σ m = var_tm m.
Proof.
  intros H. induction n in σ, m, H |-*.
  1: easy.
  rewrite iter_succ_l. destruct m.
  1: easy. unfold Up_tm_tm at 1. unfold up_tm_tm. cbn.
  unfold core.funcomp. rewrite IHn. 2: lia. easy.
Qed.

Lemma up_tm_tm_iter2 n m σ:
  m >= n -> iter n Up_tm_tm σ m = iter n (ren_tm shift) (σ (m - n)).
Proof.
  intros H. induction n in σ, m, H |-*.
  1: now destruct m.
  rewrite iter_succ_l. destruct m.
  1: lia. unfold Up_tm_tm at 1. unfold up_tm_tm. cbn.
  unfold core.funcomp. rewrite IHn. 2: lia. easy.
Qed.

Lemma val_list_closed l:
    Forall (fun t : tm => is_bval t = true) l -> Forall (fun t : tm => closed t) l.
Proof.
    intros H. apply Forall_impl with (P := fun t => is_bval t = true); eauto.
    intros a. now destruct a.
Qed.