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

open import LogicalFormulae
open import Groups.Lemmas
open import Groups.Definition
open import Setoids.Orders.Partial.Definition
open import Setoids.Setoids
open import Functions.Definition
open import Sets.EquivalenceRelations
open import Rings.Definition
open import Rings.Orders.Partial.Definition
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order

module Rings.Orders.Partial.Lemmas {n m p : _} {A : Set n} {S : Setoid {n} {m} A} {_+_ : A → A → A} {_*_ : A → A → A} {_<_ : Rel {_} {p} A} {R : Ring S _+_ _*_} {pOrder : SetoidPartialOrder S _<_} (pRing : PartiallyOrderedRing R pOrder) where

abstract

  open PartiallyOrderedRing pRing
  open Setoid S
  open SetoidPartialOrder pOrder
  open Ring R
  open Group additiveGroup
  open Equivalence eq

  open import Rings.Lemmas R

  ringAddInequalities : {w x y z : A} → w < x → y < z → (w + y) < (x + z)
  ringAddInequalities {w = w} {x} {y} {z} w<x y<z = <Transitive (orderRespectsAddition w<x y) (<WellDefined groupIsAbelian groupIsAbelian (orderRespectsAddition y<z x))

  ringCanMultiplyByPositive : {x y c : A} → (Ring.0R R) < c → x < y → (x * c) < (y * c)
  ringCanMultiplyByPositive {x} {y} {c} 0<c x<y = SetoidPartialOrder.<WellDefined pOrder reflexive (Group.identRight additiveGroup) q'
    where
      have : 0R < (y + Group.inverse additiveGroup x)
      have = SetoidPartialOrder.<WellDefined pOrder (Group.invRight additiveGroup) reflexive (orderRespectsAddition x<y (Group.inverse additiveGroup x))
      p1 : 0R < ((y * c) + ((Group.inverse additiveGroup x) * c))
      p1 = SetoidPartialOrder.<WellDefined pOrder reflexive (transitive *Commutative (transitive *DistributesOver+ ((Group.+WellDefined additiveGroup) *Commutative *Commutative))) (orderRespectsMultiplication have 0<c)
      p' : 0R < ((y * c) + (Group.inverse additiveGroup (x * c)))
      p' = SetoidPartialOrder.<WellDefined pOrder reflexive (Group.+WellDefined additiveGroup reflexive (transitive (transitive *Commutative ringMinusExtracts) (inverseWellDefined additiveGroup *Commutative))) p1
      q : (0R + (x * c)) < (((y * c) + (Group.inverse additiveGroup (x * c))) + (x * c))
      q = orderRespectsAddition p' (x * c)
      q' : (x * c) < ((y * c) + 0R)
      q' = SetoidPartialOrder.<WellDefined pOrder (Group.identLeft additiveGroup) (transitive (symmetric (Group.+Associative additiveGroup)) (Group.+WellDefined additiveGroup reflexive (Group.invLeft additiveGroup))) q

  ringCanMultiplyByPositive' : {x y c : A} → (Ring.0R R) < c → x < y → (c * x) < (c * y)
  ringCanMultiplyByPositive' {x} {y} {c} 0<c x<y = SetoidPartialOrder.<WellDefined pOrder *Commutative *Commutative (ringCanMultiplyByPositive 0<c x<y)

  ringMultiplyPositives : {x y a b : A} → 0R < x → 0R < a → (x < y) → (a < b) → (x * a) < (y * b)
  ringMultiplyPositives {x} {y} {a} {b} 0<x 0<a x<y a<b = SetoidPartialOrder.<Transitive pOrder (ringCanMultiplyByPositive 0<a x<y) (<WellDefined *Commutative *Commutative (ringCanMultiplyByPositive (SetoidPartialOrder.<Transitive pOrder 0<x x<y) a<b))

  ringSwapNegatives : {x y : A} → (Group.inverse (Ring.additiveGroup R) x) < (Group.inverse (Ring.additiveGroup R) y) → y < x
  ringSwapNegatives {x} {y} -x<-y = SetoidPartialOrder.<WellDefined pOrder (transitive (symmetric (Group.+Associative additiveGroup)) (transitive (Group.+WellDefined additiveGroup reflexive (Group.invLeft additiveGroup)) (Group.identRight additiveGroup))) (Group.identLeft additiveGroup) v
    where
      t : ((Group.inverse additiveGroup x) + y) < ((Group.inverse additiveGroup y) + y)
      t = orderRespectsAddition -x<-y y
      u : (y + (Group.inverse additiveGroup x)) < 0R
      u = SetoidPartialOrder.<WellDefined pOrder (groupIsAbelian) (Group.invLeft additiveGroup) t
      v : ((y + (Group.inverse additiveGroup x)) + x) < (0R + x)
      v = orderRespectsAddition u x

  ringSwapNegatives' : {x y : A} → x < y → (Group.inverse (Ring.additiveGroup R) y) < (Group.inverse (Ring.additiveGroup R) x)
  ringSwapNegatives' {x} {y} x<y = ringSwapNegatives (<WellDefined (Equivalence.symmetric eq (invTwice additiveGroup _)) (Equivalence.symmetric eq (invTwice additiveGroup _)) x<y)

  ringCanMultiplyByNegative : {x y c : A} → c < (Ring.0R R) → x < y → (y * c) < (x * c)
  ringCanMultiplyByNegative {x} {y} {c} c<0 x<y = ringSwapNegatives u
    where
    open Equivalence eq
    p1 : (c + Group.inverse additiveGroup c) < (0R + Group.inverse additiveGroup c)
    p1 = orderRespectsAddition c<0 _
    0<-c : 0R < (Group.inverse additiveGroup c)
    0<-c = SetoidPartialOrder.<WellDefined pOrder (Group.invRight additiveGroup) (Group.identLeft additiveGroup) p1
    t : (x * Group.inverse additiveGroup c) < (y * Group.inverse additiveGroup c)
    t = ringCanMultiplyByPositive 0<-c x<y
    u : (Group.inverse additiveGroup (x * c)) < Group.inverse additiveGroup (y * c)
    u = SetoidPartialOrder.<WellDefined pOrder ringMinusExtracts ringMinusExtracts t

  anyComparisonImpliesNontrivial : {a b : A} → a < b → (0R ∼ 1R) → False
  anyComparisonImpliesNontrivial {a} {b} a<b 0=1 = irreflexive (<WellDefined (oneZeroImpliesAllZero 0=1) (oneZeroImpliesAllZero 0=1) a<b)

  moveInequality : {a b : A} → a < b → 0R < (b + inverse a)
  moveInequality {a} {b} a<b = <WellDefined invRight reflexive (orderRespectsAddition a<b (inverse a))

  moveInequality' : {a b : A} → a < b → (a + inverse b) < 0R
  moveInequality' {a} {b} a<b = <WellDefined reflexive invRight (orderRespectsAddition a<b (inverse b))

  greaterImpliesNotEqual : {a b : A} → a < b → (a ∼ b → False)
  greaterImpliesNotEqual {a} {b} a<b a=b = irreflexive (<WellDefined a=b reflexive a<b)

  greaterImpliesNotEqual' : {a b : A} → a < b → (b ∼ a → False)
  greaterImpliesNotEqual' {a} {b} a<b a=b = irreflexive (<WellDefined reflexive a=b a<b)

  negativeInequality : {a : A} → a < 0G → 0G < inverse a
  negativeInequality {a} a<0 = <WellDefined invRight identLeft (orderRespectsAddition a<0 (inverse a))

  negativeInequality' : {a : A} → 0G < a → inverse a < 0G
  negativeInequality' {a} 0<a = <WellDefined identLeft invRight (orderRespectsAddition 0<a (inverse a))

open import Rings.InitialRing R

fromNIncreasing : (0R < 1R) → (n : ℕ) → (fromN n) < (fromN (succ n))
fromNIncreasing 0<1 zero = <WellDefined reflexive (symmetric identRight) 0<1
fromNIncreasing 0<1 (succ n) = <WellDefined groupIsAbelian groupIsAbelian (orderRespectsAddition (fromNIncreasing 0<1 n) 1R)

fromNPreservesOrder : (0R < 1R) → {a b : ℕ} → (a <N b) → (fromN a) < (fromN b)
fromNPreservesOrder 0<1 {zero} {succ zero} a<b = fromNIncreasing 0<1 0
fromNPreservesOrder 0<1 {zero} {succ (succ b)} a<b = <Transitive (fromNPreservesOrder 0<1 (succIsPositive b)) (fromNIncreasing 0<1 (succ b))
fromNPreservesOrder 0<1 {succ a} {succ b} a<b = <WellDefined groupIsAbelian groupIsAbelian (orderRespectsAddition (fromNPreservesOrder 0<1 (canRemoveSuccFrom<N a<b)) 1R)


module Type.WellOrdering where

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

private variable ℓ ℓ₁ ℓ₂ : Lvl.Level

-- Types with terms that are well-founded trees.
-- Constructs types that are similar to some kind of tree.
-- The first parameter is the index for a constructor.
-- The second parameter is the arity for each constructor.
--
-- A type able to describe all non-dependent inductively defined data types assuming there are some previously defined types.
-- When described like this, the parameters should be interpreted as the following:
-- • The first parameter `A` indicates the "number" of branches based on another type's "cardinality" and should also contain the data for every branch.
-- • The second parameter `B` is used when the type to be defined refers to itself.
-- Examples:
--   open import Data
--   open import Data.Boolean
--
--   module _ (L R : Type{Lvl.𝟎}) where
--     E : Type{Lvl.𝟎}
--     E = W{A = Σ(Bool)(if_then R else L)}(const Empty) -- Either type using W.
--     l : L → E                                         -- Left branch introduction.
--     l x = sup (intro 𝐹 x) empty
--     r : R → E                                         -- Rght branch introduction.
--     r x = sup (intro 𝑇 x) empty
--
--   N = W{A = Bool}(if_then Unit{Lvl.𝟎} else Empty{Lvl.𝟎}) -- Natural numbers using W.
--   z : N                                                  -- Zero branch introduction.
--   z = sup 𝐹 empty
--   z' : _ → N                                             -- Zero branch introduction (defined like this because empty functions are not unique (from no function extensionality) resulting in more than one zero for this definition of the natural numbers).
--   z' empty = sup 𝐹 empty
--   s : N → N                                              -- Successor branch introduction.
--   s n = sup 𝑇 (\{<> → n})
--   e : ∀{P : N → Type{Lvl.𝟎}} → (∀{empty} → P(z empty)) → (∀{n} → P(n) → P(s n)) → (∀{n} → P(n)) -- TODO: Is this a correct eliminator? Note: It does not pass the termination checker
--   e pz ps {sup 𝐹 b} = pz
--   e pz ps {sup 𝑇 b} = ps (e pz (\{n} → ps{n}) {b <>})
record W {A : Type{ℓ₁}} (B : A → Type{ℓ₂}) : Type{ℓ₁ Lvl.⊔ ℓ₂} where
  inductive
  eta-equality
  constructor sup
  field
    a : A
    b : B(a) → W(B)

-- TODO: Is the type of this eliminator correct?
-- W-elim : ∀{A : Type{ℓ₁}}{B : A → Type{ℓ₂}}{P : W(B) → Type{ℓ}} → (∀{a : A}{b : B(a) → W(B)} → (∀{ba : B(a)} → P(b(ba))) → P(sup a b)) → (∀{w : W(B)} → P(w))

-- TODO: Note that this is essentially Sets.IterativeSet
V : ∀{ℓ₁} → Type{Lvl.𝐒(ℓ₁)}
V {ℓ₁} = W {A = Type{ℓ₁}} id


{-# OPTIONS --safe #-}
module Cubical.Algebra.CommMonoid.CommMonoidProd where

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

open import Cubical.Data.Sigma

open import Cubical.Algebra.Monoid
open import Cubical.Algebra.Semigroup
open import Cubical.Algebra.CommMonoid.Base

open CommMonoidStr
open IsCommMonoid hiding (rid ; lid)
open IsMonoid hiding (rid ; lid)
open IsSemigroup

private
  variable
    ℓ ℓ' : Level

CommMonoidProd : CommMonoid ℓ → CommMonoid ℓ' → CommMonoid (ℓ-max ℓ ℓ')
CommMonoidProd M N = makeCommMonoid ε× _·×_ is-set× assoc× rid× comm×
  where
  ε× : (fst M) × (fst N)
  ε× = (ε (snd M)) , (ε (snd N))

  _·×_ : (fst M) × (fst N) → (fst M) × (fst N) → (fst M) × (fst N)
  (x₁ , x₂) ·× (y₁ , y₂) = (_·_ (snd M) x₁ y₁) , (_·_ (snd N) x₂ y₂)

  is-set× : isSet ((fst M) × (fst N))
  is-set× = isSet× (is-set (snd M)) (is-set (snd N))

  assoc× : ∀ x y z → x ·× (y ·× z) ≡ (x ·× y) ·× z
  assoc× _ _ _ = cong₂ (_,_) (assoc (snd M) _ _ _) (assoc (snd N) _ _ _)

  rid× : ∀ x → x ·× ε× ≡ x
  rid× _ =  cong₂ (_,_) (rid (snd M) _) (rid (snd N) _)

  comm× : ∀ x y → x ·× y ≡ y ·× x
  comm× _ _ = cong₂ (_,_) (comm (snd M) _ _) (comm (snd N) _ _)


