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

open import Prelude renaming ([_] to [_]⊩)
open import Algebra
open import Algebra.Monus

module Codata.Neighbours.Solves
  {W : Type} (mon : WellFoundedMonus W)
  (s : W)
  (s≢ε : s  WellFoundedMonus.ε mon)
  where

open WellFoundedMonus mon

open Weight (weight W tapom)

open import Data.Weighted ⊕-semigroup hiding (⟪_⟫)
open import Data.Weighted.Monad (weight W tapom)
  using
    ( _⋊_
    ; bind-alg
    ; wmap-alg
    ; ⋊-alg
    ; wmap
    )
  renaming (return to ηʷ)
import Data.Weighted.Monad (weight _ tapom) as WB
open import Data.Weighted.Functor
open import Data.Weighted.Cutoff totalOrder
open import Data.Weighted.CutoffMonus tmapom

open import Path.Reasoning

open import Codata.Neighbours mon

module Solution {X : Type} (eᵢ : (X  INeighbours (X  A)  A)) where
  σ : INeighbours B  Neighbours B
  σ = _⋊ₙ_ s
 
  open Solve s s≢ε eᵢ

  cut-to-<s :  w wf p x  w < p  s  cut-to w wf p x  ⟅⟆
  cut-to-<s w wf p x w<p∙s with p  s ≤? w
  ... | no p∙s≰w = refl
  ... | yes p∙s≤w = absurd (w<p∙s p∙s≤w)

  cut-<s :  w wf  w < s   x  cut w wf x  ⟅⟆
  cut-<s w wf w<s =  alg 
    where
    alg : Ψ[ x  Weighted (X  A) ]  cut w wf x  ⟅⟆
    alg .snd = eq-coh
    alg .fst ⟅⟆ = refl
    alg .fst (p  x  xs  P⟨xs⟩ ) = cong₂ _∪_ (cut-to-<s w wf p x ≲[ <[ w<s ] ≲; ≤[ x≤x∙y ] ≲; ≡[ comm s p ] ]) P⟨xs⟩

  soln-step :  w wf p v
             (p≺w : p ≺ₛ w)
             step w p p≺w wf v  (solve  η) v  p≺w .fst
  soln-step w wf       p (inr x) _   = refl
  soln-step w (acc wf) p (inl x) p≺w = cong  e  find (p≺w .fst) e x) (isPropAcc _ _)

  soln-cut-to :  w wf p v  (p≺w : p ≺ₛ w)  cut-to w wf p v  s  p  (solve  η) v  p≺w .fst
  soln-cut-to w wf p v p≺w₁ with p  s ≤? w
  ... | no  p⊀w = absurd (p⊀w p≺w₁)
  ... | yes p≺w₂ =
    p  s  step w p p≺w₂ wf v        ≡⟨ cong (_⋊ step w p p≺w₂ wf v) (comm p s) 
    s  p  step w p p≺w₂ wf v        ≡⟨ cong (s  p ⋊_) (soln-step w wf p v p≺w₂)  
    s  p  (solve  η) v  p≺w₂ .fst ≡⟨ cong  e  s  p  (solve  η) v  e) (cancelˡ (p  s) _ _ (sym (p≺w₂ .snd) ; p≺w₁ .snd)) 
    s  p  (solve  η) v  p≺w₁ .fst 

  soln-compd :  k w wf  w  s  k   p v  cut-to w wf p v  W[ [ w ⊣-alg ]W-hom W∘ bind-alg (_⊩ w) W∘ wmap-alg (solve  η) W∘ ⋊-alg s ]↓ (k / p  v)
  soln-compd k w wf w≡s∙k p v with p ≤? k
  ... | no p≰k = cut-to-<s w wf p v ≲[ ≡[ w≡s∙k ] ≲; <[ <-congˡ s p≰k ] ≲; ≡[ comm s p ] ]
  ... | yes (k₂ , k≡p∙k₂) =
    cut-to w wf p v                                                                     ≡⟨ soln-cut-to w wf p v (k₂ , (w≡s∙k ; cong (s ∙_) k≡p∙k₂ ; sym (assoc s p k₂) ; cong (_∙ k₂) (comm s p))) 
    s  p  (solve  η) v  k₂                                                          ≡˘⟨ cong (s  p ⋊_) (neighbourly ((solve  η) v) w k₂ (p  s , (w≡s∙k ; cong (s ∙_) k≡p∙k₂ ; sym (assoc s p k₂) ; cong (_∙ k₂) (comm s p) ; comm (p  s) k₂))) 
    s  p  (solve  η) v  w  k₂                                                      ≡˘⟨ ≤-⋊-⊢ (s  p) ((solve  η) v  w) w (k₂ , (w≡s∙k ; cong (s ∙_) k≡p∙k₂ ; sym (assoc s p k₂) )) 
    (s  p  (solve  η) v  w)  w                                                     ≡⟨⟩
    W[ [ w ⊣-alg ]W-hom ]↓ (s  p  (solve  η) v  w)                                  ≡˘⟨ ∪-idʳ _ 
    W[ [ w ⊣-alg ]W-hom W∘ bind-alg (_⊩ w) ]↓ (s  p  (solve  η) v  ⟅⟆)              ≡˘⟨ ∪-idʳ _ 
    W[ [ w ⊣-alg ]W-hom W∘ bind-alg (_⊩ w) W∘ wmap-alg (solve  η) ]↓ (s  p  v  ⟅⟆)  ≡˘⟨ ∪-idʳ _ 
    W[ [ w ⊣-alg ]W-hom W∘ bind-alg (_⊩ w) W∘ wmap-alg (solve  η) W∘ ⋊-alg s ]↓ (p  v  ⟅⟆) 

  solution′ :  x w wf  find w wf x  (μ  mapₙ (solve  η)  (σ  η  inr)  eᵢ) x  w
  solution′ x w wf with eᵢ x
  ... | inr y = ηʷ y                 ≡˘⟨ return-⊢ y w 
                ηʷ y  w             ≡˘⟨ cong  v  (v  y  ⟅⟆)  w) (ε∙ ε) 
                (ε  ε  y  ⟅⟆)  w ≡⟨⟩
                (μ  mapₙ (solve  η)  (σ  η  inr)) (inr y)  w 
  ... | inl y with s ≤? w
  ... | no s≰w = cut-<s w wf s≰w (y  w)
  ... | yes (k , w≡s∙k) = 

    cut w wf (y  w)

      ≡⟨⟩

    [ cut-alg w wf ]W↓ (y  w)

      ≡⟨ cong  e  W[ e ]↓ (y  w))
        (W⟶-≡
          [ cut-alg w wf ]W-hom
          ([ w ⊣-alg ]W-hom W∘ bind-alg (_⊩ w) W∘ wmap-alg (solve  η) W∘ ⋊-alg s W∘ k ⊣-alg)
          (soln-compd k w wf w≡s∙k)
           _ _  refl)
          refl) 

    W[ [ w ⊣-alg ]W-hom W∘ bind-alg (_⊩ w) W∘ wmap-alg (solve  η) W∘ ⋊-alg s W∘ k ⊣-alg ]↓ (y  w)

      ≡⟨ W-comp-eq _ (k ⊣-alg) (y  w)
       ; W-comp-eq _ (⋊-alg s) (y  w  k)
       ; W-comp-eq _ (wmap-alg (solve  η)) (s  (y  w  k))
       ; W-comp-eq _ (bind-alg (_⊩ w)) (wmap (solve  η) (s  (y  w  k)))
       

    wmap (solve  η) (s  y  w  k)  w  w

      ≡⟨ cong  e  wmap (solve  η) (s  e)  w  w) (neighbourly y w k (s , (w≡s∙k ; comm s k))) 

    wmap (solve  η) (s  y  k)  w  w 

  solution :  x  solve x  (μ  mapₙ (solve  η)  (σ  η  inr)  eᵢ) x
  solution x = search≡  w  solution′ x w (well-founded (s , s≢ε) w))

