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

module Bundles where

open import Algebra.Core
open import Relation.Binary
open import Level
open import Algebra.Bundles
open import Algebra.Structures
open import Structures

------------------------------------------------------------------------
-- Bundles with 1 binary operation
------------------------------------------------------------------------

record IdempotentMagma c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    isIdempotentMagma  : IsIdempotentMagma _≈_ _∙_

  open IsIdempotentMagma isIdempotentMagma public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (rawMagma)

record AlternateMagma c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    isAlternateMagma  : IsAlternateMagma _≈_ _∙_

  open IsAlternateMagma isAlternateMagma public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (rawMagma)

record FlexibleMagma c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    isFlexibleMagma  : IsFlexibleMagma _≈_ _∙_

  open IsFlexibleMagma isFlexibleMagma public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (rawMagma)

record MedialMagma c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    isMedialMagma  : IsMedialMagma _≈_ _∙_

  open IsMedialMagma isMedialMagma public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (rawMagma)

record SemimedialMagma c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    isSemimedialMagma  : IsSemimedialMagma _≈_ _∙_

  open IsSemimedialMagma isSemimedialMagma public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (rawMagma)

record LatinQuasigroup c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    isLatinQuasigroup : IsLatinQuasigroup _≈_ _∙_

  open IsLatinQuasigroup isLatinQuasigroup public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (_≉_; rawMagma)

record InverseSemigroup c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier            : Set c
    _≈_                : Rel Carrier ℓ
    _∙_                : Op₂ Carrier
    isInverseSemigroup : IsInverseSemigroup _≈_ _∙_

  open IsInverseSemigroup isInverseSemigroup public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (_≉_; rawMagma)

------------------------------------------------------------------------
-- Bundles with 1 binary operation and 1 element
------------------------------------------------------------------------

record LeftUnitalMagma c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier  : Set c
    _≈_      : Rel Carrier ℓ
    _∙_      : Op₂ Carrier
    ε        : Carrier
    isLeftUnitalMagma  : IsLeftUnitalMagma _≈_ _∙_ ε

  open IsLeftUnitalMagma isLeftUnitalMagma public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (rawMagma)

record RightUnitalMagma c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier  : Set c
    _≈_      : Rel Carrier ℓ
    _∙_      : Op₂ Carrier
    ε        : Carrier
    isRightUnitalMagma  : IsRightUnitalMagma _≈_ _∙_ ε

  open IsRightUnitalMagma isRightUnitalMagma public

  magma : Magma c ℓ
  magma = record { isMagma = isMagma }

  open Magma magma public
    using (rawMagma)

------------------------------------------------------------------------
-- Bundles with 2 binary operations, 1 unary operation & 1 element
------------------------------------------------------------------------

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

  open IsNonAssociativeRing isNonAssociativeRing public

  +-abelianGroup : AbelianGroup _ _
  +-abelianGroup = record { isAbelianGroup = +-isAbelianGroup }

  open AbelianGroup +-abelianGroup public
    using () renaming (group to +-group; invertibleMagma to +-invertibleMagma; invertibleUnitalMagma to +-invertibleUnitalMagma)

------------------------------------------------------------------------
-- Bundles with 3 binary operation and 1 element
------------------------------------------------------------------------

record Pique c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infixl 7 _\\_
  infixl 7 _//_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    _\\_    : Op₂ Carrier
    _//_    : Op₂ Carrier
    ε       : Carrier
    isPique : IsPique _≈_ _∙_ _\\_ _//_ ε

  open IsPique isPique public

record LeftBolLoop c ℓ : Set (suc (c ⊔ ℓ)) where
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    _\\_    : Op₂ Carrier
    _//_    : Op₂ Carrier
    ε       : Carrier
    isLeftBolLoop : IsLeftBolLoop  _≈_ _∙_ _\\_ _//_ ε

  open IsLeftBolLoop isLeftBolLoop public

  loop : Loop _ _
  loop = record { isLoop = isLoop }

  open Loop loop public
    using (quasigroup)

record RightBolLoop c ℓ : Set (suc (c ⊔ ℓ)) where
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    _\\_    : Op₂ Carrier
    _//_    : Op₂ Carrier
    ε       : Carrier
    isRightBolLoop : IsRightBolLoop  _≈_ _∙_ _\\_ _//_ ε

  open IsRightBolLoop isRightBolLoop public

  loop : Loop _ _
  loop = record { isLoop = isLoop }

  open Loop loop public
    using (quasigroup)

record MoufangLoop c ℓ : Set (suc (c ⊔ ℓ)) where
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _∙_     : Op₂ Carrier
    _\\_    : Op₂ Carrier
    _//_    : Op₂ Carrier
    ε       : Carrier
    isMoufangLoop : IsMoufangLoop  _≈_ _∙_ _\\_ _//_ ε

  open IsMoufangLoop isMoufangLoop public

  loop : Loop _ _
  loop = record { isLoop = isLoop }

  open Loop loop public
    using (quasigroup)


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

open import Base

module Spaces.WedgeCircles {i} (A : Set i) where

{-
The idea is

data wedge-circles : Set (suc i) where
  base : wedge-circles
  loops : A → base ≡ base
-}

private
  data #wedge-circles : Set (suc i) where
    #base : #wedge-circles

wedge-circles : Set (suc i)
wedge-circles = #wedge-circles

base : wedge-circles
base = #base

postulate  -- HIT
  loops : A → base ≡ base

wedge-circles-rec : ∀ {i} (P : wedge-circles → Set i) (x : P base)
  (p : (t : A) → transport P (loops t) x ≡ x) → ((t : wedge-circles) → P t)
wedge-circles-rec P x p #base = x

postulate  -- HIT
  β : ∀ {i} (P : wedge-circles → Set i) (x : P base)
      (p : (t : A) → transport P (loops t) x ≡ x) (t : A)
      → apd (wedge-circles-rec P x p) (loops t) ≡ p t

wedge-circles-rec-nondep : ∀ {i} (B : Set i) (x : B) (p : A → x ≡ x)
  → (wedge-circles → B)
wedge-circles-rec-nondep B x p #base = x

postulate  -- HIT
  β-nondep : ∀ {i} (B : Set i) (x : B) (p : A → x ≡ x) (t : A)
      → ap (wedge-circles-rec-nondep B x p) (loops t) ≡ p t


module Section9 where

open import Section8 public

-- 9. A decision algorithm for terms
-- =================================
--
-- The reduction defined above can be used for deciding if two well-typed terms are convertible
-- with each other or not: reduce the terms and check if the results are equal.  This algorithm
-- is implicit in Martin-Löf’s notes [13].
--
-- The decision algorithm is complete:

-- Theorem 11.
postulate
  thm₁₁ : ∀ {Γ A t₀ t₁} →
            Γ ⊢ t₀ ≊ t₁ ∷ A →
            Σ 𝕋 (λ t → Γ ⊢ t₀ ⇓ t ∷ A × Γ ⊢ t₁ ⇓ t ∷ A)

-- Proof:  By Lemma 14 and Lemma 12 we know that there exist proof trees `M, N` such
-- that `t₀` is equal to `M ⁻` and `t₁` is equal to `N ⁻`.  By Theorem 9 we get that `M ≅ N`.  We can
-- choose `nf M ⁻` for `t` since, by Lemma 8, `Γ ⊢ M ⁻ ⇓ nf M ⁻ ∷ A`, `Γ ⊢ N ⁻ ⇓ nf N ⁻ ∷ A`
-- and by Theorem 6 we have `nf M ≡ nf N` and hence that `nf M ⁻` and `nf N ⁻` are the
-- same.
--
-- This decision algorithm is correct, i.e.,

-- Theorem 12.
postulate
  thm₁₂ : ∀ {Γ A t} →
            (M N : Γ ⊢ A) → Γ ⊢ M ⁻ ⇓ t ∷ A → Γ ⊢ N ⁻ ⇓ t ∷ A →
            M ≅ N

-- Proof:  We have `nf M ⁻ ≡ nf N ⁻` since, by Lemma 8, `Γ ⊢ M ⁻ ⇓ nf M ⁻ ∷ A`
-- and `Γ ⊢ N ⁻ ⇓ nf N ⁻ ∷ A` and the reduction is deterministic.  By Corollary 2 we get
-- `M ≅ N`.


module Data.List.All.Properties.Extra {a}{A : Set a} where

open import Relation.Binary.PropositionalEquality
open import Data.Nat
open import Data.Fin
open import Data.List as List hiding (reverse)
open import Data.List.Relation.Unary.Any hiding (tail)
open import Data.List.Membership.Propositional
open import Data.List.Properties.Extra
open import Data.List.At
open import Data.List.Relation.Unary.All as All hiding (_[_]=_)
open import Data.List.Relation.Unary.All.Properties
open import Data.Product
open import Function

map-map : ∀ {p q r}{P : A → Set p}{Q : A → Set q}{R : A → Set r}{l : List A}(ps : All P l)
          {f : ∀ {x : A} → P x → Q x}{g : ∀ {x : A} → Q x → R x} →
          All.map g (All.map f ps) ≡ All.map (g ∘ f) ps
map-map [] = refl
map-map (px ∷ ps) = cong₂ _∷_ refl (map-map ps)

drop-all : ∀ {p}{P : A → Set p}{l : List A} n → All P l → All P (drop n l)
drop-all zero px = px
drop-all (suc n) [] = []
drop-all (suc n) (px ∷ pxs) = drop-all n pxs

split-++ : ∀ {p}{P : A → Set p}(l m : List A) → All P (l ++ m) → All P l × All P m
split-++ [] m pxs = [] , pxs
split-++ (x ∷ l) m (px ∷ pxs) with split-++ l m pxs
... | lp , rp = px ∷ lp , rp

_++-all_ : ∀ {l m p}{P : A → Set p} → All P l → All P m → All P (l ++ m)
[] ++-all pm = pm
(px ∷ pl) ++-all pm = px ∷ (pl ++-all pm)

∈-all : ∀ {p}{P : A → Set p}{l : List A}{x} → x ∈ l → All P l → P x
∈-all (here refl) (px ∷ q) = px
∈-all (there p) (px ∷ q) = ∈-all p q

-- proof matters; update a particular witness of a property
_All[_]≔_ : ∀ {p}{P : A → Set p} {xs : List A} {i x} →
            All P xs → xs [ i ]= x → P x → All P xs
_All[_]≔_ [] ()
_All[_]≔_ {xs = .(_ ∷ _)} {zero} (px ∷ xs) refl px' = px' ∷ xs
_All[_]≔_ {xs = .(_ ∷ _)} {suc i} (px ∷ xs) q px' = px ∷ (xs All[ q ]≔ px')

-- strong update
-- _All[_]≔!_ : ∀ {p}{P : A → Set p} {xs : List A} {x} →
--             All P xs → (i : Fin (length xs)) → P x → All P (xs [ i ]≔ x)
-- [] All[ () ]≔! y
-- (px ∷ xs) All[ zero ]≔! y = y ∷ xs
-- (px ∷ xs) All[ suc i ]≔! y = px ∷ xs All[ i ]≔! y

_all-∷ʳ_ : ∀ {p}{l : List A} {x} {P : A → Set p} → All P l → P x → All P (l ∷ʳ x)
_all-∷ʳ_ [] q = q ∷ []
_all-∷ʳ_ (px ∷ p) q = px ∷ (p all-∷ʳ q)

-- All≔'∘map : ∀ {p q}{P : A → Set p}{Q : A → Set q}{xs : List A} {x}(pxs : All P xs)(e : x ∈ xs)(px : P x)(f : ∀ {x} → P x → Q x) →
--             (All.map f pxs) All[ e ]≔' (f px) ≡ All.map f (pxs All[ e ]≔' px)
-- All≔'∘map [] ()
-- All≔'∘map (px ∷ pxs) (here refl) qx f = refl
-- All≔'∘map (px ∷ pxs) (there e) qx f = cong₂ _∷_ refl (All≔'∘map pxs e qx f)

all-∷ʳ∘map : ∀ {p q}{P : A → Set p}{Q : A → Set q}{xs : List A} {x}(pxs : All P xs)(px : P x)(f : ∀ {x} → P x → Q x) →
             (All.map f pxs) all-∷ʳ (f px) ≡ All.map f (pxs all-∷ʳ px)
all-∷ʳ∘map [] px f = refl
all-∷ʳ∘map (px₁ ∷ pxs) px f = cong₂ _∷_ refl (all-∷ʳ∘map pxs px f)

lookup∘map : ∀ {p q}{P : A → Set p}{Q : A → Set q}{xs : List A} {x}(pxs : All P xs)(e : x ∈ xs)(f : ∀ {x} → P x → Q x) →
             All.lookup (All.map f pxs) e ≡ f (All.lookup pxs e)
lookup∘map [] ()
lookup∘map (px ∷ pxs) (here refl) f = refl
lookup∘map (px ∷ pxs) (there e) f = lookup∘map pxs e f

erase : ∀ {p b}{P : A → Set p}{l : List A}{B : Set b} → (∀ {x} → P x → B) → All P l → List B
erase f [] = []
erase f (px ∷ xs₁) = f px ∷ erase f xs₁

-- pop₁ : ∀ {x : A}{p xs}{P : A → Set p} → All P (x ∷ xs) → P x × All P xs
-- pop₁ st = head st , tail st

-- open import Data.List.Properties
-- popₙ : ∀ (as : List A){p xs}{P : A → Set p} → All P (as ++ xs) → All P (List.reverse as) × All P xs
-- popₙ [] st = [] , st
-- popₙ (x ∷ xs) (px ∷ pxs) rewrite unfold-reverse x xs =
--   let pys , pzs = popₙ xs pxs in (pys all-∷ʳ px) , pzs

-- open import Relation.Binary.Core
-- open import Data.List.Relation.Pointwise hiding (Rel)
-- module _ {a ℓ}{A : Set a}(_∼_ : Rel A ℓ) where

--   pw-map : ∀ {l m p}{P : A → Set p} → (∀ {a b} → a ∼ b → P a → P b) → Pointwise _∼_ l m → All P l → All P m
--   pw-map f [] [] = []
--   pw-map f (x∼y ∷ r) (px ∷ xs) = f x∼y px ∷ pw-map f r xs


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

open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; sym; trans; cong; cong₂; isEquivalence; setoid; module ≡-Reasoning)
open import Relation.Binary.PropositionalEquality.WithK using (≡-irrelevant)
open import Relation.Nullary.Negation using (contradiction)
open ≡-Reasoning

open import Data.Unit using (⊤; tt)
open import Agda.Builtin.FromNat using (Number)

open import Data.Product using (_,_)

open import AKS.Primality using (IsPrime; Prime; bézout-prime)

module AKS.Modular.Quotient.Properties {P : Prime} where

open Prime P using () renaming (prime to p; isPrime to p-isPrime)
open IsPrime p-isPrime using (1<p)

open import AKS.Nat using (ℕ; zero; suc; _<_; _≤_; _∸_) renaming (_+_ to _+ℕ_; _*_ to _*ℕ_; _≟_ to _≟ℕ_)
open import AKS.Nat using (<⇒≤; [n∸m]+m≡n; suc-mono-≤; *-mono-≤; ≤-refl)
import AKS.Nat as Nat
import Data.Nat.Properties as Nat
open import AKS.Nat.Divisibility using (_%_; %-distribˡ-+; %-distribˡ-∸; %-distribˡ-*; m%n%n≡m%n; n<m⇒n%m≡n; n%m<m; n%n≡0; 0%m≡0; 1%m≡1; [m+kn]%n≡m%n)
open import AKS.Nat.GCD using (+ʳ; +ˡ)
open import AKS.Modular.Quotient.Base using (ℤ/[_]; ℤ✓; mod; mod<p; module Operations)
open Operations {P} using ([_]; _+_; _*_; -_; _⁻¹; _/_; _≟_; p≢0; ℤ/[]-irrelevance; n≢0⇒mod[n]≢0)

open import Algebra.Structures {A = ℤ/[ P ]} _≡_ using
  ( IsCommutativeRing; IsRing; IsAbelianGroup
  ; IsGroup; IsMonoid; IsSemigroup; IsMagma
  )
open import Algebra.Definitions {A = ℤ/[ P ]} _≡_ using
  ( _DistributesOver_; _DistributesOverʳ_; _DistributesOverˡ_
  ; RightIdentity; LeftIdentity; Identity; Associative; Commutative
  ; RightInverse; LeftInverse; Inverse
  )
open import AKS.Algebra.Structures (ℤ/[ P ]) _≡_ using (IsNonZeroCommutativeRing; IsIntegralDomain; IsGCDDomain; IsDecField)
open import Algebra.Bundles using (Ring; CommutativeRing)
open import AKS.Algebra.Bundles using (NonZeroCommutativeRing; DecField)

open import AKS.Unsafe using (≢-irrelevant)

+-isMagma : IsMagma _+_
+-isMagma = record
  { isEquivalence = isEquivalence
  ; ∙-cong = cong₂ _+_
  }

mod≡mod%p : ∀ (x : ℤ/[ P ]) → mod x ≡ (mod x % p) {p≢0}
mod≡mod%p (ℤ✓ x x<p) = sym (n<m⇒n%m≡n x<p)

mod≡mod%p%p : ∀ (x : ℤ/[ P ]) → mod x ≡ ((mod x % p) {p≢0} % p) {p≢0}
mod≡mod%p%p x = begin
  mod x         ≡⟨ mod≡mod%p x ⟩
  mod x % p     ≡⟨ sym (m%n%n≡m%n (mod x) p) ⟩
  mod x % p % p ∎

