{-# OPTIONS --safe #-}

module Multiset.FCM.Base where

open import Multiset.Prelude

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
  using (_∘_)
open import Cubical.Foundations.HLevels
  using
    ( isOfHLevelDep
    ; isOfHLevel→isOfHLevelDep
    ; isPropDep→isSetDep
    ; isSetΠ
    )

-- Finite multisets of a type, a.k.a. the free commutative monoid
-- -- As a HIT
-- -- (check Choudhury, Fiore: https://arxiv.org/abs/2110.05412)

infixr 8 _⊕_

data M { : Level} (X : Type ) : Type  where
-- -- point constructors
  η : (x : X)  M X          -- η = \eta
  ε : M X                     -- ε = \Ge or \epsilon
  _⊕_ : (m n : M X)  M X    -- ⊕ = \o+ or \oplus

-- -- path constructors
  unit  : (m     : M X)  ε  m  m
  assoc : (m n o : M X)  m  n  o  (m  n)  o
  comm  : (m n   : M X)  m  n  n  m

-- -- set truncation
  trunc : isSet (M X)

unit' :  {}  {X : Type }  (m : M X)  m  ε  m
unit' m = (comm m ε)  (unit m)

open M

private
  variable
     ℓ' : Level
    X : Type 

isSetM : isSet (M X)
isSetM = trunc

rec : {A : Type ℓ'}
   (setA : isSet A)
   ( : A)
   (η̂ : X  A)
   (_+_ : (a b : A)  A)
   (+-unit :  a   + a  a)
   (+-assoc :  a b c  a + (b + c)  (a + b) + c)
   (+-comm :  a b  a + b  b + a)
   M X  A
rec {A = A} setA  η̂ _+_ +-unit +-assoc +-comm = go where
  go : _  A
  go (η x) = η̂ x
  go ε = 
  go (m  n) = (go m) + (go n)
  go (unit m i) = +-unit (go m) i
  go (assoc m n k i) = +-assoc (go m) (go n) (go k) i
  go (comm m n i) = +-comm (go m) (go n) i
  go (trunc m n p q i j) = setA (go m) (go n) (cong go p) (cong go q) i j


map :  {ℓ''} {Y : Type ℓ''}  (X  Y)  M X  M Y
map f = rec trunc ε (η  f) _⊕_ unit assoc comm

-- Elimination into a family of sets.
elim : {A : M X  Type ℓ'}
   (setA :  xs  isSet (A xs))
   ( : A ε)
   (singleton : (x : X)  A (η x))
   (_∪_ : {m n : M X}  A m  A n  A (m  n))
   (∪-unit :  {m} (a : A m)  PathP  i  A (unit m i)) (  a) a)
   (∪-assoc :  {m n k} (a : A m) (b : A n) (c : A k)  PathP  i  A (assoc m n k i)) (a  (b  c)) ((a  b)  c))
   (∪-comm :  {m n} (a : A m) (b : A n)  PathP  i  A (comm m n i)) (a  b) (b  a))
   (m : M X)  A m
elim {X = X} {A = A} setA Ø singleton _∪_ ∪-unit ∪-assoc ∪-comm = go where
  setA' : isOfHLevelDep 2 A
  setA' = isOfHLevel→isOfHLevelDep 2 setA

  go : (m : M X)  A m
  go (η x) = singleton x
  go ε = Ø
  go (m  n) = go m  go n
  go (unit m i) = ∪-unit (go m) i
  go (assoc m n k i) = ∪-assoc (go m) (go n) (go k) i
  go (comm m n i) = ∪-comm (go m) (go n) i
  go (trunc m n p q i j) = setA' (go m) (go n) (cong go p) (cong go q) (trunc m n p q) i j

-- Induction principle.
--
-- Given a family `P` of properties over `M X`, we can show `P(m)` for
-- any `m ∈ M X` provided that:
-- ∙ Pη : P holds for all singleton multisets
-- ∙ Pε : P holds for the empty multiset
-- ∙ ∨ : P holds for the union of multisets if it holds for its factors
ind : {X : Type } {P : M X  Type ℓ'}
   (propP :  m  isProp (P m))
   ( : P ε)
   (singleton : (x : X)  P (η x))
   (_∨_ : {m n : M X}  (p : P m)  (q : P n)  P (m  n))
   (m : M X)  P m
ind {X = X} {P = P} propP  singleton _∨_ =
  elim  m  isProp→isSet (propP m))  singleton _∨_ ∨-unit ∨-assoc ∨-comm where
    propDepP : isOfHLevelDep 1 P
    propDepP = isOfHLevel→isOfHLevelDep 1 propP {a0 = _}

    ∨-unit :  {m} (p : P m) 
      PathP  i  P (unit m i)) (  p) p
    ∨-unit p = isProp→PathP  i  propP (unit _ i)) _ _

    ∨-assoc : {m n k : M X} (p : P m) (q : P n) (r : P k) 
      PathP  i  P (assoc m n k i)) (p  (q  r)) ((p  q)  r)
    ∨-assoc p q r = isProp→PathP  i  propP (assoc _ _ _ i)) _ _

    ∨-comm : {m n : M X} (p : P m) (q : P n) 
      PathP  i  P (comm m n i)) (p  q) (q  p)
    ∨-comm p q = isProp→PathP  i  propP (comm _ _ i)) _ _

