l3lab/ntp-mathlib huggingface dataset:
theorem compatible_iff_sieveCompatible (x : FamilyOfElements P (S : Presieve X)) :
    x.Compatible ↔ x.SieveCompatible := by
  constructor
  · intro h Y Z f g hf
    simpa using h (𝟙 _) g (S.downward_closed hf g) hf (id_comp _)
  · intro h Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ k
    

Proof state:
case mpr
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P S.arrows
h : FamilyOfElements.SieveCompatible x
Y₁ Y₂ Z : C
g₁ : Z ⟶ Y₁
g₂ : Z ⟶ Y₂
f₁ : Y₁ ⟶ X
f₂ : Y₂ ⟶ X
h₁ : S.arrows f₁
h₂ : S.arrows f₂
k : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ P.map g₁.op (x f₁ h₁) = P.map g₂.op (x f₂ h₂)

Next tactic:
simp_rw [← h f₁ g₁ h₁, ← h f₂ g₂ h₂]
theorem compatible_iff_sieveCompatible (x : FamilyOfElements P (S : Presieve X)) :
    x.Compatible ↔ x.SieveCompatible := by
  constructor
  · intro h Y Z f g hf
    simpa using h (𝟙 _) g (S.downward_closed hf g) hf (id_comp _)
  · intro h Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ k
    simp_rw [← h f₁ g₁ h₁, ← h f₂ g₂ h₂]
    

Proof state:
case mpr
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P S.arrows
h : FamilyOfElements.SieveCompatible x
Y₁ Y₂ Z : C
g₁ : Z ⟶ Y₁
g₂ : Z ⟶ Y₂
f₁ : Y₁ ⟶ X
f₂ : Y₂ ⟶ X
h₁ : S.arrows f₁
h₂ : S.arrows f₂
k : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ x (g₁ ≫ f₁) (_ : S.arrows (g₁ ≫ f₁)) = x (g₂ ≫ f₂) (_ : S.arrows (g₂ ≫ f₂))

Next tactic:
congr
/--
Given a family of elements `x` for the sieve `S` generated by a presieve `R`, if `x` is restricted
to `R` and then extended back up to `S`, the resulting extension equals `x`.
-/
@[simp]
theorem extend_restrict {x : FamilyOfElements P (generate R)} (t : x.Compatible) :
    (x.restrict (le_generate R)).sieveExtend = x := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P (generate R).arrows
t : FamilyOfElements.Compatible x
⊢ FamilyOfElements.sieveExtend (FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x) = x

Next tactic:
rw [compatible_iff_sieveCompatible] at t
/--
Given a family of elements `x` for the sieve `S` generated by a presieve `R`, if `x` is restricted
to `R` and then extended back up to `S`, the resulting extension equals `x`.
-/
@[simp]
theorem extend_restrict {x : FamilyOfElements P (generate R)} (t : x.Compatible) :
    (x.restrict (le_generate R)).sieveExtend = x := by
  rw [compatible_iff_sieveCompatible] at t
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P (generate R).arrows
t : FamilyOfElements.SieveCompatible x
⊢ FamilyOfElements.sieveExtend (FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x) = x

Next tactic:
funext _ _ h
/--
Given a family of elements `x` for the sieve `S` generated by a presieve `R`, if `x` is restricted
to `R` and then extended back up to `S`, the resulting extension equals `x`.
-/
@[simp]
theorem extend_restrict {x : FamilyOfElements P (generate R)} (t : x.Compatible) :
    (x.restrict (le_generate R)).sieveExtend = x := by
  rw [compatible_iff_sieveCompatible] at t
  funext _ _ h
  

Proof state:
case h.h.h
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P (generate R).arrows
t : FamilyOfElements.SieveCompatible x
x✝¹ : C
x✝ : x✝¹ ⟶ X
h : (generate R).arrows x✝
⊢ FamilyOfElements.sieveExtend (FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x) x✝ h = x x✝ h

Next tactic:
apply (t _ _ _).symm.trans
/--
Given a family of elements `x` for the sieve `S` generated by a presieve `R`, if `x` is restricted
to `R` and then extended back up to `S`, the resulting extension equals `x`.
-/
@[simp]
theorem extend_restrict {x : FamilyOfElements P (generate R)} (t : x.Compatible) :
    (x.restrict (le_generate R)).sieveExtend = x := by
  rw [compatible_iff_sieveCompatible] at t
  funext _ _ h
  apply (t _ _ _).symm.trans
  

