{-# OPTIONS --cubical --safe #-}

module Data.Bag where

open import Prelude
open import Algebra
open import Path.Reasoning

infixr 5 _∷_
data ⟅_⟆ (A : Type a) : Type a where
  []     :  A 
  _∷_    : A   A    A 
  com    :  x y xs  x  y  xs  y  x  xs
  trunc  : isSet  A 

record Elim {a }
             (A : Type a)
             (P :  A   Type )
             : Type (a ℓ⊔ ) where
  constructor elim
  field
    ⟅_⟆-set :  {xs}  isSet (P xs)
    ⟅_⟆[] : P []
    ⟅_⟆_∷_⟨_⟩ :  x xs  P xs  P (x  xs)
  private z = ⟅_⟆[]; f = ⟅_⟆_∷_⟨_⟩
  field
    ⟅_⟆-com : (∀ x y xs pxs 
      PathP   i  P (com x y xs i))
             (f x (y  xs) (f y xs pxs))
             (f y (x  xs) (f x xs pxs)))
  ⟅_⟆⇓ : (xs :  A )  P xs
   [] ⟆⇓ = z
   x  xs ⟆⇓ = f x xs  xs ⟆⇓
   com x y xs i ⟆⇓ = ⟅_⟆-com x y xs  xs ⟆⇓ i
   trunc xs ys x y i j ⟆⇓ =
    isOfHLevel→isOfHLevelDep 2
       xs  ⟅_⟆-set {xs})
       xs ⟆⇓  ys ⟆⇓
      (cong ⟅_⟆⇓ x) (cong ⟅_⟆⇓ y)
      (trunc xs ys x y)
      i j

open Elim


infixr 0 elim-syntax
elim-syntax  :  {a }
              (A : Type a)
              ( A   Type )
              Type (a ℓ⊔ )
elim-syntax = Elim

syntax elim-syntax A  xs  Pxs) = xs ⦂⟅ A ⟆→ Pxs

record ElimProp {a } (A : Type a) (P :  A   Type ) : Type (a ℓ⊔ ) where
  constructor elim-prop
  field
    ⟦_⟧-prop :  {xs}  isProp (P xs)
    ⟦_⟧[] : P []
    ⟦_⟧_∷_⟨_⟩ :  x xs  P xs  P (x  xs)
  private z = ⟦_⟧[]; f = ⟦_⟧_∷_⟨_⟩
  ⟦_⟧⇑ = elim
          (isProp→isSet ⟦_⟧-prop)
          z f
           x y xs pxs  toPathP (⟦_⟧-prop (transp  i  P (com x y xs i)) i0
            (f x (y  xs) (f y xs pxs))) (f y (x  xs) (f x xs pxs))))
  ⟦_⟧⇓ =  ⟦_⟧⇑ ⟆⇓

open ElimProp

infixr 0 elim-prop-syntax
elim-prop-syntax :  {a }  (A : Type a)  ( A   Type )  Type (a ℓ⊔ )
elim-prop-syntax = ElimProp

syntax elim-prop-syntax A  xs  Pxs) = xs ⦂⟅ A ⟆→∥ Pxs 

record [⟅_⟆→_] {a b} (A : Type a) (B : Type b) : Type (a ℓ⊔ b) where
  constructor rec
  field
    [_]-set  : isSet B
    [_]_∷_ : A  B  B
    [_][]    : B
  private f = [_]_∷_; z = [_][]
  field
    [_]-com :  x y xs  f x (f y xs)  f y (f x xs)
  [_]⇑ = elim [_]-set z  x _  f x)  x y _  [_]-com x y)
  [_]↓ =  [_]⇑ ⟆⇓
open [⟅_⟆→_]

infixr 5 _∪_
_∪_ :  A    A    A 
_∪_ = λ xs ys  [ ys ∪′ ]↓ xs
  where
  _∪′ :  A   [⟅ A ⟆→  A  ]
  [ ys ∪′ ]-set = trunc
  [ ys ∪′ ] x  xs = x  xs
  [ ys ∪′ ][] = ys
  [ ys ∪′ ]-com = com

