l3lab/ntp-mathlib huggingface dataset:
/-- Taking projective resolutions is functorial,
if considered with target the homotopy category
(`ℕ`-indexed chain complexes and chain maps up to homotopy).
-/
def projectiveResolutions : C ⥤ HomotopyCategory C (ComplexShape.down ℕ) where
  obj X := (HomotopyCategory.quotient _ _).obj (projectiveResolution X).complex
  map f := (HomotopyCategory.quotient _ _).map (ProjectiveResolution.lift f _ _)
  map_id X := by
    rw [← (HomotopyCategory.quotient _ _).map_id]
    apply HomotopyCategory.eq_of_homotopy
    apply ProjectiveResolution.liftIdHomotopy
  map_comp f g := by
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X✝ Y✝ Z✝ : C
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ { obj := fun X => (HomotopyCategory.quotient C (ComplexShape.down ℕ)).obj (projectiveResolution X).complex,
          map := fun {X Y} f =>
            (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map
              (ProjectiveResolution.lift f (projectiveResolution X) (projectiveResolution Y)) }.map
      (f ≫ g) =
    { obj := fun X => (HomotopyCategory.quotient C (ComplexShape.down ℕ)).obj (projectiveResolution X).complex,
            map := fun {X Y} f =>
              (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map
                (ProjectiveResolution.lift f (projectiveResolution X) (projectiveResolution Y)) }.map
        f ≫
      { obj := fun X => (HomotopyCategory.quotient C (ComplexShape.down ℕ)).obj (projectiveResolution X).complex,
            map := fun {X Y} f =>
              (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map
                (ProjectiveResolution.lift f (projectiveResolution X) (projectiveResolution Y)) }.map
        g

Next tactic:
rw [← (HomotopyCategory.quotient _ _).map_comp]
/-- Taking projective resolutions is functorial,
if considered with target the homotopy category
(`ℕ`-indexed chain complexes and chain maps up to homotopy).
-/
def projectiveResolutions : C ⥤ HomotopyCategory C (ComplexShape.down ℕ) where
  obj X := (HomotopyCategory.quotient _ _).obj (projectiveResolution X).complex
  map f := (HomotopyCategory.quotient _ _).map (ProjectiveResolution.lift f _ _)
  map_id X := by
    rw [← (HomotopyCategory.quotient _ _).map_id]
    apply HomotopyCategory.eq_of_homotopy
    apply ProjectiveResolution.liftIdHomotopy
  map_comp f g := by
    rw [← (HomotopyCategory.quotient _ _).map_comp]
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X✝ Y✝ Z✝ : C
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ { obj := fun X => (HomotopyCategory.quotient C (ComplexShape.down ℕ)).obj (projectiveResolution X).complex,
          map := fun {X Y} f =>
            (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map
              (ProjectiveResolution.lift f (projectiveResolution X) (projectiveResolution Y)) }.map
      (f ≫ g) =
    (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map
      (ProjectiveResolution.lift f (projectiveResolution X✝) (projectiveResolution Y✝) ≫
        ProjectiveResolution.lift g (projectiveResolution Y✝) (projectiveResolution Z✝))

Next tactic:
apply HomotopyCategory.eq_of_homotopy
/-- Taking projective resolutions is functorial,
if considered with target the homotopy category
(`ℕ`-indexed chain complexes and chain maps up to homotopy).
-/
def projectiveResolutions : C ⥤ HomotopyCategory C (ComplexShape.down ℕ) where
  obj X := (HomotopyCategory.quotient _ _).obj (projectiveResolution X).complex
  map f := (HomotopyCategory.quotient _ _).map (ProjectiveResolution.lift f _ _)
  map_id X := by
    rw [← (HomotopyCategory.quotient _ _).map_id]
    apply HomotopyCategory.eq_of_homotopy
    apply ProjectiveResolution.liftIdHomotopy
  map_comp f g := by
    rw [← (HomotopyCategory.quotient _ _).map_comp]
    apply HomotopyCategory.eq_of_homotopy
    

Proof state:
case h
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X✝ Y✝ Z✝ : C
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ Homotopy (ProjectiveResolution.lift (f ≫ g) (projectiveResolution X✝) (projectiveResolution Z✝))
    (ProjectiveResolution.lift f (projectiveResolution X✝) (projectiveResolution Y✝) ≫
      ProjectiveResolution.lift g (projectiveResolution Y✝) (projectiveResolution Z✝))

Next tactic:
apply ProjectiveResolution.liftCompHomotopy
@[reassoc]
lemma ProjectiveResolution.iso_inv_naturality {X Y : C} (f : X ⟶ Y)
    (P : ProjectiveResolution X) (Q : ProjectiveResolution Y)
    (φ : P.complex ⟶ Q.complex) (comm : φ.f 0 ≫ Q.π.f 0 = P.π.f 0 ≫ f) :
    P.iso.inv ≫ (projectiveResolutions C).map f =
      (HomotopyCategory.quotient _ _).map φ ≫ Q.iso.inv := by
  

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X Y : C
f : X ⟶ Y
P : ProjectiveResolution X
Q : ProjectiveResolution Y
φ : P.complex ⟶ Q.complex
comm : HomologicalComplex.Hom.f φ 0 ≫ HomologicalComplex.Hom.f Q.π 0 = HomologicalComplex.Hom.f P.π 0 ≫ f
⊢ (iso P).inv ≫ (projectiveResolutions C).map f =
    (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map φ ≫ (iso Q).inv

Next tactic:
apply HomotopyCategory.eq_of_homotopy
@[reassoc]
lemma ProjectiveResolution.iso_inv_naturality {X Y : C} (f : X ⟶ Y)
    (P : ProjectiveResolution X) (Q : ProjectiveResolution Y)
    (φ : P.complex ⟶ Q.complex) (comm : φ.f 0 ≫ Q.π.f 0 = P.π.f 0 ≫ f) :
    P.iso.inv ≫ (projectiveResolutions C).map f =
      (HomotopyCategory.quotient _ _).map φ ≫ Q.iso.inv := by
  apply HomotopyCategory.eq_of_homotopy
  

Proof state:
case h
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X Y : C
f : X ⟶ Y
P : ProjectiveResolution X
Q : ProjectiveResolution Y
φ : P.complex ⟶ Q.complex
comm : HomologicalComplex.Hom.f φ 0 ≫ HomologicalComplex.Hom.f Q.π 0 = HomologicalComplex.Hom.f P.π 0 ≫ f
⊢ Homotopy ((homotopyEquiv (projectiveResolution X) P).inv ≫ lift f (projectiveResolution X) (projectiveResolution Y))
    (φ ≫ (homotopyEquiv (projectiveResolution Y) Q).inv)

Next tactic:
apply liftHomotopy f
@[reassoc]
lemma ProjectiveResolution.iso_inv_naturality {X Y : C} (f : X ⟶ Y)
    (P : ProjectiveResolution X) (Q : ProjectiveResolution Y)
    (φ : P.complex ⟶ Q.complex) (comm : φ.f 0 ≫ Q.π.f 0 = P.π.f 0 ≫ f) :
    P.iso.inv ≫ (projectiveResolutions C).map f =
      (HomotopyCategory.quotient _ _).map φ ≫ Q.iso.inv := by
  apply HomotopyCategory.eq_of_homotopy
  apply liftHomotopy f
  

Proof state:
case h.g_comm
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X Y : C
f : X ⟶ Y
P : ProjectiveResolution X
Q : ProjectiveResolution Y
φ : P.complex ⟶ Q.complex
comm : HomologicalComplex.Hom.f φ 0 ≫ HomologicalComplex.Hom.f Q.π 0 = HomologicalComplex.Hom.f P.π 0 ≫ f
⊢ ((homotopyEquiv (projectiveResolution X) P).inv ≫ lift f (projectiveResolution X) (projectiveResolution Y)) ≫
      (projectiveResolution Y).π =
    P.π ≫ (ChainComplex.single₀ C).map f
case h.h_comm
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X Y : C
f : X ⟶ Y
P : ProjectiveResolution X
Q : ProjectiveResolution Y
φ : P.complex ⟶ Q.complex
comm : HomologicalComplex.Hom.f φ 0 ≫ HomologicalComplex.Hom.f Q.π 0 = HomologicalComplex.Hom.f P.π 0 ≫ f
⊢ (φ ≫ (homotopyEquiv (projectiveResolution Y) Q).inv) ≫ (projectiveResolution Y).π =
    P.π ≫ (ChainComplex.single₀ C).map f

Next tactic:
all_goals
    aesop_cat
@[reassoc]
lemma ProjectiveResolution.iso_inv_naturality {X Y : C} (f : X ⟶ Y)
    (P : ProjectiveResolution X) (Q : ProjectiveResolution Y)
    (φ : P.complex ⟶ Q.complex) (comm : φ.f 0 ≫ Q.π.f 0 = P.π.f 0 ≫ f) :
    P.iso.inv ≫ (projectiveResolutions C).map f =
      (HomotopyCategory.quotient _ _).map φ ≫ Q.iso.inv := by
  apply HomotopyCategory.eq_of_homotopy
  apply liftHomotopy f
  all_goals
    

Proof state:
case h.g_comm
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X Y : C
f : X ⟶ Y
P : ProjectiveResolution X
Q : ProjectiveResolution Y
φ : P.complex ⟶ Q.complex
comm : HomologicalComplex.Hom.f φ 0 ≫ HomologicalComplex.Hom.f Q.π 0 = HomologicalComplex.Hom.f P.π 0 ≫ f
⊢ ((homotopyEquiv (projectiveResolution X) P).inv ≫ lift f (projectiveResolution X) (projectiveResolution Y)) ≫
      (projectiveResolution Y).π =
    P.π ≫ (ChainComplex.single₀ C).map f

Next tactic:
aesop_cat
@[reassoc]
lemma ProjectiveResolution.iso_inv_naturality {X Y : C} (f : X ⟶ Y)
    (P : ProjectiveResolution X) (Q : ProjectiveResolution Y)
    (φ : P.complex ⟶ Q.complex) (comm : φ.f 0 ≫ Q.π.f 0 = P.π.f 0 ≫ f) :
    P.iso.inv ≫ (projectiveResolutions C).map f =
      (HomotopyCategory.quotient _ _).map φ ≫ Q.iso.inv := by
  apply HomotopyCategory.eq_of_homotopy
  apply liftHomotopy f
  all_goals
    

Proof state:
case h.h_comm
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X Y : C
f : X ⟶ Y
P : ProjectiveResolution X
Q : ProjectiveResolution Y
φ : P.complex ⟶ Q.complex
comm : HomologicalComplex.Hom.f φ 0 ≫ HomologicalComplex.Hom.f Q.π 0 = HomologicalComplex.Hom.f P.π 0 ≫ f
⊢ (φ ≫ (homotopyEquiv (projectiveResolution Y) Q).inv) ≫ (projectiveResolution Y).π =
    P.π ≫ (ChainComplex.single₀ C).map f

Next tactic:
aesop_cat
@[reassoc]
lemma ProjectiveResolution.iso_hom_naturality {X Y : C} (f : X ⟶ Y)
    (P : ProjectiveResolution X) (Q : ProjectiveResolution Y)
    (φ : P.complex ⟶ Q.complex) (comm : φ.f 0 ≫ Q.π.f 0 = P.π.f 0 ≫ f) :
    (projectiveResolutions C).map f ≫ Q.iso.hom =
      P.iso.hom ≫ (HomotopyCategory.quotient _ _).map φ := by
  

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X Y : C
f : X ⟶ Y
P : ProjectiveResolution X
Q : ProjectiveResolution Y
φ : P.complex ⟶ Q.complex
comm : HomologicalComplex.Hom.f φ 0 ≫ HomologicalComplex.Hom.f Q.π 0 = HomologicalComplex.Hom.f P.π 0 ≫ f
⊢ (projectiveResolutions C).map f ≫ (iso Q).hom =
    (iso P).hom ≫ (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map φ

Next tactic:
rw [← cancel_epi (P.iso).inv, iso_inv_naturality_assoc f P Q φ comm,
    Iso.inv_hom_id_assoc, Iso.inv_hom_id, comp_id]
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
⊢ d f ≫ f = 0

Next tactic:
simp
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
⊢ ShortComplex.Exact (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0))

Next tactic:
let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
⊢ d f ≫ f = 0

Next tactic:
simp
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
⊢ kernel.ι f ≫ f = 0

Next tactic:
simp
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
⊢ ShortComplex.Exact (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0))

Next tactic:
have : Epi α.τ₁ := by dsimp; infer_instance
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
⊢ Epi α.τ₁

Next tactic:
dsimp
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
⊢ Epi (π (kernel f))

Next tactic:
infer_instance
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this : Epi α.τ₁
⊢ ShortComplex.Exact (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0))

Next tactic:
have : IsIso α.τ₂ := by dsimp; infer_instance
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this : Epi α.τ₁
⊢ IsIso α.τ₂

Next tactic:
dsimp
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by dsimp; 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this : Epi α.τ₁
⊢ IsIso (𝟙 X)

Next tactic:
infer_instance
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by dsimp; infer_instance
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this✝ : Epi α.τ₁
this : IsIso α.τ₂
⊢ ShortComplex.Exact (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0))

Next tactic:
have : Mono α.τ₃ := by dsimp; infer_instance
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by dsimp; infer_instance
  have : Mono α.τ₃ := by 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this✝ : Epi α.τ₁
this : IsIso α.τ₂
⊢ Mono α.τ₃

Next tactic:
dsimp
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by dsimp; infer_instance
  have : Mono α.τ₃ := by dsimp; 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this✝ : Epi α.τ₁
this : IsIso α.τ₂
⊢ Mono (𝟙 Y)

Next tactic:
infer_instance
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by dsimp; infer_instance
  have : Mono α.τ₃ := by dsimp; infer_instance
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this✝¹ : Epi α.τ₁
this✝ : IsIso α.τ₂
this : Mono α.τ₃
⊢ ShortComplex.Exact (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0))

Next tactic:
rw [ShortComplex.exact_iff_of_epi_of_isIso_of_mono α]
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by dsimp; infer_instance
  have : Mono α.τ₃ := by dsimp; infer_instance
  rw [ShortComplex.exact_iff_of_epi_of_isIso_of_mono α]
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this✝¹ : Epi α.τ₁
this✝ : IsIso α.τ₂
this : Mono α.τ₃
⊢ ShortComplex.Exact (ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0))

Next tactic:
apply ShortComplex.exact_of_f_is_kernel
theorem exact_d_f {X Y : C} (f : X ⟶ Y) :
    (ShortComplex.mk (d f) f (by simp)).Exact := by
  let α : ShortComplex.mk (d f) f (by simp) ⟶ ShortComplex.mk (kernel.ι f) f (by simp) :=
    { τ₁ := Projective.π _
      τ₂ := 𝟙 _
      τ₃ := 𝟙 _ }
  have : Epi α.τ₁ := by dsimp; infer_instance
  have : IsIso α.τ₂ := by dsimp; infer_instance
  have : Mono α.τ₃ := by dsimp; infer_instance
  rw [ShortComplex.exact_iff_of_epi_of_isIso_of_mono α]
  apply ShortComplex.exact_of_f_is_kernel
  

Proof state:
case hS
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
X Y : C
f : X ⟶ Y
α : ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0) ⟶
  ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0) :=
  ShortComplex.Hom.mk (π (kernel f)) (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₂)
    (𝟙 (ShortComplex.mk (d f) f (_ : (π (kernel f) ≫ kernel.ι f) ≫ f = 0)).X₃)
