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

open import Prelude
open import Algebra

open import Path.Reasoning

module Codata.CIM where

private variable X : Type

module _ (M  : Type  Type)
  (mon : Monad M)
  (fnc : Functor )
  where

  open Monad mon
  open Functor fnc

  μ : M (M A)  M A
  μ xs = xs >>= id

  η = return

  M[_] : (A  B)  M A  M B
  M[_] = mmap

  M̅[_] : (A  B)   A   B
  M̅[_] = map

  μ∘η : μ  η {A = M A}  id
  μ∘η = funExt (>>=-idˡ _)

  η∘Mμ : μ  M[ η {A = A} ]  id
  η∘Mμ = funExt λ x  >>=-assoc x _ _ ; cong (x >>=_) (funExt λ x′  >>=-idˡ _ (η x′)) ; >>=-idʳ x

  η∘f : (f : A  B)  η  f  M[ f ]  η
  η∘f f = funExt λ x  sym (>>=-idˡ _ _)

  f∘μ : (f : A  B)  μ  M[ M[ f ] ]  M[ f ]  μ
  f∘μ f = funExt λ x  >>=-assoc x _ _ ; cong (x >>=_) (funExt  x′  >>=-idˡ _ _)) ; sym (>>=-assoc x _ _)

  μ-assoc : μ  μ  μ  M[ μ { A = A } ]
  μ-assoc = funExt λ x  >>=-assoc x _ _ ; cong (x >>=_) (funExt λ x′  sym (>>=-idˡ _ _)) ; sym (>>=-assoc x _ _)

  M[]-comp : (f : B  C) (g : A  B)  M[ f ]  M[ g ]  M[ f  g ]
  M[]-comp f g = funExt (mmap-comp f g)

  μ-M : (f : A  M (M B))  μ  μ  M[ f ]  μ  M[ μ  f ]
  μ-M f =
    μ  μ  M[ f ] ≡⟨ cong (_∘ M[ f ]) μ-assoc 
    μ  M[ μ ]  M[ f ] ≡⟨ cong (μ ∘_) (M[]-comp μ f) 
    μ  M[ μ  f ] 
    
  record Module : Type₁ where
    field
      μ′ :  (M A)   A
      μ-η : μ′  M̅[ η ]  id {A =  A}
      μ-μ : μ′  μ′ {A = M A}  μ′  M̅[ μ ]

  record Ideal : Type₁ where
    field
      mod : Module
    open Module mod public
    field
      σ :  A  M A
      hom : μ  σ  σ  μ′ {A = A}
      natural : (f : A  B)  σ  M̅[ f ]  M[ f ]  σ

  Equation : Type  Type  Type
\end{code}
%<*equation>
\begin{code}
  Equation X A = X  M (X  A)
\end{code}
%</equation>
\begin{code}
  Solution : Type  Type  Type
\end{code}
%<*soln>
\begin{code}
  Solution X A = X  M A
\end{code}
%</soln>
\begin{code}

  infix 4 _Solves_
\end{code}
%<*solves>
\begin{code}
  _Solves_ : Solution X A  Equation X A  Type
  e† Solves e = e†  μ  M[ e†  η ]  e
\end{code}
%</solves>
\begin{code}

  Flat : Type  Type  Type
\end{code}
%<*flat>
\begin{code}
  Flat X A = X   (X  A)  A
\end{code}
%</flat>
\begin{code}

  record CIM : Type₁ where
    field ideal : Ideal
    open Ideal ideal public

    infixl 30 _‡
    field
\end{code}
%<*solve>
\begin{code}
      _‡ : Flat X A  Solution X A
\end{code}
%</solve>
%<*solution>
\begin{code}
      solution :  (eᵢ : Flat X A) 
                  eᵢ  Solves (σ  η  inr)  eᵢ
