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. 

In this file we instanciate our OGS construction with the fully-dual polarized mu-mu-tilde calculus 'System D' from P. Downen & Z. Ariola. The presentation may be slightly unusual as we go for one-sided sequent. The only real divergence from the original calculus is the addition of a restricted form of recursion, making the language non-normalizing.

Types

Type have polarities, basically whether their values are CBV-biased or CBN-biased

Variant pol : Type := pos | neg .

Syntax of types. We have a fully dual type system with:

  • 0, positive void (no constructor),
  • ⊀, negative unit (no destructor),
  • βŠ₯, negative void (one trivial destructor),
  • 1, positive unit (one trivial constructor),
  • A βŠ— B, positive product (pairs with pattern-matching),
  • A β…‹ B, negative sum (one binary destructor),
  • A βŠ• B, positive sum (usual coproduct with injections),
  • A & B, negative product (usual product with projections),
  • ↓ A, positive shift (thunking),
  • ↑ A, negative shift ('returners'),
  • βŠ– A, positive negation (approximately continuations accepting an A),
  • Β¬ A, negative negation (approximately refutations of A).

We opt for an explicit treatment of polarity, by indexing the family of types.

Inductive pre_ty : pol -> Type :=
| Zer : pre_ty pos
| Top : pre_ty neg
| One : pre_ty pos
| Bot : pre_ty neg
| Tens : pre_ty pos -> pre_ty pos -> pre_ty pos
| Par : pre_ty neg -> pre_ty neg -> pre_ty neg
| Or : pre_ty pos -> pre_ty pos -> pre_ty pos
| And : pre_ty neg -> pre_ty neg -> pre_ty neg
| ShiftP : pre_ty neg -> pre_ty pos
| ShiftN : pre_ty pos -> pre_ty neg
| NegP : pre_ty neg -> pre_ty pos
| NegN : pre_ty pos -> pre_ty neg
.
Notation "0" := (Zer) : ty_scope .
Notation "1" := (One) : ty_scope .
Notation "⊀" := (Top) : ty_scope .
Notation "βŠ₯" := (Bot) : ty_scope .
Notation "A βŠ— B" := (Tens A B) (at level 40) : ty_scope.
Notation "A β…‹ B" := (Par A B) (at level 40) : ty_scope .
Notation "A βŠ• B" := (Or A B) (at level 40) : ty_scope.
Notation "A & B" := (And A B) (at level 40) : ty_scope.
Notation "↓ A" := (ShiftP A) (at level 40) : ty_scope.
Notation "↑ A" := (ShiftN A) (at level 40) : ty_scope.
Notation "βŠ– A" := (NegP A) (at level 5) : ty_scope .
Notation "Β¬ A" := (NegN A) (at level 5) : ty_scope .

As hinted above, we go for one-sided sequents. This enables to have only one context instead of two, simplifying the theory of substitution. On the flip-side, as we still need two kinds of variables, the 'normal' ones and the co-variables, our contexts will not contain bare types but side-annotated types. A variable of type A will thus be stored as \`-A in the context while a co-variable of type A will be stored as \`-A.

Variant ty : Type :=
| LTy {p} : pre_ty p -> ty
| RTy {p} : pre_ty p -> ty
.
Notation "'`+' t" := (LTy t) (at level 5) : ty_scope .
Notation "'`-' t" := (RTy t) (at level 5) : 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.

Finally we define contexts as backward lists of side-annotated types.

The reference Ctx.ctx was not found in the current environment.

Terms

We define the well-typed syntax of the language with 3 mutually defined syntactic categories: terms, weak head-normal forms and states ('language configurations' in the paper).

Nothing should be too surprising. A first notable point is that by choosing to have an explicit notion of 'side' of a variable, we can have a single construct for both mu and mu-tilde. A second notable point is our new slightly exotic RecL and RecR constructions. They allow arbitrary recursion at co-terms of positive types and at terms of negative types. These polarity restrictions allow us to have minimal disruption of the evaluation rules.

