{-# OPTIONS --without-K #-}
module factorial where

open import Type
open import Data.Nat.NP
open import Data.Product
open import Data.Sum.NP
open import Data.Zero
open import Data.Maybe
open import Data.One using (𝟙)
open import Data.Fin.NP using (Fin; Fin′; zero; suc;
                               inject₁; inject!;
                               Fin▹ℕ; ℕ▹Fin; [zero:_,suc:_])
open import Function.NP
open import Function.Extensionality
open import HoTT
open Equivalences
open import Type.Identities
open import Algebra.FunctionProperties.Eq
{-
open import Function.Related as FR
open import Function.Related.TypeIsomorphisms.NP
import Function.Inverse.NP as I
open I using (_↔_; inverses; module Inverse) renaming (_$₁_ to to; _$₂_ to from)
open import Relation.Binary.Product.Pointwise
open import Relation.Binary.Sum
-}
open import Relation.Binary.PropositionalEquality.NP
  renaming (!_ to ¡_)
open ≡-Reasoning

-- n ! ≡ product [1..n]

module v1 where
  _! : ℕ → ℕ
  zero  ! = 1
  suc n ! = (1 + n) * n !

  _!★ : ℕ → ★
  zero  !★ = 𝟙
  suc n !★ = Fin (1 + n) × n !★

  module _ {{_ : UA}} where
    !★≡Fin! : ∀ {n} → n !★ ≡ Fin (n !)
    !★≡Fin! {zero}  = ¡ Fin1≡𝟙
    !★≡Fin! {suc n} = ×= idp !★≡Fin! ∙ Fin-×-*

module v2 where
  _! : ℕ → ℕ
  _+1! : ℕ → ℕ

  zero  ! = 1
  suc n ! = n +1!

  n +1! = n ! + n * n !

  _!★ : ℕ → ★
  zero  !★ = 𝟙
  suc n !★ = n !★ ⊎ Fin n × n !★

  1+_!★ : ℕ → ★
  1+ n !★ = n !★ ⊎ Fin n × n !★

  module _ {{_ : UA}} where
    !★≡Fin! : ∀ {n} → n !★ ≡ Fin (n !)
    !★≡Fin! {zero}  = ¡ Fin1≡𝟙
    !★≡Fin! {suc n} = ⊎= !★≡Fin! (×= idp !★≡Fin! ∙ Fin-×-*)
                    ∙ Fin-⊎-+

productFin : ∀ n → (Fin n → ℕ) → ℕ
productFin zero    f = 1
productFin (suc n) f = f zero * productFin n (f ∘ suc)

product[0…_] : ℕ → (ℕ → ℕ) → ℕ
product[0… n ] f = productFin n (f ∘ Fin▹ℕ)
{-
product[0… zero  ] f = 1
product[0… suc n ] f = f zero * product[0… n ] (f ∘ suc)
-}

product[1…_] : ℕ → (ℕ → ℕ) → ℕ
product[1… zero  ] f = 1
product[1… suc n ] f = f 1 * product[1… n ] (f ∘ suc)

product[_to_] : ℕ → ℕ → (ℕ → ℕ) → ℕ
product[ start to stop ] f = product[0… stop ∸ start ] (f ∘ _+_ start)

{-
data Bnd (l : ℕ) : (h : ℕ) → Set where
  low : Bnd l (suc l)
  suc : ∀ {h} → Bnd l h → Bnd l (suc h)

Bnd▹ℕ : {l h : ℕ} → Bnd l h → ℕ
Bnd▹ℕ {l} low = l
Bnd▹ℕ (suc x) = suc (Bnd▹ℕ x)

productBnd : ∀ {l h} → l ≤ h → (Bnd l h → ℕ) → ℕ
productBnd z≤n f = {!!}
productBnd (s≤s p) f = {!!}

productBnd p f = {!f low * productBnd l h (f ∘ suc)!}
-}

test-product[1…3] : product[1… 3 ] ≡ (λ f → f 1 * (f 2 * (f 3 * 1)))
test-product[1…3] = idp

open v1
{-
adequate1 : ∀ n → n ! ≡ product[1… n ] id
adequate1 zero = ≡.refl
adequate1 (suc n) rewrite adequate1 n = {!!}
  Π{x∈1…n}x + n * Π{x∈1…n}x

  (1 + n) * Π{x∈1…n}x

  Π{x∈1…n}1+x
  -}

Π[0…_-1] : ℕ → (ℕ → ℕ) → ★
Π[0… n -1] f = Π (Fin n) (Fin ∘ f ∘ Fin▹ℕ)

Π[0…_] : ℕ → (ℕ → ℕ) → ★
Π[0… n ] = Π[0… suc n -1]

{-
Π[1…_] : ℕ → (ℕ → ℕ) → ★
Π[1… 0     ] f = 𝟙
Π[1… suc n ] f = {!!}
-}

Π[0…_-1]′ : ℕ → (ℕ → ℕ) → ★
Π[0… zero  -1]′ f = 𝟙
Π[0… suc n -1]′ f = Fin (f 0) × Π[0… n -1]′ (f ∘ suc)

Π[0…_]′ : ℕ → (ℕ → ℕ) → ★
Π[0… n ]′ = Π[0… suc n -1]′

{-
Π[1…_]′ : ℕ → (ℕ → ℕ) → ★
Π[1… zero  ]′ f = 𝟙
Π[1… suc n ]′ f = Fin (f 1) × Π[1… n ]′ (f ∘ suc)
-}

[zero:,suc:] = [zero:_,suc:_]
syntax [zero:,suc:] z (λ y → s) = [zero: z ,suc y :→ s ]

Π! : ℕ → ★
Π! n = Π[0… n ] suc

↺ : ℕ → ★
↺ n = Fin n → Fin n

Π!↺ : ∀ {n} → Π! n → ↺ (suc n)
Π!↺ {n} f = inject! ∘ f

Π!↺-inj : ∀ {n}(f : Π! n) → Injective (Π!↺ f)
Π!↺-inj f {x} {y} e = {!!}

-- Move to Type.Identities
module _ {{_ : UA}}{{_ : FunExt}} where
  ΠMaybe : ∀ {A : ★} {F : Maybe A → ★} →
           Π (Maybe A) F ≡ (F nothing × Π A (F ∘ just))
  ΠMaybe {A} {F}
    = (Π (Maybe A) F)
    ≡⟨ Π≃ Maybe≃𝟙⊎ (maybe (λ _ → idp) idp) ⟩
      (Π (𝟙 ⊎ A) [inl: (λ _ → F nothing) ,inr: F ∘ just ])
    ≡⟨ dist-×-Π ⟩
      ((𝟙 → F nothing) × Π A (F ∘ just))
    ≡⟨ ×= 𝟙→A≡A idp ⟩
      (F nothing × Π A (F ∘ just))
    ∎

  ΠFin0≡𝟙 : ∀ {F : Fin 0 → ★} → Π (Fin 0) F ≡ 𝟙
  ΠFin0≡𝟙 = Π= Fin0≡𝟘 (λ()) ∙ Π𝟘-uniq₀ (λ ())

  ΠFin1+ : ∀ n {F : Fin (1 + n) → ★} →
           Π (Fin (1 + n)) F ≡ (F zero × Π (Fin n) (F ∘ suc))
  ΠFin1+ n {F}
    = Π (Fin (1 + n)) F
    ≡⟨ Π≃ Fin∘suc≃𝟙⊎Fin [zero: idp ,suc _ :→ idp ] ⟩
       Π (𝟙 ⊎ Fin n) [inl: (λ _ → F zero) ,inr: F ∘ suc ]
    ≡⟨ dist-×-Π[] ⟩
       ((𝟙 → F zero) × Π (Fin n) (F ∘ suc))
    ≡⟨ ×= 𝟙→A≡A idp ⟩
       (F zero × Π (Fin n) (F ∘ suc))
    ∎

  rot : ∀ {n} → Fin (suc n) → Fin (suc n)
  rot {n} = [zero: ℕ▹Fin n ,suc: inject₁ ]

  ΠFin1+' : ∀ n {F : Fin (1 + n) → ★} →
           Π (Fin (1 + n)) F ≡ (F (ℕ▹Fin n) × Π (Fin n) (F ∘ inject₁))
  ΠFin1+' n {F} =
     Π (Fin (1 + n)) F
    ≡⟨ Π≃ Fin∘suc≃𝟙⊎Fin (λ x → {!WRONG!}) ⟩
     Π (𝟙 ⊎ Fin n) [inl: (λ _ → F {!!}) ,inr: F ∘ {!!} ]
    ≡⟨ {!!} ⟩
     Π (𝟙 ⊎ Fin n) [inl: (λ _ → F (ℕ▹Fin n)) ,inr: F ∘ inject₁ ]
    ≡⟨ dist-×-Π[] ⟩
     ((𝟙 → F (ℕ▹Fin n)) × Π (Fin n) (F ∘ inject₁))
    ≡⟨ ×= 𝟙→A≡A idp ⟩
     (F (ℕ▹Fin n) × Π (Fin n) (F ∘ inject₁))
    ∎

{-
  ΠFin1≡F0 : {F : Fin 1 → ★} → Π (Fin 1) F ≡ F zero
  ΠFin1≡F0 = ΠFin1+ 0 ∙ ×= idp ΠFin0≡𝟙 ∙ A×𝟙≡A

  ΠFin2≡F0×F1 : {F : Fin 2 → ★} → Π (Fin 2) F ≡ (F zero × F (suc zero))
  ΠFin2≡F0×F1 = ΠFin1+ 1 ∙ ×= idp ΠFin1≡F0

  test-Π[0…-1] : ∀ {f} → Π[0… 0 -1] f ≡ 𝟙
  test-Π[0…-1] = ΠFin0≡𝟙

  test-Π[0…0] : ∀ {f} → Π[0… 0 ] f ≡ Fin (f zero)
  test-Π[0…0] = ΠFin1≡F0

  test-Π[0…1] : ∀ {f} → Π[0… 1 ] f ≡ (Fin (f zero) × Fin (f (suc zero)))
  test-Π[0…1] = ΠFin2≡F0×F1

  ΠFin1+ʳ′ : ∀ n (F : ℕ → ★) →
               Π (Fin (1 + n)) (F ∘ Fin▹ℕ) ≡ (F n × Π (Fin n) (F ∘ Fin▹ℕ))
  ΠFin1+ʳ′ n F
      = Π (Fin (1 + n)) (F ∘ Fin▹ℕ)
      ≡⟨ Π=′ (Fin (1 + n)) F∘Fin▹ℕ≗F' ⟩
        Π (Fin (1 + n)) F'
      ≡⟨ ΠFin1+ n ⟩
        (F' zero × Π (Fin n) (F' ∘ suc))
      ≡⟨by-definition⟩
        (F n × Π (Fin n) (F ∘ Fin▹ℕ))
      ∎
    where F' : (x : Fin (suc n)) → ★₀
          F' zero = F n
          F' (suc x) = F (Fin▹ℕ x)
          F∘Fin▹ℕ≗F' : F ∘ Fin▹ℕ ≗ F'
          F∘Fin▹ℕ≗F' zero = {!!}
          F∘Fin▹ℕ≗F' (suc x₂) = {!!}

{-Π (Fin (1 + n)) (F ∘ Fin▹ℕ)
            ≡⟨ {!!} ⟩
               Π (𝟙 ⊎ Fin n) {!!}
            ≡⟨ {!dist-×-Π!} ⟩
               (F n × Π (Fin n) (F ∘ Fin▹ℕ))
            ∎
-}

module _ {{_ : UA}}{{_ : FunExt}} where
  Π≡Π′[0…_-1] : ∀ n f → Π[0… n -1]′ f ≡ Π[0… n -1] f
  Π≡Π′[0… zero  -1] f = ¡ ΠFin0≡𝟙
  Π≡Π′[0… suc n -1] f = Π[0… 1 + n -1]′ f
                     ≡⟨by-definition⟩
                       (Fin (f 0) × Π[0… n -1]′ (f ∘ suc))
                     ≡⟨ ×= idp (Π≡Π′[0… n -1] (f ∘ suc)) ⟩
                       (Fin (f 0) × Π[0… n -1] (f ∘ suc))
                     ≡⟨ ¡ ΠFin1+ n ⟩
                       Π[0… 1 + n -1] f
                     ∎

  Π≡Π′[0…_] : ∀ n f → Π[0… n ]′ f ≡ Π[0… n ] f
  Π≡Π′[0… n ] = Π≡Π′[0… suc n -1]
-- (A → B ⊎ C)

-- TODO
-- adequate! : ∀ n → Fin (suc n) × Fin (n !) ≡ Π (Fin n) (Fin′ ∘ suc)

example : ∀ {n} → (2 + n)! ≡ (2 + n) * (1 + n)!
example = idp

module _ {{_ : UA}}{{_ : FunExt}} where

{-
  -- WRONG
  adequate! : ∀ {n} → Fin (n !) ≡ Π[0… n -1] suc
  adequate! {zero}  = {!¡ ΠFin1≡F0!}
  adequate! {suc n} = (Fin ((1 + n)!))
                    ≡⟨by-definition⟩
                      Fin ((1 + n) * n !)
                    ≡⟨ ¡ Fin-×-* ⟩
                      (Fin (1 + n) × Fin (n !))
                    ≡⟨ ×= idp adequate! ⟩
                      (Fin (1 + n) × Π[0… n -1] suc)
                    ≡⟨ {!ΠFin1+!} ⟩
                      (Fin 1 × Π[0… n -1] (_+_ 2))
                    ≡⟨ ¡ ΠFin1+ n ⟩
                      Π[0… n ] suc
                    ∎
                    -}

  adequate! : ∀ {n} → Fin (n !) ≡ Π[0… n ] suc
  adequate! {zero}  = ¡ ΠFin1≡F0
  adequate! {suc n} = (Fin ((1 + n)!))
                    ≡⟨by-definition⟩
                      Fin ((1 + n) * n !)
                    ≡⟨ ¡ Fin-×-* ⟩
                      (Fin (1 + n) × Fin (n !))
                    ≡⟨ ×= idp adequate! ⟩
                      (Fin (1 + n) × Π[0… n ] suc)
                    ≡⟨ ¡ ΠFin1+ʳ′ (suc n) _ ⟩
                      ((x : Fin (2 + n)) → Fin (suc (Fin▹ℕ x)))
                    ≡⟨ idp ⟩
                      Π[0… 1 + n ] suc
                    ∎

{-
  adequate! : ∀ {n} → Fin ((1 + n)!) ≡ Π[0… n -1] suc
  adequate! {zero}  = {!idp!}
  adequate! {suc n} = (Fin ((2 + n)!))
                    ≡⟨ {!!} ⟩
                      Fin ((1 + n) * (1 + n)!)
                    ≡⟨ ¡ Fin-×-* ⟩
                      (Fin (1 + n) × Fin ((1 + n)!))
                    ≡⟨ ×= idp adequate! ⟩
                      (Fin (1 + n) × Π[0… n -1] suc)
                    ≡⟨ ¡ (ΠFin1+ʳ′ n (Fin ∘ suc)) ⟩
                      Π[0… suc n -1] suc
                    ∎
-}
{-

HI: (n+1)! ≡ Π n suc

(n+2)!
(n+2) * (n+1)!
(n+2) * Π n suc
(n+1) * Π n suc + Π n suc

(2+n)*(n! * 1+n)
(n! * 1+n) + (1+n)*(n! * 1+n)

≡ Π n (2+)
≡ 1 × Π n (2+)

(2+n) * Π n suc
Π (1+n) suc

-- -}
-- -}
-- -}
-- -}
-- -}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Universe levels
------------------------------------------------------------------------

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

