------------------------------------------------------------------------
-- Normalization/simplification of kinding in Fω with interval kinds
------------------------------------------------------------------------

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

module FOmegaInt.Kinding.Simple.Normalization where

open import Data.Fin using (Fin; zero; suc; raise; lift)
open import Data.Fin.Substitution using (module VarSubst)
open import Data.Fin.Substitution.Lemmas
open import Data.Fin.Substitution.ExtraLemmas
open import Data.List using ([]; _∷_; _∷ʳ_; map)
open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Product as Prod using (_,_)
open import Data.Vec as Vec using ([]; _∷_)
import Data.Vec.Properties as VecProp
open import Function using (flip; _∘_)
open import Relation.Binary.PropositionalEquality hiding ([_])

open import FOmegaInt.Syntax
open import FOmegaInt.Syntax.SingleVariableSubstitution
  renaming (sub to hsub; _↑ to _H↑)
open import FOmegaInt.Syntax.HereditarySubstitution
open import FOmegaInt.Syntax.Normalization
open import FOmegaInt.Syntax.WeakEquality
open import FOmegaInt.Kinding.Simple as Simple
open import FOmegaInt.Kinding.Simple.EtaExpansion
open import FOmegaInt.Kinding.Declarative as Declarative

------------------------------------------------------------------------
-- Normalization/simplification of kinded types to simply kinded
-- types.
--
-- TODO: explain the point of this and how "simple" kinding differs
-- from "canonical" kinding.

open Syntax
open TermCtx
open SimpleCtx    using (kd; tp; ⌊_⌋Asc; kd-inj′)
open Substitution hiding (subst)
open ContextConversions
open RenamingCommutesNorm
open WeakHereditarySubstitutionEquality
open WeakEqEtaExpansion
open WeakEqNormalization
open KindedHereditarySubstitution
open Simple.Kinding
open Declarative.Kinding

private
  module S where
    open Simple.Kinding public
    open SimpleCtx      public
    open SimplyWfCtx    public
  module D where
    open Declarative.Kinding public
    open TermCtx             public
  module E = ElimCtx

mutual

  -- Normalization and simultaneous simplification of kinding:
  -- well-kinded types normalize to simply kinded normal forms.
  nf-Tp∈ : ∀ {n} {Γ : Ctx n} {a k} →
           let Γ′ = nfCtx Γ in Γ ⊢Tp a ∈ k → ⌊ Γ′ ⌋Ctx ⊢Nf nf Γ′ a ∈ ⌊ k ⌋
  nf-Tp∈ {_} {Γ} (∈-var {j} x Γ-ctx Γ[x]≡kd-k)
    with E.lookup (nfCtx Γ) x | nfCtx-lookup-kd x Γ Γ[x]≡kd-k
  nf-Tp∈ {_} {Γ} (∈-var {j} x Γ-ctx Γ[x]≡kd-j) | kd ._ | refl =
    subst (_ ⊢Nf η-exp (nfKind _ j) (var∙ x) ∈_) (⌊⌋-nf _)
          (η-exp-Var∈ (lookup-kd x (nf-ctx Γ-ctx) nf-Γ[x]≡kd-nf-j)
                      (∈-var x (⌊⌋-lookup (nfCtx Γ) x nf-Γ[x]≡kd-nf-j)))
    where
      open WfsCtxOps using (lookup-kd)
      nf-Γ[x]≡kd-nf-j = nfCtx-lookup-kd x Γ Γ[x]≡kd-j
  nf-Tp∈ (∈-var x Γ-ctx Γ[x]≡kd-k) | tp _  | ()
  nf-Tp∈ (∈-⊥-f Γ-ctx)    = ∈-⊥-f
  nf-Tp∈ (∈-⊤-f Γ-ctx)    = ∈-⊤-f
  nf-Tp∈ (∈-∀-f k-kd a∈*) = ∈-∀-f (nf-kd k-kd) (nf-Tp∈ a∈*)
  nf-Tp∈ (∈-→-f a∈* b∈*)  = ∈-→-f (nf-Tp∈ a∈*) (nf-Tp∈ b∈*)
  nf-Tp∈ (∈-Π-i {j} {a} j-kd a∈k k-kd) =
    let j′ = nfKind _ j
    in subst (λ l → _ ⊢Nf Λ∙ j′ (nf (kd j′ ∷ _) a) ∈ l ⇒ _) (⌊⌋-nf j)
             (∈-Π-i (nf-kd j-kd) (nf-Tp∈ a∈k))
  nf-Tp∈ (∈-Π-e {a} {b} a∈Πjk b∈j _ _) =
    subst (_ ⊢Nf nf _ a ↓⌜·⌝ nf _ b ∈_) (sym (⌊⌋-Kind/ _))
          (Nf∈-Π-e′ (nf-Tp∈ a∈Πjk) (nf-Tp∈ b∈j))
  nf-Tp∈ (∈-s-i a∈a⋯b)  = nf-Tp∈ a∈a⋯b
  nf-Tp∈ (∈-⇑ a∈j j<∷k) = subst (_ ⊢Nf _ ∈_) (<∷-⌊⌋ j<∷k) (nf-Tp∈ a∈j)

  -- Normalization and simultaneous simplification of kind
  -- well-formedness: well-formed kinds normalize to simply
  -- well-formed kinds.
  nf-kd : ∀ {n} {Γ : Ctx n} {k} →
          let Γ′ = nfCtx Γ in Γ ⊢ k kd → ⌊ Γ′ ⌋Ctx ⊢ nfKind Γ′ k kds
  nf-kd (kd-⋯ a∈* b∈*)   = kds-⋯ (nf-Tp∈ a∈*) (nf-Tp∈ b∈*)
  nf-kd (kd-Π j-kd k-kd) = kds-Π (nf-kd j-kd) (nf-kd k-kd)

  -- Normalization and simultaneous simplification of contexts:
  -- well-formed contexts normalize to simply well-formed simple
  -- contexts.

  nf-wf : ∀ {n} {Γ : Ctx n} {a} →
          let Γ′ = nfCtx Γ in Γ ⊢ a wf → ⌊ Γ′ ⌋Ctx ⊢ nfAsc Γ′ a wfs
  nf-wf (wf-kd k-kds) = wfs-kd (nf-kd k-kds)
  nf-wf (wf-tp a∈*)   = wfs-tp (nf-Tp∈ a∈*)

  nf-ctx : ∀ {n} {Γ : Ctx n} → Γ ctx → nfCtx Γ ctxs
  nf-ctx D.[]             = S.[]
  nf-ctx (a-wf D.∷ Γ-ctx) = nf-wf a-wf S.∷ nf-ctx Γ-ctx

-- Normal forms of type operators are η-long.
≈-η-nf : ∀ {n} {Γ : Ctx n} {a j k} → Γ ⊢Tp a ∈ Π j k →
         let nf-Γ = nfCtx Γ
         in nf nf-Γ (Λ j (weaken a · var zero)) ≈ nf nf-Γ a
≈-η-nf a∈Πjk with Nf∈-⇒-inv (nf-Tp∈ a∈Πjk)
≈-η-nf {_} {Γ} {a} {j} {k} a∈Πjk
  | l , b , l-kds , b∈⌊k⌋ , ⌊l⌋≡⌊j⌋ , nf-a≡Λlb =
  begin
    nf nf-Γ (Λ j (weaken a · var zero))
  ≈⟨ ≈-Λ∙ ⌊nf-j⌋≡⌊l⌋ (begin
        nf-a′ ↓⌜·⌝ η-exp (weakenKind′ nf-j) (var∙ zero)
      ≡⟨ cong (_↓⌜·⌝ η-exp (weakenKind′ nf-j) (var∙ zero))
              (trans (sym (nf-weaken (kd nf-j) a)) (cong weakenElim nf-a≡Λlb)) ⟩
        weakenElim (Λ∙ l b) ↓⌜·⌝ η-exp (weakenKind′ nf-j) (var∙ zero)
      ≡⟨⟩
        b Elim/Var (VarSubst.wk VarSubst.↑) /⟨ ⌊ weakenKind′ l ⌋ ⟩
          hsub (η-exp (weakenKind′ nf-j) (var∙ zero))
      ≈⟨ ≈-[∈] (≈-refl (b Elim/Var (VarSubst.wk VarSubst.↑)))
               (≈-η-exp ⌊nf-j/wk⌋≡⌊l/wk⌋ (≈-var∙ zero)) ⟩
        b Elim/Var (VarSubst.wk VarSubst.↑) /⟨ ⌊ weakenKind′ l ⌋ ⟩
          hsub (η-exp (weakenKind′ l) (var∙ zero))
      ≈⟨ Nf∈-[]-η-var [] l-kds
                      (subst (λ l → kd l ∷ _ ⊢Nf b ∈ _) (sym ⌊l⌋≡⌊j⌋) b∈⌊k⌋) ⟩
        b
      ∎) ⟩
    Λ∙ l b
  ≡⟨ sym nf-a≡Λlb ⟩
    nf nf-Γ a
  ∎
  where
    nf-Γ   = nfCtx Γ
    nf-j   = nfKind nf-Γ j
    nf-j∷Γ = kd nf-j ∷ nf-Γ
    nf-a′  = nf nf-j∷Γ (weaken a)
    ⌊nf-j⌋≡⌊l⌋ = begin
      ⌊ nf-j ⌋ ≡⟨ ⌊⌋-nf j ⟩
      ⌊ j ⌋    ≡⟨ sym ⌊l⌋≡⌊j⌋ ⟩
      ⌊ l ⌋    ∎
      where open ≡-Reasoning
    ⌊nf-j/wk⌋≡⌊l/wk⌋ = begin
      ⌊ weakenKind′ nf-j ⌋   ≡⟨ ⌊⌋-Kind′/Var nf-j ⟩
      ⌊ nf-j ⌋               ≡⟨ ⌊nf-j⌋≡⌊l⌋ ⟩
      ⌊ l ⌋                  ≡⟨ sym (⌊⌋-Kind′/Var l) ⟩
      ⌊ weakenKind′ l ⌋      ∎
      where open ≡-Reasoning
    open ≈-Reasoning

-- Well-kinded hereditary substitions.

infix 4 _⊢/Tp⟨_⟩_∈_

data _⊢/Tp⟨_⟩_∈_ : ∀ {m n} → Ctx n → SKind → SVSub m n → Ctx m → Set where
  ∈-hsub : ∀ {n} {Γ : Ctx n} {k a} →
           Γ ⊢Tp a ∈ k → Γ ⊢/Tp⟨ ⌊ k ⌋ ⟩ hsub ⌜ a ⌝ ∈ kd k ∷ Γ
  ∈-H↑   : ∀ {m n Δ k Γ} {σ : SVSub m n} {a} →
           Δ ⊢/Tp⟨ k ⟩ σ ∈ Γ → (a TermAsc/ toSub σ) ∷ Δ ⊢/Tp⟨ k ⟩ σ H↑ ∈ a ∷ Γ

-- Normalize and simplify a well-kinded hereditary substitution.

nf-/⟨⟩∈ : ∀ {m n k Δ} {σ : SVSub m n} {Γ} → Δ ⊢/Tp⟨ k ⟩ σ ∈ Γ →
          ⌊ nfCtx Δ ⌋Ctx ⊢/⟨ k ⟩ nfSVSub (nfCtx Δ) σ ∈ ⌊ nfCtx Γ ⌋Ctx
nf-/⟨⟩∈ (∈-hsub {Γ = Γ} {k} {a} a∈k) =
  subst₂ (⌊ nfCtx Γ ⌋Ctx ⊢/⟨ ⌊ k ⌋ ⟩_∈_)
         (cong (hsub ∘ nf (nfCtx Γ)) (sym (⌞⌟∘⌜⌝-id a)))
         (cong (λ j → kd j ∷ ⌊ nfCtx Γ ⌋Ctx) (sym (⌊⌋-nf k)))
         (∈-hsub (nf-Tp∈ a∈k))
nf-/⟨⟩∈ (∈-H↑ {Δ = Δ} {k} {Γ} {σ} {a} σ∈Γ) =
  subst (_⊢/⟨ k ⟩ nfSVSub (nfCtx (a/σ ∷ Δ)) (σ H↑) ∈ ⌊ nfCtx (a ∷ Γ) ⌋Ctx)
        (cong (_∷ ⌊ nfCtx Δ ⌋Ctx) (begin
          ⌊ nfAsc (nfCtx Γ) a ⌋Asc                     ≡⟨ ⌊⌋Asc-nf a ⟩
          ⌊ a ⌋Asc                                     ≡˘⟨ S.⌊⌋-TermAsc/ a ⟩
          ⌊ a TermAsc/ toSub σ ⌋Asc                    ≡˘⟨ ⌊⌋Asc-nf _ ⟩
          ⌊ nfAsc (nfCtx Δ) (a TermAsc/ toSub σ) ⌋Asc  ∎))
        (∈-H↑ (nf-/⟨⟩∈ σ∈Γ))
  where
    open ≡-Reasoning
    a/σ = a TermAsc/ toSub σ

open ≈-Reasoning
private module P = ≡-Reasoning

mutual

  -- Substitution (weakly) commutes with normalization.

  nf-/⟨⟩ : ∀ {m n Δ k Γ} {σ : SVSub m n} {a j} →
           Γ ⊢Tp a ∈ j → Δ ⊢/Tp⟨ k ⟩ σ ∈ Γ →
           nf (nfCtx Δ) (a / toSub σ)  ≈
             nf (nfCtx Γ) a /⟨ k ⟩ nfSVSub (nfCtx Δ) σ
  nf-/⟨⟩ {Γ = Γ} (∈-var x Γ-ctx Γ[x]≡kd-j) σ∈Γ
    with E.lookup (nfCtx Γ) x | nfCtx-lookup-kd x Γ Γ[x]≡kd-j
  nf-/⟨⟩ {Δ = Δ} {k} {Γ} {σ} {_} {j} (∈-var x Γ-ctx Γ[x]≡kd-j) σ∈Γ
    | kd nf-j | refl with hit? σ x
  ... | hit a hitP =
    let open WfsCtxOps using (lookup-kd)
        nf-Γ-ctxs       = nf-ctx Γ-ctx
        nf-Γ[x]≡kd-nf-j = nfCtx-lookup-kd x Γ Γ[x]≡kd-j
        nf-j-kds  = lookup-kd x nf-Γ-ctxs nf-Γ[x]≡kd-nf-j
        hitP-nf-σ = nf-Hit (nfCtx Δ) hitP
        x∈⌊j⌋     = ∈-var x (⌊⌋-lookup (nfCtx Γ) x nf-Γ[x]≡kd-nf-j)
    in begin
      nf (nfCtx Δ) (Vec.lookup (toSub σ) x)
    ≡⟨ cong (nf (nfCtx Δ)) (lookup-toSub σ x) ⟩
      nf (nfCtx Δ) (⌞ toElim (lookupSV σ x) ⌟)
    ≡⟨ cong (nf (nfCtx Δ) ∘ ⌞_⌟ ∘ toElim) (lookup-Hit hitP) ⟩
      nf (nfCtx Δ) ⌞ a ⌟
    ≡˘⟨ cong (_?∙∙⟨ k ⟩ []) (lookup-Hit hitP-nf-σ) ⟩
      var∙ x /⟨ k ⟩ nfSVSub (nfCtx Δ) σ
    ≈˘⟨ η-exp-var-Hit-/⟨⟩ hitP-nf-σ nf-j-kds x∈⌊j⌋ (nf-/⟨⟩∈ σ∈Γ) ⟩
      η-exp (nfKind (nfCtx Γ) j) (var∙ x) /⟨ k ⟩ nfSVSub (nfCtx Δ) σ
    ∎
  ... | miss y missP =
    let open WfsCtxOps using (lookup-kd)
        nf-Γ-ctxs       = nf-ctx Γ-ctx
        nf-Γ[x]≡kd-nf-j = nfCtx-lookup-kd x Γ Γ[x]≡kd-j
        nf-j-kds   = lookup-kd x nf-Γ-ctxs nf-Γ[x]≡kd-nf-j
        missP-nf-σ = nf-Miss (nfCtx Δ) missP
        x∈⌊j⌋      = ∈-var x (⌊⌋-lookup (nfCtx Γ) x nf-Γ[x]≡kd-nf-j)
    in begin
      nf (nfCtx Δ) (Vec.lookup (toSub σ) x)
    ≡⟨ cong (nf (nfCtx Δ)) (lookup-toSub σ x) ⟩
      nf (nfCtx Δ) ⌞ toElim (lookupSV σ x) ⌟
    ≡⟨ cong (nf (nfCtx Δ) ∘ ⌞_⌟ ∘ toElim) (lookup-Miss missP) ⟩
      nf (nfCtx Δ) (var y)
    ≈⟨ nf-var-kd-⌊⌋ (nfCtx Δ) y (P.begin
          ⌊ E.lookup (nfCtx Δ) y ⌋Asc
        P.≡˘⟨ ⌊⌋Asc-lookup (nfCtx Δ) y ⟩
          S.lookup ⌊ nfCtx Δ ⌋Ctx y
        P.≡⟨ lookup-/⟨⟩∈-Miss (nf-/⟨⟩∈ σ∈Γ) x∈⌊j⌋ missP-nf-σ  ⟩
          S.lookup ⌊ nfCtx Γ ⌋Ctx x
        P.≡⟨ ⌊⌋-lookup (nfCtx Γ) x nf-Γ[x]≡kd-nf-j ⟩
          kd ⌊ nfKind (nfCtx Γ) j ⌋
        P.≡˘⟨ cong kd (⌊⌋-Kind/⟨⟩ (nfKind (nfCtx Γ) j)) ⟩
          kd ⌊ nfKind (nfCtx Γ) j Kind/⟨ k ⟩ nfSVSub (nfCtx Δ) σ ⌋
        P.∎) ⟩
      η-exp (nfKind (nfCtx Γ) j Kind/⟨ k ⟩ nfSVSub (nfCtx Δ) σ) (var∙ y)
    ≡˘⟨ η-exp-ne-Miss-/⟨⟩ x y [] (nfKind (nfCtx Γ) j) missP-nf-σ ⟩
      η-exp (nfKind (nfCtx Γ) j) (var∙ x) /⟨ k ⟩ nfSVSub (nfCtx Δ) σ
    ∎
  nf-/⟨⟩ (∈-var _ _ _) σ∈Γ | tp a | ()
  nf-/⟨⟩ (∈-⊥-f Γ-ctx) σ∈Γ = ≈-refl _
  nf-/⟨⟩ (∈-⊤-f Γ-ctx) σ∈Γ = ≈-refl _
  nf-/⟨⟩ (∈-∀-f {j} j-kd a∈*) σ∈Γ =
    ≈-∀∙ (nfKind-/⟨⟩ j-kd σ∈Γ) (nf-/⟨⟩ a∈* (∈-H↑ σ∈Γ))
  nf-/⟨⟩ (∈-→-f a∈* b∈*) σ∈Γ =
    ≈-⇒∙ (nf-/⟨⟩ a∈* σ∈Γ) (nf-/⟨⟩ b∈* σ∈Γ)
  nf-/⟨⟩ (∈-Π-i j-kd a∈k k-kd) σ∈Γ =
    ≈-Λ∙ (≋-⌊⌋ (nfKind-/⟨⟩ j-kd σ∈Γ)) (nf-/⟨⟩ a∈k (∈-H↑ σ∈Γ))
  nf-/⟨⟩ {Δ = Δ} {k} {Γ} {σ} {a · b} (∈-Π-e a∈Πjk b∈j _ _) σ∈Γ = begin
      nf (nfCtx Δ) (a / toSub σ) ↓⌜·⌝ nf (nfCtx Δ) (b / toSub σ)
    ≈⟨ ≈-↓⌜·⌝ (nf-/⟨⟩ a∈Πjk σ∈Γ) (nf-/⟨⟩ b∈j σ∈Γ) ⟩
      (nf (nfCtx Γ) a /⟨ k ⟩ nfSVSub (nfCtx Δ) σ) ↓⌜·⌝
        (nf (nfCtx Γ) b /⟨ k ⟩ nfSVSub (nfCtx Δ) σ)
    ≡⟨ sym (Nf∈-Π-e-/⟨⟩′ (nf-Tp∈ a∈Πjk) (nf-Tp∈ b∈j) (nf-/⟨⟩∈ σ∈Γ)) ⟩
      nf (nfCtx Γ) a ↓⌜·⌝ nf (nfCtx Γ) b /⟨ k ⟩ nfSVSub (nfCtx Δ) σ
    ∎
  nf-/⟨⟩ (∈-s-i a∈c⋯d)  σ∈Γ = nf-/⟨⟩ a∈c⋯d σ∈Γ
  nf-/⟨⟩ (∈-⇑ a∈j j<∷k) σ∈Γ = nf-/⟨⟩ a∈j σ∈Γ

  nfKind-/⟨⟩ : ∀ {m n Δ k Γ} {σ : SVSub m n} {j} →
               Γ ⊢ j kd → Δ ⊢/Tp⟨ k ⟩ σ ∈ Γ →
               nfKind (nfCtx Δ) (j Kind/ toSub σ)  ≋
                 nfKind (nfCtx Γ) j Kind/⟨ k ⟩ nfSVSub (nfCtx Δ) σ
  nfKind-/⟨⟩ (kd-⋯ a∈* b∈*)   σ∈Γ = ≋-⋯ (nf-/⟨⟩ a∈* σ∈Γ) (nf-/⟨⟩ b∈* σ∈Γ)
  nfKind-/⟨⟩ (kd-Π j-kd k-kd) σ∈Γ =
    ≋-Π (nfKind-/⟨⟩ j-kd σ∈Γ) (nfKind-/⟨⟩ k-kd (∈-H↑ σ∈Γ))

