module CPSterm where

open import Data.Nat
open import Data.Product
open import Function
open import Relation.Binary.PropositionalEquality

-- target type
mutual
  data cpstyp : Set where
    Nat : cpstyp
    _⇒[_⇒_]⇒_ : cpstyp  cpstyp  cpstyp  cpstyp  cpstyp

  data conttyp : Set where
    K_⇒_ : cpstyp  cpstyp  conttyp
    •_ : cpstyp  conttyp

Δ-cpstype : conttyp  cpstyp  cpstyp  Set
Δ-cpstype (K τ₁′  τ₂′) τ₁ τ₂ = τ₁′  τ₁ × τ₂′  τ₂
Δ-cpstype ( τ) τ₁ τ₂ = τ  τ₁ × τ  τ₂

-- CPS terms
mutual
  data cpsvalue[_]_ (var : cpstyp  Set) : cpstyp  Set where
    -- x
    CPSVar : {τ₁ : cpstyp}  (x : var τ₁)  cpsvalue[ var ] τ₁
    -- n
    CPSNum : (n : )  cpsvalue[ var ] Nat
    -- λx.λk.M
    CPSFun : {τ₁ τ₂ τ₃ τ₄ : cpstyp} 
             (e : var τ₂  cpsterm[ var , K τ₁  τ₃ ] τ₄) 
             cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄)
    -- S
    CPSShift : {τ₁ τ₂ τ₃ τ₄ τ₅ : cpstyp} 
             cpsvalue[ var ]
             (((τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) ⇒[ τ₄  τ₄ ]⇒ τ₅)
              ⇒[ τ₁  τ₂ ]⇒ τ₅)

  data cpsterm[_,_]_ (var : cpstyp  Set) : conttyp  cpstyp  Set where
    -- K V
    CPSRet : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
             (k : cpscont[ var , Δ , τ₁ ] τ₂) 
             (v : cpsvalue[ var ] τ₁) 
             cpsterm[ var , Δ ] τ₂
    -- V W K
    CPSApp : {τ₁ τ₂ τ₃ τ₄ : cpstyp}  {Δ : conttyp} 
             (v : cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄)) 
             (w : cpsvalue[ var ] τ₂) 
             (k : cpscont[ var , Δ , τ₁ ] τ₃) 
             cpsterm[ var , Δ ] τ₄
    -- (Sk.M) K
    CPSShift2 : {τ₁ τ₂ τ₃ τ₄ τ₅ : cpstyp}  {Δ : conttyp} 
             (e : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) 
                  cpsterm[ var , K τ₄  τ₄ ] τ₅) 
             (k : cpscont[ var , Δ , τ₁ ] τ₂) 
             cpsterm[ var , Δ ] τ₅
    -- K M
    CPSRetE : {τ τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
             (k : cpscont[ var , Δ , τ₁ ] τ₂) 
             (e : cpsterm[ var ,  τ ] τ₁) 
             cpsterm[ var , Δ ] τ₂

  data cpscont[_,_,_]_ (var : cpstyp  Set) :
       conttyp  cpstyp  cpstyp  Set where
    -- k
    CPSKVar : {τ₁ τ₂ : cpstyp} 
              cpscont[ var , K τ₁  τ₂ , τ₁ ] τ₂
    -- λx.x
    CPSKId  : {τ₁ : cpstyp} 
              cpscont[ var ,  τ₁ , τ₁ ] τ₁
    -- λx.M
    CPSKLet : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
              (e : var τ₁  cpsterm[ var , Δ ] τ₂) 
              cpscont[ var , Δ , τ₁ ] τ₂

-- example
-- λx.λk.k x
val1 : {var : cpstyp  Set}  {τ₁ τ₂ : cpstyp} 
       cpsvalue[ var ] (τ₁ ⇒[ τ₁  τ₂ ]⇒ τ₂)
val1 = CPSFun  x  CPSRet CPSKVar (CPSVar x))

-- interpreter
〚_〛 : cpstyp  Set
 Nat  = 
 τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄  =
   τ₂   ( τ₁    τ₃ )   τ₄ 

