------------------------------------------------------------------------
-- Alternative functional semantics for an untyped λ-calculus with
-- constants
------------------------------------------------------------------------

-- I noted that the compiler correctness proof in
-- Lambda.Closure.Functional might be easier if the semantics was
-- defined using continuation-passing style, so I decided to try this
-- out. The proof at the end of this module /is/ slightly shorter, but
-- the continuation-passing definition of the semantics is quite
-- awkward (due to the limitations imposed by Agda). Furthermore it
-- seemed to me as if the soundness proofs in
-- Lambda.Closure.Equivalence would be harder to prove using this
-- formulation of the semantics.

module Lambda.Closure.Functional.Alternative where

open import Category.Monad
open import Category.Monad.Partiality as Partiality
  using (_⊥; never; Kind; OtherKind)
open import Codata.Musical.Notation
open import Data.List hiding (lookup)
open import Data.Maybe
import Data.Maybe.Categorical as Maybe
open import Data.Vec using (Vec; []; _∷_; lookup)
open import Function
open import Level
open import Relation.Binary using (module Preorder)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

open Partiality._⊥
open Partiality.Kind
private
  open module E {A : Set} = Partiality.Equality (_≡_ {A = A})
  open module R {A : Set} =
    Partiality.Reasoning (P.isEquivalence {A = A})

open import Lambda.Syntax
open Closure Tm
open import Lambda.VirtualMachine
open Functional
private
  module VM = Closure Code

------------------------------------------------------------------------
-- A monad with partiality and failure

PF : RawMonad {f = zero} (_⊥ ∘ Maybe)
PF = Maybe.monadT Partiality.monad

module PF where
  open RawMonad PF public

  fail : {A : Set} → Maybe A ⊥
  fail = now nothing

open PF

------------------------------------------------------------------------
-- Semantics

module Workaround₁ where

  -- This module provides a workaround for the limitations of
  -- guardedness.

  mutual

    data Maybe_⊥P (A : Set) : Set where
      ⌈_⌉   : (x : Maybe A ⊥) → Maybe A ⊥P
      later : (x : ∞ (Maybe A ⊥P)) → Maybe A ⊥P
      ⟦_⟧P  : ∀ {n} (t : Tm n) (ρ : Env n) (k : Value → Maybe A ⊥W) →
              Maybe A ⊥P

    data Maybe_⊥W (A : Set) : Set where
      ⌈_⌉   : (x : Maybe A ⊥) → Maybe A ⊥W
      later : (x : Maybe A ⊥P) → Maybe A ⊥W

  mutual

    -- The semantics, in slightly scrambled form.

    infix 5 _∙W_

    ⟦_⟧W : ∀ {A n} → Tm n → Env n → (Value → Maybe A ⊥W) → Maybe A ⊥W
    ⟦ con i ⟧W   ρ k = k (con i)
    ⟦ var x ⟧W   ρ k = k (lookup ρ x)
    ⟦ ƛ t ⟧W     ρ k = k (ƛ t ρ)
    ⟦ t₁ · t₂ ⟧W ρ k = ⟦ t₁ ⟧W ρ (λ v₁ →
                       ⟦ t₂ ⟧W ρ (λ v₂ →
                       (v₁ ∙W v₂) k))

    _∙W_ : ∀ {A} → Value → Value → (Value → Maybe A ⊥W) → Maybe A ⊥W
    (con i  ∙W v₂) k = ⌈ fail ⌉
    (ƛ t₁ ρ ∙W v₂) k = later (⟦ t₁ ⟧P (v₂ ∷ ρ) k)

  -- Interpretation of the definitions above.

  whnf : ∀ {A} → Maybe A ⊥P → Maybe A ⊥W
  whnf ⌈ x ⌉        = ⌈ x ⌉
  whnf (later x)    = later (♭ x)
  whnf (⟦ t ⟧P ρ k) = ⟦ t ⟧W ρ k

  mutual

    ⟪_⟫W : ∀ {A} → Maybe A ⊥W → Maybe A ⊥
    ⟪ ⌈ x ⌉    ⟫W = x
    ⟪ later  x ⟫W = later (♯ ⟪ x ⟫P)

    ⟪_⟫P : ∀ {A} → Maybe A ⊥P → Maybe A ⊥
    ⟪ x ⟫P = ⟪ whnf x ⟫W

-- The actual semantics. Note that this definition gives us
-- determinism "for free".

infix 5 _∙_

⟦_⟧ : ∀ {A n} → Tm n → Env n → (Value → Maybe A ⊥) → Maybe A ⊥
⟦ t ⟧ ρ k = ⟪ ⟦ t ⟧P ρ (λ v → ⌈ k v ⌉) ⟫P
  where open Workaround₁

_∙_ : ∀ {A} → Value → Value → (Value → Maybe A ⊥) → Maybe A ⊥
(v₁ ∙ v₂) k = ⟪ (v₁ ∙W v₂) (λ v → ⌈ k v ⌉) ⟫W
  where open Workaround₁

-- ⟦_⟧ and _∙_ preserve equality of their continuations.

module Workaround₂ {k : Kind} where

  open Workaround₁

  mutual

    infix 4 _≈P_ _≈W_

    data _≈P_ {A : Set} : Maybe A ⊥ → Maybe A ⊥ → Set where
      ⌈_⌉        : ∀ {x y} (x≈y : Rel k x y) → x ≈P y
      later      : ∀ {x y} (x≈y : ∞ (♭ x ≈P ♭ y)) → later x ≈P later y
      ⟦_⟧W-congP : ∀ {n} t (ρ : Env n) {k₁ k₂ : Value → Maybe A ⊥W}
                   (k₁≈k₂ : ∀ v → ⟪ k₁ v ⟫W ≈W ⟪ k₂ v ⟫W) →
                   ⟪ ⟦ t ⟧W ρ k₁ ⟫W ≈P ⟪ ⟦ t ⟧W ρ k₂ ⟫W

    data _≈W_ {A : Set} : Maybe A ⊥ → Maybe A ⊥ → Set where
      ⌈_⌉   : ∀ {x y} (x≈y : Rel k x y) → x ≈W y
      later : ∀ {x y} (x≈y : ♭ x ≈P ♭ y) → later x ≈W later y

  mutual

    ⟦_⟧W-congW : ∀ {A n} t (ρ : Env n) {k₁ k₂ : Value → Maybe A ⊥W} →
                 (∀ v → ⟪ k₁ v ⟫W ≈W ⟪ k₂ v ⟫W) →
                 ⟪ ⟦ t ⟧W ρ k₁ ⟫W ≈W ⟪ ⟦ t ⟧W ρ k₂ ⟫W
    ⟦ con i   ⟧W-congW ρ k₁≈k₂ = k₁≈k₂ (con i)
    ⟦ var x   ⟧W-congW ρ k₁≈k₂ = k₁≈k₂ (lookup ρ x)
    ⟦ ƛ t     ⟧W-congW ρ k₁≈k₂ = k₁≈k₂ (ƛ t ρ)
    ⟦ t₁ · t₂ ⟧W-congW ρ k₁≈k₂ = ⟦ t₁ ⟧W-congW ρ (λ v₁ →
                                 ⟦ t₂ ⟧W-congW ρ (λ v₂ →
                                 (v₁ ∙W v₂ -congW) k₁≈k₂))

    _∙W_-congW : ∀ {A} v₁ v₂ {k₁ k₂ : Value → Maybe A ⊥W} →
                 (∀ v → ⟪ k₁ v ⟫W ≈W ⟪ k₂ v ⟫W) →
                 ⟪ (v₁ ∙W v₂) k₁ ⟫W ≈W ⟪ (v₁ ∙W v₂) k₂ ⟫W
    (con i  ∙W v₂ -congW) k₁≈k₂ = ⌈ fail ∎ ⌉
    (ƛ t₁ ρ ∙W v₂ -congW) k₁≈k₂ = later (⟦ t₁ ⟧W-congP (v₂ ∷ ρ) k₁≈k₂)

  whnf≈ : ∀ {A} {x y : Maybe A ⊥} → x ≈P y → x ≈W y
  whnf≈ ⌈ x≈y ⌉                = ⌈ x≈y ⌉
  whnf≈ (later x≈y)            = later (♭ x≈y)
  whnf≈ (⟦ t ⟧W-congP ρ k₁≈k₂) = ⟦ t ⟧W-congW ρ k₁≈k₂

  mutual

    soundW : ∀ {A} {x y : Maybe A ⊥} → x ≈W y → Rel k x y
    soundW ⌈ x≈y ⌉     = x≈y
    soundW (later x≈y) = later (♯ soundP x≈y)

    soundP : ∀ {A} {x y : Maybe A ⊥} → x ≈P y → Rel k x y
    soundP x≈y = soundW (whnf≈ x≈y)

⟦_⟧-cong : ∀ {k A n} t (ρ : Env n) {k₁ k₂ : Value → Maybe A ⊥} →
           (∀ v → Rel k (k₁ v) (k₂ v)) →
           Rel k (⟦ t ⟧ ρ k₁) (⟦ t ⟧ ρ k₂)
⟦ t ⟧-cong ρ k₁≈k₂ = soundW (⟦ t ⟧W-congW ρ (λ v → ⌈ k₁≈k₂ v ⌉))
  where open Workaround₂

_∙_-cong : ∀ {k A} v₁ v₂ {k₁ k₂ : Value → Maybe A ⊥} →
           (∀ v → Rel k (k₁ v) (k₂ v)) →
           Rel k ((v₁ ∙ v₂) k₁) ((v₁ ∙ v₂) k₂)
(v₁ ∙ v₂ -cong) k₁≈k₂ = soundW ((v₁ ∙W v₂ -congW) (λ v → ⌈ k₁≈k₂ v ⌉))
  where open Workaround₂

-- ⟦_⟧ and _∙_ satisfy their intended defining equations.

sem-con : ∀ {A n} i {ρ : Env n} {k : Value → Maybe A ⊥} →
          ⟦ con i ⟧ ρ k ≅ k (con i)
sem-con i {k = k} = k (con i) ∎

sem-var : ∀ {A n} x {ρ : Env n} {k : Value → Maybe A ⊥} →
          ⟦ var x ⟧ ρ k ≅ k (lookup ρ x)
sem-var x {ρ} {k} = k (lookup ρ x) ∎

sem-ƛ : ∀ {A n} t {ρ : Env n} {k : Value → Maybe A ⊥} →
        ⟦ ƛ t ⟧ ρ k ≅ k (ƛ t ρ)
sem-ƛ t {ρ} {k} = k (ƛ t ρ) ∎

sem-· : ∀ {A n} t₁ t₂ {ρ : Env n} {k : Value → Maybe A ⊥} →
        ⟦ t₁ · t₂ ⟧ ρ k ≅ (⟦ t₁ ⟧ ρ λ v₁ → ⟦ t₂ ⟧ ρ λ v₂ → (v₁ ∙ v₂) k)
sem-· t₁ t₂ {ρ} {k} = soundW $
  ⟦ t₁ ⟧W-congW ρ λ v₁ →
  ⟦ t₂ ⟧W-congW ρ λ v₂ →
  ⌈ (v₁ ∙ v₂) k ∎ ⌉
  where open Workaround₂

app-con : ∀ {A} i v₂ {k : Value → Maybe A ⊥} →
          (con i ∙ v₂) k ≅ fail
app-con i v₂ = fail ∎

app-ƛ : ∀ {A n} t₁ (ρ : Env n) v₂ {k : Value → Maybe A ⊥} →
        (ƛ t₁ ρ ∙ v₂) k ≅ later (♯ ⟦ t₁ ⟧ (v₂ ∷ ρ) k)
app-ƛ t₁ ρ v₂ {k} = later (♯ (⟦ t₁ ⟧ (v₂ ∷ ρ) k ∎))

-- ⟦_⟧ and _∙_ are the unique solutions to the system of equations
-- which is intended to define them. I have stated this result using
-- strong equality, which is (more or less) the kind of equality you
-- get from a definition. I tried using weak equality instead, but
-- this turned out to make the proof tricky (due to the lack of
-- transitivity).

module Unique
  {A : Set}
  (⟪_⟫ : ∀ {n} t (ρ : Env n) (k : Value → Maybe A ⊥) → Maybe A ⊥)
  (_○_ : Value → Value → (Value → Maybe A ⊥) → Maybe A ⊥)

  (⟪con⟫ : ∀ {n} i {ρ : Env n} {k : Value → Maybe A ⊥} →
   ⟪ con i ⟫ ρ k ≅ k (con i))
  (⟪var⟫ : ∀ {n} x {ρ : Env n} {k : Value → Maybe A ⊥} →
   ⟪ var x ⟫ ρ k ≅ k (lookup ρ x))
  (⟪ƛ⟫ : ∀ {n} t {ρ : Env n} {k : Value → Maybe A ⊥} →
   ⟪ ƛ t ⟫ ρ k ≅ k (ƛ t ρ))
  (⟪·⟫ : ∀ {n} t₁ t₂ {ρ : Env n} {k : Value → Maybe A ⊥} →
   ⟪ t₁ · t₂ ⟫ ρ k ≅ (⟪ t₁ ⟫ ρ λ v₁ → ⟪ t₂ ⟫ ρ λ v₂ → (v₁ ○ v₂) k))

  (con○ : ∀ i v₂ {k : Value → Maybe A ⊥} →
   (con i ○ v₂) k ≅ fail)
  (ƛ○ : ∀ {n} t₁ (ρ : Env n) v₂ {k : Value → Maybe A ⊥} →
   (ƛ t₁ ρ ○ v₂) k ≅ later (♯ ⟪ t₁ ⟫ (v₂ ∷ ρ) k))
  where

  infix  4 _≅P_ _≅W_
  infixr 2 _≅⟨_⟩P_ _≅⟨_⟩W_ _≅⟪_⟫P_ _≅⟪_⟫W_

  mutual

    data _≅P_ : Maybe A ⊥ → Maybe A ⊥ → Set where
      _≅⟨_⟩P_ : ∀ x {y z} (x≅y : x ≅  y) (y≅z : y ≅P z) → x ≅P z
      _≅⟪_⟫P_ : ∀ x {y z} (x≅y : x ≅P y) (y≅z : y ≅  z) → x ≅P z
      semP    : ∀ {n} t {ρ : Env n} {k₁ k₂ : Value → Maybe A ⊥} →
                (k₁≅k₂ : ∀ v → k₁ v ≅W k₂ v) →
                ⟪ t ⟫ ρ k₁ ≅P ⟦ t ⟧ ρ k₂

    data _≅W_ : Maybe A ⊥ → Maybe A ⊥ → Set where
      ⌈_⌉    : ∀ {x y} (x≅y :   x ≅    y) →       x ≅W       y
      later  : ∀ {x y} (x≅y : ♭ x ≅P ♭ y) → later x ≅W later y

  _≅⟨_⟩W_ : ∀ x {y z} → x ≅ y → y ≅W z → x ≅W z
  _  ≅⟨ x≅y       ⟩W ⌈ y≅z ⌉    = ⌈ _ ≅⟨ x≅y ⟩ y≅z ⌉
  ._ ≅⟨ later x≅y ⟩W later  y≅z = later (_ ≅⟨ ♭ x≅y ⟩P y≅z)

  _≅⟪_⟫W_ : ∀ x {y z} → x ≅W y → y ≅ z → x ≅W z
  _  ≅⟪ ⌈ x≅y ⌉   ⟫W       y≅z = ⌈ _ ≅⟨ x≅y ⟩ y≅z ⌉
  ._ ≅⟪ later x≅y ⟫W later y≅z = later (_ ≅⟪ x≅y ⟫P ♭ y≅z)

  mutual

    semW : ∀ {n} t {ρ : Env n} {k₁ k₂ : Value → Maybe A ⊥} →
           (∀ v → k₁ v ≅W k₂ v) → ⟪ t ⟫ ρ k₁ ≅W ⟦ t ⟧ ρ k₂
    semW (con i) {ρ} {k₁} {k₂} k₁≅k₂ =
      ⟪ con i ⟫ ρ k₁  ≅⟨ ⟪con⟫ i ⟩W
      k₁ (con i)      ≅⟪ k₁≅k₂ _ ⟫W
      k₂ (con i)      ∎
    semW (var x) {ρ} {k₁} {k₂} k₁≅k₂ =
      ⟪ var x ⟫ ρ k₁   ≅⟨ ⟪var⟫ x ⟩W
      k₁ (lookup ρ x)  ≅⟪ k₁≅k₂ _ ⟫W
      k₂ (lookup ρ x)  ∎
    semW (ƛ t) {ρ} {k₁} {k₂} k₁≅k₂ =
      ⟪ ƛ t ⟫ ρ k₁  ≅⟨ ⟪ƛ⟫ t ⟩W
      k₁ (ƛ t ρ)    ≅⟪ k₁≅k₂ _ ⟫W
      k₂ (ƛ t ρ)    ∎
    semW (t₁ · t₂) {ρ} {k₁} {k₂} k₁≅k₂ =
      ⟪ t₁ · t₂ ⟫ ρ k₁                                ≅⟨ ⟪·⟫ t₁ t₂ ⟩W
      (⟪ t₁ ⟫ ρ λ v₁ → ⟪ t₂ ⟫ ρ λ v₂ → (v₁ ○ v₂) k₁)  ≅⟪ semW t₁ (λ v₁ → semW t₂ (λ v₂ → appW v₁ v₂ k₁≅k₂)) ⟫W
      (⟦ t₁ ⟧ ρ λ v₁ → ⟦ t₂ ⟧ ρ λ v₂ → (v₁ ∙ v₂) k₂)  ≅⟨ sym $ sem-· t₁ t₂ ⟩
      ⟦ t₁ · t₂ ⟧ ρ k₂                                ∎

    appW : ∀ v₁ v₂ {k₁ k₂ : Value → Maybe A ⊥} →
           (∀ v → k₁ v ≅W k₂ v) → (v₁ ○ v₂) k₁ ≅W (v₁ ∙ v₂) k₂
    appW (con i) v₂ {k₁} {k₂} k₁≅k₂ =
      ⌈ (con i ○ v₂) k₁  ≅⟨ con○ i v₂ ⟩
        fail             ∎ ⌉
    appW (ƛ t₁ ρ) v₂ {k₁} {k₂} k₁≅k₂ =
      (ƛ t₁ ρ ○ v₂) k₁  ≅⟨ ƛ○ t₁ ρ v₂ ⟩W
      later _           ≅⟪ later (semP t₁ k₁≅k₂) ⟫W
      (ƛ t₁ ρ ∙ v₂) k₂  ∎

  whnf : ∀ {x y} → x ≅P y → x ≅W y
  whnf (x ≅⟨ x≅y ⟩P y≅z) = x ≅⟨      x≅y ⟩W whnf y≅z
  whnf (x ≅⟪ x≅y ⟫P y≅z) = x ≅⟪ whnf x≅y ⟫W      y≅z
  whnf (semP t k₁≅k₂)    = semW t k₁≅k₂

  mutual

    soundW : ∀ {x y} → x ≅W y → x ≅ y
    soundW ⌈ x≅y ⌉     = x≅y
    soundW (later x≅y) = later (♯ soundP x≅y)

    soundP : ∀ {x y} → x ≅P y → x ≅ y
    soundP x≅y = soundW (whnf x≅y)

  sem : ∀ {n} t {ρ : Env n} {k : Value → Maybe A ⊥} →
        ⟪ t ⟫ ρ k ≅ ⟦ t ⟧ ρ k
  sem t {k = k} = soundW (semW t (λ v → ⌈ k v ∎ ⌉))

  dot : ∀ v₁ v₂ {k : Value → Maybe A ⊥} →
        (v₁ ○ v₂) k ≅ (v₁ ∙ v₂) k
  dot v₁ v₂ {k} = soundW (appW v₁ v₂ (λ v → ⌈ k v ∎ ⌉))

