module Sandbox.Pred where

open import Data.Num
open import Data.Num.Properties

open import Data.Nat
open import Data.Fin using (Fin; suc; zero; #_)
open import Data.Vec
open import Relation.Nullary.Decidable using (True; fromWitness)
open import Relation.Binary.PropositionalEquality hiding ([_])
-- open import Relation.Binary
open ≡-Reasoning

data Term : ℕ → Set where
    var : ∀ {n}
        → Fin n     -- the index of this variable
        → Term n
    _∔_ : ∀ {n} → Term n → Term n → Term n

data Pred : ℕ → Set where
  _≣_ : ∀ {n} → Term n → Term n → Pred n
  _⇒_ : ∀ {n} → Pred n → Pred n → Pred n
  -- captures 1 free variable in the Pred
  All : ∀ {n} → Pred (suc n) → Pred n

record Signature : Set₁ where
    constructor signature
    field
        carrier : Set
        _⊕_ : carrier → carrier → carrier
        _≈_ : carrier → carrier → Set

open Signature

-- signatures
ℕ-sig : Signature
ℕ-sig = signature ℕ _+_ _≡_

Num-sig : (b d o : ℕ) → True (Surjective? b d o) → Signature
Num-sig b d o surj = signature (Num b d o) (_⊹_ {surj = surj}) _≋_

⟦_⟧Term : ∀ {n}
    → Term n
    → (sig : Signature)
    → let A = carrier sig in Vec A n
    → A
⟦ var x ⟧Term sig env = lookup x env
⟦ a ∔ b ⟧Term sig env = let (signature A _⊕_ _≈_) = sig in
                        (⟦ a ⟧Term sig env) ⊕ (⟦ b ⟧Term sig env)

⟦_⟧ : ∀ {n}
    → Pred n
    → (sig : Signature)
    → Vec (carrier sig) n
    → Set
⟦ p ≣ q ⟧ sig env = let (signature carrier _⊕_ _≈_) = sig in
                    ⟦ p ⟧Term sig env ≈ ⟦ q ⟧Term sig env
⟦ p ⇒ q ⟧ sig env = ⟦ p ⟧ sig env → ⟦ q ⟧ sig env
⟦ All p ⟧ sig env = ∀ x → ⟦ p ⟧ sig (x ∷ env)

-- lemma for env
lookup-map : ∀ {i j} → {A : Set i} {B : Set j}
           → (f : A → B) →
           ∀ {n} → (xs : Vec A n) (i : Fin n)
           → lookup i (map f xs) ≡ f (lookup i xs)
lookup-map f []        ()
lookup-map f (x ∷ env) zero    = refl
lookup-map f (x ∷ env) (suc i) = lookup-map f env i

toℕ-term-homo : ∀ {b d o}
    → {surj : True (Surjective? b d o)}
    → ∀ {n}
    → (t : Term n)
    → (env : Vec (Num b d o) n)
    → ⟦ t ⟧Term ℕ-sig (map toℕ env) ≡ toℕ (⟦ t ⟧Term (Num-sig b d o surj) env )
toℕ-term-homo (var x) env = lookup-map toℕ env x
toℕ-term-homo {b} {d} {o} {surj} (s ∔ t) env =
    begin
        ⟦ s ∔ t ⟧Term ℕ-sig (map toℕ env)
    ≡⟨ cong₂ _+_ (toℕ-term-homo {surj = surj} s env) (toℕ-term-homo {surj = surj} t env) ⟩
        toℕ (⟦ s ⟧Term _ env) + toℕ (⟦ t ⟧Term _ env)
    ≡⟨ sym (toℕ-⊹-homo (⟦ s ⟧Term _ env) (⟦ t ⟧Term _ env)) ⟩
        toℕ (⟦ s ∔ t ⟧Term (Num-sig b d o surj) env)
    ∎

mutual
    toℕ-pred-ℕ⇒Num : ∀ {b d o}
        → {surj : True (Surjective? b d o)}
        → ∀ {n}
        → (p : Pred n)
        → (env : Vec (Num b d o) n)
        → ⟦ p ⟧ ℕ-sig (map toℕ env)
        → ⟦ p ⟧ (Num-sig b d o surj) env
    -- toℕ-pred-ℕ⇒Num (p ≣ q) env ⟦p≡q⟧ℕ = {!   !}
    toℕ-pred-ℕ⇒Num {b} {d} {o} {surj} (p ≣ q) env a =
        -- a : ⟦ p ⟧Term ℕ-sig (ℕ-env) ≡ ⟦ q ⟧Term ℕ-sig (ℕ-env)

        let p' = ⟦ p ⟧Term {! Num-sig b d o surj  !} {!   !} {!   !}
            temp = toℕ-injective p' {!   !} a
        in
        {! toℕ-injective  !}
    toℕ-pred-ℕ⇒Num (p ⇒ q) env a = {!   !}
    toℕ-pred-ℕ⇒Num (All p) env a = {!   !}

    toℕ-pred-Num→ℕ : ∀ {b d o}
        → {surj : True (Surjective? b d o)}
        → ∀ {n}
        → (p : Pred n)
        → (env : Vec (Num b d o) n)
        → ⟦ p ⟧ (Num-sig b d o surj) env
        → ⟦ p ⟧ ℕ-sig (map toℕ env)
    toℕ-pred-Num→ℕ (p ≣ q) env a = {!   !}
    toℕ-pred-Num→ℕ (p ⇒ q) env a = {!   !}
    toℕ-pred-Num→ℕ (All p) env a = {!   !}

-- begin
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ∎

--
-- toℕ-term-homo : ∀ {b n}
--     → (t : Term n)
--     → (env : Vec (BijN b) n)
--     → ⟦ t ⟧Term ℕ-sig (map toℕ env) ≡ toℕ (⟦ t ⟧Term (BijN-sig b) env)
-- toℕ-term-homo     (var i)   env = ?
-- toℕ-term-homo {b} (t₁ ∔ t₂) env = ?


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

module Algebra.Linear.Structures.Bundles where

open import Algebra
open import Level using (Level; suc; _⊔_)
open import Relation.Binary using (Rel; IsEquivalence)

open import Algebra.FunctionProperties
open import Algebra.Structures.Field
open import Algebra.Structures.Bundles.Field
open import Algebra.Linear.Structures.VectorSpace

record RawVectorSpace {k ℓᵏ} (K : Field k ℓᵏ) (c ℓ : Level) : Set (suc (c ⊔ k ⊔ ℓ)) where
  infix  20 _≈_
  infixl 25 _+_
  infixr 30 _∙_

  field
    Carrier       : Set c
    _≈_           : Rel Carrier ℓ
    _+_           : Carrier -> Carrier -> Carrier
    _∙_           : Field.Carrier K -> Carrier -> Carrier
    -_            : Carrier -> Carrier
    0#            : Carrier

  rawGroup : RawGroup c ℓ
  rawGroup = record { _≈_ = _≈_; _∙_ = _+_; ε = 0#; _⁻¹ = -_ }

  open RawGroup rawGroup public
    using (rawMagma; rawMonoid)

record VectorSpace {k ℓᵏ} (K : Field k ℓᵏ) (c ℓ : Level) : Set (suc (c ⊔ k ⊔ ℓ ⊔ ℓᵏ)) where
  infix  20 _≈_
  infixl 25 _+_
  infixr 30 _∙_

  field
    Carrier       : Set c
    _≈_           : Rel Carrier ℓ
    _+_           : Carrier -> Carrier -> Carrier
    _∙_           : Field.Carrier K -> Carrier -> Carrier
    -_            : Carrier -> Carrier
    0#            : Carrier
    isVectorSpace : IsVectorSpace K _≈_ _+_ _∙_ -_ 0#

  open IsVectorSpace isVectorSpace public

  rawVectorSpace : RawVectorSpace K c ℓ
  rawVectorSpace = record
    { _≈_ = _≈_; _+_ = _+_; _∙_ = _∙_; -_ = -_; 0# = 0# }

  abelianGroup : AbelianGroup c ℓ
  abelianGroup = record { isAbelianGroup = isAbelianGroup }

  open AbelianGroup abelianGroup public
    using (rawMagma; magma; rawMonoid; monoid; rawGroup; group)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Some derivable properties
------------------------------------------------------------------------

open import Algebra

module Algebra.Properties.Lattice {l₁ l₂} (L : Lattice l₁ l₂) where

open Lattice L
open import Algebra.Structures
import Algebra.FunctionProperties as P; open P _≈_
open import Relation.Binary
import Relation.Binary.EqReasoning as EqR; open EqR setoid
open import Function
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (_⇔_; module Equivalence)
open import Data.Product

∧-idempotent : Idempotent _∧_
∧-idempotent x = begin
  x ∧ x            ≈⟨ refl ⟨ ∧-cong ⟩ sym (proj₁ absorptive _ _) ⟩
  x ∧ (x ∨ x ∧ x)  ≈⟨ proj₂ absorptive _ _ ⟩
  x                ∎

∨-idempotent : Idempotent _∨_
∨-idempotent x = begin
  x ∨ x      ≈⟨ refl ⟨ ∨-cong ⟩ sym (∧-idempotent _) ⟩
  x ∨ x ∧ x  ≈⟨ proj₁ absorptive _ _ ⟩
  x          ∎

-- The dual construction is also a lattice.

∧-∨-isLattice : IsLattice _≈_ _∧_ _∨_
∧-∨-isLattice = record
  { isEquivalence = isEquivalence
  ; ∨-comm        = ∧-comm
  ; ∨-assoc       = ∧-assoc
  ; ∨-cong        = ∧-cong
  ; ∧-comm        = ∨-comm
  ; ∧-assoc       = ∨-assoc
  ; ∧-cong        = ∨-cong
  ; absorptive    = swap absorptive
  }

∧-∨-lattice : Lattice _ _
∧-∨-lattice = record
  { _∧_       = _∨_
  ; _∨_       = _∧_
  ; isLattice = ∧-∨-isLattice
  }

-- Every lattice can be turned into a poset.

poset : Poset _ _ _
poset = record
  { Carrier        = Carrier
  ; _≈_            = _≈_
  ; _≤_            = λ x y → x ≈ x ∧ y
  ; isPartialOrder = record
    { isPreorder = record
      { isEquivalence = isEquivalence
      ; reflexive     = λ {i} {j} i≈j → begin
                          i      ≈⟨ sym $ ∧-idempotent _ ⟩
                          i ∧ i  ≈⟨ ∧-cong refl i≈j ⟩
                          i ∧ j  ∎
      ; trans         = λ {i} {j} {k} i≈i∧j j≈j∧k → begin
                          i            ≈⟨ i≈i∧j ⟩
                          i ∧ j        ≈⟨ ∧-cong refl j≈j∧k ⟩
                          i ∧ (j ∧ k)  ≈⟨ sym (∧-assoc _ _ _) ⟩
                          (i ∧ j) ∧ k  ≈⟨ ∧-cong (sym i≈i∧j) refl ⟩
                          i ∧ k        ∎
      }
    ; antisym = λ {x} {y} x≈x∧y y≈y∧x → begin
                  x      ≈⟨ x≈x∧y ⟩
                  x ∧ y  ≈⟨ ∧-comm _ _ ⟩
                  y ∧ x  ≈⟨ sym y≈y∧x ⟩
                  y      ∎
    }
  }

-- One can replace the underlying equality with an equivalent one.

replace-equality : {_≈′_ : Rel Carrier l₂} →
                   (∀ {x y} → x ≈ y ⇔ x ≈′ y) → Lattice _ _
replace-equality {_≈′_} ≈⇔≈′ = record
  { _≈_       = _≈′_
  ; _∧_       = _∧_
  ; _∨_       = _∨_
  ; isLattice = record
    { isEquivalence = record
      { refl  = to ⟨$⟩ refl
      ; sym   = λ x≈y → to ⟨$⟩ sym (from ⟨$⟩ x≈y)
      ; trans = λ x≈y y≈z → to ⟨$⟩ trans (from ⟨$⟩ x≈y) (from ⟨$⟩ y≈z)
      }
    ; ∨-comm     = λ x y → to ⟨$⟩ ∨-comm x y
    ; ∨-assoc    = λ x y z → to ⟨$⟩ ∨-assoc x y z
    ; ∨-cong     = λ x≈y u≈v → to ⟨$⟩ ∨-cong (from ⟨$⟩ x≈y) (from ⟨$⟩ u≈v)
    ; ∧-comm     = λ x y → to ⟨$⟩ ∧-comm x y
    ; ∧-assoc    = λ x y z → to ⟨$⟩ ∧-assoc x y z
    ; ∧-cong     = λ x≈y u≈v → to ⟨$⟩ ∧-cong (from ⟨$⟩ x≈y) (from ⟨$⟩ u≈v)
    ; absorptive = (λ x y → to ⟨$⟩ proj₁ absorptive x y)
                 , (λ x y → to ⟨$⟩ proj₂ absorptive x y)
    }
  } where open module E {x y} = Equivalence (≈⇔≈′ {x} {y})


module Logic.IntroInstances where

import      Data.Tuple as Tuple
import      Lvl
open import Logic.Predicate
open import Logic.Propositional
open import Type

private variable ℓ : Lvl.Level
private variable A B Obj : Type{ℓ}
private variable P : Obj → Type{ℓ}
private variable x : Obj

instance
  [∧]-intro-instance : ⦃ _ : A ⦄ → ⦃ _ : B ⦄ → (A ∧ B)
  [∧]-intro-instance ⦃ a ⦄ ⦃ b ⦄ = [∧]-intro a b

instance
  [∨]-introₗ-instance : ⦃ _ : A ⦄ → (A ∨ B)
  [∨]-introₗ-instance ⦃ a ⦄ = [∨]-introₗ a

instance
  [∨]-introᵣ-instance : ⦃ _ : B ⦄ → (A ∨ B)
  [∨]-introᵣ-instance ⦃ b ⦄ = [∨]-introᵣ b

instance
  [∃]-intro-instance : ⦃ _ : P(x) ⦄ → ∃(P)
  [∃]-intro-instance {x = x} ⦃ proof ⦄ = [∃]-intro (x) ⦃ proof ⦄


module MLib.Finite where

open import MLib.Prelude
import MLib.Fin.Parts as P
import MLib.Fin.Parts.Simple as PS
open import MLib.Prelude.RelProps

open import Data.List.All as All using (All; []; _∷_) hiding (module All)
open import Data.List.Any as Any using (Any; here; there) hiding (module Any)
import Data.List.Membership.Setoid as Membership
import Data.List.Membership.Propositional as PropMembership

import Relation.Binary.Indexed as I
import Relation.Unary as U using (Decidable)
open LeftInverse using () renaming (_∘_ to _ⁱ∘_)
open FE using (_⇨_; cong)
open import Function.Related using () renaming (module EquationalReasoning to RelReasoning)

import Data.Product.Relation.SigmaPropositional as OverΣ

open Algebra using (IdempotentCommutativeMonoid)

--------------------------------------------------------------------------------
--  Main Definition
--------------------------------------------------------------------------------

record IsFiniteSetoid {c ℓ} (setoid : Setoid c ℓ) (N : ℕ) : Set (c ⊔ˡ ℓ) where
  open Setoid setoid public
  open Setoid setoid using () renaming (Carrier to A)

  OntoFin : ℕ → Set _
  OntoFin N = LeftInverse setoid (≡.setoid (Fin N))

  field
    ontoFin : OntoFin N

  fromIx : Fin N → A
  fromIx i = LeftInverse.from ontoFin ⟨$⟩ i

  toIx : A → Fin N
  toIx x = LeftInverse.to ontoFin ⟨$⟩ x

  fromIx-toIx : ∀ x → fromIx (toIx x) ≈ x
  fromIx-toIx = LeftInverse.left-inverse-of ontoFin

  enumₜ : Table A N
  enumₜ = tabulate fromIx

  enumₗ : List A
  enumₗ = Table.toList enumₜ

IsFiniteSet : ∀ {a} → Set a → ℕ → Set a
IsFiniteSet = IsFiniteSetoid ∘ ≡.setoid

record FiniteSet c ℓ : Set (sucˡ (c ⊔ˡ ℓ)) where
  field
    setoid : Setoid c ℓ
    N : ℕ
    isFiniteSetoid : IsFiniteSetoid setoid N

  open IsFiniteSetoid isFiniteSetoid public

--------------------------------------------------------------------------------
--  Combinators
--------------------------------------------------------------------------------

-- An enumerable setoid is finite

module _ {c ℓ} (setoid : Setoid c ℓ) where
  open Setoid setoid
  open Membership setoid

  Any-cong : ∀ {xs} → (∀ x → x ∈ xs) → Set _
  Any-cong f = ∀ {x y} → x ≈ y → Any.index (f x) ≡ Any.index (f y)

  private
    from : ∀ xs → Fin (List.length xs) → Carrier
    from List.[] ()
    from (x List.∷ xs) Fin.zero = x
    from (x List.∷ xs) (Fin.suc i) = from xs i

    left-inverse-of : ∀ {xs} (f : ∀ x → x ∈ xs) x → from xs (Any.index (f x)) ≈ x
    left-inverse-of f x = go (f x)
      where
      go : ∀ {xs} (p : x ∈ xs) → from xs (Any.index p) ≈ x
      go (here px) = sym px
      go (there p) = go p

  enumerable-isFiniteSetoid : ∀ {xs} (f : ∀ x → x ∈ xs) → Any-cong f → IsFiniteSetoid setoid (List.length xs)
  enumerable-isFiniteSetoid {xs} f f-cong = record
    { ontoFin = record
      { to = record
        { _⟨$⟩_ = Any.index ∘ f
        ; cong = f-cong
        }
      ; from = ≡.→-to-⟶ (from xs)
      ; left-inverse-of = left-inverse-of f
      }
    }

-- As a special case of the previous theorem requiring fewer conditions, an
-- enumerable set is finite.

module _ {a} {A : Set a} where
  open PropMembership

  enumerable-isFiniteSet : (xs : List A) (f : ∀ x → x ∈ xs) → IsFiniteSet A (List.length xs)
  enumerable-isFiniteSet _ f = enumerable-isFiniteSetoid (≡.setoid _) f (≡.cong (Any.index ∘ f))

-- Given a function with a left inverse from some 'A' to a finite set, 'A' must also be finite.

extendedIsFinite : ∀ {c ℓ c′ ℓ′} {S : Setoid c ℓ} (F : FiniteSet c′ ℓ′) → LeftInverse S (FiniteSet.setoid F) → IsFiniteSetoid S (FiniteSet.N F)
extendedIsFinite finiteSet ontoF = record
  { ontoFin = ontoFin ⁱ∘ ontoF
  }
  where
    open FiniteSet finiteSet using (ontoFin)

extendFinite : ∀ {c ℓ c′ ℓ′} {S : Setoid c ℓ} (F : FiniteSet c′ ℓ′) → LeftInverse S (FiniteSet.setoid F) → FiniteSet c ℓ
extendFinite finiteSet ontoF = record
  { isFiniteSetoid = extendedIsFinite finiteSet ontoF
  }

module _ {c ℓ c′ ℓ′} {S : Setoid c ℓ} (F : FiniteSet c′ ℓ′) where

  extendedIsFinite′N : (¬ Setoid.Carrier S) ⊎ LeftInverse S (FiniteSet.setoid F) → ℕ
  extendedIsFinite′N (inj₁ x) = 0
  extendedIsFinite′N (inj₂ y) = FiniteSet.N F

  extendedIsFinite′ : (inj : (¬ Setoid.Carrier S) ⊎ LeftInverse S (FiniteSet.setoid F)) → IsFiniteSetoid S (extendedIsFinite′N inj)
  extendedIsFinite′ (inj₂ f) = extendedIsFinite F f
  extendedIsFinite′ (inj₁ p) = record
    { ontoFin = record
      { to = record { _⟨$⟩_ = ⊥-elim ∘ p ; cong = λ {i} → ⊥-elim (p i) }
      ; from = ≡.→-to-⟶ λ ()
      ; left-inverse-of = ⊥-elim ∘ p
      }
    }

-- Given a family of finite sets, indexed by a finite set, the sum over the entire family is finite.

module _ {c} {A : Set c} {N} (isFiniteSet : IsFiniteSet A N) where
  private
    finiteSet : FiniteSet c c
    finiteSet = record { isFiniteSetoid = isFiniteSet }
    module F = FiniteSet finiteSet

    Σᶠ : ∀ {p} → (A → Set p) → Set p
    Σᶠ P = ∃ (P ∘ lookup F.enumₜ)

  module _ {p ℓ} (finiteAt : A → FiniteSet p ℓ) where
    private
      module PW x = FiniteSet (finiteAt x)
      open PW using () renaming (Carrier to P)

      parts : P.Parts A PW.N
      parts = record
        { numParts = N
        ; parts = F.enumₜ
        }

      open P.Parts parts hiding (parts)

    Σ-isFiniteSetoid : IsFiniteSetoid (OverΣ.setoid PW.setoid) totalSize
    Σ-isFiniteSetoid = record
      { ontoFin
        =  Inverse.left-inverse (Inverse.sym asParts)
        ⁱ∘ intoCoords
        ⁱ∘ Σ-↞′ {B-setoid = PW.setoid} F.ontoFin
      }
      where
        P-setoidᶠ : Fin N → Setoid _ _
        P-setoidᶠ i = record
          { Carrier = P (F.fromIx i)
          ; _≈_ = PW._≈_ _
          ; isEquivalence = record
            { refl = PW.refl _
            ; sym = PW.sym _
            ; trans = PW.trans _
            }
          }

        ΣᶠP-setoid = OverΣ.setoid P-setoidᶠ

        open Setoid ΣᶠP-setoid using ()
          renaming (_≈_ to _≈ᶠ_)

        to : Σᶠ P → Σ (Fin N) (Fin ∘ sizeAt)
        to (_ , px) = _ , PW.toIx _ px

        to-cong : ∀ {x y} → x ≈ᶠ y → to x ≡ to y
        to-cong (≡.refl , q) = OverΣ.to-≡ (≡.refl , cong (LeftInverse.to (PW.ontoFin _)) q)

        from : Σ (Fin N) (Fin ∘ sizeAt) → Σᶠ P
        from (i , j) = _ , PW.fromIx _ j

        left-inverse-of : ∀ x → from (to x) ≈ᶠ x
        left-inverse-of (i , x) = ≡.refl , PW.fromIx-toIx _ _

        intoCoords : LeftInverse ΣᶠP-setoid (≡.setoid (Σ (Fin N) (Fin ∘ sizeAt)))
        intoCoords = record
          { to = record { _⟨$⟩_ = to ; cong = to-cong }
          ; from = ≡.→-to-⟶ from
          ; left-inverse-of = left-inverse-of
          }

    Σ-finiteSet : FiniteSet _ _
    Σ-finiteSet = record { isFiniteSetoid = Σ-isFiniteSetoid }

module All′ {a} {A : Set a} where
  module _ {p ℓ} (setoid : A → Setoid p ℓ) where
    module P′ x = Setoid (setoid x)
    module P′′ {x} = P′ x
    open P′ using () renaming (Carrier to P)
    open P′′ using (_≈_)

    data PW : {xs : List A} → All P xs → All P xs → Set (p ⊔ˡ ℓ) where
      [] : PW [] []
      _∷_ : ∀ {x xs} {px py : P x} {apx apy : All P xs} → px ≈ py → PW apx apy → PW (px ∷ apx) (py ∷ apy)

    PW-setoid : List A → Setoid _ _
    Setoid.Carrier (PW-setoid xs) = All P xs
    Setoid._≈_ (PW-setoid xs) = PW
    IsEquivalence.refl (Setoid.isEquivalence (PW-setoid .[])) {[]} = []
    IsEquivalence.refl (Setoid.isEquivalence (PW-setoid .(_ ∷ _))) {px ∷ ap} =
      P′′.refl ∷ IsEquivalence.refl (Setoid.isEquivalence (PW-setoid _))
    IsEquivalence.sym (Setoid.isEquivalence (PW-setoid .[])) [] = []
    IsEquivalence.sym (Setoid.isEquivalence (PW-setoid .(_ ∷ _))) (p ∷ q) =
      P′′.sym p ∷ IsEquivalence.sym (Setoid.isEquivalence (PW-setoid _)) q
    IsEquivalence.trans (Setoid.isEquivalence (PW-setoid .[])) [] [] = []
    IsEquivalence.trans (Setoid.isEquivalence (PW-setoid .(_ ∷ _))) (p ∷ q) (p′ ∷ q′) =
      P′′.trans p p′ ∷ IsEquivalence.trans (Setoid.isEquivalence (PW-setoid _)) q q′

  module _ {p} (P : A → Set p) where
    PW-≡ : ∀ {xs} {apx apy : All P xs} → PW (≡.setoid ∘ P) apx apy → apx ≡ apy
    PW-≡ [] = ≡.refl
    PW-≡ (p ∷ q) = ≡.cong₂ _∷_ p (PW-≡ q)

-- module _ {c ℓ} (finiteSet : FiniteSet c ℓ) where
--   private
--     module A = FiniteSet finiteSet

--   open A using (_≈_) renaming (Carrier to A)

--   open IsFiniteSetoid
--   open LeftInverse

--   private
--     boolToFin : Bool → Fin 2
--     boolToFin false = Fin.zero
--     boolToFin true = Fin.suc Fin.zero

--     open Nat using (_^_; _+_; _*_)

--     _ℕ+_ : ∀ {n} (m : ℕ) → Fin n → Fin (m + n)
--     zero ℕ+ i = i
--     suc m ℕ+ i = Fin.suc (m ℕ+ i)

--     -- ℕ*-+ m "i" "k" = "i + m * k"
--     ℕ*-+′ : ∀ {n} (m : ℕ) → Fin m → Fin n → Fin (n * m)
--     ℕ*-+′ {zero} m i ()
--     ℕ*-+′ {suc n} m i Fin.zero = Fin.inject+ (n * m) i
--     ℕ*-+′ {suc n} m i (Fin.suc k) = m ℕ+ (ℕ*-+′ m i k)

--     -- ℕ*-+ m "i" "k" = "i + m * k"
--     ℕ*-+ : ∀ {n} m → Fin m → Fin n → Fin (m * n)
--     ℕ*-+ m i k = ≡.subst Fin (Nat.*-comm _ m) (ℕ*-+′ m i k)

--     from-n-ary : ∀ {n m} → Table (Fin n) m → Fin (n ^ m)
--     from-n-ary {m = zero} _ = Fin.zero -- an empty table represents an empty n-ary string
--     from-n-ary {n} {suc m} t = ℕ*-+ n (Table.lookup t Fin.zero) (from-n-ary (Table.tail t))

--     from-n-ary-cong : ∀ {n m} (t t′ : Table (Fin n) m) → t Table.≗ t′ → from-n-ary t ≡ from-n-ary t′
--     from-n-ary-cong {m = zero} t t′ eq = ≡.refl
--     from-n-ary-cong {n} {suc m} t t′ eq = ≡.cong₂ (ℕ*-+ n) (eq Fin.zero) (from-n-ary-cong (Table.tail t) (Table.tail t′) (eq ∘ Fin.suc))

--     -- div-rem m i = "i % m" , "i / m"
--     div-rem : ∀ {n} m → Fin (m * n) → Fin m × Fin n
--     div-rem zero ()
--     div-rem {zero} (suc m) i with ≡.subst Fin (Nat.*-zeroʳ m) i
--     div-rem {zero} (suc m) i | ()
--     div-rem {suc n} (suc m) Fin.zero = Fin.zero , Fin.zero
--     div-rem {suc n} (suc m) (Fin.suc i) = {!!}

--     to-n-ary : ∀ {n m} → Fin (n ^ m) → Table (Fin n) m
--     to-n-ary {m = zero} _ = tabulate λ ()
--     to-n-ary {n} {suc m} k =
--       let r , d = div-rem n k
--           ind = to-n-ary {n} {m} d
--       in tabulate λ
--          { Fin.zero → r
--          ; (Fin.suc i) → lookup ind i
--          }

--   boolF-isFiniteSetoid : IsFiniteSetoid (A.setoid ⇨ ≡.setoid Bool) (2 Nat.^ A.N)
--   _⟨$⟩_ (to (ontoFin boolF-isFiniteSetoid)) f =
--     let digits = Table.map (boolToFin ∘ (f ⟨$⟩_)) A.enumₜ
--     in from-n-ary digits
--   cong (to (ontoFin boolF-isFiniteSetoid)) {f} {f′} p =
--     let t = Table.map (boolToFin ∘ (f ⟨$⟩_)) A.enumₜ
--     in from-n-ary-cong t _ λ _ → ≡.cong boolToFin (p A.refl)
--   _⟨$⟩_ (_⟨$⟩_ (from (ontoFin boolF-isFiniteSetoid)) i) x = {!!}
--   cong (_⟨$⟩_ (from (ontoFin boolF-isFiniteSetoid)) i) = {!!}
--   cong (from (ontoFin boolF-isFiniteSetoid)) ≡.refl = {!≡.cong ?!}
--   left-inverse-of (ontoFin boolF-isFiniteSetoid) = {!!}

-- module _ {c₁ ℓ₁ c₂ ℓ₂} (A-finiteSet : FiniteSet c₁ ℓ₁) (B-finiteSet : FiniteSet c₂ ℓ₂) where
--   private
--     module A = FiniteSet A-finiteSet
--     module B = FiniteSet B-finiteSet

--   open A using (_≈_) renaming (Carrier to A)
--   open B using () renaming (Carrier to B; _≈_ to _≈′_)

--   open IsFiniteSetoid
--   open LeftInverse

--   function-isFiniteSetoid : IsFiniteSetoid (A.setoid ⇨ B.setoid) (B.N Nat.^ A.N)
--   _⟨$⟩_ (to (ontoFin function-isFiniteSetoid)) f = {!!}
--   cong (to (ontoFin function-isFiniteSetoid)) = {!!}
--   _⟨$⟩_ (from (ontoFin function-isFiniteSetoid)) i = {!!}
--   cong (from (ontoFin function-isFiniteSetoid)) = {!!}
--   left-inverse-of (ontoFin function-isFiniteSetoid) = {!!}

-- TODO: Prove dependent function spaces with finite domain and codomain are
-- finite sets, and recast as an instance of that.

module _ {a p ℓ} {A : Set a} (finiteAt : A → FiniteSet p ℓ) where
  private
    module PW x = FiniteSet (finiteAt x)
    module PW′ {x} = PW x
    open PW using () renaming (Carrier to P; N to boundAt)
    open PW′ using (_≈_)

    All≈ = All′.PW PW.setoid
    All-setoid = All′.PW-setoid PW.setoid
    open All′.PW PW.setoid

  finiteAllSize : List A → ℕ
  finiteAllSize = List.product ∘ List.map boundAt

  All-isFiniteSetoid : (xs : List A) → IsFiniteSetoid (All-setoid xs) _
  All-isFiniteSetoid _ = record
    { ontoFin = record
      { to = record { _⟨$⟩_ = to ; cong = to-cong }
      ; from = ≡.→-to-⟶ from
      ; left-inverse-of = left-inverse-of
      }
    }
    where
      to : ∀ {xs} → All P xs → Fin (finiteAllSize xs)
      to [] = Fin.zero
      to (_∷_ {x} {xs} px ap) = PS.fromParts (PW.toIx _ px , to ap)

      to-cong : ∀ {xs}{apx apy : All P xs} → All≈ apx apy → to apx ≡ to apy
      to-cong [] = ≡.refl
      to-cong (p ∷ q) = ≡.cong₂ (curry PS.fromParts) (cong (LeftInverse.to PW′.ontoFin) p) (to-cong q)

      from : ∀ {xs} → Fin (finiteAllSize xs) → All P xs
      from {List.[]} _ = []
      from {x List.∷ xs} i =
        (PW.fromIx _ (proj₁ (PS.toParts i))) ∷
        from {xs} (proj₂ (PS.toParts {boundAt x} i))

      left-inverse-of : ∀ {xs} (ap : All P xs) → All≈ (from (to ap)) ap
      left-inverse-of [] = Setoid.refl (All-setoid _)
      left-inverse-of (px ∷ ap) rewrite Inverse.right-inverse-of PS.asParts (PW.toIx _ px , to ap) =
        PW′.fromIx-toIx px ∷ left-inverse-of ap

  All-finiteSet : List A → FiniteSet _ _
  All-finiteSet xs = record { isFiniteSetoid = All-isFiniteSetoid xs }

1-Truncate : ∀ {c ℓ} (setoid : Setoid c ℓ) → Setoid _ _
1-Truncate setoid = record
  { Carrier = Carrier
  ; _≈_ = λ _ _ → ⊤
  ; isEquivalence = record { refl = _ ; sym = _ ; trans = _ }
  }
  where open Setoid setoid

module DecFinite {a p} {A : Set a} {P : A → Set p} (decP : U.Decidable P) where
  P-setoid : A → Setoid _ _
  P-setoid = 1-Truncate ∘ ≡.setoid ∘ P

  P-size : A → ℕ
  P-size x = if ⌊ decP x ⌋ then 1 else 0

  P-isFinite : ∀ x → IsFiniteSetoid (P-setoid x) (P-size x)
  P-isFinite x with decP x
  P-isFinite x | yes p = record
    { ontoFin = record
      { to = record
        { _⟨$⟩_ = λ _ → Fin.zero ; cong = λ _ → ≡.refl }
        ; from = ≡.→-to-⟶ λ _ → p
        ; left-inverse-of = _
      }
    }
  P-isFinite x | no ¬p = record
    { ontoFin = record
      { to = record { _⟨$⟩_ = ⊥-elim ∘ ¬p ; cong = λ {x} → ⊥-elim (¬p x) }
      ; from = ≡.→-to-⟶ λ ()
      ; left-inverse-of = _
      }
    }

  Decidable-finite : A → FiniteSet p _
  Decidable-finite x = record { isFiniteSetoid = P-isFinite x }

module _ {c} {A : Set c} {N} (isFiniteSet : IsFiniteSet A N) {p} {P : A → Set p} (decP : U.Decidable P) where
  open IsFiniteSetoid isFiniteSet

  subsetFinite : FiniteSet _ _
  subsetFinite = Σ-finiteSet isFiniteSet (DecFinite.Decidable-finite decP)

  subset-isFiniteSet : ∀ {a′} {A′ : Set a′} → LeftInverse (≡.setoid A′) (FiniteSet.setoid subsetFinite) → IsFiniteSet A′ _
  subset-isFiniteSet = extendedIsFinite subsetFinite


module Utils.NatOrdLemmas where

open import Data.Nat.Properties.Simple
open import Data.Nat
open import Data.Empty
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Function

{- Just a bunch of lemmas about the standard ordering on naturals -}

data Cmp (a b : ℕ) : Set where
  LT : a Data.Nat.< b → Cmp a b
  EQ : a ≡ b → Cmp a b
  GT : a > b → Cmp a b

cmp : ∀ a b → Cmp a b
cmp zero zero = EQ refl
cmp zero (suc b) = LT (s≤s z≤n)
cmp (suc a) zero = GT (s≤s z≤n)
cmp (suc a) (suc b) with cmp a b
cmp (suc a) (suc b) | LT x = LT (s≤s x)
cmp (suc a) (suc .a) | EQ refl = EQ refl
cmp (suc a) (suc b) | GT x = GT (s≤s x)

a≮a : ∀ a → ¬ (a < a)
a≮a zero ()
a≮a (suc a) (s≤s p) = a≮a a p

<-weakenr1 : ∀ a b → a < b → a < suc b
<-weakenr1 zero b p = s≤s z≤n
<-weakenr1 (suc a) zero ()
<-weakenr1 (suc a) (suc b) (s≤s p) = s≤s (<-weakenr1 a b p)

<-weakenr : ∀ c a b → a < b → a < c + b
<-weakenr zero a b p = p
<-weakenr (suc c) a b p = <-weakenr1 a (c + b) (<-weakenr c a b p)

<-weakenl1 : ∀ a b → suc a < b → a < b
<-weakenl1 zero ._ (s≤s p) = s≤s z≤n
<-weakenl1 (suc a) zero ()
<-weakenl1 (suc a) (suc b) (s≤s p) = s≤s (<-weakenl1 a b p)

<-weakenl : ∀ c a b → c + a < b → a < b
<-weakenl zero a b p = p
<-weakenl (suc c) a b p = <-weakenl c a b (<-weakenl1 (c + a) b p)

a≢sa : (a : ℕ) → a ≢ suc a
a≢sa a ()

a<b⟶a≢sb : ∀ a b → a < b → a ≢ suc b
a<b⟶a≢sb zero b p1 ()
a<b⟶a≢sb (suc ._) ._ (s≤s p1) refl = a<b⟶a≢sb _ _ p1 refl

a<v⟶a≢c+b : ∀ c a b → a < b → a ≢ c + b
a<v⟶a≢c+b zero a .a p1 refl = a≮a a p1
a<v⟶a≢c+b (suc c) a b p1 p2 = a<b⟶a≢sb a (c + b) (<-weakenr c a b p1) p2

a<sb+a : ∀ a b → a < suc b + a
a<sb+a zero b = s≤s z≤n
a<sb+a (suc a) b rewrite +-suc b a = s≤s (a<sb+a a b)

a<b+sa : ∀ a b → a < b + suc a
a<b+sa a b rewrite +-suc b a = a<sb+a a b

≤-strict : ∀ a b → a ≤ b → a ≢ b → a < b
≤-strict zero zero z≤n p2 = ⊥-elim (p2 refl)
≤-strict zero (suc b) z≤n p2 = s≤s z≤n
≤-strict (suc a) zero () p2
≤-strict (suc a) (suc b) (s≤s p1) p2 = s≤s (≤-strict a b p1 (p2 ∘ cong suc))

a≢sa+b : ∀ a b → a ≢ suc a + b
a≢sa+b zero b ()
a≢sa+b (suc a) b p = a≢sa+b a b $ cong pred p

a≢sb+a : ∀ a b → a ≢ suc b + a
a≢sb+a a b p rewrite +-comm b a = a≢sa+b a b p


module Data.Proofs where

import      Lvl
open import Data
open import Logic
open import Logic.Propositional
open import Structure.Setoid renaming (_≡_ to _≡ₛ_)
open import Structure.Function
open import Structure.Function.Domain
open import Structure.Function.Domain.Proofs
open import Structure.Relator.Equivalence
open import Structure.Relator.Properties
open import Type
open import Type.Properties.Empty
open import Type.Properties.Singleton

private variable ℓ ℓₑ ℓₑ₁ ℓₑ₂ ℓ₁ ℓ₂ : Lvl.Level
private variable T : Type{ℓ}

module General where
  module _ {_▫_ : Empty{ℓ} → Empty{ℓ} → Stmt{ℓₑ}} where
    Empty-equiv : Equiv(Empty)
    Equiv._≡_ Empty-equiv = _▫_
    Reflexivity.proof  (Equivalence.reflexivity  (Equiv.equivalence Empty-equiv)) {}
    Symmetry.proof     (Equivalence.symmetry     (Equiv.equivalence Empty-equiv)) {}
    Transitivity.proof (Equivalence.transitivity (Equiv.equivalence Empty-equiv)) {}

  -- Any binary relation on Unit is an equivalence given that it is reflexive.
  module _ {_▫_ : Unit{ℓ} → Unit{ℓ} → Stmt{ℓ}} ⦃ proof : (<> ▫ <>) ⦄ where
    Unit-equiv : Equiv(Unit)
    Equiv._≡_ Unit-equiv = (_▫_)
    Reflexivity.proof  (Equivalence.reflexivity  (Equiv.equivalence Unit-equiv))       = proof
    Symmetry.proof     (Equivalence.symmetry     (Equiv.equivalence Unit-equiv)) _     = proof
    Transitivity.proof (Equivalence.transitivity (Equiv.equivalence Unit-equiv)) _ _   = proof

module _ where
  open import Relator.Equals

  instance
    Empty-equiv : Equiv(Empty{ℓ})
    Empty-equiv = General.Empty-equiv {_▫_ = _≡_}

  instance
    Unit-equiv : Equiv(Unit{ℓ})
    Unit-equiv = General.Unit-equiv ⦃ [≡]-intro {x = <>} ⦄

  {- TODO: So, why is this unprovable but Unit-IsUnit is? UIP? What is the difference?
  module _ where
    open import Relator.Equals.Proofs.Equiv
    testee : ∀{T : Type{ℓ}}{a : T} → IsUnit{ℓ}(a ≡ a)
    IsUnit.unit       testee     = [≡]-intro
    IsUnit.uniqueness testee {x} = {!!}
  -}

instance
  -- `Unit` is an unit type.
  Unit-IsUnit : ⦃ equiv : Equiv{ℓₑ}(Unit) ⦄ → IsUnit{ℓ}(Unit)
  IsUnit.unit       Unit-IsUnit = <>
  IsUnit.uniqueness Unit-IsUnit = reflexivity(_≡ₛ_)

instance
  -- `Empty` is an empty type.
  Empty-IsEmpty : IsEmpty{ℓ}(Empty)
  Empty-IsEmpty = intro(empty)

module _ ⦃ _ : Equiv{ℓₑ₁}(T) ⦄ ⦃ empty-equiv : Equiv{ℓₑ₂}(Empty{ℓ₂}) ⦄ where
  instance
    empty-injective : Injective ⦃ empty-equiv ⦄(empty{T = T})
    Injective.proof(empty-injective) {}

  instance
    empty-function : Function ⦃ empty-equiv ⦄(empty{T = T})
    Function.congruence empty-function {()}

module _ ⦃ equiv : Equiv{ℓₑ}(T) ⦄ where -- TODO: Duplicated in Type.Properties.Singleton.Proofs
  Unit-fn-unique-value : ∀{f : Unit{ℓ} → T} → (∀{x y} → (f(x) ≡ₛ f(y)))
  Unit-fn-unique-value {x = <>} {y = <>} = reflexivity(_≡ₛ_)

module _ ⦃ equiv : Equiv{ℓₑ}(Unit{ℓ₁}) ⦄ where
  Unit-fn-unique-fn : ∀{f g : T → Unit{ℓ₁}} → (∀{x y} → (_≡ₛ_ ⦃ equiv ⦄ (f(x)) (g(y))))
  Unit-fn-unique-fn {f = f}{g = g}{x = x}{y = y} with f(x) | g(y)
  ... | <> | <> = reflexivity(_≡ₛ_ ⦃ equiv ⦄)


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

module LogicalTopology where
  open import Basics

  open import lib.types.Truncation

  -- For convenience, we define the "power type" ℘ A
  ℘ : {i : ULevel} (j : ULevel) (A : Type i) → Type _
  ℘ j A = A → Prop j

  _∩_ : {i j k : ULevel} {A : Type i}
        → ℘ j A → ℘ k A → ℘ _ A
  U ∩ V = λ a → (U a) ∧ (V a)

  _∪_ : ∀ {i j k} {A : Type i}
        → ℘ j A → ℘ k A → ℘ _ A
  P ∪ Q = λ a → (P a) ∨ (Q a)

  ⋃ : {i j : ULevel} {A : Type i}
      {I : Type j} (U : I → ℘ (lmax i j) A)
      → ℘ _ A
  ⋃ U = λ a → ∃ₚ λ i → (U i a)

  infix 30 _⊆_
  _⊆_ : ∀ {i j k} {A : Type i} (P : ℘ j A) (Q : ℘ k A)
        → Prop _
  _⊆_ {A = A} P Q = ∀ₚ \(a : A) → (P a) ⇒ (Q a)

  -- The definition of logically (Penon) open.
  _is-open : {i j : ULevel} {A : Type i} (U : ℘ j A) → Type (lmax i j)
  U is-open = ∀ x y → (p : (U x) holds) → ∥ (x ≠ y) ⊔ ((U y) holds) ∥

  _is-closed : ∀ {i j} {A : Type i} (U : ℘ j A) → Type (lmax i j)
  C is-closed = (not ∘ C) is-open

  maps-are-cts : {i j k : ULevel} {A : Type i} {B : Type j}
                 {f : A → B} {U : ℘ k B} (p : U is-open)
                 → (U ∘ f) is-open
  maps-are-cts {A = A} {f = f} {U = U} p x y q =
    let[ p' ]:= (p (f x) (f y) q) in-
      [ (case₀ ⊔→ case₁) p' ]
    where
      case₀ : (f x ≠ f y) → x ≠ y
      case₀ ne p = ne (ap f p)

      case₁ : (U (f y)) holds → (U (f y)) holds
      case₁ z = z

  maps-are-cts-closed : {i j k : ULevel} {A : Type i} {B : Type j}
                        {f : A → B} {C : ℘ k B} (p : C is-closed)
                        → (C ∘ f) is-closed
  maps-are-cts-closed {C = C} = maps-are-cts {U = not ∘ C}

  unions-of-opens-are-open : {i j : ULevel} {A : Type i}
                             {I : Type j} (U : I → ℘ _ A)
                             (o : (i : I) → (U i) is-open)
                             → (⋃ U) is-open
  unions-of-opens-are-open U o x y p = -- Supposing that x is in ⋃ U,
    let[ q ]:= p in- -- We know that x is in some particular U i (i ≡ fst q), so
      let[ z ]:= (o (fst q) x y (snd q)) in- -- we use the open-ness of U i to split into two cases
           [ z |> -- z : (x ≠ y) ⊔ (U i y) holds
             ((idf (x ≠ y)) -- Case₀: x already isn't y
                   ⊔→
             ((λ (w : U (fst q) y holds) → -- Case₁: y is in U i and therefore ⋃ U
               [ (fst q) , w ])))
           ]

  _is-logically-connected : ∀ {i j} {A : Type i} (U : ℘ j A) → Prop _
  _is-logically-connected {j = j} {A = A} U =
    ∀ₚ λ (P : ℘ j A) → (U ⊆ (P ∪ (not ∘ P)))
                     ⇒ ((U ⊆ P) ∨ (U ⊆ not ∘ P))

  _is-detachable : ∀ {i j} {A : Type i} (P : ℘ j A) → Prop _
  _is-detachable {A = A} P =
    ∀ₚ λ (a : A) → (P a) ∨ (not $ P a)

  _is-inhabited : ∀ {i j} {A : Type i} (P : ℘ j A) → Prop _
  _is-inhabited {A = A} P =
    ∃ₚ λ (a : A) → P a

  _is-logical-ctd-cmp : ∀ {i j} {A : Type i} (P : ℘ j A) → Prop _
  P is-logical-ctd-cmp =
    (P is-inhabited) ∧
    (P is-detachable) ∧
    (P is-logically-connected)


open import Formalization.PredicateLogic.Signature

module Formalization.PredicateLogic.Constructive.NaturalDeduction (𝔏 : Signature) where
open Signature(𝔏)

open import Data.ListSized
import      Lvl
open import Formalization.PredicateLogic.Syntax(𝔏)
open import Formalization.PredicateLogic.Syntax.Substitution(𝔏)
open import Functional using (_∘_ ; _∘₂_ ; swap)
open import Numeral.Finite
open import Numeral.Natural
open import Relator.Equals.Proofs.Equiv
open import Sets.PredicateSet using (PredSet ; _∈_ ; _∉_ ; _∪_ ; _∪•_ ; _∖_ ; _⊆_ ; _⊇_ ; ∅ ; [≡]-to-[⊆] ; [≡]-to-[⊇]) renaming (•_ to · ; _≡_ to _≡ₛ_)
open import Type

private variable ℓ : Lvl.Level
private variable args vars : ℕ
private variable Γ : PredSet{ℓ}(Formula(vars))

data _⊢_ {ℓ} : PredSet{ℓ}(Formula(vars)) → Formula(vars) → Type{Lvl.𝐒(ℓₚ Lvl.⊔ ℓₒ Lvl.⊔ ℓ)} where
  direct : (Γ ⊆ (Γ ⊢_))

  [⊤]-intro : (Γ ⊢ ⊤)

  [⊥]-elim  : ∀{φ} → (Γ ⊢ ⊥) → (Γ ⊢ φ)

  [∧]-intro : ∀{φ ψ} → (Γ ⊢ φ) → (Γ ⊢ ψ) → (Γ ⊢ (φ ∧ ψ))
  [∧]-elimₗ : ∀{φ ψ} → (Γ ⊢ (φ ∧ ψ)) → (Γ ⊢ φ)
  [∧]-elimᵣ : ∀{φ ψ} → (Γ ⊢ (φ ∧ ψ)) → (Γ ⊢ ψ)

  [∨]-introₗ : ∀{φ ψ} → (Γ ⊢ φ) → (Γ ⊢ (φ ∨ ψ))
  [∨]-introᵣ : ∀{φ ψ} → (Γ ⊢ ψ) → (Γ ⊢ (φ ∨ ψ))
  [∨]-elim   : ∀{φ ψ χ} → ((Γ ∪ · φ) ⊢ χ) → ((Γ ∪ · ψ) ⊢ χ) → (Γ ⊢ (φ ∨ ψ)) → (Γ ⊢ χ)

  [⟶]-intro : ∀{φ ψ} → ((Γ ∪ · φ) ⊢ ψ) → (Γ ⊢ (φ ⟶ ψ))
  [⟶]-elim  : ∀{φ ψ} → (Γ ⊢ φ) → (Γ ⊢ (φ ⟶ ψ)) → (Γ ⊢ ψ)

  [Ɐ]-intro : ∀{φ} → (∀{t} → (Γ ⊢ (substitute0 t φ))) → (Γ ⊢ (Ɐ φ))
  [Ɐ]-elim  : ∀{φ} → (Γ ⊢ (Ɐ φ)) → ∀{t} → (Γ ⊢ (substitute0 t φ))

  [∃]-intro : ∀{φ}{t} → (Γ ⊢ (substitute0 t φ)) → (Γ ⊢ (∃ φ))
  [∃]-elim  : ∀{φ ψ} → (∀{t} → (Γ ∪ ·(substitute0 t φ)) ⊢ ψ) → (Γ ⊢ (∃ φ)) → (Γ ⊢ ψ)


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

open import Cubical.Core.Everything

module Cubical.Algebra.Structures {a} (A : Type a) where

-- The file is divided into sections depending on the arities of the
-- components of the algebraic structure.

open import Cubical.Foundations.Prelude using (isSet; cong; _∙_)
open import Cubical.Foundations.HLevels using (hSet)

open import Cubical.Algebra.Base
open import Cubical.Algebra.Definitions
open import Cubical.Algebra.Properties
open import Cubical.Data.Sigma using (_,_; fst; snd)
open import Cubical.Data.Nat.Base renaming (zero to ℕ-zero; suc to ℕ-suc)
open import Cubical.Data.Int.Base
open import Cubical.Data.NatPlusOne.Base

open import Cubical.Relation.Nullary using (¬_)
open import Cubical.Relation.Binary.Reasoning.Equality

_NotEqualTo_ : A → Type a
_NotEqualTo_ z = Σ[ x ∈ A ] ¬ (x ≡ z)

------------------------------------------------------------------------
-- Algebra with 1 binary operation
------------------------------------------------------------------------

record IsMagma (_•_ : Op₂ A) : Type a where
  constructor ismagma
  field
    is-set : isSet A

  set : hSet a
  set = A , is-set

record IsSemigroup (_•_ : Op₂ A) : Type a where
  constructor issemigroup
  field
    isMagma : IsMagma _•_
    assoc   : Associative _•_

  open IsMagma isMagma public

  infixl 10 _^_
  _^_ : A → ℕ₊₁ → A
  x ^ one    = x
  x ^ (2+ n) = x • (x ^ 1+ n)

record IsBand (_•_ : Op₂ A) : Type a where
  constructor isband
  field
    isSemigroup : IsSemigroup _•_
    idem        : Idempotent _•_

  open IsSemigroup isSemigroup public

record IsCommutativeSemigroup (_•_ : Op₂ A) : Type a where
  constructor iscommsemigroup
  field
    isSemigroup : IsSemigroup _•_
    comm        : Commutative _•_

  open IsSemigroup isSemigroup public

record IsSemilattice (_•_ : Op₂ A) : Type a where
  constructor issemilattice
  field
    isBand : IsBand _•_
    comm   : Commutative _•_

  open IsBand isBand public

record IsSelectiveMagma (_•_ : Op₂ A) : Type a where
  constructor isselmagma
  field
    isMagma : IsMagma _•_
    sel     : Selective _•_

  open IsMagma isMagma public

------------------------------------------------------------------------
-- Algebra with 1 binary operation & 1 element
------------------------------------------------------------------------

record IsMonoid (_•_ : Op₂ A) (ε : A) : Type a where
  constructor ismonoid
  field
    isSemigroup : IsSemigroup _•_
    identity    : Identity ε _•_

  open IsSemigroup isSemigroup public hiding (_^_)

  identityˡ : LeftIdentity ε _•_
  identityˡ = fst identity

  identityʳ : RightIdentity ε _•_
  identityʳ = snd identity

  ε-uniqueʳ : ∀ {e} → RightIdentity e _•_ → ε ≡ e
  ε-uniqueʳ idʳ = id-unique′ identityˡ idʳ

  ε-uniqueˡ : ∀ {e} → LeftIdentity e _•_ → e ≡ ε
  ε-uniqueˡ idˡ = id-unique′ idˡ identityʳ

  infixl 10 _^_
  _^_ : A → ℕ → A
  _ ^ ℕ-zero    = ε
  x ^ (ℕ-suc n) = x • (x ^ n)

record IsCommutativeMonoid (_•_ : Op₂ A) (ε : A) : Type a where
  constructor iscommmonoid
  field
    isMonoid : IsMonoid _•_ ε
    comm     : Commutative _•_

  open IsMonoid isMonoid public

  isCommutativeSemigroup : IsCommutativeSemigroup _•_
  isCommutativeSemigroup = record
    { isSemigroup = isSemigroup
    ; comm        = comm
    }

record IsIdempotentCommutativeMonoid (_•_ : Op₂ A) (ε : A) : Type a where
  constructor isidemcommmonoid
  field
    isCommutativeMonoid : IsCommutativeMonoid _•_ ε
    idem                : Idempotent _•_

  open IsCommutativeMonoid isCommutativeMonoid public

-- Idempotent commutative monoids are also known as bounded lattices.
-- Note that the BoundedLattice necessarily uses the notation inherited
-- from monoids rather than lattices.

IsBoundedLattice = IsIdempotentCommutativeMonoid
pattern isboundedlattice = isidemcommmonoid

module IsBoundedLattice {_•_ : Op₂ A}
                        {ε : A}
                        (isIdemCommMonoid : IsIdempotentCommutativeMonoid _•_ ε) =
       IsIdempotentCommutativeMonoid isIdemCommMonoid

------------------------------------------------------------------------
-- Algebra with 1 binary operation, 1 unary operation & 1 element
------------------------------------------------------------------------

record IsGroup (_•_ : Op₂ A) (ε : A) (_⁻¹ : Op₁ A) : Type a where
  constructor isgroup
  field
    isMonoid  : IsMonoid _•_ ε
    inverse   : Inverse ε _⁻¹ _•_

  open IsMonoid isMonoid public hiding (_^_)

  infixl 10 _^_
  _^_ : A → ℤ → A
  x ^ pos ℕ-zero       = ε
  x ^ pos (ℕ-suc n)    = x • (x ^ pos n)
  x ^ negsuc ℕ-zero    = x ⁻¹
  x ^ negsuc (ℕ-suc n) = (x ⁻¹) • (x ^ negsuc n)

  -- Right division
  infixl 6 _/_
  _/_ : Op₂ A
  x / y = x • (y ⁻¹)

  -- Left division
  infixr 6 _/ˡ_
  _/ˡ_ : Op₂ A
  x /ˡ y = (x ⁻¹) • y

  inverseˡ : LeftInverse ε _⁻¹ _•_
  inverseˡ = fst inverse

  inverseʳ : RightInverse ε _⁻¹ _•_
  inverseʳ = snd inverse

  inv-uniqueʳ : ∀ x y → (x • y) ≡ ε → x ≡ (y ⁻¹)
  inv-uniqueʳ = assoc+id+invʳ⇒invʳ-unique assoc identity inverseʳ

  inv-uniqueˡ : ∀ x y → (x • y) ≡ ε → y ≡ (x ⁻¹)
  inv-uniqueˡ = assoc+id+invˡ⇒invˡ-unique assoc identity inverseˡ

  cancelˡ : LeftCancellative _•_
  cancelˡ = assoc+idˡ+invˡ⇒cancelˡ assoc identityˡ inverseˡ

  cancelʳ : RightCancellative _•_
  cancelʳ = assoc+idʳ+invʳ⇒cancelʳ assoc identityʳ inverseʳ

record IsAbelianGroup (_+_ : Op₂ A) (ε : A) (-_ : Op₁ A) : Type a where
  constructor isabgroup
  field
    isGroup : IsGroup _+_ ε -_
    comm    : Commutative _+_

  open IsGroup isGroup public hiding (_/ˡ_) renaming (_/_ to _-_; _^_ to _*_)

  isCommutativeMonoid : IsCommutativeMonoid _+_ ε
  isCommutativeMonoid = record
    { isMonoid = isMonoid
    ; comm     = comm
    }

  open IsCommutativeMonoid isCommutativeMonoid public
    using (isCommutativeSemigroup)

------------------------------------------------------------------------
-- Algebra with 2 binary operations
------------------------------------------------------------------------

-- Note that `IsLattice` is not defined in terms of `IsSemilattice`
-- because the idempotence laws of ⋀ and ⋁ can be derived from the
-- absorption laws, which makes the corresponding "idem" fields
-- redundant.  The derived idempotence laws are stated and proved in
-- `Algebra.Properties.Lattice` along with the fact that every lattice
-- consists of two semilattices.

record IsLattice (_⋀_ _⋁_ : Op₂ A) : Type a where
  constructor islattice
  field
    ⋀-comm        : Commutative _⋀_
    ⋀-assoc       : Associative _⋀_
    ⋁-comm        : Commutative _⋁_
    ⋁-assoc       : Associative _⋁_
    absorptive    : Absorptive _⋀_ _⋁_

  ⋀-absorbs-⋁ : _⋀_ Absorbs _⋁_
  ⋀-absorbs-⋁ = fst absorptive

  ⋁-absorbs-⋀ : _⋁_ Absorbs _⋀_
  ⋁-absorbs-⋀ = snd absorptive

record IsDistributiveLattice (_⋀_ _⋁_ : Op₂ A) : Type a where
  constructor isdistrlattice
  field
    isLattice    : IsLattice _⋀_ _⋁_
    ⋀-distribʳ-⋁ : _⋀_ DistributesOverʳ _⋁_

  open IsLattice isLattice public

------------------------------------------------------------------------
-- Algebra with 2 binary operations & 1 element
------------------------------------------------------------------------

record IsNearSemiring (_+_ _*_ : Op₂ A) (0# : A) : Type a where
  constructor isnearsemiring
  field
    +-isMonoid    : IsMonoid _+_ 0#
    *-isSemigroup : IsSemigroup _*_
    distribˡ      : _*_ DistributesOverˡ _+_
    zeroˡ         : LeftZero 0# _*_

  open IsMonoid +-isMonoid public
    renaming
    ( assoc       to +-assoc
    ; identity    to +-identity
    ; identityˡ   to +-identityˡ
    ; identityʳ   to +-identityʳ
    ; ε-uniqueˡ   to 0#-uniqueˡ
    ; ε-uniqueʳ   to 0#-uniqueʳ
    ; isMagma     to +-isMagma
    ; isSemigroup to +-isSemigroup
    ; _^_         to _**_
    )

  open IsSemigroup *-isSemigroup public
    using (_^_)
    renaming
    ( assoc    to *-assoc
    ; isMagma  to *-isMagma
    )

record IsSemiringWithoutOne (_+_ _*_ : Op₂ A) (0# : A) : Type a where
  constructor issemiringwo1
  field
    +-isCommutativeMonoid : IsCommutativeMonoid _+_ 0#
    *-isSemigroup         : IsSemigroup _*_
    distrib               : _*_ DistributesOver _+_
    zero                  : Zero 0# _*_

  open IsCommutativeMonoid +-isCommutativeMonoid public
    using ()
    renaming
    ( comm                   to +-comm
    ; isMonoid               to +-isMonoid
    ; isCommutativeSemigroup to +-isCommutativeSemigroup
    )

  zeroˡ : LeftZero 0# _*_
  zeroˡ = fst zero

  zeroʳ : RightZero 0# _*_
  zeroʳ = snd zero

  distribˡ : _*_ DistributesOverˡ _+_
  distribˡ = fst distrib

  distribʳ : _*_ DistributesOverʳ _+_
  distribʳ = snd distrib

  isNearSemiring : IsNearSemiring _+_ _*_ 0#
  isNearSemiring = record
    { +-isMonoid    = +-isMonoid
    ; *-isSemigroup = *-isSemigroup
    ; distribˡ      = distribˡ
    ; zeroˡ         = zeroˡ
    }

  open IsNearSemiring isNearSemiring public
    hiding (+-isMonoid; zeroˡ; *-isSemigroup; distribˡ)

record IsCommutativeSemiringWithoutOne (_+_ _*_ : Op₂ A) (0# : A) : Type a where
  constructor iscommsemiringwo1
  field
    isSemiringWithoutOne : IsSemiringWithoutOne _+_ _*_ 0#
    *-comm               : Commutative _*_

  open IsSemiringWithoutOne isSemiringWithoutOne public

------------------------------------------------------------------------
-- Algebra with 2 binary operations & 2 elements
------------------------------------------------------------------------

record IsSemiringWithoutAnnihilatingZero (_+_ _*_ : Op₂ A)
                                         (0# 1# : A) : Type a where
  constructor issemiringwoa0
  field
    -- Note that these Algebra do have an additive unit, but this
    -- unit does not necessarily annihilate multiplication.
    +-isCommutativeMonoid : IsCommutativeMonoid _+_ 0#
    *-isMonoid            : IsMonoid _*_ 1#
    distrib               : _*_ DistributesOver _+_

  distribˡ : _*_ DistributesOverˡ _+_
  distribˡ = fst distrib

  distribʳ : _*_ DistributesOverʳ _+_
  distribʳ = snd distrib

  open IsCommutativeMonoid +-isCommutativeMonoid public
    renaming
    ( assoc                  to +-assoc
    ; identity               to +-identity
    ; identityˡ              to +-identityˡ
    ; identityʳ              to +-identityʳ
    ; ε-uniqueˡ              to 0#-uniqueˡ
    ; ε-uniqueʳ              to 0#-uniqueʳ
    ; comm                   to +-comm
    ; isMagma                to +-isMagma
    ; isSemigroup            to +-isSemigroup
    ; isMonoid               to +-isMonoid
    ; isCommutativeSemigroup to +-isCommutativeSemigroup
    ; _^_                    to _**_
    )

  open IsMonoid *-isMonoid public
    using ()
    renaming
    ( assoc       to *-assoc
    ; identity    to *-identity
    ; identityˡ   to *-identityˡ
    ; identityʳ   to *-identityʳ
    ; ε-uniqueˡ   to 1#-uniqueˡ
    ; ε-uniqueʳ   to 1#-uniqueʳ
    ; isMagma     to *-isMagma
    ; isSemigroup to *-isSemigroup
    )

record IsSemiring (_+_ _*_ : Op₂ A) (0# 1# : A) : Type a where
  constructor issemiring
  field
    isSemiringWithoutAnnihilatingZero :
      IsSemiringWithoutAnnihilatingZero _+_ _*_ 0# 1#
    zero : Zero 0# _*_

  open IsSemiringWithoutAnnihilatingZero
         isSemiringWithoutAnnihilatingZero public

  isSemiringWithoutOne : IsSemiringWithoutOne _+_ _*_ 0#
  isSemiringWithoutOne = record
    { +-isCommutativeMonoid = +-isCommutativeMonoid
    ; *-isSemigroup         = *-isSemigroup
    ; distrib               = distrib
    ; zero                  = zero
    }

  open IsSemiringWithoutOne isSemiringWithoutOne public
    using
    ( isNearSemiring
    ; zeroˡ
    ; zeroʳ
    )

record IsCommutativeSemiring (_+_ _*_ : Op₂ A) (0# 1# : A) : Type a where
  constructor iscommsemiring
  field
    isSemiring : IsSemiring _+_ _*_ 0# 1#
    *-comm     : Commutative _*_

  open IsSemiring isSemiring public

  isCommutativeSemiringWithoutOne :
    IsCommutativeSemiringWithoutOne _+_ _*_ 0#
  isCommutativeSemiringWithoutOne = record
    { isSemiringWithoutOne = isSemiringWithoutOne
    ; *-comm = *-comm
    }

  *-isCommutativeSemigroup : IsCommutativeSemigroup _*_
  *-isCommutativeSemigroup = record
    { isSemigroup = *-isSemigroup
    ; comm        = *-comm
    }

  *-isCommutativeMonoid : IsCommutativeMonoid _*_ 1#
  *-isCommutativeMonoid = record
    { isMonoid = *-isMonoid
    ; comm     = *-comm
    }

------------------------------------------------------------------------
-- Algebra with 2 binary operations, 1 unary operation & 2 elements
------------------------------------------------------------------------

record IsRing (_+_ _*_ : Op₂ A) (-_ : Op₁ A) (0# 1# : A) : Type a where
  constructor isring
  field
    +-isAbelianGroup : IsAbelianGroup _+_ 0# -_
    *-isMonoid       : IsMonoid _*_ 1#
    distrib          : _*_ DistributesOver _+_

  open IsAbelianGroup +-isAbelianGroup public
    renaming
    ( assoc                  to +-assoc
    ; identity               to +-identity
    ; identityˡ              to +-identityˡ
    ; identityʳ              to +-identityʳ
    ; ε-uniqueˡ              to 0#-uniqueˡ
    ; ε-uniqueʳ              to 0#-uniqueʳ
    ; inverse                to +-inverse
    ; inverseˡ               to +-inverseˡ
    ; inverseʳ               to +-inverseʳ
    ; inv-uniqueˡ            to neg-uniqueˡ
    ; inv-uniqueʳ            to neg-uniqueʳ
    ; cancelˡ                to +-cancelˡ
    ; cancelʳ                to +-cancelʳ
    ; comm                   to +-comm
    ; isMagma                to +-isMagma
    ; isSemigroup            to +-isSemigroup
    ; isMonoid               to +-isMonoid
    ; isCommutativeMonoid    to +-isCommutativeMonoid
    ; isCommutativeSemigroup to +-isCommutativeSemigroup
    ; isGroup                to +-isGroup
    ; _*_                    to _**_
    )

  open IsMonoid *-isMonoid public
    using ()
    renaming
    ( assoc       to *-assoc
    ; identity    to *-identity
    ; identityˡ   to *-identityˡ
    ; identityʳ   to *-identityʳ
    ; ε-uniqueˡ   to 1#-uniqueˡ
    ; ε-uniqueʳ   to 1#-uniqueʳ
    ; isMagma     to *-isMagma
    ; isSemigroup to *-isSemigroup
    )

  distribˡ : _*_ DistributesOverˡ _+_
  distribˡ = fst distrib

  distribʳ : _*_ DistributesOverʳ _+_
  distribʳ = snd distrib

  zeroˡ : LeftZero 0# _*_
  zeroˡ = assoc+distribʳ+idʳ+invʳ⇒zeˡ {_+_ = _+_} {_*_ = _*_} { -_} +-assoc distribʳ +-identityʳ +-inverseʳ

  zeroʳ : RightZero 0# _*_
  zeroʳ = assoc+distribˡ+idʳ+invʳ⇒zeʳ {_+_ = _+_} {_*_ = _*_} { -_} +-assoc distribˡ +-identityʳ +-inverseʳ

  zero : Zero 0# _*_
  zero = zeroˡ , zeroʳ

  isSemiringWithoutAnnihilatingZero
    : IsSemiringWithoutAnnihilatingZero _+_ _*_ 0# 1#
  isSemiringWithoutAnnihilatingZero = record
    { +-isCommutativeMonoid = +-isCommutativeMonoid
    ; *-isMonoid            = *-isMonoid
    ; distrib               = distrib
    }

  isSemiring : IsSemiring _+_ _*_ 0# 1#
  isSemiring = record
    { isSemiringWithoutAnnihilatingZero =
        isSemiringWithoutAnnihilatingZero
    ; zero = zero
    }

  open IsSemiring isSemiring public
    using (isNearSemiring; isSemiringWithoutOne)

record IsCommutativeRing (_+_ _*_ : Op₂ A) (-_ : Op₁ A) (0# 1# : A) : Type a where
  constructor iscommring
  field
    isRing : IsRing _+_ _*_ -_ 0# 1#
    *-comm : Commutative _*_

  open IsRing isRing public

  *-isCommutativeMonoid : IsCommutativeMonoid _*_ 1#
  *-isCommutativeMonoid =  record
    { isMonoid = *-isMonoid
    ; comm     = *-comm
    }

  isCommutativeSemiring : IsCommutativeSemiring _+_ _*_ 0# 1#
  isCommutativeSemiring = record
    { isSemiring = isSemiring
    ; *-comm = *-comm
    }

  open IsCommutativeSemiring isCommutativeSemiring public
    using ( isCommutativeSemiringWithoutOne )

record IsIntegralDomain {ℓ} (Cancellable : A → Type ℓ) (_+_ _*_ : Op₂ A) (-_ : Op₁ A) (0# 1# : A) : Type (ℓ-max a ℓ) where
  constructor isintegraldomain

  Cancellables : Type (ℓ-max a ℓ)
  Cancellables = Σ A Cancellable

  field
    isCommutativeRing : IsCommutativeRing _+_ _*_ -_ 0# 1#
    *-cancelˡ         : ∀ (x : Cancellables) {y z} → (x .fst) * y ≡ (x .fst) * z → y ≡ z

  open IsCommutativeRing isCommutativeRing public

  *-cancelʳ : ∀ {x y} (z : Cancellables) → x * (z .fst) ≡ y * (z .fst) → x ≡ y
  *-cancelʳ {x} {y} zᵖ@(z , _) eq = *-cancelˡ zᵖ (
    z * x ≡⟨ *-comm z x ⟩
    x * z ≡⟨ eq ⟩
    y * z ≡⟨ *-comm y z ⟩
    z * y ∎)

record IsBooleanAlgebra (_⋀_ _⋁_ : Op₂ A) (¬_ : Op₁ A) (⊤ ⊥ : A) : Type a where
  constructor isbooleanalgebra
  field
    isDistributiveLattice : IsDistributiveLattice _⋀_ _⋁_
    ⋀-identityʳ           : RightIdentity ⊤ _⋀_
    ⋁-identityʳ           : RightIdentity ⊥ _⋁_
    ⋀-complementʳ         : RightInverse ⊥ ¬_ _⋀_
    ⋁-complementʳ         : RightInverse ⊤ ¬_ _⋁_

  open IsDistributiveLattice isDistributiveLattice public

  ⋀-identityˡ : LeftIdentity ⊤ _⋀_
  ⋀-identityˡ x = ⋀-comm _ _ ∙ ⋀-identityʳ x

  ⋀-identity : Identity ⊤ _⋀_
  ⋀-identity = ⋀-identityˡ , ⋀-identityʳ

  ⋁-identityˡ : LeftIdentity ⊥ _⋁_
  ⋁-identityˡ x = ⋁-comm _ _ ∙ ⋁-identityʳ x

  ⋁-identity : Identity ⊥ _⋁_
  ⋁-identity = ⋁-identityˡ , ⋁-identityʳ

  ⋀-complementˡ : LeftInverse ⊥ ¬_ _⋀_
  ⋀-complementˡ = comm+invʳ⇒invˡ ⋀-comm ⋀-complementʳ

  ⋀-complement : Inverse ⊥ ¬_ _⋀_
  ⋀-complement = ⋀-complementˡ , ⋀-complementʳ

  ⋁-complementˡ : LeftInverse ⊤ ¬_ _⋁_
  ⋁-complementˡ = comm+invʳ⇒invˡ ⋁-comm ⋁-complementʳ

  ⋁-complement : Inverse ⊤ ¬_ _⋁_
  ⋁-complement = ⋁-complementˡ , ⋁-complementʳ

------------------------------------------------------------------------
-- Algebra with 2 binary operations, 2 unary operations & 2 elements
------------------------------------------------------------------------

-- The standard definition of division rings excludes the zero ring, but such a restriction is rarely important
record IsDivisionRing {ℓ} (Invertible : A → Type ℓ) (_+_ _*_ : Op₂ A) (-_ : Op₁ A) (_⁻¹ : Σ A Invertible → A) (0# 1# : A) : Type (ℓ-max a ℓ) where
  constructor isdivring

  Invertibles : Type (ℓ-max a ℓ)
  Invertibles = Σ A Invertible

  field
    isRing   : IsRing _+_ _*_ -_ 0# 1#
    *-inverseˡ : (x : Invertibles) → (x ⁻¹) * (x .fst) ≡ 1#
    *-inverseʳ : (x : Invertibles) → (x .fst) * (x ⁻¹) ≡ 1#

  open IsRing isRing public

  infixl 6 _/_
  _/_ : A → Invertibles → A
  x / y = x * (y ⁻¹)

  infixr 6 _/ˡ_
  _/ˡ_ : Invertibles → A → A
  x /ˡ y = (x ⁻¹) * y

  *-cancelˡ : ∀ (x : Invertibles) {y z} → (x .fst) * y ≡ (x .fst) * z → y ≡ z
  *-cancelˡ xᵖ@(x , _) {y} {z} eq =
    y                 ≡˘⟨ *-identityˡ y ⟩
    1# * y            ≡˘⟨ cong (_* y) (*-inverseˡ xᵖ) ⟩
    ((xᵖ ⁻¹) * x) * y ≡⟨ *-assoc (xᵖ ⁻¹) x y ⟩
    (xᵖ ⁻¹) * (x * y) ≡⟨ cong ((xᵖ ⁻¹) *_) eq ⟩
    (xᵖ ⁻¹) * (x * z) ≡˘⟨ *-assoc (xᵖ ⁻¹) x z ⟩
    ((xᵖ ⁻¹) * x) * z ≡⟨ cong (_* z) (*-inverseˡ xᵖ) ⟩
    1# * z            ≡⟨ *-identityˡ z ⟩
    z                 ∎

  *-cancelʳ : ∀ {x y} (z : Invertibles) → x * (z .fst) ≡ y * (z .fst) → x ≡ y
  *-cancelʳ {x} {y} zᵖ@(z , _) eq =
    x                 ≡˘⟨ *-identityʳ x ⟩
    x * 1#            ≡˘⟨ cong (x *_) (*-inverseʳ zᵖ) ⟩
    x * (z * (zᵖ ⁻¹)) ≡˘⟨ *-assoc x z (zᵖ ⁻¹) ⟩
    (x * z) * (zᵖ ⁻¹) ≡⟨ cong (_* (zᵖ ⁻¹)) eq ⟩
    (y * z) * (zᵖ ⁻¹) ≡⟨ *-assoc y z (zᵖ ⁻¹) ⟩
    y * (z * (zᵖ ⁻¹)) ≡⟨ cong (y *_) (*-inverseʳ zᵖ) ⟩
    y * 1#            ≡⟨ *-identityʳ y ⟩
    y                 ∎

record IsField {ℓ} (Invertible : A → Type ℓ) (_+_ _*_ : Op₂ A) (-_ : Op₁ A) (_⁻¹ : Σ A Invertible → A) (0# 1# : A) : Type (ℓ-max a ℓ) where
  constructor isfield
  field
    isDivisionRing : IsDivisionRing Invertible _+_ _*_ -_ _⁻¹ 0# 1#
    *-comm         : Commutative _*_

  open IsDivisionRing isDivisionRing public hiding (_/ˡ_)

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

  isIntegralDomain : IsIntegralDomain Invertible _+_ _*_ -_ 0# 1#
  isIntegralDomain = record
    { isCommutativeRing = isCommutativeRing
    ; *-cancelˡ         = *-cancelˡ
    }

  open IsIntegralDomain isIntegralDomain public
    using (*-isCommutativeMonoid; isCommutativeSemiring; isCommutativeSemiringWithoutOne)


{-# OPTIONS --universe-polymorphism #-}

module 13-implicitProofObligations where

module Imports where
  module L where
    open import Agda.Primitive public
      using (Level; _⊔_) renaming (lzero to zero; lsuc to suc)

  -- extract from Data.Unit
  record ⊤ : Set where
    constructor tt

  -- extract from Data.Empty
  data ⊥ : Set where

  ⊥-elim : ∀ {w} {Whatever : Set w} → ⊥ → Whatever
  ⊥-elim ()

  -- extract from Function
  id : ∀ {a} {A : Set a} → A → A
  id x = x

  _$_ : ∀ {a b} {A : Set a} {B : A → Set b} →
        ((x : A) → B x) → ((x : A) → B x)
  f $ x = f x

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

  -- extract from Data.Bool
  data Bool : Set where
    true  : Bool
    false : Bool

  not : Bool → Bool
  not true  = false
  not false = true

  T : Bool → Set
  T true  = ⊤
  T false = ⊥

  -- extract from Relation.Nullary.Decidable and friends
  infix 3 ¬_

  ¬_ : ∀ {ℓ} → Set ℓ → Set ℓ
  ¬ P = P → ⊥

  data Dec {p} (P : Set p) : Set p where
    yes : ( p :   P) → Dec P
    no  : (¬p : ¬ P) → Dec P
  ⌊_⌋ : ∀ {p} {P : Set p} → Dec P → Bool
  ⌊ yes _ ⌋ = true
  ⌊ no  _ ⌋ = false

  False : ∀ {p} {P : Set p} → Dec P → Set
  False Q = T (not ⌊ Q ⌋)

  -- extract from Relation.Binary.PropositionalEquality
  data _≡_ {a} {A : Set a} (x : A) : A → Set a where
    refl : x ≡ x

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

  _≢_ : ∀ {a} {A : Set a} → A → A → Set a
  x ≢ y = ¬ x ≡ y

  -- extract from Data.Nat
  data ℕ : Set where
    zero : ℕ
    suc  : (n : ℕ) → ℕ

  {-# BUILTIN NATURAL ℕ    #-}

  pred : ℕ → ℕ
  pred zero    = zero
  pred (suc n) = n

  infixl 6 _+_

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

  _*_ : ℕ → ℕ → ℕ
  zero  * n = zero
  suc m * n = n + m * n

  _≟_ : (x y : ℕ) → Dec (x ≡ y)
  zero  ≟ zero   = yes refl
  suc m ≟ suc n  with m ≟ n
  suc m ≟ suc .m | yes refl = yes refl
  suc m ≟ suc n  | no prf   = no (prf ∘ cong pred)
  zero  ≟ suc n  = no λ()
  suc m ≟ zero   = no λ()

  -- extract from Data.Fin
  data Fin : ℕ → Set where
    zero : {n : ℕ} → Fin (suc n)
    suc  : {n : ℕ} (i : Fin n) → Fin (suc n)

  -- A conversion: toℕ "n" = n.

  toℕ : ∀ {n} → Fin n → ℕ
  toℕ zero    = 0
  toℕ (suc i) = suc (toℕ i)

  -- extract from Data.Product
  record Σ {a b} (A : Set a) (B : A → Set b) : Set (a L.⊔ b) where
    constructor _,_
    field
      proj₁ : A
      proj₂ : B proj₁

  _×_ : ∀ {a b} (A : Set a) (B : Set b) → Set (a L.⊔ b)
  A × B = Σ A (λ (_ : A) → B)

  -- extract from Data.Nat.DivMod

  data DivMod : ℕ → ℕ → Set where
    result : {divisor : ℕ} (q : ℕ) (r : Fin divisor) →
             DivMod (toℕ r + q * divisor) divisor

  data DivMod' (dividend divisor : ℕ) : Set where
    result : (q : ℕ) (r : Fin divisor)
             (eq : dividend ≡ (toℕ r + q * divisor)) →
             DivMod' dividend divisor

  postulate
    _div_ : (dividend divisor : ℕ) {≢0 : False (divisor ≟ 0)} → ℕ
    _divMod_ : (dividend divisor : ℕ) {≢0 : False (divisor ≟ 0)} →
               DivMod dividend divisor
    _divMod'_ : (dividend divisor : ℕ) {≢0 : False (divisor ≟ 0)} →
                        DivMod' dividend divisor

open Imports

-- Begin of actual example!

postulate
  d : ℕ
  d≢0 : d ≢ 0
--  d≢0' : d ≢ 0

fromWitnessFalse : ∀ {p} {P : Set p} {Q : Dec P} → ¬ P → False Q
fromWitnessFalse {Q = yes p} ¬P = ⊥-elim $ ¬P p
fromWitnessFalse {Q = no ¬p} ¬P = tt

⋯ : {A : Set} → {{v : A}} → A
⋯ {{v}} = v

_divMod′_ : (dividend divisor : ℕ) {{ ≢0 : divisor ≢ 0 }} → ℕ × ℕ
_divMod′_ a d {{_}} with _divMod_ a d { fromWitnessFalse ⋯ }
._ divMod′ d | (result q r) = q , toℕ r

_div′_ : (dividend divisor : ℕ) {{ ≢0 : divisor ≢ 0 }} → ℕ
_div′_ a b {{_}} with a divMod′ b
a div′ b | (q , _) = q

--Agda can't resolve hiddens
-- test : {d≢0 : False (d ≟ 0)} → ℕ
-- test = 5 div d
-- test2 : {{d≢0 : d ≢ 0}} → ℕ
-- test2 = 5 div′ d

test3 = 5 div 2
test4 = 5 div′ 2
  where nz : 2 ≢ 0
        nz ()


------------------------------------------------------------------------
-- The Agda standard library
--
-- Types and functions which are used to keep track of height
-- invariants in AVL Trees
------------------------------------------------------------------------

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

module Data.AVL.Height where

open import Data.Nat.Base
open import Data.Fin using (Fin; zero; suc)

ℕ₂ = Fin 2
pattern 0# = zero
pattern 1# = suc zero
pattern ## = suc (suc ())

-- Addition.

infixl 6 _⊕_

_⊕_ : ℕ₂ → ℕ → ℕ
0# ⊕ n = n
1# ⊕ n = 1 + n
## ⊕ n

-- pred[ i ⊕ n ] = pred (i ⊕ n).

pred[_⊕_] : ℕ₂ → ℕ → ℕ
pred[ i ⊕ zero  ] = 0
pred[ i ⊕ suc n ] = i ⊕ n

infix 4 _∼_⊔_

-- If i ∼ j ⊔ m, then the difference between i and j is at most 1,
-- and the maximum of i and j is m. _∼_⊔_ is used to record the
-- balance factor of the AVL trees, and also to ensure that the
-- absolute value of the balance factor is never more than 1.

data _∼_⊔_ : ℕ → ℕ → ℕ → Set where
  ∼+ : ∀ {n} →     n ∼ 1 + n ⊔ 1 + n
  ∼0 : ∀ {n} →     n ∼ n     ⊔ n
  ∼- : ∀ {n} → 1 + n ∼ n     ⊔ 1 + n

-- Some lemmas.

max∼ : ∀ {i j m} → i ∼ j ⊔ m → m ∼ i ⊔ m
max∼ ∼+ = ∼-
max∼ ∼0 = ∼0
max∼ ∼- = ∼0

∼max : ∀ {i j m} → i ∼ j ⊔ m → j ∼ m ⊔ m
∼max ∼+ = ∼0
∼max ∼0 = ∼0
∼max ∼- = ∼+


module Data.List.Equiv where

open import Logic.Propositional
import      Lvl
open import Data.List
open import Structure.Operator
open import Structure.Setoid
open import Type

private variable ℓ ℓₑ ℓₚ : Lvl.Level
private variable T : Type{ℓ}

-- A correct equality relation on lists should state that prepend is a function and have the generalized cancellation properties for lists.
record Extensionality ⦃ equiv : Equiv{ℓₑ}(T) ⦄ (equiv-List : Equiv{ℓₚ}(List(T))) : Type{ℓₑ Lvl.⊔ Lvl.of(T) Lvl.⊔ ℓₚ} where
  constructor intro
  private instance _ = equiv-List
  field
    ⦃ binaryOperator ⦄ : BinaryOperator(List._⊰_)
    generalized-cancellationᵣ : ∀{x y : T}{l₁ l₂ : List(T)} → (x ⊰ l₁ ≡ y ⊰ l₂) → (x ≡ y)
    generalized-cancellationₗ : ∀{x y : T}{l₁ l₂ : List(T)} → (x ⊰ l₁ ≡ y ⊰ l₂) → (l₁ ≡ l₂)
    case-unequality : ∀{x : T}{l : List(T)} → (∅ ≢ x ⊰ l)

  generalized-cancellation : ∀{x y : T}{l₁ l₂ : List(T)} → (x ⊰ l₁ ≡ y ⊰ l₂) → ((x ≡ y) ∧ (l₁ ≡ l₂))
  generalized-cancellation p = [∧]-intro (generalized-cancellationᵣ p) (generalized-cancellationₗ p)
open Extensionality ⦃ … ⦄ renaming
  ( binaryOperator to [⊰]-binaryOperator
  ; generalized-cancellationₗ to [⊰]-generalized-cancellationₗ
  ; generalized-cancellationᵣ to [⊰]-generalized-cancellationᵣ
  ; generalized-cancellation to [⊰]-generalized-cancellation
  ; case-unequality to [∅][⊰]-unequal
  ) public


{-# OPTIONS --safe #-}

module Cubical.Algebra.CommRing.Instances.Polynomials.UnivariatePolyList where

open import Cubical.Foundations.Prelude
open import Cubical.Data.Nat using (ℕ ; zero ; suc)

open import Cubical.Algebra.CommRing
open import Cubical.Algebra.Polynomials.UnivariateList.Base
open import Cubical.Algebra.Polynomials.UnivariateList.Properties

private
  variable
    ℓ : Level

open CommRingStr

module _ (R : CommRing ℓ) where

  open PolyMod R
  open PolyModTheory R

  UnivariatePolyList : CommRing ℓ
  fst UnivariatePolyList = Poly R
  0r (snd UnivariatePolyList) = 0P
  1r (snd UnivariatePolyList) = 1P
  _+_ (snd UnivariatePolyList) = _Poly+_
  _·_ (snd UnivariatePolyList) = _Poly*_
  - snd UnivariatePolyList = Poly-
  isCommRing (snd UnivariatePolyList) = makeIsCommRing isSetPoly
                                    Poly+Assoc Poly+Rid Poly+Inverses Poly+Comm
                                    Poly*Associative Poly*Rid Poly*LDistrPoly+ Poly*Commutative

nUnivariatePolyList : (A' : CommRing ℓ) → (n : ℕ) → CommRing ℓ
nUnivariatePolyList A' zero = A'
nUnivariatePolyList A' (suc n) = UnivariatePolyList (nUnivariatePolyList A' n)


{-# OPTIONS --without-K --safe #-}
module Cats.Util.SetoidReasoning where

open import Relation.Binary.Reasoning.MultiSetoid public

open import Relation.Binary using (Setoid)

module _ {c ℓ} (S : Setoid c ℓ) where

  open Setoid S

  triangle :
    ∀ m {x y}
    → x ≈ m
    → y ≈ m
    → x ≈ y
  triangle m x≈m y≈m = trans x≈m (sym y≈m)


module NamedWhere where

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

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

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

reverse : {A : Set} -> List A -> List A
reverse {A} xs = rev xs []
  module reverse where
    rev : List A -> List A -> List A
    rev []        ys = ys
    rev (x :: xs) ys = rev xs (x :: ys)

rev : {A : Set} -> List A -> List A -> List A
rev = reverse.rev []


------------------------------------------------------------------------
-- The Agda standard library
--
-- Heterogeneous N-ary Relations
------------------------------------------------------------------------

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

module Relation.Nary where

------------------------------------------------------------------------
-- Concrete examples can be found in README.Nary. This file's comments
-- are more focused on the implementation details and the motivations
-- behind the design decisions.
------------------------------------------------------------------------

open import Level using (Level; _⊔_; Lift)
open import Data.Unit.Base
open import Data.Bool.Base using (true; false)
open import Data.Empty
open import Data.Nat.Base using (zero; suc)
open import Data.Product as Prod using (_×_; _,_)
open import Data.Product.Nary.NonDependent
open import Data.Sum.Base using (_⊎_)
open import Function using (_$_; _∘′_)
open import Function.Nary.NonDependent
open import Relation.Nullary using (¬_; Dec; yes; no; _because_)
import Relation.Nullary.Decidable as Dec
open import Relation.Nullary.Product using (_×-dec_)
import Relation.Unary as Unary
open import Relation.Binary.PropositionalEquality using (_≡_; cong; subst)

private
  variable
    r : Level
    R : Set r

------------------------------------------------------------------------
-- Generic type constructors

-- `Relation.Unary` provides users with a wealth of combinators to work
-- with indexed sets. We can generalise these to n-ary relations.

-- The crucial thing to notice here is that because we are explicitly
-- considering that the input function should be a `Set`-ended `Arrows`,
-- all the other parameters are inferrable. This allows us to make the
-- number arguments (`n`) implicit.
------------------------------------------------------------------------

------------------------------------------------------------------------
-- Quantifiers

-- If we already know how to quantify over one variable, we can easily
-- describe how to quantify over `n` variables by induction over said `n`.

quantₙ : (∀ {i l} {I : Set i} → (I → Set l) → Set (i ⊔ l)) →
         ∀ n {ls} {as : Sets n ls} →
         Arrows n as (Set r) → Set (r ⊔ (⨆ n ls))
quantₙ Q zero     f = f
quantₙ Q (suc n)  f = Q (λ x → quantₙ Q n (f x))

infix 5 ∃⟨_⟩ Π[_] ∀[_]

-- existential quantifier

∃⟨_⟩ : ∀ {n ls r} {as : Sets n ls} → as ⇉ Set r → Set (r ⊔ (⨆ n ls))
∃⟨_⟩ = quantₙ Unary.Satisfiable _

-- explicit universal quantifiers

Π[_] : ∀ {n ls r} {as : Sets n ls} → as ⇉ Set r → Set (r ⊔ (⨆ n ls))
Π[_] = quantₙ Unary.Universal _

-- implicit universal quantifiers

∀[_] : ∀ {n ls r} {as : Sets n ls} → as ⇉ Set r → Set (r ⊔ (⨆ n ls))
∀[_] = quantₙ Unary.IUniversal _

-- ≟-mapₙ : ∀ n. (con : A₁ → ⋯ → Aₙ → R) →
--               Injectiveₙ n con →
--               ∀ a₁₁ a₁₂ ⋯ aₙ₁ aₙ₂ →
--               Dec (a₁₁ ≡ a₁₂) → ⋯ → Dec (aₙ₁ ≡ aₙ₂) →
--               Dec (con a₁₁ ⋯ aₙ₁ ≡ con a₁₂ ⋯ aₙ₂)

≟-mapₙ : ∀ n {ls} {as : Sets n ls} (con : Arrows n as R) → Injectiveₙ n con →
         ∀ {l r} → Arrows n (Dec <$> Equalₙ n l r) (Dec (uncurryₙ n con l ≡ uncurryₙ n con r))
≟-mapₙ n con con-inj =
  curryₙ n λ a?s → let as? = Product-dec n a?s in
  Dec.map′ (cong (uncurryₙ n con) ∘′ fromEqualₙ n) con-inj as?

------------------------------------------------------------------------
-- Substitution

module _ {n r ls} {as : Sets n ls} (P : as ⇉ Set r) where

-- Substitutionₙ : ∀ n. ∀ a₁₁ a₁₂ ⋯ aₙ₁ aₙ₂ →
--                      a₁₁ ≡ a₁₂ → ⋯ → aₙ₁ ≡ aₙ₂ →
--                      P a₁₁ ⋯ aₙ₁ → P a₁₂ ⋯ aₙ₂

  Substitutionₙ : Set (r ⊔ (⨆ n ls))
  Substitutionₙ = ∀ {l r} → Equalₙ n l r ⇉ (uncurryₙ n P l → uncurryₙ n P r)

  substₙ : Substitutionₙ
  substₙ = curryₙ n (subst (uncurryₙ n P) ∘′ fromEqualₙ n)

------------------------------------------------------------------------
-- Pointwise liftings of k-ary operators

-- Rather than having multiple ad-hoc lifting functions for various arities
-- we have a fully generic liftₙ functional which lifts a k-ary operator
-- to work with k n-ary functions whose respective codomains match the domains
-- of the operator.
-- The type of liftₙ is fairly unreadable. Here it is written with ellipsis:

-- liftₙ : ∀ k n. (B₁ → ⋯ → Bₖ → R) →
--                (A₁ → ⋯ → Aₙ → B₁) →
--                       ⋮
--                (A₁ → ⋯ → Aₙ → B₁) →
--                (A₁ → ⋯ → Aₙ → R)

liftₙ : ∀ k n {ls rs} {as : Sets n ls} {bs : Sets k rs} →
        Arrows k bs R → Arrows k (smap _ (Arrows n as) k bs) (Arrows n as R)
liftₙ k n op = curry⊤ₙ k λ fs →
               curry⊤ₙ n λ vs →
               uncurry⊤ₙ k op $
               palg _ _ k (λ f → uncurry⊤ₙ n f vs) fs where

  -- The bulk of the work happens in this auxiliary definition:
  palg : ∀ f (F : ∀ {l} → Set l → Set (f l)) n {ls} {as : Sets n ls} →
         (∀ {l} {r : Set l} → F r → r) → Product⊤ n (smap f F n as) → Product⊤ n as
  palg f F zero    alg ps       = _
  palg f F (suc n) alg (p , ps) = alg p , palg f F n alg ps

-- implication

infixr 6 _⇒_
_⇒_ : ∀ {n} {ls r s} {as : Sets n ls} →
      as ⇉ Set r → as ⇉ Set s → as ⇉ Set (r ⊔ s)
_⇒_ = liftₙ 2 _ (λ A B → A → B)

-- conjunction

infixr 7 _∩_
_∩_ : ∀ {n} {ls r s} {as : Sets n ls} →
      as ⇉ Set r → as ⇉ Set s → as ⇉ Set (r ⊔ s)
_∩_ = liftₙ 2 _ _×_

-- disjunction

infixr 8 _∪_
_∪_ : ∀ {n} {ls r s} {as : Sets n ls} →
      as ⇉ Set r → as ⇉ Set s → as ⇉ Set (r ⊔ s)
_∪_ = liftₙ 2 _ _⊎_

-- negation

∁ : ∀ {n ls r} {as : Sets n ls} → as ⇉ Set r → as ⇉ Set r
∁ = liftₙ 1 _ ¬_

apply⊤ₙ : ∀ {n ls r} {as : Sets n ls} {R : as ⇉ Set r} →
          Π[ R ] → (vs : Product⊤ n as) → uncurry⊤ₙ n R vs
apply⊤ₙ {zero}  prf vs       = prf
apply⊤ₙ {suc n} prf (v , vs) = apply⊤ₙ (prf v) vs

applyₙ : ∀ {n ls r} {as : Sets n ls} {R : as ⇉ Set r} →
         Π[ R ] → (vs : Product n as) → uncurry⊤ₙ n R (toProduct⊤ n vs)
applyₙ {n} prf vs = apply⊤ₙ prf (toProduct⊤ n vs)

iapply⊤ₙ : ∀ {n ls r} {as : Sets n ls} {R : as ⇉ Set r} →
           ∀[ R ] → {vs : Product⊤ n as} → uncurry⊤ₙ n R vs
iapply⊤ₙ {zero}  prf = prf
iapply⊤ₙ {suc n} prf = iapply⊤ₙ {n} prf

iapplyₙ : ∀ {n ls r} {as : Sets n ls} {R : as ⇉ Set r} →
          ∀[ R ] → {vs : Product n as} → uncurry⊤ₙ n R (toProduct⊤ n vs)
iapplyₙ {n} prf = iapply⊤ₙ {n} prf

------------------------------------------------------------------------
-- Properties of N-ary relations

-- Decidability

Decidable : ∀ {n ls r} {as : Sets n ls} → as ⇉ Set r → Set (r ⊔ ⨆ n ls)
Decidable R = Π[ mapₙ _ Dec R ]

-- erasure

⌊_⌋ : ∀ {n ls r} {as : Sets n ls} {R : as ⇉ Set r} → Decidable R → as ⇉ Set r
⌊_⌋ {zero}  R? = Lift _ (Dec.True R?)
⌊_⌋ {suc n} R? a = ⌊ R? a ⌋

-- equivalence between R and its erasure

fromWitness : ∀ {n ls r} {as : Sets n ls} (R : as ⇉ Set r) (R? : Decidable R) →
              ∀[ ⌊ R? ⌋ ⇒ R ]
fromWitness {zero} R R? with R?
... | yes           r = λ _ → r
... | false because _ = λ ()
fromWitness {suc n} R R? = fromWitness (R _) (R? _)

toWitness : ∀ {n ls r} {as : Sets n ls} (R : as ⇉ Set r) (R? : Decidable R) →
              ∀[ R ⇒ ⌊ R? ⌋ ]
toWitness {zero} R R? with R?
... | true because _ = _
... | no          ¬r = ⊥-elim ∘′ ¬r
toWitness {suc n} R R? = toWitness (R _) (R? _)


------------------------------------------------------------------------------
-- Example of a partial function using the Bove-Capretta method
------------------------------------------------------------------------------

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

-- From: Ana Bove and Venanzio Capretta. Nested general recursion and
-- partiality in type theory. vol 2152 LNCS. 2001.

module FOT.FOTC.Program.Min.MinBC-SL where

open import Data.Nat renaming ( suc to succ )
open import Relation.Binary.PropositionalEquality

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

data minAcc : (ℕ → ℕ) → Set where
  minacc0 : (f : ℕ → ℕ) → f 0 ≡ 0 → minAcc f
  minacc1 : (f : ℕ → ℕ) → f 0 ≢ 0 → minAcc (λ n → f (succ n)) → minAcc f

min : (f : ℕ → ℕ) → minAcc f → ℕ
min f (minacc0 .f q)   = 0
min f (minacc1 .f q h) = succ (min (λ n → f (succ n)) h)


{-# OPTIONS --cubical --safe --guardedness --postfix-projections #-}

module Codata.Stream where

open import Prelude
open import Data.List using (List; _∷_; [])
open import Data.List.Kleene
import Data.List.Kleene.Membership as Kleene
open import Data.Fin

infixr 5 _◃_
record Stream (A : Type a) : Type a where
  constructor _◃_
  coinductive
  field
    head  : A
    tail  : Stream A
open Stream public

Stream′ : Type a → Type a
Stream′ A = ℕ → A

_!_ : Stream A → Stream′ A
xs ! zero  = xs .head
xs ! suc n = xs .tail ! n

tabulate : Stream′ A → Stream A
tabulate xs .head = xs zero
tabulate xs .tail = tabulate (xs ∘ suc)

linv : (xs : Stream A) → tabulate (xs !_) ≡ xs
linv xs i .head = xs .head
linv xs i .tail = linv (xs .tail) i

rinv : (xs : Stream′ A) → (tabulate xs !_) ≡ xs
rinv xs i zero    = xs zero
rinv xs i (suc n) = rinv (xs ∘ suc) i n

stream : Stream A ⇔ Stream′ A
stream .fun = _!_
stream .inv = tabulate
stream .rightInv = rinv
stream .leftInv = linv

-- infixr 5 _∈_
-- _∈_ : A → Stream A → Type _
-- x ∈ xs = fiber xs x

-- toList : ℕ → Stream A → List A
-- toList zero xs = []
-- toList (suc n) xs = xs zero ∷ toList n (xs ∘ suc)

-- mutual
--   concat⋆ : A ⋆ → Stream (A ⁺) → Stream A
--   concat⋆ []    xs = concat⁺ (xs zero) (xs ∘ suc)
--   concat⋆ (∹ x) xs = concat⁺ x xs

--   concat⁺ : A ⁺ → Stream (A ⁺) → Stream A
--   concat⁺ x xs zero    = x .head
--   concat⁺ x xs (suc n) = concat⋆ (x .tail) xs n

-- concat : Stream (A ⁺) → Stream A
-- concat xs = concat⁺ (xs zero) (xs ∘ suc)

-- infixr 5 _∈²_
-- _∈²_ : A → Stream (A ⁺) → Type _
-- x ∈² xs = ∃ n x Kleene.∈⁺ xs n

-- mutual
--   ◇++⋆ : ∀ (x : A) y ys → x Kleene.∈⋆ y → x ∈ concat⋆ y ys
--   ◇++⋆ x (∹ y) ys x∈y = ◇++⁺ x y ys x∈y

--   ◇++⁺ : ∀ (x : A) y ys → x Kleene.∈⁺ y → x ∈ concat⁺ y ys
--   ◇++⁺ x y ys (f0 , x∈y) = zero , x∈y
--   ◇++⁺ x y ys (fs n , x∈y) = let m , p = ◇++⋆ x (y .tail) ys (n , x∈y) in suc m , p

-- mutual
--   ++◇⋆ : ∀ (x : A) y ys → x ∈ concat ys → x ∈ concat⋆ y ys
--   ++◇⋆ x [] ys x∈ys = x∈ys
--   ++◇⋆ x (∹ y) ys x∈ys = ++◇⁺ x y ys x∈ys

--   ++◇⁺ : ∀ (x : A) y ys → x ∈ concat ys → x ∈ concat⁺ y ys
--   ++◇⁺ x y ys x∈ys = let n , p = ++◇⋆ x (y .tail) ys x∈ys in suc n , p

-- concat-∈ : ∀ (x : A) xs → x ∈² xs → x ∈ concat xs
-- concat-∈ x xs (zero  , x∈xs) = ◇++⁺ x (xs zero) (xs ∘ suc) x∈xs
-- concat-∈ x xs (suc n , x∈xs) = ++◇⁺ x (xs zero) (xs ∘ suc) (concat-∈ x (xs ∘ suc) (n , x∈xs))


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

open import LogicalFormulae
open import Numbers.Naturals.Definition
open import Numbers.Naturals.Order
open import Numbers.Naturals.Naturals -- for length
open import Lists.Lists
open import Orders.Partial.Definition
open import Orders.Total.Definition
open import Functions.Definition
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Groups.FinitePermutations
open import Boolean.Definition

module Lists.SortList where
  isSorted : {a b : _} {A : Set a} (ord : TotalOrder A) (l : List A) → Set (a ⊔ b)
  isSorted ord [] = True'
  isSorted ord (x :: []) = True'
  isSorted ord (x :: (y :: l)) = (TotalOrder._≤_ ord x y) && (isSorted ord (y :: l))

  sortedTailIsSorted : {a b : _} {A : Set a} (ord : TotalOrder A) → (x : A) → (l : List A) → (isSorted ord (x :: l)) → isSorted ord l
  sortedTailIsSorted ord x [] pr = record {}
  sortedTailIsSorted ord x (y :: l) (fst ,, snd) = snd

  insert : {a b : _} {A : Set a} (ord : TotalOrder A) → (l : List A) → (isSorted ord l) → (x : A) → List A
  insert ord [] pr x = [ x ]
  insert ord (y :: l) pr x with TotalOrder.totality ord x y
  insert ord (y :: l) pr x | inl (inl x<y) = x :: (y :: l)
  insert ord (y :: l) pr x | inl (inr y<x) = y :: insert ord l (sortedTailIsSorted ord y l pr) x
  insert ord (y :: l) pr x | inr x=y = x :: (y :: l)

  insertionIncreasesLength : {a b : _} {A : Set a} (ord : TotalOrder A) → (l : List A) → (x : A) → (pr : isSorted ord l) → length (insert ord l pr x) ≡ succ (length l)
  insertionIncreasesLength ord [] x pr = refl
  insertionIncreasesLength ord (y :: l) x pr with TotalOrder.totality ord x y
  insertionIncreasesLength ord (y :: l) x pr | inl (inl x<y) = refl
  insertionIncreasesLength ord (y :: l) x pr | inl (inr y<x) = applyEquality succ (insertionIncreasesLength ord l x (sortedTailIsSorted ord y l pr))
  insertionIncreasesLength ord (y :: l) x pr | inr x=y = refl

  isEmpty : {a : _} {A : Set a} (l : List A) → Bool
  isEmpty [] = BoolTrue
  isEmpty (x :: l) = BoolFalse

  minList : {a b : _} {A : Set a} (ord : TotalOrder A) → (l : List A) → (isEmpty l ≡ BoolFalse) → A
  minList ord [] ()
  minList ord (x :: []) pr = x
  minList ord (x :: (y :: l)) refl with minList ord (y :: l) refl
  minList ord (x :: (y :: l)) refl | minSoFar with TotalOrder.totality ord x minSoFar
  minList ord (x :: (y :: l)) refl | minSoFar | inl (inl x<m) = x
  minList ord (x :: (y :: l)) refl | minSoFar | inl (inr m<x) = minSoFar
  minList ord (x :: (y :: l)) refl | minSoFar | inr x=m = x

  insertionSorts : {a b : _} {A : Set a} (ord : TotalOrder  A) → (l : List A) → (pr : isSorted ord l) → (x : A) → isSorted ord (insert ord l pr x)
  insertionSorts ord [] _ _ = record {}
  insertionSorts ord (y :: l) pr x with TotalOrder.totality ord x y
  insertionSorts ord (y :: l) pr x | inl (inl x<y) = inl x<y ,, pr
  insertionSorts ord (y :: l) pr x | inl (inr y<x) with insertionSorts ord l (sortedTailIsSorted ord y l pr) x
  ... | bl = {!!}
  insertionSorts ord (y :: l) pr x | inr x=y = inr x=y ,, pr

  orderNotLessThanAndEqual : {a b : _} {A : Set a} (ord : TotalOrder A) → {x y : A} → (x ≡ y) → TotalOrder._<_ ord x y → False
  orderNotLessThanAndEqual ord {x} {y} x=y x<y rewrite x=y = PartialOrder.irreflexive (TotalOrder.order ord) x<y

  orderToDecidableEquality : {a b : _} {A : Set a} (ord : TotalOrder A) → {x y : A} → (x ≡ y) || ((x ≡ y) → False)
  orderToDecidableEquality ord {x} {y} with TotalOrder.totality ord x y
  orderToDecidableEquality ord {x} {y} | inl (inl x<y) = inr λ x=y → orderNotLessThanAndEqual ord x=y x<y
  orderToDecidableEquality ord {x} {y} | inl (inr y<x) = inr λ x=y → orderNotLessThanAndEqual ord (equalityCommutative x=y) y<x
  orderToDecidableEquality ord {x} {y} | inr x=y = inl x=y

  --SortingAlgorithm : {a b : _} → Set (lsuc a ⊔ lsuc b)
  --SortingAlgorithm {a} {b} = {A : Set a} → (ord : TotalOrder {a} {b} A) → (input : List A) → Sg (List A) (λ l → isSorted ord l && isPermutation (orderToDecidableEquality ord) l input)

  insertionSort : {a b : _} {A : Set a} (ord : TotalOrder A) (l : List A) → List A
  insertionSortIsSorted : {a b : _} {A : Set a} (ord : TotalOrder A) (l : List A) → isSorted ord (insertionSort ord l)

  insertionSort ord [] = []
  insertionSort ord (x :: l) = insert ord (insertionSort ord l) (insertionSortIsSorted ord l) x
  insertionSortIsSorted ord [] = record {}
  insertionSortIsSorted ord (x :: l) = insertionSorts ord (insertionSort ord l) (insertionSortIsSorted ord l) x

  insertionSortLength : {a b : _} {A : Set a} (ord : TotalOrder A) (l : List A) → length l ≡ length (insertionSort ord l)
  insertionSortLength ord [] = refl
  insertionSortLength ord (x :: l) rewrite insertionIncreasesLength ord (insertionSort ord l) x (insertionSortIsSorted ord l) = {!!} --applyEquality succ (insertionSortLength ord l)

  isPermutation : {a b : _} {A : Set a} (ord : TotalOrder A) → (l m : List A) → Set a
  isPermutation ord l m = insertionSort ord l ≡ insertionSort ord m

  --insertionSort : {a b : _} → SortingAlgorithm {a} {b}
  --insertionSort {A = A} ord l = {!!}
  --  where
  --    lemma : Sg (List A) (isSorted ord) → (x : A) → Sg (List A) (isSorted ord)
  --    lemma (l , b) x = insert ord l b x , insertionSorts ord l b x

  --InsertionSort : {a b : _} → SortingAlgorithm {a} {b}
  --InsertionSort ord l = insertionSort ord l

  lexicographicOrderRel : {a b : _} {A : Set a} → (TotalOrder A) → Rel {a} {a ⊔ b} (List A)
  lexicographicOrderRel _ [] [] = False'
  lexicographicOrderRel _ [] (x :: l2) = True'
  lexicographicOrderRel _ (x :: l1) [] = False'
  lexicographicOrderRel order (x :: l1) (y :: l2) = (TotalOrder._<_ order x y) || ((x ≡ y) && lexicographicOrderRel order l1 l2)

  lexIrrefl : {a b : _} {A : Set a} (o : TotalOrder A) → {x : List A} → lexicographicOrderRel o x x → False
  lexIrrefl o {[]} ()
  lexIrrefl o {x :: l} (inl x<x) = PartialOrder.irreflexive (TotalOrder.order o) x<x
  lexIrrefl o {x :: l} (inr (pr ,, l=l)) = lexIrrefl o {l} l=l

  lexTransitive : {a b : _} {A : Set a} (o : TotalOrder A) → {x y z : List A} → lexicographicOrderRel o x y → lexicographicOrderRel o y z → lexicographicOrderRel o x z
  lexTransitive o {[]} {[]} {z} x<y y<z = y<z
  lexTransitive o {[]} {x :: y} {[]} record {} y<z = y<z
  lexTransitive o {[]} {x :: y} {x₁ :: z} record {} y<z = record {}
  lexTransitive o {x :: xs} {[]} {z} () y<z
  lexTransitive o {x :: xs} {y :: ys} {[]} (inl x<y) ()
  lexTransitive o {x :: xs} {y :: ys} {z :: zs} (inl x<y) (inl y<z) = inl (PartialOrder.<Transitive (TotalOrder.order o) x<y y<z)
  lexTransitive o {x :: xs} {y :: ys} {.y :: zs} (inl x<y) (inr (refl ,, ys<zs)) = inl x<y
  lexTransitive o {x :: xs} {.x :: ys} {[]} (inr (refl ,, xs<ys)) ()
  lexTransitive o {x :: xs} {.x :: ys} {z :: zs} (inr (refl ,, xs<ys)) (inl y<z) = inl y<z
  lexTransitive o {x :: xs} {.x :: ys} {.x :: zs} (inr (refl ,, xs<ys)) (inr (refl ,, u)) = inr (refl ,, lexTransitive o xs<ys u)

  lexTotal : {a b : _} {A : Set a} (o : TotalOrder A) → (x y : List A) → ((lexicographicOrderRel o x y) || (lexicographicOrderRel o y x)) || (x ≡ y)
  lexTotal o [] [] = inr refl
  lexTotal o [] (x :: y) = inl (inl (record {}))
  lexTotal o (x :: xs) [] = inl (inr (record {}))
  lexTotal o (x :: xs) (y :: ys) with TotalOrder.totality o x y
  lexTotal o (x :: xs) (y :: ys) | inl (inl x<y) = inl (inl (inl x<y))
  lexTotal o (x :: xs) (y :: ys) | inl (inr y<x) = inl (inr (inl y<x))
  lexTotal o (x :: xs) (y :: ys) | inr x=y with lexTotal o xs ys
  lexTotal o (x :: xs) (y :: ys) | inr x=y | inl (inl xs<ys) = inl (inl (inr (x=y ,, xs<ys)))
  lexTotal o (x :: xs) (y :: ys) | inr x=y | inl (inr ys<xs) = inl (inr (inr (equalityCommutative x=y ,, ys<xs)))
  lexTotal o (x :: xs) (y :: ys) | inr x=y | inr xs=ys rewrite x=y | xs=ys = inr refl

  lexicographicOrder : {a b : _} {A : Set a} → (TotalOrder A) → TotalOrder (List A)
  PartialOrder._<_ (TotalOrder.order (lexicographicOrder order)) = lexicographicOrderRel order
  PartialOrder.irreflexive (TotalOrder.order (lexicographicOrder order)) = lexIrrefl order
  PartialOrder.<Transitive (TotalOrder.order (lexicographicOrder order)) = lexTransitive order
  TotalOrder.totality (lexicographicOrder order) = lexTotal order

  badSort : {a b : _} {A : Set a} (order : TotalOrder A) → ℕ → List A → List A
  badSortLength : {a b : _} {A : Set a} (order : TotalOrder A) → (n : ℕ) → (l : List A) → length (badSort order n l) ≡ length l
  allTrueRespectsBadsort : {a b c : _} {A : Set a} (order : TotalOrder A) → (n : ℕ) → (l : List A) → (pred : A → Set c) → allTrue pred l → allTrue pred (badSort order n l)

  badSort order zero = insertionSort order
  badSort order (succ n) [] = []
  badSort {A = A} order (succ n) (x :: l) = head (badSort (lexicographicOrder order) n (permutations (x :: l))) bsNonempty
    where
      bs : List (List A)
      bs = badSort (lexicographicOrder order) n (permutations (x :: l))
      bsNonempty : 0 <N length bs
      bsNonempty with bs
      bsNonempty | [] = {!!}
      bsNonempty | bl :: _ = succIsPositive _

  allTrueRespectsBadsort order n [] pred record {} = {!!}
  allTrueRespectsBadsort order n (x :: l) pred (fst ,, snd) = {!!}
  badSortLength order zero [] = refl
  badSortLength order zero (x :: l) = transitivity (insertionIncreasesLength order (insertionSort order l) x (insertionSortIsSorted order l)) (applyEquality succ (equalityCommutative (insertionSortLength order l)))
  badSortLength order (succ n) [] = refl
  badSortLength order (succ n) (x :: l) = {!!}
    where
      allLen : allTrue (λ i → length i ≡ succ (length l)) (badSort (lexicographicOrder order) n (flatten (map (allInsertions x) (permutations l))))
      allLen = {!!}

-- TODO: show that insertion sort actually produces a permutation


-- Minimal implicational logic, PHOAS approach, final encoding

module Pf.ArrMp where

-- Types

infixr 0 _=>_
data Ty : Set where
  UNIT :             Ty
  _=>_ : Ty -> Ty -> Ty

-- Context and truth judgement

Cx : Set1
Cx = Ty -> Set

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

-- Terms

TmRepr : Set1
TmRepr = Cx -> Ty -> Set

module ArrMp where
  record Tm (tr : TmRepr) : Set1 where
    infixl 1 _$_
    field
      var  : forall {tc a}   -> isTrue a tc               -> tr tc a
      lam' : forall {tc a b} -> (isTrue a tc -> tr tc b)  -> tr tc (a => b)
      _$_  : forall {tc a b} -> tr tc (a => b) -> tr tc a -> tr tc b

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

    syntax lam'' (\a -> b) = lam a => b
  open Tm {{...}} public

  Thm : Ty -> Set1
  Thm a = forall {tr tc} {{_ : Tm tr}} -> tr tc a
open ArrMp public

-- Example theorems

aI : forall {a} -> Thm (a => a)
aI =
  lam x => x

aK : forall {a b} -> Thm (a => b => a)
aK =
  lam x =>
    lam _ => x

aS : forall {a b c} -> Thm ((a => b => c) => (a => b) => a => c)
aS =
  lam f =>
    lam g =>
      lam x => f $ x $ (g $ x)

tSKK : forall {a} -> Thm (a => a)
tSKK {a = a} =
  aS {b = a => a} $ aK $ aK


------------------------------------------------------------------------
-- The Agda standard library
--
-- Divisibility
------------------------------------------------------------------------

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

module Data.Nat.Divisibility where

open import Algebra
open import Data.Nat.Base
open import Data.Nat.DivMod
open import Data.Nat.Properties
open import Data.Product
open import Function.Base
open import Function.Equivalence using (_⇔_; equivalence)
open import Level using (0ℓ)
open import Relation.Nullary using (yes; no)
open import Relation.Nullary.Decidable as Dec using (False)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary
import Relation.Binary.Reasoning.Preorder as PreorderReasoning
open import Relation.Binary.PropositionalEquality as PropEq
  using (_≡_; _≢_; refl; sym; trans; cong; cong₂; subst)

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

open import Data.Nat.Divisibility.Core public

------------------------------------------------------------------------
-- Relationship with _%_

m%n≡0⇒n∣m : ∀ m n → m % suc n ≡ 0 → suc n ∣ m
m%n≡0⇒n∣m m n eq = divides (m / suc n) (begin-equality
  m                               ≡⟨ m≡m%n+[m/n]*n m n ⟩
  m % suc n + m / suc n * (suc n) ≡⟨ cong₂ _+_ eq refl ⟩
  m / suc n * (suc n)             ∎)
  where open ≤-Reasoning

n∣m⇒m%n≡0 : ∀ m n → suc n ∣ m → m % suc n ≡ 0
n∣m⇒m%n≡0 m n (divides v eq) = begin-equality
  m           % suc n  ≡⟨ cong (_% suc n) eq ⟩
  (v * suc n) % suc n  ≡⟨ m*n%n≡0 v n ⟩
  0                    ∎
  where open ≤-Reasoning

m%n≡0⇔n∣m : ∀ m n → m % suc n ≡ 0 ⇔ suc n ∣ m
m%n≡0⇔n∣m m n = equivalence (m%n≡0⇒n∣m m n) (n∣m⇒m%n≡0 m n)

------------------------------------------------------------------------
-- Properties of _∣_ and _≤_

∣⇒≤ : ∀ {m n} → m ∣ suc n → m ≤ suc n
∣⇒≤ {m} {n} (divides (suc q) eq) = begin
  m          ≤⟨ m≤m+n m (q * m) ⟩
  suc q * m  ≡⟨ sym eq ⟩
  suc n      ∎
  where open ≤-Reasoning

>⇒∤ : ∀ {m n} → m > suc n → m ∤ suc n
>⇒∤ (s≤s m>n) m∣n = contradiction (∣⇒≤ m∣n) (≤⇒≯ m>n)

------------------------------------------------------------------------
-- _∣_ is a partial order

∣-reflexive : _≡_ ⇒ _∣_
∣-reflexive {n} refl = divides 1 (sym (*-identityˡ n))

∣-refl : Reflexive _∣_
∣-refl = ∣-reflexive refl

∣-trans : Transitive _∣_
∣-trans (divides p refl) (divides q refl) =
  divides (q * p) (sym (*-assoc q p _))

∣-antisym : Antisymmetric _≡_ _∣_
∣-antisym {m}     {zero}  _ (divides q refl) = *-zeroʳ q
∣-antisym {zero}  {n}     (divides p eq) _   = sym (trans eq (*-comm p 0))
∣-antisym {suc m} {suc n} p∣q           q∣p  = ≤-antisym (∣⇒≤ p∣q) (∣⇒≤ q∣p)

infix 4 _∣?_

_∣?_ : Decidable _∣_
zero  ∣? zero   = yes (divides 0 refl)
zero  ∣? suc m  = no ((λ()) ∘′ ∣-antisym (divides 0 refl))
suc n ∣? m      = Dec.map (m%n≡0⇔n∣m m n) (m % suc n ≟ 0)

∣-isPreorder : IsPreorder _≡_ _∣_
∣-isPreorder = record
  { isEquivalence = PropEq.isEquivalence
  ; reflexive     = ∣-reflexive
  ; trans         = ∣-trans
  }

∣-isPartialOrder : IsPartialOrder _≡_ _∣_
∣-isPartialOrder = record
  { isPreorder = ∣-isPreorder
  ; antisym    = ∣-antisym
  }

∣-preorder : Preorder 0ℓ 0ℓ 0ℓ
∣-preorder = record
  { isPreorder = ∣-isPreorder
  }

∣-poset : Poset 0ℓ 0ℓ 0ℓ
∣-poset = record
  { isPartialOrder = ∣-isPartialOrder
  }

------------------------------------------------------------------------
-- A reasoning module for the _∣_ relation

module ∣-Reasoning where
  private
    module Base = PreorderReasoning ∣-preorder

  open Base public
    hiding (step-≈; step-≈˘; step-∼)

  infixr 2 step-∣
  step-∣ = Base.step-∼
  syntax step-∣ x y∣z x∣y = x ∣⟨ x∣y ⟩ y∣z

------------------------------------------------------------------------
-- Simple properties of _∣_

infix 10 1∣_ _∣0

1∣_ : ∀ n → 1 ∣ n
1∣ n = divides n (sym (*-identityʳ n))

_∣0 : ∀ n → n ∣ 0
n ∣0 = divides 0 refl

0∣⇒≡0 : ∀ {n} → 0 ∣ n → n ≡ 0
0∣⇒≡0 {n} 0∣n = ∣-antisym (n ∣0) 0∣n

∣1⇒≡1 : ∀ {n} → n ∣ 1 → n ≡ 1
∣1⇒≡1 {n} n∣1 = ∣-antisym n∣1 (1∣ n)

n∣n : ∀ {n} → n ∣ n
n∣n {n} = ∣-refl

------------------------------------------------------------------------
-- Properties of _∣_ and _+_

∣m∣n⇒∣m+n : ∀ {i m n} → i ∣ m → i ∣ n → i ∣ m + n
∣m∣n⇒∣m+n (divides p refl) (divides q refl) =
  divides (p + q) (sym (*-distribʳ-+ _ p q))

∣m+n∣m⇒∣n : ∀ {i m n} → i ∣ m + n → i ∣ m → i ∣ n
∣m+n∣m⇒∣n {i} {m} {n} (divides p m+n≡p*i) (divides q m≡q*i) =
  divides (p ∸ q) $ begin-equality
    n             ≡⟨ sym (m+n∸n≡m n m) ⟩
    n + m ∸ m     ≡⟨ cong (_∸ m) (+-comm n m) ⟩
    m + n ∸ m     ≡⟨ cong₂ _∸_ m+n≡p*i m≡q*i ⟩
    p * i ∸ q * i ≡⟨ sym (*-distribʳ-∸ i p q) ⟩
    (p ∸ q) * i   ∎
  where open ∣-Reasoning

------------------------------------------------------------------------
-- Properties of _∣_ and _*_

n∣m*n : ∀ m {n} → n ∣ m * n
n∣m*n m = divides m refl

m∣m*n : ∀ {m} n → m ∣ m * n
m∣m*n n = divides n (*-comm _ n)

∣m⇒∣m*n : ∀ {i m} n → i ∣ m → i ∣ m * n
∣m⇒∣m*n {i} {m} n (divides q refl) = ∣-trans (n∣m*n q) (m∣m*n n)

∣n⇒∣m*n : ∀ {i} m {n} → i ∣ n → i ∣ m * n
∣n⇒∣m*n {i} m {n} i∣n = subst (i ∣_) (*-comm n m) (∣m⇒∣m*n m i∣n)

*-monoʳ-∣ : ∀ {i j} k → i ∣ j → k * i ∣ k * j
*-monoʳ-∣ {i} {j} k (divides q refl) = divides q $ begin-equality
  k * (q * i)  ≡⟨ sym (*-assoc k q i) ⟩
  (k * q) * i  ≡⟨ cong (_* i) (*-comm k q) ⟩
  (q * k) * i  ≡⟨ *-assoc q k i ⟩
  q * (k * i)  ∎
  where open ≤-Reasoning

*-monoˡ-∣ : ∀ {i j} k → i ∣ j → i * k ∣ j * k
*-monoˡ-∣ {i} {j} k rewrite *-comm i k | *-comm j k = *-monoʳ-∣ k

*-cancelˡ-∣ : ∀ {i j} k → suc k * i ∣ suc k * j → i ∣ j
*-cancelˡ-∣ {i} {j} k (divides q eq) =
  divides q $ *-cancelʳ-≡ j (q * i) $ begin-equality
    j * (suc k)      ≡⟨ *-comm j (suc k) ⟩
    suc k * j        ≡⟨ eq ⟩
    q * (suc k * i)  ≡⟨ cong (q *_) (*-comm (suc k) i) ⟩
    q * (i * suc k)  ≡⟨ sym (*-assoc q i (suc k)) ⟩
    (q * i) * suc k  ∎
    where open ≤-Reasoning

*-cancelʳ-∣ : ∀ {i j} k {k≢0 : False (k ≟ 0)} → i * k ∣ j * k → i ∣ j
*-cancelʳ-∣ {i} {j} k@(suc k-1) rewrite *-comm i k | *-comm j k = *-cancelˡ-∣ k-1

------------------------------------------------------------------------
-- Properties of _∣_ and _∸_

∣m∸n∣n⇒∣m : ∀ i {m n} → n ≤ m → i ∣ m ∸ n → i ∣ n → i ∣ m
∣m∸n∣n⇒∣m i {m} {n} n≤m (divides p m∸n≡p*i) (divides q n≡q*o) =
  divides (p + q) $ begin-equality
    m             ≡⟨ sym (m+[n∸m]≡n n≤m) ⟩
    n + (m ∸ n)   ≡⟨ +-comm n (m ∸ n) ⟩
    m ∸ n + n     ≡⟨ cong₂ _+_ m∸n≡p*i n≡q*o ⟩
    p * i + q * i ≡⟨ sym (*-distribʳ-+ i p q)  ⟩
    (p + q) * i   ∎
  where open ≤-Reasoning

------------------------------------------------------------------------
-- Properties of _∣_ and _/_

m/n∣m : ∀ {m n n≢0} → n ∣ m → (m / n) {n≢0} ∣ m
m/n∣m {m} {n} (divides p refl) = begin
  p * n / n ≡⟨ m*n/n≡m p n ⟩
  p         ∣⟨ m∣m*n n ⟩
  p * n     ∎
  where open ∣-Reasoning

m*n∣o⇒m∣o/n : ∀ m n {o n≢0} → m * n ∣ o → m ∣ (o / n) {n≢0}
m*n∣o⇒m∣o/n m n {_} {≢0} (divides p refl) = begin
  m               ∣⟨ n∣m*n p ⟩
  p * m           ≡⟨ sym (*-identityʳ (p * m)) ⟩
  p * m * 1       ≡⟨ sym (cong (p * m *_) (n/n≡1 n)) ⟩
  p * m * (n / n) ≡⟨ sym (*-/-assoc (p * m) {≢0 = ≢0} (n∣n {n})) ⟩
  p * m * n / n   ≡⟨ cong (λ v → (v / n) {≢0}) (*-assoc p m n) ⟩
  p * (m * n) / n ∎
  where open ∣-Reasoning

m*n∣o⇒n∣o/m : ∀ m n {o n≢0} → m * n ∣ o → n ∣ (o / m) {n≢0}
m*n∣o⇒n∣o/m m n {o} {≢0} rewrite *-comm m n = m*n∣o⇒m∣o/n n m {o} {≢0}

m∣n/o⇒m*o∣n : ∀ {m n o n≢0} → o ∣ n → m ∣ (n / o) {n≢0} → m * o ∣ n
m∣n/o⇒m*o∣n {m} {n} {o} (divides p refl) m∣p*o/o = begin
  m * o ∣⟨ *-monoˡ-∣ o (subst (m ∣_) (m*n/n≡m p o) m∣p*o/o) ⟩
  p * o ∎
  where open ∣-Reasoning

m∣n/o⇒o*m∣n : ∀ {m n o o≢0} → o ∣ n → m ∣ (n / o) {o≢0} → o * m ∣ n
m∣n/o⇒o*m∣n {m} {_} {o} {≢0} rewrite *-comm o m = m∣n/o⇒m*o∣n {n≢0 = ≢0}

m/n∣o⇒m∣o*n : ∀ {m n o n≢0} → n ∣ m → (m / n) {n≢0} ∣ o → m ∣ o * n
m/n∣o⇒m∣o*n {_} {n} {o} (divides p refl) p*n/n∣o = begin
  p * n ∣⟨ *-monoˡ-∣ n (subst (_∣ o) (m*n/n≡m p n) p*n/n∣o) ⟩
  o * n ∎
  where open ∣-Reasoning

m∣n*o⇒m/n∣o : ∀ {m n o n≢0} → n ∣ m → m ∣ o * n → (m / n) {n≢0} ∣ o
m∣n*o⇒m/n∣o {_} {n@(suc _)} {o} (divides p refl) pn∣on = begin
  p * n / n ≡⟨ m*n/n≡m p n ⟩
  p         ∣⟨ *-cancelʳ-∣ n pn∣on ⟩
  o         ∎
  where open ∣-Reasoning

------------------------------------------------------------------------
-- Properties of _∣_ and _%_

∣n∣m%n⇒∣m : ∀ {m n d ≢0} → d ∣ n → d ∣ (m % n) {≢0} → d ∣ m
∣n∣m%n⇒∣m {m} {n@(suc n-1)} {d} (divides a n≡ad) (divides b m%n≡bd) =
  divides (b + (m / n) * a) (begin-equality
    m                         ≡⟨ m≡m%n+[m/n]*n m n-1 ⟩
    m % n + (m / n) * n       ≡⟨ cong₂ _+_ m%n≡bd (cong (m / n *_) n≡ad) ⟩
    b * d + (m / n) * (a * d) ≡⟨ sym (cong (b * d +_) (*-assoc (m / n) a d)) ⟩
    b * d + ((m / n) * a) * d ≡⟨ sym (*-distribʳ-+ d b _) ⟩
    (b + (m / n) * a) * d     ∎)
    where open ≤-Reasoning

%-presˡ-∣ : ∀ {m n d ≢0} → d ∣ m → d ∣ n → d ∣ (m % n) {≢0}
%-presˡ-∣ {m} {n@(suc n-1)} {d} (divides a refl) (divides b 1+n≡bd) =
  divides (a ∸ ad/n * b) $ begin-equality
    a * d % n              ≡⟨ m%n≡m∸m/n*n (a * d) n-1 ⟩
    a * d ∸ ad/n * n       ≡⟨ cong (λ v → a * d ∸ ad/n * v) 1+n≡bd ⟩
    a * d ∸ ad/n * (b * d) ≡⟨ sym (cong (a * d ∸_) (*-assoc ad/n b d)) ⟩
    a * d ∸ (ad/n * b) * d ≡⟨ sym (*-distribʳ-∸ d a (ad/n * b)) ⟩
    (a ∸ ad/n * b) * d     ∎
  where open ≤-Reasoning; ad/n = a * d / n

------------------------------------------------------------------------
-- DEPRECATED - please use new names as continuing support for the old
-- names is not guaranteed.

-- Version 0.14

∣-+ = ∣m∣n⇒∣m+n
{-# WARNING_ON_USAGE ∣-+
"Warning: ∣-+ was deprecated in v0.14.
Please use ∣m∣n⇒∣m+n instead."
#-}
∣-∸ = ∣m+n∣m⇒∣n
{-# WARNING_ON_USAGE ∣-∸
"Warning: ∣-∸ was deprecated in v0.14.
Please use ∣m+n∣m⇒∣n instead."
#-}
∣-* = n∣m*n
{-# WARNING_ON_USAGE ∣-*
"Warning: ∣-* was deprecated in v0.14.
Please use n∣m*n instead."
#-}

-- Version 0.17

open import Data.Fin.Base using (Fin; zero; suc; toℕ)
import Data.Fin.Properties as FP
open import Data.Nat.Solver
open +-*-Solver

nonZeroDivisor-lemma : ∀ m q (r : Fin (1 + m)) → toℕ r ≢ 0 →
                       1 + m ∤ toℕ r + q * (1 + m)
nonZeroDivisor-lemma m zero r r≢zero (divides zero eq) = r≢zero $ begin-equality
  toℕ r      ≡⟨ sym (*-identityˡ (toℕ r)) ⟩
  1 * toℕ r  ≡⟨ eq ⟩
  0          ∎
  where open ≤-Reasoning
nonZeroDivisor-lemma m zero r r≢zero (divides (suc q) eq) =
  m+1+n≰m m $ begin
    m + suc (q * suc m) ≡⟨ +-suc m (q * suc m) ⟩
    suc (m + q * suc m) ≡⟨ sym eq ⟩
    1 * toℕ r           ≡⟨ *-identityˡ (toℕ r) ⟩
    toℕ r               ≤⟨ FP.toℕ≤pred[n] r ⟩
    m                   ∎
    where open ≤-Reasoning
nonZeroDivisor-lemma m (suc q) r r≢zero d =
  nonZeroDivisor-lemma m q r r≢zero (∣m+n∣m⇒∣n d' ∣-refl)
  where
  lem = solve 3 (λ m r q → r :+ (m :+ q)  :=  m :+ (r :+ q))
                refl (suc m) (toℕ r) (q * suc m)
  d' = subst (1 + m ∣_) lem d
{-# WARNING_ON_USAGE nonZeroDivisor-lemma
"Warning: nonZeroDivisor-lemma was deprecated in v0.17."
#-}

-- Version 1.1

poset = ∣-poset
{-# WARNING_ON_USAGE poset
"Warning: poset was deprecated in v1.1.
Please use ∣-poset instead."
#-}
*-cong = *-monoʳ-∣
{-# WARNING_ON_USAGE *-cong
"Warning: *-cong was deprecated in v1.1.
Please use *-monoʳ-∣ instead."
#-}
/-cong = *-cancelˡ-∣
{-# WARNING_ON_USAGE /-cong
"Warning: /-cong was deprecated in v1.1.
Please use *-cancelˡ-∣ instead."
#-}


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

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Function using (id; _∘_)

open import Cubical.Data.Unit
open import Cubical.Data.Empty

open import Cubical.HITs.PropositionalTruncation

open import Cubical.Relation.Unary

private
  variable
    ℓ ℓ₁ ℓ₂ ℓ₃ : Level
    A : Type ℓ

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

Empty∅ : Empty {A = A} ∅
Empty∅ _ ()

UniversalU : Π[ U {A = A} ]
UniversalU _ = tt

∁∅≡U : ∁ ∅ ≡ U {A = A}
∁∅≡U = ⇔toPath _ _ ((λ _ → tt) , λ _ ())

∁U≡∅ : ∁ U ≡ ∅ {A = A}
∁U≡∅ = ⇔toPath _ _ ((λ f → f tt) , λ ())

------------------------------------------------------------------------
-- Propositions

module _ (P : Pred A ℓ₁) (Q : Pred A ℓ₂) where

  isProp⊆ : isProp (P ⊆ Q)
  isProp⊆ = isPropImplicitΠ λ _ → isPropΠ λ _ → isProp[ Q ] _

  isProp⊂ : isProp (P ⊂ Q)
  isProp⊂ = isProp× isProp⊆ (isPropΠ λ _ → isProp⊥)

  isProp⇔ : isProp (P ⇔ Q)
  isProp⇔ = isProp× isProp⊆ (isPropImplicitΠ λ _ → isPropΠ λ _ → isProp[ P ] _)

module _ (P : Pred A ℓ) where

  isPropEmpty : isProp (Empty P)
  isPropEmpty = isPropΠ2 λ _ _ → isProp⊥

  isPropSatisfiable : isProp ∃⟨ P ⟩
  isPropSatisfiable = squash

  isPropUniversal : isProp Π[ P ]
  isPropUniversal = isPropΠ isProp[ P ]

------------------------------------------------------------------------
-- Predicate equivalence

⇔-reflexive : (P : Pred A ℓ) → P ⇔ P
⇔-reflexive P = id , id

⇔-symmetric : (P : Pred A ℓ₁) (Q : Pred A ℓ₂) → P ⇔ Q → Q ⇔ P
⇔-symmetric P Q (x , y) = (y , x)

⇔-transitive : (P : Pred A ℓ₁) (Q : Pred A ℓ₂) (R : Pred A ℓ₃) →
                  P ⇔ Q → Q ⇔ R → P ⇔ R
⇔-transitive P Q R (x , y) (w , z) = w ∘ x , y ∘ z