open import SOAS.Common

-- Free construction with respect to a forgetful functor between categories
module SOAS.Construction.Free {ℂ 𝕊 : Category 1ℓ 0ℓ 0ℓ}
                               (U : Functor 𝕊 ℂ) where

open import Categories.Adjoint
import Categories.Morphism.Reasoning as MR
open import Categories.Category.Equivalence using (WeakInverse; StrongEquivalence)
open import Categories.Adjoint.Properties
open import Categories.Monad

private module ℂ = Category ℂ
private module 𝕊 = Category 𝕊
private module U = Functor U

-- Mapping from an object of the carrier category to the carrier of an object
-- from the structure category
_↪_ : ℂ.Obj → 𝕊.Obj → Set
C ↪ S = ℂ [ C , U.₀ S ]

-- Definition of F being a free structure over a carrier C:
-- any carrier map c : C → S into the carrier of a structure S factorises
-- through a unique extension of c to a structure homomorphism ĉ : F C → S:
--
--           ⌊-⌋
--   C ────────────── FC
--  ╰─────── S ───────╯
--      c         ĉ

record FreeConstruction (F : ℂ.Obj → 𝕊.Obj)
                        (C : ℂ.Obj)
                        (embed : C ↪ F C)
                        (S : 𝕊.Obj)
                        (c : C ↪ S)
                        : Set₁ where

  field
    -- Given another structure S, any mapping from C to the carrier of S can be
    -- extended into a structure homomorphism from F C to S.
    extend : 𝕊 [ F C , S ]

    -- Any map from C to the carrier of a structure S factors through the
    -- embedding and extension
    factor : U.₁ extend ℂ.∘ embed ℂ.≈ c

    -- Extension is the unique factorising morphism
    unique : (e : 𝕊 [ F C , S ])
           → (p : (U.₁ e ℂ.∘ embed) ℂ.≈ c)
           → 𝕊 [ e ≈ extend ]

record FreeMapping (F : ℂ.Obj → 𝕊.Obj) : Set₁ where

  field
    embed : {C : ℂ.Obj} → C ↪ F C
    univ  : (C : ℂ.Obj) (S : 𝕊.Obj)(c : C ↪ S)
          → FreeConstruction F C embed S c

  module Universal C S c = FreeConstruction (univ C S c)

  module _ {C : ℂ.Obj} {S : 𝕊.Obj}(c : C ↪ S) where
    open Universal C S c public

  open MR ℂ
  open ℂ.HomReasoning
  private module 𝕊R = 𝕊.HomReasoning
  open NT

  -- The uniqueness of the factors means that any two morphisms
  -- that factorise the same arrow must be equal
  equate : {C : ℂ.Obj}{S : 𝕊.Obj}
        → (f g : 𝕊 [ F C , S ])
        → (p : U.₁ f ℂ.∘ embed ℂ.≈ U.₁ g ℂ.∘ embed)
        → 𝕊 [ f ≈ g ]
  equate f g p = unique _ f p 𝕊R.○ 𝕊R.⟺ (unique _ g ℂ.Equiv.refl)

  -- Infix shorthand
  [_≋_]! : {C : ℂ.Obj}{S : 𝕊.Obj}
        → (f g : 𝕊 [ F C , S ])
        → (p : U.₁ f ℂ.∘ embed ℂ.≈ U.₁ g ℂ.∘ embed)
        → 𝕊 [ f ≈ g ]
  [ f ≋ g ]! p = equate f g p

  -- Extensions of equal embeddings are equal
  extend-cong : {C : ℂ.Obj}{S : 𝕊.Obj}(c₁ c₂ : C ↪ S)
             → ℂ [ c₁ ≈ c₂ ] → 𝕊 [ extend c₁ ≈ extend c₂ ]
  extend-cong {C}{S} c₁ c₂ p = unique _ (extend c₁) (factor c₁ ○ p)

  -- | Freeness makes F into a functor

  map : {C D : ℂ.Obj} → C ℂ.⇒ D → F C 𝕊.⇒ F D
  map {C}{D} f = extend (embed ℂ.∘ f)

  embed-commute : {C D : ℂ.Obj}(f : C ℂ.⇒ D)
               → embed ℂ.∘ f ℂ.≈ (U.₁ (map f) ℂ.∘ embed)
  embed-commute {C}{D} f = ⟺ (factor (embed ℂ.∘ f))

  identity : {C : ℂ.Obj} → map (ℂ.id {C}) 𝕊.≈ 𝕊.id
  identity {C} = 𝕊R.⟺ $ᶠ unique _ 𝕊.id (begin
         U.₁ 𝕊.id ℂ.∘ embed  ≈⟨ U.identity ⟩∘⟨refl ⟩
         ℂ.id ℂ.∘ embed      ≈⟨ id-comm-sym ⟩
         embed ℂ.∘ ℂ.id      ∎)

  homomorphism : {C D E : ℂ.Obj} {f : ℂ [ C , D ]} {g : ℂ [ D , E ]}
              → 𝕊 [ map (ℂ [ g ∘ f ])
               ≈ 𝕊 [ map g ∘ map f ] ]
  homomorphism {C}{D}{E}{f}{g} = 𝕊R.⟺ $ᶠ unique _ (𝕊 [ map g ∘ map f ]) (begin
         U.₁ (map g 𝕊.∘ map f) ℂ.∘ embed           ≈⟨ pushˡ U.homomorphism ⟩
         U.₁ (map g)  ℂ.∘  U.₁ (map f)  ℂ.∘ embed  ≈⟨ pushʳ (⟺ (embed-commute f)) ⟩
         (U.₁ (map g)  ℂ.∘  embed) ℂ.∘ f           ≈⟨ pushˡ (⟺ (embed-commute g)) ⟩
         embed ℂ.∘ g ℂ.∘ f                          ∎)

  -- Free functor from ℂ to 𝕊
  Free : Functor ℂ 𝕊
  Free = record
    { F₀ = F
    ; F₁ = map
    ; identity = identity
    ; homomorphism = homomorphism
    ; F-resp-≈ = λ {C}{D}{f}{g} p →
          extend-cong (embed ℂ.∘ f) (embed ℂ.∘ g) (ℂ.∘-resp-≈ʳ p)
    }

  module F = Functor Free

  -- | Freeness also induces a free-forgetful adjunction

  -- Embedding is a natural transformation
  embed-NT : NT idF (U ∘F Free)
  embed-NT = ntHelper record
    { η = λ C → embed
    ; commute = embed-commute
    }

  -- Extension of the identity on US is a natural transformation
  extract : (S : 𝕊.Obj) → F (U.₀ S) 𝕊.⇒ S
  extract S = extend ℂ.id

  -- Extraction is a natural transformation
  extract-NT : NT (Free ∘F U) idF
  extract-NT = ntHelper record
    { η = extract
    ; commute = λ {S}{T} f → [ extract T 𝕊.∘ F.₁ (U.₁ f) ≋ f 𝕊.∘ extract S ]!
        (begin
             U.₁ (extract T 𝕊.∘ F.₁ (U.₁ f)) ℂ.∘ embed
        ≈⟨ pushˡ U.homomorphism ⟩
             U.₁ (extract T) ℂ.∘ U.₁ (F.₁ (U.₁ f)) ℂ.∘ embed
        ≈⟨ refl⟩∘⟨ sym-commute embed-NT (U.₁ f) ⟩
             U.₁ (extract T) ℂ.∘ embed ℂ.∘ U.₁ f
        ≈⟨ pullˡ (factor ℂ.id) ○ ℂ.identityˡ ⟩
             U.₁ f
        ≈˘⟨ (refl⟩∘⟨ factor ℂ.id) ○ ℂ.identityʳ ⟩
             U.₁ f ℂ.∘ U.₁ (extract S) ℂ.∘ embed
        ≈˘⟨ pushˡ U.homomorphism ⟩
             U.₁ (f 𝕊.∘ extract S) ℂ.∘ embed
        ∎)
    }

  -- The free-forgetful adjunction arising from the universal morphisms
  Free⊣Forgetful : Free ⊣ U
  Free⊣Forgetful = record
    { unit = embed-NT
    ; counit = extract-NT
    ; zig = λ {C} → [ extract (F.₀ C) 𝕊.∘ F.₁ embed ≋ 𝕊.id ]!
        (begin
             U.₁ (extract (F.₀ C) 𝕊.∘ F.₁ embed) ℂ.∘ embed
         ≈⟨ pushˡ U.homomorphism ⟩
             U.₁ (extract (F.₀ C)) ℂ.∘ U.₁ (F.₁ embed) ℂ.∘ embed
         ≈⟨ refl⟩∘⟨ (sym-commute embed-NT embed) ⟩
             U.₁ (extract (F.₀ C)) ℂ.∘ (embed ℂ.∘ embed)
         ≈⟨ pullˡ (factor (ℂ.id)) ⟩
             ℂ.id ℂ.∘ embed
         ≈˘⟨ U.identity ⟩∘⟨refl ⟩
             U.₁ 𝕊.id ℂ.∘ embed
         ∎)
    ; zag = factor ℂ.id
    }

  FreeMonad : Monad ℂ
  FreeMonad = adjoint⇒monad Free⊣Forgetful


module Modal where

open import Prelude
open import Star

data Progress (A : Set) : Set where
  cont : A -> Progress A
  stop : Progress A

record Some {A : Set}(R : Rel A) : Set where
  field
    a    : A
    b    : A
    edge : R a b

some : {A : Set}{R : Rel A}{a b : A} -> R a b -> Some R
some x = record {a = _; b = _; edge = x}

EdgePred : {A : Set} -> Rel A -> Set1
EdgePred R = forall {a b} -> R a b -> Set

data PStep {A : Set}{R : Rel A}(P : EdgePred R) :
           Rel (Progress (Some (Star R))) where
  step : {a b c : A}{x : R a b}{xs : Star R b c} ->
         PStep P (cont (some (x • xs))) (cont (some xs))
  done : {a b c : A}{x : R a b}{xs : Star R b c} ->
         P x -> PStep P (cont (some (x • xs))) stop

Any : {A : Set}{R : Rel A}(P : EdgePred R) -> EdgePred (Star R)
Any P xs = Star (PStep P) (cont (some xs)) stop

mapAny : {A₁ A₂ : Set}{R₁ : Rel A₁}{R₂ : Rel A₂}
         {P₁ : EdgePred R₁}{P₂ : EdgePred R₂}{a b : A₁}{xs : Star R₁ a b}
         {i : A₁ -> A₂}{f : R₁ =[ i ]=> R₂} ->
         ({a b : A₁}{x : R₁ a b} -> P₁ x -> P₂ (f x)) ->
         Any P₁ xs -> Any (\{a b} -> P₂{a}{b}) (map i f xs)
mapAny h (step   • i) = step • mapAny h i
mapAny h (done p • ε) = done (h p) • ε
mapAny h (done p • (() • _))

data Check {A : Set}{R : Rel A}(P : EdgePred R) :
           Rel (Some (Star R)) where
  check : {a b c : A}{x : R a b}{xs : Star R b c} ->
          P x -> Check P (some (x • xs)) (some xs)

checkedEdge : {A : Set}{R : Rel A}{P : EdgePred R}{xs ys : Some (Star R)} ->
              Check P xs ys -> Some R
checkedEdge (check {x = x} _) = some x

uncheck : {X : Set}{R : Rel X}{P : EdgePred R}{xs ys : Some (Star R)}
          (chk : Check P xs ys) -> P (Some.edge (checkedEdge chk))
uncheck (check p) = p

All : {A : Set}{R : Rel A}(P : EdgePred R) -> EdgePred (Star R)
All P {a}{b} xs = Star (Check P) (some xs) (some {a = b} ε)

data Lookup {A : Set}{R : Rel A}(P Q : EdgePred R) : Set where
  result : {a b : A} -> (x : R a b) -> P x -> Q x -> Lookup P Q

lookup : {A : Set}{R : Rel A}{P Q : EdgePred R}{a b : A}{xs : Star R a b} ->
         Any P xs -> All Q xs -> Lookup (\{a b} -> P{a}{b}) Q
lookup (step   • i) (check _ • xs) = lookup i xs
lookup (done p • ε) (check q • _ ) = result _ p q
lookup (done p • (() • _)) (check q • _ )


open import Oscar.Prelude
open import Oscar.Class
open import Oscar.Class.Transitivity
open import Oscar.Class.Reflexivity
open import Oscar.Class.Transleftidentity
open import Oscar.Class.Transrightidentity
open import Oscar.Class.Symmetry
open import Oscar.Class.Hmap
open import Oscar.Class.Leftunit

module Oscar.Class.Hmap.Transleftidentity where

instance
  Relprop'idFromTransleftidentity : ∀
    {𝔵} {𝔛 : Ø 𝔵}
    {𝔞} {𝔄 : 𝔛 → Ø 𝔞}
    {𝔟} {𝔅 : 𝔛 → Ø 𝔟}
    (let _∼_ = Arrow 𝔄 𝔅)
    {ℓ̇} {_∼̇_ : ∀ {x y} → x ∼ y → x ∼ y → Ø ℓ̇}
    {transitivity : Transitivity.type _∼_}
    {reflexivity : Reflexivity.type _∼_}
    {ℓ}
    ⦃ _ : Transleftidentity.class _∼_ _∼̇_ reflexivity transitivity ⦄
    ⦃ _ : ∀ {x y} → Symmetry.class (_∼̇_ {x} {y}) ⦄
    → ∀ {m n}
    → Hmap.class (λ (f : m ∼ n) (P : LeftExtensionṖroperty ℓ _∼_ _∼̇_ m) → π₀ (π₀ P) f)
                 (λ f P → π₀ (π₀ P) (transitivity f reflexivity))
  Relprop'idFromTransleftidentity .⋆ _ (_ , P₁) = P₁ $ symmetry transleftidentity


------------------------------------------------------------------------
-- The Agda standard library
--
-- Conversion of < to ≤, along with a number of properties
------------------------------------------------------------------------

-- Possible TODO: Prove that a conversion ≤ → < → ≤ returns a
-- relation equivalent to the original one (and similarly for
-- < → ≤ → <).

open import Relation.Binary

module Relation.Binary.StrictToNonStrict
         {a ℓ₁ ℓ₂} {A : Set a}
         (_≈_ : Rel A ℓ₁) (_<_ : Rel A ℓ₂)
         where