------------------------------------------------------------------------
-- Example

Ω-loops : ⟦ Ω ⟧ [] return ≈ never
Ω-loops = later (♯ Ω-loops)

------------------------------------------------------------------------
-- Compiler correctness

module Correctness {k : OtherKind} where

  infix  4 _≈P_ _≈W_
  infixr 2 _≡⟨_⟩W_ _≈⟨_⟩P_ _≈⟨_⟩W_

  mutual

    data _≈P_ : Maybe VM.Value ⊥ → Maybe VM.Value ⊥ → Set where
      _≈⟨_⟩P_ : ∀ x {y z} (x≈y : x ≈P y) (y≅z : y ≅ z) → x ≈P z
      correct :
        ∀ {n} t {ρ : Env n} {c s} {k : Value → Maybe VM.Value ⊥} →
        (hyp : ∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩
                       ≈W k v) →
        exec ⟨ comp t c , s , comp-env ρ ⟩ ≈P ⟦ t ⟧ ρ k

    data _≈W_ : Maybe VM.Value ⊥ → Maybe VM.Value ⊥ → Set where
      ⌈_⌉    : ∀ {x y} (x≈y : Rel (other k) x y) → x ≈W y
      later  : ∀ {x y} (x≈y : ♭ x ≈P ♭ y) → later x ≈W later y
      laterˡ : ∀ {x y} (x≈y : ♭ x ≈W   y) → later x ≈W       y

  _≡⟨_⟩W_ : ∀ x {y z} → x ≡ y → y ≈W z → x ≈W z
  _ ≡⟨ P.refl ⟩W y≈z = y≈z

  _≈⟨_⟩W_ : ∀ x {y z} → x ≈W y → y ≅ z → x ≈W z
  ._ ≈⟨ later  x≈y ⟩W later y≅z = later  (_ ≈⟨ x≈y ⟩P ♭ y≅z)
  ._ ≈⟨ laterˡ x≈y ⟩W       y≅z = laterˡ (_ ≈⟨ x≈y ⟩W   y≅z)
  _  ≈⟨ ⌈ x≈y ⌉    ⟩W       y≅z = ⌈ trans x≈y (Partiality.≅⇒ y≅z) ⌉
    where trans = Preorder.trans (Partiality.preorder P.isPreorder _)

  mutual

    correctW :
      ∀ {n} t {ρ : Env n} {c s} {k : Value → Maybe VM.Value ⊥} →
      (∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩ ≈W k v) →
      exec ⟨ comp t c , s , comp-env ρ ⟩ ≈W ⟦ t ⟧ ρ k
    correctW (con i) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (Lambda.Syntax.Closure.con i) ∷ s , comp-env ρ ⟩  ≈⟨ hyp (con i) ⟩W
      k (con i)                                                        ∎)
    correctW (var x) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (lookup (comp-env ρ) x) ∷ s , comp-env ρ ⟩  ≡⟨ P.cong (λ v → exec ⟨ c , val v ∷ s , comp-env ρ ⟩) $ lookup-hom x ρ ⟩W
      exec ⟨ c , val (comp-val (lookup ρ x)) ∷ s , comp-env ρ ⟩  ≈⟨ hyp (lookup ρ x) ⟩W
      k (lookup ρ x)                                             ∎)
    correctW (ƛ t) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (comp-val (ƛ t ρ)) ∷ s , comp-env ρ ⟩  ≈⟨ hyp (ƛ t ρ) ⟩W
      k (ƛ t ρ)                                             ∎)
    correctW (t₁ · t₂) {ρ} {c} {s} {k} hyp =
      exec ⟨ comp t₁ (comp t₂ (app ∷ c)) , s , comp-env ρ ⟩  ≈⟨ correctW t₁ (λ v₁ → correctW t₂ (λ v₂ → ∙-correctW v₁ v₂ hyp)) ⟩W
      (⟦ t₁ ⟧ ρ λ v₁ → ⟦ t₂ ⟧ ρ λ v₂ → (v₁ ∙ v₂) k)          ≅⟨ sym $ sem-· t₁ t₂ ⟩
      ⟦ t₁ · t₂ ⟧ ρ k                                        ∎

    ∙-correctW :
      ∀ {n} v₁ v₂ {ρ : Env n} {c s} {k : Value → Maybe VM.Value ⊥} →
      (∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩ ≈W k v) →
      exec ⟨ app ∷ c , val (comp-val v₂) ∷ val (comp-val v₁) ∷ s , comp-env ρ ⟩ ≈W
      (v₁ ∙ v₂) k
    ∙-correctW (con i)   v₂                 _   = ⌈ fail ∎ ⌉
    ∙-correctW (ƛ t₁ ρ₁) v₂ {ρ} {c} {s} {k} hyp =
      exec ⟨ app ∷ c , val (comp-val v₂) ∷ val (comp-val (ƛ t₁ ρ₁)) ∷ s , comp-env ρ ⟩  ≈⟨ later (

        exec ⟨ comp t₁ [ ret ] , ret c (comp-env ρ) ∷ s , comp-env (v₂ ∷ ρ₁) ⟩               ≈⟨ correct t₁ (λ v → laterˡ (hyp v)) ⟩P
        (⟦ t₁ ⟧ (v₂ ∷ ρ₁) k)                                                                 ∎) ⟩W

      (ƛ t₁ ρ₁ ∙ v₂) k                                                                  ∎

  whnf : ∀ {x y} → x ≈P y → x ≈W y
  whnf (x ≈⟨ x≈y ⟩P y≅z) = x ≈⟨ whnf x≈y ⟩W y≅z
  whnf (correct t hyp)   = correctW t hyp

  mutual

    soundW : ∀ {x y} → x ≈W y → Rel (other k) x y
    soundW ⌈ x≈y ⌉      = x≈y
    soundW (later  x≈y) = later (♯ soundP x≈y)
    soundW (laterˡ x≈y) = laterˡ (soundW x≈y)

    soundP : ∀ {x y} → x ≈P y → Rel (other k) x y
    soundP x≈y = soundW (whnf x≈y)

correct : ∀ t →
          exec ⟨ comp t [] , [] , [] ⟩ ≈
          ⟦ t ⟧ [] (λ v → return (comp-val v))
correct t =
  soundP $ Correctness.correct t (λ v → ⌈ return (comp-val v) ∎ ⌉)
  where open Correctness


module _ where

postulate
  A B C D E F : Set
  a : A
  b : B
  c : C
  d : D
  e : E
  f : F
  T : {A : Set} → A → Set

module M1 (a : A) where
  module M2 (b : B) where
    postulate g : T a → T b
    module M3 (c : C) where
      postulate h : T a → T b → T c
  module M4 (d : D) where
    module M5 (e : E) (f : F) where
      open M2 public
      open M2.M3 public
      open M3 public renaming (h to h')

module M  = M1.M4 a d
module M' = M.M5 e f

M'g : (x : B) → T a → T x
M'g = M'.g

M'M3h M'h M'h' : (x : B) (y : C) → T a → T x → T y
M'M3h = M'.M3.h
M'h = M'.h
M'h' = M'.h'


------------------------------------------------------------------------
-- Examples/exercises related to CCS from "Enhancements of the
-- bisimulation proof method" by Pous and Sangiorgi
--
-- Implemented using coinductive definitions of strong and weak
-- bisimilarity and expansion.
------------------------------------------------------------------------

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

open import Prelude hiding (module W; step-→)

module Bisimilarity.Weak.CCS.Examples {ℓ} {Name : Type ℓ} where

open import Equality.Propositional

open import Function-universe equality-with-J hiding (id; _∘_)

open import Bisimilarity.CCS
import Bisimilarity.Equational-reasoning-instances
import Bisimilarity.Weak.CCS as WL
import Bisimilarity.Weak.Equational-reasoning-instances
open import Equational-reasoning
import Expansion.CCS as EL
import Expansion.Equational-reasoning-instances
open import Labelled-transition-system.CCS Name

open import Bisimilarity CCS using (_∼_; ∼:_)
open import Bisimilarity.Weak CCS as W
open import Expansion CCS as E
import Labelled-transition-system.Equational-reasoning-instances CCS
  as Dummy

mutual

  -- Example 6.5.4.

  6-5-4 :
    ∀ {i a b} →
    [ i ] ! name a ∙ (b ∙) ∣ ! co a ∙ ≈ (! a ∙ ∣ ! b ∙) ∣ ! co a ∙
  6-5-4 {i} {a} {b} = W.⟨ lr , rl ⟩
    where
    lemma =
      (! a ∙ ∣ ! b ∙) ∣ b ∙  ∼⟨ symmetric ∣-assoc ⟩
      ! a ∙ ∣ (! b ∙ ∣ b ∙)  ∼⟨ symmetric ∣-right-identity ∣-cong 6-1-2 ⟩■
      (! a ∙ ∣ ∅) ∣ ! b ∙

    lr :
      ∀ {P μ} →
      ! name a ∙ (b ∙) ∣ ! co a ∙ [ μ ]⟶ P →
      ∃ λ Q → (! a ∙ ∣ ! b ∙) ∣ ! co a ∙ [ μ ]⇒̂ Q × [ i ] P ≈′ Q
    lr (par-left {P′ = P} tr) = case 6-1-3-2 tr of λ where
      (inj₁ (.(b ∙) , action , P∼!ab∣b)) →
        P ∣ ! co a ∙                         ∼⟨ P∼!ab∣b ∣-cong reflexive ⟩
        (! name a ∙ (b ∙) ∣ b ∙) ∣ ! co a ∙  ∼⟨ swap-rightmost ⟩
        (! name a ∙ (b ∙) ∣ ! co a ∙) ∣ b ∙  ∼′⟨ 6-5-4′ WL.∣-cong′ reflexive ⟩
        ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙) ∣ b ∙   ∼⟨ swap-rightmost ⟩ ∼:
        ((! a ∙ ∣ ! b ∙) ∣ b ∙) ∣ ! co a ∙   ∼⟨ lemma ∣-cong reflexive ⟩■
        ((! a ∙ ∣ ∅) ∣ ! b ∙) ∣ ! co a ∙
          W.⇐̂[ name a ]                      ←⟨ ⟶: par-left (par-left (replication (par-right action))) ⟩■
        (! a ∙ ∣ ! b ∙) ∣ ! co a ∙

      (inj₂ (_ , .(b ∙) , _ , .a , action , ab[a̅]⟶ , _)) →
        ⊥-elim (names-are-not-inverted ab[a̅]⟶)

    lr (par-right {Q′ = Q} tr) = case 6-1-3-2 tr of λ where
      (inj₁ (.∅ , action , Q∼!a̅∣∅)) →
        ! name a ∙ (b ∙) ∣ Q               ∼⟨ reflexive ∣-cong Q∼!a̅∣∅ ⟩
        ! name a ∙ (b ∙) ∣ (! co a ∙ ∣ ∅)  ∼⟨ ∣-assoc ⟩
        (! name a ∙ (b ∙) ∣ ! co a ∙) ∣ ∅  ∼′⟨ 6-5-4′ WL.∣-cong′ reflexive ⟩ ∼:
        ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙) ∣ ∅   ∼⟨ symmetric ∣-assoc ⟩■
        (! a ∙ ∣ ! b ∙) ∣ (! co a ∙ ∣ ∅)
          W.⇐̂[ name (co a) ]               ←⟨ ⟶: par-right (replication (par-right action)) ⟩■
        (! a ∙ ∣ ! b ∙) ∣ ! co a ∙

      (inj₂ (_ , .∅ , _ , .(co a) , action , a̅[a̅̅]⟶ , _)) →
        ⊥-elim (names-are-not-inverted a̅[a̅̅]⟶)

    lr (par-τ {P′ = P} {Q′ = Q} tr₁ tr₂) =
      case 6-1-3-2 tr₁ ,′ 6-1-3-2 tr₂ of λ where
        (inj₁ (.(b ∙) , action , P∼!ab∣b) ,
         inj₁ (R      , tr     , Q∼!a̅∣R)) →
          P ∣ Q                                      ∼⟨ P∼!ab∣b ∣-cong Q∼!a̅∣R ⟩
          (! name a ∙ (b ∙) ∣ b ∙) ∣ (! co a ∙ ∣ R)  ∼≡⟨ cong (λ R → _ ∣ (_ ∣ R)) (·-only⟶ tr) ⟩
          (! name a ∙ (b ∙) ∣ b ∙) ∣ (! co a ∙ ∣ ∅)  ∼⟨ swap-in-the-middle ⟩
          (! name a ∙ (b ∙) ∣ ! co a ∙) ∣ (b ∙ ∣ ∅)  ∼′⟨ 6-5-4′ WL.∣-cong′ reflexive ⟩
          ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙) ∣ (b ∙ ∣ ∅)   ∼⟨ swap-in-the-middle ⟩ ∼:
          ((! a ∙ ∣ ! b ∙) ∣ b ∙) ∣ (! co a ∙ ∣ ∅)   ∼⟨ lemma ∣-cong reflexive ⟩■
          ((! a ∙ ∣ ∅) ∣ ! b ∙) ∣ (! co a ∙ ∣ ∅)
            W.⇐̂[ τ ]                                 ←⟨ par-τ (par-left (replication (par-right action)))
                                                              (replication (par-right action)) ⟩■
          (! a ∙ ∣ ! b ∙) ∣ ! co a ∙

        (_ , inj₂ (() , _))
        (inj₂ (() , _) , _)

    rl-lemma :
      ∀ {Q μ} →
      (! a ∙ ∣ ! b ∙) ∣ ! co a ∙ [ μ ]⟶ Q →
      (! a ∙ ∣ ! b ∙) ∣ ! co a ∙ ∼ Q
        ×
      (μ ≡ name a ⊎ μ ≡ name b ⊎ μ ≡ name (co a) ⊎ μ ≡ τ)
    rl-lemma (par-left (par-left {P′ = P} tr)) =
      case 6-1-3-2 tr of λ where
        (inj₁ (.∅ , action , P∼!a∣∅)) →
            ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙        ∼⟨ (symmetric ∣-right-identity ∣-cong reflexive) ∣-cong reflexive ⟩
             ((! a ∙ ∣ ∅) ∣ ! b ∙) ∣ ! co a ∙  ∼⟨ (symmetric P∼!a∣∅ ∣-cong reflexive) ∣-cong reflexive ⟩■
             (P ∣ ! b ∙) ∣ ! co a ∙)
          , inj₁ refl

        (inj₂ (refl , .∅ , _ , .a , action , a[a̅]⟶ , _)) →
          ⊥-elim (names-are-not-inverted a[a̅]⟶)

    rl-lemma (par-left (par-right {Q′ = P} tr)) =
      case 6-1-3-2 tr of λ where
        (inj₁ (.∅ , action , P∼!b∣∅)) →
            ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙        ∼⟨ (reflexive ∣-cong symmetric ∣-right-identity) ∣-cong reflexive ⟩
             (! a ∙ ∣ (! b ∙ ∣ ∅)) ∣ ! co a ∙  ∼⟨ (reflexive ∣-cong symmetric P∼!b∣∅) ∣-cong reflexive ⟩■
             (! a ∙ ∣ P) ∣ ! co a ∙)
          , inj₂ (inj₁ refl)

        (inj₂ (_ , .∅ , _ , .b , action , b[b̅]⟶ , _)) →
          ⊥-elim (names-are-not-inverted b[b̅]⟶)

    rl-lemma (par-left (par-τ {P′ = P} {Q′ = Q} tr₁ tr₂)) =
      case 6-1-3-2 tr₁ ,′ 6-1-3-2 tr₂ of λ where
        (inj₁ (.∅ , action , P∼!a∣∅) ,
         inj₁ (.∅ , action , Q∼!b∣∅)) →
            ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙              ∼⟨ symmetric (∣-right-identity ∣-cong ∣-right-identity) ∣-cong reflexive ⟩
             ((! a ∙ ∣ ∅) ∣ (! b ∙ ∣ ∅)) ∣ ! co a ∙  ∼⟨ symmetric (P∼!a∣∅ ∣-cong Q∼!b∣∅) ∣-cong reflexive ⟩■
             (P ∣ Q) ∣ ! co a ∙)
          , inj₂ (inj₂ (inj₂ refl))

        (inj₂ (() , _) , _)
        (_ , inj₂ (() , _))

    rl-lemma (par-right {Q′ = Q} tr) =
      case 6-1-3-2 tr of λ where
        (inj₁ (.∅ , action , Q∼!a̅∣∅)) →
            ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙        ∼⟨ reflexive ∣-cong symmetric ∣-right-identity ⟩
             (! a ∙ ∣ ! b ∙) ∣ (! co a ∙ ∣ ∅)  ∼⟨ reflexive ∣-cong symmetric Q∼!a̅∣∅ ⟩■
             (! a ∙ ∣ ! b ∙) ∣ Q)
          , inj₂ (inj₂ (inj₁ refl))

        (inj₂ (_ , .∅ , _ , .(co a) , action , a̅[a̅̅]⟶ , _)) →
          ⊥-elim (names-are-not-inverted a̅[a̅̅]⟶)

    rl-lemma (par-τ {Q′ = Q} (par-left {P′ = P} tr₁) tr₂) =
      case 6-1-3-2 tr₁ ,′ 6-1-3-2 tr₂ of λ where
        (inj₁ (.∅ , action , P∼!a∣∅) ,
         inj₁ (R  , tr     , Q∼!a̅∣R)) →
            ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙              ∼⟨ symmetric ((∣-right-identity ∣-cong reflexive) ∣-cong ∣-right-identity) ⟩
             ((! a ∙ ∣ ∅) ∣ ! b ∙) ∣ (! co a ∙ ∣ ∅)  ∼≡⟨ cong (λ R → _ ∣ (_ ∣ R)) (sym $ ·-only⟶ tr) ⟩
             ((! a ∙ ∣ ∅) ∣ ! b ∙) ∣ (! co a ∙ ∣ R)  ∼⟨ symmetric ((P∼!a∣∅ ∣-cong reflexive) ∣-cong Q∼!a̅∣R) ⟩■
             (P ∣ ! b ∙) ∣ Q)
          , inj₂ (inj₂ (inj₂ refl))

        (inj₂ (() , _) , _)
        (_ , inj₂ (() , _))

    rl-lemma (par-τ {Q′ = Q} (par-right {Q′ = P} tr₁) tr₂) =
      case 6-1-3-2 tr₁ ,′ 6-1-3-2 tr₂ of λ where
        (inj₁ (.∅ , action , P∼!b∣∅) ,
         inj₁ (R  , tr     , Q∼!a̅∣R)) →
            ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙              ∼⟨ symmetric ((reflexive ∣-cong ∣-right-identity) ∣-cong ∣-right-identity) ⟩
             (! a ∙ ∣ (! b ∙ ∣ ∅)) ∣ (! co a ∙ ∣ ∅)  ∼≡⟨ cong (λ R → _ ∣ (_ ∣ R)) (sym $ ·-only⟶ tr) ⟩
             (! a ∙ ∣ (! b ∙ ∣ ∅)) ∣ (! co a ∙ ∣ R)  ∼⟨ symmetric ((reflexive ∣-cong P∼!b∣∅) ∣-cong Q∼!a̅∣R) ⟩■
             (! a ∙ ∣ P) ∣ Q)
          , inj₂ (inj₂ (inj₂ refl))

        (inj₂ (() , _) , _)
        (_ , inj₂ (() , _))

    rl :
      ∀ {Q μ} →
      (! a ∙ ∣ ! b ∙) ∣ ! co a ∙ [ μ ]⟶ Q →
      ∃ λ P → ! name a ∙ (b ∙) ∣ ! co a ∙ [ μ ]⇒̂ P × [ i ] P ≈′ Q
    rl {Q} tr = case rl-lemma tr of λ where
      (!a∣!b∣!a̅∼Q , inj₁ refl) →
        ! name a ∙ (b ∙) ∣ ! co a ∙          →⟨ ⟶: par-left (replication (par-right action)) ⟩■
          W.⇒̂[ name a ]
        (! name a ∙ (b ∙) ∣ b ∙) ∣ ! co a ∙  ∼⟨ swap-rightmost ⟩
        (! name a ∙ (b ∙) ∣ ! co a ∙) ∣ b ∙  ∼′⟨ 6-5-4′ WL.∣-cong′ reflexive ⟩
        ((! a ∙ ∣ ! b ∙) ∣ ! co a ∙) ∣ b ∙   ∼⟨ swap-rightmost ⟩
        ((! a ∙ ∣ ! b ∙) ∣ b ∙) ∣ ! co a ∙   ∼⟨ symmetric ∣-assoc ∣-cong reflexive ⟩
        (! a ∙ ∣ (! b ∙ ∣ b ∙)) ∣ ! co a ∙   ∼⟨ (reflexive ∣-cong 6-1-2) ∣-cong reflexive ⟩ ∼:
        (! a ∙ ∣ ! b ∙) ∣ ! co a ∙           ∼⟨ !a∣!b∣!a̅∼Q ⟩■
        Q

      (!a∣!b∣!a̅∼Q , inj₂ (inj₁ refl)) →
        ! name a ∙ (b ∙) ∣ ! co a ∙                  →⟨ par-τ (replication (par-right action))
                                                              (replication (par-right action)) ⟩
        (! name a ∙ (b ∙) ∣ (b ∙)) ∣ (! co a ∙ ∣ ∅)  →⟨ ⟶: par-left (par-right action) ⟩■
          W.⇒̂[ name b ]
        (! name a ∙ (b ∙) ∣ ∅) ∣ (! co a ∙ ∣ ∅)      ∼⟨ ∣-right-identity ∣-cong ∣-right-identity ⟩
        (! name a ∙ (b ∙)) ∣ ! co a ∙                ∼′⟨ 6-5-4′ ⟩ ∼:
        (! a ∙ ∣ ! b ∙) ∣ ! co a ∙                   ∼⟨ !a∣!b∣!a̅∼Q ⟩■
        Q

      (!a∣!b∣!a̅∼Q , inj₂ (inj₂ (inj₁ refl))) →
        ! name a ∙ (b ∙) ∣ ! co a ∙        →⟨ ⟶: par-right (replication (par-right action)) ⟩■
          W.⇒̂[ name (co a) ]
        ! name a ∙ (b ∙) ∣ (! co a ∙ ∣ ∅)  ∼⟨ reflexive ∣-cong ∣-right-identity ⟩
        ! name a ∙ (b ∙) ∣ ! co a ∙        ∼′⟨ 6-5-4′ ⟩ ∼:
        (! a ∙ ∣ ! b ∙) ∣ ! co a ∙         ∼⟨ !a∣!b∣!a̅∼Q ⟩■
        Q

      (!a∣!b∣!a̅∼Q , inj₂ (inj₂ (inj₂ refl))) →
        ! name a ∙ (b ∙) ∣ ! co a ∙  ■
          W.⇒̂[ τ ]
        ! name a ∙ (b ∙) ∣ ! co a ∙  ∼′⟨ 6-5-4′ ⟩ ∼:
        (! a ∙ ∣ ! b ∙) ∣ ! co a ∙   ∼⟨ !a∣!b∣!a̅∼Q ⟩■
        Q

  6-5-4′ :
    ∀ {i a b} →
    [ i ] ! name a ∙ (b ∙) ∣ ! co a ∙ ≈′ (! a ∙ ∣ ! b ∙) ∣ ! co a ∙
  force 6-5-4′ = 6-5-4

