Set Implicit Arguments.
Require Import Omega Logic List Classes.Morphisms Setoid FinFun Morphisms.
Import List Notations.

Require Export CBPV.DenotationalSemantics Confluence
        CBPV.StrongReduction CBPV.LogicalEquivalence
        CBV CBV_CBPV CBV.weakStory.

Denotational Semantics CBV

Contexts


Inductive vctx (t: bool) (m: nat): nat -> Type :=
  | vctxHole : (if t then True else False) -> vctx t m m
  | vctxPairL n: vctx t m n -> Value m -> vctx t m n
  | vctxPairR n : Value m -> vctx t m n -> vctx t m n
  | vctxInj n : bool -> vctx t m n -> vctx t m n
  | vctxLambda n: cctx t (S m) n -> vctx t m n

with cctx (t: bool) (m: nat) : nat -> Type :=
  | cctxHole: (if t then False else True) -> cctx t m m
  | cctxVctx n: vctx t m n -> cctx t m n
  | cctxAppL n: cctx t m n -> Exp m -> cctx t m n
  | cctxAppR n: Exp m -> cctx t m n -> cctx t m n
  | cctxCaseSV n: cctx t m n -> Exp (S m) -> Exp (S m) -> cctx t m n
  | cctxCaseSL n: Exp m -> cctx t (S m) n -> Exp (S m) -> cctx t m n
  | cctxCaseSR n: Exp m -> Exp (S m) -> cctx t (S m) n -> cctx t m n
  | cctxCasePV n: cctx t m n -> Exp (S (S m)) -> cctx t m n
  | cctxCasePC n: Exp m -> cctx t (S (S m)) n -> cctx t m n.

Scheme vctx_ind_2 := Induction for vctx Sort Prop
  with cctx_ind_2 := Induction for cctx Sort Prop.

Combined Scheme mutind_cbv_ctx from vctx_ind_2, cctx_ind_2.

Fixpoint fillv {m n: nat} {t: bool} (C: vctx t m n) : (if t then Value n else Exp n) -> Value m :=
  match C in vctx _ _ n return (if t then Value n else Exp n) -> Value m with
  | vctxHole _ _ H =>
    (match t return (if t then True else False) -> (if t then Value m else Exp m) -> Value m with
    | true => fun _ v' => v'
    | false => fun f _ => match f with end
    end) H
  | vctxPairL C v => fun v' => Pair (fillv C v') v
  | vctxPairR v C => fun v' => Pair v (fillv C v')
  | vctxInj b C => fun v' => Inj b (fillv C v')
  | vctxLambda C => fun v' => Lam (fillc C v')
  end
with fillc {m n: nat} {t: bool} (C: cctx t m n) : (if t then Value n else Exp n) -> Exp m :=
  match C in cctx _ _ n return (if t then Value n else Exp n) -> Exp m with
  | cctxHole _ _ H =>
    (match t return (if t then False else True) -> (if t then Value m else Exp m) -> Exp m with
     | false => fun _ v' => v'
     | true => fun f _ => match f with end
     end) H
  | cctxVctx C => fun v' => Val (fillv C v')
  | cctxAppL C v => fun v' => App (fillc C v') v
  | cctxAppR c C => fun v' => App c (fillc C v')
  | cctxCaseSV C c1 c2 => fun v' => CaseS (fillc C v') c1 c2
  | cctxCaseSL v C c2 => fun v' => CaseS v (fillc C v') c2
  | cctxCaseSR v c1 C => fun v' => CaseS v c1 (fillc C v')
  | cctxCasePV C c => fun v' => CaseP (fillc C v') c
  | cctxCasePC v C => fun v' => CaseP v (fillc C v')
  end.

Reserved Notation "Gamma [[ Delta ]] ⊩v C : B ; T" (at level 53, C at level 99).
Reserved Notation "Gamma [[ Delta ]] ⊢v C : B ; T" (at level 53, C at level 99).

Inductive vctxTyping {m: nat} {t: bool} (Gamma: ctx_cbv m) : forall n, ctx_cbv n -> vctx t m n -> type -> type -> Type :=
| vctxTypingHole A H:
    Gamma [[ Gamma ]] v vctxHole t m H : A; A
