-- Actually, we're cheating (for expediency); this is
-- Symmetric Rig, not just Rig.
module Categories.RigCategory where

open import Level
open import Data.Fin renaming (zero to 0F; suc to sucF)
open import Data.Product using (_,_)

open import Categories.Category
open import Categories.Functor
open import Categories.Monoidal
open import Categories.Monoidal.Braided
open import Categories.Monoidal.Braided.Helpers
open import Categories.Monoidal.Symmetric
open import Categories.Monoidal.Helpers
open import Categories.NaturalIsomorphism
open import Categories.NaturalTransformation using (_∘₀_; _∘₁_; _∘ˡ_; _∘ʳ_; NaturalTransformation) renaming (_≡_ to _≡ⁿ_; id to idⁿ)

-- first round of setting up, just for defining the NaturalIsomorphisms
-- of Rig.  Next one will be for the equalities.
module BimonoidalHelperFunctors {o ℓ e} {C : Category o ℓ e}
  {M⊎ M× : Monoidal C} (B⊎ : Braided M⊎) (B× : Braided M×) where

  private
    module C = Category C
    module M⊎ = Monoidal M⊎
    module M× = Monoidal M×
    module B⊎ = Braided B⊎
    module B× = Braided B×

  module h⊎ = MonoidalHelperFunctors C M⊎.⊗ M⊎.id
  module h× = MonoidalHelperFunctors C M×.⊗ M×.id
  module b⊎ = BraidedHelperFunctors C M⊎.⊗ M⊎.id
  module b× = BraidedHelperFunctors C M×.⊗ M×.id

  module br⊎ = b⊎.Braiding M⊎.identityˡ M⊎.identityʳ M⊎.assoc B⊎.braid
  module br× = b×.Braiding M×.identityˡ M×.identityʳ M×.assoc B×.braid

  import Categories.Power.NaturalTransformation
  private module PowNat = Categories.Power.NaturalTransformation C
  open PowNat hiding (module C)

  -- for convenience of multiple of the larger equations, define
  -- shorthands for 3 and 4 variables
  x⊗z : Powerendo 3
  x⊗z = select 0 h×.⊗₂ select 2

  z⊗x : Powerendo 3
  z⊗x = (select 2) h×.⊗₂ (select 0)

  z⊗y : Powerendo 3
  z⊗y = select 2 h×.⊗₂ select 1

  y⊗z : Powerendo 3
  y⊗z = select 1 h×.⊗₂ select 2

  x : Powerendo 4
  x = select 0

  y : Powerendo 4
  y = select 1

  z : Powerendo 4
  z = select 2

  w : Powerendo 4
  w = select 3

  -- combinations of 3 variables
  x⊗[y⊕z] : Powerendo 3
  x⊗[y⊕z] = h×.x h×.⊗ (h⊎.x⊗y)

  x⊗[z⊕y] : Powerendo 3
  x⊗[z⊕y] = h×.x h×.⊗ h⊎.y⊗x

  [x⊕y]⊗z : Powerendo 3
  [x⊕y]⊗z = (h⊎.x⊗y) h×.⊗ h×.x

  z⊗[x⊕y] : Powerendo 3
  z⊗[x⊕y] = (select 2) h×.⊗₂ ((select 0) h⊎.⊗₂ (select 1))

  [x⊗y]⊕[x⊗z] : Powerendo 3
  [x⊗y]⊕[x⊗z] = (select 0 h×.⊗₂ select 1) h⊎.⊗₂ (select 0 h×.⊗₂ select 2)

  [x⊗z]⊕[y⊗z] : Powerendo 3
  [x⊗z]⊕[y⊗z] = (select 0 h×.⊗₂ select 2) h⊎.⊗₂ (select 1 h×.⊗₂ select 2)

  [z⊗x]⊕[z⊗y] : Powerendo 3
  [z⊗x]⊕[z⊗y] = z⊗x h⊎.⊗₂ z⊗y

  [x⊗z]⊕[x⊗y] : Powerendo 3
  [x⊗z]⊕[x⊗y] = x⊗z h⊎.⊗₂ (select 0 h×.⊗₂ select 1)

  x⊗0 : Powerendo 1
  x⊗0 = h⊎.x h×.⊗ h⊎.id↑

  0⊗x : Powerendo 1
  0⊗x = h⊎.id↑ h×.⊗ h⊎.x

  0↑ : Powerendo 1
  0↑ = widenˡ 1 h⊎.id↑

  xy : Powerendo 4
  xy = widenʳ 2 h×.x⊗y

  yz : Powerendo 4
  yz = select 1 h×.⊗₂ select 2

  xw : Powerendo 4
  xw = select 0 h×.⊗₂ select 3

  yw : Powerendo 4
  yw = select 1 h×.⊗₂ select 3

  zw : Powerendo 4
  zw = select 2 h×.⊗₂ select 3

  xz : Powerendo 4
  xz = select 0 h×.⊗₂ select 2

  z⊕w : Powerendo 4
  z⊕w = widenˡ 2 h⊎.x⊗y

  -- for 2 variables
  x₂ : Powerendo 2
  x₂ = widenʳ 1 h×.x

  y₂ : Powerendo 2
  y₂ = widenˡ 1 h×.x

  -- 0 variables!
  0₀ : Powerendo 0
  0₀ = h⊎.id↑

  0⊗0 : Powerendo 0
  0⊗0 = 0₀ h×.⊗ 0₀

  0⊕0 : Powerendo 0
  0⊕0 = 0₀ h⊎.⊗ 0₀

  1₀ : Powerendo 0
  1₀ = h×.id↑

  -- 1 variable + 0
  A0 : Powerendo 1
  A0 = h×.x h×.⊗ 0₀

  0A : Powerendo 1
  0A = 0₀ h×.⊗ h×.x

  -- 2 variables + 0
  0₂ : Powerendo 2
  0₂ = widenʳ 2 0₀

  0[A⊕B] : Powerendo 2
  0[A⊕B] = 0₂ h×.⊗₂ h⊎.x⊗y

  0[AB] : Powerendo 2
  0[AB] = 0₀ h×.⊗ (h×.x⊗y)

  [0A]B : Powerendo 2
  [0A]B = 0A h×.⊗ h×.x

  0A₂ : Powerendo 2
  0A₂ = 0₂ h×.⊗₂ select 0

  0B : Powerendo 2
  0B = 0₂ h×.⊗₂ (select 1)

  A0₂ : Powerendo 2
  A0₂ = select 0 h×.⊗₂ 0₂

  AB : Powerendo 2
  AB = h×.x⊗y

  0A⊕0B : Powerendo 2
  0A⊕0B = 0A₂ h⊎.⊗₂ 0B

  A[0B] : Powerendo 2
  A[0B] = (widenʳ 1 h×.x) h×.⊗₂ 0B

  [A0]B : Powerendo 2
  [A0]B = A0 h×.⊗ h×.x

  A[0⊕B] : Powerendo 2
  A[0⊕B] = h×.x h×.⊗ h⊎.id⊗x

  A0⊕AB : Powerendo 2
  A0⊕AB = A0₂ h⊎.⊗₂ AB

  0⊕AB : Powerendo 2
  0⊕AB = 0₂ h⊎.⊗₂ AB

  -- 2 variables + 1
  1₂ : Powerendo 2
  1₂ = widenʳ 2 1₀

  A⊕B : Powerendo 2
  A⊕B = h⊎.x⊗y

  1[A⊕B] : Powerendo 2
  1[A⊕B] = 1₀ h×.⊗ A⊕B

  1A⊕1B : Powerendo 2
  1A⊕1B = h×.id⊗x h⊎.⊗ h×.id⊗x

  -- like Laplaza, use concatenation for ⊗ to make things easier to read
  -- also ⊗ binds more tightly, so skip those parens

  -- combinations of 4 variables
  [x⊕[y⊕z]]w : Powerendo 4
  [x⊕[y⊕z]]w = h⊎.x⊗[y⊗z] h×.⊗ h⊎.x

  xw⊕[y⊕z]w : Powerendo 4
  xw⊕[y⊕z]w = (x h×.⊗₂ w) h⊎.⊗₂ ((y h⊎.⊗₂ z) h×.⊗₂ w)

  xw⊕[yw⊕zw] : Powerendo 4
  xw⊕[yw⊕zw] = (x h×.⊗₂ w) h⊎.⊗₂ ((y h×.⊗₂ w) h⊎.⊗₂ (z h×.⊗₂ w))

  [xw⊕yw]⊕zw : Powerendo 4
  [xw⊕yw]⊕zw = ((x h×.⊗₂ w) h⊎.⊗₂ (y h×.⊗₂ w)) h⊎.⊗₂ (z h×.⊗₂ w)

  [[x⊕y]⊕z]w : Powerendo 4
  [[x⊕y]⊕z]w = h⊎.[x⊗y]⊗z h×.⊗ h⊎.x

  [x⊕y]w⊕zw : Powerendo 4
  [x⊕y]w⊕zw = ((x h⊎.⊗₂ y) h×.⊗₂ w) h⊎.⊗₂ (z h×.⊗₂ w)

  x[y[z⊕w]] : Powerendo 4
  x[y[z⊕w]] = x h×.⊗₂ (y h×.⊗₂ (z h⊎.⊗₂ w))

  x[yz⊕yw] : Powerendo 4
  x[yz⊕yw] = x h×.⊗₂ ((y h×.⊗₂ z) h⊎.⊗₂ (y h×.⊗₂ w))

  x[yz]⊕x[yw] : Powerendo 4
  x[yz]⊕x[yw] = (x h×.⊗₂ (y h×.⊗₂ z)) h⊎.⊗₂ (x h×.⊗₂ (y h×.⊗₂ w))

  [xy][z⊕w] : Powerendo 4
  [xy][z⊕w] = (x h×.⊗₂ y) h×.⊗₂ (z h⊎.⊗₂ w)

  [xy]z⊕[xy]w : Powerendo 4
  [xy]z⊕[xy]w = let xy = (x h×.⊗₂ y) in (xy h×.⊗₂ z) h⊎.⊗₂ (xy h×.⊗₂ w)

  x⊕y : Powerendo 4
  x⊕y = widenʳ 2 h⊎.x⊗y

  x[z⊕w] : Powerendo 4
  x[z⊕w] = x h×.⊗₂ z⊕w

  y[z⊕w] : Powerendo 4
  y[z⊕w] = y h×.⊗₂ z⊕w

  xz⊕xw : Powerendo 4
  xz⊕xw = xz h⊎.⊗₂ xw

  yz⊕yw : Powerendo 4
  yz⊕yw = yz h⊎.⊗₂ yw

  xz⊕yz : Powerendo 4
  xz⊕yz = xz h⊎.⊗₂ yz

  xw⊕yw : Powerendo 4
  xw⊕yw = xw h⊎.⊗₂ yw

  [x⊕y]z : Powerendo 4
  [x⊕y]z = x⊕y h×.⊗₂ z

  [x⊕y]w : Powerendo 4
  [x⊕y]w = x⊕y h×.⊗₂ w

  [x⊕y][z⊕w] : Powerendo 4
  [x⊕y][z⊕w] = x⊕y h×.⊗₂ z⊕w

  [x⊕y]z⊕[x⊕y]w : Powerendo 4
  [x⊕y]z⊕[x⊕y]w = [x⊕y]z h⊎.⊗₂ [x⊕y]w

  [xz⊕yz]⊕[xw⊕yw] : Powerendo 4
  [xz⊕yz]⊕[xw⊕yw] = xz⊕yz h⊎.⊗₂ xw⊕yw

  [[xz⊕yz]⊕xw]⊕yw : Powerendo 4
  [[xz⊕yz]⊕xw]⊕yw = (xz⊕yz h⊎.⊗₂ xw) h⊎.⊗₂ yw

  x[z⊕w]⊕y[z⊕w] : Powerendo 4
  x[z⊕w]⊕y[z⊕w] = x[z⊕w] h⊎.⊗₂ y[z⊕w]

  [xz⊕xw]⊕[yz⊕yw] : Powerendo 4
  [xz⊕xw]⊕[yz⊕yw] = xz⊕xw h⊎.⊗₂ yz⊕yw

  [[xz⊕xw]⊕yz]⊕yw : Powerendo 4
  [[xz⊕xw]⊕yz]⊕yw = (xz⊕xw h⊎.⊗₂ yz) h⊎.⊗₂ yw

  [xz⊕[xw⊕yz]]⊕yw : Powerendo 4
  [xz⊕[xw⊕yz]]⊕yw = (xz h⊎.⊗₂ (xw h⊎.⊗₂ yz)) h⊎.⊗₂ yw

  [xz⊕[yz⊕xw]]⊕yw : Powerendo 4
  [xz⊕[yz⊕xw]]⊕yw = (xz h⊎.⊗₂ (yz h⊎.⊗₂ xw)) h⊎.⊗₂ yw

  module SRig (S⊎ : Symmetric B⊎) (S× : Symmetric B×)
    (distribₗ : NaturalIsomorphism x⊗[y⊕z] [x⊗y]⊕[x⊗z])
    (distribᵣ : NaturalIsomorphism [x⊕y]⊗z [x⊗z]⊕[y⊗z])
    (annₗ : NaturalIsomorphism 0⊗x 0↑)
    (annᵣ : NaturalIsomorphism x⊗0 0↑) where

    open NaturalIsomorphism distribₗ using () renaming (F⇒G to dₗ)
    open NaturalIsomorphism distribᵣ using () renaming (F⇒G to dᵣ)
    open NaturalIsomorphism annₗ using () renaming (F⇒G to aₗ)
    open NaturalIsomorphism annᵣ using () renaming (F⇒G to aᵣ)

    dₗ-over : ∀ {n} (F₁ F₂ F₃ : Powerendo n) → NaturalTransformation (F₁ h×.⊗₂ (F₂ h⊎.⊗₂ F₃)) ((F₁ h×.⊗₂ F₂) h⊎.⊗₂ (F₁ h×.⊗₂ F₃))
    dₗ-over F₁ F₂ F₃ = dₗ ∘ʳ plex {3} F₁ F₂ F₃

    dᵣ-over : ∀ {n} (F₁ F₂ F₃ : Powerendo n) → NaturalTransformation ((F₁ h⊎.⊗₂ F₂) h×.⊗₂ F₃) ((F₁ h×.⊗₂ F₃) h⊎.⊗₂ (F₂ h×.⊗₂ F₃))
    dᵣ-over F₁ F₂ F₃ = dᵣ ∘ʳ plex {3} F₁ F₂ F₃

    aₗ-over : ∀ {n} (F₁ : Powerendo n) → NaturalTransformation ((widenʳ n 0₀) h×.⊗₂ F₁) (widenʳ n 0₀)
    aₗ-over F₁ = aₗ ∘ʳ plex {1} F₁

    aᵣ-over : ∀ {n} (F₁ : Powerendo n) → NaturalTransformation (F₁ h×.⊗₂ (widenʳ n 0₀)) (widenʳ n 0₀)
    aᵣ-over F₁ = aᵣ ∘ʳ plex {1} F₁

    uₗ⊕-over : ∀ {n} (F₁ : Powerendo n) → NaturalTransformation ((widenʳ n 0₀) h⊎.⊗₂ F₁) F₁
    uₗ⊕-over F₁ = (NaturalIsomorphism.F⇒G M⊎.identityˡ) ∘ʳ plex {1} F₁

    uₗ⊗-over : ∀ {n} (F₁ : Powerendo n) → NaturalTransformation ((widenʳ n 1₀) h×.⊗₂ F₁) F₁
    uₗ⊗-over F₁ = (NaturalIsomorphism.F⇒G M×.identityˡ) ∘ʳ plex {1} F₁

    uᵣ⊗-over : ∀ {n} (F₁ : Powerendo n) → NaturalTransformation (F₁ h×.⊗₂ (widenʳ n 1₀)) F₁
    uᵣ⊗-over F₁ = (NaturalIsomorphism.F⇒G M×.identityʳ) ∘ʳ plex {1} F₁

    s⊕-over : ∀ {n} (F₁ F₂ : Powerendo n) → NaturalTransformation (F₁ h⊎.⊗₂ F₂) (F₂ h⊎.⊗₂ F₁)
    s⊕-over F₁ F₂ = (NaturalIsomorphism.F⇒G B⊎.braid) ∘ʳ plex {2} F₁ F₂

    s⊗-over : ∀ {n} (F₁ F₂ : Powerendo n) → NaturalTransformation (F₁ h×.⊗₂ F₂) (F₂ h×.⊗₂ F₁)
    s⊗-over F₁ F₂ = (NaturalIsomorphism.F⇒G B×.braid) ∘ʳ plex {2} F₁ F₂

    -- for 2 variables
    idx₂ : NaturalTransformation x₂ x₂
    idx₂ = idⁿ

    idy₂ : NaturalTransformation y₂ y₂
    idy₂ = idⁿ

    idxy : NaturalTransformation AB AB
    idxy = idⁿ

    -- these are all for 3 variables
    Bxz : NaturalTransformation x⊗z z⊗x
    Bxz = br×.B-over (select 0) (select 2)

    Byz : NaturalTransformation y⊗z z⊗y
    Byz = br×.B-over (select 1) (select 2)

    Bxz⊕Byz : NaturalTransformation [x⊗z]⊕[y⊗z] [z⊗x]⊕[z⊗y]
    Bxz⊕Byz = Bxz h⊎.⊗ⁿ′ Byz

    B[x⊕y]z : NaturalTransformation [x⊕y]⊗z z⊗[x⊕y]
    B[x⊕y]z = br×.B-over (widenʳ 1 h⊎.x⊗y) (select 2)

    dᵣABC : NaturalTransformation [x⊕y]⊗z [x⊗z]⊕[y⊗z]
    dᵣABC = dᵣ-over (select 0) (select 1) (select 2)

    dₗABC : NaturalTransformation x⊗[y⊕z] [x⊗y]⊕[x⊗z]
    dₗABC = dₗ-over (select 0) (select 1) (select 2)

    dₗACB : NaturalTransformation x⊗[z⊕y] [x⊗z]⊕[x⊗y]
    dₗACB = dₗ-over (select 0) (select 2) (select 1)

    dₗCAB : NaturalTransformation z⊗[x⊕y] [z⊗x]⊕[z⊗y]
    dₗCAB = dₗ-over (select 2) (select 0) (select 1)

    1⊗Byz : NaturalTransformation x⊗[y⊕z] x⊗[z⊕y]
    1⊗Byz = reduceN M×.⊗ h×.id₂ (br⊎.B-over (select 0) (select 1))

    B[x⊗y][x⊗z] : NaturalTransformation [x⊗y]⊕[x⊗z] [x⊗z]⊕[x⊗y]
    B[x⊗y][x⊗z] = br⊎.B-over (widenʳ 1 h×.x⊗y) x⊗z

    -- these are all for 4 variables
    dᵣA[B⊕C]D : NaturalTransformation [x⊕[y⊕z]]w xw⊕[y⊕z]w
    dᵣA[B⊕C]D = dᵣ-over (select 0) (widenʳ 1 (widenˡ 1 h⊎.x⊗y)) (select 3)

    dᵣBCD : NaturalTransformation (widenˡ 1 [x⊕y]⊗z) (widenˡ 1 [x⊗z]⊕[y⊗z])
    dᵣBCD = dᵣ-over (select 1) (select 2) (select 3)

    id03 : NaturalTransformation xw xw
    id03 = idⁿ

    id23 : NaturalTransformation zw zw
    id23 = idⁿ

    idx : NaturalTransformation x x
    idx = idⁿ

    idw : NaturalTransformation w w
    idw = idⁿ

    idyw : NaturalTransformation yw yw
    idyw = idⁿ

    idxz : NaturalTransformation xz xz
    idxz = idⁿ

    1⊗dᵣBCD : NaturalTransformation xw⊕[y⊕z]w xw⊕[yw⊕zw]
    1⊗dᵣBCD = overlapN M⊎.⊗ id03 dᵣBCD

    assocˡAD-BD-CD : NaturalTransformation xw⊕[yw⊕zw] [xw⊕yw]⊕zw
    assocˡAD-BD-CD = br⊎.α₂-over xw yw zw

    αˡABC⊗1 : NaturalTransformation [x⊕[y⊕z]]w [[x⊕y]⊕z]w
    αˡABC⊗1 = overlapN M×.⊗ (br⊎.α₂-over (select 0) (select 1) (select 2)) idw

    dᵣ[A⊕B]CD : NaturalTransformation [[x⊕y]⊕z]w [x⊕y]w⊕zw
    dᵣ[A⊕B]CD = dᵣ-over (widenʳ 2 h⊎.x⊗y) (select 2) (select 3)

    dᵣABD⊗1 : NaturalTransformation [x⊕y]w⊕zw [xw⊕yw]⊕zw
    dᵣABD⊗1 = overlapN M⊎.⊗ (dᵣ-over (select 0) (select 1) (select 3)) id23

    1A⊗dₗBCD : NaturalTransformation x[y[z⊕w]] x[yz⊕yw]
    1A⊗dₗBCD = overlapN M×.⊗ idx (dₗ-over y z w)

    dₗA[BC][BD] : NaturalTransformation x[yz⊕yw] x[yz]⊕x[yw]
    dₗA[BC][BD] = dₗ-over x yz yw

    αABC⊕αABD : NaturalTransformation x[yz]⊕x[yw] [xy]z⊕[xy]w
    αABC⊕αABD = overlapN M⊎.⊗ (br×.α₂-over x y z) (br×.α₂-over x y w)

    αAB[C⊕D] : NaturalTransformation x[y[z⊕w]] [xy][z⊕w]
    αAB[C⊕D] = br×.α₂-over x y z⊕w

    dₗ[AB]CD : NaturalTransformation [xy][z⊕w] [xy]z⊕[xy]w
    dₗ[AB]CD = dₗ-over xy z w

    dₗ0AB : NaturalTransformation 0[A⊕B] 0A⊕0B
    dₗ0AB = dₗ-over (widenʳ 2 0₀) (select 0) (select 1)

    aₗA⊕aₗB : NaturalTransformation 0A⊕0B (widenʳ 2 0⊕0)
    aₗA⊕aₗB = overlapN M⊎.⊗ (aₗ-over (select 0)) (aₗ-over (select 1))

    -- a bit weird, but the widening is needed
    uˡ0 : NaturalTransformation (widenʳ 2 0⊕0) (widenʳ 2 0₀)
    uˡ0 = uₗ⊕-over (widenʳ 2 0₀)

    aᵣA : NaturalTransformation A0 0↑
    aᵣA = aᵣ-over (select 0)

    aₗA : NaturalTransformation 0A (widenʳ 1 0₀)
    aₗA = aₗ-over (select 0)

    s⊗A0 : NaturalTransformation A0 0A
    s⊗A0 = s⊗-over (select 0) 0↑

    α0AB : NaturalTransformation 0[AB] [0A]B
    α0AB = br×.α₂-over 0₂ (select 0) (select 1)

    aₗA⊗1B : NaturalTransformation [0A]B 0B
    aₗA⊗1B = overlapN M×.⊗ (aₗ-over (select 0)) idy₂

    aₗAB : NaturalTransformation 0[AB] 0₂
    aₗAB = aₗ-over h×.x⊗y

    aₗB : NaturalTransformation 0B 0₂
    aₗB = aₗ-over y₂

    αA0B : NaturalTransformation A[0B] [A0]B
    αA0B = br×.α₂-over (select 0) 0₂ (select 1)

    aᵣA⊗1B : NaturalTransformation [A0]B 0B
    aᵣA⊗1B = overlapN M×.⊗ (aᵣ-over (select 0)) idy₂

    1A⊗aₗB : NaturalTransformation A[0B] (widenʳ 1 A0)
    1A⊗aₗB = overlapN M×.⊗ idx₂ aₗB

    aᵣA₂ : NaturalTransformation (widenʳ 1 A0) 0₂
    aᵣA₂ = aᵣ-over (select 0)

    1A⊗uₗB : NaturalTransformation A[0⊕B] AB
    1A⊗uₗB = overlapN M×.⊗ idx₂ (uₗ⊕-over y₂)

    dₗA0B : NaturalTransformation A[0⊕B] A0⊕AB
    dₗA0B = dₗ-over x₂ 0₂ y₂

    aᵣA⊕1AB : NaturalTransformation A0⊕AB 0⊕AB
    aᵣA⊕1AB = overlapN M⊎.⊗ (aᵣ-over x₂) idxy

    uₗAB : NaturalTransformation 0⊕AB AB
    uₗAB = uₗ⊕-over AB

    dₗ1AB : NaturalTransformation 1[A⊕B] 1A⊕1B
    dₗ1AB = dₗ-over 1₂ x₂ y₂

    uₗA⊕B : NaturalTransformation 1[A⊕B] A⊕B
    uₗA⊕B = uₗ⊗-over h⊎.x⊗y

    uₗA⊕uₗB : NaturalTransformation 1A⊕1B A⊕B
    uₗA⊕uₗB = overlapN M⊎.⊗ (uₗ⊗-over x₂) (uₗ⊗-over y₂)

    -- the monster, aka diagram IX
    dₗ[A⊕B]CD : NaturalTransformation [x⊕y][z⊕w] [x⊕y]z⊕[x⊕y]w
    dₗ[A⊕B]CD = dₗ-over x⊕y z w

    dᵣABC⊕dᵣABD : NaturalTransformation [x⊕y]z⊕[x⊕y]w [xz⊕yz]⊕[xw⊕yw]
    dᵣABC⊕dᵣABD = overlapN M⊎.⊗ ( dᵣ-over x y z ) (dᵣ-over x y w)

    α[AC⊕BC][AD][BD] : NaturalTransformation [xz⊕yz]⊕[xw⊕yw] [[xz⊕yz]⊕xw]⊕yw
    α[AC⊕BC][AD][BD] = br⊎.α₂-over xz⊕yz xw yw

    dᵣAB[C⊕D] : NaturalTransformation [x⊕y][z⊕w] x[z⊕w]⊕y[z⊕w]
    dᵣAB[C⊕D] = dᵣ-over x y z⊕w

    dₗACD⊕dₗBCD : NaturalTransformation x[z⊕w]⊕y[z⊕w] [xz⊕xw]⊕[yz⊕yw]
    dₗACD⊕dₗBCD = overlapN M⊎.⊗ (dₗ-over x z w) (dₗ-over y z w)

    α[AC⊕AD][BC][BD] : NaturalTransformation [xz⊕xw]⊕[yz⊕yw] [[xz⊕xw]⊕yz]⊕yw
    α[AC⊕AD][BC][BD] = br⊎.α₂-over xz⊕xw yz yw

    α′[AC][AD][BC]⊕1BD : NaturalTransformation [[xz⊕xw]⊕yz]⊕yw [xz⊕[xw⊕yz]]⊕yw
    α′[AC][AD][BC]⊕1BD = overlapN M⊎.⊗ (br⊎.α-over xz xw yz) idyw

    [1AC⊕s[AD][BC]]⊕1BD : NaturalTransformation [xz⊕[xw⊕yz]]⊕yw [xz⊕[yz⊕xw]]⊕yw
    [1AC⊕s[AD][BC]]⊕1BD = overlapN M⊎.⊗ (overlapN M⊎.⊗ idxz (s⊕-over xw yz)) idyw

    α[AC][BC][AD]⊕1BD : NaturalTransformation [xz⊕[yz⊕xw]]⊕yw [[xz⊕yz]⊕xw]⊕yw
    α[AC][BC][AD]⊕1BD = overlapN M⊎.⊗ (br⊎.α₂-over xz yz xw) idyw

