module cry.ec where

open import Level
open import Relation.Nullary
open import Agda.Builtin.Bool
open import Agda.Builtin.Nat using () renaming (Nat to ℕ)
open import Agda.Builtin.List

open import cry.gfp

infixr 4 _,_
infixr 2 _×_

record _×_ {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    proj₁ : A
    proj₂ : B

open _×_ public

-- EC: group of points of elliptic curve (in jacobian coordinates, without conversion to affine)

module ec {c ℓ} (gfp : RawField c ℓ) (a b : RawField.Carrier gfp) where
  module F = RawField gfp
  open F renaming (Carrier to 𝔽; _≈_ to _=F_; _≈?_ to _≟F_)

  record Point : Set c where
    constructor _∶_∶_
    field
      x y z : 𝔽

  is-point : Point → Bool
  -- (y/z³) ² ≡ (x/z²) ³ + a * (x/z²) + b
  is-point (x ∶ y ∶ z) =
    let
      y² = y ²
      x² = x ²
      x³ = x² * x
      z² = z ²
      z⁴ = z² ²
      z⁶ = z² * z⁴
      xz⁴ = x * z⁴
      axz⁴ = a * xz⁴
      bz⁶ = b * z⁶
      x³+axz⁴ = x³ + axz⁴
      x³+axz⁴+bz⁶ = x³+axz⁴ + bz⁶
    in y² F.?≈ x³+axz⁴+bz⁶

  aff : Point → Point
  aff (x ∶ y ∶ z) = x′ ∶ y′ ∶ 1# where
    z⁻¹ = z ⁻¹
    z⁻² = z⁻¹ ²
    z⁻³ = z⁻² * z⁻¹
    x′ = x * z⁻²
    y′ = y * z⁻³

  _==_ : Point → Point → Set _
  (x₁ ∶ y₁ ∶ z₁) == (x₂ ∶ y₂ ∶ z₂)
  -- p₁ == p₂ with norm2 p₁ p₂
  -- ... | (x₁z₂² , x₂z₁²) , (y₁z₂³ , y₂z₁³)
    = x₁z₂² =F x₂z₁² × y₁z₂³ =F y₂z₁³ where
    {-
    x₁/z₁² ≡ x₂/z₂²
    y₁/z₁³ ≡ y₂/z₂³
    -}
    z₂² = z₂ ²
    z₁² = z₁ ²
    x₁z₂² = x₁ * z₂²
    x₂z₁² = x₂ * z₁²
    z₂³ = z₂ * z₂²
    z₁³ = z₁ * z₁²
    y₁z₂³ = y₁ * z₂³
    y₂z₁³ = y₂ * z₁³

  _≟_ : (p₁ p₂ : Point) → Dec (p₁ == p₂)
  (x₁ ∶ y₁ ∶ z₁) ≟ (x₂ ∶ y₂ ∶ z₂) = r where
    z₂² = z₂ ²
    z₁² = z₁ ²
    x₁z₂² = x₁ * z₂²
    x₂z₁² = x₂ * z₁²
    z₂³ = z₂ * z₂²
    z₁³ = z₁ * z₁²
    y₁z₂³ = y₁ * z₂³
    y₂z₁³ = y₂ * z₁³
    r : _
    r with x₁z₂² ≟F x₂z₁²
    ... | no x₁≠x₂ = no (λ p₁=p₂ → x₁≠x₂ (proj₁ p₁=p₂))
    ... | yes x₁=x₂ with y₁z₂³ ≟F y₂z₁³
    ... | no y₁≠y₂ = no (λ p₁=p₂ → y₁≠y₂ (proj₂ p₁=p₂))
    ... | yes y₁=y₂ = yes (x₁=x₂ , y₁=y₂)

  is-𝕆 : Point → Set _
  is-𝕆 (_ ∶ _ ∶ z) = z =F 0#
  {-
  x₃ = λ² − x₁ − x₂
  y₃ = λ(x₁ − x₃) − y₁
  λ = (y₂ - y₁) / (x₂ - x₁), p₁ ≠ p₂
  λ = (3 x₁ ² + a) / (2 y₁), p₁ = p₂

  (x/z²)₃ = λ² − (x/z²)₁ − (x/z²)₂
  (y/z³)₃ = λ((x/z²)₁ − (x/z²)₃) − (y/z³)₁
  λ = ((y/z³)₂ - (y/z³)₁) / ((x/z²)₂ - (x/z²)₁), p₁ ≠ p₂
  λ = (3 (x/z²)₁ ² + a) / (2 (y/z³)₁), p₁ = p₂

  x₃/z₃² z₁² z₂² = λ² z₁² z₂² − x₁ z₂² − x₂ z₁²
  y₁/z₁³ + y₃/z₃³ = λ(x₁/z₁² − (x₃/z₃²))
  y₂/z₂³ + y₃/z₃³ = λ(x₂/z₂² − (x₃/z₃²))
  λ z₁ z₂ (x₂ z₁² - x₁ z₂²) = (y₂ z₁³ - y₁ z₂³), p₁ ≠ p₂
  λ = (3 (x/z²)₁ ² + a) / (2 (y/z³)₁), p₁ = p₂

  p₁ ≠ p₂:
  z₃ = z₁ z₂ (x₂ z₁² - x₁ z₂²)
  λ z₃ = (y₂ z₁³ - y₁ z₂³)
  x₃ = (y₂ z₁³ - y₁ z₂³)² − (x₁ z₂² + x₂ z₁²) (x₂ z₁² - x₁ z₂²)²
  y₃ z₁³ = (y₂ z₁³ - y₁ z₂³) (z₃² x₁ − x₃ z₁²) z₁ − y₁ z₃³

  (y₁ z₃³ + y₃ z₁³)/(y₂ z₁³ - y₁ z₂³) = (x₁ z₃² − x₃ z₁²) z₁
  (y₂ z₃³ + y₃ z₂³)/(y₂ z₁³ - y₁ z₂³) = (x₂ z₃² − x₃ z₂²) z₂

  x₁ z₃² − x₃ z₁² =
     x₁ (z₁ z₂ (x₂ z₁² - x₁ z₂²))² -
     (y₂ z₁³ - y₁ z₂³)² z₁² +
     ((x₂ z₁²)² - (x₁ z₂²)²) (x₂ z₁² - x₁ z₂²) z₁²
  = x₁ z₁² z₂² ((x₂ z₁²)² - 2 x₂ z₁² x₁ z₂² + (x₁ z₂²)²)
    + ((x₂ z₁²)³ - (x₂ z₁²)² x₁ z₂² - (x₁ z₂²)² x₂ z₁² + (x₁ z₂²)³) z₁²
    - (y₂ z₁³ - y₁ z₂³)² z₁²
  = (- (y₂ z₁³ - y₁ z₂³)²
    + 2 x₁³ z₂⁶ + x₂³ z₁⁶ + x₁² x₂ (-3 z₁² z₂⁴)) z₁²

  (y₁ z₃³ + y₃ z₁³) = (y₂ z₁³ - y₁ z₂³) (- (y₂ z₁³ - y₁ z₂³)²
    + 2 x₁³ z₂⁶ + x₂³ z₁⁶ + x₁² x₂ (-3 z₁² z₂⁴)) z₁³
  -}

