l3lab/ntp-mathlib huggingface dataset:
theorem card_Ioc : (Ioc a b).card = (∏ i, (Icc (a i) (b i)).card) - 1 := by
  

Proof state:
ι : Type u_1
α : ι → Type u_2
inst✝⁴ : Fintype ι
inst✝³ : DecidableEq ι
inst✝² : (i : ι) → DecidableEq (α i)
inst✝¹ : (i : ι) → PartialOrder (α i)
inst✝ : (i : ι) → LocallyFiniteOrder (α i)
a b : (i : ι) → α i
⊢ Finset.card (Ioc a b) = ∏ i : ι, Finset.card (Icc (a i) (b i)) - 1

Next tactic:
rw [card_Ioc_eq_card_Icc_sub_one, card_Icc]
theorem card_Ioo : (Ioo a b).card = (∏ i, (Icc (a i) (b i)).card) - 2 := by
  

Proof state:
ι : Type u_1
α : ι → Type u_2
inst✝⁴ : Fintype ι
inst✝³ : DecidableEq ι
inst✝² : (i : ι) → DecidableEq (α i)
inst✝¹ : (i : ι) → PartialOrder (α i)
inst✝ : (i : ι) → LocallyFiniteOrder (α i)
a b : (i : ι) → α i
⊢ Finset.card (Ioo a b) = ∏ i : ι, Finset.card (Icc (a i) (b i)) - 2

Next tactic:
rw [card_Ioo_eq_card_Icc_sub_two, card_Icc]
instance : LocallyFiniteOrderBot (∀ i, α i) :=
  LocallyFiniteOrderTop.ofIic _ (fun b => piFinset fun i => Iic (b i)) fun b x => by
    

Proof state:
ι : Type u_1
α : ι → Type u_2
inst✝⁴ : Fintype ι
inst✝³ : DecidableEq ι
inst✝² : (i : ι) → DecidableEq (α i)
inst✝¹ : (i : ι) → PartialOrder (α i)
inst✝ : (i : ι) → LocallyFiniteOrderBot (α i)
b✝ b x : (i : ι) → α i
⊢ x ∈ (fun b => piFinset fun i => Iic (b i)) b ↔ x ≤ b

Next tactic:
simp_rw [mem_piFinset, mem_Iic, le_def]
theorem card_Iio : (Iio b).card = (∏ i, (Iic (b i)).card) - 1 := by
  

Proof state:
ι : Type u_1
α : ι → Type u_2
inst✝⁴ : Fintype ι
inst✝³ : DecidableEq ι
inst✝² : (i : ι) → DecidableEq (α i)
inst✝¹ : (i : ι) → PartialOrder (α i)
inst✝ : (i : ι) → LocallyFiniteOrderBot (α i)
b : (i : ι) → α i
⊢ Finset.card (Iio b) = ∏ i : ι, Finset.card (Iic (b i)) - 1

Next tactic:
rw [card_Iio_eq_card_Iic_sub_one, card_Iic]
instance : LocallyFiniteOrderTop (∀ i, α i) :=
  LocallyFiniteOrderTop.ofIci _ (fun a => piFinset fun i => Ici (a i)) fun a x => by
    

Proof state:
ι : Type u_1
α : ι → Type u_2
inst✝⁴ : Fintype ι
inst✝³ : DecidableEq ι
inst✝² : (i : ι) → DecidableEq (α i)
inst✝¹ : (i : ι) → PartialOrder (α i)
inst✝ : (i : ι) → LocallyFiniteOrderTop (α i)
a✝ a x : (i : ι) → α i
⊢ x ∈ (fun a => piFinset fun i => Ici (a i)) a ↔ a ≤ x

Next tactic:
simp_rw [mem_piFinset, mem_Ici, le_def]
theorem card_Ioi : (Ioi a).card = (∏ i, (Ici (a i)).card) - 1 := by
  