-- Shorthands.

nf-[] : ∀ {n} {Γ : Ctx n} {k a j b} →
        kd k ∷ Γ ⊢Tp a ∈ j → Γ ⊢Tp b ∈ k →
        nf (nfCtx Γ) (a [ b ])  ≈
          nf (nfCtx (kd k ∷ Γ)) a [ nf (nfCtx Γ) b ∈ ⌊ k ⌋ ]
nf-[] {_} {Γ} {k} {a} a∈j b∈k =
  subst (λ c → nf (nfCtx Γ) (a [ c ]) ≈
               (nf (nfCtx (kd k ∷ Γ)) a [ nf (nfCtx Γ) c ∈ ⌊ k ⌋ ]))
        (⌞⌟∘⌜⌝-id _) (nf-/⟨⟩ a∈j (∈-hsub b∈k))

nfKind-[] : ∀ {n} {Γ : Ctx n} {k j b} →
            kd k ∷ Γ ⊢ j kd → Γ ⊢Tp b ∈ k →
            nfKind (nfCtx Γ) (j Kind[ b ])  ≋
              nfKind (nfCtx (kd k ∷ Γ)) j Kind[ nf (nfCtx Γ) b ∈ ⌊ k ⌋ ]
nfKind-[] {_} {Γ} {k} {j} j-kd b∈k =
  subst (λ c → nfKind (nfCtx Γ) (j Kind[ c ]) ≋
               (nfKind (nfCtx (kd k ∷ Γ)) j Kind[ nf (nfCtx Γ) c ∈ ⌊ k ⌋ ]))
        (⌞⌟∘⌜⌝-id _) (nfKind-/⟨⟩ j-kd (∈-hsub b∈k))


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

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

module cohomology.RephraseSubFinCoboundary (OT : OrdinaryTheory lzero) where

open OrdinaryTheory OT
open import cohomology.SphereEndomorphism cohomology-theory
open import cohomology.Sphere OT
open import cohomology.SubFinBouquet OT

abstract
  rephrase-in-degree : ∀ n {I : ℕ} {JA JB : Type₀}
    (JB-ac : has-choice 0 JB lzero)
    (JB-dec : has-dec-eq JB)
    {J} (p : Fin J ≃ Coprod JA JB)
    (f : ⊙FinBouquet I (S n) ⊙→ ⊙Susp (⊙Bouquet JB n)) g
    → GroupIso.f (C-FinBouquet-diag (S n) I)
        (CEl-fmap (ℕ-to-ℤ (S n)) f
          (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Bouquet JB n))
            (GroupIso.g (C-SubFinBouquet-diag n JB-ac (–> p)) g)))
    ∼ λ <I → Group.subsum-r (C2 0) (–> p)
          (λ b → Group.exp (C2 0) (g b) (⊙SphereS-endo-degree n (⊙Susp-fmap (⊙bwproj JB-dec b) ⊙∘ f ⊙∘ ⊙fwin <I)))
  rephrase-in-degree n {I} {JA} {JB} JB-ac JB-dec {J} p f g <I =
    GroupIso.f (C-FinBouquet-diag (S n) I)
      (CEl-fmap (ℕ-to-ℤ (S n)) f
        (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Bouquet JB n))
          (GroupIso.g (C-SubFinBouquet-diag n JB-ac (–> p)) g)))
      <I
      =⟨ ap
          (λ g → GroupIso.f (C-FinBouquet-diag (S n) I)
            (CEl-fmap (ℕ-to-ℤ (S n)) f
              (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Bouquet JB n))
                g)) <I)
          (inverse-C-SubFinBouquet-diag-β n JB-ac JB-dec p g) ⟩
    GroupIso.f (C-FinBouquet-diag (S n) I)
      (CEl-fmap (ℕ-to-ℤ (S n)) f
        (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Bouquet JB n))
          (Group.subsum-r (C (ℕ-to-ℤ n) (⊙Bouquet JB n)) (–> p)
            (λ b → CEl-fmap (ℕ-to-ℤ n) (⊙bwproj JB-dec b)
              (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                (GroupIso.g (C-Sphere-diag n) (g b)))))))
      <I
      =⟨ ap (λ g → GroupIso.f (C-FinBouquet-diag (S n) I) g <I) $
          GroupHom.pres-subsum-r
            (C-fmap (ℕ-to-ℤ (S n)) f ∘ᴳ GroupIso.g-hom (C-Susp (ℕ-to-ℤ n) (⊙Bouquet JB n)))
            (–> p)
            (λ b → CEl-fmap (ℕ-to-ℤ n) (⊙bwproj JB-dec b)
              (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                (GroupIso.g (C-Sphere-diag n) (g b)))) ⟩
    GroupIso.f (C-FinBouquet-diag (S n) I)
      (Group.subsum-r (C (ℕ-to-ℤ (S n)) (⊙FinBouquet I (S n))) (–> p)
        (λ b → CEl-fmap (ℕ-to-ℤ (S n)) f
          (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Bouquet JB n))
            (CEl-fmap (ℕ-to-ℤ n) (⊙bwproj JB-dec b)
              (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                (GroupIso.g (C-Sphere-diag n) (g b)))))))
      <I
      =⟨ ap
          (λ f → GroupIso.f (C-FinBouquet-diag (S n) I)
            (Group.subsum-r (C (ℕ-to-ℤ (S n)) (⊙FinBouquet I (S n))) (–> p) f) <I)
          (λ= λ b → ap (CEl-fmap (ℕ-to-ℤ (S n)) f) $
            C-Susp-fmap' (ℕ-to-ℤ n) (⊙bwproj JB-dec b) □$ᴳ
              CEl-fmap (ℕ-to-ℤ n) ⊙lift (GroupIso.g (C-Sphere-diag n) (g b))) ⟩
    GroupIso.f (C-FinBouquet-diag (S n) I)
      (Group.subsum-r (C (ℕ-to-ℤ (S n)) (⊙FinBouquet I (S n))) (–> p)
        (λ b → CEl-fmap (ℕ-to-ℤ (S n)) f
          (CEl-fmap (ℕ-to-ℤ (S n)) (⊙Susp-fmap (⊙bwproj JB-dec b))
            (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
              (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                (GroupIso.g (C-Sphere-diag n) (g b)))))))
      <I
      =⟨ C-FinBouquet-diag-β (S n) I
          (Group.subsum-r (C (ℕ-to-ℤ (S n)) (⊙FinBouquet I (S n))) (–> p)
            (λ b → CEl-fmap (ℕ-to-ℤ (S n)) f
              (CEl-fmap (ℕ-to-ℤ (S n)) (⊙Susp-fmap (⊙bwproj JB-dec b))
                (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
                  (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                    (GroupIso.g (C-Sphere-diag n) (g b)))))))
          <I ⟩
    GroupIso.f (C-Sphere-diag (S n))
      (CEl-fmap (ℕ-to-ℤ (S n)) ⊙lower
        (CEl-fmap (ℕ-to-ℤ (S n)) (⊙fwin <I)
          (Group.subsum-r (C (ℕ-to-ℤ (S n)) (⊙FinBouquet I (S n))) (–> p)
            (λ b → CEl-fmap (ℕ-to-ℤ (S n)) f
              (CEl-fmap (ℕ-to-ℤ (S n)) (⊙Susp-fmap (⊙bwproj JB-dec b))
                (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
                  (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                    (GroupIso.g (C-Sphere-diag n) (g b)))))))))
      =⟨ GroupHom.pres-subsum-r
          (  GroupIso.f-hom (C-Sphere-diag (S n))
          ∘ᴳ C-fmap (ℕ-to-ℤ (S n)) ⊙lower
          ∘ᴳ C-fmap (ℕ-to-ℤ (S n)) (⊙fwin <I))
          (–> p)
          (λ b → CEl-fmap (ℕ-to-ℤ (S n)) f
            (CEl-fmap (ℕ-to-ℤ (S n)) (⊙Susp-fmap (⊙bwproj JB-dec b))
              (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
                (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                  (GroupIso.g (C-Sphere-diag n) (g b)))))) ⟩
    Group.subsum-r (C2 0) (–> p)
      (λ b → GroupIso.f (C-Sphere-diag (S n))
        (CEl-fmap (ℕ-to-ℤ (S n)) ⊙lower
          (CEl-fmap (ℕ-to-ℤ (S n)) (⊙fwin <I)
            (CEl-fmap (ℕ-to-ℤ (S n)) f
              (CEl-fmap (ℕ-to-ℤ (S n)) (⊙Susp-fmap (⊙bwproj JB-dec b))
                (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
                  (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                    (GroupIso.g (C-Sphere-diag n) (g b)))))))))
      =⟨ ap (Group.subsum-r (C2 0) (–> p))
          (λ= λ b → ap (GroupIso.f (C-Sphere-diag (S n)) ∘ CEl-fmap (ℕ-to-ℤ (S n)) ⊙lower) $
              ∘-CEl-fmap (ℕ-to-ℤ (S n)) (⊙fwin <I) f _
            ∙ ∘-CEl-fmap (ℕ-to-ℤ (S n)) (f ⊙∘ ⊙fwin <I) (⊙Susp-fmap (⊙bwproj JB-dec b)) _
            ∙ CEl-fmap-⊙Sphere-endo-η (ℕ-to-ℤ (S n)) n
                (⊙Susp-fmap (⊙bwproj JB-dec b) ⊙∘ f ⊙∘ ⊙fwin <I)
                (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
                  (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                    (GroupIso.g (C-Sphere-diag n) (g b))))) ⟩
    Group.subsum-r (C2 0) (–> p)
      (λ b → GroupIso.f (C-Sphere-diag (S n))
        (CEl-fmap (ℕ-to-ℤ (S n)) ⊙lower
          (Group.exp (C (ℕ-to-ℤ (S n)) (⊙Sphere (S n)))
            (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
              (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                (GroupIso.g (C-Sphere-diag n) (g b))))
            (⊙SphereS-endo-degree n (⊙Susp-fmap (⊙bwproj JB-dec b) ⊙∘ f ⊙∘ ⊙fwin <I)))))
      =⟨ ap (Group.subsum-r (C2 0) (–> p))
          (λ= λ b →
            GroupHom.pres-exp
              (GroupIso.f-hom (C-Sphere-diag (S n)) ∘ᴳ C-fmap (ℕ-to-ℤ (S n)) ⊙lower)
              (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
                (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                  (GroupIso.g (C-Sphere-diag n) (g b))))
              (⊙SphereS-endo-degree n (⊙Susp-fmap (⊙bwproj JB-dec b) ⊙∘ f ⊙∘ ⊙fwin <I))) ⟩
    Group.subsum-r (C2 0) (–> p)
      (λ b → Group.exp (C2 0)
        (GroupIso.f (C-Sphere-diag n)
          (–> (CEl-Susp (ℕ-to-ℤ n) (⊙Lift (⊙Sphere n)))
            (CEl-fmap (ℕ-to-ℤ (S n)) (⊙lift {j = lzero} ⊙∘ ⊙Susp-fmap {Y = ⊙Sphere n} ⊙lower)
              (CEl-fmap (ℕ-to-ℤ (S n)) ⊙lower
                (<– (CEl-Susp (ℕ-to-ℤ n) (⊙Sphere n))
                  (CEl-fmap (ℕ-to-ℤ n) ⊙lift
                    (GroupIso.g (C-Sphere-diag n) (g b))))))))
        (⊙SphereS-endo-degree n (⊙Susp-fmap (⊙bwproj JB-dec b) ⊙∘ f ⊙∘ ⊙fwin <I)))
      =⟨ ap (Group.subsum-r (C2 0) (–> p))
          (λ= λ b → ap (λ g → Group.exp (C2 0) g (⊙SphereS-endo-degree n (⊙Susp-fmap (⊙bwproj JB-dec b) ⊙∘ f ⊙∘ ⊙fwin <I))) $
            ap (GroupIso.f (C-Sphere-diag n) ∘ –> (CEl-Susp (ℕ-to-ℤ n) (⊙Lift (⊙Sphere n))))
              ( CEl-fmap-∘ (ℕ-to-ℤ (S n)) ⊙lift (⊙Susp-fmap {Y = ⊙Sphere n} ⊙lower) _
              ∙ ap (CEl-fmap (ℕ-to-ℤ (S n)) (⊙Susp-fmap {Y = ⊙Sphere n} ⊙lower))
                  ( ∘-CEl-fmap (ℕ-to-ℤ (S n)) ⊙lift ⊙lower _
                  ∙ CEl-fmap-idf (ℕ-to-ℤ (S n)) _))
            ∙ ap (GroupIso.f (C-Sphere-diag n))
                ( (C-Susp-fmap (ℕ-to-ℤ n) ⊙lower □$ᴳ _)
                ∙ ap (CEl-fmap (ℕ-to-ℤ n) ⊙lower) (GroupIso.f-g (C-Susp (ℕ-to-ℤ n) (⊙Sphere n)) _)
                ∙ ∘-CEl-fmap (ℕ-to-ℤ n) ⊙lower ⊙lift _
                ∙ CEl-fmap-idf (ℕ-to-ℤ n) _)
            ∙ GroupIso.f-g (C-Sphere-diag n) (g b)) ⟩
    Group.subsum-r (C2 0) (–> p)
      (λ b → Group.exp (C2 0) (g b)
        (⊙SphereS-endo-degree n (⊙Susp-fmap (⊙bwproj JB-dec b) ⊙∘ f ⊙∘ ⊙fwin <I)))
      =∎

  {- the version specialzied to [Fin J]. -}
  rephrase-in-degree' : ∀ n {I J : ℕ} (f : ⊙FinBouquet I (S n) ⊙→ ⊙Susp (⊙FinBouquet J n)) g
    → GroupIso.f (C-FinBouquet-diag (S n) I)
        (CEl-fmap (ℕ-to-ℤ (S n)) f
          (<– (CEl-Susp (ℕ-to-ℤ n) (⊙FinBouquet J n))
            (GroupIso.g (C-FinBouquet-diag n J) g)))
    ∼ λ <I → Group.sum (C2 0)
          (λ <J → Group.exp (C2 0) (g <J) (⊙SphereS-endo-degree n (⊙Susp-fmap (⊙fwproj <J) ⊙∘ f ⊙∘ ⊙fwin <I)))
  rephrase-in-degree' n {I} {J} f g =
    rephrase-in-degree n {I} {JA = Empty} {JB = Fin J}
      (Fin-has-choice 0 lzero) Fin-has-dec-eq (⊔₁-Empty (Fin J) ⁻¹) f g


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Substitution.Introductions.IdUniv {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Properties
import Definition.Typed.Weakening as Twk
open import Definition.Typed.EqualityRelation
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 Definition.LogicalRelation.Application
open import Definition.LogicalRelation.Substitution
import Definition.LogicalRelation.Weakening as Lwk
open import Definition.LogicalRelation.Substitution.Properties
import Definition.LogicalRelation.Substitution.Irrelevance as S
open import Definition.LogicalRelation.Substitution.Reflexivity
open import Definition.LogicalRelation.Substitution.Weakening
-- open import Definition.LogicalRelation.Substitution.Introductions.Nat
open import Definition.LogicalRelation.Substitution.Introductions.Empty
open import Definition.LogicalRelation.Substitution.Introductions.Pi
open import Definition.LogicalRelation.Substitution.Introductions.Idlemmas
open import Definition.LogicalRelation.Substitution.MaybeEmbed
-- open import Definition.LogicalRelation.Substitution.Introductions.SingleSubst
open import Definition.LogicalRelation.Substitution.Introductions.Universe

open import Tools.Product
open import Tools.Empty
import Tools.Unit as TU
import Tools.PropositionalEquality as PE
import Data.Nat as Nat

[Id]SProp : ∀ {t u Γ}
         (⊢Γ : ⊢ Γ)
         ([t] : Γ ⊩⟨ ι ⁰ ⟩ t ^ [ % , ι ⁰ ])
         ([u] : Γ ⊩⟨ ι ⁰ ⟩ u ^ [ % , ι ⁰ ])
         → Γ ⊩⟨ ι ¹ ⟩ Id (SProp ⁰) t u ^ [ % , ι ¹ ]
[Id]SProp {t} {u} {Γ} ⊢Γ [t] [u] =
  let
    ⊢t = escape [t]
    ⊢u = escape [u]
    ⊢wkt = Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u) ⊢t
    ⊢wku = Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t) ⊢u
    [wkt] = λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ [t]
    [wku] = λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ [u]

    [t▹u] : Γ ⊩⟨ ι ⁰ ⟩ t ^ % ° ⁰ ▹▹ u ° ⁰ ° ¹ ^ [ % , ι ¹ ]
    [t▹u] = Πᵣ′ % ⁰ ⁰ (<is≤ 0<1) (<is≤ 0<1) t (wk1 u)
      (idRed:*: (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢t ▹ un-univ ⊢wku))) ⊢t ⊢wku
      (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢t (≅-un-univ (escapeEqRefl [t])) (≅-un-univ (≅-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t) (escapeEqRefl [u])))))
      [wkt] ([nondep] [u] [wkt]) ([nondepext] [u] [wkt])
    [u▹t] : Γ ⊩⟨ ι ⁰ ⟩ u ^ % ° ⁰ ▹▹ t ° ⁰ ° ¹ ^ [ % , ι ¹ ]
    [u▹t] = Πᵣ′ % ⁰ ⁰ (<is≤ 0<1) (<is≤ 0<1) u (wk1 t)
      (idRed:*: (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢u ▹ un-univ ⊢wkt))) ⊢u ⊢wkt
      (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢u (≅-un-univ (escapeEqRefl [u])) (≅-un-univ (≅-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u) (escapeEqRefl [t])))))
      [wku] ([nondep] [t] [wku]) ([nondepext] [t] [wku])
    [wkt▹u] = λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ (emb emb< [t▹u])
    ⊢t▹u = escape [t▹u]
    ⊢u▹t = Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t▹u) (escape [u▹t])

    ⊢Id = univ (Idⱼ (univ 0<1 ⊢Γ) (un-univ ⊢t) (un-univ ⊢u))
    ⊢Eq = univ (∃ⱼ un-univ ⊢t▹u ▹ un-univ ⊢u▹t)
  in ∃ᵣ′ (t ^ % ° ⁰ ▹▹ u ° ⁰ ° ¹) (wk1 (u ^ % ° ⁰ ▹▹ t ° ⁰ ° ¹))
    [[ ⊢Id , ⊢Eq , univ (Id-SProp (un-univ ⊢t) (un-univ ⊢u)) ⇨ id ⊢Eq ]] ⊢t▹u ⊢u▹t
    (≅-univ (≅ₜ-∃-cong ⊢t▹u (≅-un-univ (escapeEqRefl [t▹u])) (≅-un-univ (≅-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t▹u) (escapeEqRefl [u▹t])))))
    [wkt▹u] ([nondep] (emb emb< [u▹t]) [wkt▹u]) ([nondepext] (emb emb< [u▹t]) [wkt▹u])

