l3lab/ntp-mathlib huggingface dataset:
instance sheafHomHasZSMul : SMul ℤ (P ⟶ Q) where
  smul n f :=
    Sheaf.Hom.mk
      { app := fun U => n • f.1.app U
        naturality := fun U V i => by
          induction' n using Int.induction_on with n ih n ih
          · 

Proof state:
case hz
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
f : P ⟶ Q
U V : Cᵒᵖ
i : U ⟶ V
⊢ P.val.map i ≫ (fun U => 0 • f.val.app U) V = (fun U => 0 • f.val.app U) U ≫ Q.val.map i

Next tactic:
simp only [zero_smul, comp_zero, zero_comp]
instance sheafHomHasZSMul : SMul ℤ (P ⟶ Q) where
  smul n f :=
    Sheaf.Hom.mk
      { app := fun U => n • f.1.app U
        naturality := fun U V i => by
          induction' n using Int.induction_on with n ih n ih
          · simp only [zero_smul, comp_zero, zero_comp]
          · 

Proof state:
case hp
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
f : P ⟶ Q
U V : Cᵒᵖ
i : U ⟶ V
n : ℕ
ih : P.val.map i ≫ (fun U => ↑n • f.val.app U) V = (fun U => ↑n • f.val.app U) U ≫ Q.val.map i
⊢ P.val.map i ≫ (fun U => (↑n + 1) • f.val.app U) V = (fun U => (↑n + 1) • f.val.app U) U ≫ Q.val.map i

Next tactic:
simpa only [add_zsmul, one_zsmul, comp_add, NatTrans.naturality, add_comp,
              add_left_inj]
instance sheafHomHasZSMul : SMul ℤ (P ⟶ Q) where
  smul n f :=
    Sheaf.Hom.mk
      { app := fun U => n • f.1.app U
        naturality := fun U V i => by
          induction' n using Int.induction_on with n ih n ih
          · simp only [zero_smul, comp_zero, zero_comp]
          · simpa only [add_zsmul, one_zsmul, comp_add, NatTrans.naturality, add_comp,
              add_left_inj]
          · 

Proof state:
case hn
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
f : P ⟶ Q
U V : Cᵒᵖ
i : U ⟶ V
n : ℕ
ih : P.val.map i ≫ (fun U => -↑n • f.val.app U) V = (fun U => -↑n • f.val.app U) U ≫ Q.val.map i
⊢ P.val.map i ≫ (fun U => (-↑n - 1) • f.val.app U) V = (fun U => (-↑n - 1) • f.val.app U) U ≫ Q.val.map i

Next tactic:
simpa only [sub_smul, one_zsmul, comp_sub, NatTrans.naturality, sub_comp,
              sub_left_inj] using ih
instance sheafHomHasNSMul : SMul ℕ (P ⟶ Q) where
  smul n f :=
    Sheaf.Hom.mk
      { app := fun U => n • f.1.app U
        naturality := fun U V i => by
          

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
n : ℕ
f : P ⟶ Q
U V : Cᵒᵖ
i : U ⟶ V
⊢ P.val.map i ≫ (fun U => n • f.val.app U) V = (fun U => n • f.val.app U) U ≫ Q.val.map i

Next tactic:
induction' n with n ih
instance sheafHomHasNSMul : SMul ℕ (P ⟶ Q) where
  smul n f :=
    Sheaf.Hom.mk
      { app := fun U => n • f.1.app U
        naturality := fun U V i => by
          induction' n with n ih
          · 

Proof state:
case zero
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
f : P ⟶ Q
U V : Cᵒᵖ
i : U ⟶ V
⊢ P.val.map i ≫ (fun U => Nat.zero • f.val.app U) V = (fun U => Nat.zero • f.val.app U) U ≫ Q.val.map i

Next tactic:
simp only [zero_smul, comp_zero, zero_comp, Nat.zero_eq]
instance sheafHomHasNSMul : SMul ℕ (P ⟶ Q) where
  smul n f :=
    Sheaf.Hom.mk
      { app := fun U => n • f.1.app U
        naturality := fun U V i => by
          induction' n with n ih
          · simp only [zero_smul, comp_zero, zero_comp, Nat.zero_eq]
          · 

Proof state:
case succ
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
f : P ⟶ Q
U V : Cᵒᵖ
i : U ⟶ V
n : ℕ
ih : P.val.map i ≫ (fun U => n • f.val.app U) V = (fun U => n • f.val.app U) U ≫ Q.val.map i
⊢ P.val.map i ≫ (fun U => Nat.succ n • f.val.app U) V = (fun U => Nat.succ n • f.val.app U) U ≫ Q.val.map i

Next tactic:
simp only [Nat.succ_eq_add_one, add_smul, ih, one_nsmul, comp_add,
              NatTrans.naturality, add_comp]
instance Sheaf.Hom.addCommGroup : AddCommGroup (P ⟶ Q) :=
  Function.Injective.addCommGroup (fun f : Sheaf.Hom P Q => f.1)
    (fun _ _ h => Sheaf.Hom.ext _ _ h) rfl (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl)
    (fun _ _ => by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
x✝¹ : P ⟶ Q
x✝ : ℕ
⊢ (fun f => f.val) (x✝ • x✝¹) = x✝ • (fun f => f.val) x✝¹

Next tactic:
aesop_cat
instance Sheaf.Hom.addCommGroup : AddCommGroup (P ⟶ Q) :=
  Function.Injective.addCommGroup (fun f : Sheaf.Hom P Q => f.1)
    (fun _ _ h => Sheaf.Hom.ext _ _ h) rfl (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl)
    (fun _ _ => by aesop_cat) (fun _ _ => by 

Proof state:
C : Type u₁
inst✝² : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝¹ : Category.{v₂, u₂} A
inst✝ : Preadditive A
P Q : Sheaf J A
x✝¹ : P ⟶ Q
x✝ : ℤ
⊢ (fun f => f.val) (x✝ • x✝¹) = x✝ • (fun f => f.val) x✝¹

Next tactic:
aesop_cat
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := 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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
⊢ ∀ (s : Cone (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P)))
    (j : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo),
    (fun E =>
            IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
              (_ :
                ∀ (I : GrothendieckTopology.Cover.Relation S),
                  Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                    Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
          s ≫
        (GrothendieckTopology.Cover.multifork S P).π.app j =
      s.π.app j

Next tactic:
rintro (E : Multifork _) (a | b)
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · 

Proof state:
case left
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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
a : (GrothendieckTopology.Cover.index S P).L
⊢ (fun E =>
          IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
            (_ :
              ∀ (I : GrothendieckTopology.Cover.Relation S),
                Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                  Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
        E ≫
      (GrothendieckTopology.Cover.multifork S P).π.app (WalkingMulticospan.left a) =
    E.π.app (WalkingMulticospan.left a)

Next tactic:
apply hP.amalgamate_map
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · 

Proof state:
case right
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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
b : (GrothendieckTopology.Cover.index S P).R
⊢ (fun E =>
          IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
            (_ :
              ∀ (I : GrothendieckTopology.Cover.Relation S),
                Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                  Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
        E ≫
      (GrothendieckTopology.Cover.multifork S P).π.app (WalkingMulticospan.right b) =
    E.π.app (WalkingMulticospan.right b)

Next tactic:
rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      

Proof state:
case right
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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
b : (GrothendieckTopology.Cover.index S P).R
⊢ ((fun E =>
            IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
              (_ :
                ∀ (I : GrothendieckTopology.Cover.Relation S),
                  Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                    Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
          E ≫
        (GrothendieckTopology.Cover.multifork S P).π.app
          (WalkingMulticospan.left (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) b))) ≫
      (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P)).map (WalkingMulticospan.Hom.fst b) =
    E.π.app (WalkingMulticospan.left (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) b)) ≫
      (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P)).map (WalkingMulticospan.Hom.fst b)

Next tactic:
congr 1
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      congr 1
      

Proof state:
case 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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
b : (GrothendieckTopology.Cover.index S P).R
⊢ (fun E =>
          IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
            (_ :
              ∀ (I : GrothendieckTopology.Cover.Relation S),
                Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                  Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
        E ≫
      (GrothendieckTopology.Cover.multifork S P).π.app
        (WalkingMulticospan.left (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) b)) =
    E.π.app (WalkingMulticospan.left (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) b))

Next tactic:
apply hP.amalgamate_map
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      congr 1
      apply hP.amalgamate_map
  uniq := 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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
⊢ ∀ (s : Cone (MulticospanIndex.multicospan (GrothendieckTopology.Cover.index S P)))
    (m : s.pt ⟶ (GrothendieckTopology.Cover.multifork S P).pt),
    (∀
        (j :
          WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo),
        m ≫ (GrothendieckTopology.Cover.multifork S P).π.app j = s.π.app j) →
      m =
        (fun E =>
            IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
              (_ :
                ∀ (I : GrothendieckTopology.Cover.Relation S),
                  Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                    Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
          s

Next tactic:
rintro (E : Multifork _) m hm
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      congr 1
      apply hP.amalgamate_map
  uniq := by
    rintro (E : Multifork _) m hm
    

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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
m : E.pt ⟶ (GrothendieckTopology.Cover.multifork S P).pt
hm :
  ∀ (j : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo),
    m ≫ (GrothendieckTopology.Cover.multifork S P).π.app j = E.π.app j
⊢ m =
    (fun E =>
        IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
          (_ :
            ∀ (I : GrothendieckTopology.Cover.Relation S),
              Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                  MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                  MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
      E

Next tactic:
apply hP.hom_ext S
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      congr 1
      apply hP.amalgamate_map
  uniq := by
    rintro (E : Multifork _) m hm
    apply hP.hom_ext S
    

Proof state:
case h
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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
m : E.pt ⟶ (GrothendieckTopology.Cover.multifork S P).pt
hm :
  ∀ (j : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo),
    m ≫ (GrothendieckTopology.Cover.multifork S P).π.app j = E.π.app j
⊢ ∀ (I : GrothendieckTopology.Cover.Arrow S),
    m ≫ P.map I.f.op =
      (fun E =>
            IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
              (_ :
                ∀ (I : GrothendieckTopology.Cover.Relation S),
                  Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                    Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                      MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
          E ≫
        P.map I.f.op

Next tactic:
intro I
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      congr 1
      apply hP.amalgamate_map
  uniq := by
    rintro (E : Multifork _) m hm
    apply hP.hom_ext S
    intro I
    

Proof state:
case h
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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
m : E.pt ⟶ (GrothendieckTopology.Cover.multifork S P).pt
hm :
  ∀ (j : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo),
    m ≫ (GrothendieckTopology.Cover.multifork S P).π.app j = E.π.app j
I : GrothendieckTopology.Cover.Arrow S
⊢ m ≫ P.map I.f.op =
    (fun E =>
          IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
            (_ :
              ∀ (I : GrothendieckTopology.Cover.Relation S),
                Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                  Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
        E ≫
      P.map I.f.op

Next tactic:
erw [hm (WalkingMulticospan.left I)]
/-- When `P` is a sheaf and `S` is a cover, the associated multifork is a limit. -/
def isLimitOfIsSheaf {X : C} (S : J.Cover X) (hP : IsSheaf J P) : IsLimit (S.multifork P) where
  lift := fun E : Multifork _ => hP.amalgamate S (fun I => E.ι _) fun I => E.condition _
  fac := by
    rintro (E : Multifork _) (a | b)
    · apply hP.amalgamate_map
    · rw [← E.w (WalkingMulticospan.Hom.fst b),
        ← (S.multifork P).w (WalkingMulticospan.Hom.fst b), ← assoc]
      congr 1
      apply hP.amalgamate_map
  uniq := by
    rintro (E : Multifork _) m hm
    apply hP.hom_ext S
    intro I
    erw [hm (WalkingMulticospan.left I)]
    

Proof state:
case h
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'
X : C
S : GrothendieckTopology.Cover J X
hP : IsSheaf J P
E : Multifork (GrothendieckTopology.Cover.index S P)
m : E.pt ⟶ (GrothendieckTopology.Cover.multifork S P).pt
hm :
  ∀ (j : WalkingMulticospan (GrothendieckTopology.Cover.index S P).fstTo (GrothendieckTopology.Cover.index S P).sndTo),
    m ≫ (GrothendieckTopology.Cover.multifork S P).π.app j = E.π.app j
I : GrothendieckTopology.Cover.Arrow S
⊢ E.π.app (WalkingMulticospan.left I) =
    (fun E =>
          IsSheaf.amalgamate hP S (fun I => Multifork.ι E I)
            (_ :
              ∀ (I : GrothendieckTopology.Cover.Relation S),
                Multifork.ι E (MulticospanIndex.fstTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.fst (GrothendieckTopology.Cover.index S P) I =
                  Multifork.ι E (MulticospanIndex.sndTo (GrothendieckTopology.Cover.index S P) I) ≫
                    MulticospanIndex.snd (GrothendieckTopology.Cover.index S P) I))
        E ≫
      P.map I.f.op

Next tactic:
symm