Proof state:
ι : Type u_1
α : ι → Type u_2
inst✝⁴ : Fintype ι
inst✝³ : DecidableEq ι
inst✝² : (i : ι) → DecidableEq (α i)
inst✝¹ : (i : ι) → PartialOrder (α i)
inst✝ : (i : ι) → LocallyFiniteOrderTop (α i)
a : (i : ι) → α i
⊢ Finset.card (Ioi a) = ∏ i : ι, Finset.card (Ici (a i)) - 1

Next tactic:
rw [card_Ioi_eq_card_Ici_sub_one, card_Ici]
@[to_additive]
instance groupObj (F : J ⥤ GroupCatMax.{v, u}) (j) : Group ((F ⋙ forget GroupCat).obj j) := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCatMax
j : J
⊢ Group ((F ⋙ forget GroupCat).obj j)

Next tactic:
change Group (F.obj j)
@[to_additive]
instance groupObj (F : J ⥤ GroupCatMax.{v, u}) (j) : Group ((F ⋙ forget GroupCat).obj j) := by
  change Group (F.obj j)
  

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCatMax
j : J
⊢ Group ↑(F.obj j)

Next tactic:
infer_instance
/-- The flat sections of a functor into `GroupCat` form a subgroup of all sections.
-/
@[to_additive
  "The flat sections of a functor into `AddGroupCat` form an additive subgroup of all sections."]