-- The first part of Exercise 6.5.8.

6-5-8-1 :
  ∀ a {P Q} →
  ⟨ν proj₁ a ⟩ (name a ∙ P ∣ name (co a) ∙ Q) ≳ ⟨ν proj₁ a ⟩ (P ∣ Q)
6-5-8-1 a {P} {Q} = E.⟨ lr , rl ⟩
  where
  lr :
    ∀ {μ R} →
    ⟨ν proj₁ a ⟩ (name a ∙ P ∣ name (co a) ∙ Q) [ μ ]⟶ R →
    ∃ λ R′ → ⟨ν proj₁ a ⟩ (P ∣ Q) [ μ ]⟶̂ R′ × R ≳′ R′
  lr (restriction a∉μ (par-left action))          = ⊥-elim (a∉μ refl)
  lr (restriction a∉μ (par-right action))         = ⊥-elim (a∉μ refl)
  lr (restriction a∉μ (par-τ {Q′ = R} action tr)) =
    ⟨ν proj₁ a ⟩ (P ∣ R)  ∼≡⟨ cong (λ R → ⟨ν _ ⟩ (_ ∣ R)) (·-only⟶ tr) ⟩■
    ⟨ν proj₁ a ⟩ (P ∣ Q)
      ⟵̂[ τ ]
    ⟨ν proj₁ a ⟩ (P ∣ Q)  ■

  rl :
    ∀ {μ R′} →
    ⟨ν proj₁ a ⟩ (P ∣ Q) [ μ ]⟶ R′ →
    ∃ λ R → ⟨ν proj₁ a ⟩ (name a ∙ P ∣ name (co a) ∙ Q) [ μ ]⇒ R ×
            R ≳′ R′
  rl {μ} (restriction {P′ = R} a∉μ P∣Q⟶R) =
    ⟨ν proj₁ a ⟩ (name a ∙ P ∣ name (co a) ∙ Q)  →⟨ ⟶: restriction _ (par-τ action action) ⟩
    ⟨ν proj₁ a ⟩ (P ∣ Q)                         →⟨ ⟶: restriction a∉μ P∣Q⟶R ⟩■
      E.⇒[ μ ]
    ⟨ν proj₁ a ⟩ R                               ■

-- One interpretation of the second part of Exercise 6.5.8 is
-- contradictory, assuming that Name is inhabited.

¬-6-5-8-2 :
  Name →
  ¬ (∀ {a b P} →
     ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) ≈
     ! name b ∙ ⟨ν proj₁ a ⟩ P)
¬-6-5-8-2 x =
  (∀ {a b P} →
   ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) ≈
   ! name b ∙ ⟨ν proj₁ a ⟩ P)                                          ↝⟨ (λ hyp → hyp {a = a} {b = co a}) ⟩

  (! ⟨ν proj₁ a ⟩ (name (co a) ∙ (a ∙) ∣ co a ∙) ≈
   ! name (co a) ∙ ⟨ν proj₁ a ⟩ ∅)                                     ↝⟨ (λ hyp → Σ-map id proj₁ $ W.right-to-left hyp
                                                                                (replication (par-right action))) ⟩
  ∃ (! ⟨ν proj₁ a ⟩ (name (co a) ∙ (a ∙) ∣ co a ∙) [ name (co a) ]⇒̂_)  ↝⟨ !P⇒̂ ∘ proj₂ ⟩□

  ⊥                                                                    □
  where
  a = x , true

  P = ⟨ν proj₁ a ⟩ (name (co a) ∙ (a ∙) ∣ co a ∙)

  P⟶ : ∀ {μ Q} → ¬ P [ μ ]⟶ Q
  P⟶ (restriction x≢x (par-left action))  = ⊥-elim (x≢x refl)
  P⟶ (restriction x≢x (par-right action)) = ⊥-elim (x≢x refl)
  P⟶ (restriction _   (par-τ action tr))  = names-are-not-inverted tr

  !P⟶ : ∀ {μ Q} → ¬ ! P [ μ ]⟶ Q
  !P⟶ (replication (par-left tr))  = !P⟶ tr
  !P⟶ (replication (par-right tr)) = P⟶ tr
  !P⟶ (replication (par-τ _ tr))   = P⟶ tr

  !P⇒ : ∀ {Q} → ! P ⇒ Q → Q ≡ ! P
  !P⇒ done          = refl
  !P⇒ (step _ tr _) = ⊥-elim (!P⟶ tr)

  !P[]⇒ : ∀ {μ Q} → ¬ ! P [ μ ]⇒ Q
  !P[]⇒ (steps trs tr _) rewrite !P⇒ trs = !P⟶ tr

  !P⇒̂ : ∀ {b Q} → ¬ ! P [ name b ]⇒̂ Q
  !P⇒̂ (silent () _)
  !P⇒̂ (non-silent _ tr) = !P[]⇒ tr

-- Another interpretation of the second part of Exercise 6.5.8 can be
-- proved.

6-5-8-2 :
  ∀ {i a b P} →
  proj₁ a ≢ proj₁ b →
  [ i ] ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) ≈
        ! name b ∙ ⟨ν proj₁ a ⟩ P
6-5-8-2 {i} {a} {b} {P} a≢b = W.⟨ lr , rl ⟩
  where
  6-5-8-2′ :
    [ i ] ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) ≈′
          ! name b ∙ ⟨ν proj₁ a ⟩ P
  force 6-5-8-2′ = 6-5-8-2 a≢b

  lr :
    ∀ {Q μ} →
    ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) [ μ ]⟶ Q →
    ∃ λ Q′ → ! name b ∙ ⟨ν proj₁ a ⟩ P [ μ ]⇒̂ Q′ × [ i ] Q ≈′ Q′
  lr {Q} tr = case 6-1-3-2 tr of λ where
    (inj₁ (_ , restriction a≢b (par-left action)
             , Q∼!νa[ba∣a̅P]∣νa[a∣a̅P])) →
      Q                                                    ∼⟨ Q∼!νa[ba∣a̅P]∣νa[a∣a̅P] ⟩

      ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) ∣
      ⟨ν proj₁ a ⟩ (a ∙ ∣ name (co a) ∙ P)                 ∼⟨ (_ ■) EL.∣-cong 6-5-8-1 _ ⟩

      ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) ∣
      ⟨ν proj₁ a ⟩ (∅ ∣ P)                                 ∼⟨ 6-5-8-2′ WL.∣-cong′ convert {a = ℓ} (⟨ν refl ⟩-cong ∣-left-identity) ⟩■

      ! name b ∙ ⟨ν proj₁ a ⟩ P ∣ ⟨ν proj₁ a ⟩ P

        W.⇐̂[ name b ]                                      ←⟨ ⟶: replication (par-right action) ⟩■

      ! name b ∙ ⟨ν proj₁ a ⟩ P

    (inj₁ (_ , restriction _ (par-τ action tr) , _)) →
      ⊥-elim (                              $⟨ tr ⟩
        name (co a) · _ [ name (co b) ]⟶ _  ↝⟨ ·-only ⟩
        name (co a) ≡ name (co b)           ↝⟨ cancel-name ⟩
        co a ≡ co b                         ↝⟨ cong proj₁ ⟩
        proj₁ a ≡ proj₁ b                   ↝⟨ a≢b ⟩□
        ⊥                                   □)

    (inj₁ (_ , restriction a≢a (par-right action) , _)) →
      ⊥-elim (a≢a refl)

    (inj₂ (refl , _ , _ , _
                , restriction _ (par-left action)
                , restriction _ (par-left tr)
                , _)) →
      ⊥-elim (names-are-not-inverted tr)

    (inj₂ (refl , _ , _ , _
                , restriction _ (par-right action)
                , restriction _ (par-right tr)
                , _)) →
      ⊥-elim (names-are-not-inverted tr)

    (inj₂ (refl , _ , _ , _
                , restriction a≢b (par-left action)
                , restriction _   (par-right tr)
                , _)) →
                                          $⟨ tr ⟩
      name (co a) ∙ P [ name (co b) ]⟶ _  ↝⟨ cong proj₁ ∘ cancel-name ∘ ·-only ⟩
      proj₁ a ≡ proj₁ b                   ↝⟨ a≢b ⟩
      ⊥                                   ↝⟨ ⊥-elim ⟩□
      _                                   □

    (inj₂ (refl , _ , _ , c
                , restriction a≢c (par-right tr)
                , restriction _   (par-left action)
                , _)) →
                                     $⟨ tr ⟩
      name (co a) ∙ P [ name c ]⟶ _  ↝⟨ cong proj₁ ∘ cancel-name ∘ ·-only ⟩
      proj₁ a ≡ proj₁ c              ↝⟨ a≢c ⟩
      ⊥                              ↝⟨ ⊥-elim ⟩□
      _                              □

  rl :
    ∀ {Q′ μ} →
    ! name b ∙ ⟨ν proj₁ a ⟩ P [ μ ]⟶ Q′ →
    ∃ λ Q → ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) [ μ ]⇒̂ Q ×
            [ i ] Q ≈′ Q′
  rl {Q′} tr = case 6-1-3-2 tr of λ where
    (inj₁ (_ , action , Q′∼!bνaP∣νaP)) →
      ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P)    →⟨ ⟶: replication (par-right (restriction a≢b (par-left action))) ⟩■

        W.⇒̂[ name b ]

      ! ⟨ν proj₁ a ⟩ (name b ∙ (a ∙) ∣ name (co a) ∙ P) ∣
      ⟨ν proj₁ a ⟩ (a ∙ ∣ name (co a) ∙ P)                 ∼′⟨ 6-5-8-2′ WL.∣-cong′ convert {a = ℓ} (6-5-8-1 a) ⟩

      ! name b ∙ ⟨ν proj₁ a ⟩ P ∣ ⟨ν proj₁ a ⟩ (∅ ∣ P)     ∼⟨ (_ ■) ∣-cong ⟨ν refl ⟩-cong ∣-left-identity ⟩ ∼:

      ! name b ∙ ⟨ν proj₁ a ⟩ P ∣ ⟨ν proj₁ a ⟩ P           ∼⟨ symmetric Q′∼!bνaP∣νaP ⟩■

      Q′

    (inj₂ (refl , _ , _ , _ , action , tr , _)) →
      ⊥-elim (names-are-not-inverted tr)


