l3lab/ntp-mathlib huggingface dataset:
/-- If `(f, g)` is exact, then `Abelian.image.ι f` is a kernel of `g`. -/
def isLimitImage (h : Exact f g) :
    IsLimit (KernelFork.ofι (Abelian.image.ι f) (image_ι_comp_eq_zero h.1) : KernelFork g) := by
  rw [exact_iff] at h
  exact KernelFork.IsLimit.ofι _ _
    (fun u hu ↦ kernel.lift (cokernel.π f) u
      (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
W'✝ : C
u : W'✝ ⟶ Y
hu : u ≫ g = 0
⊢ u ≫ cokernel.π f = 0

Next tactic:
rw [← kernel.lift_ι g u hu, Category.assoc, h.2, comp_zero]
/-- If `(f, g)` is exact, then `Abelian.image.ι f` is a kernel of `g`. -/
def isLimitImage (h : Exact f g) :
    IsLimit (KernelFork.ofι (Abelian.image.ι f) (image_ι_comp_eq_zero h.1) : KernelFork g) := by
  rw [exact_iff] at h
  exact KernelFork.IsLimit.ofι _ _
    (fun u hu ↦ kernel.lift (cokernel.π f) u
      (by rw [← kernel.lift_ι g u hu, Category.assoc, h.2, comp_zero])) (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
⊢ ∀ {W' : C} (g' : W' ⟶ Y) (eq' : g' ≫ g = 0),
    (fun {W'} u hu => kernel.lift (cokernel.π f) u (_ : u ≫ cokernel.π f = 0)) g' eq' ≫ image.ι f = g'

Next tactic:
aesop_cat
/-- If `(f, g)` is exact, then `Abelian.image.ι f` is a kernel of `g`. -/
def isLimitImage (h : Exact f g) :
    IsLimit (KernelFork.ofι (Abelian.image.ι f) (image_ι_comp_eq_zero h.1) : KernelFork g) := by
  rw [exact_iff] at h
  exact KernelFork.IsLimit.ofι _ _
    (fun u hu ↦ kernel.lift (cokernel.π f) u
      (by rw [← kernel.lift_ι g u hu, Category.assoc, h.2, comp_zero])) (by aesop_cat)
    (fun _ _ _ hm => by
      

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
W'✝ : C
x✝² : W'✝ ⟶ Y
x✝¹ : x✝² ≫ g = 0
x✝ : W'✝ ⟶ Abelian.image f
hm : x✝ ≫ image.ι f = x✝²
⊢ x✝ = (fun {W'} u hu => kernel.lift (cokernel.π f) u (_ : u ≫ cokernel.π f = 0)) x✝² x✝¹

Next tactic:
rw [← cancel_mono (image.ι f), hm, kernel.lift_ι]
/-- If `(f, g)` is exact, then `Abelian.coimage.π g` is a cokernel of `f`. -/
def isColimitCoimage (h : Exact f g) :
    IsColimit
      (CokernelCofork.ofπ (Abelian.coimage.π g) (Abelian.comp_coimage_π_eq_zero h.1) :
        CokernelCofork f) := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h : Exact f g
⊢ IsColimit (CokernelCofork.ofπ (coimage.π g) (_ : f ≫ coimage.π g = 0))

Next tactic:
rw [exact_iff] at h
/-- If `(f, g)` is exact, then `Abelian.coimage.π g` is a cokernel of `f`. -/
def isColimitCoimage (h : Exact f g) :
    IsColimit
      (CokernelCofork.ofπ (Abelian.coimage.π g) (Abelian.comp_coimage_π_eq_zero h.1) :
        CokernelCofork f) := by
  rw [exact_iff] at h
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
⊢ IsColimit (CokernelCofork.ofπ (coimage.π g) (_ : f ≫ coimage.π g = 0))

Next tactic:
refine' CokernelCofork.IsColimit.ofπ _ _
    (fun u hu => cokernel.desc (kernel.ι g) u
      (by rw [← cokernel.π_desc f u hu, ← Category.assoc, h.2, zero_comp]))
    (by aesop_cat) _
/-- If `(f, g)` is exact, then `Abelian.coimage.π g` is a cokernel of `f`. -/
def isColimitCoimage (h : Exact f g) :
    IsColimit
      (CokernelCofork.ofπ (Abelian.coimage.π g) (Abelian.comp_coimage_π_eq_zero h.1) :
        CokernelCofork f) := by
  rw [exact_iff] at h
  refine' CokernelCofork.IsColimit.ofπ _ _
    (fun u hu => cokernel.desc (kernel.ι g) u
      (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
Z'✝ : C
u : Y ⟶ Z'✝
hu : f ≫ u = 0
⊢ kernel.ι g ≫ u = 0

Next tactic:
rw [← cokernel.π_desc f u hu, ← Category.assoc, h.2, zero_comp]
/-- If `(f, g)` is exact, then `Abelian.coimage.π g` is a cokernel of `f`. -/
def isColimitCoimage (h : Exact f g) :
    IsColimit
      (CokernelCofork.ofπ (Abelian.coimage.π g) (Abelian.comp_coimage_π_eq_zero h.1) :
        CokernelCofork f) := by
  rw [exact_iff] at h
  refine' CokernelCofork.IsColimit.ofπ _ _
    (fun u hu => cokernel.desc (kernel.ι g) u
      (by rw [← cokernel.π_desc f u hu, ← Category.assoc, h.2, zero_comp]))
    (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
⊢ ∀ {Z' : C} (g' : Y ⟶ Z') (eq' : f ≫ g' = 0),
    coimage.π g ≫ (fun {Z'} u hu => cokernel.desc (kernel.ι g) u (_ : kernel.ι g ≫ u = 0)) g' eq' = g'

Next tactic:
aesop_cat
/-- If `(f, g)` is exact, then `Abelian.coimage.π g` is a cokernel of `f`. -/
def isColimitCoimage (h : Exact f g) :
    IsColimit
      (CokernelCofork.ofπ (Abelian.coimage.π g) (Abelian.comp_coimage_π_eq_zero h.1) :
        CokernelCofork f) := by
  rw [exact_iff] at h
  refine' CokernelCofork.IsColimit.ofπ _ _
    (fun u hu => cokernel.desc (kernel.ι g) u
      (by rw [← cokernel.π_desc f u hu, ← Category.assoc, h.2, zero_comp]))
    (by aesop_cat) _
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
⊢ ∀ {Z' : C} (g' : Y ⟶ Z') (eq' : f ≫ g' = 0) (m : Abelian.coimage g ⟶ Z'),
    coimage.π g ≫ m = g' → m = (fun {Z'} u hu => cokernel.desc (kernel.ι g) u (_ : kernel.ι g ≫ u = 0)) g' eq'

Next tactic:
intros _ _ _ _ hm
/-- If `(f, g)` is exact, then `Abelian.coimage.π g` is a cokernel of `f`. -/
def isColimitCoimage (h : Exact f g) :
    IsColimit
      (CokernelCofork.ofπ (Abelian.coimage.π g) (Abelian.comp_coimage_π_eq_zero h.1) :
        CokernelCofork f) := by
  rw [exact_iff] at h
  refine' CokernelCofork.IsColimit.ofπ _ _
    (fun u hu => cokernel.desc (kernel.ι g) u
      (by rw [← cokernel.π_desc f u hu, ← Category.assoc, h.2, zero_comp]))
    (by aesop_cat) _
  intros _ _ _ _ hm
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
Z'✝ : C
g'✝ : Y ⟶ Z'✝
eq'✝ : f ≫ g'✝ = 0
m✝ : Abelian.coimage g ⟶ Z'✝
hm : coimage.π g ≫ m✝ = g'✝
⊢ m✝ = (fun {Z'} u hu => cokernel.desc (kernel.ι g) u (_ : kernel.ι g ≫ u = 0)) g'✝ eq'✝

Next tactic:
ext
/-- If `(f, g)` is exact, then `Abelian.coimage.π g` is a cokernel of `f`. -/
def isColimitCoimage (h : Exact f g) :
    IsColimit
      (CokernelCofork.ofπ (Abelian.coimage.π g) (Abelian.comp_coimage_π_eq_zero h.1) :
        CokernelCofork f) := by
  rw [exact_iff] at h
  refine' CokernelCofork.IsColimit.ofπ _ _
    (fun u hu => cokernel.desc (kernel.ι g) u
      (by rw [← cokernel.π_desc f u hu, ← Category.assoc, h.2, zero_comp]))
    (by aesop_cat) _
  intros _ _ _ _ hm
  ext
  

Proof state:
case h
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : Exact f g
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
Z'✝ : C
g'✝ : Y ⟶ Z'✝
eq'✝ : f ≫ g'✝ = 0
m✝ : Abelian.coimage g ⟶ Z'✝
hm : coimage.π g ≫ m✝ = g'✝
⊢ coequalizer.π (kernel.ι g) 0 ≫ m✝ =
    coequalizer.π (kernel.ι g) 0 ≫ (fun {Z'} u hu => cokernel.desc (kernel.ι g) u (_ : kernel.ι g ≫ u = 0)) g'✝ eq'✝

Next tactic:
rw [hm, cokernel.π_desc]
/-- If `(f, g)` is exact, then `factorThruImage g` is a cokernel of `f`. -/
def isColimitImage (h : Exact f g) :
    IsColimit (CokernelCofork.ofπ (Limits.factorThruImage g) (comp_factorThruImage_eq_zero h.1)) :=
  IsCokernel.cokernelIso _ _ (isColimitCoimage f g h) (coimageIsoImage' g) <|
    (cancel_mono (Limits.image.ι g)).1 <| by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h : Exact f g
⊢ (Cofork.π (CokernelCofork.ofπ (coimage.π g) (_ : f ≫ coimage.π g = 0)) ≫ (coimageIsoImage' g).hom) ≫
      Limits.image.ι g =
    factorThruImage g ≫ Limits.image.ι g

Next tactic:
simp
theorem exact_cokernel : Exact f (cokernel.π f) := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
⊢ Exact f (cokernel.π f)

Next tactic:
rw [exact_iff]
theorem exact_cokernel : Exact f (cokernel.π f) := by
  rw [exact_iff]
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
⊢ f ≫ cokernel.π f = 0 ∧ kernel.ι (cokernel.π f) ≫ cokernel.π f = 0

Next tactic:
aesop_cat
lemma mono_cokernel_desc_of_exact (h : Exact f g) : Mono (cokernel.desc f g h.w) :=
  suffices h : cokernel.desc f g h.w =
      (IsColimit.coconePointUniqueUpToIso (colimit.isColimit _) (isColimitImage f g h)).hom ≫
        Limits.image.ι g
    from h.symm ▸ mono_comp _ _
  (cancel_epi (cokernel.π f)).1 <| by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h : Exact f g
⊢ cokernel.π f ≫ cokernel.desc f g (_ : f ≫ g = 0) =
    cokernel.π f ≫
      (IsColimit.coconePointUniqueUpToIso (colimit.isColimit (parallelPair f 0)) (isColimitImage f g h)).hom ≫
        Limits.image.ι g

Next tactic:
simp
@[reassoc (attr := nolint unusedHavesSuffices)]
theorem cokernel.desc.inv [Epi g] (ex : Exact f g) :
    have := isIso_cokernel_desc_of_exact_of_epi _ _ ex
    g ≫ inv (cokernel.desc _ _ ex.w) = cokernel.π _ := by
  

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Epi g
ex : Exact f g
⊢ let_fun this := (_ : IsIso (cokernel.desc f g (_ : f ≫ g = 0)));
  g ≫ CategoryTheory.inv (cokernel.desc f g (_ : f ≫ g = 0)) = cokernel.π f

Next tactic:
have := isIso_cokernel_desc_of_exact_of_epi _ _ ex
@[reassoc (attr := nolint unusedHavesSuffices)]
theorem cokernel.desc.inv [Epi g] (ex : Exact f g) :
    have := isIso_cokernel_desc_of_exact_of_epi _ _ ex
    g ≫ inv (cokernel.desc _ _ ex.w) = cokernel.π _ := by
  have := isIso_cokernel_desc_of_exact_of_epi _ _ ex
  

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Epi g
ex : Exact f g
this : IsIso (cokernel.desc f g (_ : f ≫ g = 0))
⊢ let_fun this := (_ : IsIso (cokernel.desc f g (_ : f ≫ g = 0)));
  g ≫ CategoryTheory.inv (cokernel.desc f g (_ : f ≫ g = 0)) = cokernel.π f

Next tactic:
simp
@[reassoc (attr := nolint unusedHavesSuffices)]
theorem kernel.lift.inv [Mono f] (ex : Exact f g) :
    have := isIso_kernel_lift_of_exact_of_mono _ _ ex
    inv (kernel.lift _ _ ex.w) ≫ f = kernel.ι g := by
  

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Mono f
ex : Exact f g
⊢ let_fun this := (_ : IsIso (kernel.lift g f (_ : f ≫ g = 0)));
  CategoryTheory.inv (kernel.lift g f (_ : f ≫ g = 0)) ≫ f = kernel.ι g

Next tactic:
have := isIso_kernel_lift_of_exact_of_mono _ _ ex
@[reassoc (attr := nolint unusedHavesSuffices)]
theorem kernel.lift.inv [Mono f] (ex : Exact f g) :
    have := isIso_kernel_lift_of_exact_of_mono _ _ ex
    inv (kernel.lift _ _ ex.w) ≫ f = kernel.ι g := by
  have := isIso_kernel_lift_of_exact_of_mono _ _ ex
  

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Mono f
ex : Exact f g
this : IsIso (kernel.lift g f (_ : f ≫ g = 0))
⊢ let_fun this := (_ : IsIso (kernel.lift g f (_ : f ≫ g = 0)));
  CategoryTheory.inv (kernel.lift g f (_ : f ≫ g = 0)) ≫ f = kernel.ι g

Next tactic:
simp
/-- If `X ⟶ Y ⟶ Z ⟶ 0` is exact, then the second map is a cokernel of the first. -/
def isColimitOfExactOfEpi [Epi g] (h : Exact f g) : IsColimit (CokernelCofork.ofπ _ h.w) :=
  IsColimit.ofIsoColimit (colimit.isColimit _) <|
    Cocones.ext
      ⟨cokernel.desc _ _ h.w, epiDesc g (cokernel.π f) ((exact_iff _ _).1 h).2,
        (cancel_epi (cokernel.π f)).1 (by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Epi g
h : Exact f g
⊢ cokernel.π f ≫ cokernel.desc f g (_ : f ≫ g = 0) ≫ epiDesc g (cokernel.π f) (_ : kernel.ι g ≫ cokernel.π f = 0) =
    cokernel.π f ≫ 𝟙 (colimit.cocone (parallelPair f 0)).pt

Next tactic:
aesop_cat
/-- If `X ⟶ Y ⟶ Z ⟶ 0` is exact, then the second map is a cokernel of the first. -/
def isColimitOfExactOfEpi [Epi g] (h : Exact f g) : IsColimit (CokernelCofork.ofπ _ h.w) :=
  IsColimit.ofIsoColimit (colimit.isColimit _) <|
    Cocones.ext
      ⟨cokernel.desc _ _ h.w, epiDesc g (cokernel.π f) ((exact_iff _ _).1 h).2,
        (cancel_epi (cokernel.π f)).1 (by aesop_cat), (cancel_epi g).1 (by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Epi g
h : Exact f g
⊢ g ≫ epiDesc g (cokernel.π f) (_ : kernel.ι g ≫ cokernel.π f = 0) ≫ cokernel.desc f g (_ : f ≫ g = 0) =
    g ≫ 𝟙 (CokernelCofork.ofπ g (_ : f ≫ g = 0)).pt

Next tactic:
aesop_cat
/-- If `X ⟶ Y ⟶ Z ⟶ 0` is exact, then the second map is a cokernel of the first. -/
def isColimitOfExactOfEpi [Epi g] (h : Exact f g) : IsColimit (CokernelCofork.ofπ _ h.w) :=
  IsColimit.ofIsoColimit (colimit.isColimit _) <|
    Cocones.ext
      ⟨cokernel.desc _ _ h.w, epiDesc g (cokernel.π f) ((exact_iff _ _).1 h).2,
        (cancel_epi (cokernel.π f)).1 (by aesop_cat), (cancel_epi g).1 (by aesop_cat)⟩
          (by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Epi g
h : Exact f g
⊢ ∀ (j : WalkingParallelPair),
    (colimit.cocone (parallelPair f 0)).ι.app j ≫
        (Iso.mk (cokernel.desc f g (_ : f ≫ g = 0))
            (epiDesc g (cokernel.π f) (_ : kernel.ι g ≫ cokernel.π f = 0))).hom =
      (CokernelCofork.ofπ g (_ : f ≫ g = 0)).ι.app j

Next tactic:
rintro (_|_)
/-- If `X ⟶ Y ⟶ Z ⟶ 0` is exact, then the second map is a cokernel of the first. -/
def isColimitOfExactOfEpi [Epi g] (h : Exact f g) : IsColimit (CokernelCofork.ofπ _ h.w) :=
  IsColimit.ofIsoColimit (colimit.isColimit _) <|
    Cocones.ext
      ⟨cokernel.desc _ _ h.w, epiDesc g (cokernel.π f) ((exact_iff _ _).1 h).2,
        (cancel_epi (cokernel.π f)).1 (by aesop_cat), (cancel_epi g).1 (by aesop_cat)⟩
          (by rintro (_|_) <;> 

Proof state:
case zero
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Epi g
h : Exact f g
⊢ (colimit.cocone (parallelPair f 0)).ι.app WalkingParallelPair.zero ≫
      (Iso.mk (cokernel.desc f g (_ : f ≫ g = 0)) (epiDesc g (cokernel.π f) (_ : kernel.ι g ≫ cokernel.π f = 0))).hom =
    (CokernelCofork.ofπ g (_ : f ≫ g = 0)).ι.app WalkingParallelPair.zero

Next tactic:
simp [h.w]
/-- If `X ⟶ Y ⟶ Z ⟶ 0` is exact, then the second map is a cokernel of the first. -/
def isColimitOfExactOfEpi [Epi g] (h : Exact f g) : IsColimit (CokernelCofork.ofπ _ h.w) :=
  IsColimit.ofIsoColimit (colimit.isColimit _) <|
    Cocones.ext
      ⟨cokernel.desc _ _ h.w, epiDesc g (cokernel.π f) ((exact_iff _ _).1 h).2,
        (cancel_epi (cokernel.π f)).1 (by aesop_cat), (cancel_epi g).1 (by aesop_cat)⟩
          (by rintro (_|_) <;> 

Proof state:
case one
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Epi g
h : Exact f g
⊢ (colimit.cocone (parallelPair f 0)).ι.app WalkingParallelPair.one ≫
      (Iso.mk (cokernel.desc f g (_ : f ≫ g = 0)) (epiDesc g (cokernel.π f) (_ : kernel.ι g ≫ cokernel.π f = 0))).hom =
    (CokernelCofork.ofπ g (_ : f ≫ g = 0)).ι.app WalkingParallelPair.one

Next tactic:
simp [h.w]
/-- If `0 ⟶ X ⟶ Y ⟶ Z` is exact, then the first map is a kernel of the second. -/
def isLimitOfExactOfMono [Mono f] (h : Exact f g) : IsLimit (KernelFork.ofι _ h.w) :=
  IsLimit.ofIsoLimit (limit.isLimit _) <|
    Cones.ext
      ⟨monoLift f (kernel.ι g) ((exact_iff _ _).1 h).2, kernel.lift _ _ h.w,
        (cancel_mono (kernel.ι g)).1 (by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Mono f
h : Exact f g
⊢ (monoLift f (kernel.ι g) (_ : kernel.ι g ≫ cokernel.π f = 0) ≫ kernel.lift g f (_ : f ≫ g = 0)) ≫ kernel.ι g =
    𝟙 (limit.cone (parallelPair g 0)).pt ≫ kernel.ι g

Next tactic:
aesop_cat
/-- If `0 ⟶ X ⟶ Y ⟶ Z` is exact, then the first map is a kernel of the second. -/
def isLimitOfExactOfMono [Mono f] (h : Exact f g) : IsLimit (KernelFork.ofι _ h.w) :=
  IsLimit.ofIsoLimit (limit.isLimit _) <|
    Cones.ext
      ⟨monoLift f (kernel.ι g) ((exact_iff _ _).1 h).2, kernel.lift _ _ h.w,
        (cancel_mono (kernel.ι g)).1 (by aesop_cat), (cancel_mono f).1 (by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Mono f
h : Exact f g
⊢ (kernel.lift g f (_ : f ≫ g = 0) ≫ monoLift f (kernel.ι g) (_ : kernel.ι g ≫ cokernel.π f = 0)) ≫ f =
    𝟙 (KernelFork.ofι f (_ : f ≫ g = 0)).pt ≫ f

Next tactic:
aesop_cat
/-- If `0 ⟶ X ⟶ Y ⟶ Z` is exact, then the first map is a kernel of the second. -/
def isLimitOfExactOfMono [Mono f] (h : Exact f g) : IsLimit (KernelFork.ofι _ h.w) :=
  IsLimit.ofIsoLimit (limit.isLimit _) <|
    Cones.ext
      ⟨monoLift f (kernel.ι g) ((exact_iff _ _).1 h).2, kernel.lift _ _ h.w,
        (cancel_mono (kernel.ι g)).1 (by aesop_cat), (cancel_mono f).1 (by aesop_cat)⟩
      fun j => by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Mono f
h : Exact f g
j : WalkingParallelPair
⊢ (limit.cone (parallelPair g 0)).π.app j =
    (Iso.mk (monoLift f (kernel.ι g) (_ : kernel.ι g ≫ cokernel.π f = 0)) (kernel.lift g f (_ : f ≫ g = 0))).hom ≫
      (KernelFork.ofι f (_ : f ≫ g = 0)).π.app j

Next tactic:
cases j
/-- If `0 ⟶ X ⟶ Y ⟶ Z` is exact, then the first map is a kernel of the second. -/
def isLimitOfExactOfMono [Mono f] (h : Exact f g) : IsLimit (KernelFork.ofι _ h.w) :=
  IsLimit.ofIsoLimit (limit.isLimit _) <|
    Cones.ext
      ⟨monoLift f (kernel.ι g) ((exact_iff _ _).1 h).2, kernel.lift _ _ h.w,
        (cancel_mono (kernel.ι g)).1 (by aesop_cat), (cancel_mono f).1 (by aesop_cat)⟩
      fun j => by cases j <;> 

Proof state:
case zero
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Mono f
h : Exact f g
⊢ (limit.cone (parallelPair g 0)).π.app WalkingParallelPair.zero =
    (Iso.mk (monoLift f (kernel.ι g) (_ : kernel.ι g ≫ cokernel.π f = 0)) (kernel.lift g f (_ : f ≫ g = 0))).hom ≫
      (KernelFork.ofι f (_ : f ≫ g = 0)).π.app WalkingParallelPair.zero

Next tactic:
simp
/-- If `0 ⟶ X ⟶ Y ⟶ Z` is exact, then the first map is a kernel of the second. -/
def isLimitOfExactOfMono [Mono f] (h : Exact f g) : IsLimit (KernelFork.ofι _ h.w) :=
  IsLimit.ofIsoLimit (limit.isLimit _) <|
    Cones.ext
      ⟨monoLift f (kernel.ι g) ((exact_iff _ _).1 h).2, kernel.lift _ _ h.w,
        (cancel_mono (kernel.ι g)).1 (by aesop_cat), (cancel_mono f).1 (by aesop_cat)⟩
      fun j => by cases j <;> 

Proof state:
case one
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
inst✝ : Mono f
h : Exact f g
⊢ (limit.cone (parallelPair g 0)).π.app WalkingParallelPair.one =
    (Iso.mk (monoLift f (kernel.ι g) (_ : kernel.ι g ≫ cokernel.π f = 0)) (kernel.lift g f (_ : f ≫ g = 0))).hom ≫
      (KernelFork.ofι f (_ : f ≫ g = 0)).π.app WalkingParallelPair.one

Next tactic:
simp
theorem exact_of_is_cokernel (w : f ≫ g = 0)
    (h : IsColimit (CokernelCofork.ofπ _ w)) : Exact f g := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsColimit (CokernelCofork.ofπ g w)
⊢ Exact f g

Next tactic:
refine' (exact_iff _ _).2 ⟨w, _⟩
theorem exact_of_is_cokernel (w : f ≫ g = 0)
    (h : IsColimit (CokernelCofork.ofπ _ w)) : Exact f g := by
  refine' (exact_iff _ _).2 ⟨w, _⟩
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsColimit (CokernelCofork.ofπ g w)
⊢ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
have := h.fac (CokernelCofork.ofπ _ (cokernel.condition f)) WalkingParallelPair.one
theorem exact_of_is_cokernel (w : f ≫ g = 0)
    (h : IsColimit (CokernelCofork.ofπ _ w)) : Exact f g := by
  refine' (exact_iff _ _).2 ⟨w, _⟩
  have := h.fac (CokernelCofork.ofπ _ (cokernel.condition f)) WalkingParallelPair.one
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsColimit (CokernelCofork.ofπ g w)
this :
  (CokernelCofork.ofπ g w).ι.app WalkingParallelPair.one ≫
      IsColimit.desc h (CokernelCofork.ofπ (cokernel.π f) (_ : f ≫ cokernel.π f = 0)) =
    (CokernelCofork.ofπ (cokernel.π f) (_ : f ≫ cokernel.π f = 0)).ι.app WalkingParallelPair.one
⊢ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
simp only [Cofork.ofπ_ι_app] at this
theorem exact_of_is_cokernel (w : f ≫ g = 0)
    (h : IsColimit (CokernelCofork.ofπ _ w)) : Exact f g := by
  refine' (exact_iff _ _).2 ⟨w, _⟩
  have := h.fac (CokernelCofork.ofπ _ (cokernel.condition f)) WalkingParallelPair.one
  simp only [Cofork.ofπ_ι_app] at this
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsColimit (CokernelCofork.ofπ g w)
this : g ≫ IsColimit.desc h (CokernelCofork.ofπ (cokernel.π f) (_ : f ≫ cokernel.π f = 0)) = cokernel.π f
⊢ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
rw [← this, ← Category.assoc, kernel.condition, zero_comp]
theorem exact_of_is_kernel (w : f ≫ g = 0) (h : IsLimit (KernelFork.ofι _ w)) : Exact f g := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsLimit (KernelFork.ofι f w)
⊢ Exact f g

Next tactic:
refine' (exact_iff _ _).2 ⟨w, _⟩
theorem exact_of_is_kernel (w : f ≫ g = 0) (h : IsLimit (KernelFork.ofι _ w)) : Exact f g := by
  refine' (exact_iff _ _).2 ⟨w, _⟩
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsLimit (KernelFork.ofι f w)
⊢ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
have := h.fac (KernelFork.ofι _ (kernel.condition g)) WalkingParallelPair.zero
theorem exact_of_is_kernel (w : f ≫ g = 0) (h : IsLimit (KernelFork.ofι _ w)) : Exact f g := by
  refine' (exact_iff _ _).2 ⟨w, _⟩
  have := h.fac (KernelFork.ofι _ (kernel.condition g)) WalkingParallelPair.zero
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsLimit (KernelFork.ofι f w)
this :
  IsLimit.lift h (KernelFork.ofι (kernel.ι g) (_ : kernel.ι g ≫ g = 0)) ≫
      (KernelFork.ofι f w).π.app WalkingParallelPair.zero =
    (KernelFork.ofι (kernel.ι g) (_ : kernel.ι g ≫ g = 0)).π.app WalkingParallelPair.zero
⊢ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
simp only [Fork.ofι_π_app] at this
theorem exact_of_is_kernel (w : f ≫ g = 0) (h : IsLimit (KernelFork.ofι _ w)) : Exact f g := by
  refine' (exact_iff _ _).2 ⟨w, _⟩
  have := h.fac (KernelFork.ofι _ (kernel.condition g)) WalkingParallelPair.zero
  simp only [Fork.ofι_π_app] at this
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
w : f ≫ g = 0
h : IsLimit (KernelFork.ofι f w)
this : IsLimit.lift h (KernelFork.ofι (kernel.ι g) (_ : kernel.ι g ≫ g = 0)) ≫ f = kernel.ι g
⊢ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
rw [← this, Category.assoc, cokernel.condition, comp_zero]
theorem exact_iff_exact_image_ι : Exact f g ↔ Exact (Abelian.image.ι f) g := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
⊢ Exact f g ↔ Exact (image.ι f) g

Next tactic:
conv_lhs => rw [← Abelian.image.fac f]
theorem exact_iff_exact_image_ι : Exact f g ↔ Exact (Abelian.image.ι f) g := by
  conv_lhs => 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
| Exact f g

Next tactic:
rw [← Abelian.image.fac f]
theorem exact_iff_exact_image_ι : Exact f g ↔ Exact (Abelian.image.ι f) g := by
  conv_lhs => rw [← Abelian.image.fac f]
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
⊢ Exact (Abelian.factorThruImage f ≫ image.ι f) g ↔ Exact (image.ι f) g

Next tactic:
rw [exact_epi_comp_iff]