open import Level

open import Data.Product using (_,_)
open import Categories.Category
open import Categories.Functor hiding (id)
open import Categories.Functor.Coalgebra
open import Monad.Instance.Delay
open import Categories.Monad.Strong
open import Categories.NaturalTransformation hiding (id)
open import Categories.Object.Terminal
open import Categories.Category.Distributive

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

-- The Delay Monad is Strong
module Monad.Instance.Delay.Strong {o  e} {C : Category o  e} (distributive : Distributive C) (D : DelayM (Distributive.cocartesian distributive)) where
  open Category C
  open import Category.Distributive.Helper distributive
  open Bundles
  open HomReasoning
  open Equiv
  open MP C
  open MR C
  open M C
  open DelayM D
  open D-Kleisli
  open D-Monad
  open Coit
  module τ-mod where
    abstract
      τ :  {X Y}  X × D₀ Y  D₀ (X × Y)
      τ {X} {Y} = coit (distributeˡ⁻¹  (id  out))
      
      τ-commutes :  {X Y}  out  τ {X} {Y}  (id +₁ τ)  distributeˡ⁻¹  (id  out)
      τ-commutes = coit-commutes (distributeˡ⁻¹  (id  out))

      τ-out⁻¹ :  {X Y}  τ {X} {Y}  (id  out⁻¹)  out⁻¹  (id +₁ τ)  distributeˡ⁻¹
      τ-out⁻¹ = begin 
        τ  (id  out⁻¹)                                                ≈⟨ introˡ out⁻¹∘out  
        (out⁻¹  out)  τ  (id  out⁻¹)                                ≈⟨ pullʳ (pullˡ τ-commutes) 
        out⁻¹  ((id +₁ τ)  distributeˡ⁻¹  (id  out))  (id  out⁻¹) ≈⟨ refl⟩∘⟨ (assoc  refl⟩∘⟨ assoc) 
        out⁻¹  (id +₁ τ)  distributeˡ⁻¹  (id  out)  (id  out⁻¹)   ≈⟨ refl⟩∘⟨ (refl⟩∘⟨ (elimʳ (⁂∘⁂  (⁂-cong₂ identity² out∘out⁻¹)  ((⟨⟩-cong₂ identityˡ identityˡ)  ⁂-η)))) 
        out⁻¹  (id +₁ τ)  distributeˡ⁻¹                               

      τ-now :  {X Y}  τ {X} {Y}  (id  now)  now
      τ-now = begin 
        τ  (id  now)                                  ≈⟨ refl⟩∘⟨ sym (⁂∘⁂  (⁂-cong₂ identity² refl))  
        τ  (id  out⁻¹)  (id  i₁)                    ≈⟨ pullˡ τ-out⁻¹ 
        (out⁻¹  (id +₁ τ)  distributeˡ⁻¹)  (id  i₁) ≈⟨ pullʳ (pullʳ distributeˡ⁻¹-i₁) 
        out⁻¹  (id +₁ τ)  i₁                          ≈⟨ refl⟩∘⟨ +₁∘i₁ 
        out⁻¹  i₁  id                                 ≈⟨ refl⟩∘⟨ identityʳ 
        now                                             
      
      τ-later :  {X} {Y}  τ {X} {Y}  (id  later)  later  τ
      τ-later = begin 
        τ  (id  later)                                ≈⟨ refl⟩∘⟨ (sym (⁂∘⁂  ⁂-cong₂ identity² refl))  
        τ  (id  out⁻¹)  (id  i₂)                    ≈⟨ pullˡ τ-out⁻¹  
        (out⁻¹  (id +₁ τ)  distributeˡ⁻¹)  (id  i₂) ≈⟨ pullʳ (pullʳ distributeˡ⁻¹-i₂)  
        out⁻¹  (id +₁ τ)  i₂                          ≈⟨ refl⟩∘⟨ +₁∘i₂  
        out⁻¹  i₂  τ                                  ≈⟨ sym-assoc  
        later  τ                                       

      τ-identityˡ :  {X} {Y}  D₁ (π₂ {X} {Y})  τ  π₂
      τ-identityˡ {X} {Y} = by-coinduction {f = D₁ (π₂ {X} {Y})  τ} {g = π₂} ((π₂ +₁ id)  distributeˡ⁻¹  (id  out)) coind₁ coind₂
        where
        coind₁ : out  D₁ (π₂ {X} {Y})  τ  (id +₁ extend (now  π₂)  τ)  (π₂ +₁ id)  distributeˡ⁻¹  (id  out)
        coind₁ = begin 
          out  D₁ π₂  τ                                             ≈⟨ extendʳ (D₁-commutes π₂)  
          (π₂ +₁ D₁ π₂)  out  τ                                     ≈⟨ refl⟩∘⟨ τ-commutes  
          (π₂ +₁ D₁ π₂)  (id +₁ τ)  distributeˡ⁻¹  (id  out)      ≈⟨ pullˡ (+₁∘+₁  +₁-cong₂ identityʳ refl)  
          (π₂ +₁ D₁ π₂  τ)  distributeˡ⁻¹  (id  out)              ≈˘⟨ pullˡ (+₁∘+₁  +₁-cong₂ identityˡ identityʳ)  
          (id +₁ D₁ π₂  τ)  (π₂ +₁ id)  distributeˡ⁻¹  (id  out) 
        coind₂ : out  (π₂ {X} {D₀ Y})  (id +₁ π₂)  (π₂ +₁ id)  distributeˡ⁻¹  (id  out)
        coind₂ = begin 
          out  π₂                                             ≈˘⟨ π₂∘⁂  
          π₂  (id  out)                                      ≈˘⟨ pullˡ distributeˡ⁻¹-π₂ 
          (π₂ +₁ π₂)  distributeˡ⁻¹  (id  out)              ≈˘⟨ (+₁-cong₂ identityˡ identityʳ) ⟩∘⟨refl  
          (id  π₂ +₁ π₂  id)  distributeˡ⁻¹  (id  out)    ≈˘⟨ pullˡ +₁∘+₁  
          (id +₁ π₂)  (π₂ +₁ id)  distributeˡ⁻¹  (id  out) 

      τ-unique :  {X Y}  (t : X × D₀ Y  D₀ (X × Y))  (out  t  (id +₁ t)  distributeˡ⁻¹  (id  out))  τ  t
      τ-unique {X} {Y} t t-commutes = coit-unique (distributeˡ⁻¹  (id  out)) t t-commutes

      τ-natural :  {X Y} {A} {B} (f : X  A) (g : Y  B)  τ  (f  D₁ g)  D₁ (f  g)  τ
      τ-natural f g = by-coinduction (((f  g +₁ id)  distributeˡ⁻¹)  (id  out)) coind₁ coind₂
        where
        coind₁ : out  τ  (f  extend (now  g))  (id +₁ (τ  (f  extend (now  g))))  ((f  g +₁ id)  distributeˡ⁻¹)  (id  out)
        coind₁ = begin 
          out  τ  (f  extend (now  g))                                                    ≈⟨ pullˡ τ-commutes  
          ((id +₁ τ)  distributeˡ⁻¹  (id  out))  (f  extend (now  g))                   ≈⟨ pullʳ (pullʳ ⁂∘⁂)  
          (id +₁ τ)  distributeˡ⁻¹  (id  f  out  extend (now  g))                       ≈⟨ refl⟩∘⟨ (refl⟩∘⟨ (⁂-cong₂ identityˡ (extend-commutes (now  g))))  
          (id +₁ τ)  distributeˡ⁻¹  (f  [ out  now  g , i₂  extend (now  g) ]  out)   ≈⟨ refl⟩∘⟨ (refl⟩∘⟨ (⁂-cong₂ refl (([]-cong₂ (pullˡ unitlaw) refl) ⟩∘⟨refl)))  
          (id +₁ τ)  distributeˡ⁻¹  (f  [ i₁  g , i₂  extend (now  g) ]  out)          ≈⟨ refl⟩∘⟨ (refl⟩∘⟨ (⁂-cong₂ (sym identityʳ) refl))  
          (id +₁ τ)  distributeˡ⁻¹  (f  id  (g +₁ extend (now  g))  out)                ≈⟨ sym (pullʳ (pullʳ ⁂∘⁂))  
          ((id +₁ τ)  distributeˡ⁻¹  (f  (g +₁ extend (now  g))))  (id  out)            ≈⟨ (refl⟩∘⟨ (sym (distributeˡ⁻¹-natural f g (extend (now  g))))) ⟩∘⟨refl  
          ((id +₁ τ)  ((f  g) +₁ (f  extend (now  g)))  distributeˡ⁻¹)  (id  out)      ≈⟨ (pullˡ +₁∘+₁) ⟩∘⟨refl  
          ((id  (f  g) +₁ τ  (f  extend (now  g)))  distributeˡ⁻¹)  (id  out)         ≈⟨ sym (((+₁-cong₂ refl identityʳ) ⟩∘⟨refl) ⟩∘⟨refl)  
          ((id  (f  g) +₁ (τ  (f  extend (now  g)))  id)  distributeˡ⁻¹)  (id  out)  ≈⟨ sym (pullˡ (pullˡ +₁∘+₁))  
          (id +₁ (τ  (f  extend (now  g))))  ((f  g +₁ id)  distributeˡ⁻¹)  (id  out) 
        coind₂ : out  extend (now  (f  g))  τ  (id +₁ (extend (now  (f  g))  τ))  (((f  g) +₁ id)  distributeˡ⁻¹)  (id  out)
        coind₂ = begin 
          out  extend (now  (f  g))  τ                                                       ≈⟨ pullˡ (extend-commutes (now  (f  g)))  
          ([ out  now  (f  g) , i₂  extend (now  (f  g)) ]  out)  τ                      ≈⟨ (([]-cong₂ (pullˡ unitlaw) refl) ⟩∘⟨refl) ⟩∘⟨refl 
          ([ i₁  (f  g) , i₂  extend (now  (f  g)) ]  out)  τ                             ≈⟨ pullʳ τ-commutes 
          ((f  g) +₁ (extend (now  (f  g))))  (id +₁ τ)  distributeˡ⁻¹  (id  out)         ≈⟨ sym-assoc  sym-assoc 
          ((((f  g) +₁ (extend (now  (f  g))))  (id +₁ τ))  distributeˡ⁻¹)  (id  out)     ≈⟨ (+₁∘+₁ ⟩∘⟨refl) ⟩∘⟨refl 
          ((((f  g)  id) +₁ (extend (now  (f  g))  τ))  distributeˡ⁻¹)  (id  out)        ≈⟨ sym (((+₁-cong₂ id-comm-sym identityʳ) ⟩∘⟨refl) ⟩∘⟨refl) 
          (((id  (f  g)) +₁ ((extend (now  (f  g))  τ)  id))  distributeˡ⁻¹)  (id  out) ≈⟨ sym (pullˡ (pullˡ +₁∘+₁)) 
          (id +₁ (extend (now  (f  g))  τ))  (((f  g) +₁ id)  distributeˡ⁻¹)  (id  out)  
  open τ-mod

  module D-Strong where
    strength : Strength monoidal monad
    Strength.strengthen strength = ntHelper (record { η = λ (X , Y)  τ {X} {Y}; commute = λ (f , g)  τ-natural f g })

    Strength.identityˡ strength {X} = τ-identityˡ {Terminal.⊤ terminal} {X}

    Strength.η-comm strength {X} {Y} = τ-now {X} {Y}

    Strength.μ-η-comm strength {X} {Y} = by-coinduction ([ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  (id  out)) coind₁ coind₂
      where
      -- diagram: https://q.uiver.app/#q=WzAsNyxbMCwwLCJYXFx0aW1lcyBERFkiXSxbMiwwLCJEKFhcXHRpbWVzIERZKSJdLFs0LDAsIkREKFhcXHRpbWVzIFkpIl0sWzAsMiwiWFxcdGltZXMgKERZK0REWSkiXSxbMiwyLCJYXFx0aW1lcyBEWSsgWFxcdGltZXMgRERZIl0sWzQsMiwiWFxcdGltZXMgWSsgREQoWFxcdGltZXMgWSkiXSxbMCw0LCJYXFx0aW1lcyBZKyBYXFx0aW1lcyBERFkiXSxbMCwzLCJpZFxcdGltZXMgb3V0IiwyXSxbMCwxLCJcXHRhdSJdLFsxLDIsIlxcdGF1XioiXSxbMyw0LCIoaWQrXFx0YXUpZGlzdCJdLFs0LDUsIltvdXRcXHRhdSxpbnJcXHRhdV4qXSJdLFsyLDUsIm91dCJdLFsxLDRdLFs2LDUsImlkK1xcdGF1XipcXHRhdSIsMl0sWzMsNiwiWyhpZCArIGlkXFx0aW1lcyBub3cpZGlzdCAoaWRcXHRpbWVzIG91dCksaW5yXWRpc3QiLDJdXQ==
      id*∘Dτ : extend id  extend (now  τ)  extend τ
      id*∘Dτ = begin 
        extend id  extend (now  τ) ≈⟨ DK.sym-assoc  
        extend (extend id  now  τ) ≈⟨ extend-≈ (pullˡ DK.identityʳ) 
        extend (id  τ)              ≈⟨ extend-≈ identityˡ  
        extend τ                    
      coind₁ : out  extend id  extend (now  τ)  τ {X} {D₀ Y}  (id +₁ (extend id  extend (now  τ)  τ))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  (id  out)
      coind₁ = begin 
        out  extend id  extend (now  τ)  τ                                                                                             ≈⟨ refl⟩∘⟨ (pullˡ id*∘Dτ) 
        out  extend τ  τ                                                                                                                 ≈⟨ square 
        [ out  τ , i₂  extend τ ]  (id +₁ τ)  distributeˡ⁻¹  (id  out)                                                               ≈⟨ assoc²εβ  ∘-resp-≈ˡ tri  assoc²βε 
        (id +₁ (extend τ  τ))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  (id  out)                     ≈⟨ (+₁-cong₂ refl (sym (pullˡ id*∘Dτ))) ⟩∘⟨refl 
        (id +₁ (extend id  extend (now  τ)  τ))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  (id  out) 
        where
        tri : [ out  τ , i₂  extend τ ]  (id +₁ τ)  distributeˡ⁻¹  (id +₁ extend τ  τ)  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹
        tri = begin 
          [ out  τ , i₂  extend τ ]  (id +₁ τ)  distributeˡ⁻¹                                                                ≈⟨ pullˡ []∘+₁  
          [ (out  τ)  id , (i₂  extend τ)  τ ]  distributeˡ⁻¹                                                               ≈⟨ ([]-cong₂ (identityʳ  τ-commutes) assoc) ⟩∘⟨refl  
          [ (id +₁ τ)  distributeˡ⁻¹  (id  out) , i₂  extend τ  τ ]  distributeˡ⁻¹                                         ≈˘⟨ ([]-cong₂ ((+₁-cong₂ refl DK.identityʳ) ⟩∘⟨refl) refl) ⟩∘⟨refl  
          [ (id +₁ extend τ  now)  distributeˡ⁻¹  (id  out) , i₂  extend τ  τ ]  distributeˡ⁻¹                            ≈˘⟨ ([]-cong₂ ((+₁-cong₂ identity² (pullʳ (τ-now))) ⟩∘⟨refl) refl) ⟩∘⟨refl  
          [ (id  id +₁ (extend τ  τ)  (id  now))  distributeˡ⁻¹  (id  out) , i₂  extend τ  τ ]  distributeˡ⁻¹          ≈˘⟨ ([]-cong₂ (pullˡ +₁∘+₁) +₁∘i₂) ⟩∘⟨refl  
          [ (id +₁ extend τ  τ)  (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , (id +₁ extend τ  τ)  i₂ ]  distributeˡ⁻¹ ≈˘⟨ pullˡ ∘[]  
          (id +₁ extend τ  τ)  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹                        
        square : out  extend τ  τ  [ out  τ , i₂  extend τ ]  (id +₁ τ)  distributeˡ⁻¹  (id  out)
        square = begin 
          out  extend τ  τ                                                   ≈⟨ pullˡ (extend-commutes τ)  
          ([ out  τ , i₂  extend τ ]  out)  τ                              ≈⟨ pullʳ τ-commutes  
          [ out  τ , i₂  extend τ ]  (id +₁ τ)  distributeˡ⁻¹  (id  out) 
      coind₂ : out  τ {X} {Y}  (id  extend id)  (id +₁ τ  (id  extend id))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  (id  out)
      coind₂ = begin 
        out  τ  (id  extend id)                                                                                             ≈⟨ pullˡ τ-commutes  
        ((id +₁ τ)  distributeˡ⁻¹  (id  out))  (id  extend id)                                                            ≈⟨ pullʳ (pullʳ (⁂∘⁂  ⁂-cong₂ identity² refl)) 
        (id +₁ τ)  distributeˡ⁻¹  (id  out  extend id)                                                                     ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ⁂-cong₂ refl (extend-commutes id) 
        (id +₁ τ)  distributeˡ⁻¹  (id  [ out  id , i₂  extend id ]  out)                                                 ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ⁂-cong₂ refl (([]-cong₂ identityʳ refl) ⟩∘⟨refl) 
        (id +₁ τ)  distributeˡ⁻¹  (id  [ out , i₂  extend id ]  out)                                                      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ sym (⁂∘⁂  ⁂-cong₂ identity² refl) 
        (id +₁ τ)  distributeˡ⁻¹  (id  [ out , i₂  extend id ])  (id  out)                                               ≈⟨ sym-assoc  pullˡ (assoc  Iso⇒Epi (IsIso.iso isIsoˡ) _ _ (assoc²βε  epi-helper  assoc²εβ))  assoc²βε 
        (id +₁ τ  (id  extend id))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  (id  out)   
        where
          epi-helper : (id +₁ τ)  distributeˡ⁻¹  (id  [ out , i₂  extend id ])  [ (id  i₁) , (id  i₂) ]  (id +₁ τ  (id  extend id))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  distributeˡ
          epi-helper = begin 
            (id +₁ τ)  distributeˡ⁻¹  (id  [ out , i₂  extend id ])  [ (id  i₁) , (id  i₂) ]                                           ≈⟨ refl⟩∘⟨ refl⟩∘⟨ (∘[]  []-cong₂ (⁂∘⁂  ⁂-cong₂ identity² inject₁) (⁂∘⁂  ⁂-cong₂ identity² inject₂))  
            (id +₁ τ)  distributeˡ⁻¹  [ id  out , id  i₂  extend id ]                                                                    ≈⟨ ∘-resp-≈ʳ ∘[]  ∘[] 
            [ (id +₁ τ)  distributeˡ⁻¹  (id  out) , (id +₁ τ)  distributeˡ⁻¹  (id  i₂  extend id) ]                                    ≈˘⟨ []-cong₂ refl (pullʳ (pullʳ (⁂∘⁂  ⁂-cong₂ identity² refl))) 
            [ (id +₁ τ)  distributeˡ⁻¹  (id  out) , ((id +₁ τ)  distributeˡ⁻¹  (id  i₂))  (id  extend id) ]                           ≈⟨ []-cong₂ refl ((refl⟩∘⟨ distributeˡ⁻¹-i₂) ⟩∘⟨refl) 
            [ (id +₁ τ)  distributeˡ⁻¹  (id  out) , ((id +₁ τ)  i₂)  (id  extend id) ]                                                  ≈⟨ []-cong₂ refl (pushˡ +₁∘i₂) 
            [ (id +₁ τ)  distributeˡ⁻¹  (id  out) , i₂  τ  (id  extend id) ]                                                            ≈⟨ []-cong₂ ((+₁-cong₂ refl (introʳ (⟨⟩-unique id-comm (id-comm  (sym DK.identityʳ) ⟩∘⟨refl)))) ⟩∘⟨refl) refl 
            [ (id +₁ τ  (id  extend id  now))  distributeˡ⁻¹  (id  out) , i₂  τ  (id  extend id) ]                                   ≈⟨ []-cong₂ ((sym (+₁-cong₂ refl (refl⟩∘⟨ (⁂∘⁂  ⁂-cong₂ identity² refl)))) ⟩∘⟨refl) refl 
            [ (id +₁ τ  (id  extend id)  (id  now))  distributeˡ⁻¹  (id  out) , i₂  τ  (id  extend id) ]                            ≈˘⟨ ∘[]  []-cong₂ (pullˡ (+₁∘+₁  +₁-cong₂ identity² assoc)) +₁∘i₂ 
            (id +₁ τ  (id  extend id))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]                                           ≈˘⟨ refl⟩∘⟨ (elimʳ (IsIso.isoˡ isIsoˡ)) 
            (id +₁ τ  (id  extend id))  [ (id +₁ (id  now))  distributeˡ⁻¹  (id  out) , i₂ ]  distributeˡ⁻¹  distributeˡ             

    Strength.strength-assoc strength {X} {Y} {Z} = by-coinduction (( π₁  π₁ ,  π₂  π₁ , π₂   +₁ id)  distributeˡ⁻¹  (id  out)) coind₁ coind₂
      where
      coind₁ : out  extend (now   π₁  π₁ ,  π₂  π₁ , π₂  )  τ {X × Y} {Z}  (id +₁ extend (now   π₁  π₁ ,  π₂  π₁ , π₂  )  τ)  ( π₁  π₁ ,  π₂  π₁ , π₂   +₁ id)  distributeˡ⁻¹  (id  out)
      coind₁ = begin 
        out  extend (now   π₁  π₁ ,  π₂  π₁ , π₂  )  τ                                                                                      ≈⟨ pullˡ (extend-commutes (now   π₁  π₁ ,  π₂  π₁ , π₂  ))  
        ([ out  now   π₁  π₁ ,  π₂  π₁ , π₂   , i₂  extend (now   π₁  π₁ ,  π₂  π₁ , π₂  ) ]  out)  τ                              ≈⟨ pullʳ τ-commutes  
        [ out  now   π₁  π₁ ,  π₂  π₁ , π₂   , i₂  extend (now   π₁  π₁ ,  π₂  π₁ , π₂  ) ]  (id +₁ τ)  distributeˡ⁻¹  (id  out) ≈⟨ ([]-cong₂ (pullˡ unitlaw) refl) ⟩∘⟨refl  
        ( π₁  π₁ ,  π₂  π₁ , π₂   +₁ extend (now   π₁  π₁ ,  π₂  π₁ , π₂  ))  (id +₁ τ)  distributeˡ⁻¹  (id  out)                   ≈⟨ pullˡ (+₁∘+₁  +₁-cong₂ id-comm (sym identityʳ))  
        (id   π₁  π₁ ,  π₂  π₁ , π₂   +₁ (extend (now   π₁  π₁ ,  π₂  π₁ , π₂  )  τ)  id)  distributeˡ⁻¹  (id  out)               ≈˘⟨ pullˡ +₁∘+₁  
        (id +₁ extend (now   π₁  π₁ ,  π₂  π₁ , π₂  )  τ)  ( π₁  π₁ ,  π₂  π₁ , π₂   +₁ id)  distributeˡ⁻¹  (id  out)              
      coind₂ : out  τ {X} {Y × Z}  (id  τ)   π₁  π₁ ,  π₂  π₁ , π₂    (id +₁ τ  (id  τ)   π₁  π₁ ,  π₂  π₁ , π₂  )  ( π₁  π₁ ,  π₂  π₁ , π₂   +₁ id)  distributeˡ⁻¹  (id  out)
      coind₂ = begin 
        out  τ  (id  τ)   π₁  π₁ ,  π₂  π₁ , π₂                                                                             ≈⟨ pullˡ τ-commutes  
        ((id +₁ τ)  distributeˡ⁻¹  (id  out))  (id  τ)   π₁  π₁ ,  π₂  π₁ , π₂                                            ≈⟨ pullʳ (pullˡ (pullʳ (⁂∘⁂  ⁂-cong₂ identity² τ-commutes)))  ∘-resp-≈ʳ assoc  
        (id +₁ τ)  distributeˡ⁻¹  (id  (id +₁ τ)  distributeˡ⁻¹  (id  out))   π₁  π₁ ,  π₂  π₁ , π₂                      ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (pullˡ (⁂∘⁂  ⁂-cong₂ identity² assoc)) 
        (id +₁ τ)  distributeˡ⁻¹  (id  (id +₁ τ)  distributeˡ⁻¹)  (id  (id  out))   π₁  π₁ ,  π₂  π₁ , π₂               ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ helper₁ 
        (id +₁ τ)  distributeˡ⁻¹  (id  (id +₁ τ)  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂    (id  out)                    ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (pullˡ (⁂∘⁂  ⁂-cong₂ identity² refl)) 
        (id +₁ τ)  distributeˡ⁻¹  (id  (id +₁ τ))  (id  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂    (id  out)             ≈⟨ refl⟩∘⟨ extendʳ (sym (distributeˡ⁻¹-natural id id τ)  ∘-resp-≈ˡ (+₁-cong₂ (⟨⟩-unique id-comm id-comm) refl))  
        (id +₁ τ)  (id +₁ (id  τ))  distributeˡ⁻¹  (id  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂    (id  out)             ≈⟨ pullˡ (+₁∘+₁  +₁-cong₂ identity² refl)  
        (id +₁ τ  (id  τ))  distributeˡ⁻¹  (id  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂    (id  out)                     ≈⟨ refl⟩∘⟨ (assoc²εβ  ∘-resp-≈ˡ helper₃  assoc)  
        (id +₁ τ  (id  τ))  ( π₁  π₁ ,  π₂  π₁ , π₂   +₁  π₁  π₁ ,  π₂  π₁ , π₂  )  distributeˡ⁻¹  (id  out)        ≈˘⟨ extendʳ (+₁∘+₁  +₁-cong₂ refl (identityʳ  sym-assoc)  sym +₁∘+₁)  
        (id +₁ τ  (id  τ)   π₁  π₁ ,  π₂  π₁ , π₂  )  ( π₁  π₁ ,  π₂  π₁ , π₂   +₁ id)  distributeˡ⁻¹  (id  out)   
        where
        helper₁ : (id  (id  out))   π₁  π₁ ,  π₂  π₁ , π₂     π₁  π₁ ,  π₂  π₁ , π₂    (id {X × Y}  out {Z})
        helper₁ = begin 
          (id  (id  out))   π₁  π₁ ,  π₂  π₁ , π₂        ≈⟨ ⁂∘⟨⟩  ⟨⟩-cong₂ identityˡ (⁂∘⟨⟩  ⟨⟩-cong₂ identityˡ refl)  
           π₁  π₁ ,  π₂  π₁ , out  π₂                      ≈˘⟨ ⟨⟩∘  ⟨⟩-cong₂ (pullʳ project₁  ∘-resp-≈ʳ identityˡ) (⟨⟩∘  ⟨⟩-cong₂ (pullʳ π₁∘⁂  ∘-resp-≈ʳ identityˡ) π₂∘⁂)  
           π₁  π₁ ,  π₂  π₁ , π₂    (id {X × Y}  out {Z}) 
        helper₃ : distributeˡ⁻¹  (id  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂    ( π₁  π₁ ,  π₂  π₁ , π₂   +₁  π₁  π₁ ,  π₂  π₁ , π₂  )  distributeˡ⁻¹
        helper₃ = Iso⇒Mono (IsIso.iso isIsoˡ) (distributeˡ⁻¹  (id  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂  ) (( π₁  π₁ ,  π₂  π₁ , π₂   +₁  π₁  π₁ ,  π₂  π₁ , π₂  )  distributeˡ⁻¹) (begin 
          distributeˡ  distributeˡ⁻¹  (id  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂   ≈⟨ cancelˡ (IsIso.isoʳ isIsoˡ)  
          (id  distributeˡ⁻¹)   π₁  π₁ ,  π₂  π₁ , π₂   ≈⟨ ⁂∘⟨⟩  ⟨⟩-cong₂ identityˡ refl  
           π₁  π₁ , distributeˡ⁻¹   π₂  π₁ , π₂   ≈⟨ ⟨⟩-unique unique₁ unique₂  
          [  π₁  π₁ , i₁   π₂  π₁ , π₂   ,  π₁  π₁ , i₂   π₂  π₁ , π₂   ]  distributeˡ⁻¹ ≈˘⟨ pullˡ ([]∘+₁  []-cong₂ (⁂∘⟨⟩  ⟨⟩-cong₂ identityˡ refl) (⁂∘⟨⟩  ⟨⟩-cong₂ identityˡ refl))  
          distributeˡ  ( π₁  π₁ ,  π₂  π₁ , π₂   +₁  π₁  π₁ ,  π₂  π₁ , π₂  )  distributeˡ⁻¹ )
          where
          unique₁ : π₁  [  π₁  π₁ , i₁   π₂  π₁ , π₂   ,  π₁  π₁ , i₂   π₂  π₁ , π₂   ]  distributeˡ⁻¹  π₁  π₁
          unique₁ = begin 
            π₁  [  π₁  π₁ , i₁   π₂  π₁ , π₂   ,  π₁  π₁ , i₂   π₂  π₁ , π₂   ]  distributeˡ⁻¹ ≈⟨ extendʳ (∘[]  []-cong₂ project₁ project₁  sym ∘[])  
            π₁  [ π₁ , π₁ ]  distributeˡ⁻¹ ≈⟨ refl⟩∘⟨ distributeˡ⁻¹-π₁  
            π₁  π₁ 
          unique₂ : π₂  [  π₁  π₁ , i₁   π₂  π₁ , π₂   ,  π₁  π₁ , i₂   π₂  π₁ , π₂   ]  distributeˡ⁻¹  distributeˡ⁻¹   π₂  π₁ , π₂ 
          unique₂ = begin 
            π₂  [  π₁  π₁ , i₁   π₂  π₁ , π₂   ,  π₁  π₁ , i₂   π₂  π₁ , π₂   ]  distributeˡ⁻¹ ≈⟨ pullˡ (∘[]  []-cong₂ project₂ project₂)  
            ( π₂  π₁ , π₂  +₁  π₂  π₁ , π₂ )  distributeˡ⁻¹                                             ≈˘⟨ (+₁-cong₂ (⟨⟩-cong₂ refl identityˡ) (⟨⟩-cong₂ refl identityˡ)) ⟩∘⟨refl  
            ((π₂  id) +₁ (π₂  id))  distributeˡ⁻¹                                                           ≈⟨ distributeˡ⁻¹-natural π₂ id id  
            distributeˡ⁻¹  (π₂  (id +₁ id))                                                                  ≈⟨ refl⟩∘⟨ (⟨⟩-cong₂ refl (elimˡ ([]-unique id-comm-sym id-comm-sym)))  
            distributeˡ⁻¹   π₂  π₁ , π₂                                                                    

    strongMonad : StrongMonad monoidal
    strongMonad = record { M = monad ; strength = strength }

    module strength = Strength strength