{-# OPTIONS --safe #-}
module Cubical.Categories.Profunctor.Base where
open import Cubical.Foundations.Prelude hiding (Path)
open import Cubical.Foundations.Structure
open import Cubical.Data.Graph.Base
open import Cubical.Data.Graph.Path
open import Cubical.Categories.Category
open import Cubical.Categories.Functor
open import Cubical.Categories.Instances.Functors
open import Cubical.Categories.NaturalTransformation
open import Cubical.Categories.Constructions.BinProduct
open import Cubical.Categories.Instances.Sets
open import Cubical.Categories.Functors.HomFunctor
module _ (ℓ ℓ' : Level) where
  Cat : Type _
  Cat = Cubical.Categories.Category.Category ℓ ℓ'
  
  
  
  
  
  
  
  
  
  
  PROF⊶ : (C D : Cat) → Category _ _
  PROF⊶ C D = FUNCTOR ((C ^op) ×C D) (SET ℓ')
  PROF⊷ : (C D : Cat) → Category _ _
  PROF⊷ C D = PROF⊶ D C
  record Profunctor⊶ (C D : Cat) : Type (ℓ-max ℓ (ℓ-suc ℓ')) where
    constructor fromFunc
    field
      asFunc : Category.ob (PROF⊶ C D)
    private
      R = asFunc
    module C = Category C
    module D = Category D
    _⋆C_ = C._⋆_
    _⋆D_ = D._⋆_
    Het[_,_] : C.ob → D.ob → Type ℓ'
    Het[ c , d ] = ⟨ asFunc ⟅ c , d ⟆ ⟩
    _⋆L⟨_⟩⋆R_ : ∀ {c c' d' d}
              → (f : C.Hom[ c , c' ])(r : Het[ c' , d' ])(g : D.Hom[ d' , d ])
              → Het[ c , d ]
    f ⋆L⟨ r ⟩⋆R g = Functor.F-hom R (f , g) r
    ⋆L⟨⟩⋆RAssoc : ∀ {c c' c'' d'' d' d}
                → (f : C.Hom[ c , c' ]) (f' : C.Hom[ c' , c'' ])
                  (r : Het[ c'' , d'' ])
                  (g' : D.Hom[ d'' , d' ]) (g : D.Hom[ d' , d ])
                → (f ⋆C f') ⋆L⟨ r ⟩⋆R (g' ⋆D g) ≡ f ⋆L⟨ f' ⋆L⟨ r ⟩⋆R g' ⟩⋆R g
    ⋆L⟨⟩⋆RAssoc f f' r g' g i = Functor.F-seq R (f' , g') (f , g) i r
    ⋆L⟨⟩⋆RId : ∀ {c d} → (r : Het[ c , d ])
             → C.id ⋆L⟨ r ⟩⋆R D.id ≡ r
    ⋆L⟨⟩⋆RId r i = Functor.F-id R i r
    _⋆L_ : {c c' : C.ob} {d : D.ob}
              → C.Hom[ c , c' ]
              → Het[ c' , d ]
              → Het[ c  , d ]
    _⋆L_ f r = f ⋆L⟨ r ⟩⋆R D.id
    infixr 9 _⋆L_
    ⋆LId : ∀ {c d} → (r : Het[ c , d ]) → C.id ⋆L r ≡ r
    ⋆LId = ⋆L⟨⟩⋆RId
    ⋆LAssoc : ∀ {c c' c'' d} → (f : C.Hom[ c , c' ])(f' : C.Hom[ c' , c'' ])(r : Het[ c'' , d ])
            → (f ⋆C f' ⋆L r) ≡ (f ⋆L f' ⋆L r)
    ⋆LAssoc f f' r =
      ((f ⋆C f') ⋆L⟨ r ⟩⋆R D.id)           ≡⟨ (λ i → (f ⋆C f') ⋆L⟨ r ⟩⋆R sym (D.⋆IdL D.id) i) ⟩
      ((f ⋆C f') ⋆L⟨ r ⟩⋆R (D.id ⋆D D.id)) ≡⟨ ⋆L⟨⟩⋆RAssoc f f' r D.id D.id ⟩
      f ⋆L f' ⋆L r ∎
    _⋆R_ : {c : C.ob} {d' d : D.ob}
         → Het[ c , d' ]
         → D [ d' , d ]
         → Het[ c , d ]
    _⋆R_ r g = C.id ⋆L⟨ r ⟩⋆R g
    infixl 9 _⋆R_
    ⋆RId : ∀ {c d} → (r : Het[ c , d ]) → r ⋆R D.id ≡ r
    ⋆RId = ⋆L⟨⟩⋆RId
    ⋆RAssoc : ∀ {c d'' d' d} → (r : Het[ c , d'' ])(g' : D.Hom[ d'' , d' ])(g : D.Hom[ d' , d ])
            → (r ⋆R g' ⋆D g) ≡ (r ⋆R g' ⋆R g)
    ⋆RAssoc r g' g =
      (C.id ⋆L⟨ r ⟩⋆R (g' ⋆D g))           ≡⟨ (λ i → sym (C.⋆IdL C.id) i ⋆L⟨ r ⟩⋆R (g' ⋆D g) ) ⟩
      ((C.id ⋆C C.id) ⋆L⟨ r ⟩⋆R (g' ⋆D g)) ≡⟨ ⋆L⟨⟩⋆RAssoc C.id C.id r g' g ⟩
      (r ⋆R g' ⋆R g) ∎
    ⋆L⋆R-unary-binaryL : ∀ {c c' d' d}
                       → (f : C.Hom[ c , c' ]) (r : Het[ c' , d' ]) (g : D.Hom[ d' , d ])
                       → ((f ⋆L r) ⋆R g) ≡ (f ⋆L⟨ r ⟩⋆R g)
    ⋆L⋆R-unary-binaryL f r g =
      ((f ⋆L r) ⋆R g) ≡⟨ sym (⋆L⟨⟩⋆RAssoc C.id f r D.id g) ⟩
      ((C.id ⋆C f) ⋆L⟨ r ⟩⋆R (D.id ⋆D g)) ≡⟨ (λ i → C.⋆IdL f i ⋆L⟨ r ⟩⋆R D.⋆IdL g i) ⟩
      (f ⋆L⟨ r ⟩⋆R g) ∎
    ⋆L⋆R-unary-binaryR : ∀ {c c' d' d}
                       → (f : C.Hom[ c , c' ]) (r : Het[ c' , d' ]) (g : D.Hom[ d' , d ])
                       → (f ⋆L (r ⋆R g)) ≡ (f ⋆L⟨ r ⟩⋆R g)
    ⋆L⋆R-unary-binaryR f r g =
      (f ⋆L (r ⋆R g))                     ≡⟨ sym (⋆L⟨⟩⋆RAssoc f C.id r g D.id) ⟩
      ((f ⋆C C.id) ⋆L⟨ r ⟩⋆R (g ⋆D D.id)) ≡⟨ (λ i → C.⋆IdR f i ⋆L⟨ r ⟩⋆R D.⋆IdR g i) ⟩
      (f ⋆L⟨ r ⟩⋆R g) ∎
    ⋆L⋆RAssoc : ∀ {c c' d' d} → (f : C.Hom[ c , c' ])(r : Het[ c' , d' ])(g : D.Hom[ d' , d ])
              → ((f ⋆L r) ⋆R g) ≡ (f ⋆L (r ⋆R g))
    ⋆L⋆RAssoc f r g =
      ((f ⋆L r) ⋆R g) ≡⟨ ⋆L⋆R-unary-binaryL f r g ⟩
      f ⋆L⟨ r ⟩⋆R g   ≡⟨ sym (⋆L⋆R-unary-binaryR f r g) ⟩
      (f ⋆L (r ⋆R g)) ∎
  _⊶_ = Profunctor⊶
  Profunctor⊷ : ∀ (C D : Cat) → Type _
  Profunctor⊷ C D = Profunctor⊶ D C
  _⊷_ = Profunctor⊷
  record Homomorphism {C D : Cat} (P Q : C ⊶ D) : Type (ℓ-max ℓ ℓ') where
    module C = Category C
    module D = Category D
    module P = Profunctor⊶ P
    module Q = Profunctor⊶ Q
    _⋆LP⟨_⟩⋆R_ = P._⋆L⟨_⟩⋆R_
    _⋆LQ⟨_⟩⋆R_ = Q._⋆L⟨_⟩⋆R_
    field
      asNatTrans : PROF⊶ C D [ P.asFunc , Q.asFunc ]
    app : ∀ {c d} → P.Het[ c , d ] → Q.Het[ c , d ]
    app {c}{d} p = NatTrans.N-ob asNatTrans (c , d) p
    homomorphism : ∀ {c c' d' d} (f : C.Hom[ c , c' ])(p : P.Het[ c' , d' ])(g : D.Hom[ d' , d ])
               → app (f ⋆LP⟨ p ⟩⋆R g) ≡ (f ⋆LQ⟨ app p ⟩⋆R g)
    homomorphism f p g = λ i → NatTrans.N-hom asNatTrans (f , g) i p
  homomorphism : {C D : Cat} → C ⊶ D → C ⊶ D → Type _
  homomorphism {C} {D} P Q = PROF⊶ C D [ Profunctor⊶.asFunc P , Profunctor⊶.asFunc Q ]
  swap : {C D : Cat} → C ⊶ D → (D ^op) ⊶ (C ^op)
  swap R = fromFunc
    record { F-ob  = λ (d , c) → R.F-ob (c , d)
           ; F-hom = λ (f , g) → R.F-hom (g , f)
           ; F-id  = R.F-id
           ; F-seq = λ (fl , fr) (gl , gr) → R.F-seq (fr , fl) (gr , gl)
           }
    where module R = Functor (Profunctor⊶.asFunc R)
  HomProf : (C : Cat) → C ⊶ C
  HomProf C = fromFunc (HomFunctor C)
  _profF⊶[_,_] : {B C D E : Cat}
             → (R : D ⊶ E)
             → (F : Functor B D)
             → (G : Functor C E)
             → B ⊶ C
  R profF⊶[ F , G ] = fromFunc ((Profunctor⊶.asFunc R) ∘F ((F ^opF) ×F G))
  _Represents_ : {C D : Cat} (F : Functor C D) (R : C ⊶ D) → Type _
  _Represents_ {C}{D} F R =
    NatIso (Profunctor⊶.asFunc (HomProf D profF⊶[ F , Id {C = D} ])) (Profunctor⊶.asFunc R)
  Representable : {C D : Cat} → C ⊶ D → Type _
  Representable {C}{D} R = Σ[ F ∈ Functor C D ] (F Represents R)
  record Representable' {C D : Cat} (R : C ⊶ D) : Type (ℓ-max ℓ (ℓ-suc ℓ')) where
    module R = Profunctor⊶ R
    open R
    field
      F₀             : C.ob → D.ob
      
      unit           : ∀ {c : C.ob} → Het[ c , F₀ c ]
      
      induction      : ∀ {c : C.ob} {d : D.ob} → Het[ c , d ] → D.Hom[ F₀ c , d ]
      
      computation    : ∀ {c : C.ob} {d : D.ob} → (r : Het[ c , d ])
                     → (unit ⋆R induction r) ≡ r
      
      extensionality : ∀ {c d} (f : D.Hom[ F₀ c , d ]) → f ≡ induction (unit ⋆R f)
    weak-extensionality : ∀ {c} → D.id ≡ induction (unit {c = c})
    weak-extensionality =
      D.id                     ≡⟨ extensionality D.id ⟩
      induction (unit ⋆R D.id) ≡⟨ (λ i → induction (⋆RId unit i)) ⟩
      induction unit ∎
    naturality : ∀ {c : C.ob}{d d' : D.ob}(r : Het[ c , d' ]) (k : D.Hom[ d' , d ])
               → (induction r ⋆D k) ≡ induction (r ⋆R k)
    naturality r k =
      induction r ⋆D k                       ≡⟨ extensionality (induction r ⋆D k) ⟩
      induction (unit ⋆R induction r ⋆D k)   ≡⟨ (λ i → induction (⋆RAssoc unit (induction r) k i)) ⟩
      induction ((unit ⋆R induction r) ⋆R k) ≡⟨ (λ i → induction (computation r i ⋆R k)) ⟩
      induction (r ⋆R k) ∎
    F : Functor C D
    F = record
      { F-ob = F₀
      ; F-hom = λ f → induction ( f ⋆L unit)
      ; F-id = induction (C.id ⋆L unit) ≡⟨ (λ i → induction (⋆LId unit i)) ⟩
               induction unit           ≡⟨ sym weak-extensionality ⟩
               D.id ∎
      ; F-seq = λ f g →
        induction ((f ⋆C g) ⋆L unit)                        ≡⟨ (λ i → induction (⋆LAssoc f g unit i)) ⟩
        induction (f ⋆L (g ⋆L unit))                        ≡⟨ (λ i → induction (f ⋆L sym (computation (g ⋆L unit)) i)) ⟩
        induction (f ⋆L (unit ⋆R (induction (g ⋆L unit))))  ≡⟨ (λ i → induction (sym (⋆L⋆RAssoc f unit (induction (g ⋆L unit))) i)) ⟩
        induction ((f ⋆L unit) ⋆R (induction (g ⋆L unit)))  ≡⟨ sym (naturality (f ⋆L unit) (induction (g ⋆L unit))) ⟩
        induction (f ⋆L unit) ⋆D induction (g ⋆L unit) ∎
      }
    module F = Functor F
    unduction : ∀ {c : C.ob} {d : D.ob} → D.Hom[ F₀ c , d ] → Het[ c , d ]
    unduction f = unit ⋆R f
    induction⁻¹ : homomorphism (HomProf D profF⊶[ F , Id ]) R
    induction⁻¹ = natTrans (λ x r → unduction r) λ f⋆g i r → unduction-is-natural (fst f⋆g) (snd f⋆g) r i
      where
      unduction-is-natural : ∀ {c c' d' d}
                           → (f : C.Hom[ c , c' ])(g : D.Hom[ d' , d ])(IP : D.Hom[ F₀ c' , d' ])
                           → unduction ((F ⟪ f ⟫ ⋆D IP) ⋆D g) ≡ f ⋆L⟨ unduction IP ⟩⋆R g
      unduction-is-natural f g IP =
        unit ⋆R ((induction (f ⋆L unit) ⋆D IP) ⋆D g) ≡⟨ (λ i → unit ⋆R D.⋆Assoc (induction (f ⋆L unit)) IP g i) ⟩
        unit ⋆R (induction (f ⋆L unit) ⋆D (IP ⋆D g)) ≡⟨ ⋆RAssoc unit _ (IP ⋆D g) ⟩
        (unit ⋆R induction (f ⋆L unit)) ⋆R (IP ⋆D g) ≡⟨ (λ i → computation (f ⋆L unit) i ⋆R (IP ⋆D g)) ⟩
        (f ⋆L unit) ⋆R IP ⋆D g                       ≡⟨ ⋆L⋆RAssoc f unit (IP ⋆D g) ⟩
        f ⋆L (unit ⋆R IP ⋆D g)                       ≡⟨ (λ i → f ⋆L ⋆RAssoc unit IP g i) ⟩
        f ⋆L ((unit ⋆R IP) ⋆R g)                     ≡⟨ ⋆L⋆R-unary-binaryR f _ g ⟩
        f ⋆L⟨ unit ⋆R IP ⟩⋆R g ∎
    F-represents-R : F Represents R
    F-represents-R = record
                   { trans = induction⁻¹
                   ; nIso =  inductionIso }
      where
      induction-induction⁻¹≡id : ∀ {c d}(IP : D.Hom[ F₀ c , d ]) → induction (unduction IP) ≡ IP
      induction-induction⁻¹≡id IP =
        induction (unit ⋆R IP) ≡⟨ sym (naturality unit IP) ⟩
        induction unit ⋆D IP ≡⟨ (λ i → sym weak-extensionality i ⋆D IP) ⟩
        D.id ⋆D IP               ≡⟨ D.⋆IdL _ ⟩
        IP ∎
      induction⁻¹-induction≡id : ∀ {c d}(r : Het[ c , d ]) → unduction (induction r) ≡ r
      induction⁻¹-induction≡id r = computation r
      inductionIso = λ x →
        isiso induction
              (λ i r → induction⁻¹-induction≡id r i)
              (λ i IP → induction-induction⁻¹≡id IP i)
  Repr⇒Repr' : ∀ {C D} (R : C ⊶ D) → Representable R → Representable' R
  Repr⇒Repr' {C}{D} R (F , F-repr-R) = record
                                     { F₀ = F.F-ob
                                     ; unit = unduction D.id
                                     ; induction = induction
                                     ; computation = computation
                                     ; extensionality = extensionality
                                     }
    where
    module R = Profunctor⊶ R
    open R
    module F = Functor F
    module F-repr-R = NatIso F-repr-R
    induction : ∀ {c d} → Het[ c , d ] → D.Hom[ F.F-ob c , d ]
    induction r = isIso.inv (NatIso.nIso F-repr-R (_ , _)) r
    unduction-homo : Homomorphism (HomProf D profF⊶[ F , Id ]) R
    unduction-homo = record { asNatTrans = F-repr-R.trans }
    module unduction-homo = Homomorphism unduction-homo
    unduction : ∀ {c d} → D.Hom[ F.F-ob c , d ] → Het[ c , d ]
    unduction = Homomorphism.app unduction-homo
    computation : ∀ {c d} (r : Het[ c , d ]) → unduction D.id ⋆R induction r ≡ r
    computation r =
      (C.id ⋆L⟨ unduction D.id ⟩⋆R induction r)         ≡⟨ sym (unduction-homo.homomorphism C.id  D.id (induction r)) ⟩
      unduction ((F.F-hom C.id ⋆D D.id) ⋆D induction r) ≡⟨ (λ i → unduction (D.⋆IdR (F.F-hom C.id) i ⋆D induction r)) ⟩
      unduction (F.F-hom C.id ⋆D induction r)           ≡⟨ (λ i → unduction (F.F-id i ⋆D induction r)) ⟩
      unduction (D.id ⋆D (induction r))                 ≡⟨ (λ i → unduction (D.⋆IdL (induction r) i)) ⟩
      unduction (induction r) ≡⟨ (λ i → isIso.sec (NatIso.nIso F-repr-R _) i r) ⟩
      r ∎
    extensionality : ∀ {c d} (f : D.Hom[ F.F-ob c , d ]) → f ≡ induction (unduction D.id ⋆R f)
    extensionality f =
      f ≡⟨ sym (λ i → isIso.ret (NatIso.nIso F-repr-R _) i f) ⟩
      induction (unduction f)                             ≡⟨ (λ i → induction (unduction (sym (D.⋆IdL f) i))) ⟩
      induction (unduction (D.id ⋆D f))                   ≡⟨ (λ i → induction (unduction (sym (D.⋆IdL D.id) i ⋆D f))) ⟩
      induction (unduction ((D.id ⋆D D.id) ⋆D f))         ≡⟨ (λ i → induction (unduction ((sym F.F-id i ⋆D D.id) ⋆D f))) ⟩
      induction (unduction ((F.F-hom C.id ⋆D D.id) ⋆D f)) ≡⟨ (λ i → induction (unduction-homo.homomorphism C.id D.id f i)) ⟩
      induction (C.id ⋆L⟨ unduction D.id ⟩⋆R f) ∎
  Repr'⇒Repr : ∀ {C D} (R : C ⊶ D) → Representable' R → Representable R
  Repr'⇒Repr R R-representable =
    (Representable'.F R-representable) , Representable'.F-represents-R R-representable