l3lab/ntp-mathlib huggingface dataset:
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
⊢ g₁ = g₂

Next tactic:
ext U ⟨s, hx⟩
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  

Proof state:
case h.w.h.h.mk
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
⊢ g₁.val.app U { val := s, property := hx } = g₂.val.app U { val := s, property := hx }

Next tactic:
apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  

Proof state:
case h.w.h.h.mk
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
⊢ ∀ ⦃Y : C⦄ ⦃f_1 : Y ⟶ U.unop⦄,
    (Subpresheaf.sieveOfSection (imagePresheaf f.val) s).arrows f_1 →
      G'.val.map f_1.op (g₁.val.app U { val := s, property := hx }) =
        G'.val.map f_1.op (g₂.val.app U { val := s, property := hx })

Next tactic:
rintro V i ⟨y, e'⟩
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  

Proof state:
case h.w.h.h.mk.intro
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
⊢ G'.val.map i.op (g₁.val.app U { val := s, property := hx }) =
    G'.val.map i.op (g₂.val.app U { val := s, property := hx })

Next tactic:
change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
  

Proof state:
case h.w.h.h.mk.intro
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
⊢ (g₁.val.app (op U.unop) ≫ G'.val.map i.op) { val := s, property := hx } =
    (g₂.val.app (op U.unop) ≫ G'.val.map i.op) { val := s, property := hx }

Next tactic:
rw [← NatTrans.naturality, ← NatTrans.naturality]
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
  rw [← NatTrans.naturality, ← NatTrans.naturality]
  

Proof state:
case h.w.h.h.mk.intro
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
⊢ ((imageSheaf f).val.map i.op ≫ g₁.val.app (op V)) { val := s, property := hx } =
    ((imageSheaf f).val.map i.op ≫ g₂.val.app (op V)) { val := s, property := hx }

Next tactic:
have E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op ⟨s, hx⟩ :=
    Subtype.ext e'
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
  rw [← NatTrans.naturality, ← NatTrans.naturality]
  have E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op ⟨s, hx⟩ :=
    Subtype.ext e'
  

Proof state:
case h.w.h.h.mk.intro
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op { val := s, property := hx }
⊢ ((imageSheaf f).val.map i.op ≫ g₁.val.app (op V)) { val := s, property := hx } =
    ((imageSheaf f).val.map i.op ≫ g₂.val.app (op V)) { val := s, property := hx }

Next tactic:
have := congr_arg (fun f : F ⟶ G' => (Sheaf.Hom.val f).app _ y) e
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
  rw [← NatTrans.naturality, ← NatTrans.naturality]
  have E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op ⟨s, hx⟩ :=
    Subtype.ext e'
  have := congr_arg (fun f : F ⟶ G' => (Sheaf.Hom.val f).app _ y) e
  

Proof state:
case h.w.h.h.mk.intro
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op { val := s, property := hx }
this : (fun f => f.val.app (op V) y) (toImageSheaf f ≫ g₁) = (fun f => f.val.app (op V) y) (toImageSheaf f ≫ g₂)
⊢ ((imageSheaf f).val.map i.op ≫ g₁.val.app (op V)) { val := s, property := hx } =
    ((imageSheaf f).val.map i.op ≫ g₂.val.app (op V)) { val := s, property := hx }

Next tactic:
dsimp at this ⊢
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
  rw [← NatTrans.naturality, ← NatTrans.naturality]
  have E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op ⟨s, hx⟩ :=
    Subtype.ext e'
  have := congr_arg (fun f : F ⟶ G' => (Sheaf.Hom.val f).app _ y) e
  dsimp at this ⊢
  

Proof state:
case h.w.h.h.mk.intro
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op { val := s, property := hx }
this :
  g₁.val.app (op V) ((toImagePresheafSheafify J f.val).app (op V) y) =
    g₂.val.app (op V) ((toImagePresheafSheafify J f.val).app (op V) y)
⊢ g₁.val.app (op V)
      ((Subpresheaf.toPresheaf (Subpresheaf.sheafify J (imagePresheaf f.val))).map i.op { val := s, property := hx }) =
    g₂.val.app (op V)
      ((Subpresheaf.toPresheaf (Subpresheaf.sheafify J (imagePresheaf f.val))).map i.op { val := s, property := hx })

Next tactic:
convert this
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
  rw [← NatTrans.naturality, ← NatTrans.naturality]
  have E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op ⟨s, hx⟩ :=
    Subtype.ext e'
  have := congr_arg (fun f : F ⟶ G' => (Sheaf.Hom.val f).app _ y) e
  dsimp at this ⊢
  convert this <;> 

Proof state:
case h.e'_2.h.e'_5
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op { val := s, property := hx }
this :
  g₁.val.app (op V) ((toImagePresheafSheafify J f.val).app (op V) y) =
    g₂.val.app (op V) ((toImagePresheafSheafify J f.val).app (op V) y)
⊢ (Subpresheaf.toPresheaf (Subpresheaf.sheafify J (imagePresheaf f.val))).map i.op { val := s, property := hx } =
    (toImagePresheafSheafify J f.val).app (op V) y

Next tactic:
exact E.symm
instance {F F' : Sheaf J (Type w)} (f : F ⟶ F') : Epi (toImageSheaf f) := by
  refine' ⟨@fun G' g₁ g₂ e => _⟩
  ext U ⟨s, hx⟩
  apply ((isSheaf_iff_isSheaf_of_type J _).mp G'.2 _ hx).isSeparatedFor.ext
  rintro V i ⟨y, e'⟩
  change (g₁.val.app _ ≫ G'.val.map _) _ = (g₂.val.app _ ≫ G'.val.map _) _
  rw [← NatTrans.naturality, ← NatTrans.naturality]
  have E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op ⟨s, hx⟩ :=
    Subtype.ext e'
  have := congr_arg (fun f : F ⟶ G' => (Sheaf.Hom.val f).app _ y) e
  dsimp at this ⊢
  convert this <;> 

Proof state:
case h.e'_3.h.e'_5
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G'✝ : Subpresheaf F✝
F F' : Sheaf J (Type w)
f : F ⟶ F'
G' : Sheaf J (Type w)
g₁ g₂ : imageSheaf f ⟶ G'
e : toImageSheaf f ≫ g₁ = toImageSheaf f ≫ g₂
U : Cᵒᵖ
s : F'.val.obj U
hx : s ∈ Subpresheaf.obj (Subpresheaf.sheafify J (imagePresheaf f.val)) U
V : C
i : V ⟶ U.unop
y : F.val.obj (op V)
e' : f.val.app (op V) y = F'.val.map i.op s
E : (toImageSheaf f).val.app (op V) y = (imageSheaf f).val.map i.op { val := s, property := hx }
this :
  g₁.val.app (op V) ((toImagePresheafSheafify J f.val).app (op V) y) =
    g₂.val.app (op V) ((toImagePresheafSheafify J f.val).app (op V) y)
⊢ (Subpresheaf.toPresheaf (Subpresheaf.sheafify J (imagePresheaf f.val))).map i.op { val := s, property := hx } =
    (toImagePresheafSheafify J f.val).app (op V) y

Next tactic:
exact E.symm
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
⊢ (imageMonoFactorization f).I ⟶ I.I

Next tactic:
haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
⊢ (imageMonoFactorization f).I ⟶ I.I

Next tactic:
haveI := isIso_toImagePresheaf I.m.1
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ (imageMonoFactorization f).I ⟶ I.I

Next tactic:
refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ Subpresheaf.sheafify J (imagePresheaf f.val) ≤ imagePresheaf I.m.val

Next tactic:
apply Subpresheaf.sheafify_le
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · 

Proof state:
case h
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ imagePresheaf f.val ≤ imagePresheaf I.m.val

Next tactic:
conv_lhs => rw [← I.fac]
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · conv_lhs => 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
| imagePresheaf f.val

Next tactic:
rw [← I.fac]
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · conv_lhs => rw [← I.fac]
          

Proof state:
case h
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ imagePresheaf (I.e ≫ I.m).val ≤ imagePresheaf I.m.val

Next tactic:
apply imagePresheaf_comp_le
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · conv_lhs => rw [← I.fac]
          apply imagePresheaf_comp_le
        · 

Proof state:
case hF
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ Presieve.IsSheaf J F'.val

Next tactic:
rw [← isSheaf_iff_isSheaf_of_type]
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · conv_lhs => rw [← I.fac]
          apply imagePresheaf_comp_le
        · rw [← isSheaf_iff_isSheaf_of_type]
          

Proof state:
case hF
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ Presheaf.IsSheaf J F'.val

Next tactic:
exact F'.2
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · conv_lhs => rw [← I.fac]
          apply imagePresheaf_comp_le
        · rw [← isSheaf_iff_isSheaf_of_type]
          exact F'.2
        · 

Proof state:
case hG'
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ Presieve.IsSheaf J (Subpresheaf.toPresheaf (imagePresheaf I.m.val))

Next tactic:
apply Presieve.isSheaf_iso J (asIso <| toImagePresheaf I.m.1)
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · conv_lhs => rw [← I.fac]
          apply imagePresheaf_comp_le
        · rw [← isSheaf_iff_isSheaf_of_type]
          exact F'.2
        · apply Presieve.isSheaf_iso J (asIso <| toImagePresheaf I.m.1)
          

Proof state:
case hG'
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ Presieve.IsSheaf J I.I.val

Next tactic:
rw [← isSheaf_iff_isSheaf_of_type]
/-- The mono factorization given by `image_sheaf` for a morphism is an image. -/
noncomputable def imageFactorization {F F' : Sheaf J TypeMax.{v, u}} (f : F ⟶ F') :
    Limits.ImageFactorisation f where
  F := imageMonoFactorization f
  isImage :=
    { lift := fun I => by
        -- port note: need to specify the target category (TypeMax.{v, u}) for this to work.
        haveI M := (Sheaf.Hom.mono_iff_presheaf_mono J TypeMax.{v, u} _).mp I.m_mono
        haveI := isIso_toImagePresheaf I.m.1
        refine' ⟨Subpresheaf.homOfLe _ ≫ inv (toImagePresheaf I.m.1)⟩
        apply Subpresheaf.sheafify_le
        · conv_lhs => rw [← I.fac]
          apply imagePresheaf_comp_le
        · rw [← isSheaf_iff_isSheaf_of_type]
          exact F'.2
        · apply Presieve.isSheaf_iso J (asIso <| toImagePresheaf I.m.1)
          rw [← isSheaf_iff_isSheaf_of_type]
          

Proof state:
case hG'
C : Type u
inst✝ : Category.{v, u} C
J : GrothendieckTopology C
F✝ F'✝ F'' : Cᵒᵖ ⥤ Type w
G G' : Subpresheaf F✝
F F' : Sheaf J TypeMax
f : F ⟶ F'
I : Limits.MonoFactorisation f
M : Mono I.m.val
this : IsIso (toImagePresheaf I.m.val)
⊢ Presheaf.IsSheaf J I.I.val

Next tactic:
exact I.I.2