Built with Alectryon, running Coq+SerAPI v8.17.0+0.17.3. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+πŸ–±οΈ to focus. On Mac, use ⌘ instead of Ctrl.
From OGS Require Import Prelude.
From OGS.Utils Require Import Psh Rel.
From OGS.Ctx Require Import All Ctx Covering Subset Subst.
From OGS.ITree Require Import Event ITree Eq Delay Structure Properties.
From OGS.OGS Require Import Soundness.
Set Equations Transparent.

Inductive pre_ty : Type :=
| Zer : pre_ty
| One : pre_ty
| Prod : pre_ty -> pre_ty -> pre_ty
| Sum : pre_ty -> pre_ty -> pre_ty
| Arr : pre_ty -> pre_ty -> pre_ty
.
Notation "`0" := (Zer) : ty_scope.
Notation "`1" := (One) : ty_scope.
Notation "A `Γ— B" := (Prod A B) (at level 40) : ty_scope.
Notation "A `+ B" := (Sum A B) (at level 40) : ty_scope.
Notation "A `β†’ B" := (Arr A B) (at level 40) : ty_scope .

Variant ty : Type :=
| LTy : pre_ty -> ty
| RTy : pre_ty -> ty
.
Derive NoConfusion for ty.
Bind Scope ty_scope with ty.
#[global] Coercion LTy : pre_ty >-> ty.
#[global] Notation "↑ t" := (LTy t) (at level 5) : ty_scope .
#[global] Notation "Β¬ t" := (RTy t) (at level 5) : ty_scope .
Open Scope ty_scope.

Equations t_neg : ty -> ty :=
  t_neg ↑a := Β¬a ;
  t_neg Β¬a := ↑a .
Notation "a †" := (t_neg a) (at level 5) : ty_scope.

Definition t_ctx : Type := ctx ty.
Bind Scope ctx_scope with t_ctx.

Inductive term : t_ctx -> ty -> Type :=
| Val {Ξ“ A} : whn Ξ“ A -> term Ξ“ ↑A
| Mu {Ξ“ A} : state (Ξ“ β–Άβ‚“ Β¬A) -> term Ξ“ ↑A

| VarR {Ξ“ A} : Ξ“ βˆ‹ Β¬A -> term Ξ“ Β¬A
| MuT {Ξ“ A} : state (Ξ“ β–Άβ‚“ ↑A) -> term Ξ“ Β¬A

| Boom {Ξ“} : term Ξ“ Β¬`0
| Case {Ξ“ A B} : state (Ξ“ β–Άβ‚“ ↑A) -> state (Ξ“ β–Άβ‚“ ↑B) -> term Ξ“ Β¬(A `+ B)