{-# OPTIONS --safe --no-qualified-instances #-}
module CF.Syntax where

open import Level

open import Data.Integer
open import Data.Bool
open import Data.String
open import Data.Product
open import Data.List hiding (null)
open import Data.List.Relation.Unary.All

open import Relation.Unary hiding (_⊢_)
open import Relation.Unary.PredicateTransformer using (Pt)
open import Relation.Binary.Structures using (IsPreorder)
open import Relation.Binary.PropositionalEquality using (isEquivalence; refl)
open import Relation.Ternary.Core
open import Relation.Ternary.Structures
open import Relation.Ternary.Structures.Syntax
open import Relation.Ternary.Monad.Possibly
open import Relation.Ternary.Monad.Weakening
open import Relation.Ternary.Data.Bigstar hiding ([_])

open import CF.Types
open import CF.Contexts.Lexical

open import Relation.Ternary.Construct.Product using (fst; snd)

open import Relation.Ternary.Data.Allstar Ty

data BinOp : Ty → Ty → Ty → Set where
  add sub mul div xor : BinOp int int int
  eq ne lt ge gt le   : BinOp int int bool

data Exp : Ty → Pred Ctx 0ℓ where
  unit       : ε[ Exp void ]
  num        : ℤ → ε[ Exp int ]
  bool       : Bool → ε[ Exp bool ]
  ifthenelse : ∀[ Exp bool ✴ Exp a ✴ Exp a ⇒ Exp a ]
  var'       : ∀[ Var a ⇒ Exp a ]
  bop        : BinOp a b c → ∀[ Exp a ✴ Exp b ⇒ Exp c ]

pattern var  = var' vars

module Statements (Block : Ty → Pred Ctx 0ℓ) where

  data Statement (r : Ty) : Pred Ctx 0ℓ where
    asgn          : ∀[ Var a ✴ Exp a ⇒ Statement r ]
    run           : ∀[ Exp a ⇒ Statement r ]
    ifthenelse    : ∀[ Exp bool ✴ Statement r ✴ Statement r ⇒ Statement r ]
    while         : ∀[ Exp bool ✴ Statement r ⇒ Statement r ]
    block         : ∀[ Block r ⇒ Statement r ]

mutual
  Stmt = Statements.Statement Block

  data Block (r : Ty) : Pred Ctx 0ℓ where
    local : ∀[ Exp a ✴ [ a ] ⊢ (Block r) ⇒ Block r ]
    cons  : ∀[ Stmt r ✴ Block r ⇒ Block r ]
    emp   : ε[ Block r ]

-- make constructors visible
open Statements Block public

infixr 5 _⍮⟨_⟩_
pattern _⍮⟨_⟩_ s σ b = cons (s ∙⟨ σ ⟩ b)
pattern _≔⟨_⟩_ e σ b = local (e ∙⟨ σ ⟩ b)


------------------------------------------------------------------------
-- Sequential colimits
------------------------------------------------------------------------

{-# OPTIONS --erased-cubical --safe #-}

-- The definition of sequential colimits and the statement of the
-- non-dependent universal property are based on those in van Doorn's
-- "Constructing the Propositional Truncation using Non-recursive
-- HITs".

-- The module is parametrised by a notion of equality. The higher
-- constructor of the HIT defining sequential colimits uses path
-- equality, but the supplied notion of equality is used for many
-- other things.

import Equality.Path as P

module Colimit.Sequential
  {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

open P.Derived-definitions-and-properties eq hiding (elim)

open import Prelude

open import Bijection equality-with-J using (_↔_)
import Colimit.Sequential.Erased eq as E
import Colimit.Sequential.Very-erased eq as VE
open import Equality.Path.Isomorphisms eq
open import Equivalence equality-with-J as Eq using (_≃_)
import Equivalence P.equality-with-J as PEq
open import Function-universe equality-with-J hiding (id; _∘_)

private
  variable
    a b p q : Level
    A B     : Type a
    P       : A → Type p
    n       : ℕ
    e x     : A

------------------------------------------------------------------------
-- The type

-- Sequential colimits.

data Colimit (P : ℕ → Type p) (step : ∀ {n} → P n → P (suc n)) :
             Type p where
  ∣_∣    : P n → Colimit P step
  ∣∣≡∣∣ᴾ : (x : P n) → ∣ step x ∣ P.≡ ∣ x ∣

-- A variant of ∣∣≡∣∣ᴾ.

∣∣≡∣∣ : {step : ∀ {n} → P n → P (suc n)} (x : P n) →
        _≡_ {A = Colimit P step} ∣ step x ∣ ∣ x ∣
∣∣≡∣∣ x = _↔_.from ≡↔≡ (∣∣≡∣∣ᴾ x)

------------------------------------------------------------------------
-- Eliminators

-- A dependent eliminator, expressed using paths.

record Elimᴾ
         {P : ℕ → Type p} {step : ∀ {n} → P n → P (suc n)}
         (Q : Colimit P step → Type q) : Type (p ⊔ q) where
  no-eta-equality
  field
    ∣∣ʳ    : (x : P n) → Q ∣ x ∣
    ∣∣≡∣∣ʳ :
      (x : P n) →
      P.[ (λ i → Q (∣∣≡∣∣ᴾ x i)) ] ∣∣ʳ (step x) ≡ ∣∣ʳ x

open Elimᴾ public

elimᴾ :
  {step : ∀ {n} → P n → P (suc n)} {Q : Colimit P step → Type q} →
  Elimᴾ Q → (x : Colimit P step) → Q x
elimᴾ {P = P} {step = step} {Q = Q} e = helper
  where
  module E′ = Elimᴾ e

  helper : (x : Colimit P step) → Q x
  helper ∣ x ∣        = E′.∣∣ʳ x
  helper (∣∣≡∣∣ᴾ x i) = E′.∣∣≡∣∣ʳ x i

-- A non-dependent eliminator, expressed using paths.

record Recᴾ
         (P : ℕ → Type p) (step : ∀ {n} → P n → P (suc n))
         (B : Type b) : Type (p ⊔ b) where
  no-eta-equality
  field
    ∣∣ʳ    : P n → B
    ∣∣≡∣∣ʳ : (x : P n) → ∣∣ʳ (step x) P.≡ ∣∣ʳ x

open Recᴾ public

recᴾ :
  {step : ∀ {n} → P n → P (suc n)} →
  Recᴾ P step B → Colimit P step → B
recᴾ r = elimᴾ λ where
    .∣∣ʳ    → R.∣∣ʳ
    .∣∣≡∣∣ʳ → R.∣∣≡∣∣ʳ
  where
  module R = Recᴾ r

-- A dependent eliminator.

record Elim
         {P : ℕ → Type p} {step : ∀ {n} → P n → P (suc n)}
         (Q : Colimit P step → Type q) : Type (p ⊔ q) where
  no-eta-equality
  field
    ∣∣ʳ    : (x : P n) → Q ∣ x ∣
    ∣∣≡∣∣ʳ : (x : P n) → subst Q (∣∣≡∣∣ x) (∣∣ʳ (step x)) ≡ ∣∣ʳ x

open Elim public

elim :
  {step : ∀ {n} → P n → P (suc n)} {Q : Colimit P step → Type q} →
  Elim Q → (x : Colimit P step) → Q x
elim e = elimᴾ λ where
    .∣∣ʳ      → E′.∣∣ʳ
    .∣∣≡∣∣ʳ x → subst≡→[]≡ (E′.∣∣≡∣∣ʳ x)
  where
  module E′ = Elim e

-- A "computation" rule.

elim-∣∣≡∣∣ : dcong (elim e) (∣∣≡∣∣ x) ≡ Elim.∣∣≡∣∣ʳ e x
elim-∣∣≡∣∣ = dcong-subst≡→[]≡ (refl _)

-- A non-dependent eliminator.

record Rec
         (P : ℕ → Type p) (step : ∀ {n} → P n → P (suc n))
         (B : Type b) : Type (p ⊔ b) where
  no-eta-equality
  field
    ∣∣ʳ    : P n → B
    ∣∣≡∣∣ʳ : (x : P n) → ∣∣ʳ (step x) ≡ ∣∣ʳ x

open Rec public

rec :
  {step : ∀ {n} → P n → P (suc n)} →
  Rec P step B → Colimit P step → B
rec r = recᴾ λ where
    .∣∣ʳ      → R.∣∣ʳ
    .∣∣≡∣∣ʳ x → _↔_.to ≡↔≡ (R.∣∣≡∣∣ʳ x)
  where
  module R = Rec r

-- A "computation" rule.

rec-∣∣≡∣∣ :
  {step : ∀ {n} → P n → P (suc n)} {r : Rec P step B} {x : P n} →
  cong (rec r) (∣∣≡∣∣ x) ≡ Rec.∣∣≡∣∣ʳ r x
rec-∣∣≡∣∣ = cong-≡↔≡ (refl _)

------------------------------------------------------------------------
-- The universal property

-- The universal property of the sequential colimit.

universal-property :
  {step : ∀ {n} → P n → P (suc n)} →
  (Colimit P step → B) ≃
  (∃ λ (f : ∀ n → P n → B) → ∀ n x → f (suc n) (step x) ≡ f n x)
universal-property {P = P} {B = B} {step = step} =
  Eq.↔→≃ to from to∘from from∘to
  where
  to :
    (Colimit P step → B) →
    ∃ λ (f : ∀ n → P n → B) → ∀ n x → f (suc n) (step x) ≡ f n x
  to h = (λ _ → h ∘ ∣_∣)
       , (λ _ x →
            h ∣ step x ∣  ≡⟨ cong h (∣∣≡∣∣ x) ⟩∎
            h ∣ x ∣       ∎)

  from :
    (∃ λ (f : ∀ n → P n → B) → ∀ n x → f (suc n) (step x) ≡ f n x) →
    Colimit P step → B
  from (f , g) = rec λ where
    .∣∣ʳ    → f _
    .∣∣≡∣∣ʳ → g _

  to∘from : ∀ p → to (from p) ≡ p
  to∘from (f , g) = cong (f ,_) $ ⟨ext⟩ λ n → ⟨ext⟩ λ x →
    cong (rec _) (∣∣≡∣∣ x)  ≡⟨ rec-∣∣≡∣∣ ⟩∎
    g n x                   ∎

  from∘to : ∀ h → from (to h) ≡ h
  from∘to h = ⟨ext⟩ $ elim λ where
    .∣∣ʳ    _ → refl _
    .∣∣≡∣∣ʳ x →
      subst (λ z → from (to h) z ≡ h z) (∣∣≡∣∣ x) (refl _)  ≡⟨ subst-in-terms-of-trans-and-cong ⟩

      trans (sym (cong (from (to h)) (∣∣≡∣∣ x)))
        (trans (refl _) (cong h (∣∣≡∣∣ x)))                 ≡⟨ cong₂ (λ p q → trans (sym p) q)
                                                                 rec-∣∣≡∣∣
                                                                 (trans-reflˡ _) ⟩

      trans (sym (cong h (∣∣≡∣∣ x))) (cong h (∣∣≡∣∣ x))     ≡⟨ trans-symˡ _ ⟩∎

      refl _                                                ∎

-- A dependently typed variant of the sequential colimit's universal
-- property.

universal-property-Π :
  {step : ∀ {n} → P n → P (suc n)} →
  {Q : Colimit P step → Type q} →
  ((x : Colimit P step) → Q x) ≃
  (∃ λ (f : ∀ n (x : P n) → Q ∣ x ∣) →
     ∀ n x → subst Q (∣∣≡∣∣ x) (f (suc n) (step x)) ≡ f n x)
universal-property-Π {P = P} {step = step} {Q = Q} =
  Eq.↔→≃ to from to∘from from∘to
  where
  to :
    ((x : Colimit P step) → Q x) →
    ∃ λ (f : ∀ n (x : P n) → Q ∣ x ∣) →
      ∀ n x → subst Q (∣∣≡∣∣ x) (f (suc n) (step x)) ≡ f n x
  to h = (λ _ → h ∘ ∣_∣)
       , (λ _ x →
            subst Q (∣∣≡∣∣ x) (h ∣ step x ∣)  ≡⟨ dcong h (∣∣≡∣∣ x) ⟩∎
            h ∣ x ∣                           ∎)

  from :
    (∃ λ (f : ∀ n (x : P n) → Q ∣ x ∣) →
       ∀ n x → subst Q (∣∣≡∣∣ x) (f (suc n) (step x)) ≡ f n x) →
    (x : Colimit P step) → Q x
  from (f , g) = elim λ where
    .∣∣ʳ    → f _
    .∣∣≡∣∣ʳ → g _

  to∘from : ∀ p → to (from p) ≡ p
  to∘from (f , g) = cong (f ,_) $ ⟨ext⟩ λ n → ⟨ext⟩ λ x →
    dcong (elim _) (∣∣≡∣∣ x)  ≡⟨ elim-∣∣≡∣∣ ⟩∎
    g n x                     ∎

  from∘to : ∀ h → from (to h) ≡ h
  from∘to h = ⟨ext⟩ $ elim λ where
    .∣∣ʳ    _ → refl _
    .∣∣≡∣∣ʳ x →
      subst (λ z → from (to h) z ≡ h z) (∣∣≡∣∣ x) (refl _)  ≡⟨ subst-in-terms-of-trans-and-dcong ⟩

      trans (sym (dcong (from (to h)) (∣∣≡∣∣ x)))
        (trans (cong (subst Q (∣∣≡∣∣ x)) (refl _))
           (dcong h (∣∣≡∣∣ x)))                             ≡⟨ cong₂ (λ p q → trans (sym p) q)
                                                                 elim-∣∣≡∣∣
                                                                 (trans (cong (flip trans _) $
                                                                         cong-refl _) $
                                                                  trans-reflˡ _) ⟩

      trans (sym (dcong h (∣∣≡∣∣ x))) (dcong h (∣∣≡∣∣ x))   ≡⟨ trans-symˡ _ ⟩∎

      refl _                                                ∎

------------------------------------------------------------------------
-- Some conversion functions

-- E.Colimitᴱ P step implies Colimit P step.

Colimitᴱ→Colimit :
  {step : ∀ {n} → P n → P (suc n)} →
  E.Colimitᴱ P step → Colimit P step
Colimitᴱ→Colimit = E.rec λ where
  .E.∣∣ʳ    → ∣_∣
  .E.∣∣≡∣∣ʳ → ∣∣≡∣∣

-- In erased contexts E.Colimitᴱ P step is equivalent to
-- Colimit P step.

@0 Colimitᴱ≃Colimit :
  {step : ∀ {n} → P n → P (suc n)} →
  E.Colimitᴱ P step ≃ Colimit P step
Colimitᴱ≃Colimit = Eq.↔→≃
  Colimitᴱ→Colimit
  Colimit→Colimitᴱ
  (elim λ @0 where
     .∣∣ʳ _    → refl _
     .∣∣≡∣∣ʳ x →
       subst (λ x → Colimitᴱ→Colimit (Colimit→Colimitᴱ x) ≡ x)
         (∣∣≡∣∣ x) (refl _)                                     ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (Colimitᴱ→Colimit ∘ Colimit→Colimitᴱ)
                     (∣∣≡∣∣ x)))
         (trans (refl _) (cong id (∣∣≡∣∣ x)))                   ≡⟨ cong₂ (trans ∘ sym)
                                                                     (trans (sym $ cong-∘ _ _ _) $
                                                                      trans (cong (cong Colimitᴱ→Colimit) rec-∣∣≡∣∣) $
                                                                      E.rec-∣∣≡∣∣)
                                                                     (trans (trans-reflˡ _) $
                                                                      sym $ cong-id _) ⟩

       trans (sym (∣∣≡∣∣ x)) (∣∣≡∣∣ x)                          ≡⟨ trans-symˡ _ ⟩∎

       refl _                                                   ∎)
  (E.elim λ where
     .E.∣∣ʳ _    → refl _
     .E.∣∣≡∣∣ʳ x →
       subst (λ x → Colimit→Colimitᴱ (Colimitᴱ→Colimit x) ≡ x)
         (E.∣∣≡∣∣ x) (refl _)                                   ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (Colimit→Colimitᴱ ∘ Colimitᴱ→Colimit)
                     (E.∣∣≡∣∣ x)))
         (trans (refl _) (cong id (E.∣∣≡∣∣ x)))                 ≡⟨ cong₂ (trans ∘ sym)
                                                                     (trans (sym $ cong-∘ _ _ _) $
                                                                      trans (cong (cong Colimit→Colimitᴱ) E.rec-∣∣≡∣∣) $
                                                                      rec-∣∣≡∣∣)
                                                                     (trans (trans-reflˡ _) $
                                                                      sym $ cong-id _) ⟩

       trans (sym (E.∣∣≡∣∣ x)) (E.∣∣≡∣∣ x)                      ≡⟨ trans-symˡ _ ⟩∎

       refl _                                                   ∎)
  where
  Colimit→Colimitᴱ = rec λ @0 where
    .∣∣ʳ    → E.∣_∣
    .∣∣≡∣∣ʳ → E.∣∣≡∣∣

-- Some instances of VE.Colimitᴱ can be converted to instances of
-- Colimit. Note that P₀ and P₊ target the same universe.

