Module docstring
{"# Categories of indexed families of objects.
We define the pointwise category structure on indexed families of objects in a category (and also the dependent generalization).
"}
{"# Categories of indexed families of objects.
We define the pointwise category structure on indexed families of objects in a category (and also the dependent generalization).
"}
CategoryTheory.pi
      instance
     : Category.{max w₀ v₁} (∀ i, C i)
        /-- `pi C` gives the cartesian product of an indexed family of categories.
-/
instance pi : Category.{max w₀ v₁} (∀ i, C i) where
  Hom X Y := ∀ i, X i ⟶ Y i
  id X i := 𝟙 (X i)
  comp f g i := f i ≫ g i
        CategoryTheory.Pi.id_apply
      theorem
     (X : ∀ i, C i) (i) : (𝟙 X : ∀ i, X i ⟶ X i) i = 𝟙 (X i)
        
      CategoryTheory.Pi.comp_apply
      theorem
     {X Y Z : ∀ i, C i} (f : X ⟶ Y) (g : Y ⟶ Z) (i) : (f ≫ g : ∀ i, X i ⟶ Z i) i = f i ≫ g i
        
      CategoryTheory.Pi.ext
      theorem
     {X Y : ∀ i, C i} {f g : X ⟶ Y} (w : ∀ i, f i = g i) : f = g
        
      CategoryTheory.Pi.eval
      definition
     (i : I) : (∀ i, C i) ⥤ C i
        /--
The evaluation functor at `i : I`, sending an `I`-indexed family of objects to the object over `i`.
-/
@[simps]
def eval (i : I) : (∀ i, C i) ⥤ C i where
  obj f := f i
  map α := α i
        CategoryTheory.Pi.instCategoryComp
      instance
     (f : J → I) : (j : J) → Category ((C ∘ f) j)
        instance (f : J → I) : (j : J) → Category ((C ∘ f) j) := by
  dsimp
  infer_instance
        CategoryTheory.Pi.comap
      definition
     (h : J → I) : (∀ i, C i) ⥤ (∀ j, C (h j))
        /-- Pull back an `I`-indexed family of objects to a `J`-indexed family, along a function `J → I`.
-/
@[simps]
def comap (h : J → I) : (∀ i, C i) ⥤ (∀ j, C (h j)) where
  obj f i := f (h i)
  map α i := α (h i)
        CategoryTheory.Pi.comapId
      definition
     : comap C (id : I → I) ≅ 𝟭 (∀ i, C i)
        /-- The natural isomorphism between
pulling back a grading along the identity function,
and the identity functor. -/
@[simps]
def comapId : comapcomap C (id : I → I) ≅ 𝟭 (∀ i, C i) where
  hom := { app := fun X => 𝟙 X }
  inv := { app := fun X => 𝟙 X }
        CategoryTheory.Pi.comapComp
      definition
     (f : K → J) (g : J → I) : comap C g ⋙ comap (C ∘ g) f ≅ comap C (g ∘ f)
        /-- The natural isomorphism comparing between
pulling back along two successive functions, and
pulling back along their composition
-/
@[simps!]
def comapComp (f : K → J) (g : J → I) : comapcomap C g ⋙ comap (C ∘ g) fcomap C g ⋙ comap (C ∘ g) f ≅ comap C (g ∘ f) where
  hom :=
  { app := fun X b => 𝟙 (X (g (f b)))
    naturality := fun X Y f' => by simp only [comap, Function.comp]; funext; simp }
  inv :=
  { app := fun X b => 𝟙 (X (g (f b)))
    naturality := fun X Y f' => by simp only [comap, Function.comp]; funext; simp }
        CategoryTheory.Pi.comapEvalIsoEval
      definition
     (h : J → I) (j : J) : comap C h ⋙ eval (C ∘ h) j ≅ eval C (h j)
        /-- The natural isomorphism between pulling back then evaluating, and just evaluating. -/
@[simps!]
def comapEvalIsoEval (h : J → I) (j : J) : comapcomap C h ⋙ eval (C ∘ h) jcomap C h ⋙ eval (C ∘ h) j ≅ eval C (h j) :=
  NatIso.ofComponents (fun _ => Iso.refl _) (by simp only [Iso.refl]; simp)
        CategoryTheory.Pi.sumElimCategory
      instance
     : ∀ s : I ⊕ J, Category.{v₁} (Sum.elim C D s)
        instance sumElimCategory : ∀ s : I ⊕ J, Category.{v₁} (Sum.elim C D s)
  | Sum.inl i => by
    dsimp
    infer_instance
  | Sum.inr j => by
    dsimp
    infer_instance
        CategoryTheory.Pi.sum
      definition
     : (∀ i, C i) ⥤ (∀ j, D j) ⥤ ∀ s : I ⊕ J, Sum.elim C D s
        /-- The bifunctor combining an `I`-indexed family of objects with a `J`-indexed family of objects
to obtain an `I ⊕ J`-indexed family of objects.
-/
@[simps]
def sum : (∀ i, C i) ⥤ (∀ j, D j) ⥤ ∀ s : I ⊕ J, Sum.elim C D s where
  obj X :=
    { obj := fun Y s =>
        match s with
        | .inl i => X i
        | .inr j => Y j
      map := fun {_} {_} f s =>
        match s with
        | .inl i => 𝟙 (X i)
        | .inr j => f j }
  map {X} {X'} f :=
    { app := fun Y s =>
        match s with
        | .inl i => f i
        | .inr j => 𝟙 (Y j) }
        CategoryTheory.Pi.isoApp
      definition
     {X Y : ∀ i, C i} (f : X ≅ Y) (i : I) : X i ≅ Y i
        /-- An isomorphism between `I`-indexed objects gives an isomorphism between each
pair of corresponding components. -/
@[simps]
def isoApp {X Y : ∀ i, C i} (f : X ≅ Y) (i : I) : X i ≅ Y i :=
  ⟨f.hom i, f.inv i,
    by rw [← comp_apply, Iso.hom_inv_id, id_apply], by rw [← comp_apply, Iso.inv_hom_id, id_apply]⟩
        CategoryTheory.Pi.isoApp_refl
      theorem
     (X : ∀ i, C i) (i : I) : isoApp (Iso.refl X) i = Iso.refl (X i)
        @[simp]
theorem isoApp_refl (X : ∀ i, C i) (i : I) : isoApp (Iso.refl X) i = Iso.refl (X i) :=
  rfl
        CategoryTheory.Pi.isoApp_symm
      theorem
     {X Y : ∀ i, C i} (f : X ≅ Y) (i : I) : isoApp f.symm i = (isoApp f i).symm
        
      CategoryTheory.Pi.isoApp_trans
      theorem
     {X Y Z : ∀ i, C i} (f : X ≅ Y) (g : Y ≅ Z) (i : I) : isoApp (f ≪≫ g) i = isoApp f i ≪≫ isoApp g i
        @[simp]
theorem isoApp_trans {X Y Z : ∀ i, C i} (f : X ≅ Y) (g : Y ≅ Z) (i : I) :
    isoApp (f ≪≫ g) i = isoAppisoApp f i ≪≫ isoApp g i :=
  rfl
        CategoryTheory.Functor.pi
      definition
     (F : ∀ i, C i ⥤ D i) : (∀ i, C i) ⥤ ∀ i, D i
        /-- Assemble an `I`-indexed family of functors into a functor between the pi types.
-/
@[simps]
def pi (F : ∀ i, C i ⥤ D i) : (∀ i, C i) ⥤ ∀ i, D i where
  obj f i := (F i).obj (f i)
  map α i := (F i).map (α i)
        CategoryTheory.Functor.pi'
      definition
     (f : ∀ i, A ⥤ C i) : A ⥤ ∀ i, C i
        /-- Similar to `pi`, but all functors come from the same category `A`
-/
@[simps]
def pi' (f : ∀ i, A ⥤ C i) : A ⥤ ∀ i, C i where
  obj a i := (f i).obj a
  map h i := (f i).map h
        CategoryTheory.Functor.pi'CompEval
      definition
     {A : Type*} [Category A] (F : ∀ i, A ⥤ C i) (i : I) : pi' F ⋙ Pi.eval C i ≅ F i
        /-- The projections of `Functor.pi' F` are isomorphic to the functors of the family `F` -/
@[simps!]
def pi'CompEval {A : Type*} [Category A] (F : ∀ i, A ⥤ C i) (i : I) :
    pi'pi' F ⋙ Pi.eval C ipi' F ⋙ Pi.eval C i ≅ F i :=
  Iso.refl _
        CategoryTheory.Functor.eqToHom_proj
      theorem
     {x x' : ∀ i, C i} (h : x = x') (i : I) : (eqToHom h : x ⟶ x') i = eqToHom (funext_iff.mp h i)
        @[simp]
theorem eqToHom_proj {x x' : ∀ i, C i} (h : x = x') (i : I) :
    (eqToHom h : x ⟶ x') i = eqToHom (funext_iff.mp h i) := by
  subst h
  rfl
        CategoryTheory.Functor.pi'_eval
      theorem
     (f : ∀ i, A ⥤ C i) (i : I) : pi' f ⋙ Pi.eval C i = f i
        @[simp]
theorem pi'_eval (f : ∀ i, A ⥤ C i) (i : I) : pi'pi' f ⋙ Pi.eval C i = f i := by
  apply Functor.ext
  · intro _ _ _
    simp
  · intro _
    rfl
        CategoryTheory.Functor.pi_ext
      theorem
     (f f' : A ⥤ ∀ i, C i) (h : ∀ i, f ⋙ (Pi.eval C i) = f' ⋙ (Pi.eval C i)) : f = f'
        /-- Two functors to a product category are equal iff they agree on every coordinate. -/
theorem pi_ext (f f' : A ⥤ ∀ i, C i) (h : ∀ i, f ⋙ (Pi.eval C i) = f' ⋙ (Pi.eval C i)) :
    f = f' := by
  apply Functor.ext; rotate_left
  · intro X
    ext i
    specialize h i
    have := congr_obj h X
    simpa
  · intro X Y g
    dsimp
    funext i
    specialize h i
    have := congr_hom h g
    simpa
        CategoryTheory.NatTrans.pi
      definition
     (α : ∀ i, F i ⟶ G i) : Functor.pi F ⟶ Functor.pi G
        /-- Assemble an `I`-indexed family of natural transformations into a single natural transformation.
-/
@[simps!]
def pi (α : ∀ i, F i ⟶ G i) : Functor.piFunctor.pi F ⟶ Functor.pi G where
  app f i := (α i).app (f i)
        CategoryTheory.NatTrans.pi'
      definition
     {E : Type*} [Category E] {F G : E ⥤ ∀ i, C i} (τ : ∀ i, F ⋙ Pi.eval C i ⟶ G ⋙ Pi.eval C i) : F ⟶ G
        /-- Assemble an `I`-indexed family of natural transformations into a single natural transformation.
-/
@[simps]
def pi' {E : Type*} [Category E] {F G : E ⥤ ∀ i, C i}
    (τ : ∀ i, F ⋙ Pi.eval C iF ⋙ Pi.eval C i ⟶ G ⋙ Pi.eval C i) : F ⟶ G where
  app := fun X i => (τ i).app X
  naturality _ _ f := by
    ext i
    exact (τ i).naturality f
        CategoryTheory.NatIso.pi
      definition
     (e : ∀ i, F i ≅ G i) : Functor.pi F ≅ Functor.pi G
        /-- Assemble an `I`-indexed family of natural isomorphisms into a single natural isomorphism.
-/
@[simps]
def pi (e : ∀ i, F i ≅ G i) : Functor.piFunctor.pi F ≅ Functor.pi G where
  hom := NatTrans.pi (fun i => (e i).hom)
  inv := NatTrans.pi (fun i => (e i).inv)
        CategoryTheory.NatIso.pi'
      definition
     {E : Type*} [Category E] {F G : E ⥤ ∀ i, C i} (e : ∀ i, F ⋙ Pi.eval C i ≅ G ⋙ Pi.eval C i) : F ≅ G
        /-- Assemble an `I`-indexed family of natural isomorphisms into a single natural isomorphism.
-/
@[simps]
def pi' {E : Type*} [Category E] {F G : E ⥤ ∀ i, C i}
    (e : ∀ i, F ⋙ Pi.eval C iF ⋙ Pi.eval C i ≅ G ⋙ Pi.eval C i) : F ≅ G where
  hom := NatTrans.pi' (fun i => (e i).hom)
  inv := NatTrans.pi' (fun i => (e i).inv)
        CategoryTheory.isIso_pi_iff
      theorem
     {X Y : ∀ i, C i} (f : X ⟶ Y) : IsIso f ↔ ∀ i, IsIso (f i)
        lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIsoIsIso f ↔ ∀ i, IsIso (f i) := by
  constructor
  · intro _ i
    exact (Pi.isoApp (asIso f) i).isIso_hom
  · intro
    exact ⟨fun i => inv (f i), by aesop_cat, by aesop_cat⟩
        CategoryTheory.Pi.eqToEquivalence
      definition
     {i j : I} (h : i = j) : C i ≌ C j
        /-- For a family of categories `C i` indexed by `I`, an equality `i = j` in `I` induces
an equivalence `C i ≌ C j`. -/
def Pi.eqToEquivalence {i j : I} (h : i = j) : C i ≌ C j := by subst h; rfl
        CategoryTheory.Pi.evalCompEqToEquivalenceFunctor
      definition
     {i j : I} (h : i = j) : Pi.eval C i ⋙ (Pi.eqToEquivalence C h).functor ≅ Pi.eval C j
        /-- When `i = j`, projections `Pi.eval C i` and `Pi.eval C j` are related by the equivalence
`Pi.eqToEquivalence C h : C i ≌ C j`. -/
@[simps!]
def Pi.evalCompEqToEquivalenceFunctor {i j : I} (h : i = j) :
    Pi.evalPi.eval C i ⋙ (Pi.eqToEquivalence C h).functorPi.eval C i ⋙ (Pi.eqToEquivalence C h).functor ≅
      Pi.eval C j :=
  eqToIso (by subst h; rfl)
        CategoryTheory.Pi.eqToEquivalenceFunctorIso
      definition
     (f : J → I) {i' j' : J} (h : i' = j') :
  (Pi.eqToEquivalence C (congr_arg f h)).functor ≅ (Pi.eqToEquivalence (fun i' => C (f i')) h).functor
        /-- The equivalences given by `Pi.eqToEquivalence` are compatible with reindexing. -/
@[simps!]
def Pi.eqToEquivalenceFunctorIso (f : J → I) {i' j' : J} (h : i' = j') :
    (Pi.eqToEquivalence C (congr_arg f h)).functor ≅
      (Pi.eqToEquivalence (fun i' => C (f i')) h).functor :=
  eqToIso (by subst h; rfl)
        CategoryTheory.Pi.equivalenceOfEquiv
      definition
     (e : J ≃ I) : (∀ j, C (e j)) ≌ (∀ i, C i)
        /-- Reindexing a family of categories gives equivalent `Pi` categories. -/
@[simps]
noncomputable def Pi.equivalenceOfEquiv (e : J ≃ I) :
    (∀ j, C (e j)) ≌ (∀ i, C i) where
  functor := Functor.pi' (fun i => Pi.eval _ (e.symm i) ⋙
    (Pi.eqToEquivalence C (by simp)).functor)
  inverse := Functor.pi' (fun i' => Pi.eval _ (e i'))
  unitIso := NatIso.pi' (fun i' => Functor.leftUnitor _ ≪≫
    (Pi.evalCompEqToEquivalenceFunctor (fun j => C (e j)) (e.symm_apply_apply i')).symm ≪≫
    isoWhiskerLeft _ ((Pi.eqToEquivalenceFunctorIso C e (e.symm_apply_apply i')).symm) ≪≫
    (Functor.pi'CompEval _ _).symm ≪≫ isoWhiskerLeft _ (Functor.pi'CompEval _ _).symm ≪≫
    (Functor.associator _ _ _).symm)
  counitIso := NatIso.pi' (fun i => (Functor.associator _ _ _).symm ≪≫
    isoWhiskerRight (Functor.pi'CompEval _ _) _ ≪≫
    Pi.evalCompEqToEquivalenceFunctor C (e.apply_symm_apply i) ≪≫
    (Functor.leftUnitor _).symm)
        CategoryTheory.Pi.optionEquivalence
      definition
     (C' : Option J → Type u₁) [∀ i, Category.{v₁} (C' i)] : (∀ i, C' i) ≌ C' none × (∀ (j : J), C' (some j))
        /-- A product of categories indexed by `Option J` identifies to a binary product. -/
@[simps]
def Pi.optionEquivalence (C' : Option J → Type u₁) [∀ i, Category.{v₁} (C' i)] :
    (∀ i, C' i) ≌ C' none × (∀ (j : J), C' (some j)) where
  functor := Functor.prod' (Pi.eval C' none)
    (Functor.pi' (fun i => (Pi.eval _ (some i))))
  inverse := Functor.pi' (fun i => match i with
    | none => Prod.fst _ _
    | some i => Prod.snd _ _ ⋙ (Pi.eval _ i))
  unitIso := NatIso.pi' (fun i => match i with
    | none => Iso.refl _
    | some _ => Iso.refl _)
  counitIso := by exact Iso.refl _
        CategoryTheory.Equivalence.pi
      definition
     (E : ∀ i, C i ≌ D i) : (∀ i, C i) ≌ (∀ i, D i)
        /-- Assemble an `I`-indexed family of equivalences of categories
into a single equivalence. -/
@[simps]
def pi (E : ∀ i, C i ≌ D i) : (∀ i, C i) ≌ (∀ i, D i) where
  functor := Functor.pi (fun i => (E i).functor)
  inverse := Functor.pi (fun i => (E i).inverse)
  unitIso := NatIso.pi (fun i => (E i).unitIso)
  counitIso := NatIso.pi (fun i => (E i).counitIso)
        CategoryTheory.Equivalence.instIsEquivalenceForallPi
      instance
     (F : ∀ i, C i ⥤ D i) [∀ i, (F i).IsEquivalence] : (Functor.pi F).IsEquivalence
        instance (F : ∀ i, C i ⥤ D i) [∀ i, (F i).IsEquivalence] :
    (Functor.pi F).IsEquivalence :=
  (pi (fun i => (F i).asEquivalence)).isEquivalence_functor