{-# OPTIONS --safe #-}
module Cubical.Categories.Category.Precategory where

open import Cubical.Foundations.Prelude
open import Cubical.Data.Sigma renaming (_×_ to _×'_)

open import Cubical.Foundations.Pointed hiding ( ; id)

private
  variable
     ℓ' : Level

-- Precategories
record Precategory  ℓ' : Type (ℓ-suc (ℓ-max  ℓ')) where
  -- no-eta-equality ; NOTE: need eta equality for `opop`
  field
    ob : Type 
    Hom[_,_] : ob  ob  Type ℓ'
    id   :  {x}  Hom[ x , x ]
    _⋆_  :  {x y z} (f : Hom[ x , y ]) (g : Hom[ y , z ])  Hom[ x , z ]
    ⋆IdL :  {x y} (f : Hom[ x , y ])  id  f  f
    ⋆IdR :  {x y} (f : Hom[ x , y ])  f  id  f
    ⋆Assoc :  {u v w x} (f : Hom[ u , v ]) (g : Hom[ v , w ]) (h : Hom[ w , x ])
       (f  g)  h  f  (g  h)

  -- composition: alternative to diagramatic order
  _∘_ :  {x y z} (g : Hom[ y , z ]) (f : Hom[ x , y ])  Hom[ x , z ]
  g  f = f  g

open Precategory

-- Helpful syntax/notation
_[_,_] : (C : Precategory  ℓ')  (x y : C .ob)  Type ℓ'
_[_,_] = Hom[_,_]

-- Needed to define this in order to be able to make the subsequence syntax declaration
seq' :  (C : Precategory  ℓ') {x y z} (f : C [ x , y ]) (g : C [ y , z ])  C [ x , z ]
seq' = _⋆_

infixl 15 seq'
syntax seq' C f g = f ⋆⟨ C  g

-- composition
comp' :  (C : Precategory  ℓ') {x y z} (g : C [ y , z ]) (f : C [ x , y ])  C [ x , z ]
comp' = _∘_

infixr 16 comp'
syntax comp' C g f = g ∘⟨ C  f

