Skip to content

Commit 6dc6d70

Browse files
committed
feat(Trigonometric/Chebyshev/RootsExtrema): compute roots and extrema of Chebyshev polynomials (leanprover-community#32970)
We compute the roots of the Chebyshev T and U polynomials over the reals, as well as the extremal points of Chebyshev T on the interval [-1, 1]. In addition, we determine when Chebyshev T is bounded by 1 in absolute value.
1 parent efb5cfc commit 6dc6d70

3 files changed

Lines changed: 328 additions & 0 deletions

File tree

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2167,6 +2167,7 @@ public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic
21672167
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Bounds
21682168
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev
21692169
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.Basic
2170+
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.RootsExtrema
21702171
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Complex
21712172
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.ComplexDeriv
21722173
public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Cotangent
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
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

Mathlib/Analysis/SpecialFunctions/Trigonometric/Complex.lean

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,36 @@ theorem sin_eq_sin_iff {x y : ℝ} :
275275
theorem cos_eq_neg_one_iff {x : ℝ} : cos x = -1 ↔ ∃ k : ℤ, π + k * (2 * π) = x :=
276276
mod_cast @Complex.cos_eq_neg_one_iff x
277277

278+
lemma abs_cos_eq_one_iff {x : ℝ} :
279+
|cos x| = 1 ↔ ∃ k : ℤ, k * π = x := by
280+
rw [← abs_one, abs_eq_abs, cos_eq_one_iff, cos_eq_neg_one_iff]
281+
constructor
282+
· rintro (⟨n, h⟩ | ⟨n, h⟩)
283+
· exact ⟨2 * n, by grind⟩
284+
· exact ⟨1 + n * 2, by grind⟩
285+
· rintro (⟨n, h⟩)
286+
obtain (⟨n, rfl⟩ | ⟨n, rfl⟩) := n.even_or_odd
287+
· exact .inl ⟨n, by grind⟩
288+
· exact .inr ⟨n, by grind⟩
289+
278290
theorem sin_eq_one_iff {x : ℝ} : sin x = 1 ↔ ∃ k : ℤ, π / 2 + k * (2 * π) = x :=
279291
mod_cast @Complex.sin_eq_one_iff x
280292

281293
theorem sin_eq_neg_one_iff {x : ℝ} : sin x = -1 ↔ ∃ k : ℤ, -(π / 2) + k * (2 * π) = x :=
282294
mod_cast @Complex.sin_eq_neg_one_iff x
283295

296+
lemma abs_sin_eq_one_iff {x : ℝ} :
297+
|sin x| = 1 ↔ ∃ k : ℤ, π / 2 + k * π = x := by
298+
rw [← abs_one, abs_eq_abs, sin_eq_one_iff, sin_eq_neg_one_iff]
299+
constructor
300+
· rintro (⟨n, h⟩ | ⟨n, h⟩)
301+
· exact ⟨2 * n, by grind⟩
302+
· exact ⟨-1 + n * 2, by grind⟩
303+
· rintro (⟨n, h⟩)
304+
obtain (⟨n, rfl⟩ | ⟨n, rfl⟩) := n.even_or_odd
305+
· exact .inl ⟨n, by grind⟩
306+
· exact .inr ⟨n + 1, by grind⟩
307+
284308
theorem tan_eq_zero_iff {θ : ℝ} : tan θ = 0 ↔ ∃ k : ℤ, k * π / 2 = θ :=
285309
mod_cast @Complex.tan_eq_zero_iff θ
286310

0 commit comments

Comments
 (0)