l3lab/ntp-mathlib huggingface dataset:
/-- Auxiliary construction for `coimageIsoImage` -/
def coimageIsoImageAux {X Y : C} (f : X ⟶ Y) :
    kernel (G.map (cokernel.π (F.map f))) ≅ kernel (cokernel.π f) := by
  have : PreservesColimits G := adj.leftAdjointPreservesColimits
  -- porting note: the next `have` has been added, otherwise some instance were not found
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  calc
    kernel (G.map (cokernel.π (F.map f))) ≅
        kernel (cokernel.π (G.map (F.map f)) ≫ cokernelComparison (F.map f) G) :=
      kernelIsoOfEq (π_comp_cokernelComparison _ _).symm
    _ ≅ kernel (cokernel.π (G.map (F.map f))) := (kernelCompMono _ _)
    _ ≅ kernel (cokernel.π (_ ≫ f ≫ _) ≫ (cokernelIsoOfEq _).hom) :=
      (kernelIsoOfEq (π_comp_cokernelIsoOfEq_hom (NatIso.naturality_2 i f)).symm)
    _ ≅ kernel (cokernel.π (_ ≫ f ≫ _)) := (kernelCompMono _ _)
    _ ≅ kernel (cokernel.π (f ≫ i.inv.app Y) ≫ (cokernelEpiComp (i.hom.app X) _).inv) :=
      (kernelIsoOfEq (by simp only [cokernel.π_desc, cokernelEpiComp_inv]))
    _ ≅ kernel (cokernel.π (f ≫ _)) := (kernelCompMono _ _)
    _ ≅ kernel (inv (i.inv.app Y) ≫ cokernel.π f ≫ (cokernelCompIsIso f (i.inv.app Y)).inv) :=
      (kernelIsoOfEq
        (by 

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
D : Type u₂
inst✝⁵ : Category.{v, u₂} D
inst✝⁴ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝³ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝² : HasCokernels C
inst✝¹ : HasKernels C
inst✝ : PreservesFiniteLimits G
X Y : C
f : X ⟶ Y
this✝ : PreservesColimits G
this : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f'
⊢ cokernel.π (f ≫ i.inv.app Y) = inv (i.inv.app Y) ≫ cokernel.π f ≫ (cokernelCompIsIso f (i.inv.app Y)).inv

Next tactic:
simp only [cokernel.π_desc, cokernelCompIsIso_inv, Iso.hom_inv_id_app_assoc,
          NatIso.inv_inv_app]
/-- Auxiliary definition: the abelian coimage and abelian image agree.
We still need to check that this agrees with the canonical morphism.
-/
def coimageIsoImage {X Y : C} (f : X ⟶ Y) : Abelian.coimage f ≅ Abelian.image f := by
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
⊢ Abelian.coimage f ≅ Abelian.image f

Next tactic:
have : PreservesLimits F := adj.rightAdjointPreservesLimits
/-- Auxiliary definition: the abelian coimage and abelian image agree.
We still need to check that this agrees with the canonical morphism.
-/
def coimageIsoImage {X Y : C} (f : X ⟶ Y) : Abelian.coimage f ≅ Abelian.image f := by
  have : PreservesLimits F := adj.rightAdjointPreservesLimits
  -- porting note: the next `have` has been added, otherwise some instance were not found
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
this : PreservesLimits F
⊢ Abelian.coimage f ≅ Abelian.image f

Next tactic:
haveI : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
/-- Auxiliary definition: the abelian coimage and abelian image agree.
We still need to check that this agrees with the canonical morphism.
-/
def coimageIsoImage {X Y : C} (f : X ⟶ Y) : Abelian.coimage f ≅ Abelian.image f := by
  have : PreservesLimits F := adj.rightAdjointPreservesLimits
  -- porting note: the next `have` has been added, otherwise some instance were not found
  haveI : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
this✝ : PreservesLimits F
this : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f'
⊢ Abelian.coimage f ≅ Abelian.image f

Next tactic:
calc
    Abelian.coimage f ≅ cokernel (kernel.ι f) := Iso.refl _
    _ ≅ G.obj (cokernel (F.map (kernel.ι f))) := (cokernelIso _ _ i adj _).symm
    _ ≅ G.obj (cokernel (kernelComparison f F ≫ kernel.ι (F.map f))) :=
      (G.mapIso (cokernelIsoOfEq (by simp)))
    _ ≅ G.obj (cokernel (kernel.ι (F.map f))) := (G.mapIso (cokernelEpiComp _ _))
    _ ≅ G.obj (Abelian.coimage (F.map f)) := (Iso.refl _)
    _ ≅ G.obj (Abelian.image (F.map f)) := (G.mapIso (Abelian.coimageIsoImage _))
    _ ≅ G.obj (kernel (cokernel.π (F.map f))) := (Iso.refl _)
    _ ≅ kernel (G.map (cokernel.π (F.map f))) := (PreservesKernel.iso _ _)
    _ ≅ kernel (cokernel.π f) := (coimageIsoImageAux F G i adj f)
    _ ≅ Abelian.image f := Iso.refl _
/-- Auxiliary definition: the abelian coimage and abelian image agree.
We still need to check that this agrees with the canonical morphism.
-/
def coimageIsoImage {X Y : C} (f : X ⟶ Y) : Abelian.coimage f ≅ Abelian.image f := by
  have : PreservesLimits F := adj.rightAdjointPreservesLimits
  -- porting note: the next `have` has been added, otherwise some instance were not found
  haveI : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  calc
    Abelian.coimage f ≅ cokernel (kernel.ι f) := Iso.refl _
    _ ≅ G.obj (cokernel (F.map (kernel.ι f))) := (cokernelIso _ _ i adj _).symm
    _ ≅ G.obj (cokernel (kernelComparison f F ≫ kernel.ι (F.map f))) :=
      (G.mapIso (cokernelIsoOfEq (by 

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
this✝ : PreservesLimits F
this : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f'
⊢ F.map (kernel.ι f) = kernelComparison f F ≫ kernel.ι (F.map f)

Next tactic:
simp
@[nolint unusedHavesSuffices]
theorem coimageIsoImage_hom {X Y : C} (f : X ⟶ Y) :
    (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f := by
  -- porting note: the next `have` have been added, otherwise some instance were not found
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
⊢ (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f

Next tactic:
have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
@[nolint unusedHavesSuffices]
theorem coimageIsoImage_hom {X Y : C} (f : X ⟶ Y) :
    (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f := by
  -- porting note: the next `have` have been added, otherwise some instance were not found
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
this : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f'
⊢ (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f

Next tactic:
have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasKernel f' := inferInstance
@[nolint unusedHavesSuffices]
theorem coimageIsoImage_hom {X Y : C} (f : X ⟶ Y) :
    (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f := by
  -- porting note: the next `have` have been added, otherwise some instance were not found
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasKernel f' := inferInstance
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
this✝ : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f'
this : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasKernel f'
⊢ (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f

Next tactic:
have : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
@[nolint unusedHavesSuffices]
theorem coimageIsoImage_hom {X Y : C} (f : X ⟶ Y) :
    (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f := by
  -- porting note: the next `have` have been added, otherwise some instance were not found
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasKernel f' := inferInstance
  have : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
this✝¹ : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f'
this✝ : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasKernel f'
this : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f'
⊢ (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f

Next tactic:
have : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasKernel f' := inferInstance
@[nolint unusedHavesSuffices]
theorem coimageIsoImage_hom {X Y : C} (f : X ⟶ Y) :
    (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f := by
  -- porting note: the next `have` have been added, otherwise some instance were not found
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  have : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasKernel f' := inferInstance
  have : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f' := inferInstance
  have : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasKernel f' := inferInstance
  

Proof state:
C : Type u₁
inst✝⁸ : Category.{v, u₁} C
inst✝⁷ : Preadditive C
D : Type u₂
inst✝⁶ : Category.{v, u₂} D
inst✝⁵ : Abelian D
F : C ⥤ D
G : D ⥤ C
inst✝⁴ : Functor.PreservesZeroMorphisms G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
inst✝³ : HasCokernels C
inst✝² : HasKernels C
inst✝¹ : PreservesFiniteLimits G
inst✝ : Functor.PreservesZeroMorphisms F
X Y : C
f : X ⟶ Y
this✝² : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasCokernel f'
this✝¹ : ∀ (X' Y' : C) (f' : X' ⟶ Y'), HasKernel f'
this✝ : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasCokernel f'
this : ∀ (X' Y' : D) (f' : X' ⟶ Y'), HasKernel f'
⊢ (coimageIsoImage F G i adj f).hom = Abelian.coimageImageComparison f

Next tactic:
dsimp only [coimageIsoImage, Iso.instTransIso_trans, Iso.refl, Iso.trans, Iso.symm,
    Functor.mapIso, cokernelEpiComp, cokernelIso, cokernelCompIsIso_inv,
    asIso, coimageIsoImageAux, kernelCompMono]
/-- If `C` is an additive category, `D` is an abelian category,
we have `F : C ⥤ D` `G : D ⥤ C` (both preserving zero morphisms),
`G` is left exact (that is, preserves finite limits),
and further we have `adj : G ⊣ F` and `i : F ⋙ G ≅ 𝟭 C`,
then `C` is also abelian.

See <https://stacks.math.columbia.edu/tag/03A3>
-/
def abelianOfAdjunction {C : Type u₁} [Category.{v} C] [Preadditive C] [HasFiniteProducts C]
    {D : Type u₂} [Category.{v} D] [Abelian D] (F : C ⥤ D) [Functor.PreservesZeroMorphisms F]
    (G : D ⥤ C) [Functor.PreservesZeroMorphisms G] [PreservesFiniteLimits G] (i : F ⋙ G ≅ 𝟭 C)
    (adj : G ⊣ F) : Abelian C := by
  

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
inst✝⁵ : HasFiniteProducts C
D : Type u₂
inst✝⁴ : Category.{v, u₂} D
inst✝³ : Abelian D
F : C ⥤ D
inst✝² : Functor.PreservesZeroMorphisms F
G : D ⥤ C
inst✝¹ : Functor.PreservesZeroMorphisms G
inst✝ : PreservesFiniteLimits G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
⊢ Abelian C

Next tactic:
haveI := hasKernels F G i
/-- If `C` is an additive category, `D` is an abelian category,
we have `F : C ⥤ D` `G : D ⥤ C` (both preserving zero morphisms),
`G` is left exact (that is, preserves finite limits),
and further we have `adj : G ⊣ F` and `i : F ⋙ G ≅ 𝟭 C`,
then `C` is also abelian.

See <https://stacks.math.columbia.edu/tag/03A3>
-/
def abelianOfAdjunction {C : Type u₁} [Category.{v} C] [Preadditive C] [HasFiniteProducts C]
    {D : Type u₂} [Category.{v} D] [Abelian D] (F : C ⥤ D) [Functor.PreservesZeroMorphisms F]
    (G : D ⥤ C) [Functor.PreservesZeroMorphisms G] [PreservesFiniteLimits G] (i : F ⋙ G ≅ 𝟭 C)
    (adj : G ⊣ F) : Abelian C := by
  haveI := hasKernels F G i
  

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
inst✝⁵ : HasFiniteProducts C
D : Type u₂
inst✝⁴ : Category.{v, u₂} D
inst✝³ : Abelian D
F : C ⥤ D
inst✝² : Functor.PreservesZeroMorphisms F
G : D ⥤ C
inst✝¹ : Functor.PreservesZeroMorphisms G
inst✝ : PreservesFiniteLimits G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
this : HasKernels C
⊢ Abelian C

Next tactic:
haveI := hasCokernels F G i adj
/-- If `C` is an additive category, `D` is an abelian category,
we have `F : C ⥤ D` `G : D ⥤ C` (both preserving zero morphisms),
`G` is left exact (that is, preserves finite limits),
and further we have `adj : G ⊣ F` and `i : F ⋙ G ≅ 𝟭 C`,
then `C` is also abelian.

See <https://stacks.math.columbia.edu/tag/03A3>
-/
def abelianOfAdjunction {C : Type u₁} [Category.{v} C] [Preadditive C] [HasFiniteProducts C]
    {D : Type u₂} [Category.{v} D] [Abelian D] (F : C ⥤ D) [Functor.PreservesZeroMorphisms F]
    (G : D ⥤ C) [Functor.PreservesZeroMorphisms G] [PreservesFiniteLimits G] (i : F ⋙ G ≅ 𝟭 C)
    (adj : G ⊣ F) : Abelian C := by
  haveI := hasKernels F G i
  haveI := hasCokernels F G i adj
  

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
inst✝⁵ : HasFiniteProducts C
D : Type u₂
inst✝⁴ : Category.{v, u₂} D
inst✝³ : Abelian D
F : C ⥤ D
inst✝² : Functor.PreservesZeroMorphisms F
G : D ⥤ C
inst✝¹ : Functor.PreservesZeroMorphisms G
inst✝ : PreservesFiniteLimits G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
this✝ : HasKernels C
this : HasCokernels C
⊢ Abelian C

Next tactic:
have : ∀ {X Y : C} (f : X ⟶ Y), IsIso (Abelian.coimageImageComparison f) := by
    intro X Y f
    rw [← coimageIsoImage_hom F G i adj f]
    infer_instance
/-- If `C` is an additive category, `D` is an abelian category,
we have `F : C ⥤ D` `G : D ⥤ C` (both preserving zero morphisms),
`G` is left exact (that is, preserves finite limits),
and further we have `adj : G ⊣ F` and `i : F ⋙ G ≅ 𝟭 C`,
then `C` is also abelian.

See <https://stacks.math.columbia.edu/tag/03A3>
-/
def abelianOfAdjunction {C : Type u₁} [Category.{v} C] [Preadditive C] [HasFiniteProducts C]
    {D : Type u₂} [Category.{v} D] [Abelian D] (F : C ⥤ D) [Functor.PreservesZeroMorphisms F]
    (G : D ⥤ C) [Functor.PreservesZeroMorphisms G] [PreservesFiniteLimits G] (i : F ⋙ G ≅ 𝟭 C)
    (adj : G ⊣ F) : Abelian C := by
  haveI := hasKernels F G i
  haveI := hasCokernels F G i adj
  have : ∀ {X Y : C} (f : X ⟶ Y), IsIso (Abelian.coimageImageComparison f) := by
    

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
inst✝⁵ : HasFiniteProducts C
D : Type u₂
inst✝⁴ : Category.{v, u₂} D
inst✝³ : Abelian D
F : C ⥤ D
inst✝² : Functor.PreservesZeroMorphisms F
G : D ⥤ C
inst✝¹ : Functor.PreservesZeroMorphisms G
inst✝ : PreservesFiniteLimits G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
this✝ : HasKernels C
this : HasCokernels C
⊢ ∀ {X Y : C} (f : X ⟶ Y), IsIso (Abelian.coimageImageComparison f)

Next tactic:
intro X Y f
/-- If `C` is an additive category, `D` is an abelian category,
we have `F : C ⥤ D` `G : D ⥤ C` (both preserving zero morphisms),
`G` is left exact (that is, preserves finite limits),
and further we have `adj : G ⊣ F` and `i : F ⋙ G ≅ 𝟭 C`,
then `C` is also abelian.

See <https://stacks.math.columbia.edu/tag/03A3>
-/
def abelianOfAdjunction {C : Type u₁} [Category.{v} C] [Preadditive C] [HasFiniteProducts C]
    {D : Type u₂} [Category.{v} D] [Abelian D] (F : C ⥤ D) [Functor.PreservesZeroMorphisms F]
    (G : D ⥤ C) [Functor.PreservesZeroMorphisms G] [PreservesFiniteLimits G] (i : F ⋙ G ≅ 𝟭 C)
    (adj : G ⊣ F) : Abelian C := by
  haveI := hasKernels F G i
  haveI := hasCokernels F G i adj
  have : ∀ {X Y : C} (f : X ⟶ Y), IsIso (Abelian.coimageImageComparison f) := by
    intro X Y f
    

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
inst✝⁵ : HasFiniteProducts C
D : Type u₂
inst✝⁴ : Category.{v, u₂} D
inst✝³ : Abelian D
F : C ⥤ D
inst✝² : Functor.PreservesZeroMorphisms F
G : D ⥤ C
inst✝¹ : Functor.PreservesZeroMorphisms G
inst✝ : PreservesFiniteLimits G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
this✝ : HasKernels C
this : HasCokernels C
X Y : C
f : X ⟶ Y
⊢ IsIso (Abelian.coimageImageComparison f)

Next tactic:
rw [← coimageIsoImage_hom F G i adj f]
/-- If `C` is an additive category, `D` is an abelian category,
we have `F : C ⥤ D` `G : D ⥤ C` (both preserving zero morphisms),
`G` is left exact (that is, preserves finite limits),
and further we have `adj : G ⊣ F` and `i : F ⋙ G ≅ 𝟭 C`,
then `C` is also abelian.

See <https://stacks.math.columbia.edu/tag/03A3>
-/
def abelianOfAdjunction {C : Type u₁} [Category.{v} C] [Preadditive C] [HasFiniteProducts C]
    {D : Type u₂} [Category.{v} D] [Abelian D] (F : C ⥤ D) [Functor.PreservesZeroMorphisms F]
    (G : D ⥤ C) [Functor.PreservesZeroMorphisms G] [PreservesFiniteLimits G] (i : F ⋙ G ≅ 𝟭 C)
    (adj : G ⊣ F) : Abelian C := by
  haveI := hasKernels F G i
  haveI := hasCokernels F G i adj
  have : ∀ {X Y : C} (f : X ⟶ Y), IsIso (Abelian.coimageImageComparison f) := by
    intro X Y f
    rw [← coimageIsoImage_hom F G i adj f]
    

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
inst✝⁵ : HasFiniteProducts C
D : Type u₂
inst✝⁴ : Category.{v, u₂} D
inst✝³ : Abelian D
F : C ⥤ D
inst✝² : Functor.PreservesZeroMorphisms F
G : D ⥤ C
inst✝¹ : Functor.PreservesZeroMorphisms G
inst✝ : PreservesFiniteLimits G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
this✝ : HasKernels C
this : HasCokernels C
X Y : C
f : X ⟶ Y
⊢ IsIso (coimageIsoImage F G i adj f).hom

Next tactic:
infer_instance
/-- If `C` is an additive category, `D` is an abelian category,
we have `F : C ⥤ D` `G : D ⥤ C` (both preserving zero morphisms),
`G` is left exact (that is, preserves finite limits),
and further we have `adj : G ⊣ F` and `i : F ⋙ G ≅ 𝟭 C`,
then `C` is also abelian.

See <https://stacks.math.columbia.edu/tag/03A3>
-/
def abelianOfAdjunction {C : Type u₁} [Category.{v} C] [Preadditive C] [HasFiniteProducts C]
    {D : Type u₂} [Category.{v} D] [Abelian D] (F : C ⥤ D) [Functor.PreservesZeroMorphisms F]
    (G : D ⥤ C) [Functor.PreservesZeroMorphisms G] [PreservesFiniteLimits G] (i : F ⋙ G ≅ 𝟭 C)
    (adj : G ⊣ F) : Abelian C := by
  haveI := hasKernels F G i
  haveI := hasCokernels F G i adj
  have : ∀ {X Y : C} (f : X ⟶ Y), IsIso (Abelian.coimageImageComparison f) := by
    intro X Y f
    rw [← coimageIsoImage_hom F G i adj f]
    infer_instance
  

Proof state:
C : Type u₁
inst✝⁷ : Category.{v, u₁} C
inst✝⁶ : Preadditive C
inst✝⁵ : HasFiniteProducts C
D : Type u₂
inst✝⁴ : Category.{v, u₂} D
inst✝³ : Abelian D
F : C ⥤ D
inst✝² : Functor.PreservesZeroMorphisms F
G : D ⥤ C
inst✝¹ : Functor.PreservesZeroMorphisms G
inst✝ : PreservesFiniteLimits G
i : F ⋙ G ≅ 𝟭 C
adj : G ⊣ F
this✝¹ : HasKernels C
this✝ : HasCokernels C
this : ∀ {X Y : C} (f : X ⟶ Y), IsIso (Abelian.coimageImageComparison f)
⊢ Abelian C

Next tactic:
apply Abelian.ofCoimageImageComparisonIsIso
protected theorem fix_def {x : α} (h' : ∃ i, (Fix.approx f i x).Dom) :
    Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x := by
  

Proof state:
α : Type u_1
β : α → Type u_2
f : ((a : α) → Part (β a)) → (a : α) → Part (β a)
x : α
h' : ∃ i, (Fix.approx f i x).Dom
⊢ Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x

Next tactic:
let p := fun i : ℕ => (Fix.approx f i x).Dom
protected theorem fix_def {x : α} (h' : ∃ i, (Fix.approx f i x).Dom) :
    Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x := by
  let p := fun i : ℕ => (Fix.approx f i x).Dom
  

Proof state:
α : Type u_1
β : α → Type u_2
f : ((a : α) → Part (β a)) → (a : α) → Part (β a)
x : α
h' : ∃ i, (Fix.approx f i x).Dom
p : ℕ → Prop := fun i => (Fix.approx f i x).Dom
⊢ Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x

Next tactic:
have : p (Nat.find h') := Nat.find_spec h'
protected theorem fix_def {x : α} (h' : ∃ i, (Fix.approx f i x).Dom) :
    Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x := by
  let p := fun i : ℕ => (Fix.approx f i x).Dom
  have : p (Nat.find h') := Nat.find_spec h'
  

Proof state:
α : Type u_1
β : α → Type u_2
f : ((a : α) → Part (β a)) → (a : α) → Part (β a)
x : α
h' : ∃ i, (Fix.approx f i x).Dom
p : ℕ → Prop := fun i => (Fix.approx f i x).Dom
this : p (Nat.find h')
⊢ Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x

Next tactic:
generalize hk : Nat.find h' = k
protected theorem fix_def {x : α} (h' : ∃ i, (Fix.approx f i x).Dom) :
    Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x := by
  let p := fun i : ℕ => (Fix.approx f i x).Dom
  have : p (Nat.find h') := Nat.find_spec h'
  generalize hk : Nat.find h' = k
  

Proof state:
α : Type u_1
β : α → Type u_2
f : ((a : α) → Part (β a)) → (a : α) → Part (β a)
x : α
h' : ∃ i, (Fix.approx f i x).Dom
p : ℕ → Prop := fun i => (Fix.approx f i x).Dom
this : p (Nat.find h')
k : ℕ
hk : Nat.find h' = k
⊢ Part.fix f x = Fix.approx f (Nat.succ k) x

Next tactic:
replace hk : Nat.find h' = k + (@Upto.zero p).val := hk
protected theorem fix_def {x : α} (h' : ∃ i, (Fix.approx f i x).Dom) :
    Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x := by
  let p := fun i : ℕ => (Fix.approx f i x).Dom
  have : p (Nat.find h') := Nat.find_spec h'
  generalize hk : Nat.find h' = k
  replace hk : Nat.find h' = k + (@Upto.zero p).val := hk
  

Proof state:
α : Type u_1
β : α → Type u_2
f : ((a : α) → Part (β a)) → (a : α) → Part (β a)
x : α
h' : ∃ i, (Fix.approx f i x).Dom
p : ℕ → Prop := fun i => (Fix.approx f i x).Dom
this : p (Nat.find h')
k : ℕ
hk : Nat.find h' = k + ↑Upto.zero
⊢ Part.fix f x = Fix.approx f (Nat.succ k) x

Next tactic:
rw [hk] at this
protected theorem fix_def {x : α} (h' : ∃ i, (Fix.approx f i x).Dom) :
    Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x := by
  let p := fun i : ℕ => (Fix.approx f i x).Dom
  have : p (Nat.find h') := Nat.find_spec h'
  generalize hk : Nat.find h' = k
  replace hk : Nat.find h' = k + (@Upto.zero p).val := hk
  rw [hk] at this
  

Proof state:
α : Type u_1
β : α → Type u_2
f : ((a : α) → Part (β a)) → (a : α) → Part (β a)
x : α
h' : ∃ i, (Fix.approx f i x).Dom
p : ℕ → Prop := fun i => (Fix.approx f i x).Dom
k : ℕ
this : p (k + ↑Upto.zero)
hk : Nat.find h' = k + ↑Upto.zero
⊢ Part.fix f x = Fix.approx f (Nat.succ k) x

Next tactic:
revert hk
protected theorem fix_def {x : α} (h' : ∃ i, (Fix.approx f i x).Dom) :
    Part.fix f x = Fix.approx f (Nat.succ (Nat.find h')) x := by
  let p := fun i : ℕ => (Fix.approx f i x).Dom
  have : p (Nat.find h') := Nat.find_spec h'
  generalize hk : Nat.find h' = k
  replace hk : Nat.find h' = k + (@Upto.zero p).val := hk
  rw [hk] at this
  revert hk
  

Proof state:
α : Type u_1
β : α → Type u_2
f : ((a : α) → Part (β a)) → (a : α) → Part (β a)
x : α
h' : ∃ i, (Fix.approx f i x).Dom
p : ℕ → Prop := fun i => (Fix.approx f i x).Dom
k : ℕ
this : p (k + ↑Upto.zero)
⊢ Nat.find h' = k + ↑Upto.zero → Part.fix f x = Fix.approx f (Nat.succ k) x

Next tactic:
dsimp [Part.fix]