∪-assoc : (xs ys zs :  A ) 
  (xs  ys)  zs  xs  (ys  zs)
∪-assoc = λ xs ys zs   ∪-assoc′ ys zs ⟧⇓ xs
  where
  ∪-assoc′ :  ys zs 
    xs ⦂⟅ A ⟆→∥ (xs  ys)  zs  xs  (ys  zs) 
   ∪-assoc′ ys zs ⟧-prop = trunc _ _
   ∪-assoc′ ys zs ⟧[] = refl
   ∪-assoc′ ys zs  x  xs  P  = cong (x ∷_) P

∪-cons  :  (x : A) xs ys
         (x  xs)  ys  xs  (x  ys)
∪-cons = λ x xs ys   ∪-cons′ x ys ⟧⇓ xs
  where
  ∪-cons′ :  x ys 
    xs ⦂⟅ A ⟆→∥ (x  xs)  ys  xs  (x  ys) 
   ∪-cons′ x ys ⟧-prop = trunc _ _
   ∪-cons′ x ys ⟧[] = refl
   ∪-cons′ x ys  y  xs  P  = cong (_∪ ys) (com x y xs) ; cong (y ∷_) P

∪-idʳ : (xs :  A )  xs  []  xs
∪-idʳ =  ∪-idʳ′ ⟧⇓
  where
  ∪-idʳ′ : xs ⦂⟅ A ⟆→∥ xs  []  xs 
   ∪-idʳ′ ⟧-prop = trunc _ _
   ∪-idʳ′ ⟧[] = refl
   ∪-idʳ′  x  xs  P  = cong (x ∷_) P

∪-comm : (xs ys :  A )  xs  ys  ys  xs
∪-comm {A = A} = λ xs ys   ∪-comm′ ys ⟧⇓ xs
  where
  ∪-comm′ : (ys :  A ) 
    xs ⦂⟅ A ⟆→∥ xs  ys  ys  xs 
   ∪-comm′ ys ⟧-prop = trunc _ _
   ∪-comm′ ys ⟧[] = sym (∪-idʳ ys)
   ∪-comm′ ys  x  xs  P  =
    (x  xs)  ys ≡⟨ cong (x ∷_) P 
    (x  ys)  xs ≡⟨ ∪-cons x ys xs 
    ys  x  xs 

⟅⟆-commutative-monoid :  {a} (A : Type a)  CommutativeMonoid  A 
Monoid._∙_ (CommutativeMonoid.monoid (⟅⟆-commutative-monoid A)) = _∪_
Monoid.ε (CommutativeMonoid.monoid (⟅⟆-commutative-monoid A)) = []
Monoid.assoc (CommutativeMonoid.monoid (⟅⟆-commutative-monoid A)) = ∪-assoc
Monoid.ε∙ (CommutativeMonoid.monoid (⟅⟆-commutative-monoid A)) _ = refl
Monoid.∙ε (CommutativeMonoid.monoid (⟅⟆-commutative-monoid A)) = ∪-idʳ
CommutativeMonoid.comm (⟅⟆-commutative-monoid A) = ∪-comm

module _ {} {𝑆 : Type } (mon : CommutativeMonoid 𝑆) (sIsSet : isSet 𝑆) where
  open CommutativeMonoid mon
  ⟦_⟧ : (A  𝑆)   A   𝑆
  ⟦_⟧ = λ h  [  h ⟧′ ]↓
    where
    ⟦_⟧′ : (A  𝑆)  [⟅ A ⟆→ 𝑆 ]
    [  h ⟧′ ] x  xs = h x  xs
    [  h ⟧′ ][] = ε
    [  h ⟧′ ]-com x y xs =
      h x  (h y  xs)
        ≡˘⟨ assoc _ _ _ 
      (h x  h y)  xs
        ≡⟨ cong (_∙ xs) (comm _ _) 
      (h y  h x)  xs
        ≡⟨ assoc _ _ _ 
      h y  (h x  xs) 
    [  h ⟧′ ]-set = sIsSet

