{-

This file contains:

- Definition of functions of the equivalence between FreeGroup and the FundamentalGroup
- Definition of encode decode functions
- Proof that for all x : Bouquet A → p : base ≡ x → decode x (encode x p) ≡ p (no truncations)
- Proof of the truncated versions of encodeDecode and of right-homotopy
- Definition of truncated encode decode functions
- Proof of the truncated versions of decodeEncode and encodeDecode
- Proof that π₁Bouquet ≡ FreeGroup A

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

module Cubical.HITs.Bouquet.FundamentalGroupProof where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.GroupoidLaws renaming (assoc to pathAssoc)
open import Cubical.HITs.SetTruncation hiding (rec2)
open import Cubical.HITs.PropositionalTruncation hiding (map ; elim) renaming (rec to propRec)
open import Cubical.Algebra.Group
open import Cubical.Homotopy.Group.Base
open import Cubical.Homotopy.Loopspace

open import Cubical.HITs.Bouquet.Base
open import Cubical.HITs.FreeGroup.Base
open import Cubical.HITs.FreeGroupoid

private
  variable
     : Level
    A : Type 

-- Pointed versions of the non truncated types

ΩBouquet : {A : Type }  Pointed 
ΩBouquet {A = A} = Ω (Bouquet∙ A)

FreeGroupoid∙ : {A : Type }  Pointed 
FreeGroupoid∙ {A = A} = FreeGroupoid A , ε

-- Functions without using the truncated forms of types

looping : FreeGroupoid A  typ ΩBouquet
looping (η a)              = loop a
looping (g1 · g2)          = looping g1  looping g2
looping ε                  = refl
looping (inv g)            = sym (looping g)
looping (assoc g1 g2 g3 i) = pathAssoc (looping g1) (looping g2) (looping g3) i
looping (idr g i)          = rUnit (looping g) i
looping (idl g i)          = lUnit (looping g) i
looping (invr g i)         = rCancel (looping g) i
looping (invl g i)         = lCancel (looping g) i

looping∙ : FreeGroupoid∙ →∙ ΩBouquet {A = A}
looping∙ = looping , refl

code : {A : Type }  (Bouquet A)  Type 
code {A = A} base = (FreeGroupoid A)
code (loop a i)   = pathsInU (η a) i

winding : typ ΩBouquet  FreeGroupoid A
winding l = subst code l ε

winding∙ : ΩBouquet →∙ FreeGroupoid∙ {A = A}
winding∙ = winding , refl

-- Functions using the truncated forms of types

π₁Bouquet : {A : Type }  Type 
π₁Bouquet {A = A} = π 1 (Bouquet∙ A)

loopingT :  FreeGroupoid A ∥₂  π₁Bouquet
loopingT = map looping

windingT : π₁Bouquet   FreeGroupoid A ∥₂
windingT = map winding

-- Utility proofs

substPathsR : {C : Type }{y z : C}  (x : C)  (p : y  z)  subst  y  x  y) p  λ q  q  p
substPathsR {y = y} x p = funExt homotopy where
  homotopy :  q  subst  y  x  y) p q  q  p
  homotopy q = J P d p where
    P :  z'  y  z'  Type _
    P z' p' = subst  y  x  y) p' q  q  p'
    d : P y refl
    d =
      subst  y  x  y) refl q
      ≡⟨ substRefl {B = λ y  x  y} q 
      q
      ≡⟨ rUnit q 
      q  refl 

substFunctions : {B C : A  Type }{x y : A}
                   (p : x  y)
                   (f : B x  C x)
                   subst  z  (B z  C z)) p f  subst C p  f  subst B (sym p)
substFunctions {B = B} {C = C} {x = x} p f =  J P d p where
  auxC : idfun (C x)  subst C refl
  auxC = funExt  c  sym (substRefl {B = C} c))
  auxB : idfun (B x)  subst B refl
  auxB = funExt  b  sym (substRefl {B = B} b))
  P :  y'  x  y'  Type _
  P y' p' = subst  z  (B z  C z)) p' f  subst C p'  f  subst B (sym p')
  d : P x refl
  d =
    subst  z  (B z  C z)) refl f
    ≡⟨ substRefl {B = λ z  (B z  C z)} f 
    f
    ≡⟨ cong  h  h  f  idfun (B x)) auxC 
    subst C refl  f  idfun (B x)
    ≡⟨ cong  h  subst C refl  f  h) auxB 
    subst C refl  f  subst B (sym refl) 

-- Definition of the encode - decode functions over the family of types Π(x : W A) → code x

encode : (x : Bouquet A)  base  x  code x
encode x l = subst code l ε