open import Relation.Nullary
open import Relation.Binary.Consequences
open import Function
open import Data.Product
open import Data.Sum
open import Data.Empty

------------------------------------------------------------------------
-- Conversion

-- _<_ can be turned into _≤_ as follows:

_≤_ : Rel A _
x ≤ y = (x < y) ⊎ (x ≈ y)

------------------------------------------------------------------------
-- The converted relations have certain properties
-- (if the original relations have certain other properties)

reflexive : _≈_ ⇒ _≤_
reflexive = inj₂

antisym : IsEquivalence _≈_ →
          Transitive _<_ →
          Irreflexive _≈_ _<_ →
          Antisymmetric _≈_ _≤_
antisym eq trans irrefl = as
  where
  module Eq = IsEquivalence eq

  as : Antisymmetric _≈_ _≤_
  as (inj₂ x≈y) _          = x≈y
  as (inj₁ _)   (inj₂ y≈x) = Eq.sym y≈x
  as (inj₁ x<y) (inj₁ y<x) =
    ⊥-elim (trans∧irr⟶asym {_≈_ = _≈_} Eq.refl trans irrefl x<y y<x)

trans : IsEquivalence _≈_ → _<_ Respects₂ _≈_ →
        Transitive _<_ → Transitive _≤_
trans eq <-resp-≈ <-trans = tr
  where
  module Eq = IsEquivalence eq

  tr : Transitive _≤_
  tr (inj₁ x<y) (inj₁ y<z) = inj₁ $ <-trans x<y y<z
  tr (inj₁ x<y) (inj₂ y≈z) = inj₁ $ proj₁ <-resp-≈ y≈z x<y
  tr (inj₂ x≈y) (inj₁ y<z) = inj₁ $ proj₂ <-resp-≈ (Eq.sym x≈y) y<z
  tr (inj₂ x≈y) (inj₂ y≈z) = inj₂ $ Eq.trans x≈y y≈z