| Fst {Ξ“ A B} : term Ξ“ Β¬A -> term Ξ“ Β¬(A `Γ— B)
| Snd {Ξ“ A B} : term Ξ“ Β¬B -> term Ξ“ Β¬(A `Γ— B)
| App {Ξ“ A B} : whn Ξ“ A -> term Ξ“ Β¬B -> term Ξ“ Β¬(A `β†’ B)

with whn : t_ctx -> pre_ty -> Type :=
| VarL {Ξ“ A} : Ξ“ βˆ‹ ↑A -> whn Ξ“ A

| Inl {Ξ“ A B} : whn Ξ“ A -> whn Ξ“ (A `+ B)
| Inr {Ξ“ A B} : whn Ξ“ B -> whn Ξ“ (A `+ B)

| Tt {Ξ“} : whn Ξ“ `1
| Pair {Ξ“ A B} : state (Ξ“ β–Άβ‚“ Β¬A) -> state (Ξ“ β–Άβ‚“ Β¬B) -> whn Ξ“ (A `Γ— B)
| Lam {Ξ“ A B} : state (Ξ“ β–Άβ‚“ ↑(A `β†’ B) β–Άβ‚“ ↑A β–Άβ‚“ Β¬B) -> whn Ξ“ (A `β†’ B)

with state : t_ctx -> Type :=
| Cut {Ξ“ A} : term Ξ“ ↑A -> term Ξ“ Β¬A -> state Ξ“
.

Definition Cut' {Ξ“ A} : term Ξ“ A -> term Ξ“ A† -> state Ξ“
  := match A with
     | ↑_ => fun x y => Cut x y
     | Β¬_ => fun x y => Cut y x
     end .

Equations val : t_ctx -> ty -> Type :=
  val Ξ“ ↑A := whn Ξ“ A ;
  val Ξ“ Β¬A := term Ξ“ Β¬A .

Equations Var Ξ“ A : Ξ“ βˆ‹ A -> val Ξ“ A :=
  Var _ ↑_ i := VarL i ;
  Var _ Β¬_ i := VarR i .
Arguments Var {Ξ“} [A] i.

Equations t_of_v Ξ“ A : val Ξ“ A -> term Ξ“ A :=
  t_of_v _ ↑_ v := Val v ;
  t_of_v _ Β¬_ k := k .
Arguments t_of_v [Ξ“ A] v.
Coercion t_of_v : val >-> term.

Equations t_rename : term ⇒₁ ⟦ c_var , term βŸ§β‚ :=
  t_rename _ _ (Mu c)     _ f := Mu (s_rename _ c _ (r_shift1 f)) ;
  t_rename _ _ (Val v)    _ f := Val (w_rename _ _ v _ f) ;
  t_rename _ _ (VarR i)   _ f := VarR (f _ i) ;
  t_rename _ _ (MuT c)    _ f := MuT (s_rename _ c _ (r_shift1 f)) ;
  t_rename _ _ (Boom)     _ f := Boom ;
  t_rename _ _ (App u k)  _ f := App (w_rename _ _ u _ f) (t_rename _ _ k _ f) ;
  t_rename _ _ (Fst k)    _ f := Fst (t_rename _ _ k _ f) ;
  t_rename _ _ (Snd k)    _ f := Snd (t_rename _ _ k _ f) ;
  t_rename _ _ (Case u v) _ f :=
    Case (s_rename _ u _ (r_shift1 f))
         (s_rename _ v _ (r_shift1 f))
with w_rename : whn ⇒₁ ⟦ c_var , val βŸ§β‚ :=
  w_rename _ _ (VarL i)   _ f := VarL (f _ i) ;
  w_rename _ _ (Tt)       _ f := Tt ;
  w_rename _ _ (Lam u)    _ f := Lam (s_rename _ u _ (r_shift3 f)) ;
  w_rename _ _ (Pair u v) _ f :=
    Pair (s_rename _ u _ (r_shift1 f))
         (s_rename _ v _ (r_shift1 f)) ;
  w_rename _ _ (Inl u)    _ f := Inl (w_rename _ _ u _ f) ;
  w_rename _ _ (Inr u)    _ f := Inr (w_rename _ _ u _ f)
with s_rename : state β‡’β‚€ ⟦ c_var , state βŸ§β‚€ :=
   s_rename _ (Cut v k) _ f := Cut (t_rename _ _ v _ f) (t_rename _ _ k _ f) .

Equations v_rename : val ⇒₁ ⟦ c_var , val βŸ§β‚ :=
  v_rename _ ↑_ v _ f := w_rename _ _ v _ f ;
  v_rename _ Β¬_ k _ f := t_rename _ _ k _ f .

Notation "t β‚œβŠ›α΅£ r" := (t_rename _ _ t _ r%asgn) (at level 14).
Notation "w `α΅₯βŠ›α΅£ r" := (w_rename _ _ w _ r%asgn) (at level 14).
Notation "v α΅₯βŠ›α΅£ r" := (v_rename _ _ v _ r%asgn) (at level 14).
Notation "s β‚›βŠ›α΅£ r" := (s_rename _ s _ r%asgn) (at level 14).

Definition a_ren {Ξ“1 Ξ“2 Ξ“3} : Ξ“1 =[val]> Ξ“2 -> Ξ“2 βŠ† Ξ“3 -> Ξ“1 =[val]> Ξ“3 :=
  fun f g _ i => v_rename _ _ (f _ i) _ g .
Arguments a_ren {_ _ _} _ _ _ _ /.
Notation "a βŠ›α΅£ r" := (a_ren a r%asgn) (at level 14) : asgn_scope.

Definition t_shift1 {Ξ“ A} : term Ξ“  β‡’α΅’ term (Ξ“ β–Άβ‚“ A) := fun _ t => t β‚œβŠ›α΅£ r_pop.
Definition w_shift1 {Ξ“ A} : whn Ξ“   β‡’α΅’ whn (Ξ“ β–Άβ‚“ A)  := fun _ w => w `α΅₯βŠ›α΅£ r_pop.
Definition s_shift1 {Ξ“ A} : state Ξ“ -> state (Ξ“ β–Άβ‚“ A) := fun s => s β‚›βŠ›α΅£ r_pop.
Definition v_shift1 {Ξ“ A} : val Ξ“   β‡’α΅’ val (Ξ“ β–Άβ‚“ A)  := fun _ v => v α΅₯βŠ›α΅£ r_pop.
Definition v_shift3 {Ξ“ A B C} : val Ξ“ β‡’α΅’ val (Ξ“ β–Άβ‚“ A β–Άβ‚“ B β–Άβ‚“ C)
  := fun _ v => v α΅₯βŠ›α΅£ (r_pop α΅£βŠ› r_pop α΅£βŠ› r_pop).
Definition a_shift1 {Ξ“ Ξ”} [A] (a : Ξ“ =[val]> Ξ”) : (Ξ“ β–Άβ‚“ A) =[val]> (Ξ” β–Άβ‚“ A)
  := [ fun _ i => v_shift1 _ (a _ i) ,β‚“ Var top ].
Definition a_shift3 {Ξ“ Ξ”} [A B C] (a : Ξ“ =[val]> Ξ”)
  : (Ξ“ β–Άβ‚“ A β–Άβ‚“ B β–Άβ‚“ C) =[val]> (Ξ” β–Άβ‚“ A β–Άβ‚“ B β–Άβ‚“ C)
  := [ [ [ fun _ i => v_shift3 _ (a _ i) ,β‚“
           Var (pop (pop top)) ] ,β‚“
         Var (pop top) ] ,β‚“
       Var top ].

Equations t_subst : term ⇒₁ ⟦ val , term βŸ§β‚ :=
  t_subst _ _ (Mu c)     _ f := Mu (s_subst _ c _ (a_shift1 f)) ;
  t_subst _ _ (Val v)    _ f := Val (w_subst _ _ v _ f) ;
  t_subst _ _ (VarR i)   _ f := f _ i ;
  t_subst _ _ (MuT c)    _ f := MuT (s_subst _ c _ (a_shift1 f)) ;
  t_subst _ _ (Boom)     _ f := Boom ;
  t_subst _ _ (App u k)  _ f := App (w_subst _ _ u _ f) (t_subst _ _ k _ f) ;
  t_subst _ _ (Fst k)    _ f := Fst (t_subst _ _ k _ f) ;
  t_subst _ _ (Snd k)    _ f := Snd (t_subst _ _ k _ f) ;
  t_subst _ _ (Case u v) _ f :=
    Case (s_subst _ u _ (a_shift1 f))
          (s_subst _ v _ (a_shift1 f))
with w_subst : forall Ξ“ A, whn Ξ“ A -> forall Ξ”, Ξ“ =[val]> Ξ” -> whn Ξ” A :=
  w_subst _ _ (VarL i)   _ f := f _ i ;
  w_subst _ _ (Tt)       _ f := Tt ;
  w_subst _ _ (Lam u)    _ f := Lam (s_subst _ u _ (a_shift3 f)) ;
  w_subst _ _ (Pair u v) _ f := Pair (s_subst _ u _ (a_shift1 f))
                                     (s_subst _ v _ (a_shift1 f)) ;
  w_subst _ _ (Inl u)    _ f := Inl (w_subst _ _ u _ f) ;
  w_subst _ _ (Inr u)    _ f := Inr (w_subst _ _ u _ f)
with s_subst : state β‡’β‚€ ⟦ val , state βŸ§β‚€ :=
   s_subst _ (Cut v k) _ f := Cut (t_subst _ _ v _ f) (t_subst _ _ k _ f) .

Notation "t `β‚œβŠ› a" := (t_subst _ _ t _ a%asgn) (at level 30).
Notation "w `α΅₯βŠ› a" := (w_subst _ _ w _ a%asgn) (at level 30).

Equations v_subst : val ⇒₁ ⟦ val , val βŸ§β‚ :=
  v_subst _ ↑_ v _ f := v `α΅₯βŠ› f ;
  v_subst _ Β¬_ k _ f := k `β‚œβŠ› f .

#[global] Instance val_monoid : subst_monoid val :=
  {| v_var := @Var ; v_sub := v_subst |} .
#[global] Instance state_module : subst_module val state :=
  {| c_sub := s_subst |} .

Definition asgn1 {Ξ“ A} (v : val Ξ“ A) : (Ξ“ β–Άβ‚“ A) =[val]> Ξ“
  := [ Var ,β‚“ v ] .
Definition asgn3 {Ξ“ A B C} (v1 : val Ξ“ A) (v2 : val Ξ“ B) (v3 : val Ξ“ C)
  : (Ξ“ β–Άβ‚“ A β–Άβ‚“ B β–Άβ‚“ C) =[val]> Ξ“
  := [ [ [ Var ,β‚“ v1 ] ,β‚“ v2 ] ,β‚“ v3 ].
Arguments asgn1 {_ _} & _.
Arguments asgn3 {_ _ _ _} & _ _.

Notation "₁[ v ]" := (asgn1 v).
Notation "₃[ v1 , v2 , v3 ]" := (asgn3 v1 v2 v3).

(*
Variant forcing0 (Ξ“ : t_ctx) : pre_ty -> Type :=
| FBoom : forcing0 Ξ“ Zer
| FApp {a b} : whn Ξ“ a -> term Ξ“ Β¬b -> forcing0 Ξ“ (a `β†’ b)
| FFst {a b} : term Ξ“ Β¬a -> forcing0 Ξ“ (a `Γ— b)
| FSnd {a b} : term Ξ“ Β¬b -> forcing0 Ξ“ (a `Γ— b)
| FCase {a b} : state (Ξ“ β–Άβ‚“ ↑a) -> state (Ξ“ β–Άβ‚“ ↑b) -> forcing0 Ξ“ (a `+ b)
.
Arguments FBoom {Ξ“}.
Arguments FApp {Ξ“ a b}.
Arguments FFst {Ξ“ a b}.
Arguments FSnd {Ξ“ a b}.
Arguments FCase {Ξ“ a b}.

Equations f0_subst {Ξ“ Ξ”} : Ξ“ =[val]> Ξ” -> forcing0 Ξ“ β‡’α΅’ forcing0 Ξ” :=
  f0_subst f a (FBoom)        := FBoom ;
  f0_subst f a (FApp v k)     := FApp (w_subst f _ v) (t_subst f _ k) ;
  f0_subst f a (FFst k)       := FFst (t_subst f _ k) ;
  f0_subst f a (FSnd k)       := FSnd (t_subst f _ k) ;
  f0_subst f a (FCase s1 s2) := FCase (s_subst (a_shift1 f) s1) (s_subst (a_shift1 f) s2) .

Equations forcing : t_ctx -> ty -> Type :=
  forcing Ξ“ (t+ a) := whn Ξ“ a ;
  forcing Ξ“ (t- a) := forcing0 Ξ“ a .

Equations f_subst {Ξ“ Ξ”} : Ξ“ =[val]> Ξ” -> forcing Ξ“ β‡’α΅’ forcing Ξ” :=
  f_subst s (t+ a) v := w_subst s a v ;
  f_subst s (t- a) f := f0_subst s a f .
*)

Equations is_neg_pre : pre_ty -> SProp :=
  is_neg_pre `0       := sEmpty ;
  is_neg_pre `1       := sUnit ;
  is_neg_pre (_ `Γ— _) := sUnit ;
  is_neg_pre (_ `+ _) := sEmpty ;
  is_neg_pre (_ `β†’ _) := sUnit .

Equations is_neg : ty -> SProp :=
  is_neg ↑a := is_neg_pre a ;
  is_neg Β¬a := sUnit .

Definition neg_ty : Type := sigS is_neg.
Definition neg_coe : neg_ty -> ty := sub_elt.
Global Coercion neg_coe : neg_ty >-> ty.

Definition neg_ctx : Type := ctxS ty t_ctx is_neg.
Definition neg_c_coe : neg_ctx -> ctx ty := sub_elt.
Global Coercion neg_c_coe : neg_ctx >-> ctx.

Bind Scope ctx_scope with neg_ctx.
Bind Scope ctx_scope with ctx.

Inductive pat : ty -> Type :=
| PTt : pat ↑`1
| PPair {a b} : pat ↑(a `Γ— b)
| PInl {a b} : pat ↑a -> pat ↑(a `+ b)
| PInr {a b} : pat ↑b -> pat ↑(a `+ b)
| PLam {a b} : pat ↑(a `β†’ b)
| PFst {a b} : pat Β¬(a `Γ— b)
| PSnd {a b} : pat Β¬(a `Γ— b)
| PApp {a b} : pat ↑a -> pat Β¬(a `β†’ b)
.

Equations pat_dom {t} : pat t -> neg_ctx :=
  pat_dom (PInl u) := pat_dom u ;
  pat_dom (PInr u) := pat_dom u ;
  pat_dom (PTt) := βˆ…β‚› β–Άβ‚› {| sub_elt := ↑`1 ; sub_prf := stt |} ;
  pat_dom (@PLam a b) := βˆ…β‚› β–Άβ‚› {| sub_elt := ↑(a `β†’ b) ; sub_prf := stt |} ;
  pat_dom (@PPair a b) := βˆ…β‚› β–Άβ‚› {| sub_elt := ↑(a `Γ— b) ; sub_prf := stt |} ;
  pat_dom (@PApp a b v) := pat_dom v β–Άβ‚› {| sub_elt := Β¬b ; sub_prf := stt |} ;
  pat_dom (@PFst a b) := βˆ…β‚› β–Άβ‚› {| sub_elt := Β¬a ; sub_prf := stt |} ;
  pat_dom (@PSnd a b) := βˆ…β‚› β–Άβ‚› {| sub_elt := Β¬b ; sub_prf := stt |} .

Definition op_pat : Oper ty neg_ctx :=
  {| o_op a := pat a ; o_dom _ p := (pat_dom p) |} .

Definition op_copat : Oper ty neg_ctx :=
  {| o_op a := pat (t_neg a) ; o_dom _ p := (pat_dom p) |} .

Definition bare_copat := op_copatβˆ™ .

Equations v_of_p {A} (p : pat A) : val (pat_dom p) A :=
  v_of_p (PInl u) := Inl (v_of_p u) ;
  v_of_p (PInr u) := Inr (v_of_p u) ;
  v_of_p (PTt) := VarL top ;
  v_of_p (PLam) := VarL top ;
  v_of_p (PPair) := VarL top ;
  v_of_p (PApp v) := App (v_shift1 _ (v_of_p v)) (VarR top) ;
  v_of_p (PFst) := Fst (VarR top) ;
  v_of_p (PSnd) := Snd (VarR top) .
Coercion v_of_p : pat >-> val.

Definition elim_var_zer {A : Type} {Ξ“ : neg_ctx} (i : Ξ“ βˆ‹ ↑ `0) : A
  := match s_prf i with end .
Definition elim_var_sum {A : Type} {Ξ“ : neg_ctx} {s t} (i : Ξ“ βˆ‹ ↑ (s `+ t)) : A
  := match s_prf i with end .

Equations p_of_w {Ξ“ : neg_ctx} a : whn Ξ“ a -> pat ↑a :=
  p_of_w (`0)     (VarL i) := elim_var_zer i ;
  p_of_w (a `+ b) (VarL i) := elim_var_sum i ;
  p_of_w (a `+ b) (Inl v)  := PInl (p_of_w _ v) ;
  p_of_w (a `+ b) (Inr v)  := PInr (p_of_w _ v) ;
  p_of_w (`1)     _        := PTt ;
  p_of_w (a `Γ— b) _        := PPair ;
  p_of_w (a `β†’ b) _        := PLam .

Equations p_dom_of_w {Ξ“ : neg_ctx} a (v : whn Ξ“ a) : pat_dom (p_of_w a v) =[val]> Ξ“ :=
  p_dom_of_w (`0)     (VarL i) := elim_var_zer i ;
  p_dom_of_w (a `+ b) (VarL i) := elim_var_sum i ;
  p_dom_of_w (a `+ b) (Inl v)  := p_dom_of_w a v ;
  p_dom_of_w (a `+ b) (Inr v)  := p_dom_of_w b v ;
  p_dom_of_w (`1)     v        := [ ! ,β‚“ v ] ;
  p_dom_of_w (a `β†’ b) v        := [ ! ,β‚“ v ] ;
  p_dom_of_w (a `Γ— b) v        := [ ! ,β‚“ v ] .

Program Definition w_split {Ξ“ : neg_ctx} a (v : whn Ξ“ a) : (op_copat # val) Ξ“ Β¬a
  := p_of_w _ v ⦇ p_dom_of_w _ v ⦈ .

Definition L_nf : Famβ‚€ ty neg_ctx := c_var βˆ₯β‚› (op_copat # val ).

(*
Definition n_rename {Ξ“ Ξ” : neg_ctx} : Ξ“ βŠ† Ξ” -> L_nf Ξ“ -> L_nf Ξ”
  := fun r n => r _ (nf_var n) β‹… nf_obs n ⦇ a_ren r (nf_args n) ⦈.
*)

(*
Definition nf0_eq {Ξ“ a} : relation (nf0 Ξ“ a) :=
  fun a b => exists H : projT1 a = projT1 b, rew H in projT2 a ≑ₐ projT2 b .

Definition nf_eq {Ξ“} : relation (nf Ξ“) :=
  fun a b => exists H : projT1 a = projT1 b,
      (rew H in fst (projT2 a) = fst (projT2 b)) /\ (nf0_eq (rew H in snd (projT2 a)) (snd (projT2 b))).

#[global] Instance nf0_eq_rfl {Ξ“ t} : Reflexive (@nf0_eq Ξ“ t) .
  intros [ m a ]; unshelve econstructor; auto.
Qed.

#[global] Instance nf0_eq_sym {Ξ“ t} : Symmetric (@nf0_eq Ξ“ t) .
  intros [ m1 a1 ] [ m2 a2 ] [ p q ]; unshelve econstructor; cbn in *.
  - now symmetry.
  - revert a1 q ; rewrite p; intros a1 q.
    now symmetry.
Qed.

#[global] Instance nf0_eq_tra {Ξ“ t} : Transitive (@nf0_eq Ξ“ t) .
  intros [ m1 a1 ] [ m2 a2 ] [ m3 a3 ] [ p1 q1 ] [ p2 q2 ]; unshelve econstructor; cbn in *.
  - now transitivity m2.
  - transitivity (rew [fun p : pat (t_neg t) => pat_dom p =[ val ]> Ξ“] p2 in a2); auto.
    now rewrite <- p2.
Qed.

#[global] Instance nf_eq_rfl {Ξ“} : Reflexiveα΅’ (fun _ : T1 => @nf_eq Ξ“) .
  intros _ [ x [ i n ] ].
  unshelve econstructor; auto.
Qed.

#[global] Instance nf_eq_sym {Ξ“} : Symmetricα΅’ (fun _ : T1 => @nf_eq Ξ“) .
  intros _ [ x1 [ i1 n1 ] ] [ x2 [ i2 n2 ] ] [ p [ q1 q2 ] ].
  unshelve econstructor; [ | split ]; cbn in *.
  - now symmetry.
  - revert i1 q1; rewrite p; intros i1 q1; now symmetry.
  - revert n1 q2; rewrite p; intros n1 q2; now symmetry.
Qed.

#[global] Instance nf_eq_tra {Ξ“} : Transitiveα΅’ (fun _ : T1 => @nf_eq Ξ“) .
  intros _ [ x1 [ i1 n1 ] ] [ x2 [ i2 n2 ] ] [ x3 [ i3 n3 ] ] [ p1 [ q1 r1 ] ] [ p2 [ q2 r2 ] ].
  unshelve econstructor; [ | split ]; cbn in *.
  - now transitivity x2.
  - transitivity (rew [has Ξ“] p2 in i2); auto.
    now rewrite <- p2.
  - transitivity (rew [nf0 Ξ“] p2 in n2); auto.
    now rewrite <- p2.
Qed.

Definition comp_eq {Ξ“} : relation (delay (nf Ξ“)) :=
  it_eq (fun _ : T1 => nf_eq) (i := T1_0) .
Notation "u ≋ v" := (comp_eq u v) (at level 40) .

Definition pat_of_nf : nf β‡’α΅’ pat' :=
  fun Ξ“ u => (projT1 u ,' (fst (projT2 u) , projT1 (snd (projT2 u)))) .
*)

Program Definition app_nf {Ξ“ : neg_ctx} {a b} (i : Ξ“ βˆ‹ ↑(a `β†’ b))
  (v : whn Ξ“ a) (k : term Ξ“ Β¬b) : L_nf Ξ“
  := i β‹… PApp (p_of_w _ v) ⦇ [ p_dom_of_w _ v ,β‚“ (k : val _ Β¬_) ] ⦈ .

Program Definition fst_nf {Ξ“ : neg_ctx} {a b} (i : Ξ“ βˆ‹ ↑(a `Γ— b))
  (k : term Ξ“ Β¬a) : L_nf Ξ“
  := i β‹… PFst ⦇ [ ! ,β‚“ (k : val _ Β¬_) ] ⦈ .

Program Definition snd_nf {Ξ“ : neg_ctx} {a b} (i : Ξ“ βˆ‹ ↑(a `Γ— b))
  (k : term Ξ“ Β¬b) : L_nf Ξ“
  := i β‹… PSnd ⦇ [ ! ,β‚“ (k : val _ Β¬_) ] ⦈ .

Equations eval_aux {Ξ“ : neg_ctx} : state Ξ“ -> (state Ξ“ + L_nf Ξ“) :=
  eval_aux (Cut (Mu s)           (k))        := inl (s β‚œβŠ› ₁[ k ]) ;
  eval_aux (Cut (Val v)          (MuT s))    := inl (s β‚œβŠ› ₁[ v ]) ;

  eval_aux (Cut (Val v)          (VarR i))   := inr (s_var_upg i β‹… w_split _ v) ;

  eval_aux (Cut (Val (VarL i))   (Boom))     := elim_var_zer i ;
  eval_aux (Cut (Val (VarL i))   (Case s t)) := elim_var_sum i ;

  eval_aux (Cut (Val (VarL i))   (App v k))  := inr (app_nf i v k) ;
  eval_aux (Cut (Val (VarL i))   (Fst k))    := inr (fst_nf i k) ;
  eval_aux (Cut (Val (VarL i))   (Snd k))    := inr (snd_nf i k) ;


  eval_aux (Cut (Val (Lam s))    (App v k))  := inl (s β‚œβŠ› ₃[ Lam s , v , k ]) ;
  eval_aux (Cut (Val (Pair s t)) (Fst k))    := inl (s β‚œβŠ› ₁[ k ]) ;
  eval_aux (Cut (Val (Pair s t)) (Snd k))    := inl (t β‚œβŠ› ₁[ k ]) ;
  eval_aux (Cut (Val (Inl u))    (Case s t)) := inl (s β‚œβŠ› ₁[ u ]) ;
  eval_aux (Cut (Val (Inr u))    (Case s t)) := inl (t β‚œβŠ› ₁[ u ]) .

Definition eval {Ξ“ : neg_ctx} : state Ξ“ -> delay (L_nf Ξ“)
  := iter_delay (fun c => Ret' (eval_aux c)).

(*
Definition refold {Ξ“ : neg_ctx} (p : nf Ξ“)
  : (Ξ“ βˆ‹ (projT1 p) * val Ξ“ (t_neg (projT1 p)))%type.
destruct p as [x [i [ p s ]]]; cbn in *.
exact (i , v_subst s _ (v_of_p p)).
Defined.
*)

Definition p_app {Ξ“ A} (v : val Ξ“ A) (m : pat A†) (e : pat_dom m =[val]> Ξ“) : state Ξ“
  := Cut' v (m `β‚œβŠ› e) .

(*
Definition emb {Ξ“} (m : pat' Ξ“) : state (Ξ“ +β–Άβ‚“ pat_dom' Ξ“ m) .
  destruct m as [a [i v]]; cbn in *.
  destruct a.
  - refine (Cut _ _).
    + refine (Val (VarL (r_concat_l _ i))).
    + refine (t_rename r_concat_r _ (v_of_p v)).
  - refine (Cut _ _).
    + refine (Val (v_rename r_concat_r _ (v_of_p v))).
    + refine (VarR (r_concat_l _ i)).
Defined.
*)

Scheme term_mut := Induction for term Sort Prop
  with whn_mut := Induction for whn Sort Prop
  with state_mut := Induction for state Sort Prop.

Record syn_ind_args (Pt : forall Ξ“ A, term Ξ“ A -> Prop)
                    (Pv : forall Ξ“ A, whn Ξ“ A -> Prop)
                    (Ps : forall Ξ“, state Ξ“ -> Prop) :=
{
  ind_s_mu : forall Ξ“ A s, Ps _ s -> Pt Ξ“ ↑A (Mu s) ;
  ind_s_val : forall Ξ“ A v, Pv _ _ v -> Pt Ξ“ ↑A (Val v) ;
  ind_s_varn : forall Ξ“ A i, Pt Ξ“ Β¬A (VarR i) ;
  ind_s_mut : forall Ξ“ A s, Ps _ s -> Pt Ξ“ Β¬A (MuT s) ;
  ind_s_zer : forall Ξ“, Pt Ξ“ Β¬`0 Boom ;
  ind_s_app : forall Ξ“ A B, forall v, Pv _ _ v -> forall k, Pt _ _ k -> Pt Ξ“ Β¬(A `β†’ B) (App v k) ;
  ind_s_fst : forall Ξ“ A B, forall k, Pt _ _ k -> Pt Ξ“ Β¬(A `Γ— B) (Fst k) ;
  ind_s_snd : forall Ξ“ A B, forall k, Pt _ _ k -> Pt Ξ“ Β¬(A `Γ— B) (Snd k) ;
  ind_s_match : forall Ξ“ A B, forall s, Ps _ s -> forall t, Ps _ t -> Pt Ξ“ Β¬(A `+ B) (Case s t) ;
  ind_s_varp : forall Ξ“ A i, Pv Ξ“ A (VarL i) ;
  ind_s_inl : forall Ξ“ A B v, Pv _ _ v -> Pv Ξ“ (A `+ B) (Inl v) ;
  ind_s_inr : forall Ξ“ A B v, Pv _ _ v -> Pv Ξ“ (A `+ B) (Inr v) ;
  ind_s_onei : forall Ξ“, Pv Ξ“ `1 Tt ;
  ind_s_lam : forall Ξ“ A B s, Ps _ s -> Pv Ξ“ (A `β†’ B) (Lam s) ;
  ind_s_pair : forall Ξ“ A B, forall s, Ps _ s -> forall t, Ps _ t -> Pv Ξ“ (A `Γ— B) (Pair s t) ;
  ind_s_cut : forall Ξ“ A, forall u, Pt _ _ u -> forall v, Pt _ _ v -> Ps Ξ“ (@Cut _ A u v)
} .

Lemma term_ind_mut Pt Pv Ps (arg : syn_ind_args Pt Pv Ps) Ξ“ A u : Pt Ξ“ A u.
  destruct arg; now apply (term_mut Pt Pv Ps).
Qed.

Lemma whn_ind_mut Pt Pv Ps (arg : syn_ind_args Pt Pv Ps) Ξ“ A v : Pv Ξ“ A v.
  destruct arg; now apply (whn_mut Pt Pv Ps).
Qed.

Lemma state_ind_mut Pt Pv Ps (arg : syn_ind_args Pt Pv Ps) Ξ“ s : Ps Ξ“ s.
  destruct arg; now apply (state_mut Pt Pv Ps).
Qed.

Definition t_ren_proper_P Ξ“ A (t : term Ξ“ A) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> t β‚œβŠ›α΅£ f1 = t β‚œβŠ›α΅£ f2 .
Definition w_ren_proper_P Ξ“ A (v : whn Ξ“ A) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> v `α΅₯βŠ›α΅£ f1 = v `α΅₯βŠ›α΅£ f2 .
Definition s_ren_proper_P Ξ“ (s : state Ξ“) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> s β‚›βŠ›α΅£ f1 = s β‚›βŠ›α΅£ f2 .
Lemma ren_proper_prf : syn_ind_args t_ren_proper_P w_ren_proper_P s_ren_proper_P.
  econstructor.
  all: unfold t_ren_proper_P, w_ren_proper_P, s_ren_proper_P.
  all: intros; cbn; f_equal; cbn; eauto.
  all: try now apply H.
  all: first [ apply H | apply H0 | apply H1 | apply H2 ]; auto.
  all: first [ apply r_shift1_eq | apply r_shift3_eq ]; auto.
Qed.

#[global] Instance t_ren_eq {Ξ“ a t Ξ”} : Proper (asgn_eq _ _ ==> eq) (t_rename Ξ“ a t Ξ”).
  intros f1 f2 H1; now apply (term_ind_mut _ _ _ ren_proper_prf).
Qed.

#[global] Instance w_ren_eq {Ξ“ a v Ξ”} : Proper (asgn_eq _ _ ==> eq) (w_rename Ξ“ a v Ξ”).
  intros f1 f2 H1; now apply (whn_ind_mut _ _ _ ren_proper_prf).
Qed.

#[global] Instance s_ren_eq {Ξ“ s Ξ”} : Proper (asgn_eq _ _ ==> eq) (s_rename Ξ“ s Ξ”).
  intros f1 f2 H1; now apply (state_ind_mut _ _ _ ren_proper_prf).
Qed.

#[global] Instance v_ren_eq {Ξ“ a v Ξ”} : Proper (asgn_eq _ _ ==> eq) (v_rename Ξ“ a v Ξ”).
  destruct a.
  now apply w_ren_eq.
  now apply t_ren_eq.
Qed.

#[global] Instance a_ren_eq {Ξ“1 Ξ“2 Ξ“3}
  : Proper (asgn_eq _ _ ==> asgn_eq _ _ ==> asgn_eq _ _) (@a_ren Ξ“1 Ξ“2 Ξ“3).
  intros r1 r2 H1 a1 a2 H2 ? i; cbn; now rewrite H1, (v_ren_eq _ _ H2).
Qed.

#[global] Instance a_shift1_eq {Ξ“ Ξ” A} : Proper (asgn_eq _ _ ==> asgn_eq _ _) (@a_shift1 Ξ“ Ξ” A).
  intros ? ? H ? h.
  dependent elimination h; auto; cbn; now rewrite H.
Qed.

#[global] Instance a_shift3_eq {Ξ“ Ξ” A B C}
  : Proper (asgn_eq _ _ ==> asgn_eq _ _) (@a_shift3 Ξ“ Ξ” A B C).
  intros ? ? H ? v.
  do 3 (dependent elimination v; auto).
  cbn; now rewrite H.
Qed.

Definition t_ren_ren_P Ξ“1 A (t : term Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2) .
Definition w_ren_ren_P Ξ“1 A (v : whn Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (v `α΅₯βŠ›α΅£ f1) `α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2) .
Definition s_ren_ren_P Ξ“1 (s : state Ξ“1) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (s β‚›βŠ›α΅£ f1) β‚›βŠ›α΅£ f2 = s β‚›βŠ›α΅£ (f1 α΅£βŠ› f2) .

Lemma ren_ren_prf : syn_ind_args t_ren_ren_P w_ren_ren_P s_ren_ren_P.
  econstructor.
  all: unfold t_ren_ren_P, w_ren_ren_P, s_ren_ren_P.
  all: intros; cbn; f_equal; eauto.
  all: first [ rewrite r_shift1_comp | rewrite r_shift3_comp ]; eauto.
Qed.

Lemma t_ren_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) A (t : term Ξ“1 A)
  : (t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2) .
  now apply (term_ind_mut _ _ _ ren_ren_prf).
Qed.
Lemma w_ren_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) A (v : whn Ξ“1 A)
  : (v `α΅₯βŠ›α΅£ f1) `α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2) .
  now apply (whn_ind_mut _ _ _ ren_ren_prf).
Qed.
Lemma s_ren_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) (s : state Ξ“1)
  : (s β‚›βŠ›α΅£ f1) β‚›βŠ›α΅£ f2 = s β‚›βŠ›α΅£ (f1 α΅£βŠ› f2) .
  now apply (state_ind_mut _ _ _ ren_ren_prf).
Qed.
Lemma v_ren_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) A (v : val Ξ“1 A)
  : (v α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = v α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2) .
  destruct A.
  now apply w_ren_ren.
  now apply t_ren_ren.
Qed.

Definition t_ren_id_l_P Ξ“ A (t : term Ξ“ A) : Prop := t β‚œβŠ›α΅£ r_id = t.
Definition w_ren_id_l_P Ξ“ A (v : whn Ξ“ A) : Prop := v `α΅₯βŠ›α΅£ r_id = v.
Definition s_ren_id_l_P Ξ“ (s : state Ξ“) : Prop := s β‚›βŠ›α΅£ r_id  = s.

Lemma ren_id_l_prf : syn_ind_args t_ren_id_l_P w_ren_id_l_P s_ren_id_l_P.
  econstructor.
  all: unfold t_ren_id_l_P, w_ren_id_l_P, s_ren_id_l_P.
  all: intros; cbn; f_equal; eauto.
  all: first [ rewrite r_shift1_id | rewrite r_shift3_id ]; eauto.
Qed.

Lemma t_ren_id_l {Ξ“} A (t : term Ξ“ A) : t β‚œβŠ›α΅£ r_id = t.
  now apply (term_ind_mut _ _ _ ren_id_l_prf).
Qed.
Lemma w_ren_id_l {Ξ“} A (v : whn Ξ“ A) : v `α΅₯βŠ›α΅£ r_id = v.
  now apply (whn_ind_mut _ _ _ ren_id_l_prf).
Qed.
Lemma s_ren_id_l {Ξ“} (s : state Ξ“) : s β‚›βŠ›α΅£ r_id  = s.
  now apply (state_ind_mut _ _ _ ren_id_l_prf).
Qed.
Lemma v_ren_id_l {Ξ“} A (v : val Ξ“ A) : v α΅₯βŠ›α΅£ r_id = v.
  destruct A.
  now apply w_ren_id_l.
  now apply t_ren_id_l.
Qed.

Lemma v_ren_id_r {Ξ“ Ξ”} (f : Ξ“ βŠ† Ξ”) A (i : Ξ“ βˆ‹ A) : (Var i) α΅₯βŠ›α΅£ f = Var (f _ i).
  now destruct A.
Qed.

Lemma a_shift1_id {Ξ“ A} : @a_shift1 Ξ“ Ξ“ A Var ≑ₐ Var.
  intros [ [] | [] ] i; dependent elimination i; auto.
Qed.

Lemma a_shift3_id {Ξ“ A B C} : @a_shift3 Ξ“ Ξ“ A B C Var ≑ₐ Var.
  intros ? v; cbn.
  do 3 (dependent elimination v; cbn; auto).
  now destruct a.
Qed.

Arguments Var : simpl never.
Lemma a_shift1_ren_r {Ξ“1 Ξ“2 Ξ“3 A} (f1 : Ξ“1 =[ val ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3)
      : a_shift1 (A:=A) (f1 βŠ›α΅£ f2) ≑ₐ a_shift1 f1 βŠ›α΅£ r_shift1 f2 .
  intros ? h; dependent elimination h; cbn.
  - now rewrite v_ren_id_r.
  - now unfold v_shift1; rewrite 2 v_ren_ren.
Qed.

Lemma a_shift3_ren_r {Ξ“1 Ξ“2 Ξ“3 A B C} (f1 : Ξ“1 =[ val ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3)
      : a_shift3 (A:=A) (B:=B) (C:=C) (f1 βŠ›α΅£ f2) ≑ₐ a_shift3 f1 βŠ›α΅£ r_shift3 f2 .
  intros ? v; do 3 (dependent elimination v; cbn; [ now rewrite v_ren_id_r | ]).
  unfold v_shift3; now rewrite 2 v_ren_ren.
Qed.

Lemma a_shift1_ren_l {Ξ“1 Ξ“2 Ξ“3 A} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3)
  : a_shift1 (A:=A) (f1 α΅£βŠ› f2) ≑ₐ r_shift1 f1 α΅£βŠ› a_shift1 f2 .
  intros ? i; dependent elimination i; auto.
Qed.

Lemma a_shift3_ren_l {Ξ“1 Ξ“2 Ξ“3 A B C} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3)
      : a_shift3 (A:=A) (B:=B) (C:=C) (f1 α΅£βŠ› f2) ≑ₐ r_shift3 f1 α΅£βŠ› a_shift3 f2 .
  intros ? v; do 3 (dependent elimination v; auto).
Qed.

Definition t_sub_proper_P Ξ“ A (t : term Ξ“ A) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ =[val]> Ξ”), f1 ≑ₐ f2 -> t `β‚œβŠ› f1 = t `β‚œβŠ› f2 .
Definition w_sub_proper_P Ξ“ A (v : whn Ξ“ A) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ =[val]> Ξ”), f1 ≑ₐ f2 -> v `α΅₯βŠ› f1 = v `α΅₯βŠ› f2 .
Definition s_sub_proper_P Ξ“ (s : state Ξ“) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ =[val]> Ξ”), f1 ≑ₐ f2 -> s β‚œβŠ› f1 = s β‚œβŠ› f2 .