decode : {A : Type }(x : Bouquet A)  code x  base  x
decode {A = A} base       = looping
decode {A = A} (loop a i) = path i where
  pathover : PathP  i  (code (loop a i)  base  (loop a i))) looping (subst  z  (code z  base  z)) (loop a) looping)
  pathover = subst-filler  z  (code z  base  z)) (loop a) looping
  aux : (a : A)  subst code (sym (loop a))  action (inv (η a))
  aux a = funExt homotopy where
    homotopy :  (g : FreeGroupoid A)  subst code (sym (loop a)) g  action (inv (η a)) g
    homotopy g =
      subst code (sym (loop a)) g
      ≡⟨ cong  x  transport x g) (sym (invPathsInUNaturality (η a))) 
      transport (pathsInU (inv (η a))) g
      ≡⟨ uaβ  (equivs (inv (η a))) g 
      action (inv (η a)) g 
  calculations :  (a : A)   g  looping (g · (inv (η a)))  loop a  looping g
  calculations a g =
    looping (g · (inv (η a)))  loop a
    ≡⟨ sym (pathAssoc (looping g) (sym (loop a)) (loop a)) 
    looping g  (sym (loop a)  loop a)
    ≡⟨ cong  x  looping g  x) (lCancel (loop a)) 
    looping g  refl
    ≡⟨ sym (rUnit (looping g)) 
    looping g 
  path' : subst  z  (code z  base  z)) (loop a) looping  looping
  path' =
    subst  z  (code z  base  z)) (loop a) looping
    ≡⟨ substFunctions {B = code} {C = λ z  base  z} (loop a) looping 
    subst  z  base  z) (loop a)  looping  subst code (sym (loop a))
    ≡⟨ cong  x  x  looping  subst code (sym (loop a))) (substPathsR base (loop a)) 
     p  p  loop a)  looping  subst code (sym (loop a))
    ≡⟨ cong  x   p  p  loop a)  looping  x) (aux a) 
     p  p  loop a)  looping  action (inv (η a))
    ≡⟨ funExt (calculations a) 
    looping 
  path'' : PathP  i  code ((loop a  refl) i)  base  ((loop a  refl) i)) looping looping
  path'' = compPathP' {A = Bouquet A} {B = λ z  code z  base  z} pathover path'
  p''≡p : PathP  i  code ((loop a  refl) i)  base  ((loop a  refl) i)) looping looping 
          PathP  i  code (loop a i)  base  (loop a i)) looping looping
  p''≡p = cong  x  PathP  i  code (x i)  base  (x i)) looping looping) (sym (rUnit (loop a)))
  path : PathP  i  code (loop a i)  base  (loop a i)) looping looping
  path = transport p''≡p path''

-- Non truncated Left Homotopy

decodeEncode : (x : Bouquet A)  (p : base  x)  decode x (encode x p)  p
decodeEncode x p = J P d p where
  P : (x' : Bouquet A)  base  x'  Type _
  P x' p' = decode x' (encode x' p')  p'
  d : P base refl
  d =
    decode base (encode base refl)
    ≡⟨ cong  e'  looping e') (transportRefl ε) 
    refl 

left-homotopy :  (l : typ (ΩBouquet {A = A}))  looping (winding l)  l
left-homotopy l = decodeEncode base l

-- Truncated proofs of right homotopy of winding/looping functions

truncatedPathEquality : (g : FreeGroupoid A)   cong code (looping g)  ua (equivs g) ∥₁
truncatedPathEquality = elimProp
            Bprop
             a   η-ind a ∣₁)
             g1 g2  λ ∣ind1∣₁ ∣ind2∣₁  rec2 squash₁  ind1 ind2   ·-ind g1 g2 ind1 ind2 ∣₁) ∣ind1∣₁ ∣ind2∣₁)
             ε-ind ∣₁
             g  λ ∣ind∣₁  propRec squash₁  ind   inv-ind g ind ∣₁) ∣ind∣₁) where
  B :  g  Type _
  B g = cong code (looping g)  ua (equivs g)
  Bprop :  g  isProp  B g ∥₁
  Bprop g = squash₁
  η-ind :  a  B (η a)
  η-ind a = refl
  ·-ind :  g1 g2  B g1  B g2  B (g1 · g2)
  ·-ind g1 g2 ind1 ind2 =
    cong code (looping (g1 · g2))
    ≡⟨ cong  x  x  cong code (looping g2)) ind1 
    pathsInU g1  cong code (looping g2)
    ≡⟨ cong  x  pathsInU g1  x) ind2 
    pathsInU g1  pathsInU g2
    ≡⟨ sym (multPathsInUNaturality g1 g2) 
    pathsInU (g1 · g2) 
  ε-ind : B ε
  ε-ind =
    cong code (looping ε)
    ≡⟨ sym idPathsInUNaturality 
    pathsInU ε 
  inv-ind :  g  B g  B (inv g)
  inv-ind g ind =
    cong code (looping (inv g))
    ≡⟨ cong sym ind 
    sym (pathsInU g)
    ≡⟨ sym (invPathsInUNaturality g) 
    ua (equivs (inv g)) 