{-# OPTIONS --exact-split #-}

module ExactSplitMin where

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

min : ℕ → ℕ → ℕ
min zero    y       = zero
min x       zero    = zero
min (suc x) (suc y) = suc (min x y)


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

open import Categories.Category
open import Categories.Comonad

-- verbatim dual of Categories.Category.Construction.EilenbergMoore
module Categories.Category.Construction.CoEilenbergMoore {o ℓ e} {C : Category o ℓ e} (M : Comonad C) where

open import Level

open import Categories.Morphism.Reasoning C

private
  module C = Category C
  module M = Comonad M
  open C
  open M.F
  open HomReasoning
  open Equiv

record Comodule : Set (o ⊔ ℓ ⊔ e) where
  field
    A        : Obj
    coaction   : A ⇒ F₀ A
    commute  : F₁ coaction ∘ coaction ≈ M.δ.η A ∘ coaction
    identity : M.ε.η A ∘ coaction ≈ C.id

record Comodule⇒ (X Y : Comodule) : Set (ℓ ⊔ e) where
  private
    module X = Comodule X
    module Y = Comodule Y
  field
    arr     : X.A ⇒ Y.A
    commute : Y.coaction ∘ arr ≈ F₁ arr ∘ X.coaction

CoEilenbergMoore : Category (o ⊔ ℓ ⊔ e) (ℓ ⊔ e) e
CoEilenbergMoore =
 record
  { Obj = Comodule
  ; _⇒_ = Comodule⇒
  ; _≈_ = λ f g → Comodule⇒.arr f ≈ Comodule⇒.arr g
  ; id = record { arr = C.id ; commute = identityʳ ○ introˡ identity }
  ; _∘_ = compose
  ; assoc = assoc
  ; sym-assoc = sym-assoc
  ; identityˡ = identityˡ
  ; identityʳ = identityʳ
  ; identity² = identity²
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = ∘-resp-≈
  }
  where
   open Equiv
   compose : ∀ {X Y Z} → Comodule⇒ Y Z → Comodule⇒ X Y → Comodule⇒ X Z
   compose {X} {Y} {Z} f g = record
    { arr     = f.arr ∘ g.arr
    ; commute = begin
       Comodule.coaction Z ∘ f.arr ∘ g.arr       ≈⟨ pullˡ f.commute ⟩
       (F₁ f.arr ∘ Comodule.coaction Y) ∘ g.arr  ≈⟨ pullʳ g.commute ⟩
       F₁ f.arr ∘ F₁ g.arr ∘ Comodule.coaction X ≈⟨ pullˡ (sym homomorphism) ⟩
       F₁ (f.arr ∘ g.arr) ∘ Comodule.coaction X  ∎
    }
    where module f = Comodule⇒ f
          module g = Comodule⇒ g


module Function.Iteration where

open import Data
open import Functional
open import Numeral.Natural
open import Type
open import Syntax.Number

module _ {ℓ} {T : Type{ℓ}} where
  -- Repeated function composition
  -- Example:
  --   f ^ 0 = id
  --   f ^ 1 = f
  --   f ^ 2 = f ∘ f
  --   f ^ 3 = f ∘ f ∘ f
  --   f ^ 4 = f ∘ f ∘ f ∘ f
  _^_ : (T → T) → ℕ → (T → T)
  _^_ f 𝟎      = id
  _^_ f (𝐒(n)) = f ∘ (f ^ n)

  _⁰ : (T → T) → (T → T)
  _⁰ = _^ 0

  _¹ : (T → T) → (T → T)
  _¹ = _^ 1

  _² : (T → T) → (T → T)
  _² = _^ 2

  _³ : (T → T) → (T → T)
  _³ = _^ 3

  _⁴ : (T → T) → (T → T)
  _⁴ = _^ 4

  _⁵ : (T → T) → (T → T)
  _⁵ = _^ 5

  _⁶ : (T → T) → (T → T)
  _⁶ = _^ 6

  _⁷ : (T → T) → (T → T)
  _⁷ = _^ 7

  _⁸ : (T → T) → (T → T)
  _⁸ = _^ 8

  _⁹ : (T → T) → (T → T)
  _⁹ = _^ 9

module _ {ℓ₁}{ℓ₂} {X : Type{ℓ₁}} {Y : Type{ℓ₂}} where
  -- Repeat a binary operation n times for the same element and an initial element
  -- Example: repeatₗ 3 id (_∘_) f = ((id ∘ f) ∘ f) ∘ f
  -- Example in Haskell: (foldl (.) (id) (take 5 (repeat f)))
  -- Implementation in Haskell: (\n null op elem -> foldl op null (take n (repeat elem))) :: Int -> a -> (b -> a -> b) -> b -> b
  repeatₗ : ℕ → (Y → X → Y) → (Y → X → Y)
  repeatₗ n (_▫_) null elem = ((_▫ elem) ^ n) (null)

  -- Repeat a binary operation n times for the same element and an initial element
  -- Example: repeatᵣ 3 id (_∘_) f = f ∘ (f ∘ (f ∘ id))
  -- Implementation in Haskell: (\n elem op null -> foldr op null (take n (repeat elem))) :: Int -> a -> (a -> b -> b) -> b -> b
  repeatᵣ : ℕ → (X → Y → Y) → (X → Y → Y)
  repeatᵣ n (_▫_) elem = (elem ▫_) ^ n

module _ {ℓ} {X : Type{ℓ}} where
  -- Repeat a binary operation n times for the same element and using the default element on zero.
  -- Examples:
  --   repeatₗ 0 def (_∘_) f = def
  --   repeatₗ 1 def (_∘_) f = f
  --   repeatₗ 4 def (_∘_) f = ((f ∘ f) ∘ f) ∘ f
  repeatₗ-default : ℕ → (X → X → X) → X → (X → X)
  repeatₗ-default 𝟎      _     def  _    = def
  repeatₗ-default (𝐒(n)) (_▫_) _    elem = repeatₗ(n) (_▫_) elem elem

  -- Repeat a binary operation n times for the same element and using the default element on zero.
  -- Examples:
  --   repeatᵣ 0 f (_∘_) def = def
  --   repeatᵣ 1 f (_∘_) def = f
  --   repeatᵣ 4 f (_∘_) def = f ∘ (f ∘ (f ∘ f))
  repeatᵣ-default : ℕ → (X → X → X) → X → (X → X)
  repeatᵣ-default 𝟎      _     _    def = def
  repeatᵣ-default (𝐒(n)) (_▫_) elem _   = repeatᵣ(n) (_▫_) elem elem


{-# OPTIONS --sized-types #-}

module SizedTypesMergeSort where

postulate
  Size : Set
  _^   : Size -> Size
  ∞    : Size

{-# BUILTIN SIZE Size  #-}
{-# BUILTIN SIZESUC _^ #-}
{-# BUILTIN SIZEINF ∞  #-}

-- sized lists

data List (A : Set) : {_ : Size} -> Set where
  []   : {size : Size} -> List A {size ^}
  _::_ : {size : Size} -> A -> List A {size} -> List A {size ^}

-- CPS split

split : {A : Set}{i : Size} -> List A {i} ->
        {C : Set} -> (List A {i} -> List A {i} -> C) -> C
split []        k = k [] []
split (x :: xs) k = split xs (\ l r -> k (x :: r) l)

module Sort (A : Set) (compare : A -> A -> {B : Set} -> B -> B -> B) where

{- merge is currently rejected by the termination checker
   it would be nice if it worked
   see test/succeed/SizedTypesMergeSort.agda
 -}
  merge : List A -> List A -> List A
  merge [] ys = ys
  merge xs [] = xs
  merge (x :: xs) (y :: ys) =
    compare x y (x :: merge xs (y :: ys))
                (y :: merge (x :: xs) ys)

  sort : {i : Size} -> List A {i} -> List A
  sort [] = []
  sort (x :: []) = x :: []
  sort (x :: (y :: xs)) = split xs (\ l r -> merge (sort (x :: l))
                                                   (sort (y :: r)))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Examples showing how the generic n-ary operations the stdlib provides
-- can be used
------------------------------------------------------------------------

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

module README.Nary where

open import Level using (Level)
open import Data.Nat.Base
open import Data.Nat.Properties
open import Data.Fin using (Fin; fromℕ; #_; inject₁)
open import Data.List
open import Data.List.Properties
open import Data.Product using (_×_; _,_)
open import Data.Sum using (inj₁; inj₂)
open import Function
open import Relation.Nullary
open import Relation.Binary using (module Tri); open Tri
open import Relation.Binary.PropositionalEquality

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

------------------------------------------------------------------------
-- Introduction
------------------------------------------------------------------------

-- Function.Nary.NonDependent and Data.Product.N-ary.Heterogeneous provide
-- a generic representation of n-ary heterogeneous (non dependent) products
-- and the corresponding types of (non-dependent) n-ary functions. The
-- representation works well with inference thus allowing us to use generic
-- combinators to manipulate such functions.

open import Data.Product.Nary.NonDependent
open import Function.Nary.NonDependent
open import Relation.Nary

------------------------------------------------------------------------
-- Generalised equality-manipulating combinators
------------------------------------------------------------------------

-- By default the standard library provides users with (we are leaving out
-- the implicit arguments here):
--
-- cong   : (f : A₁      → B) → a₁ ≡ b₁           → f a₁   ≡ f b₁
-- cong₂  : (f : A₁ → A₂ → B) → a₁ ≡ b₁ → a₂ ≡ b₂ → f a₁ a₂ ≡ f b₁ b₂
--
-- and
--
-- subst  : (P : A₁      → Set p) → a₁ ≡ b₁           → P a₁    → P b₁
-- subst₂ : (P : A₁ → A₂ → Set p) → a₁ ≡ b₁ → a₂ ≡ b₂ → P a₁ a₂ → P b₁ b₂
--
-- This pattern can be generalised to any natural number `n`. Thanks to our
-- library for n-ary functions, we can write the types and implementations
-- of `congₙ` and `substₙ`.

------------------------------------------------------------------------
-- congₙ : ∀ n (f : A₁ → ⋯ → Aₙ → B) →
--         a₁ ≡ b₁ → ⋯ aₙ ≡ bₙ → f a₁ ⋯ aₙ ≡ f b₁ ⋯ bₙ

-- It may be used directly to prove something:

_ : ∀ (as bs cs : List ℕ) →
       zip (zip (as ++ []) (map id cs)) (reverse (reverse bs))
     ≡ zip (zip as cs) bs
_ = λ as bs cs → congₙ 3 (λ as bs → zip (zip as bs))
                         (++-identityʳ as)
                         (map-id cs)
                         (reverse-involutive bs)

-- Or as part of a longer derivation:

_ : ∀ m n p q → suc (m + (p * n) + (q ^ (m + n)))
              ≡ (m + 0) + (n * p) + (q ^ m * q ^ n) + 1
_ = λ m n p q → begin
    suc (m + (p * n) + (q ^ (m + n))) ≡⟨ +-comm 1 _ ⟩
    m + (p * n) + (q ^ (m + n)) + 1   ≡⟨ congₙ 3 (λ m n p → m + n + p + 1)
                                                 (+-comm 0 m)
                                                 (*-comm p n)
                                                 (^-distribˡ-+-* q m n)
                                       ⟩
    m + 0 + n * p + (q ^ m) * (q ^ n) + 1 ∎ where open ≡-Reasoning

-- Partial application of the functional argument is fine: the number of arguments
-- `congₙ` is going to take is determined by its first argument (a natural number)
-- and not by the type of the function it works on.

_ : ∀ m → (m +_) ≡ ((m + 0) +_)
_ = λ m → congₙ 1 _+_ (+-comm 0 m)

-- We don't have to work on the function's first argument either: we can just as
-- easily use `congₙ` to act on the second one by `flip`ping it. See `holeₙ` for
-- a generalisation of this idea allowing to target *any* of the function's
-- arguments and not just the first or second one.

_ : ∀ m → (_+ m) ≡ (_+ (m + 0))
_ = λ m → congₙ 1 (flip _+_) (+-comm 0 m)

------------------------------------------------------------------------
-- substₙ : (P : A₁ → ⋯ → Aₙ → Set p) →
--          a₁ ≡ b₁ → ⋯ aₙ ≡ bₙ → P a₁ ⋯ aₙ → P b₁ ⋯ bₙ

-- We can play the same type of game with subst

open import Agda.Builtin.Nat using (mod-helper)

-- Because we know from the definition `mod-helper` that this equation holds:
-- mod-helper k m (suc n) (suc j) = mod-helper (suc k) m n j
-- we should be able to prove the slightly modified statement by transforming
-- all the `x + 1` into `suc x`. We can do so using `substₙ`.

_ : ∀ k m n j → mod-helper k m (n + 1) (j + 1) ≡ mod-helper (k + 1) m n j
_ = λ k m n j →
    let P sk sn sj = mod-helper k m sn sj ≡ mod-helper sk m n j
    in substₙ P (+-comm 1 k) (+-comm 1 n) (+-comm 1 j) refl

-----------------------------------------------------------------------
-- Generic programs working on n-ary products & functions
-----------------------------------------------------------------------

-----------------------------------------------------------------------
-- curryₙ   : ∀ n → (A₁ × ⋯ × Aₙ → B) → A₁ → ⋯ → Aₙ → B
-- uncurryₙ : ∀ n → (A₁ → ⋯ → Aₙ → B) → A₁ × ⋯ × Aₙ → B

-- The first thing we may want to do generically is convert between
-- curried function types and uncurried ones. We can do this by using:

-- They both work the same way so we will focus on curryₙ only here.
-- If we pass to `curryₙ` the arity of its argument then we obtain a
-- fully curried function.

curry₁ : (A × B × C × D → E) → A → B → C → D → E
curry₁ = curryₙ 4

-- Note that here we are not flattening arbitrary nestings: products have
-- to be right nested. Which means that if you have a deeply-nested product
-- then it won't be affected by the procedure.

curry₁' : (A × (B × C) × D → E) → A → (B × C) → D → E
curry₁' = curryₙ 3

-- When we are currying a function, we have no obligation to pass its exact
-- arity as the parameter: we can decide to only curry part of it like so:
-- Indeed (A₁ × ⋯ × Aₙ → B) can also be seen as (A₁ × ⋯ × (Aₖ × ⋯ × Aₙ) → B)

curry₂ : (A × B × C × D → E) → A → B → (C × D) → E
curry₂ = curryₙ 3

-----------------------------------------------------------------------
-- projₙ : ∀ n (k : Fin n) → (A₁ × ⋯ × Aₙ) → Aₖ₊₁

-- Another useful class of functions to manipulate n-ary product is a
-- generic projection function. Note the (k + 1) in the return index:
-- Fin counts from 0 up.

-- It behaves as one expects (Data.Fin's #_ comes in handy to write down
-- Fin literals):

proj₃ : (A × B × C × D × E) → C
proj₃ = projₙ 5 (# 2)

-- Of course we can once more project the "tail" of the n-ary product by
-- passing `projₙ` a natural number which is smaller than the size of the
-- n-ary product, seeing (A₁ × ⋯ × Aₙ) as (A₁ × ⋯ × (Aₖ × ⋯ × Aₙ)).

proj₃' : (A × B × C × D × E) → C × D × E
proj₃' = projₙ 3 (# 2)

-----------------------------------------------------------------------
-- insertₙ : ∀ n (k : Fin (suc n)) →
--           B → (A₁ × ⋯ Aₙ) → (A₁ × ⋯ × Aₖ × B × Aₖ₊₁ × ⋯ Aₙ)

insert₁ : C → (A × B × D × E) → (A × B × C × D × E)
insert₁ = insertₙ 4 (# 2)

insert₁' : C → (A × B × D × E) → (A × B × C × D × E)
insert₁' = insertₙ 3 (# 2)

-- Note that `insertₙ` takes a `Fin (suc n)`. Indeed in an n-ary product
-- there are (suc n) positions at which one may insert a value. We may
-- insert at the front or the back of the product:

insert-front : A → (B × C × D × E) → (A × B × C × D × E)
insert-front = insertₙ 4 (# 0)

insert-back : E → (A × B × C × D) → (A × B × C × D × E)
insert-back = insertₙ 4 (# 4)

-----------------------------------------------------------------------
-- removeₙ : ∀ n (k : Fin n) → (A₁ × ⋯ Aₙ) → (A₁ × ⋯ × Aₖ × Aₖ₊₂ × ⋯ Aₙ)

-- Dual to `insertₙ`, we may remove a value.

remove₁ : (A × B × C × D × E) → (A × B × D × E)
remove₁ = removeₙ 5 (# 2)

-- Inserting at `k` and then removing at `inject₁ k` should yield the identity

remove-insert : C → (A × B × D × E) → (A × B × D × E)
remove-insert c = removeₙ 5 (inject₁ k) ∘′ insertₙ 4 k c
    where k = # 2

-----------------------------------------------------------------------
-- updateₙ : ∀ n (k : Fin n) (f : (a : Aₖ₊₁) → B a) →
--           (p : A₁ × ⋯ Aₙ) → (A₁ × ⋯ × Aₖ × B (projₙ n k p) × Aₖ₊₂ × ⋯ Aₙ)

-- We can not only project out, insert or remove values: we can update them
-- in place. The type (and value) of the replacement at position k may depend
-- upon the current value at position k.

update₁ : (p : A × B × ℕ × C × D) → (A × B × Fin _ × C × D)
update₁ = updateₙ 5 (# 2) fromℕ

-- We can explicitly use the primed version of `updateₙ` to make it known to
-- Agda that the update function is non dependent. This type of information
-- is useful for inference: the tighter the constraints, the easier it is to
-- find a solution (if possible).

update₂ : (p : A × B × ℕ × C × D) → (A × B × List D × C × D)
update₂ = λ p → updateₙ′ 5 (# 2) (λ n → replicate n (projₙ 5 (# 4) p)) p

-----------------------------------------------------------------------
-- _%=_⊢_ : ∀ n → (C → D) → (A₁ → ⋯ Aₙ → D → B) → A₁ → ⋯ → Aₙ → C → B

-- Traditional composition (also known as the index update operator `_⊢_`
-- in `Relation.Unary`) focuses solely on the first argument of an n-ary
-- function. `_%=_⊢_` on the other hand allows us to touch any one of the
-- arguments.

-- In the following example we have a function `f : A → B` and `replicate`
-- of type `ℕ → B → List B`. We want ̀f` to act on the second argument of
-- replicate. Which we can do like so.

compose₁ : (A → B) → ℕ → A → List B
compose₁ f = 1 %= f ⊢ replicate

-- Here we spell out the equivalent explicit variable-manipulation and
-- prove the two functions equal.

compose₁' : (A → B) → ℕ → A → List B
compose₁' f n a = replicate n (f a)

compose₁-eq : compose₁ {a} {A} {b} {B} ≡ compose₁'
compose₁-eq = refl

-----------------------------------------------------------------------
-- _∷=_⊢_ : ∀ n → A → (A₁ → ⋯ Aₙ → A → B) → A₁ → ⋯ → Aₙ → B

-- Partial application usually focuses on the first argument of a function.
-- We can now partially apply a function in any of its arguments using
-- `_∷=_⊢_`. Reusing our example involving replicate: we can specialise it
-- to only output finite lists of `0`:

apply₁ : ℕ → List ℕ
apply₁ = 1 ∷= 0 ⊢ replicate

apply₁-eq : apply₁ 3 ≡ 0 ∷ 0 ∷ 0 ∷ []
apply₁-eq = refl

------------------------------------------------------------------------
-- holeₙ : ∀ n → (A → (A₁ → ⋯ Aₙ → B)) → A₁ → ⋯ → Aₙ → (A → B)

-- As we have seen earlier, `cong` acts on a function's first variable.
-- If we want to access the second one, we can use `flip`. But what about
-- the fourth one? We typically use an explicit λ-abstraction shuffling
-- variables. Not anymore.

-- Reusing mod-helper just because it takes a lot of arguments:

hole₁ : ∀ k m n j → mod-helper k (m + 1) n j ≡ mod-helper k (suc m) n j
hole₁ = λ k m n j → cong (holeₙ 2 (mod-helper k) n j) (+-comm m 1)

-----------------------------------------------------------------------
-- mapₙ : ∀ n → (B → C) → (A₁ → ⋯ Aₙ → B) → (A₁ → ⋯ → Aₙ → C)

-- (R →_) gives us the reader monad (and, a fortiori, functor). That is to
-- say that given a function (A → B) and an (R → A) we can get an (R → B)
-- This generalises to n-ary functions.

-- Reusing our `composeₙ` example: instead of applying `f` to the replicated
-- element, we can map it on the resulting list. Giving us:

map₁ : (A → B) → ℕ → A → List B
map₁ f = mapₙ 2 (map f) replicate

------------------------------------------------------------------------
-- constₙ : ∀ n → B → A₁ → ⋯ → Aₙ → B

-- `const` is basically `pure` for the reader monad discussed above. Just
-- like we can generalise the functorial action corresponding to the reader
-- functor to n-ary functions, we can do the same for `pure`.

const₁ : A → B → C → D → E → A
const₁ = constₙ 4

-- Together with `holeₙ`, this means we can make a constant function out
-- of any of the arguments. The fourth for instance:

const₂ : A → B → C → D → E → D
const₂ = holeₙ 3 (constₙ 4)

------------------------------------------------------------------------
-- Generalised quantifiers
------------------------------------------------------------------------

-- As we have seen multiple times already, one of the advantages of working
-- with non-dependent products is that they can be easily inferred. This is
-- a prime opportunity to define generic quantifiers.

-- And because n-ary relations are Set-terminated, there is no ambiguity
-- where to split between arguments & codomain. As a consequence Agda can
-- infer even `n`, the number of arguments. We can use notations which are
-- just like the ones defined in `Relation.Unary`.

------------------------------------------------------------------------
-- ∃⟨_⟩ : (A₁ → ⋯ → Aₙ → Set r) → Set _
-- ∃⟨ P ⟩ = ∃ λ a₁ → ⋯ → ∃ λ aₙ → P a₁ ⋯ aₙ

-- Returning to our favourite function taking a lot of arguments: we can
-- find a set of input for which it evaluates to 666

exist₁ : ∃⟨ (λ k m n j → mod-helper k m n j ≡ 666) ⟩
exist₁ = 19 , 793 , 3059 , 10 , refl

------------------------------------------------------------------------
-- ∀[_] : (A₁ → ⋯ → Aₙ → Set r) → Set _
-- ∀[_] P = ∀ {a₁} → ⋯ → ∀ {aₙ} → P a₁ ⋯ aₙ

all₁ : ∀[ (λ (a₁ a₂ : ℕ) → Dec (a₁ ≡ a₂)) ]
all₁ {a₁} {a₂} = a₁ ≟ a₂

------------------------------------------------------------------------
-- Π : (A₁ → ⋯ → Aₙ → Set r) → Set _
-- Π P = ∀ a₁ → ⋯ → ∀ aₙ → P a₁ ⋯ aₙ

all₂ : Π[ (λ (a₁ a₂ : ℕ) → Dec (a₁ ≡ a₂)) ]
all₂ = _≟_

------------------------------------------------------------------------
-- _⇒_ : (A₁ → ⋯ → Aₙ → Set r) → (A₁ → ⋯ → Aₙ → Set s) → (A₁ → ⋯ → Aₙ → Set _)
-- P ⇒ Q = λ a₁ → ⋯ → λ aₙ → P a₁ ⋯ aₙ → Q a₁ ⋯ aₙ

antisym : ∀[ _≤_ ⇒ _≥_ ⇒ _≡_ ]
antisym = ≤-antisym

------------------------------------------------------------------------
-- _∪_ : (A₁ → ⋯ → Aₙ → Set r) → (A₁ → ⋯ → Aₙ → Set s) → (A₁ → ⋯ → Aₙ → Set _)
-- P ∪ Q = λ a₁ → ⋯ → λ aₙ → P a₁ ⋯ aₙ ⊎ Q a₁ ⋯ aₙ

≤->-connex : Π[ _≤_ ∪ _>_ ]
≤->-connex m n with <-cmp m n
... | tri< a ¬b ¬c = inj₁ (<⇒≤ a)
... | tri≈ ¬a b ¬c = inj₁ (≤-reflexive b)
... | tri> ¬a ¬b c = inj₂ c

------------------------------------------------------------------------
-- _∩_ : (A₁ → ⋯ → Aₙ → Set r) → (A₁ → ⋯ → Aₙ → Set s) → (A₁ → ⋯ → Aₙ → Set _)
-- P ∩ Q = λ a₁ → ⋯ → λ aₙ → P a₁ ⋯ aₙ × Q a₁ ⋯ aₙ

<-inversion : ∀[ _<_ ⇒ _≤_ ∩ _≢_ ]
<-inversion m<n = <⇒≤ m<n , <⇒≢ m<n

------------------------------------------------------------------------
-- ∁ : (A₁ → ⋯ → Aₙ → Set r) → (A₁ → ⋯ → Aₙ → Set _)
-- ∁ P = λ a₁ → ⋯ → λ aₙ → ¬ (P a₁ ⋯ aₙ)

m<n⇒m≱n : ∀[ _>_ ⇒ ∁ _≤_ ]
m<n⇒m≱n m>n m≤n = <⇒≱ m>n m≤n


------------------------------------------------------------------------------
-- Equivalence of definitions of total lists
------------------------------------------------------------------------------

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

module LFPs.List where

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

------------------------------------------------------------------------------
module LFP where

  -- List is a least fixed-point of a functor

  -- The functor.
  ListF : (D → Set) → D → Set
  ListF A xs = xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs')

  -- List is the least fixed-point of ListF. i.e.
  postulate
    List : D → Set

    -- List is a pre-fixed point of ListF, i.e.
    --
    -- ListF List ≤ List.
    --
    -- Peter: It corresponds to the introduction rules.
    List-in : ∀ {xs} → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ List xs') →
              List xs

    -- The higher-order version.
    List-in-ho : {xs : D} → ListF List xs → List xs

    -- List is the least pre-fixed point of ListF, i.e.
    --
    -- ∀ A. ListF A ≤ A ⇒ List ≤ A.
    --
    -- Peter: It corresponds to the elimination rule of an inductively
    -- defined predicate.
    List-ind :
      (A : D → Set) →
      (∀ {xs} → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs') → A xs) →
      ∀ {xs} → List xs → A xs

    -- Higher-order version.
    List-ind-ho :
      (A : D → Set) →
      (∀ {xs} → ListF A xs → A xs) →
      ∀ {xs} → List xs → A xs

  ----------------------------------------------------------------------------
  -- List-in and List-in-ho are equivalents

  List-in-fo : ∀ {xs} → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ List xs') →
               List xs
  List-in-fo = List-in-ho

  List-in-ho' : {xs : D} → ListF List xs → List xs
  List-in-ho' = List-in-ho

  ----------------------------------------------------------------------------
  -- List-ind and List-ind-ho are equivalents

  List-ind-fo :
    (A : D → Set) →
    (∀ {xs} → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs') → A xs) →
    ∀ {xs} → List xs → A xs
  List-ind-fo = List-ind-ho

  List-ind-ho' :
    (A : D → Set) →
    (∀ {xs} → ListF A xs → A xs) →
    ∀ {xs} → List xs → A xs
  List-ind-ho' = List-ind

  ----------------------------------------------------------------------------
  -- The data constructors of List.
  lnil : List []
  lnil = List-in (inj₁ refl)

  lcons : ∀ x {xs} → List xs → List (x ∷ xs)
  lcons x {xs} Lxs = List-in (inj₂ (x , xs , refl , Lxs))

  ----------------------------------------------------------------------------
  -- The type theoretical induction principle for List.
  List-ind' : (A : D → Set) →
              A [] →
              (∀ x {xs} → A xs → A (x ∷ xs)) →
              ∀ {xs} → List xs → A xs
  List-ind' A A[] is = List-ind A prf
    where
    prf : ∀ {xs} → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs') → A xs
    prf (inj₁ xs≡[])                  = subst A (sym xs≡[]) A[]
    prf (inj₂ (x' , xs' , h₁ , Axs')) = subst A (sym h₁) (is x' Axs')

  ----------------------------------------------------------------------------
  -- Example

  xs : D
  xs = 0' ∷ true ∷ 1' ∷ false ∷ []

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

------------------------------------------------------------------------------
module Data where

  data List : D → Set where
    lnil  : List []
    lcons : ∀ x {xs} → List xs → List (x ∷ xs)

  -- Induction principle.
  List-ind : (A : D → Set) →
             A [] →
             (∀ x {xs} → A xs → A (x ∷ xs)) →
             ∀ {xs} → List xs → A xs
  List-ind A A[] h lnil          = A[]
  List-ind A A[] h (lcons x Lxs) = h x (List-ind A A[] h Lxs)

  ----------------------------------------------------------------------------
  -- List-in

  List-in : ∀ {xs} → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ List xs') →
            List xs
  List-in {xs} h = case prf₁ prf₂ h
    where
    prf₁ : xs ≡ [] → List xs
    prf₁ xs≡[] = subst List (sym xs≡[]) lnil

    prf₂ : ∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ List xs' → List xs
    prf₂ (x' , xs' , prf , Lxs') =  subst List (sym prf) (lcons x' Lxs')

  ----------------------------------------------------------------------------
  -- The fixed-point induction principle for List.

  List-ind' :
    (A : D → Set) →
    (∀ {xs} → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs') → A xs) →
    ∀ {xs} → List xs → A xs
  List-ind' A h Lxs = List-ind A h₁ h₂ Lxs
    where
    h₁ : A []
    h₁ = h (inj₁ refl)

    h₂ : ∀ y {ys} → A ys → A (y ∷ ys)
    h₂ y {ys} Ays = h (inj₂ (y , ys , refl , Ays))


{-# OPTIONS --without-K --exact-split --allow-unsolved-metas #-}

module 12-univalence where

import 11-function-extensionality
open 11-function-extensionality public

-- Section 10.1 Type extensionality

equiv-eq : {i : Level} {A : UU i} {B : UU i} → Id A B → A ≃ B
equiv-eq {A = A} refl = equiv-id A

UNIVALENCE : {i : Level} (A B : UU i) → UU (lsuc i)
UNIVALENCE A B = is-equiv (equiv-eq {A = A} {B = B})

is-contr-total-equiv-UNIVALENCE : {i : Level} (A : UU i) →
  ((B : UU i) → UNIVALENCE A B) → is-contr (Σ (UU i) (λ X → A ≃ X))
is-contr-total-equiv-UNIVALENCE A UA =
  fundamental-theorem-id' A
    ( equiv-id A)
    ( λ B → equiv-eq {B = B})
    ( UA)

UNIVALENCE-is-contr-total-equiv : {i : Level} (A : UU i) →
  is-contr (Σ (UU i) (λ X → A ≃ X)) → (B : UU i) → UNIVALENCE A B
UNIVALENCE-is-contr-total-equiv A c =
  fundamental-theorem-id A
    ( equiv-id A)
    ( c)
    ( λ B → equiv-eq {B = B})

ev-id : {i j : Level} {A : UU i} (P : (B : UU i) → (A ≃ B) → UU j) →
  ((B : UU i) (e : A ≃ B) → P B e) → P A (equiv-id A)
ev-id {A = A} P f = f A (equiv-id A)

IND-EQUIV : {i j : Level} {A : UU i} → ((B : UU i) (e : A ≃ B) → UU j) → UU _
IND-EQUIV P = sec (ev-id P)

triangle-ev-id : {i j : Level} {A : UU i}
  (P : (Σ (UU i) (λ X → A ≃ X)) → UU j) →
  (ev-pt (Σ (UU i) (λ X → A ≃ X)) (pair A (equiv-id A)) P)
  ~ ((ev-id (λ X e → P (pair X e))) ∘ (ev-pair {A = UU i} {B = λ X → A ≃ X} {C = P}))
triangle-ev-id P f = refl

abstract
  IND-EQUIV-is-contr-total-equiv : {i j : Level} (A : UU i) →
    is-contr (Σ (UU i) (λ X → A ≃ X)) →
    (P : (Σ (UU i) (λ X → A ≃ X)) → UU j) → IND-EQUIV (λ B e → P (pair B e))
  IND-EQUIV-is-contr-total-equiv {i} {j} A c P =
    section-comp
      ( ev-pt (Σ (UU i) (λ X → A ≃ X)) (pair A (equiv-id A)) P)
      ( ev-id (λ X e → P (pair X e)))
      ( ev-pair {A = UU i} {B = λ X → A ≃ X} {C = P})
      ( triangle-ev-id P)
      ( sec-ev-pair (UU i) (λ X → A ≃ X) P)
      ( is-sing-is-contr (Σ (UU i) (λ X → A ≃ X))
        ( pair
          ( pair A (equiv-id A))
          ( λ t →
            ( inv (contraction c (pair A (equiv-id A)))) ∙
            ( contraction c t)))
        ( P)
        ( pair A (equiv-id A)))

abstract
  is-contr-total-equiv-IND-EQUIV : {i : Level} (A : UU i) →
    ( {j : Level} (P : (Σ (UU i) (λ X → A ≃ X)) → UU j) →
      IND-EQUIV (λ B e → P (pair B e))) →
    is-contr (Σ (UU i) (λ X → A ≃ X))
  is-contr-total-equiv-IND-EQUIV {i} A ind =
    is-contr-is-sing
      ( Σ (UU i) (λ X → A ≃ X))
      ( pair A (equiv-id A))
      ( λ P → section-comp'
        ( ev-pt (Σ (UU i) (λ X → A ≃ X)) (pair A (equiv-id A)) P)
        ( ev-id (λ X e → P (pair X e)))
        ( ev-pair {A = UU i} {B = λ X → A ≃ X} {C = P})
        ( triangle-ev-id P)
        ( sec-ev-pair (UU i) (λ X → A ≃ X) P)
        ( ind P))

-- The univalence axiom

postulate univalence : {i : Level} (A B : UU i) → UNIVALENCE A B

eq-equiv : {i : Level} (A B : UU i) → (A ≃ B) → Id A B
eq-equiv A B = inv-is-equiv (univalence A B)

abstract
  is-contr-total-equiv : {i : Level} (A : UU i) →
    is-contr (Σ (UU i) (λ X → A ≃ X))
  is-contr-total-equiv A = is-contr-total-equiv-UNIVALENCE A (univalence A)

inv-inv-equiv :
  {i j : Level} {A : UU i} {B : UU j} (e : A ≃ B) →
  Id (inv-equiv (inv-equiv e)) e
inv-inv-equiv (pair f (pair (pair g G) (pair h H))) = eq-htpy-equiv refl-htpy

is-equiv-inv-equiv :
  {i j : Level} {A : UU i} {B : UU j} → is-equiv (inv-equiv {A = A} {B = B})
is-equiv-inv-equiv =
  is-equiv-has-inverse
    ( inv-equiv)
    ( inv-inv-equiv)
    ( inv-inv-equiv)

equiv-inv-equiv :
  {i j : Level} {A : UU i} {B : UU j} → (A ≃ B) ≃ (B ≃ A)
equiv-inv-equiv = pair inv-equiv is-equiv-inv-equiv

is-contr-total-equiv' : {i : Level} (A : UU i) →
  is-contr (Σ (UU i) (λ X → X ≃ A))
is-contr-total-equiv' A =
  is-contr-equiv
    ( Σ (UU _) (λ X → A ≃ X))
    ( equiv-tot (λ X → equiv-inv-equiv))
    ( is-contr-total-equiv A)

abstract
  Ind-equiv : {i j : Level} (A : UU i) (P : (B : UU i) (e : A ≃ B) → UU j) →
    sec (ev-id P)
  Ind-equiv A P =
    IND-EQUIV-is-contr-total-equiv A
    ( is-contr-total-equiv A)
    ( λ t → P (pr1 t) (pr2 t))

ind-equiv : {i j : Level} (A : UU i) (P : (B : UU i) (e : A ≃ B) → UU j) →
  P A (equiv-id A) → {B : UU i} (e : A ≃ B) → P B e
ind-equiv A P p {B} = pr1 (Ind-equiv A P) p B

-- Subuniverses

is-subuniverse :
  {l1 l2 : Level} (P : UU l1 → UU l2) → UU ((lsuc l1) ⊔ l2)
is-subuniverse P = is-subtype P

subuniverse :
  (l1 l2 : Level) → UU ((lsuc l1) ⊔ (lsuc l2))
subuniverse l1 l2 = Σ (UU l1 → UU l2) is-subuniverse

{- By univalence, subuniverses are closed under equivalences. -}
in-subuniverse-equiv :
  {l1 l2 : Level} (P : UU l1 → UU l2) {X Y : UU l1} → X ≃ Y → P X → P Y
in-subuniverse-equiv P e = tr P (eq-equiv _ _ e)

in-subuniverse-equiv' :
  {l1 l2 : Level} (P : UU l1 → UU l2) {X Y : UU l1} → X ≃ Y → P Y → P X
in-subuniverse-equiv' P e = tr P (inv (eq-equiv _ _ e))

total-subuniverse :
  {l1 l2 : Level} (P : subuniverse l1 l2) → UU ((lsuc l1) ⊔ l2)
total-subuniverse {l1} P = Σ (UU l1) (pr1 P)

{- We also introduce the notion of 'global subuniverse'. The handling of
   universe levels is a bit more complicated here, since (l : Level) → A l are
   kinds but not types. -}

is-global-subuniverse :
  (α : Level → Level) (P : (l : Level) → subuniverse l (α l)) →
  (l1 l2 : Level) → UU _
is-global-subuniverse α P l1 l2 =
  (X : UU l1) (Y : UU l2) → X ≃ Y → (pr1 (P l1)) X → (pr1 (P l2)) Y

{- Next we characterize the identity type of a subuniverse. -}

Eq-total-subuniverse :
  {l1 l2 : Level} (P : subuniverse l1 l2) →
  (s t : total-subuniverse P) → UU l1
Eq-total-subuniverse (pair P H) (pair X p) t = X ≃ (pr1 t)

Eq-total-subuniverse-eq :
  {l1 l2 : Level} (P : subuniverse l1 l2) →
  (s t : total-subuniverse P) → Id s t → Eq-total-subuniverse P s t
Eq-total-subuniverse-eq (pair P H) (pair X p) .(pair X p) refl = equiv-id X

abstract
  is-contr-total-Eq-total-subuniverse :
    {l1 l2 : Level} (P : subuniverse l1 l2)
    (s : total-subuniverse P) →
    is-contr (Σ (total-subuniverse P) (λ t → Eq-total-subuniverse P s t))
  is-contr-total-Eq-total-subuniverse (pair P H) (pair X p) =
    is-contr-total-Eq-substructure (is-contr-total-equiv X) H X (equiv-id X) p

abstract
  is-equiv-Eq-total-subuniverse-eq :
    {l1 l2 : Level} (P : subuniverse l1 l2)
    (s t : total-subuniverse P) → is-equiv (Eq-total-subuniverse-eq P s t)
  is-equiv-Eq-total-subuniverse-eq (pair P H) (pair X p) =
    fundamental-theorem-id
      ( pair X p)
      ( equiv-id X)
      ( is-contr-total-Eq-total-subuniverse (pair P H) (pair X p))
      ( Eq-total-subuniverse-eq (pair P H) (pair X p))

eq-Eq-total-subuniverse :
  {l1 l2 : Level} (P : subuniverse l1 l2) →
  {s t : total-subuniverse P} → Eq-total-subuniverse P s t → Id s t
eq-Eq-total-subuniverse P {s} {t} =
  inv-is-equiv (is-equiv-Eq-total-subuniverse-eq P s t)

-- Section 12.2 Univalence implies function extensionality

is-equiv-postcomp-univalence :
  {l1 l2 : Level} {X Y : UU l1} (A : UU l2) (e : X ≃ Y) →
  is-equiv (postcomp A (map-equiv e))
is-equiv-postcomp-univalence {X = X} A =
  ind-equiv X
    ( λ Y e → is-equiv (postcomp A (map-equiv e)))
    ( is-equiv-id (A → X))

weak-funext-univalence :
  {l : Level} {A : UU l} {B : A → UU l} → WEAK-FUNEXT A B
weak-funext-univalence {A = A} {B} is-contr-B =
  is-contr-retract-of
    ( fib (postcomp A (pr1 {B = B})) id)
    ( pair
      ( λ f → pair (λ x → pair x (f x)) refl)
      ( pair
        ( λ h x → tr B (htpy-eq (pr2 h) x) (pr2 (pr1 h x)))
        ( refl-htpy)))
    ( is-contr-map-is-equiv
      ( is-equiv-postcomp-univalence A (equiv-pr1 is-contr-B))
      ( id))

funext-univalence :
  {l : Level} {A : UU l} {B : A → UU l} (f : (x : A) → B x) → FUNEXT f
funext-univalence {A = A} {B} f =
  FUNEXT-WEAK-FUNEXT (λ A B → weak-funext-univalence) A B f

-- Exercises

-- Exercise 10.1

tr-equiv-eq-ap : {l1 l2 : Level} {A : UU l1} {B : A → UU l2} {x y : A}
  (p : Id x y) → (map-equiv (equiv-eq (ap B p))) ~ tr B p
tr-equiv-eq-ap refl = refl-htpy

-- Exercise 10.2

subuniverse-is-contr :
  {i : Level} → subuniverse i i
subuniverse-is-contr {i} = pair is-contr is-subtype-is-contr

unit' :
  (i : Level) → UU i
unit' i = pr1 (Raise i unit)

abstract
  is-contr-unit' :
    (i : Level) → is-contr (unit' i)
  is-contr-unit' i =
    is-contr-equiv' unit (pr2 (Raise i unit)) is-contr-unit

abstract
  center-UU-contr :
    (i : Level) → total-subuniverse (subuniverse-is-contr {i})
  center-UU-contr i =
    pair (unit' i) (is-contr-unit' i)

  contraction-UU-contr :
    {i : Level} (A : Σ (UU i) is-contr) →
    Id (center-UU-contr i) A
  contraction-UU-contr (pair A is-contr-A) =
    eq-Eq-total-subuniverse subuniverse-is-contr
      ( equiv-is-contr (is-contr-unit' _) is-contr-A)

abstract
  is-contr-UU-contr : (i : Level) → is-contr (Σ (UU i) is-contr)
  is-contr-UU-contr i =
    pair (center-UU-contr i) (contraction-UU-contr)

is-trunc-UU-trunc :
  (k : 𝕋) (i : Level) → is-trunc (succ-𝕋 k) (Σ (UU i) (is-trunc k))
is-trunc-UU-trunc k i X Y =
  is-trunc-is-equiv k
    ( Id (pr1 X) (pr1 Y))
    ( ap pr1)
    ( is-emb-pr1-is-subtype
      ( is-prop-is-trunc k) X Y)
    ( is-trunc-is-equiv k
      ( (pr1 X) ≃ (pr1 Y))
      ( equiv-eq)
      ( univalence (pr1 X) (pr1 Y))
      ( is-trunc-equiv-is-trunc k (pr2 X) (pr2 Y)))

ev-true-false :
  {l : Level} (A : UU l) → (f : bool → A) → A × A
ev-true-false A f = pair (f true) (f false)

map-universal-property-bool :
  {l : Level} {A : UU l} →
  A × A → (bool → A)
map-universal-property-bool (pair x y) true = x
map-universal-property-bool (pair x y) false = y

issec-map-universal-property-bool :
  {l : Level} {A : UU l} →
  ((ev-true-false A) ∘ map-universal-property-bool) ~ id
issec-map-universal-property-bool (pair x y) =
  eq-pair-triv (pair refl refl)

isretr-map-universal-property-bool' :
  {l : Level} {A : UU l} (f : bool → A) →
  (map-universal-property-bool (ev-true-false A f)) ~ f
isretr-map-universal-property-bool' f true = refl
isretr-map-universal-property-bool' f false = refl

isretr-map-universal-property-bool :
  {l : Level} {A : UU l} →
  (map-universal-property-bool ∘ (ev-true-false A)) ~ id
isretr-map-universal-property-bool f =
  eq-htpy (isretr-map-universal-property-bool' f)

universal-property-bool :
  {l : Level} (A : UU l) →
  is-equiv (λ (f : bool → A) → pair (f true) (f false))
universal-property-bool A =
  is-equiv-has-inverse
    map-universal-property-bool
    issec-map-universal-property-bool
    isretr-map-universal-property-bool

ev-true :
  {l : Level} {A : UU l} → (bool → A) → A
ev-true f = f true

triangle-ev-true :
  {l : Level} (A : UU l) →
  (ev-true) ~ (pr1 ∘ (ev-true-false A))
triangle-ev-true A = refl-htpy

aut-bool-bool :
  bool → (bool ≃ bool)
aut-bool-bool true = equiv-id bool
aut-bool-bool false = equiv-neg-𝟚

bool-aut-bool :
  (bool ≃ bool) → bool
bool-aut-bool e = map-equiv e true

decide-true-false :
  (b : bool) → coprod (Id b true) (Id b false)
decide-true-false true = inl refl
decide-true-false false = inr refl

eq-false :
  (b : bool) → (¬ (Id b true)) → (Id b false)
eq-false true p = ind-empty (p refl)
eq-false false p = refl

eq-true :
  (b : bool) → (¬ (Id b false)) → Id b true
eq-true true p = refl
eq-true false p = ind-empty (p refl)

Eq-𝟚-eq : (x y : bool) → Id x y → Eq-𝟚 x y
Eq-𝟚-eq x .x refl = reflexive-Eq-𝟚 x

eq-false-equiv' :
  (e : bool ≃ bool) → Id (map-equiv e true) true →
  is-decidable (Id (map-equiv e false) false) → Id (map-equiv e false) false
eq-false-equiv' e p (inl q) = q
eq-false-equiv' e p (inr x) =
  ind-empty
    ( Eq-𝟚-eq true false
      ( ap pr1
        ( is-prop-is-contr'
          ( is-contr-map-is-equiv (is-equiv-map-equiv e) true)
          ( pair true p)
          ( pair false (eq-true (map-equiv e false) x)))))

{-
eq-false-equiv :
  (e : bool ≃ bool) → Id (map-equiv e true) true → Id (map-equiv e false) false
eq-false-equiv e p =
  eq-false-equiv' e p (has-decidable-equality-𝟚 (map-equiv e false) false)
-}

{-
eq-true-equiv :
  (e : bool ≃ bool) →
  ¬ (Id (map-equiv e true) true) → Id (map-equiv e false) true
eq-true-equiv e f = {!!}

issec-bool-aut-bool' :
  ( e : bool ≃ bool) (d : is-decidable (Id (map-equiv e true) true)) →
  htpy-equiv (aut-bool-bool (bool-aut-bool e)) e
issec-bool-aut-bool' e (inl p) true =
  ( htpy-equiv-eq (ap aut-bool-bool p) true) ∙ (inv p)
issec-bool-aut-bool' e (inl p) false =
  ( htpy-equiv-eq (ap aut-bool-bool p) false) ∙
  ( inv (eq-false-equiv e p))
issec-bool-aut-bool' e (inr f) true =
  ( htpy-equiv-eq
    ( ap aut-bool-bool (eq-false (map-equiv e true) f)) true) ∙
  ( inv (eq-false (map-equiv e true) f))
issec-bool-aut-bool' e (inr f) false =
  ( htpy-equiv-eq (ap aut-bool-bool {!eq-true-equiv e ?!}) {!!}) ∙
  ( inv {!!})

issec-bool-aut-bool :
  (aut-bool-bool ∘ bool-aut-bool) ~ id
issec-bool-aut-bool e =
  eq-htpy-equiv
    ( issec-bool-aut-bool' e
      ( has-decidable-equality-𝟚 (map-equiv e true) true))
-}


module Structure.Relator.Function.Multi where

open import Data.Tuple.RaiseTypeᵣ
import      Data.Tuple.RaiseTypeᵣ.Functions as RaiseType
open import Function.Multi
import      Function.Multi.Functions as Multi
import      Lvl
import      Lvl.MultiFunctions as Lvl
open import Logic
open import Logic.Predicate
open import Logic.Predicate.Multi
open import Functional
open import Numeral.Natural
import      Structure.Function.Multi as Multi
open import Structure.Setoid
open import Type

private variable ℓ ℓₑ : Lvl.Level

module _ (n : ℕ) {ℓ𝓈}{ℓ₁ ℓ₂} {Xs : Types{n}(ℓ𝓈)}{Y : Type{ℓ₁}} where
  module Names where
    -- A relator is total when every LHS have at least one RHS in which the relation holds.
    Total : (Xs ⇉ (Y → Stmt{ℓ₂})) → Stmt
    Total(φ) = ∀₊(n)(Multi.compose(n) ∃ φ)

    module _ ⦃ equiv-Y : Equiv{ℓₑ}(Y) ⦄ where
      -- A relator is a function when every LHS have at least one RHS in which the relation holds.
      Function : ∀{ℓ𝓈ₑ} → (RaiseType.mapWithLvls(\X ℓₑ → Equiv{ℓₑ}(X)) Xs ℓ𝓈ₑ) ⇉ᵢₙₛₜ ((Xs ⇉ (Y → Type{ℓ₂})) → Stmt)
      Function = Multi.expl-to-inst(n) (Multi.compose(n) (_$₂_) (Multi.inst-to-expl(n) (Multi.Names.RelationReplacement(f ↦ g ↦ ∀{y₁ y₂} → f(y₁) → g(y₂) → (y₁ ≡ y₂))(n))))

  record Total(φ : Xs ⇉ (Y → Stmt{ℓ₂})) : Stmt{ℓ₁ Lvl.⊔ ℓ₂ Lvl.⊔ (Lvl.⨆ ℓ𝓈)} where
    constructor intro
    field proof : Names.Total(φ)


module Logic.WeaklyClassical where

import      Lvl
open import Functional
open import Logic.Names
open import Logic.Predicate
open import Logic.Propositional
open import Logic.Propositional.Theorems
open import Type

private variable ℓ : Lvl.Level
private variable T X Y : Type{ℓ}
private variable P : T → Type{ℓ}

-- TODO: What should (¬¬ P → P) for a P be called? DoubleNegation? Stable proposition?

[⊤]-doubleNegation : DoubleNegationOn(⊤)
[⊤]-doubleNegation = const [⊤]-intro

[⊥]-doubleNegation : DoubleNegationOn(⊥)
[⊥]-doubleNegation = apply id

[¬]-doubleNegation : DoubleNegationOn(¬ X)
[¬]-doubleNegation = _∘ apply

[∧]-doubleNegation : DoubleNegationOn(X) → DoubleNegationOn(Y) → DoubleNegationOn(X ∧ Y)
[∧]-doubleNegation nnxx nnyy nnxy = [∧]-intro (nnxx(nx ↦ nnxy(nx ∘ [∧]-elimₗ))) (nnyy(ny ↦ nnxy(ny ∘ [∧]-elimᵣ)))

[→]-doubleNegation : DoubleNegationOn(Y) → DoubleNegationOn(X → Y)
[→]-doubleNegation nnyy nnxy x = nnyy(ny ↦ nnxy(xy ↦ (ny ∘ xy) x))

[∀]-doubleNegation-distribute : (∀{x} → DoubleNegationOn(P(x))) → DoubleNegationOn(∀{x} → P(x))
[∀]-doubleNegation-distribute annp nnap = annp(npx ↦ nnap(npx $_))

doubleNegation-to-callCC : DoubleNegationOn(X) → DoubleNegationOn(Y) → (((X → Y) → X) → X)
doubleNegation-to-callCC dx dy xyx = dx(nx ↦ nx(xyx(dy ∘ const ∘ nx)))


module Function.Iteration.Order where

open import Data
open import Data.Boolean.Stmt
open import Functional renaming (id to id-fn)
open import Function.Iteration hiding (_^_)
open import Function.Iteration.Proofs
open import Logic
open import Logic.IntroInstances
open import Logic.Propositional
open import Logic.Propositional.Theorems
open import Logic.Predicate
import      Lvl
open import Numeral.Natural
open import Numeral.Natural.Oper hiding (_^_)
open import Numeral.Natural.Oper.Comparisons
open import Numeral.Natural.Oper.DivMod.Proofs
open import Numeral.Natural.Oper.FlooredDivision
open import Numeral.Natural.Oper.Modulo
open import Numeral.Natural.Oper.Modulo.Proofs
open import Numeral.Natural.Relation.Divisibility
open import Numeral.Natural.Relation.Divisibility.Proofs
open import Numeral.Natural.Relation.Order.Decidable
open import Numeral.Natural.Relation.Order.Proofs
open import Numeral.Natural.Relation.Order
open import Relator.Equals using () renaming (_≡_ to _≡ₑ_ ; _≢_ to _≢ₑ_ ; [≡]-intro to [≡ₑ]-intro)
open import Structure.Setoid
open import Structure.Function.Domain
open import Structure.Function
open import Structure.Operator.Properties
open import Structure.Operator.Proofs
open import Structure.Operator
open import Structure.Relator.Properties
open import Structure.Relator.Ordering
open import Structure.Relator.Ordering.Lattice
open import Syntax.Transitivity
open import Type
open import Type.Properties.Decidable.Proofs
open import Type.Properties.Empty
open import Type.Size.Finite

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

module _ ⦃ equiv-T : Equiv{ℓₑ}(T) ⦄ (_▫_ : T → T → T) ⦃ op : BinaryOperator(_▫_) ⦄ {id} ⦃ ident : Identity(_▫_)(id) ⦄ ⦃ assoc : Associativity(_▫_) ⦄ where
  -- Operator alias for iterated application of an operator with an element.
  _^_ : T → ℕ → T
  x ^ n = Function.Iteration.repeatₗ(n)(_▫_)(id)(x)

  -- `FiniteOrder(x)(n)` means that the element `x` is of order `n`.
  -- It is finite in the sense that it is a number and not infinite.
  -- An element's order is the smallest positive integer power of x such that the result is the identity element.
  data FiniteOrder (x : T) : ℕ → Stmt{ℓₑ} where
    intro : ∀{n} → LE.Minimum(_≤_)(n ↦ x ^ 𝐒(n) ≡ id)(n) → FiniteOrder(x)(𝐒(n))

  -- `Ord(x)` means that the element `x` has a finite order.
  Ord : T → Stmt
  Ord(x) = ∃(FiniteOrder(x))

  -- `ord(x)` is the order of x (when it is finite).
  ord : (x : T) → ⦃ _ : Ord(x) ⦄ → ℕ
  ord(_) ⦃ [∃]-intro n ⦄ = n

  module _ {x : T} where
    -- An order is never 0 by definition.
    finite-order-0 : ¬ FiniteOrder(x)(𝟎)
    finite-order-0 ()

    -- An element power its order is the identity element.
    [^]-by-ord : ⦃ p : Ord(x) ⦄ → (x ^ ord(x) ⦃ p ⦄ ≡ id)
    [^]-by-ord ⦃ [∃]-intro (𝐒(_)) ⦃ intro p ⦄ ⦄ = LE.Minimum.membership{_≤_ = _≤_}(p)

    -- When an element power something is the identity element, then the power is either zero or greater/equal its order.
    ord-is-minimum : ⦃ p : Ord(x) ⦄ → ∀{n} → (x ^ n ≡ id) → (n ≡ₑ 𝟎) ∨ (ord(x) ⦃ p ⦄ ≤ n)
    ord-is-minimum ⦃ [∃]-intro (_)     ⦃ intro p ⦄ ⦄      {𝟎}   x0id  = [∨]-introₗ [≡ₑ]-intro
    ord-is-minimum ⦃ [∃]-intro .(𝐒 po) ⦃ intro {po} p ⦄ ⦄ {𝐒 n} xsnid = [∨]-introᵣ ([≤]-with-[𝐒] ⦃ LE.Minimum.proof{_≤_ = _≤_}(p) ⦃ xsnid ⦄ ⦄)

    -- When an element power something less than its order and it is the identity element, then the power is 0.
    ord-is-minimum-but-0 : ⦃ p : Ord(x) ⦄ → ∀{n} → (x ^ n ≡ id) → (n < ord(x) ⦃ p ⦄) → (n ≡ₑ 𝟎)
    ord-is-minimum-but-0 ⦃ p ⦄ {𝟎}    _    _    = [≡ₑ]-intro
    ord-is-minimum-but-0 ⦃ p ⦄ {𝐒(n)} xnid nord with ord-is-minimum ⦃ p ⦄ {𝐒(n)} xnid
    ... | [∨]-introᵣ ordsn = [⊥]-elim([<]-to-[≱] nord (ordsn))

    -- An order is never 0.
    ord-non-zero : ⦃ p : Ord(x) ⦄ → (ord(x) ⦃ p ⦄ ≢ₑ 𝟎)
    ord-non-zero ⦃ [∃]-intro 𝟎 ⦃ ⦄ ⦄ [≡ₑ]-intro

    -- Iteration (_^_) distributes to the right.
    -- Can also be seen as iteration preserving from addition to the operation.
    [^]-by-add : ∀{a b} → ((x ^ a) ▫ (x ^ b) ≡ x ^ (a + b))
    [^]-by-add {a}{b} = repeatₗ-by-sum {X = T}{_▫_}{x}{id}{a}{b}

    -- Nested iterations can join to be a product.
    [^]-by-product : ∀{a b} → ((((x ^ a)) ^ b) ≡ x ^ (a ⋅ b))
    [^]-by-product {a}{b} = repeatₗ-by-product {X = T}{_▫_}{x}{id}{a}{b}

    -- When powering a difference yields the identity element, the powered elements are the same.
    [^]-by-distanceₗ : ∀{a b} → (x ^ a ≡ x ^ b) ← (x ^ (a 𝄩 b) ≡ id)
    [^]-by-distanceₗ {a}{b} = repeatₗ-by-distanceₗ {X = T}{_▫_}{x}{id}{a}{b}

    -- Theorems where `n` is a power which yields an identity element.
    module _ {n} (n-id : (x ^ n ≡ id)) where
      -- Adding something to `n` is only leaving the something behind.
      [^]-by-id-add : ∀{a} → (x ^ (n + a)  ≡ x ^ a)
      [^]-by-id-add {a} =
        x ^ (n + a)       🝖-[ symmetry(_≡_) ([^]-by-add {n}{a}) ]
        (x ^ n) ▫ (x ^ a) 🝖-[ congruence₂ₗ(_▫_)(_) n-id ]
        id ▫ (x ^ a)      🝖-[ identityₗ(_▫_)(id) ]
        x ^ a             🝖-end

      -- Multiplying something by `n` is still the identity element.
      [^]-by-id-multiple : ∀{a} → (x ^ (n ⋅ a) ≡ id)
      [^]-by-id-multiple {𝟎}    = repeatₗ-by-0 {X = T}{_▫_}{x}{id}
      [^]-by-id-multiple {𝐒(a)} =
        x ^ (n + (n ⋅ a))       🝖-[ symmetry(_≡_) ([^]-by-add {n}{n ⋅ a}) ]
        (x ^ n) ▫ (x ^ (n ⋅ a)) 🝖-[ congruence₂(_▫_) n-id ([^]-by-id-multiple {a}) ]
        id ▫ id                 🝖-[ identityₗ(_▫_)(id) ]
        id                      🝖-end

    -- A power yields an identity element only when it is a multiple of the order of the element.
    [^]-id-when-div : ⦃ p : Ord(x) ⦄ → ∀{n} → (x ^ n ≡ id) ↔ (ord(x) ⦃ p ⦄ ∣ n)
    [^]-id-when-div ⦃ p ⦄ {n} = [↔]-intro (l{n}) (r{n}) where
      l : ∀{n} → (x ^ n ≡ id) ← (ord(x) ⦃ p ⦄ ∣ n)
      l {.0}                  Div𝟎 = repeatₗ-by-0 {X = T}{_▫_}{x}{id}
      l {.(ord(x) ⦃ p ⦄ + n)} (Div𝐒 {_}{n} div) =
        x ^ (ord x ⦃ p ⦄ + n)       🝖-[ symmetry(_≡_) ([^]-by-add {ord(x) ⦃ p ⦄}{n}) ]
        (x ^ ord x ⦃ p ⦄) ▫ (x ^ n) 🝖-[ congruence₂(_▫_) ([^]-by-ord ⦃ p ⦄) (l{n} div) ]
        id ▫ id                     🝖-[ identityₗ(_▫_)(id) ]
        id                          🝖-end

      r : ∀{n} → (x ^ n ≡ id) → (ord(x) ⦃ p ⦄ ∣ n)
      r {𝟎}    _    = Div𝟎
      r {𝐒(n)} xnid = [↔]-to-[→] mod-divisibility mod-is-0 where
        open import Relator.Equals.Proofs.Equiv using ([≡]-to-equivalence)

        instance
          ord-non-zero-comp : IsTrue(ord(x) ⦃ p ⦄ ≢? 𝟎)
          ord-non-zero-comp = [↔]-to-[→] decider-true (ord-non-zero ⦃ p ⦄)

        instance
          ord-positive : IsTrue(positive?(ord(x) ⦃ p ⦄))
          ord-positive with ord(x) ⦃ p ⦄ | [∃]-proof p
          ... | 𝟎   | pp = finite-order-0 pp
          ... | 𝐒 _ | _  = <>

        instance
          ord-n-ineq : ord(x) ⦃ p ⦄ ≤ 𝐒(n)
          ord-n-ineq with ord-is-minimum ⦃ p ⦄ {𝐒(n)} xnid
          ord-n-ineq | [∨]-introₗ ()
          ord-n-ineq | [∨]-introᵣ proof      = proof

        mod-is-id : x ^ (𝐒(n) mod ord(x) ⦃ p ⦄) ≡ id
        mod-is-id =
          x ^ (𝐒(n) mod ord(x) ⦃ p ⦄)                                                      🝖-[ symmetry(_≡_) (identityₗ(_▫_)(id)) ]
          id ▫ (x ^ (𝐒(n) mod ord(x) ⦃ p ⦄))                                               🝖-[ congruence₂ₗ(_▫_)(_) (symmetry(_≡_) ([^]-by-id-multiple {ord(x) ⦃ p ⦄} ([^]-by-ord ⦃ p ⦄) {𝐒(n) ⌊/⌋ ord(x) ⦃ p ⦄})) ]
          (x ^ ((ord(x) ⦃ p ⦄) ⋅ (𝐒(n) ⌊/⌋ ord(x) ⦃ p ⦄))) ▫ (x ^ (𝐒(n) mod ord(x) ⦃ p ⦄)) 🝖-[ [^]-by-add {(ord(x) ⦃ p ⦄) ⋅ (𝐒(n) ⌊/⌋ ord(x) ⦃ p ⦄)} {𝐒(n) mod ord(x) ⦃ p ⦄} ]
          x ^ (((ord(x) ⦃ p ⦄) ⋅ (𝐒(n) ⌊/⌋ ord(x) ⦃ p ⦄)) + (𝐒(n) mod ord(x) ⦃ p ⦄))       🝖-[ congruence₁(x ^_) ⦃ Relator.Equals.Proofs.Equiv.[≡]-to-function ⦄ ([≡]-to-equivalence([⌊/⌋][mod]-is-division-with-remainder-pred-commuted{𝐒(n)}{ord(x) ⦃ p ⦄})) ]
          x ^ 𝐒(n)                                                                         🝖-[ xnid ]
          id                                                                               🝖-end

        mod-is-0 : 𝐒(n) mod ord(x) ⦃ p ⦄ ≡ 𝟎
        mod-is-0 = ord-is-minimum-but-0 ⦃ p ⦄ mod-is-id mod-maxᵣ

    module _ {inv} ⦃ _ : InverseFunctionᵣ(_▫_)(inv) ⦄ where
      [^]-by-distanceᵣ : ∀{a b} → (x ^ a ≡ x ^ b) → (x ^ (a 𝄩 b) ≡ id)
      [^]-by-distanceᵣ {a}{b} = repeatₗ-by-distanceᵣ{X = T}{_▫_}{x}{id} ⦃ cancᵣ = One.cancellationᵣ-by-associativity-inverse ⦄ {a}{b}

      [^]-equal-[𝄩] : ⦃ p : Ord(x) ⦄ → ∀{a b} → (x ^ a ≡ x ^ b) ↔ (ord(x) ⦃ p ⦄ ∣ (a 𝄩 b))
      [^]-equal-[𝄩] ⦃ p ⦄ {a}{b} = [↔]-transitivity ([↔]-intro ([^]-by-distanceₗ{a}{b}) ([^]-by-distanceᵣ{a}{b})) ([^]-id-when-div ⦃ p ⦄)

      postulate [^]-cancellationₗ : ⦃ p : Ord(x) ⦄ → ∀{a b} → ⦃ a < ord(x) ⦃ p ⦄ ⦄ → ⦃ b < ord(x) ⦃ p ⦄ ⦄ → (x ^ a ≡ x ^ b) → (a ≡ₑ b)

  -- ord(x ^ n) ≡ ord(x) / gcd(n)(ord(x))
  -- (x ▫ y ≡ y ▫ x) → ord(x ▫ y) ∣ lcm(ord(x))(ord(y))
  -- (∀{x} → (ord(x) ≡ 2)) → Commutativity(_▫_)

  -- One element in the group can "generate" any element element in the group by repeated application of the operator.
  Generator : T → Stmt
  Generator(x) = Surjective(x ^_)

  -- A group is cyclic when there is an element that can generate it.
  Cyclic : Stmt
  Cyclic = ∃(Generator)

  {- TODO: Because the thing exists, there is finitely many. Search for the first one
  finite-order-from-dec-existence : ⦃ Decidable(_≡_) ⦄ → ∀{a} → ∃(n ↦ (a ^ 𝐒(n) ≡ id)) → Ord(a)
  finite-order-from-dec-existence {n} asnid = {!!}
  -- intro (Weak.Properties.intro ⦃ asnid ⦄ {!!})

  -- TODO: Assume decidable equality and use finite-order-from-dec-existence above. Existence should follow from finiteness
  finite-have-order : ⦃ Finite(T) ⦄ → ∀{a} → Ord(a)
  ∃.witness (finite-have-order ⦃ [∃]-intro size ⦃ fin-bij ⦄ ⦄) = size
  ∃.proof (finite-have-order ⦃ [∃]-intro 𝟎 ⦃ [∃]-intro fin-bij ⦄ ⦄ {a = a}) with fin-bij a
  ... | ()
  ∃.proof (finite-have-order ⦃ [∃]-intro (𝐒(size)) ⦃ fin-bij ⦄ ⦄) = intro {!!}
  -}

  postulate cyclic-commutative : ⦃ Cyclic ⦄ → Commutativity(_▫_)
  -- generator-order-size : ⦃ Finite(T) ⦄ → ∀{a} → ⦃ p : Ord(a) ⦄ → Generator(a) ↔ ((# T) ≡ₑ ord(a) ⦃ p ⦄)
  -- cyclic-order-size : ⦃ Finite(T) ⦄ → ⦃ Cyclic ⦄ ↔ ∃(a ↦ (# T) ≡ₑ ord(a))

  -- generator-of-power : Generator(a ^ k) ↔ Generator(a ^ gcd(ord(a))(k))
  -- order-of-power : ord(a ^ k) ∣ ord(a) / gcd(ord(a),k)

{-
  module _ {id} ⦃ ident : Identity(_▫_)(id) ⦄ where
    open import Data.Boolean
    open import Data.Boolean.Stmt
    import      Function.Iteration
    open import Logic.Computability
    open import Logic.Computability.Binary renaming (ComputablyDecidable to ComputablyDecidable2)
    open import Logic

    open import Numeral.Natural.Relation.Computability

-}
    {-
    boundedMinOr : ℕ → (ℕ → Bool) → ℕ → ℕ
    boundedMinOr 𝟎          p default = default
    boundedMinOr (𝐒(bound)) p default = if p(bound) then bound else (boundedMinOr bound p default)

    boundedMinOr-proof : ∀{p : ℕ → Bool}{bound default : ℕ} → IsTrue(p(default)) → IsTrue(p(boundedMinOr bound p default))

    min-by-bruteforce : ∀{ℓ}{P : ℕ → Stmt{ℓ}} → ⦃ comp : ComputablyDecidable(P) ⦄ → ⦃ e : ∃(P) ⦄ → ∃(Weak.Properties.MinimumOf(_≤_)(P))
    ∃.witness (min-by-bruteforce {P = P} ⦃ comp ⦄ ⦃ e ⦄) = boundedMinOr([∃]-witness(e)) (ComputablyDecidable.decide(comp)) ([∃]-witness(e))
    Weak.Properties.MinimumOf.proof (∃.proof min-by-bruteforce) {x} ⦃ x₁ ⦄ = {!!}

    ord : (x : T) → ⦃ e : FiniteOrder(x) ⦄ → ⦃ comp : ComputablyDecidable2{X = T}(_≡_) ⦄ → ℕ
    ord(x) ⦃ e ⦄ ⦃ comp ⦄ = Weak.minOf(_≤_)(n ↦ x ^ 𝐒(n) ≡ id) ⦃ min-by-bruteforce ⦃ {!!} ⦄ ⦃ e ⦄ ⦄
    -}


module Data.Union where

open import Data.List using (List; _∷_; [_]; [])
open import Data.List.Relation.Unary.Any using (here; there; _─_)
open import Data.List.Membership.Propositional using (_∈_)

open import Data.Maybe using (Maybe; just; nothing; _>>=_)

open import Level using (Level; _⊔_)

open import Function using (_∘_)

-- ----------------------------------------------------------------------
-- Definition
--
-- A union of the family B defined by the list of tags ts
-- of type A

data Union {a b} (A : Set a) (B : A → Set b) : List A → Set (a ⊔ b) where
  here : ∀ {t ts} → B t → Union A B (t ∷ ts)
  there : ∀ {t ts} → Union A B ts → Union A B (t ∷ ts)

-- ----------------------------------------------------------------------
-- Injection and projections to (and from) unions

private
  variable
    a b : Level
    A : Set a
    B : A → Set b
    t : A
    ts ts′ : List A

inj : t ∈ ts → B t → Union A B ts
inj (here t≡t′) x rewrite t≡t′ = here x
inj (there t∈ts) x = there (inj t∈ts x)

`_ : ⦃ t ∈ ts ⦄ → B t → Union A B ts
`_ ⦃ t∈ts ⦄ x = inj t∈ts x

proj : t ∈ ts → Union A B ts → Maybe (B t)
proj (here t≡t′) (here x) rewrite t≡t′ = just x
proj (there t∈ts) (here x) = nothing
proj (here t≡t′) (there x) = nothing
proj (there t∈ts) (there x) = proj t∈ts x

`proj : ⦃ t ∈ ts ⦄ → Union A B ts → Maybe (B t)
`proj ⦃ t∈ts ⦄ x = proj t∈ts x

proj₀ : Union A B [ t ] → B t
proj₀ (here x) = x

remove : Union A B ts → (i : t ∈ ts) → Maybe (Union A B (ts ─ i))
remove (here x) (here _) = nothing
remove (here x) (there _) = just (here x)
remove (there x) (here _) = just x
remove (there x) (there t∈ts) = remove x t∈ts >>= just ∘ there


module container.m.extensionality where

open import sum
open import equality
open import container.core
open import container.fixpoint
open import container.equality
open import container.m.core

module Extensionality {li la lb}(c : Container li la lb) where
  private
    module C where
      open Definition c public

      fp : Fixpoint c _
      fp = fix M fixpoint

      open Fixpoint fp public
        using (head; tail)

    module S where
      open Equality c C.fp
        using (equality)
      open Equality c C.fp public
        using (substX)
      open Definition equality public

      fp : Fixpoint equality _
      fp = fix M fixpoint

      open Fixpoint fp public
        using (head; tail)

    open C

  -- bisimilarity relation
  _≡M_ : ∀ {i}(u v : M i) → Set _
  u ≡M v = S.M (_ , u , v)

  reflM : ∀ {i}{u : M i} → u ≡M u
  reflM = S.inf refl (λ b → ♯ reflM)

  private
    -- total space of bisimilarity
    E : ∀ i → Set _
    E i = Σ (M i × M i) (uncurry _≡M_)

    f : E →ⁱ F E
    f i ((xs , ys) , bisim)
      = head xs
      , (λ b → (( tail xs b
                , S.substX (S.head bisim) b
                           (tail ys (subst B (S.head bisim) b)))
                , S.tail bisim b))

    π₁ : E →ⁱ M
    π₁ i ((xs , _), _) = xs

    π₁-mor : ∀ {i} (e : E i) → out i (π₁ i e) ≡ imap π₁ i (f i e)
    π₁-mor ((xs , ys) , p) = refl

    π₂ : E →ⁱ M
    π₂ _ ((_ , ys), _) = ys

    π₂-mor : ∀ {i} (e : E i) → out i (π₂ i e) ≡ imap π₂ i (f i e)
    π₂-mor {i} ((xs , ys) , bisim) = lem (S.head bisim) (tail ys)
      where
        lem : {a a' : A i}(p : a ≡ a')
            → (f : (b' : B a') → M (r b'))
            → _≡_ {A = F M i}
              (a' , f)
              (a , λ b → S.substX p b (f (subst B p b)))
        lem refl f = refl

    equal-π : ∀ {i}(e : E i) → π₁ i e ≡ π₂ i e
    equal-π e = unfold-η f π₁ π₁-mor e · sym (unfold-η f π₂ π₂-mor e)

    abstract
      mext₀ : ∀ {i} {xs ys : M i} → xs ≡M ys → xs ≡ ys
      mext₀ p = equal-π (_ , p)

  mext-inv : ∀ {i}{xs ys : M i} → xs ≡ ys → xs ≡M ys
  mext-inv refl = reflM

  mext : ∀ {i} {xs ys : M i} → xs ≡M ys → xs ≡ ys
  mext p = mext₀ p · sym (mext₀ reflM)

  mext-id : ∀ {i}{u : M i} → mext (reflM {u = u}) ≡ refl
  mext-id = left-inverse (mext₀ reflM)

  mext-retraction : ∀ {i}{xs ys : M i}(p : xs ≡ ys)
                  → mext (mext-inv p) ≡ p
  mext-retraction refl = left-inverse (mext₀ reflM)


module Numeral.Natural.Oper.Proofs.Multiplication where

open import Logic.Propositional
open import Numeral.Natural
open import Numeral.Natural.Oper
open import Numeral.Natural.Oper.Proofs
open import Numeral.Natural.Oper.Proofs.Order
open import Numeral.Natural.Relation
open import Numeral.Natural.Relation.Order
open import Numeral.Natural.Relation.Order.Classical
open import Numeral.Natural.Relation.Order.Proofs
open import Relator.Equals
open import Relator.Equals.Proofs
open import Structure.Operator
import      Structure.Operator.Names as Names
open import Structure.Operator.Properties
open import Structure.Relator.Properties
open import Syntax.Transitivity

instance
  [⋅][−₀]-distributivityᵣ : Distributivityᵣ(_⋅_)(_−₀_)
  Distributivityᵣ.proof([⋅][−₀]-distributivityᵣ) {x}{y}{z} = p{x}{y}{z} where
    p : ∀{x y z : ℕ} → ((x −₀ y) ⋅ z) ≡ (x ⋅ z) −₀ (y ⋅ z)
    p {x} {y} {𝟎} = [≡]-intro
    p {x} {y} {𝐒 z} with [≥]-or-[<] {x}{y}
    ... | [∨]-introₗ gt =
      (x −₀ y) ⋅ 𝐒(z)                 🝖[ _≡_ ]-[]
      (x −₀ y) + ((x −₀ y) ⋅ z)       🝖[ _≡_ ]-[ congruence₂ᵣ(_+_)(x −₀ y) (p {x}{y}{z}) ]
      (x −₀ y) + ((x ⋅ z) −₀ (y ⋅ z)) 🝖[ _≡_ ]-[ [+][−₀]-almost-associativity {x −₀ y} ([≤]-with-[⋅]ᵣ {c = z} gt) ]-sym
      ((x −₀ y) + (x ⋅ z)) −₀ (y ⋅ z) 🝖[ _≡_ ]-[ congruence₂ₗ(_−₀_)(y ⋅ z) (commutativity(_+_) {x −₀ y}{x ⋅ z}) ]
      ((x ⋅ z) + (x −₀ y)) −₀ (y ⋅ z) 🝖[ _≡_ ]-[ congruence₂ₗ(_−₀_)(y ⋅ z) ([+][−₀]-almost-associativity {x ⋅ z} gt) ]-sym
      (((x ⋅ z) + x) −₀ y) −₀ (y ⋅ z) 🝖[ _≡_ ]-[ congruence₂ₗ(_−₀_)(y ⋅ z) (congruence₂ₗ(_−₀_)(y) (commutativity(_+_) {x ⋅ z}{x})) ]
      ((x + (x ⋅ z)) −₀ y) −₀ (y ⋅ z) 🝖[ _≡_ ]-[ [−₀][−₀]-to-[−₀][+] {x + (x ⋅ z)}{y}{y ⋅ z} ]
      (x + (x ⋅ z)) −₀ (y + (y ⋅ z))  🝖[ _≡_ ]-[]
      (x ⋅ 𝐒(z)) −₀ (y ⋅ 𝐒(z))        🝖-end
    ... | [∨]-introᵣ lt =
      (x −₀ y) ⋅ 𝐒(z)                🝖[ _≡_ ]-[ congruence₂ₗ(_⋅_)(𝐒(z)) ([↔]-to-[→] [−₀]-when-0 (sub₂(_<_)(_≤_) lt)) ]
      𝟎 ⋅ 𝐒(z)                       🝖[ _≡_ ]-[ absorberₗ(_⋅_)(𝟎) {𝐒(z)} ]
      𝟎                              🝖[ _≡_ ]-[ [↔]-to-[→] [−₀]-when-0 ([≤]-with-[+] ⦃ sub₂(_<_)(_≤_) lt ⦄ ⦃ [≤]-with-[⋅]ᵣ {c = z} (sub₂(_<_)(_≤_) lt) ⦄) ]-sym
      (x + (x ⋅ z)) −₀ (y + (y ⋅ z)) 🝖-end

-- TODO: This is a specialized distributivity-equivalence-by-commutativity
instance
  [⋅][−₀]-distributivityₗ : Distributivityₗ(_⋅_)(_−₀_)
  Distributivityₗ.proof([⋅][−₀]-distributivityₗ) {x}{y}{z} = p{x}{y}{z} where
    p : ∀{x y z : ℕ} → (x ⋅ (y −₀ z)) ≡ (x ⋅ y) −₀ (x ⋅ z)
    p{x}{y}{z} =
      x ⋅ (y −₀ z)       🝖[ _≡_ ]-[ commutativity(_⋅_) {x}{y −₀ z} ]
      (y −₀ z) ⋅ x       🝖[ _≡_ ]-[ distributivityᵣ(_⋅_)(_−₀_) {y}{z}{x} ]
      (y ⋅ x) −₀ (z ⋅ x) 🝖[ _≡_ ]-[ congruence₂(_−₀_) (commutativity(_⋅_) {y}{x}) (commutativity(_⋅_) {z}{x}) ]
      (x ⋅ y) −₀ (x ⋅ z) 🝖-end

[⋅]-cancellationₗ : ∀{x} → ⦃ pos : Positive(x) ⦄ → (Names.CancellationOnₗ(_⋅_)(x))
[⋅]-cancellationₗ {𝐒 a}{b}{c} p with [<]-trichotomy {b}{c}
... | [∨]-introₗ ([∨]-introₗ lt) with () ← [<]-to-[≢] ([<]-with-[⋅]ₗ {a = a} lt) p
... | [∨]-introₗ ([∨]-introᵣ eq) = eq
... | [∨]-introᵣ gt              with () ← [>]-to-[≢] ([<]-with-[⋅]ₗ {a = a} gt) p

[⋅]-cancellationᵣ : ∀{x} → ⦃ pos : Positive(x) ⦄ → (Names.CancellationOnᵣ(_⋅_)(x))
[⋅]-cancellationᵣ {𝐒 c}{a}{b} p with [<]-trichotomy {a}{b}
... | [∨]-introₗ ([∨]-introₗ lt) with () ← [<]-to-[≢] ([<]-with-[⋅]ᵣ {c = c} lt) p
... | [∨]-introₗ ([∨]-introᵣ eq) = eq
... | [∨]-introᵣ gt              with () ← [>]-to-[≢] ([<]-with-[⋅]ᵣ {c = c} gt) p


module plfa.Induction where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; cong; sym)
open Eq.≡-Reasoning using (begin_; _≡⟨⟩_; _≡⟨_⟩_ ;_∎)
open import Data.Nat using (ℕ; zero; suc; _+_; _*_; _∸_; _^_)

-- @practice: exercise `operators` start
--
-- 1. _+_ _*_: Unit (Zero), Associativity, Commutativity
-- operator _*_ distributes over operator _+_ from the left and right.
--
-- 2. x operator of matrices: Unit (Unit matrix),
-- Associativity (Associativity of the Linear transformation)
-- No Commutativity

-- @practice: exercise `operators` end

_ : (3 + 4) + 5 ≡ 3 + (4 + 5)
_ =
  begin
    (3 + 4) + 5
  ≡⟨⟩
    7 + 5
  ≡⟨⟩
    12
  ≡⟨⟩
    3 + 9
  ≡⟨⟩
    3 + (4 + 5)
  ∎

+-assoc : ∀ (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-assoc zero n p =
  begin
    (zero + n) + p
  ≡⟨⟩
    n + p
  ≡⟨⟩
    zero + (n + p)
  ∎
+-assoc (suc m) n p =
  begin
    (suc m + n) + p
  ≡⟨⟩
    suc (m + n) + p
  ≡⟨⟩
    suc ((m + n) + p)
  ≡⟨ cong suc (+-assoc m n p) ⟩
    suc (m + (n + p))
  ≡⟨⟩
    suc m + (n + p)
  ∎

-- examples start

+-assoc-2 : ∀ (n p : ℕ) → (2 + n) + p ≡ 2 + (n + p)
+-assoc-2 n p =
  begin
    (2 + n) + p
  ≡⟨⟩
    suc (1 + n) + p
  ≡⟨⟩
    suc ((1 + n) + p)
  ≡⟨ cong suc (+-assoc-1 n p) ⟩
    suc (1 + (n + p))
  ≡⟨⟩
    2 + (n + p)
  ∎
  where
  +-assoc-1 : ∀ (n p : ℕ) → (1 + n) + p ≡ 1 + (n + p)
  +-assoc-1 n p =
    begin
      (1 + n) + p
    ≡⟨⟩
      suc (0 + n) + p
    ≡⟨⟩
      suc ((0 + n) + p)
    ≡⟨ cong suc (+-assoc-0 n p) ⟩
      suc (0 + (n + p))
    ≡⟨⟩
      1 + (n + p)
    ∎
    where
    +-assoc-0 : ∀ (n p : ℕ) → (0 + n) + p ≡ 0 + (n + p)
    +-assoc-0 n p =
      begin
        (0 + n) + p
      ≡⟨⟩
        n + p
      ≡⟨⟩
        0 + (n + p)
      ∎

-- examples end

+-identityʳ : ∀ ( m : ℕ ) → m + zero ≡ m
+-identityʳ zero =
  begin
    zero + zero
  ≡⟨⟩
    zero
  ∎
+-identityʳ (suc m) =
  begin
    suc m + zero
  ≡⟨⟩
    suc (m + zero)
  ≡⟨ cong suc (+-identityʳ m) ⟩
    suc m
  ∎

+-suc : ∀ ( m n : ℕ ) → m + suc n ≡ suc ( m + n )
+-suc zero n =
  begin
    zero + suc n
  ≡⟨⟩
    suc n
  ≡⟨⟩
    suc (zero + n)
  ∎
+-suc (suc m) n =
  begin
    suc m + suc n
  ≡⟨⟩
    suc ( m + suc n )
  ≡⟨ cong suc (+-suc m n)⟩
    suc ( suc ( m + n ))
  ≡⟨⟩
    suc ( suc m + n)
  ∎

+-comm : ∀ ( m n : ℕ ) → m + n ≡ n + m
+-comm m zero =
  begin
    m + zero
  ≡⟨ +-identityʳ m ⟩
    m
  ≡⟨⟩
    zero + m
  ∎
+-comm m (suc n) =
  begin
    m + suc n
  ≡⟨ +-suc m n ⟩
    suc (m + n)
  ≡⟨ cong suc ( +-comm m n )⟩
    suc (n + m)
  ≡⟨⟩
    suc n + m
  ∎

+-rearrange : ∀ (m n p q : ℕ) → (m + n) + (p + q) ≡ m + (n + p) + q
+-rearrange m n p q =
  begin
    (m + n) + (p + q)
  ≡⟨ +-assoc m n (p + q) ⟩
    m + (n + (p + q))
  ≡⟨ cong ( m +_ ) (sym (+-assoc n p q)) ⟩
    m + ((n + p) + q)
  ≡⟨ sym (+-assoc m (n + p) q)⟩
    (m + (n + p)) + q
  ∎

-- @stretch: exercise `finite-+-assoc` start

-- Day 0

-- Day 1
-- 0: ℕ

--Day 2
-- 1: ℕ
-- (0 + 0) + 0 ≡ 0 + (0 + 0)

-- Day 3
-- 2: ℕ
-- (0 + 0) + 1 ≡ 0 + (0 + 1)
-- (0 + 1) + 0 ≡ 0 + (1 + 0)
-- (0 + 1) + 1 ≡ 0 + (1 + 1)
-- (1 + 0) + 0 ≡ 1 + (0 + 0)
-- (1 + 0) + 1 ≡ 1 + (0 + 1)
-- (1 + 1) + 0 ≡ 1 + (1 + 0)
-- (1 + 1) + 1 ≡ 1 + (1 + 1)

-- Day 4
-- 3 : ℕ
-- (0 + 0) + 2 ≡ 0 + (0 + 2)
-- (0 + 1) + 2 ≡ 0 + (1 + 2)
-- (0 + 2) + 0 ≡ 0 + (2 + 0)
-- (0 + 2) + 1 ≡ 0 + (2 + 1)
-- (0 + 2) + 2 ≡ 0 + (2 + 2)
-- (1 + 0) + 2 ≡ 1 + (0 + 2)
-- (1 + 1) + 2 ≡ 1 + (1 + 2)
-- (1 + 2) + 0 ≡ 1 + (2 + 0)
-- (1 + 2) + 1 ≡ 1 + (2 + 1)
-- (1 + 2) + 2 ≡ 1 + (2 + 2)
-- (2 + 0) + 0 ≡ 2 + (0 + 0)
-- (2 + 0) + 1 ≡ 2 + (0 + 1)
-- (2 + 0) + 2 ≡ 2 + (0 + 2)
-- (2 + 1) + 0 ≡ 2 + (1 + 0)
-- (2 + 1) + 1 ≡ 2 + (1 + 1)
-- (2 + 1) + 2 ≡ 2 + (1 + 2)
-- (2 + 2) + 0 ≡ 2 + (2 + 0)
-- (2 + 2) + 1 ≡ 2 + (2 + 1)
-- (2 + 2) + 2 ≡ 2 + (2 + 2)

-- @stretch: exercise `finite-+-assoc` end

+-assoc' : ∀ ( m n p : ℕ ) → (m + n) + p ≡ m + (n + p)
+-assoc' zero n p = refl
+-assoc' (suc m) n p rewrite +-assoc' m n p  = refl

+-identity' : ∀ ( n : ℕ ) → n + zero ≡ n
+-identity' zero = refl
+-identity' (suc m) rewrite +-identity' m = refl

+-suc' : ∀ ( m n : ℕ ) → m + suc n ≡ suc (m + n)
+-suc' zero n = refl
+-suc' (suc m) n rewrite +-suc' m n  = refl

+-comm' : ∀ ( m n : ℕ ) → m + n ≡ n + m
+-comm' m zero rewrite +-identity' m = refl
+-comm' m (suc n) rewrite +-suc' m n | +-comm' m n = refl

-- with hole

+-assoc'' : ∀ (m n p : ℕ ) → ( m + n ) + p ≡ m + (n + p)
+-assoc'' zero n p = refl
+-assoc'' (suc m) n p rewrite +-assoc'' m n p = refl

-- @recommended: exercise `+-swap` start

+-swap : ∀ ( m n p : ℕ ) → m + (n + p) ≡ n + (m + p)
+-swap zero n p = refl
+-swap (suc m) n p rewrite +-swap m n p | +-suc n (m + p) = refl

-- @recommended: exercise `+-swap` end

-- @recommended: exercise `*-distrib-+` start

*-distrib-+ : ∀ ( m n p : ℕ ) → ( m + n ) * p ≡ m * p + n * p
*-distrib-+ zero n p = refl
*-distrib-+ (suc m) n p rewrite *-distrib-+ m n p | +-assoc p (m * p) (n * p) = refl

-- @recommended: exercise `*-distrib-+` end

-- @recommended: exercise `*-assoc` start

*-assoc : ∀ ( m n p : ℕ ) → (m * n) * p ≡ m * ( n * p)
*-assoc zero n p = refl
*-assoc (suc m) n p rewrite *-distrib-+ n (m * n) p | *-assoc m n p = refl

-- @recommended: exercise `*-assoc` end

-- @practice: exercise `*-comm` start

*-zeroʳ : ∀ ( m : ℕ ) → m * zero ≡ zero
*-zeroʳ zero = refl
*-zeroʳ ( suc m ) rewrite *-distrib-+ 1 m zero | *-zeroʳ m = refl

*-suc : ∀ ( n m : ℕ ) → n + n * m ≡ n * suc m
*-suc zero m = refl
*-suc (suc n) m
  rewrite
    sym (*-suc n m) |
    sym (+-assoc m n ( n * m )) | sym ( +-assoc n m ( n * m )) |
    +-comm m n = refl

*-comm : ∀ ( m n : ℕ ) → m * n ≡ n * m
*-comm zero n rewrite *-zeroʳ n = refl
*-comm (suc m) n rewrite *-distrib-+ 1 m n | +-identityʳ n | *-comm m n | *-suc n m =  refl

-- @practice: exercise `*-comm` end
-- @practice: exercise `0∸n≡0` start

0∸n≡0 : ∀ ( n : ℕ ) → 0 ∸ n ≡ 0
0∸n≡0 zero = refl
0∸n≡0 (suc n) = refl

-- @practice: exercise `0∸n≡0` end
-- @practice: exercise `∸-|-assoc` start

∸-|-assoc : ∀ ( m n p : ℕ ) → m ∸ n ∸ p ≡ m ∸ ( n + p )
∸-|-assoc zero n p rewrite 0∸n≡0 n | 0∸n≡0 p | 0∸n≡0 ( n + p )  = refl
∸-|-assoc (suc m) zero p = refl
∸-|-assoc (suc m) (suc n) p rewrite ∸-|-assoc m n p = refl

-- @practice: exercise `∸-|-assoc` end

-- @stretch: exercise `+*^` start

^-distribʳ-+-* : ∀ ( m n p : ℕ ) → m ^ ( n + p ) ≡ (m ^ n) * (m ^ p)
^-distribʳ-+-* m zero zero = refl
^-distribʳ-+-* m zero (suc p) rewrite +-identityʳ (m * (m ^ p)) = refl
^-distribʳ-+-* m (suc n) p =
  begin
    m ^ (suc n + p)
  ≡⟨⟩
    m ^ suc ( n + p )
  ≡⟨⟩
    m * ( m ^ ( n + p ) )
  ≡⟨ cong (_*_ m) ( ^-distribʳ-+-* m n p ) ⟩
    m * ( ( m ^ n ) * ( m ^ p ) )
  ≡⟨ sym (*-assoc m (m ^ n) (m ^ p)) ⟩
    m * ( m ^ n ) * (m ^ p)
  ≡⟨⟩
    (m ^ suc n ) * ( m ^ p )
  ∎

^-sucʳ : ∀ ( n p : ℕ ) → n ^ suc p ≡ n * n ^ p
^-sucʳ n p  = refl

^-distribˡ-* : ∀ ( m n p : ℕ ) → (m * n) ^ p ≡ ( m ^ p ) * ( n ^ p)
^-distribˡ-* m n zero = refl
^-distribˡ-* m n (suc p) =
  begin
    (m * n) ^ ( suc p )
  ≡⟨ ^-sucʳ (m * n) p ⟩
    (m * n) * ((m * n) ^ p)
  ≡⟨ cong (_*_ (m * n)) (^-distribˡ-* m n p) ⟩
    m * n  * ((m ^ p) * (n ^ p))
  ≡⟨ sym (*-assoc (m * n) (m ^ p) (n ^ p)) ⟩
    m * n * (m ^ p) * (n ^ p)
  ≡⟨ *-1234-[13][24] m n (m ^ p) (n ^ p) ⟩
    (m * m ^ p ) * (n * n ^ p)
  ≡⟨ cong (_*_ (m * m ^ p)) (sym (^-sucʳ n p))⟩
    (m * m ^ p ) * ( n ^ suc p)
  ≡⟨ cong (λ {x → x * (n ^ suc p)}) (^-sucʳ m p) ⟩
    (m ^ suc p) * (n ^ suc p)
  ∎
  where
  *-1234-[13][24] : ∀ ( a b c d : ℕ ) → a * b * c * d ≡ (a * c) * ( b * d )
  *-1234-[13][24] a b c d
    rewrite
      *-assoc a b c |
      *-comm b c |
      sym (*-assoc a c b) |
      *-assoc (a * c) b d = refl

^-oneˡ : ∀ ( p : ℕ ) → 1 ^ p ≡ 1
^-oneˡ zero = refl
^-oneˡ (suc p) rewrite ^-oneˡ p = refl

^-distribʳ-* :  ∀ ( m n p : ℕ ) → m ^ (n * p) ≡ (m ^ n) ^ p
^-distribʳ-* m zero p rewrite ^-oneˡ p = refl
^-distribʳ-* m n zero rewrite *-zeroʳ n = refl
^-distribʳ-* m (suc n) (suc p)
  rewrite
    ^-distribʳ-+-* m p ( n * suc p) |
    ^-distribˡ-* m (m ^ n) p |
    ^-distribʳ-* m n (suc p) =
    begin
      m * ((m ^ p) * ((m ^ n) * ((m ^ n) ^ p)))
    ≡⟨ *-1[2[34]]-13[24] m (m ^ p) (m ^ n) ((m ^ n) ^ p) ⟩
      m * (m ^ n) * ((m ^ p) * ((m ^ n) ^ p))
    ∎
    where
    *-1[2[34]]-13[24] : ∀ ( a b c d : ℕ ) → a * ( b * ( c * d ) ) ≡ a * c * ( b * d )
    *-1[2[34]]-13[24] a b c d
      rewrite
        sym (*-assoc b c d) |
        *-comm b c |
        *-assoc c b d |
        sym (*-assoc a c (b * d)) = refl

-- @stretch: exercise `+*^` end

-- @stretch: exercise `Bin-laws` start

data Bin : Set where
  ⟨⟩ : Bin
  _O : Bin → Bin
  _I : Bin → Bin

inc : Bin → Bin
inc (m O) = m I
inc (m I) = (inc m) O
inc ⟨⟩ = ⟨⟩ I

to : ℕ → Bin
to zero = ⟨⟩ O
to (suc n) = inc (to n)

from : Bin -> ℕ
from (m O) = 2 * (from m)
from (m I) = 2 * (from m) + 1
from ⟨⟩ = 0

comm-+-from : ∀ ( b : Bin ) → from (inc b) ≡ suc (from b)
comm-+-from ⟨⟩ = refl
comm-+-from (b O)
  rewrite
    sym ( +-suc' (from b) (from b + zero)) |
    +-assoc' (from b) (from b + 0) 1 |
    +-identityʳ (from b) |
    +-comm' (from b) 1 = refl
comm-+-from (b I)
  rewrite
    +-identityʳ (from (inc b)) | +-identityʳ ( from b) |
    comm-+-from b |
    sym (+-comm' (from b) 1) |
    +-assoc' (from b) (from b) 1 = refl

identity-to-from : ∀ ( n : ℕ ) → from (to n) ≡ n
identity-to-from zero = refl
identity-to-from (suc n) rewrite comm-+-from (to n) | identity-to-from n  = refl

-- ⟨⟩ !== ⟨⟩ O
-- @todo: rewrite with ∃-syntax

-- @stretch: exercise `Bin-laws` end

-- import Data.Nat.Properties using (+-assoc; +-identityʳ; +-suc; +-comm)


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

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import LogicalFormulae
open import Sets.EquivalenceRelations
open import Setoids.Setoids

module Setoids.Equality {a b : _} {A : Set a} (S : Setoid {a} {b} A) where

open import Setoids.Subset S

_=S_ : {c d : _} {pred1 : A → Set c} {pred2 : A → Set d} (s1 : subset pred1) (s2 : subset pred2) → Set _
_=S_ {pred1 = pred1} {pred2} s1 s2 = (x : A) → (pred1 x → pred2 x) && (pred2 x → pred1 x)

setoidEqualitySymmetric : {c d : _} {pred1 : A → Set c} {pred2 : A → Set d} → (s1 : subset pred1) (s2 : subset pred2) → s1 =S s2 → s2 =S s1
setoidEqualitySymmetric s1 s2 s1=s2 x = _&&_.snd (s1=s2 x) ,, _&&_.fst (s1=s2 x)

setoidEqualityTransitive : {c d e : _} {pred1 : A → Set c} {pred2 : A → Set d} {pred3 : A → Set e} (s1 : subset pred1) (s2 : subset pred2) (s3 : subset pred3) → s1 =S s2 → s2 =S s3 → s1 =S s3
setoidEqualityTransitive s1 s2 s3 s1=s2 s2=s3 x with s1=s2 x
setoidEqualityTransitive s1 s2 s3 s1=s2 s2=s3 x | p1top2 ,, p1top2' with s2=s3 x
setoidEqualityTransitive s1 s2 s3 s1=s2 s2=s3 x | p1top2 ,, p1top2' | fst ,, snd = (λ i → fst (p1top2 i)) ,, λ i → p1top2' (snd i)

setoidEqualityReflexive : {c : _} {pred : A → Set c} (s : subset pred) → s =S s
setoidEqualityReflexive s x = (λ x → x) ,, λ x → x


------------------------------------------------------------------------------
-- The relation of divisibility on partial natural numbers
------------------------------------------------------------------------------

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

module LTC-PCF.Data.Nat.Divisibility.By0 where

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

infix 4 _∣_

------------------------------------------------------------------------------
-- The relation of divisibility (the symbol is '\mid' not '|')
--
-- (See documentation in FOTC.Data.Nat.Divisibility.By0)
--
-- In our definition 0∣0, which is used to prove properties of the gcd
-- as it is in GHC ≥ 7.2.1, where gcd 0 0 = 0 (see
-- http://hackage.haskell.org/trac/ghc/ticket/3304).

-- Note that @k@ should be a total natural number.
_∣_ : D → D → Set
m ∣ n = ∃[ k ] N k ∧ n ≡ k * m


module Generic.Lib.Equality.Propositional where

open import Level
open import Relation.Binary
open import Data.Empty

infix 3 _≡_ _≢_ _≗_

data _≡_ {α} {A : Set α} (x : A) : A -> Set where
  instance refl : x ≡ x

pattern lrefl = lift refl

_≢_ : ∀ {α} {A : Set α} -> A -> A -> Set
x ≢ y = x ≡ y -> ⊥

_≗_ : ∀ {α β} {A : Set α} {B : A -> Set β} -> (∀ x -> B x) -> (∀ x -> B x) -> Set α
f ≗ g = ∀ x -> f x ≡ g x

sym : ∀ {α} {A : Set α} {x y : A} -> x ≡ y -> y ≡ x
sym refl = refl

trans : ∀ {α} {A : Set α} {x y z : A} -> x ≡ y -> y ≡ z -> x ≡ z
trans refl refl = refl

left : ∀ {α} {A : Set α} {x y z : A} -> y ≡ x -> z ≡ x -> y ≡ z
left refl refl = refl

right : ∀ {α} {A : Set α} {x y z : A} -> x ≡ y -> x ≡ z -> y ≡ z
right refl refl = refl

subst : ∀ {α β} {A : Set α} {x y} -> (B : A -> Set β) -> x ≡ y -> B x -> B y
subst B refl z = z

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

cong₂ : ∀ {α β γ} {A : Set α} {B : Set β} {C : Set γ} {x₁ x₂ y₁ y₂}
      -> (g : A -> B -> C) -> x₁ ≡ x₂ -> y₁ ≡ y₂ -> g x₁ y₁ ≡ g x₂ y₂
cong₂ g refl refl = refl

≡-Setoid : ∀ {α} -> Set α -> Setoid α zero
≡-Setoid A = record
  { Carrier       = A
  ; _≈_           = _≡_
  ; isEquivalence = record
      { refl  = refl
      ; sym   = sym
      ; trans = trans
      }
  }


module List.Permutation.Base.Bag (A : Set) where

open import Data.List
open import Data.List.Any as Any
open import Data.List.Any.BagAndSetEquality
open import Data.List.Any.Properties
open import Data.Sum renaming (_⊎_ to _∨_)
open import Function
open import Function.Inverse hiding (sym ; _∘_ ; id)
open import Function.Related as Related hiding (_∼[_]_)
open import List.Permutation.Base A
import Relation.Binary.EqReasoning as EqR
open import Relation.Binary.PropositionalEquality as P hiding (sym)

open Any.Membership-≡
open EqR ([ bag ]-Equality A)
open Related.EquationalReasoning renaming (_∎ to _□)

infix 4 _≈-bag_

_≈-bag_ : ∀ {a} {A : Set a} → List A → List A → Set a
xs ≈-bag ys = xs ∼[ bag ] ys

sym-≈-bag : {xs ys : List A} → xs ≈-bag ys → ys ≈-bag xs
sym-≈-bag {xs} {ys} xs≈ys {z} = record
  { to         = Inverse.from xs≈ys
  ; from       = Inverse.to xs≈ys
  ; inverse-of = record
    { left-inverse-of  = Inverse.right-inverse-of xs≈ys
    ; right-inverse-of = Inverse.left-inverse-of xs≈ys
    }
  }

refl-≈-bag : {xs : List A} → xs ≈-bag xs
refl-≈-bag {xs} = begin xs ∎

∨↔ : ∀{a b}{A : Set a}{B : Set b} → (A ∨ B) ↔ (B ∨ A)
∨↔ = record
  { to         = P.→-to-⟶ ∨→
  ; from       = P.→-to-⟶ ∨→
  ; inverse-of = record
    { left-inverse-of  = v→∘v→
    ; right-inverse-of = v→∘v→
    }
  }
  where
  ∨→ : ∀{a b}{A : Set a}{B : Set b} → (A ∨ B) → (B ∨ A)
  ∨→ (inj₁ x) = inj₂ x
  ∨→ (inj₂ x) = inj₁ x

  v→∘v→ : ∀{a b}{A : Set a}{B : Set b}(a∨b : A ∨ B) → ∨→ (∨→ a∨b) ≡ a∨b
  v→∘v→ (inj₁ _) = refl
  v→∘v→ (inj₂ _) = refl

xy≈-bag-yx : {x y : A} → x ∷ y ∷ [] ≈-bag y ∷ x ∷ []
xy≈-bag-yx {x} {y} {z} =
  z ∈ x ∷ y ∷ [] ↔⟨ sym $ ++↔ {xs = x ∷ []} {ys = y ∷ []} ⟩
  (z ∈ x ∷ [] ∨ z ∈ y ∷ []) ↔⟨ ∨↔ ⟩
  (z ∈ y ∷ [] ∨ z ∈ x ∷ []) ↔⟨ ++↔ ⟩
  z ∈ y ∷ x ∷ [] □

lemma-/-≈-bag : {y : A}{xs ys : List A} → xs / y ⟶ ys → xs ≈-bag (y ∷ ys)
lemma-/-≈-bag /head = refl-≈-bag
lemma-/-≈-bag (/tail {x} {y} {xs} {ys} xs/y⟶ys) = begin
  x ∷ xs ≈⟨ ∷-cong refl (lemma-/-≈-bag xs/y⟶ys) ⟩
  x ∷ y ∷ ys ≈⟨ ++-cong xy≈-bag-yx refl-≈-bag ⟩
  y ∷ x ∷ ys ∎

lemma-∼-≈-bag : {xs ys : List A} → xs ∼ ys → xs ≈-bag ys
lemma-∼-≈-bag ∼[] = refl-≈-bag
lemma-∼-≈-bag (∼x {x} {xs} {ys} {xs'} {ys'} xs/x⟶xs' ys/x⟶ys' xs'∼ys') = begin
  xs ≈⟨ lemma-/-≈-bag xs/x⟶xs' ⟩
  x ∷ xs' ≈⟨ ∷-cong refl (lemma-∼-≈-bag xs'∼ys') ⟩
  x ∷ ys' ≈⟨ sym-≈-bag (lemma-/-≈-bag ys/x⟶ys') ⟩
  ys ∎


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

open import Setoids.Setoids
open import Sets.EquivalenceRelations
open import Rings.Definition

module Rings.Ideals.Principal.Lemmas {a b : _} {A : Set a} {S : Setoid {a} {b} A} {_+_ _*_ : A → A → A} (R : Ring S _+_ _*_) where

open Setoid S
open Ring R
open Equivalence eq
open import Rings.Ideals.Principal.Definition R
open import Rings.Ideals.Definition R
open import Rings.Ideals.Lemmas R
open import Rings.Divisible.Definition R

generatorZeroImpliesAllZero : {c : _} {pred : A → Set c} → {i : Ideal pred} → (princ : PrincipalIdeal i) → PrincipalIdeal.generator princ ∼ 0R → {x : A} → pred x → x ∼ 0R
generatorZeroImpliesAllZero record { generator = gen ; genIsInIdeal = genIsInIdeal ; genGenerates = genGenerates } gen=0 {x} predX = generatorZeroImpliesMembersZero {x} (divisibleWellDefined gen=0 reflexive (genGenerates predX))


module Setoids where

open import Eq
open import Prelude

record Setoid : Set1 where
  field
    carrier : Set
    _≈_     : carrier -> carrier -> Set
    equiv   : Equiv _≈_

record Datoid : Set1 where
  field
    setoid : Setoid
    _≟_    : forall x y -> Dec (Setoid._≈_ setoid x y)

Setoid-≡ : Set -> Setoid
Setoid-≡ a = record { carrier = a; _≈_ = _≡_; equiv = Equiv-≡ }


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

open import Setoids.Setoids
open import Rings.Definition

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

module Rings.Ideals.Principal.Definition {a b : _} {A : Set a} {S : Setoid {a} {b} A} {_+_ _*_ : A → A → A} (R : Ring S _+_ _*_) where

open import Rings.Ideals.Definition R
open import Rings.Divisible.Definition R
open Setoid S

record PrincipalIdeal {c : _} {pred : A → Set c} (ideal : Ideal pred) : Set (a ⊔ b ⊔ c) where
  field
    generator : A
    genIsInIdeal : pred generator
    genGenerates : {x : A} → pred x → generator ∣ x


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

open import Categories.Category using (Category; module Commutation)
open import Categories.Category.Cartesian using (Cartesian)

-- Defines the following properties of a Category:
-- Cartesian.SymmetricMonoidal
--    a Cartesian category is Symmetric Monoidal if its induced monoidal structure is symmetric

module Categories.Category.Cartesian.SymmetricMonoidal {o ℓ e} (𝒞 : Category o ℓ e) (cartesian : Cartesian 𝒞) where

open import Data.Product using (_,_)

open Category 𝒞
open Commutation 𝒞
open HomReasoning

open import Categories.Category.BinaryProducts 𝒞 using (module BinaryProducts)
open import Categories.Category.Cartesian.Monoidal using (module CartesianMonoidal)
open import Categories.Category.Monoidal using (Monoidal)
import Categories.Category.Monoidal.Symmetric as Sym

open import Categories.NaturalTransformation using (ntHelper)

private
  variable
    W X Y Z : Obj

open Cartesian cartesian using (products)
open CartesianMonoidal cartesian using (monoidal)
open Sym monoidal using (Symmetric; symmetricHelper)
open Monoidal monoidal using (_⊗₀_; _⊗₁_; module associator)
open BinaryProducts products

private
  B : ∀ {X Y} → X ⊗₀ Y ⇒ Y ⊗₀ X
  B = swap

hexagon : [ (X ⊗₀ Y) ⊗₀ Z ⇒ Y ⊗₀ Z ⊗₀ X ]⟨
            B  ⊗₁ id                    ⇒⟨ (Y ⊗₀ X) ⊗₀ Z ⟩
            associator.from             ⇒⟨ Y ⊗₀ X ⊗₀ Z ⟩
            id ⊗₁ B
          ≈ associator.from             ⇒⟨ X ⊗₀ Y ⊗₀ Z ⟩
            B                           ⇒⟨ (Y ⊗₀ Z) ⊗₀ X ⟩
            associator.from
          ⟩
hexagon = begin
      id ⊗₁ swap ∘ assocˡ ∘ swap ⊗₁ id                        ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ⟨⟩-congʳ ⟨⟩∘ ⟩
      id ⊗₁ swap ∘ assocˡ ∘ ⟨ ⟨ π₂ ∘ π₁ , π₁ ∘ π₁ ⟩ , id ∘ π₂ ⟩ ≈⟨ refl⟩∘⟨ assocˡ∘⟨⟩ ⟩
      id ⊗₁ swap ∘ ⟨ π₂ ∘ π₁ , ⟨ π₁ ∘ π₁ , id ∘ π₂ ⟩ ⟩          ≈⟨ ⁂∘⟨⟩ ⟩
      ⟨ id ∘ π₂ ∘ π₁ , swap ∘ ⟨ π₁ ∘ π₁ , id ∘ π₂ ⟩ ⟩           ≈⟨ ⟨⟩-cong₂ identityˡ swap∘⟨⟩ ⟩
      ⟨ π₂ ∘ π₁ , ⟨ id ∘ π₂ , π₁ ∘ π₁ ⟩ ⟩                       ≈⟨ ⟨⟩-congˡ (⟨⟩-congʳ identityˡ) ⟩
      ⟨ π₂ ∘ π₁ , ⟨ π₂ , π₁ ∘ π₁ ⟩ ⟩                            ≈˘⟨ assocˡ∘⟨⟩ ⟩
      assocˡ ∘ ⟨ ⟨ π₂ ∘ π₁ , π₂ ⟩ , π₁ ∘ π₁ ⟩                   ≈˘⟨ refl⟩∘⟨ swap∘⟨⟩ ⟩
      assocˡ ∘ swap ∘ assocˡ                                    ∎

symmetric : Symmetric
symmetric = symmetricHelper record
  { braiding    = record
    { F⇒G = ntHelper record
      { η       = λ _ → swap
      ; commute = λ _ → swap∘⁂
      }
    ; F⇐G = ntHelper record
      { η       = λ _ → swap
      ; commute = λ _ → swap∘⁂
      }
    ; iso = λ _ → record
      { isoˡ = swap∘swap
      ; isoʳ = swap∘swap
      }
    }
  ; commutative = swap∘swap
  ; hexagon     = hexagon
  }


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

open import HoTT
open import cw.CW
open import cw.FinCW
open import cw.FinBoundary
open import cohomology.Theory

module cw.cohomology.cochainequiv.DualizedHigherBoundary (OT : OrdinaryTheory lzero)
  {n} (⊙fin-skel : ⊙FinSkeleton (S (S n))) where

open OrdinaryTheory OT

private
  fin-skel = ⊙FinSkeleton.skel ⊙fin-skel
  I = AttachedFinSkeleton.numCells fin-skel

  fin-skel₋₁ = AttachedFinSkeleton.skel fin-skel
  I₋₁ = AttachedFinSkeleton.numCells fin-skel₋₁

  module FAG   = FreeAbelianGroup (Fin I)
  module FAG₋₁ = FreeAbelianGroup (Fin I₋₁)

  open FAG   renaming (FreeAbGroup to G)   using ()
  open FAG₋₁ renaming (FreeAbGroup to G₋₁) using ()

abstract
  rephrase-dualized-higher-boundary-in-degree : ∀ (g : Fin I₋₁ → Group.El (C2 0)) <I
    → GroupIso.g (FAG.Freeness.extend-iso (C2-abgroup 0))
        (GroupIso.f (FAG₋₁.Freeness.extend-iso (C2-abgroup 0)) g ∘ᴳ fboundary-last fin-skel) <I
    == Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (fdegree-last fin-skel <I <I₋₁))
  rephrase-dualized-higher-boundary-in-degree g <I =
    γ.f (GroupHom.f (fboundary-last fin-skel) (FAG.insert <I))
      =⟨ ap γ.f $ app= (is-equiv.g-f (snd (FAG.Freeness.extend-equiv G₋₁)) (fboundary'-last fin-skel)) <I ⟩
    γ.f (G₋₁.sum (λ <I₋₁ → G₋₁.exp (FAG₋₁.insert <I₋₁) (deg <I₋₁)))
      =⟨ γ.pres-sum (λ <I₋₁ → G₋₁.exp (FAG₋₁.insert <I₋₁) (deg <I₋₁)) ⟩
    C⁰2.sum (λ <I₋₁ → γ.f (G₋₁.exp (FAG₋₁.insert <I₋₁) (deg <I₋₁)))
      =⟨ ap C⁰2.sum (λ= $ λ <I₋₁ → γ.pres-exp (FAG₋₁.insert <I₋₁) (deg <I₋₁)) ⟩
    C⁰2.sum (λ <I₋₁ → C⁰2.exp (γ.f (FAG₋₁.insert <I₋₁)) (deg <I₋₁))
      =⟨ ap C⁰2.sum
          (λ= λ <I₋₁ → ap (λ g → C⁰2.exp g (deg <I₋₁)) $
            app= (is-equiv.g-f (snd (FAG₋₁.Freeness.extend-equiv C⁰2)) g) <I₋₁) ⟩
    C⁰2.sum (λ <I₋₁ → C⁰2.exp (g <I₋₁) (deg <I₋₁))
      =∎
    where
      deg : Fin I₋₁ → ℤ
      deg <I₋₁ = fdegree-last fin-skel <I <I₋₁
      C⁰2 : AbGroup lzero
      C⁰2 = C2-abgroup 0
      module C⁰2 = AbGroup C⁰2
      γ : G₋₁.grp →ᴳ C⁰2.grp
      γ = FAG₋₁.Freeness.extend C⁰2 g
      module γ = GroupHom γ


module Cats.Category.Setoids.Facts.Exponentials where

open import Data.Product using (_,_)
open import Relation.Binary using (Setoid)

open import Cats.Category
open import Cats.Category.Setoids as Setoids using (Setoids ; ≈-intro ; ≈-elim)
open import Cats.Category.Setoids.Facts.Products as Products using
  (hasBinaryProducts)
open import Cats.Util.Conv

import Relation.Binary.PropositionalEquality as ≡

import Cats.Category.Base as Base
import Cats.Util.SetoidReasoning as SetoidReasoning

module Build l where

  infixr 1 _↝_

  open Base.Category (Setoids l l)
  open Category (Setoids l l) using (∃!-intro ; Exp ; IsUniqueSuchThat)
  open HasBinaryProducts (hasBinaryProducts l l)
  open Setoids._⇒_ using (resp)

  _↝_ : Obj → Obj → Obj
  A ↝ B = record
      { Carrier = A ⇒ B
      ; _≈_ = _≈_ {A} {B}
      ; isEquivalence = equiv
      }

  eval : ∀ {B C} → (B ↝ C) × B ⇒ C
  eval = record
      { arr = λ { (f , x) → (f ⃗) x }
      ; resp = λ { (eq₁ , eq₂) → ≈-elim eq₁ eq₂ }
      }

  curry : ∀ {A B C} → A × B ⇒ C → A ⇒ B ↝ C
  curry {A} f = record
      { arr = λ a → record
           { arr = λ b → (f ⃗) (a , b)
           ; resp = λ eqb → resp f (refl , eqb)
           }
      ; resp = λ eqa → ≈-intro λ eqb → resp f (eqa , eqb)
      }
    where
      open Setoid A using (refl)

  curry-correct : ∀ {A B C} (f : A × B ⇒ C)
    → eval ∘ ⟨ curry f × id ⟩ ≈ f
  curry-correct f = ≈-intro (resp f)

  curry-unique : ∀ {A B C} (f : A × B ⇒ C)
    → IsUniqueSuchThat (λ f̃ → eval ∘ ⟨ f̃ × id ⟩ ≈ f) (curry f)
  curry-unique {A} {B} {C} f {g} eval∘g≈f
      = ≈-intro λ {a} {a′} a≈a′ → ≈-intro λ {b} {b′} b≈b′ →
        begin⟨ C ⟩
          ((((curry f) ⃗) a) ⃗) b
        ≡⟨⟩
          (f ⃗) (a , b)
        ≈⟨ sym C (≈-elim eval∘g≈f (sym A a≈a′ , sym B b≈b′)) ⟩
          (((g ⃗) a′) ⃗) b′
        ∎
    where
      open SetoidReasoning
      open Setoid using (sym)

  _↝′_ : ∀ B C → Exp B C
  B ↝′ C = record
      { Cᴮ = B ↝ C
      ; eval = eval
      ; curry′ = λ f → ∃!-intro
          (curry f)
          (curry-correct f)
          (λ {g} eval∘g≈f → curry-unique f {g} eval∘g≈f)
      }

instance
  hasExponentials : ∀ l → HasExponentials (Setoids l l)
  hasExponentials l = record { _↝′_ = Build._↝′_ l }


module Data.List.Properties.Pointwise where

open import Data.Nat
open import Data.List
open import Relation.Binary.List.Pointwise hiding (refl; map)
open import Relation.Binary.PropositionalEquality

pointwise-∷ʳ : ∀ {a b ℓ A B P l m x y} → Rel {a} {b} {ℓ} {A} {B} P l m → P x y →
              Rel {a} {b} {ℓ} {A} {B} P (l ∷ʳ x) (m ∷ʳ y)
pointwise-∷ʳ [] q = q ∷ []
pointwise-∷ʳ (x∼y ∷ p) q = x∼y ∷ (pointwise-∷ʳ p q)

{-}
pointwise-[]≔ : ∀ {a b ℓ A B P l m i x y} →
                Rel {a} {b} {ℓ} {A} {B} P l m → (p : l [ i ]= x) → P x y →
                Rel {a} {b} {ℓ} {A} {B} P l (m [ fromℕ ([]=-length l p) ]≔ y)
pointwise-[]≔ r p q z = ?
postulate
pointwise-[]≔ [] () r
pointwise-[]≔ (x∼y ∷ r) here (s≤s z≤n) z = z ∷ r
pointwise-[]≔ (x∼y ∷ r) (there p) (s≤s q) z = subst (λ x → Rel _ _ x) {!!} (x∼y ∷ pointwise-[]≔ r p q z)
-}

pointwise-length : ∀ {a b ℓ A B P l m} → Rel {a} {b} {ℓ} {A} {B} P l m → length l ≡ length m
pointwise-length [] = refl
pointwise-length (x∼y ∷ p) = cong suc (pointwise-length p)


module JMEq where

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

subst : {A : Set}{x y : A}(P : A -> Set) -> x == y -> P x -> P y
subst {A} P refl px = px


module Numeric.Nat.LCM where

open import Prelude
open import Numeric.Nat.Divide
open import Numeric.Nat.Divide.Properties
open import Numeric.Nat.GCD
open import Numeric.Nat.GCD.Extended
open import Numeric.Nat.GCD.Properties
open import Numeric.Nat.Properties
open import Tactic.Nat

--- Least common multiple ---

record IsLCM m a b : Set where
  no-eta-equality
  constructor is-lcm
  field
    a|m : a Divides m
    b|m : b Divides m
    l   : ∀ k → a Divides k → b Divides k → m Divides k

record LCM a b : Set where
  no-eta-equality
  constructor lcm-res
  field
    m : Nat
    isLCM : IsLCM m a b

open LCM using () renaming (m to get-lcm) public

eraseIsLCM : ∀ {a b m} → IsLCM m a b → IsLCM m a b
eraseIsLCM (is-lcm a|m b|m g) = is-lcm (fast-divides a|m) (fast-divides b|m)
                                       λ k a|k b|k → fast-divides (g k a|k b|k)

eraseLCM : ∀ {a b} → LCM a b → LCM a b
eraseLCM (lcm-res m p) = lcm-res m (eraseIsLCM p)

private
  lem-is-lcm : ∀ {a b d} (g : IsGCD d a b) →
               IsLCM (is-gcd-factor₁ g * b) a b
  lem-is-lcm {a} {b} {0} (is-gcd (factor q eq) d|b g)
             rewrite a ≡⟨ by eq ⟩ 0 ∎ | divides-zero d|b | q * 0 ≡⟨ auto ⟩ 0 ∎ =
    is-lcm divides-refl divides-refl (λ _ 0|k _ → 0|k)
  lem-is-lcm {a} {b} {d@(suc d′)} isg@(is-gcd (factor! q) d|b@(factor! q′) g) =
    is-lcm (divides-mul-cong-l q d|b)
           (divides-mul-r q divides-refl) least
    where
      lem : IsGCD d a b
      lem = is-gcd (factor! q) d|b g

      lem₂ : Coprime q q′
      lem₂ = is-gcd-factors-coprime lem

      least : ∀ k → a Divides k → b Divides k → (q * b) Divides k
      least k (factor qa qa=k) (factor qb qb=k) =
        case lem₄ of λ where
          (factor! qqb) → factor qqb (by qb=k)
        where
          lem₃ : qa * q ≡ q′ * qb
          lem₃ = mul-inj₁ (qa * q) (q′ * qb) (suc d′)
                 (by (qa=k ⟨≡⟩ʳ qb=k))

          lem₄ : q Divides qb
          lem₄ = coprime-divide-mul-l q q′ qb (is-gcd-factors-coprime isg)
                 (factor qa lem₃)

lcm : ∀ a b → LCM a b
lcm a b = eraseLCM $
  case gcd a b of λ where
    (gcd-res d g) →
      lcm-res (is-gcd-factor₁ g * b) (lem-is-lcm g)

lcm! : Nat → Nat → Nat
lcm! a b = get-lcm (lcm a b)


open import Agda.Builtin.Nat

data B {A : Set} : A → Set where
  b : (a : A) → B a

data C {A : Set} : ∀ {a} → B {A} a → Set where
  c : {a : A} → C (b a)

id : ∀ {b} → C b → B 0
id c = {!!}