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

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

open import Algebra.Bundles

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

open Lattice L
open import Algebra.Structures _≈_
open import Algebra.Definitions _≈_
import Algebra.Properties.Semilattice as SemilatticeProperties
open import Relation.Binary
import Relation.Binary.Lattice as R
open import Relation.Binary.Reasoning.Setoid  setoid
open import Function.Base
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (_⇔_; module Equivalence)
open import Data.Product using (_,_; swap)

------------------------------------------------------------------------
-- _∧_ is a semilattice

∧-idem : Idempotent _∧_
∧-idem x = begin
  x ∧ x            ≈⟨ ∧-congˡ (sym (∨-absorbs-∧ _ _)) ⟩
  x ∧ (x ∨ x ∧ x)  ≈⟨ ∧-absorbs-∨ _ _ ⟩
  x                ∎

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

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

∧-isBand : IsBand _∧_
∧-isBand = record
  { isSemigroup = ∧-isSemigroup
  ; idem        = ∧-idem
  }

∧-isSemilattice : IsSemilattice _∧_
∧-isSemilattice = record
  { isBand = ∧-isBand
  ; comm   = ∧-comm
  }

∧-semilattice : Semilattice l₁ l₂
∧-semilattice = record
  { isSemilattice = ∧-isSemilattice
  }

open SemilatticeProperties ∧-semilattice public
  using
  ( ∧-isOrderTheoreticMeetSemilattice
  ; ∧-isOrderTheoreticJoinSemilattice
  ; ∧-orderTheoreticMeetSemilattice
  ; ∧-orderTheoreticJoinSemilattice
  )

------------------------------------------------------------------------
-- _∨_ is a semilattice

∨-idem : Idempotent _∨_
∨-idem x = begin
  x ∨ x      ≈⟨ ∨-congˡ (sym (∧-idem _)) ⟩
  x ∨ x ∧ x  ≈⟨ ∨-absorbs-∧ _ _ ⟩
  x          ∎

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

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

∨-isBand : IsBand _∨_
∨-isBand = record
  { isSemigroup = ∨-isSemigroup
  ; idem        = ∨-idem
  }

∨-isSemilattice : IsSemilattice _∨_
∨-isSemilattice = record
  { isBand = ∨-isBand
  ; comm   = ∨-comm
  }

∨-semilattice : Semilattice l₁ l₂
∨-semilattice = record
  { isSemilattice = ∨-isSemilattice
  }

open SemilatticeProperties ∨-semilattice public
  using ()
  renaming
  ( ∧-isOrderTheoreticMeetSemilattice to ∨-isOrderTheoreticMeetSemilattice
  ; ∧-isOrderTheoreticJoinSemilattice to ∨-isOrderTheoreticJoinSemilattice
  ; ∧-orderTheoreticMeetSemilattice   to ∨-orderTheoreticMeetSemilattice
  ; ∧-orderTheoreticJoinSemilattice   to ∨-orderTheoreticJoinSemilattice
  )

------------------------------------------------------------------------
-- 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 algebraic lattice can be turned into an order-theoretic one.

open SemilatticeProperties ∧-semilattice public using (poset)
open Poset poset using (_≤_; isPartialOrder)

∨-∧-isOrderTheoreticLattice : R.IsLattice _≈_ _≤_ _∨_ _∧_
∨-∧-isOrderTheoreticLattice = record
  { isPartialOrder = isPartialOrder
  ; supremum       = supremum
  ; infimum        = infimum
  }
  where
  open R.MeetSemilattice ∧-orderTheoreticMeetSemilattice using (infimum)
  open R.JoinSemilattice ∨-orderTheoreticJoinSemilattice using (x≤x∨y; y≤x∨y; ∨-least)
    renaming (_≤_ to _≤′_)

  -- An alternative but equivalent interpretation of the order _≤_.

  sound : ∀ {x y} → x ≤′ y → x ≤ y
  sound {x} {y} y≈y∨x = sym $ begin
    x ∧ y        ≈⟨ ∧-congˡ y≈y∨x ⟩
    x ∧ (y ∨ x)  ≈⟨ ∧-congˡ (∨-comm y x) ⟩
    x ∧ (x ∨ y)  ≈⟨ ∧-absorbs-∨ x y ⟩
    x            ∎

  complete : ∀ {x y} → x ≤ y → x ≤′ y
  complete {x} {y} x≈x∧y = sym $ begin
    y ∨ x        ≈⟨ ∨-congˡ x≈x∧y ⟩
    y ∨ (x ∧ y)  ≈⟨ ∨-congˡ (∧-comm x y) ⟩
    y ∨ (y ∧ x)  ≈⟨ ∨-absorbs-∧ y x ⟩
    y            ∎

  supremum : R.Supremum _≤_ _∨_
  supremum x y =
     sound (x≤x∨y x y) ,
     sound (y≤x∨y x y) ,
     λ z x≤z y≤z → sound (∨-least (complete x≤z) (complete y≤z))

∨-∧-orderTheoreticLattice : R.Lattice _ _ _
∨-∧-orderTheoreticLattice = record
  { isLattice = ∨-∧-isOrderTheoreticLattice
  }

------------------------------------------------------------------------
-- 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 ⟨$⟩ ∨-absorbs-∧ x y)
                 , (λ x y → to ⟨$⟩ ∧-absorbs-∨ x y)
    }
  } where open module E {x y} = Equivalence (≈⇔≈′ {x} {y})

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

-- Version 1.1

