l3lab/ntp-mathlib huggingface dataset:
theorem isSheaf_iff_multifork :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), Nonempty (IsLimit (S.multifork P)) := by
  refine' ⟨fun hP X S => ⟨isLimitOfIsSheaf _ _ _ hP⟩, _⟩
  intro h E X S hS x hx
  let T : J.Cover X := ⟨S, hS⟩
  obtain ⟨hh⟩ := h _ T
  let K : Multifork (T.index P) := Multifork.ofι _ E (fun I => x I.f I.hf) fun I => hx _ _ _ _ I.w
  use hh.lift K
  dsimp; constructor
  · intro Y f hf
    apply hh.fac K (WalkingMulticospan.left ⟨Y, f, hf⟩)
  · intro e he
    apply hh.uniq K
    rintro (a | b)
    · apply he
    · rw [← K.w (WalkingMulticospan.Hom.fst b), ←
        (T.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      congr 1
      

Proof state:
case h.right.x.right.e_a
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
A : Type u₂
inst✝² : Category.{v₂, u₂} A
A' : Type u₂
inst✝¹ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
h : ∀ (X : C) (S : GrothendieckTopology.Cover J X), Nonempty (IsLimit (GrothendieckTopology.Cover.multifork S P))
E : A
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
x : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op E)) S.arrows
hx : Presieve.FamilyOfElements.Compatible x
T : GrothendieckTopology.Cover J X := { val := S, property := hS }
hh : IsLimit (GrothendieckTopology.Cover.multifork T P)
K : Multifork (GrothendieckTopology.Cover.index T P) :=
  Multifork.ofι (GrothendieckTopology.Cover.index T P) E
    (fun I => x I.f (_ : (GrothendieckTopology.Cover.sieve T).arrows I.f))
    (_ :
      ∀ (I : (GrothendieckTopology.Cover.index T P).R),
        (P ⋙ coyoneda.obj (op E)).map I.g₁.op
            (x (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index T P) I).f
              (_ :
                (GrothendieckTopology.Cover.sieve T).arrows
                  (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index T P) I).f)) =
          (P ⋙ coyoneda.obj (op E)).map I.g₂.op
            (x (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index T P) I).f
              (_ :
                (GrothendieckTopology.Cover.sieve T).arrows
                  (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index T P) I).f)))
e : E ⟶ P.obj (op X)
he : Presieve.FamilyOfElements.IsAmalgamation x e
b : (GrothendieckTopology.Cover.index T P).R
⊢ e ≫
      (GrothendieckTopology.Cover.multifork T P).π.app
        (WalkingMulticospan.left (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index T P) b)) =
    K.π.app (WalkingMulticospan.left (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index T P) b))

Next tactic:
apply he
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  

Proof state:
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
⊢ IsSheaf J P ↔ ∀ (X : C) (S : GrothendieckTopology.Cover J X), IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)

Next tactic:
rw [isSheaf_iff_multifork]
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  

Proof state:
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
⊢ (∀ (X : C) (S : GrothendieckTopology.Cover J X), Nonempty (IsLimit (GrothendieckTopology.Cover.multifork S P))) ↔
    ∀ (X : C) (S : GrothendieckTopology.Cover J X), IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)

Next tactic:
refine' forall₂_congr fun X S => ⟨_, _⟩
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · 

Proof state:
case refine'_1
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
⊢ Nonempty (IsLimit (GrothendieckTopology.Cover.multifork S P)) →
    IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)

Next tactic:
rintro ⟨h⟩
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    

Proof state:
case refine'_1.intro
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsLimit (GrothendieckTopology.Cover.multifork S P)
⊢ IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)

Next tactic:
let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    

Proof state:
case refine'_1.intro
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsLimit (GrothendieckTopology.Cover.multifork S P)
e : P.obj (op X) ≅ multiequalizer (GrothendieckTopology.Cover.index S P) :=
  IsLimit.conePointUniqueUpToIso h (limit.isLimit (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P)))
⊢ IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)

Next tactic:
exact (inferInstance : IsIso e.hom)
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · 

Proof state:
case refine'_2
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
⊢ IsIso (GrothendieckTopology.Cover.toMultiequalizer S P) →
    Nonempty (IsLimit (GrothendieckTopology.Cover.multifork S P))

Next tactic:
intro h
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · intro h
    

Proof state:
case refine'_2
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)
⊢ Nonempty (IsLimit (GrothendieckTopology.Cover.multifork S P))

Next tactic:
refine' ⟨IsLimit.ofIsoLimit (limit.isLimit _) (Cones.ext _ _)⟩
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · intro h
    refine' ⟨IsLimit.ofIsoLimit (limit.isLimit _) (Cones.ext _ _)⟩
    · 

