-- Basic intuitionistic modal logic S4, without ∨, ⊥, or ◇.
-- Tarski-style semantics with contexts as concrete worlds, and glueing for α, ▻, and □.
-- Gentzen-style syntax.

module BasicIS4.Semantics.TarskiOvergluedGentzen where

open import BasicIS4.Syntax.Common public
open import Common.Semantics public

-- Intuitionistic Tarski models.

record Model : Set₁ where
  infix 3 _⊩ᵅ_ _[⊢]_ _[⊢⋆]_
  field
    -- Forcing for atomic propositions; monotonic.
    _⊩ᵅ_   : Cx Ty → Atom → Set
    mono⊩ᵅ : ∀ {P Γ Γ′} → Γ ⊆ Γ′ → Γ ⊩ᵅ P → Γ′ ⊩ᵅ P

    -- Gentzen-style syntax representation; monotonic.
    _[⊢]_     : Cx Ty → Ty → Set
    _[⊢⋆]_    : Cx Ty → Cx Ty → Set
    mono[⊢]   : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ [⊢] A → Γ′ [⊢] A
    [var]      : ∀ {A Γ}    → A ∈ Γ → Γ [⊢] A
    [lam]      : ∀ {A B Γ}  → Γ , A [⊢] B → Γ [⊢] A ▻ B
    [app]      : ∀ {A B Γ}  → Γ [⊢] A ▻ B → Γ [⊢] A → Γ [⊢] B
    [multibox] : ∀ {A Δ Γ}  → Γ [⊢⋆] □⋆ Δ → □⋆ Δ [⊢] A → Γ [⊢] □ A
    [down]     : ∀ {A Γ}    → Γ [⊢] □ A → Γ [⊢] A
    [pair]     : ∀ {A B Γ}  → Γ [⊢] A → Γ [⊢] B → Γ [⊢] A ∧ B
    [fst]      : ∀ {A B Γ}  → Γ [⊢] A ∧ B → Γ [⊢] A
    [snd]      : ∀ {A B Γ}  → Γ [⊢] A ∧ B → Γ [⊢] B
    [unit]     : ∀ {Γ}      → Γ [⊢] ⊤

    -- TODO: Workarounds for Agda bug #2143.
    top[⊢⋆] : ∀ {Γ}     → (Γ [⊢⋆] ∅) ≡ 𝟙
    pop[⊢⋆] : ∀ {Ξ A Γ} → (Γ [⊢⋆] Ξ , A) ≡ (Γ [⊢⋆] Ξ × Γ [⊢] A)

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

  [⊢⋆]→[⊢]⋆ : ∀ {Ξ Γ} → Γ [⊢⋆] Ξ → Γ [⊢]⋆ Ξ
  [⊢⋆]→[⊢]⋆ {∅}     {Γ} ts = ∙
  [⊢⋆]→[⊢]⋆ {Ξ , A} {Γ} ts rewrite pop[⊢⋆] {Ξ} {A} {Γ} = [⊢⋆]→[⊢]⋆ (π₁ ts) , π₂ ts

  [⊢]⋆→[⊢⋆] : ∀ {Ξ Γ} → Γ [⊢]⋆ Ξ → Γ [⊢⋆] Ξ
  [⊢]⋆→[⊢⋆] {∅}     {Γ} ∙        rewrite top[⊢⋆] {Γ}         = ∙
  [⊢]⋆→[⊢⋆] {Ξ , A} {Γ} (ts , t) rewrite pop[⊢⋆] {Ξ} {A} {Γ} = [⊢]⋆→[⊢⋆] ts , t

open Model {{…}} public

-- Forcing in a particular model.

module _ {{_ : Model}} where
  infix 3 _⊩_
  _⊩_ : Cx Ty → Ty → Set
  Γ ⊩ α P   = Glue (Γ [⊢] α P) (Γ ⊩ᵅ P)
  Γ ⊩ A ▻ B = ∀ {Γ′} → Γ ⊆ Γ′ → Glue (Γ′ [⊢] A ▻ B) (Γ′ ⊩ A → Γ′ ⊩ B)
  Γ ⊩ □ A   = ∀ {Γ′} → Γ ⊆ Γ′ → Glue (Γ′ [⊢] □ A) (Γ′ ⊩ A)
  Γ ⊩ A ∧ B = Γ ⊩ A × Γ ⊩ B
  Γ ⊩ ⊤    = 𝟙

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

-- Monotonicity with respect to context inclusion.

module _ {{_ : Model}} where
  mono⊩ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊩ A → Γ′ ⊩ A
  mono⊩ {α P}   η s = mono[⊢] η (syn s) ⅋ mono⊩ᵅ η (sem s)
  mono⊩ {A ▻ B} η s = λ η′ → s (trans⊆ η η′)
  mono⊩ {□ A}   η s = λ η′ → s (trans⊆ η η′)
  mono⊩ {A ∧ B} η s = mono⊩ {A} η (π₁ s) , mono⊩ {B} η (π₂ s)
  mono⊩ {⊤}    η s = ∙

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

-- Extraction of syntax representation in a particular model.

module _ {{_ : Model}} where
  reifyʳ : ∀ {A Γ} → Γ ⊩ A → Γ [⊢] A
  reifyʳ {α P}   s = syn s
  reifyʳ {A ▻ B} s = syn (s refl⊆)
  reifyʳ {□ A}   s = syn (s refl⊆)
  reifyʳ {A ∧ B} s = [pair] (reifyʳ {A} (π₁ s)) (reifyʳ {B} (π₂ s))
  reifyʳ {⊤}    s = [unit]

  reifyʳ⋆ : ∀ {Ξ Γ} → Γ ⊩⋆ Ξ → Γ [⊢]⋆ Ξ
  reifyʳ⋆ {∅}     ∙        = ∙
  reifyʳ⋆ {Ξ , A} (ts , t) = reifyʳ⋆ ts , reifyʳ t

-- Shorthand for variables.

module _ {{_ : Model}} where
  [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₂

-- Useful theorems in functional form.

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

  [dist] : ∀ {A B Γ} → Γ [⊢] □ (A ▻ B) → Γ [⊢] □ A → Γ [⊢] □ B
  [dist] t u = [multibox] ([⊢]⋆→[⊢⋆] ((∙ , t) , u)) ([app] ([down] [v₁]) ([down] [v₀]))

  [up] : ∀ {A Γ} → Γ [⊢] □ A → Γ [⊢] □ □ A
  [up] t = [multibox] ([⊢]⋆→[⊢⋆] ((∙ , t))) [v₀]

-- Useful theorems in combinatory form.

module _ {{_ : Model}} where
  [ci] : ∀ {A Γ} → Γ [⊢] A ▻ A
  [ci] = [lam] [v₀]

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

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

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

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

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

  [box] : ∀ {A Γ} → ∅ [⊢] A → Γ [⊢] □ A
  [box] t = [multibox] ([⊢]⋆→[⊢⋆] ∙) t

  [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₀])

-- Additional useful equipment.

module _ {{_ : Model}} where
  _⟪$⟫_ : ∀ {A B Γ} → Γ ⊩ A ▻ B → Γ ⊩ A → Γ ⊩ B
  s ⟪$⟫ a = sem (s refl⊆) a

  ⟪K⟫ : ∀ {A B Γ} → Γ ⊩ A → Γ ⊩ B ▻ A
  ⟪K⟫ {A} a η = let a′ = mono⊩ {A} η a
                in  [app] [ck] (reifyʳ a′) ⅋ K a′

  ⟪S⟫ : ∀ {A B C Γ} → Γ ⊩ A ▻ B ▻ C → Γ ⊩ A ▻ B → Γ ⊩ A → Γ ⊩ C
  ⟪S⟫ s₁ s₂ a = (s₁ ⟪$⟫ a) ⟪$⟫ (s₂ ⟪$⟫ a)

  ⟪S⟫′ : ∀ {A B C Γ} → Γ ⊩ A ▻ B ▻ C → Γ ⊩ (A ▻ B) ▻ A ▻ C
  ⟪S⟫′ {A} {B} {C} s₁ η = let s₁′ = mono⊩ {A ▻ B ▻ C} η s₁
                              t   = syn (s₁′ refl⊆)
                          in  [app] [cs] t ⅋ λ s₂ η′ →
                                let s₁″ = mono⊩ {A ▻ B ▻ C} (trans⊆ η η′) s₁
                                    s₂′ = mono⊩ {A ▻ B} η′ s₂
                                    t′  = syn (s₁″ refl⊆)
                                    u   = syn (s₂′ refl⊆)
                                in  [app] ([app] [cs] t′) u ⅋ ⟪S⟫ s₁″ s₂′

  _⟪D⟫_ : ∀ {A B Γ} → Γ ⊩ □ (A ▻ B) → Γ ⊩ □ A → Γ ⊩ □ B
  (s₁ ⟪D⟫ s₂) η = let t ⅋ s₁′ = s₁ η
                      u ⅋ a   = s₂ η
                  in  [app] ([app] [cdist] t) u ⅋ s₁′ ⟪$⟫ a

  _⟪D⟫′_ : ∀ {A B Γ} → Γ ⊩ □ (A ▻ B) → Γ ⊩ □ A ▻ □ B
  _⟪D⟫′_ {A} {B} s₁ η = let s₁′ = mono⊩ {□ (A ▻ B)} η s₁
                        in  [app] [cdist] (reifyʳ (λ {_} η′ → s₁′ η′ )) ⅋ _⟪D⟫_ s₁′

  ⟪↑⟫ : ∀ {A Γ} → Γ ⊩ □ A → Γ ⊩ □ □ A
  ⟪↑⟫ s η = [app] [cup] (syn (s η)) ⅋ λ η′ → s (trans⊆ η η′)

  ⟪↓⟫ : ∀ {A Γ} → Γ ⊩ □ A → Γ ⊩ A
  ⟪↓⟫ s = sem (s refl⊆)

  _⟪,⟫′_ : ∀ {A B Γ} → Γ ⊩ A → Γ ⊩ B ▻ A ∧ B
  _⟪,⟫′_ {A} a η = let a′ = mono⊩ {A} η a
                   in  [app] [cpair] (reifyʳ a′) ⅋ _,_ a′

-- Forcing in a particular world of a particular model, for sequents.

module _ {{_ : Model}} where
  infix 3 _⊩_⇒_
  _⊩_⇒_ : Cx Ty → Cx Ty → Ty → Set
  w ⊩ Γ ⇒ A = w ⊩⋆ Γ → w ⊩ A

  infix 3 _⊩_⇒⋆_
  _⊩_⇒⋆_ : Cx Ty → Cx Ty → Cx Ty → Set
  w ⊩ Γ ⇒⋆ Ξ = w ⊩⋆ Γ → w ⊩⋆ Ξ

-- Entailment, or forcing in all worlds of all models, for sequents.

infix 3 _⊨_
_⊨_ : Cx Ty → Ty → Set₁
Γ ⊨ A = ∀ {{_ : Model}} {w : Cx Ty} → w ⊩ Γ ⇒ A

infix 3 _⊨⋆_
_⊨⋆_ : Cx Ty → Cx Ty → Set₁
Γ ⊨⋆ Ξ = ∀ {{_ : Model}} {w : Cx Ty} → w ⊩ Γ ⇒⋆ Ξ

-- Additional useful equipment, for sequents.

module _ {{_ : Model}} where
  lookup : ∀ {A Γ w} → A ∈ Γ → w ⊩ Γ ⇒ A
  lookup top     (γ , a) = a
  lookup (pop i) (γ , b) = lookup i γ

  _⟦$⟧_ : ∀ {A B Γ w} → w ⊩ Γ ⇒ A ▻ B → w ⊩ Γ ⇒ A → w ⊩ Γ ⇒ B
  (s₁ ⟦$⟧ s₂) γ = s₁ γ ⟪$⟫ s₂ γ

  ⟦K⟧ : ∀ {A B Γ w} → w ⊩ Γ ⇒ A → w ⊩ Γ ⇒ B ▻ A
  ⟦K⟧ {A} {B} a γ = ⟪K⟫ {A} {B} (a γ)

  ⟦S⟧ : ∀ {A B C Γ w} → w ⊩ Γ ⇒ A ▻ B ▻ C → w ⊩ Γ ⇒ A ▻ B → w ⊩ Γ ⇒ A → w ⊩ Γ ⇒ C
  ⟦S⟧ s₁ s₂ a γ = ⟪S⟫ (s₁ γ) (s₂ γ) (a γ)

  _⟦D⟧_ : ∀ {A B Γ w} → w ⊩ Γ ⇒ □ (A ▻ B) → w ⊩ Γ ⇒ □ A → w ⊩ Γ ⇒ □ B
  (s₁ ⟦D⟧ s₂) γ = (s₁ γ) ⟪D⟫ (s₂ γ)

  ⟦↑⟧ : ∀ {A Γ w} → w ⊩ Γ ⇒ □ A → w ⊩ Γ ⇒ □ □ A
  ⟦↑⟧ s γ = ⟪↑⟫ (s γ)

  ⟦↓⟧ : ∀ {A Γ w} → w ⊩ Γ ⇒ □ A → w ⊩ Γ ⇒ A
  ⟦↓⟧ s γ = ⟪↓⟫ (s γ)

  _⟦,⟧_ : ∀ {A B Γ w} → w ⊩ Γ ⇒ A → w ⊩ Γ ⇒ B → w ⊩ Γ ⇒ A ∧ B
  (a ⟦,⟧ b) γ = a γ , b γ

  ⟦π₁⟧ : ∀ {A B Γ w} → w ⊩ Γ ⇒ A ∧ B → w ⊩ Γ ⇒ A
  ⟦π₁⟧ s γ = π₁ (s γ)

  ⟦π₂⟧ : ∀ {A B Γ w} → w ⊩ Γ ⇒ A ∧ B → w ⊩ Γ ⇒ B
  ⟦π₂⟧ s γ = π₂ (s γ)


------------------------------------------------------------------------
-- A type soundness result
------------------------------------------------------------------------

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

module Lambda.Partiality-monad.Inductive.Type-soundness where

open import Equality.Propositional.Cubical
open import Prelude hiding (⊥)
open import Prelude.Size