〚_〛' : conttyp  Set
 K τ  α 〛' =  τ    α 
  τ 〛' =  τ    τ 

mutual
  gv : {τ : cpstyp}  cpsvalue[ 〚_〛 ] τ   τ 
  gv (CPSVar x) = x
  gv (CPSNum n) = n
  gv (CPSFun e) = λ x k  g (e x) k
  gv CPSShift = λ v k  v  x₂ k₂  k₂ (k x₂))  x  x)

  g : {τ : cpstyp}  {Δ : conttyp} 
      cpsterm[ 〚_〛 , Δ ] τ   Δ 〛'   τ 
  g (CPSRet k v) Δ = (gc k Δ) (gv v)
  g (CPSApp v w k) Δ = (gv v) (gv w) (gc k Δ)
  g (CPSShift2 e k) Δ = g (e λ x₂ k₂  k₂ (gc k Δ x₂))  x  x)
  g (CPSRetE k e) Δ = (gc k Δ) (g e  x  x))

  gc : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
       cpscont[ 〚_〛 , Δ , τ₁ ] τ₂   Δ 〛' 
        τ₁    τ₂ 
  gc CPSKVar k = k
  gc CPSKId Δ = λ x  x
  gc (CPSKLet e) Δ = λ x  g (e x) Δ

