l3lab/ntp-mathlib huggingface dataset:
variable (C) in
/--
The Grothendieck topology associated to a coverage `K`.
It is defined *inductively* as follows:
1. If `S` is a covering presieve for `K`, then the sieve generated by `S` is a covering
  sieve for the associated Grothendieck topology.
2. The top sieves are in the associated Grothendieck topology.
3. Add all sieves required by the *local character* axiom of a Grothendieck topology.

The pullback compatibility condition for a coverage ensures that the
associated Grothendieck topology is pullback stable, and so an additional constructor
in the inductive construction is not needed.
-/
def toGrothendieck (K : Coverage C) : GrothendieckTopology C where
  sieves := saturate K
  top_mem' := .top
  pullback_stable' := by
    intro X Y S f hS
    induction hS generalizing Y with
    | of X S hS =>
      obtain ⟨R,hR1,hR2⟩ := K.pullback f S hS
      suffices Sieve.generate R ≤ (Sieve.generate S).pullback f from
        saturate_of_superset _ this (saturate.of _ _ hR1)
      rintro Z g ⟨W, i, e, h1, h2⟩
      obtain ⟨WW, ii, ee, hh1, hh2⟩ := hR2 h1
      refine ⟨WW, i ≫ ii, ee, hh1, ?_⟩
      simp only [hh2, reassoc_of% h2, Category.assoc]
    | top X => apply saturate.top
    | transitive X R S _ hS H1 _ =>
      apply saturate.transitive
      apply H1 f
      intro Z g hg
      

Proof state:
case transitive.a
C : Type ?u.27900
inst✝ : Category.{?u.27904, ?u.27900} C
K : Coverage C
X✝ : C
S✝ : Sieve X✝
X : C
R S : Sieve X
a✝ : saturate K X R
hS : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → saturate K Y (Sieve.pullback f S)
H1 : ∀ ⦃Y : C⦄ (f : Y ⟶ X), Sieve.pullback f R ∈ saturate K Y
a_ih✝ :
  ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄,
    R.arrows f → ∀ ⦃Y_1 : C⦄ (f_1 : Y_1 ⟶ Y), Sieve.pullback f_1 (Sieve.pullback f S) ∈ saturate K Y_1
Y : C
f : Y ⟶ X
Z : C
g : Z ⟶ Y
hg : (Sieve.pullback f R).arrows g
⊢ saturate K Z (Sieve.pullback g (Sieve.pullback f S))

Next tactic:
rw [← Sieve.pullback_comp]
variable (C) in
/--
The Grothendieck topology associated to a coverage `K`.
It is defined *inductively* as follows:
1. If `S` is a covering presieve for `K`, then the sieve generated by `S` is a covering
  sieve for the associated Grothendieck topology.
2. The top sieves are in the associated Grothendieck topology.
3. Add all sieves required by the *local character* axiom of a Grothendieck topology.

The pullback compatibility condition for a coverage ensures that the
associated Grothendieck topology is pullback stable, and so an additional constructor
in the inductive construction is not needed.
-/
def toGrothendieck (K : Coverage C) : GrothendieckTopology C where
  sieves := saturate K
  top_mem' := .top
  pullback_stable' := by
    intro X Y S f hS
    induction hS generalizing Y with
    | of X S hS =>
      obtain ⟨R,hR1,hR2⟩ := K.pullback f S hS
      suffices Sieve.generate R ≤ (Sieve.generate S).pullback f from
        saturate_of_superset _ this (saturate.of _ _ hR1)
      rintro Z g ⟨W, i, e, h1, h2⟩
      obtain ⟨WW, ii, ee, hh1, hh2⟩ := hR2 h1
      refine ⟨WW, i ≫ ii, ee, hh1, ?_⟩
      simp only [hh2, reassoc_of% h2, Category.assoc]
    | top X => apply saturate.top
    | transitive X R S _ hS H1 _ =>
      apply saturate.transitive
      apply H1 f
      intro Z g hg
      rw [← Sieve.pullback_comp]
      

Proof state:
case transitive.a
C : Type ?u.27900
inst✝ : Category.{?u.27904, ?u.27900} C
K : Coverage C
X✝ : C
S✝ : Sieve X✝
X : C
R S : Sieve X
a✝ : saturate K X R
hS : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → saturate K Y (Sieve.pullback f S)
H1 : ∀ ⦃Y : C⦄ (f : Y ⟶ X), Sieve.pullback f R ∈ saturate K Y
a_ih✝ :
  ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄,
    R.arrows f → ∀ ⦃Y_1 : C⦄ (f_1 : Y_1 ⟶ Y), Sieve.pullback f_1 (Sieve.pullback f S) ∈ saturate K Y_1
Y : C
f : Y ⟶ X
Z : C
g : Z ⟶ Y
hg : (Sieve.pullback f R).arrows g
⊢ saturate K Z (Sieve.pullback (g ≫ f) S)

Next tactic:
exact hS hg
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    