Lemma sub_proper_prf : syn_ind_args t_sub_proper_P w_sub_proper_P s_sub_proper_P.
  econstructor.
  all: unfold t_sub_proper_P, w_sub_proper_P, s_sub_proper_P.
  all: intros; cbn; f_equal.
  all: first [ apply H | apply H0 | apply H1 | apply H2 ]; auto.
  all: first [ apply a_shift1_eq | apply a_shift3_eq ]; auto.
Qed.

#[global] Instance t_sub_eq {Ξ“ a t Ξ”} : Proper (asgn_eq _ _ ==> eq) (t_subst Ξ“ a t Ξ”).
  intros f1 f2 H1; now apply (term_ind_mut _ _ _ sub_proper_prf).
Qed.

#[global] Instance w_sub_eq {Ξ“ a v Ξ”} : Proper (asgn_eq _ _ ==> eq) (w_subst Ξ“ a v Ξ”).
  intros f1 f2 H1; now apply (whn_ind_mut _ _ _ sub_proper_prf).
Qed.

#[global] Instance s_sub_eq {Ξ“ s Ξ”} : Proper (asgn_eq _ _ ==> eq) (s_subst Ξ“ s Ξ”).
  intros f1 f2 H1; now apply (state_ind_mut _ _ _ sub_proper_prf).
