l3lab/ntp-mathlib huggingface dataset:
theorem zero_term {α} [AddCommMonoid α] (x a) : @term α _ 0 x a = a := by
  

Proof state:
α : Type u_1
inst✝ : AddCommMonoid α
x a : α
⊢ term 0 x a = a

Next tactic:
simp [term, zero_nsmul, one_nsmul]
theorem zero_termg {α} [AddCommGroup α] (x a) : @termg α _ 0 x a = a := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
x a : α
⊢ termg 0 x a = a

Next tactic:
simp [termg, zero_zsmul]
theorem term_neg {α} [AddCommGroup α] (n x a n' a')
    (h₁ : -n = n') (h₂ : -a = a') : -@termg α _ n x a = termg n' x a' := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
n : ℤ
x a : α
n' : ℤ
a' : α
h₁ : -n = n'
h₂ : -a = a'
⊢ -termg n x a = termg n' x a'

Next tactic:
simp [h₂.symm, h₁.symm, termg]
theorem term_neg {α} [AddCommGroup α] (n x a n' a')
    (h₁ : -n = n') (h₂ : -a = a') : -@termg α _ n x a = termg n' x a' := by
  simp [h₂.symm, h₁.symm, termg]; 

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
n : ℤ
x a : α
n' : ℤ
a' : α
h₁ : -n = n'
h₂ : -a = a'
⊢ -a + -(n • x) = -(n • x) + -a

Next tactic:
exact add_comm _ _
theorem zero_smul {α} [AddCommMonoid α] (c) : smul c (0 : α) = 0 := by
  

Proof state:
α : Type u_1
inst✝ : AddCommMonoid α
c : ℕ
⊢ smul c 0 = 0

Next tactic:
simp [smul, nsmul_zero]
theorem zero_smulg {α} [AddCommGroup α] (c) : smulg c (0 : α) = 0 := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
c : ℤ
⊢ smulg c 0 = 0

Next tactic:
simp [smulg, zsmul_zero]
theorem term_smul {α} [AddCommMonoid α] (c n x a n' a')
    (h₁ : c * n = n') (h₂ : smul c a = a') :
    smul c (@term α _ n x a) = term n' x a' := by
  

Proof state:
α : Type u_1
inst✝ : AddCommMonoid α
c n : ℕ
x a : α
n' : ℕ
a' : α
h₁ : c * n = n'
h₂ : smul c a = a'
⊢ smul c (term n x a) = term n' x a'

