{-

Computable stuff constructed from the Combinatorics of Finite Sets

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

module Cubical.Experiments.Combinatorics where

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

open import Cubical.Data.Nat
open import Cubical.Data.Nat.Order
open import Cubical.Data.Bool
open import Cubical.Data.Sum
open import Cubical.Data.Sigma
open import Cubical.Data.Vec

open import Cubical.Data.SumFin renaming (Fin to Fin')
open import Cubical.Data.FinSet.Base
open import Cubical.Data.FinSet.Properties
open import Cubical.Data.FinSet.Constructors
open import Cubical.Data.FinSet.Cardinality
open import Cubical.Data.FinSet.DecidablePredicate
open import Cubical.Data.FinSet.Quotients

open import Cubical.HITs.PropositionalTruncation

open import Cubical.Relation.Nullary
open import Cubical.Relation.Nullary.DecidablePropositions
  hiding (DecProp) renaming (DecProp' to DecProp)
open import Cubical.Relation.Binary

open import Cubical.Functions.Embedding
open import Cubical.Functions.Surjection

private
  variable
    ℓ ℓ' ℓ'' : Level

-- convenient renaming

Fin : ℕ → FinSet ℓ-zero
Fin n = _ , isFinSetFin {n = n}

-- explicit numbers

s2 : card (_ , isFinSet≃ (Fin 2) (Fin 2)) ≡ 2
s2 = refl

s3 : card (_ , isFinSet≃ (Fin 3) (Fin 3)) ≡ 6
s3 = refl

a3,2 : card (_ , isFinSet↪ (Fin 2) (Fin 3)) ≡ 6
a3,2 = refl

2^4 : card (_ , isFinSet→ (Fin 4) (Fin 2)) ≡ 16
2^4 = refl

-- construct numerical functions from list
getFun : {n : ℕ} → Vec ℕ n → Fin n .fst → ℕ
getFun {n = n} ns = fun n ns
  where
    fun : (n : ℕ) → Vec ℕ n → Fin' n → ℕ
    fun 0 _ _ = 0
    fun (suc m) (n ∷ ns) (inl tt) = n
    fun (suc m) (n ∷ ns) (inr x) = fun m ns x

-- an example function
f = getFun (3 ∷ 1 ∷ 4 ∷ 1 ∷ 5 ∷ 9 ∷ 2 ∷ 6 ∷ [])

-- the total sum
s : sum (Fin _) f ≡ 31
s = refl

-- the total product
p : prod (Fin _) f ≡ 6480
p = refl

-- the maximal value
m : maxValue (Fin _) f ∣ fzero ∣ ≡ 9
m = refl

-- the number of numeral 1
n1 : card (_ , isFinSetFiberDisc (Fin _) ℕ discreteℕ f 1) ≡ 2
n1 = refl

-- a somewhat trivial equivalence relation making everything equivalent
R : {n : ℕ} → Fin n .fst → Fin n .fst → Type
R _ _ = Unit

isDecR : {n : ℕ} → (x y : Fin n .fst) → isDecProp (R {n = n} x y)
isDecR _ _ = true , idEquiv _

open BinaryRelation
open isEquivRel

isEquivRelR : {n : ℕ} → isEquivRel (R {n = n})
isEquivRelR {n = n} .reflexive _ = tt
isEquivRelR {n = n} .symmetric _ _ tt = tt
isEquivRelR {n = n} .transitive _ _ _ tt tt = tt

collapsed : (n : ℕ) → FinSet ℓ-zero
collapsed n = _ , isFinSetQuot (Fin n) R isEquivRelR isDecR

-- this number should be 1
≡1 : card (collapsed 2) ≡ 1
≡1 = refl


{-# OPTIONS --no-pattern-matching #-}

id : {A : Set} (x : A) → A
id x = x

const : {A B : Set} (x : A) (y : B) → A
const x y = x

happ : {A B C : Set} (f : A → B → C) (g : A → B) (x : A) → C
happ f g x = f x (g x)

K = const
S = happ

I : {A : Set} (x : A) → A
I = S K K

-- Mmh, pretty boring...


------------------------------------------------------------------------------
-- The division program is correct
------------------------------------------------------------------------------

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

-- This module proves the correctness of the division program using
-- repeated subtraction (Dybjer 1985).

module FOTC.Program.Division.CorrectnessProofI where

open import FOTC.Base
open import FOTC.Data.Nat
open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.Inequalities.PropertiesI
open import FOTC.Data.Nat.PropertiesI

import FOTC.Data.Nat.Induction.NonAcc.WF-I
open module WFInd = FOTC.Data.Nat.Induction.NonAcc.WF-I.WFInd

open import FOTC.Program.Division.Division
open import FOTC.Program.Division.ResultI
open import FOTC.Program.Division.Specification
open import FOTC.Program.Division.TotalityI

------------------------------------------------------------------------------
-- The division program is correct when the dividend is less than the
-- divisor.
div-x<y-correct : ∀ {i j} → N i → N j → i < j → divSpec i j (div i j)
div-x<y-correct Ni Nj i<j = div-x<y-N i<j , div-x<y-resultCorrect Ni Nj i<j

-- The division program is correct when the dividend is greater or
-- equal than the divisor.
div-x≮y-correct : ∀ {i j} → N i → N j →
                  (∀ {i'} → N i' → i' < i → divSpec i' j (div i' j)) →
                  j > zero →
                  i ≮ j →
                  divSpec i j (div i j)
div-x≮y-correct {i} {j} Ni Nj ah j>0 i≮j =
  div-x≮y-N ih i≮j , div-x≮y-resultCorrect Ni Nj ih i≮j

  where
  -- The inductive hypothesis on i ∸ j.
  ih : divSpec (i ∸ j) j (div (i ∸ j) j)
  ih = ah {i ∸ j}
          (∸-N Ni Nj)
          (x≥y→y>0→x∸y<x Ni Nj (x≮y→x≥y Ni Nj i≮j) j>0)

------------------------------------------------------------------------------
-- The division program is correct.

-- We do the well-founded induction on i and we keep j fixed.
divCorrect : ∀ {i j} → N i → N j → j > zero → divSpec i j (div i j)
divCorrect {j = j} Ni Nj j>0 = <-wfind A ih Ni
  where
  A : D → Set
  A d = divSpec d j (div d j)

  -- The inductive step doesn't use the variable i (nor Ni). To make
  -- this clear we write down the inductive step using the variables m
  -- and n.
  ih : ∀ {n} → N n → (∀ {m} → N m → m < n → A m) → A n
  ih {n} Nn ah =
    case (div-x<y-correct Nn Nj) (div-x≮y-correct Nn Nj ah j>0) (x<y∨x≮y Nn Nj)

------------------------------------------------------------------------------
-- References
--
-- Dybjer, Peter (1985). Program Verification in a Logical Theory of
-- Constructions. In: Functional Programming Languages and Computer
-- Architecture. Ed. by Jouannaud,
-- Jean-Pierre. Vol. 201. LNCS. Appears in revised form as Programming
-- Methodology Group Report 26, University of Gothenburg and Chalmers
-- University of Technology, June 1986. Springer, pp. 334–349.


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

open import Level

record Category (o ℓ e : Level) : Set (suc (o ⊔ ℓ ⊔ e)) where
  eta-equality
  infix  4 _≈_ _⇒_
  infixr 9 _∘_

  field
    Obj : Set o
    _⇒_ : Obj → Obj → Set ℓ
    _≈_ : ∀ {A B} → (A ⇒ B) → (A ⇒ B) → Set e

    _∘_ : ∀ {A B C} → (B ⇒ C) → (A ⇒ B) → (A ⇒ C)

  CommutativeSquare : ∀ {A B C D} → (f : A ⇒ B) (g : A ⇒ C) (h : B ⇒ D) (i : C ⇒ D) → Set _
  CommutativeSquare f g h i = h ∘ f ≈ i ∘ g

infix 10  _[_,_]

_[_,_] : ∀ {o ℓ e} → (C : Category o ℓ e) → (X : Category.Obj C) → (Y : Category.Obj C) → Set ℓ
_[_,_] = Category._⇒_

module Inner {x₁ x₂ x₃} (CC : Category x₁ x₂ x₃) where

  open import Level
  private
    variable
      o ℓ e o′ ℓ′ e′ o″ ℓ″ e″ : Level

  open import Data.Product using (_×_; Σ; _,_; curry′; proj₁; proj₂; zip; map; <_,_>; swap)

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

  record Functor (C : Category o ℓ e) (D : Category o′ ℓ′ e′) : Set (o ⊔ ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) where
    eta-equality
    private module C = Category C
    private module D = Category D

    field
      F₀ : C.Obj → D.Obj
      F₁ : ∀ {A B} (f : C [ A , B ]) → D [ F₀ A , F₀ B ]

  Product : (C : Category o ℓ e) (D : Category o′ ℓ′ e′) → Category (o ⊔ o′) (ℓ ⊔ ℓ′) (e ⊔ e′)
  Product C D = record
    { Obj       = C.Obj × D.Obj
    ; _⇒_       = C._⇒_ -< _×_ >- D._⇒_
    ; _≈_       = C._≈_ -< _×_ >- D._≈_
    ; _∘_       = zip C._∘_ D._∘_
    }
    where module C = Category C
          module D = Category D

  Bifunctor : Category o ℓ e → Category o′ ℓ′ e′ → Category o″ ℓ″ e″ → Set _
  Bifunctor C D E = Functor (Product C D) E

  private
    module CC = Category CC

  open CC

  infix 4 _≅_
  record _≅_ (A B : Obj) : Set (x₂) where
    field
      from : A ⇒ B
      to   : B ⇒ A

  private
    variable
      X Y Z W : Obj
      f g h : X ⇒ Y

  record Monoidal : Set (x₁ ⊔ x₂ ⊔ x₃) where
    infixr 10 _⊗₀_ _⊗₁_

    field
      ⊗  : Bifunctor CC CC CC

    module ⊗ = Functor ⊗

    open Functor ⊗

    _⊗₀_ : Obj → Obj → Obj
    _⊗₀_ = curry′ F₀

    -- this is also 'curry', but a very-dependent version
    _⊗₁_ : X ⇒ Y → Z ⇒ W → X ⊗₀ Z ⇒ Y ⊗₀ W
    f ⊗₁ g = F₁ (f , g)

    field
      associator : (X ⊗₀ Y) ⊗₀ Z ≅ X ⊗₀ (Y ⊗₀ Z)

    module associator {X} {Y} {Z} = _≅_ (associator {X} {Y} {Z})

    -- for exporting, it makes sense to use the above long names, but for
    -- internal consumption, the traditional (short!) categorical names are more
    -- convenient. However, they are not symmetric, even though the concepts are, so
    -- we'll use ⇒ and ⇐ arrows to indicate that
    private
      α⇒ = associator.from
      α⇐ = λ {X} {Y} {Z} → associator.to {X} {Y} {Z}

    field
      assoc-commute-from   : CommutativeSquare ((f ⊗₁ g) ⊗₁ h) α⇒ α⇒ (f ⊗₁ (g ⊗₁ h))
      assoc-commute-to     : CommutativeSquare (f ⊗₁ (g ⊗₁ h)) α⇐ α⇐ ((f ⊗₁ g) ⊗₁ h)


------------------------------------------------------------------------
-- A bunch of properties
------------------------------------------------------------------------

module Data.Bool.Properties where

open import Data.Bool as Bool
open import Data.Fin
open import Data.Function
open import Algebra
open import Algebra.Structures
import Algebra.RingSolver.Simple as Solver
import Algebra.RingSolver.AlmostCommutativeRing as ACR
open import Relation.Nullary using (_⇔_)
open import Relation.Binary.PropositionalEquality
import Algebra.FunctionProperties as P; open P _≡_
open import Data.Product
open import Data.Sum
open import Data.Empty

import Relation.Binary.EqReasoning as EqR; open EqR Bool.setoid

------------------------------------------------------------------------
-- Duality

-- Can we take advantage of duality in some (nice) way?

------------------------------------------------------------------------
-- (Bool, ∨, ∧, false, true) forms a commutative semiring

private

  ∨-assoc : Associative _∨_
  ∨-assoc true  y z = refl
  ∨-assoc false y z = refl

  ∧-assoc : Associative _∧_
  ∧-assoc true  y z = refl
  ∧-assoc false y z = refl

  ∨-comm : Commutative _∨_
  ∨-comm true  true  = refl
  ∨-comm true  false = refl
  ∨-comm false true  = refl
  ∨-comm false false = refl

  ∧-comm : Commutative _∧_
  ∧-comm true  true  = refl
  ∧-comm true  false = refl
  ∧-comm false true  = refl
  ∧-comm false false = refl

  ∨-identity : Identity false _∨_
  ∨-identity = (λ _ → refl) , (λ x → ∨-comm x false)

  ∧-identity : Identity true _∧_
  ∧-identity = (λ _ → refl) , (λ x → ∧-comm x true)

  zero-∧ : Zero false _∧_
  zero-∧ = (λ _ → refl) , (λ x → ∧-comm x false)

  distrib-∧-∨ : _∧_ DistributesOver _∨_
  distrib-∧-∨ = distˡ , distʳ
    where
    distˡ : _∧_ DistributesOverˡ _∨_
    distˡ true  y z = refl
    distˡ false y z = refl

    distʳ : _∧_ DistributesOverʳ _∨_
    distʳ x y z =
                      begin
       (y ∨ z) ∧ x
                      ≈⟨ ∧-comm (y ∨ z) x ⟩
       x ∧ (y ∨ z)
                      ≈⟨ distˡ x y z ⟩
       x ∧ y ∨ x ∧ z
                      ≈⟨ cong₂ _∨_ (∧-comm x y) (∧-comm x z) ⟩
       y ∧ x ∨ z ∧ x
                      ∎

isCommutativeSemiring-∨-∧
  : IsCommutativeSemiring _≡_ _∨_ _∧_ false true
isCommutativeSemiring-∨-∧ = record
  { isSemiring = record
    { isSemiringWithoutAnnihilatingZero = record
      { +-isCommutativeMonoid = record
        { isMonoid = record
          { isSemigroup = record
            { isEquivalence = isEquivalence
            ; assoc         = ∨-assoc
            ; ∙-pres-≈      = cong₂ _∨_
            }
          ; identity = ∨-identity
          }
        ; comm = ∨-comm
        }
      ; *-isMonoid = record
        { isSemigroup = record
          { isEquivalence = isEquivalence
          ; assoc         = ∧-assoc
          ; ∙-pres-≈      = cong₂ _∧_
          }
        ; identity = ∧-identity
        }
      ; distrib = distrib-∧-∨
      }
    ; zero = zero-∧
    }
  ; *-comm = ∧-comm
  }

commutativeSemiring-∨-∧ : CommutativeSemiring
commutativeSemiring-∨-∧ = record
  { _+_                   = _∨_
  ; _*_                   = _∧_
  ; 0#                    = false
  ; 1#                    = true
  ; isCommutativeSemiring = isCommutativeSemiring-∨-∧
  }

module RingSolver =
  Solver (ACR.fromCommutativeSemiring commutativeSemiring-∨-∧)

------------------------------------------------------------------------
-- (Bool, ∧, ∨, true, false) forms a commutative semiring

private

  zero-∨ : Zero true _∨_
  zero-∨ = (λ _ → refl) , (λ x → ∨-comm x true)

  distrib-∨-∧ : _∨_ DistributesOver _∧_
  distrib-∨-∧ = distˡ , distʳ
    where
    distˡ : _∨_ DistributesOverˡ _∧_
    distˡ true  y z = refl
    distˡ false y z = refl

    distʳ : _∨_ DistributesOverʳ _∧_
    distʳ x y z =
                          begin
       (y ∧ z) ∨ x
                          ≈⟨ ∨-comm (y ∧ z) x ⟩
       x ∨ (y ∧ z)
                          ≈⟨ distˡ x y z ⟩
       (x ∨ y) ∧ (x ∨ z)
                          ≈⟨ cong₂ _∧_ (∨-comm x y) (∨-comm x z) ⟩
       (y ∨ x) ∧ (z ∨ x)
                          ∎

isCommutativeSemiring-∧-∨
  : IsCommutativeSemiring _≡_ _∧_ _∨_ true false
isCommutativeSemiring-∧-∨ = record
  { isSemiring = record
    { isSemiringWithoutAnnihilatingZero = record
      { +-isCommutativeMonoid = record
        { isMonoid = record
          { isSemigroup = record
            { isEquivalence = isEquivalence
            ; assoc         = ∧-assoc
            ; ∙-pres-≈      = cong₂ _∧_
            }
          ; identity = ∧-identity
          }
        ; comm = ∧-comm
        }
      ; *-isMonoid = record
        { isSemigroup = record
          { isEquivalence = isEquivalence
          ; assoc         = ∨-assoc
          ; ∙-pres-≈      = cong₂ _∨_
          }
        ; identity = ∨-identity
        }
      ; distrib = distrib-∨-∧
      }
    ; zero = zero-∨
    }
  ; *-comm = ∨-comm
  }

commutativeSemiring-∧-∨ : CommutativeSemiring
commutativeSemiring-∧-∨ = record
  { _+_                   = _∧_
  ; _*_                   = _∨_
  ; 0#                    = true
  ; 1#                    = false
  ; isCommutativeSemiring = isCommutativeSemiring-∧-∨
  }

------------------------------------------------------------------------
-- (Bool, ∨, ∧, not, true, false) is a boolean algebra

private

  absorptive : Absorptive _∨_ _∧_
  absorptive = abs-∨-∧ , abs-∧-∨
    where
    abs-∨-∧ : _∨_ Absorbs _∧_
    abs-∨-∧ true  y = refl
    abs-∨-∧ false y = refl

    abs-∧-∨ : _∧_ Absorbs _∨_
    abs-∧-∨ true  y = refl
    abs-∧-∨ false y = refl

  not-∧-inverse : Inverse false not _∧_
  not-∧-inverse =
    ¬x∧x≡⊥ , (λ x → ∧-comm x (not x) ⟨ trans ⟩ ¬x∧x≡⊥ x)
    where
    ¬x∧x≡⊥ : LeftInverse false not _∧_
    ¬x∧x≡⊥ false = refl
    ¬x∧x≡⊥ true  = refl

  not-∨-inverse : Inverse true not _∨_
  not-∨-inverse =
    ¬x∨x≡⊤ , (λ x → ∨-comm x (not x) ⟨ trans ⟩ ¬x∨x≡⊤ x)
    where
    ¬x∨x≡⊤ : LeftInverse true not _∨_
    ¬x∨x≡⊤ false = refl
    ¬x∨x≡⊤ true  = refl

isBooleanAlgebra : IsBooleanAlgebra _≡_ _∨_ _∧_ not true false
isBooleanAlgebra = record
  { isDistributiveLattice = record
      { isLattice = record
          { isEquivalence = isEquivalence
          ; ∨-comm        = ∨-comm
          ; ∨-assoc       = ∨-assoc
          ; ∨-pres-≈      = cong₂ _∨_
          ; ∧-comm        = ∧-comm
          ; ∧-assoc       = ∧-assoc
          ; ∧-pres-≈      = cong₂ _∧_
          ; absorptive    = absorptive
          }
      ; ∨-∧-distribʳ = proj₂ distrib-∨-∧
      }
  ; ∨-complementʳ = proj₂ not-∨-inverse
  ; ∧-complementʳ = proj₂ not-∧-inverse
  ; ¬-pres-≈      = cong not
  }

booleanAlgebra : BooleanAlgebra
booleanAlgebra = record
  { _∨_              = _∨_
  ; _∧_              = _∧_
  ; ¬_               = not
  ; ⊤                = true
  ; ⊥                = false
  ; isBooleanAlgebra = isBooleanAlgebra
  }

------------------------------------------------------------------------
-- (Bool, xor, ∧, id, false, true) forms a commutative ring

private

  xor-is-ok : ∀ x y → x xor y ≡ (x ∨ y) ∧ not (x ∧ y)
  xor-is-ok true  y = refl
  xor-is-ok false y = sym $ proj₂ ∧-identity _

commutativeRing-xor-∧ : CommutativeRing
commutativeRing-xor-∧ = commutativeRing
  where
  import Algebra.Props.BooleanAlgebra as BA
  open BA booleanAlgebra
  open XorRing _xor_ xor-is-ok

module XorRingSolver =
  Solver (ACR.fromCommutativeRing commutativeRing-xor-∧)

------------------------------------------------------------------------
-- Miscellaneous other properties

not-involutive : Involutive not
not-involutive true  = refl
not-involutive false = refl

not-¬ : ∀ {x y} → x ≡ y → x ≢ not y
not-¬ {true}  refl ()
not-¬ {false} refl ()

¬-not : ∀ {x y} → x ≢ y → x ≡ not y
¬-not {true}  {true}  x≢y = ⊥-elim (x≢y refl)
¬-not {true}  {false} _   = refl
¬-not {false} {true}  _   = refl
¬-not {false} {false} x≢y = ⊥-elim (x≢y refl)

⇔→≡ : {b₁ b₂ b : Bool} → b₁ ≡ b ⇔ b₂ ≡ b → b₁ ≡ b₂
⇔→≡ {true } {true }         hyp = refl
⇔→≡ {true } {false} {true } hyp = sym (proj₁ hyp refl)
⇔→≡ {true } {false} {false} hyp = proj₂ hyp refl
⇔→≡ {false} {true } {true } hyp = proj₂ hyp refl
⇔→≡ {false} {true } {false} hyp = sym (proj₁ hyp refl)
⇔→≡ {false} {false}         hyp = refl

T-≡ : ∀ {b} → T b ⇔ b ≡ true
T-≡ {false} = ((λ ())     , λ ())
T-≡ {true}  = (const refl , const _)

T-∧ : ∀ {b₁ b₂} → T (b₁ ∧ b₂) ⇔ (T b₁ × T b₂)
T-∧ {true}  {true}  = (const (_ , _) , const _)
T-∧ {true}  {false} = ((λ ()) , proj₂)
T-∧ {false} {_}     = ((λ ()) , proj₁)

T-∨ : ∀ {b₁ b₂} → T (b₁ ∨ b₂) ⇔ (T b₁ ⊎ T b₂)
T-∨ {true}  {b₂}    = (inj₁ , const _)
T-∨ {false} {true}  = (inj₂ , const _)
T-∨ {false} {false} = (inj₁ , [ id , id ])

proof-irrelevance : ∀ {b} (p q : T b) → p ≡ q
proof-irrelevance {true}  _  _  = refl
proof-irrelevance {false} () ()


module ExCoinduction where
open import Coinduction
open import Relation.Binary.PropositionalEquality
open import Data.Stream
open import Data.Nat
open import Data.Bool

data Loc : Set where
  A : Loc
  DONE : Loc

record Values : Set where
  field
    x : ℕ
    δ : ℕ
    k : ℕ

data _Π_ (A B : Set) : Set where
  <_,_> : (a : Loc) → (b : Values) →  A Π B

-- The computation of x in the state
-- The state machine step function

funₓ : ℕ → ℕ → ℕ → ℕ
funₓ x slope δ = x + δ * slope

step : (Loc Π Values) → (Loc Π Values)
step < A , b > with (X >= 10)
 where
 _>=_ : ℕ → ℕ → Bool
 zero >= zero = true
 zero >= suc y = false
 suc x >= zero = false
 suc x >= suc y = x >= y

 _==_ : ℕ → ℕ → Bool
 zero == zero = true
 zero == suc y = false
 suc x == zero = false
 suc x == suc y = x == y

 X : ℕ
 X = funₓ (Values.x b) 1 (Values.δ b)
step < A , b > | false = < A , record { x = X; δ = Values.δ b ; k = Values.k b + 1 } >
 where
 X : ℕ
 X = funₓ (Values.x b) 1 (Values.δ b)
step < A , b > | true = < DONE , record { x = X; δ = Values.δ b ; k = Values.k b + 1 } >
 where
 X : ℕ
 X = funₓ (Values.x b) 1 (Values.δ b)

step < DONE , b > = < DONE , b > -- Just remain in this state forever

-- Make a stream of runFSM
--
f' : ∀ st → Stream (Loc Π Values)
f' st = st ∷ ♯ (f' (step st))
from : Stream (Loc Π Values)
from =  f' (< A , (record { x = zero ; δ = 1 ; k = zero }) >)

thm : from ≈ from
thm = refl ∷ ♯ (refl ∷ (♯ (refl ∷ (♯ (refl ∷ (♯ (refl ∷ (♯ (refl ∷ (♯ (refl ∷ (♯ (refl ∷ (♯ (refl ∷
             (♯ (refl ∷ ♯ (t'))))))))))))))))))

    where
    y : Stream (Loc Π Values)
    y = f' (< DONE , record { x = 10 ; δ = 1 ; k = 10 } > )
    t' : y ≈ y
    t' = refl ∷ (♯ t')


{-# OPTIONS --cubical #-}

open import Agda.Primitive.Cubical
  renaming ( primIMin       to _∧_  -- I → I → I
           ; primIMax       to _∨_  -- I → I → I
           ; primINeg       to ~_   -- I → I
           )

infix 10 _≡_
postulate
  _≡_ : I → I → Set
  refl : ∀ {i} → i ≡ i

module Vars (i j k : I) where

  _ : i ∧ i1 ≡ i
  _ = refl

  _ : i ∧ i0 ≡ i0
  _ = refl

  _ : i1 ∧ i ≡ i
  _ = refl

  _ : i0 ∧ i ≡ i0
  _ = refl

  _ : i ∧ j ≡ j ∧ i
  _ = refl

  _ : i ∧ (j ∧ k) ≡ (i ∧ j) ∧ k
  _ = refl

  _ : i ∧ i ≡ i
  _ = refl

  _ : i ∨ j ≡ j ∨ i
  _ = refl

  _ : i ∨ (j ∨ k) ≡ (i ∨ j) ∨ k
  _ = refl

  _ : i ∨ i ≡ i
  _ = refl

  _ : i ∨ i1 ≡ i1
  _ = refl

  _ : i ∨ i0 ≡ i
  _ = refl

  _ : i1 ∨ i ≡ i1
  _ = refl

  _ : i0 ∨ i ≡ i
  _ = refl

  _ : ~ i0 ≡ i1
  _ = refl

  _ : ~ i1 ≡ i0
  _ = refl

  _ : ~ (~ i) ≡ i
  _ = refl

  _ : ~ (i ∧ j) ≡ ~ i ∨ ~ j
  _ = refl

  _ : ~ (i ∨ j) ≡ ~ i ∧ ~ j
  _ = refl

  _ : i ∧ (j ∨ k) ≡ (i ∧ j) ∨ (i ∧ k)
  _ = refl

  _ : i ∨ (j ∧ k) ≡ (i ∨ j) ∧ (i ∨ k)
  _ = refl

module Postulates where
  postulate
    i j k : I

  _ : i ∧ i1 ≡ i
  _ = refl

  _ : i ∧ i0 ≡ i0
  _ = refl

  _ : i1 ∧ i ≡ i
  _ = refl

  _ : i0 ∧ i ≡ i0
  _ = refl

  _ : i ∧ j ≡ j ∧ i
  _ = refl

  _ : i ∧ (j ∧ k) ≡ (i ∧ j) ∧ k
  _ = refl

  _ : i ∧ i ≡ i
  _ = refl

  _ : i ∨ j ≡ j ∨ i
  _ = refl

  _ : i ∨ (j ∨ k) ≡ (i ∨ j) ∨ k
  _ = refl

  _ : i ∨ i ≡ i
  _ = refl

  _ : i ∨ i1 ≡ i1
  _ = refl

  _ : i ∨ i0 ≡ i
  _ = refl

  _ : i1 ∨ i ≡ i1
  _ = refl

  _ : i0 ∨ i ≡ i
  _ = refl

  _ : ~ i0 ≡ i1
  _ = refl

  _ : ~ i1 ≡ i0
  _ = refl

  _ : ~ (~ i) ≡ i
  _ = refl

  _ : ~ (i ∧ j) ≡ ~ i ∨ ~ j
  _ = refl

  _ : ~ (i ∨ j) ≡ ~ i ∧ ~ j
  _ = refl

  _ : i ∧ (j ∨ k) ≡ (i ∧ j) ∨ (i ∧ k)
  _ = refl

  _ : i ∨ (j ∧ k) ≡ (i ∨ j) ∧ (i ∨ k)
  _ = refl


open import Agda.Builtin.Nat

data Bal : Nat → Nat → Nat → Set where
  leanR : ∀ {n} → Bal n (suc n) (suc n)
  leanL : ∀ {n} → Bal (suc n) n (suc n)

data Tree : Nat → Set where
  leaf : Tree 0
  node : ∀ {hˡ hʳ h} (t : Tree hʳ) (b : Bal hˡ hʳ h) → Tree (suc h)

join : ∀ {hˡ hʳ} h → Bal hˡ hʳ h → Tree (suc hˡ) → Nat
join _       leanL (node (node t b) leanR) = 0
join (suc _) leanL (node t          leanL) = 1
join _       leanR t₁                      = 2


-- Andreas, 2017-05-26
-- Expand ellipsis with C-c C-c . RET

open import Agda.Builtin.Nat

test0 : Nat → Nat
test0 x with zero

... | q = {!.!}  -- C-c C-c

-- Expected result:
-- test0 x | q = ?

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

test1 : ∀{n} → Fin n → Nat

test1 (zero _) with Nat

... | q = {!.!} -- C-c C-c

-- Expected result:
-- test1 (zero _) | q = ?

test1 {.(suc n)} (suc {n} i) with Fin zero

... | q = {!.!} -- C-c C-c

-- Expected result:
-- test1 {.(suc n)} (suc {n} i) | q = ?


{-# OPTIONS --cubical-compatible --show-implicit #-}
-- {-# OPTIONS -v tc.lhs.split.well-formed:100 #-}
-- Andreas, adapted from Andres Sicard, 2013-05-29
module WithoutKRestrictive where

open import Common.Level
open import Common.Equality
open import Common.Product

data ℕ : Set where
  zero : ℕ
  suc  : ℕ → ℕ

data _≤_ : ℕ → ℕ → Set where
  z≤n : ∀ {n} → zero ≤ n
  s≤s : ∀ {n m} → n ≤ m → suc n ≤ suc m

_<_ : ℕ → ℕ → Set
n < m = suc n ≤ m

refl≤ : ∀ (n : ℕ) → n ≤ n
refl≤ zero    = z≤n
refl≤ (suc n) = s≤s (refl≤ n)

data List {a} (A : Set a) : Set a where
  []  : List A
  _∷_ : (x : A) (xs : List A) → List A

length : {A : Set} → List A → ℕ
length []       = zero
length (x ∷ xs) = suc (length xs)

P : {A : Set} → List A → List A → Set
P xs ys = Σ _ (λ x → ys ≡ (x ∷ xs))

Q : {A : Set} → List A → List A → Set
Q xs ys = (length xs) < (length ys)

helper : {A : Set}(y : A)(xs : List A) → (length xs) < (length (y ∷ xs))
helper y []       = s≤s z≤n
helper y (x ∷ xs) = s≤s (refl≤ _)

-- Why the --cubical-compatible option rejects the following proof

foo : {A : Set}(xs ys : List A) → P xs ys → Q xs ys
foo xs .(x ∷ xs) (x , refl) = helper x xs

-- if I can prove foo using only subst

foo' : {A : Set}(xs ys : List A) → P xs ys → Q xs ys
foo' xs ys (x , h) =
  subst (λ ys' → length xs < length ys') (sym h) (helper x xs)


module Preliminaries where

  open import Agda.Primitive using (Level) renaming (lzero to lZ; lsuc to lS; _⊔_ to lmax)

  -- ----------------------------------------------------------------------
  -- functions

  _o_ : {A B C : Set} → (B → C) → (A → B) → A → C
  g o f = \ x → g (f x)
  infixr 10 _o_

  -- ----------------------------------------------------------------------
  -- identity type

  data _==_ {l : Level} {A : Set l} (M : A) : A → Set l where
    Refl : M == M

  Id : {l : Level} {A : Set l} (M : A) → A → Set l
  Id M N = M == N

  {-# BUILTIN EQUALITY _==_ #-}
  {-# BUILTIN REFL Refl #-}

  transport : {l1 : Level} {l2 : Level} {A : Set l1} (B : A → Set l2)
              {a1 a2 : A} → a1 == a2 → (B a1 → B a2)
  transport B Refl = λ x → x

  ! : {l : Level} {A : Set l} {M N : A} → M == N → N == M
  ! Refl = Refl

  _∘_  : {l : Level} {A : Set l} {M N P : A}
      → N == P → M == N → M == P
  β ∘ Refl = β

  ap :  {l1 l2 : Level} {A : Set l1} {B : Set l2} {M N : A}
       (f : A → B) → M == N → (f M) == (f N)
  ap f Refl = Refl

  ap2 : {l1 l2 l3 : Level} {A : Set l1} {B : Set l2} {C : Set l3} {M N : A} {M' N' : B} (f : A -> B -> C) -> M == N -> M' == N' -> (f M M') == (f N N')
  ap2 f Refl Refl = Refl

  ap3 : {l1 l2 l3 l4 : Level} {A : Set l1} {B : Set l2} {C : Set l3} {D : Set l4} {M N : A} {M' N' : B} {M'' N'' : C}
      (f : A → B → C → D) → M == N → M' == N' → M'' == N'' → (f M M' M'') == (f N N' N'')
  ap3 f Refl Refl Refl = Refl

  postulate
    -- function extensionality
    λ=  : {l1 l2 : Level} {A : Set l1} {B : A -> Set l2} {f g : (x : A) -> B x} -> ((x : A) -> (f x) == (g x)) -> f == g
    -- function extensionality for implicit functions
    λ=i  : {l1 l2 : Level} {A : Set l1} {B : A -> Set l2} {f g : {x : A} -> B x} -> ((x : A) -> (f {x}) == (g {x})) -> _==_ {_}{ {x : A} → B x } f g

  private primitive primTrustMe : {l : Level} {A : Set l} {x y : A} -> x == y

  infixr 9 _==_

  infix  2 _∎
  infixr 2 _=⟨_⟩_

  _=⟨_⟩_ : {l : Level} {A : Set l} (x : A) {y z : A} → x == y → y == z → x == z
  _ =⟨ p1 ⟩ p2 = (p2 ∘ p1)

  _∎ : {l : Level} {A : Set l} (x : A) → x == x
  _∎ _ = Refl

  -- ----------------------------------------------------------------------
  -- product types

  record Unit : Set where
    constructor <>

  record Σ {l1 l2 : Level} {A : Set l1} (B : A -> Set l2) : Set (lmax l1 l2) where
    constructor _,_
    field
      fst : A
      snd : B fst
  open Σ public

  infixr 0 _,_

  _×_ : {l1 l2 : Level} → Set l1 -> Set l2 -> Set (lmax l1 l2)
  A × B = Σ (\ (_ : A) -> B)

  infixr 10 _×_

  -- ----------------------------------------------------------------------
  -- booleans

  data Bool : Set where
     True : Bool
     False : Bool
  {-# COMPILED_DATA Bool Bool True False #-}
  {-# BUILTIN BOOL  Bool  #-}
  {-# BUILTIN TRUE  True  #-}
  {-# BUILTIN FALSE False #-}

  -- ----------------------------------------------------------------------
  -- order

  data Order : Set where
    Less : Order
    Equal : Order
    Greater : Order

  -- ----------------------------------------------------------------------
  -- sums

  data Void : Set where

  abort : {A : Set} → Void → A
  abort ()

  data Either (A B : Set) : Set where
    Inl : A → Either A B
    Inr : B → Either A B

  DecEq : Set → Set
  DecEq A = (x y : A) → Either (x == y) (x == y → Void)

  -- ----------------------------------------------------------------------
  -- natural numbers

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

    -- let's you use numerals for Nat
    {-# BUILTIN NATURAL Nat #-}

    _+_ : Nat → Nat → Nat
    Z + n = n
    (S m) + n = S (m + n)

    max : Nat → Nat → Nat
    max Z n = n
    max m Z = m
    max (S m) (S n) = S (max m n)

    equal : Nat → Nat → Bool
    equal Z Z = True
    equal Z (S _) = False
    equal (S _) Z = False
    equal (S m) (S n) = equal m n

    compare : Nat → Nat → Order
    compare Z Z = Equal
    compare Z (S m) = Less
    compare (S n) Z = Greater
    compare (S n) (S m) = compare n m

  open Nat public using (Nat ; Z ; S)

  -- ----------------------------------------------------------------------
  -- monad

  module Monad where

    record Monad : Set1 where
      field
        T : Set → Set
        return : ∀ {A} → A → T A
        _>>=_  : ∀ {A B} → T A → (A → T B) -> T B

  -- ----------------------------------------------------------------------
  -- options

  module Maybe where

    data Maybe {l : Level} (A : Set l) : Set l where
      Some : A → Maybe A
      None : Maybe A

    Monad : Monad.Monad
    Monad = record { T = Maybe; return = Some; _>>=_ = (λ {None _ → None; (Some v) f → f v}) }

  open Maybe public using (Maybe;Some;None)

  -- ----------------------------------------------------------------------
  -- lists

  module List where
    data List {l : Level} (A : Set l) : Set l where
      []  : List A
      _::_ : A -> List A -> List A

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

    infixr 99 _::_

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

    infixr 10 _++_

    map : {l1 l2 : Level} {A : Set l1} {B : Set l2} → (A → B) → List A → List B
    map f [] = []
    map f (x :: xs) = f x :: map f xs

    map-id : {l : Level} {A : Set l} (l : List A) → map (\ (x : A) → x) l == l
    map-id [] = Refl
    map-id (x :: l) with map (\ x -> x) l | map-id l
    ... | ._ | Refl = Refl

    ++-assoc : ∀ {A} (l1 l2 l3 : List A) → (l1 ++ l2) ++ l3 == l1 ++ (l2 ++ l3)
    ++-assoc [] l2 l3 = Refl
    ++-assoc (x :: xs) l2 l3 = ap (_::_ x) (++-assoc xs l2 l3)

  open List public using (List ; [] ; _::_)

  -- ----------------------------------------------------------------------
  -- characters

  module Char where

    postulate {- Agda Primitive -}
      Char : Set

    {-# BUILTIN CHAR Char #-}
    {-# COMPILED_TYPE Char Char #-}

    private
     primitive
      primCharToNat    : Char → Nat
      primCharEquality : Char → Char → Bool

    toNat : Char → Nat
    toNat = primCharToNat

    equalb : Char -> Char -> Bool
    equalb = primCharEquality

    -- need to go outside the real language a little to give the primitives good types,
    -- but from the outside this should be safe
    equal : DecEq Char
    equal x y with equalb x y
    ... | True = Inl primTrustMe
    ... | False = Inr canthappen where
      postulate canthappen : _

  open Char public using (Char)

  -- ----------------------------------------------------------------------
  -- vectors

  module Vector where

    data Vec (A : Set) : Nat → Set where
      []   : Vec A 0
      _::_ : ∀ {n} → A → Vec A n → Vec A (S n)

    infixr 99 _::_

    Vec-elim : {A : Set} (P : {n : Nat} → Vec A n → Set)
               → (P [])
               → ({n : Nat} (x : A) (xs : Vec A n) → P xs → P (x :: xs))
               → {n : Nat} (v : Vec A n) → P v
    Vec-elim P n c [] = n
    Vec-elim P n c (y :: ys) = c y ys (Vec-elim P n c ys)

    fromList : {A : Set} → List A → Σ \n → Vec A n
    fromList [] = _ , []
    fromList (x :: xs) = _ , x :: snd (fromList xs)

    toList : {A : Set} {n : Nat} → Vec A n → List A
    toList [] = []
    toList (x :: xs) = x :: (toList xs)

    toList' : {A : Set} → (Σ \ n → Vec A n) → List A
    toList' (._ , []) = []
    toList' (._ , (x :: xs)) = x :: (toList' (_ , xs))

  -- ----------------------------------------------------------------------
  -- strings

  module String where

    postulate {- Agda Primitive -}
      String : Set
    {-# BUILTIN STRING  String #-}
    {-# COMPILED_TYPE String String #-}

    private
      primitive
         primStringToList   : String -> List Char
         primStringFromList : List Char -> String
         primStringAppend   : String -> String -> String
         primStringEquality : String -> String -> Bool

    equal : String -> String -> Bool
    equal = primStringEquality

    toList = primStringToList
    fromList = primStringFromList

    append = primStringAppend

    toVec : String -> Σ \ m → Vector.Vec Char m
    toVec = Vector.fromList o toList


open import Data.Product renaming (_×_ to _∧_)

×-comm : {A B : Set} → (A ∧ B) → (B ∧ A)
×-comm (fst , snd) = snd , fst

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

K : {A B : Set} → A → B → A
K a b = a

app : {A B : Set} → (A → B) → A → B
app f a = f a

flip : {A B C : Set} → (A → B → C) → B → A → C
flip f b a = f a b

comp : {A B C : Set} → (A → B) → (B → C) → (A → C)
comp ab bc = λ x → bc (ab x)

S : {A B C : Set} → (A → B → C) → (A → B) → A → C
S g f = λ x → g x (f x)

proj1 : {A B : Set} → (A ∧ B) → A
proj1 (fst , snd) = fst

proj2 : {A B : Set} → (A ∧ B) → B
proj2 (fst , snd) = snd

diagonal : {A B : Set} → A → (A ∧ A)
diagonal a = a , a

commut : {A B : Set} → (A ∧ B) → (B ∧ A)
commut (fst , snd) = snd , fst

curry1 : {A B C : Set} → (A ∧ B → C) → (A → B → C)
curry1 f = λ x x₁ → f (x , x₁)

curry2 : {A B C : Set} → (A → B → C) → (A ∧ B → C)
curry2 f (fst , snd) = f fst snd

_↔_ : (A B : Set) → Set
A ↔ B = (A → B) ∧ (B → A)

currying : {A B C : Set} → (A ∧ B → C) ↔ (A → B → C)
currying = curry1 , curry2

distrib : {A B C : Set} → (A → (B ∧ C)) ↔ ((A → B) ∧ (A → C))
distrib = (λ x → (λ x₁ → proj1 (x x₁)) , λ x₁ → proj2 (x x₁)) , λ x x₁ → ((proj1 x) x₁) , ((proj2 x) x₁)

data _∨_ (A B : Set) : Set where
  left : A → A ∨ B
  right : B → A ∨ B

or-elim : {A B C : Set} → (A ∨ B) → (A → C) → (B → C) → C
or-elim (left x) = λ x₁ x₂ → x₁ x
or-elim (right x) = λ x₁ x₂ → x₂ x

or-comm : {A B : Set} → (A ∨ B) → (B ∨ A)
or-comm (left x) = right x
or-comm (right x) = left x

or-dist : {A B C : Set} → (A ∧ (B ∨ C)) → ((A ∧ B) ∨ (A ∧ C))
or-dist (fst , left x) = left (fst , x)
or-dist (fst , right x) = right (fst , x)

data ⊥ : Set where

⊥-elim : {A : Set} → ⊥ → A
⊥-elim ()

¬ : Set → Set
¬ A = A → ⊥

contr : {A B : Set} → (A → B) → (¬ B → ¬ A)
contr f = λ x x₁ → x (f x₁)

non-contr : {A : Set} → ¬ (A ∧ ¬ A)
non-contr (fst , snd) = snd fst

nni : {A : Set} → A → ¬ (¬ A)
nni a = λ x → x a

⊥-nne : ¬ (¬ ⊥) → ⊥
⊥-nne x = x ⊥-elim

¬-elim : {A B : Set} → ¬ A → A → B
¬-elim n a = ⊥-elim (n a)

nnlem : {A : Set} → ¬ (¬ (A ∨ ¬ A))
nnlem = (λ x → x (right λ y → x (left y)))

rp2 : {A : Set} → (A → ¬ A) → (¬ A → A) → ⊥
rp2 a na = nnlem (λ x → or-elim x (λ x₁ → a x₁ x₁) λ x₁ → x₁ (na x₁))

data ⊤ : Set where
  tt : ⊤

ti : {A : Set} → (⊤ → A) → A
ti f = f tt

dmnt : ¬ ⊤ → ⊥
dmnt f = f tt

dmtn : ⊥ → ¬ ⊤
dmtn = λ x x₁ → x

lem : Set₁
lem = (A : Set) → A ∨ ¬ A

nne : Set₁
nne = (A : Set) → ¬ (¬ A) → A

nne-lem : nne → lem
nne-lem x A = x (A ∨ ¬ A) nnlem

lem-nne : lem → nne
lem-nne x A y = or-elim (x A) (λ x₁ → x₁) λ x₁ → ¬-elim y x₁

_↔₁_ : (A B : Set₁) → Set₁
A ↔₁ B = (A → B) ∧ (B → A)

peirce : Set₁
peirce = (A B : Set) → ((A → B) → A) → A

lem-peirce : lem ↔₁ peirce
lem-peirce = (λ x A B x₁ → or-elim (x A) id λ x₂ → x₁ λ x₃ → ¬-elim x₂ x₃) , λ x A → x (A ∨ ¬ A) ⊥ λ x₁ → right λ x₂ → x₁ (left x₂)

nne-peirce : nne ↔₁ peirce
nne-peirce = (λ x A B x₁ → x A λ x₂ → x₂ (x₁ λ x₃ → ¬-elim x₂ x₃)) , (λ x A x₁ → x A ⊥ λ x₂ → ¬-elim x₁ x₂)


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

open import lib.Basics
open import lib.NConnected

open import Graphs.Definition
open import Coequalizers.Definition

{- We will consider two properties of graphs: if they are trees and if they are connected,
   both are defined in terms of the geometric realisation (coequalizer). -}
module Graphs.Properties where

{- A graph is a tree if its coequalizer is contractible. -}
is-tree : {i j : ULevel} {E : Type i} {V : Type j} ( gph : Graph E V ) → Type (lmax i j)
is-tree gph = is-contr (Coeq gph)

{- A graph is connected if its coequalizer is connected. -}
gph-is-connected : {i j : ULevel} {E : Type i} {V : Type j} (gph : Graph E V) → Type (lmax i j)
gph-is-connected gph = is-connected 0 (Coeq gph)


------------------------------------------------------------------------
-- agda-misc
-- All modules
------------------------------------------------------------------------

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

module AgdaMiscEverything where

------------------------------------------------------------------------
-- Algorithms

-- Insertion sort
import Algorithms.List.Sort.Insertion

import Algorithms.List.Sort.Insertion.Properties

-- Quicksort
import Algorithms.List.Sort.Quick

import Algorithms.List.Sort.Quick.Properties

------------------------------------------------------------------------
-- Constructive mathematics

-- Definitions of Axioms that are nonconstructive
import Constructive.Axiom

import Constructive.Axiom.Properties

import Constructive.Axiom.Properties.Base

import Constructive.Axiom.Properties.Base.Lemma

import Constructive.Axiom.Properties.Bool

import Constructive.Axiom.Properties.Transport

-- Alternative proof of LLPO => MP∨
import Constructive.Axiom.Properties.Alternative

-- Combinators for reasoning
import Constructive.Combinators

import Constructive.Common

-- Searchable set
import Constructive.Searchable

-- Experiment

-- Category theory
import Experiment.Categories.Solver.Category

import Experiment.Categories.Solver.Category.Example

import Experiment.Categories.Solver.Category.Cartesian

import Experiment.Categories.Solver.Category.Cartesian.Example

import Experiment.Categories.Solver.Functor

import Experiment.Categories.Solver.Functor.Example

import Experiment.Categories.Solver.MultiFunctor

import Experiment.Categories.Solver.MultiFunctor.Example

------------------------------------------------------------------------
-- Formal language

import Math.FormalLanguage

------------------------------------------------------------------------
-- Googology

import Math.Googology.Function

import Math.Googology.Function.Properties

------------------------------------------------------------------------
-- Number theory

-- Fibonacci number
import Math.NumberTheory.Fibonacci.Generic

import Math.NumberTheory.Fibonacci.Nat

import Math.NumberTheory.Fibonacci.Nat.Properties

-- Summation
import Math.NumberTheory.Summation.Generic

import Math.NumberTheory.Summation.Generic.Properties

import Math.NumberTheory.Summation.Nat

import Math.NumberTheory.Summation.Nat.Properties

-- Product
import Math.NumberTheory.Product.Generic

import Math.NumberTheory.Product.Generic.Properties

import Math.NumberTheory.Product.Nat

import Math.NumberTheory.Product.Nat.Properties

------------------------------------------------------------------------
-- Type theory

-- Natural number
import TypeTheory.Nat.Operations

import TypeTheory.Nat.Properties

import TypeTheory.Nat.Instance

-- Homotopy Type Theory
import TypeTheory.HoTT.Base

import TypeTheory.HoTT.Data.Empty.Properties

import TypeTheory.HoTT.Data.Sum.Properties

import TypeTheory.HoTT.Function.Properties

import TypeTheory.HoTT.Relation.Nullary.Negation.Properties

-- Identity type
import TypeTheory.Identity


{-# OPTIONS --safe --warning=error --without-K #-}

open import LogicalFormulae
open import Setoids.Setoids
open import Rings.Definition
open import Rings.Ideals.Definition

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)

module Rings.Ideals.Maximal.Definition {a b : _} {A : Set a} {S : Setoid {a} {b} A} {_+_ _*_ : A → A → A} {R : Ring S _+_ _*_} {c : _} {pred : A → Set c} (i : Ideal R pred) where

record MaximalIdeal {d : _} : Set (a ⊔ b ⊔ c ⊔ lsuc d) where
  field
    notContained : A
    notContainedIsNotContained : (pred notContained) → False
    isMaximal : {bigger : A → Set d} → Ideal R bigger → ({a : A} → pred a → bigger a) → (Sg A (λ a → bigger a && (pred a → False))) → ({a : A} → bigger a)


module Structure.Category.NaturalTransformation.NaturalTransformations where

open import Functional           using () renaming (id to idᶠⁿ)
open import Functional.Dependent using () renaming (_∘_ to _∘ᶠⁿ_)
open import Logic
open import Logic.Predicate
import      Lvl
open import Structure.Category
open import Structure.Category.Functor
open import Structure.Category.NaturalTransformation
open import Structure.Categorical.Properties
open import Structure.Operator
open import Structure.Relator.Equivalence
open import Structure.Setoid
open import Syntax.Transitivity
open import Type

open CategoryObject
private variable ℓₒₗ ℓₒᵣ ℓₘₗ ℓₘᵣ ℓₑₗ ℓₑᵣ : Lvl.Level

module Raw
  (catₗ : CategoryObject{ℓₒₗ}{ℓₘₗ}{ℓₑₗ})
  (catᵣ : CategoryObject{ℓₒᵣ}{ℓₘᵣ}{ℓₑᵣ})
  where

  private variable F F₁ F₂ F₃ : Object(catₗ) → Object(catᵣ)
  private instance _ = category catₗ
  private instance _ = category catᵣ

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

  idᴺᵀ : (x : Object(catₗ)) → (F(x) ⟶ F(x))
  idᴺᵀ _ = id

  _∘ᴺᵀ_ : ((x : Object(catₗ)) → (F₂(x) ⟶ F₃(x))) → ((x : Object(catₗ)) → (F₁(x) ⟶ F₂(x))) → ((x : Object(catₗ)) → (F₁(x) ⟶ F₃(x)))
  (comp₁ ∘ᴺᵀ comp₂)(x) = comp₁(x) ∘ comp₂(x)

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

  private instance _ = category catₗ
  private instance _ = category catᵣ

  open Category ⦃ … ⦄ hiding (identity)
  open Functor ⦃ … ⦄
  private open module Equivᵣ {x}{y} = Equivalence (Equiv-equivalence ⦃ morphism-equiv(catᵣ){x}{y} ⦄) using ()

  module _ where
    open Raw(catₗ)(catᵣ)

    module _ {functor@([∃]-intro F) : catₗ →ᶠᵘⁿᶜᵗᵒʳ catᵣ} where
      identity : NaturalTransformation(functor)(functor)(idᴺᵀ)
      NaturalTransformation.natural identity {x} {y} {f} =
        id ∘ map f 🝖-[ Morphism.identityₗ(_)(id) ⦃ identityₗ ⦄ ]
        map f      🝖-[ Morphism.identityᵣ(_)(id) ⦃ identityᵣ ⦄ ]-sym
        map f ∘ id 🝖-end

    module _ {functor₁@([∃]-intro F₁) functor₂@([∃]-intro F₂) functor₃@([∃]-intro F₃) : catₗ →ᶠᵘⁿᶜᵗᵒʳ catᵣ} where
      composition : ∀{comp₁ comp₂} → NaturalTransformation(functor₂)(functor₃)(comp₁) → NaturalTransformation(functor₁)(functor₂)(comp₂) → NaturalTransformation(functor₁)(functor₃)(comp₁ ∘ᴺᵀ comp₂)
      NaturalTransformation.natural (composition {comp₁} {comp₂} nat₁ nat₂) {x} {y} {f} =
        (comp₁(y) ∘ comp₂(y)) ∘ map f 🝖-[ Morphism.associativity(_) ⦃ associativity ⦄ ]
        comp₁(y) ∘ (comp₂(y) ∘ map f) 🝖-[ congruence₂ᵣ(_∘_)(comp₁(y)) (NaturalTransformation.natural nat₂) ]
        comp₁(y) ∘ (map f ∘ comp₂(x)) 🝖-[ Morphism.associativity(_) ⦃ associativity ⦄ ]-sym
        (comp₁(y) ∘ map f) ∘ comp₂(x) 🝖-[ congruence₂ₗ(_∘_)(comp₂(x)) (NaturalTransformation.natural nat₁) ]
        (map f ∘ comp₁(x)) ∘ comp₂(x) 🝖-[ Morphism.associativity(_) ⦃ associativity ⦄ ]
        map f ∘ (comp₁(x) ∘ comp₂(x)) 🝖-end

  module Wrapped where
    private variable F F₁ F₂ F₃ : catₗ →ᶠᵘⁿᶜᵗᵒʳ catᵣ

    idᴺᵀ : (F →ᴺᵀ F)
    idᴺᵀ = [∃]-intro (Raw.idᴺᵀ(catₗ)(catᵣ)) ⦃ identity ⦄

    _∘ᴺᵀ_ : (F₂ →ᴺᵀ F₃) → (F₁ →ᴺᵀ F₂) → (F₁ →ᴺᵀ F₃)
    _∘ᴺᵀ_ ([∃]-intro F ⦃ F-proof ⦄) ([∃]-intro G ⦃ G-proof ⦄) = [∃]-intro (Raw._∘ᴺᵀ_ (catₗ)(catᵣ) F G) ⦃ composition F-proof G-proof ⦄


{-# OPTIONS --omega-in-omega --no-termination-check --overlapping-instances #-}

module Light.Library.Relation where

open import Light.Level using (Level ; Setω)
open import Light.Variable.Sets

record Kind : Setω where
      field
            iℓ : Level
            Index : Set iℓ
            ℓf : Index → Level
            Proposition : ∀ i → Set (ℓf i)

record Style (kind : Kind) : Setω where
      open Kind kind
      field
            true‐i false‐i : Index
            true : Proposition true‐i
            false : Proposition false‐i

            ¬‐index‐f : Index → Index
            ¬_ : ∀ {i} → Proposition i → Proposition (¬‐index‐f i)

            ∧‐index‐f ∨‐index‐f ⇢‐index‐f : Index → Index → Index
            _∧_ : ∀ {ia ib} → Proposition ia → Proposition ib → Proposition (∧‐index‐f ia ib)
            _∨_ : ∀ {ia ib} → Proposition ia → Proposition ib → Proposition (∨‐index‐f ia ib)
            _⇢_ : ∀ {ia ib} → Proposition ia → Proposition ib → Proposition (⇢‐index‐f ia ib)

            true‐set‐ℓf false‐set‐ℓf : Index → Level
            True : ∀ {i} → Proposition i → Set (true‐set‐ℓf i)
            False : ∀ {i} → Proposition i → Set (false‐set‐ℓf i)
            true‐is‐true : True true
            false‐is‐false : False false

record Base : Setω where
      field ⦃ kind ⦄ : Kind
      field ⦃ style ⦄ : Style kind

open Kind ⦃ ... ⦄ public
open Style ⦃ ... ⦄ public


------------------------------------------------------------------------
-- The Agda standard library
--
-- Ways to give instances of certain structures where some fields can
-- be given in terms of others
------------------------------------------------------------------------

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

open import Relation.Binary using (Rel; Setoid; IsEquivalence)

module Algebra.Structures.Biased
  {a ℓ} {A : Set a}  -- The underlying set
  (_≈_ : Rel A ℓ)    -- The underlying equality relation
  where

open import Algebra.Core
open import Algebra.Definitions _≈_
open import Algebra.Structures _≈_
import Algebra.Consequences.Setoid as Consequences
open import Data.Product using (_,_; proj₁; proj₂)
open import Level using (_⊔_)

------------------------------------------------------------------------
-- IsCommutativeMonoid

record IsCommutativeMonoidˡ (∙ : Op₂ A) (ε : A) : Set (a ⊔ ℓ) where
  field
    isSemigroup : IsSemigroup ∙
    identityˡ   : LeftIdentity ε ∙
    comm        : Commutative ∙

  open IsSemigroup isSemigroup

  private

    identityʳ : RightIdentity ε ∙
    identityʳ = Consequences.comm+idˡ⇒idʳ setoid comm identityˡ

    identity : Identity ε ∙
    identity = (identityˡ , identityʳ)

  isCommutativeMonoid : IsCommutativeMonoid ∙ ε
  isCommutativeMonoid = record
    { isMonoid = record
      { isSemigroup = isSemigroup
      ; identity    = identity
      }
    ; comm = comm
    }

open IsCommutativeMonoidˡ public
  using () renaming (isCommutativeMonoid to isCommutativeMonoidˡ)

record IsCommutativeMonoidʳ (∙ : Op₂ A) (ε : A) : Set (a ⊔ ℓ) where
  field
    isSemigroup : IsSemigroup ∙
    identityʳ   : RightIdentity ε ∙
    comm        : Commutative ∙

  open IsSemigroup isSemigroup

  private

    identityˡ : LeftIdentity ε ∙
    identityˡ = Consequences.comm+idʳ⇒idˡ setoid comm identityʳ

    identity : Identity ε ∙
    identity = (identityˡ , identityʳ)

  isCommutativeMonoid : IsCommutativeMonoid ∙ ε
  isCommutativeMonoid = record
    { isMonoid = record
      { isSemigroup = isSemigroup
      ; identity    = identity
      }
    ; comm = comm
    }

open IsCommutativeMonoidʳ public
  using () renaming (isCommutativeMonoid to isCommutativeMonoidʳ)

------------------------------------------------------------------------
-- IsCommutativeSemiring

record IsCommutativeSemiringˡ (+ * : Op₂ A) (0# 1# : A) : Set (a ⊔ ℓ) where
  field
    +-isCommutativeMonoid : IsCommutativeMonoid + 0#
    *-isCommutativeMonoid : IsCommutativeMonoid * 1#
    distribʳ              : * DistributesOverʳ +
    zeroˡ                 : LeftZero 0# *

  private
    module +-CM = IsCommutativeMonoid +-isCommutativeMonoid
    open module *-CM = IsCommutativeMonoid *-isCommutativeMonoid public
           using () renaming (comm to *-comm)

    distribˡ : * DistributesOverˡ +
    distribˡ = Consequences.comm+distrʳ⇒distrˡ
                +-CM.setoid +-CM.∙-cong *-comm distribʳ

    distrib : * DistributesOver +
    distrib = (distribˡ , distribʳ)

    zeroʳ : RightZero 0# *
    zeroʳ = Consequences.comm+zeˡ⇒zeʳ +-CM.setoid *-comm zeroˡ

    zero : Zero 0# *
    zero = (zeroˡ , zeroʳ)

  isCommutativeSemiring : IsCommutativeSemiring + * 0# 1#
  isCommutativeSemiring = record
    { isSemiring = record
      { isSemiringWithoutAnnihilatingZero = record
        { +-isCommutativeMonoid = +-isCommutativeMonoid
        ; *-isMonoid = *-CM.isMonoid
        ; distrib = distrib
        }
      ; zero = zero
      }
    ; *-comm = *-comm
    }

open IsCommutativeSemiringˡ public
  using () renaming (isCommutativeSemiring to isCommutativeSemiringˡ)

record IsCommutativeSemiringʳ (+ * : Op₂ A) (0# 1# : A) : Set (a ⊔ ℓ) where
  field
    +-isCommutativeMonoid : IsCommutativeMonoid + 0#
    *-isCommutativeMonoid : IsCommutativeMonoid * 1#
    distribˡ              : * DistributesOverˡ +
    zeroʳ                 : RightZero 0# *

  private
    module +-CM = IsCommutativeMonoid +-isCommutativeMonoid
    open module *-CM = IsCommutativeMonoid *-isCommutativeMonoid public
           using () renaming (comm to *-comm)

    distribʳ : * DistributesOverʳ +
    distribʳ = Consequences.comm+distrˡ⇒distrʳ
                +-CM.setoid +-CM.∙-cong *-comm distribˡ

    distrib : * DistributesOver +
    distrib = (distribˡ , distribʳ)

    zeroˡ : LeftZero 0# *
    zeroˡ = Consequences.comm+zeʳ⇒zeˡ +-CM.setoid *-comm zeroʳ

    zero : Zero 0# *
    zero = (zeroˡ , zeroʳ)

  isCommutativeSemiring : IsCommutativeSemiring + * 0# 1#
  isCommutativeSemiring = record
    { isSemiring = record
      { isSemiringWithoutAnnihilatingZero = record
        { +-isCommutativeMonoid = +-isCommutativeMonoid
        ; *-isMonoid = *-CM.isMonoid
        ; distrib = distrib
        }
      ; zero = zero
      }
    ; *-comm = *-comm
    }

open IsCommutativeSemiringʳ public
  using () renaming (isCommutativeSemiring to isCommutativeSemiringʳ)

------------------------------------------------------------------------
-- IsRing

-- We can recover a ring without proving that 0# annihilates *.
record IsRingWithoutAnnihilatingZero (+ * : Op₂ A) (-_ : Op₁ A) (0# 1# : A)
                                     : Set (a ⊔ ℓ) where
  field
    +-isAbelianGroup : IsAbelianGroup + 0# -_
    *-isMonoid       : IsMonoid * 1#
    distrib          : * DistributesOver +

  private

    module + = IsAbelianGroup +-isAbelianGroup
    module * = IsMonoid *-isMonoid

    open + using (setoid) renaming (∙-cong to +-cong)
    open * using ()       renaming (∙-cong to *-cong)

    zeroˡ : LeftZero 0# *
    zeroˡ = Consequences.assoc+distribʳ+idʳ+invʳ⇒zeˡ setoid
             +-cong *-cong +.assoc (proj₂ distrib) +.identityʳ +.inverseʳ

    zeroʳ : RightZero 0# *
    zeroʳ = Consequences.assoc+distribˡ+idʳ+invʳ⇒zeʳ setoid
             +-cong *-cong +.assoc (proj₁ distrib) +.identityʳ +.inverseʳ

    zero : Zero 0# *
    zero = (zeroˡ , zeroʳ)

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

open IsRingWithoutAnnihilatingZero public
  using () renaming (isRing to isRingWithoutAnnihilatingZero)


module _ where

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

record Top : Set where

_o_ : {A B : Set}{C : Set1} →
      (f : B → C) → (g : A → B) → (A → C)
f o g = \ x → f (g x)

mutual
  data U  : Set where
    top : U
    sig : (X : U) → (T X → U) → U

  T : U → Set
  T top = Top
  T (sig a b) = Sigma (T a) (T o b)


open import Type

module Graph.Properties.Proofs where

open import Data.Either.Proofs
open import Functional
open import Function.Equals
open import Lang.Instance
open import Logic
open import Logic.Propositional
open import Logic.Propositional.Theorems
import      Lvl
open import Graph
open import Graph.Properties
open import Relator.Equals.Proofs.Equiv
open import Structure.Setoid.Uniqueness
open import Structure.Relator.Properties
open import Type.Properties.Singleton

module _ {ℓ₁ ℓ₂} {V : Type{ℓ₁}} (_⟶_ : Graph{ℓ₁}{ℓ₂}(V)) where
  instance
    undirect-undirected : Undirected(undirect(_⟶_))
    Undirected.reversable         undirect-undirected = intro [∨]-symmetry
    Undirected.reverse-involution undirect-undirected = intro (_⊜_.proof swap-involution)

  -- [++]-visits : ∀{ae be a₁ b₁ a₂ b₂}{e : ae ⟶ be}{w₁ : Walk(_⟶_) a₁ b₁}{w₂ : Walk(_⟶_) a₂ b₂} → (Visits(_⟶_) e w₁) ∨ (Visits(_⟶_) e w₂) → Visits(_⟶_) e (w₁ ++ w₂)

  complete-singular-is-undirected : ⦃ CompleteWithLoops(_⟶_) ⦄ → ⦃ Singular(_⟶_) ⦄ → Undirected(_⟶_)
  Undirected.reversable         complete-singular-is-undirected = intro(const (completeWithLoops(_⟶_)))
  Undirected.reverse-involution complete-singular-is-undirected = intro(singular(_⟶_))

  -- traceable-is-connected : ⦃ Traceable(_⟶_) ⦄ → Connected(_⟶_)


{-

Type-level differentiation in Agda; or differentiation of functors!

Most things below are just restatements of elementary category theory.

The new things are Diff, which defines the derivative of an arbitrary
functor from Set to Set, and _+++_ _***_ _∘∘∘_ which show how to
differentiate sums, products and compositions of such functors.

-}

{-# OPTIONS --type-in-type #-}  -- needed in (fun (Diff f) x)
module DiffFunctor where

-- ( syntax )
infix 6 _[_]
infix 5 _∘∘_
infix 5 _∘∘∘_
infix 4 _**_
infix 4 _***_
infix 3 _++_
infix 2 _=>_

-- Numbers are formalised as sets!
Num = Set

-- ( addition of numbers )
data _+_ (a : Num) (b : Num) : Num where
  inl : a -> a + b
  inr : b -> a + b

[_,_] : {a b c : Num} -> (a -> c) -> (b -> c) -> (a + b -> c)
[ a , b ] (inl x) = a x
[ a , b ] (inr x) = b x

map[+] : {a₀ b₀ a₁ b₁ : Num} -> (a₀ -> a₁) -> (b₀ -> b₁) -> (a₀ + b₀ -> a₁ + b₁)
map[+] a b (inl x) = inl (a x)
map[+] a b (inr x) = inr (b x)

-- ( multiplication of numbers )
data _*_ (a : Num) (b : Num) : Num where
  pair : a -> b -> a * b

⟨_,_⟩ : {a b c : Num} -> (c -> a) -> (c -> b) -> (c -> a * b)
⟨ a , b ⟩ x = pair (a x) (b x)

map[*] : {a₀ b₀ a₁ b₁ : Num} -> (a₀ -> a₁) -> (b₀ -> b₁) -> (a₀ * b₀ -> a₁ * b₁)
map[*] a b (pair x y) = pair (a x) (b y)

-- Functions are formalised as functors!
record Fun : Set where
  constructor _[_]
  field
    fun : Num -> Num
    map : {x y : Num} -> (u : x -> y) -> (fun x -> fun y)

open Fun

-- ( pointwise addition of functions )
_++_ : Fun -> Fun -> Fun
fun (f ++ g) x = fun f x + fun g x
map (f ++ g) u = map[+] (map f u) (map g u)

-- ( pointwise multiplication of functions )
_**_ : Fun -> Fun -> Fun
fun (f ** g) x = fun f x * fun g x
map (f ** g) u = map[*] (map f u) (map g u)

-- ( composition of functions )
_∘∘_ : Fun -> Fun -> Fun
fun (f ∘∘ g) x = fun f (fun g x)
map (f ∘∘ g) u = map f (map g u)

-- Differentiation is parametric consumption of a resource!
-- You are supposed to use dx : h precisely once, like in linear logic.
Diff : Fun -> Fun
fun (Diff f) x = {h : Num} -> (dx : h) -> fun f (x + h)
map (Diff f) u f[x+·] dx = map f (map[+] u (\dx -> dx)) (f[x+·] dx)

-- A differentiation rule is a natural transformation! (in reverse)
_=>_ : Fun -> Fun -> Set
f => g = ((x : Num) -> fun f x -> fun g x)
-- (omitting naturality conditions)

-- ( derivative of sum )
_+++_ : (f g : Fun) -> Diff f ++ Diff g => Diff (f ++ g)
(f +++ g) x (inl f[x+·]) dx = inl (f[x+·] dx)
(f +++ g) x (inr g[x+·]) dx = inr (g[x+·] dx)

-- (derivative of product )
_***_ : (f g : Fun) -> Diff f ** g ++ f ** Diff g => Diff (f ** g)
(f *** g) x (inl (pair f[x+·] g[x])) dx = pair (f[x+·] dx) (map g inl g[x])
(f *** g) x (inr (pair f[x] g[x+·])) dx = pair (map f inl f[x]) (g[x+·] dx)

-- ( derivative of composition )
_∘∘∘_ : (f g : Fun) -> Diff f ∘∘ g ** Diff g => Diff (f ∘∘ g)
(f ∘∘∘ g) x (pair f[g[x]+·] g[x+·]) {h} dx = f[g[x+dx]]
  where
    f[g[x+dx]] : fun (f ∘∘ g) (x + h)
    f[g[x+dx]] = map f [ map g inl , g[x+·] ] (f[g[x]+·] dx)

{-

Things that need to be done:
 - Define higher order derivatives as follows:
       HighDiff n f x = forall h. h |n| -> f (x + h)
   Here h |n| stands for n copies of the resource h. Try to show
       n! -> Diff^n f => HighDiff n f.
 - Explore differential calculus in several variables.
 - Explore integration and differential equations. (hard)
 - Solve partial differential equations. (good luck)
 - Explore inverse functions. (hard)
 - Write some examples, such as
       0 => Diff (Const a)
       1 => Diff Id
       n * Id^(n-1) => Diff Id^n
       List ** List => Diff List
       derivatives of linear transformations

-}


module Cats.Profunctor where

open import Data.Product using (_,_)
open import Level using (suc ; _⊔_)

open import Cats.Category
open import Cats.Category.Op using (_ᵒᵖ)
open import Cats.Category.Product.Binary using (_×_)
open import Cats.Category.Setoids using (Setoids)
open import Cats.Functor

import Cats.Category.Cat as Cat
import Cats.Category.Cat.Facts.Product as Cat

-- The usual definition inverts C and D, so
--
--   Profunctor C D E = Functor (Dᵒᵖ × C) E
--
-- but that's just confusing.
Profunctor : ∀ {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″}
  → Category lo la l≈
  → Category lo′ la′ l≈′
  → Category lo″ la″ l≈″
  → Set (lo ⊔ la ⊔ l≈ ⊔ lo′ ⊔ la′ ⊔ l≈′ ⊔ lo″ ⊔ la″ ⊔ l≈″)
Profunctor C D E = Functor ((C ᵒᵖ) × D) E

module Profunctor {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″}
  {C : Category lo la l≈}
  {D : Category lo′ la′ l≈′}
  {E : Category lo″ la″ l≈″}
  (F : Profunctor C D E)
  where

  private
    module C = Category C
    module D = Category D
    module E = Category E

  pobj : C.Obj → D.Obj → E.Obj
  pobj c d = fobj F (c , d)

  pmap : ∀ {c c′} (f : c′ C.⇒ c) {d d′} (g : d D.⇒ d′)
    → pobj c d E.⇒ pobj c′ d′
  pmap f g = fmap F (f , g)

  pmap₁ : ∀ {c c′ d} (f : c′ C.⇒ c)
    → pobj c d E.⇒ pobj c′ d
  pmap₁ f = pmap f D.id

  pmap₂ : ∀ {c d d′} (g : d D.⇒ d′)
    → pobj c d E.⇒ pobj c d′
  pmap₂ g = pmap C.id g

open Profunctor public

Functor→Profunctor₁ : ∀ {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″}
  → {C : Category lo la l≈} {D : Category lo′ la′ l≈′} {X : Category lo″ la″ l≈″}
  → Functor (C ᵒᵖ) D
  → Profunctor C X D
Functor→Profunctor₁ F = F Cat.∘ Cat.proj₁

Functor→Profunctor₂ : ∀ {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″}
  → {C : Category lo la l≈} {D : Category lo′ la′ l≈′} {X : Category lo″ la″ l≈″}
  → Functor C D
  → Profunctor X C D
Functor→Profunctor₂ F = F Cat.∘ Cat.proj₂


module Dual where

open import Prelude using (flip)
open import Logic.Equivalence
open import Category

_op : Cat -> Cat
ℂ@(cat _ _ _ _ _ _ _ _ _) op = cat Obj
	   (\A B -> B ─→ A)
	    id
	   (\{_}{_}{_} -> flip _∘_)
	   (\{_}{_} -> Eq)
	   (\{_}{_}{_}{_}{_}{_}{_} -> flip cong)
	   (\{_}{_}{_} -> idR)
	   (\{_}{_}{_} -> idL)
	   (\{_}{_}{_}{_}{_}{_}{_} -> sym assoc)
  where open module C = Cat ℂ

{-
open Poly-Cat

dualObj : {ℂ : Cat} -> Obj ℂ -> Obj (ℂ op)
dualObj {cat _ _ _ _ _ _ _ _ _}(obj A) = obj A

undualObj : {ℂ : Cat} -> Obj (ℂ op) -> Obj ℂ
undualObj {cat _ _ _ _ _ _ _ _ _}(obj A) = obj A

dualdualArr : {ℂ : Cat}{A B : Obj ℂ} -> A ─→ B -> dualObj B ─→ dualObj A
dualdualArr {cat _ _ _ _ _ _ _ _ _}{A = obj _}{B = obj _}(arr f) = arr f

dualundualArr : {ℂ : Cat}{A : Obj ℂ}{B : Obj (ℂ op)} ->
		A ─→ undualObj B -> B ─→ dualObj A
dualundualArr {cat _ _ _ _ _ _ _ _ _}{A = obj _}{B = obj _}(arr f) = arr f
-}


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

open import HoTT
open import groups.KernelImage

module cohomology.ChainComplex where

  record ChainComplex i : Type (lsucc i) where
    field
      head : AbGroup i
      chain : ℕ → AbGroup i
      augment : AbGroup.grp (chain 0) →ᴳ AbGroup.grp head
      boundary : ∀ n → (AbGroup.grp (chain (S n)) →ᴳ AbGroup.grp (chain n))

  record CochainComplex i : Type (lsucc i) where
    field
      head : AbGroup i
      cochain : ℕ → AbGroup i
      augment : AbGroup.grp head →ᴳ AbGroup.grp (cochain 0)
      coboundary : ∀ n → (AbGroup.grp (cochain n) →ᴳ AbGroup.grp (cochain (S n)))

  homology-group : ∀ {i} → ChainComplex i
    → (n : ℤ) → Group i
  homology-group cc (pos 0) = Ker/Im cc.augment (cc.boundary 0) (snd (cc.chain 0))
    where module cc = ChainComplex cc
  homology-group cc (pos (S n)) = Ker/Im (cc.boundary n) (cc.boundary (S n)) (snd (cc.chain (S n)))
    where module cc = ChainComplex cc
  homology-group {i} cc (negsucc _) = Lift-group {j = i} Unit-group

  cohomology-group : ∀ {i} → CochainComplex i
    → (n : ℤ) → Group i
  cohomology-group cc (pos 0) = Ker/Im (cc.coboundary 0) cc.augment (snd (cc.cochain 0))
    where module cc = CochainComplex cc
  cohomology-group cc (pos (S n)) = Ker/Im (cc.coboundary (S n)) (cc.coboundary n) (snd (cc.cochain (S n)))
    where module cc = CochainComplex cc
  cohomology-group {i} cc (negsucc _) = Lift-group {j = i} Unit-group

  complex-dualize : ∀ {i j} → ChainComplex i → AbGroup j
    →  CochainComplex (lmax i j)
  complex-dualize {i} {j} cc G = record {M} where
    module cc = ChainComplex cc
    module M where
      head : AbGroup (lmax i j)
      head = hom-abgroup (AbGroup.grp cc.head) G

      cochain : ℕ → AbGroup (lmax i j)
      cochain n = hom-abgroup (AbGroup.grp (cc.chain n)) G

      augment : AbGroup.grp head →ᴳ AbGroup.grp (cochain 0)
      augment = pre∘ᴳ-hom G cc.augment

      coboundary : ∀ n → (AbGroup.grp (cochain n) →ᴳ AbGroup.grp (cochain (S n)))
      coboundary n = pre∘ᴳ-hom G (cc.boundary n)


open import Numeral.Natural
open import Type

module Formalization.ClassicalPredicateLogic.Syntax {ℓₚ ℓᵥ ℓₒ} (Prop : ℕ → Type{ℓₚ}) (Var : Type{ℓᵥ}) (Obj : ℕ → Type{ℓₒ}) where

open import Data.ListSized
import      Lvl
open import Functional using (_∘_ ; _∘₂_ ; swap)
open import Sets.PredicateSet using (PredSet)

private variable ℓ : Lvl.Level
private variable n : ℕ

data Term : Type{ℓᵥ Lvl.⊔ ℓₒ} where
  var  : Var → Term                -- Variables
  func : Obj(n) → List(Term)(n) → Term -- Constants/functions

-- Formulas.
-- Inductive definition of the grammatical elements of the language of predicate logic.
data Formula : Type{ℓₚ Lvl.⊔ ℓᵥ Lvl.⊔ ℓₒ} where
  _$_ : Prop(n) → List(Term)(n) → Formula -- Relations

  ⊤ : Formula -- Tautology (Top / True)
  ⊥ : Formula -- Contradiction (Bottom / False)

  _∧_ : Formula → Formula → Formula -- Conjunction (And)
  _∨_ : Formula → Formula → Formula -- Disjunction (Or)
  _⟶_ : Formula → Formula → Formula -- Implication

  Ɐ : Var → Formula → Formula
  ∃ : Var → Formula → Formula

infix  1011 _$_
infixr 1005 _∧_
infixr 1004 _∨_
infixr 1000 _⟶_

-- Negation
¬_ : Formula → Formula
¬_ = _⟶ ⊥

-- Double negation
¬¬_ : Formula → Formula
¬¬_ = (¬_) ∘ (¬_)

-- Reverse implication
_⟵_ : Formula → Formula → Formula
_⟵_ = swap(_⟶_)

-- Equivalence
_⟷_ : Formula → Formula → Formula
p ⟷ q = (p ⟵ q) ∧ (p ⟶ q)

-- (Nor)
_⊽_ : Formula → Formula → Formula
_⊽_ = (¬_) ∘₂ (_∨_)

-- (Nand)
_⊼_ : Formula → Formula → Formula
_⊼_ = (¬_) ∘₂ (_∧_)

-- (Exclusive or / Xor)
_⊻_ : Formula → Formula → Formula
_⊻_ = (¬_) ∘₂ (_⟷_)

infix  1010 ¬_ ¬¬_
infixl 1000 _⟵_ _⟷_


module DerivedProps where

{-
open import Relation.Binary.PropositionalEquality
open import Data.Nat hiding (_>_)
-}

open import StdLibStuff

open import Syntax
open import STT

lem3 : {n : ℕ} {Γ-t : Ctx n} (F G : Form Γ-t $o) → ⊢ ((F => (F => G)) => (F => G))
lem3 F G = inf-V (inf-V ax-4-s ax-1-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s ax-2-s)) ax-3-s)

lem4 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
       ⊢ ((F => (G => H)) => (G => (F => H)))
lem4 F G H = inf-V (inf-V ax-4-s ax-1-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V (inf-V ax-4-s (inf-V (inf-V ax-4-s ax-3-s) ax-2-s)) ax-2-s))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V ax-4-s (inf-V ax-4-s (inf-V (inf-V ax-4-s ax-3-s) ax-2-s)))))

lem5 : {n : ℕ} {Γ-t : Ctx n} (F G : Form Γ-t $o) →
       ⊢ ((F => G) => (~ G => ~ F))
lem5 F G = inf-V (inf-V ax-4-s ax-3-s) (inf-V ax-4-s (inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s)))

lem6 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
       ⊢ ((F => H) => ((F & G) => H))
lem6 F G H = inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V (inf-V ax-4-s (inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s))) ax-2-s))) ax-3-s)

lem6b : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
       ⊢ ((G => H) => ((F & G) => H))
lem6b F G H = inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V (inf-V ax-4-s (inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s))) (inf-V (inf-V ax-4-s ax-3-s) ax-2-s)))) ax-3-s)

lemb3 : {n : ℕ} {Γ-t : Ctx n} (F G : Form Γ-t $o) →
        ⊢ (F => ((F => G) => G))
lemb3 F G = inf-V ax-1-s (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s ax-2-s) (inf-V (inf-V ax-4-s ax-2-s) (inf-V ax-3-s (inf-V ax-4-s (inf-V (inf-V ax-4-s ax-3-s) ax-2-s))))))

lem7 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
       ⊢ (F => ((G => H) => ((F => G) => H)))
lem7 F G H = inf-V (inf-V ax-4-s (lem4 (F => G) (G => H) H)) (inf-V (inf-V ax-4-s (inf-V ax-4-s (lemb3 G H))) (lemb3 F G))

lem8h : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
        ⊢ ((H => (F => G)) => ((H => F) => (H => G)))
lem8h F G H = inf-V (inf-V ax-4-s ax-1-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s ax-2-s) ax-2-s)))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V ax-4-s ax-4-s)))

lem8 : {n : ℕ} {Γ-t : Ctx n} (X F G H : Form Γ-t $o) →
       ⊢ ((F => (G => H)) => ((X => F) => ((X => G) => (X => H))))
lem8 X F G H = inf-V (inf-V ax-4-s (inf-V ax-4-s (lem8h G H X))) (inf-V (inf-V ax-4-s (lem8h F (G => H) X)) (inf-V (lem4 X (F => (G => H)) (F => (G => H))) (inf-V ax-3-s (inf-V ax-2-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s)))))

lem8-3 : {n : ℕ} {Γ-t : Ctx n} (X F G H I : Form Γ-t $o) →
       ⊢ ((F => (G => (H => I))) => ((X => F) => ((X => G) => ((X => H) => (X => I)))))
lem8-3 X F G H I = inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V ax-4-s (lem8h H I X)))) (inf-V (inf-V ax-4-s (inf-V ax-4-s (lem8h G (H => I) X))) (inf-V (inf-V ax-4-s (lem8h F (G => (H => I)) X)) (inf-V (lem4 X (F => (G => (H => I))) (F => (G => (H => I)))) (inf-V ax-3-s (inf-V ax-2-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s))))))

lemb2 : {n : ℕ} {Γ-t : Ctx n} (F : Form Γ-t $o) →
        ⊢ (~ (~ F) => F)
lemb2 F = inf-V ax-3-s (inf-V (inf-V ax-4-s (inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s))) (inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s)))

lem2h2h : {n : ℕ} {Γ-t : Ctx n} (F : Form Γ-t $o) →
          ⊢ (F => ~ (~ F))
lem2h2h F = inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s)

lem2h2hb : {n : ℕ} {Γ-t : Ctx n} (F : Form Γ-t $o) →
           ⊢ (F => F)
lem2h2hb F = inf-V (inf-V ax-4-s ax-1-s) ax-2-s

lem2h2 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
         ⊢ ((F || (G || H)) => ((F || G) || H))
lem2h2 F G H = inf-V (inf-V ax-4-s (inf-V ax-4-s (lemb2 H))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s ax-3-s)) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V ax-4-s (lemb2 F)))) (inf-V (inf-V ax-4-s (inf-V ax-4-s ax-3-s)) (inf-V (inf-V ax-4-s (lem4 (~ F) (~ H) G)) (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (lem2h2h F))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s ax-3-s)) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V ax-4-s (lem2h2h H)))) (lem2h2hb (F || (G || H)))))))))))))