  𝕆 : Point
  𝕆 = (1# ∶ 1# ∶ 0#)

  dbl : Point → Point
  dbl (x₁ ∶ y₁ ∶ z₁) = (x₃ ∶ y₃ ∶ z₃) where
    {- p₁ = p₂
    x₃ = λ² − x₁ − x₁
    y₃ = λ(x₁ − x₃) − y₁
    λ = (3 x₁ ² + a) / (2 y₁)

    x₃ = λ² z₃² − 2 x₁/z₁² z₃²
    y₃/z₃³ = λ(x₁/z₁² − x₃/z₃²) − y₁/z₁³
    λ = (3 (x₁/z₁²) ² + a) / (2 y₁/z₁³)

    λ 2 y₁ z₁ = 3 x₁² + a z₁⁴
    z₃ = 2 y₁ z₁
    x₃ = (3 x₁² + a z₁⁴)² − 2 x₁ (2 y₁)²

    y₃ = (3 x₁² + a z₁⁴) (x₁ (2 y₁)² − x₃) − y₁ (2 y₁)³
    -}
    2y₁ = y₁ + y₁
    z₃ = 2y₁ * z₁

    x₁² = x₁ ²
    2x₁² = x₁² + x₁²
    3x₁² = 2x₁² + x₁²
    z₁² = z₁ ²
    z₁⁴ = z₁² ²
    az₁⁴ = a * z₁⁴
    3x₁²+az₁⁴ = 3x₁² + az₁⁴
    [3x₁²+az₁⁴]² = 3x₁²+az₁⁴ ²
    y₁² = y₁ ²
    2y₁² = y₁² + y₁²
    4y₁² = 2y₁² + 2y₁²
    x₁[2y₁]² = x₁ * 4y₁²
    2x₁[2y₁]² = x₁[2y₁]² + x₁[2y₁]²
    x₃ = [3x₁²+az₁⁴]² - 2x₁[2y₁]²

    4y₁⁴ = 2y₁² ²
    8y₁⁴ = 4y₁⁴ + 4y₁⁴
    x₁[2y₁]²-x₃ = x₁[2y₁]² - x₃
    [3x₁²+az₁⁴][x₁[2y₁]²-x₃] = 3x₁²+az₁⁴ * x₁[2y₁]²-x₃
    y₃ = [3x₁²+az₁⁴][x₁[2y₁]²-x₃] - 8y₁⁴

  add : Point → Point → Point
  add (x₁ ∶ y₁ ∶ z₁) (x₂ ∶ y₂ ∶ z₂) = x₃ ∶ y₃ ∶ z₃ where
    {- p₁ ≠ p₂
    x₃ = λ² − x₁ − x₂
    y₃ = λ(x₁ − x₃) − y₁
    λ = (y₂ - y₁) / (x₂ - x₁)

    (x/z²)₃ = λ² − (x/z²)₁ − (x/z²)₂
    (y/z³)₃ = λ((x/z²)₁ − (x/z²)₃) − (y/z³)₁
    λ = ((y/z³)₂ - (y/z³)₁) / ((x/z²)₂ - (x/z²)₁)

    λ z₁ z₂ (x₂ z₁² - x₁ z₂²) = (y₂ z₁³ - y₁ z₂³)
    x₃/z₃² z₁² z₂² + x₁ z₂² + x₂ z₁² = λ² z₁² z₂²
    y₃/z₃³ = λ(x₁/z₁² − x₃/z₃²) − y₁/z₁³

    z₃ = z₁ z₂ (x₂ z₁² - x₁ z₂²)
    x₃ = (y₂ z₁³ - y₁ z₂³)² - (x₁ z₂² + x₂ z₁²) (x₂ z₁² - x₁ z₂²)²
    y₃ = (y₂ z₁³ - y₁ z₂³) (x₁ z₂² (x₂ z₁² - x₁ z₂²)² − x₃) − y₁ z₂³ (x₂ z₁² - x₁ z₂²)³
    y₃ = (y₂ z₁³ - y₁ z₂³) ((x₁ z₂²+x₂ z₁²)/2 (x₂ z₁² - x₁ z₂²)² − x₃)
       − (x₂ z₁² - x₁ z₂²)³ (y₁ z₂³ + y₂ z₁³)/2
    -}

    z₁z₂ = z₁ * z₂
    z₁² = z₁ ²
    z₂² = z₂ ²
    x₁z₂² = x₁ * z₂²
    x₂z₁² = x₂ * z₁²
    z₂³ = z₂ * z₂²
    z₁³ = z₁ * z₁²
    y₁z₂³ = y₁ * z₂³
    y₂z₁³ = y₂ * z₁³
    x₂z₁²+x₁z₂² = x₂z₁² + x₁z₂²
    x₂z₁²-x₁z₂² = x₂z₁² - x₁z₂²
    y₂z₁³-y₁z₂³ = y₂z₁³ - y₁z₂³

    z₃ = z₁z₂ * x₂z₁²-x₁z₂²

    [x₂z₁²-x₁z₂²]² = x₂z₁²-x₁z₂² ²
    [y₂z₁³-y₁z₂³]² = y₂z₁³-y₁z₂³ ²
    [x₂z₁²+x₁z₂²][x₂z₁²-x₁z₂²]² = x₂z₁²+x₁z₂² * [x₂z₁²-x₁z₂²]²
    x₃ = [y₂z₁³-y₁z₂³]² - [x₂z₁²+x₁z₂²][x₂z₁²-x₁z₂²]²

    [x₂z₁²-x₁z₂²]³ = x₂z₁²-x₁z₂² * [x₂z₁²-x₁z₂²]²
    y₁z₂³[x₂z₁²-x₁z₂²]³ = y₁z₂³ * [x₂z₁²-x₁z₂²]³
    x₁z₂²[x₂z₁²-x₁z₂²]² = x₁z₂² * [x₂z₁²-x₁z₂²]²
    x₁z₂²[x₂z₁²-x₁z₂²]²-x₃ = x₁z₂²[x₂z₁²-x₁z₂²]² - x₃
    [y₂z₁³-y₁z₂³][x₁z₂²[x₂z₁²-x₁z₂²]²-x₃] = y₂z₁³-y₁z₂³ * x₁z₂²[x₂z₁²-x₁z₂²]²-x₃
    y₃ = [y₂z₁³-y₁z₂³][x₁z₂²[x₂z₁²-x₁z₂²]²-x₃] - y₁z₂³[x₂z₁²-x₁z₂²]³

  dblAdd : Point → ℕ → Point
  dblAdd = times 𝕆 dbl add

module test-ec where
  g = cry.gfp.gfp 7

  open RawField g renaming (Carrier to 𝔽) public

  a b : 𝔽
  a = 4
  b = 1

  xₚ yₚ zₚ : 𝔽
  xₚ = 4
  yₚ = 2
  zₚ = 1

  open ec g a b public
  P 2P : Point
  P = (xₚ ∶ yₚ ∶ zₚ)
  2P = dbl P


module Issue802b where

data I : Set where
  i : I

f : I → I
f i = i

mutual

  data P : I → Set where
    p : (x : I) → Q x x → P (f x)

  Q : I → I → Set
  Q i = P

g : (x y : I) → Q x y → P y
g i _ q = q

data R : (x : I) → P x → Set where
  r : (x : I) (q : Q x x) → R _ (g x _ q) → R (f x) (p x q)


module MJ.Examples.While where

open import MJ.Examples.Integer

open import Prelude
open import MJ.Types
open import MJ.Classtable.Code Σ
open import MJ.Syntax Σ
open import MJ.Syntax.Program Σ
open import MJ.Semantics Σ Lib
open import MJ.Semantics.Values Σ
open import Data.List.Any
open import Data.List.Membership.Propositional
open import Data.Star
open import Data.Bool as Bools hiding (_≤?_)
open import Relation.Nullary.Decidable

-- a simple program that computes 10 using a while loop
p₁ : Prog int
p₁ = Lib ,
  let
    x = (here refl)
  in body
    (
        loc int
      ◅ asgn x (num 1)
      ◅ while iop (λ x y → Bools.if ⌊ suc x ≤? y ⌋ then 0 else 1) (var x) (num 10) run (
        asgn x (iop (λ x y → x + y) (var x) (num 1))
      )
      -- test simplest if-then-else and early return from statement
      ◅ if (num 0) then (ret (var x)) else (ret (num 0))
      ◅ ε
    )
    (num 0)

test1 : p₁ ⇓⟨ 100 ⟩ (λ {W} (v : Val W int) → v ≡ num 10)
test1 = refl


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

module Data.Binary.Conversion.Fast where

-- This module provides a conversion function from
-- nats which uses built-in functions.
-- It is dramatically faster than the normal conversion
-- even at smaller numbers.

open import Data.Binary.Definition
open import Data.Nat.DivMod
open import Data.Nat.Base
open import Data.Bool

⟦_⇑⟧⟨_⟩ : ℕ → ℕ → 𝔹
⟦ suc n ⇑⟧⟨ suc w ⟩ =
  if even n
    then 1ᵇ ⟦ n ÷ 2 ⇑⟧⟨ w ⟩
    else 2ᵇ ⟦ n ÷ 2 ⇑⟧⟨ w ⟩
⟦ zero  ⇑⟧⟨ _    ⟩ = 0ᵇ
⟦ suc _ ⇑⟧⟨ zero ⟩ = 0ᵇ -- will not happen

-- We build the output by repeatedly halving the input,
-- but we also pass in the number to reduce as we go so that
-- we satisfy the termination checker.
⟦_⇑⟧ : ℕ → 𝔹
⟦ n ⇑⟧ = ⟦ n ⇑⟧⟨ n ⟩
{-# INLINE ⟦_⇑⟧ #-}

-- Without the added argument to the recursor, the function does not
-- pass the termination checker:
-- {-# TERMINATING #-}
-- ⟦_⇑⟧″ : ℕ → 𝔹
-- ⟦ zero  ⇑⟧″ = 0ᵇ
-- ⟦ suc n ⇑⟧″ =
--   if rem n 2 ℕ.≡ᴮ 0
--   then 1ᵇ ⟦ n ÷ 2 ⇑⟧″
--   else 2ᵇ ⟦ n ÷ 2 ⇑⟧″

-- The "principled" version (which uses well-founded recursion) is
-- incredibly slow. (and the following doesn't even compute, because of
-- cubical)

-- open import Data.Nat.WellFounded

-- ⟦_⇑⟧‴ : ℕ → 𝔹
-- ⟦ n ⇑⟧‴ = go n (≤-wellFounded n)
--   where
--   go : ∀ n → Acc _<_ n → 𝔹
--   go zero    wf = 0ᵇ
--   go (suc n) (acc wf) =
--     if rem n 2 ℕ.≡ᴮ 0
--     then 1ᵇ go (n ÷ 2) (wf (n ÷ 2) (s≤s (div2≤ n)))
--     else 2ᵇ go (n ÷ 2) (wf (n ÷ 2) (s≤s (div2≤ n)))


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

module Categories.Adjoint.Instance.StrictCore where

-- The adjunction between the forgetful functor from (strict) Cats to
-- (strict) Groupoids and the (strict) Core functor.

open import Data.Product
open import Level using (_⊔_)
import Function
open import Relation.Binary using (IsEquivalence)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)

open import Categories.Adjoint
open import Categories.Category using (Category)
open import Categories.Category.Groupoid using (Groupoid)
import Categories.Category.Construction.Core as C
open import Categories.Category.Instance.StrictCats
open import Categories.Category.Instance.StrictGroupoids
open import Categories.Functor renaming (id to idF)
open import Categories.Functor.Equivalence
open import Categories.Functor.Instance.StrictCore
import Categories.Morphism as Morphism
import Categories.Morphism.Reasoning as MR
open import Categories.Morphism.IsoEquiv using (⌞_⌟; _≃_)

-- The forgetful functor from StrictGroupoids to StrictCats

Forgetful : ∀ {o ℓ e} → Functor (Groupoids o ℓ e) (Cats o ℓ e)
Forgetful {o} {ℓ} {e} = record
  { F₀ = Groupoid.category
  ; F₁ = Function.id
  ; identity     = λ {G} → Groupoids.Equiv.refl {G} {G} {idF}
  ; homomorphism = λ {G H K F H} → Groupoids.Equiv.refl {G} {K} {H ∘F F}
  ; F-resp-≈     = Function.id
  }
  where
    module Groupoids = Category (Groupoids o ℓ e)

-- Core is right-adjoint to the forgetful functor from Groupoids to
-- Cats

CoreAdj : ∀ {o ℓ e} → Forgetful {o} {ℓ ⊔ e} {e} ⊣ Core
CoreAdj = record
  { unit   = record { η = unit   ; commute = λ {G H} F → unit-commute {G} {H} F ; sym-commute = λ {G H} F → unit-sym-commute {G} {H} F }
  ; counit = record { η = counit ; commute = counit-commute                     ; sym-commute = counit-sym-commute }
  ; zig    = λ {G} → zig {G}
  ; zag    = zag
  }
  where
    open Groupoid using (category)
    module Core = Functor Core

    unit : ∀ G → Functor (category G) (C.Core (category G))
    unit G = record
      { F₀ = Function.id
      ; F₁ = λ f → record { from = f ; to = f ⁻¹ ; iso = iso }
      ; identity     = ⌞ Equiv.refl ⌟
      ; homomorphism = ⌞ Equiv.refl ⌟
      ; F-resp-≈     = ⌞_⌟
      }
      where open Groupoid G

    unit-commute : ∀ {G H} (F : Functor (category G) (category H)) →
                   unit H ∘F F ≡F Core.F₁ F ∘F unit G
    unit-commute {_} {H} F = record
      { eq₀ = λ _ → refl
      ; eq₁ = λ _ → ⌞ MR.id-comm-sym (category H) ⌟
      }

    unit-sym-commute : ∀ {G H} (F : Functor (category G) (category H)) →
                       Core.F₁ F ∘F unit G ≡F unit H ∘F F
    unit-sym-commute {_} {H} F = record
      { eq₀ = λ _ → refl
      ; eq₁ = λ _ → ⌞ MR.id-comm-sym (category H) ⌟
      }

    counit : ∀ C → Functor (C.Core C) C
    counit C = record
      { F₀ = Function.id
      ; F₁ = _≅_.from
      ; identity     = Equiv.refl
      ; homomorphism = Equiv.refl
      ; F-resp-≈     = _≃_.from-≈
      }
      where
        open Category C
        open Morphism C

    counit-commute : ∀ {C D} (F : Functor C D) →
                     counit D ∘F Core.F₁ F ≡F F ∘F counit C
    counit-commute {C} {D} F = record
      { eq₀ = λ _ → refl
      ; eq₁ = λ _ → MR.id-comm-sym D
      }

    counit-sym-commute : ∀ {C D} (F : Functor C D) →
                         F ∘F counit C ≡F counit D ∘F Core.F₁ F
    counit-sym-commute {C} {D} F = record
      { eq₀ = λ _ → refl
      ; eq₁ = λ _ → MR.id-comm-sym D
      }

    zig : ∀ {G} → counit (category G) ∘F unit G ≡F idF
    zig {G} = record
      { eq₀ = λ _ → refl
      ; eq₁ = λ _ → MR.id-comm-sym (category G)
      }

    zag : ∀ {B} → Core.F₁ (counit B) ∘F unit (Core.F₀ B) ≡F idF
    zag {B} = record { eq₀ = λ _ → refl ; eq₁ = λ _ → ⌞ MR.id-comm-sym B ⌟ }


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.ZCohomology.Base where

open import Cubical.Data.Int.Base
open import Cubical.Data.Nat.Base
open import Cubical.Data.Sigma

open import Cubical.Foundations.Pointed.Base

open import Cubical.HITs.Nullification.Base
open import Cubical.HITs.SetTruncation.Base
open import Cubical.HITs.Sn.Base
open import Cubical.HITs.S1.Base
open import Cubical.HITs.Susp.Base
open import Cubical.HITs.Truncation.Base

private
  variable
    ℓ : Level
    A : Type ℓ

--- Cohomology ---

{- EM-spaces Kₙ from Brunerie 2016 -}
coHomK : (n : ℕ) → Type₀
coHomK zero = Int
coHomK (suc n) = ∥ S₊ (suc n) ∥ (2 + suc n)

{- Cohomology -}
coHom : (n : ℕ) → Type ℓ → Type ℓ
coHom n A = ∥ (A → coHomK n) ∥₂

--- Reduced cohomology ---

coHom-pt : (n : ℕ) → coHomK n
coHom-pt 0 = 0
coHom-pt 1 = ∣ base ∣
coHom-pt (suc (suc n)) = ∣ north ∣

{- Pointed version of Kₙ  -}
coHomK-ptd : (n : ℕ) → Pointed (ℓ-zero)
coHomK-ptd n = coHomK n , coHom-pt n

{- Reduced cohomology -}
coHomRed : (n : ℕ) → (A : Pointed ℓ) → Type ℓ
coHomRed n A = ∥ A →∙ coHomK-ptd n ∥₂


open import Agda.Primitive
open import Data.List
-- f1 : {l : Level} -> (a : Set l) -> (x : a) -> (y : x) -> x
-- f1 : (a : Set2) -> (x : a) -> (y : x) -> x
-- f1 a x y = ?

f0 : (a : Set) -> (x : a) -> a
f0 a x = x

f1 : (a : Set1) -> (x : a) -> a
f1 a x = x

myprod : (As : List Set) -> Set
myprod = {!!}

mysum : (As : List Set) -> Set
mysum = {!!}

hej : (tyOp : List Set -> Set) -> Set
hej = {!!}

test1 = hej myprod
test2 = hej mysum

Type = Set

data Dummy : Set where Dum : Dummy

-- Start of example

hej2 : List Type -> Type     -- Is universe-monomorphic in Agda, but polymorphic in Idris
hej2 (t ∷ ts) = t
hej2 _ = ?

test21 : Type
test21 = hej2 [ Type ]  -- test21 == Type in Idris, but Agda complains

test22 : Type
test22 = hej2 [ Dummy ]   -- test22 == Dummy


------------------------------------------------------------------------
-- Parser indices
------------------------------------------------------------------------

module RecursiveDescent.Index where

open import Data.Bool
open import Data.Product.Record
open import Relation.Nullary
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Data.Function

------------------------------------------------------------------------
-- Indices to the parser type

-- Does the parser accept empty strings?

Empty : Set
Empty = Bool

-- The proper left corners of the parser, represented as a tree. See
-- the definition of RecursiveDescent.(Coi|I)nductive.Internal.Parser.

data Corners : Set where
  leaf : Corners
  step : Corners -> Corners
  node : Corners -> Corners -> Corners

Index : Set
Index = Empty × Corners

-- The parser type signature. The second argument is the result type.

ParserType : Set1
ParserType = Index -> Set -> Set

ParserType₁ : Set2
ParserType₁ = Index -> Set -> Set1

------------------------------------------------------------------------
-- Operations on indices

infixr 50 _·I_
infixr 40 _∣I_

0I : Index
0I = (false , leaf)

1I : Index
1I = (true , leaf)

_∣I_ : Index -> Index -> Index
i₁ ∣I i₂ = (proj₁ i₁ ∨ proj₁ i₂ , node (proj₂ i₁) (proj₂ i₂))

_·I_ : Index -> Index -> Index
i₁ ·I i₂ = ( proj₁ i₁ ∧ proj₁ i₂
           , (if proj₁ i₁ then node (proj₂ i₁) (proj₂ i₂)
                          else step (proj₂ i₁))
           )

------------------------------------------------------------------------
-- Testing indices for equality

infix 15 _Index-≟_ _Corners-≟_

private

  drop-step : forall {c₁ c₂} -> step c₁ ≡ step c₂ -> c₁ ≡ c₂
  drop-step refl = refl

  drop-node₁ : forall {c₁ c₂ c₃ c₄} ->
               node c₁ c₂ ≡ node c₃ c₄ -> c₁ ≡ c₃
  drop-node₁ refl = refl

  drop-node₂ : forall {c₁ c₂ c₃ c₄} ->
               node c₁ c₂ ≡ node c₃ c₄ -> c₂ ≡ c₄
  drop-node₂ refl = refl

_Corners-≟_ : Decidable {Corners} _≡_
leaf       Corners-≟ leaf         = yes refl
step c₁    Corners-≟ step  c₂     with c₁ Corners-≟ c₂
step c₁    Corners-≟ step .c₁     | yes refl   = yes refl
step c₁    Corners-≟ step  c₂     | no  ¬c₁≡c₂ = no (¬c₁≡c₂ ∘ drop-step)
node c₁ c₂ Corners-≟ node  c₃  c₄ with c₁ Corners-≟ c₃ | c₂ Corners-≟ c₄
node c₁ c₂ Corners-≟ node .c₁ .c₂ | yes refl   | yes refl = yes refl
node c₁ c₂ Corners-≟ node  c₃  c₄ | no  ¬c₁≡c₂ | _          = no (¬c₁≡c₂ ∘ drop-node₁)
node c₁ c₂ Corners-≟ node  c₃  c₄ | _          | no  ¬c₁≡c₂ = no (¬c₁≡c₂ ∘ drop-node₂)
leaf       Corners-≟ step _       = no \()
leaf       Corners-≟ node _ _     = no \()
step _     Corners-≟ leaf         = no \()
step _     Corners-≟ node _ _     = no \()
node _ _   Corners-≟ leaf         = no \()
node _ _   Corners-≟ step _       = no \()

_Index-≟_ : Decidable {Index} _≡_
i₁ Index-≟ i₂ with proj₁ i₁ ≟ proj₁ i₂
                 | proj₂ i₁ Corners-≟ proj₂ i₂
... | yes e₁≡e₂ | yes c₁≡c₂ = yes (cong₂ (_,_) e₁≡e₂ c₁≡c₂)
... | no ¬e₁≡e₂ | _         = no (¬e₁≡e₂ ∘ cong proj₁)
... | _         | no ¬c₁≡c₂ = no (¬c₁≡c₂ ∘ cong proj₂)


{- This file contains a direct proof that the Brunerie number (the
number n s.t. π₄(S³)≅ℤ/nℤ) is 2, not relying on any of the more
advanced constructions in chapters 4-6 in Brunerie's thesis (but still
using chapters 1-3 for the construction). The Brunerie number is defined via

S³ ≃ S¹ * S¹ -ᵂ→ S² ∨ S² -ᶠᵒˡᵈ→ S²

where * denotes the join, ∨ denotes the wedge sum, W is the Whitehead
map (see joinTo⋁ in Cubical.Homotopy.Whitehead) and the final map is
just the folding map. η := ∣ fold ∘ W ∣₀ defines an element of π₃(S²).
The (absolute value) of the Brunerie number is given by the absolute
value of ϕ(η) for any iso π₃(S²)≅ℤ. The reason it's hard to prove
ϕ(η) = ± 2 directly is mainly because the equivalence S³ ≃ S¹ * S¹
complicates things. In this file, we try to work around this problem.

The proof goes as follows.

1. Define π₃*(A) := ∥ S¹ * S¹ →∙ A ∥₀ and define explicitly an
addition on this type. Prove that the equivalence π₃(A) ≃ π₃*(A) is
structure preserving, thereby giving a group structure on π₃*(A) and a
group iso π₃*(A) ≅ π₃(A)

2. Under this iso, η gets mapped to η₁ (by construction) defined by
S¹ * S¹ -ᵂ→ S² ∨ S² -ᶠᵒˡᵈ→ S²
which is much easier to work with.

3. Define a sequence of equivalences
π₃*(S²) ≅ π₃*(S¹ * S¹) ≅ π₃*(S³) ≅ π₃(S³) ≅ ℤ
and trace η₁ in each step, proving that it ends up at -2. It turns out
that that the iso S³ ≃ S¹ * S¹, which has been relatively explicitly
defined in Cubical.HITs.Sphere.Properties, kills off a good deal of
``annoying'' terms on the way, making the proof rather straightforward.

4. Conclude that π₄(S³) ≅ ℤ/2ℤ.

-}
{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Homotopy.Group.Pi4S3.QuickProof where

open import Cubical.Homotopy.Loopspace
open import Cubical.Homotopy.Group.Base
open import Cubical.Homotopy.Group.Pi3S2
open import Cubical.Homotopy.Group.PinSn
open import Cubical.Homotopy.Hopf
open import Cubical.Homotopy.Whitehead using (joinTo⋁)
open import Cubical.Homotopy.Connected
open import Cubical.Homotopy.HopfInvariant.HopfMap using (hopfMap≡HopfMap')
-- Only imports a simple equality of two constructions of the Hopf map.
open import Cubical.Homotopy.Group.Pi4S3.BrunerieNumber
  using (fold∘W ; coFib-fold∘W∙ ; π₄S³≅π₃coFib-fold∘W∙ ; S³→S²→Pushout→Unit)
-- Only imports definitions/proofs from chapter 1-3 in Brunerie's thesis

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.GroupoidLaws renaming (assoc to ∙assoc)
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function

open import Cubical.Data.Sigma
open import Cubical.Data.Nat
open import Cubical.Data.Int
  renaming (ℤ to Z ; _·_ to _·Z_ ; _+_ to _+Z_)

open import Cubical.HITs.S1 renaming (_·_ to _*_)
open import Cubical.HITs.Sn
open import Cubical.HITs.Susp renaming (toSusp to σ)
open import Cubical.HITs.Join hiding (joinS¹S¹→S³)
open import Cubical.HITs.Wedge
open import Cubical.HITs.Pushout
open import Cubical.HITs.SetTruncation
  renaming (rec2 to sRec2 ; elim to sElim ; elim2 to sElim2 ; map to sMap)
open import Cubical.HITs.Truncation renaming (rec to trRec)

open import Cubical.Algebra.Group
open import Cubical.Algebra.Group.Exact
open import Cubical.Algebra.Group.ZAction
open import Cubical.Algebra.Group.Instances.IntMod

open S¹Hopf
open Iso
open GroupStr

private
  variable
    ℓ : Level
    A B : Pointed ℓ

-- Some abbreviations and simple lemmas
private
  σ₁ = σ (S₊∙ 1)
  σ₂ = σ (S₊∙ 2)

  σ-filler : ∀ {ℓ} {A : Type ℓ} (x y : A) (i j : I) → Susp A
  σ-filler x y i j = compPath-filler (merid x) (sym (merid y)) i j

  to3ConnectedId : {f g : A →∙ B}
    → (isConnected 3 (typ B)) → fst f ≡ fst g → ∣ f ∣₂ ≡ ∣ g ∣₂
  to3ConnectedId {f = f} {g = g} con p =
    trRec (squash₂ _ _)
      (λ q → cong ∣_∣₂ (ΣPathP (p , q)))
      (fst (isConnectedPathP 1 (isConnectedPath 2 con _ _) (snd f) (snd g)))

  connS³ : isConnected 3 (S₊ 3)
  connS³ =
    isConnectedSubtr 3 1 (sphereConnected 3)

  con-joinS¹S¹ : isConnected 3 (join S¹ S¹)
  con-joinS¹S¹ =
    (isConnectedRetractFromIso 3
        (IsoSphereJoin 1 1)
        (isConnectedSubtr 3 1 (sphereConnected 3)))

-- Key goal: prove that the following element of π₃(S²) gets mapped to -2
η : π' 3 (S₊∙ 2)
η = fst (π'∘∙Hom 2 (fold∘W , refl)) ∣ id∙ (S₊∙ 3) ∣₂

{- Step 1. Define an addition on π₃*(A) := ∥ S¹ * S¹ →∙ A ∥₀ -}
-- On the underlying function spaces.
_+join_ : (f g : (join S¹ S¹ , inl base) →∙ A)
       → (join S¹ S¹ , inl base) →∙ A
fst (f +join g) (inl x) = fst f (inl x)
fst (f +join g) (inr x) = fst g (inr x)
fst (f +join g) (push a b i) =
  (cong (fst f) (push a b ∙ sym (push base b))
  ∙∙ snd f ∙ sym (snd g)
  ∙∙ cong (fst g) (push base base ∙∙ sym (push a base) ∙∙ push a b)) i
snd (f +join g) = snd f

-- Homotopy group version
_π₃*+_ : (f g : ∥ (join S¹ S¹ , inl base) →∙ S₊∙ 2 ∥₂)
      → ∥ (join S¹ S¹ , inl base) →∙ S₊∙ 2 ∥₂
_π₃*+_ = sRec2 squash₂ λ x y → ∣ x +join y ∣₂

-- transferring between π₃ and π₃*
-- (homotopy groups defined in terms of S¹ * S¹)
joinify :  S₊∙ 3 →∙ A → (join S¹ S¹ , inl base) →∙ A
fst (joinify f) x = fst f (joinS¹S¹→S³ x)
snd (joinify f) = snd f

disjoin : (join S¹ S¹ , inl base) →∙ A → S₊∙ 3 →∙ A
fst (disjoin f) = λ x → fst f (Iso.inv (IsoSphereJoin 1 1) x)
snd (disjoin f) = snd f

-- joinify is structure preserving
+join≡∙Π : (f g : S₊∙ 3 →∙ A)
         → joinify (∙Π f g)
         ≡ (joinify f +join joinify g)
+join≡∙Π f' g' =
  ΣPathP ((funExt (λ { (inl x) → sym fp
                     ; (inr x) → sym gp ∙ cong g (merid north)
                     ; (push a b i) j → main a b j i}))
        , λ i j → fp (j ∨ ~ i))
  where
  f = fst f'
  g = fst g'

  fp = snd f'
  gp = snd g'

  path-lem : ∀ {ℓ} {A : Type ℓ} {x y z w u : A}
       (p : x ≡ y) (q : y ≡ z) (r : z ≡ w) (s : w ≡ u)
    → (refl ∙∙ p ∙∙ q) ∙ (r ∙∙ s ∙∙ refl)
     ≡ (p ∙∙ (q ∙ r) ∙∙ s)
  path-lem p q r s =
       cong ((p ∙ q) ∙_) (sym (compPath≡compPath' r s))
    ∙∙ sym (∙assoc p q (r ∙ s))
    ∙∙ cong (p ∙_) (∙assoc q r s)
     ∙ sym (doubleCompPath≡compPath p (q ∙ r) s)

  main-helper : (a b : S¹)
    → Square ((refl ∙∙ cong f (σ₂ (S¹×S¹→S² a b)) ∙∙ fp)
             ∙ (sym gp ∙∙ cong g (σ₂ (S¹×S¹→S² a b)) ∙∙ refl))
             ((cong f (merid (S¹×S¹→S² a b))
             ∙ sym (cong f (merid north)))
               ∙∙ (fp ∙ sym gp)
               ∙∙ cong g (merid (S¹×S¹→S² a b)))
           (λ _ → f north)
           (cong g (merid north))
  main-helper a b =
    path-lem (cong f (σ₂ (S¹×S¹→S² a b)))  fp (sym gp)
             (cong g (σ₂ (S¹×S¹→S² a b)))
          ◁ lem
    where
    lem : PathP (λ i → f north ≡ cong g (merid north) i)
              ((λ i → f (σ₂ (S¹×S¹→S² a b) i))
                    ∙∙ fp ∙ (sym gp) ∙∙
               (cong g (σ₂ (S¹×S¹→S² a b))))
              ((cong f (merid (S¹×S¹→S² a b)) ∙ sym (cong f (merid north)))
               ∙∙ fp ∙ sym gp
               ∙∙ cong g (merid (S¹×S¹→S² a b)))
    lem i j =
      hcomp (λ k → λ { (i = i0) →
                          (cong-∙ f (merid (S¹×S¹→S² a b))
                                    (sym (merid north)) (~ k)
                       ∙∙ fp ∙ sym gp
                       ∙∙ (λ i → g (σ-filler (S¹×S¹→S² a b) north k i))) j
                       ; (i = i1) → ((cong f (merid (S¹×S¹→S² a b))
                                       ∙ sym (cong f (merid north)))
                                     ∙∙ (fp ∙ sym gp)
                                     ∙∙ cong g (merid (S¹×S¹→S² a b))) j
                       ; (j = i0) → f north
                       ; (j = i1) → g (merid north (~ k ∨ i))})
            (((cong f (merid (S¹×S¹→S² a b)) ∙ sym (cong f (merid north)))
            ∙∙ (fp ∙ sym gp)
            ∙∙ cong g (merid (S¹×S¹→S² a b))) j)

  main-helper₂ : (a b : S¹)
    → cong (fst (joinify g')) (push base base ∙∙ sym (push a base) ∙∙ push a b)
    ≡ cong g (merid (S¹×S¹→S² a b))
  main-helper₂ a b = cong-∙∙ (fst (joinify g'))
       (push base base) (sym (push a base)) (push a b)
       ∙ cong (cong g (merid north) ∙∙_∙∙ cong g (merid (S¹×S¹→S² a b)))
              (cong (cong g) (cong sym (cong merid (S¹×S¹→S²rUnit a))))
       ∙  ((λ i → (cong g (λ j → merid north (j ∧ ~ i)))
       ∙∙ (cong g (λ j → merid north (~ j ∧ ~ i)))
       ∙∙ cong g (merid (S¹×S¹→S² a b)))
       ∙ sym (lUnit (cong g (merid (S¹×S¹→S² a b)))))

  main : (a b : S¹)
    → PathP (λ i → fp (~ i) ≡ (sym gp ∙ cong g (merid north)) i)
            ((sym fp ∙∙ cong f (σ₂ (S¹×S¹→S² a b)) ∙∙ fp)
           ∙ (sym gp ∙∙ cong g (σ₂ (S¹×S¹→S² a b)) ∙∙ gp))
            ((cong (fst (joinify f')) (push a b ∙ sym (push base b))
          ∙∙ fp ∙ sym gp
          ∙∙ cong (fst (joinify g'))
              (push base base ∙∙ sym (push a base) ∙∙ push a b)))
  main a b =
    ((λ i j → hcomp (λ k → λ {(i = i0) → (((λ j → fp (~ j ∧ k))
                                          ∙∙ cong f (σ₂ (S¹×S¹→S² a b))
                                          ∙∙ fp)
                                          ∙ (sym gp
                                          ∙∙ cong g (σ₂ (S¹×S¹→S² a b))
                                          ∙∙ λ j → gp (j ∧ k))) j
                              ; (i = i1) → ((cong f (merid (S¹×S¹→S² a b))
                                           ∙ sym (cong f (merid north)))
                                          ∙∙ fp ∙ sym gp
                                          ∙∙ cong g (merid (S¹×S¹→S² a b))) j
                              ; (j = i0) → fp (~ i ∧ k)
                              ; (j = i1) → compPath-filler'
                                           (sym gp) (cong g (merid north)) k i})
                     (main-helper a b i j)))
    ▷ λ i →
      cong-∙ (fst (joinify f')) (push a b) (sym (push base b)) (~ i)
      ∙∙ fp ∙ sym gp
      ∙∙ main-helper₂ a b (~ i)

-- Group structure on π₃*
-- todo: remove connectivity assumption
module _ (A : Pointed ℓ) (con : (isConnected 3 (typ A))) where
  π₃*Iso : Iso (typ (π'Gr 2 A)) ∥ (join S¹ S¹ , inl base) →∙ A ∥₂
  fun π₃*Iso = sMap joinify
  inv π₃*Iso = sMap disjoin
  rightInv π₃*Iso =
    sElim (λ _ → isSetPathImplicit)
      λ f → to3ConnectedId
        con (funExt λ x → cong (fst f) (Iso.leftInv (IsoSphereJoin 1 1) x))
  leftInv π₃*Iso =
    sElim (λ _ → isSetPathImplicit)
      λ f → to3ConnectedId
        con (funExt (λ x → cong (fst f) (Iso.rightInv (IsoSphereJoin 1 1) x)))

  π₃* : Group ℓ
  π₃* = InducedGroup (π'Gr 2 A) (sRec2 squash₂ (λ x y → ∣ x +join y ∣₂))
        (isoToEquiv π₃*Iso)
          (sElim2 (λ _ _ → isSetPathImplicit) (λ f g → cong ∣_∣₂ (+join≡∙Π f g)))

  π₃≅π₃* : GroupEquiv (π'Gr 2 A) π₃*
  π₃≅π₃* =
    InducedGroupEquiv (π'Gr 2 A) (sRec2 squash₂ (λ x y → ∣ x +join y ∣₂))
        (isoToEquiv π₃*Iso)
          (sElim2 (λ _ _ → isSetPathImplicit) (λ f g → cong ∣_∣₂ (+join≡∙Π f g)))

-- Induced homomorphisms (A →∙ B) → (π₃*(A) → π₃*(B))
-- todo: remove connectivity assumptions
module _ (conA : (isConnected 3 (typ A))) (conB : (isConnected 3 (typ B)))
         (f : A →∙ B) where
  postCompπ₃* : GroupHom (π₃* A conA) (π₃* B conB)
  fst postCompπ₃* = sMap (f ∘∙_)
  snd postCompπ₃* =
    makeIsGroupHom
      (sElim2 (λ _ _ → isSetPathImplicit)
        λ h g → to3ConnectedId conB
          (funExt λ { (inl x) → refl
                    ; (inr x) → refl
                    ; (push a b i) j →
           (cong-∙∙ (fst f)
                    (cong (fst h) ((push a b ∙ (sym (push base b)))))
                    (snd h ∙ (sym (snd g)))
                    (cong (fst g) ((push base base
                               ∙∙ (sym (push a base))
                               ∙∙ push a b)))
          ∙ cong (cong (fst f)
                   (cong (fst h) (push a b ∙ (sym (push base b))))
                    ∙∙_∙∙
                   cong (fst f ∘ fst g)
                     ((push base base ∙∙ (sym (push a base)) ∙∙ push a b)))
                 (cong-∙ (fst f) (snd h) (sym (snd g))
                 ∙ λ j → compPath-filler (cong (fst f) (snd h)) (snd f) j
                        ∙ sym (compPath-filler
                               (cong (fst f) (snd g)) (snd f) j))) j i}))

-- Induced iso (A ≃∙ B) → π₃*(A) ≅ π₃*(B)
-- todo: remove connectivity assumptions
module _ (conA : (isConnected 3 (typ A))) (conB : (isConnected 3 (typ B)))
         (f : A ≃∙ B) where
  postCompπ₃*Equiv : GroupEquiv (π₃* A conA) (π₃* B conB)
  fst postCompπ₃*Equiv = isoToEquiv h
    where
    h : Iso (π₃* A conA .fst) (π₃* B conB .fst)
    fun h = fst (postCompπ₃* conA conB (≃∙map f))
    inv h = fst (postCompπ₃* conB conA (≃∙map (invEquiv∙ f)))
    rightInv h =
      sElim (λ _ → isSetPathImplicit)
        λ g → to3ConnectedId conB (funExt λ x → secEq (fst f) (fst g x))
    leftInv h =
      sElim (λ _ → isSetPathImplicit)
        λ g → to3ConnectedId conA (funExt λ x → retEq (fst f) (fst g x))
  snd postCompπ₃*Equiv = snd (postCompπ₃* conA conB (≃∙map f))

-- The relevant groups (in order of the iso π₃(S²) ≅ ℤ)
π₃S² = π'Gr 2 (S₊∙ 2)

π₃*S² = π₃* (S₊∙ 2) (sphereConnected 2)

π₃*joinS¹S¹ = π₃* (join S¹ S¹ , inl base) con-joinS¹S¹

π₃*S³ = π₃* (S₊∙ 3) connS³

π₃S³ = π'Gr 2 (S₊∙ 3)

{- Goal now:  Show that
   (η : π₃(S²))
↦ (η₁ : π₃*(S²))
↦ (η₂ : π₃*(S¹ * S¹))
↦ (η₃ : π₃*(S³))
↦ (η₄ : π₃(S³))
↦ (-2 : ℤ)

for some terms η₁ ... η₄ by a sequence of isomorphisms
π₃(S²) ≅ π₃*(S²) ≅ π₃*(S¹ * S¹) ≅ π₃*(S³) ≅ π₃(S³) ≅ ℤ

Hence, there is an is an iso π₃(S²) ≅ ℤ taking η to
-2, from which we can conclude π₄(S³) ≅ ℤ/2ℤ.
-}

-- Underlying functions of (some of) the ηs
η₁-raw : (join S¹ S¹ , inl base) →∙ S₊∙ 2
fst η₁-raw (inl x) = north
fst η₁-raw (inr x) = north
fst η₁-raw (push a b i) = (σ₁ b ∙ σ₁ a) i
snd η₁-raw = refl

η₂-raw : (join S¹ S¹ , inl base) →∙ (join S¹ S¹ , inl base)
fst η₂-raw (inl x) = inr (invLooper x)
fst η₂-raw (inr x) = inr x
fst η₂-raw (push a b i) =
    (sym (push (b * invLooper a) (invLooper a))
  ∙ push (b * invLooper a) b) i
snd η₂-raw = sym (push base base)

η₃-raw : (join S¹ S¹ , inl base) →∙ S₊∙ 3
fst η₃-raw (inl x) = north
fst η₃-raw (inr x) = north
fst η₃-raw (push a b i) =
  (sym (σ₂ (S¹×S¹→S² a b)) ∙ sym (σ₂ (S¹×S¹→S² a b))) i
snd η₃-raw = refl

-- Homotopy group versions
η₁ : fst π₃*S²
η₁ = ∣ η₁-raw ∣₂

η₂ : fst (π₃*joinS¹S¹)
η₂ = ∣ η₂-raw ∣₂

η₃ : π₃*S³ .fst
η₃ = ∣ η₃-raw ∣₂

η₄ : fst π₃S³
η₄ = ·π' 2 (-π' 2 ∣ idfun∙ (S₊∙ 3) ∣₂) (-π' 2 ∣ idfun∙ (S₊∙ 3) ∣₂)

-- π₃S²≅π₃*S²
π₃S²→π₃*S² : GroupEquiv π₃S² π₃*S²
π₃S²→π₃*S² = π₃≅π₃* (S₊∙ 2) (sphereConnected 2)

-- Time for π₃*(S¹ * S¹) ≅ π₃*S².
-- We have this iso already, but slightly differently stated,
-- so the following proof becomes a bit technical.
-- We define it in terms a slight variation of the Hopf map

Hopfσ : join S¹ S¹ → S₊ 2
Hopfσ (inl x) = north
Hopfσ (inr x) = north
Hopfσ (push a b i) = σ₁ (invLooper a * b) i

π₃*joinS¹S¹→π₃*S² : GroupHom π₃*joinS¹S¹ π₃*S²
π₃*joinS¹S¹→π₃*S² =
  postCompπ₃* con-joinS¹S¹ (sphereConnected 2)
    (Hopfσ , refl)

π₃*joinS¹S¹≅π₃*S² : GroupEquiv π₃*joinS¹S¹ π₃*S²
fst (fst π₃*joinS¹S¹≅π₃*S²) = fst π₃*joinS¹S¹→π₃*S²
snd (fst π₃*joinS¹S¹≅π₃*S²) =
  subst isEquiv idLem isEquivπ₃*joinS¹S¹→π₃*S²'
  where
  π₃*joinS¹S¹→π₃*S²' : GroupHom π₃*joinS¹S¹ π₃*S²
  π₃*joinS¹S¹→π₃*S²' =
    postCompπ₃* con-joinS¹S¹ (sphereConnected 2)
      (fst ∘ JoinS¹S¹→TotalHopf , refl)

  isEquivπ₃*joinS¹S¹→π₃*S²' : isEquiv (fst π₃*joinS¹S¹→π₃*S²')
  isEquivπ₃*joinS¹S¹→π₃*S²' =
    transport (λ i → isEquiv (fst (help (~ i))))
      (snd (fst GrEq))
    where
    GrEq = compGroupEquiv (πS³≅πTotalHopf 2) π'₃S²≅π'₃TotalHopf

    help : PathP
        (λ i → GroupHom
                (GroupPath π₃*joinS¹S¹ π₃S³ .fst
                  (compGroupEquiv
                   (invGroupEquiv (π₃≅π₃* (join S¹ S¹ , inl base) con-joinS¹S¹))
                   (π'Iso 2 (isoToEquiv (IsoSphereJoin 1 1) , refl))) i)
                (GroupPath π₃*S² π₃S² .fst
                  (invGroupEquiv (π₃≅π₃* (S₊∙ 2) (sphereConnected 2))) i))
         π₃*joinS¹S¹→π₃*S²'
         (fst (fst GrEq) , snd GrEq)
    help =
      toPathP (Σ≡Prop (λ _ → isPropIsGroupHom _ _)
        (funExt
          λ f → (λ i
           → transportRefl
              ((invGroupEquiv (π₃≅π₃* (S₊∙ 2) (sphereConnected 2))) .fst .fst
                (fst π₃*joinS¹S¹→π₃*S²' (
                  ((fst (fst (π₃≅π₃* (join S¹ S¹ , inl base) con-joinS¹S¹)))
                  (invEq (fst (π'Iso 2 (isoToEquiv (IsoSphereJoin 1 1) , refl)))
                     (transportRefl f i)))))) i)
              ∙ main f))

      where
      main : (f : _) → invEquiv (fst (π₃≅π₃* (S₊∙ 2) (sphereConnected 2))) .fst
        (fst π₃*joinS¹S¹→π₃*S²'
         (invEq
          (invEquiv (fst (π₃≅π₃* (join S¹ S¹ , inl base) con-joinS¹S¹)))
          (invEq (fst (π'Iso 2 (isoToEquiv Iso-joinS¹S¹-S³ , (λ _ → north))))
           f)))
        ≡ fst GrEq .fst f
      main = sElim (λ _ → isSetPathImplicit)
        λ f → to3ConnectedId (sphereConnected 2)
          (funExt λ x
            → (λ i → fst (JoinS¹S¹→TotalHopf (Iso.inv (IsoSphereJoin 1 1)
                              (fst f (Iso.rightInv (IsoSphereJoin 1 1) x i)))))
                    ∙ sym (funExt⁻ (sym (cong fst hopfMap≡HopfMap'))
                                (fst f x)))

  idLem : fst π₃*joinS¹S¹→π₃*S²' ≡ fst π₃*joinS¹S¹→π₃*S²
  idLem =
    funExt (sElim (λ _ → isSetPathImplicit)
           λ f → to3ConnectedId (sphereConnected 2)
      (funExt λ x → lem (fst f x)))
    where
    lem : (x : _) → fst (JoinS¹S¹→TotalHopf x) ≡ Hopfσ x
    lem (inl x) = refl
    lem (inr x) = sym (merid base)
    lem (push a b i) j =
      compPath-filler (merid (invLooper a * b)) (sym (merid base)) j i
snd π₃*joinS¹S¹≅π₃*S² = snd π₃*joinS¹S¹→π₃*S²

-- π₃*(S³) ≅ π₃*(S¹ * S¹)
π₃*S³≅π₃*joinS¹S¹ : GroupEquiv π₃*S³ π₃*joinS¹S¹
π₃*S³≅π₃*joinS¹S¹ =
  postCompπ₃*Equiv
    connS³ con-joinS¹S¹
      (isoToEquiv (invIso (IsoSphereJoin 1 1)) , refl)

-- π₃(S³)≅π₃*(S³)
π₃S³≅π₃*S³ : GroupEquiv π₃S³ π₃*S³
π₃S³≅π₃*S³ = π₃≅π₃* (S₊∙ 3) connS³

η↦η₁ :  fst (fst π₃S²→π₃*S²) η ≡ η₁
η↦η₁ = to3ConnectedId (sphereConnected 2)
         (funExt λ x → (funExt⁻ lem₁ x) ∙ sym (lem₂ x))
  where
  lem₁ : fold∘W ∘ joinS¹S¹→S³ ≡ fold⋁ ∘ (joinTo⋁ {A = S₊∙ 1} {B = S₊∙ 1})
  lem₁ = funExt λ x
    → cong (fold⋁ ∘ (joinTo⋁ {A = S₊∙ 1} {B = S₊∙ 1}))
      (leftInv (IsoSphereJoin 1 1) x)

  lem₂ : (x : join S¹ S¹) → fst η₁-raw x ≡ (fold⋁ ∘ joinTo⋁) x
  lem₂ (inl x) = refl
  lem₂ (inr x) = refl
  lem₂ (push a b i) j = help j i
    where
    help : (σ₁ b ∙ σ₁ a) ≡ cong (fold⋁ ∘ joinTo⋁) (push a b)
    help = sym (cong-∙∙ fold⋁ (λ j → inr (σ₁ b j))
                        (sym (push tt)) (λ j → inl (σ₁ a j))
             ∙ λ i → (λ j → σ₁ b (j ∧ ~ i))
                   ∙∙ (λ j → σ₁ b (j ∨ ~ i))
                   ∙∙ σ₁ a)

-- We show that η₂ ↦ η₁ (this is easier than η₁ ↦ η₂)
η₂↦η₁ : fst (fst π₃*joinS¹S¹≅π₃*S²) η₂ ≡ η₁
η₂↦η₁ =
  to3ConnectedId (sphereConnected 2)
    (funExt λ { (inl x) → refl
              ; (inr x) → refl
              ; (push a b i) j → main a b j i})
  where
  lem : (a b : S¹)
    → (sym (σ₁ (invLooper (b * invLooper a) * invLooper a)) ≡ σ₁ b)
     × (σ₁ (invLooper (b * invLooper a) * b) ≡ σ₁ a)
  fst (lem a b) =
       cong sym (cong σ₁ (sym (invLooperDistr (b * invLooper a) a))
               ∙ σ-invSphere 0 (b * invLooper a * a))
     ∙ cong σ₁ (sym (assocS¹ b (invLooper a) a)
     ∙ cong (b *_) (commS¹ _ _ ∙ sym (rCancelS¹ a))
     ∙ rUnitS¹ b)
  snd (lem a b) =
    cong σ₁ (cong (_* b) (invLooperDistr b (invLooper a)
           ∙ cong (invLooper b *_) (invSphere² 1 a)
           ∙ commS¹ (invLooper b) a)
           ∙ sym (assocS¹ a (invLooper b) b)
           ∙ cong (a *_) (commS¹ _ _ ∙ sym (rCancelS¹ b))
           ∙ rUnitS¹ a)

  main : (a b : S¹)
    → cong Hopfσ ((sym (push (b * invLooper a) (invLooper a))
                        ∙ push (b * invLooper a) b))
     ≡ σ₁ b ∙ σ₁ a
  main a b =
      cong-∙ Hopfσ (sym (push (b * invLooper a) (invLooper a)))
                          (push (b * invLooper a) b)
    ∙ cong₂ _∙_ (fst (lem a b)) (snd (lem a b))

-- We show that η₂ ↦ η₃
η₂↦η₃ : invEq (fst π₃*S³≅π₃*joinS¹S¹) η₂ ≡ η₃
η₂↦η₃ =
  to3ConnectedId connS³
   (funExt λ x → sym (joinS¹S¹→S³σ≡ (fst η₂-raw x))
                ∙ lem x)
  where
  joinS¹S¹→S³σ : join S¹ S¹ → S₊ 3
  joinS¹S¹→S³σ (inl x) = north
  joinS¹S¹→S³σ (inr x) = north
  joinS¹S¹→S³σ (push a b i) = σ₂ (S¹×S¹→S² a b) i

  joinS¹S¹→S³σ≡ : (x : _) → joinS¹S¹→S³σ x ≡ joinS¹S¹→S³ x
  joinS¹S¹→S³σ≡ (inl x) = refl
  joinS¹S¹→S³σ≡ (inr x) = merid north
  joinS¹S¹→S³σ≡ (push a b i) j =
    compPath-filler (merid (S¹×S¹→S² a b)) (sym (merid north)) (~ j) i

  lem : (x : _) → joinS¹S¹→S³σ (fst η₂-raw x) ≡ fst η₃-raw x
  lem (inl x) = refl
  lem (inr x) = refl
  lem (push a b i) j = main j i
    where
    left-lem : σ₂ (S¹×S¹→S² (b * invLooper a) (invLooper a))
             ≡ σ₂ (S¹×S¹→S² a b)
    left-lem = cong σ₂ (S¹×S¹→S²-Distr b (invLooper a)
             ∙ sym (S¹×S¹→S²-antiComm a b))

    right-lem : σ₂ (S¹×S¹→S² (b * invLooper a) b) ≡ sym (σ₂ (S¹×S¹→S² a b))
    right-lem =
         cong σ₂ ((cong (λ x → S¹×S¹→S² x b) (commS¹ b (invLooper a))
                 ∙ S¹×S¹→S²-Distr (invLooper a) b)
                 ∙∙ S¹×S¹→S²-antiComm (invLooper a) b
                 ∙∙ invSusp∘S¹×S¹→S² b (invLooper a))
      ∙∙ σ-invSphere 1 (S¹×S¹→S² b (invLooper a))
      ∙∙ cong (sym ∘ σ₂) (sym (S¹×S¹→S²-antiComm a b))

    main : cong (joinS¹S¹→S³σ ∘ fst η₂-raw) (push a b)
        ≡ sym (σ₂ (S¹×S¹→S² a b)) ∙ sym (σ₂ (S¹×S¹→S² a b))
    main = cong-∙ joinS¹S¹→S³σ
            (sym (push (b * invLooper a) (invLooper a)))
            (push (b * invLooper a) b)
         ∙ cong₂ _∙_ (cong sym left-lem) right-lem

-- We show that η₄ ↦ η₃ (this is easier than η₃ ↦ η₄)
η₄↦η₃ : fst (fst π₃S³≅π₃*S³) η₄ ≡ η₃
η₄↦η₃ = IsGroupHom.pres· (snd π₃S³≅π₃*S³)
            (-π' 2 ∣ idfun∙ (S₊∙ 3) ∣₂) (-π' 2 ∣ idfun∙ (S₊∙ 3) ∣₂)
       ∙ cong₂ _+π₃*_ gen↦η₃/2 gen↦η₃/2
       ∙ η₃/2+η₃/2≡η₃
  where
  _+π₃*_ : fst π₃*S³ → fst π₃*S³ → fst π₃*S³
  _+π₃*_ = GroupStr._·_ (snd π₃*S³)

  η₃-raw/2 : (join S¹ S¹ , inl base) →∙ S₊∙ 3
  fst η₃-raw/2 (inl x) = north
  fst η₃-raw/2 (inr x) = north
  fst η₃-raw/2 (push a b i) = σ₂ (S¹×S¹→S² a b) (~ i)
  snd η₃-raw/2 = refl

  η₃/2 : π₃*S³ .fst
  η₃/2 = ∣ η₃-raw/2 ∣₂

  gen↦η₃/2 : fst (fst π₃S³≅π₃*S³) (-π' 2 ∣ idfun∙ (S₊∙ 3) ∣₂) ≡ η₃/2
  gen↦η₃/2 =
    to3ConnectedId connS³
      (funExt λ { (inl x) → refl
                ; (inr x) → refl
                ; (push a b i) → refl})

  η₃/2+η₃/2≡η₃ : η₃/2 +π₃* η₃/2 ≡ η₃
  η₃/2+η₃/2≡η₃ =
    to3ConnectedId connS³
      (funExt λ { (inl x) → refl
                ; (inr x) → refl
                ; (push a b i) → λ j → lem a b j i})
    where
    lem : (a b : S¹) → cong (fst (η₃-raw/2 +join η₃-raw/2)) (push a b)
                      ≡ cong (fst η₃-raw) (push a b)
    lem a b = (λ i → cong-∙ (fst η₃-raw/2) (push a b) (sym (push base b)) i
                   ∙∙ rUnit refl (~ i)
                   ∙∙ cong-∙∙ (fst η₃-raw/2)
                        (push base base) (sym (push a base)) (push a b) i)
           ∙∙ (λ i → (sym (σ₂ (S¹×S¹→S² a b)) ∙ rCancel (merid north) i)
                   ∙∙ refl
                   ∙∙ (sym (rCancel (merid north) i)
                   ∙∙ (cong σ₂ (S¹×S¹→S²rUnit a) ∙ rCancel (merid north)) i
                   ∙∙ sym (σ₂ (S¹×S¹→S² a b))))
           ∙∙ ((λ i → rUnit (sym (σ₂ (S¹×S¹→S² a b))) (~ i)
                    ∙∙ refl
                    ∙∙ lUnit (sym (σ₂ (S¹×S¹→S² a b))) (~ i))
             ∙ λ i → (λ j → σ₂ (S¹×S¹→S² a b) (i ∨ ~ j))
                   ∙∙ (λ j → σ₂ (S¹×S¹→S² a b) (i ∧ ~ j))
                   ∙∙ sym (σ₂ (S¹×S¹→S² a b)))

-- Agda is very keen on expanding things, so we make an abstract
-- summary of the main lemmas above
abstract
  π₃S²≅π₃*S²-abs : GroupEquiv π₃S² π₃*S²
  π₃S²≅π₃*S²-abs = π₃S²→π₃*S²

  π₃*S²≅π₃*joinS¹S¹-abs : GroupEquiv π₃*S² π₃*joinS¹S¹
  π₃*S²≅π₃*joinS¹S¹-abs = invGroupEquiv π₃*joinS¹S¹≅π₃*S²

  π₃*joinS¹S¹≅π₃*S³-abs : GroupEquiv π₃*joinS¹S¹ π₃*S³
  π₃*joinS¹S¹≅π₃*S³-abs = invGroupEquiv π₃*S³≅π₃*joinS¹S¹

  π₃*S³≅π₃*S³-abs : GroupEquiv π₃*S³ π₃S³
  π₃*S³≅π₃*S³-abs = invGroupEquiv π₃S³≅π₃*S³

  -- stated in terms of (n : ℕ) to prevent normalisation
  π₃'S³≅ℤ-abs : (n : ℕ) → GroupEquiv (π'Gr n (S₊∙ (suc n))) ℤ
  π₃'S³≅ℤ-abs n = GroupIso→GroupEquiv (πₙ'Sⁿ≅ℤ n)

  η↦η₁-abs : fst (fst π₃S²≅π₃*S²-abs) η ≡ η₁
  η↦η₁-abs = η↦η₁

  η₁↦η₂-abs : fst (fst π₃*S²≅π₃*joinS¹S¹-abs) η₁ ≡ η₂
  η₁↦η₂-abs = cong (fst (fst π₃*S²≅π₃*joinS¹S¹-abs)) (sym η₂↦η₁)
              ∙ secEq (fst π₃*S²≅π₃*joinS¹S¹-abs) η₂

  η₂↦η₃-abs : fst (fst π₃*joinS¹S¹≅π₃*S³-abs) η₂ ≡ η₃
  η₂↦η₃-abs = η₂↦η₃

  η₃↦η₄-abs : fst (fst π₃*S³≅π₃*S³-abs) η₃ ≡ η₄
  η₃↦η₄-abs = cong (invEq (fst π₃S³≅π₃*S³)) (sym η₄↦η₃)
                  ∙ retEq (fst π₃S³≅π₃*S³) η₄

  gen↦1 : (n : ℕ) → fst (fst (π₃'S³≅ℤ-abs n)) ∣ idfun∙ (S₊∙ (suc n)) ∣₂ ≡ 1
  gen↦1 = πₙ'Sⁿ≅ℤ-idfun∙

-- We finally prove that η₄ ↦ -2
abstract
  η₄↦-2 : fst (fst (π₃'S³≅ℤ-abs 2)) η₄ ≡ -2
  η₄↦-2 = speedUp (∣ idfun∙ (S₊∙ 3) ∣₂) (gen↦1 2)
    where
    speedUp : (x : _)
      → fst (fst (π₃'S³≅ℤ-abs (suc (suc zero)))) x ≡ (pos (suc zero))
      → (fst (fst (π₃'S³≅ℤ-abs 2))) (·π' 2 (-π' 2 x) (-π' 2 x)) ≡ -2
    speedUp x p =
        IsGroupHom.pres· (π₃'S³≅ℤ-abs 2 .snd)
              (-π' 2 x) (-π' 2 x)
      ∙ cong (λ x → x +Z x)
        (IsGroupHom.presinv (π₃'S³≅ℤ-abs 2 .snd) x ∙ cong (inv (ℤ .snd)) p)

-- Puting it all together, we get our group iso π₃(S²) ≅ ℤ
π₃'S²≅ℤ : GroupEquiv (π'Gr 2 (S₊∙ 2)) ℤ
π₃'S²≅ℤ =
  compGroupEquiv
    π₃S²≅π₃*S²-abs
    (compGroupEquiv
      π₃*S²≅π₃*joinS¹S¹-abs
      (compGroupEquiv
        π₃*joinS¹S¹≅π₃*S³-abs
        (compGroupEquiv π₃*S³≅π₃*S³-abs
          (π₃'S³≅ℤ-abs 2))))

-- ... which takes η to -2
η↦-2 : fst (fst π₃'S²≅ℤ) η ≡ - 2
η↦-2 =
    cong (fst (fst (π₃'S³≅ℤ-abs 2)))
      (cong (fst π₃*S³≅π₃*S³-abs .fst)
        (cong (fst π₃*joinS¹S¹≅π₃*S³-abs .fst)
          (cong (fst (fst π₃*S²≅π₃*joinS¹S¹-abs))
            η↦η₁-abs
          ∙ η₁↦η₂-abs)
        ∙ η₂↦η₃-abs)
     ∙ η₃↦η₄-abs)
  ∙ η₄↦-2

-- We combine this with the rest of the main conclusions of chapters
-- 1-3 in Brunerie's thesis
BrunerieIso : GroupEquiv (π'Gr 3 (S₊∙ 3)) (ℤ/ 2)
BrunerieIso =
  compGroupEquiv
    (compGroupEquiv π₄S³≅π₃coFib-fold∘W∙
    (invGroupEquiv
      (GroupEquiv-abstractℤ/abs-gen
        (π'Gr 2 (S₊∙ 3)) (π'Gr 2 (S₊∙ 2)) (π'Gr 2 coFib-fold∘W∙)
          (invGroupEquiv (π₃'S³≅ℤ-abs 2))
          (invGroupEquiv π₃'S²≅ℤ)
          (π'∘∙Hom 2 (fold∘W , refl))
          _
          S³→S²→Pushout→Unit 2
            (cong abs (cong (invEq (invEquiv (fst π₃'S²≅ℤ))
                     ∘ sMap (_∘∙_ (fold∘W , refl)))
                      (sym (cong (invEq (fst (π₃'S³≅ℤ-abs 2))) (gen↦1 2))
                    ∙ retEq (fst (π₃'S³≅ℤ-abs 2)) ∣ idfun∙ (S₊∙ 3) ∣₂))
           ∙ cong abs η↦-2))))
           (abstractℤ/≅ℤ 2)


module Issue276 where

boo : {S T : Set}(f : S -> T)(x y : S) ->
      ((P : S -> Set) -> P x -> P y) ->
       (P : T -> Set) -> P (f x) -> P (f y)
boo = \ f x y q P -> q (\ s -> P (f s))

record Pack (S : Set) : Set where
  constructor pack
  field
    unpack : S

open Pack

unpack' : {S : Set} -> Pack S -> S
unpack' (pack s) = s

foo : {S : Set}(x : Pack S)(P : Pack S -> Set) -> P (pack (unpack x)) -> P x
foo = \ x P p -> p

goo : {S : Set}(x : Pack S)(P : S -> Set) -> P (unpack x) -> P (unpack' x)
goo = \ x -> boo unpack' (pack (unpack x)) x (foo x)

{- normal form of goo is \ x P p -> p -}

goo' : {S : Set}(x : Pack S)(P : S -> Set) -> P (unpack x) -> P (unpack' x)
goo' = \ x P p -> p
{-
/Users/conor/Desktop/fooling/RecConBug.agda:27,19-20
unpack x != unpack' x of type .S
when checking that the expression p has type P (unpack' x)
-}


{-# OPTIONS --no-positivity-check
            --no-termination-check
  #-}

-- A universe setoids
module Setoid where

import Chain

record True : Set where
data False : Set where

Rel : Set -> Set1
Rel A = A -> A -> Set

Pred : Set -> Set1
Pred A = A -> Set

Resp : {A : Set} -> Rel A -> {B : Set} -> Rel B -> Pred (A -> B)
Resp _R_ _S_ f = forall x y -> x R y -> f x S f y

mutual

  infix 40 _=El=_ _=S=_ _=Fam=_
  infix 60 _!_

  data Setoid : Set where
    nat : Setoid
    Π   : (A : Setoid)(F : Fam A) -> Setoid
    Σ   : (A : Setoid)(F : Fam A) -> Setoid

  data Fam (A : Setoid) : Set where
    fam : (F : El A -> Setoid) ->
          Resp _=El=_ _=S=_ F -> Fam A

  data El : Setoid -> Set where
    zero : El nat
    suc  : El nat -> El nat
    ƛ    : {A : Setoid}{F : Fam A}
           (f : (x : El A) -> El (F ! x)) ->
           ((x y : El A) -> x =El= y -> f x =El= f y) -> El (Π A F)
    _,_  : {A : Setoid}{F : Fam A}(x : El A) -> El (F ! x) -> El (Σ A F)

  data _=S=_ : (A B : Setoid) -> Set where
    eqNat : nat =S= nat
    eqΠ   : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂} ->
             A₂ =S= A₁ -> F₁ =Fam= F₂ -> Π A₁ F₁ =S= Π A₂ F₂
    eqΣ   : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂} ->
            A₁ =S= A₂ -> F₁ =Fam= F₂ -> Σ A₁ F₁ =S= Σ A₂ F₂

  data _=El=_ : {A B : Setoid} -> El A -> El B -> Set where
    eqInNat : {n : El nat} -> n =El= n
    eqInΠ   : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂}
              {f₁ : (x : El A₁) -> El (F₁ ! x)}
              {pf₁ : (x y : El A₁) -> x =El= y -> f₁ x =El= f₁ y}
              {f₂ : (x : El A₂) -> El (F₂ ! x)} ->
              {pf₂ : (x y : El A₂) -> x =El= y -> f₂ x =El= f₂ y} ->
              A₂ =S= A₁ ->
              ((x : El A₁)(y : El A₂) -> x =El= y -> f₁ x =El= f₂ y) ->
              ƛ f₁ pf₁ =El= ƛ f₂ pf₂
    eqInΣ   : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂}
              {x₁ : El A₁}{y₁ : El (F₁ ! x₁)}
              {x₂ : El A₂}{y₂ : El (F₂ ! x₂)} ->
              F₁ =Fam= F₂ -> x₁ =El= x₂ -> y₁ =El= y₂ -> (x₁ , y₁) =El= (x₂ , y₂)

  data _=Fam=_ {A B : Setoid}(F : Fam A)(G : Fam B) : Set where
    eqFam : B =S= A -> (forall x y -> x =El= y -> F ! x =S= G ! y) -> F =Fam= G

  _!_ : {A : Setoid} -> Fam A -> El A -> Setoid
  fam F _ ! x = F x

-- Inversions
famEqDom : {A B : Setoid}{F : Fam A}{G : Fam B} -> F =Fam= G -> B =S= A
famEqDom (eqFam p _) = p

famEqCodom : {A B : Setoid}{F : Fam A}{G : Fam B} -> F =Fam= G ->
        (x : El A)(y : El B) -> x =El= y -> F ! x =S= G ! y
famEqCodom (eqFam _ p) = p

eqΠ-inv₁ : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂} ->
           Π A₁ F₁ =S= Π A₂ F₂ -> A₂ =S= A₁
eqΠ-inv₁ (eqΠ p _) = p

eqΠ-inv₂ : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂} ->
           Π A₁ F₁ =S= Π A₂ F₂ -> F₁ =Fam= F₂
eqΠ-inv₂ (eqΠ _ p) = p

eqΣ-inv₁ : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂} ->
           Σ A₁ F₁ =S= Σ A₂ F₂ -> A₁ =S= A₂
eqΣ-inv₁ (eqΣ p _) = p

eqΣ-inv₂ : {A₁ A₂ : Setoid}{F₁ : Fam A₁}{F₂ : Fam A₂} ->
           Σ A₁ F₁ =S= Σ A₂ F₂ -> F₁ =Fam= F₂
eqΣ-inv₂ (eqΣ _ p) = p

-- Equivalence proofs and casts
mutual

  -- _=Fam=_ is an equivalence
  =Fam=-refl : {A : Setoid}{F : Fam A} -> F =Fam= F
  =Fam=-refl {F = fam _ p} = eqFam =S=-refl p

  =Fam=-sym : {A B : Setoid}{F : Fam A}{G : Fam B} -> F =Fam= G -> G =Fam= F
  =Fam=-sym (eqFam B=A F=G) = eqFam (=S=-sym B=A) \x y x=y -> =S=-sym (F=G _ _ (=El=-sym x=y))

  =Fam=-trans : {A B C : Setoid}{F : Fam A}{G : Fam B}{H : Fam C} ->
                F =Fam= G -> G =Fam= H -> F =Fam= H
  =Fam=-trans (eqFam B=A F=G) (eqFam C=B G=H) =
    eqFam (=S=-trans C=B B=A) \x y x=y ->
           =S=-trans (F=G x (B=A << x) (cast-id _))
                     (G=H _ y (=El=-trans (=El=-sym (cast-id _)) x=y))

  -- _=S=_ is an equivalence
  =S=-refl : {A : Setoid} -> A =S= A
  =S=-refl {nat}   = eqNat
  =S=-refl {Π A F} = eqΠ =S=-refl =Fam=-refl
  =S=-refl {Σ A F} = eqΣ =S=-refl =Fam=-refl

  =S=-sym : {A B : Setoid} -> A =S= B -> B =S= A
  =S=-sym eqNat = eqNat
  =S=-sym (eqΠ pA pF) = eqΠ (=S=-sym pA) (=Fam=-sym pF)
  =S=-sym (eqΣ pA pF) = eqΣ (=S=-sym pA) (=Fam=-sym pF)

  =S=-trans : {A B C : Setoid} -> A =S= B -> B =S= C -> A =S= C
  =S=-trans eqNat eqNat = eqNat
  =S=-trans (eqΠ B=A F=G) (eqΠ C=B G=H) = eqΠ (=S=-trans C=B B=A) (=Fam=-trans F=G G=H)
  =S=-trans (eqΣ A=B F=G) (eqΣ B=C G=H) = eqΣ (=S=-trans A=B B=C) (=Fam=-trans F=G G=H)

  -- _=El=_ is an equivalence
  =El=-refl : {A : Setoid}{x : El A} -> x =El= x
  =El=-refl {nat}   = eqInNat
  =El=-refl {Π A F}{ƛ f pf} = eqInΠ =S=-refl pf
  =El=-refl {Σ A F}{x , y}  = eqInΣ =Fam=-refl =El=-refl =El=-refl

  =El=-sym : {A B : Setoid}{x : El A}{y : El B} -> x =El= y -> y =El= x
  =El=-sym eqInNat = eqInNat
  =El=-sym (eqInΠ B=A p) = eqInΠ (=S=-sym B=A) \x y x=y -> =El=-sym (p y x (=El=-sym x=y))
  =El=-sym (eqInΣ pF px py) = eqInΣ (=Fam=-sym pF) (=El=-sym px) (=El=-sym py)

  =El=-trans : {A B C : Setoid}{x : El A}{y : El B}{z : El C} ->
             x =El= y -> y =El= z -> x =El= z
  =El=-trans eqInNat eqInNat = eqInNat
  =El=-trans (eqInΠ B=A f=g) (eqInΠ C=B g=h) =
    eqInΠ (=S=-trans C=B B=A) \x y x=y ->
      =El=-trans (f=g x (B=A << x) (cast-id _))
               (g=h _ y (=El=-trans (=El=-sym (cast-id _)) x=y))
  =El=-trans (eqInΣ F₁=F₂ x₁=x₂ y₁=y₂) (eqInΣ F₂=F₃ x₂=x₃ y₂=y₃) =
    eqInΣ (=Fam=-trans F₁=F₂ F₂=F₃) (=El=-trans x₁=x₂ x₂=x₃) (=El=-trans y₁=y₂ y₂=y₃)

  -- Casting. Important: don't look at the proof!
  infix 50 _<<_ _>>_

  _<<_ : {A B : Setoid} -> A =S= B -> El B -> El A
  _<<_ {nat}{Π _ _}   () _
  _<<_ {nat}{Σ _ _}   () _
  _<<_ {Π _ _}{nat}   () _
  _<<_ {Π _ _}{Σ _ _} () _
  _<<_ {Σ _ _}{nat}   () _
  _<<_ {Σ _ _}{Π _ _} () _
  _<<_ {nat}{nat} p x = x
  _<<_ {Π A₁ F₁}{Π A₂ F₂} p (ƛ f pf) = ƛ g pg
    where
      g : (x : El A₁) -> El (F₁ ! x)
      g x = let A₂=A₁ = eqΠ-inv₁ p
                F₁=F₂ = famEqCodom (eqΠ-inv₂ p) x _ (cast-id _)
            in  F₁=F₂ << f (A₂=A₁ << x)

      pg : (x y : El A₁) -> x =El= y -> g x =El= g y
      pg x y x=y = cast-irr _ _ (pf _ _ (cast-irr _ _ x=y))

  _<<_ {Σ A₁ F₁}{Σ A₂ F₂} p (x , y) = eqΣ-inv₁ p << x , F₁=F₂ << y
    where
      F₁=F₂ : F₁ ! (eqΣ-inv₁ p << x) =S= F₂ ! x
      F₁=F₂ = famEqCodom (eqΣ-inv₂ p) _ _ (=El=-sym (cast-id _))

  _>>_ : {A B : Setoid} -> Fam A -> A =S= B -> Fam B
  fam F pF >> A=B = fam G pG
    where
      G : El _ -> Setoid
      G y = F (A=B << y)

      pG : forall x y -> x =El= y -> G x =S= G y
      pG x y x=y = pF _ _ (cast-irr _ _ x=y)

  cast-id : {A B : Setoid}{x : El A}(p : B =S= A) -> x =El= p << x
  cast-id eqNat = eqInNat
  cast-id {x = x , y } (eqΣ A=B F=G) = eqInΣ (=Fam=-sym F=G) (cast-id _) (cast-id _)
  cast-id {x = ƛ f pf} (eqΠ B=A F=G) =
    eqInΠ (=S=-sym B=A) \x y x=y ->
      proof f x
          ≡ f (_ << y)      by pf _ _ (=El=-trans x=y (cast-id _))
          ≡ _ << f (_ << y) by cast-id _
      qed
    where
      open Chain El _=El=_ (\x -> =El=-refl) (\x y z -> =El=-trans)

  cast-irr : {A₁ A₂ B₁ B₂ : Setoid}{x : El A₁}{y : El A₂}
             (p₁ : B₁ =S= A₁)(p₂ : B₂ =S= A₂) -> x =El= y -> p₁ << x =El= p₂ << y
  cast-irr {x = x}{y = y} p q x=y =
    proof p << x
        ≡ x by =El=-sym (cast-id _)
        ≡ y by x=y
        ≡ q << y by cast-id _
    qed
    where
      open Chain El _=El=_ (\x -> =El=-refl) (\x y z -> =El=-trans)

-- Let's do some overloading

data EqFam : Set -> Set where
  el     : EqFam Setoid
  setoid : EqFam True
  fam    : EqFam Setoid

[_] : {I : Set} -> EqFam I -> I -> Set
[ el ]     A = El A
[ setoid ] _ = Setoid
[ fam ]    A = Fam A

_==_ : {I : Set}{eqf : EqFam I}{i j : I} -> [ eqf ] i -> [ eqf ] j -> Set
_==_ {eqf = el    } x y = x =El= y
_==_ {eqf = setoid} A B = A =S= B
_==_ {eqf = fam   } F G = F =Fam= G

refl : {I : Set}{eqf : EqFam I}{i : I}{x : [ eqf ] i} -> x == x
refl {eqf = el}     = =El=-refl
refl {eqf = setoid} = =S=-refl
refl {eqf = fam}    = =Fam=-refl

sym : {I : Set}{eqf : EqFam I}{i j : I}{x : [ eqf ] i}{y : [ eqf ] j} -> x == y -> y == x
sym {eqf = el}     = =El=-sym
sym {eqf = setoid} = =S=-sym
sym {eqf = fam}    = =Fam=-sym

trans : {I : Set}{eqf : EqFam I}{i j k : I}{x : [ eqf ] i}{y : [ eqf ] j}{z : [ eqf ] k} ->
        x == y -> y == z -> x == z
trans {eqf = el}     = =El=-trans
trans {eqf = setoid} = =S=-trans
trans {eqf = fam}    = =Fam=-trans

open module EqChain {I : Set}{eqf : EqFam I} =
  Chain ([ eqf ]) _==_ (\x -> refl) (\x y z -> trans)

homo : {A B : Setoid}{x : El A}{y : El B} -> x == y -> A == B
homo eqInNat = eqNat
homo (eqInΠ B=A p) = eqΠ B=A (eqFam B=A \x y x=y -> homo (p x y x=y))
homo (eqInΣ F=G p q) = eqΣ (homo p) F=G

cast-id' : {A B C : Setoid}{x : El A}{y : El B}(p : C == B) -> x == y -> x == p << y
cast-id' C=B x=y = trans x=y (cast-id _)

-- Some helper stuff
K : {A : Setoid} -> Setoid -> Fam A
K B = fam (\_ -> B) (\_ _ _ -> refl)

infixr 20 _==>_
infixl 70 _·_ _∘_ _○_
infixl 90 _#_

!-cong : {A B : Setoid}(F : Fam A)(G : Fam B){x : El A}{y : El B} ->
         F == G -> x == y -> F ! x == G ! y
!-cong F G (eqFam B=A F=G) x=y = F=G _ _ x=y

!-cong-R : {A : Setoid}(F : Fam A){x y : El A} ->
           x == y -> F ! x == F ! y
!-cong-R F x=y = !-cong F F refl x=y

_==>_ : Setoid -> Setoid -> Setoid
A ==> B = Π A (K B)

_#_ : {A : Setoid}{F : Fam A} -> El (Π A F) -> (x : El A) -> El (F ! x)
ƛ f _ # x = f x

#-cong : {A B : Setoid}{F : Fam A}{G : Fam B}
         (f : El (Π A F))(g : El (Π B G)){x : El A}{y : El B} ->
         f == g -> x == y -> f # x == g # y
#-cong ._ ._ (eqInΠ _ f=g) x=y = f=g _ _ x=y

#-cong-R : {A : Setoid}{F : Fam A}(f : El (Π A F)){x y : El A} ->
           x == y -> f # x == f # y
#-cong-R f p = #-cong f f refl p

id : {A : Setoid} -> El (A ==> A)
id = ƛ (\x -> x) (\_ _ p -> p)

-- Family composition
_○_ : {A B : Setoid} -> Fam A -> El (B ==> A) -> Fam B
F ○ f = fam (\x -> F ! (f # x)) (\x y x=y -> !-cong-R F (#-cong-R f x=y))

lem-○-id : {A : Setoid}{F : Fam A} -> F ○ id == F
lem-○-id {F = F} = eqFam refl \x y x=y -> !-cong-R F x=y

_∘_ : {A B : Setoid}{F : Fam B} -> El (Π B F) -> (g : El (A ==> B)) -> El (Π A (F ○ g))
f ∘ g = ƛ (\x -> f # (g # x)) \x y x=y -> #-cong-R f (#-cong-R g x=y)

lem-∘-id : {A : Setoid}{F : Fam A}(f : El (Π A F)) -> f ∘ id == f
lem-∘-id (ƛ f pf) = eqInΠ refl pf

lem-id-∘ : {A B : Setoid}(f : El (A ==> B)) -> id ∘ f == f
lem-id-∘ (ƛ f pf) = eqInΠ refl pf

-- Simply type composition (not quite a special case of ∘ because of proof relevance)
_·_ : {A B C : Setoid} -> El (B ==> C) -> El (A ==> B) -> El (A ==> C)
f · g = eqΠ refl (eqFam refl \_ _ _ -> refl) << f ∘ g

fst : {A : Setoid}{F : Fam A} -> El (Σ A F) -> El A
fst (x , y) = x

snd : {A : Setoid}{F : Fam A}(p : El (Σ A F)) -> El (F ! fst p)
snd (x , y) = y

fst-eq : {A B : Setoid}{F : Fam A}{G : Fam B}
         {x : El (Σ A F)}{y : El (Σ B G)} -> x == y -> fst x == fst y
fst-eq (eqInΣ _ x₁=x₂ _) = x₁=x₂

snd-eq : {A B : Setoid}{F : Fam A}{G : Fam B}
        {x : El (Σ A F)}{y : El (Σ B G)} -> x == y -> snd x == snd y
snd-eq (eqInΣ _ _ y₁=y₂) = y₁=y₂

η : {A : Setoid}{F : Fam A}(f : El (Π A F)){pf : (x y : El A) -> x == y -> f # x == f # y} ->
    f == ƛ {F = F} (_#_ f) pf
η (ƛ f pf) = eqInΠ refl pf


------------------------------------------------------------------------
-- The Agda standard library
--
-- Commutative semirings with some additional structure ("almost"
-- commutative rings), used by the ring solver
------------------------------------------------------------------------

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

module Algebra.Solver.Ring.AlmostCommutativeRing where

open import Relation.Binary
open import Algebra
open import Algebra.Structures
open import Algebra.FunctionProperties
import Algebra.Morphism as Morphism
open import Function
open import Level

------------------------------------------------------------------------
-- Definitions

record IsAlmostCommutativeRing
         {a ℓ} {A : Set a} (_≈_ : Rel A ℓ)
         (_+_ _*_ : Op₂ A) (-_ : Op₁ A) (0# 1# : A) : Set (a ⊔ ℓ) where
  field
    isCommutativeSemiring : IsCommutativeSemiring _≈_ _+_ _*_ 0# 1#
    -‿cong                : -_ Preserves _≈_ ⟶ _≈_
    -‿*-distribˡ          : ∀ x y → ((- x) * y)     ≈ (- (x * y))
    -‿+-comm              : ∀ x y → ((- x) + (- y)) ≈ (- (x + y))

  open IsCommutativeSemiring isCommutativeSemiring public

record AlmostCommutativeRing c ℓ : Set (suc (c ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 6 _+_
  infix  4 _≈_
  field
    Carrier                 : Set c
    _≈_                     : Rel Carrier ℓ
    _+_                     : Op₂ Carrier
    _*_                     : Op₂ Carrier
    -_                      : Op₁ Carrier
    0#                      : Carrier
    1#                      : Carrier
    isAlmostCommutativeRing :
      IsAlmostCommutativeRing _≈_ _+_ _*_ -_ 0# 1#

  open IsAlmostCommutativeRing isAlmostCommutativeRing public

  commutativeSemiring : CommutativeSemiring _ _
  commutativeSemiring =
    record { isCommutativeSemiring = isCommutativeSemiring }

  open CommutativeSemiring commutativeSemiring public
         using ( +-semigroup; +-monoid; +-commutativeMonoid
               ; *-semigroup; *-monoid; *-commutativeMonoid
               ; semiring
               )

  rawRing : RawRing _
  rawRing = record
    { _+_ = _+_
    ; _*_ = _*_
    ; -_  = -_
    ; 0#  = 0#
    ; 1#  = 1#
    }

------------------------------------------------------------------------
-- Homomorphisms

record _-Raw-AlmostCommutative⟶_
         {r₁ r₂ r₃}
         (From : RawRing r₁)
         (To : AlmostCommutativeRing r₂ r₃) : Set (r₁ ⊔ r₂ ⊔ r₃) where
  private
    module F = RawRing From
    module T = AlmostCommutativeRing To
  open Morphism.Definitions F.Carrier T.Carrier T._≈_
  field
    ⟦_⟧    : Morphism
    +-homo : Homomorphic₂ ⟦_⟧ F._+_ T._+_
    *-homo : Homomorphic₂ ⟦_⟧ F._*_ T._*_
    -‿homo : Homomorphic₁ ⟦_⟧ F.-_  T.-_
    0-homo : Homomorphic₀ ⟦_⟧ F.0#  T.0#
    1-homo : Homomorphic₀ ⟦_⟧ F.1#  T.1#

-raw-almostCommutative⟶
  : ∀ {r₁ r₂} (R : AlmostCommutativeRing r₁ r₂) →
    AlmostCommutativeRing.rawRing R -Raw-AlmostCommutative⟶ R
-raw-almostCommutative⟶ R = record
  { ⟦_⟧    = id
  ; +-homo = λ _ _ → refl
  ; *-homo = λ _ _ → refl
  ; -‿homo = λ _ → refl
  ; 0-homo = refl
  ; 1-homo = refl
  }
  where open AlmostCommutativeRing R

-- A homomorphism induces a notion of equivalence on the raw ring.

Induced-equivalence :
  ∀ {c₁ c₂ ℓ} {Coeff : RawRing c₁} {R : AlmostCommutativeRing c₂ ℓ} →
  Coeff -Raw-AlmostCommutative⟶ R → Rel (RawRing.Carrier Coeff) ℓ
Induced-equivalence {R = R} morphism a b = ⟦ a ⟧ ≈ ⟦ b ⟧
  where
  open AlmostCommutativeRing R
  open _-Raw-AlmostCommutative⟶_ morphism

------------------------------------------------------------------------
-- Conversions

-- Commutative rings are almost commutative rings.

fromCommutativeRing :
  ∀ {r₁ r₂} → CommutativeRing r₁ r₂ → AlmostCommutativeRing _ _
fromCommutativeRing CR = record
  { isAlmostCommutativeRing = record
      { isCommutativeSemiring = isCommutativeSemiring
      ; -‿cong                = -‿cong
      ; -‿*-distribˡ          = -‿*-distribˡ
      ; -‿+-comm              = ⁻¹-∙-comm
      }
  }
  where
  open CommutativeRing CR
  import Algebra.Properties.Ring as R; open R ring
  import Algebra.Properties.AbelianGroup as AG; open AG +-abelianGroup

-- Commutative semirings can be viewed as almost commutative rings by
-- using identity as the "almost negation".

fromCommutativeSemiring :
  ∀ {r₁ r₂} → CommutativeSemiring r₁ r₂ → AlmostCommutativeRing _ _
fromCommutativeSemiring CS = record
  { -_                      = id
  ; isAlmostCommutativeRing = record
      { isCommutativeSemiring = isCommutativeSemiring
      ; -‿cong                = id
      ; -‿*-distribˡ          = λ _ _ → refl
      ; -‿+-comm              = λ _ _ → refl
      }
  }
  where open CommutativeSemiring CS


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Data.Empty.Polymorphic where

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude

import Cubical.Data.Empty as Empty

⊥ : ∀ {ℓ} → Type ℓ
⊥ = Lift Empty.⊥

⊥-elim : ∀ {w ℓ} {Whatever : ⊥ {ℓ} → Type w} → (witness : ⊥ {ℓ}) → Whatever witness
⊥-elim ()

isProp⊥ : ∀ {ℓ} → isProp (⊥ {ℓ})
isProp⊥ ()

isContr⊥→A : ∀ {ℓ ℓ′} {A : Type ℓ′} → isContr (⊥ {ℓ} → A)
fst isContr⊥→A ()
snd isContr⊥→A f i ()


module Structure.Operator.Functions where

import      Lvl
open import Logic
open import Structure.Setoid
open import Structure.Operator.Properties
open import Type

private variable ℓ ℓ₁ ℓ₂ ℓ₃ ℓₑ ℓₑ₁ ℓₑ₂ ℓₑ₃ : Lvl.Level

module _ {A : Type{ℓ₁}} {X : Type{ℓ₂}} ⦃ equiv-X : Equiv{ℓₑ₁}(X) ⦄ {Y : Type{ℓ₃}} ⦃ equiv-Y : Equiv{ℓₑ₂}(Y) ⦄ where
  -- `a` is a element which yields the left identity element in the function `f`.
  -- (a ∈ kernelₗ(f)) means (f(a) = id).
  kernelₗ : ∀{_▫_ : X → Y → Y}{id : X} → ⦃ _ : Identityₗ(_▫_)(id) ⦄ → (A → X) → A → Stmt
  kernelₗ {_}{id} (f)(a) = (f(a) ≡ id)

  -- `a` is a element which yields the right identity element in the function `f`.
  -- (a ∈ kernelₗ(f)) means (f(a) = id).
  kernelᵣ : ∀{_▫_ : Y → X → Y}{id : X} → ⦃ _ : Identityᵣ(_▫_)(id) ⦄ → (A → X) → A → Stmt
  kernelᵣ {_}{id} (f)(a) = (f(a) ≡ id)


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

module Categories.Functor.Construction.Limit where

open import Function using (_$_)

open import Categories.Adjoint
open import Categories.Adjoint.Equivalence
open import Categories.Adjoint.Properties
open import Categories.Category
open import Categories.Category.Equivalence
open import Categories.Category.Construction.Cones
open import Categories.Category.Complete
open import Categories.Category.Cocomplete
open import Categories.Category.Construction.Functors
open import Categories.Functor
open import Categories.Functor.Construction.Diagonal
open import Categories.Functor.Construction.Constant
open import Categories.NaturalTransformation renaming (id to idN)
open import Categories.NaturalTransformation.NaturalIsomorphism using (_≃_; module ≃)
open import Categories.Diagram.Cone.Properties
open import Categories.Diagram.Duality

import Categories.Diagram.Limit as Lim
import Categories.Diagram.Cone as Con
import Categories.Morphism.Reasoning as MR

module _ {o ℓ e o′ ℓ′ e′} (C : Category o ℓ e) (Com : Complete o′ ℓ′ e′ C) {J : Category o′ ℓ′ e′} where
  private
    module C = Category C
    module J = Category J
    open C
    open HomReasoning
    open Lim.Limit
    open Con.Cone⇒ renaming (commute to ⇒-commute)
    open MR C

    K⇒lim : ∀ {F G : Functor J C} (α : NaturalTransformation F G) (K : Con.Cone F) →
              Cones G [ nat-map-Cone α K , limit (Com G) ]
    K⇒lim {G = G} α K = rep-cone (Com G) (nat-map-Cone α K)

    lim⇒lim : ∀ {F G : Functor J C} (α : NaturalTransformation F G) →
                Cones G [ nat-map-Cone α (limit (Com F)) , limit (Com G) ]
    lim⇒lim {F} α = K⇒lim α (limit (Com F))

    id-Cone : ∀ X → Con.Cone {C = C} {J = J} (const X)
    id-Cone X = record
      { apex = record
        { ψ       = λ _ → C.id
        ; commute = λ _ → C.identity²
        }
      }

    X⇒limΔ : ∀ X → Cones (const {C = J} X) [ id-Cone X , limit (Com (const X)) ]
    X⇒limΔ X = rep-cone (Com (const X)) _

  LimitF : Functor (Functors J C) C
  LimitF = record
    { F₀           = λ F → apex (Com F)
    ; F₁           = λ {F G} α → arr (lim⇒lim α)
    ; identity     = λ {F} → terminal.!-unique (Com F) $ record
      { arr     = C.id
      ; commute = id-comm
      }
    ; homomorphism = λ {F G H} {α β} →
      let module α = NaturalTransformation α
          module β = NaturalTransformation β
      in terminal.!-unique₂ (Com H) {_} {terminal.! (Com H)}
      {record { commute = λ {j} → begin
        proj (Com H) j ∘ arr (lim⇒lim β) ∘ arr (lim⇒lim α) ≈⟨ pullˡ (⇒-commute (lim⇒lim β)) ⟩
        (β.η j ∘ proj (Com G) j) ∘ arr (lim⇒lim α)         ≈⟨ assoc ⟩
        β.η j ∘ proj (Com G) j ∘ arr (lim⇒lim α)           ≈⟨ pushʳ (⇒-commute (lim⇒lim α)) ⟩
        (β.η j ∘ α.η j) ∘ proj (Com F) j                   ∎ }}
    ; F-resp-≈     = λ {F G} {α β} eq → terminal.!-unique (Com G) $ record
      { commute = ⇒-commute (lim⇒lim β) ○ ∘-resp-≈ˡ (⟺ eq)
      }
    }

  Δ⊣LimitF : ΔF J ⊣ LimitF
  Δ⊣LimitF = record
    { unit   = ntHelper record
      { η       = λ X → rep (Com (const X)) (id-Cone X)
      ; commute = λ {X Y} f → terminal.!-unique₂ (Com (const Y))
        {record
          { apex = record
            { ψ       = λ _ → f
            ; commute = λ _ → C.identityˡ
            }
          }}
        {record
          { commute = cancelˡ (⇒-commute (X⇒limΔ Y))
          }}
        {record
          { commute =
            ⇒-commute (Cones (const Y) [ lim⇒lim (constNat f)
                                       ∘ nat-map-Cone⇒ (constNat f) (terminal.! (Com (const X)) {id-Cone X}) ])
            ○ identityʳ
          }}
      }
    ; counit = ntHelper record
      { η       = counit-nat
      ; commute = λ α → ⇒-commute (lim⇒lim α)
      }
    ; zig    = λ {X} → ⇒-commute (X⇒limΔ X)
    ; zag    = λ {F} →
      let apF = apex (Com F)
          align : Cones F [ limit (Com F) , nat-map-Cone (counit-nat F) (id-Cone apF) ]
          align = record
            { arr     = C.id
            ; commute = identityʳ ○ identityʳ
            }
          limF⇒limF : Cones F [ limit (Com F) , limit (Com F) ]
          limF⇒limF = Cones F [ Cones F [ lim⇒lim (counit-nat F)
                              ∘ nat-map-Cone⇒ (counit-nat F) (terminal.! (Com (const apF))) ]
                              ∘ align ]
      in terminal.!-unique₂ (Com F)
         {limit (Com F)}
         {record { commute = ∘-resp-≈ʳ (⟺ identityʳ) ○ ⇒-commute limF⇒limF }}
         {record { commute = identityʳ }}
    }
    where counit-nat : (F : Functor J C) → NaturalTransformation (const (apex (Com F))) F
          counit-nat F = ntHelper record
            { η       = proj (Com F)
            ; commute = λ f → identityʳ ○ ⟺ (limit-commute (Com F) f)
            }

module _ {o ℓ e o′ ℓ′ e′} (C : Category o ℓ e) (Coc : Cocomplete o′ ℓ′ e′ C) {J : Category o′ ℓ′ e′} where
  private
    module C = Category C
    module J = Category J
    open C
    open MR C

    Com : Complete o′ ℓ′ e′ C.op
    Com F = Colimit⇒coLimit C (Coc (Functor.op F))
    LF  : Functor (Functors J.op op) C.op
    LF    = LimitF C.op Com {J.op}

  ColimitF : Functor (Functors J C) C
  ColimitF = Functor.op LF ∘F opF⇐

  ColimitF⊣Δ : ColimitF ⊣ ΔF J
  ColimitF⊣Δ = ⊣×≃⇒⊣ helper ≃.refl ΔF≃
    where Δ⊣LimitFᵒᵖ : ΔF J.op ⊣ LF
          Δ⊣LimitFᵒᵖ = Δ⊣LimitF op Com {J.op}
          opF⊣ : opF⇐ {A = J} {C} ⊣ opF⇒
          opF⊣ = StrongEquivalence.F⊣G.R⊣L Functorsᵒᵖ-equiv
          helper : ColimitF ⊣ opF⇒ ∘F Functor.op (ΔF J.op)
          helper = opF⊣ ∘⊣ Adjoint.op Δ⊣LimitFᵒᵖ
          ΔF≃ : opF⇒ ∘F Functor.op (ΔF J.op) ≃ ΔF J
          ΔF≃ = record
            { F⇒G = record
              { η           = λ _ → idN
              ; commute     = λ _ → id-comm-sym
              ; sym-commute = λ _ → id-comm
              }
            ; F⇐G = record
              { η           = λ _ → idN
              ; commute     = λ _ → id-comm-sym
              ; sym-commute = λ _ → id-comm
              }
            ; iso = λ X → record
              { isoˡ = identity²
              ; isoʳ = identity²
              }
            }


module Eta (Gnd : Set)(U : Set)(El : U -> Set) where

open import Basics
open import Pr
open import Nom
import Kind
open Kind Gnd U El
import Cxt
open Cxt Kind
import Loc
open Loc Kind
import Term
open Term Gnd U El
import Shift
open Shift Gnd U El

data Sawn (G : Cxt)(C : Kind)(L : Loc)(R : Kind) : Kind -> Set where
  snil : Sawn G C L R R
  scons : {S T : Kind} -> Sawn G C L R (S |> T) ->
                          G [ L / Term C ]- S ->
                          Sawn G C L R T
  sarg : {A : U}{K : El A -> Kind} ->
         Sawn G C L R (Pi A K) -> (a : El A) ->
         Sawn G C L R (K a)

stitch : {G : Cxt}{C : Kind}{Z : Gnd}{L : Loc}{R S : Kind} ->
         Sawn G C L R S -> G [ L / Args C Z ]- S -> G [ L / Args C Z ]- R
stitch snil s = s
stitch (scons r s) t = stitch r (s G& t)
stitch (sarg r a) t = stitch r (a G^ t)

sawsh : {G : Cxt}{C : Kind}{L M : Loc} ->
        ({T : Kind} -> G [ L / Head ]- T -> G [ M / Head ]- T) ->
        {R S : Kind} -> Sawn G C L R S -> Sawn G C M R S
sawsh rho snil = snil
sawsh rho (scons r s) = scons (sawsh rho r) (shift rho s)
sawsh rho (sarg r a) = sarg (sawsh rho r) a

long : {G : Cxt}{C : Kind}{L : Loc}(S : Kind){T : Kind} ->
       G [ L / Head ]- T ->
       Sawn G C L T S ->
       G [ L / Term C ]- S
long (Ty Z)   h s = h G$ (stitch s Gnil)
long (Pi A K) h s = Gfn A \ a -> long (K a) h (sarg s a)
long (S |> T) h s =
  G\\ (long T (popH h) (scons (sawsh popH s) (long S (# top -! _) snil)))

var : {G : Cxt}{C : Kind}(x : Nom){Gx : [| G Has x |]} ->
      G [ EL / Term C ]- (wit ((G ?- x) {Gx}))
var {G} x {Gx} with (G ?- x) {Gx}
... | [ T / g ] = long T (` x -! g) snil


{-# OPTIONS --cubical --no-import-sorts --no-exact-split --safe #-}
module Cubical.Data.NatMinusOne.Properties where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Univalence
open import Cubical.Data.Nat
open import Cubical.Data.NatMinusOne.Base
open import Cubical.Reflection.StrictEquiv

-1+Path : ℕ ≡ ℕ₋₁
-1+Path = ua e
  where
  unquoteDecl e = declStrictEquiv e -1+_ 1+_


module WellTypedTermsModel where

open import Library
open import WellTypedTerms
open import RMonads.REM
open import FunctorCat
open import Categories.Sets

-- interpretation of types

Val : Ty → Set
Val ι       = ⊤
Val (σ ⇒ τ) = Val σ → Val τ

-- interpretation of contexts

Env : Con → Set
Env Γ = ∀{σ} →  Var Γ σ → Val σ

_<<_ : ∀{Γ σ} → Env Γ → Val σ → Env (Γ < σ)
(γ << v) vz     = v
(γ << v) (vs x) = γ x

-- intepretation of terms

eval : ∀{Γ σ} → Env Γ → Tm Γ σ → Val σ
eval γ (var x)   = γ x
eval γ (app t u) = eval γ t (eval γ u)
eval γ (lam t)   = λ v → eval (γ << v) t

substeval : ∀{σ τ}(p : σ ≅ τ){Γ : Con}{γ : Env Γ}(t : Tm Γ σ) →
      (subst Val p  ∘ eval γ) t ≅ (eval γ ∘ subst (Tm Γ) p) t
substeval refl t = refl

wk<< : ∀{Γ Δ}(α  : Ren Γ Δ)(β : Env Δ){σ}(v : Val σ) →
          ∀{ρ}(y : Var (Γ < σ) ρ) →
          ((β ∘ α) << v) y ≅ (β << v) (wk α y)
wk<< α β v vz     = refl
wk<< α β v (vs y) = refl

reneval : ∀{Γ Δ σ}(α : Ren Γ Δ)(β : Env Δ)(t : Tm Γ σ) →
          eval (β ∘ α) t ≅ (eval β ∘ ren α) t
reneval α β (var x)   = refl
reneval α β (app t u) =
  proof
  eval (β ∘ α) t (eval (β ∘ α) u)
  ≅⟨ cong₂ (λ f x → f x) (reneval α β t) (reneval α β u) ⟩
  eval β (ren α t) (eval β (ren α u))
  ∎
reneval α β (lam t)   = ext λ v →
  proof
  eval ((β ∘ α) << v) t
  ≅⟨ cong (λ (γ : Env _) → eval γ t) (iext λ _ → ext (wk<< α β v)) ⟩
  eval ((β << v) ∘ wk α) t
  ≅⟨ reneval (wk α) (β << v) t ⟩
  eval (β << v) (ren (wk α) t)
  ∎

lifteval : ∀{Γ Δ σ τ}(α : Sub Γ Δ)(β : Env Δ)(v : Val σ)(y : Var (Γ < σ) τ) →
            ((eval β ∘ α) << v) y ≅ (eval (β << v) ∘ lift α) y
lifteval α β v vz     = refl
lifteval α β v (vs x) =
  proof
  eval β (α x)
  ≅⟨ reneval vs (β << v) (α x) ⟩
  eval (β << v) (ren vs (α x))
  ∎

subeval : ∀{Γ Δ σ}(α : Sub Γ Δ)(β : Env Δ)(t : Tm Γ σ) →
          eval (eval β ∘ α) t ≅ (eval β ∘ sub α) t
subeval α β (var x)   = refl
subeval α β (app t u) =
  proof
  eval (eval β ∘ α) t (eval (eval β ∘ α) u)
  ≅⟨ cong₂ (λ f x → f x) (subeval α β t) (subeval α β u) ⟩
  eval β (sub α t) (eval β (sub α u))
  ∎
subeval α β (lam t)   = ext λ v →
  proof
  eval ((eval β ∘ α) << v) t
  ≅⟨ cong (λ (γ : Env _) → eval γ t) (iext λ _ → ext (lifteval α β v)) ⟩
  eval (eval (β << v) ∘ lift α) t
  ≅⟨ subeval (lift α) (β << v) t ⟩
  eval (β << v) (sub (lift α) t)
  ∎

modelRAlg : RAlg TmRMonad
modelRAlg = record {
  acar  = Val;
  astr  = λ {Γ} → λ γ → eval γ;
  alaw1 = refl;
  alaw2 = λ {Γ Δ α γ} → iext λ σ → ext (subeval α γ)}


{-# OPTIONS --cubical #-}

open import 17-number-theory public

{- streams -}

record stream (A : Set) : Set where
  coinductive
  field
    hd : A
    tl : stream A

open stream public

from : ℕ → stream ℕ
hd (from n) = n
tl (from n) = from (succ-ℕ n)

map-stream : {A B : Set} → (A → B) → stream A → stream B
hd (map-stream f xs) = f (hd xs)
tl (map-stream f xs) = map-stream f (tl xs)

list-stream :
  {A : Set} → stream A → ℕ → list A
list-stream xs zero-ℕ = nil
list-stream xs (succ-ℕ n) = cons (hd xs) (list-stream xs n)

record ℕ∞ : Set where
  coinductive
  field
    pred∞ : coprod ℕ∞ unit

zero-ℕ∞ : ℕ∞
pred∞ zero-ℕ∞ = {!inr star!}


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Relation.Unary.Raw where

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude using (isProp)

open import Cubical.Data.Empty hiding (rec)
open import Cubical.Data.Unit.Base using (⊤)
open import Cubical.Data.Sigma
open import Cubical.Data.Sum.Base using (_⊎_; rec)
open import Cubical.Foundations.Function

open import Cubical.Relation.Nullary
open import Cubical.Relation.Nullary.Decidable using (IsYes)

open import Cubical.Relation.Unary using (RawPred)

private
  variable
    a b c ℓ ℓ₁ ℓ₂ : Level
    A : Type a
    B : Type b
    C : Type c

------------------------------------------------------------------------
-- Special sets

-- The empty set.

∅ : RawPred A _
∅ = λ _ → ⊥

-- The singleton set.

｛_｝ : A → RawPred A _
｛ x ｝ = _≡ x

-- The universal set.

U : RawPred A _
U = λ _ → ⊤

------------------------------------------------------------------------
-- Membership

infix 6 _∈_ _∉_

_∈_ : A → RawPred A ℓ → Type _
x ∈ P = P x

_∉_ : A → RawPred A ℓ → Type _
x ∉ P = ¬ x ∈ P

------------------------------------------------------------------------
-- Subset relations

infix 6 _⊆_ _⊇_ _⊈_ _⊉_ _⊂_ _⊃_ _⊄_ _⊅_

_⊆_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊆ Q = ∀ {x} → x ∈ P → x ∈ Q

_⊇_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊇ Q = Q ⊆ P

_⊈_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊈ Q = ¬ (P ⊆ Q)

_⊉_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊉ Q = ¬ (P ⊇ Q)

_⊂_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊂ Q = P ⊆ Q × Q ⊈ P

_⊃_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊃ Q = Q ⊂ P

_⊄_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊄ Q = ¬ (P ⊂ Q)

_⊅_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⊅ Q = ¬ (P ⊃ Q)

------------------------------------------------------------------------
-- Properties of sets

infix 10 Satisfiable Universal IUniversal

-- Emptiness - no element satisfies P.

Empty : RawPred A ℓ → Type _
Empty P = ∀ x → x ∉ P

-- Satisfiable - at least one element satisfies P.

Satisfiable : RawPred A ℓ → Type _
Satisfiable {A = A} P = ∃[ x ∈ A ] x ∈ P

syntax Satisfiable P = ∃⟨ P ⟩

-- Universality - all elements satisfy P.

Universal : RawPred A ℓ → Type _
Universal P = ∀ x → x ∈ P

syntax Universal P = Π[ P ]

-- Implicit universality - all elements satisfy P.

IUniversal : RawPred A ℓ → Type _
IUniversal P = ∀ {x} → x ∈ P

syntax IUniversal P = ∀[ P ]

-- Decidability - it is possible to determine if an arbitrary element
-- satisfies P.

Decidable : RawPred A ℓ → Type _
Decidable P = ∀ x → Dec (P x)

-- Disjointness - Any element satisfying both P and Q is contradictory.

_⊃⊂_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
_⊃⊂_ P Q = ∀ {x} → x ∈ P → x ∈ Q → ⊥

-- Positive version of non-disjointness, dual to inclusion.

_≬_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
_≬_ {A = A} P Q = ∃[ x ∈ A ] x ∈ P × x ∈ Q

------------------------------------------------------------------------
-- Operations on sets

infix 10 ⋃ ⋂
infixr 9 _⊢_
infixr 8 _⇒_
infixr 7 _∩_
infixr 6 _∪_
infix 5 _≬_

-- Complement.

∁ : RawPred A ℓ → RawPred A ℓ
∁ P = λ x → x ∉ P

-- Implication.

_⇒_ : RawPred A ℓ₁ → RawPred A ℓ₂ → RawPred A _
P ⇒ Q = λ x → x ∈ P → x ∈ Q

-- Union.

_∪_ : RawPred A ℓ₁ → RawPred A ℓ₂ → RawPred A _
P ∪ Q = λ x → x ∈ P ⊎ x ∈ Q

-- Intersection.

_∩_ : RawPred A ℓ₁ → RawPred A ℓ₂ → RawPred A _
P ∩ Q = λ x → x ∈ P × x ∈ Q

-- Infinitary union.

⋃ : ∀ {i} (I : Type i) → (I → RawPred A ℓ) → RawPred A _
⋃ I P = λ x → Σ[ i ∈ I ] P i x

syntax ⋃ I (λ i → P) = ⋃[ i ∶ I ] P

-- Infinitary intersection.

⋂ : ∀ {i} (I : Type i) → (I → RawPred A ℓ) → RawPred A _
⋂ I P = λ x → (i : I) → P i x

syntax ⋂ I (λ i → P) = ⋂[ i ∶ I ] P

-- Preimage.

_⊢_ : (A → B) → RawPred B ℓ → RawPred A ℓ
f ⊢ P = λ x → P (f x)

------------------------------------------------------------------------
-- Preservation under operations

_Preserves_⟶_ : (A → B) → RawPred A ℓ₁ → RawPred B ℓ₂ → Type _
f Preserves P ⟶ Q = P ⊆ f ⊢ Q

_Preserves_ : (A → A) → RawPred A ℓ → Type _
f Preserves P = f Preserves P ⟶ P

-- A binary variant of _Preserves_⟶_.

_Preserves₂_⟶_⟶_ : (A → B → C) → RawPred A ℓ₁ → RawPred B ℓ₂ → RawPred C ℓ → Type _
_∙_ Preserves₂ P ⟶ Q ⟶ R = ∀ {x y} → x ∈ P → y ∈ Q → x ∙ y ∈ R

_Preserves₂_ : (A → A → A) → RawPred A ℓ → Type _
_∙_ Preserves₂ P = _∙_ Preserves₂ P ⟶ P ⟶ P

------------------------------------------------------------------------
-- Logical equivalence

_⇔_ : RawPred A ℓ₁ → RawPred A ℓ₂ → Type _
P ⇔ Q = Π[ P ⇒ Q ∩ Q ⇒ P ]

------------------------------------------------------------------------
-- Predicate combinators

-- These differ from the set operations above, as the carrier set of the
-- resulting predicates are not the same as the carrier set of the
-- component predicates.

infixr  2 _⟨×⟩_
infixr  2 _⟨⊙⟩_
infixr  1 _⟨⊎⟩_
infixr  0 _⟨→⟩_
infixl  9 _⟨·⟩_
infix  10 _~
infixr  9 _⟨∘⟩_
infixr  2 _//_ _\\_

-- Product.

_⟨×⟩_ : RawPred A ℓ₁ → RawPred B ℓ₂ → RawPred (A × B) _
(P ⟨×⟩ Q) (x , y) = x ∈ P × y ∈ Q

-- Sum over one element.

_⟨⊎⟩_ : RawPred A ℓ → RawPred B ℓ → RawPred (A ⊎ B) _
P ⟨⊎⟩ Q = rec P Q

-- Sum over two elements.

_⟨⊙⟩_ : RawPred A ℓ₁ → RawPred B ℓ₂ → RawPred (A × B) _
(P ⟨⊙⟩ Q) (x , y) = x ∈ P ⊎ y ∈ Q

-- Implication.

_⟨→⟩_ : RawPred A ℓ₁ → RawPred B ℓ₂ → RawPred (A → B) _
(P ⟨→⟩ Q) f = f Preserves P ⟶ Q

-- Product.

_⟨·⟩_ : (P : RawPred A ℓ₁) (Q : RawPred B ℓ₂) →
        (P ⟨×⟩ (P ⟨→⟩ Q)) ⊆ Q ∘ uncurry (flip _$_)
(P ⟨·⟩ Q) (p , f) = f p

-- Converse.

_~ : RawPred (A × B) ℓ → RawPred (B × A) ℓ
P ~ = P ∘ λ { (x , y) → y , x }

-- Composition.

_⟨∘⟩_ : RawPred (A × B) ℓ₁ → RawPred (B × C) ℓ₂ → RawPred (A × C) _
_⟨∘⟩_ {B = B} P Q (x , z) = ∃[ y ∈ B ] (x , y) ∈ P × (y , z) ∈ Q

-- Post-division.

_//_ : RawPred (A × C) ℓ₁ → RawPred (B × C) ℓ₂ → RawPred (A × B) _
(P // Q) (x , y) = Q ∘ (y ,_) ⊆ P ∘ (x ,_)

-- Pre-division.

_\\_ : RawPred (A × C) ℓ₁ → RawPred (A × B) ℓ₂ → RawPred (B × C) _
P \\ Q = (P ~ // Q ~) ~


module Structure.Category.NaturalTransformation where

open import Functional           using () renaming (id to idᶠⁿ)
open import Functional.Dependent using () renaming (_∘_ to _∘ᶠⁿ_)
import      Lvl
open import Logic
open import Logic.Predicate
open import Structure.Category
open import Structure.Category.Functor
open import Structure.Category.Proofs
open import Structure.Categorical.Properties
open import Structure.Setoid
open import Syntax.Function
open import Type

open Category.ArrowNotation ⦃ … ⦄
open Category ⦃ … ⦄ hiding (identity)
open CategoryObject ⦃ … ⦄
open Functor ⦃ … ⦄

module _
  {ℓₗₒ ℓₗₘ ℓₗₑ ℓᵣₒ ℓᵣₘ ℓᵣₑ}
  {Cₗ : CategoryObject{ℓₗₒ}{ℓₗₘ}{ℓₗₑ}}
  {Cᵣ : CategoryObject{ℓᵣₒ}{ℓᵣₘ}{ℓᵣₑ}}
  where

  private instance _ = Cₗ
  private instance _ = Cᵣ

  module _ (([∃]-intro F₁ ⦃ functor₁ ⦄) ([∃]-intro F₂ ⦃ functor₂ ⦄) : Cₗ →ᶠᵘⁿᶜᵗᵒʳ Cᵣ) where
    -- A natural transformation is a family of morphisms on
    record NaturalTransformation(η : ∀(x) → (F₁(x) ⟶ F₂(x))) : Type{Lvl.of(Type.of(Cₗ)) Lvl.⊔ Lvl.of(Type.of(Cᵣ))} where
      constructor intro
      field natural : ∀{x y}{f : x ⟶ y} → (η(y) ∘ map(f) ≡ map(f) ∘ η(x))

    record NaturalIsomorphism(η : ∀(x) → (F₁(x) ⟶ F₂(x))) : Type{Lvl.of(Type.of(Cₗ)) Lvl.⊔ Lvl.of(Type.of(Cᵣ))} where -- TODO: Consider defining this by two natural tranformations instead
      constructor intro
      field
        ⦃ naturalTransformation ⦄ : NaturalTransformation(η)
        ⦃ components-isomorphism ⦄ : ∀{x} → Morphism.Isomorphism{Morphism = Morphism ⦃ Cᵣ ⦄}(_∘_)(id) (η(x))

    _→ᴺᵀ_ = ∃(NaturalTransformation)
    _↔ᴺᵀ_ = ∃(NaturalIsomorphism)

  module _ (F₁ F₂ : Cₗ →ᶠᵘⁿᶜᵗᵒʳ Cᵣ) where
    open NaturalIsomorphism ⦃ … ⦄

    NaturalIsomorphism-inverse : ∀{η} → ⦃ ni : NaturalIsomorphism(F₁)(F₂)(η) ⦄ → NaturalIsomorphism(F₂)(F₁)(x ↦ Morphism.inv(_∘_)(id) (η(x))) -- TODO: Should not be an instance parameter
    NaturalTransformation.natural (NaturalIsomorphism.naturalTransformation NaturalIsomorphism-inverse) {x} {y} {f} = a where
      postulate a : ∀{a} → a -- TODO: Prove, and also move natural isomorphisms to a new file
    NaturalIsomorphism.components-isomorphism (NaturalIsomorphism-inverse {η}) {x} = inverse-isomorphism category (η(x))

  module _ {F₁ F₂ : Cₗ →ᶠᵘⁿᶜᵗᵒʳ Cᵣ} where
    open NaturalIsomorphism ⦃ … ⦄

    invᴺᵀ : (F₁ ↔ᴺᵀ F₂) → (F₂ ↔ᴺᵀ F₁)
    invᴺᵀ ([∃]-intro _) = [∃]-intro _ ⦃ NaturalIsomorphism-inverse F₁ F₂ ⦄


module Reduction where

open import Prelude
open import Lambda
open import Subst
open import Trans

infix 10 _⟶β_

data _⟶β_ : forall {Γ τ} -> (t u : Term Γ τ) -> Set where
  β   : forall {Γ σ τ}{t : Term (Γ , σ) τ} Δ {u : Term (Γ ++ Δ) σ} ->
        (ƛ t) ↑ Δ • u ⟶β t ↑ Δ / [ Δ ⟵ u ]
  wk⟶ : forall {Γ σ τ}{t₁ t₂ : Term Γ τ} ->
        t₁ ⟶β t₂ -> wk {σ = σ} t₁ ⟶β wk t₂
  •⟶L : forall {Γ σ τ}{t₁ t₂ : Term Γ (σ ⟶ τ)}{u : Term Γ σ} ->
        t₁ ⟶β t₂ -> t₁ • u ⟶β t₂ • u
  •⟶R : forall {Γ σ τ}{t : Term Γ (σ ⟶ τ)}{u₁ u₂ : Term Γ σ} ->
        u₁ ⟶β u₂ -> t • u₁ ⟶β t • u₂
  ƛ⟶  : forall {Γ σ τ}{t₁ t₂ : Term (Γ , σ) τ} ->
        t₁ ⟶β t₂ -> ƛ t₁ ⟶β ƛ t₂

_⟶β*_ : {Γ : Ctx}{τ : Type}(x y : Term Γ τ) -> Set
x ⟶β* y = [ _⟶β_ ]* x y

↑⟶β : {Γ : Ctx}(Δ : Ctx){τ : Type}{t u : Term Γ τ} ->
         t ⟶β u ->  t ↑ Δ ⟶β u ↑ Δ
↑⟶β ε       r = r
↑⟶β (Δ , σ) r = wk⟶ (↑⟶β Δ r)


module interactiveProgramsAgdaUnsized where

open import NativeIO
open import Function

record IOInterface : Set₁ where
  field  Command   :  Set
         Response  :  (c : Command) → Set

open IOInterface public

mutual
  record IO (I : IOInterface)  (A : Set) : Set where
     coinductive
     constructor delay
     field force : IO' I A

  data IO' (I : IOInterface)  (A : Set) : Set where
    do'     : (c : Command I) (f : Response I c → IO I A)  → IO' I A
    return' : (a : A)                                       → IO' I A

open IO public

module _ {I : IOInterface} (let C = Command I) (let R = Response I) where
  do  : ∀{A} (c : C) (f : R c → IO I A) → IO I A
  force (do c f) = do' c f

  return : ∀{A} (a : A) → IO I A
  force (return a) = return' a

  infixl 2 _>>=_

  _>>=_ :  ∀{A B} (m : IO I A) (k : A → IO I B) → IO I B
  force (m >>= k) with force m
  ... | do' c f   = do' c λ x → f x >>= k
  ... | return' a = force (k a)

  {-# NON_TERMINATING #-}
  translateIO : ∀ {A} (tr : (c : C) → NativeIO (R c)) → IO I A → NativeIO A
  translateIO tr m = case (force m) of λ
     {  (do' c f)    →  (tr c) native>>= λ r → translateIO tr (f r)
     ;  (return' a)  →  nativeReturn a
     }

data ConsoleCommand : Set where
  getLine   :  ConsoleCommand
  putStrLn  :  String → ConsoleCommand

ConsoleResponse : ConsoleCommand → Set
ConsoleResponse  getLine      =  String
ConsoleResponse (putStrLn s)  =  Unit

ConsoleInterface : IOInterface
Command ConsoleInterface  = ConsoleCommand
Response ConsoleInterface = ConsoleResponse

IOConsole : Set → Set
IOConsole = IO ConsoleInterface

translateIOConsoleLocal : (c : ConsoleCommand) → NativeIO (ConsoleResponse c)
translateIOConsoleLocal (putStrLn s)  =  nativePutStrLn s
translateIOConsoleLocal getLine       =  nativeGetLine

translateIOConsole : {A : Set} → IOConsole A → NativeIO A
translateIOConsole = translateIO translateIOConsoleLocal

{-# NON_TERMINATING #-}
cat   :  IOConsole Unit
force cat =  do' getLine λ{ line →
             do (putStrLn line)   λ _ →
             cat
             }

mutual
  cat' :  IOConsole Unit
  force cat' =  do' getLine λ{ line →
                cat'' line}

  cat'' : String → IOConsole Unit
  force (cat'' line) = do' (putStrLn line)   λ _ →
                       cat'

main : NativeIO Unit
main = translateIOConsole cat


{-# OPTIONS --cubical --safe #-}
module Cubical.HITs.Sn.Base where

open import Cubical.HITs.Susp
open import Cubical.Data.Nat
open import Cubical.Data.NatMinusOne
open import Cubical.Data.Empty
open import Cubical.Foundations.Prelude

S₊ : ℕ₋₁ → Type₀
S₊ neg1 = ⊥
S₊ (suc n) = Susp (S₊ n)

S : ℕ → Type₀
S n = S₊ (ℕ→ℕ₋₁ n)


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

module TypeEquivCat where

-- We will define a rig category whose objects are types and whose
-- morphisms are type equivalences; and where the equivalence of
-- morphisms ≋ is extensional

open import Level renaming (zero to lzero; suc to lsuc)

open import Data.Empty using (⊥)
open import Data.Unit using (⊤)
open import Data.Sum using (_⊎_)
open import Data.Product using (_,_; _×_; uncurry)
open import Data.Fin using (Fin; zero; suc) -- needed for distributivity

import Relation.Binary.PropositionalEquality as P
  using (sym)

open import Categories.Category using (Category)
open import Categories.Groupoid using (Groupoid)
open import Categories.Monoidal using (Monoidal)
open import Categories.Monoidal.Helpers using (module MonoidalHelperFunctors)
open import Categories.Bifunctor using (Bifunctor)
open import Categories.NaturalIsomorphism using (NaturalIsomorphism)
open import Categories.Monoidal.Braided using (Braided)
open import Categories.Monoidal.Symmetric using (Symmetric)
open import Categories.RigCategory
  using (RigCategory; module BimonoidalHelperFunctors)

open import Equiv
  using (_≃_; id≃; sym≃; _●_; _⊎≃_; _×≃_)
open import EquivEquiv
  using (_≋_; eq; id≋; sym≋; trans≋; ●-assoc; _◎_;
    linv≋; rinv≋; lid≋; rid≋; flip-sym≋)

-- list all explicitly, but these are all equivalences

open import TypeEquiv
  using (unite₊equiv; uniti₊equiv; unite₊′equiv; uniti₊′equiv;
         assocr₊equiv; assocl₊equiv;
         unite⋆equiv; uniti⋆equiv; unite⋆′equiv; uniti⋆′equiv;
         assocr⋆equiv; assocl⋆equiv; swap₊equiv;
         swap⋆equiv;
         distequiv; factorequiv;
         distlequiv; factorlequiv;
         distzequiv; factorzequiv;
         distzrequiv; factorzrequiv)

open import TypeEquivEquiv -- need them all!

-- convenience
one two : Fin 3
one = suc zero
two = suc (suc zero)

------------------------------------------------------------------------------
-- We show that types with type equivalences are a commutative rig
-- groupoid

-- First it is a category

TypeEquivCat : Category (lsuc lzero) lzero lzero
TypeEquivCat = record
  { Obj = Set
  ; _⇒_ = _≃_
  ; _≡_ = _≋_
  ; id = id≃
  ; _∘_ = _●_
  ; assoc = λ { {f = f} {g} {h} → ●-assoc {f = f} {g} {h} }
  ; identityˡ = lid≋
  ; identityʳ = rid≋
  ; equiv = record { refl = id≋ ; sym = sym≋ ; trans = trans≋ }
  ; ∘-resp-≡ = _◎_
  }

-- The category has inverses and hence a groupoid

TypeEquivGroupoid : Groupoid TypeEquivCat
TypeEquivGroupoid = record
  { _⁻¹ = sym≃
  ; iso = λ { {f = A≃B} → record
    { isoˡ = linv≋ A≃B
    ; isoʳ = rinv≋ A≃B
    } }
  }

-- The additive structure is monoidal

⊎-bifunctor : Bifunctor TypeEquivCat TypeEquivCat TypeEquivCat
⊎-bifunctor = record
  { F₀ = uncurry _⊎_
  ; F₁ = uncurry _⊎≃_
  ; identity = [id,id]≋id
  ; homomorphism = ⊎●≋●⊎
  ; F-resp-≡ = uncurry _⊎≋_
  }

module ⊎h = MonoidalHelperFunctors TypeEquivCat ⊎-bifunctor ⊥

0⊎x≡x : NaturalIsomorphism ⊎h.id⊗x ⊎h.x
0⊎x≡x = record
  { F⇒G = record
    { η = λ X → unite₊equiv {X zero}
    ; commute = λ f → unite₊-nat {f = f zero} }
  ; F⇐G = record
    { η = λ X → uniti₊equiv {X zero}
    ; commute = λ f →  uniti₊-nat {f = f zero} }
  ; iso = λ _ → record
    { isoˡ = linv≋ unite₊equiv
    ; isoʳ = rinv≋ unite₊equiv
    }
  }

x⊎0≡x : NaturalIsomorphism ⊎h.x⊗id ⊎h.x
x⊎0≡x = record
  { F⇒G = record
    { η = λ X → unite₊′equiv {X zero}
    ; commute = λ f → unite₊′-nat {f = f zero}
    }
  ; F⇐G = record
    { η = λ X → uniti₊′equiv {X zero}
    ; commute = λ f → uniti₊′-nat {f = f zero}
    }
  ; iso = λ X → record
    { isoˡ = linv≋ unite₊′equiv
    ; isoʳ = rinv≋ unite₊′equiv
    }
  }

[x⊎y]⊎z≡x⊎[y⊎z] : NaturalIsomorphism ⊎h.[x⊗y]⊗z ⊎h.x⊗[y⊗z]
[x⊎y]⊎z≡x⊎[y⊎z] = record
  { F⇒G = record
    { η = λ X → assocr₊equiv {X zero} {X one} {X two}
    ; commute = λ f → assocr₊-nat {f₀ = f zero} {f one} {f two}
    }
  ; F⇐G = record
    { η = λ X → assocl₊equiv {X zero} {X one} {X two}
    ; commute = λ f → assocl₊-nat {f₀ = f zero} {f one} {f two}
    }
  ; iso = λ X → record
    { isoˡ = linv≋ assocr₊equiv
    ; isoʳ = rinv≋ assocr₊equiv
    }
  }

CPM⊎ : Monoidal TypeEquivCat
CPM⊎ = record
  { ⊗ = ⊎-bifunctor
   ; id = ⊥
   ; identityˡ = 0⊎x≡x
   ; identityʳ = x⊎0≡x
   ; assoc = [x⊎y]⊎z≡x⊎[y⊎z]
   ; triangle = unite-assocr₊-coh
   ; pentagon = assocr₊-coh
   }

-- The multiplicative structure is also monoidal

×-bifunctor : Bifunctor TypeEquivCat TypeEquivCat TypeEquivCat
×-bifunctor = record
  { F₀ = uncurry _×_
  ; F₁ = uncurry _×≃_
  ; identity = id×id≋id
      -- the following would have unresolved metas without the extra precision
  ; homomorphism = λ { {f = (f , g)} {h , i} → ×●≋●× {f = f} {g} {h} {i}}
  ; F-resp-≡ = uncurry _×≋_
  }

module ×h = MonoidalHelperFunctors TypeEquivCat ×-bifunctor ⊤

1×y≡y : NaturalIsomorphism ×h.id⊗x ×h.x
1×y≡y = record
  { F⇒G = record
    { η = λ X → unite⋆equiv {X zero}
    ; commute = λ f → unite⋆-nat {f = f zero}
    }
  ; F⇐G = record
    { η = λ X → uniti⋆equiv {X zero}
    ; commute = λ f → uniti⋆-nat {f = f zero}
    }
  ; iso = λ X → record
    { isoˡ = linv≋ unite⋆equiv
    ; isoʳ = rinv≋ unite⋆equiv
    }
  }

y×1≡y : NaturalIsomorphism ×h.x⊗id ×h.x
y×1≡y = record
  { F⇒G = record
    { η = λ X → unite⋆′equiv {X zero}
    ;  commute = λ f → unite⋆′-nat {f = f zero}
    }
  ; F⇐G = record
    { η = λ X → uniti⋆′equiv {X zero}
    ; commute = λ f → uniti⋆′-nat {f = f zero}
    }
  ; iso = λ X → record
    { isoˡ = linv≋ unite⋆′equiv
    ; isoʳ = rinv≋ unite⋆′equiv
    }
  }

[x×y]×z≡x×[y×z] : NaturalIsomorphism ×h.[x⊗y]⊗z ×h.x⊗[y⊗z]
[x×y]×z≡x×[y×z] = record
  { F⇒G = record
    { η = λ X → assocr⋆equiv {X zero} {X one} {X two}
    ; commute = λ f → assocr⋆-nat {f₀ = f zero} {f one} {f two}}
  ; F⇐G = record
    { η = λ X → assocl⋆equiv {X zero} {X one} {X two}
    ; commute = λ f → assocl⋆-nat {f₀ = f zero} {f one} {f two}}
  ; iso = λ X → record
    { isoˡ = linv≋ assocr⋆equiv
    ; isoʳ = rinv≋ assocr⋆equiv }
  }

CPM× : Monoidal TypeEquivCat
CPM× = record
  { ⊗ = ×-bifunctor
  ; id = ⊤
  ; identityˡ = 1×y≡y
  ; identityʳ = y×1≡y
  ; assoc = [x×y]×z≡x×[y×z]
  ; triangle = unite-assocr⋆-coh
  ; pentagon = assocr⋆-coh
  }

-- The monoidal structures are symmetric

x⊎y≈y⊎x : NaturalIsomorphism ⊎h.x⊗y ⊎h.y⊗x
x⊎y≈y⊎x = record
  { F⇒G = record
    { η = λ X → swap₊equiv {X zero} {X (suc zero)}
    ; commute = λ f → swap₊-nat {f = f zero} {f (suc zero)}
    }
  ; F⇐G = record
    { η = λ X → sym≃ (swap₊equiv {X zero} {X (suc zero)})
    ; commute = λ f → swap₊-nat {f = f (suc zero)} {f zero}
    }
  ; iso = λ X → record
    { isoˡ = linv≋ swap₊equiv
    ; isoʳ = rinv≋ swap₊equiv
    }
  }

BM⊎ : Braided CPM⊎
BM⊎ = record
  { braid = x⊎y≈y⊎x
  ; unit-coh = unite₊l-coh
  ; hexagon₁ = assocr₊-swap₊-coh
  ; hexagon₂ = assocl₊-swap₊-coh
  }

x×y≈y×x : NaturalIsomorphism ×h.x⊗y ×h.y⊗x
x×y≈y×x = record
  { F⇒G = record
    { η = λ X → swap⋆equiv
    ; commute = λ _ → swap⋆-nat
    }
  ; F⇐G = record
    { η = λ X → sym≃ swap⋆equiv
    ; commute = λ f → swap⋆-nat
    }
  ; iso = λ X → record
    { isoˡ = linv≋ swap⋆equiv
    ; isoʳ = rinv≋ swap⋆equiv
    }
  }

BM× : Braided CPM×
BM× = record
  { braid = x×y≈y×x
  ; unit-coh = unite⋆l-coh
  ; hexagon₁ = assocr⋆-swap⋆-coh
  ; hexagon₂ = assocl⋆-swap⋆-coh
  }

SBM⊎ : Symmetric BM⊎
SBM⊎ = record { symmetry = linv≋ swap₊equiv }

SBM× : Symmetric BM×
SBM× = record { symmetry = linv≋ swap⋆equiv }

-- And finally the multiplicative structure distributes over the
-- additive one

module r = BimonoidalHelperFunctors BM⊎ BM×

x⊗[y⊕z]≡[x⊗y]⊕[x⊗z] : NaturalIsomorphism r.x⊗[y⊕z] r.[x⊗y]⊕[x⊗z]
x⊗[y⊕z]≡[x⊗y]⊕[x⊗z] = record
  { F⇒G = record
    { η = λ X → distlequiv {X zero} {X one} {X two}
    ; commute = λ f → distl-nat {f = f zero} {f one} {f two}
    }
  ; F⇐G = record
    { η = λ X → factorlequiv {X zero} {X one} {X two}
    ; commute = λ f → factorl-nat {f = f zero} {f one} {f two}
    }
  ; iso = λ X → record { isoˡ = linv≋ distlequiv
                       ; isoʳ = rinv≋ distlequiv }
  }

[x⊕y]⊗z≡[x⊗z]⊕[y⊗z] : NaturalIsomorphism r.[x⊕y]⊗z r.[x⊗z]⊕[y⊗z]
[x⊕y]⊗z≡[x⊗z]⊕[y⊗z] = record
  { F⇒G = record
    { η = λ X → distequiv
    ; commute = λ f → dist-nat
    }
  ; F⇐G = record
    { η = λ X → factorequiv
    ; commute = λ f → factor-nat
    }
  ; iso = λ X → record { isoˡ = linv≋ distequiv
                       ; isoʳ = rinv≋ distequiv }
  }

x⊗0≡0 : NaturalIsomorphism r.x⊗0 r.0↑
x⊗0≡0 = record
  { F⇒G = record
    { η = λ X → distzrequiv {X zero}
    ; commute = λ f → distzr-nat {f = f zero}
    }
  ; F⇐G = record
    { η = λ X → factorzrequiv {X zero}
    ; commute = λ f → factorzr-nat {f = f zero}
    }
  ; iso = λ X → record
    { isoˡ = linv≋ distzrequiv
    ; isoʳ = rinv≋ distzrequiv
    }
  }

0⊗x≡0 : NaturalIsomorphism r.0⊗x r.0↑
0⊗x≡0 = record
  { F⇒G = record
    { η = λ X → distzequiv {X zero}
    ; commute = λ f → distz-nat {f = f zero}
    }
  ; F⇐G = record
    { η = λ X → factorzequiv {X zero}
    ; commute = λ f → factorz-nat {f = f zero}
    }
  ; iso = λ X → record
    { isoˡ = linv≋ (distzequiv {X zero})
    ; isoʳ = rinv≋ distzequiv
    }
  }

TERig : RigCategory SBM⊎ SBM×
TERig = record
  -- new combinators
  { distribₗ = x⊗[y⊕z]≡[x⊗y]⊕[x⊗z]
  ; distribᵣ = [x⊕y]⊗z≡[x⊗z]⊕[y⊗z]
  ; annₗ = 0⊗x≡0
  ; annᵣ = x⊗0≡0
  -- derived identities which are ``coherent'' in that the
  -- two natural programs, using the new combinators and
  -- the underlying ones of the symmetric bimonoidal groupoid
  -- are in fact to be identified.
  ; laplazaI = λ {X} → A×[B⊎C]≃[A×C]⊎[A×B] {X zero} {X one} {X two}
  ; laplazaII = λ {X} → [A⊎B]×C≃[C×A]⊎[C×B] {X zero} {X one} {X two}
  ; laplazaIV = λ {X} → [A⊎B⊎C]×D≃[A×D⊎B×D]⊎C×D {X zero} {X (suc zero)}
                          {X (suc (suc zero))} {X (suc (suc (suc zero)))}
  ; laplazaVI = λ {X} → A×B×[C⊎D]≃[A×B]×C⊎[A×B]×D {X zero} {X (suc zero)}
                          {X (suc (suc zero))} {X (suc (suc (suc zero)))}
  ; laplazaIX = λ {X} → [A⊎B]×[C⊎D]≃[[A×C⊎B×C]⊎A×D]⊎B×D {X zero} {X (suc zero)}
                          {X (suc (suc zero))} {X (suc (suc (suc zero)))}
  ; laplazaX = 0×0≃0
  ; laplazaXI = λ {X} → 0×[A⊎B]≃0 {X zero} {X (suc zero)}
  ; laplazaXIII = 0×1≃0
  ; laplazaXV = λ {X} → A×0≃0 {X zero}
  ; laplazaXVI = λ {X} → 0×A×B≃0 {X zero} {X (suc zero)}
  ; laplazaXVII = λ {X} → A×0×B≃0 {X zero} {X (suc zero)}
  ; laplazaXIX = λ {X} → A×[0+B]≃A×B {X zero} {X (suc zero)}
  ; laplazaXXIII = λ {X} → 1×[A⊎B]≃A⊎B {X zero} {X (suc zero)}
  }

-- Notes from Laplaza, 72
-- All of 2, 9, 10, 15
-- one of each of {1,3}, {4,5}, {6,7}, {9, 12},{13,,14},
--   {19,20,21,22}, {23,24}
-- two-of-three of {16,17,18}
--
-- for natural isos
-- α : A × (B × C) → (A × B) × C
-- γ : A × B → B × A
-- λ : U × A → A  (so that U is 1)
-- ρ : A × U → A
-- λ* : N × A → N (so that N is 0)
-- ρ* : A × n → N
-- and ' versions for ⊕
-- as well as natural monomorphisms
-- δ : A × (B ⊎ C) → (A × B) ⊎ (A × C)
-- δ# : (A ⊎ B) × C → (A × C) ⊎ (B × C)

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


open import Oscar.Prelude
open import Oscar.Class.IsPrefunctor
open import Oscar.Class.Smap
open import Oscar.Class.Surjection
open import Oscar.Class.Transitivity

module Oscar.Class.Prefunctor where

record Prefunctor 𝔬₁ 𝔯₁ ℓ₁ 𝔬₂ 𝔯₂ ℓ₂ : Ø ↑̂ (𝔬₁ ∙̂ 𝔯₁ ∙̂ ℓ₁ ∙̂ 𝔬₂ ∙̂ 𝔯₂ ∙̂ ℓ₂) where
  constructor ∁
  field
    {𝔒₁} : Ø 𝔬₁
    _∼₁_ : 𝔒₁ → 𝔒₁ → Ø 𝔯₁
    _∼̇₁_ : ∀ {x y} → x ∼₁ y → x ∼₁ y → Ø ℓ₁
    _↦₁_ : Transitivity.type _∼₁_
    {𝔒₂} : Ø 𝔬₂
    _∼₂_ : 𝔒₂ → 𝔒₂ → Ø 𝔯₂
    _∼̇₂_ : ∀ {x y} → x ∼₂ y → x ∼₂ y → Ø ℓ₂
    _↦₂_ : Transitivity.type _∼₂_
    {μ} : Surjection.type 𝔒₁ 𝔒₂
    prefunctor-smap : Smap.type _∼₁_ _∼₂_ μ μ
    ⦃ `IsPrefunctor ⦄ : IsPrefunctor _∼₁_ _∼̇₁_ _↦₁_ _∼₂_ _∼̇₂_ _↦₂_ prefunctor-smap


module Terminal where

open import Base
open import Category
open import Unique
open import Dual
import Iso

module Term (ℂ : Cat) where

  private ℂ' = η-Cat ℂ
  private open module C	= Cat ℂ'
  private open module U = Uniq ℂ'
  private open module I = Iso ℂ'

  Terminal : (A : Obj) -> Set1
  Terminal A = (B : Obj) -> ∃! \(f : B ─→ A) -> True

  toTerminal : {A B : Obj} -> Terminal A -> B ─→ A
  toTerminal term = getWitness (term _)

  terminalIso : {A B : Obj} -> Terminal A -> Terminal B -> A ≅ B
  terminalIso tA tB = iso (toTerminal tB)
			   (toTerminal tA)
			   p q
    where
      p : toTerminal tB ∘ toTerminal tA == id
      p = witnessEqual (tB _) tt tt

      q : toTerminal tA ∘ toTerminal tB == id
      q = witnessEqual (tA _) tt tt

module Init (ℂ : Cat) = Term (η-Cat ℂ op)
    renaming
      ( Terminal    to Initial
      ; toTerminal  to fromInitial
      ; terminalIso to initialIso
      )