l3lab/ntp-mathlib huggingface dataset:
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
hl : IsLimit (KernelFork.ofι (Subobject.arrow (imageSubobject f)) (_ : Subobject.arrow (imageSubobject f) ≫ g = 0))
this :
  imageToKernel f g (_ : f ≫ g = 0) =
    (IsLimit.conePointUniqueUpToIso hl (limit.isLimit (parallelPair g 0))).hom ≫ (kernelSubobjectIso g).inv
⊢ Epi (imageToKernel f g (_ : f ≫ g = 0))

Next tactic:
rw [this]
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
hl : IsLimit (KernelFork.ofι (Subobject.arrow (imageSubobject f)) (_ : Subobject.arrow (imageSubobject f) ≫ g = 0))
this :
  imageToKernel f g (_ : f ≫ g = 0) =
    (IsLimit.conePointUniqueUpToIso hl (limit.isLimit (parallelPair g 0))).hom ≫ (kernelSubobjectIso g).inv
⊢ Epi ((IsLimit.conePointUniqueUpToIso hl (limit.isLimit (parallelPair g 0))).hom ≫ (kernelSubobjectIso g).inv)

Next tactic:
infer_instance
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      infer_instance
    

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

Next tactic:
refine KernelFork.IsLimit.ofι _ _ (fun u hu ↦ ?_) ?_ (fun _ _ _ h ↦ ?_)
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      infer_instance
    refine KernelFork.IsLimit.ofι _ _ (fun u hu ↦ ?_) ?_ (fun _ _ _ h ↦ ?_)
    · 

Proof state:
case hl.refine_1
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
W'✝ : C
u : W'✝ ⟶ Y
hu : u ≫ g = 0
⊢ W'✝ ⟶ Subobject.underlying.obj (imageSubobject f)

Next tactic:
refine kernel.lift (cokernel.π f) u ?_ ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso _).inv
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      infer_instance
    refine KernelFork.IsLimit.ofι _ _ (fun u hu ↦ ?_) ?_ (fun _ _ _ h ↦ ?_)
    · refine kernel.lift (cokernel.π f) u ?_ ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso _).inv
      

Proof state:
case hl.refine_1
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
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]
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      infer_instance
    refine KernelFork.IsLimit.ofι _ _ (fun u hu ↦ ?_) ?_ (fun _ _ _ h ↦ ?_)
    · refine kernel.lift (cokernel.π f) u ?_ ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso _).inv
      rw [← kernel.lift_ι g u hu, Category.assoc, h.2, comp_zero]
    · 

Proof state:
case hl.refine_2
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
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) ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso f).inv)
          g' eq' ≫
        Subobject.arrow (imageSubobject f) =
      g'

Next tactic:
aesop_cat
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      infer_instance
    refine KernelFork.IsLimit.ofι _ _ (fun u hu ↦ ?_) ?_ (fun _ _ _ h ↦ ?_)
    · refine kernel.lift (cokernel.π f) u ?_ ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso _).inv
      rw [← kernel.lift_ι g u hu, Category.assoc, h.2, comp_zero]
    · aesop_cat
    · 

Proof state:
case hl.refine_3
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
W'✝ : C
x✝² : W'✝ ⟶ Y
x✝¹ : x✝² ≫ g = 0
x✝ : W'✝ ⟶ Subobject.underlying.obj (imageSubobject f)
h : x✝ ≫ Subobject.arrow (imageSubobject f) = x✝²
⊢ x✝ =
    (fun {W'} u hu =>
        kernel.lift (cokernel.π f) u (_ : u ≫ cokernel.π f = 0) ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso f).inv)
      x✝² x✝¹

Next tactic:
intros
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      infer_instance
    refine KernelFork.IsLimit.ofι _ _ (fun u hu ↦ ?_) ?_ (fun _ _ _ h ↦ ?_)
    · refine kernel.lift (cokernel.π f) u ?_ ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso _).inv
      rw [← kernel.lift_ι g u hu, Category.assoc, h.2, comp_zero]
    · aesop_cat
    · intros
      

