l3lab/ntp-mathlib huggingface dataset:
theorem pow_natDegree_le_of_root_of_monic_mem {x : R} (hroot : IsRoot f x) (hmo : f.Monic) :
    ∀ i, f.natDegree ≤ i → x ^ i ∈ 𝓟 := by
  intro i hi
  obtain ⟨k, hk⟩ := exists_add_of_le hi
  rw [hk, pow_add]
  suffices x ^ f.natDegree ∈ 𝓟 by exact mul_mem_right (x ^ k) 𝓟 this
  rw [IsRoot.def, eval_eq_sum_range, Finset.range_add_one,
    Finset.sum_insert Finset.not_mem_range_self, Finset.sum_range, hmo.coeff_natDegree, one_mul] at
    *
  

Proof state:
case intro
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : R
hroot : x ^ natDegree f + ∑ i : Fin (natDegree f), coeff f ↑i * x ^ ↑i = 0
hmo : Monic f
i : ℕ
hi : natDegree f ≤ i
k : ℕ
hk : i = natDegree f + k
⊢ x ^ natDegree f ∈ 𝓟

Next tactic:
rw [eq_neg_of_add_eq_zero_left hroot, Ideal.neg_mem_iff]
theorem pow_natDegree_le_of_root_of_monic_mem {x : R} (hroot : IsRoot f x) (hmo : f.Monic) :
    ∀ i, f.natDegree ≤ i → x ^ i ∈ 𝓟 := by
  intro i hi
  obtain ⟨k, hk⟩ := exists_add_of_le hi
  rw [hk, pow_add]
  suffices x ^ f.natDegree ∈ 𝓟 by exact mul_mem_right (x ^ k) 𝓟 this
  rw [IsRoot.def, eval_eq_sum_range, Finset.range_add_one,
    Finset.sum_insert Finset.not_mem_range_self, Finset.sum_range, hmo.coeff_natDegree, one_mul] at
    *
  rw [eq_neg_of_add_eq_zero_left hroot, Ideal.neg_mem_iff]
  

Proof state:
case intro
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : R
hroot : x ^ natDegree f + ∑ i : Fin (natDegree f), coeff f ↑i * x ^ ↑i = 0
hmo : Monic f
i : ℕ
hi : natDegree f ≤ i
k : ℕ
hk : i = natDegree f + k
⊢ ∑ i : Fin (natDegree f), coeff f ↑i * x ^ ↑i ∈ 𝓟

Next tactic:
refine' Submodule.sum_mem _ fun i _ => mul_mem_right _ _ (hf.mem (Fin.is_lt i))
theorem pow_natDegree_le_of_aeval_zero_of_monic_mem_map {x : S} (hx : aeval x f = 0)
    (hmo : f.Monic) :
    ∀ i, (f.map (algebraMap R S)).natDegree ≤ i → x ^ i ∈ 𝓟.map (algebraMap R S) := by
  

Proof state:
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : S
hx : (aeval x) f = 0
hmo : Monic f
⊢ ∀ (i : ℕ), natDegree (Polynomial.map (algebraMap R S) f) ≤ i → x ^ i ∈ Ideal.map (algebraMap R S) 𝓟

Next tactic:
suffices x ^ (f.map (algebraMap R S)).natDegree ∈ 𝓟.map (algebraMap R S) by
    intro i hi
    obtain ⟨k, hk⟩ := exists_add_of_le hi
    rw [hk, pow_add]
    refine' mul_mem_right _ _ this
theorem pow_natDegree_le_of_aeval_zero_of_monic_mem_map {x : S} (hx : aeval x f = 0)
    (hmo : f.Monic) :
    ∀ i, (f.map (algebraMap R S)).natDegree ≤ i → x ^ i ∈ 𝓟.map (algebraMap R S) := by
  suffices x ^ (f.map (algebraMap R S)).natDegree ∈ 𝓟.map (algebraMap R S) by
    

Proof state:
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : S
hx : (aeval x) f = 0
hmo : Monic f
this : x ^ natDegree (Polynomial.map (algebraMap R S) f) ∈ Ideal.map (algebraMap R S) 𝓟
⊢ ∀ (i : ℕ), natDegree (Polynomial.map (algebraMap R S) f) ≤ i → x ^ i ∈ Ideal.map (algebraMap R S) 𝓟