Qed.

#[global] Instance v_sub_eq {Ξ“ a v Ξ”} : Proper (asgn_eq _ _ ==> eq) (v_subst Ξ“ a v Ξ”).
  destruct a.
  - now apply w_sub_eq.
  - now apply t_sub_eq.
Qed.

#[global] Instance a_comp_eq {Ξ“1 Ξ“2 Ξ“3}
  : Proper (asgn_eq _ _ ==> asgn_eq _ _ ==> asgn_eq _ _) (@a_comp _ _ _ _ _ Ξ“1 Ξ“2 Ξ“3).
  intros ? ? H1 ? ? H2 ? ?; cbn; rewrite H1; now eapply v_sub_eq.
Qed.

Definition t_ren_sub_P Ξ“1 A (t : term Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› (f1 βŠ›α΅£ f2) .
Definition w_ren_sub_P Ξ“1 A (v : whn Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (v `α΅₯βŠ› f1) `α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› (f1 βŠ›α΅£ f2) .
Definition s_ren_sub_P Ξ“1 (s : state Ξ“1) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (s β‚œβŠ› f1) β‚›βŠ›α΅£ f2 = s β‚œβŠ› (f1 βŠ›α΅£ f2) .
Lemma ren_sub_prf : syn_ind_args t_ren_sub_P w_ren_sub_P s_ren_sub_P.
  econstructor.
  all: unfold t_ren_sub_P, w_ren_sub_P, s_ren_sub_P.
  all: intros; cbn; f_equal; auto.
  all: first [ rewrite a_shift1_ren_r | rewrite a_shift3_ren_r ]; auto.
Qed.

Lemma t_ren_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) A (t : term Ξ“1 A)
  : (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› (f1 βŠ›α΅£ f2) .
  now apply (term_ind_mut _ _ _ ren_sub_prf).
Qed.
Lemma w_ren_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) A (v : whn Ξ“1 A)
  : (v `α΅₯βŠ› f1) `α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› (f1 βŠ›α΅£ f2) .
  now apply (whn_ind_mut _ _ _ ren_sub_prf).
Qed.
Lemma s_ren_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) (s : state Ξ“1)
  : (s β‚œβŠ› f1) β‚›βŠ›α΅£ f2 = s β‚œβŠ› (f1 βŠ›α΅£ f2) .
  now apply (state_ind_mut _ _ _ ren_sub_prf).
Qed.
Lemma v_ren_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) A (v : val Ξ“1 A)
  : (v α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v α΅₯βŠ› (f1 βŠ›α΅£ f2) .
  destruct A.
  - now apply w_ren_sub.
  - now apply t_ren_sub.
Qed.

Definition t_sub_ren_P Ξ“1 A (t : term Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3),
    (t β‚œβŠ›α΅£ f1) `β‚œβŠ› f2 = t `β‚œβŠ› (f1 α΅£βŠ› f2).
Definition w_sub_ren_P Ξ“1 A (v : whn Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3),
    (v `α΅₯βŠ›α΅£ f1) `α΅₯βŠ› f2 = v `α΅₯βŠ› (f1 α΅£βŠ› f2).
Definition s_sub_ren_P Ξ“1 (s : state Ξ“1) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3),
    (s β‚›βŠ›α΅£ f1) β‚œβŠ› f2 = s β‚œβŠ› (f1 α΅£βŠ› f2).

Lemma sub_ren_prf : syn_ind_args t_sub_ren_P w_sub_ren_P s_sub_ren_P.
  econstructor.
  all: unfold t_sub_ren_P, w_sub_ren_P, s_sub_ren_P.
  all: intros; cbn; f_equal; auto.
  all: first [ rewrite a_shift1_ren_l | rewrite a_shift3_ren_l ]; auto.
Qed.

Lemma t_sub_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) A (t : term Ξ“1 A)
  : (t β‚œβŠ›α΅£ f1) `β‚œβŠ› f2 = t `β‚œβŠ› (f1 α΅£βŠ› f2).
  now apply (term_ind_mut _ _ _ sub_ren_prf).
Qed.
Lemma w_sub_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) A (v : whn Ξ“1 A)
  : (v `α΅₯βŠ›α΅£ f1) `α΅₯βŠ› f2 = v `α΅₯βŠ› (f1 α΅£βŠ› f2).
  now apply (whn_ind_mut _ _ _ sub_ren_prf).
Qed.
Lemma s_sub_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) (s : state Ξ“1)
  : (s β‚›βŠ›α΅£ f1) β‚œβŠ› f2 = s β‚œβŠ› (f1 α΅£βŠ› f2).
  now apply (state_ind_mut _ _ _ sub_ren_prf).
Qed.
Lemma v_sub_ren {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) A (v : val Ξ“1 A)
  : (v α΅₯βŠ›α΅£ f1) α΅₯βŠ› f2 = v α΅₯βŠ› (f1 α΅£βŠ› f2).
  destruct A.
  - now apply w_sub_ren.
  - now apply t_sub_ren.
Qed.

Lemma v_sub_id_r {Ξ“ Ξ”} (f : Ξ“ =[val]> Ξ”) A (i : Ξ“ βˆ‹ A) : Var i α΅₯βŠ› f = f A i.
  now destruct A.
Qed.

Lemma a_shift1_comp {Ξ“1 Ξ“2 Ξ“3 A} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3)
  : @a_shift1 _ _ A (f1 βŠ› f2) ≑ₐ a_shift1 f1 βŠ› a_shift1 f2 .
  intros x i; dependent elimination i; cbn.
  - now rewrite v_sub_id_r.
  - now unfold v_shift1; rewrite v_ren_sub, v_sub_ren.
