l3lab/ntp-mathlib huggingface dataset:
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    

Proof state:
case left
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x y : (forget D).obj ((plusObj J P).obj (op X))
I : Cover.Arrow S
h :
  ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) x)) I =
    ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) y)) I
⊢ ((plusObj J P).map I.f.op) x = ((plusObj J P).map I.f.op) y

Next tactic:
convert h
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> 

Proof state:
case h.e'_2
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x y : (forget D).obj ((plusObj J P).obj (op X))
I : Cover.Arrow S
h :
  ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) x)) I =
    ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) y)) I
⊢ ((plusObj J P).map I.f.op) x =
    ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) x)) I

Next tactic:
erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι]
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> 

Proof state:
case h.e'_3
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x y : (forget D).obj ((plusObj J P).obj (op X))
I : Cover.Arrow S
h :
  ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) x)) I =
    ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) y)) I
⊢ ((plusObj J P).map I.f.op) y =
    ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) y)) I

Next tactic:
erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι]
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> 

Proof state:
case h.e'_2
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x y : (forget D).obj ((plusObj J P).obj (op X))
I : Cover.Arrow S
h :
  ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) x)) I =
    ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) y)) I
⊢ ((plusObj J P).map I.f.op) x = ((plusObj J P).map I.f.op) x

Next tactic:
rfl
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> 

Proof state:
case h.e'_3
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x y : (forget D).obj ((plusObj J P).obj (op X))
I : Cover.Arrow S
h :
  ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) x)) I =
    ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) y)) I
⊢ ((plusObj J P).map I.f.op) y = ((plusObj J P).map I.f.op) y

Next tactic:
rfl
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · 

Proof state:
case right
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
⊢ Function.Surjective ((forget D).map (Cover.toMultiequalizer S (plusObj J P)))

Next tactic:
rintro (x : (multiequalizer (S.index _) : D))
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    

Proof state:
case right
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
⊢ ∃ a, (forget D).map (Cover.toMultiequalizer S (plusObj J P)) a = x

Next tactic:
obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
    

Proof state:
case right.intro
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
t : (forget D).obj ((plusObj J P).obj (op X))
ht : Meq.mk S t = (Meq.equiv (plusObj J P) S) x
⊢ ∃ a, (forget D).map (Cover.toMultiequalizer S (plusObj J P)) a = x

Next tactic:
use t
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
    use t
    

Proof state:
case h
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
t : (forget D).obj ((plusObj J P).obj (op X))
ht : Meq.mk S t = (Meq.equiv (plusObj J P) S) x
⊢ (forget D).map (Cover.toMultiequalizer S (plusObj J P)) t = x

Next tactic:
apply (Meq.equiv _ _).injective
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
    use t
    apply (Meq.equiv _ _).injective
    

Proof state:
case h.a
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
t : (forget D).obj ((plusObj J P).obj (op X))
ht : Meq.mk S t = (Meq.equiv (plusObj J P) S) x
⊢ (Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) t) =
    (Meq.equiv (plusObj J P) S) x

Next tactic:
rw [← ht]
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
    use t
    apply (Meq.equiv _ _).injective
    rw [← ht]
    

Proof state:
case h.a
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
t : (forget D).obj ((plusObj J P).obj (op X))
ht : Meq.mk S t = (Meq.equiv (plusObj J P) S) x
⊢ (Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) t) = Meq.mk S t

Next tactic:
ext i
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
    use t
    apply (Meq.equiv _ _).injective
    rw [← ht]
    ext i
    

Proof state:
case h.a.h
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
t : (forget D).obj ((plusObj J P).obj (op X))
ht : Meq.mk S t = (Meq.equiv (plusObj J P) S) x
i : Cover.Arrow S
⊢ ↑((Meq.equiv (plusObj J P) S) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) t)) i = ↑(Meq.mk S t) i

Next tactic:
dsimp
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
    use t
    apply (Meq.equiv _ _).injective
    rw [← ht]
    ext i
    dsimp
    

Proof state:
case h.a.h
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
t : (forget D).obj ((plusObj J P).obj (op X))
ht : Meq.mk S t = (Meq.equiv (plusObj J P) S) x
i : Cover.Arrow S
⊢ (Multiequalizer.ι (Cover.index S (plusObj J P)) i) ((forget D).map (Cover.toMultiequalizer S (plusObj J P)) t) =
    ↑(Meq.mk S t) i

Next tactic:
erw [← comp_apply]
/-- If `P` is separated, then `P⁺` is a sheaf. -/
theorem isSheaf_of_sep (P : Cᵒᵖ ⥤ D)
    (hsep :
      ∀ (X : C) (S : J.Cover X) (x y : P.obj (op X)),
        (∀ I : S.Arrow, P.map I.f.op x = P.map I.f.op y) → x = y) :
    Presheaf.IsSheaf J (J.plusObj P) := by
  rw [Presheaf.isSheaf_iff_multiequalizer]
  intro X S
  apply @isIso_of_reflects_iso _ _ _ _ _ _ _ (forget D) ?_
  rw [isIso_iff_bijective]
  constructor
  · intro x y h
    apply sep P S _ _
    intro I
    apply_fun Meq.equiv _ _ at h
    apply_fun fun e => e I at h
    convert h <;> erw [Meq.equiv_apply, ← comp_apply, Multiequalizer.lift_ι] <;> rfl
  · rintro (x : (multiequalizer (S.index _) : D))
    obtain ⟨t, ht⟩ := exists_of_sep P hsep X S (Meq.equiv _ _ x)
    use t
    apply (Meq.equiv _ _).injective
    rw [← ht]
    ext i
    dsimp
    erw [← comp_apply]
    

Proof state:
case h.a.h
C : Type u
inst✝⁷ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁶ : Category.{max v u, w} D
inst✝⁵ : ConcreteCategory D
inst✝⁴ : PreservesLimits (forget D)
inst✝³ : ∀ (X : C), HasColimitsOfShape (Cover J X)ᵒᵖ D
inst✝² : ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : Cover J X), HasMultiequalizer (Cover.index S P)
inst✝¹ : (X : C) → PreservesColimitsOfShape (Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
P : Cᵒᵖ ⥤ D
hsep :
  ∀ (X : C) (S : Cover J X) (x y : (forget D).obj (P.obj (op X))),
    (∀ (I : Cover.Arrow S), (P.map I.f.op) x = (P.map I.f.op) y) → x = y
X : C
S : Cover J X
x : (forget D).obj (multiequalizer (Cover.index S (plusObj J P)))
t : (forget D).obj ((plusObj J P).obj (op X))
ht : Meq.mk S t = (Meq.equiv (plusObj J P) S) x
i : Cover.Arrow S
⊢ (Cover.toMultiequalizer S (plusObj J P) ≫ Multiequalizer.ι (Cover.index S (plusObj J P)) i) t = ↑(Meq.mk S t) i

Next tactic:
rw [Multiequalizer.lift_ι]