+-assoc : Associative _+_
+-assoc x y z = ℤ/[]-irrelevance $ begin
  ((mod x +ℕ mod y) % p +ℕ mod z) % p             ≡⟨ cong₂ (λ a b → (a +ℕ b) % p) (%-distribˡ-+ (mod x) (mod y) p {p≢0}) (mod≡mod%p z) ⟩
  ((mod x % p +ℕ mod y % p) % p +ℕ mod z % p) % p ≡⟨ sym (%-distribˡ-+ (mod x % p +ℕ mod y % p) (mod z) p) ⟩
  ((mod x % p +ℕ mod y % p) +ℕ mod z) % p         ≡⟨ cong (λ a → (a % p) {p≢0}) (Nat.+-assoc (mod x % p) (mod y % p) (mod z)) ⟩
  (mod x % p +ℕ (mod y % p +ℕ mod z)) % p         ≡⟨ %-distribˡ-+ (mod x % p) (mod y % p +ℕ mod z) p ⟩
  (mod x % p % p +ℕ (mod y % p +ℕ mod z) % p) % p ≡⟨ cong₂ (λ a b → (a +ℕ (b +ℕ mod z) % p) % p) (sym (mod≡mod%p%p x)) (sym (mod≡mod%p y)) ⟩
  (mod x +ℕ (mod y +ℕ mod z) % p) % p ∎

+-isSemigroup : IsSemigroup _+_
+-isSemigroup = record
  { isMagma = +-isMagma
  ; assoc = +-assoc
  }

+-comm : Commutative _+_
+-comm x y = ℤ/[]-irrelevance $ begin
  (mod x +ℕ mod y) % p ≡⟨ cong (λ a → (a % p) {p≢0}) (Nat.+-comm (mod x) (mod y)) ⟩
  (mod y +ℕ mod x) % p ∎

+-identityˡ : LeftIdentity 0 _+_
+-identityˡ x = ℤ/[]-irrelevance $ begin
  (0 % p +ℕ mod x) % p     ≡⟨ cong (λ a → (0 % p +ℕ a) % p) (mod≡mod%p x) ⟩
  (0 % p +ℕ mod x % p) % p ≡⟨ sym (%-distribˡ-+ 0 (mod x) p) ⟩
  (0 +ℕ mod x) % p         ≡⟨ sym (mod≡mod%p x) ⟩
  mod x                    ∎

open import Algebra.FunctionProperties.Consequences.Propositional using (comm+idˡ⇒idʳ; comm+invˡ⇒invʳ; comm+distrˡ⇒distrʳ)

+-identityʳ : RightIdentity 0 _+_
+-identityʳ = comm+idˡ⇒idʳ +-comm {0} +-identityˡ

+-identity : Identity 0 _+_
+-identity = +-identityˡ , +-identityʳ

+-isMonoid : IsMonoid _+_ 0
+-isMonoid = record
  { isSemigroup = +-isSemigroup
  ; identity = +-identity
  }

-‿inverseˡ : LeftInverse 0 -_ _+_
-‿inverseˡ (ℤ✓ zero x<p) = refl
-‿inverseˡ (ℤ✓ (suc x) x<p) = ℤ/[]-irrelevance $ begin
  ((p ∸ suc x) +ℕ suc x) % p ≡⟨ cong (λ a → (a % p) {p≢0}) ([n∸m]+m≡n (<⇒≤ x<p)) ⟩
  p % p ≡⟨ n%n≡0 p ⟩
  0     ≡⟨ sym (0%m≡0 p {p≢0}) ⟩
  0 % p ∎

-‿inverseʳ : RightInverse 0 -_ _+_
-‿inverseʳ = comm+invˡ⇒invʳ {_⁻¹ = -_} +-comm -‿inverseˡ

-‿inverse : Inverse 0 -_ _+_
-‿inverse = -‿inverseˡ , -‿inverseʳ

+-isGroup : IsGroup _+_ 0 -_
+-isGroup = record
  { isMonoid = +-isMonoid
  ; inverse = -‿inverse
  ; ⁻¹-cong = cong -_
  }

+-isAbelianGroup : IsAbelianGroup _+_ 0 -_
+-isAbelianGroup = record
  { isGroup = +-isGroup
  ; comm = +-comm
  }

*-isMagma : IsMagma _*_
*-isMagma = record
  { isEquivalence = isEquivalence
  ; ∙-cong = cong₂ _*_
  }

*-assoc : Associative _*_
*-assoc x y z = ℤ/[]-irrelevance $ begin
  ((mod x *ℕ mod y) % p *ℕ mod z) % p                   ≡⟨ cong₂ (λ a b → (a *ℕ b) % p) (%-distribˡ-* (mod x) (mod y) p {p≢0}) (mod≡mod%p z) ⟩
  (((mod x % p) *ℕ (mod y % p)) % p *ℕ (mod z % p)) % p ≡⟨ sym (%-distribˡ-* ((mod x % p) *ℕ (mod y % p)) (mod z) p) ⟩
  (((mod x % p) *ℕ (mod y % p)) *ℕ mod z) % p           ≡⟨ cong (λ a → (a % p) {p≢0}) (Nat.*-assoc (mod x % p) (mod y % p) (mod z)) ⟩
  ((mod x % p) *ℕ ((mod y % p) *ℕ mod z)) % p           ≡⟨ %-distribˡ-* (mod x % p) ((mod y % p) *ℕ mod z) p ⟩
  ((mod x % p % p) *ℕ (((mod y % p) *ℕ mod z) % p)) % p ≡⟨ cong₂ (λ a b → (a *ℕ ((b *ℕ mod z) % p)) % p) (sym (mod≡mod%p%p x)) (sym (mod≡mod%p y)) ⟩
  (mod x *ℕ ((mod y *ℕ mod z) % p)) % p ∎

*-isSemigroup : IsSemigroup _*_
*-isSemigroup = record
  { isMagma = *-isMagma
  ; assoc = *-assoc
  }

*-comm : Commutative _*_
*-comm x y = ℤ/[]-irrelevance $ begin
  (mod x *ℕ mod y) % p ≡⟨ cong (λ a → (a % p) {p≢0}) (Nat.*-comm (mod x) (mod y)) ⟩
  (mod y *ℕ mod x) % p ∎

*-identityˡ : LeftIdentity 1 _*_
*-identityˡ x = ℤ/[]-irrelevance $ begin
  (1 % p *ℕ mod x) % p ≡⟨ cong (λ a → ((a *ℕ mod x) % p) {p≢0}) (1%m≡1 1<p) ⟩
  (1 *ℕ mod x) % p     ≡⟨ cong (λ a → a % p) (Nat.*-identityˡ (mod x)) ⟩
  mod x % p            ≡⟨ sym (mod≡mod%p x) ⟩
  mod x                ∎

*-identityʳ : RightIdentity 1 _*_
*-identityʳ = comm+idˡ⇒idʳ *-comm {1} *-identityˡ

*-identity : Identity 1 _*_
*-identity = *-identityˡ , *-identityʳ

*-isMonoid : IsMonoid _*_ 1
*-isMonoid = record
  { isSemigroup = *-isSemigroup
  ; identity = *-identity
  }

*-distribˡ-+ : _*_ DistributesOverˡ _+_
*-distribˡ-+ r x y = ℤ/[]-irrelevance $ begin
  (mod r *ℕ ((mod x +ℕ mod y) % p)) % p              ≡⟨ cong (λ a → (a *ℕ ((mod x +ℕ mod y) % p)) % p) (mod≡mod%p r) ⟩
  (mod r % p *ℕ ((mod x +ℕ mod y) % p)) % p          ≡⟨ sym (%-distribˡ-* (mod r) (mod x +ℕ mod y) p) ⟩
  (mod r *ℕ (mod x +ℕ mod y)) % p                    ≡⟨ cong (λ a → (a % p) {p≢0}) (Nat.*-distribˡ-+ (mod r) (mod x) (mod y)) ⟩
  (mod r *ℕ mod x +ℕ mod r *ℕ mod y) % p             ≡⟨ %-distribˡ-+ (mod r *ℕ mod x) (mod r *ℕ mod y) p ⟩
  ((mod r *ℕ mod x) % p +ℕ (mod r *ℕ mod y) % p) % p ∎

*-distribʳ-+ : _*_ DistributesOverʳ _+_
*-distribʳ-+ = comm+distrˡ⇒distrʳ {_} {_} {_*_} {_+_} *-comm *-distribˡ-+

*-distrib-+ : _*_ DistributesOver _+_
*-distrib-+ = *-distribˡ-+ , *-distribʳ-+

+-*-isRing : IsRing _+_ _*_ -_ 0 1
+-*-isRing = record
  { +-isAbelianGroup = +-isAbelianGroup
  ; *-isMonoid = *-isMonoid
  ; distrib = *-distrib-+
  }

+-*-isCommutativeRing : IsCommutativeRing _+_ _*_ -_ 0 1
+-*-isCommutativeRing = record
  { isRing = +-*-isRing
  ; *-comm = *-comm
  }

open import Data.Empty using (⊥)

0≢1 : [ 0 ] ≢ [ 1 ]
0≢1 [0]≡[1] = false
  where
  0≡1 : zero ≡ 1
  0≡1 = begin
    0     ≡⟨ sym (0%m≡0 p {p≢0}) ⟩
    0 % p ≡⟨ cong mod [0]≡[1] ⟩
    1 % p ≡⟨ 1%m≡1 1<p ⟩
    1     ∎
  false : ⊥
  false with 0≡1
  ... | ()

+-*-isNonZeroCommutativeRing : IsNonZeroCommutativeRing _+_ _*_ -_ 0 1
+-*-isNonZeroCommutativeRing = record
  { isCommutativeRing = +-*-isCommutativeRing
  ; 0#≉1# = 0≢1
  }

+-*-nonZeroCommutativeRing : NonZeroCommutativeRing 0ℓ 0ℓ
+-*-nonZeroCommutativeRing = record { isNonZeroCommutativeRing = +-*-isNonZeroCommutativeRing }

⁻¹-inverse-lemma₁ : ∀ (n : ℤ/[ P ]) x y → 1 +ℕ x *ℕ mod n ≡ y *ℕ p → ((mod n *ℕ (p ∸ (x % p) {p≢0})) % p) {p≢0} ≡ (1 % p) {p≢0}
⁻¹-inverse-lemma₁ n x y 1+x*n≡y*p = begin
  (mod n *ℕ (p ∸ x % p)) % p                             ≡⟨ cong (λ t → (t % p) {p≢0}) (Nat.*-distribˡ-∸ (mod n) p (x % p)) ⟩
  ((1 +ℕ mod n *ℕ p) ∸ (1 +ℕ mod n *ℕ (x % p))) % p     ≡⟨ %-distribˡ-∸ (1 +ℕ mod n *ℕ p) (1 +ℕ mod n *ℕ (x % p)) p 1+n*x%p≤1+n*p ⟩
  ((1 +ℕ mod n *ℕ p) ∸ (1 +ℕ mod n *ℕ (x % p)) % p) % p ≡⟨ cong (λ t → ((1 +ℕ mod n *ℕ p) ∸ t) % p) lemma ⟩
  ((1 +ℕ mod n *ℕ p) ∸ 0) % p                           ≡⟨ [m+kn]%n≡m%n 1 (mod n) p ⟩
  1 % p                                                  ∎
  where
  lemma : ((1 +ℕ mod n *ℕ (x % p) {p≢0}) % p) {p≢0} ≡ 0
  lemma = begin
    (1 +ℕ mod n *ℕ (x % p)) % p                 ≡⟨ cong (λ t → ((1 +ℕ t *ℕ (x % p) {p≢0}) % p) {p≢0}) (mod≡mod%p n) ⟩
    (1 +ℕ (mod n % p) *ℕ (x % p)) % p           ≡⟨ %-distribˡ-+ 1 ((mod n % p) *ℕ (x % p)) p ⟩
    (1 % p +ℕ ((mod n % p) *ℕ (x % p)) % p) % p ≡⟨ cong (λ t → (1 % p +ℕ t) % p) (sym (%-distribˡ-* (mod n) x p)) ⟩
    (1 % p +ℕ (mod n *ℕ x) % p) % p             ≡⟨ sym (%-distribˡ-+ 1 (mod n *ℕ x) p) ⟩
    (1 +ℕ mod n *ℕ x) % p                       ≡⟨ cong (λ t → ((1 +ℕ t) % p) {p≢0}) (Nat.*-comm (mod n) x) ⟩
    (1 +ℕ x *ℕ mod n) % p                       ≡⟨ cong (λ t → (t % p) {p≢0}) 1+x*n≡y*p ⟩
    (y *ℕ p) % p                                ≡⟨ [m+kn]%n≡m%n 0 y p ⟩
    0 % p                                       ≡⟨ 0%m≡0 p ⟩
    0 ∎
  1+n*x%p≤1+n*p : 1 +ℕ mod n *ℕ (x % p) {p≢0} ≤ 1 +ℕ mod n *ℕ p
  1+n*x%p≤1+n*p = suc-mono-≤ (*-mono-≤ {mod n} ≤-refl (<⇒≤ (n%m<m x p)))

⁻¹-inverse-lemma₂ : ∀ (n : ℤ/[ P ]) x y → 1 +ℕ y *ℕ p ≡ x *ℕ mod n → ((mod n *ℕ (x % p) {p≢0}) % p) {p≢0} ≡ (1 % p) {p≢0}
⁻¹-inverse-lemma₂ n x y 1+y*p≡x*n = begin
  (mod n *ℕ (x % p)) % p       ≡⟨ cong (λ t → (t *ℕ (x % p)) % p) (mod≡mod%p n) ⟩
  ((mod n % p) *ℕ (x % p)) % p ≡⟨ sym (%-distribˡ-* (mod n) x p) ⟩
  (mod n *ℕ x) % p             ≡⟨ cong (λ t → (t % p) {p≢0}) (Nat.*-comm (mod n) x) ⟩
  (x *ℕ mod n) % p             ≡⟨ cong (λ t → (t % p) {p≢0}) (sym 1+y*p≡x*n) ⟩
  (1 +ℕ y *ℕ p) % p            ≡⟨ [m+kn]%n≡m%n 1 y p ⟩
  1 % p ∎

⁻¹-inverse : ∀ n {n≢0} → n * (n ⁻¹) {n≢0} ≡ 1
⁻¹-inverse n {n≢0} with bézout-prime (mod n) p (n≢0⇒mod[n]≢0 n≢0) (mod<p n) p-isPrime
... | +ʳ x y 1+x*n≡y*p = ℤ/[]-irrelevance (⁻¹-inverse-lemma₁ n x y 1+x*n≡y*p)
... | +ˡ x y 1+y*p≡x*n = ℤ/[]-irrelevance (⁻¹-inverse-lemma₂ n x y 1+y*p≡x*n)

/-inverse : ∀ x y {y≢0} → x ≡ y * (x / y) {y≢0}
/-inverse x y {y≢0} = begin
  x                      ≡⟨ sym (*-identityʳ x) ⟩
  x * 1                  ≡⟨ cong (λ a → x * a) (sym (⁻¹-inverse y {y≢0})) ⟩
  x * (y * (y ⁻¹) {y≢0}) ≡⟨ sym (*-assoc x y ((y ⁻¹) {y≢0})) ⟩
  (x * y) * (y ⁻¹) {y≢0} ≡⟨ cong (λ a → a * (y ⁻¹) {y≢0}) (*-comm x y) ⟩
  (y * x) * (y ⁻¹) {y≢0} ≡⟨ *-assoc y x ((y ⁻¹) {y≢0}) ⟩
  y * (x / y) {y≢0} ∎

open import AKS.Algebra.Consequences +-*-nonZeroCommutativeRing using (module Inverse⇒Field)

open Inverse⇒Field _≟_ ≡-irrelevant ≢-irrelevant _/_ /-inverse
  using (gcd)
  renaming (isField to +-*-/-isField; [field] to +-*-/-field) public

+-*-/-isDecField : IsDecField _≟_ _+_ _*_ -_ 0 1 _/_ gcd
+-*-/-isDecField = record { isField = +-*-/-isField }

+-*-/-decField : DecField 0ℓ 0ℓ
+-*-/-decField = record { isDecField = +-*-/-isDecField }


------------------------------------------------------------------------
-- The Agda standard library
--
-- Comonads
------------------------------------------------------------------------

-- Note that currently the monad laws are not included here.

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

module Category.Comonad where

open import Level
open import Function

private
  variable
    a b c f : Level
    A : Set a
    B : Set b
    C : Set c

record RawComonad (W : Set f → Set f) : Set (suc f) where

  infixl 1 _=>>_ _=>=_
  infixr 1 _<<=_ _=<=_

  field
    extract : W A → A
    extend  : (W A → B) → (W A → W B)

  duplicate : W A → W (W A)
  duplicate = extend id

  liftW : (A → B) → W A → W B
  liftW f = extend (f ∘′ extract)

  _=>>_ : W A → (W A → B) → W B
  _=>>_ = flip extend

  _=>=_ : (W A → B) → (W B → C) → W A → C
  f =>= g = g ∘′ extend f

  _<<=_ : (W A → B) → W A → W B
  _<<=_ = extend

  _=<=_ : (W B → C) → (W A → B) → W A → C
  _=<=_ = flip _=>=_


