l3lab/ntp-mathlib huggingface dataset:
theorem reflect_mul_induction (cf cg : ℕ) :
    ∀ N O : ℕ,
      ∀ f g : R[X],
        f.support.card ≤ cf.succ →
          g.support.card ≤ cg.succ →
            f.natDegree ≤ N →
              g.natDegree ≤ O → reflect (N + O) (f * g) = reflect N f * reflect O g := by
  induction' cf with cf hcf
  --first induction (left): base case
  · induction' cg with cg hcg
    -- second induction (right): base case
    · intro N O f g Cf Cg Nf Og
      rw [← C_mul_X_pow_eq_self Cf, ← C_mul_X_pow_eq_self Cg]
      simp_rw [mul_assoc, X_pow_mul, mul_assoc, ← pow_add (X : R[X]), reflect_C_mul,
        reflect_monomial, add_comm, revAt_add Nf Og, mul_assoc, X_pow_mul, mul_assoc, ←
        pow_add (X : R[X]), add_comm]
    -- second induction (right): induction step
    · intro N O f g Cf Cg Nf Og
      by_cases g0 : g = 0
      · rw [g0, reflect_zero, mul_zero, mul_zero, reflect_zero]
      rw [← eraseLead_add_C_mul_X_pow g, mul_add, reflect_add, reflect_add, mul_add, hcg, hcg] <;>
        try assumption
      · exact le_add_left card_support_C_mul_X_pow_le_one
      · exact le_trans (natDegree_C_mul_X_pow_le g.leadingCoeff g.natDegree) Og
      · exact Nat.lt_succ_iff.mp (gt_of_ge_of_gt Cg (eraseLead_support_card_lt g0))
      · exact le_trans eraseLead_natDegree_le_aux Og
  --first induction (left): induction step
  · intro N O f g Cf Cg Nf Og
    by_cases f0 : f = 0
    · rw [f0, reflect_zero, zero_mul, zero_mul, reflect_zero]
    rw [← eraseLead_add_C_mul_X_pow f, add_mul, reflect_add, reflect_add, add_mul, hcf, hcf] <;>
      try assumption
    · exact le_add_left card_support_C_mul_X_pow_le_one
    · exact le_trans (natDegree_C_mul_X_pow_le f.leadingCoeff f.natDegree) Nf
    · exact Nat.lt_succ_iff.mp (gt_of_ge_of_gt Cf (eraseLead_support_card_lt f0))
    · 

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
f✝ : R[X]
cg cf : ℕ
hcf :
  ∀ (N O : ℕ) (f g : R[X]),
    card (support f) ≤ Nat.succ cf →
      card (support g) ≤ Nat.succ cg →
        natDegree f ≤ N → natDegree g ≤ O → reflect (N + O) (f * g) = reflect N f * reflect O g
N O : ℕ
f g : R[X]
Cf : card (support f) ≤ Nat.succ (Nat.succ cf)
Cg : card (support g) ≤ Nat.succ cg
Nf : natDegree f ≤ N
Og : natDegree g ≤ O
f0 : ¬f = 0
⊢ natDegree (eraseLead f) ≤ N

Next tactic:
exact le_trans eraseLead_natDegree_le_aux Nf
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  

Proof state:
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ eval₂ i (⅟x) (reflect N f) * x ^ N = eval₂ i x f

Next tactic:
refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · 

Proof state:
case refine'_1
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ (fun f => eval₂ i (⅟x) (reflect N f) * x ^ N = eval₂ i x f) 0

Next tactic:
simp
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · 

Proof state:
case refine'_2
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ ∀ (n : ℕ) (r : R), r ≠ 0 → n ≤ N → (fun f => eval₂ i (⅟x) (reflect N f) * x ^ N = eval₂ i x f) (C r * X ^ n)

Next tactic:
intro n r _ hnN
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r _ hnN
    

Proof state:
case refine'_2
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
n : ℕ
r : R
a✝ : r ≠ 0
hnN : n ≤ N
⊢ eval₂ i (⅟x) (reflect N (C r * X ^ n)) * x ^ N = eval₂ i x (C r * X ^ n)