Proof state:
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
⊢ toGrothendieck C K ≤ J ↔ K ≤ ofGrothendieck C J

Next tactic:
constructor
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · 

Proof state:
case mp
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
⊢ toGrothendieck C K ≤ J → K ≤ ofGrothendieck C J

Next tactic:
intro H X S hS
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      

Proof state:
case mp
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : toGrothendieck C K ≤ J
X : C
S : Presieve X
hS : S ∈ covering K X
⊢ S ∈ covering (ofGrothendieck C J) X

Next tactic:
exact H _ <| saturate.of _ _ hS
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · 

Proof state:
case mpr
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
⊢ K ≤ ofGrothendieck C J → toGrothendieck C K ≤ J

Next tactic:
intro H X S hS
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · intro H X S hS
      

Proof state:
case mpr
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : K ≤ ofGrothendieck C J
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves (toGrothendieck C K) X
⊢ S ∈ GrothendieckTopology.sieves J X

Next tactic:
induction hS with
      | of X S hS => exact H _ hS
      | top => apply J.top_mem
      | transitive X R S _ _ H1 H2 => exact J.transitive H1 _ H2
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · intro H X S hS
      induction hS with
      

Proof state:
case mpr.of
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : K ≤ ofGrothendieck C J
X✝ : C
S✝ : Sieve X✝
X : C
S : Presieve X
hS : S ∈ covering K X
⊢ Sieve.generate S ∈ GrothendieckTopology.sieves J X

Next tactic:
| of X S hS => exact H _ hS
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · intro H X S hS
      induction hS with
      | of X S hS => 

Proof state:
case mpr.of
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : K ≤ ofGrothendieck C J
X✝ : C
S✝ : Sieve X✝
X : C
S : Presieve X
hS : S ∈ covering K X
⊢ Sieve.generate S ∈ GrothendieckTopology.sieves J X

Next tactic:
exact H _ hS
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · intro H X S hS
      induction hS with
      | of X S hS => exact H _ hS
      

Proof state:
case mpr.top
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : K ≤ ofGrothendieck C J
X : C
S : Sieve X
X✝ : C
⊢ ⊤ ∈ GrothendieckTopology.sieves J X✝

Next tactic:
| top => apply J.top_mem
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · intro H X S hS
      induction hS with
      | of X S hS => exact H _ hS
      | top => 

Proof state:
case mpr.top
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : K ≤ ofGrothendieck C J
X : C
S : Sieve X
X✝ : C
⊢ ⊤ ∈ GrothendieckTopology.sieves J X✝

Next tactic:
apply J.top_mem
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · intro H X S hS
      induction hS with
      | of X S hS => exact H _ hS
      | top => apply J.top_mem
      

Proof state:
case mpr.transitive
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : K ≤ ofGrothendieck C J
X✝ : C
S✝ : Sieve X✝
X : C
R S : Sieve X
a✝¹ : saturate K X R
a✝ : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → saturate K Y (Sieve.pullback f S)
H1 : R ∈ GrothendieckTopology.sieves J X
H2 : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → Sieve.pullback f S ∈ GrothendieckTopology.sieves J Y
⊢ S ∈ GrothendieckTopology.sieves J X

Next tactic:
| transitive X R S _ _ H1 H2 => exact J.transitive H1 _ H2
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    rwa [Sieve.generate_sieve S]
  gc K J := by
    constructor
    · intro H X S hS
      exact H _ <| saturate.of _ _ hS
    · intro H X S hS
      induction hS with
      | of X S hS => exact H _ hS
      | top => apply J.top_mem
      | transitive X R S _ _ H1 H2 => 

Proof state:
case mpr.transitive
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
K : Coverage C
J : GrothendieckTopology C
H : K ≤ ofGrothendieck C J
X✝ : C
S✝ : Sieve X✝
X : C
R S : Sieve X
a✝¹ : saturate K X R
a✝ : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → saturate K Y (Sieve.pullback f S)
H1 : R ∈ GrothendieckTopology.sieves J X
H2 : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → Sieve.pullback f S ∈ GrothendieckTopology.sieves J Y
⊢ S ∈ GrothendieckTopology.sieves J X

Next tactic:
exact J.transitive H1 _ H2
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    

Proof state:
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
J : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
⊢ S ∈ GrothendieckTopology.sieves (toGrothendieck C (ofGrothendieck C J)) X

Next tactic:
rw [← Sieve.generate_sieve S]
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    

Proof state:
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
J : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
⊢ Sieve.generate S.arrows ∈ GrothendieckTopology.sieves (toGrothendieck C (ofGrothendieck C J)) X

Next tactic:
apply saturate.of
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    

Proof state:
case hS
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
J : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
⊢ S.arrows ∈ covering (ofGrothendieck C J) X

