module Cat.Displayed.Cartesian.Indexing
  {o β„“ o' β„“'} {B : Precategory o β„“}
  (E : Displayed B o' β„“')
  (cartesian : Cartesian-fibration E)
  where

Reindexing for cartesian fibrationsπŸ”—

A cartesian fibration can be thought of as a displayed category whose fibre categories depend (pseudo)functorially on the object from the base category. A canonical example is the canonical self-indexing: If is a category with pullbacks, then each gives rise to a functor the change of base along

module _ {𝒢 𝒷} (f : Hom 𝒢 𝒷) where
  base-change : Functor (Fibre E 𝒷) (Fibre E 𝒢)
  base-change .Fβ‚€ ob = has-lift f ob .x'
  base-change .F₁ {x} {y} vert = rebase f vert

Moreover, this assignment is itself functorial in Along the identity morphism, it’s the same thing as not changing bases at all.

module _ {𝒢} where
  private
    module FC = Cat.Reasoning (Cat[ Fibre E 𝒢 , Fibre E 𝒢 ])
    module Fa = Cat.Reasoning (Fibre E 𝒢)

  base-change-id : base-change id FC.β‰… Id
I’ll warn you in advance that this proof is not for the faint of heart.
  base-change-id = to-natural-iso mi where
    open make-natural-iso
    mi : make-natural-iso (base-change id) Id
    mi .eta x = has-lift.lifting id x
    mi .inv x = has-lift.universalv id x id'
    mi .eta∘inv x = cancel _ _ (has-lift.commutesv _ _ _)
    mi .inv∘eta x = sym $ has-lift.uniquepβ‚‚ id x _ _ _ _ _
      (idr' _)
      (Fib.cancellf (has-lift.commutesv _ _ _))
    mi .natural x y f =
      sym $ from-pathp $ cast[] $
        has-lift.commutesp id y id-comm _
        βˆ™[] Fib.to-fibre

And similarly, composing changes of base is the same thing as changing base along a composite.

  ^*-id-to : βˆ€ {x} β†’ Hom[ id {𝒢} ] (id ^* x) x
  ^*-id-to = has-lift.lifting id _

  ^*-id-from : βˆ€ {x} β†’ Hom[ id {𝒢} ] x (id ^* x)
  ^*-id-from = has-lift.universalv id _ id'

^*-comp-from
  : βˆ€ {a b c} {z} {f : Hom b c} {g : Hom a b}
  β†’ Hom[ id ] (g ^* (f ^* z)) ((f ∘ g) ^* z)
^*-comp-from = has-lift.universalv _ _ (has-lift.lifting _ _ ∘' has-lift.lifting _ _)

^*-comp-to
  : βˆ€ {a b c} {z} {f : Hom b c} {g : Hom a b}
  β†’ Hom[ id ] ((f ∘ g) ^* z) (g ^* (f ^* z))
^*-comp-to = has-lift.universalv _ _ (has-lift.universal _ _ _ (has-lift.lifting _ _))

^*-comp
  : βˆ€ {a b c} {z} {f : Hom b c} {g : Hom a b}
  β†’ ((f ∘ g) ^* z) Fib.β‰… (g ^* (f ^* z))
^*-comp = Fib.make-iso ^*-comp-to ^*-comp-from
  (has-lift.uniquepβ‚‚ _ _ _ _ _ _ _
    (Fib.pulllf (has-lift.commutesv _ _ _) βˆ™[]
      has-lift.uniquepβ‚‚ _ _ _ (idr _) refl _ _
        (pulll[] _ (has-lift.commutes _ _ _ _) βˆ™[]
          has-lift.commutesv _ _ _) refl)
    (idr' _))
  (has-lift.uniquepβ‚‚ _ _ _ _ _ _ _
    (Fib.pulllf (has-lift.commutesv _ _ _)
      βˆ™[] pullr[] _ (has-lift.commutesv _ _ _)
      βˆ™[] has-lift.commutes _ _ _ _)
    (idr' _))

^*-comp-to-natural
  : βˆ€ {a b c} {f : Hom b c} {g : Hom a b} {x y : Ob[ c ]} (f' : Hom[ id ] x y)
  β†’ rebase g (rebase f f') Fib.∘ ^*-comp-to ≑ ^*-comp-to Fib.∘ rebase (f ∘ g) f'
^*-comp-to-natural {f = f} {g = g} f' =
  ap hom[] $ cartesian→weak-monic E (has-lift.cartesian g _) _ _ $ cast[] $
    pulll[] _ (has-lift.commutesp g _ id-comm _)
    βˆ™[] pullr[] _ (has-lift.commutesv g _ _)
    βˆ™[] has-lift.uniquepβ‚‚ _ _ _ id-comm-sym _ _ _
      (pulll[] _ (has-lift.commutesp _ _ id-comm _)
        βˆ™[] pullr[] _ (has-lift.commutes _ _ _ _))
      (pulll[] _ (has-lift.commutes _ _ _ _)
        βˆ™[] has-lift.commutesp _ _ id-comm _)
    βˆ™[] pushl[] _ (symP (has-lift.commutesv g _ _))
module _ {𝒢} {𝒷} {𝒸} (f : Hom 𝒷 𝒸) (g : Hom 𝒢 𝒷) where
  private
    module FC = Cat.Reasoning (Cat[ Fibre E 𝒸 , Fibre E 𝒢 ])
    module Fa = Cat.Reasoning (Fibre E 𝒢)

  base-change-comp : base-change (f ∘ g) FC.β‰… (base-change g F∘ base-change f)
This proof is a truly nightmarish application of universal properties and I recommend that nobody look at it, ever.

.

  base-change-comp = to-natural-iso mi where
    open make-natural-iso
    mi : make-natural-iso (base-change (f ∘ g)) (base-change g F∘ base-change f)
    mi .eta x = ^*-comp-to
    mi .inv x = ^*-comp-from
    mi .eta∘inv x = ^*-comp .Fib.invl
    mi .inv∘eta x = ^*-comp .Fib.invr
    mi .natural x y f' = ^*-comp-to-natural f'

In order to assemble this into a pseudofunctor out of (seen as a locally discrete bicategory) into we start by bundling up all the base changes into a functor between categories. We also prove a lemma that will be useful later, relating base changes along equal morphisms.

base-changes : βˆ€ {a b}
  β†’ Functor (Locally-discrete (B ^op) .Prebicategory.Hom a b)
            Cat[ Fibre E a , Fibre E b ]
base-changes = Disc-adjunct base-change

base-change-coherence
  : βˆ€ {a b} {b' : Ob[ b ]} {f g : Hom a b} (p : f ≑ g)
  β†’ has-lift.lifting g b' ∘' base-changes .F₁ p .Ξ· b'
  ≑[ idr _ βˆ™ sym p ] has-lift.lifting f b'
base-change-coherence {b' = b'} {f} = J
  (Ξ» g p β†’ has-lift.lifting g b' ∘' base-changes .F₁ p .Ξ· b'
         ≑[ idr _ βˆ™ sym p ] has-lift.lifting f b')
  (elimr' refl Regularity.reduce!)

We have enough data to start defining our pseudofunctor:

Fibres : Pseudofunctor (Locally-discrete (B ^op)) (Cat o' β„“')
Fibres .lax .Pβ‚€ = Fibre E
Fibres .lax .P₁ = base-changes
Fibres .lax .compositor = Disc-naturalβ‚‚
  Ξ» (f , g) β†’ base-change-comp g f .Mor.from
Fibres .lax .unitor = base-change-id .Mor.from
Fibres .unitor-inv = FC.isoβ†’invertible (base-change-id FC.Iso⁻¹)
Fibres .compositor-inv f g =
  FC.isoβ†’invertible (base-change-comp g f FC.Iso⁻¹)

It remains to verify that this data is coherent, which is so tedious that it serves as a decent self-contained motivation for displayed categories.

You’ve been warned.

We start with the left-unit. In the diagram below, we have to show that the composite vertical morphism over is equal to the identity over By the uniqueness property of cartesian lifts, it suffices to show that the composites with the lift of are equal, which is witnessed by the commutativity of the whole diagram.

The bottom triangle is our base-change-coherence lemma, the middle square is by definition of the compositor and the top triangle is by definition of the unitor.

Fibres .lax .left-unit f = ext Ξ» a' β†’
  has-lift.uniquepβ‚‚ f a' _ refl refl _ _
    (Fib.pulllf (base-change-coherence (idr f))
    βˆ™[] Fib.pulllf (has-lift.commutesv (f ∘ id) a' _)
    βˆ™[] (refl⟩∘'⟨ Fib.eliml (base-change id .F-id))
    βˆ™[] pullr[] _ (has-lift.commutesv id _ id'))
    refl

For the right-unit, we proceed similarly. The diagram below shows that the composite on the left, composed with the lift of is equal to the lift of

The bottom triangle is base-change-coherence, the middle square is by definition of the compositor, the outer triangle is by definition of the unitor, and the top square is by definition of rebase (the action of on morphisms).

Fibres .lax .right-unit f = ext Ξ» a' β†’
  has-lift.uniquepβ‚‚ f a' _ refl _ _ _
    (Fib.pulllf (base-change-coherence (idl f))
    βˆ™[] Fib.pulllf (has-lift.commutesv (id ∘ f) a' _)
    βˆ™[] (refl⟩∘'⟨ Fib.idr _)
    βˆ™[] extendr[] id-comm (has-lift.commutesp f _ _ _)
    βˆ™[] (has-lift.commutesv id _ id' ⟩∘'⟨refl))
    (idr' _ βˆ™[] symP (idl' _))

Last but definitely not least, the hexagon witnessing the coherence of associativity follows again by uniqueness of cartesian lifts, by the commutativity of the following diagram.

Fibres .lax .hexagon f g h = ext Ξ» a' β†’
  has-lift.uniquepβ‚‚ ((h ∘ g) ∘ f) a' _ refl _ _ _
    (Fib.pulllf (base-change-coherence (assoc h g f))
    βˆ™[] Fib.pulllf (has-lift.commutesv (h ∘ (g ∘ f)) a' _)
    βˆ™[] (refl⟩∘'⟨ Fib.eliml (base-change (g ∘ f) .F-id))
    βˆ™[] extendr[] _ (has-lift.commutesv (g ∘ f) _ _))
    (Fib.pulllf (has-lift.commutesv ((h ∘ g) ∘ f) a' _)
    βˆ™[] (refl⟩∘'⟨ Fib.idr _) βˆ™[] (refl⟩∘'⟨ Fib.idr _)
    βˆ™[] extendr[] id-comm (has-lift.commutesp f _ _ _)
    βˆ™[] (has-lift.commutesv (h ∘ g) a' _ ⟩∘'⟨refl))
-- Optimized natural iso, avoids a bunch of junk from composition.
opaque
  base-change-square
    : βˆ€ {Ξ“ Ξ” Θ Ξ¨ : Ob}
    β†’ {Οƒ : Hom Ξ“ Ξ”} {Ξ΄ : Hom Ξ“ Θ} {Ξ³ : Hom Ξ” Ξ¨} {Ο„ : Hom Θ Ξ¨}
    β†’ Ξ³ ∘ Οƒ ≑ Ο„ ∘ Ξ΄
    β†’ βˆ€ x' β†’ Hom[ id ]
      (base-change Οƒ .Fβ‚€ (base-change Ξ³ .Fβ‚€ x'))
      (base-change Ξ΄ .Fβ‚€ (base-change Ο„ .Fβ‚€ x'))
  base-change-square {Οƒ = Οƒ} {Ξ΄ = Ξ΄} {Ξ³ = Ξ³} {Ο„ = Ο„} p x' =
    has-lift.universalv Ξ΄ _ $
    has-lift.universal' Ο„ _ (sym p) $
    has-lift.lifting Ξ³ x' ∘' has-lift.lifting Οƒ _

  base-change-square-lifting
    : βˆ€ {Ξ“ Ξ” Θ Ξ¨ : Ob}
    β†’ {Οƒ : Hom Ξ“ Ξ”} {Ξ΄ : Hom Ξ“ Θ} {Ξ³ : Hom Ξ” Ξ¨} {Ο„ : Hom Θ Ξ¨}
    β†’ (p : Ξ³ ∘ Οƒ ≑ Ο„ ∘ Ξ΄) (x' : Ob[ Ξ¨ ])
    β†’ has-lift.lifting Ο„ x' ∘' has-lift.lifting Ξ΄ (base-change Ο„ .Fβ‚€ x') ∘' base-change-square p x'
    ≑[ ap (Ο„ ∘_) (idr _) βˆ™ sym p ] has-lift.lifting Ξ³ x' ∘' has-lift.lifting Οƒ _
  base-change-square-lifting {Οƒ = Οƒ} {Ξ΄ = Ξ΄} {Ξ³ = Ξ³} {Ο„ = Ο„} p x' =
    cast[] $
    apd (Ξ» _ β†’ has-lift.lifting Ο„ x' ∘'_) (has-lift.commutesv _ _ _)
    βˆ™[] has-lift.commutesp Ο„ x' (sym p) _

  base-change-square-natural
    : βˆ€ {Ξ“ Ξ” Θ Ξ¨ : Ob}
    β†’ {Οƒ : Hom Ξ“ Ξ”} {Ξ΄ : Hom Ξ“ Θ} {Ξ³ : Hom Ξ” Ξ¨} {Ο„ : Hom Θ Ξ¨}
    β†’ (p : Ξ³ ∘ Οƒ ≑ Ο„ ∘ Ξ΄)
    β†’ βˆ€ {x' y'} (f' : Hom[ id ] x' y')
    β†’ base-change-square p y' ∘' base-change Οƒ .F₁ (base-change Ξ³ .F₁ f')
    ≑ base-change Ξ΄ .F₁ (base-change Ο„ .F₁ f') ∘' base-change-square p x'
  base-change-square-natural {Οƒ = Οƒ} {Ξ΄ = Ξ΄} {Ξ³ = Ξ³} {Ο„ = Ο„} p f' =
    has-lift.uniquepβ‚‚ Ξ΄ _ _ _ _ _ _
      (pulll[] _ (has-lift.commutesv Ξ΄ _ _)
       βˆ™[] has-lift.uniquepβ‚‚ Ο„ _ _ (idr _) _ _ _
         (pulll[] _ (has-lift.commutesp Ο„ _ (sym p) _)
          βˆ™[] pullr[] _ (has-lift.commutesp Οƒ _ id-comm _)
          βˆ™[] extendl[] _ (has-lift.commutesp Ξ³ _ id-comm _))
         (has-lift.commutesp Ο„ _ (sym p βˆ™ sym (idl _ )) _))
      (pulll[] _ (has-lift.commutesp Ξ΄ _ id-comm _)
       βˆ™[] pullr[] _ (has-lift.commutesv Ξ΄ _ _)
       βˆ™[] has-lift.uniquep Ο„ _ _ (idl _) (sym p βˆ™ sym (idl _)) _
         (pulll[] _ (has-lift.commutesp _ _ id-comm _ )
          βˆ™[] pullr[] _ (has-lift.commutesp _ _ (sym p) _)))

  base-change-square-inv
    : βˆ€ {Ξ“ Ξ” Θ Ξ¨ : Ob}
    β†’ {Οƒ : Hom Ξ“ Ξ”} {Ξ΄ : Hom Ξ“ Θ} {Ξ³ : Hom Ξ” Ξ¨} {Ο„ : Hom Θ Ξ¨}
    β†’ (p : Ξ³ ∘ Οƒ ≑ Ο„ ∘ Ξ΄)
    β†’ βˆ€ x' β†’ base-change-square p x' ∘' base-change-square (sym p) x' ≑[ idl _ ] id'
  base-change-square-inv {Οƒ = Οƒ} {Ξ΄ = Ξ΄} {Ξ³ = Ξ³} {Ο„ = Ο„} p x' =
    has-lift.uniquepβ‚‚ _ _ _ _ _ _ _
      (pulll[] _ (has-lift.commutesv Ξ΄ _ _)
       βˆ™[] has-lift.uniquepβ‚‚ Ο„ _ _ (idr _) refl _ _
         (pulll[] _ (has-lift.commutesp Ο„ _ (sym p) _)
          βˆ™[] pullr[] _ (has-lift.commutesv Οƒ _ _)
          βˆ™[] has-lift.commutesp Ξ³ _ p _)
         refl)
      (idr' _)

base-change-square-ni
  : βˆ€ {Ξ“ Ξ” Θ Ξ¨ : Ob}
  β†’ {Οƒ : Hom Ξ“ Ξ”} {Ξ΄ : Hom Ξ“ Θ} {Ξ³ : Hom Ξ” Ξ¨} {Ο„ : Hom Θ Ξ¨}
  β†’ Ξ³ ∘ Οƒ ≑ Ο„ ∘ Ξ΄
  β†’ (base-change Οƒ F∘ base-change Ξ³) ≅ⁿ (base-change Ξ΄ F∘ base-change Ο„)
base-change-square-ni {Οƒ = Οƒ} {Ξ΄ = Ξ΄} {Ξ³ = Ξ³} {Ο„ = Ο„} p =
  to-natural-iso ni where

  open make-natural-iso
  ni : make-natural-iso _ _
  ni .eta = base-change-square p
  ni .inv = base-change-square (sym p)
  ni .eta∘inv x = from-pathp $ base-change-square-inv p x
  ni .inv∘eta x = from-pathp $ base-change-square-inv (sym p) x
  ni .natural x y f = sym $ Fib.over-fibre (base-change-square-natural p f)