{-# OPTIONS --without-K #-}
open import HoTT.Base
open import HoTT.Equivalence
open import HoTT.Equivalence.Lift
open import HoTT.Identity.Sigma
open import HoTT.Identity.Universe
open import HoTT.HLevel
open import HoTT.Logic

module HoTT.Exercises.Chapter3.Exercise10 {i} where

postulate
  lem : LEM {lsuc i}

_ : Prop𝒰 i ≃ Prop𝒰 (lsuc i)
_ = f , qinv→isequiv (g , η , ε)
  where
  f : _
  f P = LiftProp P
  g : _
  g P with lem P
  ... | inl _ = ⊤
  ... | inr _ = ⊥
  η : g ∘ f ~ id
  η P with lem (f P)
  ... | inl t = hlevel⁼ (ua (prop-equiv (const (lower t)) (const ★)))
  ... | inr f = hlevel⁼ (ua (prop-equiv 𝟎-rec (𝟎-rec ∘ f ∘ lift)))
  ε : f ∘ g ~ id
  ε P with lem P
  ... | inl t = hlevel⁼ (ua (Lift-equiv ∙ₑ prop-equiv (const t) (const ★)))
  ... | inr f = hlevel⁼ (ua (Lift-equiv ∙ₑ prop-equiv 𝟎-rec (𝟎-rec ∘ f)))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Automatic solvers for equations over integers
------------------------------------------------------------------------

-- See README.Integer for examples of how to use this solver

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

module Data.Integer.Solver where

import Algebra.Solver.Ring.Simple as Solver
import Algebra.Solver.Ring.AlmostCommutativeRing as ACR
open import Data.Integer using (_≟_)
open import Data.Integer.Properties using (+-*-commutativeRing)

------------------------------------------------------------------------
-- A module for automatically solving propositional equivalences
-- containing _+_ and _*_

-- A module for automatically solving propositional equivalences
module +-*-Solver =
  Solver (ACR.fromCommutativeRing +-*-commutativeRing) _≟_


-- Andrea & Andreas, 2014-11-12
-- Pruning projected vars during solving

open import Common.Product
open import Common.Equality

postulate
  A : Set

works1 : {q1 : Set} {q2 : Set → Set} ->
       let M : Set -> Set; M = _ in {z : Set} -> q1 ≡ M (q2 z)
works1 = refl

works2 : {q1 : Set} {q2 : Set → Set} ->
       let M : Set -> Set; M = _ in q1 ≡ M (q2 A)
works2 = refl

works3 : {q : Set × Set} ->
       let M : Set -> Set; M = _ in proj₁ q ≡ M (proj₂ q)
works3 = refl

test1 : {q : Set × (Set → Set)} ->
       let M : Set -> Set; M = _ in {z : Set} -> proj₁ q ≡ M (proj₂ q z)
test1 = refl

test2 : {q : Set × (Set → Set)} ->
       let M : Set -> Set; M = _ in proj₁ q ≡ M (proj₂ q A)
test2 = refl

-- these tests should succeed, as expanding q into a pair gets us back to
-- works1 and works2


------------------------------------------------------------------------------
-- Agda-Prop Library.
-- Properties.
------------------------------------------------------------------------------

open import Data.Nat using ( ℕ )

module Data.PropFormula.Properties ( n : ℕ ) where

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

open import Data.Bool.Base using ( Bool; false; true; not; T )
open import Data.Fin       using ( Fin ; suc; zero )

open import Data.PropFormula.Syntax n
open import Data.PropFormula.Dec n

open import Relation.Binary.PropositionalEquality using ( _≡_ ; refl ; cong )

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

suc-injective : ∀ {o} {m n : Fin o} → Fin.suc m ≡ Fin.suc n → m ≡ n
suc-injective refl = refl

var-injective : ∀ {x x₁} → Var x ≡ Var x₁ → x ≡ x₁
var-injective refl = refl

∧-injective₁ : ∀ {φ φ₁ ψ ψ₁} → (φ ∧ φ₁) ≡ (ψ ∧ ψ₁) → φ ≡ ψ
∧-injective₁ refl = refl

∧-injective₂ : ∀ {φ φ₁ ψ ψ₁} → (φ ∧ φ₁) ≡ (ψ ∧ ψ₁) → φ₁ ≡ ψ₁
∧-injective₂ refl = refl

∨-injective₁ : ∀ {φ φ₁ ψ ψ₁} → (φ ∨ φ₁) ≡ (ψ ∨ ψ₁) → φ ≡ ψ
∨-injective₁ refl = refl

∨-injective₂ : ∀ {φ φ₁ ψ ψ₁} → (φ ∨ φ₁) ≡ (ψ ∨ ψ₁) → φ₁ ≡ ψ₁
∨-injective₂ refl = refl

⊃-injective₁ : ∀ {φ φ₁ ψ ψ₁} → (φ ⊃ φ₁) ≡ (ψ ⊃ ψ₁) → φ ≡ ψ
⊃-injective₁ refl = refl

⊃-injective₂ : ∀ {φ φ₁ ψ ψ₁} → (φ ⊃ φ₁) ≡ (ψ ⊃ ψ₁) → φ₁ ≡ ψ₁
⊃-injective₂ refl = refl

⇔-injective₁ : ∀ {φ φ₁ ψ ψ₁} → (φ ⇔ φ₁) ≡ (ψ ⇔ ψ₁) → φ ≡ ψ
⇔-injective₁ refl = refl

⇔-injective₂ : ∀ {φ φ₁ ψ ψ₁} → (φ ⇔ φ₁) ≡ (ψ ⇔ ψ₁) → φ₁ ≡ ψ₁
⇔-injective₂ refl = refl

¬-injective : ∀ {φ ψ} → ¬ φ ≡ ¬ ψ → φ ≡ ψ
¬-injective refl = refl

-- Def.
_≟_ : {n : ℕ} → Decidable {A = Fin n} _≡_
zero  ≟ zero  = yes refl
zero  ≟ suc y = no λ()
suc x ≟ zero  = no λ()
suc x ≟ suc y with x ≟ y
... | yes x≡y = yes (cong Fin.suc x≡y)
... | no  x≢y = no (λ r → x≢y (suc-injective r))

-- Def.
eq : (φ ψ : PropFormula) → Dec (φ ≡ ψ)

-- Equality with Var.
eq (Var x) ⊤        = no λ()
eq (Var x) ⊥        = no λ()
eq (Var x) (ψ ∧ ψ₁) = no λ()
eq (Var x) (ψ ∨ ψ₁) = no λ()
eq (Var x) (ψ ⊃ ψ₁) = no λ()
eq (Var x) (ψ ⇔ ψ₁) = no λ()
eq (Var x) (¬ ψ)    = no λ()
eq (Var x) (Var x₁) with x ≟ x₁
... | yes refl = yes refl
... | no x≢x₁  = no (λ r → x≢x₁ (var-injective r))

-- Equality with ⊤.
eq ⊤ (Var x)        = no λ()
eq ⊤ ⊥              = no λ()
eq ⊤ (ψ ∧ ψ₁)       = no λ()
eq ⊤ (ψ ∨ ψ₁)       = no λ()
eq ⊤ (ψ ⊃ ψ₁)       = no λ()
eq ⊤ (ψ ⇔ ψ₁)      = no λ()
eq ⊤ (¬ ψ)          = no λ()
eq ⊤ ⊤              = yes refl

-- Equality with ⊥.
eq ⊥ (Var x)        = no λ()
eq ⊥ ⊤              = no λ()
eq ⊥ (ψ ∧ ψ₁)       = no λ()
eq ⊥ (ψ ∨ ψ₁)       = no λ()
eq ⊥ (ψ ⊃ ψ₁)       = no λ()
eq ⊥ (ψ ⇔ ψ₁)       = no λ()
eq ⊥ (¬ ψ)          = no λ()
eq ⊥ ⊥              = yes refl

-- Equality with ∧.
eq (φ ∧ φ₁) (Var x)  = no λ()
eq (φ ∧ φ₁) ⊤        = no λ()
eq (φ ∧ φ₁) ⊥        = no λ()
eq (φ ∧ φ₁) (ψ ∨ ψ₁) = no λ()
eq (φ ∧ φ₁) (ψ ⊃ ψ₁) = no λ()
eq (φ ∧ φ₁) (ψ ⇔ ψ₁) = no λ()
eq (φ ∧ φ₁) (¬ ψ)    = no λ()
eq (φ ∧ φ₁) (ψ ∧ ψ₁) with eq φ ψ | eq φ₁ ψ₁
... | yes refl | yes refl = yes refl
... | yes _    | no φ₁≢ψ₁ = no (λ r → φ₁≢ψ₁ (∧-injective₂ r))
... | no φ≢ψ   | _        = no (λ r → φ≢ψ   (∧-injective₁ r))

-- Equality with ∨.
eq (φ ∨ φ₁) (Var x)  = no λ()
eq (φ ∨ φ₁) ⊤        = no λ()
eq (φ ∨ φ₁) ⊥        = no λ()
eq (φ ∨ φ₁) (ψ ∧ ψ₁) = no λ()
eq (φ ∨ φ₁) (ψ ⊃ ψ₁) = no λ()
eq (φ ∨ φ₁) (ψ ⇔ ψ₁) = no λ()
eq (φ ∨ φ₁) (¬ ψ)    = no λ()
eq (φ ∨ φ₁) (ψ ∨ ψ₁) with eq φ ψ | eq φ₁ ψ₁
... | yes refl | yes refl  = yes refl
... | yes _    | no  φ₁≢ψ₁ = no (λ r → φ₁≢ψ₁ (∨-injective₂ r))
... | no φ≢ψ   | _         = no (λ r → φ≢ψ   (∨-injective₁ r))

-- Equality with ⊃.
eq (φ ⊃ φ₁) (Var x)  = no λ()
eq (φ ⊃ φ₁) ⊤        = no λ()
eq (φ ⊃ φ₁) ⊥        = no λ()
eq (φ ⊃ φ₁) (ψ ∧ ψ₁) = no λ()
eq (φ ⊃ φ₁) (ψ ∨ ψ₁) = no λ()
eq (φ ⊃ φ₁) (ψ ⇔ ψ₁) = no λ()
eq (φ ⊃ φ₁) (¬ ψ)    = no λ()
eq (φ ⊃ φ₁) (ψ ⊃ ψ₁) with eq φ ψ | eq φ₁ ψ₁
... | yes refl | yes refl  = yes refl
... | yes _    | no  φ₁≢ψ₁ = no (λ r → φ₁≢ψ₁ (⊃-injective₂ r))
... | no φ≢ψ   | _         = no (λ r → φ≢ψ   (⊃-injective₁ r))

-- Equality with ⇔.
eq (φ ⇔ φ₁) (Var x)  = no λ()
eq (φ ⇔ φ₁) ⊤        = no λ()
eq (φ ⇔ φ₁) ⊥        = no λ()
eq (φ ⇔ φ₁) (ψ ∧ ψ₁) = no λ()
eq (φ ⇔ φ₁) (ψ ∨ ψ₁) = no λ()
eq (φ ⇔ φ₁) (ψ ⊃ ψ₁) = no λ()
eq (φ ⇔ φ₁) (¬ ψ)    = no λ()
eq (φ ⇔ φ₁) (ψ ⇔ ψ₁) with eq φ ψ | eq φ₁ ψ₁
... | yes refl | yes refl  = yes refl
... | yes _    | no  φ₁≢ψ₁ = no (λ r → φ₁≢ψ₁ (⇔-injective₂ r))
... | no φ≢ψ   | _         = no (λ r → φ≢ψ   (⇔-injective₁ r))

-- Equality with ¬.
eq (¬ φ) (Var x)  = no λ()
eq (¬ φ) ⊤        = no λ()
eq (¬ φ) ⊥        = no λ()
eq (¬ φ) (ψ ∧ ψ₁) = no λ()
eq (¬ φ) (ψ ∨ ψ₁) = no λ()
eq (¬ φ) (ψ ⊃ ψ₁) = no λ()
eq (¬ φ) (ψ ⇔ ψ₁) = no λ()
eq (¬ φ) (¬ ψ) with eq φ ψ
... | yes refl = yes refl
... | no φ≢ψ   = no (λ r → φ≢ψ (¬-injective r))

-- Theorem.
subst
  : ∀ {Γ} {φ ψ}
  → φ ≡ ψ
  → Γ ⊢ φ
  → Γ ⊢ ψ
subst refl o = o

-- Theorem.
substΓ
  : ∀ {Γ₁ Γ₂} {φ}
  → Γ₁ ≡ Γ₂
  → Γ₁ ⊢ φ
  → Γ₂ ⊢ φ
substΓ refl o = o


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

open import Base
open import Homotopy.PullbackDef

-- We only consider the universal property internally to a fixed
-- universe [Set i]. If we don’t, we would have to consider an universe
-- polymorphic [P] and I don’t want to quantify over universe polymorphic things
module Homotopy.PullbackUP {i} (d : pullback-diag i)
  (P : Set i → Set i) ⦃ PA : P (pullback-diag.A d) ⦄
  ⦃ PB : P (pullback-diag.B d) ⦄ ⦃ PC : P (pullback-diag.C d) ⦄ where

open pullback-diag d

record cone (top : Set i) : Set i where
  constructor _,_,_
  field
    top→A : top → A
    top→B : top → B
    h : (t : top) → f (top→A t) ≡ g (top→B t)

top→D : (top : Set i) → pullback-diag i
top→D top = diag (top → A), (top → B), (top → C), (λ h → f ◯ h), (λ h → g ◯ h)

pullback-to-cone : (top : Set i) → (pullback (top→D top) → cone top)
pullback-to-cone top (top→A , top→B , h) = (top→A , top→B , happly h)

cone-to-pullback : (top : Set i) → (cone top → pullback (top→D top))
cone-to-pullback top (a , b , h) = (a , b , funext h)

pullback-to-cone-equiv : (top : Set i) → is-equiv (pullback-to-cone top)
pullback-to-cone-equiv top = iso-is-eq _ (cone-to-pullback top)
  (λ y → ap (λ u → _ , _ , u) (happly-funext _))
  (λ x → ap (λ u → _ , _ , u) (funext-happly _))

cone-to-pullback-equiv : (top : Set i) → is-equiv (cone-to-pullback top)
cone-to-pullback-equiv top = iso-is-eq _ (pullback-to-cone top)
  (λ x → ap (λ u → _ , _ , u) (funext-happly _))
  (λ y → ap (λ u → _ , _ , u) (happly-funext _))

compose-cone-map : (D E : Set i) (Dcone : cone D)
  → ((f : E → D) → cone E)
compose-cone-map D E (top→A , top→B , h) f =
  ((top→A ◯ f) , (top→B ◯ f) , λ x → h (f x))

is-pullback : (D : Set i) ⦃ PD : P D ⦄ (Dcone : cone D) → Set _
is-pullback D Dcone = (E : Set i) ⦃ PE : P E ⦄
                        → is-equiv (compose-cone-map D E Dcone)


module Cats.Ditrans where

open import Level using (Level ; _⊔_)

open import Cats.Category
open import Cats.Category.Setoids using (Setoids)
open import Cats.Profunctor

module _ {lo la l≈ lo′ la′ l≈′}
  {C : Category lo la l≈} {D : Category lo′ la′ l≈′}
  where

  private
    module C = Category C
    open Category D

  record Ditrans (F G : Profunctor C C D) : Set (lo ⊔ la ⊔ la′ ⊔ l≈′)
    where
    field
      dicomponent : ∀ c → pobj F c c ⇒ pobj G c c
      dinatural : ∀ {a b} (f : a C.⇒ b)
        → pmap₂ G f ∘ dicomponent a ∘ pmap₁ F f
        ≈ pmap₁ G f ∘ dicomponent b ∘ pmap₂ F f


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

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Transport
open import Cubical.Functions.FunExtEquiv

import Cubical.Data.Empty as ⊥
open import Cubical.Data.Bool
open import Cubical.Data.Nat hiding (_+_ ; +-comm ; +-assoc)
open import Cubical.Data.Vec
open import Cubical.Data.Sigma.Base
open import Cubical.Data.FinData
open import Cubical.Relation.Nullary

open import Cubical.Algebra.Group hiding (⟨_⟩)
open import Cubical.Algebra.AbGroup hiding (⟨_⟩)
open import Cubical.Algebra.Monoid hiding (⟨_⟩)
open import Cubical.Algebra.Ring

open Iso

private
  variable
    ℓ : Level
    A : Type ℓ

-- Equivalence between Vec matrix and Fin function matrix

FinMatrix : (A : Type ℓ) (m n : ℕ) → Type ℓ
FinMatrix A m n = FinVec (FinVec A n) m

VecMatrix : (A : Type ℓ) (m n : ℕ) → Type ℓ
VecMatrix A m n = Vec (Vec A n) m

FinMatrix→VecMatrix : {m n : ℕ} → FinMatrix A m n → VecMatrix A m n
FinMatrix→VecMatrix M = FinVec→Vec (λ fm → FinVec→Vec (M fm))

VecMatrix→FinMatrix : {m n : ℕ} → VecMatrix A m n → FinMatrix A m n
VecMatrix→FinMatrix M fn fm = lookup fm (lookup fn M)

FinMatrix→VecMatrix→FinMatrix : {m n : ℕ} (M : FinMatrix A m n)
                              → VecMatrix→FinMatrix (FinMatrix→VecMatrix M) ≡ M
FinMatrix→VecMatrix→FinMatrix {m = zero} M = funExt (⊥.rec ∘ ¬Fin0)
FinMatrix→VecMatrix→FinMatrix {n = zero} M = funExt₂ (λ _ → ⊥.rec ∘ ¬Fin0)
FinMatrix→VecMatrix→FinMatrix {m = suc m} {n = suc n} M = funExt₂ goal
  where
  goal : (fm : Fin (suc m)) (fn : Fin (suc n))
       → VecMatrix→FinMatrix (_ ∷ FinMatrix→VecMatrix (M ∘ suc)) fm fn ≡ M fm fn
  goal zero zero       = refl
  goal zero (suc fn) i = FinVec→Vec→FinVec (M zero ∘ suc) i fn
  goal (suc fm) fn i   = FinMatrix→VecMatrix→FinMatrix (M ∘ suc) i fm fn

VecMatrix→FinMatrix→VecMatrix : {m n : ℕ} (M : VecMatrix A m n)
                              → FinMatrix→VecMatrix (VecMatrix→FinMatrix M) ≡ M
VecMatrix→FinMatrix→VecMatrix {m = zero} [] = refl
VecMatrix→FinMatrix→VecMatrix {m = suc m} (M ∷ MS) i =
  Vec→FinVec→Vec M i ∷ VecMatrix→FinMatrix→VecMatrix MS i

FinMatrixIsoVecMatrix : (A : Type ℓ) (m n : ℕ) → Iso (FinMatrix A m n) (VecMatrix A m n)
fun (FinMatrixIsoVecMatrix A m n)      = FinMatrix→VecMatrix
inv (FinMatrixIsoVecMatrix A m n)      = VecMatrix→FinMatrix
rightInv (FinMatrixIsoVecMatrix A m n) = VecMatrix→FinMatrix→VecMatrix
leftInv (FinMatrixIsoVecMatrix A m n)  = FinMatrix→VecMatrix→FinMatrix

FinMatrix≃VecMatrix : {m n : ℕ} → FinMatrix A m n ≃ VecMatrix A m n
FinMatrix≃VecMatrix {_} {A} {m} {n} = isoToEquiv (FinMatrixIsoVecMatrix A m n)

FinMatrix≡VecMatrix : (A : Type ℓ) (m n : ℕ) → FinMatrix A m n ≡ VecMatrix A m n
FinMatrix≡VecMatrix _ _ _ = ua FinMatrix≃VecMatrix

-- Define abelian group structure on matrices
module FinMatrixAbGroup (G' : AbGroup {ℓ}) where

  open AbGroup G' renaming ( Carrier to G ; is-set to isSetG )

  zeroFinMatrix : ∀ {m n} → FinMatrix G m n
  zeroFinMatrix _ _ = 0g

  negFinMatrix : ∀ {m n} → FinMatrix G m n → FinMatrix G m n
  negFinMatrix M i j = - M i j

  addFinMatrix : ∀ {m n} → FinMatrix G m n → FinMatrix G m n → FinMatrix G m n
  addFinMatrix M N i j = M i j + N i j

  isSetFinMatrix : ∀ {m n} → isSet (FinMatrix G m n)
  isSetFinMatrix = isSetΠ2 λ _ _ → isSetG

  addFinMatrixAssoc : ∀ {m n} → (M N K : FinMatrix G m n)
                    → addFinMatrix M (addFinMatrix N K) ≡ addFinMatrix (addFinMatrix M N) K
  addFinMatrixAssoc M N K i j k = assoc (M j k) (N j k) (K j k) i

  addFinMatrix0r : ∀ {m n} → (M : FinMatrix G m n)
                 → addFinMatrix M zeroFinMatrix ≡ M
  addFinMatrix0r M i j k = rid (M j k) i

  addFinMatrix0l : ∀ {m n} → (M : FinMatrix G m n)
                 → addFinMatrix zeroFinMatrix M ≡ M
  addFinMatrix0l M i j k = lid (M j k) i

  addFinMatrixNegMatrixr : ∀ {m n} → (M : FinMatrix G m n)
                         → addFinMatrix M (negFinMatrix M) ≡ zeroFinMatrix
  addFinMatrixNegMatrixr M i j k = invr (M j k) i

  addFinMatrixNegMatrixl : ∀ {m n} → (M : FinMatrix G m n)
                         → addFinMatrix (negFinMatrix M) M ≡ zeroFinMatrix
  addFinMatrixNegMatrixl M i j k = invl (M j k) i

  addFinMatrixComm : ∀ {m n} → (M N : FinMatrix G m n)
                   → addFinMatrix M N ≡ addFinMatrix N M
  addFinMatrixComm M N i k l = comm (M k l) (N k l) i

  FinMatrixAbGroup : (m n : ℕ) → AbGroup {ℓ}
  FinMatrixAbGroup m n =
    makeAbGroup {G = FinMatrix G m n} zeroFinMatrix addFinMatrix negFinMatrix
                isSetFinMatrix addFinMatrixAssoc addFinMatrix0r
                addFinMatrixNegMatrixr addFinMatrixComm

-- Define a abelian group structure on vector matrices and prove that
-- it is equal to FinMatrixAbGroup using the SIP
module _ (G' : AbGroup {ℓ}) where

  open AbGroup G' renaming ( Carrier to G )

  zeroVecMatrix : ∀ {m n} → VecMatrix G m n
  zeroVecMatrix = replicate (replicate 0g)

  negVecMatrix : ∀ {m n} → VecMatrix G m n → VecMatrix G m n
  negVecMatrix = map (map (λ x → - x))

  addVec : ∀ {m} → Vec G m → Vec G m → Vec G m
  addVec [] [] = []
  addVec (x ∷ xs) (y ∷ ys) = x + y ∷ addVec xs ys

  addVecMatrix : ∀ {m n} → VecMatrix G m n → VecMatrix G m n → VecMatrix G m n
  addVecMatrix [] [] = []
  addVecMatrix (M ∷ MS) (N ∷ NS) = addVec M N ∷ addVecMatrix MS NS

  open FinMatrixAbGroup

  -- Proof that FinMatrix→VecMatrix is a group homorphism
  FinMatrix→VecMatrixHomAdd : (m n : ℕ) (M N : FinMatrix G m n)
    → FinMatrix→VecMatrix (addFinMatrix G' M N) ≡
      addVecMatrix (FinMatrix→VecMatrix M) (FinMatrix→VecMatrix N)
  FinMatrix→VecMatrixHomAdd zero n M N = refl
  FinMatrix→VecMatrixHomAdd (suc m) n M N =
    λ i → lem n (M zero) (N zero) i
        ∷ FinMatrix→VecMatrixHomAdd m n (λ i j → M (suc i) j) (λ i j → N (suc i) j) i
     where
     lem : (n : ℕ) (V W : FinVec G n)
       → FinVec→Vec (λ j → V j + W j) ≡ addVec (FinVec→Vec V) (FinVec→Vec W)
     lem zero V W = refl
     lem (suc n) V W = λ i → V zero + W zero ∷ lem n (V ∘ suc) (W ∘ suc) i

  -- Combine everything to get an induced abelian group structure of
  -- VecMatrix that is equal to the one on FinMatrix
  VecMatrixAbGroup : (m n : ℕ) → AbGroup
  VecMatrixAbGroup m n =
    InducedAbGroup (FinMatrixAbGroup G' m n) (_ , addVecMatrix)
                    FinMatrix≃VecMatrix (FinMatrix→VecMatrixHomAdd m n)

  FinMatrixAbGroup≡VecMatrixAbGroup : (m n : ℕ) → FinMatrixAbGroup G' m n ≡ VecMatrixAbGroup m n
  FinMatrixAbGroup≡VecMatrixAbGroup m n =
    InducedAbGroupPath (FinMatrixAbGroup G' m n) (_ , addVecMatrix)
                        FinMatrix≃VecMatrix (FinMatrix→VecMatrixHomAdd m n)

-- Define identity matrix and matrix multiplication for FinMatrix and
-- prove that square matrices form a ring
module _ (R' : Ring {ℓ}) where

  open Ring R' renaming ( Carrier to R ; is-set to isSetR )
  open Theory R'
  open FinMatrixAbGroup (abgroup R _ _ _ (R' .Ring.+-isAbGroup))

  oneFinMatrix : ∀ {n} → FinMatrix R n n
  oneFinMatrix i j = if i == j then 1r else 0r

  -- TODO: upstream and state for monoids
  ∑ : ∀ {n} → FinVec R n → R
  ∑ = foldrFin _+_ 0r

  mulFinMatrix : ∀ {m1 m2 m3} → FinMatrix R m1 m2 → FinMatrix R m2 m3 → FinMatrix R m1 m3
  mulFinMatrix M N i k = ∑ λ j → M i j · N j k

  -- Properties
  sumVecExt : ∀ {n} → {V W : FinVec R n} → ((i : Fin n) → V i ≡ W i) → ∑ V ≡ ∑ W
  sumVecExt {n = zero} _    = refl
  sumVecExt {n = suc n} h i = h zero i + sumVecExt (h ∘ suc) i

  sumVecSplit : ∀ {n} → (V W : FinVec R n) → ∑ (λ i → V i + W i) ≡ ∑ V + ∑ W
  sumVecSplit {n = zero}  V W = sym (+-rid 0r)
  sumVecSplit {n = suc n} V W =
    V zero + W zero + ∑ (λ i → V (suc i) + W (suc i)) ≡⟨ (λ i → V zero + W zero + sumVecSplit (V ∘ suc) (W ∘ suc) i) ⟩
    V zero + W zero + (∑ (V ∘ suc) + ∑ (W ∘ suc))     ≡⟨ sym (+-assoc _ _ _) ⟩
    V zero + (W zero + (∑ (V ∘ suc) + ∑ (W ∘ suc)))   ≡⟨ cong (λ x → V zero + x) (+-assoc-comm1 _ _ _) ⟩
    V zero + (∑ (V ∘ suc) + (W zero + (∑ (W ∘ suc)))) ≡⟨ +-assoc _ _ _ ⟩
    V zero + ∑ (V ∘ suc) + (W zero + ∑ (W ∘ suc))     ∎

  sumVec0r : (n : ℕ) → ∑ (λ (i : Fin n) → 0r) ≡ 0r
  sumVec0r zero    = refl
  sumVec0r (suc n) = cong (λ x → 0r + x) (sumVec0r n) ∙ +-rid 0r

  sumVecExchange : ∀ {m n} → (M : FinMatrix R m n) → ∑ (λ i → ∑ (λ j → M i j)) ≡ ∑ (λ j → ∑ (λ i → M i j))
  sumVecExchange {m = zero}  {n = n}     M = sym (sumVec0r n)
  sumVecExchange {m = suc m} {n = zero}  M = cong (λ x → 0r + x) (sumVec0r m) ∙ +-rid 0r
  sumVecExchange {m = suc m} {n = suc n} M =
     let a  = M zero zero
         L  = ∑ λ j → M zero (suc j)
         C  = ∑ λ i → M (suc i) zero
         N  = ∑ λ i → ∑ λ j → M (suc i) (suc j)
         -- N reindexed
         N' = ∑ λ j → ∑ λ i → M (suc i) (suc j)
     in a + L + ∑ (λ i → ∑ (λ j → M (suc i) j)) ≡⟨ (λ k → a + L + sumVecSplit (λ i → M (suc i) zero) (λ i → ∑ (λ j → M (suc i) (suc j))) k) ⟩
        a + L + (C + N)                         ≡⟨ (λ k → a + L + (C + sumVecExchange (λ i j → M (suc i) (suc j)) k)) ⟩
        a + L + (C + N')                        ≡⟨ sym (+-assoc _ _ _) ⟩
        a + (L + (C + N'))                      ≡⟨ (λ k → a + +-assoc-comm1 L C N' k) ⟩
        a + (C + (L + N'))                      ≡⟨ +-assoc _ _ _ ⟩
        a + C + (L + N')                        ≡⟨ (λ k → a + C + sumVecSplit (λ j → M zero (suc j)) (λ j → ∑ (λ i → M (suc i) (suc j))) (~ k)) ⟩
        a + C + ∑ (λ j → ∑ (λ i → M i (suc j))) ∎

  sumVecMulrdist : ∀ {n} → (x : R) → (V : FinVec R n)
                 → x · ∑ V ≡ ∑ λ i → x · V i
  sumVecMulrdist {n = zero}  x _ = 0-rightNullifies x
  sumVecMulrdist {n = suc n} x V =
    x · (V zero + ∑ (V ∘ suc))           ≡⟨ ·-rdist-+ _ _ _ ⟩
    x · V zero + x · ∑ (V ∘ suc)         ≡⟨ (λ i → x · V zero + sumVecMulrdist x (V ∘ suc) i) ⟩
    x · V zero + ∑ (λ i → x · V (suc i)) ∎

  sumVecMulldist : ∀ {n} → (x : R) → (V : FinVec R n)
                 → (∑ V) · x ≡ ∑ λ i → V i · x
  sumVecMulldist {n = zero}  x _ = 0-leftNullifies x
  sumVecMulldist {n = suc n} x V =
    (V zero + ∑ (V ∘ suc)) · x           ≡⟨ ·-ldist-+ _ _ _ ⟩
    V zero · x + (∑ (V ∘ suc)) · x       ≡⟨ (λ i → V zero · x + sumVecMulldist x (V ∘ suc) i) ⟩
    V zero · x + ∑ (λ i → V (suc i) · x) ∎

  sumVecMulr0 : ∀ {n} → (V : FinVec R n) → ∑ (λ i → V i · 0r) ≡ 0r
  sumVecMulr0 V = sym (sumVecMulldist 0r V) ∙ 0-rightNullifies _

  sumVecMul0r : ∀ {n} → (V : FinVec R n) → ∑ (λ i → 0r · V i) ≡ 0r
  sumVecMul0r V = sym (sumVecMulrdist 0r V) ∙ 0-leftNullifies _

  sumVecMulr1 : (n : ℕ) (V : FinVec R n) → (j : Fin n) → ∑ (λ i → V i · (if i == j then 1r else 0r)) ≡ V j
  sumVecMulr1 (suc n) V zero = (λ k → ·-rid (V zero) k + sumVecMulr0 (V ∘ suc) k) ∙ +-rid (V zero)
  sumVecMulr1 (suc n) V (suc j) =
     (λ i → 0-rightNullifies (V zero) i + ∑ (λ x → V (suc x) · (if x == j then 1r else 0r)))
     ∙∙ +-lid _ ∙∙ sumVecMulr1 n (V ∘ suc) j

  sumVecMul1r : (n : ℕ) (V : FinVec R n) → (j : Fin n) → ∑ (λ i → (if j == i then 1r else 0r) · V i) ≡ V j
  sumVecMul1r (suc n) V zero = (λ k → ·-lid (V zero) k + sumVecMul0r (V ∘ suc) k) ∙ +-rid (V zero)
  sumVecMul1r (suc n) V (suc j) =
    (λ i → 0-leftNullifies (V zero) i + ∑ (λ i → (if j == i then 1r else 0r) · V (suc i)))
    ∙∙ +-lid _ ∙∙ sumVecMul1r n (V ∘ suc) j

  mulFinMatrixAssoc : ∀ {m n k l} → (M : FinMatrix R m n) → (N : FinMatrix R n k) → (K : FinMatrix R k l)
                   → mulFinMatrix M (mulFinMatrix N K) ≡ mulFinMatrix (mulFinMatrix M N) K
  mulFinMatrixAssoc M N K = funExt₂ λ i j →
    ∑ (λ k → M i k · ∑ (λ l → N k l · K l j))   ≡⟨ sumVecExt (λ k → sumVecMulrdist (M i k) (λ l → N k l · K l j)) ⟩
    ∑ (λ k → ∑ (λ l → M i k · (N k l · K l j))) ≡⟨ sumVecExt (λ k → sumVecExt (λ l → ·-assoc (M i k) (N k l) (K l j))) ⟩
    ∑ (λ k → ∑ (λ l → M i k · N k l · K l j))   ≡⟨ sumVecExchange (λ k l → M i k · N k l · K l j) ⟩
    ∑ (λ l → ∑ (λ k → M i k · N k l · K l j))   ≡⟨ sumVecExt (λ l → sym (sumVecMulldist (K l j) (λ k → M i k · N k l))) ⟩
    ∑ (λ l → ∑ (λ k → M i k · N k l) · K l j)   ∎

  mulFinMatrixr1 : ∀ {m n} → (M : FinMatrix R m n) → mulFinMatrix M oneFinMatrix ≡ M
  mulFinMatrixr1 M = funExt₂ λ i j → sumVecMulr1 _ (M i) j

  mulFinMatrix1r : ∀ {m n} → (M : FinMatrix R m n) → mulFinMatrix oneFinMatrix M ≡ M
  mulFinMatrix1r M = funExt₂ λ i j → sumVecMul1r _ (λ x → M x j) i

  mulFinMatrixrDistrAddFinMatrix : ∀ {n} (M N K : FinMatrix R n n)
                                 → mulFinMatrix M (addFinMatrix N K) ≡ addFinMatrix (mulFinMatrix M N) (mulFinMatrix M K)
  mulFinMatrixrDistrAddFinMatrix M N K = funExt₂ λ i j →
    ∑ (λ k → M i k · (N k j + K k j))                 ≡⟨ sumVecExt (λ k → ·-rdist-+ (M i k) (N k j) (K k j)) ⟩
    ∑ (λ k → M i k · N k j + M i k · K k j)           ≡⟨ sumVecSplit (λ k → M i k · N k j) (λ k → M i k · K k j) ⟩
    ∑ (λ k → M i k · N k j) + ∑ (λ k → M i k · K k j) ∎

  mulFinMatrixlDistrAddFinMatrix : ∀ {n} (M N K : FinMatrix R n n)
                                 → mulFinMatrix (addFinMatrix M N) K ≡ addFinMatrix (mulFinMatrix M K) (mulFinMatrix N K)
  mulFinMatrixlDistrAddFinMatrix M N K = funExt₂ λ i j →
    ∑ (λ k → (M i k + N i k) · K k j)                 ≡⟨ sumVecExt (λ k → ·-ldist-+ (M i k) (N i k) (K k j)) ⟩
    ∑ (λ k → M i k · K k j + N i k · K k j)           ≡⟨ sumVecSplit (λ k → M i k · K k j) (λ k → N i k · K k j) ⟩
    ∑ (λ k → M i k · K k j) + ∑ (λ k → N i k · K k j) ∎

  FinMatrixRing : (n : ℕ) → Ring {ℓ}
  FinMatrixRing n =
    makeRing {R = FinMatrix R n n} zeroFinMatrix oneFinMatrix addFinMatrix
             mulFinMatrix negFinMatrix isSetFinMatrix addFinMatrixAssoc
             addFinMatrix0r addFinMatrixNegMatrixr addFinMatrixComm
             mulFinMatrixAssoc mulFinMatrixr1 mulFinMatrix1r
             mulFinMatrixrDistrAddFinMatrix mulFinMatrixlDistrAddFinMatrix


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

module Fin where

open import Cubical.Core.Everything using (_≡_; Level; Type; Σ; _,_; fst; snd; _≃_; ~_)

open import Cubical.Foundations.Prelude     using (refl; sym; _∙_; cong; transport; subst; funExt; transp; I; i0; i1)
open import Cubical.Foundations.Function    using (_∘_)
open import Cubical.Foundations.Isomorphism using (iso; Iso; isoToPath; section; retract)

open import Data.Fin   using (Fin; toℕ; fromℕ<; #_; _≟_) renaming (zero to fz; suc to fs)
open import Data.Nat   using (ℕ; zero; suc; _<_; _>_; z≤n; s≤s)

-- Alternate representation of Fin as a number and proof of its upper bound.
record Fin1 (n : ℕ) : Type where
  constructor fin1
  field
    r   : ℕ
    r<n : r < n

fin1suc : {n : ℕ} → Fin1 n → Fin1 (suc n)
fin1suc (fin1 r r<n) = fin1 (suc r) (s≤s r<n)

-- From Data.Fin.Properites
-- They need to be re-defined here since Cubical uses a different ≡

fromℕ<-toℕ : ∀ {m} (i : Fin m) (i<m : toℕ i < m) → fromℕ< i<m ≡ i
fromℕ<-toℕ fz     (s≤s z≤n)       = refl
fromℕ<-toℕ (fs i) (s≤s (s≤s m≤n)) = cong fs (fromℕ<-toℕ i (s≤s m≤n))

toℕ-fromℕ< : ∀ {m n} (m<n : m < n) → toℕ (fromℕ< m<n) ≡ m
toℕ-fromℕ< (s≤s z≤n)       = refl
toℕ-fromℕ< (s≤s (s≤s m<n)) = cong suc (toℕ-fromℕ< (s≤s m<n))

toℕ<n : ∀ {n} (i : Fin n) → toℕ i < n
toℕ<n fz     = s≤s z≤n
toℕ<n (fs i) = s≤s (toℕ<n i)

-------------------
-- Equivalence of Fin and Fin1

fin→fin1 : {n : ℕ} → Fin n → Fin1 n
fin→fin1 fz     = fin1 0 (s≤s z≤n)
fin→fin1 (fs x) = let fin1 r r<n = fin→fin1 x in fin1 (suc r) (s≤s r<n)

fin1→fin : {n : ℕ} → Fin1 n → Fin n
fin1→fin (fin1 _ r<n) = fromℕ< r<n

fin→fin1→fin : {n : ℕ} → (r : Fin n) → (fin1→fin ∘ fin→fin1) r ≡ r
fin→fin1→fin fz     = refl
fin→fin1→fin (fs r) = cong fs (fin→fin1→fin r)

fin1→fin→fin1 : {n : ℕ} → (r : Fin1 n) → (fin→fin1 ∘ fin1→fin) r ≡ r
fin1→fin→fin1 (fin1 zero    (s≤s z≤n))       = refl
fin1→fin→fin1 (fin1 (suc r) (s≤s (s≤s r<n))) = cong fin1suc (fin1→fin→fin1 (fin1 r (s≤s r<n)))

fin≃fin1 : {n : ℕ} → Iso (Fin n) (Fin1 n)
fin≃fin1 = iso fin→fin1 fin1→fin fin1→fin→fin1 fin→fin1→fin

fin≡fin1 : {n : ℕ} → Fin n ≡ Fin1 n
fin≡fin1 = isoToPath fin≃fin1

-- Equivalence of toℕ and Fin1.r

toℕ≡Fin1r1 : {n : ℕ} → (r : Fin n) → toℕ r ≡ Fin1.r (fin→fin1 r)
toℕ≡Fin1r1 fz     = refl
toℕ≡Fin1r1 (fs r) = cong suc (toℕ≡Fin1r1 r)

toℕ≡Fin1r : {n : ℕ} → toℕ {n} ≡ Fin1.r ∘ fin→fin1
toℕ≡Fin1r = funExt toℕ≡Fin1r1

Fin1r≡toℕ1 : {n : ℕ} → (r : Fin1 n) → toℕ (fin1→fin r) ≡ Fin1.r r
Fin1r≡toℕ1 (fin1 r r<n) = toℕ-fromℕ< r<n

Fin1r≡toℕ : {n : ℕ} → toℕ {n} ∘ fin1→fin ≡ Fin1.r
Fin1r≡toℕ = funExt Fin1r≡toℕ1


open import FRP.JS.Bool using ( Bool ; true ; false ; not ; _∨_ )
open import FRP.JS.True using ( True )
open import FRP.JS.Maybe using ( Maybe ; just ; nothing )
open import FRP.JS.Primitive using ( ℤ ; String )

module FRP.JS.Float where

infixr 4 _≤_ _<_ _≟_ _≠_
infixl 6 _+_ _-_
infixl 7 _*_ _/_ _/?_

open import FRP.JS.Primitive public using ( ℝ )

primitive
  primFloatPlus      : ℝ -> ℝ -> ℝ
  primFloatMinus     : ℝ -> ℝ -> ℝ
  primFloatTimes     : ℝ -> ℝ -> ℝ
  primFloatDiv       : ℝ -> ℝ -> ℝ
  primFloatLess      : ℝ -> ℝ -> Bool
  primFloor          : ℝ -> ℤ
  primCeiling        : ℝ -> ℤ
  primExp            : ℝ -> ℝ
  primLog            : ℝ -> ℝ
  primSin            : ℝ -> ℝ
  primShowFloat      : ℝ -> String
  primShowInteger    : ℤ → String
  primIntegerToFloat : ℤ → ℝ

_+_   = primFloatPlus
_-_   = primFloatMinus
_*_   = primFloatTimes
_<_   = primFloatLess
⌊_⌋   = primFloor
⌈_⌉   = primCeiling
exp   = primExp
log   = primLog
sin   = primSin

{-# COMPILED_JS _+_   function(x) { return function(y) { return x + y; }; } #-}
{-# COMPILED_JS _-_   function(x) { return function(y) { return x - y; }; } #-}
{-# COMPILED_JS _*_   function(x) { return function(y) { return x * y; }; } #-}
{-# COMPILED_JS _<_   function(x) { return function(y) { return x < y; }; } #-}
{-# COMPILED_JS ⌊_⌋   Math.floor #-}
{-# COMPILED_JS ⌈_⌉   Math.ceil #-}
{-# COMPILED_JS exp   Math.exp #-}
{-# COMPILED_JS log   Math.log #-}
{-# COMPILED_JS sin   Math.sin #-}

-_ : ℝ → ℝ
- x = 0.0 - x

_≠_ : ℝ → ℝ → Bool
(x ≠ y) = (x < y) ∨ (y < x)

_≟_ : ℝ → ℝ → Bool
(x ≟ y) = not (x ≠ y)

_≤_ : ℝ → ℝ → Bool
(x ≤ y) = not (y < x)

-- Some hoop-jumping to avoid division by zero, so we don't end up with
-- Infinity, -Infinity or NaN.

_/_ : (x y : ℝ) → {y≠0 : True (y ≠ 0.0)} → ℝ
x / y = primFloatDiv x y

_/?_ : ℝ → ℝ → Maybe ℝ
x /? 0.0 = nothing
x /? y   = just (primFloatDiv x y)

show : ℝ → String
show x
 with (x ≟ primIntegerToFloat ⌊ x ⌋)
... | true  = primShowInteger ⌊ x ⌋
... | false = primShowFloat x

{-# COMPILED_JS -_   function(x) { return -x; } #-}
{-# COMPILED_JS _≠_ function(x) { return function(y) { return x !== y; }; } #-}
{-# COMPILED_JS _≟_ function(x) { return function(y) { return x === y; }; } #-}
{-# COMPILED_JS _≤_  function(x) { return function(y) { return x <= y; }; } #-}
{-# COMPILED_JS _/_  function(x) { return function(y) { return function() { return x / y; }; }; } #-}
{-# COMPILED_JS _/?_  function(x) { return function(y) { if (y === 0) { return null; } else { return x / y; } }; } #-}
{-# COMPILED_JS show  function(x) { return x.toString(); } #-}


-- Andreas, 2011-05-30
-- {-# OPTIONS -v tc.lhs.unify:50 #-}
module Issue292 where

data Bool  : Set where true  false  : Bool
data Bool2 : Set where true2 false2 : Bool2

data ⊥ : Set where

infix 3 ¬_

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

infix 4 _≅_

data _≅_ {A : Set} (x : A) : ∀ {B : Set} → B → Set where
  refl : x ≅ x

record Σ (A : Set) (B : A → Set) : Set where
  constructor _,_
  field
    proj₁ : A
    proj₂ : B proj₁

open Σ public

P : Set -> Set
P S = Σ S (\s → s ≅ true)

pbool : P Bool
pbool = true , refl

-- the following should fail:
¬pbool2 : ¬ P Bool2
¬pbool2 ( true2 , () )
¬pbool2 ( false2 , () )

{- using subst, one could now prove distinctness of types, which we don't want
tada : ¬ (Bool ≡ Bool2)
tada eq = ¬pbool2 (subst (\ S → P S) eq pbool )
-}


{-

This file contains:

- Definition of propositional truncation

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

open import Cubical.Core.Primitives

-- Propositional truncation as a higher inductive type:

data ∥_∥ {ℓ} (A : Type ℓ) : Type ℓ where
  ∣_∣ : A → ∥ A ∥
  squash : ∀ (x y : ∥ A ∥) → x ≡ y

-- Mere existence

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

infix 2 ∃-syntax

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

syntax ∃-syntax A (λ x → B) = ∃[ x ∈ A ] B


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

open import Bundles

module Properties.AlmostOrderedField {ℓ ℓ'} (AOF : AlmostOrderedField {ℓ} {ℓ'}) where

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

private
  variable
    ℓ'' : Level

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

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

import MoreLogic
open MoreLogic.Reasoning
import MoreAlgebra

-- Lemma 4.1.11.
-- In the presence of the first five axioms of Definition 4.1.10, conditions (†) and (∗) are together equivalent to the condition that for all x, y, z : F,
--  1. x ≤ y ⇔ ¬(y < x),
--  2. x # y ⇔ (x < y) ∨ (y < x)
--  3. x ≤ y ⇔ x + z ≤ y + z,
--  4. x < y ⇔ x + z < y + z,
--  5. 0 < x + y ⇒ 0 < x ∨ 0 < y,
--  6. x < y ≤ z ⇒ x < z,
--  7. x ≤ y < z ⇒ x < z,
--  8. x ≤ y ∧ 0 ≤ z ⇒ x z ≤ y z,
--  9. 0 < z ⇒ (x < y ⇔ x z < y z),
-- 10. 0 < 1.

open AlmostOrderedField AOF renaming (Carrier to F)
import Cubical.Structures.Ring
open Cubical.Structures.Ring.Theory (record {AlmostOrderedField AOF})
open MoreAlgebra.Properties.Group   (record {AlmostOrderedField AOF renaming (+-isGroup to isGroup )})

-- NOTE: ported from Cubical.Structures.Group.GroupLemmas
-- NOTE: these versions differ from the Group-versions because they are defined w.r.t. an appartness relation _#_ that is not present for the Group
simplR : {a b : F} (c : F) {{_ : c # 0f}} → a · c ≡ b · c → a ≡ b
simplR {a} {b} c {{_}} a·c≡b·c =
   a                ≡⟨ sym (fst (·-identity a)) ∙ cong (a ·_) (sym (·-rinv c it)) ∙ ·-assoc _ _ _ ⟩
  (a · c) · (c ⁻¹ᶠ) ≡⟨ cong (_· c ⁻¹ᶠ) a·c≡b·c ⟩
  (b · c) · (c ⁻¹ᶠ) ≡⟨ sym (·-assoc _ _ _) ∙ cong (b ·_) (·-rinv c it) ∙ fst (·-identity b)  ⟩
   b ∎

·-preserves-≡ʳ : {a b : F} (c : F) {{_ : c # 0f}} → a · c ≡ b · c → a ≡ b
·-preserves-≡ʳ = simplR

-- ·-linv-unique : (x y : F) (x·y≡1 : (x ·₁ y) ≡ 1f) → x ≡ (y ⁻¹ᶠ₁)
module _ (x y : F) (x·y≡1 : x · y ≡ 1f) where
  y#0 = snd (·-inv-back _ _ x·y≡1) -- duplicated inhabitant (see notes)
  instance _ = y # 0f ∋ y#0
  import Cubical.Structures.Group

  -- NOTE: ported from Cubical.Structures.Group.GroupLemmas
  abstract
    ·-linv-unique' : Σ[ p ∈ y # 0f ] (x ≡ _⁻¹ᶠ y {{p}})
    ·-linv-unique' = it , (
      x · y ≡ 1f        ⇒⟨ transport (λ i → x · y ≡ ·-linv y it (~ i)) ⟩
      x · y ≡ y ⁻¹ᶠ · y ⇒⟨ simplR _  ⟩
      x     ≡ y ⁻¹ᶠ     ◼) x·y≡1

·-linv-unique : (x y : F) → ((x · y) ≡ 1f) → Σ[ p ∈ y # 0f ] x ≡ (_⁻¹ᶠ y {{p}})
·-linv-unique = ·-linv-unique'

-- ⁻¹ᶠ-involutive : (x : F) (z#0 : x #' 0f) → ((x ⁻¹ᶠ₁) ⁻¹ᶠ₁) ≡ x
module _ (z : F) (z#0 : z # 0f) where
  private
    instance _ = z#0
    z⁻¹ = z ⁻¹ᶠ -- NOTE: interestingly, the instance argument is not applied and y remains normalized in terms of z
              --       so we get `y : {{ _ : z #' 0f }} → F` here
    z⁻¹#0 = snd (·-inv-back z z⁻¹ (·-rinv z it))
    -- NOTE: for some reason I get "There are instances whose type is still unsolved when checking that the expression it has type z #' 0f"
    --       typing `y : F` did not help much. therefore this goes in two lines
    instance _ = z⁻¹#0
    z⁻¹⁻¹ = z⁻¹ ⁻¹ᶠ

  -- NOTE: this should be similar to `right-helper` + `-involutive`
  ⁻¹ᶠ-involutive : (z ⁻¹ᶠ) ⁻¹ᶠ ≡ z
  ⁻¹ᶠ-involutive = (
     z⁻¹⁻¹              ≡⟨ sym (fst (·-identity _)) ⟩
     z⁻¹⁻¹ ·      1f    ≡⟨ (λ i →  z⁻¹⁻¹ · ·-linv _ it (~ i)) ⟩
     z⁻¹⁻¹ · (z⁻¹  · z) ≡⟨ ·-assoc _ _ _ ⟩
    (z⁻¹⁻¹ ·  z⁻¹) · z  ≡⟨ (λ i → ·-linv z⁻¹ it i · z) ⟩
          1f       · z  ≡⟨  snd (·-identity _)  ⟩
                     z  ∎)

module forward -- 6. ⇒ 1. 2. 3. 4. 5.
  -- 6. (†)
  (+-<-extensional : ∀ w x y z → (x + y) < (z + w) → (x < z) ⊎ (y < w))
  -- 6. (∗)
  (·-preserves-< : ∀ x y z → 0f < z → x < y → (x · z) < (y · z))
  where
  -- abstract
    --  1. x ≤ y ⇔ ¬(y < x),
    item-1 : ∀ x y → x ≤ y → ¬(y < x)
    item-1 = λ _ _ x≤y → x≤y -- holds definitionally

    item-1-back : ∀ x y → ¬(y < x) → x ≤ y
    item-1-back = λ _ _ ¬[y<x] → ¬[y<x]

    --  2. x # y ⇔ (x < y) ∨ (y < x)
    item-2 : ∀ x y → x # y → (x < y) ⊎ (y < x)
    item-2 = λ _ _ x#y → x#y -- holds definitionally

    item-2-back : ∀ x y → (x < y) ⊎ (y < x) → x # y
    item-2-back = λ _ _ [x<y]⊎[y<x] → [x<y]⊎[y<x] -- holds definitionally

    -- NOTE: just a plain copy of the previous proof
    +-preserves-< : ∀ a b x → a < b → a + x < b + x
    +-preserves-< a b x a<b = (
       a            <  b            ⇒⟨ transport (λ i → sym (fst (+-identity a)) i < sym (fst (+-identity b)) i) ⟩
       a +    0f    <  b +    0f    ⇒⟨ transport (λ i → a + sym (+-rinv x) i < b + sym (+-rinv x) i) ⟩
       a + (x  - x) <  b + (x  - x) ⇒⟨ transport (λ i → +-assoc a x (- x) i < +-assoc b x (- x) i) ⟩
      (a +  x) - x  < (b +  x) - x  ⇒⟨ +-<-extensional (- x) (a + x) (- x) (b + x) ⟩
      (a + x < b + x) ⊎ (- x < - x) ⇒⟨ (λ{ (inl a+x<b+x) → a+x<b+x -- somehow ⊥-elim needs a hint in the next line
                                         ; (inr  -x<-x ) → ⊥-elim {A = λ _ → (a + x < b + x)} (<-irrefl (- x) -x<-x) }) ⟩
       a + x < b + x ◼) a<b

    +-preserves-<-back : ∀ x y z → x + z < y + z → x < y
    +-preserves-<-back x y z =
      ( x + z < y + z              ⇒⟨ +-preserves-< _ _ (- z) ⟩
        (x + z) - z  < (y + z) - z ⇒⟨ transport (λ i → +-assoc x z (- z) (~ i) < +-assoc y z (- z) (~ i)) ⟩
        x + (z - z) < y + (z - z)  ⇒⟨ transport (λ i → x + +-rinv z i < y + +-rinv z i) ⟩
        x + 0f < y + 0f            ⇒⟨ transport (λ i → fst (+-identity x) i < fst (+-identity y) i) ⟩
        x < y ◼)

    --  3. x ≤ y ⇔ x + z ≤ y + z,
    item-3 : ∀ x y z → x ≤ y → x + z ≤ y + z
    item-3 x y z = (
       x     ≤ y          ⇒⟨ (λ z → z) ⟩ -- unfold the definition
      (y     < x     → ⊥) ⇒⟨ (λ f → f ∘ (+-preserves-<-back y x z) ) ⟩
      (y + z < x + z → ⊥) ⇒⟨ (λ z → z) ⟩ -- refold the definition
       x + z ≤ y + z ◼)

    item-3-back : ∀ x y z → x + z ≤ y + z → x ≤ y
    item-3-back x y z = (
       x + z ≤ y + z      ⇒⟨ (λ z → z) ⟩ -- unfold the definition
      (y + z < x + z → ⊥) ⇒⟨ (λ f p → f (+-preserves-< y x z p)) ⟩ -- just a variant of the above
      (y     < x     → ⊥) ⇒⟨ (λ z → z) ⟩ -- refold the definition
       x     ≤ y ◼)

    --  4. x < y ⇔ x + z < y + z,
    item-4 : ∀ x y z → x < y → x + z < y + z
    item-4 = +-preserves-<

    item-4-back : ∀ x y z → x + z < y + z → x < y
    item-4-back = +-preserves-<-back

    --  5. 0 < x + y ⇒ 0 < x ∨ 0 < y,
    item-5 : ∀ x y → 0f < x + y → (0f < x) ⊎ (0f < y)
    item-5 x y = (
      (0f      < x + y) ⇒⟨ transport (λ i → fst (+-identity 0f) (~ i) < x + y) ⟩
      (0f + 0f < x + y) ⇒⟨ +-<-extensional y 0f 0f x ⟩
      (0f < x) ⊎ (0f < y) ◼)

    --  6. x < y ≤ z ⇒ x < z,
    item-6 : ∀ x y z → x < y → y ≤ z → x < z
    item-6 x y z x<y y≤z = (
       x      <  y      ⇒⟨ +-preserves-< _ _ _ ⟩
       x + z  <  y + z  ⇒⟨ transport (λ i → x + z < +-comm y z i) ⟩
       x + z  <  z + y  ⇒⟨ +-<-extensional y x z z  ⟩
      (x < z) ⊎ (z < y) ⇒⟨ (λ{ (inl x<z) → x<z
                             ; (inr z<y) → ⊥-elim (y≤z z<y) }) ⟩
       x < z  ◼) x<y

    --  7. x ≤ y < z ⇒ x < z,
    item-7 : ∀ x y z → x ≤ y → y < z → x < z
    item-7 x y z x≤y = ( -- very similar to the previous one
       y      <  z      ⇒⟨ +-preserves-< y z x ⟩
       y + x  <  z + x  ⇒⟨ transport (λ i → +-comm y x i < z + x) ⟩
       x + y  <  z + x  ⇒⟨ +-<-extensional x x y z ⟩
      (x < z) ⊎ (y < x) ⇒⟨ (λ{ (inl x<z) → x<z
                             ; (inr y<x) → ⊥-elim (x≤y y<x)}) ⟩
       x < z  ◼)

    item-10 : 0f < 1f

    module _ (z : F) (0<z : 0f < z) where
      private
        instance _ = z # 0f ∋ inr 0<z
        z⁻¹ = z ⁻¹ᶠ
        z⁻¹#0 = snd (·-inv-back z z⁻¹ (·-rinv z it))
      abstract
        ⁻¹ᶠ-preserves-sign : 0f < z ⁻¹ᶠ
        ⁻¹ᶠ-preserves-sign with z⁻¹#0
        ... | inl z⁻¹<0 = (
          z⁻¹     < 0f     ⇒⟨ ·-preserves-< _ _ z 0<z ⟩
          z⁻¹ · z < 0f · z ⇒⟨ transport (λ i → ·-linv z it i <  0-leftNullifies z i) ⟩
          1f      < 0f     ⇒⟨ <-trans _ _ _ item-10 ⟩
          0f      < 0f     ⇒⟨ <-irrefl _ ⟩
                  ⊥        ⇒⟨ ⊥-elim ⟩ _ ◼) z⁻¹<0
        ... | inr 0<z⁻¹ = 0<z⁻¹

    --  8. x ≤ y ∧ 0 ≤ z ⇒ x z ≤ y z,
    item-8 : ∀ x y z → x ≤ y → 0f ≤ z → x · z ≤ y · z
    -- For item 8, suppose x ≤ y and 0 ≤ z and yz < xz.
    item-8 x y z x≤y 0≤z y·z<x·z = let
      -- Then 0 < z (x − y) by (†),
      i   = (  y · z            <  x · z                ⇒⟨ transport (λ i → ·-comm y z i < ·-comm x z i) ⟩
               z · y            <  z · x                ⇒⟨ +-preserves-< _ _ _ ⟩
              (z · y) - (z · y) < (z · x) - (z ·    y ) ⇒⟨ transport (cong₂ _<_ (+-rinv (z · y))
                                                             ( λ i → (z · x) + sym (-commutesWithRight-· z y) i )) ⟩
                             0f < (z · x) + (z · (- y)) ⇒⟨ transport (cong₂ _<_ refl (sym (fst (dist z x (- y))))) ⟩ -- [XX]
                             0f <  z · (x - y) ◼) y·z<x·z
      instance _ = z · (x - y) # 0f ∋ inr i
      -- and so, being apart from 0, z (x − y) has a multiplicative inverse w.
      w   = (z · (x - y)) ⁻¹ᶠ
      ii  : 1f ≡ (z · (x - y)) · w
      ii  = sym (·-rinv _ _)
      -- Hence z itself has a multiplicative inverse w (x − y),
      iii : 1f ≡ z · ((x - y) · w)
      iii = transport (λ i → 1f ≡ ·-assoc z (x - y) w (~ i)) ii
      instance z#0f = z # 0f ∋ fst (·-inv-back _ _ (sym iii))
      -- and so 0 < z ∨ z < 0, where the latter case contradicts the assumption 0 ≤ z, so that we have 0 < z.
      instance _    = 0f < z ∋ case z#0f of λ where
                      (inl z<0) → ⊥-elim (0≤z z<0)
                      (inr 0<z) → 0<z
      -- Now w (x − y) has multiplicative inverse z, so it is apart from 0,
      iv  :  (x - y) · w # 0f
      iv  = snd (·-inv-back _ _ (sym iii))
      -- that is (0 < w (x − y)) ∨ (w (x − y) < 0).
      in case iv of λ where
        -- By (∗), from 0 < w (x − y) and yz < xz we get yzw (x − y) < xzw (x − y), so y < x, contradicting our assumption that x ≤ y.
        (inr 0<[x-y]·w) → (
           y ·  z                   <  x ·  z                    ⇒⟨ ·-preserves-< _ _ _ 0<[x-y]·w ⟩
          (y ·  z) · ((x - y) · w)  < (x ·  z) · ((x - y) · w)   ⇒⟨ transport (λ i →
                                                                        (·-assoc y z ((x - y) · w)) (~ i)
                                                                      < (·-assoc x z ((x - y) · w)) (~ i)) ⟩
           y · (z  · ((x - y) · w)) <  x · (z  · ((x - y) · w))  ⇒⟨ transport (λ i →
                                                                       y · (iii (~ i)) < x · (iii (~ i))) ⟩
           y · 1f                   <  x · 1f                    ⇒⟨ transport (cong₂ _<_
                                                                      (fst (·-identity y)) (fst (·-identity x))) ⟩
           y                        <  x                         ⇒⟨ x≤y ⟩
          ⊥ ◼) y·z<x·z
        -- In the latter case, from (∗) we get zw (x − y) < 0, i.e.
        -- 1 < 0 which contradicts item 10, so that we have 0 < w (x − y).
        (inl p) → (
               (x - y) · w      < 0f     ⇒⟨ ·-preserves-< _ _ _ it ⟩
              ((x - y) · w) · z < 0f · z ⇒⟨ transport (cong₂ _<_ (·-comm _ _) (0-leftNullifies z)) ⟩
          z · ((x - y) · w)     < 0f     ⇒⟨ ( transport λ i → iii (~ i) < 0f) ⟩
                             1f < 0f     ⇒⟨ <-trans _ _ _  item-10 ⟩
                             0f < 0f     ⇒⟨ <-irrefl _ ⟩
          ⊥ ◼) p

    --  9. 0 < z ⇒ (x < y ⇔ x z < y z),
    item-9 : ∀ x y z → 0f < z → (x < y → x · z < y · z)
    item-9 = ·-preserves-<

    item-9-back : ∀ x y z → 0f < z → (x · z < y · z → x < y)
    -- For the other direction of item 9, assume 0 < z and xz < yz,
    item-9-back x y z 0<z x·z<y·z = let
      instance _ = (          x · z  <  y · z            ⇒⟨ +-preserves-< _ _ _ ⟩
                   (x · z) - (x · z) < (y · z) - (x · z) ⇒⟨ transport (cong₂ _<_ (+-rinv (x · z)) refl) ⟩
                                  0f < (y · z) - (x · z) ◼) x·z<y·z
               _ = (y · z) - (x · z) # 0f ∋ inr it
      -- so that yz − xz has a multiplicative inverse w,
      w = ((y · z) - (x · z)) ⁻¹ᶠ
      o = ( (y · z) - (   x  · z) ≡⟨ ( λ i → (y · z) + (-commutesWithLeft-· x z) (~ i)) ⟩
            (y · z) + ((- x) · z) ≡⟨ sym (snd (dist y (- x) z)) ⟩
            (y - x) · z ∎)
      instance _ = (y - x) · z # 0f ∋  transport (λ i → o i # 0f) it
      -- and so z itself has multiplicative inverse w (y − x).
      1≡z·[w·[y-x]] = γ
      iii = 1≡z·[w·[y-x]]
      1≡[w·[y-x]]·z : 1f ≡ (w · (y - x)) · z
      1≡[w·[y-x]]·z = transport (λ i → 1f ≡ ·-comm z (w · (y - x)) i) 1≡z·[w·[y-x]]
      -- Then since 0 < z and xz < yz, by (∗), we get xzw (y − x) < yzw (y − x), and hence x < y.
      instance _ = z # 0f ∋ inr 0<z
      z⁻¹ = w · (y - x)
      z⁻¹≡w·[y-x] : z ⁻¹ᶠ ≡ (w · (y - x))
      z⁻¹≡w·[y-x] = let tmp = sym (snd (·-linv-unique (w · (y - x)) z (sym 1≡[w·[y-x]]·z)))
                    in transport (cong (λ z#0 → _⁻¹ᶠ z {{z#0}} ≡ (w · (y - x))) (#-isProp z 0f _ _)) tmp
      0<z⁻¹ : 0f < z ⁻¹ᶠ
      0<z⁻¹ = ⁻¹ᶠ-preserves-sign z 0<z
      instance _ = 0f < w · (y - x) ∋ transport (λ i → 0f < z⁻¹≡w·[y-x] i) 0<z⁻¹
      -- instance _ = 0f < z⁻¹ ∋ ?
      in (  x ·  z         <  y ·  z         ⇒⟨ ·-preserves-< _ _ z⁻¹ it ⟩
           (x ·  z) · z⁻¹  < (y ·  z) · z⁻¹  ⇒⟨ transport (λ i → ·-assoc x z z⁻¹ (~ i) < ·-assoc y z z⁻¹ (~ i)) ⟩
            x · (z  · z⁻¹) <  y · (z  · z⁻¹) ⇒⟨ transport (λ i → x · iii (~ i) < y · iii (~ i)) ⟩
            x · 1f         <  y · 1f         ⇒⟨ transport (cong₂ _<_ (fst (·-identity x)) (fst (·-identity y))) ⟩
            x              <  y              ◼) x·z<y·z
      where
        abstract -- NOTE: `abstract` is only allowed in `where` blocks and `where` blocks are not allowed in `let` blocks
          γ =
            let -- NOTE: for some reason the instance resolution does only work in let-blocks
            -- I get a "Terms marked as eligible for instance search should end with a name, so `instance' is ignored here. when checking the definition of my-instance"
            instance my-instance = (          x · z  <  y · z            ⇒⟨ +-preserves-< _ _ _ ⟩
                         (x · z) - (x · z) < (y · z) - (x · z) ⇒⟨ transport (cong₂ _<_ (+-rinv (x · z)) refl) ⟩
                                        0f < (y · z) - (x · z) ◼) x·z<y·z
                     _ = (y · z) - (x · z) # 0f ∋ inr it
            -- so that yz − xz has a multiplicative inverse w,
            w = ((y · z) - (x · z)) ⁻¹ᶠ
            o = ( (y · z) - (   x  · z) ≡⟨ ( λ i → (y · z) + (-commutesWithLeft-· x z) (~ i)) ⟩
                  (y · z) + ((- x) · z) ≡⟨ sym (snd (dist y (- x) z)) ⟩
                  (y - x) · z ∎)
            instance _ = (y - x) · z # 0f ∋  transport (λ i → o i # 0f) it
            in (
              1f                      ≡⟨ (λ i → ·-linv ((y · z) - (x · z)) it (~ i)) ⟩
              w · ((y · z) - (x · z)) ≡⟨ (λ i → w · o i) ⟩
              w · ((y - x) · z)       ≡⟨ (λ i → w · ·-comm (y - x) z i ) ⟩
              w · (z · (y - x))       ≡⟨ (λ i → ·-assoc w z (y - x) i) ⟩
              (w · z) · (y - x)       ≡⟨ (λ i → ·-comm w z i · (y - x)) ⟩
              (z · w) · (y - x)       ≡⟨ (λ i → ·-assoc z w (y - x) (~ i)) ⟩
              z · (w · (y - x))       ∎)

    -- 10. 0 < 1.
    item-10 with snd (·-inv-back _ _ (fst (·-identity 1f)))
    -- For item 10, since 1 has multiplicative inverse 1, it is apart from 0, hence 0 < 1 ∨ 1 < 0.
    ... | inl 1<0 =
      -- If 1 < 0 then by item 4 we have 0 < −1 and so by (∗) we get 0 < (−1) · (−1), that is, 0 < 1, so by transitivity 1 < 1, contradicting irreflexivity of <.
       (1f          < 0f                ⇒⟨ +-preserves-< 1f 0f (- 1f) ⟩
        1f    - 1f  < 0f - 1f           ⇒⟨ transport (λ i → +-rinv 1f i < snd (+-identity (- 1f)) i) ⟩
        0f          <    - 1f           ⇒⟨ ( λ 0<-1 → ·-preserves-< 0f (- 1f) (- 1f) 0<-1 0<-1) ⟩
        0f · (- 1f) <   (- 1f) · (- 1f) ⇒⟨ transport (cong₂ _<_ (0-leftNullifies (- 1f)) refl) ⟩
        0f          <   (- 1f) · (- 1f) ⇒⟨ transport (λ i → 0f < -commutesWithRight-· (- 1f) (1f)   i ) ⟩
        0f          < -((- 1f) ·    1f )⇒⟨ transport (λ i → 0f < -commutesWithLeft-·  (- 1f)  1f (~ i)) ⟩
        0f          < (-(- 1f))·    1f  ⇒⟨ transport (λ i → 0f < -involutive 1f i · 1f) ⟩
        0f          <      1f  ·    1f  ⇒⟨ transport (λ i → 0f < fst (·-identity 1f) i) ⟩
        0f          <      1f           ⇒⟨ <-trans _ _ _ 1<0 ⟩
        1f          <      1f           ⇒⟨ <-irrefl 1f ⟩
                    ⊥                   ⇒⟨ ⊥-elim ⟩ _ ◼) 1<0
    ... | inr 0<1 = 0<1

-- Conversely, assume the 10 listed items—in particular, items 4, 5 and 9.
module back -- 1. 2. 3. 4. 5. ⇒ 6.
  -- (item-1      : ∀ x y → x ≤ y → ¬(y < x))
  -- (item-1-back : ∀ x y → ¬(y < x) → x ≤ y)
  -- (item-2      : ∀ x y → x # y → (x < y) ⊎ (y < x))
  -- (item-2-back : ∀ x y → (x < y) ⊎ (y < x) → x # y)
  -- (item-3      : ∀ x y z → x ≤ y → x + z ≤ y + z)
  -- (item-3-back : ∀ x y z → x + z ≤ y + z → x ≤ y)
     (item-4      : ∀ x y z → x < y → x + z < y + z)
  -- (item-4-back : ∀ x y z → x + z < y + z → x < y)
     (item-5      : ∀ x y → 0f < x + y → (0f < x) ⊎ (0f < y))
  -- (item-6      : ∀ x y z → x < y → y ≤ z → x < z)
  -- (item-7      : ∀ x y z → x ≤ y → y < z → x < z)
  -- (item-8      : ∀ x y z → x ≤ y → 0f ≤ z → x · z ≤ y · z)
     (item-9      : ∀ x y z → 0f < z → (x < y → x · z < y · z))
  -- (item-9-back : ∀ x y z → 0f < z → (x · z < y · z → x < y))
  -- (item-10     : 0f < 1f)
  where

  item-4' : ∀ x y → 0f < x - y → y < x
  item-4' x y = (
    0f     <  x - y         ⇒⟨ item-4 0f (x + (- y)) y ⟩
    0f + y < (x - y) + y    ⇒⟨ transport (λ i → snd (+-identity y) i < sym (+-assoc x (- y) y) i) ⟩
         y <  x + (- y + y) ⇒⟨ transport (λ i → y < x + snd (+-inv y) i) ⟩
         y <  x + 0f        ⇒⟨ transport (λ i → y < fst (+-identity x) i)  ⟩
         y <  x ◼)

  lemma : ∀ x y z w → (z +   w) + ((- x)  + (- y)) ≡ (z - x) + (w - y)
  lemma x y z w = (
    -- NOTE: there has to be a shorter way to to this kind of calculations ...
    --       also I got not much introspection while creating the paths
    (z +   w) + ((- x)  + (- y))   ≡⟨ ( λ i → +-assoc z w ((- x)  + (- y)) (~ i)) ⟩
    (z + ( w  + ((- x)  + (- y)))) ≡⟨ ( λ i → z + (+-assoc w (- x) (- y) i)) ⟩
    (z + ((w  +  (- x)) + (- y)))  ≡⟨ ( λ i → z + ((+-comm w (- x) i) + (- y)) ) ⟩
    (z + (((- x) +  w)  + (- y)))  ≡⟨ ( λ i → z + (+-assoc (- x) w (- y) (~ i))) ⟩
    (z + (( - x) + (w - y)))       ≡⟨ ( λ i → +-assoc z (- x) (w  - y) i ) ⟩
    (z - x) + (w - y) ∎)

  -- 6. (†)
  -- In order to show (†), suppose x + y < z + w.
  -- So, by item 4, we get (x + y) − (x + y) < (z + w) − (x + y), that is, 0 < (z − x) + (w − y).
  -- By item 5, (0 < z − x) ∨ (0 < w − y), and so by item 4 in either case, we get x < z ∨ y < w.
  +-<-extensional : ∀ w x y z → (x + y) < (z + w) → (x < z) ⊎ (y < w)
  +-<-extensional w x y z = (
    (x + y)           < (z + w)           ⇒⟨ item-4 (x + y) (z + w) (- (x + y)) ⟩
    (x + y) - (x + y) < (z + w) - (x + y)
      ⇒⟨ transport (λ i → +-rinv (x + y) i < (z + w) + (-isDistributive x y) (~ i))    ⟩

                   0f < (z +   w) + ((- x)  + (- y))   ⇒⟨ transport (λ i → 0f < lemma x y z w i) ⟩
                   0f < (z - x) + (w - y) ⇒⟨ item-5 (z - x) (w - y) ⟩
    (0f < z - x) ⊎ (0f < w - y)           ⇒⟨ (λ{ (inl p) → inl (item-4' z x p)
                                               ; (inr p) → inr (item-4' w y p)}) ⟩
    ( x < z    ) ⊎ ( y < w    ) ◼)

  -- 6. (∗)
  ·-preserves-< : ∀ x y z → 0f < z → x < y → (x · z) < (y · z)
  ·-preserves-< = item-9


module Categories.Initial where

open import Library
open import Categories
open import Categories.Sets
open Cat

record Init {a b} (C : Cat {a}{b})(I : Obj C) : Set (a ⊔ b) where
  constructor init
  field i : ∀{X} → Hom C I X
        law : ∀{X}{f : Hom C I X} → i {X} ≅ f

ZeroSet : Init Sets ⊥
ZeroSet = record {i = λ(); law = ext λ()}


Finite : Domain → Formula
Finite(s) = ∃ₛ(ℕ)(n ↦ s ≼ 𝕟(n)) -- TODO: Now this means that there is an injection (s → 𝕟(n)), which is equivalent to the existance of an surjection (𝕟(n) → s) because stuff that follows from excluded middle (more specifically ((s ≼ 𝕟(n)) ↔ (𝕟(n) ≽ s))). Define ∑ (summation over finite sets) by using the existance of a finite sequence

-- reduce : (_▫_ : Domain → Domain → Domain) → ⦃ _ : Commutativity(_▫_) ⦄ → ⦃ _ : Associativity(_▫_) ⦄ → ⦃ _ : Identity(_▫_) ⦄ → (s : Domain) → ⦃ _ : Finite(s) ⦄ → Domain
-- reduce₀ : (_▫_ : Domain → Domain → Domain) → ⦃ _ : Commutativity(_▫_) ⦄ → ⦃ _ : Associativity(_▫_) ⦄ → (s : Domain) → ⦃ _ : Finite(s) ⦄ → Domain → Domain


module TermUnification where

open import OscarPrelude
open import Delay
open import VariableName
open import FunctionName
open import Arity
open import Vector
open import Term

mutual
  substituteTerm⇑ : VariableName → Term → ∀ {i} → Term → Delay i Term
  substituteTerm⇑ 𝑥ₛ τₛ τ@(variable 𝑥)  = now $ ifYes 𝑥ₛ ≟ 𝑥 then τₛ else τ
  substituteTerm⇑ 𝑥ₛ τₛ (function 𝑓 τs) =
    substituteTerms⇑ 𝑥ₛ τₛ τs >>= λ τsₛ →
    now $ function 𝑓 τsₛ

  substituteTerms⇑ : VariableName → Term → ∀ {i} → Terms → Delay i Terms
  substituteTerms⇑ 𝑥ₛ τₛ ⟨ ⟨ [] ⟩ ⟩ = now ⟨ ⟨ [] ⟩ ⟩
  substituteTerms⇑ 𝑥ₛ τₛ ⟨ ⟨ τ ∷ τs ⟩ ⟩ =
    let τs = substituteTerms⇑ 𝑥ₛ τₛ ⟨ ⟨ τs ⟩ ⟩
        τ = substituteTerm⇑ 𝑥ₛ τₛ τ in
    τs >>= λ { ⟨ ⟨ τs ⟩ ⟩ →
    τ >>= λ { τ →
    now $ ⟨ ⟨ τ ∷ τs ⟩ ⟩ } }

substituteTerms⇓ : (𝑥ₛ : VariableName) → (τₛ : Term) → (τs : Terms) → substituteTerms⇑ 𝑥ₛ τₛ τs ⇓
substituteTerms⇓ 𝑥ₛ τₛ ⟨ ⟨ [] ⟩ ⟩ = _ , now⇓
substituteTerms⇓ 𝑥ₛ τₛ ⟨ ⟨ (variable 𝑥) ∷ τs ⟩ ⟩ = _ , substituteTerms⇓ 𝑥ₛ τₛ ⟨ ⟨ τs ⟩ ⟩ ⇓>>=⇓ now⇓
substituteTerms⇓ 𝑥ₛ τₛ ⟨ ⟨ (function 𝑓 τs₁) ∷ τs ⟩ ⟩ = _ , substituteTerms⇓ 𝑥ₛ τₛ ⟨ ⟨ τs ⟩ ⟩ ⇓>>=⇓ ((substituteTerms⇓ 𝑥ₛ τₛ τs₁ ⇓>>=⇓ now⇓) >>=⇓ now⇓)

substituteTerm⇓ : (𝑥ₛ : VariableName) → (τₛ : Term) → (τ : Term) → substituteTerm⇑ 𝑥ₛ τₛ τ ⇓
substituteTerm⇓ 𝑥ₛ τₛ (variable 𝑥) = _ , now⇓
substituteTerm⇓ 𝑥ₛ τₛ (function 𝑓 τs) = _ , substituteTerms⇓ 𝑥ₛ τₛ τs ⇓>>=⇓ now⇓

substitute : VariableName → Term → Term → Term
substitute 𝑥ₛ τₛ τ = fst $ substituteTerm⇓ 𝑥ₛ τₛ τ

data AListOfSubstitutions : Set
 where
  [] : AListOfSubstitutions
  _∷_ : VariableName × Term → AListOfSubstitutions → AListOfSubstitutions

substituteA : AListOfSubstitutions → Term → Term
substituteA [] τ = τ
substituteA ((𝑥ₛ , τₛ) ∷ as) τ = substituteA as (substitute 𝑥ₛ τₛ τ)

record Unifier (τ₁ τ₂ : Term) : Set
 where
  field
    u₁ u₂ : AListOfSubstitutions
    unifier-law : substituteA u₁ τ₁ ≡ substituteA u₂ τ₂

unify? : (τ₁ τ₂ : Term) → Dec $ Unifier τ₁ τ₂
unify? (variable x) (variable x₁) = {!!}
unify? (variable x) (function x₁ x₂) = {!!}
unify? (function x x₁) (variable x₂) = {!!}
unify? (function 𝑓₁ x₁) (function 𝑓₂ x₃) with 𝑓₁ ≟ 𝑓₂
unify? (function 𝑓₁ x₁) (function .𝑓₁ x₃) | yes refl = {!!}
unify? (function 𝑓₁ x₁) (function 𝑓₂ x₃) | no 𝑓₁≢𝑓₂ = no (λ { record { u₁ = u₁ ; u₂ = u₂ ; unifier-law = unifier-law } → {!!}})


{-# OPTIONS --safe #-}
module Cubical.Algebra.DirectSum.DirectSumFun.Base where

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

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

open import Cubical.HITs.PropositionalTruncation as PT

open import Cubical.Algebra.AbGroup

private variable
  ℓ : Level

open AbGroupStr

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

AlmostNullProof : (G : ℕ → Type ℓ) → (Gstr : (n : ℕ) → AbGroupStr (G n))
                  → (f : (n : ℕ) → G n) → (k : ℕ) → Type ℓ
AlmostNullProof G Gstr f k = (n : ℕ) → k < n → f n ≡ 0g (Gstr n)

AlmostNull : (G : ℕ → Type ℓ) → (Gstr : (n : ℕ) → AbGroupStr (G n))
             → (f : (n : ℕ) → G n) → Type ℓ
AlmostNull G Gstr f = Σ[ k ∈ ℕ ] AlmostNullProof G Gstr f k

AlmostNullP : (G : ℕ → Type ℓ) → (Gstr : (n : ℕ) → AbGroupStr (G n))
             → (f : (n : ℕ) → G n) → Type ℓ
AlmostNullP G Gstr f = ∥ (AlmostNull G Gstr f) ∥₁

⊕Fun : (G : ℕ → Type ℓ) → (Gstr : (n : ℕ) → AbGroupStr (G n)) → Type ℓ
⊕Fun G Gstr = Σ[ f ∈ ((n : ℕ) → G n) ] AlmostNullP G Gstr f


-- Copyright 2019, Xuanrui Qi
-- Original algorithm by: Jacques Garrigue, Xuanrui Qi & Kazunari Tanaka
open import Data.Nat hiding (compare)
open import Data.Nat.Properties
open import Relation.Binary
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; subst; sym; inspect; [_])
open Eq.≡-Reasoning
open import Data.Product
open import Data.Empty using (⊥; ⊥-elim)
open import Data.Unit using (⊤; tt)

module RedBlack
  {c r} {A : Set c} {_<_ : Rel A r}
  (isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_)
  where

  open IsStrictTotalOrder isStrictTotalOrder

  data Color : Set where
    Red : Color
    Black : Color

  incr-Black : ℕ → Color → ℕ
  incr-Black d Black = suc d
  incr-Black d Red   = d

  color-valid : Color → Color → Set
  color-valid Red Red = ⊥
  color-valid _ _ = ⊤

  inv : Color → Color
  inv Red = Black
  inv Black = Red

  valid-*-Black : ∀ c → color-valid c Black
  valid-*-Black Red = tt
  valid-*-Black Black = tt

  data Tree : ℕ → Color → Set c where
    Leaf : Tree 0 Black
    Node : ∀ {d cₗ cᵣ} c → color-valid c cₗ → color-valid c cᵣ →
           Tree d cₗ → A → Tree d cᵣ → Tree (incr-Black d c) c

  RNode : ∀ {d} → Tree d Black → A → Tree d Black → Tree d Red
  RNode = Node Red tt tt

  BNode : ∀ {d cₗ cᵣ} → Tree d cₗ → A → Tree d cᵣ → Tree (suc d) Black
  BNode = Node Black tt tt

  -- Insertion algorithm
  data InsTree : ℕ → Color → Set c where
    Fix : ∀ {d} → Tree d Black → A → Tree d Black → A → Tree d Black → InsTree d Red
    T : ∀ {d c} cₚ → Tree d c → InsTree d cₚ

  InsTree-color : ∀ {d c} → InsTree d c → Color
  InsTree-color (Fix _ _ _ _ _) = Red
  InsTree-color (T _ _) = Black

  fix-color : ∀ {d c} → InsTree d c → Color
  fix-color (Fix _ _ _ _ _) = Black
  fix-color (T {c = c} _ _) = c

  fix-InsTree : ∀ {d c} → (t : InsTree d c) → Tree (incr-Black d (inv (InsTree-color t))) (fix-color t)
  fix-InsTree (Fix t₁ x t₂ y t₃) = BNode (RNode t₁ x t₂) y t₃
  fix-InsTree (T _ t) = t

  balanceₗ : ∀ {d cₗ cᵣ} c → (l : InsTree d cₗ) → A → Tree d cᵣ → color-valid c (InsTree-color l) → color-valid c cᵣ → InsTree (incr-Black d c) c
  balanceₗ Black (Fix t₁ x t₂ y t₃) z t₄ _ _ = T Black (RNode (BNode t₁ x t₂) y (BNode t₃ z t₄))
  balanceₗ Black (T _ l) v r _ _ = T Black (BNode l v r)
  balanceₗ {cᵣ = Black} Red (T _ (Node {cₗ = Black} {cᵣ = Black} Red _ _ t₁ x t₂)) y t₃ _ _ = Fix t₁ x t₂ y t₃
  balanceₗ {cᵣ = Black} Red (T {c = Black} _ l) v r _ _ = T Red (RNode l v r)

  balanceᵣ : ∀ {d cₗ cᵣ} c → Tree d cₗ → A → (r : InsTree d cᵣ) → color-valid c cₗ → color-valid c (InsTree-color r) → InsTree (incr-Black d c) c
  balanceᵣ Black t₁ x (Fix t₂ y t₃ z t₄) _ _ = T Black (RNode (BNode t₁ x t₂) y (BNode t₃ z t₄))
  balanceᵣ Black l v (T _ r) _ _ = T Black (BNode l v r)
  balanceᵣ {cₗ = Black} Red t₁ x (T _ (Node {cₗ = Black} {cᵣ = Black} Red _ _ t₂ y t₃)) _ _ = Fix t₁ x t₂ y t₃
  balanceᵣ {cₗ = Black} Red l v (T {c = Black} _ r) _ _ = T Red (RNode l v r)

  ins : ∀ {d c} → A → Tree d c → InsTree d c
  ins x Leaf = T Black (RNode Leaf x Leaf)
  ins x (Node c _ _ l v r) with (compare x v)
  ins x (Node c _ _ l v r)    | tri< x<v _ _ with ins x l | inspect (ins x) l
  ins x (Node Black _ validᵣ l v r) | _ | Fix _ _ _ _ _   | [ insₗ ] = balanceₗ Black (ins x l) v r tt tt
  ins x (Node c     _ validᵣ l v r) | _ | T _ _           | [ insₗ ] = balanceₗ c (ins x l) v r validₗ validᵣ
    where
      validₗ = subst (λ t → color-valid c (InsTree-color t)) (sym insₗ) (valid-*-Black c)
  ins x t@(Node c _ _ l v r)  | tri≈ _ x≡v _ = T _ t
  ins x (Node c _ _ l v r)    | tri> _ _ x>v with ins x r | inspect (ins x) r
  ins x (Node Black validₗ _ l v r) | _ | Fix _ _ _ _ _   | [ insᵣ ] = balanceᵣ Black l v (ins x r) tt tt
  ins x (Node c     validₗ _ l v r) | _ | T _ _           | [ insᵣ ] = balanceᵣ c l v (ins x r) validₗ validᵣ
    where
      validᵣ = subst (λ t → color-valid c (InsTree-color t)) (sym insᵣ) (valid-*-Black c)

  blacken : ∀ {d c} → Tree d c → Tree (incr-Black d (inv c)) Black
  blacken Leaf = Leaf
  blacken (Node Black _ _ l v r) = BNode l v r
  blacken (Node Red _ _ l v r) = BNode l v r

  insert : ∀ {d} → A → Tree d Black → ∃ (λ d' → Tree d' Black)
  insert {d = d} x t with ins x t
  ... | T {c = c} _ t' = (incr-Black d (inv c) , blacken t')

  -- Deletion algorithm
  data DelTree : ℕ → Color → Set c where
    Stay : ∀ {d c} cₚ → color-valid c (inv cₚ) → Tree d c → DelTree d cₚ
    Down : ∀ {d} → Tree d Black → DelTree (suc d) Black

  bal-right : ∀ {d cₗ cᵣ} c → Tree d cₗ → A → DelTree d cᵣ → color-valid c cₗ → color-valid c cᵣ → DelTree (incr-Black d c) c
  bal-right {cₗ = Black} Red l v (Stay {c = Black} _ _ r) _ _ = Stay Red tt (RNode l v r)
  bal-right {cₗ = Black} Red l v (Stay {c = Red} Red _ _) _ ()
  bal-right Red (Node {cᵣ = Black} Black _ _ t₁ x t₂) y (Down t₃) _ _ = Stay Red tt (BNode t₁ x (RNode t₂ y t₃))
  bal-right Red (Node {cᵣ = Red} Black _ _ t₁ x (Node _ _ _ t₂ y t₃)) z (Down t₄) _ _ = Stay Red tt (RNode (BNode t₁ x t₂) y (BNode t₃ z t₄))
  bal-right Black l v (Stay _ _ r) _ _ = Stay Black tt (BNode l v r)
  bal-right Black (Node {cᵣ = Red} Black _ _ t₁ x (Node _ _ _ t₂ y t₃)) z (Down t₄) _ _ = Stay Black tt (BNode (BNode t₁ x t₂) y (BNode t₃ z t₄))
  bal-right Black (Node {cₗ = Black} {cᵣ = Black} Red _ _ t₁ x (Node {cᵣ = Black} _ _ _ t₂ y t₃)) z (Down t₄) _ _ =
    Stay Black tt (BNode t₁ x (BNode t₂ y (RNode t₃ z t₄)))
  bal-right Black (Node {cₗ = Black} {cᵣ = Black} Red _ _ t₁ x (Node {cᵣ = Red} _ _ _ t₂ y (Node {cₗ = Black} {cᵣ = Black} _ _ _ t₃ z t₄))) w (Down t₅) _ _ =
    Stay Black tt (BNode t₁ x (RNode (BNode t₂ y t₃) z (BNode t₄ w t₅)))
  bal-right Black (Node {cᵣ = Black} Black _ _ t₁ x t₂) y (Down t₃) _ _ = Down (BNode t₁ x (RNode t₂ y t₃))

  bal-left : ∀ {d cₗ cᵣ} c → DelTree d cₗ → A → Tree d cᵣ → color-valid c cₗ → color-valid c cᵣ → DelTree (incr-Black d c) c
  bal-left {cₗ = Black} {cᵣ = Black} Red (Stay {c = Black} _ _ l) v r _ _ = Stay Red tt (RNode l v r)
  bal-left {cₗ = Black} {cᵣ = Black} Red (Stay {c = Red} _ () l) v r _ _
  bal-left {cₗ = Black} {cᵣ = Black} Red (Down t₁) x (Node {cₗ = Red} _ _ _ (Node {cₗ = Black} {cᵣ = Black} _ _ _ t₂ y t₃) z t₄) _ _ =
    Stay Red tt (RNode (BNode t₁ x t₂) y (BNode t₃ z t₄))
  bal-left {cₗ = Black} {cᵣ = Black} Red (Down t₁) x (Node {cₗ = Black} _ _ _ t₂ y t₃) _ _ = Stay Red tt (BNode (RNode t₁ x t₂) y t₃)
  bal-left Black (Stay _ _ l) v r _ _ = Stay Black tt (BNode l v r)
  bal-left {cᵣ = Black} Black (Down t₁) x (Node {cₗ = Red} Black _ _ (Node {cₗ = Black} {cᵣ = Black} _ _ _ t₂ y t₃) z t₄) _ _ =
    Stay Black tt (BNode (BNode t₁ x t₂) y (BNode t₃ z t₄))
  bal-left {cᵣ = Red} Black (Down t₁) x (Node {cₗ = Black} {cᵣ = Black} _ _ _ (Node {cₗ = Red} Black _ _ (Node {cₗ = Black} {cᵣ = Black} _ _ _ t₂ y t₃) z t₄) w t₅) _ _ =
    Stay Black tt (BNode (BNode t₁ x t₂) y (RNode (BNode t₃ z t₄) w t₅))
  bal-left {cᵣ = Red} Black (Down t₁) x (Node {cₗ = Black} {cᵣ = Black} _ _ _ (Node {cₗ = Black} _ _ _ t₂ y t₃) z t₄) _ _ =
    Stay Black tt (BNode (BNode (RNode t₁ x t₂) y t₃) z t₄)
  bal-left {cᵣ = Black} Black (Down t₁) x (Node {cₗ = Black} _ _ _ t₂ y t₃) _ _ = Down (BNode (RNode t₁ x t₂) y t₃)


module 14-implicitConfigurations where

postulate
  Integral : Set → Set
  add : ∀ {A} {{ intA : Integral A }} → A → A → A
  mul : ∀ {A} {{ intA : Integral A }} → A → A → A
  mod : ∀ {A} {{ intA : Integral A }} → A → A → A

  N : Set
  zero one two three : N
  nInt : Integral N

private postulate Token : Set

record Modulus (s : Token) (A : Set) : Set where
  field modulus : A

data M (s : Token) (A : Set) : Set where
  MkM : A → M s A

unMkM : ∀ {A s} → M s A → A
unMkM (MkM a) = a

private postulate theOnlyToken : Token

withModulus :
  ∀ {A} → {{ intA : Integral A }} → (modulus : A) →
  (∀ {s} → {{ mod : Modulus s A }} → M s A) → A
withModulus modulus f = unMkM
  (f {theOnlyToken} {{  record { modulus = modulus }  }})

open Modulus {{...}}

normalize : ∀ {s A} {{intA : Integral A}} {{mod : Modulus s A}} →
            A → M s A
normalize a = MkM (mod modulus a)

_+_ : ∀ {s A} {{intA : Integral A}} {{mod : Modulus s A}} →
      M s A → M s A → M s A
(MkM a) + (MkM b) = normalize (add a b)

_*_ : ∀ {s A} → {{intA : Integral A}} → {{mod : Modulus s A}} →
      M s A → M s A → M s A
(MkM a) * (MkM b) = normalize (mul a b)

test₁ : N
test₁ = withModulus two (let o = MkM one in (o + o)*(o + o))

testExpr : ∀ {s} → {{mod : Modulus s N}} → M s N
testExpr = let o = MkM one ; t = MkM two in
           (o + t) * t

test₂ : N
test₂ = withModulus three testExpr


{-# OPTIONS --safe #-}

module Cubical.Relation.Binary.Extensionality where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Transport
open import Cubical.Data.Sigma
open import Cubical.Relation.Binary.Base

module _ {ℓ ℓ'} {A : Type ℓ} (_≺_ : Rel A A ℓ') where

  ≡→≺Equiv : (x y : A) → x ≡ y → ∀ z → (z ≺ x) ≃ (z ≺ y)
  ≡→≺Equiv _ _ p z = substEquiv (z ≺_) p

  isWeaklyExtensional : Type _
  isWeaklyExtensional = ∀ {x y} → isEquiv (≡→≺Equiv x y)

  isPropIsWeaklyExtensional : isProp isWeaklyExtensional
  isPropIsWeaklyExtensional = isPropImplicitΠ2 λ _ _ → isPropIsEquiv _


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

open import HoTT

module homotopy.SuspAdjointLoop where

module SuspAdjointLoop {i j} (X : Ptd i) (Y : Ptd j) where

  private
    A = fst X; a₀ = snd X
    B = fst Y; b₀ = snd Y

  R : {b : B}
    → Σ (Suspension A → B) (λ h → h (north A) == b)
    → Σ (A → (b == b)) (λ k → k a₀ == idp)
  R (h , idp) =
    (λ a → ap h (merid A a ∙ ! (merid A a₀))) ,
    ap (ap h) (!-inv-r (merid A a₀))

  L : {b : B}
    → Σ (A → (b == b)) (λ k → k a₀ == idp)
    → Σ (Suspension A → B) (λ h → h (north A) == b)
  L {b} (k , _) = (SuspensionRec.f A b b k) , idp

  {- Show that R ∘ L ∼ idf -}

  R-L : {b : B} → ∀ K → R {b} (L K) == K
  R-L {b} (k , kpt) = ⊙λ= R-L-fst R-L-snd
    where
    R-L-fst : (a : A)
      → ap (SuspensionRec.f A b b k) (merid A a ∙ ! (merid A a₀)) == k a
    R-L-fst a =
      ap-∙ (SuspensionRec.f A b b k) (merid A a) (! (merid A a₀))
      ∙ ap2 _∙_ (SuspensionRec.glue-β A b b k a)
                (ap-! (SuspensionRec.f A b b k) (merid A a₀)
                 ∙ ap ! (SuspensionRec.glue-β A b b k a₀ ∙ kpt))
      ∙ ∙-unit-r (k a)

    -- lemmas generalize to do some path induction for R-L-snd
    lemma₁ : ∀ {i j} {A : Type i} {B : Type j} (f : A → B) {a₁ a₂ : A}
      (p : a₁ == a₂) {q r : f a₁ == f a₂} {s : f a₁ == f a₁}
      (α : ap f p == q) (β : q == r) (γ : q ∙ ! r == s) (δ : s == idp)
      (σ : !-inv-r r == transport (λ t → t ∙ ! r == idp) β (γ ∙ δ))
      → ap (ap f) (!-inv-r p)
        == (ap-∙ f p (! p) ∙ ap2 _∙_ α (ap-! f p ∙ ap ! (α ∙ β)) ∙ γ) ∙ δ
    lemma₁ f idp idp idp γ idp σ = σ

    lemma₂ : ∀ {i} {A : Type i} {x : A} {p : x == x} (α : p == idp)
      → idp == transport (λ t → t ∙ idp == idp) α (∙-unit-r p ∙ α)
    lemma₂ idp = idp

    R-L-snd : ap (ap (SuspensionRec.f A b b k)) (!-inv-r (merid A a₀))
           == R-L-fst a₀ ∙ kpt
    R-L-snd =
      ap (ap (SuspensionRec.f A b b k)) (!-inv-r (merid A a₀))
        =⟨ lemma₁ (SuspensionRec.f A b b k) (merid A a₀)
             (SuspensionRec.glue-β A b b k a₀)
             kpt (∙-unit-r (k a₀)) kpt (lemma₂ kpt) ⟩
      R-L-fst a₀ ∙ kpt ∎

  {- Show that L ∘ R ∼ idf -}

  L-R : {b : B} → ∀ H → L {b} (R H) == H
  L-R (h , idp) = ⊙λ= L-R-fst idp
    where
    fst-lemma : ∀ {i j} {A : Type i} {B : Type j} {x y z : A}
      (f : A → B) (p : x == y) (q : z == y)
      → ap f p == ap f (p ∙ ! q) ∙' ap f q
    fst-lemma _ idp idp = idp

    L-R-fst : (σ : Suspension A) →
      SuspensionRec.f A (h (north _)) (h (north _)) (fst (R (h , idp))) σ == h σ
    L-R-fst = Suspension-elim A
      idp
      (ap h (merid A a₀))
      (λ a → ↓-='-in $
        ap h (merid A a)
          =⟨ fst-lemma h (merid A a) (merid A a₀) ⟩
        ap h (merid A a ∙ ! (merid A a₀)) ∙' ap h (merid A a₀)
          =⟨ ! (SuspensionRec.glue-β A _ _ (fst (R (h , idp))) a)
             |in-ctx (λ w → w ∙' (ap h (merid A a₀))) ⟩
        ap (fst (L (R (h , idp)))) (merid A a) ∙' ap h (merid A a₀)
        ∎)

  {- Show that R respects basepoint -}

  pres-ident : {b : B}
    → R {b} ((λ _ → b) , idp) == ((λ _ → idp) , idp)
  pres-ident {b} = ⊙λ=
    (λ a → ap-cst b (merid A a ∙ ! (merid A a₀)))
    (ap (ap (λ _ → b)) (!-inv-r (merid A a₀))
       =⟨ lemma (merid A a₀) b ⟩
     ap-cst b (merid A a₀ ∙ ! (merid A a₀))
       =⟨ ! (∙-unit-r _) ⟩
     ap-cst b (merid A a₀ ∙ ! (merid A a₀)) ∙ idp ∎)
    where
    lemma : ∀ {i j} {A : Type i} {B : Type j} {x y : A} (p : x == y) (b : B)
      → ap (ap (λ _ → b)) (!-inv-r p) == ap-cst b (p ∙ ! p)
    lemma idp b = idp

  {- Show that if there is a composition operation ⊙ on B, then R respects
     that composition, that is R {b ⊙ c} (F ⊙ G) == R {b} F ∙ R {c} G -}

  -- lift a composition operation on the codomain to the function space
  comp-lift : ∀ {i j} {A : Type i} {B C D : Type j}
    (a : A) (b : B) (c : C) (_⊙_ : B → C → D)
    → Σ (A → B) (λ f → f a == b)
    → Σ (A → C) (λ g → g a == c)
    → Σ (A → D) (λ h → h a == b ⊙ c)
  comp-lift a b c _⊙_ (f , fpt) (g , gpt) =
    (λ x → f x ⊙ g x) , ap2 _⊙_ fpt gpt

  pres-comp-fst : ∀ {i j} {A : Type i} {B : Type j} (f g : A → B)
    (_⊙_ : B → B → B) {a₁ a₂ : A} (p : a₁ == a₂)
    → ap (λ x → f x ⊙ g x) p == ap2 _⊙_ (ap f p) (ap g p)
  pres-comp-fst f g _⊙_ idp = idp

  pres-comp-snd : ∀ {i j} {A : Type i} {B : Type j} (f g : A → B)
    (_⊙_ : B → B → B) {a₁ a₂ : A} (q : a₁ == a₂)
    → ap (ap (λ x → f x ⊙ g x)) (!-inv-r q)
      == pres-comp-fst f g _⊙_ (q ∙ ! q)
         ∙ ap2 (ap2 _⊙_) (ap (ap f) (!-inv-r q)) (ap (ap g) (!-inv-r q))
  pres-comp-snd f g _⊙_ idp = idp

  pres-comp : {b c : B} (_⊙_ : B → B → B)
    (F : Σ (Suspension A → B) (λ f → f (north A) == b))
    (G : Σ (Suspension A → B) (λ f → f (north A) == c))
    → R (comp-lift (north A) b c _⊙_ F G)
      == comp-lift a₀ idp idp (ap2 _⊙_) (R F) (R G)
  pres-comp _⊙_ (f , idp) (g , idp) = ⊙λ=
    (λ a → pres-comp-fst f g _⊙_ (merid A a ∙ ! (merid A a₀)))
    (pres-comp-snd f g _⊙_ (merid A a₀))

  eqv : fst (⊙Susp X ⊙→ Y) ≃ fst (X ⊙→ ⊙Ω Y)
  eqv = equiv R L R-L L-R

  ⊙path : (⊙Susp X ⊙→ Y) == (X ⊙→ ⊙Ω Y)
  ⊙path = ⊙ua eqv pres-ident


--
-- Created by Dependently-Typed Lambda Calculus on 2020-10-07
-- Isomorphism
-- Author: dplaindoux
--

{-# OPTIONS --without-K --no-sized-types --no-guardedness bbbbb#-}

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; cong; cong-app)
open Eq.≡-Reasoning
open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Nat.Properties using (+-comm)

module Isomorphism where

_∘_ : ∀ {A B C : Set} → (B → C) → (A → B) → (A → C)
(g ∘ f) = λ x → g (f x)

-- Extensionality and function equivalence

_+′_ : ℕ → ℕ → ℕ
m +′ zero  = m
m +′ suc n = suc (m +′ n)

same-app : ∀ (m n : ℕ) → m +′ n ≡ m + n
same-app m n rewrite +-comm m n = helper m n
  where
  helper : ∀ (m n : ℕ) → m +′ n ≡ n + m
  helper m zero    = refl
  helper m (suc n) = cong suc (helper m n)

postulate
  extensionality : ∀ {A B : Set} {f g : A → B}
    → (∀ (x : A) → f x ≡ g x)
      -----------------------
    → f ≡ g

postulate
  ∀-extensionality : ∀ {A : Set} {B : A → Set} {f g : ∀(x : A) → B x}
    → (∀ (x : A) → f x ≡ g x)
      -----------------------
    → f ≡ g

same : _+′_ ≡ _+_
same = ∀-extensionality (λ m → ∀-extensionality (λ n → same-app m n))

-- Isomorphism definition

infix 0 _≃_

record _≃_ (A B : Set) : Set where
  field
    to   : A → B
    from : B → A
    from∘to : ∀ (x : A) → from (to x) ≡ x
    to∘from : ∀ (y : B) → to (from y) ≡ y

open _≃_

≃-refl : ∀ {A : Set}
    -----
  → A ≃ A
≃-refl =
  record
    { to      = λ{x → x}
    ; from    = λ{y → y}
    ; from∘to = λ{x → refl}
    ; to∘from = λ{y → refl}
    }

≃-sym : ∀ {A B : Set}
  → A ≃ B
    -----
  → B ≃ A
≃-sym A≃B =
  record
    { to      = from A≃B
    ; from    = to   A≃B
    ; from∘to = to∘from A≃B
    ; to∘from = from∘to A≃B
    }

≃-trans : ∀ {A B C : Set}
  → A ≃ B
  → B ≃ C
    -----
  → A ≃ C
≃-trans A≃B B≃C =
  record
    { to       = to   B≃C ∘ to   A≃B
    ; from     = from A≃B ∘ from B≃C
    ; from∘to  = λ{x →
        begin
          ((from A≃B ∘ from B≃C) ∘ (to B≃C ∘ to A≃B)) x
        ≡⟨⟩
          (from A≃B ∘ from B≃C) ((to B≃C ∘ to A≃B) x)
        ≡⟨⟩
          from A≃B (from B≃C (to B≃C (to A≃B x)))
        ≡⟨ cong (from A≃B) (from∘to B≃C (to A≃B x)) ⟩
          from A≃B (to A≃B x)
        ≡⟨ from∘to A≃B x ⟩
          x
        ∎}
    ; to∘from = λ{y →
        begin
          ((to B≃C ∘ to A≃B) ∘ (from A≃B ∘ from B≃C)) y
        ≡⟨⟩
          (to B≃C ∘ to A≃B) ((from A≃B ∘ from B≃C) y)
        ≡⟨⟩
          to B≃C (to A≃B (from A≃B (from B≃C y)))
        ≡⟨ cong (to B≃C) (to∘from A≃B (from B≃C y)) ⟩
          to B≃C (from B≃C y)
        ≡⟨ to∘from B≃C y ⟩
          y
        ∎}
     }

module ≃-Reasoning where

  infix  1 ≃-begin_
  infixr 2 _≃⟨_⟩_
  infix  3 _≃-∎

  ≃-begin_ : ∀ {A B : Set}
    → A ≃ B
      -----
    → A ≃ B
  ≃-begin A≃B = A≃B

  _≃⟨_⟩_ : ∀ (A : Set) {B C : Set}
    → A ≃ B
    → B ≃ C
      -----
    → A ≃ C
  A ≃⟨ A≃B ⟩ B≃C = ≃-trans A≃B B≃C

  _≃-∎ : ∀ (A : Set)
      -----
    → A ≃ A
  A ≃-∎ = ≃-refl

open ≃-Reasoning

infix 0 _≲_

record _≲_ (A B : Set) : Set where
  field
    to      : A → B
    from    : B → A
    from∘to : ∀ (x : A) → from (to x) ≡ x

open _≲_

≲-refl : ∀ {A : Set}
    -----
  → A ≲ A
≲-refl =
  record
    { to      = λ{x → x}
    ; from    = λ{y → y}
    ; from∘to = λ{x → refl}
    }

≲-trans : ∀ {A B C : Set}
  → A ≲ B
  → B ≲ C
    -----
  → A ≲ C
≲-trans A≲B B≲C =
  record
    { to      = λ{x → to   B≲C (to   A≲B x)}
    ; from    = λ{y → from A≲B (from B≲C y)}
    ; from∘to = λ{x →
        begin
          ((from A≲B ∘ from B≲C) ∘ (to B≲C ∘ to A≲B)) x
        ≡⟨⟩
          (from A≲B ∘ from B≲C) ((to B≲C ∘ to A≲B) x)
        ≡⟨⟩
          from A≲B (from B≲C (to B≲C (to A≲B x)))
        ≡⟨ cong (from A≲B) (from∘to B≲C (to A≲B x)) ⟩
          from A≲B (to A≲B x)
        ≡⟨ from∘to A≲B x ⟩
          x
        ∎}
     }

≲-antisym : ∀ {A B : Set}
  → (A≲B : A ≲ B)
  → (B≲A : B ≲ A)
  → (to A≲B ≡ from B≲A)
  → (from A≲B ≡ to B≲A)
    -------------------
  → A ≃ B
≲-antisym A≲B B≲A to≡from from≡to =
  record
    { to      = to A≲B
    ; from    = from A≲B
    ; from∘to = from∘to A≲B
    ; to∘from = λ{y →
        begin
          (to A≲B ∘ from A≲B) y
        ≡⟨⟩
          to A≲B (from A≲B y)
        ≡⟨ cong (to A≲B) (cong-app from≡to y) ⟩
          to A≲B (to B≲A y)
        ≡⟨ cong-app to≡from (to B≲A y) ⟩
          from B≲A (to B≲A y)
        ≡⟨ from∘to B≲A y ⟩
          y
        ∎}
    }