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

open import HoTT renaming (pt to ⊙pt)
open import homotopy.Bouquet
open import homotopy.FinWedge
open import homotopy.SphereEndomorphism
open import homotopy.DisjointlyPointedSet
open import groups.SphereEndomorphism
open import groups.SumOfSubIndicator
open import groups.DisjointlyPointedSet
open import cw.CW
open import cw.DegreeByProjection
open import cw.FinBoundary
open import cw.FinCW
open import cw.WedgeOfCells
open import cohomology.Theory

module cw.cohomology.cochainequiv.FirstCoboundary (OT : OrdinaryTheory lzero)
  (⊙fin-skel : ⊙FinSkeleton 1) where

open OrdinaryTheory OT
open import cohomology.RephraseSubFinCoboundary OT
open import cohomology.SubFinBouquet OT

private
  ⊙skel = ⊙FinSkeleton-realize ⊙fin-skel
  fin-skel = ⊙FinSkeleton.skel ⊙fin-skel
  I = AttachedFinSkeleton.numCells fin-skel
  skel = ⊙Skeleton.skel ⊙skel
  dec = FinSkeleton-has-cells-with-dec-eq fin-skel
  ac = FinSkeleton-has-cells-with-choice 0 fin-skel lzero

  fin-skel₋₁ = fcw-init fin-skel
  ac₋₁ = FinSkeleton-has-cells-with-choice 0 fin-skel₋₁ lzero
  endpoint = attaching-last skel
  I₋₁ = AttachedFinSkeleton.skel fin-skel
  ⊙head = ⊙cw-head ⊙skel
  pt = ⊙pt ⊙head

open DegreeAtOne skel dec
open import cw.cohomology.WedgeOfCells OT ⊙skel
open import cw.cohomology.reconstructed.TipAndAugment OT (⊙cw-init ⊙skel)
open import cw.cohomology.reconstructed.TipCoboundary OT ⊙skel
open import cw.cohomology.cochainequiv.FirstCoboundaryAbstractDefs OT ⊙fin-skel

