l3lab/ntp-mathlib huggingface dataset:
theorem stalkFunctor_map_injective_of_app_injective {F G : Presheaf C X} (f : F ⟶ G)
    (h : ∀ U : Opens X, Function.Injective (f.app (op U))) (x : X) :
    Function.Injective ((stalkFunctor C x).map f) := fun s t hst => by
  rcases germ_exist F x s with ⟨U₁, hxU₁, s, rfl⟩
  rcases germ_exist F x t with ⟨U₂, hxU₂, t, rfl⟩
  

Proof state:
case intro.intro.intro.intro.intro.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  ((stalkFunctor C x).map f) ((germ F { val := x, property := hxU₁ }) s) =
    ((stalkFunctor C x).map f) ((germ F { val := x, property := hxU₂ }) t)
⊢ (germ F { val := x, property := hxU₁ }) s = (germ F { val := x, property := hxU₂ }) t

Next tactic:
erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
theorem stalkFunctor_map_injective_of_app_injective {F G : Presheaf C X} (f : F ⟶ G)
    (h : ∀ U : Opens X, Function.Injective (f.app (op U))) (x : X) :
    Function.Injective ((stalkFunctor C x).map f) := fun s t hst => by
  rcases germ_exist F x s with ⟨U₁, hxU₁, s, rfl⟩
  rcases germ_exist F x t with ⟨U₂, hxU₂, t, rfl⟩
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  

Proof state:
case intro.intro.intro.intro.intro.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₁ }).op ⋙ G)
        (op { obj := U₁, property := (_ : ↑{ val := x, property := hxU₁ } ∈ U₁) }))
      ((f.app (op U₁)) s) =
    ((stalkFunctor C x).map f) ((germ F { val := x, property := hxU₂ }) t)
⊢ (germ F { val := x, property := hxU₁ }) s = (germ F { val := x, property := hxU₂ }) t

Next tactic:
erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
theorem stalkFunctor_map_injective_of_app_injective {F G : Presheaf C X} (f : F ⟶ G)
    (h : ∀ U : Opens X, Function.Injective (f.app (op U))) (x : X) :
    Function.Injective ((stalkFunctor C x).map f) := fun s t hst => by
  rcases germ_exist F x s with ⟨U₁, hxU₁, s, rfl⟩
  rcases germ_exist F x t with ⟨U₂, hxU₂, t, rfl⟩
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  

Proof state:
case intro.intro.intro.intro.intro.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₁ }).op ⋙ G)
        (op { obj := U₁, property := (_ : ↑{ val := x, property := hxU₁ } ∈ U₁) }))
      ((f.app (op U₁)) s) =
    (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₂ }).op ⋙ G)
        (op { obj := U₂, property := (_ : ↑{ val := x, property := hxU₂ } ∈ U₂) }))
      ((f.app (op U₂)) t)
⊢ (germ F { val := x, property := hxU₁ }) s = (germ F { val := x, property := hxU₂ }) t

Next tactic:
obtain ⟨W, hxW, iWU₁, iWU₂, heq⟩ := G.germ_eq x hxU₁ hxU₂ _ _ hst
theorem stalkFunctor_map_injective_of_app_injective {F G : Presheaf C X} (f : F ⟶ G)
    (h : ∀ U : Opens X, Function.Injective (f.app (op U))) (x : X) :
    Function.Injective ((stalkFunctor C x).map f) := fun s t hst => by
  rcases germ_exist F x s with ⟨U₁, hxU₁, s, rfl⟩
  rcases germ_exist F x t with ⟨U₂, hxU₂, t, rfl⟩
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  obtain ⟨W, hxW, iWU₁, iWU₂, heq⟩ := G.germ_eq x hxU₁ hxU₂ _ _ hst
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.intro.intro.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₁ }).op ⋙ G)
        (op { obj := U₁, property := (_ : ↑{ val := x, property := hxU₁ } ∈ U₁) }))
      ((f.app (op U₁)) s) =
    (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₂ }).op ⋙ G)
        (op { obj := U₂, property := (_ : ↑{ val := x, property := hxU₂ } ∈ U₂) }))
      ((f.app (op U₂)) t)
