{-# OPTIONS --without-K --safe #-}

open import Categories.Category using (Category)
open import Categories.Category.Monoidal using (Monoidal)

module Categories.Category.Monoidal.Reasoning {o  e} {C : Category o  e} (M : Monoidal C) where

open import Data.Product using (_,_)

open import Categories.Functor using (Functor)

open Category C

private
  variable
    X Y : Obj
    f g h i : X  Y

open Monoidal M using (_⊗₀_; _⊗₁_; )
open Functor  using (F-resp-≈; homomorphism)
open HomReasoning public

infixr 6 _⟩⊗⟨_ refl⟩⊗⟨_
infixl 7 _⟩⊗⟨refl

⊗-resp-≈ : f  h  g  i  (f ⊗₁ g)  (h ⊗₁ i)
⊗-resp-≈ p q = F-resp-≈ (p , q)

⊗-resp-≈ˡ : f  h  (f ⊗₁ g)  (h ⊗₁ g)
⊗-resp-≈ˡ p = ⊗-resp-≈ p Equiv.refl

⊗-resp-≈ʳ : g  i  (f ⊗₁ g)  (f ⊗₁ i)
⊗-resp-≈ʳ p = ⊗-resp-≈ Equiv.refl p

_⟩⊗⟨_ : f  h  g  i  (f ⊗₁ g)  (h ⊗₁ i)
_⟩⊗⟨_ = ⊗-resp-≈

refl⟩⊗⟨_ : g  i  (f ⊗₁ g)  (f ⊗₁ i)
refl⟩⊗⟨_ = ⊗-resp-≈ʳ

_⟩⊗⟨refl : f  h  (f ⊗₁ g)  (h ⊗₁ g)
_⟩⊗⟨refl = ⊗-resp-≈ˡ

-- This corresponds to the graphical coherence property of diagrams
-- modelling monoidal categories:
--
--   |        |         |   |
--  [h]      [i]       [h] [i]
--   |        |    ≈    |   |
--  [f]      [g]        |   |
--   |        |         |   |
--                     [f] [g]
--                      |   |

⊗-distrib-over-∘ : ((f  h) ⊗₁ (g  i))  ((f ⊗₁ g)  (h ⊗₁ i))
⊗-distrib-over-∘ = homomorphism

-- Parallel commutation

parallel :  {X₁ X₂ Y₁ Y₂ Z₁ Z₂ W₁ W₂}
           {f₁ : Y₁  W₁} {f₂ : Z₁  W₁} {g₁ : Y₂  W₂} {g₂ : Z₂  W₂}
           {h₁ : X₁  Y₁} {h₂ : X₁  Z₁} {i₁ : X₂  Y₂} {i₂ : X₂  Z₂} 
           f₁  h₁  f₂  h₂  g₁  i₁  g₂  i₂ 
           f₁ ⊗₁ g₁  h₁ ⊗₁ i₁  f₂ ⊗₁ g₂  h₂ ⊗₁ i₂
parallel {f₁ = f₁} {f₂} {g₁} {g₂} {h₁} {h₂} {i₁} {i₂} hyp₁ hyp₂ = begin
  f₁ ⊗₁ g₁  h₁ ⊗₁ i₁      ≈˘⟨ ⊗-distrib-over-∘ 
  (f₁  h₁) ⊗₁ (g₁  i₁)   ≈⟨ hyp₁ ⟩⊗⟨ hyp₂ 
  (f₂  h₂) ⊗₁ (g₂  i₂)   ≈⟨ ⊗-distrib-over-∘ 
  f₂ ⊗₁ g₂  h₂ ⊗₁ i₂      

-- Parallel-to-serial conversions
--
--   |   |        |   |       |   |
--   |   |        |  [g]     [f]  |
--  [f] [g]   =   |   |   =   |   |
--   |   |       [f]  |       |  [g]
--   |   |        |   |       |   |

serialize₁₂ :  {X₁ Y₁ X₂ Y₂} {f : X₁  Y₁} {g : X₂  Y₂} 
              f ⊗₁ g  f ⊗₁ id  id ⊗₁ g
serialize₁₂ {f = f} {g} = begin
  f ⊗₁ g                 ≈˘⟨ identityʳ ⟩⊗⟨ identityˡ 
  (f  id) ⊗₁ (id  g)   ≈⟨ ⊗-distrib-over-∘ 
  f ⊗₁ id  id ⊗₁ g      

serialize₂₁ :  {X₁ Y₁ X₂ Y₂} {f : X₁  Y₁} {g : X₂  Y₂} 
              f ⊗₁ g  id ⊗₁ g  f ⊗₁ id
serialize₂₁ {f = f} {g} = begin
  f ⊗₁ g                 ≈˘⟨ identityˡ ⟩⊗⟨ identityʳ 
  (id  f) ⊗₁ (g  id)   ≈⟨ ⊗-distrib-over-∘ 
  id ⊗₁ g  f ⊗₁ id      

-- Split a composite in the first component
--
--   |   |        |   |       |   |
--  [g]  |       [g]  |      [g] [h]
--   |  [h]   =   |   |   =   |   |
--  [f]  |       [f] [h]     [f]  |
--   |   |        |   |       |   |

split₁ʳ :  {X₁ Y₁ Z₁ X₂ Y₂} {f : Y₁  Z₁} {g : X₁  Y₁} {h : X₂  Y₂} 
          (f  g) ⊗₁ h  f ⊗₁ h  g ⊗₁ id
split₁ʳ {f = f} {g} {h} = begin
  (f  g) ⊗₁ h          ≈˘⟨ refl⟩⊗⟨ identityʳ 
  (f  g) ⊗₁ (h  id)   ≈⟨ ⊗-distrib-over-∘ 
  f ⊗₁ h  g ⊗₁ id      

split₁ˡ :  {X₁ Y₁ Z₁ X₂ Y₂} {f : Y₁  Z₁} {g : X₁  Y₁} {h : X₂  Y₂} 
          (f  g) ⊗₁ h  f ⊗₁ id  g ⊗₁ h
split₁ˡ {f = f} {g} {h} = begin
  (f  g) ⊗₁ h          ≈˘⟨ refl⟩⊗⟨ identityˡ 
  (f  g) ⊗₁ (id  h)   ≈⟨ ⊗-distrib-over-∘ 
  f ⊗₁ id  g ⊗₁ h      

-- Split a composite in the second component
--
--   |   |        |   |       |   |
--   |  [h]       |  [h]     [f] [h]
--  [f]  |    =   |   |   =   |   |
--   |  [g]      [f] [g]      |  [g]
--   |   |        |   |       |   |

split₂ʳ :  {X₁ Y₁ X₂ Y₂ Z₂} {f : X₁  Y₁} {g : Y₂  Z₂} {h : X₂  Y₂} 
          f ⊗₁ (g  h)  f ⊗₁ g  id ⊗₁ h
split₂ʳ {f = f} {g} {h} = begin
  f ⊗₁ (g  h)          ≈˘⟨ identityʳ ⟩⊗⟨refl 
  (f  id) ⊗₁ (g  h)   ≈⟨ ⊗-distrib-over-∘ 
  f ⊗₁ g  id ⊗₁ h      

split₂ˡ :  {X₁ Y₁ X₂ Y₂ Z₂} {f : X₁  Y₁} {g : Y₂  Z₂} {h : X₂  Y₂} 
          f ⊗₁ (g  h)  id ⊗₁ g  f ⊗₁ h
split₂ˡ {f = f} {g} {h} = begin
  f ⊗₁ (g  h)          ≈˘⟨ identityˡ ⟩⊗⟨refl 
  (id  f) ⊗₁ (g  h)   ≈⟨ ⊗-distrib-over-∘ 
  id ⊗₁ g  f ⊗₁ h      

-- The opposite, i.e. merge
merge₂ʳ :   {X₁ Y₁ X₂ Y₂ Z₂} {f : X₁  Y₁} {g : Y₂  Z₂} {h : X₂  Y₂} 
          f ⊗₁ g  id ⊗₁ h  f ⊗₁ (g  h)
merge₂ʳ = Equiv.sym split₂ʳ

merge₂ˡ :  {X₁ Y₁ X₂ Y₂ Z₂} {f : X₁  Y₁} {g : Y₂  Z₂} {h : X₂  Y₂} 
          id ⊗₁ g  f ⊗₁ h  f ⊗₁ (g  h)
merge₂ˡ = Equiv.sym split₂ˡ

-- Combined splitting and re-association.

module _ {X Y Z} {f : X  Z} {g : Y  Z} {h : X  Y} (f≈gh : f  g  h) where

  infixr 4 split₁_⟩∘⟨_ split₂_⟩∘⟨_
  infixl 5 _⟩∘⟨split₁_ _⟩∘⟨split₂_

  split₁_⟩∘⟨_ :  {V W} {i j : V  X ⊗₀ W}  i  j 
                f ⊗₁ id  i  g ⊗₁ id  h ⊗₁ id  j
  split₁_⟩∘⟨_ {_} {_} {i} {j} i≈j = begin
    f ⊗₁ id  i                ≈⟨ f≈gh ⟩⊗⟨refl ⟩∘⟨ i≈j 
    (g  h) ⊗₁ id  j          ≈⟨ split₁ˡ ⟩∘⟨refl 
    (g ⊗₁ id  h ⊗₁ id)  j    ≈⟨ assoc 
    g ⊗₁ id  (h ⊗₁ id  j)    

  split₂_⟩∘⟨_ :  {V W} {i j : V  W ⊗₀ X}  i  j 
                id ⊗₁ f  i  id ⊗₁ g  id ⊗₁ h  j
  split₂_⟩∘⟨_ {_} {_} {i} {j} i≈j = begin
    id ⊗₁ f  i                ≈⟨ refl⟩⊗⟨ f≈gh ⟩∘⟨ i≈j 
    id ⊗₁ (g  h)  j          ≈⟨ split₂ˡ ⟩∘⟨refl 
    (id ⊗₁ g  id ⊗₁ h)  j    ≈⟨ assoc 
    id ⊗₁ g  (id ⊗₁ h  j)    

  _⟩∘⟨split₁_ :  {V W} {i j : Z ⊗₀ W  V}  i  j 
                i  f ⊗₁ id  (j  g ⊗₁ id)  h ⊗₁ id
  _⟩∘⟨split₁_ {_} {_} {i} {j} i≈j = begin
    i  f ⊗₁ id                ≈⟨ i≈j ⟩∘⟨ f≈gh ⟩⊗⟨refl 
    j  (g  h) ⊗₁ id          ≈⟨ refl⟩∘⟨ split₁ˡ 
    j  (g ⊗₁ id  h ⊗₁ id)    ≈⟨ sym-assoc 
    (j  g ⊗₁ id)  h ⊗₁ id    

  _⟩∘⟨split₂_ :  {V W} {i j : W ⊗₀ Z  V}  i  j 
                i  id ⊗₁ f  (j  id ⊗₁ g)  id ⊗₁ h
  _⟩∘⟨split₂_ {_} {_} {i} {j} i≈j = begin
    i  id ⊗₁ f                ≈⟨ i≈j ⟩∘⟨ refl⟩⊗⟨ f≈gh 
    j  id ⊗₁ (g  h)          ≈⟨ refl⟩∘⟨ split₂ˡ 
    j  (id ⊗₁ g  id ⊗₁ h)    ≈⟨ sym-assoc 
    (j  id ⊗₁ g)  id ⊗₁ h    

-- Combined merging and re-association.

module _ {X Y Z} {f : Y  Z} {g : X  Y} {h : X  Z} (fg≈h : f  g  h) where

  infixr 4 merge₁_⟩∘⟨_ merge₂_⟩∘⟨_
  infixl 5 _⟩∘⟨merge₁_ _⟩∘⟨merge₂_

  merge₁_⟩∘⟨_ :  {V W} {i j : V  X ⊗₀ W}  i  j 
                f ⊗₁ id  g ⊗₁ id  i  h ⊗₁ id  j
  merge₁_⟩∘⟨_ i≈j =  (split₁  fg≈h ⟩∘⟨  i≈j)

  merge₂_⟩∘⟨_ :  {V W} {i j : V  W ⊗₀ X}  i  j 
                id ⊗₁ f  id ⊗₁ g  i  id ⊗₁ h  j
  merge₂_⟩∘⟨_ i≈j =  (split₂  fg≈h ⟩∘⟨  i≈j)

  _⟩∘⟨merge₁_ :  {V W} {i j : Z ⊗₀ W  V}  i  j 
                (i  f ⊗₁ id)  g ⊗₁ id  j  h ⊗₁ id
  _⟩∘⟨merge₁_ i≈j =  ( fg≈h ⟩∘⟨split₁  i≈j)

  _⟩∘⟨merge₂_ :  {V W} {i j : W ⊗₀ Z  V}  i  j 
                (i  id ⊗₁ f)  id ⊗₁ g  j  id ⊗₁ h
  _⟩∘⟨merge₂_ i≈j =  ( fg≈h ⟩∘⟨split₂  i≈j)