Proof state:
case hl.refine_3
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
W'✝ : C
x✝² : W'✝ ⟶ Y
x✝¹ : x✝² ≫ g = 0
x✝ : W'✝ ⟶ Subobject.underlying.obj (imageSubobject f)
h : x✝ ≫ Subobject.arrow (imageSubobject f) = x✝²
⊢ x✝ =
    (fun {W'} u hu =>
        kernel.lift (cokernel.π f) u (_ : u ≫ cokernel.π f = 0) ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso f).inv)
      x✝² x✝¹

Next tactic:
rw [← cancel_mono (imageSubobject f).arrow, h]
theorem exact_iff : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 := by
  constructor
  · exact fun h ↦ ⟨h.1, kernel_comp_cokernel f g h⟩
  · refine fun h ↦ ⟨h.1, ?_⟩
    suffices hl :
      IsLimit (KernelFork.ofι (imageSubobject f).arrow (imageSubobject_arrow_comp_eq_zero h.1))
    · have : imageToKernel f g h.1 = (hl.conePointUniqueUpToIso (limit.isLimit _)).hom ≫
          (kernelSubobjectIso _).inv := by ext; simp
      rw [this]
      infer_instance
    refine KernelFork.IsLimit.ofι _ _ (fun u hu ↦ ?_) ?_ (fun _ _ _ h ↦ ?_)
    · refine kernel.lift (cokernel.π f) u ?_ ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso _).inv
      rw [← kernel.lift_ι g u hu, Category.assoc, h.2, comp_zero]
    · aesop_cat
    · intros
      rw [← cancel_mono (imageSubobject f).arrow, h]
      

Proof state:
case hl.refine_3
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
h✝ : f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
W'✝ : C
x✝² : W'✝ ⟶ Y
x✝¹ : x✝² ≫ g = 0
x✝ : W'✝ ⟶ Subobject.underlying.obj (imageSubobject f)
h : x✝ ≫ Subobject.arrow (imageSubobject f) = x✝²
⊢ x✝² =
    (fun {W'} u hu =>
          kernel.lift (cokernel.π f) u (_ : u ≫ cokernel.π f = 0) ≫ (imageIsoImage f).hom ≫ (imageSubobjectIso f).inv)
        x✝² x✝¹ ≫
      Subobject.arrow (imageSubobject f)

Next tactic:
simp
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
⊢ Exact f g ↔ f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0

Next tactic:
constructor
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  constructor
  · 

Proof state:
case mp
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
⊢ Exact f g → f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0

Next tactic:
intro h
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  constructor
  · intro h
    

Proof state:
case mp
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
h : Exact f g
⊢ f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0

Next tactic:
exact ⟨h.1, fork_ι_comp_cofork_π f g h cg cf⟩
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  constructor
  · intro h
    exact ⟨h.1, fork_ι_comp_cofork_π f g h cg cf⟩
  · 

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
⊢ f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0 → Exact f g

Next tactic:
rw [exact_iff]
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  constructor
  · intro h
    exact ⟨h.1, fork_ι_comp_cofork_π f g h cg cf⟩
  · rw [exact_iff]
    

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
⊢ f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0 → f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
refine fun h => ⟨h.1, ?_⟩
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  constructor
  · intro h
    exact ⟨h.1, fork_ι_comp_cofork_π f g h cg cf⟩
  · rw [exact_iff]
    refine fun h => ⟨h.1, ?_⟩
    

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
h : f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0
⊢ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
apply zero_of_epi_comp (IsLimit.conePointUniqueUpToIso hg (limit.isLimit _)).hom
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  constructor
  · intro h
    exact ⟨h.1, fork_ι_comp_cofork_π f g h cg cf⟩
  · rw [exact_iff]
    refine fun h => ⟨h.1, ?_⟩
    apply zero_of_epi_comp (IsLimit.conePointUniqueUpToIso hg (limit.isLimit _)).hom
    

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
h : f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0
⊢ (IsLimit.conePointUniqueUpToIso hg (limit.isLimit (parallelPair g 0))).hom ≫ kernel.ι g ≫ cokernel.π f = 0

Next tactic:
apply zero_of_comp_mono (IsColimit.coconePointUniqueUpToIso (colimit.isColimit _) hf).hom
theorem exact_iff' {cg : KernelFork g} (hg : IsLimit cg) {cf : CokernelCofork f}
    (hf : IsColimit cf) : Exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 := by
  constructor
  · intro h
    exact ⟨h.1, fork_ι_comp_cofork_π f g h cg cf⟩
  · rw [exact_iff]
    refine fun h => ⟨h.1, ?_⟩
    apply zero_of_epi_comp (IsLimit.conePointUniqueUpToIso hg (limit.isLimit _)).hom
    apply zero_of_comp_mono (IsColimit.coconePointUniqueUpToIso (colimit.isColimit _) hf).hom
    

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
cg : KernelFork g
hg : IsLimit cg
cf : CokernelCofork f
hf : IsColimit cf
h : f ≫ g = 0 ∧ Fork.ι cg ≫ Cofork.π cf = 0
⊢ ((IsLimit.conePointUniqueUpToIso hg (limit.isLimit (parallelPair g 0))).hom ≫ kernel.ι g ≫ cokernel.π f) ≫
      (IsColimit.coconePointUniqueUpToIso (colimit.isColimit (parallelPair f 0)) hf).hom =
    0

Next tactic:
simp [h.2]
open List in
theorem exact_tfae :
    TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0,
      imageSubobject f = kernelSubobject 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
⊢ TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0, imageSubobject f = kernelSubobject g]