-- 値による代入規則
mutual
  data cpsSubstV {var : cpstyp  Set} : {τ τ₁ : cpstyp} 
                 (var τ  cpsvalue[ var ] τ₁) 
                 cpsvalue[ var ] τ 
                 cpsvalue[ var ] τ₁  Set where
    sVar= : {τ : cpstyp} {v : cpsvalue[ var ] τ} 
            cpsSubstV  x  CPSVar x) v v
    sVar≠ : {τ τ₁ : cpstyp} {v : cpsvalue[ var ] τ} {x : var τ₁} 
            cpsSubstV  _  CPSVar x) v (CPSVar x)
    sNum  : {τ : cpstyp} {v : cpsvalue[ var ] τ} {n : } 
            cpsSubstV  _  CPSNum n) v (CPSNum n)
    sFun  : {τ′ τ₀ τ₁ τ₃ τ₄ : cpstyp} 
            {e  : var τ′   var τ₀  cpsterm[ var , K τ₁  τ₃ ] τ₄} 
            {v  : cpsvalue[ var ] τ′} 
            {e′ : var τ₀  cpsterm[ var , K τ₁  τ₃ ] τ₄} 
            ((x : var τ₀)  cpsSubst  y  (e y) x) v (e′ x)) 
            cpsSubstV  y  CPSFun  x  (e y) x)) v (CPSFun e′)
    sShift : {τ₁ τ₂ τ₃ τ₄ τ₅ τ : cpstyp} {v : cpsvalue[ var ] τ} 
            cpsSubstV  _  CPSShift {τ₁ = τ₁} {τ₂} {τ₃} {τ₄} {τ₅})
                      v CPSShift

  data cpsSubst {var : cpstyp  Set} : {τ₁ τ₂ : cpstyp} {Δ : conttyp} 
                (var τ₁  cpsterm[ var , Δ ] τ₂) 
                cpsvalue[ var ] τ₁ 
                cpsterm[ var , Δ ] τ₂  Set where
    sRet  : {τ τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
            {k₁ : var τ  cpscont[ var , Δ , τ₁ ] τ₂} 
            {v₁ : var τ  cpsvalue[ var ] τ₁} 
            {v  : cpsvalue[ var ] τ} 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₂} 
            {v₂ : cpsvalue[ var ] τ₁} 
            cpsSubstC k₁ v k₂  cpsSubstV v₁ v v₂ 
            cpsSubst  y  CPSRet (k₁ y) (v₁ y)) v (CPSRet k₂ v₂)
    sApp  : {τ τ₁ τ₂ τ₃ τ₄ : cpstyp}  {Δ : conttyp} 
            {v₁ : var τ  cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄) } 
            {w₁ : var τ  cpsvalue[ var ] τ₂ } 
            {k₁ : var τ  cpscont[ var , Δ , τ₁ ] τ₃ } 
            {v  : cpsvalue[ var ] τ } 
            {v₂ : cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄) } 
            {w₂ : cpsvalue[ var ] τ₂ } 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₃ } 
            cpsSubstV v₁ v v₂ 
            cpsSubstV w₁ v w₂ 
            cpsSubstC k₁ v k₂ 
            cpsSubst  y  CPSApp (v₁ y) (w₁ y) (k₁ y)) v
                     (CPSApp v₂ w₂ k₂)
    sShift2 : {τ τ₁ τ₂ τ₃ τ₄ τ₅ : cpstyp}  {Δ : conttyp} 
            {e₁ : var τ  var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) 
                  cpsterm[ var , K τ₄  τ₄ ] τ₅} 
            {k₁ : var τ  cpscont[ var , Δ , τ₁ ] τ₂} 
            {v  : cpsvalue[ var ] τ} 
            {e₂ : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) 
                  cpsterm[ var , K τ₄  τ₄ ] τ₅} 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₂} 
            ((x : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃)) 
             cpsSubst  y  (e₁ y) x) v (e₂ x)) 
            cpsSubstC k₁ v k₂ 
            cpsSubst  y  (CPSShift2 (e₁ y) (k₁ y))) v (CPSShift2 e₂ k₂)
    sRetE : {τ τ₁ τ₂ α : cpstyp}  {Δ : conttyp} 
            {k₁ : var τ  cpscont[ var , Δ , τ₁ ] τ₂} 
            {e₁ : var τ  cpsterm[ var ,  α ] τ₁} 
            {v  : cpsvalue[ var ] τ} 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₂} 
            {e₂ : cpsterm[ var ,  α ] τ₁} 
            cpsSubstC k₁ v k₂  cpsSubst e₁ v e₂ 
            cpsSubst  y  CPSRetE (k₁ y) (e₁ y)) v (CPSRetE k₂ e₂)

  data cpsSubstC {var : cpstyp  Set} :
                 {τ τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
                 (var τ  cpscont[ var , Δ , τ₁ ] τ₂) 
                 cpsvalue[ var ] τ 
                 cpscont[ var , Δ , τ₁ ] τ₂  Set where
    sKVar≠ : {τ τ₁ τ₂ : cpstyp} 
             {v : cpsvalue[ var ] τ} 
             cpsSubstC {τ₁ = τ₁} {τ₂}  _  CPSKVar) v CPSKVar
    sKId   : {τ τ₁ : cpstyp} 
             {v : cpsvalue[ var ] τ} 
             cpsSubstC {τ₁ = τ₁}  _  CPSKId) v CPSKId
    sKLet  : {τ τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
             {e₁ : var τ  var τ₁  cpsterm[ var , Δ ] τ₂} 
             {v  : cpsvalue[ var ] τ} 
             {e₂ : var τ₁  cpsterm[ var , Δ ] τ₂ } 
             ((x : var τ₁)  cpsSubst  y  (e₁ y) x) v (e₂ x)) 
             cpsSubstC  y  CPSKLet (e₁ y)) v (CPSKLet e₂)