Proof state:
case refine'_2.refine'_1
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)
⊢ (limit.cone (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P))).pt ≅
    (GrothendieckTopology.Cover.multifork S P).pt

Next tactic:
apply (@asIso _ _ _ _ _ h).symm
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · intro h
    refine' ⟨IsLimit.ofIsoLimit (limit.isLimit _) (Cones.ext _ _)⟩
    · apply (@asIso _ _ _ _ _ h).symm
    · 

Proof state:
case refine'_2.refine'_2
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)
⊢ ∀ (j : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo),
    (limit.cone (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P))).π.app j =
      (asIso (GrothendieckTopology.Cover.toMultiequalizer S P)).symm.hom ≫
        (GrothendieckTopology.Cover.multifork S P).π.app j

Next tactic:
intro a
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · intro h
    refine' ⟨IsLimit.ofIsoLimit (limit.isLimit _) (Cones.ext _ _)⟩
    · apply (@asIso _ _ _ _ _ h).symm
    · intro a
      

Proof state:
case refine'_2.refine'_2
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)
a : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo
⊢ (limit.cone (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P))).π.app a =
    (asIso (GrothendieckTopology.Cover.toMultiequalizer S P)).symm.hom ≫
      (GrothendieckTopology.Cover.multifork S P).π.app a

Next tactic:
symm
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · intro h
    refine' ⟨IsLimit.ofIsoLimit (limit.isLimit _) (Cones.ext _ _)⟩
    · apply (@asIso _ _ _ _ _ h).symm
    · intro a
      symm
      

Proof state:
case refine'_2.refine'_2
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)
a : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo
⊢ (asIso (GrothendieckTopology.Cover.toMultiequalizer S P)).symm.hom ≫
      (GrothendieckTopology.Cover.multifork S P).π.app a =
    (limit.cone (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P))).π.app a

Next tactic:
erw [IsIso.inv_comp_eq]
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · intro h
    refine' ⟨IsLimit.ofIsoLimit (limit.isLimit _) (Cones.ext _ _)⟩
    · apply (@asIso _ _ _ _ _ h).symm
    · intro a
      symm
      erw [IsIso.inv_comp_eq]
      

Proof state:
case refine'_2.refine'_2
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)
a : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo
⊢ (GrothendieckTopology.Cover.multifork S P).π.app a =
    GrothendieckTopology.Cover.toMultiequalizer S P ≫
      (limit.cone (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P))).π.app a

Next tactic:
dsimp
theorem isSheaf_iff_multiequalizer [∀ (X : C) (S : J.Cover X), HasMultiequalizer (S.index P)] :
    IsSheaf J P ↔ ∀ (X : C) (S : J.Cover X), IsIso (S.toMultiequalizer P) := by
  rw [isSheaf_iff_multifork]
  refine' forall₂_congr fun X S => ⟨_, _⟩
  · rintro ⟨h⟩
    let e : P.obj (op X) ≅ multiequalizer (S.index P) :=
      h.conePointUniqueUpToIso (limit.isLimit _)
    exact (inferInstance : IsIso e.hom)
  · intro h
    refine' ⟨IsLimit.ofIsoLimit (limit.isLimit _) (Cones.ext _ _)⟩
    · apply (@asIso _ _ _ _ _ h).symm
    · intro a
      symm
      erw [IsIso.inv_comp_eq]
      dsimp
      

Proof state:
case refine'_2.refine'_2
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
A : Type u₂
inst✝³ : Category.{v₂, u₂} A
A' : Type u₂
inst✝² : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝¹ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝ : ∀ (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
X : C
S : GrothendieckTopology.Cover J X
h : IsIso (GrothendieckTopology.Cover.toMultiequalizer S P)
a : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo
⊢ (match a with
    | WalkingMulticospan.left a => P.map a.f.op
    | WalkingMulticospan.right b =>
      P.map (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) b).f.op ≫
        MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) b) =
    GrothendieckTopology.Cover.toMultiequalizer S P ≫
      limit.π (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P)) a

Next tactic:
simp
theorem w : forkMap R P ≫ firstMap R P = forkMap R P ≫ secondMap R P := by
  

Proof state:
C : Type u₁
inst✝⁶ : Category.{v₁, u₁} C
A : Type u₂
inst✝⁵ : Category.{v₂, u₂} A
A' : Type u₂
inst✝⁴ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝³ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝² : HasProducts A
inst✝¹ : HasProducts A'
inst✝ : HasPullbacks C
⊢ forkMap R P ≫ firstMap R P = forkMap R P ≫ secondMap R P