W : Opens ↑X
hxW : x ∈ W
iWU₁ : W ⟶ U₁
iWU₂ : W ⟶ U₂
heq : (G.map iWU₁.op) ((f.app (op U₁)) s) = (G.map iWU₂.op) ((f.app (op U₂)) t)
⊢ (germ F { val := x, property := hxU₁ }) s = (germ F { val := x, property := hxU₂ }) t

Next tactic:
rw [← comp_apply, ← comp_apply, ← f.naturality, ← f.naturality, comp_apply, comp_apply] at heq
theorem stalkFunctor_map_injective_of_app_injective {F G : Presheaf C X} (f : F ⟶ G)
    (h : ∀ U : Opens X, Function.Injective (f.app (op U))) (x : X) :
    Function.Injective ((stalkFunctor C x).map f) := fun s t hst => by
  rcases germ_exist F x s with ⟨U₁, hxU₁, s, rfl⟩
  rcases germ_exist F x t with ⟨U₂, hxU₂, t, rfl⟩
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  obtain ⟨W, hxW, iWU₁, iWU₂, heq⟩ := G.germ_eq x hxU₁ hxU₂ _ _ hst
  rw [← comp_apply, ← comp_apply, ← f.naturality, ← f.naturality, comp_apply, comp_apply] at heq
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.intro.intro.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₁ }).op ⋙ G)
        (op { obj := U₁, property := (_ : ↑{ val := x, property := hxU₁ } ∈ U₁) }))
      ((f.app (op U₁)) s) =
    (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₂ }).op ⋙ G)
        (op { obj := U₂, property := (_ : ↑{ val := x, property := hxU₂ } ∈ U₂) }))
      ((f.app (op U₂)) t)
W : Opens ↑X
hxW : x ∈ W
iWU₁ : W ⟶ U₁
iWU₂ : W ⟶ U₂
heq : (f.app (op W)) ((F.map iWU₁.op) s) = (f.app (op W)) ((F.map iWU₂.op) t)
⊢ (germ F { val := x, property := hxU₁ }) s = (germ F { val := x, property := hxU₂ }) t

Next tactic:
replace heq := h W heq
theorem stalkFunctor_map_injective_of_app_injective {F G : Presheaf C X} (f : F ⟶ G)
    (h : ∀ U : Opens X, Function.Injective (f.app (op U))) (x : X) :
    Function.Injective ((stalkFunctor C x).map f) := fun s t hst => by
  rcases germ_exist F x s with ⟨U₁, hxU₁, s, rfl⟩
  rcases germ_exist F x t with ⟨U₂, hxU₂, t, rfl⟩
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  obtain ⟨W, hxW, iWU₁, iWU₂, heq⟩ := G.germ_eq x hxU₁ hxU₂ _ _ hst
  rw [← comp_apply, ← comp_apply, ← f.naturality, ← f.naturality, comp_apply, comp_apply] at heq
  replace heq := h W heq
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.intro.intro.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₁ }).op ⋙ G)
        (op { obj := U₁, property := (_ : ↑{ val := x, property := hxU₁ } ∈ U₁) }))
      ((f.app (op U₁)) s) =
    (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₂ }).op ⋙ G)
        (op { obj := U₂, property := (_ : ↑{ val := x, property := hxU₂ } ∈ U₂) }))
      ((f.app (op U₂)) t)
W : Opens ↑X
hxW : x ∈ W
iWU₁ : W ⟶ U₁
iWU₂ : W ⟶ U₂
heq : (F.map iWU₁.op) s = (F.map iWU₂.op) t
⊢ (germ F { val := x, property := hxU₁ }) s = (germ F { val := x, property := hxU₂ }) t

Next tactic:
convert congr_arg (F.germ ⟨x, hxW⟩) heq using 1
theorem stalkFunctor_map_injective_of_app_injective {F G : Presheaf C X} (f : F ⟶ G)
    (h : ∀ U : Opens X, Function.Injective (f.app (op U))) (x : X) :
    Function.Injective ((stalkFunctor C x).map f) := fun s t hst => by
  rcases germ_exist F x s with ⟨U₁, hxU₁, s, rfl⟩
  rcases germ_exist F x t with ⟨U₂, hxU₂, t, rfl⟩
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  erw [stalkFunctor_map_germ_apply _ ⟨x, _⟩] at hst
  obtain ⟨W, hxW, iWU₁, iWU₂, heq⟩ := G.germ_eq x hxU₁ hxU₂ _ _ hst
  rw [← comp_apply, ← comp_apply, ← f.naturality, ← f.naturality, comp_apply, comp_apply] at heq
  replace heq := h W heq
  convert congr_arg (F.germ ⟨x, hxW⟩) heq using 1
  