Next tactic:
intro i hi
theorem pow_natDegree_le_of_aeval_zero_of_monic_mem_map {x : S} (hx : aeval x f = 0)
    (hmo : f.Monic) :
    ∀ i, (f.map (algebraMap R S)).natDegree ≤ i → x ^ i ∈ 𝓟.map (algebraMap R S) := by
  suffices x ^ (f.map (algebraMap R S)).natDegree ∈ 𝓟.map (algebraMap R S) by
    intro i hi
    

Proof state:
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : S
hx : (aeval x) f = 0
hmo : Monic f
this : x ^ natDegree (Polynomial.map (algebraMap R S) f) ∈ Ideal.map (algebraMap R S) 𝓟
i : ℕ
hi : natDegree (Polynomial.map (algebraMap R S) f) ≤ i
⊢ x ^ i ∈ Ideal.map (algebraMap R S) 𝓟

Next tactic:
obtain ⟨k, hk⟩ := exists_add_of_le hi
theorem pow_natDegree_le_of_aeval_zero_of_monic_mem_map {x : S} (hx : aeval x f = 0)
    (hmo : f.Monic) :
    ∀ i, (f.map (algebraMap R S)).natDegree ≤ i → x ^ i ∈ 𝓟.map (algebraMap R S) := by
  suffices x ^ (f.map (algebraMap R S)).natDegree ∈ 𝓟.map (algebraMap R S) by
    intro i hi
    obtain ⟨k, hk⟩ := exists_add_of_le hi
    

Proof state:
case intro
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : S
hx : (aeval x) f = 0
hmo : Monic f
this : x ^ natDegree (Polynomial.map (algebraMap R S) f) ∈ Ideal.map (algebraMap R S) 𝓟
i : ℕ
hi : natDegree (Polynomial.map (algebraMap R S) f) ≤ i
k : ℕ
hk : i = natDegree (Polynomial.map (algebraMap R S) f) + k
⊢ x ^ i ∈ Ideal.map (algebraMap R S) 𝓟

Next tactic:
rw [hk, pow_add]
theorem pow_natDegree_le_of_aeval_zero_of_monic_mem_map {x : S} (hx : aeval x f = 0)
    (hmo : f.Monic) :
    ∀ i, (f.map (algebraMap R S)).natDegree ≤ i → x ^ i ∈ 𝓟.map (algebraMap R S) := by
  suffices x ^ (f.map (algebraMap R S)).natDegree ∈ 𝓟.map (algebraMap R S) by
    intro i hi
    obtain ⟨k, hk⟩ := exists_add_of_le hi
    rw [hk, pow_add]
    

Proof state:
case intro
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : S
hx : (aeval x) f = 0
hmo : Monic f
this : x ^ natDegree (Polynomial.map (algebraMap R S) f) ∈ Ideal.map (algebraMap R S) 𝓟
i : ℕ
hi : natDegree (Polynomial.map (algebraMap R S) f) ≤ i
k : ℕ
hk : i = natDegree (Polynomial.map (algebraMap R S) f) + k
⊢ x ^ natDegree (Polynomial.map (algebraMap R S) f) * x ^ k ∈ Ideal.map (algebraMap R S) 𝓟

Next tactic:
refine' mul_mem_right _ _ this
theorem pow_natDegree_le_of_aeval_zero_of_monic_mem_map {x : S} (hx : aeval x f = 0)
    (hmo : f.Monic) :
    ∀ i, (f.map (algebraMap R S)).natDegree ≤ i → x ^ i ∈ 𝓟.map (algebraMap R S) := by
  suffices x ^ (f.map (algebraMap R S)).natDegree ∈ 𝓟.map (algebraMap R S) by
    intro i hi
    obtain ⟨k, hk⟩ := exists_add_of_le hi
    rw [hk, pow_add]
    refine' mul_mem_right _ _ this
  

Proof state:
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : S
hx : (aeval x) f = 0
hmo : Monic f
⊢ x ^ natDegree (Polynomial.map (algebraMap R S) f) ∈ Ideal.map (algebraMap R S) 𝓟

