l3lab/ntp-mathlib huggingface dataset:
theorem hasseDeriv_comp (k l : ℕ) :
    (@hasseDeriv R _ k).comp (hasseDeriv l) = (k + l).choose k • hasseDeriv (k + l) := by
  ext i : 2
  simp only [LinearMap.smul_apply, comp_apply, LinearMap.coe_comp, smul_monomial, hasseDeriv_apply,
    mul_one, monomial_eq_zero_iff, sum_monomial_index, mul_zero, ←
    tsub_add_eq_tsub_tsub, add_comm l k]
  rw_mod_cast [nsmul_eq_mul]
  rw [← Nat.cast_mul]
  congr 2
  by_cases hikl : i < k + l
  · rw [choose_eq_zero_of_lt hikl, mul_zero]
    by_cases hil : i < l
    · rw [choose_eq_zero_of_lt hil, mul_zero]
    · push_neg at hil
      rw [← tsub_lt_iff_right hil] at hikl
      rw [choose_eq_zero_of_lt hikl, zero_mul]
  push_neg at hikl
  apply @cast_injective ℚ
  have h1 : l ≤ i := le_of_add_le_right hikl
  have h2 : k ≤ i - l := le_tsub_of_add_le_right hikl
  have h3 : k ≤ k + l := le_self_add
  push_cast
  rw [cast_choose ℚ h1, cast_choose ℚ h2, cast_choose ℚ h3, cast_choose ℚ hikl]
  rw [show i - (k + l) = i - l - k by rw [add_comm]; 

Proof state:
R : Type u_1
inst✝ : Semiring R
k✝ : ℕ
f : R[X]
k l i : ℕ
hikl : k + l ≤ i
h1 : l ≤ i
h2 : k ≤ i - l
h3 : k ≤ k + l
⊢ i - (l + k) = i - l - k

Next tactic:
apply tsub_add_eq_tsub_tsub
theorem hasseDeriv_comp (k l : ℕ) :
    (@hasseDeriv R _ k).comp (hasseDeriv l) = (k + l).choose k • hasseDeriv (k + l) := by
  ext i : 2
  simp only [LinearMap.smul_apply, comp_apply, LinearMap.coe_comp, smul_monomial, hasseDeriv_apply,
    mul_one, monomial_eq_zero_iff, sum_monomial_index, mul_zero, ←
    tsub_add_eq_tsub_tsub, add_comm l k]
  rw_mod_cast [nsmul_eq_mul]
  rw [← Nat.cast_mul]
  congr 2
  by_cases hikl : i < k + l
  · rw [choose_eq_zero_of_lt hikl, mul_zero]
    by_cases hil : i < l
    · rw [choose_eq_zero_of_lt hil, mul_zero]
    · push_neg at hil
      rw [← tsub_lt_iff_right hil] at hikl
      rw [choose_eq_zero_of_lt hikl, zero_mul]
  push_neg at hikl
  apply @cast_injective ℚ
  have h1 : l ≤ i := le_of_add_le_right hikl
  have h2 : k ≤ i - l := le_tsub_of_add_le_right hikl
  have h3 : k ≤ k + l := le_self_add
  push_cast
  rw [cast_choose ℚ h1, cast_choose ℚ h2, cast_choose ℚ h3, cast_choose ℚ hikl]
  rw [show i - (k + l) = i - l - k by rw [add_comm]; apply tsub_add_eq_tsub_tsub]
  

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
k✝ : ℕ
f : R[X]
k l i : ℕ
hikl : k + l ≤ i
h1 : l ≤ i
h2 : k ≤ i - l
h3 : k ≤ k + l
⊢ ↑(i - l)! / (↑k ! * ↑(i - l - k)!) * (↑i ! / (↑l ! * ↑(i - l)!)) =
    ↑(k + l)! / (↑k ! * ↑(k + l - k)!) * (↑i ! / (↑(k + l)! * ↑(i - l - k)!))

Next tactic:
simp only [add_tsub_cancel_left]
theorem hasseDeriv_comp (k l : ℕ) :
    (@hasseDeriv R _ k).comp (hasseDeriv l) = (k + l).choose k • hasseDeriv (k + l) := by
  ext i : 2
  simp only [LinearMap.smul_apply, comp_apply, LinearMap.coe_comp, smul_monomial, hasseDeriv_apply,
    mul_one, monomial_eq_zero_iff, sum_monomial_index, mul_zero, ←
    tsub_add_eq_tsub_tsub, add_comm l k]
  rw_mod_cast [nsmul_eq_mul]
  rw [← Nat.cast_mul]
  congr 2
  by_cases hikl : i < k + l
  · rw [choose_eq_zero_of_lt hikl, mul_zero]
    by_cases hil : i < l
    · rw [choose_eq_zero_of_lt hil, mul_zero]
    · push_neg at hil
      rw [← tsub_lt_iff_right hil] at hikl
      rw [choose_eq_zero_of_lt hikl, zero_mul]
  push_neg at hikl
  apply @cast_injective ℚ
  have h1 : l ≤ i := le_of_add_le_right hikl
  have h2 : k ≤ i - l := le_tsub_of_add_le_right hikl
  have h3 : k ≤ k + l := le_self_add
  push_cast
  rw [cast_choose ℚ h1, cast_choose ℚ h2, cast_choose ℚ h3, cast_choose ℚ hikl]
  rw [show i - (k + l) = i - l - k by rw [add_comm]; apply tsub_add_eq_tsub_tsub]
  simp only [add_tsub_cancel_left]
  

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
k✝ : ℕ
f : R[X]
k l i : ℕ
hikl : k + l ≤ i
h1 : l ≤ i
h2 : k ≤ i - l
h3 : k ≤ k + l
⊢ ↑(i - l)! / (↑k ! * ↑(i - l - k)!) * (↑i ! / (↑l ! * ↑(i - l)!)) =
    ↑(k + l)! / (↑k ! * ↑l !) * (↑i ! / (↑(k + l)! * ↑(i - l - k)!))

Next tactic:
field_simp
theorem hasseDeriv_comp (k l : ℕ) :
    (@hasseDeriv R _ k).comp (hasseDeriv l) = (k + l).choose k • hasseDeriv (k + l) := by
  ext i : 2
  simp only [LinearMap.smul_apply, comp_apply, LinearMap.coe_comp, smul_monomial, hasseDeriv_apply,
    mul_one, monomial_eq_zero_iff, sum_monomial_index, mul_zero, ←
    tsub_add_eq_tsub_tsub, add_comm l k]
  rw_mod_cast [nsmul_eq_mul]
  rw [← Nat.cast_mul]
  congr 2
  by_cases hikl : i < k + l
  · rw [choose_eq_zero_of_lt hikl, mul_zero]
    by_cases hil : i < l
    · rw [choose_eq_zero_of_lt hil, mul_zero]
    · push_neg at hil
      rw [← tsub_lt_iff_right hil] at hikl
      rw [choose_eq_zero_of_lt hikl, zero_mul]
  push_neg at hikl
  apply @cast_injective ℚ
  have h1 : l ≤ i := le_of_add_le_right hikl
  have h2 : k ≤ i - l := le_tsub_of_add_le_right hikl
  have h3 : k ≤ k + l := le_self_add
  push_cast
  rw [cast_choose ℚ h1, cast_choose ℚ h2, cast_choose ℚ h3, cast_choose ℚ hikl]
  rw [show i - (k + l) = i - l - k by rw [add_comm]; apply tsub_add_eq_tsub_tsub]
  simp only [add_tsub_cancel_left]
  field_simp; 

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
k✝ : ℕ
f : R[X]
k l i : ℕ
hikl : k + l ≤ i
h1 : l ≤ i
h2 : k ≤ i - l
h3 : k ≤ k + l
⊢ ↑(i - l)! * ↑i ! * (↑k ! * ↑l ! * (↑(k + l)! * ↑(i - l - k)!)) =
    ↑(k + l)! * ↑i ! * (↑k ! * ↑(i - l - k)! * (↑l ! * ↑(i - l)!))

Next tactic:
ring
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ natDegree ((hasseDeriv n) p) ≤ natDegree p - n

Next tactic:
classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      intro x hx hx'
      have hxp : x ≤ p.natDegree := le_natDegree_of_ne_zero hx
      have hxn : n ≤ x := by
        contrapose! hx'
        simp [Nat.choose_eq_zero_of_lt hx']
      rwa [tsub_add_cancel_of_le (hxn.trans hxp)]
    · simp
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ natDegree ((hasseDeriv n) p) ≤ natDegree p - n

Next tactic:
rw [hasseDeriv_apply, sum_def]
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ natDegree (∑ n_1 in support p, (monomial (n_1 - n)) (↑(choose n_1 n) * coeff p n_1)) ≤ natDegree p - n

Next tactic:
refine' (natDegree_sum_le _ _).trans _
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ Finset.fold max 0 (natDegree ∘ fun n_1 => (monomial (n_1 - n)) (↑(choose n_1 n) * coeff p n_1)) (support p) ≤
    natDegree p - n

Next tactic:
simp_rw [Function.comp, natDegree_monomial]
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ Finset.fold max 0 (fun x => if ↑(choose x n) * coeff p x = 0 then 0 else x - n) (support p) ≤ natDegree p - n

Next tactic:
rw [Finset.fold_ite, Finset.fold_const]
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · 

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ max (if Finset.filter (fun x => ↑(choose x n) * coeff p x = 0) (support p) = ∅ then 0 else max 0 0)
      (Finset.fold max 0 (fun x => x - n) (Finset.filter (fun i => ¬↑(choose i n) * coeff p i = 0) (support p))) ≤
    natDegree p - n

Next tactic:
simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ ∀ (x : ℕ), ¬coeff p x = 0 → ¬↑(choose x n) * coeff p x = 0 → x ≤ natDegree p - n + n

Next tactic:
intro x hx hx'
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      intro x hx hx'
      

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n x : ℕ
hx : ¬coeff p x = 0
hx' : ¬↑(choose x n) * coeff p x = 0
⊢ x ≤ natDegree p - n + n

Next tactic:
have hxp : x ≤ p.natDegree := le_natDegree_of_ne_zero hx
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      intro x hx hx'
      have hxp : x ≤ p.natDegree := le_natDegree_of_ne_zero hx
      

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n x : ℕ
hx : ¬coeff p x = 0
hx' : ¬↑(choose x n) * coeff p x = 0
hxp : x ≤ natDegree p
⊢ x ≤ natDegree p - n + n

Next tactic:
have hxn : n ≤ x := by
        contrapose! hx'
        simp [Nat.choose_eq_zero_of_lt hx']
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      intro x hx hx'
      have hxp : x ≤ p.natDegree := le_natDegree_of_ne_zero hx
      have hxn : n ≤ x := by
        

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n x : ℕ
hx : ¬coeff p x = 0
hx' : ¬↑(choose x n) * coeff p x = 0
hxp : x ≤ natDegree p
⊢ n ≤ x

Next tactic:
contrapose! hx'
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      intro x hx hx'
      have hxp : x ≤ p.natDegree := le_natDegree_of_ne_zero hx
      have hxn : n ≤ x := by
        contrapose! hx'
        

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n x : ℕ
hx : ¬coeff p x = 0
hxp : x ≤ natDegree p
hx' : x < n
⊢ ↑(choose x n) * coeff p x = 0

Next tactic:
simp [Nat.choose_eq_zero_of_lt hx']
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      intro x hx hx'
      have hxp : x ≤ p.natDegree := le_natDegree_of_ne_zero hx
      have hxn : n ≤ x := by
        contrapose! hx'
        simp [Nat.choose_eq_zero_of_lt hx']
      

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n x : ℕ
hx : ¬coeff p x = 0
hx' : ¬↑(choose x n) * coeff p x = 0
hxp : x ≤ natDegree p
hxn : n ≤ x
⊢ x ≤ natDegree p - n + n

Next tactic:
rwa [tsub_add_cancel_of_le (hxn.trans hxp)]
theorem natDegree_hasseDeriv_le (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) ≤ natDegree p - n := by
  classical
    rw [hasseDeriv_apply, sum_def]
    refine' (natDegree_sum_le _ _).trans _
    simp_rw [Function.comp, natDegree_monomial]
    rw [Finset.fold_ite, Finset.fold_const]
    · simp only [ite_self, max_eq_right, zero_le', Finset.fold_max_le, true_and_iff, and_imp,
        tsub_le_iff_right, mem_support_iff, Ne.def, Finset.mem_filter]
      intro x hx hx'
      have hxp : x ≤ p.natDegree := le_natDegree_of_ne_zero hx
      have hxn : n ≤ x := by
        contrapose! hx'
        simp [Nat.choose_eq_zero_of_lt hx']
      rwa [tsub_add_cancel_of_le (hxn.trans hxp)]
    · 

Proof state:
case h
R : Type u_1
inst✝ : Semiring R
k : ℕ
f p : R[X]
n : ℕ
⊢ max 0 (max 0 0) = max 0 0

Next tactic:
simp
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  

Proof state:
R : Type u_1
inst✝¹ : Semiring R
k : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
⊢ natDegree ((hasseDeriv n) p) = natDegree p - n

Next tactic:
cases' lt_or_le p.natDegree n with hn hn
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · 

Proof state:
case inl
R : Type u_1
inst✝¹ : Semiring R
k : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : natDegree p < n
⊢ natDegree ((hasseDeriv n) p) = natDegree p - n

Next tactic:
simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
  · 

Proof state:
case inr
R : Type u_1
inst✝¹ : Semiring R
k : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : n ≤ natDegree p
⊢ natDegree ((hasseDeriv n) p) = natDegree p - n

Next tactic:
refine' map_natDegree_eq_sub _ _
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
  · refine' map_natDegree_eq_sub _ _
    · 

Proof state:
case inr.refine'_1
R : Type u_1
inst✝¹ : Semiring R
k : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : n ≤ natDegree p
⊢ ∀ (f : R[X]), natDegree f < n → (hasseDeriv n) f = 0

Next tactic:
exact fun h => hasseDeriv_eq_zero_of_lt_natDegree _ _
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
  · refine' map_natDegree_eq_sub _ _
    · exact fun h => hasseDeriv_eq_zero_of_lt_natDegree _ _
    · 

Proof state:
case inr.refine'_2
R : Type u_1
inst✝¹ : Semiring R
k : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : n ≤ natDegree p
⊢ ∀ (n_1 : ℕ) (c : R), c ≠ 0 → natDegree ((hasseDeriv n) ((monomial n_1) c)) = n_1 - n

Next tactic:
classical
        simp only [ite_eq_right_iff, Ne.def, natDegree_monomial, hasseDeriv_monomial]
        intro k c c0 hh
        -- this is where we use the `smul_eq_zero` from `NoZeroSMulDivisors`
        rw [← nsmul_eq_mul, smul_eq_zero, Nat.choose_eq_zero_iff] at hh
        exact (tsub_eq_zero_of_le (Or.resolve_right hh c0).le).symm
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
  · refine' map_natDegree_eq_sub _ _
    · exact fun h => hasseDeriv_eq_zero_of_lt_natDegree _ _
    · classical
        

Proof state:
case inr.refine'_2
R : Type u_1
inst✝¹ : Semiring R
k : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : n ≤ natDegree p
⊢ ∀ (n_1 : ℕ) (c : R), c ≠ 0 → natDegree ((hasseDeriv n) ((monomial n_1) c)) = n_1 - n

Next tactic:
simp only [ite_eq_right_iff, Ne.def, natDegree_monomial, hasseDeriv_monomial]
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
  · refine' map_natDegree_eq_sub _ _
    · exact fun h => hasseDeriv_eq_zero_of_lt_natDegree _ _
    · classical
        simp only [ite_eq_right_iff, Ne.def, natDegree_monomial, hasseDeriv_monomial]
        

Proof state:
case inr.refine'_2
R : Type u_1
inst✝¹ : Semiring R
k : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : n ≤ natDegree p
⊢ ∀ (n_1 : ℕ) (c : R), ¬c = 0 → ↑(choose n_1 n) * c = 0 → 0 = n_1 - n

Next tactic:
intro k c c0 hh
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
  · refine' map_natDegree_eq_sub _ _
    · exact fun h => hasseDeriv_eq_zero_of_lt_natDegree _ _
    · classical
        simp only [ite_eq_right_iff, Ne.def, natDegree_monomial, hasseDeriv_monomial]
        intro k c c0 hh
        -- this is where we use the `smul_eq_zero` from `NoZeroSMulDivisors`
        

Proof state:
case inr.refine'_2
R : Type u_1
inst✝¹ : Semiring R
k✝ : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : n ≤ natDegree p
k : ℕ
c : R
c0 : ¬c = 0
hh : ↑(choose k n) * c = 0
⊢ 0 = k - n

Next tactic:
rw [← nsmul_eq_mul, smul_eq_zero, Nat.choose_eq_zero_iff] at hh
theorem natDegree_hasseDeriv [NoZeroSMulDivisors ℕ R] (p : R[X]) (n : ℕ) :
    natDegree (hasseDeriv n p) = natDegree p - n := by
  cases' lt_or_le p.natDegree n with hn hn
  · simpa [hasseDeriv_eq_zero_of_lt_natDegree, hn] using (tsub_eq_zero_of_le hn.le).symm
  · refine' map_natDegree_eq_sub _ _
    · exact fun h => hasseDeriv_eq_zero_of_lt_natDegree _ _
    · classical
        simp only [ite_eq_right_iff, Ne.def, natDegree_monomial, hasseDeriv_monomial]
        intro k c c0 hh
        -- this is where we use the `smul_eq_zero` from `NoZeroSMulDivisors`
        rw [← nsmul_eq_mul, smul_eq_zero, Nat.choose_eq_zero_iff] at hh
        

Proof state:
case inr.refine'_2
R : Type u_1
inst✝¹ : Semiring R
k✝ : ℕ
f : R[X]
inst✝ : NoZeroSMulDivisors ℕ R
p : R[X]
n : ℕ
hn : n ≤ natDegree p
k : ℕ
c : R
c0 : ¬c = 0
hh : k < n ∨ c = 0
⊢ 0 = k - n

Next tactic:
exact (tsub_eq_zero_of_le (Or.resolve_right hh c0).le).symm
theorem hasseDeriv_mul (f g : R[X]) :
    hasseDeriv k (f * g) = ∑ ij in antidiagonal k, hasseDeriv ij.1 f * hasseDeriv ij.2 g := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f✝ f g : R[X]
⊢ (hasseDeriv k) (f * g) = ∑ ij in antidiagonal k, (hasseDeriv ij.1) f * (hasseDeriv ij.2) g

Next tactic:
let D k := (@hasseDeriv R _ k).toAddMonoidHom
theorem hasseDeriv_mul (f g : R[X]) :
    hasseDeriv k (f * g) = ∑ ij in antidiagonal k, hasseDeriv ij.1 f * hasseDeriv ij.2 g := by
  let D k := (@hasseDeriv R _ k).toAddMonoidHom
  

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f✝ f g : R[X]
D : ℕ → R[X] →+ R[X] := fun k => LinearMap.toAddMonoidHom (hasseDeriv k)
⊢ (hasseDeriv k) (f * g) = ∑ ij in antidiagonal k, (hasseDeriv ij.1) f * (hasseDeriv ij.2) g

Next tactic:
let Φ := @AddMonoidHom.mul R[X] _
theorem hasseDeriv_mul (f g : R[X]) :
    hasseDeriv k (f * g) = ∑ ij in antidiagonal k, hasseDeriv ij.1 f * hasseDeriv ij.2 g := by
  let D k := (@hasseDeriv R _ k).toAddMonoidHom
  let Φ := @AddMonoidHom.mul R[X] _
  

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f✝ f g : R[X]
D : ℕ → R[X] →+ R[X] := fun k => LinearMap.toAddMonoidHom (hasseDeriv k)
Φ : R[X] →+ R[X] →+ R[X] := mul
⊢ (hasseDeriv k) (f * g) = ∑ ij in antidiagonal k, (hasseDeriv ij.1) f * (hasseDeriv ij.2) g

Next tactic:
show
    (compHom (D k)).comp Φ f g =
      ∑ ij : ℕ × ℕ in antidiagonal k, ((compHom.comp ((compHom Φ) (D ij.1))).flip (D ij.2) f) g
theorem hasseDeriv_mul (f g : R[X]) :
    hasseDeriv k (f * g) = ∑ ij in antidiagonal k, hasseDeriv ij.1 f * hasseDeriv ij.2 g := by
  let D k := (@hasseDeriv R _ k).toAddMonoidHom
  let Φ := @AddMonoidHom.mul R[X] _
  show
    (compHom (D k)).comp Φ f g =
      ∑ ij : ℕ × ℕ in antidiagonal k, ((compHom.comp ((compHom Φ) (D ij.1))).flip (D ij.2) f) g
  

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f✝ f g : R[X]
D : ℕ → R[X] →+ R[X] := fun k => LinearMap.toAddMonoidHom (hasseDeriv k)
Φ : R[X] →+ R[X] →+ R[X] := mul
⊢ ((AddMonoidHom.comp (compHom (D k)) Φ) f) g =
    ∑ ij in antidiagonal k, (((AddMonoidHom.flip (AddMonoidHom.comp compHom ((compHom Φ) (D ij.1)))) (D ij.2)) f) g

Next tactic:
simp only [← finset_sum_apply]
theorem hasseDeriv_mul (f g : R[X]) :
    hasseDeriv k (f * g) = ∑ ij in antidiagonal k, hasseDeriv ij.1 f * hasseDeriv ij.2 g := by
  let D k := (@hasseDeriv R _ k).toAddMonoidHom
  let Φ := @AddMonoidHom.mul R[X] _
  show
    (compHom (D k)).comp Φ f g =
      ∑ ij : ℕ × ℕ in antidiagonal k, ((compHom.comp ((compHom Φ) (D ij.1))).flip (D ij.2) f) g
  simp only [← finset_sum_apply]
  

Proof state:
R : Type u_1
inst✝ : Semiring R
k : ℕ
f✝ f g : R[X]
D : ℕ → R[X] →+ R[X] := fun k => LinearMap.toAddMonoidHom (hasseDeriv k)
Φ : R[X] →+ R[X] →+ R[X] := mul
⊢ ((AddMonoidHom.comp (compHom (LinearMap.toAddMonoidHom (hasseDeriv k))) mul) f) g =
    ((∑ x in antidiagonal k,
          (AddMonoidHom.flip (AddMonoidHom.comp compHom ((compHom mul) (LinearMap.toAddMonoidHom (hasseDeriv x.1)))))
            (LinearMap.toAddMonoidHom (hasseDeriv x.2)))
        f)
      g

Next tactic:
congr 2
theorem hasseDeriv_mul (f g : R[X]) :
    hasseDeriv k (f * g) = ∑ ij in antidiagonal k, hasseDeriv ij.1 f * hasseDeriv ij.2 g := by
  let D k := (@hasseDeriv R _ k).toAddMonoidHom
  let Φ := @AddMonoidHom.mul R[X] _
  show
    (compHom (D k)).comp Φ f g =
      ∑ ij : ℕ × ℕ in antidiagonal k, ((compHom.comp ((compHom Φ) (D ij.1))).flip (D ij.2) f) g
  simp only [← finset_sum_apply]
  congr 2
  

Proof state:
case e_a.e_a
R : Type u_1
inst✝ : Semiring R
k : ℕ
f✝ f g : R[X]
D : ℕ → R[X] →+ R[X] := fun k => LinearMap.toAddMonoidHom (hasseDeriv k)
Φ : R[X] →+ R[X] →+ R[X] := mul
⊢ AddMonoidHom.comp (compHom (LinearMap.toAddMonoidHom (hasseDeriv k))) mul =
    ∑ x in antidiagonal k,
      (AddMonoidHom.flip (AddMonoidHom.comp compHom ((compHom mul) (LinearMap.toAddMonoidHom (hasseDeriv x.1)))))
        (LinearMap.toAddMonoidHom (hasseDeriv x.2))

Next tactic:
clear f g