≤-resp-≈ : IsEquivalence _≈_ → _<_ Respects₂ _≈_ → _≤_ Respects₂ _≈_
≤-resp-≈ eq <-resp-≈ = ((λ {_ _ _} → resp₁) , (λ {_ _ _} → resp₂))
  where
  module Eq = IsEquivalence eq

  resp₁ : ∀ {x y' y} → y' ≈ y → x  ≤ y' → x ≤ y
  resp₁ y'≈y (inj₁ x<y') = inj₁ (proj₁ <-resp-≈ y'≈y x<y')
  resp₁ y'≈y (inj₂ x≈y') = inj₂ (Eq.trans x≈y' y'≈y)

  resp₂ : ∀ {y x' x} → x' ≈ x → x' ≤ y  → x ≤ y
  resp₂ x'≈x (inj₁ x'<y) = inj₁ (proj₂ <-resp-≈ x'≈x x'<y)
  resp₂ x'≈x (inj₂ x'≈y) = inj₂ (Eq.trans (Eq.sym x'≈x) x'≈y)

total : Trichotomous _≈_ _<_ → Total _≤_
total <-tri x y with <-tri x y
... | tri< x<y x≉y x≯y = inj₁ (inj₁ x<y)
... | tri≈ x≮y x≈y x≯y = inj₁ (inj₂ x≈y)
... | tri> x≮y x≉y x>y = inj₂ (inj₁ x>y)

decidable : Decidable _≈_ → Decidable _<_ → Decidable _≤_
decidable ≈-dec <-dec x y with ≈-dec x y | <-dec x y
... | yes x≈y | _       = yes (inj₂ x≈y)
... | no  x≉y | yes x<y = yes (inj₁ x<y)
... | no  x≉y | no  x≮y = no helper
  where
  helper : x ≤ y → ⊥
  helper (inj₁ x<y) = x≮y x<y
  helper (inj₂ x≈y) = x≉y x≈y

decidable' : Trichotomous _≈_ _<_ → Decidable _≤_
decidable' compare x y with compare x y
... | tri< x<y _   _ = yes (inj₁ x<y)
... | tri≈ _   x≈y _ = yes (inj₂ x≈y)
... | tri> x≮y x≉y _ = no helper
  where
  helper : x ≤ y → ⊥
  helper (inj₁ x<y) = x≮y x<y
  helper (inj₂ x≈y) = x≉y x≈y


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

module Cubical.Data.Group.Base where

import Cubical.Core.Glue as G

open import Cubical.Foundations.Prelude hiding ( comp )

import Cubical.Foundations.Isomorphism as I
import Cubical.Foundations.Equiv as E
import Cubical.Foundations.HAEquiv as HAE

record isGroup {ℓ} (A : Type ℓ) : Type ℓ where
  constructor group-struct
  field
    id  : A
    inv  : A → A
    comp  : A → A → A
    lUnit : ∀ a → comp id a ≡ a
    rUnit : ∀ a → comp a id ≡ a
    assoc  : ∀ a b c → comp (comp a b) c ≡ comp a (comp b c)
    lCancel  : ∀ a → comp (inv a) a ≡ id
    rCalcel : ∀ a → comp a (inv a) ≡ id

record Group {ℓ} : Type (ℓ-suc ℓ) where
  constructor group
  field
    type : Type ℓ
    setStruc : isSet type
    groupStruc : isGroup type

isMorph : ∀ {ℓ ℓ'} (G : Group {ℓ}) (H : Group {ℓ'}) → (f : (Group.type G → Group.type H)) → Type (ℓ-max ℓ ℓ')
isMorph (group G Gset (group-struct _ _ _⊙_ _ _ _ _ _))
        (group H Hset (group-struct _ _ _∘_ _ _ _ _ _)) f
        = (g0 g1 : G) → f (g0 ⊙ g1) ≡ (f g0) ∘ (f g1)

morph : ∀ {ℓ ℓ'} (G : Group {ℓ}) (H : Group {ℓ'}) → Type (ℓ-max ℓ ℓ')
morph G H = Σ (Group.type G →  Group.type H) (isMorph G H)

record Iso {ℓ ℓ'} (G : Group {ℓ}) (H : Group {ℓ'}) : Type (ℓ-max ℓ ℓ') where
  constructor iso
  field
    fun : morph G H
    inv : morph H G
    rightInv : I.section (fun .fst) (inv .fst)
    leftInv : I.retract (fun .fst) (inv .fst)

record Iso' {ℓ ℓ'} (G : Group {ℓ}) (H : Group {ℓ'}) : Type (ℓ-max ℓ ℓ') where
  constructor iso'
  field
    isoSet : I.Iso (Group.type G) (Group.type H)
    isoSetMorph : isMorph G H (I.Iso.fun isoSet)

_≃_ : ∀ {ℓ ℓ'} (A : Group {ℓ}) (B : Group {ℓ'}) → Type (ℓ-max ℓ ℓ')
A ≃ B = Σ (morph A B) \ f → (G.isEquiv (f .fst))

Iso'→Iso : ∀ {ℓ ℓ'} {G : Group {ℓ}} {H : Group {ℓ'}} → Iso' G  H → Iso G H
Iso'→Iso {G = group G Gset Ggroup} {H = group H Hset Hgroup} i = iso (fun , funMorph) (inv , invMorph) rightInv leftInv
  where
    G_ : Group
    G_ = (group G Gset Ggroup)

    H_ : Group
    H_ = (group H Hset Hgroup)

    open Iso'

    fun : G → H
    fun = I.Iso.fun (isoSet i)

    inv : H → G
    inv = I.Iso.inv (isoSet i)

    rightInv : I.section fun inv
    rightInv = I.Iso.rightInv (isoSet i)

    leftInv : I.retract fun inv
    leftInv = I.Iso.leftInv (isoSet i)

    e' : G G.≃ H
    e' = E.isoToEquiv (I.iso fun inv rightInv leftInv)

    funMorph : isMorph G_ H_ fun
    funMorph = isoSetMorph i

    _∘_ : H → H → H
    _∘_ = isGroup.comp Hgroup

    _⊙_ : G → G → G
    _⊙_ = isGroup.comp Ggroup

    invMorph : isMorph H_ G_ inv
    invMorph h0 h1 = E.invEq (HAE.congEquiv e')
          (fun (inv (h0 ∘ h1)) ≡⟨ rightInv (h0 ∘ h1) ⟩
           h0 ∘ h1 ≡⟨ cong (λ x → x ∘ h1) (sym (rightInv h0)) ⟩
           (fun (inv h0)) ∘ h1 ≡⟨ cong (λ x → fun (inv h0) ∘ x) (sym (rightInv h1)) ⟩
           (fun (inv h0)) ∘ (fun (inv h1)) ≡⟨ sym (funMorph (inv h0) (inv h1)) ⟩
           fun ((inv h0) ⊙ (inv h1)) ∎ )

Equiv→Iso' : ∀ {ℓ ℓ'} {G : Group {ℓ}} {H : Group {ℓ'}} → G ≃ H → Iso' G H
Equiv→Iso' {G = group G Gset Ggroup}
           {H = group H Hset Hgroup}
           e = iso' i' (e .fst .snd)
  where
    e' : G G.≃ H
    e' = (e .fst .fst) , (e .snd)

    i' : I.Iso G H
    i' = E.equivToIso e'

compMorph : ∀ {ℓ} {F G H : Group {ℓ}} (I : morph F G) (J : morph G H) → morph F H
compMorph {ℓ} {group F Fset (group-struct _ _ _⊙_ _ _ _ _ _)}
              {group G Gset (group-struct _ _ _∙_ _ _ _ _ _)}
              {group H Hset (group-struct _ _ _∘_ _ _ _ _ _)}
              (i , ic) (j , jc) = k , kc
  where
    k : F → H
    k f = j (i f)

    kc : (g0 g1 : F) → k (g0 ⊙ g1) ≡ (k g0) ∘ (k g1)
    kc g0 g1 = j (i (g0 ⊙ g1)) ≡⟨ cong j (ic _ _) ⟩
                j (i g0 ∙ i g1) ≡⟨ jc _ _ ⟩
                j (i g0) ∘ j (i g1) ∎

compIso : ∀ {ℓ} {F G H : Group {ℓ}} (I : Iso F G) (J : Iso G H) → Iso F H
compIso {ℓ} {F} {G} {H}
        (iso F→G G→F fg gf) (iso G→H H→G gh hg ) = iso F→H H→F sec ret
  where
    F→H : morph F H
    F→H = compMorph {ℓ} {F} {G} {H} F→G G→H

    H→F : morph H F
    H→F = compMorph {ℓ} {H} {G} {F} H→G G→F

    open Group

    f→h : F .type → H .type
    f→h = F→H .fst

    f→g : F .type → G .type
    f→g = F→G .fst

    g→h : G .type → H .type
    g→h = G→H .fst

    h→f : H .type → F .type
    h→f = H→F .fst

    h→g : H .type → G .type
    h→g = H→G .fst

    g→f : G .type → F .type
    g→f = G→F .fst

    sec : I.section f→h h→f
    sec h = f→h (h→f h) ≡⟨ cong g→h (fg (h→g h)) ⟩
             g→h (h→g h) ≡⟨ gh _ ⟩
             h ∎

    ret : I.retract f→h h→f
    ret f = h→f (f→h f) ≡⟨ cong g→f (hg (f→g f)) ⟩
             g→f (f→g f) ≡⟨ gf _ ⟩
             f ∎


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

open import Cubical.Data.NatMinusOne
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.HITs.Sn

data  ∥_∥_ {ℓ} (A : Type ℓ) (n : ℕ₋₁) : Type ℓ where
  ∣_∣ : A  → ∥ A ∥ n
  hub : (f : S (1+ n) → ∥ A ∥ n) → ∥ A ∥ n
  spoke : (f : S (1+ n) → ∥ A ∥ n) (x : S (1+ n)) → hub f ≡ f x


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

open import HoTT
import homotopy.HopfConstruction
open import homotopy.CircleHSpace
open import homotopy.SuspensionJoin using () renaming (e to suspension-join)
open import homotopy.S1SuspensionS0 using () renaming (e to S¹≃SuspensionS⁰)
import homotopy.JoinAssoc

module homotopy.Hopf where

-- To move
S⁰ = Bool
S² = Suspension S¹
S³ = Suspension S²

-- To move
S¹-connected : is-connected ⟨0⟩ S¹
S¹-connected =
  ([ base ] , Trunc-elim (λ x → =-preserves-level ⟨0⟩ Trunc-level)
             (S¹-elim idp (prop-has-all-paths-↓ ((Trunc-level :> is-set (Trunc ⟨0⟩ S¹)) _ _))))

module Hopf = homotopy.HopfConstruction S¹ S¹-connected S¹-hSpace

Hopf : S² → Type₀
Hopf = Hopf.H.f

Hopf-fiber : Hopf (north _) == S¹
Hopf-fiber = idp

Hopf-total : Σ _ Hopf == S³
Hopf-total =
  Σ _ Hopf               =⟨ Hopf.theorem ⟩
  S¹ * S¹                =⟨ ua S¹≃SuspensionS⁰ |in-ctx (λ u → u * S¹) ⟩
  (Suspension S⁰) * S¹   =⟨ ua (suspension-join S⁰) |in-ctx (λ u → u * S¹) ⟩
  (S⁰ * S⁰) * S¹         =⟨ ua (homotopy.JoinAssoc.*-assoc S⁰ S⁰ S¹) ⟩
  S⁰ * (S⁰ * S¹)         =⟨ ! (ua (suspension-join S¹)) |in-ctx (λ u → S⁰ * u) ⟩
  S⁰ * S²                =⟨ ! (ua (suspension-join S²)) ⟩
  S³ ∎


-- ----------------------------------------------------------------------
-- The Agda Descriptor Library
--
-- Natural transformations on indexed families (predicates)
-- ----------------------------------------------------------------------

module Relation.Unary.Predicate.Transformation where

open import Data.Empty.Polymorphic using (⊥)
open import Data.Product using (_×_)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Unit.Polymorphic using (⊤)
open import Function using (_∘_)
open import Level using (Level; _⊔_; 0ℓ)
open import Relation.Unary

private
  variable
    a b c d ℓ₁ ℓ₂ ℓ₃ ℓ₄ : Level
    A : Set a
    B : Set b
    C : Set c
    D : Set d

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

-- A predicate transformation is a morphism between 2 predicates
-- indexed by A and B (w/ levels ℓ₁ ℓ₂ for universe polymorphism)

infixr 0 _▷_ _►_

_▷_ : Set a → Set b → (ℓ₁ ℓ₂ : Level) → Set _
(A ▷ B) ℓ₁ ℓ₂ = Pred A ℓ₁ → Pred B ℓ₂

_►_ : Set a → Set b → Set _
(A ► B) = Pred A 0ℓ → Pred B 0ℓ

-- ----------------------------------------------------------------------
-- Composition and Identity

id : (A ▷ A) ℓ₁ ℓ₁
id = Function.id

_○_ : (B ▷ C) ℓ₂ ℓ₃ → (A ▷ B) ℓ₁ ℓ₂ → (A ▷ C) ℓ₁ _
X ○ Y = X ∘ Y

-- TODO: Cateogrical view

-- ----------------------------------------------------------------------
-- Special transformations

empty : (A ▷ B) ℓ₁ ℓ₂
empty = λ _ _ → ⊥

univ : (A ▷ B) ℓ₁ ℓ₂
univ = λ _ _ → ⊤

-- ----------------------------------------------------------------------
-- Operations on transformations

infixr 8 _⇉_
infixl 7 _⋏_
infixl 6 _⋎_

-- Negation
∼ : (A ▷ B) ℓ₁ ℓ₂ → (A ▷ B) ℓ₁ ℓ₂
∼ X = ∁ ∘ X

-- Implication
_⇉_ : (A ▷ B) ℓ₁ ℓ₂ → (A ▷ B) ℓ₁ ℓ₂ → (A ▷ B) ℓ₁ ℓ₂
X ⇉ Y = λ P → X P ⇒ Y P

-- Intersection
_⋏_ : (A ▷ B) ℓ₁ ℓ₂ → (A ▷ B) ℓ₁ ℓ₂ → (A ▷ B) ℓ₁ ℓ₂
X ⋏ Y = λ P → X P ∩ Y P

-- Union
_⋎_ : (A ▷ B) ℓ₁ ℓ₂ → (A ▷ B) ℓ₁ ℓ₂ → (A ▷ B) ℓ₁ ℓ₂
X ⋎ Y = λ P → X P ∪ Y P

-- TODO: Infinitary union + intersection

-- ----------------------------------------------------------------------
-- Combinators on transformations

-- Disjoint sum
_∣∣_ : (A ▷ C) ℓ₁ ℓ₂ → (B ▷ D) ℓ₁ ℓ₂ → (A ⊎ B ▷ C ⊎ D) ℓ₁ ℓ₂
(X ∣∣ Y) P (inj₁ x) = X (P ∘ inj₁) x
(X ∣∣ Y) P (inj₂ y) = Y (P ∘ inj₂) y

-- TODO: Product, etc


module Preduploid where

open import Relation.Unary using (Pred)
open import Relation.Binary using (REL)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym)

open import Level

data Polarity : Set where
  + : Polarity
  ⊝ : Polarity

private
  variable p q r s : Polarity

record Preduploid o ℓ : Set (suc (o ⊔ ℓ)) where
  infix 4 _⇒_
  infix 9 _∙_ _∘_ _⊙_

  field
    Ob : Polarity -> Set o
    _⇒_ : REL (Ob p) (Ob q) ℓ

    id : forall {A : Ob p} -> A ⇒ A
    _∙_ : forall {A : Ob p} {B : Ob +} {C : Ob q} -> B ⇒ C -> A ⇒ B -> A ⇒ C
    _∘_ : forall {A : Ob p} {B : Ob ⊝} {C : Ob q} -> B ⇒ C -> A ⇒ B -> A ⇒ C

    identity∙ˡ : forall {A : Ob p} {B : Ob +} {f : A ⇒ B} -> id ∙ f ≡ f
    identity∙ʳ : forall {A : Ob +} {B : Ob p} {f : A ⇒ B} -> f ∙ id ≡ f

    identity∘ˡ : forall {A : Ob p} {B : Ob ⊝} {f : A ⇒ B} -> id ∘ f ≡ f
    identity∘ʳ : forall {A : Ob ⊝} {B : Ob p} {f : A ⇒ B} -> f ∘ id ≡ f

    assoc∙∙ : forall {A : Ob p} {B : Ob +} {C : Ob +} {D : Ob q}
      {f : A ⇒ B} {g : B ⇒ C} {h : C ⇒ D} -> (h ∙ g) ∙ f ≡ h ∙ (g ∙ f)
    assoc∘∘ : forall {A : Ob p} {B : Ob ⊝} {C : Ob ⊝} {D : Ob q}
      {f : A ⇒ B} {g : B ⇒ C} {h : C ⇒ D} -> (h ∘ g) ∘ f ≡ h ∘ (g ∘ f)
    assoc∙∘ : forall {A : Ob p} {B : Ob ⊝} {C : Ob +} {D : Ob q}
      {f : A ⇒ B} {g : B ⇒ C} {h : C ⇒ D} -> (h ∙ g) ∘ f ≡ h ∙ (g ∘ f)

  π : Ob p -> Polarity
  π {p = p} _ = p

  _⊙_ : forall {A : Ob p} {B : Ob q} {C : Ob r} -> B ⇒ C -> A ⇒ B -> A ⇒ C
  _⊙_ {q = +} = _∙_
  _⊙_ {q = ⊝} = _∘_

  identityˡ : forall {A : Ob p} {B : Ob q} {f : A ⇒ B} -> id ⊙ f ≡ f
  identityˡ {q = +} = identity∙ˡ
  identityˡ {q = ⊝} = identity∘ˡ

  identityʳ : forall {A : Ob p} {B : Ob q} {f : A ⇒ B} -> f ⊙ id ≡ f
  identityʳ {p = +} = identity∙ʳ
  identityʳ {p = ⊝} = identity∘ʳ

  Linear : forall {C : Ob r} {D : Ob s} -> Pred (C ⇒ D) (o ⊔ ℓ)
  Linear {C = C} {D = D} f = forall {p q} {A : Ob p} {B : Ob q} {g : B ⇒ C} {h : A ⇒ B} -> f ⊙ (g ⊙ h) ≡ (f ⊙ g) ⊙ h

  Thunkable : forall {A : Ob p} {B : Ob q} -> Pred (A ⇒ B) (o ⊔ ℓ)
  Thunkable {A = A} {B = B} f = forall {r s} {C : Ob r} {D : Ob s} {g : B ⇒ C} {h : C ⇒ D} -> h ⊙ (g ⊙ f) ≡ (h ⊙ g) ⊙ f

  P-Linear : forall {P : Ob +} {A : Ob p} -> (f : P ⇒ A) -> Linear f
  P-Linear f {q = +} = sym assoc∙∙
  P-Linear f {q = ⊝} = sym assoc∙∘

  N-Thunkable : forall {A : Ob p} {N : Ob ⊝} -> (f : A ⇒ N) -> Thunkable f
  N-Thunkable f {r = +} = sym assoc∙∘
  N-Thunkable f {r = ⊝} = sym assoc∘∘


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

open import HoTT
open import homotopy.HSpace

module homotopy.EM1HSpace where

module EM₁HSpace {i} (A : Group i) (A-abelian : is-abelian A) where

  module A = Group A
  open EM₁ A

  mult-loop : (g : A.El) (x : EM₁) → x == x
  mult-loop g = EM₁-elim
    {P = λ x → x == x}
    (λ _ → =-preserves-level _ emlevel)
    (emloop g)
    loop'
    (set-↓-has-all-paths-↓ (emlevel embase embase))
    (λ _ _ → set-↓-has-all-paths-↓ (emlevel embase embase))
    where
    abstract
      loop' : (g' : A.El) → emloop g == emloop g [ (λ x → x == x) ↓ emloop g' ]
      loop' g' = ↓-idf=idf-in $
        emloop g ∙ emloop g'     =⟨ ! (emloop-comp g g') ⟩
        emloop (A.comp g g')      =⟨ ap emloop (A-abelian g g') ⟩
        emloop (A.comp g' g)      =⟨ emloop-comp g' g ⟩
        emloop g' ∙ emloop g     =⟨ ∙=∙' (emloop g') (emloop g) ⟩
        emloop g' ∙' emloop g    ∎

  mult-hom : GroupHom A
    (Ω^S-group 0 ((EM₁ → EM₁) , (λ x → x)) (Π-level (λ _ → emlevel)))
  mult-hom = record {f = f; pres-comp = pres-comp}
    where
    f = λ g → λ= (mult-loop g)

    pres-comp = λ g₁ g₂ →
      ap λ= (λ= (EM₁-elim
        {P = λ x → mult-loop (A.comp g₁ g₂) x
                == mult-loop g₁ x ∙ mult-loop g₂ x}
        (λ _ →  =-preserves-level _ (=-preserves-level _ emlevel))
        (emloop-comp g₁ g₂)
        (λ _ → prop-has-all-paths-↓ (emlevel _ _ _ _))
        (set-↓-has-all-paths-↓ (=-preserves-level _ (emlevel _ _)))
        (λ _ _ → set-↓-has-all-paths-↓ (=-preserves-level _ (emlevel _ _)))))
      ∙ ! (∙-λ= _ _)

  mult : EM₁ → EM₁ → EM₁
  mult = EM₁-rec {C = EM₁ → EM₁} (Π-level (λ _ → emlevel)) (λ x → x) mult-hom

  H-EM₁ : HSpaceStructure EM₁
  H-EM₁ = record { e = embase; μ = mult; μ-e-l = μ-e-l; μ-e-r = μ-e-r; μ-coh = μ-coh }
    where
    μ-e-l : (x : EM₁) → mult embase x == x
    μ-e-l = EM₁-elim
      {P = λ x → mult embase x == x}
      (λ _ → =-preserves-level 1 emlevel)
      idp
      (λ g → ↓-app=idf-in $ ∙'-unit-l (emloop g) ∙ (! (ap-idf (emloop g)))
                            ∙ ! (∙-unit-r (ap (mult embase) (emloop g))))
      (set-↓-has-all-paths-↓ (emlevel _ _))
      (λ _ _ → set-↓-has-all-paths-↓ (emlevel _ _))

    μ-e-r : (x : EM₁) → mult x embase == x
    μ-e-r = EM₁-elim
      {P = λ x → mult x embase == x}
      (λ _ → =-preserves-level 1 emlevel)
      idp
      (λ g → ↓-app=idf-in $
         idp ∙' emloop g
           =⟨ ∙'-unit-l (emloop g) ⟩
         emloop g
           =⟨ ! (app=-β (mult-loop g) embase) ⟩
         app= (λ= (mult-loop g)) embase
           =⟨ ! (EM₁Rec.emloop-β {C = EM₁ → EM₁}
                   (Π-level (λ _ → emlevel)) (λ x → x) mult-hom g)
              |in-ctx (λ w → app= w embase) ⟩
         app= (ap mult (emloop g)) embase
           =⟨ ∘-ap (λ f → f embase) mult (emloop g) ⟩
         ap (λ z → mult z embase) (emloop g)
           =⟨ ! (∙-unit-r (ap (λ z → mult z embase) (emloop g))) ⟩
         ap (λ z → mult z embase) (emloop g) ∙ idp ∎)
      (set-↓-has-all-paths-↓ (emlevel _ _))
      (λ _ _ → set-↓-has-all-paths-↓ (emlevel _ _))

    μ-coh : μ-e-l embase == μ-e-r embase
    μ-coh = idp

  open HSpaceStructure H-EM₁


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

open import M-types.Base.Core
open import M-types.Base.Sum
open import M-types.Base.Prod
open import M-types.Base.Eq

module M-types.Base.Contr where
    IsContr : ∏[ X ∈ Ty ℓ ] Ty ℓ
    IsContr X = ∑[ x ∈ X ] ∏[ x′ ∈ X ] x′ ≡ x

    Contr : Ty (ℓ-suc ℓ)
    Contr {ℓ} = ∑[ X ∈ Ty ℓ ] IsContr X


import      Lvl
open import Structure.Operator.Vector
open import Structure.Setoid
open import Type

module Structure.Operator.Vector.LinearCombination.Proofs
  {ℓᵥ ℓₛ ℓᵥₑ ℓₛₑ}
  {V : Type{ℓᵥ}} ⦃ equiv-V : Equiv{ℓᵥₑ}(V) ⦄
  {S : Type{ℓₛ}} ⦃ equiv-S : Equiv{ℓₛₑ}(S) ⦄
  {_+ᵥ_ : V → V → V}
  {_⋅ₛᵥ_ : S → V → V}
  {_+ₛ_ _⋅ₛ_ : S → S → S}
  ⦃ vectorSpace : VectorSpace(_+ᵥ_)(_⋅ₛᵥ_)(_+ₛ_)(_⋅ₛ_) ⦄
  where

open VectorSpace(vectorSpace)

import      Lvl
open import Function.Equals
open import Logic.Predicate
open import Numeral.CoordinateVector as Vec using () renaming (Vector to Vec)
open import Numeral.Finite
open import Numeral.Natural
open import Structure.Function.Multi
import      Structure.Function.Names as Names
open import Structure.Operator.Proofs.Util
open import Structure.Operator.Properties
open import Structure.Operator
open import Structure.Operator.Vector.LinearCombination ⦃ vectorSpace = vectorSpace ⦄
open import Structure.Operator.Vector.Proofs
open import Structure.Relator.Properties
open import Syntax.Function
open import Syntax.Number
open import Syntax.Transitivity
open import Type

private variable ℓ ℓ₁ ℓ₂ ℓₗ : Lvl.Level
private variable n n₁ n₂ : ℕ
private variable i j k : 𝕟(n)
private variable vf vf₁ vf₂ : Vec(n)(V)
private variable sf sf₁ sf₂ : Vec(n)(S)

instance
  linearCombination-binaryOperator : BinaryOperator(linearCombination{n})
  linearCombination-binaryOperator = intro p where
    p : Names.Congruence₂(linearCombination{n})
    p {𝟎}       {vf₁} {vf₂} (intro vfeq) {sf₁} {sf₂} (intro sfeq) = reflexivity(_≡_)
    p {𝐒(𝟎)}    {vf₁} {vf₂} (intro vfeq) {sf₁} {sf₂} (intro sfeq) = congruence₂(_⋅ₛᵥ_) sfeq vfeq
    p {𝐒(𝐒(n))} {vf₁} {vf₂} (intro vfeq) {sf₁} {sf₂} (intro sfeq) =
      (sf₁(𝟎) ⋅ₛᵥ vf₁(𝟎)) +ᵥ linearCombination(Vec.tail vf₁) (Vec.tail sf₁) 🝖[ _≡_ ]-[ congruence₂(_+ᵥ_) (congruence₂(_⋅ₛᵥ_) sfeq vfeq) (p {𝐒(n)} (intro vfeq) (intro sfeq)) ]
      (sf₂(𝟎) ⋅ₛᵥ vf₂(𝟎)) +ᵥ linearCombination(Vec.tail vf₂) (Vec.tail sf₂) 🝖-end

instance
  linearCombination-scalar-preserves-[+] : Preserving₂(linearCombination vf) (Vec.map₂(_+ₛ_)) (_+ᵥ_)
  linearCombination-scalar-preserves-[+] {vf = vf} = intro(p{vf = vf}) where
    p : ∀{n}{vf : Vec(n)(V)} → Names.Preserving₂(linearCombination vf) (Vec.map₂(_+ₛ_)) (_+ᵥ_)
    p {𝟎}{vf} {sf₁} {sf₂} =
      𝟎ᵥ       🝖[ _≡_ ]-[ identityₗ(_+ᵥ_)(𝟎ᵥ) ]-sym
      𝟎ᵥ +ᵥ 𝟎ᵥ 🝖-end
    p {𝐒(𝟎)}{vf} {sf₁} {sf₂} =
      (Vec.map₂(_+ₛ_) sf₁ sf₂ 𝟎) ⋅ₛᵥ vf(𝟎)     🝖[ _≡_ ]-[]
      (sf₁(𝟎) +ₛ sf₂(𝟎)) ⋅ₛᵥ vf(𝟎)             🝖[ _≡_ ]-[ [⋅ₛᵥ][+ₛ][+ᵥ]-distributivityᵣ ]
      (sf₁(𝟎) ⋅ₛᵥ vf(𝟎)) +ᵥ (sf₂(𝟎) ⋅ₛᵥ vf(𝟎)) 🝖-end
    p {𝐒(𝐒(n))}{vf} {sf₁} {sf₂} =
      ((Vec.map₂(_+ₛ_) sf₁ sf₂ 𝟎) ⋅ₛᵥ vf(𝟎)) +ᵥ (linearCombination {𝐒(n)} (Vec.tail vf) (Vec.tail(Vec.map₂(_+ₛ_) sf₁ sf₂)))                                                🝖[ _≡_ ]-[]
      ((sf₁(𝟎) +ₛ sf₂(𝟎)) ⋅ₛᵥ vf(𝟎)) +ᵥ (linearCombination {𝐒(n)} (Vec.tail vf) (Vec.tail(Vec.map₂(_+ₛ_) sf₁ sf₂)))                                                        🝖[ _≡_ ]-[ congruence₂(_+ᵥ_) [⋅ₛᵥ][+ₛ][+ᵥ]-distributivityᵣ (p {𝐒(n)}{Vec.tail vf} {Vec.tail sf₁} {Vec.tail sf₂}) ]
      ((sf₁(𝟎) ⋅ₛᵥ vf(𝟎)) +ᵥ (sf₂(𝟎) ⋅ₛᵥ vf(𝟎))) +ᵥ ((linearCombination {𝐒(n)} (Vec.tail vf) (Vec.tail sf₁)) +ᵥ (linearCombination {𝐒(n)} (Vec.tail vf) (Vec.tail sf₂)))   🝖[ _≡_ ]-[ One.associate-commute4 (commutativity(_+ᵥ_)) ]
      (((sf₁(𝟎) ⋅ₛᵥ vf(𝟎)) +ᵥ (linearCombination {𝐒(n)} (Vec.tail vf) (Vec.tail sf₁))) +ᵥ ((sf₂(𝟎) ⋅ₛᵥ vf(𝟎)) +ᵥ (linearCombination {𝐒(n)} (Vec.tail vf) (Vec.tail sf₂)))) 🝖-end

instance
  linearCombination-scalar-preserves-[⋅] : ∀{s} → Preserving₁(linearCombination vf) (Vec.map(s ⋅ₛ_)) (s ⋅ₛᵥ_)
  linearCombination-scalar-preserves-[⋅] {vf = vf} {s = s} = intro(p{vf = vf}) where
    p : ∀{n}{vf : Vec(n)(V)} → Names.Preserving₁(linearCombination vf) (Vec.map(s ⋅ₛ_)) (s ⋅ₛᵥ_)
    p {𝟎} {vf} {sf} =
      𝟎ᵥ       🝖[ _≡_ ]-[ [⋅ₛᵥ]-absorberᵣ ]-sym
      s ⋅ₛᵥ 𝟎ᵥ 🝖-end
    p {𝐒(𝟎)} {vf} {sf} =
      (s ⋅ₛ sf(𝟎)) ⋅ₛᵥ vf(𝟎)  🝖[ _≡_ ]-[ [⋅ₛ][⋅ₛᵥ]-compatibility ]
      s ⋅ₛᵥ (sf(𝟎) ⋅ₛᵥ vf(𝟎)) 🝖-end
    p {𝐒(𝐒(n))} {vf} {sf} =
      linearCombination vf (Vec.map (s ⋅ₛ_) sf)                                                     🝖[ _≡_ ]-[]
      ((s ⋅ₛ sf(𝟎)) ⋅ₛᵥ vf(𝟎)) +ᵥ (linearCombination (Vec.tail vf) (Vec.map (s ⋅ₛ_) (Vec.tail sf))) 🝖[ _≡_ ]-[ congruence₂(_+ᵥ_) ⦃ [+ᵥ]-binary-operator ⦄ [⋅ₛ][⋅ₛᵥ]-compatibility (p {𝐒(n)} {Vec.tail vf} {Vec.tail sf}) ]
      (s ⋅ₛᵥ (sf(𝟎) ⋅ₛᵥ vf(𝟎))) +ᵥ (s ⋅ₛᵥ (linearCombination (Vec.tail vf) (Vec.tail sf)))          🝖[ _≡_ ]-[ distributivityₗ(_⋅ₛᵥ_)(_+ᵥ_) ]-sym
      s ⋅ₛᵥ ((sf(𝟎) ⋅ₛᵥ vf(𝟎)) +ᵥ (linearCombination (Vec.tail vf) (Vec.tail sf)))                  🝖[ _≡_ ]-[]
      s ⋅ₛᵥ (linearCombination vf sf)                                                               🝖-end

-- linearCombination-of-unit : linearCombination vf (Vec.fill 𝟏ₛ) ≡ (foldᵣ(_+_) 𝟎ᵥ vf)
postulate linearCombination-of-indexProject : (linearCombination vf (Vec.indexProject i 𝟏ₛ 𝟎ₛ) ≡ vf(i))


------------------------------------------------------------------------
-- Pointwise products of binary relations
------------------------------------------------------------------------

module Relation.Binary.Product.Pointwise where

open import Data.Function
open import Data.Product
open import Data.Sum
open import Relation.Nullary.Product
open import Relation.Binary

private
 module Dummy {a₁ a₂ : Set} where

  infixr 2 _×-Rel_

  _×-Rel_ : Rel a₁ → Rel a₂ → Rel (a₁ × a₂)
  ∼₁ ×-Rel ∼₂ = (∼₁ on₁ proj₁) -×- (∼₂ on₁ proj₂)

  -- Some properties which are preserved by ×-Rel (under certain
  -- assumptions).

  _×-reflexive_ : ∀ {≈₁ ∼₁ ≈₂ ∼₂} →
                  ≈₁ ⇒ ∼₁ → ≈₂ ⇒ ∼₂ → (≈₁ ×-Rel ≈₂) ⇒ (∼₁ ×-Rel ∼₂)
  refl₁ ×-reflexive refl₂ = λ x≈y →
    (refl₁ (proj₁ x≈y) , refl₂ (proj₂ x≈y))

  _×-refl_ : ∀ {∼₁ ∼₂} →
             Reflexive ∼₁ → Reflexive ∼₂ → Reflexive (∼₁ ×-Rel ∼₂)
  refl₁ ×-refl refl₂ = (refl₁ , refl₂)

  ×-irreflexive₁ :
    ∀ {≈₁ <₁ ≈₂ <₂} →
    Irreflexive ≈₁ <₁ → Irreflexive (≈₁ ×-Rel ≈₂) (<₁ ×-Rel <₂)
  ×-irreflexive₁ ir = λ x≈y x<y → ir (proj₁ x≈y) (proj₁ x<y)

  ×-irreflexive₂ :
    ∀ {≈₁ <₁ ≈₂ <₂} →
    Irreflexive ≈₂ <₂ → Irreflexive (≈₁ ×-Rel ≈₂) (<₁ ×-Rel <₂)
  ×-irreflexive₂ ir = λ x≈y x<y → ir (proj₂ x≈y) (proj₂ x<y)

  _×-symmetric_ : ∀ {∼₁ ∼₂} →
                  Symmetric ∼₁ → Symmetric ∼₂ → Symmetric (∼₁ ×-Rel ∼₂)
  sym₁ ×-symmetric sym₂ = λ x∼y → sym₁ (proj₁ x∼y) , sym₂ (proj₂ x∼y)

  _×-transitive_ : ∀ {∼₁ ∼₂} →
                   Transitive ∼₁ → Transitive ∼₂ →
                   Transitive (∼₁ ×-Rel ∼₂)
  trans₁ ×-transitive trans₂ = λ x∼y y∼z →
    trans₁ (proj₁ x∼y) (proj₁ y∼z) ,
    trans₂ (proj₂ x∼y) (proj₂ y∼z)

  _×-antisymmetric_ : ∀ {≈₁ ≤₁ ≈₂ ≤₂} →
                      Antisymmetric ≈₁ ≤₁ → Antisymmetric ≈₂ ≤₂ →
                      Antisymmetric (≈₁ ×-Rel ≈₂) (≤₁ ×-Rel ≤₂)
  antisym₁ ×-antisymmetric antisym₂ = λ x≤y y≤x →
    ( antisym₁ (proj₁ x≤y) (proj₁ y≤x)
    , antisym₂ (proj₂ x≤y) (proj₂ y≤x) )

  ×-asymmetric₁ : ∀ {<₁ ∼₂} → Asymmetric <₁ → Asymmetric (<₁ ×-Rel ∼₂)
  ×-asymmetric₁ asym₁ = λ x<y y<x → asym₁ (proj₁ x<y) (proj₁ y<x)

  ×-asymmetric₂ : ∀ {∼₁ <₂} → Asymmetric <₂ → Asymmetric (∼₁ ×-Rel <₂)
  ×-asymmetric₂ asym₂ = λ x<y y<x → asym₂ (proj₂ x<y) (proj₂ y<x)

  _×-≈-respects₂_ : ∀ {≈₁ ∼₁ ≈₂ ∼₂} →
                    ∼₁ Respects₂ ≈₁ → ∼₂ Respects₂ ≈₂ →
                    (∼₁ ×-Rel ∼₂) Respects₂ (≈₁ ×-Rel ≈₂)
  _×-≈-respects₂_ {≈₁ = ≈₁} {∼₁ = ∼₁} {≈₂ = ≈₂} {∼₂ = ∼₂}
                  resp₁ resp₂ =
    (λ {x y z} → resp¹ {x} {y} {z}) ,
    (λ {x y z} → resp² {x} {y} {z})
    where
    ∼ = ∼₁ ×-Rel ∼₂

    resp¹ : ∀ {x} → (∼ x) Respects (≈₁ ×-Rel ≈₂)
    resp¹ y≈y' x∼y = proj₁ resp₁ (proj₁ y≈y') (proj₁ x∼y) ,
                     proj₁ resp₂ (proj₂ y≈y') (proj₂ x∼y)

    resp² : ∀ {y} → (flip₁ ∼ y) Respects (≈₁ ×-Rel ≈₂)
    resp² x≈x' x∼y = proj₂ resp₁ (proj₁ x≈x') (proj₁ x∼y) ,
                     proj₂ resp₂ (proj₂ x≈x') (proj₂ x∼y)

  ×-total : ∀ {∼₁ ∼₂} →
            Symmetric ∼₁ → Total ∼₁ → Total ∼₂ → Total (∼₁ ×-Rel ∼₂)
  ×-total {∼₁ = ∼₁} {∼₂ = ∼₂} sym₁ total₁ total₂ = total
    where
    total : Total (∼₁ ×-Rel ∼₂)
    total x y with total₁ (proj₁ x) (proj₁ y)
                 | total₂ (proj₂ x) (proj₂ y)
    ... | inj₁ x₁∼y₁ | inj₁ x₂∼y₂ = inj₁ (     x₁∼y₁ , x₂∼y₂)
    ... | inj₁ x₁∼y₁ | inj₂ y₂∼x₂ = inj₂ (sym₁ x₁∼y₁ , y₂∼x₂)
    ... | inj₂ y₁∼x₁ | inj₂ y₂∼x₂ = inj₂ (     y₁∼x₁ , y₂∼x₂)
    ... | inj₂ y₁∼x₁ | inj₁ x₂∼y₂ = inj₁ (sym₁ y₁∼x₁ , x₂∼y₂)

  _×-decidable_ : ∀ {∼₁ ∼₂} →
                  Decidable ∼₁ → Decidable ∼₂ → Decidable (∼₁ ×-Rel ∼₂)
  dec₁ ×-decidable dec₂ = λ x y →
    dec₁ (proj₁ x) (proj₁ y)
      ×-dec
    dec₂ (proj₂ x) (proj₂ y)

  -- Some collections of properties which are preserved by ×-Rel.

  _×-isEquivalence_ : ∀ {≈₁ ≈₂} →
                      IsEquivalence ≈₁ → IsEquivalence ≈₂ →
                      IsEquivalence (≈₁ ×-Rel ≈₂)
  _×-isEquivalence_ {≈₁ = ≈₁} {≈₂ = ≈₂} eq₁ eq₂ = record
    { refl  = λ {x} →
              _×-refl_        {∼₁ = ≈₁} {∼₂ = ≈₂}
                              (refl  eq₁) (refl  eq₂) {x}
    ; sym   = λ {x y} →
              _×-symmetric_   {∼₁ = ≈₁} {∼₂ = ≈₂}
                              (sym   eq₁) (sym   eq₂) {x} {y}
    ; trans = λ {x y z} →
              _×-transitive_  {∼₁ = ≈₁} {∼₂ = ≈₂}
                              (trans eq₁) (trans eq₂) {x} {y} {z}
    }
    where open IsEquivalence

  _×-isPreorder_ : ∀ {≈₁ ∼₁ ≈₂ ∼₂} →
                   IsPreorder ≈₁ ∼₁ → IsPreorder ≈₂ ∼₂ →
                   IsPreorder (≈₁ ×-Rel ≈₂) (∼₁ ×-Rel ∼₂)
  _×-isPreorder_ {∼₁ = ∼₁} {∼₂ = ∼₂} pre₁ pre₂ = record
    { isEquivalence = isEquivalence pre₁ ×-isEquivalence
                      isEquivalence pre₂
    ; reflexive     = λ {x y} →
                      _×-reflexive_  {∼₁ = ∼₁} {∼₂ = ∼₂}
                                     (reflexive pre₁) (reflexive pre₂)
                                     {x} {y}
    ; trans         = λ {x y z} →
                      _×-transitive_ {∼₁ = ∼₁} {∼₂ = ∼₂}
                                     (trans pre₁) (trans pre₂)
                                     {x} {y} {z}
    ; ∼-resp-≈      = ∼-resp-≈ pre₁ ×-≈-respects₂ ∼-resp-≈ pre₂
    }
    where open IsPreorder

  _×-isDecEquivalence_ : ∀ {≈₁ ≈₂} →
                         IsDecEquivalence ≈₁ → IsDecEquivalence ≈₂ →
                         IsDecEquivalence (≈₁ ×-Rel ≈₂)
  eq₁ ×-isDecEquivalence eq₂ = record
    { isEquivalence = isEquivalence eq₁ ×-isEquivalence
                      isEquivalence eq₂
    ; _≟_           = _≟_ eq₁ ×-decidable _≟_ eq₂
    }
    where open IsDecEquivalence

  _×-isPartialOrder_ : ∀ {≈₁ ≤₁ ≈₂ ≤₂} →
                       IsPartialOrder ≈₁ ≤₁ → IsPartialOrder ≈₂ ≤₂ →
                       IsPartialOrder (≈₁ ×-Rel ≈₂) (≤₁ ×-Rel ≤₂)
  _×-isPartialOrder_ {≤₁ = ≤₁} {≤₂ = ≤₂} po₁ po₂ = record
    { isPreorder = isPreorder po₁ ×-isPreorder isPreorder po₂
    ; antisym    = λ {x y} →
                   _×-antisymmetric_ {≤₁ = ≤₁} {≤₂ = ≤₂}
                                     (antisym po₁) (antisym po₂)
                                     {x} {y}
    }
    where open IsPartialOrder

  _×-isStrictPartialOrder_ :
    ∀ {≈₁ <₁ ≈₂ <₂} →
    IsStrictPartialOrder ≈₁ <₁ → IsStrictPartialOrder ≈₂ <₂ →
    IsStrictPartialOrder (≈₁ ×-Rel ≈₂) (<₁ ×-Rel <₂)
  _×-isStrictPartialOrder_ {<₁ = <₁} {≈₂ = ≈₂} {<₂ = <₂} spo₁ spo₂ =
    record
      { isEquivalence = isEquivalence spo₁ ×-isEquivalence
                        isEquivalence spo₂
      ; irrefl        = λ {x y} →
                        ×-irreflexive₁ {<₁ = <₁} {≈₂ = ≈₂} {<₂ = <₂}
                                       (irrefl spo₁) {x} {y}
      ; trans         = λ {x y z} →
                        _×-transitive_ {∼₁ = <₁} {∼₂ = <₂}
                                       (trans spo₁) (trans spo₂)
                                       {x} {y} {z}
      ; <-resp-≈      = <-resp-≈ spo₁ ×-≈-respects₂ <-resp-≈ spo₂
      }
    where open IsStrictPartialOrder

open Dummy public

-- "Packages" (e.g. setoids) can also be combined.

_×-preorder_ : Preorder → Preorder → Preorder
p₁ ×-preorder p₂ = record
  { isPreorder = isPreorder p₁ ×-isPreorder isPreorder p₂
  } where open Preorder

_×-setoid_ : Setoid → Setoid → Setoid
s₁ ×-setoid s₂ = record
  { isEquivalence = isEquivalence s₁ ×-isEquivalence isEquivalence s₂
  } where open Setoid

_×-decSetoid_ : DecSetoid → DecSetoid → DecSetoid
s₁ ×-decSetoid s₂ = record
  { isDecEquivalence = isDecEquivalence s₁ ×-isDecEquivalence
                       isDecEquivalence s₂
  } where open DecSetoid

_×-poset_ : Poset → Poset → Poset
s₁ ×-poset s₂ = record
  { isPartialOrder = isPartialOrder s₁ ×-isPartialOrder
                     isPartialOrder s₂
  } where open Poset

_×-strictPartialOrder_ :
  StrictPartialOrder → StrictPartialOrder → StrictPartialOrder
s₁ ×-strictPartialOrder s₂ = record
  { isStrictPartialOrder = isStrictPartialOrder s₁
                             ×-isStrictPartialOrder
                           isStrictPartialOrder s₂
  } where open StrictPartialOrder


open import Agda.Builtin.Equality
open import Agda.Builtin.Nat

foo : Nat → (Nat → Nat) → Nat
foo zero    f = f zero
foo (suc n) f = foo n λ n → f (suc n)

fsuc : (Nat → Nat) → Nat → Nat
fsuc f n = f (suc n)

foo' : Nat → (Nat → Nat) → Nat
foo' zero    f = f zero
foo' (suc n) f = foo' n (fsuc f)

foo'' : Nat → (Nat → Nat) → Nat
foo'' zero    f = f zero
foo'' (suc n) f = foo'' n λ { n → f (suc n) }

test : foo 50000 (λ n → n) ≡ 50000
test = refl

test' : foo' 50000 (λ n → n) ≡ 50000
test' = refl

test'' : foo'' 50000 (λ n → n) ≡ 50000
test'' = refl


-- 2011-09-15 posted by Nisse, variant of Issue292e
-- {-# OPTIONS --show-implicit -v tc.lhs.unify:15 #-}
module Issue292-16b where

data _≡_ {A : Set} (x : A) : A → Set where
  refl : x ≡ x

postulate
  A : Set
  f : A → A

data C (A : Set)(f : A → A) : A → Set where
  c : ∀ x → C A f (f x)

record Box : Set where
  constructor box
  field
    a : A
    b : C A f a

test : ∀ {x₁ x₂} → box (f x₁) (c x₁) ≡ box (f x₂) (c x₂) → x₁ ≡ x₂
test refl = refl

-- We recover from the heteogenerous
--
--   c x₁ : C A f (f x₁) =?= c₂ x₂ : C A f (f x₂)
--
-- to the homogeneous
--
--   x₁ =?= x₂ : A
--
-- since the parameters to C are syntactically equal on lhs and rhs


module _ where

module Batch4 where
  data D1 (open Star) (A : ★) : ★ where
    c : (x : A) → D1 A

  data D2 (open Star) : ★ where

  data D3 (open Star) : ★₁ where
    c : (A : ★) → D3

  data D4 : (open Star) → ★ where

  data D0 (A : Set) (let B = A → A) : B → Set where
    c : (f : B) → D0 A f

  module _ where

    data D5 (open Star) (A : ★) (open MEndo A) : ★

    data D5 A (open MEndo A) where
      c : (f : Endo) → D5 A

  data D6 (open Star) (A : ★) (open MEndo A) : Endo → ★ where
    c : (f : Endo) → D6 A f

module Batch5 where
  data D1 (open Star) (A : ★) : ★

  data D1 A where
    c : (x : A) → D1 A

  data D2 (open Star) : ★
  data D2 where

  data D3 (open Star) : ★₁
  data D3 where
    c : (A : ★) → D3

  data D4 : (open Star) → ★
  data D4 where

open import Common.Equality

module Batch6 where

  BinOp : (A : Set) → Set
  BinOp A = A → A → A

  record IsAssoc {A : Set} (_∘_ : BinOp A) : Set where
    field
      assoc : ∀ {a b c} →
        ((a ∘ b) ∘ c) ≡ (a ∘ (b ∘ c))

  record RawSemiGroup : Set₁ where
    field
      Carrier : Set
      _∘_     : Carrier → Carrier → Carrier

  record SemiGroupLaws1 (G : RawSemiGroup) : Set where
    open RawSemiGroup G
    field
      isAssoc : IsAssoc _∘_

  module _ where
   record SemiGroupBLA
      (G : RawSemiGroup)
      (open RawSemiGroup G)
      (ass : IsAssoc _∘_) : Set
    where

  record SemiGroupLaws (G : RawSemiGroup) (open RawSemiGroup G) : Set where
    field
      isAssoc : IsAssoc {A = Carrier} _∘_

  record SemiGroup : Set₁ where
    field
      rawSemiGroup  : RawSemiGroup
      semiGroupLaws : SemiGroupLaws rawSemiGroup


{-

This file contains:
    - the abelianization of groups as a coequalizer of sets as performed
      in https://1lab.dev/Algebra.Group.Ab.Free.html
    - the proof that this way of defining the abelianization of groups is equivalent to defining
      it as a HIT, more precisely that there is a unique isomorphism between the resulting
      abelian groups

-}
{-# OPTIONS --safe #-}
module Cubical.Algebra.Group.Abelianization.AbelianizationAsCoeq where

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

open import Cubical.Data.Sigma

open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.Properties
open import Cubical.Algebra.Group.Morphisms
open import Cubical.Algebra.Group.MorphismProperties
  using (isPropIsGroupHom; compGroupHom; idGroupHom; compGroupHomAssoc; invGroupIso;
         compGroupHomId; GroupIso≡; GroupIso→GroupHom)

open import Cubical.Algebra.AbGroup.Base

open import Cubical.HITs.SetCoequalizer

open import Cubical.Algebra.Group.Abelianization.Base
  renaming (Abelianization to HITAbelianization;
            η to HITη;
            comm to HITcomm;
            isset to HITisset)

open import Cubical.Algebra.Group.Abelianization.Properties
  hiding (elimProp; elimProp2; elimProp3; elimContr; elimContr2; rec; rec2)
  renaming (AbelianizationAbGroup to HITasAbelianGroup;
            AbelianizationHom to HITηAsGroupHom)
open Cubical.Algebra.Group.Abelianization.Properties.UniversalProperty
  renaming (inducedHom to HITinducedHom;
            commutativity to HITcommutativity;
            uniqueness to HITuniqueness)

private
  variable
    ℓ : Level
    G : Group ℓ

module AbelianizationAsCoeq (G : Group ℓ) where
  open GroupStr {{...}}
  open GroupTheory G
  private
    instance
      _ = snd G
    G' = fst G

  {-
    The definition of the abelianization of a group as a coequalizer of sets. The generality
    of the coequalized functions will be needed to define the group structure on the
    abelianization.
  -}
  Abelianization : Type ℓ
  Abelianization =
    SetCoequalizer {A = G' × G' × G'} {B = G'}
      (λ (x , y , z) → x · y · z)
      (λ (x , y , z) → x · z · y)

  -- some convenient relabellings, so it looks more similar to the abelianization as a HIT.
  incAb : G' → Abelianization
  incAb = inc

  comm : (x y z : G') → incAb (x · (y · z)) ≡ incAb (x · (z · y))
  comm = λ x y z → coeq (x , y , z)

  {-
    Definition of the group structure on the abelianization. Here the generality of the
    coequalized functions is used.
  -}
  _·Ab_ : Abelianization → Abelianization → Abelianization
  _·Ab_ =
    rec2
      squash
      (λ x y → incAb (x · y))
      (λ (a , b , c) d →
        incAb ((a · (b · c)) · d) ≡⟨ cong (λ x → incAb (x · d)) (assoc _ _ _) ⟩
        incAb (((a · b) · c) · d) ≡⟨ cong incAb (sym (assoc (a · b) c d)) ⟩
        incAb ((a · b) · (c · d)) ≡⟨ comm (a · b) c d ⟩
        incAb ((a · b) · (d · c)) ≡⟨ cong incAb (sym (assoc _ _ _)) ⟩
        incAb (a · (b · (d · c))) ≡⟨ cong (λ x → incAb (a · x)) (assoc _ _ _) ⟩
        incAb (a · ((b · d) · c)) ≡⟨ comm a (b · d) c ⟩
        incAb (a · (c · (b · d))) ≡⟨ cong (λ x → incAb (a · x)) (assoc _ _ _) ⟩
        incAb (a · ((c · b) · d)) ≡⟨ cong incAb (assoc a (c · b) d) ⟩
        incAb ((a · (c · b)) · d) ∎)
      (λ (b , c , d) a →
        incAb (a · (b · (c · d))) ≡⟨ cong incAb (assoc _ _ _) ⟩
        incAb ((a · b) · (c · d)) ≡⟨ comm (a · b) c d ⟩
        incAb ((a · b) · (d · c)) ≡⟨ cong incAb (sym (assoc _ _ _)) ⟩
        incAb (a · (b · (d · c))) ∎)

  1Ab : Abelianization
  1Ab = incAb 1g

  invAb : Abelianization → Abelianization
  invAb =
    rec
      squash
      (λ x → incAb (inv x))
      (λ (a , b , c) →
        incAb (inv (a · (b · c)))              ≡⟨ cong incAb (invDistr a (b · c)) ⟩
        incAb (inv (b · c) · inv a)            ≡⟨ cong (λ x → incAb (x · inv a)) (invDistr b c) ⟩
        incAb ((inv c · inv b) · inv a)        ≡⟨ cong
                                                    incAb
                                                    (sym (lid ((inv c · inv b) · inv a))) ⟩
        incAb (1g · ((inv c · inv b) · inv a)) ≡⟨ comm 1g (inv c · inv b) (inv a) ⟩
        incAb (1g · (inv a · (inv c · inv b))) ≡⟨ cong incAb (lid (inv a · (inv c · inv b))) ⟩
        incAb (inv a · (inv c · inv b))        ≡⟨ comm (inv a) (inv c) (inv b) ⟩
        incAb (inv a · (inv b · inv c))        ≡⟨ cong
                                                    incAb
                                                    (sym (lid (inv a · (inv b · inv c)))) ⟩
        incAb (1g · (inv a · (inv b · inv c))) ≡⟨ comm 1g (inv a) (inv b · inv c) ⟩
        incAb (1g · ((inv b · inv c) · inv a)) ≡⟨ cong incAb (lid ((inv b · inv c) · inv a)) ⟩
        incAb ((inv b · inv c) · inv a)        ≡⟨ cong
                                                    (λ x → incAb (x · inv a))
                                                    (sym (invDistr c b)) ⟩
        incAb (inv (c · b) · inv a)            ≡⟨ cong incAb (sym (invDistr a (c · b))) ⟩
        incAb (inv (a · (c · b))) ∎)

  assocAb : (x y z : Abelianization) → x ·Ab (y ·Ab z) ≡ (x ·Ab y) ·Ab z
  assocAb =
    elimProp3
      (λ x y z → squash (x ·Ab (y ·Ab z))
      ((x ·Ab y) ·Ab z))
      (λ x y z → cong incAb (assoc x y z))

  ridAb : (x : Abelianization) → x ·Ab 1Ab ≡ x
  ridAb =
    elimProp
      (λ x → squash (x ·Ab 1Ab) x)
      (λ x → cong incAb (rid x))

  rinvAb : (x : Abelianization) → x ·Ab (invAb x) ≡ 1Ab
  rinvAb =
    elimProp
      (λ x → squash (x ·Ab (invAb x)) 1Ab)
      (λ x → (incAb x) ·Ab (invAb (incAb x)) ≡⟨ refl ⟩
             (incAb x) ·Ab (incAb (inv x))   ≡⟨ refl ⟩
             incAb (x · (inv x))             ≡⟨ cong incAb (fst (inverse x)) ⟩
             incAb 1g                        ≡⟨ refl ⟩
             1Ab ∎)

  commAb : (x y : Abelianization) → x ·Ab y ≡ y ·Ab x
  commAb =
    elimProp2
      (λ x y → squash (x ·Ab y) (y ·Ab x))
      (λ x y → (incAb x) ·Ab (incAb y) ≡⟨ refl ⟩
               incAb (x · y)           ≡⟨ cong incAb (sym (lid (x · y))) ⟩
               incAb (1g · (x · y))    ≡⟨ comm 1g x y ⟩
               incAb (1g · (y · x))    ≡⟨ cong incAb (lid (y · x)) ⟩
               incAb (y · x)           ≡⟨ refl ⟩
               (incAb y) ·Ab (incAb x) ∎)

  -- The proof that the abelianization is in fact an abelian group.
  asAbelianGroup : AbGroup ℓ
  asAbelianGroup = makeAbGroup 1Ab _·Ab_ invAb squash assocAb ridAb rinvAb commAb

  -- The proof that incAb can be seen as a group homomorphism
  incAbAsGroupHom : GroupHom G (AbGroup→Group asAbelianGroup)
  incAbAsGroupHom = f , fIsHom
    where
    f = λ x → incAb x
    fIsHom : IsGroupHom (snd G) f (snd (AbGroup→Group asAbelianGroup))
    IsGroupHom.pres· fIsHom = λ x y → refl
    IsGroupHom.pres1 fIsHom = refl
    IsGroupHom.presinv fIsHom = λ x → refl

AbelianizationAbGroup : (G : Group ℓ) → AbGroup ℓ
AbelianizationAbGroup G = AbelianizationAsCoeq.asAbelianGroup G

AbelianizationHom : (G : Group ℓ) → GroupHom G (AbGroup→Group (AbelianizationAbGroup G))
AbelianizationHom G = AbelianizationAsCoeq.incAbAsGroupHom G

module UniversalPropertyCoeq (G : Group ℓ) where
  open GroupStr {{...}}
  open GroupTheory G
  open AbelianizationAsCoeq G
  private
    instance
      _ = snd G
    G' = fst G
  abstract
    {- The proof of the universal property of the abelianization.

    G -incAb-> Abelianization
    \         .
      \       .
        f   ∃! inducedHom
          \   .
            \ .
              H
    commuting diagram
    -}
    inducedHom : (H : AbGroup ℓ)
              → (f : GroupHom G (AbGroup→Group H))
              → AbGroupHom asAbelianGroup H
    inducedHom H f = g , gIsHom
      where open IsGroupHom
            instance
              _ : GroupStr (fst H)
              _ = snd (AbGroup→Group H)
            f' : fst G → fst H
            f' = fst f
            g : Abelianization → fst H
            g = rec
                  (isSetAbGroup H)
                  (λ x → (f') x)
                  (λ (a , b , c) →
                    f' (a · b · c)           ≡⟨ (snd f).pres· a (b · c) ⟩
                    (f' a) · (f' (b · c))    ≡⟨ cong (λ x → (f' a) · x) ((snd f).pres· b c) ⟩
                    (f' a) · (f' b) · (f' c) ≡⟨ cong (λ x → (f' a) · x)
                                                     ((snd H).AbGroupStr.comm (f' b) (f' c)) ⟩
                    (f' a) · (f' c) · (f' b) ≡⟨ cong (λ x → (f' a) · x)
                                                     (sym ((snd f).pres· c b)) ⟩
                    (f' a) · (f' (c · b))    ≡⟨ sym ((snd f).pres· a (c · b)) ⟩
                    f' (a · c · b) ∎)
            gIsHom : IsGroupHom (snd (AbGroup→Group asAbelianGroup)) g (snd (AbGroup→Group H))
            pres· gIsHom =
              elimProp2
                (λ x y → isSetAbGroup H _ _)
                ((snd f).pres·)
            pres1 gIsHom = (snd f).pres1
            presinv gIsHom =
              elimProp
                (λ x → isSetAbGroup H _ _)
                ((snd f).presinv)

    commutativity : (H : AbGroup ℓ)
                  → (f : GroupHom G (AbGroup→Group H))
                  → (compGroupHom incAbAsGroupHom (inducedHom H f) ≡ f)
    commutativity H f =
        Σ≡Prop
          (λ _ → isPropIsGroupHom _ _)
          (λ i x → q x i)
      where q : (x : fst  G)
                → fst (compGroupHom incAbAsGroupHom (inducedHom H f)) x ≡ fst f x
            q = (λ x → refl)

    uniqueness : (H : AbGroup ℓ)
              → (f : GroupHom G (AbGroup→Group H))
              → (g : AbGroupHom asAbelianGroup H)
              → (p : compGroupHom incAbAsGroupHom g ≡ f)
              → (g ≡ inducedHom H f)
    uniqueness H f g p =
        Σ≡Prop
          (λ _ → isPropIsGroupHom _ _)
          (λ i x →  q x i)
      where q : (x : Abelianization)
                →  fst g x ≡ fst (inducedHom H f) x
            q = elimProp
                  (λ _ → isSetAbGroup H _ _)
                  (λ x → fst g (incAb x) ≡⟨ cong (λ f → f x) (cong fst p) ⟩
                        (fst f) x       ≡⟨ refl ⟩
                        fst (inducedHom H f) (incAb x)∎)

module IsoCoeqHIT (G : Group ℓ) where
  open GroupStr {{...}}
  open GroupTheory G
  open AbelianizationAsCoeq G
  open UniversalPropertyCoeq G
  private
    instance
      _ = snd G
    G' = fst G

  {- The proof that defining the abelianization of groups as a coequalizer of sets is equivalent
  to defining it as a HIT, more precisely that there is an isomorphism between the resulting
  abelian groups unique with respect to commuting with the constructors η and inc.

  G -incAb-> abelianization as coequalizer
   \         .
     \       .
       η   ∃! isomorphism
         \   .
           \ .
             abelianization as HIT
  commuting diagram
  -}
  isomorphism : AbGroupIso asAbelianGroup (HITasAbelianGroup G)
  isomorphism = h , hIsHomo
    where
    f = inducedHom (HITasAbelianGroup G) (HITηAsGroupHom G)
    g = HITinducedHom G asAbelianGroup incAbAsGroupHom

    HITgfcomm : compGroupHom (HITηAsGroupHom G) (compGroupHom g f) ≡ (HITηAsGroupHom G)
    HITgfcomm =
      compGroupHom (HITηAsGroupHom G) (compGroupHom g f)
        ≡⟨ sym (compGroupHomAssoc (HITηAsGroupHom G) g f) ⟩
      compGroupHom (compGroupHom (HITηAsGroupHom G) g) f
        ≡⟨ cong (λ x → compGroupHom x f) (HITcommutativity G asAbelianGroup incAbAsGroupHom) ⟩
      compGroupHom incAbAsGroupHom f
        ≡⟨ commutativity (HITasAbelianGroup G) (HITηAsGroupHom G) ⟩
      (HITηAsGroupHom G) ∎

    HITidcomm : compGroupHom (HITηAsGroupHom G) idGroupHom ≡ HITηAsGroupHom G
    HITidcomm = compGroupHomId (HITηAsGroupHom G)

    HITidIsInduced : HITinducedHom G (HITasAbelianGroup G) (HITηAsGroupHom G) ≡ idGroupHom
    HITidIsInduced = sym (HITuniqueness G
                           (HITasAbelianGroup G)
                           (HITηAsGroupHom G)
                           idGroupHom
                           HITidcomm)

    HITgfIsInduced : HITinducedHom G (HITasAbelianGroup G) (HITηAsGroupHom G) ≡ compGroupHom g f
    HITgfIsInduced = sym (HITuniqueness G
                           (HITasAbelianGroup G)
                           (HITηAsGroupHom G)
                           (compGroupHom g f)
                           HITgfcomm)

    i : idGroupHom ≡ compGroupHom g f
    i = idGroupHom ≡⟨ sym HITidIsInduced ⟩
        HITinducedHom G (HITasAbelianGroup G) (HITηAsGroupHom G) ≡⟨ HITgfIsInduced ⟩
        compGroupHom g f ∎

    fgcomm : compGroupHom incAbAsGroupHom (compGroupHom f g) ≡ incAbAsGroupHom
    fgcomm =
      compGroupHom incAbAsGroupHom (compGroupHom f g)
        ≡⟨ sym (compGroupHomAssoc incAbAsGroupHom f g) ⟩
      compGroupHom (compGroupHom incAbAsGroupHom f) g
        ≡⟨ cong (λ x → compGroupHom x g) (commutativity (HITasAbelianGroup G) (HITηAsGroupHom G)) ⟩
      compGroupHom (HITηAsGroupHom G) g
        ≡⟨ (HITcommutativity G) asAbelianGroup incAbAsGroupHom ⟩
      incAbAsGroupHom ∎

    idcomm : compGroupHom incAbAsGroupHom idGroupHom ≡ incAbAsGroupHom
    idcomm = compGroupHomId incAbAsGroupHom

    idIsInduced : inducedHom asAbelianGroup incAbAsGroupHom ≡ idGroupHom
    idIsInduced = sym (uniqueness asAbelianGroup incAbAsGroupHom idGroupHom idcomm)

    fgIsInduced : inducedHom asAbelianGroup incAbAsGroupHom ≡ compGroupHom f g
    fgIsInduced = sym (uniqueness asAbelianGroup incAbAsGroupHom (compGroupHom f g) fgcomm)

    j : idGroupHom ≡ compGroupHom f g
    j = idGroupHom ≡⟨ sym idIsInduced ⟩
        inducedHom asAbelianGroup incAbAsGroupHom ≡⟨ fgIsInduced ⟩
        compGroupHom f g ∎

    h = iso
          (fst f)
          (fst g)
          (λ b → cong (λ e → e b) (cong fst (sym i)))
          (λ a → cong (λ e → e a) (cong fst (sym j)))
    hIsHomo = snd f

  isomorphismCommutativity : compGroupHom incAbAsGroupHom (GroupIso→GroupHom isomorphism)
                               ≡ (HITηAsGroupHom G)
  isomorphismCommutativity = commutativity (HITasAbelianGroup G) (HITηAsGroupHom G)

  isomorphismUniqueness : (h : AbGroupIso asAbelianGroup (HITasAbelianGroup G))
    → (hcomm : compGroupHom incAbAsGroupHom (GroupIso→GroupHom h) ≡ (HITηAsGroupHom G))
    → h ≡ isomorphism
  isomorphismUniqueness h hcomm = GroupIso≡ p
    where
      r : (x : fst asAbelianGroup)
        → fst (compGroupHom (GroupIso→GroupHom h) (GroupIso→GroupHom (invGroupIso h))) x ≡ x
      r = λ x → (h .fst .Iso.leftInv) x

      leftInvGroupHom : (compGroupHom
                          (GroupIso→GroupHom h)
                          (GroupIso→GroupHom (invGroupIso h))) ≡ idGroupHom
      leftInvGroupHom =
        Σ≡Prop
          (λ _ → isPropIsGroupHom _ _)
          (λ i x → r x i)

      q : (g : fst  G)
        → fst (compGroupHom
                (HITηAsGroupHom G)
                (GroupIso→GroupHom (invGroupIso h))) g ≡ fst incAbAsGroupHom g
      q = λ g
        → fst (compGroupHom (HITηAsGroupHom G) (GroupIso→GroupHom (invGroupIso h))) g
            ≡⟨ cong
                 (λ f → f g)
                 (cong fst (cong
                             (λ f → compGroupHom f (GroupIso→GroupHom (invGroupIso h)))
                             (sym hcomm))) ⟩
          fst (compGroupHom
                (compGroupHom incAbAsGroupHom (GroupIso→GroupHom h))
                (GroupIso→GroupHom (invGroupIso h))) g
            ≡⟨ cong
                 (λ f → f g)
                 (cong fst (compGroupHomAssoc
                              incAbAsGroupHom
                              (GroupIso→GroupHom h)
                              (GroupIso→GroupHom (invGroupIso h)))) ⟩
          fst (compGroupHom
                incAbAsGroupHom
                (compGroupHom (GroupIso→GroupHom h) (GroupIso→GroupHom (invGroupIso h)))) g
            ≡⟨ cong
                (λ f → f g)
                (cong fst (cong (λ f → (compGroupHom incAbAsGroupHom f)) leftInvGroupHom)) ⟩
          fst incAbAsGroupHom g ∎

      isocomm : compGroupHom
                  (HITηAsGroupHom G)
                  (GroupIso→GroupHom (invGroupIso h)) ≡ incAbAsGroupHom
      isocomm =
        Σ≡Prop
          (λ _ → isPropIsGroupHom _ _)
          (λ i x → q x i)

      p : h .fst ≡ isomorphism .fst
      p =
        Iso≡Set
          (isSetAbGroup asAbelianGroup)
          (isSetAbGroup (HITasAbelianGroup G))
          (h .fst)
          (isomorphism .fst)
          (λ x → cong
                   (λ f → f x)
                   (cong fst (uniqueness
                               (HITasAbelianGroup G)
                               (HITηAsGroupHom G)
                               (GroupIso→GroupHom h)
                               hcomm)))
          (λ x → cong
                   (λ f → f x)
                   (cong fst (HITuniqueness G
                                asAbelianGroup
                                incAbAsGroupHom
                                (GroupIso→GroupHom (invGroupIso h))
                                isocomm)))

AbelianizationComparisonIsomorphism : (G : Group ℓ)
                                    → AbGroupIso (AbelianizationAbGroup G) (HITasAbelianGroup G)
AbelianizationComparisonIsomorphism G = IsoCoeqHIT.isomorphism G


module Delta (K : Set) \{\{bij : bij K Nat\}\} where

DL : (V : Set) \altRArr Set
DL V = List (Nat \altAnd V)

data DD (V : Set) : Set where
  DD : DL V \altRArr DD V

delta
  : \altFAll\{n m\} \altRArr n < m \altRArr Nat
delta n<m =
  difference (n<m\altRArr1+n\ensuremath{\leq}m n<m) -- i.e. m - n - 1

-- lookup
_\altLAng_\altRAng
  : \altFAll\{V\} \altRArr DD V \altRArr K \altRArr Maybe V
(DD dd) \altLAng k \altRAng =
  lkup dd (toNat k)
  where
    lkup : \altFAll\{V\} \altRArr DL V \altRArr Nat \altRArr Maybe V
    lkup [] n = None
    lkup ((hn , ha) :: t) n
      with <dec n hn
\fourChars{}... | Inl n<hn       = None
\fourChars{}... | Inr (Inl refl) = Some ha
\fourChars{}... | Inr (Inr hn<n) = lkup t (delta hn<n)

-- insert/extend
_,,_
  : \altFAll\{V\} \altRArr DD V \altRArr (K \altAnd V) \altRArr DD V
(DD dd) ,, (k , v) =
  DD (ins dd (toNat k , v))
  where
    ins : \altFAll\{V\} \altRArr DL V \altRArr (Nat \altAnd V) \altRArr DL V
    ins [] (n , v) = (n , v) :: []
    ins ((hn , hv) :: t) (n , v)
      with <dec n hn
\fourChars{}... | Inl n<hn =
          (n , v) :: (delta n<hn , hv) :: t
\fourChars{}... | Inr (Inl refl) =
          (n , v) :: t
\fourChars{}... | Inr (Inr hn<n) =
          (hn , hv) :: ins t (delta hn<n , v)

destruct
  : \altFAll\{V\} \altRArr DD V \altRArr Maybe ((K \altAnd V) \altAnd DD V)
destruct (DD []) = None
destruct (DD ((n , v) :: [])) =
  Some ((fromNat n , v) , DD [])
destruct (DD ((n , v) :: (m , v') :: t)) =
  Some ((fromNat n , v) , DD ((n + m + 1 , v') :: t))


-- {-# OPTIONS -v term:30 #-}
-- 2010-10-14
module ProjectionsPreserveGuardednessTrivialExample where

open import Common.Level
open import Common.Coinduction
open import Common.Product

-- Streams

infixr 5 _∷_

data Stream (A : Set) : Set where
  _∷_ : (x : A) (xs : ∞ (Stream A)) → Stream A

mutual

  repeat : {A : Set}(a : A) → Stream A
  repeat a = a ∷ proj₂ (repeat' a)

  repeat' : {A : Set}(a : A) → A × ∞ (Stream A)
  repeat' a = a , ♯ repeat a


------------------------------------------------------------------------------
-- Arithmetic properties
------------------------------------------------------------------------------

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

module FOTC.Data.Nat.PropertiesATP where

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

------------------------------------------------------------------------------
-- Totality properties

pred-N : ∀ {n} → N n → N (pred₁ n)
pred-N nzero = prf
  where postulate prf : N (pred₁ zero)
        {-# ATP prove prf #-}

pred-N (nsucc {n} Nn) = prf
  where postulate prf : N (pred₁ (succ₁ n))
        {-# ATP prove prf #-}

+-N : ∀ {m n} → N m → N n → N (m + n)
+-N {n = n} nzero Nn = prf
  where postulate prf : N (zero + n)
        {-# ATP prove prf #-}
+-N {n = n} (nsucc {m} Nm) Nn = prf (+-N Nm Nn)
  where postulate prf : N (m + n) → N (succ₁ m + n)
        {-# ATP prove prf #-}

∸-N : ∀ {m n} → N m → N n → N (m ∸ n)
∸-N {m} Nm nzero = prf
  where postulate prf : N (m ∸ zero)
        {-# ATP prove prf #-}
∸-N {m} Nm (nsucc {n} Nn) = prf (∸-N Nm Nn)
  where postulate prf : N (m ∸ n) → N (m ∸ succ₁ n)
        {-# ATP prove prf pred-N #-}

*-N : ∀ {m n} → N m → N n → N (m * n)
*-N {n = n} nzero Nn = prf
  where postulate prf : N (zero * n)
        {-# ATP prove prf #-}
*-N {n = n} (nsucc {m} Nm) Nn = prf (*-N Nm Nn)
  where postulate prf : N (m * n) → N (succ₁ m * n)
        {-# ATP prove prf +-N #-}

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

Sx≢x : ∀ {n} → N n → succ₁ n ≢ n
Sx≢x nzero h = prf
  where postulate prf : ⊥
        {-# ATP prove prf #-}
Sx≢x (nsucc {n} Nn) h = prf (Sx≢x Nn)
  where postulate prf : succ₁ n ≢ n → ⊥
        {-# ATP prove prf #-}

+-leftIdentity : ∀ n → zero + n ≡ n
+-leftIdentity n = +-0x n

+-rightIdentity : ∀ {n} → N n → n + zero ≡ n
+-rightIdentity nzero          = +-leftIdentity zero
+-rightIdentity (nsucc {n} Nn) = prf (+-rightIdentity Nn)
   where postulate prf : n + zero ≡ n → succ₁ n + zero ≡ succ₁ n
         {-# ATP prove prf #-}

+-assoc : ∀ {m} → N m → ∀ n o → m + n + o ≡ m + (n + o)
+-assoc nzero n o = prf
  where postulate prf : zero + n + o ≡ zero + (n + o)
        {-# ATP prove prf #-}
+-assoc (nsucc {m} Nm) n o = prf (+-assoc Nm n o)
  where postulate prf : m + n + o ≡ m + (n + o) →
                        succ₁ m + n + o ≡ succ₁ m + (n + o)
        {-# ATP prove prf #-}

x+Sy≡S[x+y] : ∀ {m} → N m → ∀ n → m + succ₁ n ≡ succ₁ (m + n)
x+Sy≡S[x+y] nzero n = prf
  where postulate prf : zero + succ₁ n ≡ succ₁ (zero + n)
        {-# ATP prove prf #-}
x+Sy≡S[x+y] (nsucc {m} Nm) n = prf (x+Sy≡S[x+y] Nm n)
  where postulate prf : m + succ₁ n ≡ succ₁ (m + n) →
                        succ₁ m + succ₁ n ≡ succ₁ (succ₁ m + n)
        {-# ATP prove prf #-}

+-comm : ∀ {m n} → N m → N n → m + n ≡ n + m
+-comm {n = n} nzero Nn = prf
  where postulate prf : zero + n ≡ n + zero
        {-# ATP prove prf +-rightIdentity #-}
+-comm {n = n} (nsucc {m} Nm) Nn = prf (+-comm Nm Nn)
  where postulate prf : m + n ≡ n + m → succ₁ m + n ≡ n + succ₁ m
        {-# ATP prove prf x+Sy≡S[x+y] #-}

x+S0≡Sx : ∀ {n} → N n → n + succ₁ zero ≡ succ₁ n
x+S0≡Sx nzero          = +-leftIdentity (succ₁ zero)
x+S0≡Sx (nsucc {n} Nn) = prf (x+S0≡Sx Nn)
  where postulate prf : n + succ₁ zero ≡ succ₁ n →
                        succ₁ n + succ₁ zero ≡ succ₁ (succ₁ n)
        {-# ATP prove prf #-}

0∸x : ∀ {n} → N n → zero ∸ n ≡ zero
0∸x nzero          = ∸-x0 zero
0∸x (nsucc {n} Nn) = prf (0∸x Nn)
  where postulate prf : zero ∸ n ≡ zero → zero ∸ succ₁ n ≡ zero
        {-# ATP prove prf #-}

S∸S : ∀ {m n} → N m → N n → succ₁ m ∸ succ₁ n ≡ m ∸ n
S∸S {m} Nm nzero = prf
  where postulate prf : succ₁ m ∸ succ₁ zero ≡ m ∸ zero
        {-# ATP prove prf #-}

S∸S {m} Nm (nsucc {n} Nn) = prf (S∸S Nm Nn)
  where postulate prf : succ₁ m ∸ succ₁ n ≡ m ∸ n →
                        succ₁ m ∸ succ₁ (succ₁ n) ≡ m ∸ succ₁ n
        {-# ATP prove prf #-}

x∸x≡0 : ∀ {n} → N n → n ∸ n ≡ zero
x∸x≡0 nzero          = ∸-x0 zero
x∸x≡0 (nsucc {n} Nn) = prf (x∸x≡0 Nn)
  where postulate prf : n ∸ n ≡ zero → succ₁ n ∸ succ₁ n ≡ zero
        {-# ATP prove prf S∸S #-}

Sx∸x≡S0 : ∀ {n} → N n → succ₁ n ∸ n ≡ succ₁ zero
Sx∸x≡S0 nzero =  prf
  where postulate prf : succ₁ zero ∸ zero ≡ succ₁ zero
        {-# ATP prove prf #-}
Sx∸x≡S0 (nsucc {n} Nn) = prf (Sx∸x≡S0 Nn)
  where postulate prf : succ₁ n ∸ n ≡ succ₁ zero →
                        succ₁ (succ₁ n) ∸ succ₁ n ≡ succ₁ zero
        {-# ATP prove prf S∸S #-}

[x+Sy]∸y≡Sx : ∀ {m n} → N m → N n → (m + succ₁ n) ∸ n ≡ succ₁ m
[x+Sy]∸y≡Sx {n = n} nzero Nn = prf
  where postulate prf : zero + succ₁ n ∸ n ≡ succ₁ zero
        {-# ATP prove prf Sx∸x≡S0 #-}
[x+Sy]∸y≡Sx (nsucc {m} Nm) nzero = prf
  where postulate prf : succ₁ m + succ₁ zero ∸ zero ≡ succ₁ (succ₁ m)
        {-# ATP prove prf x+S0≡Sx #-}

[x+Sy]∸y≡Sx (nsucc {m} Nm) (nsucc {n} Nn) = prf ([x+Sy]∸y≡Sx (nsucc Nm) Nn)
  where postulate prf : succ₁ m + succ₁ n ∸ n ≡ succ₁ (succ₁ m) →
                        succ₁ m + succ₁ (succ₁ n) ∸ succ₁ n ≡ succ₁ (succ₁ m)
        {-# ATP prove prf S∸S +-N x+Sy≡S[x+y] #-}

[x+y]∸[x+z]≡y∸z : ∀ {m n o} → N m → N n → N o → (m + n) ∸ (m + o) ≡ n ∸ o
[x+y]∸[x+z]≡y∸z {n = n} {o} nzero Nn No = prf
  where postulate prf : (zero + n) ∸ (zero + o) ≡ n ∸ o
        {-# ATP prove prf #-}

[x+y]∸[x+z]≡y∸z {n = n} {o} (nsucc {m} Nm) Nn No = prf ([x+y]∸[x+z]≡y∸z Nm Nn No)
  where postulate prf : (m + n) ∸ (m + o) ≡ n ∸ o →
                        (succ₁ m + n) ∸ (succ₁ m + o) ≡ n ∸ o
        {-# ATP prove prf +-N S∸S #-}

*-leftZero : ∀ n → zero * n ≡ zero
*-leftZero = *-0x

*-rightZero : ∀ {n} → N n → n * zero ≡ zero
*-rightZero nzero          = *-leftZero zero
*-rightZero (nsucc {n} Nn) = prf (*-rightZero Nn)
  where postulate prf : n * zero ≡ zero → succ₁ n * zero ≡ zero
        {-# ATP prove prf #-}

postulate *-leftIdentity : ∀ {n} → N n → succ₁ zero * n ≡ n
{-# ATP prove *-leftIdentity +-rightIdentity #-}

x*Sy≡x+xy : ∀ {m n} → N m → N n → m * succ₁ n ≡ m + m * n
x*Sy≡x+xy {n = n} nzero Nn = prf
  where postulate prf : zero * succ₁ n ≡ zero + zero * n
        {-# ATP prove prf #-}
x*Sy≡x+xy {n = n} (nsucc {m} Nm) Nn = prf (x*Sy≡x+xy Nm Nn)
                                       (+-assoc Nn m (m * n))
                                       (+-assoc Nm n (m * n))
  where
  -- N.B. We had to feed the ATP with the instances of the associate law
  postulate prf :  m * succ₁ n ≡ m + m * n →
                   (n + m) + (m * n) ≡ n + (m + (m * n)) →  -- Associative law
                   (m + n) + (m * n) ≡ m + (n + (m * n)) →  -- Associateve law
                   succ₁ m * succ₁ n ≡ succ₁ m + succ₁ m * n
  {-# ATP prove prf +-comm #-}

*-comm : ∀ {m n} → N m → N n → m * n ≡ n * m
*-comm {n = n} nzero Nn = prf
  where postulate prf : zero * n ≡ n * zero
        {-# ATP prove prf *-rightZero #-}
*-comm {n = n} (nsucc {m} Nm) Nn = prf (*-comm Nm Nn)
  where postulate prf : m * n ≡ n * m → succ₁ m * n ≡ n * succ₁ m
        {-# ATP prove prf x*Sy≡x+xy #-}

*-rightIdentity : ∀ {n} → N n → n * succ₁ zero ≡ n
*-rightIdentity nzero = prf
  where postulate prf : zero * succ₁ zero ≡ zero
        {-# ATP prove prf #-}
*-rightIdentity (nsucc {n} Nn) = prf (*-rightIdentity Nn)
  where postulate prf : n * succ₁ zero ≡ n →
                        succ₁ n * succ₁ zero ≡ succ₁ n
        {-# ATP prove prf #-}

*∸-leftDistributive : ∀ {m n o} → N m → N n → N o → (m ∸ n) * o ≡ m * o ∸ n * o
*∸-leftDistributive {m} {o = o} Nm nzero No = prf
  where postulate prf : (m ∸ zero) * o ≡ m * o ∸ zero * o
        {-# ATP prove prf #-}

*∸-leftDistributive {o = o} nzero (nsucc {n} Nn) No = prf
  where postulate prf : (zero ∸ succ₁ n) * o ≡ zero * o ∸ succ₁ n * o
        {-# ATP prove prf 0∸x *-N #-}

*∸-leftDistributive (nsucc {m} Nm) (nsucc {n} Nn) nzero = prf
  where
  postulate prf : (succ₁ m ∸ succ₁ n) * zero ≡ succ₁ m * zero ∸ succ₁ n * zero
  {-# ATP prove prf ∸-N *-comm #-}

*∸-leftDistributive (nsucc {m} Nm) (nsucc {n} Nn) (nsucc {o} No) =
  prf (*∸-leftDistributive Nm Nn (nsucc No))
  where
  postulate prf : (m ∸ n) * succ₁ o ≡ m * succ₁ o ∸ n * succ₁ o →
                  (succ₁ m ∸ succ₁ n) * succ₁ o ≡
                  succ₁ m * succ₁ o ∸ succ₁ n * succ₁ o
  {-# ATP prove prf [x+y]∸[x+z]≡y∸z S∸S *-N #-}

*+-leftDistributive : ∀ {m n o} → N m → N n → N o → (m + n) * o ≡ m * o + n * o
*+-leftDistributive {m} {n} Nm Nn nzero = prf
  where postulate prf : (m + n) * zero ≡ m * zero + n * zero
        {-# ATP prove prf *-comm +-rightIdentity *-N +-N #-}

*+-leftDistributive {n = n} nzero Nn (nsucc {o} No) = prf
  where postulate prf :  (zero + n) * succ₁ o ≡ zero * succ₁ o + n * succ₁ o
        {-# ATP prove prf #-}

*+-leftDistributive (nsucc {m} Nm) nzero (nsucc {o} No) = prf
  where
  postulate prf : (succ₁ m + zero) * succ₁ o ≡ succ₁ m * succ₁ o + zero * succ₁ o
  {-# ATP prove prf +-rightIdentity *-N #-}

*+-leftDistributive (nsucc {m} Nm) (nsucc {n} Nn) (nsucc {o} No) =
  prf (*+-leftDistributive Nm (nsucc Nn) (nsucc No))
  where
  postulate
    prf : (m + succ₁ n) * succ₁ o ≡ m * succ₁ o + succ₁ n * succ₁ o →
          (succ₁ m + succ₁ n) * succ₁ o ≡ succ₁ m * succ₁ o + succ₁ n * succ₁ o
  {-# ATP prove prf +-assoc *-N #-}

xy≡0→x≡0∨y≡0 : ∀ {m n} → N m → N n → m * n ≡ zero → m ≡ zero ∨ n ≡ zero
xy≡0→x≡0∨y≡0 {n = n} nzero Nn h = prf
  where postulate prf : zero ≡ zero ∨ n ≡ zero
        {-# ATP prove prf #-}
xy≡0→x≡0∨y≡0 (nsucc {m} Nm) nzero h = prf
  where postulate prf : succ₁ m ≡ zero ∨ zero ≡ zero
        {-# ATP prove prf #-}
xy≡0→x≡0∨y≡0 (nsucc {m} Nm) (nsucc {n} Nn) SmSn≡0 = ⊥-elim (0≢S prf)
  where postulate prf : zero ≡ succ₁ (n + m * succ₁ n)
        {-# ATP prove prf #-}

xy≡1→x≡1 : ∀ {m n} → N m → N n → m * n ≡ 1' → m ≡ 1'
xy≡1→x≡1 {n = n} nzero Nn h = ⊥-elim prf
  where postulate prf : ⊥
        {-# ATP prove prf #-}
xy≡1→x≡1 (nsucc nzero) Nn h = refl
xy≡1→x≡1 (nsucc (nsucc {m} Nm)) nzero h = ⊥-elim prf
  where postulate prf : ⊥
        {-# ATP prove prf *-rightZero #-}
xy≡1→x≡1 (nsucc (nsucc {m} Nm)) (nsucc {n} Nn) h = ⊥-elim (0≢S prf)
  where postulate prf : zero ≡ succ₁ (m + n * succ₁ (succ₁ m))
        {-# ATP prove prf *-comm #-}


{-# OPTIONS --verbose=10 #-}

module leafs where

  open import Data.Nat
  open import Data.Vec
  open import Agda.Builtin.Sigma
  open import Data.Product
  open import Data.Fin using (fromℕ)

  open import trees
  open import optics
  open import lemmas

  leafsTree : {A : Set} -> Tree A -> ∃[ n ] (Vec A n × (Vec A n -> Tree A))
  leafsTree empty = 0 , ([] , λ _ -> empty)
  leafsTree (node empty root empty) =
    (1 , (root ∷ [] , λ v -> node empty (head v) empty))
  leafsTree {A} (node left root right) with leafsTree left | leafsTree right
  ...                                     | (n1 , (g1 , p1)) | (n2 , (g2 , p2)) =
     (n1 + n2 , (g1 ++ g2 , λ v -> node (p1 (take n1 v)) root (p2 (drop n1 v))))

  leafs : {A : Set} -> Traversal (Tree A) (Tree A) A A
  leafs = record{ extract = leafsTree }

  module tests where
    tree1 : Tree ℕ
    tree1 = node (node empty 1 empty) 3 empty

    open Traversal

    leafs1 : Vec ℕ 1
    leafs1 = get leafs tree1

    updatedleafs1 : Tree ℕ
    updatedleafs1 = put leafs (node tree1 5 tree1) (2 ∷ 5 ∷ [])


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

module 16-sets where

import 15-groups
open 15-groups public

{- Equivalence relations -}

Rel-Prop :
  (l : Level) {l1 : Level} (A : UU l1) → UU ((lsuc l) ⊔ l1)
Rel-Prop l A = A → (A → UU-Prop l)

type-Rel-Prop :
  {l1 l2 : Level} {A : UU l1} (R : Rel-Prop l2 A) → A → A → UU l2
type-Rel-Prop R x y = pr1 (R x y)

is-prop-type-Rel-Prop :
  {l1 l2 : Level} {A : UU l1} (R : Rel-Prop l2 A) →
  (x y : A) → is-prop (type-Rel-Prop R x y)
is-prop-type-Rel-Prop R x y = pr2 (R x y)

is-reflexive-Rel-Prop :
  {l1 l2 : Level} {A : UU l1} → Rel-Prop l2 A → UU (l1 ⊔ l2)
is-reflexive-Rel-Prop {A = A} R =
  (x : A) → type-Rel-Prop R x x

is-symmetric-Rel-Prop :
  {l1 l2 : Level} {A : UU l1} → Rel-Prop l2 A → UU (l1 ⊔ l2)
is-symmetric-Rel-Prop {A = A} R =
  (x y : A) → type-Rel-Prop R x y → type-Rel-Prop R y x

is-transitive-Rel-Prop :
  {l1 l2 : Level} {A : UU l1} → Rel-Prop l2 A → UU (l1 ⊔ l2)
is-transitive-Rel-Prop {A = A} R =
  (x y z : A) → type-Rel-Prop R x y → type-Rel-Prop R y z → type-Rel-Prop R x z

is-equivalence-relation :
  {l1 l2 : Level} {A : UU l1} (R : Rel-Prop l2 A) → UU (l1 ⊔ l2)
is-equivalence-relation R =
  is-reflexive-Rel-Prop R ×
    ( is-symmetric-Rel-Prop R × is-transitive-Rel-Prop R)

Eq-Rel :
  (l : Level) {l1 : Level} (A : UU l1) → UU ((lsuc l) ⊔ l1)
Eq-Rel l A = Σ (Rel-Prop l A) is-equivalence-relation

rel-prop-Eq-Rel :
  {l1 l2 : Level} {A : UU l1} → Eq-Rel l2 A → Rel-Prop l2 A
rel-prop-Eq-Rel = pr1

type-Eq-Rel :
  {l1 l2 : Level} {A : UU l1} → Eq-Rel l2 A → A → A → UU l2
type-Eq-Rel R = type-Rel-Prop (rel-prop-Eq-Rel R)

is-equivalence-relation-rel-prop-Eq-Rel :
  {l1 l2 : Level} {A : UU l1} (R : Eq-Rel l2 A) →
  is-equivalence-relation (rel-prop-Eq-Rel R)
is-equivalence-relation-rel-prop-Eq-Rel R = pr2 R

refl-Eq-Rel :
  {l1 l2 : Level} {A : UU l1} (R : Eq-Rel l2 A) →
  is-reflexive-Rel-Prop (rel-prop-Eq-Rel R)
refl-Eq-Rel R = pr1 (is-equivalence-relation-rel-prop-Eq-Rel R)

symm-Eq-Rel :
  {l1 l2 : Level} {A : UU l1} (R : Eq-Rel l2 A) →
  is-symmetric-Rel-Prop (rel-prop-Eq-Rel R)
symm-Eq-Rel R = pr1 (pr2 (is-equivalence-relation-rel-prop-Eq-Rel R))

trans-Eq-Rel :
  {l1 l2 : Level} {A : UU l1} (R : Eq-Rel l2 A) →
  is-transitive-Rel-Prop (rel-prop-Eq-Rel R)
trans-Eq-Rel R = pr2 (pr2 (is-equivalence-relation-rel-prop-Eq-Rel R))

{- Section 15.2 The universal property of set quotients -}

identifies-Eq-Rel :
  {l1 l2 l3 : Level} {A : UU l1} (R : Eq-Rel l2 A) →
  {B : UU l3} → (A → B) → UU (l1 ⊔ (l2 ⊔ l3))
identifies-Eq-Rel {A = A} R f =
  (x y : A) → type-Eq-Rel R x y → Id (f x) (f y)

precomp-map-universal-property-Eq-Rel :
  {l l1 l2 l3 : Level} {A : UU l1} (R : Eq-Rel l2 A)
  {B : UU-Set l3} (f : A → type-Set B) (H : identifies-Eq-Rel R f) →
  (X : UU-Set l) → (type-hom-Set B X) → Σ (A → type-Set X) (identifies-Eq-Rel R)
precomp-map-universal-property-Eq-Rel R f H X g =
  pair (g ∘ f) (λ x y r → ap g (H x y r))

universal-property-set-quotient :
  (l : Level) {l1 l2 l3 : Level} {A : UU l1} (R : Eq-Rel l2 A)
  {B : UU-Set l3} (f : A → type-Set B) (H : identifies-Eq-Rel R f) → UU _
universal-property-set-quotient l R {B} f H =
  (X : UU-Set l) → is-equiv (precomp-map-universal-property-Eq-Rel R {B} f H X)

{- Effective quotients -}

is-effective-Set-Quotient :
  {l1 l2 l3 : Level} {A : UU l1} (R : Eq-Rel l2 A) (B : UU-Set l3)
  (f : A → type-Set B) (H : identifies-Eq-Rel R f) → UU (l1 ⊔ l2 ⊔ l3)
is-effective-Set-Quotient {A = A} R B f H = (x y : A) → is-equiv (H x y)

-- Section 13.4

is-small :
  (l : Level) {l1 : Level} (A : UU l1) → UU (lsuc l ⊔ l1)
is-small l A = Σ (UU l) (λ X → A ≃ X)

is-small-map :
  (l : Level) {l1 l2 : Level} {A : UU l1} {B : UU l2} →
  (A → B) → UU (lsuc l ⊔ (l1 ⊔ l2))
is-small-map l {B = B} f = (b : B) → is-small l (fib f b)

is-locally-small :
  (l : Level) {l1 : Level} (A : UU l1) → UU (lsuc l ⊔ l1)
is-locally-small l A = (x y : A) → is-small l (Id x y)

total-subtype :
  {l1 l2 : Level} {A : UU l1} (P : A → UU-Prop l2) → UU (l1 ⊔ l2)
total-subtype {A = A} P = Σ A (λ x → pr1 (P x))

equiv-subtype-equiv :
  {l1 l2 l3 l4 : Level}
  {A : UU l1} {B : UU l2} (e : A ≃ B)
  (C : A → UU-Prop l3) (D : B → UU-Prop l4) →
  ((x : A) → (C x) ↔ (D (map-equiv e x))) →
  total-subtype C ≃ total-subtype D
equiv-subtype-equiv e C D H =
  equiv-toto (λ y → type-Prop (D y)) e
    ( λ x → equiv-iff (C x) (D (map-equiv e x)) (H x))

equiv-comp-equiv' :
  {l1 l2 l3 : Level} {A : UU l1} {B : UU l2} →
  (A ≃ B) → (C : UU l3) → (B ≃ C) ≃ (A ≃ C)
equiv-comp-equiv' e C =
  equiv-subtype-equiv
    ( equiv-precomp-equiv e C)
    ( is-equiv-Prop)
    ( is-equiv-Prop)
    ( λ g →
      pair
        ( is-equiv-comp' g (map-equiv e) (is-equiv-map-equiv e))
        ( λ is-equiv-eg →
          is-equiv-left-factor'
            g (map-equiv e) is-equiv-eg (is-equiv-map-equiv e)))

is-prop-is-small :
  (l : Level) {l1 : Level} (A : UU l1) → is-prop (is-small l A)
is-prop-is-small l A =
  is-prop-is-contr-if-inh
    ( λ Xe →
      is-contr-equiv'
        ( Σ (UU l) (λ Y → (pr1 Xe) ≃ Y))
        ( equiv-tot ((λ Y → equiv-comp-equiv' (pr2 Xe) Y)))
        ( is-contr-total-equiv (pr1 Xe)))

is-prop-is-locally-small :
  (l : Level) {l1 : Level} (A : UU l1) → is-prop (is-locally-small l A)
is-prop-is-locally-small l A =
  is-prop-Π (λ x → is-prop-Π (λ y → is-prop-is-small l (Id x y)))