Very-Colimitᴱ→Colimit :
  {P₀ : Type p}
  {P₊ : ℕ → Type p}
  {step₀ : P₀ → P₊ zero} →
  {step₊ : ∀ {n} → P₊ n → P₊ (suc n)} →
  VE.Colimitᴱ P₀ P₊ step₀ step₊ →
  Colimit
    (ℕ-rec P₀ (λ n _ → P₊ n))
    (λ {n} → ℕ-rec {P = λ n → ℕ-rec P₀ (λ n _ → P₊ n) n → P₊ n}
                   step₀ (λ _ _ → step₊) n)
Very-Colimitᴱ→Colimit = VE.rec λ where
  .VE.∣∣₀ʳ     → ∣_∣ {n = 0}
  .VE.∣∣₊ʳ     → ∣_∣
  .VE.∣∣₊≡∣∣₀ʳ → ∣∣≡∣∣
  .VE.∣∣₊≡∣∣₊ʳ → ∣∣≡∣∣

-- In erased contexts there are equivalences between some instances of
-- VE.Colimitᴱ and some instances of Colimit.

@0 Very-Colimitᴱ≃Colimit :
  {P₀ : Type p}
  {P₊ : ℕ → Type p}
  {step₀ : P₀ → P₊ zero} →
  {step₊ : ∀ {n} → P₊ n → P₊ (suc n)} →
  VE.Colimitᴱ P₀ P₊ step₀ step₊ ≃
  Colimit
    (ℕ-rec P₀ (λ n _ → P₊ n))
    (λ {n} → ℕ-rec {P = λ n → ℕ-rec P₀ (λ n _ → P₊ n) n → P₊ n}
                   step₀ (λ _ _ → step₊) n)
Very-Colimitᴱ≃Colimit = Eq.↔→≃
  Very-Colimitᴱ→Colimit
  Colimit→Colimitᴱ
  (elim λ @0 where
     .∣∣ʳ {n = zero} _    → refl _
     .∣∣ʳ {n = suc _} _   → refl _
     .∣∣≡∣∣ʳ {n = zero} x →
       subst (λ x → Very-Colimitᴱ→Colimit (Colimit→Colimitᴱ x) ≡ x)
         (∣∣≡∣∣ x) (refl _)                                          ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (Very-Colimitᴱ→Colimit ∘ Colimit→Colimitᴱ)
                     (∣∣≡∣∣ x)))
         (trans (refl _) (cong id (∣∣≡∣∣ x)))                        ≡⟨ cong₂ (trans ∘ sym)
                                                                          (trans (sym $ cong-∘ _ _ _) $
                                                                           trans (cong (cong Very-Colimitᴱ→Colimit) rec-∣∣≡∣∣) $
                                                                           VE.rec-∣∣₊≡∣∣₀)
                                                                          (trans (trans-reflˡ _) $
                                                                           sym $ cong-id _) ⟩

       trans (sym (∣∣≡∣∣ x)) (∣∣≡∣∣ x)                               ≡⟨ trans-symˡ _ ⟩∎

       refl _                                                        ∎
     .∣∣≡∣∣ʳ {n = suc _} x →
       subst (λ x → Very-Colimitᴱ→Colimit (Colimit→Colimitᴱ x) ≡ x)
         (∣∣≡∣∣ x) (refl _)                                          ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (Very-Colimitᴱ→Colimit ∘ Colimit→Colimitᴱ)
                     (∣∣≡∣∣ x)))
         (trans (refl _) (cong id (∣∣≡∣∣ x)))                        ≡⟨ cong₂ (trans ∘ sym)
                                                                          (trans (sym $ cong-∘ _ _ _) $
                                                                           trans (cong (cong Very-Colimitᴱ→Colimit) rec-∣∣≡∣∣) $
                                                                           VE.rec-∣∣₊≡∣∣₊)
                                                                          (trans (trans-reflˡ _) $
                                                                           sym $ cong-id _) ⟩

       trans (sym (∣∣≡∣∣ x)) (∣∣≡∣∣ x)                               ≡⟨ trans-symˡ _ ⟩∎

       refl _                                                        ∎)
  (VE.elim λ where
     .VE.∣∣₀ʳ _     → refl _
     .VE.∣∣₊ʳ _     → refl _
     .VE.∣∣₊≡∣∣₀ʳ x →
       subst (λ x → Colimit→Colimitᴱ (Very-Colimitᴱ→Colimit x) ≡ x)
         (VE.∣∣₊≡∣∣₀ x) (refl _)                                     ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (Colimit→Colimitᴱ ∘ Very-Colimitᴱ→Colimit)
                     (VE.∣∣₊≡∣∣₀ x)))
         (trans (refl _) (cong id (VE.∣∣₊≡∣∣₀ x)))                   ≡⟨ cong₂ (trans ∘ sym)
                                                                          (trans (sym $ cong-∘ _ _ _) $
                                                                           trans (cong (cong Colimit→Colimitᴱ) VE.rec-∣∣₊≡∣∣₀) $
                                                                           rec-∣∣≡∣∣)
                                                                          (trans (trans-reflˡ _) $
                                                                           sym $ cong-id _) ⟩

       trans (sym (VE.∣∣₊≡∣∣₀ x)) (VE.∣∣₊≡∣∣₀ x)                     ≡⟨ trans-symˡ _ ⟩∎

       refl _                                                        ∎
     .VE.∣∣₊≡∣∣₊ʳ x →
       subst (λ x → Colimit→Colimitᴱ (Very-Colimitᴱ→Colimit x) ≡ x)
         (VE.∣∣₊≡∣∣₊ x) (refl _)                                     ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (Colimit→Colimitᴱ ∘ Very-Colimitᴱ→Colimit)
                     (VE.∣∣₊≡∣∣₊ x)))
         (trans (refl _) (cong id (VE.∣∣₊≡∣∣₊ x)))                   ≡⟨ cong₂ (trans ∘ sym)
                                                                          (trans (sym $ cong-∘ _ _ _) $
                                                                           trans (cong (cong Colimit→Colimitᴱ) VE.rec-∣∣₊≡∣∣₊) $
                                                                           rec-∣∣≡∣∣)
                                                                          (trans (trans-reflˡ _) $
                                                                           sym $ cong-id _) ⟩

       trans (sym (VE.∣∣₊≡∣∣₊ x)) (VE.∣∣₊≡∣∣₊ x)                     ≡⟨ trans-symˡ _ ⟩∎

       refl _                                                        ∎)
  where
  Colimit→Colimitᴱ = rec λ @0 where
    .∣∣ʳ {n = zero}     → VE.∣_∣₀
    .∣∣ʳ {n = suc _}    → VE.∣_∣₊
    .∣∣≡∣∣ʳ {n = zero}  → VE.∣∣₊≡∣∣₀
    .∣∣≡∣∣ʳ {n = suc _} → VE.∣∣₊≡∣∣₊


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

module sets.empty where

open import level using ()

data ⊥ : Set where

⊥-elim : ∀ {i}{A : Set i} → ⊥ → A
⊥-elim ()

¬_ : ∀ {i} → Set i → Set i
¬ X = X → ⊥
infix 3 ¬_


------------------------------------------------------------------------
-- A variant of set quotients with erased higher constructors
------------------------------------------------------------------------

{-# OPTIONS --erased-cubical --safe #-}

-- Partly following the HoTT book, but adapted for erasure.
--
-- Unlike the HoTT book, but following the cubical library (in which
-- set quotients were implemented by Zesen Qian and Anders Mörtberg),
-- the quotienting relations are not (always) required to be
-- propositional.

-- The module is parametrised by a notion of equality. The higher
-- constructors of the HIT defining quotients use path equality, but
-- the supplied notion of equality is used for many other things.

import Equality.Path as P

module Quotient.Erased
  {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

open P.Derived-definitions-and-properties eq hiding (elim)

open import Logical-equivalence using (_⇔_)
open import Prelude

open import Bijection equality-with-J using (_↔_)
open import Equality.Decidable-UIP equality-with-J using (Constant)
open import Equality.Path.Isomorphisms eq
import Equality.Path.Isomorphisms.Univalence eq as U
open import Equivalence equality-with-J as Eq using (_≃_)
import Equivalence.Erased equality-with-J as EEq
open import Equivalence-relation equality-with-J
open import Erased.Cubical eq as Er using (Erased; Erasedᴾ; [_])
open import Function-universe equality-with-J as F hiding (id; _∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional eq as PT using (∥_∥; ∣_∣)
open import H-level.Truncation.Propositional.Erased eq as PTᴱ
  using (∥_∥ᴱ; ∣_∣; Surjectiveᴱ)
open import Quotient eq as Q using (_/_)
open import Sum equality-with-J
open import Surjection equality-with-J using (_↠_)
open import Univalence-axiom equality-with-J

private
  variable
    a a₁ a₂ p r r₁ r₂ : Level
    A A₁ A₂ B         : Type a
    P                 : A → Type p
    R                 : A → A → Type r
    f k x y           : A

------------------------------------------------------------------------
-- A re-export

-- This module re-exports Quotient.Erased.Basics.

open import Quotient.Erased.Basics eq public

------------------------------------------------------------------------
-- Preservation lemmas

-- Two preservation lemmas for functions.

infix 5 _/ᴱ-map-∥∥_ _/ᴱ-map_

_/ᴱ-map-∥∥_ :
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁→A₂ : A₁ → A₂) →
  @0 (∀ x y → ∥ R₁ x y ∥ → ∥ R₂ (A₁→A₂ x) (A₁→A₂ y) ∥) →
  A₁ /ᴱ R₁ → A₂ /ᴱ R₂
_/ᴱ-map-∥∥_ {R₁ = R₁} {R₂ = R₂} A₁→A₂ R₁→R₂ = rec λ where
  .[]ʳ                                   → [_] ∘ A₁→A₂
  .is-setʳ                               → /ᴱ-is-set
  .[]-respects-relationʳ {x = x} {y = y} →
     R₁ x y                      ↝⟨ ∣_∣ ⟩
     ∥ R₁ x y ∥                  ↝⟨ R₁→R₂ _ _ ⟩
     ∥ R₂ (A₁→A₂ x) (A₁→A₂ y) ∥  ↝⟨ PT.rec /ᴱ-is-set []-respects-relation ⟩□
     [ A₁→A₂ x ] ≡ [ A₁→A₂ y ]   □

_/ᴱ-map_ :
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁→A₂ : A₁ → A₂) →
  @0 (∀ x y → R₁ x y → R₂ (A₁→A₂ x) (A₁→A₂ y)) →
  A₁ /ᴱ R₁ → A₂ /ᴱ R₂
A₁→A₂ /ᴱ-map R₁→R₂ = A₁→A₂ /ᴱ-map-∥∥ λ x y → PT.∥∥-map (R₁→R₂ x y)

-- Two preservation lemmas for logical equivalences.

/ᴱ-cong-∥∥-⇔ :
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁⇔A₂ : A₁ ⇔ A₂) →
  @0 (∀ x y → ∥ R₁ x y ∥ → ∥ R₂ (_⇔_.to   A₁⇔A₂ x) (_⇔_.to   A₁⇔A₂ y) ∥) →
  @0 (∀ x y → ∥ R₂ x y ∥ → ∥ R₁ (_⇔_.from A₁⇔A₂ x) (_⇔_.from A₁⇔A₂ y) ∥) →
  A₁ /ᴱ R₁ ⇔ A₂ /ᴱ R₂
/ᴱ-cong-∥∥-⇔ A₁⇔A₂ R₁→R₂ R₂→R₁ = record
  { to   = _⇔_.to   A₁⇔A₂ /ᴱ-map-∥∥ R₁→R₂
  ; from = _⇔_.from A₁⇔A₂ /ᴱ-map-∥∥ R₂→R₁
  }

/ᴱ-cong-⇔ :
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁⇔A₂ : A₁ ⇔ A₂) →
  @0 (∀ x y → R₁ x y → R₂ (_⇔_.to   A₁⇔A₂ x) (_⇔_.to   A₁⇔A₂ y)) →
  @0 (∀ x y → R₂ x y → R₁ (_⇔_.from A₁⇔A₂ x) (_⇔_.from A₁⇔A₂ y)) →
  A₁ /ᴱ R₁ ⇔ A₂ /ᴱ R₂
/ᴱ-cong-⇔ A₁⇔A₂ R₁→R₂ R₂→R₁ =
  /ᴱ-cong-∥∥-⇔ A₁⇔A₂ (λ x y → PT.∥∥-map (R₁→R₂ x y))
                     (λ x y → PT.∥∥-map (R₂→R₁ x y))

-- Two preservation lemmas for split surjections.

infix 5 _/ᴱ-cong-∥∥-↠_ _/ᴱ-cong-↠_

_/ᴱ-cong-∥∥-↠_ :
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁↠A₂ : A₁ ↠ A₂) →
  @0 (∀ x y → ∥ R₁ x y ∥ ⇔ ∥ R₂ (_↠_.to A₁↠A₂ x) (_↠_.to A₁↠A₂ y) ∥) →
  A₁ /ᴱ R₁ ↠ A₂ /ᴱ R₂
_/ᴱ-cong-∥∥-↠_ {R₁ = R₁} {R₂ = R₂} A₁↠A₂ R₁⇔R₂ = record
  { logical-equivalence = /ᴱ-cong-∥∥-⇔
      (_↠_.logical-equivalence A₁↠A₂)
      (λ x y → _⇔_.to (R₁⇔R₂ x y))
      (λ x y → ∥ R₂ x y ∥                          ↝⟨ ≡⇒↝ _ (sym $ cong₂ (λ x y → ∥ R₂ x y ∥) (right-inverse-of x) (right-inverse-of y)) ⟩
               ∥ R₂ (to (from x)) (to (from y)) ∥  ↝⟨ _⇔_.from (R₁⇔R₂ _ _) ⟩□
               ∥ R₁ (from x) (from y) ∥            □)
  ; right-inverse-of = elim-prop λ where
      .[]ʳ x →
        [ to (from x) ]  ≡⟨ cong [_] $ right-inverse-of x ⟩∎
        [ x ]            ∎
      .is-propositionʳ _ → /ᴱ-is-set
  }
  where
  open _↠_ A₁↠A₂

_/ᴱ-cong-↠_ :
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁↠A₂ : A₁ ↠ A₂) →
  @0 (∀ x y → R₁ x y ⇔ R₂ (_↠_.to A₁↠A₂ x) (_↠_.to A₁↠A₂ y)) →
  A₁ /ᴱ R₁ ↠ A₂ /ᴱ R₂
A₁↠A₂ /ᴱ-cong-↠ R₁⇔R₂ =
  A₁↠A₂ /ᴱ-cong-∥∥-↠ λ x y → PT.∥∥-cong-⇔ (R₁⇔R₂ x y)

-- Two preservation lemmas for isomorphisms.

infix 5 _/ᴱ-cong-∥∥_ _/ᴱ-cong_

_/ᴱ-cong-∥∥_ :
  {A₁ : Type a₁} {A₂ : Type a₂}
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁↔A₂ : A₁ ↔[ k ] A₂) →
  @0 (∀ x y →
        ∥ R₁ x y ∥ ⇔
        ∥ R₂ (to-implication A₁↔A₂ x) (to-implication A₁↔A₂ y) ∥) →
  A₁ /ᴱ R₁ ↔[ k ] A₂ /ᴱ R₂
_/ᴱ-cong-∥∥_ {k = k} {R₁ = R₁} {R₂ = R₂} A₁↔A₂′ R₁⇔R₂ =
  from-bijection (record
    { surjection = from-isomorphism A₁↔A₂ /ᴱ-cong-∥∥-↠ λ x y →
        ∥ R₁ x y ∥                                                  ↝⟨ R₁⇔R₂ x y ⟩
        ∥ R₂ (to-implication A₁↔A₂′ x) (to-implication A₁↔A₂′ y) ∥  ↝⟨ ≡⇒↝ _ $ cong₂ (λ f g → ∥ R₂ (f x) (g y) ∥)
                                                                                (to-implication∘from-isomorphism k bijection)
                                                                                (to-implication∘from-isomorphism k bijection) ⟩□
        ∥ R₂ (to x) (to y) ∥                                        □
    ; left-inverse-of = elim-prop λ where
        .[]ʳ x →
          [ from (to x) ]  ≡⟨ cong [_] $ left-inverse-of x ⟩∎
          [ x ]            ∎
        .is-propositionʳ _ → /ᴱ-is-set
    })
  where
  A₁↔A₂ = from-isomorphism A₁↔A₂′

  open _↔_ A₁↔A₂

_/ᴱ-cong_ :
  {A₁ : Type a₁} {A₂ : Type a₂}
  {@0 R₁ : A₁ → A₁ → Type r₁}
  {@0 R₂ : A₂ → A₂ → Type r₂} →
  (A₁↔A₂ : A₁ ↔[ k ] A₂) →
  @0 (∀ x y →
        R₁ x y ⇔ R₂ (to-implication A₁↔A₂ x) (to-implication A₁↔A₂ y)) →
  A₁ /ᴱ R₁ ↔[ k ] A₂ /ᴱ R₂
_/ᴱ-cong_ A₁↔A₂ R₁⇔R₂ =
  A₁↔A₂ /ᴱ-cong-∥∥ λ x y → PT.∥∥-cong-⇔ (R₁⇔R₂ x y)

------------------------------------------------------------------------
-- Some properties

-- [_] is surjective with erased proofs.

Surjectiveᴱ-[] : Surjectiveᴱ ([_] {R = R})
Surjectiveᴱ-[] = elim-prop λ where
  .[]ʳ x             → ∣ x , [ refl _ ] ∣
  .is-propositionʳ _ → PTᴱ.truncation-is-proposition

-- Quotienting by the propositional truncation of a relation is
-- equivalent to quotienting by the relation itself.