module Level where

-- Levels.

open import Agda.Primitive as Prim public
  using    (Level; _⊔_; Setω)
  renaming (lzero to zero; lsuc to suc)

-- Lifting.

record Lift {a} ℓ (A : Set a) : Set (a ⊔ ℓ) where
  constructor lift
  field lower : A

open Lift public

-- Synonyms

0ℓ : Level
0ℓ = zero

levelOfType : ∀ {a} → Set a → Level
levelOfType {a} _ = a

levelOfTerm : ∀ {a} {A : Set a} → A → Level
levelOfTerm {a} _ = a


module Issue488 where

open import Data.Product using (∃-syntax; -,_; _×_; _,_)
open import Relation.Nullary using (¬_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; trans)

module CounterExample where

  data Term : Set where
    A B C D : Term

  data _—→_ : (M N : Term) → Set where
    B—→C : B —→ C
    C—→B : C —→ B
    B—→A : B —→ A
    C—→D : C —→ D

  infix  2 _—↠_
  infix  1 begin_
  infixr 2 _—→⟨_⟩_
  infix  3 _∎

  data _—↠_ : Term → Term → Set where
    _∎ : ∀ M
      ---------
      → M —↠ M

    _—→⟨_⟩_ : ∀ L {M N}
      → L —→ M
      → M —↠ N
        ---------
      → L —↠ N

  begin_ : ∀ {M N}
    → M —↠ N
      ------
    → M —↠ N
  begin M—↠N = M—↠N

  diamond : ∀ {L M N}
    → ((L —→ M) × (L —→ N))
      -----------------------------
    → ∃[ P ] ((M —↠ P) × (N —↠ P))
  diamond (B—→A , B—→A) = -, ((A ∎) , (A ∎))
  diamond (C—→B , C—→B) = -, ((B ∎) , (B ∎))
  diamond (B—→C , B—→C) = -, ((C ∎) , (C ∎))
  diamond (C—→D , C—→D) = -, ((D ∎) , (D ∎))
  diamond (B—→C , B—→A) = -, ((begin C —→⟨ C—→B ⟩ B —→⟨ B—→A ⟩ A ∎) , (A ∎))
  diamond (C—→B , C—→D) = -, ((begin B —→⟨ B—→C ⟩ C —→⟨ C—→D ⟩ D ∎) , (D ∎))
  diamond (B—→A , B—→C) = -, ((A ∎) , (begin C —→⟨ C—→B ⟩ B —→⟨ B—→A ⟩ A ∎))
  diamond (C—→D , C—→B) = -, ((D ∎) , (begin B —→⟨ B—→C ⟩ C —→⟨ C—→D ⟩ D ∎))

  A—↠A : ∀ {P} → A —↠ P → P ≡ A
  A—↠A (.A ∎) = refl

  D—↠D : ∀ {P} → D —↠ P → P ≡ D
  D—↠D (.D ∎) = refl

  ¬confluence : ¬ (∀ {L M N}
    → ((L —↠ M) × (L —↠ N))
      -----------------------------
    → ∃[ P ] ((M —↠ P) × (N —↠ P)))
  ¬confluence confluence
    with confluence ( (begin B —→⟨ B—→A ⟩ A ∎)
                    , (begin B —→⟨ B—→C ⟩ C —→⟨ C—→D ⟩ D ∎) )
  ... | (P , (A—↠P , D—↠P))
    with trans (sym (A—↠A A—↠P)) (D—↠D D—↠P)
  ... | ()

module DeterministicImpliesDiamondPropertyAndConfluence where

  infix  2 _—↠_
  infix  1 begin_
  infixr 2 _—→⟨_⟩_
  infix  3 _∎

  postulate
    Term : Set
    _—→_ : Term → Term → Set

  postulate
    deterministic : ∀ {L M N}
      → L —→ M
      → L —→ N
      ------
      → M ≡ N

  data _—↠_ : Term → Term → Set where
    _∎ : ∀ M
      ---------
      → M —↠ M

    _—→⟨_⟩_ : ∀ L {M N}
      → L —→ M
      → M —↠ N
        -------
      → L —↠ N

  begin_ : ∀ {M N}
    → M —↠ N
      ------
    → M —↠ N
  begin M—↠N = M—↠N

  diamond : ∀ {L M N}
    → ((L —→ M) × (L —→ N))
      --------------------
    → ∃[ P ] ((M —↠ P) × (N —↠ P))
  diamond (L—→M , L—→N)
    rewrite deterministic L—→M L—→N = -, ((_ ∎) , (_ ∎))

  confluence : ∀ {L M N}
    → (L —↠ M)
    → (L —↠ N)
      --------------------
    → ∃[ P ] ((M —↠ P) × (N —↠ P))
  confluence {L} {.L} { N} (.L ∎) L—↠N = -, (L—↠N , (N ∎))
  confluence {L} { M} {.L} L—↠M (.L ∎) = -, ((M ∎) , L—↠M)
  confluence {L} { M} { N} (.L —→⟨ L—→M′ ⟩ M′—↠M) (.L —→⟨ L—→N′ ⟩ N′—↠N)
    rewrite deterministic L—→M′ L—→N′ = confluence M′—↠M N′—↠N


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

open import HoTT
open import groups.Pointed

module groups.Int where

abstract
  ℤ→ᴳ-η : ∀ {i} {G : Group i} (φ : ℤ-group →ᴳ G)
    → GroupHom.f φ ∼ Group.exp G (GroupHom.f φ 1)
  ℤ→ᴳ-η φ (pos 0) = GroupHom.pres-ident φ
  ℤ→ᴳ-η φ (pos 1) = idp
  ℤ→ᴳ-η {G = G} φ (pos (S (S n))) =
      GroupHom.pres-comp φ 1 (pos (S n))
    ∙ ap (Group.comp G (GroupHom.f φ 1)) (ℤ→ᴳ-η φ (pos (S n)))
    ∙ ! (Group.exp-+ G (GroupHom.f φ 1) 1 (pos (S n)))
  ℤ→ᴳ-η φ (negsucc 0) = GroupHom.pres-inv φ 1
  ℤ→ᴳ-η {G = G} φ (negsucc (S n)) =
      GroupHom.pres-comp φ -1 (negsucc n)
    ∙ ap2 (Group.comp G) (GroupHom.pres-inv φ 1) (ℤ→ᴳ-η φ (negsucc n))
    ∙ ! (Group.exp-+ G (GroupHom.f φ 1) -1 (negsucc n))

ℤ-idf-η : ∀ i → i == Group.exp ℤ-group 1 i
ℤ-idf-η = ℤ→ᴳ-η (idhom _)

ℤ→ᴳ-unicity : ∀ {i} {G : Group i}
  → (φ ψ : ℤ-group →ᴳ G)
  → GroupHom.f φ 1 == GroupHom.f ψ 1
  → GroupHom.f φ ∼ GroupHom.f ψ
ℤ→ᴳ-unicity {G = G} φ ψ p i =
  ℤ→ᴳ-η φ i ∙ ap (λ g₁ → Group.exp G g₁ i) p ∙ ! (ℤ→ᴳ-η ψ i)

ℤ→ᴳ-equiv-idf : ∀ {i} (G : Group i) → (ℤ-group →ᴳ G) ≃ Group.El G
ℤ→ᴳ-equiv-idf G = equiv (λ φ → GroupHom.f φ 1) (exp-hom G)
  (λ _ → idp) (λ φ → group-hom= $ λ= $ ! ∘ ℤ→ᴳ-η φ)
  where open Group G

ℤ→ᴳ-iso-idf : ∀ {i} (G : AbGroup i) → hom-group ℤ-group G ≃ᴳ AbGroup.grp G
ℤ→ᴳ-iso-idf G = ≃-to-≃ᴳ (ℤ→ᴳ-equiv-idf (AbGroup.grp G)) (λ _ _ → idp)

ℤ-⊙group : ⊙Group₀
ℤ-⊙group = ⊙[ ℤ-group , 1 ]ᴳ

ℤ-is-infinite-cyclic : is-infinite-cyclic ℤ-⊙group
ℤ-is-infinite-cyclic = is-eq
  (Group.exp ℤ-group 1)
  (idf ℤ)
  (! ∘ ℤ-idf-η)
  (! ∘ ℤ-idf-η)


-- Minimal propositional logic, PHOAS approach, initial encoding

module Pi.Mp where

-- Types

infixl 2 _&&_
infixl 1 _||_
infixr 0 _=>_
data Ty : Set where
  UNIT  :             Ty
  _=>_  : Ty -> Ty -> Ty
  _&&_  : Ty -> Ty -> Ty
  _||_  : Ty -> Ty -> Ty
  FALSE :             Ty

infixr 0 _<=>_
_<=>_ : Ty -> Ty -> Ty
a <=> b = (a => b) && (b => a)

NOT : Ty -> Ty
NOT a = a => FALSE

TRUE : Ty
TRUE = FALSE => FALSE

-- Context and truth judgement

Cx : Set1
Cx = Ty -> Set

isTrue : Ty -> Cx -> Set
isTrue a tc = tc a

-- Terms

module Mp where
  infixl 1 _$_
  data Tm (tc : Cx) : Ty -> Set where
    var   : forall {a}     -> isTrue a tc               -> Tm tc a
    lam'  : forall {a b}   -> (isTrue a tc -> Tm tc b)  -> Tm tc (a => b)
    _$_   : forall {a b}   -> Tm tc (a => b) -> Tm tc a -> Tm tc b
    pair' : forall {a b}   -> Tm tc a        -> Tm tc b -> Tm tc (a && b)
    fst   : forall {a b}   -> Tm tc (a && b)            -> Tm tc a
    snd   : forall {a b}   -> Tm tc (a && b)            -> Tm tc b
    left  : forall {a b}   -> Tm tc a                   -> Tm tc (a || b)
    right : forall {a b}   -> Tm tc b                   -> Tm tc (a || b)
    case' : forall {a b c} -> Tm tc (a || b) -> (isTrue a tc -> Tm tc c) -> (isTrue b tc -> Tm tc c) -> Tm tc c

  lam'' : forall {tc a b} -> (Tm tc a -> Tm tc b) -> Tm tc (a => b)
  lam'' f = lam' \x -> f (var x)

  case'' : forall {tc a b c} -> Tm tc (a || b) -> (Tm tc a -> Tm tc c) -> (Tm tc b -> Tm tc c) -> Tm tc c
  case'' xy f g = case' xy (\x -> f (var x)) (\y -> g (var y))

  syntax lam''  (\a -> b) = lam a => b
  syntax pair'  x y       = [ x , y ]
  syntax case'' xy (\x -> z1) (\y -> z2) = case xy of x => z1 or y => z2

  Thm : Ty -> Set1
  Thm a = forall {tc} -> Tm tc a
open Mp public

-- Example theorems

c1 : forall {a b} -> Thm (a && b <=> b && a)
c1 =
  [ lam xy => [ snd xy , fst xy ]
  , lam yx => [ snd yx , fst yx ]
  ]

c2 : forall {a b} -> Thm (a || b <=> b || a)
c2 =
  [ lam xy =>
      case xy
        of x => right x
        or y => left y
  , lam yx =>
      case yx
        of y => right y
        or x => left x
  ]

i1 : forall {a} -> Thm (a && a <=> a)
i1 =
  [ lam xx => fst xx
  , lam x  => [ x , x ]
  ]

i2 : forall {a} -> Thm (a || a <=> a)
i2 =
  [ lam xx =>
      case xx
        of x => x
        or x => x
  , lam x => left x
  ]

l3 : forall {a} -> Thm ((a => a) <=> TRUE)
l3 =
  [ lam _ => lam nt => nt
  , lam _ => lam x => x
  ]

l1 : forall {a b c} -> Thm (a && (b && c) <=> (a && b) && c)
l1 =
  [ lam xyz =>
      (let yz = snd xyz in
        [ [ fst xyz , fst yz ] , snd yz ])
  , lam xyz =>
      (let xy = fst xyz in
        [ fst xy , [ snd xy , snd xyz ] ])
  ]

l2 : forall {a} -> Thm (a && TRUE <=> a)
l2 =
  [ lam xt => fst xt
  , lam x  => [ x , lam nt => nt ]
  ]

l4 : forall {a b c} -> Thm (a && (b || c) <=> (a && b) || (a && c))
l4 =
  [ lam xyz =>
      (let x = fst xyz in
        case snd xyz
          of y => left  [ x , y ]
          or z => right [ x , z ])
  , lam xyxz =>
      case xyxz
        of xy => [ fst xy , left  (snd xy) ]
        or xz => [ fst xz , right (snd xz) ]
  ]

l6 : forall {a b c} -> Thm (a || (b && c) <=> (a || b) && (a || c))
l6 =
  [ lam xyz =>
      case xyz
        of x  => [ left x , left x ]
        or yz => [ right (fst yz) , right (snd yz) ]
  , lam xyxz =>
      case fst xyxz
        of x => left x
        or y =>
          case snd xyxz
            of x => left x
            or z => right [ y , z ]
  ]

l7 : forall {a} -> Thm (a || TRUE <=> TRUE)
l7 =
  [ lam _ => lam nt => nt
  , lam t => right t
  ]

l9 : forall {a b c} -> Thm (a || (b || c) <=> (a || b) || c)
l9 =
  [ lam xyz =>
      case xyz
        of x  => left (left x)
        or yz =>
          case yz
            of y => left (right y)
            or z => right z
  , lam xyz =>
      case xyz
        of xy =>
          case xy
            of x => left x
            or y => right (left y)
        or z => right (right z)
  ]

l11 : forall {a b c} -> Thm ((a => (b && c)) <=> (a => b) && (a => c))
l11 =
  [ lam xyz =>
      [ lam x => fst (xyz $ x)
      , lam x => snd (xyz $ x)
      ]
  , lam xyxz =>
      lam x => [ fst xyxz $ x , snd xyxz $ x ]
  ]

l12 : forall {a} -> Thm ((a => TRUE) <=> TRUE)
l12 =
  [ lam _ => lam nt => nt
  , lam t => lam _  => t
  ]

l13 : forall {a b c} -> Thm ((a => (b => c)) <=> ((a && b) => c))
l13 =
  [ lam xyz =>
      lam xy => xyz $ fst xy $ snd xy
  , lam xyz =>
      lam x =>
        lam y => xyz $ [ x , y ]
  ]

l16 : forall {a b c} -> Thm (((a && b) => c) <=> (a => (b => c)))
l16 =
  [ lam xyz =>
      lam x =>
        lam y => xyz $ [ x , y ]
  , lam xyz =>
      lam xy => xyz $ fst xy $ snd xy
  ]

