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