def sectionsSubgroup (F : J ⥤ GroupCat) : Subgroup (∀ j, F.obj j) :=
  { MonCat.sectionsSubmonoid (F ⋙ forget₂ GroupCat MonCat) with
    carrier := (F ⋙ forget GroupCat).sections
    inv_mem' := fun {a} ah j j' f => by
      

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCat
src✝ : Submonoid ((j : J) → ↑((F ⋙ forget₂ GroupCat MonCat).obj j)) :=
  MonCat.sectionsSubmonoid (F ⋙ forget₂ GroupCat MonCat)
a : (j : J) → ↑(F.obj j)
ah :
  a ∈
    {
          toSubsemigroup :=
            { carrier := Functor.sections (F ⋙ forget GroupCat),
              mul_mem' :=
                (_ : ∀ {a b : (j : J) → ↑(F.obj j)}, a ∈ src✝.carrier → b ∈ src✝.carrier → a * b ∈ src✝.carrier) },
          one_mem' :=
            (_ :
              ∀ {j j' : J} (f : j ⟶ j'),
                ((F ⋙ forget₂ GroupCat MonCat) ⋙ forget MonCat).map f (OfNat.ofNat 1 j) =
                  OfNat.ofNat 1 j') }.toSubsemigroup.carrier
j j' : J
f : j ⟶ j'
⊢ (F ⋙ forget GroupCat).map f (a⁻¹ j) = a⁻¹ j'

Next tactic:
simp only [Functor.comp_map, Pi.inv_apply, MonoidHom.map_inv, inv_inj]
/-- The flat sections of a functor into `GroupCat` form a subgroup of all sections.
-/
@[to_additive
  "The flat sections of a functor into `AddGroupCat` form an additive subgroup of all sections."]
def sectionsSubgroup (F : J ⥤ GroupCat) : Subgroup (∀ j, F.obj j) :=
  { MonCat.sectionsSubmonoid (F ⋙ forget₂ GroupCat MonCat) with
    carrier := (F ⋙ forget GroupCat).sections
    inv_mem' := fun {a} ah j j' f => by
      simp only [Functor.comp_map, Pi.inv_apply, MonoidHom.map_inv, inv_inj]
      

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCat
src✝ : Submonoid ((j : J) → ↑((F ⋙ forget₂ GroupCat MonCat).obj j)) :=
  MonCat.sectionsSubmonoid (F ⋙ forget₂ GroupCat MonCat)
a : (j : J) → ↑(F.obj j)
ah :
  a ∈
    {
          toSubsemigroup :=
            { carrier := Functor.sections (F ⋙ forget GroupCat),
              mul_mem' :=
                (_ : ∀ {a b : (j : J) → ↑(F.obj j)}, a ∈ src✝.carrier → b ∈ src✝.carrier → a * b ∈ src✝.carrier) },
          one_mem' :=
            (_ :
              ∀ {j j' : J} (f : j ⟶ j'),
                ((F ⋙ forget₂ GroupCat MonCat) ⋙ forget MonCat).map f (OfNat.ofNat 1 j) =
                  OfNat.ofNat 1 j') }.toSubsemigroup.carrier
j j' : J
f : j ⟶ j'
⊢ (forget GroupCat).map (F.map f) (a j)⁻¹ = (a j')⁻¹

Next tactic:
dsimp [Functor.sections] at ah ⊢
/-- The flat sections of a functor into `GroupCat` form a subgroup of all sections.
-/
@[to_additive
  "The flat sections of a functor into `AddGroupCat` form an additive subgroup of all sections."]
def sectionsSubgroup (F : J ⥤ GroupCat) : Subgroup (∀ j, F.obj j) :=
  { MonCat.sectionsSubmonoid (F ⋙ forget₂ GroupCat MonCat) with
    carrier := (F ⋙ forget GroupCat).sections
    inv_mem' := fun {a} ah j j' f => by
      simp only [Functor.comp_map, Pi.inv_apply, MonoidHom.map_inv, inv_inj]
      dsimp [Functor.sections] at ah ⊢
      

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCat
src✝ : Submonoid ((j : J) → ↑((F ⋙ forget₂ GroupCat MonCat).obj j)) :=
  MonCat.sectionsSubmonoid (F ⋙ forget₂ GroupCat MonCat)
a : (j : J) → ↑(F.obj j)
ah : a ∈ {u | ∀ {j j' : J} (f : j ⟶ j'), (F.map f) (u j) = u j'}
j j' : J
f : j ⟶ j'
⊢ (F.map f) (a j)⁻¹ = (a j')⁻¹

Next tactic:
rw [(F.map f).map_inv (a j), ah f]
@[to_additive]
noncomputable instance limitGroup (F : J ⥤ GroupCatMax.{v, u}) :
    Group (Types.limitCone.{v, u} (F ⋙ forget GroupCat)).pt := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCatMax
⊢ Group (Types.limitCone (F ⋙ forget GroupCat)).pt

Next tactic:
change Group (sectionsSubgroup.{v, u} F)
@[to_additive]
noncomputable instance limitGroup (F : J ⥤ GroupCatMax.{v, u}) :
    Group (Types.limitCone.{v, u} (F ⋙ forget GroupCat)).pt := by
  change Group (sectionsSubgroup.{v, u} F)
  

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCatMax
⊢ Group ↥(sectionsSubgroup F)

Next tactic:
infer_instance
/-- We show that the forgetful functor `GroupCat ⥤ MonCat` creates limits.

All we need to do is notice that the limit point has a `Group` instance available, and then reuse
the existing limit. -/
@[to_additive "We show that the forgetful functor `AddGroupCat ⥤ AddMonCat` creates limits.

All we need to do is notice that the limit point has an `AddGroup` instance available, and then
reuse the existing limit."]
noncomputable instance Forget₂.createsLimit (F : J ⥤ GroupCatMax.{v, u}) :
    CreatesLimit F (forget₂ GroupCatMax.{v, u} MonCatMax.{v, u}) :=
  -- Porting note: need to add `forget₂ GrpCat MonCat` reflects isomorphism
  letI : ReflectsIsomorphisms (forget₂ GroupCatMax.{v, u} MonCatMax.{v, u}) :=
    CategoryTheory.reflectsIsomorphisms_forget₂ _ _
  createsLimitOfReflectsIso (K := F) (F := (forget₂ GroupCat.{max v u} MonCat.{max v u}))
    fun c' t =>
    { liftedCone :=
        { pt := GroupCat.of (Types.limitCone (F ⋙ forget GroupCatMax)).pt
          π :=
            { app := MonCat.limitπMonoidHom (F ⋙ forget₂ GroupCatMax MonCatMax)
              naturality :=
                (MonCat.HasLimits.limitCone
                      (F ⋙ forget₂ GroupCat MonCat.{max v u})).π.naturality } }
      validLift := by 

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ GroupCatMax
this : ReflectsIsomorphisms (forget₂ GroupCatMax MonCatMax) := reflectsIsomorphisms_forget₂ GroupCatMax MonCatMax
c' : Cone (F ⋙ forget₂ GroupCat MonCat)
t : IsLimit c'
⊢ (forget₂ GroupCat MonCat).mapCone
      { pt := of (Types.limitCone (F ⋙ forget GroupCatMax)).pt,
        π := NatTrans.mk (MonCat.limitπMonoidHom (F ⋙ forget₂ GroupCatMax MonCatMax)) } ≅
    c'

Next tactic:
apply IsLimit.uniqueUpToIso (MonCat.HasLimits.limitConeIsLimit.{v, u} _) t
@[to_additive]
instance commGroupObj (F : J ⥤ CommGroupCatMax.{v, u}) (j) :
    CommGroup ((F ⋙ forget CommGroupCatMax).obj j) := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ CommGroupCatMax
j : J
⊢ CommGroup ((F ⋙ forget CommGroupCatMax).obj j)

Next tactic:
change CommGroup (F.obj j)
@[to_additive]
instance commGroupObj (F : J ⥤ CommGroupCatMax.{v, u}) (j) :
    CommGroup ((F ⋙ forget CommGroupCatMax).obj j) := by
  change CommGroup (F.obj j)
  

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ CommGroupCatMax
j : J
⊢ CommGroup ↑(F.obj j)

Next tactic:
infer_instance
/-- We show that the forgetful functor `CommGroupCat ⥤ GroupCat` creates limits.

All we need to do is notice that the limit point has a `CommGroup` instance available,
and then reuse the existing limit.
-/
@[to_additive "We show that the forgetful functor `AddCommGroupCat ⥤ AddGroupCat` creates limits.

  All we need to do is notice that the limit point has an `AddCommGroup` instance available,
  and then reuse the existing limit."]
noncomputable instance Forget₂.createsLimit (F : J ⥤ CommGroupCatMax.{v, u}) :
    CreatesLimit F (forget₂ CommGroupCat GroupCatMax.{v, u}) :=
  letI : ReflectsIsomorphisms (forget₂ CommGroupCatMax.{v, u} GroupCatMax.{v, u}) :=
    CategoryTheory.reflectsIsomorphisms_forget₂ _ _
  createsLimitOfReflectsIso fun c' t =>
    { liftedCone :=
        { pt := CommGroupCat.of (Types.limitCone.{v, u} (F ⋙ forget CommGroupCat)).pt
          π :=
            { app := MonCat.limitπMonoidHom
                (F ⋙ forget₂ CommGroupCat GroupCat.{max v u} ⋙ forget₂ GroupCat MonCat.{max v u})
              naturality := (MonCat.HasLimits.limitCone _).π.naturality } }
      validLift := by 

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ CommGroupCatMax
this : ReflectsIsomorphisms (forget₂ CommGroupCatMax GroupCatMax) :=
  reflectsIsomorphisms_forget₂ CommGroupCatMax GroupCatMax
c' : Cone (F ⋙ forget₂ CommGroupCat GroupCatMax)
t : IsLimit c'
⊢ (forget₂ CommGroupCat GroupCatMax).mapCone
      { pt := of (Types.limitCone (F ⋙ forget CommGroupCat)).pt,
        π := NatTrans.mk (MonCat.limitπMonoidHom (F ⋙ forget₂ CommGroupCat GroupCat ⋙ forget₂ GroupCat MonCat)) } ≅
    c'

Next tactic:
apply IsLimit.uniqueUpToIso (GroupCat.limitConeIsLimit _) t
/-- We show that the forgetful functor `CommGroupCat ⥤ GroupCat` creates limits.

All we need to do is notice that the limit point has a `CommGroup` instance available,
and then reuse the existing limit.
-/
@[to_additive "We show that the forgetful functor `AddCommGroupCat ⥤ AddGroupCat` creates limits.

  All we need to do is notice that the limit point has an `AddCommGroup` instance available,
  and then reuse the existing limit."]
noncomputable instance Forget₂.createsLimit (F : J ⥤ CommGroupCatMax.{v, u}) :
    CreatesLimit F (forget₂ CommGroupCat GroupCatMax.{v, u}) :=
  letI : ReflectsIsomorphisms (forget₂ CommGroupCatMax.{v, u} GroupCatMax.{v, u}) :=
    CategoryTheory.reflectsIsomorphisms_forget₂ _ _
  createsLimitOfReflectsIso fun c' t =>
    { liftedCone :=
        { pt := CommGroupCat.of (Types.limitCone.{v, u} (F ⋙ forget CommGroupCat)).pt
          π :=
            { app := MonCat.limitπMonoidHom
                (F ⋙ forget₂ CommGroupCat GroupCat.{max v u} ⋙ forget₂ GroupCat MonCat.{max v u})
              naturality := (MonCat.HasLimits.limitCone _).π.naturality } }
      validLift := by apply IsLimit.uniqueUpToIso (GroupCat.limitConeIsLimit _) t
      makesLimit :=
        IsLimit.ofFaithful (forget₂ _ GroupCat.{max v u} ⋙ forget₂ _ MonCat.{max v u})
          (by 

Proof state:
J : Type v
inst✝ : SmallCategory J
F : J ⥤ CommGroupCatMax
this : ReflectsIsomorphisms (forget₂ CommGroupCatMax GroupCatMax) :=
  reflectsIsomorphisms_forget₂ CommGroupCatMax GroupCatMax
c' : Cone (F ⋙ forget₂ CommGroupCat GroupCatMax)
t : IsLimit c'
⊢ IsLimit
    ((forget₂ CommGroupCatMax GroupCat ⋙ forget₂ GroupCat MonCat).mapCone
      {
          liftedCone :=
            { pt := of (Types.limitCone (F ⋙ forget CommGroupCat)).pt,
              π := NatTrans.mk (MonCat.limitπMonoidHom (F ⋙ forget₂ CommGroupCat GroupCat ⋙ forget₂ GroupCat MonCat)) },
          validLift :=
            IsLimit.uniqueUpToIso (GroupCat.limitConeIsLimit (F ⋙ forget₂ CommGroupCat GroupCatMax)) t }.liftedCone)

Next tactic:
apply MonCat.HasLimits.limitConeIsLimit _
/-- The forgetful functor from commutative groups to groups preserves all limits.
(That is, the underlying group could have been computed instead as limits in the category
of groups.)
-/
@[to_additive
  "The forgetful functor from additive commutative groups to groups preserves all limits.
  (That is, the underlying group could have been computed instead as limits in the category
    of additive groups.)"]
noncomputable instance forget₂GroupPreservesLimitsOfSize :
    PreservesLimitsOfSize.{v, v} (forget₂ CommGroupCatMax.{v, u} GroupCatMax.{v, u}) where
  preservesLimitsOfShape {J 𝒥} := { preservesLimit := fun {F} => by 

Proof state:
J✝ : Type v
inst✝ : SmallCategory J✝
J : Type v
𝒥 : Category.{v, v} J
F : J ⥤ CommGroupCatMax
⊢ PreservesLimit F (forget₂ CommGroupCatMax GroupCatMax)

Next tactic:
infer_instance
example (f : ℕ → AddCommGroupCat) : HasProduct f := by 

Proof state:
J : Type v
inst✝ : SmallCategory J
f : ℕ → AddCommGroupCat
⊢ HasProduct f

Next tactic:
infer_instance
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
⊢ (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0

Next tactic:
refine Subtype.ext ?_
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
⊢ ↑((fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0) = ↑0

Next tactic:
simp [(AddSubgroup.coe_zero _).symm]
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        simp [(AddSubgroup.coe_zero _).symm]
      map_add' := fun g g' => by
        

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
g g' : ↑(kernel f)
⊢ ZeroHom.toFun
      { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
        map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
      (g + g') =
    ZeroHom.toFun
        { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
          map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
        g +
      ZeroHom.toFun
        { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
          map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
        g'

Next tactic:
refine Subtype.ext ?_
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        simp [(AddSubgroup.coe_zero _).symm]
      map_add' := fun g g' => by
        refine Subtype.ext ?_
        

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
g g' : ↑(kernel f)
⊢ ↑(ZeroHom.toFun
        { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
          map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
        (g + g')) =
    ↑(ZeroHom.toFun
          { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
            map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
          g +
        ZeroHom.toFun
          { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
            map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
          g')

Next tactic:
change _ = _ + _
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        simp [(AddSubgroup.coe_zero _).symm]
      map_add' := fun g g' => by
        refine Subtype.ext ?_
        change _ = _ + _
        

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
g g' : ↑(kernel f)
⊢ ↑(ZeroHom.toFun
        { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
          map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
        (g + g')) =
    ↑(ZeroHom.toFun
          { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
            map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
          g) +
      ↑(ZeroHom.toFun
          { toFun := fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) },
            map_zero' := (_ : (fun g => { val := (kernel.ι f) g, property := (_ : (kernel.ι f ≫ f) g = 0 g) }) 0 = 0) }
          g')

Next tactic:
dsimp
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        simp [(AddSubgroup.coe_zero _).symm]
      map_add' := fun g g' => by
        refine Subtype.ext ?_
        change _ = _ + _
        dsimp
        

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
g g' : ↑(kernel f)
⊢ (kernel.ι f) (g + g') = (kernel.ι f) g + (kernel.ι f) g'

Next tactic:
simp
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        simp [(AddSubgroup.coe_zero _).symm]
      map_add' := fun g g' => by
        refine Subtype.ext ?_
        change _ = _ + _
        dsimp
        simp }
  inv := kernel.lift f (AddSubgroup.subtype f.ker) <| by
    -- porting note : used to be `tidy`, but `aesop` can't do it
    

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
⊢ AddSubgroup.subtype (AddMonoidHom.ker f) ≫ f = 0

Next tactic:
refine FunLike.ext _ _ ?_
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        simp [(AddSubgroup.coe_zero _).symm]
      map_add' := fun g g' => by
        refine Subtype.ext ?_
        change _ = _ + _
        dsimp
        simp }
  inv := kernel.lift f (AddSubgroup.subtype f.ker) <| by
    -- porting note : used to be `tidy`, but `aesop` can't do it
    refine FunLike.ext _ _ ?_
    

Proof state:
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
⊢ ∀ (x : ↑(of ↥(AddMonoidHom.ker f))), (AddSubgroup.subtype (AddMonoidHom.ker f) ≫ f) x = 0 x

Next tactic:
rintro ⟨x, (hx : f _ = 0)⟩
/-- The categorical kernel of a morphism in `AddCommGroupCat`
agrees with the usual group-theoretical kernel.
-/
def kernelIsoKer {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    kernel f ≅ AddCommGroupCat.of f.ker where
  hom :=
    { toFun := fun g => ⟨kernel.ι f g, FunLike.congr_fun (kernel.condition f) g⟩
      map_zero' := by
        refine Subtype.ext ?_
        simp [(AddSubgroup.coe_zero _).symm]
      map_add' := fun g g' => by
        refine Subtype.ext ?_
        change _ = _ + _
        dsimp
        simp }
  inv := kernel.lift f (AddSubgroup.subtype f.ker) <| by
    -- porting note : used to be `tidy`, but `aesop` can't do it
    refine FunLike.ext _ _ ?_
    rintro ⟨x, (hx : f _ = 0)⟩
    

Proof state:
case mk
J : Type v
inst✝ : SmallCategory J
G H : AddCommGroupCat
f : G ⟶ H
x : ↑G
hx : f x = 0
⊢ (AddSubgroup.subtype (AddMonoidHom.ker f) ≫ f) { val := x, property := hx } = 0 { val := x, property := hx }

Next tactic:
exact hx