|
| 1 | +/- |
| 2 | +Copyright (c) 2025 Yuval Filmus. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Yuval Filmus |
| 5 | +-/ |
| 6 | +module |
| 7 | + |
| 8 | +public import Mathlib.RingTheory.Polynomial.Chebyshev |
| 9 | +public import Mathlib.Data.Real.Basic |
| 10 | +public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic |
| 11 | +public import Mathlib.Algebra.Polynomial.Roots |
| 12 | +import Mathlib.Analysis.Calculus.Deriv.Polynomial |
| 13 | +import Mathlib.Analysis.SpecialFunctions.Arcosh |
| 14 | +import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.Basic |
| 15 | +import Mathlib.Analysis.SpecialFunctions.Trigonometric.Complex |
| 16 | + |
| 17 | +/-! |
| 18 | +# Chebyshev polynomials over the reals: roots and extrema |
| 19 | +
|
| 20 | +## Main statements |
| 21 | +
|
| 22 | +* T_n(x) ∈ [-1, 1] iff x ∈ [-1, 1]: `abs_eval_T_real_le_one_iff` |
| 23 | +* Zeroes of T and U: `roots_T_real`, `roots_U_real` |
| 24 | +* Local extrema of T: `isLocalExtr_T_real_iff`, `isExtrOn_T_real_iff` |
| 25 | +-/ |
| 26 | + |
| 27 | +@[expose] public section |
| 28 | + |
| 29 | +namespace Polynomial.Chebyshev |
| 30 | + |
| 31 | +open Real |
| 32 | + |
| 33 | +theorem eval_T_real_mem_Icc (n : ℤ) {x : ℝ} (hx : x ∈ Set.Icc (-1) 1) : |
| 34 | + (T ℝ n).eval x ∈ Set.Icc (-1) 1 := by |
| 35 | + rw [← cos_arccos (x := x) (by grind) (by grind)] |
| 36 | + grind [T_real_cos, cos_mem_Icc] |
| 37 | + |
| 38 | +theorem abs_eval_T_real_le_one (n : ℤ) {x : ℝ} (hx : |x| ≤ 1) : |
| 39 | + |(T ℝ n).eval x| ≤ 1 := by grind [eval_T_real_mem_Icc] |
| 40 | + |
| 41 | +theorem one_le_eval_T_real (n : ℤ) {x : ℝ} (hx : 1 ≤ x) : 1 ≤ (T ℝ n).eval x := by |
| 42 | + rw [← cosh_arcosh hx] |
| 43 | + grind [T_real_cosh, one_le_cosh] |
| 44 | + |
| 45 | +theorem one_lt_eval_T_real {n : ℤ} (hn : n ≠ 0) {x : ℝ} (hx : 1 < x) : |
| 46 | + 1 < (T ℝ n).eval x := by |
| 47 | + have : arcosh x ≠ 0 := by |
| 48 | + by_contra! h |
| 49 | + grind [cosh_arcosh, cosh_zero] |
| 50 | + rw [←cosh_arcosh (le_of_lt hx), T_real_cosh, one_lt_cosh, mul_ne_zero_iff] |
| 51 | + exact ⟨by norm_cast, by assumption⟩ |
| 52 | + |
| 53 | +theorem one_le_negOnePow_mul_eval_T_real (n : ℤ) {x : ℝ} (hx : x ≤ -1) : |
| 54 | + 1 ≤ n.negOnePow * (T ℝ n).eval x := by |
| 55 | + rw [← neg_neg x, T_eval_neg] |
| 56 | + convert one_le_eval_T_real n (le_neg_of_le_neg hx) |
| 57 | + rw [Int.cast_negOnePow, ← mul_assoc, ← mul_zpow] |
| 58 | + simp |
| 59 | + |
| 60 | +theorem one_lt_negOnePow_mul_eval_T_real {n : ℤ} (hn : n ≠ 0) {x : ℝ} (hx : x < -1) : |
| 61 | + 1 < n.negOnePow * (T ℝ n).eval x := by |
| 62 | + rw [← neg_neg x, T_eval_neg] |
| 63 | + convert one_lt_eval_T_real hn (lt_neg_of_lt_neg hx) |
| 64 | + rw [Int.cast_negOnePow, ← mul_assoc, ← mul_zpow] |
| 65 | + simp |
| 66 | + |
| 67 | +theorem one_le_abs_eval_T_real (n : ℤ) {x : ℝ} (hx : 1 ≤ |x|) : |
| 68 | + 1 ≤ |(T ℝ n).eval x| := by |
| 69 | + wlog! h : 0 ≤ x |
| 70 | + · simpa [T_eval_neg, abs_mul, abs_unit_intCast] using @this n (-x) (by grind) (by grind) |
| 71 | + · exact one_le_eval_T_real n (abs_of_nonneg h ▸ hx) |>.trans <| le_abs_self _ |
| 72 | + |
| 73 | +theorem one_lt_abs_eval_T_real {n : ℤ} (hn : n ≠ 0) {x : ℝ} (hx : 1 < |x|) : |
| 74 | + 1 < |(T ℝ n).eval x| := by |
| 75 | + wlog! h : 0 ≤ x |
| 76 | + · simpa [T_eval_neg, abs_mul, abs_unit_intCast] using @this n hn (-x) (by grind) (by grind) |
| 77 | + · exact one_lt_eval_T_real hn (abs_of_nonneg h ▸ hx) |>.trans_le <| le_abs_self _ |
| 78 | + |
| 79 | +theorem abs_eval_T_real_le_one_iff {n : ℤ} (hn : n ≠ 0) (x : ℝ) : |
| 80 | + |x| ≤ 1 ↔ |(T ℝ n).eval x| ≤ 1 := |
| 81 | + ⟨abs_eval_T_real_le_one n, by simpa using mt <| one_lt_abs_eval_T_real hn⟩ |
| 82 | + |
| 83 | +theorem abs_eval_T_real_eq_one_iff {n : ℕ} (hn : n ≠ 0) (x : ℝ) : |
| 84 | + |(T ℝ n).eval x| = 1 ↔ ∃ k ≤ n, x = cos (k * π / n) := by |
| 85 | + constructor |
| 86 | + · intro hTx |
| 87 | + have hx := (abs_eval_T_real_le_one_iff (Nat.cast_ne_zero.mpr hn) x).mpr (le_of_eq hTx) |
| 88 | + rw [← cos_arccos (neg_le_of_abs_le hx) (le_of_max_le_left hx), T_real_cos, |
| 89 | + Int.cast_natCast, abs_cos_eq_one_iff] at hTx |
| 90 | + obtain ⟨k, hk⟩ := hTx |
| 91 | + have hk' : k = n * (arccos x / π) := by simpa [field] |
| 92 | + lift k to ℕ using (by rw [← Int.cast_nonneg_iff (R := ℝ), hk']; positivity [arccos_nonneg x]) |
| 93 | + simp only [Int.cast_natCast] at hk hk' |
| 94 | + have hkn : (k : ℝ) ≤ n := by |
| 95 | + rw [← mul_one (n : ℝ), hk'] |
| 96 | + gcongr |
| 97 | + exact div_le_one_of_le₀ (arccos_le_pi x) (by positivity) |
| 98 | + refine ⟨k, by simpa using hkn, ?_⟩ |
| 99 | + convert congr(cos ($hk.symm / n)) |
| 100 | + rw [mul_div_cancel_left₀ _ (by simpa), cos_arccos (by grind) (by grind)] |
| 101 | + · rintro ⟨k, hk, rfl⟩ |
| 102 | + rw [T_real_cos, abs_cos_eq_one_iff] |
| 103 | + exact ⟨k, by simp [field]⟩ |
| 104 | + |
| 105 | +theorem eval_T_real_cos_int_mul_pi_div {k : ℕ} {n : ℕ} (hn : n ≠ 0) : |
| 106 | + (T ℝ n).eval (cos (k * π / n)) = (k : ℤ).negOnePow := by |
| 107 | + rw [T_real_cos, Int.cast_negOnePow] |
| 108 | + convert Real.cos_int_mul_pi k using 2 |
| 109 | + simp [field] |
| 110 | + |
| 111 | +theorem eval_T_real_eq_one_iff {n : ℕ} (hn : n ≠ 0) (x : ℝ) : |
| 112 | + (T ℝ n).eval x = 1 ↔ ∃ k ≤ n, Even k ∧ x = cos (k * π / n) := by |
| 113 | + constructor |
| 114 | + · intro hx |
| 115 | + obtain ⟨k, hk₁, hk₂⟩ := (abs_eval_T_real_eq_one_iff hn x).mp |
| 116 | + ((abs_eq_abs.mpr (.inl hx)).trans abs_one) |
| 117 | + use k |
| 118 | + refine ⟨hk₁, ?_, hk₂⟩ |
| 119 | + rw [hk₂, eval_T_real_cos_int_mul_pi_div hn, Int.cast_negOnePow_natCast] at hx |
| 120 | + exact (neg_one_pow_eq_one_iff_even (by grind)).mp hx |
| 121 | + · rintro ⟨k, hk₁, hk₂, hx⟩ |
| 122 | + rw [hx, eval_T_real_cos_int_mul_pi_div hn, Int.negOnePow_even k ((Int.even_coe_nat k).mpr hk₂)] |
| 123 | + norm_cast |
| 124 | + |
| 125 | +theorem eval_T_real_eq_neg_one_iff {n : ℕ} (hn : n ≠ 0) (x : ℝ) : |
| 126 | + (T ℝ n).eval x = -1 ↔ ∃ k ≤ n, Odd k ∧ x = cos (k * π / n) := by |
| 127 | + constructor |
| 128 | + · intro hx |
| 129 | + obtain ⟨k, hk₁, hk₂⟩ := (abs_eval_T_real_eq_one_iff hn x).mp |
| 130 | + ((abs_eq_abs.mpr (.inl hx)).trans ((abs_neg 1).trans abs_one)) |
| 131 | + use k |
| 132 | + refine ⟨hk₁, ?_, hk₂⟩ |
| 133 | + rw [hk₂, eval_T_real_cos_int_mul_pi_div hn, Int.cast_negOnePow_natCast] at hx |
| 134 | + exact (neg_one_pow_eq_neg_one_iff_odd (by grind)).mp hx |
| 135 | + · rintro ⟨k, hk₁, hk₂, hx⟩ |
| 136 | + rw [hx, eval_T_real_cos_int_mul_pi_div hn, Int.negOnePow_odd k ((Int.odd_coe_nat k).mpr hk₂)] |
| 137 | + norm_cast |
| 138 | + |
| 139 | +theorem roots_T_real_nodup (n : ℕ) : |
| 140 | + (Multiset.map (fun k : ℕ ↦ cos ((2 * k + 1) * π / (2 * n))) (.range n)).Nodup := by |
| 141 | + wlog! hn : n ≠ 0 |
| 142 | + · simp [hn] |
| 143 | + refine (Finset.range n).nodup_map_iff_injOn.mpr ?_ |
| 144 | + refine injOn_cos.comp (by aesop) fun k hk => Set.mem_Icc.mpr ⟨by positivity, ?_⟩ |
| 145 | + field_simp |
| 146 | + norm_cast |
| 147 | + grind |
| 148 | + |
| 149 | +theorem roots_T_real (n : ℕ) : |
| 150 | + (T ℝ n).roots = |
| 151 | + ((Finset.range n).image (fun (k : ℕ) => cos ((2 * k + 1) * π / (2 * n)))).val := by |
| 152 | + wlog! hn : n ≠ 0 |
| 153 | + · simp [hn] |
| 154 | + refine roots_eq_of_degree_eq_card (fun x hx ↦ ?_) ?_ |
| 155 | + · obtain ⟨k, hk, hx⟩ := Finset.mem_image.mp hx |
| 156 | + rw [← hx, T_real_cos, cos_eq_zero_iff] |
| 157 | + use k |
| 158 | + field_simp |
| 159 | + norm_cast |
| 160 | + · rw [Finset.card_image_of_injOn, Finset.card_range, degree_T, Int.natAbs_natCast] |
| 161 | + exact (Finset.range n).nodup_map_iff_injOn.mp (roots_T_real_nodup n) |
| 162 | + |
| 163 | +theorem rootMultiplicity_T_real {n k : ℕ} (hk : k < n) : |
| 164 | + (T ℝ n).rootMultiplicity (cos ((2 * k + 1) * π / (2 * n))) = 1 := by |
| 165 | + rw [← count_roots, roots_T_real, Multiset.count_eq_one_of_mem (by simp)] |
| 166 | + grind |
| 167 | + |
| 168 | +theorem roots_U_real_nodup (n : ℕ) : |
| 169 | + (Multiset.map (fun k : ℕ ↦ cos ((k + 1) * π / (n + 1))) (.range n)).Nodup := by |
| 170 | + refine (Finset.range n).nodup_map_iff_injOn.mpr ?_ |
| 171 | + apply injOn_cos.comp |
| 172 | + · intro x hx y hy hxy |
| 173 | + field_simp at hxy |
| 174 | + aesop |
| 175 | + · refine fun k hk => Set.mem_Icc.mpr ⟨by positivity, ?_⟩ |
| 176 | + field_simp |
| 177 | + norm_cast |
| 178 | + grind |
| 179 | + |
| 180 | +theorem roots_U_real (n : ℕ) : |
| 181 | + (U ℝ n).roots = |
| 182 | + ((Finset.range n).image (fun (k : ℕ) => cos ((k + 1) * π / (n + 1)))).val := by |
| 183 | + wlog! hn : n ≠ 0 |
| 184 | + · simp [hn] |
| 185 | + refine roots_eq_of_degree_eq_card (fun x hx ↦ ?_) ?_ |
| 186 | + · obtain ⟨k, hk, hx⟩ := Finset.mem_image.mp hx |
| 187 | + suffices (U ℝ n).eval x * sin ((k + 1) * π / (n + 1)) = 0 by |
| 188 | + refine (mul_eq_zero_iff_right (ne_of_gt (sin_pos_of_pos_of_lt_pi (by positivity) ?_))).mp this |
| 189 | + field_simp |
| 190 | + norm_cast |
| 191 | + grind |
| 192 | + rw [← hx, U_real_cos, sin_eq_zero_iff] |
| 193 | + use k + 1 |
| 194 | + field_simp |
| 195 | + norm_cast |
| 196 | + ring |
| 197 | + · rw [Finset.card_image_of_injOn, Finset.card_range, degree_U_natCast] |
| 198 | + exact (Finset.range n).nodup_map_iff_injOn.mp (roots_U_real_nodup n) |
| 199 | + |
| 200 | +theorem rootMultiplicity_U_real {n k : ℕ} (hk : k < n) : |
| 201 | + (U ℝ n).rootMultiplicity (cos ((k + 1) * π / (n + 1))) = 1 := by |
| 202 | + rw [← count_roots, roots_U_real, Multiset.count_eq_one_of_mem (by simp)] |
| 203 | + grind |
| 204 | + |
| 205 | +theorem isLocalMax_T_real {n k : ℕ} (hn : n ≠ 0) (hk₀ : 0 < k) (hk₁ : k < n) (hk₂ : Even k) : |
| 206 | + IsLocalMax (T ℝ n).eval (cos (k * π / n)) := by |
| 207 | + have zero_lt : 0 < k * π / n := by positivity |
| 208 | + have lt_pi : k * π / n < π := calc |
| 209 | + k * π / n < n * π / n := by gcongr |
| 210 | + _ = π := mul_div_cancel_left₀ _ (Nat.cast_ne_zero.mpr hn) |
| 211 | + refine eventually_nhds_iff.mpr ⟨Set.Ioo (-1) 1, ?_, isOpen_Ioo, ?_, ?_⟩ |
| 212 | + · intro x hx |
| 213 | + dsimp |
| 214 | + rw [(eval_T_real_eq_one_iff hn _).mpr ⟨k, le_of_lt hk₁, hk₂, rfl⟩] |
| 215 | + exact (abs_le.mp (abs_eval_T_real_le_one n (by grind))).2 |
| 216 | + · rw [← cos_pi] |
| 217 | + exact cos_lt_cos_of_nonneg_of_le_pi (le_of_lt zero_lt) (le_refl π) lt_pi |
| 218 | + · rw [← cos_zero] |
| 219 | + exact cos_lt_cos_of_nonneg_of_le_pi (le_refl 0) (le_of_lt lt_pi) zero_lt |
| 220 | + |
| 221 | +theorem isLocalMin_T_real {n k : ℕ} (hn : n ≠ 0) (hk₁ : k < n) (hk₂ : Odd k) : |
| 222 | + IsLocalMin (T ℝ n).eval (cos (k * π / n)) := by |
| 223 | + have k_pos : 0 < k := hk₂.pos |
| 224 | + have zero_lt : 0 < k * π / n := by positivity |
| 225 | + have lt_pi : k * π / n < π := calc |
| 226 | + k * π / n < n * π / n := by gcongr |
| 227 | + _ = π := mul_div_cancel_left₀ _ (Nat.cast_ne_zero.mpr hn) |
| 228 | + refine eventually_nhds_iff.mpr ⟨Set.Ioo (-1) 1, ?_, isOpen_Ioo, ?_, ?_⟩ |
| 229 | + · intro x hx |
| 230 | + dsimp |
| 231 | + rw [(eval_T_real_eq_neg_one_iff hn _).mpr ⟨k, le_of_lt hk₁, hk₂, rfl⟩] |
| 232 | + refine (abs_le.mp (abs_eval_T_real_le_one n (by grind))).1 |
| 233 | + · rw [← cos_pi] |
| 234 | + exact cos_lt_cos_of_nonneg_of_le_pi (le_of_lt zero_lt) (le_refl π) lt_pi |
| 235 | + · rw [← cos_zero] |
| 236 | + exact cos_lt_cos_of_nonneg_of_le_pi (le_refl 0) (le_of_lt lt_pi) zero_lt |
| 237 | + |
| 238 | +theorem isLocalExtr_T_real {n k : ℕ} (hn : n ≠ 0) (hk₀ : 0 < k) (hk₁ : k < n) : |
| 239 | + IsLocalExtr (T ℝ n).eval (cos (k * π / n)) := by |
| 240 | + cases k.even_or_odd |
| 241 | + case inl hk₂ => exact .inr (isLocalMax_T_real hn hk₀ hk₁ hk₂) |
| 242 | + case inr hk₂ => exact .inl (isLocalMin_T_real hn hk₁ hk₂) |
| 243 | + |
| 244 | +theorem isLocalExtr_T_real_iff {n : ℕ} (hn : 2 ≤ n) (x : ℝ) : |
| 245 | + IsLocalExtr (T ℝ n).eval x ↔ ∃ k ∈ Finset.Ioo 0 n, x = cos (k * π / n) := by |
| 246 | + constructor |
| 247 | + · intro hx |
| 248 | + replace hx := hx.deriv_eq_zero |
| 249 | + rw [Polynomial.deriv, T_derivative_eq_U, eval_mul, Int.cast_natCast, eval_natCast, |
| 250 | + mul_eq_zero_iff_left (by aesop)] at hx |
| 251 | + replace hx : x ∈ (U ℝ (n - 1)).roots := |
| 252 | + (mem_roots (degree_ne_bot.mp (ne_of_eq_of_ne (by grind [degree_U_natCast]) |
| 253 | + (WithBot.natCast_ne_bot (n - 1))))).mpr hx |
| 254 | + rw [show (n - 1 : ℤ) = (n - 1 : ℕ) by grind, roots_U_real] at hx |
| 255 | + obtain ⟨k, hk₁, hx⟩ := Finset.mem_image.mp hx |
| 256 | + refine ⟨k + 1, Finset.mem_Ioo.mpr ⟨k.zero_lt_succ, by grind⟩, ?_⟩ |
| 257 | + rw [← hx] |
| 258 | + congr <;> norm_cast |
| 259 | + exact Nat.sub_add_cancel (Nat.one_le_of_lt hn) |
| 260 | + · rintro ⟨k, hk, hx⟩ |
| 261 | + rw [hx] |
| 262 | + exact isLocalExtr_T_real (Nat.ne_zero_of_lt hn) |
| 263 | + (Finset.mem_Ioo.mp hk).1 (Finset.mem_Ioo.mp hk).2 |
| 264 | + |
| 265 | +theorem isMaxOn_T_real {n k : ℕ} (hn : n ≠ 0) (hk₁ : k ≤ n) (hk₂ : Even k) : |
| 266 | + IsMaxOn (T ℝ n).eval (Set.Icc (-1) 1) (cos (k * π / n)) := |
| 267 | + isMaxOn_iff.mpr (fun x hx => le_of_le_of_eq (abs_le.mp (abs_eval_T_real_le_one n (by grind))).2 |
| 268 | + ((eval_T_real_eq_one_iff hn _).mpr ⟨k, hk₁, hk₂, rfl⟩).symm) |
| 269 | + |
| 270 | +theorem isMinOn_T_real {n k : ℕ} (hn : n ≠ 0) (hk₁ : k ≤ n) (hk₂ : Odd k) : |
| 271 | + IsMinOn (T ℝ n).eval (Set.Icc (-1) 1) (cos (k * π / n)) := |
| 272 | + isMinOn_iff.mpr (fun x hx => le_of_eq_of_le |
| 273 | + ((eval_T_real_eq_neg_one_iff hn _).mpr ⟨k, hk₁, hk₂, rfl⟩) |
| 274 | + (abs_le.mp (abs_eval_T_real_le_one n (by grind))).1) |
| 275 | + |
| 276 | +theorem isExtrOn_T_real {n k : ℕ} (hn : n ≠ 0) (hk : k ≤ n) : |
| 277 | + IsExtrOn (T ℝ n).eval (Set.Icc (-1) 1) (cos (k * π / n)) := by |
| 278 | + cases k.even_or_odd |
| 279 | + case inl hk₂ => exact .inr (isMaxOn_T_real hn hk hk₂) |
| 280 | + case inr hk₂ => exact .inl (isMinOn_T_real hn hk hk₂) |
| 281 | + |
| 282 | +theorem isExtrOn_T_real_iff {n : ℕ} (hn : n ≠ 0) {x : ℝ} (hx : x ∈ Set.Icc (-1) 1) : |
| 283 | + IsExtrOn (T ℝ n).eval (Set.Icc (-1) 1) x ↔ |
| 284 | + ∃ k ≤ n, x = cos (k * π / n) := by |
| 285 | + constructor |
| 286 | + · intro h |
| 287 | + apply (abs_eval_T_real_eq_one_iff hn x).mp |
| 288 | + apply eq_of_le_of_ge (abs_eval_T_real_le_one n (by grind)) |
| 289 | + refine h.elim (fun h => ?_) (fun h => ?_) |
| 290 | + · refine le_abs.mpr (.inr (le_neg_of_le_neg ?_)) |
| 291 | + have := isMinOn_iff.mp h (cos (1 * π / n)) (by grind [abs_cos_le_one]) |
| 292 | + rw [(eval_T_real_eq_neg_one_iff hn (cos (1 * π / n))).mpr ⟨1, Nat.one_le_iff_ne_zero.mpr hn, |
| 293 | + by simp⟩] at this |
| 294 | + assumption |
| 295 | + · refine le_abs.mpr (.inl ?_) |
| 296 | + have := isMaxOn_iff.mp h (cos (0 * π / n)) (by simp) |
| 297 | + rw [(eval_T_real_eq_one_iff hn _).mpr ⟨0, by simp, by simp⟩] at this |
| 298 | + assumption |
| 299 | + · rintro ⟨k, hk, hx⟩ |
| 300 | + rw [hx] |
| 301 | + exact isExtrOn_T_real hn hk |
| 302 | + |
| 303 | +end Polynomial.Chebyshev |
0 commit comments