lem2h1 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
         ⊢ ((F => (G => H)) => ((F & G) => H))
lem2h1 F G H = inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s)))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (lem2h2 (~ F) (~ G) H)) (inf-V (inf-V ax-4-s ax-1-s) ax-2-s))))

lemb1 : {n : ℕ} {Γ-t : Ctx n} (F G : Form Γ-t $o) →
        ⊢ (F => (G => (F & G)))
lemb1 F G = inf-V ax-1-s (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s ax-2-s) (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s ax-2-s) (inf-V ax-3-s (inf-V ax-4-s ax-2-s))))))

lem2 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) → ⊢ (((F => G) & (F => H)) => (F => (G & H)))
lem2 F G H = inf-V (lem2h1 (F => G) (F => H) (F => (G & H))) (inf-V (lem8 F G H (G & H)) (lemb1 G H))

lem9 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
       ⊢ ((F => G) => ((G => H) => (F => H)))
lem9 F G H = inf-V (inf-V ax-4-s (lem4 F (G => H) H)) (inf-V (inf-V ax-4-s (inf-V ax-4-s (lemb3 G H))) (inf-V (lem4 F (F => G) G) (lemb3 F G)))

lemb4 : {n : ℕ} {Γ-t : Ctx n} (F G : Form Γ-t $o) →
        ⊢ (F => (~ F => G))
