{-# OPTIONS --without-K --safe #-}
open import Categories.Category
open import Categories.Functor using (Functor; Endofunctor)

module Coalgebra.Recursive {o β„“ e} (π’ž : Category o β„“ e) (F : Endofunctor π’ž) where

private
  module π’ž = Category π’ž
  module F = Functor F

open import Level

open import Categories.Functor.Coalgebra
open import Categories.Functor.Algebra hiding (iterate)
open import Categories.Category using (Category)
open import Categories.Category.Construction.F-Algebras
open import Categories.Diagram.Cocone.Properties
open import Categories.Category.Construction.F-Coalgebras
open import Categories.Morphism using (IsIso; Iso; module β‰…; Retract)
import Categories.Morphism
open import Categories.Object.Initial using (IsInitial)
open import Categories.Morphism.Reasoning

open import F-Coalgebra-Colimit
open import Colimit-Lemmas


-- We first recap some lemmas from:
--   [CUV06] Venanzio Capretta, Tarmo Uustalu, and Varmo Vene.
--           Recursive coalgebras from comonads.
--           Inf. Comput., 204(4):437–468, 2006.

record C2A-morphism {o β„“ e} {π’ž : Category o β„“ e} {F : Endofunctor π’ž}
  (X : F-Coalgebra F)
  (Y : F-Algebra F) : Set (β„“ βŠ” e) where
  open Category π’ž
  module X = F-Coalgebra X
  module Y = F-Algebra Y
  open Functor F
  field
    f : X.A β‡’ Y.A
    commutes : f β‰ˆ Y.Ξ± ∘ F₁ f ∘ X.Ξ±

-- we can precompose solutions with coalgebra morphisms:
C2A-precompose : {B D : F-Coalgebra F} β†’ {A : F-Algebra F} β†’
  C2A-morphism D A β†’ F-Coalgebra-Morphism B D β†’ C2A-morphism B A
C2A-precompose {B} {D} {A} eval mor =
  let
    open Category π’ž
    module eval = C2A-morphism eval
    module mor = F-Coalgebra-Morphism mor
    module B = F-Coalgebra B
    module D = F-Coalgebra D
    module A = F-Algebra A
    open HomReasoning
    open Functor F
  in
  record
  { f = eval.f ∘ mor.f ;
  commutes = begin
    eval.f ∘ mor.f                     β‰ˆβŸ¨ eval.commutes ⟩∘⟨refl  ⟩
    (A.Ξ± ∘ F₁ eval.f ∘ D.Ξ±) ∘ mor.f    β‰ˆβŸ¨ assoc ⟩
    A.Ξ± ∘ (F₁ eval.f ∘ D.Ξ±) ∘ mor.f    β‰ˆβŸ¨ refl⟩∘⟨ assoc ⟩
    A.Ξ± ∘ F₁ eval.f ∘ D.Ξ± ∘ mor.f      β‰ˆβŸ¨ refl⟩∘⟨ refl⟩∘⟨ mor.commutes ⟩
    A.Ξ± ∘ F₁ eval.f ∘ F₁ mor.f ∘ B.Ξ±   β‰ˆβŸ¨ refl⟩∘⟨ sym-assoc ⟩
    A.Ξ± ∘ (F₁ eval.f ∘ F₁ mor.f) ∘ B.Ξ± β‰ˆΛ˜βŸ¨ refl⟩∘⟨ homomorphism ⟩∘⟨refl ⟩
    A.Ξ± ∘ F₁ (eval.f ∘ mor.f) ∘ B.Ξ±
          ∎
  }

record IsRecursive (X : F-Coalgebra F) : Set (o βŠ” β„“ βŠ” e) where
  morph = C2A-morphism.f
  field
    -- there is at least one solution:
    recur : (B : F-Algebra F) β†’ C2A-morphism X B
    -- there is at most one solution:
    unique : (B : F-Algebra F) β†’ (g h : C2A-morphism X B) β†’
      π’ž [ morph g β‰ˆ morph h ]


-- whenever a recursive coalgebra is an iso, it is the initial algebra:
-- [CUV06, Prop. 7(a)]
iso-recursive⇒initial :
  (R : F-Coalgebra F)
  β†’ IsRecursive R
  β†’ (r-iso : IsIso π’ž (F-Coalgebra.Ξ± R))
  β†’ IsInitial (F-Algebras F) (to-Algebra (IsIso.inv r-iso))
iso-recursive⇒initial R is-rec r-iso =
  let
    open Category π’ž
    open HomReasoning
    r⁻¹ = IsIso.inv r-iso
    r = F-Coalgebra.Ξ± R
  in
  record
  { ! = Ξ» {A : F-Algebra F} β†’
      let
        coalg2alg = IsRecursive.recur is-rec A
        a = F-Algebra.Ξ± A
        h : (F-Coalgebra.A R) β‡’ (F-Algebra.A A)
        h = C2A-morphism.f coalg2alg
        Fh = Functor.F₁ F h
      in
      record
        { f = h
        ; commutes = begin
          h ∘ r⁻¹
            β‰ˆβŸ¨  C2A-morphism.commutes coalg2alg ⟩∘⟨refl ⟩
          (a ∘ Fh ∘ r) ∘ r⁻¹   β‰ˆβŸ¨ assoc ⟩
          a ∘ ((Fh ∘ r) ∘ r⁻¹) β‰ˆβŸ¨ refl⟩∘⟨ assoc ⟩
          a ∘ Fh ∘ (r ∘ r⁻¹)
            β‰ˆΛ˜βŸ¨ assoc ⟩
          (a ∘ Fh) ∘ (r ∘ r⁻¹)
            β‰ˆβŸ¨ refl⟩∘⟨ Iso.isoΚ³ (IsIso.iso r-iso) ⟩
          (a ∘ Fh) ∘ id
            β‰ˆβŸ¨ identityΚ³ ⟩
          a ∘ Fh
          ∎
        }
  ; !-unique = Ξ» {A} g-hom β†’
    let
      g = (F-Algebra-Morphism.f g-hom)
      Fg = Functor.F₁ F g
      a = F-Algebra.Ξ± A
      -- we first show that 'g' is a coalg2algebra homomorphism
      g-coalg2alg : C2A-morphism R A
      g-coalg2alg = record {
        f = g ;
        commutes =
          begin
          g          β‰ˆΛ˜βŸ¨ identityΚ³ ⟩
          g ∘ id      β‰ˆΛ˜βŸ¨ refl⟩∘⟨ Iso.isoΛ‘ (IsIso.iso r-iso) ⟩
          g ∘ (r⁻¹ ∘ r) β‰ˆΛ˜βŸ¨ assoc ⟩
          (g ∘ r⁻¹) ∘ r β‰ˆβŸ¨ F-Algebra-Morphism.commutes g-hom ⟩∘⟨refl ⟩
          (a ∘ Fg) ∘ r β‰ˆβŸ¨ assoc ⟩
          a ∘ Fg ∘ r
          ∎
        }
      -- and thus, it has to be identical to h:
      h = IsRecursive.recur is-rec A
    in
    IsRecursive.unique is-rec A h g-coalg2alg
  }

-- ([CUV06, Prop. 5])
sandwich-recursive :
  βˆ€ (R B : F-Coalgebra F)
  β†’ IsRecursive R
  β†’ (h : F-Coalgebra-Morphism R B)
  β†’ (g : F-Coalgebra-Morphism B (iterate R))
  β†’ π’ž [ F-Coalgebra.Ξ± B β‰ˆ F.₁ (F-Coalgebra-Morphism.f h) π’ž.∘ (F-Coalgebra-Morphism.f g) ]
  β†’ IsRecursive B
sandwich-recursive R B R-is-rec h g equation =
  let
    module R = F-Coalgebra R
    module B = F-Coalgebra B
    module h = F-Coalgebra-Morphism h
    module g = F-Coalgebra-Morphism g
    open IsRecursive R-is-rec
    open Category π’ž
  in
  record {
  recur = Ξ» D β†’
    let
      -- for an F-algebra D, consider the induced solution by R:
      module D = F-Algebra D
      R2D = recur D
      module R2D = C2A-morphism R2D
      -- use this under the functor to get a solution from B to D:
      sol = D.Ξ± ∘ F.₁ R2D.f ∘ g.f
      open HomReasoning
    in
    record {
    f = sol;
    commutes =
        -- in the following, the only non-trivial steps are R2D.commutes and g.commutes
        begin
        sol                                            β‰‘βŸ¨βŸ©
        D.Ξ± ∘ F.₁ R2D.f ∘ g.f                          β‰ˆβŸ¨ refl⟩∘⟨ F.F-resp-β‰ˆ R2D.commutes ⟩∘⟨refl ⟩
        D.Ξ± ∘ F.₁ (D.Ξ± ∘ F.₁ R2D.f ∘ R.Ξ±) ∘ g.f        β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F.F-resp-β‰ˆ assoc ⟩∘⟨refl ⟩
        D.Ξ± ∘ F.₁ ((D.Ξ± ∘ F.₁ R2D.f) ∘ R.Ξ±) ∘ g.f      β‰ˆβŸ¨ refl⟩∘⟨ F.homomorphism ⟩∘⟨refl ⟩
        D.Ξ± ∘ (F.₁ (D.Ξ± ∘ F.₁ R2D.f) ∘ F.₁ R.Ξ±) ∘ g.f  β‰ˆβŸ¨ refl⟩∘⟨ assoc ⟩
        D.Ξ± ∘ F.₁ (D.Ξ± ∘ F.₁ R2D.f) ∘ F.₁ R.Ξ± ∘ g.f    β‰ˆβŸ¨ refl⟩∘⟨ refl⟩∘⟨ g.commutes ⟩
        D.Ξ± ∘ F.₁ (D.Ξ± ∘ F.₁ R2D.f) ∘ F.₁ g.f ∘ B.Ξ±    β‰ˆΛ˜βŸ¨ refl⟩∘⟨ assoc ⟩
        D.Ξ± ∘ (F.₁ (D.Ξ± ∘ F.₁ R2D.f) ∘ F.₁ g.f) ∘ B.Ξ±  β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F.homomorphism ⟩∘⟨refl ⟩
        D.Ξ± ∘ F.₁ ((D.Ξ± ∘ F.₁ R2D.f) ∘ g.f) ∘ B.Ξ±      β‰ˆβŸ¨ refl⟩∘⟨ F.F-resp-β‰ˆ assoc ⟩∘⟨refl ⟩
        D.Ξ± ∘ F.₁ (D.Ξ± ∘ F.₁ R2D.f ∘ g.f) ∘ B.Ξ±        β‰‘βŸ¨βŸ©
        D.Ξ± ∘ F.₁ sol ∘ B.Ξ±
        ∎
    } ;
  unique = Ξ» D sol1 sol2 β†’
    let
      module D = F-Algebra D
      module sol1 = C2A-morphism sol1
      module sol2 = C2A-morphism sol2
      open HomReasoning
      -- first of all, the solutions are equal when precomposed with 'h: R -> B':
      sol1-h-is-sol2-h : sol1.f ∘ h.f β‰ˆ sol2.f ∘ h.f
      sol1-h-is-sol2-h =
        IsRecursive.unique R-is-rec D
           (C2A-precompose sol1 h)
           (C2A-precompose sol2 h)

      -- this is essentially the reasoning: we do it forward for sol1 and
      -- backwards for sol2.
      sol-transformation sol =
        let
          module sol = C2A-morphism sol
        in
        begin
        sol.f                              β‰ˆβŸ¨ sol.commutes ⟩
        D.Ξ± ∘ F.₁ sol.f ∘ B.Ξ±              β‰ˆβŸ¨ refl⟩∘⟨ refl⟩∘⟨ equation ⟩
        D.Ξ± ∘ F.₁ sol.f ∘ F.₁ h.f ∘ g.f    β‰ˆβŸ¨ refl⟩∘⟨ sym-assoc ⟩
        D.Ξ± ∘ (F.₁ sol.f ∘ F.₁ h.f) ∘ g.f  β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F.homomorphism ⟩∘⟨refl ⟩
        D.Ξ± ∘ F.₁ (sol.f ∘ h.f) ∘ g.f
        ∎
    in
    begin
    sol1.f                          β‰ˆβŸ¨ sol-transformation sol1 ⟩
    D.Ξ± ∘ F.₁ (sol1.f ∘ h.f) ∘ g.f  β‰ˆβŸ¨ refl⟩∘⟨ F.F-resp-β‰ˆ sol1-h-is-sol2-h ⟩∘⟨refl ⟩
    D.Ξ± ∘ F.₁ (sol2.f ∘ h.f) ∘ g.f  β‰ˆΛ˜βŸ¨ sol-transformation sol2 ⟩
    sol2.f
    ∎
  }


-- Corollary: If (R,r) is recursive, then so is (FR,Fr) ([CUV06, Prop. 6]).
iterate-recursive : (R : F-Coalgebra F)
                    β†’ IsRecursive R
                    β†’ IsRecursive (iterate R)
iterate-recursive R is-rec =
  sandwich-recursive R (iterate R) is-rec r (Category.id (F-Coalgebras F)) equation
  where
    module R = F-Coalgebra R
    module FR = F-Coalgebra (iterate R)
    r : F-Coalgebra-Morphism R (iterate R)
    r = record { f = R.Ξ± ; commutes = π’ž.Equiv.refl }

    equation : π’ž [ FR.Ξ± β‰ˆ F.₁ R.Ξ± π’ž.∘ π’ž.id ]
    equation = π’ž.Equiv.sym π’ž.identityΚ³

-- the functor sends coalgebra morphisms to coalgebra morphisms:
iterate-F-Coalgebra-Morphism : {A B : F-Coalgebra F}
  β†’ (h : F-Coalgebra-Morphism A B)
  β†’ F-Coalgebra-Morphism (iterate A) (iterate B)
iterate-F-Coalgebra-Morphism {A} {B} h =
  record {
    f = F.₁ h.f ; commutes = begin
      F.₁ Ξ² ∘ F.₁ h.f         β‰ˆΛ˜βŸ¨ F.homomorphism ⟩
      F.₁ (Ξ² ∘ h.f)           β‰ˆβŸ¨ F.F-resp-β‰ˆ h.commutes ⟩
      F.₁ (F.₁ h.f ∘ Ξ±)       β‰ˆβŸ¨ F.homomorphism ⟩
      F.₁ (F.₁ h.f) ∘ F.₁ Ξ±
      ∎}
  where
    module h = F-Coalgebra-Morphism h
    open F-Coalgebra A
    open F-Coalgebra B renaming (A to B; Ξ± to Ξ²)
    open Category π’ž
    open HomReasoning


record R-Coalgebra : Set (o βŠ” β„“ βŠ” e) where
  field
    coalg : F-Coalgebra F
    ump : IsRecursive coalg
  open F-Coalgebra coalg public
  open IsRecursive ump public

-- The recursive coalgebras form a full subcategory of F-Coalgebras:
R-Coalgebras : Category (β„“ βŠ” o βŠ” e) (e βŠ” β„“) e
R-Coalgebras = FullSubCategory (F-Coalgebras F) R-Coalgebra.coalg
  where
    open import Categories.Category.SubCategory using (FullSubCategory)


module _ where

  open import Categories.Functor.Construction.SubCategory
  forget-rec : Functor (R-Coalgebras) (F-Coalgebras F)
  forget-rec = FullSub (F-Coalgebras F)

open import Categories.Diagram.Colimit using (Colimit)
open import Categories.Diagram.Cocone
open import Categories.Functor using (_∘F_)

module _ {o' β„“' e' : Level} {π’Ÿ : Category o' β„“' e'} (J : Functor π’Ÿ (F-Coalgebras F)) where
  private
    module π’Ÿ = Category π’Ÿ
    module J = Functor J


  Limitting-Cocone-IsRecursive :
        (βˆ€ (i : π’Ÿ.Obj) β†’ IsRecursive (J.β‚€ i))
        -- ^- if all coalgebras in the diagram are recursive
      β†’ (cocone : Cocone J)
        -- ^- if we have a cocone in coalgebras
      β†’ IsLimitting (F-map-CoconeΛ‘ forget-Coalgebra cocone)
        -- ^- which is limitting in the base category
      β†’ IsRecursive (Cocone.N cocone)
        -- ^- then the tip of the cocone is also recursive
  Limitting-Cocone-IsRecursive all-recursive cocone limitting =
    -- we convert Cocone-Morphisms and C2A-morphisms back and forth:
    -- we write 'sol' (short for 'solution') for the unique C2A-morphisms.
    record { recur = λ B → cocone⇒-to-sol B (limitting.! {alg2cocone B})
           ; unique = λ B g h → limitting.!-unique₂ (sol-to-cocone⇒ B g) (sol-to-cocone⇒ B h)
           }
    where
      open Category π’ž
      open HomReasoning
      module cocone = Cocone cocone
      module limitting = IsInitial limitting
      obj-cocone = (F-map-CoconeΛ‘ forget-Coalgebra cocone)
      module obj-cocone = Cocone obj-cocone

      -- every algebra induces a cocone of the unique C2A-morphisms:
      alg2cocone : F-Algebra F β†’ Cocone (forget-Coalgebra ∘F J)
      alg2cocone B =
        let module B = F-Algebra B in
        record { coapex = record {
          ψ = Ξ» i β†’ C2A-morphism.f (IsRecursive.recur (all-recursive i) B) ;
          commute = Ξ» {i} {i'} h β†’
            let
              sol1 = IsRecursive.recur (all-recursive i) B
              sol2 = C2A-precompose (IsRecursive.recur (all-recursive i') B) (J.₁ h)
            in
            -- the triangles of the cocone commute because of uniqueness of C2A-morphisms:
            IsRecursive.unique (all-recursive i) B sol2 sol1 } }

      -- every Cocone-Morphism induces a C2A-morphism
      cocone⇒-to-sol : (B : F-Algebra F)
                  β†’ Coconeβ‡’ (forget-Coalgebra ∘F J) obj-cocone (alg2cocone B)
                  β†’ C2A-morphism cocone.N B
      cocone⇒-to-sol B mor = let
          module B = F-Algebra B
          module mor = Cocone⇒ mor
        in
        record { f = mor.arr ; commutes = limitting-cocone-is-jointly-epic obj-cocone limitting (Ξ» i β†’
          let
            sol = IsRecursive.recur (all-recursive i) B
            module sol = C2A-morphism sol
          in
          begin
          mor.arr ∘ obj-cocone.ψ i β‰ˆβŸ¨ mor.commute {i} ⟩
          sol.f  β‰ˆβŸ¨ sol.commutes ⟩
          B.Ξ± ∘ F.F₁ sol.f ∘ F-Coalgebra.Ξ± (J.β‚€ i)  β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F.F-resp-β‰ˆ mor.commute ⟩∘⟨refl ⟩
          B.Ξ± ∘ F.F₁ (mor.arr ∘ obj-cocone.ψ i) ∘ F-Coalgebra.Ξ± (J.β‚€ i)  β‰ˆβŸ¨ refl⟩∘⟨ pushΛ‘ π’ž F.homomorphism ⟩
          B.Ξ± ∘ F.F₁ mor.arr ∘ F.₁ (obj-cocone.ψ i) ∘ F-Coalgebra.Ξ± (J.β‚€ i)  β‰ˆΛ˜βŸ¨ refl⟩∘⟨ refl⟩∘⟨ F-Coalgebra-Morphism.commutes (cocone.ψ i) ⟩
          B.Ξ± ∘ F.F₁ mor.arr ∘ F-Coalgebra.Ξ± cocone.N ∘ obj-cocone.ψ i  β‰ˆΛ˜βŸ¨ (assoc β—‹ (refl⟩∘⟨ assoc)) ⟩
          (B.Ξ± ∘ F.F₁ mor.arr ∘ F-Coalgebra.Ξ± cocone.N) ∘ obj-cocone.ψ i
          ∎) }

      -- And conversely, every C2A-Morphism induces a every Cocone-Morphism
      sol-to-cocone⇒ : (B : F-Algebra F) → C2A-morphism cocone.N B
                  β†’ Coconeβ‡’ (forget-Coalgebra ∘F J) obj-cocone (alg2cocone B)
      sol-to-cocone⇒ B sol = let
          module B = F-Algebra B
          module sol = C2A-morphism sol
        in record
        { arr = sol.f
        ; commute = Ξ» {i} β†’
            IsRecursive.unique (all-recursive i) B
            (C2A-precompose sol (cocone.ψ i))
            (IsRecursive.recur (all-recursive i) B)
        }


R-Coalgebras-Colimit : {o' β„“' e' : Level} β†’ {D : Category o' β„“' e'} β†’ (J : Functor D R-Coalgebras)
        β†’ Colimit (forget-Coalgebra ∘F forget-rec ∘F  J) β†’ Colimit J
R-Coalgebras-Colimit J π’ž-colim =
  FullSub-Colimit R-Coalgebra.coalg J Coalg-colim R (β‰….refl (F-Coalgebras F))
  where
    module J = Functor J
    module π’ž-colim = Colimit π’ž-colim
    Coalg-colim : Colimit (forget-rec ∘F J)
    Coalg-colim = F-Coalgebras-Colimit _ π’ž-colim
    module Coalg-colim = Colimit Coalg-colim

    -- every F-Algebra induces a competing cocone for the above colimit:
    alg2cocone : F-Algebra F β†’ Cocone (forget-Coalgebra ∘F forget-rec ∘F  J)
    alg2cocone B =
      let
            module B = F-Algebra B
      in
      record {
        N = B.A ;
        coapex = record {
          ψ = Ξ» R β†’
            let
              module R = R-Coalgebra (J.Fβ‚€ R)
            in
            C2A-morphism.f (R.recur B) ;
          commute = Ξ» {R} {R'} h β†’
            let
              -- h is a coalg-hom from R to R':
              module R = R-Coalgebra (J.Fβ‚€ R)
              module R' = R-Coalgebra (J.Fβ‚€ R')
              open Category π’ž
              open HomReasoning
              open Equiv
              module h = F-Coalgebra-Morphism (J.F₁ h)
              module U = Functor (forget-Coalgebra ∘F forget-rec ∘F J)
              module U' = Functor (forget-rec ∘F J)
              -- we can use it to construct another solution from R to B:
              sol : C2A-morphism R.coalg B
              sol =
                let
                  module r' = C2A-morphism (R'.recur B)
                in
                record {
                f = r'.f ∘ U.F₁ h;
                commutes =
                begin
                r'.f ∘ U.F₁ h β‰ˆβŸ¨ r'.commutes ⟩∘⟨refl ⟩
                (B.Ξ± ∘ (F.F₁ r'.f ∘ R'.Ξ±)) ∘ U.F₁ h β‰ˆβŸ¨ assoc ⟩
                B.Ξ± ∘ ((F.F₁ r'.f ∘ R'.Ξ±) ∘ U.F₁ h) β‰ˆβŸ¨ refl⟩∘⟨ assoc ⟩
                B.Ξ± ∘ (F.F₁ r'.f ∘ (R'.Ξ± ∘ U.F₁ h)) β‰ˆβŸ¨ refl⟩∘⟨ refl⟩∘⟨ h.commutes ⟩
                B.Ξ± ∘ (F.F₁ r'.f ∘ (F.F₁ (U.F₁ h) ∘ R.Ξ±)) β‰ˆβŸ¨ refl⟩∘⟨ sym-assoc ⟩
                B.Ξ± ∘ ((F.F₁ r'.f ∘ F.F₁ (U.F₁ h)) ∘ R.Ξ±) β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F.homomorphism ⟩∘⟨refl ⟩
                B.Ξ± ∘ F.F₁ (r'.f ∘ U.F₁ h) ∘ R.Ξ±
                ∎
                }
            in
            R.unique B sol (R.recur B)
        } }
    --
    -- the induced solution for an algebra
    alg2solution : (B : F-Algebra F) β†’ C2A-morphism Coalg-colim.coapex B
    alg2solution B =
      let
        module B = F-Algebra B
        open Category π’ž
        open HomReasoning

        sol : π’ž [ F-Coalgebra.A Coalg-colim.coapex , B.A ]
        sol = π’ž-colim.rep (alg2cocone B)
      in
      record { f = sol ;
        commutes = colimit-is-jointly-epic π’ž-colim Ξ» R β†’
            let
              module R = R-Coalgebra (J.Fβ‚€ R)
              module R-sol = C2A-morphism (R.recur B)
            in
            begin
            sol ∘ π’ž-colim.proj R
              β‰ˆβŸ¨ π’ž-colim.commute ⟩
            R-sol.f
              β‰ˆβŸ¨ R-sol.commutes ⟩
            B.Ξ± ∘ F.F₁ R-sol.f ∘ R.Ξ±
              β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F.F-resp-β‰ˆ π’ž-colim.commute ⟩∘⟨refl ⟩
            B.Ξ± ∘ F.F₁ (sol ∘ π’ž-colim.proj R) ∘ R.Ξ±
              β‰ˆβŸ¨ refl⟩∘⟨ F.homomorphism ⟩∘⟨refl ⟩
            B.Ξ± ∘ (F.F₁ sol ∘ F.F₁ (π’ž-colim.proj R)) ∘ R.Ξ±
              β‰ˆβŸ¨ refl⟩∘⟨ assoc ⟩
            B.Ξ± ∘ F.F₁ sol ∘ (F.F₁ (π’ž-colim.proj R) ∘ R.Ξ±)
              β‰ˆΛ˜βŸ¨ refl⟩∘⟨ refl⟩∘⟨ F-Coalgebra-Morphism.commutes (Coalg-colim.proj R) ⟩
            B.Ξ± ∘ F.F₁ sol ∘ F-Coalgebra.Ξ± Coalg-colim.coapex ∘ π’ž-colim.proj R
              β‰ˆΛ˜βŸ¨ refl⟩∘⟨  assoc ⟩
            B.Ξ± ∘ (F.F₁ sol ∘ F-Coalgebra.Ξ± Coalg-colim.coapex) ∘ π’ž-colim.proj R
              β‰ˆΛ˜βŸ¨  assoc ⟩
            (B.Ξ± ∘ F.F₁ sol ∘ F-Coalgebra.Ξ± Coalg-colim.coapex) ∘ π’ž-colim.proj R
            ∎
          }

    -- we can then show that the colimit coalgebra must be recursive:
    R : R-Coalgebra
    R = record {
      coalg = Coalg-colim.coapex ;
      ump = record {
        recur = alg2solution;
        unique = Ξ» B g h β†’
          colimit-is-jointly-epic π’ž-colim Ξ» R β†’
            let
              open Category π’ž
              open HomReasoning
              module B = F-Algebra B
              module R = R-Coalgebra (J.Fβ‚€ R)
              -- we need to show that every solution in the colim induces a solution of R
              proj-sol : C2A-morphism Coalg-colim.coapex B β†’ C2A-morphism R.coalg B
              proj-sol s =
                let module s = C2A-morphism s in
                record {
                f = s.f ∘ π’ž-colim.proj R ;
                commutes =
                  begin
                  s.f ∘ π’ž-colim.proj R
                    β‰ˆβŸ¨ s.commutes ⟩∘⟨refl ⟩
                  (B.Ξ± ∘ F.F₁ s.f ∘ F-Coalgebra.Ξ± Coalg-colim.coapex) ∘ π’ž-colim.proj R
                    β‰ˆβŸ¨ assoc ⟩
                  B.Ξ± ∘ ((F.F₁ s.f ∘ F-Coalgebra.Ξ± Coalg-colim.coapex) ∘ π’ž-colim.proj R)
                    β‰ˆβŸ¨ refl⟩∘⟨ assoc ⟩
                  B.Ξ± ∘ F.F₁ s.f ∘ F-Coalgebra.Ξ± Coalg-colim.coapex ∘ π’ž-colim.proj R
                    β‰ˆβŸ¨ refl⟩∘⟨ refl⟩∘⟨ F-Coalgebra-Morphism.commutes (Coalg-colim.proj R) ⟩
                  B.Ξ± ∘ F.F₁ s.f ∘ F.F₁ (π’ž-colim.proj R) ∘ R.Ξ±
                    β‰ˆΛ˜βŸ¨ refl⟩∘⟨ assoc ⟩
                  B.Ξ± ∘ (F.F₁ s.f ∘ F.F₁ (π’ž-colim.proj R)) ∘ R.Ξ±
                    β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F.homomorphism ⟩∘⟨refl ⟩
                  B.Ξ± ∘ F.F₁ (s.f ∘ π’ž-colim.proj R) ∘ R.Ξ±
                  ∎
                }
            in
            R.unique B (proj-sol g) (proj-sol h)
        } }


retract-coalgebra : (X : F-Coalgebra F) {Y : π’ž.Obj}
  β†’ Retract π’ž (F-Coalgebra.A X) Y
  β†’ F-Coalgebra F
retract-coalgebra X {Y} r = record { A = Y ; Ξ± = F₁ r.section ∘ X.Ξ± ∘ r.retract }
  where
    open Functor F
    open Category π’ž
    module r = Retract r
    module X = F-Coalgebra X


retract-coalgebra-hom : (X : F-Coalgebra F) {Y : π’ž.Obj}
  β†’ (r : Retract π’ž (F-Coalgebra.A X) Y)
  β†’ F-Coalgebras F [ X , retract-coalgebra X r ]
retract-coalgebra-hom X {Y} r =
  record { f = r.section ; commutes = begin
    (F₁ r.section ∘ X.Ξ± ∘ r.retract) ∘ r.section β‰ˆβŸ¨ assocΒ²' π’ž ⟩
    F₁ r.section ∘ X.Ξ± ∘ r.retract ∘ r.section β‰ˆβŸ¨ refl⟩∘⟨ elimΚ³ π’ž r.is-retract ⟩
    F₁ r.section ∘ X.Ξ±
    ∎}
  where
    open Functor F
    open Category π’ž
    open HomReasoning
    module r = Retract r
    module X = F-Coalgebra X

retract-coalgebra-hom⁻¹ : (X : F-Coalgebra F) {Y : π’ž.Obj}
  β†’ (r : Retract π’ž (F-Coalgebra.A X) Y)
  β†’ F-Coalgebras F [ retract-coalgebra X r , X ]
retract-coalgebra-hom⁻¹ X {Y} r =
  record { f = r.retract ; commutes = begin
    X.Ξ± ∘ r.retract β‰ˆΛ˜βŸ¨ pullΛ‘ π’ž (elimΛ‘ π’ž (F-resp-β‰ˆ r.is-retract β—‹ identity)) ⟩
    F₁ (r.retract ∘ r.section) ∘ X.Ξ± ∘ r.retract β‰ˆβŸ¨ pushΛ‘ π’ž homomorphism ⟩
    F₁ r.retract ∘ F₁ r.section ∘ X.Ξ± ∘ r.retract
    ∎}
  where
    open Functor F
    open Category π’ž
    open HomReasoning
    module r = Retract r
    module X = F-Coalgebra X

retract-coalgebra-hom-to-iterate : (X : F-Coalgebra F) {Y : π’ž.Obj}
  β†’ (r : Retract π’ž (F-Coalgebra.A X) Y)
  β†’ F-Coalgebras F [ retract-coalgebra X r , (iterate X) ]
retract-coalgebra-hom-to-iterate X {Y} r =
  record { f = X.α ∘ r.retract ; commutes =
    begin
    F₁ X.Ξ± ∘ X.Ξ± ∘ r.retract
      β‰ˆΛ˜βŸ¨ refl⟩∘⟨ elimΛ‘ π’ž identity ⟩
    F₁ X.Ξ± ∘ F₁ id ∘ X.Ξ± ∘ r.retract
      β‰ˆΛ˜βŸ¨ refl⟩∘⟨ F-resp-β‰ˆ r.is-retract ⟩∘⟨refl  ⟩
    F₁ X.Ξ± ∘ F₁ (r.retract ∘ r.section) ∘ X.Ξ± ∘ r.retract
      β‰ˆβŸ¨ refl⟩∘⟨ pushΛ‘ π’ž homomorphism ⟩
    F₁ X.Ξ± ∘ F₁ r.retract ∘ F₁ r.section ∘ X.Ξ± ∘ r.retract
      β‰ˆΛ˜βŸ¨ pushΛ‘ π’ž homomorphism ⟩
    F₁ (X.Ξ± ∘ r.retract) ∘ F₁ r.section ∘ X.Ξ± ∘ r.retract
    ∎
  }
  where
    open Functor F
    open Category π’ž
    open HomReasoning
    module r = Retract r
    module X = F-Coalgebra X

retract-coalgebra-recursive : (X : F-Coalgebra F) {Y : π’ž.Obj}
  β†’ (r : Retract π’ž (F-Coalgebra.A X) Y)
  β†’ IsRecursive X
  β†’ IsRecursive (retract-coalgebra X r)
retract-coalgebra-recursive X {Y} r X-rec =
  sandwich-recursive X (retract-coalgebra X r) X-rec
    (retract-coalgebra-hom X r)
    (retract-coalgebra-hom-to-iterate X r) π’ž.Equiv.refl
  where
    open Functor F
    open Category π’ž
    open HomReasoning
    module r = Retract r
    module X = F-Coalgebra X