forked from leanprover-community/mathlib4
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathRootsExtrema.lean
More file actions
353 lines (314 loc) · 15.3 KB
/
Copy pathRootsExtrema.lean
File metadata and controls
353 lines (314 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/-
Copyright (c) 2025 Yuval Filmus. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yuval Filmus
-/
module
public import Mathlib.RingTheory.Polynomial.Chebyshev
public import Mathlib.Data.Real.Basic
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic
public import Mathlib.Algebra.Polynomial.Roots
public import Mathlib.NumberTheory.Real.Irrational
import Mathlib.Analysis.Calculus.Deriv.Polynomial
import Mathlib.Analysis.SpecialFunctions.Arcosh
import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.Basic
import Mathlib.Analysis.SpecialFunctions.Trigonometric.Complex
import Mathlib.NumberTheory.Niven
/-!
# Chebyshev polynomials over the reals: roots and extrema
## Main statements
* `T_n(x) ∈ [-1, 1]` iff `x ∈ [-1, 1]`: `abs_eval_T_real_le_one_iff`
* Zeroes of `T` and `U`: `roots_T_real`, `roots_U_real`
* Local extrema of `T`: `isLocalExtr_T_real_iff`, `isExtrOn_T_real_iff`
* Irrationality of zeroes of `T` other than zero: `irrational_of_isRoot_T_real`
* `|T_n^{(k)} (x)| ≤ T_n^{(k)} (1)` for `x ∈ [-1, 1]`: `abs_iterate_derivative_T_real_le`
## TODO
Show that the bound on `T_n^{(k)} (x)` is achieved only at `x = ±1`
-/
public section
namespace Polynomial.Chebyshev
open Real
theorem eval_T_real_mem_Icc (n : ℤ) {x : ℝ} (hx : x ∈ Set.Icc (-1) 1) :
(T ℝ n).eval x ∈ Set.Icc (-1) 1 := by
rw [← cos_arccos (x := x) (by grind) (by grind)]
grind [T_real_cos, cos_mem_Icc]
theorem abs_eval_T_real_le_one (n : ℤ) {x : ℝ} (hx : |x| ≤ 1) :
|(T ℝ n).eval x| ≤ 1 := by
#adaptation_note /-- Before nightly-2026-04-07, this was just
`grind [eval_T_real_mem_Icc]`. `grind`'s e-matching now keeps the
`Polynomial.eval` produced by the lemma (which uses `instCommSemiring.toSemiring`)
and the `Polynomial.eval` propagated by abs unfolding (which uses `Real.semiring`)
as distinct atoms, even though they are `rfl`-equal, so the contradiction is
never found. -/
have h := eval_T_real_mem_Icc n (Set.mem_Icc.mpr (abs_le.mp hx))
exact abs_le.mpr (Set.mem_Icc.mp h)
theorem one_le_eval_T_real (n : ℤ) {x : ℝ} (hx : 1 ≤ x) : 1 ≤ (T ℝ n).eval x := by
rw [← cosh_arcosh hx]
grind [T_real_cosh, one_le_cosh]
theorem one_lt_eval_T_real {n : ℤ} (hn : n ≠ 0) {x : ℝ} (hx : 1 < x) :
1 < (T ℝ n).eval x := by
have : arcosh x ≠ 0 := by grind [cosh_arcosh, cosh_zero]
rw [← cosh_arcosh (le_of_lt hx), T_real_cosh, one_lt_cosh, mul_ne_zero_iff]
exact ⟨by norm_cast, by assumption⟩
theorem one_le_negOnePow_mul_eval_T_real (n : ℤ) {x : ℝ} (hx : x ≤ -1) :
1 ≤ n.negOnePow * (T ℝ n).eval x := by
rw [← neg_neg x, T_eval_neg]
convert one_le_eval_T_real n (le_neg_of_le_neg hx)
rw [Int.cast_negOnePow, ← mul_assoc, ← mul_zpow]
simp
theorem one_lt_negOnePow_mul_eval_T_real {n : ℤ} (hn : n ≠ 0) {x : ℝ} (hx : x < -1) :
1 < n.negOnePow * (T ℝ n).eval x := by
rw [← neg_neg x, T_eval_neg]
convert one_lt_eval_T_real hn (lt_neg_of_lt_neg hx)
rw [Int.cast_negOnePow, ← mul_assoc, ← mul_zpow]
simp
theorem one_le_abs_eval_T_real (n : ℤ) {x : ℝ} (hx : 1 ≤ |x|) :
1 ≤ |(T ℝ n).eval x| := by
wlog! h : 0 ≤ x
· simpa [T_eval_neg, abs_mul, abs_unit_intCast] using @this n (-x) (by grind) (by grind)
· exact one_le_eval_T_real n (abs_of_nonneg h ▸ hx) |>.trans <| le_abs_self _
theorem one_lt_abs_eval_T_real {n : ℤ} (hn : n ≠ 0) {x : ℝ} (hx : 1 < |x|) :
1 < |(T ℝ n).eval x| := by
wlog! h : 0 ≤ x
· simpa [T_eval_neg, abs_mul, abs_unit_intCast] using @this n hn (-x) (by grind) (by grind)
· exact one_lt_eval_T_real hn (abs_of_nonneg h ▸ hx) |>.trans_le <| le_abs_self _
theorem abs_eval_T_real_le_one_iff {n : ℤ} (hn : n ≠ 0) (x : ℝ) :
|x| ≤ 1 ↔ |(T ℝ n).eval x| ≤ 1 :=
⟨abs_eval_T_real_le_one n, by simpa using mt <| one_lt_abs_eval_T_real hn⟩
theorem abs_eval_T_real_eq_one_iff {n : ℕ} (hn : n ≠ 0) (x : ℝ) :
|(T ℝ n).eval x| = 1 ↔ ∃ k ≤ n, x = cos (k * π / n) := by
constructor
· intro hTx
have hx := (abs_eval_T_real_le_one_iff (Nat.cast_ne_zero.mpr hn) x).mpr (le_of_eq hTx)
rw [← cos_arccos (neg_le_of_abs_le hx) (le_of_max_le_left hx), T_real_cos,
Int.cast_natCast, abs_cos_eq_one_iff] at hTx
obtain ⟨k, hk⟩ := hTx
have hk' : k = n * (arccos x / π) := by simpa [field]
lift k to ℕ using (by rw [← Int.cast_nonneg_iff (R := ℝ), hk']; positivity [arccos_nonneg x])
simp only [Int.cast_natCast] at hk hk'
have hkn : (k : ℝ) ≤ n := by
rw [← mul_one (n : ℝ), hk']
gcongr
exact div_le_one_of_le₀ (arccos_le_pi x) (by positivity)
refine ⟨k, by simpa using hkn, ?_⟩
convert congr(cos ($hk.symm / n))
rw [mul_div_cancel_left₀ _ (by simpa), cos_arccos (by grind) (by grind)]
· rintro ⟨k, hk, rfl⟩
rw [T_real_cos, abs_cos_eq_one_iff]
exact ⟨k, by simp [field]⟩
theorem eval_T_real_cos_int_mul_pi_div {k : ℕ} {n : ℕ} (hn : n ≠ 0) :
(T ℝ n).eval (cos (k * π / n)) = (k : ℤ).negOnePow := by
rw [T_real_cos, Int.cast_negOnePow]
convert Real.cos_int_mul_pi k using 2
simp [field]
theorem eval_T_real_eq_one_iff {n : ℕ} (hn : n ≠ 0) (x : ℝ) :
(T ℝ n).eval x = 1 ↔ ∃ k ≤ n, Even k ∧ x = cos (k * π / n) := by
constructor
· intro hx
obtain ⟨k, hk₁, hk₂⟩ := (abs_eval_T_real_eq_one_iff hn x).mp
((abs_eq_abs.mpr (.inl hx)).trans abs_one)
use k
refine ⟨hk₁, ?_, hk₂⟩
rw [hk₂, eval_T_real_cos_int_mul_pi_div hn, Int.cast_negOnePow_natCast] at hx
exact (neg_one_pow_eq_one_iff_even (by grind)).mp hx
· rintro ⟨k, hk₁, hk₂, hx⟩
rw [hx, eval_T_real_cos_int_mul_pi_div hn, Int.negOnePow_even k ((Int.even_coe_nat k).mpr hk₂)]
norm_cast
theorem eval_T_real_eq_neg_one_iff {n : ℕ} (hn : n ≠ 0) (x : ℝ) :
(T ℝ n).eval x = -1 ↔ ∃ k ≤ n, Odd k ∧ x = cos (k * π / n) := by
constructor
· intro hx
obtain ⟨k, hk₁, hk₂⟩ := (abs_eval_T_real_eq_one_iff hn x).mp
((abs_eq_abs.mpr (.inl hx)).trans ((abs_neg 1).trans abs_one))
use k
refine ⟨hk₁, ?_, hk₂⟩
rw [hk₂, eval_T_real_cos_int_mul_pi_div hn, Int.cast_negOnePow_natCast] at hx
exact (neg_one_pow_eq_neg_one_iff_odd (by grind)).mp hx
· rintro ⟨k, hk₁, hk₂, hx⟩
rw [hx, eval_T_real_cos_int_mul_pi_div hn, Int.negOnePow_odd k ((Int.odd_coe_nat k).mpr hk₂)]
norm_cast
theorem roots_T_real_nodup (n : ℕ) :
(Multiset.map (fun k : ℕ ↦ cos ((2 * k + 1) * π / (2 * n))) (.range n)).Nodup := by
wlog! hn : n ≠ 0
· simp [hn]
refine (Finset.range n).nodup_map_iff_injOn.mpr ?_
refine injOn_cos.comp (by aesop) fun k hk => Set.mem_Icc.mpr ⟨by positivity, ?_⟩
field_simp
norm_cast
grind
theorem roots_T_real (n : ℕ) :
(T ℝ n).roots =
((Finset.range n).image (fun (k : ℕ) => cos ((2 * k + 1) * π / (2 * n)))).val := by
wlog! hn : n ≠ 0
· simp [hn]
refine roots_eq_of_degree_eq_card (fun x hx ↦ ?_) ?_
· obtain ⟨k, hk, hx⟩ := Finset.mem_image.mp hx
rw [← hx, T_real_cos, cos_eq_zero_iff]
use k
field_simp
norm_cast
· rw [Finset.card_image_of_injOn, Finset.card_range, degree_T, Int.natAbs_natCast]
exact (Finset.range n).nodup_map_iff_injOn.mp (roots_T_real_nodup n)
theorem rootMultiplicity_T_real {n k : ℕ} (hk : k < n) :
(T ℝ n).rootMultiplicity (cos ((2 * k + 1) * π / (2 * n))) = 1 := by
rw [← count_roots, roots_T_real, Multiset.count_eq_one_of_mem (by simp)]
grind
theorem roots_U_real_nodup (n : ℕ) :
(Multiset.map (fun k : ℕ ↦ cos ((k + 1) * π / (n + 1))) (.range n)).Nodup := by
refine (Finset.range n).nodup_map_iff_injOn.mpr ?_
apply injOn_cos.comp
· intro x hx y hy hxy
field_simp at hxy
aesop
· refine fun k hk => Set.mem_Icc.mpr ⟨by positivity, ?_⟩
field_simp
norm_cast
grind
theorem roots_U_real (n : ℕ) :
(U ℝ n).roots =
((Finset.range n).image (fun (k : ℕ) => cos ((k + 1) * π / (n + 1)))).val := by
wlog! hn : n ≠ 0
· simp [hn]
refine roots_eq_of_degree_eq_card (fun x hx ↦ ?_) ?_
· obtain ⟨k, hk, hx⟩ := Finset.mem_image.mp hx
suffices (U ℝ n).eval x * sin ((k + 1) * π / (n + 1)) = 0 by
refine (mul_eq_zero_iff_right (ne_of_gt (sin_pos_of_pos_of_lt_pi (by positivity) ?_))).mp this
field_simp
norm_cast
grind
rw [← hx, U_real_cos, sin_eq_zero_iff]
use k + 1
field_simp
norm_cast
ring
· rw [Finset.card_image_of_injOn, Finset.card_range, degree_U_natCast]
exact (Finset.range n).nodup_map_iff_injOn.mp (roots_U_real_nodup n)
theorem rootMultiplicity_U_real {n k : ℕ} (hk : k < n) :
(U ℝ n).rootMultiplicity (cos ((k + 1) * π / (n + 1))) = 1 := by
rw [← count_roots, roots_U_real, Multiset.count_eq_one_of_mem (by simp)]
grind
theorem isLocalMax_T_real {n k : ℕ} (hn : n ≠ 0) (hk₀ : 0 < k) (hk₁ : k < n) (hk₂ : Even k) :
IsLocalMax (T ℝ n).eval (cos (k * π / n)) := by
have zero_lt : 0 < k * π / n := by positivity
have lt_pi : k * π / n < π := calc
k * π / n < n * π / n := by gcongr
_ = π := mul_div_cancel_left₀ _ (Nat.cast_ne_zero.mpr hn)
refine eventually_nhds_iff.mpr ⟨Set.Ioo (-1) 1, ?_, isOpen_Ioo, ?_, ?_⟩
· intro x hx
dsimp
rw [(eval_T_real_eq_one_iff hn _).mpr ⟨k, le_of_lt hk₁, hk₂, rfl⟩]
exact (abs_le.mp (abs_eval_T_real_le_one n (by grind))).2
· rw [← cos_pi]
exact cos_lt_cos_of_nonneg_of_le_pi (le_of_lt zero_lt) (le_refl π) lt_pi
· rw [← cos_zero]
exact cos_lt_cos_of_nonneg_of_le_pi (le_refl 0) (le_of_lt lt_pi) zero_lt
theorem isLocalMin_T_real {n k : ℕ} (hn : n ≠ 0) (hk₁ : k < n) (hk₂ : Odd k) :
IsLocalMin (T ℝ n).eval (cos (k * π / n)) := by
have k_pos : 0 < k := hk₂.pos
have zero_lt : 0 < k * π / n := by positivity
have lt_pi : k * π / n < π := calc
k * π / n < n * π / n := by gcongr
_ = π := mul_div_cancel_left₀ _ (Nat.cast_ne_zero.mpr hn)
refine eventually_nhds_iff.mpr ⟨Set.Ioo (-1) 1, ?_, isOpen_Ioo, ?_, ?_⟩
· intro x hx
dsimp
rw [(eval_T_real_eq_neg_one_iff hn _).mpr ⟨k, le_of_lt hk₁, hk₂, rfl⟩]
refine (abs_le.mp (abs_eval_T_real_le_one n (by grind))).1
· rw [← cos_pi]
exact cos_lt_cos_of_nonneg_of_le_pi (le_of_lt zero_lt) (le_refl π) lt_pi
· rw [← cos_zero]
exact cos_lt_cos_of_nonneg_of_le_pi (le_refl 0) (le_of_lt lt_pi) zero_lt
theorem isLocalExtr_T_real {n k : ℕ} (hn : n ≠ 0) (hk₀ : 0 < k) (hk₁ : k < n) :
IsLocalExtr (T ℝ n).eval (cos (k * π / n)) := by
cases k.even_or_odd
case inl hk₂ => exact .inr (isLocalMax_T_real hn hk₀ hk₁ hk₂)
case inr hk₂ => exact .inl (isLocalMin_T_real hn hk₁ hk₂)
theorem isLocalExtr_T_real_iff {n : ℕ} (hn : 2 ≤ n) (x : ℝ) :
IsLocalExtr (T ℝ n).eval x ↔ ∃ k ∈ Finset.Ioo 0 n, x = cos (k * π / n) := by
constructor
· intro hx
replace hx := hx.deriv_eq_zero
rw [Polynomial.deriv, T_derivative_eq_U, eval_mul, Int.cast_natCast, eval_natCast,
mul_eq_zero_iff_left (by aesop)] at hx
replace hx : x ∈ (U ℝ (n - 1)).roots :=
(mem_roots (degree_ne_bot.mp (ne_of_eq_of_ne (by grind [degree_U_natCast])
(WithBot.natCast_ne_bot (n - 1))))).mpr hx
rw [show (n - 1 : ℤ) = (n - 1 : ℕ) by grind, roots_U_real, Finset.mem_val] at hx
obtain ⟨k, hk₁, hx⟩ := Finset.mem_image.mp hx
refine ⟨k + 1, Finset.mem_Ioo.mpr ⟨k.zero_lt_succ, by grind⟩, ?_⟩
rw [← hx]
congr <;> norm_cast
exact Nat.sub_add_cancel (Nat.one_le_of_lt hn)
· rintro ⟨k, hk, hx⟩
rw [hx]
exact isLocalExtr_T_real (Nat.ne_zero_of_lt hn)
(Finset.mem_Ioo.mp hk).1 (Finset.mem_Ioo.mp hk).2
theorem isMaxOn_T_real {n k : ℕ} (hn : n ≠ 0) (hk₁ : k ≤ n) (hk₂ : Even k) :
IsMaxOn (T ℝ n).eval (Set.Icc (-1) 1) (cos (k * π / n)) :=
isMaxOn_iff.mpr (fun x hx => le_of_le_of_eq (abs_le.mp (abs_eval_T_real_le_one n (by grind))).2
((eval_T_real_eq_one_iff hn _).mpr ⟨k, hk₁, hk₂, rfl⟩).symm)
theorem isMinOn_T_real {n k : ℕ} (hn : n ≠ 0) (hk₁ : k ≤ n) (hk₂ : Odd k) :
IsMinOn (T ℝ n).eval (Set.Icc (-1) 1) (cos (k * π / n)) :=
isMinOn_iff.mpr (fun x hx => le_of_eq_of_le
((eval_T_real_eq_neg_one_iff hn _).mpr ⟨k, hk₁, hk₂, rfl⟩)
(abs_le.mp (abs_eval_T_real_le_one n (by grind))).1)
theorem isExtrOn_T_real {n k : ℕ} (hn : n ≠ 0) (hk : k ≤ n) :
IsExtrOn (T ℝ n).eval (Set.Icc (-1) 1) (cos (k * π / n)) := by
cases k.even_or_odd
case inl hk₂ => exact .inr (isMaxOn_T_real hn hk hk₂)
case inr hk₂ => exact .inl (isMinOn_T_real hn hk hk₂)
theorem isExtrOn_T_real_iff {n : ℕ} (hn : n ≠ 0) {x : ℝ} (hx : x ∈ Set.Icc (-1) 1) :
IsExtrOn (T ℝ n).eval (Set.Icc (-1) 1) x ↔
∃ k ≤ n, x = cos (k * π / n) := by
constructor
· intro h
apply (abs_eval_T_real_eq_one_iff hn x).mp
apply eq_of_le_of_ge (abs_eval_T_real_le_one n (by grind))
refine h.elim (fun h => ?_) (fun h => ?_)
· refine le_abs.mpr (.inr (le_neg_of_le_neg ?_))
have := isMinOn_iff.mp h (cos (1 * π / n)) (by grind [abs_cos_le_one])
rw [(eval_T_real_eq_neg_one_iff hn (cos (1 * π / n))).mpr ⟨1, Nat.one_le_iff_ne_zero.mpr hn,
by simp⟩] at this
assumption
· refine le_abs.mpr (.inl ?_)
have := isMaxOn_iff.mp h (cos (0 * π / n)) (by simp)
rw [(eval_T_real_eq_one_iff hn _).mpr ⟨0, by simp, by simp⟩] at this
assumption
· rintro ⟨k, hk, hx⟩
rw [hx]
exact isExtrOn_T_real hn hk
theorem irrational_of_isRoot_T_real {n : ℕ} {x : ℝ} (hroot : (T ℝ n).IsRoot x) (hnz : x ≠ 0) :
Irrational x := by
rw [← mem_roots (T_ne_zero ℝ n), roots_T_real, Finset.mem_val] at hroot
obtain ⟨k, hk₁, hk₂⟩ := Finset.mem_image.mp hroot
have hn : n ≠ 0 := by grind
suffices Irrational (cos ((Rat.divInt (2 * k + 1) (2 * n)) * π)) by
rw [← hk₂]; convert this using 2; push_cast; field_simp
apply irrational_cos_rat_mul_pi
contrapose! hnz
have : (Rat.divInt (2 * k + 1) (2 * n)).den = 2 * (n / n.gcd (2 * k + 1)) := calc
_ = 2 * n / (2 * n).gcd (2 * k + 1) := by rw [Rat.den_divInt]; norm_cast; simp [hn]
_ = _ := by rw [Nat.Coprime.gcd_mul_left_cancel n (by simp),
Nat.mul_div_assoc _ (Nat.gcd_dvd_left ..)]
have hn : 2 * k + 1 = n := Nat.eq_of_dvd_of_lt_two_mul (by simp) (Nat.gcd_eq_left_iff_dvd.mp <|
Nat.eq_of_dvd_of_div_eq_one (Nat.gcd_dvd_left ..) (by grind [Rat.den_pos])) (by grind)
rw_mod_cast [← hk₂, hn]; convert cos_pi_div_two using 2; push_cast; field_simp
theorem abs_iterate_derivative_T_real_le (n : ℤ) (k : ℕ) {x : ℝ} (hx : |x| ≤ 1) :
|(derivative^[k] (T ℝ n)).eval x| ≤ (derivative^[k] (T ℝ n)).eval 1 := by
wlog hn : 0 ≤ n
· convert this (-n) k hx (by grind) using 1 <;> rw [T_neg]
lift n to ℕ using hn
have := T_iterate_derivative_mem_span_T (R := ℝ) n k
obtain ⟨f, hfsupp, hfderiv⟩ := Submodule.mem_span_set.mp this
replace hfderiv : ∑ p ∈ f.support, f p • p = derivative^[k] (T ℝ n) := by rw [← hfderiv]; rfl
have hf (y : ℝ) :
∑ p ∈ f.support, f p • p.eval y = (derivative^[k] (T ℝ n)).eval y := by
rw [← hfderiv, Polynomial.eval_finset_sum]
simp_rw [Polynomial.eval_smul]
rw [← hf x, ← hf 1]
grw [Finset.abs_sum_le_sum_abs]
refine Finset.sum_le_sum (fun i hi => ?_)
obtain ⟨m, hm, hi⟩ := (Set.mem_image ..).mp (hfsupp hi)
grw [abs_nsmul, ← hi, abs_eval_T_real_le_one m hx]
simp
end Polynomial.Chebyshev