l3lab/ntp-mathlib huggingface dataset:
theorem exists_multiset_roots [DecidableEq R] :
    ∀ {p : R[X]} (_ : p ≠ 0), ∃ s : Multiset R,
      (Multiset.card s : WithBot ℕ) ≤ degree p ∧ ∀ a, s.count a = rootMultiplicity a p
  | p, hp =>
    haveI := Classical.propDecidable (∃ x, IsRoot p x)
    if h : ∃ x, IsRoot p x then
      let ⟨x, hx⟩ := h
      have hpd : 0 < degree p := degree_pos_of_root hp hx
      have hd0 : p /ₘ (X - C x) ≠ 0 := fun h => by
        rw [← mul_divByMonic_eq_iff_isRoot.2 hx, h, mul_zero] at hp; exact hp rfl
      have wf : degree (p /ₘ (X - C x)) < degree p :=
        degree_divByMonic_lt _ (monic_X_sub_C x) hp ((degree_X_sub_C x).symm ▸ by decide)
      let ⟨t, htd, htr⟩ := @exists_multiset_roots _ (p /ₘ (X - C x)) hd0
      have hdeg : degree (X - C x) ≤ degree p := by
        rw [degree_X_sub_C, degree_eq_natDegree hp]
        rw [degree_eq_natDegree hp] at hpd
        exact WithBot.coe_le_coe.2 (WithBot.coe_lt_coe.1 hpd)
      have hdiv0 : p /ₘ (X - C x) ≠ 0 :=
        mt (divByMonic_eq_zero_iff (monic_X_sub_C x)).1 <| not_lt.2 hdeg
      ⟨x ::ₘ t,
        calc
          (card (x ::ₘ t) : WithBot ℕ) = Multiset.card t + 1 := by
            congr
            exact mod_cast Multiset.card_cons _ _
          _ ≤ degree p := by
            rw [← degree_add_divByMonic (monic_X_sub_C x) hdeg, degree_X_sub_C, add_comm];
              exact add_le_add (le_refl (1 : WithBot ℕ)) htd,
        by
          change ∀ (a : R), count a (x ::ₘ t) = rootMultiplicity a p
          intro a
          conv_rhs => rw [← mul_divByMonic_eq_iff_isRoot.mpr hx]
          rw [rootMultiplicity_mul (mul_ne_zero (X_sub_C_ne_zero x) hdiv0),
            rootMultiplicity_X_sub_C, ← htr a]
          split_ifs with ha
          · rw [ha, count_cons_self, add_comm]
          · rw [count_cons_of_ne ha, zero_add]⟩
    else
      ⟨0, (degree_eq_natDegree hp).symm ▸ WithBot.coe_le_coe.2 (Nat.zero_le _), by
        intro a
        rw [count_zero, rootMultiplicity_eq_zero (not_exists.mp h a)]⟩