-- 継続の代入規則
mutual
  data cpsSubst₂ {var : cpstyp  Set} :
                 {τ₁ α β : cpstyp}  {Δ : conttyp} 
                 cpsterm[ var , K α  β ] τ₁  -- has to be K
                 cpscont[ var , Δ , α ] β 
                 cpsterm[ var , Δ ] τ₁  Set where
    sRet  : {τ₁ τ₂ α β : cpstyp}  {Δ : conttyp} 
            {k₁ : cpscont[ var , K α  β , τ₁ ] τ₂} 
            {v  : cpsvalue[ var ] τ₁} 
            {c  : cpscont[ var , Δ , α ] β} 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₂} 
            cpsSubstC₂ k₁ c k₂ 
            cpsSubst₂ (CPSRet k₁ v) c (CPSRet k₂ v)
    sApp  : {τ₁ τ₂ τ₃ τ₄ α β : cpstyp}  {Δ : conttyp} 
            {v  : cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄) } 
            {w  : cpsvalue[ var ] τ₂ } 
            {k₁ : cpscont[ var , K α  β , τ₁ ] τ₃ } 
            {c  : cpscont[ var , Δ , α ] β } 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₃ } 
            cpsSubstC₂ k₁ c k₂ 
            cpsSubst₂ (CPSApp v w k₁) c (CPSApp v w k₂)
    sShift2 : {τ₁ τ₂ τ₃ τ₄ τ₅ α β : cpstyp}  {Δ : conttyp} 
            {e  : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) 
                  cpsterm[ var , K τ₄  τ₄ ] τ₅} 
            {k₁ : cpscont[ var , K α  β , τ₁ ] τ₂} 
            {c  : cpscont[ var , Δ , α ] β} 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₂} 
            cpsSubstC₂ k₁ c k₂ 
            cpsSubst₂ (CPSShift2 e k₁) c (CPSShift2 e k₂)
    sRetE : {τ₁ τ₂ α β γ : cpstyp}  {Δ : conttyp} 
            {k₁ : cpscont[ var , K α  β , τ₁ ] τ₂} 
            {e  : cpsterm[ var ,  γ ] τ₁} 
            {c  : cpscont[ var , Δ , α ] β} 
            {k₂ : cpscont[ var , Δ , τ₁ ] τ₂} 
            cpsSubstC₂ k₁ c k₂ 
            cpsSubst₂ (CPSRetE k₁ e) c (CPSRetE k₂ e)

  data cpsSubstC₂ {var : cpstyp  Set} :
                  {τ₁ τ₂ α β : cpstyp}  {Δ : conttyp} 
                  cpscont[ var , K α  β , τ₁ ] τ₂ 
                  cpscont[ var , Δ , α ] β 
                  cpscont[ var , Δ , τ₁ ] τ₂  Set where
    sKVar= : {α β : cpstyp} {Δ : conttyp} 
             {c : cpscont[ var , Δ , α ] β} 
             cpsSubstC₂ CPSKVar c c
    sKLet  : {τ₁ τ₂ α β : cpstyp}  {Δ : conttyp} 
             {e₁ : var τ₁  cpsterm[ var , K α  β ] τ₂} 
             {c  : cpscont[ var , Δ , α ] β} 
             {e₂ : var τ₁  cpsterm[ var , Δ ] τ₂} 
             ((x : var τ₁)  cpsSubst₂ (e₁ x) c (e₂ x)) 
             cpsSubstC₂ (CPSKLet e₁) c (CPSKLet e₂)