Next tactic:
simp [h₂.symm, h₁.symm, term, smul, nsmul_add, mul_nsmul']
theorem term_smulg {α} [AddCommGroup α] (c n x a n' a')
    (h₁ : c * n = n') (h₂ : smulg c a = a') :
    smulg c (@termg α _ n x a) = termg n' x a' := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
c n : ℤ
x a : α
n' : ℤ
a' : α
h₁ : c * n = n'
h₂ : smulg c a = a'
⊢ smulg c (termg n x a) = termg n' x a'

Next tactic:
simp [h₂.symm, h₁.symm, termg, smulg, zsmul_add, mul_zsmul]
theorem term_atom {α} [AddCommMonoid α] (x : α) : x = term 1 x 0 := by 

Proof state:
α : Type u_1
inst✝ : AddCommMonoid α
x : α
⊢ x = term 1 x 0

Next tactic:
simp [term]
theorem term_atomg {α} [AddCommGroup α] (x : α) : x = termg 1 x 0 := by 

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
x : α
⊢ x = termg 1 x 0

Next tactic:
simp [termg]
theorem term_atom_pf {α} [AddCommMonoid α] (x x' : α) (h : x = x') : x = term 1 x' 0 := by
  

Proof state:
α : Type u_1
inst✝ : AddCommMonoid α
x x' : α
h : x = x'
⊢ x = term 1 x' 0

Next tactic:
simp [term, h]
theorem term_atom_pfg {α} [AddCommGroup α] (x x' : α) (h : x = x') : x = termg 1 x' 0 := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
x x' : α
h : x = x'
⊢ x = termg 1 x' 0

Next tactic:
simp [termg, h]
theorem unfold_sub {α} [SubtractionMonoid α] (a b c : α) (h : a + -b = c) : a - b = c := by
  

Proof state:
α : Type u_1
inst✝ : SubtractionMonoid α
a b c : α
h : a + -b = c
⊢ a - b = c

Next tactic:
rw [sub_eq_add_neg, h]
lemma subst_into_smul {α} [AddCommMonoid α]
    (l r tl tr t) (prl : l = tl) (prr : r = tr)
    (prt : @smul α _ tl tr = t) : smul l r = t := by 

Proof state:
α : Type u_1
inst✝ : AddCommMonoid α
l : ℕ
r : α
tl : ℕ
tr t : α
prl : l = tl
prr : r = tr
prt : smul tl tr = t
⊢ smul l r = t

Next tactic:
simp [prl, prr, prt]
lemma subst_into_smulg {α} [AddCommGroup α]
    (l r tl tr t) (prl : l = tl) (prr : r = tr)
    (prt : @smulg α _ tl tr = t) : smulg l r = t := by 

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
l : ℤ
r : α
tl : ℤ
tr t : α
prl : l = tl
prr : r = tr
prt : smulg tl tr = t
⊢ smulg l r = t

Next tactic:
simp [prl, prr, prt]
lemma subst_into_smul_upcast {α} [AddCommGroup α]
    (l r tl zl tr t) (prl₁ : l = tl) (prl₂ : ↑tl = zl) (prr : r = tr)
    (prt : @smulg α _ zl tr = t) : smul l r = t := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
l : ℕ
r : α
tl : ℕ
zl : ℤ
tr t : α
prl₁ : l = tl
prl₂ : ↑tl = zl
prr : r = tr
prt : smulg zl tr = t
⊢ smul l r = t

Next tactic:
simp [← prt, prl₁, ← prl₂, prr, smul, smulg, coe_nat_zsmul]
lemma subst_into_add {α} [AddCommMonoid α] (l r tl tr t)
    (prl : (l : α) = tl) (prr : r = tr) (prt : tl + tr = t) : l + r = t := by
  

Proof state:
α : Type u_1
inst✝ : AddCommMonoid α
l r tl tr t : α
prl : l = tl
prr : r = tr
prt : tl + tr = t
⊢ l + r = t

Next tactic:
rw [prl, prr, prt]
lemma subst_into_addg {α} [AddCommGroup α] (l r tl tr t)
    (prl : (l : α) = tl) (prr : r = tr) (prt : tl + tr = t) : l + r = t := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
l r tl tr t : α
prl : l = tl
prr : r = tr
prt : tl + tr = t
⊢ l + r = t

Next tactic:
rw [prl, prr, prt]
lemma subst_into_negg {α} [AddCommGroup α] (a ta t : α)
    (pra : a = ta) (prt : -ta = t) : -a = t := by
  

Proof state:
α : Type u_1
inst✝ : AddCommGroup α
a ta t : α
pra : a = ta
prt : -ta = t
⊢ -a = t

Next tactic:
simp [pra, prt]
/-- Auxiliary construction for `lift`. -/
def liftFOne {Y Z : C} (f : Y ⟶ Z) (P : ProjectiveResolution Y) (Q : ProjectiveResolution Z) :
    P.complex.X 1 ⟶ Q.complex.X 1 :=
  Q.exact₀.liftFromProjective (P.complex.d 1 0 ≫ liftFZero f P Q) (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
f : Y ⟶ Z
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
⊢ (HomologicalComplex.d P.complex 1 0 ≫ liftFZero f P Q) ≫
      (ShortComplex.mk (HomologicalComplex.d Q.complex 1 0) (HomologicalComplex.Hom.f Q.π 0)
          (_ : HomologicalComplex.d Q.complex 1 0 ≫ HomologicalComplex.Hom.f Q.π 0 = 0)).g =
    0

Next tactic:
simp [liftFZero]
@[simp]
theorem liftFOne_zero_comm {Y Z : C} (f : Y ⟶ Z) (P : ProjectiveResolution Y)
    (Q : ProjectiveResolution Z) :
    liftFOne f P Q ≫ Q.complex.d 1 0 = P.complex.d 1 0 ≫ liftFZero f P Q :=  by
  

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
f : Y ⟶ Z
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
⊢ liftFOne f P Q ≫ HomologicalComplex.d Q.complex 1 0 = HomologicalComplex.d P.complex 1 0 ≫ liftFZero f P Q

Next tactic:
apply Q.exact₀.liftFromProjective_comp
/-- Auxiliary construction for `lift`. -/
def liftFSucc {Y Z : C} (P : ProjectiveResolution Y) (Q : ProjectiveResolution Z) (n : ℕ)
    (g : P.complex.X n ⟶ Q.complex.X n) (g' : P.complex.X (n + 1) ⟶ Q.complex.X (n + 1))
    (w : g' ≫ Q.complex.d (n + 1) n = P.complex.d (n + 1) n ≫ g) :
    Σ'g'' : P.complex.X (n + 2) ⟶ Q.complex.X (n + 2),
      g'' ≫ Q.complex.d (n + 2) (n + 1) = P.complex.d (n + 2) (n + 1) ≫ g' :=
  ⟨(Q.exact_succ n).liftFromProjective
    (P.complex.d (n + 2) (n + 1) ≫ g') (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
n : ℕ
g : HomologicalComplex.X P.complex n ⟶ HomologicalComplex.X Q.complex n
g' : HomologicalComplex.X P.complex (n + 1) ⟶ HomologicalComplex.X Q.complex (n + 1)
w : g' ≫ HomologicalComplex.d Q.complex (n + 1) n = HomologicalComplex.d P.complex (n + 1) n ≫ g
⊢ (HomologicalComplex.d P.complex (n + 2) (n + 1) ≫ g') ≫
      (ShortComplex.mk (HomologicalComplex.d Q.complex (n + 2) (n + 1)) (HomologicalComplex.d Q.complex (n + 1) n)
          (_ : HomologicalComplex.d Q.complex (n + 2) (n + 1) ≫ HomologicalComplex.d Q.complex (n + 1) n = 0)).g =
    0

Next tactic:
simp [w]
/-- The resolution maps intertwine the lift of a morphism and that morphism. -/
@[reassoc (attr := simp)]
theorem lift_commutes {Y Z : C} (f : Y ⟶ Z) (P : ProjectiveResolution Y)
    (Q : ProjectiveResolution Z) : lift f P Q ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f := by
  

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
f : Y ⟶ Z
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
⊢ lift f P Q ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f

Next tactic:
ext
/-- The resolution maps intertwine the lift of a morphism and that morphism. -/
@[reassoc (attr := simp)]
theorem lift_commutes {Y Z : C} (f : Y ⟶ Z) (P : ProjectiveResolution Y)
    (Q : ProjectiveResolution Z) : lift f P Q ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f := by
  ext
  

Proof state:
case hfg
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
f : Y ⟶ Z
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
⊢ HomologicalComplex.Hom.f (lift f P Q ≫ Q.π) 0 = HomologicalComplex.Hom.f (P.π ≫ (ChainComplex.single₀ C).map f) 0

Next tactic:
simp [lift, liftFZero, liftFOne]
@[reassoc (attr := simp)]
lemma lift_commutes_zero {Y Z : C} (f : Y ⟶ Z)
    (P : ProjectiveResolution Y) (Q : ProjectiveResolution Z) :
    (lift f P Q).f 0 ≫ Q.π.f 0 = P.π.f 0 ≫ f :=
  (HomologicalComplex.congr_hom (lift_commutes f P Q) 0).trans (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
f : Y ⟶ Z
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
⊢ HomologicalComplex.Hom.f (P.π ≫ (ChainComplex.single₀ C).map f) 0 = HomologicalComplex.Hom.f P.π 0 ≫ f

Next tactic:
simp
/-- An auxiliary definition for `liftHomotopyZero`. -/
def liftHomotopyZeroOne {Y Z : C} {P : ProjectiveResolution Y} {Q : ProjectiveResolution Z}
    (f : P.complex ⟶ Q.complex) (comm : f ≫ Q.π = 0) :
    P.complex.X 1 ⟶ Q.complex.X 2 :=
  (Q.exact_succ 0).liftFromProjective (f.f 1 - P.complex.d 1 0 ≫ liftHomotopyZeroZero f comm)
    (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
f : P.complex ⟶ Q.complex
comm : f ≫ Q.π = 0
⊢ (HomologicalComplex.Hom.f f 1 - HomologicalComplex.d P.complex 1 0 ≫ liftHomotopyZeroZero f comm) ≫
      (ShortComplex.mk (HomologicalComplex.d Q.complex (0 + 2) (0 + 1)) (HomologicalComplex.d Q.complex (0 + 1) 0)
          (_ : HomologicalComplex.d Q.complex (0 + 2) (0 + 1) ≫ HomologicalComplex.d Q.complex (0 + 1) 0 = 0)).g =
    0

Next tactic:
rw [Preadditive.sub_comp, assoc, HomologicalComplex.Hom.comm,
              liftHomotopyZeroZero_comp, sub_self]
/-- An auxiliary definition for `liftHomotopyZero`. -/
def liftHomotopyZeroSucc {Y Z : C} {P : ProjectiveResolution Y} {Q : ProjectiveResolution Z}
    (f : P.complex ⟶ Q.complex) (n : ℕ) (g : P.complex.X n ⟶ Q.complex.X (n + 1))
    (g' : P.complex.X (n + 1) ⟶ Q.complex.X (n + 2))
    (w : f.f (n + 1) = P.complex.d (n + 1) n ≫ g + g' ≫ Q.complex.d (n + 2) (n + 1)) :
    P.complex.X (n + 2) ⟶ Q.complex.X (n + 3) :=
  (Q.exact_succ (n + 1)).liftFromProjective (f.f (n + 2) - P.complex.d _ _ ≫ g') (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
f : P.complex ⟶ Q.complex
n : ℕ
g : HomologicalComplex.X P.complex n ⟶ HomologicalComplex.X Q.complex (n + 1)
g' : HomologicalComplex.X P.complex (n + 1) ⟶ HomologicalComplex.X Q.complex (n + 2)
w :
  HomologicalComplex.Hom.f f (n + 1) =
    HomologicalComplex.d P.complex (n + 1) n ≫ g + g' ≫ HomologicalComplex.d Q.complex (n + 2) (n + 1)
⊢ (HomologicalComplex.Hom.f f (n + 2) - HomologicalComplex.d P.complex (n + 2) (n + 1) ≫ g') ≫
      (ShortComplex.mk (HomologicalComplex.d Q.complex (n + 1 + 2) (n + 1 + 1))
          (HomologicalComplex.d Q.complex (n + 1 + 1) (n + 1))
          (_ :
            HomologicalComplex.d Q.complex (n + 1 + 2) (n + 1 + 1) ≫
                HomologicalComplex.d Q.complex (n + 1 + 1) (n + 1) =
              0)).g =
    0

Next tactic:
simp [w]
/-- Any lift of the zero morphism is homotopic to zero. -/
def liftHomotopyZero {Y Z : C} {P : ProjectiveResolution Y} {Q : ProjectiveResolution Z}
    (f : P.complex ⟶ Q.complex) (comm : f ≫ Q.π = 0) : Homotopy f 0 :=
  Homotopy.mkInductive _ (liftHomotopyZeroZero f comm) (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
f : P.complex ⟶ Q.complex
comm : f ≫ Q.π = 0
⊢ HomologicalComplex.Hom.f f 0 = liftHomotopyZeroZero f comm ≫ HomologicalComplex.d Q.complex 1 0

Next tactic:
simp
/-- Any lift of the zero morphism is homotopic to zero. -/
def liftHomotopyZero {Y Z : C} {P : ProjectiveResolution Y} {Q : ProjectiveResolution Z}
    (f : P.complex ⟶ Q.complex) (comm : f ≫ Q.π = 0) : Homotopy f 0 :=
  Homotopy.mkInductive _ (liftHomotopyZeroZero f comm) (by simp )
    (liftHomotopyZeroOne f comm) (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
f : P.complex ⟶ Q.complex
comm : f ≫ Q.π = 0
⊢ HomologicalComplex.Hom.f f 1 =
    HomologicalComplex.d P.complex 1 0 ≫ liftHomotopyZeroZero f comm +
      liftHomotopyZeroOne f comm ≫ HomologicalComplex.d Q.complex 2 1

Next tactic:
simp
/-- Any lift of the zero morphism is homotopic to zero. -/
def liftHomotopyZero {Y Z : C} {P : ProjectiveResolution Y} {Q : ProjectiveResolution Z}
    (f : P.complex ⟶ Q.complex) (comm : f ≫ Q.π = 0) : Homotopy f 0 :=
  Homotopy.mkInductive _ (liftHomotopyZeroZero f comm) (by simp )
    (liftHomotopyZeroOne f comm) (by simp) fun n ⟨g, g', w⟩ =>
    ⟨liftHomotopyZeroSucc f n g g' w, by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
f : P.complex ⟶ Q.complex
comm : f ≫ Q.π = 0
n : ℕ
x✝ :
  (f_1 : HomologicalComplex.X P.complex n ⟶ HomologicalComplex.X Q.complex (n + 1)) ×'
    (f' : HomologicalComplex.X P.complex (n + 1) ⟶ HomologicalComplex.X Q.complex (n + 2)) ×'
      HomologicalComplex.Hom.f f (n + 1) =
        HomologicalComplex.d P.complex (n + 1) n ≫ f_1 + f' ≫ HomologicalComplex.d Q.complex (n + 2) (n + 1)
g : HomologicalComplex.X P.complex n ⟶ HomologicalComplex.X Q.complex (n + 1)
g' : HomologicalComplex.X P.complex (n + 1) ⟶ HomologicalComplex.X Q.complex (n + 2)
w :
  HomologicalComplex.Hom.f f (n + 1) =
    HomologicalComplex.d P.complex (n + 1) n ≫ g + g' ≫ HomologicalComplex.d Q.complex (n + 2) (n + 1)
⊢ HomologicalComplex.Hom.f f (n + 2) =
    HomologicalComplex.d P.complex (n + 2) (n + 1) ≫ { fst := g, snd := { fst := g', snd := w } }.snd.fst +
      liftHomotopyZeroSucc f n g g' w ≫ HomologicalComplex.d Q.complex (n + 3) (n + 2)

Next tactic:
simp
/-- Two lifts of the same morphism are homotopic. -/
def liftHomotopy {Y Z : C} (f : Y ⟶ Z) {P : ProjectiveResolution Y} {Q : ProjectiveResolution Z}
    (g h : P.complex ⟶ Q.complex) (g_comm : g ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f)
    (h_comm : h ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f) : Homotopy g h :=
  Homotopy.equivSubZero.invFun (liftHomotopyZero _ (by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
Y Z : C
f : Y ⟶ Z
P : ProjectiveResolution Y
Q : ProjectiveResolution Z
g h : P.complex ⟶ Q.complex
g_comm : g ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f
h_comm : h ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f
⊢ (g - h) ≫ Q.π = 0

Next tactic:
simp [g_comm, h_comm]
/-- The lift of the identity morphism is homotopic to the identity chain map. -/
def liftIdHomotopy (X : C) (P : ProjectiveResolution X) :
    Homotopy (lift (𝟙 X) P P) (𝟙 P.complex) := by
  

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X : C
P : ProjectiveResolution X
⊢ Homotopy (lift (𝟙 X) P P) (𝟙 P.complex)

Next tactic:
apply liftHomotopy (𝟙 X)
/-- The lift of the identity morphism is homotopic to the identity chain map. -/
def liftIdHomotopy (X : C) (P : ProjectiveResolution X) :
    Homotopy (lift (𝟙 X) P P) (𝟙 P.complex) := by
  apply liftHomotopy (𝟙 X) <;> 

Proof state:
case g_comm
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X : C
P : ProjectiveResolution X
⊢ lift (𝟙 X) P P ≫ P.π = P.π ≫ (ChainComplex.single₀ C).map (𝟙 X)

Next tactic:
simp
/-- The lift of the identity morphism is homotopic to the identity chain map. -/
def liftIdHomotopy (X : C) (P : ProjectiveResolution X) :
    Homotopy (lift (𝟙 X) P P) (𝟙 P.complex) := by
  apply liftHomotopy (𝟙 X) <;> 

Proof state:
case h_comm
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X : C
P : ProjectiveResolution X
⊢ 𝟙 P.complex ≫ P.π = P.π ≫ (ChainComplex.single₀ C).map (𝟙 X)

Next tactic:
simp
/-- The lift of a composition is homotopic to the composition of the lifts. -/
def liftCompHomotopy {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) (P : ProjectiveResolution X)
    (Q : ProjectiveResolution Y) (R : ProjectiveResolution Z) :
    Homotopy (lift (f ≫ g) P R) (lift f P Q ≫ lift g Q R) := by
  

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
P : ProjectiveResolution X
Q : ProjectiveResolution Y
R : ProjectiveResolution Z
⊢ Homotopy (lift (f ≫ g) P R) (lift f P Q ≫ lift g Q R)

Next tactic:
apply liftHomotopy (f ≫ g)
/-- The lift of a composition is homotopic to the composition of the lifts. -/
def liftCompHomotopy {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) (P : ProjectiveResolution X)
    (Q : ProjectiveResolution Y) (R : ProjectiveResolution Z) :
    Homotopy (lift (f ≫ g) P R) (lift f P Q ≫ lift g Q R) := by
  apply liftHomotopy (f ≫ g) <;> 

Proof state:
case g_comm
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
P : ProjectiveResolution X
Q : ProjectiveResolution Y
R : ProjectiveResolution Z
⊢ lift (f ≫ g) P R ≫ R.π = P.π ≫ (ChainComplex.single₀ C).map (f ≫ g)

Next tactic:
simp
/-- The lift of a composition is homotopic to the composition of the lifts. -/
def liftCompHomotopy {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) (P : ProjectiveResolution X)
    (Q : ProjectiveResolution Y) (R : ProjectiveResolution Z) :
    Homotopy (lift (f ≫ g) P R) (lift f P Q ≫ lift g Q R) := by
  apply liftHomotopy (f ≫ g) <;> 

Proof state:
case h_comm
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
P : ProjectiveResolution X
Q : ProjectiveResolution Y
R : ProjectiveResolution Z
⊢ (lift f P Q ≫ lift g Q R) ≫ R.π = P.π ≫ (ChainComplex.single₀ C).map (f ≫ g)

Next tactic:
simp
/-- Any two projective resolutions are homotopy equivalent. -/
def homotopyEquiv {X : C} (P Q : ProjectiveResolution X) :
    HomotopyEquiv P.complex Q.complex where
  hom := lift (𝟙 X) P Q
  inv := lift (𝟙 X) Q P
  homotopyHomInvId := (liftCompHomotopy (𝟙 X) (𝟙 X) P Q P).symm.trans <| by
    

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X : C
P Q : ProjectiveResolution X
⊢ Homotopy (lift (𝟙 X ≫ 𝟙 X) P P) (𝟙 P.complex)

Next tactic:
simpa [id_comp] using liftIdHomotopy _ _
/-- Any two projective resolutions are homotopy equivalent. -/
def homotopyEquiv {X : C} (P Q : ProjectiveResolution X) :
    HomotopyEquiv P.complex Q.complex where
  hom := lift (𝟙 X) P Q
  inv := lift (𝟙 X) Q P
  homotopyHomInvId := (liftCompHomotopy (𝟙 X) (𝟙 X) P Q P).symm.trans <| by
    simpa [id_comp] using liftIdHomotopy _ _
  homotopyInvHomId := (liftCompHomotopy (𝟙 X) (𝟙 X) Q P Q).symm.trans <| by
    

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X : C
P Q : ProjectiveResolution X
⊢ Homotopy (lift (𝟙 X ≫ 𝟙 X) Q Q) (𝟙 Q.complex)

Next tactic:
simpa [id_comp] using liftIdHomotopy _ _
@[reassoc (attr := simp)]
theorem homotopyEquiv_hom_π {X : C} (P Q : ProjectiveResolution X) :
    (homotopyEquiv P Q).hom ≫ Q.π = P.π := by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X : C
P Q : ProjectiveResolution X
⊢ (homotopyEquiv P Q).hom ≫ Q.π = P.π

Next tactic:
simp [homotopyEquiv]
@[reassoc (attr := simp)]
theorem homotopyEquiv_inv_π {X : C} (P Q : ProjectiveResolution X) :
    (homotopyEquiv P Q).inv ≫ P.π = Q.π := by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Abelian C
X : C
P Q : ProjectiveResolution X
⊢ (homotopyEquiv P Q).inv ≫ P.π = Q.π

Next tactic:
simp [homotopyEquiv]
/-- 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
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X : C
⊢ { 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
      (𝟙 X) =
    𝟙
      ({ 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)) }.obj
        X)

Next tactic:
rw [← (HomotopyCategory.quotient _ _).map_id]
/-- 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]
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X : C
⊢ { 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
      (𝟙 X) =
    (HomotopyCategory.quotient C (ComplexShape.down ℕ)).map (𝟙 (projectiveResolution X).complex)

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
    

Proof state:
case h
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Abelian C
inst✝ : HasProjectiveResolutions C
X : C
⊢ Homotopy (ProjectiveResolution.lift (𝟙 X) (projectiveResolution X) (projectiveResolution X))
    (𝟙 (projectiveResolution X).complex)

Next tactic:
apply ProjectiveResolution.liftIdHomotopy