lemb4 F G = inf-V (inf-V ax-4-s ax-2-s) (inf-V ax-3-s (inf-V (inf-V ax-4-s ax-1-s) ax-2-s))

lem5r : {n : ℕ} {Γ-t : Ctx n} (F G : Form Γ-t $o) →
       ⊢ ((~ F => ~ G) => (G => F))
lem5r F G = inf-V (inf-V ax-4-s (inf-V ax-4-s (lemb2 F))) (inf-V (inf-V ax-4-s ax-3-s) (lem2h2hb (~ F => ~ G)))

lemb5 : {n : ℕ} {Γ-t : Ctx n} (F G H : Form Γ-t $o) →
        ⊢ ((F => H) => ((G => H) => ((F || G) => H)))
lemb5 F G H = inf-V (inf-V ax-4-s (inf-V ax-4-s (lem5r H (F || G)))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V (lem5 (G => H) (~ H => ~ G)) (lem5 G H)))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V ax-3-s (inf-V (inf-V ax-4-s (inf-V (lem5 (F => H) (~ H => ~ F)) (lem5 F H))) (inf-V ax-3-s (inf-V (lem8 (~ H) (~ F) (~ G) (~ (F || G))) (inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V (lem5 (F || G) (~ (~ F) || ~ (~ G))) (inf-V (inf-V ax-4-s (inf-V ax-4-s (lem2h2h G))) (inf-V (inf-V ax-4-s ax-3-s) (inf-V (inf-V ax-4-s (inf-V ax-4-s (lem2h2h F))) (inf-V (inf-V ax-4-s ax-3-s) (lem2h2hb (F || G))))))))) (lemb1 (~ F) (~ G))))))))))

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