--reduction rules
mutual
  data cpsReduce {var : cpstyp  Set} :
                 {τ₁ : cpstyp}  {Δ : conttyp} 
                 cpsterm[ var , Δ ] τ₁ 
                 cpsterm[ var , Δ ] τ₁  Set where
     -- (λx.λk.M) V K -> M[x:=V][k:=K]
     RBetaV  : {τ₁ τ₂ τ₃ τ₄ : cpstyp}  {Δ : conttyp} 
               {e₁ : var τ₂  cpsterm[ var , K τ₁  τ₃ ] τ₄} 
               {v : cpsvalue[ var ] τ₂} 
               {c : cpscont[ var , Δ , τ₁ ] τ₃} 
               {e₁′ : cpsterm[ var , K τ₁  τ₃ ] τ₄} 
               {e₂ : cpsterm[ var , Δ ] τ₄} 
               cpsSubst e₁ v e₁′ 
               cpsSubst₂ e₁′ c e₂ 
               cpsReduce (CPSApp (CPSFun  x  e₁ x)) v c)
                         e₂
     -- (λx.M) V -> M[x:=V]
     RBetaLet : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {e : var τ₁  cpsterm[ var , Δ ] τ₂} 
               {v : cpsvalue[ var ] τ₁} 
               {e′ : cpsterm[ var , Δ ] τ₂} 
               cpsSubst e v e′ 
               cpsReduce (CPSRet (CPSKLet e) v) e′
     -- K (S@V@J) -> K (V@(λy.λk.k@(J@y))@(λx.x))
     RShift  : {τ₁ τ₂ τ₃ τ₄ τ₅ τ₆ : cpstyp}  {Δ : conttyp} 
               {v : cpsvalue[ var ]
                    ((τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) ⇒[ τ₄  τ₄ ]⇒ τ₅)} 
               {j : cpscont[ var ,  τ₄ , τ₁ ] τ₂} 
               {k : cpscont[ var , Δ , τ₅ ] τ₆} 
               cpsReduce (CPSRetE k (CPSApp CPSShift v j))
                         (CPSRetE k (CPSApp v
                                       (CPSFun  y 
                                         CPSRetE CPSKVar (CPSRet j (CPSVar y))))
                                       CPSKId))
     -- K (S@V@J) -> K (V@(λy.λk.k@(J@y))@(λx.x))
     RShift2 : {τ₁ τ₂ τ₃ τ₄ τ₅ τ₆ : cpstyp}  {Δ : conttyp} 
               {e : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) 
                    cpsterm[ var , K τ₄  τ₄ ] τ₅} 
               {j : cpscont[ var ,  τ₄ , τ₁ ] τ₂} 
               {k : cpscont[ var , Δ , τ₅ ] τ₆} 
               cpsReduce (CPSRetE k (CPSShift2 e j))
                         (CPSRetE k (CPSApp (CPSFun e)
                                       (CPSFun  y 
                                         CPSRetE CPSKVar (CPSRet j (CPSVar y))))
                                       CPSKId))
     -- K ((λx.x) V) -> K V
     RReset  : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {v : cpsvalue[ var ] τ₁} 
               {k : cpscont[ var , Δ , τ₁ ] τ₂} 
               cpsReduce (CPSRetE k (CPSRet CPSKId v))
                         (CPSRet k v)

     -- congruence rules
     RRet₁   : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {k k' : cpscont[ var , Δ , τ₁ ] τ₂} 
               {v : cpsvalue[ var ] τ₁} 
               cpsReduceC k k' 
               cpsReduce (CPSRet k v)
                         (CPSRet k' v)
     RRet₂   : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {k : cpscont[ var , Δ , τ₁ ] τ₂} 
               {v v' : cpsvalue[ var ] τ₁} 
               cpsReduceV v v' 
               cpsReduce (CPSRet k v)
                         (CPSRet k v')
     RApp₁   : {τ₁ τ₂ τ₃ τ₄ : cpstyp}  {Δ : conttyp} 
               {v v' : cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄)} 
               {w : cpsvalue[ var ] τ₂} 
               {k : cpscont[ var , Δ , τ₁ ] τ₃} 
               cpsReduceV v v' 
               cpsReduce (CPSApp v w k)
                         (CPSApp v' w k)
     RApp₂   : {τ₁ τ₂ τ₃ τ₄ : cpstyp}  {Δ : conttyp} 
               {v : cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄)} 
               {w w' : cpsvalue[ var ] τ₂} 
               {k : cpscont[ var , Δ , τ₁ ] τ₃} 
               cpsReduceV w w' 
               cpsReduce (CPSApp v w k)
                         (CPSApp v w' k)
     RApp₃   : {τ₁ τ₂ τ₃ τ₄ : cpstyp}  {Δ : conttyp} 
               {v : cpsvalue[ var ] (τ₂ ⇒[ τ₁  τ₃ ]⇒ τ₄)} 
               {w : cpsvalue[ var ] τ₂} 
               {k k' : cpscont[ var , Δ , τ₁ ] τ₃} 
               cpsReduceC k k' 
               cpsReduce (CPSApp v w k)
                         (CPSApp v w k')
     RShift₁  : {τ₁ τ₂ τ₃ τ₄ τ₅ : cpstyp}  {Δ : conttyp} 
               {k k' : cpscont[ var , Δ , τ₁ ] τ₂} 
               {e : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) 
                        cpsterm[ var , K τ₄  τ₄ ] τ₅} 
               cpsReduceC k k' 
               cpsReduce (CPSShift2 e k) (CPSShift2 e k')
     RShift₂  : {τ₁ τ₂ τ₃ τ₄ τ₅ : cpstyp}  {Δ : conttyp} 
               {k : cpscont[ var , Δ , τ₁ ] τ₂} 
               {e e' : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃) 
                       cpsterm[ var , K τ₄  τ₄ ] τ₅} 
               ((x : var (τ₁ ⇒[ τ₂  τ₃ ]⇒ τ₃)) 
               cpsReduce (e x) (e' x)) 
               cpsReduce (CPSShift2 e k) (CPSShift2 e' k)
     RRetE₁  : {τ τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {k k' : cpscont[ var , Δ , τ₁ ] τ₂} 
               {e : cpsterm[ var ,  τ ] τ₁} 
               cpsReduceC k k' 
               cpsReduce (CPSRetE k e)
                         (CPSRetE k' e)
     RRetE₂  : {τ τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {k : cpscont[ var , Δ , τ₁ ] τ₂} 
               {e e' : cpsterm[ var ,  τ ] τ₁} 
               cpsReduce e e' 
               cpsReduce (CPSRetE k e)
                         (CPSRetE k e')
     -- closure rules
     RId     : {τ₁ : cpstyp} {Δ : conttyp} 
               {e : cpsterm[ var , Δ ] τ₁} 
               cpsReduce e e
     RTrans  : {τ₁ : cpstyp} {Δ : conttyp} 
               {e₁ e₂ e₃ : cpsterm[ var , Δ ] τ₁} 
               cpsReduce e₁ e₂ 
               cpsReduce e₂ e₃ 
               cpsReduce e₁ e₃

  data cpsReduceV {var : cpstyp  Set} :
                  {τ₁ : cpstyp} 
                  cpsvalue[ var ] τ₁ 
                  cpsvalue[ var ] τ₁  Set where
     -- λx.λk.V x k -> V
     REtaV   : {τ₀ τ₁ τ₃ τ₄ : cpstyp} 
               (v : cpsvalue[ var ] (τ₀ ⇒[ τ₁  τ₃ ]⇒ τ₄)) 
               cpsReduceV (CPSFun  x  CPSApp v (CPSVar x) CPSKVar)) v
     -- congruence rule
     RFun    : {τ₀ τ₁ τ₃ τ₄ : cpstyp} 
               (e e' : var τ₀  cpsterm[ var , K τ₁  τ₃ ] τ₄) 
               ((x : var τ₀)  cpsReduce (e x) (e' x)) 
               cpsReduceV (CPSFun e) (CPSFun e')
     -- closure rules
     RId     : {τ₁ : cpstyp} 
               {v : cpsvalue[ var ] τ₁} 
               cpsReduceV v v
     RTrans  : {τ₁ : cpstyp} 
               {v₁ v₂ v₃ : cpsvalue[ var ] τ₁} 
               cpsReduceV v₁ v₂ 
               cpsReduceV v₂ v₃ 
               cpsReduceV v₁ v₃

  data cpsReduceC {var : cpstyp  Set} :
                  {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
                  cpscont[ var , Δ , τ₁ ] τ₂ 
                  cpscont[ var , Δ , τ₁ ] τ₂  Set where
     -- (λx.K x) -> K
     REtaLet : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               (k : cpscont[ var , Δ , τ₁ ] τ₂) 
               cpsReduceC (CPSKLet  x  CPSRet k (CPSVar x))) k

     -- congruence rule
     RKLet   : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {e e' : var τ₁  cpsterm[ var , Δ ] τ₂} 
               ((x : var τ₁)  cpsReduce (e x) (e' x)) 
               cpsReduceC (CPSKLet e) (CPSKLet e')
     -- closure rules
     RId     : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {k : cpscont[ var , Δ , τ₁ ] τ₂} 
               cpsReduceC k k
     RTrans  : {τ₁ τ₂ : cpstyp}  {Δ : conttyp} 
               {k₁ k₂ k₃ : cpscont[ var , Δ , τ₁ ] τ₂} 
               cpsReduceC k₁ k₂ 
               cpsReduceC k₂ k₃ 
               cpsReduceC k₁ k₃

-- equational reasoning
module Reasoning where

  open import Relation.Binary.PropositionalEquality

  infix  3 _∎
  infixr 2 _⟶⟨_⟩_ _≡⟨_⟩_
  infix  1 begin_

  begin_ : {var : cpstyp  Set} {τ₁ : cpstyp} {Δ : conttyp} 
           {e₁ e₂ : cpsterm[ var , Δ ] τ₁} 
           cpsReduce e₁ e₂  cpsReduce e₁ e₂
  begin_ red = red

  _⟶⟨_⟩_ : {var : cpstyp  Set} {τ₁ : cpstyp} {Δ : conttyp} 
            (e₁ {e₂ e₃} : cpsterm[ var , Δ ] τ₁) 
            cpsReduce e₁ e₂  cpsReduce e₂ e₃  cpsReduce e₁ e₃
  _⟶⟨_⟩_ e₁ {e₂} {e₃} e₁-red-e₂ e₂-red-e₃ = RTrans e₁-red-e₂ e₂-red-e₃

  _≡⟨_⟩_ : {var : cpstyp  Set} {τ₁ : cpstyp} {Δ : conttyp} 
           (e₁ {e₂ e₃} : cpsterm[ var , Δ ] τ₁) 
           e₁  e₂  cpsReduce e₂ e₃ 
           cpsReduce e₁ e₃
  _≡⟨_⟩_ e₁ {e₂} {e₃} refl e₂-red-e₃ = e₂-red-e₃

  _∎ : {var : cpstyp  Set} {τ₁ : cpstyp} {Δ : conttyp} 
       (e : cpsterm[ var , Δ ] τ₁)  cpsReduce e e
  _∎ e = RId

-- lemma
mutual
  cpsSubstV≠ : {var : cpstyp  Set} {τ₁ τ : cpstyp} 
               (v₁ : cpsvalue[ var ] τ₁) 
               {v : cpsvalue[ var ] τ} 
               cpsSubstV  _  v₁) v v₁
  cpsSubstV≠ (CPSVar x) = sVar≠
  cpsSubstV≠ (CPSNum n) = sNum
  cpsSubstV≠ (CPSFun e) = sFun  x  cpsSubst≠ (e x))
  cpsSubstV≠ CPSShift = sShift

  cpsSubst≠ : {var : cpstyp  Set} {τ₄ τ : cpstyp} {Δ : conttyp} 
              (e₁ : cpsterm[ var , Δ ] τ₄) 
              {v : cpsvalue[ var ] τ} 
              cpsSubst  _  e₁) v e₁
  cpsSubst≠ (CPSRet k v) = sRet (cpsSubstC≠ k) (cpsSubstV≠ v)
  cpsSubst≠ (CPSApp v w k) = sApp (cpsSubstV≠ v) (cpsSubstV≠ w) (cpsSubstC≠ k)
  cpsSubst≠ (CPSShift2 e k) = sShift2  k  cpsSubst≠ (e k)) (cpsSubstC≠ k)
  cpsSubst≠ (CPSRetE k e) = sRetE (cpsSubstC≠ k) (cpsSubst≠ e)

  cpsSubstC≠ : {var : cpstyp  Set} {τ₂ τ₄ τ : cpstyp} {Δ : conttyp} 
               (k₁ : cpscont[ var , Δ , τ₂ ] τ₄) 
               {v : cpsvalue[ var ] τ} 
               cpsSubstC  _  k₁) v k₁
  cpsSubstC≠ CPSKVar = sKVar≠
  cpsSubstC≠ CPSKId = sKId
  cpsSubstC≠ (CPSKLet e) = sKLet λ x  cpsSubst≠ (e x)