l3lab/ntp-mathlib huggingface dataset:
/-- The dense Grothendieck topology.

See https://ncatlab.org/nlab/show/dense+topology, or [MM92] Chapter III, Section 2, example (e).
-/
def dense : GrothendieckTopology C
    where
  sieves X S := ∀ {Y : C} (f : Y ⟶ X), ∃ (Z : _) (g : Z ⟶ Y), S (g ≫ f)
  top_mem' X Y f := ⟨Y, 𝟙 Y, ⟨⟩⟩
  pullback_stable' := by
    intro X Y S h H Z f
    rcases H (f ≫ h) with ⟨W, g, H'⟩
    exact ⟨W, g, by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
X Y : C
S : Sieve X
h : Y ⟶ X
H : S ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) X
Z : C
f : Z ⟶ Y
W : C
g : W ⟶ Z
H' : S.arrows (g ≫ f ≫ h)
⊢ (Sieve.pullback h S).arrows (g ≫ f)

Next tactic:
simpa
/-- The dense Grothendieck topology.

See https://ncatlab.org/nlab/show/dense+topology, or [MM92] Chapter III, Section 2, example (e).
-/
def dense : GrothendieckTopology C
    where
  sieves X S := ∀ {Y : C} (f : Y ⟶ X), ∃ (Z : _) (g : Z ⟶ Y), S (g ≫ f)
  top_mem' X Y f := ⟨Y, 𝟙 Y, ⟨⟩⟩
  pullback_stable' := by
    intro X Y S h H Z f
    rcases H (f ≫ h) with ⟨W, g, H'⟩
    exact ⟨W, g, by simpa⟩
  transitive' := by
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y : C
S R : Sieve X
J : GrothendieckTopology C
⊢ ∀ ⦃X : C⦄ ⦃S : Sieve X⦄,
    S ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) X →
      ∀ (R : Sieve X),
        (∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄,
            S.arrows f → Sieve.pullback f R ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) Y) →
          R ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) X

Next tactic:
intro X S H₁ R H₂ Y f
/-- The dense Grothendieck topology.

See https://ncatlab.org/nlab/show/dense+topology, or [MM92] Chapter III, Section 2, example (e).
-/
def dense : GrothendieckTopology C
    where
  sieves X S := ∀ {Y : C} (f : Y ⟶ X), ∃ (Z : _) (g : Z ⟶ Y), S (g ≫ f)
  top_mem' X Y f := ⟨Y, 𝟙 Y, ⟨⟩⟩
  pullback_stable' := by
    intro X Y S h H Z f
    rcases H (f ≫ h) with ⟨W, g, H'⟩
    exact ⟨W, g, by simpa⟩
  transitive' := by
    intro X S H₁ R H₂ Y f
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R✝ : Sieve X✝
J : GrothendieckTopology C
X : C
S : Sieve X
H₁ : S ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) X
R : Sieve X
H₂ :
  ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, S.arrows f → Sieve.pullback f R ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) Y
Y : C
f : Y ⟶ X
⊢ ∃ Z g, R.arrows (g ≫ f)

Next tactic:
rcases H₁ f with ⟨Z, g, H₃⟩
/-- The dense Grothendieck topology.

See https://ncatlab.org/nlab/show/dense+topology, or [MM92] Chapter III, Section 2, example (e).
-/
def dense : GrothendieckTopology C
    where
  sieves X S := ∀ {Y : C} (f : Y ⟶ X), ∃ (Z : _) (g : Z ⟶ Y), S (g ≫ f)
  top_mem' X Y f := ⟨Y, 𝟙 Y, ⟨⟩⟩
  pullback_stable' := by
    intro X Y S h H Z f
    rcases H (f ≫ h) with ⟨W, g, H'⟩
    exact ⟨W, g, by simpa⟩
  transitive' := by
    intro X S H₁ R H₂ Y f
    rcases H₁ f with ⟨Z, g, H₃⟩
    

Proof state:
case intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R✝ : Sieve X✝
J : GrothendieckTopology C
X : C
S : Sieve X
H₁ : S ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) X
R : Sieve X
H₂ :
  ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, S.arrows f → Sieve.pullback f R ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) Y
