use num_complex::Complex;
use crate::algo::bknu::zbknu;
use crate::algo::rati::zrati;
use crate::machine::BesselFloat;
use crate::types::{Error, Scaling};
use crate::utils::{mul_add, zabs, zdiv};
pub(crate) fn zwrsk<T: BesselFloat>(
z: Complex<T>,
fnu: T,
kode: Scaling,
y: &mut [Complex<T>],
tol: T,
elim: T,
alim: T,
) -> Result<usize, Error> {
let zero = T::zero();
let one = T::one();
let czero = Complex::new(zero, zero);
let nz: usize = 0;
let n = y.len();
let mut cw = [czero; 2];
let nw = zbknu(z, fnu, kode, &mut cw, tol, elim, alim)?;
if nw != 0 {
return Err(Error::Overflow);
}
zrati(z, fnu, y, tol);
let mut cinu = match kode {
Scaling::Unscaled => Complex::from(one),
Scaling::Exponential => Complex::new(z.im.cos(), z.im.sin()),
};
let acw = zabs(cw[1]);
let ascle = T::from_f64(1.0e3) * T::MACH_TINY / tol;
let csclr;
if acw <= ascle {
csclr = one / tol; } else {
let ascle_inv = one / ascle;
if acw >= ascle_inv {
csclr = tol; } else {
csclr = one; }
}
let c1 = cw[0] * csclr;
let c2 = cw[1] * csclr;
let mut ratio_saved = y[0];
let pt = mul_add(ratio_saved, c1, c2);
let ct = z * pt;
cinu = zdiv(cinu, ct);
y[0] = cinu * csclr;
if n == 1 {
return Ok(nz);
}
for item in y.iter_mut().skip(1) {
cinu = ratio_saved * cinu;
ratio_saved = *item; *item = cinu * csclr;
}
Ok(nz)
}
#[cfg(test)]
mod tests {
use super::*;
use num_complex::Complex64;
#[test]
fn wrsk_wronskian_identity() {
let z = Complex64::new(2.0, 1.0);
let fnu = 0.5;
let tol = f64::tol();
let elim = f64::elim();
let alim = f64::alim();
let czero = Complex64::new(0.0, 0.0);
let mut i_vals = [czero; 2];
let mut k_vals = [czero; 2];
zwrsk(z, fnu, Scaling::Unscaled, &mut i_vals, tol, elim, alim).unwrap();
zbknu(z, fnu, Scaling::Unscaled, &mut k_vals, tol, elim, alim).unwrap();
let wronskian = i_vals[0] * k_vals[1] + i_vals[1] * k_vals[0];
let expected = Complex64::new(1.0, 0.0) / z;
let err = (wronskian - expected).norm() / expected.norm();
assert!(err < 1e-13, "Wronskian error = {err:.2e}");
}
#[test]
fn wrsk_i0_real_positive() {
let z = Complex64::new(1.0, 0.0);
let tol = f64::tol();
let elim = f64::elim();
let alim = f64::alim();
let mut y = [Complex64::new(0.0, 0.0)];
let nz = zwrsk(z, 0.0, Scaling::Unscaled, &mut y, tol, elim, alim).unwrap();
assert_eq!(nz, 0);
let err = (y[0].re - 1.2660658777520084).abs();
assert!(err < 1e-13, "I(0,1) error = {err:.2e}");
assert!(y[0].im.abs() < 1e-14);
}
#[test]
fn wrsk_sequence() {
let z = Complex64::new(2.0, 0.0);
let tol = f64::tol();
let elim = f64::elim();
let alim = f64::alim();
let mut y = [Complex64::new(0.0, 0.0); 3];
zwrsk(z, 0.0, Scaling::Unscaled, &mut y, tol, elim, alim).unwrap();
let err0 = (y[0].re - 2.2795853023360673).abs();
assert!(err0 < 1e-13, "I(0,2) error = {err0:.2e}");
let err1 = (y[1].re - 1.590_636_854_637_329).abs();
assert!(err1 < 1e-13, "I(1,2) error = {err1:.2e}");
let err2 = (y[2].re - 0.688_948_447_698_738_1).abs();
assert!(err2 < 1e-13, "I(2,2) error = {err2:.2e}");
}
}