| vctxTypingPairL n (Delta: ctx_cbv n) (C: vctx t m n) A A1 A2 (v: Value m):
    Gamma[[Delta]] v C : A1; A ->
    Gamma v v : A2 ->
    Gamma[[Delta]] v vctxPairL C v : Cross A1 A2; A
| vctxTypingPairR n (Delta: ctx_cbv n) (C: vctx t m n) A A1 A2 v:
    Gamma v v : A1 ->
    Gamma[[Delta]] v C : A2; A ->
    Gamma[[Delta]] v vctxPairR v C : Cross A1 A2; A
| vctxTypingInj n (Delta: ctx_cbv n) (C: vctx t m n) A A1 A2 (b: bool):
    Gamma[[Delta]] v C : (if b then A1 else A2); A ->
    Gamma[[Delta]] v vctxInj b C : Plus A1 A2; A
| vctxTypingLambda n (Delta: ctx_cbv n) (C: cctx t (S m) n) A A' B:
    (A .: Gamma) [[Delta]] v C : B; A' ->
      Gamma [[Delta]] v vctxLambda C : Arr A B; A'
where "Gamma [[ Delta ]] ⊩v C : A ; T" := (@vctxTyping _ _ Gamma _ Delta C A T)

with cctxTyping {m: nat} {t: bool} (Gamma: ctx_cbv m) : forall n, ctx_cbv n -> cctx t m n -> type -> type -> Type :=
| cctxTypingHole A H:
    Gamma [[Gamma]] v cctxHole t m H : A; A
| cctxTypingVctx n (Delta: ctx_cbv n) (C: vctx t m n) A A':
    Gamma [[Delta]] v C : A; A' -> Gamma [[Delta]] v cctxVctx C : A; A'
| cctxTypingAppL n (Delta: ctx_cbv n) (C: cctx t m n) A A' B v:
    Gamma[[Delta]] v C : Arr A B; A' ->
    Gamma v v : A ->
    Gamma[[Delta]] v cctxAppL C v : B; A'
| cctxTypingAppR n (Delta: ctx_cbv n) c (C: cctx t m n) A A' B :
    Gamma v c : Arr A B ->
    Gamma[[Delta]] v C : A; A' ->
    Gamma[[Delta]] v cctxAppR c C : B; A'
| cctxTypingCaseSV n (Delta: ctx_cbv n) (C: cctx t m n) A1 A2 A' B c1 c2:
    Gamma[[Delta]] v C : Plus A1 A2; A' ->
    A1 .: Gamma v c1 : B ->
    A2 .: Gamma v c2 : B ->
    Gamma[[Delta]] v cctxCaseSV C c1 c2 : B; A'
| cctxTypingCaseSL n (Delta: ctx_cbv n) (C: cctx t (S m) n) A1 A2 A' B v c2:
    Gamma v v : Plus A1 A2 ->
    (A1 .: Gamma)[[Delta]] v C : B; A' ->
    A2 .: Gamma v c2 : B ->
    Gamma[[Delta]] v cctxCaseSL v C c2 : B; A'
| cctxTypingCaseSR n (Delta: ctx_cbv n) (C: cctx t (S m) n) A1 A2 A' B v c1:
    Gamma v v : Plus A1 A2 ->
    A1 .: Gamma v c1 : B ->
    (A2 .: Gamma)[[Delta]] v C : B; A' ->
    Gamma[[Delta]] v cctxCaseSR v c1 C : B; A'
| cctxTypingCasePV n (Delta: ctx_cbv n) (C: cctx t m n) A1 A2 A' B c:
    Gamma[[Delta]] v C : Cross A1 A2; A' ->
    (A2 .: (A1 .: Gamma)) v c : B ->
    Gamma[[Delta]] v cctxCasePV C c : B; A'
| cctxTypingCasePC n (Delta: ctx_cbv n) (C: cctx t (S (S m)) n) A1 A2 A' B v:
    Gamma v v : Cross A1 A2 ->
    (A2 .: (A1 .: Gamma))[[Delta]] v C : B; A' ->
    Gamma[[Delta]] v cctxCasePC v C : B; A'