lem10 : {n : ℕ} {Γ-t : Ctx n} (F : Form Γ-t $o) →
        ⊢ ((F => $false) => ~ F)
lem10 F = inf-V ax-3-s (inf-V (inf-V ax-4-s (inf-V (lem5 (F => $false) (F => ~ F)) (inf-V ax-4-s (inf-V (inf-V ax-4-s (ax-5-s ($ this {refl}) (~ F))) (lem2h2hb (![ _ ] ($ this {refl}))))))) (inf-V ax-3-s ax-1-s))

lem11 : {n : ℕ} {Γ-t : Ctx n} (F : Form Γ-t $o) →
        ⊢ ($false => F)
lem11 F = inf-V (inf-V ax-4-s (ax-5-s ($ this {refl}) F)) (lem2h2hb (![ _ ] ($ this {refl})))

lem12 : {n : ℕ} {Γ-t : Ctx n} {t : Type n} (F : Form Γ-t t) →
        ⊢ (F == F)
lem12 {n} {Γ-t} {t} F = inf-V (inf-III-samectx {n} {Γ-t} {t} {t > $o} (λ z → (z · F)) (^[ t ] (![ t > $o ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) F) (inf-V (inf-III-samectx {n} {Γ-t} {t} {$o} (λ z → (z)) (![ t > $o ] (($ this {refl} · weak (weak F)) => ($ this {refl} · $ (next this) {refl}))) F) (inf-VI-s (subst (λ z → ⊢ ((($ this {refl} · z) => ($ this {refl} · weak F)))) (sub-weak-p-1 F F) (inf-V (inf-V ax-4-s ax-1-s) ax-2-s))))

lem13 : {n : ℕ} {Γ-t : Ctx n} (F G : Form Γ-t $o) →
        ⊢ ((G == F) => (F => G))
lem13 F G =
 inf-V (inf-III-samectx (λ z → ((z · F) => (F => G))) (^[ $o ] (![ $o > $o ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) G) (
 inf-V (inf-III-samectx (λ z → (z => (F => G))) (![ $o > $o ] (($ this {refl} · weak (weak G)) => ($ this {refl} · $ (next this) {refl}))) F) (
 subst (λ z → ⊢ ((![ $o > $o ] (($ this {refl} · z) => ($ this {refl} · weak F))) => (F => G))) (sub-weak-p-1 G F) (
 inf-V ax-3-s (
 inf-V (inf-V ax-4-s (inf-V (lem5 (![ $o > $o ] (($ this {refl} · weak G) => ($ this {refl} · weak F))) (((^[ $o ] ~ ($ this {refl})) · sub (^[ $o ] ~ ($ this {refl})) (weak G)) => ((^[ $o ] ~ ($ this {refl})) · sub (^[ $o ] ~ ($ this {refl})) (weak F))))
  (ax-5-s (($ this {refl} · weak G) => ($ this {refl} · weak F)) (^[ $o ] ~ ($ this {refl}))))) (
 inf-V ax-3-s (
 subst (λ z → ⊢ ((((^[ $o ] ~ ($ this {refl})) · z) => ((^[ $o ] ~ ($ this {refl})) · sub (^[ $o ] ~ ($ this {refl})) (weak F))) => (F => G))) (sub-weak-p-1' G (^[ $o ] ~ ($ this {refl}))) (
 subst (λ z → ⊢ ((((^[ $o ] ~ ($ this {refl})) · G) => ((^[ $o ] ~ ($ this {refl})) · z)) => (F => G))) (sub-weak-p-1' F (^[ $o ] ~ ($ this {refl}))) (
 inf-V (inf-III-samectx (λ z → ((z => ((^[ $o ] ~ ($ this {refl})) · F)) => (F => G))) (~ ($ this {refl})) G) (
 inf-V (inf-III-samectx (λ z → (((~ G) => z) => (F => G))) (~ ($ this {refl})) F) (
 lem5r G F
 ))))))))))

transitivity : {n : ℕ} {Γ-t : Ctx n} {t : Type n} (F G H : Form Γ-t t) →
         ⊢ ((F == G) => ((G == H) => (F == H)))
transitivity F G H =
 inf-V (inf-III-samectx (λ z → ((z · G) => ((G == H) => (F == H)))) (^[ _ ] (![ _ > $o ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) F) (
 inf-V (inf-III-samectx (λ z → (z => ((G == H) => (F == H)))) (![ _ > $o ] (($ this {refl} · weak (weak F)) => ($ this {refl} · $ (next this) {refl}))) G) (
 subst (λ z → ⊢ ((![ _ > $o ] (($ this {refl} · z) => ($ this {refl} · weak G))) => ((G == H) => (F == H)))) (sub-weak-p-1 F G) (
 inf-V (inf-III-samectx (λ z → ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => ((z · H) => (F == H)))) (^[ _ ] (![ _ > $o ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) G) (
 inf-V (inf-III-samectx (λ z → ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => (z => (F == H)))) (![ _ > $o ] (($ this {refl} · weak (weak G)) => ($ this {refl} · $ (next this) {refl}))) H) (
 subst (λ z → ⊢ ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => ((![ _ > $o ] (($ this {refl} · z) => ($ this {refl} · weak H))) => (F == H)))) (sub-weak-p-1 G H) (
 inf-V (inf-III-samectx (λ z → ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => ((![ _ > $o ] (($ this {refl} · weak G) => ($ this {refl} · weak H))) => (z · H)))) (^[ _ ] (![ _ > $o ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) F) (
 inf-V (inf-III-samectx (λ z → ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => ((![ _ > $o ] (($ this {refl} · weak G) => ($ this {refl} · weak H))) => z))) (![ _ > $o ] (($ this {refl} · weak (weak F)) => ($ this {refl} · $ (next this) {refl}))) H) (
 subst (λ z → ⊢ ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => ((![ _ > $o ] (($ this {refl} · weak G) => ($ this {refl} · weak H))) => (![ _ > $o ] (($ this {refl} · z) => ($ this {refl} · weak H)))))) (sub-weak-p-1 F H) (
 inf-V (inf-V ax-4-s (ax-6-s {_} {_} {_} {~ (![ _ ] (($ this {refl} · weak G) => ($ this {refl} · weak H)))} {($ this {refl} · weak F) => ($ this {refl} · weak H)})) (
 inf-V (ax-6-s {_} {_} {_} {~ (![ _ ] (($ this {refl} · weak F) => ($ this {refl} · weak G)))} {(weak (![ _ ] (($ this {refl} · weak G) => ($ this {refl} · weak H)))) => (($ this {refl} · weak F) => ($ this {refl} · weak H))}) (
 inf-VI-s (
 inf-V ax-3-s (
 inf-V (inf-V ax-4-s (inf-V (lem5 (![ _ ] (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G)))) (sub ($ this {refl}) (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G)))))
  (ax-5-s (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G))) ($ this {refl})))) (
 inf-V ax-3-s (
 inf-V (inf-V ax-4-s ax-3-s) (
 inf-V (inf-V ax-4-s (inf-V ax-4-s (inf-V (lem5 (![ _ ] (($ this {refl} · weak-i (_ ∷ ε) _ (weak G)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak H)))) (sub ($ this {refl}) (($ this {refl} · weak-i (_ ∷ ε) _ (weak G)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak H)))))
  (ax-5-s (($ this {refl} · weak-i (_ ∷ ε) _ (weak G)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak H))) ($ this {refl}))))) (
 inf-V (inf-V ax-4-s ax-3-s) (
 subst (λ z → ⊢ (((($ this {refl}) · z) => (($ this {refl}) · (sub ($ this {refl}) (weak-i (_ ∷ ε) _ (weak G))))) => (((($ this {refl}) · (sub ($ this {refl}) (weak-i (_ ∷ ε) _ (weak G)))) => (($ this {refl}) · (sub ($ this {refl}) (weak-i (_ ∷ ε) _ (weak H))))) => (($ this {refl} · weak F) => ($ this {refl} · weak H))))) (sub-weak-p-23 F ($ this {refl})) (
 subst (λ z → ⊢ (((($ this {refl}) · (weak F)) => (($ this {refl}) · z)) => (((($ this {refl}) · (sub ($ this {refl}) (weak-i (_ ∷ ε) _ (weak G)))) => (($ this {refl}) · (sub ($ this {refl}) (weak-i (_ ∷ ε) _ (weak H))))) => (($ this {refl} · weak F) => ($ this {refl} · weak H))))) (sub-weak-p-23 G ($ this {refl})) (
 subst (λ z → ⊢ (((($ this {refl}) · (weak F)) => (($ this {refl}) · (weak G))) => (((($ this {refl}) · z) => (($ this {refl}) · (sub ($ this {refl}) (weak-i (_ ∷ ε) _ (weak H))))) => (($ this {refl} · weak F) => ($ this {refl} · weak H))))) (sub-weak-p-23 G ($ this {refl})) (
 subst (λ z → ⊢ (((($ this {refl}) · (weak F)) => (($ this {refl}) · (weak G))) => (((($ this {refl}) · (weak G)) => (($ this {refl}) · z)) => (($ this {refl} · weak F) => ($ this {refl} · weak H))))) (sub-weak-p-23 H ($ this {refl})) (
 lem9 ($ this {refl} · weak F) ($ this {refl} · weak G) ($ this {refl} · weak H)
 ))))))))))))))))))))))