/ᴱ-∥∥≃/ᴱ : A /ᴱ (λ x y → ∥ R x y ∥) ≃ A /ᴱ R
/ᴱ-∥∥≃/ᴱ {R = R} = F.id /ᴱ-cong-∥∥ λ x y →
  ∥ ∥ R x y ∥ ∥  ↔⟨ PT.flatten ⟩□
  ∥ R x y ∥      □

-- If R is an equivalence relation, then A /ᴱ R is weakly effective
-- (where this is expressed using ∥_∥ᴱ).
--
-- This proof is based on that of Proposition 2 in "Quotienting the
-- Delay Monad by Weak Bisimilarity" by Chapman, Uustalu and Veltri.

weakly-effective :
  {R : A → A → Type r} →
  @0 Is-equivalence-relation R →
  ∥ R x x ∥ᴱ →
  _≡_ {A = A /ᴱ R} [ x ] [ y ] → ∥ R x y ∥ᴱ
weakly-effective
  {A = A} {r = r} {x = x} {y = y} {R = R} eq ∥Rxx∥ᴱ [x]≡[y] =
                     $⟨ ∥Rxx∥ᴱ ⟩
  R′ x [ x ] .proj₁  ↝⟨ ≡⇒→ (cong (λ y → R′ x y .proj₁) [x]≡[y]) ⟩
  R′ x [ y ] .proj₁  ↔⟨⟩
  ∥ R x y ∥ᴱ         □
  where
  R′ : A → A /ᴱ R → ∃ λ (P : Type r) → Erased (Is-proposition P)
  R′ x = rec λ where
    .[]ʳ y → ∥ R x y ∥ᴱ , [ PTᴱ.truncation-is-proposition ]

    .is-setʳ →                                               $⟨ (λ {_ _} → Is-set-∃-Is-proposition ext U.prop-ext) ⟩
      Is-set (Proposition r)                                 ↝⟨ H-level-cong _ 2
                                                                  (∃-cong λ _ → inverse $ Er.Erased↔ .Er.erased)
                                                                  ⦂ (_ → _) ⟩□
      Is-set (∃ λ (P : Type r) → Erased (Is-proposition P))  □

    .[]-respects-relationʳ {x = y} {y = z} →
      R y z                                        ↝⟨ (λ r → record
                                                         { to   = flip (eq .Is-equivalence-relation.transitive) r
                                                         ; from = flip (eq .Is-equivalence-relation.transitive)
                                                                    (eq .Is-equivalence-relation.symmetric r)
                                                         }) ⟩
      R x y ⇔ R x z                                ↝⟨ EEq._≃ᴱ_.logical-equivalence ∘ PTᴱ.∥∥ᴱ-cong-⇔ ⟩
      ∥ R x y ∥ᴱ ⇔ ∥ R x z ∥ᴱ                      ↔⟨ ⇔↔≡″ ext U.prop-ext ⟩
      (∥ R x y ∥ᴱ , _) ≡ (∥ R x z ∥ᴱ , _)          ↝⟨ cong (Σ-map id Er.[_]→) ⟩□
      (∥ R x y ∥ᴱ , [ _ ]) ≡ (∥ R x z ∥ᴱ , [ _ ])  □

-- If R is an equivalence relation, and R is propositional (for x
-- and y), then A /ᴱ R is effective (for x and y).

effective :
  @0 Is-equivalence-relation R →
  @0 Is-proposition (R x y) →
  R x x →
  _≡_ {A = A /ᴱ R} [ x ] [ y ] → R x y
effective {R = R} {x = x} {y = y} eq prop Rxx =
  [ x ] ≡ [ y ]  ↝⟨ weakly-effective eq ∣ Rxx ∣ ⟩
  ∥ R x y ∥ᴱ     ↔⟨ PTᴱ.∥∥ᴱ↔ prop ⟩□
  R x y          □

-- If R is an equivalence relation, and R is propositional (for x
-- and y), then R x y is equivalent to equality of x and y under [_]
-- (in erased contexts).

@0 related≃[equal] :
  Is-equivalence-relation R →
  Is-proposition (R x y) →
  R x y ≃ _≡_ {A = A /ᴱ R} [ x ] [ y ]
related≃[equal] eq prop =
  Eq.⇔→≃
    prop
    /ᴱ-is-set
    []-respects-relation
    (effective eq prop (eq .Is-equivalence-relation.reflexive))

-- If R is an equivalence relation, then ∥ R x y ∥ is equivalent to
-- equality of x and y under [_] (in erased contexts).

@0 ∥related∥≃[equal] :
  Is-equivalence-relation R →
  ∥ R x y ∥ ≃ _≡_ {A = A /ᴱ R} [ x ] [ y ]
∥related∥≃[equal] {R = R} {x = x} {y = y} eq =
  ∥ R x y ∥      ↝⟨ inverse PT.∥∥ᴱ≃∥∥ ⟩
  ∥ R x y ∥ᴱ     ↝⟨ Eq.⇔→≃
                      PTᴱ.truncation-is-proposition
                      /ᴱ-is-set
                      (PTᴱ.rec λ @0 where
                         .PTᴱ.truncation-is-propositionʳ → /ᴱ-is-set
                         .PTᴱ.∣∣ʳ                        →
                           []-respects-relation)
                      (weakly-effective eq ∣ eq .Is-equivalence-relation.reflexive ∣) ⟩□
  [ x ] ≡ [ y ]  □

-- Quotienting with equality (for a set) amounts to the same thing as
-- not quotienting at all.

/ᴱ≡↔ : @0 Is-set A → A /ᴱ _≡_ ↔ A
/ᴱ≡↔ A-set = record
  { surjection = record
    { logical-equivalence = record
      { from = [_]
      ; to   = rec λ where
          .[]ʳ                   → id
          .[]-respects-relationʳ → id
          .is-setʳ               → A-set
      }
    ; right-inverse-of = λ _ → refl _
    }
  ; left-inverse-of = elim-prop λ where
      .[]ʳ _             → refl _
      .is-propositionʳ _ → /ᴱ-is-set
  }

-- Quotienting with a trivial relation amounts to the same thing as
-- using the propositional truncation (with erased proofs).

/ᴱtrivial↔∥∥ᴱ : @0 (∀ x y → R x y) → A /ᴱ R ↔ ∥ A ∥ᴱ
/ᴱtrivial↔∥∥ᴱ {A = A} {R = R} trivial = record
  { surjection = record
    { logical-equivalence = record
      { to = rec-prop λ where
          .[]ʳ             → ∣_∣
          .is-propositionʳ → PTᴱ.truncation-is-proposition
      ; from = PTᴱ.rec λ where
          .PTᴱ.∣∣ʳ                        → [_]
          .PTᴱ.truncation-is-propositionʳ → elim-prop λ @0 where
            .is-propositionʳ _ →
              Π-closure ext 1 λ _ →
              /ᴱ-is-set
            .[]ʳ x → elim-prop λ @0 where
              .is-propositionʳ _ → /ᴱ-is-set
              .[]ʳ y             → []-respects-relation (trivial x y)
      }
    ; right-inverse-of = PTᴱ.elim λ where
        .PTᴱ.∣∣ʳ _                        → refl _
        .PTᴱ.truncation-is-propositionʳ _ →
          ⇒≡ 1 PTᴱ.truncation-is-proposition
    }
  ; left-inverse-of = elim-prop λ where
      .[]ʳ _             → refl _
      .is-propositionʳ _ → /ᴱ-is-set
  }

------------------------------------------------------------------------
-- A property related to ∥_∥ᴱ, proved using _/ᴱ_

-- Having a constant function (with an erased proof of constancy) into
-- a set is equivalent to having a function from a propositionally
-- truncated type (with erased proofs) into the set.
--
-- The statement of this result is adapted from that of
-- Proposition 2.2 in "The General Universal Property of the
-- Propositional Truncation" by Kraus.

Σ→Erased-Constant≃∥∥ᴱ→ :
  @0 Is-set B →
  (∃ λ (f : A → B) → Erased (Constant f)) ≃ (∥ A ∥ᴱ → B)
Σ→Erased-Constant≃∥∥ᴱ→ {B = B} {A = A} B-set =
  (∃ λ (f : A → B) → Erased (Constant f))  ↔⟨ lemma ⟩
  (A /ᴱ (λ _ _ → ⊤) → B)                   ↝⟨ →-cong₁ ext (/ᴱtrivial↔∥∥ᴱ _) ⟩□
  (∥ A ∥ᴱ → B)                             □
  where
  lemma : _ ↔ _
  lemma = record
    { surjection = record
      { logical-equivalence = record
        { to   = λ (f , [ c ]) → rec λ where
                   .[]ʳ                     → f
                   .[]-respects-relationʳ _ → c _ _
                   .is-setʳ                 → B-set
        ; from = λ f → (f ∘ [_])
                     , [ (λ _ _ → cong f ([]-respects-relation _)) ]
        }
      ; right-inverse-of = λ f → ⟨ext⟩ $ elim λ where
          .[]ʳ _                   → refl _
          .[]-respects-relationʳ _ → B-set _ _
          .is-setʳ _               → mono₁ 2 B-set
      }
    ; left-inverse-of = λ _ → Σ-≡,≡→≡
        (refl _)
        (Er.H-level-Erased 1
           (Π-closure ext 1 λ _ →
            Π-closure ext 1 λ _ →
            B-set)
           _ _)
    }

-- The two directions of the proposition above compute in the
-- "right" way.

_ :
  (@0 B-set : Is-set B) →
  _≃_.to (Σ→Erased-Constant≃∥∥ᴱ→ B-set) f ∣ x ∣ ≡ proj₁ f x
_ = λ _ → refl _

_ :
  (@0 B-set : Is-set B) →
  proj₁ (_≃_.from (Σ→Erased-Constant≃∥∥ᴱ→ B-set) f) x ≡ f ∣ x ∣
_ = λ _ → refl _

------------------------------------------------------------------------
-- Various type formers commute with quotients

-- _⊎_ commutes with quotients.

⊎/ᴱ :
  {@0 R₁ : A₁ → A₁ → Type r} {@0 R₂ : A₂ → A₂ → Type r} →
  (A₁ ⊎ A₂) /ᴱ (R₁ ⊎ᴾ R₂) ≃ (A₁ /ᴱ R₁ ⊎ A₂ /ᴱ R₂)
⊎/ᴱ = Eq.↔→≃
  (rec λ where
     .[]ʳ → ⊎-map [_] [_]

     .is-setʳ → ⊎-closure 0 /ᴱ-is-set /ᴱ-is-set

     .[]-respects-relationʳ {x = inj₁ _} {y = inj₁ _} →
       cong inj₁ ∘ []-respects-relation
     .[]-respects-relationʳ {x = inj₂ _} {y = inj₂ _} →
       cong inj₂ ∘ []-respects-relation)
  Prelude.[ rec (λ where
              .[]ʳ x                 → [ inj₁ x ]
              .[]-respects-relationʳ → []-respects-relation
              .is-setʳ               → /ᴱ-is-set)
          , rec (λ where
              .[]ʳ x                 → [ inj₂ x ]
              .[]-respects-relationʳ → []-respects-relation
              .is-setʳ               → /ᴱ-is-set)
          ]
  Prelude.[ elim-prop (λ where
              .[]ʳ _             → refl _
              .is-propositionʳ _ → ⊎-closure 0 /ᴱ-is-set /ᴱ-is-set)
          , elim-prop (λ where
              .[]ʳ _             → refl _
              .is-propositionʳ _ → ⊎-closure 0 /ᴱ-is-set /ᴱ-is-set)
          ]
  (elim-prop λ where
     .[]ʳ               → Prelude.[ (λ _ → refl _) , (λ _ → refl _) ]
     .is-propositionʳ _ → /ᴱ-is-set)

-- Maybe commutes with quotients.
--
-- Chapman, Uustalu and Veltri mention a similar result in
-- "Quotienting the Delay Monad by Weak Bisimilarity".

Maybe/ᴱ :
  {@0 R : A → A → Type r} →
  Maybe A /ᴱ Maybeᴾ R ≃ Maybe (A /ᴱ R)
Maybe/ᴱ {A = A} {R = R} =
  Maybe A /ᴱ Maybeᴾ R    ↝⟨ ⊎/ᴱ ⟩
  ⊤ /ᴱ Trivial ⊎ A /ᴱ R  ↔⟨ /ᴱtrivial↔∥∥ᴱ _ ⊎-cong F.id ⟩
  ∥ ⊤ ∥ᴱ ⊎ A /ᴱ R        ↔⟨ PTᴱ.∥∥ᴱ↔ (mono₁ 0 ⊤-contractible) ⊎-cong F.id ⟩□
  Maybe (A /ᴱ R)         □

-- A simplification lemma for Maybe/-comm.

Maybe/ᴱ-[] :
  {@0 R : A → A → Type r} →
  _≃_.to (Maybe/ᴱ {R = R}) ∘ [_] ≡ ⊎-map id ([_] {R = R})
Maybe/ᴱ-[] = ⟨ext⟩ λ x →
  _≃_.to Maybe/ᴱ [ x ]                       ≡⟨⟩
  ⊎-map _ id (⊎-map _ id (⊎-map [_] [_] x))  ≡⟨ sym $ ⊎-map-∘ (⊎-map [_] [_] x) ⟩
  ⊎-map _ id (⊎-map [_] [_] x)               ≡⟨ sym $ ⊎-map-∘ x ⟩∎
  ⊎-map id [_] x                             ∎

-- Cartesian products commute with quotients, assuming that the two
-- binary relations involved in the statement are reflexive.

×/ᴱ :
  {@0 R₁ : A₁ → A₁ → Type r₁} {@0 R₂ : A₂ → A₂ → Type r₂} →
  @0 (∀ {x} → R₁ x x) →
  @0 (∀ {x} → R₂ x x) →
  (A₁ × A₂) /ᴱ (R₁ ×ᴾ R₂) ≃ (A₁ /ᴱ R₁ × A₂ /ᴱ R₂)
×/ᴱ {R₁ = R₁} {R₂ = R₂} R₁-refl R₂-refl = Eq.↔→≃
  (rec λ where
     .is-setʳ → ×-closure 2 /ᴱ-is-set /ᴱ-is-set

     .[]ʳ → Σ-map [_] [_]

     .[]-respects-relationʳ {x = x₁ , x₂} {y = y₁ , y₂} →
       R₁ x₁ y₁ × R₂ x₂ y₂                    ↝⟨ Σ-map []-respects-relation
                                                       []-respects-relation ⟩
       [ x₁ ] ≡ [ y₁ ] × [ x₂ ] ≡ [ y₂ ]      ↝⟨ uncurry (cong₂ _,_) ⟩□
       ([ x₁ ] , [ x₂ ]) ≡ ([ y₁ ] , [ y₂ ])  □)
  (uncurry $ rec λ where
     .is-setʳ →
       Π-closure ext 2 λ _ →
       /ᴱ-is-set
     .[]ʳ x → (x ,_) /ᴱ-map λ y₁ y₂ →
       R₂ y₁ y₂           ↝⟨ R₁-refl ,_ ⟩□
       R₁ x x × R₂ y₁ y₂  □
     .[]-respects-relationʳ {x = x₁} {y = x₂} R₁x₁x₂ →
       ⟨ext⟩ $ elim-prop λ @0 where
         .is-propositionʳ _ →
           /ᴱ-is-set
         .[]ʳ y →
           [ (x₁ , y) ]  ≡⟨ []-respects-relation (R₁x₁x₂ , R₂-refl) ⟩∎
           [ (x₂ , y) ]  ∎)
  (uncurry $ elim-prop λ where
     .is-propositionʳ _ →
       Π-closure ext 1 λ _ →
       ×-closure 2 /ᴱ-is-set /ᴱ-is-set
     .[]ʳ _ → elim-prop λ where
       .is-propositionʳ _ →
         ×-closure 2 /ᴱ-is-set /ᴱ-is-set
       .[]ʳ _ →
         refl _)
  (elim-prop λ where
     .is-propositionʳ _ → /ᴱ-is-set
     .[]ʳ _             → refl _)

-- The sigma type former commutes (kind of) with quotients, assuming
-- that the second projections come from propositional types.

Σ/ᴱ :
  @0 (∀ {x} → Is-proposition (P x)) →
  Σ (A /ᴱ R) P ≃ Σ A (P ∘ [_]) /ᴱ (R on proj₁)
Σ/ᴱ {A = A} {R = R} {P = P} prop = Eq.↔→≃
  (uncurry $ elim λ where
     .is-setʳ _ →
       Π-closure ext 2 λ _ →
       /ᴱ-is-set

     .[]ʳ → curry [_]

     .[]-respects-relationʳ {x = x} {y = y} r → ⟨ext⟩ λ P[y] →
       subst (λ x → P x → Σ A (P ∘ [_]) /ᴱ (R on proj₁))
             ([]-respects-relation r)
             (curry [_] x) P[y]                               ≡⟨ subst-→-domain P {f = curry [_] x} ([]-respects-relation r) ⟩

       [ (x , subst P (sym $ []-respects-relation r) P[y]) ]  ≡⟨ []-respects-relation r ⟩∎

       [ (y , P[y]) ]                                         ∎)
  (rec λ where
     .is-setʳ → Σ-closure 2 /ᴱ-is-set (λ _ → mono₁ 1 prop)

     .[]ʳ → Σ-map [_] id

     .[]-respects-relationʳ {x = (x₁ , x₂)} {y = (y₁ , y₂)} →
       R x₁ y₁                        ↝⟨ []-respects-relation ⟩
       [ x₁ ] ≡ [ y₁ ]                ↔⟨ ignore-propositional-component prop ⟩
       ([ x₁ ] , x₂) ≡ ([ y₁ ] , y₂)  □)
  (elim-prop λ where
     .[]ʳ _             → refl _
     .is-propositionʳ _ → /ᴱ-is-set)
  (uncurry $ elim-prop λ where
     .[]ʳ _ _           → refl _
     .is-propositionʳ _ →
       Π-closure ext 1 λ _ →
       Σ-closure 2 /ᴱ-is-set (λ _ → mono₁ 1 prop))