\end{code}
%</solution>
\begin{code}
    Flatᵍ : Type  Type  Type
    Flatᵍ X A = X  M ( X  A)

    module _ (eⱼ : Flatᵍ X A) where
      e : Equation X A
      e = μ  M[ M[ inl ]  σ  η  inr ]  eⱼ

      ẽᵢ : Flat ( X) A
      ẽᵢ = inl  μ′  M̅[ eⱼ ]

       : Equation ( X) A
       = (σ  η  inr)  ẽᵢ

      _ :   σ  μ′  M̅[ eⱼ ]
      _ = refl

      ẽ† : Solution ( X) A
      ẽ† = ẽᵢ 

      e† : Solution X A
      e† = μ  M[ ẽ†  η ]  eⱼ

      lemma : μ  M[ ẽ†  η ]    μ  M[ e† ]  σ
      lemma =
        μ  M[ ẽ†  η ]                       ≡⟨⟩
        μ  M[ ẽ†  η ]  σ  μ′  M̅[ eⱼ ]      ≡˘⟨ cong  s  μ  M[ ẽ†  η ]  s  M̅[ eⱼ ]) hom  
        μ  M[ ẽ†  η ]  μ  σ  M̅[ eⱼ ]       ≡⟨ cong  s  μ  M[ ẽ†  η ]  μ  s) (natural eⱼ) 
        μ  M[ ẽ†  η ]  μ  M[ eⱼ ]  σ       ≡˘⟨ cong  s  μ  s  M[ eⱼ ]  σ) (f∘μ (ẽ†  η) ) 
        μ  μ  M[ M[ ẽ†  η ] ]  M[ eⱼ ]  σ  ≡⟨ cong  s  μ  μ  s  σ) (M[]-comp M[ ẽ†  η ] eⱼ) 
        μ  μ  M[ M[ ẽ†  η ]  eⱼ ]  σ       ≡⟨ cong (_∘ σ) (μ-M _) 
        μ  M[ μ  M[ ẽ†  η ]  eⱼ ]  σ       ≡⟨⟩
        μ  M[ e† ]  σ 

      soln : e† Solves e
      soln =
        e†                                                                      ≡⟨⟩
        μ  M[ ẽ†  η ]  eⱼ                                                    ≡⟨ cong  s  μ  M[ s  η ]  eⱼ) (solution ẽᵢ) 
        μ  M[ μ  M[ ẽ†  η ]    η ]  eⱼ                                   ≡⟨ cong  s  μ  M[ s  η ]  eⱼ) lemma 
        μ  M[ μ  M[ e† ]  σ  η ]  eⱼ                                       ≡˘⟨ cong  s  μ  M[ μ  M[ e† ]  σ  s  η ]  eⱼ ) μ∘η 
        μ  M[ μ  M[ e† ]  σ  μ  η  η ]  eⱼ                               ≡⟨ cong  s  μ  M[ s ]  eⱼ) (funExt (▿-fusion μ _ _)) 
        μ  M[ μ  (M[ e† ]  σ  η  η) ]  eⱼ                                 ≡˘⟨ cong (_∘ eⱼ) (μ-M (M[ e† ]  σ  η  η)) 
        μ  μ  M[ M[ e† ]  σ  η  η  ]  eⱼ                                  ≡⟨⟩
        μ  μ  M[ M[ (e†  η)  inl ]  σ  η  η ]  eⱼ                       ≡˘⟨ cong  s  μ  μ  M[ s  σ  η  η ]  eⱼ) (M[]-comp (e†  η) inl) 
        μ  μ  M[ M[ e†  η ]  M[ inl ]  σ  η  η ]  eⱼ                    ≡⟨⟩
        μ  μ  M[ M[ e†  η ]  M[ inl ]  σ  η  (e†  η)  inr ]  eⱼ       ≡⟨ cong  s  μ  μ  M[ M[ e†  η ]  M[ inl ]  σ  s  inr ]  eⱼ) (η∘f (e†  η)) 
        μ  μ  M[ M[ e†  η ]  M[ inl ]  σ  M[ e†  η ]  η  inr ]  eⱼ    ≡⟨ cong  s  μ  μ  M[ s ]  eⱼ) (funExt (▿-fusion M[ e†  η ] _ _)) 
        μ  μ  M[ M[ e†  η ]  (M[ inl ]  σ  η  inr) ]  eⱼ                ≡˘⟨ cong  s  μ  μ  s  eⱼ) (M[]-comp M[ e†  η ] (M[ inl ]  σ  η  inr)) 
        μ  μ  M[ M[ e†  η ] ]  M[ M[ inl ]  σ  η  inr ]  eⱼ             ≡⟨ cong  s  μ  s  M[ M[ inl ]  σ  η  inr ]  eⱼ) (f∘μ (e†  η)) 
        μ  M[ e†  η ]  μ       M[ M[ inl ]  σ  η  inr ]  eⱼ             ≡⟨⟩
        μ  M[ e†  η ]  e 
\end{code}