l3lab/ntp-mathlib huggingface dataset:
/-- Criterion for a subset `S ⊆ X` to be closed and discrete in terms of the punctured
neighbourhood filter at an arbitrary point of `X`. (Compare `discreteTopology_subtype_iff`.) -/
theorem isClosed_and_discrete_iff {S : Set X} :
    IsClosed S ∧ DiscreteTopology S ↔ ∀ x, Disjoint (𝓝[≠] x) (𝓟 S) := by
  rw [discreteTopology_subtype_iff, isClosed_iff_clusterPt, ← forall_and]
  congrm (∀ x, ?_)
  rw [← not_imp_not, clusterPt_iff_not_disjoint, not_not, ← disjoint_iff]
  constructor <;> intro H
  · by_cases hx : x ∈ S
    exacts [H.2 hx, (H.1 hx).mono_left nhdsWithin_le_nhds]
  · 

Proof state:
case a.mpr
X : Type u_1
Y : Type u_2
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace Y
f : X → Y
S : Set X
x : X
H : Disjoint (𝓝[≠] x) (𝓟 S)
⊢ (x ∉ S → Disjoint (𝓝 x) (𝓟 S)) ∧ (x ∈ S → Disjoint (𝓝[≠] x) (𝓟 S))

Next tactic:
refine ⟨fun hx ↦ ?_, fun _ ↦ H⟩
/-- Criterion for a subset `S ⊆ X` to be closed and discrete in terms of the punctured
neighbourhood filter at an arbitrary point of `X`. (Compare `discreteTopology_subtype_iff`.) -/
theorem isClosed_and_discrete_iff {S : Set X} :
    IsClosed S ∧ DiscreteTopology S ↔ ∀ x, Disjoint (𝓝[≠] x) (𝓟 S) := by
  rw [discreteTopology_subtype_iff, isClosed_iff_clusterPt, ← forall_and]
  congrm (∀ x, ?_)
  rw [← not_imp_not, clusterPt_iff_not_disjoint, not_not, ← disjoint_iff]
  constructor <;> intro H
  · by_cases hx : x ∈ S
    exacts [H.2 hx, (H.1 hx).mono_left nhdsWithin_le_nhds]
  · refine ⟨fun hx ↦ ?_, fun _ ↦ H⟩
    

Proof state:
case a.mpr
X : Type u_1
Y : Type u_2
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace Y
f : X → Y
S : Set X
x : X
H : Disjoint (𝓝[≠] x) (𝓟 S)
hx : x ∉ S
⊢ Disjoint (𝓝 x) (𝓟 S)

Next tactic:
simpa [disjoint_iff, nhdsWithin, inf_assoc, hx] using H
/-- In any topological space, the open sets with with discrete complement form a filter. -/
def Filter.codiscrete (X : Type*) [TopologicalSpace X] : Filter X where
  sets := {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
  univ_sets := ⟨isOpen_univ, compl_univ.symm ▸ Subsingleton.discreteTopology⟩
  sets_of_superset := by
    

Proof state:
X✝ : Type u_1
Y : Type u_2
inst✝² : TopologicalSpace X✝
inst✝¹ : TopologicalSpace Y
f : X✝ → Y
X : Type u_3
inst✝ : TopologicalSpace X
⊢ ∀ {x y : Set X}, x ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ} → x ⊆ y → y ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}

Next tactic:
intro U V hU hV
/-- In any topological space, the open sets with with discrete complement form a filter. -/
def Filter.codiscrete (X : Type*) [TopologicalSpace X] : Filter X where
  sets := {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
  univ_sets := ⟨isOpen_univ, compl_univ.symm ▸ Subsingleton.discreteTopology⟩
  sets_of_superset := by
    intro U V hU hV
    

Proof state:
X✝ : Type u_1
Y : Type u_2
inst✝² : TopologicalSpace X✝
inst✝¹ : TopologicalSpace Y
f : X✝ → Y
X : Type u_3
inst✝ : TopologicalSpace X
U V : Set X
hU : U ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
hV : U ⊆ V
⊢ V ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}

