l3lab/ntp-mathlib huggingface dataset:
/-- 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) : comap 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 

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₁
K : Type w₂
f : K → J
g : J → I
X Y : (i : I) → C i
f' : X ⟶ Y
⊢ (comap C (g ∘ f)).map f' ≫ (fun X b => 𝟙 (X (g (f b)))) Y =
    (fun X b => 𝟙 (X (g (f b)))) X ≫ (comap C g ⋙ comap (C ∘ g) f).map f'

Next tactic:
simp only [comap, Function.comp]
/-- 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) : comap 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]; 

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₁
K : Type w₂
f : K → J
g : J → I
X Y : (i : I) → C i
f' : X ⟶ Y
⊢ ((fun i => f' (g (f i))) ≫ fun b => 𝟙 (Y (g (f b)))) =
    (fun b => 𝟙 (X (g (f b)))) ≫
      (Functor.mk { obj := fun f i => f (g i), map := fun {X Y} α i => α (g i) } ⋙
            Functor.mk { obj := fun f_1 i => f_1 (f i), map := fun {X Y} α i => α (f i) }).map
        f'

Next tactic:
funext
/-- 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) : comap 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; 

Proof state:
case h
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₁
K : Type w₂
f : K → J
g : J → I
X Y : (i : I) → C i
f' : X ⟶ Y
x✝ : K
⊢ ((fun i => f' (g (f i))) ≫ fun b => 𝟙 (Y (g (f b)))) x✝ =
    ((fun b => 𝟙 (X (g (f b)))) ≫
        (Functor.mk { obj := fun f i => f (g i), map := fun {X Y} α i => α (g i) } ⋙
              Functor.mk { obj := fun f_1 i => f_1 (f i), map := fun {X Y} α i => α (f i) }).map
          f')
      x✝

Next tactic:
simp
/-- The natural isomorphism between pulling back then evaluating, and just evaluating. -/
@[simps!]
def comapEvalIsoEval (h : J → I) (j : J) : comap C h ⋙ eval (C ∘ h) j ≅ eval C (h j) :=
  NatIso.ofComponents (fun f => Iso.refl _) (by 

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₁
K : Type w₂
h : J → I
j : J
⊢ ∀ {X Y : (i : I) → C i} (f : X ⟶ Y),
    (comap C h ⋙ eval (C ∘ h) j).map f ≫ ((fun f => Iso.refl ((comap C h ⋙ eval (C ∘ h) j).obj f)) Y).hom =
      ((fun f => Iso.refl ((comap C h ⋙ eval (C ∘ h) j).obj f)) X).hom ≫ (eval C (h j)).map f

Next tactic:
simp only [Iso.refl]
/-- The natural isomorphism between pulling back then evaluating, and just evaluating. -/
@[simps!]
def comapEvalIsoEval (h : J → I) (j : J) : comap C h ⋙ eval (C ∘ h) j ≅ eval C (h j) :=
  NatIso.ofComponents (fun f => Iso.refl _) (by simp only [Iso.refl]; 

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₁
K : Type w₂
h : J → I
j : J
⊢ ∀ {X Y : (i : I) → C i} (f : X ⟶ Y),
    (comap C h ⋙ eval (C ∘ h) j).map f ≫ 𝟙 ((comap C h ⋙ eval (C ∘ h) j).obj Y) =
      𝟙 ((comap C h ⋙ eval (C ∘ h) j).obj X) ≫ (eval C (h j)).map f

Next tactic:
aesop_cat
instance sumElimCategory : ∀ s : Sum I J, Category.{v₁} (Sum.elim C D s)
  | Sum.inl i => by
    

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝¹ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₀
D : J → Type u₁
inst✝ : (j : J) → Category.{v₁, u₁} (D j)
i : I
⊢ Category.{v₁, u₁} (Sum.elim C D (Sum.inl i))

Next tactic:
dsimp
instance sumElimCategory : ∀ s : Sum I J, Category.{v₁} (Sum.elim C D s)
  | Sum.inl i => by
    dsimp
    

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝¹ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₀
D : J → Type u₁
inst✝ : (j : J) → Category.{v₁, u₁} (D j)
i : I
⊢ Category.{v₁, u₁} (C i)

Next tactic:
infer_instance
instance sumElimCategory : ∀ s : Sum I J, Category.{v₁} (Sum.elim C D s)
  | Sum.inl i => by
    dsimp
    infer_instance
  | Sum.inr j => by
    

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝¹ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₀
D : J → Type u₁
inst✝ : (j : J) → Category.{v₁, u₁} (D j)
j : J
⊢ Category.{v₁, u₁} (Sum.elim C D (Sum.inr j))

Next tactic:
dsimp
instance sumElimCategory : ∀ s : Sum I J, Category.{v₁} (Sum.elim C D s)
  | Sum.inl i => by
    dsimp
    infer_instance
  | Sum.inr j => by
    dsimp
    

Proof state:
I : Type w₀
J✝ : Type w₁
C : I → Type u₁
inst✝¹ : (i : I) → Category.{v₁, u₁} (C i)
J : Type w₀
D : J → Type u₁
inst✝ : (j : J) → Category.{v₁, u₁} (D j)
j : J
⊢ Category.{v₁, u₁} (D j)

Next tactic:
infer_instance
/-- 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 

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ≅ Y
i : I
⊢ f.hom i ≫ f.inv i = 𝟙 (X i)

Next tactic:
rw [← comp_apply, Iso.hom_inv_id, id_apply]
/-- 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 

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ≅ Y
i : I
⊢ f.inv i ≫ f.hom i = 𝟙 (Y i)

Next tactic:
rw [← comp_apply, Iso.inv_hom_id, id_apply]
@[simp]
theorem eqToHom_proj {x x' : ∀ i, C i} (h : x = x') (i : I) :
    (eqToHom h : x ⟶ x') i = eqToHom (Function.funext_iff.mp h i) := by
  

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
x x' : (i : I) → C i
h : x = x'
i : I
⊢ eqToHom h i = eqToHom (_ : x i = x' i)

Next tactic:
subst h
@[simp]
theorem eqToHom_proj {x x' : ∀ i, C i} (h : x = x') (i : I) :
    (eqToHom h : x ⟶ x') i = eqToHom (Function.funext_iff.mp h i) := by
  subst h
  

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
x : (i : I) → C i
i : I
⊢ eqToHom (_ : x = x) i = eqToHom (_ : x i = x i)

Next tactic:
rfl
@[simp]
theorem pi'_eval (f : ∀ i, A ⥤ C i) (i : I) : pi' f ⋙ Pi.eval C i = f i := by
  

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f : (i : I) → A ⥤ C i
i : I
⊢ pi' f ⋙ Pi.eval C i = f i

Next tactic:
apply Functor.ext
@[simp]
theorem pi'_eval (f : ∀ i, A ⥤ C i) (i : I) : pi' f ⋙ Pi.eval C i = f i := by
  apply Functor.ext
  

Proof state:
case h_map
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f : (i : I) → A ⥤ C i
i : I
⊢ autoParam
    (∀ (X Y : A) (f_1 : X ⟶ Y),
      (pi' f ⋙ Pi.eval C i).map f_1 =
        eqToHom (_ : ?F.obj X = ?G.obj X) ≫ (f i).map f_1 ≫ eqToHom (_ : (f i).obj Y = (pi' f ⋙ Pi.eval C i).obj Y))
    _auto✝
case h_obj
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f : (i : I) → A ⥤ C i
i : I
⊢ ∀ (X : A), (pi' f ⋙ Pi.eval C i).obj X = (f i).obj X

Next tactic:
intro _ _ _
@[simp]
theorem pi'_eval (f : ∀ i, A ⥤ C i) (i : I) : pi' f ⋙ Pi.eval C i = f i := by
  apply Functor.ext
  intro _ _ _
  · 

Proof state:
case h_map
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f : (i : I) → A ⥤ C i
i : I
X✝ Y✝ : A
f✝ : X✝ ⟶ Y✝
⊢ (pi' f ⋙ Pi.eval C i).map f✝ =
    eqToHom (_ : ?F.obj X✝ = ?G.obj X✝) ≫ (f i).map f✝ ≫ eqToHom (_ : (f i).obj Y✝ = (pi' f ⋙ Pi.eval C i).obj Y✝)

Next tactic:
simp
@[simp]
theorem pi'_eval (f : ∀ i, A ⥤ C i) (i : I) : pi' f ⋙ Pi.eval C i = f i := by
  apply Functor.ext
  intro _ _ _
  · simp
  · 

Proof state:
case h_obj
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f : (i : I) → A ⥤ C i
i : I
⊢ ∀ (X : A), (pi' f ⋙ Pi.eval C i).obj X = (f i).obj X

Next tactic:
intro _
@[simp]
theorem pi'_eval (f : ∀ i, A ⥤ C i) (i : I) : pi' f ⋙ Pi.eval C i = f i := by
  apply Functor.ext
  intro _ _ _
  · simp
  · intro _
    

Proof state:
case h_obj
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f : (i : I) → A ⥤ C i
i : I
X✝ : A
⊢ (pi' f ⋙ Pi.eval C i).obj X✝ = (f i).obj X✝

Next tactic:
rfl
/-- 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
  

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
⊢ f = f'

Next tactic:
apply Functor.ext
/-- 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; 

Proof state:
case h_map
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
⊢ autoParam
    (∀ (X Y : A) (f_1 : X ⟶ Y),
      f.map f_1 = eqToHom (_ : ?F.obj X = ?G.obj X) ≫ f'.map f_1 ≫ eqToHom (_ : f'.obj Y = f.obj Y))
    _auto✝
case h_obj
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
⊢ ∀ (X : A), f.obj X = f'.obj X

Next tactic:
rotate_left
/-- 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
  · 

Proof state:
case h_obj
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
⊢ ∀ (X : A), f.obj X = f'.obj X

Next tactic:
intro X
/-- 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
    

Proof state:
case h_obj
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
X : A
⊢ f.obj X = f'.obj X

Next tactic:
ext i
/-- 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
    

Proof state:
case h_obj.h
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
X : A
i : I
⊢ f.obj X i = f'.obj X i

Next tactic:
specialize h i
/-- 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
    

Proof state:
case h_obj.h
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
X : A
i : I
h : f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
⊢ f.obj X i = f'.obj X i

Next tactic:
have := congr_obj h X
/-- 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
    

Proof state:
case h_obj.h
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
X : A
i : I
h : f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
this : (f ⋙ Pi.eval C i).obj X = (f' ⋙ Pi.eval C i).obj X
⊢ f.obj X i = f'.obj X i

Next tactic:
simpa
/-- 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
  · 

Proof state:
case h_map
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
⊢ autoParam
    (∀ (X Y : A) (f_1 : X ⟶ Y),
      f.map f_1 = eqToHom (_ : f.obj X = f'.obj X) ≫ f'.map f_1 ≫ eqToHom (_ : f'.obj Y = f.obj Y))
    _auto✝

Next tactic:
intro X Y g
/-- 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
    

Proof state:
case h_map
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
X Y : A
g : X ⟶ Y
⊢ f.map g = eqToHom (_ : f.obj X = f'.obj X) ≫ f'.map g ≫ eqToHom (_ : f'.obj Y = f.obj Y)

Next tactic:
dsimp
/-- 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
    

Proof state:
case h_map
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
X Y : A
g : X ⟶ Y
⊢ f.map g = eqToHom (_ : f.obj X = f'.obj X) ≫ f'.map g ≫ eqToHom (_ : f'.obj Y = f.obj Y)

Next tactic:
funext i
/-- 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
    

Proof state:
case h_map.h
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
X Y : A
g : X ⟶ Y
i : I
⊢ f.map g i = (eqToHom (_ : f.obj X = f'.obj X) ≫ f'.map g ≫ eqToHom (_ : f'.obj Y = f.obj Y)) i

Next tactic:
specialize h i
/-- 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
    

Proof state:
case h_map.h
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h✝ : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
X Y : A
g : X ⟶ Y
i : I
h : f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
⊢ f.map g i = (eqToHom (_ : f.obj X = f'.obj X) ≫ f'.map g ≫ eqToHom (_ : f'.obj Y = f.obj Y)) i

Next tactic:
have := congr_hom h g
/-- 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
    

Proof state:
case h_map.h
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
A : Type u₃
inst✝ : Category.{v₃, u₃} A
f f' : A ⥤ ((i : I) → C i)
h✝ : ∀ (i : I), f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
X Y : A
g : X ⟶ Y
i : I
h : f ⋙ Pi.eval C i = f' ⋙ Pi.eval C i
this :
  (f ⋙ Pi.eval C i).map g =
    eqToHom (_ : (f ⋙ Pi.eval C i).obj X = (f' ⋙ Pi.eval C i).obj X) ≫
      (f' ⋙ Pi.eval C i).map g ≫ eqToHom (_ : (f' ⋙ Pi.eval C i).obj Y = (f ⋙ Pi.eval C i).obj Y)
⊢ f.map g i = (eqToHom (_ : f.obj X = f'.obj X) ≫ f'.map g ≫ eqToHom (_ : f'.obj Y = f.obj Y)) i

Next tactic:
simpa
/-- 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 i ⟶ G ⋙ Pi.eval C i) : F ⟶ G where
  app := fun X i => (τ i).app X
  naturality _ _ f := by
    

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
F✝ G✝ : (i : I) → C i ⥤ D i
E : Type u_1
inst✝ : Category.{?u.175596, u_1} E
F G : E ⥤ ((i : I) → C i)
τ : (i : I) → F ⋙ Pi.eval C i ⟶ G ⋙ Pi.eval C i
x✝¹ x✝ : E
f : x✝¹ ⟶ x✝
⊢ F.map f ≫ (fun X i => (τ i).app X) x✝ = (fun X i => (τ i).app X) x✝¹ ≫ G.map f

Next tactic:
ext i
/-- 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 i ⟶ G ⋙ Pi.eval C i) : F ⟶ G where
  app := fun X i => (τ i).app X
  naturality _ _ f := by
    ext i
    

Proof state:
case w
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝² : (i : I) → Category.{v₁, u₁} (C i)
D : I → Type u₂
inst✝¹ : (i : I) → Category.{v₂, u₂} (D i)
F✝ G✝ : (i : I) → C i ⥤ D i
E : Type u_1
inst✝ : Category.{?u.175596, u_1} E
F G : E ⥤ ((i : I) → C i)
τ : (i : I) → F ⋙ Pi.eval C i ⟶ G ⋙ Pi.eval C i
x✝¹ x✝ : E
f : x✝¹ ⟶ x✝
i : I
⊢ (F.map f ≫ (fun X i => (τ i).app X) x✝) i = ((fun X i => (τ i).app X) x✝¹ ≫ G.map f) i

Next tactic:
exact (τ i).naturality f
lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIso f ↔ ∀ i, IsIso (f i) := by
  

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ⟶ Y
⊢ IsIso f ↔ ∀ (i : I), IsIso (f i)

Next tactic:
constructor
lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIso f ↔ ∀ i, IsIso (f i) := by
  constructor
  · 

Proof state:
case mp
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ⟶ Y
⊢ IsIso f → ∀ (i : I), IsIso (f i)

Next tactic:
intro _ i
lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIso f ↔ ∀ i, IsIso (f i) := by
  constructor
  · intro _ i
    

Proof state:
case mp
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ⟶ Y
a✝ : IsIso f
i : I
⊢ IsIso (f i)

Next tactic:
exact IsIso.of_iso (Pi.isoApp (asIso f) i)
lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIso f ↔ ∀ i, IsIso (f i) := by
  constructor
  · intro _ i
    exact IsIso.of_iso (Pi.isoApp (asIso f) i)
  · 

Proof state:
case mpr
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ⟶ Y
⊢ (∀ (i : I), IsIso (f i)) → IsIso f

Next tactic:
intro
lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIso f ↔ ∀ i, IsIso (f i) := by
  constructor
  · intro _ i
    exact IsIso.of_iso (Pi.isoApp (asIso f) i)
  · intro
    

Proof state:
case mpr
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ⟶ Y
a✝ : ∀ (i : I), IsIso (f i)
⊢ IsIso f

Next tactic:
exact ⟨fun i => inv (f i), by aesop_cat, by aesop_cat⟩
lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIso f ↔ ∀ i, IsIso (f i) := by
  constructor
  · intro _ i
    exact IsIso.of_iso (Pi.isoApp (asIso f) i)
  · intro
    exact ⟨fun i => inv (f i), by 

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ⟶ Y
a✝ : ∀ (i : I), IsIso (f i)
⊢ (f ≫ fun i => inv (f i)) = 𝟙 X

Next tactic:
aesop_cat
lemma isIso_pi_iff {X Y : ∀ i, C i} (f : X ⟶ Y) :
    IsIso f ↔ ∀ i, IsIso (f i) := by
  constructor
  · intro _ i
    exact IsIso.of_iso (Pi.isoApp (asIso f) i)
  · intro
    exact ⟨fun i => inv (f i), by aesop_cat, by 

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
X Y : (i : I) → C i
f : X ⟶ Y
a✝ : ∀ (i : I), IsIso (f i)
⊢ (fun i => inv (f i)) ≫ f = 𝟙 Y

Next tactic:
aesop_cat
/-- 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 

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
i j : I
h : i = j
⊢ C i ≌ C j

Next tactic:
subst h
/-- 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; 

Proof state:
I : Type w₀
J : Type w₁
C : I → Type u₁
inst✝ : (i : I) → Category.{v₁, u₁} (C i)
i : I
⊢ C i ≌ C i

Next tactic:
rfl