open import Categories.Category.Core
open import Categories.Category.Distributive
open import Categories.Category.Restriction
open import Categories.Object.Terminal
open import Relation.Binary.Bundles using (Poset)
open import Monad.Helper
open import Data.Product using (_,_)
open import Algebra.Elgot
open import Category.Construction.ElgotAlgebras
open import Categories.FreeObjects.Free
open import Categories.Object.NaturalNumbers
open import Categories.Object.NaturalNumbers.Parametrized
open import Categories.Category.Construction.F-Algebras

import Categories.Morphism as M
import Categories.Morphism.Reasoning as MR
import Categories.Morphism.Properties as MP

import Monad.Instance.K as MIK
-- The kleisli category of K is enriched over pointed partial orders and strict monotone maps.

module Monad.Instance.K.OrderEnriched {o  e} {C : Category o  e} (distributive : Distributive C) (MK : MIK.MonadK distributive) where
  open import Category.Distributive.Helper distributive
  open Bundles
  
  open MIK distributive
  open Terminal terminal using (!; !-unique; !-unique₂; )
  open MonadK MK
  open Category C
  open Equiv
  open HomReasoning
  open M C
  open MR C
  open MP C
  open kleisliK using (extend; extend-≈)
  open monadK using (μ)
  open import Monad.Instance.K.Strong distributive MK
  open import Monad.Instance.K.Commutative distributive MK
  open import Monad.Instance.K.EquationalLifting distributive MK
  open import Monad.Instance.K.Restriction distributive MK
  open strongK using (strengthen; strength-assoc)
  open Restriction KRestriction renaming (_↓ to _↓'; ↓-denestʳ to ↓-denestʳ'; ↓-cong to ↓-cong'; pidʳ to pidʳ'; ↓-comm to ↓-comm'; ↓-skew-comm to ↓-skew-comm')
  open import Categories.Category.Restriction.Properties.Poset KRestriction
  open import Categories.Category.Restriction.Properties KRestriction renaming (↓-idempotent to ↓-idempotent')
  open import Categories.Object.NaturalNumbers.Parametrized.Properties.F-Algebras C cartesian coproducts
  open import Categories.Monad.Commutative.Properties

  open CommutativeProperties braided KCommutativeMonad
 
  -- NOTE: f ↓' ≈ extend (η _ ∘ π₂) ∘ extend (ψ ∘ (η _ ∘ ! ⁂ η _)) ∘ extend (ψ ∘ (f ⁂ η _)) ∘ η _ ∘ ⟨ id , id ⟩

  _↓ :  {X Y} (f : X  K.₀ Y)  X  K.₀ X
  f  = K.₁ π₁  τ _   id , f 

  ↓-charac :  {X Y} {f : X  K.₀ Y}  f ↓'  f 
  ↓-charac {f = f} = begin 
    extend (η _  π₂)  extend (ψ  (η _  !  η _))  extend (ψ  (f  η _))  η _   id , id  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ pullˡ kleisliK.identityʳ  
    extend (η _  π₂)  extend (ψ  (η _  !  η _))  (ψ  (f  η _))   id , id               ≈⟨ refl⟩∘⟨ (extend-≈ (∘-resp-≈ʳ (sym (⁂∘⁂  ⁂-cong₂ refl identityʳ))) ⟩∘⟨refl)  
    extend (η _  π₂)  extend (ψ  (η _  η _)  (!  id))  (ψ  (f  η _))   id , id        ≈⟨ refl⟩∘⟨ (extend-≈ (pullˡ ψ-η) ⟩∘⟨refl)  
    extend (η _  π₂)  extend (η _  (!  id))  (ψ  (f  η _))   id , id                    ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ψ-τ' ⟩∘⟨refl  
    extend (η _  π₂)  extend (η _  (!  id))  (σ _  (f  id))   id , id                   ≈⟨ refl⟩∘⟨ refl⟩∘⟨ pullʳ ⁂∘Δ  
    extend (η _  π₂)  extend (η _  (!  id))  σ _   f , id                                 ≈⟨ pullˡ (kleisliK.sym-assoc  extend-≈ (pullˡ kleisliK.identityʳ))  
    extend ((η _  π₂)  (!  id))  σ _   f , id                                              ≈⟨ (extend-≈ (pullʳ (project₂  identityˡ))) ⟩∘⟨refl  
    extend (η _  π₂)  σ _   f , id                                                           ≈˘⟨ sym (F₁⇒extend monadK π₂) ⟩∘⟨refl  
    K.₁ π₂  (K.₁ swap  τ _  swap)   f , id                                                  ≈⟨ refl⟩∘⟨ (pullʳ (pullʳ swap∘⟨⟩))  
    K.₁ π₂  K.₁ swap  τ _   id , f                                                           ≈⟨ pullˡ (sym K.homomorphism  K.F-resp-≈ project₂)  
    K.₁ π₁  τ _   id , f                                                                      

  pidʳ :  {X Y} {f : X  K.₀ Y}  extend f  f   f
  pidʳ {f=f} = ∘-resp-≈ʳ (sym ↓-charac)  pidʳ'

  ↓-comm :  {X Y Z} {f : X  K.₀ Y} {g : X  K.₀ Z}  extend (f )  g   extend (g )  f 
  ↓-comm {f=f} {g} = ∘-resp-≈ (extend-≈ (sym ↓-charac)) (sym ↓-charac)
     ↓-comm'
     ∘-resp-≈ (extend-≈ ↓-charac) ↓-charac

  ↓-cong :  {X Y} {f g : X  K.₀ Y}  f  g  f   g 
  ↓-cong f≈g = sym ↓-charac  ↓-cong' f≈g  ↓-charac

  ↓-denestʳ :  {X Y Z} {f : X  K.₀ Y} {g : X  K.₀ Z}  (extend g  f )   extend (g )  f 
  ↓-denestʳ {f = f} {g} = begin 
    (extend g  f )    ≈˘⟨ ↓-charac  ↓-cong (∘-resp-≈ʳ ↓-charac)  
    (extend g  f ↓') ↓' ≈⟨ ↓-denestʳ'  
    extend (g ↓')  f ↓' ≈⟨ extend-≈ ↓-charac ⟩∘⟨ ↓-charac  
    extend (g )  f    

  ↓-idempotent :  {X Y} {f : X  K.₀ Y}  f    f 
  ↓-idempotent {f = f} = begin 
    f     ≈˘⟨ ↓-charac  ↓-cong ↓-charac  
    f ↓' ↓' ≈⟨ ↓-idempotent' 
    f ↓'    ≈⟨ ↓-charac 
    f      

  -- restriction of f to domain of g
  _⇂_ :  {X Y Z} (f : X  K.₀ Y) (g : X  K.₀ Z)  X  K.₀ Y
  f  g = extend π₁  τ _   f , g 

  -- lemmas on domain and restriction
  ⇂-cong₂ :  {X Y Z} {f h : X  K.₀ Y} {g i : X  K.₀ Z}  f  h  g  i  f  g  h  i
  ⇂-cong₂ eq₁ eq₂ = refl⟩∘⟨ refl⟩∘⟨ ⟨⟩-cong₂ eq₁ eq₂

  restrict-η :  {X Y} {f : X  K.₀ Y}  η X  f  f 
  restrict-η {X} {Y} {f} = begin 
    extend π₁  τ _   η X , f                    ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identityʳ identityˡ) 
    extend π₁  τ _  (η X  id)   id , f        ≈⟨ refl⟩∘⟨ pullˡ (τ-comm-id (η X))  
    extend π₁  (K.₁ (η X  id)  τ _)   id , f  ≈⟨ pullˡ (pullˡ (extend∘F₁ monadK π₁ (η X  id)))  
    (extend (π₁  (η X  id))  τ _)   id , f    ≈⟨ (kleisliK.extend-≈ project₁ ⟩∘⟨refl) ⟩∘⟨refl  
    (extend (η X  π₁)  τ _)   id , f           ≈⟨ pushˡ ((F₁⇒extend monadK π₁) ⟩∘⟨refl)  
    K.₁ π₁  τ _   id , f                        

  restrict-law :  {X Y Z} (f : X  K.₀ Y) (g : X  K.₀ Z)  f  g  extend f  g 
  restrict-law {X} {Y} {Z} f g = begin 
    extend π₁  τ (K.₀ Y , Z)   f , g                   ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identityʳ identityˡ)  
    extend π₁  τ (K.₀ Y , Z)  (f  id)   id , g       ≈⟨ refl⟩∘⟨ (pullˡ (τ-comm-id f))  
    extend π₁  (K.₁ (f  id)  τ (X , Z))   id , g     ≈⟨ (refl⟩∘⟨ assoc)  pullˡ (pullʳ (sym K.homomorphism))  
    (μ.η _  K.₁ (π₁  (f  id)))  τ (X , Z)   id , g  ≈⟨ (refl⟩∘⟨ ((K.F-resp-≈ π₁∘⁂)  K.homomorphism)) ⟩∘⟨refl  
    (μ.η Y  (K.₁ f  K.₁ π₁))  τ (X , Z)   id , g     ≈⟨ sym-assoc ⟩∘⟨refl  assoc  
    extend f  K.₁ π₁  τ _   id , g                    

  ⇂∘ :  {A X Y Z} {f : X  K.₀ Y} {g : X  K.₀ Z} {h : A  X}  (f  g)  h  (f  h)  (g  h)
  ⇂∘ {A} {X} {Y} {Z} {f} {g} {h} = pullʳ (pullʳ ⟨⟩∘)

  η↓ :  {X Y} (f : X  K.₀ Y)  (η (K.₀ Y)  f)   η X
  η↓ {X} {Y} f = begin 
    K.₁ π₁  τ _   id , η _  f         ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identity² refl)  
    K.₁ π₁  τ _  (id  η _)   id , f  ≈⟨ refl⟩∘⟨ (pullˡ (τ-η _))  
    K.₁ π₁  η _   id , f               ≈⟨ pullˡ (K₁η π₁)  
    (η _  π₁)   id , f                 ≈⟨ cancelʳ project₁  
    η _                                    

  -- f ↓ == M₁ π₁ ∘ σ ∘ ⟨ id , f ⟩
  ⇂-assoc :  {A X Y Z} {f : X  K.₀ Y} {g : X  K.₀ Z} {h : X  K.₀ A}  (f  g)  h  f  (g  h)
  ⇂-assoc {A} {X} {Y} {Z} {f} {g} {h} = begin 
    (f  g)  h                     ≈⟨ restrict-law _ _  ∘-resp-≈ˡ (extend-≈ (restrict-law _ _))  
    extend (extend f  g )  h    ≈˘⟨ pullˡ (sym kleisliK.assoc)  
    extend f  extend (g )  h    ≈˘⟨ refl⟩∘⟨ ↓-denestʳ  
    extend f  ((extend g  h ) ) ≈˘⟨ restrict-law _ _  ∘-resp-≈ʳ (↓-cong (restrict-law _ _))  
    f  (g  h)                     

  infixr 10 _⊑_

  -- This is an alternative definition of the order to the one in `Categories.Category.Restriction.Properties.Poset`
  -- But they are in fact equivalent, which is what we use to get `refl`, `trans` and `antisym`

  _⊑_ :  {X Y} (f g : X  K.₀ Y)  Set e
  f  g = f  g  f

  module PartialOrder where
    private
      _≤_ :  {X Y} (f g : X  K.₀ Y)  Set e
      _≤_ {X} {Y} = _≤'_
        where open Poset (poset X Y) using () renaming (_≤_ to _≤'_) 
      
      ≤⇔⊑-helper' :  {X Y} {f g : X  K.₀ Y}  extend g  f   extend π₁  τ _   g , f 
      ≤⇔⊑-helper' {X} {Y} {f} {g} = begin 
        extend g  K.₁ π₁  τ _   id , f         ≈⟨ pullˡ (extend∘F₁ monadK g π₁)  
        extend (g  π₁)  τ _   id , f           ≈˘⟨ (extend-≈ project₁) ⟩∘⟨refl  
        extend (π₁  (g  id))  τ _   id , f    ≈˘⟨ pullˡ ((extend∘F₁ monadK π₁ (g  id)))  
        extend π₁  K.₁ (g  id)  τ _   id , f  ≈˘⟨ refl⟩∘⟨ (extendʳ (strongK.strengthen.commute (g , id)))  
        extend π₁  τ _  (g  K.₁ id)   id , f  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identityʳ (elimˡ K.identity))  
        extend π₁  τ _   g , f                  

      ≤⇔⊑-helper :  {X Y} {f g : X  K.₀ Y}  extend g  f ↓'  extend π₁  τ _   g , f 
      ≤⇔⊑-helper {X} {Y} {f} {g} = ∘-resp-≈ʳ ↓-charac  ≤⇔⊑-helper'

      ≤⇒⊑ :  {X Y} {f g : X  K.₀ Y}  f  g  f  g
      ≤⇒⊑ {X} {Y} {f} {g} leq = begin 
        f                           ≈⟨ leq 
        extend g  f ↓'             ≈⟨ ≤⇔⊑-helper  
        extend π₁  τ _   g , f  
      ⊑⇒≤ :  {X Y} {f g : X  K.₀ Y}  f  g  f  g
      ⊑⇒≤ {X} {Y} {f} {g} sqleq = begin 
        f                                    ≈⟨ sqleq  
        extend π₁  τ _   g , f           ≈˘⟨ ≤⇔⊑-helper   
        extend g  f ↓' 

    ⊑-refl :  {X Y} {f : X  K.₀ Y}  f  f
    ⊑-refl {X} {Y} = ≤⇒⊑ (refl' refl)
      where open Poset (poset X Y) using () renaming (reflexive to refl')

    ⊑-trans :  {X Y} {f g h : X  K.₀ Y}  f  g  g  h  f  h
    ⊑-trans {X} {Y} f⊑g g⊑h = ≤⇒⊑ (trans' (⊑⇒≤ f⊑g) (⊑⇒≤ g⊑h))
      where open Poset (poset X Y) using () renaming (trans to trans')

    ⊑-antisym :  {X Y} {f g : X  K.₀ Y}  f  g  g  f  f  g
    ⊑-antisym {X} {Y} f⊑g g⊑f = antisym' (⊑⇒≤ f⊑g) (⊑⇒≤ g⊑f)
      where open Poset (poset X Y) using () renaming (antisym to antisym')

  open PartialOrder public


  -- right monotonicity of composition
  ⊑∘ʳ :  {A X Y} {f g : X  K.₀ Y} {h : Y  K.₀ A}  f  g  (extend h  f)  (extend h  g)
  ⊑∘ʳ {A} {X} {Y} {f} {g} {h} leq = begin
    extend h  f ≈⟨ refl⟩∘⟨ leq 
    extend h  (g  f) ≈⟨ refl⟩∘⟨ (restrict-law g f) 
    extend h  extend g  f  ≈⟨ pullˡ kleisliK.sym-assoc 
    extend (extend h  g)  f  ≈˘⟨ ∘-resp-≈ˡ (extend-≈ pidʳ) 
    extend (extend (extend h  g)  ((extend h  g) ))  f  ≈˘⟨ pullˡ kleisliK.sym-assoc 
    extend (extend h  g)  extend ((extend h  g) )  f  ≈˘⟨ refl⟩∘⟨ ↓-denestʳ 
    extend (extend h  g)  (extend (extend h  g)  (f ))  ≈˘⟨ pullˡ kleisliK.sym-assoc 
    extend h  extend g  (extend (extend h  g)  (f ))  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ↓-cong (∘-resp-≈ˡ kleisliK.assoc  assoc) 
    extend h  extend g  (extend h  extend g  f ) ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ ↓-cong (refl⟩∘⟨ leq  ∘-resp-≈ʳ (restrict-law g f) )  -- h g dom(h g dom(f))
    extend h  extend g  (extend h  f) ≈⟨ pullˡ (kleisliK.sym-assoc)  -- h g dom(h f)
    extend (extend h  g)  (extend h  f) ≈˘⟨ restrict-law (extend h  g) (extend h  f) 
    (extend h  g)  (extend h  f) 

  -- left monotonocity of composition
  ⊑∘ˡ :  {A X Y} {f g : X  K.₀ Y} {h : A  X}  f  g  (f  h)  (g  h)
  ⊑∘ˡ {A} {X} {Y} {f} {g} {h} leq = begin 
    f  h               ≈⟨ leq ⟩∘⟨refl  
    (g  f)  h         ≈⟨ ⇂∘  
    ((g  h)  (f  h)) 

  ⊑-cong₂ :  {X Y} {f g h i : X  K.₀ Y}  f  h  g  i  f  g  h  i
  ⊑-cong₂ eq₁ eq₂ leq = (sym eq₁)  leq  ⇂-cong₂ eq₂ eq₁

  f↓⊑η :  {X Y} {f : X  K.₀ Y}  f   η X
  f↓⊑η {X} {Y} {f} = begin 
    f          ≈˘⟨ ↓-idempotent  
    (f )      ≈˘⟨ restrict-η  
    η X  (f ) 


  -- bottom element of restriction order
  bot :  {X} {Y}  X  K.₀ Y
  bot = i₂ #

  bot∘! :  {X} {Y}  bot {X} {Y}  bot {} {Y}  !
  bot∘! = #-Uniformity (algebras _) inject₂

  bot-absorbs :  {X Y Z} {f : Z  X}  bot {X} {Y}  f  bot {Z} {Y}
  bot-absorbs {X} {Y} {Z} {f} = begin 
    bot {X} {Y}  f ≈⟨ bot∘! ⟩∘⟨refl  
    (bot  !)  f   ≈⟨ pullʳ (sym (!-unique (!  f)))  
    bot  !         ≈˘⟨ bot∘! 
    bot {Z} {Y}     

  bot⊑f :  {X} {Y} {f : X  K.₀ Y}  bot  f
  bot⊑f {X} {Y} {f} = sym (begin
    extend π₁  τ _   f , i₂ #                                       ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identityˡ identityʳ) 
    extend π₁  τ _  (id  i₂ #)   f , id                           ≈⟨ refl⟩∘⟨ (pullˡ (τ-comm i₂)) 
    extend π₁  ((τ _ +₁ id)  distributeˡ⁻¹  (id  i₂))#   f , id  ≈⟨ refl⟩∘⟨ ((#-resp-≈ (algebras (K.₀ Y × Y)) (refl⟩∘⟨ distributeˡ⁻¹-i₂)) ⟩∘⟨refl) 
    extend π₁  ((τ _ +₁ id)  i₂)#   f , id                         ≈⟨ refl⟩∘⟨ ((#-resp-≈ (algebras (K.₀ Y × Y)) ((inject₂  identityʳ))) ⟩∘⟨refl) 
    extend π₁  i₂ #   f , id                                        ≈⟨ pullˡ (extend-preserve π₁ i₂) 
    ((extend π₁ +₁ id)  i₂) #   f , id                              ≈⟨ (#-resp-≈ (algebras Y) (inject₂  identityʳ)) ⟩∘⟨refl 
    i₂ #   f , id                                                    ≈˘⟨ #-Uniformity (algebras Y) inject₂ 
    i₂ #                                                                )

  -- strictness of strength
  τ-strict :  {X Y Z}  τ (X , Y)  (id  bot {Z} {Y})  bot
  τ-strict {X} {Y} {Z} = begin
    τ _  (id  i₂ #)                           ≈⟨ τ-comm i₂ 
    ((τ _ +₁ id)  distributeˡ⁻¹  (id  i₂)) # ≈⟨ #-resp-≈ (algebras _) (refl⟩∘⟨ distributeˡ⁻¹-i₂) 
    ((τ _ +₁ id)  i₂) #                        ≈⟨ #-resp-≈ (algebras _) (inject₂  identityʳ) 
    (i₂ #)                                      

  -- monotonicity of strength
  τ-monotonicity :  {X Y} {f g : X  K.₀ Y}  f  g  (τ (X , Y)   id , f )  (τ (X , Y)   id , g )
  τ-monotonicity {f = f} {g} f⊑g = begin
    τ _   id , f  ≈⟨ refl⟩∘⟨ (⟨⟩-cong₂ refl (f⊑g  restrict-law g f)) 
    τ _   id , extend g  f   ≈˘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identity² refl) 
    τ _  (id  extend g)   id , f   ≈⟨ pullˡ (sym (τ-extendʳ g))  assoc 
    extend (τ _  (id  g))  τ _   id , K.₁ π₁  τ _   id , f   ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identity² refl) 
    extend (τ _  (id  g))  τ _  (id  K.₁ π₁)   id , τ _   id , f   ≈⟨ refl⟩∘⟨ (extendʳ (strengthen.commute (id , π₁))) 
    extend (τ _  (id  g))  K.₁ (id  π₁)   τ _   id , τ _   id , f   ≈⟨ pullˡ (extend∘F₁ monadK _ _  extend-≈ (pullʳ (⁂∘⁂  ⁂-cong₂ identity² refl))) 
    extend (τ _  (id  g  π₁))  τ _   id , τ _   id , f   ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identity² refl) 
    extend (τ _  (id  g  π₁))  τ _  (id  τ _)   id ,  id , f   ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ assocˡ∘⟨⟩ 
    extend (τ _  (id  g  π₁))  τ _  (id  τ _)  assocˡ   Δ , f  ≈⟨ refl⟩∘⟨ (sym-assoc  pullˡ (assoc  sym strength-assoc)) 
    extend (τ _  (id  g  π₁))  (K.₁ assocˡ  τ _)   Δ , f  ≈⟨ pullˡ (pullˡ (extend∘F₁ monadK _ _)) 
    (extend ((τ _  (id  g  π₁))  assocˡ)  τ _)   Δ , f  ≈⟨ (extend-≈ (pullʳ (⟨⟩∘  ⟨⟩-cong₂ (pullʳ project₁  identityˡ) (pullʳ project₂  pullʳ project₁))) ⟩∘⟨refl) ⟩∘⟨refl 
    (extend (τ _   π₁  π₁ , g  π₂  π₁ )  τ _)   Δ , f  ≈˘⟨ ((extend-≈ (∘-resp-≈ʳ (⟨⟩∘  ⟨⟩-cong₂ (∘-resp-≈ˡ identityˡ) assoc))) ⟩∘⟨refl) ⟩∘⟨refl 
    (extend (τ _  (id  g)  π₁)  τ _)   Δ , f  ≈˘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identityʳ identityˡ) 
    (extend (τ _  (id  g)  π₁)  τ _)  (Δ  id)   id , f  ≈⟨ pullʳ (extendʳ (τ-comm-id Δ)) 
    extend (τ _  (id  g)  π₁)  K.₁ (Δ  id)  τ _   id , f  ≈⟨ pullˡ (extend∘F₁ monadK _ _  extend-≈ (pullʳ (pullʳ project₁  pullˡ ⁂∘Δ))) 
    extend (τ _   id , g   π₁)  τ _   id , f  ≈˘⟨ pullˡ (extend∘F₁ monadK _ _  extend-≈ assoc) 
    extend (τ _   id , g )  K.₁ π₁  τ _   id , f  ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ project₁ identityˡ) 
    extend (τ _   id , g )  K.₁ π₁  τ _  (π₁  id)   Δ , f  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ extendʳ (τ-comm-id π₁) 
    extend (τ _   id , g )  K.₁ π₁  K.₁ (π₁  id)  τ _   Δ , f  ≈⟨ refl⟩∘⟨ pullˡ (sym monadK.F.homomorphism  monadK.F.F-resp-≈ project₁) 
    extend (τ _   id , g )  K.₁ (π₁  π₁)  τ _   Δ , f  ≈˘⟨ refl⟩∘⟨ (pullˡ (pullˡ (sym monadK.F.homomorphism  monadK.F.F-resp-≈ project₁))  assoc) 
    extend (τ _   id , g )  K.₁ π₁  (K.₁ assocˡ  τ _)   Δ , f  ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (sym-assoc  pullˡ (assoc  sym strength-assoc)) 
    extend (τ _   id , g )  K.₁ π₁  τ _  (id  τ _)  assocˡ   Δ , f  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ assocˡ∘⟨⟩ 
    extend (τ _   id , g )  K.₁ π₁  τ _  (id  τ _)   id ,  id , f   ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identity² refl) 
    extend (τ _   id , g )  K.₁ π₁  τ _   id , τ _   id , f   ≈˘⟨ restrict-law _ _ 
    ((τ _   id , g )  (τ _   id , f )) 

  -- right strictness of composition
  ∘-right-strict :  {X Y Z} (f : X  K.₀ Y)  extend f  bot {Z} {X}  bot
  ∘-right-strict f = begin
    extend f  bot            ≈⟨ extend-preserve f i₂ 
    ((extend f +₁ id)  i₂) # ≈⟨ #-resp-≈ (algebras _) (inject₂  identityʳ) 
    bot                       

  bot-restrict :  {X Y Z} (f : X  K.₀ Y)  f  (bot {X} {Z})  bot
  bot-restrict f = begin
    extend π₁  τ _   f , bot              ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identityˡ identityʳ) 
    extend π₁  τ _  (id  bot)   f , id  ≈⟨ refl⟩∘⟨ (pullˡ τ-strict) 
    extend π₁  bot   f , id               ≈⟨ pullˡ (∘-right-strict π₁) 
    bot   f , id                           ≈˘⟨ #-Uniformity (algebras _) inject₂ 
    bot                                       

  -- bottom element is iteration preserving
  bot-preserves :  {X Y Z} {f : X  K.₀ Y + X}  bot {K.₀ Y} {Z}  f #  ((bot +₁ id)  f)#
  bot-preserves {f = f} = begin 
    bot  f # ≈⟨ bot-absorbs  
    bot ≈⟨ ⊑-antisym bot⊑f helper 
    extend bot  ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f))#  Δ
    ≈⟨ pullˡ (extend-preserve bot ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f))  #-resp-≈ (algebras _) (pullˡ (+₁∘+₁  +₁-cong₂ refl identity²))) 
    ((extend bot  η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f))#  Δ ≈⟨ (#-resp-≈ (algebras _) (∘-resp-≈ˡ (+₁-cong₂ (pullˡ kleisliK.identityʳ  bot-absorbs) refl))) ⟩∘⟨refl 
    ((bot +₁ id)  distributeˡ⁻¹  (id  f))#  Δ ≈⟨ pushˡ (#-Uniformity (algebras _) (begin
      (id +₁ π₂)  (bot +₁ id)  distributeˡ⁻¹  (id  f) ≈⟨ extendʳ (+₁∘+₁  +₁-cong₂ (identityˡ  sym bot-absorbs) id-comm  sym +₁∘+₁) 
      (bot +₁ id)  (π₂ +₁ π₂)  distributeˡ⁻¹  (id  f) ≈⟨ refl⟩∘⟨ (pullˡ distributeˡ⁻¹-π₂  ) 
      (bot +₁ id)  π₂  (id  f) ≈⟨ ∘-resp-≈ʳ project₂  sym-assoc 
      ((bot +₁ id)  f)  π₂ )) 
    ((bot +₁ id)  f)#  π₂  Δ ≈⟨  elimʳ project₂  
    ((bot +₁ id)  f)# 
    where
      ηπ₁-strict :  {X Y Z} (f : Z  Y + Z)  ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f))#  (η X  π₁)
      ηπ₁-strict f = begin
        ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) # ≈⟨ introʳ (⁂∘Δ  ⁂-η) 
        ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) #  (π₁  π₂)  Δ ≈˘⟨ pushˡ (#-Uniformity (algebras _) by-uniformity) 
        ((η _  π₁  π₁ +₁ id)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f)))#  Δ ≈˘⟨ (#-resp-≈ (algebras _) ((+₁-cong₂ (extendʳ (K₁η (π₁  π₁))  ∘-resp-≈ʳ (pullʳ (project₁  identityˡ))) refl) ⟩∘⟨refl)) ⟩∘⟨refl 
        ((K.₁ (π₁  π₁)  η _  (id  π₁) +₁ id)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f)))#  Δ ≈˘⟨ (#-resp-≈ (algebras _) ((+₁-cong₂ (refl⟩∘⟨ (∘-resp-≈ʳ ( sym (⁂∘⁂  ⁂-cong₂ identity² refl))  pullˡ (τ-η (A×B , _)))) refl) ⟩∘⟨refl)) ⟩∘⟨refl 
        ((K.₁ (π₁  π₁)  τ _  (id  η _  π₁) +₁ id)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f)))#  Δ ≈˘⟨ (#-resp-≈ (algebras _) (pullˡ (+₁∘+₁  +₁-cong₂ assoc identity² ))) ⟩∘⟨refl 
        ((K.₁ (π₁  π₁)  τ _ +₁ id)  ((id  η _  π₁) +₁ id)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f)))#  Δ ≈˘⟨ (#-resp-≈ (algebras _) (refl⟩∘⟨ (extendʳ (sym (distributeˡ⁻¹-natural _ _ _)  ∘-resp-≈ˡ (+₁-cong₂ refl (⟨⟩-unique id-comm id-comm)))))) ⟩∘⟨refl 
        ((K.₁ (π₁  π₁)  τ _ +₁ id)  distributeˡ⁻¹  (id  (η _  π₁ +₁ id))  (id  distributeˡ⁻¹  (id  f)))#  Δ ≈⟨ (#-resp-≈ (algebras _) (refl⟩∘⟨ (refl⟩∘⟨ (⁂∘⁂  ⁂-cong₂ identity² refl)))) ⟩∘⟨refl 
        ((K.₁ (π₁  π₁)  τ _ +₁ id)  distributeˡ⁻¹  (id  (η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)))#  Δ ≈˘⟨ pullˡ (K-preserve _ _  #-resp-≈ (algebras _) (pullˡ (+₁∘+₁  +₁-cong₂ refl identity²))) 
        K.₁ (π₁  π₁)  ((τ _ +₁ id)  distributeˡ⁻¹  (id  (η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)))#  Δ ≈˘⟨ refl⟩∘⟨ (pullˡ (τ-comm _)) 
        K.₁ (π₁  π₁)  τ _  (id  ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) #)  Δ ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ⁂∘Δ 
        K.₁ (π₁  π₁)  τ _   id , ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) #  ≈˘⟨ pullˡ (extend∘F₁ monadK π₁ _  ∘-resp-≈ʳ (monadK.F.F-resp-≈ (project₁  assoc))  F₁⇒extend monadK (π₁  π₁)) 
        extend π₁  K.₁ (η _  π₁  id)  τ _   id , ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) #  ≈˘⟨ refl⟩∘⟨ (extendʳ (τ-comm-id (η _  π₁))) 
        extend π₁  τ _  (η _  π₁  id)   id , ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) #  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⟨⟩  ⟨⟩-cong₂ identityʳ identityˡ) 
        extend π₁  τ _   η _  π₁ , ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) #  
        where
          by-uniformity : (id +₁ π₁  π₂)  (η _  π₁  π₁ +₁ id)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f))
             ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f))  (π₁  π₂)
          by-uniformity = begin
            (id +₁ π₁  π₂)  (η _  π₁  π₁ +₁ id)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f)) ≈⟨ pullˡ (+₁∘+₁  +₁-cong₂ (identityˡ  ∘-resp-≈ʳ (sym project₁)) identityʳ) 
            (η _  π₁  (π₁  π₂) +₁ π₁  π₂)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f)) ≈˘⟨ pullˡ (+₁∘+₁  +₁-cong₂ assoc identityˡ) 
            (η _  π₁ +₁ id)  (π₁  π₂ +₁ π₁  π₂)  distributeˡ⁻¹  (id  distributeˡ⁻¹  (id  f)) ≈⟨ refl⟩∘⟨ extendʳ (distributeˡ⁻¹-natural π₁ π₂ π₂) 
            (η _  π₁ +₁ id)  distributeˡ⁻¹  (π₁  (π₂ +₁ π₂))  (id  distributeˡ⁻¹  (id  f)) ≈⟨ refl⟩∘⟨ refl⟩∘⟨ (⁂∘⁂  ⁂-cong₂ identityʳ refl) 
            (η _  π₁ +₁ id)  distributeˡ⁻¹  (π₁  (π₂ +₁ π₂)  distributeˡ⁻¹  (id  f)) ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ⁂-cong₂ refl (pullˡ distributeˡ⁻¹-π₂  project₂) 
            (η _  π₁ +₁ id)  distributeˡ⁻¹  (π₁  f  π₂) ≈˘⟨ pullʳ (pullʳ (⁂∘⁂  ⁂-cong₂ identityˡ refl)) 
            ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f))  (π₁  π₂) 
      helper : (extend bot  ((η _  π₁ +₁ id)  distributeˡ⁻¹  (id  f)) #  Δ)  bot
      helper =  ⊑-cong₂ refl (begin
        extend bot  (η _  π₁)  Δ ≈⟨ pullˡ (pullˡ kleisliK.identityʳ)  cancelʳ project₁ 
        bot ) (⊑∘ʳ (⊑∘ˡ (ηπ₁-strict f)))

  -- strictness of extend
  extend-strict :  {X Y : Obj}  bot {K.₀ X} {Y}  extend bot
  extend-strict {X} {Y} = begin
    bot ≈⟨ FreeObject.*-uniq (freealgebras X) bot (record { h = bot ; preserves = bot-preserves }) bot-absorbs 
    Elgot-Algebra-Morphism.h (((freealgebras X) FreeObject.*) bot) ≈⟨ sym (FreeObject.*-uniq (freealgebras X) {algebras Y} bot (record { h = extend bot ; preserves = extend-preserve bot _ }) kleisliK.identityʳ) 
    extend bot  

  -- left strictness of composition
  ∘-left-strict :  {X Y Z} (f : X  K.₀ Y)  extend (bot {Y} {Z})  f  bot
  ∘-left-strict f =  begin
    extend bot  f ≈˘⟨ extend-strict ⟩∘⟨refl 
    bot  f ≈⟨ bot-absorbs 
    bot 

  bot∘!-id : bot {} {}  ! {K.₀ }  id
  bot∘!-id = begin 
    bot  ! ≈⟨ bot-absorbs 
    bot ≈˘⟨ ∘-left-strict id 
    extend bot  id ≈⟨ identityʳ 
    μ.η _  (K.₁ bot) ≈⟨ refl⟩∘⟨ K.F-resp-≈ (¡-unique₂ bot (η _)) 
    μ.η _  (K.₁ (η )) ≈⟨ monadK.identityˡ 
    id 

  K⊥⊤ : K.₀   
  K⊥⊤ = record { from = ! ; to = bot ; iso = record { isoˡ = bot∘!-id ; isoʳ = !-unique₂ } }