Next tactic:
simp_rw [← isClosed_compl_iff, isClosed_and_discrete_iff] at hU ⊢
/-- In any topological space, the open sets with with discrete complement form a filter. -/
def Filter.codiscrete (X : Type*) [TopologicalSpace X] : Filter X where
  sets := {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
  univ_sets := ⟨isOpen_univ, compl_univ.symm ▸ Subsingleton.discreteTopology⟩
  sets_of_superset := by
    intro U V hU hV
    simp_rw [← isClosed_compl_iff, isClosed_and_discrete_iff] at hU ⊢
    

Proof state:
X✝ : Type u_1
Y : Type u_2
inst✝² : TopologicalSpace X✝
inst✝¹ : TopologicalSpace Y
f : X✝ → Y
X : Type u_3
inst✝ : TopologicalSpace X
U V : Set X
hV : U ⊆ V
hU : U ∈ {U | ∀ (x : X), Disjoint (𝓝[≠] x) (𝓟 Uᶜ)}
⊢ V ∈ {U | ∀ (x : X), Disjoint (𝓝[≠] x) (𝓟 Uᶜ)}

Next tactic:
exact fun x ↦ (hU x).mono_right (principal_mono.mpr <| compl_subset_compl.mpr hV)
/-- In any topological space, the open sets with with discrete complement form a filter. -/
def Filter.codiscrete (X : Type*) [TopologicalSpace X] : Filter X where
  sets := {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
  univ_sets := ⟨isOpen_univ, compl_univ.symm ▸ Subsingleton.discreteTopology⟩
  sets_of_superset := by
    intro U V hU hV
    simp_rw [← isClosed_compl_iff, isClosed_and_discrete_iff] at hU ⊢
    exact fun x ↦ (hU x).mono_right (principal_mono.mpr <| compl_subset_compl.mpr hV)
  inter_sets := by
    

Proof state:
X✝ : Type u_1
Y : Type u_2
inst✝² : TopologicalSpace X✝
inst✝¹ : TopologicalSpace Y
f : X✝ → Y
X : Type u_3
inst✝ : TopologicalSpace X
⊢ ∀ {x y : Set X},
    x ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ} →
      y ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ} → x ∩ y ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}

