{-# OPTIONS --sized-types #-}

-- Basic intuitionistic modal logic S4, without ∨, ⊥, or ◇.
-- Gentzen-style formalisation of syntax with context pairs, after Pfenning-Davies.

module A201607.BasicIS4.Syntax.DyadicGentzen where

open import A201607.BasicIS4.Syntax.Common public


-- Derivations.

infix 3 _⊢_
data _⊢_ : Cx² Ty Ty → Ty → Set where
  var   : ∀ {A Γ Δ}   → A ∈ Γ → Γ ⁏ Δ ⊢ A
  lam   : ∀ {A B Γ Δ} → Γ , A ⁏ Δ ⊢ B → Γ ⁏ Δ ⊢ A ▻ B
  app   : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ B
  mvar  : ∀ {A Γ Δ}   → A ∈ Δ → Γ ⁏ Δ ⊢ A
  box   : ∀ {A Γ Δ}   → ∅ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ □ A
  unbox : ∀ {A C Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ , A ⊢ C → Γ ⁏ Δ ⊢ C
  pair  : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ B → Γ ⁏ Δ ⊢ A ∧ B
  fst   : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ∧ B → Γ ⁏ Δ ⊢ A
  snd   : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ∧ B → Γ ⁏ Δ ⊢ B
  unit  : ∀ {Γ Δ}     → Γ ⁏ Δ ⊢ ⊤

infix 3 _⊢⋆_
_⊢⋆_ : Cx² Ty Ty → Cx Ty → Set
Γ ⁏ Δ ⊢⋆ ∅     = 𝟙
Γ ⁏ Δ ⊢⋆ Ξ , A = Γ ⁏ Δ ⊢⋆ Ξ × Γ ⁏ Δ ⊢ A


-- Inversion principles.

invvar : ∀ {A Γ Δ} {i i′ : A ∈ Γ} → var {Δ = Δ} i ≡ var i′ → i ≡ i′
invvar refl = refl

invlam : ∀ {A B Γ Δ} {t t′ : Γ , A ⁏ Δ ⊢ B} → lam t ≡ lam t′ → t ≡ t′
invlam refl = refl

invappᵀ : ∀ {A A′ B Γ Δ} {t : Γ ⁏ Δ ⊢ A ▻ B} {t′ : Γ ⁏ Δ ⊢ A′ ▻ B} {u : Γ ⁏ Δ ⊢ A} {u′ : Γ ⁏ Δ ⊢ A′} → app t u ≡ app t′ u′ → A ≡ A′
invappᵀ refl = refl

invapp₁ : ∀ {A B Γ Δ} {t t′ : Γ ⁏ Δ ⊢ A ▻ B} {u u′ : Γ ⁏ Δ ⊢ A} → app t u ≡ app t′ u′ → t ≡ t′
invapp₁ refl = refl

invapp₂ : ∀ {A B Γ Δ} {t t′ : Γ ⁏ Δ ⊢ A ▻ B} {u u′ : Γ ⁏ Δ ⊢ A} → app t u ≡ app t′ u′ → u ≡ u′
invapp₂ refl = refl

invmvar : ∀ {A Γ Δ} {i i′ : A ∈ Δ} → mvar {Γ = Γ} i ≡ mvar i′ → i ≡ i′
invmvar refl = refl

invbox : ∀ {A Γ Δ} {t t′ : ∅ ⁏ Δ ⊢ A} → box {Γ = Γ} t ≡ box t′ → t ≡ t′
invbox refl = refl

invunboxᵀ : ∀ {A A′ C Γ Δ} {t : Γ ⁏ Δ ⊢ □ A} {t′ : Γ ⁏ Δ ⊢ □ A′} {u : Γ ⁏ Δ , A ⊢ C} {u′ : Γ ⁏ Δ , A′ ⊢ C} → unbox t u ≡ unbox t′ u′ → A ≡ A′
invunboxᵀ refl = refl

invunbox₁ : ∀ {A C Γ Δ} {t t′ : Γ ⁏ Δ ⊢ □ A} {u u′ : Γ ⁏ Δ , A ⊢ C} → unbox t u ≡ unbox t′ u′ → t ≡ t′
invunbox₁ refl = refl

invunbox₂ : ∀ {A C Γ Δ} {t t′ : Γ ⁏ Δ ⊢ □ A} {u u′ : Γ ⁏ Δ , A ⊢ C} → unbox t u ≡ unbox t′ u′ → u ≡ u′
invunbox₂ refl = refl

invpair₁ : ∀ {A B Γ Δ} {t t′ : Γ ⁏ Δ ⊢ A} {u u′ : Γ ⁏ Δ ⊢ B} → pair t u ≡ pair t′ u′ → t ≡ t′
invpair₁ refl = refl

invpair₂ : ∀ {A B Γ Δ} {t t′ : Γ ⁏ Δ ⊢ A} {u u′ : Γ ⁏ Δ ⊢ B} → pair t u ≡ pair t′ u′ → u ≡ u′
invpair₂ refl = refl

invfstᵀ : ∀ {A B B′ Γ Δ} {t : Γ ⁏ Δ ⊢ A ∧ B} {t′ : Γ ⁏ Δ ⊢ A ∧ B′} → fst t ≡ fst t′ → B ≡ B′
invfstᵀ refl = refl

invfst : ∀ {A B Γ Δ} {t t′ : Γ ⁏ Δ ⊢ A ∧ B} → fst t ≡ fst t′ → t ≡ t′
invfst refl = refl

invsndᵀ : ∀ {A A′ B Γ Δ} {t : Γ ⁏ Δ ⊢ A ∧ B} {t′ : Γ ⁏ Δ ⊢ A′ ∧ B} → snd t ≡ snd t′ → A ≡ A′
invsndᵀ refl = refl

invsnd : ∀ {A B Γ Δ} {t t′ : Γ ⁏ Δ ⊢ A ∧ B} → snd t ≡ snd t′ → t ≡ t′
invsnd refl = refl


-- Decidable equality.

_≟_ : ∀ {A Γ Δ} → (t t′ : Γ ⁏ Δ ⊢ A) → Dec (t ≡ t′)
var i     ≟ var i′      with i ≟ⁱ i′
var i     ≟ var .i      | yes refl = yes refl
var i     ≟ var i′      | no  i≢i′ = no (i≢i′ ∘ invvar)
var i     ≟ lam t′      = no λ ()
var i     ≟ app t′ u′   = no λ ()
var i     ≟ mvar i′     = no λ ()
var i     ≟ box t′      = no λ ()
var i     ≟ unbox t′ u′ = no λ ()
var i     ≟ pair t′ u′  = no λ ()
var i     ≟ fst t′      = no λ ()
var i     ≟ snd t′      = no λ ()
var i     ≟ unit        = no λ ()
lam t     ≟ var i′      = no λ ()
lam t     ≟ lam t′      with t ≟ t′
lam t     ≟ lam .t      | yes refl = yes refl
lam t     ≟ lam t′      | no  t≢t′ = no (t≢t′ ∘ invlam)
lam t     ≟ app t′ u′   = no λ ()
lam t     ≟ mvar i′     = no λ ()
lam t     ≟ unbox t′ u′ = no λ ()
lam t     ≟ fst t′      = no λ ()
lam t     ≟ snd t′      = no λ ()
app t u   ≟ var i′      = no λ ()
app t u   ≟ lam t′      = no λ ()
app {A = A} t u ≟ app {A = A′} t′ u′ with A ≟ᵀ A′
app t u   ≟ app t′ u′   | yes refl with t ≟ t′ | u ≟ u′
app t u   ≟ app .t .u   | yes refl | yes refl | yes refl = yes refl
app t u   ≟ app t′ u′   | yes refl | no  t≢t′ | _        = no (t≢t′ ∘ invapp₁)
app t u   ≟ app t′ u′   | yes refl | _        | no  u≢u′ = no (u≢u′ ∘ invapp₂)
app t u   ≟ app t′ u′   | no  A≢A′ = no (A≢A′ ∘ invappᵀ)
app t u   ≟ mvar i′     = no λ ()
app t u   ≟ box t′      = no λ ()
app t u   ≟ unbox t′ u′ = no λ ()
app t u   ≟ pair t′ u′  = no λ ()
app t u   ≟ fst t′      = no λ ()
app t u   ≟ snd t′      = no λ ()
app t u   ≟ unit        = no λ ()
mvar i    ≟ var i′      = no λ ()
mvar i    ≟ lam t′      = no λ ()
mvar i    ≟ app t′ u′   = no λ ()
mvar i    ≟ mvar i′     with i ≟ⁱ i′
mvar i    ≟ mvar .i     | yes refl = yes refl
mvar i    ≟ mvar i′     | no  i≢i′ = no (i≢i′ ∘ invmvar)
mvar i    ≟ box t′      = no λ ()
mvar i    ≟ unbox t′ u′ = no λ ()
mvar i    ≟ pair t′ u′  = no λ ()
mvar i    ≟ fst t′      = no λ ()
mvar i    ≟ snd t′      = no λ ()
mvar i    ≟ unit        = no λ ()
box t     ≟ var i′      = no λ ()
box t     ≟ app t′ u′   = no λ ()
box t     ≟ mvar i′     = no λ ()
box t     ≟ box t′      with t ≟ t′
box t     ≟ box .t      | yes refl = yes refl
box t     ≟ box t′      | no  t≢t′ = no (t≢t′ ∘ invbox)
box t     ≟ unbox t′ u′ = no λ ()
box t     ≟ fst t′      = no λ ()
box t     ≟ snd t′      = no λ ()
unbox t u ≟ var i′      = no λ ()
unbox t u ≟ lam t′      = no λ ()
unbox t u ≟ app t′ u′   = no λ ()
unbox t u ≟ mvar i′     = no λ ()
unbox t u ≟ box t′      = no λ ()
unbox {A = A} t u ≟ unbox {A = A′} t′ u′ with A ≟ᵀ A′
unbox t u ≟ unbox t′ u′ | yes refl with t ≟ t′ | u ≟ u′
unbox t u ≟ unbox .t .u | yes refl | yes refl | yes refl = yes refl
unbox t u ≟ unbox t′ u′ | yes refl | no  t≢t′ | _        = no (t≢t′ ∘ invunbox₁)
unbox t u ≟ unbox t′ u′ | yes refl | _        | no  u≢u′ = no (u≢u′ ∘ invunbox₂)
unbox t u ≟ unbox t′ u′ | no  A≢A′ = no (A≢A′ ∘ invunboxᵀ)
unbox t u ≟ pair t′ u′  = no λ ()
unbox t u ≟ fst t′      = no λ ()
unbox t u ≟ snd t′      = no λ ()
unbox t u ≟ unit        = no λ ()
pair t u  ≟ var i′      = no λ ()
pair t u  ≟ app t′ u′   = no λ ()
pair t u  ≟ mvar i′     = no λ ()
pair t u  ≟ unbox t′ u′ = no λ ()
pair t u  ≟ pair t′ u′  with t ≟ t′ | u ≟ u′
pair t u  ≟ pair .t .u  | yes refl | yes refl = yes refl
pair t u  ≟ pair t′ u′  | no  t≢t′ | _        = no (t≢t′ ∘ invpair₁)
pair t u  ≟ pair t′ u′  | _        | no  u≢u′ = no (u≢u′ ∘ invpair₂)
pair t u  ≟ fst t′      = no λ ()
pair t u  ≟ snd t′      = no λ ()
fst t     ≟ var i′      = no λ ()
fst t     ≟ lam t′      = no λ ()
fst t     ≟ app t′ u′   = no λ ()
fst t     ≟ mvar i′     = no λ ()
fst t     ≟ box t′      = no λ ()
fst t     ≟ unbox t′ u′ = no λ ()
fst t     ≟ pair t′ u′  = no λ ()
fst {B = B} t ≟ fst {B = B′} t′ with B ≟ᵀ B′
fst t     ≟ fst t′      | yes refl with t ≟ t′
fst t     ≟ fst .t      | yes refl | yes refl = yes refl
fst t     ≟ fst t′      | yes refl | no  t≢t′ = no (t≢t′ ∘ invfst)
fst t     ≟ fst t′      | no  B≢B′ = no (B≢B′ ∘ invfstᵀ)
fst t     ≟ snd t′      = no λ ()
fst t     ≟ unit        = no λ ()
snd t     ≟ var i′      = no λ ()
snd t     ≟ lam t′      = no λ ()
snd t     ≟ app t′ u′   = no λ ()
snd t     ≟ mvar i′     = no λ ()
snd t     ≟ box t′      = no λ ()
snd t     ≟ unbox t′ u′ = no λ ()
snd t     ≟ pair t′ u′  = no λ ()
snd t     ≟ fst t′      = no λ ()
snd {A = A} t ≟ snd {A = A′} t′ with A ≟ᵀ A′
snd t     ≟ snd t′      | yes refl with t ≟ t′
snd t     ≟ snd .t      | yes refl | yes refl = yes refl
snd t     ≟ snd t′      | yes refl | no  t≢t′ = no (t≢t′ ∘ invsnd)
snd t     ≟ snd t′      | no  A≢A′ = no (A≢A′ ∘ invsndᵀ)
snd t     ≟ unit        = no λ ()
unit      ≟ var i′      = no λ ()
unit      ≟ app t′ u′   = no λ ()
unit      ≟ mvar i′     = no λ ()
unit      ≟ unbox t′ u′ = no λ ()
unit      ≟ fst t′      = no λ ()
unit      ≟ snd t′      = no λ ()
unit      ≟ unit        = yes refl


-- Structural induction.

private
  data _≤_ : ∀ {A′ A Γ′ Γ Δ′ Δ} → Γ′ ⁏ Δ′ ⊢ A′ → Γ ⁏ Δ ⊢ A → Set where
    refl≤       : ∀ {A Γ Δ}   → {t : Γ ⁏ Δ ⊢ A}
                              → t ≤ t

    steplam≤    : ∀ {A B Γ Δ} → {t′ t : Γ , A ⁏ Δ ⊢ B}
                              → t′ ≡ t → t′ ≤ lam t

    stepapp≤₁   : ∀ {A B Γ Δ} → {t′ t : Γ ⁏ Δ ⊢ A ▻ B} → {u : Γ ⁏ Δ ⊢ A}
                              → t′ ≡ t → t′ ≤ app t u

    stepapp≤₂   : ∀ {A B Γ Δ} → {t : Γ ⁏ Δ ⊢ A ▻ B} → {u′ u : Γ ⁏ Δ ⊢ A}
                              → u′ ≡ u → u′ ≤ app t u

    stepbox≤    : ∀ {A Γ Δ}   → {t′ t : ∅ ⁏ Δ ⊢ A}
                              → t′ ≡ t → t′ ≤ box {Γ = Γ} t

    stepunbox≤₁ : ∀ {A C Γ Δ} → {t′ t : Γ ⁏ Δ ⊢ □ A} → {u : Γ ⁏ Δ , A ⊢ C}
                              → t′ ≡ t → t′ ≤ unbox t u

    stepunbox≤₂ : ∀ {A C Γ Δ} → {t : Γ ⁏ Δ ⊢ □ A} → {u′ u : Γ ⁏ Δ , A ⊢ C}
                              → u′ ≡ u → u′ ≤ unbox t u

    steppair≤₁  : ∀ {A B Γ Δ} → {t′ t : Γ ⁏ Δ ⊢ A} → {u : Γ ⁏ Δ ⊢ B}
                              → t′ ≡ t → t′ ≤ pair t u

    steppair≤₂  : ∀ {A B Γ Δ} → {t : Γ ⁏ Δ ⊢ A} → {u′ u : Γ ⁏ Δ ⊢ B}
                              → u′ ≡ u → u′ ≤ pair t u

    stepfst≤    : ∀ {A B Γ Δ} → {t′ t : Γ ⁏ Δ ⊢ A ∧ B}
                              → t′ ≡ t → t′ ≤ fst t

    stepsnd≤    : ∀ {A B Γ Δ} → {t′ t : Γ ⁏ Δ ⊢ A ∧ B}
                              → t′ ≡ t → t′ ≤ snd t

  _<_ : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ A → Set
  t < t′ = t ≢ t′ × t ≤ t′

  -- TODO: unfinished
  -- wf< : ∀ {A Γ Δ} → WellFounded (_<_ {A} {Γ} {Δ})
  -- wf< t = acc (access t)
  --   where
  --     access : ∀ {A Γ Δ} → (t t′ : Γ ⁏ Δ ⊢ A) → t′ < t → Acc _<_ t′
  --     access t             .t (t≢t , refl≤)                    = refl ↯ t≢t
  --     access (app t .t′)   t′ (t′≢apptt′ , stepapp≤₂ refl)     = {!!}
  --     access (unbox .t′ u) t′ (t′≢unboxt′u , stepunbox≤₁ refl) = {!!}


-- Monotonicity with respect to context inclusion.

mono⊢ : ∀ {A Γ Γ′ Δ} → Γ ⊆ Γ′ → Γ ⁏ Δ ⊢ A → Γ′ ⁏ Δ ⊢ A
mono⊢ η (var i)     = var (mono∈ η i)
mono⊢ η (lam t)     = lam (mono⊢ (keep η) t)
mono⊢ η (app t u)   = app (mono⊢ η t) (mono⊢ η u)
mono⊢ η (mvar i)    = mvar i
mono⊢ η (box t)     = box t
mono⊢ η (unbox t u) = unbox (mono⊢ η t) (mono⊢ η u)
mono⊢ η (pair t u)  = pair (mono⊢ η t) (mono⊢ η u)
mono⊢ η (fst t)     = fst (mono⊢ η t)
mono⊢ η (snd t)     = snd (mono⊢ η t)
mono⊢ η unit        = unit

mono⊢⋆ : ∀ {Ξ Γ Γ′ Δ} → Γ ⊆ Γ′ → Γ ⁏ Δ ⊢⋆ Ξ → Γ′ ⁏ Δ ⊢⋆ Ξ
mono⊢⋆ {∅}     η ∙        = ∙
mono⊢⋆ {Ξ , A} η (ts , t) = mono⊢⋆ η ts , mono⊢ η t


-- Monotonicity with respect to modal context inclusion.

mmono⊢ : ∀ {A Γ Δ Δ′} → Δ ⊆ Δ′ → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ′ ⊢ A
mmono⊢ θ (var i)     = var i
mmono⊢ θ (lam t)     = lam (mmono⊢ θ t)
mmono⊢ θ (app t u)   = app (mmono⊢ θ t) (mmono⊢ θ u)
mmono⊢ θ (mvar i)    = mvar (mono∈ θ i)
mmono⊢ θ (box t)     = box (mmono⊢ θ t)
mmono⊢ θ (unbox t u) = unbox (mmono⊢ θ t) (mmono⊢ (keep θ) u)
mmono⊢ θ (pair t u)  = pair (mmono⊢ θ t) (mmono⊢ θ u)
mmono⊢ θ (fst t)     = fst (mmono⊢ θ t)
mmono⊢ θ (snd t)     = snd (mmono⊢ θ t)
mmono⊢ θ unit        = unit

mmono⊢⋆ : ∀ {Ξ Γ Δ Δ′} → Δ ⊆ Δ′ → Γ ⁏ Δ ⊢⋆ Ξ → Γ ⁏ Δ′ ⊢⋆ Ξ
mmono⊢⋆ {∅}     θ ∙        = ∙
mmono⊢⋆ {Ξ , A} θ (ts , t) = mmono⊢⋆ θ ts , mmono⊢ θ t


-- Monotonicity using context pairs.

mono²⊢ : ∀ {A Π Π′} → Π ⊆² Π′ → Π ⊢ A → Π′ ⊢ A
mono²⊢ (η , θ) = mono⊢ η ∘ mmono⊢ θ

mono²⊢⋆ : ∀ {Ξ Π Π′} → Π ⊆² Π′ → Π ⊢⋆ Ξ → Π′ ⊢⋆ Ξ
mono²⊢⋆ (η , θ) = mono⊢⋆ η ∘ mmono⊢⋆ θ


-- Shorthand for variables.

v₀ : ∀ {A Γ Δ} → Γ , A ⁏ Δ ⊢ A
v₀ = var i₀

v₁ : ∀ {A B Γ Δ} → Γ , A , B ⁏ Δ ⊢ A
v₁ = var i₁

v₂ : ∀ {A B C Γ Δ} → Γ , A , B , C ⁏ Δ ⊢ A
v₂ = var i₂

mv₀ : ∀ {A Γ Δ} → Γ ⁏ Δ , A ⊢ A
mv₀ = mvar i₀

mv₁ : ∀ {A B Γ Δ} → Γ ⁏ Δ , A , B ⊢ A
mv₁ = mvar i₁

mv₂ : ∀ {A B C Γ Δ} → Γ ⁏ Δ , A , B , C ⊢ A
mv₂ = mvar i₂


-- Reflexivity.

refl⊢⋆ : ∀ {Γ Δ} → Γ ⁏ Δ ⊢⋆ Γ
refl⊢⋆ {∅}     = ∙
refl⊢⋆ {Γ , A} = mono⊢⋆ weak⊆ refl⊢⋆ , v₀

mrefl⊢⋆ : ∀ {Δ Γ} → Γ ⁏ Δ ⊢⋆ □⋆ Δ
mrefl⊢⋆ {∅}     = ∙
mrefl⊢⋆ {Δ , A} = mmono⊢⋆ weak⊆ mrefl⊢⋆ , box mv₀

mrefl⊢⋆′ : ∀ {Δ Δ′ Γ Γ′} → (∀ {A} → Γ ⁏ Δ ⊢ □ A → Γ′ ⁏ Δ′ ⊢ A) → Γ′ ⁏ Δ′ ⊢⋆ Δ
mrefl⊢⋆′ {∅}     f = ∙
mrefl⊢⋆′ {Δ , B} f = mrefl⊢⋆′ (f ∘ mmono⊢ weak⊆) , f (box mv₀)


-- Deduction theorem is built-in.

lam⋆ : ∀ {Ξ A Γ Δ} → Γ ⧺ Ξ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ Ξ ▻⋯▻ A
lam⋆ {∅}     = I
lam⋆ {Ξ , B} = lam⋆ {Ξ} ∘ lam

lam⋆₀ : ∀ {Γ A Δ} → Γ ⁏ Δ ⊢ A → ∅ ⁏ Δ ⊢ Γ ▻⋯▻ A
lam⋆₀ {∅}     = I
lam⋆₀ {Γ , B} = lam⋆₀ ∘ lam


-- Modal deduction theorem.

mlam : ∀ {A B Γ Δ} → Γ ⁏ Δ , A ⊢ B → Γ ⁏ Δ ⊢ □ A ▻ B
mlam t = lam (unbox v₀ (mono⊢ weak⊆ t))

mlam⋆ : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⧺ Ξ ⊢ A → Γ ⁏ Δ ⊢ □⋆ Ξ ▻⋯▻ A
mlam⋆ {∅}     = I
mlam⋆ {Ξ , B} = mlam⋆ {Ξ} ∘ mlam

mlam⋆₀ : ∀ {Δ A Γ} → Γ ⁏ Δ ⊢ A → Γ ⁏ ∅ ⊢ □⋆ Δ ▻⋯▻ A
mlam⋆₀ {∅}     = I
mlam⋆₀ {Δ , B} = mlam⋆₀ ∘ mlam


-- Detachment theorems.

det : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B → Γ , A ⁏ Δ ⊢ B
det t = app (mono⊢ weak⊆ t) v₀

det⋆ : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢ Ξ ▻⋯▻ A → Γ ⧺ Ξ ⁏ Δ ⊢ A
det⋆ {∅}     = I
det⋆ {Ξ , B} = det ∘ det⋆ {Ξ}

det⋆₀ : ∀ {Γ A Δ} → ∅ ⁏ Δ ⊢ Γ ▻⋯▻ A → Γ ⁏ Δ ⊢ A
det⋆₀ {∅}     = I
det⋆₀ {Γ , B} = det ∘ det⋆₀

mdet : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ A ▻ B → Γ ⁏ Δ , A ⊢ B
mdet t = app (mmono⊢ weak⊆ t) (box mv₀)

mdet⋆ : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢ □⋆ Ξ ▻⋯▻ A → Γ ⁏ Δ ⧺ Ξ ⊢ A
mdet⋆ {∅}     = I
mdet⋆ {Ξ , B} = mdet ∘ mdet⋆ {Ξ}

mdet⋆₀ : ∀ {Δ A Γ} → Γ ⁏ ∅ ⊢ □⋆ Δ ▻⋯▻ A → Γ ⁏ Δ ⊢ A
mdet⋆₀ {∅}     = I
mdet⋆₀ {Δ , B} = mdet ∘ mdet⋆₀


-- Context manipulation.

merge : ∀ {Δ A Γ} → Γ ⁏ Δ ⊢ A → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢ A
merge {Δ} = det⋆ {□⋆ Δ} ∘ mlam⋆₀

mmerge : ∀ {Γ A Δ} → □⋆ Γ ⁏ Δ ⊢ A → ∅ ⁏ Δ ⧺ Γ ⊢ A
mmerge {Γ} = mdet⋆ {Γ} ∘ lam⋆₀

split : ∀ {Δ A Γ} → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢ A → Γ ⁏ Δ ⊢ A
split {Δ} = mdet⋆₀ ∘ lam⋆ {□⋆ Δ}

msplit : ∀ {Γ A Δ} → ∅ ⁏ Δ ⧺ Γ ⊢ A → □⋆ Γ ⁏ Δ ⊢ A
msplit {Γ} = det⋆₀ ∘ mlam⋆ {Γ}

merge⋆ : ∀ {Ξ Δ Γ} → Γ ⁏ Δ ⊢⋆ Ξ → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢⋆ Ξ
merge⋆ {∅}     ∙        = ∙
merge⋆ {Ξ , A} (ts , t) = merge⋆ ts , merge t

split⋆ : ∀ {Ξ Δ Γ} → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ
split⋆ {∅}     ∙        = ∙
split⋆ {Ξ , A} (ts , t) = split⋆ ts , split t


-- Cut and multicut.

cut : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A → Γ , A ⁏ Δ ⊢ B → Γ ⁏ Δ ⊢ B
cut t u = app (lam u) t

mcut : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ , A ⊢ B → Γ ⁏ Δ ⊢ B
mcut t u = app (mlam u) t

multicut : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢⋆ Ξ → Ξ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ A
multicut {∅}     ∙        u = mono⊢ bot⊆ u
multicut {Ξ , B} (ts , t) u = app (multicut ts (lam u)) t

mmulticut : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → Γ ⁏ Ξ ⊢ A → Γ ⁏ Δ ⊢ A
mmulticut {∅}     ∙        u = mmono⊢ bot⊆ u
mmulticut {Ξ , B} (ts , t) u = app (mmulticut ts (mlam u)) t

multicut² : ∀ {Ξ Ξ′ A Γ Δ} → Γ ⁏ Δ ⊢⋆ Ξ → Γ ⁏ Δ ⊢⋆ □⋆ Ξ′ → Ξ ⁏ Ξ′ ⊢ A → Γ ⁏ Δ ⊢ A
multicut² {∅}     ∙        us v = mmulticut us (mono⊢ bot⊆ v)
multicut² {Ξ , B} (ts , t) us v = app (multicut² ts us (lam v)) t


-- Transitivity.

trans⊢⋆₀ : ∀ {Γ″ Γ′ Γ} → Γ ⁏ ∅ ⊢⋆ Γ′ → Γ′ ⁏ ∅ ⊢⋆ Γ″ → Γ ⁏ ∅ ⊢⋆ Γ″
trans⊢⋆₀ {∅}      ts ∙        = ∙
trans⊢⋆₀ {Γ″ , A} ts (us , u) = trans⊢⋆₀ ts us , multicut ts u

trans⊢⋆ : ∀ {Ξ Γ Γ′ Δ Δ′} → Γ ⁏ Δ ⊢⋆ Γ′ ⧺ (□⋆ Δ′) → Γ′ ⁏ Δ′ ⊢⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ
trans⊢⋆ ts us = split⋆ (trans⊢⋆₀ (merge⋆ ts) (merge⋆ us))


-- Contraction.

ccont : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ (A ▻ A ▻ B) ▻ A ▻ B
ccont = lam (lam (app (app v₁ v₀) v₀))

cont : ∀ {A B Γ Δ} → Γ , A , A ⁏ Δ ⊢ B → Γ , A ⁏ Δ ⊢ B
cont t = det (app ccont (lam (lam t)))

mcont : ∀ {A B Γ Δ} → Γ ⁏ Δ , A , A ⊢ B → Γ ⁏ Δ , A ⊢ B
mcont t = mdet (app ccont (mlam (mlam t)))


-- Exchange, or Schönfinkel’s C combinator.

cexch : ∀ {A B C Γ Δ} → Γ ⁏ Δ ⊢ (A ▻ B ▻ C) ▻ B ▻ A ▻ C
cexch = lam (lam (lam (app (app v₂ v₀) v₁)))

exch : ∀ {A B C Γ Δ} → Γ , A , B ⁏ Δ ⊢ C → Γ , B , A ⁏ Δ ⊢ C
exch t = det (det (app cexch (lam (lam t))))

mexch : ∀ {A B C Γ Δ} → Γ ⁏ Δ , A , B ⊢ C → Γ ⁏ Δ , B , A ⊢ C
mexch t = mdet (mdet (app cexch (mlam (mlam t))))


-- Composition, or Schönfinkel’s B combinator.

ccomp : ∀ {A B C Γ Δ} → Γ ⁏ Δ ⊢ (B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
ccomp = lam (lam (lam (app v₂ (app v₁ v₀))))

comp : ∀ {A B C Γ Δ} → Γ , B ⁏ Δ ⊢ C → Γ , A ⁏ Δ ⊢ B → Γ , A ⁏ Δ ⊢ C
comp t u = det (app (app ccomp (lam t)) (lam u))

mcomp : ∀ {A B C Γ Δ} → Γ ⁏ Δ , B ⊢ □ C → Γ ⁏ Δ , A ⊢ □ B → Γ ⁏ Δ , A ⊢ □ C
mcomp t u = mdet (app (app ccomp (mlam t)) (mlam u))


-- Useful theorems in functional form.

dist : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ▻ B) → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ B
dist t u = unbox t (unbox (mmono⊢ weak⊆ u) (box (app mv₁ mv₀)))

up : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ □ A
up t = unbox t (box (box mv₀))

down : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ A
down t = unbox t mv₀

distup : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (□ A ▻ B) → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ B
distup t u = dist t (up u)


-- Useful theorems in combinatory form.

ci : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ A ▻ A
ci = lam v₀

ck : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B ▻ A
ck = lam (lam v₁)

cs : ∀ {A B C Γ Δ} → Γ ⁏ Δ ⊢ (A ▻ B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
cs = lam (lam (lam (app (app v₂ v₀) (app v₁ v₀))))

cdist : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ▻ B) ▻ □ A ▻ □ B
cdist = lam (lam (dist v₁ v₀))

cup : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ □ A ▻ □ □ A
cup = lam (up v₀)

cdown : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ □ A ▻ A
cdown = lam (down v₀)

cdistup : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (□ A ▻ B) ▻ □ A ▻ □ B
cdistup = lam (lam (dist v₁ (up v₀)))

cunbox : ∀ {A C Γ Δ} → Γ ⁏ Δ ⊢ □ A ▻ (□ A ▻ C) ▻ C
cunbox = lam (lam (app v₀ v₁))

cpair : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B ▻ A ∧ B
cpair = lam (lam (pair v₁ v₀))

cfst : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ∧ B ▻ A
cfst = lam (fst v₀)

csnd : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ∧ B ▻ B
csnd = lam (snd v₀)


-- Internalisation, or lifting, and additional theorems.

lift : ∀ {Γ A Δ} → Γ ⁏ Δ ⊢ A → □⋆ Γ ⁏ Δ ⊢ □ A
lift {∅}     t = box t
lift {Γ , B} t = det (app cdist (lift (lam t)))

hypup : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B → Γ ⁏ Δ ⊢ □ A ▻ B
hypup t = lam (app (mono⊢ weak⊆ t) (down v₀))

hypdown : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ □ A ▻ B → Γ ⁏ Δ ⊢ □ A ▻ B
hypdown t = lam (app (mono⊢ weak⊆ t) (up v₀))

cxup : ∀ {Γ A Δ} → Γ ⁏ Δ ⊢ A → □⋆ Γ ⁏ Δ ⊢ A
cxup {∅}     t = t
cxup {Γ , B} t = det (hypup (cxup (lam t)))

cxdown : ∀ {Γ A Δ} → □⋆ □⋆ Γ ⁏ Δ ⊢ A → □⋆ Γ ⁏ Δ ⊢ A
cxdown {∅}     t = t
cxdown {Γ , B} t = det (hypdown (cxdown (lam t)))

box⋆ : ∀ {Ξ Γ Δ} → ∅ ⁏ Δ ⊢⋆ Ξ → Γ ⁏ Δ ⊢⋆ □⋆ Ξ
box⋆ {∅}     ∙        = ∙
box⋆ {Ξ , A} (ts , t) = box⋆ ts , box t

lift⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊢⋆ Ξ → □⋆ Γ ⁏ Δ ⊢⋆ □⋆ Ξ
lift⋆ {∅}     ∙        = ∙
lift⋆ {Ξ , A} (ts , t) = lift⋆ ts , lift t

up⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → Γ ⁏ Δ ⊢⋆ □⋆ □⋆ Ξ
up⋆ {∅}     ∙        = ∙
up⋆ {Ξ , A} (ts , t) = up⋆ ts , up t

down⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ
down⋆ {∅}     ∙        = ∙
down⋆ {Ξ , A} (ts , t) = down⋆ ts , down t

multibox : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → □⋆ Ξ ⁏ ∅ ⊢ A → Γ ⁏ Δ ⊢ □ A
multibox ts u = multicut (up⋆ ts) (mmono⊢ bot⊆ (lift u))

dist′ : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ▻ B) → Γ ⁏ Δ ⊢ □ A ▻ □ B
dist′ t = lam (dist (mono⊢ weak⊆ t) v₀)

mpair : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ B → Γ ⁏ Δ ⊢ □ (A ∧ B)
mpair t u = unbox t (unbox (mmono⊢ weak⊆ u) (box (pair mv₁ mv₀)))

mfst : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ∧ B) → Γ ⁏ Δ ⊢ □ A
mfst t = unbox t (box (fst mv₀))