Next tactic:
tfae_have 1 ↔ 2
open List in
theorem exact_tfae :
    TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0,
      imageSubobject f = kernelSubobject g] := by
  tfae_have 1 ↔ 2; · 

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

Next tactic:
apply exact_iff
open List in
theorem exact_tfae :
    TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0,
      imageSubobject f = kernelSubobject g] := by
  tfae_have 1 ↔ 2; · apply 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
tfae_1_iff_2 : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
⊢ TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0, imageSubobject f = kernelSubobject g]

Next tactic:
tfae_have 1 ↔ 3
open List in
theorem exact_tfae :
    TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0,
      imageSubobject f = kernelSubobject g] := by
  tfae_have 1 ↔ 2; · apply exact_iff
  tfae_have 1 ↔ 3; · 

Proof state:
case tfae_1_iff_3
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
tfae_1_iff_2 : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
⊢ Exact f g ↔ imageSubobject f = kernelSubobject g

Next tactic:
apply exact_iff_image_eq_kernel
open List in
theorem exact_tfae :
    TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0,
      imageSubobject f = kernelSubobject g] := by
  tfae_have 1 ↔ 2; · apply exact_iff
  tfae_have 1 ↔ 3; · apply exact_iff_image_eq_kernel
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
inst✝ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
tfae_1_iff_2 : Exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0
tfae_1_iff_3 : Exact f g ↔ imageSubobject f = kernelSubobject g
⊢ TFAE [Exact f g, f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0, imageSubobject f = kernelSubobject g]

Next tactic:
tfae_finish
nonrec theorem IsEquivalence.exact_iff {D : Type u₁} [Category.{v₁} D] [Abelian D] (F : C ⥤ D)
    [IsEquivalence F] : Exact (F.map f) (F.map g) ↔ 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
D : Type u₁
inst✝² : Category.{v₁, u₁} D
inst✝¹ : Abelian D
F : C ⥤ D
inst✝ : IsEquivalence F
⊢ Exact (F.map f) (F.map g) ↔ Exact f g