mapComp :  {ℓ″ ℓ‴} {Y : Type ℓ″} {Z : Type ℓ‴}
   (g : Y  Z)
   (f : X  Y)
   (xs : M X)
   map (g  f) xs  map g (map f xs)
mapComp g f = ind {P = λ xs  map (g  f) xs  map g (map f xs)}  xs  isSetM _ _)
  (refl {x = ε})
   x  refl {x = η (g (f x))})
   {xs ys} mapComp-xs mapComp-ys  cong₂ _⊕_ mapComp-xs mapComp-ys)

mapId :  (xs : M X)
   map  x  x) xs  xs
mapId = ind  xs  isSetM _ _) refl  _  refl) λ mapId-xs mapId-ys  cong₂ _⊕_ mapId-xs mapId-ys

-- indPath : {X : Type ℓ} {Y : Type ℓ'}
--   → {P : {xs ys : M X} → xs ≡ ys}
--   → (∀ ys → P {ε} ys)
--   → (∀ ys → P {ε} ys)
--   → (xs : M X) → ys ≡ ys'

_∷_ : X  M X  M X
x  m = η x  m

infixr 7 _∷_

_∷ʳ_ : M X  X  M X
m ∷ʳ x = m  η x

∷-swap :  (x y : X) xs  x  y  xs  y  x  xs
∷-swap x y xs =
  x  y  xs       ≡⟨⟩
  η x  (η y  xs) ≡⟨ assoc _ _ _ 
  (η x  η y)  xs ≡⟨ cong (_⊕ xs) (comm _ _) 
  (η y  η x)  xs ≡⟨ sym (assoc _ _ _) 
  η y  (η x  xs) ≡⟨⟩
  y  x  xs 

∷-swap-split≡ :  {} {X : Type } {x y : X} {xs ys zs : M X}
   (xs  y  zs)
   (x  zs  ys)
   x  xs  y  ys
∷-swap-split≡ {x = x} {y} {xs} {ys} {zs} xs-split ys-split =
  x  xs ≡⟨ cong (x ∷_) xs-split 
  x  y  zs ≡⟨ ∷-swap x y zs 
  y  x  zs ≡⟨ cong (y ∷_) ys-split 
  y  ys 
  
-- ∷-elim : {B : M X → Type ℓ'}
--   → (setB : ∀ m → isSet (B m))
--   → (nil : B ε)
--   → (cons : (x : X) → {xs : M X} → B xs → B (x ∷ xs))
--   → (swap : (x y : X) → {xs : M X} → {b : B xs} → PathP (λ i → B (∷-swap x y xs i)) (cons x (cons y b)) (cons y (cons x b)))
--   → (xs : M X) → B xs
-- ∷-elim {B = B} setB nil cons swap = elim setB nil η* _⊕*_ {! !} {! !} {! !} where
--   η* : ∀ x → B (η x)
--   η* x = subst B (unit' (η x)) (cons x nil)

--   _⊕*_ : ∀ {xs ys} → B xs → B ys → B (xs ⊕ ys)
--   b-xs ⊕* b-ys = {! !}

module _ where
  open import Cubical.Data.Nat as  using ()

  count : (♯_ : X  )  M X  
  count ♯_ = rec ℕ.isSetℕ 0 ♯_ ℕ._+_  _  refl) ℕ.+-assoc ℕ.+-comm