Next tactic:
simp only [revAt_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r _ hnN
    simp only [revAt_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
    

Proof state:
case refine'_2
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
n : ℕ
r : R
a✝ : r ≠ 0
hnN : n ≤ N
⊢ i r * ⅟x ^ (N - n) * x ^ N = i r * x ^ n

Next tactic:
conv in x ^ N => rw [← Nat.sub_add_cancel hnN]
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r _ hnN
    simp only [revAt_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
    conv in x ^ N => 

Proof state:
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
n : ℕ
r : R
a✝ : r ≠ 0
hnN : n ≤ N
| x ^ N

Next tactic:
rw [← Nat.sub_add_cancel hnN]
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r _ hnN
    simp only [revAt_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
    conv in x ^ N => rw [← Nat.sub_add_cancel hnN]
    

Proof state:
case refine'_2
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
n : ℕ
r : R
a✝ : r ≠ 0
hnN : n ≤ N
⊢ i r * ⅟x ^ (N - n) * x ^ (N - n + n) = i r * x ^ n

Next tactic:
rw [pow_add, ← mul_assoc, mul_assoc (i r), ← mul_pow, invOf_mul_self, one_pow, mul_one]
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r _ hnN
    simp only [revAt_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
    conv in x ^ N => rw [← Nat.sub_add_cancel hnN]
    rw [pow_add, ← mul_assoc, mul_assoc (i r), ← mul_pow, invOf_mul_self, one_pow, mul_one]
  · 

Proof state:
case refine'_3
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ ∀ (f g : R[X]),
    natDegree f < natDegree g →
      natDegree g ≤ N →
        (fun f => eval₂ i (⅟x) (reflect N f) * x ^ N = eval₂ i x f) f →
          (fun f => eval₂ i (⅟x) (reflect N f) * x ^ N = eval₂ i x f) g →
            (fun f => eval₂ i (⅟x) (reflect N f) * x ^ N = eval₂ i x f) (f + g)

Next tactic:
intros
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r _ hnN
    simp only [revAt_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
    conv in x ^ N => rw [← Nat.sub_add_cancel hnN]
    rw [pow_add, ← mul_assoc, mul_assoc (i r), ← mul_pow, invOf_mul_self, one_pow, mul_one]
  · intros
    

Proof state:
case refine'_3
R : Type u_1
inst✝² : Semiring R
f✝¹ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
f✝ g✝ : R[X]
a✝³ : natDegree f✝ < natDegree g✝
a✝² : natDegree g✝ ≤ N
a✝¹ : eval₂ i (⅟x) (reflect N f✝) * x ^ N = eval₂ i x f✝
a✝ : eval₂ i (⅟x) (reflect N g✝) * x ^ N = eval₂ i x g✝
⊢ eval₂ i (⅟x) (reflect N (f✝ + g✝)) * x ^ N = eval₂ i x (f✝ + g✝)

Next tactic:
simp [*, add_mul]
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  

Proof state:
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ eval₂ i (⅟x) (reflect N f) = 0 ↔ eval₂ i x f = 0

Next tactic:
conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  conv_rhs => 

Proof state:
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
| eval₂ i x f = 0

Next tactic:
rw [← eval₂_reflect_mul_pow i x N f hf]
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
  

Proof state:
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ eval₂ i (⅟x) (reflect N f) = 0 ↔ eval₂ i (⅟x) (reflect N f) * x ^ N = 0

Next tactic:
constructor
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
  constructor
  · 

Proof state:
case mp
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ eval₂ i (⅟x) (reflect N f) = 0 → eval₂ i (⅟x) (reflect N f) * x ^ N = 0

Next tactic:
intro h
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
  constructor
  · intro h
    

Proof state:
case mp
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
h : eval₂ i (⅟x) (reflect N f) = 0
⊢ eval₂ i (⅟x) (reflect N f) * x ^ N = 0

Next tactic:
rw [h, zero_mul]
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
  constructor
  · intro h
    rw [h, zero_mul]
  · 

Proof state:
case mpr
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
⊢ eval₂ i (⅟x) (reflect N f) * x ^ N = 0 → eval₂ i (⅟x) (reflect N f) = 0

Next tactic:
intro h
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
  constructor
  · intro h
    rw [h, zero_mul]
  · intro h
    

Proof state:
case mpr
R : Type u_1
inst✝² : Semiring R
f✝ : R[X]
S : Type u_2
inst✝¹ : CommSemiring S
i : R →+* S
x : S
inst✝ : Invertible x
N : ℕ
f : R[X]
hf : natDegree f ≤ N
h : eval₂ i (⅟x) (reflect N f) * x ^ N = 0
⊢ eval₂ i (⅟x) (reflect N f) = 0

Next tactic:
rw [← mul_one (eval₂ i (⅟ x) _), ← one_pow N, ← mul_invOf_self x, mul_pow, ← mul_assoc, h,
      zero_mul]
theorem coeff_reverse (f : R[X]) (n : ℕ) : f.reverse.coeff n = f.coeff (revAt f.natDegree n) := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
n : ℕ
⊢ coeff (reverse f) n = coeff f ((revAt (natDegree f)) n)

Next tactic:
rw [reverse, coeff_reflect]
@[simp]
theorem coeff_zero_reverse (f : R[X]) : coeff (reverse f) 0 = leadingCoeff f := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ coeff (reverse f) 0 = leadingCoeff f

Next tactic:
rw [coeff_reverse, revAt_le (zero_le f.natDegree), tsub_zero, leadingCoeff]
@[simp]
theorem reverse_eq_zero : f.reverse = 0 ↔ f = 0 := by 

Proof state:
R : Type u_1
inst✝ : Semiring R
f : R[X]
⊢ reverse f = 0 ↔ f = 0

Next tactic:
simp [reverse]
theorem reverse_natDegree_le (f : R[X]) : f.reverse.natDegree ≤ f.natDegree := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ natDegree (reverse f) ≤ natDegree f

Next tactic:
rw [natDegree_le_iff_degree_le, degree_le_iff_coeff_zero]
theorem reverse_natDegree_le (f : R[X]) : f.reverse.natDegree ≤ f.natDegree := by
  rw [natDegree_le_iff_degree_le, degree_le_iff_coeff_zero]
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ ∀ (m : ℕ), ↑(natDegree f) < ↑m → coeff (reverse f) m = 0

Next tactic:
intro n hn
theorem reverse_natDegree_le (f : R[X]) : f.reverse.natDegree ≤ f.natDegree := by
  rw [natDegree_le_iff_degree_le, degree_le_iff_coeff_zero]
  intro n hn
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
n : ℕ
hn : ↑(natDegree f) < ↑n
⊢ coeff (reverse f) n = 0

Next tactic:
rw [Nat.cast_lt] at hn
theorem reverse_natDegree_le (f : R[X]) : f.reverse.natDegree ≤ f.natDegree := by
  rw [natDegree_le_iff_degree_le, degree_le_iff_coeff_zero]
  intro n hn
  rw [Nat.cast_lt] at hn
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
n : ℕ
hn : natDegree f < n
⊢ coeff (reverse f) n = 0

Next tactic:
rw [coeff_reverse, revAt, Function.Embedding.coeFn_mk, if_neg (not_le_of_gt hn),
    coeff_eq_zero_of_natDegree_lt hn]
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ natDegree f = natDegree (reverse f) + natTrailingDegree f

Next tactic:
by_cases hf : f = 0
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · 

Proof state:
case pos
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : f = 0
⊢ natDegree f = natDegree (reverse f) + natTrailingDegree f

Next tactic:
rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  

Proof state:
case neg
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ natDegree f = natDegree (reverse f) + natTrailingDegree f

Next tactic:
apply le_antisymm
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · 

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ natDegree f ≤ natDegree (reverse f) + natTrailingDegree f

Next tactic:
refine' tsub_le_iff_right.mp _
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ natDegree f - natTrailingDegree f ≤ natDegree (reverse f)

Next tactic:
apply le_natDegree_of_ne_zero
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_natDegree_of_ne_zero
    

Proof state:
case neg.a.h
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ coeff (reverse f) (natDegree f - natTrailingDegree f) ≠ 0

Next tactic:
rw [reverse, coeff_reflect, ← revAt_le f.natTrailingDegree_le_natDegree, revAt_invol]
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_natDegree_of_ne_zero
    rw [reverse, coeff_reflect, ← revAt_le f.natTrailingDegree_le_natDegree, revAt_invol]
    

Proof state:
case neg.a.h
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ coeff f (natTrailingDegree f) ≠ 0

Next tactic:
exact trailingCoeff_nonzero_iff_nonzero.mpr hf
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_natDegree_of_ne_zero
    rw [reverse, coeff_reflect, ← revAt_le f.natTrailingDegree_le_natDegree, revAt_invol]
    exact trailingCoeff_nonzero_iff_nonzero.mpr hf
  · 

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ natDegree (reverse f) + natTrailingDegree f ≤ natDegree f

Next tactic:
rw [← le_tsub_iff_left f.reverse_natDegree_le]
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_natDegree_of_ne_zero
    rw [reverse, coeff_reflect, ← revAt_le f.natTrailingDegree_le_natDegree, revAt_invol]
    exact trailingCoeff_nonzero_iff_nonzero.mpr hf
  · rw [← le_tsub_iff_left f.reverse_natDegree_le]
    

Proof state:
case neg.a
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ natTrailingDegree f ≤ natDegree f - natDegree (reverse f)

Next tactic:
apply natTrailingDegree_le_of_ne_zero
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_natDegree_of_ne_zero
    rw [reverse, coeff_reflect, ← revAt_le f.natTrailingDegree_le_natDegree, revAt_invol]
    exact trailingCoeff_nonzero_iff_nonzero.mpr hf
  · rw [← le_tsub_iff_left f.reverse_natDegree_le]
    apply natTrailingDegree_le_of_ne_zero
    

Proof state:
case neg.a.h
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ coeff f (natDegree f - natDegree (reverse f)) ≠ 0

Next tactic:
have key := mt leadingCoeff_eq_zero.mp (mt reverse_eq_zero.mp hf)
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_natDegree_of_ne_zero
    rw [reverse, coeff_reflect, ← revAt_le f.natTrailingDegree_le_natDegree, revAt_invol]
    exact trailingCoeff_nonzero_iff_nonzero.mpr hf
  · rw [← le_tsub_iff_left f.reverse_natDegree_le]
    apply natTrailingDegree_le_of_ne_zero
    have key := mt leadingCoeff_eq_zero.mp (mt reverse_eq_zero.mp hf)
    

Proof state:
case neg.a.h
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
key : ¬leadingCoeff (reverse f) = 0
⊢ coeff f (natDegree f - natDegree (reverse f)) ≠ 0

Next tactic:
rwa [leadingCoeff, coeff_reverse, revAt_le f.reverse_natDegree_le] at key
theorem reverse_natDegree (f : R[X]) : f.reverse.natDegree = f.natDegree - f.natTrailingDegree := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ natDegree (reverse f) = natDegree f - natTrailingDegree f

Next tactic:
rw [f.natDegree_eq_reverse_natDegree_add_natTrailingDegree, add_tsub_cancel_right]
theorem reverse_leadingCoeff (f : R[X]) : f.reverse.leadingCoeff = f.trailingCoeff := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ leadingCoeff (reverse f) = trailingCoeff f

Next tactic:
rw [leadingCoeff, reverse_natDegree, ← revAt_le f.natTrailingDegree_le_natDegree,
    coeff_reverse, revAt_invol, trailingCoeff]
theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ natTrailingDegree (reverse f) = 0

Next tactic:
by_cases hf : f = 0
theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 := by
  by_cases hf : f = 0
  · 

Proof state:
case pos
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : f = 0
⊢ natTrailingDegree (reverse f) = 0

Next tactic:
rw [hf, reverse_zero, natTrailingDegree_zero]
theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natTrailingDegree_zero]
  · 

Proof state:
case neg
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ natTrailingDegree (reverse f) = 0

Next tactic:
rw [← le_zero_iff]
theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natTrailingDegree_zero]
  · rw [← le_zero_iff]
    

Proof state:
case neg
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ natTrailingDegree (reverse f) ≤ 0

Next tactic:
apply natTrailingDegree_le_of_ne_zero
theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natTrailingDegree_zero]
  · rw [← le_zero_iff]
    apply natTrailingDegree_le_of_ne_zero
    

Proof state:
case neg.h
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ coeff (reverse f) 0 ≠ 0

Next tactic:
rw [coeff_zero_reverse]
theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natTrailingDegree_zero]
  · rw [← le_zero_iff]
    apply natTrailingDegree_le_of_ne_zero
    rw [coeff_zero_reverse]
    

Proof state:
case neg.h
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
hf : ¬f = 0
⊢ leadingCoeff f ≠ 0

Next tactic:
exact mt leadingCoeff_eq_zero.mp hf
theorem reverse_trailingCoeff (f : R[X]) : f.reverse.trailingCoeff = f.leadingCoeff := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f : R[X]
⊢ trailingCoeff (reverse f) = leadingCoeff f

Next tactic:
rw [trailingCoeff, reverse_natTrailingDegree, coeff_zero_reverse]
theorem reverse_mul {f g : R[X]} (fg : f.leadingCoeff * g.leadingCoeff ≠ 0) :
    reverse (f * g) = reverse f * reverse g := by
  

Proof state:
R : Type u_1
inst✝ : Semiring R
f✝ f g : R[X]
fg : leadingCoeff f * leadingCoeff g ≠ 0
⊢ reverse (f * g) = reverse f * reverse g

Next tactic:
unfold reverse