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

open import Algebra.Monus
open import Prelude
open import Algebra

module Data.Weighted.Free  {S : Type}  (mon : TMAPOM S) where

open TMAPOM mon

open import Data.Weighted.Definition ⊓-semigroup
open import Data.Weighted.Eliminators ⊓-semigroup
open import Data.Weighted.CommutativeMonoid ⊓-semigroup
  using ()
  renaming
    ( _∪_ to _∪′_
    )
open import Data.Weighted.Monad (weight S tapom) renaming (_⋊_ to _⋊′_) hiding (⋊⟨∪⟩)
open import Data.Weighted.Functor

open import Path.Reasoning

S-weight : Weight S
S-weight = weight S tapom

𝒲 : Type  Type _
\end{code}
%<*church>
\begin{code}
𝒲 A =  (V : Type)  isSet V  (mod : WeightSemimodule S-weight V)  (A  V)  V
\end{code}
%</church>
\begin{code}

η : A  Weighted A
η = return

wng : Weight S
wng = weight S tapom

open import Data.Weighted.Semimodule mon

module _ {ℓ̬} {V : Type ℓ̬} (isSetV : isSet V) (mod : WeightSemimodule wng V) where
  open WeightSemimodule mod
  
  module _ (f : A  V) where
    ⟦f⟧ : Weighted A  V
    ⟦f⟧ =  alg 
      where
      alg : Ψ[ xs  Weighted A ]  V
      alg .fst ⟅⟆ = 
      alg .fst (w  x  xs  P⟨xs⟩ ) = (w  f x)  P⟨xs⟩
      alg .snd .c-set _ = isSetV
      alg .snd .c-dup p q x xs ψ⟨xs⟩ =
        p  f x  (q  f x  ψ⟨xs⟩) ≡˘⟨ ∪-assoc (p  f x) (q  f x) ψ⟨xs⟩ 
        (p  f x  q  f x)  ψ⟨xs⟩ ≡˘⟨ cong (_∪ ψ⟨xs⟩) (⟨+⟩⋊ p q (f x)) 
        p  q  f x  ψ⟨xs⟩ 

      alg .snd .c-com p x q y xs ψ⟨xs⟩ =
        p  f x  (q  f y  ψ⟨xs⟩) ≡˘⟨ ∪-assoc _ _ _ 
        (p  f x  q  f y)  ψ⟨xs⟩ ≡⟨ cong (_∪ ψ⟨xs⟩) ( ∪-com (p  f x) (q  f y)) 
        (q  f y  p  f x)  ψ⟨xs⟩ ≡⟨ ∪-assoc _ _ _ 
        q  f y  (p  f x  ψ⟨xs⟩) 

    ⟦f⟧-⋊-hom :  p x  ⟦f⟧ (p ⋊′ x)  p  ⟦f⟧ x
    ⟦f⟧-⋊-hom p =  alg p 
      where
      alg :  p  Ψ[ x  Weighted A ]  ⟦f⟧ (p ⋊′ x)  p  ⟦f⟧ x
      alg p .snd = prop-coh λ _  isSetV _ _
      alg p .fst ⟅⟆ = sym (⋊∅ _)
      alg p .fst (q  x  xs  P⟨xs⟩ ) =
        ⟦f⟧ (p ⋊′ (q  x  xs)) ≡⟨⟩
        ⟦f⟧ (p  q  x  (p ⋊′ xs)) ≡⟨⟩
        ((p  q)  f x)  ⟦f⟧ (p ⋊′ xs) ≡⟨ cong₂ _∪_ (⟨*⟩⋊ p q (f x)) P⟨xs⟩ 
        (p  q  f x)  (p  ⟦f⟧ xs) ≡˘⟨ ⋊⟨∪⟩ p _ _ 
        p  (q  f x  ⟦f⟧ xs) ≡⟨⟩
        p  ⟦f⟧ (q  x  xs) 

    ⟦f⟧-∙-hom :  x y  ⟦f⟧ (x ∪′ y)  ⟦f⟧ x  ⟦f⟧ y
    ⟦f⟧-∙-hom x y =  alg y  x
      where
      alg :  y  Ψ[ x  Weighted A ]  ⟦f⟧ (x ∪′ y)  ⟦f⟧ x  ⟦f⟧ y
      alg ys .snd = prop-coh λ _  isSetV _ _
      alg ys .fst ⟅⟆ = sym (∅∪ _)
      alg ys .fst (w  x  xs  P⟨xs⟩ ) =
        ⟦f⟧ (w  x  xs ∪′ ys) ≡⟨⟩
        (w  f x)  ⟦f⟧ (xs ∪′ ys) ≡⟨ cong ((w  f x) ∪_) P⟨xs⟩ 
        (w  f x)  (⟦f⟧ xs  ⟦f⟧ ys) ≡˘⟨ ∪-assoc (w  f x) (⟦f⟧ xs) (⟦f⟧ ys) 
        ((w  f x)  ⟦f⟧ xs)  ⟦f⟧ ys ≡⟨⟩
        ⟦f⟧ (w  x  xs)  ⟦f⟧ ys 

    hom : WeightHomomorphism[ wng ] weightedSemimodule {A = A}  mod
    hom .WeightHomomorphism[_]_⟶_.mon-homo .MonoidHomomorphism_⟶_.f = ⟦f⟧
    hom .WeightHomomorphism[_]_⟶_.mon-homo .MonoidHomomorphism_⟶_.∙-homo = ⟦f⟧-∙-hom
    hom .WeightHomomorphism[_]_⟶_.mon-homo .MonoidHomomorphism_⟶_.ε-homo = refl
    hom .WeightHomomorphism[_]_⟶_.⋊-homo = ⟦f⟧-⋊-hom

    module _ (hom′ : WeightHomomorphism[ wng ] weightedSemimodule { A = A }  mod)
             (diag :  x  WeightHomomorphism[_]_⟶_.f hom′ (return x)  f x) where
      open WeightHomomorphism[_]_⟶_ hom′
        renaming (f to h)

      uniq :  x  h x  ⟦f⟧ x
      uniq =  alg 
        where
        alg : Ψ[ xs  Weighted A ]  h xs  ⟦f⟧ xs
        alg .snd = prop-coh λ _  isSetV _ _
        alg .fst ⟅⟆ = ε-homo
        alg .fst (w  x  xs  P⟨xs⟩ ) =
          h (w  x  xs) ≡⟨⟩
          h ((w  x  ⟅⟆) ∪′ xs) ≡⟨ ∙-homo (w  x  ⟅⟆) xs 
          h (w  x  ⟅⟆)  h xs ≡˘⟨ cong  e  h (e  x  ⟅⟆)  h xs) (⊗𝟙 w) 
          h (w  𝟙  x  ⟅⟆)  h xs ≡⟨⟩
          h (w ⋊′ η x)  h xs ≡⟨ cong₂ _∪_ (⋊-homo w (η x) ; cong (w ⋊_) (diag x)) P⟨xs⟩ 
          w  f x  ⟦f⟧ xs ≡⟨⟩
          ⟦f⟧ (w  x  xs) 

module _ {A : Type} where
  𝒲-surj : 𝒲 A ↠! Weighted A
  𝒲-surj .fst w = w (Weighted A) trunc weightedSemimodule return
  𝒲-surj .snd xs .image _ set mod f = ⟦f⟧ set mod f xs
  𝒲-surj .snd xs .reflects =  alg  xs
    where
    alg : Ψ[ xs  Weighted A ]  ⟦f⟧ trunc weightedSemimodule return xs  xs
    alg .snd = eq-coh
    alg .fst ⟅⟆ = refl
    alg .fst (w  x  xs  P⟨xs⟩ ) = cong₂ _▹ x ∷_ (∙ε w) P⟨xs⟩
\end{code}