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

open import Algebra
open import Prelude hiding ([_])
open import Relation.Binary

module Data.Weighted.Cutoff {ℓ₁ ℓ₂ ℓ₃} {W : Type ℓ₁} (ord : TotalOrder W ℓ₂ ℓ₃) where

open TotalOrder ord hiding (refl)

open import Data.Weighted.Definition ⊓-semigroup
open import Data.Weighted.Eliminators ⊓-semigroup
open import Data.Weighted.CommutativeMonoid ⊓-semigroup
open import Data.Weighted.Functor



open import Path.Reasoning

module _ {A : Type a} where
  _/_▹_ : W  W  A  Weighted A
  c / p  x = if p ≤ᵇ c then p  x  ⟅⟆ else ⟅⟆

  _//_▹_ : W  W  A  Weighted A
  c // p  x = if p <ᵇ c then p  x  ⟅⟆ else ⟅⟆

  _⊣-alg : W  A ⟶W A
  (w ⊣-alg) .act-w p x = w / p  x
  (w ⊣-alg) .coh-w p q x with p ≤? w | q ≤? w | p  q ≤? w
  ... | no  p≰w | no  q≰w | no  p⊓q≰w = refl
  ... | yes p≤w | yes q≤w | yes p⊓q≤w = dup p q x ⟅⟆
  ... | yes p≤w | no  q≰w | yes p⊓q≤w = cong (_▹ x  ⟅⟆) (sym (⊓≤≡ p q ≲[ ≤[ p≤w ] ≲; ≤[ <⇒≤ (≰⇒> q≰w) ] ]))
  ... | no  p≰w | yes q≤w | yes p⊓q≤w = cong (_▹ x  ⟅⟆) (sym (⊓≤≡ q p ≲[ ≤[ q≤w ] ≲; ≤[ <⇒≤ (≰⇒> p≰w) ] ] ) ; ⊓-comm q p)
  ... | yes p≤w | _       | no  p⊓q≰w = absurd (p⊓q≰w ≲[ ≤[ ⊓≤ p q ] ≲; ≤[ p≤w ] ])
  ... | _       | yes q≤w | no  p⊓q≰w = absurd (p⊓q≰w ≲[ ≡[ ⊓-comm p q ] ≲; ≤[ ⊓≤ q p ] ≲; ≤[ q≤w ] ])
  ... | no  p≰w | no  q≰w | yes p⊓q≤w with p <? q
  ... | yes _ = absurd (p≰w p⊓q≤w)
  ... | no  _ = absurd (q≰w p⊓q≤w)

  cutoff-lt-alg : W  Ψ[ xs  Weighted A ]  Weighted A
  cutoff-lt-alg w .fst ⟅⟆ = ⟅⟆
  cutoff-lt-alg w .fst (p  x  _  xs ) = w // p  x  xs
  cutoff-lt-alg w .snd .c-set _ = trunc
  cutoff-lt-alg w .snd .c-com p x q y _ xs =
        w // p  x  w // q  y  xs   ≡˘⟨ ∪-assoc (w // p  x) _ _ 
        (w // p  x  w // q  y)  xs ≡⟨ cong (_∪ xs) ( ∪-com (w // p  x) _) 
        (w // q  y  w // p  x)  xs ≡⟨ ∪-assoc (w // q  y) _ _ 
        w // q  y  w // p  x  xs 

  cutoff-lt-alg w .snd .c-dup p q x _ xs with p <? w | q <? w | p  q <? w
  ... | no  p≮w | no  q≮w | no  p⊓q≮w = refl
  ... | yes p<w | yes q<w | yes p⊓q<w = dup p q x xs
  ... | yes p<w | no  q≮w | yes p⊓q<w = cong (_▹ x  xs) (sym (⊓≤≡ p q (<⇒≤ ≲[ <[ p<w ] ≲; ≤[ ≮⇒≥ q≮w ] ])))
  ... | no  p≮w | yes q<w | yes p⊓q<w = cong (_▹ x  xs) (sym (⊓≤≡ q p (<⇒≤ ≲[ <[ q<w ] ≲; ≤[ ≮⇒≥ p≮w ] ]) ) ; ⊓-comm q p)
  ... | yes p<w | _       | no  p⊓q≮w = absurd (p⊓q≮w ≲[ ≤[ ⊓≤ p q ] ≲; <[ p<w ] ])
  ... | _       | yes q<w | no  p⊓q≮w = absurd (p⊓q≮w ≲[ ≡[ ⊓-comm p q ] ≲; ≤[ ⊓≤ q p ] ≲; <[ q<w ] ])
  ... | no  p≮w | no  q≮w | yes p⊓q<w with p <? q
  ... | yes _ = absurd (p≮w p⊓q<w)
  ... | no  _ = absurd (q≮w p⊓q<w)

  infixl 6 _⊢_
  _⊢_ : Weighted A  W  Weighted A
  x  w = [ w ⊣-alg ]W↓ x

  infixl 6 _⊨_
  _⊨_ : Weighted A  W  Weighted A
  x  w =  cutoff-lt-alg w  x
  ⊢-≥ :  s v w  v  w  s  v  w  s  w
  ⊢-≥ s v w v≥w =  cutoff-≤-alg  s
    where
    cutoff-≤-alg : Ψ[ s  Weighted A ]  s  v  w  s  w
    cutoff-≤-alg .fst ⟅⟆ = refl
    cutoff-≤-alg .fst (p  x  xs  P⟨xs⟩ ) with p ≤? v
    cutoff-≤-alg .fst (p  x  xs  P⟨xs⟩ ) | no  p≰v with p ≤? w
    cutoff-≤-alg .fst (p  x  xs  P⟨xs⟩ ) | no p≰v | yes p≤w = absurd (p≰v (≤-trans p≤w v≥w))
    cutoff-≤-alg .fst (p  x  xs  P⟨xs⟩ ) | no p≰v | no  p≰w = P⟨xs⟩
    cutoff-≤-alg .fst (p  x  xs  P⟨xs⟩ ) | yes p≤v with p ≤? w
    cutoff-≤-alg .fst (p  x  xs  P⟨xs⟩ ) | yes p≤v | yes p≤w = cong (p  x ∷_) P⟨xs⟩
    cutoff-≤-alg .fst (p  x  xs  P⟨xs⟩ ) | yes p≤v | no  p≰w = P⟨xs⟩
    cutoff-≤-alg .snd = prop-coh λ _  trunc _ _ 

  ⊢-ε :  w  ⟅⟆  w  ⟅⟆
  ⊢-ε w = refl

  ⊢-∪ :  s t w  (s  t)  w  s  w  t  w
  ⊢-∪ xs ys w = collect-∪ (w ⊣-alg) xs ys 

  /≤ :  w p x  p  w  w / p  x  p  x  ⟅⟆
  /≤ w p x p≤w with p ≤? w
  ... | yes _ = refl
  ... | no p≰w = absurd (p≰w p≤w)

  /≰ :  w p x  p  w  w / p  x  ⟅⟆
  /≰ w p x p≰w with p ≤? w
  ... | yes p≤w = absurd (p≰w p≤w)
  ... | no _ = refl

  /▹-comm :  w₁ w₂ p x  (w₁ / p  x)  w₂  (w₂ / p  x)  w₁
  /▹-comm w₁ w₂ p x with p ≤? w₁ | p ≤? w₂
  ... | yes p≤w₁ | yes p≤w₂ = cong (_∪ ⟅⟆) (/≤ w₂ p x p≤w₂ ; sym (/≤ w₁ p x p≤w₁))
  ... | no  p≰w₁ | no  p≰w₂ = refl
  ... | no  p≰w₁ | yes p≤w₂ = sym (cong (_∪ ⟅⟆) (/≰ w₁ p x p≰w₁))
  ... | yes p≤w₁ | no  p≰w₂ = cong (_∪ ⟅⟆) (/≰ w₂ p x p≰w₂)

  ⊢-com :  s v w  s  v  w  s  w  v
  ⊢-com s w₁ w₂ =  alg  s
    where
    alg : Ψ[ s  Weighted A ]  s  w₁  w₂  s  w₂  w₁
    alg .fst (p  x  xs  P⟨xs⟩ ) =
      (p  x  xs)  w₁  w₂         ≡⟨⟩
      (w₁ / p  x  xs  w₁)  w₂    ≡⟨ ⊢-∪ (w₁ / p  x) _ _ 
      w₁ / p  x  w₂  xs  w₁  w₂ ≡⟨ cong₂ _∪_ (/▹-comm w₁ w₂ p x) P⟨xs⟩ 
      w₂ / p  x  w₁  xs  w₂  w₁ ≡˘⟨ ⊢-∪ (w₂ / p  x) _ _ 
      (p  x  xs)  w₂  w₁ 
    alg .fst ⟅⟆ = refl
    alg .snd = eq-coh

  ⊢-⊓ :
\end{code}
%<*cutoff-min>
\begin{code}[number=code:cutoff-min]
     s v w  (s  v)  w  s  (v  w)
\end{code}
%</cutoff-min>
\begin{code}
  ⊢-⊓ s v w with v <? w
  ... | no  v≮w = ⊢-≥ s v w (≮⇒≥ v≮w)
  ... | yes v<w = ⊢-com s v w ; ⊢-≥ s w v (<⇒≤ v<w)

infixl 6 _∘⊢_
_∘⊢_ : (A  Weighted B)  W  A  Weighted B
(k ∘⊢ w) x = k x  w

infix 4 _⊑_
_⊑_ : Weighted A  Weighted A  Type-
xs  ys =  k × xs  ys  k

⊑-antisym : {x y : Weighted A}  x  y  y  x  x  y
⊑-antisym {x = x} {y} (k₁ , x≡y⊢k₁) (k₂ , y≡x⊢k₂) with k₁ ≤|≥ k₂ 
... | inl k₁≤k₂ = x≡y⊢k₁ ; sym (⊢-≥ y _ _ k₁≤k₂) ; ⊢-com y k₂ k₁ ; sym (cong (_⊢ k₂) x≡y⊢k₁) ; sym y≡x⊢k₂
... | inr k₂≤k₁ = x≡y⊢k₁ ; cong (_⊢ k₁) y≡x⊢k₂ ; ⊢-com x k₂ k₁ ; ⊢-≥ x _ _ k₂≤k₁ ; sym y≡x⊢k₂

⊢-∪-idem :  k (x : Weighted A)  x  x  k  x
⊢-∪-idem k =  alg k 
  where
  alg :  k  Ψ[ x  Weighted A ]  x  x  k  x
  alg k .fst ⟅⟆ = refl
  alg k .fst (w  x  xs  P⟨xs⟩ ) with w ≤? k
  ... | yes w≤k =
    w  x  xs  w  x  xs  k ≡˘⟨ cong (w  x ∷_) (∪-cons w x xs (xs  k)) 
    w  x  w  x  xs  xs  k ≡⟨ dup w w x (xs  xs  k) 
    w  w  x  xs  xs  k ≡⟨ cong (_▹ x  xs  xs  k) (⊓-idem w) 
    w  x  xs  xs  k ≡⟨ cong (w  x ∷_) P⟨xs⟩ 
    w  x  xs 
  ... | no  _ = cong (w  x ∷_) P⟨xs⟩
  alg k .snd = eq-coh

⊑-∪ : (x y : Weighted A)  x  y  x  y  y
⊑-∪ x y (k , x⊑y) = cong (_∪ y) x⊑y ; ∪-com (y  k) y ; ⊢-∪-idem k y
\end{code}