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

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Data.Nat hiding (_·_)
open import Cubical.Data.Fin.Inductive.Base
open import Cubical.Data.Fin.Inductive.Properties
open import Cubical.Data.Empty as 

infixl 7 _·_
infix 20 _⁻¹

private variable
   ℓ' : Level
  A : Type 

data FreeAbGroup (A : Type ) : Type  where
  ⟦_⟧       : A  FreeAbGroup A
  ε         : FreeAbGroup A
  _·_       : FreeAbGroup A  FreeAbGroup A  FreeAbGroup A
  _⁻¹       : FreeAbGroup A  FreeAbGroup A
  assoc     :  x y z  x · (y · z)  (x · y) · z
  comm      :  x y    x · y        y · x
  identityᵣ :  x      x · ε        x
  invᵣ      :  x      x · x ⁻¹     ε
  trunc     : isSet (FreeAbGroup A)

module Elim {B : FreeAbGroup A  Type ℓ'}
  (⟦_⟧*       : (x : A)  B  x )
  (ε*         : B ε)
  (_·*_       :  {x y}    B x  B y  B (x · y))
  (_⁻¹*       :  {x}      B x  B (x ⁻¹))
  (assoc*     :  {x y z}  (xs : B x) (ys : B y) (zs : B z)
     PathP  i  B (assoc x y z i)) (xs ·* (ys ·* zs)) ((xs ·* ys) ·* zs))
  (comm*      :  {x y}    (xs : B x) (ys : B y)
     PathP  i  B (comm x y i)) (xs ·* ys) (ys ·* xs))
  (identityᵣ* :  {x}      (xs : B x)
     PathP  i  B (identityᵣ x i)) (xs ·* ε*) xs)
  (invᵣ* :  {x}  (xs : B x)
     PathP  i  B (invᵣ x i)) (xs ·* (xs ⁻¹*)) ε*)
  (trunc*     :  xs  isSet (B xs)) where

  f : (xs : FreeAbGroup A)  B xs
  f  x  =  x ⟧*
  f ε = ε*
  f (xs · ys) = f xs ·* f ys
  f (xs ⁻¹) = f xs ⁻¹*
  f (assoc xs ys zs i) = assoc* (f xs) (f ys) (f zs) i
  f (comm xs ys i) = comm* (f xs) (f ys) i
  f (identityᵣ xs i) = identityᵣ* (f xs) i
  f (invᵣ xs i) = invᵣ* (f xs) i
  f (trunc xs ys p q i j) = isOfHLevel→isOfHLevelDep 2 trunc*  (f xs) (f ys)
    (cong f p) (cong f q) (trunc xs ys p q) i j

module ElimProp {B : FreeAbGroup A  Type ℓ'}
  (BProp : {xs : FreeAbGroup A}  isProp (B xs))
  (⟦_⟧*       : (x : A)  B  x )
  (ε*         : B ε)
  (_·*_       :  {x y}    B x  B y  B (x · y))
  (_⁻¹*       :  {x}      B x  B (x ⁻¹)) where

  f : (xs : FreeAbGroup A)  B xs
  f = Elim.f ⟦_⟧* ε* _·*_ _⁻¹*
     {x y z} xs ys zs  toPathP (BProp (transport  i  B (assoc x y z i)) (xs ·* (ys ·* zs))) ((xs ·* ys) ·* zs)))
     {x y} xs ys  toPathP (BProp (transport  i  B (comm x y i)) (xs ·* ys)) (ys ·* xs)))
     {x} xs  toPathP (BProp (transport  i  B (identityᵣ x i)) (xs ·* ε*)) xs))
     {x} xs  toPathP (BProp (transport  i  B (invᵣ x i)) (xs ·* (xs ⁻¹*))) ε*))
     _  (isProp→isSet BProp))

module Rec {B : Type ℓ'} (BType : isSet B)
  (⟦_⟧*       : (x : A)  B)
  (ε*         : B)
  (_·*_       : B  B  B)
  (_⁻¹*       : B  B)
  (assoc*     : (x y z : B)  x ·* (y ·* z)  (x ·* y) ·* z)
  (comm*      : (x y : B)    x ·* y         y ·* x)
  (identityᵣ* : (x : B)      x ·* ε*        x)
  (invᵣ*      : (x : B)      x ·* (x ⁻¹*)   ε*)
  where

  f : FreeAbGroup A  B
  f = Elim.f ⟦_⟧* ε* _·*_ _⁻¹* assoc* comm* identityᵣ* invᵣ* (const BType)

isContr-Free⊥ : isContr (FreeAbGroup )
fst isContr-Free⊥ = ε
snd isContr-Free⊥ =
  ElimProp.f (trunc _ _)
     {()})
    refl
     p q  sym (identityᵣ ε)  cong₂ _·_ p q)
    λ p  sym (invᵣ ε)  comm _ _  identityᵣ (ε ⁻¹)  cong _⁻¹ p

isContr-FreeFin0 : isContr (FreeAbGroup (Fin 0))
isContr-FreeFin0 = subst (isContr  FreeAbGroup) (sym lem) isContr-Free⊥
  where
  lem : Fin 0  
  lem = isoToPath (iso ¬Fin0 (λ{()}) (λ{()}) λ p  ⊥.rec (¬Fin0 p))

Free↑ : (n : )  FreeAbGroup (Fin n)  FreeAbGroup (Fin (suc n))
Free↑ n  x  =  injectSuc x 
Free↑ n ε = ε
Free↑ n (x · x₁) = Free↑ n x · Free↑ n x₁
Free↑ n (x ⁻¹) = (Free↑ n x) ⁻¹
Free↑ n (assoc x x₁ x₂ i) = assoc (Free↑ n x) (Free↑ n x₁) (Free↑ n x₂) i
Free↑ n (comm x x₁ i) = comm (Free↑ n x) (Free↑ n x₁) i
Free↑ n (identityᵣ x i) = identityᵣ (Free↑ n x) i
Free↑ n (invᵣ x i) = invᵣ (Free↑ n x) i
Free↑ n (trunc x x₁ x₂ y i i₁) =
  isSet→isSet' trunc
     _  Free↑ n x)  _  Free↑ n x₁)
     j  Free↑ n (x₂ j))  j  Free↑ n (y j)) i₁ i

Free↑sumFinℤ : (n m : ) (g : Fin m  FreeAbGroup (Fin n))
   Free↑ n (sumFinGen {n = m} _·_ ε g)  sumFinGen {n = m} _·_ ε (Free↑ n  g)
Free↑sumFinℤ zero zero g = refl
Free↑sumFinℤ zero (suc m) g =
  cong (Free↑ zero (g flast) ·_) (Free↑sumFinℤ zero m  x  g (injectSuc x)))
Free↑sumFinℤ (suc n) zero g = refl
Free↑sumFinℤ (suc n) (suc m) g =
  cong (Free↑ (suc n) (g flast) ·_) (Free↑sumFinℤ (suc n) m  x  g (injectSuc x)))