msnd : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ∧ B) → Γ ⁏ Δ ⊢ □ B
msnd t = unbox t (box (snd mv₀))


-- Closure under context concatenation.

concat : ∀ {A B Γ} Γ′ {Δ} → Γ , A ⁏ Δ ⊢ B → Γ′ ⁏ Δ ⊢ A → Γ ⧺ Γ′ ⁏ Δ ⊢ B
concat Γ′ t u = app (mono⊢ (weak⊆⧺₁ Γ′) (lam t)) (mono⊢ weak⊆⧺₂ u)

mconcat : ∀ {A B Γ Δ} Δ′ → Γ ⁏ Δ , A ⊢ B → Γ ⁏ Δ′ ⊢ □ A → Γ ⁏ Δ ⧺ Δ′ ⊢ B
mconcat Δ′ t u = app (mmono⊢ (weak⊆⧺₁ Δ′) (mlam t)) (mmono⊢ weak⊆⧺₂ u)


-- Substitution.

[_≔_]_ : ∀ {A B Γ Δ} → (i : A ∈ Γ) → Γ ∖ i ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ B → Γ ∖ i ⁏ Δ ⊢ B
[ i ≔ s ] var j     with i ≟∈ j
[ i ≔ s ] var .i    | same   = s
[ i ≔ s ] var ._    | diff j = var j
[ i ≔ s ] lam t     = lam ([ pop i ≔ mono⊢ weak⊆ s ] t)
[ i ≔ s ] app t u   = app ([ i ≔ s ] t) ([ i ≔ s ] u)
[ i ≔ s ] mvar j    = mvar j
[ i ≔ s ] box t     = box t
[ i ≔ s ] unbox t u = unbox ([ i ≔ s ] t) ([ i ≔ mmono⊢ weak⊆ s ] u)
[ i ≔ s ] pair t u  = pair ([ i ≔ s ] t) ([ i ≔ s ] u)
[ i ≔ s ] fst t     = fst ([ i ≔ s ] t)
[ i ≔ s ] snd t     = snd ([ i ≔ s ] t)
[ i ≔ s ] unit      = unit