Proof state:
case h.h.h
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P (generate R).arrows
t : FamilyOfElements.SieveCompatible x
x✝¹ : C
x✝ : x✝¹ ⟶ X
h : (generate R).arrows x✝
⊢ x
      (Exists.choose (_ : ∃ h_1 g, R g ∧ h_1 ≫ g = x✝) ≫
        Exists.choose (_ : ∃ g, R g ∧ Exists.choose (_ : ∃ h_1 g, R g ∧ h_1 ≫ g = x✝) ≫ g = x✝))
      (_ :
        (generate R).arrows
          (Exists.choose (_ : ∃ h_1 g, R g ∧ h_1 ≫ g = x✝) ≫
            Exists.choose (_ : ∃ g, R g ∧ Exists.choose (_ : ∃ h_1 g, R g ∧ h_1 ≫ g = x✝) ≫ g = x✝))) =
    x x✝ h

Next tactic:
congr
/--
Given a family of elements `x` for the sieve `S` generated by a presieve `R`, if `x` is restricted
to `R` and then extended back up to `S`, the resulting extension equals `x`.
-/
@[simp]
theorem extend_restrict {x : FamilyOfElements P (generate R)} (t : x.Compatible) :
    (x.restrict (le_generate R)).sieveExtend = x := by
  rw [compatible_iff_sieveCompatible] at t
  funext _ _ h
  apply (t _ _ _).symm.trans
  congr
  

Proof state:
case h.h.h.e_f
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P (generate R).arrows
t : FamilyOfElements.SieveCompatible x
x✝¹ : C
x✝ : x✝¹ ⟶ X
h : (generate R).arrows x✝
⊢ Exists.choose (_ : ∃ h_1 g, R g ∧ h_1 ≫ g = x✝) ≫
      Exists.choose (_ : ∃ g, R g ∧ Exists.choose (_ : ∃ h_1 g, R g ∧ h_1 ≫ g = x✝) ≫ g = x✝) =
    x✝

Next tactic:
exact h.choose_spec.choose_spec.choose_spec.2
/--
Two compatible families on the sieve generated by a presieve `R` are equal if and only if they are
equal when restricted to `R`.
-/
theorem restrict_inj {x₁ x₂ : FamilyOfElements P (generate R)} (t₁ : x₁.Compatible)
    (t₂ : x₂.Compatible) : x₁.restrict (le_generate R) = x₂.restrict (le_generate R) → x₁ = x₂ :=
  fun h => by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x₁ x₂ : FamilyOfElements P (generate R).arrows
t₁ : FamilyOfElements.Compatible x₁
t₂ : FamilyOfElements.Compatible x₂
h :
  FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₁ =
    FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₂
⊢ x₁ = x₂

Next tactic:
rw [← extend_restrict t₁, ← extend_restrict t₂]
/--
Two compatible families on the sieve generated by a presieve `R` are equal if and only if they are
equal when restricted to `R`.
-/
theorem restrict_inj {x₁ x₂ : FamilyOfElements P (generate R)} (t₁ : x₁.Compatible)
    (t₂ : x₂.Compatible) : x₁.restrict (le_generate R) = x₂.restrict (le_generate R) → x₁ = x₂ :=
  fun h => by
  rw [← extend_restrict t₁, ← extend_restrict t₂]
  -- porting note: congr fails to make progress
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x₁ x₂ : FamilyOfElements P (generate R).arrows
t₁ : FamilyOfElements.Compatible x₁
t₂ : FamilyOfElements.Compatible x₂
h :
  FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₁ =
    FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₂
⊢ FamilyOfElements.sieveExtend (FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₁) =
    FamilyOfElements.sieveExtend (FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₂)

Next tactic:
apply congr_arg
/--
Two compatible families on the sieve generated by a presieve `R` are equal if and only if they are
equal when restricted to `R`.
-/
theorem restrict_inj {x₁ x₂ : FamilyOfElements P (generate R)} (t₁ : x₁.Compatible)
    (t₂ : x₂.Compatible) : x₁.restrict (le_generate R) = x₂.restrict (le_generate R) → x₁ = x₂ :=
  fun h => by
  rw [← extend_restrict t₁, ← extend_restrict t₂]
  -- porting note: congr fails to make progress
  apply congr_arg
  