Y : C
f : Y ⟶ X
Z : C
g : Z ⟶ Y
H₃ : S.arrows (g ≫ f)
⊢ ∃ Z g, R.arrows (g ≫ f)

Next tactic:
rcases H₂ H₃ (𝟙 Z) with ⟨W, h, H₄⟩
/-- The dense Grothendieck topology.

See https://ncatlab.org/nlab/show/dense+topology, or [MM92] Chapter III, Section 2, example (e).
-/
def dense : GrothendieckTopology C
    where
  sieves X S := ∀ {Y : C} (f : Y ⟶ X), ∃ (Z : _) (g : Z ⟶ Y), S (g ≫ f)
  top_mem' X Y f := ⟨Y, 𝟙 Y, ⟨⟩⟩
  pullback_stable' := by
    intro X Y S h H Z f
    rcases H (f ≫ h) with ⟨W, g, H'⟩
    exact ⟨W, g, by simpa⟩
  transitive' := by
    intro X S H₁ R H₂ Y f
    rcases H₁ f with ⟨Z, g, H₃⟩
    rcases H₂ H₃ (𝟙 Z) with ⟨W, h, H₄⟩
    

Proof state:
case intro.intro.intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R✝ : Sieve X✝
J : GrothendieckTopology C
X : C
S : Sieve X
H₁ : S ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) X
R : Sieve X
H₂ :
  ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, S.arrows f → Sieve.pullback f R ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) Y
Y : C
f : Y ⟶ X
Z : C
g : Z ⟶ Y
H₃ : S.arrows (g ≫ f)
W : C
h : W ⟶ Z
H₄ : (Sieve.pullback (g ≫ f) R).arrows (h ≫ 𝟙 Z)
⊢ ∃ Z g, R.arrows (g ≫ f)

Next tactic:
exact ⟨W, h ≫ g, by simpa using H₄⟩
/-- The dense Grothendieck topology.

See https://ncatlab.org/nlab/show/dense+topology, or [MM92] Chapter III, Section 2, example (e).
-/
def dense : GrothendieckTopology C
    where
  sieves X S := ∀ {Y : C} (f : Y ⟶ X), ∃ (Z : _) (g : Z ⟶ Y), S (g ≫ f)
  top_mem' X Y f := ⟨Y, 𝟙 Y, ⟨⟩⟩
  pullback_stable' := by
    intro X Y S h H Z f
    rcases H (f ≫ h) with ⟨W, g, H'⟩
    exact ⟨W, g, by simpa⟩
  transitive' := by
    intro X S H₁ R H₂ Y f
    rcases H₁ f with ⟨Z, g, H₃⟩
    rcases H₂ H₃ (𝟙 Z) with ⟨W, h, H₄⟩
    exact ⟨W, h ≫ g, by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R✝ : Sieve X✝
J : GrothendieckTopology C
X : C
S : Sieve X
H₁ : S ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) X
R : Sieve X
H₂ :
  ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, S.arrows f → Sieve.pullback f R ∈ (fun X S => ∀ {Y : C} (f : Y ⟶ X), ∃ Z g, S.arrows (g ≫ f)) Y
Y : C
f : Y ⟶ X
Z : C
g : Z ⟶ Y
H₃ : S.arrows (g ≫ f)
W : C
h : W ⟶ Z
H₄ : (Sieve.pullback (g ≫ f) R).arrows (h ≫ 𝟙 Z)
⊢ R.arrows ((h ≫ g) ≫ f)

Next tactic:
simpa using H₄
/-- The atomic Grothendieck topology: a sieve is covering iff it is nonempty.
For the pullback stability condition, we need the right Ore condition to hold.