[_≔_]⋆_ : ∀ {Ξ A Γ Δ} → (i : A ∈ Γ) → Γ ∖ i ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢⋆ Ξ → Γ ∖ i ⁏ Δ ⊢⋆ Ξ
[_≔_]⋆_ {∅}     i s ∙        = ∙
[_≔_]⋆_ {Ξ , B} i s (ts , t) = [ i ≔ s ]⋆ ts , [ i ≔ s ] t


-- Modal substitution.

m[_≔_]_ : ∀ {A B Γ Δ} → (i : A ∈ Δ) → ∅ ⁏ Δ ∖ i ⊢ A → Γ ⁏ Δ ⊢ B → Γ ⁏ Δ ∖ i ⊢ B
m[ i ≔ s ] var j     = var j
m[ i ≔ s ] lam t     = lam (m[ i ≔ s ] t)
m[ i ≔ s ] app t u   = app (m[ i ≔ s ] t) (m[ i ≔ s ] u)
m[ i ≔ s ] mvar j    with i ≟∈ j
m[ i ≔ s ] mvar .i   | same   = mono⊢ bot⊆ s
m[ i ≔ s ] mvar ._   | diff j = mvar j
m[ i ≔ s ] box t     = box (m[ i ≔ s ] t)
m[ i ≔ s ] unbox t u = unbox (m[ i ≔ s ] t) (m[ pop i ≔ mmono⊢ weak⊆ s ] u)
m[ i ≔ s ] pair t u  = pair (m[ i ≔ s ] t) (m[ i ≔ s ] u)
m[ i ≔ s ] fst t     = fst (m[ i ≔ s ] t)
m[ i ≔ s ] snd t     = snd (m[ i ≔ s ] t)
m[ i ≔ s ] unit      = unit