this✝¹ : Epi α.τ₁
this✝ : IsIso α.τ₂
this : Mono α.τ₃
⊢ IsLimit
    (KernelFork.ofι (ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0)).f
      (_ :
        (ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0)).f ≫
            (ShortComplex.mk (kernel.ι f) f (_ : kernel.ι f ≫ f = 0)).g =
          0))

Next tactic:
apply kernelIsKernel
/-- Auxiliary definition for `ProjectiveResolution.of`. -/
def ofComplex : ChainComplex C ℕ :=
  ChainComplex.mk' (Projective.over Z) (Projective.syzygies (Projective.π Z))
    (Projective.d (Projective.π Z)) fun ⟨_, _, f⟩ =>
    ⟨Projective.syzygies f, Projective.d f, by 

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
Z : C
x✝ : (X₀ : C) × (X₁ : C) × (X₁ ⟶ X₀)
fst✝¹ fst✝ : C
f : fst✝ ⟶ fst✝¹
⊢ d f ≫ { fst := fst✝¹, snd := { fst := fst✝, snd := f } }.snd.snd = 0

Next tactic:
simp
lemma ofComplex_d_1_0 :
    (ofComplex Z).d 1 0 = d (Projective.π Z) := by
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
Z : C
⊢ HomologicalComplex.d (ofComplex Z) 1 0 = d (Projective.π Z)

Next tactic:
simp [ofComplex]
lemma ofComplex_exactAt_succ (n : ℕ) :
    (ofComplex Z).ExactAt (n + 1) := by
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : Abelian C
inst✝¹ : HasProjectiveResolutions C
inst✝ : EnoughProjectives C
Z : C
n : ℕ
⊢ HomologicalComplex.ExactAt (ofComplex Z) (n + 1)

Next tactic:
rw [HomologicalComplex.exactAt_iff' _ (n + 1 + 1) (n + 1) n (by simp) (by simp)]