record RigCategory {o ℓ e} {C : Category o ℓ e}
  {M⊎ M× : Monoidal C} {B⊎ : Braided M⊎} (S⊎ : Symmetric B⊎)
   {B× : Braided M×} (S× : Symmetric B×) : Set (o ⊔ ℓ ⊔ e) where

  open BimonoidalHelperFunctors B⊎ B×

  field
    distribₗ : NaturalIsomorphism x⊗[y⊕z] [x⊗y]⊕[x⊗z]
    distribᵣ : NaturalIsomorphism [x⊕y]⊗z [x⊗z]⊕[y⊗z]
    annₗ : NaturalIsomorphism 0⊗x 0↑
    annᵣ : NaturalIsomorphism x⊗0 0↑

  open SRig S⊎ S× distribₗ distribᵣ annₗ annᵣ

  -- need II, IX, X, XV
  -- choose I, IV, VI, XI, XIII, XIX, XXIII and (XVI, XVII)
  field
    .laplazaI : dₗACB ∘₁ 1⊗Byz ≡ⁿ B[x⊗y][x⊗z] ∘₁ dₗABC
    .laplazaII : Bxz⊕Byz ∘₁ dᵣABC ≡ⁿ dₗCAB ∘₁ B[x⊕y]z
    .laplazaIV : dᵣABD⊗1 ∘₁ (dᵣ[A⊕B]CD ∘₁ αˡABC⊗1) ≡ⁿ assocˡAD-BD-CD ∘₁ (1⊗dᵣBCD ∘₁ dᵣA[B⊕C]D)
    .laplazaVI : dₗ[AB]CD ∘₁ αAB[C⊕D] ≡ⁿ αABC⊕αABD ∘₁ (dₗA[BC][BD] ∘₁ 1A⊗dₗBCD)
    .laplazaIX : α[AC⊕BC][AD][BD] ∘₁ (dᵣABC⊕dᵣABD ∘₁ dₗ[A⊕B]CD) ≡ⁿ
        α[AC][BC][AD]⊕1BD ∘₁ ([1AC⊕s[AD][BC]]⊕1BD ∘₁ (α′[AC][AD][BC]⊕1BD ∘₁
           (α[AC⊕AD][BC][BD] ∘₁ (dₗACD⊕dₗBCD ∘₁ dᵣAB[C⊕D]))))
    .laplazaX : aₗ-over 0₀ ≡ⁿ aᵣ-over 0₀
    .laplazaXI : aₗ-over (h⊎.x⊗y) ≡ⁿ uˡ0 ∘₁ (aₗA⊕aₗB ∘₁ dₗ0AB)
    .laplazaXIII : uᵣ⊗-over 0₀ ≡ⁿ aₗ-over 1₀
    .laplazaXV : aᵣA ≡ⁿ aₗA ∘₁ s⊗A0
    .laplazaXVI : aₗAB ≡ⁿ aₗB ∘₁ (aₗA⊗1B ∘₁ α0AB)
    .laplazaXVII : aᵣA₂ ∘₁ 1A⊗aₗB ≡ⁿ aₗB ∘₁ (aᵣA⊗1B ∘₁ αA0B)
    .laplazaXIX : 1A⊗uₗB ≡ⁿ uₗAB ∘₁ (aᵣA⊕1AB ∘₁ dₗA0B)
    .laplazaXXIII : uₗA⊕B ≡ⁿ uₗA⊕uₗB ∘₁ dₗ1AB


------------------------------------------------------------------------
-- An alternative characterisation of the information ordering, along
-- with related results
------------------------------------------------------------------------

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

open import Prelude hiding (⊥)

module Partiality-monad.Inductive.Alternative-order
         {a} {A : Type a} where

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