Proof state:
case h
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x₁ x₂ : FamilyOfElements P (generate R).arrows
t₁ : FamilyOfElements.Compatible x₁
t₂ : FamilyOfElements.Compatible x₂
h :
  FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₁ =
    FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₂
⊢ FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₁ =
    FamilyOfElements.restrict (_ : R ≤ (generate R).arrows) x₂

Next tactic:
exact h
theorem FamilyOfElements.comp_of_compatible (S : Sieve X) {x : FamilyOfElements P S}
    (t : x.Compatible) {f : Y ⟶ X} (hf : S f) {Z} (g : Z ⟶ Y) :
    x (g ≫ f) (S.downward_closed hf g) = P.map g.op (x f hf) := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S✝ : Sieve X
R : Presieve X
S : Sieve X
x : FamilyOfElements P S.arrows
t : Compatible x
f : Y ⟶ X
hf : S.arrows f
Z : C
g : Z ⟶ Y
⊢ x (g ≫ f) (_ : S.arrows (g ≫ f)) = P.map g.op (x f hf)

Next tactic:
simpa using t (𝟙 _) g (S.downward_closed hf g) hf (id_comp _)
theorem FamilyOfElements.Compatible.functorPullback (h : x.Compatible) :
    (x.functorPullback F).Compatible := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
D : Type u₂
inst✝ : Category.{v₂, u₂} D
F : D ⥤ C
Z : D
T : Presieve (F.obj Z)
x : FamilyOfElements P T
h : Compatible x
⊢ Compatible (FamilyOfElements.functorPullback F x)

Next tactic:
intro Z₁ Z₂ W g₁ g₂ f₁ f₂ h₁ h₂ eq
theorem FamilyOfElements.Compatible.functorPullback (h : x.Compatible) :
    (x.functorPullback F).Compatible := by
  intro Z₁ Z₂ W g₁ g₂ f₁ f₂ h₁ h₂ eq
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
D : Type u₂
inst✝ : Category.{v₂, u₂} D
F : D ⥤ C
Z : D
T : Presieve (F.obj Z)
x : FamilyOfElements P T
h : Compatible x
Z₁ Z₂ W : D
g₁ : W ⟶ Z₁
g₂ : W ⟶ Z₂
f₁ : Z₁ ⟶ Z
f₂ : Z₂ ⟶ Z
h₁ : Presieve.functorPullback F T f₁
h₂ : Presieve.functorPullback F T f₂
eq : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ (F.op ⋙ P).map g₁.op (FamilyOfElements.functorPullback F x f₁ h₁) =
    (F.op ⋙ P).map g₂.op (FamilyOfElements.functorPullback F x f₂ h₂)