module _ {X : Type} (eᵢ : (X  INeighbours (X  A)  A)) where
  open Solution eᵢ
  open Solve {X = X} {A = A} s s≢ε

  solution-disp :
\end{code}
%<*solution>
\begin{code}
     x  solve eᵢ x  (μ  mapₙ (solve eᵢ  η)  (σ  η  inr)  eᵢ) x
\end{code}
%</solution>
\begin{code}
  solution-disp = solution

open import Codata.Neighbours.Monad mon
open NeighboursMonad using (>>=-map)
open Monad neighboursMonad
open Functor neighboursFunctor using (map-id)

open import Codata.CIM hiding (μ; η)

μ″ : Neighbours (Neighbours A)  Neighbours A
μ″ x = x >>= id

map″ : (A  B)  Neighbours A  Neighbours B
map″ f x = x >>= return  f

μ-lemma : μ {A = A}  μ″
μ-lemma = funExt  x  sym (cong μ (map-id x)))

map-lemma : (f : A  B)  mapₙ f  map″ f
map-lemma f = funExt (>>=-map f)

module _ {X A : Type} where
  open Solve {X = X} {A = A} s s≢ε public
  open Solution  {A = A} {X = X} public

ideal : Ideal Neighbours INeighbours neighboursMonad neighboursFunctor
ideal .Ideal.mod .Module.μ′ = μ
ideal .Ideal.mod .Module.μ-η = funExt >>=-idʳ
ideal .Ideal.mod .Module.μ-μ = funExt  x  μ (μ x) ≡⟨ cong (_$ μ x) μ-lemma ; cong μ″ (cong (_$ x) μ-lemma) ; >>=-assoc x _ _  μ (mapₙ (_>>= id) x) )
ideal .Ideal.σ = _⋊ₙ_ s
ideal .Ideal.hom = funExt λ x  ( (s ⋊ₙ x) >>= id ) ≡⟨ sym (cong (_$ (s ⋊ₙ x)) μ-lemma) ; μ-⋊ s x  s ⋊ₙ (μ x)  
ideal .Ideal.natural f = funExt λ x 
  s ⋊ₙ mapₙ f x ≡⟨ mapₙ-⋊ₙ f s x 
  mapₙ f (s ⋊ₙ x) ≡⟨ cong (_$ (s ⋊ₙ x)) (map-lemma f) 
  mmap f (s ⋊ₙ x) 

cim : CIM Neighbours INeighbours neighboursMonad neighboursFunctor
cim .CIM.ideal = ideal
cim .CIM._‡ = solve
cim .CIM.solution eᵢ = funExt (solution eᵢ)
  ; cong₂ _∘_ μ-lemma (cong (_∘ (σ eᵢ  η  inr)  eᵢ) (map-lemma (solve eᵢ  η)))

open CIM cim

open ComMonadPlus neighboursMonadPlus

infixl 8 _∗
_∗ : (A  INeighbours A)  A  Neighbours A
g  = e† λ v  η (inr v) <|> η (inl (g v))
\end{code}