l17 : forall {a} -> Thm ((TRUE => a) <=> a)
l17 =
  [ lam tx => tx $ (lam nt => nt)
  , lam x  => lam _ => x
  ]

l19 : forall {a b c} -> Thm (((a || b) => c) <=> (a => c) && (b => c))
l19 =
  [ lam xyz =>
      [ lam x => xyz $ left x
      , lam y => xyz $ right y
      ]
  , lam xzyz =>
      lam xy =>
        case xy
          of x => fst xzyz $ x
          or y => snd xzyz $ y
  ]


-- Proof irrelevance is nice when you want to work with subsets.
module Subset where

data True : Prop where
  tt : True

data False : Prop where

data (|) (A:Set)(P:A -> Prop) : Set where
  sub : (x:A) -> P x -> A | P

data Nat : Set where
  zero : Nat
  suc  : Nat -> Nat

mutual
  IsEven : Nat -> Prop
  IsEven  zero   = True
  IsEven (suc n) = IsOdd n

  IsOdd : Nat -> Prop
  IsOdd  zero   = False
  IsOdd (suc n) = IsEven n

Even : Set
Even = Nat | IsEven

Odd : Set
Odd = Nat | IsOdd

(+) : Nat -> Nat -> Nat
zero  + m = m
suc n + m = suc (n + m)


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

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.HalfAdjoint
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Transport
open import Cubical.Foundations.SIP

open import Cubical.Data.Sigma

open import Cubical.Structures.Axioms
open import Cubical.Structures.Auto
open import Cubical.Algebra.Semigroup hiding (⟨_⟩)

open Iso

private
  variable
    ℓ : Level

record IsMonoid {A : Type ℓ} (ε : A) (_·_ : A → A → A) : Type ℓ where
  constructor ismonoid

  field
    isSemigroup : IsSemigroup _·_
    identity    : (x : A) → (x · ε ≡ x) × (ε · x ≡ x)

  open IsSemigroup isSemigroup public

  lid : (x : A) → ε · x ≡ x
  lid x = identity x .snd

  rid : (x : A) → x · ε ≡ x
  rid x = identity x .fst

record Monoid : Type (ℓ-suc ℓ) where
  constructor monoid

  field
    Carrier  : Type ℓ
    ε        : Carrier
    _·_      : Carrier → Carrier → Carrier
    isMonoid : IsMonoid ε _·_

  infixl 7 _·_

  open IsMonoid isMonoid public

  -- semigrp : Semigroup
  -- semigrp = record { isSemigroup = isSemigroup }

  -- open Semigroup semigrp public

-- Extractor for the carrier type
⟨_⟩ : Monoid → Type ℓ
⟨_⟩ = Monoid.Carrier

η-isMonoid : {A : Type ℓ} {ε : A} {_∙_ :  A → A → A} (b : IsMonoid ε _∙_)
          → ismonoid (IsMonoid.isSemigroup b) (IsMonoid.identity b) ≡ b
IsMonoid.isSemigroup (η-isMonoid b i) = IsMonoid.isSemigroup b
IsMonoid.identity (η-isMonoid b i) = IsMonoid.identity b

-- Easier to use constructors

makeIsMonoid : {M : Type ℓ} {ε : M} {_·_ : M → M → M}
               (is-setM : isSet M)
               (assoc : (x y z : M) → x · (y · z) ≡ (x · y) · z)
               (rid : (x : M) → x · ε ≡ x)
               (lid : (x : M) → ε · x ≡ x)
             → IsMonoid ε _·_
IsMonoid.isSemigroup (makeIsMonoid is-setM assoc rid lid) = issemigroup is-setM assoc
IsMonoid.identity (makeIsMonoid is-setM assoc rid lid) = λ x → rid x , lid x

makeMonoid : {M : Type ℓ} (ε : M) (_·_ : M → M → M)
             (is-setM : isSet M)
             (assoc : (x y z : M) → x · (y · z) ≡ (x · y) · z)
             (rid : (x : M) → x · ε ≡ x)
             (lid : (x : M) → ε · x ≡ x)
           → Monoid
makeMonoid ε _·_ is-setM assoc rid lid =
  monoid _ ε _·_ (makeIsMonoid is-setM assoc rid lid)

record MonoidEquiv (M N : Monoid {ℓ}) : Type ℓ where

  constructor monoidiso

  private
    module M = Monoid M
    module N = Monoid N

  field
    e     : ⟨ M ⟩ ≃ ⟨ N ⟩
    presε : equivFun e M.ε ≡ N.ε
    isHom : (x y : ⟨ M ⟩) → equivFun e (x M.· y) ≡ equivFun e x N.· equivFun e y

module MonoidΣTheory {ℓ} where

  RawMonoidStructure : Type ℓ → Type ℓ
  RawMonoidStructure X = X × (X → X → X)

  RawMonoidEquivStr = AutoEquivStr RawMonoidStructure

  rawMonoidUnivalentStr : UnivalentStr _ RawMonoidEquivStr
  rawMonoidUnivalentStr = autoUnivalentStr RawMonoidStructure

  MonoidAxioms : (M : Type ℓ) → RawMonoidStructure M → Type ℓ
  MonoidAxioms M (e , _·_) = IsSemigroup _·_
                            × ((x : M) → (x · e ≡ x) × (e · x ≡ x))

  MonoidStructure : Type ℓ → Type ℓ
  MonoidStructure = AxiomsStructure RawMonoidStructure MonoidAxioms

  MonoidΣ : Type (ℓ-suc ℓ)
  MonoidΣ = TypeWithStr ℓ MonoidStructure

  isPropMonoidAxioms : (M : Type ℓ) (s : RawMonoidStructure M) → isProp (MonoidAxioms M s)
  isPropMonoidAxioms M (e , _·_) =
    isPropΣ (isPropIsSemigroup _·_)
            λ α → isPropΠ λ _ → isProp× (IsSemigroup.is-set α _ _) (IsSemigroup.is-set α _ _)

  MonoidEquivStr : StrEquiv MonoidStructure ℓ
  MonoidEquivStr = AxiomsEquivStr RawMonoidEquivStr MonoidAxioms

  MonoidAxiomsIsoIsMonoid : {M : Type ℓ} (s : RawMonoidStructure M)
    → Iso (MonoidAxioms M s) (IsMonoid (s .fst) (s .snd))
  fun (MonoidAxiomsIsoIsMonoid s) (x , y)        = ismonoid x y
  inv (MonoidAxiomsIsoIsMonoid s) a = (IsMonoid.isSemigroup a) , IsMonoid.identity a
  rightInv (MonoidAxiomsIsoIsMonoid s) b         = η-isMonoid b
  leftInv (MonoidAxiomsIsoIsMonoid s) _          = refl

  MonoidAxioms≡IsMonoid : {M : Type ℓ} (s : RawMonoidStructure M)
    → MonoidAxioms M s ≡ IsMonoid (s .fst) (s .snd)
  MonoidAxioms≡IsMonoid s = isoToPath (MonoidAxiomsIsoIsMonoid s)
  open Monoid
  Monoid→MonoidΣ : Monoid → MonoidΣ
  Monoid→MonoidΣ M =
    ⟨ M ⟩ , ((ε M) , _·_ M) , MonoidAxiomsIsoIsMonoid ((ε M) , _·_ M) .inv (isMonoid M)

  MonoidΣ→Monoid : MonoidΣ → Monoid
  MonoidΣ→Monoid (M , (ε , _·_) , isMonoidΣ) =
    monoid M ε _·_ (MonoidAxiomsIsoIsMonoid (ε , _·_) .fun isMonoidΣ)

  MonoidIsoMonoidΣ : Iso Monoid MonoidΣ
  MonoidIsoMonoidΣ =
    iso Monoid→MonoidΣ MonoidΣ→Monoid (λ _ → refl) helper
    where
    helper : _
    Carrier (helper a i) = ⟨ a ⟩
    ε (helper a i) = ε a
    _·_ (helper a i) = _·_ a
    isMonoid (helper a i) = η-isMonoid (isMonoid a) i

  monoidUnivalentStr : UnivalentStr MonoidStructure MonoidEquivStr
  monoidUnivalentStr = axiomsUnivalentStr _ isPropMonoidAxioms rawMonoidUnivalentStr

  MonoidΣPath : (M N : MonoidΣ) → (M ≃[ MonoidEquivStr ] N) ≃ (M ≡ N)
  MonoidΣPath = SIP monoidUnivalentStr

  MonoidEquivΣ : (M N : Monoid) → Type ℓ
  MonoidEquivΣ M N = Monoid→MonoidΣ M ≃[ MonoidEquivStr ] Monoid→MonoidΣ N

  MonoidIsoΣPath : {M N : Monoid} → Iso (MonoidEquiv M N) (MonoidEquivΣ M N)
  fun MonoidIsoΣPath (monoidiso e h1 h2) = (e , h1 , h2)
  inv MonoidIsoΣPath (e , h1 , h2)       = monoidiso e h1 h2
  rightInv MonoidIsoΣPath _              = refl
  leftInv MonoidIsoΣPath _               = refl

  MonoidPath : (M N : Monoid) → (MonoidEquiv M N) ≃ (M ≡ N)
  MonoidPath M N =
    MonoidEquiv M N                       ≃⟨ isoToEquiv MonoidIsoΣPath ⟩
    MonoidEquivΣ M N                      ≃⟨ MonoidΣPath _ _ ⟩
    Monoid→MonoidΣ M ≡ Monoid→MonoidΣ N ≃⟨ isoToEquiv (invIso (congIso MonoidIsoMonoidΣ)) ⟩
    M ≡ N ■

  RawMonoidΣ : Type (ℓ-suc ℓ)
  RawMonoidΣ = TypeWithStr ℓ RawMonoidStructure

  Monoid→RawMonoidΣ : Monoid → RawMonoidΣ
  Monoid→RawMonoidΣ A = ⟨ A ⟩ , (ε A) , (_·_ A)

  InducedMonoid : (M : Monoid) (N : RawMonoidΣ) (e : M .Monoid.Carrier ≃ N .fst)
                 → RawMonoidEquivStr (Monoid→RawMonoidΣ M) N e → Monoid
  InducedMonoid M N e r =
    MonoidΣ→Monoid (transferAxioms rawMonoidUnivalentStr (Monoid→MonoidΣ M) N (e , r))

  InducedMonoidPath : (M : Monoid {ℓ}) (N : RawMonoidΣ) (e : M .Monoid.Carrier ≃ N .fst)
                      (E : RawMonoidEquivStr (Monoid→RawMonoidΣ M) N e)
                    → M ≡ InducedMonoid M N e E
  InducedMonoidPath M N e E =
    MonoidPath M (InducedMonoid M N e E) .fst (monoidiso e (E .fst) (E .snd))

-- We now extract the important results from the above module

isPropIsMonoid : {M : Type ℓ} (ε : M) (_·_ : M → M → M) → isProp (IsMonoid ε _·_)
isPropIsMonoid ε _·_ =
  subst isProp (MonoidΣTheory.MonoidAxioms≡IsMonoid (ε , _·_))
        (MonoidΣTheory.isPropMonoidAxioms _ (ε , _·_))

MonoidPath : (M N : Monoid {ℓ}) → (MonoidEquiv M N) ≃ (M ≡ N)
MonoidPath = MonoidΣTheory.MonoidPath

InducedMonoid : (M : Monoid {ℓ}) (N : MonoidΣTheory.RawMonoidΣ) (e : M .Monoid.Carrier ≃ N .fst)
              → MonoidΣTheory.RawMonoidEquivStr (MonoidΣTheory.Monoid→RawMonoidΣ M) N e
              → Monoid
InducedMonoid = MonoidΣTheory.InducedMonoid

InducedMonoidPath : (M : Monoid {ℓ}) (N : MonoidΣTheory.RawMonoidΣ) (e : M .Monoid.Carrier ≃ N .fst)
                    (E : MonoidΣTheory.RawMonoidEquivStr (MonoidΣTheory.Monoid→RawMonoidΣ M) N e)
                  → M ≡ InducedMonoid M N e E
InducedMonoidPath = MonoidΣTheory.InducedMonoidPath

