Skip to main content

complex_bessel/
lib.rs

1//! Pure Rust implementation of complex Bessel functions based on Amos Algorithm 644 (ACM TOMS 644).
2//!
3//! # Features
4//!
5//! - **f32 & f64** — all functions accept `Complex<f64>` or `Complex<f32>`
6//! - **Complete function set** — J, Y, I, K, H<sup>(1)</sup>, H<sup>(2)</sup>, Ai, Bi
7//! - **Consecutive orders** — `_seq` variants return ν, ν+1, …, ν+n−1 in one call
8//! - **Exponential scaling** — `_scaled` variants prevent overflow/underflow
9//! - **Negative orders** — supports ν < 0 via DLMF reflection formulas (not in Amos)
10//! - **`no_std` support** — 3-tier: bare `no_std`, `alloc`, `std` (default)
11//!
12//! # Quick start
13//!
14//! ```
15//! use complex_bessel::*;
16//! use num_complex::Complex;
17//!
18//! let z = Complex::new(1.0, 2.0);
19//!
20//! let j = besselj(0.5, z).unwrap();
21//! let k = besselk(1.0, z).unwrap();
22//! let h = hankel1(0.0, z).unwrap();
23//!
24//! // Scaled versions prevent overflow/underflow
25//! let k_scaled = besselk_scaled(1.0, z).unwrap();
26//!
27//! // Airy functions
28//! let ai = airy(z).unwrap();
29//! let ai_prime = airyprime(z).unwrap();
30//! ```
31//!
32//! # Functions
33//!
34//! | Function | Description | Scaled variant returns |
35//! |----------|-------------|------------------------|
36//! | [`besselj`]`(ν, z)`<br>[`besselj_scaled`]`(ν, z)`<br>[`besselj_seq`]`(ν, z, n, scaling)` | J<sub>ν</sub>(z), Bessel first kind | exp(−\|Im(z)\|) · J<sub>ν</sub>(z) |
37//! | [`bessely`]`(ν, z)`<br>[`bessely_scaled`]`(ν, z)`<br>[`bessely_seq`]`(ν, z, n, scaling)` | Y<sub>ν</sub>(z), Bessel second kind | exp(−\|Im(z)\|) · Y<sub>ν</sub>(z) |
38//! | [`besseli`]`(ν, z)`<br>[`besseli_scaled`]`(ν, z)`<br>[`besseli_seq`]`(ν, z, n, scaling)` | I<sub>ν</sub>(z), modified first kind | exp(−\|Re(z)\|) · I<sub>ν</sub>(z) |
39//! | [`besselk`]`(ν, z)`<br>[`besselk_scaled`]`(ν, z)`<br>[`besselk_seq`]`(ν, z, n, scaling)` | K<sub>ν</sub>(z), modified second kind | exp(z) · K<sub>ν</sub>(z) |
40//! | [`hankel1`]`(ν, z)`<br>[`hankel1_scaled`]`(ν, z)`<br>[`hankel1_seq`]`(ν, z, n, scaling)` | H<sub>ν</sub><sup>(1)</sup>(z), Hankel first kind | exp(−iz) · H<sub>ν</sub><sup>(1)</sup>(z) |
41//! | [`hankel2`]`(ν, z)`<br>[`hankel2_scaled`]`(ν, z)`<br>[`hankel2_seq`]`(ν, z, n, scaling)` | H<sub>ν</sub><sup>(2)</sup>(z), Hankel second kind | exp(iz) · H<sub>ν</sub><sup>(2)</sup>(z) |
42//! | [`airy`]`(z)`<br>[`airy_scaled`]`(z)`<br>[`airy_raw`]`(z, scaling)` | Ai(z), Airy first kind | exp(ζ) · Ai(z) |
43//! | [`airyprime`]`(z)`<br>[`airyprime_scaled`]`(z)`<br>[`airyprime_raw`]`(z, scaling)` | Ai′(z), derivative of Airy first kind | exp(ζ) · Ai′(z) |
44//! | [`biry`]`(z)`<br>[`biry_scaled`]`(z)`<br>[`biry_raw`]`(z, scaling)` | Bi(z), Airy second kind | exp(−\|Re(ζ)\|) · Bi(z) |
45//! | [`biryprime`]`(z)`<br>[`biryprime_scaled`]`(z)`<br>[`biryprime_raw`]`(z, scaling)` | Bi′(z), derivative of Airy second kind | exp(−\|Re(ζ)\|) · Bi′(z) |
46//!
47//! where ζ = (2/3) z√z.
48//!
49//! ## Exponential scaling
50//!
51//! Each `_scaled` variant multiplies the result by an exponential factor that
52//! cancels the asymptotic growth (or decay), keeping values in a representable
53//! floating-point range. The exact factor is listed in the "Scaled variant
54//! returns" column above. Use scaled functions whenever |z| or |Im(z)| is
55//! large enough that the unscaled result would overflow or underflow.
56//!
57//! # Negative orders
58//!
59//! All functions accept any real order, including negative values.
60//! DLMF reflection formulas are applied automatically:
61//!
62//! - **J**: J<sub>−ν</sub>(z) = cos(νπ) J<sub>ν</sub>(z) − sin(νπ) Y<sub>ν</sub>(z) (DLMF 10.2.3)
63//! - **Y**: Y<sub>−ν</sub>(z) = sin(νπ) J<sub>ν</sub>(z) + cos(νπ) Y<sub>ν</sub>(z) (DLMF 10.2.3)
64//! - **I**: I<sub>−ν</sub>(z) = I<sub>ν</sub>(z) + (2/π) sin(νπ) K<sub>ν</sub>(z) (DLMF 10.27.2)
65//! - **K**: K<sub>−ν</sub>(z) = K<sub>ν</sub>(z) (DLMF 10.27.3)
66//! - **H<sup>(1)</sup>**: H<sup>(1)</sup><sub>−ν</sub>(z) = exp(νπi) H<sup>(1)</sup><sub>ν</sub>(z) (DLMF 10.4.6)
67//! - **H<sup>(2)</sup>**: H<sup>(2)</sup><sub>−ν</sub>(z) = exp(−νπi) H<sup>(2)</sup><sub>ν</sub>(z) (DLMF 10.4.6)
68//!
69//! For integer orders, simplified identities are used (e.g., J<sub>−n</sub>(z) = (−1)<sup>n</sup> J<sub>n</sub>(z)).
70//!
71//! # Function variants
72//!
73//! ## Consecutive orders
74//!
75//! The `_seq` variants ([`besselj_seq`], [`besselk_seq`], …) compute values at
76//! consecutive orders ν, ν+1, …, ν+n−1 in a single call and return
77//! a [`BesselResult`] that includes an [`Accuracy`] field.
78//! The `_raw` Airy variants ([`airy_raw`], [`biry_raw`], …) similarly return
79//! an [`AiryResult`] with [`Accuracy`].
80//! All other functions return only the computed value without [`Accuracy`].
81//!
82//! **[`Accuracy`]:**
83//!
84//! | Status | Meaning |
85//! |--------|---------|
86//! | [`Normal`](Accuracy::Normal) | Full machine precision |
87//! | [`Reduced`](Accuracy::Reduced) | More than half of significant digits may be lost |
88//!
89//! [`Reduced`](Accuracy::Reduced) is extremely rare in practice. SciPy's Bessel wrappers also silently
90//! discard the equivalent Amos IERR=3 flag by default.
91//!
92//! To check accuracy status, use a `_seq` function (Bessel) or a `_raw` function (Airy):
93//!
94//! ```
95//! # #[cfg(feature = "alloc")] {
96//! use complex_bessel::*;
97//! use num_complex::Complex;
98//!
99//! let z = Complex::new(1.0, 2.0);
100//! let result = besselk_seq(0.0, z, 1, Scaling::Unscaled).unwrap();
101//! assert!(matches!(result.status, Accuracy::Normal));
102//!
103//! let result = airy_raw(z, Scaling::Unscaled).unwrap();
104//! assert!(matches!(result.status, Accuracy::Normal));
105//! # }
106//! ```
107//!
108//! # Error handling
109//!
110//! All functions return `Result<_, Error>`. The four error variants are:
111//!
112//! | Variant | Cause |
113//! |---------|-------|
114//! | [`InvalidInput`](Error::InvalidInput) | z = 0 for K/Y/H, n < 1 |
115//! | [`Overflow`](Error::Overflow) | \|z\| or \|ν\| too large (or \|z\| too small) for finite result |
116//! | [`TotalPrecisionLoss`](Error::TotalPrecisionLoss) | \|z\| or \|ν\| too large for meaningful computation |
117//! | [`ConvergenceFailure`](Error::ConvergenceFailure) | Internal algorithm did not converge |
118//!
119//! # `no_std` support
120//!
121//! | Cargo features | Available API |
122//! |---------------|---------------|
123//! | `default-features = false` | 24 single-value functions |
124//! | `features = ["alloc"]` | full API (+ 6 `_seq` variants) |
125//! | `features = ["std"]` (default) | full API, with platform-native math |
126//!
127//! The 24 single-value functions include 12 Bessel (J/Y/I/K/H<sup>(1)</sup>/H<sup>(2)</sup> × unscaled/scaled)
128//! and 12 Airy (Ai/Ai'/Bi/Bi' × unscaled/scaled/raw).
129//!
130//! ```toml
131//! # Bare no_std — no allocator needed:
132//! complex-bessel = { version = "0.1", default-features = false }
133//!
134//! # no_std + alloc — full API:
135//! complex-bessel = { version = "0.1", default-features = false, features = ["alloc"] }
136//! ```
137
138#![warn(missing_docs)]
139#![no_std]
140
141#[cfg(feature = "alloc")]
142extern crate alloc;
143#[cfg(feature = "std")]
144extern crate std;
145
146pub(crate) mod airy;
147pub(crate) mod algo;
148pub(crate) mod besh;
149pub(crate) mod besi;
150pub(crate) mod besj;
151pub(crate) mod besk;
152pub(crate) mod besy;
153pub mod machine;
154pub mod types;
155pub(crate) mod utils;
156
157pub use machine::BesselFloat;
158#[cfg(feature = "alloc")]
159pub use types::BesselResult;
160pub use types::{Accuracy, AiryResult, Error, Scaling};
161
162use num_complex::Complex;
163use types::{AiryDerivative, HankelKind};
164
165// ── Helper: integer order detection ──
166
167/// Check if `nu` is a non-negative integer. Returns `Some(n)` if so.
168#[inline]
169fn as_integer<T: BesselFloat>(nu: T) -> Option<i64> {
170    if nu == nu.floor() {
171        // Safe conversion: orders beyond i64 range are not practical
172        nu.to_i64()
173    } else {
174        None
175    }
176}
177
178// ── Element-wise reflection helpers (shared by single-value and _seq) ──
179
180/// J_{-ν}(z) = cos(νπ)·J_ν(z) − sin(νπ)·Y_ν(z)  (DLMF 10.2.3)
181#[inline]
182fn reflect_j_element<T: BesselFloat>(order: T, j: Complex<T>, y: Complex<T>) -> Complex<T> {
183    j * utils::cospi(order) - y * utils::sinpi(order)
184}
185
186/// Y_{-ν}(z) = sin(νπ)·J_ν(z) + cos(νπ)·Y_ν(z)  (DLMF 10.2.3)
187#[inline]
188fn reflect_y_element<T: BesselFloat>(order: T, j: Complex<T>, y: Complex<T>) -> Complex<T> {
189    j * utils::sinpi(order) + y * utils::cospi(order)
190}
191
192/// I_{-ν}(z) = I_ν(z) + (2/π)·sin(νπ)·K_ν(z)  (DLMF 10.27.2)
193/// `k_val` must already have scaling correction applied by the caller.
194#[inline]
195fn reflect_i_element<T: BesselFloat>(order: T, i_val: Complex<T>, k_val: Complex<T>) -> Complex<T> {
196    let pi = T::from_f64(core::f64::consts::PI);
197    let two = T::from_f64(2.0);
198    utils::mul_add_scalar(k_val, two / pi * utils::sinpi(order), i_val)
199}
200
201/// H^(m)_{-ν}(z) = exp(±νπi)·H^(m)_ν(z)  (DLMF 10.4.6/7)
202#[inline]
203fn reflect_h_element<T: BesselFloat>(order: T, kind: HankelKind, h: Complex<T>) -> Complex<T> {
204    let cos_nu_pi = utils::cospi(order);
205    let sin_nu_pi = utils::sinpi(order);
206    let rotation = match kind {
207        HankelKind::First => Complex::new(cos_nu_pi, sin_nu_pi),
208        HankelKind::Second => Complex::new(cos_nu_pi, -sin_nu_pi),
209    };
210    h * rotation
211}
212
213/// Convert K scaling to I scaling for the reflection formula.
214/// I_scaled = exp(-|Re(z)|)·I, K_scaled = exp(z)·K.
215/// factor = exp(-|Re(z)|) / exp(z) = exp(-i·Im(z)) · [exp(-2·Re(z)) if Re(z)>0]
216#[inline]
217fn k_to_i_scaling_correction<T: BesselFloat>(z: Complex<T>, k_val: Complex<T>) -> Complex<T> {
218    let two = T::from_f64(2.0);
219    let (sin_a, cos_a) = (-z.im).sin_cos();
220    let mut result = k_val * Complex::new(cos_a, sin_a);
221    if z.re > T::zero() {
222        result = result * (-two * z.re).exp();
223    }
224    result
225}
226
227/// (-1)^n sign factor for integer order reflection.
228#[inline]
229fn integer_sign<T: BesselFloat>(n: i64) -> T {
230    if n % 2 == 0 { T::one() } else { -T::one() }
231}
232
233// ── Internal: compute with given scaling for negative order support ──
234
235#[inline]
236fn besselj_internal<T: BesselFloat>(
237    nu: T,
238    z: Complex<T>,
239    scaling: Scaling,
240) -> Result<Complex<T>, Error> {
241    let zero = T::zero();
242    let czero = Complex::new(zero, zero);
243    if nu >= zero {
244        let mut y = [czero];
245        besj::zbesj(z, nu, scaling, &mut y)?;
246        return Ok(y[0]);
247    }
248
249    let abs_nu = nu.abs();
250
251    // Integer shortcut: J_{-n}(z) = (-1)^n * J_n(z)
252    if let Some(n) = as_integer(abs_nu) {
253        let mut y = [czero];
254        besj::zbesj(z, abs_nu, scaling, &mut y)?;
255        return Ok(y[0] * integer_sign::<T>(n));
256    }
257
258    // General case: need both J and Y at positive |ν|
259    let mut j_buf = [czero];
260    let mut y_buf = [czero];
261    besj::zbesj(z, abs_nu, scaling, &mut j_buf)?;
262    besy::zbesy(z, abs_nu, scaling, &mut y_buf)?;
263
264    Ok(reflect_j_element(abs_nu, j_buf[0], y_buf[0]))
265}
266
267#[inline]
268fn bessely_internal<T: BesselFloat>(
269    nu: T,
270    z: Complex<T>,
271    scaling: Scaling,
272) -> Result<Complex<T>, Error> {
273    let zero = T::zero();
274    let czero = Complex::new(zero, zero);
275    if nu >= zero {
276        let mut y = [czero];
277        besy::zbesy(z, nu, scaling, &mut y)?;
278        return Ok(y[0]);
279    }
280
281    let abs_nu = nu.abs();
282
283    // Integer shortcut: Y_{-n}(z) = (-1)^n * Y_n(z)
284    if let Some(n) = as_integer(abs_nu) {
285        let mut y = [czero];
286        besy::zbesy(z, abs_nu, scaling, &mut y)?;
287        return Ok(y[0] * integer_sign::<T>(n));
288    }
289
290    // General case: need both J and Y at positive |ν|
291    let mut j_buf = [czero];
292    let mut y_buf = [czero];
293    besj::zbesj(z, abs_nu, scaling, &mut j_buf)?;
294    besy::zbesy(z, abs_nu, scaling, &mut y_buf)?;
295
296    Ok(reflect_y_element(abs_nu, j_buf[0], y_buf[0]))
297}
298
299#[inline]
300fn besseli_internal<T: BesselFloat>(
301    nu: T,
302    z: Complex<T>,
303    scaling: Scaling,
304) -> Result<Complex<T>, Error> {
305    let zero = T::zero();
306    let czero = Complex::new(zero, zero);
307    if nu >= zero {
308        let mut y = [czero];
309        besi::zbesi(z, nu, scaling, &mut y)?;
310        return Ok(y[0]);
311    }
312
313    let abs_nu = nu.abs();
314
315    // Integer shortcut: I_{-n}(z) = I_n(z)
316    if as_integer(abs_nu).is_some() {
317        let mut y = [czero];
318        besi::zbesi(z, abs_nu, scaling, &mut y)?;
319        return Ok(y[0]);
320    }
321
322    // General case: need both I and K at positive |ν|
323    let mut i_buf = [czero];
324    let mut k_buf = [czero];
325    besi::zbesi(z, abs_nu, scaling, &mut i_buf)?;
326    besk::zbesk(z, abs_nu, scaling, &mut k_buf)?;
327
328    let mut k_val = k_buf[0];
329    if scaling == Scaling::Exponential {
330        k_val = k_to_i_scaling_correction(z, k_val);
331    }
332
333    Ok(reflect_i_element(abs_nu, i_buf[0], k_val))
334}
335
336#[inline]
337fn besselk_internal<T: BesselFloat>(
338    nu: T,
339    z: Complex<T>,
340    scaling: Scaling,
341) -> Result<Complex<T>, Error> {
342    // K_{-ν}(z) = K_ν(z) (DLMF 10.27.3)
343    let abs_nu = nu.abs();
344    let zero = T::zero();
345    let mut y = [Complex::new(zero, zero)];
346    besk::zbesk(z, abs_nu, scaling, &mut y)?;
347    Ok(y[0])
348}
349
350#[inline]
351fn hankel_internal<T: BesselFloat>(
352    kind: HankelKind,
353    nu: T,
354    z: Complex<T>,
355    scaling: Scaling,
356) -> Result<Complex<T>, Error> {
357    let zero = T::zero();
358    if nu >= zero {
359        let mut y = [Complex::new(zero, zero)];
360        besh::zbesh(z, nu, kind, scaling, &mut y)?;
361        return Ok(y[0]);
362    }
363
364    let abs_nu = nu.abs();
365    let mut y = [Complex::new(zero, zero)];
366    besh::zbesh(z, abs_nu, kind, scaling, &mut y)?;
367
368    Ok(reflect_h_element(abs_nu, kind, y[0]))
369}
370
371// ── Single-value convenience functions ──
372
373/// Bessel function of the first kind, J<sub>ν</sub>(z).
374///
375/// Computes a single value of J_ν(z) for complex z and real order ν
376/// (any real value, including negative).
377///
378/// For negative ν, the DLMF 10.2.3 reflection formula is applied:
379/// `J_{-ν}(z) = cos(νπ) J_ν(z) - sin(νπ) Y_ν(z)`.
380/// For negative integer n, `J_{−n}(z) = (−1)^n J_n(z)`.
381///
382/// # Example
383///
384/// ```
385/// use complex_bessel::besselj;
386/// use num_complex::Complex;
387///
388/// let z = Complex::new(1.0_f64, 0.0);
389/// let j0 = besselj(0.0, z).unwrap();
390/// assert!((j0.re - 0.7652).abs() < 1e-3); // J_0(1) ≈ 0.7652
391/// ```
392///
393/// # Errors
394///
395/// - [`Error::InvalidInput`] if z = 0 and ν is a negative non-integer.
396/// - [`Error::Overflow`] if |z| or |ν| is too large for a finite result.
397/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
398/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
399#[inline]
400pub fn besselj<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
401    besselj_internal(nu, z, Scaling::Unscaled)
402}
403
404/// Bessel function of the second kind, Y<sub>ν</sub>(z).
405///
406/// Computes a single value of Y_ν(z) for complex z and real order ν
407/// (any real value, including negative).
408///
409/// For negative ν, the DLMF 10.2.3 reflection formula is applied:
410/// `Y_{-ν}(z) = sin(νπ) J_ν(z) + cos(νπ) Y_ν(z)`.
411/// For negative integer n, `Y_{−n}(z) = (−1)^n Y_n(z)`.
412///
413/// # Example
414///
415/// ```
416/// use complex_bessel::bessely;
417/// use num_complex::Complex;
418///
419/// let z = Complex::new(1.0_f64, 0.0);
420/// let y0 = bessely(0.0, z).unwrap();
421/// assert!((y0.re - 0.0883).abs() < 1e-3); // Y_0(1) ≈ 0.0883
422/// ```
423///
424/// # Errors
425///
426/// - [`Error::InvalidInput`] if z = 0.
427/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
428/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
429/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
430#[inline]
431pub fn bessely<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
432    bessely_internal(nu, z, Scaling::Unscaled)
433}
434
435/// Modified Bessel function of the first kind, I<sub>ν</sub>(z).
436///
437/// Computes a single value of I_ν(z) for complex z and real order ν
438/// (any real value, including negative).
439///
440/// For negative ν, the DLMF 10.27.2 reflection formula is applied:
441/// `I_{-ν}(z) = I_ν(z) + (2/π) sin(νπ) K_ν(z)`.
442/// For negative integer n, `I_{−n}(z) = I_n(z)`.
443///
444/// # Example
445///
446/// ```
447/// use complex_bessel::besseli;
448/// use num_complex::Complex;
449///
450/// let z = Complex::new(1.0_f64, 0.0);
451/// let i0 = besseli(0.0, z).unwrap();
452/// assert!((i0.re - 1.2661).abs() < 1e-3); // I_0(1) ≈ 1.2661
453/// ```
454///
455/// # Errors
456///
457/// - [`Error::InvalidInput`] if z = 0 and ν is a negative non-integer.
458/// - [`Error::Overflow`] if |z| or |ν| is too large for a finite result.
459/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
460/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
461#[inline]
462pub fn besseli<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
463    besseli_internal(nu, z, Scaling::Unscaled)
464}
465
466/// Modified Bessel function of the second kind, K<sub>ν</sub>(z).
467///
468/// Computes a single value of K_ν(z) for complex z and real order ν
469/// (any real value, including negative).
470///
471/// For negative ν, K_{-ν}(z) = K_ν(z) (DLMF 10.27.3).
472///
473/// # Example
474///
475/// ```
476/// use complex_bessel::besselk;
477/// use num_complex::Complex;
478///
479/// let z = Complex::new(1.0_f64, 0.0);
480/// let k0 = besselk(0.0, z).unwrap();
481/// assert!((k0.re - 0.4211).abs() < 1e-3); // K_0(1) ≈ 0.4211
482/// ```
483///
484/// # Errors
485///
486/// - [`Error::InvalidInput`] if z = 0.
487/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
488/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
489/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
490#[inline]
491pub fn besselk<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
492    besselk_internal(nu, z, Scaling::Unscaled)
493}
494
495/// Hankel function of the first kind, H<sub>ν</sub><sup>(1)</sup>(z).
496///
497/// Computes a single value of H_ν^(1)(z) for complex z and real order ν
498/// (any real value, including negative).
499///
500/// For negative ν, the DLMF 10.4.6 reflection formula is applied:
501/// `H^(1)_{-ν}(z) = exp(νπi) H^(1)_ν(z)`.
502/// For negative integer n, `H^(1)_{−n}(z) = (−1)^n H^(1)_n(z)`.
503///
504/// # Example
505///
506/// ```
507/// use complex_bessel::hankel1;
508/// use num_complex::Complex;
509///
510/// let z = Complex::new(1.0_f64, 0.0);
511/// let h = hankel1(0.0, z).unwrap();
512/// assert!((h.re - 0.7652).abs() < 1e-3); // Re = J_0(1) ≈ 0.7652
513/// assert!((h.im - 0.0883).abs() < 1e-3); // Im = Y_0(1) ≈ 0.0883
514/// ```
515///
516/// # Errors
517///
518/// - [`Error::InvalidInput`] if z = 0.
519/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
520/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
521/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
522#[inline]
523pub fn hankel1<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
524    hankel_internal(HankelKind::First, nu, z, Scaling::Unscaled)
525}
526
527/// Hankel function of the second kind, H<sub>ν</sub><sup>(2)</sup>(z).
528///
529/// Computes a single value of H_ν^(2)(z) for complex z and real order ν
530/// (any real value, including negative).
531///
532/// For negative ν, the DLMF 10.4.6 reflection formula is applied:
533/// `H^(2)_{-ν}(z) = exp(-νπi) H^(2)_ν(z)`.
534/// For negative integer n, `H^(2)_{−n}(z) = (−1)^n H^(2)_n(z)`.
535///
536/// # Example
537///
538/// ```
539/// use complex_bessel::hankel2;
540/// use num_complex::Complex;
541///
542/// let z = Complex::new(1.0_f64, 0.0);
543/// let h = hankel2(0.0, z).unwrap();
544/// assert!((h.re - 0.7652).abs() < 1e-3); // Re = J_0(1) ≈ 0.7652
545/// assert!((h.im + 0.0883).abs() < 1e-3); // Im = -Y_0(1) ≈ -0.0883
546/// ```
547///
548/// # Errors
549///
550/// - [`Error::InvalidInput`] if z = 0.
551/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
552/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
553/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
554#[inline]
555pub fn hankel2<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
556    hankel_internal(HankelKind::Second, nu, z, Scaling::Unscaled)
557}
558
559/// Airy function Ai(z).
560///
561/// Computes the Airy function Ai(z) for complex z.
562/// Ai(z) is a solution to the differential equation w′′ − z·w = 0
563/// that decays super-exponentially for large positive real z.
564///
565/// # Example
566///
567/// ```
568/// use complex_bessel::airy;
569/// use num_complex::Complex;
570///
571/// let z = Complex::new(0.0_f64, 0.0);
572/// let ai = airy(z).unwrap();
573/// assert!((ai.re - 0.3550).abs() < 1e-3); // Ai(0) ≈ 0.3550
574/// ```
575///
576/// # Errors
577///
578/// - [`Error::Overflow`] if |z| is too large for a finite result.
579/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
580/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
581#[inline]
582pub fn airy<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
583    let (result, _nz, _status) = airy::zairy(z, AiryDerivative::Value, Scaling::Unscaled)?;
584    Ok(result)
585}
586
587/// Derivative of the Airy function, Ai'(z).
588///
589/// Computes the derivative of Ai(z) for complex z.
590/// Ai'(z) decays super-exponentially for large positive real z, just as Ai(z) does.
591/// Satisfies the differential equation Ai′′(z) = z · Ai(z).
592///
593/// # Example
594///
595/// ```
596/// use complex_bessel::airyprime;
597/// use num_complex::Complex;
598///
599/// let z = Complex::new(0.0_f64, 0.0);
600/// let ai_prime = airyprime(z).unwrap();
601/// assert!((ai_prime.re + 0.2588).abs() < 1e-3); // Ai'(0) ≈ -0.2588
602/// ```
603///
604/// # Errors
605///
606/// - [`Error::Overflow`] if |z| is too large for a finite result.
607/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
608/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
609#[inline]
610pub fn airyprime<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
611    let (result, _nz, _status) = airy::zairy(z, AiryDerivative::Derivative, Scaling::Unscaled)?;
612    Ok(result)
613}
614
615/// Airy function of the second kind, Bi(z).
616///
617/// Computes the Airy function Bi(z) for complex z.
618/// Bi(z) is the solution to w′′ − z·w = 0 that grows super-exponentially
619/// for large positive real z.
620///
621/// # Example
622///
623/// ```
624/// use complex_bessel::biry;
625/// use num_complex::Complex;
626///
627/// let z = Complex::new(0.0_f64, 0.0);
628/// let bi = biry(z).unwrap();
629/// assert!((bi.re - 0.6149).abs() < 1e-3); // Bi(0) ≈ 0.6149
630/// ```
631///
632/// # Errors
633///
634/// - [`Error::Overflow`] if |z| is too large for a finite result.
635/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
636/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
637#[inline]
638pub fn biry<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
639    let (result, _status) = airy::zbiry(z, AiryDerivative::Value, Scaling::Unscaled)?;
640    Ok(result)
641}
642
643/// Derivative of the Airy function of the second kind, Bi'(z).
644///
645/// Computes the derivative of Bi(z) for complex z.
646/// Bi'(z) grows super-exponentially for large positive real z, just as Bi(z) does.
647/// Satisfies the differential equation Bi′′(z) = z · Bi(z).
648///
649/// # Example
650///
651/// ```
652/// use complex_bessel::biryprime;
653/// use num_complex::Complex;
654///
655/// let z = Complex::new(0.0_f64, 0.0);
656/// let bi_prime = biryprime(z).unwrap();
657/// assert!((bi_prime.re - 0.4483).abs() < 1e-3); // Bi'(0) ≈ 0.4483
658/// ```
659///
660/// # Errors
661///
662/// - [`Error::Overflow`] if |z| is too large for a finite result.
663/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
664/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
665#[inline]
666pub fn biryprime<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
667    let (result, _status) = airy::zbiry(z, AiryDerivative::Derivative, Scaling::Unscaled)?;
668    Ok(result)
669}
670
671// ── Scaled single-value functions ──
672
673/// Scaled Bessel function of the first kind, exp(−|Im(z)|) · J<sub>ν</sub>(z).
674///
675/// The exponential factor cancels the asymptotic growth of J for large imaginary
676/// arguments, keeping results in a representable floating-point range.
677///
678/// Supports negative ν via the same reflection formula as [`besselj`].
679///
680/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
681///
682/// # Example
683///
684/// ```
685/// use complex_bessel::besselj_scaled;
686/// use num_complex::Complex;
687///
688/// let z = Complex::new(0.0_f64, 10.0);
689/// let j_s = besselj_scaled(0.0, z).unwrap();
690/// assert!((j_s.re - 0.1278).abs() < 1e-3); // exp(-|Im(z)|) * J_0(10i) ≈ 0.1278
691/// ```
692///
693/// # Errors
694///
695/// - [`Error::InvalidInput`] if z = 0 and ν is a negative non-integer.
696/// - [`Error::Overflow`] if |z| or |ν| is too large for a finite result.
697/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
698/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
699#[inline]
700pub fn besselj_scaled<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
701    besselj_internal(nu, z, Scaling::Exponential)
702}
703
704/// Scaled Bessel function of the second kind, exp(−|Im(z)|) · Y<sub>ν</sub>(z).
705///
706/// The exponential factor cancels the asymptotic growth of Y for large imaginary
707/// arguments, keeping results in a representable floating-point range.
708///
709/// Supports negative ν via the same reflection formula as [`bessely`].
710///
711/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
712///
713/// # Example
714///
715/// ```
716/// use complex_bessel::bessely_scaled;
717/// use num_complex::Complex;
718///
719/// let z = Complex::new(0.0_f64, 10.0);
720/// let y_s = bessely_scaled(0.0, z).unwrap();
721/// assert!((y_s.im - 0.1278).abs() < 1e-3); // exp(-|Im(z)|) * Y_0(10i), Im ≈ 0.1278
722/// ```
723///
724/// # Errors
725///
726/// - [`Error::InvalidInput`] if z = 0.
727/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
728/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
729/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
730#[inline]
731pub fn bessely_scaled<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
732    bessely_internal(nu, z, Scaling::Exponential)
733}
734
735/// Scaled modified Bessel function of the first kind, exp(−|Re(z)|) · I<sub>ν</sub>(z).
736///
737/// I_ν(z) grows exponentially for large |Re(z)|, so the unscaled value can
738/// easily overflow. The scaling factor exp(−|Re(z)|) keeps the result finite.
739///
740/// Supports negative ν via the same reflection formula as [`besseli`].
741///
742/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
743///
744/// # Example
745///
746/// ```
747/// use complex_bessel::besseli_scaled;
748/// use num_complex::Complex;
749///
750/// let z = Complex::new(10.0_f64, 0.0);
751/// let i_s = besseli_scaled(0.0, z).unwrap();
752/// assert!((i_s.re - 0.1278).abs() < 1e-3); // exp(-10) * I_0(10) ≈ 0.1278
753/// ```
754///
755/// # Errors
756///
757/// - [`Error::InvalidInput`] if z = 0 and ν is a negative non-integer.
758/// - [`Error::Overflow`] if |z| or |ν| is too large for a finite result.
759/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
760/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
761#[inline]
762pub fn besseli_scaled<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
763    besseli_internal(nu, z, Scaling::Exponential)
764}
765
766/// Scaled modified Bessel function of the second kind, exp(z) · K<sub>ν</sub>(z).
767///
768/// K_ν(z) decays exponentially for large Re(z), so unscaled values can underflow
769/// to zero. The scaling factor exp(z) keeps the result in a normal range.
770///
771/// Supports negative ν via the same reflection formula as [`besselk`].
772///
773/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
774///
775/// # Example
776///
777/// ```
778/// use complex_bessel::besselk_scaled;
779/// use num_complex::Complex;
780///
781/// let z = Complex::new(500.0_f64, 0.0);
782///
783/// // K_0(500) would underflow to 0, but the scaled version stays finite:
784/// let k_s = besselk_scaled(0.0, z).unwrap();
785/// assert!((k_s.re - 0.0560).abs() < 1e-3); // exp(500) * K_0(500) ≈ 0.0560
786/// ```
787///
788/// # Errors
789///
790/// - [`Error::InvalidInput`] if z = 0.
791/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
792/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
793/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
794#[inline]
795pub fn besselk_scaled<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
796    besselk_internal(nu, z, Scaling::Exponential)
797}
798
799/// Scaled Hankel function of the first kind, exp(−iz) · H<sub>ν</sub><sup>(1)</sup>(z).
800///
801/// H^(1) grows exponentially in the lower half-plane;
802/// the scaling factor removes this growth, preventing overflow.
803///
804/// Supports negative ν via the same reflection formula as [`hankel1`].
805///
806/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
807///
808/// # Example
809///
810/// ```
811/// use complex_bessel::hankel1_scaled;
812/// use num_complex::Complex;
813///
814/// let z = Complex::new(1.0_f64, 0.0);
815/// let h_s = hankel1_scaled(0.0, z).unwrap();
816/// assert!((h_s.re - 0.4877).abs() < 1e-3); // exp(-i) * H^(1)_0(1), Re ≈ 0.4877
817/// ```
818///
819/// # Errors
820///
821/// - [`Error::InvalidInput`] if z = 0.
822/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
823/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
824/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
825#[inline]
826pub fn hankel1_scaled<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
827    hankel_internal(HankelKind::First, nu, z, Scaling::Exponential)
828}
829
830/// Scaled Hankel function of the second kind, exp(iz) · H<sub>ν</sub><sup>(2)</sup>(z).
831///
832/// H^(2) grows exponentially in the upper half-plane;
833/// the scaling factor removes this growth, preventing overflow.
834///
835/// Supports negative ν via the same reflection formula as [`hankel2`].
836///
837/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
838///
839/// # Example
840///
841/// ```
842/// use complex_bessel::hankel2_scaled;
843/// use num_complex::Complex;
844///
845/// let z = Complex::new(1.0_f64, 0.0);
846/// let h_s = hankel2_scaled(0.0, z).unwrap();
847/// assert!((h_s.re - 0.4877).abs() < 1e-3); // exp(i) * H^(2)_0(1), Re ≈ 0.4877
848/// ```
849///
850/// # Errors
851///
852/// - [`Error::InvalidInput`] if z = 0.
853/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
854/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
855/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
856#[inline]
857pub fn hankel2_scaled<T: BesselFloat>(nu: T, z: Complex<T>) -> Result<Complex<T>, Error> {
858    hankel_internal(HankelKind::Second, nu, z, Scaling::Exponential)
859}
860
861/// Scaled Airy function, exp(ζ) · Ai(z), where ζ = (2/3) z√z.
862///
863/// Ai(z) decays super-exponentially for large positive real z.
864/// The scaling factor exp(ζ) keeps the result representable.
865///
866/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
867///
868/// # Example
869///
870/// ```
871/// use complex_bessel::airy_scaled;
872/// use num_complex::Complex;
873///
874/// let z = Complex::new(5.0_f64, 0.0);
875/// let ai_s = airy_scaled(z).unwrap();
876/// assert!((ai_s.re - 0.1870).abs() < 1e-3); // exp(ζ) * Ai(5) ≈ 0.1870
877/// ```
878///
879/// # Errors
880///
881/// - [`Error::Overflow`] if |z| is too large for a finite result.
882/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
883/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
884#[inline]
885pub fn airy_scaled<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
886    let (result, _nz, _status) = airy::zairy(z, AiryDerivative::Value, Scaling::Exponential)?;
887    Ok(result)
888}
889
890/// Scaled derivative of the Airy function, exp(ζ) · Ai'(z), where ζ = (2/3) z√z.
891///
892/// Ai'(z) decays super-exponentially for large positive real z, just as Ai(z) does.
893/// The scaling factor exp(ζ) keeps the result representable.
894///
895/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
896///
897/// # Example
898///
899/// ```
900/// use complex_bessel::airyprime_scaled;
901/// use num_complex::Complex;
902///
903/// let z = Complex::new(5.0_f64, 0.0);
904/// let ai_s = airyprime_scaled(z).unwrap();
905/// assert!((ai_s.re + 0.4270).abs() < 1e-3); // exp(ζ) * Ai'(5) ≈ -0.4270
906/// ```
907///
908/// # Errors
909///
910/// - [`Error::Overflow`] if |z| is too large for a finite result.
911/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
912/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
913#[inline]
914pub fn airyprime_scaled<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
915    let (result, _nz, _status) = airy::zairy(z, AiryDerivative::Derivative, Scaling::Exponential)?;
916    Ok(result)
917}
918
919/// Scaled Airy function of the second kind, exp(−|Re(ζ)|) · Bi(z), where ζ = (2/3) z√z.
920///
921/// Bi(z) grows super-exponentially for large positive real z.
922/// The scaling factor exp(−|Re(ζ)|) keeps the result representable.
923///
924/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
925///
926/// # Example
927///
928/// ```
929/// use complex_bessel::biry_scaled;
930/// use num_complex::Complex;
931///
932/// let z = Complex::new(5.0_f64, 0.0);
933/// let bi_s = biry_scaled(z).unwrap();
934/// assert!((bi_s.re - 0.3811).abs() < 1e-3); // exp(-|Re(ζ)|) * Bi(5) ≈ 0.3811
935/// ```
936///
937/// # Errors
938///
939/// - [`Error::Overflow`] if |z| is too large for a finite result.
940/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
941/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
942#[inline]
943pub fn biry_scaled<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
944    let (result, _status) = airy::zbiry(z, AiryDerivative::Value, Scaling::Exponential)?;
945    Ok(result)
946}
947
948/// Scaled derivative of the Airy function of the second kind, exp(−|Re(ζ)|) · Bi'(z), where ζ = (2/3) z√z.
949///
950/// Bi'(z) grows super-exponentially for large positive real z, just as Bi(z) does.
951/// The scaling factor exp(−|Re(ζ)|) keeps the result representable.
952///
953/// See [crate-level docs](crate#exponential-scaling) for the full scaling table.
954///
955/// # Example
956///
957/// ```
958/// use complex_bessel::biryprime_scaled;
959/// use num_complex::Complex;
960///
961/// let z = Complex::new(5.0_f64, 0.0);
962/// let bi_s = biryprime_scaled(z).unwrap();
963/// assert!((bi_s.re - 0.8319).abs() < 1e-3); // exp(-|Re(ζ)|) * Bi'(5) ≈ 0.8319
964/// ```
965///
966/// # Errors
967///
968/// - [`Error::Overflow`] if |z| is too large for a finite result.
969/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
970/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
971#[inline]
972pub fn biryprime_scaled<T: BesselFloat>(z: Complex<T>) -> Result<Complex<T>, Error> {
973    let (result, _status) = airy::zbiry(z, AiryDerivative::Derivative, Scaling::Exponential)?;
974    Ok(result)
975}
976
977// ── Airy _raw functions (expose Accuracy) ──
978
979/// Airy function Ai(z) with accuracy status.
980///
981/// Like [`airy`], but returns an [`AiryResult`] that includes an [`Accuracy`] status:
982/// - [`Accuracy::Normal`] — no significant precision loss
983/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| very large)
984///
985/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
986/// see [crate-level docs](crate#exponential-scaling) for details.
987///
988/// # Example
989///
990/// ```
991/// use complex_bessel::*;
992/// use num_complex::Complex;
993///
994/// let z = Complex::new(0.0_f64, 0.0);
995/// let result = airy_raw(z, Scaling::Unscaled).unwrap();
996/// assert!((result.value.re - 0.3550).abs() < 1e-3); // Ai(0) ≈ 0.3550
997/// assert!(matches!(result.status, Accuracy::Normal));
998/// ```
999///
1000/// # Errors
1001///
1002/// - [`Error::Overflow`] if |z| is too large for a finite result.
1003/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
1004/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1005#[inline]
1006pub fn airy_raw<T: BesselFloat>(z: Complex<T>, scaling: Scaling) -> Result<AiryResult<T>, Error> {
1007    let (value, _nz, status) = airy::zairy(z, AiryDerivative::Value, scaling)?;
1008    Ok(AiryResult { value, status })
1009}
1010
1011/// Derivative of the Airy function Ai'(z) with accuracy status.
1012///
1013/// Like [`airyprime`], but returns an [`AiryResult`] that includes an [`Accuracy`] status:
1014/// - [`Accuracy::Normal`] — no significant precision loss
1015/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| very large)
1016///
1017/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1018/// see [crate-level docs](crate#exponential-scaling) for details.
1019///
1020/// # Example
1021///
1022/// ```
1023/// use complex_bessel::*;
1024/// use num_complex::Complex;
1025///
1026/// let z = Complex::new(0.0_f64, 0.0);
1027/// let result = airyprime_raw(z, Scaling::Unscaled).unwrap();
1028/// assert!((result.value.re + 0.2588).abs() < 1e-3); // Ai'(0) ≈ -0.2588
1029/// assert!(matches!(result.status, Accuracy::Normal));
1030/// ```
1031///
1032/// # Errors
1033///
1034/// - [`Error::Overflow`] if |z| is too large for a finite result.
1035/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
1036/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1037#[inline]
1038pub fn airyprime_raw<T: BesselFloat>(
1039    z: Complex<T>,
1040    scaling: Scaling,
1041) -> Result<AiryResult<T>, Error> {
1042    let (value, _nz, status) = airy::zairy(z, AiryDerivative::Derivative, scaling)?;
1043    Ok(AiryResult { value, status })
1044}
1045
1046/// Airy function of the second kind Bi(z) with accuracy status.
1047///
1048/// Like [`biry`], but returns an [`AiryResult`] that includes an [`Accuracy`] status:
1049/// - [`Accuracy::Normal`] — no significant precision loss
1050/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| very large)
1051///
1052/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1053/// see [crate-level docs](crate#exponential-scaling) for details.
1054///
1055/// # Example
1056///
1057/// ```
1058/// use complex_bessel::*;
1059/// use num_complex::Complex;
1060///
1061/// let z = Complex::new(0.0_f64, 0.0);
1062/// let result = biry_raw(z, Scaling::Unscaled).unwrap();
1063/// assert!((result.value.re - 0.6149).abs() < 1e-3); // Bi(0) ≈ 0.6149
1064/// assert!(matches!(result.status, Accuracy::Normal));
1065/// ```
1066///
1067/// # Errors
1068///
1069/// - [`Error::Overflow`] if |z| is too large for a finite result.
1070/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
1071/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1072#[inline]
1073pub fn biry_raw<T: BesselFloat>(z: Complex<T>, scaling: Scaling) -> Result<AiryResult<T>, Error> {
1074    let (value, status) = airy::zbiry(z, AiryDerivative::Value, scaling)?;
1075    Ok(AiryResult { value, status })
1076}
1077
1078/// Derivative of the Airy function of the second kind Bi'(z) with accuracy status.
1079///
1080/// Like [`biryprime`], but returns an [`AiryResult`] that includes an [`Accuracy`] status:
1081/// - [`Accuracy::Normal`] — no significant precision loss
1082/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| very large)
1083///
1084/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1085/// see [crate-level docs](crate#exponential-scaling) for details.
1086///
1087/// # Example
1088///
1089/// ```
1090/// use complex_bessel::*;
1091/// use num_complex::Complex;
1092///
1093/// let z = Complex::new(0.0_f64, 0.0);
1094/// let result = biryprime_raw(z, Scaling::Unscaled).unwrap();
1095/// assert!((result.value.re - 0.4483).abs() < 1e-3); // Bi'(0) ≈ 0.4483
1096/// assert!(matches!(result.status, Accuracy::Normal));
1097/// ```
1098///
1099/// # Errors
1100///
1101/// - [`Error::Overflow`] if |z| is too large for a finite result.
1102/// - [`Error::TotalPrecisionLoss`] if |z| is too large for any significant digits (roughly > 10⁶ for f64).
1103/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1104#[inline]
1105pub fn biryprime_raw<T: BesselFloat>(
1106    z: Complex<T>,
1107    scaling: Scaling,
1108) -> Result<AiryResult<T>, Error> {
1109    let (value, status) = airy::zbiry(z, AiryDerivative::Derivative, scaling)?;
1110    Ok(AiryResult { value, status })
1111}
1112
1113// ── Sequence functions with scaling option (require alloc) ──
1114
1115#[cfg(feature = "alloc")]
1116extern crate alloc as alloc_crate;
1117
1118#[cfg(feature = "alloc")]
1119fn seq_helper<T: BesselFloat>(
1120    n: usize,
1121    f: impl FnOnce(&mut [Complex<T>]) -> Result<(usize, Accuracy), Error>,
1122) -> Result<BesselResult<T>, Error> {
1123    let zero = T::zero();
1124    let mut values = alloc_crate::vec![Complex::new(zero, zero); n];
1125    let (underflow_count, status) = f(&mut values)?;
1126    Ok(BesselResult {
1127        values,
1128        underflow_count,
1129        status,
1130    })
1131}
1132
1133/// Take the worse of two statuses (Reduced > Normal).
1134#[cfg(feature = "alloc")]
1135#[inline]
1136fn worse_status(a: Accuracy, b: Accuracy) -> Accuracy {
1137    match (a, b) {
1138        (Accuracy::Reduced, _) | (_, Accuracy::Reduced) => Accuracy::Reduced,
1139        _ => Accuracy::Normal,
1140    }
1141}
1142
1143/// Recount underflow: number of leading zero elements in the output.
1144#[cfg(feature = "alloc")]
1145#[inline]
1146fn recount_underflow<T: BesselFloat>(values: &[Complex<T>]) -> usize {
1147    let zero = Complex::new(T::zero(), T::zero());
1148    values.iter().take_while(|v| **v == zero).count()
1149}
1150
1151/// Compute the number of negative-order elements (before crossing zero).
1152/// For orders ν, ν+1, …, ν+n−1, this is the count where ν+j < 0.
1153#[cfg(feature = "alloc")]
1154#[inline]
1155fn neg_count<T: BesselFloat>(nu: T, n: usize) -> usize {
1156    if nu >= T::zero() {
1157        return 0;
1158    }
1159    // Number of j in [0,n) with nu + j < 0
1160    let abs_nu = nu.abs();
1161    // ceil(|nu|) gives the first j where nu+j >= 0
1162    let c = abs_nu.ceil();
1163    let cn = if let Some(v) = c.to_usize() { v } else { n };
1164    cn.min(n)
1165}
1166
1167// ── Negative-order _seq implementations ──
1168
1169/// K_seq with negative order support: K_{-ν} = K_ν (even function).
1170///
1171/// Orders: ν, ν+1, …, ν+n−1. Negative orders map to |order|.
1172/// - All negative: single call zbesk(|ν|-(n-1), n), reverse.
1173/// - All positive: single call zbesk(ν, n).
1174/// - Integer crossing: single call covering max(|ν|, ν+n-1), mirror.
1175/// - Non-integer crossing: two calls (neg lattice + pos lattice).
1176#[cfg(feature = "alloc")]
1177#[allow(clippy::needless_range_loop, clippy::manual_memcpy)]
1178fn besselk_seq_neg<T: BesselFloat>(
1179    nu: T,
1180    z: Complex<T>,
1181    n: usize,
1182    scaling: Scaling,
1183) -> Result<BesselResult<T>, Error> {
1184    let zero = T::zero();
1185    let czero = Complex::new(zero, zero);
1186    let abs_nu = nu.abs();
1187    let nc = neg_count(nu, n);
1188
1189    // All negative: absolute values are |ν|, |ν|-1, …, |ν|-(nc-1) (descending)
1190    // = ascending from |ν|-(nc-1) to |ν|
1191    if nc == n {
1192        // All orders negative, e.g., ν=-3.7, n=3 → orders -3.7,-2.7,-1.7
1193        // |orders| = 3.7, 2.7, 1.7 → start at 1.7, compute 3 ascending → reverse
1194        let start = abs_nu - T::from_f64((n - 1) as f64);
1195        let mut buf = alloc_crate::vec![czero; n];
1196        let (_, status) = besk::zbesk(z, start, scaling, &mut buf)?;
1197        buf.reverse();
1198        let uc = recount_underflow(&buf);
1199        return Ok(BesselResult {
1200            values: buf,
1201            underflow_count: uc,
1202            status,
1203        });
1204    }
1205
1206    // Check if integer crossing (fractional part is 0 for integer nu)
1207    let is_int = as_integer(abs_nu).is_some();
1208
1209    if is_int {
1210        // Integer crossing: e.g., ν=-2, n=5 → orders -2,-1,0,1,2
1211        // All have same fractional part (0). One call covers all |orders|.
1212        // Max absolute order = max(|ν|, ν+n-1)
1213        let last_order = nu + T::from_f64((n - 1) as f64);
1214        let max_abs = if abs_nu > last_order {
1215            abs_nu
1216        } else {
1217            last_order
1218        };
1219        let buf_n = max_abs.to_usize().unwrap_or(0) + 1; // orders 0..=max_abs
1220        let mut buf = alloc_crate::vec![czero; buf_n];
1221        let (_, status) = besk::zbesk(z, zero, scaling, &mut buf)?;
1222
1223        let mut values = alloc_crate::vec![czero; n];
1224        for j in 0..n {
1225            let order = nu + T::from_f64(j as f64);
1226            let idx = order.abs().to_usize().unwrap_or(0);
1227            values[j] = buf[idx];
1228        }
1229        let uc = recount_underflow(&values);
1230        return Ok(BesselResult {
1231            values,
1232            underflow_count: uc,
1233            status,
1234        });
1235    }
1236
1237    // Non-integer crossing: two separate lattices
1238    // Negative part: orders ν, ν+1, …, ν+(nc-1) → |orders| descending
1239    // frac_neg = fractional part of |ν|
1240    let frac_neg = abs_nu - abs_nu.floor();
1241    let neg_start = frac_neg; // smallest |order| in negative lattice
1242    let mut neg_buf = alloc_crate::vec![czero; nc];
1243    let (_, status1) = besk::zbesk(z, neg_start, scaling, &mut neg_buf)?;
1244
1245    // Positive part: orders ν+nc, ν+nc+1, …, ν+n-1
1246    let pos_start = nu + T::from_f64(nc as f64);
1247    let pos_n = n - nc;
1248    let mut pos_buf = alloc_crate::vec![czero; pos_n];
1249    let (_, status2) = besk::zbesk(z, pos_start, scaling, &mut pos_buf)?;
1250
1251    let mut values = alloc_crate::vec![czero; n];
1252    // Negative part: buf[0]=K(frac_neg), buf[1]=K(frac_neg+1), …
1253    // Need to map: order ν+j → |ν+j| → index in neg_buf
1254    // |ν+j| = abs_nu - j, and neg_buf[k] = K(frac_neg + k)
1255    // So k = |ν+j| - frac_neg = (abs_nu - j) - frac_neg = floor(abs_nu) - j
1256    for j in 0..nc {
1257        let k = (abs_nu - T::from_f64(j as f64))
1258            .floor()
1259            .to_usize()
1260            .unwrap_or(0);
1261        values[j] = neg_buf[k];
1262    }
1263    // Positive part: direct copy
1264    for j in 0..pos_n {
1265        values[nc + j] = pos_buf[j];
1266    }
1267
1268    let status = worse_status(status1, status2);
1269    let uc = recount_underflow(&values);
1270    Ok(BesselResult {
1271        values,
1272        underflow_count: uc,
1273        status,
1274    })
1275}
1276
1277/// H_seq with negative order support: H^(m)_{-ν} = exp(±νπi)·H^(m)_ν.
1278///
1279/// Same lattice-splitting as K, but negative-order elements get rotation.
1280#[cfg(feature = "alloc")]
1281#[allow(clippy::needless_range_loop, clippy::manual_memcpy)]
1282fn hankel_seq_neg<T: BesselFloat>(
1283    kind: HankelKind,
1284    nu: T,
1285    z: Complex<T>,
1286    n: usize,
1287    scaling: Scaling,
1288) -> Result<BesselResult<T>, Error> {
1289    let zero = T::zero();
1290    let czero = Complex::new(zero, zero);
1291    let abs_nu = nu.abs();
1292    let nc = neg_count(nu, n);
1293    let is_int = as_integer(abs_nu).is_some();
1294
1295    if nc == n {
1296        // All negative: compute at positive |orders| then rotate each
1297        let start = abs_nu - T::from_f64((n - 1) as f64);
1298        let mut buf = alloc_crate::vec![czero; n];
1299        let (_, status) = besh::zbesh(z, start, kind, scaling, &mut buf)?;
1300        buf.reverse();
1301        // Apply rotation: element j has |order| = abs_nu - j
1302        for j in 0..n {
1303            let order = abs_nu - T::from_f64(j as f64);
1304            buf[j] = reflect_h_element(order, kind, buf[j]);
1305        }
1306        let uc = recount_underflow(&buf);
1307        return Ok(BesselResult {
1308            values: buf,
1309            underflow_count: uc,
1310            status,
1311        });
1312    }
1313
1314    if is_int {
1315        // Integer crossing: single call, rotate negative elements
1316        let last_order = nu + T::from_f64((n - 1) as f64);
1317        let max_abs = if abs_nu > last_order {
1318            abs_nu
1319        } else {
1320            last_order
1321        };
1322        let buf_n = max_abs.to_usize().unwrap_or(0) + 1;
1323        let mut buf = alloc_crate::vec![czero; buf_n];
1324        let (_, status) = besh::zbesh(z, zero, kind, scaling, &mut buf)?;
1325
1326        let mut values = alloc_crate::vec![czero; n];
1327        for j in 0..n {
1328            let order = nu + T::from_f64(j as f64);
1329            let idx = order.abs().to_usize().unwrap_or(0);
1330            values[j] = buf[idx];
1331            if order < zero {
1332                values[j] = reflect_h_element(order.abs(), kind, values[j]);
1333            }
1334        }
1335        let uc = recount_underflow(&values);
1336        return Ok(BesselResult {
1337            values,
1338            underflow_count: uc,
1339            status,
1340        });
1341    }
1342
1343    // Non-integer crossing: two lattices
1344    let frac_neg = abs_nu - abs_nu.floor();
1345    let mut neg_buf = alloc_crate::vec![czero; nc];
1346    let (_, status1) = besh::zbesh(z, frac_neg, kind, scaling, &mut neg_buf)?;
1347
1348    let pos_start = nu + T::from_f64(nc as f64);
1349    let pos_n = n - nc;
1350    let mut pos_buf = alloc_crate::vec![czero; pos_n];
1351    let (_, status2) = besh::zbesh(z, pos_start, kind, scaling, &mut pos_buf)?;
1352
1353    let mut values = alloc_crate::vec![czero; n];
1354    for j in 0..nc {
1355        let abs_order = abs_nu - T::from_f64(j as f64);
1356        let k = abs_order.floor().to_usize().unwrap_or(0);
1357        values[j] = reflect_h_element(abs_order, kind, neg_buf[k]);
1358    }
1359    for j in 0..pos_n {
1360        values[nc + j] = pos_buf[j];
1361    }
1362
1363    let status = worse_status(status1, status2);
1364    let uc = recount_underflow(&values);
1365    Ok(BesselResult {
1366        values,
1367        underflow_count: uc,
1368        status,
1369    })
1370}
1371
1372/// J_seq with negative order support.
1373/// J_{-ν} = cos(νπ)·J_ν − sin(νπ)·Y_ν (non-integer)
1374/// J_{-n} = (-1)^n · J_n (integer)
1375#[cfg(feature = "alloc")]
1376#[allow(clippy::needless_range_loop, clippy::manual_memcpy)]
1377fn besselj_seq_neg<T: BesselFloat>(
1378    nu: T,
1379    z: Complex<T>,
1380    n: usize,
1381    scaling: Scaling,
1382) -> Result<BesselResult<T>, Error> {
1383    let zero = T::zero();
1384    let czero = Complex::new(zero, zero);
1385    let abs_nu = nu.abs();
1386    let nc = neg_count(nu, n);
1387    let is_int = as_integer(abs_nu).is_some();
1388
1389    if is_int {
1390        // Integer: J_{-n}(z) = (-1)^n J_n(z). Same lattice.
1391        let last_order = nu + T::from_f64((n - 1) as f64);
1392        let max_abs = if abs_nu > last_order {
1393            abs_nu
1394        } else {
1395            last_order
1396        };
1397        let buf_n = max_abs.to_usize().unwrap_or(0) + 1;
1398        let mut buf = alloc_crate::vec![czero; buf_n];
1399        let (_, status) = besj::zbesj(z, zero, scaling, &mut buf)?;
1400
1401        let mut values = alloc_crate::vec![czero; n];
1402        for j in 0..n {
1403            let order = nu + T::from_f64(j as f64);
1404            let idx = order.abs().to_usize().unwrap_or(0);
1405            values[j] = buf[idx];
1406            if order < zero {
1407                let int_order = order.abs().to_i64().unwrap_or(0);
1408                values[j] = values[j] * integer_sign::<T>(int_order);
1409            }
1410        }
1411        let uc = recount_underflow(&values);
1412        return Ok(BesselResult {
1413            values,
1414            underflow_count: uc,
1415            status,
1416        });
1417    }
1418
1419    if nc == n {
1420        // All negative, non-integer: need J and Y at positive |orders|
1421        let start = abs_nu - T::from_f64((n - 1) as f64);
1422        let mut j_buf = alloc_crate::vec![czero; n];
1423        let mut y_buf = alloc_crate::vec![czero; n];
1424        let (_, s1) = besj::zbesj(z, start, scaling, &mut j_buf)?;
1425        let (_, s2) = besy::zbesy(z, start, scaling, &mut y_buf)?;
1426
1427        let mut values = alloc_crate::vec![czero; n];
1428        // buf[k] = f(start+k). For output j, |order| = abs_nu - j, k = |order| - start = n-1-j
1429        for j in 0..n {
1430            let k = n - 1 - j;
1431            let order = abs_nu - T::from_f64(j as f64);
1432            values[j] = reflect_j_element(order, j_buf[k], y_buf[k]);
1433        }
1434        let uc = recount_underflow(&values);
1435        return Ok(BesselResult {
1436            values,
1437            underflow_count: uc,
1438            status: worse_status(s1, s2),
1439        });
1440    }
1441
1442    // Non-integer crossing: two lattices
1443    let frac_neg = abs_nu - abs_nu.floor();
1444    let mut neg_j = alloc_crate::vec![czero; nc];
1445    let mut neg_y = alloc_crate::vec![czero; nc];
1446    let (_, s1) = besj::zbesj(z, frac_neg, scaling, &mut neg_j)?;
1447    let (_, s2) = besy::zbesy(z, frac_neg, scaling, &mut neg_y)?;
1448
1449    let pos_start = nu + T::from_f64(nc as f64);
1450    let pos_n = n - nc;
1451    let mut pos_buf = alloc_crate::vec![czero; pos_n];
1452    let (_, s3) = besj::zbesj(z, pos_start, scaling, &mut pos_buf)?;
1453
1454    let mut values = alloc_crate::vec![czero; n];
1455    for j in 0..nc {
1456        let abs_order = abs_nu - T::from_f64(j as f64);
1457        let k = abs_order.floor().to_usize().unwrap_or(0);
1458        values[j] = reflect_j_element(abs_order, neg_j[k], neg_y[k]);
1459    }
1460    for j in 0..pos_n {
1461        values[nc + j] = pos_buf[j];
1462    }
1463
1464    let status = worse_status(worse_status(s1, s2), s3);
1465    let uc = recount_underflow(&values);
1466    Ok(BesselResult {
1467        values,
1468        underflow_count: uc,
1469        status,
1470    })
1471}
1472
1473/// Y_seq with negative order support.
1474/// Y_{-ν} = sin(νπ)·J_ν + cos(νπ)·Y_ν (non-integer)
1475/// Y_{-n} = (-1)^n · Y_n (integer)
1476#[cfg(feature = "alloc")]
1477#[allow(clippy::needless_range_loop, clippy::manual_memcpy)]
1478fn bessely_seq_neg<T: BesselFloat>(
1479    nu: T,
1480    z: Complex<T>,
1481    n: usize,
1482    scaling: Scaling,
1483) -> Result<BesselResult<T>, Error> {
1484    let zero = T::zero();
1485    let czero = Complex::new(zero, zero);
1486    let abs_nu = nu.abs();
1487    let nc = neg_count(nu, n);
1488    let is_int = as_integer(abs_nu).is_some();
1489
1490    if is_int {
1491        let last_order = nu + T::from_f64((n - 1) as f64);
1492        let max_abs = if abs_nu > last_order {
1493            abs_nu
1494        } else {
1495            last_order
1496        };
1497        let buf_n = max_abs.to_usize().unwrap_or(0) + 1;
1498        let mut buf = alloc_crate::vec![czero; buf_n];
1499        let (_, status) = besy::zbesy(z, zero, scaling, &mut buf)?;
1500
1501        let mut values = alloc_crate::vec![czero; n];
1502        for j in 0..n {
1503            let order = nu + T::from_f64(j as f64);
1504            let idx = order.abs().to_usize().unwrap_or(0);
1505            values[j] = buf[idx];
1506            if order < zero {
1507                let int_order = order.abs().to_i64().unwrap_or(0);
1508                values[j] = values[j] * integer_sign::<T>(int_order);
1509            }
1510        }
1511        let uc = recount_underflow(&values);
1512        return Ok(BesselResult {
1513            values,
1514            underflow_count: uc,
1515            status,
1516        });
1517    }
1518
1519    if nc == n {
1520        let start = abs_nu - T::from_f64((n - 1) as f64);
1521        let mut j_buf = alloc_crate::vec![czero; n];
1522        let mut y_buf = alloc_crate::vec![czero; n];
1523        let (_, s1) = besj::zbesj(z, start, scaling, &mut j_buf)?;
1524        let (_, s2) = besy::zbesy(z, start, scaling, &mut y_buf)?;
1525
1526        let mut values = alloc_crate::vec![czero; n];
1527        for j in 0..n {
1528            let k = n - 1 - j;
1529            let order = abs_nu - T::from_f64(j as f64);
1530            values[j] = reflect_y_element(order, j_buf[k], y_buf[k]);
1531        }
1532        let uc = recount_underflow(&values);
1533        return Ok(BesselResult {
1534            values,
1535            underflow_count: uc,
1536            status: worse_status(s1, s2),
1537        });
1538    }
1539
1540    // Non-integer crossing: two lattices
1541    let frac_neg = abs_nu - abs_nu.floor();
1542    let mut neg_j = alloc_crate::vec![czero; nc];
1543    let mut neg_y = alloc_crate::vec![czero; nc];
1544    let (_, s1) = besj::zbesj(z, frac_neg, scaling, &mut neg_j)?;
1545    let (_, s2) = besy::zbesy(z, frac_neg, scaling, &mut neg_y)?;
1546
1547    let pos_start = nu + T::from_f64(nc as f64);
1548    let pos_n = n - nc;
1549    let mut pos_buf = alloc_crate::vec![czero; pos_n];
1550    let (_, s3) = besy::zbesy(z, pos_start, scaling, &mut pos_buf)?;
1551
1552    let mut values = alloc_crate::vec![czero; n];
1553    for j in 0..nc {
1554        let abs_order = abs_nu - T::from_f64(j as f64);
1555        let k = abs_order.floor().to_usize().unwrap_or(0);
1556        values[j] = reflect_y_element(abs_order, neg_j[k], neg_y[k]);
1557    }
1558    for j in 0..pos_n {
1559        values[nc + j] = pos_buf[j];
1560    }
1561
1562    let status = worse_status(worse_status(s1, s2), s3);
1563    let uc = recount_underflow(&values);
1564    Ok(BesselResult {
1565        values,
1566        underflow_count: uc,
1567        status,
1568    })
1569}
1570
1571/// I_seq with negative order support.
1572/// I_{-ν} = I_ν + (2/π)sin(νπ)K_ν (non-integer)
1573/// I_{-n} = I_n (integer)
1574#[cfg(feature = "alloc")]
1575#[allow(clippy::needless_range_loop, clippy::manual_memcpy)]
1576fn besseli_seq_neg<T: BesselFloat>(
1577    nu: T,
1578    z: Complex<T>,
1579    n: usize,
1580    scaling: Scaling,
1581) -> Result<BesselResult<T>, Error> {
1582    let zero = T::zero();
1583    let czero = Complex::new(zero, zero);
1584    let abs_nu = nu.abs();
1585    let nc = neg_count(nu, n);
1586    let is_int = as_integer(abs_nu).is_some();
1587
1588    if is_int {
1589        // I_{-n} = I_n. Same lattice, no correction needed.
1590        let last_order = nu + T::from_f64((n - 1) as f64);
1591        let max_abs = if abs_nu > last_order {
1592            abs_nu
1593        } else {
1594            last_order
1595        };
1596        let buf_n = max_abs.to_usize().unwrap_or(0) + 1;
1597        let mut buf = alloc_crate::vec![czero; buf_n];
1598        let (_, status) = besi::zbesi(z, zero, scaling, &mut buf)?;
1599
1600        let mut values = alloc_crate::vec![czero; n];
1601        for j in 0..n {
1602            let order = nu + T::from_f64(j as f64);
1603            let idx = order.abs().to_usize().unwrap_or(0);
1604            values[j] = buf[idx];
1605        }
1606        let uc = recount_underflow(&values);
1607        return Ok(BesselResult {
1608            values,
1609            underflow_count: uc,
1610            status,
1611        });
1612    }
1613
1614    if nc == n {
1615        // All negative, non-integer: need I and K at positive |orders|
1616        let start = abs_nu - T::from_f64((n - 1) as f64);
1617        let mut i_buf = alloc_crate::vec![czero; n];
1618        let mut k_buf = alloc_crate::vec![czero; n];
1619        let (_, s1) = besi::zbesi(z, start, scaling, &mut i_buf)?;
1620        let (_, s2) = besk::zbesk(z, start, scaling, &mut k_buf)?;
1621
1622        let mut values = alloc_crate::vec![czero; n];
1623        for j in 0..n {
1624            let k_idx = n - 1 - j;
1625            let order = abs_nu - T::from_f64(j as f64);
1626            let mut k_val = k_buf[k_idx];
1627            if scaling == Scaling::Exponential {
1628                k_val = k_to_i_scaling_correction(z, k_val);
1629            }
1630            values[j] = reflect_i_element(order, i_buf[k_idx], k_val);
1631        }
1632        let uc = recount_underflow(&values);
1633        return Ok(BesselResult {
1634            values,
1635            underflow_count: uc,
1636            status: worse_status(s1, s2),
1637        });
1638    }
1639
1640    // Non-integer crossing: two lattices
1641    let frac_neg = abs_nu - abs_nu.floor();
1642    let mut neg_i = alloc_crate::vec![czero; nc];
1643    let mut neg_k = alloc_crate::vec![czero; nc];
1644    let (_, s1) = besi::zbesi(z, frac_neg, scaling, &mut neg_i)?;
1645    let (_, s2) = besk::zbesk(z, frac_neg, scaling, &mut neg_k)?;
1646
1647    let pos_start = nu + T::from_f64(nc as f64);
1648    let pos_n = n - nc;
1649    let mut pos_buf = alloc_crate::vec![czero; pos_n];
1650    let (_, s3) = besi::zbesi(z, pos_start, scaling, &mut pos_buf)?;
1651
1652    let mut values = alloc_crate::vec![czero; n];
1653    for j in 0..nc {
1654        let abs_order = abs_nu - T::from_f64(j as f64);
1655        let k_idx = abs_order.floor().to_usize().unwrap_or(0);
1656        let mut k_val = neg_k[k_idx];
1657        if scaling == Scaling::Exponential {
1658            k_val = k_to_i_scaling_correction(z, k_val);
1659        }
1660        values[j] = reflect_i_element(abs_order, neg_i[k_idx], k_val);
1661    }
1662    for j in 0..pos_n {
1663        values[nc + j] = pos_buf[j];
1664    }
1665
1666    let status = worse_status(worse_status(s1, s2), s3);
1667    let uc = recount_underflow(&values);
1668    Ok(BesselResult {
1669        values,
1670        underflow_count: uc,
1671        status,
1672    })
1673}
1674
1675#[cfg(feature = "alloc")]
1676/// Compute J<sub>ν+j</sub>(z) for j = 0, 1, …, n−1 in a single call.
1677///
1678/// Returns a [`BesselResult`] containing `n` values and an [`Accuracy`]:
1679/// - [`Accuracy::Normal`] — no significant precision loss
1680/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| or |ν| very large)
1681///
1682/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1683/// see [crate-level docs](crate#exponential-scaling) for details.
1684///
1685/// Supports negative ν via the same reflection formula as [`besselj`].
1686///
1687/// # Example
1688///
1689/// ```
1690/// # #[cfg(feature = "alloc")] {
1691/// use complex_bessel::*;
1692/// use num_complex::Complex;
1693///
1694/// let z = Complex::new(1.0_f64, 0.0);
1695///
1696/// // J_0(z), J_1(z), J_2(z) in one call
1697/// let result = besselj_seq(0.0, z, 3, Scaling::Unscaled).unwrap();
1698/// assert_eq!(result.values.len(), 3);
1699/// assert!((result.values[0].re - 0.7652).abs() < 1e-3); // J_0(1) ≈ 0.7652
1700/// # }
1701/// ```
1702///
1703/// # Errors
1704///
1705/// - [`Error::InvalidInput`] if n < 1, or if z = 0 and ν is a negative non-integer.
1706/// - [`Error::Overflow`] if |z| or |ν| is too large for a finite result.
1707/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
1708/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1709pub fn besselj_seq<T: BesselFloat>(
1710    nu: T,
1711    z: Complex<T>,
1712    n: usize,
1713    scaling: Scaling,
1714) -> Result<BesselResult<T>, Error> {
1715    if nu < T::zero() {
1716        return besselj_seq_neg(nu, z, n, scaling);
1717    }
1718    seq_helper(n, |y| besj::zbesj(z, nu, scaling, y))
1719}
1720
1721#[cfg(feature = "alloc")]
1722/// Compute Y<sub>ν+j</sub>(z) for j = 0, 1, …, n−1 in a single call.
1723///
1724/// Returns a [`BesselResult`] containing `n` values and an [`Accuracy`]:
1725/// - [`Accuracy::Normal`] — no significant precision loss
1726/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| or |ν| very large)
1727///
1728/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1729/// see [crate-level docs](crate#exponential-scaling) for details.
1730///
1731/// Supports negative ν via the same reflection formula as [`bessely`].
1732///
1733/// # Example
1734///
1735/// ```
1736/// # #[cfg(feature = "alloc")] {
1737/// use complex_bessel::*;
1738/// use num_complex::Complex;
1739///
1740/// let z = Complex::new(1.0_f64, 0.0);
1741///
1742/// // Y_0(z), Y_1(z), Y_2(z) in one call
1743/// let result = bessely_seq(0.0, z, 3, Scaling::Unscaled).unwrap();
1744/// assert_eq!(result.values.len(), 3);
1745/// assert!((result.values[0].re - 0.0883).abs() < 1e-3); // Y_0(1) ≈ 0.0883
1746/// # }
1747/// ```
1748///
1749/// # Errors
1750///
1751/// - [`Error::InvalidInput`] if n < 1, or if z = 0.
1752/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
1753/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
1754/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1755pub fn bessely_seq<T: BesselFloat>(
1756    nu: T,
1757    z: Complex<T>,
1758    n: usize,
1759    scaling: Scaling,
1760) -> Result<BesselResult<T>, Error> {
1761    if nu < T::zero() {
1762        return bessely_seq_neg(nu, z, n, scaling);
1763    }
1764    seq_helper(n, |y| besy::zbesy(z, nu, scaling, y))
1765}
1766
1767#[cfg(feature = "alloc")]
1768/// Compute I<sub>ν+j</sub>(z) for j = 0, 1, …, n−1 in a single call.
1769///
1770/// Returns a [`BesselResult`] containing `n` values and an [`Accuracy`]:
1771/// - [`Accuracy::Normal`] — no significant precision loss
1772/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| or |ν| very large)
1773///
1774/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1775/// see [crate-level docs](crate#exponential-scaling) for details.
1776///
1777/// Supports negative ν via the same reflection formula as [`besseli`].
1778///
1779/// # Example
1780///
1781/// ```
1782/// # #[cfg(feature = "alloc")] {
1783/// use complex_bessel::*;
1784/// use num_complex::Complex;
1785///
1786/// let z = Complex::new(1.0_f64, 0.0);
1787///
1788/// // I_0(z), I_1(z), I_2(z) in one call
1789/// let result = besseli_seq(0.0, z, 3, Scaling::Unscaled).unwrap();
1790/// assert_eq!(result.values.len(), 3);
1791/// assert!((result.values[0].re - 1.2661).abs() < 1e-3); // I_0(1) ≈ 1.2661
1792/// # }
1793/// ```
1794///
1795/// # Errors
1796///
1797/// - [`Error::InvalidInput`] if n < 1, or if z = 0 and ν is a negative non-integer.
1798/// - [`Error::Overflow`] if |z| or |ν| is too large for a finite result.
1799/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
1800/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1801pub fn besseli_seq<T: BesselFloat>(
1802    nu: T,
1803    z: Complex<T>,
1804    n: usize,
1805    scaling: Scaling,
1806) -> Result<BesselResult<T>, Error> {
1807    if nu < T::zero() {
1808        return besseli_seq_neg(nu, z, n, scaling);
1809    }
1810    seq_helper(n, |y| besi::zbesi(z, nu, scaling, y))
1811}
1812
1813#[cfg(feature = "alloc")]
1814/// Compute K<sub>ν+j</sub>(z) for j = 0, 1, …, n−1 in a single call.
1815///
1816/// Returns a [`BesselResult`] containing `n` values and an [`Accuracy`]:
1817/// - [`Accuracy::Normal`] — no significant precision loss
1818/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| or |ν| very large)
1819///
1820/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1821/// see [crate-level docs](crate#exponential-scaling) for details.
1822///
1823/// Supports negative ν via the same reflection formula as [`besselk`].
1824///
1825/// # Example
1826///
1827/// ```
1828/// # #[cfg(feature = "alloc")] {
1829/// use complex_bessel::*;
1830/// use num_complex::Complex;
1831///
1832/// let z = Complex::new(1.0_f64, 0.0);
1833///
1834/// // K_0(z), K_1(z), K_2(z) in one call
1835/// let result = besselk_seq(0.0, z, 3, Scaling::Unscaled).unwrap();
1836/// assert_eq!(result.values.len(), 3);
1837/// assert!((result.values[0].re - 0.4211).abs() < 1e-3); // K_0(1) ≈ 0.4211
1838/// # }
1839/// ```
1840///
1841/// # Errors
1842///
1843/// - [`Error::InvalidInput`] if n < 1, or if z = 0.
1844/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
1845/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
1846/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1847pub fn besselk_seq<T: BesselFloat>(
1848    nu: T,
1849    z: Complex<T>,
1850    n: usize,
1851    scaling: Scaling,
1852) -> Result<BesselResult<T>, Error> {
1853    if nu < T::zero() {
1854        return besselk_seq_neg(nu, z, n, scaling);
1855    }
1856    seq_helper(n, |y| besk::zbesk(z, nu, scaling, y))
1857}
1858
1859#[cfg(feature = "alloc")]
1860/// Compute H<sub>ν+j</sub><sup>(1)</sup>(z) for j = 0, 1, …, n−1 in a single call.
1861///
1862/// Returns a [`BesselResult`] containing `n` values and an [`Accuracy`]:
1863/// - [`Accuracy::Normal`] — no significant precision loss
1864/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| or |ν| very large)
1865///
1866/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1867/// see [crate-level docs](crate#exponential-scaling) for details.
1868///
1869/// Supports negative ν via the same reflection formula as [`hankel1`].
1870///
1871/// # Example
1872///
1873/// ```
1874/// # #[cfg(feature = "alloc")] {
1875/// use complex_bessel::*;
1876/// use num_complex::Complex;
1877///
1878/// let z = Complex::new(1.0_f64, 0.0);
1879///
1880/// // H^(1)_0(z), H^(1)_1(z) in one call
1881/// let result = hankel1_seq(0.0, z, 2, Scaling::Unscaled).unwrap();
1882/// assert_eq!(result.values.len(), 2);
1883/// assert!((result.values[0].im - 0.0883).abs() < 1e-3); // Im = Y_0(1) ≈ 0.0883
1884/// # }
1885/// ```
1886///
1887/// # Errors
1888///
1889/// - [`Error::InvalidInput`] if n < 1, or if z = 0.
1890/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
1891/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
1892/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1893pub fn hankel1_seq<T: BesselFloat>(
1894    nu: T,
1895    z: Complex<T>,
1896    n: usize,
1897    scaling: Scaling,
1898) -> Result<BesselResult<T>, Error> {
1899    if nu < T::zero() {
1900        return hankel_seq_neg(HankelKind::First, nu, z, n, scaling);
1901    }
1902    seq_helper(n, |y| besh::zbesh(z, nu, HankelKind::First, scaling, y))
1903}
1904
1905#[cfg(feature = "alloc")]
1906/// Compute H<sub>ν+j</sub><sup>(2)</sup>(z) for j = 0, 1, …, n−1 in a single call.
1907///
1908/// Returns a [`BesselResult`] containing `n` values and an [`Accuracy`]:
1909/// - [`Accuracy::Normal`] — no significant precision loss
1910/// - [`Accuracy::Reduced`] — more than half of significant digits may be lost (|z| or |ν| very large)
1911///
1912/// The `scaling` parameter selects [`Scaling::Unscaled`] or [`Scaling::Exponential`];
1913/// see [crate-level docs](crate#exponential-scaling) for details.
1914///
1915/// Supports negative ν via the same reflection formula as [`hankel2`].
1916///
1917/// # Example
1918///
1919/// ```
1920/// # #[cfg(feature = "alloc")] {
1921/// use complex_bessel::*;
1922/// use num_complex::Complex;
1923///
1924/// let z = Complex::new(1.0_f64, 0.0);
1925///
1926/// // H^(2)_0(z), H^(2)_1(z) in one call
1927/// let result = hankel2_seq(0.0, z, 2, Scaling::Unscaled).unwrap();
1928/// assert_eq!(result.values.len(), 2);
1929/// assert!((result.values[0].im + 0.0883).abs() < 1e-3); // Im = -Y_0(1) ≈ -0.0883
1930/// # }
1931/// ```
1932///
1933/// # Errors
1934///
1935/// - [`Error::InvalidInput`] if n < 1, or if z = 0.
1936/// - [`Error::Overflow`] if |z| is too small or too large for a finite result.
1937/// - [`Error::TotalPrecisionLoss`] if |z| or |ν| is too large for any significant digits (roughly > 10⁹ for f64).
1938/// - [`Error::ConvergenceFailure`] if an internal series or recurrence does not converge (rare).
1939pub fn hankel2_seq<T: BesselFloat>(
1940    nu: T,
1941    z: Complex<T>,
1942    n: usize,
1943    scaling: Scaling,
1944) -> Result<BesselResult<T>, Error> {
1945    if nu < T::zero() {
1946        return hankel_seq_neg(HankelKind::Second, nu, z, n, scaling);
1947    }
1948    seq_helper(n, |y| besh::zbesh(z, nu, HankelKind::Second, scaling, y))
1949}
1950
1951// ── Tests for negative order _seq functions ──
1952
1953#[cfg(all(test, feature = "alloc"))]
1954mod neg_order_seq_tests {
1955    use super::*;
1956    use num_complex::Complex64;
1957
1958    const TOL: f64 = 2e-14;
1959
1960    fn rel_err(a: Complex64, b: Complex64) -> f64 {
1961        let diff = (a - b).norm();
1962        let mag = a.norm().max(b.norm());
1963        if mag == 0.0 { diff } else { diff / mag }
1964    }
1965
1966    /// Verify _seq(ν, z, n)[j] == single_value(ν+j, z) for all j.
1967    fn check_seq_vs_single<F, G>(
1968        seq_fn: F,
1969        single_fn: G,
1970        nu: f64,
1971        z: Complex64,
1972        n: usize,
1973        scaling: Scaling,
1974        tol: f64,
1975        label: &str,
1976    ) where
1977        F: FnOnce(f64, Complex64, usize, Scaling) -> Result<BesselResult<f64>, Error>,
1978        G: Fn(f64, Complex64) -> Result<Complex64, Error>,
1979    {
1980        let result = seq_fn(nu, z, n, scaling).unwrap();
1981        assert_eq!(result.values.len(), n, "{label}: wrong length");
1982        for j in 0..n {
1983            let order = nu + j as f64;
1984            let expected = single_fn(order, z).unwrap();
1985            let err = rel_err(result.values[j], expected);
1986            assert!(
1987                err < tol,
1988                "{label}: order={order}, seq={:?}, single={expected:?}, rel_err={err}",
1989                result.values[j]
1990            );
1991        }
1992    }
1993
1994    // ── K (even function) ──
1995
1996    #[test]
1997    fn besselk_seq_all_negative_nonint() {
1998        let z = Complex64::new(1.5, 0.5);
1999        check_seq_vs_single(
2000            besselk_seq,
2001            |nu, z| besselk(nu, z),
2002            -3.7,
2003            z,
2004            3,
2005            Scaling::Unscaled,
2006            TOL,
2007            "K all neg",
2008        );
2009    }
2010
2011    #[test]
2012    fn besselk_seq_crossing_nonint() {
2013        let z = Complex64::new(1.5, 0.5);
2014        check_seq_vs_single(
2015            besselk_seq,
2016            |nu, z| besselk(nu, z),
2017            -1.5,
2018            z,
2019            5,
2020            Scaling::Unscaled,
2021            TOL,
2022            "K crossing nonint",
2023        );
2024    }
2025
2026    #[test]
2027    fn besselk_seq_int_negative() {
2028        let z = Complex64::new(1.5, 0.5);
2029        check_seq_vs_single(
2030            besselk_seq,
2031            |nu, z| besselk(nu, z),
2032            -3.0,
2033            z,
2034            4,
2035            Scaling::Unscaled,
2036            TOL,
2037            "K int neg",
2038        );
2039    }
2040
2041    #[test]
2042    fn besselk_seq_int_crossing() {
2043        let z = Complex64::new(1.5, 0.5);
2044        check_seq_vs_single(
2045            besselk_seq,
2046            |nu, z| besselk(nu, z),
2047            -2.0,
2048            z,
2049            5,
2050            Scaling::Unscaled,
2051            TOL,
2052            "K int cross",
2053        );
2054    }
2055
2056    #[test]
2057    fn besselk_seq_neg_zero() {
2058        let z = Complex64::new(1.0, 0.0);
2059        check_seq_vs_single(
2060            besselk_seq,
2061            |nu, z| besselk(nu, z),
2062            -0.0,
2063            z,
2064            1,
2065            Scaling::Unscaled,
2066            TOL,
2067            "K neg zero",
2068        );
2069    }
2070
2071    // ── H^(1) ──
2072
2073    #[test]
2074    fn hankel1_seq_all_negative_nonint() {
2075        let z = Complex64::new(1.5, 0.5);
2076        check_seq_vs_single(
2077            hankel1_seq,
2078            |nu, z| hankel1(nu, z),
2079            -3.7,
2080            z,
2081            3,
2082            Scaling::Unscaled,
2083            TOL,
2084            "H1 all neg",
2085        );
2086    }
2087
2088    #[test]
2089    fn hankel1_seq_crossing_nonint() {
2090        let z = Complex64::new(1.5, 0.5);
2091        check_seq_vs_single(
2092            hankel1_seq,
2093            |nu, z| hankel1(nu, z),
2094            -1.5,
2095            z,
2096            5,
2097            Scaling::Unscaled,
2098            TOL,
2099            "H1 crossing",
2100        );
2101    }
2102
2103    #[test]
2104    fn hankel1_seq_int_crossing() {
2105        let z = Complex64::new(1.5, 0.5);
2106        check_seq_vs_single(
2107            hankel1_seq,
2108            |nu, z| hankel1(nu, z),
2109            -2.0,
2110            z,
2111            5,
2112            Scaling::Unscaled,
2113            TOL,
2114            "H1 int cross",
2115        );
2116    }
2117
2118    // ── H^(2) ──
2119
2120    #[test]
2121    fn hankel2_seq_all_negative_nonint() {
2122        let z = Complex64::new(1.5, 0.5);
2123        check_seq_vs_single(
2124            hankel2_seq,
2125            |nu, z| hankel2(nu, z),
2126            -3.7,
2127            z,
2128            3,
2129            Scaling::Unscaled,
2130            TOL,
2131            "H2 all neg",
2132        );
2133    }
2134
2135    #[test]
2136    fn hankel2_seq_crossing_nonint() {
2137        let z = Complex64::new(1.5, 0.5);
2138        check_seq_vs_single(
2139            hankel2_seq,
2140            |nu, z| hankel2(nu, z),
2141            -1.5,
2142            z,
2143            5,
2144            Scaling::Unscaled,
2145            TOL,
2146            "H2 crossing",
2147        );
2148    }
2149
2150    #[test]
2151    fn hankel2_seq_int_crossing() {
2152        let z = Complex64::new(1.5, 0.5);
2153        check_seq_vs_single(
2154            hankel2_seq,
2155            |nu, z| hankel2(nu, z),
2156            -2.0,
2157            z,
2158            5,
2159            Scaling::Unscaled,
2160            TOL,
2161            "H2 int cross",
2162        );
2163    }
2164
2165    // ── J ──
2166
2167    #[test]
2168    fn besselj_seq_all_negative_nonint() {
2169        let z = Complex64::new(1.5, 0.5);
2170        check_seq_vs_single(
2171            besselj_seq,
2172            |nu, z| besselj(nu, z),
2173            -3.7,
2174            z,
2175            3,
2176            Scaling::Unscaled,
2177            TOL,
2178            "J all neg",
2179        );
2180    }
2181
2182    #[test]
2183    fn besselj_seq_crossing_nonint() {
2184        let z = Complex64::new(1.5, 0.5);
2185        check_seq_vs_single(
2186            besselj_seq,
2187            |nu, z| besselj(nu, z),
2188            -1.5,
2189            z,
2190            5,
2191            Scaling::Unscaled,
2192            TOL,
2193            "J crossing",
2194        );
2195    }
2196
2197    #[test]
2198    fn besselj_seq_int_negative() {
2199        let z = Complex64::new(1.5, 0.5);
2200        check_seq_vs_single(
2201            besselj_seq,
2202            |nu, z| besselj(nu, z),
2203            -3.0,
2204            z,
2205            4,
2206            Scaling::Unscaled,
2207            TOL,
2208            "J int neg",
2209        );
2210    }
2211
2212    #[test]
2213    fn besselj_seq_int_crossing() {
2214        let z = Complex64::new(1.5, 0.5);
2215        check_seq_vs_single(
2216            besselj_seq,
2217            |nu, z| besselj(nu, z),
2218            -2.0,
2219            z,
2220            5,
2221            Scaling::Unscaled,
2222            TOL,
2223            "J int cross",
2224        );
2225    }
2226
2227    // ── Y ──
2228
2229    #[test]
2230    fn bessely_seq_all_negative_nonint() {
2231        let z = Complex64::new(1.5, 0.5);
2232        check_seq_vs_single(
2233            bessely_seq,
2234            |nu, z| bessely(nu, z),
2235            -3.7,
2236            z,
2237            3,
2238            Scaling::Unscaled,
2239            TOL,
2240            "Y all neg",
2241        );
2242    }
2243
2244    #[test]
2245    fn bessely_seq_crossing_nonint() {
2246        let z = Complex64::new(1.5, 0.5);
2247        check_seq_vs_single(
2248            bessely_seq,
2249            |nu, z| bessely(nu, z),
2250            -1.5,
2251            z,
2252            5,
2253            Scaling::Unscaled,
2254            TOL,
2255            "Y crossing",
2256        );
2257    }
2258
2259    #[test]
2260    fn bessely_seq_int_negative() {
2261        let z = Complex64::new(1.5, 0.5);
2262        check_seq_vs_single(
2263            bessely_seq,
2264            |nu, z| bessely(nu, z),
2265            -3.0,
2266            z,
2267            4,
2268            Scaling::Unscaled,
2269            TOL,
2270            "Y int neg",
2271        );
2272    }
2273
2274    #[test]
2275    fn bessely_seq_int_crossing() {
2276        let z = Complex64::new(1.5, 0.5);
2277        check_seq_vs_single(
2278            bessely_seq,
2279            |nu, z| bessely(nu, z),
2280            -2.0,
2281            z,
2282            5,
2283            Scaling::Unscaled,
2284            TOL,
2285            "Y int cross",
2286        );
2287    }
2288
2289    // ── I ──
2290
2291    #[test]
2292    fn besseli_seq_all_negative_nonint() {
2293        let z = Complex64::new(1.5, 0.5);
2294        check_seq_vs_single(
2295            besseli_seq,
2296            |nu, z| besseli(nu, z),
2297            -3.7,
2298            z,
2299            3,
2300            Scaling::Unscaled,
2301            TOL,
2302            "I all neg",
2303        );
2304    }
2305
2306    #[test]
2307    fn besseli_seq_crossing_nonint() {
2308        let z = Complex64::new(1.5, 0.5);
2309        check_seq_vs_single(
2310            besseli_seq,
2311            |nu, z| besseli(nu, z),
2312            -1.5,
2313            z,
2314            5,
2315            Scaling::Unscaled,
2316            TOL,
2317            "I crossing",
2318        );
2319    }
2320
2321    #[test]
2322    fn besseli_seq_int_negative() {
2323        let z = Complex64::new(1.5, 0.5);
2324        check_seq_vs_single(
2325            besseli_seq,
2326            |nu, z| besseli(nu, z),
2327            -3.0,
2328            z,
2329            4,
2330            Scaling::Unscaled,
2331            TOL,
2332            "I int neg",
2333        );
2334    }
2335
2336    #[test]
2337    fn besseli_seq_int_crossing() {
2338        let z = Complex64::new(1.5, 0.5);
2339        check_seq_vs_single(
2340            besseli_seq,
2341            |nu, z| besseli(nu, z),
2342            -2.0,
2343            z,
2344            5,
2345            Scaling::Unscaled,
2346            TOL,
2347            "I int cross",
2348        );
2349    }
2350
2351    // ── Scaled variants ──
2352
2353    #[test]
2354    fn besseli_seq_scaled_negative_nonint() {
2355        let z = Complex64::new(2.0, 1.0);
2356        check_seq_vs_single(
2357            besseli_seq,
2358            |nu, z| besseli_scaled(nu, z),
2359            -2.3,
2360            z,
2361            5,
2362            Scaling::Exponential,
2363            TOL,
2364            "I scaled neg",
2365        );
2366    }
2367
2368    #[test]
2369    fn besselj_seq_scaled_crossing() {
2370        let z = Complex64::new(2.0, 1.0);
2371        check_seq_vs_single(
2372            besselj_seq,
2373            |nu, z| besselj_scaled(nu, z),
2374            -1.5,
2375            z,
2376            4,
2377            Scaling::Exponential,
2378            TOL,
2379            "J scaled cross",
2380        );
2381    }
2382
2383    #[test]
2384    fn besselk_seq_scaled_negative() {
2385        let z = Complex64::new(2.0, 1.0);
2386        check_seq_vs_single(
2387            besselk_seq,
2388            |nu, z| besselk_scaled(nu, z),
2389            -2.5,
2390            z,
2391            4,
2392            Scaling::Exponential,
2393            TOL,
2394            "K scaled neg",
2395        );
2396    }
2397
2398    #[test]
2399    fn hankel1_seq_scaled_negative() {
2400        let z = Complex64::new(2.0, 1.0);
2401        check_seq_vs_single(
2402            hankel1_seq,
2403            |nu, z| hankel1_scaled(nu, z),
2404            -2.5,
2405            z,
2406            4,
2407            Scaling::Exponential,
2408            TOL,
2409            "H1 scaled neg",
2410        );
2411    }
2412
2413    #[test]
2414    fn bessely_seq_scaled_crossing() {
2415        let z = Complex64::new(2.0, 1.0);
2416        check_seq_vs_single(
2417            bessely_seq,
2418            |nu, z| bessely_scaled(nu, z),
2419            -1.5,
2420            z,
2421            4,
2422            Scaling::Exponential,
2423            TOL,
2424            "Y scaled cross",
2425        );
2426    }
2427
2428    // ── Edge: -0.0 ──
2429
2430    #[test]
2431    fn besselj_seq_neg_zero() {
2432        let z = Complex64::new(1.0, 0.0);
2433        check_seq_vs_single(
2434            besselj_seq,
2435            |nu, z| besselj(nu, z),
2436            -0.0,
2437            z,
2438            3,
2439            Scaling::Unscaled,
2440            TOL,
2441            "J neg zero",
2442        );
2443    }
2444}