lem14 : {n : ℕ} {Γ-t : Ctx n} {t : Type n} (F G H I : Form Γ-t t) →
        ⊢ ((F == G) => ((G == H) => ((H == I) => (F == I))))
lem14 F G H I = inf-V (inf-V ax-4-s (inf-V ax-4-s (transitivity F H I))) (transitivity F G H)

lem15 : {n : ℕ} {Γ-t : Ctx n} {t : Type n} (F G : Form Γ-t t) →
        ⊢ ((F == G) => (G == F))
lem15 F G =
 inf-V (inf-III-samectx (λ z → ((z · G) => (G == F))) (^[ _ ] (![ _ > $o ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) F) (
 inf-V (inf-III-samectx (λ z → (z => (G == F))) (![ _ > $o ] (($ this {refl} · weak (weak F)) => ($ this {refl} · $ (next this) {refl}))) G) (
 subst (λ z → ⊢ ((![ _ > $o ] (($ this {refl} · z) => ($ this {refl} · weak G))) => (G == F))) (sub-weak-p-1 F G) (
 inf-V (inf-III-samectx (λ z → ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => (z · F))) (^[ _ ] (![ _ > $o ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) G) (
 inf-V (inf-III-samectx (λ z → ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => z)) (![ _ > $o ] (($ this {refl} · weak (weak G)) => ($ this {refl} · $ (next this) {refl}))) F) (
 subst (λ z → ⊢ ((![ _ > $o ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => (![ _ > $o ] (($ this {refl} · z) => ($ this {refl} · weak F))))) (sub-weak-p-1 G F) (
 inf-V (ax-6-s {_} {_} {_} {~ (![ _ ] (($ this {refl} · weak F) => ($ this {refl} · weak G)))} {($ this {refl} · weak G) => ($ this {refl} · weak F)}) (
 inf-VI-s (
 inf-V ax-3-s (
 inf-V (inf-V ax-4-s (inf-V (lem5 (![ _ ] (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G)))) (sub (^[ _ ] ~ ($ (next this) {refl} · $ this {refl})) (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G)))))
  (ax-5-s (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G))) (^[ _ ] ~ ($ (next this) {refl} · $ this {refl}))))) (
 inf-V ax-3-s (
 subst (λ z → ⊢ ((((^[ _ ] ~ ($ (next this) {refl} · $ this {refl})) · z) => ((^[ _ ] ~ ($ (next this) {refl} · $ this {refl})) · (sub (^[ _ ] ~ ($ (next this) {refl} · $ this {refl})) (weak-i (_ ∷ ε) _ (weak G))))) => (($ this {refl} · weak G) => ($ this {refl} · weak F)))) (sub-weak-p-23 F (^[ _ ] ~ ($ (next this) {refl} · $ this {refl}))) (
 subst (λ z → ⊢ ((((^[ _ ] ~ ($ (next this) {refl} · $ this {refl})) · (weak F)) => ((^[ _ ] ~ ($ (next this) {refl} · $ this {refl})) · z)) => (($ this {refl} · weak G) => ($ this {refl} · weak F)))) (sub-weak-p-23 G (^[ _ ] ~ ($ (next this) {refl} · $ this {refl}))) (
 inf-V (inf-III-samectx (λ z → ((z => ((^[ _ ] ~ ($ (next this) {refl} · $ this {refl})) · weak G)) => (($ this {refl} · weak G) => ($ this {refl} · weak F)))) (~ ($ (next this) {refl} · $ this {refl})) (weak F)) (
 inf-V (inf-III-samectx (λ z → (((~ ($ this {refl} · weak F)) => z) => (($ this {refl} · weak G) => ($ this {refl} · weak F)))) (~ ($ (next this) {refl} · $ this {refl})) (weak G)) (
 lem5r ($ this {refl} · weak F) ($ this {refl} · weak G)
 )))))))))))))))