Next tactic:
exact h (F.map g₁) (F.map g₂) h₁ h₂ (by simp only [← F.map_comp, eq])
theorem FamilyOfElements.Compatible.functorPullback (h : x.Compatible) :
    (x.functorPullback F).Compatible := by
  intro Z₁ Z₂ W g₁ g₂ f₁ f₂ h₁ h₂ eq
  exact h (F.map g₁) (F.map g₂) h₁ h₂ (by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
D : Type u₂
inst✝ : Category.{v₂, u₂} D
F : D ⥤ C
Z : D
T : Presieve (F.obj Z)
x : FamilyOfElements P T
h : Compatible x
Z₁ Z₂ W : D
g₁ : W ⟶ Z₁
g₂ : W ⟶ Z₂
f₁ : Z₁ ⟶ Z
f₂ : Z₂ ⟶ Z
h₁ : Presieve.functorPullback F T f₁
h₂ : Presieve.functorPullback F T f₂
eq : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ F.map g₁ ≫ F.map f₁ = F.map g₂ ≫ F.map f₂

Next tactic:
simp only [← F.map_comp, eq]
/-- Given a family of elements of a sieve `S` on `X` whose values factors through `F`, we can
realize it as a family of elements of `S.functorPushforward F`. Since the preimage is obtained by
choice, this is not well-defined generally.
-/
noncomputable def FamilyOfElements.functorPushforward {D : Type u₂} [Category.{v₂} D] (F : D ⥤ C)
    {X : D} {T : Presieve X} (x : FamilyOfElements (F.op ⋙ P) T) :
    FamilyOfElements P (T.functorPushforward F) := fun Y f h => by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X✝ Y✝ : C
S : Sieve X✝
R : Presieve X✝
D : Type u₂
inst✝ : Category.{v₂, u₂} D
F : D ⥤ C
X : D
T : Presieve X
x : FamilyOfElements (F.op ⋙ P) T
Y : C
f : Y ⟶ F.obj X
h : Presieve.functorPushforward F T f
⊢ P.obj (op Y)

Next tactic:
obtain ⟨Z, g, h, h₁, _⟩ := getFunctorPushforwardStructure h
/-- Given a family of elements of a sieve `S` on `X` whose values factors through `F`, we can
realize it as a family of elements of `S.functorPushforward F`. Since the preimage is obtained by
choice, this is not well-defined generally.
-/
noncomputable def FamilyOfElements.functorPushforward {D : Type u₂} [Category.{v₂} D] (F : D ⥤ C)
    {X : D} {T : Presieve X} (x : FamilyOfElements (F.op ⋙ P) T) :
    FamilyOfElements P (T.functorPushforward F) := fun Y f h => by
  obtain ⟨Z, g, h, h₁, _⟩ := getFunctorPushforwardStructure h
  

Proof state:
case mk
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X✝ Y✝ : C
S : Sieve X✝
R : Presieve X✝
D : Type u₂
inst✝ : Category.{v₂, u₂} D
F : D ⥤ C
X : D
T : Presieve X
x : FamilyOfElements (F.op ⋙ P) T
Y : C
f : Y ⟶ F.obj X
h✝ : Presieve.functorPushforward F T f
Z : D
g : Z ⟶ X
h : Y ⟶ F.obj Z
h₁ : T g
fac✝ : f = h ≫ F.map g
⊢ P.obj (op Y)

Next tactic:
exact P.map h.op (x g h₁)
theorem FamilyOfElements.Compatible.pullback (f : Y ⟶ X) {x : FamilyOfElements P S}
    (h : x.Compatible) : (x.pullback f).Compatible := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : Y ⟶ X
x : FamilyOfElements P S.arrows
h : Compatible x
⊢ Compatible (FamilyOfElements.pullback f x)

Next tactic:
simp only [compatible_iff_sieveCompatible] at h ⊢
theorem FamilyOfElements.Compatible.pullback (f : Y ⟶ X) {x : FamilyOfElements P S}
    (h : x.Compatible) : (x.pullback f).Compatible := by
  simp only [compatible_iff_sieveCompatible] at h ⊢
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : Y ⟶ X
x : FamilyOfElements P S.arrows
h : SieveCompatible x
⊢ SieveCompatible (FamilyOfElements.pullback f x)

Next tactic:
intro W Z f₁ f₂ hf
theorem FamilyOfElements.Compatible.pullback (f : Y ⟶ X) {x : FamilyOfElements P S}
    (h : x.Compatible) : (x.pullback f).Compatible := by
  simp only [compatible_iff_sieveCompatible] at h ⊢
  intro W Z f₁ f₂ hf
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : Y ⟶ X
x : FamilyOfElements P S.arrows
h : SieveCompatible x
W Z : C
f₁ : W ⟶ Y
f₂ : Z ⟶ W
hf : (Sieve.pullback f S).arrows f₁
⊢ FamilyOfElements.pullback f x (f₂ ≫ f₁) (_ : (Sieve.pullback f S).arrows (f₂ ≫ f₁)) =
    P.map f₂.op (FamilyOfElements.pullback f x f₁ hf)

Next tactic:
unfold FamilyOfElements.pullback
theorem FamilyOfElements.Compatible.pullback (f : Y ⟶ X) {x : FamilyOfElements P S}
    (h : x.Compatible) : (x.pullback f).Compatible := by
  simp only [compatible_iff_sieveCompatible] at h ⊢
  intro W Z f₁ f₂ hf
  unfold FamilyOfElements.pullback
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : Y ⟶ X
x : FamilyOfElements P S.arrows
h : SieveCompatible x
W Z : C
f₁ : W ⟶ Y
f₂ : Z ⟶ W
hf : (Sieve.pullback f S).arrows f₁
⊢ x ((f₂ ≫ f₁) ≫ f) (_ : (Sieve.pullback f S).arrows (f₂ ≫ f₁)) = P.map f₂.op (x (f₁ ≫ f) hf)

Next tactic:
rw [← h (f₁ ≫ f) f₂ hf]
theorem FamilyOfElements.Compatible.pullback (f : Y ⟶ X) {x : FamilyOfElements P S}
    (h : x.Compatible) : (x.pullback f).Compatible := by
  simp only [compatible_iff_sieveCompatible] at h ⊢
  intro W Z f₁ f₂ hf
  unfold FamilyOfElements.pullback
  rw [← h (f₁ ≫ f) f₂ hf]
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : Y ⟶ X
x : FamilyOfElements P S.arrows
h : SieveCompatible x
W Z : C
f₁ : W ⟶ Y
f₂ : Z ⟶ W
hf : (Sieve.pullback f S).arrows f₁
⊢ x ((f₂ ≫ f₁) ≫ f) (_ : (Sieve.pullback f S).arrows (f₂ ≫ f₁)) = x (f₂ ≫ f₁ ≫ f) (_ : S.arrows (f₂ ≫ f₁ ≫ f))

Next tactic:
congr 1
theorem FamilyOfElements.Compatible.pullback (f : Y ⟶ X) {x : FamilyOfElements P S}
    (h : x.Compatible) : (x.pullback f).Compatible := by
  simp only [compatible_iff_sieveCompatible] at h ⊢
  intro W Z f₁ f₂ hf
  unfold FamilyOfElements.pullback
  rw [← h (f₁ ≫ f) f₂ hf]
  congr 1
  

Proof state:
case e_f
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : Y ⟶ X
x : FamilyOfElements P S.arrows
h : SieveCompatible x
W Z : C
f₁ : W ⟶ Y
f₂ : Z ⟶ W
hf : (Sieve.pullback f S).arrows f₁
⊢ (f₂ ≫ f₁) ≫ f = f₂ ≫ f₁ ≫ f

Next tactic:
simp only [assoc]
theorem FamilyOfElements.Compatible.compPresheafMap (f : P ⟶ Q) {x : FamilyOfElements P R}
    (h : x.Compatible) : (x.compPresheafMap f).Compatible := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : P ⟶ Q
x : FamilyOfElements P R
h : Compatible x
⊢ Compatible (FamilyOfElements.compPresheafMap f x)

Next tactic:
intro Z₁ Z₂ W g₁ g₂ f₁ f₂ h₁ h₂ eq
theorem FamilyOfElements.Compatible.compPresheafMap (f : P ⟶ Q) {x : FamilyOfElements P R}
    (h : x.Compatible) : (x.compPresheafMap f).Compatible := by
  intro Z₁ Z₂ W g₁ g₂ f₁ f₂ h₁ h₂ eq
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : P ⟶ Q
x : FamilyOfElements P R
h : Compatible x
Z₁ Z₂ W : C
g₁ : W ⟶ Z₁
g₂ : W ⟶ Z₂
f₁ : Z₁ ⟶ X
f₂ : Z₂ ⟶ X
h₁ : R f₁
h₂ : R f₂
eq : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ Q.map g₁.op (FamilyOfElements.compPresheafMap f x f₁ h₁) = Q.map g₂.op (FamilyOfElements.compPresheafMap f x f₂ h₂)

Next tactic:
unfold FamilyOfElements.compPresheafMap
theorem FamilyOfElements.Compatible.compPresheafMap (f : P ⟶ Q) {x : FamilyOfElements P R}
    (h : x.Compatible) : (x.compPresheafMap f).Compatible := by
  intro Z₁ Z₂ W g₁ g₂ f₁ f₂ h₁ h₂ eq
  unfold FamilyOfElements.compPresheafMap
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
f : P ⟶ Q
x : FamilyOfElements P R
h : Compatible x
Z₁ Z₂ W : C
g₁ : W ⟶ Z₁
g₂ : W ⟶ Z₂
f₁ : Z₁ ⟶ X
f₂ : Z₂ ⟶ X
h₁ : R f₁
h₂ : R f₂
eq : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ Q.map g₁.op (f.app (op Z₁) (x f₁ h₁)) = Q.map g₂.op (f.app (op Z₂) (x f₂ h₂))

Next tactic:
rwa [← FunctorToTypes.naturality, ← FunctorToTypes.naturality, h]
theorem FamilyOfElements.IsAmalgamation.compPresheafMap {x : FamilyOfElements P R} {t} (f : P ⟶ Q)
    (h : x.IsAmalgamation t) : (x.compPresheafMap f).IsAmalgamation (f.app (op X) t) := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
f : P ⟶ Q
h : IsAmalgamation x t
⊢ IsAmalgamation (FamilyOfElements.compPresheafMap f x) (f.app (op X) t)

Next tactic:
intro Y g hg
theorem FamilyOfElements.IsAmalgamation.compPresheafMap {x : FamilyOfElements P R} {t} (f : P ⟶ Q)
    (h : x.IsAmalgamation t) : (x.compPresheafMap f).IsAmalgamation (f.app (op X) t) := by
  intro Y g hg
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y✝ : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
f : P ⟶ Q
h : IsAmalgamation x t
Y : C
g : Y ⟶ X
hg : R g
⊢ Q.map g.op (f.app (op X) t) = FamilyOfElements.compPresheafMap f x g hg

Next tactic:
dsimp [FamilyOfElements.compPresheafMap]
theorem FamilyOfElements.IsAmalgamation.compPresheafMap {x : FamilyOfElements P R} {t} (f : P ⟶ Q)
    (h : x.IsAmalgamation t) : (x.compPresheafMap f).IsAmalgamation (f.app (op X) t) := by
  intro Y g hg
  dsimp [FamilyOfElements.compPresheafMap]
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y✝ : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
f : P ⟶ Q
h : IsAmalgamation x t
Y : C
g : Y ⟶ X
hg : R g
⊢ Q.map g.op (f.app (op X) t) = f.app (op Y) (x g hg)

Next tactic:
change (f.app _ ≫ Q.map _) _ = _
theorem FamilyOfElements.IsAmalgamation.compPresheafMap {x : FamilyOfElements P R} {t} (f : P ⟶ Q)
    (h : x.IsAmalgamation t) : (x.compPresheafMap f).IsAmalgamation (f.app (op X) t) := by
  intro Y g hg
  dsimp [FamilyOfElements.compPresheafMap]
  change (f.app _ ≫ Q.map _) _ = _
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y✝ : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
f : P ⟶ Q
h : IsAmalgamation x t
Y : C
g : Y ⟶ X
hg : R g
⊢ (f.app (op X) ≫ Q.map g.op) t = f.app (op Y) (x g hg)

Next tactic:
rw [← f.naturality, types_comp_apply, h g hg]
theorem is_compatible_of_exists_amalgamation (x : FamilyOfElements P R)
    (h : ∃ t, x.IsAmalgamation t) : x.Compatible := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
h : ∃ t, FamilyOfElements.IsAmalgamation x t
⊢ FamilyOfElements.Compatible x

Next tactic:
cases' h with t ht
theorem is_compatible_of_exists_amalgamation (x : FamilyOfElements P R)
    (h : ∃ t, x.IsAmalgamation t) : x.Compatible := by
  cases' h with t ht
  

Proof state:
case intro
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
ht : FamilyOfElements.IsAmalgamation x t
⊢ FamilyOfElements.Compatible x

Next tactic:
intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ comm
theorem is_compatible_of_exists_amalgamation (x : FamilyOfElements P R)
    (h : ∃ t, x.IsAmalgamation t) : x.Compatible := by
  cases' h with t ht
  intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ comm
  

Proof state:
case intro
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
ht : FamilyOfElements.IsAmalgamation x t
Y₁ Y₂ Z : C
g₁ : Z ⟶ Y₁
g₂ : Z ⟶ Y₂
f₁ : Y₁ ⟶ X
f₂ : Y₂ ⟶ X
h₁ : R f₁
h₂ : R f₂
comm : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ P.map g₁.op (x f₁ h₁) = P.map g₂.op (x f₂ h₂)

Next tactic:
rw [← ht _ h₁, ← ht _ h₂, ← FunctorToTypes.map_comp_apply, ← op_comp, comm]
theorem is_compatible_of_exists_amalgamation (x : FamilyOfElements P R)
    (h : ∃ t, x.IsAmalgamation t) : x.Compatible := by
  cases' h with t ht
  intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ comm
  rw [← ht _ h₁, ← ht _ h₂, ← FunctorToTypes.map_comp_apply, ← op_comp, comm]
  

Proof state:
case intro
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
ht : FamilyOfElements.IsAmalgamation x t
Y₁ Y₂ Z : C
g₁ : Z ⟶ Y₁
g₂ : Z ⟶ Y₂
f₁ : Y₁ ⟶ X
f₂ : Y₂ ⟶ X
h₁ : R f₁
h₂ : R f₂
comm : g₁ ≫ f₁ = g₂ ≫ f₂
⊢ P.map (g₂ ≫ f₂).op t = P.map g₂.op (P.map f₂.op t)

Next tactic:
simp
theorem isAmalgamation_sieveExtend {R : Presieve X} (x : FamilyOfElements P R) (t : P.obj (op X))
    (ht : x.IsAmalgamation t) : x.sieveExtend.IsAmalgamation t := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R✝ R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
ht : FamilyOfElements.IsAmalgamation x t
⊢ FamilyOfElements.IsAmalgamation (FamilyOfElements.sieveExtend x) t

Next tactic:
intro Y f hf
theorem isAmalgamation_sieveExtend {R : Presieve X} (x : FamilyOfElements P R) (t : P.obj (op X))
    (ht : x.IsAmalgamation t) : x.sieveExtend.IsAmalgamation t := by
  intro Y f hf
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y✝ : C
S : Sieve X
R✝ R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
ht : FamilyOfElements.IsAmalgamation x t
Y : C
f : Y ⟶ X
hf : (generate R).arrows f
⊢ P.map f.op t = FamilyOfElements.sieveExtend x f hf

Next tactic:
dsimp [FamilyOfElements.sieveExtend]
theorem isAmalgamation_sieveExtend {R : Presieve X} (x : FamilyOfElements P R) (t : P.obj (op X))
    (ht : x.IsAmalgamation t) : x.sieveExtend.IsAmalgamation t := by
  intro Y f hf
  dsimp [FamilyOfElements.sieveExtend]
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y✝ : C
S : Sieve X
R✝ R : Presieve X
x : FamilyOfElements P R
t : P.obj (op X)
ht : FamilyOfElements.IsAmalgamation x t
Y : C
f : Y ⟶ X
hf : (generate R).arrows f
⊢ P.map f.op t =
    P.map (Exists.choose (_ : ∃ h g, R g ∧ h ≫ g = f)).op
      (x (Exists.choose (_ : ∃ g, R g ∧ Exists.choose (_ : ∃ h g, R g ∧ h ≫ g = f) ≫ g = f))
        (_ : R (Exists.choose (_ : ∃ g, R g ∧ Exists.choose (_ : ∃ h g, R g ∧ h ≫ g = f) ≫ g = f))))

Next tactic:
rw [← ht _, ← FunctorToTypes.map_comp_apply, ← op_comp, hf.choose_spec.choose_spec.choose_spec.2]
theorem isSeparatedFor_iff_generate :
    IsSeparatedFor P R ↔ IsSeparatedFor P (generate R : Presieve X) := by
  

Proof state:
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
⊢ IsSeparatedFor P R ↔ IsSeparatedFor P (generate R).arrows

Next tactic:
constructor
theorem isSeparatedFor_iff_generate :
    IsSeparatedFor P R ↔ IsSeparatedFor P (generate R : Presieve X) := by
  constructor
  · 

Proof state:
case mp
C : Type u₁
inst✝ : Category.{v₁, u₁} C
P Q U : Cᵒᵖ ⥤ Type w
X Y : C
S : Sieve X
R : Presieve X
⊢ IsSeparatedFor P R → IsSeparatedFor P (generate R).arrows

Next tactic:
intro h x t₁ t₂ ht₁ ht₂