Next tactic:
intro U V hU hV
/-- In any topological space, the open sets with with discrete complement form a filter. -/
def Filter.codiscrete (X : Type*) [TopologicalSpace X] : Filter X where
  sets := {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
  univ_sets := ⟨isOpen_univ, compl_univ.symm ▸ Subsingleton.discreteTopology⟩
  sets_of_superset := by
    intro U V hU hV
    simp_rw [← isClosed_compl_iff, isClosed_and_discrete_iff] at hU ⊢
    exact fun x ↦ (hU x).mono_right (principal_mono.mpr <| compl_subset_compl.mpr hV)
  inter_sets := by
    intro U V hU hV
    

Proof state:
X✝ : Type u_1
Y : Type u_2
inst✝² : TopologicalSpace X✝
inst✝¹ : TopologicalSpace Y
f : X✝ → Y
X : Type u_3
inst✝ : TopologicalSpace X
U V : Set X
hU : U ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
hV : V ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
⊢ U ∩ V ∈ {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}

Next tactic:
simp_rw [← isClosed_compl_iff, isClosed_and_discrete_iff] at hU hV ⊢
/-- In any topological space, the open sets with with discrete complement form a filter. -/
def Filter.codiscrete (X : Type*) [TopologicalSpace X] : Filter X where
  sets := {U | IsOpen U ∧ DiscreteTopology ↑Uᶜ}
  univ_sets := ⟨isOpen_univ, compl_univ.symm ▸ Subsingleton.discreteTopology⟩
  sets_of_superset := by
    intro U V hU hV
    simp_rw [← isClosed_compl_iff, isClosed_and_discrete_iff] at hU ⊢
    exact fun x ↦ (hU x).mono_right (principal_mono.mpr <| compl_subset_compl.mpr hV)
  inter_sets := by
    intro U V hU hV
    simp_rw [← isClosed_compl_iff, isClosed_and_discrete_iff] at hU hV ⊢
    

Proof state:
X✝ : Type u_1
Y : Type u_2
inst✝² : TopologicalSpace X✝
inst✝¹ : TopologicalSpace Y
f : X✝ → Y
X : Type u_3
inst✝ : TopologicalSpace X
U V : Set X
hU : U ∈ {U | ∀ (x : X), Disjoint (𝓝[≠] x) (𝓟 Uᶜ)}
hV : V ∈ {U | ∀ (x : X), Disjoint (𝓝[≠] x) (𝓟 Uᶜ)}
⊢ U ∩ V ∈ {U | ∀ (x : X), Disjoint (𝓝[≠] x) (𝓟 Uᶜ)}

Next tactic:
exact fun x ↦ compl_inter U V ▸ sup_principal ▸ disjoint_sup_right.mpr ⟨hU x, hV x⟩
/-- Any power of a left-regular element is left-regular. -/
theorem IsLeftRegular.pow (n : ℕ) (rla : IsLeftRegular a) : IsLeftRegular (a ^ n) := by
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
rla : IsLeftRegular a
⊢ IsLeftRegular (a ^ n)

Next tactic:
simp only [IsLeftRegular, ← mul_left_iterate, rla.iterate n]
/-- Any power of a right-regular element is right-regular. -/
theorem IsRightRegular.pow (n : ℕ) (rra : IsRightRegular a) : IsRightRegular (a ^ n) := by
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
rra : IsRightRegular a
⊢ IsRightRegular (a ^ n)

Next tactic:
rw [IsRightRegular, ← mul_right_iterate]
/-- Any power of a right-regular element is right-regular. -/
theorem IsRightRegular.pow (n : ℕ) (rra : IsRightRegular a) : IsRightRegular (a ^ n) := by
  rw [IsRightRegular, ← mul_right_iterate]
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
rra : IsRightRegular a
⊢ Function.Injective (fun x => x * a)^[n]

Next tactic:
exact rra.iterate n
/-- An element `a` is left-regular if and only if a positive power of `a` is left-regular. -/
theorem IsLeftRegular.pow_iff {n : ℕ} (n0 : 0 < n) : IsLeftRegular (a ^ n) ↔ IsLeftRegular a := by
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
n0 : 0 < n
⊢ IsLeftRegular (a ^ n) ↔ IsLeftRegular a

Next tactic:
refine' ⟨_, IsLeftRegular.pow n⟩
/-- An element `a` is left-regular if and only if a positive power of `a` is left-regular. -/
theorem IsLeftRegular.pow_iff {n : ℕ} (n0 : 0 < n) : IsLeftRegular (a ^ n) ↔ IsLeftRegular a := by
  refine' ⟨_, IsLeftRegular.pow n⟩
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
n0 : 0 < n
⊢ IsLeftRegular (a ^ n) → IsLeftRegular a

Next tactic:
rw [← Nat.succ_pred_eq_of_pos n0, pow_succ']
/-- An element `a` is left-regular if and only if a positive power of `a` is left-regular. -/
theorem IsLeftRegular.pow_iff {n : ℕ} (n0 : 0 < n) : IsLeftRegular (a ^ n) ↔ IsLeftRegular a := by
  refine' ⟨_, IsLeftRegular.pow n⟩
  rw [← Nat.succ_pred_eq_of_pos n0, pow_succ']
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
n0 : 0 < n
⊢ IsLeftRegular (a ^ Nat.pred n * a) → IsLeftRegular a

Next tactic:
exact IsLeftRegular.of_mul
/-- An element `a` is right-regular if and only if a positive power of `a` is right-regular. -/
theorem IsRightRegular.pow_iff {n : ℕ} (n0 : 0 < n) :
    IsRightRegular (a ^ n) ↔ IsRightRegular a := by
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
n0 : 0 < n
⊢ IsRightRegular (a ^ n) ↔ IsRightRegular a

Next tactic:
refine' ⟨_, IsRightRegular.pow n⟩
/-- An element `a` is right-regular if and only if a positive power of `a` is right-regular. -/
theorem IsRightRegular.pow_iff {n : ℕ} (n0 : 0 < n) :
    IsRightRegular (a ^ n) ↔ IsRightRegular a := by
  refine' ⟨_, IsRightRegular.pow n⟩
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
n0 : 0 < n
⊢ IsRightRegular (a ^ n) → IsRightRegular a

Next tactic:
rw [← Nat.succ_pred_eq_of_pos n0, pow_succ]
/-- An element `a` is right-regular if and only if a positive power of `a` is right-regular. -/
theorem IsRightRegular.pow_iff {n : ℕ} (n0 : 0 < n) :
    IsRightRegular (a ^ n) ↔ IsRightRegular a := by
  refine' ⟨_, IsRightRegular.pow n⟩
  rw [← Nat.succ_pred_eq_of_pos n0, pow_succ]
  

Proof state:
R : Type u_1
a b : R
inst✝ : Monoid R
n : ℕ
n0 : 0 < n
⊢ IsRightRegular (a * a ^ Nat.pred n) → IsRightRegular a

Next tactic:
exact IsRightRegular.of_mul
/--
The functor `restrictedYoneda` is isomorphic to the identity functor when evaluated at the yoneda
embedding.
-/
def restrictedYonedaYoneda : restrictedYoneda (yoneda : C ⥤ Cᵒᵖ ⥤ Type u₁) ≅ 𝟭 _ :=
  NatIso.ofComponents fun P =>
    NatIso.ofComponents (fun X => yonedaSectionsSmall X.unop _) @ fun X Y f =>
      funext fun x => by
        

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
X Y : Cᵒᵖ
f : X ⟶ Y
x : ((restrictedYoneda yoneda).obj P).obj X
⊢ (((restrictedYoneda yoneda).obj P).map f ≫ ((fun X => yonedaSectionsSmall X.unop P) Y).hom) x =
    (((fun X => yonedaSectionsSmall X.unop P) X).hom ≫ ((𝟭 (Cᵒᵖ ⥤ Type u₁)).obj P).map f) x

Next tactic:
dsimp
/--
The functor `restrictedYoneda` is isomorphic to the identity functor when evaluated at the yoneda
embedding.
-/
def restrictedYonedaYoneda : restrictedYoneda (yoneda : C ⥤ Cᵒᵖ ⥤ Type u₁) ≅ 𝟭 _ :=
  NatIso.ofComponents fun P =>
    NatIso.ofComponents (fun X => yonedaSectionsSmall X.unop _) @ fun X Y f =>
      funext fun x => by
        dsimp
        

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
X Y : Cᵒᵖ
f : X ⟶ Y
x : ((restrictedYoneda yoneda).obj P).obj X
⊢ x.app { unop := Y.unop } (𝟙 Y.unop ≫ f.unop) = P.map f (x.app { unop := X.unop } (𝟙 X.unop))

Next tactic:
have : x.app X (CategoryStruct.id (Opposite.unop X)) =
            (x.app X (𝟙 (Opposite.unop X))) := rfl
/--
The functor `restrictedYoneda` is isomorphic to the identity functor when evaluated at the yoneda
embedding.
-/
def restrictedYonedaYoneda : restrictedYoneda (yoneda : C ⥤ Cᵒᵖ ⥤ Type u₁) ≅ 𝟭 _ :=
  NatIso.ofComponents fun P =>
    NatIso.ofComponents (fun X => yonedaSectionsSmall X.unop _) @ fun X Y f =>
      funext fun x => by
        dsimp
        have : x.app X (CategoryStruct.id (Opposite.unop X)) =
            (x.app X (𝟙 (Opposite.unop X))) := rfl
        

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
X Y : Cᵒᵖ
f : X ⟶ Y
x : ((restrictedYoneda yoneda).obj P).obj X
this : x.app X (𝟙 X.unop) = x.app X (𝟙 X.unop)
⊢ x.app { unop := Y.unop } (𝟙 Y.unop ≫ f.unop) = P.map f (x.app { unop := X.unop } (𝟙 X.unop))

Next tactic:
rw [this]
/--
The functor `restrictedYoneda` is isomorphic to the identity functor when evaluated at the yoneda
embedding.
-/
def restrictedYonedaYoneda : restrictedYoneda (yoneda : C ⥤ Cᵒᵖ ⥤ Type u₁) ≅ 𝟭 _ :=
  NatIso.ofComponents fun P =>
    NatIso.ofComponents (fun X => yonedaSectionsSmall X.unop _) @ fun X Y f =>
      funext fun x => by
        dsimp
        have : x.app X (CategoryStruct.id (Opposite.unop X)) =
            (x.app X (𝟙 (Opposite.unop X))) := rfl
        rw [this]
        

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
X Y : Cᵒᵖ
f : X ⟶ Y
x : ((restrictedYoneda yoneda).obj P).obj X
this : x.app X (𝟙 X.unop) = x.app X (𝟙 X.unop)
⊢ x.app { unop := Y.unop } (𝟙 Y.unop ≫ f.unop) = P.map f (x.app X (𝟙 X.unop))

Next tactic:
rw [← FunctorToTypes.naturality _ _ x f (𝟙 _)]
/--
The functor `restrictedYoneda` is isomorphic to the identity functor when evaluated at the yoneda
embedding.
-/
def restrictedYonedaYoneda : restrictedYoneda (yoneda : C ⥤ Cᵒᵖ ⥤ Type u₁) ≅ 𝟭 _ :=
  NatIso.ofComponents fun P =>
    NatIso.ofComponents (fun X => yonedaSectionsSmall X.unop _) @ fun X Y f =>
      funext fun x => by
        dsimp
        have : x.app X (CategoryStruct.id (Opposite.unop X)) =
            (x.app X (𝟙 (Opposite.unop X))) := rfl
        rw [this]
        rw [← FunctorToTypes.naturality _ _ x f (𝟙 _)]
        

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
X Y : Cᵒᵖ
f : X ⟶ Y
x : ((restrictedYoneda yoneda).obj P).obj X
this : x.app X (𝟙 X.unop) = x.app X (𝟙 X.unop)
⊢ x.app { unop := Y.unop } (𝟙 Y.unop ≫ f.unop) = x.app Y ((yoneda.op.obj X).unop.map f (𝟙 X.unop))

Next tactic:
simp only [id_comp, Functor.op_obj, Opposite.unop_op, yoneda_obj_map, comp_id]
/-- (Implementation). The equivalence of homsets which helps construct the left adjoint to
`colimitAdj.restrictedYoneda`.
It is shown in `restrictYonedaHomEquivNatural` that this is a natural bijection.
-/
def restrictYonedaHomEquiv (P : Cᵒᵖ ⥤ Type u₁) (E : ℰ)
    {c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)} (t : IsColimit c) :
    (c.pt ⟶ E) ≃ (P ⟶ (restrictedYoneda A).obj E) :=
  ((uliftTrivial _).symm ≪≫ t.homIso' E).toEquiv.trans
    { toFun := fun k =>
        { app := fun c p => k.1 (Opposite.op ⟨_, p⟩)
          naturality := fun c c' f =>
            funext fun p =>
              (k.2
                  (Quiver.Hom.op ⟨f, rfl⟩ :
                    (Opposite.op ⟨c', P.map f p⟩ : P.Elementsᵒᵖ) ⟶ Opposite.op ⟨c, p⟩)).symm }
      invFun := fun τ =>
        { val := fun p => τ.app p.unop.1 p.unop.2
          property := @fun p p' f => by
            

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
E : ℰ
c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)
t : IsColimit c
τ : P ⟶ (restrictedYoneda A).obj E
p p' : (Functor.Elements P)ᵒᵖ
f : p ⟶ p'
⊢ ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ (fun p => τ.app p.unop.fst p.unop.snd) p' =
    (fun p => τ.app p.unop.fst p.unop.snd) p

Next tactic:
simp_rw [← f.unop.2]
/-- (Implementation). The equivalence of homsets which helps construct the left adjoint to
`colimitAdj.restrictedYoneda`.
It is shown in `restrictYonedaHomEquivNatural` that this is a natural bijection.
-/
def restrictYonedaHomEquiv (P : Cᵒᵖ ⥤ Type u₁) (E : ℰ)
    {c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)} (t : IsColimit c) :
    (c.pt ⟶ E) ≃ (P ⟶ (restrictedYoneda A).obj E) :=
  ((uliftTrivial _).symm ≪≫ t.homIso' E).toEquiv.trans
    { toFun := fun k =>
        { app := fun c p => k.1 (Opposite.op ⟨_, p⟩)
          naturality := fun c c' f =>
            funext fun p =>
              (k.2
                  (Quiver.Hom.op ⟨f, rfl⟩ :
                    (Opposite.op ⟨c', P.map f p⟩ : P.Elementsᵒᵖ) ⟶ Opposite.op ⟨c, p⟩)).symm }
      invFun := fun τ =>
        { val := fun p => τ.app p.unop.1 p.unop.2
          property := @fun p p' f => by
            simp_rw [← f.unop.2]
            

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
E : ℰ
c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)
t : IsColimit c
τ : P ⟶ (restrictedYoneda A).obj E
p p' : (Functor.Elements P)ᵒᵖ
f : p ⟶ p'
⊢ ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ τ.app p'.unop.fst p'.unop.snd =
    τ.app p.unop.fst (P.map (↑f.unop) p'.unop.snd)

Next tactic:
apply (congr_fun (τ.naturality f.unop.1) p'.unop.2).symm
/-- (Implementation). The equivalence of homsets which helps construct the left adjoint to
`colimitAdj.restrictedYoneda`.
It is shown in `restrictYonedaHomEquivNatural` that this is a natural bijection.
-/
def restrictYonedaHomEquiv (P : Cᵒᵖ ⥤ Type u₁) (E : ℰ)
    {c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)} (t : IsColimit c) :
    (c.pt ⟶ E) ≃ (P ⟶ (restrictedYoneda A).obj E) :=
  ((uliftTrivial _).symm ≪≫ t.homIso' E).toEquiv.trans
    { toFun := fun k =>
        { app := fun c p => k.1 (Opposite.op ⟨_, p⟩)
          naturality := fun c c' f =>
            funext fun p =>
              (k.2
                  (Quiver.Hom.op ⟨f, rfl⟩ :
                    (Opposite.op ⟨c', P.map f p⟩ : P.Elementsᵒᵖ) ⟶ Opposite.op ⟨c, p⟩)).symm }
      invFun := fun τ =>
        { val := fun p => τ.app p.unop.1 p.unop.2
          property := @fun p p' f => by
            simp_rw [← f.unop.2]
            apply (congr_fun (τ.naturality f.unop.1) p'.unop.2).symm }
      left_inv := by
        

Proof state:
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
E : ℰ
c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)
t : IsColimit c
⊢ Function.LeftInverse
    (fun τ =>
      { val := fun p => τ.app p.unop.fst p.unop.snd,
        property :=
          (_ :
            ∀ (p p' : (Functor.Elements P)ᵒᵖ) (f : p ⟶ p'),
              ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ (fun p => τ.app p.unop.fst p.unop.snd) p' =
                (fun p => τ.app p.unop.fst p.unop.snd) p) })
    fun k => NatTrans.mk fun c p => ↑k (Opposite.op { fst := c, snd := p })

Next tactic:
rintro ⟨k₁, k₂⟩
/-- (Implementation). The equivalence of homsets which helps construct the left adjoint to
`colimitAdj.restrictedYoneda`.
It is shown in `restrictYonedaHomEquivNatural` that this is a natural bijection.
-/
def restrictYonedaHomEquiv (P : Cᵒᵖ ⥤ Type u₁) (E : ℰ)
    {c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)} (t : IsColimit c) :
    (c.pt ⟶ E) ≃ (P ⟶ (restrictedYoneda A).obj E) :=
  ((uliftTrivial _).symm ≪≫ t.homIso' E).toEquiv.trans
    { toFun := fun k =>
        { app := fun c p => k.1 (Opposite.op ⟨_, p⟩)
          naturality := fun c c' f =>
            funext fun p =>
              (k.2
                  (Quiver.Hom.op ⟨f, rfl⟩ :
                    (Opposite.op ⟨c', P.map f p⟩ : P.Elementsᵒᵖ) ⟶ Opposite.op ⟨c, p⟩)).symm }
      invFun := fun τ =>
        { val := fun p => τ.app p.unop.1 p.unop.2
          property := @fun p p' f => by
            simp_rw [← f.unop.2]
            apply (congr_fun (τ.naturality f.unop.1) p'.unop.2).symm }
      left_inv := by
        rintro ⟨k₁, k₂⟩
        

Proof state:
case mk
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
E : ℰ
c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)
t : IsColimit c
k₁ : (j : (Functor.Elements P)ᵒᵖ) → ((CategoryOfElements.π P).leftOp ⋙ A).obj j ⟶ E
k₂ : ∀ {j j' : (Functor.Elements P)ᵒᵖ} (f : j ⟶ j'), ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ k₁ j' = k₁ j
⊢ (fun τ =>
        { val := fun p => τ.app p.unop.fst p.unop.snd,
          property :=
            (_ :
              ∀ (p p' : (Functor.Elements P)ᵒᵖ) (f : p ⟶ p'),
                ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ (fun p => τ.app p.unop.fst p.unop.snd) p' =
                  (fun p => τ.app p.unop.fst p.unop.snd) p) })
      ((fun k => NatTrans.mk fun c p => ↑k (Opposite.op { fst := c, snd := p })) { val := k₁, property := k₂ }) =
    { val := k₁, property := k₂ }

Next tactic:
ext
/-- (Implementation). The equivalence of homsets which helps construct the left adjoint to
`colimitAdj.restrictedYoneda`.
It is shown in `restrictYonedaHomEquivNatural` that this is a natural bijection.
-/
def restrictYonedaHomEquiv (P : Cᵒᵖ ⥤ Type u₁) (E : ℰ)
    {c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)} (t : IsColimit c) :
    (c.pt ⟶ E) ≃ (P ⟶ (restrictedYoneda A).obj E) :=
  ((uliftTrivial _).symm ≪≫ t.homIso' E).toEquiv.trans
    { toFun := fun k =>
        { app := fun c p => k.1 (Opposite.op ⟨_, p⟩)
          naturality := fun c c' f =>
            funext fun p =>
              (k.2
                  (Quiver.Hom.op ⟨f, rfl⟩ :
                    (Opposite.op ⟨c', P.map f p⟩ : P.Elementsᵒᵖ) ⟶ Opposite.op ⟨c, p⟩)).symm }
      invFun := fun τ =>
        { val := fun p => τ.app p.unop.1 p.unop.2
          property := @fun p p' f => by
            simp_rw [← f.unop.2]
            apply (congr_fun (τ.naturality f.unop.1) p'.unop.2).symm }
      left_inv := by
        rintro ⟨k₁, k₂⟩
        ext
        

Proof state:
case mk.a.h
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
E : ℰ
c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)
t : IsColimit c
k₁ : (j : (Functor.Elements P)ᵒᵖ) → ((CategoryOfElements.π P).leftOp ⋙ A).obj j ⟶ E
k₂ : ∀ {j j' : (Functor.Elements P)ᵒᵖ} (f : j ⟶ j'), ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ k₁ j' = k₁ j
x✝ : (Functor.Elements P)ᵒᵖ
⊢ ↑((fun τ =>
            { val := fun p => τ.app p.unop.fst p.unop.snd,
              property :=
                (_ :
                  ∀ (p p' : (Functor.Elements P)ᵒᵖ) (f : p ⟶ p'),
                    ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ (fun p => τ.app p.unop.fst p.unop.snd) p' =
                      (fun p => τ.app p.unop.fst p.unop.snd) p) })
          ((fun k => NatTrans.mk fun c p => ↑k (Opposite.op { fst := c, snd := p })) { val := k₁, property := k₂ }))
      x✝ =
    ↑{ val := k₁, property := k₂ } x✝

Next tactic:
dsimp
/-- (Implementation). The equivalence of homsets which helps construct the left adjoint to
`colimitAdj.restrictedYoneda`.
It is shown in `restrictYonedaHomEquivNatural` that this is a natural bijection.
-/
def restrictYonedaHomEquiv (P : Cᵒᵖ ⥤ Type u₁) (E : ℰ)
    {c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)} (t : IsColimit c) :
    (c.pt ⟶ E) ≃ (P ⟶ (restrictedYoneda A).obj E) :=
  ((uliftTrivial _).symm ≪≫ t.homIso' E).toEquiv.trans
    { toFun := fun k =>
        { app := fun c p => k.1 (Opposite.op ⟨_, p⟩)
          naturality := fun c c' f =>
            funext fun p =>
              (k.2
                  (Quiver.Hom.op ⟨f, rfl⟩ :
                    (Opposite.op ⟨c', P.map f p⟩ : P.Elementsᵒᵖ) ⟶ Opposite.op ⟨c, p⟩)).symm }
      invFun := fun τ =>
        { val := fun p => τ.app p.unop.1 p.unop.2
          property := @fun p p' f => by
            simp_rw [← f.unop.2]
            apply (congr_fun (τ.naturality f.unop.1) p'.unop.2).symm }
      left_inv := by
        rintro ⟨k₁, k₂⟩
        ext
        dsimp
        

Proof state:
case mk.a.h
C : Type u₁
inst✝¹ : SmallCategory C
ℰ : Type u₂
inst✝ : Category.{u₁, u₂} ℰ
A : C ⥤ ℰ
P : Cᵒᵖ ⥤ Type u₁
E : ℰ
c : Cocone ((CategoryOfElements.π P).leftOp ⋙ A)
t : IsColimit c
k₁ : (j : (Functor.Elements P)ᵒᵖ) → ((CategoryOfElements.π P).leftOp ⋙ A).obj j ⟶ E
k₂ : ∀ {j j' : (Functor.Elements P)ᵒᵖ} (f : j ⟶ j'), ((CategoryOfElements.π P).leftOp ⋙ A).map f ≫ k₁ j' = k₁ j
x✝ : (Functor.Elements P)ᵒᵖ
⊢ k₁ (Opposite.op { fst := x✝.unop.fst, snd := x✝.unop.snd }) = k₁ x✝

Next tactic:
congr 1