Next tactic:
simp only [exact_iff, ← F.map_eq_zero_iff, F.map_comp, Category.assoc, ←
    kernelComparison_comp_ι g F, ← π_comp_cokernelComparison f F]
nonrec theorem IsEquivalence.exact_iff {D : Type u₁} [Category.{v₁} D] [Abelian D] (F : C ⥤ D)
    [IsEquivalence F] : Exact (F.map f) (F.map g) ↔ Exact f g := by
  simp only [exact_iff, ← F.map_eq_zero_iff, F.map_comp, Category.assoc, ←
    kernelComparison_comp_ι g F, ← π_comp_cokernelComparison f F]
  

Proof state:
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
inst✝³ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
D : Type u₁
inst✝² : Category.{v₁, u₁} D
inst✝¹ : Abelian D
F : C ⥤ D
inst✝ : IsEquivalence F
⊢ F.map f ≫ F.map g = 0 ∧ kernel.ι (F.map g) ≫ cokernel.π (F.map f) = 0 ↔
    F.map f ≫ F.map g = 0 ∧
      kernelComparison g F ≫ kernel.ι (F.map g) ≫ cokernel.π (F.map f) ≫ cokernelComparison f F = 0

Next tactic:
rw [IsIso.comp_left_eq_zero (kernelComparison g F), ← Category.assoc,
    IsIso.comp_right_eq_zero _ (cokernelComparison f F)]
/-- The dual result is true even in non-abelian categories, see
    `CategoryTheory.exact_comp_mono_iff`. -/
theorem exact_epi_comp_iff {W : C} (h : W ⟶ X) [Epi h] : Exact (h ≫ f) g ↔ 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 : C
h : W ⟶ X
inst✝ : Epi h
⊢ Exact (h ≫ f) g ↔ Exact f g

Next tactic:
refine' ⟨fun hfg => _, fun h => exact_epi_comp h⟩
/-- The dual result is true even in non-abelian categories, see
    `CategoryTheory.exact_comp_mono_iff`. -/
theorem exact_epi_comp_iff {W : C} (h : W ⟶ X) [Epi h] : Exact (h ≫ f) g ↔ Exact f g := by
  refine' ⟨fun hfg => _, fun h => exact_epi_comp h⟩
  

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
W : C
h : W ⟶ X
inst✝ : Epi h
hfg : Exact (h ≫ f) g
⊢ Exact f g

Next tactic:
let hc := isCokernelOfComp _ _ (colimit.isColimit (parallelPair (h ≫ f) 0))
    (by rw [← cancel_epi h, ← Category.assoc, CokernelCofork.condition, comp_zero]) rfl
/-- The dual result is true even in non-abelian categories, see
    `CategoryTheory.exact_comp_mono_iff`. -/
theorem exact_epi_comp_iff {W : C} (h : W ⟶ X) [Epi h] : Exact (h ≫ f) g ↔ Exact f g := by
  refine' ⟨fun hfg => _, fun h => exact_epi_comp h⟩
  let hc := isCokernelOfComp _ _ (colimit.isColimit (parallelPair (h ≫ f) 0))
    (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 : C
h : W ⟶ X
inst✝ : Epi h
hfg : Exact (h ≫ f) g
⊢ f ≫ Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)) = 0

Next tactic:
rw [← cancel_epi h, ← Category.assoc, CokernelCofork.condition, comp_zero]
/-- The dual result is true even in non-abelian categories, see
    `CategoryTheory.exact_comp_mono_iff`. -/