[IdExt]SProp : ∀ {t t′ u u′ Γ}
         (⊢Γ : ⊢ Γ)
         ([t] : Γ ⊩⟨ ι ⁰ ⟩ t ^ [ % , ι ⁰ ])
         ([t′] : Γ ⊩⟨ ι ⁰ ⟩ t′ ^ [ % , ι ⁰ ])
         ([t≡t′] : Γ ⊩⟨ ι ⁰ ⟩ t ≡ t′ ^ [ % , ι ⁰ ] / [t])
         ([u] : Γ ⊩⟨ ι ⁰ ⟩ u ^ [ % , ι ⁰ ])
         ([u′] : Γ ⊩⟨ ι ⁰ ⟩ u′ ^ [ % , ι ⁰ ])
         ([u≡u′] : Γ ⊩⟨ ι ⁰ ⟩ u ≡ u′ ^ [ % , ι ⁰ ] / [u])
         → Γ ⊩⟨ ι ¹ ⟩ Id (SProp ⁰) t u ≡ Id (SProp ⁰) t′ u′ ^ [ % , ι ¹ ] / [Id]SProp ⊢Γ [t] [u]
[IdExt]SProp {t} {t′} {u} {u′} {Γ} ⊢Γ [t] [t′] [t≡t′] [u] [u′] [u≡u′] =
  let ⊢t = escape [t]
      ⊢t′ = escape [t′]
      ⊢u = escape [u]
      ⊢u′ = escape [u′]
      ⊢t≡t′ = ≅-un-univ (escapeEq [t] [t≡t′])
      ⊢u≡u′ = ≅-un-univ (escapeEq [u] [u≡u′])

      ⊢wkt = Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u) ⊢t
      ⊢wku = Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t) ⊢u
      [wkt] = λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ [t]
      [wku] = λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ [u]

      ⊢wkt′ = Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u′) ⊢t′
      ⊢wku′ = Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t′) ⊢u′
      [wkt′] = λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ [t′]
      [wku′] = λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ [u′]

      [t▹u] : Γ ⊩⟨ ι ⁰ ⟩ t ^ % ° ⁰ ▹▹ u ° ⁰ ° ¹ ^ [ % , ι ¹ ]
      [t▹u] = Πᵣ′ % ⁰ ⁰ (<is≤ 0<1) (<is≤ 0<1) t (wk1 u)
        (idRed:*: (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢t ▹ un-univ ⊢wku))) ⊢t ⊢wku
        (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢t (≅-un-univ (escapeEqRefl [t])) (≅-un-univ (≅-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t) (escapeEqRefl [u])))))
        [wkt] ([nondep] [u] [wkt]) ([nondepext] [u] [wkt])
      [u▹t] : Γ ⊩⟨ ι ⁰ ⟩ u ^ % ° ⁰ ▹▹ t ° ⁰ ° ¹ ^ [ % , ι ¹ ]
      [u▹t] = Πᵣ′ % ⁰ ⁰ (<is≤ 0<1) (<is≤ 0<1) u (wk1 t)
        (idRed:*: (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢u ▹ un-univ ⊢wkt))) ⊢u ⊢wkt
        (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢u (≅-un-univ (escapeEqRefl [u])) (≅-un-univ (≅-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u) (escapeEqRefl [t])))))
        [wku] ([nondep] [t] [wku]) ([nondepext] [t] [wku])

      [t▹u′] : Γ ⊩⟨ ι ⁰ ⟩ t′ ^ % ° ⁰ ▹▹ u′ ° ⁰ ° ¹ ^ [ % , ι ¹ ]
      [t▹u′] = Πᵣ′ % ⁰ ⁰ (<is≤ 0<1) (<is≤ 0<1) t′ (wk1 u′)
        (idRed:*: (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢t′ ▹ un-univ ⊢wku′))) ⊢t′ ⊢wku′
        (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢t′ (≅-un-univ (escapeEqRefl [t′])) (≅-un-univ (≅-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t′) (escapeEqRefl [u′])))))
        [wkt′] ([nondep] [u′] [wkt′]) ([nondepext] [u′] [wkt′])
      [u▹t′] : Γ ⊩⟨ ι ⁰ ⟩ u′ ^ % ° ⁰ ▹▹ t′ ° ⁰ ° ¹ ^ [ % , ι ¹ ]
      [u▹t′] = Πᵣ′ % ⁰ ⁰ (<is≤ 0<1) (<is≤ 0<1) u′ (wk1 t′)
        (idRed:*: (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢u′ ▹ un-univ ⊢wkt′)))
        ⊢u′ ⊢wkt′
        (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢u′ (≅-un-univ (escapeEqRefl [u′])) (≅-un-univ (≅-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u′) (escapeEqRefl [t′])))))
        [wku′] ([nondep] [t′] [wku′]) ([nondepext] [t′] [wku′])

      [t▹u≡t▹u′] : Γ ⊩⟨ ι ⁰ ⟩ t ^ % ° ⁰ ▹▹ u ° ⁰ ° ¹  ≡ t′ ^ % ° ⁰ ▹▹ u′ ° ⁰ ° ¹  ^ [ % , ι ¹ ] / [t▹u]
      [t▹u≡t▹u′] = Π₌ t′ (wk1 u′) (id (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢t′ ▹ un-univ ⊢wku′)))
                      (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢t ⊢t≡t′ (≅ₜ-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t) ⊢u≡u′)))
                      (λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wkEq [ρ] ⊢Δ (emb emb< [t]) [t≡t′])
                      ([nondepext'] [u] [u′] [u≡u′] [wkt] [wkt′])

      [u▹t≡u▹t′] : Γ ⊩⟨ ι ⁰ ⟩ u ^ % ° ⁰ ▹▹ t ° ⁰ ° ¹  ≡ u′ ^ % ° ⁰ ▹▹ t′ ° ⁰ ° ¹  ^ [ % , ι ¹ ] / [u▹t]
      [u▹t≡u▹t′] = Π₌ u′ (wk1 t′) (id (univ (Πⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢u′ ▹ un-univ ⊢wkt′)))
                      (≅-univ (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢u ⊢u≡u′ (≅ₜ-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u) ⊢t≡t′)))
                      (λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wkEq [ρ] ⊢Δ (emb emb< [u]) [u≡u′])
                      ([nondepext'] [t] [t′] [t≡t′] [wku] [wku′])

      ⊢t▹u = escape [t▹u]

  in ∃₌ {l′ = ¹} (t′ ^ % ° ⁰ ▹▹ u′ ° ⁰ ° ¹ ) (wk1 (u′ ^ % ° ⁰ ▹▹ t′ ° ⁰ ° ¹ ))
        (univ (Id-SProp (un-univ ⊢t′) (un-univ ⊢u′)) ⇨ id (univ (××ⱼ (▹▹ⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢t′ ▹ un-univ ⊢u′) ▹
                                                                     (▹▹ⱼ <is≤ 0<1 ▹ <is≤ 0<1 ▹ un-univ ⊢u′ ▹ un-univ ⊢t′))))
        (≅-univ (≅ₜ-∃-cong ⊢t▹u
                           (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) ⊢t ⊢t≡t′ (≅ₜ-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t) ⊢u≡u′))
                           (≅ₜ-Π-cong (<is≤ 0<1) (<is≤ 0<1) (Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t▹u) ⊢u)
                                      (≅ₜ-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t▹u) ⊢u≡u′)
                                      (≅ₜ-wk (Twk.lift (Twk.step Twk.id)) ((⊢Γ ∙ ⊢t▹u) ∙
                                                       (Twk.wk (Twk.step Twk.id) (⊢Γ ∙ ⊢t▹u) ⊢u))
                                             (≅ₜ-wk (Twk.step Twk.id) (⊢Γ ∙ ⊢u) ⊢t≡t′)) )))
        (λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wkEq [ρ] ⊢Δ [t▹u] [t▹u≡t▹u′])
        ([nondepext'] (emb emb< [u▹t]) (emb emb< [u▹t′]) [u▹t≡u▹t′]
                      (λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ (emb emb< [t▹u]))
                      (λ {ρ} {Δ} ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Lwk.wk [ρ] ⊢Δ (emb emb< [t▹u′])))

[Id]U : ∀ {A B Γ}
         (⊢Γ : ⊢ Γ)
         ([A] : Γ ⊩⟨ ι ⁰ ⟩ A ^ [ ! , ι ⁰ ])
         ([B] : Γ ⊩⟨ ι ⁰ ⟩ B ^ [ ! , ι ⁰ ])
         → Γ ⊩⟨ ι ¹ ⟩ Id (U ⁰) A B ^ [ % , ι ¹ ]
[IdExtShape]U : ∀ {A A′ B B′ Γ}
         (⊢Γ : ⊢ Γ)
         ([A] : Γ ⊩⟨ ι ⁰ ⟩ A ^ [ ! , ι ⁰ ])
         ([A′] : Γ ⊩⟨ ι ⁰ ⟩ A′ ^ [ ! , ι ⁰ ])
         (ShapeA : ShapeView Γ (ι ⁰) (ι ⁰) A A′ [ ! , ι ⁰ ] [ ! , ι ⁰ ] [A] [A′])
         ([A≡A′] : Γ ⊩⟨ ι ⁰ ⟩ A ≡ A′ ^ [ ! , ι ⁰ ] / [A])
         ([B] : Γ ⊩⟨ ι ⁰ ⟩ B ^ [ ! , ι ⁰ ])
         ([B′] : Γ ⊩⟨ ι ⁰ ⟩ B′ ^ [ ! , ι ⁰ ])
         (ShapeB : ShapeView Γ (ι ⁰) (ι ⁰) B B′ [ ! , ι ⁰ ] [ ! , ι ⁰ ] [B] [B′])
         ([B≡B′] : Γ ⊩⟨ ι ⁰ ⟩ B ≡ B′ ^ [ ! , ι ⁰ ] / [B])
         → Γ ⊩⟨ ι ¹ ⟩ Id (U ⁰) A B ≡ Id (U ⁰) A′ B′ ^ [ % , ι ¹ ] / [Id]U ⊢Γ [A] [B]
[IdExt]U : ∀ {A A′ B B′ Γ}
         (⊢Γ : ⊢ Γ)
         ([A] : Γ ⊩⟨ ι ⁰ ⟩ A ^ [ ! , ι ⁰ ])
         ([A′] : Γ ⊩⟨ ι ⁰ ⟩ A′ ^ [ ! , ι ⁰ ])
         ([A≡A′] : Γ ⊩⟨ ι ⁰ ⟩ A ≡ A′ ^ [ ! , ι ⁰ ] / [A])
         ([B] : Γ ⊩⟨ ι ⁰ ⟩ B ^ [ ! , ι ⁰ ])
         ([B′] : Γ ⊩⟨ ι ⁰ ⟩ B′ ^ [ ! , ι ⁰ ])
         ([B≡B′] : Γ ⊩⟨ ι ⁰ ⟩ B ≡ B′ ^ [ ! , ι ⁰ ] / [B])
         → Γ ⊩⟨ ι ¹ ⟩ Id (U ⁰) A B ≡ Id (U ⁰) A′ B′ ^ [ % , ι ¹ ] / [Id]U ⊢Γ [A] [B]

[Id]U {A} {B} ⊢Γ (ne′ K D neK K≡K) [B] =
  let ⊢B = escape [B]
      B≡B = escapeEqRefl [B]
  in ne (ne (Id (U ⁰) K B) (univ:⇒*: (IdURed*Term (un-univ:⇒*: D) (un-univ ⊢B))) (IdUₙ neK) (~-IdU ⊢Γ K≡K (≅-un-univ B≡B)))

[Id]U ⊢Γ (ℕᵣ [[ ⊢A , ⊢K , D ]]) (ℕᵣ [[ ⊢B , ⊢L , D₁ ]]) =
  let
    ⊢tA = (subset* D)
  in proj₁ (redSubst* (trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A) (un-univ ⊢K) (un-univ⇒* D) (un-univ ⊢B)))
                        (trans⇒* (univ⇒* (IdUℕRed*Term′ (un-univ ⊢B) (un-univ ⊢L) (un-univ⇒* D₁))) (univ (Id-U-ℕℕ ⊢Γ) ⇨ id (univ (Unitⱼ ⊢Γ))))) (UnitType ⊢Γ))

[Id]U ⊢Γ (ℕᵣ D) (ne′ K D₁ neK K≡K) =
  let [B] = ne′ K D₁ neK K≡K
      ⊢B = escape {l = ι ¹} [B]
  in ne′ (Id (U ⁰) ℕ K) (univ:⇒*: (transTerm:⇒:* (IdURed*Term (un-univ:⇒*: D) (un-univ ⊢B) ) (IdUℕRed*Term (un-univ:⇒*: D₁))))
         (IdUℕₙ neK) (~-IdUℕ ⊢Γ K≡K)

