l3lab/ntp-mathlib huggingface dataset:
@[reassoc (attr := simp)]
theorem IsSheaf.amalgamate_map {A : Type u₂} [Category.{v₂} A] {E : A} {X : C} {P : Cᵒᵖ ⥤ A}
    (hP : Presheaf.IsSheaf J P) (S : J.Cover X) (x : ∀ I : S.Arrow, E ⟶ P.obj (op I.Y))
    (hx : ∀ I : S.Relation, x I.fst ≫ P.map I.g₁.op = x I.snd ≫ P.map I.g₂.op) (I : S.Arrow) :
    hP.amalgamate S x hx ≫ P.map I.f.op = x _ := by
  rcases I with ⟨Y, f, hf⟩
  

Proof state:
case mk
C : Type u₁
inst✝² : Category.{v₁, u₁} C
A✝ : Type u₂
inst✝¹ : Category.{v₂, u₂} A✝
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
E : A
X : C
P : Cᵒᵖ ⥤ A
hP : IsSheaf J P
S : GrothendieckTopology.Cover J X
x : (I : GrothendieckTopology.Cover.Arrow S) → E ⟶ P.obj (op I.Y)
hx :
  ∀ (I : GrothendieckTopology.Cover.Relation S),
    x (GrothendieckTopology.Cover.Relation.fst I) ≫ P.map I.g₁.op =
      x (GrothendieckTopology.Cover.Relation.snd I) ≫ P.map I.g₂.op
Y : C
f : Y ⟶ X
hf : (GrothendieckTopology.Cover.sieve S).arrows f
⊢ amalgamate hP S x hx ≫ P.map { Y := Y, f := f, hf := hf }.f.op = x { Y := Y, f := f, hf := hf }

Next tactic:
apply
    @Presieve.IsSheafFor.valid_glue _ _ _ _ _ _ (hP _ _ S.condition) (fun Y f hf => x ⟨Y, f, hf⟩)
      (fun Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ w => hx ⟨Y₁, Y₂, Z, g₁, g₂, f₁, f₂, h₁, h₂, w⟩) f hf
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
⊢ Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P

Next tactic:
constructor
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · 

Proof state:
case mp
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
⊢ Presheaf.IsSheaf J P → Presieve.IsSheaf J P

Next tactic:
intro hP
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    

Proof state:
case mp
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presheaf.IsSheaf J P
⊢ Presieve.IsSheaf J P

Next tactic:
refine' Presieve.isSheaf_iso J _ (hP PUnit)
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    

Proof state:
case mp
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presheaf.IsSheaf J P
⊢ P ⋙ coyoneda.obj (op PUnit.{w + 1}) ≅ P

Next tactic:
refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · 

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
⊢ Presieve.IsSheaf J P → Presheaf.IsSheaf J P

Next tactic:
intro hP X Y S hS z hz
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    

Proof state:
case mpr
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
⊢ ∃! t, Presieve.FamilyOfElements.IsAmalgamation z t

Next tactic:
refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · 

Proof state:
case mpr.refine'_1
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
x : (op X).unop
⊢ Presieve.FamilyOfElements.Compatible fun Z f hf => z f hf x

Next tactic:
intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      

Proof state:
case mpr.refine'_1
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
x : (op X).unop
Y₁ Y₂ Z : C
g₁ : Z ⟶ Y₁
g₂ : Z ⟶ Y₂
f₁ : Y₁ ⟶ Y
f₂ : Y₂ ⟶ Y
hf₁ : S.arrows f₁
hf₂ : S.arrows f₂
h : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ P.map g₁.op ((fun Z f hf => z f hf x) Y₁ f₁ hf₁) = P.map g₂.op ((fun Z f hf => z f hf x) Y₂ f₂ hf₂)

Next tactic:
exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · 

Proof state:
case mpr.refine'_2
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
⊢ (fun t => Presieve.FamilyOfElements.IsAmalgamation z t) fun x =>
    Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
      (_ :
        ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁) (hf₂ : S.arrows f₂),
          g₁ ≫ f₁ = g₂ ≫ f₂ →
            (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x)

Next tactic:
intro Z f hf
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      