Proof state:
case h.e'_2.h
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₁ }).op ⋙ G)
        (op { obj := U₁, property := (_ : ↑{ val := x, property := hxU₁ } ∈ U₁) }))
      ((f.app (op U₁)) s) =
    (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₂ }).op ⋙ G)
        (op { obj := U₂, property := (_ : ↑{ val := x, property := hxU₂ } ∈ U₂) }))
      ((f.app (op U₂)) t)
W : Opens ↑X
hxW : x ∈ W
iWU₁ : W ⟶ U₁
iWU₂ : W ⟶ U₂
heq : (F.map iWU₁.op) s = (F.map iWU₂.op) t
e_1✝ : (forget C).obj ((stalkFunctor C x).obj F) = (forget C).obj (stalk F ↑{ val := x, property := hxW })
⊢ (germ F { val := x, property := hxU₁ }) s = (germ F { val := x, property := hxW }) ((F.map iWU₁.op) s)
case h.e'_3.h
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : HasColimits C
X Y Z : TopCat
inst✝¹ : ConcreteCategory C
inst✝ : PreservesFilteredColimits (forget C)
F G : Presheaf C X
f : F ⟶ G
h : ∀ (U : Opens ↑X), Function.Injective ⇑(f.app (op U))
x : ↑X
U₁ : Opens ↑X
hxU₁ : x ∈ U₁
s : (forget C).obj (F.obj (op U₁))
U₂ : Opens ↑X
hxU₂ : x ∈ U₂
t : (forget C).obj (F.obj (op U₂))
hst :
  (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₁ }).op ⋙ G)
        (op { obj := U₁, property := (_ : ↑{ val := x, property := hxU₁ } ∈ U₁) }))
      ((f.app (op U₁)) s) =
    (colimit.ι ((OpenNhds.inclusion ↑{ val := x, property := hxU₂ }).op ⋙ G)
        (op { obj := U₂, property := (_ : ↑{ val := x, property := hxU₂ } ∈ U₂) }))
      ((f.app (op U₂)) t)
W : Opens ↑X
hxW : x ∈ W
iWU₁ : W ⟶ U₁
iWU₂ : W ⟶ U₂
heq : (F.map iWU₁.op) s = (F.map iWU₂.op) t
e_1✝ : (forget C).obj ((stalkFunctor C x).obj F) = (forget C).obj (stalk F ↑{ val := x, property := hxW })
⊢ (germ F { val := x, property := hxU₂ }) t = (germ F { val := x, property := hxW }) ((F.map iWU₂.op) t)

Next tactic:
exacts [(F.germ_res_apply iWU₁ ⟨x, hxW⟩ s).symm, (F.germ_res_apply iWU₂ ⟨x, hxW⟩ t).symm]
/-- Let `F` be a sheaf valued in a concrete category, whose forgetful functor reflects isomorphisms,
preserves limits and filtered colimits. Then two sections who agree on every stalk must be equal.
-/
theorem section_ext (F : Sheaf C X) (U : Opens X) (s t : F.1.obj (op U))
    (h : ∀ x : U, F.presheaf.germ x s = F.presheaf.germ x t) : s = t := by
  -- We use `germ_eq` and the axiom of choice, to pick for every point `x` a neighbourhood
  -- `V x`, such that the restrictions of `s` and `t` to `V x` coincide.
  

Proof state:
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
U : Opens ↑X
s t : (forget C).obj (F.val.obj (op U))
h : ∀ (x : ↥U), (germ (Sheaf.presheaf F) x) s = (germ (Sheaf.presheaf F) x) t
⊢ s = t

Next tactic:
choose V m i₁ i₂ heq using fun x : U => F.presheaf.germ_eq x.1 x.2 x.2 s t (h x)
/-- Let `F` be a sheaf valued in a concrete category, whose forgetful functor reflects isomorphisms,
preserves limits and filtered colimits. Then two sections who agree on every stalk must be equal.
-/
theorem section_ext (F : Sheaf C X) (U : Opens X) (s t : F.1.obj (op U))
    (h : ∀ x : U, F.presheaf.germ x s = F.presheaf.germ x t) : s = t := by
  -- We use `germ_eq` and the axiom of choice, to pick for every point `x` a neighbourhood
  -- `V x`, such that the restrictions of `s` and `t` to `V x` coincide.
  choose V m i₁ i₂ heq using fun x : U => F.presheaf.germ_eq x.1 x.2 x.2 s t (h x)
  -- Since `F` is a sheaf, we can prove the equality locally, if we can show that these
  -- neighborhoods form a cover of `U`.
  

