l3lab/ntp-mathlib huggingface dataset:
/-- The equalizer definition of a sheaf given by `isSheaf'` is equivalent to `isSheaf`. -/
theorem isSheaf_iff_isSheaf' : IsSheaf J P' ↔ IsSheaf' J P' := by
  constructor
  · intro h U R hR
    refine' ⟨_⟩
    apply coyonedaJointlyReflectsLimits
    intro X
    have q : Presieve.IsSheafFor (P' ⋙ coyoneda.obj X) _ := h X.unop _ hR
    rw [← Presieve.isSheafFor_iff_generate] at q
    rw [Equalizer.Presieve.sheaf_condition] at q
    replace q := Classical.choice q
    apply (isSheafForIsSheafFor' _ _ _ _).symm q
  · intro h U X S hS
    rw [Equalizer.Presieve.sheaf_condition]
    

Proof state:
case mpr
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
h : IsSheaf' J P'
U : A'
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
⊢ Nonempty
    (IsLimit
      (Fork.ofι (Equalizer.forkMap (P' ⋙ coyoneda.obj (op U)) S.arrows)
        (_ :
          Equalizer.forkMap (P' ⋙ coyoneda.obj (op U)) S.arrows ≫
              Equalizer.Presieve.firstMap (P' ⋙ coyoneda.obj (op U)) S.arrows =
            Equalizer.forkMap (P' ⋙ coyoneda.obj (op U)) S.arrows ≫
              Equalizer.Presieve.secondMap (P' ⋙ coyoneda.obj (op U)) S.arrows)))

Next tactic:
refine' ⟨_⟩
/-- The equalizer definition of a sheaf given by `isSheaf'` is equivalent to `isSheaf`. -/
theorem isSheaf_iff_isSheaf' : IsSheaf J P' ↔ IsSheaf' J P' := by
  constructor
  · intro h U R hR
    refine' ⟨_⟩
    apply coyonedaJointlyReflectsLimits
    intro X
    have q : Presieve.IsSheafFor (P' ⋙ coyoneda.obj X) _ := h X.unop _ hR
    rw [← Presieve.isSheafFor_iff_generate] at q
    rw [Equalizer.Presieve.sheaf_condition] at q
    replace q := Classical.choice q
    apply (isSheafForIsSheafFor' _ _ _ _).symm q
  · intro h U X S hS
    rw [Equalizer.Presieve.sheaf_condition]
    refine' ⟨_⟩
    

Proof state:
case mpr
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
h : IsSheaf' J P'
U : A'
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
⊢ IsLimit
    (Fork.ofι (Equalizer.forkMap (P' ⋙ coyoneda.obj (op U)) S.arrows)
      (_ :
        Equalizer.forkMap (P' ⋙ coyoneda.obj (op U)) S.arrows ≫
            Equalizer.Presieve.firstMap (P' ⋙ coyoneda.obj (op U)) S.arrows =
          Equalizer.forkMap (P' ⋙ coyoneda.obj (op U)) S.arrows ≫
            Equalizer.Presieve.secondMap (P' ⋙ coyoneda.obj (op U)) S.arrows))

Next tactic:
refine' isSheafForIsSheafFor' _ _ _ _ _
/-- The equalizer definition of a sheaf given by `isSheaf'` is equivalent to `isSheaf`. -/
theorem isSheaf_iff_isSheaf' : IsSheaf J P' ↔ IsSheaf' J P' := by
  constructor
  · intro h U R hR
    refine' ⟨_⟩
    apply coyonedaJointlyReflectsLimits
    intro X
    have q : Presieve.IsSheafFor (P' ⋙ coyoneda.obj X) _ := h X.unop _ hR
    rw [← Presieve.isSheafFor_iff_generate] at q
    rw [Equalizer.Presieve.sheaf_condition] at q
    replace q := Classical.choice q
    apply (isSheafForIsSheafFor' _ _ _ _).symm q
  · intro h U X S hS
    rw [Equalizer.Presieve.sheaf_condition]
    refine' ⟨_⟩
    refine' isSheafForIsSheafFor' _ _ _ _ _
    

Proof state:
case mpr
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
h : IsSheaf' J P'
U : A'
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
⊢ IsLimit
    ((coyoneda.obj (op U)).mapCone
      (Fork.ofι (forkMap S.arrows P')
        (_ : forkMap S.arrows P' ≫ firstMap S.arrows P' = forkMap S.arrows P' ≫ secondMap S.arrows P')))

Next tactic:
letI := preservesSmallestLimitsOfPreservesLimits (coyoneda.obj (op U))
/-- The equalizer definition of a sheaf given by `isSheaf'` is equivalent to `isSheaf`. -/
theorem isSheaf_iff_isSheaf' : IsSheaf J P' ↔ IsSheaf' J P' := by
  constructor
  · intro h U R hR
    refine' ⟨_⟩
    apply coyonedaJointlyReflectsLimits
    intro X
    have q : Presieve.IsSheafFor (P' ⋙ coyoneda.obj X) _ := h X.unop _ hR
    rw [← Presieve.isSheafFor_iff_generate] at q
    rw [Equalizer.Presieve.sheaf_condition] at q
    replace q := Classical.choice q
    apply (isSheafForIsSheafFor' _ _ _ _).symm q
  · intro h U X S hS
    rw [Equalizer.Presieve.sheaf_condition]
    refine' ⟨_⟩
    refine' isSheafForIsSheafFor' _ _ _ _ _
    letI := preservesSmallestLimitsOfPreservesLimits (coyoneda.obj (op U))
    

Proof state:
case mpr
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
h : IsSheaf' J P'
U : A'
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
this : PreservesLimitsOfSize.{0, 0, max u₁ v₁, max u₁ v₁, u₂, (max u₁ v₁) + 1} (coyoneda.obj (op U)) :=
  preservesSmallestLimitsOfPreservesLimits (coyoneda.obj (op U))
⊢ IsLimit
    ((coyoneda.obj (op U)).mapCone
      (Fork.ofι (forkMap S.arrows P')
        (_ : forkMap S.arrows P' ≫ firstMap S.arrows P' = forkMap S.arrows P' ≫ secondMap S.arrows P')))

Next tactic:
apply isLimitOfPreserves
/-- The equalizer definition of a sheaf given by `isSheaf'` is equivalent to `isSheaf`. -/
theorem isSheaf_iff_isSheaf' : IsSheaf J P' ↔ IsSheaf' J P' := by
  constructor
  · intro h U R hR
    refine' ⟨_⟩
    apply coyonedaJointlyReflectsLimits
    intro X
    have q : Presieve.IsSheafFor (P' ⋙ coyoneda.obj X) _ := h X.unop _ hR
    rw [← Presieve.isSheafFor_iff_generate] at q
    rw [Equalizer.Presieve.sheaf_condition] at q
    replace q := Classical.choice q
    apply (isSheafForIsSheafFor' _ _ _ _).symm q
  · intro h U X S hS
    rw [Equalizer.Presieve.sheaf_condition]
    refine' ⟨_⟩
    refine' isSheafForIsSheafFor' _ _ _ _ _
    letI := preservesSmallestLimitsOfPreservesLimits (coyoneda.obj (op U))
    apply isLimitOfPreserves
    

Proof state:
case mpr.t
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
h : IsSheaf' J P'
U : A'
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
this : PreservesLimitsOfSize.{0, 0, max u₁ v₁, max u₁ v₁, u₂, (max u₁ v₁) + 1} (coyoneda.obj (op U)) :=
  preservesSmallestLimitsOfPreservesLimits (coyoneda.obj (op U))
⊢ IsLimit
    (Fork.ofι (forkMap S.arrows P')
      (_ : forkMap S.arrows P' ≫ firstMap S.arrows P' = forkMap S.arrows P' ≫ secondMap S.arrows P'))

Next tactic:
apply Classical.choice (h _ S.arrows _)
/-- The equalizer definition of a sheaf given by `isSheaf'` is equivalent to `isSheaf`. -/
theorem isSheaf_iff_isSheaf' : IsSheaf J P' ↔ IsSheaf' J P' := by
  constructor
  · intro h U R hR
    refine' ⟨_⟩
    apply coyonedaJointlyReflectsLimits
    intro X
    have q : Presieve.IsSheafFor (P' ⋙ coyoneda.obj X) _ := h X.unop _ hR
    rw [← Presieve.isSheafFor_iff_generate] at q
    rw [Equalizer.Presieve.sheaf_condition] at q
    replace q := Classical.choice q
    apply (isSheafForIsSheafFor' _ _ _ _).symm q
  · intro h U X S hS
    rw [Equalizer.Presieve.sheaf_condition]
    refine' ⟨_⟩
    refine' isSheafForIsSheafFor' _ _ _ _ _
    letI := preservesSmallestLimitsOfPreservesLimits (coyoneda.obj (op U))
    apply isLimitOfPreserves
    apply Classical.choice (h _ S.arrows _)
    

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
h : IsSheaf' J P'
U : A'
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
this : PreservesLimitsOfSize.{0, 0, max u₁ v₁, max u₁ v₁, u₂, (max u₁ v₁) + 1} (coyoneda.obj (op U)) :=
  preservesSmallestLimitsOfPreservesLimits (coyoneda.obj (op U))
⊢ generate S.arrows ∈ GrothendieckTopology.sieves J X

Next tactic:
simpa
theorem isSheaf_of_isSheaf_comp (s : A ⥤ B) [ReflectsLimitsOfSize.{v₁, max v₁ u₁} s]
    (h : IsSheaf J (P ⋙ s)) : IsSheaf J 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'
s : A ⥤ B
inst✝ : ReflectsLimitsOfSize.{v₁, max v₁ u₁, v₂, v₃, u₂, u₃} s
h : IsSheaf J (P ⋙ s)
⊢ IsSheaf J P

Next tactic:
rw [isSheaf_iff_isLimit] at h ⊢
theorem isSheaf_of_isSheaf_comp (s : A ⥤ B) [ReflectsLimitsOfSize.{v₁, max v₁ u₁} s]
    (h : IsSheaf J (P ⋙ s)) : IsSheaf J P := by
  rw [isSheaf_iff_isLimit] at h ⊢
  

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'
s : A ⥤ B
inst✝ : ReflectsLimitsOfSize.{v₁, max v₁ u₁, v₂, v₃, u₂, u₃} s
h :
  ∀ ⦃X : C⦄,
    ∀ S ∈ GrothendieckTopology.sieves J X, Nonempty (IsLimit ((P ⋙ s).mapCone (Cocone.op (Presieve.cocone S.arrows))))
⊢ ∀ ⦃X : C⦄,
    ∀ S ∈ GrothendieckTopology.sieves J X, Nonempty (IsLimit (P.mapCone (Cocone.op (Presieve.cocone S.arrows))))

Next tactic:
exact fun X S hS ↦ (h S hS).map fun t ↦ isLimitOfReflects s t
theorem isSheaf_comp_of_isSheaf (s : A ⥤ B) [PreservesLimitsOfSize.{v₁, max v₁ u₁} s]
    (h : IsSheaf J P) : IsSheaf J (P ⋙ s) := 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'
s : A ⥤ B
inst✝ : PreservesLimitsOfSize.{v₁, max v₁ u₁, v₂, v₃, u₂, u₃} s
h : IsSheaf J P
⊢ IsSheaf J (P ⋙ s)

Next tactic:
rw [isSheaf_iff_isLimit] at h ⊢
theorem isSheaf_comp_of_isSheaf (s : A ⥤ B) [PreservesLimitsOfSize.{v₁, max v₁ u₁} s]
    (h : IsSheaf J P) : IsSheaf J (P ⋙ s) := by
  rw [isSheaf_iff_isLimit] at h ⊢
  

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'
s : A ⥤ B
inst✝ : PreservesLimitsOfSize.{v₁, max v₁ u₁, v₂, v₃, u₂, u₃} s
h :
  ∀ ⦃X : C⦄,
    ∀ S ∈ GrothendieckTopology.sieves J X, Nonempty (IsLimit (P.mapCone (Cocone.op (Presieve.cocone S.arrows))))
⊢ ∀ ⦃X : C⦄,
    ∀ S ∈ GrothendieckTopology.sieves J X, Nonempty (IsLimit ((P ⋙ s).mapCone (Cocone.op (Presieve.cocone S.arrows))))

Next tactic:
apply fun X S hS ↦ (h S hS).map fun t ↦ isLimitOfPreserves s t
theorem isSheaf_iff_isSheaf_comp (s : A ⥤ B) [HasLimitsOfSize.{v₁, max v₁ u₁} A]
    [PreservesLimitsOfSize.{v₁, max v₁ u₁} s] [ReflectsIsomorphisms s] :
    IsSheaf J P ↔ IsSheaf J (P ⋙ s) := 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'
s : A ⥤ B
inst✝² : HasLimitsOfSize.{v₁, max v₁ u₁, v₂, u₂} A
inst✝¹ : PreservesLimitsOfSize.{v₁, max v₁ u₁, v₂, v₃, u₂, u₃} s
inst✝ : ReflectsIsomorphisms s
⊢ IsSheaf J P ↔ IsSheaf J (P ⋙ s)

Next tactic:
letI : ReflectsLimitsOfSize s := reflectsLimitsOfReflectsIsomorphisms
theorem isSheaf_iff_isSheaf_comp (s : A ⥤ B) [HasLimitsOfSize.{v₁, max v₁ u₁} A]
    [PreservesLimitsOfSize.{v₁, max v₁ u₁} s] [ReflectsIsomorphisms s] :
    IsSheaf J P ↔ IsSheaf J (P ⋙ s) := by
  letI : ReflectsLimitsOfSize s := reflectsLimitsOfReflectsIsomorphisms
  

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'
s : A ⥤ B
inst✝² : HasLimitsOfSize.{v₁, max v₁ u₁, v₂, u₂} A
inst✝¹ : PreservesLimitsOfSize.{v₁, max v₁ u₁, v₂, v₃, u₂, u₃} s
inst✝ : ReflectsIsomorphisms s
this : ReflectsLimitsOfSize.{v₁, max u₁ v₁, v₂, v₃, u₂, u₃} s := reflectsLimitsOfReflectsIsomorphisms
⊢ IsSheaf J P ↔ IsSheaf J (P ⋙ s)

Next tactic:
exact ⟨isSheaf_comp_of_isSheaf J P s, isSheaf_of_isSheaf_comp J P s⟩
/--
For a concrete category `(A, s)` where the forgetful functor `s : A ⥤ Type v` preserves limits and
reflects isomorphisms, and `A` has limits, an `A`-valued presheaf `P : Cᵒᵖ ⥤ A` is a sheaf iff its
underlying `Type`-valued presheaf `P ⋙ s : Cᵒᵖ ⥤ Type` is a sheaf.

Note this lemma applies for "algebraic" categories, eg groups, abelian groups and rings, but not
for the category of topological spaces, topological rings, etc since reflecting isomorphisms doesn't
hold.
-/
theorem isSheaf_iff_isSheaf_forget (s : A' ⥤ Type max v₁ u₁) [HasLimits A'] [PreservesLimits s]
    [ReflectsIsomorphisms s] : IsSheaf J P' ↔ IsSheaf J (P' ⋙ s) := 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'
s : A' ⥤ Type (max v₁ u₁)
inst✝² : HasLimits A'
inst✝¹ : PreservesLimits s
inst✝ : ReflectsIsomorphisms s
⊢ IsSheaf J P' ↔ IsSheaf J (P' ⋙ s)

Next tactic:
have : HasLimitsOfSize.{v₁, max v₁ u₁} A' := hasLimitsOfSizeShrink.{_, _, u₁, 0} A'
/--
For a concrete category `(A, s)` where the forgetful functor `s : A ⥤ Type v` preserves limits and
reflects isomorphisms, and `A` has limits, an `A`-valued presheaf `P : Cᵒᵖ ⥤ A` is a sheaf iff its
underlying `Type`-valued presheaf `P ⋙ s : Cᵒᵖ ⥤ Type` is a sheaf.

Note this lemma applies for "algebraic" categories, eg groups, abelian groups and rings, but not
for the category of topological spaces, topological rings, etc since reflecting isomorphisms doesn't
hold.
-/
theorem isSheaf_iff_isSheaf_forget (s : A' ⥤ Type max v₁ u₁) [HasLimits A'] [PreservesLimits s]
    [ReflectsIsomorphisms s] : IsSheaf J P' ↔ IsSheaf J (P' ⋙ s) := by
  have : HasLimitsOfSize.{v₁, max v₁ u₁} A' := hasLimitsOfSizeShrink.{_, _, u₁, 0} A'
  

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'
s : A' ⥤ Type (max v₁ u₁)
inst✝² : HasLimits A'
inst✝¹ : PreservesLimits s
inst✝ : ReflectsIsomorphisms s
this : HasLimitsOfSize.{v₁, max v₁ u₁, max u₁ v₁, u₂} A'
⊢ IsSheaf J P' ↔ IsSheaf J (P' ⋙ s)

Next tactic:
have : PreservesLimitsOfSize.{v₁, max v₁ u₁} s := preservesLimitsOfSizeShrink.{_, 0, _, u₁} s
/--
For a concrete category `(A, s)` where the forgetful functor `s : A ⥤ Type v` preserves limits and
reflects isomorphisms, and `A` has limits, an `A`-valued presheaf `P : Cᵒᵖ ⥤ A` is a sheaf iff its
underlying `Type`-valued presheaf `P ⋙ s : Cᵒᵖ ⥤ Type` is a sheaf.

Note this lemma applies for "algebraic" categories, eg groups, abelian groups and rings, but not
for the category of topological spaces, topological rings, etc since reflecting isomorphisms doesn't
hold.
-/
theorem isSheaf_iff_isSheaf_forget (s : A' ⥤ Type max v₁ u₁) [HasLimits A'] [PreservesLimits s]
    [ReflectsIsomorphisms s] : IsSheaf J P' ↔ IsSheaf J (P' ⋙ s) := by
  have : HasLimitsOfSize.{v₁, max v₁ u₁} A' := hasLimitsOfSizeShrink.{_, _, u₁, 0} A'
  have : PreservesLimitsOfSize.{v₁, max v₁ u₁} s := preservesLimitsOfSizeShrink.{_, 0, _, u₁} s
  

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'
s : A' ⥤ Type (max v₁ u₁)
inst✝² : HasLimits A'
inst✝¹ : PreservesLimits s
inst✝ : ReflectsIsomorphisms s
this✝ : HasLimitsOfSize.{v₁, max v₁ u₁, max u₁ v₁, u₂} A'
this : PreservesLimitsOfSize.{v₁, max v₁ u₁, max u₁ v₁, max u₁ v₁, u₂, max (u₁ + 1) (v₁ + 1)} s
⊢ IsSheaf J P' ↔ IsSheaf J (P' ⋙ s)

Next tactic:
apply isSheaf_iff_isSheaf_comp
instance uniqueBasis [Subsingleton R] : Unique (Basis ι R M) :=
  ⟨⟨⟨default⟩⟩, fun ⟨b⟩ => by 

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁵ : Semiring R
inst✝⁴ : AddCommMonoid M
inst✝³ : Module R M
inst✝² : AddCommMonoid M'
inst✝¹ : Module R M'
inst✝ : Subsingleton R
x✝ : Basis ι R M
b : M ≃ₗ[R] ι →₀ R
⊢ { repr := b } = default

Next tactic:
rw [Subsingleton.elim b]
theorem repr_injective : Injective (repr : Basis ι R M → M ≃ₗ[R] ι →₀ R) := fun f g h => by
  

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
f g : Basis ι R M
h : f.repr = g.repr
⊢ f = g

Next tactic:
cases f
theorem repr_injective : Injective (repr : Basis ι R M → M ≃ₗ[R] ι →₀ R) := fun f g h => by
  cases f; 

Proof state:
case ofRepr
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
g : Basis ι R M
repr✝ : M ≃ₗ[R] ι →₀ R
h : { repr := repr✝ }.repr = g.repr
⊢ { repr := repr✝ } = g

Next tactic:
cases g
theorem repr_injective : Injective (repr : Basis ι R M → M ≃ₗ[R] ι →₀ R) := fun f g h => by
  cases f; cases g; 

Proof state:
case ofRepr.ofRepr
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
repr✝¹ repr✝ : M ≃ₗ[R] ι →₀ R
h : { repr := repr✝¹ }.repr = { repr := repr✝ }.repr
⊢ { repr := repr✝¹ } = { repr := repr✝ }

Next tactic:
congr
/-- `b i` is the `i`th basis vector. -/
instance funLike : FunLike (Basis ι R M) ι fun _ => M where
  coe b i := b.repr.symm (Finsupp.single i 1)
  coe_injective' f g h := repr_injective <| LinearEquiv.symm_bijective.injective <|
    LinearEquiv.toLinearMap_injective <| by 

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
f g : Basis ι R M
h : (fun b i => (LinearEquiv.symm b.repr) fun₀ | i => 1) f = (fun b i => (LinearEquiv.symm b.repr) fun₀ | i => 1) g
⊢ ↑(LinearEquiv.symm f.repr) = ↑(LinearEquiv.symm g.repr)

Next tactic:
ext
/-- `b i` is the `i`th basis vector. -/
instance funLike : FunLike (Basis ι R M) ι fun _ => M where
  coe b i := b.repr.symm (Finsupp.single i 1)
  coe_injective' f g h := repr_injective <| LinearEquiv.symm_bijective.injective <|
    LinearEquiv.toLinearMap_injective <| by ext; 

Proof state:
case h.h
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
f g : Basis ι R M
h : (fun b i => (LinearEquiv.symm b.repr) fun₀ | i => 1) f = (fun b i => (LinearEquiv.symm b.repr) fun₀ | i => 1) g
a✝ : ι
⊢ (↑(LinearEquiv.symm f.repr) ∘ₗ Finsupp.lsingle a✝) 1 = (↑(LinearEquiv.symm g.repr) ∘ₗ Finsupp.lsingle a✝) 1

Next tactic:
exact congr_fun h _
theorem repr_symm_single : b.repr.symm (Finsupp.single i c) = c • b i :=
  calc
    b.repr.symm (Finsupp.single i c) = b.repr.symm (c • Finsupp.single i (1 : R)) := by
      

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
⊢ ((LinearEquiv.symm b.repr) fun₀ | i => c) = (LinearEquiv.symm b.repr) (c • fun₀ | i => 1)

Next tactic:
{ rw [Finsupp.smul_single', mul_one] }
theorem repr_symm_single : b.repr.symm (Finsupp.single i c) = c • b i :=
  calc
    b.repr.symm (Finsupp.single i c) = b.repr.symm (c • Finsupp.single i (1 : R)) := by
      { 

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
⊢ ((LinearEquiv.symm b.repr) fun₀ | i => c) = (LinearEquiv.symm b.repr) (c • fun₀ | i => 1)

Next tactic:
rw [Finsupp.smul_single', mul_one]
theorem repr_symm_single : b.repr.symm (Finsupp.single i c) = c • b i :=
  calc
    b.repr.symm (Finsupp.single i c) = b.repr.symm (c • Finsupp.single i (1 : R)) := by
      { rw [Finsupp.smul_single', mul_one] }
    _ = c • b i := by 

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
⊢ (LinearEquiv.symm b.repr) (c • fun₀ | i => 1) = c • b i

Next tactic:
rw [LinearEquiv.map_smul, repr_symm_single_one]
theorem repr_self_apply (j) [Decidable (i = j)] : b.repr (b i) j = if i = j then 1 else 0 := by
  

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁵ : Semiring R
inst✝⁴ : AddCommMonoid M
inst✝³ : Module R M
inst✝² : AddCommMonoid M'
inst✝¹ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
j : ι
inst✝ : Decidable (i = j)
⊢ (b.repr (b i)) j = if i = j then 1 else 0

Next tactic:
rw [repr_self, Finsupp.single_apply]
@[simp]
theorem repr_symm_apply (v) : b.repr.symm v = Finsupp.total ι M R b v :=
  calc
    b.repr.symm v = b.repr.symm (v.sum Finsupp.single) := by 

Proof state:
ι : Type u_1
ι' : Type u_2
R : Type u_3
R₂ : Type u_4
K : Type u_5
M : Type u_6
M' : Type u_7
M'' : Type u_8
V : Type u
V' : Type u_9
inst✝⁴ : Semiring R
inst✝³ : AddCommMonoid M
inst✝² : Module R M
inst✝¹ : AddCommMonoid M'
inst✝ : Module R M'
b b₁ : Basis ι R M
i : ι
c : R
x : M
v : ι →₀ R
⊢ (LinearEquiv.symm b.repr) v = (LinearEquiv.symm b.repr) (Finsupp.sum v Finsupp.single)

Next tactic:
simp