open import Bijection equality-with-J using (_↔_)
open import Equality.Decision-procedures equality-with-J
open import Equivalence equality-with-J using (_≃_)
open import Function-universe equality-with-J hiding (id; _∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional equality-with-paths
  as Trunc
open import Maybe equality-with-J
open import Monad equality-with-J
open import Univalence-axiom equality-with-J
open import Vec.Function equality-with-J

open import Partiality-monad.Inductive
open import Partiality-monad.Inductive.Alternative-order
open import Partiality-monad.Inductive.Monad

open import Lambda.Partiality-monad.Inductive.Interpreter
open import Lambda.Syntax

open Closure Tm

-- A propositionally truncated variant of WF-MV.

∥WF-MV∥ : Ty ∞ → Maybe Value → Type
∥WF-MV∥ σ x = ∥ WF-MV σ x ∥

-- If we can prove □ (∥WF-MV∥ σ) (run x), then x does not "go wrong".

does-not-go-wrong : ∀ {σ} {x : M Value} →
                    □ (∥WF-MV∥ σ) (run x) → ¬ x ≡ fail
does-not-go-wrong {σ} {x} always =
  x ≡ fail             ↝⟨ cong MaybeT.run ⟩
  run x ≡ now nothing  ↝⟨ always _ ⟩
  ∥WF-MV∥ σ nothing    ↝⟨ id ⟩
  ∥ ⊥₀ ∥               ↝⟨ _↔_.to (not-inhabited⇒∥∥↔⊥ id) ⟩□
  ⊥₀                   □

-- Some "constructors" for □ ∘ ∥WF-MV∥.

return-wf :
  ∀ {σ v} →
  WF-Value σ v →
  □ (∥WF-MV∥ σ) (MaybeT.run (return v))
return-wf v-wf = □-now truncation-is-proposition ∣ v-wf ∣

_>>=-wf_ :
  ∀ {σ τ} {x : M Value} {f : Value → M Value} →
  □ (∥WF-MV∥ σ) (run x) →
  (∀ {v} → WF-Value σ v → □ (∥WF-MV∥ τ) (run (f v))) →
  □ (∥WF-MV∥ τ) (MaybeT.run (x >>= f))
_>>=-wf_ {σ} {τ} {f = f} x-wf f-wf =
  □->>= (λ _ → truncation-is-proposition) x-wf
    λ { {nothing} → Trunc.rec prop ⊥-elim
      ; {just v}  → Trunc.rec prop f-wf
      }
  where
  prop : ∀ {x} → Is-proposition (□ (∥WF-MV∥ τ) x)
  prop = □-closure 1 (λ _ → truncation-is-proposition)

-- Type soundness.

mutual

  ⟦⟧′-wf : ∀ {n Γ} (t : Tm n) {σ} → Γ ⊢ t ∈ σ →
           ∀ {ρ} → WF-Env Γ ρ →
           ∀ n → □ (∥WF-MV∥ σ) (run (⟦ t ⟧′ ρ n))
  ⟦⟧′-wf (con i)   con             ρ-wf n = return-wf con
  ⟦⟧′-wf (var x)   var             ρ-wf n = return-wf (ρ-wf x)
  ⟦⟧′-wf (ƛ t)     (ƛ t∈)          ρ-wf n = return-wf (ƛ t∈ ρ-wf)
  ⟦⟧′-wf (t₁ · t₂) (t₁∈ · t₂∈) {ρ} ρ-wf n =
     ⟦⟧′-wf t₁ t₁∈ ρ-wf n >>=-wf λ f-wf →
     ⟦⟧′-wf t₂ t₂∈ ρ-wf n >>=-wf λ v-wf →
     ∙-wf f-wf v-wf n

  ∙-wf : ∀ {σ τ f v} →
         WF-Value (σ ⇾ τ) f → WF-Value (force σ) v →
         ∀ n → □ (∥WF-MV∥ (force τ)) (run ((f ∙ v) n))
  ∙-wf (ƛ t₁∈ ρ₁-wf) v₂-wf (suc n) = ⟦⟧′-wf _ t₁∈ (cons-wf v₂-wf ρ₁-wf) n
  ∙-wf (ƛ t₁∈ ρ₁-wf) v₂-wf zero    =         $⟨ □-never ⟩
    □ (∥WF-MV∥ _) never                      ↝⟨ ≡⇒↝ bijection $ cong (□ (∥WF-MV∥ _)) (sym never->>=) ⟩□
    □ (∥WF-MV∥ _) (never >>= return ∘ just)  □

type-soundness : ∀ {t : Tm 0} {σ} →
                 nil ⊢ t ∈ σ → ¬ ⟦ t ⟧ nil ≡ fail
type-soundness {t} {σ} =
  nil ⊢ t ∈ σ                                 ↝⟨ (λ t∈ → ⟦⟧′-wf _ t∈ nil-wf) ⟩
  (∀ n → □ (∥WF-MV∥ σ) (run (⟦ t ⟧′ nil n)))  ↝⟨ □-⨆ (λ _ → truncation-is-proposition) ⟩
  □ (∥WF-MV∥ σ) (run (⟦ t ⟧ nil))             ↝⟨ does-not-go-wrong ⟩□
  ¬ ⟦ t ⟧ nil ≡ fail                          □


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Weakening {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped as U hiding (wk)
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Weakening as T hiding (wk; wkEq; wkTerm; wkEqTerm)
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Irrelevance

open import Tools.Product
import Tools.PropositionalEquality as PE

import Data.Nat as Nat

-- Weakening of neutrals in WHNF

wkTermNe : ∀ {ρ Γ Δ k A rA} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
         → Γ ⊩neNf k ∷ A ^ rA → Δ ⊩neNf U.wk ρ k ∷ U.wk ρ A ^ rA
wkTermNe {ρ} [ρ] ⊢Δ (neNfₜ neK ⊢k k≡k) =
  neNfₜ (wkNeutral ρ neK) (T.wkTerm [ρ] ⊢Δ ⊢k) (~-wk [ρ] ⊢Δ k≡k)

wkEqTermNe : ∀ {ρ Γ Δ k k′ A rA} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
           → Γ ⊩neNf k ≡ k′ ∷ A ^ rA → Δ ⊩neNf U.wk ρ k ≡ U.wk ρ k′ ∷ U.wk ρ A ^ rA
wkEqTermNe {ρ} [ρ] ⊢Δ (neNfₜ₌ neK neM k≡m) =
  neNfₜ₌ (wkNeutral ρ neK) (wkNeutral ρ neM) (~-wk [ρ] ⊢Δ k≡m)

-- Weakening of reducible natural numbers

mutual
  wkTermℕ : ∀ {ρ Γ Δ n} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
          → Γ ⊩ℕ n ∷ℕ → Δ ⊩ℕ U.wk ρ n ∷ℕ
  wkTermℕ {ρ} [ρ] ⊢Δ (ℕₜ n d n≡n prop) =
    ℕₜ (U.wk ρ n) (wkRed:*:Term [ρ] ⊢Δ d)
       (≅ₜ-wk [ρ] ⊢Δ n≡n)
       (wkNatural-prop [ρ] ⊢Δ prop)

  wkNatural-prop : ∀ {ρ Γ Δ n} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
                 → Natural-prop Γ n
                 → Natural-prop Δ (U.wk ρ n)
  wkNatural-prop ρ ⊢Δ (sucᵣ n) = sucᵣ (wkTermℕ ρ ⊢Δ n)
  wkNatural-prop ρ ⊢Δ zeroᵣ = zeroᵣ
  wkNatural-prop ρ ⊢Δ (ne nf) = ne (wkTermNe ρ ⊢Δ nf)

mutual
  wkEqTermℕ : ∀ {ρ Γ Δ t u} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
            → Γ ⊩ℕ t ≡ u ∷ℕ
            → Δ ⊩ℕ U.wk ρ t ≡ U.wk ρ u ∷ℕ
  wkEqTermℕ {ρ} [ρ] ⊢Δ (ℕₜ₌ k k′ d d′ t≡u prop) =
    ℕₜ₌ (U.wk ρ k) (U.wk ρ k′) (wkRed:*:Term [ρ] ⊢Δ d)
        (wkRed:*:Term [ρ] ⊢Δ d′) (≅ₜ-wk [ρ] ⊢Δ t≡u)
        (wk[Natural]-prop [ρ] ⊢Δ prop)

  wk[Natural]-prop : ∀ {ρ Γ Δ n n′} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
                   → [Natural]-prop Γ n n′
                   → [Natural]-prop Δ (U.wk ρ n) (U.wk ρ n′)
  wk[Natural]-prop ρ ⊢Δ (sucᵣ [n≡n′]) = sucᵣ (wkEqTermℕ ρ ⊢Δ [n≡n′])
  wk[Natural]-prop ρ ⊢Δ zeroᵣ = zeroᵣ
  wk[Natural]-prop ρ ⊢Δ (ne x) = ne (wkEqTermNe ρ ⊢Δ x)

-- Empty
wkTermEmpty : ∀ {ρ Γ Δ l n} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
  → Γ ⊩Empty n ∷Empty^ l → Δ ⊩Empty U.wk ρ n ∷Empty^ l
wkTermEmpty {ρ} [ρ] ⊢Δ (Emptyₜ (ne d)) = Emptyₜ (ne (T.wkTerm [ρ] ⊢Δ d))

wk[Empty]-prop : ∀ {ρ Γ Δ n l n′} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
  → [Empty]-prop Γ n n′ l
  → [Empty]-prop Δ (U.wk ρ n) (U.wk ρ n′) l
wk[Empty]-prop {ρ} [ρ] ⊢Δ (ne d d') = ne (T.wkTerm [ρ] ⊢Δ d) (T.wkTerm [ρ] ⊢Δ d')

wkEqTermEmpty : ∀ {ρ Γ Δ t u l } → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
  → Γ ⊩Empty t ≡ u ∷Empty^ l
  → Δ ⊩Empty U.wk ρ t ≡ U.wk ρ u ∷Empty^ l
wkEqTermEmpty {ρ} [ρ] ⊢Δ (Emptyₜ₌ (ne d d')) = Emptyₜ₌ (ne (T.wkTerm [ρ] ⊢Δ d) (T.wkTerm [ρ] ⊢Δ d'))

-- Weakening of the logical relation

wk : ∀ {ρ Γ Δ A rA l} → ρ ∷ Δ ⊆ Γ → ⊢ Δ → Γ ⊩⟨ l ⟩ A ^ rA → Δ ⊩⟨ l ⟩ U.wk ρ A ^ rA
wk ρ ⊢Δ (Uᵣ (Uᵣ r l′ l< eq d)) = Uᵣ (Uᵣ r l′ l< eq (wkRed:*: ρ ⊢Δ d))
wk ρ ⊢Δ (ℕᵣ D) = ℕᵣ (wkRed:*: ρ ⊢Δ D)
wk ρ ⊢Δ (Emptyᵣ D) = Emptyᵣ (wkRed:*: ρ ⊢Δ D)
wk {ρ} [ρ] ⊢Δ (ne′ K D neK K≡K) =
  ne′ (U.wk ρ K) (wkRed:*: [ρ] ⊢Δ D) (wkNeutral ρ neK) (~-wk [ρ] ⊢Δ K≡K)
wk {ρ} {Γ} {Δ} {A} {rA} {l} [ρ] ⊢Δ (Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext) =
  let ⊢ρF = T.wk [ρ] ⊢Δ ⊢F
      iF = [ rF , ι lF ]
      iG = [ TypeInfo.r rA , ι lG ]
      [F]′ : ∀ {ρ ρ′ E} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
           → E ⊩⟨ l ⟩ U.wk ρ (U.wk ρ′ F) ^ iF
      [F]′ {ρ} {ρ′} [ρ] [ρ′] ⊢E = irrelevance′
                              (PE.sym (wk-comp ρ ρ′ F))
                              ([F] ([ρ] •ₜ [ρ′]) ⊢E)
      [a]′ : ∀ {ρ ρ′ E a} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
             ([a] : E ⊩⟨ l ⟩ a ∷ U.wk ρ (U.wk ρ′ F) ^ iF / [F]′ [ρ] [ρ′] ⊢E)
           → E ⊩⟨ l ⟩ a ∷ U.wk (ρ • ρ′) F ^ iF / [F] ([ρ] •ₜ [ρ′]) ⊢E
      [a]′ {ρ} {ρ′} [ρ] [ρ′] ⊢E [a] = irrelevanceTerm′ (wk-comp ρ ρ′ F) PE.refl PE.refl
                                          ([F]′ [ρ] [ρ′] ⊢E) ([F] ([ρ] •ₜ [ρ′]) ⊢E) [a]
      [G]′ : ∀ {ρ ρ′ E a} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
             ([a] : E ⊩⟨ l ⟩ a ∷ U.wk ρ (U.wk ρ′ F) ^ iF / [F]′ [ρ] [ρ′] ⊢E)
           → E ⊩⟨ l ⟩ U.wk (lift (ρ • ρ′)) G [ a ] ^ iG
      [G]′ η η′ ⊢E [a] = [G] (η •ₜ η′) ⊢E ([a]′ η η′ ⊢E [a])
  in  Πᵣ′ rF lF lG lF≤ lG≤ (U.wk ρ F) (U.wk (lift ρ) G) (T.wkRed:*: [ρ] ⊢Δ D) ⊢ρF
           (T.wk (lift [ρ]) (⊢Δ ∙ ⊢ρF) ⊢G)
           (≅-wk [ρ] ⊢Δ A≡A)
           (λ {ρ₁} [ρ₁] ⊢Δ₁ → irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                    ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
           (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] → irrelevance′ (wk-comp-subst ρ₁ ρ G)
                                        ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a]))
           (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] [b] [a≡b] →
              let [a≡b]′ = irrelevanceEqTerm′ (wk-comp ρ₁ ρ F) PE.refl PE.refl
                                              ([F]′ [ρ₁] [ρ] ⊢Δ₁)
                                              ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁)
                                              [a≡b]
              in  irrelevanceEq″ (wk-comp-subst ρ₁ ρ G)
                                  (wk-comp-subst ρ₁ ρ G) PE.refl PE.refl
                                  ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a])
                                  (irrelevance′
                                            (wk-comp-subst ρ₁ ρ G)
                                            ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a]))
                                  (G-ext ([ρ₁] •ₜ [ρ]) ⊢Δ₁
                                         ([a]′ [ρ₁] [ρ] ⊢Δ₁ [a])
                                         ([a]′ [ρ₁] [ρ] ⊢Δ₁ [b])
                                         [a≡b]′))
wk {ρ} {Γ} {Δ} {A} {rA} {l} [ρ] ⊢Δ (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) =
  let ⊢ρF = T.wk [ρ] ⊢Δ ⊢F
      iF = [ % , TypeInfo.l rA ]
      [F]′ : ∀ {ρ ρ′ E} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
           → E ⊩⟨ l ⟩ U.wk ρ (U.wk ρ′ F) ^ iF
      [F]′ {ρ} {ρ′} [ρ] [ρ′] ⊢E = irrelevance′
                              (PE.sym (wk-comp ρ ρ′ F))
                              ([F] ([ρ] •ₜ [ρ′]) ⊢E)
      [a]′ : ∀ {ρ ρ′ E a} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
             ([a] : E ⊩⟨ l ⟩ a ∷ U.wk ρ (U.wk ρ′ F) ^ iF / [F]′ [ρ] [ρ′] ⊢E)
           → E ⊩⟨ l ⟩ a ∷ U.wk (ρ • ρ′) F ^ iF / [F] ([ρ] •ₜ [ρ′]) ⊢E
      [a]′ {ρ} {ρ′} [ρ] [ρ′] ⊢E [a] = irrelevanceTerm′ (wk-comp ρ ρ′ F) PE.refl PE.refl
                                          ([F]′ [ρ] [ρ′] ⊢E) ([F] ([ρ] •ₜ [ρ′]) ⊢E) [a]
      [G]′ : ∀ {ρ ρ′ E a} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
             ([a] : E ⊩⟨ l ⟩ a ∷ U.wk ρ (U.wk ρ′ F) ^ iF / [F]′ [ρ] [ρ′] ⊢E)
           → E ⊩⟨ l ⟩ U.wk (lift (ρ • ρ′)) G [ a ] ^ rA
      [G]′ η η′ ⊢E [a] = [G] (η •ₜ η′) ⊢E ([a]′ η η′ ⊢E [a])
  in  ∃ᵣ′ (U.wk ρ F) (U.wk (lift ρ) G) (T.wkRed:*: [ρ] ⊢Δ D) ⊢ρF
           (T.wk (lift [ρ]) (⊢Δ ∙ ⊢ρF) ⊢G)
           (≅-wk [ρ] ⊢Δ A≡A)
           (λ {ρ₁} [ρ₁] ⊢Δ₁ → irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                    ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
           (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] → irrelevance′ (wk-comp-subst ρ₁ ρ G)
                                        ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a]))
           (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] [b] [a≡b] →
              let [a≡b]′ = irrelevanceEqTerm′ (wk-comp ρ₁ ρ F) PE.refl PE.refl
                                              ([F]′ [ρ₁] [ρ] ⊢Δ₁)
                                              ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁)
                                              [a≡b]
              in  irrelevanceEq″ (wk-comp-subst ρ₁ ρ G)
                                  (wk-comp-subst ρ₁ ρ G) PE.refl PE.refl
                                  ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a])
                                  (irrelevance′
                                            (wk-comp-subst ρ₁ ρ G)
                                            ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a]))
                                  (G-ext ([ρ₁] •ₜ [ρ]) ⊢Δ₁
                                         ([a]′ [ρ₁] [ρ] ⊢Δ₁ [a])
                                         ([a]′ [ρ₁] [ρ] ⊢Δ₁ [b])
                                         [a≡b]′))
wk {l = ι ¹} ρ ⊢Δ (emb l< X) = emb l< (wk ρ ⊢Δ X)
wk {l = ∞} ρ ⊢Δ (emb l< X) = emb l< (wk ρ ⊢Δ X)