See https://ncatlab.org/nlab/show/atomic+site, or [MM92] Chapter III, Section 2, example (f).
-/
def atomic (hro : RightOreCondition C) : GrothendieckTopology C
    where
  sieves X S := ∃ (Y : _) (f : Y ⟶ X), S f
  top_mem' X := ⟨_, 𝟙 _, ⟨⟩⟩
  pullback_stable' := by
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y : C
S R : Sieve X
J : GrothendieckTopology C
hro : RightOreCondition C
⊢ ∀ ⦃X Y : C⦄ ⦃S : Sieve X⦄ (f : Y ⟶ X),
    S ∈ (fun X S => ∃ Y f, S.arrows f) X → Sieve.pullback f S ∈ (fun X S => ∃ Y f, S.arrows f) Y

Next tactic:
rintro X Y S h ⟨Z, f, hf⟩
/-- The atomic Grothendieck topology: a sieve is covering iff it is nonempty.
For the pullback stability condition, we need the right Ore condition to hold.

See https://ncatlab.org/nlab/show/atomic+site, or [MM92] Chapter III, Section 2, example (f).
-/
def atomic (hro : RightOreCondition C) : GrothendieckTopology C
    where
  sieves X S := ∃ (Y : _) (f : Y ⟶ X), S f
  top_mem' X := ⟨_, 𝟙 _, ⟨⟩⟩
  pullback_stable' := by
    rintro X Y S h ⟨Z, f, hf⟩
    

Proof state:
case intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
hro : RightOreCondition C
X Y : C
S : Sieve X
h : Y ⟶ X
Z : C
f : Z ⟶ X
hf : S.arrows f
⊢ Sieve.pullback h S ∈ (fun X S => ∃ Y f, S.arrows f) Y

Next tactic:
rcases hro h f with ⟨W, g, k, comm⟩
/-- The atomic Grothendieck topology: a sieve is covering iff it is nonempty.
For the pullback stability condition, we need the right Ore condition to hold.

See https://ncatlab.org/nlab/show/atomic+site, or [MM92] Chapter III, Section 2, example (f).
-/
def atomic (hro : RightOreCondition C) : GrothendieckTopology C
    where
  sieves X S := ∃ (Y : _) (f : Y ⟶ X), S f
  top_mem' X := ⟨_, 𝟙 _, ⟨⟩⟩
  pullback_stable' := by
    rintro X Y S h ⟨Z, f, hf⟩
    rcases hro h f with ⟨W, g, k, comm⟩
    

Proof state:
case intro.intro.intro.intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
hro : RightOreCondition C
X Y : C
S : Sieve X
h : Y ⟶ X
Z : C
f : Z ⟶ X
hf : S.arrows f
W : C
g : W ⟶ Y
k : W ⟶ Z
comm : g ≫ h = k ≫ f
⊢ Sieve.pullback h S ∈ (fun X S => ∃ Y f, S.arrows f) Y

Next tactic:
refine' ⟨_, g, _⟩
/-- The atomic Grothendieck topology: a sieve is covering iff it is nonempty.
For the pullback stability condition, we need the right Ore condition to hold.

See https://ncatlab.org/nlab/show/atomic+site, or [MM92] Chapter III, Section 2, example (f).
-/
def atomic (hro : RightOreCondition C) : GrothendieckTopology C
    where
  sieves X S := ∃ (Y : _) (f : Y ⟶ X), S f
  top_mem' X := ⟨_, 𝟙 _, ⟨⟩⟩
  pullback_stable' := by
    rintro X Y S h ⟨Z, f, hf⟩
    rcases hro h f with ⟨W, g, k, comm⟩
    refine' ⟨_, g, _⟩
    

Proof state:
case intro.intro.intro.intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
hro : RightOreCondition C
X Y : C
S : Sieve X
h : Y ⟶ X
Z : C
f : Z ⟶ X
hf : S.arrows f
W : C
g : W ⟶ Y
k : W ⟶ Z
comm : g ≫ h = k ≫ f
⊢ (Sieve.pullback h S).arrows g

Next tactic:
simp [comm, hf]
/-- The atomic Grothendieck topology: a sieve is covering iff it is nonempty.
For the pullback stability condition, we need the right Ore condition to hold.