substitutivity : {n : ℕ} {Γ-t : Ctx n} {t u : Type n} (F G : Form Γ-t t) (H : Form Γ-t (t > u)) →
        ⊢ ((F == G) => ((H · F) == (H · G)))
substitutivity F G H =
 inf-V (inf-III-samectx (λ z → ((z · G) => ((H · F) == (H · G)))) (^[ _ ] (![ _ > _ ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) F) (
 inf-V (inf-III-samectx (λ z → (z => ((H · F) == (H · G)))) (![ _ > _ ] (($ this {refl} · weak (weak F)) => ($ this {refl} · $ (next this) {refl}))) G) (
 subst (λ z → ⊢ ((![ _ > _ ] (($ this {refl} · z) => ($ this {refl} · weak G))) => ((H · F) == (H · G)))) (sub-weak-p-1 F G) (
 inf-V (inf-III-samectx (λ z → ((![ _ > _ ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => (z · (H · G)))) (^[ _ ] (![ _ > _ ] (($ this {refl} · $ (next (next this)) {refl}) => ($ this {refl} · $ (next this) {refl})))) ((H · F))) (
 inf-V (inf-III-samectx (λ z → ((![ _ > _ ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => z)) (![ _ > _ ] (($ this {refl} · weak (weak ((H · F)))) => ($ this {refl} · $ (next this) {refl}))) ((H · G))) (
 subst (λ z → ⊢ ((![ _ > _ ] (($ this {refl} · weak F) => ($ this {refl} · weak G))) => (![ _ > _ ] (($ this {refl} · z) => ($ this {refl} · weak ((H · G))))))) (sub-weak-p-1 ((H · F)) ((H · G))) (
 inf-V (ax-6-s {_} {_} {_} {~ (![ _ ] (($ this {refl} · weak F) => ($ this {refl} · weak G)))} {($ this {refl} · weak ((H · F))) => ($ this {refl} · weak ((H · G)))}) (
 inf-VI-s (
 inf-V ax-3-s (
 inf-V (inf-V ax-4-s (inf-V (lem5 (![ _ ] (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G)))) (sub (^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G)))))
  (ax-5-s (($ this {refl} · weak-i (_ ∷ ε) _ (weak F)) => ($ this {refl} · weak-i (_ ∷ ε) _ (weak G))) (^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl})))))) (
 inf-V ax-3-s (
 subst (λ z → ⊢ ((((^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) · z) => ((^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) · (sub (^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) (weak-i (_ ∷ ε) _ (weak G))))) => (($ this {refl} · weak (H · F)) => ($ this {refl} · weak (H · G))))) {-{weak F} {sub (^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) (weak-i (_ ∷ ε) _ (weak F))}-} (sub-weak-p-23 F (^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl})))) (
  subst (λ z → ⊢ ((((^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) · (weak F)) => ((^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) · z)) => (($ this {refl} · weak (H · F)) => ($ this {refl} · weak (H · G))))) {-{weak G} {sub (^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) (weak-i (_ ∷ ε) _ (weak G))}-} (sub-weak-p-23 G (^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl})))) (
 inf-V (inf-III-samectx (λ z → ((z => ((^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) · weak G)) => (($ this {refl} · weak (H · F)) => ($ this {refl} · weak (H · G))))) ($ (next this) {refl} · (weak (weak H) · $ this {refl})) (weak F)) (
 subst (λ z → ⊢ ((($ this {refl} · (z · weak F)) => ((^[ _ ] ($ (next this) {refl} · (weak (weak H) · $ this {refl}))) · weak G)) => (($ this {refl} · weak (H · F)) => ($ this {refl} · weak (H · G))))) {-{weak H} {sub (weak F) (weak (weak H))}-} (sub-weak-p-1' (weak H) (weak F)) (
 inf-V (inf-III-samectx (λ z → ((($ this {refl} · weak (H · F)) => z) => (($ this {refl} · weak (H · F)) => ($ this {refl} · weak (H · G))))) ($ (next this) {refl} · (weak (weak H) · $ this {refl})) (weak G)) (
 subst (λ z → ⊢ ((($ this {refl} · (weak (H · F))) => ($ this {refl} · (z · weak G))) => (($ this {refl} · weak (H · F)) => ($ this {refl} · weak (H · G))))) {-{weak H} {sub (weak G) (weak (weak H))}-} (sub-weak-p-1' (weak H) (weak G)) (
 lem2h2hb (($ this {refl} · weak (H · F)) => ($ this {refl} · (weak H · weak G)))
 )))))))))))))))))