record ⟦_≡_⟧ {a b} {A : Type a} {B : Type b}
            (h :  A   B)
            (xf : [⟅ A ⟆→ B ])
            : Type (a ℓ⊔ b) where
  constructor elim-univ
  field
    ⟦_≡⟧_∷_ :  x xs  h (x  xs)  [ xf ] x  h xs
    ⟦_≡⟧[] : h []  [ xf ][]
  ⟦_≡⟧⇓ :  xs  h xs  [ xf ]↓ xs
  ⟦_≡⟧⇓ =  ≡⇓′ ⟧⇓
    where
    ≡⇓′ : xs ⦂⟅ A ⟆→∥ h xs  [ xf ]↓ xs 
     ≡⇓′ ⟧-prop = [ xf ]-set _ _
     ≡⇓′ ⟧[] = ⟦_≡⟧[]
     ≡⇓′  x  xs  P  = ⟦_≡⟧_∷_ x _ ; cong ([ xf ] x ∷_) P
open ⟦_≡_⟧

record ⟦_⊚_≡_⟧ {a b c} {A : Type a} {B : Type b} {C : Type c}
               (h : B  C)
               (xf : [⟅ A ⟆→ B ])
               (yf : [⟅ A ⟆→ C ])
               : Type (a ℓ⊔ b ℓ⊔ c)
    where
  constructor elim-fuse
  field
    ⟦_∘≡⟧_∷_ :  x xs  h ([ xf ] x  xs)  [ yf ] x  h xs
    ⟦_∘≡⟧[] : h [ xf ][]  [ yf ][]
  ⟦_∘≡⟧⇓ :  xs  h ([ xf ]↓ xs)  [ yf ]↓ xs
  ⟦_∘≡⟧⇓ =  ≡⇓′ ⟧⇓
    where
    ≡⇓′ : xs ⦂⟅ A ⟆→∥ h ([ xf ]↓ xs)  [ yf ]↓ xs 
     ≡⇓′ ⟧-prop = [ yf ]-set _ _
     ≡⇓′ ⟧[] = ⟦_∘≡⟧[]
     ≡⇓′  x  xs  P  = ⟦_∘≡⟧_∷_ x _ ; cong ([ yf ] x ∷_) P
open ⟦_⊚_≡_⟧

map-alg : (A  B)  [⟅ A ⟆→  B  ]
[ map-alg f ]-set = trunc
[ map-alg f ][] = []
[ map-alg f ] x  xs = f x  xs
[ map-alg f ]-com x y = com (f x) (f y)

map : (A  B)   A    B 
map f = [ map-alg f ]↓

[_]∘_ : [⟅ B ⟆→ C ]  (A  B)  [⟅ A ⟆→ C ]
[ [ g ]∘ f ]-set      = [ g ]-set
[ [ g ]∘ f ][]        = [ g ][]
[ [ g ]∘ f ] x  xs   = [ g ] f x  xs
[ [ g ]∘ f ]-com x y  = [ g ]-com (f x) (f y)

map-fuse  :  (g : [⟅ B ⟆→ C ]) (f : A  B)
           [ g ]↓  map f  [ [ g ]∘ f ]↓
map-fuse g f = funExt  map-fuse′ g f ∘≡⟧⇓
  where
  map-fuse′  : (g : [⟅ B ⟆→ C ]) (f : A  B)
               [ g ]↓  map-alg f  [ g ]∘ f 
   map-fuse′ g f ∘≡⟧ x  xs = refl
   map-fuse′ g f ∘≡⟧[] = refl

bind :  A   (A   B )   B 
bind xs f =  ⟅⟆-commutative-monoid _  trunc f xs