Next tactic:
rw [aeval_def, eval₂_eq_eval_map, ← IsRoot.def] at hx
theorem pow_natDegree_le_of_aeval_zero_of_monic_mem_map {x : S} (hx : aeval x f = 0)
    (hmo : f.Monic) :
    ∀ i, (f.map (algebraMap R S)).natDegree ≤ i → x ^ i ∈ 𝓟.map (algebraMap R S) := by
  suffices x ^ (f.map (algebraMap R S)).natDegree ∈ 𝓟.map (algebraMap R S) by
    intro i hi
    obtain ⟨k, hk⟩ := exists_add_of_le hi
    rw [hk, pow_add]
    refine' mul_mem_right _ _ this
  rw [aeval_def, eval₂_eq_eval_map, ← IsRoot.def] at hx
  

Proof state:
R : Type u
inst✝² : CommRing R
𝓟 : Ideal R
f : R[X]
hf : IsWeaklyEisensteinAt f 𝓟
S : Type v
inst✝¹ : CommRing S
inst✝ : Algebra R S
x : S
hx : IsRoot (Polynomial.map (algebraMap R S) f) x
hmo : Monic f
⊢ x ^ natDegree (Polynomial.map (algebraMap R S) f) ∈ Ideal.map (algebraMap R S) 𝓟

Next tactic:
refine' pow_natDegree_le_of_root_of_monic_mem (hf.map _) hx (hmo.map _) _ rfl.le
theorem scaleRoots.isWeaklyEisensteinAt (p : R[X]) {x : R} {P : Ideal R} (hP : x ∈ P) :
    (scaleRoots p x).IsWeaklyEisensteinAt P := by
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
p : R[X]
x : R
P : Ideal R
hP : x ∈ P
⊢ IsWeaklyEisensteinAt (scaleRoots p x) P

Next tactic:
refine' ⟨fun i => _⟩
theorem scaleRoots.isWeaklyEisensteinAt (p : R[X]) {x : R} {P : Ideal R} (hP : x ∈ P) :
    (scaleRoots p x).IsWeaklyEisensteinAt P := by
  refine' ⟨fun i => _⟩
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
p : R[X]
x : R
P : Ideal R
hP : x ∈ P
n✝ : ℕ
i : n✝ < natDegree (scaleRoots p x)
⊢ coeff (scaleRoots p x) n✝ ∈ P

Next tactic:
rw [coeff_scaleRoots]
theorem scaleRoots.isWeaklyEisensteinAt (p : R[X]) {x : R} {P : Ideal R} (hP : x ∈ P) :
    (scaleRoots p x).IsWeaklyEisensteinAt P := by
  refine' ⟨fun i => _⟩
  rw [coeff_scaleRoots]
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
p : R[X]
x : R
P : Ideal R
hP : x ∈ P
n✝ : ℕ
i : n✝ < natDegree (scaleRoots p x)
⊢ coeff p n✝ * x ^ (natDegree p - n✝) ∈ P

Next tactic:
rw [natDegree_scaleRoots, ← tsub_pos_iff_lt] at i
theorem scaleRoots.isWeaklyEisensteinAt (p : R[X]) {x : R} {P : Ideal R} (hP : x ∈ P) :
    (scaleRoots p x).IsWeaklyEisensteinAt P := by
  refine' ⟨fun i => _⟩
  rw [coeff_scaleRoots]
  rw [natDegree_scaleRoots, ← tsub_pos_iff_lt] at i
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
p : R[X]
x : R
P : Ideal R
hP : x ∈ P
n✝ : ℕ
i : 0 < natDegree p - n✝
⊢ coeff p n✝ * x ^ (natDegree p - n✝) ∈ P

Next tactic:
exact Ideal.mul_mem_left _ _ (Ideal.pow_mem_of_mem P hP _ i)
theorem dvd_pow_natDegree_of_eval₂_eq_zero {f : R →+* A} (hf : Function.Injective f) {p : R[X]}
    (hp : p.Monic) (x y : R) (z : A) (h : p.eval₂ f z = 0) (hz : f x * z = f y) :
    x ∣ y ^ p.natDegree := by
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
f : R →+* A
hf : Function.Injective ⇑f
p : R[X]
hp : Monic p
x y : R
z : A
h : eval₂ f z p = 0
hz : f x * z = f y
⊢ x ∣ y ^ natDegree p