Inductive term : t_ctx -> ty -> Type :=
| Mu {Ξ“ A} : state (Ξ“ β–Άβ‚“ A†) -> term Ξ“ A
| RecL {Ξ“} {A : pre_ty pos} : term (Ξ“ β–Άβ‚“ `-A) `-A -> term Ξ“ `-A
| RecR {Ξ“} {A : pre_ty neg} : term (Ξ“ β–Άβ‚“ `+A) `+A -> term Ξ“ `+A
| Whn {Ξ“ A} : whn Ξ“ A -> term Ξ“ A
with whn : t_ctx -> ty -> Type :=
| Var {Ξ“ A} : Ξ“ βˆ‹ A -> whn Ξ“ A
| ZerL {Ξ“} : whn Ξ“ `-0
| TopR {Ξ“} : whn Ξ“ `+⊀
| OneR {Ξ“} : whn Ξ“ `+1
| OneL {Ξ“} : state Ξ“ -> whn Ξ“ `-1
| BotR {Ξ“} : state Ξ“ -> whn Ξ“ `+βŠ₯
| BotL {Ξ“} : whn Ξ“ `-βŠ₯
| TenR {Ξ“ A B} : whn Ξ“ `+A -> whn Ξ“ `+B -> whn Ξ“ `+(A βŠ— B)
| TenL {Ξ“ A B} : state (Ξ“ β–Άβ‚“ `+A β–Άβ‚“ `+B) -> whn Ξ“ `-(A βŠ— B)
| ParR {Ξ“ A B} : state (Ξ“ β–Άβ‚“ `-A β–Άβ‚“ `-B) -> whn Ξ“ `+(A β…‹ B)
| ParL {Ξ“ A B} : whn Ξ“ `-A -> whn Ξ“ `-B -> whn Ξ“ `-(A β…‹ B)
| OrR1 {Ξ“ A B} : whn Ξ“ `+A -> whn Ξ“ `+(A βŠ• B)
| OrR2 {Ξ“ A B} : whn Ξ“ `+B -> whn Ξ“ `+(A βŠ• B)
| OrL {Ξ“ A B} : state (Ξ“ β–Άβ‚“ `+A) -> state (Ξ“ β–Άβ‚“ `+B) -> whn Ξ“ `-(A βŠ• B)
| AndR {Ξ“ A B} : state (Ξ“ β–Άβ‚“ `-A) -> state (Ξ“ β–Άβ‚“ `-B) -> whn Ξ“ `+(A & B)
| AndL1 {Ξ“ A B} : whn Ξ“ `-A -> whn Ξ“ `-(A & B)
| AndL2 {Ξ“ A B} : whn Ξ“ `-B -> whn Ξ“ `-(A & B)
| ShiftPR {Ξ“ A} : term Ξ“ `+A -> whn Ξ“ `+(↓ A)
| ShiftPL {Ξ“ A} : state (Ξ“ β–Άβ‚“ `+A) -> whn Ξ“ `-(↓ A)
| ShiftNR {Ξ“ A} : state (Ξ“ β–Άβ‚“ `-A) -> whn Ξ“ `+(↑ A)
| ShiftNL {Ξ“ A} : term Ξ“ `-A -> whn Ξ“ `-(↑ A)
| NegPR {Ξ“ A} : whn Ξ“ `-A -> whn Ξ“ `+(βŠ– A)
| NegPL {Ξ“ A} : state (Ξ“ β–Άβ‚“ `-A) -> whn Ξ“ `-(βŠ– A)
| NegNR {Ξ“ A} : state (Ξ“ β–Άβ‚“ `+A) -> whn Ξ“ `+(Β¬ A)
| NegNL {Ξ“ A} : whn Ξ“ `+A -> whn Ξ“ `-(Β¬ A)
with state : t_ctx -> Type :=
| Cut {Ξ“} p {A : pre_ty p} : term Ξ“ `+A -> term Ξ“ `-A -> state Ξ“
.

Definition Cut' {Ξ“ A} : term Ξ“ A -> term Ξ“ A† -> state Ξ“ :=
  match A with
  | `+A => fun t1 t2 => Cut _ t1 t2
  | `-A => fun t1 t2 => Cut _ t2 t1
  end .

Values are not exactly weak head-normal forms, but depend on the polarity of the type. As positive types have CBV evaluation, their values are weak head-normal forms, but their co-values (evaluation contexts) are just any co-term (context) as they are delayed anyways. Dually for negative types, values are delayed hence can be any term while co-values must be weak head-normal form contexts.

Equations val : t_ctx -> ty -> Type :=
  val Ξ“ (@LTy pos A) := whn Ξ“ `+A ;
  val Ξ“ (@RTy pos A) := term Ξ“ `-A ;
  val Ξ“ (@LTy neg A) := term Ξ“ `+A ;
  val Ξ“ (@RTy neg A) := whn Ξ“ `-A .
Arguments val _ _ /.

We provide a 'smart-constructor' for variables, embedding variables in values.

Equations var : c_var ⇒₁ val :=
  var _ (@LTy pos _) i := Var i ;
  var _ (@RTy pos _) i := Whn (Var i) ;
  var _ (@LTy neg _) i := Whn (Var i) ;
  var _ (@RTy neg _) i := Var i .
#[global] Arguments var {Ξ“} [x] / i.

Renaming

Without surprise parallel renaming goes by a big mutual induction, shifting the renaming apropriately while going under binders. Note the use of the internal substitution hom to type it.

Equations t_rename : term ⇒₁ ⟦ c_var , term βŸ§β‚ :=
  t_rename _ _ (Mu c)    _ f := Mu (s_rename _ c _ (r_shift1 f)) ;
  t_rename _ _ (RecL t)  _ f := RecL (t_rename _ _ t _ (r_shift1 f)) ;
  t_rename _ _ (RecR t)  _ f := RecR (t_rename _ _ t _ (r_shift1 f)) ;
  t_rename _ _ (Whn v)   _ f := Whn (w_rename _ _ v _ f) ;
with w_rename : whn ⇒₁ ⟦ c_var , whn βŸ§β‚ :=
  w_rename _  _ (Var i)       _ f := Var (f _ i) ;
  w_rename _  _ (ZerL)        _ f := ZerL ;
  w_rename _  _ (TopR)        _ f := TopR ;
  w_rename _  _ (OneR)        _ f := OneR ;
  w_rename _  _ (OneL c)      _ f := OneL (s_rename _ c _ f) ;
  w_rename _  _ (BotR c)      _ f := BotR (s_rename _ c _ f) ;
  w_rename _  _ (BotL)        _ f := BotL ;
  w_rename _  _ (TenR v1 v2)  _ f := TenR (w_rename _ _ v1 _ f) (w_rename _ _ v2 _ f) ;
  w_rename _  _ (TenL c)      _ f := TenL (s_rename _ c _ (r_shift2 f)) ;
  w_rename _  _ (ParR c)      _ f := ParR (s_rename _ c _ (r_shift2 f)) ;
  w_rename _  _ (ParL k1 k2)  _ f := ParL (w_rename _ _ k1 _ f) (w_rename _ _ k2 _ f) ;
  w_rename _  _ (OrR1 v)      _ f := OrR1 (w_rename _ _ v _ f) ;
  w_rename _  _ (OrR2 v)      _ f := OrR2 (w_rename _ _ v _ f) ;
  w_rename _  _ (OrL c1 c2)   _ f := OrL (s_rename _ c1 _ (r_shift1 f))
                                         (s_rename _ c2 _ (r_shift1 f)) ;
  w_rename _  _ (AndR c1 c2)  _ f := AndR (s_rename _ c1 _ (r_shift1 f))
                                          (s_rename _ c2 _ (r_shift1 f)) ;
  w_rename _  _ (AndL1 k)     _ f := AndL1 (w_rename _ _ k _ f) ;
  w_rename _  _ (AndL2 k)     _ f := AndL2 (w_rename _ _ k _ f) ;
  w_rename _  _ (ShiftPR t)   _ f := ShiftPR (t_rename _ _ t _ f) ;
  w_rename _  _ (ShiftPL c)   _ f := ShiftPL (s_rename _ c _ (r_shift1 f)) ;
  w_rename _  _ (ShiftNR c)   _ f := ShiftNR (s_rename _ c _ (r_shift1 f)) ;
  w_rename _  _ (ShiftNL t)   _ f := ShiftNL (t_rename _ _ t _ f) ;
  w_rename _  _ (NegPR k)     _ f := NegPR (w_rename _ _ k _ f) ;
  w_rename _  _ (NegPL c)     _ f := NegPL (s_rename _ c _ (r_shift1 f)) ;
  w_rename _  _ (NegNR c)     _ f := NegNR (s_rename _ c _ (r_shift1 f)) ;
  w_rename _  _ (NegNL v)     _ f := NegNL (w_rename _ _ v _ f) ;
with s_rename : state β‡’β‚€ ⟦ c_var , state βŸ§β‚€ :=
  s_rename _ (Cut _ v k) _ f := Cut _ (t_rename _ _ v _ f) (t_rename _ _ k _ f) .

We extend it to values...

Equations v_rename : val ⇒₁ ⟦ c_var , val βŸ§β‚ :=
  v_rename _ (@LTy pos a) := w_rename _ _ ;
  v_rename _ (@RTy pos a) := t_rename _ _ ;
  v_rename _ (@LTy neg a) := t_rename _ _ ;
  v_rename _ (@RTy neg a) := w_rename _ _ .

... provide a couple infix notations...

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).

... and extend it to assignments.

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.

The following bunch of shifting functions will help us define parallel substitution.

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

We also define two embeddings linking the various syntactical categories.

Equations v_of_w Ξ“ A : whn Ξ“ A -> val Ξ“ A :=
  v_of_w _ (@LTy pos _) v := v ;
  v_of_w _ (@RTy pos _) u := Whn u ;
  v_of_w _ (@LTy neg _) u := Whn u ;
  v_of_w _ (@RTy neg _) k := k .
Arguments v_of_w {Ξ“ A} v.
#[global] Coercion v_of_w : whn >-> val.

Equations t_of_v Ξ“ A : val Ξ“ A -> term Ξ“ A :=
  t_of_v _ (@LTy pos _) v := Whn v ;
  t_of_v _ (@RTy pos _) u := u ;
  t_of_v _ (@LTy neg _) u := u ;
  t_of_v _ (@RTy neg _) k := Whn k .
Arguments t_of_v {Ξ“ A} v.
#[global] Coercion t_of_v : val >-> term.

Substitution

Having done with renaming, we reapply the same pattern to define parallel substitution. Note that substituting a weak head-normal form with values may not yield a weak head-normal form, but only a value!

Equations t_subst : term ⇒₁ ⟦ val , term βŸ§β‚ :=
  t_subst _ _ (Mu c)    _ f := Mu (s_subst _ c _ (a_shift1 f)) ;
  t_subst _ _ (RecL t)  _ f := RecL (t_subst _ _ t _ (a_shift1 f)) ;
  t_subst _ _ (RecR t)  _ f := RecR (t_subst _ _ t _ (a_shift1 f)) ;
  t_subst _ _ (Whn v)   _ f := w_subst _ _ v _ f ;
with w_subst : whn ⇒₁ ⟦ val , val βŸ§β‚ :=
  w_subst _  _ (Var i)      _ f := f _ i ;
  w_subst _  _ (ZerL)       _ f := Whn ZerL ;
  w_subst _  _ (TopR)       _ f := Whn TopR ;
  w_subst _  _ (OneR)       _ f := OneR ;
  w_subst _  _ (OneL c)     _ f := Whn (OneL (s_subst _ c _ f)) ;
  w_subst _  _ (BotR c)     _ f := Whn (BotR (s_subst _ c _ f)) ;
  w_subst _  _ (BotL)       _ f := BotL ;
  w_subst _  _ (TenR v1 v2) _ f := TenR (w_subst _ _ v1 _ f) (w_subst _ _ v2 _ f) ;
  w_subst _  _ (TenL c)     _ f := Whn (TenL (s_subst _ c _ (a_shift2 f))) ;
  w_subst _  _ (ParR c)     _ f := Whn (ParR (s_subst _ c _ (a_shift2 f))) ;
  w_subst _  _ (ParL k1 k2) _ f := ParL (w_subst _ _ k1 _ f) (w_subst _ _ k2 _ f) ;
  w_subst _  _ (OrR1 v)     _ f := OrR1 (w_subst _ _ v _ f) ;
  w_subst _  _ (OrR2 v)     _ f := OrR2 (w_subst _ _ v _ f) ;
  w_subst _  _ (OrL c1 c2)  _ f := Whn (OrL (s_subst _ c1 _ (a_shift1 f))
                                            (s_subst _ c2 _ (a_shift1 f))) ;
  w_subst _  _ (AndR c1 c2) _ f := Whn (AndR (s_subst _ c1 _ (a_shift1 f))
                                             (s_subst _ c2 _ (a_shift1 f))) ;
  w_subst _  _ (AndL1 k)    _ f := AndL1 (w_subst _ _ k _ f) ;
  w_subst _  _ (AndL2 k)    _ f := AndL2 (w_subst _ _ k _ f) ;
  w_subst _  _ (ShiftPR t)  _ f := ShiftPR (t_subst _ _ t _ f) ;
  w_subst _  _ (ShiftPL c)  _ f := Whn (ShiftPL (s_subst _ c _ (a_shift1 f))) ;
  w_subst _  _ (ShiftNR c)  _ f := Whn (ShiftNR (s_subst _ c _ (a_shift1 f))) ;
  w_subst _  _ (ShiftNL t)  _ f := ShiftNL (t_subst _ _ t _ f) ;
  w_subst _  _ (NegPR k)    _ f := NegPR (w_subst _ _ k _ f) ;
  w_subst _  _ (NegPL c)    _ f := Whn (NegPL (s_subst _ c _ (a_shift1 f))) ;
  w_subst _  _ (NegNR c)    _ f := Whn (NegNR (s_subst _ c _ (a_shift1 f))) ;
  w_subst _  _ (NegNL v)    _ f := NegNL (w_subst _ _ v _ f) ;
with s_subst : state β‡’β‚€ ⟦ val , state βŸ§β‚€ :=
   s_subst _ (Cut p v k) _ f := Cut p (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 _ (@LTy pos a) v _ f := v `α΅₯βŠ› f ;
  v_subst _ (@RTy pos a) t _ f := t `β‚œβŠ› f ;
  v_subst _ (@LTy neg a) t _ f := t `β‚œβŠ› f ;
  v_subst _ (@RTy neg a) k _ f := k `α΅₯βŠ› f .

With this in hand we can instanciate the relevant part of substitution monoid and module structures for values and states. This will provide us with the missing infix notations.

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

We now define helpers for substituting the top one or top two variables from a context.

Definition asgn1 {Ξ“ a} (v : val Ξ“ a) : (Ξ“ β–Άβ‚“ a) =[val]> Ξ“ := [ var ,β‚“ v ] .
Definition asgn2 {Ξ“ a b} (v1 : val Ξ“ a) (v2 : val Ξ“ b) : (Ξ“ β–Άβ‚“ a β–Άβ‚“ b) =[val]> Ξ“
  := [ [ var ,β‚“ v1 ] ,β‚“ v2 ].

Arguments asgn1 {_ _} & _.
Arguments asgn2 {_ _ _} & _ _.

Notation "₁[ v ]" := (asgn1 v).
Notation "β‚‚[ v1 , v2 ]" := (asgn2 v1 v2).

Observations

When defining (co-)patterns, we will enforce a form of focalisation, where no negative variables are introduced. In this context, 'negative' is a new notion applying to side-annotated types, mixing both type polarity and side annotation: a side-annotated variable is positive iff it is a positive variable or a negative co-variable.

Equations is_neg : ty -> SProp :=
  is_neg (@LTy pos a) := sEmpty ;
  is_neg (@RTy pos a) := sUnit ;
  is_neg (@LTy neg a) := sUnit ;
  is_neg (@RTy neg a) := sEmpty .

We define negative types as a subset of types, and negative contexts as a subset of contexts. Our generic infrastructure for contexts and variables really shines here as the type of variables in a negative context is convertible to the type of variables in the underlying context. See Ctx/Subset.v.

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.

We can now define patterns...

Inductive pat : ty -> Type :=
| PVarP (A : pre_ty neg) : pat `+A
| PVarN (A : pre_ty pos) : pat `-A
| POne : pat `+1
| PBot : pat `-βŠ₯
| PTen {A B} : pat `+A -> pat `+B -> pat `+(A βŠ— B)
| PPar {A B} : pat `-A -> pat `-B -> pat `-(A β…‹ B)
| POr1 {A B} : pat `+A -> pat `+(A βŠ• B)
| POr2 {A B} : pat `+B -> pat `+(A βŠ• B)
| PAnd1 {A B} : pat `-A -> pat `-(A & B)
| PAnd2 {A B} : pat `-B -> pat `-(A & B)
| PShiftP A : pat `+(↓ A)
| PShiftN A : pat `-(↑ A)
| PNegP {A} : pat `-A -> pat `+(βŠ– A)
| PNegN {A} : pat `+A -> pat `-(Β¬ A)
.

... and their domain, i.e. the context they bind.

Equations p_dom {t} : pat t -> neg_ctx :=
  p_dom (PVarP A)    := βˆ…β‚› β–Άβ‚› {| sub_elt := `+A ; sub_prf := stt |} ;
  p_dom (PVarN A)    := βˆ…β‚› β–Άβ‚› {| sub_elt := `-A ; sub_prf := stt |} ;
  p_dom (POne)       := βˆ…β‚› ;
  p_dom (PBot)       := βˆ…β‚› ;
  p_dom (PTen p1 p2) := p_dom p1 +β–Άβ‚› p_dom p2 ;
  p_dom (PPar p1 p2) := p_dom p1 +β–Άβ‚› p_dom p2 ;
  p_dom (POr1 p)     := p_dom p ;
  p_dom (POr2 p)     := p_dom p ;
  p_dom (PAnd1 p)    := p_dom p ;
  p_dom (PAnd2 p)    := p_dom p ;
  p_dom (PShiftP A)  := βˆ…β‚› β–Άβ‚› {| sub_elt := `+A ; sub_prf := stt |} ;
  p_dom (PShiftN A)  := βˆ…β‚› β–Άβ‚› {| sub_elt := `-A ; sub_prf := stt |} ;
  p_dom (PNegP p)    := p_dom p ;
  p_dom (PNegN p)    := p_dom p .

We finally instanciate the observation structure. Note that our generic formalization mostly cares about 'observations', that is co-patterns. As such we instanciate observations by patterns at the dual type.

Definition obs_op : Oper ty neg_ctx :=
  {| o_op A := pat A† ; o_dom _ p := p_dom p |} .

Now come a rather tedious set of embeddings between syntactic categories related to patterns. We start by embedding patterns into weak head-normal forms.

Equations w_of_p {a} (p : pat a) : whn (p_dom p) a :=
  w_of_p (PVarP _)    := Var top ;
  w_of_p (PVarN _)    := Var top ;
  w_of_p (POne)       := OneR ;
  w_of_p (PBot)       := BotL ;
  w_of_p (PTen p1 p2) := TenR (w_of_p p1 `α΅₯βŠ›α΅£ r_cat_l) (w_of_p p2 `α΅₯βŠ›α΅£ r_cat_r) ;
  w_of_p (PPar p1 p2) := ParL (w_of_p p1 `α΅₯βŠ›α΅£ r_cat_l) (w_of_p p2 `α΅₯βŠ›α΅£ r_cat_r) ;
  w_of_p (POr1 p)     := OrR1 (w_of_p p) ;
  w_of_p (POr2 p)     := OrR2 (w_of_p p) ;
  w_of_p (PAnd1 p)    := AndL1 (w_of_p p) ;
  w_of_p (PAnd2 p)    := AndL2 (w_of_p p) ;
  w_of_p (PShiftP _)  := ShiftPR (Whn (Var top)) ;
  w_of_p (PShiftN _)  := ShiftNL (Whn (Var top)) ;
  w_of_p (PNegP p)    := NegPR (w_of_p p) ;
  w_of_p (PNegN p)    := NegNL (w_of_p p) .
#[global] Coercion w_of_p : pat >-> whn.

Now we explain how to split (some) weak head-normal forms into a pattern filled with values. I am sorry in advance for your CPU-cycles wasted to typechecking these quite hard dependent pattern matchings. We start off by two helpers for refuting impossible variables in negative context, which because of the use of SProp give trouble to Equations for deriving functional elimination principles if inlined.

Definition elim_var_p {Ξ“ : neg_ctx} {A : pre_ty pos} {X : Type} : Ξ“ βˆ‹ `+A -> X
  := fun i => match s_prf i with end .

Definition elim_var_n {Ξ“ : neg_ctx} {A : pre_ty neg} {X : Type} : Ξ“ βˆ‹ `-A -> X
  := fun i => match s_prf i with end .

Equations p_of_w_0p {Ξ“ : neg_ctx} (A : pre_ty pos) : whn Ξ“ `+A -> pat `+A :=
  p_of_w_0p (0)     (Var i)      := elim_var_p i ;
  p_of_w_0p (1)     (Var i)      := elim_var_p i ;
  p_of_w_0p (A βŠ— B) (Var i)      := elim_var_p i ;
  p_of_w_0p (A βŠ• B) (Var i)      := elim_var_p i ;
  p_of_w_0p (↓ A)   (Var i)      := elim_var_p i ;
  p_of_w_0p (βŠ– A)   (Var i)      := elim_var_p i ;
  p_of_w_0p (1)     (OneR)       := POne ;
  p_of_w_0p (A βŠ— B) (TenR v1 v2) := PTen (p_of_w_0p A v1) (p_of_w_0p B v2) ;
  p_of_w_0p (A βŠ• B) (OrR1 v)     := POr1 (p_of_w_0p A v) ;
  p_of_w_0p (A βŠ• B) (OrR2 v)     := POr2 (p_of_w_0p B v) ;
  p_of_w_0p (↓ A)   (ShiftPR _)  := PShiftP A ;
  p_of_w_0p (βŠ– A)   (NegPR k)    := PNegP (p_of_w_0n A k) ;
with p_of_w_0n {Ξ“ : neg_ctx} (A : pre_ty neg) : whn Ξ“ `-A -> pat `-A :=
  p_of_w_0n (⊀)     (Var i)      := elim_var_n i ;
  p_of_w_0n (βŠ₯)     (Var i)      := elim_var_n i ;
  p_of_w_0n (A β…‹ B) (Var i)      := elim_var_n i ;
  p_of_w_0n (A & B) (Var i)      := elim_var_n i ;
  p_of_w_0n (↑ A)   (Var i)      := elim_var_n i ;
  p_of_w_0n (Β¬ A)   (Var i)      := elim_var_n i ;
  p_of_w_0n (βŠ₯)     (BotL)       := PBot ;
  p_of_w_0n (A β…‹ B) (ParL k1 k2) := PPar (p_of_w_0n A k1) (p_of_w_0n B k2) ;
  p_of_w_0n (A & B) (AndL1 k)    := PAnd1 (p_of_w_0n A k) ;
  p_of_w_0n (A & B) (AndL2 k)    := PAnd2 (p_of_w_0n B k) ;
  p_of_w_0n (↑ A)   (ShiftNL _)  := PShiftN A ;
  p_of_w_0n (Β¬ A)   (NegNL v)    := PNegN (p_of_w_0p A v) .

Equations p_dom_of_w_0p {Ξ“ : neg_ctx} (A : pre_ty pos) (v : whn Ξ“ `+A)
          : p_dom (p_of_w_0p A v) =[val]> Ξ“ by struct A :=
  p_dom_of_w_0p (0)     (Var i)      := elim_var_p i ;
  p_dom_of_w_0p (1)     (Var i)      := elim_var_p i ;
  p_dom_of_w_0p (A βŠ— B) (Var i)      := elim_var_p i ;
  p_dom_of_w_0p (A βŠ• B) (Var i)      := elim_var_p i ;
  p_dom_of_w_0p (↓ A)   (Var i)      := elim_var_p i ;
  p_dom_of_w_0p (βŠ– A)   (Var i)      := elim_var_p i ;
  p_dom_of_w_0p (1)     (OneR)       := a_empty ;
  p_dom_of_w_0p (A βŠ— B) (TenR v1 v2) := [ p_dom_of_w_0p A v1 , p_dom_of_w_0p B v2 ] ;
  p_dom_of_w_0p (A βŠ• B) (OrR1 v)     := p_dom_of_w_0p A v ;
  p_dom_of_w_0p (A βŠ• B) (OrR2 v)     := p_dom_of_w_0p B v ;
  p_dom_of_w_0p (↓ A)   (ShiftPR x)  := a_append a_empty x ;
  p_dom_of_w_0p (βŠ– A)   (NegPR k)    := p_dom_of_w_0n A k ;
     with p_dom_of_w_0n {Ξ“ : neg_ctx} (A : pre_ty neg) (k : whn Ξ“ `-A)
          : p_dom (p_of_w_0n A k) =[val]> Ξ“ by struct A :=
  p_dom_of_w_0n (⊀)     (Var i)      := elim_var_n i ;
  p_dom_of_w_0n (βŠ₯)     (Var i)      := elim_var_n i ;
  p_dom_of_w_0n (A β…‹ B) (Var i)      := elim_var_n i ;
  p_dom_of_w_0n (A & B) (Var i)      := elim_var_n i ;
  p_dom_of_w_0n (↑ A)   (Var i)      := elim_var_n i ;
  p_dom_of_w_0n (Β¬ A)   (Var i)      := elim_var_n i ;
  p_dom_of_w_0n (βŠ₯)     (BotL)       := a_empty ;
  p_dom_of_w_0n (A β…‹ B) (ParL k1 k2) := [ p_dom_of_w_0n A k1 , p_dom_of_w_0n B k2 ] ;
  p_dom_of_w_0n (A & B) (AndL1 k)    := p_dom_of_w_0n A k ;
  p_dom_of_w_0n (A & B) (AndL2 k)    := p_dom_of_w_0n B k ;
  p_dom_of_w_0n (↑ A)   (ShiftNL x)  := a_append a_empty x ;
  p_dom_of_w_0n (Β¬ A)   (NegNL v)    := p_dom_of_w_0p A v .

We can now package up all these auxiliary functions into the following ones, abstracting polarity and side-annotation.

Equations p_of_v {Ξ“ : neg_ctx} A : val Ξ“ A -> pat A :=
  p_of_v (@LTy pos A) v := p_of_w_0p A v ;
  p_of_v (@RTy pos A) _ := PVarN A ;
  p_of_v (@LTy neg A) _ := PVarP A ;
  p_of_v (@RTy neg A) k := p_of_w_0n A k .

Equations p_dom_of_v {Ξ“ : neg_ctx} A (v : val Ξ“ A) : p_dom (p_of_v A v) =[val]> Ξ“ :=
  p_dom_of_v (@LTy pos A) v := p_dom_of_w_0p A v ;
  p_dom_of_v (@RTy pos A) x := [ ! ,β‚“ x ] ;
  p_dom_of_v (@LTy neg A) x := [ ! ,β‚“ x ] ;
  p_dom_of_v (@RTy neg A) k := p_dom_of_w_0n A k .

Definition v_split_p {Ξ“ : neg_ctx} {A} (v : whn Ξ“ `+A) : (obs_op # val) Ξ“ `-A
  := (p_of_w_0p A v : o_op obs_op `-A) ⦇ p_dom_of_w_0p A v ⦈.

Definition v_split_n {Ξ“ : neg_ctx} {A} (v : whn Ξ“ `-A) : (obs_op # val) Ξ“ `+A
  := (p_of_w_0n A v : o_op obs_op `+_) ⦇ p_dom_of_w_0n A v ⦈.

Evaluation

With patterns and observations now in hand we prepare for the definition of evaluation and define a shorthand for normal forms. 'Normal forms' are here understood---as in the paper---in our slightly non-standard presentation of triplets of a variable, an observation and an assignment.

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

Now the bulk of evaluation: the step function. Once again we are greatful for Equations providing us with a justification for the fact that this complex dependent pattern-matching is indeed total.

Equations eval_aux {Ξ“ : neg_ctx} : state Ξ“ -> (state Ξ“ + nf Ξ“) :=
  eval_aux (Cut pos (Mu c)  (x))    := inl (c β‚œβŠ› ₁[ x ]) ;
  eval_aux (Cut neg (x)     (Mu c)) := inl (c β‚œβŠ› ₁[ x ]) ;

  eval_aux (Cut pos (Whn v) (Mu c))  := inl (c β‚œβŠ› ₁[ v ]) ;
  eval_aux (Cut neg (Mu c)  (Whn k)) := inl (c β‚œβŠ› ₁[ k ]) ;

  eval_aux (Cut pos (Whn v)  (RecL k)) := inl (Cut pos (Whn v) (k `β‚œβŠ› ₁[ RecL k ])) ;
  eval_aux (Cut neg (RecR t) (Whn k))  := inl (Cut neg (t `β‚œβŠ› ₁[ RecR t ]) (Whn k)) ;

  eval_aux (Cut pos (Whn v)       (Whn (Var i))) := inr (s_var_upg i β‹… v_split_p v) ;
  eval_aux (Cut neg (Whn (Var i)) (Whn k))       := inr (s_var_upg i β‹… v_split_n k) ;

  eval_aux (Cut pos (Whn (Var i)) (Whn _))       := elim_var_p i ;
  eval_aux (Cut neg (Whn _)       (Whn (Var i))) := elim_var_n i ;

  eval_aux (Cut pos (Whn (OneR))       (Whn (OneL c)))     := inl c ;
  eval_aux (Cut neg (Whn (BotR c))     (Whn (BotL)))       := inl c ;
  eval_aux (Cut pos (Whn (TenR v1 v2)) (Whn (TenL c)))     := inl (c β‚œβŠ› β‚‚[ v1 , v2 ]) ;
  eval_aux (Cut neg (Whn (ParR c))     (Whn (ParL k1 k2))) := inl (c β‚œβŠ› β‚‚[ k1 , k2 ]) ;
  eval_aux (Cut pos (Whn (OrR1 v))     (Whn (OrL c1 c2)))  := inl (c1 β‚œβŠ› ₁[ v ]) ;
  eval_aux (Cut pos (Whn (OrR2 v))     (Whn (OrL c1 c2)))  := inl (c2 β‚œβŠ› ₁[ v ]) ;
  eval_aux (Cut neg (Whn (AndR c1 c2)) (Whn (AndL1 k)))    := inl (c1 β‚œβŠ› ₁[ k ]) ;
  eval_aux (Cut neg (Whn (AndR c1 c2)) (Whn (AndL2 k)))    := inl (c2 β‚œβŠ› ₁[ k ]) ;
  eval_aux (Cut pos (Whn (ShiftPR x))  (Whn (ShiftPL c)))  := inl (c β‚œβŠ› ₁[ x ]) ;
  eval_aux (Cut neg (Whn (ShiftNR c))  (Whn (ShiftNL x)))  := inl (c β‚œβŠ› ₁[ x ]) ;
  eval_aux (Cut pos (Whn (NegPR k))    (Whn (NegPL c)))    := inl (c β‚œβŠ› ₁[ k ]) ;
  eval_aux (Cut neg (Whn (NegNR c))    (Whn (NegNL v)))    := inl (c β‚œβŠ› ₁[ v ]) .

Finally we define evaluation as the iteration of the step function in the Delay monad, and also define application of an observation with arguments to a value.

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

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

Metatheory

Now comes a rather ugly part: the metatheory of our syntax. Comments will be rather more sparse. For a thorough explaination of its structure, see Examples/Lambda/CBLTyped.v. We will here be concerned with extensional equality preservation, identity and composition laws for renaming and substitution, and also refolding lemmas for splitting and embedding patterns. You are encouraged to just skip until line ~1300.

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
  (P_t : forall Ξ“ A, term Ξ“ A -> Prop)
  (P_w : forall Ξ“ A, whn Ξ“ A -> Prop)
  (P_s : forall Ξ“, state Ξ“ -> Prop) :=
{
  ind_mu : forall Ξ“ A s (H : P_s _ s), P_t Ξ“ A (Mu s) ;
  ind_recp : forall Ξ“ A t (H : P_t _ _ t), P_t Ξ“ `-A (RecL t) ;
  ind_recn : forall Ξ“ A t (H : P_t _ _ t), P_t Ξ“ `+A (RecR t) ;
  ind_whn : forall Ξ“ A w (H : P_w _ _ w), P_t Ξ“ A (Whn w) ;
  ind_var : forall Ξ“ A h, P_w Ξ“ A (Var h) ;
  ind_zerl : forall Ξ“, P_w Ξ“ `-0 ZerL ;
  ind_topr : forall Ξ“, P_w Ξ“ `+⊀ TopR ;
  ind_oner : forall Ξ“, P_w Ξ“ `+1 OneR ;
  ind_onel : forall Ξ“ s, P_s Ξ“ s -> P_w Ξ“ `-1 (OneL s) ;
  ind_botr : forall Ξ“ s, P_s Ξ“ s -> P_w Ξ“ `+βŠ₯ (BotR s) ;
  ind_botl : forall Ξ“, P_w Ξ“ `-βŠ₯ BotL ;
  ind_tenr : forall Ξ“ A B w1 (H1 : P_w _ _ w1) w2 (H2 : P_w _ _ w2), P_w Ξ“ `+(A βŠ— B) (TenR w1 w2) ;
  ind_tenl : forall Ξ“ A B s (H : P_s _ s), P_w Ξ“ `-(A βŠ— B) (TenL s) ;
  ind_parr : forall Ξ“ A B s (H : P_s _ s), P_w Ξ“ `+(A β…‹ B) (ParR s) ;
  ind_parl : forall Ξ“ A B w1 (H1 : P_w _ _ w1) w2 (H2 : P_w Ξ“ `-B w2), P_w Ξ“ `-(A β…‹ B) (ParL w1 w2) ;
  ind_orr1 : forall Ξ“ A B w (H : P_w _ _ w), P_w Ξ“ `+(A βŠ• B) (OrR1 w) ;
  ind_orr2 : forall Ξ“ A B w (H : P_w _ _ w), P_w Ξ“ `+(A βŠ• B) (OrR2 w) ;
  ind_orl : forall Ξ“ A B s1 (H1 : P_s _ s1) s2 (H2 : P_s _ s2), P_w Ξ“ `-(A βŠ• B) (OrL s1 s2) ;
  ind_andr : forall Ξ“ A B s1 (H1 : P_s _ s1) s2 (H2 : P_s _ s2), P_w Ξ“ `+(A & B) (AndR s1 s2) ;
  ind_andl1 : forall Ξ“ A B w (H : P_w _ _ w), P_w Ξ“ `-(A & B) (AndL1 w) ;
  ind_andl2 : forall Ξ“ A B w (H : P_w _ _ w), P_w Ξ“ `-(A & B) (AndL2 w) ;
  ind_shiftpr : forall Ξ“ A t (H : P_t _ _ t), P_w Ξ“ `+(↓ A) (ShiftPR t) ;
  ind_shiftpl : forall Ξ“ A s (H : P_s _ s), P_w Ξ“ `-(↓ A) (ShiftPL s) ;
  ind_shiftnr : forall Ξ“ A s (H : P_s _ s), P_w Ξ“ `+(↑ A) (ShiftNR s) ;
  ind_shiftnl : forall Ξ“ A t (H : P_t _ _ t), P_w Ξ“ `-(↑ A) (ShiftNL t) ;
  ind_negpr : forall Ξ“ A w (H : P_w _ _ w), P_w Ξ“ `+(βŠ– A) (NegPR w) ;
  ind_negpl : forall Ξ“ A s (H : P_s _ s), P_w Ξ“ `-(βŠ– A) (NegPL s) ;
  ind_negnr : forall Ξ“ A s (H : P_s _ s), P_w Ξ“ `+(Β¬ A) (NegNR s) ;
  ind_negnl : forall Ξ“ A w (H : P_w _ _ w), P_w Ξ“ `-(Β¬ A) (NegNL w) ;
  ind_cut : forall Ξ“ p A t1 (H1 : P_t _ _ t1) t2 (H2 : P_t _ _ t2), P_s Ξ“ (@Cut _ p A t1 t2)
} .

Lemma term_ind_mut P_t P_w P_s (H : syn_ind_args P_t P_w P_s) Ξ“ A t : P_t Ξ“ A t .
  destruct H; now apply (term_mut P_t P_w P_s).
Qed.

Lemma whn_ind_mut P_t P_w P_s (H : syn_ind_args P_t P_w P_s) Ξ“ A w : P_w Ξ“ A w .
  destruct H; now apply (whn_mut P_t P_w P_s).
Qed.

Lemma state_ind_mut P_t P_w P_s (H : syn_ind_args P_t P_w P_s) Ξ“ s : P_s Ξ“ s .
  destruct H; now apply (state_mut P_t P_w P_s).
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; eauto.
  all: first [ apply H | apply H1 | apply H2 ]; auto.
  all: first [ apply r_shift1_eq | apply r_shift2_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 as [ [] | [] ].
  now apply w_ren_eq.
  now apply t_ren_eq.
  now apply t_ren_eq.
  now apply w_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_shift2_eq {Ξ“ Ξ” A B} : Proper (asgn_eq _ _ ==> asgn_eq _ _) (@a_shift2 Ξ“ Ξ” A B).
  intros ? ? H ? v.
  do 2 (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_shift2_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 as [ [] | [] ].
  now apply w_ren_ren.
  now apply t_ren_ren.
  now apply t_ren_ren.
  now apply w_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_shift2_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 as [ [] | [] ].
  now apply w_ren_id_l.
  now apply t_ren_id_l.
  now apply t_ren_id_l.
  now apply w_ren_id_l.
Qed.

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

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

Lemma a_shift2_id {Ξ“ A B} : @a_shift2 Ξ“ Ξ“ A B var ≑ₐ var.
  intros ? v; cbn.
  do 2 (dependent elimination v; cbn; auto).
  now destruct a as [[]|[]].
Qed.

Arguments var : simpl never.
Lemma a_shift1_ren_r {Ξ“1 Ξ“2 Ξ“3 y} (f1 : Ξ“1 =[ val ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3)
      : a_shift1 (y:=y) (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_shift2_ren_r {Ξ“1 Ξ“2 Ξ“3 y z} (f1 : Ξ“1 =[ val ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3)
      : a_shift2 (y:=y) (z:=z) (f1 βŠ›α΅£ f2) ≑ₐ a_shift2 f1 βŠ›α΅£ r_shift2 f2 .
  intros ? v; do 2 (dependent elimination v; cbn; [ now rewrite v_ren_id_r | ]).
  unfold v_shift2; now rewrite 2 v_ren_ren.
Qed.

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

Lemma a_shift2_ren_l {Ξ“1 Ξ“2 Ξ“3 y z} (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3)
      : a_shift2 (y:=y) (z:=z) (f1 α΅£βŠ› f2) ≑ₐ r_shift2 f1 α΅£βŠ› a_shift2 f2 .
  intros ? v; do 2 (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.
  all: match goal with
       | |- Whn _ = Whn _ => do 2 f_equal
       | _ => f_equal
       end .
  all: first [ apply H | apply H1 | apply H2 ]; auto.
  all: first [ apply a_shift1_eq | apply a_shift2_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 as [[]|[]].
  - now apply w_sub_eq.
  - now apply t_sub_eq.
  - now apply t_sub_eq.
  - now apply w_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.
  4: destruct A as [ [] | [] ]; cbn.
  all: match goal with
       | |- Whn _ = Whn _ => do 2 f_equal
       | _ => f_equal
       end ; eauto.
  all: first [ rewrite a_shift1_ren_r | rewrite a_shift2_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 as [ [] | [] ]; cbn [ v_subst ].
  - now apply w_ren_sub.
  - now apply t_ren_sub.
  - now apply t_ren_sub.
  - now apply w_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.
  all: match goal with
       | |- Whn _ = Whn _ => do 2 f_equal
       | _ => f_equal
       end ; eauto.
  all: first [ rewrite a_shift1_ren_l | rewrite a_shift2_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 as [ [] | [] ].
  - now apply w_sub_ren.
  - now apply t_sub_ren.
  - now apply t_sub_ren.
  - now apply w_sub_ren.
Qed.

Lemma v_sub_id_r {Ξ“ Ξ”} (f : Ξ“ =[val]> Ξ”) A (i : Ξ“ βˆ‹ A) : var i α΅₯βŠ› f = f A i.
  destruct A as [ [] | [] ]; auto.
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_shift2_comp {Ξ“1 Ξ“2 Ξ“3 A B} (f1 : Ξ“1 =[val]> Ξ“2) (f2 : Ξ“2 =[val]> Ξ“3)
  : @a_shift2 _ _ A B (f1 βŠ› f2) ≑ₐ a_shift2 f1 βŠ› a_shift2 f2 .
  intros ? v; do 2 (dependent elimination v; cbn; [ now rewrite v_sub_id_r | ]).
  now unfold v_shift2; 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; intros; cbn.
  4: destruct A as [ [] | [] ]; cbn.
  all: match goal with
       | |- Whn _ = Whn _ => do 2 f_equal
       | _ => f_equal
       end ; eauto.
  all: first [ rewrite a_shift1_comp | rewrite a_shift2_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 as [ [] | [] ].
  - now apply w_sub_sub.
  - now apply t_sub_sub.
  - now apply t_sub_sub.
  - now apply w_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; intros; cbn.
  4,5: destruct A as [ [] | [] ]; cbn.
  all: match goal with
       | |- Whn _ = Whn _ => do 2 f_equal
       | _ => f_equal
       end; eauto.
  all: first [ rewrite a_shift1_id | rewrite a_shift2_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 as [ [] | [] ].
  - now apply w_sub_id_l.
  - now apply t_sub_id_l.
  - now apply t_sub_id_l.
  - now apply w_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_sub2_sub {Ξ“ Ξ” A B} (f : Ξ“ =[val]> Ξ”) (s : state (Ξ“ β–Άβ‚“ A β–Άβ‚“ B)) u v
  : (s β‚œβŠ› a_shift2 f) β‚œβŠ› β‚‚[ u α΅₯βŠ› f , v α΅₯βŠ› f ] = (s β‚œβŠ› β‚‚[ u, v ]) β‚œβŠ› f .
  cbn; rewrite 2 s_sub_sub; apply s_sub_eq.
  intros ? v0; cbn.
  do 2 (dependent elimination v0; cbn; [ now rewrite v_sub_id_r | ]).
  unfold v_shift2; 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 as [ [] | []]; cbn; now rewrite (w_sub_eq u1 u2 H).
Qed.

Definition refold_id_aux_P (Ξ“ : neg_ctx) p : pre_ty p -> Prop :=
  match p with
  | pos => fun A => forall (v : whn Ξ“ `+A), p_of_w_0p _ v `α΅₯βŠ› p_dom_of_w_0p _ v = v
  | neg => fun A => forall (v : whn Ξ“ `-A), p_of_w_0n _ v `α΅₯βŠ› p_dom_of_w_0n _ v = v
  end .

Lemma refold_id_aux {Ξ“ p} A : refold_id_aux_P Ξ“ p A .
  induction A; intros v.
  - dependent elimination v; destruct (s_prf c).
  - dependent elimination v; destruct (s_prf c).
  - dependent elimination v; [ destruct (s_prf c) | ]; auto.
  - dependent elimination v; [ destruct (s_prf c) | ]; auto.
  - dependent elimination v; [ destruct (s_prf c) | ].
    cbn; f_equal; rewrite w_sub_ren.
    rewrite <- IHA1; apply w_sub_eq; exact (a_cat_proj_l _ _).
    rewrite <- IHA2; apply w_sub_eq; exact (a_cat_proj_r _ _).
  - dependent elimination v; [ destruct (s_prf c) | ].
    cbn; f_equal; rewrite w_sub_ren.
    rewrite <- IHA1; apply w_sub_eq; exact (a_cat_proj_l _ _).
    rewrite <- IHA2; apply w_sub_eq; exact (a_cat_proj_r _ _).
  - dependent elimination v; [ destruct (s_prf c) | | ];
      cbn; f_equal; [ apply IHA1 | apply IHA2 ].
  - dependent elimination v; [ destruct (s_prf c) | | ];
      cbn; f_equal; [ apply IHA1 | apply IHA2 ].
  - dependent elimination v; [ destruct (s_prf c) | ]; cbn; f_equal.
  - dependent elimination v; [ destruct (s_prf c) | ]; cbn; f_equal.
  - dependent elimination v; [ destruct (s_prf c) | ].
    cbn; f_equal; apply IHA.
  - dependent elimination v; [ destruct (s_prf c) | ].
    cbn; f_equal; apply IHA.
Qed.

Lemma refold_id {Ξ“ : neg_ctx} A (v : val Ξ“ A)
  : p_of_v A v `α΅₯βŠ› p_dom_of_v A v = v.
  destruct A as [ [] A | [] A ]; auto; exact (refold_id_aux A v).
Qed.

Equations p_of_w_eq_aux_p {Ξ“ : neg_ctx} (A : pre_ty pos) (p : pat `+A) (e : p_dom p =[val]> Ξ“)
          : p_of_w_0p A (p `α΅₯βŠ› e) = p
          by struct p :=
  p_of_w_eq_aux_p (1)     (POne)       e := eq_refl ;
  p_of_w_eq_aux_p (A βŠ— B) (PTen p1 p2) e := f_equal2 PTen
        (eq_trans (f_equal _ (w_sub_ren _ _ _ _)) (p_of_w_eq_aux_p A p1 _))
        (eq_trans (f_equal _ (w_sub_ren _ _ _ _)) (p_of_w_eq_aux_p B p2 _)) ;
  p_of_w_eq_aux_p (A βŠ• B) (POr1 p)     e := f_equal POr1 (p_of_w_eq_aux_p A p e) ;
  p_of_w_eq_aux_p (A βŠ• B) (POr2 p)     e := f_equal POr2 (p_of_w_eq_aux_p B p e) ;
  p_of_w_eq_aux_p (↓ A)   (PShiftP _)  e := eq_refl ;
  p_of_w_eq_aux_p (βŠ– A)   (PNegP p)    e := f_equal PNegP (p_of_w_eq_aux_n A p e) ;

with p_of_w_eq_aux_n {Ξ“ : neg_ctx} (A : pre_ty neg) (p : pat `-A) (e : p_dom p =[val]> Ξ“)
         : p_of_w_0n A (p `α΅₯βŠ› e) = p
         by struct p :=
  p_of_w_eq_aux_n (βŠ₯)     (PBot)       e := eq_refl ;
  p_of_w_eq_aux_n (A β…‹ B) (PPar p1 p2) e := f_equal2 PPar
        (eq_trans (f_equal _ (w_sub_ren _ _ _ _)) (p_of_w_eq_aux_n A p1 _))
        (eq_trans (f_equal _ (w_sub_ren _ _ _ _)) (p_of_w_eq_aux_n B p2 _)) ;
  p_of_w_eq_aux_n (A & B) (PAnd1 p)    e := f_equal PAnd1 (p_of_w_eq_aux_n A p e) ;
  p_of_w_eq_aux_n (A & B) (PAnd2 p)    e := f_equal PAnd2 (p_of_w_eq_aux_n B p e) ;
  p_of_w_eq_aux_n (↑ A)   (PShiftN _)  e := eq_refl ;
  p_of_w_eq_aux_n (Β¬ A)   (PNegN p)    e := f_equal PNegN (p_of_w_eq_aux_p A p e) .

Definition p_dom_of_w_eq_P (Ξ“ : neg_ctx) p : pre_ty p -> Prop :=
  match p with
  | pos => fun A => forall (p : pat `+A) (e : p_dom p =[val]> Ξ“),
      rew [fun p => p_dom p =[ val ]> Ξ“] p_of_w_eq_aux_p A p e in p_dom_of_w_0p A (p `α΅₯βŠ› e) ≑ₐ e
  | neg => fun A => forall (p : pat `-A) (e : p_dom p =[val]> Ξ“),
      rew [fun p => p_dom p =[ val ]> Ξ“] p_of_w_eq_aux_n A p e in p_dom_of_w_0n A (p `α΅₯βŠ› e) ≑ₐ e
  end .

Lemma p_dom_of_v_eq {Ξ“ p} A : p_dom_of_w_eq_P Ξ“ p A .
  induction A; intros p e; dependent elimination p; cbn - [ r_cat_l r_cat_r ].
  - intros ? h; repeat (dependent elimination h; auto).
  - intros ? h; repeat (dependent elimination h; auto).
  - pose (H1 := w_sub_ren r_cat_l e `+A3 (w_of_p p)) .
    pose (H2 := w_sub_ren r_cat_r e `+B (w_of_p p0)) .
    pose (x1 := w_of_p p `α΅₯βŠ›α΅£ r_cat_l `α΅₯βŠ› e).
    pose (x2 := w_of_p p0 `α΅₯βŠ›α΅£ r_cat_r `α΅₯βŠ› e).
    change (w_sub_ren r_cat_l e _ _) with H1.
    change (w_sub_ren r_cat_r e _ _) with H2.
    change (_ `α΅₯βŠ›α΅£ r_cat_l `α΅₯βŠ› e) with x1 in H1 |- *.
    change (_ `α΅₯βŠ›α΅£ r_cat_r `α΅₯βŠ› e) with x2 in H2 |- *.
    rewrite H1, H2; clear H1 H2 x1 x2; cbn - [ r_cat_l r_cat_r ].
    pose (H1 := p_of_w_eq_aux_p A3 p (r_cat_l α΅£βŠ› e));
      change (p_of_w_eq_aux_p A3 _ _) with H1 in IHA1 |- *.
    pose (H2 := p_of_w_eq_aux_p B p0 (r_cat_r α΅£βŠ› e));
      change (p_of_w_eq_aux_p B _ _) with H2 in IHA2 |- *.
    transitivity ([ rew [fun p : pat `+A3 => p_dom p =[ val ]> Ξ“] H1
                     in p_dom_of_w_0p A3 (w_of_p p `α΅₯βŠ› r_cat_l α΅£βŠ› e) ,
                    rew [fun p : pat `+B => p_dom p =[ val ]> Ξ“] H2
                     in p_dom_of_w_0p B (w_of_p p0 `α΅₯βŠ› r_cat_r α΅£βŠ› e) ])%asgn.
    now rewrite <- H1, <- H2, eq_refl_map2_distr.
    refine (a_cat_uniq _ _ _ _ _); [ apply IHA1 | apply IHA2 ] .
  - pose (H1 := w_sub_ren r_cat_l e `-A4 (w_of_p p1)) .
    pose (H2 := w_sub_ren r_cat_r e `-B0 (w_of_p p2)) .
    pose (x1 := w_of_p p1 `α΅₯βŠ›α΅£ r_cat_l `α΅₯βŠ› e).
    pose (x2 := w_of_p p2 `α΅₯βŠ›α΅£ r_cat_r `α΅₯βŠ› e).
    change (w_sub_ren r_cat_l e _ _) with H1.
    change (w_sub_ren r_cat_r e _ _) with H2.
    change (_ `α΅₯βŠ›α΅£ r_cat_l `α΅₯βŠ› e) with x1 in H1 |- *.
    change (_ `α΅₯βŠ›α΅£ r_cat_r `α΅₯βŠ› e) with x2 in H2 |- *.
    rewrite H1, H2; clear H1 H2 x1 x2; cbn - [ r_cat_l r_cat_r ].
    pose (H1 := p_of_w_eq_aux_n A4 p1 (r_cat_l α΅£βŠ› e));
      change (p_of_w_eq_aux_n A4 _ _) with H1 in IHA1 |- *.
    pose (H2 := p_of_w_eq_aux_n B0 p2 (r_cat_r α΅£βŠ› e));
      change (p_of_w_eq_aux_n B0 _ _) with H2 in IHA2 |- *.
    transitivity ([ rew [fun p : pat `-A4 => p_dom p =[ val ]> Ξ“] H1
                     in p_dom_of_w_0n A4 (w_of_p p1 `α΅₯βŠ› r_cat_l α΅£βŠ› e) ,
                    rew [fun p : pat `-B0 => p_dom p =[ val ]> Ξ“] H2
                     in p_dom_of_w_0n B0 (w_of_p p2 `α΅₯βŠ› r_cat_r α΅£βŠ› e) ])%asgn.
    now rewrite <- H1, <- H2, eq_refl_map2_distr.
    refine (a_cat_uniq _ _ _ _ _); [ apply IHA1 | apply IHA2 ] .
  - match goal with | |- ?s ≑ₐ e => pose (xx := s); change (_ ≑ₐ e) with (xx ≑ₐ e) end .
    remember xx as xx'; unfold xx in Heqxx'; clear xx.
    rewrite (eq_trans Heqxx' (eq_sym (rew_map _ POr1 _ _))).
    apply IHA1.
  - match goal with | |- ?s ≑ₐ e => pose (xx := s); change (_ ≑ₐ e) with (xx ≑ₐ e) end .
    remember xx as xx'; unfold xx in Heqxx'; clear xx.
    rewrite (eq_trans Heqxx' (eq_sym (rew_map _ POr2 _ _))).
    apply IHA2.
  - match goal with | |- ?s ≑ₐ e => pose (xx := s); change (_ ≑ₐ e) with (xx ≑ₐ e) end .
    remember xx as xx'; unfold xx in Heqxx'; clear xx.
    rewrite (eq_trans Heqxx' (eq_sym (rew_map _ PAnd1 _ _))).
    apply IHA1.
  - match goal with | |- ?s ≑ₐ e => pose (xx := s); change (_ ≑ₐ e) with (xx ≑ₐ e) end .
    remember xx as xx'; unfold xx in Heqxx'; clear xx.
    rewrite (eq_trans Heqxx' (eq_sym (rew_map _ PAnd2 _ _))).
    apply IHA2.
  - 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.
    rewrite (eq_trans Heqxx' (eq_sym (rew_map _ PNegP _ _))).
    apply IHA.
  - match goal with | |- ?s ≑ₐ e => pose (xx := s); change (_ ≑ₐ e) with (xx ≑ₐ e) end .
    remember xx as xx'; unfold xx in Heqxx'; clear xx.
    rewrite (eq_trans Heqxx' (eq_sym (rew_map _ PNegN _ _))).
    apply IHA.
Qed.

(* coq unification drives me crazy!! *)
Definition upg_vp {Ξ“} {A : pre_ty pos} : whn Ξ“ `+A  -> val Ξ“ `+A := fun v => v.
Definition upg_kp {Ξ“} {A : pre_ty pos} : term Ξ“ `-A -> val Ξ“ `-A := fun v => v.
Definition upg_vn {Ξ“} {A : pre_ty neg} : term Ξ“ `+A -> val Ξ“ `+A := fun v => v.
Definition upg_kn {Ξ“} {A : pre_ty neg} : whn Ξ“ `-A  -> val Ξ“ `-A := fun v => v.
Definition dwn_vp {Ξ“} {A : pre_ty pos} : val Ξ“ `+A -> whn Ξ“ `+A  := fun v => v.
Definition dwn_kp {Ξ“} {A : pre_ty pos} : val Ξ“ `-A -> term Ξ“ `-A := fun v => v.
Definition dwn_vn {Ξ“} {A : pre_ty neg} : val Ξ“ `+A -> term Ξ“ `+A := fun v => v.
Definition dwn_kn {Ξ“} {A : pre_ty neg} : val Ξ“ `-A -> whn Ξ“ `-A  := fun v => v.

Lemma nf_eq_split_p {Ξ“ : neg_ctx} {A : pre_ty pos} (i : Ξ“ βˆ‹ `-A) (p : pat `+A) Ξ³
  : nf_eq (i β‹… v_split_p (dwn_vp (p `α΅₯βŠ› Ξ³)))
          (i β‹… (p : o_op obs_op `-A) ⦇ Ξ³ ⦈).
  unfold v_split_p, dwn_vp; cbn.
  pose proof (p_dom_of_v_eq A p Ξ³).
  pose (H' := p_of_w_eq_aux_p A p Ξ³); fold H' in H.
  pose (a := p_dom_of_w_0p A (w_of_p p `α΅₯βŠ› Ξ³)); fold a in H |- *.
  remember a as a'; clear a Heqa'.
  revert a' H; rewrite H'; intros; now econstructor.
Qed.
Lemma nf_eq_split_n {Ξ“ : neg_ctx} {A : pre_ty neg} (i : Ξ“ βˆ‹ `+A) (p : pat `-A) Ξ³
  : nf_eq (i β‹… v_split_n (dwn_kn (p `α΅₯βŠ› Ξ³)))
          (i β‹… (p : o_op obs_op `+A) ⦇ Ξ³ ⦈).
  unfold v_split_n, dwn_kn; cbn.
  pose proof (p_dom_of_v_eq A p Ξ³).
  pose (H' := p_of_w_eq_aux_n A p Ξ³); fold H' in H.
  pose (a := p_dom_of_w_0n A (w_of_p p `α΅₯βŠ› Ξ³)); fold a in H |- *.
  remember a as a'; clear a Heqa'.
  revert a' H; rewrite H'; intros; now econstructor.
Qed.

Finally we can return to saner pursuits.

OGS Instanciation

The notion of values and configurations we will pass to the generic OGS construction are our mu-mu-tilde values and states, but restricted to negative typing contexts. As such, while we have proven all the metatheory for arbitrary typing contexts, we still need a tiny bit of work to provide the laws in this special case. Once again, thanks to our tricky notion of subset context (Ctx/Subset.v), there is no need to cross a complex isomorphism (between contexts of negative types and contexts of types where all types are negative). We start by instanciating the substitution structures and their laws.

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.

Variable assumptions, that is, lemmata related to decidability of a value being a variable are easily proven inline.

#[global] Instance var_laws : var_assumptions val_n.
  esplit.
  - intros ? [[]|[]] ?? H; now dependent destruction H.
  - intros ? [[]|[]] v; dependent elimination v.
    10,13: dependent elimination w.
    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.

And we conclude the easy part by instanciating the relevant part of the language machine.

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

It now suffices to prove the remaining assumptions on the language machine: that evaluation respects substitution and that the 'bad-instanciation' relation has finite chains. For this we pull in some tooling for coinductive reasoning.

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).

Let's go.

#[global] Instance machine_law : machine_laws val_n state_n obs_op.

First we have easy lemmata on observation application.

  esplit.
  - intros; apply p_app_eq.
  - intros ?? [[]|[]] ????; cbn.
    1,4: change (?a `β‚œβŠ› ?b) with (a α΅₯βŠ› b); now rewrite (w_sub_sub _ _ _ _).
    all: change (?a `α΅₯βŠ› ?b) with (a α΅₯βŠ› b) at 1; now rewrite (w_sub_sub _ _ _ _).

Next we prove the core argument of OGS soundness: evaluating a substitution is like evaluating the configuration, substituting the result and evaluating again. While tedious the proof is basically direct, going by induction on the structure of one-step evaluation.

  - cbn; intros Ξ“ Ξ”; unfold comp_eq, it_eq; coinduction R CIH; intros c a.
    cbn; funelim (eval_aux c); try now destruct (s_prf i).
    + cbn; econstructor; refold_eval.
      change (?t `β‚œβŠ› ?a) with (upg_kp t α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?t `α΅₯βŠ› ?a) with (upg_vp t α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (RecL (?t `β‚œβŠ› a_shift1 ?a)) with (upg_kp (RecL t) α΅₯βŠ› a); rewrite t_sub1_sub.
      apply CIH.
    + change (it_eqF _ ?RX ?RY _ _ _) with
        (it_eq_map βˆ…β‚‘ RX RY T1_0
           (eval (Cut _ (Whn (v `α΅₯βŠ› a)) (a `- A i)))
           (eval (Cut _
              (Whn ((w_of_p (p_of_w_0p A v) `α΅₯βŠ› nf_args (s_var_upg i β‹… v_split_p v)) `α΅₯βŠ› a))
              (var (s_var_upg i) `β‚œβŠ› a)))).
      unfold nf_args, v_split_p, cut_r, fill_args.
      now rewrite (refold_id_aux A v).
    + cbn; econstructor; refold_eval; apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_vp v α΅₯βŠ› a); rewrite s_sub2_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_vp v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_vp v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `β‚œβŠ› ?a) with (upg_vn v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_kn v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `β‚œβŠ› ?a) with (upg_vn v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_kn v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (RecR (?t `β‚œβŠ› a_shift1 ?a)) with (upg_vn (RecR t) α΅₯βŠ› a); rewrite t_sub1_sub.
      apply CIH.
    + change (it_eqF _ ?RX ?RY _ _ _) with
        (it_eq_map βˆ…β‚‘ RX RY T1_0
           (eval (Cut _ (a `+ A i) (Whn (k `α΅₯βŠ› a))))
           (eval (Cut _
              (var (s_var_upg i) `β‚œβŠ› a)
              (Whn ((w_of_p (p_of_w_0n A k) `α΅₯βŠ› nf_args (s_var_upg i β‹… v_split_n k))
                      `α΅₯βŠ› a))))).
      unfold nf_args, v_split_n, cut_r, fill_args.
      now rewrite (refold_id_aux A k).
    + cbn; econstructor; refold_eval; apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_kn v α΅₯βŠ› a); rewrite s_sub2_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_kn v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_kn v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `β‚œβŠ› ?a) with (upg_kp v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.
    + cbn; econstructor; refold_eval.
      change (?v `α΅₯βŠ› ?a) with (upg_vp v α΅₯βŠ› a); rewrite s_sub1_sub.
      apply CIH.

Next we prove that evaluating a normal form is just returning this normal form. This goes by approximately induction on observations.

  - cbn; intros ? [ A i [ o Ξ³ ]]; cbn; unfold p_app, nf_args, cut_r, fill_args.
    cbn in o; funelim (w_of_p o); simpl_depind; inversion eqargs.
    all: match goal with
         | H : _ = ?A† |- _ => destruct A; dependent destruction H
         end.
    1-2: unfold c_var in i; destruct (s_prf i).
    all: dependent destruction eqargs; cbn.
    all: apply it_eq_unstep; cbn -[w_of_p]; econstructor.
    1-2: now econstructor.
    all: match goal with
         | Ξ³ : dom ?p =[val_n]> _ |- _ => first [ exact (nf_eq_split_p _ p Ξ³)
                                              | exact (nf_eq_split_n _ p Ξ³) ]
         end.

Finally we prove the finite chain property. The proof here is quite tedious again, with numerous cases, but it is still by brutally walking through the structure of one-step evaluation and finding the needed redex.

  - intros A; econstructor; intros [ B m ] H; dependent elimination H;
      cbn [projT1 projT2] in i, i0.
    destruct y as [ [] | [] ].
    all: dependent elimination v; try now destruct (t0 (Vvar _)).
    all: clear t0.
    all: 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.
    7-18,25-36: apply it_eq_step in i0; now inversion i0.
    1-9,19-24: match goal with
       | t : term _ _ |- _ =>
           dependent elimination t;
           [ apply it_eq_step in i0; now inversion i0
           | apply it_eq_step in i0; now inversion i0
           | ]
       | _ => idtac
         end.
    all: 
      match goal with
      | t : evalβ‚’ (Cut _ (Whn ?w) _) β‰Š _ |- _ =>
          dependent elimination w;
          [ | apply it_eq_step in i0; now inversion i0 ]
      | t : evalβ‚’ (Cut _ _ (Whn ?w)) β‰Š _ |- _ =>
          dependent elimination w;
          [ | apply it_eq_step in i0; now inversion i0 ]
      | _ => idtac
      end.
    all:
      apply it_eq_step in i0; cbn in i0; dependent elimination i0; cbn in r_rel;
      apply noConfusion_inv in r_rel; unfold v_split_n,v_split_p 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;
      dependent elimination v;
      [ apply it_eq_step in i0; cbn in i0; now inversion i0
      | apply it_eq_step in i0; cbn in i0; now inversion i0 
      | ].
    all:
      match goal with
      | H : is_var (Whn ?w) -> _ |- _ =>
          dependent elimination w;
          try now destruct (H (Vvar _))
      end.
    all: apply it_eq_step in i0; cbn in i0; dependent elimination i0.
Qed.

Final theorem

We have finished instanciating our generic OGS construction on the System D calculus. For good measure we give here the concrete instanciation of the soundness theorem, stating that bisimilarity of the OGS strategies substitution equivalence.

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.

Finally it does not cost much to recover the more standard CIU equivalence, which we derive here for the case of terms (not co-terms).

Definition c_of_t {Ξ“ : neg_ctx} {A : pre_ty pos} (t : term Ξ“ `+A)
           : state_n (Ξ“ β–Άβ‚› {| sub_elt := `-A ; sub_prf := stt |}) :=
  Cut _ (t_shift1 _ t) (Whn (Var Ctx.top)) .
Notation "'name⁺'" := c_of_t.

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

Lemma sub_csk {Ξ“ Ξ” : neg_ctx} {A : pre_ty pos} (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 : pre_ty pos} : 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.