Qed.

Lemma a_shift3_comp {Ξ“1 Ξ“2 Ξ“3 A B C} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3)
  : @a_shift3 _ _ A B C (f1 βŠ› f2) ≑ₐ a_shift3 f1 βŠ› a_shift3 f2 .
  intros ? v; do 3 (dependent elimination v; cbn; [ now rewrite v_sub_id_r | ]).
  now unfold v_shift3; rewrite v_ren_sub, v_sub_ren.
Qed.

Definition t_sub_sub_P Ξ“1 A (t : term Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3),
    (t `β‚œβŠ› f1) `β‚œβŠ› f2 = t `β‚œβŠ› (f1 βŠ› f2) .
Definition w_sub_sub_P Ξ“1 A (v : whn Ξ“1 A) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3),
    (v `α΅₯βŠ› f1) `α΅₯βŠ› f2 = v `α΅₯βŠ› (f1 βŠ› f2) .
Definition s_sub_sub_P Ξ“1 (s : state Ξ“1) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3),
    (s β‚œβŠ› f1) β‚œβŠ› f2 = s β‚œβŠ› (f1 βŠ› f2) .

Lemma sub_sub_prf : syn_ind_args t_sub_sub_P w_sub_sub_P s_sub_sub_P.
  econstructor.
  all: unfold t_sub_sub_P, w_sub_sub_P, s_sub_sub_P.
  all: intros; cbn; f_equal; auto.
  all: first [ rewrite a_shift1_comp | rewrite a_shift3_comp ]; auto.