abstract
  rephrase-cw-co∂-head'-in-degree : ∀ g
    → GroupIso.f (CXₙ/Xₙ₋₁-diag-β ac) (GroupHom.f cw-co∂-head' (GroupIso.g (CX₀-diag-β ac₋₁) g))
    ∼ λ <I → Group.subsum-r (C2 0) ⊙head-separate
        (λ b → Group.exp (C2 0) (g b) (degree <I (fst b)))
  rephrase-cw-co∂-head'-in-degree g <I =
    GroupIso.f (C-FinBouquet-diag 1 I)
      (CEl-fmap 1 (⊙–> (Bouquet-⊙equiv-Xₙ/Xₙ₋₁ skel))
        (CEl-fmap 1 ⊙cw-∂-head'-before-Susp
          (<– (CEl-Susp 0 ⊙head)
            (CEl-fmap 0 (⊙<– (Bouquet-⊙equiv-X ⊙head-is-separable))
              (GroupIso.g (C-SubFinBouquet-diag 0 MinusPoint-⊙head-has-choice ⊙head-separate)
                g))))) <I
      =⟨ ap
          (λ g →
            GroupIso.f (C-FinBouquet-diag 1 I)
              (CEl-fmap 1 (⊙–> (Bouquet-⊙equiv-Xₙ/Xₙ₋₁ skel))
                (CEl-fmap 1 ⊙cw-∂-head'-before-Susp g)) <I) $
          C-Susp-fmap' 0 (⊙<– (Bouquet-⊙equiv-X (Fin-has-dec-eq pt))) □$ᴳ
            (GroupIso.g (C-SubFinBouquet-diag 0 MinusPoint-⊙head-has-choice ⊙head-separate)
              g) ⟩
    GroupIso.f (C-FinBouquet-diag 1 I)
      (CEl-fmap 1 (⊙–> (Bouquet-⊙equiv-Xₙ/Xₙ₋₁ skel))
        (CEl-fmap 1 ⊙cw-∂-head'-before-Susp
          (CEl-fmap 1 (⊙Susp-fmap (⊙<– (Bouquet-⊙equiv-X ⊙head-is-separable)))
            (<– (CEl-Susp 0 (⊙Bouquet (MinusPoint ⊙head) 0))
              (GroupIso.g (C-SubFinBouquet-diag 0 MinusPoint-⊙head-has-choice ⊙head-separate)
                g))))) <I
      =⟨ ap (λ g → GroupIso.f (C-FinBouquet-diag 1 I) g <I) $
            ∘-CEl-fmap 1 (⊙–> (Bouquet-⊙equiv-Xₙ/Xₙ₋₁ skel)) ⊙cw-∂-head'-before-Susp
              (CEl-fmap 1 (⊙Susp-fmap (⊙<– (Bouquet-⊙equiv-X ⊙head-is-separable)))
                (<– (CEl-Susp 0 (⊙Bouquet (MinusPoint ⊙head) 0))
                  (GroupIso.g (C-SubFinBouquet-diag 0 MinusPoint-⊙head-has-choice ⊙head-separate)
                    g)))
          ∙ ∘-CEl-fmap 1
              (⊙cw-∂-head'-before-Susp ⊙∘ ⊙–> (Bouquet-⊙equiv-Xₙ/Xₙ₋₁ skel))
              (⊙Susp-fmap (⊙<– (Bouquet-⊙equiv-X ⊙head-is-separable)))
              (<– (CEl-Susp 0 (⊙Bouquet (MinusPoint ⊙head) 0))
                (GroupIso.g (C-SubFinBouquet-diag 0 MinusPoint-⊙head-has-choice ⊙head-separate)
                  g))
          ∙ ap (λ f → CEl-fmap 1 f
              (<– (CEl-Susp 0 (⊙Bouquet (MinusPoint ⊙head) 0))
                (GroupIso.g (C-SubFinBouquet-diag 0 MinusPoint-⊙head-has-choice ⊙head-separate)
                  g)))
              (! ⊙function₀'-β) ⟩
    GroupIso.f (C-FinBouquet-diag 1 I)
      (CEl-fmap 1 ⊙function₀'
        (<– (CEl-Susp 0 (⊙Bouquet (MinusPoint ⊙head) 0))
          (GroupIso.g (C-SubFinBouquet-diag 0 MinusPoint-⊙head-has-choice ⊙head-separate)
            g))) <I
      =⟨ rephrase-in-degree 0 {I = I} MinusPoint-⊙head-has-choice
          MinusPoint-⊙head-has-dec-eq ⊙head-separate-equiv ⊙function₀' g <I ⟩
    Group.subsum-r (C2 0) ⊙head-separate
      (λ b → Group.exp (C2 0) (g b)
        (⊙SphereS-endo-degree 0
          (⊙Susp-fmap (⊙bwproj MinusPoint-⊙head-has-dec-eq b) ⊙∘ ⊙function₀' ⊙∘ ⊙fwin <I)))
      =⟨ ap (Group.subsum-r (C2 0) ⊙head-separate)
          (λ= λ b → ap (Group.exp (C2 0) (g b)) $
            ⊙SphereS-endo-degree-base-indep 0
              {f = (  ⊙Susp-fmap (⊙bwproj MinusPoint-⊙head-has-dec-eq b)
                   ⊙∘ ⊙function₀'
                   ⊙∘ ⊙fwin <I)}
              {g = (Susp-fmap (function₁' <I b) , idp)}
              (mega-reduction <I b)) ⟩
    Group.subsum-r (C2 0) ⊙head-separate
      (λ b → Group.exp (C2 0) (g b)
        (⊙SphereS-endo-degree 0
          (Susp-fmap (function₁' <I b) , idp)))
      =⟨ ap (Group.subsum-r (C2 0) ⊙head-separate)
          (λ= λ b → ap (Group.exp (C2 0) (g b)) $
            degree-matches <I b) ⟩
    Group.subsum-r (C2 0) ⊙head-separate
      (λ b → Group.exp (C2 0) (g b)
        (degree <I (fst b)))
      =∎

abstract
  private
    degree-true-≠ : ∀ {<I <I₋₁} → (<I₋₁ ≠ endpoint <I true) → degree-true <I <I₋₁ == 0
    degree-true-≠ {<I} {<I₋₁} neq with Fin-has-dec-eq <I₋₁ (endpoint <I true)
    ... | inl eq = ⊥-rec (neq eq)
    ... | inr _ = idp

    degree-true-diag : ∀ <I → degree-true <I (endpoint <I true) == -1
    degree-true-diag <I with Fin-has-dec-eq (endpoint <I true) (endpoint <I true)
    ... | inl _ = idp
    ... | inr neq = ⊥-rec (neq idp)

    sum-degree-true : ∀ <I g → Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) g (degree-true <I <I₋₁)) == Group.inv (C2 0) g
    sum-degree-true <I g =
        sum-subindicator (C2 0) (λ <I₋₁ → Group.exp (C2 0) g (degree-true <I <I₋₁))
          (endpoint <I true) (λ neq → ap (Group.exp (C2 0) g) (degree-true-≠ neq))
      ∙ ap (Group.exp (C2 0) g) (degree-true-diag <I)

    degree-false-≠ : ∀ {<I <I₋₁} → (<I₋₁ ≠ endpoint <I false) → degree-false <I <I₋₁ == 0
    degree-false-≠ {<I} {<I₋₁} neq with Fin-has-dec-eq <I₋₁ (endpoint <I false)
    ... | inl eq = ⊥-rec (neq eq)
    ... | inr _ = idp

    degree-false-diag : ∀ <I → degree-false <I (endpoint <I false) == 1
    degree-false-diag <I with Fin-has-dec-eq (endpoint <I false) (endpoint <I false)
    ... | inl _ = idp
    ... | inr neq = ⊥-rec (neq idp)

    sum-degree-false : ∀ <I g → Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) g (degree-false <I <I₋₁)) == g
    sum-degree-false <I g =
        sum-subindicator (C2 0) (λ <I₋₁ → Group.exp (C2 0) g (degree-false <I <I₋₁))
          (endpoint <I false) (λ neq → ap (Group.exp (C2 0) g) (degree-false-≠ neq))
      ∙ ap (Group.exp (C2 0) g) (degree-false-diag <I)

    sum-degree : ∀ <I g → Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) g (degree <I <I₋₁)) == Group.ident (C2 0)
    sum-degree <I g =
        ap (Group.sum (C2 0)) (λ= λ <I₋₁ → Group.exp-+ (C2 0) g (degree-true <I <I₋₁) (degree-false <I <I₋₁))
      ∙ AbGroup.sum-comp (C2-abgroup 0)
          (λ <I₋₁ → Group.exp (C2 0) g (degree-true <I <I₋₁))
          (λ <I₋₁ → Group.exp (C2 0) g (degree-false <I <I₋₁))
      ∙ ap2 (Group.comp (C2 0))
          (sum-degree-true <I g)
          (sum-degree-false <I g)
      ∙ Group.inv-l (C2 0) g

    merge-branches : ∀ (g : Fin I₋₁ → Group.El (C2 0)) (g-pt : g pt == Group.ident (C2 0)) x
      →  Coprod-rec (λ _ → Group.ident (C2 0)) (λ b → g (fst b)) (⊙head-separate x)
      == g x
    merge-branches g g-pt x with Fin-has-dec-eq pt x
    merge-branches g g-pt x | inl idp = ! g-pt
    merge-branches g g-pt x | inr _ = idp

  rephrase-cw-co∂-head-in-degree : ∀ g
    → GroupIso.f (CXₙ/Xₙ₋₁-diag-β ac) (GroupHom.f cw-co∂-head (GroupIso.g (C2×CX₀-diag-β ac₋₁) g))
    ∼ λ <I → Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (degree <I <I₋₁))
  rephrase-cw-co∂-head-in-degree g <I =
    GroupIso.f (CXₙ/Xₙ₋₁-diag-β ac) (GroupHom.f cw-co∂-head' (GroupIso.g (CX₀-diag-β ac₋₁) (snd (diff-and-separate (C2 0) g)))) <I
      =⟨ rephrase-cw-co∂-head'-in-degree (snd (diff-and-separate (C2 0) g)) <I ⟩
    Group.subsum-r (C2 0) ⊙head-separate
      (λ b → Group.exp (C2 0) (Group.diff (C2 0) (g (fst b)) (g pt))
        (degree <I (fst b)))
      =⟨ ap (Group.sum (C2 0)) (λ= λ <I₋₁ →
          merge-branches
            (λ <I₋₁ → Group.exp (C2 0) (Group.diff (C2 0) (g <I₋₁) (g pt)) (degree <I <I₋₁))
            ( ap (λ g → Group.exp (C2 0) g (degree <I pt)) (Group.inv-r (C2 0) (g pt))
            ∙ Group.exp-ident (C2 0) (degree <I pt))
            <I₋₁
          ∙ AbGroup.exp-comp (C2-abgroup 0) (g <I₋₁) (Group.inv (C2 0) (g pt)) (degree <I <I₋₁)) ⟩
    Group.sum (C2 0) (λ <I₋₁ →
      Group.comp (C2 0)
        (Group.exp (C2 0) (g <I₋₁) (degree <I <I₋₁))
        (Group.exp (C2 0) (Group.inv (C2 0) (g pt)) (degree <I <I₋₁)))
      =⟨ AbGroup.sum-comp (C2-abgroup 0)
          (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (degree <I <I₋₁))
          (λ <I₋₁ → Group.exp (C2 0) (Group.inv (C2 0) (g pt)) (degree <I <I₋₁)) ⟩
    Group.comp (C2 0)
      (Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (degree <I <I₋₁)))
      (Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (Group.inv (C2 0) (g pt)) (degree <I <I₋₁)))
      =⟨ ap (Group.comp (C2 0) (Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (degree <I <I₋₁))))
          (sum-degree <I (Group.inv (C2 0) (g pt)))
        ∙ Group.unit-r (C2 0) _ ⟩
    Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (degree <I <I₋₁))
      =∎


{-# OPTIONS --omega-in-omega --no-termination-check --overlapping-instances #-}

module Light.Implementation.Data.Empty where

open import Light.Library.Data.Empty using (Library ; Dependencies)
open import Light.Level using (0ℓ)
open import Light.Variable.Sets

instance dependencies : Dependencies
dependencies = record {}

instance library : Library dependencies
library = record { Implementation }
      where
      module Implementation where
            data Empty : Set where

            eliminate : Empty → 𝕒
            eliminate ()


-- Andreas, 2014-03-27 fixed issue

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

open import Common.Size

record R (i : Size) : Set where
  inductive
  constructor c
  field
    j : Size< i
    r : R j

data ⊥ : Set where

elim : (i : Size) → R i → ⊥
elim i (c j r) = elim j r

-- elim should be rejected by termination checker.

-- Being accepted, its is translated into
--
--   elim i x = elim (R.j x) (R.r x)
--
-- which is making reduceHead in the injectivity checker
-- produce larger and larger applications of elim.
-- Leading to a stack overflow.

inh : R ∞
R.j inh = ∞
R.r inh = inh

-- inh should also be rejected


------------------------------------------------------------------------
-- Tactics for proving instances of Σ-cong (and Surjection.Σ-cong)
-- with "better" computational behaviour
------------------------------------------------------------------------

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

open import Equality

module Tactic.Sigma-cong
  {e⁺} (eq : ∀ {a p} → Equality-with-J a p e⁺) where

open Derived-definitions-and-properties eq

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

open import Bijection eq as B using (_↔_)
open import Equivalence eq as Eq using (_≃_)
open import Function-universe eq as F hiding (id; _∘_)
open import Monad eq
open import Surjection eq as S using (_↠_)
open import TC-monad eq hiding (Type)

private

  variable
    a b                 : Level
    A B                 : Type a
    f g k k₁ k₂ p q x y : A
    P Q                 : A → Type p

------------------------------------------------------------------------
-- Σ-cong-refl

-- Building blocks used to construct the proofs in Σ-cong-refl.

record Σ-cong-refl-proofs
         (k : Kind)
         (A : Type a) (B : Type b) (P : A → Type p) (Q : B → Type q) :
         Type (lsuc (a ⊔ b ⊔ p ⊔ q)) where
  field
    refl₁ : (x : Σ B Q) → x ≡ x
    refl₂ : (x : Σ A P) → x ≡ x

    cong-refl₃ :
      (f : Σ A P → Σ B Q)
      (p : Σ A P) →
      cong f (refl p) ≡ refl (f p)

    →↝ :
      (A⇔B : A ⇔ B)
      (P→Q : ∀ x → P x → Q (_⇔_.to A⇔B x))
      (Q→P : ∀ x → Q x → P (_⇔_.from A⇔B x)) →
      (eq₁ : (p : Σ B Q) →
             Σ-map (_⇔_.to A⇔B) (P→Q _)
               (Σ-map (_⇔_.from A⇔B) (Q→P _) p) ≡
             p) →
      (eq₂ : (p : Σ A P) →
             Σ-map (_⇔_.from A⇔B) (Q→P _)
               (Σ-map (_⇔_.to A⇔B) (P→Q _) p) ≡
             p) →
      ((p : Σ A P) →
       cong (Σ-map (_⇔_.to A⇔B) (P→Q _)) (eq₂ p) ≡
       eq₁ (Σ-map (_⇔_.to A⇔B) (P→Q _) p)) →
      Σ A P ↝[ k ] Σ B Q

instance

  -- The building blocks can be proved.

  instance-of-Σ-cong-refl-proofs :
    Σ-cong-refl-proofs k A B P Q
  instance-of-Σ-cong-refl-proofs = λ where
      .refl₁ _              → refl _
      .refl₂ _              → refl _
      .cong-refl₃ _ _       → cong-refl _
      .→↝ A⇔B P→Q Q→P p q r →
        from-equivalence $
        Eq.⟨   Σ-map (_⇔_.to   A⇔B) (P→Q _)
           , ( Σ-map (_⇔_.from A⇔B) (Q→P _)
             , p
             , q
             , r
             )
           ⟩
    where
    open Σ-cong-refl-proofs

private

  -- Used to implement Σ-cong-refl.
  --
  -- The constructed term is returned.

  Σ-cong-refl-tactic :
    Σ-cong-refl-proofs k A B P Q →
    (A⇔B : A ⇔ B) → (∀ x → P x ⇔ Q (_⇔_.to A⇔B x)) →
    Term → TC Term
  Σ-cong-refl-tactic proofs A⇔B P⇔Q goal = do
    refl₁      ← quoteTC (Σ-cong-refl-proofs.refl₁      proofs)
    refl₂      ← quoteTC (Σ-cong-refl-proofs.refl₂      proofs)
    cong-refl₃ ← quoteTC (Σ-cong-refl-proofs.cong-refl₃ proofs
                            (Σ-map (_⇔_.to A⇔B) (_⇔_.to (P⇔Q _))))
    proofs     ← quoteTC proofs
    P→Q        ← quoteTC (_⇔_.to   ∘ P⇔Q)
    Q→P        ← quoteTC (_⇔_.from ∘ P⇔Q ∘ _⇔_.from A⇔B)
    A⇔B        ← quoteTC A⇔B
    let t = def (quote Σ-cong-refl-proofs.→↝)
                (varg proofs ∷
                 varg A⇔B ∷
                 varg P→Q ∷
                 varg Q→P ∷
                 varg refl₁ ∷
                 varg refl₂ ∷
                 varg cong-refl₃ ∷
                 [])
    unify goal t
    return t

macro

  -- A macro that tries to prove Σ A P ↝[ k₃ ] Σ B Q.
  --
  -- If k₃ is symmetric, then the two directions of the proof are
  -- constructed in the following way:
  --
  -- * The forward direction is a function from Σ A P to Σ B Q,
  --   constructed in the "obvious" way using the forward directions
  --   of the two supplied proofs.
  --
  -- * The other direction is a function from Σ B (Q ∘ f ∘ g) to
  --   Σ A P, where f and g are the two directions of the first proof.
  --   This function is also constructed in the "obvious" way, using
  --   the right-to-left directions of the two supplied proofs. It is
  --   assumed that Q ∘ f ∘ g is definitionally equal to Q.
  --
  -- The two functions are assumed to be definitionally equal, so that
  -- they can be proved to be inverses of each other (pointwise) using
  -- reflexivity.

  Σ-cong-refl :
    {k₁ k₂ : Symmetric-kind} {k₃ : Kind}
    ⦃ proofs : Σ-cong-refl-proofs k₃ A B P Q ⦄ →
    (A↝B : A ↝[ ⌊ k₁ ⌋-sym ] B) →
    (∀ x → P x ↝[ ⌊ k₂ ⌋-sym ] Q (_⇔_.to (sym→⇔ A↝B) x)) →
    Term → TC ⊤
  Σ-cong-refl ⦃ proofs = proofs ⦄ A↝B P↝Q goal = do
    Σ-cong-refl-tactic proofs (sym→⇔ A↝B) (sym→⇔ ∘ P↝Q) goal
    return _

private

  -- Some unit tests.

  t₁ : (A × A) ≃ (A × A)
  t₁ = Σ-cong-refl L.id λ _ → B.id

  _ : _≃_.to t₁ p ≡ p
  _ = refl _

  _ : _≃_.from t₁ p ≡ p
  _ = refl _

  _ :
    {p : A × A} →
    _≃_.right-inverse-of t₁ p ≡ refl _
  _ = refl _

  _ :
    {p : A × A} →
    _≃_.left-inverse-of t₁ p ≡ refl _
  _ = refl _

  t₂ : (A × A) ↔ (A × A)
  t₂ = Σ-cong-refl Eq.id λ _ → L.id

  _ : _↔_.to t₂ p ≡ p
  _ = refl _

  _ : _↔_.from t₂ p ≡ p
  _ = refl _

  _ :
    {p : A × A} →
    _↔_.right-inverse-of t₂ p ≡ refl _
  _ = refl _

  _ :
    {p : A × A} →
    _↔_.left-inverse-of t₂ p ≡ refl _
  _ = refl _

  t₃ :
    {P : A → B → Type p} {Q : A → B → (∀ x y → P x y) → Type q} →
    (∃ λ (f : ∀ x y → P x y) → ∀ x y → Q x y f) ≃
    (∃ λ (f : ∀ y x → P x y) → ∀ y x → Q x y (flip f))
  t₃ = Σ-cong-refl Π-comm λ _ → Π-comm

  _ : _≃_.to t₃ p ≡ Σ-map flip flip p
  _ = refl _

  _ : _≃_.from t₃ p ≡ Σ-map flip flip p
  _ = refl _

  _ :
    {P : A → B → Type p} {Q : A → B → (∀ x y → P x y) → Type q}
    {p : ∃ λ (f : ∀ y x → P x y) → ∀ y x → Q x y (flip f)} →
    _≃_.right-inverse-of (t₃ {Q = Q}) p ≡ refl _
  _ = refl _

  _ :
    {P : A → B → Type p} {Q : A → B → (∀ x y → P x y) → Type q}
    {p : ∃ λ (f : ∀ x y → P x y) → ∀ x y → Q x y f} →
    _≃_.left-inverse-of (t₃ {Q = Q}) p ≡ refl _
  _ = refl _

  -- The following proof—implemented using Σ-cong—is, at the time of
  -- writing, "worse".

  t₃′ :
    {P : A → B → Type p} {Q : A → B → (∀ x y → P x y) → Type q} →
    (∃ λ (f : ∀ x y → P x y) → ∀ x y → Q x y f) ≃
    (∃ λ (f : ∀ y x → P x y) → ∀ y x → Q x y (flip f))
  t₃′ = Eq.↔⇒≃ $ Σ-cong Π-comm λ _ → Π-comm

  _ : _≃_.to t₃′ p ≡ Σ-map flip flip p
  _ = refl _

  _ :
    ∀ {P : A → B → Type p} {Q : A → B → (∀ x y → P x y) → Type q} {p} →
    _≃_.from (t₃′ {Q = Q}) p ≡
    Σ-map flip
      (flip ∘ subst (λ f → ∀ y x → Q x y (flip f)) (sym (refl _)))
      p
  _ = refl _

------------------------------------------------------------------------
-- Σ-cong-id

-- Building blocks used to construct the proofs in Σ-cong-id.

record Σ-cong-id-proofs
         (k : Kind) (A : Type a) (B : Type b) (P : B → Type p) :
         Type (lsuc (a ⊔ b ⊔ p)) where
  field
    refl₁ : (x : Σ B P) → x ≡ x

    subst-refl₂ :
      (A≃B : A ≃ B)
      ((x , y) : Σ A (P ∘ _≃_.to A≃B)) →
      subst P (refl (_≃_.to A≃B x)) y ≡ y

    →↝ :
      (A≃B : A ≃ B)
      (g : ∀ x → P x → P (_≃_.to A≃B (_≃_.from A≃B x))) →
      ((p : Σ B P) →
       Σ-map (_≃_.to A≃B) id (Σ-map (_≃_.from A≃B) (g _) p) ≡ p) →
      (((x , y) : Σ A (P ∘ _≃_.to A≃B)) →
       subst P (_≃_.right-inverse-of A≃B (_≃_.to A≃B x)) (g _ y) ≡ y) →
      Σ A (P ∘ _≃_.to A≃B) ↝[ k ] Σ B P

instance

  -- The building blocks can be proved.

  instance-of-Σ-cong-id-proofs : Σ-cong-id-proofs k A B P
  instance-of-Σ-cong-id-proofs {P = P} = λ where
      .refl₁           → refl
      .subst-refl₂ _ _ → subst-refl _ _
      .→↝ A≃B g p q    →
        let open _≃_ A≃B in
        from-equivalence $
        Eq.↔→≃
          (Σ-map to id)
          (Σ-map from (g _))
          p
          (λ (x , y) →
             Σ-≡,≡→≡ (left-inverse-of _)
               (subst (P ∘ to) (left-inverse-of x) (g _ y)     ≡⟨ subst-∘ _ _ _ ⟩
                subst P (cong to (left-inverse-of x)) (g _ y)  ≡⟨ cong (λ eq → subst P eq _) (left-right-lemma x) ⟩
                subst P (right-inverse-of (to x)) (g _ y)      ≡⟨ q _ ⟩∎
                y                                              ∎))
    where
    open Σ-cong-id-proofs

private

  -- Used to implement Σ-cong-id.
  --
  -- The constructed term is returned.

  Σ-cong-id-tactic :
    Σ-cong-id-proofs k A B P →
    A ≃ B → Term → TC Term
  Σ-cong-id-tactic proofs A≃B goal = do
    refl₁       ← quoteTC (Σ-cong-id-proofs.refl₁       proofs)
    subst-refl₂ ← quoteTC (Σ-cong-id-proofs.subst-refl₂ proofs A≃B)
    proofs      ← quoteTC proofs
    A≃B         ← quoteTC A≃B
    let t = def (quote Σ-cong-id-proofs.→↝)
                (varg proofs ∷
                 varg A≃B ∷
                 varg (lam visible $ abs "_" $
                       lam visible $ abs "x" $
                       var 0 []) ∷
                 varg refl₁ ∷
                 varg subst-refl₂ ∷
                 [])
    unify goal t
    return t

macro

  -- A macro that tries to prove
  -- Σ A (P ∘ from-isomorphism eq) ↝[ k₂ ] Σ B P, given that there is
  -- a proof eq : A ↔[ k₁ ] B for which the "right inverse of" proof
  -- is definitionally equal to reflexivity. If k₂ is surjection,
  -- bijection or equivalence, then the "right inverse of" component
  -- of the resulting proof is reflexivity.

  Σ-cong-id :
    ⦃ proofs : Σ-cong-id-proofs k₂ A B P ⦄ →
    A ↔[ k₁ ] B → Term → TC ⊤
  Σ-cong-id ⦃ proofs = proofs ⦄ A↝B goal = do
    Σ-cong-id-tactic proofs (from-isomorphism A↝B) goal
    return _

private

  -- Some unit tests.

  t₄ : (A × A) ↔ (A × A)
  t₄ = Σ-cong-id B.id

  _ : _↔_.to t₄ p ≡ p
  _ = refl _

  _ : _↔_.from t₄ p ≡ p
  _ = refl _

  _ :
    {p : A × A} →
    _↔_.right-inverse-of t₄ p ≡ refl _
  _ = refl _

  t₅ : (∃ λ (x : ⊥₀ ⊎ A) → _↔_.to ⊎-left-identity x ≡ y) ≃
       (∃ λ (x : A) → x ≡ y)
  t₅ = Σ-cong-id ⊎-left-identity

  _ : _≃_.to t₅ (inj₂ x , p) ≡ (x , p)
  _ = refl _

  _ : _≃_.from t₅ p ≡ Σ-map inj₂ id p
  _ = refl _

  _ :
    {p : ∃ λ (x : A) → x ≡ y} →
    _≃_.right-inverse-of t₅ p ≡ refl _
  _ = refl _

  -- The following proof—implemented using Σ-cong—is, at the time of
  -- writing, "worse".

  t₅′ : (∃ λ (x : ⊥₀ ⊎ A) → _↔_.to ⊎-left-identity x ≡ y) ≃
        (∃ λ (x : A) → x ≡ y)
  t₅′ = Eq.↔⇒≃ $ Σ-cong ⊎-left-identity λ _ → F.id

  _ : _≃_.to t₅′ (inj₂ x , p) ≡ (x , p)
  _ = refl _

  _ : _≃_.from t₅′ p ≡
      Σ-map inj₂ (subst (_≡ y) (sym (refl _))) p
  _ = refl _

------------------------------------------------------------------------
-- Σ-cong-id-↠

-- Building blocks used to construct the proofs in Σ-cong-id-↠.

record Σ-cong-id-↠-proofs
         (A : Type a) (B : Type b) (P : B → Type p) :
         Type (lsuc (a ⊔ b ⊔ p)) where
  field
    refl₁ : (x : Σ B P) → x ≡ x

    →↠ :
      (A↠B : A ↠ B)
      (g : ∀ x → P x → P (_↠_.to A↠B (_↠_.from A↠B x))) →
      ((p : Σ B P) →
       Σ-map (_↠_.to A↠B) id (Σ-map (_↠_.from A↠B) (g _) p) ≡ p) →
      Σ A (P ∘ _↠_.to A↠B) ↠ Σ B P

instance

  -- The building blocks can be proved.

  instance-of-Σ-cong-id-↠-proofs : Σ-cong-id-↠-proofs A B P
  instance-of-Σ-cong-id-↠-proofs {P = P} = λ where
      .refl₁      → refl
      .→↠ A↠B g p → record
        { logical-equivalence = record
          { to   = Σ-map (_↠_.to   A↠B) id
          ; from = Σ-map (_↠_.from A↠B) (g _)
          }
        ; right-inverse-of = p
        }
    where
    open Σ-cong-id-↠-proofs

private

  -- Used to implement Σ-cong-id-↠.
  --
  -- The constructed term is returned.

  Σ-cong-id-↠-tactic :
    Σ-cong-id-↠-proofs A B P →
    A ↠ B → Term → TC Term
  Σ-cong-id-↠-tactic proofs A↠B goal = do
    refl₁  ← quoteTC (Σ-cong-id-↠-proofs.refl₁ proofs)
    proofs ← quoteTC proofs
    A↠B    ← quoteTC A↠B
    let t = def (quote Σ-cong-id-↠-proofs.→↠)
                (varg proofs ∷
                 varg A↠B ∷
                 varg (lam visible $ abs "_" $
                       lam visible $ abs "x" $
                       var 0 []) ∷
                 varg refl₁ ∷
                 [])
    unify goal t
    return t

macro

  -- A macro that tries to prove Σ A (P ∘ _↠_.to eq) ↠ Σ B P, given a
  -- proof eq for which _↠_.to eq ∘ _↠_.from eq is definitionally
  -- equal to the identity function. The right-inverse-of component of
  -- the resulting proof is reflexivity.

  Σ-cong-id-↠ :
    ⦃ proofs : Σ-cong-id-↠-proofs A B P ⦄ →
    A ↠ B → Term → TC ⊤
  Σ-cong-id-↠ ⦃ proofs = proofs ⦄ A↠B goal = do
    Σ-cong-id-↠-tactic proofs A↠B goal
    return _

private

  -- Some unit tests.

  t₆ : (A × A) ↠ (A × A)
  t₆ = Σ-cong-id-↠ S.id

  _ : _↠_.to t₆ p ≡ p
  _ = refl _

  _ : _↠_.from t₆ p ≡ p
  _ = refl _

  _ :
    {p : A × A} →
    _↠_.right-inverse-of t₆ p ≡ refl _
  _ = refl _

  t₇ :
    (∃ λ (f : (x : A ⊎ B) → P x) → (f ∘ inj₁ , f ∘ inj₂) ≡ q) ↠
    (∃ λ (p : ((x : A) → P (inj₁ x)) ×
              ((y : B) → P (inj₂ y))) →
       p ≡ q)
  t₇ = Σ-cong-id-↠ Π⊎↠Π×Π

  _ : _↠_.to t₇ (f , p) ≡ ((f ∘ inj₁ , f ∘ inj₂) , p)
  _ = refl _

  _ : _↠_.from t₇ ((f , g) , p) ≡ ([ f , g ] , p)
  _ = refl _

  _ :
    ∀ {P : A ⊎ B → Type p} {q}
      {p : ∃ λ (p : ((x : A) → P (inj₁ x)) ×
                    ((y : B) → P (inj₂ y))) →
             p ≡ q} →
    _↠_.right-inverse-of (t₇ {P = P}) p ≡ refl p
  _ = refl _

  -- The following proof—implemented using S.Σ-cong—is, at the time of
  -- writing, "worse".

  t₇′ :
    (∃ λ (f : (x : A ⊎ B) → P x) → (f ∘ inj₁ , f ∘ inj₂) ≡ q) ↠
    (∃ λ (p : ((x : A) → P (inj₁ x)) ×
              ((y : B) → P (inj₂ y))) →
       p ≡ q)
  t₇′ = S.Σ-cong Π⊎↠Π×Π λ _ → F.id

  _ : _↠_.to t₇′ (f , p) ≡ ((f ∘ inj₁ , f ∘ inj₂) , p)
  _ = refl _

  _ : _↠_.from t₇′ ((f , g) , p) ≡
      ([ f , g ] , subst (_≡ q) (sym (refl _)) p)
  _ = refl _


open import Auto.Core                  using (Rule; RuleName; _≟-RuleName_; name2rule; IsHintDB)
open import Level                      using (zero)
open import Function                   using (id; _∘_)
open import Category.Functor           using (module RawFunctor)
open import Data.Bool    as Bool       using (if_then_else_)
open import Data.List    as List       using (List; _++_; []; [_])
open import Data.Maybe   as Maybe      using (Maybe; just; nothing)
open import Data.Nat     as Nat        using (ℕ; pred)
open import Data.Product as Prod       using (∃; _,_; proj₂)
open import Data.Sum     as Sum        using (_⊎_; inj₁; inj₂)
open import Data.Unit    as Unit       using (⊤)
open import Reflection                 using (Name)
open import Relation.Nullary.Decidable using (⌊_⌋)

module Auto.Counting where

--------------------------------------------------------------------------------
-- Define a 'counting' hint database which, upon selection of a rule will     --
-- decrement an associated 'count' value, and upon reaching 0 will delete     --
-- the hint from the hint database.                                           --
--                                                                            --
-- The count values can either be natural numbers, in which case they         --
-- will be decremented as expected, or the value ⊤, in which case they        --
-- will not be decremented, effectively inserting infinite copies of the      --
-- rule into the hint database.                                               --
--------------------------------------------------------------------------------

module CountingHintDB where

  open RawFunctor (Maybe.functor {zero}) using (_<$>_)

  Count : Set
  Count = ℕ ⊎ ⊤

  record Hint (k : ℕ) : Set where
    constructor mkHint
    field
      rule  : Rule k
      count : Count

    ruleName : RuleName
    ruleName = Rule.name rule

  HintDB : Set
  HintDB = List (∃ Hint)

  decrCount : ∀ {k} → Hint k → Maybe (Hint k)
  decrCount {k} (mkHint r c) = mkHint r <$> decrCount′ c
    where
      decrCount′ : Count → Maybe Count
      decrCount′ (inj₁ 0) = nothing
      decrCount′ (inj₁ 1) = nothing
      decrCount′ (inj₁ x) = just (inj₁ (pred x))
      decrCount′ (inj₂ _) = just (inj₂ _)

  getTr : ∀ {k} → Hint k → (HintDB → HintDB)
  getTr h₁ = List.concatMap (List.fromMaybe ∘ mdecr₁)
    where
      mdecr₁ : ∃ Hint → Maybe (∃ Hint)
      mdecr₁ (_ , h₂) =
        if ⌊ Hint.ruleName h₁ ≟-RuleName Hint.ruleName h₂ ⌋
        then (_,_ _) <$> decrCount h₂
        else just (_ , h₂)

  countingHintDB : IsHintDB
  countingHintDB = record
    { HintDB   = HintDB
    ; Hint     = Hint
    ; getHints = id
    ; getRule  = Hint.rule
    ; getTr    = getTr
    ; ε        = []
    ; _∙_      = _++_
    ; return   = λ r → [ _ , mkHint r (inj₂ _) ]
    }

open CountingHintDB using (mkHint; countingHintDB)
open import Auto.Extensible countingHintDB public

--------------------------------------------------------------------------------
-- Define some new syntax in order to insert rules with limited usage.        --
--------------------------------------------------------------------------------

infixl 5 _<<[_]_

_<<[_]_ : HintDB → ℕ → Name → HintDB
db <<[ 0 ] _ = db
db <<[ x ] n with (name2rule n)
db <<[ x ] n | inj₁ msg     = db
db <<[ x ] n | inj₂ (k , r) = db ∙ [ k , mkHint r (inj₁ x) ]


-- 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 #-}

open import Data.Empty using (⊥-elim)
open import Data.Unit using (⊤)
open import Data.Product
open import Data.Sum
open import Data.Vec using ([]; _∷_)
open import Data.List as List
import Data.Fin as Fin
open import Data.Nat

open import Relation.Nullary using (¬_; yes; no)
open import Relation.Binary.Definitions
open import Relation.Unary using (_∈_; _∉_; _⊆_; Satisfiable)
open import Relation.Binary.Construct.Closure.ReflexiveTransitive using (ε; _◅_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; cong; inspect)
open import Common

open import is-lib.InfSys

module FairTermination-IS {𝕋 : Set} (message : Message 𝕋) where

  open import SessionType message
  open import Trace message
  open import Transitions message
  open import HasTrace message
  open import TraceSet message

  U : Set
  U = SessionType

  data RuleNames : Set where
    nil inp out : RuleNames

  data CoRuleNames : Set where
    inp out : CoRuleNames

  nil-r : FinMetaRule U
  nil-r .Ctx = ⊤
  nil-r .comp _ =
    [] ,
    ----
    nil

  inp-r : MetaRule U
  inp-r .Ctx = Continuation
  inp-r .Pos _ = 𝕋
  inp-r .prems f p = f p .force
  inp-r .conclu f = inp f

  out-r : MetaRule U
  out-r .Ctx = Continuation
  out-r .Pos _ = 𝕋
  out-r .prems f p = f p .force
  out-r .conclu f = out f

  inp-co-r : FinMetaRule U
  inp-co-r .Ctx = Σ[ (f , x) ∈ Continuation × 𝕋 ] x ∈ dom f
  inp-co-r .comp ((f , x) , _) =
    f x .force ∷ [] ,
    --------------------
    inp f

  out-co-r : FinMetaRule U
  out-co-r .Ctx = Σ[ (f , x) ∈ Continuation × 𝕋 ] x ∈ dom f
  out-co-r .comp ((f , x) , _) =
    f x .force ∷ [] ,
    --------------------
    out f

  WeakTerminationS : SessionType → Set
  WeakTerminationS T = ∀{φ} → φ ∈ ⟦ T ⟧ → ∃[ ψ ] (φ ++ ψ ∈ Maximal ⟦ T ⟧)

  WeakTerminationIS : IS U
  Names WeakTerminationIS = RuleNames
  rules WeakTerminationIS nil    = from nil-r
  rules WeakTerminationIS inp    = inp-r
  rules WeakTerminationIS out    = out-r

  WeakTerminationCOIS : IS U
  WeakTerminationCOIS .Names = CoRuleNames
  WeakTerminationCOIS .rules inp = from inp-co-r
  WeakTerminationCOIS .rules out = from out-co-r

  WeakTermination : SessionType → Set
  WeakTermination = FCoInd⟦ WeakTerminationIS , WeakTerminationCOIS ⟧

  WeakTerminationI : SessionType → Set
  WeakTerminationI = Ind⟦ WeakTerminationIS ∪ WeakTerminationCOIS ⟧

  {- Soundness -}

  lemma-inp : ∀{f φ x} → φ ∈ Maximal ⟦ f x .force ⟧ → I x ∷ φ ∈ Maximal ⟦ inp f ⟧
  lemma-inp (maximal wit F) =
    maximal (inp-has-trace wit) λ { (some le) (_ , def , step inp tr) → cong (_ ∷_) (F le (_ , def , tr))}

  lemma-out : ∀{f φ x} → φ ∈ Maximal ⟦ f x .force ⟧ → O x ∷ φ ∈ Maximal ⟦ out f ⟧
  lemma-out (maximal wit F) =
    maximal (out-has-trace wit) λ { (some le) (_ , def , step (out _) tr) → cong (_ ∷_) (F le (_ , def , tr))}

  lemma-end : ∀{f} → (∀{x} → x ∉ dom f) → [] ∈ Maximal ⟦ inp f ⟧
  lemma-end no-x = maximal (_ , inp , refl)
    λ { {[]} none _ → refl
      ; {O _ ∷ _} none (_ , _ , step () _)
      ; {I _ ∷ _} none (_ , def , step inp tr) → ⊥-elim (no-x (transitions+defined->defined tr def))}

  lemma-win : ∀{f} → (∀{x} → x ∉ dom f) → [] ∈ Maximal ⟦ out f ⟧
  lemma-win no-x = maximal (_ , out , refl)
    λ { {[]} none _ → refl
      ; {I _ ∷ _} none (_ , _ , step () _)
      ; {O _ ∷ _} none (_ , def , step (out ok) _) → ⊥-elim (no-x ok)}

  may-terminate : ∀{T} → Defined T → WeakTerminationI T → Satisfiable (Maximal ⟦ T ⟧)
  may-terminate _ (fold (inj₁ inp , f , refl , pr)) with Empty? f
  ... | inj₁ e = _ , lemma-end λ x → e _ x
  ... | inj₂ (p , def) =
    let tr , max = may-terminate def (pr p) in
    I p ∷ tr , lemma-inp max
  may-terminate _ (fold (inj₁ out , f , refl , pr)) with Empty? f
  ... | inj₁ e = _ , lemma-win λ x → e _ x
  ... | inj₂ (p , def) =
    let tr , max = may-terminate def (pr p) in
    O p ∷ tr , lemma-out max
  may-terminate _ (fold (inj₂ inp , ((_ , x) , def) , refl , pr)) =
    let tr , max = may-terminate def (pr Fin.zero) in
    I x ∷ tr , lemma-inp max
  may-terminate _ (fold (inj₂ out , ((_ , x) , def) , refl , pr)) =
    let tr , max = may-terminate def (pr Fin.zero) in
    O x ∷ tr , lemma-out max

  wt-sound : WeakTermination ⊆ WeakTerminationS
  wt-sound wt (_ , def , refl) = may-terminate def (fcoind-to-ind wt)
  wt-sound wt (_ , def , step t tr) with wt .CoInd⟦_⟧.unfold
  wt-sound wt (_ , def , step (inp {_} {p}) tr) | inp , _ , refl , pr =
    let tr' , max = wt-sound (pr p) (_ , def , tr) in
    tr' , lemma-inp max
  wt-sound wt (_ , def , step (out {_} {p} fx) tr) | out , _ , refl , pr =
    let tr' , max = wt-sound (pr p) (_ , def , tr) in
    tr' , lemma-out max

  {- Boundedness -}

  undefined→terminates : ∀{T} → ¬ Defined T → WeakTerminationI T
  undefined→terminates {nil}   _   = apply-ind (inj₁ nil) _ λ ()
  undefined→terminates {inp f} und = ⊥-elim (und inp)
  undefined→terminates {out f} und = ⊥-elim (und out)

  input-maximal→terminates : ∀{f x} → [] ∈ Maximal ⟦ inp f ⟧ → WeakTerminationI (f x .force)
  input-maximal→terminates {f} {x} (maximal (_ , inp , refl) F) with x ∈? f
  ... | no nfx = undefined→terminates nfx
  ... | yes fx with F none (_ , fx , step inp refl)
  ... | ()

  output-maximal→terminates : ∀{f x} → [] ∈ Maximal ⟦ out f ⟧ → WeakTerminationI (f x .force)
  output-maximal→terminates {f} {x} (maximal (_ , out , refl) F) with x ∈? f
  ... | no nfx = undefined→terminates nfx
  ... | yes fx with F none (_ , fx , step (out fx) refl)
  ... | ()

  bounded-lemma : ∀{T ϕ} → ϕ ∈ Maximal ⟦ T ⟧ → WeakTerminationI T
  bounded-lemma {nil} (maximal _ _) = apply-ind (inj₁ nil) _ λ ()
  bounded-lemma {inp f} c[]@(maximal (_ , def , refl) F) =
    apply-ind (inj₁ inp) _ λ p → input-maximal→terminates {f} {p} c[]
  bounded-lemma {inp f} cϕ@(maximal (_ , def , step inp tr) F) =
    apply-ind (inj₂ inp) (_ , (transitions+defined->defined tr def)) λ{Fin.zero → bounded-lemma (input-maximal cϕ)}
  bounded-lemma {out f} c[]@(maximal (_ , def , refl) F) =
    apply-ind (inj₁ out) _ λ p → output-maximal→terminates {f} {p} c[]
  bounded-lemma {out f} cϕ@(maximal (_ , def , step (out x) tr) F) =
    apply-ind (inj₂ out) (_ , x) λ{Fin.zero → bounded-lemma (output-maximal cϕ)}

  wtS-bounded : WeakTerminationS ⊆ WeakTerminationI
  wtS-bounded {nil} _ = apply-ind (inj₁ nil) _ λ ()
  wtS-bounded {inp f} s with s (_ , inp , refl)
  ... | _ , max = bounded-lemma max
  wtS-bounded {out f} s with s (_ , out , refl)
  ... | _ , max = bounded-lemma max

  {- Consistency -}

  lemma-input-maximal : ∀{f x} → WeakTerminationS (inp f) → WeakTerminationS (f x .force)
  lemma-input-maximal {f} {x} spec tφ with x ∈? f
  ... | no nfx = ⊥-elim (nfx (has-trace->defined tφ))
  ... | yes fx with spec (inp-has-trace tφ)
  ... | _ , cψ = _ , input-maximal cψ

  lemma-output-maximal : ∀{f x} → WeakTerminationS (out f) → WeakTerminationS (f x .force)
  lemma-output-maximal {f} {x} spec tφ with x ∈? f
  ... | no nfx = ⊥-elim (nfx (has-trace->defined tφ))
  ... | yes fx with spec (out-has-trace tφ)
  ... | _ , cψ = _ , output-maximal cψ

  wtS-cons : WeakTerminationS ⊆ ISF[ WeakTerminationIS ] WeakTerminationS
  wtS-cons {nil} spec = nil , _ , refl , λ ()
  wtS-cons {inp f} spec =
    inp , f , refl , λ p → lemma-input-maximal {f} {p} spec
  wtS-cons {out f} spec =
    out , f , refl , λ p → lemma-output-maximal {f} {p} spec

  {- Completeness -}

  wt-complete : WeakTerminationS ⊆ WeakTermination
  wt-complete =
    bounded-coind[ WeakTerminationIS , WeakTerminationCOIS ] WeakTerminationS wtS-bounded wtS-cons


{-# OPTIONS --safe #-}

module Definition.Typed.Consequences.Inequality where

open import Definition.Untyped hiding (U≢ℕ; U≢Π; U≢ne; ℕ≢Π; ℕ≢ne; Π≢ne; U≢Empty; ℕ≢Empty; Empty≢Π; Empty≢ne)
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.EqRelInstance
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Fundamental.Reducibility
open import Definition.Typed.Consequences.Syntactic

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

A≢B : ∀ {A B rA rB Γ} (_⊩′⟨_⟩A_ _⊩′⟨_⟩B_ : Con Term → TypeLevel → Term → Set)
      (A-intr : ∀ {l} → Γ ⊩′⟨ l ⟩A A → Γ ⊩⟨ l ⟩ A ^ rA)
      (B-intr : ∀ {l} → Γ ⊩′⟨ l ⟩B B → Γ ⊩⟨ l ⟩ B ^ rB)
      (A-elim : ∀ {l} → Γ ⊩⟨ l ⟩ A ^ rA → ∃ λ l′ → Γ ⊩′⟨ l′ ⟩A A)
      (B-elim : ∀ {l} → Γ ⊩⟨ l ⟩ B ^ rA → ∃ λ l′ → Γ ⊩′⟨ l′ ⟩B B)
      (A≢B′ : ∀ {l l′} ([A] : Γ ⊩′⟨ l ⟩A A) ([B] : Γ ⊩′⟨ l′ ⟩B B)
            → ShapeView Γ l l′ A B rA rB (A-intr [A]) (B-intr [B]) → ⊥)
    → Γ ⊢ A ≡ B ^ rA → ⊥
A≢B {A} {B} _ _ A-intr B-intr A-elim B-elim A≢B′ A≡B =
  let X = reducibleEq A≡B
      [A] = proj₁ X
      [B] = proj₁ (proj₂ X)
      [A≡B] = proj₂ (proj₂ X)
      _ , [A]′ = A-elim ([A])
      _ , [B]′ = B-elim ([B])
      [A≡B]′ = irrelevanceEq [A] (A-intr [A]′) [A≡B]
  in  A≢B′ [A]′ [B]′ (goodCases (A-intr [A]′) (B-intr [B]′) [A≡B]′)

U≢ℕ′ : ∀ {Γ A ll B l l′}
       ([U] : Γ ⊩′⟨ l ⟩U A ^ ll)
       ([ℕ] : Γ ⊩ℕ B)
     → ShapeView Γ l l′ _ _ [ ! , _ ] [ ! , _ ] (Uᵣ {ll = ll} [U]) (ℕᵣ [ℕ]) → ⊥
U≢ℕ′ a b ()

U≢ℕ-red : ∀ {ll r lU B Γ} → Γ ⊢ B ⇒* ℕ ^ [ ! , ll ] → Γ ⊢ Univ r lU ≡ B ^ [ ! , ll ] → ⊥
U≢ℕ-red {ll} D = A≢B (λ Γ l A → Γ ⊩′⟨ l ⟩U A ^ ll) (λ Γ l B → Γ ⊩ℕ B) Uᵣ ℕᵣ
                (λ x → extractMaybeEmb (U-elim x))
                (λ x → extractMaybeEmb (ℕ-elim′ D x))
                U≢ℕ′

-- U and ℕ cannot be judgmentally equal.
U≢ℕ! : ∀ {r l ll Γ} → Γ ⊢ Univ r l ≡ ℕ ^ [ ! , ll ] → ⊥
U≢ℕ! U≡ℕ =
  let _ , ⊢ℕ = syntacticEq U≡ℕ
  in  U≢ℕ-red (id ⊢ℕ) U≡ℕ

-- U vs Pi
U≢Π′ : ∀ {A lA B rB lB Γ l l′}
       ([U] : Γ ⊩′⟨ l ⟩U A ^ lA)
       ([Π] : Γ ⊩′⟨ l′ ⟩Π B ^[ rB , lB ])
     → ShapeView Γ l l′ _ _ _ _ (Uᵣ {ll = lA} [U]) (Πᵣ [Π]) → ⊥
U≢Π′ a b ()

U≢Π-red : ∀ {ll B F G rF lF lG rU lU Γ} → Γ ⊢ B ⇒* Π F ^ rF ° lF ▹ G ° lG ° ll ^ [ ! , ι ll ]
            → Γ ⊢ Univ rU lU ≡ B ^ [ ! , ι ll ] → ⊥
U≢Π-red {ll} D = A≢B (λ Γ l A → Γ ⊩′⟨ l ⟩U A ^ ι ll)
                (λ Γ l A → Γ ⊩′⟨ l ⟩Π A ^[ ! , ll ]) Uᵣ Πᵣ
                (λ x → extractMaybeEmb (U-elim x))
                (λ x → extractMaybeEmb (Π-elim′ D x))
                U≢Π′

-- U and Π F ▹ G for any F and G cannot be judgmentally equal.
U≢Π! : ∀ {rU lU F rF lF lG G l Γ} → Γ ⊢ Univ rU lU ≡ Π F ^ rF ° lF ▹ G  ° lG ° l ^ [ ! , ι l ] → ⊥
U≢Π! U≡Π =
  let _ , ⊢Π = syntacticEq U≡Π
  in  U≢Π-red (id ⊢Π) U≡Π

-- ∃ vs Pi

∃≢Π′ : ∀ {A lA B rB lB Γ l l′}
       ([∃] : Γ ⊩′⟨ l ⟩∃ A ^ lA)
       ([Π] : Γ ⊩′⟨ l′ ⟩Π B ^[ rB , lB ])
     → ShapeView Γ l l′ _ _ _ _ (∃ᵣ [∃]) (Πᵣ [Π]) → ⊥
∃≢Π′ a b ()

∃≢Π-red : ∀ {ll B F G rF lF lG P Q Γ} → Γ ⊢ B ⇒* Π F ^ rF ° lF ▹ G ° lG ° ll ^ [ % , ι ll ]
            → Γ ⊢ ∃ P ▹ Q ≡ B ^ [ % , ι ll ] → ⊥
∃≢Π-red {ll} D = A≢B (λ Γ l A → Γ ⊩′⟨ l ⟩∃ A ^ ll)
                (λ Γ l A → Γ ⊩′⟨ l ⟩Π A ^[ % , ll ]) ∃ᵣ Πᵣ
                (λ x → extractMaybeEmb (∃-elim x))
                (λ x → extractMaybeEmb (Π-elim′ D x))
                ∃≢Π′

-- U and Π F ▹ G for any F and G cannot be judgmentally equal.
∃≢Π! : ∀ {P Q F rF lF lG G l Γ} → Γ ⊢ ∃ P ▹ Q ≡ Π F ^ rF ° lF ▹ G  ° lG ° l ^ [ % , ι l ] → ⊥
∃≢Π! ∃≡Π =
  let _ , ⊢Π = syntacticEq ∃≡Π
  in ∃≢Π-red (id ⊢Π) ∃≡Π

U≢ne′ : ∀ {A lU r lK K Γ l l′}
       ([U] : Γ ⊩′⟨ l ⟩U A ^ lU)
       ([K] : Γ ⊩ne K ^[ r , lK ] )
     → ShapeView Γ l l′ _ _ _ _ (Uᵣ {ll = lU} [U]) (ne [K]) → ⊥
U≢ne′ a b ()

U≢ne-red : ∀ {ll rU lU B K Γ} → Γ ⊢ B ⇒* K ^ [ ! , ι ll ] → Neutral K → Γ ⊢ Univ rU lU ≡ B ^ [ ! , ι ll ] → ⊥
U≢ne-red {ll} D neK = A≢B (λ Γ l A → Γ ⊩′⟨ l ⟩U A ^ ι ll) (λ Γ l B → Γ ⊩ne B ^[ ! , ll ]) Uᵣ ne
                     (λ x → extractMaybeEmb (U-elim x))
                     (λ x → extractMaybeEmb (ne-elim′ D neK x PE.refl))
                     U≢ne′

-- U and K for any neutral K cannot be judgmentally equal.
U≢ne! : ∀ {r l ll K Γ} → Neutral K → Γ ⊢ Univ r l ≡ K ^ [ ! , ι ll ] → ⊥
U≢ne! neK U≡K =
  let _ , ⊢K = syntacticEq U≡K
  in  U≢ne-red (id ⊢K) neK U≡K

ℕ≢Π′ : ∀ {A B Γ ll l l′}
       ([ℕ] : Γ ⊩ℕ A)
       ([Π] : Γ ⊩′⟨ l′ ⟩Π B ^[ ! , ll ])
     → ShapeView Γ l l′ _ _ _ _ (ℕᵣ [ℕ]) (Πᵣ [Π]) → ⊥
ℕ≢Π′ a b ()

ℕ≢Π-red : ∀ {A B F rF lF lG G Γ} → Γ ⊢ A ⇒* ℕ ^ [ ! , ι ⁰ ] → Γ ⊢ B ⇒* Π F ^ rF ° lF ▹ G ° lG ° ⁰ ^ [ ! , ι ⁰ ] → Γ ⊢ A ≡ B ^ [ ! , ι ⁰ ] → ⊥
ℕ≢Π-red D D′ = A≢B (λ Γ l A → Γ ⊩ℕ A)
                   (λ Γ l A → Γ ⊩′⟨ l ⟩Π A ^[ ! , ⁰ ]) ℕᵣ Πᵣ
                   (λ x → extractMaybeEmb (ℕ-elim′ D x))
                   (λ x → extractMaybeEmb (Π-elim′ D′ x))
                   ℕ≢Π′

-- ℕ and Π F ▹ G for any F and G cannot be judgmentally equal.
ℕ≢Π! : ∀ {F rF G lF lG  Γ} → Γ ⊢ ℕ ≡ Π F ^ rF ° lF ▹ G ° lG ° ⁰ ^ [ ! , ι ⁰ ]  → ⊥
ℕ≢Π! ℕ≡Π =
  let ⊢ℕ , ⊢Π = syntacticEq ℕ≡Π
  in  ℕ≢Π-red (id ⊢ℕ) (id ⊢Π) ℕ≡Π

-- Empty and Π
Empty≢Π′ : ∀ {ll A B Γ l l′}
       ([Empty] : Γ ⊩Empty A ^ ll)
       ([Π] : Γ ⊩′⟨ l′ ⟩Π B ^[ % , ll ])
     → ShapeView Γ l l′ _ _ _ _ (Emptyᵣ [Empty]) (Πᵣ [Π]) → ⊥
Empty≢Π′ a b ()

Empty≢Π-red : ∀ {ll A B F rF lF lG G Γ} → Γ ⊢ A ⇒* Empty ll ^ [ % , ι ll ] → Γ ⊢ B ⇒* Π F ^ rF ° lF ▹ G ° lG ° ll ^ [ % , ι ll ]
                → Γ ⊢ A ≡ B ^ [ % , ι ll ] → ⊥
Empty≢Π-red {ll} D D′ = A≢B (λ Γ l A → Γ ⊩Empty A ^ ll)
                   (λ Γ l A → Γ ⊩′⟨ l ⟩Π A ^[ % , ll ]) Emptyᵣ Πᵣ
                   (λ x → extractMaybeEmb (Empty-elim′ D x))
                   (λ x → extractMaybeEmb (Π-elim′ D′ x))
                   Empty≢Π′

Empty≢Π% : ∀ {F rF lF lG l G Γ} → Γ ⊢ Empty l ≡ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ % , ι l ] → ⊥
Empty≢Π% Empty≡Π =
  let ⊢Empty , ⊢Π = syntacticEq Empty≡Π
  in  Empty≢Π-red (id ⊢Empty) (id ⊢Π) Empty≡Π

ℕ≢ne′ : ∀ {ll A K Γ l l′}
       ([ℕ] : Γ ⊩ℕ A)
       ([K] : Γ ⊩ne K ^[ ! , ll ])
     → ShapeView Γ l l′ _ _ _ _ (ℕᵣ [ℕ]) (ne [K]) → ⊥
ℕ≢ne′ a b ()

ℕ≢ne-red : ∀ {A B K Γ} → Γ ⊢ A ⇒* ℕ ^ [ ! , ι ⁰ ] → Γ ⊢ B ⇒* K ^ [ ! , ι ⁰ ] → Neutral K → Γ ⊢ A ≡ B ^ [ ! , ι ⁰ ] → ⊥
ℕ≢ne-red D D′ neK = A≢B (λ Γ l A → Γ ⊩ℕ A) (λ Γ l B → Γ ⊩ne B ^[ ! , ⁰ ]) ℕᵣ ne
                        (λ x → extractMaybeEmb (ℕ-elim′ D x))
                        (λ x → extractMaybeEmb (ne-elim′ D′ neK x PE.refl ))
                        ℕ≢ne′

-- ℕ and K for any neutral K cannot be judgmentally equal.
ℕ≢ne! : ∀ {K Γ} → Neutral K → Γ ⊢ ℕ ≡ K ^ [ ! , ι ⁰ ] → ⊥
ℕ≢ne! neK ℕ≡K =
  let ⊢ℕ , ⊢K = syntacticEq ℕ≡K
  in  ℕ≢ne-red (id ⊢ℕ) (id ⊢K) neK ℕ≡K

-- Empty and neutral
Empty≢ne′ : ∀ {ll A K Γ l l′}
       ([Empty] : Γ ⊩Empty A ^ ll)
       ([K] : Γ ⊩ne K ^[ % , ll ])
     → ShapeView Γ l l′ _ _ _ _ (Emptyᵣ [Empty]) (ne [K]) → ⊥
Empty≢ne′ a b ()

Empty≢ne-red : ∀ {ll A B K Γ} → Γ ⊢ A ⇒* Empty ll ^ [ % , ι ll ] → Γ ⊢ B ⇒* K ^ [ % , ι ll ] →
                                Neutral K → Γ ⊢ A ≡ B ^ [ % , ι ll ] → ⊥
Empty≢ne-red {ll} D D′ neK = A≢B (λ Γ l A → Γ ⊩Empty A ^ ll) (λ Γ l B → Γ ⊩ne B ^[ % , ll ]) Emptyᵣ ne
                        (λ x → extractMaybeEmb (Empty-elim′ D x))
                        (λ x → extractMaybeEmb (ne-elim′ D′ neK x PE.refl))
                        Empty≢ne′

Empty≢ne% : ∀ {l K Γ} → Neutral K → Γ ⊢ Empty l ≡ K ^ [ % , ι l ]  → ⊥
Empty≢ne% neK Empty≡K =
  let ⊢Empty , ⊢K = syntacticEq Empty≡K
  in  Empty≢ne-red (id ⊢Empty) (id ⊢K) neK Empty≡K

Π≢ne′ : ∀ {ll A K r Γ l l′}
       ([Π] : Γ ⊩′⟨ l ⟩Π A ^[ r , ll ])
       ([K] : Γ ⊩ne K ^[ r , ll ])
     → ShapeView Γ l l′ _ _ _ _ (Πᵣ [Π]) (ne [K]) → ⊥
Π≢ne′ a b ()

Π≢ne-red : ∀ {ll r A B F rF lF lG G K Γ} → Γ ⊢ A ⇒* Π F ^ rF ° lF ▹ G ° lG ° ll ^ [ r , ι ll ]
                                         → Γ ⊢ B ⇒* K ^ [ r , ι ll ] → Neutral K
                                         → Γ ⊢ A ≡ B ^ [ r , ι ll ] → ⊥
Π≢ne-red {ll} {r} D D′ neK = A≢B (λ Γ l A → Γ ⊩′⟨ l ⟩Π A ^[ r , ll ])
                        (λ Γ l B → Γ ⊩ne B ^[ r , ll ]) Πᵣ ne
                        (λ x → extractMaybeEmb (Π-elim′ D x))
                        (λ x → extractMaybeEmb (ne-elim′ D′ neK x PE.refl))
                        Π≢ne′

-- Π F ▹ G and K for any F and G and neutral K cannot be judgmentally equal.
Π≢ne : ∀ {F rF lF lG G K r l Γ} → Neutral K → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° l ≡ K ^ [ r , ι l ] → ⊥
Π≢ne neK Π≡K =
  let ⊢Π , ⊢K = syntacticEq Π≡K
  in  Π≢ne-red (id ⊢Π) (id ⊢K) neK Π≡K


open import Agda.Primitive using (lzero; lsuc; _⊔_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; trans; cong; cong₂; subst; setoid)
open import Data.Product using (_×_; Σ; _,_; proj₁; proj₂; zip; map; <_,_>; swap)
import Function.Equality
open import Relation.Binary using (Setoid)
import Relation.Binary.Reasoning.Setoid as SetoidR

import SecondOrder.Arity
import SecondOrder.Signature
import SecondOrder.Metavariable
import SecondOrder.VRenaming
import SecondOrder.MRenaming
import SecondOrder.Term
import SecondOrder.Substitution
import SecondOrder.RelativeMonadMorphism
import SecondOrder.Instantiation
import SecondOrder.IndexedCategory
import SecondOrder.RelativeKleisli
import SecondOrder.Mslot
import SecondOrder.MRelativeMonad
import SecondOrder.VRelativeMonad

module SecondOrder.MRelMonMorphism
  {ℓ}
  {𝔸 : SecondOrder.Arity.Arity}
  (Σ : SecondOrder.Signature.Signature ℓ 𝔸)
  where

  open SecondOrder.Signature.Signature Σ
  open SecondOrder.Metavariable Σ
  open SecondOrder.Term Σ
  open SecondOrder.VRenaming Σ
  open SecondOrder.MRenaming Σ
  open SecondOrder.Mslot Σ
  open SecondOrder.Substitution Σ
  open SecondOrder.Instantiation Σ
  open SecondOrder.MRelativeMonad Σ
  open SecondOrder.VRelativeMonad Σ
  open SecondOrder.RelativeMonadMorphism

  module MRelMonMorph {Γ Γ′ : VContext} where
    open MTerm {Γ} renaming (MMonad to M_Γ)
    open MTerm {Γ′} renaming (MMonad to M_Γ′)

    Fᴹ : ∀ (ρ : Γ ⇒ᵛ Γ′) →  RMonadMorph (M_Γ) (M_Γ′)
    Fᴹ ρ = record
            { morph = λ Γᴹ Aᴹ →
                        record
                          { _⟨$⟩_ = [ ⇑ᵛ ρ ]ᵛ_
                          ; cong =  λ M≈N → []ᵛ-resp-≈ M≈N
                          }
            ; law-unit =  λ Γᴹ Aᴹ M≡N → ≈-≡
                                          (I-resp-≡
                                            {I = λ M → tm-meta M λ i → tm-var (var-inr i) }
                                            M≡N)
            ; law-extend = λ {Θ} {Ω} {k} Γᴹ Aᴹ {s} {t} s≈t →
                                       ≈-trans
                                           ([ᵛ∘ⁱ] s)
                                           (≈-trans
                                             ([]ⁱ-resp-≈
                                               (⇑ᵛ ρ ᵛ∘ⁱ ⇑ⁱ (λ {Λ} {B} →
                                                   Function.Equality._⟨$⟩_ (k Λ B)))
                                               ([]ᵛ-resp-≈ s≈t))
                                             ([]ⁱ-resp-≈ⁱ ([ (⇑ᵛ ρ) ]ᵛ t) λ M →
                                               (≈-trans
                                                 (≈-sym [∘ᵛ])
                                                 (≈-trans
                                                   ([]ᵛ-resp-≡ᵛ extend-aux)
                                                   [∘ᵛ]))))
            }

           where
             extend-aux : ∀ {Ξ Λ} → ⇑ᵛ {Ξ = Ξ} (⇑ᵛ {Ξ = Λ} ρ) ∘ᵛ (⇑ᵛ var-inl) ≡ᵛ ⇑ᵛ var-inl ∘ᵛ (⇑ᵛ ρ)
             extend-aux (var-inl x) = refl
             extend-aux (var-inr x) = refl

  -- Attempt with substitutions instead of renamings : not sure it works
    -- hat : (f : (Θ : MContext) → Θ ⊕ Γ ⇒ˢ Γ') → RMonadMorph M_Γ M_Γ'
    -- hat f =
    --   let open Function.Equality using (_⟨$⟩_) renaming (cong to func-cong) in
    --   record
    --   { morph = λ {Θ} Δ A → record { _⟨$⟩_ = λ t → [ ⇑ˢ (f Θ) ]ˢ t ; cong = []ˢ-resp-≈ (⇑ˢ (f Θ))}
    --   ; law-unit = λ Δ A {t} {s} t≡s → ≈-≡ (cong tm-meta-generic t≡s)
    --   ; law-extend = λ {Θ} {Ψ} {I} Δ A {t} {s} t≈s →
    --       let open SetoidR (Term-setoid Ψ (Γ' ,, Δ) A) in
    --       begin
    --       ([ ⇑ˢ (f Ψ) ]ˢ ([ ⇑ⁱ (λ {Λ} {B} M →  (I Λ B ⟨$⟩ M) ) ]ⁱ t) )
    --         ≈⟨ {!!} ⟩ -- this is the crucial step
    --       ([ ⇑ⁱ (λ {Λ} {B} M → [ ⇑ˢ (f Ψ) ]ˢ (I Λ B ⟨$⟩ M)) ]ⁱ ([ ⇑ˢ (f Θ) ]ˢ t))
    --         ≈⟨ []ⁱ-resp-≈ (⇑ⁱ (λ {Λ} {B} M → [ ⇑ˢ (f Ψ) ]ˢ (I Λ B ⟨$⟩ M))) ([]ˢ-resp-≈ (⇑ˢ (f Θ)) t≈s) ⟩
    --       ([ ⇑ⁱ (λ {Λ} {B} M → [ ⇑ˢ (f Ψ) ]ˢ (I Λ B ⟨$⟩ M)) ]ⁱ ([ ⇑ˢ (f Θ) ]ˢ s))
    --       ∎
    --   }


open import SOAS.Metatheory.Syntax

-- Unit law of metasubstitution
module SOAS.Metatheory.SecondOrder.Unit {T : Set}(Syn : Syntax {T}) where

open Syntax Syn

open import SOAS.Metatheory.FreeMonoid Syn

open import SOAS.Metatheory.SecondOrder.Metasubstitution Syn

open import SOAS.Families.Core {T}
open import SOAS.Common
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Construction.Structure as Structure
open import SOAS.ContextMaps.Combinators
open import SOAS.ContextMaps.CategoryOfRenamings {T}
import SOAS.Abstract.Coalgebra {T} as →□
open →□.Sorted
open →□.Unsorted using (⊤ᵇ) renaming (Coalg to UCoalg ; Coalg⇒ to UCoalg⇒)
import SOAS.Abstract.Box {T} as □ ; open □.Sorted
open import SOAS.Abstract.Monoid
open import SOAS.Abstract.ExpStrength

open import SOAS.Coalgebraic.Monoid

open import SOAS.Metatheory Syn
open import SOAS.Metatheory.Monoid ⅀F ⅀:Str
open import SOAS.ContextMaps.Properties

open Theory

private
  variable
    Γ Δ Π : Ctx
    α β : T
    𝔛 𝔜 ℨ : Familyₛ

-- Metasubstitution unit is a coalgebra homomorphisem from ⊤
ms-unitᵇ⇒ : UCoalg⇒ ⊤ᵇ [ 𝔛 ⊸ (𝕋ᵇ 𝔛) ]ᵇ (λ _ → ms-unit)
ms-unitᵇ⇒ {𝔛} = record
  { ⟨r⟩ = λ{ {Γ = Γ}{Δ}{ρ} → iext (dext λ {Π} 𝔪 → sym (begin
        𝕣𝕖𝕟 𝔛 (ms-unit 𝔪) (Π ∔∣ ρ)
    ≡⟨ Renaming.𝕥⟨𝕞⟩ 𝔛 ⟩
        𝕞𝕧𝕒𝕣 𝔛 𝔪 (λ p → 𝕣𝕖𝕟 𝔛 (𝕧𝕒𝕣 𝔛 (inl Π p)) (Π ∔∣ ρ))
    ≡⟨ 𝕞≈₂ 𝔛 (λ v → Renaming.𝕥⟨𝕧⟩ 𝔛) ⟩
        𝕞𝕧𝕒𝕣 𝔛 𝔪 (λ v → 𝕧𝕒𝕣 𝔛 ((Π ∔∣ ρ) (inl Π v)))
    ≡⟨ 𝕞≈₂ 𝔛 (λ v → cong (𝕧𝕒𝕣 𝔛) (∔.+₁∘i₁ {f = id′ᵣ Π}{g = ρ})) ⟩
        𝕞𝕧𝕒𝕣 𝔛 𝔪 (λ v → 𝕧𝕒𝕣 𝔛 (∔.i₁ v))
    ∎))}
  } where open ≡-Reasoning

-- Right unit of metasubstitution
□msub-runitᵃ⇒ : MetaAlg⇒  𝔛 (𝕋ᵃ 𝔛) (□ᵃ 𝔛 (𝕋ᵃ 𝔛)) λ t ρ → □msub t ρ ms-unit
□msub-runitᵃ⇒ {𝔛} = record
  { ⟨𝑎𝑙𝑔⟩ = λ{ {t = t} → dext λ ρ → begin
        □msub (𝕒𝕝𝕘 𝔛 t) ρ ms-unit
    ≡⟨ cong (λ - → - ms-unit) □MS.𝕥⟨𝕒⟩ ⟩
        𝕒𝕝𝕘 𝔛 (□estr [ 𝔛 ⊸ 𝕋ᵇ 𝔛 ]ᵇ (𝕋 𝔛) (⅀₁ □msub t) ρ ms-unit)
    ≡⟨ cong (𝕒𝕝𝕘 𝔛) (estr-unit′ ms-unitᵇ⇒) ⟩
        𝕒𝕝𝕘 𝔛 (⅀₁ (λ e′ → e′ ms-unit) (str ℐᴮ ⟅ 𝔛 ⇨ 𝕋 𝔛 ⟆ (⅀₁ □msub t) ρ))
    ≡˘⟨ cong (𝕒𝕝𝕘 𝔛) (str-nat₂ ((λ e′ → e′ ms-unit)) (⅀₁ □msub t) ρ) ⟩
        𝕒𝕝𝕘 𝔛 (str ℐᴮ (𝕋 𝔛) (⅀₁ (λ { h′ ς → h′ ς ms-unit }) (⅀₁ □msub t)) ρ)
    ≡˘⟨ congr ⅀.homomorphism (λ - → 𝕒𝕝𝕘 𝔛 (str ℐᴮ (𝕋 𝔛) - ρ)) ⟩
        𝕒𝕝𝕘 𝔛 (str ℐᴮ (𝕋 𝔛) (⅀₁ (λ{ t ρ → □msub t ρ ms-unit}) t) ρ)
    ∎ }
  ; ⟨𝑣𝑎𝑟⟩ = λ{ {v = v} → dext λ ρ → cong (λ - → - ms-unit) □MS.𝕥⟨𝕧⟩}
  ; ⟨𝑚𝑣𝑎𝑟⟩ = λ{ {𝔪 = 𝔪}{ε} → dext λ ρ → begin
        □msub (𝕞𝕧𝕒𝕣 𝔛 𝔪 ε) ρ ms-unit
    ≡⟨ cong (λ - → - ms-unit) □MS.𝕥⟨𝕞⟩ ⟩
        𝕤𝕦𝕓 𝔛 (ms-unit 𝔪) (copair (𝕋 𝔛) (λ v → □msub (ε v) ρ ms-unit) (𝕧𝕒𝕣 𝔛))
    ≡⟨ Substitution.𝕥⟨𝕞⟩ 𝔛 ⟩
        𝕞𝕧𝕒𝕣 𝔛 𝔪 (λ v → 𝕤𝕦𝕓 𝔛 (𝕧𝕒𝕣 𝔛 (∔.i₁ v)) (copair (𝕋 𝔛) (λ v → □msub (ε v) ρ ms-unit) (𝕧𝕒𝕣 𝔛)))
    ≡⟨ 𝕞≈₂ 𝔛 (λ v → begin
              𝕤𝕦𝕓 𝔛 (𝕧𝕒𝕣 𝔛 (∔.i₁ v)) (copair (𝕋 𝔛) (λ 𝔫 → □msub (ε 𝔫) ρ ms-unit) (𝕧𝕒𝕣 𝔛))
          ≡⟨ Mon.lunit (𝕋ᵐ 𝔛) ⟩
              copair (𝕋 𝔛) (λ 𝔫 → □msub (ε 𝔫) ρ ms-unit) (𝕧𝕒𝕣 𝔛) (∔.i₁ v)
          ≡⟨ copair∘i₁ (𝕋 𝔛) v ⟩
              □msub (ε v) ρ ms-unit
          ∎) ⟩
        𝕞𝕧𝕒𝕣 𝔛 𝔪 (λ v → □msub (ε v) ρ ms-unit)
    ∎ }
  } where open ≡-Reasoning

□msub-runit : (t : 𝕋 𝔛 α Γ)(ρ : Γ ↝ Δ)
           → □msub t ρ ms-unit ≡ 𝕣𝕖𝕟 𝔛 t ρ
□msub-runit {𝔛} t ρ = sym (cong (λ - → - ρ) (Renaming.𝕤𝕖𝕞! 𝔛 □msub-runitᵃ⇒ t))


------------------------------------------------------------------------
-- A map function for the substitutions
------------------------------------------------------------------------

open import Data.Universe.Indexed

module deBruijn.Substitution.Data.Map
  {i u e} {Uni : IndexedUniverse i u e} where

import deBruijn.Context; open deBruijn.Context Uni
open import deBruijn.Substitution.Data.Basics
open import Function using (_$_)
import Relation.Binary.PropositionalEquality as P

open P.≡-Reasoning

private
 module Dummy
   {t₁} {T₁ : Term-like t₁}
   {t₂} {T₂ : Term-like t₂}
   where

  open Term-like T₁ using ()
                    renaming (_⊢_ to _⊢₁_; _≅-⊢_ to _≅-⊢₁_; [_] to [_]₁)
  open Term-like T₂ using () renaming (_≅-⊢_ to _≅-⊢₂_; [_] to [_]₂)

  -- Map.

  map : ∀ {Γ Δ Ε} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε} →
        [ T₁ ⟶ T₂ ] ρ̂₂ → Sub T₁ ρ̂₁ → Sub T₂ (ρ̂₁ ∘̂ ρ̂₂)
  map           f ε        = ε
  map {ρ̂₂ = ρ̂₂} f (ρ₁ ▻ t) =
    P.subst (λ v → Sub T₂ (⟦ ρ₁ ⟧⇨ ∘̂ ρ̂₂ ▻̂ v))
            (≅-Value-⇒-≡ $ P.sym $ corresponds f t)
            (map f ρ₁ ▻ f · t)

  abstract

    -- An unfolding lemma.

    map-▻ :
      ∀ {Γ Δ Ε} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε} {σ}
      (f : [ T₁ ⟶ T₂ ] ρ̂₂) (ρ : Sub T₁ ρ̂₁) t →
      map f (ρ ▻⇨[ σ ] t) ≅-⇨ map f ρ ▻⇨[ σ ] f · t
    map-▻ {ρ̂₂ = ρ̂₂} f ρ t =
      drop-subst-Sub (λ v → ⟦ ρ ⟧⇨ ∘̂ ρ̂₂ ▻̂ v)
                     (≅-Value-⇒-≡ $ P.sym $ corresponds f t)

    -- A congruence lemma.

    map-cong : ∀ {Γ₁ Δ₁ Ε₁} {ρ̂₁₁ : Γ₁ ⇨̂ Δ₁} {ρ̂₂₁ : Δ₁ ⇨̂ Ε₁}
                 {f₁ : [ T₁ ⟶ T₂ ] ρ̂₂₁} {ρ₁ : Sub T₁ ρ̂₁₁}
                 {Γ₂ Δ₂ Ε₂} {ρ̂₁₂ : Γ₂ ⇨̂ Δ₂} {ρ̂₂₂ : Δ₂ ⇨̂ Ε₂}
                 {f₂ : [ T₁ ⟶ T₂ ] ρ̂₂₂} {ρ₂ : Sub T₁ ρ̂₁₂} →
               f₁ ≅-⟶ f₂ → ρ₁ ≅-⇨ ρ₂ → map f₁ ρ₁ ≅-⇨ map f₂ ρ₂
    map-cong {f₁ = _ , _} {f₂ = ._ , _} {ρ₂ = ε} [ P.refl ] P.refl =
             P.refl
    map-cong {f₁ = f₁} {f₂ = f₂} {ρ₂ = ρ ▻ t} f₁≅f₂ P.refl = begin
      [ map f₁ (ρ ▻ t)    ]  ≡⟨ map-▻ f₁ ρ t ⟩
      [ map f₁ ρ ▻ f₁ · t ]  ≡⟨ ▻⇨-cong P.refl
                                        (map-cong f₁≅f₂ (P.refl {x = [ ρ ]}))
                                        (·-cong f₁≅f₂ (P.refl {x = [ t ]₁})) ⟩
      [ map f₂ ρ ▻ f₂ · t ]  ≡⟨ P.sym $ map-▻ f₂ ρ t ⟩
      [ map f₂ (ρ ▻ t)    ]  ∎

    -- Variants which only require that the functions are
    -- extensionally equal.

    map-cong-ext₁ : ∀ {Γ₁ Δ Ε₁} {ρ̂₁₁ : Γ₁ ⇨̂ Δ} {ρ̂₂₁ : Δ ⇨̂ Ε₁}
                      {f₁ : [ T₁ ⟶ T₂ ] ρ̂₂₁} {ρ₁ : Sub T₁ ρ̂₁₁}
                      {Γ₂   Ε₂} {ρ̂₁₂ : Γ₂ ⇨̂ Δ} {ρ̂₂₂ : Δ ⇨̂ Ε₂}
                      {f₂ : [ T₁ ⟶ T₂ ] ρ̂₂₂} {ρ₂ : Sub T₁ ρ̂₁₂} →
                    Ε₁ ≅-Ctxt Ε₂ →
                    (∀ {σ} (t : Δ ⊢₁ σ) → f₁ · t ≅-⊢₂ f₂ · t) →
                    ρ₁ ≅-⇨ ρ₂ → map f₁ ρ₁ ≅-⇨ map f₂ ρ₂
    map-cong-ext₁ {Δ = Δ} {f₁ = f₁} {f₂ = f₂} {ρ₂ = ρ}
                  Ε₁≅Ε₂ f₁≅f₂ P.refl = helper ρ
      where
      helper : ∀ {Γ} {ρ̂ : Γ ⇨̂ Δ} (ρ : Sub T₁ ρ̂) → map f₁ ρ ≅-⇨ map f₂ ρ
      helper ε       = ε⇨-cong Ε₁≅Ε₂
      helper (ρ ▻ t) = begin
        [ map f₁ (ρ ▻ t)    ]  ≡⟨ map-▻ f₁ ρ t ⟩
        [ map f₁ ρ ▻ f₁ · t ]  ≡⟨ ▻⇨-cong P.refl (helper ρ) (f₁≅f₂ t) ⟩
        [ map f₂ ρ ▻ f₂ · t ]  ≡⟨ P.sym $ map-▻ f₂ ρ t ⟩
        [ map f₂ (ρ ▻ t)    ]  ∎

    map-cong-ext₂ : ∀ {Γ₁ Δ₁ Ε₁} {ρ̂₁₁ : Γ₁ ⇨̂ Δ₁} {ρ̂₂₁ : Δ₁ ⇨̂ Ε₁}
                      {f₁ : [ T₁ ⟶ T₂ ] ρ̂₂₁} {ρ₁ : Sub T₁ ρ̂₁₁}
                      {Γ₂ Δ₂ Ε₂} {ρ̂₁₂ : Γ₂ ⇨̂ Δ₂} {ρ̂₂₂ : Δ₂ ⇨̂ Ε₂}
                      {f₂ : [ T₁ ⟶ T₂ ] ρ̂₂₂} {ρ₂ : Sub T₁ ρ̂₁₂} →
                    Δ₁ ≅-Ctxt Δ₂ → Ε₁ ≅-Ctxt Ε₂ →
                    (∀ {σ₁ σ₂} {t₁ : Δ₁ ⊢₁ σ₁} {t₂ : Δ₂ ⊢₁ σ₂} →
                       t₁ ≅-⊢₁ t₂ → f₁ · t₁ ≅-⊢₂ f₂ · t₂) →
                    ρ₁ ≅-⇨ ρ₂ → map f₁ ρ₁ ≅-⇨ map f₂ ρ₂
    map-cong-ext₂ P.refl Ε₁≅Ε₂ f₁≅f₂ ρ₁≅ρ₂ =
      map-cong-ext₁ Ε₁≅Ε₂ (λ t → f₁≅f₂ (P.refl {x = [ t ]₁})) ρ₁≅ρ₂

    private

      -- A helper lemma.

      /∋-map-▻ :
        ∀ {Γ Δ Ε σ τ} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε} {t} →
        (x : Γ ▻ σ ∋ τ) (f : [ T₁ ⟶ T₂ ] ρ̂₂) (ρ : Sub T₁ ρ̂₁) →
        x /∋ map f (ρ ▻ t) ≅-⊢₂ x /∋ (map f ρ ▻ f · t)
      /∋-map-▻ {t = t} x f ρ =
        /∋-cong (P.refl {x = [ x ]}) (map-▻ f ρ t)

    -- Some sort of naturality statement for _/∋_.

    /∋-map : ∀ {Γ Δ Ε σ} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε} →
             (x : Γ ∋ σ) (f : [ T₁ ⟶ T₂ ] ρ̂₂) (ρ : Sub T₁ ρ̂₁) →
             x /∋ map f ρ ≅-⊢₂ f · (x /∋ ρ)
    /∋-map (zero {σ = σ}) f (ρ ▻ t) = begin
      [ zero[ σ ] /∋ map f (ρ ▻ t)     ]₂  ≡⟨ /∋-map-▻ zero[ σ ] f ρ ⟩
      [ zero[ σ ] /∋ (map f ρ ▻ f · t) ]₂  ≡⟨ P.refl ⟩
      [ f · t                          ]₂  ∎
    /∋-map (suc {σ = σ} x) f (ρ ▻ t) = begin
      [ suc      x /∋ map f (ρ ▻ t)     ]₂  ≡⟨ /∋-map-▻ (suc x) f ρ ⟩
      [ suc[ σ ] x /∋ (map f ρ ▻ f · t) ]₂  ≡⟨ P.refl ⟩
      [ x /∋ map f ρ                    ]₂  ≡⟨ /∋-map x f ρ ⟩
      [ f · (x /∋ ρ)                    ]₂  ∎

open Dummy public

abstract

  -- Map is functorial.

  map-[id] : ∀ {t} {T : Term-like t} {Γ Δ} {ρ̂ : Γ ⇨̂ Δ}
             (ρ : Sub T ρ̂) → map ([id] {T = T}) ρ ≅-⇨ ρ
  map-[id] ε       = P.refl
  map-[id] (ρ ▻ t) = ▻⇨-cong P.refl (map-[id] ρ) P.refl

  map-[∘] :
    ∀ {t₁} {T₁ : Term-like t₁}
      {t₂} {T₂ : Term-like t₂}
      {t₃} {T₃ : Term-like t₃}
      {Γ Δ Ε Ζ} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε} {ρ̂₃ : Ε ⇨̂ Ζ}
    (f₂ : [ T₂ ⟶ T₃ ] ρ̂₃) (f₁ : [ T₁ ⟶ T₂ ] ρ̂₂)
    (ρ : Sub T₁ ρ̂₁) →
    map (f₂ [∘] f₁) ρ ≅-⇨ map f₂ (map f₁ ρ)
  map-[∘] f₂ f₁ ε       = P.refl
  map-[∘] f₂ f₁ (ρ ▻ t) = begin
    [ map (f₂ [∘] f₁) (ρ ▻ t)           ]  ≡⟨ map-▻ (f₂ [∘] f₁) ρ t ⟩
    [ map (f₂ [∘] f₁) ρ ▻ f₂ · (f₁ · t) ]  ≡⟨ ▻⇨-cong P.refl (map-[∘] f₂ f₁ ρ) P.refl ⟩
    [ map f₂ (map f₁ ρ) ▻ f₂ · (f₁ · t) ]  ≡⟨ P.sym $ map-▻ f₂ (map f₁ ρ) (f₁ · t) ⟩
    [ map f₂ (map f₁ ρ ▻ f₁ · t)        ]  ≡⟨ map-cong (f₂ ∎-⟶) (P.sym $ map-▻ f₁ ρ t) ⟩
    [ map f₂ (map f₁ (ρ ▻ t))           ]  ∎


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Substitution.Introductions.IdNat {{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.Irrelevance
open import Definition.LogicalRelation.ShapeView
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.Introductions.IdUniv
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

[ℕ] : ∀ {Γ l} → ⊢ Γ → Γ ⊩⟨ l ⟩ ℕ ^ [ ! , ι ⁰ ]
[ℕ] ⊢Γ = ℕᵣ (idRed:*: (univ (ℕⱼ ⊢Γ)))

[Id]ℕ : ∀ {Γ l t u}
  (⊢Γ : ⊢ Γ)
  ([t] : Γ ⊩ℕ t ∷ℕ)
  ([u] : Γ ⊩ℕ u ∷ℕ)
  → Γ ⊩⟨ l ⟩ Id ℕ t u ^ [ % , ι ⁰ ]
[Id]ℕ {Γ} {l} {t} {u} ⊢Γ (ℕₜ .(suc m′) [[ ⊢tℕ , ⊢mℕ , dt ]] m≡m (sucᵣ {m′} [m′])) (ℕₜ .(suc n′) [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (sucᵣ {n′} [n′])) =
  let [Idmn] = [Id]ℕ ⊢Γ [m′] [n′]
      ⊢m′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [m′]
      ⊢n′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [n′]
      nfId = univ⇒* ((IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ ⇨∷* IdℕSRed*Term′ ⊢m′ℕ ⊢uℕ ⊢nℕ du)
        ⇨∷* (Id-ℕ-SS ⊢m′ℕ ⊢n′ℕ ⇨ id (Idⱼ (ℕⱼ ⊢Γ) ⊢m′ℕ ⊢n′ℕ)))
  in proj₁ (redSubst* nfId [Idmn])
[Id]ℕ {Γ} {l} {t} {u} ⊢Γ (ℕₜ .(suc m′) [[ ⊢tℕ , ⊢mℕ , dt ]] m≡m (sucᵣ {m′} [m′])) (ℕₜ .zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ) =
  let ⊢m′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [m′]
      nfId = (IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ)
        ⇨∷* (IdℕSRed*Term′ ⊢m′ℕ ⊢uℕ ⊢nℕ du ⇨∷* (Id-ℕ-S0 ⊢m′ℕ ⇨ id (Emptyⱼ ⊢Γ)))
  in Emptyᵣ [[ univ (Idⱼ (ℕⱼ ⊢Γ) ⊢tℕ ⊢uℕ) , univ (Emptyⱼ ⊢Γ) , univ⇒* nfId ]]
[Id]ℕ {Γ} {l} {t} {u} ⊢Γ (ℕₜ .(suc m′) [[ ⊢tℕ , ⊢mℕ , dt ]] m≡m (sucᵣ {m′} [m′])) (ℕₜ n [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (ne (neNfₜ nen _ n∼n))) =
  let ⊢m′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [m′]
      nfId = (IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ) ⇨∷* IdℕSRed*Term′ ⊢m′ℕ ⊢uℕ ⊢nℕ du
      m′≡m′ = escapeTermEq ([ℕ] {l = ι ⁰} ⊢Γ) (reflEqTerm ([ℕ] {l = ι ⁰} ⊢Γ) [m′])
  in ne′ (Id ℕ (suc m′) n) [[ univ (Idⱼ (ℕⱼ ⊢Γ) ⊢tℕ ⊢uℕ) , univ (Idⱼ (ℕⱼ ⊢Γ) ⊢mℕ ⊢nℕ) , univ⇒* nfId ]] (IdℕSₙ nen) (~-IdℕS ⊢Γ m′≡m′ n∼n)
[Id]ℕ {Γ} {l} {t} {u} ⊢Γ (ℕₜ .zero [[ ⊢tℕ , ⊢mℕ , dt ]] m≡m zeroᵣ) (ℕₜ .(suc n′) [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (sucᵣ {n′} [n′])) =
  let ⊢n′ = escapeTerm ([ℕ] {l = ι ⁰} ⊢Γ) [n′]
      nfId = (IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ)
        ⇨∷* (Idℕ0Red*Term′ ⊢uℕ ⊢nℕ du ⇨∷* (Id-ℕ-0S ⊢n′ ⇨ id (Emptyⱼ ⊢Γ)))
  in Emptyᵣ [[ univ (Idⱼ (ℕⱼ ⊢Γ) ⊢tℕ ⊢uℕ) , univ (Emptyⱼ ⊢Γ) , univ⇒* nfId ]]
[Id]ℕ {Γ} {l} {t} {u} ⊢Γ (ℕₜ .zero [[ ⊢tℕ , ⊢mℕ , dt ]] m≡m zeroᵣ) (ℕₜ .zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ) =
  let nfId = (IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ)
        ⇨∷* (Idℕ0Red*Term′ ⊢uℕ ⊢nℕ du ⇨∷* (Id-ℕ-00 ⊢Γ ⇨ id (Unitⱼ ⊢Γ)))
  in proj₁ (redSubst* (univ⇒* nfId) (maybeEmb″ (UnitType ⊢Γ)))
[Id]ℕ {Γ} {l} {t} {u} ⊢Γ (ℕₜ .zero [[ ⊢tℕ , ⊢mℕ , dt ]] m≡m zeroᵣ) (ℕₜ n [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (ne (neNfₜ nen _ n∼n))) =
  let nfId = (IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ) ⇨∷* Idℕ0Red*Term′ ⊢uℕ ⊢nℕ du
  in ne′ (Id ℕ zero n) [[ univ (Idⱼ (ℕⱼ ⊢Γ) ⊢tℕ ⊢uℕ) , univ (Idⱼ (ℕⱼ ⊢Γ) (zeroⱼ ⊢Γ) ⊢nℕ) , univ⇒* nfId ]] (Idℕ0ₙ nen) (~-Idℕ0 ⊢Γ n∼n)
[Id]ℕ {Γ} {l} {t} {u} ⊢Γ (ℕₜ m [[ ⊢tℕ , ⊢mℕ , dt ]] m≡m (ne (neNfₜ nem _ m∼m))) [u] =
  let
    ⊢u = escapeTerm ([ℕ] {l = ι ⁰} ⊢Γ) [u]
    u≡u = escapeTermEq {l = ι ⁰} ([ℕ] ⊢Γ) (reflEqTerm ([ℕ] {l = ι ⁰} ⊢Γ) [u])
  in ne′ (Id ℕ m u) [[ univ (Idⱼ (ℕⱼ ⊢Γ) ⊢tℕ ⊢u) , univ (Idⱼ (ℕⱼ ⊢Γ) ⊢mℕ ⊢u) ,
    univ⇒* (IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢u) ]] (Idℕₙ nem) (~-Idℕ ⊢Γ m∼m u≡u)

escapeEqℕ₁  : ∀ {Γ m n} → Γ ⊩ℕ m ≡ n ∷ℕ → Γ ⊢ m ∷ ℕ ^ [ ! , ι ⁰ ]
escapeEqℕ₁ (ℕₜ₌ k k′ [[ ⊢t , ⊢u , d ]] d′ k≡k′ prop) = ⊢t

escapeEqℕ₂  : ∀ {Γ m n} → Γ ⊩ℕ m ≡ n ∷ℕ → Γ ⊢ n ∷ ℕ ^ [ ! , ι ⁰ ]
escapeEqℕ₂ (ℕₜ₌ k k′ d [[ ⊢t , ⊢u , d' ]] k≡k′ prop) = ⊢t

irrelevanceEqTermℕ : ∀ {Γ A t t′ u u′ r ll l}
                        (eqt : t PE.≡ t′) (equ : u PE.≡ u′)
                        (p : Γ ⊩⟨ l ⟩ A ^ [ r , ll ])
                      → Γ ⊩⟨ l ⟩ t ≡ u ∷ A ^ [ r , ll ] / p → Γ ⊩⟨ l ⟩ t′ ≡ u′ ∷ A ^ [ r , ll ] / p
irrelevanceEqTermℕ PE.refl PE.refl p t≡u = t≡u

[IdExt]ℕ : ∀ {Γ l t u v w}
  (⊢Γ : ⊢ Γ)
  ([t] : Γ ⊩ℕ t ∷ℕ)
  ([u] : Γ ⊩ℕ u ∷ℕ)
  ([v] : Γ ⊩ℕ v ∷ℕ)
  ([w] : Γ ⊩ℕ w ∷ℕ)
  ([t≡v] : Γ ⊩ℕ t ≡ v ∷ℕ)
  ([u≡w] : Γ ⊩ℕ u ≡ w ∷ℕ)
  →  Γ ⊩⟨ l ⟩ Id ℕ t u ≡ Id ℕ v w ^ [ % , ι ⁰ ] / [Id]ℕ ⊢Γ [t] [u]

[IdExt]ℕ {Γ} {l} {t} {u} {v} {w} ⊢Γ
            (ℕₜ .(suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ {n} [t']))
            (ℕₜ .(suc _) [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (sucᵣ {n₂} [u']))
            (ℕₜ .(suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ {n₁} [v']))
            (ℕₜ .(suc _) [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ (sucᵣ {n₃} [w']))
            (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ [t≡v']))
            (ℕₜ₌ .(suc _) .(suc _) d₅ d′₁ k≡k′₁ (sucᵣ [u≡w'])) =
  let t≡t' = suc-PE-injectivity (whrDet*Term (dt , sucₙ) (redₜ d₄ , sucₙ))
      u≡u' = suc-PE-injectivity (whrDet*Term (du , sucₙ) (redₜ d₅ , sucₙ))
      v≡v' = suc-PE-injectivity (whrDet*Term (dv , sucₙ) (redₜ d′ , sucₙ))
      w≡w' = suc-PE-injectivity (whrDet*Term (dw , sucₙ) (redₜ d′₁ , sucₙ))
      [t] = ℕₜ (suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ [t'])
      [u] = ℕₜ (suc _) [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (sucᵣ [u'])
      [v] = ℕₜ (suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ [v'])
      [w] = ℕₜ (suc _) [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ (sucᵣ [w'])
      [Idmn] = [IdExt]ℕ {l = l} ⊢Γ [t'] [u'] [v'] [w'] (irrelevanceEqTermℕ {l = l} (PE.sym t≡t') (PE.sym v≡v') ([ℕ] ⊢Γ) [t≡v'])
                                               (irrelevanceEqTermℕ {l = l} (PE.sym u≡u') (PE.sym w≡w') ([ℕ] ⊢Γ) [u≡w'])
      ⊢t′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [t']
      ⊢u′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [u']
      nfId = univ⇒* ((IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ ⇨∷* IdℕSRed*Term′ ⊢t′ℕ ⊢uℕ ⊢nℕ du)
        ⇨∷* (Id-ℕ-SS ⊢t′ℕ ⊢u′ℕ ⇨ id (Idⱼ (ℕⱼ ⊢Γ) ⊢t′ℕ ⊢u′ℕ)))
      ⊢v′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [v']
      ⊢w′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [w']
      nfId' = univ⇒* ((IdℕRed*Term′ ⊢vℕ ⊢oℕ dv ⊢wℕ ⇨∷* IdℕSRed*Term′ ⊢v′ℕ ⊢wℕ ⊢pℕ dw)
        ⇨∷* (Id-ℕ-SS ⊢v′ℕ ⊢w′ℕ ⇨ id (Idⱼ (ℕⱼ ⊢Γ) ⊢v′ℕ ⊢w′ℕ)))
      [IdA] , [IdA≡ℕ] = redSubst* {l = l} nfId ([Id]ℕ ⊢Γ [t'] [u'])
      [IdB] , [IdB≡ℕ] = redSubst* {l = l} nfId' ([Id]ℕ ⊢Γ [v'] [w'])
      [IdA≡ℕ]′ = irrelevanceEq {A = Id ℕ t u} {B = Id ℕ n n₂} {l = l} [IdA] ([Id]ℕ ⊢Γ [t] [u]) [IdA≡ℕ]
      [IdB≡ℕ]′ = irrelevanceEq {A = Id ℕ v w} {B = Id ℕ n₁ n₃} {l = l} {l′ = l} [IdB] ([Id]ℕ ⊢Γ [v] [w]) [IdB≡ℕ]
  in transEq {A = Id ℕ t u} {B = Id ℕ n n₂} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [t] [u]) ([Id]ℕ ⊢Γ [t'] [u']) ([Id]ℕ ⊢Γ [v] [w])
                  [IdA≡ℕ]′
                  (transEq {A = Id ℕ n n₂} {B = Id ℕ n₁ n₃} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [t'] [u']) ([Id]ℕ ⊢Γ [v'] [w']) ([Id]ℕ ⊢Γ [v] [w])
                                   [Idmn] (symEq {A = Id ℕ v w} {B = Id ℕ n₁ n₃} ([Id]ℕ ⊢Γ [v] [w]) ([Id]ℕ ⊢Γ [v'] [w']) [IdB≡ℕ]′))

[IdExt]ℕ {Γ} {l} {t} {u} {v} {w} ⊢Γ
  (ℕₜ .(suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ {n} [t']))
  (ℕₜ .zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ)
  (ℕₜ .(suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ {m} [v']))
  (ℕₜ .zero [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ zeroᵣ)
  (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x))
  (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) =
  let [t] = ℕₜ (suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ [t'])
      [u] = ℕₜ zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ
      [v] = ℕₜ (suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ [v'])
      [w] = ℕₜ zero [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ zeroᵣ
      ⊢t′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [t']
      nfId = univ⇒* ((IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ)
        ⇨∷* (IdℕSRed*Term′ ⊢t′ℕ ⊢uℕ ⊢nℕ du ⇨∷* (Id-ℕ-S0 ⊢t′ℕ ⇨ id (Emptyⱼ ⊢Γ))))
      ⊢v′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [v']
      nfId' = univ⇒* ((IdℕRed*Term′ ⊢vℕ ⊢oℕ dv ⊢wℕ)
        ⇨∷* (IdℕSRed*Term′ ⊢v′ℕ ⊢wℕ ⊢pℕ dw ⇨∷* (Id-ℕ-S0 ⊢v′ℕ ⇨ id (Emptyⱼ ⊢Γ))))
      [Empty] = Emptyᵣ (idRed:*: (univ (Emptyⱼ ⊢Γ)))
      [IdA] , [IdA≡ℕ] = redSubst* {l = l} nfId [Empty]
      [IdB] , [IdB≡ℕ] = redSubst* {l = l} nfId' [Empty]
      [IdA≡ℕ]′ = irrelevanceEq {A = Id ℕ t u} {B = Empty _} {l = l} {l′ = l} [IdA] ([Id]ℕ ⊢Γ [t] [u]) [IdA≡ℕ]
      [IdB≡ℕ]′ = irrelevanceEq {A = Id ℕ v w} {B = Empty _} {l = l} {l′ = l} [IdB] ([Id]ℕ ⊢Γ [v] [w]) [IdB≡ℕ]
  in transEq {A = Id ℕ t u} {B = Empty _} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [t] [u]) [Empty] ([Id]ℕ ⊢Γ [v] [w])
                  [IdA≡ℕ]′  (symEq {A = Id ℕ v w} {B = Empty _} {l = l} {l′ = l} ([Id]ℕ ⊢Γ [v] [w]) [Empty] [IdB≡ℕ]′)

[IdExt]ℕ {Γ} {l} {u} {t} {w} {v} ⊢Γ
  (ℕₜ .zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ)
  (ℕₜ .(suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ {n} [t']))
  (ℕₜ .zero [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ zeroᵣ)
  (ℕₜ .(suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ {m} [v']))
  (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ)
  (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) =
  let [t] = ℕₜ (suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ [t'])
      [u] = ℕₜ zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ
      [v] = ℕₜ (suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ [v'])
      [w] = ℕₜ zero [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ zeroᵣ
      ⊢t′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [t']
      nfId = univ⇒* ((IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ)
        ⇨∷* (IdℕSRed*Term′ ⊢t′ℕ ⊢uℕ ⊢nℕ du ⇨∷* (Id-ℕ-S0 ⊢t′ℕ ⇨ id (Emptyⱼ ⊢Γ))))
      ⊢v′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [v']
      nfId' = univ⇒* ((IdℕRed*Term′ ⊢vℕ ⊢oℕ dv ⊢wℕ)
        ⇨∷* (IdℕSRed*Term′ ⊢v′ℕ ⊢wℕ ⊢pℕ dw ⇨∷* (Id-ℕ-S0 ⊢v′ℕ ⇨ id (Emptyⱼ ⊢Γ))))
      [Empty] = Emptyᵣ (idRed:*: (univ (Emptyⱼ ⊢Γ)))
      [IdA] , [IdA≡ℕ] = redSubst* {l = l} nfId [Empty]
      [IdB] , [IdB≡ℕ] = redSubst* {l = l} nfId' [Empty]
      [IdA≡ℕ]′ = irrelevanceEq {A = Id ℕ t u} {B = Empty _} {l = l} {l′ = l} [IdA] ([Id]ℕ ⊢Γ [t] [u]) [IdA≡ℕ]
      [IdB≡ℕ]′ = irrelevanceEq {A = Id ℕ v w} {B = Empty _} {l = l} {l′ = l} [IdB] ([Id]ℕ ⊢Γ [v] [w]) [IdB≡ℕ]
   in transEq {A = Id ℕ u t} {B = Empty _} {C = Id ℕ w v} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [u] [t]) [Empty] ([Id]ℕ ⊢Γ [w] [v])
                  [IdB≡ℕ]′  (symEq {A = Id ℕ w v} {B = Empty _} {l = l} {l′ = l} ([Id]ℕ ⊢Γ [w] [v]) [Empty] [IdA≡ℕ]′)

[IdExt]ℕ {Γ} {l} {t} {u} {v} {w} ⊢Γ (ℕₜ .zero [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ zeroᵣ) (ℕₜ .zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ)
            (ℕₜ .zero [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ zeroᵣ) (ℕₜ .zero [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ zeroᵣ)
            (ℕₜ₌ .zero .zero d₄ d′ k≡k′ zeroᵣ) (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) =
  let [t] = ℕₜ zero [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ zeroᵣ
      [u] = ℕₜ zero [[ ⊢uℕ , ⊢nℕ , du ]] n≡n zeroᵣ
      [v] = ℕₜ zero [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ zeroᵣ
      [w] = ℕₜ zero [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ zeroᵣ
      nfId = univ⇒* ((IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ)
        ⇨∷* (Idℕ0Red*Term′ ⊢uℕ ⊢nℕ du ⇨∷* (Id-ℕ-00 ⊢Γ ⇨ id (Unitⱼ ⊢Γ))))
      nfId' = univ⇒* ((IdℕRed*Term′ ⊢vℕ ⊢oℕ dv ⊢wℕ)
        ⇨∷* (Idℕ0Red*Term′ ⊢wℕ ⊢pℕ dw ⇨∷* (Id-ℕ-00 ⊢Γ ⇨ id (Unitⱼ ⊢Γ))))
      [Unit] = UnitType ⊢Γ
      [IdA] , [IdA≡ℕ] = redSubst* nfId [Unit]
      [IdB] , [IdB≡ℕ] = redSubst* nfId' [Unit]
      [IdA≡ℕ]′ = irrelevanceEq {A = Id ℕ t u} {B = Unit} {l = ι ⁰} {l′ = l} [IdA] ([Id]ℕ ⊢Γ [t] [u]) [IdA≡ℕ]
      [IdB≡ℕ]′ = irrelevanceEq {A = Id ℕ v w} {B = Unit} {l = ι ⁰} {l′ = l} [IdB] ([Id]ℕ ⊢Γ [v] [w]) [IdB≡ℕ]
  in transEq {A = Id ℕ t u} {B = Unit} {C = Id ℕ v w} {l = l} {l′ = ι ⁰} {l″ = l} ([Id]ℕ ⊢Γ [t] [u]) [Unit] ([Id]ℕ ⊢Γ [v] [w])
                  [IdA≡ℕ]′  (symEq {A = Id ℕ v w} {B = Unit} {l = l} {l′ = ι ⁰} ([Id]ℕ ⊢Γ [v] [w]) [Unit] [IdB≡ℕ]′)

[IdExt]ℕ {Γ} {l} {t} {u} {v} {w} ⊢Γ (ℕₜ .(suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ {t'} [t'])) (ℕₜ n [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (ne (neNfₜ neKu ⊢ku k≡ku)))
         (ℕₜ .(suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ {v'} [v'])) (ℕₜ n₃ [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ (ne (neNfₜ neKw ⊢kw k≡kw)))
         (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ [t≡v'])) (ℕₜ₌ k₁ k′₁ d₅ d′₁ k≡k′₁ (ne (neNfₜ₌ neK neM K~M))) =
  let t≡t' = suc-PE-injectivity (whrDet*Term (dt , sucₙ) (redₜ d₄ , sucₙ))
      v≡v' = suc-PE-injectivity (whrDet*Term (dv , sucₙ) (redₜ d′ , sucₙ))
      u≡u' = whrDet*Term (du , ne neKu) (redₜ d₅ , ne neK)
      w≡w' = whrDet*Term (dw , ne neKw) (redₜ d′₁ , ne neM)
      [t] = ℕₜ (suc _) [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (sucᵣ [t'])
      [u] = ℕₜ n [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (ne (neNfₜ neKu ⊢ku k≡ku))
      [v] = ℕₜ (suc _) [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (sucᵣ [v'])
      [w] = ℕₜ n₃ [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ (ne (neNfₜ neKw ⊢kw k≡kw))
      [suct'] = ℕₜ (suc t') (idRedTerm:*: ⊢mℕ) n≡n₁ (sucᵣ [t'])
      [n] = ℕₜ n (idRedTerm:*: ⊢nℕ) n≡n (ne (neNfₜ neKu ⊢ku k≡ku))
      [sucv'] = ℕₜ (suc v') (idRedTerm:*: ⊢oℕ) n≡n₂ (sucᵣ [v'])
      [n₃] = ℕₜ n₃ (idRedTerm:*: ⊢pℕ) n≡n₃ (ne (neNfₜ neKw ⊢kw k≡kw))
      ⊢t′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [t']
      ⊢v′ℕ = escapeTerm {l = ι ⁰} ([ℕ] ⊢Γ) [v']
      nfId = univ⇒* ((IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ) ⇨∷* IdℕSRed*Term′ ⊢t′ℕ ⊢uℕ ⊢nℕ du)
      nfId' = univ⇒* ((IdℕRed*Term′ ⊢vℕ ⊢oℕ dv ⊢wℕ) ⇨∷* IdℕSRed*Term′ ⊢v′ℕ ⊢wℕ ⊢pℕ dw)
      [IdA] , [IdA≡ℕ] = redSubst* nfId ([Id]ℕ ⊢Γ [suct'] [n])
      [IdB] , [IdB≡ℕ] = redSubst* nfId' ([Id]ℕ ⊢Γ [sucv'] [n₃])
      [IdA≡ℕ]′ = irrelevanceEq {A = Id ℕ t u} {B = Id ℕ (suc t') n} {l = ι ⁰} {l′ = l} [IdA] ([Id]ℕ ⊢Γ [t] [u]) [IdA≡ℕ]
      [IdB≡ℕ]′ = irrelevanceEq {A = Id ℕ v w} {B = Id ℕ (suc v') n₃} {l = ι ⁰} {l′ = l} [IdB] ([Id]ℕ ⊢Γ [v] [w]) [IdB≡ℕ]
      [Idneutral] : Γ ⊩⟨ l ⟩ Id ℕ (suc t') n ≡ Id ℕ (suc v') n₃ ^ [ % , ι ⁰ ] / ([Id]ℕ ⊢Γ [suct'] [n])
      [Idneutral] = ne₌ (Id ℕ (suc v') n₃) (idRed:*: (univ (Idⱼ (ℕⱼ ⊢Γ) ⊢oℕ ⊢pℕ))) (IdℕSₙ neKw)
                (~-IdℕS ⊢Γ (let X = escapeTermEq {l = l} ([ℕ] ⊢Γ) [t≡v'] in
                                PE.subst (λ X → Γ ⊢ X ≅ _ ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym t≡t') (PE.subst (λ X → Γ ⊢ _ ≅ X ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym v≡v') X) )
                           (PE.subst (λ X → Γ ⊢ X ~ _ ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym u≡u') (PE.subst (λ X → Γ ⊢ k₁ ~ X ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym w≡w') K~M)))
  in transEq {A = Id ℕ t u} {B = Id ℕ (suc t') n} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [t] [u]) ([Id]ℕ ⊢Γ [suct'] [n]) ([Id]ℕ ⊢Γ [v] [w])
                  [IdA≡ℕ]′
                  (transEq {A = Id ℕ (suc t') n} {B = Id ℕ (suc v') n₃} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [suct'] [n]) ([Id]ℕ ⊢Γ [sucv'] [n₃]) ([Id]ℕ ⊢Γ [v] [w])
                                   [Idneutral] (symEq {A = Id ℕ v w} {B = Id ℕ (suc v') n₃} {l = l} {l′ = l} ([Id]ℕ ⊢Γ [v] [w]) ([Id]ℕ ⊢Γ [sucv'] [n₃]) [IdB≡ℕ]′))

[IdExt]ℕ {Γ} {l} {t} {u} {v} {w} ⊢Γ (ℕₜ n₁ [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (ne (neNfₜ neKt ⊢kt k≡kt))) [u] (ℕₜ n₂ [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (ne (neNfₜ neKv ⊢kv k≡kv))) [w]
            (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM K~M))) [u=w] =
  let [t] = ℕₜ n₁ [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ (ne (neNfₜ neKt ⊢kt k≡kt))
      [v] = ℕₜ n₂ [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ (ne (neNfₜ neKv ⊢kv k≡kv))
      ⊢u = escapeTerm ([ℕ] {l = ι ⁰} ⊢Γ) [u]
      ⊢w = escapeTerm ([ℕ] {l = ι ⁰} ⊢Γ) [w]
      u≡w = escapeTermEq {l = ι ⁰} ([ℕ] ⊢Γ) [u=w]
      t≡t' = whrDet*Term (dt , ne neKt) (redₜ d₄ , ne neK)
      v≡v' = whrDet*Term (dv , ne neKv) (redₜ d′ , ne neM)
      nfId = univ⇒* (IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢u)
      nfId' = univ⇒* (IdℕRed*Term′ ⊢vℕ ⊢oℕ dv ⊢w)
      [n₁] = ℕₜ n₁ (idRedTerm:*: ⊢mℕ) n≡n₁ (ne (neNfₜ neKt ⊢kt k≡kt))
      [n₂] = ℕₜ n₂ (idRedTerm:*: ⊢oℕ) n≡n₂ (ne (neNfₜ neKv ⊢kv k≡kv))
      [IdA] , [IdA≡ℕ] = redSubst* nfId ([Id]ℕ ⊢Γ [n₁] [u])
      [IdB] , [IdB≡ℕ] = redSubst* nfId' ([Id]ℕ ⊢Γ [n₂] [w])
      [IdA≡ℕ]′ = irrelevanceEq {A = Id ℕ t u} {B = Id ℕ n₁ u} {l = ι ⁰} {l′ = l} [IdA] ([Id]ℕ ⊢Γ [t] [u]) [IdA≡ℕ]
      [IdB≡ℕ]′ = irrelevanceEq {A = Id ℕ v w} {B = Id ℕ n₂ w} {l = ι ⁰} {l′ = l} [IdB] ([Id]ℕ ⊢Γ [v] [w]) [IdB≡ℕ]
      [Idneutral] : Γ ⊩⟨ l ⟩ Id ℕ n₁ u ≡ Id ℕ n₂ w ^ [ % , ι ⁰ ] / ([Id]ℕ ⊢Γ [n₁] [u])
      [Idneutral] = ne₌ (Id ℕ n₂ w) (idRed:*: (univ (Idⱼ (ℕⱼ ⊢Γ) ⊢oℕ ⊢w))) (Idℕₙ neKv)
                        (~-Idℕ ⊢Γ (PE.subst (λ X → Γ ⊢ X ~ _ ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym t≡t') (PE.subst (λ X → Γ ⊢ _ ~ X ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym v≡v') K~M)) u≡w)
  in transEq {A = Id ℕ t u} {B = Id ℕ n₁ u} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [t] [u]) ([Id]ℕ ⊢Γ [n₁] [u]) ([Id]ℕ ⊢Γ [v] [w])
                  [IdA≡ℕ]′
                  (transEq {A = Id ℕ n₁ u} {B = Id ℕ n₂ w} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [n₁] [u]) ([Id]ℕ ⊢Γ [n₂] [w]) ([Id]ℕ ⊢Γ [v] [w])
                                   [Idneutral] (symEq {A = Id ℕ v w} {B = Id ℕ n₂ w} {l = l} {l′ = l} ([Id]ℕ ⊢Γ [v] [w]) ([Id]ℕ ⊢Γ [n₂] [w]) [IdB≡ℕ]′))

[IdExt]ℕ {Γ} {l} {t} {u} {v} {w} ⊢Γ (ℕₜ .zero [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ zeroᵣ) (ℕₜ n [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (ne (neNfₜ neKu ⊢ku k≡ku)))
         (ℕₜ .zero [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ zeroᵣ) (ℕₜ n₃ [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ (ne (neNfₜ neKw ⊢kw k≡kw)))
         (ℕₜ₌ .zero .zero d₄ d′ k≡k′ zeroᵣ) (ℕₜ₌ k₁ k′₁ d₅ d′₁ k≡k′₁ (ne (neNfₜ₌ neK neM K~M))) =
  let u≡u' = whrDet*Term (du , ne neKu) (redₜ d₅ , ne neK)
      w≡w' = whrDet*Term (dw , ne neKw) (redₜ d′₁ , ne neM)
      [t] = ℕₜ zero [[ ⊢tℕ , ⊢mℕ , dt ]] n≡n₁ zeroᵣ
      [u] = ℕₜ n [[ ⊢uℕ , ⊢nℕ , du ]] n≡n (ne (neNfₜ neKu ⊢ku k≡ku))
      [v] = ℕₜ zero [[ ⊢vℕ , ⊢oℕ , dv ]] n≡n₂ zeroᵣ
      [w] = ℕₜ n₃ [[ ⊢wℕ , ⊢pℕ , dw ]] n≡n₃ (ne (neNfₜ neKw ⊢kw k≡kw))
      [n] = ℕₜ n (idRedTerm:*: ⊢nℕ) n≡n (ne (neNfₜ neKu ⊢ku k≡ku))
      [n₃] = ℕₜ n₃ (idRedTerm:*: ⊢pℕ) n≡n₃ (ne (neNfₜ neKw ⊢kw k≡kw))
      [zero] = ℕₜ zero (idRedTerm:*: (zeroⱼ ⊢Γ)) (≅ₜ-zerorefl ⊢Γ) zeroᵣ
      nfId = univ⇒* ((IdℕRed*Term′ ⊢tℕ ⊢mℕ dt ⊢uℕ) ⇨∷* Idℕ0Red*Term′ ⊢uℕ ⊢nℕ du)
      nfId' = univ⇒* ((IdℕRed*Term′ ⊢vℕ ⊢oℕ dv ⊢wℕ) ⇨∷* Idℕ0Red*Term′ ⊢wℕ ⊢pℕ dw)
      [IdA] , [IdA≡ℕ] = redSubst* nfId ([Id]ℕ ⊢Γ [zero] [n])
      [IdB] , [IdB≡ℕ] = redSubst* nfId' ([Id]ℕ ⊢Γ [zero] [n₃])
      [IdA≡ℕ]′ = irrelevanceEq {A = Id ℕ t u} {B = Id ℕ zero n} {l = ι ⁰} {l′ = l} [IdA] ([Id]ℕ ⊢Γ [t] [u]) [IdA≡ℕ]
      [IdB≡ℕ]′ = irrelevanceEq {A = Id ℕ v w} {B = Id ℕ zero n₃} {l = ι ⁰} {l′ = l} [IdB] ([Id]ℕ ⊢Γ [v] [w]) [IdB≡ℕ]
      [Idneutral] : Γ ⊩⟨ l ⟩ Id ℕ zero n ≡ Id ℕ zero n₃ ^ [ % , ι ⁰ ] / ([Id]ℕ ⊢Γ [zero] [n])
      [Idneutral] = ne₌ (Id ℕ zero n₃) (idRed:*: (univ (Idⱼ (ℕⱼ ⊢Γ) ⊢oℕ ⊢pℕ))) (Idℕ0ₙ neKw)
                (~-Idℕ0 ⊢Γ (PE.subst (λ X → Γ ⊢ X ~ _ ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym u≡u') (PE.subst (λ X → Γ ⊢ k₁ ~ X ∷ ℕ ^ [ ! , ι ⁰ ]) (PE.sym w≡w') K~M)))
  in transEq {A = Id ℕ t u} {B = Id ℕ zero n} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [t] [u]) ([Id]ℕ ⊢Γ [zero] [n]) ([Id]ℕ ⊢Γ [v] [w])
                  [IdA≡ℕ]′
                  (transEq {A = Id ℕ zero n} {B = Id ℕ zero n₃} {C = Id ℕ v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [zero] [n]) ([Id]ℕ ⊢Γ [zero] [n₃]) ([Id]ℕ ⊢Γ [v] [w])
                                   [Idneutral] (symEq {A = Id ℕ v w} {B = Id ℕ zero n₃} {l = l} {l′ = l} ([Id]ℕ ⊢Γ [v] [w]) ([Id]ℕ ⊢Γ [zero] [n₃]) [IdB≡ℕ]′))

-- refuting cases

[IdExt]ℕ ⊢Γ (ℕₜ .zero d₁ n≡n₁ zeroᵣ) _ _ _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) _ = ⊥-elim (zero≢suc (whrDet*Term (redₜ d₁ , zeroₙ) (redₜ d₄ , sucₙ)))
[IdExt]ℕ ⊢Γ (ℕₜ n₁ d₁ n≡n₁ (ne (neNfₜ neK ⊢k k≡k))) _ _ _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) _ = ⊥-elim (suc≢ne neK (whrDet*Term (redₜ d₄ , sucₙ) (redₜ d₁ , ne neK)))
[IdExt]ℕ ⊢Γ _ _ (ℕₜ .zero d₂ n≡n₂ zeroᵣ) _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) _ = ⊥-elim (zero≢suc (whrDet*Term (redₜ d₂ , zeroₙ) (redₜ d′ , sucₙ)))
[IdExt]ℕ ⊢Γ _ _ (ℕₜ n₂ d₂ n≡n₂ (ne (neNfₜ neK ⊢k k≡k))) _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) _ = ⊥-elim (suc≢ne neK (whrDet*Term (redₜ d′ , sucₙ) (redₜ d₂ , ne neK)))

[IdExt]ℕ ⊢Γ _ (ℕₜ .zero d₁ n≡n₁ zeroᵣ) _ _ _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) = ⊥-elim (zero≢suc (whrDet*Term (redₜ d₁ , zeroₙ) (redₜ d₄ , sucₙ)))
[IdExt]ℕ ⊢Γ _ (ℕₜ n₁ d₁ n≡n₁ (ne (neNfₜ neK ⊢k k≡k))) _ _ _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) = ⊥-elim (suc≢ne neK (whrDet*Term (redₜ d₄ , sucₙ) (redₜ d₁ , ne neK)))
[IdExt]ℕ ⊢Γ _ _ _ (ℕₜ .zero d₂ n≡n₂ zeroᵣ) _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) = ⊥-elim (zero≢suc (whrDet*Term (redₜ d₂ , zeroₙ) (redₜ d′ , sucₙ)))
[IdExt]ℕ ⊢Γ _ _ _ (ℕₜ n₂ d₂ n≡n₂ (ne (neNfₜ neK ⊢k k≡k))) _ (ℕₜ₌ .(suc _) .(suc _) d₄ d′ k≡k′ (sucᵣ x)) = ⊥-elim (suc≢ne neK (whrDet*Term (redₜ d′ , sucₙ) (redₜ d₂ , ne neK)))

[IdExt]ℕ ⊢Γ (ℕₜ .(suc _) d n≡n (sucᵣ x₃)) _ _ _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) _ = ⊥-elim (zero≢suc (whrDet*Term (redₜ d₅ , zeroₙ) (redₜ d , sucₙ)))
[IdExt]ℕ ⊢Γ (ℕₜ n d n≡n (ne (neNfₜ neK ⊢k k≡k))) _ _ _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) _ = ⊥-elim (zero≢ne neK (whrDet*Term (redₜ d₅ , zeroₙ) (redₜ d , ne neK)))
[IdExt]ℕ ⊢Γ _ _ (ℕₜ .(suc _) d₃ n≡n₃ (sucᵣ x₃)) _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) _ = ⊥-elim (zero≢suc (whrDet*Term (redₜ d′₁ , zeroₙ) (redₜ d₃ , sucₙ)))
[IdExt]ℕ ⊢Γ _ _ (ℕₜ n₃ d₃ n≡n₃ (ne (neNfₜ neK ⊢k k≡k))) _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) _ = ⊥-elim (zero≢ne neK (whrDet*Term (redₜ d′₁ , zeroₙ) (redₜ d₃ , ne neK)))

[IdExt]ℕ ⊢Γ _ (ℕₜ .(suc _) d n≡n (sucᵣ x₃)) _ _ _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) = ⊥-elim (zero≢suc (whrDet*Term (redₜ d₅ , zeroₙ) (redₜ d , sucₙ)))
[IdExt]ℕ ⊢Γ _ (ℕₜ n d n≡n (ne (neNfₜ neK ⊢k k≡k))) _ _ _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) = ⊥-elim (zero≢ne neK (whrDet*Term (redₜ d₅ , zeroₙ) (redₜ d , ne neK)))
[IdExt]ℕ ⊢Γ _ _ _ (ℕₜ .(suc _) d₃ n≡n₃ (sucᵣ x₃)) _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) = ⊥-elim (zero≢suc (whrDet*Term (redₜ d′₁ , zeroₙ) (redₜ d₃ , sucₙ)))
[IdExt]ℕ ⊢Γ _ _ _ (ℕₜ n₃ d₃ n≡n₃ (ne (neNfₜ neK ⊢k k≡k))) _ (ℕₜ₌ .zero .zero d₅ d′₁ k≡k′₁ zeroᵣ) = ⊥-elim (zero≢ne neK (whrDet*Term (redₜ d′₁ , zeroₙ) (redₜ d₃ , ne neK)))

[IdExt]ℕ ⊢Γ (ℕₜ .(suc _) d₁ n≡n₁ (sucᵣ x₁)) _ _ _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) _ =  ⊥-elim (suc≢ne neK (whrDet*Term (redₜ d₁ , sucₙ) (redₜ d₄ , ne neK)))
[IdExt]ℕ ⊢Γ (ℕₜ .zero d₁ n≡n₁ zeroᵣ) _ _ _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) _ = ⊥-elim (zero≢ne neK (whrDet*Term (redₜ d₁ , zeroₙ) (redₜ d₄ , ne neK)))
[IdExt]ℕ ⊢Γ _ _ (ℕₜ .(suc _) d₁ n≡n₁ (sucᵣ x₁)) _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) _ =  ⊥-elim (suc≢ne neM (whrDet*Term (redₜ d₁ , sucₙ) (redₜ d′ , ne neM)))
[IdExt]ℕ ⊢Γ _ _ (ℕₜ .zero d₁ n≡n₁ zeroᵣ) _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) _ = ⊥-elim (zero≢ne neM (whrDet*Term (redₜ d₁ , zeroₙ) (redₜ d′ , ne neM)))

[IdExt]ℕ ⊢Γ _ (ℕₜ .(suc _) d₁ n≡n₁ (sucᵣ x₁)) _ _ _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) =  ⊥-elim (suc≢ne neK (whrDet*Term (redₜ d₁ , sucₙ) (redₜ d₄ , ne neK)))
[IdExt]ℕ ⊢Γ _ (ℕₜ .zero d₁ n≡n₁ zeroᵣ) _ _ _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) = ⊥-elim (zero≢ne neK (whrDet*Term (redₜ d₁ , zeroₙ) (redₜ d₄ , ne neK)))
[IdExt]ℕ ⊢Γ _ _ _ (ℕₜ .(suc _) d₁ n≡n₁ (sucᵣ x₁)) _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) =  ⊥-elim (suc≢ne neM (whrDet*Term (redₜ d₁ , sucₙ) (redₜ d′ , ne neM)))
[IdExt]ℕ ⊢Γ _ _ _ (ℕₜ .zero d₁ n≡n₁ zeroᵣ) _ (ℕₜ₌ k k′ d₄ d′ k≡k′ (ne (neNfₜ₌ neK neM k≡m))) = ⊥-elim (zero≢ne neM (whrDet*Term (redₜ d₁ , zeroₙ) (redₜ d′ , ne neM)))

[Id]ℕGen : ∀ {Γ l A t u}
  (⊢Γ : ⊢ Γ)
  ([A] : Γ ⊩ℕ A)
  ([t] : Γ ⊩⟨ l ⟩ t ∷ A ^ [ ! , ι ⁰ ] / ℕᵣ [A])
  ([u] : Γ ⊩⟨ l ⟩ u ∷ A ^ [ ! , ι ⁰ ] / ℕᵣ [A])
  → Γ ⊩⟨ l ⟩ Id A t u ^ [ % , ι ⁰ ]

[Id]ℕGen ⊢Γ [[ ⊢A , ⊢B , D ]] (ℕₜ n d n≡n prop) (ℕₜ n₁ d₁ n≡n₁ prop₁) =
  let
    [[ ⊢tℕ , _ , _ ]] = d
    [[ ⊢uℕ , _ , _ ]] = d₁
  in proj₁ (redSubst* (IdRed* (conv ⊢tℕ (sym (subset* D))) (conv ⊢uℕ (sym (subset* D))) D)
    ([Id]ℕ ⊢Γ (ℕₜ n d n≡n prop) (ℕₜ n₁ d₁ n≡n₁ prop₁)))

[IdExt]ℕGen : ∀ {A B t v u w Γ l l'}
         (⊢Γ : ⊢ Γ)
         ([A] : Γ ⊩ℕ A)
         ([B] : Γ ⊩ℕ B)
         ([A≡B] : Γ ⊩⟨ l ⟩ A ≡ B ^ [ ! , ι ⁰ ] / ℕᵣ [A])
         ([t] : Γ ⊩⟨ l ⟩ t ∷ A ^ [ ! , ι ⁰ ] / ℕᵣ [A])
         ([v] : Γ ⊩⟨ l' ⟩ v ∷ B ^ [ ! , ι ⁰ ] / ℕᵣ [B])
         ([t≡v] : Γ ⊩⟨ l ⟩ t ≡ v ∷ A ^ [ ! , ι ⁰ ] / ℕᵣ [A])
         ([u] : Γ ⊩⟨ l ⟩ u ∷ A ^ [ ! , ι ⁰ ] / ℕᵣ [A])
         ([w] : Γ ⊩⟨ l' ⟩ w ∷ B ^ [ ! , ι ⁰ ] / ℕᵣ [B])
         ([u≡w] : Γ ⊩⟨ l ⟩ u ≡ w ∷ A ^ [ ! , ι ⁰ ] / ℕᵣ [A])
       → Γ ⊩⟨ l ⟩ Id A t u ≡ Id B v w ^ [ % , ι ⁰ ] / [Id]ℕGen ⊢Γ [A] [t] [u]

[IdExt]ℕGen {A} {B} {t} {v} {u} {w} {Γ} {l} {l'} ⊢Γ [[ ⊢A , ⊢B , D ]] [[ ⊢A₁ , ⊢B₁ , D₁ ]] [A≡B] [t] [v] [t≡v] [u] [w] [u≡w] =
  let [A] = [[ ⊢A , ⊢B , D ]]
      [B] = [[ ⊢A₁ , ⊢B₁ , D₁ ]]
      [ℕ]' = [ℕ] {l = l} ⊢Γ
      [A]' , [Aeq] = redSubst* D [ℕ]'
      [B]' , [Beq] = redSubst* D₁ [ℕ]'
      [t]′ = convTerm₁ {t = t} [A]' [ℕ]' [Aeq] (irrelevanceTerm {l = l} (ℕᵣ [A]) [A]' [t])
      [u]′ = convTerm₁ {t = u} [B]' [ℕ]' [Beq] (irrelevanceTerm {l = l} (ℕᵣ [B]) [B]' [u])
      [v]′ = convTerm₁ {t = v} [A]' [ℕ]' [Aeq] (irrelevanceTerm {l = l} (ℕᵣ [A]) [A]' [v])
      [w]′ = convTerm₁ {t = w} [B]' [ℕ]' [Beq] (irrelevanceTerm {l = l} (ℕᵣ [B]) [B]' [w])
      [t≡v]′ = convEqTerm₁ {t = t} {u = v} [A]' [ℕ]' [Aeq] (irrelevanceEqTerm {l = l} (ℕᵣ [A]) [A]' [t≡v])
      [u≡w]′ = convEqTerm₁ {t = u} {u = w} [B]' [ℕ]' [Beq] (irrelevanceEqTerm {l = l} (ℕᵣ [B]) [B]' [u≡w])
      X = irrelevanceEq {A = Id ℕ t u} {B = Id ℕ v w} {l = l} ([Id]ℕ ⊢Γ [t] [u]) ([Id]ℕ ⊢Γ [t]′ [u]′) ([IdExt]ℕ ⊢Γ [t]′ [u]′ [v]′ [w]′ [t≡v]′ [u≡w]′)
      [IdA] , [IdA≡U] = redSubst* {l = l} (IdRed* (escapeTerm {l = l} (ℕᵣ [A]) [t]) (escapeTerm {l = l} (ℕᵣ [A]) [u]) D) ([Id]ℕ ⊢Γ [t]′ [u]′)
      [IdB] , [IdB≡U] = redSubst* (IdRed* (escapeTerm {l = l} (ℕᵣ [B]) [v]) (escapeTerm {l = l} (ℕᵣ [B]) [w]) D₁) ([Id]ℕ ⊢Γ [v]′ [w]′)
      [IDAtu] = [Id]ℕGen ⊢Γ [A] [t] [u]
      [IDBvw] = [Id]ℕGen ⊢Γ [B] [v] [w]
      [IdA≡U]′ = irrelevanceEq {A = Id A t u} {B = Id ℕ t u} [IdA] [IDAtu] [IdA≡U]
      [IdB≡U]′ = irrelevanceEq {A = Id B v w} {B = Id ℕ v w} {l = l} {l′ = l}  [IdB] [IDBvw] [IdB≡U]
  in  transEq {A = Id A t u} {B = Id ℕ t u} {C = Id B v w} {l = l} {l′ = l} {l″ = l} [IDAtu] ([Id]ℕ ⊢Γ [t]′ [u]′) [IDBvw]
              [IdA≡U]′
              (transEq {A = Id ℕ t u} {B = Id ℕ v w} {C = Id B v w} {l = l} {l′ = l} {l″ = l} ([Id]ℕ ⊢Γ [t]′ [u]′) ([Id]ℕ ⊢Γ [v]′ [w]′) [IDBvw]
                       X (symEq {A = Id B v w} {B = Id ℕ v w} [IDBvw] ([Id]ℕ ⊢Γ [v]′ [w]′) [IdB≡U]′))


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

open import M-types.Base

module M-types.Coalg.Bisim (A : Ty ℓ) (B : A → Ty ℓ) where
    open import M-types.Coalg.Core A B

    TyBisim : Coalg → Ty (ℓ-suc ℓ)
    TyBisim X =
        ∑[ coalg ∈ Coalg ]
        CoalgMor coalg X × CoalgMor coalg X

    coalg = pr₀

    spanRel : {X : Coalg} →
        ∏[ ∼ ∈ TyBisim X ] SpanRel (ty X)
    spanRel (coalg , ρ₀ , ρ₁) = (ty coalg , fun ρ₀ , fun ρ₁)

    tyLift : {X : Coalg} →
        ∏[ ∼ ∈ TyBisim X ] ∏[ x₀ ∈ ty X ] ∏[ x₁ ∈ ty X ]
        (x₀ ⟨ spanRel {X} ∼ ⟩ x₁ → (obs X x₀) ⟨ P-SpanRel (spanRel {X} ∼) ⟩ (obs X x₁))
    tyLift ∼ x₀ x₁ (s , refl , refl) =
        (
            obs (coalg ∼) s ,
            ≡-apply (com (ρ₀ ∼)⁻¹) s ,
            ≡-apply (com (ρ₁ ∼)⁻¹) s
        )

    ≡-TyBisim : {X : Coalg} →
        TyBisim X
    ≡-TyBisim {X} =
        (
            X ,
            (id , refl) ,
            (id , refl)
        )

    P-TyBisim : {X : Coalg} →
        TyBisim X → TyBisim (P-Coalg X)
    P-TyBisim {X} ∼ =
        (
            P-Coalg (coalg ∼) ,
            P-CoalgMor {coalg ∼} {X} (ρ₀ ∼) ,
            P-CoalgMor {coalg ∼} {X} (ρ₁ ∼)
        )

    TyBisimMor : {X : Coalg} →
        TyBisim X → TyBisim X → Ty ℓ
    TyBisimMor {X} ∼ ≈ =
        ∑[ mor ∈ SpanRelMor (spanRel {X} ∼) (spanRel {X} ≈) ]
        obs (coalg ≈) ∘ fun mor ≡ P-Fun (fun mor) ∘ obs (coalg ∼)

    FunBisim : Coalg → Ty (ℓ-suc ℓ)
    FunBisim X =
        ∑[ depRel ∈ (ty X → ty X → Ty ℓ) ]
        ∏[ x₀ ∈ ty X ] ∏[ x₁ ∈ ty X ]
        (depRel x₀ x₁ → (P-DepRel depRel) (obs X x₀) (obs X x₁))

    depRel : {X : Coalg} →
        ∏[ ∼ ∈ FunBisim X ] DepRel (ty X)
    depRel = pr₀

    funLift : {X : Coalg} →
        ∏[ ∼ ∈ FunBisim X ] ∏[ d₀ ∈ ty X ] ∏[ d₁ ∈ ty X ]
        (d₀ [ depRel ∼ ] d₁ → (obs X d₀) [ P-DepRel (depRel ∼) ] (obs X d₁))
    funLift = pr₁

    ≡-funLift : {X : Coalg} →
        ∏[ x₀ ∈ ty X ] ∏[ x₁ ∈ ty X ]
        (≡-depRel x₀ x₁ → P-DepRel ≡-depRel (obs X x₀) (obs X x₁))
    ≡-funLift x x refl = (refl , λ b → refl)

    ≡-funBisim : {X : Coalg} →
        FunBisim X
    ≡-funBisim {X} = (≡-depRel , ≡-funLift)

    P-FunBisim : {X : Coalg} →
        FunBisim X → FunBisim (P-Coalg X)
    P-FunBisim {X} ∼ =
        (
            P-DepRel (depRel ∼) ,
            λ (a₀ , d₀) → λ (a₁ , d₁) → λ (p , e) → (
                p ,
                λ b₀ → pr₁ ∼ (d₀ b₀) (d₁ (tra B p b₀)) (e b₀)
            )
        )

    FunBisimMor : {X : Coalg} →
        FunBisim X → FunBisim X → Ty ℓ
    FunBisimMor {X} ∼ ≈ =
        ∑[ mor ∈ DepRelMor (depRel ∼) (depRel ≈) ]
        ∏[ x₀ ∈ ty X ] ∏[ x₁ ∈ ty X ]
        funLift ≈ x₀ x₁ ∘ mor x₀ x₁ ≡
        P-DepRelMor mor (obs X x₀) (obs X x₁) ∘ funLift ∼ x₀ x₁

    fun-tra : {a₀ a₁ : A} {C : Ty ℓ} →
        ∏[ p ∈ a₀ ≡ a₁ ] ∏[ f₁ ∈ (B a₁ → C) ]
        tra (λ a → (B a → C)) p (f₁ ∘ (tra B p)) ≡ f₁
    fun-tra refl f₁ = refl

    apply-pr₁ : {X : Coalg} {(a₀ , d₀) (a₁ , d₁) : P (ty X)} →
        ∏[ p ∈ (a₀ , d₀) ≡ (a₁ , d₁) ] ∏[ b₀ ∈ B a₀ ]
        d₀ b₀ ≡ d₁ (tra B (ap pr₀ p) b₀)
    apply-pr₁ refl b = refl

    TyBisim→FunBisim : {X : Coalg} →
        TyBisim X → FunBisim X
    TyBisim→FunBisim {X} ∼ =
        (
            (λ x₀ → λ x₁ → x₀ ⟨ spanRel {X} ∼ ⟩ x₁) ,
            (λ x₀ → λ x₁ → λ {(s , refl , refl) → let
                q₀ :
                    obs X x₀ ≡ (
                        pr₀ (obs (coalg ∼) s) ,
                        fun (ρ₀ ∼) ∘ pr₁ (obs (coalg ∼) s)
                    )
                q₀ = ≡-apply (com (ρ₀ ∼)) s

                q₁ :
                    obs X x₁ ≡ (
                        pr₀ (obs (coalg ∼) s) ,
                        fun (ρ₁ ∼) ∘ pr₁ (obs (coalg ∼) s)
                    )
                q₁ = ≡-apply (com (ρ₁ ∼)) s
            in (
                 (ap pr₀ q₀) · (ap pr₀ q₁) ⁻¹ ,
                 λ b₀ → (
                     pr₁ (obs (coalg ∼) s) (tra B (ap pr₀ q₀) b₀) ,
                     (apply-pr₁ {X} q₀ b₀) ⁻¹ ,
                     (apply-pr₁ {X} (q₁ ⁻¹) (tra B (ap pr₀ q₀) b₀)) ·
                     ap (pr₁ (obs X x₁)) (≡-apply (
                         tra-con B (ap pr₀ q₀) (ap pr₀ (q₁ ⁻¹)) ·
                         ap (λ r → tra B (ap pr₀ q₀ · r)) ((ap-inv pr₀ q₁) ⁻¹)
                     ) b₀)
                 )
            )})
        )

    FunBisim→TyBisim : {X : Coalg} →
        FunBisim X → TyBisim X
    FunBisim→TyBisim {X} ∼ =
        (
            (
                (∑[ x₀ ∈ ty X ] ∑[ x₁ ∈ ty X ] x₀ [ depRel ∼ ] x₁) ,
                λ (x₀ , x₁ , s) → (
                    pr₀ (obs X x₀) ,
                    λ b₀ → (
                        pr₁ (obs X x₀) b₀ ,
                        pr₁ (obs X x₁) (tra B (pr₀ (funLift ∼ x₀ x₁ s)) b₀) ,
                        pr₁ (funLift ∼ x₀ x₁ s) b₀
                    )
                )
            ) , (
                pr₀ ,
                funext (λ (x₀ , x₁ , s) → refl)
            ) , (
                pr₀ ∘ pr₁ ,
                funext (λ (x₀ , x₁ , s) → ≡-pair (
                    pr₀ (funLift ∼ x₀ x₁ s) ,
                    fun-tra (pr₀ (funLift ∼ x₀ x₁ s)) (pr₁ (obs X x₁))
                ) ⁻¹)
            )
        )

    TyBisim→FunBisim-pres : {X : Coalg} →
        ∏[ ∼ ∈ TyBisim X ] ∏[ x₀ ∈ ty X ] ∏[ x₁ ∈ ty X ]
        (x₀ [ depRel {X} (TyBisim→FunBisim {X} ∼) ] x₁) ≡ (x₀ ⟨ spanRel {X} ∼ ⟩ x₁)
    TyBisim→FunBisim-pres {X} ∼ x₀ x₁ = SpanRel→DepRel-pres (spanRel {X} ∼) x₀ x₁

    FunBisim→TyBisim-pres : {X : Coalg} →
        ∏[ ∼ ∈ FunBisim X ] ∏[ x₀ ∈ ty X ] ∏[ x₁ ∈ ty X ]
        (x₀ ⟨ spanRel {X} (FunBisim→TyBisim {X} ∼) ⟩ x₁) ≃ (x₀ [ depRel {X} ∼ ] x₁)
    FunBisim→TyBisim-pres ∼ x₀ x₁ = DepRel→SpanRel-pres (depRel ∼) x₀ x₁


-- Proof that consistent negative axioms do not jeopardize canonicity.
-- https://www.cs.bham.ac.uk/~mhe/papers/negative-axioms.pdf

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

module Application.NegativeAxioms.Canonicity where

open import Definition.Untyped as U

open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.Weakening as T
open import Definition.Typed.Consequences.Inequality
open import Definition.Typed.Consequences.Injectivity
open import Definition.Typed.Consequences.Substitution
open import Definition.Typed.Consequences.Syntactic

open import Definition.Conversion.Consequences.Completeness
open import Definition.Conversion.FullReduction

open import Tools.Empty
open import Tools.Fin
open import Tools.Nat
open import Tools.Product
open import Tools.Sum using (_⊎_; inj₁; inj₂)
open import Tools.Unit

-- Preliminaries
---------------------------------------------------------------------------

private
  Ty  = Term
  Cxt = Con Ty
  variable
    m m'  : Nat
    x     : Fin m
    ρ     : Wk m m'
    σ     : Subst m m'
    Γ Δ   : Con Term m
    A B C : Term m
    t u   : Term m

-- Numerals

data Numeral {m : Nat} : Term m → Set where
  zeroₙ : Numeral zero
  sucₙ  : Numeral t → Numeral (suc t)

-- Negative types
---------------------------------------------------------------------------

-- A type is negative if all of its branches end in ⊥.
-- The prime example is negation ¬A.

data NegativeType (Γ : Cxt m) : Ty m → Set where

  empty : NegativeType Γ Empty

  pi    : Γ ⊢ A
        → NegativeType (Γ ∙ A) B
        → NegativeType Γ (Π A ▹ B)

  sigma : Γ ⊢ A
        → NegativeType Γ A
        → NegativeType (Γ ∙ A) B
        → NegativeType Γ (Σ A ▹ B)

  conv  : NegativeType Γ A
        → Γ ⊢ A ≡ B
        → NegativeType Γ B

-- Lemma: Negative types are closed under weakening.

wkNeg : ρ ∷ Δ ⊆ Γ → ⊢ Δ → NegativeType Γ A → NegativeType Δ (U.wk ρ A)

wkNeg w ⊢Δ empty
  = empty

wkNeg w ⊢Δ (pi dA nB)
  = pi dA' (wkNeg (lift w) (⊢Δ ∙ dA') nB)
    where dA' = T.wk w ⊢Δ dA

wkNeg w ⊢Δ (sigma dA nA nB)
  = sigma dA' (wkNeg w ⊢Δ nA) (wkNeg (lift w) (⊢Δ ∙ dA') nB)
    where dA' = T.wk w ⊢Δ dA

wkNeg w ⊢Δ (conv n c)
  = conv (wkNeg w ⊢Δ n) (wkEq w ⊢Δ c)

-- Lemma: Negative types are closed under parallel substitution.

subNeg : NegativeType Γ A → Δ ⊢ˢ σ ∷ Γ → ⊢ Δ → NegativeType Δ (subst σ A)

subNeg empty _ _ = empty

subNeg (pi ⊢A n) s ⊢Δ
  = pi ⊢σA (subNeg n (liftSubst′ (wf ⊢A) ⊢Δ ⊢A s) (⊢Δ ∙ ⊢σA))
    where ⊢σA = substitution ⊢A s ⊢Δ

subNeg (sigma ⊢A nA nB) s ⊢Δ
  = sigma ⊢σA (subNeg nA s ⊢Δ) (subNeg nB (liftSubst′ (wf ⊢A) ⊢Δ ⊢A s) (⊢Δ ∙ ⊢σA))
    where ⊢σA = substitution ⊢A s ⊢Δ

subNeg (conv n c) s ⊢Δ = conv (subNeg n s ⊢Δ) (substitutionEq c (substRefl s) ⊢Δ)

-- Corollary: Negative types are closed under single substitution.

subNeg1 : NegativeType (Γ ∙ A) B → Γ ⊢ t ∷ A → NegativeType Γ (B [ t ])
subNeg1 n ⊢t = subNeg n (singleSubst ⊢t) (wfTerm ⊢t)

-- Lemma: The first component of a negative Σ-type is negative.

fstNeg : NegativeType Γ C → Γ ⊢ C ≡ Σ A ▹ B → NegativeType Γ A
fstNeg empty          c = ⊥-elim (Empty≢Σⱼ c)
fstNeg (pi _ _)       c = ⊥-elim (Π≢Σ c)
fstNeg (sigma _ nA _) c = conv nA (proj₁ (Σ-injectivity c))
fstNeg (conv n c)    c' = fstNeg n (trans c c')

-- Lemma: Any instance of the second component of a negative Σ-type is negative.

sndNeg : NegativeType Γ C → Γ ⊢ C ≡ Σ A ▹ B → Γ ⊢ t ∷ A → NegativeType Γ (B [ t ])
sndNeg empty          c = ⊥-elim (Empty≢Σⱼ c)
sndNeg (pi _ _)       c = ⊥-elim (Π≢Σ c)
sndNeg (sigma _ _ nB) c ⊢t = let (cA , cB) = Σ-injectivity c in
  subNeg (conv nB cB) (singleSubst (conv ⊢t (sym cA))) (wfTerm ⊢t)
sndNeg (conv n c)    c' = sndNeg n (trans c c')

-- Lemma: Any instance of the codomain of a negative Π-type is negative.

appNeg : NegativeType Γ C → Γ ⊢ C ≡ Π A ▹ B → Γ ⊢ t ∷ A → NegativeType Γ (B [ t ])
appNeg empty          c = ⊥-elim (Empty≢Πⱼ c)
appNeg (sigma _ _ _)  c = ⊥-elim (Π≢Σ (sym c))
appNeg (pi _ nB) c ⊢t = let (cA , cB) = injectivity c in
  subNeg (conv nB cB) (singleSubst (conv ⊢t (sym cA))) (wfTerm ⊢t)
appNeg (conv n c)    c' = appNeg n (trans c c')

-- Lemma: The type ℕ is not negative.

¬negℕ : NegativeType Γ C → Γ ⊢ C ≡ ℕ → ⊥
¬negℕ empty         c = ℕ≢Emptyⱼ (sym c)
¬negℕ (pi _ _)      c = ℕ≢Π (sym c)
¬negℕ (sigma _ _ _) c = ℕ≢Σ (sym c)
¬negℕ (conv n c)   c' = ¬negℕ n (trans c c')

-- Negative contexts
---------------------------------------------------------------------------

-- A context is negative if all of its type entries are negative.

data NegativeContext : Con Ty m → Set where
  ε   : NegativeContext ε
  _∙_ : NegativeContext Γ → NegativeType Γ A → NegativeContext (Γ ∙ A)

-- Lemma: Any entry in negative context is a negative type (needs weakening).

lookupNegative : ⊢ Γ → NegativeContext Γ → (x ∷ A ∈ Γ) → NegativeType Γ A
lookupNegative ⊢Γ∙A            (nΓ ∙ nA) here
  = wkNeg (step id) ⊢Γ∙A nA
lookupNegative ⊢Γ∙A@(⊢Γ ∙ Γ⊢A) (nΓ ∙ nA) (there h)
  = wkNeg (step id) ⊢Γ∙A (lookupNegative ⊢Γ nΓ h)

-- Main results
---------------------------------------------------------------------------

-- We assume a negative, consistent context.

module Main (nΓ : NegativeContext Γ) (consistent : ∀{t} → Γ ⊢ t ∷ Empty → ⊥) where

  -- Lemma: A neutral has negative type in a consistent negative context.

  neNeg : (d : Γ ⊢ u ∷ A) (n : NfNeutral u) → NegativeType Γ A
  neNeg (var ⊢Γ h       ) (var _           ) = lookupNegative ⊢Γ nΓ h
  neNeg (d ∘ⱼ ⊢t        ) (∘ₙ n _          ) = appNeg (neNeg d n) (refl (syntacticTerm d)) ⊢t
  neNeg (fstⱼ ⊢A A⊢B d  ) (fstₙ n          ) = fstNeg (neNeg d n) (refl (Σⱼ ⊢A ▹ A⊢B))
  neNeg (sndⱼ ⊢A A⊢B d  ) (sndₙ n          ) = sndNeg (neNeg d n) (refl (Σⱼ ⊢A ▹ A⊢B)) (fstⱼ ⊢A A⊢B d)
  neNeg (natrecⱼ _ _ _ d) (natrecₙ _ _ _ n ) = ⊥-elim (¬negℕ (neNeg d n) ⊢ℕ) where ⊢ℕ = refl (ℕⱼ (wfTerm d))
  neNeg (Emptyrecⱼ _ d  ) (Emptyrecₙ _ _   ) = ⊥-elim (consistent d)
  neNeg (conv d c       ) n                  = conv (neNeg d n) c

  -- Lemma: A normal form of type ℕ is a numeral in a consistent negative context.

  nfN : (d : Γ ⊢ u ∷ A)
      → (n : Nf u)
      → (c : Γ ⊢ A ≡ ℕ)
      → Numeral u

  -- Case: neutrals. The type cannot be ℕ since it must be negative.
  nfN d (ne n) c = ⊥-elim (¬negℕ (neNeg d n) c)

  -- Case: numerals.
  nfN (zeroⱼ x) zeroₙ   c = zeroₙ
  nfN (sucⱼ d) (sucₙ n) c = sucₙ (nfN d n c)

  -- Case: conversion.
  nfN (conv d c) n c' = nfN d n (trans c c')

  -- Impossible cases: type is not ℕ.

  -- * Canonical types
  nfN (Πⱼ _ ▹ _)      (Πₙ _ _)    c = ⊥-elim (U≢ℕ c)
  nfN (Σⱼ _ ▹ _)      (Σₙ _ _)    c = ⊥-elim (U≢ℕ c)
  nfN (ℕⱼ _)           ℕₙ         c = ⊥-elim (U≢ℕ c)
  nfN (Emptyⱼ _)       Emptyₙ     c = ⊥-elim (U≢ℕ c)
  nfN (Unitⱼ _)        Unitₙ      c = ⊥-elim (U≢ℕ c)

  -- * Canonical forms
  nfN (lamⱼ _ _)      (lamₙ _)    c = ⊥-elim (ℕ≢Π (sym c))
  nfN (prodⱼ _ _ _ _) (prodₙ _ _) c = ⊥-elim (ℕ≢Σ (sym c))
  nfN (starⱼ _)       starₙ       c = ⊥-elim (ℕ≢Unitⱼ (sym c))
  -- q.e.d

  -- Canonicity theorem: Any well-typed term Γ ⊢ t : ℕ is convertible to a numeral.

  thm : (⊢t : Γ ⊢ t ∷ ℕ) → ∃ λ u → Numeral u × Γ ⊢ t ≡ u ∷ ℕ

  thm ⊢t with fullRedTerm (completeEqTerm (refl ⊢t))
  ... | u , nf , eq = u , nfN (proj₂ (proj₂ (syntacticEqTerm eq))) nf (refl (ℕⱼ (wfTerm ⊢t))) , eq

-- Q.E.D. 2021-05-27