m[_≔_]⋆_ : ∀ {Ξ A Γ Δ} → (i : A ∈ Δ) → ∅ ⁏ Δ ∖ i ⊢ A → Γ ⁏ Δ ⊢⋆ Ξ → Γ ⁏ Δ ∖ i ⊢⋆ Ξ
m[_≔_]⋆_ {∅}     i s ∙        = ∙
m[_≔_]⋆_ {Ξ , B} i s (ts , t) = m[ i ≔ s ]⋆ ts , m[ i ≔ s ] t


-- Convertibility.

data _⋙_ {Γ Δ : Cx Ty} : ∀ {A} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ A → Set where
  refl⋙      : ∀ {A} → {t : Γ ⁏ Δ ⊢ A}
                      → t ⋙ t

  trans⋙     : ∀ {A} → {t t′ t″ : Γ ⁏ Δ ⊢ A}
                      → t ⋙ t′ → t′ ⋙ t″
                      → t ⋙ t″

  sym⋙       : ∀ {A} → {t t′ : Γ ⁏ Δ ⊢ A}
                      → t ⋙ t′
                      → t′ ⋙ t

  conglam⋙   : ∀ {A B} → {t t′ : Γ , A ⁏ Δ ⊢ B}
                        → t ⋙ t′
                        → lam t ⋙ lam t′

  congapp⋙   : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A ▻ B} → {u u′ : Γ ⁏ Δ ⊢ A}
                        → t ⋙ t′ → u ⋙ u′
                        → app t u ⋙ app t′ u′

  -- NOTE: Rejected by Pfenning and Davies.
  -- congbox⋙   : ∀ {A} → {t t′ : ∅ ⁏ Δ ⊢ A}
  --                     → t ⋙ t′
  --                     → box {Γ} t ⋙ box {Γ} t′

  congunbox⋙ : ∀ {A C} → {t t′ : Γ ⁏ Δ ⊢ □ A} → {u u′ : Γ ⁏ Δ , A ⊢ C}
                        → t ⋙ t′ → u ⋙ u′
                        → unbox t u ⋙ unbox t′ u′

  congpair⋙  : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A} → {u u′ : Γ ⁏ Δ ⊢ B}
                        → t ⋙ t′ → u ⋙ u′
                        → pair t u ⋙ pair t′ u′

  congfst⋙   : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A ∧ B}
                        → t ⋙ t′
                        → fst t ⋙ fst t′

  congsnd⋙   : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A ∧ B}
                        → t ⋙ t′
                        → snd t ⋙ snd t′

  beta▻⋙     : ∀ {A B} → {t : Γ , A ⁏ Δ ⊢ B} → {u : Γ ⁏ Δ ⊢ A}
                        → app (lam t) u ⋙ ([ top ≔ u ] t)

  eta▻⋙      : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A ▻ B}
                        → t ⋙ lam (app (mono⊢ weak⊆ t) v₀)

  beta□⋙     : ∀ {A C} → {t : ∅ ⁏ Δ ⊢ A} → {u : Γ ⁏ Δ , A ⊢ C}
                        → unbox (box t) u ⋙ (m[ top ≔ t ] u)

  eta□⋙      : ∀ {A} → {t : Γ ⁏ Δ ⊢ □ A}
                      → t ⋙ unbox t (box mv₀)

  -- TODO: What about commuting conversions for □?

  beta∧₁⋙    : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A} → {u : Γ ⁏ Δ ⊢ B}
                        → fst (pair t u) ⋙ t

  beta∧₂⋙    : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A} → {u : Γ ⁏ Δ ⊢ B}
                        → snd (pair t u) ⋙ u

  eta∧⋙      : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A ∧ B}
                        → t ⋙ pair (fst t) (snd t)

  eta⊤⋙     : ∀ {t : Γ ⁏ Δ ⊢ ⊤} → t ⋙ unit