Proof state:
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
U : Opens ↑X
s t : (forget C).obj (F.val.obj (op U))
h : ∀ (x : ↥U), (germ (Sheaf.presheaf F) x) s = (germ (Sheaf.presheaf F) x) t
V : ↥U → Opens ↑X
m : ∀ (x : ↥U), ↑x ∈ V x
i₁ i₂ : (x : ↥U) → V x ⟶ U
heq : ∀ (x : ↥U), ((Sheaf.presheaf F).map (i₁ x).op) s = ((Sheaf.presheaf F).map (i₂ x).op) t
⊢ s = t

Next tactic:
apply F.eq_of_locally_eq' V U i₁
/-- Let `F` be a sheaf valued in a concrete category, whose forgetful functor reflects isomorphisms,
preserves limits and filtered colimits. Then two sections who agree on every stalk must be equal.
-/
theorem section_ext (F : Sheaf C X) (U : Opens X) (s t : F.1.obj (op U))
    (h : ∀ x : U, F.presheaf.germ x s = F.presheaf.germ x t) : s = t := by
  -- We use `germ_eq` and the axiom of choice, to pick for every point `x` a neighbourhood
  -- `V x`, such that the restrictions of `s` and `t` to `V x` coincide.
  choose V m i₁ i₂ heq using fun x : U => F.presheaf.germ_eq x.1 x.2 x.2 s t (h x)
  -- Since `F` is a sheaf, we can prove the equality locally, if we can show that these
  -- neighborhoods form a cover of `U`.
  apply F.eq_of_locally_eq' V U i₁
  · 

Proof state:
case hcover
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
U : Opens ↑X
s t : (forget C).obj (F.val.obj (op U))
h : ∀ (x : ↥U), (germ (Sheaf.presheaf F) x) s = (germ (Sheaf.presheaf F) x) t
V : ↥U → Opens ↑X
m : ∀ (x : ↥U), ↑x ∈ V x
i₁ i₂ : (x : ↥U) → V x ⟶ U
heq : ∀ (x : ↥U), ((Sheaf.presheaf F).map (i₁ x).op) s = ((Sheaf.presheaf F).map (i₂ x).op) t
⊢ U ≤ iSup V

Next tactic:
intro x hxU
/-- Let `F` be a sheaf valued in a concrete category, whose forgetful functor reflects isomorphisms,
preserves limits and filtered colimits. Then two sections who agree on every stalk must be equal.
-/
theorem section_ext (F : Sheaf C X) (U : Opens X) (s t : F.1.obj (op U))
    (h : ∀ x : U, F.presheaf.germ x s = F.presheaf.germ x t) : s = t := by
  -- We use `germ_eq` and the axiom of choice, to pick for every point `x` a neighbourhood
  -- `V x`, such that the restrictions of `s` and `t` to `V x` coincide.
  choose V m i₁ i₂ heq using fun x : U => F.presheaf.germ_eq x.1 x.2 x.2 s t (h x)
  -- Since `F` is a sheaf, we can prove the equality locally, if we can show that these
  -- neighborhoods form a cover of `U`.
  apply F.eq_of_locally_eq' V U i₁
  · intro x hxU
    

Proof state:
case hcover
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
U : Opens ↑X
s t : (forget C).obj (F.val.obj (op U))
h : ∀ (x : ↥U), (germ (Sheaf.presheaf F) x) s = (germ (Sheaf.presheaf F) x) t
V : ↥U → Opens ↑X
m : ∀ (x : ↥U), ↑x ∈ V x
i₁ i₂ : (x : ↥U) → V x ⟶ U
heq : ∀ (x : ↥U), ((Sheaf.presheaf F).map (i₁ x).op) s = ((Sheaf.presheaf F).map (i₂ x).op) t
x : ↑X
hxU : x ∈ ↑U
⊢ x ∈ ↑(iSup V)