open import Bijection equality-with-J using (_↔_)
open import Double-negation equality-with-J as DN
open import Equality.Path.Isomorphisms.Univalence equality-with-paths
open import Equivalence equality-with-J as Eq using (_≃_)
open import Function-universe equality-with-J as F hiding (id; _∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional equality-with-paths
  as Trunc
open import Monad equality-with-J
open import Nat equality-with-J
open import Univalence-axiom equality-with-J

open import Partiality-monad.Inductive
open import Partiality-monad.Inductive.Eliminators

------------------------------------------------------------------------
-- An alternative characterisation of λ x y → now x ⊑ y

-- This characterisation uses a technique from the first edition of
-- the HoTT book (Theorems 11.3.16 and 11.3.32).
--
-- The characterisation was developed together with Paolo Capriotti.

-- A binary relation, defined using structural recursion.

private

  now[_]≲-args : A → Arguments-nd (lsuc a) a A
  now[ x ]≲-args = record
    { P  = Proposition a
    ; Q  = λ P Q → proj₁ P → proj₁ Q
    ; pe = Prelude.⊥ , ⊥-propositional
    ; po = λ y → ∥ x ≡ y ∥ , truncation-is-proposition
    ; pl = λ { s (now-x≲s[_] , _) → ∥ ∃ (λ n → proj₁ (now-x≲s[ n ])) ∥
                                  , truncation-is-proposition
             }
    ; pa = λ now-x≲y now-x≲z now-x≲y→now-x≲z now-x≲z→now-x≲y →
                                            $⟨ record { to = now-x≲y→now-x≲z; from = now-x≲z→now-x≲y } ⟩
             proj₁ now-x≲y ⇔ proj₁ now-x≲z  ↝⟨ _↔_.to (⇔↔≡″ ext prop-ext) ⟩□
             now-x≲y ≡ now-x≲z              □
    ; ps = ps
    ; qr = λ { _ (now-x≲y , _) →
               now-x≲y  ↝⟨ id ⟩□
               now-x≲y  □
             }
    ; qt = λ { _ _ (P , _) (Q , _) (R , _) P→Q Q→R →
               P  ↝⟨ P→Q ⟩
               Q  ↝⟨ Q→R ⟩□
               R  □
             }
    ; qe = λ { _ (now-x≲⊥ , _) →
               Prelude.⊥  ↝⟨ ⊥-elim ⟩□
               now-x≲⊥    □
             }
    ; qu = λ { s (now-x≲s[_] , _) n →
               proj₁ now-x≲s[ n ]                ↝⟨ ∣_∣ ∘ (n ,_) ⟩□
               ∥ ∃ (λ n → proj₁ now-x≲s[ n ]) ∥  □
             }
    ; ql = λ { s ub is-ub (now-x≲s[_] , _) now-x≲ub now-x≲s[]→now-x≲ub →
               ∥ ∃ (λ n → proj₁ now-x≲s[ n ]) ∥  ↝⟨ Trunc.rec (proj₂ now-x≲ub) (uncurry now-x≲s[]→now-x≲ub) ⟩□
               proj₁ now-x≲ub                    □
             }
    ; qp = λ _ now-x≲z → Π-closure ext 1 λ _ →
                         proj₂ now-x≲z
    }
    where
    abstract
      ps : Is-set (Proposition a)
      ps = Is-set-∃-Is-proposition ext prop-ext

infix 4 now[_]≲_

now[_]≲_ : A → A ⊥ → Type a
now[ x ]≲ y = proj₁ (⊥-rec-nd now[ x ]≲-args y)

-- The relation is propositional.

now[]≲-propositional : ∀ {x y} → Is-proposition (now[ x ]≲ y)
now[]≲-propositional = proj₂ (⊥-rec-nd now[ _ ]≲-args _)

-- If a computation terminates with a certain value, then all larger
-- computations terminate with the same value (according to now[_]≲_).

larger-terminate-with-same-value≲ :
  ∀ {x y} → x ⊑ y → ∀ {z} → now[ z ]≲ x → now[ z ]≲ y
larger-terminate-with-same-value≲ x⊑y =
  ⊑-rec-nd now[ _ ]≲-args x⊑y

-- "Evaluation" lemmas for now[_]≲_.

now[]≲never : ∀ {x} → (now[ x ]≲ never) ≡ Prelude.⊥
now[]≲never {x} =
  now[ x ]≲ never  ≡⟨ cong proj₁ (⊥-rec-nd-never now[ x ]≲-args) ⟩∎
  Prelude.⊥        ∎

now[]≲now : ∀ {x y} → (now[ x ]≲ now y) ≡ ∥ x ≡ y ∥
now[]≲now {x} {y} =
  now[ x ]≲ now y  ≡⟨ cong proj₁ (⊥-rec-nd-now now[ x ]≲-args y) ⟩∎
  ∥ x ≡ y ∥        ∎

now[]≲⨆ : ∀ {x s} → (now[ x ]≲ ⨆ s) ≡ ∥ (∃ λ n → now[ x ]≲ s [ n ]) ∥
now[]≲⨆ {x} {s} =
  now[ x ]≲ ⨆ s                    ≡⟨ cong proj₁ (⊥-rec-nd-⨆ now[ x ]≲-args s) ⟩∎
  ∥ (∃ λ n → now[ x ]≲ s [ n ]) ∥  ∎

-- now[_]≲_ is pointwise equivalent to λ x y → now x ⊑ y.

now⊑≃now[]≲ : ∀ {x y} → (now x ⊑ y) ≃ (now[ x ]≲ y)
now⊑≃now[]≲ {x} {y} =
  _↔_.to (Eq.⇔↔≃ ext ⊑-propositional now[]≲-propositional)
    (record { to   = now x ⊑ y                                ↝⟨ larger-terminate-with-same-value≲ ⟩
                     (∀ {z} → now[ z ]≲ now x → now[ z ]≲ y)  ↝⟨ (λ hyp {_} eq → hyp (≡⇒→ (sym now[]≲now) eq)) ⟩
                     (∀ {z} → ∥ z ≡ x ∥ → now[ z ]≲ y)        ↝⟨ (λ hyp → hyp ∣ refl ∣) ⟩□
                     now[ x ]≲ y                              □
            ; from = ⊥-rec-⊥ from-args _
            })
  where
  from-args : Arguments-⊥ a A
  from-args = record
    { P  = λ y → now[ x ]≲ y → now x ⊑ y
    ; pe = now[ x ]≲ never  ↝⟨ ≡⇒↝ _ now[]≲never ⟩
           Prelude.⊥        ↝⟨ ⊥-elim ⟩□
           now x ⊑ never    □
    ; po = λ y →
             now[ x ]≲ now y  ↝⟨ ≡⇒↝ _ now[]≲now ⟩

             ∥ x ≡ y ∥        ↝⟨ Trunc.rec ⊑-propositional (

               x ≡ y               ↝⟨ cong now ⟩
               now x ≡ now y       ↝⟨ flip (subst (now x ⊑_)) (⊑-refl _) ⟩□
               now x ⊑ now y       □) ⟩□

             now x ⊑ now y    □
    ; pl = λ s now-x≲s→now-x⊑s →
             now[ x ]≲ ⨆ s                    ↝⟨ ≡⇒↝ _ now[]≲⨆ ⟩

             ∥ ∃ (λ n → now[ x ]≲ s [ n ]) ∥  ↝⟨ Trunc.rec ⊑-propositional (uncurry λ n now-x≲s[n] →

               now x                               ⊑⟨ now-x≲s→now-x⊑s n now-x≲s[n] ⟩
               s [ n ]                             ⊑⟨ upper-bound s n ⟩■
               ⨆ s                                 ■) ⟩□

             now x ⊑ ⨆ s                      □
    ; pp = λ _ → Π-closure ext 1 λ _ →
                 ⊑-propositional
    }

------------------------------------------------------------------------
-- Some properties that follow from the equivalence between now[_]≲_
-- and λ x y → now x ⊑ y

-- An equivalence between "now x ⊑ never" and an empty type.
--
-- This lemma was proved together with Paolo Capriotti.

now⊑never≃⊥ : {x : A} → (now x ⊑ never) ≃ Prelude.⊥ {ℓ = a}
now⊑never≃⊥ {x} =
  now x ⊑ never    ↝⟨ now⊑≃now[]≲ ⟩
  now[ x ]≲ never  ↝⟨ ≡⇒↝ _ now[]≲never ⟩□
  Prelude.⊥        □

-- Defined values of the form now x are never smaller than or equal to
-- never.
--
-- This lemma was proved together with Paolo Capriotti.

now⋢never : (x : A) → ¬ now x ⊑ never
now⋢never x =
  now x ⊑ never  ↔⟨ now⊑never≃⊥ ⟩
  Prelude.⊥      ↝⟨ ⊥-elim ⟩□
  ⊥₀             □

-- Defined values of the form now x are never equal to never.

now≢never : (x : A) → now x ≢ never
now≢never x =
  now x ≡ never                  ↝⟨ _≃_.from equality-characterisation-⊥ ⟩
  now x ⊑ never × now x ⊒ never  ↝⟨ proj₁ ⟩
  now x ⊑ never                  ↝⟨ now⋢never x ⟩□
  ⊥₀                             □

-- There is an equivalence between "now x is smaller than or equal
-- to now y" and "x is merely equal to y".

now⊑now≃∥≡∥ : {x y : A} → (now x ⊑ now y) ≃ ∥ x ≡ y ∥
now⊑now≃∥≡∥ {x} {y} =
  now x ⊑ now y    ↝⟨ now⊑≃now[]≲ ⟩
  now[ x ]≲ now y  ↝⟨ ≡⇒↝ _ now[]≲now ⟩□
  ∥ x ≡ y ∥        □

-- There is an equivalence between "now x is equal to now y" and "x
-- is merely equal to y".

now≡now≃∥≡∥ : {x y : A} → (now x ≡ now y) ≃ ∥ x ≡ y ∥
now≡now≃∥≡∥ {x} {y} =
  now x ≡ now y                  ↝⟨ inverse equality-characterisation-⊥ ⟩
  now x ⊑ now y × now x ⊒ now y  ↝⟨ now⊑now≃∥≡∥ ×-cong now⊑now≃∥≡∥ ⟩
  ∥ x ≡ y ∥ × ∥ y ≡ x ∥          ↝⟨ _↔_.to (Eq.⇔↔≃ ext (×-closure 1 truncation-is-proposition
                                                                    truncation-is-proposition)
                                                       truncation-is-proposition)
                                           (record { to = proj₁
                                                   ; from = λ ∥x≡y∥ → ∥x≡y∥ , ∥∥-map sym ∥x≡y∥
                                                   }) ⟩□
  ∥ x ≡ y ∥                      □

-- There is an equivalence between "now x is smaller than or equal to
-- now y" and "now x is equal to now y".

now⊑now≃now≡now : {x y : A} → (now x ⊑ now y) ≃ (now x ≡ now y)
now⊑now≃now≡now {x} {y} =
  now x ⊑ now y  ↝⟨ now⊑now≃∥≡∥ ⟩
  ∥ x ≡ y ∥      ↝⟨ inverse now≡now≃∥≡∥ ⟩□
  now x ≡ now y  □

-- A computation can terminate with at most one value.

termination-value-merely-unique :
  ∀ {x y z} → x ⇓ y → x ⇓ z → ∥ y ≡ z ∥
termination-value-merely-unique {x} {y} {z} x⇓y x⇓z =
  _≃_.to now≡now≃∥≡∥ (
    now y  ≡⟨ sym x⇓y ⟩
    x      ≡⟨ x⇓z ⟩∎
    now z  ∎)

-- There is an equivalence between now x ⊑ ⨆ s and
-- ∥ ∃ (λ n → now x ⊑ s [ n ]) ∥.

now⊑⨆≃∥∃now⊑∥ :
  ∀ {s : Increasing-sequence A} {x} →
  (now x ⊑ ⨆ s) ≃ ∥ ∃ (λ n → now x ⊑ s [ n ]) ∥
now⊑⨆≃∥∃now⊑∥ {s} {x} =
  now x ⊑ ⨆ s                      ↝⟨ now⊑≃now[]≲ ⟩
  now[ x ]≲ ⨆ s                    ↝⟨ ≡⇒↝ _ now[]≲⨆ ⟩
  ∥ (∃ λ n → now[ x ]≲ s [ n ]) ∥  ↝⟨ ∥∥-cong (∃-cong λ _ → inverse now⊑≃now[]≲) ⟩□
  ∥ (∃ λ n → now x ⊑ s [ n ]) ∥    □

-- If x is larger than or equal to now y, then x is equal to now y.

now⊑→⇓ : ∀ {x} {y : A} → now y ⊑ x → x ⇓ y
now⊑→⇓ {x} {y} = ⊥-rec-⊥ (record
  { P  = λ x → now y ⊑ x → x ⇓ y
  ; pe = now y ⊑ never  ↝⟨ now⋢never y ⟩
         Prelude.⊥      ↝⟨ ⊥-elim ⟩□
         never ≡ now y  □
  ; po = λ x →
           now y ⊑ now x  ↔⟨ now⊑now≃∥≡∥ ⟩
           ∥ y ≡ x ∥      ↝⟨ ∥∥-map sym ⟩
           ∥ x ≡ y ∥      ↝⟨ Trunc.rec ⊥-is-set (cong now) ⟩□
           now x ≡ now y  □
  ; pl = λ s hyp →
           now y ⊑ ⨆ s                                  ↝⟨ (λ p → p , _≃_.to now⊑⨆≃∥∃now⊑∥ p) ⟩
           now y ⊑ ⨆ s × ∥ (∃ λ n → now y ⊑ s [ n ]) ∥  ↝⟨ uncurry (λ p → Trunc.rec ⊥-is-set (uncurry λ n →

               now y ⊑ s [ n ]                               ↝⟨ (λ now⊑ _ n≤m → ⊑-trans now⊑ (later-larger s n≤m)) ⟩
               (∀ m → n ≤ m → now y ⊑ s [ m ])               ↝⟨ (∀-cong _ λ _ → ∀-cong _ λ _ → hyp _) ⟩
               (∀ m → n ≤ m → s [ m ] ≡ now y)               ↝⟨ (∀-cong _ λ _ → ∀-cong _ λ _ → flip (subst (_ ⊑_)) (⊑-refl _)) ⟩
               (∀ m → n ≤ m → s [ m ] ⊑ now y)               ↝⟨ upper-bound-≤→upper-bound s ⟩
               (∀ n → s [ n ] ⊑ now y)                       ↝⟨ least-upper-bound _ _ ⟩
               ⨆ s ⊑ now y                                   ↝⟨ flip antisymmetry p ⟩□
               ⨆ s ≡ now y                                   □)) ⟩□

           ⨆ s ≡ now y                                  □
  ; pp = λ _ → Π-closure ext 1 λ _ →
               ⊥-is-set
  })
  x

-- If a computation terminates with a certain value, then all larger
-- computations terminate with the same value.
--
-- Capretta proved a similar result in "General Recursion via
-- Coinductive Types".

larger-terminate-with-same-value : {x y : A ⊥} → x ⊑ y → x ≼ y
larger-terminate-with-same-value now-x⊑y _ refl = now⊑→⇓ now-x⊑y

-- If one element in an increasing sequence terminates with a given
-- value, then this value is the sequence's least upper bound.

terminating-element-is-⨆ :
  ∀ (s : Increasing-sequence A) {n x} →
  s [ n ] ⇓ x → ⨆ s ⇓ x
terminating-element-is-⨆ s {n} {x} =
  larger-terminate-with-same-value (upper-bound s n) x

-- The relation _≼_ is contained in _⊑_.
--
-- Capretta proved a similar result in "General Recursion via
-- Coinductive Types".

≼→⊑ : {x y : A ⊥} → x ≼ y → x ⊑ y
≼→⊑ {x} {y} = ⊥-rec-⊥
  (record
     { P  = λ x → x ≼ y → x ⊑ y
     ; pe = never ≼ y  ↝⟨ (λ _ → never⊑ y) ⟩□
            never ⊑ y   □
     ; po = λ x →
              now x ≼ y              ↝⟨ (λ hyp → hyp x refl) ⟩
              y ⇓ x                  ↔⟨ inverse equality-characterisation-⊥ ⟩
              y ⊑ now x × y ⊒ now x  ↝⟨ proj₂ ⟩□
              now x ⊑ y              □
     ; pl = λ s s≼y→s⊑y →
              ⨆ s ≼ y                        ↝⟨ id ⟩
              (∀ z → ⨆ s ⇓ z → y ⇓ z)        ↝⟨ (λ hyp n z →

                s [ n ] ⇓ z                       ↝⟨ larger-terminate-with-same-value (upper-bound s n) z ⟩
                ⨆ s ⇓ z                           ↝⟨ hyp z ⟩□
                y ⇓ z                             □) ⟩

              (∀ n z → s [ n ] ⇓ z → y ⇓ z)  ↝⟨ id ⟩
              (∀ n → s [ n ] ≼ y)            ↝⟨ (λ hyp n → s≼y→s⊑y n (hyp n)) ⟩
              (∀ n → s [ n ] ⊑ y)            ↝⟨ least-upper-bound s y ⟩□
              ⨆ s ⊑ y                        □
     ; pp = λ _ →
              Π-closure ext 1 λ _ →
              ⊑-propositional
     })
  x

-- The two relations _≼_ and _⊑_ are pointwise equivalent.
--
-- Capretta proved a similar result in "General Recursion via
-- Coinductive Types".

≼≃⊑ : {x y : A ⊥} → (x ≼ y) ≃ (x ⊑ y)
≼≃⊑ = _↔_.to (Eq.⇔↔≃ ext ≼-propositional ⊑-propositional)
             (record { to   = ≼→⊑
                     ; from = larger-terminate-with-same-value
                     })

-- An alternative characterisation of _⇓_.

⇓≃now⊑ : ∀ {x} {y : A} → (x ⇓ y) ≃ (now y ⊑ x)
⇓≃now⊑ {x} {y} =
  _↔_.to (Eq.⇔↔≃ ext ⊥-is-set ⊑-propositional) (record
    { to   = x ≡ now y              ↔⟨ inverse equality-characterisation-⊥ ⟩
             x ⊑ now y × x ⊒ now y  ↝⟨ proj₂ ⟩□
             now y ⊑ x              □
    ; from = now y ⊑ x                  ↝⟨ larger-terminate-with-same-value ⟩
             (∀ z → now y ⇓ z → x ⇓ z)  ↝⟨ (λ hyp → hyp y refl) ⟩□
             x ⇓ y                      □
    })

-- Another alternative characterisation of _⇓_.

⇓≃now[]≲ : ∀ {x y} → (x ⇓ y) ≃ (now[ y ]≲ x)
⇓≃now[]≲ {x} {y} =
  x ⇓ y        ↝⟨ ⇓≃now⊑ ⟩
  now y ⊑ x    ↝⟨ now⊑≃now[]≲ ⟩□
  now[ y ]≲ x  □

-- Two corollaries of ⇓≃now[]≲.

never⇓≃⊥ : {x : A} → (never ⇓ x) ≃ Prelude.⊥ {ℓ = a}
never⇓≃⊥ {x = x} =
  never ≡ now x    ↝⟨ ⇓≃now[]≲ ⟩
  now[ x ]≲ never  ↝⟨ ≡⇒↝ _ now[]≲never ⟩□
  Prelude.⊥        □

⨆⇓≃∥∃⇓∥ :
  ∀ {s : Increasing-sequence A} {x} →
  (⨆ s ⇓ x) ≃ ∥ ∃ (λ n → s [ n ] ⇓ x) ∥
⨆⇓≃∥∃⇓∥ {s} {x} =
  ⨆ s ⇓ x                          ↝⟨ ⇓≃now[]≲ ⟩
  now[ x ]≲ ⨆ s                    ↝⟨ ≡⇒↝ _ now[]≲⨆ ⟩
  ∥ ∃ (λ n → now[ x ]≲ s [ n ]) ∥  ↝⟨ ∥∥-cong (∃-cong λ _ → inverse ⇓≃now[]≲) ⟩□
  ∥ ∃ (λ n → s [ n ] ⇓ x) ∥        □

-- If x does not terminate, then x is equal to never.

¬⇓→⇑ : {x : A ⊥} → ¬ (∃ λ y → x ⇓ y) → x ⇑
¬⇓→⇑ {x} = ⊥-rec-⊥
  (record
     { P  = λ x → ¬ (∃ λ y → x ⇓ y) → x ⇑
     ; pe = ¬ ∃ (never ⇓_)  ↝⟨ const refl ⟩□
            never ⇑         □
     ; po = λ x →
              ¬ ∃ (now x ⇓_) ↝⟨ _$ (x , refl) ⟩
              ⊥₀             ↝⟨ ⊥-elim ⟩□
              now x ⇑        □
     ; pl = λ s ih →
              ¬ ∃ (⨆ s ⇓_)                           ↔⟨ →-cong ext (∃-cong (λ _ → ⨆⇓≃∥∃⇓∥)) F.id ⟩
              ¬ ∃ (λ x → ∥ ∃ (λ n → s [ n ] ⇓ x) ∥)  ↝⟨ (λ { hyp (n , x , s[n]⇓x) → hyp (x , ∣ n , s[n]⇓x ∣) }) ⟩
              ¬ ∃ (λ n → ∃ λ x → s [ n ] ⇓ x)        ↝⟨ (λ hyp n → ih n (hyp ∘ (n ,_))) ⟩
              (∀ n → s [ n ] ⇑)                      ↝⟨ sym ∘ _↔_.to equality-characterisation-increasing ⟩
              constˢ never ≡ s                       ↝⟨ flip (subst (λ s → ⨆ s ⇑)) ⨆-const ⟩□
              ⨆ s ⇑                                  □
     ; pp = λ _ → Π-closure ext 1 λ _ →
                  ⊥-is-set
     })
  x

-- In the double-negation monad a computation is either terminating or
-- non-terminating.

now-or-never : (x : A ⊥) → ¬ ¬ ((∃ λ y → x ⇓ y) ⊎ x ⇑)
now-or-never x = run (map (⊎-map id ¬⇓→⇑) excluded-middle)

-- _⊑_ is a flat order, in the sense that distinct elements that are
-- distinct from never are unrelated.

flat-order : {x y : A ⊥} → x ≢ y → ¬ x ⇑ → ¬ y ⇑ → ¬ (x ⊑ y)
flat-order {x} {y} x≢y never≢x never≢y x⊑y = ¬¬¬⊥ $
  ¬⇑→¬¬⇓ never≢x >>= λ x⇓ →
  ¬⇑→¬¬⇓ never≢y >>= λ y⇓ →
  return (⊥-elim $ ¬x⇓×y⇓ (x⇓ , y⇓))
  where
  -- The computations x and y cannot both terminate.

  ¬x⇓×y⇓ : ¬ ((∃ λ z → x ⇓ z) × (∃ λ z → y ⇓ z))
  ¬x⇓×y⇓ ((xz , refl) , (yz , refl)) = x≢y (
    now xz      ≡⟨ _≃_.to now⊑now≃now≡now (

        now xz       ⊑⟨ x⊑y ⟩■
        now yz       ■) ⟩∎

    now yz      ∎)

  -- Computations that fail to be equal to never do not fail to
  -- terminate.

  ¬⇑→¬¬⇓ : {x : A ⊥} → ¬ x ⇑ → ¬¬ (∃ λ y → x ⇓ y)
  run (¬⇑→¬¬⇓ ¬x⇑) = ¬x⇑ ∘ ¬⇓→⇑

-- Some "constructors" for □.

□-never :
  ∀ {ℓ} {P : A → Type ℓ} →
  □ P never
□-never {P = P} y =
  never ⇓ y        ↔⟨ ⇓≃now[]≲ ⟩
  now[ y ]≲ never  ↔⟨ ≡⇒↝ bijection now[]≲never ⟩
  Prelude.⊥        ↝⟨ ⊥-elim ⟩□
  P y              □

□-now :
  ∀ {ℓ} {P : A → Type ℓ} {x} →
  Is-proposition (P x) →
  P x → □ P (now x)
□-now {P = P} {x} P-prop p y =
  now x ⇓ y  ↔⟨ now≡now≃∥≡∥ ⟩
  ∥ x ≡ y ∥  ↝⟨ (λ ∥x≡y∥ →
                   Trunc.rec (Trunc.rec (H-level-propositional ext 1)
                                        (λ x≡y → subst (Is-proposition ∘ P) x≡y P-prop)
                                        ∥x≡y∥)
                             (λ x≡y → subst P x≡y p)
                             ∥x≡y∥) ⟩□
  P y        □

□-⨆ :
  ∀ {ℓ} {P : A → Type ℓ} →
  (∀ x → Is-proposition (P x)) →
  ∀ {s} → (∀ n → □ P (s [ n ])) → □ P (⨆ s)
□-⨆ {P = P} P-prop {s} p y =
  ⨆ s ⇓ y                    ↔⟨ ⨆⇓≃∥∃⇓∥ ⟩
  ∥ ∃ (λ n → s [ n ] ⇓ y) ∥  ↝⟨ Trunc.rec (P-prop y) (uncurry λ n s[n]⇓y → p n y s[n]⇓y) ⟩□
  P y                        □

-- One "non-constructor" and one "constructor" for ◇.

◇-never :
  ∀ {ℓ} {P : A → Type ℓ} →
  ¬ ◇ P never
◇-never {P = P} =
  ◇ P never                      ↝⟨ id ⟩
  ∥ (∃ λ y → never ⇓ y × P y) ∥  ↝⟨ Trunc.rec ⊥-propositional (now≢never _ ∘ sym ∘ proj₁ ∘ proj₂) ⟩□
  ⊥₀                             □

◇-⨆ :
  ∀ {ℓ} {P : A → Type ℓ} →
  ∀ {s n} → ◇ P (s [ n ]) → ◇ P (⨆ s)
◇-⨆ {P = P} =
  ∥∥-map (Σ-map id (λ {x} → Σ-map {Q = λ _ → P x}
                                  (terminating-element-is-⨆ _) id))

------------------------------------------------------------------------
-- An alternative characterisation of _⊑_

-- This characterisation uses a technique from the first edition of
-- the HoTT book (Theorems 11.3.16 and 11.3.32).
--
-- The characterisation was developed together with Paolo Capriotti.

-- A binary relation, defined using structural recursion.

private

  ≲-args : Arguments-nd (lsuc a) a A
  ≲-args = record
    { P  = A ⊥ → Proposition a
    ; Q  = λ P Q → ∀ z → proj₁ (Q z) → proj₁ (P z)
    ; pe = λ _ → ↑ _ ⊤ , ↑-closure 1 (mono₁ 0 ⊤-contractible)
    ; po = λ x y → ⊥-rec-nd now[ x ]≲-args y
    ; pl = λ { _ (s[_]≲ , _) y → (∀ n → proj₁ (s[ n ]≲ y))
                               , Π-closure ext 1 λ n →
                                 proj₂ (s[ n ]≲ y)
             }
    ; pa = λ x≲ y≲ y≲→x≲ x≲→y≲ → ⟨ext⟩ λ z →
                                          $⟨ record { to = x≲→y≲ z; from = y≲→x≲ z } ⟩
             proj₁ (x≲ z) ⇔ proj₁ (y≲ z)  ↝⟨ _↔_.to (⇔↔≡″ ext prop-ext) ⟩□
             x≲ z ≡ y≲ z                  □
    ; ps = ps
    ; qr = λ _ x≲ z →
             proj₁ (x≲ z)  ↝⟨ id ⟩□
             proj₁ (x≲ z)  □
    ; qt = λ _ _ P Q R Q→P R→Q z →
             proj₁ (R z)  ↝⟨ R→Q z ⟩
             proj₁ (Q z)  ↝⟨ Q→P z ⟩□
             proj₁ (P z)  □
    ; qe = λ _ ⊥≲ z →
             proj₁ (⊥≲ z)  ↝⟨ _ ⟩□
             ↑ _ ⊤         □
    ; qu = λ { s (s[_]≲ , _) n z →
               (∀ m → proj₁ (s[ m ]≲ z))  ↝⟨ (_$ n) ⟩□
               proj₁ (s[ n ]≲ z)          □
             }
    ; ql = λ { _ _ _ (s[_]≲ , _) ub≲ ub≲→s[]≲ z →
               proj₁ (ub≲ z)              ↝⟨ flip (flip ub≲→s[]≲ z) ⟩□
               (∀ n → proj₁ (s[ n ]≲ z))  □
             }
    ; qp = λ x≲ y≲ → Π-closure ext 1 λ z →
                     Π-closure ext 1 λ _ →
                     proj₂ (x≲ z)
    }
    where
    abstract
      ps : Is-set (A ⊥ → Proposition a)
      ps =
        Π-closure ext 2 λ _ →
        Is-set-∃-Is-proposition ext prop-ext

infix 4 _≲_

_≲_ : A ⊥ → A ⊥ → Type a
x ≲ y = proj₁ (⊥-rec-nd ≲-args x y)

-- The relation is propositional.

≲-propositional : ∀ x y → Is-proposition (x ≲ y)
≲-propositional x y = proj₂ (⊥-rec-nd ≲-args x y)

-- A form of transitivity involving _⊑_ and _≲_.

⊑≲-trans : ∀ {x y} (z : A ⊥) → x ⊑ y → y ≲ z → x ≲ z
⊑≲-trans z x⊑y = ⊑-rec-nd ≲-args x⊑y z

-- "Evaluation" lemmas for _≲_.

never≲ : ∀ {y} → (never ≲ y) ≡ ↑ _ ⊤
never≲ {y} = cong (proj₁ ∘ (_$ _)) (
  ⊥-rec-nd ≲-args never  ≡⟨ ⊥-rec-nd-never ≲-args ⟩∎
  (λ _ → ↑ _ ⊤ , _)      ∎)

⨆≲ : ∀ {s y} → (⨆ s ≲ y) ≡ ∀ n → s [ n ] ≲ y
⨆≲ {s} {y} = cong (proj₁ ∘ (_$ _)) (
  ⊥-rec-nd ≲-args (⨆ s)            ≡⟨ ⊥-rec-nd-⨆ ≲-args s ⟩∎
  (λ y → (∀ n → s [ n ] ≲ y) , _)  ∎)

now≲ : ∀ {x y} → (now x ≲ y) ≡ (now[ x ]≲ y)
now≲ {x} {y} =
  now x ≲ y    ≡⟨ cong (proj₁ ∘ (_$ _)) (⊥-rec-nd-now ≲-args x) ⟩∎
  now[ x ]≲ y  ∎

now≲never : ∀ {x} → (now x ≲ never) ≡ Prelude.⊥
now≲never {x} =
  now x ≲ never    ≡⟨ now≲ ⟩
  now[ x ]≲ never  ≡⟨ now[]≲never ⟩∎
  Prelude.⊥        ∎

now≲now : ∀ {x y} → (now x ≲ now y) ≡ ∥ x ≡ y ∥
now≲now {x} {y} =
  now x ≲ now y    ≡⟨ now≲ ⟩
  now[ x ]≲ now y  ≡⟨ now[]≲now ⟩∎
  ∥ x ≡ y ∥        ∎

now≲⨆ : ∀ {x s} → (now x ≲ ⨆ s) ≡ ∥ (∃ λ n → now x ≲ s [ n ]) ∥
now≲⨆ {x} {s} =
  now x ≲ ⨆ s                      ≡⟨ now≲ ⟩
  now[ x ]≲ ⨆ s                    ≡⟨ now[]≲⨆ ⟩
  ∥ (∃ λ n → now[ x ]≲ s [ n ]) ∥  ≡⟨ cong (λ P → ∥ ∃ P ∥) (⟨ext⟩ λ _ → sym now≲) ⟩∎
  ∥ (∃ λ n → now x ≲ s [ n ]) ∥    ∎

-- _≲_ is reflexive.

≲-refl : ∀ x → x ≲ x
≲-refl = ⊥-rec-⊥ (record
  { pe =                $⟨ _ ⟩
         ↑ _ ⊤          ↝⟨ ≡⇒↝ bijection $ sym never≲ ⟩□
         never ≲ never  □
  ; po = λ x →            $⟨ ∣ refl ∣ ⟩
           ∥ x ≡ x ∥      ↝⟨ ≡⇒↝ bijection $ sym now≲now ⟩□
           now x ≲ now x  □
  ; pl = λ s →
           (∀ n → s [ n ] ≲ s [ n ])  ↝⟨ (λ s≲s n → ⨆-lemma s (s [ n ]) n (s≲s n)) ⟩
           (∀ n → s [ n ] ≲ ⨆ s)      ↔⟨ ≡⇒↝ bijection $ sym ⨆≲ ⟩□
           ⨆ s ≲ ⨆ s                  □
  ; pp = λ x → ≲-propositional x x
  })
  where
  ⨆-lemma : ∀ s x n → x ≲ s [ n ] → x ≲ ⨆ s
  ⨆-lemma s = ⊥-rec-⊥
    (record
       { P  = λ x → ∀ n → x ≲ s [ n ] → x ≲ ⨆ s
       ; pe = λ n →
                never ≲ s [ n ]  ↔⟨ ≡⇒↝ bijection $ never≲ ⟩
                ↑ _ ⊤            ↔⟨ ≡⇒↝ bijection $ sym never≲ ⟩□
                never ≲ ⨆ s      □
       ; po = λ x n →
                now x ≲ s [ n ]                ↝⟨ ∣_∣ ∘ (n ,_) ⟩
                ∥ (∃ λ n → now x ≲ s [ n ]) ∥  ↔⟨ ≡⇒↝ bijection $ sym now≲⨆ ⟩□
                now x ≲ ⨆ s                    □
       ; pl = λ s′ →
                (∀ m n → s′ [ m ] ≲ s [ n ] → s′ [ m ] ≲ ⨆ s)  ↝⟨ (λ hyp n s′≲s m → hyp m n (s′≲s m)) ⟩

                (∀ n → (∀ m → s′ [ m ] ≲ s [ n ]) →
                       (∀ m → s′ [ m ] ≲ ⨆ s))                 ↝⟨ ∀-cong _ (λ _ →
                                                                    ≡⇒↝ _ $ sym $ cong₂ (λ x y → x → y) ⨆≲ ⨆≲) ⟩□
                (∀ n → ⨆ s′ ≲ s [ n ] → ⨆ s′ ≲ ⨆ s)            □
       ; pp = λ x → Π-closure ext 1 λ _ →
                    Π-closure ext 1 λ _ →
                    ≲-propositional x (⨆ s)
       })

-- _⊑_ and _≲_ are pointwise equivalent.

⊑≃≲ : ∀ {x y} → (x ⊑ y) ≃ (x ≲ y)
⊑≃≲ {x} {y} =
  _↔_.to (Eq.⇔↔≃ ext ⊑-propositional (≲-propositional x y))
    (record { to   = λ x⊑y → ⊑≲-trans _ x⊑y (≲-refl y)
            ; from = ⊥-rec-⊥ from-args _ _
            })
  where
  from-args : Arguments-⊥ a A
  from-args = record
    { P  = λ x → ∀ y → x ≲ y → x ⊑ y
    ; pe = λ y _ → never⊑ y
    ; po = λ x y →
             now x ≲ y    ↝⟨ ≡⇒↝ _ now≲ ⟩
             now[ x ]≲ y  ↔⟨ inverse now⊑≃now[]≲ ⟩□
             now x ⊑ y    □
    ; pl = λ s s≲→s⊑ y →
             ⨆ s ≲ y              ↝⟨ ≡⇒↝ _ ⨆≲ ⟩
             (∀ n → s [ n ] ≲ y)  ↝⟨ (λ s[_]≲y n → s≲→s⊑ n y s[ n ]≲y) ⟩
             (∀ n → s [ n ] ⊑ y)  ↝⟨ least-upper-bound s y ⟩□
             ⨆ s ⊑ y              □
    ; pp = λ _ → Π-closure ext 1 λ _ →
                 Π-closure ext 1 λ _ →
                 ⊑-propositional
    }


open import Nat
open import Prelude
open import List

open import contexts
open import core

module completeness where
  -- any hole is new to a complete expression
  e-complete-hnn : ∀{e u} → e ecomplete → hole-name-new e u
  e-complete-hnn (ECFix cmp) = HNNFix (e-complete-hnn cmp)
  e-complete-hnn ECVar = HNNVar
  e-complete-hnn (ECAp cmp1 cmp2) = HNNAp (e-complete-hnn cmp1) (e-complete-hnn cmp2)
  e-complete-hnn ECUnit = HNNUnit
  e-complete-hnn (ECPair cmp cmp₁) = HNNPair (e-complete-hnn cmp) (e-complete-hnn cmp₁)
  e-complete-hnn (ECFst cmp) = HNNFst (e-complete-hnn cmp)
  e-complete-hnn (ECSnd cmp) = HNNSnd (e-complete-hnn cmp)
  e-complete-hnn (ECCtor cmp) = HNNCtor (e-complete-hnn cmp)
  e-complete-hnn (ECCase cmp h) = HNNCase (e-complete-hnn cmp) λ i<∥rules∥ → e-complete-hnn (h i<∥rules∥)
  e-complete-hnn (ECAsrt cmp1 cmp2) = HNNAsrt (e-complete-hnn cmp1) (e-complete-hnn cmp2)

  {- TODO : probably delete
  e-hnn-complete : ∀{e} → (∀{u} → hole-name-new e u) → e ecomplete
  e-hnn-complete hnn∀ = {!!}
  -}

  -- a complete expression is holes-disjoint to all expressions
  e-complete-disjoint : ∀{e1 e2} → e1 ecomplete → holes-disjoint e1 e2
  e-complete-disjoint (ECFix cmp) = HDFix (e-complete-disjoint cmp)
  e-complete-disjoint ECVar = HDVar
  e-complete-disjoint (ECAp cmp1 cmp2) = HDAp (e-complete-disjoint cmp1) (e-complete-disjoint cmp2)
  e-complete-disjoint ECUnit = HDUnit
  e-complete-disjoint (ECPair cmp cmp₁) = HDPair (e-complete-disjoint cmp) (e-complete-disjoint cmp₁)
  e-complete-disjoint (ECFst cmp) = HDFst (e-complete-disjoint cmp)
  e-complete-disjoint (ECSnd cmp) = HDSnd (e-complete-disjoint cmp)
  e-complete-disjoint (ECCtor cmp) = HDCtor (e-complete-disjoint cmp)
  e-complete-disjoint (ECCase cmp h) = HDCase (e-complete-disjoint cmp) λ i<∥rules∥ → e-complete-disjoint (h i<∥rules∥)
  e-complete-disjoint (ECAsrt cmp1 cmp2) = HDAsrt (e-complete-disjoint cmp1) (e-complete-disjoint cmp2)

  -- TODO a holes-disjoint-sym check - very involved but arguably pretty important

  -- TODO we should generalize this, to a theorem that says that if a hole name is new in
  -- e, then it is new in r and k
  {- TODO}
  -- if e evals to r, and e is complete, then r is complete
  eval-completeness : ∀{Δ Σ' Γ E e r τ k} →
                        E env-complete →
                        Δ , Σ' , Γ ⊢ E →
                        Δ , Σ' , Γ ⊢ e :: τ →
                        E ⊢ e ⇒ r ⊣ k →
                        e ecomplete →
                        r rcomplete
  eval-completeness Ecmp Γ⊢E (TALam _ _) EFun (ECLam ecmp) = RCLam Ecmp ecmp
  eval-completeness Ecmp Γ⊢E (TAFix _ _ _) EFix (ECFix ecmp) = RCFix Ecmp ecmp
  eval-completeness (ENVC Ecmp) Γ⊢E (TAVar _) (EVar h) ECVar = Ecmp h
  eval-completeness Ecmp Γ⊢E (TAApp _ ta-f ta-arg) (EApp {Ef = Ef} {x} {r2 = r2} CF∞ eval-f eval-arg eval-ef) (ECAp cmp-f cmp-arg)
    with eval-completeness Ecmp Γ⊢E ta-f eval-f cmp-f | preservation Γ⊢E ta-f eval-f
  ... | RCLam (ENVC Efcmp) efcmp | TALam Γ'⊢Ef (TALam _ ta-ef)
    = eval-completeness (ENVC env-cmp) (EnvInd Γ'⊢Ef (preservation Γ⊢E ta-arg eval-arg)) ta-ef eval-ef efcmp
      where
        env-cmp : ∀{x' rx'} → (x' , rx') ∈ (Ef ,, (x , r2)) → rx' rcomplete
        env-cmp {x'} {rx'} h with ctx-split {Γ = Ef} h
        env-cmp {x'} {rx'} h | Inl (_ , x'∈Ef) = Efcmp x'∈Ef
        env-cmp {x'} {rx'} h | Inr (_ , rx'==r2) rewrite rx'==r2 =
          eval-completeness Ecmp Γ⊢E ta-arg eval-arg cmp-arg
  eval-completeness Ecmp Γ⊢E (TAApp _ ta-f ta-arg) (EAppFix {Ef = Ef} {f} {x} {ef} {r2 = r2} CF∞ h eval-f eval-arg eval-ef) (ECAp cmp-f cmp-arg)
    rewrite h with eval-completeness Ecmp Γ⊢E ta-f eval-f cmp-f | preservation Γ⊢E ta-f eval-f
  ... | RCFix (ENVC Efcmp) efcmp | TAFix Γ'⊢Ef (TAFix _ _ ta-ef) =
    eval-completeness (ENVC new-Ef+-cmp) new-ctxcons ta-ef eval-ef efcmp
    where
      new-ctxcons =
        EnvInd (EnvInd Γ'⊢Ef (preservation Γ⊢E ta-f eval-f)) (preservation Γ⊢E ta-arg eval-arg)
      new-Ef-cmp  : ∀{x' rx'} → (x' , rx') ∈ (Ef ,, (f , [ Ef ]fix f ⦇·λ x => ef ·⦈)) → rx' rcomplete
      new-Ef-cmp  {x'} {rx'} h with ctx-split {Γ = Ef} h
      new-Ef-cmp  {x'} {rx'} h | Inl (_ , x'∈Ef) = Efcmp x'∈Ef
      new-Ef-cmp  {x'} {rx'} h | Inr (_ , rx'==r2) rewrite rx'==r2 = RCFix (ENVC Efcmp) efcmp
      new-Ef+-cmp : ∀{x' rx'} → (x' , rx') ∈ (Ef ,, (f , [ Ef ]fix f ⦇·λ x => ef ·⦈) ,, (x , r2)) → rx' rcomplete
      new-Ef+-cmp {x'} {rx'} h with ctx-split {Γ = Ef ,, (f , [ Ef ]fix f ⦇·λ x => ef ·⦈)} h
      new-Ef+-cmp {x'} {rx'} h | Inl (_ , x'∈Ef+) = new-Ef-cmp x'∈Ef+
      new-Ef+-cmp {x'} {rx'} h | Inr (_ , rx'==r2) rewrite rx'==r2 =
        eval-completeness Ecmp Γ⊢E ta-arg eval-arg cmp-arg
  eval-completeness Ecmp Γ⊢E (TAApp _ ta1 ta2) (EAppUnfinished eval1 _ _ eval2) (ECAp ecmp1 ecmp2) =
    RCAp (eval-completeness Ecmp Γ⊢E ta1 eval1 ecmp1) (eval-completeness Ecmp Γ⊢E ta2 eval2 ecmp2)
  eval-completeness Ecmp Γ⊢E (TATpl ∥es∥==∥τs∥ _ tas) (ETuple ∥es∥==∥rs∥ ∥es∥==∥ks∥ evals) (ECTpl cmps) =
    RCTpl λ {i} rs[i] →
      let
        _ , es[i] = ∥l1∥==∥l2∥→l1[i]→l2[i] (! ∥es∥==∥rs∥) rs[i]
        _ , ks[i] = ∥l1∥==∥l2∥→l1[i]→l2[i] ∥es∥==∥ks∥ es[i]
        _ , τs[i] = ∥l1∥==∥l2∥→l1[i]→l2[i] ∥es∥==∥τs∥ es[i]
      in
      eval-completeness Ecmp Γ⊢E (tas es[i] τs[i]) (evals es[i] rs[i] ks[i]) (cmps es[i])
  eval-completeness Ecmp Γ⊢E (TAGet ∥rs⊫=∥τs∥ i<∥τs∥ ta) (EGet _ i<∥rs∥ eval) (ECGet ecmp)
    with eval-completeness Ecmp Γ⊢E ta eval ecmp
  ... | RCTpl h = h i<∥rs∥
  eval-completeness Ecmp Γ⊢E (TAGet _ i<∥τs∥ ta) (EGetUnfinished eval _) (ECGet ecmp) =
    RCGet (eval-completeness Ecmp Γ⊢E ta eval ecmp)
  eval-completeness Ecmp Γ⊢E (TACtor _ _ ta) (ECtor eval) (ECCtor ecmp) =
    RCCtor (eval-completeness Ecmp Γ⊢E ta eval ecmp)
  eval-completeness {Σ' = Σ'} (ENVC Ecmp) Γ⊢E (TACase d∈Σ'1 ta h1 h2) (EMatch {E = E} {xc = xc} {r' = r'} CF∞ form eval eval-ec) (ECCase ecmp rules-cmp)
    with h2 form
  ... | _ , _ , _ , _ , c∈cctx1 , ta-ec
    with preservation Γ⊢E ta eval
  ... | TACtor {cctx = cctx} d∈Σ' c∈cctx ta-r'
    rewrite ctxunicity {Γ = π1 Σ'} d∈Σ'1 d∈Σ' | ctxunicity {Γ = cctx} c∈cctx1 c∈cctx =
    eval-completeness (ENVC new-E-cmp) (EnvInd Γ⊢E ta-r') ta-ec eval-ec (rules-cmp form)
    where
      new-E-cmp : ∀{x' rx'} → (x' , rx') ∈ (E ,, (xc , r')) → rx' rcomplete
      new-E-cmp {x'} {rx'} x'∈E+ with ctx-split {Γ = E} x'∈E+
      ... | Inl (_ , x'∈E) = Ecmp x'∈E
      ... | Inr (_ , rx'==r') rewrite rx'==r'
        with eval-completeness (ENVC Ecmp) Γ⊢E ta eval ecmp
      ... | RCCtor r'cmp = r'cmp
  eval-completeness Ecmp Γ⊢E (TACase _ ta _ _) (EMatchUnfinished eval _) (ECCase ecmp rulescmp) =
    RCCase Ecmp (eval-completeness Ecmp Γ⊢E ta eval ecmp) rulescmp
  eval-completeness Ecmp Γ⊢E (TAHole _) EHole ()
  eval-completeness Ecmp Γ⊢E (TAPF _) EPF (ECPF pf-cmp) = RCPF pf-cmp
  eval-completeness Ecmp Γ⊢E (TAAsrt _ _ _) (EAsrt _ _ _) (ECAsrt _ _) = RCTpl (λ ())
  -}


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Properties.Neutral {{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 Wk
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties.Reflexivity
open import Definition.LogicalRelation.Properties.Escape
open import Definition.LogicalRelation.Properties.Symmetry

open import Tools.Product
import Tools.PropositionalEquality as PE

-- Neutral reflexive types are reducible.
neu : ∀ {l Γ A} (neA : Neutral A)
    → Γ ⊢ A
    → Γ ⊢ A ~ A ∷ U
    → Γ ⊩⟨ l ⟩ A
neu neA A A~A = ne′ _ (idRed:*: A) neA A~A

  -- Helper function for reducible neutral equality of a specific type of derivation.
neuEq′ : ∀ {l Γ A B} ([A] : Γ ⊩⟨ l ⟩ne A)
         (neA : Neutral A)
         (neB : Neutral B)
       → Γ ⊢ A → Γ ⊢ B
       → Γ ⊢ A ~ B ∷ U
       → Γ ⊩⟨ l ⟩ A ≡ B / ne-intr [A]
neuEq′ (noemb (ne K [ ⊢A , ⊢B , D ] neK K≡K)) neA neB A B A~B =
  let A≡K = whnfRed* D (ne neA)
  in  ne₌ _ (idRed:*: B) neB (PE.subst (λ x → _ ⊢ x ~ _ ∷ _) A≡K A~B)
neuEq′ (emb 0<1 x) neB A:≡:B = neuEq′ x neB A:≡:B

-- Neutrally equal types are of reducible equality.
neuEq : ∀ {l Γ A B} ([A] : Γ ⊩⟨ l ⟩ A)
        (neA : Neutral A)
        (neB : Neutral B)
      → Γ ⊢ A → Γ ⊢ B
      → Γ ⊢ A ~ B ∷ U
      → Γ ⊩⟨ l ⟩ A ≡ B / [A]
neuEq [A] neA neB A B A~B =
  irrelevanceEq (ne-intr (ne-elim neA [A]))
                [A]
                (neuEq′ (ne-elim neA [A]) neA neB A B A~B)

mutual
  -- Neutral reflexive terms are reducible.
  neuTerm : ∀ {l Γ A n} ([A] : Γ ⊩⟨ l ⟩ A) (neN : Neutral n)
          → Γ ⊢ n ∷ A
          → Γ ⊢ n ~ n ∷ A
          → Γ ⊩⟨ l ⟩ n ∷ A / [A]
  neuTerm (Uᵣ′ .⁰ 0<1 ⊢Γ) neN n n~n =
    Uₜ _ (idRedTerm:*: n) (ne neN) (~-to-≅ₜ n~n) (neu neN (univ n) n~n)
  neuTerm (ℕᵣ [ ⊢A , ⊢B , D ]) neN n n~n =
    let A≡ℕ  = subset* D
        n~n′ = ~-conv n~n A≡ℕ
        n≡n  = ~-to-≅ₜ n~n′
    in  ℕₜ _ (idRedTerm:*: (conv n A≡ℕ)) n≡n (ne (neNfₜ neN (conv n A≡ℕ) n~n′))
  neuTerm (Emptyᵣ [ ⊢A , ⊢B , D ]) neN n n~n =
    let A≡Empty  = subset* D
        n~n′ = ~-conv n~n A≡Empty
        n≡n  = ~-to-≅ₜ n~n′
    in  Emptyₜ _ (idRedTerm:*: (conv n A≡Empty)) n≡n (ne (neNfₜ neN (conv n A≡Empty) n~n′))
  neuTerm (Unitᵣ [ ⊢A , ⊢B , D ]) neN n n~n =
    let A≡Unit  = subset* D
        n~n′ = ~-conv n~n A≡Unit
    in  Unitₜ _ (idRedTerm:*: (conv n A≡Unit)) (ne neN)
  neuTerm (ne′ K [ ⊢A , ⊢B , D ] neK K≡K) neN n n~n =
    let A≡K = subset* D
    in  neₜ _ (idRedTerm:*: (conv n A≡K)) (neNfₜ neN (conv n A≡K)
            (~-conv n~n A≡K))
  neuTerm (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) neN n n~n =
    let A≡ΠFG = subset* (red D)
    in  Πₜ _ (idRedTerm:*: (conv n A≡ΠFG)) (ne neN) (~-to-≅ₜ (~-conv n~n A≡ΠFG))
           (λ {ρ} [ρ] ⊢Δ [a] [b] [a≡b] →
              let A≡ΠFG = subset* (red D)
                  ρA≡ρΠFG = Wk.wkEq [ρ] ⊢Δ (subset* (red D))
                  G[a]≡G[b] = escapeEq ([G] [ρ] ⊢Δ [b])
                                          (symEq ([G] [ρ] ⊢Δ [a]) ([G] [ρ] ⊢Δ [b])
                                                 (G-ext [ρ] ⊢Δ [a] [b] [a≡b]))
                  a = escapeTerm ([F] [ρ] ⊢Δ) [a]
                  b = escapeTerm ([F] [ρ] ⊢Δ) [b]
                  a≡b = escapeTermEq ([F] [ρ] ⊢Δ) [a≡b]
                  ρn = conv (Wk.wkTerm [ρ] ⊢Δ n) ρA≡ρΠFG
                  neN∘a = ∘ₙ (wkNeutral ρ neN)
                  neN∘b = ∘ₙ (wkNeutral ρ neN)
              in  neuEqTerm ([G] [ρ] ⊢Δ [a]) neN∘a neN∘b
                            (ρn ∘ⱼ a)
                            (conv (ρn ∘ⱼ b) (≅-eq G[a]≡G[b]))
                            (~-app (~-wk [ρ] ⊢Δ (~-conv n~n A≡ΠFG)) a≡b))
           (λ {ρ} [ρ] ⊢Δ [a] →
              let ρA≡ρΠFG = Wk.wkEq [ρ] ⊢Δ (subset* (red D))
                  a = escapeTerm ([F] [ρ] ⊢Δ) [a]
                  a≡a = escapeTermEq ([F] [ρ] ⊢Δ) (reflEqTerm ([F] [ρ] ⊢Δ) [a])
              in  neuTerm ([G] [ρ] ⊢Δ [a]) (∘ₙ (wkNeutral ρ neN))
                          (conv (Wk.wkTerm [ρ] ⊢Δ n) ρA≡ρΠFG ∘ⱼ a)
                          (~-app (~-wk [ρ] ⊢Δ (~-conv n~n A≡ΠFG)) a≡a))
  neuTerm (Σᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) neN ⊢n n~n =
    let A≡ΣFG = subset* (red D)
        ⊢Γ = wf ⊢F
        ⊢n = conv ⊢n A≡ΣFG
        n~n = ~-conv n~n A≡ΣFG

        [F] = [F] Wk.id ⊢Γ
        [fst] = neuTerm [F] (fstₙ neN)
                        (PE.subst
                          (λ x → _ ⊢ fst _ ∷ x)
                          (PE.sym (wk-id F))
                          (fstⱼ ⊢F ⊢G ⊢n))
                        (PE.subst
                          (λ x → _ ⊢ _ ~ _ ∷ x)
                          (PE.sym (wk-id F))
                          (~-fst ⊢F ⊢G n~n))
        [Gfst] = [G] Wk.id ⊢Γ [fst]
        [snd] = neuTerm [Gfst] (sndₙ neN)
                        (PE.subst
                          (λ x → _ ⊢ snd _ ∷ x)
                          (PE.cong (λ x → x [ fst _ ]) (PE.sym (wk-lift-id G)))
                          (sndⱼ ⊢F ⊢G ⊢n))
                        (PE.subst
                          (λ x → _ ⊢ _ ~ _ ∷ x)
                          (PE.cong (λ x → x [ fst _ ]) (PE.sym (wk-lift-id G)))
                          (~-snd ⊢F ⊢G n~n))
    in  Σₜ _ (idRedTerm:*: ⊢n) (ne neN) (~-to-≅ₜ n~n)
           [fst]
           [snd]
  neuTerm (emb 0<1 x) neN n = neuTerm x neN n

  -- Neutrally equal terms are of reducible equality.
  neuEqTerm : ∀ {l Γ A n n′} ([A] : Γ ⊩⟨ l ⟩ A)
              (neN : Neutral n) (neN′ : Neutral n′)
            → Γ ⊢ n  ∷ A
            → Γ ⊢ n′ ∷ A
            → Γ ⊢ n ~ n′ ∷ A
            → Γ ⊩⟨ l ⟩ n ≡ n′ ∷ A / [A]
  neuEqTerm (Uᵣ′ .⁰ 0<1 ⊢Γ) neN neN′ n n′ n~n′ =
    let [n]  = neu neN  (univ n) (~-trans n~n′ (~-sym n~n′))
        [n′] = neu neN′ (univ n′) (~-trans (~-sym n~n′) n~n′)
    in  Uₜ₌ _ _ (idRedTerm:*: n) (idRedTerm:*: n′) (ne neN) (ne neN′)
            (~-to-≅ₜ n~n′) [n] [n′] (neuEq [n] neN neN′ (univ n) (univ n′) n~n′)
  neuEqTerm (ℕᵣ [ ⊢A , ⊢B , D ]) neN neN′ n n′ n~n′ =
    let A≡ℕ = subset* D
        n~n′₁ = ~-conv n~n′ A≡ℕ
        n≡n′ = ~-to-≅ₜ n~n′₁
    in  ℕₜ₌ _ _ (idRedTerm:*: (conv n A≡ℕ)) (idRedTerm:*: (conv n′ A≡ℕ))
            n≡n′ (ne (neNfₜ₌ neN neN′ n~n′₁))
  neuEqTerm (Emptyᵣ [ ⊢A , ⊢B , D ]) neN neN′ n n′ n~n′ =
    let A≡Empty = subset* D
        n~n′₁ = ~-conv n~n′ A≡Empty
        n≡n′ = ~-to-≅ₜ n~n′₁
    in  Emptyₜ₌ _ _ (idRedTerm:*: (conv n A≡Empty)) (idRedTerm:*: (conv n′ A≡Empty))
            n≡n′ (ne (neNfₜ₌ neN neN′ n~n′₁))
  neuEqTerm (Unitᵣ [ ⊢A , ⊢B , D ]) neN neN′ n n′ n~n′ =
    let A≡Unit = subset* D
    in  Unitₜ₌ (conv n A≡Unit) (conv n′ A≡Unit)
  neuEqTerm (ne (ne K [ ⊢A , ⊢B , D ] neK K≡K)) neN neN′ n n′ n~n′ =
    let A≡K = subset* D
    in  neₜ₌ _ _ (idRedTerm:*: (conv n A≡K)) (idRedTerm:*: (conv n′ A≡K))
             (neNfₜ₌ neN neN′ (~-conv n~n′ A≡K))
  neuEqTerm (Πᵣ′ F G [ ⊢A , ⊢B , D ] ⊢F ⊢G A≡A [F] [G] G-ext) neN neN′ n n′ n~n′ =
    let [ΠFG] = Πᵣ′ F G [ ⊢A , ⊢B , D ] ⊢F ⊢G A≡A [F] [G] G-ext
        A≡ΠFG = subset* D
        n~n′₁ = ~-conv n~n′ A≡ΠFG
        n≡n′ = ~-to-≅ₜ n~n′₁
        n~n = ~-trans n~n′ (~-sym n~n′)
        n′~n′ = ~-trans (~-sym n~n′) n~n′
    in  Πₜ₌ _ _ (idRedTerm:*: (conv n A≡ΠFG)) (idRedTerm:*: (conv n′ A≡ΠFG))
            (ne neN) (ne neN′) n≡n′
            (neuTerm [ΠFG] neN n n~n) (neuTerm [ΠFG] neN′ n′ n′~n′)
            (λ {ρ} [ρ] ⊢Δ [a] →
               let ρA≡ρΠFG = Wk.wkEq [ρ] ⊢Δ A≡ΠFG
                   ρn = Wk.wkTerm [ρ] ⊢Δ n
                   ρn′ = Wk.wkTerm [ρ] ⊢Δ n′
                   a = escapeTerm ([F] [ρ] ⊢Δ) [a]
                   a≡a = escapeTermEq ([F] [ρ] ⊢Δ)
                                          (reflEqTerm ([F] [ρ] ⊢Δ) [a])
                   neN∙a   = ∘ₙ (wkNeutral ρ neN)
                   neN′∙a′ = ∘ₙ (wkNeutral ρ neN′)
               in  neuEqTerm ([G] [ρ] ⊢Δ [a]) neN∙a neN′∙a′
                             (conv ρn  ρA≡ρΠFG ∘ⱼ a)
                             (conv ρn′ ρA≡ρΠFG ∘ⱼ a)
                             (~-app (~-wk [ρ] ⊢Δ n~n′₁) a≡a))
  neuEqTerm (Σᵣ′ F G [ ⊢A , ⊢B , D ] ⊢F ⊢G A≡A [F] [G] G-ext) neN neN′ ⊢n ⊢n′ n~n′ =
    let [ΣFG] = Σᵣ′ F G [ ⊢A , ⊢B , D ] ⊢F ⊢G A≡A [F] [G] G-ext
        A≡ΣFG = subset* D
        n~n = ~-trans n~n′ (~-sym n~n′)
        n′~n′ = ~-trans (~-sym n~n′) n~n′

        ⊢Γ = wf ⊢F
        ⊢nΣ = conv ⊢n A≡ΣFG
        ⊢n′Σ = conv ⊢n′ A≡ΣFG
        n~n′Σ = ~-conv n~n′ A≡ΣFG
        n~nΣ = ~-conv n~n A≡ΣFG
        n′~n′Σ = ~-conv n′~n′ A≡ΣFG
        [F] = [F] Wk.id ⊢Γ
        ⊢fstnΣ = (PE.subst
                (λ x → _ ⊢ fst _ ∷ x)
                (PE.sym (wk-id F))
                (fstⱼ ⊢F ⊢G ⊢nΣ))
        ⊢fstn′Σ = (PE.subst
                    (λ x → _ ⊢ fst _ ∷ x)
                    (PE.sym (wk-id F))
                    (fstⱼ ⊢F ⊢G ⊢n′Σ))
        [fstn] = neuTerm [F] (fstₙ neN)
                         ⊢fstnΣ
                         (PE.subst
                           (λ x → _ ⊢ _ ~ _ ∷ x)
                           (PE.sym (wk-id F))
                           (~-fst ⊢F ⊢G n~nΣ))
        [fstn′] = neuTerm [F] (fstₙ neN′)
                          ⊢fstn′Σ
                          (PE.subst
                            (λ x → _ ⊢ _ ~ _ ∷ x)
                            (PE.sym (wk-id F))
                            (~-fst ⊢F ⊢G n′~n′Σ))
        [fstn≡fstn′] = neuEqTerm [F] (fstₙ neN) (fstₙ neN′)
                         ⊢fstnΣ
                         ⊢fstn′Σ
                         (PE.subst
                           (λ x → _ ⊢ _ ~ _ ∷ x)
                           (PE.sym (wk-id F))
                           (~-fst ⊢F ⊢G n~n′Σ))
        [Gfstn] = [G] Wk.id ⊢Γ [fstn]
        [Gfstn′] = PE.subst (λ x → _ ⊩⟨ _ ⟩ x [ fst _ ]) (wk-lift-id G) ([G] Wk.id ⊢Γ [fstn′])
        [fstn′≡fstn] = symEqTerm [F] [fstn≡fstn′]
        [Gfstn′≡Gfstn] = irrelevanceEq″
                           (PE.cong (λ x → x [ fst _ ]) (wk-lift-id G))
                           (PE.cong (λ x → x [ fst _ ]) (wk-lift-id G))
                           ([G] Wk.id ⊢Γ [fstn′]) [Gfstn′]
                           (G-ext Wk.id ⊢Γ [fstn′] [fstn] [fstn′≡fstn])
        Gfstn′≡Gfstn = ≅-eq (escapeEq [Gfstn′] [Gfstn′≡Gfstn])
        [sndn≡sndn′] = neuEqTerm [Gfstn] (sndₙ neN) (sndₙ neN′)
                          (PE.subst
                            (λ x → _ ⊢ snd _ ∷ x)
                            (PE.cong (λ x → x [ fst _ ]) (PE.sym (wk-lift-id G)))
                            (sndⱼ ⊢F ⊢G ⊢nΣ))
                          (PE.subst
                            (λ x → _ ⊢ snd _ ∷ x)
                            (PE.cong (λ x → x [ fst _ ]) (PE.sym (wk-lift-id G)))
                            (conv (sndⱼ ⊢F ⊢G ⊢n′Σ) Gfstn′≡Gfstn))
                          (PE.subst
                            (λ x → _ ⊢ _ ~ _ ∷ x)
                            (PE.cong (λ x → x [ fst _ ]) (PE.sym (wk-lift-id G)))
                            (~-snd ⊢F ⊢G n~n′Σ))
    in  Σₜ₌ _ _ (idRedTerm:*: ⊢nΣ) (idRedTerm:*: ⊢n′Σ)
            (ne neN) (ne neN′) (~-to-≅ₜ n~n′Σ)
            (neuTerm [ΣFG] neN ⊢n n~n) (neuTerm [ΣFG] neN′ ⊢n′ n′~n′)
            [fstn] [fstn′] [fstn≡fstn′] [sndn≡sndn′]
  neuEqTerm (emb 0<1 x) neN neN′ n:≡:n′ = neuEqTerm x neN neN′ n:≡:n′


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

open import HoTT
open import homotopy.LoopSpaceCircle
open import homotopy.PinSn
open import homotopy.SphereEndomorphism
open import homotopy.SuspAdjointLoop
open import groups.FromSusp
open import groups.Int
open import groups.Pointed
open import groups.SuspAdjointLoop
open import groups.ToOmega

module groups.SphereEndomorphism where

  ⊙SphereS-endo-group-structure : ∀ n → GroupStructure (⊙Sphere-endo (S n))
  ⊙SphereS-endo-group-structure n = Susp⊙→-group-structure (⊙Sphere n) (⊙Sphere (S n))

{-
  ⊙LiftSphereS-endo-group-structure : ∀ {i} n → GroupStructure (⊙LiftSphere-endo {i} (S n))
  ⊙LiftSphereS-endo-group-structure {i} n =
    LiftSusp⊙→-group-structure {j = i} (⊙Sphere n) (⊙Lift {j = i} (⊙Sphere (S n)))

  ⊙SphereS-endo-siso-⊙LiftSphereS-endo : ∀ {i} n →
    ⊙SphereS-endo-group-structure n ≃ᴳˢ ⊙LiftSphereS-endo-group-structure {i} n
  ⊙SphereS-endo-siso-⊙LiftSphereS-endo {i} n = ≃-to-≃ᴳˢ
    ⊙Lift-fmap-equiv
    (λ f g → ap (_⊙∘ ⊙pinch (⊙Sphere n) ⊙∘ ⊙lower {j = i}) $ ! $
      ⊙∨-rec (⊙Lift-fmap f) (⊙Lift-fmap g) ⊙∘ ⊙∨-fmap ⊙lift ⊙lift
        =⟨ ⊙λ= $ ⊙∨-rec-fmap (⊙Lift-fmap f) (⊙Lift-fmap g) ⊙lift ⊙lift ⟩
      ⊙∨-rec (⊙lift ⊙∘ f) (⊙lift ⊙∘ g)
        =⟨ ! $ ⊙λ= $ ⊙∨-rec-post∘ ⊙lift f g ⟩
      ⊙lift ⊙∘ ⊙∨-rec f g
        =∎)
-}

  Trunc-⊙SphereS-endo-group : ∀ n → Group₀
  Trunc-⊙SphereS-endo-group n = Trunc-Susp⊙→-group (⊙Sphere n) (⊙Sphere (S n))

{-
  Trunc-⊙LiftSphereS-endo-group : ∀ {i} n → Group i
  Trunc-⊙LiftSphereS-endo-group {i} n = Trunc-LiftSusp⊙→-group {j = i} (⊙Sphere n) (⊙Lift {j = i} (⊙Sphere (S n)))
-}

  Trunc-⊙SphereS-endo-⊙group : ∀ n → ⊙Group₀
  Trunc-⊙SphereS-endo-⊙group n = ⊙[ Trunc-⊙SphereS-endo-group n , [ ⊙idf _ ] ]ᴳ

{-
  Trunc-⊙LiftSphereS-endo-⊙group : ∀ {i} n → ⊙Group i
  Trunc-⊙LiftSphereS-endo-⊙group {i} n = ⊙[ Trunc-⊙LiftSphereS-endo-group {i = i} n , [ ⊙idf _ ] ]ᴳ
-}

  Trunc-⊙S¹-endo-group-iso-ℤ : Trunc-⊙SphereS-endo-group 0 ≃ᴳ ℤ-group
  Trunc-⊙S¹-endo-group-iso-ℤ =
    Trunc-⊙SphereS-endo-group 0
      ≃ᴳ⟨ Trunc-Susp⊙→-iso-Trunc-⊙→Ω ⊙Bool ⊙S¹ ⟩
    Trunc-⊙→Ω-group ⊙Bool ⊙S¹
      ≃ᴳ⟨ Trunc-⊙Bool→Ω-iso-π₁ ⊙S¹ ⟩
    πS 0 ⊙S¹
      ≃ᴳ⟨ πS-SphereS-iso-ℤ 0 ⟩
    ℤ-group
      ≃ᴳ∎

  Trunc-⊙S¹-endo-⊙group-iso-⊙ℤ : Trunc-⊙SphereS-endo-⊙group 0 ⊙≃ᴳ ℤ-⊙group
  Trunc-⊙S¹-endo-⊙group-iso-⊙ℤ = Trunc-⊙S¹-endo-group-iso-ℤ ,
    equiv-adj' ΩS¹-equiv-ℤ {a = ap (idf _) loop} {b = 1} (ap-idf loop)

  Trunc-⊙S¹-endo-⊙group-is-infinite-cyclic : is-infinite-cyclic (Trunc-⊙SphereS-endo-⊙group 0)
  Trunc-⊙S¹-endo-⊙group-is-infinite-cyclic =
    isomorphism-preserves'-infinite-cyclic Trunc-⊙S¹-endo-⊙group-iso-⊙ℤ ℤ-is-infinite-cyclic

  Trunc-⊙SphereS-endo-Susp-fmap-iso :
    ∀ n → Trunc-⊙SphereS-endo-group n ≃ᴳ Trunc-⊙SphereS-endo-group (S n)
  Trunc-⊙SphereS-endo-Susp-fmap-iso n =
    Trunc-group-fmap (Susp⊙→-Susp-fmap-shom (⊙Sphere n) (⊙Sphere (S n))) ,
    Trunc-⊙SphereS-endo-Susp-is-equiv n

  Trunc-⊙SphereS-endo-Susp-fmap-⊙iso :
    ∀ n → Trunc-⊙SphereS-endo-⊙group n ⊙≃ᴳ Trunc-⊙SphereS-endo-⊙group (S n)
  Trunc-⊙SphereS-endo-Susp-fmap-⊙iso n =
    Trunc-⊙SphereS-endo-Susp-fmap-iso n , ap [_] (⊙Susp-fmap-idf (⊙Sphere (S n)))

  Trunc-⊙SphereS-endo-⊙group-is-infinite-cyclic : ∀ n → is-infinite-cyclic (Trunc-⊙SphereS-endo-⊙group n)
  Trunc-⊙SphereS-endo-⊙group-is-infinite-cyclic O = Trunc-⊙S¹-endo-⊙group-is-infinite-cyclic
  Trunc-⊙SphereS-endo-⊙group-is-infinite-cyclic (S n) =
    isomorphism-preserves-infinite-cyclic
      (Trunc-⊙SphereS-endo-Susp-fmap-⊙iso n)
      (Trunc-⊙SphereS-endo-⊙group-is-infinite-cyclic n)

{-
  Trunc-⊙SphereS-endo-iso-Trunc-⊙LiftSphereS-endo : ∀ {i} n →
    Trunc-⊙SphereS-endo-group n ≃ᴳ Trunc-⊙LiftSphereS-endo-group {i} n
  Trunc-⊙SphereS-endo-iso-Trunc-⊙LiftSphereS-endo {i} n =
    Trunc-group-emap (⊙SphereS-endo-siso-⊙LiftSphereS-endo {i} n)

  Trunc-⊙SphereS-endo-⊙iso-Trunc-⊙LiftSphereS-endo : ∀ {i} n →
    Trunc-⊙SphereS-endo-⊙group n ⊙≃ᴳ Trunc-⊙LiftSphereS-endo-⊙group {i} n
  Trunc-⊙SphereS-endo-⊙iso-Trunc-⊙LiftSphereS-endo {i} n =
    Trunc-⊙SphereS-endo-iso-Trunc-⊙LiftSphereS-endo {i} n , idp

  Trunc-⊙LiftSphereS-endo-⊙group-is-infinite-cyclic : ∀ {i} n → is-infinite-cyclic (Trunc-⊙LiftSphereS-endo-⊙group {i} n)
  Trunc-⊙LiftSphereS-endo-⊙group-is-infinite-cyclic {i} n =
    isomorphism-preserves-infinite-cyclic
      (Trunc-⊙SphereS-endo-⊙iso-Trunc-⊙LiftSphereS-endo {i} n)
      (Trunc-⊙SphereS-endo-⊙group-is-infinite-cyclic n)
-}

  {- definition of degree -}

  Trunc-⊙SphereS-endo-degree : ∀ n → Trunc-⊙Sphere-endo (S n) → ℤ
  Trunc-⊙SphereS-endo-degree n = is-equiv.g (Trunc-⊙SphereS-endo-⊙group-is-infinite-cyclic n)

  abstract
    Trunc-⊙SphereS-endo-degree-Susp : ∀ n f
      →  Trunc-⊙SphereS-endo-degree (S n) (Trunc-fmap ⊙Susp-fmap f)
      == Trunc-⊙SphereS-endo-degree n f
    Trunc-⊙SphereS-endo-degree-Susp n f =
      ap (Trunc-⊙SphereS-endo-degree n) (GroupIso.g-f (Trunc-⊙SphereS-endo-Susp-fmap-iso n) f)

  ⊙SphereS-endo-degree : ∀ n → ⊙Sphere-endo (S n) → ℤ
  ⊙SphereS-endo-degree n = Trunc-⊙SphereS-endo-degree n ∘ [_]

  abstract
    ⊙SphereS-endo-degree-Susp : ∀ n f
      →  ⊙SphereS-endo-degree (S n) (⊙Susp-fmap f)
      == ⊙SphereS-endo-degree n f
    ⊙SphereS-endo-degree-Susp n f = Trunc-⊙SphereS-endo-degree-Susp n [ f ]

    ⊙SphereS-endo-degree-Susp' : ∀ n f
      →  ⊙SphereS-endo-degree (S n) (Susp-fmap f , idp)
      == Trunc-⊙SphereS-endo-degree n (Trunc-⊙SphereS-endo-in n [ f ])
    ⊙SphereS-endo-degree-Susp' n f =
      ⊙SphereS-endo-degree (S n) (Susp-fmap f , idp)
        =⟨ ap
            (Trunc-⊙SphereS-endo-degree (S n) ∘ Trunc-fmap (λ f → Susp-fmap f , idp))
            (! $ is-equiv.f-g (Trunc-⊙SphereS-endo-out-is-equiv n) [ f ]) ⟩
      Trunc-⊙SphereS-endo-degree (S n) (Trunc-fmap (λ f → Susp-fmap f , idp) (Trunc-fmap fst (Trunc-⊙SphereS-endo-in n [ f ])))
        =⟨ ap (Trunc-⊙SphereS-endo-degree (S n))
            (Trunc-fmap-∘ (λ f → Susp-fmap f , idp) fst (Trunc-⊙SphereS-endo-in n [ f ])) ⟩
      Trunc-⊙SphereS-endo-degree (S n) (Trunc-fmap ⊙Susp-fmap (Trunc-⊙SphereS-endo-in n [ f ]))
        =⟨ Trunc-⊙SphereS-endo-degree-Susp n (Trunc-⊙SphereS-endo-in n [ f ]) ⟩
      Trunc-⊙SphereS-endo-degree n (Trunc-⊙SphereS-endo-in n [ f ])
        =∎

    ⊙SphereS-endo-degree-base-indep : ∀ n {f g}
      → fst f ∼ fst g
      → ⊙SphereS-endo-degree n f == ⊙SphereS-endo-degree n g
    ⊙SphereS-endo-degree-base-indep n {f} {g} f∼g =
      ! (⊙SphereS-endo-degree-Susp n f)
      ∙ ap (λ f → ⊙SphereS-endo-degree (S n) (Susp-fmap f , idp)) (λ= f∼g)
      ∙ ⊙SphereS-endo-degree-Susp n g

{-
  Trunc-⊙LiftSphereS-endo-degree : ∀ {i} n → Trunc-⊙LiftSphere-endo {i} (S n) → ℤ
  Trunc-⊙LiftSphereS-endo-degree {i} n = is-equiv.g (Trunc-⊙LiftSphereS-endo-⊙group-is-infinite-cyclic {i} n)

  abstract
    Trunc-⊙LiftSphereS-endo-degree-Susp : ∀ {i} n f
      →  Trunc-⊙LiftSphereS-endo-degree {i} (S n) (Trunc-fmap (λ f → ⊙Lift-fmap (⊙Susp-fmap (⊙lower ⊙∘ f ⊙∘ ⊙lift))) f)
      == Trunc-⊙LiftSphereS-endo-degree {i} n f
    Trunc-⊙LiftSphereS-endo-degree-Susp {i} n =
      Trunc-elim
        {{λ _ → =-preserves-level ℤ-is-set}}
        (λ f → ⊙SphereS-endo-degree-Susp n (⊙lower ⊙∘ f ⊙∘ ⊙lift))

  ⊙LiftSphereS-endo-degree : ∀ {i} n → ⊙LiftSphere-endo {i} (S n) → ℤ
  ⊙LiftSphereS-endo-degree {i} n = Trunc-⊙LiftSphereS-endo-degree {i} n ∘ [_]

  abstract
    ⊙LiftSphereS-endo-degree-Susp : ∀ {i} n f
      →  ⊙LiftSphereS-endo-degree {i} (S n) (⊙Lift-fmap (⊙Susp-fmap (⊙lower ⊙∘ f ⊙∘ ⊙lift)))
      == ⊙LiftSphereS-endo-degree {i} n f
    ⊙LiftSphereS-endo-degree-Susp {i} n f =
      Trunc-⊙LiftSphereS-endo-degree-Susp {i} n [ f ]

    ⊙LiftSphereS-endo-degree-base-indep : ∀ {i} n {f g}
      → fst f ∼ fst g
      → ⊙LiftSphereS-endo-degree {i} n f == ⊙LiftSphereS-endo-degree {i} n g
    ⊙LiftSphereS-endo-degree-base-indep {i} n {f} {g} f∼g =
      ! (⊙LiftSphereS-endo-degree-Susp {i} n f)
      ∙ ap (λ f → ⊙LiftSphereS-endo-degree {i} (S n) (⊙Lift-fmap (Susp-fmap (lower ∘ f ∘ lift) , idp))) (λ= f∼g)
      ∙ ⊙LiftSphereS-endo-degree-Susp {i} n g
-}


{-# OPTIONS --safe #-}

module Definition.Conversion.Weakening where

open import Definition.Untyped as U hiding (wk)
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Weakening
open import Definition.Conversion

import Tools.PropositionalEquality as PE

mutual
  -- Weakening of algorithmic equality of neutrals.
  wk~↑! : ∀ {ρ t u A Γ Δ l} ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
      → Γ ⊢ t ~ u ↑! A ^ l
      → Δ ⊢ U.wk ρ t ~ U.wk ρ u ↑! U.wk ρ A ^ l
  wk~↑! {ρ} [ρ] ⊢Δ (var-refl x₁ x≡y) = var-refl (wkTerm [ρ] ⊢Δ x₁) (PE.cong (wkVar ρ) x≡y)
  wk~↑! ρ ⊢Δ (app-cong {rF = !} {G = G} t~u x) =
    PE.subst (λ x → _ ⊢ _ ~ _ ↑! x ^ _) (PE.sym (wk-β G))
             (app-cong (wk~↓! ρ ⊢Δ t~u) (wkConv↑Term ρ ⊢Δ x))
  wk~↑! ρ ⊢Δ (app-cong {rF = %} {G = G} t~u x) =
    PE.subst (λ x → _ ⊢ _ ~ _ ↑! x ^ _) (PE.sym (wk-β G))
             (app-cong (wk~↓! ρ ⊢Δ t~u) (wk~↑% ρ ⊢Δ x))
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (natrec-cong {k} {l} {h} {g} {a₀} {b₀} {F} {G} {ll} x x₁ x₂ t~u) =
    PE.subst (λ x → _ ⊢ U.wk ρ (natrec _ F a₀ h k) ~ _ ↑! x ^ _) (PE.sym (wk-β F))
             (natrec-cong (wkConv↑ (lift [ρ]) (⊢Δ ∙ (univ (ℕⱼ ⊢Δ))) x)
                          (PE.subst (λ x → _ ⊢ _ [conv↑] _ ∷ x ^ _) (wk-β F)
                                    (wkConv↑Term [ρ] ⊢Δ x₁))
                          (PE.subst (λ x → Δ ⊢ U.wk ρ h [conv↑] U.wk ρ g ∷ x ^ ι ll)
                                    (wk-β-natrec _ F ! _) (wkConv↑Term [ρ] ⊢Δ x₂))
                          (wk~↓! [ρ] ⊢Δ t~u))
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Emptyrec-cong {k} {l} {F} {G} x t~u) =
    Emptyrec-cong (wkConv↑ [ρ] ⊢Δ x) (wk~↑% [ρ] ⊢Δ t~u)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Id-cong X x x₁) = Id-cong (wk~↓! [ρ] ⊢Δ X) (wkConv↑Term [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Id-ℕ x x₁) = Id-ℕ (wk~↓! [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Id-ℕ0 x) = Id-ℕ0 (wk~↓! [ρ] ⊢Δ x)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Id-ℕS x x₁) = Id-ℕS (wkConv↑Term [ρ] ⊢Δ x) (wk~↓! [ρ] ⊢Δ x₁)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Id-U x x₁) = Id-U (wk~↓! [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Id-Uℕ x) = Id-Uℕ (wk~↓! [ρ] ⊢Δ x)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (Id-UΠ x x₁) = Id-UΠ (wkConv↑Term [ρ] ⊢Δ x) (wk~↓! [ρ] ⊢Δ x₁)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-cong X x x₁ x₂ x₃) =
    cast-cong (wk~↓! [ρ] ⊢Δ X) (wkConv↑Term [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁) (wkTerm [ρ] ⊢Δ x₂) (wkTerm [ρ] ⊢Δ x₃)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-ℕ X x x₁ x₂) =
    cast-ℕ (wk~↓! [ρ] ⊢Δ X) (wkConv↑Term [ρ] ⊢Δ x) (wkTerm [ρ] ⊢Δ x₁) (wkTerm [ρ] ⊢Δ x₂)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-ℕℕ X x x₁) =
    cast-ℕℕ (wk~↓! [ρ] ⊢Δ X) (wkTerm [ρ] ⊢Δ x) (wkTerm [ρ] ⊢Δ x₁)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-Π x X x₁ x₂ x₃) =
    cast-Π (wkConv↑Term [ρ] ⊢Δ x) (wk~↓! [ρ] ⊢Δ X) (wkConv↑Term [ρ] ⊢Δ x₁) (wkTerm [ρ] ⊢Δ x₂) (wkTerm [ρ] ⊢Δ x₃)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-Πℕ x x₁ x₂ x₃) =
    cast-Πℕ (wkConv↑Term [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁) (wkTerm [ρ] ⊢Δ x₂) (wkTerm [ρ] ⊢Δ x₃)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-ℕΠ x x₁ x₂ x₃) =
    cast-ℕΠ (wkConv↑Term [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁) (wkTerm [ρ] ⊢Δ x₂) (wkTerm [ρ] ⊢Δ x₃)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-ΠΠ%! x x₁ x₂ x₃ x₄) =
    cast-ΠΠ%! (wkConv↑Term [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁) (wkConv↑Term [ρ] ⊢Δ x₂) (wkTerm [ρ] ⊢Δ x₃) (wkTerm [ρ] ⊢Δ x₄)
  wk~↑! {ρ} {Δ = Δ} [ρ] ⊢Δ (cast-ΠΠ!% x x₁ x₂ x₃ x₄) =
    cast-ΠΠ!% (wkConv↑Term [ρ] ⊢Δ x) (wkConv↑Term [ρ] ⊢Δ x₁) (wkConv↑Term [ρ] ⊢Δ x₂) (wkTerm [ρ] ⊢Δ x₃) (wkTerm [ρ] ⊢Δ x₄)

  wk~↑% : ∀ {ρ t u A Γ Δ l } ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
        → Γ ⊢ t ~ u ↑% A ^ l
        → Δ ⊢ U.wk ρ t ~ U.wk ρ u ↑% U.wk ρ A ^ l
  wk~↑% [ρ] ⊢Δ (%~↑ ⊢k ⊢l) =
    %~↑ (wkTerm [ρ] ⊢Δ ⊢k) (wkTerm [ρ] ⊢Δ ⊢l)

  wk~↑ : ∀ {ρ t u A rA lA Γ Δ} ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
      → Γ ⊢ t ~ u ↑ A ^ [ rA , lA ]
      → Δ ⊢ U.wk ρ t ~ U.wk ρ u ↑ U.wk ρ A ^ [ rA , lA ]
  wk~↑ [ρ] ⊢Δ (~↑! x) = ~↑! (wk~↑! [ρ] ⊢Δ x)
  wk~↑ [ρ] ⊢Δ (~↑% x) = ~↑% (wk~↑% [ρ] ⊢Δ x)

  -- Weakening of algorithmic equality of neutrals in WHNF.
  wk~↓! : ∀ {ρ t u A Γ Δ l} ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
      → Γ ⊢ t ~ u ↓! A ^ l
      → Δ ⊢ U.wk ρ t ~ U.wk ρ u ↓! U.wk ρ A ^ l
  wk~↓! {ρ} [ρ] ⊢Δ ([~] A₁ D whnfA k~l) =
    [~] (U.wk ρ A₁) (wkRed* [ρ] ⊢Δ D) (wkWhnf ρ whnfA) (wk~↑! [ρ] ⊢Δ k~l)

  -- Weakening of algorithmic equality of types.
  wkConv↑ : ∀ {ρ A B rA Γ Δ} ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
          → Γ ⊢ A [conv↑] B ^ rA
          → Δ ⊢ U.wk ρ A [conv↑] U.wk ρ B ^ rA
  wkConv↑ {ρ} [ρ] ⊢Δ ([↑] A′ B′ D D′ whnfA′ whnfB′ A′<>B′) =
    [↑] (U.wk ρ A′) (U.wk ρ B′) (wkRed* [ρ] ⊢Δ D) (wkRed* [ρ] ⊢Δ D′)
        (wkWhnf ρ whnfA′) (wkWhnf ρ whnfB′) (wkConv↓ [ρ] ⊢Δ A′<>B′)

  -- Weakening of algorithmic equality of types in WHNF.
  wkConv↓ : ∀ {ρ A B rA Γ Δ} ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
         → Γ ⊢ A [conv↓] B ^ rA
         → Δ ⊢ U.wk ρ A [conv↓] U.wk ρ B ^ rA
  wkConv↓ ρ ⊢Δ (U-refl eqr x) = U-refl eqr ⊢Δ
  wkConv↓ ρ ⊢Δ (univ x) = univ (wkConv↓Term ρ ⊢Δ x)

  -- Weakening of algorithmic equality of terms.
  wkConv↑Term : ∀ {ρ t u A Γ Δ l} ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
             → Γ ⊢ t [conv↑] u ∷ A ^ l
             → Δ ⊢ U.wk ρ t [conv↑] U.wk ρ u ∷ U.wk ρ A ^ l
  wkConv↑Term {ρ} [ρ] ⊢Δ ([↑]ₜ B t′ u′ D d d′ whnfB whnft′ whnfu′ t<>u) =
    [↑]ₜ (U.wk ρ B) (U.wk ρ t′) (U.wk ρ u′)
         (wkRed* [ρ] ⊢Δ D) (wkRed*Term [ρ] ⊢Δ d) (wkRed*Term [ρ] ⊢Δ d′)
         (wkWhnf ρ whnfB) (wkWhnf ρ whnft′) (wkWhnf ρ whnfu′)
         (wkConv↓Term [ρ] ⊢Δ t<>u)

  -- Weakening of algorithmic equality of terms in WHNF.
  wkConv↓Term : ∀ {ρ t u A Γ Δ l} ([ρ] : ρ ∷ Δ ⊆ Γ) → ⊢ Δ
             → Γ ⊢ t [conv↓] u ∷ A ^ l
             → Δ ⊢ U.wk ρ t [conv↓] U.wk ρ u ∷ U.wk ρ A ^ l
  wkConv↓Term ρ ⊢Δ (U-refl eqr x) = U-refl eqr ⊢Δ
  wkConv↓Term ρ ⊢Δ (ne x) = ne (wk~↓! ρ ⊢Δ x)
  wkConv↓Term ρ ⊢Δ (ℕ-ins x) =
    ℕ-ins (wk~↓! ρ ⊢Δ x)
  -- wkConv↓Term ρ ⊢Δ (Empty-ins x) =
  --   Empty-ins (wk~↓% ρ ⊢Δ x)
  wkConv↓Term {ρ} [ρ] ⊢Δ (ne-ins t u x x₁) =
    ne-ins (wkTerm [ρ] ⊢Δ t) (wkTerm [ρ] ⊢Δ u) (wkNeutral ρ x) (wk~↓! [ρ] ⊢Δ x₁)
  wkConv↓Term ρ ⊢Δ (zero-refl x) = zero-refl ⊢Δ
  wkConv↓Term ρ ⊢Δ (suc-cong t<>u) = suc-cong (wkConv↑Term ρ ⊢Δ t<>u)
  wkConv↓Term {ρ} {Δ = Δ} [ρ] ⊢Δ (η-eq {F = F} {G = G} {rF = rF} {lF = lF} {lG = lG} l< l<' x x₁ x₂ y y₁ t<>u) =
    let ⊢ρF = wk [ρ] ⊢Δ x
    in  η-eq l< l<' ⊢ρF (wkTerm [ρ] ⊢Δ x₁) (wkTerm [ρ] ⊢Δ x₂)
             (wkFunction ρ y) (wkFunction ρ y₁)
             (PE.subst₃ (λ x y z → Δ ∙ U.wk ρ F ^ [ rF , ι lF ] ⊢ x [conv↑] y ∷ z ^ ι lG)
                        (PE.cong₃ _∘_^_ (PE.sym (wk1-wk≡lift-wk1 _ _)) PE.refl PE.refl)
                        (PE.cong₃ _∘_^_ (PE.sym (wk1-wk≡lift-wk1 _ _)) PE.refl PE.refl)
                        PE.refl
                        (wkConv↑Term (lift [ρ]) (⊢Δ ∙ ⊢ρF) t<>u))
  wkConv↓Term ρ ⊢Δ (ℕ-refl x) = ℕ-refl ⊢Δ
  wkConv↓Term ρ ⊢Δ (Empty-refl x _) = Empty-refl x ⊢Δ
  wkConv↓Term ρ ⊢Δ (Π-cong eql eqr eqlF eqlG l< l<'   x A<>B A<>B₁) =
    let ⊢ρF = wk ρ ⊢Δ x
    in  Π-cong eql eqr eqlF eqlG l< l<' ⊢ρF (wkConv↑Term ρ ⊢Δ A<>B) (wkConv↑Term (lift ρ) (⊢Δ ∙ ⊢ρF) A<>B₁)
  wkConv↓Term ρ ⊢Δ (∃-cong eql x A<>B A<>B₁) =
    let ⊢ρF = wk ρ ⊢Δ x
    in  ∃-cong eql ⊢ρF (wkConv↑Term ρ ⊢Δ A<>B) (wkConv↑Term (lift ρ) (⊢Δ ∙ ⊢ρF) A<>B₁)


module UniDB.Subst.Core where

open import UniDB.Spec public
open import UniDB.Morph.Unit

record Ap (T X : STX) : Set₁ where
  field
    ap : {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
      {γ₁ γ₂ : Dom} (ξ : Ξ γ₁ γ₂) (x : X γ₁) → X γ₂
open Ap {{...}} public

record ApVr (T : STX) {{vrT : Vr T}} {{apTT : Ap T T}} : Set₁ where
  field
    ap-vr :
      {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
      {γ₁ γ₂ : Dom} (ξ : Ξ γ₁ γ₂) →
      (i : Ix γ₁) → ap {T} ξ (vr i) ≡ lk {T} ξ i
open ApVr {{...}} public

record LkCompAp
  (T : STX) {{vrT : Vr T}} {{apTT : Ap T T}}
  (Ξ : MOR) {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{compΞ : Comp Ξ}} : Set₁ where
  field
    lk-⊙-ap : {γ₁ γ₂ γ₃ : Dom} (ξ₁ : Ξ γ₁ γ₂) (ξ₂ : Ξ γ₂ γ₃)
      (i : Ix γ₁) → lk {T} (ξ₁ ⊙ ξ₂) i ≡ ap {T} ξ₂ (lk ξ₁ i)
open LkCompAp {{...}} public

record ApIdm
  (T : STX) {{vrT : Vr T}}
  (X : STX) {{apTX : Ap T X}} : Set₁ where
  field
    ap-idm :
      {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{idmΞ : Idm Ξ}}
      {{lkIdmTΞ : LkIdm T Ξ}} {{upIdmΞ : UpIdm Ξ}}
      {γ : Dom} (x : X γ) →
      ap {T} (idm {Ξ} γ) x ≡ x
open ApIdm {{...}} public

record ApRel
  (T : STX) {{vrT : Vr T}}
  (X : STX) {{apTX : Ap T X}} : Set₁ where
  field
    ap-rel≅ :
      {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
      {Ζ : MOR} {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}}
      {γ₁ γ₂ : Dom} {ξ : Ξ γ₁ γ₂} {ζ : Ζ γ₁ γ₂} (hyp : [ T ] ξ ≅ ζ)
      (x : X γ₁) → ap {T} ξ x ≡ ap {T} ζ x

  ap-rel≃ :  {{wkT : Wk T}}
    {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{lkUpTΞ : LkUp T Ξ}}
    {Ζ : MOR} {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}} {{lkUpTΖ : LkUp T Ζ}}
    {γ₁ γ₂ : Dom} {ξ : Ξ γ₁ γ₂} {ζ : Ζ γ₁ γ₂} (hyp : [ T ] ξ ≃ ζ)
    (x : X γ₁) → ap {T} ξ x ≡ ap {T} ζ x
  ap-rel≃ hyp = ap-rel≅ (≃-to-≅` (≃-↑ hyp))
open ApRel {{...}} public

module _
  (T : STX) {{vrT : Vr T}} {{wkT : Wk T}}
  (X : STX) {{wkX : Wk X}} {{apTX : Ap T X}}
  where

  record ApWkmWk : Set₁ where
    field
      ap-wkm-wk₁ :
        {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{wkmΞ : Wkm Ξ}}
        {{lkUpTΞ : LkUp T Ξ}} {{lkWkmTΞ : LkWkm T Ξ}}
        {γ : Dom} (x : X γ) →
        ap {T} (wkm {Ξ} 1) x ≡ wk₁ x
      ap-wkm-wk :
        {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{wkmΞ : Wkm Ξ}}
        {{lkUpTΞ : LkUp T Ξ}} {{lkWkmTΞ : LkWkm T Ξ}}
        {γ : Dom} (δ : Dom) (x : X γ) →
        ap {T} (wkm {Ξ} δ) x ≡ wk δ x

  open ApWkmWk {{...}} public

module _
  (T : STX) {{vrT : Vr T}} {{wkT : Wk T}}
  (X : STX) {{wkX : Wk X}} {{apTX : Ap T X}}
  where

  record ApWk : Set₁ where
    field
      ap-wk₁ :
        {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
        {{lkUpTΞ : LkUp T Ξ}}
        {γ₁ γ₂ : Dom} (ξ : Ξ γ₁ γ₂) (x : X γ₁) →
        ap {T} (ξ ↑₁) (wk₁ x) ≡ wk₁ (ap {T} ξ x)
  open ApWk {{...}} public

module _
  (T : STX) {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}}
  (X : STX) {{apTX : Ap T X}}
  where

  record ApComp : Set₁ where
    field
      ap-⊙ :
        {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{compΞ : Comp Ξ}}
        {{upCompΞ : UpComp Ξ}} {{lkCompTΞ : LkCompAp T Ξ}}
        {γ₁ γ₂ γ₃ : Dom} (ξ₁ : Ξ γ₁ γ₂) (ξ₂ : Ξ γ₂ γ₃) →
          (x : X γ₁) → ap {T} (ξ₁ ⊙ ξ₂) x ≡ ap {T} ξ₂ (ap {T} ξ₁ x)
  open ApComp {{...}} public

instance
  iApIx : Ap Ix Ix
  ap {{iApIx}} = lk

  iApVrIx : ApVr Ix
  ap-vr {{iApVrIx}} ξ i = refl

  iApIdmIxIx : ApIdm Ix Ix
  ap-idm {{iApIdmIxIx}} {Ξ} = lk-idm {Ix} {Ξ}

  iApWkmWkIxIx : ApWkmWk Ix Ix
  ap-wkm-wk₁ {{iApWkmWkIxIx}} {Ξ} = lk-wkm {Ix} {Ξ} 1
  ap-wkm-wk {{iApWkmWkIxIx}} {Ξ} = lk-wkm {Ix} {Ξ}

  iApRelIxIx : ApRel Ix Ix
  ap-rel≅ {{iApRelIxIx}} = lk≃ ∘ ≅-to-≃

  iApWkIxIx : ApWk Ix Ix
  ap-wk₁ {{iApWkIxIx}} {Ξ} ξ i = lk-↑₁-suc {Ix} {Ξ} ξ i

-- TODO: Remove
module _
  (T : STX) {{vrT : Vr T}} {{apTT : Ap T T}}
  (Ξ : MOR) {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
  (Ζ : MOR) {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}}
  (Θ : MOR) {{lkTΘ : Lk T Θ}} {{upΘ : Up Θ}}
  {{hcompΞΖΘ : HComp Ξ Ζ Θ}}
  where

  record LkHCompAp : Set₁ where
    field
      lk-⊡-ap : {γ₁ γ₂ γ₃ : Dom} (ξ : Ξ γ₁ γ₂) (ζ : Ζ γ₂ γ₃)
        (i : Ix γ₁) → lk {T} {Θ} (ξ ⊡ ζ) i ≡ ap {T} ζ (lk ξ i)
  open LkHCompAp {{...}} public

-- TODO: Remove
module _
  (T : STX) {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}}
  (X : STX) {{apTX : Ap T X}}
  where

  record ApHComp : Set₁ where
    field
      ap-⊡ :
        {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
        {Ζ : MOR} {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}}
        {Θ : MOR} {{lkTΘ : Lk T Θ}} {{upΘ : Up Θ}}
        {{hcompΞΖΘ : HComp Ξ Ζ Θ}} {{upHCompΞ : UpHComp Ξ Ζ Θ}}
        {{lkHCompTΞΖΘ : LkHCompAp T Ξ Ζ Θ}}
        {γ₁ γ₂ γ₃ : Dom} (ξ : Ξ γ₁ γ₂) (ζ : Ζ γ₂ γ₃) →
          (x : X γ₁) → ap {T} {X} {Θ} (ξ ⊡ ζ) x ≡ ap {T} ζ (ap {T} ξ x)
  open ApHComp {{...}} public

-- TODO: Move
module _
  {T : STX} {{vrT : Vr T}} {{wkT : Wk T}} {{wkVrT : WkVr T}}
  {X : STX} {{wkX : Wk X}} {{apTX : Ap T X}} {{apRelTX : ApRel T X}} {{apIdmTX : ApIdm T X}}
  {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{idmΞ : Idm Ξ}} {{lkIdmTΞ : LkIdm T Ξ}}
  {{lkUpTΞ : LkUp T Ξ}}
  where

  ap-idm` : {γ : Dom} (x : X γ) → ap {T} (idm {Ξ} γ) x ≡ x
  ap-idm` {γ} x = begin
    ap {T} (idm {Ξ} γ) x    ≡⟨ ap-rel≃ {T} lem x ⟩
    ap {T} (idm {Unit} γ) x ≡⟨ ap-idm {T} x ⟩
    x ∎
    where
      lem : [ T ] idm {Ξ} γ ≃ unit
      lk≃ lem = lk-idm {T} {Ξ}


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

module Fragment.Equational.Theory.Base where

open import Function using (_∘_)

open import Fragment.Algebra.Signature
open import Fragment.Algebra.Free
open import Fragment.Algebra.Algebra
open import Fragment.Algebra.Properties

open import Data.Nat using (ℕ)
open import Data.Fin using (Fin)
open import Data.Product using (_×_; map)

Eq : (Σ : Signature) → (n : ℕ) → Set
Eq Σ n = ∥ F Σ n ∥ × ∥ F Σ n ∥

record Theory : Set₁ where
  field
    Σ     : Signature
    eqs   : ℕ → Set
    _⟦_⟧ₑ : ∀ {arity} → eqs arity → Eq Σ arity

  open Signature Σ

open Theory public

data ExtendedEq (Θ : Theory) (E : ℕ → Set) : ℕ → Set where
  newₑ : ∀ {n} → E n → ExtendedEq Θ E n
  oldₑ : ∀ {n} → eqs Θ n → ExtendedEq Θ E n

_⦅_⦆ₒ : (Θ : Theory) → (O : ℕ → Set) → Theory
Θ ⦅ O ⦆ₒ = record { Σ     = (Σ Θ) ⦅ O ⦆
                  ; eqs   = eqs Θ
                  ; _⟦_⟧ₑ = (map extend extend) ∘ Θ ⟦_⟧ₑ
                  }

_⦅_/_⦆ : (Θ : Theory)
         → (E : ℕ → Set)
         → (∀ {n} → E n → Eq (Σ Θ) n)
         → Theory
Θ ⦅ E / ⟦_⟧' ⦆ = record { Σ     = Σ Θ
                        ; eqs   = ExtendedEq Θ E
                        ; _⟦_⟧ₑ = withE
                        }
  where withE : ∀ {n} → ExtendedEq Θ E n → Eq (Σ Θ) n
        withE (newₑ eq) = ⟦ eq ⟧'
        withE (oldₑ eq) = Θ ⟦ eq ⟧ₑ

_⦅_∣_/_⦆ : (Θ : Theory)
           → (O : ℕ → Set)
           → (E : ℕ → Set)
           → (∀ {n} → E n → Eq ((Σ Θ) ⦅ O ⦆) n)
           → Theory
Θ ⦅ O ∣ E / ⟦_⟧' ⦆ = (Θ ⦅ O ⦆ₒ) ⦅ E / ⟦_⟧' ⦆


module UniDB.Subst.Pair where

open import UniDB.Subst.Core
open import UniDB.Morph.Pair

instance
  iLkPair :
    {T : STX} {{apTT : Ap T T}}
    {Ξ : MOR} {{lkTΞ : Lk T Ξ}}
    {Ζ : MOR} {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}} →
    Lk T (Pair Ξ Ζ)
  lk {{iLkPair {T}}} (ξ ⊗ ζ) i = ap {T} ζ (lk {T} ξ i)

  iLkRenPair :
    {T : STX} {{vrT : Vr T}} {{apTT : Ap T T}} {{apVrT : ApVr T}}
    {Ξ : MOR} {{lkIxΞ : Lk Ix Ξ}} {{lkTΞ : Lk T Ξ}} {{lkRenTΞ : LkRen T Ξ}}
    {Ζ : MOR} {{lkIxΖ : Lk Ix Ζ}} {{lkTΖ : Lk T Ζ}} {{lkRenTΖ : LkRen T Ζ}} {{upΖ : Up Ζ}} →
    LkRen T (Pair Ξ Ζ)
  lk-ren {{iLkRenPair {T} {Ξ} {Ζ}}} (ξ ⊗ ζ) i = begin
    ap {T} ζ (lk {T} ξ i)        ≡⟨ cong (ap {T} ζ) (lk-ren {T} ξ i) ⟩
    ap {T} ζ (vr (lk {Ix} ξ i))  ≡⟨ ap-vr {T} ζ (lk {Ix} ξ i) ⟩
    lk {T} ζ (lk {Ix} ξ i)       ≡⟨ lk-ren {T} ζ (lk {Ix} ξ i) ⟩
    vr (lk {Ix} ζ (lk {Ix} ξ i)) ∎

  iLkHCompPair :
    {T : STX} {{vrT : Vr T}} {{apTT : Ap T T}}
    {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
    {Ζ : MOR} {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}} →
    LkHCompAp T Ξ Ζ (Pair Ξ Ζ)
  lk-⊡-ap {{iLkHCompPair}} ξ ζ i = refl

iLkUpPairRenaming :
  (T : STX) {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}} {{apVrT : ApVr T}}
  (Ξ : MOR) {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{lkUpTΞ : LkUp T Ξ}}
  {{lkIxΞ : Lk Ix Ξ}} {{lkRenTΞ : LkRen T Ξ}} {{lkUpIxΞ : LkUp Ix Ξ}}
  (Ζ : MOR) {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}} {{lkUpTΖ : LkUp T Ζ}} →
  LkUp T (Pair Ξ Ζ)
lk-↑₁-zero {{iLkUpPairRenaming T Ξ Ζ}} (ξ ⊗ ζ) = begin
  ap {T} (ζ ↑₁) (lk {T} (ξ ↑₁) zero)     ≡⟨ cong (ap {T} (ζ ↑₁)) (lk-↑₁-zero ξ) ⟩
  ap {T} (ζ ↑₁) (vr zero)                ≡⟨ ap-vr {T} (ζ ↑₁) zero ⟩
  lk (ζ ↑₁) zero                         ≡⟨ lk-↑₁-zero ζ ⟩
  vr zero                                ∎
lk-↑₁-suc {{iLkUpPairRenaming T Ξ Ζ}} (ξ ⊗ ζ) i = begin
  ap {T} (ζ ↑₁) (lk (ξ ↑₁) (suc i))      ≡⟨ cong (ap {T} (ζ ↑₁)) (lk-ren (ξ ↑₁) (suc i)) ⟩
  ap {T} (ζ ↑₁) (vr (lk (ξ ↑₁) (suc i))) ≡⟨ ap-vr {T} (ζ ↑₁) (lk (ξ ↑₁) (suc i)) ⟩
  lk (ζ ↑₁) (lk (ξ ↑₁) (suc i))          ≡⟨ cong (lk (_↑₁ ζ)) (lk-↑₁-suc ξ i) ⟩
  lk (ζ ↑₁) (suc (lk ξ i))               ≡⟨ lk-↑₁-suc ζ (lk ξ i) ⟩
  wk₁ (lk ζ (lk ξ i))                    ≡⟨ cong wk₁ (sym (ap-vr {T} ζ (lk ξ i))) ⟩
  wk₁ (ap {T} ζ (vr (lk ξ i)))           ≡⟨ cong wk₁ (cong (ap {T} ζ) (sym (lk-ren ξ i))) ⟩
  wk₁ (ap {T} ζ (lk ξ i))                ∎

iLkUpPairSubstitution :
  (T : STX) {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}} {{apVrT : ApVr T}}
  (Ξ : MOR) {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{lkUpTΞ : LkUp T Ξ}}
  (Ζ : MOR) {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}} {{lkUpTΖ : LkUp T Ζ}}
  (ap-wk₁-TΖ : {γ₁ γ₂ : Dom} (ζ : Ζ γ₁ γ₂) (t : T γ₁) →
    ap {T} (ζ ↑₁) (wk₁ t) ≡ wk₁ (ap {T} ζ t)) →
  LkUp T (Pair Ξ Ζ)
lk-↑₁-zero {{iLkUpPairSubstitution T Ξ Ζ ap-wk₁-TΖ}} (ξ ⊗ ζ) = begin
  ap {T} (ζ ↑₁) (lk (ξ ↑₁) zero) ≡⟨ cong (ap {T} (ζ ↑₁)) (lk-↑₁-zero ξ) ⟩
  ap {T} (ζ ↑₁) (vr zero)        ≡⟨ ap-vr {T} (ζ ↑₁) zero ⟩
  lk (ζ ↑₁) zero                 ≡⟨ lk-↑₁-zero  ζ ⟩
  vr zero                        ∎
lk-↑₁-suc {{iLkUpPairSubstitution T Ξ Ζ ap-wk₁-TΖ}} (ξ ⊗ ζ) i = begin
  ap {T} (ζ ↑₁) (lk (ξ ↑₁) (suc i)) ≡⟨ cong (ap {T} (ζ ↑₁)) (lk-↑₁-suc ξ i) ⟩
  ap {T} (ζ ↑₁) (wk₁ (lk ξ i))      ≡⟨ ap-wk₁-TΖ ζ (lk ξ i) ⟩
  wk₁ (ap {T} ζ (lk ξ i))           ∎

instance
  iLkUpPair :
    {T : STX} {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}}
    {{apVrT : ApVr T}} {{apWkTT : ApWk T T}}
    {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}} {{lkUpTΞ : LkUp T Ξ}}
    {Ζ : MOR} {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}} {{lkUpTΖ : LkUp T Ζ}} →
    LkUp T (Pair Ξ Ζ)
  iLkUpPair {T} {Ξ} {Ζ} = iLkUpPairSubstitution T Ξ Ζ (ap-wk₁ {T})

record ApPair (T : STX) {{apTT : Ap T T}} (X : STX) {{apTX : Ap T X}} : Set₁ where
  field
    ap-pair :
      {Ξ : MOR} {{lkTΞ : Lk T Ξ}} {{upΞ : Up Ξ}}
      {Ζ : MOR} {{lkTΖ : Lk T Ζ}} {{upΖ : Up Ζ}}
      {γ₁ γ₂ γ₃ : Dom} (ξ : Ξ γ₁ γ₂) (ζ : Ζ γ₂ γ₃)
      (x : X γ₁) →
        ap {T} (ξ ⊗ ζ) x ≡ ap {T} ζ (ap {T} ξ x)
open ApPair {{...}} public

instance
  iApPairIxIx : ApPair Ix Ix
  ap-pair {{iApPairIxIx}} {Ξ} ξ ζ i = refl


open import Prelude
open import RW.Language.RTerm
open import RW.Language.FinTerm

module RW.Language.RTermIdx where

  open Eq {{...}}

  data RTermᵢ {a}(A : Set a) : Set a where
    ovarᵢ : (x : A) → RTermᵢ A
    ivarᵢ : (n : ℕ) → RTermᵢ A
    rlitᵢ : (l : Literal) → RTermᵢ A
    rlamᵢ : RTermᵢ A
    rappᵢ : (n : RTermName) → RTermᵢ A

  ovarᵢ-inj : ∀{a}{A : Set a}{x y : A} → ovarᵢ x ≡ ovarᵢ y → x ≡ y
  ovarᵢ-inj refl = refl

  ivarᵢ-inj : ∀{a}{A : Set a}{x y : ℕ} → ivarᵢ {a} {A} x ≡ ivarᵢ {a} y → x ≡ y
  ivarᵢ-inj refl = refl

  rlitᵢ-inj : ∀{a}{A : Set a}{x y : Literal} → rlitᵢ {a} {A} x ≡ rlitᵢ {a} y → x ≡ y
  rlitᵢ-inj refl = refl

  rappᵢ-inj : ∀{a}{A : Set a}{x y : RTermName} → rappᵢ {a} {A} x ≡ rappᵢ {a} y → x ≡ y
  rappᵢ-inj refl = refl

  _≟-RTermᵢ_ : {A : Set}{{eqA : Eq A}}
               → (t₁ t₂ : RTermᵢ A) → Dec (t₁ ≡ t₂)
  _≟-RTermᵢ_ {{eq _≟_}} (ovarᵢ x) (ovarᵢ y)
    with x ≟ y
  ...| yes x≡y = yes (cong ovarᵢ x≡y)
  ...| no  x≢y = no (x≢y ∘ ovarᵢ-inj)
  _≟-RTermᵢ_ (ovarᵢ _) (ivarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (ovarᵢ _) (rlitᵢ _) = no (λ ())
  _≟-RTermᵢ_ (ovarᵢ _) (rlamᵢ  ) = no (λ ())
  _≟-RTermᵢ_ (ovarᵢ _) (rappᵢ _) = no (λ ())
  _≟-RTermᵢ_ (ivarᵢ _) (ovarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (ivarᵢ x) (ivarᵢ y)
    with x ≟-ℕ y
  ...| yes x≡y = yes (cong ivarᵢ x≡y)
  ...| no  x≢y = no (x≢y ∘ ivarᵢ-inj)
  _≟-RTermᵢ_ (ivarᵢ _) (rlitᵢ _) = no (λ ())
  _≟-RTermᵢ_ (ivarᵢ _) (rlamᵢ  ) = no (λ ())
  _≟-RTermᵢ_ (ivarᵢ _) (rappᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rlitᵢ _) (ivarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rlitᵢ _) (ovarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rlitᵢ x) (rlitᵢ y)
    with x ≟-Lit y
  ...| yes x≡y = yes (cong rlitᵢ x≡y)
  ...| no  x≢y = no (x≢y ∘ rlitᵢ-inj)
  _≟-RTermᵢ_ (rlitᵢ _) (rlamᵢ  ) = no (λ ())
  _≟-RTermᵢ_ (rlitᵢ _) (rappᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rlamᵢ  ) (ovarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rlamᵢ  ) (ivarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rlamᵢ  ) (rlitᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rlamᵢ  ) (rlamᵢ  ) = yes refl
  _≟-RTermᵢ_ (rlamᵢ  ) (rappᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rappᵢ _) (ovarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rappᵢ _) (ivarᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rappᵢ _) (rlitᵢ _) = no (λ ())
  _≟-RTermᵢ_ (rappᵢ _) (rlamᵢ  ) = no (λ ())
  _≟-RTermᵢ_ (rappᵢ x) (rappᵢ y)
    with x ≟-RTermName y
  ...| yes x≡y = yes (cong rappᵢ x≡y)
  ...| no  x≢y = no (x≢y ∘ rappᵢ-inj)

  -----------------------------------
  -- Utilities

  out : ∀{a}{A : Set a} → RTerm A → RTermᵢ A × List (RTerm A)
  out (ovar x)    = ovarᵢ x , []
  out (ivar n)    = ivarᵢ n , []
  out (rlit l)    = rlitᵢ l , []
  out (rlam t)    = rlamᵢ , t ∷ []
  out (rapp n ts) = rappᵢ n , ts

  toSymbol : ∀{a}{A : Set a} → RTermᵢ A → Maybe A
  toSymbol (ovarᵢ a) = just a
  toSymbol _         = nothing

  idx-cast : ∀{a b}{A : Set a}{B : Set b}
           → (i : RTermᵢ A) → (toSymbol i ≡ nothing)
           → RTermᵢ B
  idx-cast (ovarᵢ x) ()
  idx-cast (ivarᵢ n) _ = ivarᵢ n
  idx-cast (rlitᵢ l) _ = rlitᵢ l
  idx-cast (rlamᵢ  ) _ = rlamᵢ
  idx-cast (rappᵢ n) _ = rappᵢ n

  instance
    eq-RTermᵢ : {A : Set}{{eqA : Eq A}} → Eq (RTermᵢ A)
    eq-RTermᵢ = eq _≟-RTermᵢ_

  {-
  instance

    RTerm-isTrie : {A : Set}{{eqA : Eq A}}{{enA : Enum A}}
                 → IsTrie (RTerm A)
    RTerm-isTrie {A} {{enA = enum aℕ ℕa}} = record
      { Idx     = RTermᵢ A
      ; idx≡    = eq _≟-RTermᵢ_
      ; toSym   = toSymAux
      ; fromSym = fromSymAux
      ; inₜ     = inAux
      ; outₜ    = outAux
      }
      where
        postulate
          fixme : ∀{a}{A : Set a} → A

        toSymAux : RTermᵢ A → Maybe ℕ
        toSymAux (ovarᵢ n) = aℕ n
        toSymAux _         = nothing

        fromSymAux : ℕ → Maybe (RTermᵢ A)
        fromSymAux n = maybe (λ x → just (ovarᵢ x)) nothing (ℕa n)

        inAux : RTermᵢ A × List (RTerm A) → RTerm A
        inAux (ovarᵢ x , []) = ovar x
        inAux (ivarᵢ x , []) = ivar x
        inAux (rlitᵢ l , []) = rlit l
        inAux (rlamᵢ , t ∷ []) = rlam t
        inAux (rappᵢ n , ts) = rapp n ts
        inAux _ = fixme -- maybe using vectors of the correct arity...

        outAux : RTerm A → RTermᵢ A × List (RTerm A)
        outAux (ovar x) = ovarᵢ x , []
        outAux (ivar n) = ivarᵢ n , []
        outAux (rlit l) = rlitᵢ l , []
        outAux (rlam t) = rlamᵢ , t ∷ []
        outAux (rapp n ts) = rappᵢ n , ts
  {-
    RTerm-isTrie : IsTrie (RTerm ⊥)
    RTerm-isTrie = record
      { Idx   = RTermᵢ ⊥
      ; idx≡  = eq _≟-RTermᵢ_
      ; toSym = toSymAux
      ; fromSym = fromSymAux
      ; inₜ   = inAux
      ; outₜ  = outAux
      } where
        postulate
          fixme : ∀{a}{A : Set a} → A

        toSymAux : RTermᵢ ⊥ → Maybe ℕ
        toSymAux (ovarᵢ ())
        toSymAux (ivarᵢ n) = just n
        toSymAux _         = nothing

        fromSymAux : ℕ → Maybe (RTermᵢ ⊥)
        fromSymAux x = just (ivarᵢ x)

        inAux : RTermᵢ ⊥ × List (RTerm ⊥) → RTerm ⊥
        inAux (ovarᵢ () , [])
        inAux (ivarᵢ x , []) = ivar x
        inAux (rlitᵢ l , []) = rlit l
        inAux (rlamᵢ , t ∷ []) = rlam t
        inAux (rappᵢ n , ts) = rapp n ts
        inAux _ = fixme -- maybe using vectors of the correct arity...

        outAux : RTerm ⊥ → RTermᵢ ⊥ × List (RTerm ⊥)
        outAux (ovar ()) -- = ovarᵢ x , []
        outAux (ivar n) = ivarᵢ n , []
        outAux (rlit l) = rlitᵢ l , []
        outAux (rlam t) = rlamᵢ , t ∷ []
        outAux (rapp n ts) = rappᵢ n , ts
  -}

  RTermTrie : (A : Set){{eqA : Eq A}}{{enA : Enum A}} → Set
  RTermTrie A = BTrie (RTerm A) Name

  insertTerm : {A : Set}{{eqA : Eq A}}{{enA : Enum A}}
             → Name → RTerm A → ℕ × RTermTrie A → ℕ × RTermTrie A
  insertTerm {A} = insert
    where
      open import RW.Data.BTrie.Insert (RTerm A) Name

  lookupTerm : {A : Set}{{eqA : Eq A}}{{enA : Enum A}}
             → RTerm ⊥ → RTermTrie A → List (ℕmap.to (RTerm A) × Name)
  lookupTerm {A} t = lookup (replace-A ⊥-elim t)
    where
      open import RW.Data.BTrie.Lookup (RTerm A) Name

  -}