\begin{code}
{-# OPTIONS --safe #-}

open import Algebra
open import Prelude
open import Path.Reasoning

module Data.Weighted.CommutativeMonoid {} {𝑆 : Type } (sgp : Semigroup 𝑆) where

open import Data.Weighted.Definition sgp
open import Data.Weighted.Eliminators sgp
open import Data.Weighted.Functor

open Semigroup sgp
module _ {A : Type a} where
  infixr 5 _∪_
  _∪_ :  Weighted A  Weighted A  Weighted A
  ⟅⟆             ys = ys
  (p  x  xs)   ys = p  x  xs  ys
  com p x q y xs i  ys = com p x q y (xs  ys) i
  dup p q x xs i  ys = dup p q x (xs  ys) i
  trunc xs₁ xs₂ p q i j  ys =
    trunc (xs₁  ys) (xs₂  ys) (cong (_∪ ys) p) (cong (_∪ ys) q) i j

  ∪-assoc : Associative _∪_
  ∪-assoc xs ys zs =  ∪-assoc-alg ys zs  xs
    where
    ∪-assoc-alg :  ys zs  Ψ[ xs  Weighted A ]  ((xs  ys)  zs  xs  (ys  zs))
    ∪-assoc-alg ys zs .fst ⟅⟆ = refl
    ∪-assoc-alg ys zs .fst (w  x  _  P⟨xs⟩ ) = cong (w  x ∷_) P⟨xs⟩
    ∪-assoc-alg ys zs .snd = eq-coh

  ∪-cons :  w x xs ys  w  x  xs  ys  xs  w  x  ys
  ∪-cons w x xs ys =  ∪-cons-alg w x ys  xs
    where
    ∪-cons-alg :  w x ys  Ψ[ xs  Weighted A ]  (w  x  xs  ys  xs  w  x  ys)
    ∪-cons-alg w x ys .snd = eq-coh
    ∪-cons-alg w x ys .fst ⟅⟆ = refl
    ∪-cons-alg p x ys .fst (q  y  xs  P⟨xs⟩ ) =
      p  x  q  y  xs  ys ≡⟨ com p x q y _ 
      q  y  p  x  xs  ys ≡⟨ cong (q  y ∷_) P⟨xs⟩ 
      q  y  xs  p  x  ys 

  ∪-idʳ :  xs  xs  ⟅⟆  xs
  ∪-idʳ =  ∪-idʳ-alg 
    where
    ∪-idʳ-alg : Ψ[ xs  Weighted A ]  (xs  ⟅⟆  xs)
    ∪-idʳ-alg .snd = eq-coh
    ∪-idʳ-alg .fst ⟅⟆ = refl
    ∪-idʳ-alg .fst (w  x  xs  P⟨xs⟩ ) = cong (w  x ∷_) P⟨xs⟩

  ∪-com :  xs ys  xs  ys  ys  xs
  ∪-com xs ys =  ∪-com-alg ys  xs
    where
    ∪-com-alg :  ys  Ψ[ xs  Weighted A ]  (xs  ys  ys  xs)
    ∪-com-alg ys .snd = eq-coh
    ∪-com-alg ys .fst ⟅⟆ = sym (∪-idʳ ys)
    ∪-com-alg ys .fst (w  x  xs  P⟨xs⟩ ) =
      w  x  xs  ys ≡⟨ cong (w  x ∷_) P⟨xs⟩ 
      w  x  ys  xs ≡⟨ ∪-cons w x ys xs 
      ys  w  x  xs  

  commutativeMonoid : CommutativeMonoid (Weighted A)
  commutativeMonoid .CommutativeMonoid.monoid .Monoid._∙_ = _∪_
  commutativeMonoid .CommutativeMonoid.monoid .Monoid.ε = ⟅⟆
  commutativeMonoid .CommutativeMonoid.monoid .Monoid.assoc = ∪-assoc
  commutativeMonoid .CommutativeMonoid.monoid .Monoid.ε∙ _ = refl
  commutativeMonoid .CommutativeMonoid.monoid .Monoid.∙ε = ∪-idʳ
  commutativeMonoid .CommutativeMonoid.comm = ∪-com

  infix 4 _⊆_
\end{code}
%<*subset>
\begin{code}
  _⊆_ : Weighted A  Weighted A  Type-
  xs  ys = xs  ys  ys
\end{code}
%</subset>
\begin{code}

  ⊆-trans :  xs ys zs  xs  ys  ys  zs  xs  zs
  ⊆-trans xs ys zs xs⊆ys ys⊆zs =
    xs  zs ≡˘⟨ cong (xs ∪_) ys⊆zs 
    xs  (ys  zs) ≡˘⟨ ∪-assoc xs ys zs 
    (xs  ys)  zs ≡⟨ cong (_∪ zs) xs⊆ys 
    ys  zs ≡⟨ ys⊆zs 
    zs 

  []⊆ :  xs  ⟅⟆  xs
  []⊆ _ = refl

  ⊆-cons :  w (x : A) xs ys  xs  ys  xs  w  x  ys
  ⊆-cons w x xs ys xs⊆ys = sym (∪-cons w x xs ys) ; cong (w  x ∷_) xs⊆ys

record _⟶W_ (A : Type a) (B : Type b) : Type ( ℓ⊔ a ℓ⊔ b) where
  no-eta-equality
  field
    act-w : 𝑆  A  Weighted B
    coh-w :  p q x  act-w p x  act-w q x  act-w (p  q) x
open _⟶W_ public

infixl 6 _[_]W⊣_
_[_]W⊣_ : 𝑆  A ⟶W B  A  Weighted B
_[_]W⊣_ = flip act-w


[_]W-hom : A ⟶W B  A W⟶ Weighted B
[ h ]W-hom = record
  { w-mon = commutativeMonoid
  ; w-set = trunc
  ; w-act = act-w h
  ; w-coh = coh-w h
  }

[_]W↓ : A ⟶W B  Weighted A  Weighted B
[ h ]W↓ = W[ [ h ]W-hom ]↓

module _ {A : Type a} {B : Type b} where
  collect-∪ : (h : A ⟶W B) (xs ys : Weighted A)  [ h ]W↓ (xs  ys)  [ h ]W↓ xs  [ h ]W↓ ys
  collect-∪ h xs ys =  alg  xs
    where
    alg : Ψ[ xs  Weighted A ]  [ h ]W↓ (xs  ys)  [ h ]W↓ xs  [ h ]W↓ ys
    alg .fst ⟅⟆ = refl
    alg .fst (w  x  xs  P⟨xs⟩ ) = cong (w [ h ]W⊣ x ∪_) P⟨xs⟩ ; sym (∪-assoc (w [ h ]W⊣ x) ([ h ]W↓ xs) ([ h ]W↓ ys))
    alg .snd = eq-coh


module ⟶W-Eq {A : Type a} {B : Type b} where
  ⟶W′ : Type _
  ⟶W′ = Σ[ kont  (𝑆  A  Weighted B) ] × (∀ p q x  kont p x  kont q x  kont (p  q) x)

  toSigma : (A ⟶W B)  ⟶W′
  toSigma x = x .act-w , x .coh-w

  fromSigma : ⟶W′  (A ⟶W B)
  fromSigma x = record
    { act-w = x .fst
    ; coh-w = x .snd
    }

  sigma-iso : (A ⟶W B)  ⟶W′
  sigma-iso .fun = toSigma
  sigma-iso .inv = fromSigma
  sigma-iso .rightInv x = refl
  sigma-iso .leftInv x i .act-w = x .act-w
  sigma-iso .leftInv x i .coh-w = x .coh-w

  ⟶W-≡ : (x y : A ⟶W B)  (∀ w v  act-w x w v  act-w y w v)  x  y
  ⟶W-≡ x y p = sym (sigma-iso .leftInv x)
              ; cong fromSigma (Σ≡Prop  _  isPropΠ λ _  isPropΠ λ _  isPropΠ λ _  trunc _ _) (funExt λ w  funExt (p w)))
              ; sigma-iso .leftInv y