lem16 : {n : ℕ} {Γ-t : Ctx n} {α β : Type n} (F₁ F₂ : Form Γ-t (α > β)) (G₁ G₂ : Form Γ-t α) →
        ⊢ ((F₁ == F₂) => ((G₁ == G₂) => ((F₁ · G₁) == (F₂ · G₂))))
lem16 F₁ F₂ G₁ G₂ =
 inf-V (inf-V (inf-V (lem8 (F₁ == F₂) ((G₁ == G₂) => ((F₁ · G₁) == (F₂ · G₁))) ((G₁ == G₂) => ((F₂ · G₁) == (F₂ · G₂))) ((G₁ == G₂) => ((F₁ · G₁) == (F₂ · G₂))))
 (inf-V (lem8 (G₁ == G₂) ((F₁ · G₁) == (F₂ · G₁)) ((F₂ · G₁) == (F₂ · G₂)) ((F₁ · G₁) == (F₂ · G₂))) (
  transitivity (F₁ · G₁) (F₂ · G₁) (F₂ · G₂))))
 (inf-V (lem4 (G₁ == G₂) (F₁ == F₂) ((F₁ · G₁) == (F₂ · G₁))) (inf-V ax-3-s (inf-V ax-2-s (
  subst (λ z → ⊢ ((F₁ == F₂) => ((F₁ · G₁) == (F₂ · z)))) (sym (sub-weak-p-1' G₁ F₂)) (
  inf-V (inf-II-samectx (λ z → ((F₁ == F₂) => ((F₁ · G₁) == z))) (($ this {refl}) · weak G₁) F₂) (
  subst (λ z → ⊢ ((F₁ == F₂) => ((F₁ · z) == ((^[ _ ] (($ this {refl}) · weak G₁)) · F₂)))) (sym (sub-weak-p-1' G₁ F₁)) (
  inf-V (inf-II-samectx (λ z → ((F₁ == F₂) => (z == ((^[ _ ] (($ this {refl}) · weak G₁)) · F₂)))) (($ this {refl}) · weak G₁) F₁) (
  substitutivity F₁ F₂ (^[ _ ] (($ this {refl}) · weak G₁))
 )))))))))
 (inf-V ax-3-s (inf-V ax-2-s (
  subst (λ z → ⊢ ((G₁ == G₂) => ((F₂ · G₁) == (z · G₂)))) (sym (sub-weak-p-1' F₂ G₂)) (
  inf-V (inf-II-samectx (λ z → ((G₁ == G₂) => ((F₂ · G₁) == z))) (weak F₂ · ($ this {refl})) G₂) (
  subst (λ z → ⊢ ((G₁ == G₂) => ((z · G₁) == ((^[ _ ] (weak F₂ · ($ this {refl}))) · G₂)))) (sym (sub-weak-p-1' F₂ G₁)) (
  inf-V (inf-II-samectx (λ z → ((G₁ == G₂) => (z == ((^[ _ ] (weak F₂ · ($ this {refl}))) · G₂)))) (weak F₂ · ($ this {refl})) G₁) (
  substitutivity G₁ G₂ (^[ _ ] (weak F₂ · ($ this {refl})))
 )))))))


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

module SEquivSCPermEquiv where

open import Level        using (Level; _⊔_)
open import Data.Fin     using (Fin)
open import Data.Product using (_,_; proj₁; proj₂)
open import Data.Vec     using (tabulate)
open import Function     using (_∘_; id)

open import Data.Vec.Properties using    (lookup∘tabulate)
open import Relation.Binary     using    (Setoid)
open import Function.Equality   using    (_⟨$⟩_; _⟶_)
                                renaming (_∘_ to _⊚_)

open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; sym; trans; cong; module ≡-Reasoning)

--

open import Proofs using (_!!_; finext)
open import Equiv using (_≃_; qinv; module isqinv)
open import EquivEquiv using (_S≃_; eq; _≋_; id≋)
open import ConcretePermutation using (CPerm; cp; _∘̂_; 1C; cauchyext; SCPerm)
open import ConcretePermutationProperties using (p≡)

--

infix 4 _≃S_

------------------------------------------------------------------------------
-- The big (semantic) theorem!

-- On one side we have the setoid of permutations under ≡
-- On the other we have the setoid of equivalences under ≋
--
-- The regular equivalence ≃ relates sets. To relate the above two
-- setoids, this regular equivalence is generalized to an equivalence
-- ≃S that relates setoids each with its own equivalence relation.

record _≃S_ {ℓ₁ ℓ₂ ℓ₃ ℓ₄ : Level} (A : Setoid ℓ₁ ℓ₂) (B : Setoid ℓ₃ ℓ₄) :
  Set (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃ ⊔ ℓ₄) where
  constructor equiv
  field
    f : A ⟶ B
    g : B ⟶ A
    α : ∀ {x y} → Setoid._≈_ B x y → Setoid._≈_ B ((f ⊚ g) ⟨$⟩ x) y
    β : ∀ {x y} → Setoid._≈_ A x y → Setoid._≈_ A ((g ⊚ f) ⟨$⟩ x) y

-- Big theorem

univalence : ∀ {m n} → (SCPerm m n) ≃S (Fin m S≃ Fin n)
univalence {m} {n} = equiv fwd bwd α β
  where

    fwd' : (CPerm m n) → (Fin m ≃ Fin n)
    fwd' (cp π πᵒ αp βp) =
      (λ m → πᵒ !! m) ,
      let index = (λ n → π !! n) in qinv index pf₁ pf₂
      where
        open ≡-Reasoning
        pf₁ : ∀ m → πᵒ !! (π !! m) ≡ m
        pf₁ m = begin (
          πᵒ !! (π !! m)
            ≡⟨ sym (lookup∘tabulate (λ x → πᵒ !! (π !! x)) m) ⟩
          (π ∘̂ πᵒ) !! m
            ≡⟨ cong (λ x → x !! m) αp ⟩
          1C !! m
            ≡⟨ lookup∘tabulate id m ⟩
          m ∎)
        pf₂ : ∀ m → π !! (πᵒ !! m) ≡ m
        pf₂ m = begin (
          π !! (πᵒ !! m)
            ≡⟨ sym (lookup∘tabulate (λ x → π !! (πᵒ !! x)) m) ⟩
          (πᵒ ∘̂ π) !! m
            ≡⟨ cong (λ x → x !! m) βp ⟩
          1C !! m
            ≡⟨ lookup∘tabulate id m ⟩
          m ∎)

    bwd' : (Fin m ≃ Fin n) → (CPerm m n)
    bwd' (f , qinv g α β) =
      cp (tabulate g) (tabulate f) g∘̂f≡1C f∘̂g≡1C
      where
       open ≡-Reasoning
       g∘̂f≡1C : tabulate g ∘̂ tabulate f ≡ 1C
       g∘̂f≡1C = finext (λ i → begin (
         tabulate f !! (tabulate g !! i)
           ≡⟨ lookup∘tabulate f (tabulate g !! i) ⟩
         f (tabulate g !! i)
           ≡⟨ cong f (lookup∘tabulate g i) ⟩
         f (g i)
           ≡⟨ α i ⟩
         i ∎))
       f∘̂g≡1C : tabulate f ∘̂ tabulate g ≡ 1C
       f∘̂g≡1C = finext (λ i → begin (
         tabulate g !! (tabulate f !! i)
           ≡⟨ lookup∘tabulate g (tabulate f !! i) ⟩
         g (tabulate f !! i)
           ≡⟨ cong g (lookup∘tabulate f i) ⟩
         g (f i)
           ≡⟨ β i ⟩
         i ∎))

    fwd : (SCPerm m n) ⟶ (Fin m S≃ Fin n)
    fwd = record
      { _⟨$⟩_ = fwd'
      ; cong = λ { {π} {.π} refl → id≋ }
      }

    bwd : (Fin m S≃ Fin n) ⟶ (SCPerm m n)
    bwd = record
      { _⟨$⟩_ = bwd'
      ; cong = λ eq₁≋eq₂ → p≡ (finext (_≋_.g≡ eq₁≋eq₂))
      }

    α : {eq₁ eq₂ : Fin m ≃ Fin n} → eq₁ ≋ eq₂ → (fwd ⊚ bwd ⟨$⟩ eq₁) ≋ eq₂
    α {eq₁} {eq₂} eq₁≋eq₂ =
      eq (λ x → trans (lookup∘tabulate (proj₁ eq₁) x) (_≋_.f≡ eq₁≋eq₂ x))
         (λ x → trans (lookup∘tabulate (isqinv.g (proj₂ eq₁)) x) (_≋_.g≡ eq₁≋eq₂ x))

    β : {π₁ π₂ : CPerm m n} → π₁ ≡ π₂ → (bwd ⊚ fwd ⟨$⟩ π₁) ≡ π₂
    β {π} {.π} refl = p≡ (cauchyext (CPerm.π π))


--
-- Created by Dependently-Typed Lambda Calculus on 2020-10-08
-- Connective
-- Author: dplaindoux
--

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

module Connective where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)
open Eq.≡-Reasoning
open import Data.Nat using (ℕ)
open import Function using (_∘_)
open import Isomorphism using (_≃_; _≲_; extensionality)
open Isomorphism.≃-Reasoning

data _×_ (A B : Set) : Set where

  ⟨_,_⟩ :
      A
    → B
      -----
    → A × B

proj₁ : ∀ {A B : Set}
  → A × B
    -----
  → A
proj₁ ⟨ x , y ⟩ = x

proj₂ : ∀ {A B : Set}
  → A × B
    -----
  → B
proj₂ ⟨ x , y ⟩ = y


------------------------------------------------------------------------------
-- The gcd program is correct
------------------------------------------------------------------------------

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

-- This module proves the correctness of the gcd program using
-- the Euclid's algorithm.

module FOTC.Program.GCD.Partial.CorrectnessProofI where

open import FOTC.Base
open import FOTC.Data.Nat.Divisibility.NotBy0.PropertiesI
  using ( 0∤x ; x∣S→x≤S )
open import FOTC.Data.Nat.Type
open import FOTC.Program.GCD.Partial.CommonDivisorI using ( gcdCD )
open import FOTC.Program.GCD.Partial.Definitions
open import FOTC.Program.GCD.Partial.DivisibleI using ( gcdDivisible )
open import FOTC.Program.GCD.Partial.GCD using ( gcd )

import FOTC.Program.GCD.Partial.GreatestAnyCommonDivisor
open module GreatestAnyCommonDivisorI =
  FOTC.Program.GCD.Partial.GreatestAnyCommonDivisor x∣S→x≤S 0∤x
  using ( gcdGACD )

open import FOTC.Program.GCD.Partial.TotalityI using ( gcd-N )

------------------------------------------------------------------------------
-- The gcd is correct.
gcdCorrect : ∀ {m n} → N m → N n → x≢0≢y m n → gcdSpec m n (gcd m n)
gcdCorrect Nm Nn m≢0≢n = gcdCD Nm Nn m≢0≢n
                         , gcdGACD (gcd-N Nm Nn m≢0≢n)
                                   (gcdCD Nm Nn m≢0≢n)
                                   (gcdDivisible Nm Nn m≢0≢n)


module Structure.Numeral.Integer.Proofs where

open import Data.Either as Either using (Left ; Right)
import      Data.Tuple as Tuple
open import Functional
open import Function.Proofs
open import Logic.IntroInstances
open import Logic.Predicate
open import Logic.Propositional
import      Lvl
open import Structure.Function.Domain
open import Structure.Function
open import Structure.Numeral.Integer
open import Structure.Relator
open import Structure.Relator.Proofs
open import Structure.Relator.Properties
open import Structure.Setoid
open import Structure.Operator
open import Structure.Operator.Properties
open import Structure.Operator.Ring.Proofs
open import Structure.Operator.Ring
open import Structure.OrderedField
open import Syntax.Implication
open import Syntax.Transitivity
open import Type

private variable ℓₒ ℓₗ ℓₑ ℓₗ₁ ℓₗ₂ : Lvl.Level
private variable Z : Type{ℓₒ}
private variable _+_ _⋅_ : Z → Z → Z
private variable _≤_ : Z → Z → Type{ℓₗ}

module _ ⦃ equiv : Equiv{ℓₑ}(Z) ⦄ ⦃ int : Integer ⦃ equiv ⦄ (_+_)(_⋅_)(_≤_) ⦄ where
  open Integer(int)

  negative-induction : ∀{ℓ}{P : Z → Type{ℓ}} ⦃ rel-p : UnaryRelator(P) ⦄ → P(𝟎) → (∀{n} → (n ≤ 𝟎) → P(n) → P(𝐏(n))) → (∀{n} → (n ≤ 𝟎) → P(n))
  negative-induction {P = P} pz ps {n} neg =
    substitute₁(P) (involution(−_)) (positive-induction
      {P = P ∘ (−_)}
      ⦃ [∘]-unaryRelator ⦄
      (substitute₁ₗ(P) [−]-of-𝟎 pz)
      (\{n} pos pnn → substitute₁ₗ(P) [+]-negation-distribution (ps{− n} ([↔]-to-[→] [≤]-flip-positive pos) pnn))
      {− n}
      ([↔]-to-[→] [≤]-flip-negative neg)
    )

  induction : ∀{ℓ}{P : Z → Type{ℓ}} ⦃ rel-p : UnaryRelator(P) ⦄ → P(𝟎) → (∀{n} → (n ≤ 𝟎) → P(n) → P(𝐏(n))) → (∀{n} → (𝟎 ≤ n) → P(n) → P(𝐒(n))) → (∀{n} → P(n))
  induction pz pp ps {n} with converseTotal(_≤_) {n}{𝟎}
  ... | Left  neg = negative-induction pz pp neg
  ... | Right pos = positive-induction pz ps pos

  [⋅]-commutativity : Commutativity(_⋅_)
  [⋅]-commutativity = intro(induction{P = x ↦ ∀{y} → (x ⋅ y ≡ y ⋅ x)} ⦃ {!!} ⦄ base (const pred) (const succ)) where
    base : ∀{y} → (𝟎 ⋅ y ≡ y ⋅ 𝟎)
    base{y} =
      𝟎 ⋅ y 🝖[ _≡_ ]-[ absorberₗ(_⋅_)(𝟎) ]
      𝟎     🝖[ _≡_ ]-[ absorberᵣ(_⋅_)(𝟎) ]-sym
      y ⋅ 𝟎 🝖-end

    pred : ∀{x} → (∀{y} → (x ⋅ y ≡ y ⋅ x)) → (∀{y} → (𝐏(x) ⋅ y ≡ y ⋅ 𝐏(x)))
    pred {x} p {y} =
      𝐏(x) ⋅ y          🝖[ _≡_ ]-[]
      (x − 𝟏) ⋅ y       🝖[ _≡_ ]-[ distributivityᵣ(_⋅_)(_−_) ⦃ [⋅][−]-distributivityᵣ ⦄ ]
      (x ⋅ y) − (𝟏 ⋅ y) 🝖[ _≡_ ]-[ congruence₂ᵣ(_−_) ⦃ [−]-binaryOperator ⦄ (_) (identityₗ(_⋅_)(𝟏)) ]
      (x ⋅ y) − y       🝖[ _≡_ ]-[ congruence₂ₗ(_−_) ⦃ [−]-binaryOperator ⦄ (_) p ]
      (y ⋅ x) − y       🝖[ _≡_ ]-[ congruence₂ᵣ(_−_) ⦃ [−]-binaryOperator ⦄ (_) (identityᵣ(_⋅_)(𝟏)) ]-sym
      (y ⋅ x) − (y ⋅ 𝟏) 🝖[ _≡_ ]-[ distributivityₗ(_⋅_)(_−_) ⦃ [⋅][−]-distributivityₗ ⦄ ]-sym
      y ⋅ (x − 𝟏)       🝖[ _≡_ ]-[]
      y ⋅ 𝐏(x)          🝖-end

    succ : ∀{x} → (∀{y} → (x ⋅ y ≡ y ⋅ x)) → (∀{y} → (𝐒(x) ⋅ y ≡ y ⋅ 𝐒(x)))
    succ {x} p {y} =
      𝐒(x) ⋅ y          🝖[ _≡_ ]-[]
      (x + 𝟏) ⋅ y       🝖[ _≡_ ]-[ distributivityᵣ(_⋅_)(_+_) ]
      (x ⋅ y) + (𝟏 ⋅ y) 🝖[ _≡_ ]-[ congruence₂ᵣ(_+_)(_) (identityₗ(_⋅_)(𝟏)) ]
      (x ⋅ y) + y       🝖[ _≡_ ]-[ congruence₂ₗ(_+_)(_) p ]
      (y ⋅ x) + y       🝖[ _≡_ ]-[ congruence₂ᵣ(_+_)(_) (identityᵣ(_⋅_)(𝟏)) ]-sym
      (y ⋅ x) + (y ⋅ 𝟏) 🝖[ _≡_ ]-[ distributivityₗ(_⋅_)(_+_) ]-sym
      y ⋅ (x + 𝟏)       🝖[ _≡_ ]-[]
      y ⋅ 𝐒(x)          🝖-end

  [≤]-identities : (𝟎 ≤ 𝟏)
  [≤]-identities with converseTotal(_≤_) {𝟎}{− 𝟏}
  ... | Left omi =
    omi ⇒
    (𝟎 ≤ (− 𝟏))           ⇒-[ swap apply₂ [≤][⋅]-zero ]
    (𝟎 ≤ ((− 𝟏) ⋅ (− 𝟏))) ⇒-[ _🝖 sub₂(_≡_)(_≤_) [⋅]-of-[−]  ]
    (𝟎 ≤ (𝟏 ⋅ 𝟏))         ⇒-[ _🝖 sub₂(_≡_)(_≤_) (identityₗ(_⋅_)(𝟏))  ]
    (𝟎 ≤ 𝟏)               ⇒-end
  ... | Right mio = [↔]-to-[←] [≤]-flip-positive mio

  [<]-identities : (𝟎 < 𝟏)
  [<]-identities = [≤][≢]-to-[<] [≤]-identities (NonZero.proof distinct-identities ∘ symmetry(_≡_))

  𝐒-of-𝟎 : 𝐒(𝟎) ≡ 𝟏
  𝐒-of-𝟎 =
    𝐒(𝟎)  🝖[ _≡_ ]-[]
    𝟎 + 𝟏 🝖[ _≡_ ]-[ identityₗ(_+_)(𝟎) ]
    𝟏     🝖-end

  instance
    postulate 𝐒-function : Function(𝐒)
    -- 𝐒-function = {!!}

  instance
    postulate [<]-relator : BinaryRelator(_<_)
    -- [<]-relator = {![¬]-binaryRelator!}

  [≤]-with-𝐒 : ∀{x y} → (x ≤ y) → (𝐒(x) ≤ 𝐒(y))
  [≤]-with-𝐒 = [≤][+]ₗ-preserve

  [<]-with-𝐒 : ∀{x y} → (x < y) → (𝐒(x) < 𝐒(y))
  [<]-with-𝐒 = [<][+]-preserveₗ

  [≤]-with-𝐏 : ∀{x y} → (x ≤ y) → (𝐏(x) ≤ 𝐏(y))
  [≤]-with-𝐏 = [≤][−]ₗ-preserve

  [<]-with-𝐏 : ∀{x y} → (x < y) → (𝐏(x) < 𝐏(y))
  [<]-with-𝐏 = [<][+]-preserveₗ

  𝐒-𝐏-inverse : ∀{x} → (𝐒(𝐏(x)) ≡ x)
  𝐒-𝐏-inverse = associativity(_+_) 🝖 congruence₂ᵣ(_+_)(_) (inverseFunctionₗ(_+_)(−_)) 🝖 identityᵣ(_+_)(𝟎)

  𝐏-𝐒-inverse : ∀{x} → (𝐏(𝐒(x)) ≡ x)
  𝐏-𝐒-inverse = associativity(_+_) 🝖 congruence₂ᵣ(_+_)(_) (inverseFunctionᵣ(_+_)(−_)) 🝖 identityᵣ(_+_)(𝟎)

  𝐒-order : ∀{x} → (x < 𝐒(x))
  𝐒-order {x} = induction
    {P = x ↦ x < 𝐒(x)}
    ⦃ binary-unaryRelator ⦃ rel-P = [∘]-binaryRelator ⦄ ⦄
    (subtransitivityᵣ(_<_)(_≡_) [<]-identities (symmetry(_≡_) 𝐒-of-𝟎))
    (const (p ↦ subtransitivityᵣ(_<_)(_≡_) ([<]-with-𝐏 p) (𝐏-𝐒-inverse 🝖 symmetry(_≡_) 𝐒-𝐏-inverse)))
    (const [<]-with-𝐒)

  𝐒-least-upper-bound : ∀{x m} → (x < m) → (𝐒(x) ≤ m)
  𝐒-least-upper-bound {x}{m} xm with converseTotal(_≤_) {x}{m} |  converseTotal(_≤_) {𝐒(x)}{m}
  ... | Left x₁ | Left x₂ = x₂
  ... | Left x₁ | Right x₂ = {!!} -- x<m x≤m m≤𝐒x
  ... | Right x₁ | Left x₂ = x₂
  ... | Right x₁ | Right x₂ with () ← xm x₁
  {-... | Left  𝐒xm  = {!!}
  ... | Right ¬m𝐒x = {!!}
  -}
  {-=
    𝐒(x) 🝖[ _≤_ ]-[ {!!} ]
    m    🝖-end-}

  least-upper-bound-existence : ∃{Obj = Z → Z}(S ↦ (∀{x} → (x < S(x))) ∧ (∀{x m} → (x < m) → (S(x) ≤ m)))
  ∃.witness            least-upper-bound-existence  = 𝐒
  Tuple.left  (∃.proof least-upper-bound-existence) = 𝐒-order
  Tuple.right (∃.proof least-upper-bound-existence) = 𝐒-least-upper-bound

  {- TODO: Not here. Needs invertible mult
  record Exponentiation(_^_ : Z → Z → Z) : Type{ℓₑ Lvl.⊔ Lvl.of(Z) Lvl.⊔ Lvl.of(𝟎 ≤ 𝟎)} where
    field
      base : ∀{a} → (a ^ 𝟎 ≡ 𝟏)
      step : ∀{a b} → (𝟎 ≤ b) → (a ^ (b + 𝟏) ≡ a ⋅ (a ^ b))
      neg  : ∀{a b} → (𝟎 ≤ 𝟎) → (a ^ (− b) ≡ ⅟(a ^ b))
  -}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Sums (disjoint unions)
------------------------------------------------------------------------

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

module Data.Sum.Base where

open import Data.Bool.Base using (true; false)
open import Function.Base using (_∘_; _-[_]-_ ; id)
open import Relation.Nullary.Reflects using (invert)
open import Relation.Nullary using (Dec; yes; no; _because_; ¬_)
open import Level using (Level; _⊔_)

private
  variable
    a b c d : Level
    A : Set a
    B : Set b
    C : Set c
    D : Set d

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

infixr 1 _⊎_

data _⊎_ (A : Set a) (B : Set b) : Set (a ⊔ b) where
  inj₁ : (x : A) → A ⊎ B
  inj₂ : (y : B) → A ⊎ B

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

[_,_] : ∀ {C : A ⊎ B → Set c} →
        ((x : A) → C (inj₁ x)) → ((x : B) → C (inj₂ x)) →
        ((x : A ⊎ B) → C x)
[ f , g ] (inj₁ x) = f x
[ f , g ] (inj₂ y) = g y

[_,_]′ : (A → C) → (B → C) → (A ⊎ B → C)
[_,_]′ = [_,_]

fromInj₁ : (B → A) → A ⊎ B → A
fromInj₁ = [ id ,_]′

fromInj₂ : (A → B) → A ⊎ B → B
fromInj₂ = [_, id ]′

swap : A ⊎ B → B ⊎ A
swap (inj₁ x) = inj₂ x
swap (inj₂ x) = inj₁ x

map : (A → C) → (B → D) → (A ⊎ B → C ⊎ D)
map f g = [ inj₁ ∘ f , inj₂ ∘ g ]

map₁ : (A → C) → (A ⊎ B → C ⊎ B)
map₁ f = map f id

map₂ : (B → D) → (A ⊎ B → A ⊎ D)
map₂ = map id

infixr 1 _-⊎-_
_-⊎-_ : (A → B → Set c) → (A → B → Set d) → (A → B → Set (c ⊔ d))
f -⊎- g = f -[ _⊎_ ]- g

-- Conversion back and forth with Dec

fromDec : Dec A → A ⊎ ¬ A
fromDec ( true because  [p]) = inj₁ (invert  [p])
fromDec (false because [¬p]) = inj₂ (invert [¬p])

toDec : A ⊎ ¬ A → Dec A
toDec (inj₁ p)  = yes p
toDec (inj₂ ¬p) = no ¬p