{-# OPTIONS --safe #-}

{-

This module, uses normalization and decidable equality from `Cubical.Algebra.Group.Free` to demonstrate that Bouquet over discrete type is hGroupoid by establishing a coding between loops in the bouquet and elements of the FreeGroup represented by normalised words.

-}

module Cubical.HITs.Bouquet.Discrete where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Transport
open import Cubical.Foundations.HLevels

open import Cubical.Data.Bool
open import Cubical.Data.Empty as 
open import Cubical.Data.Sigma
open import Cubical.Data.List renaming (rec to recList)
open import Cubical.Data.Sum

open import Cubical.Relation.Nullary


open import Cubical.HITs.Bouquet.Base
open import Cubical.Algebra.Group.Free


private
  variable
     : Level

module _ {A : Type } (_≟_ : Discrete A) where

  open NormalForm A

  open Discrete _≟_


  CodeBouquet : Bouquet A  Type 
  CodeBouquet base = Σ _ IsNormalised
  CodeBouquet (loop a i) = ua (η·≃ (true , a)) i

  co→ :  x  base  x  CodeBouquet x
  co→ x p = subst CodeBouquet p ([] , lower)

  co←base-step : Bool × A  Path (Bouquet A) base base

  co←base-step (b , a) = if b then (idfun _) else sym $ loop a

  co←base : [𝟚× A ]  Path (Bouquet A) base base
  co←base = recList refl (flip _∙_  co←base-step)

  co← :  x  CodeBouquet x  base  x
  co← base = co←base  fst
  co← (loop a i) x j = co←Sq a i j x
   where

   co←Sq : (a : A)  SquareP  i j   ua (η·≃ (true , a)) i  Bouquet A)
                        (funExt (co←base  fst))
                        (funExt (co←base  fst))
                         _ _  base)
                         i _  loop a i)
   co←Sq a = congP  _  funExt) (ua→ (uncurry
       xs y  co←Sq' a xs y (HeadIsRedex? ((true , a)  xs)))))
    where
    co←Sq' : (a : A)  (x : [𝟚× A ]) (y : IsNormalised x) 
        u  PathP  i  base  loop a i)
       (recList  _  base) (flip _∙_  co←base-step) x)
       (recList  _  base) (flip _∙_  co←base-step) (preη· (true , a) x u ))
    co←Sq' a ((false , snd₁)  xs) y (yes p) =
      cong  x'  co←base ((false , x')  xs)) (cong snd (sym p))
         symP (compPath-filler (co←base xs) (sym (loop a)))
    co←Sq' a xs y (no ¬p) = compPath-filler _ _
    co←Sq' a ((true , snd₁)  xs) y (yes p) = ⊥.rec (true≢false (cong fst p))

  coSec :  x  section (co← x) (co→ x)
  coSec _ = J  x b  co← x (co→ x b)  b) refl

  coRet : (x : [𝟚× A ]) (y : IsNormalised x) 
            fst (subst CodeBouquet (co← base (x , y)) ([] , lower))
                   x
  coRet [] y = refl
  coRet (x@(b , a)  xs) y =
    cong fst (substComposite CodeBouquet (co← base (xs , y  inr))
      (co←base-step x) _)
      ∙∙
      cong (fst  subst CodeBouquet (co←base-step x))
         (Σ≡Prop isPropIsNormalised (coRet xs (y  inr))) ∙∙
      lem b xs (y  inr)  η·∷ x xs (y  inl)

   where
   lem :  b xs y  fst
      (subst CodeBouquet (co←base-step (b , a)) (xs , y))
       η· (b , a) xs
   lem false xs y = cong fst (~uaβ (η·≃ (true , a)) (xs , y ))
   lem true xs y = cong fst (uaβ (η·≃ (true , a)) (xs , y ))


  codeDecode : Iso (Path (Bouquet A) base base)
                   (Σ _ IsNormalised)
  Iso.fun codeDecode p = subst CodeBouquet p ([] , lower)
  Iso.inv codeDecode = co← base
  Iso.rightInv codeDecode = Σ≡Prop isPropIsNormalised  uncurry coRet
  Iso.leftInv codeDecode = coSec base

  isSetΩBouquet : isSet (Path (Bouquet A) base base)
  isSetΩBouquet = isOfHLevelRetractFromIso 2 codeDecode
                     (isSetΣ isSet[𝟚×] (isProp→isSet  isPropIsNormalised))

  isGroupoidBouquet : isGroupoid (Bouquet A)
  isGroupoidBouquet base base = isSetΩBouquet
  isGroupoidBouquet base (loop x i) = isProp→PathP
    i  isPropIsSet {A = Path (Bouquet A) base (loop x i)}) isSetΩBouquet isSetΩBouquet i
  isGroupoidBouquet (loop x i) base = isProp→PathP
    i  isPropIsSet {A = Path (Bouquet A) (loop x i) base}) isSetΩBouquet isSetΩBouquet i
  isGroupoidBouquet (loop x i) (loop x₁ i₁) = isSet→SquareP
       i i₁  isProp→isSet (isPropIsSet {A = Path (Bouquet A) (loop x i) (loop x₁ i₁)}))
      (isProp→PathP
          i  isPropIsSet {A = Path (Bouquet A) base (loop x₁ i)}) isSetΩBouquet isSetΩBouquet)
      (isProp→PathP
          i  isPropIsSet {A = Path (Bouquet A) base (loop x₁ i)}) isSetΩBouquet isSetΩBouquet)
      (isProp→PathP
          i  isPropIsSet {A = Path (Bouquet A) (loop x i) base}) isSetΩBouquet isSetΩBouquet)
      (isProp→PathP
          i  isPropIsSet {A = Path (Bouquet A) (loop x i) base}) isSetΩBouquet isSetΩBouquet)
     i i₁