Qed.

Lemma t_sub_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) A (t : term Ξ“1 A)
  : (t `β‚œβŠ› f1) `β‚œβŠ› f2 = t `β‚œβŠ› (f1 βŠ› f2) .
  now apply (term_ind_mut _ _ _ sub_sub_prf).
Qed.
Lemma w_sub_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) A (v : whn Ξ“1 A)
  : (v `α΅₯βŠ› f1) `α΅₯βŠ› f2 = v `α΅₯βŠ› (f1 βŠ› f2) .
  now apply (whn_ind_mut _ _ _ sub_sub_prf).
Qed.
Lemma s_sub_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) (s : state Ξ“1)
  : (s β‚œβŠ› f1) β‚œβŠ› f2 = s β‚œβŠ› (f1 βŠ› f2) .
  now apply (state_ind_mut _ _ _ sub_sub_prf).
Qed.
Lemma v_sub_sub {Ξ“1 Ξ“2 Ξ“3} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3) A (v : val Ξ“1 A)
  : (v α΅₯βŠ› f1) α΅₯βŠ› f2 = v α΅₯βŠ› (f1 βŠ› f2) .
  destruct A.
  - now apply w_sub_sub.
  - now apply t_sub_sub.
Qed.

Lemma a_comp_assoc {Ξ“1 Ξ“2 Ξ“3 Ξ“4} (u : Ξ“1 =[val]> Ξ“2) (v : Ξ“2 =[val]> Ξ“3) (w : Ξ“3 =[val]> Ξ“4)
           : (u βŠ› v) βŠ› w ≑ₐ u βŠ› (v βŠ› w).
  intros ? i; unfold a_comp; now apply v_sub_sub.
Qed.

Definition t_sub_id_l_P Ξ“ A (t : term Ξ“ A) : Prop := t `β‚œβŠ› Var = t.
Definition w_sub_id_l_P Ξ“ A (v : whn Ξ“ A) : Prop := v `α΅₯βŠ› Var = v.
Definition s_sub_id_l_P Ξ“ (s : state Ξ“) : Prop := s β‚œβŠ› Var = s.

Lemma sub_id_l_prf : syn_ind_args t_sub_id_l_P w_sub_id_l_P s_sub_id_l_P.
  econstructor.
  all: unfold t_sub_id_l_P, w_sub_id_l_P, s_sub_id_l_P.
  all: intros; cbn; f_equal; auto.
  all: first [ rewrite a_shift1_id | rewrite a_shift3_id ]; auto.
Qed.

Lemma t_sub_id_l {Ξ“} A (t : term Ξ“ A) : t `β‚œβŠ› Var = t.
  now apply (term_ind_mut _ _ _ sub_id_l_prf).
Qed.
Lemma w_sub_id_l {Ξ“} A (v : whn Ξ“ A) : v `α΅₯βŠ› Var = v.
  now apply (whn_ind_mut _ _ _ sub_id_l_prf).
Qed.
Lemma s_sub_id_l {Ξ“} (s : state Ξ“) : s β‚œβŠ› Var = s.
  now apply (state_ind_mut _ _ _ sub_id_l_prf).
Qed.
Lemma v_sub_id_l {Ξ“} A (v : val Ξ“ A) : v α΅₯βŠ› Var = v.
  destruct A.
  - now apply w_sub_id_l.
  - now apply t_sub_id_l.
Qed.

Lemma sub1_sub {Ξ“ Ξ” A} (f : Ξ“ =[val]> Ξ”) (v : val Ξ“ A) :
  a_shift1 f βŠ› asgn1 (v α΅₯βŠ› f) ≑ₐ asgn1 v βŠ› f.
  intros ? h; dependent elimination h; cbn.
  - now rewrite v_sub_id_r.
  - unfold v_shift1; rewrite v_sub_ren, v_sub_id_r.
    now apply v_sub_id_l.
Qed.

Lemma sub1_ren {Ξ“ Ξ” A} (f : Ξ“ βŠ† Ξ”) (v : val Ξ“ A) :
  r_shift1 f α΅£βŠ› asgn1 (v α΅₯βŠ›α΅£ f) ≑ₐ asgn1 v βŠ›α΅£ f.
  intros ? h; dependent elimination h; auto.
  cbn; now rewrite v_ren_id_r.
Qed.

Lemma v_sub1_sub {Ξ“ Ξ” A B} (f : Ξ“ =[val]> Ξ”) (v : val Ξ“ A) (w : val (Ξ“ β–Άβ‚“ A) B)
  : (w α΅₯βŠ› a_shift1 f) α΅₯βŠ› ₁[ v α΅₯βŠ› f ] = (w α΅₯βŠ› ₁[ v ]) α΅₯βŠ› f .
  cbn; rewrite 2 v_sub_sub.
  apply v_sub_eq; now rewrite sub1_sub.
Qed.

Lemma v_sub1_ren {Ξ“ Ξ” A B} (f : Ξ“ βŠ† Ξ”) (v : val Ξ“ A) (w : val (Ξ“ β–Άβ‚“ A) B)
  : (w α΅₯βŠ›α΅£ r_shift1 f) α΅₯βŠ› ₁[ v α΅₯βŠ›α΅£ f ] = (w α΅₯βŠ› ₁[ v ]) α΅₯βŠ›α΅£ f .
  cbn; rewrite v_sub_ren, v_ren_sub.
  apply v_sub_eq; now rewrite sub1_ren.
Qed.