Next tactic:
simp only [Opens.coe_iSup, Set.mem_iUnion, SetLike.mem_coe]
/-- Let `F` be a sheaf valued in a concrete category, whose forgetful functor reflects isomorphisms,
preserves limits and filtered colimits. Then two sections who agree on every stalk must be equal.
-/
theorem section_ext (F : Sheaf C X) (U : Opens X) (s t : F.1.obj (op U))
    (h : ∀ x : U, F.presheaf.germ x s = F.presheaf.germ x t) : s = t := by
  -- We use `germ_eq` and the axiom of choice, to pick for every point `x` a neighbourhood
  -- `V x`, such that the restrictions of `s` and `t` to `V x` coincide.
  choose V m i₁ i₂ heq using fun x : U => F.presheaf.germ_eq x.1 x.2 x.2 s t (h x)
  -- Since `F` is a sheaf, we can prove the equality locally, if we can show that these
  -- neighborhoods form a cover of `U`.
  apply F.eq_of_locally_eq' V U i₁
  · intro x hxU
    simp only [Opens.coe_iSup, Set.mem_iUnion, SetLike.mem_coe]
    

Proof state:
case hcover
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
U : Opens ↑X
s t : (forget C).obj (F.val.obj (op U))
h : ∀ (x : ↥U), (germ (Sheaf.presheaf F) x) s = (germ (Sheaf.presheaf F) x) t
V : ↥U → Opens ↑X
m : ∀ (x : ↥U), ↑x ∈ V x
i₁ i₂ : (x : ↥U) → V x ⟶ U
heq : ∀ (x : ↥U), ((Sheaf.presheaf F).map (i₁ x).op) s = ((Sheaf.presheaf F).map (i₂ x).op) t
x : ↑X
hxU : x ∈ ↑U
⊢ ∃ i, x ∈ V i

Next tactic:
exact ⟨⟨x, hxU⟩, m ⟨x, hxU⟩⟩
/-- Let `F` be a sheaf valued in a concrete category, whose forgetful functor reflects isomorphisms,
preserves limits and filtered colimits. Then two sections who agree on every stalk must be equal.
-/
theorem section_ext (F : Sheaf C X) (U : Opens X) (s t : F.1.obj (op U))
    (h : ∀ x : U, F.presheaf.germ x s = F.presheaf.germ x t) : s = t := by
  -- We use `germ_eq` and the axiom of choice, to pick for every point `x` a neighbourhood
  -- `V x`, such that the restrictions of `s` and `t` to `V x` coincide.
  choose V m i₁ i₂ heq using fun x : U => F.presheaf.germ_eq x.1 x.2 x.2 s t (h x)
  -- Since `F` is a sheaf, we can prove the equality locally, if we can show that these
  -- neighborhoods form a cover of `U`.
  apply F.eq_of_locally_eq' V U i₁
  · intro x hxU
    simp only [Opens.coe_iSup, Set.mem_iUnion, SetLike.mem_coe]
    exact ⟨⟨x, hxU⟩, m ⟨x, hxU⟩⟩
  · 

Proof state:
case h
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
U : Opens ↑X
s t : (forget C).obj (F.val.obj (op U))
h : ∀ (x : ↥U), (germ (Sheaf.presheaf F) x) s = (germ (Sheaf.presheaf F) x) t
V : ↥U → Opens ↑X
m : ∀ (x : ↥U), ↑x ∈ V x
i₁ i₂ : (x : ↥U) → V x ⟶ U
heq : ∀ (x : ↥U), ((Sheaf.presheaf F).map (i₁ x).op) s = ((Sheaf.presheaf F).map (i₂ x).op) t
⊢ ∀ (i : ↥U), (F.val.map (i₁ i).op) s = (F.val.map (i₁ i).op) t