module MonoidTheory {ℓ} (M' : Monoid {ℓ}) where

  open Monoid M' renaming ( Carrier to M )

  -- Added for its use in groups
  -- If there exists a inverse of an element it is unique
  inv-lemma : (x y z : M) → y · x ≡ ε → x · z ≡ ε → y ≡ z
  inv-lemma x y z left-inverse right-inverse =
    y           ≡⟨ sym (rid y) ⟩
    y · ε       ≡⟨ cong (λ - → y · -) (sym right-inverse) ⟩
    y · (x · z) ≡⟨ assoc y x z ⟩
    (y · x) · z ≡⟨ cong (λ - → - · z) left-inverse ⟩
    ε · z       ≡⟨ lid z ⟩
    z ∎


------------------------------------------------------------------------
-- The Agda standard library
--
-- Results concerning function extensionality for propositional equality
------------------------------------------------------------------------

{-# OPTIONS --with-K --safe #-}

module Axiom.Extensionality.Heterogeneous where

import Axiom.Extensionality.Propositional as P
open import Function
open import Level
open import Relation.Binary.HeterogeneousEquality.Core
open import Relation.Binary.PropositionalEquality.Core

------------------------------------------------------------------------
-- Function extensionality states that if two functions are
-- propositionally equal for every input, then the functions themselves
-- must be propositionally equal.

Extensionality : (a b : Level) → Set _
Extensionality a b =
  {A : Set a} {B₁ B₂ : A → Set b}
  {f₁ : (x : A) → B₁ x} {f₂ : (x : A) → B₂ x} →
  (∀ x → B₁ x ≡ B₂ x) → (∀ x → f₁ x ≅ f₂ x) → f₁ ≅ f₂

------------------------------------------------------------------------
-- Properties

-- This form of extensionality follows from extensionality for _≡_.

≡-ext⇒≅-ext : ∀ {ℓ₁ ℓ₂} →
              P.Extensionality ℓ₁ (suc ℓ₂) →
              Extensionality ℓ₁ ℓ₂
≡-ext⇒≅-ext {ℓ₁} {ℓ₂} ext B₁≡B₂ f₁≅f₂ with ext B₁≡B₂
... | refl = ≡-to-≅ $ ext′ (≅-to-≡ ∘ f₁≅f₂)
  where
  ext′ : P.Extensionality ℓ₁ ℓ₂
  ext′ = P.lower-extensionality ℓ₁ (suc ℓ₂) ext


-- 2011-09-15 posted by Nisse
-- {-# OPTIONS --show-implicit -v tc.lhs.unify:15 #-}
module Issue292-16 where

data _≡_ {A : Set} (x : A) : A → Set where
  refl : x ≡ x

postulate
  A : Set
  f : A → A

data C : A → Set where
  c : ∀ x → C (f x)

record Box : Set where
  constructor box
  field
    a : A
    b : C a

test : ∀ {x₁ x₂} → box (f x₁) (c x₁) ≡ box (f x₂) (c x₂) → x₁ ≡ x₂
test refl = refl

-- this failed before because we tried
--
--   c x₁ : C (f x₁) =?= c₂ x₂ : C (f x₂)
--
-- and did not recognize that
--
--   x₁ : A =?= x₂ : A
--
-- is homogeneous


{-# OPTIONS --without-K #-}
open import HoTT.Base
open import HoTT.Equivalence
open import HoTT.Identity.Pi
open import HoTT.Identity.Product

module HoTT.Product.Universal where

×-univ : ∀ {i j k} {X : 𝒰 i} (A : X → 𝒰 j) (B : X → 𝒰 k) →
         ((c : X) → A c × B c) ≃ Π X A × Π X B
×-univ {X = X} A B = let open Iso in iso→eqv λ where
  .f f → pr₁ ∘ f , pr₂ ∘ f
  .g f x → pr₁ f x , pr₂ f x
  .η f → funext (×-uniq ∘ f)
  .ε f → ×-pair⁼ (refl , refl)


module Numeral.Natural.Relation.Divisibility.Proofs.Modulo where

import Lvl
open import Data
open import Functional
open import Logic.Predicate
open import Logic.Propositional
open import Numeral.Natural
open import Numeral.Natural.Oper
open import Numeral.Natural.Oper.DivMod.Proofs
open import Numeral.Natural.Oper.FlooredDivision
open import Numeral.Natural.Oper.Modulo
open import Numeral.Natural.Oper.Modulo.Proofs
open import Numeral.Natural.Oper.Proofs
open import Numeral.Natural.Relation.Divisibility
open import Numeral.Natural.Relation.Divisibility.Proofs
open import Relator.Equals
open import Relator.Equals.Proofs.Equiv
open import Structure.Function.Domain
open import Structure.Operator.Properties
open import Structure.Operator.Proofs.Util
open import Syntax.Transitivity
open import Type

divides-mod : ∀{a b d} → (d ∣ 𝐒(b)) → (d ∣ a) ↔ (d ∣ a mod 𝐒(b))
divides-mod {a}{b}{d} db = [↔]-intro (l db) (r db) where
  l : ∀{a b d} → (d ∣ 𝐒(b)) → (d ∣ a) ← (d ∣ (a mod₀ 𝐒(b)))
  l {a}{b}{𝟎}    db dmod with () ← [0]-only-divides-[0] db
  l {a}{b}{𝐒(d)} db dmod
    with [∃]-intro (𝐒(n)) ⦃ dnb ⦄  ← [↔]-to-[←] divides-[⋅]-existence db
    with [∃]-intro m     ⦃ dmmod ⦄ ← [↔]-to-[←] divides-[⋅]-existence dmod
    = [↔]-to-[→] divides-[⋅]-existence ([∃]-intro (((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ 𝐒(n)) + m) ⦃ p ⦄) where
    p : (𝐒(d) ⋅ (((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ 𝐒(n)) + m) ≡ a)
    p =
      𝐒(d) ⋅ (((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ 𝐒(n)) + m)                     🝖[ _≡_ ]-[ distributivityₗ(_⋅_)(_+_) {𝐒(d)}{(a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ 𝐒(n)}{m} ]
      (𝐒(d) ⋅ ((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ 𝐒(n))) + (𝐒(d) ⋅ m)            🝖[ _≡_ ]-[ [≡]-with(_+ (𝐒(d) ⋅ m)) (One.commuteₗ-assocᵣ {a = 𝐒(d)}{a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))}{𝐒(n)}) ]
      ((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ (𝐒(d) ⋅ 𝐒(n))) + (𝐒(d) ⋅ m)            🝖[ _≡_ ]-[ [≡]-with(((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ (𝐒(d) ⋅ 𝐒(n))) +_) dmmod ]
      ((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ (𝐒(d) ⋅ 𝐒(n))) + (a mod 𝐒(b))          🝖[ _≡_ ]-[ [≡]-with(expr ↦ ((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ (𝐒(d) ⋅ 𝐒(n))) + (a mod 𝐒(expr))) (injective(𝐒) dnb) ]-sym
      ((a ⌊/⌋ (𝐒(d) ⋅ 𝐒(n))) ⋅ (𝐒(d) ⋅ 𝐒(n))) + (a mod (𝐒(d) ⋅ 𝐒(n))) 🝖[ _≡_ ]-[ [⌊/⌋][mod]-is-division-with-remainder {a}{d + 𝐒(d) ⋅ n} ]
      a                                                               🝖-end

  r : ∀{a b d} → (d ∣ 𝐒(b)) → (d ∣ a) → (d ∣ (a mod₀ 𝐒(b)))
  r {a}{b}{𝟎}   db da with [≡]-intro ← [0]-only-divides-[0] da = Div𝟎
  r {a}{b}{𝐒 d} db da
    with [∃]-intro n ⦃ dna ⦄ ← [↔]-to-[←] divides-[⋅]-existence da
    with [∃]-intro m ⦃ dmb ⦄ ← [↔]-to-[←] divides-[⋅]-existence db
    = [↔]-to-[→] divides-[⋅]-existence ([∃]-intro (n mod₀ m) ⦃ p ⦄) where
    p : (𝐒(d) ⋅ (n mod₀ m) ≡ a mod₀ 𝐒(b))
    p =
      𝐒(d) ⋅ (n mod₀ m)          🝖[ _≡_ ]-[ [⋅][mod]-distributivityₗ {n}{m}{𝐒(d)} ]
      (𝐒(d) ⋅ n) mod₀ (𝐒(d) ⋅ m) 🝖[ _≡_ ]-[ [≡]-with(\expr → ((𝐒(d) ⋅ n) mod₀ expr)) dmb ]
      (𝐒(d) ⋅ n) mod₀ 𝐒(b)       🝖[ _≡_ ]-[ [≡]-with(_mod₀ 𝐒(b)) dna ]
      a mod₀ 𝐒(b)                🝖[ _≡_ ]-end


-- Adapted from Wadler: https://plfa.github.io/Lambda/

module Context (A : Set) where
  open import Prelude

  open import Data.Nat
  open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; sym)
  open import Data.Maybe
  open import Data.Product using (_×_; proj₁; proj₂; ∃-syntax) renaming (_,_ to ⟨_,_⟩)
  open import Relation.Nullary.Decidable
  open import Relation.Nullary using (Dec; yes; no)
  open import Data.Empty
  import Data.Nat.Properties

  infixl 5  _,_⦂_

  -- Internal type of contexts
  data ctx : Set where
    ∅     : ctx
    _,_⦂_  : ctx → ℕ → A → ctx

  infix  4  _∋_⦂_

  data _∋_⦂_ : ctx → ℕ → A → Set where

    Z : ∀ {Γ : ctx}
      → ∀ {x : ℕ}
      → ∀ {a : A}
        ------------------
      → Γ , x ⦂ a ∋ x ⦂ a

    S : ∀ {Γ x y a b}
      → x ≢ y
      → Γ ∋ x ⦂ a
        ------------------
      → Γ , y ⦂ b ∋ x ⦂ a

  lookup : ctx → ℕ → Maybe A
  lookup ∅ _ = nothing
  lookup (Γ , x ⦂ t) y with compare x y
  ...                       | equal _ = just t
  ...                       | _ = lookup Γ x

  data _∈dom_ : ℕ → ctx → Set where
    inDom : ∀ {x Γ a}
          → Γ ∋ x ⦂ a
          -------------
          → x ∈dom Γ

  data _∉dom_ : ℕ → ctx → Set where
    notInEmpty : ∀ {x}
               ----------
               → x ∉dom ∅

    notInNonempty : ∀ {x x' Γ T}
                    → x ≢ x'
                    → x ∉dom Γ
                    --------------------
                    → x ∉dom (Γ , x' ⦂ T)

  irrelevantExtensionsOK : ∀ {Γ : ctx}
                           → ∀ {x y t t'}
                           → Γ ∋ x ⦂ t
                           → x ≢ y
                           → Γ , y ⦂ t' ∋ x ⦂ t
  irrelevantExtensionsOK {Γ} {x} {y} {t} cont@(Z {Γ₀} {x} {t}) neq = S neq cont
  irrelevantExtensionsOK (S neq' rest) neq = S neq (irrelevantExtensionsOK rest neq')

  irrelevantReductionsOK :  ∀ {Γ : ctx}
                           → ∀ {x y t t'}
                           → Γ , x ⦂ t ∋ y ⦂ t'
                           → y ≢ x
                           → Γ ∋ y ⦂ t'
                           -- ⊥-elim (!neq (Relation.Binary.PropositionalEquality.sym x x))
  irrelevantReductionsOK {Γ} {x} {y} {t} {t'} z@(Z {Γ} {x} {t}) neq =
    let
      s : x ≡ x
      s = refl
      bot = neq s
    in
      Data.Empty.⊥-elim bot

  irrelevantReductionsOK {Γ} {x} {y} {t} {t'} (S x₁ qq) neq = qq

  irrelevantReductionsInValuesOK :  ∀ {Γ : ctx}
                                 → ∀ {x y t t'}
                                 → Γ , x ⦂ t ∋ y ⦂ t'
                                 → t ≢ t'
                                 → Γ ∋ y ⦂ t'
  irrelevantReductionsInValuesOK {Γ} {x} {.x} {t} {.t} Z tNeqt' = ⊥-elim (tNeqt' refl)
  irrelevantReductionsInValuesOK {Γ} {x} {y} {t} {t'} (S yNeqx yt'InΓ') tNeqt' = yt'InΓ'

  ∈domExcludedMiddle : ∀ {Γ x}
                       → x ∉dom Γ
                       → Relation.Nullary.¬ (x ∈dom Γ)
  ∈domExcludedMiddle {.∅} {x} notInEmpty (inDom ())
  ∈domExcludedMiddle {.(_ , _ ⦂ _)} {x} (notInNonempty xNeqx' xNotInΓ) (inDom n) =
    let
      rest = ∈domExcludedMiddle xNotInΓ
      xInΓ = irrelevantReductionsOK n xNeqx'
    in
      rest (inDom xInΓ)

  ∉domPreservation : ∀ {x x' Γ T T'}
                     → x ∉dom (Γ , x' ⦂ T)
                     ---------------------
                     → x ∉dom (Γ , x' ⦂ T')
  ∉domPreservation {x} {x'} {Γ} {T} {T'} (notInNonempty xNeqX' xNotInDom) = notInNonempty xNeqX' xNotInDom

  ∉domGreaterThan : ∀ {Γ x}
                    → (∀ x' → x' ∈dom Γ → x' < x)
                    → x ∉dom Γ
  ∉domGreaterThan {∅} {x} xBigger = notInEmpty
  ∉domGreaterThan {Γ , x' ⦂ t} {x} xBigger =
      notInNonempty x≢x' (∉domGreaterThan rest) -- (∉domGreaterThan (λ x'' → λ x''InΓ → xBigger x'' (inDom {!!})))
    where
      x'<x = xBigger x' (inDom Z)
      x≢x' : x ≢ x'
      x≢x' = ≢-sym (Data.Nat.Properties.<⇒≢ x'<x)

      rest : (x'' : ℕ)
             → x'' ∈dom Γ
             → x'' < x
      rest x'' (inDom x''InΓ) with x' ≟ x''
      ... | yes x'≡x'' rewrite x'≡x'' = xBigger x'' (inDom Z)
      ... | no x'≢x'' = xBigger x'' (inDom (S (≢-sym x'≢x'') x''InΓ))

  fresh : (Γ : ctx)
        → ∃[ x ] (x ∉dom Γ × (∀ x' → x' ∈dom Γ → x' < x))
  fresh ∅ =
    ⟨ zero , ⟨ notInEmpty , xBigger ⟩ ⟩
    where
      xBigger :  (∀ x' → x' ∈dom ∅ → x' < zero)
      xBigger x' (inDom ())
  fresh (Γ , x ⦂ t) =
    ⟨ x' , ⟨ x'IsFresh , x'Bigger ⟩ ⟩
    where
      freshInRest = fresh Γ
      biggerThanRest = suc (proj₁ freshInRest)
      x' = biggerThanRest ⊔ (suc x) -- bigger than both everything in Γ and x.
      freshInRestBigger = proj₂ (proj₂ freshInRest)
      x'Bigger : (x'' : ℕ) → (x'' ∈dom (Γ , x ⦂ t)) → x'' < x'
      x'Bigger x'' (inDom x''InΓ') with x ≟ x''
      ... | yes x≡x'' rewrite x≡x'' =
          s≤s (Data.Nat.Properties.n≤m⊔n (proj₁ (fresh Γ)) x'') -- s≤s (Data.Nat.Properties.<⇒≤ x''<oldFresh)
      ... | no x≢x'' =
        let
          x''<oldFresh = freshInRestBigger x'' (inDom (irrelevantReductionsOK x''InΓ' (≢-sym x≢x'')) )
          x''≤oldFresh = (Data.Nat.Properties.<⇒≤ x''<oldFresh)
        in
          s≤s ( Data.Nat.Properties.m≤n⇒m≤n⊔o x x''≤oldFresh) --  s≤s (Data.Nat.Properties.<⇒≤ x''<oldFresh)
      x'IsFresh : x' ∉dom (Γ , x ⦂ t)
      x'IsFresh = ∉domGreaterThan x'Bigger

-- Removing elements from a context
  _#_ : ctx → ℕ → ctx
  ∅ # x = ∅
  (Γ , x' ⦂ T) # x with compare x x'
  ... | equal _  = Γ
  ... | _ = (Γ # x) , x' ⦂ T

  contextLookupUnique : ∀ {Γ : ctx}
                        → ∀ {x t t'}
                        → Γ ∋ x ⦂ t
                        → Γ ∋ x ⦂ t'
                        → t ≡ t'
  contextLookupUnique z1@Z z2@Z = refl
  contextLookupUnique z1@Z s2@(S {Γ} {x} {y} {a} {b} neq xHasTypeT') = Data.Empty.⊥-elim (neq refl)
  contextLookupUnique (S neq xHasTypeT) Z = Data.Empty.⊥-elim (neq refl)
  contextLookupUnique (S x₁ xHasTypeT) (S x₂ xHasTypeT') = contextLookupUnique xHasTypeT xHasTypeT'

  contextLookupNeq : ∀ {Γ : ctx}
                     → ∀ {x x' t t'}
                     → Γ , x ⦂ t ∋ x' ⦂ t'
                     → t ≢ t'
                     → x ≢ x'
  contextLookupNeq Z tNeq = Data.Empty.⊥-elim (tNeq refl)
  contextLookupNeq (S xNeq x'InΓ) tNeq = λ xEq → xNeq (sym xEq)

  lookupWeakening : ∀ {Γ : ctx}
                    → ∀ {x x' t t'}
                    → Γ ∋ x ⦂ t
                    → ∃[ T ] ((Γ , x' ⦂ t') ∋ x ⦂ T)

  lookupWeakening {Γ} {x} {x'} {t} {t'} Γcontainment with x ≟ x'
  ...                                                 | yes refl = ⟨ t' , Z {Γ = Γ} {x = x'} {a = t'} ⟩
  ...                                                 | no neq =  ⟨ t , S neq Γcontainment ⟩

  ∉dom-≢ : {Γ : ctx}
           → ∀ {x x' t}
           → x ∉dom (Γ , x' ⦂ t)
           → x ≢ x'

  ∉dom-≢ {Γ} {x} {x'} {t} (notInNonempty xNeqx' xNotInΓ') xEqx' = xNeqx' xEqx'


open import Level using (_⊔_; suc)

open import Data.Empty using (⊥)

open import Relation.Binary using (Rel; Decidable; DecSetoid)

module AKS.Algebra.Bundles where

open import AKS.Nat using (ℕ)

open import Algebra.Core using (Op₁; Op₂)
open import Algebra.Bundles using (CommutativeRing) public
open import AKS.Algebra.Structures using
  ( IsNonZeroCommutativeRing; IsIntegralDomain; IsGCDDomain
  ; IsUniqueFactorizationDomain; IsEuclideanDomain; IsField
  ; IsDecField; IsFiniteField
  ) public

record NonZeroCommutativeRing a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 6 _+_
  infix  4 _≈_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_     : Op₂ Carrier
    _*_     : Op₂ Carrier
    -_      : Op₁ Carrier
    0#      : Carrier
    1#      : Carrier
    isNonZeroCommutativeRing : IsNonZeroCommutativeRing Carrier _≈_ _+_ _*_ -_ 0# 1#

  open IsNonZeroCommutativeRing isNonZeroCommutativeRing using (isCommutativeRing; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#; C/0; 1#-nonzero; -1#-nonzero) public

  commutativeRing : CommutativeRing a ℓ
  commutativeRing = record { isCommutativeRing = isCommutativeRing }

  open CommutativeRing commutativeRing using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; setoid; isEquivalence
    ) public

record IntegralDomain a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 6 _+_
  infix  4 _≈_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_     : Op₂ Carrier
    _*_     : Op₂ Carrier
    -_      : Op₁ Carrier
    0#      : Carrier
    1#      : Carrier
    isIntegralDomain : IsIntegralDomain Carrier _≈_ _+_ _*_ -_ 0# 1#

  open IsIntegralDomain isIntegralDomain using (isNonZeroCommutativeRing; *-cancelˡ; *-cancelʳ; *≉0; _*-nonzero_) public

  nonZeroCommutativeRing : NonZeroCommutativeRing a ℓ
  nonZeroCommutativeRing = record { isNonZeroCommutativeRing = isNonZeroCommutativeRing }

  open NonZeroCommutativeRing nonZeroCommutativeRing using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; commutativeRing
    ; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#; C/0; 1#-nonzero; -1#-nonzero
    ; setoid; isEquivalence
    ) public

record GCDDomain a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 6 _+_
  infix  4 _≈_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_     : Op₂ Carrier
    _*_     : Op₂ Carrier
    -_      : Op₁ Carrier
    0#      : Carrier
    1#      : Carrier
    gcd     : Op₂ Carrier
    isGCDDomain : IsGCDDomain Carrier _≈_ _+_ _*_ -_ 0# 1# gcd

  open IsGCDDomain isGCDDomain using (isIntegralDomain) public

  integralDomain : IntegralDomain a ℓ
  integralDomain = record { isIntegralDomain = isIntegralDomain }

  open IntegralDomain integralDomain using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; commutativeRing; nonZeroCommutativeRing
    ; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#
    ; C/0; *-cancelˡ; *-cancelʳ; *≉0; _*-nonzero_; 1#-nonzero; -1#-nonzero
    ; setoid; isEquivalence
    ) public

record UniqueFactorizationDomain a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 6 _+_
  infix  4 _≈_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_     : Op₂ Carrier
    _*_     : Op₂ Carrier
    -_      : Op₁ Carrier
    0#      : Carrier
    1#      : Carrier
    gcd     : Op₂ Carrier
    isUniqueFactorizationDomain : IsUniqueFactorizationDomain Carrier _≈_ _+_ _*_ -_ 0# 1# gcd

  open IsUniqueFactorizationDomain isUniqueFactorizationDomain using (isGCDDomain) public

  gcdDomain : GCDDomain a ℓ
  gcdDomain = record { isGCDDomain = isGCDDomain }

  open GCDDomain gcdDomain using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; commutativeRing; nonZeroCommutativeRing
    ; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#
    ; C/0; *-cancelˡ; *-cancelʳ; *≉0; _*-nonzero_; 1#-nonzero; -1#-nonzero
    ; setoid; isEquivalence
    ) public

record EuclideanDomain a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 7 _div_
  infixl 7 _mod_
  infixl 6 _+_
  infix  4 _≈_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_   : Op₂ Carrier
    _*_   : Op₂ Carrier
    -_    : Op₁ Carrier
    0#    : Carrier
    1#    : Carrier
    ∣_∣   : ∀ n {n≉0 : n ≉ 0#} → ℕ
    _div_ : ∀ (n m : Carrier) {m≉0 : m ≉ 0#} → Carrier
    _mod_ : ∀ (n m : Carrier) {m≉0 : m ≉ 0#} → Carrier
    gcd   : Op₂ Carrier
    isEuclideanDomain : IsEuclideanDomain Carrier _≈_ _+_ _*_ -_ 0# 1# ∣_∣ _div_ _mod_ gcd

  open import AKS.Algebra.Structures Carrier _≈_ using (module Modulus)
  open Modulus 0# ∣_∣ _mod_ using (Remainder; 0≈; 0≉)
  open IsEuclideanDomain isEuclideanDomain using
    ( isUniqueFactorizationDomain
    ; division; modulus
    ) public

  uniqueFactorizationDomain : UniqueFactorizationDomain a ℓ
  uniqueFactorizationDomain = record { isUniqueFactorizationDomain = isUniqueFactorizationDomain }

  open UniqueFactorizationDomain uniqueFactorizationDomain using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; commutativeRing; nonZeroCommutativeRing
    ; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#
    ; C/0; *-cancelˡ; *-cancelʳ; *≉0; _*-nonzero_; 1#-nonzero; -1#-nonzero
    ; setoid; isEquivalence
    ) public

record Field a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 7 _/_
  infixl 6 _+_
  infix  4 _≈_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_     : Op₂ Carrier
    _*_     : Op₂ Carrier
    -_      : Op₁ Carrier
    0#      : Carrier
    1#      : Carrier
    _/_     : ∀ (n m : Carrier) {m≉0 : m ≉ 0#} → Carrier
    gcd     : Op₂ Carrier
    isField : IsField Carrier _≈_ _+_ _*_ -_ 0# 1# _/_ gcd

  open IsField isField using
    ( isEuclideanDomain
    ; m*[n/m]≈n; [n/m]*m≈n; /-cong; _/-nonzero_
    ; _⁻¹; ⁻¹-inverseʳ; ⁻¹-inverseˡ
    ; x⁻¹≉0; ⁻¹-cong
    )public

  euclideanDomain : EuclideanDomain a ℓ
  euclideanDomain = record { isEuclideanDomain = isEuclideanDomain }

  open EuclideanDomain euclideanDomain using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; commutativeRing; nonZeroCommutativeRing
    ; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#
    ; C/0; *-cancelˡ; *-cancelʳ; *≉0; _*-nonzero_; 1#-nonzero; -1#-nonzero
    ; setoid; isEquivalence
    ) public

record DecField a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 7 _/_
  infixl 6 _+_
  infix  4 _≈_
  infix  4 _≈?_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ
    _≈?_    : Decidable _≈_

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_     : Op₂ Carrier
    _*_     : Op₂ Carrier
    -_      : Op₁ Carrier
    0#      : Carrier
    1#      : Carrier
    _/_     : ∀ (n m : Carrier) {m≉0 : m ≉ 0#} → Carrier
    gcd     : Op₂ Carrier
    isDecField : IsDecField Carrier _≈_ _≈?_ _+_ _*_ -_ 0# 1# _/_ gcd

  open IsDecField isDecField using (isField; isDecEquivalence) public

  [field] : Field a ℓ
  [field] = record { isField = isField }

  decSetoid : DecSetoid a ℓ
  decSetoid = record { isDecEquivalence = isDecEquivalence }

  open Field [field] using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; commutativeRing; nonZeroCommutativeRing
    ; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#
    ; C/0; *-cancelˡ; *-cancelʳ; *≉0; _*-nonzero_; _/-nonzero_; 1#-nonzero; -1#-nonzero
    ; m*[n/m]≈n; [n/m]*m≈n; /-cong
    ; _⁻¹; ⁻¹-inverseʳ; ⁻¹-inverseˡ
    ; x⁻¹≉0; ⁻¹-cong
    ; setoid; isEquivalence
    ) public

record FiniteField a ℓ : Set (suc (a ⊔ ℓ)) where
  infix  8 -_
  infixl 7 _*_
  infixl 7 _/_
  infixl 6 _+_
  infix  4 _≈_
  infix  4 _≈?_
  field
    Carrier : Set a
    _≈_     : Rel Carrier ℓ
    _≈?_    : Decidable _≈_

  infix 4 _≉_
  _≉_ : Rel Carrier ℓ
  x ≉ y = x ≈ y → ⊥

  field
    _+_  : Op₂ Carrier
    _*_  : Op₂ Carrier
    -_   : Op₁ Carrier
    0#   : Carrier
    1#   : Carrier
    _/_  : ∀ (n m : Carrier) {m≉0 : m ≉ 0#} → Carrier
    gcd  : Op₂ Carrier
    cardinality   : ℕ
    isFiniteField : IsFiniteField Carrier _≈_ _≈?_ _+_ _*_ -_ 0# 1# _/_ gcd cardinality

  open IsFiniteField isFiniteField using (isDecField) public

  decField : DecField a ℓ
  decField = record { isDecField = isDecField }

  open DecField decField using
    ( +-rawMagma; +-magma; +-semigroup
    ; *-rawMagma; *-magma; *-semigroup
    ; +-rawMonoid; +-monoid; +-commutativeMonoid
    ; +-group; +-abelianGroup; _-_
    ; *-rawMonoid; *-monoid; *-commutativeMonoid
    ; nearSemiring; semiringWithoutOne
    ; semiringWithoutAnnihilatingZero; semiring
    ; commutativeSemiringWithoutOne; commutativeSemiring
    ; ring; commutativeRing; nonZeroCommutativeRing
    ; 0#≉1#; 1#≉0#; 0#≉-1#; -1#≉0#
    ; C/0; *-cancelˡ; *-cancelʳ; *≉0; _*-nonzero_; _/-nonzero_; 1#-nonzero; -1#-nonzero
    ; m*[n/m]≈n; [n/m]*m≈n; /-cong
    ; _⁻¹; ⁻¹-inverseʳ; ⁻¹-inverseˡ
    ; x⁻¹≉0; ⁻¹-cong
    ; setoid; isEquivalence; decSetoid; isDecEquivalence
    ) public


open import Level using (Level; suc; zero; _⊔_)
open import Function using (const)
open import Algebra
open import Algebra.Structures
open import Algebra.OrderedMonoid
open import Algebra.Pregroup
open import Algebra.FunctionProperties as FunctionProperties using (Op₁; Op₂)
open import Data.Product using (_×_; _,_; proj₁; proj₂)
open import Relation.Binary
open import Relation.Binary.PartialOrderReasoning as ≤-Reasoning using ()

module Algebra.PregroupPlus where

record IsPregroupPlus
       {a ℓ₁ ℓ₂} {A : Set a} (≈ : Rel A ℓ₁) (≤ : Rel A ℓ₂)
       (∙ : Op₂ A) (ε₁ : A) (ˡ : Op₁ A) (ʳ : Op₁ A)
       (+ : Op₂ A) (ε₀ : A) (ᵘ : Op₁ A) (ᵈ : Op₁ A)
       : Set (a ⊔ ℓ₁ ⊔ ℓ₂) where

  field
    ∙-isPregroup : IsPregroup ≈ ≤ ∙ ε₁ ˡ ʳ
    +-isPregroup : IsPregroup ≈ ≤ + ε₀ ᵘ ᵈ

  open IsPregroup ∙-isPregroup public
       using (ˡ-cong; ʳ-cong; ˡ-contract; ʳ-contract
             ;ˡ-expand; ʳ-expand; ∙-cong)
       renaming (assoc to ∙-assoc; identity to ∙-identity
                ;compatibility to ∙-compatibility)
  open IsPregroup +-isPregroup public
       using (≤-refl; ≤-reflexive; ≤-trans; ≤-resp-≈; refl; sym; trans; antisym)
       renaming (ˡ-cong to ᵈ-cong; ʳ-cong to ᵘ-cong
                ;ˡ-contract to ᵈ-contract; ʳ-contract to ᵘ-contract
                ;ˡ-expand to ᵈ-expand; ʳ-expand to ᵘ-expand
                ;∙-cong to +-cong; assoc to +-assoc; identity to +-identity
                ;compatibility to +-compatibility)

record PregroupPlus c ℓ₁ ℓ₂ : Set (suc (c ⊔ ℓ₁ ⊔ ℓ₂)) where
  infixl 7 _∙_
  infixl 6 _+_
  infix  4 _≈_
  infix  4 _≤_
  field
    Carrier         : Set c
    _≈_             : Rel Carrier ℓ₁
    _≤_             : Rel Carrier ℓ₂
    _∙_             : Op₂ Carrier
    ε₁              : Carrier
    _ˡ              : Op₁ Carrier
    _ʳ              : Op₁ Carrier
    _+_             : Op₂ Carrier
    ε₀              : Carrier
    _ᵘ              : Op₁ Carrier
    _ᵈ              : Op₁ Carrier
    isPregroupPlus  : IsPregroupPlus _≈_ _≤_ _∙_ ε₁ _ˡ _ʳ _+_ ε₀ _ᵘ _ᵈ

  open IsPregroupPlus isPregroupPlus public

  ∙-pregroup : Pregroup _ _ _
  ∙-pregroup = record { isPregroup = ∙-isPregroup }
  open Pregroup ∙-pregroup public using (
    ˡ-unique; ˡ-identity; ˡ-distrib; ˡ-contra; ˡʳ-cancel;
    ʳ-unique; ʳ-identity; ʳ-distrib; ʳ-contra; ʳˡ-cancel)

  +-pregroup : Pregroup _ _ _
  +-pregroup = record { isPregroup = +-isPregroup }
  open Pregroup +-pregroup public using () renaming (
    ˡ-unique to ᵈ-unique; ˡ-identity to ᵈ-identity; ˡ-distrib to ᵈ-distrib;
    ˡ-contra to ᵈ-contra; ˡʳ-cancel to ᵈᵘ-cancel;
    ʳ-unique to ᵘ-unique; ʳ-identity to ᵘ-identity; ʳ-distrib to ᵘ-distrib;
    ʳ-contra to ᵘ-contra; ʳˡ-cancel to ᵘᵈ-calcel)


{-# OPTIONS --prop --rewriting #-}

module Examples.TreeSum where

open import Calf.CostMonoid
open import Calf.CostMonoids using (ℕ²-ParCostMonoid)

parCostMonoid = ℕ²-ParCostMonoid
open ParCostMonoid parCostMonoid

open import Calf costMonoid
open import Calf.ParMetalanguage parCostMonoid
open import Calf.Types.Nat
open import Calf.Types.Bounded costMonoid

open import Relation.Binary.PropositionalEquality as Eq using (_≡_; refl; _≢_; module ≡-Reasoning)
open import Data.Nat as Nat using (_+_; _⊔_)
open import Data.Nat.Properties as N using ()
open import Data.Product

add : cmp (Π nat λ _ → Π nat λ _ → F nat)
add m n = step (F nat) (1 , 1) (ret (m + n))

add/cost : cmp (Π nat λ _ → Π nat λ _ → cost)
add/cost m n = (1 , 1) ⊕ 𝟘

add/cost/closed : cmp (Π nat λ _ → Π nat λ _ → cost)
add/cost/closed m n = (1 , 1)

add/cost≤add/cost/closed : ∀ m n → ◯ (add/cost m n ≤ add/cost/closed m n)
add/cost≤add/cost/closed m n u = ≤-reflexive (⊕-identityʳ (1 , 1))

add≤add/cost : ∀ m n → IsBounded nat (add m n) (add/cost m n)
add≤add/cost m n = bound/step (1 , 1) _ bound/ret

add≤add/cost/closed : ∀ m n → IsBounded nat (add m n) (add/cost/closed m n)
add≤add/cost/closed m n = bound/relax (add/cost≤add/cost/closed m n) (add≤add/cost m n)

data Tree : Set where
  leaf : val nat → Tree
  node : Tree → Tree → Tree

tree : tp pos
tree = U (meta Tree)

sum : cmp (Π tree λ _ → F nat)
sum (leaf x)     = ret x
sum (node t₁ t₂) =
  bind (F nat) (sum t₁ & sum t₂) λ (n₁ , n₂) →
    add n₁ n₂

sum/total : ∀ t → ◯ (∃ λ n → sum t ≡ ret n)
sum/total (leaf x)     u = x , refl
sum/total (node t₁ t₂) u =
  let (n₁ , ≡₁) = sum/total t₁ u
      (n₂ , ≡₂) = sum/total t₂ u
  in
  n₁ + n₂ , (
    let open ≡-Reasoning in
    begin
      (bind (F nat) (sum t₁ & sum t₂) λ (n₁ , n₂) →
        add n₁ n₂)
    ≡⟨ Eq.cong₂ (λ e₁ e₂ → bind (F nat) (e₁ & e₂) _) ≡₁ ≡₂ ⟩
      add n₁ n₂
    ≡⟨⟩
      step (F nat) (1 , 1) (ret (n₁ + n₂))
    ≡⟨ step/ext (F nat) _ (1 , 1) u ⟩
      ret (n₁ + n₂)
    ∎
  )

sum/cost : cmp (Π tree λ _ → cost)
sum/cost (leaf x)     = 𝟘
sum/cost (node t₁ t₂) =
  bind cost (sum t₁ & sum t₂) λ (n₁ , n₂) → (sum/cost t₁ ⊗ sum/cost t₂) ⊕
    add/cost/closed n₁ n₂

size : val tree → val nat
size (leaf x)     = 0
size (node t₁ t₂) = suc (size t₁ + size t₂)

depth : val tree → val nat
depth (leaf x)     = 0
depth (node t₁ t₂) = suc (depth t₁ ⊔ depth t₂)

sum/cost/closed : cmp (Π tree λ _ → cost)
sum/cost/closed t = size t , depth t

sum/cost≤sum/cost/closed : ∀ t → ◯ (sum/cost t ≤ sum/cost/closed t)
sum/cost≤sum/cost/closed (leaf x)     u = ≤-refl
sum/cost≤sum/cost/closed (node t₁ t₂) u =
  let (_ , ≡₁) = sum/total t₁ u
      (_ , ≡₂) = sum/total t₂ u
  in
  begin
    sum/cost (node t₁ t₂)
  ≡⟨⟩
    (bind cost (sum t₁ & sum t₂) λ (n₁ , n₂) → (sum/cost t₁ ⊗ sum/cost t₂) ⊕
      add/cost/closed n₁ n₂)
  ≡⟨ Eq.cong₂ (λ e₁ e₂ → bind cost (e₁ & e₂) λ (n₁ , n₂) → (sum/cost t₁ ⊗ sum/cost t₂) ⊕ _) ≡₁ ≡₂ ⟩
    sum/cost t₁ ⊗ sum/cost t₂ ⊕ (1 , 1)
  ≤⟨ ⊕-monoˡ-≤ (1 , 1) (⊗-mono-≤ (sum/cost≤sum/cost/closed t₁ u) (sum/cost≤sum/cost/closed t₂ u)) ⟩
    sum/cost/closed t₁ ⊗ sum/cost/closed t₂ ⊕ (1 , 1)
  ≡⟨⟩
    (size t₁ , depth t₁) ⊗ (size t₂ , depth t₂) ⊕ (1 , 1)
  ≡⟨⟩
    size t₁ + size t₂ + 1 , depth t₁ ⊔ depth t₂ + 1
  ≡⟨ Eq.cong₂ _,_ (N.+-comm _ 1) (N.+-comm _ 1) ⟩
    suc (size t₁ + size t₂) , suc (depth t₁ ⊔ depth t₂)
  ≡⟨⟩
    sum/cost/closed (node t₁ t₂)
  ∎
    where open ≤-Reasoning

sum≤sum/cost : ∀ t → IsBounded nat (sum t) (sum/cost t)
sum≤sum/cost (leaf x)     = bound/ret
sum≤sum/cost (node t₁ t₂) =
  bound/bind (sum/cost t₁ ⊗ sum/cost t₂) _ (bound/par (sum≤sum/cost t₁) (sum≤sum/cost t₂)) λ (n₁ , n₂) →
    add≤add/cost/closed n₁ n₂

sum≤sum/cost/closed : ∀ t → IsBounded nat (sum t) (sum/cost/closed t)
sum≤sum/cost/closed t = bound/relax (sum/cost≤sum/cost/closed t) (sum≤sum/cost t)


record ∃ {A : Set} (B : A → Set) : Set where
  field
    fst : A
    snd : B fst

data ⊥ : Set where

infix 3 ¬_

¬_ : Set → Set
¬ P = P → ⊥

infixr 1 _⊎_

data _⊎_ (A B : Set) : Set where
  inj₁ : A → A ⊎ B
  inj₂ : B → A ⊎ B

⊎-map : ∀ {A₁ A₂ B₁ B₂ : Set} →
        (A₁ → A₂) → (B₁ → B₂) → A₁ ⊎ B₁ → A₂ ⊎ B₂
⊎-map f g (inj₁ x) = inj₁ (f x)
⊎-map f g (inj₂ y) = inj₂ (g y)

record Raw-monad (M : Set → Set) : Set₁ where
  constructor mk
  infixl 5 _>>=_
  field
    return : ∀ {A} → A → M A
    _>>=_  : ∀ {A B} → M A → (A → M B) → M B

  map : ∀ {A B} → (A → B) → M A → M B
  map f x = x >>= λ x → return (f x)

open Raw-monad ⦃ … ⦄

-- Id is included in order to make run overloaded.

record Id (A : Set) : Set where
  field
    run : A

open Id

infix 3 ¬¬_

record ¬¬_ (A : Set) : Set where
  field
    run : ¬ ¬ A

open ¬¬_

instance

  double-negation-monad : Raw-monad ¬¬_
  run (Raw-monad.return double-negation-monad x)   = λ f → f x
  run (Raw-monad._>>=_  double-negation-monad x f) =
    λ ¬b → run x (λ a → run (f a) ¬b)

excluded-middle : {A : Set} → ¬¬ (A ⊎ ¬ A)
run excluded-middle ¬[a⊎¬a] = ¬[a⊎¬a] (inj₂ λ a → ¬[a⊎¬a] (inj₁ a))

postulate

  A    : Set
  P    : Set → Set
  _⇓_  : P A → A → Set
  _⇑   : P A → Set
  ¬⇓→⇑ : {x : P A} → ¬ (∃ λ y → x ⇓ y) → x ⇑

¬¬[⇓⊎⇑] : (x : P A) → ¬ ¬ ((∃ λ y → x ⇓ y) ⊎ x ⇑)
¬¬[⇓⊎⇑] x = run (map (⊎-map (λ x → x) ¬⇓→⇑) excluded-middle)


{-

This file contains:

- Definition of set quotients

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

open import Cubical.Core.Primitives

-- Set quotients as a higher inductive type:
data _/_ {ℓ ℓ'} (A : Type ℓ) (R : A → A → Type ℓ') : Type (ℓ-max ℓ ℓ') where
  [_] : (a : A) → A / R
  eq/ : (a b : A) → (r : R a b) → [ a ] ≡ [ b ]
  squash/ : (x y : A / R) → (p q : x ≡ y) → p ≡ q


-- {-# OPTIONS -vtc.with.strip:60 -v tc.lhs.top:50 #-}

open import Agda.Builtin.Nat
open import Agda.Builtin.Equality

postulate trustMe : {A : Set} (x y : A) → x ≡ y

data Fin : Nat → Set where
  fzero : ∀ n → Fin (suc n)
  fsuc  : ∀{n} → Fin n → Fin (suc n)

test1 : ∀{n} → Fin n → Nat
test1 (fzero _) = 0
test1 {.(suc n)} (fsuc {n} i) with Fin zero
... | q = {!.!}
-- Current & expected expansion:
-- test1 {.(suc n)} (fsuc {n} i) | q = {!!}

test2 : ∀{n} → Fin n → Nat
test2 (fzero _) = 0
test2 {.(suc n)} (fsuc {n} i) with trustMe n zero
... | refl = {!.!}
-- Current & expected expansion:
-- test2 {.1} (fsuc {.0} i) | refl = {!!}

-- The test cases below do not yet work correctly, but are included
-- here as documentation of the current behaviour of Agda. does not
-- work correctly yet.

test3 : ∀{n} → Fin n → Nat
test3 (fzero _) = 0
test3 {m} (fsuc {n} i) with Fin zero
... | q = {!.!}
-- Current expansion:
-- test3 {.(suc _)} (fsuc {_} i) | q = {!!}
-- Expected expansion:
-- test3 {.(suc n)} (fsuc {n} i) | q = { }

test4 : ∀{n} → Fin n → Nat
test4 (fzero _) = 0
test4 {_} (fsuc {n} i) with Fin zero
... | q = {!.!}
-- Current expansion:
-- test4 {.(suc _)} (fsuc {_} i) | q = {!!}
-- Expected expansion:
-- test4 {_} (fsuc {n} i) | q = {!!}

test5 : ∀{n : Nat} → Fin n → Nat → Nat
test5 (fzero _) _ = 0
test5 {.(suc n)} (fsuc {n} i) m with trustMe m n
... | refl = {!.!}
-- Current expansion:
-- test5 {.(suc n)} (fsuc {n} i) n | refl = {!!}
-- Expected expansion: one of the following:
-- test5 {.(suc n)} (fsuc {n} i) .n | refl = {!!}
-- test5 {.(suc m)} (fsuc {.m} i) m | refl = {!!}

test6 : Nat → ∀{n : Nat} → Fin n → Nat
test6 _ (fzero _) = 0
test6 m {.(suc n)} (fsuc {n} i) with trustMe m n
... | refl = {!.!}
-- Current expansion:
-- test6 m {.(suc m)} (fsuc {.m} i) | refl = {!!}
-- This one is actually good, we should be sure fixing the above
-- examples doesn't change the output here!


{-# OPTIONS --profile=interactive #-}
open import Common.Prelude
open import Common.Equality

`1     = suc zero
`2     =     `1 +     `1
`4     =     `2 +     `2
`8     =     `4 +     `4
`16    =     `8 +     `8
`32    =    `16 +    `16
`64    =    `32 +    `32
`128   =    `64 +    `64
`256   =   `128 +   `128
`512   =   `256 +   `256
`1024  =   `512 +   `512
`2048  =  `1024 +  `1024
`4096  =  `2048 +  `2048
`8192  =  `4096 +  `4096
`16384 =  `8192 +  `8192
`32768 = `16384 + `16384
`65536 = `32768 + `32768

prf8 : `8 ≡ 8
prf8 = refl

prf65k : `65536 ≡ 65536
prf65k = refl


------------------------------------------------------------------------
-- Operations on and properties of decidable relations
------------------------------------------------------------------------

module Relation.Nullary.Decidable where

open import Data.Empty
open import Data.Function
open import Data.Bool
open import Data.Product hiding (map)
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality

decToBool : ∀ {P} → Dec P → Bool
decToBool (yes _) = true
decToBool (no  _) = false

True : ∀ {P} → Dec P → Set
True Q = T (decToBool Q)

False : ∀ {P} → Dec P → Set
False Q = T (not (decToBool Q))

witnessToTruth : ∀ {P} {Q : Dec P} → True Q → P
witnessToTruth {Q = yes p} _  = p
witnessToTruth {Q = no  _} ()

map : ∀ {P Q} → P ⇔ Q → Dec P → Dec Q
map eq (yes p) = yes (proj₁ eq p)
map eq (no ¬p) = no (¬p ∘ proj₂ eq)

fromYes : ∀ {P} → P → Dec P → P
fromYes _ (yes p) = p
fromYes p (no ¬p) = ⊥-elim (¬p p)

fromYes-map-commute :
  ∀ {P Q p q} (eq : P ⇔ Q) (d : Dec P) →
  fromYes q (map eq d) ≡ proj₁ eq (fromYes p d)
fromYes-map-commute         _ (yes p) = refl
fromYes-map-commute {p = p} _ (no ¬p) = ⊥-elim (¬p p)


{- formatted printing like printf, except type-safe (as proposed
   in "Cayenne -- a language with dependent types" by Augustsson).

   The types of the rest of the arguments are computed from the
   format string.  -}
module string-format where

open import char
open import eq
open import list
open import nat

open import nat-to-string
open import string

{- We will first convert the format string to the following type,
   so we can avoid default cases in the definition of format-th
   (cf. string-format-issue.agda). -}
data format-d : Set where
  format-nat : format-d → format-d
  format-string : format-d → format-d
  not-format : (c : char) → format-d → format-d
  empty-format : format-d

format-cover : 𝕃 char → format-d
format-cover ('%' :: 'n' :: s) = format-nat (format-cover s)
format-cover ('%' :: 's' :: s) = format-string (format-cover s)
format-cover (c :: s) = not-format c (format-cover s)
format-cover [] = empty-format

format-th : format-d → Set
format-th (format-nat v) = ℕ → format-th v
format-th (format-string v) = string → format-th v
format-th (not-format c v) = format-th v
format-th empty-format = string

format-t : string → Set
format-t s = format-th (format-cover (string-to-𝕃char s))

format-h : 𝕃 char → (d : format-d) → format-th d
format-h s (format-nat v) = λ n → format-h (s ++ (string-to-𝕃char (ℕ-to-string n))) v
format-h s (format-string v) = λ s' → format-h (s ++ (string-to-𝕃char s')) v
format-h s (not-format c v) = format-h (s ++ [ c ] ) v
format-h s empty-format = 𝕃char-to-string s

format : (f : string) → format-t f
format f = format-h [] (format-cover (string-to-𝕃char f))

format-type-test : Set
format-type-test = format-t "%n% of the %ss are in the %s %s"

format-test : string
format-test = format "%n% of the %ss are in the %s %s" 25 "dog" "toasty" "doghouse"

format-test-lem : format-test ≡ "25% of the dogs are in the toasty doghouse"
format-test-lem = refl


------------------------------------------------------------------------------
-- Induction principles for the total natural numbers inductive predicate
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module FOT.FOTC.Data.Nat.Type where

open import FOTC.Base hiding ( succ₁ )

------------------------------------------------------------------------------
-- We define succ₁ outside an abstract block.

succ₁ : D → D
succ₁ n = succ · n

module ConstantAndUnaryFunction where

  -- N using the constant succ.
  data N : D → Set where
    nzero : N zero
    nsucc : ∀ {n} → N n → N (succ · n)

  N-ind : (A : D → Set) →
          A zero →
          (∀ {n} → A n → A (succ · n)) →
          ∀ {n} → N n → A n
  N-ind A A0 h nzero      = A0
  N-ind A A0 h (nsucc Nn) = h (N-ind A A0 h Nn)

  -- N using the unary function succ₁.
  data N₁ : D → Set where
    nzero₁ : N₁ zero
    nsucc₁ : ∀ {n} → N₁ n → N₁ (succ₁ n)

  N-ind₁ : (A : D → Set) →
           A zero →
           (∀ {n} → A n → A (succ₁ n)) →
           ∀ {n} → N₁ n → A n
  N-ind₁ A A0 h nzero₁      = A0
  N-ind₁ A A0 h (nsucc₁ Nn) = h (N-ind₁ A A0 h Nn)

  ----------------------------------------------------------------------------
  -- From N/N₁ to N₁/N.

  N→N₁ : ∀ {n} → N n → N₁ n
  N→N₁ nzero          = nzero₁
  N→N₁ (nsucc {n} Nn) = nsucc₁ (N→N₁ Nn)

  N₁→N : ∀ {n} → N₁ n → N n
  N₁→N nzero₁           = nzero
  N₁→N (nsucc₁ {n} N₁n) = nsucc (N₁→N N₁n)

  ----------------------------------------------------------------------------
  -- From N-ind → N-ind₁.
  N-ind₁' : (A : D → Set) →
            A zero →
            (∀ {n} → A n → A (succ₁ n)) →
            ∀ {n} → N₁ n → A n
  N-ind₁' A A0 h Nn₁ = N-ind A A0 h' (N₁→N Nn₁)
    where
    h' : ∀ {n} → A n → A (succ · n)
    h' {n} An = h An

  ----------------------------------------------------------------------------
  -- From N-ind₁ → N-ind.
  N-ind' : (A : D → Set) →
           A zero →
           (∀ {n} → A n → A (succ · n)) →
           ∀ {n} → N n → A n
  N-ind' A A0 h Nn = N-ind₁ A A0 h' (N→N₁ Nn)
    where
    h' : ∀ {n} → A n → A (succ₁ n)
    h' {n} An = h An

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

module AdditionalHypotheis where

  data N : D → Set where
    nzero : N zero
    nsucc : ∀ {n} → N n → N (succ₁ n)

  -- The induction principle generated by Coq 8.4pl4 when we define
  -- the data type N in Prop.
  N-ind₁ : (A : D → Set) →
           A zero →
           (∀ {n} → N n → A n → A (succ₁ n)) →
           ∀ {n} → N n → A n
  N-ind₁ A A0 h nzero      = A0
  N-ind₁ A A0 h (nsucc Nn) = h Nn (N-ind₁ A A0 h Nn)

  -- The induction principle removing the hypothesis N n from the
  -- inductive step (see Martin-Löf 1971, p. 190).
  N-ind₂ : (A : D → Set) →
            A zero →
           (∀ {n} → A n → A (succ₁ n)) →
           ∀ {n} → N n → A n
  N-ind₂ A A0 h nzero      = A0
  N-ind₂ A A0 h (nsucc Nn) = h (N-ind₂ A A0 h Nn)

  ----------------------------------------------------------------------------
  -- N-ind₂ from N-ind₁.
  N-ind₂' : (A : D → Set) →
            A zero →
            (∀ {n} → A n → A (succ₁ n)) →
            ∀ {n} → N n → A n
  N-ind₂' A A0 h = N-ind₁ A A0 (λ _ → h)

  ----------------------------------------------------------------------------
  -- N-ind₁ from N-ind₂.
  N-ind₁' : (A : D → Set) →
            A zero →
            (∀ {n} → N n → A n → A (succ₁ n)) →
            ∀ {n} → N n → A n
  N-ind₁' A A0 h {n} Nn = ∧-proj₂ (N-ind₂ B B0 h' Nn)
    where
    B : D → Set
    B n = N n ∧ A n

    B0 : B zero
    B0 = nzero , A0

    h' : ∀ {m} → B m → B (succ₁ m)
    h' (Nm , Am) = nsucc Nm , h Nm Am

------------------------------------------------------------------------------
-- References
--
-- Martin-Löf, P. (1971). Hauptsatz for the Intuitionistic Theory of
-- Iterated Inductive Definitions. In: Proceedings of the Second
-- Scandinavian Logic Symposium. Ed. by Fenstad,
-- J. E. Vol. 63. Studies in Logic and the Foundations of
-- Mathematics. North-Holland Publishing Company, pp. 179–216.


{-# OPTIONS --cubical --no-import-sorts --allow-unsolved-metas #-}

module SyntheticReals where

open import Agda.Primitive renaming (_⊔_ to ℓ-max; lsuc to ℓ-suc; lzero to ℓ-zero)

private
  variable
    ℓ ℓ' ℓ'' : Level

open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ; assoc to ∙-assoc)
open import Cubical.Structures.CommRing
open import Cubical.Relation.Nullary.Base -- ¬_
open import Cubical.Relation.Binary.Base -- Rel
open import Cubical.Data.Sum.Base renaming (_⊎_ to infixr 4 _⊎_)
open import Cubical.Data.Sigma.Base renaming (_×_ to infixr 4 _×_)
open import Cubical.Data.Empty renaming (elim to ⊥-elim) -- `⊥` and `elim`
-- open import Cubical.Structures.Poset
open import Cubical.Foundations.Function

open import Function.Base using (_∋_)
-- open import Function.Reasoning using (∋-syntax)
open import Function.Base using (it) -- instance search

open import Utils
open import MoreLogic
open MoreLogic.Reasoning
open MoreLogic.Properties
open import MoreAlgebra
open MoreAlgebra.Definitions
open MoreAlgebra.Consequences
open import Bundles
-- open MoreAlgebra.Properties.Group

-- https://www.cs.bham.ac.uk/~abb538/thesis.pdf
-- Booij 2020 - Analysis in Univalent Type Theory

-- Lemma 4.1.6.
import Properties.ConstructiveField

-- Lemma 4.1.11.
import Properties.AlmostOrderedField

-- Lemma 4.1.12. An ordered field (F, 0, 1, +, · , min, max, <) is a constructive field (F, 0, 1, +, · , #).
lemma-4-1-12 :
  -- NOTE: we do a slightly different thing here
  ∀{ℓ ℓ'} (OF : OrderedField {ℓ} {ℓ'}) →
  let open OrderedField OF
  ----------------------------------------------------
  in (IsConstructiveField 0f 1f _+_ _·_ -_ _#_ _⁻¹ᶠ)
lemma-4-1-12 {ℓ} {ℓ'} OF = let -- NOTE: for mentioning the ℓ and ℓ' and not taking them as new "variables" we bring them into scope
  open OrderedField OF
  in record -- We need to show that + is #-extensional, and that # is tight.
   { OrderedField OF
   ; isApartnessRel  = #'-isApartnessRel <-isStrictPartialOrder -- NOTE: We've proved this before

     -- First, assume w + x # y + z. We need to show w # y ∨ x # z.
   ; +-#-extensional = λ where
                       -- Consider the case w + x < y + z, so that we can use (†) to obtain w < y ∨ x < z,
                       --   which gives w # y ∨ x # z in either case.
                       w x y z (inl w+x<y+z) → case +-<-extensional _ _ _ _ w+x<y+z of (
                         (_ → (w # y) ⊎ (x # z)) ∋ λ -- NOTE: here we had to add a (return-)type annotation to the λ
                         { (inl w<y) → inl (inl w<y)
                         ; (inr x<z) → inr (inl x<z)
                         })
                       -- The case w + x > y + z is similar.
                       w x y z (inr y+z<w+x) → case  +-<-extensional _ _ _ _ y+z<w+x of (
                         (_ → (w # y) ⊎ (x # z)) ∋ λ
                         { (inl y<w) → inl (inr y<w)
                         ; (inr z<x) → inr (inr z<x)
                         })

     -- Tightness follows from the fact that ≤ is antisymmetric, combined with the fact
     --   that ¬(P ∨ Q) is equivalent to ¬P ∧ ¬Q.
   ; #-tight         = λ x y ¬[x<y]⊎¬[y<x] → let (¬[x<y] , ¬[y<x]) = deMorgan₂' ¬[x<y]⊎¬[y<x]
                                             in  ≤-antisym _ _ ¬[y<x] ¬[x<y]
   }

-- We will mainly be concerned with ordered fields, as opposed to the more general con-
-- structive fields. This is because the Archimedean property can be phrased straightforwardly
-- for ordered fields, as in Section 4.3, and because the ordering relation allows us to define loca-
-- tors, as in Chapter 6.
--
-- We have defined ordered fields, which capture the algebraic structure of the real numbers.

-- 4.2 Rationals
-- ...
-- NOTE: we have in cubical
--   import Cubical.HITs.Rationals.HITQ
--     ℚ as a higher inductive type
--   import Cubical.HITs.Rationals.QuoQ
--     ℚ as a set quotient of ℤ × ℕ₊₁ (as in the HoTT book)
--   import Cubical.HITs.Rationals.SigmaQ
--     ℚ as the set of coprime pairs in ℤ × ℕ₊₁

import Cubical.HITs.Rationals.QuoQ renaming (ℚ to ℚ-Carrier)

postulate
  ℚℓ : Level
  ℚOF : OrderedField {ℓ-zero} {ℚℓ}
{-
ℚ = λ where
  .OrderedField.Carrier        → ℚ-Carrier
  .OrderedField.0f             → 0
  .OrderedField.1f             → 1
  .OrderedField._+_            → _+_
  .OrderedField.-_             → -_
  .OrderedField._·_            → _*_
  .OrderedField.min            → {!!}
  .OrderedField.max            → {!!}
  .OrderedField._<_            → {!!}
  .OrderedField.<-isProp       → {!!}
  .OrderedField._⁻¹ᶠ           → {!!}
  .OrderedField.isOrderedField → {!!}
-}

-- 4.3 Archimedean property
--
-- We now define the notion of Archimedean ordered fields. We phrase this in terms of a certain
-- interpolation property, that can be defined from the fact that there is a unique morphism of
-- ordered fields from the rationals to every ordered field.

-- Lemma 4.3.3. For every ordered field (F, 0 F , 1 F , + F , · F , min F , max F , < F ), there is a unique morphism
-- i of ordered fields from the rationals to F . Additionally, i preserves < in the sense that for every q, r : Q
--   q < r ⇒ i (q) < F i (r ).

-- ∃! : ∀ {ℓ ℓ'} (A : Type ℓ) (B : A → Type ℓ') → Type (ℓ-max ℓ ℓ')
-- ∃! A B = isContr (Σ A B)

-- isContr' A = Σ[ x ∈ A ] (∀ y → x ≡ y)

ℚ-IsInitialObject : ∀(OF : OrderedField {ℓ} {ℓ'}) → isContr (OrderedFieldMor ℚOF OF)
ℚ-IsInitialObject OF = {!!} , {!!}

-- Definition 4.3.5. Let (F, 0 F , 1 F , + F , · F , min F , max F , < F ) be an ordered field, so that we get a
-- canonical morphism i : Q → F of ordered fields, as in Lemma 4.3.3. We say the ordered field
-- (F, 0 F , 1 F , + F , · F , min F , max F , < F ) is Archimedean if
--   (∀x, y : F )(∃q : Q)x < i (q) < y.

IsArchimedian : OrderedField {ℓ} {ℓ'} → Type (ℓ-max ℓ ℓ')
IsArchimedian OF = let (orderedfieldmor i _) = fst (ℚ-IsInitialObject OF)
                       open OrderedField OF
                       ℚ = OrderedField.Carrier ℚOF
                   in ∀ x y → ∃[ q ∈ ℚ ] (x < i q) × (i q < y)

-- If the ordered field is clear from the context, we will identify rationals q : Q with their in-
-- clusion i (q) in the ordered field, so that we may also say that (F, 0 F , 1 F , + F , · F , min F , max F , < F )
-- is Archimedean if
-- (∀x, y : F )(∃q : Q)x < q < y.

-- Example 4.3.6. In an Archimedean ordered field, all numbers are bounded by rationals. That
-- is, for a given x : F , there exist q, r : Q with q < x < r .

Example-4-3-6 : (OF : OrderedField {ℓ} {ℓ'})
              → IsArchimedian OF
              → let open OrderedField OF renaming (Carrier to F)
                    (orderedfieldmor i _) = fst (ℚ-IsInitialObject OF)
                    ℚ = OrderedField.Carrier ℚOF
                in ∀(x : F) → (∃[ q ∈ ℚ ] i q < x) × (∃[ r ∈ ℚ ] x < i r)
-- This follows from applying the Archimedean property to x − 1 < x and x < x + 1.
Example-4-3-6 OF isArchimedian = {!!}

-- 4.4 Cauchy completeness of real numbers
--
-- We focus on Cauchy completeness, rather than Dedekind or Dedekind-MacNeille completeness,
-- as we will focus on the computation of digit expansions, for which Cauchy completeness suffices.

-- In order to state that an ordered field is Cauchy complete, we need to define when sequences
-- are Cauchy, and when a sequence has a limit. We also take the opportunity to define
-- the set of Cauchy reals in Definition 4.4.9. Surprisingly, this ordered field cannot be shown to
-- be Cauchy complete.

-- NOTE: in the following we make use of ℚ⁺ a few times. Maybe this should be a primitive?

-- Fix an ordered field (F, 0 F , 1 F , + F , · F , min F , max F , < F ).
module _ (OF : OrderedField {ℓ} {ℓ'}) where
  open OrderedField OF renaming (Carrier to F)
  -- module ℚ = OrderedField ℚ
  open OrderedField ℚOF using () renaming (_<_ to _<ᵣ_; 0f to 0ᵣ)
  ℚ = OrderedField.Carrier ℚOF
  iᵣ = OrderedFieldMor.fun (fst (ℚ-IsInitialObject OF))
  open import Data.Nat.Base using (ℕ) renaming (_≤_ to _≤ₙ_)

  -- We get a notion of distance, given by the absolute value as
  --   |x − y| := max F (x − y, −(x − y)).

  distance : ∀(x y : F) → F
  distance x y = max (x - y) (- (x - y))

  -- Consider a sequence x : N → F of elements of F . Classically, we may state that x is Cauchy as
  -- (∀ε : Q + )(∃N : N)(∀m, n : N)m, n ≥ N ⇒ |x m − x n | < ε,
  IsCauchy : (x : ℕ → F) → Type (ℓ-max ℓ' ℚℓ)
  IsCauchy x = ∀(ε : ℚ) → 0ᵣ <ᵣ ε → ∃[ N ∈ ℕ ] ∀(m n : ℕ) → N ≤ₙ m → N ≤ₙ n → distance (x m) (x n) < iᵣ ε

  -- We can interpret the quantifiers as in Definition 2.4.5.
  -- NOTE: this is the case, since `∃ A B = ∥ Σ A B ∥`

  -- Following a propositions-as-types interpretation, we may also state that x is Cauchy as the
  -- structure
  -- (Πε : Q + )(ΣN : N)(Πm, n : N)m, n ≥ N → |x m − x n | < ε.

  -- The dependent sum represents a choice of index N for every error ε, and so we have arrived at the following definition.

  -- Definition 4.4.1.
  -- For a sequence of reals x : N → F , a a modulus of Cauchy convergence is a map M : Q + → N such that
  -- (∀ε : Q + )(∀m, n : N)m, n ≥ M (ε) ⇒ |x m − x n | < ε.

  -- NOTE: do we already call these x "reals" ?
  -- NOTE: we are using the Modulus-type `((y : ℚ) → {{0ᵣ <ᵣ y}} → ℕ)` a few times and might abbreviate it

  IsModulusOfCauchyConvergence : (x : ℕ → F) → (M : ((y : ℚ) → {{0ᵣ <ᵣ y}} → ℕ)) → Type (ℓ-max ℓ' ℚℓ)
  IsModulusOfCauchyConvergence x M = ∀(ε : ℚ) → (p : 0ᵣ <ᵣ ε) → ∀(m n : ℕ)
                                   → let instance _ = p
                                     in M ε ≤ₙ m → M ε ≤ₙ n → distance (x m) (x n) < iᵣ ε

  -- In constructive mathematics, we typically use such sequences with modulus, for example,
  -- because they can sometimes be used to compute limits of Cauchy sequences, avoiding choice axioms.

  -- Definition 4.4.2.
  -- A number l : F is the limit of a sequence x : N → F if the sequence
  -- converges to l in the usual sense:
  --   (∀ε : Q + )(∃N : N)(∀n : N)n ≥ N ⇒ |x n − l | < ε.

  IsLimit : (x : ℕ → F) → (l : F) → Type (ℓ-max ℓ' ℚℓ)
  IsLimit x l = ∀(ε : ℚ) → (0ᵣ <ᵣ ε) → ∃[ N ∈ ℕ ] ∀(n : ℕ) → N ≤ₙ n → distance (x n) l < iᵣ ε

  -- Remark 4.4.3. We do not consider the statement of convergence in propositions-as-types
  --
  --   (Πε : Q + )(ΣN : N)(Πn : N)n ≥ N → |x n − l | < ε,
  --
  -- because if the sequence has a modulus of Cauchy convergence M, then λε.M (ε/2) is a
  -- modulus of convergence to the limit l, so that we get an element of the above type.

  -- Definition 4.4.4.
  -- The ordered field (F, 0 F , 1 F , + F , · F , min F , max F , < F ) is said to be Cauchy complete
  -- if for every sequence x with modulus of Cauchy convergence M, we have a limit of x.
  -- In other words, an ordered field is Cauchy complete iff from a sequence–modulus pair (x, M), we can compute a limit of x.

  IsCauchyComplete : Type (ℓ-max (ℓ-max ℓ ℓ') ℚℓ)
  IsCauchyComplete = (x : ℕ → F)
                   → (M : ((y : ℚ) → {{0ᵣ <ᵣ y}} → ℕ))
                   → IsModulusOfCauchyConvergence x M
                   → Σ[ l ∈ F ] IsLimit x l

  -- For the remainder of this section, additionally assume that F is Archimedean.
  module _ (isArchimedian : IsArchimedian OF) where

    -- Lemma 4.4.5.
    -- The type of limits of a fixed sequence x : N → F is a proposition.
    Lemma-4-4-5 : ∀(x : ℕ → F) → isProp (Σ[ l ∈ F ] IsLimit x l)
    -- Proof. This can be shown using the usual proof that limits are unique in Archimedean ordered fields, followed by an application of Lemma 2.6.20.
    Lemma-4-4-5 x = {!!}

    -- Corollary 4.4.6.
    -- Fix a given sequence x : N → F . Suppose that we know that there exists a
    -- limit of the sequence. Then we can compute a limit of the sequence.
    Corollary-4-4-6 : ∀(x : ℕ → F) → (∃[ l ∈ F ] IsLimit x l) → Σ[ l ∈ F ] IsLimit x l
    -- Proof. By applying the induction principle of propositional truncations of Definition 2.4.3.
    Corollary-4-4-6 x p = {!!} , {!!}

    -- Corollary 4.4.7.
    -- Fix a given sequence x : N → F . Suppose that, from a modulus of Cauchy
    -- convergence, we can compute a limit of the sequence. Then from the existence of the modulus of
    -- Cauchy convergence we can compute a limit of the sequence.
    Corollary-4-4-7 : (x : ℕ → F)
                    → ( (M : ((y : ℚ) → {{0ᵣ <ᵣ y}} → ℕ))
                      → (isMCC : IsModulusOfCauchyConvergence x M)
                      → Σ[ l ∈ F ] IsLimit x l
                      )
                    -----------------------------------------------------------------------
                    → ∃[ M ∈ ((y : ℚ) → {{0ᵣ <ᵣ y}} → ℕ) ] IsModulusOfCauchyConvergence x M
                    → Σ[ l ∈ F ] IsLimit x l
    -- Proof. By applying the induction principle of propositional truncations of Definition 2.4.3.
    Corollary-4-4-7 x f p = {!!}

    -- We can thus compute the limit of x : N → F as the number lim(x, p), where p is a proof
    -- that the limit of x exists. We will rather use the more traditional notation lim n→∞ x n for this
    -- number.

    -- Example 4.4.8 (Exponential function).
    -- In a Cauchy complete Archimedean ordered field, we can define an exponential function exp : F → F by
    --
    --    exp(x) = Σ_{k=0}^{∞} (xᵏ) / (k!)
    --
    -- For a given input x, we obtain the existence of a modulus of Cauchy convergence for the output from boundedness of
    -- x, that is, from the fact that (∃q, r : Q) q < x < r .

    exp : F → F
    exp x = {!!}

    Example-4-4-8 : ∀(x : F) → ∃[ M ∈ ((y : ℚ) → {{0ᵣ <ᵣ y}} → ℕ) ] IsModulusOfCauchyConvergence {!!} M
    Example-4-4-8 x with Example-4-3-6 OF isArchimedian x
    ... | q' , r' = let q : ∃[ q ∈ ℚ ] iᵣ q < x
                        q = q'
                        r : ∃[ r ∈ ℚ ] x < iᵣ r
                        r = r'
                    in {!!}

    -- The point of this work is that, because we have a single language for properties and struc-
    -- ture, we can see more precisely what is needed for certain computations. In the above example,
    -- we explicitly do not require that inputs come equipped with a modulus of Cauchy convergence,
    -- but rather that there exists such a modulus. On the one hand, we do need a modulus to obtain
    -- the limit, but as the limit value is independent of the chosen modulus, existence of such a
    -- modulus suffices.

    -- Definition 4.4.9. The Cauchy reals ℝC is the collection of rational sequences equipped with
    -- a modulus of Cauchy convergence, quotiented (as in Section 2.7) by an equivalence relation
    -- that relates two sequence–modulus pairs (x, M) and (y, N ) iff
    -- (∀ε : Q + ) x M (ε/4) − y M (ε/4) < ε.

    ℝC : {!!}
    ℝC = {!!}

    -- The Cauchy reals form an Archimedean ordered field in a natural way. The natural strategy
    -- to prove that the Cauchy reals are Cauchy complete, perhaps surprisingly, does not work, and
    -- in some constructive foundations the Cauchy completeness of the Cauchy reals is known to
    -- be false [68].
    --
    -- ...
    --
    -- An alternative interpretation of the non-completeness of the Cauchy reals is that the sequential
    -- definition of completeness ought to be amended [80].

    -- NOTE: now we're back to https://github.com/agda/cubical/issues/286 ?

-- Auke would send an email to Ayberk Tosun and Martin Escardo
-- I could prepare a PR for the cubical standard library
-- the cubical standard library does not supersede the standard library
--   using setoids need not to be a shortcoming but can be a concious decision
-- Postulating the rationals as an ordered field for which there exists a unique morphism to every other ordered field should be sufficient
--   (we went a little bit into the coprime and quotient construction of the rational numbers)
-- I can prepare some statements that I would like to formalize (with just guessing) to have a more concrete guidance for the necessary detail of a real number formulation
-- the impredicative `--prop` is not equivalent to hProp
--   therefore, one should make props explicit arguments or at least be aware of them at all times

{-

in a world where we "just have" real numbers in Agda, I would do the following:

Adjoint theory
  Vector space
  normed space
  Banach space
  Inner Product space
  unbounded Linear operator
  adjoint linear operator
  orthogonal decomposition of Banach spaces
  inf-sup conditions
  lax-milgram

Local Multilinear Algebra
  euclidean space
  linear representation of hodge star
  locally euclidean

Global Multilinear Algebra
  locally euclidean topological space
  chart representation

-}


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

module Categories.Category.Instance.Properties.Setoids.Choice where

open import Categories.Category using (Category)
open import Categories.Category.Exact using (Exact)
open import Categories.Category.Instance.Setoids using (Setoids)

open import Data.Product using (∃; proj₁; proj₂; _,_; Σ-syntax; _×_; -,_; map; zip; swap; map₂)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Function.Equality as SΠ using (Π; _⇨_) renaming (id to ⟶-id; _∘_ to _∘⟶_)

open import Level
open import Relation.Binary using (Setoid; Rel; REL; IsEquivalence)
import Relation.Binary.Reasoning.Setoid as SR

open import Data.Nat.Base
import Relation.Binary.PropositionalEquality.Core as P

open Setoid renaming (_≈_ to [_][_≈_]; Carrier to ∣_∣) using (isEquivalence; refl; sym; trans)
open Π using (_⟨$⟩_; cong)

module _ ℓ where
  private
    S = Setoids ℓ ℓ
    open Category S hiding (_≈_)
    module S = Category S

  open import Categories.Category.Instance.Properties.Setoids.Exact
  open import Categories.Object.InternalRelation using (Relation)

  -- Presentation axiom, aka CoSHEP (http://nlab-pages.s3.us-east-2.amazonaws.com/nlab/show/presentation+axiom)
  record CoSHEP (A : Setoid ℓ ℓ) : Set (Level.suc ℓ) where
    field
      {P} : Setoid ℓ ℓ
      pre   : P ⇒ A
      surj  : SSurj ℓ pre
      split : {X : Setoid ℓ ℓ} (f : X ⇒ P) → SSurj ℓ f → Σ[ g ∈ ∣ P ⇨ X ∣ ] [ P ⇨ P ][ f ∘ g ≈ id ]

  Setoid-CoSHEP : (A : Setoid ℓ ℓ) → CoSHEP A
  Setoid-CoSHEP A = record
    { P = record
           { Carrier = ∣ A ∣
           ; _≈_ = P._≡_
           ; isEquivalence = record { refl = P.refl ; sym = P.sym ; trans = P.trans }
           }
    ; pre = record
           { _⟨$⟩_ = λ x → x
           ; cong = λ {x} eq → P.subst (λ z → [ A ][ x ≈ z ]) eq (refl A)
           }
    ; surj = λ x → x , refl A
    ; split = λ {X} f surj → record
           { _⟨$⟩_ = λ y → let x , _ = surj y in x
           ; cong = λ {x}{y} x≡y → P.subst (λ z → [ X ][ proj₁ (surj x) ≈ proj₁ (surj z) ]) x≡y (refl X)
           }
           , λ {x}{y} x≡y → let z , eq = surj x in P.trans eq x≡y
    }

  entire : {A B : Setoid ℓ ℓ} → (R : Relation S A B) → Set ℓ
  entire {A} R = ∀ (x : ∣ A ∣) →  Σ[ e ∈ ∣ dom ∣ ]  [ A ][ p₁ ⟨$⟩ e ≈ x ]
    where open Relation R

  ℕ-Setoid : Setoid ℓ ℓ
  ℕ-Setoid = record { Carrier = Lift _ ℕ ; _≈_ = P._≡_  ; isEquivalence = record { refl = P.refl ; sym = P.sym ; trans = P.trans } }

  record DepChoice {A : Setoid ℓ ℓ} (R : Relation S A A) (inhb : ∣ A ∣) (ent : entire R) : Set (Level.suc ℓ) where
    open Relation R

    field
      pair   : ℕ → ∣ dom ∣
      chain  : ∀ (n : ℕ) → [ A ][ p₁ ⟨$⟩ pair (ℕ.suc n) ≈ p₂ ⟨$⟩ pair n ]

  -- Dependent choice for setoids
  Setoid-DepChoice :  {A : Setoid ℓ ℓ} (R : Relation S A A) (inhb : ∣ A ∣) (ent : entire R) → DepChoice R inhb ent
  Setoid-DepChoice {A} R inhb ent = record
    { pair  = pair
    ; chain = chain
    }
      where
        open Relation R

        pair : ℕ → ∣ dom ∣
        pair ℕ.zero = proj₁ (ent inhb)
        pair (ℕ.suc n) = let x , _ = ent (p₂ ⟨$⟩ pair n) in x

        chain : (n : ℕ) → [ A ][ p₁ ⟨$⟩ proj₁ (ent (p₂ ⟨$⟩ pair n)) ≈ p₂ ⟨$⟩ pair n ]
        chain ℕ.zero    = let _ , eq = ent (p₂ ⟨$⟩ proj₁ (ent inhb)) in eq
        chain (ℕ.suc n) = let x , eq = ent (p₂ ⟨$⟩ proj₁ (ent (p₂ ⟨$⟩ pair n))) in eq

  -- Countable choice for setoids
  ℕ-Choice :  ∀ {A : Setoid ℓ ℓ} (f : A ⇒ ℕ-Setoid) → SSurj ℓ f → Σ[ g ∈ ∣ ℕ-Setoid ⇨ A ∣ ] [ ℕ-Setoid ⇨ ℕ-Setoid ][ f ∘ g ≈ id ]
  ℕ-Choice {A} f surj = record
    { _⟨$⟩_ = λ n → let x , eq = surj n in x
    ; cong = λ {n}{m} eq → let x , _ = surj n; y , _ = surj m in P.subst (λ m → [ A ][ proj₁ (surj n) ≈ proj₁ (surj m) ]) eq (refl A)
    }
    , λ {n}{m} n≡m → let _ , eq = surj n in P.trans eq n≡m