-- Erased commutes with quotients if certain conditions hold.

Erased/ᴱ :
  {@0 A : Type a} {@0 R : A → A → Type r} →
  @0 Is-set A →
  @0 (∀ {x y} → R x y → x ≡ y) →
  Erased A /ᴱ Erasedᴾ R ≃ Erased (A /ᴱ R)
Erased/ᴱ {A = A} {R = R} set R→≡ = Eq.↔→≃
  (rec λ where
     .is-setʳ                       → Er.H-level-Erased 2 /ᴱ-is-set
     .[]ʳ                           → Er.map [_]
     .[]-respects-relationʳ [ Rxy ] →
       Er.[]-cong [ []-respects-relation Rxy ])
  (λ ([ x ]) → [ [ from′ x ] ])
  (λ ([ x ]) →
     Er.[]-cong
       [ flip (elim-prop {P = λ x → [ from′ x ] ≡ x}) x (λ @0 where
           .is-propositionʳ _ → /ᴱ-is-set
           .[]ʳ _             → refl _)
       ])
  (elim-prop λ where
     .is-propositionʳ _ → /ᴱ-is-set
     .[]ʳ _             → refl _)
  where
  @0 from′ : A /ᴱ R → A
  from′ = rec λ @0 where
    .is-setʳ               → set
    .[]ʳ                   → id
    .[]-respects-relationʳ → R→≡


------------------------------------------------------------------------
-- The Agda standard library
--
-- Lemmas for use in proving the polynomial homomorphism.
------------------------------------------------------------------------

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

open import Tactic.RingSolver.Core.Polynomial.Parameters

module Tactic.RingSolver.Core.Polynomial.Homomorphism.Lemmas
  {r₁ r₂ r₃ r₄}
  (homo : Homomorphism r₁ r₂ r₃ r₄)
  where

open import Data.Bool                                  using (Bool;true;false)
open import Data.Nat.Base as ℕ                              using (ℕ; suc; zero; compare; _≤′_; ≤′-step; ≤′-refl)
open import Data.Nat.Properties as ℕₚ                  using (≤′-trans)
open import Data.Vec.Base as Vec                            using (Vec; _∷_)
open import Data.Fin                                   using (Fin; zero; suc)
open import Data.List                                  using (_∷_; [])
open import Data.Unit using (tt)
open import Data.List.Kleene
open import Data.Product                               using (_,_; proj₁; proj₂; map₁; _×_)
open import Data.Empty                                 using (⊥-elim)
open import Data.Maybe                                 using (nothing; just)
open import Function
open import Level                                      using (lift)
open import Relation.Nullary                           using (Dec; yes; no)
open import Relation.Binary.PropositionalEquality as ≡ using (_≡_)

open Homomorphism homo hiding (_^_)
open import Tactic.RingSolver.Core.Polynomial.Reasoning to
open import Tactic.RingSolver.Core.Polynomial.Base from
open import Tactic.RingSolver.Core.Polynomial.Semantics homo

open import Algebra.Operations.Ring rawRing

------------------------------------------------------------------------
-- Power lemmas
--
-- We prove some things about our odd exponentiation operator (described
-- in Algebra.Operations.Ring.Compact) here.

-- First, that the optimised operator is the same as normal
-- exponentiation.
pow-opt : ∀ x ρ i → x *⟨ ρ ⟩^ i ≈ ρ ^ i * x
pow-opt x ρ ℕ.zero = sym (*-identityˡ x)
pow-opt x ρ (suc i) = refl

-- Some normal identities on exponentiation.
--  xⁿxᵐ = xⁿ⁺ᵐ
pow-add′ : ∀ x i j → (x ^ i +1) * (x ^ j +1) ≈ x ^ (j ℕ.+ suc i) +1
pow-add′ x i ℕ.zero = refl
pow-add′ x i (suc j) =
  begin
    x ^ i +1 * (x ^ j +1 * x)
  ≈⟨ sym (*-assoc _ _ _) ⟩
    x ^ i +1 * x ^ j +1 * x
  ≈⟨ ≪* pow-add′ x i j ⟩
    x ^ (j ℕ.+ suc i) +1 * x
  ∎

pow-add : ∀ x y i j → y ^ j +1 * x *⟨ y ⟩^ i  ≈ x *⟨ y ⟩^ (i ℕ.+ suc j)
pow-add x y ℕ.zero j = refl
pow-add x y (suc i) j = go x y i j
  where
  go : ∀ x y i j → y ^ j +1 * ((y ^ i +1) * x) ≈ y ^ (i ℕ.+ suc j) +1 * x
  go x y ℕ.zero j =  sym (*-assoc _ _ _)
  go x y (suc i) j =
    begin
      y ^ j +1 * (y ^ i +1 * y * x)
    ≈⟨ *≫ *-assoc _ y x ⟩
      y ^ j +1 * (y ^ i +1 * (y * x))
    ≈⟨ go (y * x) y i j ⟩
      y ^ (i ℕ.+ suc j) +1 * (y * x)
    ≈⟨ sym (*-assoc _ y x) ⟩
      y ^ suc (i ℕ.+ suc j) +1 * x
    ∎

-- Here we show a homomorphism on exponentiation, i.e. that using the
-- exponentiation function on polynomials and then evaluating is the same
-- as evaluating and then exponentiating.
pow-hom : ∀ {n} i
        → (xs : Coeff n +)
        → ∀ ρ ρs
        → ⅀⟦ xs ⟧ (ρ , ρs) *⟨ ρ ⟩^ i ≈ ⅀⟦ xs ⍓+ i ⟧ (ρ , ρs)
pow-hom ℕ.zero (x Δ j & xs) ρ ρs rewrite ℕₚ.+-identityʳ j = refl
pow-hom (suc i) (x ≠0 Δ j & xs) ρ ρs =
  begin
    ρ ^ i +1 * (((x , xs) ⟦∷⟧ (ρ , ρs)) *⟨ ρ ⟩^ j)
  ≈⟨ pow-add _ ρ j i ⟩
    (((x , xs) ⟦∷⟧ (ρ , ρs)) *⟨ ρ ⟩^ (j ℕ.+ suc i))
  ∎

-- Proving a congruence (we don't get this for free because we're using
-- setoids).
pow-mul-cong : ∀ {x y} → x ≈ y → ∀ ρ i → x *⟨ ρ ⟩^ i ≈ y *⟨ ρ ⟩^ i
pow-mul-cong x≈y ρ ℕ.zero = x≈y
pow-mul-cong x≈y ρ (suc i₁) = *≫ x≈y

-- The identity:
--  (xy)ⁿ = xⁿyⁿ
pow-distrib-+1 : ∀ x y i → (x * y) ^ i +1 ≈ x ^ i +1 * y ^ i +1
pow-distrib-+1 x y ℕ.zero = refl
pow-distrib-+1 x y (suc i) =
  begin
    (x * y) ^ i +1 * (x * y)
  ≈⟨ ≪* pow-distrib-+1 x y i ⟩
    (x ^ i +1 * y ^ i +1) * (x * y)
  ≈⟨ *-assoc _ _ _ ⟨ trans ⟩ (*≫ (sym (*-assoc _ _ _) ⟨ trans ⟩ (≪* *-comm _ _))) ⟩
    x ^ i +1 * (x * y ^ i +1 * y)
  ≈⟨ (*≫ *-assoc _ _ _) ⟨ trans ⟩ sym (*-assoc _ _ _) ⟩
    (x ^ i +1 * x) * (y ^ i +1 * y)
  ∎

pow-distrib : ∀ x y i → (x * y) ^ i ≈ x ^ i * y ^ i
pow-distrib x y ℕ.zero = sym (*-identityˡ _)
pow-distrib x y (suc i) = pow-distrib-+1 x y i

-- This proves the identity:
--   (xⁿ)ᵐ = xⁿᵐ
-- The reason it looks different is because we're using the special exponentiation
-- operator.
pow-mult-+1 : ∀ x i j → (x ^ i +1) ^ j +1 ≈ x ^ (i ℕ.+ j ℕ.* suc i) +1
pow-mult-+1 x i ℕ.zero rewrite ℕₚ.+-identityʳ i = refl
pow-mult-+1 x i (suc j) =
  begin
    (x ^ i +1) ^ j +1 * (x ^ i +1)
  ≈⟨ ≪* pow-mult-+1 x i j ⟩
    (x ^ (i ℕ.+ j ℕ.* suc i) +1) * (x ^ i +1)
  ≈⟨ pow-add′ x _ i ⟩
    x ^ (i ℕ.+ suc (i ℕ.+ j ℕ.* suc i)) +1
  ∎

pow-cong-+1 : ∀ {x y} i → x ≈ y → x ^ i +1 ≈ y ^ i +1
pow-cong-+1 ℕ.zero x≈y = x≈y
pow-cong-+1 (suc i) x≈y = pow-cong-+1 i x≈y ⟨ *-cong ⟩ x≈y

pow-cong : ∀ {x y} i → x ≈ y → x ^ i ≈ y ^ i
pow-cong ℕ.zero x≈y = refl
pow-cong (suc i) x≈y = pow-cong-+1 i x≈y

-- Demonstrating that the proof of zeroness is correct.
zero-hom : ∀ {n} (p : Poly n) → Zero p → (ρs : Vec Carrier n) → 0# ≈ ⟦ p ⟧ ρs
zero-hom (Κ x  ⊐ i≤n) p≡0 ρs = Zero-C⟶Zero-R x p≡0

--   x¹⁺ⁿ = xxⁿ
pow-suc : ∀ x i → x ^ suc i ≈ x * x ^ i
pow-suc x ℕ.zero  = sym (*-identityʳ _)
pow-suc x (suc i) = *-comm _ _

--   x¹⁺ⁿ = xⁿx
pow-sucʳ : ∀ x i → x ^ suc i ≈ x ^ i * x
pow-sucʳ x ℕ.zero  = sym (*-identityˡ _)
pow-sucʳ x (suc i) = refl

-- In the proper evaluation function, we avoid ever inserting an unnecessary 0#
-- like we do here. However, it is easier to prove with the form that does insert
-- 0#. So we write one here, and then prove that it's equivalent to the one that
-- adds a 0#.
⅀?⟦_⟧ : ∀ {n} (xs : Coeff n *) → Carrier × Vec Carrier n → Carrier
⅀?⟦ []  ⟧ _ = 0#
⅀?⟦ ∹ x ⟧   = ⅀⟦ x ⟧

_⟦∷⟧?_ : ∀ {n} (x : Poly n × Coeff n *) → Carrier × Vec Carrier n → Carrier
(x , xs) ⟦∷⟧? (ρ , ρs) = ρ * ⅀?⟦ xs ⟧ (ρ , ρs) + ⟦ x ⟧ ρs

⅀?-hom : ∀ {n} (xs : Coeff n +) → ∀ ρ → ⅀?⟦ ∹ xs ⟧ ρ ≈ ⅀⟦ xs ⟧ ρ
⅀?-hom _ _ = refl

⟦∷⟧?-hom : ∀ {n} (x : Poly n) → ∀ xs ρ ρs → (x , xs) ⟦∷⟧? (ρ , ρs) ≈ (x , xs) ⟦∷⟧ (ρ , ρs)
⟦∷⟧?-hom x (∹ xs ) ρ ρs = refl
⟦∷⟧?-hom x [] ρ ρs =  (≪+ zeroʳ _) ⟨ trans ⟩ +-identityˡ _

pow′-hom : ∀ {n} i (xs : Coeff n *) → ∀ ρ ρs → ((⅀?⟦ xs ⟧ (ρ , ρs)) *⟨ ρ ⟩^ i) ≈ (⅀?⟦ xs ⍓* i ⟧ (ρ , ρs))
pow′-hom i (∹ xs ) ρ ρs = pow-hom i xs ρ ρs
pow′-hom zero [] ρ ρs = refl
pow′-hom (suc i) [] ρ ρs = zeroʳ _

-- Here, we show that the normalising cons is correct.
-- This lets us prove with respect to the non-normalising form, especially
-- when we're using the folds.
∷↓-hom-0 : ∀ {n} (x : Poly n) → ∀ xs ρ ρs → ⅀?⟦ x Δ 0 ∷↓ xs ⟧ (ρ , ρs) ≈ (x , xs) ⟦∷⟧ (ρ , ρs)
∷↓-hom-0 x xs      ρ ρs with zero? x
∷↓-hom-0 x xs      ρ ρs | no ¬p = refl
∷↓-hom-0 x []      ρ ρs | yes p = zero-hom x p ρs
∷↓-hom-0 x (∹ xs ) ρ ρs | yes p =
  begin
    ⅀⟦ xs ⍓+ 1 ⟧ (ρ , ρs)
  ≈⟨ sym (pow-hom 1 xs ρ ρs) ⟩
    ρ * ⅀⟦ xs ⟧ (ρ , ρs)
  ≈⟨ sym (+-identityʳ _) ⟨ trans ⟩ (+≫ zero-hom x p ρs) ⟩
    ρ * ⅀⟦ xs ⟧ (ρ , ρs) + ⟦ x ⟧ ρs
  ∎

∷↓-hom-s : ∀ {n} (x : Poly n) → ∀ i xs ρ ρs → ⅀?⟦ x Δ suc i ∷↓ xs ⟧ (ρ , ρs) ≈ (ρ ^ i +1) * (x , xs) ⟦∷⟧ (ρ , ρs)
∷↓-hom-s x i xs ρ ρs with zero? x
∷↓-hom-s x i xs ρ ρs | no ¬p = refl
∷↓-hom-s x i [] ρ ρs | yes p = sym ((*≫ sym (zero-hom x p ρs)) ⟨ trans ⟩ zeroʳ _)
∷↓-hom-s x i (∹ xs ) ρ ρs | yes p =
  begin
    ⅀⟦ xs ⍓+ (suc (suc i)) ⟧ (ρ , ρs)
  ≈⟨ sym (pow-hom (suc (suc i)) xs ρ ρs) ⟩
    (ρ ^ suc i +1) * ⅀⟦ xs ⟧ (ρ , ρs)
  ≈⟨ *-assoc _ _ _ ⟩
    (ρ ^ i +1) * (ρ * ⅀⟦ xs ⟧ (ρ , ρs))
  ≈⟨ *≫ (sym (+-identityʳ _) ⟨ trans ⟩ (+≫ zero-hom x p ρs)) ⟩
    ρ ^ i +1 * (ρ * ⅀⟦ xs ⟧ (ρ , ρs) + ⟦ x ⟧ ρs)
  ∎

∷↓-hom : ∀ {n}
       → (x : Poly n)
       → ∀ i xs ρ ρs
       → ⅀?⟦ x Δ i ∷↓ xs ⟧ (ρ , ρs) ≈ ρ ^ i * ((x , xs) ⟦∷⟧ (ρ , ρs))
∷↓-hom x ℕ.zero  xs ρ ρs = ∷↓-hom-0 x xs ρ ρs ⟨ trans ⟩ sym (*-identityˡ _)
∷↓-hom x (suc i) xs ρ ρs = ∷↓-hom-s x i xs ρ ρs

⟦∷⟧-hom : ∀ {n}
       → (x : Poly n)
       → (xs : Coeff n *)
       → ∀ ρ ρs → (x , xs) ⟦∷⟧ (ρ , ρs) ≈ ρ * ⅀?⟦ xs ⟧ (ρ , ρs) + ⟦ x ⟧ ρs
⟦∷⟧-hom x []     ρ ρs = sym ((≪+ zeroʳ _) ⟨ trans ⟩ +-identityˡ _)
⟦∷⟧-hom x (∹ xs) ρ ρs = refl

-- This proves that injecting a polynomial into more variables is correct.
-- Basically, we show that if a polynomial doesn't care about the first few
-- variables, we can drop them from the input vector.
⅀-⊐↑-hom : ∀ {i n m}
         → (xs : Coeff i +)
         → (si≤n : suc i ≤′ n)
         → (sn≤m : suc n ≤′ m)
         → ∀ ρ
         → ⅀⟦ xs ⟧ (drop-1 (≤′-step si≤n ⟨ ≤′-trans ⟩ sn≤m) ρ)
         ≈ ⅀⟦ xs ⟧ (drop-1 si≤n (proj₂ (drop-1 sn≤m ρ)))
⅀-⊐↑-hom xs si≤n ≤′-refl        (_ ∷ _) = refl
⅀-⊐↑-hom xs si≤n (≤′-step sn≤m) (_ ∷ ρ) = ⅀-⊐↑-hom xs si≤n sn≤m ρ

⊐↑-hom : ∀ {n m}
       → (x : Poly n)
       → (sn≤m : suc n ≤′ m)
       → ∀ ρ
       → ⟦ x ⊐↑ sn≤m ⟧ ρ ≈ ⟦ x ⟧ (proj₂ (drop-1 sn≤m ρ))
⊐↑-hom (Κ x  ⊐ i≤sn) _ _ = refl
⊐↑-hom (⅀ xs ⊐ i≤sn) = ⅀-⊐↑-hom xs i≤sn

trans-join-coeffs-hom : ∀ {i j-1 n}
                      → (i≤j-1 : suc i ≤′ j-1)
                      → (j≤n   : suc j-1 ≤′ n)
                      → (xs : Coeff i +)
                      → ∀ ρ
                      → ⅀⟦ xs ⟧ (drop-1 i≤j-1 (proj₂ (drop-1 j≤n ρ))) ≈ ⅀⟦ xs ⟧ (drop-1 (≤′-step i≤j-1 ⟨ ≤′-trans ⟩ j≤n) ρ)