open ⟶W-Eq using (⟶W-≡) public

module _ {A : Type a} {B : Type b} where
  fold-⊕-hom :  (h : A W⟶ B) x y  W[ h ]↓ x W[ h ]⊕ W[ h ]↓ y  W[ h ]↓ (x  y)
  fold-⊕-hom h xs ys =  alg ys  xs
    where
    alg :  ys  Ψ[ xs  Weighted A ]  W[ h ]↓ xs W[ h ]⊕ W[ h ]↓ ys  W[ h ]↓ (xs  ys)
    alg ys .fst ⟅⟆ = W[ h ]-ε∙ _
    alg ys .fst (w  x  xs  P⟨xs⟩ ) =
      W[ h ]↓ (w  x  xs) W[ h ]⊕ W[ h ]↓ ys ≡⟨⟩
      w W[ h ]⊣ x W[ h ]⊕  W[ h ]↓ xs W[ h ]⊕ W[ h ]↓ ys ≡⟨ W[ h ]-assoc _ _ _ 
      w W[ h ]⊣ x W[ h ]⊕ (W[ h ]↓ xs W[ h ]⊕ W[ h ]↓ ys) ≡⟨ cong (w W[ h ]⊣ x W[ h ]⊕_) P⟨xs⟩ 
      w W[ h ]⊣ x W[ h ]⊕  W[ h ]↓ (xs  ys) ≡⟨⟩
      W[ h ]↓ (w  x  xs  ys) 
    alg ys .snd = prop-coh λ _  w-set h _ _

  fold-ε-hom : (h : A W⟶ B)  W[ h ]↓ ⟅⟆  W[ h ]-ε
  fold-ε-hom _ = refl