See https://ncatlab.org/nlab/show/atomic+site, or [MM92] Chapter III, Section 2, example (f).
-/
def atomic (hro : RightOreCondition C) : GrothendieckTopology C
    where
  sieves X S := ∃ (Y : _) (f : Y ⟶ X), S f
  top_mem' X := ⟨_, 𝟙 _, ⟨⟩⟩
  pullback_stable' := by
    rintro X Y S h ⟨Z, f, hf⟩
    rcases hro h f with ⟨W, g, k, comm⟩
    refine' ⟨_, g, _⟩
    simp [comm, hf]
  transitive' := by
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y : C
S R : Sieve X
J : GrothendieckTopology C
hro : RightOreCondition C
⊢ ∀ ⦃X : C⦄ ⦃S : Sieve X⦄,
    S ∈ (fun X S => ∃ Y f, S.arrows f) X →
      ∀ (R : Sieve X),
        (∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, S.arrows f → Sieve.pullback f R ∈ (fun X S => ∃ Y f, S.arrows f) Y) →
          R ∈ (fun X S => ∃ Y f, S.arrows f) X

Next tactic:
rintro X S ⟨Y, f, hf⟩ R h
/-- The atomic Grothendieck topology: a sieve is covering iff it is nonempty.
For the pullback stability condition, we need the right Ore condition to hold.

See https://ncatlab.org/nlab/show/atomic+site, or [MM92] Chapter III, Section 2, example (f).
-/
def atomic (hro : RightOreCondition C) : GrothendieckTopology C
    where
  sieves X S := ∃ (Y : _) (f : Y ⟶ X), S f
  top_mem' X := ⟨_, 𝟙 _, ⟨⟩⟩
  pullback_stable' := by
    rintro X Y S h ⟨Z, f, hf⟩
    rcases hro h f with ⟨W, g, k, comm⟩
    refine' ⟨_, g, _⟩
    simp [comm, hf]
  transitive' := by
    rintro X S ⟨Y, f, hf⟩ R h
    

Proof state:
case intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R✝ : Sieve X✝
J : GrothendieckTopology C
hro : RightOreCondition C
X : C
S : Sieve X
Y : C
f : Y ⟶ X
hf : S.arrows f
R : Sieve X
h : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, S.arrows f → Sieve.pullback f R ∈ (fun X S => ∃ Y f, S.arrows f) Y
⊢ R ∈ (fun X S => ∃ Y f, S.arrows f) X

Next tactic:
rcases h hf with ⟨Z, g, hg⟩
/-- The atomic Grothendieck topology: a sieve is covering iff it is nonempty.
For the pullback stability condition, we need the right Ore condition to hold.

See https://ncatlab.org/nlab/show/atomic+site, or [MM92] Chapter III, Section 2, example (f).
-/
def atomic (hro : RightOreCondition C) : GrothendieckTopology C
    where
  sieves X S := ∃ (Y : _) (f : Y ⟶ X), S f
  top_mem' X := ⟨_, 𝟙 _, ⟨⟩⟩
  pullback_stable' := by
    rintro X Y S h ⟨Z, f, hf⟩
    rcases hro h f with ⟨W, g, k, comm⟩
    refine' ⟨_, g, _⟩
    simp [comm, hf]
  transitive' := by
    rintro X S ⟨Y, f, hf⟩ R h
    rcases h hf with ⟨Z, g, hg⟩
    

Proof state:
case intro.intro.intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R✝ : Sieve X✝
J : GrothendieckTopology C
hro : RightOreCondition C
X : C
S : Sieve X
Y : C
f : Y ⟶ X
hf : S.arrows f
R : Sieve X
h : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄, S.arrows f → Sieve.pullback f R ∈ (fun X S => ∃ Y f, S.arrows f) Y
Z : C
g : Z ⟶ Y
hg : (Sieve.pullback f R).arrows g
⊢ R ∈ (fun X S => ∃ Y f, S.arrows f) X

