l3lab/ntp-mathlib huggingface dataset:
/-- If a family of functions `f` has locally-finite multiplicative support, subordinate to a family
of open sets, then for any point we can find a neighbourhood on which only finitely-many members of
`f` are not equal to 1. -/
@[to_additive " If a family of functions `f` has locally-finite support, subordinate to a family of
open sets, then for any point we can find a neighbourhood on which only finitely-many members of `f`
are non-zero. "]
theorem exists_finset_nhd_mulSupport_subset {f : ι → X → R}
    (hlf : LocallyFinite fun i => mulSupport (f i)) (hso : ∀ i, mulTSupport (f i) ⊆ U i)
    (ho : ∀ i, IsOpen (U i)) (x : X) :
    ∃ (is : Finset ι), ∃ n, n ∈ 𝓝 x ∧ (n ⊆ ⋂ i ∈ is, U i) ∧
      ∀ z ∈ n, (mulSupport fun i => f i z) ⊆ is := by
  obtain ⟨n, hn, hnf⟩ := hlf x
  classical
    let is := hnf.toFinset.filter fun i => x ∈ U i
    let js := hnf.toFinset.filter fun j => x ∉ U j
    refine'
      ⟨is, (n ∩ ⋂ j ∈ js, (mulTSupport (f j))ᶜ) ∩ ⋂ i ∈ is, U i, inter_mem (inter_mem hn _) _,
        inter_subset_right _ _, fun z hz => _⟩
    · exact (biInter_finset_mem js).mpr fun j hj => IsClosed.compl_mem_nhds (isClosed_mulTSupport _)
        (Set.not_mem_subset (hso j) (Finset.mem_filter.mp hj).2)
    · exact (biInter_finset_mem is).mpr fun i hi => (ho i).mem_nhds (Finset.mem_filter.mp hi).2
    · have hzn : z ∈ n := by
        rw [inter_assoc] at hz
        exact mem_of_mem_inter_left hz
      replace hz := mem_of_mem_inter_right (mem_of_mem_inter_left hz)
      simp only [Finset.mem_filter, Finite.mem_toFinset, mem_setOf_eq, mem_iInter, and_imp] at hz
      suffices (mulSupport fun i => f i z) ⊆ hnf.toFinset by
        refine' hnf.toFinset.subset_coe_filter_of_subset_forall _ this fun i hi => _
        specialize hz i ⟨z, ⟨hi, hzn⟩⟩
        contrapose hz
        simp [hz, subset_mulTSupport (f i) hi]
      intro i hi
      

Proof state:
case intro.intro.refine'_3
X : Type u_1
α : Type u_2
α' : Type u_3
β : Type u_4
γ : Type u_5
δ : Type u_6
M : Type u_7
E : Type u_8
R : Type u_9
ι : Type u_10
U : ι → Set X
inst✝¹ : TopologicalSpace X
inst✝ : One R
f : ι → X → R
hlf : LocallyFinite fun i => mulSupport (f i)
hso : ∀ (i : ι), mulTSupport (f i) ⊆ U i
ho : ∀ (i : ι), IsOpen (U i)
x : X
n : Set X
hn : n ∈ 𝓝 x
hnf : Set.Finite {i | Set.Nonempty ((fun i => mulSupport (f i)) i ∩ n)}
is : Finset ι := Finset.filter (fun i => x ∈ U i) (Finite.toFinset hnf)
js : Finset ι := Finset.filter (fun j => x ∉ U j) (Finite.toFinset hnf)
z : X
hzn : z ∈ n
hz : ∀ (i : ι), Set.Nonempty (mulSupport (f i) ∩ n) → x ∉ U i → z ∈ (mulTSupport (f i))ᶜ
i : ι
hi : i ∈ mulSupport fun i => f i z
⊢ i ∈ ↑(Finite.toFinset hnf)

Next tactic:
simp only [Finite.coe_toFinset, mem_setOf_eq]
/-- If a family of functions `f` has locally-finite multiplicative support, subordinate to a family
of open sets, then for any point we can find a neighbourhood on which only finitely-many members of
`f` are not equal to 1. -/
@[to_additive " If a family of functions `f` has locally-finite support, subordinate to a family of
open sets, then for any point we can find a neighbourhood on which only finitely-many members of `f`
are non-zero. "]
theorem exists_finset_nhd_mulSupport_subset {f : ι → X → R}
    (hlf : LocallyFinite fun i => mulSupport (f i)) (hso : ∀ i, mulTSupport (f i) ⊆ U i)
    (ho : ∀ i, IsOpen (U i)) (x : X) :
    ∃ (is : Finset ι), ∃ n, n ∈ 𝓝 x ∧ (n ⊆ ⋂ i ∈ is, U i) ∧
      ∀ z ∈ n, (mulSupport fun i => f i z) ⊆ is := by
  obtain ⟨n, hn, hnf⟩ := hlf x
  classical
    let is := hnf.toFinset.filter fun i => x ∈ U i
    let js := hnf.toFinset.filter fun j => x ∉ U j
    refine'
      ⟨is, (n ∩ ⋂ j ∈ js, (mulTSupport (f j))ᶜ) ∩ ⋂ i ∈ is, U i, inter_mem (inter_mem hn _) _,
        inter_subset_right _ _, fun z hz => _⟩
    · exact (biInter_finset_mem js).mpr fun j hj => IsClosed.compl_mem_nhds (isClosed_mulTSupport _)
        (Set.not_mem_subset (hso j) (Finset.mem_filter.mp hj).2)
    · exact (biInter_finset_mem is).mpr fun i hi => (ho i).mem_nhds (Finset.mem_filter.mp hi).2
    · have hzn : z ∈ n := by
        rw [inter_assoc] at hz
        exact mem_of_mem_inter_left hz
      replace hz := mem_of_mem_inter_right (mem_of_mem_inter_left hz)
      simp only [Finset.mem_filter, Finite.mem_toFinset, mem_setOf_eq, mem_iInter, and_imp] at hz
      suffices (mulSupport fun i => f i z) ⊆ hnf.toFinset by
        refine' hnf.toFinset.subset_coe_filter_of_subset_forall _ this fun i hi => _
        specialize hz i ⟨z, ⟨hi, hzn⟩⟩
        contrapose hz
        simp [hz, subset_mulTSupport (f i) hi]
      intro i hi
      simp only [Finite.coe_toFinset, mem_setOf_eq]
      

Proof state:
case intro.intro.refine'_3
X : Type u_1
α : Type u_2
α' : Type u_3
β : Type u_4
γ : Type u_5
δ : Type u_6
M : Type u_7
E : Type u_8
R : Type u_9
ι : Type u_10
U : ι → Set X
inst✝¹ : TopologicalSpace X
inst✝ : One R
f : ι → X → R
hlf : LocallyFinite fun i => mulSupport (f i)
hso : ∀ (i : ι), mulTSupport (f i) ⊆ U i
ho : ∀ (i : ι), IsOpen (U i)
x : X
n : Set X
hn : n ∈ 𝓝 x
hnf : Set.Finite {i | Set.Nonempty ((fun i => mulSupport (f i)) i ∩ n)}
is : Finset ι := Finset.filter (fun i => x ∈ U i) (Finite.toFinset hnf)
js : Finset ι := Finset.filter (fun j => x ∉ U j) (Finite.toFinset hnf)
z : X
hzn : z ∈ n
hz : ∀ (i : ι), Set.Nonempty (mulSupport (f i) ∩ n) → x ∉ U i → z ∈ (mulTSupport (f i))ᶜ
i : ι
hi : i ∈ mulSupport fun i => f i z
⊢ Set.Nonempty (mulSupport (f i) ∩ n)

Next tactic:
exact ⟨z, ⟨hi, hzn⟩⟩
/-- Being `J`-closed is stable under pullback. -/
theorem isClosed_pullback {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.IsClosed S → J₁.IsClosed (S.pullback f) :=
  fun hS Z g hg => hS (g ≫ f) (by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
hS : IsClosed J₁ S
Z : C
g : Z ⟶ Y
hg : Covers J₁ (Sieve.pullback f S) g
⊢ Covers J₁ S (g ≫ f)

Next tactic:
rwa [J₁.covers_iff, Sieve.pullback_comp]
/-- Closing under `J` is stable under pullback. -/
theorem pullback_close {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.close (S.pullback f) = (J₁.close S).pullback f := by
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
⊢ close J₁ (Sieve.pullback f S) = Sieve.pullback f (close J₁ S)

Next tactic:
apply le_antisymm
/-- Closing under `J` is stable under pullback. -/
theorem pullback_close {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.close (S.pullback f) = (J₁.close S).pullback f := by
  apply le_antisymm
  · 

Proof state:
case a
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
⊢ close J₁ (Sieve.pullback f S) ≤ Sieve.pullback f (close J₁ S)

Next tactic:
refine' J₁.le_close_of_isClosed (Sieve.pullback_monotone _ (J₁.le_close S)) _
/-- Closing under `J` is stable under pullback. -/
theorem pullback_close {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.close (S.pullback f) = (J₁.close S).pullback f := by
  apply le_antisymm
  · refine' J₁.le_close_of_isClosed (Sieve.pullback_monotone _ (J₁.le_close S)) _
    

Proof state:
case a
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
⊢ IsClosed J₁ (Sieve.pullback f (close J₁ S))

Next tactic:
apply J₁.isClosed_pullback _ _ (J₁.close_isClosed _)
/-- Closing under `J` is stable under pullback. -/
theorem pullback_close {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.close (S.pullback f) = (J₁.close S).pullback f := by
  apply le_antisymm
  · refine' J₁.le_close_of_isClosed (Sieve.pullback_monotone _ (J₁.le_close S)) _
    apply J₁.isClosed_pullback _ _ (J₁.close_isClosed _)
  · 

Proof state:
case a
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
⊢ Sieve.pullback f (close J₁ S) ≤ close J₁ (Sieve.pullback f S)

Next tactic:
intro Z g hg
/-- Closing under `J` is stable under pullback. -/
theorem pullback_close {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.close (S.pullback f) = (J₁.close S).pullback f := by
  apply le_antisymm
  · refine' J₁.le_close_of_isClosed (Sieve.pullback_monotone _ (J₁.le_close S)) _
    apply J₁.isClosed_pullback _ _ (J₁.close_isClosed _)
  · intro Z g hg
    

Proof state:
case a
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
Z : C
g : Z ⟶ Y
hg : (Sieve.pullback f (close J₁ S)).arrows g
⊢ (close J₁ (Sieve.pullback f S)).arrows g

Next tactic:
change _ ∈ J₁ _
/-- Closing under `J` is stable under pullback. -/
theorem pullback_close {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.close (S.pullback f) = (J₁.close S).pullback f := by
  apply le_antisymm
  · refine' J₁.le_close_of_isClosed (Sieve.pullback_monotone _ (J₁.le_close S)) _
    apply J₁.isClosed_pullback _ _ (J₁.close_isClosed _)
  · intro Z g hg
    change _ ∈ J₁ _
    

Proof state:
case a
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
Z : C
g : Z ⟶ Y
hg : (Sieve.pullback f (close J₁ S)).arrows g
⊢ Sieve.pullback g (Sieve.pullback f S) ∈ sieves J₁ Z

Next tactic:
rw [← Sieve.pullback_comp]
/-- Closing under `J` is stable under pullback. -/
theorem pullback_close {X Y : C} (f : Y ⟶ X) (S : Sieve X) :
    J₁.close (S.pullback f) = (J₁.close S).pullback f := by
  apply le_antisymm
  · refine' J₁.le_close_of_isClosed (Sieve.pullback_monotone _ (J₁.le_close S)) _
    apply J₁.isClosed_pullback _ _ (J₁.close_isClosed _)
  · intro Z g hg
    change _ ∈ J₁ _
    rw [← Sieve.pullback_comp]
    

Proof state:
case a
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X Y : C
f : Y ⟶ X
S : Sieve X
Z : C
g : Z ⟶ Y
hg : (Sieve.pullback f (close J₁ S)).arrows g
⊢ Sieve.pullback (g ≫ f) S ∈ sieves J₁ Z

Next tactic:
apply hg
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
⊢ close J₁ S = ⊤ ↔ S ∈ sieves J₁ X

Next tactic:
constructor
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · 

Proof state:
case mp
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
⊢ close J₁ S = ⊤ → S ∈ sieves J₁ X

Next tactic:
intro h
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    

Proof state:
case mp
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
h : close J₁ S = ⊤
⊢ S ∈ sieves J₁ X

Next tactic:
apply J₁.transitive (J₁.top_mem X)
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    apply J₁.transitive (J₁.top_mem X)
    

Proof state:
case mp.h
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
h : close J₁ S = ⊤
⊢ ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, ⊤.arrows f → Sieve.pullback f S ∈ sieves J₁ Y

Next tactic:
intro Y f hf
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    apply J₁.transitive (J₁.top_mem X)
    intro Y f hf
    

Proof state:
case mp.h
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
h : close J₁ S = ⊤
Y : C
f : Y ⟶ X
hf : ⊤.arrows f
⊢ Sieve.pullback f S ∈ sieves J₁ Y

Next tactic:
change J₁.close S f
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    apply J₁.transitive (J₁.top_mem X)
    intro Y f hf
    change J₁.close S f
    

Proof state:
case mp.h
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
h : close J₁ S = ⊤
Y : C
f : Y ⟶ X
hf : ⊤.arrows f
⊢ (close J₁ S).arrows f

Next tactic:
rwa [h]
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    apply J₁.transitive (J₁.top_mem X)
    intro Y f hf
    change J₁.close S f
    rwa [h]
  · 

Proof state:
case mpr
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
⊢ S ∈ sieves J₁ X → close J₁ S = ⊤

Next tactic:
intro hS
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    apply J₁.transitive (J₁.top_mem X)
    intro Y f hf
    change J₁.close S f
    rwa [h]
  · intro hS
    

Proof state:
case mpr
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ sieves J₁ X
⊢ close J₁ S = ⊤

Next tactic:
rw [eq_top_iff]
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    apply J₁.transitive (J₁.top_mem X)
    intro Y f hf
    change J₁.close S f
    rwa [h]
  · intro hS
    rw [eq_top_iff]
    

Proof state:
case mpr
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ sieves J₁ X
⊢ ⊤ ≤ close J₁ S

Next tactic:
intro Y f _
/--
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
operator determines the topology.
-/
theorem close_eq_top_iff_mem {X : C} (S : Sieve X) : J₁.close S = ⊤ ↔ S ∈ J₁ X := by
  constructor
  · intro h
    apply J₁.transitive (J₁.top_mem X)
    intro Y f hf
    change J₁.close S f
    rwa [h]
  · intro hS
    rw [eq_top_iff]
    intro Y f _
    

Proof state:
case mpr
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ sieves J₁ X
Y : C
f : Y ⟶ X
a✝ : ⊤.arrows f
⊢ (close J₁ S).arrows f

Next tactic:
apply J₁.pullback_stable _ hS
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
⊢ Presieve.IsSheaf J₁ (Functor.closedSieves J₁)

Next tactic:
intro X S hS
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
⊢ Presieve.IsSheafFor (Functor.closedSieves J₁) S.arrows

Next tactic:
rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
⊢ Presieve.IsSeparatedFor (Functor.closedSieves J₁) S.arrows ∧
    ∀ (x : Presieve.FamilyOfElements (Functor.closedSieves J₁) S.arrows),
      Presieve.FamilyOfElements.Compatible x → ∃ t, Presieve.FamilyOfElements.IsAmalgamation x t

Next tactic:
refine' ⟨_, _⟩
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  refine' ⟨_, _⟩
  · 

Proof state:
case refine'_1
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
⊢ Presieve.IsSeparatedFor (Functor.closedSieves J₁) S.arrows

Next tactic:
rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  refine' ⟨_, _⟩
  · rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂
    

Proof state:
case refine'_1.mk.mk
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
x : Presieve.FamilyOfElements (Functor.closedSieves J₁) S.arrows
M : Sieve (Opposite.op X).unop
hM : GrothendieckTopology.IsClosed J₁ M
N : Sieve (Opposite.op X).unop
hN : GrothendieckTopology.IsClosed J₁ N
hM₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := M, property := hM }
hN₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := N, property := hN }
⊢ { val := M, property := hM } = { val := N, property := hN }

Next tactic:
simp only [Functor.closedSieves_obj]
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  refine' ⟨_, _⟩
  · rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂
    simp only [Functor.closedSieves_obj]
    

Proof state:
case refine'_1.mk.mk
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
x : Presieve.FamilyOfElements (Functor.closedSieves J₁) S.arrows
M : Sieve (Opposite.op X).unop
hM : GrothendieckTopology.IsClosed J₁ M
N : Sieve (Opposite.op X).unop
hN : GrothendieckTopology.IsClosed J₁ N
hM₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := M, property := hM }
hN₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := N, property := hN }
⊢ { val := M, property := hM } = { val := N, property := hN }

Next tactic:
ext Y f
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  refine' ⟨_, _⟩
  · rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂
    simp only [Functor.closedSieves_obj]
    ext Y f
    

Proof state:
case refine'_1.mk.mk.a.h
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
x : Presieve.FamilyOfElements (Functor.closedSieves J₁) S.arrows
M : Sieve (Opposite.op X).unop
hM : GrothendieckTopology.IsClosed J₁ M
N : Sieve (Opposite.op X).unop
hN : GrothendieckTopology.IsClosed J₁ N
hM₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := M, property := hM }
hN₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := N, property := hN }
Y : C
f : Y ⟶ (Opposite.op X).unop
⊢ (↑{ val := M, property := hM }).arrows f ↔ (↑{ val := N, property := hN }).arrows f

Next tactic:
dsimp only [Subtype.coe_mk]
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  refine' ⟨_, _⟩
  · rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂
    simp only [Functor.closedSieves_obj]
    ext Y f
    dsimp only [Subtype.coe_mk]
    

Proof state:
case refine'_1.mk.mk.a.h
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
x : Presieve.FamilyOfElements (Functor.closedSieves J₁) S.arrows
M : Sieve (Opposite.op X).unop
hM : GrothendieckTopology.IsClosed J₁ M
N : Sieve (Opposite.op X).unop
hN : GrothendieckTopology.IsClosed J₁ N
hM₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := M, property := hM }
hN₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := N, property := hN }
Y : C
f : Y ⟶ (Opposite.op X).unop
⊢ M.arrows f ↔ N.arrows f

Next tactic:
rw [← J₁.covers_iff_mem_of_isClosed hM, ← J₁.covers_iff_mem_of_isClosed hN]
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  refine' ⟨_, _⟩
  · rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂
    simp only [Functor.closedSieves_obj]
    ext Y f
    dsimp only [Subtype.coe_mk]
    rw [← J₁.covers_iff_mem_of_isClosed hM, ← J₁.covers_iff_mem_of_isClosed hN]
    

Proof state:
case refine'_1.mk.mk.a.h
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
x : Presieve.FamilyOfElements (Functor.closedSieves J₁) S.arrows
M : Sieve (Opposite.op X).unop
hM : GrothendieckTopology.IsClosed J₁ M
N : Sieve (Opposite.op X).unop
hN : GrothendieckTopology.IsClosed J₁ N
hM₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := M, property := hM }
hN₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := N, property := hN }
Y : C
f : Y ⟶ (Opposite.op X).unop
⊢ GrothendieckTopology.Covers J₁ M f ↔ GrothendieckTopology.Covers J₁ N f