Lemma s_sub1_sub {Ξ“ Ξ” A} (f : Ξ“ =[val]> Ξ”) (v : val Ξ“ A) (s : state (Ξ“ β–Άβ‚“ A))
  : (s β‚œβŠ› a_shift1 f) β‚œβŠ› ₁[ v α΅₯βŠ› f ] = (s β‚œβŠ› ₁[ v ]) β‚œβŠ› f .
  cbn; now rewrite 2 s_sub_sub, sub1_sub.
Qed.

Lemma s_sub3_sub {Ξ“ Ξ” A B C} (f : Ξ“ =[val]> Ξ”) (s : state (Ξ“ β–Άβ‚“ A β–Άβ‚“ B β–Άβ‚“ C)) u v w
  : (s β‚œβŠ› a_shift3 f) β‚œβŠ› ₃[ u α΅₯βŠ› f , v α΅₯βŠ› f , w α΅₯βŠ› f ] = (s β‚œβŠ› ₃[ u, v , w ]) β‚œβŠ› f .
  cbn; rewrite 2 s_sub_sub; apply s_sub_eq.
  intros ? v0; cbn.
  do 3 (dependent elimination v0; cbn; [ now rewrite v_sub_id_r | ]).
  unfold v_shift3; rewrite v_sub_ren, v_sub_id_r, <- v_sub_id_l.
  now apply v_sub_eq.
Qed.

Lemma s_sub1_ren {Ξ“ Ξ” A} (f : Ξ“ βŠ† Ξ”) (v : val Ξ“ A) (s : state (Ξ“ β–Άβ‚“ A))
  : (s β‚›βŠ›α΅£ r_shift1 f) β‚œβŠ› ₁[ v α΅₯βŠ›α΅£ f ] = (s β‚œβŠ› ₁[ v ]) β‚›βŠ›α΅£ f .
  cbn; now rewrite s_sub_ren, s_ren_sub, sub1_ren.
Qed.