Next tactic:
exact ⟨_, _, hg⟩
instance : OrderTop (J.Cover X) :=
  { (inferInstance : Preorder (J.Cover X)) with
    top := ⟨⊤, J.top_mem _⟩
    le_top := fun S Y f _ => by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y✝ : C
S✝ R : Sieve X
J : GrothendieckTopology C
src✝ : Preorder (Cover J X) := inferInstance
S : Cover J X
Y : C
f : Y ⟶ X
x✝ : ((fun a => ↑a) S).arrows f
⊢ ((fun a => ↑a) ⊤).arrows f

Next tactic:
tauto
instance : SemilatticeInf (J.Cover X) :=
  { (inferInstance : Preorder _) with
    inf := fun S T => ⟨S.sieve ⊓ T.sieve,
      J.intersection_covering S.condition T.condition⟩
    le_antisymm := fun S T h1 h2 => ext _ _ fun {Y} f => ⟨by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y✝ : C
S✝ R : Sieve X
J : GrothendieckTopology C
src✝ : Preorder (Cover J X) := inferInstance
S T : Cover J X
h1 : S ≤ T
h2 : T ≤ S
Y : C
f : Y ⟶ X
⊢ (sieve S).arrows f → (sieve T).arrows f

Next tactic:
apply h1
instance : SemilatticeInf (J.Cover X) :=
  { (inferInstance : Preorder _) with
    inf := fun S T => ⟨S.sieve ⊓ T.sieve,
      J.intersection_covering S.condition T.condition⟩
    le_antisymm := fun S T h1 h2 => ext _ _ fun {Y} f => ⟨by apply h1, by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y✝ : C
S✝ R : Sieve X
J : GrothendieckTopology C
src✝ : Preorder (Cover J X) := inferInstance
S T : Cover J X
h1 : S ≤ T
h2 : T ≤ S
Y : C
f : Y ⟶ X
⊢ (sieve T).arrows f → (sieve S).arrows f

Next tactic:
apply h2
/-- A relation of `S.pullback f` gives rise to a relation of `S`. -/
@[simps]
def Relation.base {f : Y ⟶ X} {S : J.Cover X} (I : (S.pullback f).Relation) : S.Relation :=
  ⟨_, _, _, I.g₁, I.g₂, I.f₁ ≫ f, I.f₂ ≫ f, I.h₁, I.h₂, by 

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

Next tactic:
simp [reassoc_of% I.w]
/-- The isomorphism between `S` and the pullback of `S` w.r.t. the identity. -/
def pullbackId (S : J.Cover X) : S.pullback (𝟙 X) ≅ S :=
  eqToIso <| Cover.ext _ _ fun Y f => by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y✝ : C
S✝ R : Sieve X
J : GrothendieckTopology C
S : Cover J X
Y : C
f : Y ⟶ X
⊢ (sieve (pullback S (𝟙 X))).arrows f ↔ (sieve S).arrows f

Next tactic:
simp
/-- Pulling back with respect to a composition is the composition of the pullbacks. -/
def pullbackComp {X Y Z : C} (S : J.Cover X) (f : Z ⟶ Y) (g : Y ⟶ X) :
    S.pullback (f ≫ g) ≅ (S.pullback g).pullback f :=
  eqToIso <| Cover.ext _ _ fun Y f => by 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝¹ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
X Y✝ Z : C
S : Cover J X
f✝ : Z ⟶ Y✝
g : Y✝ ⟶ X
Y : C
f : Y ⟶ Z
⊢ (sieve (pullback S (f✝ ≫ g))).arrows f ↔ (sieve (pullback (pullback S g) f✝)).arrows f

Next tactic:
simp
/-- The canonical moprhism from `S.bind T` to `T`. -/
def bindToBase {X : C} (S : J.Cover X) (T : ∀ I : S.Arrow, J.Cover I.Y) : S.bind T ⟶ S :=
  homOfLE <| by
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
X : C
S : Cover J X
T : (I : Arrow S) → Cover J I.Y
⊢ bind S T ≤ S

Next tactic:
rintro Y f ⟨Z, e1, e2, h1, _, h3⟩
/-- The canonical moprhism from `S.bind T` to `T`. -/
def bindToBase {X : C} (S : J.Cover X) (T : ∀ I : S.Arrow, J.Cover I.Y) : S.bind T ⟶ S :=
  homOfLE <| by
    rintro Y f ⟨Z, e1, e2, h1, _, h3⟩
    

Proof state:
case intro.intro.intro.intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
X : C
S : Cover J X
T : (I : Arrow S) → Cover J I.Y
Y : C
f : Y ⟶ X
Z : C
e1 : Y ⟶ Z
e2 : Z ⟶ X
h1 : (sieve S).arrows e2
left✝ : ((fun Y f hf => sieve (T { Y := Y, f := f, hf := hf })) Z e2 h1).arrows e1
h3 : e1 ≫ e2 = f
⊢ ((fun a => ↑a) S).arrows f

Next tactic:
rw [← h3]
/-- The canonical moprhism from `S.bind T` to `T`. -/
def bindToBase {X : C} (S : J.Cover X) (T : ∀ I : S.Arrow, J.Cover I.Y) : S.bind T ⟶ S :=
  homOfLE <| by
    rintro Y f ⟨Z, e1, e2, h1, _, h3⟩
    rw [← h3]
    

Proof state:
case intro.intro.intro.intro.intro
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
X : C
S : Cover J X
T : (I : Arrow S) → Cover J I.Y
Y : C
f : Y ⟶ X
Z : C
e1 : Y ⟶ Z
e2 : Z ⟶ X
h1 : (sieve S).arrows e2
left✝ : ((fun Y f hf => sieve (T { Y := Y, f := f, hf := hf })) Z e2 h1).arrows e1
h3 : e1 ≫ e2 = f
⊢ ((fun a => ↑a) S).arrows (e1 ≫ e2)

Next tactic:
apply Sieve.downward_closed
/-- The canonical moprhism from `S.bind T` to `T`. -/
def bindToBase {X : C} (S : J.Cover X) (T : ∀ I : S.Arrow, J.Cover I.Y) : S.bind T ⟶ S :=
  homOfLE <| by
    rintro Y f ⟨Z, e1, e2, h1, _, h3⟩
    rw [← h3]
    apply Sieve.downward_closed
    

Proof state:
case intro.intro.intro.intro.intro.x
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ : C
S✝ R : Sieve X✝
J : GrothendieckTopology C
X : C
S : Cover J X
T : (I : Arrow S) → Cover J I.Y
Y : C
f : Y ⟶ X
Z : C
e1 : Y ⟶ Z
e2 : Z ⟶ X
h1 : (sieve S).arrows e2
left✝ : ((fun Y f hf => sieve (T { Y := Y, f := f, hf := hf })) Z e2 h1).arrows e1
h3 : e1 ≫ e2 = f
⊢ ((fun a => ↑a) S).arrows e2

Next tactic:
exact h1
/-- The natural multifork associated to `S : J.Cover X` for a presheaf `P`.
Saying that this multifork is a limit is essentially equivalent to the sheaf condition at the
given object for the given covering sieve. See `Sheaf.lean` for an equivalent sheaf condition
using this.
-/
abbrev multifork {D : Type u₁} [Category.{v₁} D] (S : J.Cover X) (P : Cᵒᵖ ⥤ D) :
    Limits.Multifork (S.index P) :=
  Limits.Multifork.ofι _ (P.obj (Opposite.op X)) (fun I => P.map I.f.op)
    (by
      

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
X Y : C
S✝ R : Sieve X
J : GrothendieckTopology C
D : Type u₁
inst✝ : Category.{v₁, u₁} D
S : Cover J X
P : Cᵒᵖ ⥤ D
⊢ ∀ (b : (index S P).R),
    (fun I => P.map I.f.op) (Limits.MulticospanIndex.fstTo (index S P) b) ≫ Limits.MulticospanIndex.fst (index S P) b =
      (fun I => P.map I.f.op) (Limits.MulticospanIndex.sndTo (index S P) b) ≫ Limits.MulticospanIndex.snd (index S P) b

Next tactic:
intro I
/-- The natural multifork associated to `S : J.Cover X` for a presheaf `P`.
Saying that this multifork is a limit is essentially equivalent to the sheaf condition at the
given object for the given covering sieve. See `Sheaf.lean` for an equivalent sheaf condition
using this.
-/
abbrev multifork {D : Type u₁} [Category.{v₁} D] (S : J.Cover X) (P : Cᵒᵖ ⥤ D) :
    Limits.Multifork (S.index P) :=
  Limits.Multifork.ofι _ (P.obj (Opposite.op X)) (fun I => P.map I.f.op)
    (by
      intro I
      

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
X Y : C
S✝ R : Sieve X
J : GrothendieckTopology C
D : Type u₁
inst✝ : Category.{v₁, u₁} D
S : Cover J X
P : Cᵒᵖ ⥤ D
I : (index S P).R
⊢ (fun I => P.map I.f.op) (Limits.MulticospanIndex.fstTo (index S P) I) ≫ Limits.MulticospanIndex.fst (index S P) I =
    (fun I => P.map I.f.op) (Limits.MulticospanIndex.sndTo (index S P) I) ≫ Limits.MulticospanIndex.snd (index S P) I

Next tactic:
dsimp [index]
/-- The natural multifork associated to `S : J.Cover X` for a presheaf `P`.
Saying that this multifork is a limit is essentially equivalent to the sheaf condition at the
given object for the given covering sieve. See `Sheaf.lean` for an equivalent sheaf condition
using this.
-/
abbrev multifork {D : Type u₁} [Category.{v₁} D] (S : J.Cover X) (P : Cᵒᵖ ⥤ D) :
    Limits.Multifork (S.index P) :=
  Limits.Multifork.ofι _ (P.obj (Opposite.op X)) (fun I => P.map I.f.op)
    (by
      intro I
      dsimp [index]
      

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
X Y : C
S✝ R : Sieve X
J : GrothendieckTopology C
D : Type u₁
inst✝ : Category.{v₁, u₁} D
S : Cover J X
P : Cᵒᵖ ⥤ D
I : (index S P).R
⊢ P.map I.f₁.op ≫ P.map I.g₁.op = P.map I.f₂.op ≫ P.map I.g₂.op

Next tactic:
simp only [← P.map_comp, ← op_comp, I.w]
/-- The canonical map from `P.obj (op X)` to the multiequalizer associated to a covering sieve,
assuming such a multiequalizer exists. This will be used in `Sheaf.lean` to provide an equivalent
sheaf condition in terms of multiequalizers. -/
noncomputable abbrev toMultiequalizer {D : Type u₁} [Category.{v₁} D] (S : J.Cover X)
    (P : Cᵒᵖ ⥤ D) [Limits.HasMultiequalizer (S.index P)] :
    P.obj (Opposite.op X) ⟶ Limits.multiequalizer (S.index P) :=
  Limits.Multiequalizer.lift _ _ (fun I => P.map I.f.op)
    (by
      

Proof state:
C : Type u
inst✝² : Category.{v, u} C
X Y : C
S✝ R : Sieve X
J : GrothendieckTopology C
D : Type u₁
inst✝¹ : Category.{v₁, u₁} D
S : Cover J X
P : Cᵒᵖ ⥤ D
inst✝ : Limits.HasMultiequalizer (index S P)
⊢ ∀ (b : (index S P).R),
    (fun I => P.map I.f.op) (Limits.MulticospanIndex.fstTo (index S P) b) ≫ Limits.MulticospanIndex.fst (index S P) b =
      (fun I => P.map I.f.op) (Limits.MulticospanIndex.sndTo (index S P) b) ≫ Limits.MulticospanIndex.snd (index S P) b

Next tactic:
intro I
/-- The canonical map from `P.obj (op X)` to the multiequalizer associated to a covering sieve,
assuming such a multiequalizer exists. This will be used in `Sheaf.lean` to provide an equivalent
sheaf condition in terms of multiequalizers. -/
noncomputable abbrev toMultiequalizer {D : Type u₁} [Category.{v₁} D] (S : J.Cover X)
    (P : Cᵒᵖ ⥤ D) [Limits.HasMultiequalizer (S.index P)] :
    P.obj (Opposite.op X) ⟶ Limits.multiequalizer (S.index P) :=
  Limits.Multiequalizer.lift _ _ (fun I => P.map I.f.op)
    (by
      intro I
      

Proof state:
C : Type u
inst✝² : Category.{v, u} C
X Y : C
S✝ R : Sieve X
J : GrothendieckTopology C
D : Type u₁
inst✝¹ : Category.{v₁, u₁} D
S : Cover J X
P : Cᵒᵖ ⥤ D
inst✝ : Limits.HasMultiequalizer (index S P)
I : (index S P).R
⊢ (fun I => P.map I.f.op) (Limits.MulticospanIndex.fstTo (index S P) I) ≫ Limits.MulticospanIndex.fst (index S P) I =
    (fun I => P.map I.f.op) (Limits.MulticospanIndex.sndTo (index S P) I) ≫ Limits.MulticospanIndex.snd (index S P) I

Next tactic:
dsimp only [index, Relation.fst, Relation.snd]
/-- The canonical map from `P.obj (op X)` to the multiequalizer associated to a covering sieve,
assuming such a multiequalizer exists. This will be used in `Sheaf.lean` to provide an equivalent
sheaf condition in terms of multiequalizers. -/
noncomputable abbrev toMultiequalizer {D : Type u₁} [Category.{v₁} D] (S : J.Cover X)
    (P : Cᵒᵖ ⥤ D) [Limits.HasMultiequalizer (S.index P)] :
    P.obj (Opposite.op X) ⟶ Limits.multiequalizer (S.index P) :=
  Limits.Multiequalizer.lift _ _ (fun I => P.map I.f.op)
    (by
      intro I
      dsimp only [index, Relation.fst, Relation.snd]
      

Proof state:
C : Type u
inst✝² : Category.{v, u} C
X Y : C
S✝ R : Sieve X
J : GrothendieckTopology C
D : Type u₁
inst✝¹ : Category.{v₁, u₁} D
S : Cover J X
P : Cᵒᵖ ⥤ D
inst✝ : Limits.HasMultiequalizer (index S P)
I : (index S P).R
⊢ P.map I.f₁.op ≫ P.map I.g₁.op = P.map I.f₂.op ≫ P.map I.g₂.op

Next tactic:
simp only [← P.map_comp, ← op_comp, I.w]
@[simp]
theorem zip_swap : ∀ (l₁ : List α) (l₂ : List β), (zip l₁ l₂).map Prod.swap = zip l₂ l₁
  | [], l₂ => zip_nil_right.symm
  | l₁, [] => by 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
l₁ : List α
⊢ map Prod.swap (zip l₁ []) = zip [] l₁

Next tactic:
rw [zip_nil_right]
@[simp]
theorem zip_swap : ∀ (l₁ : List α) (l₂ : List β), (zip l₁ l₂).map Prod.swap = zip l₂ l₁
  | [], l₂ => zip_nil_right.symm
  | l₁, [] => by rw [zip_nil_right]; 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
l₁ : List α
⊢ map Prod.swap [] = zip [] l₁

Next tactic:
rfl
@[simp]
theorem zip_swap : ∀ (l₁ : List α) (l₂ : List β), (zip l₁ l₂).map Prod.swap = zip l₂ l₁
  | [], l₂ => zip_nil_right.symm
  | l₁, [] => by rw [zip_nil_right]; rfl
  | a :: l₁, b :: l₂ => by
    

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
a : α
l₁ : List α
b : β
l₂ : List β
⊢ map Prod.swap (zip (a :: l₁) (b :: l₂)) = zip (b :: l₂) (a :: l₁)

Next tactic:
simp only [zip_cons_cons, map_cons, zip_swap l₁ l₂, Prod.swap_prod_mk]
theorem forall_zipWith {f : α → β → γ} {p : γ → Prop} :
    ∀ {l₁ : List α} {l₂ : List β}, length l₁ = length l₂ →
      (Forall p (zipWith f l₁ l₂) ↔ Forall₂ (fun x y => p (f x y)) l₁ l₂)
  | [], [], _ => by 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
p : γ → Prop
x✝ : length [] = length []
⊢ Forall p (zipWith f [] []) ↔ Forall₂ (fun x y => p (f x y)) [] []

Next tactic:
simp