Proof state:
case mpr.refine'_2
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
Z : C
f : Z ⟶ Y
hf : S.arrows f
⊢ ((P ⋙ coyoneda.obj (op X)).map f.op fun x =>
      Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
        (_ :
          ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁)
            (hf₂ : S.arrows f₂),
            g₁ ≫ f₁ = g₂ ≫ f₂ →
              (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x)) =
    z f hf

Next tactic:
funext x
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      funext x
      

Proof state:
case mpr.refine'_2.h
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
Z : C
f : Z ⟶ Y
hf : S.arrows f
x : (op X).unop
⊢ (P ⋙ coyoneda.obj (op X)).map f.op
      (fun x =>
        Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
          (_ :
            ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁)
              (hf₂ : S.arrows f₂),
              g₁ ≫ f₁ = g₂ ≫ f₂ →
                (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x))
      x =
    z f hf x

Next tactic:
apply Presieve.IsSheafFor.valid_glue
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      funext x
      apply Presieve.IsSheafFor.valid_glue
    · 

Proof state:
case mpr.refine'_3
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
⊢ ∀ (y : (P ⋙ coyoneda.obj (op X)).obj (op Y)),
    (fun t => Presieve.FamilyOfElements.IsAmalgamation z t) y →
      y = fun x =>
        Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
          (_ :
            ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁)
              (hf₂ : S.arrows f₂),
              g₁ ≫ f₁ = g₂ ≫ f₂ →
                (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x)

Next tactic:
intro y hy
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      funext x
      apply Presieve.IsSheafFor.valid_glue
    · intro y hy
      

Proof state:
case mpr.refine'_3
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
y : (P ⋙ coyoneda.obj (op X)).obj (op Y)
hy : Presieve.FamilyOfElements.IsAmalgamation z y
⊢ y = fun x =>
    Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
      (_ :
        ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁) (hf₂ : S.arrows f₂),
          g₁ ≫ f₁ = g₂ ≫ f₂ →
            (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x)

Next tactic:
funext x
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      funext x
      apply Presieve.IsSheafFor.valid_glue
    · intro y hy
      funext x
      

Proof state:
case mpr.refine'_3.h
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
y : (P ⋙ coyoneda.obj (op X)).obj (op Y)
hy : Presieve.FamilyOfElements.IsAmalgamation z y
x : (op X).unop
⊢ y x =
    Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
      (_ :
        ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁) (hf₂ : S.arrows f₂),
          g₁ ≫ f₁ = g₂ ≫ f₂ →
            (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x)

Next tactic:
apply (hP S hS).isSeparatedFor.ext
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      funext x
      apply Presieve.IsSheafFor.valid_glue
    · intro y hy
      funext x
      apply (hP S hS).isSeparatedFor.ext
      

Proof state:
case mpr.refine'_3.h
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
y : (P ⋙ coyoneda.obj (op X)).obj (op Y)
hy : Presieve.FamilyOfElements.IsAmalgamation z y
x : (op X).unop
⊢ ∀ ⦃Y_1 : C⦄ ⦃f : Y_1 ⟶ Y⦄,
    S.arrows f →
      P.map f.op (y x) =
        P.map f.op
          (Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
            (_ :
              ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁)
                (hf₂ : S.arrows f₂),
                g₁ ≫ f₁ = g₂ ≫ f₂ →
                  (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x))

Next tactic:
intro Y' f hf
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      funext x
      apply Presieve.IsSheafFor.valid_glue
    · intro y hy
      funext x
      apply (hP S hS).isSeparatedFor.ext
      intro Y' f hf
      

Proof state:
case mpr.refine'_3.h
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
y : (P ⋙ coyoneda.obj (op X)).obj (op Y)
hy : Presieve.FamilyOfElements.IsAmalgamation z y
x : (op X).unop
Y' : C
f : Y' ⟶ Y
hf : S.arrows f
⊢ P.map f.op (y x) =
    P.map f.op
      (Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P S.arrows) (fun Z f hf => z f hf x)
        (_ :
          ∀ ⦃Y₁ Y₂ Z : C⦄ (g₁ : Z ⟶ Y₁) (g₂ : Z ⟶ Y₂) ⦃f₁ : Y₁ ⟶ Y⦄ ⦃f₂ : Y₂ ⟶ Y⦄ (hf₁ : S.arrows f₁)
            (hf₂ : S.arrows f₂),
            g₁ ≫ f₁ = g₂ ≫ f₂ →
              (P ⋙ coyoneda.obj (op X)).map g₁.op (z f₁ hf₁) x = (P ⋙ coyoneda.obj (op X)).map g₂.op (z f₂ hf₂) x))