trans-join-coeffs-hom i<j-1 ≤′-refl       xs (_ ∷ _) = refl
trans-join-coeffs-hom i<j-1 (≤′-step j<n) xs (_ ∷ ρ) = trans-join-coeffs-hom i<j-1 j<n xs ρ

trans-join-hom : ∀ {i j-1 n}
      → (i≤j-1 : i ≤′ j-1)
      → (j≤n   : suc j-1 ≤′ n)
      → (x : FlatPoly i)
      → ∀ ρ
      → ⟦ x ⊐ i≤j-1 ⟧ (proj₂ (drop-1 j≤n ρ)) ≈ ⟦ x ⊐ (≤′-step i≤j-1 ⟨ ≤′-trans ⟩ j≤n) ⟧ ρ
trans-join-hom i≤j-1 j≤n (Κ x) _ = refl
trans-join-hom i≤j-1 j≤n (⅀ x) = trans-join-coeffs-hom i≤j-1 j≤n x

⊐↓-hom : ∀ {n m}
       → (xs : Coeff n *)
       → (sn≤m : suc n ≤′ m)
       → ∀ ρ
       → ⟦ xs ⊐↓ sn≤m ⟧ ρ ≈ ⅀?⟦ xs ⟧ (drop-1 sn≤m ρ)
⊐↓-hom []                       sn≤m _ = 0-homo
⊐↓-hom (∹ x₁   Δ zero  & ∹ xs) sn≤m _ = refl
⊐↓-hom (∹ x    Δ suc j & xs )      sn≤m _ = refl
⊐↓-hom (∹ _≠0 x {x≠0} Δ zero  & []) sn≤m ρs =
  let (ρ , ρs′) = drop-1 sn≤m ρs
  in
  begin
    ⟦ x ⊐↑ sn≤m ⟧ ρs
  ≈⟨ ⊐↑-hom x sn≤m ρs ⟩
    ⟦ x ⟧ ρs′
  ∎

drop-1⇒lookup : ∀ {n} (i : Fin n) (ρs : Vec Carrier n) →
                proj₁ (drop-1 (space≤′n i) ρs) ≡ Vec.lookup ρs i
drop-1⇒lookup zero    (ρ ∷ ρs) = ≡.refl
drop-1⇒lookup (suc i) (ρ ∷ ρs) = drop-1⇒lookup i ρs

-- The fold: this function saves us hundreds of lines of proofs in the rest of the
-- homomorphism proof.
-- Many of the functions on polynomials are defined using para: this function allows
-- us to prove properties of those functions (in a foldr-fusion style) *ignoring*
-- optimisations we have made to the polynomial structure.
poly-foldR : ∀ {n} ρ ρs
        → ([f] : Fold n)
        → (f : Carrier → Carrier)
        → (∀ {x y} → x ≈ y → f x ≈ f y)
        → (∀ x y → x * f y ≈ f (x * y))
        → (∀ y {ys} zs → ⅀?⟦ ys ⟧ (ρ , ρs) ≈ f (⅀?⟦ zs ⟧ (ρ , ρs)) → [f] (y , ys) ⟦∷⟧? (ρ , ρs) ≈ f ((y , zs) ⟦∷⟧? (ρ , ρs)) )
        → (f 0# ≈ 0#)
        → ∀ xs
        → ⅀?⟦ para [f] xs ⟧ (ρ , ρs) ≈ f (⅀⟦ xs ⟧ (ρ , ρs))
poly-foldR ρ ρs f e cng dist step base (x ≠0 Δ suc i & []) =
  let y,ys = f (x , [])
      y = proj₁ y,ys
      ys = proj₂ y,ys
  in
  begin
    ⅀?⟦ y Δ suc i ∷↓ ys ⟧ (ρ , ρs)
  ≈⟨ ∷↓-hom-s y i ys ρ ρs ⟩
    (ρ ^ i +1) * ((y , ys) ⟦∷⟧ (ρ , ρs))
  ≈˘⟨ *≫ ⟦∷⟧?-hom y ys ρ ρs ⟩
    (ρ ^ i +1) * ((y , ys) ⟦∷⟧? (ρ , ρs))
  ≈⟨ *≫ step x [] (sym base) ⟩
    (ρ ^ i +1) * e ((x , []) ⟦∷⟧? (ρ , ρs))
  ≈⟨ *≫ cng (⟦∷⟧?-hom x [] ρ ρs) ⟩
    (ρ ^ i +1) * e ((x , []) ⟦∷⟧ (ρ , ρs))
  ≈⟨ dist _ _   ⟩
    e (ρ ^ i +1 * ((x , []) ⟦∷⟧ (ρ , ρs)))
  ∎
poly-foldR ρ ρs f e cng dist step base (x ≠0 Δ suc i & ∹ xs) =
  let ys = para f xs
      y,zs = f (x , ys)
      y = proj₁ y,zs
      zs = proj₂ y,zs
  in
  begin
    ⅀?⟦ y Δ suc i ∷↓ zs ⟧ (ρ , ρs)
  ≈⟨ ∷↓-hom-s y i zs ρ ρs ⟩
    (ρ ^ i +1) * ((y , zs) ⟦∷⟧ (ρ , ρs))
  ≈˘⟨ *≫ ⟦∷⟧?-hom y zs ρ ρs ⟩
    (ρ ^ i +1) * ((y , zs) ⟦∷⟧? (ρ , ρs))
  ≈⟨ *≫ step x (∹ xs) (poly-foldR ρ ρs f e cng dist step base xs) ⟩
    (ρ ^ i +1) * e ((x , (∹ xs )) ⟦∷⟧? (ρ , ρs))
  ≈⟨ *≫ cng (⟦∷⟧?-hom x (∹ xs ) ρ ρs) ⟩
    (ρ ^ i +1) * e ((x , (∹ xs )) ⟦∷⟧ (ρ , ρs))
  ≈⟨ dist _ _   ⟩
    e (ρ ^ i +1 * ((x , (∹ xs )) ⟦∷⟧ (ρ , ρs)))
  ∎
poly-foldR ρ ρs f e cng dist step base (x ≠0 Δ ℕ.zero & []) =
  let y,zs = f (x , [])
      y = proj₁ y,zs
      zs = proj₂ y,zs
  in
  begin
    ⅀?⟦ y Δ ℕ.zero ∷↓ zs ⟧ (ρ , ρs)
  ≈⟨ ∷↓-hom-0 y zs ρ ρs ⟩
    ((y , zs) ⟦∷⟧ (ρ , ρs))
  ≈˘⟨ ⟦∷⟧?-hom y zs ρ ρs ⟩
    ((y , zs) ⟦∷⟧? (ρ , ρs))
  ≈⟨ step x [] (sym base) ⟩
    e ((x , []) ⟦∷⟧? (ρ , ρs))
  ≈⟨ cng (⟦∷⟧?-hom x [] ρ ρs) ⟩
    e ((x , []) ⟦∷⟧ (ρ , ρs))
  ∎
poly-foldR ρ ρs f e cng dist step base (x ≠0 Δ ℕ.zero & (∹ xs )) =
  let ys = para f xs
      y,zs = f (x , ys)
      y = proj₁ y,zs
      zs = proj₂ y,zs
  in
  begin
    ⅀?⟦ y Δ ℕ.zero ∷↓ zs ⟧ (ρ , ρs)
  ≈⟨ ∷↓-hom-0 y zs ρ ρs ⟩
    ((y , zs) ⟦∷⟧ (ρ , ρs))
  ≈˘⟨ ⟦∷⟧?-hom y zs ρ ρs ⟩
    ((y , zs) ⟦∷⟧? (ρ , ρs))
  ≈⟨ step x (∹ xs ) (poly-foldR ρ ρs f e cng dist step base xs) ⟩
    e ((x , (∹ xs )) ⟦∷⟧ (ρ , ρs))
  ≈⟨ cng (⟦∷⟧?-hom x (∹ xs ) ρ ρs) ⟩
    e ((x , (∹ xs )) ⟦∷⟧ (ρ , ρs))
  ∎

poly-mapR : ∀ {n} ρ ρs
          → ([f] : Poly n → Poly n)
          → (f : Carrier → Carrier)
          → (∀ {x y} → x ≈ y → f x ≈ f y)
          → (∀ x y → x * f y ≈ f (x * y))
          → (∀ x y → f (x + y) ≈ f x + f y)
          → (∀ y → ⟦ [f] y ⟧ ρs ≈ f (⟦ y ⟧ ρs) )
          → (f 0# ≈ 0#)
          → ∀ xs
          → ⅀?⟦ poly-map [f] xs ⟧ (ρ , ρs) ≈ f (⅀⟦ xs ⟧ (ρ , ρs))
poly-mapR ρ ρs [f] f cng *-dist +-dist step′ base xs = poly-foldR ρ ρs (map₁ [f]) f cng *-dist step base xs
  where
  step : ∀ y {ys} zs → ⅀?⟦ ys ⟧ (ρ , ρs) ≈ f (⅀?⟦ zs ⟧ (ρ , ρs)) →(map₁ [f] (y , ys) ⟦∷⟧? (ρ , ρs)) ≈ f ((y , zs) ⟦∷⟧? (ρ , ρs))
  step y {ys} zs ys≋zs =
    begin
      map₁ [f] (y , ys) ⟦∷⟧? (ρ , ρs)
    ≡⟨⟩
      ρ * ⅀?⟦ ys ⟧ (ρ , ρs) + ⟦ [f] y ⟧ ρs
    ≈⟨ ((*≫ ys≋zs) ⟨ trans ⟩ *-dist ρ _) ⟨ +-cong ⟩ step′ y ⟩
      f (ρ * ⅀?⟦ zs ⟧ (ρ , ρs)) + f (⟦ y ⟧ ρs)
    ≈⟨ sym (+-dist _ _) ⟩
      f ((y , zs) ⟦∷⟧? (ρ , ρs))
    ∎


module OlderBasicILP.Direct.Gentzen where

open import Common.Context public

-- Propositions of intuitionistic logic of proofs, without ∨, ⊥, or +.

mutual
  infixr 10 _⦂_
  infixl 9 _∧_
  infixr 7 _▻_
  data Ty : Set where
    α_  : Atom → Ty
    _▻_ : Ty → Ty → Ty
    _⦂_ : Box → Ty → Ty
    _∧_ : Ty → Ty → Ty
    ⊤  : Ty

  record Box : Set where
    inductive
    constructor [_]
    field
      {/A} : Ty
      t    : ∅ ⊢ /A

  _⦂⋆_ : ∀ {n} → VCx Box n → VCx Ty n → Cx Ty
  ∅        ⦂⋆ ∅       = ∅
  (ts , t) ⦂⋆ (Ξ , A) = (ts ⦂⋆ Ξ) , (t ⦂ A)

  -- Derivations, as Gentzen-style natural deduction trees.

  infix 3 _⊢_
  data _⊢_ (Γ : Cx Ty) : Ty → Set where
    var      : ∀ {A}              → A ∈ Γ → Γ ⊢ A
    lam      : ∀ {A B}            → Γ , A ⊢ B → Γ ⊢ A ▻ B
    app      : ∀ {A B}            → Γ ⊢ A ▻ B → Γ ⊢ A → Γ ⊢ B
--    multibox : ∀ {n A} {[ss] : VCx Box n} {Ξ : VCx Ty n}
--               → Γ ⊢⋆ [ss] ⦂⋆ Ξ → (u : [ss] ⦂⋆ Ξ ⊢ A)
--               → Γ ⊢ [ u ] ⦂ A
    down     : ∀ {A} {t : ∅ ⊢ A} → Γ ⊢ [ t ] ⦂ A → Γ ⊢ A
    pair     : ∀ {A B}            → Γ ⊢ A → Γ ⊢ B → Γ ⊢ A ∧ B
    fst      : ∀ {A B}            → Γ ⊢ A ∧ B → Γ ⊢ A
    snd      : ∀ {A B}            → Γ ⊢ A ∧ B → Γ ⊢ B
    unit     : Γ ⊢ ⊤

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

infix 7 _▻◅_
_▻◅_ : Ty → Ty → Ty
A ▻◅ B = (A ▻ B) ∧ (B ▻ A)

-- Monotonicity with respect to context inclusion.

mutual
  mono⊢ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢ A → Γ′ ⊢ A
  mono⊢ η (var i)         = var (mono∈ η i)
  mono⊢ η (lam t)         = lam (mono⊢ (keep η) t)
  mono⊢ η (app t u)       = app (mono⊢ η t) (mono⊢ η u)
--  mono⊢ η (multibox ts u) = multibox (mono⊢⋆ η ts) u
  mono⊢ η (down t)        = down (mono⊢ η t)
  mono⊢ η (pair t u)      = pair (mono⊢ η t) (mono⊢ η u)
  mono⊢ η (fst t)         = fst (mono⊢ η t)
  mono⊢ η (snd t)         = snd (mono⊢ η t)
  mono⊢ η unit            = unit

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

-- Shorthand for variables.

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

v₁ : ∀ {A B Γ} → (Γ , A) , B ⊢ A
v₁ = var i₁

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

-- Reflexivity.

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

-- Deduction theorem is built-in.

-- Detachment theorem.

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

-- Cut and multicut.

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

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

-- Transitivity.

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

-- Contraction.

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

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

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

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

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

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

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

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

-- Useful theorems in functional form.

-- dist : ∀ {A B Ψ Γ} {t : Ψ ⊢ A ▻ B} {u : Ψ ⊢ A}
--        → Γ ⊢ [ t ] ⦂ (A ▻ B) → Γ ⊢ [ u ] ⦂ A
--        → Γ ⊢ [ app (down v₁) (down v₀) ] ⦂ B
-- dist t u = multibox ((∙ , t) , u) (app (down v₁) (down v₀))
--
-- up : ∀ {A Ψ Γ} {t : Ψ ⊢ A}
--      → Γ ⊢ [ t ] ⦂ A
--      → Γ ⊢ [ v₀ ] ⦂ [ t ] ⦂ A
-- up t = multibox (∙ , t) v₀
--
-- distup : ∀ {A B Ψ Γ} {u : Ψ ⊢ A} {t : ∅ , [ u ] ⦂ A ⊢ [ u ] ⦂ A ▻ B}
--          → Γ ⊢ [ t ] ⦂ ([ u ] ⦂ A ▻ B) → Γ ⊢ [ u ] ⦂ A
--          → Γ ⊢ [ app (down v₁) (down v₀) ] ⦂ B
-- distup t u = dist t (up u)
--
-- box : ∀ {A Γ}
--       → (t : ∅ ⊢ A)
--       → Γ ⊢ [ t ] ⦂ A
-- box t = multibox ∙ t

unbox : ∀ {A C Γ} {t : ∅ ⊢ A} {u : ∅ ⊢ C}
        → Γ ⊢ [ t ] ⦂ A → Γ , [ t ] ⦂ A ⊢ [ u ] ⦂ C
        → Γ ⊢ [ u ] ⦂ C
unbox t u = app (lam u) t

-- Useful theorems in combinatory form.

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

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

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

-- cdist : ∀ {A B Ψ Γ} {t : Ψ ⊢ A ▻ B} {u : Ψ ⊢ A}
--         → Γ ⊢ [ t ] ⦂ (A ▻ B) ▻ [ u ] ⦂ A ▻ [ app (down v₁) (down v₀) ] ⦂ B
-- cdist = lam (lam (dist v₁ v₀))
--
-- cup : ∀ {A Ψ Γ} {t : Ψ ⊢ A} → Γ ⊢ [ t ] ⦂ A ▻ [ v₀ ] ⦂ [ t ] ⦂ A
-- cup = lam (up v₀)

cdown : ∀ {A Γ} {t : ∅ ⊢ A} → Γ ⊢ [ t ] ⦂ A ▻ A
cdown = lam (down v₀)

-- cdistup : ∀ {A B Ψ Γ} {u : Ψ ⊢ A} {t : ∅ , [ u ] ⦂ A ⊢ [ u ] ⦂ A ▻ B}
--           → Γ ⊢ [ t ] ⦂ ([ u ] ⦂ A ▻ B) ▻ [ u ] ⦂ A ▻ [ app (down v₁) (down v₀) ] ⦂ B
-- cdistup = lam (lam (dist v₁ (up v₀)))

cunbox : ∀ {A C Γ} {t : ∅ ⊢ A} {u : ∅ ⊢ C}
         → Γ ⊢ [ t ] ⦂ A ▻ ([ t ] ⦂ A ▻ C) ▻ C
cunbox = lam (lam (app v₀ v₁))

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

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

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

-- Closure under context concatenation.

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

-- Substitution.

mutual
  [_≔_]_ : ∀ {A B Γ} → (i : A ∈ Γ) → Γ ∖ i ⊢ A → Γ ⊢ B → Γ ∖ i ⊢ B
  [ i ≔ s ] var j         with i ≟∈ j
  [ i ≔ s ] var .i        | same   = s
  [ i ≔ s ] var ._        | diff j = var j
  [ i ≔ s ] lam t         = lam ([ pop i ≔ mono⊢ weak⊆ s ] t)
  [ i ≔ s ] app t u       = app ([ i ≔ s ] t) ([ i ≔ s ] u)
--  [ i ≔ s ] multibox ts u = multibox ([ i ≔ s ]⋆ ts) u
  [ i ≔ s ] down t        = down ([ i ≔ s ] t)
  [ i ≔ s ] pair t u      = pair ([ i ≔ s ] t) ([ i ≔ s ] u)
  [ i ≔ s ] fst t         = fst ([ i ≔ s ] t)
  [ i ≔ s ] snd t         = snd ([ i ≔ s ] t)
  [ i ≔ s ] unit          = unit

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