termination_by _ p _ => natDegree p
decreasing_by {
  simp_wf
  

Proof state:
R : Type u
inst✝² : CommRing R
inst✝¹ : IsDomain R
inst✝ : DecidableEq R
_x✝ : (x : R[X]) ×' x ≠ 0
a✝¹ :
  ∀ (y : (x : R[X]) ×' x ≠ 0),
    (invImage (fun a => PSigma.casesOn a fun p snd => natDegree p) instWellFoundedRelation).1 y _x✝ →
      ∃ s, ↑(Multiset.card s) ≤ degree y.1 ∧ ∀ (a : R), count a s = rootMultiplicity a y.1
x✝² : R[X]
x✝¹ : x✝² ≠ 0
a✝ :
  ∀ (y : (x : R[X]) ×' x ≠ 0),
    (invImage (fun a => PSigma.casesOn a fun p snd => natDegree p) instWellFoundedRelation).1 y
        { fst := x✝², snd := x✝¹ } →
      ∃ s, ↑(Multiset.card s) ≤ degree y.1 ∧ ∀ (a : R), count a s = rootMultiplicity a y.1
p : R[X]
hp : p ≠ 0
x✝ :
  ∀ (y : (x : R[X]) ×' x ≠ 0),
    (invImage (fun a => PSigma.casesOn a fun p snd => natDegree p) instWellFoundedRelation).1 y
        { fst := p, snd := hp } →
      ∃ s, ↑(Multiset.card s) ≤ degree y.1 ∧ ∀ (a : R), count a s = rootMultiplicity a y.1
h : ∃ x, IsRoot p x
x : R
hx : IsRoot p x
hpd : 0 < degree p
hd0 : p /ₘ (X - C x) ≠ 0
wf : degree (p /ₘ (X - C x)) < degree p
⊢ natDegree (p /ₘ (X - C x)) < natDegree p

Next tactic:
apply (Nat.cast_lt (α := WithBot ℕ)).mp
theorem roots_def [DecidableEq R] (p : R[X]) [Decidable (p = 0)] :
    p.roots = if h : p = 0 then ∅ else Classical.choose (exists_multiset_roots h) := by
  -- porting noteL `‹_›` doesn't work for instance arguments
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝³ : CommRing R
inst✝² : IsDomain R
p✝ q : R[X]
inst✝¹ : DecidableEq R
p : R[X]
inst✝ : Decidable (p = 0)
⊢ roots p =
    if h : p = 0 then ∅
    else Classical.choose (_ : ∃ s, ↑(Multiset.card s) ≤ degree p ∧ ∀ (a : R), count a s = rootMultiplicity a p)

Next tactic:
rename_i iR ip0
theorem roots_def [DecidableEq R] (p : R[X]) [Decidable (p = 0)] :
    p.roots = if h : p = 0 then ∅ else Classical.choose (exists_multiset_roots h) := by
  -- porting noteL `‹_›` doesn't work for instance arguments
  rename_i iR ip0
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q : R[X]
iR : DecidableEq R
p : R[X]
ip0 : Decidable (p = 0)
⊢ roots p =
    if h : p = 0 then ∅
    else Classical.choose (_ : ∃ s, ↑(Multiset.card s) ≤ degree p ∧ ∀ (a : R), count a s = rootMultiplicity a p)

Next tactic:
obtain rfl := Subsingleton.elim iR (Classical.decEq R)
theorem roots_def [DecidableEq R] (p : R[X]) [Decidable (p = 0)] :
    p.roots = if h : p = 0 then ∅ else Classical.choose (exists_multiset_roots h) := by
  -- porting noteL `‹_›` doesn't work for instance arguments
  rename_i iR ip0
  obtain rfl := Subsingleton.elim iR (Classical.decEq R)
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
ip0 : Decidable (p = 0)
⊢ roots p =
    if h : p = 0 then ∅
    else Classical.choose (_ : ∃ s, ↑(Multiset.card s) ≤ degree p ∧ ∀ (a : R), count a s = rootMultiplicity a p)

Next tactic:
obtain rfl := Subsingleton.elim ip0 (Classical.dec (p = 0))
theorem roots_def [DecidableEq R] (p : R[X]) [Decidable (p = 0)] :
    p.roots = if h : p = 0 then ∅ else Classical.choose (exists_multiset_roots h) := by
  -- porting noteL `‹_›` doesn't work for instance arguments
  rename_i iR ip0
  obtain rfl := Subsingleton.elim iR (Classical.decEq R)
  obtain rfl := Subsingleton.elim ip0 (Classical.dec (p = 0))
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
⊢ roots p =
    if h : p = 0 then ∅
    else Classical.choose (_ : ∃ s, ↑(Multiset.card s) ≤ degree p ∧ ∀ (a : R), count a s = rootMultiplicity a p)

Next tactic:
rfl
theorem card_roots (hp0 : p ≠ 0) : (Multiset.card (roots p) : WithBot ℕ) ≤ degree p := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
hp0 : p ≠ 0
⊢ ↑(Multiset.card (roots p)) ≤ degree p

Next tactic:
classical
  unfold roots
  rw [dif_neg hp0]
  exact (Classical.choose_spec (exists_multiset_roots hp0)).1
theorem card_roots (hp0 : p ≠ 0) : (Multiset.card (roots p) : WithBot ℕ) ≤ degree p := by
  classical
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
hp0 : p ≠ 0
⊢ ↑(Multiset.card (roots p)) ≤ degree p

Next tactic:
unfold roots
theorem card_roots (hp0 : p ≠ 0) : (Multiset.card (roots p) : WithBot ℕ) ≤ degree p := by
  classical
  unfold roots
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
hp0 : p ≠ 0
⊢ ↑(Multiset.card
        (if h : p = 0 then ∅
        else Classical.choose (_ : ∃ s, ↑(Multiset.card s) ≤ degree p ∧ ∀ (a : R), count a s = rootMultiplicity a p))) ≤
    degree p

Next tactic:
rw [dif_neg hp0]
theorem card_roots (hp0 : p ≠ 0) : (Multiset.card (roots p) : WithBot ℕ) ≤ degree p := by
  classical
  unfold roots
  rw [dif_neg hp0]
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
hp0 : p ≠ 0
⊢ ↑(Multiset.card
        (Classical.choose (_ : ∃ s, ↑(Multiset.card s) ≤ degree p ∧ ∀ (a : R), count a s = rootMultiplicity a p))) ≤
    degree p

Next tactic:
exact (Classical.choose_spec (exists_multiset_roots hp0)).1
theorem card_roots' (p : R[X]) : Multiset.card p.roots ≤ natDegree p := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
⊢ Multiset.card (roots p) ≤ natDegree p

Next tactic:
by_cases hp0 : p = 0
theorem card_roots' (p : R[X]) : Multiset.card p.roots ≤ natDegree p := by
  by_cases hp0 : p = 0
  · 

Proof state:
case pos
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
hp0 : p = 0
⊢ Multiset.card (roots p) ≤ natDegree p

Next tactic:
simp [hp0]
theorem card_roots' (p : R[X]) : Multiset.card p.roots ≤ natDegree p := by
  by_cases hp0 : p = 0
  · simp [hp0]
  

Proof state:
case neg
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
hp0 : ¬p = 0
⊢ Multiset.card (roots p) ≤ natDegree p

Next tactic:
exact WithBot.coe_le_coe.1 (le_trans (card_roots hp0) (le_of_eq <| degree_eq_natDegree hp0))
theorem card_roots_sub_C {p : R[X]} {a : R} (hp0 : 0 < degree p) :
    (Multiset.card (p - C a).roots : WithBot ℕ) ≤ degree p :=
  calc
    (Multiset.card (p - C a).roots : WithBot ℕ) ≤ degree (p - C a) :=
      card_roots <| mt sub_eq_zero.1 fun h => not_le_of_gt hp0 <| h.symm ▸ degree_C_le
    _ = degree p := by 

Proof state:
R : Type u
S : Type v
T : Type w
a✝ b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
a : R
hp0 : 0 < degree p
⊢ degree (p - C a) = degree p

Next tactic:
rw [sub_eq_add_neg, ← C_neg]
theorem card_roots_sub_C {p : R[X]} {a : R} (hp0 : 0 < degree p) :
    (Multiset.card (p - C a).roots : WithBot ℕ) ≤ degree p :=
  calc
    (Multiset.card (p - C a).roots : WithBot ℕ) ≤ degree (p - C a) :=
      card_roots <| mt sub_eq_zero.1 fun h => not_le_of_gt hp0 <| h.symm ▸ degree_C_le
    _ = degree p := by rw [sub_eq_add_neg, ← C_neg]; 

Proof state:
R : Type u
S : Type v
T : Type w
a✝ b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
a : R
hp0 : 0 < degree p
⊢ degree (p + C (-a)) = degree p

Next tactic:
exact degree_add_C hp0
theorem card_roots_sub_C' {p : R[X]} {a : R} (hp0 : 0 < degree p) :
    Multiset.card (p - C a).roots ≤ natDegree p :=
  WithBot.coe_le_coe.1
    (le_trans (card_roots_sub_C hp0)
      (le_of_eq <| degree_eq_natDegree fun h => by 

Proof state:
R : Type u
S : Type v
T : Type w
a✝ b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
a : R
hp0 : 0 < degree p
h : p = 0
⊢ False

Next tactic:
simp_all [lt_irrefl]
@[simp]
theorem count_roots [DecidableEq R] (p : R[X]) : p.roots.count a = rootMultiplicity a p := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝² : CommRing R
inst✝¹ : IsDomain R
p✝ q : R[X]
inst✝ : DecidableEq R
p : R[X]
⊢ count a (roots p) = rootMultiplicity a p

Next tactic:
classical
  by_cases hp : p = 0
  · simp [hp]
  rw [roots_def, dif_neg hp]
  exact (Classical.choose_spec (exists_multiset_roots hp)).2 a
@[simp]
theorem count_roots [DecidableEq R] (p : R[X]) : p.roots.count a = rootMultiplicity a p := by
  classical
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝² : CommRing R
inst✝¹ : IsDomain R
p✝ q : R[X]
inst✝ : DecidableEq R
p : R[X]
⊢ count a (roots p) = rootMultiplicity a p

Next tactic:
by_cases hp : p = 0
@[simp]
theorem count_roots [DecidableEq R] (p : R[X]) : p.roots.count a = rootMultiplicity a p := by
  classical
  by_cases hp : p = 0
  · 

Proof state:
case pos
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝² : CommRing R
inst✝¹ : IsDomain R
p✝ q : R[X]
inst✝ : DecidableEq R
p : R[X]
hp : p = 0
⊢ count a (roots p) = rootMultiplicity a p

Next tactic:
simp [hp]
@[simp]
theorem count_roots [DecidableEq R] (p : R[X]) : p.roots.count a = rootMultiplicity a p := by
  classical
  by_cases hp : p = 0
  · simp [hp]
  

Proof state:
case neg
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝² : CommRing R
inst✝¹ : IsDomain R
p✝ q : R[X]
inst✝ : DecidableEq R
p : R[X]
hp : ¬p = 0
⊢ count a (roots p) = rootMultiplicity a p

Next tactic:
rw [roots_def, dif_neg hp]
@[simp]
theorem count_roots [DecidableEq R] (p : R[X]) : p.roots.count a = rootMultiplicity a p := by
  classical
  by_cases hp : p = 0
  · simp [hp]
  rw [roots_def, dif_neg hp]
  

Proof state:
case neg
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝² : CommRing R
inst✝¹ : IsDomain R
p✝ q : R[X]
inst✝ : DecidableEq R
p : R[X]
hp : ¬p = 0
⊢ count a (Classical.choose (_ : ∃ s, ↑(Multiset.card s) ≤ degree p ∧ ∀ (a : R), count a s = rootMultiplicity a p)) =
    rootMultiplicity a p

Next tactic:
exact (Classical.choose_spec (exists_multiset_roots hp)).2 a
@[simp]
theorem mem_roots' : a ∈ p.roots ↔ p ≠ 0 ∧ IsRoot p a := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
⊢ a ∈ roots p ↔ p ≠ 0 ∧ IsRoot p a

Next tactic:
classical
  rw [← count_pos, count_roots p, rootMultiplicity_pos']
@[simp]
theorem mem_roots' : a ∈ p.roots ↔ p ≠ 0 ∧ IsRoot p a := by
  classical
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
⊢ a ∈ roots p ↔ p ≠ 0 ∧ IsRoot p a

Next tactic:
rw [← count_pos, count_roots p, rootMultiplicity_pos']
lemma mem_roots_iff_aeval_eq_zero (w : p ≠ 0) : x ∈ roots p ↔ aeval x p = 0 := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
x : R
w : p ≠ 0
⊢ x ∈ roots p ↔ (aeval x) p = 0

Next tactic:
rw [mem_roots w, IsRoot.def, aeval_def, eval₂_eq_eval_map]
lemma mem_roots_iff_aeval_eq_zero (w : p ≠ 0) : x ∈ roots p ↔ aeval x p = 0 := by
  rw [mem_roots w, IsRoot.def, aeval_def, eval₂_eq_eval_map]
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
x : R
w : p ≠ 0
⊢ eval x p = 0 ↔ eval x (map (algebraMap R R) p) = 0

Next tactic:
simp
theorem finite_setOf_isRoot {p : R[X]} (hp : p ≠ 0) : Set.Finite { x | IsRoot p x } := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
hp : p ≠ 0
⊢ Set.Finite {x | IsRoot p x}

Next tactic:
classical
  simpa only [← Finset.setOf_mem, Multiset.mem_toFinset, mem_roots hp]
    using p.roots.toFinset.finite_toSet
theorem finite_setOf_isRoot {p : R[X]} (hp : p ≠ 0) : Set.Finite { x | IsRoot p x } := by
  classical
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
hp : p ≠ 0
⊢ Set.Finite {x | IsRoot p x}

Next tactic:
simpa only [← Finset.setOf_mem, Multiset.mem_toFinset, mem_roots hp]
    using p.roots.toFinset.finite_toSet
theorem eq_of_infinite_eval_eq (p q : R[X]) (h : Set.Infinite { x | eval x p = eval x q }) :
    p = q := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q✝ p q : R[X]
h : Set.Infinite {x | eval x p = eval x q}
⊢ p = q

Next tactic:
rw [← sub_eq_zero]
theorem eq_of_infinite_eval_eq (p q : R[X]) (h : Set.Infinite { x | eval x p = eval x q }) :
    p = q := by
  rw [← sub_eq_zero]
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q✝ p q : R[X]
h : Set.Infinite {x | eval x p = eval x q}
⊢ p - q = 0

Next tactic:
apply eq_zero_of_infinite_isRoot
theorem eq_of_infinite_eval_eq (p q : R[X]) (h : Set.Infinite { x | eval x p = eval x q }) :
    p = q := by
  rw [← sub_eq_zero]
  apply eq_zero_of_infinite_isRoot
  

Proof state:
case h
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q✝ p q : R[X]
h : Set.Infinite {x | eval x p = eval x q}
⊢ Set.Infinite {x | IsRoot (p - q) x}

Next tactic:
simpa only [IsRoot, eval_sub, sub_eq_zero]
theorem roots_mul {p q : R[X]} (hpq : p * q ≠ 0) : (p * q).roots = p.roots + q.roots := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q✝ p q : R[X]
hpq : p * q ≠ 0
⊢ roots (p * q) = roots p + roots q

Next tactic:
classical
  exact Multiset.ext.mpr fun r => by
    rw [count_add, count_roots, count_roots, count_roots, rootMultiplicity_mul hpq]
theorem roots_mul {p q : R[X]} (hpq : p * q ≠ 0) : (p * q).roots = p.roots + q.roots := by
  classical
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q✝ p q : R[X]
hpq : p * q ≠ 0
⊢ roots (p * q) = roots p + roots q

Next tactic:
exact Multiset.ext.mpr fun r => by
    rw [count_add, count_roots, count_roots, count_roots, rootMultiplicity_mul hpq]
theorem roots_mul {p q : R[X]} (hpq : p * q ≠ 0) : (p * q).roots = p.roots + q.roots := by
  classical
  exact Multiset.ext.mpr fun r => by
    

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q✝ p q : R[X]
hpq : p * q ≠ 0
r : R
⊢ count r (roots (p * q)) = count r (roots p + roots q)

Next tactic:
rw [count_add, count_roots, count_roots, count_roots, rootMultiplicity_mul hpq]
theorem roots.le_of_dvd (h : q ≠ 0) : p ∣ q → roots p ≤ roots q := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
h : q ≠ 0
⊢ p ∣ q → roots p ≤ roots q

Next tactic:
rintro ⟨k, rfl⟩
theorem roots.le_of_dvd (h : q ≠ 0) : p ∣ q → roots p ≤ roots q := by
  rintro ⟨k, rfl⟩
  

Proof state:
case intro
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p k : R[X]
h : p * k ≠ 0
⊢ roots p ≤ roots (p * k)

Next tactic:
exact Multiset.le_iff_exists_add.mpr ⟨k.roots, roots_mul h⟩
theorem mem_roots_sub_C' {p : R[X]} {a x : R} : x ∈ (p - C a).roots ↔ p ≠ C a ∧ p.eval x = a := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a✝ b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
a x : R
⊢ x ∈ roots (p - C a) ↔ p ≠ C a ∧ eval x p = a

Next tactic:
rw [mem_roots', IsRoot.def, sub_ne_zero, eval_sub, sub_eq_zero, eval_C]
@[simp]
theorem roots_X_sub_C (r : R) : roots (X - C r) = {r} := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
r : R
⊢ roots (X - C r) = {r}

Next tactic:
classical
  ext s
  rw [count_roots, rootMultiplicity_X_sub_C, count_singleton]
@[simp]
theorem roots_X_sub_C (r : R) : roots (X - C r) = {r} := by
  classical
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
r : R
⊢ roots (X - C r) = {r}

Next tactic:
ext s
@[simp]
theorem roots_X_sub_C (r : R) : roots (X - C r) = {r} := by
  classical
  ext s
  

Proof state:
case a
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
r s : R
⊢ count s (roots (X - C r)) = count s {r}

Next tactic:
rw [count_roots, rootMultiplicity_X_sub_C, count_singleton]
@[simp]
theorem roots_X : roots (X : R[X]) = {0} := by 

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
⊢ roots X = {0}

Next tactic:
rw [← roots_X_sub_C, C_0, sub_zero]
@[simp]
theorem roots_C (x : R) : (C x).roots = 0 := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
x : R
⊢ roots (C x) = 0

Next tactic:
classical exact
  if H : x = 0 then by rw [H, C_0, roots_zero]
  else
    Multiset.ext.mpr fun r => (by
      rw [count_roots, count_zero, rootMultiplicity_eq_zero (not_isRoot_C _ _ H)])
@[simp]
theorem roots_C (x : R) : (C x).roots = 0 := by
  classical 

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
x : R
⊢ roots (C x) = 0

Next tactic:
exact
  if H : x = 0 then by rw [H, C_0, roots_zero]
  else
    Multiset.ext.mpr fun r => (by
      rw [count_roots, count_zero, rootMultiplicity_eq_zero (not_isRoot_C _ _ H)])
@[simp]
theorem roots_C (x : R) : (C x).roots = 0 := by
  classical exact
  if H : x = 0 then by 

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
x : R
H : x = 0
⊢ roots (C x) = 0

Next tactic:
rw [H, C_0, roots_zero]
@[simp]
theorem roots_C (x : R) : (C x).roots = 0 := by
  classical exact
  if H : x = 0 then by rw [H, C_0, roots_zero]
  else
    Multiset.ext.mpr fun r => (by
      

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
x : R
H : ¬x = 0
r : R
⊢ count r (roots (C x)) = count r 0

Next tactic:
rw [count_roots, count_zero, rootMultiplicity_eq_zero (not_isRoot_C _ _ H)]
@[simp]
theorem roots_C_mul (p : R[X]) (ha : a ≠ 0) : (C a * p).roots = p.roots := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
ha : a ≠ 0
⊢ roots (C a * p) = roots p

Next tactic:
by_cases hp : p = 0
@[simp]
theorem roots_C_mul (p : R[X]) (ha : a ≠ 0) : (C a * p).roots = p.roots := by
  by_cases hp : p = 0 <;>
    

Proof state:
case pos
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
ha : a ≠ 0
hp : p = 0
⊢ roots (C a * p) = roots p

Next tactic:
simp only [roots_mul, *, Ne.def, mul_eq_zero, C_eq_zero, or_self_iff, not_false_iff, roots_C,
      zero_add, mul_zero]
@[simp]
theorem roots_C_mul (p : R[X]) (ha : a ≠ 0) : (C a * p).roots = p.roots := by
  by_cases hp : p = 0 <;>
    

Proof state:
case neg
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
ha : a ≠ 0
hp : ¬p = 0
⊢ roots (C a * p) = roots p

Next tactic:
simp only [roots_mul, *, Ne.def, mul_eq_zero, C_eq_zero, or_self_iff, not_false_iff, roots_C,
      zero_add, mul_zero]
@[simp]
theorem roots_smul_nonzero (p : R[X]) (ha : a ≠ 0) : (a • p).roots = p.roots := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
ha : a ≠ 0
⊢ roots (a • p) = roots p

Next tactic:
rw [smul_eq_C_mul, roots_C_mul _ ha]
@[simp]
lemma roots_neg (p : R[X]) : (-p).roots = p.roots := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p✝ q p : R[X]
⊢ roots (-p) = roots p

Next tactic:
rw [← neg_one_smul R p, roots_smul_nonzero p (neg_ne_zero.mpr one_ne_zero)]
theorem roots_list_prod (L : List R[X]) :
    (0 : R[X]) ∉ L → L.prod.roots = (L : Multiset R[X]).bind roots :=
  List.recOn L (fun _ => roots_one) fun hd tl ih H => by
    

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
L : List R[X]
hd : R[X]
tl : List R[X]
ih : 0 ∉ tl → roots (List.prod tl) = Multiset.bind (↑tl) roots
H : 0 ∉ hd :: tl
⊢ roots (List.prod (hd :: tl)) = Multiset.bind (↑(hd :: tl)) roots

Next tactic:
rw [List.mem_cons, not_or] at H
theorem roots_list_prod (L : List R[X]) :
    (0 : R[X]) ∉ L → L.prod.roots = (L : Multiset R[X]).bind roots :=
  List.recOn L (fun _ => roots_one) fun hd tl ih H => by
    rw [List.mem_cons, not_or] at H
    

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
L : List R[X]
hd : R[X]
tl : List R[X]
ih : 0 ∉ tl → roots (List.prod tl) = Multiset.bind (↑tl) roots
H : ¬0 = hd ∧ 0 ∉ tl
⊢ roots (List.prod (hd :: tl)) = Multiset.bind (↑(hd :: tl)) roots

Next tactic:
rw [List.prod_cons, roots_mul (mul_ne_zero (Ne.symm H.1) <| List.prod_ne_zero H.2), ←
      Multiset.cons_coe, Multiset.cons_bind, ih H.2]
theorem roots_multiset_prod (m : Multiset R[X]) : (0 : R[X]) ∉ m → m.prod.roots = m.bind roots := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
m : Multiset R[X]
⊢ 0 ∉ m → roots (prod m) = Multiset.bind m roots

Next tactic:
rcases m with ⟨L⟩
theorem roots_multiset_prod (m : Multiset R[X]) : (0 : R[X]) ∉ m → m.prod.roots = m.bind roots := by
  rcases m with ⟨L⟩
  

Proof state:
case mk
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
m : Multiset R[X]
L : List R[X]
⊢ 0 ∉ Quot.mk Setoid.r L → roots (prod (Quot.mk Setoid.r L)) = Multiset.bind (Quot.mk Setoid.r L) roots

Next tactic:
simpa only [Multiset.coe_prod, quot_mk_to_coe''] using roots_list_prod L
theorem roots_prod {ι : Type*} (f : ι → R[X]) (s : Finset ι) :
    s.prod f ≠ 0 → (s.prod f).roots = s.val.bind fun i => roots (f i) := by
  

Proof state:
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
ι : Type u_1
f : ι → R[X]
s : Finset ι
⊢ Finset.prod s f ≠ 0 → roots (Finset.prod s f) = Multiset.bind s.val fun i => roots (f i)

Next tactic:
rcases s with ⟨m, hm⟩
theorem roots_prod {ι : Type*} (f : ι → R[X]) (s : Finset ι) :
    s.prod f ≠ 0 → (s.prod f).roots = s.val.bind fun i => roots (f i) := by
  rcases s with ⟨m, hm⟩
  

Proof state:
case mk
R : Type u
S : Type v
T : Type w
a b : R
n : ℕ
inst✝¹ : CommRing R
inst✝ : IsDomain R
p q : R[X]
ι : Type u_1
f : ι → R[X]
m : Multiset ι
hm : Nodup m
⊢ Finset.prod { val := m, nodup := hm } f ≠ 0 →
    roots (Finset.prod { val := m, nodup := hm } f) = Multiset.bind { val := m, nodup := hm }.val fun i => roots (f i)

Next tactic:
simpa [Multiset.prod_eq_zero_iff, Multiset.bind_map] using roots_multiset_prod (m.map f)