{-# OPTIONS --without-K #-}

module Invertibility where

open import Size
open import Codata.Delay renaming (length to dlength ; map to dmap )
open import Codata.Thunk using (Thunk ; force)
open import Relation.Binary.PropositionalEquality
open import Data.Vec.Relation.Unary.All using (All ; [] ; _∷_)

open import Data.Product

open import Syntax
open import Value
open import Definitional

open import Data.List using (List ; [] ; _∷_ ; length)
open import Data.Vec  using ([] ; _∷_)

open import Level renaming (zero to lzero ; suc to lsuc)
open import Function using () renaming (case_of_ to CASE_OF_)

open import Relation.Nullary
open import Data.Empty

open import PInj
open _⊢F_⇔_

-- m ⟶ v states that computation of m terminates in v, which is
-- suitable for structural induction more than ⇓.
data _⟶_ : {A : Set} (m : DELAY A ∞) (v : A) -> Set₁ where
  now⟶   : ∀ {A : Set} (v : A) -> Now v ⟶ v
  later⟶ : ∀ {A : Set} {x : Thunk (DELAY A) ∞} -> ∀ (v : A)
             -> (ev : force x ⟶ v)
             -> Later x ⟶ v
  bind⟶ :
    ∀ {A B : Set} {m : DELAY A ∞} {f : A -> DELAY B ∞} (u : A) (v : B)
    -> m ⟶ u
    -> f u ⟶ v
    -> Bind m f ⟶ v

-- We will check that m ⟶ v conincides with the termination of thawed
-- computation (d : runD m ⇓) that results in v (that is, extract d ≡
-- v).

module _ where
  open import Data.Nat
  open import Data.Nat.Properties
  private
    -- It is unclear that we can prove the statement ⇓-⟶ by the
    -- indunction on m⇓, as it is not immediately clear that m⇓ does
    -- not involve infinite number of binds. So, we base ourselves on
    -- the number of 'later' and prove that the descruction of "bind"
    -- cannot increase the number ('lemma', below).

    -- steps
    len : ∀ {A : Set} {m : Delay A ∞} -> m ⇓ -> ℕ
    len (now _)   = ℕ.zero
    len (later x) = ℕ.suc (len x)

    -- Destruction cannot increase "steps"
    lemma :
      ∀ {A B : Set} -> (m : Delay A ∞) (f : A -> Delay B ∞)
      -> (mf⇓ : bind m f ⇓) -> Σ (m ⇓) λ m⇓ → Σ (f (extract m⇓) ⇓) λ fu⇓ -> (extract mf⇓ ≡ extract fu⇓ × len m⇓ ≤ len mf⇓ × len fu⇓ ≤ len mf⇓)
    lemma (now x) f bd = (now x) , bd , refl , z≤n , ≤-refl
    lemma (later x) f (later bd) with lemma (force x) f bd
    ... | m⇓ , f⇓ , eq , rel₁ , rel₂ = later m⇓ , f⇓ , eq , s≤s rel₁ , ≤-step rel₂

    -- A generalized statement for induction.
    aux : ∀ {A : Set} n -> (m : DELAY A ∞) -> (m⇓ : runD m ⇓) -> len m⇓ ≤ n -> m ⟶ extract m⇓
    aux n (Now x) (now .x) ctr = now⟶ x
    aux zero    (Later x) (later m⇓) ()
    aux (suc n) (Later x) (later m⇓) (s≤s ctr) = later⟶ _ (aux n (force x) m⇓ ctr)
    aux n (Bind m f) mf⇓ ctr with lemma (runD m) _ mf⇓
    ... | m⇓ , fu⇓ , eq , rel₁ , rel₂ with aux n m m⇓ (≤-trans rel₁ ctr)
    ... | m⟶u with aux n (f (extract m⇓)) fu⇓ (≤-trans rel₂ ctr)
    ... | fu⟶v rewrite eq = bind⟶ {m = m} {f} (extract m⇓) (extract fu⇓) m⟶u fu⟶v

  ⇓-⟶ : ∀ {A : Set} (m : DELAY A ∞)  -> (m⇓ : runD m ⇓) -> m ⟶ extract m⇓
  ⇓-⟶ m m⇓ = aux (len m⇓) m m⇓ (≤-refl)

-- In contrast, the opposite direction is straightforward, thanks to
-- the frozen bind.
⟶-⇓ : ∀ {A : Set} (m : DELAY A ∞) (v : A)
        -> m ⟶ v -> Σ[ m⇓ ∈ runD m ⇓ ] (extract m⇓ ≡ v)
⟶-⇓ .(Now v) v (now⟶ .v) = now v , refl
⟶-⇓ .(Later _) v (later⟶ .v m⟶v) with ⟶-⇓ _ v m⟶v
... | m⇓ , refl = later m⇓ , refl
⟶-⇓ .(Bind m f) v (bind⟶ {m = m} {f} u .v m⟶u fu⟶v)
  with ⟶-⇓ _ u m⟶u | ⟶-⇓ _ v fu⟶v
... | m⇓ , refl | fu⇓ , refl = bind-⇓ m⇓ fu⇓ , lemma m⇓ fu⇓
  where
    lemma :
      ∀ {ℓ} {A B : Set ℓ} {m : Delay A ∞} {f : A → Delay B ∞}
      -> (m⇓ : m ⇓) -> (fu⇓ : f (extract m⇓) ⇓)
      -> extract (bind-⇓ m⇓ {f = f} fu⇓) ≡ extract fu⇓
    lemma (now a) fu⇓ = refl
    lemma {f = f} (later m⇓) fu⇓ rewrite lemma {f = f} m⇓ fu⇓ = refl

-- Never does not terminate.
¬Never⟶ : ∀ {A : Set} {v : A} -> ¬ (Never ⟶ v)
¬Never⟶ (later⟶ _ x) = ¬Never⟶ x

-- A useful variant of now⟶
now⟶≡ : ∀ {A : Set} {v v' : A} -> v' ≡ v -> Now v' ⟶ v
now⟶≡ refl = now⟶ _

-- Several properties on value environments (for forward and backward
-- evaluations) and manipulations on them.

RValEnv-∅-canon : ∀ {Θ} (ρ : RValEnv Θ ∅) -> ρ ≡ emptyRValEnv
RValEnv-∅-canon {[]} [] = refl
RValEnv-∅-canon {x ∷ Θ} (skip ρ) = cong skip (RValEnv-∅-canon {Θ} ρ)

lkup-unlkup : ∀ {Θ A} {x : Θ ∋ A} {Ξ} -> (ok : varOk● Θ x Ξ) (ρ :  RValEnv Θ Ξ) -> unlkup ok (lkup ok ρ) ≡ ρ
lkup-unlkup (there ok) (skip ρ) = cong skip (lkup-unlkup ok ρ)
lkup-unlkup (here ad) (x ∷ ρ) with all-zero-canon ad ρ
... | refl = refl

unlkup-lkup : ∀ {Θ A} {x : Θ ∋ A} {Ξ} -> (ok : varOk● Θ x Ξ) (v : Value [] ∅ A) -> lkup ok (unlkup ok v) ≡ v
unlkup-lkup (there ok) v = unlkup-lkup ok v
unlkup-lkup (here ad) v = refl

split-merge :
  ∀ {Θ Ξ₁ Ξ₂}
  -> (ano : all-no-omega (Ξ₁ +ₘ Ξ₂))
  -> ∀ (ρ : RValEnv Θ (Ξ₁ +ₘ Ξ₂))
  -> mergeRValEnv ano (proj₁ (splitRValEnv ρ)) (proj₂ (splitRValEnv ρ)) ≡ ρ
split-merge {[]} {[]} {[]} ano [] = refl
split-merge {x ∷ Θ} {Multiplicity₀.zero ∷ Ξ₁} {Multiplicity₀.zero ∷ Ξ₂} (px ∷ ano) (skip ρ)
  rewrite split-merge {Θ} {Ξ₁} {Ξ₂} ano ρ = refl
split-merge {x ∷ Θ} {Multiplicity₀.zero ∷ Ξ₁} {one ∷ Ξ₂} (px ∷ ano) (v ∷ ρ)
  rewrite split-merge {Θ} {Ξ₁} {Ξ₂} ano ρ = refl
split-merge {x ∷ Θ} {one ∷ Ξ₁} {Multiplicity₀.zero ∷ Ξ₂} (px ∷ ano) (v ∷ ρ)
    rewrite split-merge {Θ} {Ξ₁} {Ξ₂} ano ρ = refl

merge-split :
  ∀ {Θ Ξ₁ Ξ₂}
  -> (ano : all-no-omega (Ξ₁ +ₘ Ξ₂))
  -> (ρ₁ : RValEnv Θ Ξ₁)
  -> (ρ₂ : RValEnv Θ Ξ₂)
  -> splitRValEnv (mergeRValEnv ano ρ₁ ρ₂) ≡ (ρ₁ , ρ₂)
merge-split {[]} {[]} {[]} ano [] [] = refl
merge-split {x ∷ Θ} {zero ∷ Ξ₁} {zero ∷ Ξ₂} (px ∷ ano) (skip ρ₁) (skip ρ₂) rewrite merge-split ano ρ₁ ρ₂ = refl
merge-split {x ∷ Θ} {zero ∷ Ξ₁} {one ∷ Ξ₂} (px ∷ ano) (skip ρ₁) (v ∷ ρ₂) rewrite merge-split ano ρ₁ ρ₂ = refl
merge-split {x ∷ Θ} {one ∷ Ξ₁} {zero ∷ Ξ₂} (_ ∷ ano) (v ∷ ρ₁) (skip ρ₂) rewrite merge-split ano ρ₁ ρ₂  = refl
merge-split {x ∷ Θ} {one ∷ Ξ₁} {one ∷ Ξ₂} (() ∷ ano) ρ₁ ρ₂

-- Round-trip properties (corresponding to Lemma 3.3 in the paper)

forward-backward :
  ∀ {Θ Ξ A}
  -> (ano : all-no-omega Ξ)
  -> (E : Residual Θ Ξ (A ●))
  -> let h = evalR ∞ ano E
     in ∀ ρ v -> Forward h ρ ⟶ v -> Backward h v ⟶ ρ

forward-backward ano unit● ρ v fwd with RValEnv-∅-canon ρ
... | refl = now⟶ emptyRValEnv
forward-backward ano (letunit● E E₁) ρ v fwd with all-no-omega-dist _ _ ano
forward-backward ano (letunit● E E₁) ρ v (bind⟶ (unit refl) .v fwd fwd₁) | ano₁ , ano₂ =
    let bwd  = forward-backward ano₁ E  _ _ fwd
        bwd₁ = forward-backward ano₂ E₁ _ _ fwd₁
    in bind⟶ _ _ bwd₁ (bind⟶ _ _ bwd (now⟶≡ (split-merge ano ρ)))

forward-backward ano (pair● E₁ E₂) ρ (pair {Ξ₁ = []} {[]} spΞ v₁ v₂) fwd with all-no-omega-dist _ _ ano
forward-backward ano (pair● E₁ E₂) ρ (pair {_} {[]} {[]} spΞ v₁ v₂) (bind⟶ _ _ fwd₁ (bind⟶ _ _ fwd₂ (now⟶ _))) | ano₁ , ano₂ =
    let bwd₁ = forward-backward ano₁ E₁ _ _ fwd₁
        bwd₂ = forward-backward ano₂ E₂ _ _ fwd₂
    in bind⟶ _ _ bwd₁ (bind⟶ _ _ bwd₂ (now⟶≡ (split-merge ano ρ)))
forward-backward ano (letpair● E E₁) ρ v fwd
  with all-no-omega-dist _ _ ano
forward-backward ano (letpair● E E₁) ρ v (bind⟶ (pair {Ξ₁ = []} {[]} refl v₁ v₂) .v fwd fwd₁) | ano₁ , ano₂ =
   let bwd  = forward-backward ano₁ E _ _ fwd
       bwd₁ = forward-backward (one ∷ one ∷ ano₂) E₁ _ _ fwd₁
   in bind⟶ _ _ bwd₁ (bind⟶ _ _ bwd (now⟶≡ (split-merge ano ρ)))

forward-backward ano (inl● E) ρ _ (bind⟶ u _ fwd (now⟶ _)) = forward-backward ano E _ _ fwd
forward-backward ano (inr● E) ρ _ (bind⟶ u _ fwd (now⟶ _)) = forward-backward ano E _ _ fwd

forward-backward ano (case● E refl θ₁ t₁ θ₂ t₂ v₁) ρ v fwd
  with all-no-omega-dist _ _ ano
forward-backward ano (case● E refl θ₁ t₁ θ₂ t₂ v₁) ρ v (bind⟶ (inl u) .v fwd (bind⟶ (red r₁) .v ev₁ (later⟶ .v (bind⟶ _ .v fwd₁ (bind⟶ (inl (unit refl)) .v ap (now⟶ _)))))) | ano₀ , ano- =
  bind⟶ _ _ ap (
  bind⟶ _ _ ev₁ (later⟶ _ (
  bind⟶ _ _ (forward-backward (one ∷ ano-) r₁ _ _ fwd₁) (bind⟶ _ _ (forward-backward ano₀ E _ _ fwd) (now⟶≡ (split-merge ano ρ))))))
forward-backward ano (case● E refl θ₁ t₁ θ₂ t₂ v₁) ρ v (bind⟶ (inl u) .v fwd (bind⟶ (red r₁) .v ev₁ (later⟶ .v (bind⟶ u₁ .v fwd₁ (bind⟶ (inr (unit refl)) .v ap nev))))) | ano₀ , ano- = ⊥-elim (¬Never⟶ nev)

forward-backward ano (case● E refl θ₁ t₁ θ₂ t₂ v₁) ρ v (bind⟶ (inr u) .v fwd (bind⟶ (red r₁) .v ev₁ (later⟶ .v (bind⟶ u₁ .v fwd₁ (bind⟶ (inl (unit refl)) .v ap nev))))) | ano₀ , ano- = ⊥-elim (¬Never⟶ nev)
forward-backward ano (case● E refl θ₁ t₁ θ₂ t₂ v₁) ρ v (bind⟶ (inr u) .v fwd (bind⟶ (red r₁) .v ev₁ (later⟶ .v (bind⟶ _ .v fwd₁ (bind⟶ (inr (unit refl)) .v ap (now⟶ _)))))) | ano₀ , ano- =
  bind⟶ _ _ ap (
  bind⟶ _ _ ev₁ (later⟶ _ (
  bind⟶ _ _ (forward-backward (one ∷ ano-) r₁ _ _ fwd₁) (bind⟶ _ _ (forward-backward ano₀ E _ _ fwd) (now⟶≡ (split-merge ano ρ))))))

forward-backward ano (var● x ok) ρ .(lkup ok ρ) (now⟶ _) rewrite lkup-unlkup ok ρ = now⟶ ρ
forward-backward ano (pin E (clo .omega refl θ t)) ρ (pair {Ξ₁ = []} {[]} refl v₁ v₂) fwd
  with all-no-omega-dist _ _ ano
forward-backward ano (pin E (clo .omega refl θ t)) ρ (pair {_} {[]} {[]} refl v₁ v₂)
                (bind⟶ _ _ fwd (later⟶ _ (bind⟶ (red r) _ ev (bind⟶ _ _ fwd₂ (now⟶ _))))) | ano₁ , ano₂ =
  later⟶ ρ (bind⟶ _ _ ev
              (bind⟶ _ ρ (forward-backward ano₂ r _ _ fwd₂)
              (bind⟶ _ _ (forward-backward ano₁ E _ _ fwd) (now⟶≡ (split-merge ano ρ)))))

backward-forward :
  ∀ {Θ Ξ A}
  -> (ano : all-no-omega Ξ)
  -> (E : Residual Θ Ξ (A ●))
  -> let h = evalR ∞ ano E
     in ∀ ρ v -> Backward h v ⟶ ρ -> Forward h ρ ⟶ v

backward-forward ano unit● ρ (unit refl) bwd = now⟶ (unit refl)
backward-forward ano (letunit● E E₁) ρ v (bind⟶ ρ₂ _ bwd₂ (bind⟶ ρ₁ _ bwd₁ (now⟶ _))) with all-no-omega-dist _ _ ano
... | ano₁ , ano₂ rewrite merge-split ano ρ₁ ρ₂  =
  bind⟶ _ _ (backward-forward ano₁ E _ _ bwd₁) (backward-forward ano₂ E₁ _ _ bwd₂)

backward-forward ano (pair● E₁ E₂) ρ (pair {Ξ₁ = []} {[]} refl v₁ v₂) (bind⟶ ρ₁ _ bwd₁ (bind⟶ ρ₂ _ bwd₂ (now⟶ _)))
  with all-no-omega-dist _ _ ano
... | ano₁ , ano₂ rewrite merge-split ano ρ₁ ρ₂ =
  bind⟶ _ _ (backward-forward ano₁ E₁ _ _ bwd₁) (bind⟶ _ _ (backward-forward ano₂ E₂ _ _ bwd₂) (now⟶ (pair refl v₁ v₂)))

backward-forward ano (letpair● E E₁) ρ v (bind⟶ (v₁ ∷ v₂ ∷ ρ₂) _ bwd₂ (bind⟶ ρ₁ _ bwd₁ (now⟶ _)))
  with all-no-omega-dist _ _ ano
... | ano₁ , ano₂ rewrite merge-split ano ρ₁ ρ₂ =
  bind⟶ _ _ (backward-forward ano₁ E _ _ bwd₁) (backward-forward (one ∷ one ∷ ano₂) E₁ _ _ bwd₂)

backward-forward ano (inl● E) ρ (inl v) bwd = bind⟶ _ _ (backward-forward ano E ρ v bwd) (now⟶ (inl v))
backward-forward ano (inl● E) ρ (inr v) bwd = ⊥-elim (¬Never⟶ bwd)

backward-forward ano (inr● E) ρ (inl v) bwd = ⊥-elim (¬Never⟶ bwd)
backward-forward ano (inr● E) ρ (inr v) bwd = bind⟶ _ _ (backward-forward ano E ρ v bwd) (now⟶ (inr v))

backward-forward
  ano (case● E refl θ₁ t₁ θ₂ t₂ ϕ) ρ v
  (bind⟶ u _ ap bwd)
  with all-no-omega-dist _ _ ano
backward-forward
  ano (case● E refl θ₁ t₁ θ₂ t₂ ϕ) ρ v
  (bind⟶ (inl u) .ρ ap (bind⟶ (red r) _ ev (later⟶ _ (bind⟶ (v₁ ∷ ρ-) _ bwd- (bind⟶ ρ₀ _ bwd₀ (now⟶ _)))))) | ano₀ , ano- rewrite merge-split ano ρ₀ ρ- =
  bind⟶ _ _ (backward-forward ano₀ E _ _ bwd₀)
  (bind⟶ _ _ ev (later⟶ _
  (bind⟶ _ _ (backward-forward (one ∷ ano-) r _ _ bwd-)
  (bind⟶ _ _ ap (now⟶ v)))))
backward-forward
  ano (case● E refl θ₁ t₁ θ₂ t₂ ϕ) ρ v
  (bind⟶ (inr u) .ρ ap (bind⟶ (red r) _ ev (later⟶ _ (bind⟶ (v₁ ∷ ρ-) _ bwd- (bind⟶ ρ₀ _ bwd₀ (now⟶ _)))))) | ano₀ , ano- rewrite merge-split ano ρ₀ ρ- =
  bind⟶ _ _ (backward-forward ano₀ E _ _ bwd₀)
  (bind⟶ _ _ ev (later⟶ _
  (bind⟶ _ _ (backward-forward (one ∷ ano-) r _ _ bwd-)
  (bind⟶ _ _ ap (now⟶ v)))))

backward-forward ano (var● x ok) .(unlkup ok v) v (now⟶ _) rewrite unlkup-lkup ok v = now⟶ v

backward-forward ano (pin E (clo .omega refl θ t)) ρ (pair {_} {[]} {[]} refl v₁ v₂)
  (later⟶ _ (bind⟶ (red r) _ ev (bind⟶ ρ₂ _ bwd₂ (bind⟶ ρ₁ _ bwd₁ (now⟶ _)))))
  with all-no-omega-dist _ _ ano
... | ano₁ , ano₂ rewrite merge-split ano ρ₁ ρ₂ =
  bind⟶ _ _ (backward-forward ano₁ E _ _ bwd₁)
  (later⟶ _ (bind⟶ _ _ ev (bind⟶ _ _ (backward-forward ano₂ r _ _ bwd₂) (now⟶ (pair refl v₁ v₂)))))


open import Agda.Builtin.Nat
open import Agda.Builtin.List
open import Agda.Builtin.Reflection
open import Agda.Builtin.Sigma
open import Agda.Builtin.Equality

pattern vArg x = arg (arg-info visible relevant) x
pattern hArg x = arg (arg-info hidden  relevant) x

idClause-ok : Clause
idClause-ok = clause
             (("A" , hArg (agda-sort (lit 0))) ∷ ("x" , vArg (var 0 [])) ∷ [])
             (hArg (var 1) ∷ vArg (var 0) ∷ [])
             (var 0 [])

id-ok : {A : Set} → A → A
unquoteDef id-ok = defineFun id-ok (idClause-ok ∷ [])

idClause-fail : Clause
idClause-fail = clause
             (("A" , hArg (agda-sort (lit 0))) ∷ ("x" , vArg (var 0 [])) ∷ [])
             ({-hArg (var 1) ∷-} vArg (var 0) ∷ [])
             (var 0 [])

id-fail : {A : Set} → A → A
unquoteDef id-fail = defineFun id-fail (idClause-fail ∷ [])
-- Panic: unbound variable A (id: 191@9559440724209987811)
-- when checking that the expression A has type _7


open import Prelude
open import Nat
open import core
open import contexts
open import disjointness

-- this module contains lemmas and properties about the holes-disjoint
-- judgement that double check that it acts as we would expect

module holes-disjoint-checks where
  -- these lemmas are all structurally recursive and quite
  -- mechanical. morally, they establish the properties about reduction
  -- that would be obvious / baked into Agda if holes-disjoint was defined
  -- as a function rather than a judgement (datatype), or if we had defined
  -- all the O(n^2) cases rather than relying on a little indirection to
  -- only have O(n) cases. that work has to go somewhwere, and we prefer
  -- that it goes here.
  ds-lem-asc : ∀{e1 e2 τ} → holes-disjoint e2 e1 → holes-disjoint e2 (e1 ·: τ)
  ds-lem-asc HDConst = HDConst
  ds-lem-asc (HDAsc hd) = HDAsc (ds-lem-asc hd)
  ds-lem-asc HDVar = HDVar
  ds-lem-asc (HDLam1 hd) = HDLam1 (ds-lem-asc hd)
  ds-lem-asc (HDLam2 hd) = HDLam2 (ds-lem-asc hd)
  ds-lem-asc (HDHole x) = HDHole (HNAsc x)
  ds-lem-asc (HDNEHole x hd) = HDNEHole (HNAsc x) (ds-lem-asc hd)
  ds-lem-asc (HDAp hd hd₁) = HDAp (ds-lem-asc hd) (ds-lem-asc hd₁)
  ds-lem-asc (HDFst hd) = HDFst (ds-lem-asc hd)
  ds-lem-asc (HDSnd hd) = HDSnd (ds-lem-asc hd)
  ds-lem-asc (HDPair hd hd₁) = HDPair (ds-lem-asc hd) (ds-lem-asc hd₁)

  ds-lem-lam1 : ∀{e1 e2 x} → holes-disjoint e2 e1 → holes-disjoint e2 (·λ x e1)
  ds-lem-lam1 HDConst = HDConst
  ds-lem-lam1 (HDAsc hd) = HDAsc (ds-lem-lam1 hd)
  ds-lem-lam1 HDVar = HDVar
  ds-lem-lam1 (HDLam1 hd) = HDLam1 (ds-lem-lam1 hd)
  ds-lem-lam1 (HDLam2 hd) = HDLam2 (ds-lem-lam1 hd)
  ds-lem-lam1 (HDHole x₁) = HDHole (HNLam1 x₁)
  ds-lem-lam1 (HDNEHole x₁ hd) = HDNEHole (HNLam1 x₁) (ds-lem-lam1 hd)
  ds-lem-lam1 (HDAp hd hd₁) = HDAp (ds-lem-lam1 hd) (ds-lem-lam1 hd₁)
  ds-lem-lam1 (HDFst hd) = HDFst (ds-lem-lam1 hd)
  ds-lem-lam1 (HDSnd hd) = HDSnd (ds-lem-lam1 hd)
  ds-lem-lam1 (HDPair hd hd₁) = HDPair (ds-lem-lam1 hd) (ds-lem-lam1 hd₁)

  ds-lem-lam2 : ∀{e1 e2 x τ} → holes-disjoint e2 e1 → holes-disjoint e2 (·λ x [ τ ] e1)
  ds-lem-lam2 HDConst = HDConst
  ds-lem-lam2 (HDAsc hd) = HDAsc (ds-lem-lam2 hd)
  ds-lem-lam2 HDVar = HDVar
  ds-lem-lam2 (HDLam1 hd) = HDLam1 (ds-lem-lam2 hd)
  ds-lem-lam2 (HDLam2 hd) = HDLam2 (ds-lem-lam2 hd)
  ds-lem-lam2 (HDHole x₁) = HDHole (HNLam2 x₁)
  ds-lem-lam2 (HDNEHole x₁ hd) = HDNEHole (HNLam2 x₁) (ds-lem-lam2 hd)
  ds-lem-lam2 (HDAp hd hd₁) = HDAp (ds-lem-lam2 hd) (ds-lem-lam2 hd₁)
  ds-lem-lam2 (HDFst hd) = HDFst (ds-lem-lam2 hd)
  ds-lem-lam2 (HDSnd hd) = HDSnd (ds-lem-lam2 hd)
  ds-lem-lam2 (HDPair hd hd₁) = HDPair (ds-lem-lam2 hd) (ds-lem-lam2 hd₁)

  ds-lem-nehole : ∀{e e1 u} → holes-disjoint e e1 → hole-name-new e u → holes-disjoint e ⦇⌜ e1 ⌟⦈[ u ]
  ds-lem-nehole HDConst ν = HDConst
  ds-lem-nehole (HDAsc hd) (HNAsc ν) = HDAsc (ds-lem-nehole hd ν)
  ds-lem-nehole HDVar ν = HDVar
  ds-lem-nehole (HDLam1 hd) (HNLam1 ν) = HDLam1 (ds-lem-nehole hd ν)
  ds-lem-nehole (HDLam2 hd) (HNLam2 ν) = HDLam2 (ds-lem-nehole hd ν)
  ds-lem-nehole (HDHole x) (HNHole x₁) = HDHole (HNNEHole (flip x₁) x)
  ds-lem-nehole (HDNEHole x hd) (HNNEHole x₁ ν) = HDNEHole (HNNEHole (flip x₁) x) (ds-lem-nehole hd ν)
  ds-lem-nehole (HDAp hd hd₁) (HNAp ν ν₁) = HDAp (ds-lem-nehole hd ν) (ds-lem-nehole hd₁ ν₁)
  ds-lem-nehole (HDFst hd) (HNFst v) = HDFst (ds-lem-nehole hd v)
  ds-lem-nehole (HDSnd hd) (HNSnd v) = HDSnd (ds-lem-nehole hd v)
  ds-lem-nehole (HDPair hd hd₁) (HNPair v v₁) = HDPair (ds-lem-nehole hd v) (ds-lem-nehole hd₁ v₁)

  ds-lem-ap : ∀{e1 e2 e3} → holes-disjoint e3 e1 → holes-disjoint e3 e2 → holes-disjoint e3 (e1 ∘ e2)
  ds-lem-ap HDConst hd2 = HDConst
  ds-lem-ap (HDAsc hd1) (HDAsc hd2) = HDAsc (ds-lem-ap hd1 hd2)
  ds-lem-ap HDVar hd2 = HDVar
  ds-lem-ap (HDLam1 hd1) (HDLam1 hd2) = HDLam1 (ds-lem-ap hd1 hd2)
  ds-lem-ap (HDLam2 hd1) (HDLam2 hd2) = HDLam2 (ds-lem-ap hd1 hd2)
  ds-lem-ap (HDHole x) (HDHole x₁) = HDHole (HNAp x x₁)
  ds-lem-ap (HDNEHole x hd1) (HDNEHole x₁ hd2) = HDNEHole (HNAp x x₁) (ds-lem-ap hd1 hd2)
  ds-lem-ap (HDAp hd1 hd2) (HDAp hd3 hd4) = HDAp (ds-lem-ap hd1 hd3) (ds-lem-ap hd2 hd4)
  ds-lem-ap (HDFst hd) (HDFst hd2) = HDFst (ds-lem-ap hd hd2)
  ds-lem-ap (HDSnd hd) (HDSnd hd2) = HDSnd (ds-lem-ap hd hd2)
  ds-lem-ap (HDPair hd1 hd2) (HDPair hd3 hd4) = HDPair (ds-lem-ap hd1 hd3) (ds-lem-ap hd2 hd4)

  ds-lem-pair : ∀{e1 e2 e3} → holes-disjoint e3 e1 → holes-disjoint e3 e2 → holes-disjoint e3 ⟨ e1 , e2 ⟩
  ds-lem-pair HDConst HDConst = HDConst
  ds-lem-pair (HDAsc hd1) (HDAsc hd2) = HDAsc (ds-lem-pair hd1 hd2)
  ds-lem-pair HDVar HDVar = HDVar
  ds-lem-pair (HDLam1 hd1) (HDLam1 hd2) = HDLam1 (ds-lem-pair hd1 hd2)
  ds-lem-pair (HDLam2 hd1) (HDLam2 hd2) = HDLam2 (ds-lem-pair hd1 hd2)
  ds-lem-pair (HDHole x) (HDHole x₁) = HDHole (HNPair x x₁)
  ds-lem-pair (HDNEHole x hd1) (HDNEHole x₁ hd2) = HDNEHole (HNPair x x₁) (ds-lem-pair hd1 hd2)
  ds-lem-pair (HDAp hd1 hd2) (HDAp hd3 hd4) = HDAp (ds-lem-pair hd1 hd3) (ds-lem-pair hd2 hd4)
  ds-lem-pair (HDFst hd1) (HDFst hd2) = HDFst (ds-lem-pair hd1 hd2)
  ds-lem-pair (HDSnd hd1) (HDSnd hd2) = HDSnd (ds-lem-pair hd1 hd2)
  ds-lem-pair (HDPair hd1 hd2) (HDPair hd3 hd4) = HDPair (ds-lem-pair hd1 hd3) (ds-lem-pair hd2 hd4)

  ds-lem-fst : ∀{e e'} → holes-disjoint e' e → holes-disjoint e' (fst e)
  ds-lem-fst HDConst = HDConst
  ds-lem-fst (HDAsc hd) = HDAsc (ds-lem-fst hd)
  ds-lem-fst HDVar = HDVar
  ds-lem-fst (HDLam1 hd) = HDLam1 (ds-lem-fst hd)
  ds-lem-fst (HDLam2 hd) = HDLam2 (ds-lem-fst hd)
  ds-lem-fst (HDHole x) = HDHole (HNFst x)
  ds-lem-fst (HDNEHole x hd) = HDNEHole (HNFst x) (ds-lem-fst hd)
  ds-lem-fst (HDAp hd hd₁) = HDAp (ds-lem-fst hd) (ds-lem-fst hd₁)
  ds-lem-fst (HDFst hd) = HDFst (ds-lem-fst hd)
  ds-lem-fst (HDSnd hd) = HDSnd (ds-lem-fst hd)
  ds-lem-fst (HDPair hd hd₁) = HDPair (ds-lem-fst hd) (ds-lem-fst hd₁)

  ds-lem-snd : ∀{e e'} → holes-disjoint e' e → holes-disjoint e' (snd e)
  ds-lem-snd HDConst = HDConst
  ds-lem-snd (HDAsc hd) = HDAsc (ds-lem-snd hd)
  ds-lem-snd HDVar = HDVar
  ds-lem-snd (HDLam1 hd) = HDLam1 (ds-lem-snd hd)
  ds-lem-snd (HDLam2 hd) = HDLam2 (ds-lem-snd hd)
  ds-lem-snd (HDHole x) = HDHole (HNSnd x)
  ds-lem-snd (HDNEHole x hd) = HDNEHole (HNSnd x) (ds-lem-snd hd)
  ds-lem-snd (HDAp hd hd₁) = HDAp (ds-lem-snd hd) (ds-lem-snd hd₁)
  ds-lem-snd (HDFst hd) = HDFst (ds-lem-snd hd)
  ds-lem-snd (HDSnd hd) = HDSnd (ds-lem-snd hd)
  ds-lem-snd (HDPair hd hd₁) = HDPair (ds-lem-snd hd) (ds-lem-snd hd₁)

  -- holes-disjoint is symmetric
  disjoint-sym : (e1 e2 : eexp) → holes-disjoint e1 e2 → holes-disjoint e2 e1
  disjoint-sym .c c HDConst = HDConst
  disjoint-sym .c (e2 ·: x) HDConst = HDAsc (disjoint-sym _ _ HDConst)
  disjoint-sym .c (X x) HDConst = HDVar
  disjoint-sym .c (·λ x e2) HDConst = HDLam1 (disjoint-sym c e2 HDConst)
  disjoint-sym .c (·λ x [ x₁ ] e2) HDConst = HDLam2 (disjoint-sym c e2 HDConst)
  disjoint-sym .c ⦇⦈[ x ] HDConst = HDHole HNConst
  disjoint-sym .c ⦇⌜ e2 ⌟⦈[ x ] HDConst = HDNEHole HNConst (disjoint-sym c e2 HDConst)
  disjoint-sym .c (e2 ∘ e3) HDConst = HDAp (disjoint-sym c e2 HDConst) (disjoint-sym c e3 HDConst)
  disjoint-sym .c ⟨ e2 , e3 ⟩ HDConst = HDPair (disjoint-sym c e2 HDConst) (disjoint-sym c e3 HDConst)
  disjoint-sym .c (fst e2) HDConst = HDFst (disjoint-sym c e2 HDConst)
  disjoint-sym .c (snd e2) HDConst = HDSnd (disjoint-sym c e2 HDConst)

  disjoint-sym _ c (HDAsc hd) = HDConst
  disjoint-sym _ (e2 ·: x) (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ·: x) (HDAsc hd) | HDAsc ih = HDAsc (ds-lem-asc ih)
  disjoint-sym _ (X x) (HDAsc hd) = HDVar
  disjoint-sym _ (·λ x e2) (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x e2) (HDAsc hd) | HDLam1 ih = HDLam1 (ds-lem-asc ih)
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDAsc hd) | HDLam2 ih = HDLam2 (ds-lem-asc ih)
  disjoint-sym _ ⦇⦈[ x ] (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⦈[ x ] (HDAsc hd) | HDHole x₁ = HDHole (HNAsc x₁)
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDAsc hd) | HDNEHole x₁ ih = HDNEHole (HNAsc x₁) (ds-lem-asc ih)
  disjoint-sym _ (e2 ∘ e3) (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ∘ e3) (HDAsc hd) | HDAp ih ih₁ = HDAp (ds-lem-asc ih) (ds-lem-asc ih₁)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym .(_ ·: _) ⟨ e2 , e3 ⟩ (HDAsc hd) | HDPair ih ih₁ = HDPair (ds-lem-asc ih) (ds-lem-asc ih₁)
  disjoint-sym _ (fst e2) (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym .(_ ·: _) (fst e2) (HDAsc hd) | HDFst ih = HDFst (ds-lem-asc ih)
  disjoint-sym _ (snd e2) (HDAsc hd) with disjoint-sym _ _ hd
  disjoint-sym .(_ ·: _) (snd e2) (HDAsc hd) | HDSnd ih = HDSnd (ds-lem-asc ih)

  disjoint-sym _ c HDVar = HDConst
  disjoint-sym _ (e2 ·: x₁) HDVar = HDAsc (disjoint-sym _ e2 HDVar)
  disjoint-sym _ (X x₁) HDVar = HDVar
  disjoint-sym _ (·λ x₁ e2) HDVar = HDLam1 (disjoint-sym _ e2 HDVar)
  disjoint-sym _ (·λ x₁ [ x₂ ] e2) HDVar = HDLam2 (disjoint-sym _ e2 HDVar)
  disjoint-sym _ ⦇⦈[ x₁ ] HDVar = HDHole HNVar
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x₁ ] HDVar = HDNEHole HNVar (disjoint-sym _ e2 HDVar)
  disjoint-sym _ (e2 ∘ e3) HDVar = HDAp (disjoint-sym _ e2 HDVar) (disjoint-sym _ e3 HDVar)
  disjoint-sym _ ⟨ e2 , e3 ⟩ HDVar = HDPair (disjoint-sym (X _) e2 HDVar)
                                       (disjoint-sym (X _) e3 HDVar)
  disjoint-sym _ (fst e2) HDVar = HDFst (disjoint-sym (X _) e2 HDVar)
  disjoint-sym _ (snd e2) HDVar = HDSnd (disjoint-sym (X _) e2 HDVar)

  disjoint-sym _ c (HDLam1 hd) = HDConst
  disjoint-sym _ (e2 ·: x₁) (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ·: x₁) (HDLam1 hd) | HDAsc ih = HDAsc (ds-lem-lam1 ih)
  disjoint-sym _ (X x₁) (HDLam1 hd) = HDVar
  disjoint-sym _ (·λ x₁ e2) (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x₁ e2) (HDLam1 hd) | HDLam1 ih = HDLam1 (ds-lem-lam1 ih)
  disjoint-sym _ (·λ x₁ [ x₂ ] e2) (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x₁ [ x₂ ] e2) (HDLam1 hd) | HDLam2 ih = HDLam2 (ds-lem-lam1 ih)
  disjoint-sym _ ⦇⦈[ x₁ ] (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⦈[ x₁ ] (HDLam1 hd) | HDHole x = HDHole (HNLam1 x)
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x₁ ] (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x₁ ] (HDLam1 hd) | HDNEHole x ih = HDNEHole (HNLam1 x) (ds-lem-lam1 ih)
  disjoint-sym _ (e2 ∘ e3) (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ∘ e3) (HDLam1 hd) | HDAp ih ih₁ = HDAp (ds-lem-lam1 ih) (ds-lem-lam1 ih₁)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDLam1 hd) | HDPair ih ih₁ = HDPair (ds-lem-lam1 ih) (ds-lem-lam1 ih₁)
  disjoint-sym _ (fst e2) (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (fst e2) (HDLam1 hd) | HDFst ih = HDFst (ds-lem-lam1 ih)
  disjoint-sym _ (snd e2) (HDLam1 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (snd e2) (HDLam1 hd) | HDSnd ih = HDSnd (ds-lem-lam1 ih)

  disjoint-sym _ c (HDLam2 hd) = HDConst
  disjoint-sym _ (e2 ·: x₁) (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ·: x₁) (HDLam2 hd) | HDAsc ih = HDAsc (ds-lem-lam2 ih)
  disjoint-sym _ (X x₁) (HDLam2 hd) = HDVar
  disjoint-sym _ (·λ x₁ e2) (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x₁ e2) (HDLam2 hd) | HDLam1 ih = HDLam1 (ds-lem-lam2 ih)
  disjoint-sym _ (·λ x₁ [ x₂ ] e2) (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x₁ [ x₂ ] e2) (HDLam2 hd) | HDLam2 ih = HDLam2 (ds-lem-lam2 ih)
  disjoint-sym _ ⦇⦈[ x₁ ] (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⦈[ x₁ ] (HDLam2 hd) | HDHole x = HDHole (HNLam2 x)
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x₁ ] (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x₁ ] (HDLam2 hd) | HDNEHole x ih = HDNEHole (HNLam2 x) (ds-lem-lam2 ih)
  disjoint-sym _ (e2 ∘ e3) (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ∘ e3) (HDLam2 hd) | HDAp ih ih₁ = HDAp (ds-lem-lam2 ih) (ds-lem-lam2 ih₁)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDLam2 hd) | HDPair ih ih₁ = HDPair (ds-lem-lam2 ih) (ds-lem-lam2 ih₁)
  disjoint-sym _ (fst e2) (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (fst e2) (HDLam2 hd) | HDFst ih = HDFst (ds-lem-lam2 ih)
  disjoint-sym _ (snd e2) (HDLam2 hd) with disjoint-sym _ _ hd
  disjoint-sym _ (snd e2) (HDLam2 hd) | HDSnd ih = HDSnd (ds-lem-lam2 ih)

  disjoint-sym _ c (HDHole x) = HDConst
  disjoint-sym _ (e2 ·: x) (HDHole (HNAsc x₁)) = HDAsc (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x₁))
  disjoint-sym _ (X x) (HDHole x₁) = HDVar
  disjoint-sym _ (·λ x e2) (HDHole (HNLam1 x₁)) = HDLam1 (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x₁))
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDHole (HNLam2 x₂)) = HDLam2 (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x₂))
  disjoint-sym _ ⦇⦈[ x ] (HDHole (HNHole x₁)) =  HDHole (HNHole (flip x₁))
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ u' ] (HDHole (HNNEHole x x₁)) = HDNEHole (HNHole (flip x)) (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x₁))
  disjoint-sym _ (e2 ∘ e3) (HDHole (HNAp x x₁)) = HDAp (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x))
                                                    (disjoint-sym ⦇⦈[ _ ] e3 (HDHole x₁))
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDHole (HNPair x1 x2)) = HDPair (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x1)) (disjoint-sym ⦇⦈[ _ ] e3 (HDHole x2))
  disjoint-sym _ (fst e2) (HDHole (HNFst x)) = HDFst (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x))
  disjoint-sym _ (snd e2) (HDHole (HNSnd x)) = HDSnd (disjoint-sym ⦇⦈[ _ ] e2 (HDHole x))

  disjoint-sym _ c (HDNEHole x hd) = HDConst
  disjoint-sym _ (e2 ·: x) (HDNEHole x₁ hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e ·: x) (HDNEHole (HNAsc x₁) hd) | HDAsc ih = HDAsc (ds-lem-nehole ih x₁)
  disjoint-sym _ (X x) (HDNEHole x₁ hd) = HDVar
  disjoint-sym _ (·λ x e2) (HDNEHole x₁ hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x e2) (HDNEHole (HNLam1 x₁) hd) | HDLam1 ih = HDLam1 (ds-lem-nehole ih x₁)
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDNEHole x₂ hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDNEHole (HNLam2 x₂) hd) | HDLam2 ih = HDLam2 (ds-lem-nehole ih x₂)
  disjoint-sym _ ⦇⦈[ x ] (HDNEHole x₁ hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⦈[ x ] (HDNEHole (HNHole x₂) hd) | HDHole x₁ = HDHole (HNNEHole (flip x₂) x₁)
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDNEHole x₁ hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDNEHole (HNNEHole x₂ x₃) hd) | HDNEHole x₁ ih = HDNEHole (HNNEHole (flip x₂) x₁) (ds-lem-nehole ih x₃)
  disjoint-sym _ (e2 ∘ e3) (HDNEHole x hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e1 ∘ e3) (HDNEHole (HNAp x x₁) hd) | HDAp ih ih₁ = HDAp (ds-lem-nehole ih x) (ds-lem-nehole ih₁ x₁)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDNEHole x hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDNEHole (HNPair x x₁) hd) | HDPair ih ih₁ = HDPair (ds-lem-nehole ih x) (ds-lem-nehole ih₁ x₁)
  disjoint-sym _ (fst e2) (HDNEHole x hd) with disjoint-sym _ _ hd
  disjoint-sym _ (fst e2) (HDNEHole (HNFst x1) hd) | HDFst ih = HDFst (ds-lem-nehole ih x1)
  disjoint-sym _ (snd e2) (HDNEHole x hd) with disjoint-sym _ _ hd
  disjoint-sym _ (snd e2) (HDNEHole (HNSnd x1) hd) | HDSnd ih = HDSnd (ds-lem-nehole ih x1)

  disjoint-sym _ c (HDAp hd hd₁) = HDConst
  disjoint-sym _ (e3 ·: x) (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (e3 ·: x) (HDAp hd hd₁) | HDAsc ih | HDAsc ih1 = HDAsc (ds-lem-ap ih ih1)
  disjoint-sym _ (X x) (HDAp hd hd₁) = HDVar
  disjoint-sym _ (·λ x e3) (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (·λ x e3) (HDAp hd hd₁) | HDLam1 ih | HDLam1 ih1 = HDLam1 (ds-lem-ap ih ih1)
  disjoint-sym _ (·λ x [ x₁ ] e3) (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (·λ x [ x₁ ] e3) (HDAp hd hd₁) | HDLam2 ih | HDLam2 ih1 = HDLam2 (ds-lem-ap ih ih1)
  disjoint-sym _ ⦇⦈[ x ] (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ ⦇⦈[ x ] (HDAp hd hd₁) | HDHole x₁ | HDHole x₂ = HDHole (HNAp x₁ x₂)
  disjoint-sym _ ⦇⌜ e3 ⌟⦈[ x ] (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ ⦇⌜ e3 ⌟⦈[ x ] (HDAp hd hd₁) | HDNEHole x₁ ih | HDNEHole x₂ ih1 = HDNEHole (HNAp x₁ x₂) (ds-lem-ap ih ih1)
  disjoint-sym _ (e3 ∘ e4) (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (e3 ∘ e4) (HDAp hd hd₁) | HDAp ih ih₁ | HDAp ih1 ih2 = HDAp (ds-lem-ap ih ih1) (ds-lem-ap ih₁ ih2)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDAp hd hd₁) | HDPair ih ih₁ | HDPair ih1 ih2 = HDPair (ds-lem-ap ih ih1) (ds-lem-ap ih₁ ih2)
  disjoint-sym _ (fst e2) (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (fst e2) (HDAp hd hd₁) | HDFst ih | HDFst ih1 = HDFst (ds-lem-ap ih ih1)
  disjoint-sym _ (snd e2) (HDAp hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (snd e2) (HDAp hd hd₁) | HDSnd ih | HDSnd ih1 = HDSnd (ds-lem-ap ih ih1)

  disjoint-sym _ c (HDFst hd) = HDConst
  disjoint-sym _ (e2 ·: x) (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ·: x) (HDFst hd) | HDAsc ih = HDAsc (ds-lem-fst ih)
  disjoint-sym _ (X x) (HDFst hd) = HDVar
  disjoint-sym _ (·λ x e2) (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x e2) (HDFst hd) | HDLam1 ih = HDLam1 (ds-lem-fst ih)
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDFst hd) | HDLam2 ih = HDLam2 (ds-lem-fst ih)
  disjoint-sym _ ⦇⦈[ x ] (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⦈[ x ] (HDFst hd) | HDHole ih = HDHole (HNFst ih)
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDFst hd) | HDNEHole x₁ ih = HDNEHole (HNFst x₁) (ds-lem-fst ih)
  disjoint-sym _ (e2 ∘ e3) (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ∘ e3) (HDFst hd) | HDAp ih ih₁ = HDAp (ds-lem-fst ih) (ds-lem-fst ih₁)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDFst hd) | HDPair ih ih₁ = HDPair (ds-lem-fst ih) (ds-lem-fst ih₁)
  disjoint-sym _ (fst e2) (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ (fst e2) (HDFst hd) | HDFst ih = HDFst (ds-lem-fst ih)
  disjoint-sym _ (snd e2) (HDFst hd) with disjoint-sym _ _ hd
  disjoint-sym _ (snd e2) (HDFst hd) | HDSnd ih = HDSnd (ds-lem-fst ih)

  disjoint-sym _ c (HDSnd hd) = HDConst
  disjoint-sym _ (e2 ·: x) (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ·: x) (HDSnd hd) | HDAsc ih = HDAsc (ds-lem-snd ih)
  disjoint-sym _ (X x) (HDSnd hd) = HDVar
  disjoint-sym _ (·λ x e2) (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x e2) (HDSnd hd) | HDLam1 ih = HDLam1 (ds-lem-snd ih)
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ (·λ x [ x₁ ] e2) (HDSnd hd) | HDLam2 ih = HDLam2 (ds-lem-snd ih)
  disjoint-sym _ ⦇⦈[ x ] (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⦈[ x ] (HDSnd hd) | HDHole ih = HDHole (HNSnd ih)
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⦇⌜ e2 ⌟⦈[ x ] (HDSnd hd) | HDNEHole x₁ ih = HDNEHole (HNSnd x₁) (ds-lem-snd ih)
  disjoint-sym _ (e2 ∘ e3) (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ (e2 ∘ e3) (HDSnd hd) | HDAp ih ih₁ = HDAp (ds-lem-snd ih) (ds-lem-snd ih₁)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDSnd hd) | HDPair ih ih₁ = HDPair (ds-lem-snd ih) (ds-lem-snd ih₁)
  disjoint-sym _ (fst e2) (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ (fst e2) (HDSnd hd) | HDFst ih = HDFst (ds-lem-snd ih)
  disjoint-sym _ (snd e2) (HDSnd hd) with disjoint-sym _ _ hd
  disjoint-sym _ (snd e2) (HDSnd hd) | HDSnd ih = HDSnd (ds-lem-snd ih)

  disjoint-sym _ c (HDPair hd hd₁) = HDConst
  disjoint-sym _ (e3 ·: x) (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (e3 ·: x) (HDPair hd hd₁) | HDAsc ih | HDAsc ih1 = HDAsc (ds-lem-pair ih ih1)
  disjoint-sym _ (X x) (HDPair hd hd₁) = HDVar
  disjoint-sym _ (·λ x e3) (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (·λ x e3) (HDPair hd hd₁) | HDLam1 ih | HDLam1 ih1 = HDLam1 (ds-lem-pair ih ih1)
  disjoint-sym _ (·λ x [ x₁ ] e3) (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (·λ x [ x₁ ] e3) (HDPair hd hd₁) | HDLam2 ih | HDLam2 ih1 = HDLam2 (ds-lem-pair ih ih1)
  disjoint-sym _ ⦇⦈[ x ] (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ ⦇⦈[ x ] (HDPair hd hd₁) | HDHole x₁ | HDHole x₂ = HDHole (HNPair x₁ x₂)
  disjoint-sym _ ⦇⌜ e3 ⌟⦈[ x ] (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ ⦇⌜ e3 ⌟⦈[ x ] (HDPair hd hd₁) | HDNEHole x₁ ih | HDNEHole x₂ ih1 = HDNEHole (HNPair x₁ x₂) (ds-lem-pair ih ih1)
  disjoint-sym _ (e3 ∘ e4) (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (e3 ∘ e4) (HDPair hd hd₁) | HDAp ih ih₁ | HDAp ih1 ih2 = HDAp (ds-lem-pair ih ih1) (ds-lem-pair ih₁ ih2)
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ ⟨ e2 , e3 ⟩ (HDPair hd hd₁) | HDPair ih ih₁ | HDPair ih1 ih2 = HDPair (ds-lem-pair ih ih1) (ds-lem-pair ih₁ ih2)
  disjoint-sym _ (fst e2) (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (fst e2) (HDPair hd hd₁) | HDFst ih | HDFst ih1 = HDFst (ds-lem-pair ih ih1)
  disjoint-sym _ (snd e2) (HDPair hd hd₁) with disjoint-sym _ _ hd | disjoint-sym _ _ hd₁
  disjoint-sym _ (snd e2) (HDPair hd hd₁) | HDSnd ih | HDSnd ih1 = HDSnd (ds-lem-pair ih ih1)

  -- note that this is false, so holes-disjoint isn't transitive
  -- disjoint-new : ∀{e1 e2 u} → holes-disjoint e1 e2 → hole-name-new e1 u → hole-name-new e2 u

  -- it's also not reflexive, because ⦇⦈[ u ] isn't hole-disjoint with
  -- itself since refl : u == u; it's also not anti-reflexive, because the
  -- expression c *is* hole-disjoint with itself (albeit vacuously)


postulate
  A : Set

record R : Set where
  field
    a : A
  b : A
  b = a
open R

test : A
test = b


-- 2014-03-06 Andreas, Reported by Fabien Renaud

-- This is a larger example for the termination checker, to test performance.
-- In 2014-02-X, it exhausted the heap.

-- 2013-03-17
-- The termination checker now rejects this code instead of crashing.
-- I do not know whether it is supposed to terminate.

-- {-# OPTIONS -v term.matrices:40 #-}

module Issue1075 where

open import Common.Prelude renaming (Nat to ℕ) hiding (map; length)
open import Common.Product
open import Common.Sum
open import Common.Equality

[_] : {A : Set} → A → List A
[ x ] = x ∷ []

map : {A B : Set} → (A → B) → List A → List B
map f []       = []
map f (x ∷ xs) = f x ∷ map f xs

foldr : ∀ {A B : Set} → (A → B → B) → B → List A → B
foldr c n []       = n
foldr c n (x ∷ xs) = c x (foldr c n xs)

length : ∀ {A} → List A → ℕ
length = foldr (λ _ → suc) 0

postulate
  _∈_ : {A : Set} → A → List A → Set
  _⊆_ : {A : Set} → List A → List A → Set

data All {A : Set}
         (P : A → Set) : List A → Set where
  []  : All P []
  _∷_ : ∀ {x xs} (px : P x) (pxs : All P xs) → All P (x ∷ xs)

_⋐_ : ∀{A} (P Q : A → Set) → Set
P ⋐ Q = ∀{x} → P x → Q x

map-all : ∀ {A : Set} {P : A → Set} {Q : A → Set} →
      P ⋐ Q → All P ⋐ All Q
map-all g []         = []
map-all g (px ∷ pxs) = g px ∷ map-all g pxs

-- Propositions and polarity

data Polarity : Set where
  ⁺ : Polarity
  ⁻ : Polarity

data Type : Polarity → Set where
  a : (Q : String) (⁼ : Polarity) → Type ⁼
  --
  ↓ : (A : Type ⁻) → Type ⁺
  ⊥⁺ : Type ⁺
  _∨_ : (A B : Type ⁺) → Type ⁺
  ⊤⁺ : Type ⁺
  _∧⁺_ : (A B : Type ⁺) → Type ⁺
  --
  ↑ : (A : Type ⁺) → Type ⁻
  _⊃_ : (A : Type ⁺) (B : Type ⁻) → Type ⁻
  ⊤⁻ : Type ⁻
  _∧⁻_ : (A B : Type ⁻) → Type ⁻

-- Judgmental infrastructure

data Conc : Set where
  Inv  : (A : Type ⁻) → Conc
  True : (A : Type ⁺) → Conc
  Susp : (A : Type ⁻) → Conc

stable : Conc → Set
stable (Inv A) = ⊥
stable (True A) = ⊤
stable (Susp A) = ⊤

suspnormal : Conc → Set
suspnormal (Inv A) = ⊤
suspnormal (True A) = ⊤
suspnormal (Susp (a Q .⁻)) = ⊤
suspnormal (Susp (↑ A)) = ⊥
suspnormal (Susp (A ⊃ A₁)) = ⊥
suspnormal (Susp ⊤⁻) = ⊥
suspnormal (Susp (A ∧⁻ A₁)) = ⊥

suspstable : Conc -> Set
suspstable U = stable U × suspnormal U

data Hyp : Set where
  HSusp : (A : Type ⁺) → Hyp
  Pers : (A : Type ⁻) → Hyp

Ctx = List Hyp

hsusp-inj : ∀{x y} → HSusp x ≡ HSusp y → x ≡ y
hsusp-inj refl = refl

{- Suspension normality: all suspended propositions are atomic -}
suspnormalΓ : Ctx → Set
suspnormalΓ Γ = ∀{A} → HSusp A {-Membership-≡.-}∈ Γ → ∃ λ Q → A ≡ (a Q ⁺)

postulate
  conssusp   : ∀{Γ Q} → suspnormalΓ Γ → suspnormalΓ ((HSusp (a Q ⁺)) ∷ Γ)
  conspers   : ∀{Γ A} → suspnormalΓ Γ → suspnormalΓ ((Pers A) ∷ Γ)
  fromctx    : ∀{A B Γ} (Γ' : Ctx) → B ∈ (Γ' ++ A ∷ Γ) → (A ≡ B) ⊎ (B ∈ (Γ' ++ Γ))
  fromctxGen : ∀{A} {Γ : Ctx} → (Γ' : Ctx) → (L : Ctx) → A ∈ (Γ' ++ L ++ Γ) → (A ∈ L) ⊎ (A ∈ (Γ' ++ Γ))

-- Sequent calculus

data SeqForm : Set where
  Rfoc : (A : Type ⁺) → SeqForm
  Left : (L : List (Type ⁺) ⊎ Type ⁻) (U : Conc) → SeqForm

suspnormalF : SeqForm → Set
suspnormalF (Rfoc A) = ⊤
suspnormalF (Left L U) = suspnormal U

data Exp (Γ : Ctx) : SeqForm → Set

Value : (Γ : Ctx) → Type ⁺ → Set
Value Γ A = Exp Γ (Rfoc A)

Term : (Γ : Ctx) → List (Type ⁺) → Conc → Set
Term Γ Ω U = Exp Γ (Left (inj₁ Ω) U)

Spine : (Γ : Ctx) (A : Type ⁻) (U : Conc) → Set
Spine Γ A U = Exp Γ (Left (inj₂ A) U)

data Exp Γ where

  -- Values
  id⁺ : ∀{A}
    (v : HSusp A ∈ Γ)
    → Value Γ A
  ↓R : ∀{A}
    (N : Term Γ [] (Inv A))
    → Value Γ (↓ A)
  ∨R₁ : ∀{A B}
    (V : Value Γ A)
    → Value Γ (A ∨ B)
  ∨R₂ : ∀{A B}
    (V : Value Γ B)
    → Value Γ (A ∨ B)
  ⊤⁺R : Value Γ ⊤⁺
  ∧⁺R : ∀{A B}
    (V₁ : Value Γ A)
    (V₂ : Value Γ B)
    → Value Γ (A ∧⁺ B)

  -- Terms
  focR : ∀{A}
    (V : Value Γ A)
    → Term Γ [] (True A)
  focL : ∀{A U}
    (pf : stable U)
    (x : Pers A ∈ Γ)
    (Sp : Spine Γ A U)
    → Term Γ [] U
  η⁺ : ∀{Q Ω U}
    (N : Term (HSusp (a Q ⁺) ∷ Γ) Ω U)
    → Term Γ (a Q ⁺ ∷ Ω) U
  ↓L : ∀{A Ω U}
    (N : Term (Pers A ∷ Γ) Ω U)
    → Term Γ (↓ A ∷ Ω) U
  ⊥L : ∀{U Ω}
    → Term Γ (⊥⁺ ∷ Ω) U
  ∨L : ∀{A B Ω U}
    (N₁ : Term Γ (A ∷ Ω) U)
    (N₂ : Term Γ (B ∷ Ω) U)
    → Term Γ ((A ∨ B) ∷ Ω) U
  ⊤⁺L : ∀{U Ω}
    (N : Term Γ Ω U)
    → Term Γ (⊤⁺ ∷ Ω) U
  ∧⁺L : ∀{U Ω A B}
    (N : Term Γ (A ∷ B ∷ Ω) U)
    → Term Γ ((A ∧⁺ B) ∷ Ω) U
  η⁻ : ∀{Q}
    (N : Term Γ [] (Susp (a Q ⁻)))
    → Term Γ [] (Inv (a Q ⁻))
  ↑R : ∀{A}
    (N : Term Γ [] (True A))
    → Term Γ [] (Inv (↑ A))
  ⊃R : ∀{A B}
    (N : Term Γ [ A ] (Inv B))
    → Term Γ [] (Inv (A ⊃ B))
  ⊤⁻R : Term Γ [] (Inv ⊤⁻)
  ∧⁻R : ∀{A B}
    (N₁ : Term Γ [] (Inv A))
    (N₂ : Term Γ [] (Inv B))
    → Term Γ [] (Inv (A ∧⁻ B))

  -- Spines
  id⁻ : ∀{A}
    → Spine Γ A (Susp A)
  ↑L : ∀{A U}
    (N : Term Γ [ A ] U)
    → Spine Γ (↑ A) U
  ⊃L : ∀{A B U}
    (V : Value Γ A)
    (Sp : Spine Γ B U)
    → Spine Γ (A ⊃ B) U
  ∧⁻L₁ : ∀{A B U}
    (Sp : Spine Γ A U)
    → Spine Γ (A ∧⁻ B) U
  ∧⁻L₂ : ∀{A B U}
    (Sp : Spine Γ B U)
    → Spine Γ (A ∧⁻ B) U

-- Weakening
postulate
  sub-cons-congr : ∀{A : Set} {x : A} {xs ys : List A}
      → xs ⊆ ys
      → (x ∷ xs) ⊆ (x ∷ ys)

  sub-wkex : ∀{A : Set} {x y : A} {xs ys : List A}
    → (x ∷ xs) ⊆ (x ∷ y ∷ xs)

  sub-cntr : ∀{A : Set} {x : A}
       → (xs : List A)
       → x ∈ xs
       → (x ∷ xs) ⊆ xs

  wk : ∀{Γ Γ' Form} → Γ ⊆ Γ' → Exp Γ Form → Exp Γ' Form
  wken : ∀{Γ A Form} → Exp Γ Form → Exp (A ∷ Γ) Form

wken-all-rfoc : ∀{Γ' Γ xs B}
  → All (λ A → Exp (Γ' ++ Γ) (Rfoc A)) xs
  → All (λ A → Exp (B ∷ (Γ' ++ Γ)) (Rfoc A)) xs
wken-all-rfoc [] = []
wken-all-rfoc (px ∷ All) = map-all (\x → wken x) (px ∷ All)

wken-all-inv : ∀{Γ' Γ Ω xs B}
  → All (λ A → Exp (Γ' ++ Γ) (Left (inj₁ Ω) (Inv A))) xs
  → All (λ A → Exp (B ∷ (Γ' ++ Γ)) (Left (inj₁ Ω) (Inv A))) xs
wken-all-inv [] = []
wken-all-inv (px ∷ All) = map-all (\x → wken x) (px ∷ All)

postulate
  wkex : ∀{Γ A B Form} → Exp (A ∷ Γ) Form → Exp (A ∷ B ∷ Γ) Form
  wkex2 : ∀{Γ A B C Form} → Exp (A ∷ B ∷ Γ) Form → Exp (A ∷ B ∷ C ∷ Γ) Form

cntr : ∀{A Form} → (Γ : Ctx) → A ∈ Γ → Exp (A ∷ Γ) Form → Exp Γ Form
cntr Γ In Exp = wk (sub-cntr Γ In) Exp

postulate

  exch-cons : ∀{Γ Γ' LA C x} → Term (x ∷ Γ ++ Γ') LA C → Term (Γ ++ x ∷ Γ') LA C

  subst⁺ :
    ∀{Γ Form}
    → (Γ' : Ctx)
    → (LAi : List (Type ⁺))
    → All (\x → Value (Γ' ++ Γ) x) LAi
    → Exp (Γ' ++ map (\x → HSusp x) (LAi) ++ Γ) Form
    → Exp (Γ' ++ Γ) Form

  subst⁻ : ∀{Γ A L U}
    → stable U
    → Exp Γ (Left L (Susp A))
    → Spine Γ A U
    → Exp Γ (Left L U)

cut⁺ : ∀{U Γ Ω}
  → suspnormalΓ Γ
  → suspnormal U
  → (LAi : List (Type ⁺))
  → All (\x → Value Γ x) LAi
  → Term Γ (LAi ++ Ω) U
  → Term Γ Ω U

cut⁻ : ∀{U Γ N}
  → suspnormalΓ Γ
  → suspstable U
  → (LA : List (Type ⁻))
  → (length LA ≡ suc N)
  → All (\x → Term Γ [] (Inv x)) LA
  → All (\x → Spine Γ x U) LA
  → Term Γ [] U

rsubst+  : ∀{Ω Γ Form} (Γ' : Ctx)
  → suspnormalΓ (Γ' ++ Γ)
  → suspnormal Form
  → (LA+ : List (Type ⁺))
  → (LA- : List (Type ⁻))
  → All (\x →  Term (Γ' ++ Γ) [] (Inv x)) LA-
  → All (\x → Value Γ x) LA+
  → Term (Γ' ++ (map (\x → Pers x) LA-) ++ Γ) (LA+ ++ Ω) Form
  → Term (Γ' ++ Γ) Ω  Form

postulate rsubst-v : ∀{Γ Form} (Γ' : Ctx)   → suspnormalΓ (Γ' ++ Γ)  → suspnormalF Form  → (LA- : List (Type ⁻))  → All (\x →  Term (Γ' ++ Γ) [] (Inv x)) LA-  → Exp (Γ' ++ (map (\x → Pers x) LA-) ++ Γ) Form  → Exp (Γ' ++ Γ) Form

-- {-# NO_TERMINATION_CHECK #-}
--rsubst : ∀{Γ Form A} (Γ' : Ctx)
--  → suspnormalΓ (Γ' ++ Γ)
--  → suspnormalF Form
--  → Term (Γ' ++ Γ) [] (Inv A)
--  → Exp (Γ' ++ (Pers A) ∷ Γ) Form
--  → Exp (Γ' ++ Γ) Form

lsubst : ∀{Γ U L A}
  → suspnormalΓ Γ
  → suspstable U
  → Exp Γ (Left L (True A))
  → Term Γ [ A ] U
  → Exp Γ (Left L U)

-- -- Positive principal substitution

cut⁺ pfΓ pf [] Values T = T

cut⁺ pfΓ pf (z ∷ LA) ((id⁺ v) ∷ Values) N with (pfΓ v)
cut⁺ pfΓ pf (.(a A ⁺) ∷ LA) (id⁺ v ∷ Values) (η⁺ N) | A , refl =
  subst⁺ [] (a A ⁺ ∷ []) (id⁺ v ∷ []) (cut⁺ (conssusp pfΓ) pf LA (wken-all-rfoc {[]} Values) N)
cut⁺ {U} {Γ} {Ω} pfΓ pf (.(↓ A) ∷ LA) (↓R N ∷ Values) (↓L {A} N') =
  rsubst+ [] pfΓ pf LA (A ∷ []) (N ∷ []) Values N'
  -- rsubst+ [] pfΓ pf N (cut⁺ (conspers pfΓ) pf LA (wken-all-rfoc {[]} Values) N₁)
cut⁺ pfΓ pf (.(A ∨ B) ∷ LA) (∨R₁ V ∷ Values) (∨L {A} {B} N₁ N₂) = cut⁺ pfΓ pf (A ∷ LA) (V ∷ Values) N₁
cut⁺ pfΓ pf (.(A ∨ B) ∷ LA) (∨R₂ V ∷ Values) (∨L {A} {B} N₁ N₂) =  cut⁺ pfΓ pf (B ∷ LA) (V ∷ Values) N₂
cut⁺ pfΓ pf (.⊤⁺ ∷ LA) (px ∷ Values) (⊤⁺L N) = cut⁺ pfΓ pf LA Values N
cut⁺ pfΓ pf ((A ∧⁺ B) ∷ LA) (∧⁺R V₁ V₂ ∷ Values) (∧⁺L N) = cut⁺ pfΓ pf (B ∷ LA) (V₂ ∷ Values) (cut⁺ pfΓ pf ((A ∷ [])) (V₁ ∷ [])  N)

-- -- Negative principle substitution

cut⁻ pfΓ pf [] () LExp LExp'
cut⁻ pfΓ pf (_ ∷ LA) LL (focL () x Sp ∷ LExp) (px₁ ∷ LExp')

cut⁻ pfΓ pf (a Q .⁻ ∷ LA) LL (η⁻ N₁ ∷ LExp) (id⁻ ∷ LExp') = N₁

cut⁻ pfΓ (proj₁ , ()) (⊤⁻ ∷ LA) LL (⊤⁻R ∷ LExp) (id⁻ ∷ LExp')
cut⁻ pfΓ (proj₁ , ()) (↑ x ∷ LA) LL (↑R N₁ ∷ LExp) (id⁻ ∷ LExp')
cut⁻ pfΓ (proj₁ , ()) ((x ⊃ x₁) ∷ LA) LL (⊃R N₁ ∷ LExp) (id⁻ ∷ LExp')
cut⁻ pfΓ (proj₁ , ()) ((x ∧⁻ x₁) ∷ LA) LL (∧⁻R N₁ N₂ ∷ LExp) (id⁻ ∷ LExp')

cut⁻ pfΓ pf (↑ x ∷ LA) LL (↑R N₁ ∷ LExp) (↑L N₂ ∷ LExp') = lsubst pfΓ pf N₁ N₂

cut⁻ pfΓ pf ((x ⊃ x₁) ∷ LA) refl (⊃R N₁ ∷ LExp) (⊃L V Sp ∷ LExp') =
  cut⁻ pfΓ pf (x₁ ∷ LA) refl ((cut⁺ pfΓ _ (x ∷ []) (V ∷ []) N₁) ∷ LExp) (Sp ∷ LExp')

cut⁻ pfΓ pf ((x ∧⁻ x₁) ∷ LA) refl (∧⁻R N₁ N₂ ∷ LExp) (∧⁻L₁ Sp ∷ LExp') =
  cut⁻ pfΓ pf (x ∷ LA) refl (N₁ ∷ LExp) (Sp ∷ LExp')

cut⁻ pfΓ pf ((x ∧⁻ x₁) ∷ LA) LL (∧⁻R N₁ N₂ ∷ LExp) (∧⁻L₂ Sp ∷ LExp') =
  cut⁻ pfΓ pf (x₁ ∷ LA) refl (N₂ ∷ LExp) (Sp ∷ LExp')

-- helper : ∀{Γ LA- A} →
--   All (λ x₂ → Exp (Γ) (Left (inj₁ []) (Inv x₂))) LA-
--   → Any (_≡_ (Pers A)) (map Pers LA-)
--   → All (λ x₂ → Exp (Γ) (Left (inj₁ []) (Inv x₂))) (A ∷ [])
-- helper [] ()
-- helper (px ∷ L) (here refl) = px ∷ []
-- helper (px ∷ L) (there In) = helper L In

rsubst+ Γ' pfΓ pf [] LA- LT [] (focR V) = focR (rsubst-v Γ' pfΓ _ LA- LT V )
rsubst+ Γ' pfΓ pf [] LA- LT [] (focL pf₁ x Sp) with fromctxGen Γ' (map Pers LA-) x
rsubst+ Γ' pfΓ pf [] LA- LT [] (focL {A} pf₁ x Sp) | inj₁ x₁ =
  cut⁻ pfΓ (pf₁ , pf) (A ∷ []) refl {!(helper LT x₁)!} ((rsubst-v Γ' pfΓ pf LA- LT Sp) ∷ [])
rsubst+ Γ' pfΓ pf [] LA- LT [] (focL pf₁ x Sp) | inj₂ y =
  focL pf₁ y (rsubst-v Γ' pfΓ pf LA- LT Sp )
rsubst+ Γ' pfΓ pf [] LA- LT [] (η⁺ N) =
  η⁺ (rsubst-v (_ ∷ Γ' ) (conssusp pfΓ) pf LA- (wken-all-inv {[]} LT) N )
rsubst+ Γ' pfΓ pf [] LA- LT [] (↓L N) =
  ↓L (rsubst-v (_ ∷ Γ' ) (conspers pfΓ) pf LA- (wken-all-inv {[]} LT) N )
rsubst+ Γ' pfΓ pf [] LA- LT [] ⊥L = ⊥L
rsubst+ Γ' pfΓ pf [] LA- LT [] (∨L N₁ N₂) =
  ∨L (rsubst+ Γ' pfΓ pf [] LA- LT [] N₁) (rsubst+ Γ' pfΓ pf [] LA- LT [] N₂)
rsubst+ Γ' pfΓ pf [] LA- LT [] (⊤⁺L N) = ⊤⁺L (rsubst+ Γ' pfΓ pf [] LA- LT [] N )
rsubst+ Γ' pfΓ pf [] LA- LT [] (∧⁺L N) = ∧⁺L (rsubst+  Γ' pfΓ pf [] LA- LT [] N)
rsubst+ Γ' pfΓ pf [] LA- LT [] (η⁻ N) = η⁻ (rsubst+  Γ' pfΓ pf [] LA- LT [] N)
rsubst+ Γ' pfΓ pf [] LA- LT [] (↑R N) = ↑R (rsubst+  Γ' pfΓ pf [] LA- LT [] N)
rsubst+ Γ' pfΓ pf [] LA- LT [] (⊃R N) = ⊃R (rsubst+  Γ' pfΓ pf [] LA- LT [] N)
rsubst+ Γ' pfΓ pf [] LA- LT [] ⊤⁻R = ⊤⁻R
rsubst+ Γ' pfΓ pf [] LA- LT [] (∧⁻R N₁ N₂) =
  ∧⁻R (rsubst+  Γ' pfΓ pf [] LA- LT [] N₁) (rsubst+  Γ' pfΓ pf [] LA- LT [] N₂)

-- rsubst+ Γ' pfΓ pf (↓ x ∷ LA+) LA- LT (id⁺ v ∷ Values) (↓L N) with (pfΓ (++ʳ Γ' v))
-- ... | proj₁ , ()

-- ------------
-- -- Part which requires a list for negative types
-- rsubst+ {Ω} {Γ} Γ' pfΓ pf (a Q .⁺ ∷ LA+) LA- LT (id⁺ v ∷ Values) (η⁺ N) =
--   rsubst+ Γ' pfΓ pf LA+ LA- (LT ) Values
--           (cntr (Γ' ++ map Pers LA- ++ Γ) (++ʳ Γ' (++ʳ (map Pers LA-) v) ) N)

-- rsubst+ {Ω} {Γ} Γ' pfΓ pf (↓ x ∷ LA+) LA- LT (↓R N ∷ Values) (↓L N₁) =
--   rsubst+ Γ' pfΓ pf LA+ (x ∷ LA-) ((wk (λ x₂ → ++ʳ Γ' x₂) N) ∷ LT)  Values (exch-cons {Γ'} {map Pers LA- ++ Γ} N₁)
--------------
-- rsubst+ Γ' pfΓ pf (⊥⁺ ∷ LA+) LA- LT (id⁺ v ∷ Values) ⊥L with (pfΓ (++ʳ Γ' v))
-- ... | proj₁ , ()
-- rsubst+ Γ' pfΓ pf (x ∨ x₁ ∷ LA+) LA- LT (id⁺ v ∷ Values) (∨L N₁ N₂)  with (pfΓ (++ʳ Γ' v))
-- ... | proj₁ , ()
rsubst+ Γ' pfΓ pf ((x ∨ x₁) ∷ LA+) LA- LT (∨R₁ V ∷ Values) (∨L N₁ N₂) = rsubst+ Γ' pfΓ pf (x ∷ LA+) LA- LT (V ∷ Values) N₁
rsubst+ Γ' pfΓ pf ((x ∨ x₁) ∷ LA+) LA- LT (∨R₂ V ∷ Values) (∨L N₁ N₂) = rsubst+ Γ' pfΓ pf (x₁ ∷ LA+) LA- LT (V ∷ Values) N₂
rsubst+ Γ' pfΓ pf (⊤⁺ ∷ LA+) LA- LT (_ ∷ Values) (⊤⁺L N) =  rsubst+ Γ' pfΓ pf LA+ LA- LT Values N
-- rsubst+ Γ' pfΓ pf (x ∧⁺ x₁ ∷ LA+) LA- LT (id⁺ v ∷ Values) (∧⁺L N) with (pfΓ (++ʳ Γ' v))
-- ... | proj₁ , ()
rsubst+ Γ' pfΓ pf ((x ∧⁺ x₁) ∷ LA+) LA- LT (∧⁺R V₁ V₂ ∷ Values) (∧⁺L N) =
  rsubst+ Γ' pfΓ pf (x ∷ x₁ ∷ LA+) LA- LT (V₁ ∷ V₂ ∷ Values) N
rsubst+ {_} {_} {_} _ _ _ _ _ _ _ _ = ?

-- -- -- Substitution out of terms
lsubst pfΓ pf (focR {A} V) N = cut⁺ pfΓ (proj₂ pf) (A ∷ []) (V ∷ []) N
lsubst pfΓ pf (focL pf' x Sp) N = focL (proj₁ pf) x (lsubst pfΓ pf Sp N)
lsubst pfΓ pf (η⁺ M) N = η⁺ (lsubst (conssusp pfΓ) pf M (wken N))
lsubst pfΓ pf (↓L M) N = ↓L (lsubst (conspers pfΓ) pf M (wken N))
lsubst pfΓ pf ⊥L M = ⊥L
lsubst pfΓ pf (∨L M₁ M₂) N = ∨L (lsubst pfΓ pf M₁ N) (lsubst pfΓ pf M₂ N)
lsubst pfΓ pf (⊤⁺L M) N = ⊤⁺L (lsubst pfΓ pf M N)
lsubst pfΓ pf (∧⁺L M) N = ∧⁺L (lsubst pfΓ pf M N)

-- Substitution of of spines
lsubst pfΓ pf (↑L M) N = ↑L (lsubst pfΓ pf M N)
lsubst pfΓ pf (⊃L V Sp) N = ⊃L V (lsubst pfΓ pf Sp N)
lsubst pfΓ pf (∧⁻L₁ Sp) N = ∧⁻L₁ (lsubst pfΓ pf Sp N)
lsubst pfΓ pf (∧⁻L₂ Sp) N = ∧⁻L₂ (lsubst pfΓ pf Sp N)


module Issue427 where

data T : Set where
  tt : T

test = (λ {s : T} {t : T} → t) {tt} {tt}

f : {s t : T} → T
f = tt

test₂ = (let x = tt in λ {s : T} {t : T} → x) {tt} {tt}


module Impure.LFRef.Properties.Confluence where

open import Prelude
open import Data.List
open import Extensions.List
open import Extensions.Nat

open import Impure.LFRef.Syntax
open import Impure.LFRef.Eval

private
  val-lemma : ∀ {t} → (v v₁ : Val t)  → v ≡ v₁
  val-lemma loc loc = refl
  val-lemma unit unit = refl
  val-lemma con con = refl

-- A stronger property than Church-Rosser
deterministic : ∀ {𝕊 e e' e'' μ μ' μ''} →
                𝕊 ⊢ e , μ ≻ e' , μ' → 𝕊 ⊢ e , μ ≻ e'' , μ'' →
                ---------------------------------------------
                e' ≡ e'' × μ' ≡ μ''
deterministic (funapp-β x p₁) (funapp-β x' p) with []=-functional _ _ x x'
deterministic (funapp-β x refl) (funapp-β x' refl) | refl = refl , refl
deterministic {μ = μ} (ref-val p) (ref-val q) = refl , cong (λ v → μ ∷ʳ (, v)) (val-lemma p q)
deterministic (ref-val v) (ref-clos ())
deterministic (≔-val p v) (≔-val q w) with <-unique p q | val-lemma v w
... | refl | refl = refl , refl
deterministic (≔-val p v) (≔-clos₁ ())
deterministic (≔-val p v) (≔-clos₂ _ ())
deterministic {μ = μ} (!-val p) (!-val q) = (cong (λ v → tm (!load μ v)) (<-unique p q)) , refl
deterministic (!-val p) (!-clos ())
deterministic (ref-clos ()) (ref-val v)
deterministic (ref-clos p) (ref-clos q) with deterministic p q
... | refl , refl = refl , refl
deterministic (!-clos ()) (!-val p)
deterministic (!-clos p) (!-clos q) with deterministic p q
... | refl , refl = refl , refl
deterministic (≔-clos₁ ()) (≔-val p v)
deterministic (≔-clos₁ p) (≔-clos₁ q) with deterministic p q
... | refl , refl = refl , refl
deterministic (≔-clos₁ ()) (≔-clos₂ (tm x) q)
deterministic (≔-clos₂ (tm x) p) (≔-clos₁ ())
deterministic (≔-clos₂ _ ()) (≔-val p v)
deterministic (≔-clos₂ _ p) (≔-clos₂ _ q) with deterministic p q
... | refl , refl = refl , refl

deterministic-seq : ∀ {𝕊 e e' e'' μ μ' μ''} →
                𝕊 ⊢ e , μ ≻ₛ e' , μ' →
                𝕊 ⊢ e , μ ≻ₛ e'' , μ'' →
                ---------------------------------------------
                e' ≡ e'' × μ' ≡ μ''
deterministic-seq lett-β lett-β = refl , refl
deterministic-seq lett-β (lett-clos ())
deterministic-seq (lett-clos ()) lett-β
deterministic-seq (lett-clos p) (lett-clos q) with deterministic p q
... | refl , refl = refl , refl
deterministic-seq (ret-clos x) (ret-clos x') with deterministic x x'
... | refl , refl = refl , refl

open import Data.Star
confluence : ∀ {𝕊 e e' e'' μ μ' μ''} →
             𝕊 ⊢ e , μ ≻⋆ e' , μ' → 𝕊 ⊢ e , μ ≻⋆ e'' , μ'' →
             SeqExpVal e' → SeqExpVal e'' →
             -----------------------------------------------
             e' ≡ e'' × μ' ≡ μ''
confluence ε ε z y = refl , refl
confluence ε (ret-clos () ◅ _) (ret-tm _) _
confluence (ret-clos () ◅ _) ε _ (ret-tm _)
confluence (lett-β ◅ _) ε _ ()
confluence (lett-clos _ ◅ _) ε _ ()
confluence (x ◅ p) (x' ◅ p') v w with deterministic-seq x x'
... | refl , refl with confluence p p' v w
... | refl , refl = refl , refl


{-# OPTIONS --universe-polymorphism #-}

-- Support stuff mostly stolen or adapted from the Agda standard library

module Support where

data Level : Set where
  zero : Level
  suc  : (i : Level) → Level

{-# BUILTIN LEVEL     Level #-}
{-# BUILTIN LEVELZERO zero  #-}
{-# BUILTIN LEVELSUC  suc   #-}

-- Maximum.

infixl 6 _⊔_
infixr 2 _×_

_⊔_ : Level → Level → Level
zero  ⊔ j     = j
suc i ⊔ zero  = suc i
suc i ⊔ suc j = suc (i ⊔ j)

{-# BUILTIN LEVELMAX _⊔_ #-}

infix 4 _≣_

data _≣_ {ℓ} {A : Set ℓ} (x : A) : A → Set ℓ where
  ≣-refl : x ≣ x

{-# BUILTIN EQUALITY _≣_ #-}
{-# BUILTIN REFL ≣-refl #-}

≣-trans : ∀ {a} {A : Set a} → ∀ {x y z : A} → y ≣ z → x ≣ y → x ≣ z
≣-trans ≣-refl ≣-refl = ≣-refl

≣-sym : ∀ {a} {A : Set a} → ∀ {x y : A} → x ≣ y → y ≣ x
≣-sym ≣-refl = ≣-refl

≣-subst : ∀ {a p} {A : Set a} → (P : A → Set p) → ∀ {x y} → x ≣ y → P x → P y
≣-subst P ≣-refl x = x

≣-subst₂ : ∀ {a b p} {A : Set a} {B : Set b} (P : A → B → Set p)
           {x₁ x₂ y₁ y₂} → x₁ ≣ x₂ → y₁ ≣ y₂ → P x₁ y₁ → P x₂ y₂
≣-subst₂ P ≣-refl ≣-refl p = p

≣-cong : ∀ {a b} {A : Set a} {B : Set b}
         (f : A → B) {x y} → x ≣ y → f x ≣ f y
≣-cong f ≣-refl = ≣-refl

≣-cong₂ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c}
          (f : A → B → C) {x y u v} → x ≣ y → u ≣ v → f x u ≣ f y v
≣-cong₂ f ≣-refl ≣-refl = ≣-refl

infixr 4 _,_

record Σ {a b} (A : Set a) (B : A → Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    proj₁ : A
    proj₂ : B proj₁

open Σ public

∃ : ∀ {a b} {A : Set a} → (A → Set b) → Set (a ⊔ b)
∃ = Σ _

∃₂ : ∀ {a b c} {A : Set a} {B : A → Set b}
     (C : (x : A) → B x → Set c) → Set (a ⊔ b ⊔ c)
∃₂ C = ∃ λ a → ∃ λ b → C a b

record Σ′ {a b} (A : Set a) (B : A → Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    proj₁′ : A
    .proj₂′ : B proj₁′

open Σ′ public

∃′ : ∀ {a b} {A : Set a} → (A → Set b) → Set (a ⊔ b)
∃′ = Σ′ _

∃₂′ : ∀ {a b c} {A : Set a} {B : A → Set b}
     (C : (x : A) → B x → Set c) → Set (a ⊔ b ⊔ c)
∃₂′ C = ∃ λ a → ∃′ λ b → C a b

record _×_ {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    fst : A
    snd : B

open _×_ public

⟨_,_⟩ : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} → (A → B) → (C → D) → A × C → B × D
⟨ f , g ⟩ (x , y) = f x , g y

⟨_&_⟩ : ∀ {a b d} {A : Set a} {B : Set b} {D : Set d} → (A → B) → (A → D) → A → B × D
⟨ f & g ⟩ x = f x , g x

_∙_ : ∀ {a b c}
        {A : Set a} {B : A → Set b} {C : {x : A} → B x → Set c} →
      (∀ {x} (y : B x) → C y) → (g : (x : A) → B x) →
      ((x : A) → C (g x))
f ∙ g = λ x → f (g x)

∙-assoc : ∀ {a b c d} {A : Set a} {B : A → Set b} {C : {x : A} → B x → Set c} {D : {x : A} {y : B x} → C y → Set d} → (f : ∀ {x} {y : B x} (z : C y) → D z) → (g : ∀ {x} (y : B x) → C y) → (h : (x : A) → B x) → {x : A} → (f ∙ (g ∙ h)) x ≣ ((f ∙ g) ∙ h) x
∙-assoc f g h {x} = ≣-refl

_∙′_ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} →
       (B → C) → (A → B) → (A → C)
f ∙′ g = _∙_ f g

_∙₂_ : ∀ {a b c d}
         {A : Set a} {B : A → Set b} {C : {x : A} → B x → Set c}
         {D : {x : A} {y : B x} → C y → Set d} →
      (∀ {x} {y : B x} (z : C y) → D z) → (g : (x : A) (y : B x) → C y) →
      ((x : A) (y : B x) → D (g x y))
f ∙₂ g = λ x y → f (g x y)

_∙₃_ : ∀ {a b c d e}
         {A : Set a} {B : A → Set b} {C : {x : A} → B x → Set c}
         {D : {x : A} {y : B x} → C y → Set d}
         {E : {x : A} {y : B x} {z : C y} → D z → Set e} →
      (∀ {x} {y : B x} {z : C y} (w : D z) → E w) →
      (g : (x : A) (y : B x) (z : C y) → D z) →
      ((x : A) (y : B x) (z : C y) → E (g x y z))
f ∙₃ g = λ x y z → f (g x y z)

flip : ∀ {a b c} {A : Set a} {B : Set b} {C : A → B → Set c} →
       ((x : A) (y : B) → C x y) → ((y : B) (x : A) → C x y)
flip f = λ x y → f y x

_$↑_ : ∀ {a b c}
         {A : Set a} {B : A → Set b} {C : {x : A} → B x → Set c} →
       (∀ {x} (y : B x) → C y) → (g : (x : A) → B x) →
       ((x : A) → C (g x))
_$↑_ = _∙_

_$⇈_ : ∀ {ℓ a b}
          {T : Set ℓ} {A : T → Set a} {B : (i : T) → (x : A i) → Set b} →
       (f : (i : T) → ∀ (x : A i) → B i x) → (x : (i : T) → A i) →
       ((i : T) → B i (x i))
f $⇈ x = λ i → f i (x i)

{- John Major equality would be nice for stating this result ... -}
$⇈-obs : ∀ {ℓ a b}
           {T : Set ℓ} {A : T → Set a} {B : (i : T) → (x : A i) → Set b} →
         {f f′ : (i : T) → ∀ (x : A i) → B i x}
           (eq-f : ∀ {i} {x : A i} → f i x ≣ f′ i x) →
         {x x′ : (i : T) → A i} (eq-x : ∀ {i} → x i ≣ x′ i) →
         ∀ {i} → (≣-subst (B i) (eq-x {i}) ((f $⇈ x) i) ≣ (f′ $⇈ x′) i)
$⇈-obs eq-f {x} {x′} eq-x {i} rewrite eq-x {i} | eq-f {i} {x′ i} = ≣-refl

lift↑ : ∀ {ℓ a b}
          {T : Set ℓ} {A : T → Set a} {B : (i : T) → A i → Set b} →
        (f : ∀ {i} (x : A i) → B i x) → (x : (i : T) → A i) →
        ((i : T) → B i (x i))
lift↑ = _$↑_

lift↑₂ : ∀ {ℓ a b c}
           {T : Set ℓ} {A : T → Set a} {B : (i : T) → Set b}
           {C : (i : T) → A i → B i → Set c} →
         (f : ∀ {i} (x : A i) (y : B i) → C i x y) →
         (x : (i : T) → A i) (y : (i : T) → B i) →
         ((i : T) → C i (x i) (y i))
lift↑₂ = λ f x y → (lift↑ f x) $⇈ y

Rel : ∀ {a} → Set a → (ℓ : Level) → Set (a ⊔ suc ℓ)
Rel A ℓ = A → A → Set ℓ

private
  _⇒_ : ∀ {a ℓ₁ ℓ₂} {A : Set a} →
       Rel A ℓ₁ → Rel A ℓ₂ → Set _
  P ⇒ Q = ∀ {i j} → P i j → Q i j

_Respects_ : ∀ {a ℓ₁ ℓ₂} {A : Set a} → (A → Set ℓ₁) → Rel A ℓ₂ → Set _
P Respects _∼_ = ∀ {x y} → x ∼ y → P x → P y

_Respects₂_ : ∀ {a ℓ₁ ℓ₂} {A : Set a} → Rel A ℓ₁ → Rel A ℓ₂ → Set _
P Respects₂ _∼_ =
  (∀ {x} → P x      Respects _∼_) ×
  (∀ {y} → flip P y Respects _∼_)

record IsEquivalence {a ℓ} {A : Set a}
                     (_≈_ : Rel A ℓ) : Set (a ⊔ ℓ) where
  field
    refl  : ∀ {x} → x ≈ x
    sym   : ∀ {x y} → x ≈ y → y ≈ x
    trans : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z

  -- a helper to promote propositional equality to equivalence
  .prop : ∀ {x y} → x ≣ y → x ≈ y
  prop ≣-refl = refl

record IsPreorder {a ℓ₁ ℓ₂} {A : Set a}
                  (_≈_ : Rel A ℓ₁) -- The underlying equality.
                  (_∼_ : Rel A ℓ₂) -- The relation.
                  : Set (a ⊔ ℓ₁ ⊔ ℓ₂) where
  field
    isEquivalence : IsEquivalence _≈_
    -- Reflexivity is expressed in terms of an underlying equality:
    reflexive     : _≈_ ⇒ _∼_
    trans         : ∀ {x y z} → x ∼ y → y ∼ z → x ∼ z

  module Eq = IsEquivalence isEquivalence

  refl : ∀ {x} → x ∼ x
  refl = reflexive Eq.refl

  ∼-resp-≈ : _∼_ Respects₂ _≈_
  ∼-resp-≈ = (λ x≈y z∼x → trans z∼x (reflexive x≈y))
           , (λ x≈y x∼z → trans (reflexive (Eq.sym x≈y)) x∼z)

record Preorder c ℓ₁ ℓ₂ : Set (suc (c ⊔ ℓ₁ ⊔ ℓ₂)) where
  infix 4 _≈_ _∼_
  field
    Carrier    : Set c
    _≈_        : Rel Carrier ℓ₁  -- The underlying equality.
    _∼_        : Rel Carrier ℓ₂  -- The relation.
    isPreorder : IsPreorder _≈_ _∼_

  open IsPreorder isPreorder public

record Setoid c ℓ : Set (suc (c ⊔ ℓ)) where
  infix 4 _≈_
  field
    Carrier       : Set c
    _≈_           : Rel Carrier ℓ
    .isEquivalence : IsEquivalence _≈_

record SetoidFunction {c ℓ c′ ℓ′} (X : Setoid c ℓ) (Y : Setoid c′ ℓ′) : Set (c ⊔ ℓ ⊔ c′ ⊔ ℓ′) where
  field
    F : Setoid.Carrier X → Setoid.Carrier Y
    .cong : ∀ {x y} → Setoid._≈_ X x y → Setoid._≈_ Y (F x) (F y)

module SetoidReasoning {s₁ s₂} (S : Setoid s₁ s₂) where
  open Setoid S

  infix  4 _IsRelatedTo_
  infix  2 _∎
  infixr 2 _≈⟨_⟩_
  infixr 2 _↓⟨_⟩_
  infixr 2 _↑⟨_⟩_
  infixr 2 _↓≣⟨_⟩_
  infixr 2 _↑≣⟨_⟩_
  infixr 2 _↕_
  infix  1 begin_

  -- This seemingly unnecessary type is used to make it possible to
  -- infer arguments even if the underlying equality evaluates.

  data _IsRelatedTo_ (x y : Carrier) : Set s₂ where
    relTo : (x∼y : x ≈ y) → x IsRelatedTo y

  .begin_ : ∀ {x y} → x IsRelatedTo y → x ≈ y
  begin relTo x∼y = x∼y

  ._↓⟨_⟩_ : ∀ x {y z} → x ≈ y → y IsRelatedTo z → x IsRelatedTo z
  _ ↓⟨ x∼y ⟩ relTo y∼z = relTo (trans x∼y y∼z)
    where open IsEquivalence isEquivalence

  ._↑⟨_⟩_ : ∀ x {y z} → y ≈ x → y IsRelatedTo z → x IsRelatedTo z
  _ ↑⟨ y∼x ⟩ relTo y∼z = relTo (trans (sym y∼x) y∼z)
    where open IsEquivalence isEquivalence

  -- the syntax of the ancients, for compatibility
  ._≈⟨_⟩_ : ∀ x {y z} → x ≈ y → y IsRelatedTo z → x IsRelatedTo z
  _≈⟨_⟩_ = _↓⟨_⟩_

  ._↓≣⟨_⟩_ : ∀ x {y z} → x ≣ y → y IsRelatedTo z → x IsRelatedTo z
  _ ↓≣⟨ ≣-refl ⟩ y∼z = y∼z

  ._↑≣⟨_⟩_ : ∀ x {y z} → y ≣ x → y IsRelatedTo z → x IsRelatedTo z
  _ ↑≣⟨ ≣-refl ⟩ y∼z = y∼z

  ._↕_ : ∀ x {z} → x IsRelatedTo z → x IsRelatedTo z
  _ ↕ x∼z = x∼z

  ._∎ : ∀ x → x IsRelatedTo x
  _∎ _ = relTo refl
    where open IsEquivalence isEquivalence

≣-is-equivalence : ∀ {ℓ} (A : Set ℓ) → IsEquivalence (_≣_ {A = A})
≣-is-equivalence A = record { refl = λ {x} → ≣-refl
                            ; sym = λ {x} {y} → ≣-sym
                            ; trans = λ {x} {y} {z} → flip ≣-trans }

SetAsSetoid : ∀ {ℓ} (A : Set ℓ) → Setoid ℓ ℓ
SetAsSetoid A = record { Carrier = A
                       ; _≈_ = _≣_
                       ; isEquivalence = ≣-is-equivalence A }

module ≣-reasoning {ℓ} (S : Set ℓ) where
  infix  4 _IsRelatedTo_
  infix  2 _∎
  infixr 2 _≈⟨_⟩_
  infixr 2 _↓⟨_⟩_
  infixr 2 _↑⟨_⟩_
  infixr 2 _↕_
  infix  1 begin_

  -- This seemingly unnecessary type is used to make it possible to
  -- infer arguments even if the underlying equality evaluates.

  data _IsRelatedTo_ (x y : S) : Set ℓ where
    relTo : (x∼y : x ≣ y) → x IsRelatedTo y

  begin_ : ∀ {x y} → x IsRelatedTo y → x ≣ y
  begin relTo x∼y = x∼y

  -- the syntax of the ancients, for compatibility
  _≈⟨_⟩_ : ∀ x {y z} → x ≣ y → y IsRelatedTo z → x IsRelatedTo z
  _ ≈⟨ x∼y ⟩ relTo y∼z = relTo (≣-trans y∼z x∼y)

  _↓⟨_⟩_ : ∀ x {y z} → x ≣ y → y IsRelatedTo z → x IsRelatedTo z
  _ ↓⟨ x∼y ⟩ relTo y∼z = relTo (≣-trans y∼z x∼y)

  _↑⟨_⟩_ : ∀ x {y z} → y ≣ x → y IsRelatedTo z → x IsRelatedTo z
  _ ↑⟨ y∼x ⟩ relTo y∼z = relTo (≣-trans y∼z (≣-sym y∼x))

  _↕_ : ∀ x {z} → x IsRelatedTo z → x IsRelatedTo z
  _ ↕ x∼z = x∼z

  _∎ : ∀ x → x IsRelatedTo x
  _∎ _ = relTo ≣-refl

data ℕ : Set where
  zero : ℕ
  suc  : ℕ → ℕ

{-# BUILTIN NATURAL ℕ #-}
{-# BUILTIN ZERO zero #-}
{-# BUILTIN SUC suc #-}

data Fin : ℕ → Set where
  zero : ∀ {n} → Fin (suc n)
  suc  : ∀ {n} → Fin n → Fin (suc n)

data ⊥ : Set where

¬_ : ∀ {a} (A : Set a) → Set a
¬ A = A → ⊥

record ⊤ {ℓ} : Set ℓ where
  constructor tt

type-signature : ∀ {a} (A : Set a) → A → A
type-signature A x = x

syntax type-signature A x = x ∶ A

data Either {l r : Level} (L : Set l) (R : Set r) : Set (l ⊔ r) where
  inl : L → Either L R
  inr : R → Either L R

either₀ : ∀ {l r ℓ : Level} {L : Set l} {R : Set r} {A : Set ℓ} (f : L → A) (g : R → A) (v : Either L R) → A
either₀ f _ (inl x) = f x
either₀ _ g (inr y) = g y

∙-dist-either₀ : ∀ {l r ℓ ℓ′} {L : Set l} {R : Set r} {A : Set ℓ} {A′ : Set ℓ′} (f : L → A) (g : R → A) (h : A → A′) {v : Either L R} → (h ∙ either₀ f g) v ≣ either₀ (h ∙ f) (h ∙ g) v
∙-dist-either₀ f g h {inl y} = ≣-refl
∙-dist-either₀ f g h {inr y} = ≣-refl

either₀-obsˡ : ∀ {l r ℓ : Level} {L : Set l} {R : Set r} {A : Set ℓ}
                 {f : L → A} {f′ : L → A}
               (eq : ∀ {x} → f x ≣ f′ x) (g : R → A) →
               ∀ {v} → (either₀ f g v ≣ either₀ f′ g v)
either₀-obsˡ eq g {inl y} = eq
either₀-obsˡ eq g {inr y} = ≣-refl

either₀-obsʳ : ∀ {l r ℓ : Level} {L : Set l} {R : Set r} {A : Set ℓ}
                 {g : R → A} {g′ : R → A}
               (f : L → A) (eq : ∀ {x} → g x ≣ g′ x) →
               ∀ {v} → (either₀ f g v ≣ either₀ f g′ v)
either₀-obsʳ f eq {inl y} = ≣-refl
either₀-obsʳ f eq {inr y} = eq

either₀-correlate : ∀ {l r ℓ ℓ′ : Level} {L : Set l} {R : Set r} {A : Set ℓ} {A′ : Set ℓ′ } (f : L → A′ → A) (g : R → A′ → A) (f′ : L → A′) (g′ : R → A′) (v : Either L R) → either₀ (either₀ f g v ∙ f′) (either₀ f g v ∙ g′) v ≣ either₀ (f $⇈ f′) (g $⇈ g′) v
either₀-correlate f g f′ g′ (inl v) = ≣-refl
either₀-correlate f g f′ g′ (inr v) = ≣-refl

either : ∀ {l r ℓ} {L : Set l} {R : Set r} {L′ : L → Set ℓ} {R′ : R → Set ℓ} (f : (x : L) → L′ x) (g : (y : R) → R′ y) (v : Either L R) → either₀ L′ R′ v
either f _ (inl x) = f x
either _ g (inr y) = g y

either′ : ∀ {l r ℓ} {L : Set l} {R : Set r} {A : Either L R → Set ℓ} (f : (x : L) → A (inl x)) (g : (y : R) → A (inr y)) (v : Either L R) → A v
either′ f _ (inl x) = f x
either′ _ g (inr y) = g y

left : ∀ {l l′ r} {L : Set l} {R : Set r} {L′ : Set l′} (f : L → L′) (v : Either L R) → Either L′ R
left f (inl x) = inl (f x)
left _ (inr y) = inr y

right : ∀ {l r r′} {L : Set l} {R : Set r} {R′ : Set r′} (g : R → R′) (v : Either L R) → Either L R′
right _ (inl x) = inl x
right g (inr y) = inr (g y)

_+++_ : ∀ {l l′} {L : Set l} {L′ : Set l′} (f : L → L′) {r r′} {R : Set r} {R′ : Set r′} (g : R → R′) (v : Either L R) → Either L′ R′
(f +++ _) (inl x) = inl (f x)
(_ +++ g) (inr y) = inr (g y)

uncurry₀ : ∀ {x y z} {X : Set x} {Y : Set y} {Z : Set z} (f : X → Y → Z) → X × Y → Z
uncurry₀ f (x , y) = f x y

∙-commute-uncurry₀ : ∀ {x y z z′} {X : Set x} {Y : Set y} {Z : Set z} {Z′ : Set z′} (g : Z → Z′) (f : X → Y → Z) → (g ∙ uncurry₀ f) ≣ uncurry₀ (g ∙₂ f)
∙-commute-uncurry₀ g f = ≣-refl

∙₂-commute-uncurry₀ : ∀ {x y z w w′} {X : Set x} {Y : Set y} {Z : Set z} {W : Set w} {W′ : Set w′} (g : W → W′) (f : X → Y → Z → W) → (g ∙₂ uncurry₀ f) ≣ uncurry₀ (g ∙₃ f)
∙₂-commute-uncurry₀ g f = ≣-refl

uncurry₀-obs : ∀ {x y z} {X : Set x} {Y : Set y} {Z : Set z} {f : X → Y → Z} → (f-obs : ∀ {x x′ y y′} → x ≣ x′ → y ≣ y′ → f x y ≣ f x′ y′) {x,y x,y′ : X × Y} (eq : x,y ≣ x,y′) → uncurry₀ f x,y ≣ uncurry₀ f x,y′
uncurry₀-obs f-obs eq rewrite f-obs (≣-cong fst eq) (≣-cong snd eq) = ≣-refl

uncurry₀-obs′₁ : ∀ {x y z w} {X : Set x} {Y : Set y} {Z : Set z} {W : Z → Set w} {f f′ : X → Y → (c : Z) → W c} → (∀ a b c → f a b c ≣ f′ a b c) → ∀ a,b c → uncurry₀ f a,b c ≣ uncurry₀ f′ a,b c
uncurry₀-obs′₁ f-obs a,b c rewrite f-obs (fst a,b) (snd a,b) c = ≣-refl

uncurry : ∀ {x y z} {X : Set x} {Y : Set y} {Z : X → Y → Set z} (f : (x : X) → (y : Y) → Z x y) → (x,y : X × Y) → uncurry₀ Z x,y
uncurry f (x , y) = f x y

prop→obs : ∀ {a b} {A : Set a} {B : A → Set b} {f : (x : A) → B x} {g : (x : A) → B x} (eq : f ≣ g) → ∀ {x} → f x ≣ g x
prop→obs ≣-refl {x} = ≣-refl


{-# OPTIONS --rewriting #-}
module DualContractive where

open import Data.Fin
open import Data.Maybe
open import Data.Nat hiding (_≤_ ; compare) renaming (_+_ to _+ℕ_)
open import Data.Nat.Properties
open import Data.Sum hiding (map)
open import Data.Product

open import Relation.Nullary
open import Relation.Binary.PropositionalEquality hiding (Extensionality)

open import Function

open import Direction

open import Extensionality

open import Max hiding (n)

----------------------------------------------------------------------
-- see also https://github.com/zmthy/recursive-types/tree/ftfjp16
-- for encoding of recursive types

variable
  m n : ℕ
  i i' j : Fin n

----------------------------------------------------------------------
-- lemmas for rewriting

n+1=suc-n : n +ℕ 1 ≡ suc n
n+1=suc-n {zero} = refl
n+1=suc-n {suc n} = cong suc (n+1=suc-n {n})

n+0=n : n +ℕ 0 ≡ n
n+0=n {zero} = refl
n+0=n {suc n} = cong suc (n+0=n {n})

n+sucm=sucn+m : ∀ n m → n +ℕ suc m ≡ suc (n +ℕ m)
n+sucm=sucn+m 0F m = refl
n+sucm=sucn+m (suc n) m = cong suc (n+sucm=sucn+m n m)

{-# REWRITE n+sucm=sucn+m #-}

open import Agda.Builtin.Equality.Rewrite

----------------------------------------------------------------------
-- auxiliaries for automatic rewriting

{- REWRITE n+1=suc-n #-}

{-# REWRITE n+0=n #-}

-- inject+0-x=x : {x : Fin m} → inject+ 0 x ≡ x
-- inject+0-x=x {x = zero} = refl
-- inject+0-x=x {x = suc x} = cong suc inject+0-x=x

{- REWRITE inject+0-x=x #-}

----------------------------------------------------------------------
-- types and session types

data TType (n : ℕ) : Set
data SType (n : ℕ) : Set

data TType n where
  TInt : TType n
  TChn : (S : SType n) → TType n

data SType n where
  xmt : (d : Dir) (T : TType n) (S : SType n) → SType n
  end : SType n
  rec : (S : SType (suc n)) → SType n
  var : (x : Fin n) → SType n

variable
  t T : TType n
  s s₀ S S₀ : SType n

----------------------------------------------------------------------
module Examples-Types where
  sint : SType n → SType n
  sint = xmt SND TInt

  -- μ X. !Int. X
  s1 : SType 0
  s1 = rec (sint (var 0F))

  -- μ X. μ Y. !Int. Y
  s2 : SType 0
  s2 = rec (rec (sint (var 0F)))

  -- μ X. μ Y. !Int. X
  s2a : SType 0
  s2a = rec (rec (sint (var 1F)))

  -- μ X. !Int. μ Y. X
  s3 : SType 0
  s3 = rec (sint (rec (var 1F)))

----------------------------------------------------------------------
-- weakening

increase : ∀ m → (x : Fin n) → Fin (n +ℕ m)
increase 0F x = x
increase (suc m) x = suc (increase m x)

increaseS : ∀ m → SType n → SType (n +ℕ m)
increaseT : ∀ m → TType n → TType (n +ℕ m)

increaseS m (xmt d t s) = xmt d (increaseT m t) (increaseS m s)
increaseS m (rec s) = rec (increaseS m s)
increaseS m (var x) = var (inject+ m x)
increaseS m end = end

increaseT m TInt = TInt
increaseT m (TChn s) = TChn (increaseS m s)

weakenS : ∀ m → SType n → SType (n +ℕ m)
weakenT : ∀ m → TType n → TType (n +ℕ m)

weakenS m (xmt d t s) = xmt d (weakenT m t) (weakenS m s)
weakenS m (rec s) = rec (weakenS m s)
weakenS m (var x) = var (increase m x)
weakenS m end = end

weakenT m TInt = TInt
weakenT m (TChn s) = TChn (weakenS m s)

weaken1S : SType n → SType (suc n)
weaken1S s = weakenS 1 s

----------------------------------------------------------------------
-- substitution

ssubst : SType (suc n) → Fin (suc n) → SType 0 → SType n
tsubst : TType (suc n) → Fin (suc n) → SType 0 → TType n

ssubst (xmt d t s) i s0 = xmt d (tsubst t i s0) (ssubst s i s0)
ssubst (rec s) i s0 = rec (ssubst s (suc i) s0)
ssubst {n} (var 0F) 0F s0 = increaseS n s0
ssubst {suc n} (var 0F) (suc i) s0 = var 0F
ssubst (var (suc x)) 0F s0 = var x
ssubst {suc n} (var (suc x)) (suc i) s0 = increaseS 1 (ssubst (var x) i s0)
ssubst end i s0 = end

tsubst TInt i s₀ = TInt
tsubst (TChn s) i s₀ = TChn (ssubst s i s₀)

----------------------------------------------------------------------
-- contractivity

mutual
  data ContractiveT : TType n → Set where
    con-int : ContractiveT{n} TInt
    con-chn : Contractive 0F S → ContractiveT (TChn S)

  data Contractive (i : Fin (suc n)) : SType n → Set where
    con-xmt : ContractiveT t → Contractive 0F s → Contractive i (xmt d t s)
    con-end : Contractive i end
    con-rec : Contractive (suc i) S → Contractive i (rec S)
    con-var : i ≤ inject₁ j → Contractive i (var j)

----------------------------------------------------------------------
module Examples-Contractivity where
  open Examples-Types

  cn1 : ¬ Contractive {2} 1F (var 0F)
  cn1 (con-var ())

  cp1 : Contractive {2} 0F (var 1F)
  cp1 = con-var z≤n

  cp0 : Contractive {2} 0F (var 0F)
  cp0 = con-var z≤n

  cs1 : Contractive 0F s1
  cs1 = con-rec (con-xmt con-int (con-var z≤n))

  cs2 : Contractive 0F s2
  cs2 = con-rec (con-rec (con-xmt con-int (con-var z≤n)))

  cs2a : Contractive 0F s2a
  cs2a = con-rec (con-rec (con-xmt con-int (con-var z≤n)))

  sp2 : SType 0
  sp2 = s3

  cp2 : Contractive 0F sp2
  cp2 = con-rec (con-xmt con-int (con-rec (con-var (s≤s z≤n))))

  sn2 : SType 0
  sn2 = (rec (xmt SND TInt (rec (var 0F))))

  cn2 : ¬ Contractive 0F sn2
  cn2 (con-rec (con-xmt con-int (con-rec (con-var ()))))

----------------------------------------------------------------------
-- unfolding to first non-rec constructor

unfold : (s : SType n) (c : Contractive i s) (σ : SType n → SType 0) → SType 0
unfold (xmt d t s) (con-xmt ct c) σ = σ (xmt d t s)
unfold end con-end σ = end
unfold (rec s) (con-rec c) σ = unfold s c (σ ∘ λ sn' → ssubst sn' 0F (σ (rec s)))
unfold {i = 0F} (var x) (con-var z≤n) σ = σ (var x)
unfold {i = suc i} (var 0F) (con-var ()) σ
unfold {i = suc i} (var (suc x)) (con-var (s≤s x₁)) σ = unfold (var x) (con-var x₁) (σ ∘ increaseS 1)

unfold₀ : (S : SType 0) (c : Contractive 0F S) → SType 0
unfold₀ S c = unfold S c id

----------------------------------------------------------------------
module Examples-Unfold where
  open Examples-Types

  c1 : Contractive 0F s1
  c1 = con-rec (con-xmt con-int (con-var z≤n))
  s11 : SType 0
  s11 = xmt SND TInt s1

  u-s1=s11 : unfold s1 c1 id ≡ s11
  u-s1=s11 = refl

  c2 : Contractive 0F s2
  c2 = con-rec (con-rec (con-xmt con-int (con-var z≤n)))
  u-s2=s11 : unfold s2 c2 id ≡ s11
  u-s2=s11 = cong (xmt SND TInt) (cong rec (cong (xmt SND TInt) refl))
----------------------------------------------------------------------
-- contractivity is decidable

infer-contractiveT : (t : TType n) → Dec (ContractiveT t)
infer-contractive : (s : SType n) (i : Fin (suc n)) → Dec (Contractive i s)

infer-contractiveT TInt = yes con-int
infer-contractiveT (TChn s)
  with infer-contractive s 0F
infer-contractiveT (TChn s) | yes p = yes (con-chn p)
infer-contractiveT (TChn s) | no ¬p = no (λ { (con-chn cs) → ¬p cs })

infer-contractive (xmt d t s) i
  with infer-contractiveT t | infer-contractive s 0F
infer-contractive (xmt d t s) i | yes p | yes p₁ = yes (con-xmt p p₁)
infer-contractive (xmt d t s) i | yes p | no ¬p = no (λ { (con-xmt ct cs) → ¬p cs })
infer-contractive (xmt d t s) i | no ¬p | yes p = no (λ { (con-xmt ct cs) → ¬p ct })
infer-contractive (xmt d t s) i | no ¬p | no ¬p₁ = no (λ { (con-xmt ct cs) → ¬p₁ cs})
infer-contractive end i = yes con-end
infer-contractive (rec s) i
  with infer-contractive s (suc i)
infer-contractive (rec s) i | yes p = yes (con-rec p)
infer-contractive (rec s) i | no ¬p = no (λ { (con-rec c) → ¬p c })
infer-contractive (var x) 0F = yes (con-var z≤n)
infer-contractive (var 0F) (suc i) = no (λ { (con-var ()) })
infer-contractive (var (suc x)) (suc i)
  with infer-contractive (var x) i
infer-contractive (var (suc x)) (suc i) | yes (con-var x₁) = yes (con-var (s≤s x₁))
infer-contractive (var (suc x)) (suc i) | no ¬p = no (λ { (con-var (s≤s y)) → ¬p (con-var y) })

----------------------------------------------------------------------
module Examples-Inference where
  open Examples-Contractivity

  infer-p2 : infer-contractive sp2 0F ≡ yes cp2
  infer-p2 = refl

  infer-n2 : infer-contractive sn2 0F ≡ no cn2
  infer-n2 = cong no (ext (λ { (con-rec (con-xmt con-int (con-rec (con-var ())))) }))

----------------------------------------------------------------------
-- RT: if a type is contractive at level i, then it is also contractive at any smaller level

c-weakenS : {S : SType n} (i' : Fin′ i) → Contractive i S → Contractive (inject i') S
c-weakenS i' (con-rec cis) = con-rec (c-weakenS (suc i') cis)
c-weakenS i' (con-xmt x cis) = con-xmt x cis
c-weakenS i' con-end = con-end
c-weakenS {i = suc i} i' (con-var {0F} ())
c-weakenS {i = suc i} 0F (con-var {suc n} (s≤s x)) = con-var z≤n
c-weakenS {i = suc i} (suc i') (con-var {suc n} (s≤s x)) = con-var (s≤s (trans-< x))

c-weakenS₁ : {S : SType n} → Contractive (suc i) S → Contractive (inject₁ i) S
c-weakenS₁ (con-rec cis) = con-rec (c-weakenS₁ cis)
c-weakenS₁ (con-xmt x cis) = con-xmt x cis
c-weakenS₁ con-end = con-end
c-weakenS₁ {0F} {()} (con-var x)
c-weakenS₁ {suc n} {0F} (con-var x) = con-var z≤n
c-weakenS₁ {suc n} {suc i} (con-var x) = con-var (pred-≤ x)

c-weakenS! : {S : SType n} → Contractive i S → Contractive 0F S
c-weakenS! {i = 0F} (con-rec cis) = con-rec cis
c-weakenS! {i = suc i} (con-rec cis) = con-rec (c-weakenS 1F cis)
c-weakenS! {n} {i} (con-xmt x cis) = con-xmt x cis
c-weakenS! {n} {i} con-end = con-end
c-weakenS! {n} {i} (con-var x) = con-var z≤n

----------------------------------------------------------------------
-- single substitution of j ↦ Sj

subst1T : (T : TType (suc n)) (j : Fin (suc n)) (Sj : SType n) → TType n
subst1S : (S : SType (suc n)) (j : Fin (suc n)) (Sj : SType n) → SType n

subst1T TInt j Sj = TInt
subst1T (TChn S) j Sj = TChn (subst1S S j Sj)

subst1S (xmt d T S) j Sj = xmt d (subst1T T j Sj) (subst1S S j Sj)
subst1S end j Sj = end
subst1S (rec S) j Sj = rec (subst1S S (suc j) (weaken1S Sj))
subst1S (var x) j Sj
  with compare x j
subst1S (var .(inject least)) j Sj | less .j least = var (inject! least)
subst1S (var x) .x Sj | equal .x = Sj
subst1S (var (suc x)) .(inject least) Sj | greater .(suc x) least = var x

{- the termination checker doesnt like this:
subst1S (var 0F) 0F Sj = Sj
subst1S {suc n} (var 0F) (suc j) Sj = var 0F
subst1S (var (suc x)) 0F Sj = var x
subst1S (var (suc x)) (suc j) Sj = subst1S (var (inject₁ x)) (inject₁ j) Sj
-}

unfold1S : (S : SType 0) → SType 0
unfold1S (xmt d T S) = xmt d T S
unfold1S end = end
unfold1S (rec S) = subst1S S 0F (rec S)

unfoldSS : (S : SType n) → SType n
unfoldSS (xmt d T S) = xmt d T S
unfoldSS end = end
unfoldSS (rec S)
  with unfoldSS S
... | ih = subst1S ih 0F (rec ih)
unfoldSS (var x) = var x

----------------------------------------------------------------------
-- max index substitution

subst-maxT : (Sm : SType n) (T : TType (suc n)) → TType n
subst-maxS : (Sm : SType n) (S : SType (suc n)) → SType n

subst-maxT Sm TInt = TInt
subst-maxT Sm (TChn S) = TChn (subst-maxS Sm S)

subst-maxS Sm (xmt d T S) = xmt d (subst-maxT Sm T) (subst-maxS Sm S)
subst-maxS Sm end = end
subst-maxS Sm (rec S) = rec (subst-maxS (weaken1S Sm) S)
subst-maxS Sm (var x)
  with max? x
subst-maxS Sm (var x) | yes p = Sm
subst-maxS Sm (var x) | no ¬p = var (reduce ¬p)

unfoldmS : (S : SType 0) → SType 0
unfoldmS (xmt d T S) = xmt d T S
unfoldmS end = end
unfoldmS (rec S) = subst-maxS (rec S) S

----------------------------------------------------------------------
-- max substitution preserves contractivity

contr-weakenT : ContractiveT T → ContractiveT (weakenT 1 T)
contr-weakenS : Contractive i S → Contractive (suc i) (weaken1S S)

contr-weakenT con-int = con-int
contr-weakenT (con-chn x) = con-chn (c-weakenS! (contr-weakenS x))

contr-weakenS (con-xmt x cs) = con-xmt (contr-weakenT x) (c-weakenS! (contr-weakenS cs))
contr-weakenS con-end = con-end
contr-weakenS (con-rec cs) = con-rec (contr-weakenS cs)
contr-weakenS {n}{i} {S = var j} (con-var x) = con-var (s≤s x)

subst-contr-mT :
  {T : TType (suc n)} (c : ContractiveT T)
  {S' : SType n} (c' : Contractive i S')
    → ContractiveT (subst-maxT S' T)

subst-contr-mS :
  -- {i : Fin (suc (suc n))}
  {S : SType (suc n)} (c : Contractive (inject₁ i) S)
  {S' : SType n} (c' : Contractive i S')
    → Contractive i (subst-maxS S' S)

subst-contr-mT con-int csm = con-int
subst-contr-mT (con-chn x) csm = con-chn (c-weakenS! (subst-contr-mS x (c-weakenS! csm)))

subst-contr-mS (con-xmt x cs) csm = con-xmt (subst-contr-mT x csm) (subst-contr-mS cs (c-weakenS! csm))
subst-contr-mS con-end csm = con-end
subst-contr-mS (con-rec cs) csm = con-rec (subst-contr-mS cs (contr-weakenS csm))
subst-contr-mS {S = var j} (con-var x) csm
  with max? j
subst-contr-mS {i = _} {var j} (con-var x) csm | yes p = csm
subst-contr-mS {i = _} {var j} (con-var x) csm | no ¬p = con-var (lemma-reduce x ¬p)

-- one step unfolding preserves contractivity

unfold-contr : Contractive i S → Contractive i (unfoldmS S)
unfold-contr (con-xmt x c) = con-xmt x c
unfold-contr con-end = con-end
unfold-contr (con-rec c) = subst-contr-mS (c-weakenS₁ c) (con-rec c)

-- multiple unfolding

unfold! : (S : SType n) (σ : SType n → SType 0) → SType 0
unfold! (xmt d T S) σ = σ (xmt d T S)
unfold! end σ = end
unfold! (rec S) σ = unfold! S (σ ∘ subst-maxS (rec S))
unfold! (var x) σ = σ (var x)

unfold!-contr : Contractive i S → Contractive i (unfold! S id)
unfold!-contr (con-xmt x cs) = con-xmt x cs
unfold!-contr con-end = con-end
unfold!-contr (con-rec cs) = {!!}

-- multiple unfolding preserves contractivity

SCType : (n : ℕ) → Fin (suc n) → Set
SCType n i = Σ (SType n) (Contractive i)

unfold!! : (SC : SCType n i) → (SCType n i → SCType 0 0F) → SCType 0 0F
unfold!! SC@(xmt d T S , con-xmt x cs) σ = σ SC
unfold!! (end , con-end) σ = end , con-end
unfold!! (rec S , con-rec cs) σ =
  unfold!! (S , cs) (σ ∘ λ{ (S' , c') → (subst-maxS (rec S) S') , (subst-contr-mS (c-weakenS₁ c') (con-rec cs)) })
unfold!! SC@(var x , con-var x₁) σ = σ SC

----------------------------------------------------------------------
-- equivalence requires multiple unfolding

variable
  t₁ t₂ t₁' t₂' : TType n
  s₁ s₂ : SType n

-- type equivalence
data EquivT (R : SType n → SType n → Set) : TType n → TType n → Set where
  eq-int  : EquivT R TInt TInt
  eq-chan : R s₁ s₂ → EquivT R (TChn s₁) (TChn s₂)

-- session type equivalence
data EquivS (R : SType n → SType n → Set) : SType n → SType n → Set where
  eq-xmt : (d : Dir) → EquivT R t₁ t₂ → R s₁ s₂ → EquivS R (xmt d t₁ s₁) (xmt d t₂ s₂)
  eq-end : EquivS R end end

-- record Equiv (s₁ s₂ : SType 0) : Set where
--   coinductive
--   field force : EquivS Equiv (unfold s₁) (unfold s₂)

-- open Equiv


{-# OPTIONS --prop --without-K --rewriting #-}

-- Step effect.

open import Calf.CostMonoid

module Calf.Step (costMonoid : CostMonoid) where

open CostMonoid costMonoid

open import Calf.Prelude
open import Calf.Metalanguage
open import Calf.PhaseDistinction
open import Relation.Binary.PropositionalEquality

cost : tp neg
cost = meta ℂ

postulate
  step : ∀ (B : tp neg) → cmp cost → cmp B → cmp B
  step/id : ∀ {B : tp neg} {e : cmp B} →
    step B zero e ≡ e
  {-# REWRITE step/id #-}
  step/concat : ∀ {B e p q} →
    step B p (step B q e) ≡ step B (p + q) e
  {-# REWRITE step/concat #-}

  U_step : ∀ {A} {X : val A → tp neg} {e n} → U (tbind {A} (step (F A) n e) X) ≡ U (tbind {A} e X)
  {-# REWRITE U_step #-}

  Π/step : ∀ {A} {X : val A → tp neg} {f : cmp (Π A X)} {n} → step (Π A X) n f ≡ λ x → step (X x) n (f x)
  {-# REWRITE Π/step #-}

  bind/step : ∀ {A} {X} {e f n} → bind {A} X (step (F A) n e) f ≡ step X n (bind {A} X e f)
  dbind/step : ∀ {A} {X : val A → tp neg} {e f n} → dbind {A} X (step (F A) n e) f ≡ step (tbind {A} e X) n (dbind {A} X e f)
  {-# REWRITE bind/step dbind/step #-}

  step/ext : ∀ X → (e : cmp X) → (c : ℂ) → ◯ (step X c e ≡ e)
  -- sadly the above cannot be made an Agda rewrite rule


-- Basic intuitionistic modal logic S4, without ∨, ⊥, or ◇.
-- Gentzen-style formalisation of labelled syntax, after Basin-Matthews-Viganò.

module BasicIS4.Syntax.LabelledGentzen where

open import BasicIS4.Syntax.Common public

-- Labels, as in Gabbay’s labelled deductive systems.

postulate
  Label : Set

-- Label pairs, or relational access tokens.
-- x ↝ y means the world labelled y is accessible from the world labelled x.

infix 6 _↝_
record Token : Set where
  constructor _↝_
  field
    x : Label
    y : Label

-- Labelled types.
-- A ◎ x means the type A is inhabited at the world labelled x.

infix 6 _◎_
record LabelledTy : Set where
  constructor _◎_
  field
    A : Ty
    x : Label

infix 6 _◎⋆_
_◎⋆_ : Cx Ty → Label → Cx LabelledTy
∅       ◎⋆ x = ∅
(Ξ , A) ◎⋆ x = Ξ ◎⋆ x , A ◎ x

-- Derivations.

infix 3 _⊢ᴿ_
data _⊢ᴿ_ (Λ : Cx Token) : Token → Set where
  rvar   : ∀ {x y}   → x ↝ y ∈ Λ → Λ ⊢ᴿ x ↝ y
  rrefl  : ∀ {x}     → Λ ⊢ᴿ x ↝ x
  rtrans : ∀ {x y z} → Λ ⊢ᴿ x ↝ y → Λ ⊢ᴿ y ↝ z → Λ ⊢ᴿ x ↝ z

infix 3 _⊢_
data _⊢_ : Cx² Ty Token → LabelledTy → Set where
  var  : ∀ {x A Γ Λ}   → A ∈ Γ → Γ ⁏ Λ ⊢ A ◎ x
  lam  : ∀ {x A B Γ Λ} → Γ , A ⁏ Λ ⊢ B ◎ x → Γ ⁏ Λ ⊢ A ▻ B ◎ x
  app  : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ A ▻ B ◎ x → Γ ⁏ Λ ⊢ A ◎ x → Γ ⁏ Λ ⊢ B ◎ x
  scan : ∀ {x A Γ Λ}   → (∀ {y} → Γ ⁏ Λ , x ↝ y ⊢ A ◎ y) → Γ ⁏ Λ ⊢ □ A ◎ x
  move : ∀ {x y A Γ Λ} → Γ ⁏ Λ ⊢ □ A ◎ x → Λ ⊢ᴿ x ↝ y → Γ ⁏ Λ ⊢ A ◎ y
  pair : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ A ◎ x → Γ ⁏ Λ ⊢ B ◎ x → Γ ⁏ Λ ⊢ A ∧ B ◎ x
  fst  : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ A ∧ B ◎ x → Γ ⁏ Λ ⊢ A ◎ x
  snd  : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ A ∧ B ◎ x → Γ ⁏ Λ ⊢ B ◎ x
  unit : ∀ {x Γ Λ}     → Γ ⁏ Λ ⊢ ⊤ ◎ x

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

-- Monotonicity with respect to context inclusion.

mono⊢ : ∀ {x A Γ Γ′ Λ} → Γ ⊆ Γ′ → Γ ⁏ Λ ⊢ A ◎ x → Γ′ ⁏ Λ ⊢ A ◎ x
mono⊢ η (var i)    = var (mono∈ η i)
mono⊢ η (lam t)    = lam (mono⊢ (keep η) t)
mono⊢ η (app t u)  = app (mono⊢ η t) (mono⊢ η u)
mono⊢ η (scan t)   = scan (mono⊢ η t)
mono⊢ η (move t u) = move (mono⊢ η t) 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⊢⋆ : ∀ {Ξ x Γ Γ′ Λ} → Γ ⊆ Γ′ → Γ ⁏ Λ ⊢⋆ Ξ ◎⋆ x → Γ′ ⁏ Λ ⊢⋆ Ξ ◎⋆ x
mono⊢⋆ {∅}     η ∙        = ∙
mono⊢⋆ {Ξ , A} η (ts , t) = mono⊢⋆ η ts , mono⊢ η t

-- Monotonicity with respect to relational context inclusion.

rmono⊢ᴿ : ∀ {x y Λ Λ′} → Λ ⊆ Λ′ → Λ ⊢ᴿ x ↝ y → Λ′ ⊢ᴿ x ↝ y
rmono⊢ᴿ ρ (rvar i)     = rvar (mono∈ ρ i)
rmono⊢ᴿ ρ rrefl        = rrefl
rmono⊢ᴿ ρ (rtrans t u) = rtrans (rmono⊢ᴿ ρ t) (rmono⊢ᴿ ρ u)

rmono⊢ : ∀ {x A Γ Λ Λ′} → Λ ⊆ Λ′ → Γ ⁏ Λ ⊢ A ◎ x → Γ ⁏ Λ′ ⊢ A ◎ x
rmono⊢ ρ (var i)    = var i
rmono⊢ ρ (lam t)    = lam (rmono⊢ ρ t)
rmono⊢ ρ (app t u)  = app (rmono⊢ ρ t) (rmono⊢ ρ u)
rmono⊢ ρ (scan t)   = scan (rmono⊢ (keep ρ) t)
rmono⊢ ρ (move t u) = move (rmono⊢ ρ t) (rmono⊢ᴿ ρ u)
rmono⊢ ρ (pair t u) = pair (rmono⊢ ρ t) (rmono⊢ ρ u)
rmono⊢ ρ (fst t)    = fst (rmono⊢ ρ t)
rmono⊢ ρ (snd t)    = snd (rmono⊢ ρ t)
rmono⊢ ρ unit       = unit

rmono⊢⋆ : ∀ {Ξ x Γ Λ Λ′} → Λ ⊆ Λ′ → Γ ⁏ Λ ⊢⋆ Ξ ◎⋆ x → Γ ⁏ Λ′ ⊢⋆ Ξ ◎⋆ x
rmono⊢⋆ {∅}     ρ ∙        = ∙
rmono⊢⋆ {Ξ , A} ρ (ts , t) = rmono⊢⋆ ρ ts , rmono⊢ ρ t

-- Monotonicity using context pairs.

mono²⊢ : ∀ {x A Π Π′} → Π ⊆² Π′ → Π ⊢ A ◎ x → Π′ ⊢ A ◎ x
mono²⊢ (η , ρ) = mono⊢ η ∘ rmono⊢ ρ

-- Shorthand for variables.

v₀ : ∀ {x A Γ Λ} → Γ , A ⁏ Λ ⊢ A ◎ x
v₀ = var i₀

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

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

rv₀ : ∀ {x y Λ} → Λ , x ↝ y ⊢ᴿ x ↝ y
rv₀ = rvar i₀

rv₁ : ∀ {x y x′ y′ Λ} → Λ , x ↝ y , x′ ↝ y′ ⊢ᴿ x ↝ y
rv₁ = rvar i₁

rv₂ : ∀ {x y x′ y′ x″ y″ Λ} → Λ , x ↝ y , x′ ↝ y′ , x″ ↝ y″ ⊢ᴿ x ↝ y
rv₂ = rvar i₂

-- Reflexivity.

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

-- Deduction theorem is built-in.

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

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

-- Detachment theorem.

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

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

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

-- Cut and multicut.

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

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

-- Transitivity.

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

-- Contraction.

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

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

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

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

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

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

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

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

-- Useful theorems in functional form.

dist : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ □ (A ▻ B) ◎ x → Γ ⁏ Λ ⊢ □ A ◎ x → Γ ⁏ Λ ⊢ □ B ◎ x
dist t u = scan (app (move (rmono⊢ weak⊆ t) rv₀) (move (rmono⊢ weak⊆ u) rv₀))

up : ∀ {x A Γ Λ} → Γ ⁏ Λ ⊢ □ A ◎ x → Γ ⁏ Λ ⊢ □ □ A ◎ x
up t = scan (scan (move (rmono⊢ (trans⊆ weak⊆ weak⊆) t) (rtrans rv₁ rv₀)))

down : ∀ {x A Γ Λ} → Γ ⁏ Λ ⊢ □ A ◎ x → Γ ⁏ Λ ⊢ A ◎ x
down t = move t rrefl

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

box : ∀ {x A Γ Λ} → (∀ {y} → ∅ ⁏ Λ , x ↝ y ⊢ A ◎ y) → Γ ⁏ Λ ⊢ □ A ◎ x
box t = scan (mono⊢ bot⊆ t)

unbox : ∀ {x A C Γ Λ} → Γ ⁏ Λ ⊢ □ A ◎ x → Γ , □ A ⁏ Λ ⊢ C ◎ x → Γ ⁏ Λ ⊢ C ◎ x
unbox t u = app (lam u) t

-- Useful theorems in combinatory form.

ci : ∀ {x A Γ Λ} → Γ ⁏ Λ ⊢ A ▻ A ◎ x
ci = lam v₀

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

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

cdist : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ □ (A ▻ B) ▻ □ A ▻ □ B ◎ x
cdist = lam (lam (scan (app (move v₁ rv₀) (move v₀ rv₀))))

cup : ∀ {x A Γ Λ} → Γ ⁏ Λ ⊢ □ A ▻ □ □ A ◎ x
cup = lam (scan (scan (move v₀ (rtrans rv₁ rv₀))))

cdown : ∀ {x A Γ Λ} → Γ ⁏ Λ ⊢ □ A ▻ A ◎ x
cdown = lam (move v₀ rrefl)

cdistup : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ □ (□ A ▻ B) ▻ □ A ▻ □ B ◎ x
cdistup = lam (lam (app (app cdist v₁) (app cup v₀)))

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

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

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

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

-- Internalisation, or lifting, and additional theorems.

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

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

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

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

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

box⋆ : ∀ {Ξ x Γ Λ} → (∀ {y} → ∅ ⁏ Λ , x ↝ y ⊢⋆ Ξ ◎⋆ y) → Γ ⁏ Λ ⊢⋆ □⋆ Ξ ◎⋆ x
box⋆ {∅}     f = ∙
box⋆ {Ξ , A} f = box⋆ (π₁ f) , box (π₂ f)

lift⋆ : ∀ {Ξ x Γ Λ} → (∀ {y} → Γ ⁏ Λ , x ↝ y ⊢⋆ Ξ ◎⋆ y) → □⋆ Γ ⁏ Λ ⊢⋆ □⋆ Ξ ◎⋆ x
lift⋆ {∅}     f = ∙
lift⋆ {Ξ , A} f = lift⋆ (π₁ f) , lift (π₂ f)

up⋆ : ∀ {Ξ x Γ Λ} → Γ ⁏ Λ ⊢⋆ □⋆ Ξ ◎⋆ x → Γ ⁏ Λ ⊢⋆ □⋆ □⋆ Ξ ◎⋆ x
up⋆ {∅}     f = ∙
up⋆ {Ξ , A} f = up⋆ (π₁ f) , up (π₂ f)

down⋆ : ∀ {Ξ x Γ Λ} → Γ ⁏ Λ ⊢⋆ □⋆ Ξ ◎⋆ x → Γ ⁏ Λ ⊢⋆ Ξ ◎⋆ x
down⋆ {∅}     f = ∙
down⋆ {Ξ , A} f = down⋆ (π₁ f) , down (π₂ f)

multibox : ∀ {Ξ x A Γ Λ} → Γ ⁏ Λ ⊢⋆ □⋆ Ξ ◎⋆ x → (∀ {y} → □⋆ Ξ ⁏ ∅ , x ↝ y ⊢ A ◎ y) → Γ ⁏ Λ ⊢ □ A ◎ x
multibox ts u = multicut (up⋆ ts) (rmono⊢ bot⊆ (lift u))

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

mpair : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ □ A ◎ x → Γ ⁏ Λ ⊢ □ B ◎ x → Γ ⁏ Λ ⊢ □ (A ∧ B) ◎ x
mpair t u = scan (pair (move (rmono⊢ weak⊆ t) rv₀) (move (rmono⊢ weak⊆ u) rv₀))

mfst : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ □ (A ∧ B) ◎ x → Γ ⁏ Λ ⊢ □ A ◎ x
mfst t = scan (fst (move (rmono⊢ weak⊆ t) rv₀))

msnd : ∀ {x A B Γ Λ} → Γ ⁏ Λ ⊢ □ (A ∧ B) ◎ x → Γ ⁏ Λ ⊢ □ B ◎ x
msnd t = scan (snd (move (rmono⊢ weak⊆ t) rv₀))

-- Closure under context concatenation.

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

-- TODO: Substitution.

-- [_≔_]_ : ∀ {x A B Γ Λ} → (i : A ∈ Γ) → Γ ∖ i ⁏ Λ ⊢ A ◎ x → Γ ⁏ Λ ⊢ B ◎ x → Γ ∖ i ⁏ Λ ⊢ B ◎ x
-- [ 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 ] scan t   = scan {![ i ≔ rmono⊢ weak⊆ s ] t!}
-- [ i ≔ s ] move t u = move {![ i ≔ s ] t!} 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

-- TODO: Convertibility.


{-# OPTIONS --safe --without-K #-}

open import Data.Char.Base using (Char)
open import Data.List.Base using (List; []; _∷_)

module PiCalculus.Syntax where

Name : Set
Name = List Char

module Raw where
  infixr 20 _∥_
  infixr 15 ⦅ν_⦆_
  infixr 10 _⦅_⦆_ _⟨_⟩_

  data Raw : Set where
    𝟘     : Raw
    ⦅ν_⦆_ : Name → Raw → Raw
    _∥_   : Raw → Raw → Raw
    _⦅_⦆_ : Name → Name → Raw → Raw
    _⟨_⟩_ : Name → Name → Raw → Raw

module Scoped where
  open import Data.Fin.Base
  open import Data.Nat.Base

  infixr 20 _∥_
  infixr 15 ν
  infixr 10 _⦅⦆_ _⟨_⟩_

  private
    variable
      n : ℕ

  data Scoped : ℕ → Set where
    𝟘     : Scoped n
    ν : Scoped (suc n) → ⦃ name : Name ⦄ → Scoped n
    _∥_   : Scoped n → Scoped n → Scoped n
    _⦅⦆_ : Fin n → Scoped (suc n) → ⦃ name : Name ⦄ → Scoped n
    _⟨_⟩_ : Fin n → Fin n → Scoped n → Scoped n

module Conversion where
  private
    open Raw
    open Scoped

    open import Level using (Lift; _⊔_)
    open import Function using (_∘_)
    open import Relation.Nullary using (Dec; yes; no)
    open import Relation.Nullary.Decidable using (isYes; True; toWitness)
    open import Relation.Nullary.Product using (_×-dec_)
    open import Relation.Nullary.Negation using (¬?)

    import Data.Vec.Base as Vec
    import Data.Char.Base as Char
    import Data.Char.Properties as Charₚ
    import Data.List.Base as List
    import Data.List.Properties as Listₚ
    import Data.String.Base as String
    import Data.String.Properties as Stringₚ
    import Data.Nat.Show as ℕₛ
    import Data.Vec.Membership.DecPropositional as DecPropositional

    open import Data.Empty using (⊥)
    open import Data.Bool.Base using (true; false; if_then_else_)
    open import Data.Product using (_,_; _×_)
    open import Data.Unit using (⊤; tt)
    open import Data.Nat.Base using (ℕ; zero; suc)
    open import Data.Fin.Base using (Fin; zero; suc)
    open import Data.String.Base using (_++_)
    open import Data.Vec.Membership.Propositional using (_∈_; _∉_)
    open import Data.Vec.Relation.Unary.Any using (here; there)

    open Vec using (Vec; []; _∷_)

    _∈?_ = DecPropositional._∈?_ (Listₚ.≡-dec Charₚ._≟_)

    variable
      n m : ℕ

  Ctx : ℕ → Set
  Ctx = Vec Name

  count : Name → Ctx n → ℕ
  count name = Vec.count (Listₚ.≡-dec Charₚ._≟_ name)

  toChars : Name × ℕ → List Char.Char
  toChars (x , i) = x List.++ ('^' ∷ ℕₛ.charsInBase 10 i)

  repr : ∀ x (xs : Vec Name n) → Name
  repr x xs = toChars (x , (count x xs))

  apply : Ctx n → Ctx n
  apply [] = []
  apply (x ∷ xs) = repr x xs ∷ apply xs

  WellScoped : Ctx n → Raw → Set
  WellScoped ctx 𝟘 = ⊤
  WellScoped ctx (⦅ν x ⦆ P) = WellScoped (x ∷ ctx) P
  WellScoped ctx (P ∥ Q) = WellScoped ctx P × WellScoped ctx Q
  WellScoped ctx (x ⦅ y ⦆ P) = (x ∈ ctx) × WellScoped (y ∷ ctx) P
  WellScoped ctx (x ⟨ y ⟩ P) = (x ∈ ctx) × (y ∈ ctx) × WellScoped ctx P

  WellScoped? : (ctx : Ctx n) (P : Raw) → Dec (WellScoped ctx P)
  WellScoped? ctx 𝟘 = yes tt
  WellScoped? ctx (⦅ν x ⦆ P) = WellScoped? (x ∷ ctx) P
  WellScoped? ctx (P ∥ Q) = WellScoped? ctx P ×-dec WellScoped? ctx Q
  WellScoped? ctx (x ⦅ y ⦆ P) = x ∈? ctx ×-dec WellScoped? (y ∷ ctx) P
  WellScoped? ctx (x ⟨ y ⟩ P) = x ∈? ctx ×-dec y ∈? ctx ×-dec WellScoped? ctx P

  NotShadowed : Ctx n → Raw → Set
  NotShadowed ctx 𝟘 = ⊤
  NotShadowed ctx (⦅ν name ⦆ P) = name ∉ ctx × NotShadowed (name ∷ ctx) P
  NotShadowed ctx (P ∥ Q) = NotShadowed ctx P × NotShadowed ctx Q
  NotShadowed ctx (x ⦅ y ⦆ P) = y ∉ ctx × NotShadowed (y ∷ ctx) P
  NotShadowed ctx (x ⟨ y ⟩ P) = NotShadowed ctx P

  NotShadowed? : (ctx : Ctx n) (P : Raw) → Dec (NotShadowed ctx P)
  NotShadowed? ctx 𝟘 = yes tt
  NotShadowed? ctx (⦅ν name ⦆ P) = ¬? (name ∈? ctx) ×-dec NotShadowed? (name ∷ ctx) P
  NotShadowed? ctx (P ∥ Q) = NotShadowed? ctx P ×-dec NotShadowed? ctx Q
  NotShadowed? ctx (x ⦅ y ⦆ P) = ¬? (y ∈? ctx) ×-dec NotShadowed? (y ∷ ctx) P
  NotShadowed? ctx (x ⟨ y ⟩ P) = NotShadowed? ctx P

  ∈toFin : ∀ {a} {A : Set a} {x} {xs : Vec A n} → x ∈ xs → Fin n
  ∈toFin (here px) = zero
  ∈toFin (there x∈xs) = suc (∈toFin x∈xs)

  fromRaw' : (ctx : Ctx n) (P : Raw) → WellScoped ctx P → Scoped n
  fromRaw' ctx 𝟘 tt = 𝟘
  fromRaw' ctx (⦅ν x ⦆ P) wsP =
    ν (fromRaw' (x ∷ ctx) P wsP) ⦃ x ⦄
  fromRaw' ctx (P ∥ Q) (wsP , wsQ) =
    fromRaw' ctx P wsP ∥ fromRaw' ctx Q wsQ
  fromRaw' ctx (x ⦅ y ⦆ P) (x∈ctx , wsP) =
    (∈toFin x∈ctx ⦅⦆ fromRaw' (y ∷ ctx) P wsP) ⦃ y ⦄
  fromRaw' ctx (x ⟨ y ⟩ P) (x∈ctx , y∈ctx , wsP) =
    ∈toFin x∈ctx ⟨ ∈toFin y∈ctx ⟩ fromRaw' ctx P wsP

  fromRaw : (ctx : Ctx n) (P : Raw) → ⦃ _ : True (WellScoped? ctx P) ⦄ → Scoped n
  fromRaw ctx P ⦃ p ⦄ = fromRaw' ctx P (toWitness p)

  toRaw : Ctx n → Scoped n → Raw
  toRaw ctx 𝟘 = 𝟘
  toRaw ctx (ν P ⦃ name ⦄) =
    ⦅ν repr name ctx ⦆ toRaw (name ∷ ctx) P
  toRaw ctx (P ∥ Q) =
    toRaw ctx P ∥ toRaw ctx Q
  toRaw ctx ((x ⦅⦆ P) ⦃ name ⦄) =
    let ctx' = apply ctx
    in Vec.lookup ctx' x ⦅ repr name ctx ⦆ toRaw (name ∷ ctx) P
  toRaw ctx (x ⟨ y ⟩ P) =
    let ctx' = apply ctx
    in Vec.lookup ctx' x ⟨ Vec.lookup ctx' y ⟩ toRaw ctx P

  map : ∀ {a} (B : Scoped n → Set a) (ctx : Vec Name n) (P : Raw) → Set a
  map B ctx P with WellScoped? ctx P
  map B ctx P | yes wsP = B (fromRaw' ctx P wsP)
  map B ctx P | no _ = Lift _ ⊥

  map₂ : ∀ {a} (B : Scoped n → Scoped n → Set a) (ctx : Vec Name n) (P Q : Raw) → Set a
  map₂ B ctx P Q with WellScoped? ctx P | WellScoped? ctx Q
  map₂ B ctx P Q | yes wsP | yes wsQ = B (fromRaw' ctx P wsP) (fromRaw' ctx Q wsQ)
  map₂ B ctx P Q | _       | _       = Lift _ ⊥