Next tactic:
have q : ∀ ⦃Z : C⦄ (g : Z ⟶ X) (_ : S g), M.pullback g = N.pullback g :=
      fun Z g hg => congr_arg Subtype.val ((hM₂ g hg).trans (hN₂ g hg).symm)
/-- The presheaf of `J`-closed sieves is a `J`-sheaf.
The proof of this is adapted from [MM92], Chapter III, Section 7, Lemma 1.
-/
theorem classifier_isSheaf : Presieve.IsSheaf J₁ (Functor.closedSieves J₁) := by
  intro X S hS
  rw [← Presieve.isSeparatedFor_and_exists_isAmalgamation_iff_isSheafFor]
  refine' ⟨_, _⟩
  · rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂
    simp only [Functor.closedSieves_obj]
    ext Y f
    dsimp only [Subtype.coe_mk]
    rw [← J₁.covers_iff_mem_of_isClosed hM, ← J₁.covers_iff_mem_of_isClosed hN]
    have q : ∀ ⦃Z : C⦄ (g : Z ⟶ X) (_ : S g), M.pullback g = N.pullback g :=
      fun Z g hg => congr_arg Subtype.val ((hM₂ g hg).trans (hN₂ g hg).symm)
    

Proof state:
case refine'_1.mk.mk.a.h
C : Type u
inst✝ : Category.{v, u} C
J₁ J₂ : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J₁ X
x : Presieve.FamilyOfElements (Functor.closedSieves J₁) S.arrows
M : Sieve (Opposite.op X).unop
hM : GrothendieckTopology.IsClosed J₁ M
N : Sieve (Opposite.op X).unop
hN : GrothendieckTopology.IsClosed J₁ N
hM₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := M, property := hM }
hN₂ : Presieve.FamilyOfElements.IsAmalgamation x { val := N, property := hN }
Y : C
f : Y ⟶ (Opposite.op X).unop
q : ∀ ⦃Z : C⦄ (g : Z ⟶ X), S.arrows g → Sieve.pullback g M = Sieve.pullback g N
⊢ GrothendieckTopology.Covers J₁ M f ↔ GrothendieckTopology.Covers J₁ N f

Next tactic:
have MSNS : M ⊓ S = N ⊓ S := by
      ext Z g
      rw [Sieve.inter_apply, Sieve.inter_apply]
      simp only [and_comm]
      apply and_congr_right
      intro hg
      rw [Sieve.pullback_eq_top_iff_mem, Sieve.pullback_eq_top_iff_mem, q g hg]