where "Gamma [[ Delta ]] ⊢v C : B ; T" := (@cctxTyping _ _ Gamma _ Delta C B T).

Scheme vctx_typing_ind_2 := Minimality for vctxTyping Sort Prop
  with cctx_typing_ind_2 := Minimality for cctxTyping Sort Prop.

Combined Scheme mutind_vctx_cctx_typing from
         vctx_typing_ind_2, cctx_typing_ind_2.

Scheme vctx_typing_ind_3 := Induction for vctxTyping Sort Prop
  with cctx_typing_ind_3 := Induction for cctxTyping Sort Prop.

Combined Scheme mutindt_vctx_cctx_typing from
          vctx_typing_ind_3, cctx_typing_ind_3.

Hint Constructors vctx cctx vctxTyping cctxTyping.

Whenever we have a typed context and a correspondingly typed term, the result after inserting the term is well typed
Fixpoint vctx_value_typing_soundness m Gamma n Delta (C: vctx true m n) A A' (H: Gamma[[Delta]] v C : A; A'):
  forall v, Delta v v : A' -> (Gamma v fillv C v : A)
with vctx_comp_typing_soundness m Gamma n Delta (C: vctx false m n) A A' (H: Gamma[[Delta]] v C : A; A'):
  forall c, Delta v c : A' -> (Gamma v fillv C c : A)
with cctx_value_typing_soundness m Gamma n Delta (C: cctx true m n) B A' (H: Gamma[[Delta]] v C : B; A'):
  forall v, Delta v v : A' -> (Gamma v fillc C v : B)
with cctx_comp_typing_soundness m Gamma n Delta (C: cctx false m n) B A' (H: Gamma[[Delta]] v C : B; A'):
  forall c, Delta v c : A' -> (Gamma v fillc C c : B).
Proof.
  all: destruct H; intros; cbn; try econstructor; eauto; intuition.
Defined.

Alternative translation


