{-# OPTIONS --without-K --safe #-}
open import Categories.Category

-- Properties relating Initial and Terminal Objects,
-- and Product / Coproduct via op

module Categories.Object.Duality {o ℓ e} (C : Category o ℓ e) where

open Category C

open import Level

open import Categories.Morphism C
open import Categories.Object.Terminal op
open import Categories.Object.Initial C
open import Categories.Object.Product op
open import Categories.Object.Coproduct C

IsInitial⇒coIsTerminal : ∀ {X} → IsInitial X → IsTerminal X
IsInitial⇒coIsTerminal is⊥ = record
  { !        = !
  ; !-unique = !-unique
  }
  where open IsInitial is⊥

⊥⇒op⊤ : Initial → Terminal
⊥⇒op⊤ i = record
  { ⊤             = ⊥
  ; ⊤-is-terminal = IsInitial⇒coIsTerminal ⊥-is-initial
  }
  where open Initial i

coIsTerminal⇒IsInitial : ∀ {X} → IsTerminal X → IsInitial X
coIsTerminal⇒IsInitial is⊤ = record
  { !        = !
  ; !-unique = !-unique
  }
  where open IsTerminal is⊤

op⊤⇒⊥ : Terminal → Initial
op⊤⇒⊥ t = record
  { ⊥            = ⊤
  ; ⊥-is-initial = coIsTerminal⇒IsInitial ⊤-is-terminal
  }
  where open Terminal t

Coproduct⇒coProduct : ∀ {A B} → Coproduct A B → Product A B
Coproduct⇒coProduct A+B = record
  { A×B      = A+B.A+B
  ; π₁       = A+B.i₁
  ; π₂       = A+B.i₂
  ; ⟨_,_⟩    = A+B.[_,_]
  ; project₁ = A+B.inject₁
  ; project₂ = A+B.inject₂
  ; unique   = A+B.unique
  }
  where
  module A+B = Coproduct A+B

coProduct⇒Coproduct : ∀ {A B} → Product A B → Coproduct A B
coProduct⇒Coproduct A×B = record
  { A+B     = A×B.A×B
  ; i₁      = A×B.π₁
  ; i₂      = A×B.π₂
  ; [_,_]   = A×B.⟨_,_⟩
  ; inject₁ = A×B.project₁
  ; inject₂ = A×B.project₂
  ; unique  = A×B.unique
  }
  where
  module A×B = Product A×B


{-# OPTIONS --type-in-type #-}

module DescFix where

open import DescTT

aux : (C : Desc)(D : Desc)(P : Mu C -> Set)(x : [| D |] (Mu C)) -> Set
aux C id          P (con y) = P (con y) * aux C C P y
aux C (const K)   P k       = Unit
aux C (prod D D') P (s , t) = aux C D P s * aux C D' P t
aux C (sigma S T) P (s , t) = aux C (T s) P t
aux C (pi S T)    P f       = (s : S) -> aux C (T s) P (f s)

gen : (C : Desc)(D : Desc)(P : Mu C -> Set)
  (rec : (y : [| C |] Mu C) -> aux C C P y -> P (con y))
  (x : [| D |] Mu C) -> aux C D P x
gen C id          P rec (con x) = rec x (gen C C P rec x) , gen C C P rec x
gen C (const K)   P rec k       = Void
gen C (prod D D') P rec (s , t) = gen C D P rec s , gen C D' P rec t
gen C (sigma S T) P rec (s , t) = gen C (T s) P rec t
gen C (pi S T)    P rec f       = \ s -> gen C (T s) P rec (f s)

fix : (D : Desc)(P : Mu D -> Set)
  (rec : (y : [| D |] Mu D) -> aux D D P y -> P (con y))
  (x : Mu D) -> P x
fix D P rec (con x) = rec x (gen D D P rec x)

plus : Nat -> Nat -> Nat
plus (con (Ze , Void)) n = n
plus (con (Suc , m)) n = suc (plus m n)

fib : Nat -> Nat
fib = fix NatD (\ _ -> Nat) help
  where
    help : (m : [| NatD |] Nat) -> aux NatD NatD (\ _ -> Nat) m -> Nat
    help (Ze , x) a = suc ze
    help (Suc , con (Ze , _)) a = suc ze
    help (Suc , con (Suc , con n)) (fib-n , (fib-sn , a)) = plus fib-n fib-sn


{-# OPTIONS --safe #-}
module Cubical.Data.NatPlusOne.MoreNats.AssocNat.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels

open import Cubical.Data.Empty
open import Cubical.Data.Unit
open import Cubical.Data.Nat hiding (_+_)

infixl 6 _+₁_

data ℕ₊₁ : Type where
  one : ℕ₊₁
  _+₁_ : ℕ₊₁ → ℕ₊₁ → ℕ₊₁
  assoc : (a b c : ℕ₊₁) →  a +₁ (b +₁ c) ≡ a +₁ b +₁ c
  trunc : isSet ℕ₊₁

module Elim {ℓ'} {B : ℕ₊₁ → Type ℓ'}
  (one* : B one) (_+₁*_ : {m n : ℕ₊₁} → B m → B n → B (m +₁ n))
  (assoc* : {x y z : ℕ₊₁} (x' : B x) (y' : B y) (z' : B z)
            → PathP (λ i → B (assoc x y z i))
            (x' +₁* (y' +₁* z')) ((x' +₁* y') +₁* z'))
  (trunc* : (n : ℕ₊₁) → isSet (B n)) where

  f : (n : ℕ₊₁) → B n
  f one = one*
  f (m +₁ n) = f m +₁* f n
  f (assoc x y z i) = assoc* (f x) (f y) (f z) i
  f (trunc m n p q i j) =
    isOfHLevel→isOfHLevelDep 2 trunc* (f m) (f n)
    (cong f p) (cong f q) (trunc m n p q) i j

module ElimProp {ℓ'} {B : ℕ₊₁ → Type ℓ'} (BProp : {n : ℕ₊₁} → isProp (B n))
  (one* : B one) (_+₁*_ : {m n : ℕ₊₁} → B m → B n → B (m +₁ n)) where

  f : (n : ℕ₊₁) → B n
  f = Elim.f {B = B} one* _+₁*_
        (λ {x} {y} {z} x' y' z' →
          toPathP (BProp (transport (λ i → B (assoc x y z i))
          (x' +₁* (y' +₁* z'))) ((x' +₁* y') +₁* z')))
        λ n → isProp→isSet BProp

module Rec {ℓ'} {B : Type ℓ'} (BType : isSet B)
  (one* : B) (_+₁*_ : B → B → B)
  (assoc* : (a b c : B) → a +₁* (b +₁* c) ≡ (a +₁* b) +₁* c) where

  f : ℕ₊₁ → B
  f = Elim.f one* (λ m n → m +₁* n) assoc* λ _ → BType

private
  constraintNumber : ℕ → Type
  constraintNumber zero = ⊥
  constraintNumber (suc _) = Unit

  fromNat' : (n : ℕ) ⦃ _ : constraintNumber n ⦄ → ℕ₊₁
  fromNat' zero ⦃ () ⦄
  fromNat' (suc zero) = one
  fromNat' (suc (suc n)) = fromNat' (suc n) +₁ one

instance
  NumN : HasFromNat ℕ₊₁
  NumN = record { Constraint = constraintNumber ; fromNat = fromNat' }


module Structure.Numeral.Natural where

open import Lang.Instance
import      Lvl
open import Logic.Predicate
open import Logic.Propositional
open import Structure.Setoid
open import Structure.Function.Domain
open import Structure.Function
open import Structure.Operator
open import Structure.Relator
open import Structure.Setoid
open import Syntax.Function
open import Type

private variable ℓₒ ℓₗ ℓₑ ℓₗ₁ ℓₗ₂ : Lvl.Level
private variable N : Type{ℓₒ}

module _ ⦃ equiv : Equiv{ℓₑ}(N) ⦄ where
  private variable 𝟎 : N
  private variable 𝐒 : N → N
  private variable _+_ _⋅_ _^_ : N → N → N

  module _ {ℓ} (𝟎 : N) (𝐒 : N → N) where
    record Induction : Type{ℓₑ Lvl.⊔ Lvl.of(N) Lvl.⊔ Lvl.𝐒(ℓ)} where
      constructor intro
      field proof : (P : N → Type{ℓ}) ⦃ rel : UnaryRelator(P) ⦄ → P(𝟎) → ((n : N) → P(n) → P(𝐒(n))) → ((n : N) → P(n))
    induction = inst-fn Induction.proof

  record Elemental (𝟎 : N) (𝐒 : N → N) : Type{ℓₑ Lvl.⊔ Lvl.of(N)} where
    constructor intro
    field
      ⦃ 𝐒-function ⦄    : Function(𝐒)
      ⦃ 𝐒-injectivity ⦄ : Injective(𝐒)
      𝐒-positivity      : ∀{x} → (𝐒(x) ≢ 𝟎)

  record Addition ⦃ elemn : Elemental(𝟎)(𝐒) ⦄ (_+_ : N → N → N) : Type{ℓₑ Lvl.⊔ Lvl.of(N)} where
    constructor intro
    field
      ⦃ operator ⦄ : BinaryOperator(_+_)
      base : ∀{a}   → (a + 𝟎    ≡ a)
      step : ∀{a b} → (a + 𝐒(b) ≡ 𝐒(a + b))

  record Multiplication ⦃ elemn : Elemental(𝟎)(𝐒) ⦄ ⦃ addi : Addition(_+_) ⦄ (_⋅_ : N → N → N) : Type{ℓₑ Lvl.⊔ Lvl.of(N)} where
    constructor intro
    field
      ⦃ operator ⦄ : BinaryOperator(_⋅_)
      base : ∀{a}   → (a ⋅ 𝟎    ≡ 𝟎)
      step : ∀{a b} → (a ⋅ 𝐒(b) ≡ a + (a ⋅ b))

  record Exponentiation ⦃ elemn : Elemental(𝟎)(𝐒) ⦄ ⦃ addi : Addition(_+_) ⦄ ⦃ multi : Multiplication(_⋅_) ⦄ (_^_ : N → N → N) : Type{ℓₑ Lvl.⊔ Lvl.of(N)} where
    constructor intro
    field
      ⦃ operator ⦄ : BinaryOperator(_^_)
      base : ∀{a}   → (a ^ 𝟎    ≡ 𝐒(𝟎))
      step : ∀{a b} → (a ^ 𝐒(b) ≡ a ⋅ (a ^ b))

  record WeakOrdering ⦃ elemn : Elemental(𝟎)(𝐒) ⦄ {_+_ : N → N → N} ⦃ addi : Addition(_+_) ⦄ (_≤_ : N → N → Type{ℓₗ}) : Type{ℓₑ Lvl.⊔ Lvl.of(N) Lvl.⊔ ℓₗ} where
    constructor intro
    field
      ⦃ relator ⦄ : BinaryRelator(_≤_)
      proof : ∀{a b} → (a ≤ b) ↔ ∃(c ↦ a + c ≡ b)

  module _ ⦃ ind : Induction(𝟎)(𝐒) ⦄ ⦃ elem : Elemental(𝟎)(𝐒) ⦄ where
    open import Structure.Relator.Proofs
    open import Structure.Relator.Properties

    open Elemental(elem)
    open Induction(ind)

    𝟎-or-𝐒 : ∀{x} → (x ≡ 𝟎) ∨ ∃(y ↦ x ≡ 𝐒(y))
    𝟎-or-𝐒 {x} = induction(𝟎)(𝐒)
      (x ↦ (x ≡ 𝟎) ∨ ∃(y ↦ x ≡ 𝐒(y))) ⦃ [∨]-unaryRelator ⦃ rel-P = BinaryRelator.left [≡]-binaryRelator ⦄ ⦃ rel-Q = [∃]-unaryRelator ⦃ rel-P = BinaryRelator.left [≡]-binaryRelator ⦄ ⦄ ⦄
      ([∨]-introₗ (reflexivity(_≡_)))
      (x ↦ [∨]-elim
        (p ↦ [∨]-introᵣ([∃]-intro 𝟎 ⦃ congruence₁(𝐒) p ⦄))
        (\{([∃]-intro y ⦃ p ⦄) → [∨]-introᵣ([∃]-intro (𝐒(y)) ⦃ congruence₁(𝐒) p ⦄)})
      )
      x

{-
  module _ ⦃ ind : Induction(𝟎)(𝐒) ⦄ where
    import      Data.Either as Either
    open import Functional
    open import Numeral.Natural as ℕ using (ℕ)
    open import Relator.Equals renaming (_≡_ to _≡ₑ_)
    open import Structure.Relator.Proofs
    open import Syntax.Number

    from-ℕ : ℕ → N
    from-ℕ (ℕ.𝟎)    = 𝟎
    from-ℕ (ℕ.𝐒(n)) = 𝐒(from-ℕ n)
-}

  -- TODO: Also include the definition of a "naturally ordered semigroup" here

{-
  module _ (_<_ : T → T → Stmt{ℓₗ}) where
    record Minimal : Type{Lvl.of(T) Lvl.⊔ ℓₗ} where
      open From-[<][≡] (_<_) (_≡_)

      field
        ⦃ elemental ⦄ : Elemental
        [<]ₗ-𝟎 : ∀{x} → (𝟎 < x) ↔ (x ≢ 𝟎)
        [<]ᵣ-𝟎 : ∀{x} → (𝟎 ≤ x) -- Minimum in the order (TODO: Is (∀x. x≥0) neccessary? Which means (0<x)∨(0=x))
        [<]ₗ-𝐒 : ∀{x y} → (𝐒(x) < y) ↔ ((x < y)∧(𝐒(x) ≢ y)) -- TODO: Also the definition of (_≤_)?
        [<]ᵣ-𝐒 : ∀{x y} → (x < 𝐒(y)) ↔ (x ≤ y)

-}


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

open import HoTT
open import groups.Cokernel

{-
                 K
                 ↑
                 ╎ ψ₁
            q[_] ╎    ψ₂
   Coker ψ₂ ↞--- G ←------ L
      ↑          ↑
   φ₁ ╎          ╎ inject
      ↑          ↑
      H ↞------- Ker ψ₁
           φ₂

     Then, H ≃ᴳ Ker/Im.
-}

module groups.KernelImageUniqueFactorization {i j k l}
  {G : Group i} {H : Group j} {K : Group k} {L : Group l}
  (ψ₁ : G →ᴳ K) (ψ₂ : L →ᴳ G) (G-ab : is-abelian G)
  (φ₁ : Ker ψ₁ →ᴳ H) (φ₁-is-surj : is-surjᴳ φ₁)
  (φ₂ : H →ᴳ Coker ψ₂ G-ab) (φ₂-is-inj : is-injᴳ φ₂)
  (φ-comm : ∀ ker → GroupHom.f (φ₂ ∘ᴳ φ₁) ker == q[ fst ker ])
  where

  private
    module G = Group G
    module H = Group H
    module K = Group K
    module L = Group L
    module ψ₁ = GroupHom ψ₁
    module ψ₂ = GroupHom ψ₂
    module φ₁ = GroupHom φ₁
    module φ₂ = GroupHom φ₂

    φ₂' : H →ᴳ QuotGroup (im-npropᴳ ψ₂ G-ab)
    φ₂' = GroupIso.f-hom (Coker-β ψ₂ G-ab) ∘ᴳ φ₂

    abstract
      φ₂'-is-inj : is-injᴳ φ₂'
      φ₂'-is-inj = ∘-is-inj (–>-is-inj (GroupIso.f-equiv (Coker-β ψ₂ G-ab))) φ₂-is-inj

  open import groups.PropQuotUniqueFactorization
    (ker-propᴳ ψ₁) (im-npropᴳ ψ₂ G-ab) φ₁ φ₁-is-surj φ₂' φ₂'-is-inj φ-comm
  open import groups.KernelImage ψ₁ ψ₂ G-ab

  H-iso-Ker/Im : H ≃ᴳ Ker/Im
  H-iso-Ker/Im = Ker/Im-β ⁻¹ᴳ ∘eᴳ H-iso-P/Q


{-# OPTIONS --safe #-}

module Cubical.Algebra.MonoidSolver.Examples where

open import Cubical.Foundations.Prelude

open import Cubical.Algebra.Monoid.Base
open import Cubical.Algebra.CommMonoid.Base
open import Cubical.Algebra.MonoidSolver.Reflection

private
  variable
    ℓ : Level

module ExamplesMonoid (M : Monoid ℓ) where
  open MonoidStr (snd M)

  _ : ε ≡ ε
  _ = solveMonoid M

  _ : ε · ε · ε ≡ ε
  _ = solveMonoid M

  _ : ∀ x → ε · x  ≡ x
  _ = solveMonoid M

  _ : ∀ x y z → (x · y) · z ≡ x · (y · z)
  _ = solveMonoid M

  _ : ∀ x y z → z · (x · y) · ε · z ≡ z · x · (y · z)
  _ = solveMonoid M

module ExamplesCommMonoid (M : CommMonoid ℓ) where
  open CommMonoidStr (snd M)

  _ : ε ≡ ε
  _ = solveCommMonoid M

  _ : ε · ε · ε ≡ ε
  _ = solveCommMonoid M

  _ : ∀ x → ε · x  ≡ x
  _ = solveCommMonoid M

  _ : ∀ x y z → (x · z) · y ≡ x · (y · z)
  _ = solveCommMonoid M

  _ : ∀ x y  → (x · y) · y · x · (x · y) ≡ x · x · x · (y · y · y)
  _ = solveCommMonoid M


{-

ℚ is a Commutative Ring

-}
{-# OPTIONS --safe #-}
module Cubical.Algebra.CommRing.Instances.QuoQ where

open import Cubical.Foundations.Prelude

open import Cubical.Algebra.CommRing
open import Cubical.HITs.Rationals.QuoQ
  renaming (ℚ to ℚType ; _+_ to _+ℚ_; _·_ to _·ℚ_; -_ to -ℚ_)

open CommRingStr

ℚCommRing : CommRing ℓ-zero
ℚCommRing .fst = ℚType
ℚCommRing .snd .0r = 0
ℚCommRing .snd .1r = 1
ℚCommRing .snd ._+_ = _+ℚ_
ℚCommRing .snd ._·_ = _·ℚ_
ℚCommRing .snd .-_ = -ℚ_
ℚCommRing .snd .isCommRing = isCommRingℚ
  where
  abstract
    isCommRingℚ : IsCommRing 0 1 _+ℚ_ _·ℚ_ -ℚ_
    isCommRingℚ = makeIsCommRing
      isSetℚ +-assoc +-identityʳ
      +-inverseʳ +-comm ·-assoc
      ·-identityʳ (λ x y z → sym (·-distribˡ x y z)) ·-comm


{-# OPTIONS --universe-polymorphism #-}
-- {-# OPTIONS --verbose tc.constr.findInScope:15 #-}
-- {-# OPTIONS --verbose tc.term.args.ifs:15 #-}
-- {-# OPTIONS --verbose tc.checkArgs:15 #-}

module 12-constraintFamilies where

open import Level
open import Function
open import Data.Unit
open import Data.List
open import Data.Product hiding (map)
open import Relation.Binary
open import Relation.Binary.PropositionalEquality

record ConstrainedFunctor {c} (Constraint : Set → Set c)
  (f : (A : Set) → Set) : Set (suc c) where
  field
    fmap : ∀ {A B : Set} → {{cA : Constraint A}} → {{cB : Constraint B}} →
      (A → B) → f A → f B

listConstrainedFunctor : ConstrainedFunctor (const ⊤) List
listConstrainedFunctor = record { fmap = map }

postulate
  sort : {A : Set} → {{ordA : ∃ λ (_<_ : A → A → Set) → IsStrictTotalOrder {A = A} _≡_ _<_}} →
         List A → List A

⋯ : ∀ {a} {A : Set a} {{a : A}} → A
⋯ {{a}} = a

sortedListConstrainedFunctor : ConstrainedFunctor (λ A → ∃ λ (_<_ : A → A → Set) → IsStrictTotalOrder _≡_ _<_) List
sortedListConstrainedFunctor = record { fmap = λ {{x}} {{y}} → map' {{x}} {{y}} } where
  map' : {A B : Set} → {{ordA : ∃ λ (_<_ : A → A → Set) → (IsStrictTotalOrder _≡_ _<_)}} →
         {{ordB : ∃ λ (_<_ : B → B → Set) → (IsStrictTotalOrder _≡_ _<_)}} →
         (A → B) → List A → List B
  map' f = sort ∘′ map f


module Lib.List where

open import Lib.Prelude
open import Lib.Id

infix  30 _∈_
infixr 40 _::_ _++_
infix  45 _!_

data List (A : Set) : Set where
  []   : List A
  _::_ : A -> List A -> List A

{-# COMPILED_DATA List [] [] (:) #-}
{-# BUILTIN LIST List #-}
{-# BUILTIN NIL [] #-}
{-# BUILTIN CONS _::_ #-}

_++_ : {A : Set} -> List A -> List A -> List A
[]        ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)

foldr : {A B : Set} -> (A -> B -> B) -> B -> List A -> B
foldr f z [] = z
foldr f z (x :: xs) = f x (foldr f z xs)

map : {A B : Set} -> (A -> B) -> List A -> List B
map f [] = []
map f (x :: xs) = f x :: map f xs

map-id : forall {A}(xs : List A) -> map id xs ≡ xs
map-id [] = refl
map-id (x :: xs) with map id xs | map-id xs
... | .xs | refl = refl

data _∈_ {A : Set} : A -> List A -> Set where
  hd : forall {x xs}   ->           x ∈ x :: xs
  tl : forall {x y xs} -> x ∈ xs -> x ∈ y :: xs

data All {A : Set}(P : A -> Set) : List A -> Set where
  []   : All P []
  _::_ : forall {x xs} -> P x -> All P xs -> All P (x :: xs)

head : forall {A}{P : A -> Set}{x xs} -> All P (x :: xs) -> P x
head (x :: xs) = x

tail : forall {A}{P : A -> Set}{x xs} -> All P (x :: xs) -> All P xs
tail (x :: xs) = xs

_!_ : forall {A}{P : A -> Set}{x xs} -> All P xs -> x ∈ xs -> P x
xs ! hd   = head xs
xs ! tl n = tail xs ! n

tabulate : forall {A}{P : A -> Set}{xs} -> ({x : A} -> x ∈ xs -> P x) -> All P xs
tabulate {xs = []}      f = []
tabulate {xs = x :: xs} f = f hd :: tabulate (f ∘ tl)

mapAll : forall {A B}{P : A -> Set}{Q : B -> Set}{xs}(f : A -> B) ->
         ({x : A} -> P x -> Q (f x)) -> All P xs -> All Q (map f xs)
mapAll f h [] = []
mapAll f h (x :: xs) = h x :: mapAll f h xs

mapAll- : forall {A}{P Q : A -> Set}{xs} ->
          ({x : A} -> P x -> Q x) -> All P xs -> All Q xs
mapAll- {xs = xs} f zs with map id xs | map-id xs | mapAll id f zs
... | .xs | refl | r = r

infix 20 _⊆_

data _⊆_ {A : Set} : List A -> List A -> Set where
  stop : [] ⊆ []
  drop : forall {xs y ys} -> xs ⊆ ys -> xs ⊆ y :: ys
  keep : forall {x xs ys} -> xs ⊆ ys -> x :: xs ⊆ x :: ys

⊆-refl : forall {A}{xs : List A} -> xs ⊆ xs
⊆-refl {xs = []}      = stop
⊆-refl {xs = x :: xs} = keep ⊆-refl

_∣_ : forall {A}{P : A -> Set}{xs ys} -> All P ys -> xs ⊆ ys -> All P xs
[]        ∣ stop   = []
(x :: xs) ∣ drop p = xs ∣ p
(x :: xs) ∣ keep p = x :: (xs ∣ p)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Indexed universes
------------------------------------------------------------------------

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

module Data.Universe.Indexed where

open import Data.Product
open import Data.Universe
open import Function
open import Level

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

record IndexedUniverse i u e : Set (suc (i ⊔ u ⊔ e)) where
  field
    I  : Set i                 -- Index set.
    U  : I → Set u             -- Codes.
    El : ∀ {i} → U i → Set e   -- Decoding function.

  -- An indexed universe can be turned into an unindexed one.

  unindexed-universe : Universe (i ⊔ u) e
  unindexed-universe = record
    { U  = ∃ λ i → U i
    ; El = El ∘ proj₂
    }


module Cats.Util.Monad where

open import Category.Monad public

open import Data.List using (List ; [] ; _∷_ ; map)
open import Data.Unit using (⊤)
open import Function using (_∘_)
open import Level using (Lift ; lift)

open RawMonad {{...}} public

private
  _<*>_ = _⊛_

module _ {f} {M : Set f → Set f} {{_ : RawMonad M}} where

  sequence : ∀ {A} → List (M A) → M (List A)
  sequence [] = return []
  sequence (mx ∷ mxs) = ⦇ mx ∷ sequence mxs ⦈

  void : ∀ {A} → M A → M (Lift f ⊤)
  void m = m >>= λ _ → return _

  mapM : ∀ {a} {A : Set a} {B} → (A → M B) → List A → M (List B)
  mapM f = sequence ∘ map f

  mapM′ : ∀ {a} {A : Set a} {B} → (A → M B) → List A → M (Lift f ⊤)
  mapM′ f = void ∘ mapM f


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

open import HoTT
import homotopy.ConstantToSetExtendsToProp as ConstExt
open import homotopy.Pigeonhole

module homotopy.FinSet where

-- the explicit type of finite sets, carrying the cardinality on its sleeve
FinSet-exp : Type₁
FinSet-exp = Σ ℕ λ n → BAut (Fin n)

FinSet-prop : SubtypeProp Type₀ (lsucc lzero)
FinSet-prop = (λ A → Trunc -1 (Σ ℕ λ n → Fin n == A)) , λ A → Trunc-level

-- the implicit type of finite sets, hiding the cardinality in prop. trunc.
FinSet : Type₁
FinSet = Subtype FinSet-prop

FinSet= : {A B : FinSet} → fst A == fst B → A == B
FinSet= = Subtype=-out FinSet-prop

FinFS : ℕ → FinSet
FinFS n = Fin n , [ n , idp ]

UnitFS : FinSet
UnitFS = ⊤ , [ S O , ua Fin-equiv-Coprod ∙ ap (λ C → C ⊔ ⊤) (ua Fin-equiv-Empty)
                     ∙ ua (Coprod-unit-l ⊤) ]

Fin-inj-lemma : {n m : ℕ} → n < m → Fin m == Fin n → ⊥
Fin-inj-lemma {n} {m} n<m p = i≠j (<– (ap-equiv (coe-equiv p) i j) q)
  where
    i : Fin m
    i = fst (pigeonhole n<m (coe p))

    j : Fin m
    j = fst (snd (pigeonhole n<m (coe p)))

    i≠j : i ≠ j
    i≠j = fst (snd (snd (pigeonhole n<m (coe p))))

    q : coe p i == coe p j
    q = snd (snd (snd (pigeonhole n<m (coe p))))

Fin-inj : (n m : ℕ) → Fin n == Fin m → n == m
Fin-inj n m p with ℕ-trichotomy n m
Fin-inj n m p | inl q = q
Fin-inj n m p | inr (inl q) = ⊥-rec (Fin-inj-lemma q (! p))
Fin-inj n m p | inr (inr q) = ⊥-rec (Fin-inj-lemma q p)

FinSet-aux-prop : (A : Type₀) → SubtypeProp ℕ (lsucc lzero)
FinSet-aux-prop A = (λ n → Trunc -1 (Fin n == A)) , (λ n → Trunc-level)

FinSet-aux : (A : FinSet) → Subtype (FinSet-aux-prop (fst A))
FinSet-aux (A , tz) = CE.ext tz
  where
    to : (Σ ℕ λ n → Fin n == A) → Subtype (FinSet-aux-prop A)
    to (n , p) = n , [ p ]

    to-const : (z₁ z₂ : Σ ℕ λ n → Fin n == A) → to z₁ == to z₂
    to-const (n₁ , p₁) (n₂ , p₂) = Subtype=-out (FinSet-aux-prop A) (Fin-inj n₁ n₂ (p₁ ∙ ! p₂))

    module CE =
      ConstExt ⦃ Subtype-level (FinSet-aux-prop A) ⦄ to to-const

card : FinSet → ℕ
card A = fst (FinSet-aux A)

card-conv : (A : FinSet) (n : ℕ) → Trunc -1 (Fin n == fst A) → card A == n
card-conv (A , tz) n = Trunc-rec λ p → ap card (FinSet= {A , tz} {FinFS n} (! p))

FinSet-econv : FinSet-exp ≃ FinSet
FinSet-econv = equiv to from to-from from-to
  where
    to : FinSet-exp → FinSet
    to (n , A , tp) = A , Trunc-rec (λ p → [ n , p ]) tp

    from : FinSet → FinSet-exp
    from A = card A , fst A , snd (FinSet-aux A)

    to-from : (A : FinSet) → to (from A) == A
    to-from A = pair= idp (prop-has-all-paths (snd (to (from A))) (snd A))

    from-to : (A : FinSet-exp) → from (to A) == A
    from-to A = pair= (card-conv (to A) (fst A) (snd (snd A)))
      (↓-Subtype-in (λ n → BAut-prop (Fin n)) (↓-cst-in idp))

FinSet-elim-prop : ∀ {j} {P : FinSet → Type j} (p : (A : FinSet) → has-level -1 (P A))
                   (d : (n : ℕ) → P (FinFS n)) → (A : FinSet) → P A
FinSet-elim-prop {j} {P} p d (A , tz) = Trunc-elim ⦃ λ tw → p (A , tw) ⦄
  (λ {(n , p) → transport P (FinSet= p) (d n)}) tz

finset-has-dec-eq : (A : FinSet) → has-dec-eq (fst A)
finset-has-dec-eq = FinSet-elim-prop (λ A → has-dec-eq-is-prop) λ n → Fin-has-dec-eq

-- todo: closure of FinSet under product, coproduct, exponential, etc.


------------------------------------------------------------------------------
-- Testing polymorphic lists using data types
------------------------------------------------------------------------------

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

module FOT.FOTC.Polymorphism.List where

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Data.Bool.Type
open import FOTC.Data.List.Type
open import FOTC.Data.Nat.List.Type
open import FOTC.Data.Nat.Type
open import FOTC.Data.Nat.UnaryNumbers

------------------------------------------------------------------------------
-- "Heterogeneous" total lists
xs : D
xs = 0' ∷ true ∷ 1' ∷ false ∷ []

xs-List : List xs
xs-List = lcons 0' (lcons true (lcons 1' (lcons false lnil)))

-- Total lists of total natural numbers
ys : D
ys = 0' ∷ 1' ∷ 2' ∷ []

ys-ListN : ListN ys
ys-ListN =
  lncons nzero (lncons (nsucc nzero) (lncons (nsucc (nsucc nzero)) lnnil))

-- Total lists of total Booleans
data ListB : D → Set where
  lbnil  :                                ListB []
  lbcons : ∀ {b bs} → Bool b → ListB bs → ListB (b ∷ bs)

zs : D
zs = true ∷ false ∷ true ∷ []

zs-ListB : ListB zs
zs-ListB = lbcons btrue (lbcons bfalse (lbcons btrue lbnil))

------------------------------------------------------------------------------
-- Polymorphic lists.
data Plist (P : D → Set) : D → Set where
  lnil  :                               Plist P []
  lcons : ∀ {x xs} → P x → Plist P xs → Plist P (x ∷ xs)

-- "Heterogeneous" total lists
List₁ : D → Set
List₁ = Plist (λ d → d ≡ d)

xs-List₁ : List₁ xs
xs-List₁ = lcons refl (lcons refl (lcons refl (lcons refl lnil)))

-- Total lists of total natural numbers
ListN₁ : D → Set
ListN₁ = Plist N

ys-ListN₁ : ListN₁ ys
ys-ListN₁ = lcons nzero (lcons (nsucc nzero) (lcons (nsucc (nsucc nzero)) lnil))

-- Total lists of total Booleans
ListB₁ : D → Set
ListB₁ = Plist Bool

zs-ListB₁ : ListB₁ zs
zs-ListB₁ = lcons btrue (lcons bfalse (lcons btrue lnil))


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

module Math.Googology.Function.Properties where

-- agda-stdlib
open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.GeneralisedArithmetic using (fold)
open import Data.List
open import Function
open import Relation.Binary.PropositionalEquality

-- agda-misc
open import Math.Googology.Function

open ≤-Reasoning

------------------------------------------------------------------------
-- Properties of hyperoperation

H-rec : ∀ n a b → H (suc n) a (suc b) ≡ H n a (H (suc n) a b)
H-rec zero          a b = refl
H-rec (suc zero)    a b = refl
H-rec (suc (suc n)) a b = refl

-- H₀ is successor.
H-suc : ∀ a b → H 0 a b ≡ suc b
H-suc a b = refl

-- H₁ is addition.
H-+ : ∀ a b → H 1 a b ≡ a + b
H-+ a 0       = sym $ +-identityʳ a
H-+ a (suc b) = begin-equality
  suc (H 1 a b) ≡⟨ cong suc $ H-+ a b ⟩
  suc (a + b)   ≡⟨⟩
  suc a + b     ≡⟨ sym $ +-suc a b ⟩
  a + suc b     ∎

-- H₂ is multiplication.
H-* : ∀ a b → H 2 a b ≡ a * b
H-* a 0       = sym $ *-zeroʳ a
H-* a (suc b) = begin-equality
  H 1 a (H 2 a b) ≡⟨ cong (H 1 a) $ H-* a b ⟩
  H 1 a (a * b)   ≡⟨ H-+ a (a * b) ⟩
  a + a * b       ≡⟨ sym $ *-suc a b ⟩
  a * suc b       ∎

-- H₃ is exponentiation.
H-^ : ∀ a b → H 3 a b ≡ a ^ b
H-^ a 0       = refl
H-^ a (suc b) = begin-equality
  H 2 a (H 3 a b) ≡⟨ cong (H 2 a) $ H-^ a b ⟩
  H 2 a (a ^ b)   ≡⟨ H-* a (a ^ b) ⟩
  a * (a ^ b)     ∎

-- Hₙ(a, 1) = a  (n ≥ 2)
H[2+n,a,1]≡a : ∀ n a → H (2 + n) a 1 ≡ a
H[2+n,a,1]≡a 0       a = refl
H[2+n,a,1]≡a (suc n) a = begin-equality
  H (suc (suc (suc n))) a 1 ≡⟨ H-rec (suc (suc n)) a 0 ⟩
  H (2 + n) a 1             ≡⟨ H[2+n,a,1]≡a n a ⟩
  a                         ∎

-- a ↑ⁿ 1 ≡ a
a↑ⁿ1≡a : ∀ a n → a ↑[ n ] 1 ≡ a
a↑ⁿ1≡a a n = H[2+n,a,1]≡a n a

-- Hₙ(2, 2) = 4  (n ≥ 1)
H[1+n,2,2]≡4 : ∀ n → H (1 + n) 2 2 ≡ 4
H[1+n,2,2]≡4 0       = refl
H[1+n,2,2]≡4 (suc n) = begin-equality
  H (suc (suc n)) 2 (suc 1)         ≡⟨ H-rec (suc n) 2 1 ⟩
  H (suc n) 2 (H (suc (suc n)) 2 1) ≡⟨ cong (H (suc n) 2) $ H[2+n,a,1]≡a n 2 ⟩
  H (suc n) 2 2                     ≡⟨ H[1+n,2,2]≡4 n ⟩
  4                                 ∎

-- Hₙ(1, b) = 1  (n ≥ 3)
H[3+n,1,b]≡1 : ∀ n b → H (3 + n) 1 b ≡ 1
H[3+n,1,b]≡1 0       b       = trans (H-^ 1 b) (^-zeroˡ b)
H[3+n,1,b]≡1 (suc n) 0       = refl
H[3+n,1,b]≡1 (suc n) (suc b) = H[3+n,1,b]≡1 n (H (4 + n) 1 b)

-- Hₙ(a, a) = H₁₊ₙ(a, 2)  (n ≥ 1)
H[1+n,a,a]≡H[2+n,a,2] : ∀ n a → H (1 + n) a a ≡ H (2 + n) a 2
H[1+n,a,a]≡H[2+n,a,2] n a = sym lemma where
  lemma : H (2 + n) a 2 ≡ H (1 + n) a a
  lemma = begin-equality
    H (suc (suc n)) a 2
      ≡⟨ H-rec (suc n) a 1 ⟩
    H (suc n) a (H (suc (suc n)) a 1)
      ≡⟨ cong (H (suc n) a) $ H[2+n,a,1]≡a n a ⟩
    H (suc n) a a
      ∎

H-is-fold : ∀ n a b → H (3 + n) a b ≡ fold 1 (H (2 + n) a) b
H-is-fold n a 0       = refl
H-is-fold n a (suc b) = cong (H (2 + n) a) $ H-is-fold n a b

H[1+n,2,3]≡H[n,2,4] : ∀ n → H (1 + n) 2 3 ≡ H n 2 4
H[1+n,2,3]≡H[n,2,4] 0       = refl
H[1+n,2,3]≡H[n,2,4] (suc n) = begin-equality
  H (2 + n) 2 3
    ≡⟨ H-rec (1 + n) 2 2 ⟩
  H (1 + n) 2 (H (2 + n) 2 2)
    ≡⟨ cong (H (1 + n) 2) $ H[1+n,2,2]≡4 (1 + n) ⟩
  H (1 + n) 2 4
    ∎

H[2+n,0,1]≡0 : ∀ n → H (2 + n) 0 1 ≡ 0
H[2+n,0,1]≡0 zero    = refl
H[2+n,0,1]≡0 (suc n) = H[2+n,0,1]≡0 n

H[4+n,0,2*b]≡1   : ∀ n b → H (4 + n) 0 (2 * b) ≡ 1
H[4+n,0,1+2*b]≡0 : ∀ n b → H (4 + n) 0 (1 + 2 * b) ≡ 0
H[4+n,0,2*b]≡1 n zero    = refl
H[4+n,0,2*b]≡1 n (suc b) = begin-equality
  H (3 + n) 0 (H (4 + n) 0 (b + (suc (b + 0))))
    ≡⟨ cong (λ v → H (3 + n) 0 (H (4 + n) 0 v)) $ +-suc b (b + 0) ⟩
  H (3 + n) 0 (H (4 + n) 0 (1 + 2 * b))
    ≡⟨ cong (H (3 + n) 0) $ H[4+n,0,1+2*b]≡0 n b ⟩
  H (3 + n) 0 0
    ≡⟨⟩
  1
    ∎
H[4+n,0,1+2*b]≡0 n b = begin-equality
  H (3 + n) 0 (H (4 + n) 0 (2 * b))
    ≡⟨ cong (H (3 + n) 0) $ H[4+n,0,2*b]≡1 n b ⟩
  H (3 + n) 0 1
    ≡⟨ H[2+n,0,1]≡0 (suc n) ⟩
  0
    ∎

------------------------------------------------------------------------
-- Properties of `ack`

private
  ack-H-helper :
    (P : ℕ → ℕ → Set) →
    (∀ n → P 0 n) →
    (∀ m → P m 1 → P (suc m) 0) →
    (∀ m n → P (suc m) n → (∀ n′ → P m n′) → P (suc m) (suc n)) →
    ∀ m n → P m n
  ack-H-helper P P[0,n] P[m,0] P[m,n] = go where
    go : ∀ m n → P m n
    go 0       n       = P[0,n] n
    go (suc m) 0       = P[m,0] m (go m 1)
    go (suc m) (suc n) = P[m,n] m n (go (suc m) n) (go m)

-- Ackermann function can be expressed in terms of hyperoperation.
-- 3 + Ack(m, n) = Hₙ(2, 3 + n)
ack-H : ∀ m n → 3 + ack m n ≡ H m 2 (3 + n)
ack-H = ack-H-helper P (λ _ → refl) P[m,0] P[m,n]
  where
  P : ℕ → ℕ → Set
  P m n = 3 + ack m n ≡ H m 2 (3 + n)

  P[m,0] : ∀ m → P m 1 → P (suc m) 0
  P[m,0] m P[m,1] = trans P[m,1] (sym $ H[1+n,2,3]≡H[n,2,4] m)

  P[m,n] : ∀ m n → P (suc m) n → (∀ n′ → P m n′) → P (suc m) (suc n)
  P[m,n] m n P[m′,n] P[m,n′] = begin-equality
    3 + ack m (ack (1 + m) n)   ≡⟨ P[m,n′] (ack (1 + m) n) ⟩
    H m 2 (3 + ack (1 + m) n)   ≡⟨ cong (H m 2) $ P[m′,n] ⟩
    H m 2 (H (1 + m) 2 (3 + n)) ≡⟨ sym $ H-rec m 2 (3 + n) ⟩
    H (1 + m) 2 (4 + n)         ∎

iter : (ℕ → ℕ) → ℕ → ℕ
iter f 0       = f 1
iter f (suc n) = f (iter f n)

iter-is-fold : ∀ f n → iter f n ≡ fold (f 1) f n
iter-is-fold f zero    = refl
iter-is-fold f (suc n) = cong f (iter-is-fold f n)

ack-is-fold′ : ∀ m n → ack m n ≡ fold suc iter m n
ack-is-fold′ 0       n       = refl
ack-is-fold′ (suc m) 0       = ack-is-fold′ m 1
ack-is-fold′ (suc m) (suc n) = begin-equality
  ack m (ack (suc m) n)
    ≡⟨ ack-is-fold′ m (ack (suc m) n) ⟩
  fold suc iter m (ack (suc m) n)
    ≡⟨ cong (fold suc iter m) $ ack-is-fold′ (suc m) n ⟩
  fold suc iter m (iter (fold suc iter m) n)
    ∎

ack-is-fold : ∀ m n → ack m n ≡ fold suc (λ f n → fold (f 1) f n) m n
ack-is-fold zero    n       = refl
ack-is-fold (suc m) zero    = ack-is-fold m 1
ack-is-fold (suc m) (suc n) =
 trans (ack-is-fold m (ack (suc m) n))
       (cong (fold suc (λ f n → fold (f 1) f n) m) (ack-is-fold (suc m) n))


module Logic.Names where

open import Functional
import      Lvl
open import Logic
open import Logic.Predicate
open import Logic.Propositional

module _ {ℓ} where
  ExcludedMiddleOn : Stmt{ℓ} → Stmt
  ExcludedMiddleOn(X) = (X ∨ (¬ X))
  ExcludedMiddle = ∀ₗ(ExcludedMiddleOn)

  WeakExcludedMiddleOn = ExcludedMiddleOn ∘ (¬_)
  WeakExcludedMiddle = ∀ₗ(WeakExcludedMiddleOn)

  NonContradictionOn : Stmt{ℓ} → Stmt
  NonContradictionOn(X) = ¬(X ∧ (¬ X))
  NonContradiction = ∀ₗ(NonContradictionOn)

  DoubleNegationOn : Stmt{ℓ} → Stmt
  DoubleNegationOn(X) = (¬(¬ X)) → X
  DoubleNegation = ∀ₗ(DoubleNegationOn)

module _ where
  private variable ℓ₁ ℓ₂ : Lvl.Level

  TopOrBottom : (Stmt{ℓ₁} → Stmt{Lvl.𝟎} → Stmt{ℓ₂}) → Stmt{ℓ₁} → Stmt
  TopOrBottom(_≡_)(P) = (P ≡ ⊤) ∨ (P ≡ ⊥)

module _ {ℓ₁ ℓ₂} where
  CallCCOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  CallCCOn(X)(Y) = (((X → Y) → X) → X)
  CallCC = ∀²(CallCCOn)

  ContrapositiveOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  ContrapositiveOn(X)(Y) = (X → Y) → ((¬ Y) → (¬ X))
  Contrapositive = ∀²(ContrapositiveOn)

  DisjunctiveSyllogismₗOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  DisjunctiveSyllogismₗOn(X)(Y) = ((X ∨ Y) ∧ (¬ Y)) → X
  DisjunctiveSyllogismₗ = ∀²(DisjunctiveSyllogismₗOn)

  DisjunctiveSyllogismᵣOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  DisjunctiveSyllogismᵣOn(X)(Y) = ((X ∨ Y) ∧ (¬ X)) → Y
  DisjunctiveSyllogismᵣ = ∀²(DisjunctiveSyllogismᵣOn)

  MaterialImplicationOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  MaterialImplicationOn(X)(Y) = (X → Y) ↔ ((¬ X) ∨ Y)
  MaterialImplication = ∀²(MaterialImplicationOn)

module _ {ℓ₁ ℓ₂ ℓ₃ ℓ₄} where
  ConstructiveDilemmaOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt{ℓ₃} → Stmt{ℓ₄} → Stmt
  ConstructiveDilemmaOn(X₁)(X₂)(Y₁)(Y₂) = ((X₁ → X₂) ∧ (Y₁ → Y₂) ∧ (X₁ ∨ Y₁)) → (X₂ ∨ Y₂)
  ConstructiveDilemma = ∀⁴(ConstructiveDilemmaOn)

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Classical names

module _ {ℓ₁ ℓ₂} where
  ModusTollensOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  ModusTollensOn(X)(Y) = ((X → Y) ∧ (¬ Y)) → (¬ X)
  ModusTollens = ∀²(ModusTollensOn)

  ModusPonensOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  ModusPonensOn(X)(Y) = ((X → Y) ∧ X) → Y
  ModusPonens = ∀²(ModusPonensOn)

  ReductioAdAbsurdumOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  ReductioAdAbsurdumOn(X)(Y) = ((X → Y) ∧ (X → (¬ Y))) → (¬ X)
  ReductioAdAbsurdum = ∀²(ReductioAdAbsurdumOn)

  ReductioAdAbsurdumNegatedOn : Stmt{ℓ₁} → Stmt{ℓ₂} → Stmt
  ReductioAdAbsurdumNegatedOn(X)(Y) = (((¬ X) → Y) ∧ ((¬ X) → (¬ Y))) → (¬ X)
  ReductioAdAbsurdumNegated = ∀²(ReductioAdAbsurdumNegatedOn)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties that are related to pointwise lifting of binary
-- relations to sigma types and make use of heterogeneous equality
------------------------------------------------------------------------

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

module Data.Product.Relation.Binary.Pointwise.Dependent.WithK where

open import Data.Product
open import Data.Product.Relation.Binary.Pointwise.Dependent
open import Function
open import Function.Inverse using (Inverse)
open import Relation.Binary using (_⇒_)
open import Relation.Binary.HeterogeneousEquality as H using (_≅_)
open import Relation.Binary.Indexed.Heterogeneous using (IndexedSetoid)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

------------------------------------------------------------------------
-- The propositional equality setoid over sigma types can be
-- decomposed using Pointwise

module _ {a b} {A : Set a} {B : A → Set b} where

  Pointwise-≡⇒≡ : Pointwise B _≡_ (λ x y → x ≅ y) ⇒ _≡_
  Pointwise-≡⇒≡ (P.refl , H.refl) = P.refl

  ≡⇒Pointwise-≡ : _≡_ ⇒ Pointwise B _≡_ (λ x y → x ≅ y)
  ≡⇒Pointwise-≡ P.refl = (P.refl , H.refl)

  Pointwise-≡↔≡ : Inverse (setoid (P.setoid A) (H.indexedSetoid B))
                  (P.setoid (Σ A B))
  Pointwise-≡↔≡ = record
    { to         = record { _⟨$⟩_ = id; cong = Pointwise-≡⇒≡ }
    ; from       = record { _⟨$⟩_ = id; cong = ≡⇒Pointwise-≡ }
    ; inverse-of = record
      { left-inverse-of  = uncurry (λ _ _ → (P.refl , H.refl))
      ; right-inverse-of = λ _ → P.refl
      }
    }


------------------------------------------------------------------------------
-- Definition of the gcd of two natural numbers using the Euclid's algorithm
------------------------------------------------------------------------------

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

module LTC-PCF.Program.GCD.Partial.GCD where

open import LTC-PCF.Base
open import LTC-PCF.Data.Nat
open import LTC-PCF.Data.Nat.Inequalities
open import LTC-PCF.Loop

------------------------------------------------------------------------------
-- In GHC ≤ 7.0.4 the gcd is a partial function, i.e. gcd 0 0 = undefined.

-- Let T = D → D → D be a type. Instead of defining gcdh : T → T, we
-- use the LTC-PCF λ-abstraction and application to avoid use a
-- polymorphic fixed-point operator.

gcdh : D → D
gcdh g = lam (λ m → lam (λ n →
           if (iszero₁ n)
             then (if (iszero₁ m) then error else m)
             else (if (iszero₁ m)
                     then n
                     else (if (gt m n)
                             then g · (m ∸ n) · n
                             else g · m · (n ∸ m)))))

gcd : D → D → D
gcd m n = fix gcdh · m · n


module Function.Domains.Functions where

import      Lvl
open import Function.Domains
open import Function.Domains.Id
open import Structure.Relator.Properties
open import Structure.Setoid
open import Type
open import Type.Dependent

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

module _ {X : Type{ℓₒ₁}} {Y : Type{ℓₒ₂}} where
  module ⊶ where
    private variable f : X → Y

    -- The function which shrinks the given function's codomain to its image.
    shrink : (f : X → Y) → (X → (⊶ f))
    shrink f(x) = intro (f(x)) (intro x)

    -- Applies an argument of type X to a function of type ((⊶ f) → Y) according to the bijection of {X,(⊶ f)} by f.
    apply : X → ((⊶ f) → Y) → Y
    apply{f} x imgfn = imgfn (shrink f(x))

    fn : (⊶ f) → (X → Y)
    fn{f} _ = f

    arg : (⊶ f) → X
    arg (intro _ (intro x)) = x

    -- Could be interpreted as an identity function with an enlarged codomain.
    -- The value of (⊶ f) interpreted as contained in the "set" Y.
    value : (⊶ f) → Y
    value (intro y _) = y

    value-proof : ⦃ equiv-Y : Equiv{ℓₑ₂}(Y) ⦄ → ∀{x} → (value(intro (f(x)) (intro{f = f} x)) ≡ f(x))
    value-proof = reflexivity(_≡_)

    arg-proof : ⦃ equiv-X : Equiv{ℓₑ₁}(X) ⦄ → ∀{x} → (arg(intro (f(x)) (intro{f = f} x)) ≡ x)
    arg-proof = reflexivity(_≡_)

  -- TODO: Problem with levels
  -- TODO: [⊶]-function-surjective : ∀{f : X → Y} → Surjective([⊶]-function(f))
  -- .[⊶]-function-surjective : ∀{f : X → Y}{y : (⊶ f)} → ∃(x ↦ [⊶]-arg(([⊶]-fn f)(x)) ≡ [⊶]-arg(y))
  -- [⊶]-function-surjective {f} {intro(y-value) ⦃ [∃]-intro (x) ⦃ proof ⦄ ⦄} =
  --   Lang.Irrelevance.axiom(
  --     ([∃]-intro (x)
  --       ⦃ [≡]-with-specific {_}{_} {_}{_} {f(x)} {y-value}
  --         (expr ↦ \ ⦃ [≡]-intro ⦄ → \ ⦃ [≡]-intro ⦄ →
  --           (intro
  --             (expr)
  --             ⦃ [∃]-intro (x) ⦃ [≡]-intro :of: (f(x) ≡ y-value) ⦄ ⦄
  --           )
  --         )
  --         (proof :of: (f(x) ≡ y-value))
  --       ⦄
  --     )
  --   )
    -- ∃(x: X). f(x) ≡ obj(y) //proof:
    -- Take x: X
    --   f(x) ≡ obj(y)
    --   ⇒ intro(f(x)) ⦃ [∃]-intro(x) ⦃ e ⦄ ⦄ ≡ intro(obj(y)) ⦃ [∃]-intro(x) ⦃ e ⦄ ⦄

  -- [⊶]-function-injective : ∀{X}{Y}{f : X → Y} → Injective(f) → Injective([⊶]-function(f))
  -- [⊶]-function-injective{_}{_}{f} {x₁}{x₂} [≡]-intro = [≡]-intro


------------------------------------------------------------------------------
-- Properties for the equality on Conat
------------------------------------------------------------------------------

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

module FOTC.Data.Conat.Equality.PropertiesI where

open import FOTC.Base
open import FOTC.Data.Conat
open import FOTC.Data.Conat.Equality.Type

------------------------------------------------------------------------------
-- Because a greatest post-fixed point is a fixed-point, then the
-- relation _≈_ is also a pre-fixed point of the functional ≈-F, i.e.
--
-- ≈-F _≈_ ≤ _≈_ (see FOTC.Data.Conat.Equality.Type).
≈-in :
  ∀ {m n} →
  m ≡ zero ∧ n ≡ zero
    ∨ (∃[ m' ] ∃[ n' ] m ≡ succ₁ m' ∧ n ≡ succ₁ n' ∧ m' ≈ n') →
  m ≈ n
≈-in h = ≈-coind R h' h
  where
  R : D → D → Set
  R m n = m ≡ zero ∧ n ≡ zero
            ∨ (∃[ m' ] ∃[ n' ] m ≡ succ₁ m' ∧ n ≡ succ₁ n' ∧ m' ≈ n')

  h' : ∀ {m n} → R m n →
       m ≡ zero ∧ n ≡ zero
         ∨ (∃[ m' ] ∃[ n' ] m ≡ succ₁ m' ∧ n ≡ succ₁ n' ∧ R m' n')
  h' (inj₁ prf) = inj₁ prf
  h' (inj₂ (m' , n' , prf₁ , prf₂ , m'≈n')) =
    inj₂ (m' , n' , prf₁ , prf₂ , ≈-out m'≈n')

≈-refl : ∀ {n} → Conat n → n ≈ n
≈-refl {n} Cn = ≈-coind R h₁ h₂
  where
  R : D → D → Set
  R a b = Conat a ∧ Conat b ∧ a ≡ b

  h₁ : ∀ {a b} → R a b →
       a ≡ zero ∧ b ≡ zero
         ∨ (∃[ a' ] ∃[ b' ] a ≡ succ₁ a' ∧ b ≡ succ₁ b' ∧ R a' b')
  h₁ (Ca , Cb , h) with Conat-out Ca
  ... | inj₁ prf              = inj₁ (prf , trans (sym h) prf)
  ... | inj₂ (a' , prf , Ca') =
    inj₂ (a' , a' , prf , trans (sym h) prf , (Ca' , Ca' , refl))

  h₂ : R n n
  h₂ = Cn , Cn , refl

≡→≈ : ∀ {m n} → Conat m → Conat n → m ≡ n → m ≈ n
≡→≈ Cm _ refl = ≈-refl Cm


------------------------------------------------------------------------
-- The Agda standard library
--
-- Lexicographic ordering of lists
------------------------------------------------------------------------

-- The definition of lexicographic ordering used here is suitable if
-- the argument order is a (non-strict) partial order. The
-- lexicographic ordering itself can be either strict or non-strict,
-- depending on the value of a parameter.

module Relation.Binary.List.NonStrictLex where

open import Data.Empty
open import Function
open import Data.Unit using (⊤; tt)
open import Data.Product
open import Data.List
open import Level
open import Relation.Nullary
open import Relation.Binary

import Relation.Binary.NonStrictToStrict as Conv
import Relation.Binary.List.StrictLex as Strict

open import Relation.Binary.List.Pointwise as Pointwise using ([])

module _ {A : Set} where

  Lex : (P : Set) → (_≈_ _≤_ : Rel A zero) → Rel (List A) zero
  Lex P _≈_ _≤_ = Strict.Lex P _≈_ (Conv._<_ _≈_ _≤_)

  -- Strict lexicographic ordering.

  Lex-< : (_≈_ _≤_ : Rel A zero) → Rel (List A) zero
  Lex-< = Lex ⊥

  -- Non-strict lexicographic ordering.

  Lex-≤ : (_≈_ _≤_ : Rel A zero) → Rel (List A) zero
  Lex-≤ = Lex ⊤

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

  ≤-reflexive : ∀ _≈_ _≤_ → Pointwise.Rel _≈_ ⇒ Lex-≤ _≈_ _≤_
  ≤-reflexive _≈_ _≤_ = Strict.≤-reflexive _≈_ (Conv._<_ _≈_ _≤_)

  <-irreflexive : ∀ {_≈_ _≤_} →
                  Irreflexive (Pointwise.Rel _≈_) (Lex-< _≈_ _≤_)
  <-irreflexive {_≈_} {_≤_} =
    Strict.<-irreflexive {_<_ = Conv._<_ _≈_ _≤_} (Conv.irrefl _≈_ _≤_)

  transitive : ∀ {P _≈_ _≤_} →
               IsPartialOrder _≈_ _≤_ → Transitive (Lex P _≈_ _≤_)
  transitive po =
    Strict.transitive
       isEquivalence
       (Conv.<-resp-≈ _ _ isEquivalence ≤-resp-≈)
       (Conv.trans _ _ po)
    where open IsPartialOrder po

  antisymmetric : ∀ {P _≈_ _≤_} →
                  Symmetric _≈_ → Antisymmetric _≈_ _≤_ →
                  Antisymmetric (Pointwise.Rel _≈_) (Lex P _≈_ _≤_)
  antisymmetric {_≈_ = _≈_} {_≤_} sym antisym =
    Strict.antisymmetric sym (Conv.irrefl _≈_ _≤_)
                         (Conv.antisym⟶asym _≈_ _ antisym)

  <-asymmetric : ∀ {_≈_ _≤_} →
                 IsEquivalence _≈_ → _≤_ Respects₂ _≈_ →
                 Antisymmetric _≈_ _≤_ → Asymmetric (Lex-< _≈_ _≤_)
  <-asymmetric {_≈_} eq resp antisym  =
    Strict.<-asymmetric sym (Conv.<-resp-≈ _ _ eq resp)
                        (Conv.antisym⟶asym _≈_ _ antisym)
    where open IsEquivalence eq

  respects₂ : ∀ {P _≈_ _≤_} →
              IsEquivalence _≈_ → _≤_ Respects₂ _≈_ →
              Lex P _≈_ _≤_ Respects₂ Pointwise.Rel _≈_
  respects₂ eq resp = Strict.respects₂ eq (Conv.<-resp-≈ _ _ eq resp)

  decidable : ∀ {P _≈_ _≤_} →
              Dec P → Decidable _≈_ → Decidable _≤_ →
              Decidable (Lex P _≈_ _≤_)
  decidable dec-P dec-≈ dec-≤ =
    Strict.decidable dec-P dec-≈ (Conv.decidable _ _ dec-≈ dec-≤)

  <-decidable :
    ∀ {_≈_ _≤_} →
    Decidable _≈_ → Decidable _≤_ → Decidable (Lex-< _≈_ _≤_)
  <-decidable = decidable (no id)

  ≤-decidable :
    ∀ {_≈_ _≤_} →
    Decidable _≈_ → Decidable _≤_ → Decidable (Lex-≤ _≈_ _≤_)
  ≤-decidable = decidable (yes tt)

  ≤-total : ∀ {_≈_ _≤_} → Symmetric _≈_ → Decidable _≈_ →
            Antisymmetric _≈_ _≤_ → Total _≤_ →
            Total (Lex-≤ _≈_ _≤_)
  ≤-total sym dec-≈ antisym tot =
    Strict.≤-total sym (Conv.trichotomous _ _ sym dec-≈ antisym tot)

  <-compare : ∀ {_≈_ _≤_} → Symmetric _≈_ → Decidable _≈_ →
              Antisymmetric _≈_ _≤_ → Total _≤_ →
              Trichotomous (Pointwise.Rel _≈_) (Lex-< _≈_ _≤_)
  <-compare sym dec-≈ antisym tot =
    Strict.<-compare sym (Conv.trichotomous _ _ sym dec-≈ antisym tot)

  -- Some collections of properties which are preserved by Lex-≤ or
  -- Lex-<.

  ≤-isPreorder : ∀ {_≈_ _≤_} →
                 IsPartialOrder _≈_ _≤_ →
                 IsPreorder (Pointwise.Rel _≈_) (Lex-≤ _≈_ _≤_)
  ≤-isPreorder po =
    Strict.≤-isPreorder
       isEquivalence (Conv.trans _ _ po)
       (Conv.<-resp-≈ _ _ isEquivalence ≤-resp-≈)
   where open IsPartialOrder po

  ≤-isPartialOrder : ∀ {_≈_ _≤_} →
                     IsPartialOrder _≈_ _≤_ →
                     IsPartialOrder (Pointwise.Rel _≈_) (Lex-≤ _≈_ _≤_)
  ≤-isPartialOrder po =
    Strict.≤-isPartialOrder
      (Conv.isPartialOrder⟶isStrictPartialOrder _ _ po)

  ≤-isTotalOrder : ∀ {_≈_ _≤_} →
                   Decidable _≈_ → IsTotalOrder _≈_ _≤_ →
                   IsTotalOrder (Pointwise.Rel _≈_) (Lex-≤ _≈_ _≤_)
  ≤-isTotalOrder dec tot =
    Strict.≤-isTotalOrder
      (Conv.isTotalOrder⟶isStrictTotalOrder _ _ dec tot)

  ≤-isDecTotalOrder :
    ∀ {_≈_ _≤_} →
    IsDecTotalOrder _≈_ _≤_ →
    IsDecTotalOrder (Pointwise.Rel _≈_) (Lex-≤ _≈_ _≤_)
  ≤-isDecTotalOrder dtot =
    Strict.≤-isDecTotalOrder
      (Conv.isDecTotalOrder⟶isStrictTotalOrder _ _ dtot)

  <-isStrictPartialOrder : ∀ {_≈_ _≤_} →
    IsPartialOrder _≈_ _≤_ →
    IsStrictPartialOrder (Pointwise.Rel _≈_) (Lex-< _≈_ _≤_)
  <-isStrictPartialOrder po =
    Strict.<-isStrictPartialOrder
      (Conv.isPartialOrder⟶isStrictPartialOrder _ _ po)

  <-isStrictTotalOrder : ∀ {_≈_ _≤_} →
    Decidable _≈_ → IsTotalOrder _≈_ _≤_ →
    IsStrictTotalOrder (Pointwise.Rel _≈_) (Lex-< _≈_ _≤_)
  <-isStrictTotalOrder dec tot =
    Strict.<-isStrictTotalOrder
      (Conv.isTotalOrder⟶isStrictTotalOrder _ _ dec tot)

-- "Packages" (e.g. preorders) can also be handled.

≤-preorder : Poset _ _ _ → Preorder _ _ _
≤-preorder po = record
  { isPreorder = ≤-isPreorder isPartialOrder
  } where open Poset po

≤-partialOrder : Poset _ _ _ → Poset _ _ _
≤-partialOrder po = record
  { isPartialOrder = ≤-isPartialOrder isPartialOrder
  } where open Poset po

≤-decTotalOrder : DecTotalOrder _ _ _ → DecTotalOrder _ _ _
≤-decTotalOrder dtot = record
  { isDecTotalOrder = ≤-isDecTotalOrder isDecTotalOrder
  } where open DecTotalOrder dtot

<-strictPartialOrder : Poset _ _ _ → StrictPartialOrder _ _ _
<-strictPartialOrder po = record
  { isStrictPartialOrder = <-isStrictPartialOrder isPartialOrder
  } where open Poset po

<-strictTotalOrder : DecTotalOrder _ _ _ → StrictTotalOrder _ _ _
<-strictTotalOrder dtot = record
  { isStrictTotalOrder = <-isStrictTotalOrder _≟_ isTotalOrder
  } where open IsDecTotalOrder (DecTotalOrder.isDecTotalOrder dtot)


module Issue451 where

infix 10 _==_
data _==_ {A : Set} (x : A) : (y : A) -> Set where
 refl : x == x

postulate
 Nat : Set

data G : Nat -> Nat -> Set where
 I : (place : Nat) -> G place place
 s : (n m : Nat) -> G n m

mul : (l m n : Nat) -> G m n -> G l m -> G l n
mul a b .b (I .b)   x         = x
mul a .a b x        (I .a)    = x
mul a b c (s .b .c) (s .a .b) = s a c

postulate
  a b c : Nat
  f : G a b

bad : mul a a b (s a b) (I a) == s a b
bad = refl


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

-- Singleton type and its 'inverse'

module Singleton where

open import Data.Unit using (⊤; tt)
open import Data.Sum
open import Data.Product
open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; sym; trans; subst; cong ; cong₂ ; inspect ; [_])
-- open import Level
--   using (zero)
-- open import Axiom.Extensionality.Propositional
--   using (Extensionality)

is-contr : Set → Set
is-contr A = Σ A (λ a → (x : A) → a ≡ x)

is-prop : Set → Set
is-prop A = (x y : A) → x ≡ y

is-set : Set → Set
is-set A = (x y : A) → is-prop (x ≡ y)

contr-prop : {A : Set} → is-contr A → is-prop A
contr-prop (a , ϕ) x y = trans (sym (ϕ x)) (ϕ y)

apd : ∀ {a b} {A : Set a} {B : A → Set b} (f : (x : A) → B x) {x y} → (p : x ≡ y) → subst B p (f x) ≡ f y
apd f refl = refl

prop-set : {A : Set} → is-prop A → is-set A
prop-set {A} ϕ x y p q = trans (l p) (sym (l q))
  where g : (z : A) → x ≡ z
        g z = ϕ x z
        unitr : {y z : A} (p : y ≡ z) → refl ≡ trans (sym p) p
        unitr refl = refl
        l : {y z : A} (p : y ≡ z) → p ≡ trans (sym (g y)) (g z)
        l refl = unitr (g _)

prop-contr : {A : Set} → is-prop A → A → is-contr A
prop-contr ϕ a = a , ϕ a

------------------------------------------------------------------------------
-- Singleton type: A type with a distinguished point
--  The 'interesting' part is that the point is both a parameter
--  and a field.

{--
record Singleton (A : Set) (v : A) : Set where
  constructor ⇑
  field
    ● : A
    v≡● : v ≡ ●

open Singleton public
--}

Singleton : (A : Set) → (v : A) → Set
Singleton A v = ∃ (λ ● → v ≡ ●)

-- Singleton types are contractible:
pointed-contr : {A : Set} {v : A} → is-contr (Singleton A v)
--pointed-contr {A} {v} = ⇑ v refl , λ { (⇑ ● refl) → refl }
pointed-contr {A} {v} = (v , refl) , λ { ( ● , refl) → refl }

-- and thus have all paths between them:
pointed-all-paths : {A : Set} {v : A} {p q : Singleton A v} → p ≡ q
pointed-all-paths = contr-prop pointed-contr _ _

-- What does Singleton of Singleton do?
-- Values of type Singleton A v are of the form (w , p) where p : v ≡ w
-- Values of type Singleton (Singleton A v) x

ssv : (A : Set) (v : A) (x : Singleton A v) → Singleton (Singleton A v) x
ssv A v (.v , refl) = (v , refl) , refl

{--
ss=s : (A : Set) (v : A) (x : Singleton A v) → Singleton (Singleton A v) x ≡ Singleton A v
ss=s A v (.v , refl) with pointed-contr {A} {v}
... | (.v , refl) , f = let p = f (v , refl) in {!!} -- ??
--}
------------------------------------------------------------------------------
-- The 'reciprocal' of a Singleton type is a function that accepts exactly
-- that point, and returns no information. It acts as a 'witness' that
-- the right point has been fed to it.
{--
Recip : (A : Set) → (v : A) → Set
Recip A v = (w : A) → (v ≡ w) → ⊤
--}

Recip : (A : Set) → (v : A) → Set
Recip A v = Singleton A v → ⊤

-- Recip A v = Singleton A v → ⊤

-- Recip is also contractible, if we're thinking of homotopy types.
-- We need funext to prove it which is not --safe

-- posulate
--   funext : Extensionality zero zero

-- recip-contr : {A : Set} {v : A} → is-contr (Recip A v)
-- recip-contr = (λ _ _ → tt) , λ r → funext λ _ → funext λ _ → refl

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

-- Recip' : {A : Set} {v : A} → Singleton A v → Set
-- Recip' {A} {v} (⇑ w v≡w) = v ≡ w

-- Recip'-ptd : {A : Set} {v : A} → (p : Singleton A v) → Singleton (Recip' p) (v≡● p)
-- Recip'-ptd (⇑ w v≡w) = ⇑ v≡w refl

-- family of path types from arbitrary w to a fixed v

Recip' : (A : Set) → (v : A) → Set
Recip' A v = (w : A) → v ≡ w

-- If A is a n-type, Recip' is a (n-1)-type

-- recip'-contr : {A : Set} {v : A} → is-prop A → is-contr (Recip' A v)
-- recip'-contr {A} {v} ϕ = (λ w → ϕ v w) , λ r → funext λ x → prop-set ϕ v x (ϕ v x) (r x)

-- recip'-prop : {A : Set} {v : A} → is-set A → is-prop (Recip' A v)
-- recip'-prop ϕ r s = funext (λ x → ϕ _ x (r x) (s x))

------------------------------------------------------------------------------
-- Singleton is an idempotent bimonad on pointed sets
-- (need to check some coherences)

∙Set = Σ Set (λ X → X)

∙Set[_,_] : ∙Set → ∙Set → Set
∙Set[ (A , a) , (B , b) ] = Σ (A → B) λ f → f a ≡ b

_∙×_ : ∙Set → ∙Set → ∙Set
(A , a) ∙× (B , b) = (A × B) , (a , b)

-- left version, there's also a right version
-- note that this isn't a coproduct
-- wedge sum is the coproduct
_∙+_ : ∙Set → ∙Set → ∙Set
(A , a) ∙+ (B , b) = (A ⊎ B) , inj₁ a

∙id : ∀{∙A} → ∙Set[ ∙A , ∙A ]
∙id = (λ a → a) , refl

_∘_ : ∀ {∙A ∙B ∙C} → ∙Set[ ∙A , ∙B ] → ∙Set[ ∙B , ∙C ] → ∙Set[ ∙A , ∙C ]
(f , p) ∘ (g , q) = (λ x → g (f x)) , trans (cong g p) q

-- terminal and initial
∙1 : ∙Set
∙1 = ⊤ , tt

∙![_] : ∀ ∙A → ∙Set[ ∙A , ∙1 ]
∙![ (A , a) ] = (λ _ → tt) , refl

∙!-uniq : ∀ {∙A} {x : ∙A .proj₁} → (∙f : ∙Set[ ∙A , ∙1 ]) → (∙f .proj₁) x ≡ (∙![ ∙A ] .proj₁) x
∙!-uniq {A , a} {x} (f , p) = refl

∙¡[_] : ∀ ∙A → ∙Set[ ∙1 , ∙A ]
∙¡[ A , a ] = (λ _ → a) , refl

∙¡-uniq : ∀ {∙A} → (∙f : ∙Set[ ∙1 , ∙A ]) → (∙f .proj₁) tt ≡ (∙¡[ ∙A ] .proj₁) tt
∙¡-uniq (f , p) = p

record ∙Iso[_,_] (∙A ∙B : ∙Set) : Set where
  constructor iso
  field
    ∙f : ∙Set[ ∙A , ∙B ]
    ∙g : ∙Set[ ∙B , ∙A ]
  f = ∙f .proj₁
  g = ∙g .proj₁
  field
    f-g : ∀ b → f (g b) ≡ b
    g-f : ∀ a → g (f a) ≡ a

open ∙Iso[_,_]

∙Iso⁻¹ : ∀ {∙A ∙B} → ∙Iso[ ∙A , ∙B ] → ∙Iso[ ∙B , ∙A ]
∙Iso⁻¹ (iso ∙f ∙g f-g g-f) = iso ∙g ∙f g-f f-g

Sing : ∙Set → ∙Set
Sing (A , a) = Singleton A a , a , refl

Sing[_,_] : ∀ ∙A ∙B → ∙Set[ ∙A , ∙B ] → ∙Set[ Sing ∙A , Sing ∙B  ]
Sing[ (A , a) , (B , .(f a)) ] (f , refl) = (λ { (x , refl) → f x , refl }) , refl

-- monad
η[_] : ∀ ∙A → ∙Set[ ∙A , Sing ∙A ]
η[ (A , a) ] = (λ x → a , refl) , refl

-- Sing(A) is terminal
η-uniq : ∀ {∙A} {x : ∙A .proj₁} → (∙f : ∙Set[ ∙A , Sing ∙A ]) → (∙f .proj₁) x ≡ (η[ ∙A ] .proj₁) x
η-uniq {A , a} (f , p) = pointed-all-paths

Sing≃1 : ∀ {∙A} → ∙Iso[ Sing ∙A , ∙1 ]
Sing≃1 {∙A@(A , a)} = iso ∙![ Sing ∙A ] ( ((λ _ → a) , refl) ∘ η[ ∙A ]) (λ _ → refl) (λ _ → pointed-all-paths)

μ[_] : ∀ ∙A → ∙Iso[ Sing (Sing ∙A) , Sing ∙A ]
μ[ (A , a) ] = iso ((λ { (.(a , refl) , refl) → a , refl }) , refl)
                   ((λ { (a , refl) → (a , refl) , refl }) , refl)
                   (λ { (a , refl) → refl})
                   (λ { ((a , refl) , refl) → refl })

-- check
Sη-μ : ∀ {∙A} → ((Sing[ ∙A , Sing ∙A ] η[ ∙A ] ∘ (μ[ ∙A ] .∙f)) .proj₁) (∙A .proj₂ , refl) ≡ (∙A .proj₂ , refl)
Sη-μ = refl

ηS-μ : ∀ {∙A} → ((Sing[ Sing ∙A , Sing (Sing ∙A) ] η[ Sing ∙A ] ∘ (μ[ Sing ∙A ] .∙f)) .proj₁) ((∙A .proj₂ , refl) , refl) ≡ ((∙A .proj₂ , refl) , refl)
ηS-μ = refl

-- strength
σ×[_,_] : ∀ ∙A ∙B → ∙Set[ Sing ∙A ∙× ∙B , Sing (∙A ∙× ∙B) ]
σ×[ (A , a) , (B , b) ] = (λ { ((a , refl) , _) → (a , b) , refl }) , refl

τ×[_,_] : ∀ ∙A ∙B → ∙Set[ ∙A ∙× Sing ∙B , Sing (∙A ∙× ∙B) ]
τ×[ (A , a) , (B , b) ] = (λ { (_ , (b , refl)) → (a , b) , refl }) , refl

σ+[_,_] : ∀ ∙A ∙B → ∙Set[ Sing ∙A ∙+ ∙B , Sing (∙A ∙+ ∙B) ]
σ+[ (A , a) , (B , b) ] = (λ _ → inj₁ a , refl) , refl

τ+[_,_] : ∀ ∙A ∙B → ∙Set[ ∙A ∙+ Sing ∙B , Sing (∙A ∙+ ∙B) ]
τ+[ (A , a) , (B , b) ] = (λ _ → inj₁ a , refl) , refl

-- comonad
ε[_] : ∀ ∙A → ∙Set[ Sing ∙A , ∙A ]
ε[ (A , a) ] = (λ { (x , refl) → x }) , refl

δ[_] : ∀ ∙A → ∙Iso[ Sing ∙A , Sing (Sing ∙A) ]
δ[ ∙A ] = ∙Iso⁻¹ μ[ ∙A ]

-- check
δ-Sε : ∀ {∙A} → ((δ[ ∙A ] .∙f ∘ Sing[ Sing ∙A , ∙A ] ε[ ∙A ]) .proj₁) (∙A .proj₂ , refl) ≡ (∙A .proj₂ , refl)
δ-Sε = refl

δ-εS : ∀ {∙A} → ((δ[ Sing ∙A ] .∙f ∘ Sing[ Sing (Sing ∙A) , Sing ∙A ] ε[ Sing ∙A ]) .proj₁) ((∙A .proj₂ , refl) , refl) ≡ ((∙A .proj₂ , refl) , refl)
δ-εS = refl

-- costrength
σ'×[_,_] : ∀ ∙A ∙B → ∙Set[ Sing (∙A ∙× ∙B) , Sing ∙A ∙× ∙B ]
σ'×[ (A , a) , (B , b) ] = (λ { (.(a , b) , refl) → (a , refl) , b }) , refl

τ'×[_,_] : ∀ ∙A ∙B → ∙Set[ Sing (∙A ∙× ∙B) , ∙A ∙× Sing ∙B ]
τ'×[ (A , a) , (B , b) ] = (λ { (.(a , b) , refl) → a , (b , refl) }) , refl

σ'+[_,_] : ∀ ∙A ∙B → ∙Set[ Sing (∙A ∙+ ∙B) , Sing ∙A ∙+ ∙B ]
σ'+[ (A , a) , (B , b) ] = (λ _ → inj₁ (a , refl)) , refl

τ'+[_,_] : ∀ ∙A ∙B → ∙Set[ Sing (∙A ∙+ ∙B) , ∙A ∙+ Sing ∙B ]
τ'+[ (A , a) , (B , b) ] = (λ _ → inj₁ a) , refl

-- even better, strong monoidal functor
ν×[_,_] : ∀ ∙A ∙B → ∙Iso[ Sing ∙A ∙× Sing ∙B , Sing (∙A ∙× ∙B) ]
ν×[ (A , a) , (B , b) ] = iso ((λ _ → (a , b) , refl) , refl)
                              ((λ _ → (a , refl) , b , refl) , refl)
                              (λ { (.(a , b) , refl) → refl })
                              (λ { ((a , refl) , (b , refl)) → refl })

-- this one is only lax
ν+[_,_] : ∀ ∙A ∙B → ∙Set[ Sing ∙A ∙+ Sing ∙B , Sing (∙A ∙+ ∙B) ]
ν+[ (A , a) , (B , b) ] = (λ _ → inj₁ a , refl) , refl

-- free pointed set
U : ∙Set → Set
U = proj₁

U[_,_] : ∀ ∙A ∙B → ∙Set[ ∙A , ∙B ] → (U ∙A → U ∙B)
U[ _ , _ ] = proj₁

F : Set → ∙Set
F A = (A ⊎ ⊤) , inj₂ tt

F[_,_] : ∀ A B → (A → B) → ∙Set[ F A , F B ]
F[ A , B ] f = (λ { (inj₁ a) → inj₁ (f a) ; (inj₂ tt) → inj₂ tt }) , refl

->adj : ∀ {A ∙B} → (A → U ∙B) → ∙Set[ F A , ∙B ]
->adj f = (λ { (inj₁ a) → f a ; (inj₂ tt) → _ }) , refl

<-adj : ∀ {A ∙B} → ∙Set[ F A , ∙B ] → (A → U ∙B)
<-adj (f , refl) a = f (inj₁ a)

η-adj : ∀ {A} → (A → U (F A))
η-adj = <-adj ∙id

ε-adj : ∀ {∙A} → ∙Set[ F (U ∙A), ∙A ]
ε-adj = ->adj λ x → x

-- maybe monad
T : Set → Set
T A = U (F A)

T-η[_] : ∀ A → (A → T A)
T-η[ A ] = η-adj

T-μ[_] : ∀ A → (T (T A) → T A)
T-μ[ A ] = U[ F (T A) , F A ] ε-adj

-- comaybe comonad
D : ∙Set → ∙Set
D ∙A = F (U ∙A)

D-ε[_] : ∀ ∙A → ∙Set[ D ∙A , ∙A ]
D-ε[ ∙A ] = ε-adj

D-δ[_] : ∀ ∙A → ∙Set[ D ∙A , D (D ∙A) ]
D-δ[ ∙A ] = F[ U ∙A , U (D ∙A) ] η-adj

-- but also
D-η[_] : ∀ ∙A → ∙Set[ ∙A , D ∙A ]
D-η[ ∙A ] = (λ _ → inj₂ tt) , refl
-- D ∙A is not contractible

-- distributive laws?
-- same as ∙Set[ ∙1 , D ∙1 ] so follows D-η
Λ : ∀ {∙A} → ∙Set[ Sing (D ∙A) , D (Sing ∙A) ]
Λ = (λ { (.(inj₂ tt) , refl) → inj₂ tt }) , refl


------------------------------------------------------------------------
-- Products
------------------------------------------------------------------------

module Data.Product where

open import Data.Function
open import Relation.Nullary.Core

infixr 4 _,_
infix  4 ,_
infixr 2 _×_ _-×-_ _-,-_

------------------------------------------------------------------------
-- Definition

data Σ (A : Set) (B : A → Set) : Set where
  _,_ : (x : A) (y : B x) → Σ A B

∃ : {A : Set} → (A → Set) → Set
∃ = Σ _

∄ : {A : Set} → (A → Set) → Set
∄ P = ¬ ∃ P

∃₂ : {A : Set} {B : A → Set} (C : (x : A) → B x → Set) → Set
∃₂ C = ∃ λ a → ∃ λ b → C a b

_×_ : (A B : Set) → Set
A × B = Σ A (λ _ → B)

------------------------------------------------------------------------
-- Functions

-- Sometimes the first component can be inferred.

,_ : ∀ {A} {B : A → Set} {x} → B x → ∃ B
, y = _ , y

proj₁ : ∀ {A B} → Σ A B → A
proj₁ (x , y) = x

proj₂ : ∀ {A B} → (p : Σ A B) → B (proj₁ p)
proj₂ (x , y) = y

<_,_> : ∀ {A} {B : A → Set} {C : ∀ {x} → B x → Set}
        (f : (x : A) → B x) → ((x : A) → C (f x)) →
        ((x : A) → Σ (B x) C)
< f , g > x = (f x , g x)

map : ∀ {A B P Q} →
      (f : A → B) → (∀ {x} → P x → Q (f x)) →
      Σ A P → Σ B Q
map f g = < f ∘ proj₁ , g ∘ proj₂ >

zip : ∀ {A B C P Q R} →
      (_∙_ : A → B → C) →
      (∀ {x y} → P x → Q y → R (x ∙ y)) →
      Σ A P → Σ B Q → Σ C R
zip _∙_ _∘_ p₁ p₂ = (proj₁ p₁ ∙ proj₁ p₂ , proj₂ p₁ ∘ proj₂ p₂)

swap : ∀ {A B} → A × B → B × A
swap = < proj₂ , proj₁ >

_-×-_ : ∀ {A B} → (A → B → Set) → (A → B → Set) → (A → B → Set)
f -×- g = f -[ _×_ ]₁- g

_-,-_ : ∀ {A B C D} → (A → B → C) → (A → B → D) → (A → B → C × D)
f -,- g = f -[ _,_ ]- g

curry : {A : Set} {B : A → Set} {C : Σ A B → Set} →
        ((p : Σ A B) → C p) →
        ((x : A) → (y : B x) → C (x , y))
curry f x y = f (x , y)

uncurry : {A : Set} {B : A → Set} {C : Σ A B → Set} →
          ((x : A) → (y : B x) → C (x , y)) →
          ((p : Σ A B) → C p)
uncurry f (p₁ , p₂) = f p₁ p₂


{-# OPTIONS --without-K --safe #-}
module Categories.Utils.Product where

open import Level
open import Data.Product using (_×_; Σ; _,_; proj₁; proj₂)
open import Relation.Binary.PropositionalEquality

-- "very dependent" versions of map and zipWith
map⁎ : ∀ {a b p q} {A : Set a} {B : A → Set b} {P : A → Set p} {Q : {x : A} → P x → B x → Set q} →
      (f : (x : A) → B x) → (∀ {x} → (y : P x) → Q y (f x)) → (v : Σ A P) → Σ (B (proj₁ v)) (Q (proj₂ v))
map⁎ f g (x , y) = (f x , g y)

map⁎′ : ∀ {a b p q} {A : Set a} {B : A → Set b} {P : Set p} {Q : P → Set q} → (f : (x : A) → B x) → ((x : P) → Q x) → (v : A × P) → B (proj₁ v) × Q (proj₂ v)
map⁎′ f g (x , y) = (f x , g y)

zipWith : ∀ {a b c p q r s} {A : Set a} {B : Set b} {C : Set c} {P : A → Set p} {Q : B → Set q} {R : C → Set r} {S : (x : C) → R x → Set s} (_∙_ : A → B → C) → (_∘_ : ∀ {x y} → P x → Q y → R (x ∙ y)) → (_*_ : (x : C) → (y : R x) → S x y) → (x : Σ A P) → (y : Σ B Q) → S (proj₁ x ∙ proj₁ y) (proj₂ x ∘ proj₂ y)
zipWith _∙_ _∘_ _*_ (a , p) (b , q) = (a ∙ b) * (p ∘ q)
syntax zipWith f g h = f -< h >- g


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

module HoTT.Identity.Universe {i} {A B : 𝒰 i} where

-- Axiom 2.10.3 - univalence
postulate
  idtoeqv-equiv : isequiv (idtoeqv {A = A} {B = B})

=𝒰-equiv : (A == B) ≃ (A ≃ B)
=𝒰-equiv = idtoeqv , idtoeqv-equiv

module _ where
  open qinv (isequiv→qinv idtoeqv-equiv)
  abstract
    ua : A ≃ B → A == B
    ua = g
    =𝒰-η : ua ∘ idtoeqv ~ id
    =𝒰-η = η
    =𝒰-β : idtoeqv ∘ ua ~ id
    =𝒰-β = ε

_=𝒰_ = _≃_


{-

The Inductive Version of James Construction

This file contains:
  - An inductive family 𝕁, and its direct colimit is equivalence to James;
    (KANG Rongji, Feb. 2022)
  - The family 𝕁 can be iteratively constructed as pushouts;
  - Special cases of 𝕁 n for n = 0, 1 and 2;
  - Connectivity of inclusion maps.

This file is the summary of the main results.
The proof is divided into parts and put inside the fold Cubical.HITs.James.Inductive

-}
{-# OPTIONS --safe #-}
module Cubical.HITs.James.Inductive where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Pointed

open import Cubical.Data.Nat
open import Cubical.Data.Unit
open import Cubical.Data.Sigma

open import Cubical.HITs.Wedge
open import Cubical.HITs.Pushout
open import Cubical.HITs.Pushout.PushoutProduct
open import Cubical.HITs.SequentialColimit

open import Cubical.HITs.James.Base
open import Cubical.HITs.James.Inductive.Base
open import Cubical.HITs.James.Inductive.PushoutFormula
  renaming (isConnectedIncl to connIncl ; isConnectedInl to connInl)
open import Cubical.HITs.James.Inductive.Reduced
open import Cubical.HITs.James.Inductive.ColimitEquivalence

open import Cubical.Homotopy.Connected

private
  variable
    ℓ : Level

module JamesInd
  (X∙@(X , x₀) : Pointed ℓ) where

  -- The family 𝕁 n is equivalence to Brunerie's J n, as will be shown latter.
  -- Instead of his inductive procedure, 𝕁 is defined directly as an indexed HIT.

  𝕁 : ℕ → Type ℓ
  𝕁 = 𝕁ames (X , x₀)

  -- This family forms a direct system.

  𝕁Seq : Sequence ℓ
  𝕁Seq = 𝕁amesSeq (X , x₀)

  -- The inductive construction of James is called 𝕁∞.
  -- It is the direct colimit of 𝕁 n.

  𝕁∞ : Type ℓ
  𝕁∞ = Lim→ 𝕁Seq

  -- And of course it is equivalent to James.

  J≃𝕁∞ : James (X , x₀) ≃ 𝕁∞
  J≃𝕁∞ = compEquiv (James≃𝕁Red∞ _) (invEquiv (𝕁ames∞≃𝕁Red∞ _))

  -- Special cases of 𝕁 n for n = 0, 1 and 2:

  𝕁₀≃Unit : 𝕁 0 ≃ Unit
  𝕁₀≃Unit = 𝕁ames0≃ _

  𝕁₁≃X : 𝕁 1 ≃ X
  𝕁₁≃X = 𝕁ames1≃ _

  𝕁₂≃P[X×X←X⋁X→X] : 𝕁 2 ≃ Pushout ⋁↪ fold⋁
  𝕁₂≃P[X×X←X⋁X→X] = 𝕁ames2≃ _

  -- The following is defined as pushouts of 𝕁 n.

  𝕁Push : ℕ → Type ℓ
  𝕁Push = 𝕁amesPush (X , x₀)

  -- Brunerie uses f and g to denote the following maps, so do I.

  module _
    {n : ℕ} where

    f : 𝕁Push n → X × 𝕁 (1 + n)
    f = leftMap _

    g : 𝕁Push n → 𝕁 (1 + n)
    g = rightMap _

  -- Here we show that 𝕁 (n+2) can be made as double pushouts invoving only X, 𝕁 n and 𝕁 (n+1).
  -- In particular, our 𝕁 is exactly what Brunerie had defined.

  𝕁ₙ₊₂≃Pushout : (n : ℕ) → 𝕁 (2 + n) ≃ Pushout f g
  𝕁ₙ₊₂≃Pushout = 𝕁ames2+n≃ _

  -- Connectivity of inclusion maps:

  module _
    (d : ℕ)(conn : isConnected (1 + d) X) where

    -- Warning:
    -- The connectivity is shifted by 2 from the convention of usual homotopy theory.

    -- If X is (d+1)-connected, the transition incl : 𝕁 n → 𝕁 (n+1) will be (n+1)d-connected.

    isConnectedIncl : (n : ℕ) → isConnectedFun ((1 + n) · d) (incl {n = n})
    isConnectedIncl = connIncl X∙ d conn

    -- If X is (d+1)-connected, the inclusion inl : 𝕁 n → 𝕁∞ will be (n+1)d-connected.

    inl∞ : (n : ℕ) → 𝕁 n → 𝕁∞
    inl∞ _ = inl

    isConnectedInl : (n : ℕ) → isConnectedFun ((1 + n) · d) (inl∞ n)
    isConnectedInl = connInl X∙ d conn


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

open import Categories.Category.Core using (Category)

module Categories.Diagram.Coequalizer {o ℓ e} (𝒞 : Category o ℓ e) where

open Category 𝒞
open HomReasoning
open Equiv

open import Categories.Morphism 𝒞
open import Categories.Morphism.Reasoning 𝒞

open import Level
open import Function using (_$_)

private
  variable
    A B C : Obj
    h i j k : A ⇒ B

record IsCoequalizer {E} (f g : A ⇒ B) (arr : B ⇒ E) : Set (o ⊔ ℓ ⊔ e) where
  field
    equality   : arr ∘ f ≈ arr ∘ g
    coequalize : {h : B ⇒ C} → h ∘ f ≈ h ∘ g → E ⇒ C
    universal  : {h : B ⇒ C} {eq : h ∘ f ≈ h ∘ g} → h ≈ coequalize eq ∘ arr
    unique     : {h : B ⇒ C} {i : E ⇒ C} {eq : h ∘ f ≈ h ∘ g} → h ≈ i ∘ arr → i ≈ coequalize eq

  unique′ : (eq eq′ : h ∘ f ≈ h ∘ g) → coequalize eq ≈ coequalize eq′
  unique′ eq eq′ = unique universal

  id-coequalize : id ≈ coequalize equality
  id-coequalize = unique (⟺ identityˡ)

  coequalize-resp-≈ : ∀ {eq :  h ∘ f ≈ h ∘ g} {eq′ : i ∘ f ≈ i ∘ g} →
    h ≈ i → coequalize eq ≈ coequalize eq′
  coequalize-resp-≈ {h = h} {i = i} {eq = eq} {eq′ = eq′} h≈i = unique $ begin
    i                   ≈˘⟨ h≈i ⟩
    h                   ≈⟨ universal ⟩
    coequalize eq ∘ arr ∎

  coequalize-resp-≈′ : (eq :  h ∘ f ≈ h ∘ g) → (eq′ : i ∘ f ≈ i ∘ g) →
    h ≈ i → j ≈ coequalize eq → k ≈ coequalize eq′ → j ≈ k
  coequalize-resp-≈′ {j = j} {k = k} eq eq′ h≈i eqj eqk = begin
    j              ≈⟨ eqj ⟩
    coequalize eq  ≈⟨ coequalize-resp-≈ h≈i ⟩
    coequalize eq′ ≈˘⟨ eqk ⟩
    k              ∎

-- This could be proved via duality, but is easier to just write by hand,
-- as it makes the dependency graph a lot cleaner.
IsCoequalizer⇒Epi : IsCoequalizer h i j → Epi j
IsCoequalizer⇒Epi coeq _ _ eq =
  coequalize-resp-≈′ (extendˡ equality) (extendˡ equality) eq (unique refl) (unique refl)
  where
    open IsCoequalizer coeq

record Coequalizer (f g : A ⇒ B) : Set (o ⊔ ℓ ⊔ e) where
  field
    {obj} : Obj
    arr   : B ⇒ obj
    isCoequalizer : IsCoequalizer f g arr

  open IsCoequalizer isCoequalizer public

Coequalizer⇒Epi : (e : Coequalizer h i) → Epi (Coequalizer.arr e)
Coequalizer⇒Epi coeq = IsCoequalizer⇒Epi isCoequalizer
  where
    open Coequalizer coeq

-- Proving this via duality arguments is kind of annoying, as ≅ does not behave nicely in
-- concert with op.
up-to-iso : (coe₁ coe₂ : Coequalizer h i) → Coequalizer.obj coe₁ ≅ Coequalizer.obj coe₂
up-to-iso coe₁ coe₂ = record
  { from = repack coe₁ coe₂
  ; to = repack coe₂ coe₁
  ; iso = record
    { isoˡ = repack-cancel coe₂ coe₁
    ; isoʳ = repack-cancel coe₁ coe₂
    }
  }
  where
    open Coequalizer

    repack : (coe₁ coe₂ : Coequalizer h i) → obj coe₁ ⇒ obj coe₂
    repack coe₁ coe₂ = coequalize coe₁ (equality coe₂)

    repack∘ : (coe₁ coe₂ coe₃ : Coequalizer h i) → repack coe₂ coe₃ ∘ repack coe₁ coe₂ ≈ repack coe₁ coe₃
    repack∘ coe₁ coe₂ coe₃ = unique coe₁ (⟺ (glueTrianglesˡ (⟺ (universal coe₂)) (⟺ (universal coe₁)))) -- unique e₃ (⟺ (glueTrianglesʳ (⟺ (universal e₃)) (⟺ (universal e₂))))

    repack-cancel : (e₁ e₂ : Coequalizer h i) → repack e₁ e₂ ∘ repack e₂ e₁ ≈ id
    repack-cancel coe₁ coe₂ = repack∘ coe₂ coe₁ coe₂ ○ ⟺ (id-coequalize coe₂)

IsCoequalizer⇒Coequalizer : IsCoequalizer h i k → Coequalizer h i
IsCoequalizer⇒Coequalizer {k = k} is-coe = record
  { arr = k
  ; isCoequalizer = is-coe
  }


------------------------------------------------------------------------
-- The Agda standard library
--
-- W-types
------------------------------------------------------------------------

module Data.W where

open import Level
open import Relation.Nullary

-- The family of W-types.

data W {a b} (A : Set a) (B : A → Set b) : Set (a ⊔ b) where
  sup : (x : A) (f : B x → W A B) → W A B

-- Projections.

head : ∀ {a b} {A : Set a} {B : A → Set b} →
       W A B → A
head (sup x f) = x

tail : ∀ {a b} {A : Set a} {B : A → Set b} →
       (x : W A B) → B (head x) → W A B
tail (sup x f) = f

-- If B is always inhabited, then W A B is empty.

inhabited⇒empty : ∀ {a b} {A : Set a} {B : A → Set b} →
                  (∀ x → B x) → ¬ W A B
inhabited⇒empty b (sup x f) = inhabited⇒empty b (f (b x))


import      Lvl

module Function.Names where

open import Functional.Dependent
open import Logic.Predicate
open import Structure.Setoid
open import Type

private variable ℓ ℓₒ ℓₒ₁ ℓₒ₂ ℓₒ₃ ℓₗ ℓₗ₁ ℓₗ₂ ℓₗ₃ : Lvl.Level

module _ {A : Type{ℓₒ₁}} {B : A → Type{ℓₒ₂}} ⦃ _ : ∀{a} → Equiv{ℓₗ₃}(B(a)) ⦄ where
  -- Extensional equality on functions.
  -- Alternative definition: f ⊜ g = (∀{x} → (f(x) ≡ g(x)))
  _⊜_ : ((a : A) → B(a)) → ((a : A) → B(a)) → Type
  _⊜_ = ∀¹ ∘₂ pointwise₂,₁(_≡_)
  _⊜₁_ = _⊜_

module _ {A : Type{ℓₒ₁}} {B : A → Type{ℓₒ₂}} ⦃ _ : ∀{a} → Equiv{ℓₗ₃}(B(a)) ⦄ where
  _⊜ᵢ_ : ({a : A} → B(a)) → ({a : A} → B(a)) → Type
  _⊜ᵢ_ f g = (∀{x} → (f{x} ≡ g{x}))

module _ {A : Type{ℓₒ₁}} {B : A → Type{ℓₒ₂}} ⦃ _ : ∀{a} → Equiv{ℓₗ₃}(B(a)) ⦄ where
  _⦃⊜⦄_ : (⦃ a : A ⦄ → B(a)) → (⦃ a : A ⦄ → B(a)) → Type
  _⦃⊜⦄_ f g = (∀{x} → (f ⦃ x ⦄ ≡ g ⦃ x ⦄))

module _ {A₁ : Type{ℓₒ₁}} {A₂ : A₁ → Type{ℓₒ₂}} {B : (a₁ : A₁) → A₂(a₁) → Type{ℓₒ₂}} ⦃ _ : ∀{a₁}{a₂} → Equiv{ℓₗ₃}(B(a₁)(a₂)) ⦄ where
  -- Alternative definition: f ⊜ g = (∀{x}{y} → (f(x)(y) ≡ g(x)(y)))
  _⊜₂_ : (f g : ∀(a₁)(a₂) → B(a₁)(a₂)) → Type
  _⊜₂_ = ∀¹ ∘₂ (∀¹ ∘₃ pointwise₂,₂(_≡_))

module _ {A₁ : Type{ℓₒ₁}} {A₂ : A₁ → Type{ℓₒ₂}} {A₃ : (a₁ : A₁) → A₂(a₁) → Type{ℓₒ₂}} {B : (a₁ : A₁) → (a₂ : A₂(a₁)) → A₃(a₁)(a₂) → Type{ℓₒ₂}} ⦃ _ : ∀{a₁}{a₂}{a₃} → Equiv{ℓₗ₃}(B(a₁)(a₂)(a₃)) ⦄ where
  -- Alternative definition: f ⊜ g = (∀{x}{y}{z} → (f(x)(y)(z) ≡ g(x)(y)(z)))
  _⊜₃_ : (f g : ∀(a₁)(a₂)(a₃) → B(a₁)(a₂)(a₃)) → Type
  _⊜₃_ = ∀¹ ∘₂ (∀¹ ∘₃ (∀¹ ∘₄ pointwise₂,₃(_≡_)))


firstTrue : (f : ℕ → Bool) → ∃ (λ n → f n ≡ true) → ℕ
firstTrue f prf = mp-ℕ

firstTrue-true : firstTrue f prf ≡ n → f n ≡ true
firstTrue-true = ?

firstTrue-false : firstTrue f prf ≡ n → ∀ m → m < n → f m ≡ false
firstTrue-false = ?


module BBHeap {A : Set}(_≤_ : A → A → Set) where

open import BHeap _≤_ hiding (forget ; # ; flatten)
open import Bound.Lower A
open import Bound.Lower.Order _≤_
open import BTree {A} hiding (flatten)
open import Data.Nat
open import Data.List

mutual
  data BBHeap : Bound → Set where
    leaf : {b : Bound} → BBHeap b
    left : {b : Bound}{x : A}{l r : BBHeap (val x)}
                   → LeB b (val x)
                   → l ⋘ r
                   → BBHeap b
    right : {b : Bound}{x : A}{l r : BBHeap (val x)}
                   → LeB b (val x)
                   → l ⋙ r
                   → BBHeap b

  data _≃_ : {b b' : Bound} → BBHeap b → BBHeap b' → Set where
    ≃lf : {b b' : Bound} → leaf {b} ≃ leaf {b'}
    ≃nd : {b b' : Bound}{x x' : A}{l r : BBHeap (val x)}{l' r' : BBHeap (val x')}
                  → (b≤x : LeB b (val x))
                  → (b'≤x' : LeB b' (val x'))
                  → (l⋘r : l ⋘ r)
                  → (l'⋘r' : l' ⋘ r')
                  → l ≃ r
                  → l' ≃ r'
                  → l ≃ l'
                  → (left b≤x l⋘r) ≃ (left b'≤x' l'⋘r')

  data _⋘_ : {b b' : Bound} → BBHeap b → BBHeap b' → Set where
    lf⋘ : {b b' : Bound}
                  → leaf {b} ⋘ leaf {b'}
    ll⋘ : {b b' : Bound}{x x' : A}{l r : BBHeap (val x)}{l' r' : BBHeap (val x')}
                  → (b≤x : LeB b (val x))
                  → (b'≤x' : LeB b' (val x'))
                  → (l⋘r : l ⋘ r)
                  → (l'⋘r' : l' ⋘ r')
                  → (l'≃r' : l' ≃ r')
                  → r ≃ l'
                  → (left b≤x l⋘r) ⋘ (left b'≤x' l'⋘r')
    lr⋘ : {b b' : Bound}{x x' : A}{l r : BBHeap (val x)}{l' r' : BBHeap (val x')}
                  → (b≤x : LeB b (val x))
                  → (b'≤x' : LeB b' (val x'))
                  → (l⋙r : l ⋙ r)
                  → (l'⋘r' : l' ⋘ r')
                  → (l'≃r' : l' ≃ r')
                  → l ⋗ l'
                  → (right b≤x l⋙r) ⋘ (left b'≤x' l'⋘r')

  data _⋙_ : {b b' : Bound} → BBHeap b → BBHeap b' → Set where
    ⋙lf : {b b' : Bound}{x : A}
                  → (b≤x : LeB b (val x))
                  → (left b≤x lf⋘) ⋙ leaf {b'}
    ⋙rl : {b b' : Bound}{x x' : A}{l r : BBHeap (val x)}{l' r' : BBHeap (val x')}
                  → (b≤x : LeB b (val x))
                  → (b'≤x' : LeB b' (val x'))
                  → (l⋘r : l ⋘ r)
                  → (l≃r : l ≃ r)
                  → (l'⋘r' : l' ⋘ r')
                  → l ⋗ r'
                  → (left b≤x l⋘r) ⋙ (left b'≤x' l'⋘r')
    ⋙rr : {b b' : Bound}{x x' : A}{l r : BBHeap (val x)}{l' r' : BBHeap (val x')}
                  → (b≤x : LeB b (val x))
                  → (b'≤x' : LeB b' (val x'))
                  → (l⋘r : l ⋘ r)
                  → (l≃r : l ≃ r)
                  → (l'⋙r' : l' ⋙ r')
                  →  l ≃ l'
                  → (left b≤x l⋘r) ⋙ (right b'≤x' l'⋙r')

  data _⋗_ : {b b' : Bound} → BBHeap b → BBHeap b' → Set where
    ⋗lf : {b b' : Bound}{x : A}
                  → (b≤x : LeB b (val x))
                  → (left b≤x lf⋘) ⋗ (leaf {b'})
    ⋗nd : {b b' : Bound}{x x' : A}{l r : BBHeap (val x)}{l' r' : BBHeap (val x')}
                  → (b≤x : LeB b (val x))
                  → (b'≤x' : LeB b' (val x'))
                  → (l⋘r : l ⋘ r)
                  → (l'⋘r' : l' ⋘ r')
                  → l ≃ r
                  → l' ≃ r'
                  → l ⋗ l'
                  → (left b≤x l⋘r) ⋗ (left b'≤x' l'⋘r')

# : {b : Bound} → BBHeap b → ℕ
# leaf = zero
# (left {l = l} {r = r} _ _) = suc (# l + # r)
# (right {l = l} {r = r} _ _) = suc (# l + # r)

relax : {b : Bound}(h : BBHeap b) → BHeap b
relax leaf = lf
relax (left {l = l} {r = r} b≤x _) = nd b≤x (relax l) (relax r)
relax (right {l = l} {r = r} b≤x _) = nd b≤x (relax l) (relax r)

forget : {b : Bound}(h : BBHeap b) → BTree
forget leaf = leaf
forget (left {x = x} {l = l} {r = r} _ _) = node x (forget l) (forget r)
forget (right {x = x} {l = l} {r = r} _ _) = node x (forget l) (forget r)

flatten : {b : Bound} → BBHeap b → List A
flatten leaf = []
flatten (left {x = x} {l = l} {r = r} _ _) = x ∷ flatten l ++ flatten r
flatten (right {x = x} {l = l} {r = r} _ _) = x ∷ flatten l ++ flatten r


-- Empty, unit and equality.

⊥ = (X : Set) → X
⊤ = (X : Set) → X → X

data _≡_ {l}{A : Set l}(x : A) : A → Set l where
  <> : x ≡ x

-- The fixpoint of the identity functor as a data definition.

module Data where

  data μId : Set where
    In : μId → μId

-- μId can be proved empty. Here are both a direct proof and one that
-- relies on the eliminator for μId.

  ¬μId : μId → ⊥
  ¬μId (In x) = ¬μId x

  μId-elim : ∀ {l}(P : μId → Set l) → (∀ x → P x → P (In x)) → ∀ x → P x
  μId-elim P m (In x) = m x (μId-elim P m x)

  ¬Id' : μId → ⊥
  ¬Id' = μId-elim (λ _ → ⊥) (λ _ p → p)

-- To prove ∀ x → ¬ (x ≡ In x) it is enough to call ¬μId (or ¬μId'): the
-- equality proof is not inspected.

  ¬id≡In-empty : ∀ {x} → x ≡ In x → ⊥
  ¬id≡In-empty {x} _ = ¬μId x -- or ¬Id' x

-- Alternatively, one could use an absurd pattern which relies on the
-- presence of a cycle.

  ¬id≡In-pm : ∀ {x} → x ≡ In x → ⊥
  ¬id≡In-pm ()

-- The case of inductive records is a bit different. Here is the fixpoint
-- of the identity functor as an inductive record definition.

module Record where

  record μId : Set where
    inductive
    constructor In
    field       Out : μId
  open μId

-- It does not seem possible to prove Record.μId empty, as Agda does not
-- consider the following definitions as terminating.

  {-# NON_TERMINATING #-}
  ¬μId : μId → ⊥
  ¬μId (In x) = ¬μId x

  {-# NON_TERMINATING #-}
  μId-elim : ∀ {l}(P : μId → Set l) → (∀ x → P x → P (In x)) → ∀ x → P x
  μId-elim P m (In x) = m x (μId-elim P m x)

  ¬Id' : μId → ⊥
  ¬Id' = μId-elim (λ _ → ⊥) (λ _ p → p)

  ¬id≡In-empty : ∀ {x} → x ≡ In x → ⊥
  ¬id≡In-empty {x} _ = ¬μId x -- or ¬Id' x

-- However, we can still use an absurd pattern as in Data.¬id≡In-pm.

  ¬id≡In-pm : ∀ {x} → x ≡ In x → ⊥
  ¬id≡In-pm ()

-- This should not be possible.


module Structure.Setoid.Size where

import      Lvl
open import Logic
open import Logic.Propositional
open import Logic.Predicate
open import Structure.Setoid
open import Structure.Function.Domain
open import Structure.Function
open import Syntax.Function
open import Type

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

_≍_ : (A : Setoid{ℓₑ₁}{ℓ₁}) → (B : Setoid{ℓₑ₂}{ℓ₂}) → Stmt
_≍_ A B = ∃{Obj = Setoid.Type(A) → Setoid.Type(B)} (f ↦ Function(f) ∧ Bijective(f))

_≼_ : (A : Setoid{ℓₑ₁}{ℓ₁}) → (B : Setoid{ℓₑ₂}{ℓ₂}) → Stmt
_≼_ A B = ∃{Obj = Setoid.Type(A) → Setoid.Type(B)} (f ↦ Function(f) ∧ Injective(f))

_≽_ : (A : Setoid{ℓₑ₁}{ℓ₁}) → (B : Setoid{ℓₑ₂}{ℓ₂}) → Stmt
_≽_ A B = ∃{Obj = Setoid.Type(A) → Setoid.Type(B)} (f ↦ Function(f) ∧ Surjective(f))

_≭_ : (A : Setoid{ℓₑ₁}{ℓ₁}) → (B : Setoid{ℓₑ₂}{ℓ₂}) → Stmt
_≭_ A B = ¬(A ≍ B)

_≺_ : (A : Setoid{ℓₑ₁}{ℓ₁}) → (B : Setoid{ℓₑ₂}{ℓ₂}) → Stmt
_≺_ A B = (A ≼ B) ∧ (A ≭ B)

_≻_ : (A : Setoid{ℓₑ₁}{ℓ₁}) → (B : Setoid{ℓₑ₂}{ℓ₂}) → Stmt
_≻_ A B = (A ≽ B) ∧ (A ≭ B)


module Dave.Logic.Bool where

    data Bool : Set where
        false : Bool
        true : Bool

    ¬_ : Bool → Bool
    ¬ true = false
    ¬ false = true

    _∧_ : Bool → Bool → Bool
    a ∧ true = a
    a ∧ false = false

    if_then_else_ : {A : Set} → Bool → A → A → A
    if false then a else b = b
    if true then a else b = a

    identity : {A : Set} → A → A
    identity a = a


module Ord where

data Nat : Set where
  Z : Nat
  S : Nat -> Nat

data Ord : Set  where
  z : Ord
  lim : (Nat -> Ord) -> Ord

zp  : Ord -> Ord
zp z = z
zp (lim f) = lim (\x -> zp (f x))


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

open import Categories.Category

module Categories.Functor.Construction.SubCategory {o ℓ e} (C : Category o ℓ e) where

open import Categories.Category.SubCategory C

open Category C
open Equiv

open import Level
open import Function.Base using () renaming (id to id→)
open import Data.Product

open import Categories.Functor using (Functor)

private
  variable
    ℓ′ i : Level
    I : Set i
    U : I → Obj

Sub : ∀ (sub : SubCat {i} {ℓ′} I) → Functor (SubCategory sub) C
Sub (record {U = U}) = record
  { F₀ = U
  ; F₁ = proj₁
  ; identity = refl
  ; homomorphism = refl
  ; F-resp-≈ = id→
  }

FullSub : Functor (FullSubCategory U) C
FullSub {U = U} = record
  { F₀ = U
  ; F₁ = id→
  ; identity = refl
  ; homomorphism = refl
  ; F-resp-≈ = id→
  }


{-# OPTIONS --universe-polymorphism #-}
module Categories.Profunctor where

open import Level hiding (lift)

open import Categories.Category
open import Categories.Agda
open import Categories.Bifunctor using (Functor; Bifunctor; _∘_)
open import Categories.Functor.Hom
open import Categories.Lan
open import Categories.Yoneda

Profunctor : ∀ {o ℓ e} {o′ ℓ′ e′} → Category o ℓ e → Category o′ ℓ′ e′ → Set _
Profunctor {ℓ = ℓ} {e} {ℓ′ = ℓ′} {e′} C D = Bifunctor (Category.op D) C (ISetoids (ℓ ⊔ ℓ′) (e ⊔ e′))

id : ∀ {o ℓ e} → {C : Category o ℓ e} → Profunctor C C
id {C = C} = Hom[ C ][-,-]

{-
_∘_ : ∀ {o ℓ e} {o′ ℓ′ e′} {o′′ ℓ′′ e′′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′} {E : Category o′′ ℓ′′ e′′}
    → Profunctor D E → Profunctor C D → Profunctor C E
F ∘ G = {!!}
-}


module SimpleTermUnification where

open import OscarPrelude

data Term (n : Nat) : Set where
  var : Fin n → Term n
  leaf : Term n
  _fork_ : Term n → Term n → Term n

|> : ∀ {m n} → (r : Fin m → Fin n) → Fin m → Term n
|> r = var ∘ r

_<|_ : ∀ {m n} → (f : Fin m → Term n) → Term m → Term n
f <| var x = f x
f <| leaf = leaf
f <| (t₁ fork t₂) = (f <| t₁) fork (f <| t₂)

<|-assoc : ∀ {m n o} (f : Fin n → Term o) (g : Fin m → Term n) (x : Term m) → f <| (g <| x) ≡ (λ y → f <| g y) <| x
<|-assoc f g (var x) = refl
<|-assoc f g leaf = refl
<|-assoc f g (x₁ fork x₂) rewrite <|-assoc f g x₁ | <|-assoc f g x₂ = refl

_≗_ : ∀ {m n} → (f g : Fin m → Term n) → Set
f ≗ g = ∀ x → f x ≡ g x

infixr 9 _⋄_
_⋄_ : ∀ {l m n} → (f : Fin m → Term n) → (g : Fin l → Term m) → Fin l → Term n
(f ⋄ g) = (f <|_) ∘ g

⋄-assoc : ∀ {l m n o} (f : Fin n → Term o) (g : Fin m → Term n) (h : Fin l → Term m) → (f ⋄ (g ⋄ h)) ≗ ((f ⋄ g) ⋄ h)
⋄-assoc f g h x = <|-assoc f g (h x)

⋄-assoc' : ∀ {l m n o} (f : Fin n → Term o) (g : Fin m → Term n) (h : Fin l → Term m) → (f ⋄ (g ⋄ h)) ≡ ((f ⋄ g) ⋄ h)
⋄-assoc' f g h = {!⋄-assoc f g h!}

thin : ∀ {n} → (x : Fin (suc n)) → (y : Fin n) → Fin (suc n)
thin {n} zero y = suc y
thin {zero} (suc x) ()
thin {suc n} (suc x) zero = zero
thin {suc n} (suc x) (suc y) = suc (thin x y)

thick : ∀ {n} → (x y : Fin (suc n)) → Maybe (Fin n)
thick {n} zero zero = nothing
thick {n} zero (suc y) = just y
thick {zero} (suc ()) zero
thick {suc n} (suc x) zero = just zero
thick {zero} (suc ()) (suc y)
thick {suc n} (suc x) (suc y) = suc <$> thick x y

check : ∀ {n} → (x : Fin (suc n)) (t : Term (suc n)) → Maybe (Term n)
check x (var y) = var <$> thick x y
check x leaf = just leaf
check x (t₁ fork t₂) = _fork_ <$> check x t₁ <*> check x t₂

_for_ : ∀ {n} → (t' : Term n) (x : Fin (suc n)) → Fin (suc n) → Term n
(t' for x) y with thick x y
… | just y' = var y'
… | nothing = t'

data AList (n : Nat) : Nat → Set where
  anil : AList n n
  _asnoc_/_ : ∀ {m} → (σ : AList n m) → (t' : Term m) → (x : Fin (suc m)) → AList n (suc m)

sub : ∀ {n m} → (σ : AList n m) → Fin m → Term n
sub anil = var
sub (σ asnoc t' / x) = sub σ ⋄ (t' for x)

flexFlex : ∀ {m} → (x y : Fin m) → ∃ (flip AList m)
flexFlex {zero} () _
flexFlex {suc m} x y with thick x y
… | just y' = m , (anil asnoc var y' / x)
… | nothing = suc m , anil

flexRigid : ∀ {m} → (x : Fin m) (t : Term m) → Maybe (∃ (flip AList m))
flexRigid {zero} () _
flexRigid {suc m} x t with check x t
… | just t' = just $ m , anil asnoc t' / x
… | nothing = nothing

amgu : ∀ {m} → (s t : Term m) (acc : ∃ (flip AList m)) → Maybe (∃ (flip AList m))
amgu {m} leaf leaf acc = just acc
amgu {m} leaf (_ fork _) _ = nothing
amgu {m} (_ fork _) leaf _ = nothing
amgu {m} (s₁ fork s₂) (t₁ fork t₂) acc = amgu s₁ t₁ acc >>= amgu s₂ t₂
amgu {m} (var x) (var y) (.m , anil) = just $ flexFlex x y
amgu {m} (var x) t (.m , anil) = flexRigid x t
amgu {m} t (var x) (.m , anil) = flexRigid x t
amgu {suc m} s t (n , (σ asnoc r / z)) = amgu ((r for z) <| s) ((r for z) <| t) (n , σ) >>= λ {(n' , σ') → just $ n' , σ' asnoc r / z}

mgu : ∀ {m} → (s t : Term m) → Maybe (∃ (flip AList m))
mgu {m} s t = amgu s t (m , anil)

f : Fin 4 → Term 5
f n = var (thin (suc n) n) fork var (thin (suc n) n)

g : Fin 4 → Term 5
g n = var (suc n)

fs : Term 5
fs = f 0 fork (f 1 fork (f 2 fork f 3))

gs : Term 5
gs = g 0 fork (g 1 fork (g 2 fork g 3))

foo : Set
foo = {!mgu fs gs!}

Property : Nat → Set₁
Property m = ∀ {n} → (Fin m → Term n) → Set

Unifies : ∀ {m} → (s t : Term m) → Property m
Unifies s t f = f <| s ≡ f <| t

_∧_ : ∀ {m} → (P Q : Property m) → Property m
(P ∧ Q) f = P f × Q f

_⇔_ : ∀ {m} → (P Q : Property m) → Property m
(P ⇔ Q) f = (P f → Q f) × (Q f → P f)

Nothing : ∀ {m} (P : Property m) → Set
Nothing {m} P = ∀ {n} → (f : Fin m → Term n) → ¬ P f

infixl 3 _[-⋄_]_
_[-⋄_]_ : ∀ {m n} (P : Property m) (f : Fin m → Term n) → Property n
(P [-⋄ f ] g) = P (g ⋄ f)

_≤ₛ_ : ∀ {m n n'} (f : Fin m → Term n) (g : Fin m → Term n') → Set
f ≤ₛ g = ∃ λ f' → f ≗ (f' ⋄ g)

Max : ∀ {m} (P : Property m) → Property m
Max {m} P f = P f × ∀ {n} → (f' : Fin m → Term n) → P f' → f' ≤ₛ f

DClosed : ∀ {m} (P : Property m) → Set
DClosed {m} P = ∀ {n n'} (f : Fin m → Term n) (g : Fin m → Term n') → f ≤ₛ g → P g → P f

Lemma1 : ∀ {l} {P Q : Property l} {m n o}
           {p : Fin m → Term n}
           {q : Fin n → Term o}
           {a : Fin l → Term m}
         → DClosed P
         → Max (P [-⋄ a ]_) p
         → Max (Q [-⋄ (p ⋄ a)]_) q
         → Max ((P ∧ Q) [-⋄ a ]_) (q ⋄ p)
Lemma1 {P = P} {Q} {p = p} {q} {a} DCP maxP maxQ =
  let Qqpa : Q (q ⋄ p ⋄ a)
      Qqpa = fst maxQ
      Qqpa' : Q ((q ⋄ p) ⋄ a)
      Qqpa' = {!⋄-assoc q p a!}
      Pqpa : P (q ⋄ p ⋄ a)
      Pqpa = DCP (λ z → q <| (p <| a z)) (λ z → p <| a z) (q , (λ x → refl)) (fst maxP)
      lem3 : ∀ {n} → {f : Fin _ → Term n} → P (f ⋄ a) → Q (f ⋄ a) → f ≤ₛ (q ⋄ p)
      lem3 = {!!}
  in ({!!} , {!maxQ!}) , {!!}
  where
  Ppa : P (p ⋄ a)
  Ppa = fst maxP

  pMax : ∀ {n} {p' : Fin _ → Term n} → P (p' ⋄ a) → p' ≤ₛ p
  pMax {n} {p'} Pp'a = snd maxP p' Pp'a

data Step (n : Nat) : Set where
  left : Term n → Step n
  right : Term n → Step n

_+ₛ_ : ∀ {n} → (ps : List (Step n)) → (t : Term n) → Term n
[] +ₛ t = t
(left r ∷ ps) +ₛ t = (ps +ₛ t) fork r
(right l ∷ ps) +ₛ t = l fork (ps +ₛ t)

theorem-6 : ∀ m → (s t : Term m) → (r : Maybe (∃ (flip AList m))) → mgu s t ≡ r → Either (∃ λ n → ∃ λ σ → Max (Unifies s t) (sub σ) × r ≡ just (n , σ)) (Nothing (Unifies s t) × r ≡ nothing)
theorem-6 = {!!}


module Cats.Util.SetoidMorphism where

open import Data.Product using (∃-syntax ; _,_ ; proj₁ ; proj₂)
open import Level using (_⊔_ ; suc)
open import Relation.Binary using (Rel ; Setoid ; IsEquivalence ; _Preserves_⟶_)
open import Relation.Binary.SetoidReasoning

open import Cats.Util.Function using () renaming (_∘_ to _⊚_)

open Setoid renaming (_≈_ to eq)

infixr 9 _∘_

record _⇒_ {l l≈} (A : Setoid l l≈) {l′ l≈′} (B : Setoid l′ l≈′)
  : Set (l ⊔ l′ ⊔ l≈ ⊔ l≈′)
  where
  field
    arr : Carrier A → Carrier B
    resp : arr Preserves eq A ⟶ eq B

open _⇒_ public using (arr ; resp)

module _ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′} where

  infixr 4 _≈_

  record _≈_ (f g : A ⇒ B) : Set (l ⊔ l≈ ⊔ l≈′) where
    constructor ≈-intro
    field
       ≈-elim : ∀ {x y} → eq A x y → eq B (arr f x) (arr g y)

    ≈-elim′ : ∀ {x} → eq B (arr f x) (arr g x)
    ≈-elim′ = ≈-elim (refl A)

  open _≈_ public

  equiv : IsEquivalence _≈_
  equiv = record
      { refl = λ {f} → ≈-intro (resp f)
      ; sym = λ eq → ≈-intro λ x≈y → sym B (≈-elim eq (sym A x≈y))
      ; trans = λ eq₁ eq₂ → ≈-intro (λ x≈y → trans B (≈-elim eq₁ x≈y) (≈-elim′ eq₂))
      }

  setoid : Setoid (l ⊔ l≈ ⊔ l′ ⊔ l≈′) (l ⊔ l≈ ⊔ l≈′)
  setoid = record
      { Carrier = A ⇒ B
      ; _≈_ = _≈_
      ; isEquivalence = equiv
      }

id : ∀ {l l≈} {A : Setoid l l≈} → A ⇒ A
id = record { arr = λ x → x ; resp = λ x → x }

_∘_ : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → ∀ {l″ l≈″} {C : Setoid l″ l≈″}
  → B ⇒ C → A ⇒ B → A ⇒ C
_∘_ f g = record
    { arr = arr f ⊚ arr g
    ; resp = resp f ⊚ resp g
    }

∘-resp : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → ∀ {l″ l≈″} {C : Setoid l″ l≈″}
  → {f f′ : B ⇒ C} {g g′ : A ⇒ B}
  → f ≈ f′ → g ≈ g′ → f ∘ g ≈ f′ ∘ g′
∘-resp f≈f′ g≈g′ = ≈-intro (≈-elim f≈f′ ⊚ ≈-elim g≈g′)

id-l : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
   → {f : A ⇒ B}
   → id ∘ f ≈ f
id-l {f = f} = ≈-intro (resp f)

id-r : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → {f : A ⇒ B}
  → f ∘ id ≈ f
id-r {f = f} = ≈-intro (resp f)

assoc : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → ∀ {l″ l≈″} {C : Setoid l″ l≈″} {l‴ l≈‴} {D : Setoid l‴ l≈‴}
  → {f : C ⇒ D} {g : B ⇒ C} {h : A ⇒ B}
  → (f ∘ g) ∘ h ≈ f ∘ (g ∘ h)
assoc {f = f} {g} {h} = ≈-intro (resp f ⊚ resp g ⊚ resp h)

module _ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′} where

  IsInjective : A ⇒ B → Set (l ⊔ l≈ ⊔ l≈′)
  IsInjective f = ∀ {a b} → eq B (arr f a) (arr f b) → eq A a b

  IsSurjective : A ⇒ B → Set (l ⊔ l′ ⊔ l≈′)
  IsSurjective f = ∀ b → ∃[ a ] (eq B b (arr f a))


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Algebra.Semigroup.Morphism where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Functions.Embedding

open import Cubical.Algebra
open import Cubical.Algebra.Magma.Morphism

private
  variable
    s t : Level

IsSemigroupHom : (S : Semigroup s) (T : Semigroup t) → (⟨ S ⟩ → ⟨ T ⟩) → Type (ℓ-max s t)
IsSemigroupHom S T fun = Homomorphic₂ fun (Semigroup._•_ S) (Semigroup._•_ T)

record SemigroupHom (S : Semigroup s) (T : Semigroup t) : Type (ℓ-max s t) where
  constructor semigrouphom
  field
    fun : ⟨ S ⟩ → ⟨ T ⟩
    isHom : IsSemigroupHom S T fun

record SemigroupEquiv (S : Semigroup s) (T : Semigroup t) : Type (ℓ-max s t) where
  constructor semigroupequiv
  field
    eq : ⟨ S ⟩ ≃ ⟨ T ⟩
    isHom : IsSemigroupHom S T (equivFun eq)

  hom : SemigroupHom S T
  hom = record { isHom = isHom }

instance
  SemigroupHomOperators : HomOperators (Semigroup s) (Semigroup t) (ℓ-max s t)
  SemigroupHomOperators = record { _⟶ᴴ_ = SemigroupHom; _≃ᴴ_ = SemigroupEquiv }

private
  variable
    S : Semigroup s
    T : Semigroup t

SemigroupHom→MagmaHom : (S ⟶ᴴ T) → (Semigroup.magma S ⟶ᴴ Semigroup.magma T)
SemigroupHom→MagmaHom hom .MagmaHom.fun = hom .SemigroupHom.fun
SemigroupHom→MagmaHom hom .MagmaHom.isHom = hom .SemigroupHom.isHom

MagmaHom→SemigroupHom : (Semigroup.magma S ⟶ᴴ Semigroup.magma T) → (S ⟶ᴴ T)
MagmaHom→SemigroupHom hom .SemigroupHom.fun = hom .MagmaHom.fun
MagmaHom→SemigroupHom hom .SemigroupHom.isHom = hom .MagmaHom.isHom

SemigroupHom≃MagmaHom : (S ⟶ᴴ T) ≃ (Semigroup.magma S ⟶ᴴ Semigroup.magma T)
SemigroupHom≃MagmaHom = isoToEquiv (iso SemigroupHom→MagmaHom MagmaHom→SemigroupHom (λ _ → refl) (λ _ → refl))

SemigroupEquiv→MagmaEquiv : S ≃ᴴ T → Semigroup.magma S ≃ᴴ Semigroup.magma T
SemigroupEquiv→MagmaEquiv eq .MagmaEquiv.eq = eq .SemigroupEquiv.eq
SemigroupEquiv→MagmaEquiv eq .MagmaEquiv.isHom = eq .SemigroupEquiv.isHom

MagmaEquiv→SemigroupEquiv : Semigroup.magma S ≃ᴴ Semigroup.magma T → S ≃ᴴ T
MagmaEquiv→SemigroupEquiv eq .SemigroupEquiv.eq = eq .MagmaEquiv.eq
MagmaEquiv→SemigroupEquiv eq .SemigroupEquiv.isHom = eq .MagmaEquiv.isHom

SemigroupEquiv≃MagmaEquiv : (S ≃ᴴ T) ≃ (Semigroup.magma S ≃ᴴ Semigroup.magma T)
SemigroupEquiv≃MagmaEquiv = isoToEquiv (iso SemigroupEquiv→MagmaEquiv MagmaEquiv→SemigroupEquiv (λ _ → refl) (λ _ → refl))


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

-- Tested with the development version of the Agda standard library on
-- 27 May 2011.

-- Nils' idea about databases in the Agda mailing list.
-- http://thread.gmane.org/gmane.comp.lang.agda/2911/focus=2917

module FOT.Agsy.DataBase where

open import Data.Nat
open import Data.Product

open import Relation.Binary.PropositionalEquality
open ≡-Reasoning

postulate
  right-identity : ∀ n → n + 0 ≡ n
  move-suc       : ∀ m n → suc (m + n) ≡ m + suc n

db = right-identity ,′ move-suc

comm : ∀ m n → m + n ≡ n + m  -- Via auto {!-c db!}
comm zero n     = sym (proj₁ db n)
comm (suc n) n' =
  begin
    suc (n + n') ≡⟨ cong suc (comm n n') ⟩
    suc (n' + n) ≡⟨ proj₂ db n' n ⟩ (n' + suc n)
  ∎


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

module Data.List.Kleene.Properties where

open import Data.List.Kleene.Base

open import Relation.Binary
open import Relation.Unary

open import Function

module _ {a r} {A : Set a} {R : Rel A r} where
  infix 4 _≈_
  _≈_ = R

  open import Algebra.FunctionProperties _≈_

  foldr-universal : Transitive _≈_
                  → ∀ {b} {B : Set b} (h : B ⋆ → A) f e
                  → ∀[ f ⊢ Congruent₁ ]
                  → (h [] ≈ e)
                  → (∀ x xs → h (∹ x & xs) ≈ f x (h xs))
                  → ∀ xs → h xs ≈ foldr⋆ f e xs
  foldr-universal trans h f e cong-f base cons [] = base
  foldr-universal trans h f e cong-f base cons (∹ x & xs) =
    (cons x xs) ⟨ trans ⟩ cong-f (foldr-universal trans h f e cong-f base cons xs)

  foldr-fusion : Transitive _≈_
               → Reflexive _≈_
               → ∀ {b c} {B : Set b} {C : Set c} (f : C → A) {_⊕_ : B → C → C} {_⊗_ : B → A → A} e
               → ∀[ _⊗_ ⊢ Congruent₁ ]
               → (∀ x y → f (x ⊕ y) ≈ x ⊗ f y)
               → ∀ xs → f (foldr⋆ _⊕_ e xs) ≈ foldr⋆ _⊗_ (f e) xs
  foldr-fusion trans refl h {f} {g} e cong-g fuse =
    foldr-universal trans (h ∘ foldr⋆ f e) g (h e) cong-g refl (λ x xs → fuse x (foldr⋆ f e xs))


{-# OPTIONS --cubical --no-import-sorts --safe #-}

module Cubical.Homotopy.EilenbergSteenrod where

{-
This module contains the Eilenberg-Steenrod axioms for ordinary
reduced cohomology theories with binary additivity.
The axioms are based on the ones given in Cavallo's MSc thesis
(https://www.cs.cmu.edu/~ecavallo/works/thesis15.pdf) and
Buchholtz/Favonia (2018)
-}

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.HITs.Wedge
open import Cubical.HITs.Pushout
open import Cubical.HITs.Susp

open import Cubical.Data.Empty
open import Cubical.Relation.Nullary

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

open import Cubical.Algebra.Group
open import Cubical.Algebra.AbGroup
open GroupEquiv
open GroupHom

record coHomTheory {ℓ ℓ' : Level} (H : (n : Int) → Pointed ℓ → AbGroup {ℓ'}) : Type (ℓ-suc (ℓ-max ℓ ℓ'))
  where
  Boolℓ : Pointed ℓ
  Boolℓ = Lift Bool , lift true
  field
    Hmap : (n : Int) → {A B : Pointed ℓ} (f : A →∙ B) → AbGroupHom (H n B) (H n A)
    Suspension : Σ[ F ∈ ((n : Int) {A : Pointed ℓ} → AbGroupEquiv (H (sucInt n) (Susp (typ A) , north)) (H n A)) ]
                   ({A B : Pointed ℓ} (f : A →∙ B) (n : Int)
               → fun (Hmap (sucInt n) (suspFun (fst f) , refl)) ∘ invEq (eq (F n {A = B}))
                ≡ invEq (eq (F n {A = A})) ∘ fun (Hmap n f))
    Exactness : {A B : Pointed ℓ}  (f : A →∙ B) (n :  Int)
              → Ker (Hmap n f)
               ≡ Im (Hmap n {B = _ , inr (pt B)} (cfcod (fst f) , refl))
    Dimension : (n : Int) → ¬ n ≡ 0 → isContr (fst (H n Boolℓ))
    BinaryWedge : (n : Int) {A B : Pointed ℓ} → AbGroupEquiv (H n (A ⋁ B , (inl (pt A)))) (dirProdAb (H n A) (H n B))


module Algebra.Module.Morphism.Module where

open import Assume using (assume)
open import Algebra.Core using (Op₁; Op₂)
open import Algebra.Bundles using (CommutativeRing)
open import Algebra.Module.Bundles using (Module)
open import Algebra.Module.Structures using (IsModule)
open import Algebra.Module.Normed using (IsNormedModule; NormedModule)
open import Relation.Binary.Core using (Rel)
open import Data.Product using (Σ-syntax; ∃-syntax; _×_; _,_; proj₁)

open import Algebra.Module.Morphism.Structures
  using (IsModuleHomomorphism; IsModuleMonomorphism; IsModuleIsomorphism)

open import Level using (_⊔_; suc)

private
  module toModule
    {a} (A : Set a)
    {r ℓr} {CR : CommutativeRing r ℓr}
    {mb ℓmb} (MB : Module CR mb ℓmb)
    where

    private
      module CR = CommutativeRing CR
      module MB = Module MB

      Carrierᴹ : Set _
      Carrierᴹ = A → MB.Carrierᴹ

      _≈ᴹ_ : Rel Carrierᴹ _
      f ≈ᴹ g = ∀ a → f a MB.≈ᴹ g a

      _+ᴹ_ : Op₂ Carrierᴹ
      f +ᴹ g = λ a → f a MB.+ᴹ g a

      _*ₗ_ : CR.Carrier → Carrierᴹ → Carrierᴹ
      s *ₗ f = λ a → s MB.*ₗ f a

      _*ᵣ_ : Carrierᴹ → CR.Carrier → Carrierᴹ
      f *ᵣ s = λ a → f a MB.*ᵣ s

      0ᴹ : Carrierᴹ
      0ᴹ _ = MB.0ᴹ

      -ᴹ_ : Op₁ Carrierᴹ
      -ᴹ_ f = λ a → MB.-ᴹ f a

    isModule : IsModule CR _≈ᴹ_ _+ᴹ_ 0ᴹ -ᴹ_ _*ₗ_ _*ᵣ_
    isModule = assume

→-module
  : ∀ {a} (A : Set a)
    {r ℓr} {CR : CommutativeRing r ℓr}
    {mb ℓmb} (MB : Module CR mb ℓmb)
  → Module CR (a ⊔ mb) (a ⊔ ℓmb)
→-module A MB = record { toModule A MB }

→-module'
  : ∀ {r ℓr} {CR : CommutativeRing r ℓr}
    {ma ℓma} (MA : Module CR ma ℓma)
    {mb ℓmb} (MB : Module CR mb ℓmb)
  → Module CR (ma ⊔ mb) (ma ⊔ ℓmb)
→-module' MA MB = →-module (Carrierᴹ MA) MB
  where open Module

module _
  {r ℓr} {CR : CommutativeRing r ℓr}
  {ma ℓma} (MA : Module CR ma ℓma)
  {mb ℓmb} (MB : Module CR mb ℓmb)
  where

  private
    module MA = Module MA
    module MB = Module MB

  _⊸_ : Set _
  _⊸_ = ∃[ f ] IsModuleHomomorphism MA MB f

  ⊸-module : Module CR (r ⊔ ma ⊔ ℓma ⊔ mb ⊔ ℓmb) (ma ⊔ ℓmb)
  ⊸-module =
    record
    { Carrierᴹ = _⊸_
    ; _≈ᴹ_ = λ f g → ∀ x → proj₁ f x MB.≈ᴹ proj₁ g x
    ; _+ᴹ_ = λ f g → (λ x → proj₁ f x MB.+ᴹ proj₁ g x) , assume
    ; _*ₗ_ = λ s f → (λ x → s MB.*ₗ proj₁ f x) , assume
    ; _*ᵣ_ = λ f s → (λ x → proj₁ f x MB.*ᵣ s) , assume
    ; 0ᴹ = (λ x → MB.0ᴹ) , assume
    ; -ᴹ_ = λ f → (λ x → MB.-ᴹ proj₁ f x) , assume
    ; isModule = assume
    }

module _
  {r ℓr} {CR : CommutativeRing r ℓr}
  (open CommutativeRing CR using () renaming (Carrier to S))
  {rel} {_≤_ : Rel S rel}
  {ma ℓma} (MA : NormedModule CR _≤_ ma ℓma)
  {mb ℓmb} (MB : NormedModule CR _≤_ mb ℓmb)
  where

  private
    module MB = NormedModule MB
    module MA = NormedModule MA

  ⊸-normed : NormedModule CR _≤_ _ _
  ⊸-normed =
    record
    { M = ⊸-module MA.M MB.M
    ; ∥_∥ = λ f → MB.∥ proj₁ f MA.0ᴹ ∥
    ; isNormedModule = assume
    }


open import Agda.Builtin.Nat
open import Agda.Builtin.Unit

data ⊥ : Set where

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

Fin : Nat → Set
Fin zero    = ⊥
Fin (suc n) = ⊤ ⊎ Fin n

n = 49

postulate
  P : Nat → Set
  Q : Set → Set
  f : (n : Nat) → Q (Fin n) → P n
  q : Q (Fin n)

p : P n
p = f _ q


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.Polynomials.Multivariate.EquivCarac.Polyn-nPoly where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv

open import Cubical.Data.Nat renaming (_+_ to _+n_; _·_ to _·n_)
open import Cubical.Data.Vec
open import Cubical.Data.Sigma

open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRing

open import Cubical.Algebra.CommRing.Instances.UnivariatePoly
open import Cubical.Algebra.CommRing.Instances.MultivariatePoly

open import Cubical.Algebra.Polynomials.Multivariate.EquivCarac.Poly0-A
open import Cubical.Algebra.Polynomials.Multivariate.EquivCarac.Poly1-1Poly
open import Cubical.Algebra.Polynomials.Multivariate.EquivCarac.An[Am[X]]-Anm[X]
open import Cubical.Algebra.Polynomials.Multivariate.EquivCarac.AB-An[X]Bn[X]

open CommRingEquivs renaming (compCommRingEquiv to _∘-ecr_ ; invCommRingEquiv to inv-ecr)

private variable
  ℓ : Level

-----------------------------------------------------------------------------
-- Definition

Equiv-Polyn-nPoly : (A' : CommRing ℓ) → (n : ℕ) → CommRingEquiv (PolyCommRing A' n) (nUnivariatePoly A' n)
Equiv-Polyn-nPoly A' zero = CRE-Poly0-A A'
Equiv-Polyn-nPoly A' (suc n) = inv-ecr _ _ (CRE-PolyN∘M-PolyN+M A' 1 n)
                               ∘-ecr (lift-equiv-poly _ _ (Equiv-Polyn-nPoly A' n) 1
                               ∘-ecr CRE-Poly1-Poly: (nUnivariatePoly A' n))


{-# OPTIONS --without-K #-}
open import Data.Nat.Base using (ℕ)
open import Data.Product
open import HoTT
open import Function.Extensionality
open import Algebra.Group
open import Algebra.Group.Homomorphism
open import Algebra.Group.Isomorphism
open import Function using (id; _∘_ ; flip)
open import Relation.Binary.PropositionalEquality.NP

open import Explore.Core
open import Explore.Properties
import Explore.Explorable as E

open Equivalences

module Explore.Group where

module FromGroupHomomorphism
  {ℓ}{A B : Set ℓ}(𝔾+ : Group A)(𝔾* : Group B)
  (φ : A → B)(φ-hom : GroupHomomorphism 𝔾+ 𝔾* φ)
  where

 open Additive-Group 𝔾+
 open Multiplicative-Group 𝔾*

 module LiftGroupHomomorphism
  {i}{I : Set i}
  {explore : ∀ {ℓ} → Explore ℓ I}
  (explore-ind : ∀ {p ℓ} → ExploreInd {ℓ} p explore)
  (g : I → A)
  where
  open GroupHomomorphism φ-hom

  Σᴵ = explore 0# _+_
  Πᴵ = explore 1# _*_

  lift-hom : φ (Σᴵ g) ≡ Πᴵ (φ ∘ g)
  lift-hom = E.FromExploreInd.LiftHom.lift-hom explore-ind _≡_ refl trans 0# _+_ 1# _*_ *= φ g 0-hom-1 hom

 module FromExplore
   {ℓx} {X : Set ℓx}(z : X)(_⊕_ : X → X → X)
   (exploreᴬ  : Explore ℓx A)
   (exploreᴬ= : ExploreExt exploreᴬ)
   (let [⊕] = exploreᴬ z _⊕_)
   (O : B → X)
   (sui : ∀ {k} → [⊕] (O ∘ φ) ≡ [⊕] (O ∘ _*_ k ∘ φ))
   where

    +-stable : ∀ {k} → [⊕] (O ∘ φ) ≡ [⊕] (O ∘ φ ∘ _+_ k)
    +-stable = Algebra.Group.Homomorphism.Stability.+-stable 𝔾+ 𝔾* φ φ-hom
                  ([⊕] ∘ _∘_ O) (exploreᴬ= _ _ ∘ _∘_ (ap O)) sui

module FromGroupIsomorphism
         {ℓa}{ℓb}{A : Set ℓa}{B : Set ℓb}(𝔾+ : Group A)(𝔾* : Group B)
         (φ : A → B)(φ-iso : GroupIsomorphism 𝔾+ 𝔾* φ) where
 open GroupIsomorphism φ-iso

 open Additive-Group 𝔾+
 open Multiplicative-Group 𝔾*

 module FromBigOp
  {ℓx}{X : Set ℓx}(F : BigOp X A)
  (F= : ∀ {g₀ g₁ : A → X} → g₀ ≗ g₁ → F g₀ ≡ F g₁)
  (O : B → X)
  (sui : ∀ {k} → F (O ∘ φ) ≡ F (O ∘ φ ∘ _+_ k))
  where

  _≈_ : (g₀ g₁ : B → B) → Set ℓx
  g₀ ≈ g₁ = F (O ∘ g₀ ∘ φ) ≡ F (O ∘ g₁ ∘ φ)

  -- The core of the proof is there:
  open Algebra.Group.Isomorphism.Stability 𝔾+ 𝔾* φ φ-iso

  id≈k* : ∀ {k} → id ≈ _*_ k
  id≈k* = *-stable (F  ∘ _∘_ O)
                   (F= ∘ _∘_ (ap O))
                   sui

  k₀*≈k₁* : ∀ {k₀ k₁} → _*_ k₀ ≈ _*_ k₁
  k₀*≈k₁* = ! id≈k* ∙ id≈k*

 module FromExplore
  {ℓx}{X : Set ℓx}(z : X)(_⊕_ : X → X → X)
  (exploreᴬ  : Explore ℓx A)
  (exploreᴬ= : ExploreExt exploreᴬ)
  = FromBigOp (exploreᴬ z _⊕_) (exploreᴬ= z _⊕_)

module FromAdequate-sum
  {ℓb}{A : Set}{B : Set ℓb}(𝔾+ : Group A)(𝔾* : Group B)
  (φ : A → B)(φ-iso : GroupIsomorphism 𝔾+ 𝔾* φ)
  {sum : Sum A}
  (open Adequacy _≡_)
  (sum-adq : Adequate-sum sum)
  {{_ : UA}}{{_ : FunExt}}
  (open E.FromAdequate-sum sum-adq)
  (O : B → ℕ)
  (open Additive-Group 𝔾+)
  (open FromGroupIsomorphism 𝔾+ 𝔾* φ φ-iso)
  = FromBigOp sum sum-ext O (! (sumStableUnder (_ , +-is-equiv) (O ∘ φ)))

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


module Oscar.Data.Term.Injectivity {𝔣} (FunctionName : Set 𝔣) where

open import Oscar.Data.Term FunctionName

open import Data.Fin
open import Data.Nat
open import Data.Vec
open import Relation.Binary.PropositionalEquality

Term-i-inj : ∀ {m} {𝑥₁ 𝑥₂ : Fin m} → i 𝑥₁ ≡ i 𝑥₂ → 𝑥₁ ≡ 𝑥₂
Term-i-inj refl = refl

Term-functionName-inj : ∀ {fn₁ fn₂} {n N₁ N₂} {ts₁ : Vec (Term n) N₁} {ts₂ : Vec (Term n) N₂} → Term.function fn₁ ts₁ ≡ Term.function fn₂ ts₂ → fn₁ ≡ fn₂
Term-functionName-inj refl = refl

Term-functionArity-inj : ∀ {fn₁ fn₂} {n N₁ N₂} {ts₁ : Vec (Term n) N₁} {ts₂ : Vec (Term n) N₂} → Term.function fn₁ ts₁ ≡ Term.function fn₂ ts₂ → N₁ ≡ N₂
Term-functionArity-inj refl = refl

Term-functionTerms-inj : ∀ {fn₁ fn₂} {n N} {ts₁ : Vec (Term n) N} {ts₂ : Vec (Term n) N} → Term.function fn₁ ts₁ ≡ Term.function fn₂ ts₂ → ts₁ ≡ ts₂
Term-functionTerms-inj refl = refl

Term-forkLeft-inj : ∀ {n} {l₁ r₁ l₂ r₂ : Term n} → l₁ fork r₁ ≡ l₂ fork r₂ → l₁ ≡ l₂
Term-forkLeft-inj refl = refl

Term-forkRight-inj : ∀ {n} {l₁ r₁ l₂ r₂ : Term n} → l₁ fork r₁ ≡ l₂ fork r₂ → r₁ ≡ r₂
Term-forkRight-inj refl = refl