{-# 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