Next tactic:
rw [← natDegree_scaleRoots p x, ← Ideal.mem_span_singleton]
theorem dvd_pow_natDegree_of_eval₂_eq_zero {f : R →+* A} (hf : Function.Injective f) {p : R[X]}
    (hp : p.Monic) (x y : R) (z : A) (h : p.eval₂ f z = 0) (hz : f x * z = f y) :
    x ∣ y ^ p.natDegree := by
  rw [← natDegree_scaleRoots p x, ← Ideal.mem_span_singleton]
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
f : R →+* A
hf : Function.Injective ⇑f
p : R[X]
hp : Monic p
x y : R
z : A
h : eval₂ f z p = 0
hz : f x * z = f y
⊢ y ^ natDegree (scaleRoots p x) ∈ span {x}

Next tactic:
refine'
    (scaleRoots.isWeaklyEisensteinAt _
          (Ideal.mem_span_singleton.mpr <| dvd_refl x)).pow_natDegree_le_of_root_of_monic_mem
      _ ((monic_scaleRoots_iff x).mpr hp) _ le_rfl
theorem dvd_pow_natDegree_of_eval₂_eq_zero {f : R →+* A} (hf : Function.Injective f) {p : R[X]}
    (hp : p.Monic) (x y : R) (z : A) (h : p.eval₂ f z = 0) (hz : f x * z = f y) :
    x ∣ y ^ p.natDegree := by
  rw [← natDegree_scaleRoots p x, ← Ideal.mem_span_singleton]
  refine'
    (scaleRoots.isWeaklyEisensteinAt _
          (Ideal.mem_span_singleton.mpr <| dvd_refl x)).pow_natDegree_le_of_root_of_monic_mem
      _ ((monic_scaleRoots_iff x).mpr hp) _ le_rfl
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
f : R →+* A
hf : Function.Injective ⇑f
p : R[X]
hp : Monic p
x y : R
z : A
h : eval₂ f z p = 0
hz : f x * z = f y
⊢ IsRoot (scaleRoots p x) y