Next tactic:
apply limit.hom_ext
theorem w : forkMap R P ≫ firstMap R P = forkMap R P ≫ secondMap R P := by
  apply limit.hom_ext
  

Proof state:
case w
C : Type u₁
inst✝⁶ : Category.{v₁, u₁} C
A : Type u₂
inst✝⁵ : Category.{v₂, u₂} A
A' : Type u₂
inst✝⁴ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝³ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝² : HasProducts A
inst✝¹ : HasProducts A'
inst✝ : HasPullbacks C
⊢ ∀ (j : Discrete (((V : C) × { f // R f }) × (W : C) × { g // R g })),
    (forkMap R P ≫ firstMap R P) ≫
        limit.π (Discrete.functor fun fg => P.obj (op (Limits.pullback ↑fg.1.snd ↑fg.2.snd))) j =
      (forkMap R P ≫ secondMap R P) ≫
        limit.π (Discrete.functor fun fg => P.obj (op (Limits.pullback ↑fg.1.snd ↑fg.2.snd))) j

Next tactic:
rintro ⟨⟨Y, f, hf⟩, ⟨Z, g, hg⟩⟩
theorem w : forkMap R P ≫ firstMap R P = forkMap R P ≫ secondMap R P := by
  apply limit.hom_ext
  rintro ⟨⟨Y, f, hf⟩, ⟨Z, g, hg⟩⟩
  

Proof state:
case w.mk.mk.mk.mk.mk.mk
C : Type u₁
inst✝⁶ : Category.{v₁, u₁} C
A : Type u₂
inst✝⁵ : Category.{v₂, u₂} A
A' : Type u₂
inst✝⁴ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝³ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝² : HasProducts A
inst✝¹ : HasProducts A'
inst✝ : HasPullbacks C
Y : C
f : Y ⟶ U
hf : R f
Z : C
g : Z ⟶ U
hg : R g
⊢ (forkMap R P ≫ firstMap R P) ≫
      limit.π (Discrete.functor fun fg => P.obj (op (Limits.pullback ↑fg.1.snd ↑fg.2.snd)))
        {
          as :=
            ({ fst := Y, snd := { val := f, property := hf } }, { fst := Z, snd := { val := g, property := hg } }) } =
    (forkMap R P ≫ secondMap R P) ≫
      limit.π (Discrete.functor fun fg => P.obj (op (Limits.pullback ↑fg.1.snd ↑fg.2.snd)))
        { as := ({ fst := Y, snd := { val := f, property := hf } }, { fst := Z, snd := { val := g, property := hg } }) }

Next tactic:
simp only [firstMap, secondMap, forkMap, limit.lift_π, limit.lift_π_assoc, assoc, Fan.mk_π_app,
    Subtype.coe_mk]
theorem w : forkMap R P ≫ firstMap R P = forkMap R P ≫ secondMap R P := by
  apply limit.hom_ext
  rintro ⟨⟨Y, f, hf⟩, ⟨Z, g, hg⟩⟩
  simp only [firstMap, secondMap, forkMap, limit.lift_π, limit.lift_π_assoc, assoc, Fan.mk_π_app,
    Subtype.coe_mk]
  

Proof state:
case w.mk.mk.mk.mk.mk.mk
C : Type u₁
inst✝⁶ : Category.{v₁, u₁} C
A : Type u₂
inst✝⁵ : Category.{v₂, u₂} A
A' : Type u₂
inst✝⁴ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝³ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝² : HasProducts A
inst✝¹ : HasProducts A'
inst✝ : HasPullbacks C
Y : C
f : Y ⟶ U
hf : R f
Z : C
g : Z ⟶ U
hg : R g
⊢ P.map f.op ≫ P.map pullback.fst.op = P.map g.op ≫ P.map pullback.snd.op

Next tactic:
rw [← P.map_comp, ← op_comp, pullback.condition]
theorem w : forkMap R P ≫ firstMap R P = forkMap R P ≫ secondMap R P := by
  apply limit.hom_ext
  rintro ⟨⟨Y, f, hf⟩, ⟨Z, g, hg⟩⟩
  simp only [firstMap, secondMap, forkMap, limit.lift_π, limit.lift_π_assoc, assoc, Fan.mk_π_app,
    Subtype.coe_mk]
  rw [← P.map_comp, ← op_comp, pullback.condition]
  

Proof state:
case w.mk.mk.mk.mk.mk.mk
C : Type u₁
inst✝⁶ : Category.{v₁, u₁} C
A : Type u₂
inst✝⁵ : Category.{v₂, u₂} A
A' : Type u₂
inst✝⁴ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝³ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U : C
R : Presieve U
P : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝² : HasProducts A
inst✝¹ : HasProducts A'
inst✝ : HasPullbacks C
Y : C
f : Y ⟶ U
hf : R f
Z : C
g : Z ⟶ U
hg : R g
⊢ P.map (pullback.snd ≫ g).op = P.map g.op ≫ P.map pullback.snd.op

Next tactic:
simp
/-- (Implementation). An auxiliary lemma to convert between sheaf conditions. -/
def isSheafForIsSheafFor' (P : Cᵒᵖ ⥤ A) (s : A ⥤ Type max v₁ u₁)
    [∀ J, PreservesLimitsOfShape (Discrete.{max v₁ u₁} J) s] (U : C) (R : Presieve U) :
    IsLimit (s.mapCone (Fork.ofι _ (w R P))) ≃
      IsLimit (Fork.ofι _ (Equalizer.Presieve.w (P ⋙ s) R)) := by
  

Proof state:
C : Type u₁
inst✝⁷ : Category.{v₁, u₁} C
A : Type u₂
inst✝⁶ : Category.{v₂, u₂} A
A' : Type u₂
inst✝⁵ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝⁴ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U✝ : C
R✝ : Presieve U✝
P✝ : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝³ : HasProducts A
inst✝² : HasProducts A'
inst✝¹ : HasPullbacks C
P : Cᵒᵖ ⥤ A
s : A ⥤ Type (max v₁ u₁)
inst✝ : (J : Type (max v₁ u₁)) → PreservesLimitsOfShape (Discrete J) s
U : C
R : Presieve U
⊢ IsLimit (s.mapCone (Fork.ofι (forkMap R P) (_ : forkMap R P ≫ firstMap R P = forkMap R P ≫ secondMap R P))) ≃
    IsLimit
      (Fork.ofι (Equalizer.forkMap (P ⋙ s) R)
        (_ :
          Equalizer.forkMap (P ⋙ s) R ≫ Equalizer.Presieve.firstMap (P ⋙ s) R =
            Equalizer.forkMap (P ⋙ s) R ≫ Equalizer.Presieve.secondMap (P ⋙ s) R))

Next tactic:
apply Equiv.trans (isLimitMapConeForkEquiv _ _) _
/-- (Implementation). An auxiliary lemma to convert between sheaf conditions. -/
def isSheafForIsSheafFor' (P : Cᵒᵖ ⥤ A) (s : A ⥤ Type max v₁ u₁)
    [∀ J, PreservesLimitsOfShape (Discrete.{max v₁ u₁} J) s] (U : C) (R : Presieve U) :
    IsLimit (s.mapCone (Fork.ofι _ (w R P))) ≃
      IsLimit (Fork.ofι _ (Equalizer.Presieve.w (P ⋙ s) R)) := by
  apply Equiv.trans (isLimitMapConeForkEquiv _ _) _
  

Proof state:
C : Type u₁
inst✝⁷ : Category.{v₁, u₁} C
A : Type u₂
inst✝⁶ : Category.{v₂, u₂} A
A' : Type u₂
inst✝⁵ : Category.{max v₁ u₁, u₂} A'
B : Type u₃
inst✝⁴ : Category.{v₃, u₃} B
J : GrothendieckTopology C
U✝ : C
R✝ : Presieve U✝
P✝ : Cᵒᵖ ⥤ A
P' : Cᵒᵖ ⥤ A'
inst✝³ : HasProducts A
inst✝² : HasProducts A'
inst✝¹ : HasPullbacks C
P : Cᵒᵖ ⥤ A
s : A ⥤ Type (max v₁ u₁)
inst✝ : (J : Type (max v₁ u₁)) → PreservesLimitsOfShape (Discrete J) s
U : C
R : Presieve U
⊢ IsLimit
      (Fork.ofι (s.map (forkMap R P))
        (_ : s.map (forkMap R P) ≫ s.map (firstMap R P) = s.map (forkMap R P) ≫ s.map (secondMap R P))) ≃
    IsLimit
      (Fork.ofι (Equalizer.forkMap (P ⋙ s) R)
        (_ :
          Equalizer.forkMap (P ⋙ s) R ≫ Equalizer.Presieve.firstMap (P ⋙ s) R =
            Equalizer.forkMap (P ⋙ s) R ≫ Equalizer.Presieve.secondMap (P ⋙ s) R))

Next tactic:
apply (IsLimit.postcomposeHomEquiv _ _).symm.trans (IsLimit.equivIsoLimit _)