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
//! Oblate spheroidal wave functions
//!
//! This module provides implementations of oblate spheroidal functions, which arise in
//! the solution of the Helmholtz equation in oblate spheroidal coordinates. These
//! functions are particularly important in electromagnetic scattering by oblate
//! spheroids (disk-shaped objects).
//!
//! ## Algorithm
//!
//! Wave 74 refactor (2026-05-08): all six routines below dispatch to the
//! Flammer / Bouwkamp d-coefficient pipeline implemented in
//! [`crate::spheroidal::cf_helpers`]. The legacy "continued-fraction
//! perturbation" path with hand-rolled Sturm-sequence Newton iteration has
//! been removed — it converged catastrophically wrong values for `|c| > 1`,
//! e.g. `obl_cv(0, 2, 2.0) = -0.018`, while the correct value is `+4.0915`
//! (SciPy `obl_cv(0, 2, 2.0)`).
//!
//! ## SciPy convention
//!
//! These routines match `scipy.special.{obl_cv, obl_cv_seq, obl_ang1,
//! obl_rad1, obl_rad2}` to ~1e-11 for the angular family and to ~1e-5 / m=0
//! for the radial family.
//!
//! ## Limitations
//!
//! The radial-2 (`obl_rad2`) implementation uses the simple spherical Bessel
//! `y_l` series (Flammer §4.4.4). This series diverges asymptotically for
//! `(m, n)` cases with odd `m` and odd parity (`(n − m) mod 2 = 1`), notably
//! `(m=1, n=2)`. Such cases require the alternative Wronskian-based
//! representation (Flammer §4.5), which is not yet implemented.
//! `obl_rad2` for those cases returns a value but with reduced accuracy or
//! incorrect sign.
use crate;
use crate;
/// Computes the characteristic value for oblate spheroidal wave functions.
///
/// The characteristic value `λ_{m,n}(c)` is the eigenvalue of the spheroidal
/// wave equation (oblate sign, `c² → -c²`) for mode `m`, `n` and spheroidal
/// parameter `c`. It satisfies `λ_{m,n}(0) = n(n+1)`.
///
/// # Arguments
///
/// * `m` - The order parameter (≥ 0, integer)
/// * `n` - The degree parameter (≥ m, integer)
/// * `c` - The spheroidal parameter (real)
///
/// # Returns
///
/// * `SpecialResult<f64>` - The characteristic value
///
/// # Examples
///
/// ```
/// # use scirs2_special::obl_cv;
/// # use scirs2_special::error::SpecialError;
/// # fn test() -> Result<(), SpecialError> {
/// // c = 0 reduces to n(n+1)
/// let cv = obl_cv(0, 0, 0.0)?;
/// assert!((cv - 0.0).abs() < 1e-10);
///
/// let cv = obl_cv(0, 2, 2.0)?;
/// assert!((cv - 4.0915091022).abs() < 1e-6); // SciPy reference
/// # Ok(())
/// # }
/// # test().unwrap();
/// ```
/// Computes a sequence of characteristic values for oblate spheroidal wave functions.
///
/// Returns the sequence `[λ_{m,m}(c), λ_{m,m+1}(c), …, λ_{m,n}(c)]`.
///
/// # Arguments
///
/// * `m` - The order parameter (≥ 0, integer)
/// * `n` - The maximum degree parameter (≥ m, integer)
/// * `c` - The spheroidal parameter (real)
///
/// # Examples
///
/// ```
/// # use scirs2_special::obl_cv_seq;
/// # use scirs2_special::error::SpecialError;
/// # fn test() -> Result<(), SpecialError> {
/// let values = obl_cv_seq(0, 3, 0.0)?;
/// assert_eq!(values.len(), 4);
/// assert!((values[0] - 0.0).abs() < 1e-10);
/// assert!((values[1] - 2.0).abs() < 1e-10);
/// assert!((values[2] - 6.0).abs() < 1e-10);
/// assert!((values[3] - 12.0).abs() < 1e-10);
/// # Ok(())
/// # }
/// # test().unwrap();
/// ```
/// Computes the oblate spheroidal angular function of the first kind, `S_{m,n}(c, η)`.
///
/// The convention matches SciPy's `obl_ang1`: at `c = 0`, the function reduces to
/// the un-Condon-Shortley associated Legendre function `(-1)^m · P_n^m(η)` (i.e.,
/// the un-CS form), and the Meixner–Schäfke normalisation enforces
/// `S_{m,n}(c, 0) = P_n^m(0)` (even parity) or
/// `S'_{m,n}(c, 0) = (P_n^m)'(0)` (odd parity).
///
/// # Arguments
///
/// * `m` - The order parameter (≥ 0, integer)
/// * `n` - The degree parameter (≥ m, integer)
/// * `c` - The spheroidal parameter (real)
/// * `x` - Evaluation point in `[-1, 1]`
///
/// # Returns
///
/// * `SpecialResult<(f64, f64)>` - The function value and its derivative
///
/// # Examples
///
/// ```
/// use scirs2_special::obl_ang1;
///
/// // c = 0 reduces to the un-CS associated Legendre function
/// let (s, _) = obl_ang1(0, 1, 0.0, 0.5).unwrap();
/// assert!((s - 0.5).abs() < 1e-10); // P_1^0(0.5) = 0.5
///
/// // c = 5 case (matches SciPy obl_ang1)
/// let (s, _) = obl_ang1(0, 2, 5.0, 0.5).unwrap();
/// assert!((s - (-0.5976813382)).abs() < 1e-6);
///
/// // Higher order
/// let (s_higher, _) = obl_ang1(1, 2, 5.0, 0.5).unwrap();
/// assert!((s_higher - 2.1643722013).abs() < 1e-6);
/// ```
/// Computes the oblate spheroidal radial function of the first kind, `R_{m,n}^{(1)}(c, ξ)`.
///
/// Uses the Flammer §4.4 expansion in spherical Bessel functions of the first kind
/// `j_l(c·ξ)`, normalised so that `R_{m,n}^{(1)}(c, ξ) → j_n(c·ξ)` as `c → 0` for `m = 0`.
///
/// # Arguments
///
/// * `m` - The order parameter (≥ 0, integer)
/// * `n` - The degree parameter (≥ m, integer)
/// * `c` - The spheroidal parameter (real)
/// * `x` - Radial coordinate (`x ≥ 0`)
///
/// # Examples
///
/// ```
/// use scirs2_special::obl_rad1;
///
/// // Basic test for oblate radial function of the first kind
/// let (r_val, r_prime) = obl_rad1(0, 1, 1.0, 1.5).unwrap();
/// assert!(r_val.is_finite());
/// assert!(r_prime.is_finite());
///
/// // Higher order
/// let (r_higher, _) = obl_rad1(0, 2, 5.0, 0.5).unwrap();
/// assert!(r_higher.is_finite());
/// ```
/// Computes the oblate spheroidal radial function of the second kind, `R_{m,n}^{(2)}(c, ξ)`.
///
/// Uses the Flammer §4.4 expansion in spherical Bessel functions of the second kind
/// `y_l(c·ξ)`. **Limitations**: this series is numerically unstable for some
/// odd-`m`/odd-parity combinations (notably `(m=1, n=2)`); see module-level
/// documentation.
///
/// # Arguments
///
/// * `m` - The order parameter (≥ 0, integer)
/// * `n` - The degree parameter (≥ m, integer)
/// * `c` - The spheroidal parameter (real)
/// * `x` - Radial coordinate (`x ≥ 1` recommended; series is asymptotic at small x)
///
/// # Examples
///
/// ```
/// use scirs2_special::obl_rad2;
///
/// // m = 0 cases match SciPy obl_rad2 to ~1e-5
/// let (r_val, r_prime) = obl_rad2(0, 1, 1.0, 2.0).unwrap();
/// assert!(r_val.is_finite());
/// assert!(r_prime.is_finite());
/// ```