#![allow(clippy::too_many_arguments)]
use num_complex::Complex;
use crate::algo::uni1::zuni1;
use crate::algo::uni2::zuni2;
use crate::machine::BesselFloat;
use crate::types::Scaling;
use crate::utils::{mul_add_scalar, reciprocal_z, zabs};
#[derive(Debug, Clone, Copy)]
pub(crate) struct BuniOutput {
pub nz: i32,
pub nlast: usize,
}
pub(crate) fn zbuni<T: BesselFloat>(
z: Complex<T>,
fnu: T,
kode: Scaling,
y: &mut [Complex<T>],
nui: usize,
fnul: T,
tol: T,
elim: T,
alim: T,
) -> BuniOutput {
let zero = T::zero();
let one = T::one();
let czero = Complex::new(zero, zero);
let n = y.len();
y.fill(czero);
let nz: i32 = 0;
let ax = z.re.abs() * T::from_f64(1.7321);
let ay = z.im.abs();
let iform: i32 = if ay > ax { 2 } else { 1 };
if nui == 0 {
let (nw, nlast) = if iform == 1 {
let result = zuni1(z, fnu, kode, y, fnul, tol, elim, alim);
(result.nz, result.nlast as usize)
} else {
let result = zuni2(z, fnu, kode, y, fnul, tol, elim, alim);
(result.nz, result.nlast as usize)
};
if nw < 0 {
let nz_out = if nw == -2 { -2 } else { -1 };
return BuniOutput {
nz: nz_out,
nlast: 0,
};
}
return BuniOutput { nz: nw, nlast };
}
let fnui = T::from_f64(nui as f64);
let dfnu = fnu + T::from_f64((n - 1) as f64);
let gnu = dfnu + fnui;
let mut cy = [czero; 2];
let (_nlast_val, nw) = if iform == 1 {
let result = zuni1(z, gnu, kode, &mut cy, fnul, tol, elim, alim);
(result.nlast as usize, result.nz)
} else {
let result = zuni2(z, gnu, kode, &mut cy, fnul, tol, elim, alim);
(result.nlast as usize, result.nz)
};
if nw < 0 {
let nz_out = if nw == -2 { -2 } else { -1 };
return BuniOutput {
nz: nz_out,
nlast: 0,
};
}
if nw != 0 {
return BuniOutput { nz: 0, nlast: n };
}
let str_val = zabs(cy[0]);
let bry0 = T::from_f64(1.0e3) * T::MACH_TINY / tol;
let bry1 = one / bry0;
let (mut iflag, mut ascle, mut csclr) = if str_val > bry0 {
if str_val < bry1 {
(2, bry1, one)
} else {
(3, bry1, tol) }
} else {
(1, bry0, one / tol)
};
let mut cscrr = one / csclr;
let mut s1 = cy[1] * csclr;
let mut s2 = cy[0] * csclr;
let rz = reciprocal_z(z);
let mut fnui_val = fnui;
for _i in 0..nui {
let prev = s2;
let cfn = dfnu + fnui_val;
s2 = mul_add_scalar(rz * prev, cfn, s1);
s1 = prev;
fnui_val = fnui_val - one;
if iflag >= 3 {
continue;
}
let c2_scaled = s2 * cscrr;
let c1m = c2_scaled.re.abs().max(c2_scaled.im.abs());
if c1m <= ascle {
continue;
}
iflag += 1;
ascle = bry1; s1 = s1 * cscrr;
s2 = c2_scaled;
csclr = csclr * tol;
cscrr = one / csclr;
s1 = s1 * csclr;
s2 = s2 * csclr;
}
y[n - 1] = s2 * cscrr;
if n == 1 {
return BuniOutput { nz, nlast: 0 };
}
let nl = n - 1;
let mut fnui_val = T::from_f64(nl as f64);
let mut k = nl; for _i in 0..nl {
let prev = s2;
let cfn = fnu + fnui_val;
s2 = mul_add_scalar(rz * prev, cfn, s1);
s1 = prev;
let c2_scaled = s2 * cscrr;
y[k - 1] = c2_scaled;
fnui_val = fnui_val - one;
k -= 1;
if iflag >= 3 {
continue;
}
let c1m = c2_scaled.re.abs().max(c2_scaled.im.abs());
if c1m <= ascle {
continue;
}
iflag += 1;
ascle = bry1; s1 = s1 * cscrr;
s2 = c2_scaled * csclr;
csclr = csclr * tol;
cscrr = one / csclr;
s1 = s1 * csclr;
}
BuniOutput { nz, nlast: 0 }
}