[Id]U ⊢Γ (ℕᵣ [[ ⊢A , ⊢ℕ , D ]]) (Πᵣ′ rF lF lG (≡is≤ PE.refl) (≡is≤ PE.refl) F G D′ ⊢F ⊢G A≡A [F] [G] G-ext) =
  let [B] = Πᵣ′ rF lF lG (≡is≤ PE.refl) (≡is≤ PE.refl) F G D′ ⊢F ⊢G A≡A [F] [G] G-ext
      ⊢B = escape [B]
  in proj₁ (redSubst* (trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A) (un-univ ⊢ℕ) (un-univ⇒* D) (un-univ ⊢B)))
                        (trans⇒* (univ⇒* (IdUℕRed*Term′ (un-univ ⊢B) (Πⱼ ≡is≤ PE.refl ▹ ≡is≤ PE.refl ▹ (un-univ ⊢F) ▹ (un-univ ⊢G)) (un-univ⇒* (red D′))))
                                 (univ (Id-U-ℕΠ (un-univ ⊢F) (un-univ ⊢G)) ⇨ id (univ (Emptyⱼ ⊢Γ))))) (EmptyType ⊢Γ))

[Id]U ⊢Γ (Πᵣ′ rF lF lG (≡is≤ PE.refl) (≡is≤ PE.refl) F G [[ ⊢A , ⊢Π , D ]] ⊢F ⊢G A≡A [F] [G] G-ext) (ℕᵣ [[ ⊢B , ⊢ℕ , D' ]]) =
  proj₁ (redSubst* (trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A) (un-univ ⊢Π) (un-univ⇒* D) (un-univ ⊢B)))
                        (trans⇒* (univ⇒* (IdUΠRed*Term′ (un-univ ⊢F) (un-univ ⊢G) (un-univ ⊢B) (un-univ ⊢ℕ) (un-univ⇒* D')))
                                 (univ (Id-U-Πℕ (un-univ ⊢F) (un-univ ⊢G)) ⇨ id (univ (Emptyⱼ ⊢Γ))))) (EmptyType ⊢Γ))

[Id]U ⊢Γ (Πᵣ′ rF lF lG (≡is≤ PE.refl) (≡is≤ PE.refl) F G D ⊢F ⊢G A≡A [F] [G] G-ext) (ne′ K D₁ neK K≡K) =
  let [B] = ne′ K D₁ neK K≡K
      ⊢B = escape {l = ι ¹} [B]
      -- [F]' = PE.subst (λ X → _  ⊩⟨ _ ⟩ X ^ [ _ , _ ]) (wk-id F) ([F] Twk.id ⊢Γ)
      -- ⊢F≅F = ≅-un-univ (escapeEqRefl [F]')
      -- [G]' = PE.subst (λ X → _  ⊩⟨ _ ⟩ X ^ [ _ , _ ]) (wkSingleSubstId G) ([G] {a = var 0} (Twk.step Twk.id) (⊢Γ ∙ ⊢F) {!!})
      -- ⊢G≅G = ≅-un-univ (escapeEqRefl [G]')
  in ne′ (Id (U ⁰) (Π F ^ rF ° lF ▹ G ° lG ° ⁰) K)
         (univ:⇒*: (transTerm:⇒:* (IdURed*Term (un-univ:⇒*: D) (un-univ ⊢B) ) (IdUΠRed*Term (un-univ ⊢F) (un-univ ⊢G) (un-univ:⇒*: D₁))))
         (IdUΠₙ neK) (~-IdUΠ (≅-un-univ A≡A) K≡K)

[Id]U {A} {B} {Γ} ⊢Γ (Πᵣ′ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F G [[ ⊢A , ⊢ΠFG , D ]] ⊢F ⊢G A≡A [F] [G] G-ext)
  (Πᵣ′ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢B , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ B≡B [F₁] [G₁] G₁-ext) =
  ∃ᵣ′ (Id (U ⁰) F F₁) (IdGG₁ (step id) (var 0))
    [[ (univ (Idⱼ (univ 0<1 ⊢Γ) (un-univ ⊢A) (un-univ ⊢B))) , ⊢∃ , D∃ ]]
    ⊢IdFF₁ ⊢IdGG₁ ∃≡∃ [IdFF₁]
    (λ {ρ} {Δ} {a} [ρ] ⊢Δ [a] → PE.subst (λ X → Δ ⊩⟨ ι ¹ ⟩ X ^ [ % , ι ¹ ]) (PE.sym (wksubst-IdTel ρ a)) ([IdGG₁] [ρ] ⊢Δ [a]))
    (λ {ρ} {Δ} {a} {b} [ρ] ⊢Δ [a] [b] [a≡b] → irrelevanceEq″ (PE.sym (wksubst-IdTel ρ a)) (PE.sym (wksubst-IdTel ρ b)) PE.refl PE.refl
      ([IdGG₁] [ρ] ⊢Δ [a]) (PE.subst (λ X → Δ ⊩⟨ ι ¹ ⟩ X ^ [ % , ι ¹ ]) (PE.sym (wksubst-IdTel ρ a)) ([IdGG₁] [ρ] ⊢Δ [a]))
      ([IdGG₁-ext] [ρ] ⊢Δ [a] [b] [a≡b]))
  where
    open IdTypeU-lemmas ⊢Γ ⊢A ⊢ΠFG D ⊢F ⊢G A≡A [F] [G] G-ext ⊢B ⊢ΠF₁G₁ D₁ ⊢F₁ ⊢G₁ B≡B [F₁] [G₁] G₁-ext
      (λ [ρ] ⊢Δ → [Id]U ⊢Δ ([F] [ρ] ⊢Δ) ([F₁] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x] [y] → [Id]U ⊢Δ ([G] [ρ] ⊢Δ [x]) ([G₁] [ρ] ⊢Δ [y]))
      (λ [ρ] ⊢Δ [x] [x′] [x≡x′] [y] [y′] [y≡y′] →
        [IdExt]U ⊢Δ ([G] [ρ] ⊢Δ [x]) ([G] [ρ] ⊢Δ [x′]) (G-ext [ρ] ⊢Δ [x] [x′] [x≡x′]) ([G₁] [ρ] ⊢Δ [y]) ([G₁] [ρ] ⊢Δ [y′]) (G₁-ext [ρ] ⊢Δ [y] [y′] [y≡y′]))

[Id]U {A} {B} {Γ} ⊢Γ (Πᵣ′ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F G [[ ⊢A , ⊢ΠFG , D ]] ⊢F ⊢G A≡A [F] [G] G-ext)
  (Πᵣ′ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢B , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ B≡B [F₁] [G₁] G₁-ext) =
  ∃ᵣ′ (Id (SProp ⁰) F F₁) (IdGG₁ (step id) (var 0))
    [[ (univ (Idⱼ (univ 0<1 ⊢Γ) (un-univ ⊢A) (un-univ ⊢B))) , ⊢∃ , D∃ ]]
    ⊢IdFF₁ ⊢IdGG₁ ∃≡∃ [IdFF₁]
    (λ {ρ} {Δ} {a} [ρ] ⊢Δ [a] → PE.subst (λ X → Δ ⊩⟨ ι ¹ ⟩ X ^ [ % , ι ¹ ]) (PE.sym (wksubst-IdTel ρ a)) ([IdGG₁] [ρ] ⊢Δ [a]))
    (λ {ρ} {Δ} {a} {b} [ρ] ⊢Δ [a] [b] [a≡b] → irrelevanceEq″ (PE.sym (wksubst-IdTel ρ a)) (PE.sym (wksubst-IdTel ρ b)) PE.refl PE.refl
      ([IdGG₁] [ρ] ⊢Δ [a]) (PE.subst (λ X → Δ ⊩⟨ ι ¹ ⟩ X ^ [ % , ι ¹ ]) (PE.sym (wksubst-IdTel ρ a)) ([IdGG₁] [ρ] ⊢Δ [a]))
      ([IdGG₁-ext] [ρ] ⊢Δ [a] [b] [a≡b]))
  where
    open IdTypeU-lemmas ⊢Γ ⊢A ⊢ΠFG D ⊢F ⊢G A≡A [F] [G] G-ext ⊢B ⊢ΠF₁G₁ D₁ ⊢F₁ ⊢G₁ B≡B [F₁] [G₁] G₁-ext
      (λ [ρ] ⊢Δ → [Id]SProp ⊢Δ ([F] [ρ] ⊢Δ) ([F₁] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x] [y] → [Id]U ⊢Δ ([G] [ρ] ⊢Δ [x]) ([G₁] [ρ] ⊢Δ [y]))
      (λ [ρ] ⊢Δ [x] [x′] [x≡x′] [y] [y′] [y≡y′] →
        [IdExt]U ⊢Δ ([G] [ρ] ⊢Δ [x]) ([G] [ρ] ⊢Δ [x′]) (G-ext [ρ] ⊢Δ [x] [x′] [x≡x′]) ([G₁] [ρ] ⊢Δ [y]) ([G₁] [ρ] ⊢Δ [y′]) (G₁-ext [ρ] ⊢Δ [y] [y′] [y≡y′]))

[Id]U ⊢Γ (Πᵣ′ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F G [[ ⊢A , ⊢ΠFG , D ]] ⊢F ⊢G A≡A [F] [G] G-ext)
         (Πᵣ′ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢B , ⊢ΠF₁G₁ , D' ]] ⊢F₁ ⊢G₁ B≡B [F₁] [G₁] G₁-ext) =
    proj₁ (redSubst* (trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A) (un-univ ⊢ΠFG) (un-univ⇒* D) (un-univ ⊢B)))
                        (trans⇒* (univ⇒* (IdUΠRed*Term′ (un-univ ⊢F) (un-univ ⊢G) (un-univ ⊢B) (un-univ ⊢ΠF₁G₁) (un-univ⇒* D')))
                                 (univ (Id-U-ΠΠ!% !≢% (un-univ ⊢F) (un-univ ⊢G) (un-univ ⊢F₁) (un-univ ⊢G₁)) ⇨ id (univ (Emptyⱼ ⊢Γ))))) (EmptyType ⊢Γ))

[Id]U ⊢Γ (Πᵣ′ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F G [[ ⊢A , ⊢ΠFG , D ]] ⊢F ⊢G A≡A [F] [G] G-ext)
  (Πᵣ′ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢B , ⊢ΠF₁G₁ , D' ]] ⊢F₁ ⊢G₁ B≡B [F₁] [G₁] G₁-ext) =
    proj₁ (redSubst* (trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A) (un-univ ⊢ΠFG) (un-univ⇒* D) (un-univ ⊢B)))
                       (trans⇒* (univ⇒* (IdUΠRed*Term′ (un-univ ⊢F) (un-univ ⊢G) (un-univ ⊢B) (un-univ ⊢ΠF₁G₁) (un-univ⇒* D')))
                                 (univ (Id-U-ΠΠ!% (λ e → !≢% (PE.sym e)) (un-univ ⊢F) (un-univ ⊢G) (un-univ ⊢F₁) (un-univ ⊢G₁)) ⇨ id (univ (Emptyⱼ ⊢Γ))))) (EmptyType ⊢Γ))

[IdExtShape]U ⊢Γ _ _ (ℕᵥ ℕA [[ ⊢A , ⊢K , D ]]) [A≡A′] _ _ (ℕᵥ NB [[ ⊢B , ⊢L , D₁ ]]) [B≡B′] =
  Π₌ (Empty _) (Empty _)
    (trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A) (un-univ ⊢K) (un-univ⇒* D) (un-univ ⊢B)))
                        (trans⇒* (univ⇒* (IdUℕRed*Term′ (un-univ ⊢B) (un-univ ⊢L) (un-univ⇒* D₁))) (univ (Id-U-ℕℕ ⊢Γ) ⇨ id (univ (Unitⱼ ⊢Γ)))))
                        (≅-univ (Unit≡Unit ⊢Γ))
    (λ [ρ] ⊢Δ → id (univ (Emptyⱼ ⊢Δ)))
    λ [ρ] ⊢Δ [a] → id (univ (Emptyⱼ ⊢Δ))

[IdExtShape]U ⊢Γ _ _ (ℕᵥ ℕA D) [A≡A′] _ _ (ne neB neB') (ne₌ M D′ neM K≡M) =
  let [[ ⊢B , _ ,  _ ]] = D′
  in ne₌ (Id (U ⁰) ℕ M) (univ:⇒*: (transTerm:⇒:* (IdURed*Term (un-univ:⇒*: D) (un-univ ⊢B) ) (IdUℕRed*Term (un-univ:⇒*: D′))))
         (IdUℕₙ neM) (~-IdUℕ ⊢Γ K≡M)

[IdExtShape]U ⊢Γ _ _ (ℕᵥ [[ ⊢A , ⊢ℕ , D ]] [[ ⊢A' , ⊢ℕ' , D' ]]) [A≡A′] _ _
                     (Πᵥ (Πᵣ rF lF lG (≡is≤ PE.refl) (≡is≤ PE.refl) F G D′ ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ rF' lF' lG' (≡is≤ PE.refl) (≡is≤ PE.refl) F' G' D′' ⊢F' ⊢G' A≡A' [F]' [G]' G-ext')) [B≡B′] =
  let [B]' = Πᵣ′ rF' lF' lG' (≡is≤ PE.refl) (≡is≤ PE.refl) F' G' D′' ⊢F' ⊢G' A≡A' [F]' [G]' G-ext'
      ⊢B' = escape [B]'
  in trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A') (un-univ ⊢ℕ') (un-univ⇒* D') (un-univ ⊢B')))
                        (trans⇒* (univ⇒* (IdUℕRed*Term′ (un-univ ⊢B') (Πⱼ ≡is≤ PE.refl ▹ ≡is≤ PE.refl ▹ (un-univ ⊢F') ▹ (un-univ ⊢G')) (un-univ⇒* (red D′'))))
                                 (univ (Id-U-ℕΠ (un-univ ⊢F') (un-univ ⊢G')) ⇨ id (univ (Emptyⱼ ⊢Γ))))

[IdExtShape]U {A} {A′} {B} {B′} ⊢Γ _ _ (ne neA neA') (ne₌ M D neM K≡M) [B] [B'] [ShapeB] [B≡B′] =
  let ⊢B' = escape [B']
      B≡B' = escapeEq [B] [B≡B′]
  in ne₌ (Id (U ⁰) M B′) (univ:⇒*: (IdURed*Term (un-univ:⇒*: D) (un-univ ⊢B')))
         (IdUₙ neM) (~-IdU ⊢Γ K≡M (≅-un-univ B≡B'))

[IdExtShape]U ⊢Γ _ _ (Πᵥ (Πᵣ rF lF lG (≡is≤ PE.refl) (≡is≤ PE.refl) F G D′ ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ rF' lF' lG' (≡is≤ PE.refl) (≡is≤ PE.refl) F' G' [[ ⊢A' , ⊢Π' , DΠ' ]] ⊢F' ⊢G' A≡A' [F]' [G]' G-ext')) [A≡A′] _ _
                     (ℕᵥ [[ ⊢A , ⊢ℕ , D ]] [[ ⊢B' , ⊢ℕ' , D' ]]) [B≡B′] =
   trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A') (un-univ ⊢Π') (un-univ⇒* DΠ') (un-univ ⊢B')))
                        (trans⇒* (univ⇒* (IdUΠRed*Term′ (un-univ ⊢F') (un-univ ⊢G') (un-univ ⊢B') (un-univ ⊢ℕ') (un-univ⇒* D')))
                                 (univ (Id-U-Πℕ (un-univ ⊢F') (un-univ ⊢G')) ⇨ id (univ (Emptyⱼ ⊢Γ))))

[IdExtShape]U ⊢Γ _ _ (Πᵥ (Πᵣ rF' .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F' G' De' ⊢F' ⊢G' A≡A' [F]' [G]' G-ext')
                         (Πᵣ rF .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F G D ⊢F ⊢G A≡A [F] [G] G-ext)) (Π₌ F′ G′ D′₁ A≡B [F≡F′] [G≡G′])
                 _ _ (ne neA neB) (ne₌ M D′ neM K≡M) =
  let [[ ⊢B , _ ,  _ ]] = D′
      Π≡Π = whrDet* (red D , Whnf.Πₙ) (D′₁ , Whnf.Πₙ)
      F≡F' , rF≡rF' , _ , G≡G' , _  = Π-PE-injectivity Π≡Π
  in ne₌ (Id (U ⁰) (Π F ^ rF ° ⁰ ▹ G ° ⁰ ° ⁰) M)
         (univ:⇒*: (transTerm:⇒:* (IdURed*Term (un-univ:⇒*: D) (un-univ ⊢B) ) (IdUΠRed*Term (un-univ ⊢F) (un-univ ⊢G) (un-univ:⇒*: D′))))
         (IdUΠₙ neM) (PE.subst (λ X → _  ⊢ Id (U ⁰) (Π F' ^ rF' ° ⁰ ▹ G' ° ⁰ ° ⁰) _ ~ Id (U ⁰) (Π F ^ X ° ⁰ ▹ G ° ⁰ ° ⁰) M ∷ SProp _ ^ _) (PE.sym rF≡rF')
                               (~-IdUΠ (≅-un-univ
                                 (PE.subst (λ X → _ ⊢ Π F' ^ rF' ° ⁰ ▹ G' ° ⁰ ° ⁰ ≅  Π F ^ rF' ° ⁰ ▹ X ° ⁰ ° ⁰ ^ _ ) (PE.sym G≡G')
                                   (PE.subst (λ X → _ ⊢ Π F' ^ rF' ° ⁰ ▹ G' ° ⁰ ° ⁰ ≅  Π X ^ rF' ° ⁰ ▹ G′ ° ⁰ ° ⁰ ^ _ ) (PE.sym F≡F') A≡B))) K≡M))

[IdExtShape]U ⊢Γ _ _ (Πᵥ (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                         (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢Π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 ΠFG′≡ΠFG′₁ = whrDet* (D₂ , Πₙ) (D′ , Πₙ)
      F′≡F′₁ , rF≡rF′ , _ , G′≡G′₁ , _  = Π-PE-injectivity ΠFG′≡ΠFG′₁
  in ⊥-elim (!≢% rF≡rF′)

[IdExtShape]U ⊢Γ _ _ (Πᵥ (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                         (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢Π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 ΠFG′≡ΠFG′₁ = whrDet* (D₂ , Πₙ) (D′ , Πₙ)
      F′≡F′₁ , rF≡rF′ , _ , G′≡G′₁ , _  = Π-PE-injectivity ΠFG′≡ΠFG′₁
  in ⊥-elim (!≢% (PE.sym rF≡rF′))

[IdExtShape]U ⊢Γ _ _ (Πᵥ _ _)
                      (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
                 _ _ (Πᵥ (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                         (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢ΠF₂G₂ , D₂ ]] ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext)) (Π₌ F′₁ G′₁ D′₁ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
  let ΠFG′≡ΠFG′₁ = whrDet* (D₂ , Πₙ) (D′₁ , Πₙ)
      F′≡F′₁ , rF≡rF′ , _ , G′≡G′₁ , _  = Π-PE-injectivity ΠFG′≡ΠFG′₁
  in ⊥-elim (!≢% rF≡rF′)

[IdExtShape]U ⊢Γ _ _ (Πᵥ _ _)
                      (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
                 _ _ (Πᵥ (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                         (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢ΠF₂G₂ , D₂ ]] ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext)) (Π₌ F′₁ G′₁ D′₁ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
  let ΠFG′≡ΠFG′₁ = whrDet* (D₂ , Πₙ) (D′₁ , Πₙ)
      F′≡F′₁ , rF≡rF′ , _ , G′≡G′₁ , _  = Π-PE-injectivity ΠFG′≡ΠFG′₁
  in ⊥-elim (!≢% (PE.sym rF≡rF′))

[IdExtShape]U ⊢Γ _ _ (Πᵥ (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                         (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢ΠF₂G₂ , D₂ ]] ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext))
                      (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
                 _ _ (Πᵥ (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₃ G₃ [[ ⊢A₃ , ⊢ΠF₃G₃ , D₃ ]] ⊢F₃ ⊢G₃ A₃≡A₃ [F₃] [G₃] G₃-ext)
                         (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₄ G₄ [[ ⊢A₄ , ⊢ΠF₄G₄ , D₄ ]] ⊢F₄ ⊢G₄ A₄≡A₄ [F₄] [G₄] G₄-ext)) (Π₌ F′₁ G′₁ D′₁ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
  trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A₂) (un-univ ⊢ΠF₂G₂) (un-univ⇒* D₂) (un-univ ⊢A₄)))
                        (trans⇒* (univ⇒* (IdUΠRed*Term′ (un-univ ⊢F₂) (un-univ ⊢G₂) (un-univ ⊢A₄) (un-univ ⊢ΠF₄G₄) (un-univ⇒* D₄)))
                                 (univ (Id-U-ΠΠ!% !≢% (un-univ ⊢F₂) (un-univ ⊢G₂) (un-univ ⊢F₄) (un-univ ⊢G₄)) ⇨ id (univ (Emptyⱼ ⊢Γ))))

[IdExtShape]U ⊢Γ _ _ (Πᵥ (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                         (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢ΠF₂G₂ , D₂ ]] ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext))
                      (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
                 _ _ (Πᵥ (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₃ G₃ [[ ⊢A₃ , ⊢ΠF₃G₃ , D₃ ]] ⊢F₃ ⊢G₃ A₃≡A₃ [F₃] [G₃] G₃-ext)
                         (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₄ G₄ [[ ⊢A₄ , ⊢ΠF₄G₄ , D₄ ]] ⊢F₄ ⊢G₄ A₄≡A₄ [F₄] [G₄] G₄-ext)) (Π₌ F′₁ G′₁ D′₁ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
  trans⇒* (univ⇒* (IdURed*Term′ (un-univ ⊢A₂) (un-univ ⊢ΠF₂G₂) (un-univ⇒* D₂) (un-univ ⊢A₄)))
                        (trans⇒* (univ⇒* (IdUΠRed*Term′ (un-univ ⊢F₂) (un-univ ⊢G₂) (un-univ ⊢A₄) (un-univ ⊢ΠF₄G₄) (un-univ⇒* D₄)))
                                 (univ (Id-U-ΠΠ!% (λ e → !≢% (PE.sym e)) (un-univ ⊢F₂) (un-univ ⊢G₂) (un-univ ⊢F₄) (un-univ ⊢G₄)) ⇨ id (univ (Emptyⱼ ⊢Γ))))

[IdExtShape]U {A₁} {A₂} {A₃} {A₄} {Γ}  ⊢Γ _ _
                   (Πᵥ (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                   (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢ΠF₂G₂ , D₂ ]] ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext))
                   (Π₌ F₂′ G₂′ D₂′ A₁≡A₂′ [F₁≡F₂′] [G₁≡G₂′])
               _ _ (Πᵥ (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₃ G₃ [[ ⊢A₃ , ⊢ΠF₃G₃ , D₃ ]] ⊢F₃ ⊢G₃ A₃≡A₃ [F₃] [G₃] G₃-ext)
                   (Πᵣ ! .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₄ G₄ [[ ⊢A₄ , ⊢ΠF₄G₄ , D₄ ]] ⊢F₄ ⊢G₄ A₄≡A₄ [F₄] [G₄] G₄-ext))
                   (Π₌ F₄′ G₄′ D₄′ A₃≡A₄′ [F₃≡F₄′] [G₃≡G₄′]) =
    ∃₌ (Id (U ⁰) F₂ F₄) (E₂.IdGG₁ (step id) (var 0))
    E₂.D∃ ∃₁≡∃₂
    [IdFF₁≡IdFF₂]
    (λ {ρ} {Δ} {e} [ρ] ⊢Δ [e] → irrelevanceEq″ (PE.sym (E₁.wksubst-IdTel ρ e)) (PE.sym (E₂.wksubst-IdTel ρ e)) PE.refl PE.refl
      (E₁.[IdGG₁] [ρ] ⊢Δ [e]) (PE.subst (λ X → Δ ⊩⟨ ι ¹ ⟩ X ^ [ % , ι ¹ ]) (PE.sym (E₁.wksubst-IdTel ρ e)) (E₁.[IdGG₁] [ρ] ⊢Δ [e]))
      ([IdGG₁≡IdGG₂] [ρ] ⊢Δ [e]))
  where
    Π≡Π = whrDet* (D₂ , Whnf.Πₙ) (D₂′ , Whnf.Πₙ)
    F₂≡F₂′ = let x , _ , _ , _ , _ = Π-PE-injectivity Π≡Π in x
    G₂≡G₂′ = let _ , _ , _ , x , _ = Π-PE-injectivity Π≡Π in x
    Π≡Π′ = whrDet* (D₄ , Whnf.Πₙ) (D₄′ , Whnf.Πₙ)
    F₄≡F₄′ = let x , _ , _ , _ , _ = Π-PE-injectivity Π≡Π′ in x
    G₄≡G₄′ = let _ , _ , _ , x , _ = Π-PE-injectivity Π≡Π′ in x

    A₁≡A₂ = PE.subst₂ (λ X Y → Γ ⊢ Π F₁ ^ ! ° ⁰ ▹ G₁ ° ⁰ ° ⁰ ≅ Π X ^ ! ° ⁰ ▹ Y ° ⁰ ° ⁰ ^ [ ! , ι ⁰ ]) (PE.sym F₂≡F₂′) (PE.sym G₂≡G₂′) A₁≡A₂′
    A₃≡A₄ = PE.subst₂ (λ X Y → Γ ⊢ Π F₃ ^ ! ° ⁰ ▹ G₃ ° ⁰ ° ⁰ ≅ Π X ^ ! ° ⁰ ▹ Y ° ⁰ ° ⁰ ^ [ ! , ι ⁰ ]) (PE.sym F₄≡F₄′) (PE.sym G₄≡G₄′) A₃≡A₄′
    [F₁≡F₂] = PE.subst (λ X → ∀ {ρ Δ} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Δ ⊩⟨ ι ⁰ ⟩ wk ρ F₁ ≡ wk ρ X ^ [ ! , ι ⁰ ] / [F₁] [ρ] ⊢Δ)
                       (PE.sym F₂≡F₂′) [F₁≡F₂′]
    [F₃≡F₄] = PE.subst (λ X → ∀ {ρ Δ} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Δ ⊩⟨ ι ⁰ ⟩ wk ρ F₃ ≡ wk ρ X ^ [ ! , ι ⁰ ] / [F₃] [ρ] ⊢Δ)
                       (PE.sym F₄≡F₄′) [F₃≡F₄′]
    [G₁≡G₂] = PE.subst (λ X → ∀ {ρ Δ a} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
                              → ([a] : Δ ⊩⟨ ι ⁰ ⟩ a ∷ wk ρ F₁ ^ [ ! , ι ⁰ ] / [F₁] [ρ] ⊢Δ)
                              → Δ ⊩⟨ ι ⁰ ⟩ wk (lift ρ) G₁ [ a ] ≡ wk (lift ρ) X [ a ] ^ [ ! , ι ⁰ ] / [G₁] [ρ] ⊢Δ [a])
                       (PE.sym G₂≡G₂′) [G₁≡G₂′]
    [G₃≡G₄] = PE.subst (λ X → ∀ {ρ Δ a} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
                              → ([a] : Δ ⊩⟨ ι ⁰ ⟩ a ∷ wk ρ F₃ ^ [ ! , ι ⁰ ] / [F₃] [ρ] ⊢Δ)
                              → Δ ⊩⟨ ι ⁰ ⟩ wk (lift ρ) G₃ [ a ] ≡ wk (lift ρ) X [ a ] ^ [ ! , ι ⁰ ] / [G₃] [ρ] ⊢Δ [a])
                       (PE.sym G₄≡G₄′) [G₃≡G₄′]

    open IdTypeU-lemmas-2 ⊢Γ ⊢A₁ ⊢ΠF₁G₁ D₁ ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext
      ⊢A₂ ⊢ΠF₂G₂ D₂ ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext
      ⊢A₃ ⊢ΠF₃G₃ D₃ ⊢F₃ ⊢G₃ A₃≡A₃ [F₃] [G₃] G₃-ext
      ⊢A₄ ⊢ΠF₄G₄ D₄ ⊢F₄ ⊢G₄ A₄≡A₄ [F₄] [G₄] G₄-ext
      A₁≡A₂ A₃≡A₄ [F₁≡F₂] [F₃≡F₄] [G₁≡G₂] [G₃≡G₄]
      (λ [ρ] ⊢Δ → [Id]U ⊢Δ ([F₁] [ρ] ⊢Δ) ([F₃] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x] [y] → [Id]U ⊢Δ ([G₁] [ρ] ⊢Δ [x]) ([G₃] [ρ] ⊢Δ [y]))
      (λ [ρ] ⊢Δ → [Id]U ⊢Δ ([F₂] [ρ] ⊢Δ) ([F₄] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x] [y] → [Id]U ⊢Δ ([G₂] [ρ] ⊢Δ [x]) ([G₄] [ρ] ⊢Δ [y]))
      (λ [ρ] ⊢Δ [x] [x′] [x≡x′] [y] [y′] [y≡y′] →
        [IdExt]U ⊢Δ ([G₁] [ρ] ⊢Δ [x]) ([G₁] [ρ] ⊢Δ [x′]) (G₁-ext [ρ] ⊢Δ [x] [x′] [x≡x′]) ([G₃] [ρ] ⊢Δ [y]) ([G₃] [ρ] ⊢Δ [y′]) (G₃-ext [ρ] ⊢Δ [y] [y′] [y≡y′]))
      (λ [ρ] ⊢Δ [x] [x′] [x≡x′] [y] [y′] [y≡y′] →
        [IdExt]U ⊢Δ ([G₂] [ρ] ⊢Δ [x]) ([G₂] [ρ] ⊢Δ [x′]) (G₂-ext [ρ] ⊢Δ [x] [x′] [x≡x′]) ([G₄] [ρ] ⊢Δ [y]) ([G₄] [ρ] ⊢Δ [y′]) (G₄-ext [ρ] ⊢Δ [y] [y′] [y≡y′]))
      (λ [ρ] ⊢Δ → [IdExt]U ⊢Δ ([F₁] [ρ] ⊢Δ) ([F₂] [ρ] ⊢Δ) ([F₁≡F₂] [ρ] ⊢Δ) ([F₃] [ρ] ⊢Δ) ([F₄] [ρ] ⊢Δ) ([F₃≡F₄] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x₁] [x₂] [G₁x₁≡G₂x₂] [x₃] [x₄] [G₃x₃≡G₄x₄] → [IdExt]U ⊢Δ ([G₁] [ρ] ⊢Δ [x₁]) ([G₂] [ρ] ⊢Δ [x₂]) [G₁x₁≡G₂x₂] ([G₃] [ρ] ⊢Δ [x₃]) ([G₄] [ρ] ⊢Δ [x₄]) [G₃x₃≡G₄x₄])

[IdExtShape]U {A₁} {A₂} {A₃} {A₄} {Γ}  ⊢Γ _ _
                   (Πᵥ (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₁ G₁ [[ ⊢A₁ , ⊢ΠF₁G₁ , D₁ ]] ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext)
                   (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₂ G₂ [[ ⊢A₂ , ⊢ΠF₂G₂ , D₂ ]] ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext))
                   (Π₌ F₂′ G₂′ D₂′ A₁≡A₂′ [F₁≡F₂′] [G₁≡G₂′])
               _ _ (Πᵥ (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₃ G₃ [[ ⊢A₃ , ⊢ΠF₃G₃ , D₃ ]] ⊢F₃ ⊢G₃ A₃≡A₃ [F₃] [G₃] G₃-ext)
                   (Πᵣ % .⁰ .⁰ (≡is≤ PE.refl) (≡is≤ PE.refl) F₄ G₄ [[ ⊢A₄ , ⊢ΠF₄G₄ , D₄ ]] ⊢F₄ ⊢G₄ A₄≡A₄ [F₄] [G₄] G₄-ext))
                   (Π₌ F₄′ G₄′ D₄′ A₃≡A₄′ [F₃≡F₄′] [G₃≡G₄′]) =
    ∃₌ (Id (SProp ⁰) F₂ F₄) (E₂.IdGG₁ (step id) (var 0))
    E₂.D∃ ∃₁≡∃₂
    [IdFF₁≡IdFF₂]
    (λ {ρ} {Δ} {e} [ρ] ⊢Δ [e] → irrelevanceEq″ (PE.sym (E₁.wksubst-IdTel ρ e)) (PE.sym (E₂.wksubst-IdTel ρ e)) PE.refl PE.refl
      (E₁.[IdGG₁] [ρ] ⊢Δ [e]) (PE.subst (λ X → Δ ⊩⟨ ι ¹ ⟩ X ^ [ % , ι ¹ ]) (PE.sym (E₁.wksubst-IdTel ρ e)) (E₁.[IdGG₁] [ρ] ⊢Δ [e]))
      ([IdGG₁≡IdGG₂] [ρ] ⊢Δ [e]))
  where
    Π≡Π = whrDet* (D₂ , Whnf.Πₙ) (D₂′ , Whnf.Πₙ)
    F₂≡F₂′ = let x , _ , _ , _ , _ = Π-PE-injectivity Π≡Π in x
    G₂≡G₂′ = let _ , _ , _ , x , _ = Π-PE-injectivity Π≡Π in x
    Π≡Π′ = whrDet* (D₄ , Whnf.Πₙ) (D₄′ , Whnf.Πₙ)
    F₄≡F₄′ = let x , _ , _ , _ , _ = Π-PE-injectivity Π≡Π′ in x
    G₄≡G₄′ = let _ , _ , _ , x , _ = Π-PE-injectivity Π≡Π′ in x

    A₁≡A₂ = PE.subst₂ (λ X Y → Γ ⊢ Π F₁ ^ % ° ⁰ ▹ G₁ ° ⁰ ° ⁰ ≅ Π X ^ % ° ⁰ ▹ Y ° ⁰ ° ⁰ ^ [ ! , ι ⁰ ]) (PE.sym F₂≡F₂′) (PE.sym G₂≡G₂′) A₁≡A₂′
    A₃≡A₄ = PE.subst₂ (λ X Y → Γ ⊢ Π F₃ ^ % ° ⁰ ▹ G₃ ° ⁰ ° ⁰ ≅ Π X ^ % ° ⁰ ▹ Y ° ⁰ ° ⁰ ^ [ ! , ι ⁰ ]) (PE.sym F₄≡F₄′) (PE.sym G₄≡G₄′) A₃≡A₄′
    [F₁≡F₂] = PE.subst (λ X → ∀ {ρ Δ} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Δ ⊩⟨ ι ⁰ ⟩ wk ρ F₁ ≡ wk ρ X ^ [ % , ι ⁰ ] / [F₁] [ρ] ⊢Δ)
                       (PE.sym F₂≡F₂′) [F₁≡F₂′]
    [F₃≡F₄] = PE.subst (λ X → ∀ {ρ Δ} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ) → Δ ⊩⟨ ι ⁰ ⟩ wk ρ F₃ ≡ wk ρ X ^ [ % , ι ⁰ ] / [F₃] [ρ] ⊢Δ)
                       (PE.sym F₄≡F₄′) [F₃≡F₄′]
    [G₁≡G₂] = PE.subst (λ X → ∀ {ρ Δ a} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
                              → ([a] : Δ ⊩⟨ ι ⁰ ⟩ a ∷ wk ρ F₁ ^ [ % , ι ⁰ ] / [F₁] [ρ] ⊢Δ)
                              → Δ ⊩⟨ ι ⁰ ⟩ wk (lift ρ) G₁ [ a ] ≡ wk (lift ρ) X [ a ] ^ [ ! , ι ⁰ ] / [G₁] [ρ] ⊢Δ [a])
                       (PE.sym G₂≡G₂′) [G₁≡G₂′]
    [G₃≡G₄] = PE.subst (λ X → ∀ {ρ Δ a} → ([ρ] : ρ Twk.∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
                              → ([a] : Δ ⊩⟨ ι ⁰ ⟩ a ∷ wk ρ F₃ ^ [ % , ι ⁰ ] / [F₃] [ρ] ⊢Δ)
                              → Δ ⊩⟨ ι ⁰ ⟩ wk (lift ρ) G₃ [ a ] ≡ wk (lift ρ) X [ a ] ^ [ ! , ι ⁰ ] / [G₃] [ρ] ⊢Δ [a])
                       (PE.sym G₄≡G₄′) [G₃≡G₄′]

    open IdTypeU-lemmas-2 ⊢Γ ⊢A₁ ⊢ΠF₁G₁ D₁ ⊢F₁ ⊢G₁ A₁≡A₁ [F₁] [G₁] G₁-ext
      ⊢A₂ ⊢ΠF₂G₂ D₂ ⊢F₂ ⊢G₂ A₂≡A₂ [F₂] [G₂] G₂-ext
      ⊢A₃ ⊢ΠF₃G₃ D₃ ⊢F₃ ⊢G₃ A₃≡A₃ [F₃] [G₃] G₃-ext
      ⊢A₄ ⊢ΠF₄G₄ D₄ ⊢F₄ ⊢G₄ A₄≡A₄ [F₄] [G₄] G₄-ext
      A₁≡A₂ A₃≡A₄ [F₁≡F₂] [F₃≡F₄] [G₁≡G₂] [G₃≡G₄]
      (λ [ρ] ⊢Δ → [Id]SProp ⊢Δ ([F₁] [ρ] ⊢Δ) ([F₃] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x] [y] → [Id]U ⊢Δ ([G₁] [ρ] ⊢Δ [x]) ([G₃] [ρ] ⊢Δ [y]))
      (λ [ρ] ⊢Δ → [Id]SProp ⊢Δ ([F₂] [ρ] ⊢Δ) ([F₄] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x] [y] → [Id]U ⊢Δ ([G₂] [ρ] ⊢Δ [x]) ([G₄] [ρ] ⊢Δ [y]))
      (λ [ρ] ⊢Δ [x] [x′] [x≡x′] [y] [y′] [y≡y′] →
        [IdExt]U ⊢Δ ([G₁] [ρ] ⊢Δ [x]) ([G₁] [ρ] ⊢Δ [x′]) (G₁-ext [ρ] ⊢Δ [x] [x′] [x≡x′]) ([G₃] [ρ] ⊢Δ [y]) ([G₃] [ρ] ⊢Δ [y′]) (G₃-ext [ρ] ⊢Δ [y] [y′] [y≡y′]))
      (λ [ρ] ⊢Δ [x] [x′] [x≡x′] [y] [y′] [y≡y′] →
        [IdExt]U ⊢Δ ([G₂] [ρ] ⊢Δ [x]) ([G₂] [ρ] ⊢Δ [x′]) (G₂-ext [ρ] ⊢Δ [x] [x′] [x≡x′]) ([G₄] [ρ] ⊢Δ [y]) ([G₄] [ρ] ⊢Δ [y′]) (G₄-ext [ρ] ⊢Δ [y] [y′] [y≡y′]))
      (λ [ρ] ⊢Δ → [IdExt]SProp ⊢Δ ([F₁] [ρ] ⊢Δ) ([F₂] [ρ] ⊢Δ) ([F₁≡F₂] [ρ] ⊢Δ) ([F₃] [ρ] ⊢Δ) ([F₄] [ρ] ⊢Δ) ([F₃≡F₄] [ρ] ⊢Δ))
      (λ [ρ] ⊢Δ [x₁] [x₂] [G₁x₁≡G₂x₂] [x₃] [x₄] [G₃x₃≡G₄x₄] → [IdExt]U ⊢Δ ([G₁] [ρ] ⊢Δ [x₁]) ([G₂] [ρ] ⊢Δ [x₂]) [G₁x₁≡G₂x₂] ([G₃] [ρ] ⊢Δ [x₃]) ([G₄] [ρ] ⊢Δ [x₄]) [G₃x₃≡G₄x₄])

[IdExt]U ⊢Γ [A] [A′] [A≡A′] [B] [B′] [B≡B′] = [IdExtShape]U ⊢Γ [A] [A′] (goodCases [A] [A′] [A≡A′]) [A≡A′] [B] [B′] (goodCases [B] [B′] [B≡B′]) [B≡B′]

Ugen' : ∀ {Γ rU l} → (⊢Γ : ⊢ Γ) →  ((next l) LogRel.⊩¹U logRelRec (next l) ^ Γ) (Univ rU l) (next l)
Ugen' {Γ} {rU} {⁰} ⊢Γ = Uᵣ rU ⁰ emb< PE.refl ((idRed:*: (Ugenⱼ ⊢Γ)))
Ugen' {Γ} {rU} {¹} ⊢Γ = Uᵣ rU ¹ ∞< PE.refl (idRed:*: (Uⱼ ⊢Γ))

[Id]UGen : ∀ {A t u Γ l}
         (⊢Γ : ⊢ Γ)
         ([A] : (l LogRel.⊩¹U logRelRec l ^ Γ) A (ι ¹))
         ([t] : Γ ⊩⟨ l ⟩ t ∷ A ^ [ ! , ι ¹ ] / Uᵣ [A])
         ([u] : Γ ⊩⟨ l ⟩ u ∷ A ^ [ ! , ι ¹ ] / Uᵣ [A])
       → Γ ⊩⟨ l ⟩ Id A t u ^ [ % , ι ¹ ]
[Id]UGen {A} {t} {u} {Γ} {ι .¹} ⊢Γ (Uᵣ ! ⁰ emb< PE.refl [[ ⊢A , ⊢B , D ]]) (Uₜ K d typeK K≡K [t]) (Uₜ M d₁ typeM M≡M [u]) =
  let
    [t0] : Γ ⊩⟨ ι ⁰ ⟩ t ^ [ ! , ι ⁰ ]
    [t0] = PE.subst (λ X → Γ ⊩⟨ ι ⁰ ⟩ X ^ [ ! , ι ⁰ ]) (wk-id t) ([t] Twk.id ⊢Γ)
    [u0] = PE.subst (λ X → Γ ⊩⟨ ι ⁰ ⟩ X ^ [ ! , ι ⁰ ]) (wk-id u) ([u] Twk.id ⊢Γ)
    ⊢tA = conv (un-univ (escape [t0])) (sym (subset* D))
    ⊢uA = conv (un-univ (escape [u0])) (sym (subset* D))
  in proj₁ (redSubst* (IdRed* ⊢tA ⊢uA D) ([Id]U ⊢Γ [t0] [u0]))

[Id]UGen {A} {t} {u} {Γ} {ι .¹} ⊢Γ (Uᵣ % ⁰ emb< PE.refl [[ ⊢A , ⊢B , D ]]) (Uₜ K d typeK K≡K [t]) (Uₜ M d₁ typeM M≡M [u]) =
  let
    [t0] : Γ ⊩⟨ ι ⁰ ⟩ t ^ [ % , ι ⁰ ]
    [t0] = PE.subst (λ X → Γ ⊩⟨ ι ⁰ ⟩ X ^ [ % , ι ⁰ ]) (wk-id t) ([t] Twk.id ⊢Γ)
    [u0] = PE.subst (λ X → Γ ⊩⟨ ι ⁰ ⟩ X ^ [ % , ι ⁰ ]) (wk-id u) ([u] Twk.id ⊢Γ)
    ⊢tA = conv (un-univ (escape [t0])) (sym (subset* D))
    ⊢uA = conv (un-univ (escape [u0])) (sym (subset* D))
  in proj₁ (redSubst* (IdRed* ⊢tA ⊢uA D) ([Id]SProp ⊢Γ [t0] [u0]))

[IdExt]UGen : ∀ {A B t v u w Γ l l'}
         (⊢Γ : ⊢ Γ)
         ([A] : (l LogRel.⊩¹U logRelRec l ^ Γ) A (ι ¹))
         ([B] : (l' LogRel.⊩¹U logRelRec l' ^ Γ) B (ι ¹))
         ([A≡B] : Γ ⊩⟨ l ⟩ A ≡ B ^ [ ! , ι ¹ ] / Uᵣ [A])
         ([t] : Γ ⊩⟨ l ⟩ t ∷ A ^ [ ! , ι ¹ ] / Uᵣ [A])
         ([v] : Γ ⊩⟨ l' ⟩ v ∷ B ^ [ ! , ι ¹ ] / Uᵣ [B])
         ([t≡v] : Γ ⊩⟨ l ⟩ t ≡ v ∷ A ^ [ ! , ι ¹ ] / Uᵣ [A])
         ([u] : Γ ⊩⟨ l ⟩ u ∷ A ^ [ ! , ι ¹ ] / Uᵣ [A])
         ([w] : Γ ⊩⟨ l' ⟩ w ∷ B ^ [ ! , ι ¹ ] / Uᵣ [B])
         ([u≡w] : Γ ⊩⟨ l ⟩ u ≡ w ∷ A ^ [ ! , ι ¹ ] / Uᵣ [A])
       → Γ ⊩⟨ l ⟩ Id A t u ≡ Id B v w ^ [ % , ι ¹ ] / [Id]UGen ⊢Γ [A] [t] [u]
[IdExt]UGen {A} {B} {t} {v} {u} {w} {Γ} {.(ι ¹)} {.(ι ¹)} ⊢Γ (Uᵣ ! ⁰ emb< PE.refl d) (Uᵣ r₁ ⁰ emb< PE.refl d₁) [A≡B] [t] [v] [t≡v] [u] [w] [u≡w] =
  let U≡U = whrDet* (red d₁ , Uₙ) ([A≡B] , Uₙ)
      r≡r , _ = Univ-PE-injectivity U≡U
      [UA] = Uᵣ ! ⁰ emb< PE.refl d
      [UB] = Uᵣ r₁ ⁰ emb< PE.refl d₁
      [U] = Ugen' ⊢Γ
      [U]' = Ugen' ⊢Γ
      [UA]' , [UAeq] = redSubst* (red d) (Uᵣ [U])
      [UB]' , [UBeq] = redSubst* (PE.subst (λ X → Γ ⊢ _ ⇒* Univ X _ ^ _) r≡r (red d₁)) (Uᵣ [U]')
      [t]′ = convTerm₁ {t = t} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceTerm (Uᵣ [UA]) [UA]' [t])
      [t^] = univEq (Uᵣ [U]) [t]′
      [v]′ = convTerm₁ {t = v} [UB]' (Uᵣ [U]) [UBeq] (irrelevanceTerm (Uᵣ [UB]) [UB]' [v])
      [v^] = univEq (Uᵣ [U]) [v]′
      [t≡v]′ = convEqTerm₁ {t = t} {u = v} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceEqTerm (Uᵣ [UA]) [UA]' [t≡v])
      [t≡v^] = univEqEq (Uᵣ [U]) [t^] [t≡v]′
      [u]′ = convTerm₁ {t = u} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceTerm (Uᵣ [UA]) [UA]' [u])
      [u^] = univEq (Uᵣ [U]) [u]′
      [w]′ = convTerm₁ {t = w} [UB]' (Uᵣ [U]) [UBeq] (irrelevanceTerm (Uᵣ [UB]) [UB]'  [w])
      [w^] = univEq (Uᵣ [U]) [w]′
      [u≡w]′ = convEqTerm₁ {t = u} {u = w} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceEqTerm (Uᵣ [UA]) [UA]' [u≡w])
      [u≡w^] = univEqEq (Uᵣ [U]) [u^] [u≡w]′
      X = irrelevanceEq {A = Id (U ⁰) t u} {B = Id (U ⁰) v w} ([Id]U ⊢Γ [t^] [u^]) ([Id]UGen ⊢Γ [U] [t]′ [u]′) ([IdExt]U ⊢Γ [t^] [v^] [t≡v^] [u^] [w^] [u≡w^])
      [IdA] , [IdA≡U] = redSubst* (IdRed* (escapeTerm (Uᵣ [UA]) [t]) (escapeTerm (Uᵣ [UA]) [u]) (red d)) ([Id]UGen ⊢Γ [U] [t]′ [u]′)
      [IdB] , [IdB≡U] = redSubst* (IdRed* (escapeTerm (Uᵣ [UB]) [v]) (escapeTerm (Uᵣ [UB]) [w]) (PE.subst (λ X → Γ ⊢ _ ⇒* Univ X _ ^ _) r≡r (red d₁))) ([Id]UGen ⊢Γ [U] [v]′ [w]′)
      [IdA≡U]′ = irrelevanceEq {A = Id A t u} {B = Id (U ⁰) t u} [IdA] ([Id]UGen ⊢Γ [UA] [t] [u]) [IdA≡U]
      [IdB≡U]′ = irrelevanceEq {A = Id B v w} {B = Id (U ⁰) v w} [IdB] ([Id]UGen ⊢Γ [UB] [v] [w]) [IdB≡U]
  in transEq {A = Id A t u} {B = Id (U _) t u} {C = Id B v w}  ([Id]UGen ⊢Γ [UA] [t] [u]) ([Id]UGen ⊢Γ [U] [t]′ [u]′) ([Id]UGen ⊢Γ [UB] [v] [w])
                  [IdA≡U]′
                  (transEq {A = Id (U _) t u} {B = Id (U _) v w} {C = Id B v w} ([Id]UGen ⊢Γ [U] [t]′ [u]′) ([Id]UGen ⊢Γ [U] [v]′ [w]′) ([Id]UGen ⊢Γ [UB] [v] [w])
                                   X (symEq {A = Id B v w} {B = Id (U _) v w} ([Id]UGen ⊢Γ [UB] [v] [w]) ([Id]UGen ⊢Γ [U] [v]′ [w]′) [IdB≡U]′))

[IdExt]UGen {A} {B} {t} {v} {u} {w} {Γ} {.(ι ¹)} {.(ι ¹)} ⊢Γ (Uᵣ % ⁰ emb< PE.refl d) (Uᵣ r₁ ⁰ emb< PE.refl d₁) [A≡B] [t] [v] [t≡v] [u] [w] [u≡w] =
  let U≡U = whrDet* (red d₁ , Uₙ) ([A≡B] , Uₙ)
      r≡r , _ = Univ-PE-injectivity U≡U
      [UA] = Uᵣ % ⁰ emb< PE.refl d
      [UB] = Uᵣ r₁ ⁰ emb< PE.refl d₁
      [U] = Ugen' ⊢Γ
      [U]' = Ugen' ⊢Γ
      [UA]' , [UAeq] = redSubst* (red d) (Uᵣ [U])
      [UB]' , [UBeq] = redSubst* (PE.subst (λ X → Γ ⊢ _ ⇒* Univ X _ ^ _) r≡r (red d₁)) (Uᵣ [U]')
      [t]′ = convTerm₁ {t = t} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceTerm (Uᵣ [UA]) [UA]' [t])
      [t^] = univEq (Uᵣ [U]) [t]′
      [v]′ = convTerm₁ {t = v} [UB]' (Uᵣ [U]) [UBeq] (irrelevanceTerm (Uᵣ [UB]) [UB]' [v])
      [v^] = univEq (Uᵣ [U]) [v]′
      [t≡v]′ = convEqTerm₁ {t = t} {u = v} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceEqTerm (Uᵣ [UA]) [UA]' [t≡v])
      [t≡v^] = univEqEq (Uᵣ [U]) [t^] [t≡v]′
      [u]′ = convTerm₁ {t = u} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceTerm (Uᵣ [UA]) [UA]' [u])
      [u^] = univEq (Uᵣ [U]) [u]′
      [w]′ = convTerm₁ {t = w} [UB]' (Uᵣ [U]) [UBeq] (irrelevanceTerm (Uᵣ [UB]) [UB]'  [w])
      [w^] = univEq (Uᵣ [U]) [w]′
      [u≡w]′ = convEqTerm₁ {t = u} {u = w} [UA]' (Uᵣ [U]) [UAeq] (irrelevanceEqTerm (Uᵣ [UA]) [UA]' [u≡w])
      [u≡w^] = univEqEq (Uᵣ [U]) [u^] [u≡w]′
      X = irrelevanceEq {A = Id (SProp ⁰) t u} {B = Id (SProp ⁰) v w} ([Id]SProp ⊢Γ [t^] [u^]) ([Id]UGen ⊢Γ [U] [t]′ [u]′) ([IdExt]SProp ⊢Γ [t^] [v^] [t≡v^] [u^] [w^] [u≡w^])
      [IdA] , [IdA≡U] = redSubst* (IdRed* (escapeTerm (Uᵣ [UA]) [t]) (escapeTerm (Uᵣ [UA]) [u]) (red d)) ([Id]UGen ⊢Γ [U] [t]′ [u]′)
      [IdB] , [IdB≡U] = redSubst* (IdRed* (escapeTerm (Uᵣ [UB]) [v]) (escapeTerm (Uᵣ [UB]) [w]) (PE.subst (λ X → Γ ⊢ _ ⇒* Univ X _ ^ _) r≡r (red d₁))) ([Id]UGen ⊢Γ [U] [v]′ [w]′)
      [IdA≡U]′ = irrelevanceEq {A = Id A t u} {B = Id (SProp ⁰) t u} [IdA] ([Id]UGen ⊢Γ [UA] [t] [u]) [IdA≡U]
      [IdB≡U]′ = irrelevanceEq {A = Id B v w} {B = Id (SProp ⁰) v w} [IdB] ([Id]UGen ⊢Γ [UB] [v] [w]) [IdB≡U]
  in transEq {A = Id A t u} {B = Id (SProp _) t u} {C = Id B v w}  ([Id]UGen ⊢Γ [UA] [t] [u]) ([Id]UGen ⊢Γ [U] [t]′ [u]′) ([Id]UGen ⊢Γ [UB] [v] [w])
                  [IdA≡U]′
                  (transEq {A = Id (SProp _) t u} {B = Id (SProp _) v w} {C = Id B v w} ([Id]UGen ⊢Γ [U] [t]′ [u]′) ([Id]UGen ⊢Γ [U] [v]′ [w]′) ([Id]UGen ⊢Γ [UB] [v] [w])
                                   X (symEq {A = Id B v w} {B = Id (SProp _) v w} ([Id]UGen ⊢Γ [UB] [v] [w]) ([Id]UGen ⊢Γ [U] [v]′ [w]′) [IdB≡U]′))


------------------------------------------------------------------------
-- An alternative backend
------------------------------------------------------------------------

-- Acknowledgements:
--
-- • The use of parsing "processes" is based on implementation C from
--   Koen Claessen's paper Parallel parsing processes.
--
-- • The idea to use toProc is due to Jean-Philippe Bernardy.

-- It is not obvious how to adapt this backend so that it can handle
-- parsers which are simultaneously left and right recursive, like
-- TotalRecognisers.LeftRecursion.leftRight.

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

module TotalRecognisers.Simple.AlternativeBackend
         (Tok : Set)
         (_≟_ : Decidable (_≡_ {A = Tok}))
         -- The tokens must come with decidable equality.
         where

open import Codata.Musical.Notation
open import Data.Bool hiding (_≟_)
open import Data.List
open import Relation.Nullary.Decidable

import TotalRecognisers.Simple as S
open S Tok _≟_ hiding (_∈?_)

------------------------------------------------------------------------
-- Parsing "processes"

data Proc : Set where
  tokenBind : (p : Tok → ∞ Proc) → Proc
  emptyOr   : (p : Proc) → Proc
  fail      : Proc

-- The semantics of Proc is given by run: run p s is true iff s is a
-- member of the language defined by p.

run : Proc → List Tok → Bool
run (tokenBind p) (c ∷ s) = run (♭ (p c)) s
run (emptyOr p)   s       = null s ∨ run p s
run _             _       = false

------------------------------------------------------------------------
-- Parsers can be turned into processes

-- Here I use my technique for "beating the productivity checker".

-- Process "programs".

infixl 5 _∣_

data ProcP : Set where
  -- Process primitives.
  tokenBind : (p : Tok → ∞ ProcP) → ProcP
  emptyOr   : (p : ProcP) → ProcP
  fail      : ProcP

  -- Symmetric choice.
  _∣_     : (p₁ p₂ : ProcP) → ProcP

  -- Embedding of parsers.
  toProc  : ∀ {n} (p : P n) (κ : ProcP) → ProcP

-- WHNFs.

data ProcW : Set where
  tokenBind : (p : Tok → ∞ ProcP) → ProcW
  emptyOr   : (p : ProcW) → ProcW
  fail      : ProcW

-- WHNFs can be turned into programs.

program : ProcW → ProcP
program (tokenBind p) = tokenBind p
program (emptyOr p)   = emptyOr (program p)
program fail          = fail

-- Symmetric choice for WHNFs.

_∣W_ : ProcW → ProcW → ProcW
tokenBind p₁ ∣W tokenBind p₂ = tokenBind (λ c → ♯ (♭ (p₁ c) ∣ ♭ (p₂ c)))
emptyOr p₁   ∣W emptyOr p₂   = emptyOr (p₁ ∣W p₂)
emptyOr p₁   ∣W p₂           = emptyOr (p₁ ∣W p₂)
p₁           ∣W emptyOr p₂   = emptyOr (p₁ ∣W p₂)
fail         ∣W p₂           = p₂
p₁           ∣W fail         = p₁

-- Interpretation of toProc.

mutual

  toProcW′ : ∀ {n} → P n → if n then ProcW else ProcP → ProcW
  toProcW′ fail                                  κ = fail
  toProcW′ empty                                 κ = κ
  toProcW′ (tok t)                               κ = tokenBind (λ t′ →
                                                       if ⌊ t ≟ t′ ⌋ then ♯ κ else ♯ fail)
  toProcW′ (_∣_ {n₁ = true } {n₂ = true } p₁ p₂) κ = toProcW′ p₁ κ ∣W toProcW′ p₂ κ
  toProcW′ (_∣_ {n₁ = true } {n₂ = false} p₁ p₂) κ = toProcW′ p₁ κ ∣W toProcW  p₂ κ
  toProcW′ (_∣_ {n₁ = false} {n₂ = true } p₁ p₂) κ = toProcW  p₁ κ ∣W toProcW′ p₂ κ
  toProcW′ (_∣_ {n₁ = false} {n₂ = false} p₁ p₂) κ = toProcW′ p₁ κ ∣W toProcW′ p₂ κ
  toProcW′ (_·_ {n₁ = true }              p₁ p₂) κ = toProcW′ p₁ (toProcW′  p₂  κ)
  toProcW′ (_·_ {n₁ = false}              p₁ p₂) κ = toProcW′ p₁ (toProc (♭ p₂) κ)

  toProcW : ∀ {n} → P n → ProcW → ProcW
  toProcW {true } p κ = toProcW′ p          κ
  toProcW {false} p κ = toProcW′ p (program κ)

-- Programs can be turned into WHNFs.

whnf : ProcP → ProcW
whnf (tokenBind p) = tokenBind p
whnf (emptyOr p)   = emptyOr (whnf p)
whnf fail          = fail
whnf (p₁ ∣ p₂)     = whnf p₁ ∣W whnf p₂
whnf (toProc p κ)  = toProcW p (whnf κ)

-- Programs can be turned into processes.

mutual

  ⟦_⟧W : ProcW → Proc
  ⟦ tokenBind p ⟧W = tokenBind (λ c → ♯ ⟦ ♭ (p c) ⟧P)
  ⟦ emptyOr p   ⟧W = emptyOr ⟦ p ⟧W
  ⟦ fail        ⟧W = fail

  ⟦_⟧P : ProcP → Proc
  ⟦ p ⟧P = ⟦ whnf p ⟧W

------------------------------------------------------------------------
-- Alternative backend

-- I have not proved that this implementation is correct.

infix 4 _∈?_

_∈?_ : ∀ {n} → List Tok → P n → Bool
s ∈? p = run ⟦ toProc p (emptyOr fail) ⟧P s


-- MIT License

-- Copyright (c) 2021 Luca Ciccone and Luca Padovani

-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:

-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.

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

open import Size

open import Data.Empty
open import Data.Product
open import Data.Sum
open import Data.List using ([]; _∷_; _∷ʳ_; _++_)

open import Codata.Thunk

open import Relation.Nullary
open import Relation.Nullary.Negation using (contraposition)
open import Relation.Unary using (_∈_; _⊆_)
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl)
open import Relation.Binary.Construct.Closure.ReflexiveTransitive using (Star; ε; _◅_; return)

open import Function.Base using (case_of_)

open import Common

module FairSubtyping {ℙ : Set} (message : Message ℙ) where

open import Trace message
open import SessionType message
open import Transitions message
open import Session message
open import Compliance message
open import HasTrace message
open import TraceInclusion message
open import Convergence message
open import FairCompliance message

data FairSub : SessionType -> SessionType -> Size -> Set where
  nil<|any : ∀{T i} -> FairSub nil T i
  end<|def : ∀{T S i} (e : End T) (def : Defined S) -> FairSub T S i
  inp<|inp :
    ∀{f g i}
    (con : inp f ↓ inp g)
    (inc : dom f ⊆ dom g)
    (F : (x : ℙ) -> Thunk (FairSub (f x .force) (g x .force)) i) ->
    FairSub (inp f) (inp g) i
  out<|out :
    ∀{f g i}
    (con : out f ↓ out g)
    (W : Witness g)
    (inc : dom g ⊆ dom f)
    (F : ∀{x} (!x : x ∈ dom g) -> Thunk (FairSub (f x .force) (g x .force)) i) ->
    FairSub (out f) (out g) i

_<|_ : SessionType -> SessionType -> Set
_<|_ T S = FairSub T S ∞

-- sub-defined : ∀{T S} -> T <| S -> Defined T -> Defined S
-- sub-defined (end<|def _ def) _ = def
-- sub-defined (inp<|inp _ _) _   = inp
-- sub-defined (out<|out _ _ _) _ = out

fs->convergence : ∀{T S} -> T <| S -> T ↓ S
fs->convergence nil<|any = inclusion->convergence nil<=any
fs->convergence (end<|def e def) = inclusion->convergence (end<=def e def)
fs->convergence (inp<|inp con inc F) = con
fs->convergence (out<|out con W inc F) = con

fc-not-transition+end :
  ∀{R R' T α} ->
  FairComplianceS (R # T) ->
  Transition R α R' ->
  End T ->
  ⊥
fc-not-transition+end comp inp (inp U) with comp ε
... | _ , ε , win#def () _
... | _ , sync () inp ◅ _ , _
fc-not-transition+end comp inp (out U) with comp ε
... | _ , ε , win#def () _
... | _ , sync _ (out fx) ◅ _ , _ = ⊥-elim (U _ fx)
fc-not-transition+end comp (out fx) (inp V) with comp ε
... | _ , ε , win#def (out U) _ = ⊥-elim (U _ fx)
... | _ , sync (out _) inp ◅ reds , win#def w def =
  let _ , rr , tr = unzip-red* reds in
  ⊥-elim (V _ (transitions+defined->defined tr def))
fc-not-transition+end comp (out fx) (out _) with comp ε
... | _ , ε , win#def (out U) _ = ⊥-elim (U _ fx)
... | _ , sync () (out _) ◅ _ , _

sub-red :
  ∀{T S S' R R' α} ->
  FairComplianceS (R # T) ->
  T <| S ->
  Transition R (co-action α) R' ->
  Transition S α S' ->
  ∃[ T' ] (Transition T α T' × T' <| S')
sub-red comp nil<|any _ _ with comp ε
... | _ , ε , win#def _ ()
... | _ , sync _ () ◅ _ , _
sub-red comp (end<|def e _) r _ = ⊥-elim (fc-not-transition+end comp r e)
sub-red comp (inp<|inp con inc F) (out {h} hx) (inp {f}) = _ , inp , F _ .force
sub-red comp (out<|out con W inc F) inp (out fx) = _ , out (inc fx) , F fx .force

sub-red* :
  ∀{T S S' R R' φ} ->
  FairComplianceS (R # T) ->
  T <| S ->
  Transitions R (co-trace φ) R' ->
  Transitions S φ S' ->
  ∃[ T' ] (Transitions T φ T' × T' <| S')
sub-red* comp sub refl refl = _ , refl , sub
sub-red* comp sub (step r rr) (step s sr) =
  let _ , t , sub' = sub-red comp sub r s in
  let comp' = fc-transitions (step r refl) (step t refl) comp in
  let _ , tr , sub'' = sub-red* comp' sub' rr sr in
  _ , step t tr , sub''

sub-sound : ∀{T S R} -> FairComplianceS (R # T) -> T <| S -> FairComplianceS (R # S)
sub-sound comp sub {_ # _} reds =
  let _ , rr , sr = unzip-red* reds in
  let _ , tr , sub' = sub-red* comp sub rr sr in
  let comp' = fair-compliance-red* comp (zip-red* rr tr) in
  con-sound (fs->convergence sub') comp'


module Examples where

open import Data.List hiding (reverse)
open import Data.List.All
open import Data.Nat

open import Typing
open import Syntax

ex1 : Expr [] TUnit
ex1 =
  letbind [] (new [] (delay send!))
  (letpair (left []) (here [])
  (letbind (rght (left []))
           (fork (wait (here [])))
  (close (there UUnit (here [])))))

ex1dual : Expr [] TUnit
ex1dual =
  letbind [] (new [] (delay send!))
  (letpair (left []) (here [])
  (letbind (left (rght []))
           (fork (close (here [])))
  (wait (there UUnit (here [])))))

-- sending and receiving
ex2 : Expr [] TUnit
ex2 =
  letbind [] (new [] (delay (Typing.send TInt (delay send!))))
  (letpair (left []) (here [])
  (letbind (left (rght []))
           (fork (letbind (rght []) (nat [] 42)
                 (letbind (left (left [])) (Expr.send (rght (left [])) (here []) (here []))
                 (letbind (left []) (close (here []))
                 (var (here []))))))
  (letbind (rght (left [])) (Expr.recv (here []))
  (letpair (left (rght [])) (here [])
  (letbind (left (left (rght []))) (wait (here (UInt ∷ [])))
  (var (here (UUnit ∷ []))))))))

-- higher order sending and receiving
ex3 : Expr [] TUnit
ex3 =
  letbind [] (new [] (delay (Typing.send (TChan send!) (delay send!))))
  (letbind (rght []) (new [] (delay send!))
  (letpair (left (rght [])) (here [])
  (letpair (rght (rght (left []))) (here [])
  (letbind (left (rght (left (left []))))
           (fork (letbind (left (left (rght []))) (Expr.send (left (rght [])) (here []) (here []))
                 (letbind (left (rght [])) (close (here []))
                 (wait (there UUnit (here []))))))
  (letbind (left (left [])) (Expr.recv (there UUnit (here [])))
  (letpair (left []) (here [])
  (letbind (left (rght [])) (wait (here []))
  (letbind (left (left [])) (close (there UUnit (here [])))
  (var (here []))))))))))

-- branching
ex4 : Expr [] TUnit
ex4 =
  letbind [] (new [] (delay (sintern (delay send!) (delay send?))))
  (letpair (left []) (here [])
  (letbind (left (rght []))
           (fork (letbind (left []) (select Left (here []))
                 (close (here []))))
  (branch (left (left [])) (there UUnit (here []))
          (wait (here []))
          (close (here [])))))

-- simple lambda: (λx.x)()
ex5 : Expr [] TUnit
ex5 = letbind [] (llambda [] [] (var (here [])))
      (letbind (rght []) (unit [])
      (app (rght (left [])) (here []) (here [])))

-- lambda app: (λfx.fx) (λx.x)()
ex6 : Expr [] TUnit
ex6 = letbind [] (llambda [] [] (llambda (left []) [] (app (rght (left [])) (here []) (here []))))
      (letbind (rght []) (llambda [] [] (var (here [])))
      (letbind (rght (rght [])) (unit [])
      (letbind (rght (left (left []))) (app (rght (left [])) (here []) (here []))
      (app (left (rght [])) (here []) (here [])))))


record R₁ : Set₁ where
  field
    A : Set

open R₁ ⦃ … ⦄

record R₂ : Set₁ where
  field
    ⦃ r₁ ⦄ : R₁

  B : Set
  B = A


{-
  Copyright 2019 Lisandra Silva

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-}

open import Prelude
open import Data.Fin hiding (_≟_; _<?_; _≤?_ ;_+_; pred; lift)
                     renaming (_<_ to _<F_; _≤_ to _≤F_)
open import Data.List
open import Data.List.Properties
open import Data.Maybe.Base as Maybe using (Maybe; nothing; just)
open import Data.Vec.Base as Vec using (Vec)
open import Data.Bool hiding (_<_ ; _<?_; _≤_; _≤?_; _≟_)
open import Data.Vec.Any
open import Data.Vec.All hiding (lookup)

open import StateMachineModel
open import Stream

module DistributedSystem.Prototype
  {ℓ₁ ℓ₂ ℓ₃ ℓ₄ ℓ₅ : Level}
  (N : ℕ)
  (f : ℕ)
  (fta : N > 3 * f)
  (ClientRequest : Set ℓ₂)
  (Block : Set ℓ₃)
  (Vote : Set ℓ₄)
  (QC : Set ℓ₅)
  where

  -- Client can make a request at any moment

{-
    0 - Set node role
               LEADER                 ||           LEADER & FOLLOWER
    1 - block = getPoolReq
    2 - broadcast block
                                      ||    3 - receive block
                                      ||    4 - vote for block
    5 - while (#votes < N-f-1)
    6 -    reveive vote
    5 - broadcast QC
                                      ||    7 - receive QC
                                      ||    8 - check commit rule
                                      ||    9 - movenextview
-}

  -- QUESTION:
  -- To prove the liveness not only the sequence of honest leaders per view must be
  -- long enough but also the length of the requests must be greater than 3.
  -- If the sequence is smaller we don't have enough requests to ensure the commit rule

  -- A dishonest leader can deliberately left honest nodes behind, and these nodes
  -- won't move to the next view, unless they timeout. However, t if these nodes have
  -- moved for the next view for timeout, they will be missing a block and a qc
  -- in the RecordStore, how to fix this?
  --     1 - Whisper of proposed blocks and Qc's
  --     2 - Whisper only of QC's (if they have attached the proposed block)
  --     3 - Mechanism of asking records missing in the record store

  -----------------------------------------------------------------------------
  -- SPECIFICATION
  -----------------------------------------------------------------------------

  NodeID = Fin N
  HonestID = Fin (N ∸ f)
  Instruction = Fin 10

  Receiver = Fin N

  DishonestID : NodeID → Set
  DishonestID nId = N ∸ f ≤ toℕ nId

  data Message : Set (ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    blockM   : Block → Message
    voteM    : Vote  → Message
    qcM      : QC    → Message

  data Role : Set where
    leader   : Role
    follower : Role

  record NodeState : Set (ℓ₁ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    field
      nodeRole     : Role
      currNodeView : ℕ
      msgBuffer    : List Message
      readMessages : ℕ

      currProposal : Block
      votesforQC   : List Vote

      control      : Instruction

      -- id           : Node
      --recordTree    : RecordTree
      -- previousRound : ℕ
      -- lockedRound   : ℕ
      -- clock        : ℕ
      -- timeout      : ℕ
  open NodeState

  record State : Set (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    field
     -- I am not sure that we don't need the faulty Node States
     -- because we may need to express that we have at least (N ∸ f)
     -- nodes on the same view and it may be that the f nodes behave normally
     nodeStates    : Vec NodeState (N ∸ f)
     leaderPerView : Stream (Fin N)
     poolRequests  : List ClientRequest
     committedReq  : ℕ
  open State

  Honest? : Decidable ((_< N ∸ f) ∘ toℕ {N})
  Honest? x = toℕ x <? N ∸ f

  Dishonest? : Decidable ((N ∸ f ≤_) ∘ toℕ {N})
  Dishonest? x = N ∸ f ≤? toℕ x

  mkBlock : ClientRequest → NodeState → Block

  mkQC : NodeState → QC

  mkVote : NodeState → Vote

  validVote  : State → HonestID → Vote  → Set

  validBlock : State → HonestID → Block → Set

  validQC : State → HonestID → QC → Set

  ¬validMsg : State → HonestID → Message → Set

  data HonestEvent (nId : HonestID) : Set (ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    setNodeRole  : HonestEvent nId
    getPoolReq   : ClientRequest → HonestEvent nId
    broadcastB   : Block → HonestEvent nId
    broadcastQC  : QC → HonestEvent nId
    wait         : HonestEvent nId
    receive      : Message → HonestEvent nId
    dropMsg      : Message → HonestEvent nId
    sendVote     : Vote → Receiver → HonestEvent nId
    commit       : HonestEvent nId
    moveNextView : HonestEvent nId

  data DSEvent : Set (ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    newRequest     : ClientRequest → DSEvent -- A client add a request to the request pool
    honestEvent    : ∀ {nId} → HonestEvent nId → DSEvent
    dishonestEvent : Message → HonestID → DSEvent -- dishonest nodes can send any msg to honest nodes

  nodeSt : HonestID → State → NodeState
  nodeSt nId st = Vec.lookup (nodeStates st) nId

  -- Other option is to have a flag in the NodeState saying if it's leader or follower
  isLeader : State → HonestID → Set
  isLeader st nId = nodeRole (nodeSt nId st) ≡ leader

  getLeader : State → HonestID → Fin N
  getLeader st nId = get (leaderPerView st) (currNodeView (nodeSt nId st))

  {- Instruction is an abstraction for the number of intructions available for the node -}
  nextInstruction : State → HonestID → Instruction
  nextInstruction st nId = control (nodeSt nId st)

  data HonestEnabled (nId : HonestID) (st : State) : HonestEvent nId
       → Set (ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    setRoleEn      :  nextInstruction st nId ≡ 0F
                    → HonestEnabled nId st setNodeRole

    getReqEn       :  ∀ {req}
                    → isLeader st nId
                    → nextInstruction st nId ≡ 1F
                    → (comm< : committedReq st < length (poolRequests st))
                    -- Maybe it's better to have a function:
                    -- fetchNextReq : List ClientRequests → Maybe (Index , Request)
                    → req ≡ lookup (poolRequests st) (fromℕ≤ comm<)
                    → HonestEnabled nId st (getPoolReq req)

    broadcastBEn   :  isLeader st nId
                    → nextInstruction st nId ≡ 2F
                    → HonestEnabled nId st (broadcastB (currProposal (nodeSt nId st)))

    receiveBlockEn :  ∀ {b : Block}
                    → nextInstruction st nId ≡ 3F
                    → (i : readMessages (nodeSt nId st) < length (msgBuffer (nodeSt nId st)))
                    → blockM b ≡ lookup (msgBuffer (nodeSt nId st)) (fromℕ≤ i)
                    → validBlock st nId b
                    → HonestEnabled nId st (receive (blockM b))

    voteBlockEn    :  nextInstruction st nId ≡ 4F
                    → HonestEnabled nId st (sendVote
                                            (mkVote (nodeSt nId st))
                                            (getLeader st nId))

    waitVotesEn    :  isLeader st nId
                    → nextInstruction st nId ≡ 5F
                    → length (votesforQC (nodeSt nId st)) < N ∸ f ∸ 1
                    → HonestEnabled nId st wait

    receiveVoteEn  :  ∀ {v : Vote}
                    → isLeader st nId
                    → nextInstruction st nId ≡ 6F
                    → (i : readMessages (nodeSt nId st) < length (msgBuffer (nodeSt nId st)))
                    → voteM v ≡ lookup (msgBuffer (nodeSt nId st)) (fromℕ≤ i)
                    → validVote st nId v
                    → HonestEnabled nId st (receive (voteM v))

    broadcastQCEn  :  isLeader st nId
                    → nextInstruction st nId ≡ 5F
                    → length (votesforQC (nodeSt nId st)) ≡ N ∸ f ∸ 1
                    → HonestEnabled nId st (broadcastQC (mkQC (nodeSt nId st)))

    receiveQCEn    :  ∀ {qc : QC}
                    → nextInstruction st nId ≡ 7F
                    → (i : readMessages (nodeSt nId st) < length (msgBuffer (nodeSt nId st)))
                    → qcM qc ≡ lookup (msgBuffer (nodeSt nId st)) (fromℕ≤ i)
                    → validQC st nId qc
                    → HonestEnabled nId st (receive (qcM qc))

    dropMsgEn      : ∀ {msg}
                    → (i : readMessages (nodeSt nId st) < length (msgBuffer (nodeSt nId st)))
                    → msg ≡ lookup (msgBuffer (nodeSt nId st)) (fromℕ≤ i)
                    → ¬validMsg st nId msg
                    → HonestEnabled nId st (dropMsg msg)

    commitEn       :  nextInstruction st nId ≡ 8F
                    -- TODO: check commit rule
                    → HonestEnabled nId st commit

    moveNextViewEn :  nextInstruction st nId ≡ 8F ⊎ DishonestID (getLeader st nId)
                    → HonestEnabled nId st moveNextView

  data Enabled : DSEvent → State → Set (ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    newReqEn      : ∀ {st req}
                    → Enabled (newRequest req) st

    honestEvEn    : ∀ {st hId hEv}
                    → HonestEnabled hId st hEv
                    → Enabled (honestEvent hEv) st

    dishonestEvEn : ∀ {st dId msg hId}
                    → DishonestID dId
                    → Enabled (dishonestEvent msg hId) st
                    -- Maybe I need to postulate that dishonest nodes cannot
                    -- forge signatures (Node (N ∸ f))

  sendMsgToNode : Message → NodeState → NodeState
  sendMsgToNode msg nodeSt = record nodeSt { msgBuffer = msgBuffer nodeSt ++ [ msg ] }

  broadcast : State → Message → Vec NodeState (N ∸ f)
  broadcast st msg = Vec.map (λ n → sendMsgToNode msg n) (nodeStates st)

  Action : ∀ {preState} {event} → Enabled event preState → State

  Action {ps} {newRequest req} x =
    record ps
    { poolRequests = poolRequests ps ++ [ req ] }

  Action {ps} {honestEvent {nId} setNodeRole} x
    with toℕ nId ≟ toℕ (getLeader ps nId)
  ... | yes p
        = let updateNode = λ old → record old
                                   { nodeRole = leader
                                   ; control = 1F
                                   }
          in record ps
             { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }
  ... | no ¬p
        = let updateNode = λ old → record old
                               { nodeRole = follower
                               ; control = 3F
                               }
          in record ps
             { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }

  Action {ps} {honestEvent {nId} (getPoolReq req)} x
    = let updateNode = λ old → record old
                               { control = 2F
                               ; currProposal = mkBlock req old
                               }
      in record ps
         { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }

  -- QUESTION:
  -- Not sure if it's a good approach to consider a broadcast atomically,
  -- or have a different state transition to send to each node
  Action {ps} {honestEvent {nId} (broadcastB b)} x
    = let sendToAll = broadcast ps (blockM b)
          updateLeader = λ old → record old { control = 3F }
      in record ps
         { nodeStates = Vec.updateAt nId updateLeader sendToAll }

  Action {ps} {honestEvent {nId} (receive (blockM b))} x
    -- TODO : update RecordTree
    =  let updateNode = λ old → record old { readMessages = 1 + readMessages old
                                           ; currProposal = b
                                           ; control = 4F }
       in record ps
          { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }

  Action {ps} {honestEvent {nId} (sendVote v receiver)} x
    with toℕ receiver <? N ∸ f | nodeRole (nodeSt nId ps)
  -- If receiver is not honest → only updates the control variable of the sender
  ... | no ¬p | leader
        = let updateNode = λ old → record old { control = 5F }
          in record ps
             { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }
  ... | no ¬p | follower
        = let updateNode = λ old → record old { control = 7F }
          in record ps
             { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }
  -- If receiver is honest → update the message buffer of the receiver as well
  ... | yes p | leader
        = let updateReceiver = Vec.updateAt
                                 (fromℕ≤ p)
                                 (sendMsgToNode (voteM v))
                                 (nodeStates ps)
              updateNode = λ old → record old { control = 5F }
          in record ps
             { nodeStates = Vec.updateAt nId updateNode updateReceiver }
  ... | yes p | follower
        = let updateReceiver = Vec.updateAt
                                 (fromℕ≤ p)
                                 (sendMsgToNode (voteM v))
                                 (nodeStates ps)
              updateNode = λ old → record old { control = 7F }
          in record ps
             { nodeStates = Vec.updateAt nId updateNode updateReceiver }

  Action {ps} {honestEvent {nId} wait} x
    = let updateInst = λ old → record old { control = 6F }
      in record ps
         { nodeStates = Vec.updateAt nId updateInst (nodeStates ps) }

  Action {ps} {honestEvent {nId} (receive (voteM  v))} x
    = let updateNode = λ old → record old { readMessages = 1 + readMessages old
                                          ; control = 5F
                                          ; votesforQC =  v ∷ votesforQC old }
       in record ps
          { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }

  Action {ps} {honestEvent {nId} (broadcastQC q)} x
    = let sendToAll = broadcast ps (qcM q)
          updateLeader = λ old → record old { control = 7F }
       in record ps
          { nodeStates = Vec.updateAt nId updateLeader sendToAll }

  Action {ps} {honestEvent {nId} (receive (qcM qc))} x
    = let updateNode = λ old → record old { readMessages = 1 + readMessages old
                                          ; control = 8F }
      in record ps
         { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }

  Action {ps} {honestEvent {nId} (dropMsg msg)} x
    =  let updateNode = λ old → record old { readMessages = 1 + readMessages old }
       in record ps
          { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }

  Action {ps} {honestEvent commit} x = {!!}

  Action {ps} {honestEvent {nId} moveNextView} x
    = let updateNode = λ old → record old { currNodeView = 1 + currNodeView old
                                          ; control = 0F }
      in record ps
         { nodeStates = Vec.updateAt nId updateNode (nodeStates ps) }

  Action {ps} {dishonestEvent msg hId} x
    = record ps
      { nodeStates = Vec.updateAt hId (sendMsgToNode msg) (nodeStates ps) }

  nodeᵢ : NodeState → Set (ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅)
  nodeᵢ nSt =   nodeRole nSt ≡ follower
              × currNodeView nSt ≡ 0
              × msgBuffer nSt ≡ []
              × readMessages nSt ≡ 0
              × currProposal nSt ≡ {!!}
              × votesforQC nSt ≡ []
              × control nSt ≡ 0F

  stateᵢ : State → Set (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅)
  stateᵢ st = poolRequests st ≡ [] × committedReq st ≡ 0 × All nodeᵢ (nodeStates st)
    -- instantiate leaderPerView or maybe it's not necessary

  MyStateMachine : StateMachine State DSEvent
  MyStateMachine = record
                   { initial = stateᵢ
                   ; enabled = Enabled
                   ; action  = Action }

  NodeEvSet : HonestID → EventSet {Event = DSEvent}
  NodeEvSet nId (newRequest x) = ⊥
  NodeEvSet nId₁ (honestEvent {nId₂} x) = nId₁ ≡ nId₂
  NodeEvSet nId (dishonestEvent x x₁) = ⊥

  data MyWeakFairness : EventSet → Set (ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄ ⊔ ℓ₅) where
    wf : (nId : HonestID) → MyWeakFairness (NodeEvSet nId)

  MySystem : System State DSEvent
  MySystem = record
             { stateMachine = MyStateMachine
             ; weakFairness = MyWeakFairness
             }


-- Basic intuitionistic modal logic S4, without ∨, ⊥, or ◇.
-- Common syntax.

module BasicIS4.Syntax.Common where

open import Common.ContextPair public

-- Types, or propositions.

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

-- Additional useful types.

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

infixr 7 _▻⋯▻_
_▻⋯▻_ : Cx Ty → Ty → Ty
∅       ▻⋯▻ B = B
(Ξ , A) ▻⋯▻ B = Ξ ▻⋯▻ (A ▻ B)

infixr 10 □⋆_
□⋆_ : Cx Ty → Cx Ty
□⋆ ∅       = ∅
□⋆ (Ξ , A) = □⋆ Ξ , □ A

dist□⋆₁ : ∀ Ξ Ξ′ → □⋆ (Ξ ⧺ Ξ′) ≡ (□⋆ Ξ) ⧺ (□⋆ Ξ′)
dist□⋆₁ Ξ ∅        = refl
dist□⋆₁ Ξ (Ξ′ , A) = cong² _,_ (dist□⋆₁ Ξ Ξ′) refl

lift⊆ : ∀ {Δ Δ′} → Δ ⊆ Δ′ → □⋆ Δ ⊆ □⋆ Δ′
lift⊆ done     = done
lift⊆ (skip θ) = skip (lift⊆ θ)
lift⊆ (keep θ) = keep (lift⊆ θ)

-- Inversion principles.

invα : ∀ {P P′} → α P ≡ α P′ → P ≡ P′
invα refl = refl

inv▻₁ : ∀ {A A′ B B′} → A ▻ B ≡ A′ ▻ B′ → A ≡ A′
inv▻₁ refl = refl

inv▻₂ : ∀ {A A′ B B′} → A ▻ B ≡ A′ ▻ B′ → B ≡ B′
inv▻₂ refl = refl

inv□ : ∀ {A A′} → □ A ≡ □ A′ → A ≡ A′
inv□ refl = refl

inv∧₁ : ∀ {A A′ B B′} → A ∧ B ≡ A′ ∧ B′ → A ≡ A′
inv∧₁ refl = refl

inv∧₂ : ∀ {A A′ B B′} → A ∧ B ≡ A′ ∧ B′ → B ≡ B′
inv∧₂ refl = refl

-- Decidable equality on types.

_≟ᵀ_ : (A A′ : Ty) → Dec (A ≡ A′)
(α P)   ≟ᵀ (α P′)    with P ≟ᵅ P′
(α P)   ≟ᵀ (α .P)    | yes refl = yes refl
(α P)   ≟ᵀ (α P′)    | no  P≢P′ = no (P≢P′ ∘ invα)
(α P)   ≟ᵀ (A′ ▻ B′) = no λ ()
(α P)   ≟ᵀ (□ A′)    = no λ ()
(α P)   ≟ᵀ (A′ ∧ B′) = no λ ()
(α P)   ≟ᵀ ⊤        = no λ ()
(A ▻ B) ≟ᵀ (α P′)    = no λ ()
(A ▻ B) ≟ᵀ (A′ ▻ B′) with A ≟ᵀ A′ | B ≟ᵀ B′
(A ▻ B) ≟ᵀ (.A ▻ .B) | yes refl | yes refl = yes refl
(A ▻ B) ≟ᵀ (A′ ▻ B′) | no  A≢A′ | _        = no (A≢A′ ∘ inv▻₁)
(A ▻ B) ≟ᵀ (A′ ▻ B′) | _        | no  B≢B′ = no (B≢B′ ∘ inv▻₂)
(A ▻ B) ≟ᵀ (□ A′)    = no λ ()
(A ▻ B) ≟ᵀ (A′ ∧ B′) = no λ ()
(A ▻ B) ≟ᵀ ⊤        = no λ ()
(□ A)   ≟ᵀ (α P′)    = no λ ()
(□ A)   ≟ᵀ (A′ ▻ B′) = no λ ()
(□ A)   ≟ᵀ (□ A′)    with A ≟ᵀ A′
(□ A)   ≟ᵀ (□ .A)    | yes refl = yes refl
(□ A)   ≟ᵀ (□ A′)    | no  A≢A′ = no (A≢A′ ∘ inv□)
(□ A)   ≟ᵀ (A′ ∧ B′) = no λ ()
(□ A)   ≟ᵀ ⊤        = no λ ()
(A ∧ B) ≟ᵀ (α P′)    = no λ ()
(A ∧ B) ≟ᵀ (A′ ▻ B′) = no λ ()
(A ∧ B) ≟ᵀ (□ A′)    = no λ ()
(A ∧ B) ≟ᵀ (A′ ∧ B′) with A ≟ᵀ A′ | B ≟ᵀ B′
(A ∧ B) ≟ᵀ (.A ∧ .B) | yes refl | yes refl = yes refl
(A ∧ B) ≟ᵀ (A′ ∧ B′) | no  A≢A′ | _        = no (A≢A′ ∘ inv∧₁)
(A ∧ B) ≟ᵀ (A′ ∧ B′) | _        | no  B≢B′ = no (B≢B′ ∘ inv∧₂)
(A ∧ B) ≟ᵀ ⊤        = no λ ()
⊤      ≟ᵀ (α P′)    = no λ ()
⊤      ≟ᵀ (A′ ▻ B′) = no λ ()
⊤      ≟ᵀ (□ A′)    = no λ ()
⊤      ≟ᵀ (A′ ∧ B′) = no λ ()
⊤      ≟ᵀ ⊤        = yes refl

open ContextEquality (_≟ᵀ_) public