l3lab/ntp-mathlib huggingface dataset:
/-- Exactness of `L₀.X₂ ⟶ L₀.X₃ ⟶ L₃.X₁`. -/
lemma L₁'_exact : S.L₁'.Exact := by
  rw [ShortComplex.exact_iff_exact_up_to_refinements]
  intro A₀ x₃ hx₃
  dsimp at x₃ hx₃
  obtain ⟨A₁, π₁, hπ₁, p, hp⟩ := surjective_up_to_refinements_of_epi S.L₀'.g x₃
  dsimp [L₀'] at p hp
  have hp' : (p ≫ S.φ₁) ≫ S.v₂₃.τ₁ = 0 := by
    rw [assoc, ← S.snd_δ, ← reassoc_of% hp, hx₃, comp_zero]
  obtain ⟨A₂, π₂, hπ₂, x₁, hx₁⟩ := S.exact_C₁_down.exact_up_to_refinements (p ≫ S.φ₁) hp'
  dsimp at x₁ hx₁
  let x₂' := x₁ ≫ S.L₁.f
  let x₂ := π₂ ≫ p ≫ pullback.fst
  have hx₂' : (x₂ - x₂') ≫ S.v₁₂.τ₂ = 0 := by
    simp only [sub_comp, assoc, ← S.v₁₂.comm₁₂, ← reassoc_of% hx₁, φ₂, φ₁_L₂_f, sub_self]
  let k₂ : A₂ ⟶ S.L₀.X₂ := S.exact_C₂_up.lift _ hx₂'
  have hk₂ : k₂ ≫ S.v₀₁.τ₂ = x₂ - x₂' := S.exact_C₂_up.lift_f _ _
  have hk₂' : k₂ ≫ S.L₀.g = π₂ ≫ p ≫ pullback.snd := by
    simp only [← cancel_mono S.v₀₁.τ₃, assoc, ← S.v₀₁.comm₂₃, reassoc_of% hk₂,
      sub_comp, S.L₁.zero, comp_zero, sub_zero, pullback.condition]
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.intro
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
A₀ : C
x₃ : A₀ ⟶ S.L₀.X₃
hx₃ : x₃ ≫ δ S = 0
A₁ : C
π₁ : A₁ ⟶ A₀
hπ₁ : Epi π₁
p : A₁ ⟶ P S
hp : π₁ ≫ x₃ = p ≫ pullback.snd
hp' : (p ≫ φ₁ S) ≫ S.v₂₃.τ₁ = 0
A₂ : C
π₂ : A₂ ⟶ A₁
hπ₂ : Epi π₂
x₁ : A₂ ⟶ S.L₁.X₁
hx₁ : π₂ ≫ p ≫ φ₁ S = x₁ ≫ S.v₁₂.τ₁
x₂' : A₂ ⟶ S.L₁.X₂ := x₁ ≫ S.L₁.f
x₂ : A₂ ⟶ S.L₁.X₂ := π₂ ≫ p ≫ pullback.fst
hx₂' : (x₂ - x₂') ≫ S.v₁₂.τ₂ = 0
k₂ : A₂ ⟶ S.L₀.X₂ :=
  Exact.lift (_ : Exact (ShortComplex.mk S.v₀₁.τ₂ S.v₁₂.τ₂ (_ : S.v₀₁.τ₂ ≫ S.v₁₂.τ₂ = 0))) (x₂ - x₂') hx₂'
hk₂ : k₂ ≫ S.v₀₁.τ₂ = x₂ - x₂'
hk₂' : k₂ ≫ S.L₀.g = π₂ ≫ p ≫ pullback.snd
⊢ ∃ A' π, ∃ (_ : Epi π), ∃ x₁, π ≫ x₃ = x₁ ≫ (L₁' S).f