truncatedRight-homotopy : (g : FreeGroupoid A)   winding (looping g)  g ∥₁
truncatedRight-homotopy g = propRec squash₁ recursion (truncatedPathEquality g) where
  recursion : cong code (looping g)  ua (equivs g)   winding (looping g)  g ∥₁
  recursion hyp =  aux ∣₁ where
    aux : winding (looping g)  g
    aux =
      winding (looping g)
      ≡⟨ cong  x  transport x ε) hyp 
      transport (ua (equivs g)) ε
      ≡⟨ uaβ  (equivs g) ε 
      ε · g
      ≡⟨ sym (idl g) 
      g 

right-homotopyInTruncatedGroupoid : (g : FreeGroupoid A)   winding (looping g) ∣₂   g ∣₂
right-homotopyInTruncatedGroupoid g = Iso.inv PathIdTrunc₀Iso (truncatedRight-homotopy g)

-- Truncated encodeDecode over all fibrations

truncatedEncodeDecode : (x : Bouquet A)  (g : code x)   encode x (decode x g)  g ∥₁
truncatedEncodeDecode base = truncatedRight-homotopy
truncatedEncodeDecode (loop a i) = isProp→PathP prop truncatedRight-homotopy truncatedRight-homotopy i where
  prop :  i  isProp (∀ (g : code (loop a i))   encode (loop a i) (decode (loop a i) g)  g ∥₁)
  prop i f g = funExt pointwise where
    pointwise : (x : code (loop a i))  PathP  _   encode (loop a i) (decode (loop a i) x)  x ∥₁) (f x) (g x)
    pointwise x = isProp→PathP  i  squash₁) (f x) (g x)

encodeDecodeInTruncatedGroupoid : (x : Bouquet A)  (g : code x)   encode x (decode x g) ∣₂   g ∣₂
encodeDecodeInTruncatedGroupoid x g = Iso.inv PathIdTrunc₀Iso (truncatedEncodeDecode x g)

-- Encode Decode over the truncated versions of the types

encodeT : (x : Bouquet A)   base  x ∥₂   code x ∥₂
encodeT x = map (encode x)

decodeT : (x : Bouquet A)   code x ∥₂   base  x ∥₂
decodeT x = map (decode x)

decodeEncodeT : (x : Bouquet A)  (p :  base  x ∥₂)  decodeT x (encodeT x p)  p
decodeEncodeT x g = elim sethood induction g where
  sethood : (q :  base  x ∥₂)  isSet (decodeT x (encodeT x q)  q)
  sethood q = isProp→isSet (squash₂ (decodeT x (encodeT x q)) q)
  induction : (l : base  x)   decode x (encode x l) ∣₂   l ∣₂
  induction l = cong  l'   l' ∣₂) (decodeEncode x l)

encodeDecodeT : (x : Bouquet A)  (g :  code x ∥₂)  encodeT x (decodeT x g)  g
encodeDecodeT x g = elim sethood induction g where
  sethood : (z :  code x ∥₂)  isSet (encodeT x (decodeT x z)  z)
  sethood z = isProp→isSet (squash₂ (encodeT x (decodeT x z)) z)
  induction : (a : code x)   encode x (decode x a) ∣₂   a ∣₂
  induction a = encodeDecodeInTruncatedGroupoid x a

-- Final equivalences

TruncatedFamiliesIso : (x : Bouquet A)  Iso  base  x ∥₂  code x ∥₂
Iso.fun (TruncatedFamiliesIso x)      = encodeT x
Iso.inv (TruncatedFamiliesIso x)      = decodeT x
Iso.rightInv (TruncatedFamiliesIso x) = encodeDecodeT x
Iso.leftInv (TruncatedFamiliesIso x)  = decodeEncodeT x

TruncatedFamiliesEquiv : (x : Bouquet A)   base  x ∥₂   code x ∥₂
TruncatedFamiliesEquiv x = isoToEquiv (TruncatedFamiliesIso x)

TruncatedFamilies≡ : (x : Bouquet A)   base  x ∥₂   code x ∥₂
TruncatedFamilies≡ x = ua (TruncatedFamiliesEquiv x)

π₁Bouquet≡∥FreeGroupoid∥₂ : π₁Bouquet   FreeGroupoid A ∥₂
π₁Bouquet≡∥FreeGroupoid∥₂ = TruncatedFamilies≡ base

π₁Bouquet≡FreeGroup : {A : Type }  π₁Bouquet  FreeGroup A
π₁Bouquet≡FreeGroup {A = A} =
  π₁Bouquet
  ≡⟨ π₁Bouquet≡∥FreeGroupoid∥₂ 
   FreeGroupoid A ∥₂
  ≡⟨ sym freeGroupTruncIdempotent 
  FreeGroup A