∧-idempotent = ∧-idem
{-# WARNING_ON_USAGE ∧-idempotent
"Warning: ∧-idempotent was deprecated in v1.1.
Please use ∧-idem instead."
#-}
∨-idempotent = ∨-idem
{-# WARNING_ON_USAGE ∨-idempotent
"Warning: ∨-idempotent was deprecated in v1.1.
Please use ∨-idem instead."
#-}
isOrderTheoreticLattice = ∨-∧-isOrderTheoreticLattice
{-# WARNING_ON_USAGE isOrderTheoreticLattice
"Warning: isOrderTheoreticLattice was deprecated in v1.1.
Please use ∨-∧-isOrderTheoreticLattice instead."
#-}
orderTheoreticLattice = ∨-∧-orderTheoreticLattice
{-# WARNING_ON_USAGE orderTheoreticLattice
"Warning: orderTheoreticLattice was deprecated in v1.1.
Please use ∨-∧-orderTheoreticLattice instead."
#-}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of right-scaling
------------------------------------------------------------------------

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

open import Relation.Binary

-- The properties are parameterised by the two carriers and
-- the result equality.

module Algebra.Module.Definitions.Right
  {a b ℓb} (A : Set a) {B : Set b} (_≈_ : Rel B ℓb)
  where

open import Data.Product
open import Data.Sum

------------------------------------------------------------------------
-- Binary operations

open import Algebra.Core

------------------------------------------------------------------------
-- Properties of operations

RightIdentity : A → Opᵣ A B → Set _
RightIdentity a _∙ᴮ_ = ∀ m → (m ∙ᴮ a) ≈ m

Associative : Op₂ A → Opᵣ A B → Set _
Associative _∙ᴬ_ _∙ᴮ_ = ∀ m x y → ((m ∙ᴮ x) ∙ᴮ y) ≈ (m ∙ᴮ (x ∙ᴬ y))

_DistributesOverˡ_⟶_ : Opᵣ A B → Op₂ A → Op₂ B → Set _
_*_ DistributesOverˡ _+ᴬ_ ⟶ _+ᴮ_ =
  ∀ m x y → (m * (x +ᴬ y)) ≈ ((m * x) +ᴮ (m * y))

_DistributesOverʳ_ : Opᵣ A B → Op₂ B → Set _
_*_ DistributesOverʳ _+_ =
  ∀ x m n → ((m + n) * x) ≈ ((m * x) + (n * x))

LeftZero : B → Opᵣ A B → Set _
LeftZero z _∙_ = ∀ x → (z ∙ x) ≈ z

RightZero : A → B → Opᵣ A B → Set _
RightZero zᴬ zᴮ _∙_ = ∀ x → (x ∙ zᴬ) ≈ zᴮ

Commutative : Opᵣ A B → Set _
Commutative _∙_ = ∀ m x y → ((m ∙ x) ∙ y) ≈ ((m ∙ y) ∙ x)

LeftCongruent : ∀ {ℓa} → Rel A ℓa → Opᵣ A B → Set _
LeftCongruent ≈ᴬ _∙_ = ∀ {m} → (m ∙_) Preserves ≈ᴬ ⟶ _≈_

RightCongruent : Opᵣ A B → Set _
RightCongruent _∙_ = ∀ {x} → (_∙ x) Preserves _≈_ ⟶ _≈_

Congruent : ∀ {ℓa} → Rel A ℓa → Opᵣ A B → Set _
Congruent ≈ᴬ ∙ = ∙ Preserves₂ _≈_ ⟶ ≈ᴬ ⟶ _≈_


module Prelude where

infixr 50 _,_
infixl 40 _◄_
infix  30 _∈_

data _×_ (A B : Set) : Set where
  _,_ : A -> B -> A × B

data List (A : Set) : Set where
  ε   : List A
  _◄_ : List A -> A -> List A

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

data Box {A : Set}(P : A -> Set) : List A -> Set where
  ⟨⟩  : Box P ε
  _◃_ : forall {xs x} -> Box P xs -> P x -> Box P (xs ◄ x)

_!_ : {A : Set}{P : A -> Set}{xs : List A}{x : A} ->
      Box P xs -> x ∈ xs -> P x
⟨⟩      ! ()
(_ ◃ v) ! hd   = v
(ρ ◃ _) ! tl x = ρ ! x


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

open import Categories.Category

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

open import Level

open import Categories.Category.CartesianClosed C
open import Categories.Category.Complete.Finitely C
open import Categories.Diagram.Equalizer C
open import Categories.Diagram.SubobjectClassifier C

open Category C

record Topos : Set (levelOfTerm C) where
  field
    cartesianClosed     : CartesianClosed
    subobjectClassifier : SubobjectClassifier
    equalizer           : ∀ {A B} (f g : A ⇒ B) → Equalizer f g

  open CartesianClosed cartesianClosed public

  module subobjectClassifier = SubobjectClassifier subobjectClassifier

  finitelyComplete : FinitelyComplete
  finitelyComplete = record
    { cartesian = cartesian
    ; equalizer = equalizer
    }

  open FinitelyComplete finitelyComplete using (module equalizer; pullback) public


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

open import Base
open import Algebra.Groups
open import Integers

module Algebra.GroupIntegers where

_+_ : ℤ → ℤ → ℤ
O + m = m
pos O + m = succ m
pos (S n) + m = succ (pos n + m)
neg O + m = pred m
neg (S n) + m = pred (neg n + m)

-_ : ℤ → ℤ
- O = O
- (pos m) = neg m
- (neg m) = pos m

+-succ : (m n : ℤ) → succ m + n ≡ succ (m + n)
+-succ O n = refl _
+-succ (pos m) n = refl _
+-succ (neg O) n = ! (succ-pred n)
+-succ (neg (S m)) n = ! (succ-pred _)

+-pred : (m n : ℤ) → pred m + n ≡ pred (m + n)
+-pred O n = refl _
+-pred (pos O) n = ! (pred-succ n)
+-pred (pos (S m)) n = ! (pred-succ _)
+-pred (neg m) n = refl _

+-assoc : (m n p : ℤ) → (m + n) + p ≡ m + (n + p)
+-assoc O n p = refl _
+-assoc (pos O) n p = +-succ n p
+-assoc (pos (S m)) n p = +-succ (pos m + n) p ∘ map succ (+-assoc (pos m) n p)
+-assoc (neg O) n p = +-pred n p
+-assoc (neg (S m)) n p = +-pred (neg m + n) p ∘ map pred (+-assoc (neg m) n p)

O-right-unit : (m : ℤ) → m + O ≡ m
O-right-unit O = refl _
O-right-unit (pos O) = refl _
O-right-unit (pos (S m)) = map succ (O-right-unit (pos m))
O-right-unit (neg O) = refl _
O-right-unit (neg (S m)) = map pred (O-right-unit (neg m))

succ-+-right : (m n : ℤ) → succ (m + n) ≡ m + succ n
succ-+-right O n = refl _
succ-+-right (pos O) n = refl _
succ-+-right (pos (S m)) n = map succ (succ-+-right (pos m) n)
succ-+-right (neg O) n = succ-pred n ∘ ! (pred-succ n)
succ-+-right (neg (S m)) n = (succ-pred (neg m + n) ∘ ! (pred-succ (neg m + n)))
                             ∘ map pred (succ-+-right (neg m) n)

pred-+-right : (m n : ℤ) → pred (m + n) ≡ m + pred n
pred-+-right O n = refl _
pred-+-right (pos O) n = pred-succ n ∘ ! (succ-pred n)
pred-+-right (pos (S m)) n = (pred-succ (pos m + n) ∘ ! (succ-pred (pos m + n)))
                             ∘ map succ (pred-+-right (pos m) n)
pred-+-right (neg O) n = refl _
pred-+-right (neg (S m)) n = map pred (pred-+-right (neg m) n)

opp-right-inverse : (m : ℤ) → m + (- m) ≡ O
opp-right-inverse O = refl O
opp-right-inverse (pos O) = refl O
opp-right-inverse (pos (S m)) = succ-+-right (pos m) (neg (S m))
                                ∘ opp-right-inverse (pos m)
opp-right-inverse (neg O) = refl O
opp-right-inverse (neg (S m)) = pred-+-right (neg m) (pos (S m))
                                ∘ opp-right-inverse (neg m)

opp-left-inverse : (m : ℤ) → (- m) + m ≡ O
opp-left-inverse O = refl O
opp-left-inverse (pos O) = refl O
opp-left-inverse (pos (S m)) = pred-+-right (neg m) (pos (S m))
                               ∘ opp-left-inverse (pos m)
opp-left-inverse (neg O) = refl O
opp-left-inverse (neg (S m)) = succ-+-right (pos m) (neg (S m))
                               ∘ opp-left-inverse (neg m)
                 -- pred-+-right (neg m) (pos (S m)) ∘ opp-left-inverse (pos m)

ℤ-group : Group _
ℤ-group = group (pregroup
  ℤ
  _+_ O -_
  +-assoc O-right-unit (λ m → refl _) opp-right-inverse opp-left-inverse)
  ℤ-is-set


module Sets.PredicateSet.Listable where

import      Lvl
open import Data.List as List using (List)
open import Data.List.Functions renaming (_++_ to _∪ₗ_) using (singleton)
open import Data.List.Relation.Membership renaming (_∈_ to _∈ₗ_ ; _∈!_ to _∈!ₗ_) using (use ; skip)
open import Data.List.Relation.Permutation
open import Data.List.Relation.Quantification using (∀ₗᵢₛₜ ; module AllElements ; ExistsElement ; ExistsElementEquivalence ; module ExistsElementEquivalence)
open import Functional
open import Lang.Instance
open import Logic.Propositional
open import Relator.Equals
open import Relator.Equals.Proofs.Equiv
open import Sets.PredicateSet using (PredSet ; _∈_ ; ∅ ; _∪_ ; •_)
open        Sets.PredicateSet.BoundedQuantifiers
open import Type.Properties.Singleton
open import Type

private variable ℓ ℓ₁ ℓ₂ : Lvl.Level
private variable T : Type{ℓ}
private variable S S₁ S₂ : PredSet{ℓ}(T)
private variable x : T

module _ (S : PredSet{ℓ}(T)) where
  record Listable : Type{Lvl.of(T) Lvl.⊔ ℓ} where
    field
      list : List(T)
      unique : ∀ₛ(S) (_∈!ₗ list)
      restrict : ∀ₗᵢₛₜ(list)  (_∈ S)

    proof : ∀ₛ(S) (_∈ₗ list)
    proof = IsUnit.unit ∘ unique
  list = inst-fn Listable.list

private variable l l₁ l₂ l₁₂ : Listable(S)

∅-listable : Listable{ℓ = ℓ}{T = T}(∅)
Listable.list     ∅-listable = List.∅
Listable.unique   ∅-listable ()
Listable.restrict ∅-listable = AllElements.∅

singleton-listable : Listable(•_ ⦃ [≡]-equiv ⦄ x)
Listable.list     singleton-listable           = singleton _
Listable.unique   singleton-listable [≡]-intro = intro (use [≡]-intro) (\{ {ExistsElement.• x} → ExistsElementEquivalence.use})
Listable.restrict singleton-listable           = [≡]-intro AllElements.⊰ AllElements.∅

{- TODO: Will not satisfy Listable because (_++_) may yield multiples which Listable does not allow, and therefore list-[∪] below is also incorrect.
∪-listable : ⦃ Listable(S₁) ⦄ → ⦃ Listable(S₂) ⦄ → Listable(S₁ ∪ S₂)
Listable.list (∪-listable {S₁ = S₁} {S₂ = S₂}) = list(S₁) ∪ₗ list(S₂)
Listable.unique   ∪-listable = {!!}
Listable.restrict ∪-listable = {!!}-}

-- list-[∪] : ∀{Γ₁ : PredSet{ℓ₁}(Formula(P))}{Γ₂ : PredSet{ℓ₂}(Formula(P))}{l₁₂ : Listable(Γ₁ PredSet.∪ Γ₂)} → Logic.∃{Obj = Listable(Γ₁) ⨯ Listable(Γ₂)}(\(l₁ , l₂) → (list(Γ₁ PredSet.∪ Γ₂) ⦃ l₁₂ ⦄ ≡ (list Γ₁ ⦃ l₁ ⦄) ∪ (list Γ₂ ⦃ l₂ ⦄)))
postulate list-[∪] : (list(S₁ ∪ S₂) ⦃ l₁₂ ⦄ permutes (list S₁ ⦃ l₁ ⦄) ∪ₗ (list S₂ ⦃ l₂ ⦄))
postulate list-[∪·] : (list(S ∪ (•_ ⦃ [≡]-equiv ⦄ x)) ⦃ l₁₂ ⦄ permutes (list S ⦃ l ⦄) ∪ₗ singleton(x))


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

open import Sets.EquivalenceRelations
open import Setoids.Setoids
open import Groups.FreeGroup.Definition
open import Groups.Homomorphisms.Definition
open import Groups.Homomorphisms.Lemmas
open import Groups.Definition
open import Decidable.Sets
open import Numbers.Naturals.Order
open import LogicalFormulae
open import Semirings.Definition
open import Groups.Lemmas

module Groups.FreeGroup.UniversalProperty {a : _} {A : Set a} (decA : DecidableSet A) where

open import Groups.FreeGroup.Word decA
open import Groups.FreeGroup.Group decA

universalPropertyFunction : {c d : _} {C : Set c} {S : Setoid {c} {d} C} {_+_ : C → C → C} (G : Group S _+_) → (f : A → C) → ReducedWord → C
universalPropertyFunction G f empty = Group.0G G
universalPropertyFunction {_+_ = _+_} G f (prependLetter (ofLetter l) w x) = (f l) + universalPropertyFunction G f w
universalPropertyFunction {_+_ = _+_} G f (prependLetter (ofInv l) w x) = (Group.inverse G (f l)) + universalPropertyFunction G f w

private
  prepLemma : {c d : _} {C : Set c} {S : Setoid {c} {d} C} {_+_ : C → C → C} → (G : Group S _+_) → (f : A → C) → (x : ReducedWord) (l : _) → Setoid._∼_ S (universalPropertyFunction G f (prepend x (ofLetter l))) ((f l) + universalPropertyFunction G f x)
  prepLemma {S = S} G f empty l = reflexive
    where
      open Setoid S
      open Equivalence eq
  prepLemma {S = S} G f (prependLetter (ofLetter x) w pr) l = reflexive
    where
      open Setoid S
      open Equivalence eq
  prepLemma {S = S} G f (prependLetter (ofInv x) w pr) l with decA l x
  ... | inl refl = transitive (symmetric identLeft) (transitive (+WellDefined (symmetric invRight) reflexive) (symmetric +Associative))
    where
      open Group G
      open Setoid S
      open Equivalence eq
  ... | inr l!=x = reflexive
    where
      open Setoid S
      open Equivalence eq

  prepLemma' : {c d : _} {C : Set c} {S : Setoid {c} {d} C} {_+_ : C → C → C} → (G : Group S _+_) → (f : A → C) → (x : ReducedWord) (l : _) → Setoid._∼_ S (universalPropertyFunction G f (prepend x (ofInv l))) (Group.inverse G (f l) + universalPropertyFunction G f x)
  prepLemma' {S = S} G f empty l = reflexive
    where
      open Setoid S
      open Equivalence eq
  prepLemma' {S = S} G f (prependLetter (ofLetter x) w pr) l with decA l x
  ... | inl refl = symmetric (transitive +Associative (transitive (+WellDefined invLeft reflexive) identLeft))
    where
      open Group G
      open Setoid S
      open Equivalence eq
  ... | inr l!=x = reflexive
    where
      open Setoid S
      open Equivalence eq
  prepLemma' {S = S} G f (prependLetter (ofInv x) w pr) l = reflexive
    where
      open Setoid S
      open Equivalence eq

  homLemma : {c d : _} {C : Set c} {S : Setoid {c} {d} C} {_+_ : C → C → C} → (G : Group S _+_) → (f : A → C) → (x y : ReducedWord) → Setoid._∼_ S (universalPropertyFunction G f (x +W y)) (universalPropertyFunction G f x + universalPropertyFunction G f y)
  homLemma {S = S} G f empty y = symmetric identLeft
    where
      open Setoid S
      open Equivalence eq
      open Group G
  homLemma {S = S} G f (prependLetter (ofLetter l) x pr) y = transitive (transitive (prepLemma G f (x +W y) l) (+WellDefined reflexive (homLemma G f x y))) (+Associative {f l})
    where
      open Setoid S
      open Equivalence eq
      open Group G
  homLemma {S = S} G f (prependLetter (ofInv l) x pr) y = transitive (transitive (prepLemma' G f (x +W y) l) (+WellDefined reflexive (homLemma G f x y))) +Associative
    where
      open Setoid S
      open Equivalence eq
      open Group G

universalPropertyHom : {c d : _} {C : Set c} {S : Setoid {c} {d} C} {_+_ : C → C → C} (G : Group S _+_) → (f : A → C) → GroupHom freeGroup G (universalPropertyFunction G f)
GroupHom.groupHom (universalPropertyHom {S = S} G f) {x} {y} = homLemma G f x y
GroupHom.wellDefined (universalPropertyHom {S = S} G f) refl = Equivalence.reflexive (Setoid.eq S)

freeEmbedding : A → ReducedWord
freeEmbedding a = prependLetter (ofLetter a) empty (wordEmpty refl)

freeEmbeddingInjective : {a b : A} → freeEmbedding a ≡ freeEmbedding b → a ≡ b
freeEmbeddingInjective {a} {b} pr = ofLetterInjective (prependLetterInjective' pr)

universalPropertyDiagram : {c d : _} {C : Set c} {S : Setoid {c} {d} C} → {_+_ : C → C → C} → (G : Group S _+_) → (f : A → C) → (x : A) → Setoid._∼_ S (f x) (universalPropertyFunction G f (freeEmbedding x))
universalPropertyDiagram {S = S} G f x = symmetric (Group.identRight G)
  where
    open Setoid S
    open Equivalence eq

universalPropertyUniqueness : {c d : _} {C : Set c} {S : Setoid {c} {d} C} → {_+_ : C → C → C} → (G : Group S _+_) → (f : A → C) → {f' : ReducedWord → C} → (GroupHom freeGroup G f') → ((x : A) → Setoid._∼_ S (f x) (f' (freeEmbedding x))) → (x : ReducedWord) → Setoid._∼_ S (f' x) (universalPropertyFunction G f x)
universalPropertyUniqueness {S = S} G f {f'} f'IsHom commutes empty = imageOfIdentityIsIdentity f'IsHom
universalPropertyUniqueness {S = S} G f {f'} f'IsHom commutes (prependLetter (ofLetter a) w pr) = transitive (transitive (GroupHom.wellDefined f'IsHom (equalityCommutative (prependValid w a pr))) (GroupHom.groupHom f'IsHom)) (+WellDefined (symmetric (commutes a)) (universalPropertyUniqueness G f f'IsHom commutes w))
  where
    open Setoid S
    open Equivalence eq
    open Group G
universalPropertyUniqueness {S = S} G f {f'} f'IsHom commutes (prependLetter (ofInv a) w pr) = transitive (transitive (GroupHom.wellDefined f'IsHom (equalityCommutative (prependValid' w a pr))) (transitive (GroupHom.groupHom f'IsHom) (+WellDefined (homRespectsInverse f'IsHom) reflexive))) (+WellDefined (symmetric (inverseWellDefined G (commutes a))) (universalPropertyUniqueness G f f'IsHom commutes w))
  where
    open Setoid S
    open Equivalence eq
    open Group G


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

module Categories.Functor.Instance.0-Truncation where

-- 0-trucation of groupoids as a functor from Groupoids to Setoids.
--
-- This is the right-adjoint of the inclusion functor from Setoids to
-- Groupoids (see Categories.Functor.Adjoint.Instance.ZeroTruncation)

import Function
open import Function.Equality using (_⟶_)
open import Relation.Binary using (Setoid)

open import Categories.Category using (Category; _[_≈_])
open import Categories.Functor hiding (id)
open import Categories.Category.Groupoid using (Groupoid)
open import Categories.Category.Instance.Groupoids using (Groupoids)
open import Categories.Category.Instance.Setoids using (Setoids)
open import Categories.NaturalTransformation.NaturalIsomorphism
  using (NaturalIsomorphism; _≃_)

Trunc : ∀ {o ℓ e} → Functor (Groupoids o ℓ e) (Setoids o ℓ)
Trunc {o} {ℓ} {e} = record
   { F₀           = TruncSetoid
   ; F₁           = λ {G H} F → TruncMap {G} {H} F
   ; identity     = Function.id
   ; homomorphism = λ {_ _ _ F G} f → F₁ G (F₁ F f)
   ; F-resp-≈     = λ {G H} → TruncRespNI {G} {H}
   }
   where
     open Groupoid using (category)
     open Functor

     TruncSetoid : Groupoid o ℓ e → Setoid o ℓ
     TruncSetoid G = record
       { Carrier       = Obj
       ; _≈_           = _⇒_
       ; isEquivalence = record
         { refl  = id
         ; sym   = _⁻¹
         ; trans = λ f g → g ∘ f
         }
       }
       where open Groupoid G

     TruncMap : ∀ {G H} → Functor (category G) (category H) →
                TruncSetoid G ⟶ TruncSetoid H
     TruncMap F = record { _⟨$⟩_ = F₀ F ; cong  = F₁ F }

     TruncRespNI : ∀ {G H : Groupoid o ℓ e}
                   {E F : Functor (category G) (category H)} →
                   E ≃ F → Setoids o ℓ [ TruncMap {G} {H} E ≈ TruncMap {G} {H} F ]
     TruncRespNI {_} {H} {_} {F} μ {a} f = F₁ F f ∘ ⇒.η a
       where
         open Groupoid H
         open NaturalIsomorphism μ


{-

          Types Summer School 2007

                 Bertinoro
             Aug 19 - 31, 2007

                   Agda

                Ulf Norell

-}

-- Let's have a closer look at the module system

module Modules where

{-

  Importing and opening modules

-}

-- You can import a module defined in a different file.
import Nat

-- This will bring the module into scope and allows you to
-- access its contents using qualified names.
plusTwo : Nat.Nat -> Nat.Nat
plusTwo n = Nat._+_ n 2

-- To bring everything from a module into scope you can open
-- the module.
open Nat

z : Nat
z = zero

-- There's also a short-hand to import and open at the same time
open import Bool

_&&_ : Bool -> Bool -> Bool
x && y = if x then y else false

-- Sometimes it's nice to be able to control what is brought
-- into scope when you open a module. There are three modifiers
-- that affect this: using, hiding and renaming.

module DifferentWaysOfOpeningNat where

  -- nothing but Nat
  open Nat using (Nat)

  -- everything but zero
  open Nat hiding (zero)

  -- everything, but zero and suc under different names
  open Nat renaming (zero to ZZ; suc to S_S)

  two : Nat
  two = S S zero S S

  -- you can combine using or hiding with renaming, but not using
  -- with hiding (for obvious reasons).

-- To re-export something opened use the public modifier.
module A where
  open Nat public using (Nat)

N = A.Nat -- now Nat is a visible name in module A

{-

  Parameterised modules

-}

-- A very useful feature is parameterised modules.

data Vec (A : Set) : Nat -> Set where
  []   : Vec A 0
  _::_ : {n : Nat} -> A -> Vec A n -> Vec A (suc n)

infixr 40 _::_

module Sort {A : Set}(_≤_ : A -> A -> Bool) where

  insert : {n : Nat} -> A -> Vec A n -> Vec A (suc n)
  insert x [] = x :: []
  insert x (y :: ys) = if x ≤ y
                       then x :: y :: ys
                       else y :: insert x ys

  sort : {n : Nat} -> Vec A n -> Vec A n
  sort []        = []
  sort (x :: xs) = insert x (sort xs)

_≤_ : Nat -> Nat -> Bool
zero  ≤ m     = true
suc n ≤ zero  = false
suc n ≤ suc m = n ≤ m

-- When used directly, functions from parameterised modules
-- take the parameters as extra arguments.
test = Sort.sort _≤_ (6 :: 2 :: 0 :: 4 :: [])

-- But, you can also apply the entire module to its arguments.
-- Let's open the new module while we're at it.
open module SortNat = Sort _≤_

test' = sort (3 :: 2 :: 4 :: 0 :: [])

{-

  Local definitions

-}

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

subst : {A : Set}(C : A -> Set){x y : A} -> x == y -> C x -> C y
subst C refl cx = cx

cong : {A B : Set}(f : A -> B){x y : A} -> x == y -> f x == f y
cong f refl = refl

lem₁ : (n : Nat) -> n + 0 == n
lem₁ zero    = refl
lem₁ (suc n) = cong suc (lem₁ n)

lem₂ : (n m : Nat) -> n + suc m == suc n + m
lem₂ n  zero   = refl
lem₂ n (suc m) = cong suc (lem₂ n m)

{-

  What's next?

-}

-- The final thing on the agenda is records.

-- Move on to: Records.agda


module Category where

open import Prelude

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

record Category {ℓ ℓ′}
                (𝒪 : Set ℓ) (_▹_ : 𝒪 → 𝒪 → Set ℓ′)
              : Set (ℓ ⊔ ℓ′) where
  field
    idₓ    : ∀ {x} → x ▹ x

    _⋄_    : ∀ {x y z} → y ▹ x → z ▹ y → z ▹ x

    lid⋄   : ∀ {x y} → (f : y ▹ x)
                     → idₓ ⋄ f ≡ f

    rid⋄   : ∀ {x y} → (f : y ▹ x)
                     → f ⋄ idₓ ≡ f

    assoc⋄ : ∀ {x y z a} → (h : a ▹ z) (g : z ▹ y) (f : y ▹ x)
                         → (f ⋄ g) ⋄ h ≡ f ⋄ (g ⋄ h)

𝗦𝗲𝘁 : (ℓ : Level) → Category (Set ℓ) Π
𝗦𝗲𝘁 ℓ =
  record
    { idₓ    = id
    ; _⋄_    = _∘_
    ; lid⋄   = λ f → refl
    ; rid⋄   = λ f → refl
    ; assoc⋄ = λ h g f → refl
    }

𝗦𝗲𝘁₀ : Category (Set ℓ₀) Π
𝗦𝗲𝘁₀ = 𝗦𝗲𝘁 ℓ₀

record Functor {ℓ₁ ℓ₁′ ℓ₂ ℓ₂′}
               {𝒪₁ : Set ℓ₁} {_▹₁_ : 𝒪₁ → 𝒪₁ → Set ℓ₁′}
               {𝒪₂ : Set ℓ₂} {_▹₂_ : 𝒪₂ → 𝒪₂ → Set ℓ₂′}
               (𝗖 : Category 𝒪₁ _▹₁_) (𝗗 : Category 𝒪₂ _▹₂_)
             : Set (ℓ₁ ⊔ ℓ₁′ ⊔ ℓ₂ ⊔ ℓ₂′) where
  private
    module C = Category 𝗖
    module D = Category 𝗗

  field
    Fₓ  : 𝒪₁ → 𝒪₂

    F   : ∀ {x y} → y ▹₁ x → Fₓ y ▹₂ Fₓ x

    idF : ∀ {x} → F (C.idₓ {x = x}) ≡ D.idₓ

    F⋄  : ∀ {x y z} → (g : z ▹₁ y) (f : y ▹₁ x)
                    → F (f C.⋄ g) ≡ F f D.⋄ F g

record NaturalTransformation {ℓ₁ ℓ₁′ ℓ₂ ℓ₂′}
                             {𝒪₁ : Set ℓ₁} {_▹₁_ : 𝒪₁ → 𝒪₁ → Set ℓ₁′}
                             {𝒪₂ : Set ℓ₂} {_▹₂_ : 𝒪₂ → 𝒪₂ → Set ℓ₂′}
                             {𝗖 : Category 𝒪₁ _▹₁_} {𝗗 : Category 𝒪₂ _▹₂_}
                             (𝗙 𝗚 : Functor 𝗖 𝗗)
                           : Set (ℓ₁ ⊔ ℓ₁′ ⊔ ℓ₂ ⊔ ℓ₂′) where
  private
    open module D = Category 𝗗 using (_⋄_)
    open module F = Functor 𝗙 using (Fₓ ; F)
    open module G = Functor 𝗚 using () renaming (Fₓ to Gₓ ; F to G)

  field
    N    : ∀ {x} → Fₓ x ▹₂ Gₓ x

    natN : ∀ {x y} → (f : y ▹₁ x)
                   → (N ⋄ F f) ≡ (G f ⋄ N)

Opposite : ∀ {ℓ ℓ′} → {𝒪 : Set ℓ} {_▹_ : 𝒪 → 𝒪 → Set ℓ′}
                    → Category 𝒪 _▹_
                    → Category 𝒪 (flip _▹_)
Opposite 𝗖 =
  record
    { idₓ    = C.idₓ
    ; _⋄_    = flip C._⋄_
    ; lid⋄   = C.rid⋄
    ; rid⋄   = C.lid⋄
    ; assoc⋄ = λ f g h → C.assoc⋄ h g f ⁻¹
    }
  where
    module C = Category 𝗖

Presheaf : ∀ ℓ {ℓ′ ℓ″} → {𝒪 : Set ℓ′} {_▹_ : 𝒪 → 𝒪 → Set ℓ″}
                       → (𝗖 : Category 𝒪 _▹_)
                       → Set _
Presheaf ℓ 𝗖 = Functor (Opposite 𝗖) (𝗦𝗲𝘁 ℓ)

Presheaf₀ : ∀ {ℓ ℓ′} → {𝒪 : Set ℓ} {_▹_ : 𝒪 → 𝒪 → Set ℓ′}
                     → (𝗖 : Category 𝒪 _▹_)
                     → Set _
Presheaf₀ 𝗖 = Presheaf ℓ₀ 𝗖

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


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

module PiU where

open import Data.Nat using (ℕ; _+_; _*_)

------------------------------------------------------------------------------
-- Define the ``universe'' for Pi.  All versions of Pi share the same
-- universe.  Where they differ is in what combinators exist between
-- members of the universe.
--
-- ZERO is a type with no elements
-- ONE is a type with one element 'tt'
-- PLUS ONE ONE is a type with elements 'false' and 'true'
-- and so on for all finite types built from ZERO, ONE, PLUS, and TIMES
--
-- We also have that U is a type with elements ZERO, ONE, PLUS ONE ONE,
--   TIMES BOOL BOOL, etc.

data U : Set where
  ZERO  : U
  ONE   : U
  PLUS  : U → U → U
  TIMES : U → U → U

-- defines the size of a finite type

toℕ : U → ℕ
toℕ ZERO = 0
toℕ ONE = 1
toℕ (PLUS t₁ t₂) = toℕ t₁ + toℕ t₂
toℕ (TIMES t₁ t₂) = toℕ t₁ * toℕ t₂

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


module Structure.Relator.Equivalence.Proofs where

import      Lvl
open import Functional
open import Structure.Relator.Equivalence
open import Structure.Relator.Properties.Proofs
open import Type

private variable ℓ : Lvl.Level
private variable T A B : Type{ℓ}
private variable _▫_ : T → T → Type{ℓ}
private variable f : A → B

on₂-equivalence : ⦃ eq : Equivalence(_▫_) ⦄ → Equivalence((_▫_) on₂ f)
Equivalence.reflexivity  (on₂-equivalence {_▫_ = _▫_}) = on₂-reflexivity
Equivalence.symmetry     (on₂-equivalence {_▫_ = _▫_}) = on₂-symmetry
Equivalence.transitivity (on₂-equivalence {_▫_ = _▫_}) = on₂-transitivity


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

module Relation.Nullary.Discrete.Properties where

open import Relation.Nullary.Discrete
open import Relation.Nullary.Stable.Properties using (Stable≡→isSet)
open import Relation.Nullary.Decidable.Properties using (Dec→Stable; isPropDec)

open import HLevels
open import Level
open import Path

Discrete→isSet :
  Discrete A → isSet A
Discrete→isSet d = Stable≡→isSet (λ x y → Dec→Stable (x ≡ y) (d x y))

isPropDiscrete :
  isProp (Discrete A)
isPropDiscrete f g i x y = isPropDec (Discrete→isSet f x y) (f x y) (g x y) i


{-# OPTIONS --without-K --safe #-}
module Categories.Functor.Bifunctor where

-- Bifunctor, aka a Functor from C × D to E
open import Level

open import Categories.Category
open import Categories.Functor
open import Categories.Functor.Construction.Constant
open import Categories.Category.Product

private
  variable
    o ℓ e o′ ℓ′ e′ o″ ℓ″ e″ : Level
    C D D₁ D₂ E E₁ E₂ : Category o ℓ e

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

overlap-× : ∀ (H : Bifunctor D₁ D₂ C) (F : Functor E D₁) (G : Functor E D₂) → Functor E C
overlap-× H F G = H ∘F (F ※ G)

reduce-× : ∀ (H : Bifunctor D₁ D₂ C) (F : Functor E₁ D₁) (G : Functor E₂ D₂) → Bifunctor E₁ E₂ C
reduce-× H F G = H ∘F (F ⁂ G)

flip-bifunctor : Bifunctor C D E → Bifunctor D C E
flip-bifunctor b = b ∘F Swap

appˡ : Bifunctor C D E → Category.Obj C → Functor D E
appˡ F c = F ∘F constˡ c

appʳ : Bifunctor C D E → Category.Obj D → Functor C E
appʳ F d = F ∘F constʳ d


module MetaId where

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

  expr = (\ (a : Set) (x : a) -> x) _ zero


module Cats.Util.Logic.Classical where

open import Cats.Util.Logic.Constructive public

-- (Strong) law of the excluded middle
postulate lem : ∀ {p} (P : Set p) → P ∨ ¬ P

-- Double negation elimination
¬¬-elim : ∀ {p} {P : Set p} → ¬ ¬ P → P
¬¬-elim {P = P} ¬¬p with lem P
... | ∨-introl  p = p
... | ∨-intror ¬p = ⊥-elim (¬¬p ¬p)

-- Contrapositive
contrapositive : ∀ {p q} {P : Set p} {Q : Set q}
  → (¬ Q → ¬ P) → P → Q
contrapositive {P = P} {Q} contra p with lem Q
... | ∨-introl q = q
... | ∨-intror ¬q = ⊥-elim (contra ¬q p)

-- De Morgan's nonconstructive rule
¬∧→¬∨¬ : ∀ {p q} {P : Set p} {Q : Set q}
  → ¬ (P ∧ Q) → ¬ P ∨ ¬ Q
¬∧→¬∨¬ {P = P} {Q} ¬p∧q with lem P | lem Q
... | ∨-introl p  | ∨-introl q  = ⊥-elim (¬p∧q (p , q))
... | ∨-introl _  | ∨-intror ¬q = ∨-intror ¬q
... | ∨-intror ¬p | _           = ∨-introl ¬p

-- Negation and ∀
¬∀→∃¬ : ∀ {u p} {U : Set u} {P : U → Set p}
  → ¬ (∀ u → P u) → ∃[ u ] (¬ P u)
¬∀→∃¬ ¬∀ = ¬¬-elim (λ ¬∃¬ → ¬∀ (λ u → ¬¬-elim (λ pu → ¬∃¬ (u , pu))))


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

module Algebra.Pregroup where

-- Definition of the properties of left and right contraction and
-- expansion for usage in the below definition of pregroups.
module AdjointProperties {a ℓ} {A : Set a} (_≤_ : Rel A ℓ) (_∙_ : Op₂ A) (ε : A) where

  LeftContract : Op₁ A → Set _
  LeftContract _ˡ = ∀ x → (x ˡ ∙ x) ≤ ε

  LeftExpand : Op₁ A → Set _
  LeftExpand _ˡ = ∀ x → ε ≤ (x ∙ x ˡ)

  RightContract : Op₁ A → Set _
  RightContract _ʳ = ∀ x → (x ∙ x ʳ) ≤ ε

  RightExpand : Op₁ A → Set _
  RightExpand _ʳ = ∀ x → ε ≤ (x ʳ ∙ x)

  -- define shorthand notation for a term being the left/right adjoint
  -- of another term, which can be proven to be unique.
  _LeftAdjointOf_ : ∀ y x → Set _
  _LeftAdjointOf_ y x = (y ∙ x) ≤ ε × ε ≤ (x ∙ y)

  _RightAdjointOf_ : ∀ y x → Set _
  _RightAdjointOf_ y x = (x ∙ y) ≤ ε × ε ≤ (y ∙ x)

-- Definition of a pregroup, which adds a left and a right adjoint to
-- an ordered monoid.
record IsPregroup {a ℓ₁ ℓ₂} {A : Set a} (≈ : Rel A ℓ₁) (≤ : Rel A ℓ₂)
                  (∙ : Op₂ A) (ε : A) (ˡ : Op₁ A) (ʳ : Op₁ A) : Set (a ⊔ ℓ₁ ⊔ ℓ₂) where

  open AdjointProperties ≤ ∙ ε
  field
    isOrderedMonoid : IsOrderedMonoid ≈ ≤ ∙ ε
    ˡ-cong          : ˡ Preserves ≈ ⟶ ≈
    ʳ-cong          : ʳ Preserves ≈ ⟶ ≈
    ˡ-contract      : LeftContract ˡ
    ˡ-expand        : LeftExpand ˡ
    ʳ-contract      : RightContract ʳ
    ʳ-expand        : RightExpand ʳ

  open IsOrderedMonoid isOrderedMonoid public
  open AdjointProperties ≤ ∙ ε public

record Pregroup c ℓ₁ ℓ₂ : Set (suc (c ⊔ ℓ₁ ⊔ ℓ₂)) where
  infixl 7 _∙_
  infix  4 _≈_
  infix  4 _≤_
  field
    Carrier    : Set c
    _≈_        : Rel Carrier ℓ₁
    _≤_        : Rel Carrier ℓ₂
    _∙_        : Op₂ Carrier
    _ˡ         : Op₁ Carrier
    _ʳ         : Op₁ Carrier
    ε          : Carrier
    isPregroup : IsPregroup _≈_ _≤_ _∙_ ε _ˡ _ʳ

  open IsPregroup isPregroup public

  private
    -- for usage with ≤-Reasoning
    poset : Poset c ℓ₁ ℓ₂
    poset = record { isPartialOrder = isPartialOrder }
    open ≤-Reasoning poset

    -- for usage of substitutivity (which is not defined in IsPregroup)
    orderedMonoid : OrderedMonoid _ _ _
    orderedMonoid = record { isOrderedMonoid = isOrderedMonoid }
    open OrderedMonoid orderedMonoid public using (substitutivity)

  ˡ-unique : ∀ {x y} → y LeftAdjointOf x → y ≈ x ˡ
  ˡ-unique {x} {y} (y-contract , y-expand) = antisym y≤xˡ xˡ≤y
    where
      y≤xˡ : y ≤ x ˡ
      y≤xˡ =
        begin
          y             ≈⟨ sym (proj₂ identity y) ⟩
          y ∙ ε         ≤⟨ compatibility ≤-refl (ˡ-expand x) ⟩
          y ∙ (x ∙ x ˡ) ≈⟨ sym (assoc y x (x ˡ)) ⟩
          (y ∙ x) ∙ x ˡ ≤⟨ compatibility y-contract ≤-refl ⟩
          ε ∙ x ˡ       ≈⟨ proj₁ identity (x ˡ) ⟩
          x ˡ
        ∎
      xˡ≤y : x ˡ ≤ y
      xˡ≤y =
        begin
          x ˡ             ≈⟨ sym (proj₂ identity (x ˡ)) ⟩
          x ˡ ∙ ε         ≤⟨ compatibility ≤-refl y-expand ⟩
          x ˡ ∙ (x ∙ y)   ≈⟨ sym (assoc (x ˡ) x y) ⟩
          (x ˡ  ∙ x) ∙ y  ≤⟨ compatibility (ˡ-contract x) ≤-refl ⟩
          ε ∙ y           ≈⟨ proj₁ identity y ⟩
          y
        ∎

  ʳ-unique : ∀ {x y} → y RightAdjointOf x → y ≈ x ʳ
  ʳ-unique {x} {y} (y-contract , y-expand) = antisym y≤xʳ xʳ≤y
    where
      xʳ≤y : x ʳ ≤ y
      xʳ≤y =
        begin
          x ʳ             ≈⟨ sym (proj₁ identity (x ʳ)) ⟩
          ε ∙ x ʳ         ≤⟨ compatibility y-expand ≤-refl ⟩
          (y ∙ x) ∙ x ʳ ≈⟨ assoc  y x (x ʳ) ⟩
          y ∙ (x ∙ x ʳ) ≤⟨ compatibility ≤-refl (ʳ-contract x) ⟩
          y ∙ ε         ≈⟨ proj₂ identity y ⟩
          y
        ∎
      y≤xʳ : y ≤ x ʳ
      y≤xʳ =
        begin
          y             ≈⟨ sym (proj₁ identity y) ⟩
          ε ∙ y         ≤⟨ compatibility (ʳ-expand x) ≤-refl ⟩
          (x ʳ ∙ x) ∙ y ≈⟨ assoc (x ʳ) x y ⟩
          x ʳ ∙ (x ∙ y) ≤⟨ compatibility ≤-refl y-contract ⟩
          x ʳ ∙ ε       ≈⟨ proj₂ identity (x ʳ) ⟩
          x ʳ
        ∎

  ˡ-identity : ε ˡ ≈ ε
  ˡ-identity = antisym εˡ≤ε ε≤εˡ
    where
      εˡ≤ε : ε ˡ ≤ ε
      εˡ≤ε =
        begin
          ε ˡ     ≈⟨ sym (proj₂ identity (ε ˡ)) ⟩
          ε ˡ ∙ ε ≤⟨ ˡ-contract ε ⟩
          ε
        ∎
      ε≤εˡ : ε ≤ ε ˡ
      ε≤εˡ =
        begin
          ε       ≤⟨ ˡ-expand ε ⟩
          ε ∙ ε ˡ ≈⟨ proj₁ identity (ε ˡ) ⟩
          ε ˡ
        ∎

  ʳ-identity : ε ʳ ≈ ε
  ʳ-identity = antisym εʳ≤ε ε≤εʳ
    where
      εʳ≤ε : ε ʳ ≤ ε
      εʳ≤ε =
        begin
          ε ʳ     ≈⟨ sym (proj₁ identity (ε ʳ)) ⟩
          ε ∙ ε ʳ ≤⟨ ʳ-contract ε ⟩
          ε
        ∎
      ε≤εʳ : ε ≤ ε ʳ
      ε≤εʳ =
        begin
          ε       ≤⟨ ʳ-expand ε ⟩
          ε ʳ ∙ ε ≈⟨ proj₂ identity (ε ʳ) ⟩
          ε ʳ
        ∎

  ˡ-distrib : ∀ x y → y ˡ ∙ x ˡ ≈ (x ∙ y) ˡ
  ˡ-distrib x y = ˡ-unique ([yˡxˡ][xy]≤ε , ε≤[xy][yˡxˡ])
    where
      [yˡxˡ][xy]≤ε : (y ˡ ∙ x ˡ) ∙ (x ∙ y) ≤ ε
      [yˡxˡ][xy]≤ε =
        begin
          (y ˡ ∙ x ˡ) ∙ (x ∙ y) ≈⟨ sym (assoc (y ˡ ∙ x ˡ) x y) ⟩
          ((y ˡ ∙ x ˡ) ∙ x) ∙ y ≈⟨ ∙-cong (assoc (y ˡ) (x ˡ) x) refl ⟩
          (y ˡ ∙ (x ˡ ∙ x)) ∙ y ≤⟨ compatibility (compatibility ≤-refl (ˡ-contract x)) ≤-refl ⟩
          y ˡ ∙ ε ∙ y           ≈⟨ ∙-cong (proj₂ identity (y ˡ)) refl ⟩
          y ˡ ∙ y               ≤⟨ ˡ-contract y ⟩
          ε
        ∎
      ε≤[xy][yˡxˡ] : ε ≤ (x ∙ y) ∙ (y ˡ ∙ x ˡ)
      ε≤[xy][yˡxˡ] =
        begin
          ε                     ≤⟨ ˡ-expand x ⟩
          x ∙ x ˡ               ≈⟨ ∙-cong (sym (proj₂ identity x)) refl ⟩
          x ∙ ε ∙ x ˡ           ≤⟨ compatibility (compatibility ≤-refl (ˡ-expand y)) ≤-refl ⟩
          (x ∙ (y ∙ y ˡ) ∙ x ˡ) ≈⟨ ∙-cong (sym (assoc x y (y ˡ))) refl ⟩
          ((x ∙ y) ∙ y ˡ) ∙ x ˡ ≈⟨ assoc (x ∙ y) (y ˡ) (x ˡ) ⟩
          (x ∙ y) ∙ (y ˡ ∙ x ˡ)
        ∎

  ʳ-distrib : ∀ x y → y ʳ ∙ x ʳ ≈ (x ∙ y) ʳ
  ʳ-distrib x y = ʳ-unique ([xy][yʳxʳ]≤ε , ε≤[xy][yʳxʳ])
    where
      [xy][yʳxʳ]≤ε : (x ∙ y) ∙ (y ʳ ∙ x ʳ) ≤ ε
      [xy][yʳxʳ]≤ε =
        begin
          (x ∙ y) ∙ (y ʳ ∙ x ʳ) ≈⟨ sym (assoc (x ∙ y) (y ʳ) (x ʳ)) ⟩
          ((x ∙ y) ∙ y ʳ) ∙ x ʳ ≈⟨ ∙-cong (assoc x y (y ʳ)) refl ⟩
          (x ∙ (y ∙ y ʳ) ∙ x ʳ) ≤⟨ compatibility (compatibility ≤-refl (ʳ-contract y)) ≤-refl ⟩
          x ∙ ε ∙ x ʳ           ≈⟨ ∙-cong (proj₂ identity x) refl ⟩
          x ∙ x ʳ               ≤⟨ ʳ-contract x ⟩
          ε
        ∎
      ε≤[xy][yʳxʳ] : ε ≤ (y ʳ ∙ x ʳ) ∙ (x ∙ y)
      ε≤[xy][yʳxʳ] =
        begin
          ε                     ≤⟨ ʳ-expand y ⟩
          y ʳ ∙ y               ≈⟨ ∙-cong refl (sym (proj₁ identity y)) ⟩
          y ʳ ∙ (ε ∙ y)         ≤⟨ compatibility ≤-refl (compatibility (ʳ-expand x) ≤-refl) ⟩
          y ʳ ∙ ((x ʳ ∙ x) ∙ y) ≈⟨ ∙-cong refl (assoc (x ʳ) x y) ⟩
          y ʳ ∙ (x ʳ ∙ (x ∙ y)) ≈⟨ sym (assoc (y ʳ) (x ʳ) (x ∙ y)) ⟩
          (y ʳ ∙ x ʳ) ∙ (x ∙ y)
        ∎

  ˡ-contra : ∀ {x y} → x ≤ y → y ˡ ≤ x ˡ
  ˡ-contra {x} {y} x≤y =
    begin
      y ˡ             ≈⟨ sym (proj₂ identity (y ˡ)) ⟩
      y ˡ ∙ ε         ≤⟨ compatibility ≤-refl (ˡ-expand x) ⟩
      y ˡ ∙ (x ∙ x ˡ) ≈⟨ sym (assoc (y ˡ) x (x ˡ)) ⟩
      (y ˡ ∙ x) ∙ x ˡ ≤⟨ substitutivity x≤y ⟩
      (y ˡ ∙ y) ∙ x ˡ ≤⟨ compatibility (ˡ-contract y) ≤-refl ⟩
      ε ∙ x ˡ         ≈⟨ proj₁ identity (x ˡ) ⟩
      x ˡ
    ∎

  ʳ-contra : ∀ {x y} → x ≤ y → y ʳ ≤ x ʳ
  ʳ-contra {x} {y} x≤y =
    begin
      y ʳ             ≈⟨ sym (proj₁ identity (y ʳ)) ⟩
      ε ∙ y ʳ         ≤⟨ compatibility (ʳ-expand x) ≤-refl ⟩
      (x ʳ ∙ x) ∙ y ʳ ≤⟨ substitutivity x≤y ⟩
      (x ʳ ∙ y) ∙ y ʳ ≈⟨ assoc (x ʳ) y (y ʳ) ⟩
      x ʳ ∙ (y ∙ y ʳ) ≤⟨ compatibility ≤-refl (ʳ-contract y) ⟩
      x ʳ ∙ ε         ≈⟨ proj₂ identity (x ʳ) ⟩
      x ʳ
    ∎

  ʳˡ-cancel : ∀ x → x ʳ ˡ ≈ x
  ʳˡ-cancel x = sym (ˡ-unique (ʳ-contract x , ʳ-expand x))

  ˡʳ-cancel : ∀ x → x ˡ ʳ ≈ x
  ˡʳ-cancel x = sym (ʳ-unique (ˡ-contract x , ˡ-expand x))


module Section1 where

-- 1. Introduction
-- ===============
--
-- 1.1. Outline of the formalisation
-- ---------------------------------
--
-- -   We formalize terms-in-context, `Γ ⊢ t ∷ A`, as a calculus of proof trees, `Γ ⊢ A`, for
--     implicational logic, i.e., term `t` is seen as a proof tree that derives `A` from the assumptions
--     contained in `Γ`.  A notion of equivalence of terms-in-context (including, e.g., β-reduction
--     and η-conversion) is then defined with a conversion relation `_≅_` on these proof trees.
--
-- -   We give a Kripke model with an ordered set of worlds and define when a proposition `A` is
--     true for a world `w` in the model, `w ⊩ A`.  We also define an extensional equality between
--     objects in the model.
--
-- -   We define an interpreter `⟦_⟧` that maps a proof tree in the theory to its semantics in the
--     Kripke model.  According to the Curry-Howard correspondence of programs to proofs
--     and types to propositions, the type of the interpreter expresses soundness of the theory of
--     proof trees with respect to the Kripke model.  Consequently, the interpreter can be seen
--     as a constructive soundness proof.
--
-- -   We also define an inversion function, `reify`, which returns a proof tre corresponding to
--     a given semantic object, yielding—again by the Curry-Howard isomorphism—a
--     completeness proof.
--
--     The inversion function is based on the principle of normalization by evaluation [3].
--     Obviously, for proof trees `M` and `N` with the same semantics, inversion yields the same proof
--     tree `reify ⟦ M ⟧ = reify ⟦ N ⟧`.  Consequently, we define a normalization function, `nf`, as
--     the composition of the interpreter and the inversion function.  We prove that if `M` is a proof
--     tree, then `M ≅ nf M`; together with the soundness result, this yields a decision algorithm
--     for convertibility.  We further show that `nf` returns a proof term in long η-normal form.
--
-- -   We prove that the convertibility relation on proof trees is sound and complete.  The proof
--     of soundness, i.e., convertible proof trees have the same semantics, is straightforward by
--     induction on the structure of the trees.  The completeness proof, i.e., if `⟦ M ⟧` and `⟦ N ⟧` are
--     equal, then `M` and `N` are convertible, uses the fact that `reify ⟦ M ⟧` is exactly the same as
--     `reify ⟦ N ⟧`, and since `M ≅ nf M ≡ reify ⟦ M ⟧` and vice versa for `N`, we get that `M ≅ N`.
--
-- -   We define a calculus of simply typed ƛterms, a typed convertibility relation on the terms,
--     and a deterministic reduction, `_⇓_`.  We also define an erasure function on proof trees that
--     maps a proof tree `M` into a well-typed term `M ⁻`.  For every well-typed term there is at
--     least one proof tree that erases to this term; for defining the semantics of well-typed terms
--     through that of proof trees, it suffices to show that all proof trees that erase to the same
--     well-typed term have the same semantics.
--
-- -   Because we know that convertible proof trees have the same semantics, it remains to
--     show that all proof trees that erase to a given well-typed term are convertible.  We use an
--     argument due to Streicher [19]: we first prove that if `nf M ⁻` and `nf N ⁻` are the same,
--     then `M ≅ N`.  Secondly, we prove that if a proof tree `M` erases to a well-typed term `t`,
--     then `t ⇓ nf M ⁻`.  Now, if two proof trees `M` and `N` erase to the same well-typed term
--     `t`, then `t ⇓ nf M ⁻` and `t ⇓ nf N ⁻`.  Since the reduction is deterministic we have that
--     `nf M ⁻` and `nf N ⁻` are the same, and hence `M ≅ N`.
--
-- -   We prove that the convertibility relation on proof trees is sound and complete, and give
--     a decision algorithm for checking convertibility of two well-typed terms.
--
-- (…)

open import Agda.Builtin.FromString public
  using (IsString ; fromString)

open import Data.Bool public
  using (Bool ; T ; true ; false ; not)
  renaming (_∧_ to and)

open import Data.Empty public
  using (⊥)
  renaming (⊥-elim to elim⊥)

open import Data.Product public
  using (Σ ; _,_ ; _×_ ; proj₁ ; proj₂)

open import Data.Unit public
  using (⊤ ; tt)

import Data.String as Str
open Str
  using (String)

open import Function public
  using (_∘_ ; case_of_ ; flip ; id)

open import Relation.Binary.PropositionalEquality public
  using (_≡_ ; _≢_ ; refl ; cong ; subst ; sym ; trans)
  renaming (cong₂ to cong²)

open import Relation.Nullary public
  using (¬_ ; Dec ; yes ; no)

import Relation.Nullary.Decidable as Decidable
open Decidable using (⌊_⌋) public

open import Relation.Nullary.Negation public
  renaming (contradiction to _↯_)

module _ where
  data Name : Set where
    name : String → Name

  module _ where
    inj-name : ∀ {s s′} → name s ≡ name s′ → s ≡ s′
    inj-name refl = refl

    _≟_ : (x x′ : Name) → Dec (x ≡ x′)
    name s ≟ name s′ = Decidable.map′ (cong name) inj-name (s Str.≟ s′)

    _≠_ : Name → Name → Bool
    x ≠ x′ = not ⌊ x ≟ x′ ⌋

  instance
    str-Name : IsString Name
    str-Name = record
      { Constraint = λ s → ⊤
      ; fromString = λ s → name s
      }

module _ where
  record Raiseable
      {World : Set}
      (_⊩◌  : World → Set)
      {_⊒_   : World → World → Set} : Set₁ where
    field
      ↑⟨_⟩  : ∀ {w w′} → w′ ⊒ w → w ⊩◌ → w′ ⊩◌
  open Raiseable {{…}} public

  record Lowerable
      {World : Set}
      (◌⊩_  : World → Set)
      {_⊒_   : World → World → Set} : Set₁ where
    field
      ↓⟨_⟩  : ∀ {w w′} → w′ ⊒ w → ◌⊩ w′ → ◌⊩ w
  open Lowerable {{…}} public


------------------------------------------------------------------------------
-- First-order logic theorems
------------------------------------------------------------------------------

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

module FOL.TheoremsI where

-- The theorems below are valid on intuitionistic logic and with an
-- empty domain.
open import FOL.Base hiding ( D≢∅ ; pem )

------------------------------------------------------------------------------
-- We postulate some formulae and propositional functions.
postulate
  A     : Set
  A¹ B¹ : D → Set
  A²    : D → D → Set

-- The introduction and elimination rules for the quantifiers are theorems.
{-
      φ(x)
  -----------  ∀-intro
    ∀x.φ(x)

    ∀x.φ(x)
  -----------  ∀-elim
     φ(t)

      φ(t)
  -----------  ∃-intro
    ∃x.φ(x)

   ∃x.φ(x)   φ(x) → ψ
 ----------------------  ∃-elim
           ψ
-}

∀-intro : ((x : D) → A¹ x) → ∀ x → A¹ x
∀-intro h = h

∀-intro' : ((x : D) → A¹ x) → ⋀ A¹
∀-intro' = dfun

∀-elim : (t : D) → (∀ x → A¹ x) → A¹ t
∀-elim t h = h t

∀-elim' : (t : D) → ⋀ A¹ → A¹ t
∀-elim' = dapp

∃-intro : (t : D) → A¹ t → ∃ A¹
∃-intro t A¹x = t , A¹x

∃-elim : ∃ A¹ → ((x : D) → A¹ x → A) → A
∃-elim (x , A¹x) h = h x A¹x

-- Generalization of De Morgan's laws.
gDM₂ : ¬ (∃ A¹) ↔ (∀ {x} → ¬ (A¹ x))
gDM₂ = l→r , r→l
  where
  l→r : ¬ (∃ A¹) → ∀ {x} → ¬ (A¹ x)
  l→r h A¹x = h (_ , A¹x)

  r→l : (∀ {x} → ¬ (A¹ x)) → ¬ (∃ A¹)
  r→l h (x , A¹x) = h A¹x

gDM₂' : ¬ (∃ A¹) ⇔ (⋀[ x ] ¬ (A¹ x))
gDM₂' = fun l→r , fun r→l
  where
  l→r : ¬ (∃ A¹) → ⋀[ x ] ¬ A¹ x
  l→r h = dfun (λ d A¹d → h (d , A¹d))

  r→l : ⋀[ x ] ¬ A¹ x → ¬ ∃ A¹
  r→l (dfun f) (x , A¹x) = f x A¹x

-- Quantification over a variable that does not occur can be erased or
-- added.
∃-erase-add : (∃[ x ] A ∧ A¹ x) ↔ A ∧ (∃[ x ] A¹ x)
∃-erase-add = l→r , r→l
  where
  l→r : ∃[ x ] A ∧ A¹ x → A ∧ (∃[ x ] A¹ x)
  l→r (x , a , A¹x) = a , x , A¹x

  r→l : A ∧ (∃[ x ] A¹ x) → ∃[ x ] A ∧ A¹ x
  r→l (a , x , A¹x) = x , a , A¹x

-- Interchange of quantifiers.
-- The related theorem ∀x∃y.Axy → ∃y∀x.Axy is not (classically) valid.
∃∀ : ∃[ x ] (∀ y → A² x y) → ∀ y → ∃[ x ] A² x y
∃∀ (x , h) y = x , h y

-- ∃ in terms of ∀ and ¬.
∃→¬∀¬ : ∃[ x ] A¹ x → ¬ (∀ {x} → ¬ A¹ x)
∃→¬∀¬ (_ , A¹x) h = h A¹x

∃¬→¬∀ : ∃[ x ] ¬ A¹ x → ¬ (∀ {x} → A¹ x)
∃¬→¬∀ (_ , h₁) h₂ = h₁ h₂

-- ∀ in terms of ∃ and ¬.
∀→¬∃¬ : (∀ {x} → A¹ x) → ¬ (∃[ x ] ¬ A¹ x)
∀→¬∃¬ h₁ (_ ,  h₂) = h₂ h₁

∀¬→¬∃ : (∀ {x} → ¬ A¹ x) → ¬ (∃[ x ] A¹ x)
∀¬→¬∃ h₁ (_ , h₂) = h₁ h₂

-- Distribution of ∃ and ∨.
∃∨ : ∃[ x ](A¹ x ∨ B¹ x) → (∃[ x ] A¹ x) ∨ (∃[ x ] B¹ x)
∃∨ (x , inj₁ A¹x) = inj₁ (x , A¹x)
∃∨ (x , inj₂ B¹x) = inj₂ (x , B¹x)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Indexed binary relations
------------------------------------------------------------------------

-- The contents of this module should be accessed via
-- `Relation.Binary.Indexed.Heterogeneous`.

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

open import Relation.Binary.Indexed.Heterogeneous.Core

module Relation.Binary.Indexed.Heterogeneous.Structures
  {i a ℓ} {I : Set i} (A : I → Set a) (_≈_ : IRel A ℓ)
  where

open import Function.Base
open import Level using (suc; _⊔_)
open import Relation.Binary using (_⇒_)
open import Relation.Binary.PropositionalEquality.Core as P using (_≡_)
open import Relation.Binary.Indexed.Heterogeneous.Definitions

------------------------------------------------------------------------
-- Equivalences

record IsIndexedEquivalence : Set (i ⊔ a ⊔ ℓ) where
  field
    refl  : Reflexive  A _≈_
    sym   : Symmetric  A _≈_
    trans : Transitive A _≈_

  reflexive : ∀ {i} → _≡_ ⟨ _⇒_ ⟩ _≈_ {i}
  reflexive P.refl = refl

record IsIndexedPreorder {ℓ₂} (_∼_ : IRel A ℓ₂) : Set (i ⊔ a ⊔ ℓ ⊔ ℓ₂) where
  field
    isEquivalence : IsIndexedEquivalence
    reflexive     : ∀ {i j} → (_≈_ {i} {j}) ⟨ _⇒_ ⟩ _∼_
    trans         : Transitive A _∼_

  module Eq = IsIndexedEquivalence isEquivalence

  refl : Reflexive A _∼_
  refl = reflexive Eq.refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Integers, basic types and operations
------------------------------------------------------------------------

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

module Data.Integer.Base where

open import Data.Nat.Base as ℕ
  using (ℕ) renaming (_+_ to _ℕ+_; _*_ to _ℕ*_)
open import Data.Sign as Sign using (Sign) renaming (_*_ to _S*_)
open import Function
open import Relation.Nullary using (¬_)
open import Relation.Binary using (Rel)
open import Relation.Binary.PropositionalEquality using (_≡_)

infix  8 -_
infixl 7 _*_ _⊓_
infixl 6 _+_ _-_ _⊖_ _⊔_
infix  4 _≤_ _≥_ _<_ _>_ _≰_ _≱_ _≮_ _≯_

------------------------------------------------------------------------
-- The types

open import Agda.Builtin.Int public
  using ()
  renaming
  ( Int to ℤ
  ; pos    to +_      -- "+ n"      stands for "n"
  ; negsuc to -[1+_]  -- "-[1+ n ]" stands for "- (1 + n)"
  )

------------------------------------------------------------------------
-- Conversions

-- Absolute value.

∣_∣ : ℤ → ℕ
∣ + n      ∣ = n
∣ -[1+ n ] ∣ = ℕ.suc n

-- Gives the sign. For zero the sign is arbitrarily chosen to be +.

sign : ℤ → Sign
sign (+ _)    = Sign.+
sign -[1+ _ ] = Sign.-

------------------------------------------------------------------------
-- A view of integers as sign + absolute value

infix 5 _◂_ _◃_

_◃_ : Sign → ℕ → ℤ
_      ◃ ℕ.zero  = + ℕ.zero
Sign.+ ◃ n       = + n
Sign.- ◃ ℕ.suc n = -[1+ n ]

data SignAbs : ℤ → Set where
  _◂_ : (s : Sign) (n : ℕ) → SignAbs (s ◃ n)

signAbs : ∀ i → SignAbs i
signAbs (+ ℕ.zero)    = Sign.+ ◂ ℕ.zero
signAbs (+ (ℕ.suc n)) = Sign.+ ◂ ℕ.suc n
signAbs (-[1+ n ])    = Sign.- ◂ ℕ.suc n

------------------------------------------------------------------------
-- Arithmetic

-- Negation.

-_ : ℤ → ℤ
- (+ ℕ.suc n) = -[1+ n ]
- (+ ℕ.zero)  = + ℕ.zero
- -[1+ n ]    = + ℕ.suc n

-- Subtraction of natural numbers.

_⊖_ : ℕ → ℕ → ℤ
m       ⊖ ℕ.zero  = + m
ℕ.zero  ⊖ ℕ.suc n = -[1+ n ]
ℕ.suc m ⊖ ℕ.suc n = m ⊖ n

-- Addition.

_+_ : ℤ → ℤ → ℤ
-[1+ m ] + -[1+ n ] = -[1+ ℕ.suc (m ℕ+ n) ]
-[1+ m ] + +    n   = n ⊖ ℕ.suc m
+    m   + -[1+ n ] = m ⊖ ℕ.suc n
+    m   + +    n   = + (m ℕ+ n)

-- Subtraction.

_-_ : ℤ → ℤ → ℤ
i - j = i + (- j)

-- Successor.

suc : ℤ → ℤ
suc i = (+ 1) + i

-- Predecessor.

pred : ℤ → ℤ
pred i = (- + 1) + i

-- Multiplication.

_*_ : ℤ → ℤ → ℤ
i * j = sign i S* sign j ◃ ∣ i ∣ ℕ* ∣ j ∣

-- Maximum.

_⊔_ : ℤ → ℤ → ℤ
-[1+ m ] ⊔ -[1+ n ] = -[1+ ℕ._⊓_ m n ]
-[1+ m ] ⊔ +    n   = + n
+    m   ⊔ -[1+ n ] = + m
+    m   ⊔ +    n   = + (ℕ._⊔_ m n)

-- Minimum.

_⊓_ : ℤ → ℤ → ℤ
-[1+ m ] ⊓ -[1+ n ] = -[1+ ℕ._⊔_ m n ]
-[1+ m ] ⊓ +    n   = -[1+ m ]
+    m   ⊓ -[1+ n ] = -[1+ n ]
+    m   ⊓ +    n   = + (ℕ._⊓_ m n)

------------------------------------------------------------------------
-- Ordering

data _≤_ : ℤ → ℤ → Set where
  -≤+ : ∀ {m n} → -[1+ m ] ≤ + n
  -≤- : ∀ {m n} → (n≤m : n ℕ.≤ m) → -[1+ m ] ≤ -[1+ n ]
  +≤+ : ∀ {m n} → (m≤n : m ℕ.≤ n) → + m ≤ + n

_≥_ : Rel ℤ _
x ≥ y = y ≤ x

_<_ : Rel ℤ _
x < y = suc x ≤ y

_>_ : Rel ℤ _
x > y = y < x

_≰_ : Rel ℤ _
x ≰ y = ¬ (x ≤ y)

_≱_ : Rel ℤ _
x ≱ y = ¬ (x ≥ y)

_≮_ : Rel ℤ _
x ≮ y = ¬ (x < y)

_≯_ : Rel ℤ _
x ≯ y = ¬ (x > y)


module Logic.Predicate.Theorems where

import      Lvl
open import Functional
open import Logic
open import Logic.Propositional
open import Logic.Propositional.Theorems
open import Logic.Predicate
open import Syntax.Type
open import Type
open import Type.Properties.Inhabited using (◊ ; [◊]-existence)

------------------------------------------
-- Swapping nested quantifiers

module _ {ℓₒ₁}{ℓₒ₂}{ℓₗ} {X : Type{ℓₒ₁}}{Y : Type{ℓₒ₂}}{P : X → Y → Stmt{ℓₗ}} where
  [∀]-swap : ∀ₗ(x ↦ ∀ₗ(y ↦ P(x)(y))) → ∀ₗ(y ↦ ∀ₗ(x ↦ P(x)(y)))
  [∀]-swap(xypxy){y}{x} = xypxy{x}{y}

  [∃]-swap : ∃(x ↦ ∃(y ↦ P(x)(y))) → ∃(y ↦ ∃(x ↦ P(x)(y)))
  [∃]-swap([∃]-intro(x) ⦃ [∃]-intro(y) ⦃ proof ⦄ ⦄) = [∃]-intro(y) ⦃ [∃]-intro(x) ⦃ proof ⦄ ⦄

------------------------------------------
-- Introducing and eliminating unnecessary quantifiers when the predicate is constant

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} ⦃ pos : ◊ X ⦄ {P : Stmt{ℓₗ}} where
  [∃]-unnecessary-intro : P → ∃{Obj = X}(x ↦ P)
  [∃]-unnecessary-intro(p) = [∃]-intro([◊]-existence ⦃ pos ⦄) ⦃ p ⦄

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} {P : Stmt{ℓₗ}} where
  [∀]-unnecessary-intro : P → ∀ₗ{Obj = X}(x ↦ P)
  [∀]-unnecessary-intro(p){x} = p

  [∃]-unnecessary-elim : ∃{Obj = X}(x ↦ P) → P
  [∃]-unnecessary-elim([∃]-intro(_) ⦃ proof ⦄) = proof

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} ⦃ pos : ◊ X ⦄ {P : Stmt{ℓₗ}} where
  -- Note:
  --   The following is unprovable:
  --   [∀]-unnecessary : ∀{X}{P : Stmt} → ∀ₗ{X}(x ↦ P) → P
  --   X is not guaranteed to not be the empty type, and even if it was, the ∀ function requires a constructed value. It seems to need a non-empty domain to quantify over.
  [∀ₑ]-unnecessary-elim : ∀ₗ{Obj = X}(x ↦ P) → P
  [∀ₑ]-unnecessary-elim = [∀ₑ]-elim{Obj = X} ⦃ pos ⦄

------------------------------------------
-- When quantifiers contradict

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} {P : X → Stmt{ℓₗ}} where
  [∀][∃¬]-contradiction : ∀ₗ(x ↦ P(x)) → ∃(x ↦ ¬(P(x))) → ⊥
  [∀][∃¬]-contradiction(ap)(enp) =
    ([∃]-elim(\{a} → np ↦ (
      ([⊥]-intro
        ([∀]-elim{Obj = X}{P} ap(a))
        np
      )
    )) (enp))

  [∀¬][∃]-contradiction : ∀ₗ(x ↦ ¬ P(x)) → ∃(x ↦ P(x)) → ⊥
  [∀¬][∃]-contradiction (axnpx) (expx) =
    axnpx {[∃]-witness(expx)} ([∃]-proof(expx))

------------------------------------------
-- Moving in and out negation from quantifiers

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} {P : X → Stmt{ℓₗ}} where
  [∃¬]-to-[¬∀] : (∃(x ↦ ¬(P(x)))) → ¬(∀ₗ(x ↦ P(x)))
  [∃¬]-to-[¬∀] = swap ([∀][∃¬]-contradiction {ℓₒ}{ℓₗ})

  [∀¬]-to-[¬∃] : ∀ₗ(x ↦ ¬(P(x))) → ¬(∃(x ↦ P(x)))
  [∀¬]-to-[¬∃] = [∀¬][∃]-contradiction {ℓₒ}{ℓₗ} {X}{P}
    -- [∀¬]-to-[¬∃] (anpx) =
    --   ([¬]-intro(epx ↦
    --     [∃]-elim(\{a} → pa ↦
    --       ([⊥]-intro
    --         pa
    --         (anpx{a})
    --       )
    --     )(epx)
    --   ))

  [¬∃]-to-[∀¬] : ∀ₗ(x ↦ ¬(P(x))) ← ¬(∃(x ↦ P(x)))
  [¬∃]-to-[∀¬] (nepx) =
    ([∀]-intro(a ↦
      (([¬]-intro(pa ↦
        (([⊥]-intro
          (([∃]-intro a ⦃ pa ⦄) :of: (∃(x ↦ P(x))))
          (nepx :of: ¬(∃(x ↦ P(x))))
        ) :of: ⊥)
      )) :of: ¬(P(a)))
    ))

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} ⦃ pos : ◊ X ⦄ {P : X → Stmt{ℓₗ}} where
  [¬∃]-to-[∃¬] : ¬(∃ P) → ∃(¬_ ∘ P)
  [¬∃]-to-[∃¬] (nexpx) = [∃]-intro ([◊]-existence ⦃ pos ⦄) ⦃ [¬∃]-to-[∀¬] {ℓₒ}{ℓₗ} {X}{P} (nexpx) ⦄

  [¬∃]-to-[¬∀] : ¬(∃ P) → ¬(∀ₗ P)
  [¬∃]-to-[¬∀] = ([∃¬]-to-[¬∀] {ℓₒ}{ℓₗ} {X}{P}) ∘ [¬∃]-to-[∃¬]

  [∀¬]-to-[¬∀] : ∀ₗ(¬_ ∘ P) → ¬(∀ₗ P)
  [∀¬]-to-[¬∀] axnpx axpx = (axnpx{[◊]-existence ⦃ pos ⦄}) (axpx{[◊]-existence ⦃ pos ⦄})

------------------------------------------
-- Introducing negations to change quantifier

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} {P : X → Stmt{ℓₗ}} where
  [∀]-to-[¬∃¬] : ∀ₗ(x ↦ P(x)) → (¬ ∃(x ↦ ¬(P(x))))
  [∀]-to-[¬∃¬] = [∀][∃¬]-contradiction {ℓₒ}{ℓₗ} {X}{P} -- [∃]-elim(\{a} → npa ↦ npa(p{a}))(ep)

  [∃]-to-[¬∀¬] : ∃(x ↦ P(x)) → (¬ ∀ₗ(x ↦ ¬(P(x))))
  [∃]-to-[¬∀¬] = swap ([∀¬][∃]-contradiction {ℓₒ}{ℓₗ} {X}{P})

------------------------------------------
-- Stuff using double negation

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} {P : X → Stmt{ℓₗ}} where
  [∀¬¬][∃¬]-contradiction : ∀ₗ(x ↦ ¬¬(P(x))) → (∃(x ↦ ¬(P(x)))) → ⊥
  [∀¬¬][∃¬]-contradiction (annp)([∃]-intro(a) ⦃ na ⦄) =
    [∀][∃¬]-contradiction {ℓₒ}{ℓₗ} {X}{¬¬_ ∘ P} (annp)([∃]-intro(a) ⦃ [¬¬]-intro(na) ⦄)

  [∀¬¬]-to-[¬∃¬] : ∀ₗ(x ↦ ¬¬(P(x))) → (¬ ∃(x ↦ ¬(P(x))))
  [∀¬¬]-to-[¬∃¬] = [∀¬¬][∃¬]-contradiction

  [¬¬∀]-to-[¬∃¬] : ¬¬ ∀ₗ(x ↦ (P(x))) → (¬ ∃(x ↦ ¬(P(x))))
  [¬¬∀]-to-[¬∃¬] = contrapositiveᵣ ([∃¬]-to-[¬∀] {ℓₒ}{ℓₗ} {X}{P})
  -- [¬¬∀]-to-[¬∃¬] (annx) (enx) =
  --   ([⊥]-intro
  --     ([∃¬]-to-[¬∀](enx))
  --     (annx)
  --   )

  [¬∃¬]-to-[∀¬¬] : ¬(∃(x ↦ ¬(P(x)))) → ∀ₗ(x ↦ ¬¬(P(x)))
  [¬∃¬]-to-[∀¬¬] (nenx) {a} (npa) = nenx([∃]-intro(a) ⦃ npa ⦄)

  [¬¬∀]-to-[∀¬¬] : ¬¬ ∀ₗ(x ↦ (P(x))) → ∀ₗ(x ↦ ¬¬(P(x)))
  [¬¬∀]-to-[∀¬¬] = [¬∃¬]-to-[∀¬¬] ∘ [¬¬∀]-to-[¬∃¬]

  [∀]-to-[∃]-conditional-by-existence : ∃(P) → ∀{Q : X → Stmt{ℓₗ}} → ∀ₗ(x ↦ (P(x) → Q(x))) → ∃(x ↦ (P(x) ∧ Q(x)))
  ∃.witness ([∀]-to-[∃]-conditional-by-existence p pq) = [∃]-witness p
  ∃.proof   ([∀]-to-[∃]-conditional-by-existence p pq) = [∧]-intro ([∃]-proof p) (pq{[∃]-witness p} ([∃]-proof p))

  -- TODO: Probably unprovable. Not sure why. Maybe because (¬¬A is valid in constructive logic) ⇔ (A is valid in classical logic), and therefore this would not be possible because everything here is in constructive logic.
  -- [∀¬¬]-to-[¬¬∀] : ∀{X}{P : X → Stmt} → ¬¬∀ₗ(x ↦ (P(x))) ← ∀ₗ(x ↦ ¬¬(P(x)))

------------------------------------------
-- Changing quantifier

module _ {ℓₒ}{ℓₗ} {X : Type{ℓₒ}} ⦃ pos : ◊ X ⦄ {P : X → Stmt{ℓₗ}} where
  -- Note: If X would be empty, then this would be unprovable because [∀]-elim needs a constructed element.
  [∀ₑ]-to-[∃] : ∀ₗ(x ↦ P(x)) → ∃(x ↦ P(x))
  [∀ₑ]-to-[∃] (apx) =
    [∃]-intro ([◊]-existence ⦃ pos ⦄) ⦃ [∀ₑ]-elim{Obj = X}{P = P}(apx) ⦄

------------------------------------------
-- "Distributing" quantifiers into logical operators

module _ {ℓₒ}{ℓₗ₁}{ℓₗ₂} {X : Type{ℓₒ}}{P : X → Stmt{ℓₗ₁}}{Q : X → Stmt{ℓₗ₂}} where
  [∀][→]-distributivity : ∀ₗ(x ↦ (P(x) → Q(x))) → ∀ₗ(x ↦ P(x)) → ∀ₗ(x ↦ Q(x))
  [∀][→]-distributivity (PxQx) (Px) = PxQx(Px)

  [∀][∧]-distributivity : ∀ₗ(x ↦ (P(x) ∧ Q(x))) ↔ (∀ₗ(x ↦ P(x)) ∧ ∀ₗ(x ↦ Q(x)))
  [∀][∧]-distributivity = [↔]-intro l r where
    l : (∀ₗ(x ↦ P(x)) ∧ ∀ₗ(x ↦ Q(x))) → ∀ₗ(x ↦ (P(x) ∧ Q(x)))
    l ([∧]-intro (axPx) (axQx)) {x} = [∧]-intro (axPx{x}) (axQx{x})

    r : (∀ₗ(x ↦ P(x)) ∧ ∀ₗ(x ↦ Q(x))) ← ∀ₗ(x ↦ (P(x) ∧ Q(x)))
    r(apxqx) = [∧]-intro (\{x} → [∧]-elimₗ(apxqx{x})) (\{x} → [∧]-elimᵣ(apxqx{x}))

  [∃][∨]-distributivity : ∃(x ↦ (P(x) ∨ Q(x))) ↔ (∃(x ↦ P(x)) ∨ ∃(x ↦ Q(x)))
  [∃][∨]-distributivity = [↔]-intro l r where
    l : (∃(x ↦ P(x)) ∨ ∃(x ↦ Q(x))) → ∃(x ↦ (P(x) ∨ Q(x)))
    l ([∨]-introₗ ([∃]-intro x ⦃ px ⦄)) = [∃]-intro x ⦃ [∨]-introₗ px ⦄
    l ([∨]-introᵣ ([∃]-intro x ⦃ qx ⦄)) = [∃]-intro x ⦃ [∨]-introᵣ qx ⦄

    r : (∃(x ↦ P(x)) ∨ ∃(x ↦ Q(x))) ← ∃(x ↦ (P(x) ∨ Q(x)))
    r ([∃]-intro x ⦃ [∨]-introₗ px ⦄) = [∨]-introₗ ([∃]-intro x ⦃ px ⦄)
    r ([∃]-intro x ⦃ [∨]-introᵣ qx ⦄) = [∨]-introᵣ ([∃]-intro x ⦃ qx ⦄)

  [∃][∧]-semidistributivity : ∃(x ↦ ∃(y ↦ (P(x) ∧ Q(y)))) ↔ (∃(x ↦ P(x)) ∧ ∃(x ↦ Q(x)))
  [∃][∧]-semidistributivity = [↔]-intro l r where
    l : ∃(x ↦ ∃(y ↦ (P(x) ∧ Q(y)))) ← (∃(x ↦ P(x)) ∧ ∃(x ↦ Q(x)))
    l ([∧]-intro ([∃]-intro x ⦃ px ⦄) ([∃]-intro y ⦃ qy ⦄)) = [∃]-intro x ⦃ [∃]-intro y ⦃ [∧]-intro px qy ⦄ ⦄

    r : ∃(x ↦ ∃(y ↦ (P(x) ∧ Q(y)))) → (∃(x ↦ P(x)) ∧ ∃(x ↦ Q(x)))
    r ([∃]-intro x ⦃ [∃]-intro y ⦃ [∧]-intro px qy ⦄ ⦄) = [∧]-intro ([∃]-intro x ⦃ px ⦄) ([∃]-intro y ⦃ qy ⦄)

  [∃][∧]-distributivity : ∃(x ↦ (P(x) ∧ Q(x))) → (∃(x ↦ P(x)) ∧ ∃(x ↦ Q(x)))
  [∃][∧]-distributivity ([∃]-intro x ⦃ [∧]-intro px qx ⦄ ) = [∧]-intro l r where
    l = [∃]-intro x ⦃ px ⦄
    r = [∃]-intro x ⦃ qx ⦄

module _ {ℓₒ}{ℓₗ₁}{ℓₗ₂} {X : Type{ℓₒ}}{P : X → Stmt{ℓₗ₁}}{Q : X → Stmt{ℓₗ₂}} where
  [∀][↔]-distributivity : ∀ₗ(x ↦ (P(x) ↔ Q(x))) → (∀ₗ(x ↦ P(x)) ↔ ∀ₗ(x ↦ Q(x)))
  [∀][↔]-distributivity = r where -- [↔]-intro l r where
    -- l : ∀ₗ(x ↦ (P(x) ↔ Q(x))) ← (∀ₗ(x ↦ P(x)) ↔ ∀ₗ(x ↦ Q(x)))
    -- l([↔]-intro aqxapx apxaqx) {x} = [↔]-intro (\qx → aqxpx(qx{x})) (apxqx{x})

    r : ∀ₗ(x ↦ (P(x) ↔ Q(x))) → (∀ₗ(x ↦ P(x)) ↔ ∀ₗ(x ↦ Q(x)))
    r apxqx = [↔]-intro ([∀][→]-distributivity rl) ([∀][→]-distributivity rr) where
      rl : ∀ₗ(x ↦ (P(x) ← Q(x)))
      rl{x} = [↔]-to-[←](apxqx{x})

      rr : ∀ₗ(x ↦ (P(x) → Q(x)))
      rr{x} = [↔]-to-[→](apxqx{x})

  [∀]-to-[∃]-[↔]-semidistributivity : ∀ₗ(x ↦ (P(x) ↔ Q(x))) → (∃(x ↦ P(x)) ↔ ∃(x ↦ Q(x)))
  [∀]-to-[∃]-[↔]-semidistributivity apq = [↔]-intro ([∃]-map-proof ([↔]-to-[←] apq)) ([∃]-map-proof ([↔]-to-[→] apq))

------------------------------------------
-- Quantifiers with logical operators inside, but one of the predicates are constant

module _ {ℓₒ}{ℓₗ₁}{ℓₗ₂} {X : Type{ℓₒ}}{P : X → Stmt{ℓₗ₁}}{Q : Stmt{ℓₗ₂}} where
  [∀]-unrelatedₗ-[→] : ∀ₗ(x ↦ (P(x) → Q)) ↔ (∃(x ↦ P(x)) → Q)
  [∀]-unrelatedₗ-[→] = [↔]-intro l r where
    l : ∀ₗ(x ↦ (P(x) → Q)) ← (∃(x ↦ P(x)) → Q)
    l(expxq) {x} px = expxq([∃]-intro(x) ⦃ px ⦄)

    r : ∀ₗ(x ↦ (P(x) → Q)) → (∃(x ↦ P(x)) → Q)
    r(axpxq) = [∃]-elim(\{x} → px ↦ axpxq{x}(px))

  [∃]-unrelatedₗ-[→]ᵣ : ∃(x ↦ (P(x) → Q)) → (∀ₗ(x ↦ P(x)) → Q)
  [∃]-unrelatedₗ-[→]ᵣ = r where -- [↔]-intro l r where
    r : ∃(x ↦ (P(x) → Q)) → (∀ₗ(x ↦ P(x)) → Q)
    r(expxq)(axpx) = [∃]-proof(expxq) (axpx{[∃]-witness(expxq)})

  [∃]-unrelatedₗ-[∧] : ∃(x ↦ (P(x) ∧ Q)) ↔ (∃(x ↦ P(x)) ∧ Q)
  [∃]-unrelatedₗ-[∧] = [↔]-intro l r where
    l : ∃(x ↦ (P(x) ∧ Q)) ← (∃(x ↦ P(x)) ∧ Q)
    l ([∧]-intro ([∃]-intro x ⦃ px ⦄) q) = [∃]-intro x ⦃ [∧]-intro px q ⦄

    r : ∃(x ↦ (P(x) ∧ Q)) → (∃(x ↦ P(x)) ∧ Q)
    r ([∃]-intro x ⦃ [∧]-intro px q ⦄) = [∧]-intro ([∃]-intro x ⦃ px ⦄) q

module _ {ℓₒ}{ℓₗ₁}{ℓₗ₂} {X : Type{ℓₒ}}{P : Stmt{ℓₗ₁}}{Q : X → Stmt{ℓₗ₂}} where
  [∀]-unrelatedᵣ-[→] : ∀ₗ(x ↦ (P → Q(x))) ↔ (P → ∀ₗ(x ↦ Q(x)))
  [∀]-unrelatedᵣ-[→] = [↔]-intro l r where
    l : ∀ₗ(x ↦ (P → Q(x))) ← (P → ∀ₗ(x ↦ Q(x)))
    l(paxqx) {x} p = paxqx(p){x}

    r : ∀ₗ(x ↦ (P → Q(x))) → (P → ∀ₗ(x ↦ Q(x)))
    r(axpqx)(p){x} = axpqx{x}(p)

  [∃]-unrelatedᵣ-[→]ᵣ : ∃(x ↦ (P → Q(x))) → (P → ∃(x ↦ Q(x)))
  [∃]-unrelatedᵣ-[→]ᵣ = r where -- [↔]-intro l r where
    r : ∃(x ↦ (P → Q(x))) → (P → ∃(x ↦ Q(x)))
    r(expqx)(p) = [∃]-elim(\{x} → pqx ↦ [∃]-intro(x) ⦃ pqx(p) ⦄) (expqx)

  [∃]-unrelatedᵣ-[∧] : ∃(x ↦ (P ∧ Q(x))) ↔ (P ∧ ∃(x ↦ Q(x)))
  [∃]-unrelatedᵣ-[∧] = [↔]-intro l r where
    l : ∃(x ↦ (P ∧ Q(x))) ← (P ∧ ∃(x ↦ Q(x)))
    l ([∧]-intro p ([∃]-intro x ⦃ qx ⦄)) = [∃]-intro x ⦃ [∧]-intro p qx ⦄

    r : ∃(x ↦ (P ∧ Q(x))) → (P ∧ ∃(x ↦ Q(x)))
    r ([∃]-intro x ⦃ [∧]-intro p qx ⦄) = [∧]-intro p ([∃]-intro x ⦃ qx ⦄)

------------------------------------------
-- Equivalences

module _ {ℓₒ}{ℓₗ₁}{ℓₗ₂} {X : Type{ℓₒ}}{P : X → Stmt{ℓₗ₁}}{Q : X → Stmt{ℓₗ₂}} where
  [∃]-map-proof-[↔] : (∀{x} → P(x) ↔ Q(x)) → ((∃ P) ↔ (∃ Q))
  [∃]-map-proof-[↔] p = [↔]-intro ([∃]-map-proof ([↔]-to-[←] p)) ([∃]-map-proof ([↔]-to-[→] p))

------------------------------------------
-- Other rules

module _ {ℓₒ}{ℓₗ₁}{ℓₗ₂} {X : Type{ℓₒ}}{P : X → Stmt{ℓₗ₁}}{Q : X → Stmt{ℓₗ₂}} where
  [∀][→]-add-[∃] : ∀ₗ(x ↦ (P(x) → Q(x))) → ∀ₗ(x ↦ (P(x) → ∃(x ↦ Q(x))))
  [∀][→]-add-[∃] (proof) {x} px = [∃]-intro(x) ⦃ proof{x} px ⦄

  [∀][→]-to-[∃] : ∀ₗ(x ↦ (P(x) → Q(x))) → (∃(x ↦ P(x)) → ∃(x ↦ Q(x)))
  [∀][→]-to-[∃] (proof) = ([↔]-to-[→] ([∀]-unrelatedₗ-[→] {X = X} {P})) ([∀][→]-add-[∃] (proof))

module _ {ℓₒ₁}{ℓₒ₂}{ℓₗ} {X : Type{ℓₒ₁}}{Y : Type{ℓₒ₂}}{P : X → Y → Stmt{ℓₗ}} where
  [∀][∃]-swap : ∃(x ↦ ∀ₗ(y ↦ P(x)(y))) → ∀ₗ(y ↦ ∃(x ↦ P(x)(y)))
  [∀][∃]-swap ([∃]-intro x ⦃ aypxy ⦄) {y} = [∃]-intro x ⦃ aypxy{y} ⦄

  {-
  no-intermediate : ∀{X : Stmt} → ¬(¬(X ↔ ⊤) ∧ ¬(X ↔ ⊥))
  no-intermediate = [¬][∨] [¬¬]-excluded-middle

  ¬((¬ X) ∧ (¬ Y)) ↔ ¬(¬ (X ∨ Y))
  ∀{X} → ¬¬(X ∨ (¬ X))
  -}

module _ {ℓₒ₁}{ℓₒ₂}{ℓₗ} {X : Type{ℓₒ₁}}{Y : Type{ℓₒ₂}}{P : X → Y → Stmt{ℓₗ}} where
  [∀][∃]-to-function-existence : ∀ₗ(x ↦ ∃(y ↦ P(x)(y))) → ∃(f ↦ ∀ₗ(x ↦ P(x)(f(x))))
  [∀][∃]-to-function-existence p = [∃]-intro (x ↦ [∃]-witness(p{x})) ⦃ \{x} → [∃]-proof(p{x}) ⦄


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

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Setoids.Setoids
open import Rings.Definition
open import Rings.Orders.Partial.Definition
open import Rings.Orders.Total.Definition
open import Groups.Definition
open import Fields.Fields
open import Sets.EquivalenceRelations
open import Sequences
open import Functions.Definition
open import LogicalFormulae
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Setoids.Orders.Partial.Definition
open import Setoids.Orders.Total.Definition

module Fields.CauchyCompletion.Definition {m n o : _} {A : Set m} {S : Setoid {m} {n} A} {_+_ : A → A → A} {_*_ : A → A → A} {_<_ : Rel {m} {o} A} {pOrder : SetoidPartialOrder S _<_} {R : Ring S _+_ _*_} {pRing : PartiallyOrderedRing R pOrder} (order : TotallyOrderedRing pRing) (F : Field R) where

open Setoid S
open SetoidTotalOrder (TotallyOrderedRing.total order)
open SetoidPartialOrder pOrder
open Equivalence eq
open TotallyOrderedRing order
open Ring R
open Group additiveGroup
open Field F

open import Rings.Orders.Total.Lemmas order
open import Rings.Orders.Total.AbsoluteValue order
open import Rings.Orders.Total.Cauchy order
open import Groups.Lemmas additiveGroup

cauchyWellDefined : {s1 s2 : Sequence A} → ((m : ℕ) → index s1 m ∼ index s2 m) → cauchy s1 → cauchy s2
cauchyWellDefined {s1} {s2} prop c e 0<e with c e 0<e
... | N , pr = N , λ {m} {n} N<m N<n → <WellDefined (absWellDefined _ _ (+WellDefined (prop m) (inverseWellDefined (prop n)))) reflexive (pr N<m N<n)

record CauchyCompletion : Set (m ⊔ o) where
  field
    elts : Sequence A
    converges : (cauchy elts)

injection : A → CauchyCompletion
CauchyCompletion.elts (injection a) = constSequence a
CauchyCompletion.converges (injection a) = (λ ε 0<e → 0 , λ {m} {n} _ _ → <WellDefined (symmetric (identityOfIndiscernablesRight _∼_ (absWellDefined (index (constSequence a) m + inverse (index (constSequence a) n)) 0R (t m n)) absZero)) reflexive 0<e)
  where
    t : (m n : ℕ) → index (constSequence a) m + inverse (index (constSequence a) n) ∼ 0R
    t m n = identityOfIndiscernablesLeft _∼_ (identityOfIndiscernablesLeft _∼_ invRight (equalityCommutative (applyEquality (λ i → a + inverse i) (indexAndConst a n)))) (applyEquality (_+ inverse (index (constSequence a) n)) (equalityCommutative (indexAndConst a m)))

-- Some slightly odd things here relating to equality rather than equivalence. Ultimately this is here so we can say Q → R is a genuine injection, not just a setoid one.
private
  lemma : {x y : CauchyCompletion} → x ≡ y → CauchyCompletion.elts x ≡ CauchyCompletion.elts y
  lemma {x} {.x} refl = refl

CInjection' : Injection injection
CInjection' pr = headInjective (lemma pr)


module Relation.Equality where

open import Data.Product

-- Identity Types
--

{-
  The type of identifications of two (x y : A).

  While this can be read as propositional equality, in HoTT identity
  can be (and is) elevated to a data type.
-}
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

-- Path Induction over the Identity Type
--
ind₌ : ∀ {a c}{A : Set a} → (C : (x y : A) → (x ≡ y) → Set c) → ((x : A) → C x x refl) → (x y : A) → (p : x ≡ y) → C x y p
ind₌ C c x .x refl = c x


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

module Algebra.Linear.Structures.Bundles.FiniteDimensional where

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

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

open import Algebra.Linear.Structures.VectorSpace
open import Algebra.Linear.Structures.FiniteDimensional

open import Data.Nat using (ℕ)

record FiniteDimensional {k ℓᵏ} (K : Field k ℓᵏ) (c ℓ : Level) (n : ℕ) : Set (suc (c ⊔ k ⊔ ℓ ⊔ ℓᵏ)) where
  field
    Carrier             : Set c
    _≈_                 : Rel Carrier ℓ
    _+_                 : Carrier -> Carrier -> Carrier
    _∙_                 : Field.Carrier K -> Carrier -> Carrier
    -_                  : Carrier -> Carrier
    0#                  : Carrier
    isFiniteDimensional : IsFiniteDimensional K _≈_ _+_ _∙_ -_ 0# n

  open IsFiniteDimensional isFiniteDimensional public

  vectorSpace : VectorSpace K c ℓ
  vectorSpace = record { isVectorSpace = isVectorSpace }


{-# OPTIONS --universe-polymorphism #-}
module Categories.Monoidal.Cartesian where

open import Data.Product using (proj₁; proj₂) renaming (_×_ to _×₀_)
open import Data.Fin using (zero)

open import Categories.Category
open import Categories.Monoidal
open import Categories.Object.BinaryProducts
open import Categories.Object.Products
open import Categories.Object.Products.Properties
  renaming (module Properties to ProductProperties)
open import Categories.Object.Terminal
open import Categories.Bifunctor using (Bifunctor)
import Categories.Morphisms
open import Categories.Square
import Categories.Monoidal.Cartesian.Pentagon as Pentagon

Cartesian : ∀ {o ℓ e} (C : Category o ℓ e) → Products C → Monoidal C
Cartesian C Ps = record
  { ⊗ = ⊗
  ; id = ⊤
  ; identityˡ = record
    { F⇒G = record
      { η = λ X → π₂
      ; commute = λ f → commute₂
      }
    ; F⇐G = record
      { η = λ X → ⟨ ! , id ⟩
      ; commute = λ f → unitˡ-natural
      }
    ; iso = λ X → _≅_.iso unitˡ
    }
  ; identityʳ = record
    { F⇒G = record
      { η = λ X → π₁
      ; commute = λ f → commute₁
      }
    ; F⇐G = record
      { η = λ X → ⟨ id , ! ⟩
      ; commute = λ f → unitʳ-natural
      }
    ; iso = λ X → _≅_.iso unitʳ
    }
  ; assoc = record
    { F⇒G = record
      { η = λ X → assocˡ
      ; commute = λ f → assocˡ∘⁂
      }
    ; F⇐G = record
      { η = λ X → assocʳ
      ; commute = λ f → assocʳ∘⁂
      }
    ; iso = λ X → record
      { isoˡ = Iso.isoʳ (_≅_.iso ×-assoc)
      ; isoʳ = Iso.isoˡ (_≅_.iso ×-assoc)
      }
    }
  ; triangle = λ {X} → triangle {X}
  ; pentagon = pentagon
  }
  where
  open Categories.Morphisms C
  open Category C
  open Products Ps renaming (terminal to T₀; binary to P₀)
  open ProductProperties C Ps
  open Terminal T₀ using (⊤; !; !-unique; !-unique₂)

  open BinaryProducts P₀
  open Pentagon.Law C P₀ using (pentagon)

  ⊗ : Bifunctor C C C
  ⊗ = record
    { F₀ = λ p → proj₁ p × proj₂ p
    ; F₁ = λ x → proj₁ x ⁂ proj₂ x
    ; identity = identity
    ; homomorphism = Equiv.sym ⁂∘⁂
    ; F-resp-≡ = λ {A B F G} x → F-resp-≡ {A} {B} {F} {G} x
    }
    where
    .identity : ∀ {A : Obj ×₀ Obj} → (id {proj₁ A} ⁂ id {proj₂ A}) ≡ id
    identity = universal (id-comm {f = π₁}) (id-comm {f = π₂})

    .F-resp-≡ : ∀ {A B F G} F≡G → _
    F-resp-≡ {F = F} {G} x = ⟨⟩-cong₂ (∘-resp-≡ˡ (proj₁ x)) (∘-resp-≡ˡ (proj₂ x))

  -- The implicit x is actually used implicitly by the rest of the expression, so don't take it out,
  -- or Agda will complain about something referring to something to which it has no access.
  -- The connection between the mentioned x and the rest of the type is given by the caller way up
  -- there, so if that were not using these the triangle and pentagon laws would be yellow.
  .triangle : ∀ {x} → first π₁ ≡ second π₂ ∘ assocˡ
  triangle =
    begin
      first π₁
    ↓⟨ ⟨⟩-congʳ identityˡ ⟩
      ⟨ π₁ ∘ π₁ , π₂ ⟩
    ↑⟨ ⟨⟩-congʳ commute₂ ⟩
      ⟨ π₁ ∘ π₁ , π₂ ∘ ⟨ π₂ ∘ π₁ , π₂ ⟩ ⟩
    ↑⟨ second∘⟨⟩ ⟩
      second π₂ ∘ assocˡ
    ∎
    where
    open HomReasoning
    open Equiv


------------------------------------------------------------------------
-- The Agda standard library
--
-- Fin Literals
------------------------------------------------------------------------

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

module Data.Fin.Literals where

open import Agda.Builtin.FromNat
open import Data.Nat using (suc; _≤?_)
open import Data.Fin using (Fin ; #_)
open import Relation.Nullary.Decidable using (True)

number : ∀ n → Number (Fin n)
number n = record
  { Constraint = λ m → True (suc m ≤? n)
  ; fromNat    = λ m {{pr}} → (# m) {n} {pr}
  }


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

open import Functions.Definition
open import Sets.CantorBijection.Proofs

module Sets.CantorBijection.CantorBijection where

open Sets.CantorBijection.Proofs using (cantorInverse ; cantorInverseLemma) public

cantorBijection : Bijection cantorInverse
Bijection.inj cantorBijection {x} {y} = cantorInverseInjective x y
Bijection.surj cantorBijection = cantorInverseSurjective


{-# OPTIONS --safe #-}
module Cubical.Relation.Nullary.DecidablePropositions where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv renaming (_∙ₑ_ to _⋆_)

open import Cubical.Data.Bool
open import Cubical.Data.Sigma

open import Cubical.Relation.Nullary.Base
open import Cubical.Relation.Nullary.Properties

private
  variable
    ℓ ℓ'  : Level

DecProp : (ℓ : Level) → Type (ℓ-suc ℓ)
DecProp ℓ = Σ[ P ∈ hProp ℓ ] Dec (P .fst)

isSetDecProp : isSet (DecProp ℓ)
isSetDecProp = isOfHLevelΣ 2 isSetHProp (λ P → isProp→isSet (isPropDec (P .snd)))

-- the following is an alternative formulation of decidable propositions
-- it separates the boolean value from more proof-relevant part
-- so it performs better when doing computation

isDecProp : Type ℓ → Type ℓ
isDecProp P = Σ[ t ∈ Bool ] P ≃ Bool→Type t

DecProp' : (ℓ : Level) → Type (ℓ-suc ℓ)
DecProp' ℓ = Σ[ P ∈ Type ℓ ] isDecProp P

-- properties of the alternative formulation

isDecProp→isProp : {P : Type ℓ} → isDecProp P → isProp P
isDecProp→isProp h = isOfHLevelRespectEquiv 1 (invEquiv (h .snd)) isPropBool→Type

isDecProp→Dec : {P : Type ℓ} → isDecProp P → Dec P
isDecProp→Dec h = EquivPresDec (invEquiv (h .snd)) DecBool→Type

isPropIsDecProp : {P : Type ℓ} → isProp (isDecProp P)
isPropIsDecProp p q =
  Σ≡PropEquiv (λ _ → isOfHLevel⁺≃ᵣ 0 isPropBool→Type) .fst
    (Bool→TypeInj _ _ (invEquiv (p .snd) ⋆ q .snd))

isDecPropRespectEquiv : {P : Type ℓ} {Q : Type ℓ'}
  → P ≃ Q → isDecProp Q → isDecProp P
isDecPropRespectEquiv e (t , e') = t , e ⋆ e'


----------------------------------------------------------------------------
-- Well-founded induction on the natural numbers
----------------------------------------------------------------------------

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

module FOTC.Data.Nat.Induction.NonAcc.WF-I where

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

------------------------------------------------------------------------------
-- Well-founded induction on the natural numbers.
-- Adapted from http://code.haskell.org/~dolio/agda-share/induction/.

module WFInd where
  <-wfind : (A : D → Set) →
            (∀ {n} → N n → (∀ {m} → N m → m < n → A m) → A n) →
            ∀ {n} → N n → A n
  <-wfind A h Nn = h Nn (helper Nn)
    where
    helper : ∀ {n m} → N n → N m → m < n → A m
    helper nzero     Nm    m<0  = ⊥-elim (x<0→⊥ Nm m<0)
    helper (nsucc _) nzero 0<Sn = h nzero (λ Nm' m'<0 → ⊥-elim (x<0→⊥ Nm' m'<0))

    helper (nsucc {n} Nn) (nsucc {m} Nm) Sm<Sn = h (nsucc Nm)
      (λ {m'} Nm' m'<Sm →
        let Sm'≤Sm : succ₁ m' ≤ succ₁ m
            Sm'≤Sm = x<y→Sx≤y Nm' (nsucc Nm) m'<Sm

            Sm≤n : succ₁ m ≤ n
            Sm≤n = Sx≤Sy→x≤y (x<y→Sx≤y (nsucc Nm) (nsucc Nn) Sm<Sn)

            Sm'≤n : succ₁ m' ≤ n
            Sm'≤n = ≤-trans (nsucc Nm') (nsucc Nm) Nn Sm'≤Sm Sm≤n

            m'<n : m' < n
            m'<n = Sx≤y→x<y Nm' Nn Sm'≤n

        in  helper Nn Nm' m'<n
      )

------------------------------------------------------------------------------
-- Well-founded induction on the natural numbers (using different
-- properties of inequalities).

module WFInd' where

  <-wfind : (A : D → Set) →
            (∀ {n} → N n → (∀ {m} → N m → m < n → A m) → A n) →
            ∀ {n} → N n → A n
  <-wfind A h Nn = h Nn (helper Nn)
    where
    helper : ∀ {n m} → N n → N m → m < n → A m
    helper nzero Nm m<0 = ⊥-elim (x<0→⊥ Nm m<0)
    helper (nsucc _) nzero 0<Sn = h nzero (λ Nm' m'<0 → ⊥-elim (x<0→⊥ Nm' m'<0))
    helper (nsucc {n} Nn) (nsucc {m} Nm) Sm<Sn = h (nsucc Nm)
      (λ {m'} Nm' m'<Sm →
         let m<n : m < n
             m<n = Sx<Sy→x<y Sm<Sn

             m'<n : m' < n
             m'<n = case (λ m'<m → <-trans Nm' Nm Nn m'<m m<n)
                         (λ m'≡m → x≡y→y<z→x<z m'≡m m<n)
                         (x<Sy→x<y∨x≡y Nm' Nm m'<Sm)

         in  helper Nn Nm' m'<n
      )


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

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

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

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))


module Foundation.Equivalence where

open import Foundation.Primitive

record IsEquivalence {a} {A : Set a} {ℓ} (_≈_ : A → A → Set ℓ) : ℞ a ⊔ ℓ where
  field
    reflexivity : ∀ x → x ≈ x
    symmetry : ∀ x y → x ≈ y → y ≈ x
    transitivity : ∀ x y z → x ≈ y → y ≈ z → x ≈ z

open IsEquivalence ⦃ … ⦄ public

record Equivalence {a} (A : Set a) ℓ : ℞ a ⊔ ⟰ ℓ where
  infix 4 _≈_
  field
    _≈_ : A → A → Set ℓ
    instance ⦃ isEquivalence ⦄ : IsEquivalence _≈_

open Equivalence ⦃ … ⦄ public

open import Foundation.Bottom

infix 4 _≉_
_≉_ : ∀ {a} {A : Set a} {ℓ} ⦃ _ : Equivalence A ℓ ⦄ {b} ⦃ _ : Bottom b ℓ  ⦄ → A → A → Set (b ⊔ ℓ)
_≉_ {ℓ = ℓ} x y = ¬ (x ≈ y)


open import Data.Natural using ( Natural ; # ; _+_ )

module System.IO.Examples.Four where

four : Natural
four = # 2 + # 2