module _ {A : Type a} {B : Type b} {C : Type c} where
  infixl 9 _W∘_
  _W∘_ : B W⟶ C  A ⟶W B  A W⟶ C
  (b→c W∘ a→b) .w-mon = b→c .w-mon
  (b→c W∘ a→b) .w-set = b→c .w-set
  (b→c W∘ a→b) .w-act s x = W[ b→c ]↓ (s [ a→b ]W⊣ x)
  (b→c W∘ a→b) .w-coh p q x =
    W[ b→c ]↓ (p [ a→b ]W⊣ x) W[ b→c ]⊕ W[ b→c ]↓ (q [ a→b ]W⊣ x) ≡⟨ fold-⊕-hom b→c (p [ a→b ]W⊣ x) (q [ a→b ]W⊣ x) 
    W[ b→c ]↓ (p [ a→b ]W⊣ x  q [ a→b ]W⊣ x) ≡⟨ cong W[ b→c ]↓ (coh-w a→b p q x) 
    W[ b→c ]↓ (p  q [ a→b ]W⊣ x) 

  module _ (b→c : B W⟶ C) (a→b : A ⟶W B) where
    W-comp-eq :  x  W[ b→c W∘ a→b ]↓ x  W[ b→c ]↓ ([ a→b ]W↓ x)
    W-comp-eq =  alg 
      where
      alg : Ψ[ x  Weighted A ]  W[ b→c W∘ a→b ]↓ x  W[ b→c ]↓ ([ a→b ]W↓ x)
      alg .fst ⟅⟆ = refl
      alg .fst (w  x  xs  P⟨xs⟩ ) =
        W[ b→c W∘ a→b ]↓ (w  x  xs) ≡⟨⟩
        W[ b→c ]↓ (w [ a→b ]W⊣ x) W[ b→c ]⊕ W[ b→c W∘ a→b ]↓ xs ≡⟨ cong (W[ b→c ]↓ (w [ a→b ]W⊣ x) W[ b→c ]⊕_) P⟨xs⟩ 
        W[ b→c ]↓ (w [ a→b ]W⊣ x) W[ b→c ]⊕ W[ b→c ]↓ ([ a→b ]W↓ xs) ≡⟨ fold-⊕-hom b→c (w [ a→b ]W⊣ x) ([ a→b ]W↓ xs) 
        W[ b→c ]↓ (w [ a→b ]W⊣ x  [ a→b ]W↓ xs) ≡⟨⟩
        W[ b→c ]↓ ([ a→b ]W↓ (w  x  xs)) 
      alg .snd = prop-coh λ _  w-set b→c _ _

W⟶id : A ⟶W A
W⟶id .act-w p x = p  x  ⟅⟆
W⟶id .coh-w p q x = dup p q x ⟅⟆

W⟶id-id : (x : Weighted A)  [ W⟶id ]W↓ x  x
W⟶id-id =  alg 
  where
  alg : Ψ[ x  Weighted A ]  [ W⟶id ]W↓ x  x
  alg .snd = eq-coh
  alg .fst ⟅⟆ = refl
  alg .fst (w  x  xs  P⟨xs⟩ ) = cong (w  x ∷_) P⟨xs⟩

hom-inj : {A : Type a}  (x y : Weighted A)  (∀ {B : Type ( ℓ⊔ a)} (f : A W⟶ B)  W[ f ]↓ x  W[ f ]↓ y)  x  y
hom-inj x y p = sym (W⟶id-id x) ; p [ W⟶id ]W-hom ; W⟶id-id y

null : Weighted A  Bool
null =  alg 
  where
  alg : Ψ[ x  Weighted A ]  Bool
  alg .fst ⟅⟆ = true
  alg .fst (w  x  xs  P⟨xs⟩ ) = false
  alg .snd .c-set _ = isSetBool
  alg .snd .c-dup p q x xs ψ⟨xs⟩ = refl
  alg .snd .c-com p x q y xs ψ⟨xs⟩ = refl
\end{code}