{-# OPTIONS --safe #-}

module Cubical.HITs.FreeComMonoids.Properties where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.GroupoidLaws hiding (assoc)
open import Cubical.Foundations.Univalence

open import Cubical.Data.Nat hiding (_·_ ; _^_)

open import Cubical.HITs.FreeComMonoids.Base as FCM
open import Cubical.HITs.AssocList as AL

private variable
   : Level
  A : Type 



multi-· : A    FreeComMonoid A  FreeComMonoid A
multi-· x zero xs = xs
multi-· x (suc n) xs =  x  · multi-· x n xs

_^_ : A    FreeComMonoid A
x ^ n = multi-· x n ε

^+≡ :  (p : A) m n  ((p ^ m) · (p ^ n))  (p ^ (m + n))
^+≡ p zero n = identityₗ _
^+≡ p (suc m) n = sym (assoc _ _ _)  cong (_ ·_) (^+≡ _ _ _)

AL→FCM : AssocList A  FreeComMonoid A
AL→FCM = AL.Rec.f trunc ε  a n p  (a ^ n) · p)
  per* agg* (const identityₗ)
  where
  per* :  x y (mon : FreeComMonoid A) 
    (( x  · ε) · (( y  · ε) · mon)) 
    (( y  · ε) · (( x  · ε) · mon))
  per* x y mon =
    ( x  · ε) · (( y  · ε) · mon)
    ≡⟨ assoc _ _ _ 
    (( x  · ε) · ( y  · ε)) · mon
    ≡⟨ cong ( mon) (comm _ _) 
    ((( y  · ε) · ( x  · ε)) · mon)
    ≡⟨ sym (assoc _ _ _) 
    (( y  · ε) · (( x  · ε) · mon)) 

  agg* :  a m n mon 
      ((a ^ m) · ((a ^ n) · mon))  ((a ^ (m + n)) · mon)
  agg* a m n mon =
    ((a ^ m) · ((a ^ n) · mon))
    ≡⟨ assoc _ _ _ 
    (((a ^ m) · (a ^ n)) · mon)
    ≡⟨ cong ( _) (^+≡ _ _ _) 
    ((a ^ (m + n)) · mon) 

FCM→AL : FreeComMonoid A  AssocList A
FCM→AL = FCM.Rec.f trunc ⟨_⟩ ⟨⟩ _++_ comm-++ unitr-++  _  refl) assoc-++

^-id : (x : A) (m : ) (u : FreeComMonoid A)
   FCM→AL ((x ^ m) · u)   x , m ⟩∷ FCM→AL u
^-id x zero u = cong FCM→AL (identityₗ u)  sym (del _ _)
^-id x (suc m) u =
  FCM→AL (( x  · (x ^ m)) · u)
  ≡⟨ cong  x , 1 ⟩∷_ (^-id x m u) 
   x , 1 ⟩∷  x , m ⟩∷ FCM→AL u
  ≡⟨ agg _ _ _ _ 
   x , suc m ⟩∷ FCM→AL u 

++-· : (x y : AssocList A)
   AL→FCM (x ++ y)  AL→FCM x · AL→FCM y
++-· = AL.ElimProp.f
  (isPropΠ  _  trunc _ _))
   _  sym (identityₗ _))
  λ x n {xs} p ys 
  AL→FCM ((( x , n ⟩∷ ⟨⟩) ++ xs) ++ ys)
  ≡⟨ cong AL→FCM (cong (_++ ys) (comm-++ ( x , n ⟩∷ ⟨⟩) xs)  sym (assoc-++ xs _ ys)) 
  AL→FCM (xs ++ ( x , n ⟩∷ ys))
  ≡⟨ p _ 
  (AL→FCM xs · ((x ^ n) · AL→FCM ys))
  ≡⟨ assoc (AL→FCM xs) _ _ 
  ((AL→FCM xs · (x ^ n)) · AL→FCM ys)
  ≡⟨ cong ( AL→FCM ys) (comm _ _) 
  ((x ^ n) · AL→FCM xs) · AL→FCM ys 

AL→FCM∘FCM→AL≡id : section {A = AssocList A} AL→FCM FCM→AL
AL→FCM∘FCM→AL≡id = FCM.ElimProp.f (trunc _ _)  x  identityᵣ _  identityᵣ _) refl
  λ {x y} p q  ++-· (FCM→AL x) (FCM→AL y)  cong₂ _·_ p q

FCM→AL∘AL→FCM≡id : retract {A = AssocList A} AL→FCM FCM→AL
FCM→AL∘AL→FCM≡id = AL.ElimProp.f (trunc _ _) refl
  λ x n {xs} p  ^-id x n (AL→FCM xs)  cong ( _ , _ ⟩∷_) p

AssocList≃FreeComMonoid : AssocList A  FreeComMonoid A
AssocList≃FreeComMonoid = isoToEquiv (iso AL→FCM FCM→AL AL→FCM∘FCM→AL≡id FCM→AL∘AL→FCM≡id)

AssocList≡FreeComMonoid : AssocList A  FreeComMonoid A
AssocList≡FreeComMonoid = ua AssocList≃FreeComMonoid