{-

  Explicit "redundant" definition of a bifunctor F : C , D → E.
  Includes all 3 of
  1. A "parallel action" f × g
  2. Two "separate actions" f × d and c × g
  Plus axioms that say
  1. The actions are functorial
  2. and that they agree

  The full definition includes some redundant *axioms* to make it
  convenient to use, so we include two alternative formulations to
  make it easier to prove.

-}

{-# OPTIONS --safe #-}
module Cubical.Categories.Bifunctor.Redundant where

open import Cubical.Foundations.Prelude hiding (Path)

open import Cubical.Categories.Category
open import Cubical.Categories.Functor
open import Cubical.Categories.NaturalTransformation
open import Cubical.Categories.Constructions.BinProduct hiding (Fst; Snd; Sym)
open import Cubical.Categories.Instances.Sets
open import Cubical.Categories.Instances.Functors
import Cubical.Categories.Bifunctor as Separate

open import Cubical.Data.Sigma

private
  variable
     ℓ' ℓc ℓc' ℓd ℓd' ℓe ℓe' : Level

open Category
open Functor
open NatTrans

-- This definition is the most convenient to use
-- But its axioms are redundant as well as its
record Bifunctor (C : Category ℓc ℓc')
                 (D : Category ℓd ℓd')
                 (E : Category ℓe ℓe')
       : Type (ℓ-max ℓc (ℓ-max ℓc' (ℓ-max ℓd (ℓ-max ℓd' (ℓ-max ℓe ℓe'))))) where
  field
    Bif-ob : C .ob  D .ob  E .ob
    Bif-homL :  {c c'}  (f : C [ c , c' ])   d
              E [ Bif-ob c d , Bif-ob c' d ]
    Bif-homR :  {d d'} c  (g : D [ d , d' ])  E [ Bif-ob c d , Bif-ob c d' ]
    Bif-hom× :  {c c' d d'} (f : C [ c , c' ])(g : D [ d , d' ])
              E [ Bif-ob c d , Bif-ob c' d' ]
    Bif-L-id :  {c d}  Bif-homL (C .id {c}) d  E .id
    Bif-L-seq :  {c c' c'' d} (f : C [ c , c' ])(f' : C [ c' , c'' ])
              Bif-homL (f ⋆⟨ C  f') d  Bif-homL f d ⋆⟨ E  Bif-homL f' d
    Bif-R-id :  {c d}  Bif-homR c (D .id {d})  E .id
    Bif-R-seq :  {c d d' d''} (g : D [ d , d' ])(g' : D [ d' , d'' ])
              Bif-homR c (g ⋆⟨ D  g')  Bif-homR c g ⋆⟨ E  Bif-homR c g'
    Bif-×-id :  {c d}  Bif-hom× (C .id {c}) (D .id {d})  E .id
    Bif-×-seq :  {c c' c'' d d' d''}
               (f : C [ c , c' ])(f' : C [ c' , c'' ])
               (g : D [ d , d' ])(g' : D [ d' , d'' ])
              Bif-hom× (f ⋆⟨ C  f') (g ⋆⟨ D  g')
              Bif-hom× f g ⋆⟨ E  Bif-hom× f' g'
    Bif-L×-agree :  {c c' d}  (f : C [ c , c' ])
                  Bif-homL f d  Bif-hom× f (D .id)
    Bif-R×-agree :  {c d d'}  (g : D [ d , d' ])
                  Bif-homR c g  Bif-hom× (C .id) g
    Bif-LR-fuse :  {c c' d d'}  (f : C [ c , c' ]) (g : D [ d , d' ])
                Bif-homL f d ⋆⟨ E  Bif-homR c' g
                Bif-hom× f g

    Bif-RL-fuse :  {c c' d d'}  (f : C [ c , c' ]) (g : D [ d , d' ])
                Bif-homR c g ⋆⟨ E  Bif-homL f d'
                Bif-hom× f g

record BifunctorParAx (C : Category ℓc ℓc')
                  (D : Category ℓd ℓd')
                  (E : Category ℓe ℓe')
       : Type (ℓ-max ℓc (ℓ-max ℓc' (ℓ-max ℓd (ℓ-max ℓd' (ℓ-max ℓe ℓe'))))) where
  field
    Bif-ob : C .ob  D .ob  E .ob
    Bif-homL :  {c c'}  (f : C [ c , c' ])   d
              E [ Bif-ob c d , Bif-ob c' d ]
    Bif-homR :  {d d'} c  (g : D [ d , d' ])  E [ Bif-ob c d , Bif-ob c d' ]
    Bif-hom× :  {c c' d d'} (f : C [ c , c' ])(g : D [ d , d' ])
              E [ Bif-ob c d , Bif-ob c' d' ]
    Bif-×-id :  {c d}  Bif-hom× (C .id {c}) (D .id {d})  E .id
    Bif-×-seq :  {c c' c'' d d' d''}
               (f : C [ c , c' ])(f' : C [ c' , c'' ])
               (g : D [ d , d' ])(g' : D [ d' , d'' ])
              Bif-hom× (f ⋆⟨ C  f') (g ⋆⟨ D  g')
              Bif-hom× f g ⋆⟨ E  Bif-hom× f' g'
    Bif-L×-agree :  {c c' d}  (f : C [ c , c' ])
                  Bif-homL f d  Bif-hom× f (D .id)
    Bif-R×-agree :  {c d d'}  (g : D [ d , d' ])
                  Bif-homR c g  Bif-hom× (C .id) g

record BifunctorSepAx (C : Category ℓc ℓc')
                  (D : Category ℓd ℓd')
                  (E : Category ℓe ℓe')
       : Type (ℓ-max ℓc (ℓ-max ℓc' (ℓ-max ℓd (ℓ-max ℓd' (ℓ-max ℓe ℓe'))))) where
  field
    Bif-ob : C .ob  D .ob  E .ob

    Bif-homL :  {c c'}  (f : C [ c , c' ])   d
              E [ Bif-ob c d , Bif-ob c' d ]
    Bif-L-id :  {c d}  Bif-homL (C .id {c}) d  E .id
    Bif-L-seq :  {c c' c'' d} (f : C [ c , c' ])(f' : C [ c' , c'' ])
              Bif-homL (f ⋆⟨ C  f') d  Bif-homL f d ⋆⟨ E  Bif-homL f' d

    Bif-homR :  {d d'} c  (g : D [ d , d' ])  E [ Bif-ob c d , Bif-ob c d' ]
    Bif-R-id :  {c d}  Bif-homR c (D .id {d})  E .id
    Bif-R-seq :  {c d d' d''} (g : D [ d , d' ])(g' : D [ d' , d'' ])
              Bif-homR c (g ⋆⟨ D  g')  Bif-homR c g ⋆⟨ E  Bif-homR c g'

    Bif-hom× :  {c c' d d'} (f : C [ c , c' ])(g : D [ d , d' ])
              E [ Bif-ob c d , Bif-ob c' d' ]
    Bif-LR-fuse :  {c c' d d'}  (f : C [ c , c' ]) (g : D [ d , d' ])
                Bif-homL f d ⋆⟨ E  Bif-homR c' g
                Bif-hom× f g
    Bif-RL-fuse :  {c c' d d'}  (f : C [ c , c' ]) (g : D [ d , d' ])
                Bif-homR c g ⋆⟨ E  Bif-homL f d'
                Bif-hom× f g

-- A version of bifunctor that only requires the separate actions and
-- constructs the joint action from them arbitrarily.
record BifunctorSep (C : Category ℓc ℓc')
                  (D : Category ℓd ℓd')
                  (E : Category ℓe ℓe')
       : Type (ℓ-max ℓc (ℓ-max ℓc' (ℓ-max ℓd (ℓ-max ℓd' (ℓ-max ℓe ℓe'))))) where
  field
    Bif-ob : C .ob  D .ob  E .ob

    Bif-homL :  {c c'}  (f : C [ c , c' ])   d
              E [ Bif-ob c d , Bif-ob c' d ]
    Bif-L-id :  {c d}  Bif-homL (C .id {c}) d  E .id
    Bif-L-seq :  {c c' c'' d} (f : C [ c , c' ])(f' : C [ c' , c'' ])
              Bif-homL (f ⋆⟨ C  f') d  Bif-homL f d ⋆⟨ E  Bif-homL f' d

    Bif-homR :  {d d'} c  (g : D [ d , d' ])  E [ Bif-ob c d , Bif-ob c d' ]
    Bif-R-id :  {c d}  Bif-homR c (D .id {d})  E .id
    Bif-R-seq :  {c d d' d''} (g : D [ d , d' ])(g' : D [ d' , d'' ])
              Bif-homR c (g ⋆⟨ D  g')  Bif-homR c g ⋆⟨ E  Bif-homR c g'

    SepBif-RL-commute :  {c c' d d'}  (f : C [ c , c' ]) (g : D [ d , d' ])
                Bif-homR c g ⋆⟨ E  Bif-homL f d'
                Bif-homL f d ⋆⟨ E  Bif-homR c' g

-- A version of bifunctor that only requires the parallel action and
-- constructs the joint acions using id. This is definitionally
-- isomorphic to a functor out of the ordinary cartesian product of C
-- and D.
record BifunctorPar (C : Category ℓc ℓc')
                  (D : Category ℓd ℓd')
                  (E : Category ℓe ℓe')
       : Type (ℓ-max ℓc (ℓ-max ℓc' (ℓ-max ℓd (ℓ-max ℓd' (ℓ-max ℓe ℓe'))))) where
  field
    Bif-ob : C .ob  D .ob  E .ob
    Bif-hom× :  {c c' d d'} (f : C [ c , c' ])(g : D [ d , d' ])
              E [ Bif-ob c d , Bif-ob c' d' ]
    Bif-×-id :  {c d}  Bif-hom× (C .id {c}) (D .id {d})  E .id
    Bif-×-seq :  {c c' c'' d d' d''}
               (f : C [ c , c' ])(f' : C [ c' , c'' ])
               (g : D [ d , d' ])(g' : D [ d' , d'' ])
              Bif-hom× (f ⋆⟨ C  f') (g ⋆⟨ D  g')
              Bif-hom× f g ⋆⟨ E  Bif-hom× f' g'

private
  variable
    C D E : Category  ℓ'

open Bifunctor
open BifunctorParAx
open BifunctorSepAx
open BifunctorSep

mkBifunctorSepAx : BifunctorSepAx C D E  Bifunctor C D E
mkBifunctorSepAx {C = C}{D = D}{E = E} F = G where
  G : Bifunctor C D E
  G .Bif-ob = F .Bif-ob
  G .Bif-homL = F .Bif-homL
  G .Bif-homR = F .Bif-homR
  G .Bif-hom× = F .Bif-hom×
  G .Bif-L-id = F .Bif-L-id
  G .Bif-L-seq = F .Bif-L-seq
  G .Bif-R-id = F .Bif-R-id
  G .Bif-R-seq = F .Bif-R-seq
  G .Bif-LR-fuse = F .Bif-LR-fuse
  G .Bif-RL-fuse = F .Bif-RL-fuse
  G .Bif-×-id = sym (F .Bif-LR-fuse _ _)
     cong₂ (seq' E) (F .Bif-L-id) (F .Bif-R-id)
     E .⋆IdR _
  G .Bif-×-seq f f' g g' =
    sym (F .Bif-LR-fuse _ _)
     cong₂ (seq' E) (F .Bif-L-seq _ _) (F .Bif-R-seq _ _)
     E .⋆Assoc _ _ _
     cong₂ (seq' E) refl
          (sym (E .⋆Assoc _ _ _)
           cong₂ (comp' E) refl (F .Bif-LR-fuse _ _  sym (F .Bif-RL-fuse _ _))
           E .⋆Assoc _ _ _
           cong₂ (seq' E) refl (F .Bif-LR-fuse _ _))
          -- ∙ {!!})
     sym (E .⋆Assoc _ _ _)
     cong₂ (comp' E) refl (F .Bif-LR-fuse _ _)

  G .Bif-L×-agree f =
    sym (E .⋆IdR _)
     cong₂ (seq' E) refl (sym (F .Bif-R-id))
     F .Bif-LR-fuse _ _
  G .Bif-R×-agree g =
    sym (E .⋆IdL _)
     cong₂ (comp' E) refl (sym (F .Bif-L-id))
     F .Bif-LR-fuse _ _

mkBifunctorSep : BifunctorSep C D E  Bifunctor C D E
mkBifunctorSep {C = C}{D = D}{E = E} F = mkBifunctorSepAx G where
  G : BifunctorSepAx C D E
  G .Bif-ob = F .Bif-ob
  G .Bif-homL = F .Bif-homL
  G .Bif-L-id = F .Bif-L-id
  G .Bif-L-seq = F .Bif-L-seq
  G .Bif-homR = F .Bif-homR
  G .Bif-R-id = F .Bif-R-id
  G .Bif-R-seq = F .Bif-R-seq
  G .Bif-hom× f g = seq' E (F .Bif-homL f _) (F .Bif-homR _ g)
  G .Bif-LR-fuse f g = refl
  G .Bif-RL-fuse f g = F .SepBif-RL-commute f g

mkBifunctorParAx : BifunctorParAx C D E  Bifunctor C D E
mkBifunctorParAx {C = C}{D = D}{E = E} F = G where
  open BifunctorParAx F
  open Bifunctor
  G : Bifunctor C D E
  G .Bif-ob = F .Bif-ob
  G .Bif-homL = F .Bif-homL
  G .Bif-homR = F .Bif-homR
  G .Bif-hom× = F .Bif-hom×
  G .Bif-×-id = F .Bif-×-id
  G .Bif-×-seq = F .Bif-×-seq
  G .Bif-L×-agree = F .Bif-L×-agree
  G .Bif-R×-agree = F .Bif-R×-agree

  G .Bif-L-id = F .Bif-L×-agree _  F .Bif-×-id
  G .Bif-L-seq f f' = F .Bif-L×-agree _
     cong₂ (F .Bif-hom×) refl (sym (D .⋆IdR (D .id)))
     F .Bif-×-seq f f' (D .id) (D .id)
     cong₂ (seq' E) (sym (F .Bif-L×-agree _)) (sym (F .Bif-L×-agree _))
  G .Bif-R-id = F .Bif-R×-agree _  F .Bif-×-id
  G .Bif-R-seq g g' = G .Bif-R×-agree _
     cong₂ (F .Bif-hom×) (sym (C .⋆IdR (C .id))) refl
     F .Bif-×-seq _ _ _ _
     cong₂ (seq' E) (sym (F .Bif-R×-agree _)) (sym (F .Bif-R×-agree _))

  G .Bif-LR-fuse f g =
    cong₂ (seq' E) (F .Bif-L×-agree _) (F .Bif-R×-agree _)
     sym (F .Bif-×-seq _ _ _ _)
     cong₂ (F .Bif-hom×) (C .⋆IdR _) (D .⋆IdL _)
  G .Bif-RL-fuse f g =
    cong₂ (seq' E) (F .Bif-R×-agree _) (F .Bif-L×-agree _)
     sym (F .Bif-×-seq _ _ _ _)
     cong₂ (F .Bif-hom×) (C .⋆IdL _) (D .⋆IdR _)

mkBifunctorPar : BifunctorPar C D E  Bifunctor C D E
mkBifunctorPar {C = C}{D = D}{E = E} F = mkBifunctorParAx G where
  module F = BifunctorPar F
  G : BifunctorParAx C D E
  G .Bif-ob = F.Bif-ob
  G .Bif-hom× = F.Bif-hom×
  G .Bif-×-id = F.Bif-×-id
  G .Bif-×-seq = F.Bif-×-seq
  G .Bif-homL f d = F.Bif-hom× f (D .id)
  G .Bif-homR c g = F.Bif-hom× (C .id) g
  G .Bif-L×-agree f = refl
  G .Bif-R×-agree g = refl

open Bifunctor
open BifunctorParAx
open BifunctorPar
open BifunctorSepAx
open BifunctorSep
-- action on objects
infix 30 _⟅_⟆b
_⟅_⟆b : (F : Bifunctor C D E)
      C .ob × D .ob
      E .ob
F  c , d ⟆b = Bif-ob F c d

-- actions on morphisms
infix 30 _⟪_⟫l
-- same infix level as on objects since these will
-- never be used in the same context
infix 30 _⟪_⟫r

-- same infix level as on objects since these will
-- never be used in the same context
infix 30 _⟪_⟫×

_⟪_⟫l : (F : Bifunctor C D E)
       {c c' d}
      C [ c , c' ]
      E [(F  c , d ⟆b) , (F  c' , d ⟆b)]
F  f ⟫l = Bif-homL F f _

_⟪_⟫r : (F : Bifunctor C D E)
       {c d d'}
      D [ d , d' ]
      E [(F  c , d ⟆b) , (F  c , d' ⟆b)]
F  f ⟫r = Bif-homR F _ f

_⟪_⟫× : (F : Bifunctor C D E)
        {c c' d d'}
       C [ c , c' ] × D [ d , d' ]
       E [ F  c , d ⟆b , F  c' , d' ⟆b ]
F  f , g ⟫× = F .Bif-hom× f g

Bif-RL-commute
  : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}{E : Category ℓe ℓe'}
   (F : Bifunctor C D E)
    {c c' d d'}  (f : C [ c , c' ]) (g : D [ d , d' ])
   F  g ⟫r ⋆⟨ E  F  f ⟫l  F  f ⟫l ⋆⟨ E  F  g ⟫r
Bif-RL-commute F f g =
  F .Bif-RL-fuse f g  sym (F .Bif-LR-fuse f g)

-- Bif ×L commute that × commutes with homL
-- Bif L×  that × commutes with hom× ⋆ homL ≡ hom×

Bif-×L-fuse
  : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}{E : Category ℓe ℓe'}
   (F : Bifunctor C D E)
    {c c' c''}{d d'}(f : C [ c , c' ])(f' : C [ c' , c'' ])
    (g : D [ d , d' ])
   F  f , g ⟫× ⋆⟨ E  F  f' ⟫l  F  (f ⋆⟨ C  f') , g ⟫×
Bif-×L-fuse {D = D}{E = E} F f f' g =
  cong₂ (seq' E) refl (F .Bif-L×-agree f')
   sym (F .Bif-×-seq f f' g _)
   cong₂ (F .Bif-hom×) refl (D .⋆IdR g)

Bif-L×-fuse
  : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}{E : Category ℓe ℓe'}
   (F : Bifunctor C D E)
    {c c' c''}{d d'}(f : C [ c , c' ])(f' : C [ c' , c'' ])
    (g : D [ d , d' ])
   F  f ⟫l ⋆⟨ E  F  f' , g ⟫×  F  (f ⋆⟨ C  f') , g ⟫×
Bif-L×-fuse {D = D}{E = E} F f f' g =
  cong₂ (seq' E) (F .Bif-L×-agree f) refl
   sym (F .Bif-×-seq f f' (D .id) g)
   cong₂ (F .Bif-hom×) refl (D .⋆IdL g)

Bif-R×-fuse
  : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}{E : Category ℓe ℓe'}
   (F : Bifunctor C D E)
    {c c' d d' d''} (f : C [ c , c' ])
    (g : D [ d , d' ]) (g' : D [ d' , d'' ])
   F  g ⟫r ⋆⟨ E  F  f , g' ⟫×
     F  f , (g ⋆⟨ D  g')⟫×
Bif-R×-fuse {C = C}{E = E} F f g g' =
  cong₂ (comp' E) refl (F .Bif-R×-agree g)
   sym (F .Bif-×-seq _ f g g')
   cong₂ (F .Bif-hom×) (C .⋆IdL f) refl

Bif-×R-fuse
  : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}{E : Category ℓe ℓe'}
   (F : Bifunctor C D E)
    {c c' d d' d''} (f : C [ c , c' ])
    (g : D [ d , d' ]) (g' : D [ d' , d'' ])
   F  f , g ⟫× ⋆⟨ E  F  g' ⟫r
     F  f , (g ⋆⟨ D  g')⟫×
Bif-×R-fuse {C = C}{E = E} F f g g' =
  cong₂ (seq' E) refl (F .Bif-R×-agree g')
   sym (F .Bif-×-seq f _ g g')
   cong₂ (F .Bif-hom×) (C .⋆IdR _) refl

Fst : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}
     Bifunctor C D C
Fst {C = C}{D = D} = mkBifunctorSepAx Fst' where
  Fst' : BifunctorSepAx C D C
  Fst' .Bif-ob c d = c
  Fst' .Bif-homL f d = f
  Fst' .Bif-L-id = refl
  Fst' .Bif-L-seq f f' = refl
  Fst' .Bif-homR c g = C .id
  Fst' .Bif-R-id = refl
  Fst' .Bif-R-seq g g' = sym (C .⋆IdL _)
  Fst' .Bif-hom× f g = f
  Fst' .Bif-LR-fuse f g = C .⋆IdR f
  Fst' .Bif-RL-fuse f g = C .⋆IdL f


Snd : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}
     Bifunctor C D D
Snd {C = C}{D = D} = mkBifunctorSepAx Snd' where
  Snd' : BifunctorSepAx C D D
  Snd' .Bif-ob c d = d
  Snd' .Bif-homL f d = D .id
  Snd' .Bif-L-id = refl
  Snd' .Bif-L-seq f f' = sym (D .⋆IdL _)
  Snd' .Bif-homR c g = g
  Snd' .Bif-R-id = refl
  Snd' .Bif-R-seq g g' = refl
  Snd' .Bif-hom× f g = g
  Snd' .Bif-LR-fuse f g = D .⋆IdL g
  Snd' .Bif-RL-fuse f g = D .⋆IdR g

Sym : {C : Category ℓc ℓc'}{D : Category ℓd ℓd'}{E : Category ℓe ℓe'}
     Bifunctor C D E  Bifunctor D C E
Sym {C = C}{D = D}{E = E} F = mkBifunctorParAx Sym' where
  Sym' : BifunctorParAx D C E
  Sym' .Bif-ob d c = F  c , d ⟆b
  Sym' .Bif-homL g c = F  g ⟫r
  Sym' .Bif-homR d f = F  f ⟫l
  Sym' .Bif-hom× g f = F  f , g ⟫×
  Sym' .Bif-×-id = F .Bif-×-id
  Sym' .Bif-×-seq f f' g g' = F .Bif-×-seq g g' f f'
  Sym' .Bif-L×-agree = F .Bif-R×-agree
  Sym' .Bif-R×-agree = F .Bif-L×-agree

private
  variable
    C' D' E' : Category  ℓ'

appL : (F : Bifunctor C D E) (c : C .ob)  Functor D E
appL F c .F-ob d = F  c , d ⟆b
appL F c .F-hom g = F  g ⟫r
appL F c .F-id = F .Bif-R-id
appL F c .F-seq g g' = F .Bif-R-seq g g'

appR : (F : Bifunctor C D E) (d : D .ob)  Functor C E
appR F d .F-ob c = F  c , d ⟆b
appR F d .F-hom f = F  f ⟫l
appR F d .F-id = F .Bif-L-id
appR F d .F-seq f f' = F .Bif-L-seq f f'

compL : (F : Bifunctor C' D E) (G : Functor C C')  Bifunctor C D E
compL {D = D}{E = E}{C = C} F G = mkBifunctorSepAx B where
  B : BifunctorSepAx C D E
  B .Bif-ob c d = F  G  c   , d ⟆b
  B .Bif-homL f d = F  G  f  ⟫l
  B .Bif-L-id {d = d} = ((appR F d) ∘F G) .F-id
  B .Bif-L-seq {d = d} f f' = ((appR F d) ∘F G) .F-seq f f'
  B .Bif-homR c g = F  g ⟫r
  B .Bif-R-id = F .Bif-R-id
  B .Bif-R-seq = F .Bif-R-seq
  B .Bif-hom× f g = F  G  f  , g ⟫×
  B .Bif-LR-fuse f = F .Bif-LR-fuse (G  f )
  B .Bif-RL-fuse f = F .Bif-RL-fuse (G  f )

compR : (F : Bifunctor C D' E) (G : Functor D D')  Bifunctor C D E
compR {C = C}{E = E}{D = D} F G = mkBifunctorSepAx B where
  B : BifunctorSepAx C D E
  B .Bif-ob c d = F  c , G  d  ⟆b
  B .Bif-homL f d = F  f ⟫l
  B .Bif-L-id = F .Bif-L-id
  B .Bif-L-seq = F .Bif-L-seq
  B .Bif-homR c g = F  G  g  ⟫r
  B .Bif-R-id {c = c} = ((appL F c) ∘F G) .F-id
  B .Bif-R-seq {c = c} = ((appL F c) ∘F G) .F-seq
  B .Bif-hom× f g = F  f , G  g  ⟫×
  B .Bif-LR-fuse f g = F .Bif-LR-fuse f (G  g )
  B .Bif-RL-fuse f g = F .Bif-RL-fuse f (G  g )

compF : (F : Functor E E') (G : Bifunctor C D E)  Bifunctor C D E'
compF {E = E}{E' = E'}{C = C}{D = D} F  G = mkBifunctorParAx B where
  B : BifunctorParAx C D E'
  B .Bif-ob c d = F  G  c , d ⟆b 
  B .Bif-homL f d = F  G  f ⟫l 
  B .Bif-homR c g = F  G  g ⟫r 
  B .Bif-hom× f g = F  G  f , g ⟫× 
  B .Bif-×-id = cong (F ⟪_⟫) (G .Bif-×-id)  F .F-id
  B .Bif-×-seq f f' g g' = cong (F ⟪_⟫) (G .Bif-×-seq f f' g g')
     F .F-seq _ _
  B .Bif-L×-agree f = cong (F ⟪_⟫) (G .Bif-L×-agree f)
  B .Bif-R×-agree g = cong (F ⟪_⟫) (G .Bif-R×-agree g)

infixl 30 compL
infixl 30 compR
infixr 30 compF
infixl 30 compLR'

syntax compL F G = F ∘Fl G
syntax compR F G = F ∘Fr G
syntax compF F G = F ∘Fb G

-- | This includes an arbitrary choice, the action on objects should
-- | be definitionally equal to the other order, but the proofs are
-- | affected.
compLR : (F : Bifunctor C' D' E) (G : Functor C C')(H : Functor D D')
        Bifunctor C D E
compLR F G H = F ∘Fl G ∘Fr H

compLR' : (F : Bifunctor C' D' E) (GH : Functor C C' × Functor D D')
         Bifunctor C D E
compLR' F GH = compLR F (GH .fst) (GH .snd)

syntax compLR' F GH = F ∘Flr GH

-- For Hom we use the Seperate actions formulation because there is no
-- "nice" way to do triple composition (at least with this definition
-- of category).
HomBif : (C : Category  ℓ')  Bifunctor (C ^op) C (SET ℓ')
HomBif C = mkBifunctorSep Hom where
  Hom : BifunctorSep (C ^op) C (SET _)
  Hom .Bif-ob c c' = (C [ c , c' ]) , C .isSetHom
  Hom .Bif-homL f c' f' = f ⋆⟨ C  f'
  Hom .Bif-L-id = funExt (C .⋆IdL)
  Hom .Bif-L-seq f' f = funExt  f''  C .⋆Assoc f f' f'')
  Hom .Bif-homR c f' f = f ⋆⟨ C  f'
  Hom .Bif-R-id = funExt (C .⋆IdR)
  Hom .Bif-R-seq f' f'' = funExt λ f  sym (C .⋆Assoc f f' f'')
  Hom .SepBif-RL-commute f g = funExt  h  sym (C .⋆Assoc f h g))

BifunctorToParFunctor : Bifunctor C D E  Functor (C ×C D) E
BifunctorToParFunctor F .F-ob (c , d) = F .Bif-ob c d
BifunctorToParFunctor F .F-hom (f , g) = F .Bif-hom× f g
BifunctorToParFunctor F .F-id = F .Bif-×-id
BifunctorToParFunctor F .F-seq f g = F .Bif-×-seq _ _ _ _

CurriedToBifunctor : Functor C (FUNCTOR D E)  Bifunctor C D E
CurriedToBifunctor F = mkBifunctorSep G where
  G : BifunctorSep _ _ _
  G .Bif-ob c d = F  c   d 
  G .Bif-homL f d = F  f   d 
  G .Bif-homR c g = F  c   g 
  G .Bif-L-id {d = d} = (cong (_⟦ d ) (F .F-id))
  G .Bif-L-seq f f' = (cong (_⟦ _ ) (F .F-seq f f'))
  G .Bif-R-id = (F  _ ) .F-id
  G .Bif-R-seq g g' = (F  _ ) .F-seq g g'
  G .SepBif-RL-commute f g = (F  f ) .N-hom g

CurryBifunctor : Bifunctor C D E  Functor C (FUNCTOR D E)
CurryBifunctor F .F-ob c = appL F c
CurryBifunctor F .F-hom f .N-ob d = appR F d .F-hom f
CurryBifunctor F .F-hom f .N-hom g = Bif-RL-commute F f g
CurryBifunctor F .F-id = makeNatTransPath (funExt λ d  F .Bif-L-id)
CurryBifunctor F .F-seq _ _ = makeNatTransPath (funExt λ d  F .Bif-L-seq _ _)

open Separate.Bifunctor
ForgetPar : Bifunctor C D E  Separate.Bifunctor C D E
ForgetPar F .Bif-ob = F .Bif-ob
ForgetPar F .Bif-homL = F .Bif-homL
ForgetPar F .Bif-homR = F .Bif-homR
ForgetPar F .Bif-idL = F .Bif-L-id
ForgetPar F .Bif-idR = F .Bif-R-id
ForgetPar F .Bif-seqL = F .Bif-L-seq
ForgetPar F .Bif-seqR = F .Bif-R-seq
ForgetPar F .Bif-assoc f g = sym (Bif-RL-commute F f g)

_^opBif : Bifunctor C D E
         Bifunctor (C ^op) (D ^op) (E ^op)
_^opBif {C = C}{D = D}{E = E} F = mkBifunctorParAx G where
  module F = Bifunctor F
  G : BifunctorParAx (C ^op) (D ^op) (E ^op)
  G .Bif-ob = F.Bif-ob
  G .Bif-homL = F.Bif-homL
  G .Bif-homR = F.Bif-homR
  G .Bif-hom× = F.Bif-hom×
  G .Bif-×-id = F.Bif-×-id
  G .Bif-×-seq f f' g g' = F.Bif-×-seq f' f g' g
  G .Bif-L×-agree = F.Bif-L×-agree
  G .Bif-R×-agree = F.Bif-R×-agree