Fixpoint eval'_val {n: nat} (V : Value n) : value n :=
  match V with
  | One => u
  | var_Value x => var_value x
  | Pair V1 V2 => pair (eval'_val V1) (eval'_val V2)
  | Inj b V => inj b (eval'_val V)
  | Lam M => thunk (lambda (eval'_exp M))
  end
with eval'_exp {n: nat} (M: Exp n) : Syntax.comp n :=
  match M with
  | Val V => ret (eval'_val V)
  | App M N => (lambda (lambda ($ <- var 1!; $ <- var 1!; (var 1)! (var 0)))) (<{ eval'_exp M }>) (<{ eval'_exp N }>)
  | CaseP M N => (lambda (lambda ($ <- var 1!; caseP (var 0) ((var 3)! (var 1) (var 0)))))
                  (<{eval'_exp M}>)
                  (<{ lambda (lambda (eval'_exp N)) }>)
  | CaseS M N1 N2 =>
    (lambda (lambda (lambda ($ <- var 2!; caseS (var 0) ((var 3)! (var 0)) ((var 2)! (var 0))))))
      (<{ eval'_exp M }>)
      (<{ lambda (eval'_exp N1) }>)
      (<{ lambda (eval'_exp N2) }>)
  end.

Scheme has_typeV_2 := Induction for has_typeV Sort Prop
  with has_typeE_2 := Induction for has_typeE Sort Prop.

Combined Scheme has_type_ind from has_typeE_2, has_typeV_2.

Lemma eval_ctx_cons {n : nat} (Gamma: ctx_cbv n) (A: type) :
   (A .: Gamma) >> eval_ty = (eval_ty A) .: Gamma >> eval_ty.
Proof.
  fext; intros []; cbn; reflexivity.
Qed.

Fixpoint typingVal_pres' {n} (Gamma : ctx_cbv n) V A (H : Gamma v V : A) :
  value_typing (Gamma >> eval_ty) (eval'_val V) (eval_ty A)
with typingExp_pres' {n} (Gamma : ctx_cbv n) M A (H: Gamma v M : A) :
  computation_typing (Gamma >> eval_ty) (eval'_exp M) (F (eval_ty A)).
Proof.
  - destruct H; unfold eval_val, eval_exp; try (now (repeat constructor)); cbn; eauto.
    + simpl. constructor.
    constructor. specialize (typingExp_pres' _ _ _ _ h).
    revert typingExp_pres'. asimpl. auto.
    + simpl. constructor. destruct b; apply typingVal_pres'; assumption.
  - destruct H.
    + econstructor. now eapply typingVal_pres'.
    + simpl; repeat econstructor; eauto; eapply typeVar'; cbn; reflexivity.
    + cbn; repeat econstructor; eauto.
      1 - 4: eapply typeVar'; cbn; reflexivity.
      all: rewrite <-eval_ctx_cons; eauto.
    + cbn; repeat econstructor; eauto.
      1 - 3: eapply typeVar'; cbn; try reflexivity.
      rewrite <-!eval_ctx_cons; eauto.
Qed.

Ltac expand' := LogicalEquivalence.expand.
Ltac VC := match goal with
         | [H: LogicalEquivalence.V ?A ?v1 ?v2 |- _] =>
           destruct H as (? & ? & ?); intuition; subst
         | [H: LogicalEquivalence.C ?B ?c1 ?c2 |- _] =>
           destruct H as (? & ? & ?); intuition; subst
         end.

Lemma eval_eval':
    (forall m (Gamma: ctx_cbv m) (M: Exp m) A, (Gamma v M : A) -> (comp_semeq (Gamma >> eval_ty) (F (eval_ty A)) (eval_exp M) (eval'_exp M)))
    /\ (forall m (Gamma: ctx_cbv m) (V: Value m) A, (Gamma v V : A) -> (val_semeq (Gamma >> eval_ty) (eval_ty A) (eval_val V) (eval'_val V))).
Proof.
  eapply has_type_ind; cbn; intros; try eapply fundamental_property; eauto.
  - eapply logical_equivalence_congruent_vctx_comp with (C := vctxThunk (cctxLambda __c)); eauto.
    cbn. rewrite <-eval_ctx_cons. assumption.
  - etransitivity.
    eapply logical_equivalence_congruent_vctx_value with (C := vctxPairL __v (eval_val V2)); eauto.
    { repeat constructor. now eapply typingVal_pres. } cbn.
    eapply logical_equivalence_congruent_vctx_value with (C := vctxPairR (eval'_val V1) __v); eauto.
    repeat constructor. now eapply typingVal_pres'.
  - eapply logical_equivalence_congruent_vctx_value with (C := vctxInj b __v); eauto.
    cbn; now destruct b.
  - eapply logical_equivalence_congruent_cctx_value with (C := cctxRet __v); eauto.
  - intros m gamma gamma' H1. rewrite eagerlet_substcomp.
    eapply LogicalEquivalence.closure_under_reduction.
    eapply let_to_eagerlet. reflexivity.
    eapply LogicalEquivalence.closure_under_expansion.
    reflexivity. repeat (asimpl; reduce); reflexivity.
    eapply bind with
        (K1 := ectxLetin Semantics.ectxHole _)
        (K2 := ectxLetin Semantics.ectxHole _); semeq_solve.
    intros c1 c2 []; exintuition; subst.
    destruct H5; exintuition; subst; cbn [fill].
    eapply LogicalEquivalence.closure_under_expansion.
    1 - 2: repeat reduce; asimpl; rewrite ?eagerlet_substcomp; asimpl; reflexivity.
    eapply LogicalEquivalence.closure_under_reduction.
    eapply let_to_eagerlet. reflexivity.
    eapply bind with
        (K1 := ectxLetin Semantics.ectxHole _)
        (K2 := ectxLetin Semantics.ectxHole _); semeq_solve.
    intros ? ? []; exintuition; subst; cbn [fill].
    eapply LogicalEquivalence.closure_under_expansion.
    1 - 2: repeat reduce; asimpl; reflexivity.
    eapply congruence_app; eauto.
  - intros m gamma gamma' H2. rewrite eagerlet_substcomp.
    eapply LogicalEquivalence.closure_under_reduction.
    eapply let_to_eagerlet. reflexivity.
    eapply LogicalEquivalence.closure_under_expansion.
    reflexivity. repeat (asimpl; reduce); reflexivity.
    eapply bind with
        (K1 := ectxLetin Semantics.ectxHole _)
        (K2 := ectxLetin Semantics.ectxHole _); semeq_solve.
    intros ? ? []; exintuition; subst.
    destruct H6; exintuition; subst.
    cbn [fill].
    destruct x3; eapply LogicalEquivalence.closure_under_expansion.
    all: try solve [repeat (asimpl; reduce); reflexivity].
    all: asimpl.
     + eapply H0; semeq_solve.
       replace ((A .: Gamma) >> _) with (eval_ty A .: (Gamma >> eval_ty)); semeq_solve.
       unfold funcomp; fext; intros [|]; cbn; reflexivity.
     + eapply H1; semeq_solve.
       replace ((B .: Gamma) >> _) with (eval_ty B .: (Gamma >> eval_ty)); semeq_solve.
       unfold funcomp; fext; intros [|]; cbn; reflexivity.
  - intros m gamma gamma' H2. rewrite eagerlet_substcomp.
    eapply LogicalEquivalence.closure_under_reduction.
    eapply let_to_eagerlet. reflexivity.
    eapply LogicalEquivalence.closure_under_expansion.
    reflexivity. repeat (asimpl; reduce); reflexivity.
    eapply bind with
        (K1 := ectxLetin Semantics.ectxHole _)
        (K2 := ectxLetin Semantics.ectxHole _); semeq_solve.
    intros ? ? []; exintuition; subst.
    destruct H5; exintuition; subst.
    cbn [fill].
    eapply LogicalEquivalence.closure_under_expansion.
    1, 2: repeat (asimpl; reduce); reflexivity.
    asimpl.
    eapply H0. replace (_ >> eval_ty) with (eval_ty B .: (eval_ty A .: Gamma >> eval_ty)); semeq_solve.
    fext; intros [ [|] |]; cbn; reflexivity.
Qed.

Fixpoint eval'_vctx {m n: nat} t (C : vctx t m n) : vctx t m n :=
  match C in vctx _ _ n return vctx t m n with
  | vctxHole _ _ H => vctxHole t m H
  | vctxPairL C v => vctxPairL (eval'_vctx C) (eval'_val v)
  | vctxPairR v C => vctxPairR (eval'_val v) (eval'_vctx C)
  | vctxInj b C => vctxInj b (eval'_vctx C)
  | vctxLambda C => vctxThunk (cctxLambda (eval'_cctx C))
  end
with eval'_cctx {m n: nat} t (C: cctx t m n) : cctx t m n :=
  match C in cctx _ _ n return cctx t m n with
  | cctxHole _ _ H => cctxHole t m H
  | cctxVctx C => cctxRet (eval'_vctx C)
  | cctxAppL C v =>
    cctxAppL (cctxAppR (lambda (lambda ($ <- var 1!; $ <- var 1!; (var 1)! (var 0))))
        (vctxThunk (eval'_cctx C))) (<{ eval'_exp v }>)
  | cctxAppR c C => cctxAppR
        ((lambda (lambda ($ <- var 1!; $ <- var 1!; (var 1)! (var 0)))) (<{ eval'_exp c }>))
        (vctxThunk (eval'_cctx C))
  | cctxCaseSV C c1 c2 =>
        cctxAppL
          (cctxAppL
             (cctxAppR
                (lambda (lambda (lambda ($ <- var 2!; caseS (var 0) ((var 3)! (var 0)) ((var 2)! (var 0))))))
                (vctxThunk (eval'_cctx C)))
             (<{ lambda (eval'_exp c1) }>))
          (<{ lambda (eval'_exp c2) }>)
  | cctxCaseSL v C c2 =>
    cctxAppL
      (cctxAppR
         ((lambda (lambda (lambda ($ <- var 2!; caseS (var 0) ((var 3)! (var 0)) ((var 2)! (var 0))))))
            (<{ eval'_exp v }>))
         (vctxThunk (cctxLambda (eval'_cctx C))))
      (<{ lambda (eval'_exp c2) }>)
  | cctxCaseSR v c1 C =>
    cctxAppR
      ((lambda (lambda (lambda ($ <- var 2!; caseS (var 0) ((var 3)! (var 0)) ((var 2)! (var 0))))))
         (<{ eval'_exp v }>)
         (<{ lambda (eval'_exp c1) }>))
      (vctxThunk (cctxLambda (eval'_cctx C)))
  | cctxCasePV C c =>
    cctxAppL
      (cctxAppR (lambda (lambda ($ <- var 1!; caseP (var 0) ((var 3)! (var 1) (var 0)))))
                (vctxThunk (eval'_cctx C)))
      (<{ lambda (lambda (eval'_exp c)) }>)
  | cctxCasePC v C =>
    cctxAppR
      ((lambda (lambda ($ <- var 1!; caseP (var 0) ((var 3)! (var 1) (var 0)))))
         (<{ eval'_exp v }>))
      (vctxThunk (cctxLambda (cctxLambda (eval'_cctx C))))
  end.

Fixpoint vctx_vctx_value {m n} (C: vctx true m n) (e: Value n):
  eval'_val (fillv C e) = fillv (eval'_vctx C) (eval'_val e)
with vctx_vctx_comp {m n} (C: vctx false m n) (e: Exp n):
  eval'_val (fillv C e) = fillv (eval'_vctx C) (eval'_exp e)
with cctx_cctx_value {m n} (C: cctx true m n) (e: Value n):
  eval'_exp (fillc C e) = fillc (eval'_cctx C) (eval'_val e)
with cctx_cctx_comp {m n} (C: cctx false m n) (e: Exp n):
  eval'_exp (fillc C e) = fillc (eval'_cctx C) (eval'_exp e).
Proof.
  all: destruct C; cbn; intuition; congruence.
Qed.

Fixpoint eval'_cctx_value_typing {m n: nat} Gamma Delta (C: cctx true m n) A B (H: Gamma [[Delta]] v C : A; B):
  (Gamma >> eval_ty) [[Delta >> eval_ty]] eval'_cctx C : F (eval_ty A); eval_ty B
with eval'_cctx_comp_typing {m n: nat} Gamma Delta (C: cctx false m n) A B (H: Gamma [[Delta]] v C : A; B):
  (Gamma >> eval_ty) [[Delta >> eval_ty]] eval'_cctx C : F (eval_ty A); F (eval_ty B)
with eval'_vctx_value_typing {m n: nat} Gamma Delta (C: vctx true m n) A B (H: Gamma [[Delta]] v C : A; B):
  (Gamma >> eval_ty) [[Delta >> eval_ty]] eval'_vctx C : eval_ty A; eval_ty B
with eval'_vctx_comp_typing {m n: nat} Gamma Delta (C: vctx false m n) A B (H: Gamma [[Delta]] v C : A; B):
  (Gamma >> eval_ty) [[Delta >> eval_ty]] eval'_vctx C : eval_ty A; F (eval_ty B).
Proof.
  all: destruct H; cbn; intros; repeat econstructor; eauto using typingExp_pres', typingVal_pres'.
  all: try solve [eapply typeVar'; cbn; reflexivity].
  all: try solve [rewrite <-!eval_ctx_cons; eauto using typingExp_pres', typingVal_pres'].
  all: intuition.
  all: destruct b; eauto.
Qed.

Definition Bool := Plus Unit Unit.
Definition tt {n: nat}: Exp n := Val (Inj true One).
Definition ff {n: nat}: Exp n := Val (Inj false One).

Record CBV {n: nat} (Gamma: ctx_cbv n) (A: type) :=
  mkCBV { CBV_e :> Exp n; CBV_H :> Gamma v CBV_e : A }.

Record CBV {n: nat} (Gamma: ctx_cbv n) (A: type) :=
  mkCBV { CBV_v :> Value n; CBV_H :> Gamma v CBV_v : A }.

Hint Resolve CBV_H CBV_Hᵥ.

Expression Contextual Equivalence
Notation "s ≫* t" := (star Step s t) (at level 60).

Definition exp_obseq {n: nat} {B: type} {Gamma: ctx_cbv n} (H1 H2: CBV Gamma B) :=
  forall (C: cctx false 0 n), null [[Gamma]] v C : Bool; B -> (fillc C H1 ≫* tt) <-> (fillc C H2 ≫* tt).

Instance equiv_exp_obseq (n: nat) (B: type) (Gamma: ctx_cbv n):
  Equivalence (@exp_obseq n B Gamma).
Proof.
  constructor; [firstorder.. |].
  intros X1 X2 X3 H1 H2 C H; etransitivity; eauto.
Qed.

Definition value_obseq {n: nat} {B: type} {Gamma: ctx_cbv n} (H1 H2: CBV Gamma B) :=
  forall (C: cctx true 0 n), null [[Gamma]] v C : Bool; B -> (fillc C H1 ≫* tt) <-> (fillc C H2 ≫* tt).

Instance equiv_value_obseq (n: nat) (B: type) (Gamma: ctx_cbv n):
  Equivalence (@value_obseq n B Gamma).
Proof.
  constructor; [firstorder.. |].
  intros X1 X2 X3 H1 H2 C H; etransitivity; eauto.
Qed.

Notation "C1 '≃v' C2" := (exp_obseq C1 C2) (at level 50).
Notation "C1 '≈v' C2" := (value_obseq C1 C2) (at level 50).

Definition eval_CBV (n: nat) (Gamma: ctx_cbv n) (A: type) :
  CBV Gamma A -> CBPVc (Gamma >> eval_ty) (F (eval_ty A)) :=
  fun H => @mkCBPVc _ (Gamma >> eval_ty) (F (eval_ty A)) (eval_exp H) (typingExp_pres _ _ _ H).

Definition eval_CBV (n: nat) (Gamma: ctx_cbv n) (A: type) :
  CBV Gamma A -> CBPVv (Gamma >> eval_ty) (eval_ty A) :=
  fun H => @mkCBPVv _ (Gamma >> eval_ty) (eval_ty A) (eval_val H) (typingVal_pres _ _ _ H).

Observational Equivalence by translation

Section ObservationalEquivalenceSoundness.

  (* ground returner context, converting booleans to ground returners *)

  Local Definition gbool := gsum gone gone.

  Lemma eval_eval'_tt s :
    null v s : Bool -> (eval'_exp s >* eval'_exp tt <-> eval_exp s >* eval_exp tt).
  Proof.
    intros; destruct eval_eval' as [S _];
      destruct (S _ _ _ _ X 0 ids ids) as (? & ? & ? & ? & ?); [intros []|].
    asimpl in H; asimpl in H0.
    destruct H1 as (? & ? & ? & ? & ?); subst.
    eapply groundtypes_are_simple with (G := gbool) in H3 as H4; subst.
    cbn.
    split; cbn; intros H1 % steps_nf_eval; eauto; eapply eval_bigstep in H1.
     all: eapply bigstep_soundness.
     1: now rewrite <-(bigstep_functional H0 H1).
     1: now rewrite <-(bigstep_functional H H1).
  Qed.

  Lemma obseq_soundness' n (Gamma : ctx_cbv n) (B : type) (P Q : CBV Gamma B) :
    eval_CBV P eval_CBV Q ->
    forall (C: cctx false 0 n), null [[Gamma]] v C : Bool; B ->
     (fillc C P ≫* tt) -> (fillc C Q ≫* tt).
  Proof.
    intros H.
  assert (Gamma >> eval_ty eval'_exp P : F (eval_ty B)) as T1 by eauto using typingExp_pres'.
  assert (Gamma >> eval_ty eval'_exp Q : F (eval_ty B)) as T2 by eauto using typingExp_pres'.
  assert (mkCBPVc T1 mkCBPVc T2) as H'.
  { transitivity (eval_CBV P). eapply logical_equivalence_obseq; cbn. symmetry.
    now eapply eval_eval'.
    etransitivity; eauto. eapply logical_equivalence_obseq; cbn; now eapply eval_eval'.
  }
  intros C H1 H2.
  apply MStep_preserved in H2.
  eapply eval_eval'_tt in H2; eauto using cctx_comp_typing_soundness.
  eapply backward_simulation. split; [|eapply nf_normal; repeat econstructor].
  eapply eval_eval'_tt; eauto using cctx_comp_typing_soundness.
  rewrite cctx_cctx_comp in *.
  eapply eval'_cctx_comp_typing in H1.
  replace (null >> eval_ty) with (@null valtype) in H1 by (fext; intros []).
  replace (eval_ty Bool) with (gsum gone gone: valtype) in H1 by reflexivity.
  specialize (H' _ _ H1).
  cbn in H'. eapply H'. assumption.
  Qed.

  Lemma obseq_soundnessᵥ' n (Gamma : ctx_cbv n) (B : type) (P Q : CBV Gamma B) :
  eval_CBV P eval_CBV Q ->
  forall (C: cctx true 0 n), null [[Gamma]] v C : Bool; B ->
  (fillc C P ≫* tt) -> (fillc C Q ≫* tt).
  Proof.
    intros H.
    assert (Gamma >> eval_ty eval'_val P : eval_ty B) as T1 by eauto using typingVal_pres'.
    assert (Gamma >> eval_ty eval'_val Q : eval_ty B) as T2 by eauto using typingVal_pres'.
    assert (mkCBPVv T1 mkCBPVv T2) as H'.
    { transitivity (eval_CBV P). eapply logical_equivalence_obseq; cbn. symmetry.
      now eapply eval_eval'.
      etransitivity; eauto. eapply logical_equivalence_obseq; cbn; now eapply eval_eval'.
    }
    intros C H1 H2.
    apply MStep_preserved in H2.
    eapply eval_eval'_tt in H2; eauto using cctx_value_typing_soundness.
    eapply backward_simulation. split; [|eapply nf_normal; repeat econstructor].
    eapply eval_eval'_tt; eauto using cctx_value_typing_soundness.
    rewrite cctx_cctx_value in *.
    eapply eval'_cctx_value_typing in H1.
    replace (null >> eval_ty) with (@null valtype) in H1 by (fext; intros []).
    replace (eval_ty Bool) with (gsum gone gone: valtype) in H1 by reflexivity.
    specialize (H' _ _ H1).
    cbn in H'. eapply H'. assumption.
  Qed.


  Theorem obseq_soundness n (Gamma : ctx_cbv n) (B : type) (P Q : CBV Gamma B):
    eval_CBV P eval_CBV Q -> P v Q.
  Proof.
    intros H; split; [| symmetry in H]; eauto using obseq_soundness'.
  Qed.


  Theorem obseq_soundness n (Gamma : ctx_cbv n) (B : type) (P Q : CBV Gamma B):
    eval_CBV P eval_CBV Q -> P v Q.
  Proof.
    intros H; split; [| symmetry in H]; eauto using obseq_soundnessᵥ'.
  Qed.

End ObservationalEquivalenceSoundness.

Denotational Semantics

Section Denotation.
  Variable T: Monad.
  Variable mono_requirement: forall X, Injective (@mreturn T X).
  Variable (n: nat) (Gamma: ctx_cbv n) (A: type).

  Definition denote_type :=
    denote_comptype T (F (eval_ty A)).

  Definition denote_exp (M: CBV Gamma A) :=
    denote_comptyping (typingExp_pres _ _ _ M).

  Definition denote_val (M: CBV Gamma A) :=
    denote_valtyping (typingVal_pres _ _ _ M).

  Definition denote_cbv_ctx :=
    denote_context T (Gamma >> eval_ty).

  Theorem Adequacy (P Q: CBV Gamma A):
    (forall (gamma: denote_cbv_ctx), denote_exp P gamma = denote_exp Q gamma) -> P v Q.
  Proof.
    intros H.
    specialize (adequacy mono_requirement (eval_CBV P) (eval_CBV Q) H).
    clear H. eauto using obseq_soundness.
  Qed.

  Theorem Adequacy (P Q: CBV Gamma A):
    (forall (gamma: denote_cbv_ctx), denote_val P gamma = denote_val Q gamma) -> P v Q.
  Proof.
    intros H.
    specialize (adequacy mono_requirement (eval_CBV P) (eval_CBV Q) H).
    clear H. eauto using obseq_soundness.
  Qed.

End Denotation.