Next tactic:
dsimp [ofGrothendieck]
variable (C) in
/--
The two constructions `Coverage.toGrothendieck` and `Coverage.ofGrothendieck` form
a Galois insertion.
-/
def gi : GaloisInsertion (toGrothendieck C) (ofGrothendieck C) where
  choice K _ := toGrothendieck _ K
  choice_eq := fun _ _ => rfl
  le_l_u J X S hS := by
    rw [← Sieve.generate_sieve S]
    apply saturate.of
    dsimp [ofGrothendieck]
    

Proof state:
case hS
C : Type ?u.30764
inst✝ : Category.{?u.30768, ?u.30764} C
J : GrothendieckTopology C
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
⊢ Sieve.generate S.arrows ∈ GrothendieckTopology.sieves J X

Next tactic:
rwa [Sieve.generate_sieve S]
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  

Proof state:
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
⊢ toGrothendieck C K = sInf {J | K ≤ ofGrothendieck C J}

Next tactic:
apply le_antisymm
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · 

Proof state:
case a
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
⊢ toGrothendieck C K ≤ sInf {J | K ≤ ofGrothendieck C J}

Next tactic:
apply le_sInf
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; 

Proof state:
case a.a
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
⊢ ∀ b ∈ {J | K ≤ ofGrothendieck C J}, toGrothendieck C K ≤ b

Next tactic:
intro J hJ
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    

Proof state:
case a.a
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
⊢ toGrothendieck C K ≤ J

Next tactic:
intro X S hS
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    

Proof state:
case a.a
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves (toGrothendieck C K) X
⊢ S ∈ GrothendieckTopology.sieves J X

Next tactic:
induction hS with
    | of X S hS => apply hJ; assumption
    | top => apply J.top_mem
    | transitive X R S _ _ H1 H2 => exact J.transitive H1 _ H2
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    induction hS with
    

Proof state:
case a.a.of
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X✝ : C
S✝ : Sieve X✝
X : C
S : Presieve X
hS : S ∈ covering K X
⊢ Sieve.generate S ∈ GrothendieckTopology.sieves J X

Next tactic:
| of X S hS => apply hJ; assumption
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    induction hS with
    | of X S hS => 

Proof state:
case a.a.of
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X✝ : C
S✝ : Sieve X✝
X : C
S : Presieve X
hS : S ∈ covering K X
⊢ Sieve.generate S ∈ GrothendieckTopology.sieves J X

Next tactic:
apply hJ
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    induction hS with
    | of X S hS => apply hJ; 

Proof state:
case a.a.of.a
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X✝ : C
S✝ : Sieve X✝
X : C
S : Presieve X
hS : S ∈ covering K X
⊢ S ∈ covering K X

Next tactic:
assumption
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    induction hS with
    | of X S hS => apply hJ; assumption
    

Proof state:
case a.a.top
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X : C
S : Sieve X
X✝ : C
⊢ ⊤ ∈ GrothendieckTopology.sieves J X✝

Next tactic:
| top => apply J.top_mem
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    induction hS with
    | of X S hS => apply hJ; assumption
    | top => 

Proof state:
case a.a.top
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X : C
S : Sieve X
X✝ : C
⊢ ⊤ ∈ GrothendieckTopology.sieves J X✝

Next tactic:
apply J.top_mem
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    induction hS with
    | of X S hS => apply hJ; assumption
    | top => apply J.top_mem
    

Proof state:
case a.a.transitive
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X✝ : C
S✝ : Sieve X✝
X : C
R S : Sieve X
a✝¹ : saturate K X R
a✝ : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → saturate K Y (Sieve.pullback f S)
H1 : R ∈ GrothendieckTopology.sieves J X
H2 : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → Sieve.pullback f S ∈ GrothendieckTopology.sieves J Y
⊢ S ∈ GrothendieckTopology.sieves J X

Next tactic:
| transitive X R S _ _ H1 H2 => exact J.transitive H1 _ H2
/--
An alternative characterization of the Grothendieck topology associated to a coverage `K`:
it is the infimum of all Grothendieck topologies whose associated coverage contains `K`.
-/
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
    sInf {J | K ≤ ofGrothendieck _ J } := by
  apply le_antisymm
  · apply le_sInf; intro J hJ
    intro X S hS
    induction hS with
    | of X S hS => apply hJ; assumption
    | top => apply J.top_mem
    | transitive X R S _ _ H1 H2 => 

Proof state:
case a.a.transitive
C : Type u_1
inst✝ : Category.{u_2, u_1} C
K : Coverage C
J : GrothendieckTopology C
hJ : J ∈ {J | K ≤ ofGrothendieck C J}
X✝ : C
S✝ : Sieve X✝
X : C
R S : Sieve X
a✝¹ : saturate K X R
a✝ : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → saturate K Y (Sieve.pullback f S)
H1 : R ∈ GrothendieckTopology.sieves J X
H2 : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, R.arrows f → Sieve.pullback f S ∈ GrothendieckTopology.sieves J Y
⊢ S ∈ GrothendieckTopology.sieves J X

Next tactic:
exact J.transitive H1 _ H2