Next tactic:
intro x
/-- Let `F` be a sheaf valued in a concrete category, whose forgetful functor reflects isomorphisms,
preserves limits and filtered colimits. Then two sections who agree on every stalk must be equal.
-/
theorem section_ext (F : Sheaf C X) (U : Opens X) (s t : F.1.obj (op U))
    (h : ∀ x : U, F.presheaf.germ x s = F.presheaf.germ x t) : s = t := by
  -- We use `germ_eq` and the axiom of choice, to pick for every point `x` a neighbourhood
  -- `V x`, such that the restrictions of `s` and `t` to `V x` coincide.
  choose V m i₁ i₂ heq using fun x : U => F.presheaf.germ_eq x.1 x.2 x.2 s t (h x)
  -- Since `F` is a sheaf, we can prove the equality locally, if we can show that these
  -- neighborhoods form a cover of `U`.
  apply F.eq_of_locally_eq' V U i₁
  · intro x hxU
    simp only [Opens.coe_iSup, Set.mem_iUnion, SetLike.mem_coe]
    exact ⟨⟨x, hxU⟩, m ⟨x, hxU⟩⟩
  · intro x
    

Proof state:
case h
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
U : Opens ↑X
s t : (forget C).obj (F.val.obj (op U))
h : ∀ (x : ↥U), (germ (Sheaf.presheaf F) x) s = (germ (Sheaf.presheaf F) x) t
V : ↥U → Opens ↑X
m : ∀ (x : ↥U), ↑x ∈ V x
i₁ i₂ : (x : ↥U) → V x ⟶ U
heq : ∀ (x : ↥U), ((Sheaf.presheaf F).map (i₁ x).op) s = ((Sheaf.presheaf F).map (i₂ x).op) t
x : ↥U
⊢ (F.val.map (i₁ x).op) s = (F.val.map (i₁ x).op) t

Next tactic:
rw [heq, Subsingleton.elim (i₁ x) (i₂ x)]
theorem app_injective_of_stalkFunctor_map_injective {F : Sheaf C X} {G : Presheaf C X} (f : F.1 ⟶ G)
    (U : Opens X) (h : ∀ x : U, Function.Injective ((stalkFunctor C x.val).map f)) :
    Function.Injective (f.app (op U)) := fun s t hst =>
  section_ext F _ _ _ fun x =>
    h x <| by 

Proof state:
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F : Sheaf C X
G : Presheaf C X
f : F.val ⟶ G
U : Opens ↑X
h : ∀ (x : ↥U), Function.Injective ⇑((stalkFunctor C ↑x).map f)
s t : (forget C).obj (F.val.obj (op U))
hst : (f.app (op U)) s = (f.app (op U)) t
x : ↥U
⊢ ((stalkFunctor C ↑x).map f) ((germ (Sheaf.presheaf F) x) s) =
    ((stalkFunctor C ↑x).map f) ((germ (Sheaf.presheaf F) x) t)

Next tactic:
erw [stalkFunctor_map_germ_apply, stalkFunctor_map_germ_apply, hst]
/-- For surjectivity, we are given an arbitrary section `t` and need to find a preimage for it.
We claim that it suffices to find preimages *locally*. That is, for each `x : U` we construct
a neighborhood `V ≤ U` and a section `s : F.obj (op V))` such that `f.app (op V) s` and `t`
agree on `V`. -/
theorem app_surjective_of_injective_of_locally_surjective {F G : Sheaf C X} (f : F ⟶ G)
    (U : Opens X) (hinj : ∀ x : U, Function.Injective ((stalkFunctor C x.1).map f.1))
    (hsurj : ∀ (t) (x : U), ∃ (V : Opens X) (_ : x.1 ∈ V) (iVU : V ⟶ U) (s : F.1.obj (op V)),
          f.1.app (op V) s = G.1.map iVU.op t) :
    Function.Surjective (f.1.app (op U)) := by
  

Proof state:
C : Type u
inst✝⁶ : Category.{v, u} C
inst✝⁵ : HasColimits C
X Y Z : TopCat
inst✝⁴ : ConcreteCategory C
inst✝³ : PreservesFilteredColimits (forget C)
inst✝² : HasLimits C
inst✝¹ : PreservesLimits (forget C)
inst✝ : ReflectsIsomorphisms (forget C)
F G : Sheaf C X
f : F ⟶ G
U : Opens ↑X
hinj : ∀ (x : ↥U), Function.Injective ⇑((stalkFunctor C ↑x).map f.val)
hsurj :
  ∀ (t : (forget C).obj (G.val.obj (op U))) (x : ↥U),
    ∃ V, ∃ (_ : ↑x ∈ V), ∃ iVU s, (f.val.app (op V)) s = (G.val.map iVU.op) t
⊢ Function.Surjective ⇑(f.val.app (op U))

Next tactic:
intro t