Next tactic:
exact ⟨A₂, π₂ ≫ π₁, epi_comp _ _, k₂, by simp only [assoc, L₁'_f, ← hk₂', hp]⟩
/-- Exactness of `L₀.X₂ ⟶ L₀.X₃ ⟶ L₃.X₁`. -/
lemma L₁'_exact : S.L₁'.Exact := by
  rw [ShortComplex.exact_iff_exact_up_to_refinements]
  intro A₀ x₃ hx₃
  dsimp at x₃ hx₃
  obtain ⟨A₁, π₁, hπ₁, p, hp⟩ := surjective_up_to_refinements_of_epi S.L₀'.g x₃
  dsimp [L₀'] at p hp
  have hp' : (p ≫ S.φ₁) ≫ S.v₂₃.τ₁ = 0 := by
    rw [assoc, ← S.snd_δ, ← reassoc_of% hp, hx₃, comp_zero]
  obtain ⟨A₂, π₂, hπ₂, x₁, hx₁⟩ := S.exact_C₁_down.exact_up_to_refinements (p ≫ S.φ₁) hp'
  dsimp at x₁ hx₁
  let x₂' := x₁ ≫ S.L₁.f
  let x₂ := π₂ ≫ p ≫ pullback.fst
  have hx₂' : (x₂ - x₂') ≫ S.v₁₂.τ₂ = 0 := by
    simp only [sub_comp, assoc, ← S.v₁₂.comm₁₂, ← reassoc_of% hx₁, φ₂, φ₁_L₂_f, sub_self]
  let k₂ : A₂ ⟶ S.L₀.X₂ := S.exact_C₂_up.lift _ hx₂'
  have hk₂ : k₂ ≫ S.v₀₁.τ₂ = x₂ - x₂' := S.exact_C₂_up.lift_f _ _
  have hk₂' : k₂ ≫ S.L₀.g = π₂ ≫ p ≫ pullback.snd := by
    simp only [← cancel_mono S.v₀₁.τ₃, assoc, ← S.v₀₁.comm₂₃, reassoc_of% hk₂,
      sub_comp, S.L₁.zero, comp_zero, sub_zero, pullback.condition]
  exact ⟨A₂, π₂ ≫ π₁, epi_comp _ _, k₂, by 

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
A₀ : C
x₃ : A₀ ⟶ S.L₀.X₃
hx₃ : x₃ ≫ δ S = 0
A₁ : C
π₁ : A₁ ⟶ A₀
hπ₁ : Epi π₁
p : A₁ ⟶ P S
hp : π₁ ≫ x₃ = p ≫ pullback.snd
hp' : (p ≫ φ₁ S) ≫ S.v₂₃.τ₁ = 0
A₂ : C
π₂ : A₂ ⟶ A₁
hπ₂ : Epi π₂
x₁ : A₂ ⟶ S.L₁.X₁
hx₁ : π₂ ≫ p ≫ φ₁ S = x₁ ≫ S.v₁₂.τ₁
x₂' : A₂ ⟶ S.L₁.X₂ := x₁ ≫ S.L₁.f
x₂ : A₂ ⟶ S.L₁.X₂ := π₂ ≫ p ≫ pullback.fst
hx₂' : (x₂ - x₂') ≫ S.v₁₂.τ₂ = 0
k₂ : A₂ ⟶ S.L₀.X₂ :=
  Exact.lift (_ : Exact (ShortComplex.mk S.v₀₁.τ₂ S.v₁₂.τ₂ (_ : S.v₀₁.τ₂ ≫ S.v₁₂.τ₂ = 0))) (x₂ - x₂') hx₂'
hk₂ : k₂ ≫ S.v₀₁.τ₂ = x₂ - x₂'
hk₂' : k₂ ≫ S.L₀.g = π₂ ≫ p ≫ pullback.snd
⊢ (π₂ ≫ π₁) ≫ x₃ = k₂ ≫ (L₁' S).f

Next tactic:
simp only [assoc, L₁'_f, ← hk₂', hp]
lemma op_δ : S.op.δ = S.δ.op := Quiver.Hom.unop_inj (by
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ (δ (op S)).unop = (δ S).op.unop

Next tactic:
rw [Quiver.Hom.unop_op, ← cancel_mono (pushout.inr : _ ⟶ S.P'),
    ← cancel_epi (pullback.snd : S.P ⟶ _), S.snd_δ_inr,
    ← cancel_mono S.P'IsoUnopOpP.hom, ← cancel_epi S.PIsoUnopOpP'.inv,
    P'IsoUnopOpP, PIsoUnopOpP', assoc, assoc, assoc, assoc,
    pushoutIsoUnopPullback_inr_hom, pullbackIsoUnopPushout_inv_snd_assoc,
    pushoutIsoUnopPullback_inl_hom, pullbackIsoUnopPushout_inv_fst_assoc]
lemma op_δ : S.op.δ = S.δ.op := Quiver.Hom.unop_inj (by
  rw [Quiver.Hom.unop_op, ← cancel_mono (pushout.inr : _ ⟶ S.P'),
    ← cancel_epi (pullback.snd : S.P ⟶ _), S.snd_δ_inr,
    ← cancel_mono S.P'IsoUnopOpP.hom, ← cancel_epi S.PIsoUnopOpP'.inv,
    P'IsoUnopOpP, PIsoUnopOpP', assoc, assoc, assoc, assoc,
    pushoutIsoUnopPullback_inr_hom, pullbackIsoUnopPushout_inv_snd_assoc,
    pushoutIsoUnopPullback_inl_hom, pullbackIsoUnopPushout_inv_fst_assoc]
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ pushout.inr.unop ≫ (δ (op S)).unop ≫ pullback.snd.unop = pushout.inl.unop ≫ S.v₁₂.τ₂ ≫ pullback.fst.unop

Next tactic:
apply Quiver.Hom.op_inj
lemma op_δ : S.op.δ = S.δ.op := Quiver.Hom.unop_inj (by
  rw [Quiver.Hom.unop_op, ← cancel_mono (pushout.inr : _ ⟶ S.P'),
    ← cancel_epi (pullback.snd : S.P ⟶ _), S.snd_δ_inr,
    ← cancel_mono S.P'IsoUnopOpP.hom, ← cancel_epi S.PIsoUnopOpP'.inv,
    P'IsoUnopOpP, PIsoUnopOpP', assoc, assoc, assoc, assoc,
    pushoutIsoUnopPullback_inr_hom, pullbackIsoUnopPushout_inv_snd_assoc,
    pushoutIsoUnopPullback_inl_hom, pullbackIsoUnopPushout_inv_fst_assoc]
  apply Quiver.Hom.op_inj
  

Proof state:
case a
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ (pushout.inr.unop ≫ (δ (op S)).unop ≫ pullback.snd.unop).op = (pushout.inl.unop ≫ S.v₁₂.τ₂ ≫ pullback.fst.unop).op

Next tactic:
simpa only [op_comp, Quiver.Hom.op_unop, assoc] using S.op.snd_δ_inr
/-- The duality isomorphism `S.L₂'.op ≅ S.op.L₁'`. -/
noncomputable def L₂'OpIso : S.L₂'.op ≅ S.op.L₁' :=
  ShortComplex.isoMk (Iso.refl _) (Iso.refl _) (Iso.refl _) (by 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.135176, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ (Iso.refl (ShortComplex.op (L₂' S)).X₁).hom ≫ (L₁' (op S)).f =
    (ShortComplex.op (L₂' S)).f ≫ (Iso.refl (ShortComplex.op (L₂' S)).X₂).hom

Next tactic:
aesop_cat
/-- The duality isomorphism `S.L₂'.op ≅ S.op.L₁'`. -/
noncomputable def L₂'OpIso : S.L₂'.op ≅ S.op.L₁' :=
  ShortComplex.isoMk (Iso.refl _) (Iso.refl _) (Iso.refl _) (by aesop_cat)
    (by 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.135176, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ (Iso.refl (ShortComplex.op (L₂' S)).X₂).hom ≫ (L₁' (op S)).g =
    (ShortComplex.op (L₂' S)).g ≫ (Iso.refl (ShortComplex.op (L₂' S)).X₃).hom

Next tactic:
dsimp
/-- The duality isomorphism `S.L₂'.op ≅ S.op.L₁'`. -/
noncomputable def L₂'OpIso : S.L₂'.op ≅ S.op.L₁' :=
  ShortComplex.isoMk (Iso.refl _) (Iso.refl _) (Iso.refl _) (by aesop_cat)
    (by dsimp; 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.135176, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ 𝟙 (Opposite.op S.L₃.X₁) ≫ δ (op S) = (δ S).op ≫ 𝟙 (Opposite.op S.L₀.X₃)

Next tactic:
simp only [id_comp, comp_id, S.op_δ]
/-- Exactness of `L₀.X₃ ⟶ L₃.X₁ ⟶ L₃.X₂`. -/
lemma L₂'_exact : S.L₂'.Exact := by
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ Exact (L₂' S)

Next tactic:
rw [← exact_op_iff, exact_iff_of_iso S.L₂'OpIso]
/-- Exactness of `L₀.X₃ ⟶ L₃.X₁ ⟶ L₃.X₂`. -/
lemma L₂'_exact : S.L₂'.Exact := by
  rw [← exact_op_iff, exact_iff_of_iso S.L₂'OpIso]
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
⊢ Exact (L₁' (op S))

Next tactic:
exact S.op.L₁'_exact
lemma δ_eq {A : C} (x₃ : A ⟶ S.L₀.X₃) (x₂ : A ⟶ S.L₁.X₂) (x₁ : A ⟶ S.L₂.X₁)
    (h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃) (h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂) :
    x₃ ≫ S.δ = x₁ ≫ S.v₂₃.τ₁ := by
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
A : C
x₃ : A ⟶ S.L₀.X₃
x₂ : A ⟶ S.L₁.X₂
x₁ : A ⟶ S.L₂.X₁
h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃
h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂
⊢ x₃ ≫ δ S = x₁ ≫ S.v₂₃.τ₁

Next tactic:
have H := (pullback.lift x₂ x₃ h₂) ≫= S.snd_δ
lemma δ_eq {A : C} (x₃ : A ⟶ S.L₀.X₃) (x₂ : A ⟶ S.L₁.X₂) (x₁ : A ⟶ S.L₂.X₁)
    (h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃) (h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂) :
    x₃ ≫ S.δ = x₁ ≫ S.v₂₃.τ₁ := by
  have H := (pullback.lift x₂ x₃ h₂) ≫= S.snd_δ
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
A : C
x₃ : A ⟶ S.L₀.X₃
x₂ : A ⟶ S.L₁.X₂
x₁ : A ⟶ S.L₂.X₁
h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃
h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂
H : pullback.lift x₂ x₃ h₂ ≫ pullback.snd ≫ δ S = pullback.lift x₂ x₃ h₂ ≫ φ₁ S ≫ S.v₂₃.τ₁
⊢ x₃ ≫ δ S = x₁ ≫ S.v₂₃.τ₁

Next tactic:
rw [pullback.lift_snd_assoc] at H
lemma δ_eq {A : C} (x₃ : A ⟶ S.L₀.X₃) (x₂ : A ⟶ S.L₁.X₂) (x₁ : A ⟶ S.L₂.X₁)
    (h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃) (h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂) :
    x₃ ≫ S.δ = x₁ ≫ S.v₂₃.τ₁ := by
  have H := (pullback.lift x₂ x₃ h₂) ≫= S.snd_δ
  rw [pullback.lift_snd_assoc] at H
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
A : C
x₃ : A ⟶ S.L₀.X₃
x₂ : A ⟶ S.L₁.X₂
x₁ : A ⟶ S.L₂.X₁
h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃
h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂
H : x₃ ≫ δ S = pullback.lift x₂ x₃ h₂ ≫ φ₁ S ≫ S.v₂₃.τ₁
⊢ x₃ ≫ δ S = x₁ ≫ S.v₂₃.τ₁

Next tactic:
rw [H, ← assoc]
lemma δ_eq {A : C} (x₃ : A ⟶ S.L₀.X₃) (x₂ : A ⟶ S.L₁.X₂) (x₁ : A ⟶ S.L₂.X₁)
    (h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃) (h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂) :
    x₃ ≫ S.δ = x₁ ≫ S.v₂₃.τ₁ := by
  have H := (pullback.lift x₂ x₃ h₂) ≫= S.snd_δ
  rw [pullback.lift_snd_assoc] at H
  rw [H, ← assoc]
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
A : C
x₃ : A ⟶ S.L₀.X₃
x₂ : A ⟶ S.L₁.X₂
x₁ : A ⟶ S.L₂.X₁
h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃
h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂
H : x₃ ≫ δ S = pullback.lift x₂ x₃ h₂ ≫ φ₁ S ≫ S.v₂₃.τ₁
⊢ (pullback.lift x₂ x₃ h₂ ≫ φ₁ S) ≫ S.v₂₃.τ₁ = x₁ ≫ S.v₂₃.τ₁

Next tactic:
congr 1
lemma δ_eq {A : C} (x₃ : A ⟶ S.L₀.X₃) (x₂ : A ⟶ S.L₁.X₂) (x₁ : A ⟶ S.L₂.X₁)
    (h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃) (h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂) :
    x₃ ≫ S.δ = x₁ ≫ S.v₂₃.τ₁ := by
  have H := (pullback.lift x₂ x₃ h₂) ≫= S.snd_δ
  rw [pullback.lift_snd_assoc] at H
  rw [H, ← assoc]
  congr 1
  

Proof state:
case e_a
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S : SnakeInput C
A : C
x₃ : A ⟶ S.L₀.X₃
x₂ : A ⟶ S.L₁.X₂
x₁ : A ⟶ S.L₂.X₁
h₂ : x₂ ≫ S.L₁.g = x₃ ≫ S.v₀₁.τ₃
h₁ : x₁ ≫ S.L₂.f = x₂ ≫ S.v₁₂.τ₂
H : x₃ ≫ δ S = pullback.lift x₂ x₃ h₂ ≫ φ₁ S ≫ S.v₂₃.τ₁
⊢ pullback.lift x₂ x₃ h₂ ≫ φ₁ S = x₁

Next tactic:
simp only [← cancel_mono S.L₂.f, assoc, φ₁_L₂_f, lift_φ₂, h₁]
/-- The composition of morphisms of snake inputs. -/
@[simps]
def comp (f : Hom S₁ S₂) (g : Hom S₂ S₃) : Hom S₁ S₃ where
  f₀ := f.f₀ ≫ g.f₀
  f₁ := f.f₁ ≫ g.f₁
  f₂ := f.f₂ ≫ g.f₂
  f₃ := f.f₃ ≫ g.f₃
  comm₀₁ := by 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.151287, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ : SnakeInput C
f : Hom S₁ S₂
g : Hom S₂ S₃
⊢ (f.f₀ ≫ g.f₀) ≫ S₃.v₀₁ = S₁.v₀₁ ≫ f.f₁ ≫ g.f₁

Next tactic:
simp only [assoc, comm₀₁, comm₀₁_assoc]
/-- The composition of morphisms of snake inputs. -/
@[simps]
def comp (f : Hom S₁ S₂) (g : Hom S₂ S₃) : Hom S₁ S₃ where
  f₀ := f.f₀ ≫ g.f₀
  f₁ := f.f₁ ≫ g.f₁
  f₂ := f.f₂ ≫ g.f₂
  f₃ := f.f₃ ≫ g.f₃
  comm₀₁ := by simp only [assoc, comm₀₁, comm₀₁_assoc]
  comm₁₂ := by 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.151287, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ : SnakeInput C
f : Hom S₁ S₂
g : Hom S₂ S₃
⊢ (f.f₁ ≫ g.f₁) ≫ S₃.v₁₂ = S₁.v₁₂ ≫ f.f₂ ≫ g.f₂

Next tactic:
simp only [assoc, comm₁₂, comm₁₂_assoc]
/-- The composition of morphisms of snake inputs. -/
@[simps]
def comp (f : Hom S₁ S₂) (g : Hom S₂ S₃) : Hom S₁ S₃ where
  f₀ := f.f₀ ≫ g.f₀
  f₁ := f.f₁ ≫ g.f₁
  f₂ := f.f₂ ≫ g.f₂
  f₃ := f.f₃ ≫ g.f₃
  comm₀₁ := by simp only [assoc, comm₀₁, comm₀₁_assoc]
  comm₁₂ := by simp only [assoc, comm₁₂, comm₁₂_assoc]
  comm₂₃ := by 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.151287, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ : SnakeInput C
f : Hom S₁ S₂
g : Hom S₂ S₃
⊢ (f.f₂ ≫ g.f₂) ≫ S₃.v₂₃ = S₁.v₂₃ ≫ f.f₃ ≫ g.f₃

Next tactic:
simp only [assoc, comm₂₃, comm₂₃_assoc]
/-- The functor which sends `S : SnakeInput C` to the auxiliary object `S.P`,
which is `pullback S.L₁.g S.v₀₁.τ₃`. -/
@[simps]
noncomputable def functorP : SnakeInput C ⥤ C where
  obj S := S.P
  map f := pullback.map _ _ _ _ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃ f.f₁.comm₂₃.symm
      (congr_arg ShortComplex.Hom.τ₃ f.comm₀₁.symm)
  map_id _ := by 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.173744, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ x✝ : SnakeInput C
⊢ { obj := fun S => P S,
          map := fun {X Y} f =>
            pullback.map X.L₁.g X.v₀₁.τ₃ Y.L₁.g Y.v₀₁.τ₃ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃
              (_ : X.L₁.g ≫ f.f₁.τ₃ = f.f₁.τ₂ ≫ Y.L₁.g) (_ : (X.v₀₁ ≫ f.f₁).τ₃ = (f.f₀ ≫ Y.v₀₁).τ₃) }.map
      (𝟙 x✝) =
    𝟙
      ({ obj := fun S => P S,
            map := fun {X Y} f =>
              pullback.map X.L₁.g X.v₀₁.τ₃ Y.L₁.g Y.v₀₁.τ₃ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃
                (_ : X.L₁.g ≫ f.f₁.τ₃ = f.f₁.τ₂ ≫ Y.L₁.g) (_ : (X.v₀₁ ≫ f.f₁).τ₃ = (f.f₀ ≫ Y.v₀₁).τ₃) }.obj
        x✝)

Next tactic:
dsimp [P]
/-- The functor which sends `S : SnakeInput C` to the auxiliary object `S.P`,
which is `pullback S.L₁.g S.v₀₁.τ₃`. -/
@[simps]
noncomputable def functorP : SnakeInput C ⥤ C where
  obj S := S.P
  map f := pullback.map _ _ _ _ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃ f.f₁.comm₂₃.symm
      (congr_arg ShortComplex.Hom.τ₃ f.comm₀₁.symm)
  map_id _ := by dsimp [P]; 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.173744, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ x✝ : SnakeInput C
⊢ pullback.map x✝.L₁.g x✝.v₀₁.τ₃ x✝.L₁.g x✝.v₀₁.τ₃ (𝟙 x✝.L₁.X₂) (𝟙 x✝.L₀.X₃) (𝟙 x✝.L₁.X₃)
      (_ : x✝.L₁.g ≫ 𝟙 x✝.L₁.X₃ = 𝟙 x✝.L₁.X₂ ≫ x✝.L₁.g) (_ : (x✝.v₀₁ ≫ 𝟙 x✝.L₁).τ₃ = (𝟙 x✝.L₀ ≫ x✝.v₀₁).τ₃) =
    𝟙 (pullback x✝.L₁.g x✝.v₀₁.τ₃)

Next tactic:
aesop_cat
/-- The functor which sends `S : SnakeInput C` to the auxiliary object `S.P`,
which is `pullback S.L₁.g S.v₀₁.τ₃`. -/
@[simps]
noncomputable def functorP : SnakeInput C ⥤ C where
  obj S := S.P
  map f := pullback.map _ _ _ _ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃ f.f₁.comm₂₃.symm
      (congr_arg ShortComplex.Hom.τ₃ f.comm₀₁.symm)
  map_id _ := by dsimp [P]; aesop_cat
  map_comp _ _ := by 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.173744, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ X✝ Y✝ Z✝ : SnakeInput C
x✝¹ : X✝ ⟶ Y✝
x✝ : Y✝ ⟶ Z✝
⊢ { obj := fun S => P S,
          map := fun {X Y} f =>
            pullback.map X.L₁.g X.v₀₁.τ₃ Y.L₁.g Y.v₀₁.τ₃ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃
              (_ : X.L₁.g ≫ f.f₁.τ₃ = f.f₁.τ₂ ≫ Y.L₁.g) (_ : (X.v₀₁ ≫ f.f₁).τ₃ = (f.f₀ ≫ Y.v₀₁).τ₃) }.map
      (x✝¹ ≫ x✝) =
    { obj := fun S => P S,
            map := fun {X Y} f =>
              pullback.map X.L₁.g X.v₀₁.τ₃ Y.L₁.g Y.v₀₁.τ₃ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃
                (_ : X.L₁.g ≫ f.f₁.τ₃ = f.f₁.τ₂ ≫ Y.L₁.g) (_ : (X.v₀₁ ≫ f.f₁).τ₃ = (f.f₀ ≫ Y.v₀₁).τ₃) }.map
        x✝¹ ≫
      { obj := fun S => P S,
            map := fun {X Y} f =>
              pullback.map X.L₁.g X.v₀₁.τ₃ Y.L₁.g Y.v₀₁.τ₃ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃
                (_ : X.L₁.g ≫ f.f₁.τ₃ = f.f₁.τ₂ ≫ Y.L₁.g) (_ : (X.v₀₁ ≫ f.f₁).τ₃ = (f.f₀ ≫ Y.v₀₁).τ₃) }.map
        x✝

Next tactic:
dsimp [P]
/-- The functor which sends `S : SnakeInput C` to the auxiliary object `S.P`,
which is `pullback S.L₁.g S.v₀₁.τ₃`. -/
@[simps]
noncomputable def functorP : SnakeInput C ⥤ C where
  obj S := S.P
  map f := pullback.map _ _ _ _ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃ f.f₁.comm₂₃.symm
      (congr_arg ShortComplex.Hom.τ₃ f.comm₀₁.symm)
  map_id _ := by dsimp [P]; aesop_cat
  map_comp _ _ := by dsimp [P]; 

Proof state:
C : Type u_1
inst✝¹ : Category.{?u.173744, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ X✝ Y✝ Z✝ : SnakeInput C
x✝¹ : X✝ ⟶ Y✝
x✝ : Y✝ ⟶ Z✝
⊢ pullback.map X✝.L₁.g X✝.v₀₁.τ₃ Z✝.L₁.g Z✝.v₀₁.τ₃ (x✝¹.f₁.τ₂ ≫ x✝.f₁.τ₂) (x✝¹.f₀.τ₃ ≫ x✝.f₀.τ₃) (x✝¹.f₁.τ₃ ≫ x✝.f₁.τ₃)
      (_ : X✝.L₁.g ≫ x✝¹.f₁.τ₃ ≫ x✝.f₁.τ₃ = (x✝¹.f₁.τ₂ ≫ x✝.f₁.τ₂) ≫ Z✝.L₁.g)
      (_ : (X✝.v₀₁ ≫ x✝¹.f₁ ≫ x✝.f₁).τ₃ = ((x✝¹.f₀ ≫ x✝.f₀) ≫ Z✝.v₀₁).τ₃) =
    pullback.map X✝.L₁.g X✝.v₀₁.τ₃ Y✝.L₁.g Y✝.v₀₁.τ₃ x✝¹.f₁.τ₂ x✝¹.f₀.τ₃ x✝¹.f₁.τ₃
        (_ : X✝.L₁.g ≫ x✝¹.f₁.τ₃ = x✝¹.f₁.τ₂ ≫ Y✝.L₁.g) (_ : (X✝.v₀₁ ≫ x✝¹.f₁).τ₃ = (x✝¹.f₀ ≫ Y✝.v₀₁).τ₃) ≫
      pullback.map Y✝.L₁.g Y✝.v₀₁.τ₃ Z✝.L₁.g Z✝.v₀₁.τ₃ x✝.f₁.τ₂ x✝.f₀.τ₃ x✝.f₁.τ₃
        (_ : Y✝.L₁.g ≫ x✝.f₁.τ₃ = x✝.f₁.τ₂ ≫ Z✝.L₁.g) (_ : (Y✝.v₀₁ ≫ x✝.f₁).τ₃ = (x✝.f₀ ≫ Z✝.v₀₁).τ₃)

Next tactic:
aesop_cat
@[reassoc]
lemma naturality_φ₂ (f : S₁ ⟶ S₂) : S₁.φ₂ ≫ f.f₂.τ₂ = functorP.map f ≫ S₂.φ₂ := by
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ : SnakeInput C
f : S₁ ⟶ S₂
⊢ φ₂ S₁ ≫ f.f₂.τ₂ = functorP.map f ≫ φ₂ S₂

Next tactic:
dsimp [φ₂]
@[reassoc]
lemma naturality_φ₂ (f : S₁ ⟶ S₂) : S₁.φ₂ ≫ f.f₂.τ₂ = functorP.map f ≫ S₂.φ₂ := by
  dsimp [φ₂]
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ : SnakeInput C
f : S₁ ⟶ S₂
⊢ (pullback.fst ≫ S₁.v₁₂.τ₂) ≫ f.f₂.τ₂ =
    pullback.map S₁.L₁.g S₁.v₀₁.τ₃ S₂.L₁.g S₂.v₀₁.τ₃ f.f₁.τ₂ f.f₀.τ₃ f.f₁.τ₃ (_ : S₁.L₁.g ≫ f.f₁.τ₃ = f.f₁.τ₂ ≫ S₂.L₁.g)
        (_ : (S₁.v₀₁ ≫ f.f₁).τ₃ = (f.f₀ ≫ S₂.v₀₁).τ₃) ≫
      pullback.fst ≫ S₂.v₁₂.τ₂

Next tactic:
simp only [assoc, pullback.lift_fst_assoc, ← comp_τ₂, f.comm₁₂]
@[reassoc]
lemma naturality_φ₁ (f : S₁ ⟶ S₂) : S₁.φ₁ ≫ f.f₂.τ₁ = functorP.map f ≫ S₂.φ₁ := by
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ : SnakeInput C
f : S₁ ⟶ S₂
⊢ φ₁ S₁ ≫ f.f₂.τ₁ = functorP.map f ≫ φ₁ S₂

Next tactic:
simp only [← cancel_mono S₂.L₂.f, assoc, φ₁_L₂_f, ← naturality_φ₂, f.f₂.comm₁₂, φ₁_L₂_f_assoc]
@[reassoc]
lemma naturality_δ (f : S₁ ⟶ S₂) : S₁.δ ≫ f.f₃.τ₁ = f.f₀.τ₃ ≫ S₂.δ := by
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Abelian C
S S₁ S₂ S₃ : SnakeInput C
f : S₁ ⟶ S₂
⊢ δ S₁ ≫ f.f₃.τ₁ = f.f₀.τ₃ ≫ δ S₂

Next tactic:
rw [← cancel_epi (pullback.snd : S₁.P ⟶ _), S₁.snd_δ_assoc, ← comp_τ₁, ← f.comm₂₃,
    comp_τ₁, naturality_φ₁_assoc, ← S₂.snd_δ, functorP_map, pullback.lift_snd_assoc, assoc]
theorem Pi.cons_injective {a : α} {b : δ a} {s : Finset α} (hs : a ∉ s) :
    Function.Injective (Pi.cons s a b) := fun e₁ e₂ eq =>
  @Multiset.Pi.cons_injective α _ δ a b s.1 hs _ _ <|
    funext fun e =>
      funext fun h =>
        have :
          Pi.cons s a b e₁ e (by 

Proof state:
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝ : DecidableEq α
a : α
b : δ a
s : Finset α
hs : a ∉ s
e₁ e₂ : (a : α) → a ∈ s → δ a
eq : cons s a b e₁ = cons s a b e₂
e : α
h : e ∈ a ::ₘ s.val
⊢ e ∈ insert a s

Next tactic:
simpa only [Multiset.mem_cons, mem_insert] using h
theorem Pi.cons_injective {a : α} {b : δ a} {s : Finset α} (hs : a ∉ s) :
    Function.Injective (Pi.cons s a b) := fun e₁ e₂ eq =>
  @Multiset.Pi.cons_injective α _ δ a b s.1 hs _ _ <|
    funext fun e =>
      funext fun h =>
        have :
          Pi.cons s a b e₁ e (by simpa only [Multiset.mem_cons, mem_insert] using h) =
            Pi.cons s a b e₂ e (by 

Proof state:
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝ : DecidableEq α
a : α
b : δ a
s : Finset α
hs : a ∉ s
e₁ e₂ : (a : α) → a ∈ s → δ a
eq : cons s a b e₁ = cons s a b e₂
e : α
h : e ∈ a ::ₘ s.val
⊢ e ∈ insert a s

Next tactic:
simpa only [Multiset.mem_cons, mem_insert] using h
theorem Pi.cons_injective {a : α} {b : δ a} {s : Finset α} (hs : a ∉ s) :
    Function.Injective (Pi.cons s a b) := fun e₁ e₂ eq =>
  @Multiset.Pi.cons_injective α _ δ a b s.1 hs _ _ <|
    funext fun e =>
      funext fun h =>
        have :
          Pi.cons s a b e₁ e (by simpa only [Multiset.mem_cons, mem_insert] using h) =
            Pi.cons s a b e₂ e (by simpa only [Multiset.mem_cons, mem_insert] using h) :=
          by 

Proof state:
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝ : DecidableEq α
a : α
b : δ a
s : Finset α
hs : a ∉ s
e₁ e₂ : (a : α) → a ∈ s → δ a
eq : cons s a b e₁ = cons s a b e₂
e : α
h : e ∈ a ::ₘ s.val
⊢ cons s a b e₁ e (_ : e ∈ insert a s) = cons s a b e₂ e (_ : e ∈ insert a s)

Next tactic:
rw [eq]
@[simp]
theorem pi_insert [∀ a, DecidableEq (β a)] {s : Finset α} {t : ∀ a : α, Finset (β a)} {a : α}
    (ha : a ∉ s) : pi (insert a s) t = (t a).biUnion fun b => (pi s t).image (Pi.cons s a b) := by
  

Proof state:
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝¹ : DecidableEq α
inst✝ : (a : α) → DecidableEq (β a)
s : Finset α
t : (a : α) → Finset (β a)
a : α
ha : a ∉ s
⊢ pi (insert a s) t = Finset.biUnion (t a) fun b => image (Pi.cons s a b) (pi s t)

Next tactic:
apply eq_of_veq
@[simp]
theorem pi_insert [∀ a, DecidableEq (β a)] {s : Finset α} {t : ∀ a : α, Finset (β a)} {a : α}
    (ha : a ∉ s) : pi (insert a s) t = (t a).biUnion fun b => (pi s t).image (Pi.cons s a b) := by
  apply eq_of_veq
  

Proof state:
case a
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝¹ : DecidableEq α
inst✝ : (a : α) → DecidableEq (β a)
s : Finset α
t : (a : α) → Finset (β a)
a : α
ha : a ∉ s
⊢ (pi (insert a s) t).val = (Finset.biUnion (t a) fun b => image (Pi.cons s a b) (pi s t)).val

Next tactic:
rw [← (pi (insert a s) t).2.dedup]
@[simp]
theorem pi_insert [∀ a, DecidableEq (β a)] {s : Finset α} {t : ∀ a : α, Finset (β a)} {a : α}
    (ha : a ∉ s) : pi (insert a s) t = (t a).biUnion fun b => (pi s t).image (Pi.cons s a b) := by
  apply eq_of_veq
  rw [← (pi (insert a s) t).2.dedup]
  

Proof state:
case a
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝¹ : DecidableEq α
inst✝ : (a : α) → DecidableEq (β a)
s : Finset α
t : (a : α) → Finset (β a)
a : α
ha : a ∉ s
⊢ dedup (pi (insert a s) t).val = (Finset.biUnion (t a) fun b => image (Pi.cons s a b) (pi s t)).val

Next tactic:
refine'
    (fun s' (h : s' = a ::ₘ s.1) =>
        (_ :
          dedup (Multiset.pi s' fun a => (t a).1) =
            dedup
              ((t a).1.bind fun b =>
                dedup <|
                  (Multiset.pi s.1 fun a : α => (t a).val).map fun f a' h' =>
                    Multiset.Pi.cons s.1 a b f a' (h ▸ h'))))
      _ (insert_val_of_not_mem ha)
@[simp]
theorem pi_insert [∀ a, DecidableEq (β a)] {s : Finset α} {t : ∀ a : α, Finset (β a)} {a : α}
    (ha : a ∉ s) : pi (insert a s) t = (t a).biUnion fun b => (pi s t).image (Pi.cons s a b) := by
  apply eq_of_veq
  rw [← (pi (insert a s) t).2.dedup]
  refine'
    (fun s' (h : s' = a ::ₘ s.1) =>
        (_ :
          dedup (Multiset.pi s' fun a => (t a).1) =
            dedup
              ((t a).1.bind fun b =>
                dedup <|
                  (Multiset.pi s.1 fun a : α => (t a).val).map fun f a' h' =>
                    Multiset.Pi.cons s.1 a b f a' (h ▸ h'))))
      _ (insert_val_of_not_mem ha)
  

Proof state:
case a
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝¹ : DecidableEq α
inst✝ : (a : α) → DecidableEq (β a)
s : Finset α
t : (a : α) → Finset (β a)
a : α
ha : a ∉ s
s' : Multiset α
h : s' = a ::ₘ s.val
⊢ dedup (Multiset.pi s' fun a => (t a).val) =
    dedup
      (Multiset.bind (t a).val fun b =>
        dedup
          (Multiset.map (fun f a' h' => Multiset.Pi.cons s.val a b f a' (_ : a' ∈ a ::ₘ s.val))
            (Multiset.pi s.val fun a => (t a).val)))

Next tactic:
subst s'
@[simp]
theorem pi_insert [∀ a, DecidableEq (β a)] {s : Finset α} {t : ∀ a : α, Finset (β a)} {a : α}
    (ha : a ∉ s) : pi (insert a s) t = (t a).biUnion fun b => (pi s t).image (Pi.cons s a b) := by
  apply eq_of_veq
  rw [← (pi (insert a s) t).2.dedup]
  refine'
    (fun s' (h : s' = a ::ₘ s.1) =>
        (_ :
          dedup (Multiset.pi s' fun a => (t a).1) =
            dedup
              ((t a).1.bind fun b =>
                dedup <|
                  (Multiset.pi s.1 fun a : α => (t a).val).map fun f a' h' =>
                    Multiset.Pi.cons s.1 a b f a' (h ▸ h'))))
      _ (insert_val_of_not_mem ha)
  subst s'; 

Proof state:
case a
α : Type u_1
β : α → Type u
δ : α → Sort v
inst✝¹ : DecidableEq α
inst✝ : (a : α) → DecidableEq (β a)
s : Finset α
t : (a : α) → Finset (β a)
a : α
ha : a ∉ s
⊢ dedup (Multiset.pi (a ::ₘ s.val) fun a => (t a).val) =
    dedup
      (Multiset.bind (t a).val fun b =>
        dedup
          (Multiset.map (fun f a' h' => Multiset.Pi.cons s.val a b f a' (_ : a' ∈ a ::ₘ s.val))
            (Multiset.pi s.val fun a => (t a).val)))

Next tactic:
rw [pi_cons]