Next tactic:
rw [injective_iff_map_eq_zero'] at hf
theorem dvd_pow_natDegree_of_eval₂_eq_zero {f : R →+* A} (hf : Function.Injective f) {p : R[X]}
    (hp : p.Monic) (x y : R) (z : A) (h : p.eval₂ f z = 0) (hz : f x * z = f y) :
    x ∣ y ^ p.natDegree := by
  rw [← natDegree_scaleRoots p x, ← Ideal.mem_span_singleton]
  refine'
    (scaleRoots.isWeaklyEisensteinAt _
          (Ideal.mem_span_singleton.mpr <| dvd_refl x)).pow_natDegree_le_of_root_of_monic_mem
      _ ((monic_scaleRoots_iff x).mpr hp) _ le_rfl
  rw [injective_iff_map_eq_zero'] at hf
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
f : R →+* A
hf : ∀ (a : R), f a = 0 ↔ a = 0
p : R[X]
hp : Monic p
x y : R
z : A
h : eval₂ f z p = 0
hz : f x * z = f y
⊢ IsRoot (scaleRoots p x) y

Next tactic:
have : eval₂ _ _ (p.scaleRoots x) = 0 := scaleRoots_eval₂_eq_zero f h
theorem dvd_pow_natDegree_of_eval₂_eq_zero {f : R →+* A} (hf : Function.Injective f) {p : R[X]}
    (hp : p.Monic) (x y : R) (z : A) (h : p.eval₂ f z = 0) (hz : f x * z = f y) :
    x ∣ y ^ p.natDegree := by
  rw [← natDegree_scaleRoots p x, ← Ideal.mem_span_singleton]
  refine'
    (scaleRoots.isWeaklyEisensteinAt _
          (Ideal.mem_span_singleton.mpr <| dvd_refl x)).pow_natDegree_le_of_root_of_monic_mem
      _ ((monic_scaleRoots_iff x).mpr hp) _ le_rfl
  rw [injective_iff_map_eq_zero'] at hf
  have : eval₂ _ _ (p.scaleRoots x) = 0 := scaleRoots_eval₂_eq_zero f h
  

Proof state:
R : Type u
A : Type u_1
inst✝¹ : CommRing R
inst✝ : CommRing A
f : R →+* A
hf : ∀ (a : R), f a = 0 ↔ a = 0
p : R[X]
hp : Monic p
x y : R
z : A
h : eval₂ f z p = 0
hz : f x * z = f y
this : eval₂ f (f x * z) (scaleRoots p x) = 0
⊢ IsRoot (scaleRoots p x) y

Next tactic:
rwa [hz, Polynomial.eval₂_at_apply, hf] at this
theorem coeff_mem {n : ℕ} (hn : n ≠ f.natDegree) : f.coeff n ∈ 𝓟 := by
  

Proof state:
R : Type u
inst✝ : CommSemiring R
𝓟 : Ideal R
f : R[X]
hf : IsEisensteinAt f 𝓟
n : ℕ
hn : n ≠ natDegree f
⊢ coeff f n ∈ 𝓟

Next tactic:
cases' ne_iff_lt_or_gt.1 hn with h₁ h₂
theorem coeff_mem {n : ℕ} (hn : n ≠ f.natDegree) : f.coeff n ∈ 𝓟 := by
  cases' ne_iff_lt_or_gt.1 hn with h₁ h₂
  · 

Proof state:
case inl
R : Type u
inst✝ : CommSemiring R
𝓟 : Ideal R
f : R[X]
hf : IsEisensteinAt f 𝓟
n : ℕ
hn : n ≠ natDegree f
h₁ : n < natDegree f
⊢ coeff f n ∈ 𝓟

Next tactic:
exact hf.mem h₁
theorem coeff_mem {n : ℕ} (hn : n ≠ f.natDegree) : f.coeff n ∈ 𝓟 := by
  cases' ne_iff_lt_or_gt.1 hn with h₁ h₂
  · exact hf.mem h₁
  · 

Proof state:
case inr
R : Type u
inst✝ : CommSemiring R
𝓟 : Ideal R
f : R[X]
hf : IsEisensteinAt f 𝓟
n : ℕ
hn : n ≠ natDegree f
h₂ : n > natDegree f
⊢ coeff f n ∈ 𝓟

Next tactic:
rw [coeff_eq_zero_of_natDegree_lt h₂]
theorem coeff_mem {n : ℕ} (hn : n ≠ f.natDegree) : f.coeff n ∈ 𝓟 := by
  cases' ne_iff_lt_or_gt.1 hn with h₁ h₂
  · exact hf.mem h₁
  · rw [coeff_eq_zero_of_natDegree_lt h₂]
    

Proof state:
case inr
R : Type u
inst✝ : CommSemiring R
𝓟 : Ideal R
f : R[X]
hf : IsEisensteinAt f 𝓟
n : ℕ
hn : n ≠ natDegree f
h₂ : n > natDegree f
⊢ 0 ∈ 𝓟

Next tactic:
exact Ideal.zero_mem _
@[simp]
theorem Discrete.mk_as {α : Type u₁} (X : Discrete α) : Discrete.mk X.as = X := by
  

Proof state:
α : Type u₁
X : Discrete α
⊢ { as := X.as } = X

Next tactic:
ext
@[simp]
theorem Discrete.mk_as {α : Type u₁} (X : Discrete α) : Discrete.mk X.as = X := by
  ext
  

Proof state:
case as
α : Type u₁
X : Discrete α
⊢ { as := X.as }.as = X.as

Next tactic:
rfl
/-- `Discrete α` is equivalent to the original type `α`.-/
@[simps]
def discreteEquiv {α : Type u₁} : Discrete α ≃ α where
  toFun := Discrete.as
  invFun := Discrete.mk
  left_inv := by 

Proof state:
α : Type u₁
⊢ Function.LeftInverse Discrete.mk Discrete.as

Next tactic:
aesop_cat
/-- `Discrete α` is equivalent to the original type `α`.-/
@[simps]
def discreteEquiv {α : Type u₁} : Discrete α ≃ α where
  toFun := Discrete.as
  invFun := Discrete.mk
  left_inv := by aesop_cat
  right_inv := by 

Proof state:
α : Type u₁
⊢ Function.RightInverse Discrete.mk Discrete.as

Next tactic:
aesop_cat
/-- The "Discrete" category on a type, whose morphisms are equalities.

Because we do not allow morphisms in `Prop` (only in `Type`),
somewhat annoyingly we have to define `X ⟶ Y` as `ULift (PLift (X = Y))`.

See <https://stacks.math.columbia.edu/tag/001A>
-/
instance discreteCategory (α : Type u₁) : SmallCategory (Discrete α) where
  Hom X Y := ULift (PLift (X.as = Y.as))
  id X := ULift.up (PLift.up rfl)
  comp {X Y Z} g f := by
    

Proof state:
α : Type u₁
X Y Z : Discrete α
g : X ⟶ Y
f : Y ⟶ Z
⊢ X ⟶ Z

Next tactic:
cases X
/-- The "Discrete" category on a type, whose morphisms are equalities.

Because we do not allow morphisms in `Prop` (only in `Type`),
somewhat annoyingly we have to define `X ⟶ Y` as `ULift (PLift (X = Y))`.

See <https://stacks.math.columbia.edu/tag/001A>
-/
instance discreteCategory (α : Type u₁) : SmallCategory (Discrete α) where
  Hom X Y := ULift (PLift (X.as = Y.as))
  id X := ULift.up (PLift.up rfl)
  comp {X Y Z} g f := by
    cases X
    

Proof state:
case mk
α : Type u₁
Y Z : Discrete α
f : Y ⟶ Z
as✝ : α
g : { as := as✝ } ⟶ Y
⊢ { as := as✝ } ⟶ Z

Next tactic:
cases Y
/-- The "Discrete" category on a type, whose morphisms are equalities.

Because we do not allow morphisms in `Prop` (only in `Type`),
somewhat annoyingly we have to define `X ⟶ Y` as `ULift (PLift (X = Y))`.

See <https://stacks.math.columbia.edu/tag/001A>
-/
instance discreteCategory (α : Type u₁) : SmallCategory (Discrete α) where
  Hom X Y := ULift (PLift (X.as = Y.as))
  id X := ULift.up (PLift.up rfl)
  comp {X Y Z} g f := by
    cases X
    cases Y
    

Proof state:
case mk.mk
α : Type u₁
Z : Discrete α
as✝¹ as✝ : α
f : { as := as✝ } ⟶ Z
g : { as := as✝¹ } ⟶ { as := as✝ }
⊢ { as := as✝¹ } ⟶ Z

Next tactic:
cases Z
/-- The "Discrete" category on a type, whose morphisms are equalities.

Because we do not allow morphisms in `Prop` (only in `Type`),
somewhat annoyingly we have to define `X ⟶ Y` as `ULift (PLift (X = Y))`.

See <https://stacks.math.columbia.edu/tag/001A>
-/
instance discreteCategory (α : Type u₁) : SmallCategory (Discrete α) where
  Hom X Y := ULift (PLift (X.as = Y.as))
  id X := ULift.up (PLift.up rfl)
  comp {X Y Z} g f := by
    cases X
    cases Y
    cases Z
    

Proof state:
case mk.mk.mk
α : Type u₁
as✝² as✝¹ : α
g : { as := as✝² } ⟶ { as := as✝¹ }
as✝ : α
f : { as := as✝¹ } ⟶ { as := as✝ }
⊢ { as := as✝² } ⟶ { as := as✝ }

Next tactic:
rcases f with ⟨⟨⟨⟩⟩⟩
/-- The "Discrete" category on a type, whose morphisms are equalities.

Because we do not allow morphisms in `Prop` (only in `Type`),
somewhat annoyingly we have to define `X ⟶ Y` as `ULift (PLift (X = Y))`.

See <https://stacks.math.columbia.edu/tag/001A>
-/
instance discreteCategory (α : Type u₁) : SmallCategory (Discrete α) where
  Hom X Y := ULift (PLift (X.as = Y.as))
  id X := ULift.up (PLift.up rfl)
  comp {X Y Z} g f := by
    cases X
    cases Y
    cases Z
    rcases f with ⟨⟨⟨⟩⟩⟩
    

Proof state:
case mk.mk.mk.up.up.refl
α : Type u₁
as✝¹ as✝ : α
g : { as := as✝¹ } ⟶ { as := as✝ }
⊢ { as := as✝¹ } ⟶ { as := as✝ }

Next tactic:
exact g
instance [Subsingleton α] : Subsingleton (Discrete α) :=
  ⟨by 

Proof state:
α : Type u₁
inst✝ : Subsingleton α
⊢ ∀ (a b : Discrete α), a = b

Next tactic:
aesop_cat
/-- Promote an equation between the wrapped terms in `X Y : Discrete α` to a morphism `X ⟶ Y`
in the discrete category. -/
protected abbrev eqToHom {X Y : Discrete α} (h : X.as = Y.as) : X ⟶ Y :=
  eqToHom (by 

Proof state:
α : Type u₁
X Y : Discrete α
h : X.as = Y.as
⊢ X = Y

Next tactic:
aesop_cat
/-- Promote an equation between the wrapped terms in `X Y : Discrete α` to an isomorphism `X ≅ Y`
in the discrete category. -/
protected abbrev eqToIso {X Y : Discrete α} (h : X.as = Y.as) : X ≅ Y :=
  eqToIso (by 

Proof state:
α : Type u₁
X Y : Discrete α
h : X.as = Y.as
⊢ X = Y

Next tactic:
aesop_cat
instance {I : Type u₁} {i j : Discrete I} (f : i ⟶ j) : IsIso f :=
  ⟨⟨Discrete.eqToHom (eq_of_hom f).symm, by 

Proof state:
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
i j : Discrete I
f : i ⟶ j
⊢ f ≫ Discrete.eqToHom (_ : j.as = i.as) = 𝟙 i ∧ Discrete.eqToHom (_ : j.as = i.as) ≫ f = 𝟙 j

Next tactic:
aesop_cat
/-- Any function `I → C` gives a functor `Discrete I ⥤ C`.-/
def functor {I : Type u₁} (F : I → C) : Discrete I ⥤ C where
  obj := F ∘ Discrete.as
  map {X Y} f := by
    

Proof state:
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F : I → C
X Y : Discrete I
f : X ⟶ Y
⊢ (F ∘ as) X ⟶ (F ∘ as) Y

Next tactic:
dsimp
/-- Any function `I → C` gives a functor `Discrete I ⥤ C`.-/
def functor {I : Type u₁} (F : I → C) : Discrete I ⥤ C where
  obj := F ∘ Discrete.as
  map {X Y} f := by
    dsimp
    

Proof state:
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F : I → C
X Y : Discrete I
f : X ⟶ Y
⊢ F X.as ⟶ F Y.as

Next tactic:
rcases f with ⟨⟨h⟩⟩
/-- Any function `I → C` gives a functor `Discrete I ⥤ C`.-/
def functor {I : Type u₁} (F : I → C) : Discrete I ⥤ C where
  obj := F ∘ Discrete.as
  map {X Y} f := by
    dsimp
    rcases f with ⟨⟨h⟩⟩
    

Proof state:
case up.up
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F : I → C
X Y : Discrete I
h : X.as = Y.as
⊢ F X.as ⟶ F Y.as

Next tactic:
exact eqToHom (congrArg _ h)
theorem functor_map {I : Type u₁} (F : I → C) {i : Discrete I} (f : i ⟶ i) :
    (Discrete.functor F).map f = 𝟙 (F i.as) := by 

Proof state:
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F : I → C
i : Discrete I
f : i ⟶ i
⊢ (functor F).map f = 𝟙 (F i.as)

Next tactic:
aesop_cat
/-- For functors out of a discrete category,
a natural transformation is just a collection of maps,
as the naturality squares are trivial.
-/
@[simps]
def natTrans {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ⟶ G.obj i) :
    F ⟶ G where
  app := f
  naturality := fun {X Y} ⟨⟨g⟩⟩ => by
    

Proof state:
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F G : Discrete I ⥤ C
f : (i : Discrete I) → F.obj i ⟶ G.obj i
X Y : Discrete I
x✝ : X ⟶ Y
g : X.as = Y.as
⊢ F.map { down := { down := g } } ≫ f Y = f X ≫ G.map { down := { down := g } }

Next tactic:
discrete_cases
/-- For functors out of a discrete category,
a natural transformation is just a collection of maps,
as the naturality squares are trivial.
-/
@[simps]
def natTrans {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ⟶ G.obj i) :
    F ⟶ G where
  app := f
  naturality := fun {X Y} ⟨⟨g⟩⟩ => by
    discrete_cases
    

Proof state:
case mk.mk.up.up
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F G : Discrete I ⥤ C
f : (i : Discrete I) → F.obj i ⟶ G.obj i
as✝¹ as✝ : I
g down✝ : { as := as✝¹ }.as = { as := as✝ }.as
⊢ F.map { down := { down := g } } ≫ f { as := as✝ } = f { as := as✝¹ } ≫ G.map { down := { down := g } }

Next tactic:
rcases g
/-- For functors out of a discrete category,
a natural transformation is just a collection of maps,
as the naturality squares are trivial.
-/
@[simps]
def natTrans {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ⟶ G.obj i) :
    F ⟶ G where
  app := f
  naturality := fun {X Y} ⟨⟨g⟩⟩ => by
    discrete_cases
    rcases g
    

Proof state:
case mk.mk.up.up.refl
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F G : Discrete I ⥤ C
f : (i : Discrete I) → F.obj i ⟶ G.obj i
as✝ : I
down✝ : { as := as✝ }.as = { as := as✝ }.as
⊢ F.map { down := { down := (_ : { as := as✝ }.as = { as := as✝ }.as) } } ≫ f { as := as✝ } =
    f { as := as✝ } ≫ G.map { down := { down := (_ : { as := as✝ }.as = { as := as✝ }.as) } }

Next tactic:
change F.map (𝟙 _) ≫ _ = _ ≫ G.map (𝟙 _)
/-- For functors out of a discrete category,
a natural transformation is just a collection of maps,
as the naturality squares are trivial.
-/
@[simps]
def natTrans {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ⟶ G.obj i) :
    F ⟶ G where
  app := f
  naturality := fun {X Y} ⟨⟨g⟩⟩ => by
    discrete_cases
    rcases g
    change F.map (𝟙 _) ≫ _ = _ ≫ G.map (𝟙 _)
    

Proof state:
case mk.mk.up.up.refl
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F G : Discrete I ⥤ C
f : (i : Discrete I) → F.obj i ⟶ G.obj i
as✝ : I
down✝ : { as := as✝ }.as = { as := as✝ }.as
⊢ F.map (𝟙 { as := as✝ }) ≫ f { as := as✝ } = f { as := as✝ } ≫ G.map (𝟙 { as := as✝ })

Next tactic:
simp
/-- For functors out of a discrete category,
a natural isomorphism is just a collection of isomorphisms,
as the naturality squares are trivial.
-/
@[simps!]
def natIso {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ≅ G.obj i) :
    F ≅ G :=
  NatIso.ofComponents f fun ⟨⟨g⟩⟩ => by
    

Proof state:
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F G : Discrete I ⥤ C
f : (i : Discrete I) → F.obj i ≅ G.obj i
X✝ Y✝ : Discrete I
x✝ : X✝ ⟶ Y✝
g : X✝.as = Y✝.as
⊢ F.map { down := { down := g } } ≫ (f Y✝).hom = (f X✝).hom ≫ G.map { down := { down := g } }

Next tactic:
discrete_cases
/-- For functors out of a discrete category,
a natural isomorphism is just a collection of isomorphisms,
as the naturality squares are trivial.
-/
@[simps!]
def natIso {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ≅ G.obj i) :
    F ≅ G :=
  NatIso.ofComponents f fun ⟨⟨g⟩⟩ => by
    discrete_cases
    

Proof state:
case mk.mk.up.up
α : Type u₁
C : Type u₂
inst✝ : Category.{v₂, u₂} C
I : Type u₁
F G : Discrete I ⥤ C
f : (i : Discrete I) → F.obj i ≅ G.obj i
as✝¹ as✝ : I
g down✝ : { as := as✝¹ }.as = { as := as✝ }.as
⊢ F.map { down := { down := g } } ≫ (f { as := as✝ }).hom = (f { as := as✝¹ }).hom ≫ G.map { down := { down := g } }

Next tactic:
rcases g