Mcltt.Core.Completeness.NatCases

From Coq Require Import Morphisms_Relations.

From Mcltt Require Import LibTactics.
From Mcltt.Core Require Import Base.
From Mcltt.Core.Completeness Require Import LogicalRelation SubstitutionCases TermStructureCases UniverseCases.
From Mcltt.Core.Semantic Require Import Realizability.
Import Domain_Notations.

Lemma rel_exp_of_nat_inversion : forall {Γ M M'},
    {{ Γ M M' : }} ->
    exists env_rel (_ : {{ EF Γ Γ per_ctx_env env_rel }}),
    forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_rel }}),
      rel_exp M ρ M' ρ' per_nat.
Proof.
  intros * [env_relΓ].
  destruct_conjs.
  eexists.
  eexists; [eassumption |].
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  eassumption.
Qed.

Lemma rel_exp_of_nat : forall {Γ M M'},
    (exists env_rel (_ : {{ EF Γ Γ per_ctx_env env_rel }}),
      forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_rel }}),
        rel_exp M ρ M' ρ' per_nat) ->
    {{ Γ M M' : }}.
Proof.
  intros * [env_relΓ].
  destruct_conjs.
  unshelve eexists_rel_exp; [constructor |].
  intros.
  eexists; split; mauto.
  econstructor; mauto.
  per_univ_elem_econstructor; mauto.
  reflexivity.
Qed.

#[export]
Hint Resolve rel_exp_of_nat : mcltt.

Ltac eexists_rel_exp_of_nat :=
  apply rel_exp_of_nat;
  eexists;
  eexists; [eassumption |].

Lemma valid_exp_nat : forall {Γ i},
    {{ Γ }} ->
    {{ Γ : Type@i }}.
Proof.
  intros * [env_relΓ].
  eexists_rel_exp_of_typ.
  intros.
  econstructor; mauto.
  eexists.
  per_univ_elem_econstructor.
  reflexivity.
Qed.

#[export]
Hint Resolve valid_exp_nat : mcltt.

Lemma rel_exp_nat_sub : forall {Γ σ i Δ},
    {{ Γ s σ : Δ }} ->
    {{ Γ [σ] : Type@i }}.
Proof.
  intros * [env_relΓ].
  destruct_conjs.
  eexists_rel_exp_of_typ.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  econstructor; mauto.
  eexists.
  per_univ_elem_econstructor.
  reflexivity.
Qed.

#[export]
Hint Resolve rel_exp_nat_sub : mcltt.

Lemma valid_exp_zero : forall {Γ},
    {{ Γ }} ->
    {{ Γ zero : }}.
Proof.
  intros * [env_relΓ].
  eexists_rel_exp_of_nat.
  intros.
  econstructor; mauto.
Qed.

#[export]
Hint Resolve valid_exp_zero : mcltt.

Lemma rel_exp_zero_sub : forall {Γ σ Δ},
    {{ Γ s σ : Δ }} ->
    {{ Γ zero[σ] zero : }}.
Proof.
  intros * [env_relΓ].
  destruct_conjs.
  eexists_rel_exp_of_nat.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  econstructor; mauto.
Qed.

#[export]
Hint Resolve rel_exp_zero_sub : mcltt.

Lemma rel_exp_succ_sub : forall {Γ σ Δ M},
    {{ Γ s σ : Δ }} ->
    {{ Δ M : }} ->
    {{ Γ (succ M)[σ] succ (M[σ]) : }}.
Proof.
  intros * [env_relΓ] [env_relΔ]%rel_exp_of_nat_inversion.
  destruct_conjs.
  pose env_relΔ.
  handle_per_ctx_env_irrel.
  eexists_rel_exp_of_nat.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  (on_all_hyp: destruct_rel_by_assumption env_relΔ).
  econstructor; mauto.
Qed.

#[export]
Hint Resolve rel_exp_succ_sub : mcltt.

Lemma rel_exp_succ_cong : forall {Γ M M'},
    {{ Γ M M' : }} ->
    {{ Γ succ M succ M' : }}.
Proof.
  intros * [env_relΓ]%rel_exp_of_nat_inversion.
  destruct_conjs.
  eexists_rel_exp_of_nat.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  econstructor; mauto.
Qed.

#[export]
Hint Resolve rel_exp_succ_cong : mcltt.

Lemma rel_exp_of_sub_id_zero_inversion : forall {Γ M M' A},
    {{ Γ M M' : A[Id,,zero] }} ->
    exists env_rel (_ : {{ EF Γ Γ per_ctx_env env_rel }}) i,
    forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_rel }}),
    exists elem_rel, rel_typ i A d{{{ ρ zero }}} A d{{{ ρ' zero }}} elem_rel /\ rel_exp M ρ M' ρ' elem_rel.
Proof.
  intros * [env_relΓ].
  destruct_conjs.
  eexists_rel_exp.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  mauto.
Qed.

Lemma rel_exp_of_sub_id_zero : forall {Γ M M' A},
    (exists env_rel (_ : {{ EF Γ Γ per_ctx_env env_rel }}) i,
      forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_rel }}),
      exists elem_rel, rel_typ i A d{{{ ρ zero }}} A d{{{ ρ' zero }}} elem_rel /\ rel_exp M ρ M' ρ' elem_rel) ->
    {{ Γ M M' : A[Id,,zero] }}.
Proof.
  intros * [env_relΓ].
  destruct_conjs.
  eexists_rel_exp.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  destruct_by_head rel_typ.
  destruct_by_head rel_exp.
  eexists.
  split; econstructor; mauto.
Qed.

Ltac eexists_rel_exp_of_sub_id_zero :=
  apply rel_exp_of_sub_id_zero;
  eexists_rel_exp.

Lemma rel_exp_of_sub_wkwk_succ_var1_inversion : forall {Γ M M' A},
    {{ Γ, , A M M' : A[WkWk,,succ(#1)] }} ->
    exists env_rel (_ : {{ EF Γ, , A Γ, , A per_ctx_env env_rel }}) i,
    forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_rel }}),
    exists elem_rel, rel_typ i A d{{{ ρ succ ^(ρ 1) }}} A d{{{ ρ' succ ^(ρ' 1) }}} elem_rel /\ rel_exp M ρ M' ρ' elem_rel.
Proof.
  intros * [env_relΓℕA].
  destruct_conjs.
  eexists_rel_exp.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓℕA).
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  mauto.
Qed.

Lemma rel_exp_of_sub_id_N : forall {Γ M M' N A},
    {{ Γ N : }} ->
    (exists env_rel (_ : {{ EF Γ Γ per_ctx_env env_rel }}) i,
      forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_rel }}),
      exists n n',
        {{ N ρ n }} /\
          {{ N ρ' n' }} /\
          exists elem_rel, rel_typ i A d{{{ ρ n }}} A d{{{ ρ' n' }}} elem_rel /\ rel_exp M ρ M' ρ' elem_rel) ->
    {{ Γ M M' : A[Id,,N] }}.
Proof.
  intros * []%rel_exp_of_nat_inversion [env_relΓ].
  destruct_conjs.
  pose env_relΓ.
  handle_per_ctx_env_irrel.
  eexists_rel_exp.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  destruct_by_head rel_exp.
  eexists; split; mauto.
  econstructor; mauto.
Qed.

Ltac eexists_rel_exp_of_sub_id_N :=
  apply rel_exp_of_sub_id_N; [eassumption |];
  eexists_rel_exp.

Lemma eval_natrec_sub_neut : forall {Γ env_relΓ σ Δ env_relΔ MZ MZ' MS MS' A A' i m m'},
    {{ DF Γ Γ per_ctx_env env_relΓ }} ->
    {{ DF Δ Δ per_ctx_env env_relΔ }} ->
    {{ Δ, A A' : Type@i }} ->
    {{ Δ MZ MZ' : A[Id,,zero] }} ->
    {{ Δ, , A MS MS' : A[WkWk,,succ(#1)] }} ->
    {{ Dom m m' per_bot }} ->
    (forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_relΓ }}) ρσ ρ'σ' mz mz',
        {{ σ s ρ ρσ }} ->
        {{ σ s ρ' ρ'σ' }} ->
        {{ Dom ρσ ρ'σ' env_relΔ }} ->
        {{ MZ ρσ mz }} ->
        {{ MZ' ρ'σ' mz' }} ->
        {{ Dom rec m under ρσ return A | zero -> mz | succ -> MS end rec m' under ρ' return A'[q σ] | zero -> mz' | succ -> MS'[q (q σ)] end per_bot }}).
Proof.
  intros * equiv_Γ_Γ equiv_Δ_Δ
             [env_relΔℕ]%rel_exp_of_typ_inversion
             []%rel_exp_of_sub_id_zero_inversion
             [env_relΔℕA]%rel_exp_of_sub_wkwk_succ_var1_inversion
             equiv_m_m'.
  destruct_conjs.
  pose env_relΔℕA.
  pose env_relΔℕ.
  handle_per_ctx_env_irrel.
  match_by_head (per_ctx_env env_relΔℕA) invert_per_ctx_env.
  handle_per_ctx_env_irrel.
  match_by_head (per_ctx_env env_relΔℕ) invert_per_ctx_env.
  handle_per_ctx_env_irrel.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΔ).
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  destruct_by_head rel_exp.
  destruct_conjs.
  functional_eval_rewrite_clear.
  match goal with
  | _: {{ σ s ρ ^?ρ1 }},
      _: {{ σ s ρ' ^?ρ2 }} |- _ =>
      rename ρ1 into ρσ;
      rename ρ2 into ρ'σ
  end.
  intro s.
  assert {{ Dom ⇑! s ⇑! s per_nat }} by mauto.
  assert {{ Dom ρσ ⇑! s ρ'σ ⇑! s env_relΔℕ }} as HinΔℕs by (apply_relation_equivalence; mauto).
  (on_all_hyp: fun H => destruct (H _ _ HinΔℕs)).
  assert {{ Dom succ (⇑! s) succ (⇑! s) per_nat }} by mauto.
  assert {{ Dom ρσ succ (⇑! s) ρ'σ succ (⇑! s) env_relΔℕ }} as HinΔℕsuccs by (apply_relation_equivalence; mauto).
  (on_all_hyp: fun H => destruct (H _ _ HinΔℕsuccs)).
  assert {{ Dom ρσ ⇑! s ρ'σ ⇑! s env_relΔℕ }} as HinΔℕs' by (apply_relation_equivalence; mauto).
  assert {{ Dom ρσ succ (⇑! s) ρ'σ succ (⇑! s) env_relΔℕ }} as HinΔℕsuccs' by (apply_relation_equivalence; mauto).
  assert {{ Dom zero zero per_nat }} by econstructor.
  assert {{ Dom ρσ zero ρ'σ zero env_relΔℕ }} as HinΔℕz by (apply_relation_equivalence; mauto).
  apply_relation_equivalence.
  (on_all_hyp: fun H => destruct (H _ _ HinΔℕs')).
  (on_all_hyp: fun H => destruct (H _ _ HinΔℕsuccs')).
  (on_all_hyp: fun H => destruct (H _ _ HinΔℕz)).
  destruct_by_head per_univ.
  destruct_conjs.
  handle_per_univ_elem_irrel.
  rename a' into a''.
  rename m'0 into a'.
  rename a1 into asucc.
  rename m'1 into asucc'.
  assert {{ Dom ρσ ⇑! s ⇑! a (S s) ρ'σ ⇑! s ⇑! a' (S s) env_relΔℕA }} as HinΔℕA.
  {
    apply_relation_equivalence; eexists; eauto.
    unfold drop_env.
    repeat change (fun n => d{{{ ^?ρσ ^?x ^?y }}} (S n)) with (fun n => d{{{ ρ x }}} n).
    repeat change (d{{{ ^?ρσ ^?x ^?y }}} 0) with y.
    eapply per_bot_then_per_elem; mauto.
  }
  apply_relation_equivalence.
  (on_all_hyp: fun H => destruct (H _ _ HinΔℕA)).
  destruct_conjs.
  destruct_by_head rel_typ.
  handle_per_univ_elem_irrel.
  destruct_by_head rel_exp.
  (on_all_hyp: fun H => edestruct (per_univ_then_per_top_typ H (S s)) as [? []]).
  functional_read_rewrite_clear.
  (on_all_hyp: fun H => unshelve epose proof (per_elem_then_per_top H _ s) as [? []]; shelve_unifiable; [eassumption |]).
  (on_all_hyp: fun H => unshelve epose proof (per_elem_then_per_top H _ (S (S s))) as [? []]; shelve_unifiable; [eassumption |]).
  functional_read_rewrite_clear.
  destruct (equiv_m_m' s) as [? []].
  do 3 econstructor; mauto.
  repeat econstructor; mauto.
Qed.

Corollary eval_natrec_neut : forall {Γ env_relΓ MZ MZ' MS MS' A A' i m m'},
    {{ DF Γ Γ per_ctx_env env_relΓ }} ->
    {{ Γ, A A' : Type@i }} ->
    {{ Γ MZ MZ' : A[Id,,zero] }} ->
    {{ Γ, , A MS MS' : A[WkWk,,succ(#1)] }} ->
    {{ Dom m m' per_bot }} ->
    (forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_relΓ }}) mz mz',
        {{ MZ ρ mz }} ->
        {{ MZ' ρ' mz' }} ->
        {{ Dom rec m under ρ return A | zero -> mz | succ -> MS end rec m' under ρ' return A' | zero -> mz' | succ -> MS' end per_bot }}).
Proof.
  intros.
  assert {{ Dom rec m under ρ return A | zero -> mz | succ -> MS end rec m' under ρ' return A'[q Id] | zero -> mz' | succ -> MS'[q (q Id)] end per_bot }} by (mauto using eval_natrec_sub_neut).
  etransitivity; [eassumption |].
  intros s.
  match_by_head per_bot ltac:(fun H => specialize (H s) as [? []]).
  eexists; split; [eassumption |].
  dir_inversion_by_head read_ne; subst.
  simplify_evals.
  mauto.
Qed.

Lemma eval_natrec_rel : forall {Γ env_relΓ MZ MZ' MS MS' A A' i m m'},
    {{ DF Γ Γ per_ctx_env env_relΓ }} ->
    {{ Γ, A A' : Type@i }} ->
    {{ Γ MZ MZ' : A[Id,,zero] }} ->
    {{ Γ, , A MS MS' : A[WkWk,,succ(#1)] }} ->
    {{ Dom m m' per_nat }} ->
    (forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_relΓ }}),
      forall elem_rel,
        rel_typ i A d{{{ ρ m }}} A d{{{ ρ' m' }}} elem_rel ->
        exists r r',
          {{ rec m return A | zero -> MZ | succ -> MS end ρ r }} /\
            {{ rec m' return A' | zero -> MZ' | succ -> MS' end ρ' r' }} /\
            {{ Dom r r' elem_rel }}).
Proof.
  intros * equiv_Γ_Γ HA HMZ HMS equiv_m_m'.
  induction equiv_m_m'; intros;
    apply rel_exp_of_typ_inversion in HA as [env_relΓℕ];
    apply rel_exp_of_sub_id_zero_inversion in HMZ as [];
    destruct_conjs;
    pose env_relΓℕ.
  - handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΓℕ) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    (on_all_hyp: destruct_rel_by_assumption env_relΓ).
    destruct_by_head rel_typ.
    destruct_by_head rel_exp.
    handle_per_univ_elem_irrel.
    do 2 eexists; repeat split; mauto.
  - rename n into m'.
    assert {{ Γ, A A : Type@i }} as []%rel_exp_of_typ_inversion by (etransitivity; mauto).
    apply rel_exp_of_sub_wkwk_succ_var1_inversion in HMS as [env_relΓℕA].
    destruct_conjs.
    pose env_relΓℕA.
    handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΓℕA) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΓℕ) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    (on_all_hyp_rev: destruct_rel_by_assumption env_relΓ).
    destruct_by_head rel_typ.
    invert_rel_typ_body.
    match goal with
    | _: env_relΓ ρ ?ρ0 |- _ =>
        rename ρ0 into ρ'
    end.
    assert {{ Dom ρ m ρ' m' env_relΓℕ }} by (apply_relation_equivalence; mauto).
    (on_all_hyp: destruct_rel_by_assumption env_relΓℕ).
    assert {{ Dom ρ m ρ' m' env_relΓℕ }} as HinΓℕ by (apply_relation_equivalence; mauto).
    apply_relation_equivalence.
    (on_all_hyp: fun H => directed destruct (H _ _ HinΓℕ)).
    destruct_by_head per_univ.
    unshelve epose proof (IHequiv_m_m' _ _ equiv_ρ_ρ' _ _) as [? [? [? []]]]; shelve_unifiable; [solve [mauto] |].
    handle_per_univ_elem_irrel.
    match goal with
    | _: {{ rec m return A | zero -> MZ | succ -> MS end ρ ^?r0 }},
        _: {{ rec m' return A' | zero -> MZ' | succ -> MS' end ρ' ^?r0' }} |- _ =>
        rename r0 into rm;
        rename r0' into rm'
    end.
    assert {{ Dom ρ m rm ρ' m' rm' env_relΓℕA }} as HinΓℕA by (apply_relation_equivalence; mauto).
    apply_relation_equivalence.
    (on_all_hyp: fun H => directed destruct (H _ _ HinΓℕA)).
    destruct_conjs.
    destruct_by_head rel_typ.
    destruct_by_head rel_exp.
    handle_per_univ_elem_irrel.
    do 2 eexists; mauto.
  - match goal with
    | _: per_bot m ?n |- _ =>
        rename n into m'
    end.
    handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΓℕ) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    (on_all_hyp: destruct_rel_by_assumption env_relΓ).
    (on_all_hyp_rev: destruct_rel_by_assumption env_relΓ).
    invert_rel_typ_body.
    match goal with
    | _: env_relΓ ρ ?ρ0 |- _ =>
        rename ρ0 into ρ'
    end.
    assert {{ Dom a m b m' per_nat }} by (econstructor; eassumption).
    assert {{ Dom ρ a m ρ' b m' env_relΓℕ }} as HinΓℕ by (apply_relation_equivalence; mauto).
    apply_relation_equivalence.
    (on_all_hyp: fun H => directed destruct (H _ _ HinΓℕ)).
    destruct_by_head per_univ.
    destruct_by_head rel_exp.
    destruct_by_head rel_typ.
    handle_per_univ_elem_irrel.
    do 2 eexists.
    repeat split; only 1-2: mauto.
    eapply per_bot_then_per_elem; [eassumption |].
    eapply eval_natrec_neut; eauto.
    + assert {{ EF Γ, Γ, per_ctx_env env_relΓℕ }} by (per_ctx_env_econstructor; eauto).
      eexists_rel_exp_of_typ.
      apply_relation_equivalence.
      eauto.
    + eexists_rel_exp_of_sub_id_zero.
      eauto.
Qed.

Lemma rel_exp_natrec_cong_rel_typ: forall {Γ A A' i M M' env_relΓ},
    {{ DF Γ Γ per_ctx_env env_relΓ }} ->
    {{ Γ, A A' : Type@i }} ->
    {{ Γ M M' : }} ->
    forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_relΓ }}) n n',
      {{ M ρ n }} ->
      {{ M' ρ' n' }} ->
      exists elem_rel,
        rel_typ i A d{{{ ρ n }}} A d{{{ ρ' n' }}} elem_rel.
Proof.
  intros.
  assert {{ Γ }} by (eexists; eauto).
  assert {{ Γ : Type@0 }} by mauto.
  assert {{ Γ M M' : [Id] }} by mauto 4.
  assert {{ Γ s Id,,M Id,,M' : Γ, }} by mauto.
  assert {{ Γ s Id,,M : Γ, }} by mauto.
  assert {{ Γ A[Id,,M] A[Id,,M'] : Type@i[Id,,M] }} by mauto.
  assert {{ Γ A[Id,,M] A[Id,,M'] : Type@i }} as []%rel_exp_of_typ_inversion by mauto.
  destruct_conjs.
  handle_per_ctx_env_irrel.
  (on_all_hyp_rev: destruct_rel_by_assumption env_relΓ).
  destruct_by_head per_univ.
  invert_rel_typ_body.
  apply rel_exp_implies_rel_typ.
  econstructor; mauto.
Qed.

Lemma rel_exp_natrec_cong : forall {Γ MZ MZ' MS MS' A A' i M M'},
    {{ Γ, A A' : Type@i }} ->
    {{ Γ MZ MZ' : A[Id,,zero] }} ->
    {{ Γ, , A MS MS' : A[WkWk,,succ(#1)] }} ->
    {{ Γ M M' : }} ->
    {{ Γ rec M return A | zero -> MZ | succ -> MS end rec M' return A' | zero -> MZ' | succ -> MS' end : A[Id,,M] }}.
Proof.
  intros * HAA' ? ? [env_relΓ]%rel_exp_of_nat_inversion.
  destruct_conjs.
  pose env_relΓ.
  assert {{ Γ M : }} by (unfold valid_exp_under_ctx; etransitivity; [| symmetry]; eauto using rel_exp_of_nat).
  assert {{ Γ M : }} as []%rel_exp_of_nat_inversion by eassumption.
  destruct_conjs.
  handle_per_ctx_env_irrel.
  eexists_rel_exp_of_sub_id_N.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  functional_eval_rewrite_clear.
  assert (exists elem_rel, rel_typ i A d{{{ ρ m }}} A d{{{ ρ' m' }}} elem_rel) as [elem_rel]
      by mauto using rel_exp_natrec_cong_rel_typ.
  assert (exists elem_rel, rel_typ i A d{{{ ρ m }}} A d{{{ ρ' m'0 }}} elem_rel) as []
      by mauto using rel_exp_natrec_cong_rel_typ.
  do 2 eexists.
  repeat split; [eassumption | eassumption |].
  eexists.
  split; [eassumption |].
  destruct_by_head rel_typ.
  handle_per_univ_elem_irrel.
  assert (exists r r',
             {{ rec m return A | zero -> MZ | succ -> MS end ρ r }} /\
               {{ rec m'0 return A' | zero -> MZ' | succ -> MS' end ρ' r' }} /\
               {{ Dom r r' elem_rel }})
    by mauto using eval_natrec_rel.
  destruct_conjs.
  econstructor; only 1-2: econstructor; mauto.
Qed.

#[export]
Hint Resolve rel_exp_natrec_cong : mcltt.

Lemma eval_natrec_sub_rel : forall {Γ env_relΓ σ Δ env_relΔ MZ MZ' MS MS' A A' i m m'},
    {{ DF Γ Γ per_ctx_env env_relΓ }} ->
    {{ DF Δ Δ per_ctx_env env_relΔ }} ->
    {{ Δ, A A' : Type@i }} ->
    {{ Δ MZ MZ' : A[Id,,zero] }} ->
    {{ Δ, , A MS MS' : A[WkWk,,succ(#1)] }} ->
    {{ Dom m m' per_nat }} ->
    (forall ρ ρ'
        (equiv_ρ_ρ' : {{ Dom ρ ρ' env_relΓ }})
        o o' elem_rel,
        {{ σ s ρ o }} ->
        {{ σ s ρ' o' }} ->
        {{ Dom o o' env_relΔ }} ->
        rel_typ i A d{{{ o m }}} A d{{{ o' m' }}} elem_rel ->
        exists r r',
          {{ rec m return A | zero -> MZ | succ -> MS end o r }} /\
            {{ rec m' return A'[q σ] | zero -> MZ'[σ] | succ -> MS'[q (q σ)] end ρ' r' }} /\
            {{ Dom r r' elem_rel }}).
Proof.
  intros * equiv_Γ_Γ equiv_Δ_Δ HA HMZ HMS equiv_m_m'.
  induction equiv_m_m'; intros;
    apply rel_exp_of_typ_inversion in HA as [env_relΔℕ];
    apply rel_exp_of_sub_id_zero_inversion in HMZ as [];
    destruct_conjs;
    pose env_relΔℕ.
  - handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΔℕ) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    (on_all_hyp: destruct_rel_by_assumption env_relΔ).
    destruct_by_head rel_typ.
    destruct_by_head rel_exp.
    handle_per_univ_elem_irrel.
    do 2 eexists; repeat split; only 1-2: econstructor; mauto.
  - match goal with
    | _: per_nat m ?n |- _ =>
        rename n into m'
    end.
    assert {{ Δ, A A : Type@i }} as []%rel_exp_of_typ_inversion by (etransitivity; mauto).
    apply rel_exp_of_sub_wkwk_succ_var1_inversion in HMS as [env_relΔℕA].
    destruct_conjs.
    pose env_relΔℕA.
    handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΔℕA) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΔℕ) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    (on_all_hyp_rev: destruct_rel_by_assumption env_relΔ).
    destruct_by_head rel_typ.
    invert_rel_typ_body.
    match goal with
    | _: {{ σ s ρ ^?ρ1 }},
        _: {{ σ s ρ' ^?ρ2 }} |- _ =>
        rename ρ1 into ρσ;
        rename ρ2 into ρ'σ
    end.
    assert {{ Dom ρσ m ρ'σ m' env_relΔℕ }} by (apply_relation_equivalence; mauto).
    (on_all_hyp: destruct_rel_by_assumption env_relΔℕ).
    assert {{ Dom ρσ m ρ'σ m' env_relΔℕ }} as HinΔℕ by (apply_relation_equivalence; mauto).
    apply_relation_equivalence.
    (on_all_hyp: fun H => directed destruct (H _ _ HinΔℕ)).
    destruct_by_head per_univ.
    unshelve epose proof (IHequiv_m_m' _ _ equiv_ρ_ρ' _ _ _ _ _ _ _) as [? [? [? []]]]; shelve_unifiable; only 4: solve [mauto]; eauto.
    handle_per_univ_elem_irrel.
    match goal with
    | _: {{ rec m return A | zero -> MZ | succ -> MS end ^_ ^?r0 }},
        _: {{ rec m' return A'[q σ] | zero -> MZ'[σ] | succ -> MS'[q (q σ)] end ^_ ^?r0' }} |- _ =>
        rename r0 into rm;
        rename r0' into rm'
    end.
    assert {{ Dom ρσ m rm ρ'σ m' rm' env_relΔℕA }} as HinΔℕA by (apply_relation_equivalence; mauto).
    apply_relation_equivalence.
    (on_all_hyp: fun H => directed destruct (H _ _ HinΔℕA)).
    destruct_conjs.
    destruct_by_head rel_typ.
    destruct_by_head rel_exp.
    handle_per_univ_elem_irrel.
    do 2 eexists; repeat split; only 1-2: repeat econstructor; mauto.
  - match goal with
    | _: per_bot m ?n |- _ =>
        rename n into m'
    end.
    handle_per_ctx_env_irrel.
    match_by_head (per_ctx_env env_relΔℕ) invert_per_ctx_env.
    handle_per_ctx_env_irrel.
    (on_all_hyp: destruct_rel_by_assumption env_relΔ).
    (on_all_hyp_rev: destruct_rel_by_assumption env_relΔ).
    invert_rel_typ_body.
    match goal with
    | _: {{ σ s ρ ^?ρ1 }},
        _: {{ σ s ρ' ^?ρ2 }} |- _ =>
        rename ρ1 into ρσ;
        rename ρ2 into ρ'σ
    end.
    assert {{ Dom a m b m' per_nat }} by (econstructor; eassumption).
    assert {{ Dom ρσ a m ρ'σ b m' env_relΔℕ }} as HinΔℕ by (apply_relation_equivalence; mauto).
    apply_relation_equivalence.
    (on_all_hyp: fun H => directed destruct (H _ _ HinΔℕ)).
    destruct_by_head per_univ.
    destruct_by_head rel_exp.
    destruct_by_head rel_typ.
    handle_per_univ_elem_irrel.
    do 2 eexists.
    repeat split; only 1-2: repeat econstructor; mauto.
    eapply per_bot_then_per_elem; [eassumption |].
    eapply eval_natrec_sub_neut; only 7: eauto; eauto.
    + assert {{ EF Δ, Δ, per_ctx_env env_relΔℕ }} by (per_ctx_env_econstructor; eauto).
      eexists_rel_exp_of_typ.
      apply_relation_equivalence.
      eauto.
    + eexists_rel_exp_of_sub_id_zero.
      eauto.
Qed.

Lemma rel_exp_natrec_sub_rel_typ: forall {Γ σ Δ A i M env_relΓ},
    {{ DF Γ Γ per_ctx_env env_relΓ }} ->
    {{ Γ s σ : Δ }} ->
    {{ Δ, A : Type@i }} ->
    {{ Δ M : }} ->
    forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_relΓ }}),
    exists elem_rel,
      rel_typ i {{{ A[σ,,M[σ]] }}} ρ {{{ A[σ,,M[σ]] }}} ρ' elem_rel.
Proof.
  intros.
  assert {{ Γ }} by (eexists; eauto).
  assert {{ Δ }} by (eapply presup_rel_sub; eauto).
  assert {{ Γ M[σ] : [σ] }} by mauto.
  assert {{ Δ : Type@0 }} by mauto.
  assert {{ Γ s σ,,M[σ] : Δ, }} by mauto.
  assert {{ Γ A[σ,,M[σ]] : Type@i[σ,,M[σ]] }} by mauto.
  assert {{ Γ A[σ,,M[σ]] : Type@i }} as []%rel_exp_of_typ_inversion by mauto.
  destruct_conjs.
  handle_per_ctx_env_irrel.
  mauto.
Qed.

Lemma rel_exp_natrec_sub : forall {Γ σ Δ MZ MS A i M},
    {{ Γ s σ : Δ }} ->
    {{ Δ, A : Type@i }} ->
    {{ Δ MZ : A[Id,,zero] }} ->
    {{ Δ, , A MS : A[WkWk,,succ(#1)] }} ->
    {{ Δ M : }} ->
    {{ Γ rec M return A | zero -> MZ | succ -> MS end[σ] rec M[σ] return A[q σ] | zero -> MZ[σ] | succ -> MS[q (q σ)] end : A[σ,,M[σ]] }}.
Proof.
  intros * [env_relΓ [? [env_relΔ]]] HA ? ? []%rel_exp_of_nat_inversion.
  destruct_conjs.
  handle_per_ctx_env_irrel.
  eexists_rel_exp.
  intros.
  assert (exists elem_rel, rel_typ i {{{ A[σ,,M[σ]] }}} ρ {{{ A[σ,,M[σ]] }}} ρ' elem_rel) as [elem_rel]
      by (eapply rel_exp_natrec_sub_rel_typ; only 5: eassumption; mauto; eexists; mauto).
  eexists.
  split; [eassumption |].
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  (on_all_hyp: destruct_rel_by_assumption env_relΔ).
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  match goal with
  | _: {{ σ s ^?ρ0 ^?ρσ0 }},
      _: {{ A ρσ ^?m0 ^?a0 }},
        _: {{ A ^?ρσ0 ^?m0' ^?a0' }} |- _ =>
      rename ρ0 into ρ';
      rename ρσ0 into ρ'σ;
      rename a0 into a;
      rename m0 into m;
      rename a0' into a';
      rename m0' into m'
  end.
  enough (exists r r',
             {{ rec m return A | zero -> MZ | succ -> MS end ρσ r }} /\
               {{ rec m' return A[q σ] | zero -> MZ[σ] | succ -> MS[q (q σ)] end ρ' r' }} /\
               {{ Dom r r' elem_rel }})
    by (destruct_conjs; econstructor; mauto).
  mauto 4 using eval_natrec_sub_rel.
Qed.

#[export]
Hint Resolve rel_exp_natrec_sub : mcltt.

Lemma rel_exp_nat_beta_zero : forall {Γ MZ MS A},
    {{ Γ MZ : A[Id,,zero] }} ->
    {{ Γ, , A MS : A[WkWk,,succ(#1)] }} ->
    {{ Γ rec zero return A | zero -> MZ | succ -> MS end MZ : A[Id,,zero] }}.
Proof.
  intros * [env_relΓ]%rel_exp_of_sub_id_zero_inversion [env_relΓℕA]%rel_exp_of_sub_wkwk_succ_var1_inversion.
  destruct_conjs.
  assert {{ Γ, }} as [env_relΓℕ] by (match_by_head (per_ctx_env env_relΓℕA) invert_per_ctx_env; eexists; eauto).
  destruct_conjs.
  pose env_relΓℕ.
  pose env_relΓℕA.
  match_by_head (per_ctx_env env_relΓℕA) invert_per_ctx_env.
  handle_per_ctx_env_irrel.
  match_by_head (per_ctx_env env_relΓℕ) invert_per_ctx_env.
  eexists_rel_exp_of_sub_id_zero.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  destruct_by_head rel_exp.
  match goal with
  | _: env_relΓ ρ ?ρ0 |- _ =>
      rename ρ0 into ρ'
  end.
  assert {{ Dom zero zero per_nat }} by econstructor.
  assert {{ Dom ρ zero ρ' zero env_relΓℕ }} by (apply_relation_equivalence; eauto).
  (on_all_hyp: destruct_rel_by_assumption env_relΓℕ).
  handle_per_univ_elem_irrel.
  eexists.
  split; econstructor; mauto.
Qed.

#[export]
Hint Resolve rel_exp_nat_beta_zero : mcltt.

Lemma rel_exp_nat_beta_succ_rel_typ : forall {Γ env_relΓ A i M},
    {{ DF Γ Γ per_ctx_env env_relΓ }} ->
    {{ Γ, A : Type@i }} ->
    {{ Γ M : }} ->
    forall ρ ρ' (equiv_ρ_ρ' : {{ Dom ρ ρ' env_relΓ }}),
    exists elem_rel,
      rel_typ i {{{ A[Id,,succ M] }}} ρ {{{ A[Id,,succ M] }}} ρ' elem_rel.
Proof.
  intros.
  assert {{ Γ }} by (eexists; eauto).
  assert {{ Γ : Type@0 }} by mauto.
  assert {{ Γ [Id] : Type@0 }} by mauto.
  assert {{ Γ succ M : [Id] }} by mauto.
  assert {{ Γ s Id,,succ M : Γ, }} by mauto.
  assert {{ Γ A[Id,,succ M] : Type@i }} as []%rel_exp_of_typ_inversion by mauto.
  destruct_conjs.
  handle_per_ctx_env_irrel.
  (on_all_hyp_rev: destruct_rel_by_assumption env_relΓ).
  invert_rel_typ_body.
  mauto.
Qed.

Lemma rel_exp_nat_beta_succ : forall {Γ MZ MS A i M},
    {{ Γ, A : Type@i }} ->
    {{ Γ MZ : A[Id,,zero] }} ->
    {{ Γ, , A MS : A[WkWk,,succ(#1)] }} ->
    {{ Γ M : }} ->
    {{ Γ rec succ M return A | zero -> MZ | succ -> MS end MS[Id,,M,,rec M return A | zero -> MZ | succ -> MS end] : A[Id,,succ M] }}.
Proof.
  intros * HA ? ? [env_relΓ]%rel_exp_of_nat_inversion.
  destruct_conjs.
  eexists_rel_exp.
  intros.
  (on_all_hyp: destruct_rel_by_assumption env_relΓ).
  assert (exists elem_rel,
             rel_typ i {{{ A[Id,,succ M] }}} ρ {{{ A[Id,,succ M] }}} ρ' elem_rel) as [elem_rel]
      by (eapply rel_exp_nat_beta_succ_rel_typ; mauto).
  eexists; split; [eassumption |].
  destruct_by_head rel_typ.
  invert_rel_typ_body.
  match goal with
  | _: env_relΓ ρ ?ρ0 |- _ =>
      rename ρ0 into ρ'
  end.
  assert (exists r r',
             {{ rec succ m return A | zero -> MZ | succ -> MS end ρ r }} /\
               {{ rec succ m' return A | zero -> MZ | succ -> MS end ρ' r' }} /\
               {{ Dom r r' elem_rel }})
    by (eapply eval_natrec_rel; mauto).
  destruct_conjs.
  dir_inversion_by_head eval_natrec; subst.
  econstructor; mauto.
Qed.

#[export]
Hint Resolve rel_exp_nat_beta_succ : mcltt.