Mctt.Core.Soundness.LogicalRelation.Lemmas

From Coq Require Import Equivalence Morphisms Morphisms_Prop Morphisms_Relations Relation_Definitions RelationClasses.

From Mctt Require Import LibTactics.
From Mctt.Core Require Import Base.
From Mctt.Core.Completeness Require Import FundamentalTheorem.
From Mctt.Core.Semantic Require Import Realizability.
From Mctt.Core.Soundness Require Export Realizability.
Import Domain_Notations.

Add Parametric Morphism i a Γ A M : (glu_elem_bot i a Γ A M)
    with signature per_bot ==> iff as glu_elem_bot_morphism_iff4.
Proof.
  intros m m' Hmm' *.
  split; intros []; econstructor; mauto 3;
    try (etransitivity; mauto 4);
    intros;
    specialize (Hmm' (length Δ)) as [? []];
    functional_read_rewrite_clear;
    mauto.
Qed.

Add Parametric Morphism i a Γ A M R (H : per_univ_elem i R a a) : (glu_elem_top i a Γ A M)
    with signature R ==> iff as glu_elem_top_morphism_iff4.
Proof.
  intros m m' Hmm' *.
  split; intros []; econstructor; mauto 3;
    pose proof (per_elem_then_per_top H Hmm') as Hmm'';
    try (etransitivity; mauto 4);
    intros;
    specialize (Hmm'' (length Δ)) as [? []];
    functional_read_rewrite_clear;
    mauto.
Qed.

Lemma glu_univ_elem_typ_unique_upto_exp_eq : forall {i j a P P' El El' Γ A A'},
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem j P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A' ® P' }} ->
    {{ Γ A A' : Type@(max i j) }}.
Proof with mautosolve 4.
  intros.
  assert {{ Γ A ® glu_typ_top i a }} as [] by mauto 3.
  assert {{ Γ A' ® glu_typ_top j a }} as [] by mauto 3.
  match_by_head per_top_typ ltac:(fun H => destruct (H (length Γ)) as [V []]).
  clear_dups.
  functional_read_rewrite_clear.
  assert {{ Γ A : Type@(max i j) }} by mauto 4 using lift_exp_max_left.
  assert {{ Γ A' : Type@(max i j) }} by mauto 4 using lift_exp_max_right.
  assert {{ Γ A[Id] V : Type@i }} by mauto 4.
  assert {{ Γ A[Id] V : Type@(max i j) }} by mauto 4 using lift_exp_eq_max_left.
  assert {{ Γ A'[Id] V : Type@j }} by mauto 4.
  assert {{ Γ A'[Id] V : Type@(max i j) }} by mauto 4 using lift_exp_eq_max_right.
  autorewrite with mctt in *...
Qed.

#[export]
Hint Resolve glu_univ_elem_typ_unique_upto_exp_eq : mctt.

Lemma glu_univ_elem_typ_unique_upto_exp_eq_ge : forall {i j a P P' El El' Γ A A'},
    i <= j ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem j P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A' ® P' }} ->
    {{ Γ A A' : Type@j }}.
Proof with mautosolve 4.
  intros.
  replace j with (max i j) by lia...
Qed.

#[export]
Hint Resolve glu_univ_elem_typ_unique_upto_exp_eq_ge : mctt.

Lemma glu_univ_elem_typ_unique_upto_exp_eq' : forall {i a P El Γ A A'},
    {{ DG a glu_univ_elem i P El }} ->
    {{ Γ A ® P }} ->
    {{ Γ A' ® P }} ->
    {{ Γ A A' : Type@i }}.
Proof. mautosolve 4. Qed.

#[export]
Hint Resolve glu_univ_elem_typ_unique_upto_exp_eq' : mctt.

Lemma glu_univ_elem_per_univ_elem_typ_escape : forall {i a a' elem_rel P P' El El' Γ A A'},
    {{ DF a a' per_univ_elem i elem_rel }} ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a' glu_univ_elem i P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A' ® P' }} ->
    {{ Γ A A' : Type@i }}.
Proof with mautosolve 4.
  simpl in *.
  intros * Hper Hglu Hglu' HA HA'.
  assert {{ DG a glu_univ_elem i P' El' }} by (setoid_rewrite Hper; eassumption).
  handle_functional_glu_univ_elem...
Qed.

#[export]
Hint Resolve glu_univ_elem_per_univ_elem_typ_escape : mctt.

Lemma glu_univ_elem_per_univ_typ_escape : forall {i a a' P P' El El' Γ A A'},
    {{ Dom a a' per_univ i }} ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a' glu_univ_elem i P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A' ® P' }} ->
    {{ Γ A A' : Type@i }}.
Proof.
  intros * [] **...
  mauto 4.
Qed.

#[export]
Hint Resolve glu_univ_elem_per_univ_typ_escape : mctt.

Lemma glu_univ_elem_exp_unique_upto_exp_eq : forall {i j a P P' El El' Γ A A' M M' m},
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem j P' El' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ M' : A' ® m El' }} ->
    {{ Γ M M' : A }}.
Proof.
  intros.
  assert {{ Γ M : A ® m glu_elem_top i a }} as [] by mauto 3.
  assert {{ Γ M' : A' ® m glu_elem_top j a }} as [] by mauto 3.
  assert {{ Γ A A' : Type@(max i j) }} by mauto 3.
  match_by_head per_top ltac:(fun H => destruct (H (length Γ)) as [W []]).
  clear_dups.
  assert {{ Γ M[Id] W : A[Id] }} by mauto 4.
  assert {{ Γ M[Id] W : A }} by (gen_presups; mauto 4).
  assert {{ Γ M : A }} by mauto 4.
  assert {{ Γ M'[Id] W : A'[Id] }} by mauto 4.
  assert {{ Γ M'[Id] W : A' }} by (gen_presups; mauto 4).
  assert {{ Γ M'[Id] W : A }} by (gen_presups; mauto 4).
  assert {{ Γ M' : A }} by mauto 4.
  transitivity {{{M[Id]}}}; [mauto 3 |].
  transitivity W; [mauto 3 |].
  mauto 4.
Qed.

#[export]
 Hint Resolve glu_univ_elem_exp_unique_upto_exp_eq : mctt.

Lemma glu_univ_elem_exp_unique_upto_exp_eq' : forall {i a P El Γ M M' m A},
    {{ DG a glu_univ_elem i P El }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ M' : A ® m El }} ->
    {{ Γ M M' : A }}.
Proof. mautosolve 4. Qed.

#[export]
Hint Resolve glu_univ_elem_exp_unique_upto_exp_eq' : mctt.

Lemma glu_univ_elem_per_univ_typ_iff : forall {i a a' P P' El El'},
    {{ Dom a a' per_univ i }} ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a' glu_univ_elem i P' El' }} ->
    (P <∙> P') /\ (El <∙> El').
Proof.
  intros * Hper **.
  eapply functional_glu_univ_elem;
    [eassumption | rewrite Hper];
    eassumption.
Qed.

Corollary glu_univ_elem_cumu_ge : forall {i j a P El},
    i <= j ->
    {{ DG a glu_univ_elem i P El }} ->
    exists P' El', {{ DG a glu_univ_elem j P' El' }}.
Proof.
  intros.
  assert {{ Dom a a per_univ i }} as [R] by mauto.
  assert {{ DF a a per_univ_elem j R }} by mauto.
  mauto.
Qed.

#[export]
Hint Resolve glu_univ_elem_cumu_ge : mctt.

Corollary glu_univ_elem_cumu_max_left : forall {i j a P El},
    {{ DG a glu_univ_elem i P El }} ->
    exists P' El', {{ DG a glu_univ_elem (max i j) P' El' }}.
Proof.
  intros.
  assert (i <= max i j) by lia.
  mauto.
Qed.

Corollary glu_univ_elem_cumu_max_right : forall {i j a P El},
    {{ DG a glu_univ_elem j P El }} ->
    exists P' El', {{ DG a glu_univ_elem (max i j) P' El' }}.
Proof.
  intros.
  assert (j <= max i j) by lia.
  mauto.
Qed.

Section glu_univ_elem_cumulativity.
  #[local]
  Lemma glu_univ_elem_cumulativity_ge : forall {i j a P P' El El'},
      i <= j ->
      {{ DG a glu_univ_elem i P El }} ->
      {{ DG a glu_univ_elem j P' El' }} ->
      (forall Γ A, {{ Γ A ® P }} -> {{ Γ A ® P' }}) /\
        (forall Γ A M m, {{ Γ M : A ® m El }} -> {{ Γ M : A ® m El' }}) /\
        (forall Γ A M m, {{ Γ A ® P }} -> {{ Γ M : A ® m El' }} -> {{ Γ M : A ® m El }}).
  Proof with mautosolve 4.
    simpl.
    intros * Hge Hglu Hglu'. gen El' P' j.
    induction Hglu using glu_univ_elem_ind; repeat split; intros;
      try assert {{ DF a a per_univ_elem j in_rel }} by mauto 2;
      invert_glu_univ_elem Hglu';
      handle_functional_glu_univ_elem;
      simpl in *;
      try solve [repeat split; intros; destruct_conjs; mauto 2 | intuition mauto 3].

    - rename IP0 into IP'.
      rename IEl0 into IEl'.
      rename OP0 into OP'.
      rename OEl0 into OEl'.
      destruct_by_head pi_glu_typ_pred.
      econstructor; intros; mauto 4.
      + assert {{ Δ IT[σ] ® IP }} by mauto.
        enough (forall Γ A, {{ Γ A ® IP }} -> {{ Γ A ® IP' }}) by mauto 4.
        eapply proj1...
      + match_by_head per_univ_elem ltac:(fun H => directed invert_per_univ_elem H).
        apply_relation_equivalence.
        destruct_rel_mod_eval.
        handle_per_univ_elem_irrel.
        assert (forall Γ A, {{ Γ A ® OP m equiv_m }} -> {{ Γ A ® OP' m equiv_m }}) by (eapply proj1; mauto).
        enough {{ Δ OT[σ,,M] ® OP m equiv_m }} by mauto.
        enough {{ Δ M : IT[σ] ® m IEl }} by mauto.
        eapply IHHglu...
    - rename IP0 into IP'.
      rename IEl0 into IEl'.
      rename OP0 into OP'.
      rename OEl0 into OEl'.
      destruct_by_head pi_glu_exp_pred.
      handle_per_univ_elem_irrel.
      econstructor; intros; mauto 4.
      + enough (forall Γ A, {{ Γ A ® IP }} -> {{ Γ A ® IP' }}) by mauto 4.
        eapply proj1...
      + match_by_head per_univ_elem ltac:(fun H => directed invert_per_univ_elem H).
        handle_per_univ_elem_irrel.
        destruct_rel_mod_eval.
        destruct_rel_mod_app.
        handle_per_univ_elem_irrel.
        eexists; split; mauto 4.
        assert (forall Γ A M m, {{ Γ M : A ® m OEl n equiv_n }} -> {{ Γ M : A ® m OEl' n equiv_n }}) by (eapply proj1, proj2; mauto 4).
        enough {{ Δ M[σ] N : OT[σ,,N] ® fa OEl n equiv_n }} by mauto 4.
        assert {{ Δ N : IT[σ] ® n IEl }} by (eapply IHHglu; mauto 3).
        assert (exists mn, {{ $| m & n |↘ mn }} /\ {{ Δ M[σ] N : OT[σ,,N] ® mn OEl n equiv_n }}) by mauto 4.
        destruct_conjs.
        functional_eval_rewrite_clear...
    - rename IP0 into IP'.
      rename IEl0 into IEl'.
      rename OP0 into OP'.
      rename OEl0 into OEl'.
      destruct_by_head pi_glu_typ_pred.
      destruct_by_head pi_glu_exp_pred.
      handle_per_univ_elem_irrel.
      econstructor; intros; mauto.
      match_by_head per_univ_elem ltac:(fun H => directed invert_per_univ_elem H).
      handle_per_univ_elem_irrel.
      destruct_rel_mod_eval.
      destruct_rel_mod_app.
      handle_per_univ_elem_irrel.
      match goal with
      | _: {{ B ρ n ^?a }} |- _ =>
          rename a into b
      end.
      eexists; split; mauto 4.
      assert (forall Γ A M m, {{ Γ A ® OP n equiv_n }} -> {{ Γ M : A ® m OEl' n equiv_n }} -> {{ Γ M : A ® m OEl n equiv_n }}) by (eapply proj2, proj2; eauto 3).
      assert {{ Δ OT[σ,,N] ® OP n equiv_n }} by mauto 4.
      enough {{ Δ M[σ] N : OT[σ,,N] ® fa OEl' n equiv_n }} by mauto 4.
      assert {{ Δ N : IT[σ] ® n IEl' }} by (eapply IHHglu; mauto 3).
      assert {{ Δ IT[σ] ® IP' }} by (eapply glu_univ_elem_trm_typ; mauto 2).
      assert {{ Δ IT0[σ] ® IP' }} by mauto 2.
      assert {{ Δ IT[σ] IT0[σ] : Type@j }} as HITeq by mauto 2.
      assert {{ Δ N : IT0[σ] ® n IEl' }} by (rewrite <- HITeq; mauto 3).
      assert (exists mn, {{ $| m & n |↘ mn }} /\ {{ Δ M[σ] N : OT0[σ,,N] ® mn OEl' n equiv_n }}) by mauto 3.
      destruct_conjs.
      functional_eval_rewrite_clear.
      assert {{ DG b glu_univ_elem j OP' n equiv_n OEl' n equiv_n }} by mauto 3.
      assert {{ Δ OT0[σ,,N] ® OP' n equiv_n }} by (eapply glu_univ_elem_trm_typ; mauto 3).
      enough {{ Δ OT[σ,,N] OT0[σ,,N] : Type@j }} as ->...

    - invert_glu_rel1.
      econstructor; intros; firstorder mauto 3.
    - invert_glu_rel1.
      handle_per_univ_elem_irrel.

      econstructor; intros; firstorder mauto 3.
      destruct_glu_eq; econstructor; firstorder mauto 3.
    - repeat invert_glu_rel1.
      handle_per_univ_elem_irrel.

      econstructor; intros; firstorder mauto 3.
      assert {{ Γ w Id : Γ }} by mauto 4.
      assert (P Γ {{{ B[Id] }}}) as HB by mauto 3.
      bulky_rewrite_in HB.
      assert (P0 Γ B) as HB' by firstorder.
      assert (P0 Γ {{{ B0[Id] }}}) as HB0 by mauto 3.
      bulky_rewrite_in HB0.
      assert {{ Γ B0 B : Type@j }} by mauto 3.
      assert (El Γ {{{ B[Id] }}} {{{ M0[Id] }}} m) as HM0 by mauto 3.
      bulky_rewrite_in HM0.
      assert (El0 Γ B M0 m) as HM0' by firstorder.
      assert (El Γ {{{ B[Id] }}} {{{ N[Id] }}} n) as HN by mauto 3.
      bulky_rewrite_in HN.
      assert (El0 Γ B N n) as HN' by firstorder.
      assert (El0 Γ {{{ B0[Id] }}} {{{ M[Id] }}} m) as HM by mauto 3.
      bulky_rewrite_in HM.
      assert (El0 Γ {{{ B0[Id] }}} {{{ N0[Id] }}} n) as HN0 by mauto 3.
      bulky_rewrite_in HN0.
      assert {{ Γ M0 M : B }} by mauto 3.
      assert {{ Γ N N0 : B }} by mauto 3.

      destruct_glu_eq; econstructor; apply_equiv_left; mauto 3.
      + bulky_rewrite.
        eapply wf_exp_eq_conv'; [econstructor |]; mauto 3.
        transitivity M; mauto 3.
        bulky_rewrite.
      + transitivity M; mauto 3.
        transitivity M''; mauto 3.
        transitivity N0; mauto 3.
      + intros.
        assert (P Δ {{{ B[σ] }}}) by mauto 3.
        assert {{ Δ B0[σ] B[σ] : Type@j }} by mauto 3.
        assert {{ Γ M'' M0 : B }} by (transitivity M; mauto 3).
        assert {{ Δ M''[σ] M0[σ] : B[σ] }} by mauto 4.
        assert (El0 Δ {{{ B0[σ] }}} {{{M''[σ]}}} m') as HEl0 by mauto 3.
        bulky_rewrite_in HEl0.
        firstorder.

    - destruct_by_head neut_glu_exp_pred.
      econstructor; mauto.
      destruct_by_head neut_glu_typ_pred.
      econstructor...
    - destruct_by_head neut_glu_exp_pred.
      econstructor...
  Qed.

End glu_univ_elem_cumulativity.

Corollary glu_univ_elem_typ_cumu_ge : forall {i j a P P' El El' Γ A},
    i <= j ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem j P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A ® P' }}.
Proof.
  intros.
  eapply glu_univ_elem_cumulativity_ge; mauto 4.
Qed.

Corollary glu_univ_elem_typ_cumu_max_left : forall {i j a P P' El El' Γ A},
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem (max i j) P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A ® P' }}.
Proof.
  intros.
  assert (i <= max i j) by lia.
  eapply glu_univ_elem_typ_cumu_ge; mauto 4.
Qed.

Corollary glu_univ_elem_typ_cumu_max_right : forall {i j a P P' El El' Γ A},
    {{ DG a glu_univ_elem j P El }} ->
    {{ DG a glu_univ_elem (max i j) P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A ® P' }}.
Proof.
  intros.
  assert (j <= max i j) by lia.
  eapply glu_univ_elem_typ_cumu_ge; mauto 4.
Qed.

Corollary glu_univ_elem_exp_cumu_ge : forall {i j a P P' El El' Γ A M m},
    i <= j ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem j P' El' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ M : A ® m El' }}.
Proof.
  intros. gen m M A Γ.
  eapply glu_univ_elem_cumulativity_ge; mauto 4.
Qed.

Corollary glu_univ_elem_exp_cumu_max_left : forall {i j a P P' El El' Γ A M m},
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem (max i j) P' El' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ M : A ® m El' }}.
Proof.
  intros.
  assert (i <= max i j) by lia.
  eapply glu_univ_elem_exp_cumu_ge; mauto 4.
Qed.

Corollary glu_univ_elem_exp_cumu_max_right : forall {i j a P P' El El' Γ A M m},
    {{ DG a glu_univ_elem j P El }} ->
    {{ DG a glu_univ_elem (max i j) P' El' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ M : A ® m El' }}.
Proof.
  intros.
  assert (j <= max i j) by lia.
  eapply glu_univ_elem_exp_cumu_ge; mauto 4.
Qed.

Corollary glu_univ_elem_exp_lower : forall {i j a P P' El El' Γ A M m},
    i <= j ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem j P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ M : A ® m El' }} ->
    {{ Γ M : A ® m El }}.
Proof.
  intros * ? ? ?. gen m M A Γ.
  eapply glu_univ_elem_cumulativity_ge; mauto 4.
Qed.

Corollary glu_univ_elem_exp_lower_max_left : forall {i j a P P' El El' Γ A M m},
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem (max i j) P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ M : A ® m El' }} ->
    {{ Γ M : A ® m El }}.
Proof.
  intros.
  assert (i <= max i j) by lia.
  eapply glu_univ_elem_exp_lower; mauto 4.
Qed.

Corollary glu_univ_elem_exp_lower_max_right : forall {i j a P P' El El' Γ A M m},
    {{ DG a glu_univ_elem j P El }} ->
    {{ DG a glu_univ_elem (max i j) P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ M : A ® m El' }} ->
    {{ Γ M : A ® m El }}.
Proof.
  intros.
  assert (j <= max i j) by lia.
  eapply glu_univ_elem_exp_lower; mauto 4.
Qed.

Lemma glu_univ_elem_exp_conv : forall {i j k a a' P P' El El' Γ A M m},
    {{ Dom a a' per_univ k }} ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a' glu_univ_elem j P' El' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ A ® P' }} ->
    {{ Γ M : A ® m El' }}.
Proof.
  intros * [] ? ? ? ?.
  assert {{ Dom a a' per_univ (max i k) }} as Haa' by (eexists; mauto using per_univ_elem_cumu_max_right).
  assert (exists P El, {{ DG a glu_univ_elem (max i k) P El }}) as [Pik [Elik]] by mauto using glu_univ_elem_cumu_max_left.
  assert {{ DG a' glu_univ_elem (max i k) Pik Elik }} by (setoid_rewrite <- Haa'; eassumption).
  assert {{ Γ M : A ® m Elik }} by (eapply glu_univ_elem_exp_cumu_max_left; [| | eassumption]; eassumption).
  assert (exists P El, {{ DG a' glu_univ_elem (max j (max i k)) P El }}) as [Ptop [Eltop]] by mauto using glu_univ_elem_cumu_max_right.
  assert {{ Γ M : A ® m Eltop }} by (eapply glu_univ_elem_exp_cumu_max_right; [| | eassumption]; eassumption).
  eapply glu_univ_elem_exp_lower_max_left; mauto.
Qed.

Lemma glu_univ_elem_exp_conv' : forall {i j a P P' El El' Γ A M m},
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a glu_univ_elem j P' El' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ A ® P' }} ->
    {{ Γ M : A ® m El' }}.
Proof.
  intros.
  eapply glu_univ_elem_exp_conv; only 2: exact H; eauto.
  mauto 2.
Qed.

Lemma glu_univ_elem_per_subtyp_typ_escape : forall {i a a' P P' El El' Γ A A'},
    {{ Sub a <: a' at i }} ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a' glu_univ_elem i P' El' }} ->
    {{ Γ A ® P }} ->
    {{ Γ A' ® P' }} ->
    {{ Γ A A' }}.
Proof.
  intros * Hsubtyp Hglu Hglu' HA HA'.
  gen A' A Γ. gen El' El P' P.
  induction Hsubtyp using per_subtyp_ind; intros; subst;
    saturate_refl_for per_univ_elem;
    try solve [simpl in *; bulky_rewrite; mauto 3];
    invert_glu_univ_elem Hglu;
    handle_functional_glu_univ_elem;
    handle_per_univ_elem_irrel;
    destruct_by_head pi_glu_typ_pred;
    saturate_glu_by_per;
    invert_glu_univ_elem Hglu';
    handle_functional_glu_univ_elem.
  - bulky_rewrite.
    mauto 3.
  - destruct_by_head pi_glu_typ_pred.
    rename A0 into A'. rename IT0 into IT'. rename OT0 into OT'.
    rename OP0 into OP'. rename OEl0 into OEl'.
    assert {{ Γ IT ® IP }}.
    {
      assert {{ Γ IT[Id] ® IP }} by mauto 4.
      simpl in *.
      autorewrite with mctt in *; mauto 3.
    }
    assert {{ Γ IT' ® IP }}.
    {
      assert {{ Γ IT'[Id] ® IP }} by mauto 4.
      simpl in *.
      autorewrite with mctt in *; mauto 3.
    }
    do 2 bulky_rewrite1.
    assert {{ Γ IT IT' : Type@i }} by mauto 4.
    enough {{ Γ, IT' OT OT' }} by mauto 3.
    assert {{ Dom ⇑! a (length Γ) ⇑! a' (length Γ) in_rel }} as equiv_len_len' by (eapply per_bot_then_per_elem; mauto 4).
    assert {{ Dom ⇑! a (length Γ) ⇑! a (length Γ) in_rel }} as equiv_len_len by (eapply per_bot_then_per_elem; mauto 4).
    assert {{ Dom ⇑! a' (length Γ) ⇑! a' (length Γ) in_rel }} as equiv_len'_len' by (eapply per_bot_then_per_elem; mauto 4).
    handle_per_univ_elem_irrel.
    match_by_head per_univ_elem ltac:(fun H => directed invert_per_univ_elem H).
    apply_relation_equivalence.
    destruct_rel_mod_eval.
    handle_per_univ_elem_irrel.
    match goal with
    | _: {{ B ρ ⇑! a (length Γ) ^?a0 }},
        _: {{ B' ρ' ⇑! a' (length Γ) ^?a0' }} |- _ =>
        rename a0 into b;
        rename a0' into b'
    end.
    assert {{ DG b glu_univ_elem i OP d{{{ ⇑! a (length Γ) }}} equiv_len_len OEl d{{{ ⇑! a (length Γ) }}} equiv_len_len }} by mauto 4.
    assert {{ DG b' glu_univ_elem i OP' d{{{ ⇑! a' (length Γ) }}} equiv_len'_len' OEl' d{{{ ⇑! a' (length Γ) }}} equiv_len'_len' }} by mauto 4.
    assert {{ Γ, IT' OT ® OP d{{{ ⇑! a (length Γ) }}} equiv_len_len }}.
    {
      assert {{ Γ, IT #0 : IT[Wk] ® ⇑! a (length Γ) IEl }} by (eapply var0_glu_elem; mauto 3).
      assert {{ Γ, IT' Γ, IT }} as -> by mauto.
      assert {{ Γ, IT OT[Wk,,#0] OT : Type@i }} as <- by (bulky_rewrite1; mauto 2).
      mauto 4.
    }
    assert {{ Γ, IT' OT' ® OP' d{{{ ⇑! a' (length Γ) }}} equiv_len'_len' }}.
    {
      assert {{ Γ, IT' #0 : IT'[Wk] ® ⇑! a' (length Γ) IEl }} by (eapply var0_glu_elem; mauto 3).
      assert {{ Γ, IT' OT'[Wk,,#0] ® OP' d{{{ ⇑! a' (length Γ) }}} equiv_len'_len' }} by mauto 4.
      assert {{ Γ, IT' OT'[Wk,,#0] OT' : Type@i }} as <- by (bulky_rewrite1; mauto 2).
      mauto 4.
    }
    mauto 3.
  - match_by_head (per_bot b b') ltac:(fun H => specialize (H (length Γ)) as [V []]).
    simpl in *.
    destruct_conjs.
    assert {{ Γ A[Id] A : Type@i }} as <- by mauto 4.
    assert {{ Γ A'[Id] A' : Type@i }} as <- by mauto 3.
    assert {{ Γ A'[Id] V : Type@i }} as -> by mauto 4.
    eapply wf_subtyp_refl'.
    mauto 4.
Qed.

#[export]
Hint Resolve glu_univ_elem_per_subtyp_typ_escape : mctt.

Lemma glu_univ_elem_per_subtyp_trm_if : forall {i a a' P P' El El' Γ A A' M m},
    {{ Sub a <: a' at i }} ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ DG a' glu_univ_elem i P' El' }} ->
    {{ Γ A' ® P' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ M : A' ® m El' }}.
Proof.
  intros * Hsubtyp Hglu Hglu' HA HA'.
  assert {{ Γ A A' }} by (eapply glu_univ_elem_per_subtyp_typ_escape; only 4: eapply glu_univ_elem_trm_typ; mauto).
  gen m M A' A. gen Γ. gen El' El P' P.
  induction Hsubtyp using per_subtyp_ind; intros; subst;
    saturate_refl_for per_univ_elem.
  1-3,5:
    invert_glu_univ_elem Hglu;
  handle_functional_glu_univ_elem;
  handle_per_univ_elem_irrel;
  repeat invert_glu_rel1;
  saturate_glu_by_per;
  invert_glu_univ_elem Hglu';
  handle_functional_glu_univ_elem;
  repeat invert_glu_rel1;
  try solve [simpl in *; intuition mauto 3].
  - simpl in *.
    destruct_conjs.
    intuition mauto 3.
    assert (exists P El, {{ DG m glu_univ_elem j P El }}) as [? [?]] by mauto 3.
    do 2 eexists; split; mauto 3.
    eapply glu_univ_elem_typ_cumu_ge; revgoals; mautosolve 3.
  - rename A0 into A'.
    rename IT0 into IT'. rename OT0 into OT'.
    rename OP0 into OP'. rename OEl0 into OEl'.
    handle_per_univ_elem_irrel.
    econstructor; mauto 3.
    + enough {{ Sub Π a ρ B <: Π a' ρ' B' at i }} by (eapply per_elem_subtyping; try eassumption).
      econstructor; mauto 3.
    + intros.
      assert {{ Γ w Id : Γ }} by mauto 3.
      assert {{ Γ IT ® IP }}.
      {
        assert {{ Γ IT[Id] ® IP }} by mauto 3.
        simpl in *.
        autorewrite with mctt in *; mauto 3.
      }
      assert {{ Γ IT' ® IP }}.
      {
        assert {{ Γ IT'[Id] ® IP }} by mauto 3.
        simpl in *.
        autorewrite with mctt in *; mauto 3.
      }
      assert {{ Δ IT'[σ] IT[σ] : Type@i }} by (symmetry; mauto 4 using glu_univ_elem_per_univ_typ_escape).
      assert {{ Δ N : IT'[σ] ® n IEl }} by (simpl; bulky_rewrite1; eassumption).
      assert (exists mn : domain, {{ $| m & n |↘ mn }} /\ {{Δ M[σ] N : OT'[σ,,N] ® mn OEl n equiv_n }}) by mauto 3.
      destruct_conjs.
      eexists; split; mauto 3.
      match_by_head per_univ_elem ltac:(fun H => directed invert_per_univ_elem H).
      destruct_rel_mod_eval.
      handle_per_univ_elem_irrel.
      match goal with
      | _: {{ B ρ n ^?a }},
          _: {{ B' ρ' n ^?a' }} |- _ =>
          rename a into b;
          rename a' into b'
      end.
      assert {{ DG b glu_univ_elem i OP n equiv_n OEl n equiv_n }} by mauto 3.
      assert {{ DG b' glu_univ_elem i OP' n equiv_n OEl' n equiv_n }} by mauto 3.
      assert {{ Δ OT'[σ,,N] ® OP n equiv_n }} by (eapply glu_univ_elem_trm_typ; mauto 3).
      assert {{ Sub b <: b' at i }} by mauto 3.
      assert {{ Δ OT'[σ,,N] OT[σ,,N] }} by mauto 3.
      intuition.
  - match_by_head1 (per_bot b b') ltac:(fun H => destruct (H (length Γ)) as [V []]).
    econstructor; mauto 3.
    + econstructor; mauto 3.
    + intros.
      match_by_head1 (per_bot b b') ltac:(fun H => destruct (H (length Δ)) as [? []]).
      functional_read_rewrite_clear.
      assert {{ Δ A[σ] A'[σ] }} by mauto 3.
      assert {{ Δ M[σ] M' : A[σ] }} by mauto 3.
      mauto 3.
  - assert {{ Γ A ® P }} by (eapply glu_univ_elem_trm_typ; eauto).
    assert {{ Γ A A' : Type@i }} by mauto 4.
    rewrite H in *.
    rewrite H4 in *.
    handle_functional_glu_univ_elem.
    trivial.
Qed.

Lemma glu_univ_elem_per_subtyp_trm_conv : forall {i j k a a' P P' El El' Γ A A' M m},
    {{ Sub a <: a' at i }} ->
    {{ DG a glu_univ_elem j P El }} ->
    {{ DG a' glu_univ_elem k P' El' }} ->
    {{ Γ A' ® P' }} ->
    {{ Γ M : A ® m El }} ->
    {{ Γ M : A' ® m El' }}.
Proof.
  intros.
  assert {{ Sub a <: a' at (max i (max j k)) }} by mauto using per_subtyp_cumu_left.
  assert (j <= max i (max j k)) by lia.
  assert (exists P El, {{ DG a glu_univ_elem (max i (max j k)) P El }}) as [Ptop [Eltop]] by mauto using glu_univ_elem_cumu_ge.
  assert (k <= max i (max j k)) by lia.
  assert (exists P El, {{ DG a' glu_univ_elem (max i (max j k)) P El }}) as [Ptop' [Eltop']] by mauto using glu_univ_elem_cumu_ge.
  assert {{ Γ A' ® Ptop' }} by (eapply glu_univ_elem_typ_cumu_ge; mauto).
  assert {{ Γ M : A ® m Eltop }} by (eapply @glu_univ_elem_exp_cumu_ge with (i := j); mauto).
  assert {{ Γ M : A' ® m Eltop' }} by (eapply glu_univ_elem_per_subtyp_trm_if; mauto).
  eapply glu_univ_elem_exp_lower; mauto.
Qed.


Lemma mk_glu_rel_typ_with_sub' : forall {i Δ A σ ρ a},
    {{ A ρ a }} ->
    (exists P El, {{ DG a glu_univ_elem i P El }}) ->
    (forall P El, {{ DG a glu_univ_elem i P El }} -> {{ Δ A[σ] ® P }}) ->
    glu_rel_typ_with_sub i Δ A σ ρ.
Proof.
  intros * ? [? []] HEl.
  econstructor; mauto.
  eapply HEl; mauto.
Qed.

#[export]
Hint Resolve mk_glu_rel_typ_with_sub' : mctt.

Lemma mk_glu_rel_typ_with_sub'' : forall {i Δ A σ ρ a},
    {{ A ρ a }} ->
    {{ Dom a a per_univ i }} ->
    (forall P El, {{ DG a glu_univ_elem i P El }} -> {{ Δ A[σ] ® P }}) ->
    glu_rel_typ_with_sub i Δ A σ ρ.
Proof.
  intros * ? [] ?.
  assert (exists P El, {{ DG a glu_univ_elem i P El }}) as [? []] by mauto.
  eapply mk_glu_rel_typ_with_sub'; mauto.
Qed.

#[export]
Hint Resolve mk_glu_rel_typ_with_sub'' : mctt.

Lemma mk_glu_rel_exp_with_sub' : forall {i Δ A M σ ρ a m},
    {{ A ρ a }} ->
    {{ M ρ m }} ->
    (exists P El, {{ DG a glu_univ_elem i P El }}) ->
    (forall P El, {{ DG a glu_univ_elem i P El }} -> {{ Δ M[σ] : A[σ] ® m El }}) ->
    glu_rel_exp_with_sub i Δ M A σ ρ.
Proof.
  intros * ? ? [? []] HEl.
  econstructor; mauto.
  eapply HEl; mauto.
Qed.

#[export]
Hint Resolve mk_glu_rel_exp_with_sub' : mctt.

Lemma mk_glu_rel_exp_with_sub'' : forall {i Δ A M σ ρ a m},
    {{ A ρ a }} ->
    {{ M ρ m }} ->
    {{ Dom a a per_univ i }} ->
    (forall P El, {{ DG a glu_univ_elem i P El }} -> {{ Δ M[σ] : A[σ] ® m El }}) ->
    glu_rel_exp_with_sub i Δ M A σ ρ.
Proof.
  intros * ? ? [] ?.
  assert (exists P El, {{ DG a glu_univ_elem i P El }}) as [? []] by mauto.
  eapply mk_glu_rel_exp_with_sub'; mauto.
Qed.

#[export]
Hint Resolve mk_glu_rel_exp_with_sub'' : mctt.

Lemma glu_rel_exp_with_sub_implies_glu_rel_exp_sub_with_typ : forall {i Δ A M σ Γ ρ},
  {{ Δ s σ : Γ }} ->
  glu_rel_exp_with_sub i Δ M A σ ρ ->
  glu_rel_exp_with_sub (S i) Δ A {{{ Type@i }}} σ ρ.
Proof.
  intros * ? [].
  econstructor; mauto.
  assert {{ Δ A[σ] : Type@i }} by mauto using glu_univ_elem_trm_univ_lvl.
  repeat split; try do 2 eexists; mauto 3.
  split; mauto 4.
  eapply glu_univ_elem_trm_typ; mauto 3.
Qed.

#[export]
Hint Resolve glu_rel_exp_with_sub_implies_glu_rel_exp_sub_with_typ : mctt.

Lemma glu_rel_exp_with_sub_implies_glu_rel_typ_with_sub : forall {i Δ A j σ ρ},
  glu_rel_exp_with_sub i Δ A {{{ Type@j }}} σ ρ ->
  glu_rel_typ_with_sub j Δ A σ ρ.
Proof.
  intros * [].
  simplify_evals.
  match_by_head1 glu_univ_elem invert_glu_univ_elem.
  apply_predicate_equivalence.
  unfold univ_glu_exp_pred' in *.
  destruct_conjs.
  econstructor; mauto 3.
Qed.

#[export]
Hint Resolve glu_rel_exp_with_sub_implies_glu_rel_typ_with_sub : mctt.

Lemma glu_rel_typ_with_sub_implies_glu_rel_exp_with_sub : forall {Δ A j σ Γ ρ},
  {{ Δ s σ : Γ }} ->
  glu_rel_typ_with_sub j Δ A σ ρ ->
  glu_rel_exp_with_sub (S j) Δ A {{{ Type@j }}} σ ρ.
Proof.
  intros * ? [].
  simplify_evals.
  econstructor; mauto.
  assert {{ Δ A[σ] : Type@j }} by mauto 4 using glu_univ_elem_univ_lvl.
  repeat split; try do 2 eexists; mauto 3.
Qed.

#[export]
Hint Resolve glu_rel_typ_with_sub_implies_glu_rel_exp_with_sub : mctt.

Lemmas for glu_ctx_env


Lemma glu_ctx_env_sub_resp_ctx_eq : forall {Γ Sb},
    {{ EG Γ glu_ctx_env Sb }} ->
    forall {Δ Δ' σ ρ},
      {{ Δ s σ ® ρ Sb }} ->
      {{ Δ Δ' }} ->
      {{ Δ' s σ ® ρ Sb }}.
Proof.
  induction 1; intros * HSb Hctxeq;
    apply_predicate_equivalence;
    simpl in *;
    mauto 4.

  destruct_by_head cons_glu_sub_pred.
  econstructor; mauto 4.
  rewrite <- Hctxeq; eassumption.
Qed.

Add Parametric Morphism Sb Γ (H : glu_ctx_env Sb Γ) : Sb
    with signature wf_ctx_eq ==> eq ==> eq ==> iff as glu_ctx_env_sub_morphism_iff1.
Proof.
  intros.
  split; intros; eapply glu_ctx_env_sub_resp_ctx_eq; mauto.
Qed.

Lemma glu_ctx_env_sub_resp_sub_eq : forall {Γ Sb},
    {{ EG Γ glu_ctx_env Sb }} ->
    forall {Δ σ σ' ρ},
      {{ Δ s σ ® ρ Sb }} ->
      {{ Δ s σ σ' : Γ }} ->
      {{ Δ s σ' ® ρ Sb }}.
Proof.
  induction 1; intros * HSb Hsubeq;
    apply_predicate_equivalence;
    simpl in *;
    gen_presup Hsubeq;
    try eassumption.

  destruct_by_head cons_glu_sub_pred.
  econstructor; mauto 4.
  assert {{ Γ, A s Wk : Γ }} by mauto 3.
  assert {{ Δ A[Wk][σ] A[Wk][σ'] : Type@i }} as <- by mauto 3.
  assert {{ Δ #0[σ] #0[σ'] : A[Wk][σ] }} as <- by mauto 3.
  eassumption.
Qed.

Add Parametric Morphism Sb Γ (H : glu_ctx_env Sb Γ) Δ : (Sb Δ)
    with signature wf_sub_eq Δ Γ ==> eq ==> iff as glu_ctx_env_sub_morphism_iff2.
Proof.
  split; intros; eapply glu_ctx_env_sub_resp_sub_eq; mauto.
Qed.

Lemma cons_glu_sub_pred_resp_wf_sub_eq : forall {i Γ A Sb Δ σ σ' ρ},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ Γ A : Type@i }} ->
    {{ Δ s σ σ' : Γ, A }} ->
    {{ Δ s σ ® ρ cons_glu_sub_pred i Γ A Sb }} ->
    {{ Δ s σ' ® ρ cons_glu_sub_pred i Γ A Sb }}.
Proof.
  intros * Hglu HA Heq Hσ.
  dependent destruction Hσ.
  gen_presup Heq.
  assert {{ Δ s Wkσ : Γ }} by mauto 3.
  assert {{ Δ s Wkσ' : Γ }} by mauto 3.
  assert {{ Δ s Wkσ Wkσ' : Γ }} by mauto 3.
  econstructor; mauto 3.
  - assert {{ Δ A[Wk][σ] A[Wk][σ'] : Type@i }} as <- by mauto 4.
    assert {{ Δ #0[σ] #0[σ'] : A[Wk][σ] }} as <-; mauto 3.
  - assert {{ Δ s Wkσ Wkσ' : Γ }} as <-; eassumption.
Qed.

Add Parametric Morphism i Γ A Sb Δ (Hglu : {{ EG Γ glu_ctx_env Sb }}) (HA : {{ Γ A : Type@i }}) : (cons_glu_sub_pred i Γ A Sb Δ)
    with signature wf_sub_eq Δ {{{ Γ, A }}} ==> eq ==> iff as cons_glu_sub_pred_morphism_iff.
Proof.
  split; mauto using cons_glu_sub_pred_resp_wf_sub_eq.
Qed.

Lemma glu_ctx_env_per_env : forall {Γ Sb env_rel Δ σ ρ},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ EF Γ Γ per_ctx_env env_rel }} ->
    {{ Δ s σ ® ρ Sb }} ->
    {{ Dom ρ ρ env_rel }}.
Proof.
  intros * Hglu Hper.
  gen ρ σ Δ env_rel.
  induction Hglu; intros;
    invert_per_ctx_env Hper;
    apply_predicate_equivalence;
    handle_per_ctx_env_irrel;
    mauto 3.

  rename i0 into j.
  inversion_clear_by_head cons_glu_sub_pred.
  assert {{ Dom ρ ρ tail_rel }} by intuition.
  destruct_rel_typ.
  handle_per_univ_elem_irrel.
  assert (exists P' El', {{ DG a glu_univ_elem (max i j) P' El' }}) as [? []] by mauto 3 using glu_univ_elem_cumu_max_left.
  eexists; eapply glu_univ_elem_per_elem; mauto using per_univ_elem_cumu_max_right.
  eapply glu_univ_elem_exp_cumu_max_left; [| | eassumption]; eassumption.
Qed.

Lemma glu_ctx_env_wf_ctx : forall {Γ Sb},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ Γ }}.
Proof.
  induction 1; intros; mauto.
Qed.

Lemma glu_ctx_env_sub_escape : forall {Γ Sb},
    {{ EG Γ glu_ctx_env Sb }} ->
    forall Δ σ ρ,
      {{ Δ s σ ® ρ Sb }} ->
      {{ Δ s σ : Γ }}.
Proof.
  induction 1; intros;
    handle_functional_glu_univ_elem;
    destruct_by_head cons_glu_sub_pred;
    eassumption.
Qed.

#[export]
Hint Resolve glu_ctx_env_wf_ctx glu_ctx_env_sub_escape : mctt.

Lemma glu_ctx_env_per_ctx_env : forall {Γ Sb},
    {{ EG Γ glu_ctx_env Sb }} ->
    exists env_rel, {{ EF Γ Γ per_ctx_env env_rel }}.
Proof.
  intros.
  enough {{ Γ }} by eassumption.
  mauto using completeness_fundamental_ctx.
Qed.

#[export]
Hint Resolve glu_ctx_env_per_ctx_env : mctt.

Lemma glu_ctx_env_resp_per_ctx_helper : forall {Γ Γ' Sb Sb'},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ EG Γ' glu_ctx_env Sb' }} ->
    {{ Γ Γ' }} ->
    (Sb -∙> Sb').
Proof.
  intros * Hglu Hglu' HΓΓ'.
  gen Sb' Γ'.
  induction Hglu; intros;
    destruct (completeness_fundamental_ctx_eq _ _ HΓΓ') as [env_relΓ Hper];
    apply_predicate_equivalence;
    handle_per_univ_elem_irrel;
    dependent destruction Hglu';
    apply_predicate_equivalence;
    invert_per_ctx_env Hper;
    handle_per_ctx_env_irrel;
    try firstorder.

  rename i0 into j.
  rename Γ0 into Γ'.
  rename A0 into A'.
  rename TSb0 into TSb'.
  rename i1 into k.
  inversion HΓΓ' as [|? ? l]; subst.
  assert (TSb -∙> TSb') by intuition.
  intros Δ σ ρ [].
  saturate_refl_for per_ctx_env.
  assert {{ Dom ρ0 ρ0 tail_rel }}
    by (eapply glu_ctx_env_per_env; [| | eassumption]; eassumption).
  assert {{ Δ0 s Wkσ0 ® ρ0 TSb' }} by intuition.
  assert (glu_rel_typ_with_sub j Δ0 A' {{{ Wkσ0 }}} d{{{ ρ0 }}}) as [] by mauto 3.
  destruct_rel_typ.
  handle_functional_glu_univ_elem.
  rename a0 into a'.
  rename El0 into El'.
  rename P0 into P'.
  econstructor; mauto 4.
  assert (exists Pmax Elmax, {{ DG a glu_univ_elem (max i l) Pmax Elmax }}) as [Pmax [Elmax]]
by mauto using glu_univ_elem_cumu_max_left.
  assert (i <= max i l) by lia.
  assert {{ Δ0 #0[σ0] : A'[Wk][σ0] ® ^(ρ0 0) Elmax }}.
  {
    assert {{ Γ, A s Wk : Γ }} by mauto 3.
    assert {{ Δ0 A[Wk][σ0] A'[Wk][σ0] : Type@l }} by mauto 3.
    assert {{ Δ0 A[Wk][σ0] A'[Wk][σ0] : Type@(max i l) }} as <- by mauto 2 using lift_exp_eq_max_right.
    eapply glu_univ_elem_exp_cumu_ge; mauto 3.
  }
  eapply glu_univ_elem_exp_conv; [eexists | | | |]; intuition.
  autorewrite with mctt.
  eassumption.
Qed.

Corollary functional_glu_ctx_env : forall {Γ Sb Sb'},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ EG Γ glu_ctx_env Sb' }} ->
    (Sb <∙> Sb').
Proof.
  intros.
  assert {{ Γ Γ }} by mauto using glu_ctx_env_wf_ctx.
  split; eapply glu_ctx_env_resp_per_ctx_helper; eassumption.
Qed.

Ltac apply_functional_glu_ctx_env1 :=
  let tactic_error o1 o2 := fail 2 "functional_glu_ctx_env biconditional between" o1 "and" o2 "cannot be solved" in
  match goal with
  | H1 : {{ EG ^?Γ glu_ctx_env ?Sb1 }},
      H2 : {{ EG ^?Γ glu_ctx_env ?Sb2 }} |- _ =>
      assert_fails (unify Sb1 Sb2);
      match goal with
      | H : Sb1 <∙> Sb2 |- _ => fail 1
      | _ => assert (Sb1 <∙> Sb2) by (eapply functional_glu_ctx_env; [apply H1 | apply H2]) || tactic_error Sb1 Sb2
      end
  end.

Ltac apply_functional_glu_ctx_env :=
  repeat apply_functional_glu_ctx_env1.

Ltac handle_functional_glu_ctx_env :=
  functional_eval_rewrite_clear;
  fold glu_typ_pred in *;
  fold glu_exp_pred in *;
  apply_functional_glu_ctx_env;
  apply_predicate_equivalence;
  clear_dups.

Lemma glu_ctx_env_cons_clean_inversion : forall {Γ TSb A Sb},
  {{ EG Γ glu_ctx_env TSb }} ->
  {{ EG Γ, A glu_ctx_env Sb }} ->
  exists i,
    {{ Γ A : Type@i }} /\
      (forall Δ σ ρ,
          {{ Δ s σ ® ρ TSb }} ->
          glu_rel_typ_with_sub i Δ A σ ρ) /\
      (Sb <∙> cons_glu_sub_pred i Γ A TSb).
Proof.
  intros.
  simpl in *.
  match_by_head glu_ctx_env progressive_invert.
  apply_functional_glu_ctx_env.
  eexists; intuition.
  assert (Sb <∙> cons_glu_sub_pred i Γ A TSb0) as -> by eassumption.
  intros Δ σ ρ.
  split; intros [];
    econstructor; intuition.
Qed.

Ltac basic_invert_glu_ctx_env H :=
  (unshelve eapply (glu_ctx_env_cons_clean_inversion _) in H; shelve_unifiable; [eassumption |];
   destruct H as [? [? []]])
  + dependent destruction H.

Ltac invert_glu_ctx_env H := basic_invert_glu_ctx_env H.

Lemma glu_ctx_env_subtyp_sub_if : forall Γ Γ' Sb Sb' Δ σ ρ,
    {{ Γ Γ' }} ->
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ EG Γ' glu_ctx_env Sb' }} ->
    {{ Δ s σ ® ρ Sb }} ->
    {{ Δ s σ ® ρ Sb' }}.
Proof.
  intros * Hsubtyp Hglu Hglu'.
  gen ρ σ Δ. gen Sb' Sb.
  induction Hsubtyp; intros;
    invert_glu_ctx_env Hglu;
    invert_glu_ctx_env Hglu';
    handle_functional_glu_ctx_env;
    eauto.

  rename Δ into Γ'.
  rename i0 into j.
  rename i1 into k.
  rename TSb0 into TSb'.
  destruct_by_head cons_glu_sub_pred.
  assert (glu_rel_typ_with_sub k Δ A' {{{ Wkσ }}} d{{{ ρ }}}) as [] by intuition.
  rename a0 into a'.
  rename P0 into P'.
  rename El0 into El'.
  assert (exists tail_rel, {{ EF Γ Γ per_ctx_env tail_rel }}) as [tail_rel] by mauto 3 using glu_ctx_env_per_ctx_env.
  assert {{ Dom ρ ρ tail_rel }} by (eapply glu_ctx_env_per_env; revgoals; eassumption).
  assert {{ Γ, A Γ', A' }} by mauto 3.
  econstructor; mauto; intuition.
  assert {{ Γ A A' }} as [] by mauto 3 using completeness_fundamental_subtyp.
  destruct_conjs.
  handle_per_ctx_env_irrel.
  (on_all_hyp_rev: destruct_rel_by_assumption tail_rel).
  destruct_by_head rel_exp.
  handle_functional_glu_ctx_env.
  eapply glu_univ_elem_per_subtyp_trm_conv; mauto 3.
  autorewrite with mctt.
  eassumption.
Qed.

Lemma glu_ctx_env_sub_monotone : forall Γ Sb,
    {{ EG Γ glu_ctx_env Sb }} ->
    forall Δ' σ Δ τ ρ,
      {{ Δ s τ ® ρ Sb }} ->
      {{ Δ' w σ : Δ }} ->
      {{ Δ' s τ σ ® ρ Sb }}.
Proof.
  induction 1; intros * HSb Hσ;
    apply_predicate_equivalence;
    simpl in *;
    mauto 3.

  destruct_by_head cons_glu_sub_pred.
  econstructor; mauto 3.
  - assert {{ Δ' #0[σ0][σ] : A[Wk][σ0][σ] ® ^(ρ 0) El }} by (eapply glu_univ_elem_exp_monotone; mauto 3).
    assert {{ Γ, A #0 : A[Wk] }} by mauto 3.
    assert {{ Γ, A s Wk : Γ }} by mauto 3.
    assert {{ Δ' #0[σ0σ] #0[σ0][σ] : A[Wk][σ0σ] }} as -> by mauto 3.
    assert {{ Δ' s σ : Δ }} by mauto 3.
    assert {{ Δ' A[Wk][σ0][σ] A[Wk][σ0σ] : Type@i }} as <- by mauto 3.
    eassumption.
  - assert {{ Δ' s σ : Δ }} by mauto 3.
    assert {{ Γ, A s Wk : Γ }} by mauto 3.
    assert {{ Δ' s (Wk σ0) σ Wk (σ0 σ) : Γ }} as <- by mauto 3.
    mauto 3.
Qed.

Lemma cons_glu_sub_pred_helper : forall {Γ Sb Δ σ ρ A a i P El M c},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ Δ s σ ® ρ Sb }} ->
    {{ Γ A : Type@i }} ->
    {{ A ρ a }} ->
    {{ DG a glu_univ_elem i P El }} ->
    {{ Δ M : A[σ] ® c El }} ->
    {{ Δ s σ,,M ® ρ c cons_glu_sub_pred i Γ A Sb }}.
Proof.
  intros.
  assert {{ Δ s σ : Γ }} by mauto 2.
  assert {{ Δ M : A[σ] }} by mauto 2 using glu_univ_elem_trm_escape.
  assert {{ Δ s σ,,M : Γ, A }} by mauto 2.
  econstructor; mauto 3;
    autorewrite with mctt; mauto 3.

  assert {{ Δ #0[σ,,M] M : A[σ] }} as ->; mauto 2.
Qed.

#[export]
Hint Resolve cons_glu_sub_pred_helper : mctt.

Lemma initial_env_glu_rel_exp : forall {Γ ρ Sb},
    initial_env Γ ρ ->
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ Γ s Id ® ρ Sb }}.
Proof.
  intros * Hinit .
  gen ρ.
  induction ; intros * Hinit;
    dependent destruction Hinit;
    apply_predicate_equivalence;
    try solve [econstructor; mauto].

  assert (glu_rel_typ_with_sub i Γ A {{{ Id }}} ρ) as [] by mauto.
  functional_eval_rewrite_clear.
  econstructor; mauto.
  - match goal with
    | H: P Γ {{{ A[Id] }}} |- _ =>
        bulky_rewrite_in H
    end.
    eapply realize_glu_elem_bot; mauto.
    assert {{ Γ }} by mauto 3.
    assert {{ Γ, A s Wk : Γ }} by mauto 4.
    assert {{ Γ, A A[Wk] : Type@i }} by mauto 4.
    assert {{ Γ, A A[Wk] A[Wk][Id] : Type@i }} as <- by mauto 3.
    assert {{ Γ, A #0 #0[Id] : A[Wk] }} as <- by mauto.
    eapply var_glu_elem_bot; mauto.
  - assert {{ Γ, A s Wk : Γ }} by mauto 4.
    assert {{ Γ, A s WkId : Γ }} by mauto 4.
    assert {{ Γ, A s IdWk WkId : Γ }} as <- by (transitivity {{{ Wk }}}; mauto 3).
    eapply glu_ctx_env_sub_monotone; mauto 4.
Qed.

Tactics for glu_rel_*


Ltac deex_destruct_glu_rel H H' :=
  match type of H with
  | forall _ _ _ _, exists _, _ =>
      let H'' := fresh "H" in
        pose proof (H _ _ _ H') as H''; deex_once_in H''
  | _ => destruct (H _ _ _ H') as []
  end.

Ltac destruct_glu_rel_by_assumption sub_glu_rel H :=
  repeat
    match goal with
    | H' : {{ ^?Δ s ^® ^ ?sub_glu_rel0 }} |- _ =>
        unify sub_glu_rel0 sub_glu_rel;
        deex_destruct_glu_rel H H';
        destruct_conjs;
        mark_with H' 1
    end;
  unmark_all_with 1.

Ltac destruct_glu_rel_exp_with_sub :=
  repeat
    match goal with
    | H : (forall Δ σ ρ, {{ Δ s σ ® ρ ?sub_glu_rel }} -> glu_rel_exp_with_sub _ _ _ _ _ _) |- _ =>
        destruct_glu_rel_by_assumption sub_glu_rel H; fail_if_dup; mark H
    | H : glu_rel_exp_with_sub _ _ _ _ _ _ |- _ =>
        dependent destruction H
    end;
  unmark_all.

(* TODO: unify with the uip version above *)
Ltac destruct_glu_rel_exp_with_sub_nouip :=
  repeat
    match goal with
    | H : (forall Δ σ ρ, {{ Δ s σ ® ρ ?sub_glu_rel }} -> glu_rel_exp_with_sub _ _ _ _ _ _) |- _ =>
        destruct_glu_rel_by_assumption sub_glu_rel H; fail_if_dup; mark H
    | H : glu_rel_exp_with_sub _ _ _ _ _ _ |- _ =>
        dependent inversion_clear H
    end;
  unmark_all.

Ltac destruct_glu_rel_sub_with_sub :=
  repeat
    match goal with
    | H : (forall Δ σ ρ, {{ Δ s σ ® ρ ?sub_glu_rel }} -> glu_rel_sub_with_sub _ _ _ _ _) |- _ =>
        destruct_glu_rel_by_assumption sub_glu_rel H; fail_if_dup; mark H
    | H : glu_rel_exp_with_sub _ _ _ _ _ _ |- _ =>
        dependent destruction H
    end;
  unmark_all.

(* TODO: unify with the uip version above *)
Ltac destruct_glu_rel_sub_with_sub_nouip :=
  repeat
    match goal with
    | H : (forall Δ σ ρ, {{ Δ s σ ® ρ ?sub_glu_rel }} -> glu_rel_sub_with_sub _ _ _ _ _) |- _ =>
        destruct_glu_rel_by_assumption sub_glu_rel H; fail_if_dup; mark H
    | H : glu_rel_exp_with_sub _ _ _ _ _ _ |- _ =>
        dependent inversion_clear H
    end;
  unmark_all.

Ltac destruct_glu_rel_typ_with_sub :=
  repeat
    match goal with
    | H : (forall Δ σ ρ, {{ Δ s σ ® ρ ?sub_glu_rel }} -> glu_rel_typ_with_sub _ _ _ _ _) |- _ =>
        destruct_glu_rel_by_assumption sub_glu_rel H; fail_if_dup; mark H
    | H : glu_rel_exp_with_sub _ _ _ _ _ _ |- _ =>
        dependent destruction H
    end;
  unmark_all.

(* TODO: unify with the uip version above *)
Ltac destruct_glu_rel_typ_with_sub_nouip :=
  repeat
    match goal with
    | H : (forall Δ σ ρ, {{ Δ s σ ® ρ ?sub_glu_rel }} -> glu_rel_typ_with_sub _ _ _ _ _) |- _ =>
        destruct_glu_rel_by_assumption sub_glu_rel H; fail_if_dup; mark H
    | H : glu_rel_exp_with_sub _ _ _ _ _ _ |- _ =>
        dependent inversion_clear H
    end;
  unmark_all.

Lemmas about glu_rel_exp


Lemma glu_rel_exp_clean_inversion1 : forall {Γ Sb M A},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ Γ M : A }} ->
    exists i,
    forall Δ σ ρ,
      {{ Δ s σ ® ρ Sb }} ->
      glu_rel_exp_with_sub i Δ M A σ ρ.
Proof.
  intros * ? [].
  destruct_conjs.
  eexists; intros.
  handle_functional_glu_ctx_env.
  intuition.
Qed.

Lemma glu_rel_exp_clean_inversion2 : forall {i Γ Sb M A},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ Γ A : Type@i }} ->
    {{ Γ M : A }} ->
    glu_rel_exp_resp_sub_env i Sb M A.
Proof.
  simpl.
  intros * ? HA HM.
  eapply glu_rel_exp_clean_inversion1 in HA; [| eassumption].
  eapply glu_rel_exp_clean_inversion1 in HM; [| eassumption].
  destruct_conjs.
  intros.
  destruct_glu_rel_exp_with_sub.
  simplify_evals.
  match_by_head glu_univ_elem ltac:(fun H => directed invert_glu_univ_elem H).
  apply_predicate_equivalence.
  unfold univ_glu_exp_pred' in *.
  destruct_conjs.
  econstructor; mauto 3.
  eapply glu_univ_elem_exp_conv; revgoals; mauto 3.
Qed.

#[global]
Ltac basic_invert_glu_rel_exp H :=
  (unshelve eapply (glu_rel_exp_clean_inversion2 _ _) in H; shelve_unifiable; [eassumption | eassumption |];
   simpl in H)
  + (unshelve eapply (glu_rel_exp_clean_inversion1 _) in H; shelve_unifiable; [eassumption |];
     deex_once_in H)
  + (inversion H as [? [? [? ?]]]; subst).

#[global]
Ltac invert_glu_rel_exp H :=
  basic_invert_glu_rel_exp H.

Lemma glu_rel_exp_to_wf_exp : forall {Γ A M},
    {{ Γ M : A }} ->
    {{ Γ M : A }}.
Proof.
  intros * [Sb].
  destruct_conjs.
  assert (exists env_rel, {{ EF Γ Γ per_ctx_env env_rel }}) as [env_rel] by mauto 3.
  assert (exists ρ ρ', initial_env Γ ρ /\ initial_env Γ ρ' /\ {{ Dom ρ ρ' env_rel }}) as [ρ] by mauto using per_ctx_then_per_env_initial_env.
  destruct_conjs.
  functional_initial_env_rewrite_clear.
  assert {{ Γ s Id ® ρ Sb }} by (eapply initial_env_glu_rel_exp; mauto 3).
  destruct_glu_rel_exp_with_sub.
  enough {{ Γ M[Id] : A[Id] }} as HId; mauto 3 using glu_univ_elem_trm_escape.
Qed.

#[export]
Hint Resolve glu_rel_exp_to_wf_exp : mctt.

Lemma glu_ctx_env_cons_clean_inversion'_helper : forall {Γ TSb A Sb i},
  {{ EG Γ glu_ctx_env TSb }} ->
  {{ EG Γ, A glu_ctx_env Sb }} ->
  {{ Γ A : Type@i }} ->
  {{ Γ A : Type@i }} ->
  (forall Δ σ ρ,
      {{ Δ s σ ® ρ TSb }} ->
      glu_rel_typ_with_sub i Δ A σ ρ) /\
    (Sb <∙> cons_glu_sub_pred i Γ A TSb).
Proof.
  intros * HΓA [? [? [j HA]]] ?.
  handle_functional_glu_ctx_env.
  assert (forall Δ σ ρ, {{ Δ s σ ® ρ TSb }} -> glu_rel_typ_with_sub i Δ A σ ρ) by
    (intros; apply_equiv_right; mauto).
  split; trivial.

  match_by_head glu_ctx_env progressive_invert.
  handle_functional_glu_ctx_env.

  intros Δ σ ρ.
  split; intros [].
  - apply_equiv_left.
    destruct_glu_rel_typ_with_sub.
    handle_functional_glu_univ_elem.
    econstructor; intuition.
    eapply @glu_univ_elem_exp_conv' with (i:=i0) (j:=i); try eassumption.
    bulky_rewrite.
  - rewrite <- H5 in *.
    destruct_glu_rel_typ_with_sub.
    handle_functional_glu_univ_elem.
    econstructor; intuition.
    eapply @glu_univ_elem_exp_conv' with (i:=i) (j:=i0); try eassumption.
    bulky_rewrite.
Qed.

Lemma glu_ctx_env_cons_clean_inversion' : forall {Γ TSb A Sb i},
  {{ EG Γ glu_ctx_env TSb }} ->
  {{ EG Γ, A glu_ctx_env Sb }} ->
  {{ Γ A : Type@i }} ->
  (forall Δ σ ρ,
      {{ Δ s σ ® ρ TSb }} ->
      glu_rel_typ_with_sub i Δ A σ ρ) /\
    (Sb <∙> cons_glu_sub_pred i Γ A TSb).
Proof.
  mauto using glu_ctx_env_cons_clean_inversion'_helper.
Qed.

Ltac invert_glu_ctx_env H ::=
  directed (unshelve eapply (glu_ctx_env_cons_clean_inversion' _) in H; shelve_unifiable; try eassumption; destruct H)
  + basic_invert_glu_ctx_env H.

Lemmas about glu_rel_sub


Lemma glu_rel_sub_clean_inversion1 : forall {Γ Sb τ Γ'},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ Γ s τ : Γ' }} ->
    exists Sb' : glu_sub_pred,
      glu_ctx_env Sb' Γ' /\ (forall (Δ : ctx) (σ : sub) (ρ : env), Sb Δ σ ρ -> glu_rel_sub_with_sub Δ τ Sb' σ ρ).
Proof.
  intros * ? [? []].
  destruct_conjs.
  handle_functional_glu_ctx_env.
  eexists; split; mauto 3.
  intros.
  rewrite_predicate_equivalence_right.
  mauto 3.
Qed.

Lemma glu_rel_sub_clean_inversion2 : forall {Γ τ Γ' Sb'},
    {{ EG Γ' glu_ctx_env Sb' }} ->
    {{ Γ s τ : Γ' }} ->
    exists Sb : glu_sub_pred,
      glu_ctx_env Sb Γ /\ (forall (Δ : ctx) (σ : sub) (ρ : env), Sb Δ σ ρ -> glu_rel_sub_with_sub Δ τ Sb' σ ρ).
Proof.
  intros * ? [? [Sb'0]].
  destruct_conjs.
  handle_functional_glu_ctx_env.
  eexists; split; mauto 3.
  intros.
  assert (glu_rel_sub_with_sub Δ τ Sb'0 σ ρ) as [] by mauto 3.
  econstructor; mauto 3.
  rewrite_predicate_equivalence_right.
  mauto 3.
Qed.

Lemma glu_rel_sub_clean_inversion3 : forall {Γ Sb τ Γ' Sb'},
    {{ EG Γ glu_ctx_env Sb }} ->
    {{ EG Γ' glu_ctx_env Sb' }} ->
    {{ Γ s τ : Γ' }} ->
    glu_rel_sub_resp_sub_env Sb Sb' τ.
Proof.
  simpl. intros * ? ? Hglu.
  eapply glu_rel_sub_clean_inversion2 in Hglu; [| eassumption].
  destruct_conjs.
  handle_functional_glu_ctx_env.
  intros.
  rewrite_predicate_equivalence_right.
  mauto 3.
Qed.

Ltac invert_glu_rel_sub H :=
  (unshelve eapply (glu_rel_sub_clean_inversion3 _ _) in H; shelve_unifiable; [eassumption | eassumption |]; simpl in H)
  + (unshelve eapply (glu_rel_sub_clean_inversion2 _) in H; shelve_unifiable; [eassumption |];
     destruct H as [? []])
  + (unshelve eapply (glu_rel_sub_clean_inversion1 _) in H; shelve_unifiable; [eassumption |];
     destruct H as [? []])
  + (inversion H; subst).

Lemma glu_rel_sub_wf_sub : forall {Γ σ Δ},
    {{ Γ s σ : Δ }} ->
    {{ Γ s σ : Δ }}.
Proof.
  intros * [SbΓ [SbΔ]].
  destruct_conjs.
  assert (exists env_relΓ, {{ EF Γ Γ per_ctx_env env_relΓ }}) as [env_relΓ] by mauto 3.
  assert (exists ρ ρ', initial_env Γ ρ /\ initial_env Γ ρ' /\ {{ Dom ρ ρ' env_relΓ }}) as [ρ] by mauto 3 using per_ctx_then_per_env_initial_env.
  destruct_conjs.
  functional_initial_env_rewrite_clear.
  assert {{ Γ s Id ® ρ SbΓ }} by (eapply initial_env_glu_rel_exp; mauto 3).
  destruct_glu_rel_sub_with_sub.
  mauto 3.
Qed.

#[export]
Hint Resolve glu_rel_sub_wf_sub : mctt.

Ltac saturate_glu_typ_from_el1 :=
  match goal with
  | H : glu_univ_elem _ _ ?El _, H1 : ?El _ _ _ _ |- _ =>
      pose proof (glu_univ_elem_trm_typ _ _ _ _ H _ _ _ _ H1);
      fail_if_dup
  end.

Ltac saturate_glu_typ_from_el :=
  fail_if_dup;
  repeat saturate_glu_typ_from_el1.

Ltac unify_glu_univ_lvl1 i :=
  match goal with
  | H1 : glu_univ_elem _ _ ?El _, H2 : glu_univ_elem i ?P _ _, H3 : ?P _ _, H4 : ?El _ _ _ _
    |- _ =>
      pose proof (glu_univ_elem_exp_conv' H1 H2 H4 H3);
      fail_if_dup
  end.

Ltac unify_glu_univ_lvl i :=
  fail_if_dup;
  repeat unify_glu_univ_lvl1 i.

Ltac apply_glu_rel_judge :=
  destruct_glu_rel_typ_with_sub;
  destruct_glu_rel_exp_with_sub;
  destruct_glu_rel_sub_with_sub;
  simplify_evals;
  handle_functional_glu_univ_elem;
  match_by_head glu_univ_elem ltac:(fun H => directed invert_glu_univ_elem H);
  handle_functional_glu_univ_elem;
  unfold univ_glu_exp_pred' in *;
  destruct_all;
  clear_dups.

Ltac apply_glu_rel_exp_judge :=
  destruct_glu_rel_exp_with_sub;
  simplify_evals;
  handle_functional_glu_univ_elem;
  match_by_head glu_univ_elem ltac:(fun H => directed invert_glu_univ_elem H);
  handle_functional_glu_univ_elem;
  unfold univ_glu_exp_pred' in *;
  destruct_all;
  clear_dups.

(* TODO: unify with the uip version above *)
Ltac apply_glu_rel_judge_nouip :=
  destruct_glu_rel_typ_with_sub_nouip;
  destruct_glu_rel_exp_with_sub_nouip;
  destruct_glu_rel_sub_with_sub_nouip;
  simplify_evals;
  handle_functional_glu_univ_elem;
  match_by_head glu_univ_elem ltac:(fun H => directed invert_glu_univ_elem_nouip H);
  handle_functional_glu_univ_elem;
  unfold univ_glu_exp_pred' in *;
  destruct_all;
  clear_dups.

(* TODO: unify with the uip version above *)
Ltac apply_glu_rel_exp_judge_nouip :=
  destruct_glu_rel_exp_with_sub_nouip;
  simplify_evals;
  handle_functional_glu_univ_elem;
  match_by_head glu_univ_elem ltac:(fun H => directed invert_glu_univ_elem_nouip H);
  handle_functional_glu_univ_elem;
  unfold univ_glu_exp_pred' in *;
  destruct_all;
  clear_dups.

Lemma glu_rel_exp_preserves_lvl : forall Γ Sb M A i,
    {{ EG Γ glu_ctx_env Sb }} ->
    (forall Δ σ ρ,
        {{ Δ s σ ® ρ Sb }} ->
        glu_rel_exp_with_sub i Δ M A σ ρ) ->
    {{ Γ A : Type@i }}.
Proof.
  intros.
  assert (exists env_relΓ, {{ EF Γ Γ per_ctx_env env_relΓ }}) as [env_relΓ] by mauto 3.
  assert (exists ρ ρ', initial_env Γ ρ /\ initial_env Γ ρ' /\ {{ Dom ρ ρ' env_relΓ }}) as [ρ] by mauto 3 using per_ctx_then_per_env_initial_env.
  destruct_conjs.
  functional_initial_env_rewrite_clear.
  assert {{ Γ s Id ® ρ Sb }} by (eapply initial_env_glu_rel_exp; mauto 3).
  destruct_glu_rel_exp_with_sub.
  saturate_glu_typ_from_el.
  saturate_glu_info.
  mauto 3.
Qed.

#[export]
Hint Resolve glu_rel_exp_preserves_lvl : mctt.

Ltac saturate_syn_judge1 :=
  match goal with
  | H : {{ ^?Γ ^?M : ^?A }} |- _ =>
      assert {{ Γ M : A }} by mauto; fail_if_dup
  | H : {{ ^?Γ s ^: ^?Γ' }} |- _ =>
      assert {{ Γ s τ : Γ' }} by mauto; fail_if_dup
  | H : {{ ^?Γ }} |- _ =>
      assert {{ Γ }} by mauto; fail_if_dup
  end.

#[global]
  Ltac saturate_syn_judge :=
  repeat saturate_syn_judge1.

#[global]
  Ltac invert_sem_judge1 :=
  match goal with
  | H : {{ ^?Γ ^?M : ^?A }} |- _ =>
      mark H;
      let H' := fresh "H" in
      pose proof H as H';
      invert_glu_rel_exp H'
  | H : {{ ^?Γ s ^: ^?Γ' }} |- _ =>
      mark H;
      let H' := fresh "H" in
      pose proof H as H';
      invert_glu_rel_sub H'
  end.

#[global]
  Ltac invert_sem_judge :=
  repeat invert_sem_judge1; unmark_all.