Next tactic:
rw [Presieve.IsSheafFor.valid_glue _ _ _ hf, ← hy _ hf]
theorem isSheaf_iff_isSheaf_of_type (P : Cᵒᵖ ⥤ Type w) :
    Presheaf.IsSheaf J P ↔ Presieve.IsSheaf J P := by
  constructor
  · intro hP
    refine' Presieve.isSheaf_iso J _ (hP PUnit)
    refine' isoWhiskerLeft _ Coyoneda.punitIso ≪≫ P.rightUnitor
  · intro hP X Y S hS z hz
    refine' ⟨fun x => (hP S hS).amalgamate (fun Z f hf => z f hf x) _, _, _⟩
    · intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ h
      exact congr_fun (hz g₁ g₂ hf₁ hf₂ h) x
    · intro Z f hf
      funext x
      apply Presieve.IsSheafFor.valid_glue
    · intro y hy
      funext x
      apply (hP S hS).isSeparatedFor.ext
      intro Y' f hf
      rw [Presieve.IsSheafFor.valid_glue _ _ _ hf, ← hy _ hf]
      

Proof state:
case mpr.refine'_3.h
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
P : Cᵒᵖ ⥤ Type w
hP : Presieve.IsSheaf J P
X : Type w
Y : C
S : Sieve Y
hS : S ∈ GrothendieckTopology.sieves J Y
z : Presieve.FamilyOfElements (P ⋙ coyoneda.obj (op X)) S.arrows
hz : Presieve.FamilyOfElements.Compatible z
y : (P ⋙ coyoneda.obj (op X)).obj (op Y)
hy : Presieve.FamilyOfElements.IsAmalgamation z y
x : (op X).unop
Y' : C
f : Y' ⟶ Y
hf : S.arrows f
⊢ P.map f.op (y x) = (P ⋙ coyoneda.obj (op X)).map f.op y x

Next tactic:
rfl
/-- If the empty sieve is a cover of `X`, then `F(X)` is terminal. -/
def Sheaf.isTerminalOfBotCover (F : Sheaf J A) (X : C) (H : ⊥ ∈ J X) :
    IsTerminal (F.1.obj (op X)) := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
F : Sheaf J A
X : C
H : ⊥ ∈ GrothendieckTopology.sieves J X
⊢ IsTerminal (F.val.obj (op X))

Next tactic:
refine' @IsTerminal.ofUnique _ _ _ ?_
/-- If the empty sieve is a cover of `X`, then `F(X)` is terminal. -/
def Sheaf.isTerminalOfBotCover (F : Sheaf J A) (X : C) (H : ⊥ ∈ J X) :
    IsTerminal (F.1.obj (op X)) := by
  refine' @IsTerminal.ofUnique _ _ _ ?_
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
F : Sheaf J A
X : C
H : ⊥ ∈ GrothendieckTopology.sieves J X
⊢ (X_1 : A) → Unique (X_1 ⟶ F.val.obj (op X))

Next tactic:
intro Y
/-- If the empty sieve is a cover of `X`, then `F(X)` is terminal. -/
def Sheaf.isTerminalOfBotCover (F : Sheaf J A) (X : C) (H : ⊥ ∈ J X) :
    IsTerminal (F.1.obj (op X)) := by
  refine' @IsTerminal.ofUnique _ _ _ ?_
  intro Y
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
F : Sheaf J A
X : C
H : ⊥ ∈ GrothendieckTopology.sieves J X
Y : A
⊢ Unique (Y ⟶ F.val.obj (op X))