-- Isomorphisms and paths in precategories
record PrecatIso (C : Precategory  ℓ') (x y : C .ob) : Type ℓ' where
  constructor precatiso
  field
    mor : C [ x , y ]
    inv : C [ y , x ]
    sec : inv ⋆⟨ C  mor  C .id
    ret : mor ⋆⟨ C  inv  C .id

-- Opposite precategory
_^op : Precategory  ℓ'  Precategory  ℓ'
(C ^op) .ob = C .ob
(C ^op) .Hom[_,_] x y = C .Hom[_,_] y x
(C ^op) .id = C .id
(C ^op) ._⋆_ f g = C ._⋆_ g f
(C ^op) .⋆IdL = C .⋆IdR
(C ^op) .⋆IdR = C .⋆IdL
(C ^op) .⋆Assoc f g h = sym (C .⋆Assoc _ _ _)

-- Natural isomorphisms
module _ {ℓC ℓC' : Level} {C : Precategory ℓC ℓC'}
  {x y : C .ob} (f : Hom[_,_] C x y) where
  record preIsIso : Type (ℓ-max ℓC ℓC') where
    field
      inv' : Hom[_,_] C y x
      sect : _⋆_ C inv' f  id C {y}
      retr : _⋆_ C f inv'  id C {x}

open Precategory
open preIsIso

module _ {ℓC ℓC' ℓD ℓD' : Level} where
 -- Products
 _×_ :  (C : Precategory ℓC ℓC') (D : Precategory ℓD ℓD')  Precategory _ _
 ob (C × D) = ob C ×' ob D
 Hom[_,_] (C × D) (c , d) (c' , d') =
   Hom[_,_] C c c' ×' Hom[_,_] D d d'
 id (C × D) = id C , id D
 _⋆_ (C × D) f g = _⋆_ C (fst f) (fst g) , _⋆_ D (snd f) (snd g)
 ⋆IdL (C × D) (f , g) i = (⋆IdL C f i) , (⋆IdL D g i)
 ⋆IdR (C × D) (f , g) i = (⋆IdR C f i) , (⋆IdR D g i)
 ⋆Assoc (C × D) (f , g) (f' , g') (f'' , g'') i =
   ⋆Assoc C f f' f'' i , ⋆Assoc D g g' g'' i

 -- Functors
 record Prefunctor (C : Precategory ℓC ℓC') (D : Precategory ℓD ℓD') :
          Type (ℓ-max (ℓ-max ℓC ℓC') (ℓ-max ℓD ℓD')) where
   no-eta-equality

   field
     F-ob  : C .ob  D .ob
     F-hom : {x y : C .ob}  C [ x , y ]  D [ F-ob x , F-ob y ]
     F-id  : {x : C .ob}  F-hom {y = x} (id C)  id D
     F-seq : {x y z : C .ob} (f : C [ x , y ]) (g : C [ y , z ])
            F-hom (f ⋆⟨ C  g)  (F-hom f) ⋆⟨ D  (F-hom g)

 -- Natural transformation
 record PreNatTrans (C : Precategory ℓC ℓC') (D : Precategory ℓD ℓD')
          (F G : Prefunctor C D) :
          Type (ℓ-max (ℓ-max ℓC ℓC') (ℓ-max ℓD ℓD')) where
   no-eta-equality

   open Prefunctor

   field
     N-ob : (x : C .ob)  D [ F .F-ob x , G .F-ob x ]
     N-hom : {x y : C .ob} (f : C [ x , y ])
        (F .F-hom f) ⋆⟨ D  (N-ob y)  (N-ob x) ⋆⟨ D  (G .F-hom f)

 -- Natural isomorphisms
 record PreNatIso (C : Precategory ℓC ℓC') (D : Precategory ℓD ℓD')
          (F G : Prefunctor C D) :
          Type (ℓ-max (ℓ-max ℓC ℓC') (ℓ-max ℓD ℓD')) where
   open PreNatTrans

   field
     trans : PreNatTrans C D F G
     isIs : (c : C .ob)  preIsIso {C = D} (trans .N-ob c)

open Prefunctor
open PreNatTrans
open PreNatIso
open preIsIso


module _ {ℓC ℓC' ℓD ℓD' : Level}
  {C : Precategory ℓC ℓC'} {D : Precategory ℓD ℓD'}
  (F G H : Prefunctor C D) where

  compPreNatTrans : PreNatTrans _ _ F G  PreNatTrans _ _ G H  PreNatTrans _ _ F H
  N-ob (compPreNatTrans η γ) c = N-ob η c ⋆⟨ D  N-ob γ c
  N-hom (compPreNatTrans η γ) {x = x} {y = y} f =
    sym (⋆Assoc D _ _ _)  cong  w  w ⋆⟨ D  (N-ob γ y)) (N-hom η f)
     ⋆Assoc D _ _ _
     cong  (D  N-ob η x) (N-hom γ f)
     sym (⋆Assoc D _ _ _)

  compPreNatIso : PreNatIso _ _ F G  PreNatIso _ _ G H  PreNatIso _ _ F H
  trans (compPreNatIso isη isγ) = compPreNatTrans (trans isη) (trans isγ)
  inv' (isIs (compPreNatIso isη isγ) c) = inv' (isIs isγ c) ⋆⟨ D  (inv' (isIs isη c))
  sect (isIs (compPreNatIso isη isγ) c) =
    ⋆Assoc D _ _ _
     cong (D  inv' (isIs isγ c))
       (sym (⋆Assoc D _ _ _)
        cong  w  w ⋆⟨ D  (N-ob (trans isγ) c)) (sect (isIs isη c))
        ⋆IdL D _)
     sect (isIs isγ c)
  retr (isIs (compPreNatIso isη isγ) c) =
    ⋆Assoc D _ _ _
     cong (D  N-ob (trans isη) c)
        (sym (⋆Assoc D _ _ _)
         cong  w  w ⋆⟨ D  (inv' (isIs isη c))) (retr (isIs isγ c))
         ⋆IdL D _)
     retr (isIs isη c)

module _ {ℓC ℓC' ℓD ℓD' ℓE ℓE' : Level}
  {C : Precategory ℓC ℓC'} {D : Precategory ℓD ℓD'} {E : Precategory ℓE ℓE'}
 where
 comp-Prefunctor : (F : Prefunctor C D) (G : Prefunctor D E)
    Prefunctor C E
 F-ob (comp-Prefunctor F G) c = F-ob G (F-ob F c)
 F-hom (comp-Prefunctor F G) f = F-hom G (F-hom F f)
 F-id (comp-Prefunctor F G) = cong (F-hom G) (F-id F)  F-id G
 F-seq (comp-Prefunctor F G) f g = cong (F-hom G) (F-seq F f g)  F-seq G _ _


module _ {ℓC ℓC' : Level} {C : Precategory ℓC ℓC'}
  (F : Prefunctor (C × C) C) where
  assocₗ : Prefunctor (C × (C × C)) C
  F-ob assocₗ (x , y , z) = F-ob F (x , F-ob F (y , z))
  F-hom assocₗ {x} {y} (f , g) = F-hom F (f , F-hom F g)
  F-id assocₗ =
    cong  y  F-hom F (id C , y)) (F-id {C = C × C} F)
       F-id F
  F-seq assocₗ f g =
    cong (F-hom F)
         (cong (fst (f ⋆⟨ C × (C × C)  g) ,_)
           (F-seq F (snd f) (snd g)))
        F-seq F _ _

  assocᵣ : Prefunctor (C × (C × C)) C
  F-ob assocᵣ (x , y , z) = F-ob F (F-ob F (x , y) , z)
  F-hom assocᵣ (f , g) = F-hom F (F-hom F (f , (fst g)) , snd g)
  F-id assocᵣ = cong (F-hom F) (cong (_, id C) (F-id F))
               F-id F
  F-seq assocᵣ (f , f' , f'') (g , g' , g'') =
    cong (F-hom F) (cong (_, _⋆_ C f'' g'')
      (F-seq F (f , f') (g , g')))
     F-seq F _ _

-- Left and right restrictions of functors
module _ {ℓC ℓC' ℓD ℓD' ℓE ℓE' : Level}
      {C : Precategory ℓC ℓC'}
      {D : Precategory ℓD ℓD'}
      {E : Precategory ℓE ℓE'}
      where
 restrFunctorₗ : (F : Prefunctor (C × D) E) (c : C . ob)
    Prefunctor D E
 F-ob (restrFunctorₗ F c) d = F-ob F (c , d)
 F-hom (restrFunctorₗ F c) f = F-hom F (id C , f)
 F-id (restrFunctorₗ F c) = F-id F
 F-seq (restrFunctorₗ F c) f g =
     cong (F-hom F) (ΣPathP (sym (⋆IdR C _) , refl))
    F-seq F (id C , f) (id C , g)

 restrFunctorᵣ : (F : Prefunctor (C × D) E) (d : D . ob)
    Prefunctor C E
 F-ob (restrFunctorᵣ F d) c = F-ob F (c , d)
 F-hom (restrFunctorᵣ F d) f = F-hom F (f , (id D))
 F-id (restrFunctorᵣ F d) = F-id F
 F-seq (restrFunctorᵣ F d) f g =
     cong (F-hom F) (ΣPathP (refl , sym (⋆IdR D _)))
    F-seq F (f , id D) (g , id D)

-- Identity functor
idPrefunctor : {ℓC ℓC' : Level}
      (C : Precategory ℓC ℓC')
    Prefunctor C C
Prefunctor.F-ob (idPrefunctor C) c = c
Prefunctor.F-hom (idPrefunctor C) x = x
Prefunctor.F-id (idPrefunctor C) = refl
Prefunctor.F-seq (idPrefunctor C) _ _ = refl

-- Commutator
commFunctor : {ℓC ℓC' ℓD ℓD' : Level}
      {C : Precategory ℓC ℓC'}
      {D : Precategory ℓD ℓD'}
    Prefunctor (C × D) (D × C)
Prefunctor.F-ob commFunctor (x , y) = y , x
Prefunctor.F-hom commFunctor (f , g) = g , f
Prefunctor.F-id commFunctor = refl
Prefunctor.F-seq commFunctor _ _ = refl

-- Monoidal, braided and monoidal symmetric precategories
module _ (M : Precategory  ℓ') where
  record isMonoidalPrecategory : Type (ℓ-max  ℓ') where
    field
      _⊗_ : Prefunctor (M × M) M
      𝟙 : ob M

      ⊗assoc : PreNatIso (M × (M × M)) M (assocₗ _⊗_) (assocᵣ _⊗_)

      ⊗lUnit : PreNatIso M M (restrFunctorₗ _⊗_ 𝟙) (idPrefunctor M)
      ⊗rUnit : PreNatIso M M (restrFunctorᵣ _⊗_ 𝟙) (idPrefunctor M)

    private
      α = N-ob (trans ⊗assoc)
      α⁻ : (c : ob M ×' ob M ×' ob M)  M [ _ , _ ]
      α⁻ c = preIsIso.inv' (isIs ⊗assoc c)
      rId = N-ob (trans ⊗rUnit)
      lId = N-ob (trans ⊗lUnit)

    field
      -- Note: associators are on the form x ⊗ (y ⊗ z) → (x ⊗ y) ⊗ z
      triang : (a b : M .ob)
         α (a , 𝟙 , b) ⋆⟨ M  (F-hom _⊗_ ((rId a) , id M))
           F-hom _⊗_ ((id M) , lId b)

      ⊗pentagon : (a b c d : M .ob)
         (F-hom _⊗_ (id M , α (b , c , d)))
           ⋆⟨ M  ((α (a , (_⊗_ .F-ob (b , c)) , d))
           ⋆⟨ M  (F-hom _⊗_ (α (a , b , c) , id M)))
          (α (a , b , (F-ob _⊗_ (c , d))))
           ⋆⟨ M  (α((F-ob _⊗_ (a , b)) , c , d))

  module _ (mon : isMonoidalPrecategory) where
    open isMonoidalPrecategory mon
    private
      α = N-ob (trans ⊗assoc)
      α⁻ : (c : ob M ×' ob M ×' ob M)  M [ _ , _ ]
      α⁻ c = preIsIso.inv' (isIs ⊗assoc c)

    BraidingIsoType : Type _
    BraidingIsoType = PreNatIso (M × M) M _⊗_ (comp-Prefunctor commFunctor _⊗_)

    HexagonType₁ : (B : BraidingIsoType)  Type _
    HexagonType₁ B = (x y z : M .ob) 
      F-hom _⊗_ ((id M) , N-ob (trans B) (y , z))
        ⋆⟨ M  α (x , z , y)
        ⋆⟨ M  F-hom _⊗_ (N-ob (trans B) (x , z) , (id M))
        α (x , y , z)
        ⋆⟨ M  N-ob (trans B) ((F-ob _⊗_ (x , y)) , z)
        ⋆⟨ M  α (z , x , y)

    HexagonType₂ : (B : BraidingIsoType)  Type _
    HexagonType₂ B = (x y z : M .ob) 
      F-hom _⊗_ (N-ob (trans B) (x , y) , id M)
        ⋆⟨ M  α⁻ (y , x , z)
        ⋆⟨ M  F-hom _⊗_ ((id M) , N-ob (trans B) (x , z))
        α⁻ (x , y , z)
        ⋆⟨ M  N-ob (trans B) (x , F-ob _⊗_ (y , z))
        ⋆⟨ M  α⁻ (y , z , x)

    isSymmetricBraiding : (B : BraidingIsoType)
       Type _
    isSymmetricBraiding B = (x y : M .ob) 
      N-ob (trans B) (x , y) ⋆⟨ M  (N-ob (trans B) (y , x))
       id M

  record isBraidedPrecategory : Type (ℓ-max  ℓ') where
    open isMonoidalPrecategory
    field
      isMonoidal : isMonoidalPrecategory
      Braid : BraidingIsoType isMonoidal
      hexagons : (x y z : M .ob)
         HexagonType₁ isMonoidal Braid
        ×' HexagonType₂ isMonoidal Braid

  record isSymmetricPrecategory : Type (ℓ-max  ℓ') where
    field
      isMonoidal : isMonoidalPrecategory
      Braid : BraidingIsoType isMonoidal
      hexagon : HexagonType₁ isMonoidal Braid
      symBraiding : isSymmetricBraiding isMonoidal Braid

SymmetricMonoidalPrecat : ( ℓ' : Level)  Type (ℓ-suc (ℓ-max  ℓ'))
SymmetricMonoidalPrecat  ℓ' =
  Σ[ C  Precategory  ℓ' ] isSymmetricPrecategory C

-- Instances
module _ ( : Level) where
  PointedCat : Precategory (ℓ-suc ) 
  ob PointedCat = Pointed 
  Hom[_,_] PointedCat A B = A →∙ B
  Precategory.id PointedCat = idfun∙ _
  _⋆_ PointedCat f g = g ∘∙ f
  ⋆IdL PointedCat = ∘∙-idˡ
  ⋆IdR PointedCat = ∘∙-idʳ
  ⋆Assoc PointedCat f g h = sym (∘∙-assoc h g f)