wkEq : ∀ {ρ Γ Δ A B r l} → ([ρ] : ρ ∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
       ([A] : Γ ⊩⟨ l ⟩ A ^ r)
     → Γ ⊩⟨ l ⟩ A ≡ B ^ r / [A]
     → Δ ⊩⟨ l ⟩ U.wk ρ A ≡ U.wk ρ B ^ r / wk [ρ] ⊢Δ [A]
wkEq ρ ⊢Δ (Uᵣ (Uᵣ r l′ l< eq d)) D = wkRed* ρ ⊢Δ D
wkEq ρ ⊢Δ (ℕᵣ D) A≡B = wkRed* ρ ⊢Δ A≡B
wkEq ρ ⊢Δ (Emptyᵣ D) A≡B = wkRed* ρ ⊢Δ A≡B
wkEq {ρ} [ρ] ⊢Δ (ne′ _ _ _ _) (ne₌ M D′ neM K≡M) =
  ne₌ (U.wk ρ M) (wkRed:*: [ρ] ⊢Δ D′)
      (wkNeutral ρ neM) (~-wk [ρ] ⊢Δ K≡M)
wkEq {ρ} [ρ] ⊢Δ (Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]) =
  Π₌ (U.wk ρ F′) (U.wk (lift ρ) G′) (T.wkRed* [ρ] ⊢Δ D′) (≅-wk [ρ] ⊢Δ A≡B)
     (λ {ρ₁} [ρ₁] ⊢Δ₁ → irrelevanceEq″ (PE.sym (wk-comp ρ₁ ρ F))
                                 (PE.sym (wk-comp ρ₁ ρ F′)) PE.refl PE.refl
                                 ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁)
                                 (irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                               ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
                                 ([F≡F′] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
     (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] →
        let [a]′ = irrelevanceTerm′ (wk-comp ρ₁ ρ F) PE.refl PE.refl
                                    (irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                                  ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
                                    ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁) [a]
        in  irrelevanceEq″ (wk-comp-subst ρ₁ ρ G)
                            (wk-comp-subst ρ₁ ρ G′) PE.refl PE.refl
                            ([G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′)
                            (irrelevance′ (wk-comp-subst ρ₁ ρ G)
                                          ([G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
                            ([G≡G′] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
wkEq {ρ} [ρ] ⊢Δ (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                (∃₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]) =
  ∃₌ (U.wk ρ F′) (U.wk (lift ρ) G′) (T.wkRed* [ρ] ⊢Δ D′) (≅-wk [ρ] ⊢Δ A≡B)
     (λ {ρ₁} [ρ₁] ⊢Δ₁ → irrelevanceEq″ (PE.sym (wk-comp ρ₁ ρ F))
                                 (PE.sym (wk-comp ρ₁ ρ F′)) PE.refl PE.refl
                                 ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁)
                                 (irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                               ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
                                 ([F≡F′] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
     (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] →
        let [a]′ = irrelevanceTerm′ (wk-comp ρ₁ ρ F) PE.refl PE.refl
                                    (irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                                  ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
                                    ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁) [a]
        in  irrelevanceEq″ (wk-comp-subst ρ₁ ρ G)
                            (wk-comp-subst ρ₁ ρ G′) PE.refl PE.refl
                            ([G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′)
                            (irrelevance′ (wk-comp-subst ρ₁ ρ G)
                                          ([G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
                            ([G≡G′] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
wkEq {l = ι ¹} ρ ⊢Δ (emb l< X) A≡B = wkEq ρ ⊢Δ X A≡B
wkEq {l = ∞} ρ ⊢Δ (emb l< X) A≡B = wkEq ρ ⊢Δ X A≡B

wkTerm : ∀ {ρ Γ Δ A t r l} ([ρ] : ρ ∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
         ([A] : Γ ⊩⟨ l ⟩ A ^ r)
       → Γ ⊩⟨ l ⟩ t ∷ A ^ r / [A]
       → Δ ⊩⟨ l ⟩ U.wk ρ t ∷ U.wk ρ A ^ r / wk [ρ] ⊢Δ [A]
wkTerm {ρ} {Δ = Δ} {t = t} {l = ι ¹} [ρ] ⊢Δ (Uᵣ (Uᵣ r ⁰ l< eq d)) (Uₜ K d₁ typeK K≡K [t]) =
  let
    -- this code is a bit of a mess tbh
    -- it is mostly about using irrelevance back and forth between proofs of
    -- reducibility using U.wk ρ′ (U.wk ρ t) and proofs using U.wk (ρ′ • ρ) t
    [t]′ = λ {ρ′} {Δ′} [ρ′] (⊢Δ′ : ⊢ Δ′) →
      irrelevance′ (PE.sym (wk-comp ρ′ ρ t)) ([t] ([ρ′] •ₜ [ρ]) ⊢Δ′)
    [t]′_to_[t] = λ {ρ′} {Δ′} {a} [ρ′] (⊢Δ′ : ⊢ Δ′)
      ([a] : Δ′ ⊩⟨ ι ⁰ ⟩ a ∷ U.wk ρ′ (U.wk ρ t) ^ [ r , ι ⁰ ] / [t]′ [ρ′] ⊢Δ′) →
      irrelevanceTerm′ (wk-comp ρ′ ρ t) PE.refl PE.refl ([t]′ [ρ′] ⊢Δ′) ([t] ([ρ′] •ₜ [ρ]) ⊢Δ′) [a]
    [t]′_to_[t]_eq = λ {ρ′} {Δ′} {a} {a′} [ρ′] (⊢Δ′ : ⊢ Δ′)
      (a≡a′ : Δ′ ⊩⟨ ι ⁰ ⟩ a ≡ a′ ∷ U.wk ρ′ (U.wk ρ t) ^ [ r , ι ⁰ ] / [t]′ [ρ′] ⊢Δ′) →
      irrelevanceEqTerm′ (wk-comp ρ′ ρ t) PE.refl PE.refl ([t]′ [ρ′] ⊢Δ′) ([t] ([ρ′] •ₜ [ρ]) ⊢Δ′) a≡a′
    [t]′_to_[t]_id = λ {ρ′} {Δ′} {e} {B} [e] →
      (PE.subst (λ X → Δ′ ⊢ e ∷ Id (U ⁰) X B ^ [ % , ι ¹ ]) (wk-comp ρ′ ρ t) [e])
  in
  Uₜ (U.wk ρ K) (wkRed:*:Term [ρ] ⊢Δ d₁) (wkType ρ typeK) (≅ₜ-wk [ρ] ⊢Δ K≡K) [t]′
-- this is a duplicate of the above code (because we need logRelRec to compute)
-- surely there is a way to avoid this redundancy
wkTerm {ρ} {Δ = Δ} {t = t} {l = ∞} [ρ] ⊢Δ (Uᵣ (Uᵣ r ¹ l< eq d)) (Uₜ K d₁ typeK K≡K [t]) =
  let
    [t]′ = λ {ρ′} {Δ′} [ρ′] (⊢Δ′ : ⊢ Δ′) →
      irrelevance′ (PE.sym (wk-comp ρ′ ρ t)) ([t] ([ρ′] •ₜ [ρ]) ⊢Δ′)
    [t]′_to_[t] = λ {ρ′} {Δ′} {a} [ρ′] (⊢Δ′ : ⊢ Δ′)
      ([a] : Δ′ ⊩⟨ ι ¹ ⟩ a ∷ U.wk ρ′ (U.wk ρ t) ^ [ r , ι ¹ ] / [t]′ [ρ′] ⊢Δ′) →
      irrelevanceTerm′ (wk-comp ρ′ ρ t) PE.refl PE.refl ([t]′ [ρ′] ⊢Δ′) ([t] ([ρ′] •ₜ [ρ]) ⊢Δ′) [a]
    [t]′_to_[t]_eq = λ {ρ′} {Δ′} {a} {a′} [ρ′] (⊢Δ′ : ⊢ Δ′)
      (a≡a′ : Δ′ ⊩⟨ ι ¹ ⟩ a ≡ a′ ∷ U.wk ρ′ (U.wk ρ t) ^ [ r , ι ¹ ] / [t]′ [ρ′] ⊢Δ′) →
      irrelevanceEqTerm′ (wk-comp ρ′ ρ t) PE.refl PE.refl ([t]′ [ρ′] ⊢Δ′) ([t] ([ρ′] •ₜ [ρ]) ⊢Δ′) a≡a′
    [t]′_to_[t]_id = λ {ρ′} {Δ′} {e} {B} [e] →
      (PE.subst (λ X → Δ′ ⊢ e ∷ Id (U ⁰) X B ^ [ % , ι ¹ ]) (wk-comp ρ′ ρ t) [e])
  in
  Uₜ (U.wk ρ K) (wkRed:*:Term [ρ] ⊢Δ d₁) (wkType ρ typeK) (≅ₜ-wk [ρ] ⊢Δ K≡K) [t]′
wkTerm ρ ⊢Δ (ℕᵣ D) [t] = wkTermℕ ρ ⊢Δ [t]
wkTerm ρ ⊢Δ (Emptyᵣ D) [t] = wkTermEmpty ρ ⊢Δ [t]
wkTerm {ρ} {r = [ ! , l′ ]} [ρ] ⊢Δ (ne′ K D neK K≡K) (neₜ k d nf) =
  neₜ (U.wk ρ k) (wkRed:*:Term [ρ] ⊢Δ d) (wkTermNe [ρ] ⊢Δ nf)
wkTerm {ρ} {r = [ % , l′ ]} [ρ] ⊢Δ (ne′ K D neK K≡K) (neₜ d) = neₜ ( T.wkTerm [ρ] ⊢Δ d)
wkTerm {ρ} {r = [ ! , l′ ]} [ρ] ⊢Δ (Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext) (Πₜ f d funcF f≡f [f] [f]₁) =
  Πₜ (U.wk ρ f) (wkRed:*:Term [ρ] ⊢Δ d) (wkFunction ρ funcF)
     (≅ₜ-wk [ρ] ⊢Δ f≡f)
     (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] [b] [a≡b] →
        let F-compEq = wk-comp ρ₁ ρ F
            G-compEq = wk-comp-subst ρ₁ ρ G
            [F]₁ = [F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁
            [F]₂ = irrelevance′ (PE.sym (wk-comp ρ₁ ρ F)) [F]₁
            [a]′ = irrelevanceTerm′ F-compEq PE.refl PE.refl [F]₂ [F]₁ [a]
            [b]′ = irrelevanceTerm′ F-compEq PE.refl PE.refl [F]₂ [F]₁ [b]
            [G]₁ = [G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′
            [G]₂ = irrelevance′ G-compEq [G]₁
            [a≡b]′ = irrelevanceEqTerm′ F-compEq PE.refl PE.refl [F]₂ [F]₁ [a≡b]
        in  irrelevanceEqTerm″ PE.refl PE.refl (PE.cong (λ x → x ∘ _ ^ _ ) (PE.sym (wk-comp ρ₁ ρ _)))
                                (PE.cong (λ x → x ∘ _ ^ _) (PE.sym (wk-comp ρ₁ ρ _)))
                                G-compEq
                                [G]₁ [G]₂
                                ([f] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′ [b]′ [a≡b]′))
     (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] →
        let [F]₁ = [F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁
            [F]₂ = irrelevance′ (PE.sym (wk-comp ρ₁ ρ F)) [F]₁
            [a]′ = irrelevanceTerm′ (wk-comp ρ₁ ρ F) PE.refl PE.refl [F]₂ [F]₁ [a]
            [G]₁ = [G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′
            [G]₂ = irrelevance′ (wk-comp-subst ρ₁ ρ G) [G]₁
        in  irrelevanceTerm″ (wk-comp-subst ρ₁ ρ G) PE.refl PE.refl
                              (PE.cong (λ x → x ∘ _ ^ _ ) (PE.sym (wk-comp ρ₁ ρ _)))
                              [G]₁ [G]₂ ([f]₁ ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
wkTerm {ρ} {r = [ % , l′ ]} [ρ] ⊢Δ (Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext) d = T.wkTerm [ρ] ⊢Δ d
wkTerm {ρ} [ρ] ⊢Δ (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) d = T.wkTerm [ρ] ⊢Δ d
wkTerm {l = ι ¹} ρ ⊢Δ (emb l< X) t = wkTerm ρ ⊢Δ X t
wkTerm {l = ∞} ρ ⊢Δ (emb l< X) t = wkTerm ρ ⊢Δ X t

wkEqTerm : ∀ {ρ Γ Δ A t u r l} ([ρ] : ρ ∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
           ([A] : Γ ⊩⟨ l ⟩ A ^ r)
         → Γ ⊩⟨ l ⟩ t ≡ u ∷ A ^ r / [A]
         → Δ ⊩⟨ l ⟩ U.wk ρ t ≡ U.wk ρ u ∷ U.wk ρ A ^ r / wk [ρ] ⊢Δ [A]
wkEqTerm {ρ} {Γ} {Δ} {A} {t} {u} {r} {l = ι ¹} [ρ] ⊢Δ (Uᵣ (Uᵣ ti ⁰ l< eq d)) (Uₜ₌ [t] [u] A≡B [t≡u]) =
  let
    [t]′ = λ {ρ′} {Δ′} ([ρ′] : ρ′ ∷ Δ′ ⊆ Δ) (⊢Δ′ : ⊢ Δ′) →
      irrelevance′ (PE.sym (wk-comp ρ′ ρ t)) (LogRel._⊩¹U_∷_^_/_.[t] [t] ([ρ′] •ₜ [ρ]) ⊢Δ′)
    [t]″ = λ {ρ′} {Δ′} ([ρ′] : ρ′ ∷ Δ′ ⊆ Δ) (⊢Δ′ : ⊢ Δ′) →
      LogRel._⊩¹U_∷_^_/_.[t] [t] ([ρ′] •ₜ [ρ]) ⊢Δ′
    [t]′_to_[t]″ = λ {ρ′} {Δ′} {a} [ρ′] (⊢Δ′ : ⊢ Δ′)
      ([a] : Δ′ ⊩⟨ ι ⁰ ⟩ a ∷ U.wk ρ′ (U.wk ρ t) ^ [ ti , ι ⁰ ] / [t]′ [ρ′] ⊢Δ′) →
      (irrelevanceTerm′ (wk-comp ρ′ ρ t) PE.refl PE.refl ([t]′ [ρ′] ⊢Δ′) ([t]″ [ρ′] ⊢Δ′) [a])
    [t≡u]′ = λ {ρ′} {Δ′} [ρ′] (⊢Δ′ : ⊢ Δ′) →
      irrelevanceEq″ (PE.sym (wk-comp ρ′ ρ t)) (PE.sym (wk-comp ρ′ ρ u)) PE.refl PE.refl ([t]″ [ρ′] ⊢Δ′)
        ((LogRel._⊩¹U_∷_^_/_.[t] (wkTerm [ρ] ⊢Δ (Uᵣ (Uᵣ ti ⁰ l< eq d)) [t]) [ρ′] ⊢Δ′))
        ([t≡u] ([ρ′] •ₜ [ρ]) ⊢Δ′)
  in
  Uₜ₌ (wkTerm [ρ] ⊢Δ (Uᵣ (Uᵣ ti ⁰ l< eq d)) [t]) (wkTerm [ρ] ⊢Δ (Uᵣ (Uᵣ ti ⁰ l< eq d)) [u])
    (≅ₜ-wk [ρ] ⊢Δ A≡B) [t≡u]′
-- this is a duplicate of the above code (because we need logRelRec to compute)
-- surely there is a way to avoid this redundancy
wkEqTerm {ρ} {Γ} {Δ} {A} {t} {u} {r} {l = ∞} [ρ] ⊢Δ (Uᵣ (Uᵣ ti ¹ l< eq d)) (Uₜ₌ [t] [u] A≡B [t≡u]) =
  let
    [t]′ = λ {ρ′} {Δ′} ([ρ′] : ρ′ ∷ Δ′ ⊆ Δ) (⊢Δ′ : ⊢ Δ′) →
      irrelevance′ (PE.sym (wk-comp ρ′ ρ t)) (LogRel._⊩¹U_∷_^_/_.[t] [t] ([ρ′] •ₜ [ρ]) ⊢Δ′)
    [t]″ = λ {ρ′} {Δ′} ([ρ′] : ρ′ ∷ Δ′ ⊆ Δ) (⊢Δ′ : ⊢ Δ′) →
      LogRel._⊩¹U_∷_^_/_.[t] [t] ([ρ′] •ₜ [ρ]) ⊢Δ′
    [t]′_to_[t]″ = λ {ρ′} {Δ′} {a} [ρ′] (⊢Δ′ : ⊢ Δ′)
      ([a] : Δ′ ⊩⟨ ι ¹ ⟩ a ∷ U.wk ρ′ (U.wk ρ t) ^ [ ti , ι ¹ ] / [t]′ [ρ′] ⊢Δ′) →
      (irrelevanceTerm′ (wk-comp ρ′ ρ t) PE.refl PE.refl ([t]′ [ρ′] ⊢Δ′) ([t]″ [ρ′] ⊢Δ′) [a])
    [t≡u]′ = λ {ρ′} {Δ′} [ρ′] (⊢Δ′ : ⊢ Δ′) →
      irrelevanceEq″ (PE.sym (wk-comp ρ′ ρ t)) (PE.sym (wk-comp ρ′ ρ u)) PE.refl PE.refl ([t]″ [ρ′] ⊢Δ′)
        ((LogRel._⊩¹U_∷_^_/_.[t] (wkTerm [ρ] ⊢Δ (Uᵣ (Uᵣ ti ¹ l< eq d)) [t]) [ρ′] ⊢Δ′))
        ([t≡u] ([ρ′] •ₜ [ρ]) ⊢Δ′)
  in
  Uₜ₌ (wkTerm [ρ] ⊢Δ (Uᵣ (Uᵣ ti ¹ l< eq d)) [t]) (wkTerm [ρ] ⊢Δ (Uᵣ (Uᵣ ti ¹ l< eq d)) [u])
    (≅ₜ-wk [ρ] ⊢Δ A≡B) [t≡u]′
wkEqTerm ρ ⊢Δ (ℕᵣ D) [t≡u] = wkEqTermℕ ρ ⊢Δ [t≡u]
wkEqTerm ρ ⊢Δ (Emptyᵣ D) [t≡u] = wkEqTermEmpty ρ ⊢Δ [t≡u]
wkEqTerm {ρ} {r = [ ! , l′ ]} [ρ] ⊢Δ (ne′ K D neK K≡K) (neₜ₌ k m d d′ nf) =
  neₜ₌ (U.wk ρ k) (U.wk ρ m)
       (wkRed:*:Term [ρ] ⊢Δ d) (wkRed:*:Term [ρ] ⊢Δ d′)
       (wkEqTermNe [ρ] ⊢Δ nf)
wkEqTerm {ρ} {r = [ % , l′ ]}[ρ] ⊢Δ (ne′ K D neK K≡K) (neₜ₌ d d′) = neₜ₌ (T.wkTerm [ρ] ⊢Δ d) (T.wkTerm [ρ] ⊢Δ d′)
wkEqTerm {ρ} {r = [ ! , l′ ]} [ρ] ⊢Δ (Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                    (Πₜ₌ f g d d′ funcF funcG f≡g [t] [u] [f≡g]) =
  let [A] = Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext
  in  Πₜ₌ (U.wk ρ f) (U.wk ρ g) (wkRed:*:Term [ρ] ⊢Δ d) (wkRed:*:Term [ρ] ⊢Δ d′)
          (wkFunction ρ funcF) (wkFunction ρ funcG)
          (≅ₜ-wk [ρ] ⊢Δ f≡g) (wkTerm [ρ] ⊢Δ [A] [t]) (wkTerm [ρ] ⊢Δ [A] [u])
          (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] →
             let [F]₁ = [F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁
                 [F]₂ = irrelevance′ (PE.sym (wk-comp ρ₁ ρ F)) [F]₁
                 [a]′ = irrelevanceTerm′ (wk-comp ρ₁ ρ F) PE.refl PE.refl [F]₂ [F]₁ [a]
                 [G]₁ = [G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′
                 [G]₂ = irrelevance′ (wk-comp-subst ρ₁ ρ G) [G]₁
             in  irrelevanceEqTerm″ PE.refl PE.refl (PE.cong (λ y → y ∘ _ ^ _) (PE.sym (wk-comp ρ₁ ρ _)))
                                     (PE.cong (λ y → y ∘ _ ^ _) (PE.sym (wk-comp ρ₁ ρ _)))
                                     (wk-comp-subst ρ₁ ρ G)
                                     [G]₁ [G]₂
                                     ([f≡g] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
wkEqTerm {ρ} {r = [ % , l′ ]} [ρ] ⊢Δ (Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                    (d , d′) = T.wkTerm [ρ] ⊢Δ d , T.wkTerm [ρ] ⊢Δ d′
wkEqTerm {ρ} {r = [ % , l′ ]} [ρ] ⊢Δ (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                    (d , d′) = T.wkTerm [ρ] ⊢Δ d , T.wkTerm [ρ] ⊢Δ d′
wkEqTerm {l = ι ¹} ρ ⊢Δ (emb l< X) t≡u = wkEqTerm ρ ⊢Δ X t≡u
wkEqTerm {l = ∞} ρ ⊢Δ (emb l< X) t≡u = wkEqTerm ρ ⊢Δ X t≡u


{-
This second-order term syntax was created from the following second-order syntax description:

syntax QIO

type
  T : 0-ary
  P : 0-ary

term
  new      : P.T  ->  T
  measure  : P  T  T  ->  T
  applyX   : P  P.T  ->  T
  applyI2  : P  P.T  ->  T
  applyDuv : P  P  (P,P).T  ->  T
  applyDu  : P  P.T  ->  T
  applyDv  : P  P.T  ->  T

theory
  (A) a:P   t u:T         |> applyX (a, b.measure(b, t, u)) = measure(a, u, t)
  (B) a:P   b:P  t u:P.T  |> measure(a, applyDu(b, b.t[b]), applyDv(b, b.u[b])) = applyDuv(a, b, a b.measure(a, t[b], u[b]))
  (D) t u:T               |> new(a.measure(a, t, u)) = t
  (E) b:P  t:(P, P).T     |> new(a.applyDuv(a, b, a b. t[a,b])) = applyDu(b, b.new(a.t[a,b]))
-}

module QIO.Syntax where

open import SOAS.Common
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Families.Core
open import SOAS.Construction.Structure
open import SOAS.ContextMaps.Inductive

open import SOAS.Metatheory.Syntax

open import QIO.Signature

private
  variable
    Γ Δ Π : Ctx
    α : QIOT
    𝔛 : Familyₛ

-- Inductive term declaration
module QIO:Terms (𝔛 : Familyₛ) where

  data QIO : Familyₛ where
    var  : ℐ ⇾̣ QIO
    mvar : 𝔛 α Π → Sub QIO Π Γ → QIO α Γ

    new      : QIO T (P ∙ Γ) → QIO T Γ
    measure  : QIO P Γ → QIO T Γ → QIO T Γ → QIO T Γ
    applyX   : QIO P Γ → QIO T (P ∙ Γ) → QIO T Γ
    applyI2  : QIO P Γ → QIO T (P ∙ Γ) → QIO T Γ
    applyDuv : QIO P Γ → QIO P Γ → QIO T (P ∙ P ∙ Γ) → QIO T Γ
    applyDu  : QIO P Γ → QIO T (P ∙ Γ) → QIO T Γ
    applyDv  : QIO P Γ → QIO T (P ∙ Γ) → QIO T Γ

  open import SOAS.Metatheory.MetaAlgebra ⅀F 𝔛

  QIOᵃ : MetaAlg QIO
  QIOᵃ = record
    { 𝑎𝑙𝑔 = λ where
      (newₒ      ⋮ a)         → new      a
      (measureₒ  ⋮ a , b , c) → measure  a b c
      (applyXₒ   ⋮ a , b)     → applyX   a b
      (applyI2ₒ  ⋮ a , b)     → applyI2  a b
      (applyDuvₒ ⋮ a , b , c) → applyDuv a b c
      (applyDuₒ  ⋮ a , b)     → applyDu  a b
      (applyDvₒ  ⋮ a , b)     → applyDv  a b
    ; 𝑣𝑎𝑟 = var ; 𝑚𝑣𝑎𝑟 = λ 𝔪 mε → mvar 𝔪 (tabulate mε) }

  module QIOᵃ = MetaAlg QIOᵃ

  module _ {𝒜 : Familyₛ}(𝒜ᵃ : MetaAlg 𝒜) where

    open MetaAlg 𝒜ᵃ

    𝕤𝕖𝕞 : QIO ⇾̣ 𝒜
    𝕊 : Sub QIO Π Γ → Π ~[ 𝒜 ]↝ Γ
    𝕊 (t ◂ σ) new = 𝕤𝕖𝕞 t
    𝕊 (t ◂ σ) (old v) = 𝕊 σ v
    𝕤𝕖𝕞 (mvar 𝔪 mε) = 𝑚𝑣𝑎𝑟 𝔪 (𝕊 mε)
    𝕤𝕖𝕞 (var v) = 𝑣𝑎𝑟 v

    𝕤𝕖𝕞 (new      a)     = 𝑎𝑙𝑔 (newₒ      ⋮ 𝕤𝕖𝕞 a)
    𝕤𝕖𝕞 (measure  a b c) = 𝑎𝑙𝑔 (measureₒ  ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b , 𝕤𝕖𝕞 c)
    𝕤𝕖𝕞 (applyX   a b)   = 𝑎𝑙𝑔 (applyXₒ   ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)
    𝕤𝕖𝕞 (applyI2  a b)   = 𝑎𝑙𝑔 (applyI2ₒ  ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)
    𝕤𝕖𝕞 (applyDuv a b c) = 𝑎𝑙𝑔 (applyDuvₒ ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b , 𝕤𝕖𝕞 c)
    𝕤𝕖𝕞 (applyDu  a b)   = 𝑎𝑙𝑔 (applyDuₒ  ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)
    𝕤𝕖𝕞 (applyDv  a b)   = 𝑎𝑙𝑔 (applyDvₒ  ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)

    𝕤𝕖𝕞ᵃ⇒ : MetaAlg⇒ QIOᵃ 𝒜ᵃ 𝕤𝕖𝕞
    𝕤𝕖𝕞ᵃ⇒ = record
      { ⟨𝑎𝑙𝑔⟩ = λ{ {t = t} → ⟨𝑎𝑙𝑔⟩ t }
      ; ⟨𝑣𝑎𝑟⟩ = refl
      ; ⟨𝑚𝑣𝑎𝑟⟩ = λ{ {𝔪 = 𝔪}{mε} → cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-tab mε)) }  }
      where
      open ≡-Reasoning
      ⟨𝑎𝑙𝑔⟩ : (t : ⅀ QIO α Γ) → 𝕤𝕖𝕞 (QIOᵃ.𝑎𝑙𝑔 t) ≡ 𝑎𝑙𝑔 (⅀₁ 𝕤𝕖𝕞 t)
      ⟨𝑎𝑙𝑔⟩ (newₒ      ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (measureₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (applyXₒ   ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (applyI2ₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (applyDuvₒ ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (applyDuₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (applyDvₒ  ⋮ _) = refl

      𝕊-tab : (mε : Π ~[ QIO ]↝ Γ)(v : ℐ α Π) → 𝕊 (tabulate mε) v ≡ 𝕤𝕖𝕞 (mε v)
      𝕊-tab mε new = refl
      𝕊-tab mε (old v) = 𝕊-tab (mε ∘ old) v

    module _ (g : QIO ⇾̣ 𝒜)(gᵃ⇒ : MetaAlg⇒ QIOᵃ 𝒜ᵃ g) where

      open MetaAlg⇒ gᵃ⇒

      𝕤𝕖𝕞! : (t : QIO α Γ) → 𝕤𝕖𝕞 t ≡ g t
      𝕊-ix : (mε : Sub QIO Π Γ)(v : ℐ α Π) → 𝕊 mε v ≡ g (index mε v)
      𝕊-ix (x ◂ mε) new = 𝕤𝕖𝕞! x
      𝕊-ix (x ◂ mε) (old v) = 𝕊-ix mε v
      𝕤𝕖𝕞! (mvar 𝔪 mε) rewrite cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-ix mε))
        = trans (sym ⟨𝑚𝑣𝑎𝑟⟩) (cong (g ∘ mvar 𝔪) (tab∘ix≈id mε))
      𝕤𝕖𝕞! (var v) = sym ⟨𝑣𝑎𝑟⟩

      𝕤𝕖𝕞! (new a) rewrite 𝕤𝕖𝕞! a = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (measure a b c) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b | 𝕤𝕖𝕞! c = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (applyX a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (applyI2 a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (applyDuv a b c) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b | 𝕤𝕖𝕞! c = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (applyDu a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (applyDv a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩

-- Syntax instance for the signature
QIO:Syn : Syntax
QIO:Syn = record
  { ⅀F = ⅀F
  ; ⅀:CS = ⅀:CompatStr
  ; mvarᵢ = QIO:Terms.mvar
  ; 𝕋:Init = λ 𝔛 → let open QIO:Terms 𝔛 in record
    { ⊥ = QIO ⋉ QIOᵃ
    ; ⊥-is-initial = record { ! = λ{ {𝒜 ⋉ 𝒜ᵃ} → 𝕤𝕖𝕞 𝒜ᵃ ⋉ 𝕤𝕖𝕞ᵃ⇒ 𝒜ᵃ }
    ; !-unique = λ{ {𝒜 ⋉ 𝒜ᵃ} (f ⋉ fᵃ⇒) {x = t} → 𝕤𝕖𝕞! 𝒜ᵃ f fᵃ⇒ t } } } }

-- Instantiation of the syntax and metatheory
open Syntax QIO:Syn public
open QIO:Terms public
open import SOAS.Families.Build public
open import SOAS.Syntax.Shorthands QIOᵃ public
open import SOAS.Metatheory QIO:Syn public


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

open import HoTT
open import homotopy.Bouquet
open import homotopy.FinWedge
open import cohomology.Theory

module cohomology.SubFinBouquet (OT : OrdinaryTheory lzero) where

open OrdinaryTheory OT
open import cohomology.Sphere OT
open import cohomology.SubFinWedge cohomology-theory
open import cohomology.Bouquet OT

C-SubFinBouquet-diag : ∀ n {A B : Type₀} (B-ac : has-choice 0 B lzero) {I} (p : Fin I → Coprod A B)
  → C (ℕ-to-ℤ n) (⊙Bouquet B n) ≃ᴳ Πᴳ B (λ _ → C2 0)
C-SubFinBouquet-diag n {B = B} B-ac p = C-Bouquet-diag n B B-ac

C-FinBouquet-diag : ∀ n I → C (ℕ-to-ℤ n) (⊙FinBouquet I n) ≃ᴳ Πᴳ (Fin I) (λ _ → C2 0)
C-FinBouquet-diag n I = C-SubFinBouquet-diag n {A = Empty} {B = Fin I} (Fin-has-choice 0 lzero) inr

abstract
  C-SubFinBouquet-diag-β : ∀ n {A B : Type₀} (B-ac : has-choice 0 B lzero) {I} (p : Fin I → Coprod A B) g b
    →  GroupIso.f (C-FinBouquet-diag n I) g b
    == GroupIso.f (C-Sphere-diag n)
        (CEl-fmap (ℕ-to-ℤ n) ⊙lower
          (CEl-fmap (ℕ-to-ℤ n) (⊙bwin b) g))
  C-SubFinBouquet-diag-β n B-ac p g b =
    GroupIso.f (C-Sphere-diag n)
      (CEl-fmap (ℕ-to-ℤ n) (⊙bwin b)
        (CEl-fmap (ℕ-to-ℤ n) (⊙–> (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv))) g))
      =⟨ ap (GroupIso.f (C-Sphere-diag n)) $
            ∘-CEl-fmap (ℕ-to-ℤ n) (⊙bwin b) (⊙–> (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv))) g
          ∙ CEl-fmap-base-indep (ℕ-to-ℤ n) (λ _ → idp) g
          ∙ CEl-fmap-∘ (ℕ-to-ℤ n) (⊙bwin b) ⊙lower g ⟩
    GroupIso.f (C-Sphere-diag n) (CEl-fmap (ℕ-to-ℤ n) ⊙lower (CEl-fmap (ℕ-to-ℤ n) (⊙bwin b) g))
      =∎

  C-FinBouquet-diag-β : ∀ n I g <I
    →  GroupIso.f (C-FinBouquet-diag n I) g <I
    == GroupIso.f (C-Sphere-diag n)
        (CEl-fmap (ℕ-to-ℤ n) ⊙lower
          (CEl-fmap (ℕ-to-ℤ n) (⊙bwin <I) g))
  C-FinBouquet-diag-β n I =
    C-SubFinBouquet-diag-β n {A = Empty} {B = Fin I}
      (Fin-has-choice 0 lzero) inr

  inverse-C-SubFinBouquet-diag-β : ∀ n
    {A B : Type₀} (B-ac : has-choice 0 B lzero) (B-dec : has-dec-eq B) {I} (p : Fin I ≃ Coprod A B) g
    →  GroupIso.g (C-SubFinBouquet-diag n B-ac (–> p)) g
    == Group.subsum-r (C (ℕ-to-ℤ n) (⊙Bouquet B n)) (–> p)
        (λ b → CEl-fmap (ℕ-to-ℤ n) (⊙bwproj B-dec b)
          (CEl-fmap (ℕ-to-ℤ n) ⊙lift
            (GroupIso.g (C-Sphere-diag n) (g b))))
  inverse-C-SubFinBouquet-diag-β n {B = B} B-ac B-dec p g =
    CEl-fmap (ℕ-to-ℤ n) (⊙<– (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv)))
      (GroupIso.g (C-subfinite-additive-iso (ℕ-to-ℤ n) (–> p) (⊙Lift (⊙Sphere n)) B-ac)
        (GroupIso.g (C-Sphere-diag n) ∘ g))
      =⟨ ap (CEl-fmap (ℕ-to-ℤ n) (⊙<– (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv)))) $
          inverse-C-subfinite-additive-β (ℕ-to-ℤ n) B-ac B-dec p (GroupIso.g (C-Sphere-diag n) ∘ g) ⟩
    CEl-fmap (ℕ-to-ℤ n) (⊙<– (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv)))
      (Group.subsum-r (C (ℕ-to-ℤ n) (⊙BouquetLift B n)) (–> p)
        (λ b → CEl-fmap (ℕ-to-ℤ n) (⊙bwproj B-dec b) (GroupIso.g (C-Sphere-diag n) (g b))))
      =⟨ GroupHom.pres-subsum-r (C-fmap (ℕ-to-ℤ n) (⊙<– (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv)))) (–> p) $
          (λ b → CEl-fmap (ℕ-to-ℤ n) (⊙bwproj B-dec b) (GroupIso.g (C-Sphere-diag n) (g b))) ⟩
    Group.subsum-r (C (ℕ-to-ℤ n) (⊙Bouquet B n)) (–> p)
      (λ b →
        CEl-fmap (ℕ-to-ℤ n) (⊙<– (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv)))
          (CEl-fmap (ℕ-to-ℤ n) (⊙bwproj B-dec b) (GroupIso.g (C-Sphere-diag n) (g b))))
      =⟨ ap (Group.subsum-r (C (ℕ-to-ℤ n) (⊙Bouquet B n)) (–> p))
          (λ= λ b →
              ∘-CEl-fmap (ℕ-to-ℤ n)
                (⊙<– (⊙BigWedge-emap-r (λ _ → ⊙lower-equiv))) (⊙bwproj B-dec b)
                (GroupIso.g (C-Sphere-diag n) (g b))
            ∙ CEl-fmap-base-indep (ℕ-to-ℤ n)
                (bwproj-BigWedge-emap-r-lift B-dec b)
                (GroupIso.g (C-Sphere-diag n) (g b))
            ∙ CEl-fmap-∘ (ℕ-to-ℤ n) ⊙lift (⊙bwproj B-dec b)
                (GroupIso.g (C-Sphere-diag n) (g b))) ⟩
    Group.subsum-r (C (ℕ-to-ℤ n) (⊙Bouquet B n)) (–> p)
      (λ b →
        CEl-fmap (ℕ-to-ℤ n) (⊙bwproj B-dec b)
          (CEl-fmap (ℕ-to-ℤ n) ⊙lift
            (GroupIso.g (C-Sphere-diag n) (g b))))
      =∎

  inverse-C-FinBouquet-diag-β : ∀ n I g
    →  GroupIso.g (C-FinBouquet-diag n I) g
    == Group.sum (C (ℕ-to-ℤ n) (⊙FinBouquet I n))
        (λ <I → CEl-fmap (ℕ-to-ℤ n) (⊙fwproj <I)
          (CEl-fmap (ℕ-to-ℤ n) ⊙lift
            (GroupIso.g (C-Sphere-diag n) (g <I))))
  inverse-C-FinBouquet-diag-β n I =
    inverse-C-SubFinBouquet-diag-β n {A = Empty} {B = Fin I}
      (Fin-has-choice 0 lzero) Fin-has-dec-eq (⊔₁-Empty (Fin I) ⁻¹)


-- V: letpair (x,y) = (V,W) in E --> E[ V,W / x,y ]
module Properties.StepPair where

open import Data.List
open import Data.List.All
open import Data.Product

open import Relation.Nullary
open import Relation.Binary.PropositionalEquality

open import Typing
open import Syntax
open import Global
open import Values
open import Session

open import Properties.Base

-- this will require multiple steps to execute
mklhs : ∀ {Φ t₁ t₂} → Expr (t₁ ∷ t₂ ∷ Φ) TUnit → Expr (t₁ ∷ t₂ ∷ Φ) TUnit
mklhs {Φ} E =
  letbind (left (left (split-all-right Φ))) (pair (left (rght [])) (here []) (here []))
  (letpair (left (split-all-right Φ)) (here []) E)

reduction :  ∀ {t₁ t₂}
  → (E : Expr (t₁ ∷ t₂ ∷ []) TUnit)
  → (v₁ : Val [] t₁)
  → (v₂ : Val [] t₂)
  → let ϱ = vcons ss-[] v₁ (vcons ss-[] v₂ (vnil []-inactive)) in
    let lhs = (run (left (left [])) ss-[] (mklhs E) ϱ (halt []-inactive UUnit)) in
    let rhs = (run (left (left [])) ss-[] E ϱ (halt []-inactive UUnit)) in
    restart lhs ≡
    rhs
reduction E v₁ v₂
  with split-env (left (left [])) (vcons ss-[] v₁ (vcons ss-[] v₂ (vnil []-inactive)))
... | spe
  = refl

reduction-open-type : Set
reduction-open-type = ∀ {Φ t₁ t₂}
  (E : Expr (t₁ ∷ t₂ ∷ Φ) TUnit)
  (ϱ : VEnv [] (t₁ ∷ t₂ ∷ Φ))
  →
    let lhs = run (left (left (split-all-left Φ))) ss-[] (mklhs E) ϱ (halt []-inactive UUnit) in
    let rhs = run (left (left (split-all-left _))) ss-[] E ϱ (halt []-inactive UUnit) in
    restart lhs ≡ rhs

reduction-open : reduction-open-type
reduction-open {Φ} E (vcons ss-[] v (vcons ss-[] v₁ ϱ))
  rewrite split-rotate-lemma {Φ}
  | split-env-right-lemma0 ϱ
  with ssplit-compose3 ss-[] ss-[]
... | ssc3
    rewrite split-env-right-lemma0 ϱ
     | split-rotate-lemma {Φ}
  = refl


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Properties.Transitivity {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties.Conversion

open import Tools.Embedding
open import Tools.Product
import Tools.PropositionalEquality as PE

mutual
  -- Helper function for transitivity of type equality using shape views.
  transEqT : ∀ {Γ A B C l l′ l″}
             {[A] : Γ ⊩⟨ l ⟩ A} {[B] : Γ ⊩⟨ l′ ⟩ B} {[C] : Γ ⊩⟨ l″ ⟩ C}
           → ShapeView₃ Γ l l′ l″ A B C [A] [B] [C]
           → Γ ⊩⟨ l ⟩  A ≡ B / [A]
           → Γ ⊩⟨ l′ ⟩ B ≡ C / [B]
           → Γ ⊩⟨ l ⟩  A ≡ C / [A]
  transEqT (ℕᵥ D D′ D″) A≡B (ιx (ℕ₌ B≡C)) = ιx (ℕ₌ B≡C)
  transEqT (ne (ne K [ ⊢A , ⊢B , D ] neK K≡K) (ne K₁ D₁ neK₁ _)
               (ne K₂ D₂ neK₂ _))
           (ιx (ne₌ M D′ neM K≡M)) (ιx (ne₌ M₁ D″ neM₁ K≡M₁))
           rewrite whrDet* (red D₁ , ne neK₁) (red D′ , ne neM)
                 | whrDet* (red D₂ , ne neK₂) (red D″ , ne neM₁) =
    ιx (ne₌ M₁ D″ neM₁
        (~-trans K≡M K≡M₁))
  transEqT {Γ} {l = l} {l′ = l′} {l″ = l″}
           (Πᵥ (Πᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
               (Πᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁)
               (Πᵣ F₂ G₂ D₂ ⊢F₂ ⊢G₂ A≡A₂ [F]₂ [G]₂ G-ext₂))
           (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
           (Π₌ F″ G″ D″ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
    let ΠF₁G₁≡ΠF′G′    = whrDet* (red D₁ , Πₙ) (D′  , Πₙ)
        F₁≡F′  , G₁≡G′ = Π-PE-injectivity ΠF₁G₁≡ΠF′G′
        F₂≡F″ , G₂≡G″  = Π-PE-injectivity (whrDet* (red D₂ , Πₙ) (D″ , Πₙ))
        substLift {Δ} {l} {a} ρ x = Δ ⊩⟨ l ⟩ wk (lift ρ) x [ a ]
        [F′] : ∀ {ρ Δ} [ρ] ⊢Δ → Δ ⊩⟨ l′ ⟩ wk ρ F′
        [F′] {ρ} [ρ] ⊢Δ = PE.subst (λ x → _ ⊩⟨ _ ⟩ wk ρ x) F₁≡F′ ([F]₁ [ρ] ⊢Δ)
        [F″] : ∀ {ρ} {Δ} [ρ] ⊢Δ → Δ ⊩⟨ l″ ⟩ wk ρ F″
        [F″] {ρ} [ρ] ⊢Δ = PE.subst (λ x → _ ⊩⟨ _ ⟩ wk ρ x) F₂≡F″ ([F]₂ [ρ] ⊢Δ)
        [F′≡F″] : ∀ {ρ} {Δ} [ρ] ⊢Δ → Δ ⊩⟨ l′ ⟩ wk ρ F′ ≡ wk ρ F″ / [F′] [ρ] ⊢Δ
        [F′≡F″] {ρ} [ρ] ⊢Δ = irrelevanceEq′ (PE.cong (wk ρ) F₁≡F′)
                                      ([F]₁ [ρ] ⊢Δ) ([F′] [ρ] ⊢Δ) ([F≡F′]₁ [ρ] ⊢Δ)
        [G′] : ∀ {ρ Δ a} [ρ] ⊢Δ
             → Δ ⊩⟨ l′ ⟩ a ∷ wk ρ F′ / [F′] [ρ] ⊢Δ
             → Δ ⊩⟨ l′ ⟩ wk (lift ρ) G′ [ a ]
        [G′] {ρ} [ρ] ⊢Δ [a] =
          let [a′] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a]
          in  PE.subst (substLift ρ) G₁≡G′ ([G]₁ [ρ] ⊢Δ [a′])
        [G″] : ∀ {ρ Δ a} [ρ] ⊢Δ
             → Δ ⊩⟨ l″ ⟩ a ∷ wk ρ F″ / [F″] [ρ] ⊢Δ
             → Δ ⊩⟨ l″ ⟩ wk (lift ρ) G″ [ a ]
        [G″] {ρ} [ρ] ⊢Δ [a] =
          let [a″] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₂≡F″))
                                      ([F″] [ρ] ⊢Δ) ([F]₂ [ρ] ⊢Δ) [a]
          in  PE.subst (substLift ρ) G₂≡G″ ([G]₂ [ρ] ⊢Δ [a″])
        [G′≡G″] : ∀ {ρ Δ a} [ρ] ⊢Δ
                  ([a] : Δ ⊩⟨ l′ ⟩ a ∷ wk ρ F′ / [F′] [ρ] ⊢Δ)
                → Δ ⊩⟨ l′ ⟩ wk (lift ρ) G′  [ a ]
                          ≡ wk (lift ρ) G″ [ a ] / [G′] [ρ] ⊢Δ [a]
        [G′≡G″] {ρ} [ρ] ⊢Δ [a′] =
          let [a]₁ = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a′]
          in  irrelevanceEq′ (PE.cong (λ x → wk (lift ρ) x [ _ ]) G₁≡G′)
                             ([G]₁ [ρ] ⊢Δ [a]₁) ([G′] [ρ] ⊢Δ [a′])
                             ([G≡G′]₁ [ρ] ⊢Δ [a]₁)
    in  Π₌ F″ G″ D″ (≅-trans A≡B (PE.subst (λ x → Γ ⊢ x ≅ Π F″ ▹ G″) ΠF₁G₁≡ΠF′G′ A≡B₁))
           (λ ρ ⊢Δ → transEq ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ)
                             ([F≡F′] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ))
           (λ ρ ⊢Δ [a] →
              let [a′] = convTerm₁ ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F≡F′] ρ ⊢Δ) [a]
                  [a″] = convTerm₁ ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ) [a′]
              in  transEq ([G] ρ ⊢Δ [a]) ([G′] ρ ⊢Δ [a′]) ([G″] ρ ⊢Δ [a″])
                          ([G≡G′] ρ ⊢Δ [a]) ([G′≡G″] ρ ⊢Δ [a′]))
  transEqT (Uᵥ _ _ _ _ _ _ _ _ _) A≡B B≡C = A≡B
  transEqT (emb⁰¹¹ PE.refl AB) (ιx A≡B) B≡C = ιx (transEqT AB A≡B B≡C)
  transEqT (emb¹⁰¹ PE.refl AB) A≡B (ιx B≡C) = transEqT AB A≡B B≡C
  transEqT (emb¹¹⁰ PE.refl AB) A≡B B≡C = transEqT AB A≡B B≡C

  -- Transitivty of type equality.
  transEq : ∀ {Γ A B C l l′ l″}
            ([A] : Γ ⊩⟨ l ⟩ A) ([B] : Γ ⊩⟨ l′ ⟩ B) ([C] : Γ ⊩⟨ l″ ⟩ C)
          → Γ ⊩⟨ l ⟩  A ≡ B / [A]
          → Γ ⊩⟨ l′ ⟩ B ≡ C / [B]
          → Γ ⊩⟨ l ⟩  A ≡ C / [A]
  transEq [A] [B] [C] A≡B B≡C =
    transEqT (combine (goodCases [A] [B] A≡B) (goodCases [B] [C] B≡C)) A≡B B≡C

  -- Transitivty of type equality with some propositonally equal types.
  transEq′ : ∀ {Γ A B B′ C C′ l l′ l″} → B PE.≡ B′ → C PE.≡ C′
           → ([A] : Γ ⊩⟨ l ⟩ A) ([B] : Γ ⊩⟨ l′ ⟩ B) ([C] : Γ ⊩⟨ l″ ⟩ C)
           → Γ ⊩⟨ l ⟩  A ≡ B′ / [A]
           → Γ ⊩⟨ l′ ⟩ B ≡ C′ / [B]
           → Γ ⊩⟨ l ⟩  A ≡ C  / [A]
  transEq′ PE.refl PE.refl [A] [B] [C] A≡B B≡C = transEq [A] [B] [C] A≡B B≡C

transEqTermNe : ∀ {Γ n n′ n″ A}
              → Γ ⊩neNf n  ≡ n′  ∷ A
              → Γ ⊩neNf n′ ≡ n″ ∷ A
              → Γ ⊩neNf n  ≡ n″ ∷ A
transEqTermNe (neNfₜ₌ neK neM k≡m) (neNfₜ₌ neK₁ neM₁ k≡m₁) =
  neNfₜ₌ neK neM₁ (~-trans k≡m k≡m₁)

mutual
  transEqTermℕ : ∀ {Γ n n′ n″}
               → _⊩ℕ_≡_∷ℕ Γ n n′
               → _⊩ℕ_≡_∷ℕ Γ n′ n″
               → _⊩ℕ_≡_∷ℕ Γ n n″
  transEqTermℕ (ℕₜ₌ k k′ d d′ t≡u prop)
               (ℕₜ₌ k₁ k″ d₁ d″ t≡u₁ prop₁) =
    let k₁Whnf = naturalWhnf (proj₁ (split prop₁))
        k′Whnf = naturalWhnf (proj₂ (split prop))
        k₁≡k′ = whrDet*Term (redₜ d₁ , k₁Whnf) (redₜ d′ , k′Whnf)
        prop′ = PE.subst (λ x → [Natural]-prop _ x _) k₁≡k′ prop₁
    in  ℕₜ₌ k k″ d d″ (≅ₜ-trans t≡u (PE.subst (λ x → _ ⊢ x ≅ _ ∷ _) k₁≡k′ t≡u₁))
            (transNatural-prop prop prop′)

  transNatural-prop : ∀ {Γ k k′ k″}
                    → [Natural]-prop Γ k k′
                    → [Natural]-prop Γ k′ k″
                    → [Natural]-prop Γ k k″
  transNatural-prop (sucᵣ x) (sucᵣ x₁) = sucᵣ (transEqTermℕ x x₁)
  transNatural-prop (sucᵣ x) (ne (neNfₜ₌ () neM k≡m))
  transNatural-prop zeroᵣ prop₁ = prop₁
  transNatural-prop prop zeroᵣ = prop
  transNatural-prop (ne (neNfₜ₌ neK () k≡m)) (sucᵣ x₃)
  transNatural-prop (ne [k≡k′]) (ne [k′≡k″]) =
    ne (transEqTermNe [k≡k′] [k′≡k″])

-- Transitivty of term equality.
transEqTerm : ∀ {l Γ A t u v}
              ([A] : Γ ⊩⟨ l ⟩ A)
            → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / [A]
            → Γ ⊩⟨ l ⟩ u ≡ v ∷ A / [A]
            → Γ ⊩⟨ l ⟩ t ≡ v ∷ A / [A]
transEqTerm (Uᵣ′ .⁰ 0<1 ⊢Γ)
            (Uₜ₌ A B d d′ typeA typeB t≡u [t] [u] [t≡u])
            (Uₜ₌ A₁ B₁ d₁ d₁′ typeA₁ typeB₁ t≡u₁ [t]₁ [u]₁ [t≡u]₁)
            rewrite whrDet*Term (redₜ d′ , typeWhnf typeB) (redₜ d₁ , typeWhnf typeA₁) =
  Uₜ₌ A B₁ d d₁′ typeA typeB₁ (≅ₜ-trans t≡u t≡u₁) [t] [u]₁
      (transEq [t] [u] [u]₁ [t≡u] (irrelevanceEq [t]₁ [u] [t≡u]₁))
transEqTerm (ℕᵣ D) (ιx [t≡u]) (ιx [u≡v]) = ιx (transEqTermℕ [t≡u] [u≡v])
transEqTerm (ne′ K D neK K≡K) (ιx (neₜ₌ k m d d′ (neNfₜ₌ neK₁ neM k≡m)))
                              (ιx (neₜ₌ k₁ m₁ d₁ d″ (neNfₜ₌ neK₂ neM₁ k≡m₁))) =
  let k₁≡m = whrDet*Term (redₜ d₁ , ne neK₂) (redₜ d′ , ne neM)
  in ιx (neₜ₌ k m₁ d d″
           (neNfₜ₌ neK₁ neM₁
                   (~-trans k≡m (PE.subst (λ x → _ ⊢ x ~ _ ∷ _) k₁≡m k≡m₁))))
transEqTerm (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g])
            (Πₜ₌ f₁ g₁ d₁ d₁′ funcF₁ funcG₁ f≡g₁ [f]₁ [g]₁ [f≡g]₁)
            rewrite whrDet*Term (redₜ d′ , functionWhnf funcG)
                            (redₜ d₁ , functionWhnf funcF₁) =
  Πₜ₌ f g₁ d d₁′ funcF funcG₁ (≅ₜ-trans f≡g f≡g₁) [f] [g]₁
      (λ ρ ⊢Δ [a] → transEqTerm ([G] ρ ⊢Δ [a])
                                ([f≡g] ρ ⊢Δ [a])
                                ([f≡g]₁ ρ ⊢Δ [a]))
transEqTerm (emb′ 0<1 x) (ιx t≡u) (ιx u≡v) = ιx (transEqTerm x t≡u u≡v)


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

open import Calf.CostMonoid

-- Upper bound on the cost of a computation.

module Calf.Types.Bounded (costMonoid : CostMonoid) where

open CostMonoid costMonoid

open import Calf.Prelude
open import Calf.Metalanguage
open import Calf.PhaseDistinction
open import Calf.Types.Eq
open import Calf.Step costMonoid

open import Calf.Types.Bool
open import Calf.Types.Sum

open import Relation.Binary.PropositionalEquality as Eq

record IsBounded (A : tp pos) (e : cmp (F A)) (c : cmp cost) : □ where
  constructor ⇓_withCost_[_,_]
  field
    result : val A
    c' : ℂ
    h-bounded : ◯ (c' ≤ c)
    h-≡ : cmp (F (eq (U (F A)) e (step (F A) c' (ret result))))

IsBounded⁻ : (A : tp pos) → cmp (F A) → cmp cost → tp neg
IsBounded⁻ A e p = meta (IsBounded A e p)

bound/relax : ∀ {A e p p'} → ◯ (p ≤ p') → IsBounded A e p → IsBounded A e p'
bound/relax h (⇓ result withCost c' [ h-bounded , h-≡ ]) = ⇓ result withCost c' [ (λ u → ≤-trans (h-bounded u) (h u)) , h-≡ ]

bound/circ : ∀ {A e} d {c} →
  IsBounded A e c →
  IsBounded A e (step (meta ℂ) d c)
bound/circ d {c} (⇓ a withCost c' [ h-bounded , h-≡ ]) =
  ⇓ a withCost c' [ (λ u → subst (c' ≤_) (sym (step/ext cost c d u)) (h-bounded u)) , h-≡ ]

bound/ret : {A : tp pos} {a : val A} → IsBounded A (ret {A} a) zero
bound/ret {a = a} = ⇓ a withCost zero [ (λ u → ≤-refl) , ret (eq/intro refl) ]

bound/step : ∀ {A e} (d c : ℂ) →
  IsBounded A e c →
  IsBounded A (step (F A) d e) (d + c)
bound/step d c (⇓ a withCost c' [ h-bounded , h-≡ ]) with eq/ref h-≡
... | refl = ⇓ a withCost d + c' [ (λ u → +-monoʳ-≤ d (h-bounded u)) , ret (eq/intro refl) ]

bound/bind : ∀ {A B : tp pos} {e : cmp (F A)} {f : val A → cmp (F B)}
  (c : ℂ) (d : val A → ℂ) →
  IsBounded A e c →
  ((a : val A) → IsBounded B (f a) (d a)) →
  IsBounded B (bind {A} (F B) e f)
              (bind {A} cost e (λ a → c + d a))
bound/bind {f = f} c d (⇓ a withCost c' [ h-bounded₁ , h-≡₁ ]) h with eq/ref h-≡₁
... | refl with h a
... | ⇓ b withCost d' [ h-bounded₂ , h-≡₂ ] with f a | eq/ref h-≡₂
... | _ | refl = bound/circ c' (⇓ b withCost c' + d' [ (λ u → +-mono-≤ (h-bounded₁ u) (h-bounded₂ u)) , ret (eq/intro refl) ])

bound/bind/const : ∀ {A B : tp pos} {e : cmp (F A)} {f : val A → cmp (F B)}
  (c d : ℂ) →
  IsBounded A e c →
  ((a : val A) → IsBounded B (f a) d) →
  IsBounded B (bind {A} (F B) e f) (c + d)
bound/bind/const c d (⇓ a withCost c' [ h-bounded , h-≡ ]) h with eq/ref h-≡
... | refl = bound/circ' c' (bound/bind c (λ _ → d) (⇓ a withCost c' [ h-bounded , h-≡ ]) h)
  where
    bound/circ' : ∀ {A e} d {c} →
      IsBounded A e (step (meta ℂ) d c) →
      IsBounded A e c
    bound/circ' d {c} (⇓ a withCost c' [ h-bounded , h-≡ ]) =
      ⇓ a withCost c' [ (λ u → subst (c' ≤_) (step/ext cost c d u) (h-bounded u)) , h-≡ ]

bound/bool : ∀ {A : tp pos} {e0 e1} {p : val bool → cmp cost} →
  (b : val bool) →
  IsBounded A e0 (p false) →
  IsBounded A e1 (p true ) →
  IsBounded A (if b then e1 else e0) (p b)
bound/bool false h0 h1 = h0
bound/bool true  h0 h1 = h1

bound/sum/case/const/const : ∀ A B (C : val (sum A B) → tp pos) →
  (s : val (sum A B)) →
  (e0 : (a : val A) → cmp (F (C (inj₁ a)))) →
  (e1 : (b : val B) → cmp (F (C (inj₂ b)))) →
  (p : ℂ) →
  ((a : val A) → IsBounded (C (inj₁ a)) (e0 a) p) →
  ((b : val B) → IsBounded (C (inj₂ b)) (e1 b) p) →
  IsBounded (C s) (sum/case A B (λ s → F (C s)) s e0 e1) p
bound/sum/case/const/const A B C s e0 e1 p h1 h2 = sum/case A B
  (λ s → meta (IsBounded (C s) (sum/case A B (λ s₁ → F (C s₁)) s e0 e1) p)) s h1 h2


module STLC2.Kovacs.Normalisation where

open import STLC2.Kovacs.NormalForm public

--------------------------------------------------------------------------------

-- (Tyᴺ)
mutual
  infix 3 _⊩_
  _⊩_ : 𝒞 → 𝒯 → Set

  Γ ⊩ ⎵      = Γ ⊢ⁿᶠ ⎵

  Γ ⊩ A ⇒ B = ∀ {Γ′} → (η : Γ′ ⊇ Γ) (∂a : Γ′ ∂⊩ A)
                       → Γ′ ∂⊩ B

  Γ ⊩ A ⩕ B  = Γ ∂⊩ A × Γ ∂⊩ B

  Γ ⊩ ⫪     = ⊤

  Γ ⊩ ⫫     = ⊥

  Γ ⊩ A ⩖ B  = Γ ∂⊩ A ⊎ Γ ∂⊩ B

  infix 3 _∂⊩_
  _∂⊩_ : 𝒞 → 𝒯 → Set
  Γ ∂⊩ A = ∀ {Γ′ C} → (η : Γ′ ⊇ Γ)
                     → (f : ∀ {Γ″} → Γ″ ⊇ Γ′ → Γ″ ⊩ A → Γ″ ⊢ⁿᶠ C)
                     → Γ′ ⊢ⁿᶠ C

-- (Conᴺ ; ∙ ; _,_)
infix 3 _∂⊩⋆_
data _∂⊩⋆_ : 𝒞 → 𝒞 → Set
  where
    ∅   : ∀ {Γ} → Γ ∂⊩⋆ ∅

    _,_ : ∀ {Γ Ξ A} → (ρ : Γ ∂⊩⋆ Ξ) (∂a : Γ ∂⊩ A)
                    → Γ ∂⊩⋆ Ξ , A

--------------------------------------------------------------------------------

-- (Tyᴺₑ)
mutual
  acc : ∀ {A Γ Γ′} → Γ′ ⊇ Γ → Γ ⊩ A → Γ′ ⊩ A
  acc {⎵}      η M        = renⁿᶠ η M
  acc {A ⇒ B} η f        = λ η′ a → f (η ○ η′) a
  acc {A ⩕ B}  η s        = ∂acc η (proj₁ s) , ∂acc η (proj₂ s)
  acc {⫪}     η s        = tt
  acc {⫫}     η s        = elim⊥ s
  acc {A ⩖ B}  η (inj₁ a) = inj₁ (∂acc η a)
  acc {A ⩖ B}  η (inj₂ b) = inj₂ (∂acc η b)
  -- TODO: Why doesn’t this work?
  -- acc {A ⩖ B}  η s = case⊎ s (λ a → ∂acc η a)
  --                            (λ b → ∂acc η b)

  ∂acc : ∀ {A Γ Γ′} → Γ′ ⊇ Γ → Γ ∂⊩ A → Γ′ ∂⊩ A
  ∂acc η ∂a = λ η′ f → ∂a (η ○ η′) f

-- (Conᴺₑ)
-- NOTE: _⬖_ = ∂acc⋆
_⬖_ : ∀ {Γ Γ′ Ξ} → Γ ∂⊩⋆ Ξ → Γ′ ⊇ Γ → Γ′ ∂⊩⋆ Ξ
∅        ⬖ η = ∅
(ρ , ∂a) ⬖ η = ρ ⬖ η , ∂acc η ∂a

--------------------------------------------------------------------------------

!ƛ : ∀ {Γ A B} → (∀ {Γ′} → Γ′ ⊇ Γ → Γ′ ∂⊩ A → Γ′ ∂⊩ B)
               → Γ ⊩ A ⇒ B
!ƛ f = λ η ∂a → f η ∂a

_!∙_ : ∀ {Γ A B} → Γ ⊩ A ⇒ B → Γ ∂⊩ A
                 → Γ ∂⊩ B
f !∙ ∂a = f idₑ ∂a

_!,_ : ∀ {Γ A B} → Γ ∂⊩ A → Γ ∂⊩ B
                 → Γ ⊩ A ⩕ B
∂a !, ∂b = ∂a , ∂b

!π₁ : ∀ {Γ A B} → Γ ⊩ A ⩕ B
                → Γ ∂⊩ A
!π₁ s = proj₁ s

!π₂ : ∀ {Γ A B} → Γ ⊩ A ⩕ B
                → Γ ∂⊩ B
!π₂ s = proj₂ s

!τ : ∀ {Γ} → Γ ⊩ ⫪
!τ = tt

!φ : ∀ {Γ C} → Γ ⊩ ⫫
             → Γ ∂⊩ C
!φ s = elim⊥ s

!ι₁ : ∀ {Γ A B} → Γ ∂⊩ A
                → Γ ⊩ A ⩖ B
!ι₁ ∂a = inj₁ ∂a

!ι₂ : ∀ {Γ A B} → Γ ∂⊩ B
                → Γ ⊩ A ⩖ B
!ι₂ ∂b = inj₂ ∂b

_!⁇_!∥_ : ∀ {Γ A B C} → Γ ⊩ A ⩖ B
                      → (∀ {Γ′} → Γ′ ⊇ Γ → Γ′ ∂⊩ A → Γ′ ∂⊩ C)
                      → (∀ {Γ′} → Γ′ ⊇ Γ → Γ′ ∂⊩ B → Γ′ ∂⊩ C)
                      → Γ ∂⊩ C
s !⁇ f !∥ g = elim⊎ s (λ ∂a → f idₑ ∂a)
                      (λ ∂b → g idₑ ∂b)

--------------------------------------------------------------------------------

return : ∀ {A Γ} → Γ ⊩ A → Γ ∂⊩ A
return {A} a = λ η f →
                 f idₑ (acc {A} η a)

bind : ∀ {A C Γ} → Γ ∂⊩ A → (∀ {Γ′} → Γ′ ⊇ Γ → Γ′ ⊩ A → Γ′ ∂⊩ C)
                 → Γ ∂⊩ C
bind ∂a f = λ η f′ →
              ∂a η (λ η′ a →
                f (η ○ η′) a idₑ (λ η″ b →
                  f′ (η′ ○ η″) b))

--------------------------------------------------------------------------------

∂!λ : ∀ {Γ A B} → (∀ {Γ′} → Γ′ ⊇ Γ → Γ′ ∂⊩ A → Γ′ ∂⊩ B)
                → Γ ∂⊩ A ⇒ B
∂!λ {A = A} f = return {A ⇒ _}
                       (!ƛ f)

_∂!∙_ : ∀ {Γ A B} → Γ ∂⊩ A ⇒ B → Γ ∂⊩ A
                  → Γ ∂⊩ B
∂f ∂!∙ ∂a = bind ∂f (λ η f → f !∙ ∂acc η ∂a)

_∂!,_ : ∀ {Γ A B} → Γ ∂⊩ A → Γ ∂⊩ B
                  → Γ ∂⊩ A ⩕ B
∂a ∂!, ∂b = return (∂a !, ∂b)

∂!π₁ : ∀ {Γ A B} → Γ ∂⊩ A ⩕ B
                 → Γ ∂⊩ A
∂!π₁ ∂s = bind ∂s (λ η s → !π₁ s)

∂!π₂ : ∀ {Γ A B} → Γ ∂⊩ A ⩕ B
                 → Γ ∂⊩ B
∂!π₂ ∂s = bind ∂s (λ η s → !π₂ s)

∂!τ : ∀ {Γ} → Γ ∂⊩ ⫪
∂!τ {Γ} = return (!τ {Γ})

∂!φ : ∀ {Γ C} → Γ ∂⊩ ⫫
              → Γ ∂⊩ C
∂!φ ∂s = bind ∂s (λ η s → !φ s)

∂!ι₁ : ∀ {Γ A B} → Γ ∂⊩ A
                 → Γ ∂⊩ A ⩖ B
∂!ι₁ {B = B} ∂a = return {_ ⩖ B}
                         (!ι₁ ∂a)

∂!ι₂ : ∀ {Γ A B} → Γ ∂⊩ B
                 → Γ ∂⊩ A ⩖ B
∂!ι₂ {A = A} ∂b = return {A ⩖ _}
                         (!ι₂ ∂b)

_∂!⁇_∂!∥_ : ∀ {Γ A B C} → Γ ∂⊩ A ⩖ B
                        → (∀ {Γ′} → Γ′ ⊇ Γ → Γ′ ∂⊩ A → Γ′ ∂⊩ C)
                        → (∀ {Γ′} → Γ′ ⊇ Γ → Γ′ ∂⊩ B → Γ′ ∂⊩ C)
                        → Γ ∂⊩ C
∂s ∂!⁇ f ∂!∥ g = bind ∂s (λ η s → s !⁇ (λ η′ → f (η ○ η′))
                                     !∥ (λ η′ → g (η ○ η′)))

--------------------------------------------------------------------------------

-- (∈ᴺ)
getᵥ : ∀ {Γ Ξ A} → Γ ∂⊩⋆ Ξ → Ξ ∋ A → Γ ∂⊩ A
getᵥ (ρ , ∂a) zero    = ∂a
getᵥ (ρ , ∂a) (suc i) = getᵥ ρ i

-- (Tmᴺ)
eval : ∀ {Γ Ξ A} → Γ ∂⊩⋆ Ξ → Ξ ⊢ A → Γ ∂⊩ A
eval ρ (𝓋 i)         = getᵥ ρ i
eval ρ (ƛ M)         = ∂!λ (λ η ∂a → eval (ρ ⬖ η , ∂a) M)
eval ρ (M ∙ N)       = eval ρ M ∂!∙ eval ρ N
eval ρ (M , N)       = eval ρ M ∂!, eval ρ N
eval ρ (π₁ M)        = ∂!π₁ (eval ρ M)
eval ρ (π₂ M)        = ∂!π₂ (eval ρ M)
eval ρ τ             = ∂!τ
eval ρ (φ M)         = ∂!φ (eval ρ M)
eval ρ (ι₁ M)        = ∂!ι₁ (eval ρ M)
eval ρ (ι₂ M)        = ∂!ι₂ (eval ρ M)
eval ρ (M ⁇ N₁ ∥ N₂) = eval ρ M ∂!⁇ (λ η ∂a → eval (ρ ⬖ η , ∂a) N₁)
                                ∂!∥ (λ η ∂b → eval (ρ ⬖ η , ∂b) N₂)

--------------------------------------------------------------------------------

mutual
  -- (qᴺ)
  reify : ∀ {A Γ} → Γ ∂⊩ A → Γ ⊢ⁿᶠ A
  reify {⎵}      ∂a = ∂a idₑ (λ η M → M)
  reify {A ⇒ B} ∂a = ∂a idₑ (λ η f → ƛ (reify (f (wkₑ idₑ) (reflect 0))))
  reify {A ⩕ B}  ∂a = ∂a idₑ (λ η s → reify (proj₁ s) , reify (proj₂ s))
  reify {⫪}     ∂a = ∂a idₑ (λ η s → τ)
  reify {⫫}     ∂a = ∂a idₑ (λ η s → elim⊥ s)
  reify {A ⩖ B}  ∂a = ∂a idₑ (λ η s → elim⊎ s (λ a → ι₁ (reify a))
                                              (λ b → ι₂ (reify b)))

  -- (uᴺ)
  reflect : ∀ {A Γ} → Γ ⊢ⁿᵉ A → Γ ∂⊩ A
  reflect {⎵}      M = return (ne M)
  reflect {A ⇒ B} M = return {A ⇒ _}
                              (λ η ∂a → reflect (renⁿᵉ η M ∙ reify ∂a))
  reflect {A ⩕ B}  M = return (reflect (π₁ M) , reflect (π₂ M))
  reflect {⫪}     M = return tt
  reflect {⫫}     M = λ η f →
                         ne (φ (renⁿᵉ η M))
  reflect {A ⩖ B}  M = λ η f →
                         ne (renⁿᵉ η M ⁇ f (wkₑ idₑ) (inj₁ (reflect 0))
                                       ∥ f (wkₑ idₑ) (inj₂ (reflect 0)))

--------------------------------------------------------------------------------

-- (uᶜᴺ)
idᵥ : ∀ {Γ} → Γ ∂⊩⋆ Γ
idᵥ {∅}     = ∅
idᵥ {Γ , A} = idᵥ ⬖ wkₑ idₑ , reflect 0

-- (nf)
nf : ∀ {Γ A} → Γ ⊢ A → Γ ⊢ⁿᶠ A
nf M = reify (eval idᵥ M)

--------------------------------------------------------------------------------


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Application {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Weakening using (id)
open import Definition.Typed.Properties
open import Definition.Typed.RedSteps
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties

open import Tools.Embedding
open import Tools.Product
import Tools.PropositionalEquality as PE

-- Helper function for application of specific type derivations.
-- unsightly code duplication because of my hack for shapeview
-- can't be bothered to fix for now...
appTerm′ : ∀ {F G t u Γ l l′ l″}
          ([F] : Γ ⊩⟨ l″ ⟩ F)
          ([G[u]] : Γ ⊩⟨ l′ ⟩ G [ u ])
          ([ΠFG] : Γ ⊩⟨ l ⟩Π Π F ▹ G)
          ([t] : Γ ⊩⟨ l ⟩ t ∷ Π F ▹ G / Π-intr [ΠFG])
          ([u] : Γ ⊩⟨ l″ ⟩ u ∷ F / [F])
        → Γ ⊩⟨ l′ ⟩ t ∘ u ∷ G [ u ] / [G[u]]
appTerm′ {t = t} {Γ = Γ} [F] [G[u]] (noemb (Πᵣ F G D ⊢F ⊢G A≡A [F′] [G′] G-ext))
         (Πₜ f d funcF f≡f [f] [f]₁) [u] =
  let ΠFG≡ΠF′G′ = whnfRed* (red D) Πₙ
      F≡F′ , G≡G′ = Π-PE-injectivity ΠFG≡ΠF′G′
      F≡idF′ = PE.trans F≡F′ (PE.sym (wk-id _))
      idG′ᵤ≡Gᵤ = PE.cong (λ x → x [ _ ]) (PE.trans (wk-lift-id _) (PE.sym G≡G′))
      idf∘u≡f∘u = (PE.cong (λ x → x ∘ _) (wk-id _))
      ⊢Γ = wf ⊢F
      [u]′ = irrelevanceTerm′ F≡idF′ [F] ([F′] id ⊢Γ) [u]
      [f∘u] = irrelevanceTerm″ idG′ᵤ≡Gᵤ idf∘u≡f∘u
                                ([G′] id ⊢Γ [u]′) [G[u]] ([f]₁ id ⊢Γ [u]′)
      ⊢u = escapeTerm [F] [u]
      d′ = PE.subst (λ x → Γ ⊢ t ⇒* f ∷ x) (PE.sym ΠFG≡ΠF′G′) (redₜ d)
  in  proj₁ (redSubst*Term (app-subst* d′ ⊢u) [G[u]] [f∘u])
appTerm′ [F] [G[u]] (emb 0<1 x) (ιx [t]) [u] = appTerm′ [F] [G[u]] x [t] [u]

-- Application of reducible terms.
appTerm : ∀ {F G t u Γ l l′ l″}
          ([F] : Γ ⊩⟨ l″ ⟩ F)
          ([G[u]] : Γ ⊩⟨ l′ ⟩ G [ u ])
          ([ΠFG] : Γ ⊩⟨ l ⟩ Π F ▹ G)
          ([t] : Γ ⊩⟨ l ⟩ t ∷ Π F ▹ G / [ΠFG])
          ([u] : Γ ⊩⟨ l″ ⟩ u ∷ F / [F])
        → Γ ⊩⟨ l′ ⟩ t ∘ u ∷ G [ u ] / [G[u]]
appTerm [F] [G[u]] [ΠFG] [t] [u] =
  let [t]′ = irrelevanceTerm [ΠFG] (Π-intr (Π-elim [ΠFG])) [t]
  in  appTerm′ [F] [G[u]] (Π-elim [ΠFG]) [t]′ [u]

-- Helper function for application congurence of specific type derivations.
app-congTerm′ : ∀ {F G t t′ u u′ Γ l l′}
          ([F] : Γ ⊩⟨ l′ ⟩ F)
          ([G[u]] : Γ ⊩⟨ l′ ⟩ G [ u ])
          ([ΠFG] : Γ ⊩⟨ l ⟩Π Π F ▹ G)
          ([t≡t′] : Γ ⊩⟨ l ⟩ t ≡ t′ ∷ Π F ▹ G / Π-intr [ΠFG])
          ([u] : Γ ⊩⟨ l′ ⟩ u ∷ F / [F])
          ([u′] : Γ ⊩⟨ l′ ⟩ u′ ∷ F / [F])
          ([u≡u′] : Γ ⊩⟨ l′ ⟩ u ≡ u′ ∷ F / [F])
        → Γ ⊩⟨ l′ ⟩ t ∘ u ≡ t′ ∘ u′ ∷ G [ u ] / [G[u]]
app-congTerm′ {F′} {G′} {t = t} {t′ = t′} {Γ = Γ}
              [F] [G[u]] (noemb (Πᵣ F G D ⊢F ⊢G A≡A [F]₁ [G] G-ext))
              (Πₜ₌ f g [ ⊢t , ⊢f , d ] [ ⊢t′ , ⊢g , d′ ] funcF funcG t≡u
                   (Πₜ f′ [ _ , ⊢f′ , d″ ] funcF′ f≡f [f] [f]₁)
                   (Πₜ g′ [ _ , ⊢g′ , d‴ ] funcG′ g≡g [g] [g]₁) [t≡u])
              [a] [a′] [a≡a′] =
  let [ΠFG] = Πᵣ′ F G D ⊢F ⊢G A≡A [F]₁ [G] G-ext
      ΠFG≡ΠF′G′ = whnfRed* (red D) Πₙ
      F≡F′ , G≡G′ = Π-PE-injectivity ΠFG≡ΠF′G′
      f≡f′ = whrDet*Term (d , functionWhnf funcF) (d″ , functionWhnf funcF′)
      g≡g′ = whrDet*Term (d′ , functionWhnf funcG) (d‴ , functionWhnf funcG′)
      F≡wkidF′ = PE.trans F≡F′ (PE.sym (wk-id _))
      t∘x≡wkidt∘x : {a b : Term} → wk id a ∘ b PE.≡ a ∘ b
      t∘x≡wkidt∘x {a} {b} = PE.cong (λ x → x ∘ b) (wk-id a)
      t∘x≡wkidt∘x′ : {a : Term} → wk id g′ ∘ a PE.≡ g ∘ a
      t∘x≡wkidt∘x′ {a} = PE.cong (λ x → x ∘ a) (PE.trans (wk-id _) (PE.sym g≡g′))
      wkidG₁[u]≡G[u] = PE.cong (λ x → x [ _ ])
                               (PE.trans (wk-lift-id _) (PE.sym G≡G′))
      wkidG₁[u′]≡G[u′] = PE.cong (λ x → x [ _ ])
                                 (PE.trans (wk-lift-id _) (PE.sym G≡G′))
      ⊢Γ = wf ⊢F
      [u]′ = irrelevanceTerm′ F≡wkidF′ [F] ([F]₁ id ⊢Γ) [a]
      [u′]′ = irrelevanceTerm′ F≡wkidF′ [F] ([F]₁ id ⊢Γ) [a′]
      [u≡u′]′ = irrelevanceEqTerm′ F≡wkidF′ [F] ([F]₁ id ⊢Γ) [a≡a′]
      [G[u′]] = irrelevance′ wkidG₁[u′]≡G[u′] ([G] id ⊢Γ [u′]′)
      [G[u≡u′]] = irrelevanceEq″ wkidG₁[u]≡G[u] wkidG₁[u′]≡G[u′]
                                  ([G] id ⊢Γ [u]′) [G[u]]
                                  (G-ext id ⊢Γ [u]′ [u′]′ [u≡u′]′)
      [f′] : Γ ⊩⟨ _ ⟩ f′ ∷ Π F′ ▹ G′ / [ΠFG]
      [f′] = Πₜ f′ (idRedTerm:*: ⊢f′) funcF′ f≡f [f] [f]₁
      [g′] : Γ ⊩⟨ _ ⟩ g′ ∷ Π F′ ▹ G′ / [ΠFG]
      [g′] = Πₜ g′ (idRedTerm:*: ⊢g′) funcG′ g≡g [g] [g]₁
      [f∘u] = appTerm [F] [G[u]] [ΠFG]
                      (irrelevanceTerm″ PE.refl (PE.sym f≡f′) [ΠFG] [ΠFG] [f′])
                      [a]
      [g∘u′] = appTerm [F] [G[u′]] [ΠFG]
                       (irrelevanceTerm″ PE.refl (PE.sym g≡g′) [ΠFG] [ΠFG] [g′])
                       [a′]
      [tu≡t′u] = irrelevanceEqTerm″ t∘x≡wkidt∘x t∘x≡wkidt∘x wkidG₁[u]≡G[u]
                                     ([G] id ⊢Γ [u]′) [G[u]]
                                     ([t≡u] id ⊢Γ [u]′)
      [t′u≡t′u′] = irrelevanceEqTerm″ t∘x≡wkidt∘x′ t∘x≡wkidt∘x′ wkidG₁[u]≡G[u]
                                       ([G] id ⊢Γ [u]′) [G[u]]
                                       ([g] id ⊢Γ [u]′ [u′]′ [u≡u′]′)
      d₁ = PE.subst (λ x → Γ ⊢ t ⇒* f ∷ x) (PE.sym ΠFG≡ΠF′G′) d
      d₂ = PE.subst (λ x → Γ ⊢ t′ ⇒* g ∷ x) (PE.sym ΠFG≡ΠF′G′) d′
      [tu≡fu] = proj₂ (redSubst*Term (app-subst* d₁ (escapeTerm [F] [a]))
                                     [G[u]] [f∘u])
      [gu′≡t′u′] = convEqTerm₂ [G[u]] [G[u′]] [G[u≡u′]]
                     (symEqTerm [G[u′]]
                       (proj₂ (redSubst*Term (app-subst* d₂ (escapeTerm [F] [a′]))
                                             [G[u′]] [g∘u′])))
  in  transEqTerm [G[u]] (transEqTerm [G[u]] [tu≡fu] [tu≡t′u])
                         (transEqTerm [G[u]] [t′u≡t′u′] [gu′≡t′u′])
app-congTerm′ [F] [G[u]] (emb 0<1 x) (ιx [t≡t′]) [u] [u′] [u≡u′] =
  app-congTerm′ [F] [G[u]] x [t≡t′] [u] [u′] [u≡u′]

-- Application congurence of reducible terms.
app-congTerm : ∀ {F G t t′ u u′ Γ l l′}
          ([F] : Γ ⊩⟨ l′ ⟩ F)
          ([G[u]] : Γ ⊩⟨ l′ ⟩ G [ u ])
          ([ΠFG] : Γ ⊩⟨ l ⟩ Π F ▹ G)
          ([t≡t′] : Γ ⊩⟨ l ⟩ t ≡ t′ ∷ Π F ▹ G / [ΠFG])
          ([u] : Γ ⊩⟨ l′ ⟩ u ∷ F / [F])
          ([u′] : Γ ⊩⟨ l′ ⟩ u′ ∷ F / [F])
          ([u≡u′] : Γ ⊩⟨ l′ ⟩ u ≡ u′ ∷ F / [F])
        → Γ ⊩⟨ l′ ⟩ t ∘ u ≡ t′ ∘ u′ ∷ G [ u ] / [G[u]]
app-congTerm [F] [G[u]] [ΠFG] [t≡t′] =
  let [t≡t′]′ = irrelevanceEqTerm [ΠFG] (Π-intr (Π-elim [ΠFG])) [t≡t′]
  in  app-congTerm′ [F] [G[u]] (Π-elim [ΠFG]) [t≡t′]′


------------------------------------------------------------------------
-- The one-sided Step function, used to define similarity and the
-- two-sided Step function
------------------------------------------------------------------------

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

open import Prelude

open import Labelled-transition-system

module Similarity.Step
         {ℓ}
         (lts : LTS ℓ)
         (open LTS lts)
         (_[_]↝_ : Proc → Label → Proc → Type ℓ)
         where

open import Equality.Propositional
open import Logical-equivalence using (_⇔_)

open import Bijection equality-with-J as Bijection using (_↔_)
import Equivalence equality-with-J as Eq
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 Indexed-container hiding (⟨_⟩)
open import Relation

private
 module Temporarily-private where

  -- If _[_]↝_ is instantiated with _[_]⟶_, then this is basically the
  -- function s from Section 6.3.4.2 in Pous and Sangiorgi's
  -- "Enhancements of the bisimulation proof method", except that
  -- clause (3) is omitted.
  --
  -- Similarly, if _[_]↝_ is instantiated with _[_]⇒̂_, then this is
  -- basically the function w from Section 6.5.2.4, except that
  -- "P R Q" is omitted.

  record Step {r} (R : Rel₂ r Proc) (pq : Proc × Proc) :
              Type (ℓ ⊔ r) where
    constructor ⟨_⟩

    private
      p = proj₁ pq
      q = proj₂ pq

    field
      challenge : ∀ {p′ μ} →
                  p [ μ ]⟶ p′ → ∃ λ q′ → q [ μ ]↝ q′ × R (p′ , q′)

open Temporarily-private using (Step)

-- Used to aid unification. Note that this type is parametrised (see
-- the module telescope above). The inclusion of a value of this type
-- in the definition of StepC below makes it easier for Agda to infer
-- the LTS parameter from the types ν StepC i (p , q) and
-- ν′ StepC i (p , q).

record Magic : Type where

-- The Magic type is isomorphic to the unit type.

Magic↔⊤ : Magic ↔ ⊤
Magic↔⊤ = record
  { surjection = record
    { right-inverse-of = λ _ → refl
    }
  ; left-inverse-of = λ _ → refl
  }

-- The Step function, expressed as an indexed container.

StepC : Container (Proc × Proc) (Proc × Proc)
StepC =
  (λ { (p , q) → Magic  -- Included in order to aid type inference.
                   ×
                 (∀ {p′ μ} → p [ μ ]⟶ p′ → ∃ λ q′ → q [ μ ]↝ q′)
     })
    ◁
  (λ { {o = p , q} (_ , lr) (p′ , q′) →
       ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ p′) → proj₁ (lr p⟶p′) ≡ q′
     })

-- The definition of Step in terms of a container is pointwise
-- logically equivalent to the direct definition, and in the presence
-- of extensionality it is pointwise isomorphic to the direct
-- definition.

Step↔StepC :
  ∀ {k r} {R : Rel₂ r Proc} {pq} →
  Extensionality? k ℓ (ℓ ⊔ r) →
  Step R pq ↝[ k ] ⟦ StepC ⟧ R pq
Step↔StepC {r = r} {R} {pq} =
  generalise-ext?
    Step⇔StepC
    (λ ext →
         (λ (_ , f) →
            Σ-≡,≡→≡ refl $
              implicit-extensionality ext λ _ →
              apply-ext (lower-extensionality lzero ℓ ext) $
                to₂∘from f)
       , (λ _ → refl))
  where
  to₁ : Step R pq → Container.Shape StepC pq
  to₁ Temporarily-private.⟨ lr ⟩ =
      _
    , (λ p⟶p′ → Σ-map id proj₁ (lr p⟶p′))

  to₂ :
    (s : Step R pq) →
    Container.Position StepC (to₁ s) ⊆ R
  to₂ Temporarily-private.⟨ lr ⟩ (_ , p⟶p′ , refl) =
    proj₂ (proj₂ (lr p⟶p′))

  from : ⟦ StepC ⟧ R pq → Step R pq
  from ((_ , lr) , f) =
    Temporarily-private.⟨ (λ p⟶p′ →
                             let q′ , q⟶q′ = lr p⟶p′
                             in  q′ , q⟶q′ , f (_ , p⟶p′ , refl))
                        ⟩

  Step⇔StepC : Step R pq ⇔ ⟦ StepC ⟧ R pq
  Step⇔StepC = record
    { to   = λ s → to₁ s , to₂ s
    ; from = from
    }

  to₂∘from :
    ∀ {p′q′} {s : Container.Shape StepC pq}
    (f : Container.Position StepC s ⊆ R) →
    (pos : Container.Position StepC s p′q′) →
    proj₂ (_⇔_.to Step⇔StepC (_⇔_.from Step⇔StepC (s , f))) pos ≡ f pos
  to₂∘from f (_ , _ , refl) = refl

module StepC {r} {R : Rel₂ r Proc} {p q} where

  -- A "constructor".

  ⟨_⟩ :
    (∀ {p′ μ} → p [ μ ]⟶ p′ → ∃ λ q′ → q [ μ ]↝ q′ × R (p′ , q′)) →
    ⟦ StepC ⟧ R (p , q)
  ⟨ lr ⟩ = _⇔_.to (Step↔StepC _) Temporarily-private.⟨ lr ⟩

  -- A "projection".

  challenge :
    ⟦ StepC ⟧ R (p , q) →
    ∀ {p′ μ} → p [ μ ]⟶ p′ → ∃ λ q′ → q [ μ ]↝ q′ × R (p′ , q′)
  challenge = Step.challenge ∘ _⇔_.from (Step↔StepC _)

open Temporarily-private public

-- An unfolding lemma for ⟦ StepC ⟧₂.

⟦StepC⟧₂↔ :
  ∀ {x} {X : Rel₂ x Proc} →
  Extensionality ℓ ℓ →
  (R : ∀ {o} → Rel₂ ℓ (X o)) →
  ∀ {p q} (p∼q₁ p∼q₂ : ⟦ StepC ⟧ X (p , q)) →

  ⟦ StepC ⟧₂ R (p∼q₁ , p∼q₂)

    ↔

  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ , p′≤q′₁ = StepC.challenge p∼q₁ p⟶p′
       q′₂ , q⟶q′₂ , p′≤q′₂ = StepC.challenge p∼q₂ p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        R (subst (X ∘ (p′ ,_)) q′₁≡q′₂ p′≤q′₁ , p′≤q′₂))

⟦StepC⟧₂↔ {X = X} ext R {p} {q}
          (s₁@(_ , ch₁) , f₁) (s₂@(_ , ch₂) , f₂) =

  (∃ λ (eq : s₁ ≡ s₂) →
   ∀ {o} (p : Container.Position StepC s₁ o) →
   R (f₁ p , f₂ (subst (λ s → Container.Position StepC s o) eq p)))    ↝⟨ inverse $ Σ-cong ≡×≡↔≡ (λ _ → F.id) ⟩

  (∃ λ (eq : proj₁ s₁ ≡ proj₁ s₂ × (λ {_ _} → ch₁) ≡ ch₂) →
   ∀ {o} (p : Container.Position StepC s₁ o) →
   R (f₁ p , f₂ (subst (λ s → Container.Position StepC s o)
                       (cong₂ _,_ (proj₁ eq) (proj₂ eq)) p)))          ↝⟨ inverse Σ-assoc ⟩

  (∃ λ (eq₁ : proj₁ s₁ ≡ proj₁ s₂) →
   ∃ λ (eq₂ : (λ {_ _} → ch₁) ≡ ch₂) →
   ∀ {o} (p : Container.Position StepC s₁ o) →
   R (f₁ p , f₂ (subst (λ s → Container.Position StepC s o)
                       (cong₂ _,_ eq₁ eq₂) p)))                        ↝⟨ drop-⊤-left-Σ $ _⇔_.to contractible⇔↔⊤ $
                                                                          +⇒≡ $ mono₁ 0 (_⇔_.from contractible⇔↔⊤ Magic↔⊤) ⟩
  (∃ λ (eq : (λ {_ _} → ch₁) ≡ ch₂) →
   ∀ {o} (p : Container.Position StepC s₁ o) →
   R (f₁ p , f₂ (subst (λ s → Container.Position StepC s o)
                       (cong₂ _,_ refl eq) p)))                        ↝⟨ (∃-cong λ eq → implicit-∀-cong ext $ ∀-cong ext λ _ →
                                                                           ≡⇒↝ _ $
                                                                           cong (λ (eq : s₁ ≡ s₂) →
                                                                                R (f₁ _ ,
                                                                                f₂ (subst (λ s → Container.Position StepC s _) eq _))) $
                                                                           trans-reflˡ (cong (_ ,_) eq)) ⟩
  (∃ λ (eq : (λ {_ _} → ch₁) ≡ ch₂) →
   ∀ {o} (p : Container.Position StepC s₁ o) →
   R (f₁ p , f₂ (subst (λ (s : _ × _) → Container.Position StepC s o)
                       (cong (_ ,_) eq) p)))                           ↝⟨ (∃-cong λ eq → implicit-∀-cong ext λ {o} → ∀-cong ext λ p →
                                                                           ≡⇒↝ _ $ cong (λ p → R {o = o} (f₁ _ , f₂ p)) $ sym $
                                                                           subst-∘ (λ (s : Container.Shape StepC _) → Container.Position StepC s o)
                                                                                _ eq) ⟩
  (∃ λ (eq : (λ {_ _} → ch₁) ≡ ch₂) →
   ∀ {o} (p : Container.Position StepC s₁ o) →
   R (f₁ p , f₂ (subst (λ (ch : ∀ {_ _} → _) →
                          Container.Position StepC (_ , ch) o)
                       eq p)))                                         ↔⟨⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ {p′q′} →
   (pos : ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ proj₁ p′q′) →
          proj₁ (ch₁ p⟶p′) ≡ proj₂ p′q′) →
   R (f₁ pos ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ proj₁ p′q′) →
                        proj₁ (ch p⟶p′) ≡ proj₂ p′q′)
                eq pos)))                                              ↝⟨ (∃-cong λ _ → Bijection.implicit-Π↔Π) ⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′q′ →
   (pos : ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ proj₁ p′q′) →
          proj₁ (ch₁ p⟶p′) ≡ proj₂ p′q′) →
   R (f₁ pos ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ proj₁ p′q′) →
                        proj₁ (ch p⟶p′) ≡ proj₂ p′q′)
                eq pos)))                                              ↝⟨ (∃-cong λ _ → currying) ⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ q′ →
   (pos : ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ p′) → proj₁ (ch₁ p⟶p′) ≡ q′) →
   R (f₁ pos ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ p′) →
                        proj₁ (ch p⟶p′) ≡ q′)
                eq pos)))                                              ↝⟨ (∃-cong λ _ → ∀-cong ext λ _ → ∀-cong ext λ _ → currying) ⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ q′ μ →
   (pos : ∃ λ (p⟶p′ : p [ μ ]⟶ p′) → proj₁ (ch₁ p⟶p′) ≡ q′) →
   R (f₁ (μ , pos) ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ p′) →
                        proj₁ (ch p⟶p′) ≡ q′)
                eq (μ , pos))))                                        ↝⟨ (∃-cong λ _ → ∀-cong ext λ _ → ∀-cong ext λ _ → ∀-cong ext λ _ →
                                                                           currying) ⟩
  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ q′ μ (p⟶p′ : p [ μ ]⟶ p′) (≡q′ : proj₁ (ch₁ p⟶p′) ≡ q′) →
   R (f₁ (μ , p⟶p′ , ≡q′) ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ p′) →
                        proj₁ (ch p⟶p′) ≡ q′)
                eq (μ , p⟶p′ , ≡q′))))                                 ↝⟨ (∃-cong λ _ → ∀-cong ext λ _ → Π-comm) ⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ μ q′ (p⟶p′ : p [ μ ]⟶ p′) (≡q′ : proj₁ (ch₁ p⟶p′) ≡ q′) →
   R (f₁ (μ , p⟶p′ , ≡q′) ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ p′) →
                        proj₁ (ch p⟶p′) ≡ q′)
                eq (μ , p⟶p′ , ≡q′))))                                 ↝⟨ (∃-cong λ _ → ∀-cong ext λ _ → ∀-cong ext λ _ → Π-comm) ⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ μ (p⟶p′ : p [ μ ]⟶ p′) q′ (≡q′ : proj₁ (ch₁ p⟶p′) ≡ q′) →
   R (f₁ (μ , p⟶p′ , ≡q′) ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′ : p [ μ ]⟶ p′) →
                        proj₁ (ch p⟶p′) ≡ q′)
                eq (μ , p⟶p′ , ≡q′))))                                 ↝⟨ (∃-cong λ _ → ∀-cong ext λ _ → ∀-cong ext λ _ → ∀-cong ext λ _ →
                                                                              inverse $ ∀-intro _ ext) ⟩
  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ μ (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (subst (λ ch → ∃ λ μ → ∃ λ (p⟶p′′ : p [ μ ]⟶ p′) →
                        proj₁ (ch p⟶p′′) ≡ proj₁ (ch₁ p⟶p′))
                eq (μ , p⟶p′ , refl))))                                ↝⟨ (∃-cong λ eq → ∀-cong ext λ _ → ∀-cong ext λ _ → ∀-cong ext λ _ →
                                                                           ≡⇒↝ _ $ cong (R ∘ (f₁ _ ,_) ∘ f₂) $ lemma₁ eq) ⟩
  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ μ (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) eq))))         ↝⟨ (∃-cong λ eq → ∀-cong ext λ _ → inverse Bijection.implicit-Π↔Π) ⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ p′ {μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) eq))))         ↝⟨ (∃-cong λ eq → inverse Bijection.implicit-Π↔Π) ⟩

  (∃ λ (eq : _≡_ {A = ∀ {p′ μ} → _} ch₁ ch₂) →
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) eq))))         ↝⟨ (Σ-cong (inverse $ implicit-extensionality-isomorphism
                                                                                {k = bijection} ext) λ eq →
                                                                           implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ p⟶p′ →
                                                                           ≡⇒↝ _ $ sym $ cong (λ eq →
                                                                             R (f₁ _ , f₂ (_ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) eq)))) $
                                                                           _↔_.right-inverse-of (implicit-extensionality-isomorphism ext) eq) ⟩
  (∃ λ (eq : ∀ p′ → (λ {μ} → ch₁ {p′ = p′} {μ = μ}) ≡ ch₂) →
   let eq′ = _↔_.to (implicit-extensionality-isomorphism ext) eq in
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) eq′))))        ↝⟨ (∃-cong λ eq →
                                                                           implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ p⟶p′ →
                                                                           ≡⇒↝ _ $ cong (λ eq → R (f₁ _ , f₂ (_ , p⟶p′ , sym eq))) $
                                                                           lemma₂ eq) ⟩
  (∃ λ (eq : ∀ p′ → (λ {μ} → ch₁ {p′ = p′} {μ = μ}) ≡ ch₂) →
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p′ p⟶p′))
                               (apply-ext (Eq.good-ext ext) eq)))))    ↝⟨ (Σ-cong (inverse $ ∀-cong ext λ _ →
                                                                                implicit-extensionality-isomorphism
                                                                                {k = bijection} ext) λ eq →
                                                                           implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ p⟶p′ →
                                                                           ≡⇒↝ _ $ sym $ cong (λ eq →
                                                                             R (f₁ _ , f₂ (_ , p⟶p′ , sym (cong (λ ch → proj₁ (ch _ p⟶p′))
                                                                                (apply-ext (Eq.good-ext ext) eq))))) $
                                                                           apply-ext ext $
                                                                           _↔_.right-inverse-of (implicit-extensionality-isomorphism ext) ∘ eq) ⟩
  (∃ λ (eq : ∀ p′ μ → ch₁ {p′ = p′} {μ = μ} ≡ ch₂) →
   let eq′ = implicit-extensionality (Eq.good-ext ext) ∘ eq in
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p′ p⟶p′))
                               (apply-ext (Eq.good-ext ext) eq′)))))   ↝⟨ (∃-cong λ _ →
                                                                           implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ _ →
                                                                           ≡⇒↝ _ $ cong (λ eq → R (f₁ _ , f₂ (_ , _ , sym eq))) $
                                                                           lemma₃ _) ⟩
  (∃ λ (eq : ∀ p′ μ → ch₁ {p′ = p′} {μ = μ} ≡ ch₂) →
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) (eq p′ μ)))))  ↝⟨ Σ-cong (inverse Bijection.implicit-Π↔Π) (λ _ → F.id) ⟩

  (∃ λ (eq : ∀ {p′} μ → ch₁ {p′ = p′} {μ = μ} ≡ ch₂) →
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) (eq μ)))))     ↝⟨ Σ-cong (implicit-∀-cong ext $ inverse Bijection.implicit-Π↔Π)
                                                                                (λ _ → F.id) ⟩
  (∃ λ (eq : ∀ {p′ μ} → ch₁ {p′ = p′} {μ = μ} ≡ ch₂) →
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong (λ ch → proj₁ (ch p⟶p′)) eq))))         ↝⟨ (∃-cong λ eq →
                                                                           implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ p⟶p′ →
                                                                           ≡⇒↝ _ $ cong (λ eq → R (f₁ _ , f₂ (_ , _ , sym eq))) $ sym $
                                                                           cong-∘ proj₁ (_$ p⟶p′) (eq {μ = _})) ⟩
  (∃ λ (eq : ∀ {p′ μ} → ch₁ {p′ = p′} {μ = μ} ≡ ch₂) →
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong proj₁ $ cong (_$ p⟶p′) eq))))           ↝⟨ Σ-cong (implicit-∀-cong ext $ implicit-∀-cong ext $ inverse $
                                                                                Eq.extensionality-isomorphism ext)
                                                                                (λ _ → F.id) ⟩
  (∃ λ (eq : ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) → ch₁ p⟶p′ ≡ ch₂ p⟶p′) →
   ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong proj₁ (eq p⟶p′)))))                     ↝⟨ inverse implicit-ΠΣ-comm ⟩

  (∀ {p′} →
   ∃ λ (eq : ∀ {μ} (p⟶p′ : p [ μ ]⟶ p′) → ch₁ p⟶p′ ≡ ch₂ p⟶p′) →
   ∀ {μ} (p⟶p′ : p [ μ ]⟶ p′) →
   R (f₁ (μ , p⟶p′ , refl) ,
      f₂ (μ , p⟶p′ , sym (cong proj₁ (eq p⟶p′)))))                     ↝⟨ implicit-∀-cong ext $ inverse implicit-ΠΣ-comm ⟩

  (∀ {p′ μ} →
   ∃ λ (eq : (p⟶p′ : p [ μ ]⟶ p′) → ch₁ p⟶p′ ≡ ch₂ p⟶p′) →
   ∀ p⟶p′ → R (f₁ (μ , p⟶p′ , refl) ,
               f₂ (μ , p⟶p′ , sym (cong proj₁ (eq p⟶p′)))))            ↝⟨ implicit-∀-cong ext $ implicit-∀-cong ext $ inverse ΠΣ-comm ⟩

  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   ∃ λ (eq : ch₁ p⟶p′ ≡ ch₂ p⟶p′) →
   R (f₁ (μ , p⟶p′ , refl) , f₂ (μ , p⟶p′ , sym (cong proj₁ eq))))     ↝⟨ (inverse $ implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ _ →
                                                                           Σ-cong Bijection.Σ-≡,≡↔≡ λ _ → F.id) ⟩
  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ = ch₁ p⟶p′
       q′₂ , q⟶q′₂ = ch₂ p⟶p′
   in ∃ λ (eq : ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
                  subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂) →
      R (f₁ (μ , p⟶p′ , refl) ,
         f₂ (μ , p⟶p′ , sym (cong proj₁ (uncurry Σ-≡,≡→≡ eq)))))       ↝⟨ (implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ _ →
                                                                           ∃-cong λ eq →
                                                                           ≡⇒↝ _ $ cong (λ eq → R (f₁ _ , f₂ (_ , _ , sym eq))) $
                                                                           proj₁-Σ-≡,≡→≡ (proj₁ eq) (proj₂ eq)) ⟩
  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ = ch₁ p⟶p′
       q′₂ , q⟶q′₂ = ch₂ p⟶p′
   in ∃ λ (eq : ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
                  subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂) →
      R (f₁ (μ , p⟶p′ , refl) , f₂ (μ , p⟶p′ , sym (proj₁ eq))))       ↝⟨ (implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ _ →
                                                                           inverse Σ-assoc) ⟩
  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ = ch₁ p⟶p′
       q′₂ , q⟶q′₂ = ch₂ p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        R (f₁ (μ , p⟶p′ , refl) , f₂ (μ , p⟶p′ , sym q′₁≡q′₂)))        ↝⟨ (implicit-∀-cong ext $ implicit-∀-cong ext $ ∀-cong ext λ p⟶p′ →
                                                                           ∃-cong λ q′₁≡q′₂ → ∃-cong λ _ → ≡⇒↝ _ $
                                                                           lemma₄ q′₁≡q′₂) ⟩
  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ = ch₁ p⟶p′
       q′₂ , q⟶q′₂ = ch₂ p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        R (subst (X ∘ (p′ ,_)) q′₁≡q′₂ (f₁ (_ , p⟶p′ , refl)) ,
           f₂ (_ , p⟶p′ , refl)))                                      ↔⟨⟩

  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ , p′≤q′₁ = StepC.challenge (s₁ , f₁) p⟶p′
       q′₂ , q⟶q′₂ , p′≤q′₂ = StepC.challenge (s₂ , f₂) p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        R (subst (X ∘ (p′ ,_)) q′₁≡q′₂ p′≤q′₁ , p′≤q′₂))               □

  where

  lemma₁ :
    ∀ {p q} {f g : ∀ {p′ μ} → p [ μ ]⟶ p′ → ∃ (q [ μ ]↝_)}
    (eq : (λ {p′ μ} → f {p′ = p′} {μ = μ}) ≡ g)
    {p′ μ} {p⟶p′ : p [ μ ]⟶ p′} →
    _
  lemma₁ {p} {q} {f} {g} eq {p′} {μ} {p⟶p′} =

    subst (λ ch →
             ∃ λ μ → ∃ λ (p⟶p′′ : p [ μ ]⟶ p′) →
             proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′))
          eq (μ , p⟶p′ , refl)                          ≡⟨ push-subst-pair′ {y≡z = eq} _ _ _ ⟩

    ( μ
    , subst₂ (λ { (ch , μ) →
                  ∃ λ (p⟶p′′ : p [ μ ]⟶ p′) →
                  proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′) })
             eq (subst-const eq) (p⟶p′ , refl)
    )                                                   ≡⟨ cong (μ ,_) $
                                                           cong (λ eq → subst (λ { (ch , μ) →
                                                                                ∃ λ (p⟶p′′ : p [ μ ]⟶ p′) →
                                                                                proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′) })
                                                                              eq (p⟶p′ , refl)) $
                                                           Σ-≡,≡→≡-subst-const eq refl ⟩
    ( μ
    , subst (λ { (ch , μ) →
                 ∃ λ (p⟶p′′ : p [ μ ]⟶ p′) →
                 proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′) })
            (cong₂ _,_ eq refl) (p⟶p′ , refl)
    )                                                   ≡⟨⟩

    ( μ
    , subst (λ { (ch , μ) →
                 ∃ λ (p⟶p′′ : p [ μ ]⟶ p′) →
                 proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′) })
            (cong (_, _) eq) (p⟶p′ , refl)
    )                                                   ≡⟨ cong (μ ,_) $ sym $
                                                           subst-∘ _ _ eq ⟩
    ( μ
    , subst (λ ch →
               ∃ λ (p⟶p′′ : p [ μ ]⟶ p′) →
               proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′))
            eq (p⟶p′ , refl)
    )                                                   ≡⟨ cong (μ ,_) $
                                                           push-subst-pair′ {y≡z = eq} _ _ _ ⟩
    ( μ
    , p⟶p′
    , subst₂ (λ { (ch , p⟶p′′) →
                  proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′) })
             eq (subst-const eq) refl
    )                                                   ≡⟨ cong (λ eq → (_ , p⟶p′ , subst (λ { (ch , p⟶p′′) → proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′) })
                                                                                eq refl)) $
                                                           Σ-≡,≡→≡-subst-const eq refl ⟩
    ( μ
    , p⟶p′
    , subst (λ { (ch , p⟶p′′) →
                 proj₁ (ch p⟶p′′) ≡ proj₁ (f p⟶p′) })
            (cong (_, _) eq) refl
    )                                                   ≡⟨ cong (λ eq → (_ , p⟶p′ , eq)) $ sym $
                                                           subst-∘ _ _ eq ⟩
    ( μ
    , p⟶p′
    , subst (λ ch → proj₁ (ch p⟶p′) ≡ proj₁ (f p⟶p′))
            eq refl
    )                                                   ≡⟨ cong (λ eq → (_ , p⟶p′ , eq)) $
                                                           subst-∘ _ _ eq ⟩
    ( μ
    , p⟶p′
    , subst (_≡ proj₁ (f p⟶p′))
            (cong (λ ch → proj₁ (ch p⟶p′)) eq) refl
    )                                                   ≡⟨ cong (λ eq → (_ , p⟶p′ , subst (_≡ _) eq refl)) $
                                                           sym $ sym-sym (cong (λ ch → proj₁ (ch p⟶p′)) eq) ⟩
    ( μ
    , p⟶p′
    , subst (_≡ proj₁ (f p⟶p′))
            (sym $ sym $
               cong (λ ch → proj₁ (ch p⟶p′)) eq)
            refl
    )                                                   ≡⟨ cong (λ eq → (_ , p⟶p′ , eq)) $
                                                           subst-trans (sym $ cong (λ ch → proj₁ (ch p⟶p′)) eq) ⟩∎
    ( μ
    , p⟶p′
    , sym (cong (λ ch → proj₁ (ch p⟶p′)) eq)
    )                                                   ∎

  lemma₂ :
    ∀ {p q} {f g : ∀ {p′ μ} → p [ μ ]⟶ p′ → ∃ (q [ μ ]↝_)}
    (eq : ∀ p′ → (λ {μ} → f {p′ = p′} {μ = μ}) ≡ g)
    {p′ μ} {p⟶p′ : p [ μ ]⟶ p′} →
    _
  lemma₂ {p} {q} {f} {g} eq {p′} {μ} {p⟶p′} =

    cong (λ ch → proj₁ (ch p⟶p′))
         (_↔_.to (implicit-extensionality-isomorphism ext) eq)         ≡⟨⟩

    cong (λ ch → proj₁ (ch p⟶p′))
         (implicit-extensionality (Eq.good-ext ext) eq)                ≡⟨ cong-∘ _ _ (apply-ext (Eq.good-ext ext) eq) ⟩∎

    cong (λ ch → proj₁ (ch p′ p⟶p′)) (apply-ext (Eq.good-ext ext) eq)  ∎

  lemma₃ :
    ∀ {p q} {f g : ∀ {p′ μ} → p [ μ ]⟶ p′ → ∃ (q [ μ ]↝_)}
    (eq : ∀ p′ μ → f {p′ = p′} {μ = μ} ≡ g) {p′ μ p⟶p′} →
    _
  lemma₃ {p} {q} {f} {g} eq {p′} {μ} {p⟶p′} =

    cong (λ (ch : ∀ _ {μ} → _) → proj₁ (ch p′ {μ = μ} p⟶p′))
         (apply-ext (Eq.good-ext ext)
            (implicit-extensionality (Eq.good-ext ext) ∘ eq))             ≡⟨⟩

    cong (λ ch → proj₁ (ch p′ p⟶p′))
         (apply-ext (Eq.good-ext ext)
            (cong (λ f {x} → f x) ∘ (apply-ext (Eq.good-ext ext) ∘ eq)))  ≡⟨ cong (cong (λ ch → proj₁ (ch p′ p⟶p′))) $ sym $
                                                                             Eq.cong-post-∘-good-ext {h = λ f {x} → f x} ext ext
                                                                                (apply-ext (Eq.good-ext ext) ∘ eq) ⟩
    cong (λ ch → proj₁ (ch p′ p⟶p′))
         (cong (λ f x {y} → f x y)
               (apply-ext (Eq.good-ext ext)
                  (apply-ext (Eq.good-ext ext) ∘ eq)))                    ≡⟨ cong-∘ _ _ (apply-ext (Eq.good-ext ext)
                                                                                (apply-ext (Eq.good-ext ext) ∘ eq)) ⟩
    cong (λ ch → proj₁ (ch p′ μ p⟶p′))
         (apply-ext (Eq.good-ext ext)
            (apply-ext (Eq.good-ext ext) ∘ eq))                           ≡⟨ sym $ cong-∘ _ _ (apply-ext (Eq.good-ext ext)
                                                                                (apply-ext (Eq.good-ext ext) ∘ eq)) ⟩
    cong (λ ch → proj₁ (ch μ p⟶p′))
         (cong (_$ p′) (apply-ext (Eq.good-ext ext)
                          (apply-ext (Eq.good-ext ext) ∘ eq)))            ≡⟨ cong (cong (λ ch → proj₁ (ch μ p⟶p′))) $ Eq.cong-good-ext ext _ ⟩

    cong (λ ch → proj₁ (ch μ p⟶p′))
         (apply-ext (Eq.good-ext ext) (eq p′))                            ≡⟨ sym $ cong-∘ _ _ (apply-ext (Eq.good-ext ext) (eq p′)) ⟩

    cong (λ ch → proj₁ (ch p⟶p′))
         (cong (_$ μ) (apply-ext (Eq.good-ext ext) (eq p′)))              ≡⟨ cong (cong (λ ch → proj₁ (ch p⟶p′))) $ Eq.cong-good-ext ext _ ⟩∎

    cong (λ ch → proj₁ (ch p⟶p′)) (eq p′ μ)                               ∎

  lemma₄ :
    ∀ {p′ μ} {p⟶p′ : p [ μ ]⟶ p′}
    (q′₁≡q′₂ : proj₁ (ch₁ p⟶p′) ≡ proj₁ (ch₂ p⟶p′)) →
    R (f₁ (μ , p⟶p′ , refl) , f₂ (μ , p⟶p′ , sym q′₁≡q′₂))
      ≡
    R (subst (X ∘ (p′ ,_)) q′₁≡q′₂ (f₁ (_ , p⟶p′ , refl)) ,
       f₂ (_ , p⟶p′ , refl))
  lemma₄ {p′} {μ} {p⟶p′} q′₁≡q′₂ =
    R (f₁ (μ , p⟶p′ , refl) , f₂ (μ , p⟶p′ , sym q′₁≡q′₂))        ≡⟨ cong (R ∘ (f₁ _ ,_)) $ lemma′ q′₁≡q′₂ ⟩

    R (f₁ (μ , p⟶p′ , refl) ,
       subst (X ∘ (p′ ,_)) (sym q′₁≡q′₂) (f₂ (_ , p⟶p′ , refl)))  ≡⟨ lemma″ q′₁≡q′₂ ⟩∎

    R (subst (X ∘ (p′ ,_)) q′₁≡q′₂ (f₁ (_ , p⟶p′ , refl)) ,
       f₂ (_ , p⟶p′ , refl))                                      ∎
    where
    lemma′ :
      ∀ {q′₁} (q′₁≡q′₂ : q′₁ ≡ proj₁ (ch₂ p⟶p′)) →
      f₂ (μ , p⟶p′ , sym q′₁≡q′₂)
        ≡
      subst (X ∘ (p′ ,_)) (sym q′₁≡q′₂) (f₂ (_ , p⟶p′ , refl))
    lemma′ refl = refl

    lemma″ :
      ∀ {q′₂ x} (q′₁≡q′₂ : proj₁ (ch₁ p⟶p′) ≡ q′₂) →
      R (f₁ (μ , p⟶p′ , refl) , subst (X ∘ (p′ ,_)) (sym q′₁≡q′₂) x)
        ≡
      R (subst (X ∘ (p′ ,_)) q′₁≡q′₂ (f₁ (_ , p⟶p′ , refl)) , x)
    lemma″ refl = refl