Lemma t_sub1_sub {Ξ“ Ξ” A B} (f : Ξ“ =[val]> Ξ”) (v : val Ξ“ A) (t : term (Ξ“ β–Άβ‚“ A) B)
  : (t `β‚œβŠ› a_shift1 f) `β‚œβŠ› ₁[ v α΅₯βŠ› f ] = (t `β‚œβŠ› ₁[ v ]) `β‚œβŠ› f .
  cbn; rewrite 2 t_sub_sub.
  apply t_sub_eq; now rewrite sub1_sub.
Qed.

Lemma t_sub1_ren {Ξ“ Ξ” A B} (f : Ξ“ βŠ† Ξ”) (v : val Ξ“ A) (t : term (Ξ“ β–Άβ‚“ A) B)
  : (t β‚œβŠ›α΅£ r_shift1 f) `β‚œβŠ› ₁[ v α΅₯βŠ›α΅£ f ] = (t `β‚œβŠ› ₁[ v ]) β‚œβŠ›α΅£ f .
  cbn; rewrite t_sub_ren, t_ren_sub.
  apply t_sub_eq; now rewrite sub1_ren.
Qed.

#[global] Instance p_app_eq {Ξ“ A} (v : val Ξ“ A) (m : pat (t_neg A))
  : Proper (asgn_eq _ _ ==> eq) (p_app v m) .
  intros u1 u2 H; destruct A; cbn.
  now rewrite (t_sub_eq u1 u2 H).
  now rewrite (w_sub_eq u1 u2 H).
Qed.

Lemma refold_id_aux {Ξ“ : neg_ctx} A (v : whn Ξ“ A)
  : (p_of_w _ v : val _ _) `α΅₯βŠ› p_dom_of_w _ v = v .
  cbn; funelim (p_of_w A v); auto.
  - destruct (s_prf i).
  - destruct (s_prf i).
  - now cbn; f_equal. 
  - now cbn; f_equal. 
Qed.

Equations p_of_w_eq {Ξ“ : neg_ctx} A (p : pat ↑A) (e : pat_dom p =[val]> Ξ“)
          : p_of_w A ((p : val _ _) `α΅₯βŠ› e) = p :=
  p_of_w_eq (a `+ b) (PInl v) e := f_equal PInl (p_of_w_eq _ v e) ;
  p_of_w_eq (a `+ b) (PInr v) e := f_equal PInr (p_of_w_eq _ v e) ;
  p_of_w_eq (`1)     PTt      e := eq_refl ;
  p_of_w_eq (a `Γ— b) PPair    e := eq_refl ;
  p_of_w_eq (a `β†’ b) PLam     e := eq_refl .

Lemma p_dom_of_w_eq {Ξ“ : neg_ctx} A (p : pat ↑A) (e : pat_dom p =[val]> Ξ“)
      : rew [fun p => pat_dom p =[ val ]> Ξ“] p_of_w_eq A p e
        in p_dom_of_w A ((p : val _ _) `α΅₯βŠ› e)
      ≑ₐ e .
  funelim (p_of_w_eq A p e); cbn.
  - intros ? v; repeat (dependent elimination v; auto).
  - intros ? v; repeat (dependent elimination v; auto).
  - match goal with | |- ?s ≑ₐ e => pose (xx := s); change (_ ≑ₐ e) with (xx ≑ₐ e) end .
    remember xx as xx'; unfold xx in Heqxx'; clear xx.
    now rewrite (eq_trans Heqxx' (eq_sym (rew_map _ PInl _ _))).
  - match goal with | |- ?s ≑ₐ e => pose (xx := s); change (_ ≑ₐ e) with (xx ≑ₐ e) end .
    remember xx as xx'; unfold xx in Heqxx'; clear xx.
    now rewrite (eq_trans Heqxx' (eq_sym (rew_map _ PInr _ _))).
  - intros ? v; repeat (dependent elimination v; auto).
Qed.

Notation val_n := (val ∘ neg_c_coe).
Notation state_n := (state ∘ neg_c_coe).

#[global] Instance val_n_monoid : subst_monoid val_n .
  esplit.
  - intros Ξ“ x i; exact (Var i).
  - intros Ξ“ x v Ξ” f; exact (v α΅₯βŠ› f).
Defined.

#[global] Instance state_n_module : subst_module val_n state_n .
  esplit; intros Ξ“ s Ξ” f; exact (s β‚œβŠ› (f : Ξ“ =[val]> Ξ”)).
Defined.

#[global] Instance val_n_laws : subst_monoid_laws val_n .
  esplit.
  - intros ???? <- ????; now apply v_sub_eq.
  - intros ?????; now apply v_sub_id_r.
  - intros ???; now apply v_sub_id_l.
  - intros ???????; symmetry; now apply v_sub_sub.
Qed.

#[global] Instance state_n_laws : subst_module_laws val_n state_n .
  esplit.
  - intros ??? <- ????; now apply s_sub_eq.
  - intros ??; now apply s_sub_id_l.
  - intros ??????; symmetry; now apply s_sub_sub.
Qed.

#[global] Instance var_laws : var_assumptions val_n.
  esplit.
  - intros ? [] ?? H; now dependent destruction H.
  - intros ? [] v; dependent elimination v.
    all: try exact (Yes _ (Vvar _)).
    all: apply No; intro H; dependent destruction H.
  - intros ?? [] ???; cbn in v; dependent induction v.
    all: try now dependent destruction X; exact (Vvar _).
    all: dependent induction w; dependent destruction X; exact (Vvar _).
Qed.

#[global] Instance sysl_machine : machine val_n state_n op_copat :=
  {| Machine.eval := @eval ; oapp := @p_app |} .

From Coinduction Require Import coinduction lattice rel tactics.

Ltac refold_eval :=
  change (Structure.iter _ _ ?a) with (eval a);
  change (Structure.subst (fun pat : T1 => let 'T1_0 := pat in ?f) T1_0 ?u)
    with (bind_delay' u f).

Definition upg_v {Ξ“} {A : pre_ty} : whn Ξ“ A  -> val Ξ“ ↑A := fun v => v.
Definition upg_k {Ξ“} {A : pre_ty} : term Ξ“ Β¬A -> val Ξ“ Β¬A := fun v => v.
Definition dwn_v {Ξ“} {A : pre_ty} : val Ξ“ ↑A -> whn Ξ“ A  := fun v => v.
Definition dwn_k {Ξ“} {A : pre_ty} : val Ξ“ Β¬A -> term Ξ“ Β¬A := fun v => v.

Lemma nf_eq_split {Ξ“ : neg_ctx} {A : pre_ty} (i : Ξ“ βˆ‹ Β¬A) (p : pat ↑A) Ξ³
  : nf_eq (i β‹… w_split _ (dwn_v ((p : val _ _) `α΅₯βŠ› Ξ³)))
          (i β‹… (p : o_op op_copat Β¬A) ⦇ Ξ³ ⦈).
  unfold w_split, dwn_v; cbn.
  pose proof (p_dom_of_w_eq A p Ξ³).
  pose (H' := p_of_w_eq A p Ξ³); fold H' in H.
  pose (a := p_dom_of_w A (v_of_p p `α΅₯βŠ› Ξ³)); fold a in H |- *.
  remember a as a'; clear a Heqa'.
  revert a' H; rewrite H'; intros; now econstructor.
Qed.

#[global] Instance machine_law : machine_laws val_n state_n op_copat.
  esplit.
  - intros; apply p_app_eq.
  - intros ?? [] ????; cbn.
    now rewrite (t_sub_sub _ _ _ _).
    now rewrite (w_sub_sub _ _ _ _).
  - cbn; intros Ξ“ Ξ”; unfold comp_eq, it_eq; coinduction R CIH; intros c a.
    cbn; funelim (eval_aux c); try now destruct (s_prf i).
    + change (it_eqF _ ?RX ?RY _ _ _) with
        (it_eq_map βˆ…β‚‘ RX RY T1_0
           (eval (Cut (Val (v `α΅₯βŠ› a)) (a _ i)))
           (eval (Cut (Val ((v_of_p (p_of_w _ v) `α΅₯βŠ› p_dom_of_w _ v `α΅₯βŠ› a))) (a _ i)))).
      now rewrite (refold_id_aux _ v).
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_v v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_v v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_v v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + now change (it_eqF _ ?RX ?RY _ _ _) with
        (it_eq_map βˆ…β‚‘ RX RY T1_0
           (eval (Cut (a _ i) (Fst k `β‚œβŠ› a)))
           (eval (Cut (a _ i) (Fst k `β‚œβŠ› a)))).
    + cbn; econstructor; refold_eval.
      change (?v `β‚œβŠ› ?a) with (upg_k v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + now change (it_eqF _ ?RX ?RY _ _ _) with
        (it_eq_map βˆ…β‚‘ RX RY T1_0
           (eval (Cut (a _ i) (Snd k `β‚œβŠ› a)))
           (eval (Cut (a _ i) (Snd k `β‚œβŠ› a)))).
    + cbn; econstructor; refold_eval.
      change (?v `β‚œβŠ› ?a) with (upg_k v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + simp eval_aux.
      unfold p_app, app_nf, nf_var, nf_obs, nf_args, cut_l, cut_r, fill_op, fill_args.
      change (it_eqF _ ?RX ?RY _ _ _) with
        (it_eq_map βˆ…β‚‘ RX RY T1_0
           (eval (Cut (a _ i) (App v k `β‚œβŠ› a)))
           (eval (Cut (a _ i) (PApp (p_of_w A7 v) `β‚œβŠ› [ p_dom_of_w _ v ,β‚“ upg_k k ] `β‚œβŠ› a)))).
      cbn - [ it_eq_map ].
      rewrite w_sub_ren.
      change (r_pop α΅£βŠ› (p_dom_of_w _ v ▢ₐ _))%asgn with (p_dom_of_w _ v).
      now rewrite (refold_id_aux _ v).
    + cbn; econstructor; refold_eval.
      change (Lam (s_subst _ _ _ _)) with (upg_v (Lam s) α΅₯βŠ› a).
      change (?v `β‚œβŠ› ?a) with (upg_k v α΅₯βŠ› a).
      change (?v `α΅₯βŠ› ?a) with (upg_v v α΅₯βŠ› a).
      rewrite s_sub3_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `β‚œβŠ› ?a) with (upg_k v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
  - cbn; intros ? [ A i [ o Ξ³ ]]; cbn; unfold p_app, nf_args, cut_r, fill_args.
    cbn in o; funelim (v_of_p o); simpl_depind; inversion eqargs.
    all: match goal with
         | H : _ = ?A† |- _ => destruct A; dependent destruction H
         end.
    all: dependent destruction eqargs; cbn.
    all: apply it_eq_unstep; cbn; unfold Var; cbn; econstructor.
    1-2,5-7: econstructor; intros ? v; repeat (dependent elimination v; auto).
    1-2: exact (nf_eq_split _ _ Ξ³).
    (* a bit of an ugly case because we didn't write proper generic functions
       for splitting negative values.. hence knowing this one is an App is
       getting in our way *)
    clear; unfold app_nf.
    rewrite w_sub_ren.
    pose (Ξ³' := (r_pop α΅£βŠ› Ξ³)%asgn);
      change (sub_elt _) with (pat_dom v : t_ctx) in Ξ³' at 1; cbn in Ξ³'.
    change (r_pop α΅£βŠ› Ξ³)%asgn with Ξ³'.
    pose (vv := Ξ³ _ Ctx.top); cbn in vv; change (Ξ³ _ Ctx.top) with vv.
    assert (H : [ Ξ³' ,β‚“ upg_k vv ] ≑ₐ Ξ³) by now intros ? ii; dependent elimination ii.
    remember Ξ³' as a; remember vv as t; clear Ξ³' vv Heqa Heqt.
    pose proof (p_dom_of_w_eq _ v a).
    pose (H' := p_of_w_eq _ v a); fold H' in H0.
    pose (aa := p_dom_of_w _ ((v : val _ _) `α΅₯βŠ› a)); fold aa in H0 |- *.
    remember aa as a'; clear aa Heqa'.
    revert a' H0; rewrite H'; intros; econstructor.
    etransitivity; [ | exact H ].
    refine (a_append_eq _ _ _ _ _ _); auto.
  - intros A; econstructor; intros [ B m ] H; dependent elimination H;
      cbn [projT1 projT2] in i, i0.
    destruct y.
    all: dependent elimination v; try now destruct (t0 (Vvar _)).
    all: clear t0.
    all: cbn in o; dependent elimination o; cbn in i0.
    all: match goal with
         | u : dom _ =[val_n]> _ |- _ =>
             cbn in i0;
             pose (vv := u _ Ctx.top); change (u _ Ctx.top) with vv in i0;
             remember vv as v'; clear u vv Heqv'; cbn in v'
         | _ => idtac
       end.
    1-10: now apply it_eq_step in i0; inversion i0.
    all: dependent elimination v'; [ | apply it_eq_step in i0; now inversion i0 ].
    all:
      apply it_eq_step in i0; cbn in i0; dependent elimination i0; cbn in r_rel;
      apply noConfusion_inv in r_rel; unfold w_split in r_rel;
      cbn in r_rel; unfold NoConfusionHom_f_cut,s_var_upg in r_rel; cbn in r_rel;
      pose proof (H := f_equal pr1 r_rel); cbn in H; dependent destruction H;
      apply DepElim.pr2_uip in r_rel;
      pose proof (H := f_equal pr1 r_rel); cbn in H; dependent destruction H;
      apply DepElim.pr2_uip in r_rel; dependent destruction r_rel.
    all:
      econstructor; intros [ t o ] H; cbn in t,o; dependent elimination H.
    all: dependent elimination v; try now destruct (t0 (Vvar _)).
    all: apply it_eq_step in i0; cbn in i0; now inversion i0.
Qed.

Definition subst_eq (Ξ” : neg_ctx) {Ξ“} : relation (state Ξ“) :=
  fun u v => forall (Οƒ : Ξ“ =[val]> Ξ”), evalβ‚’ (u β‚œβŠ› Οƒ : state_n Ξ”) β‰ˆ evalβ‚’ (v β‚œβŠ› Οƒ : state_n Ξ”) .
Notation "x β‰ˆβŸ¦sub Ξ” βŸ§β‰ˆ y" := (subst_eq Ξ” x y) (at level 50).

Theorem subst_correct (Ξ” : neg_ctx) {Ξ“ : neg_ctx} (x y : state Ξ“)
  : x β‰ˆβŸ¦ogs Ξ” βŸ§β‰ˆ y -> x β‰ˆβŸ¦sub Ξ” βŸ§β‰ˆ y.
  exact (ogs_correction _ x y).
Qed.

Definition c_of_t {Ξ“ : neg_ctx} {A} (t : term Ξ“ ↑A)
           : state_n (Ξ“ β–Άβ‚› {| sub_elt := Β¬A ; sub_prf := stt |}) :=
  Cut (t_shift1 _ t) (VarR Ctx.top) .
Notation "'name⁺'" := c_of_t.

Definition a_of_sk {Ξ“ Ξ” : neg_ctx} {A} (s : Ξ“ =[val]> Ξ”) (k : term Ξ” Β¬A)
  : (Ξ“ β–Άβ‚› {| sub_elt := Β¬A ; sub_prf := stt |}) =[val_n]> Ξ” :=
  [ s ,β‚“ k : val _ Β¬_ ].

Lemma sub_csk {Ξ“ Ξ” : neg_ctx} {A} (t : term Ξ“ ↑A) (s : Ξ“ =[val]> Ξ”)
  (k : term Ξ” Β¬A)
  : Cut (t `β‚œβŠ› s) k = c_of_t t β‚œβŠ› a_of_sk s k.
Proof.
  cbn; f_equal; unfold t_shift1; rewrite t_sub_ren; now apply t_sub_eq.
Qed.

Definition ciu_eq (Ξ” : neg_ctx) {Ξ“ A} : relation (term Ξ“ ↑A) :=
  fun u v =>
    forall (Οƒ : Ξ“ =[val]> Ξ”) (k : term Ξ” Β¬A),
      evalβ‚’ (Cut (u `β‚œβŠ› Οƒ) k : state_n Ξ”) β‰ˆ evalβ‚’ (Cut (v `β‚œβŠ› Οƒ) k : state_n Ξ”) .
Notation "x β‰ˆβŸ¦ciu Ξ” βŸ§βΊβ‰ˆ y" := (ciu_eq Ξ” x y) (at level 50).

Theorem ciu_correct (Ξ” : neg_ctx) {Ξ“ : neg_ctx} {A} (x y : term Ξ“ ↑A)
  : (name⁺ x) β‰ˆβŸ¦ogs Ξ” βŸ§β‰ˆ (name⁺ y) -> x β‰ˆβŸ¦ciu Ξ” βŸ§βΊβ‰ˆ y.
  intros H Οƒ k; rewrite 2 sub_csk.
  now apply subst_correct.
Qed.