Next tactic:
choose t h using F.2 Y _ H (by tauto) (by tauto)
/-- If the empty sieve is a cover of `X`, then `F(X)` is terminal. -/
def Sheaf.isTerminalOfBotCover (F : Sheaf J A) (X : C) (H : ⊥ ∈ J X) :
    IsTerminal (F.1.obj (op X)) := by
  refine' @IsTerminal.ofUnique _ _ _ ?_
  intro Y
  choose t h using F.2 Y _ H (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
F : Sheaf J A
X : C
H : ⊥ ∈ GrothendieckTopology.sieves J X
Y : A
⊢ Presieve.FamilyOfElements (F.val ⋙ coyoneda.obj (op Y)) ⊥.arrows

Next tactic:
tauto
/-- If the empty sieve is a cover of `X`, then `F(X)` is terminal. -/
def Sheaf.isTerminalOfBotCover (F : Sheaf J A) (X : C) (H : ⊥ ∈ J X) :
    IsTerminal (F.1.obj (op X)) := by
  refine' @IsTerminal.ofUnique _ _ _ ?_
  intro Y
  choose t h using F.2 Y _ H (by tauto) (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
F : Sheaf J A
X : C
H : ⊥ ∈ GrothendieckTopology.sieves J X
Y : A
⊢ Presieve.FamilyOfElements.Compatible
    (let_fun em := Classical.propDecidable;
    fun ⦃Y_1⦄ f a => (_ : False).elim)

Next tactic:
tauto
/-- If the empty sieve is a cover of `X`, then `F(X)` is terminal. -/
def Sheaf.isTerminalOfBotCover (F : Sheaf J A) (X : C) (H : ⊥ ∈ J X) :
    IsTerminal (F.1.obj (op X)) := by
  refine' @IsTerminal.ofUnique _ _ _ ?_
  intro Y
  choose t h using F.2 Y _ H (by tauto) (by tauto)
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
F : Sheaf J A
X : C
H : ⊥ ∈ GrothendieckTopology.sieves J X
Y : A
t : (F.val ⋙ coyoneda.obj (op Y)).obj (op X)
h :
  (fun t =>
        Presieve.FamilyOfElements.IsAmalgamation
          (let_fun em := Classical.propDecidable;
          fun ⦃Y_1⦄ f a => (_ : False).elim)
          t)
      t ∧
    ∀ (y : (F.val ⋙ coyoneda.obj (op Y)).obj (op X)),
      (fun t =>
            Presieve.FamilyOfElements.IsAmalgamation
              (let_fun em := Classical.propDecidable;
              fun ⦃Y_1⦄ f a => (_ : False).elim)
              t)
          y →
        y = t
⊢ Unique (Y ⟶ F.val.obj (op X))

Next tactic:
exact ⟨⟨t⟩, fun a => h.2 a (by tauto)⟩
/-- If the empty sieve is a cover of `X`, then `F(X)` is terminal. -/
def Sheaf.isTerminalOfBotCover (F : Sheaf J A) (X : C) (H : ⊥ ∈ J X) :
    IsTerminal (F.1.obj (op X)) := by
  refine' @IsTerminal.ofUnique _ _ _ ?_
  intro Y
  choose t h using F.2 Y _ H (by tauto) (by tauto)
  exact ⟨⟨t⟩, fun a => h.2 a (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
J : GrothendieckTopology C
A : Type u₂
inst✝ : Category.{v₂, u₂} A
F : Sheaf J A
X : C
H : ⊥ ∈ GrothendieckTopology.sieves J X
Y : A
t : (F.val ⋙ coyoneda.obj (op Y)).obj (op X)
h :
  (fun t =>
        Presieve.FamilyOfElements.IsAmalgamation
          (let_fun em := Classical.propDecidable;
          fun ⦃Y_1⦄ f a => (_ : False).elim)
          t)
      t ∧
    ∀ (y : (F.val ⋙ coyoneda.obj (op Y)).obj (op X)),
      (fun t =>
            Presieve.FamilyOfElements.IsAmalgamation
              (let_fun em := Classical.propDecidable;
              fun ⦃Y_1⦄ f a => (_ : False).elim)
              t)
          y →
        y = t
a : Y ⟶ F.val.obj (op X)
⊢ (fun t =>
      Presieve.FamilyOfElements.IsAmalgamation
        (let_fun em := Classical.propDecidable;
        fun ⦃Y_1⦄ f a => (_ : False).elim)
        t)
    a

Next tactic:
tauto
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
          

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 using Int.induction_on with n ih n ih