theorem exact_epi_comp_iff {W : C} (h : W ⟶ X) [Epi h] : Exact (h ≫ f) g ↔ Exact f g := by
  refine' ⟨fun hfg => _, fun h => exact_epi_comp h⟩
  let hc := isCokernelOfComp _ _ (colimit.isColimit (parallelPair (h ≫ f) 0))
    (by rw [← cancel_epi h, ← Category.assoc, CokernelCofork.condition, comp_zero]) rfl
  

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
W : C
h : W ⟶ X
inst✝ : Epi h
hfg : Exact (h ≫ f) g
hc : IsColimit
  (CokernelCofork.ofπ (Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)))
    (_ : f ≫ Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)) = 0)) :=
  isCokernelOfComp h (h ≫ f) (colimit.isColimit (parallelPair (h ≫ f) 0))
    (_ : f ≫ Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)) = 0) (_ : h ≫ f = h ≫ f)
⊢ Exact f g

Next tactic:
refine' (exact_iff' _ _ (limit.isLimit _) hc).2 ⟨_, ((exact_iff _ _).1 hfg).2⟩
/-- The dual result is true even in non-abelian categories, see
    `CategoryTheory.exact_comp_mono_iff`. -/
theorem exact_epi_comp_iff {W : C} (h : W ⟶ X) [Epi h] : Exact (h ≫ f) g ↔ Exact f g := by
  refine' ⟨fun hfg => _, fun h => exact_epi_comp h⟩
  let hc := isCokernelOfComp _ _ (colimit.isColimit (parallelPair (h ≫ f) 0))
    (by rw [← cancel_epi h, ← Category.assoc, CokernelCofork.condition, comp_zero]) rfl
  refine' (exact_iff' _ _ (limit.isLimit _) hc).2 ⟨_, ((exact_iff _ _).1 hfg).2⟩
  

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
inst✝¹ : Abelian C
X Y Z : C
f : X ⟶ Y
g : Y ⟶ Z
W : C
h : W ⟶ X
inst✝ : Epi h
hfg : Exact (h ≫ f) g
hc : IsColimit
  (CokernelCofork.ofπ (Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)))
    (_ : f ≫ Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)) = 0)) :=
  isCokernelOfComp h (h ≫ f) (colimit.isColimit (parallelPair (h ≫ f) 0))
    (_ : f ≫ Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)) = 0) (_ : h ≫ f = h ≫ f)
⊢ f ≫ g = 0

Next tactic:
exact zero_of_epi_comp h (by rw [← hfg.1, Category.assoc])
/-- The dual result is true even in non-abelian categories, see
    `CategoryTheory.exact_comp_mono_iff`. -/
theorem exact_epi_comp_iff {W : C} (h : W ⟶ X) [Epi h] : Exact (h ≫ f) g ↔ Exact f g := by
  refine' ⟨fun hfg => _, fun h => exact_epi_comp h⟩
  let hc := isCokernelOfComp _ _ (colimit.isColimit (parallelPair (h ≫ f) 0))
    (by rw [← cancel_epi h, ← Category.assoc, CokernelCofork.condition, comp_zero]) rfl
  refine' (exact_iff' _ _ (limit.isLimit _) hc).2 ⟨_, ((exact_iff _ _).1 hfg).2⟩
  exact zero_of_epi_comp h (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 : C
h : W ⟶ X
inst✝ : Epi h
hfg : Exact (h ≫ f) g
hc : IsColimit
  (CokernelCofork.ofπ (Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)))
    (_ : f ≫ Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)) = 0)) :=
  isCokernelOfComp h (h ≫ f) (colimit.isColimit (parallelPair (h ≫ f) 0))
    (_ : f ≫ Cofork.π (colimit.cocone (parallelPair (h ≫ f) 0)) = 0) (_ : h ≫ f = h ≫ f)
⊢ h ≫ f ≫ g = 0

Next tactic:
rw [← hfg.1, Category.assoc]
/-- 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
  

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
⊢ IsLimit (KernelFork.ofι (image.ι f) (_ : image.ι f ≫ g = 0))

Next tactic:
rw [exact_iff] at h
/-- 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
  

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
⊢ IsLimit (KernelFork.ofι (image.ι f) (_ : image.ι f ≫ g = 0))

Next tactic:
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
      rw [← cancel_mono (image.ι f), hm, kernel.lift_ι])