#![allow(clippy::excessive_precision)]
#![allow(clippy::approx_constant)]
use num_complex::Complex;
use crate::algo::gamln::gamln;
use crate::algo::kscl::zkscl;
use crate::algo::shch::zshch;
use crate::algo::uchk::zuchk;
use crate::machine::BesselFloat;
use crate::types::{Error, Scaling};
use crate::utils::{mul_add, reciprocal_z, zabs, zdiv};
const KMAX: i32 = 30;
const R1: f64 = 2.0;
const RTHPI: f64 = 1.25331413731550025; const SPI: f64 = 1.90985931710274403; const FPI: f64 = 1.89769999331517738;
use crate::algo::constants::{HPI, PI, R1M5, TTH};
#[rustfmt::skip]
const CC: [f64; 8] = [
5.77215664901532861e-01,
-4.20026350340952355e-02,
-4.21977345555443367e-02,
7.21894324666309954e-03,
-2.15241674114950973e-04,
-2.01348547807882387e-05,
1.13302723198169588e-06,
6.11609510448141582e-09,
];
pub(crate) fn zbknu<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 two = T::from_f64(2.0);
let half = T::from_f64(0.5);
let czero = Complex::new(zero, zero);
let n = y.len();
y.fill(czero);
let caz = zabs(z);
let csclr = one / tol;
let crscr = tol;
let cssr = [csclr, one, crscr];
let csrr = [crscr, one, csclr];
let bry = [
T::from_f64(1.0e3) * T::MACH_TINY / tol,
one / (T::from_f64(1.0e3) * T::MACH_TINY / tol),
T::MACH_HUGE,
];
let mut nz: usize = 0;
let mut iflag = 0_i32;
let mut koded = kode;
let rz = reciprocal_z(z);
let inu = (fnu + half).floor().to_i32().unwrap();
let dnu = fnu - T::from_f64(inu as f64);
let dnu2 = if dnu.abs() > tol { dnu * dnu } else { zero };
if dnu.abs() != half && caz <= T::from_f64(R1) {
let mut fc = one;
let rz_log = rz.ln();
let fmu = rz_log * dnu;
let (csh, cch) = zshch(fmu);
let smu;
if dnu != zero {
fc = dnu * T::from_f64(PI);
fc = fc / fc.sin();
smu = csh / dnu;
} else {
smu = rz_log;
}
let a2 = one + dnu;
let t2 = (-gamln(a2)?).exp();
let t1 = one / (t2 * fc);
let g1;
if dnu.abs() > T::from_f64(0.1) {
g1 = (t1 - t2) / (dnu + dnu);
} else {
let mut ak = one;
let mut s = T::from_f64(CC[0]);
for cc_k in &CC[1..] {
ak = ak * dnu2;
let tm = T::from_f64(*cc_k) * ak;
s = s + tm;
if tm.abs() < tol {
break;
}
}
g1 = -s;
}
let g2 = (t1 + t2) * half;
let efmu = fmu.exp();
let mut f = (cch * g1 + smu * g2) * fc;
let mut p = efmu * (half / t2);
let mut q = zdiv(Complex::from(half), efmu) / t1;
let mut s1 = f;
let mut s2 = p;
let mut ak = one;
let mut a1 = one;
let mut ck = Complex::from(one);
let mut bk = one - dnu2;
let mut kflag: usize;
if inu > 0 || n > 1 {
if caz >= tol {
let cz = z * z * T::from_f64(0.25);
let t1_sq = T::from_f64(0.25) * caz * caz;
loop {
f = (f * ak + p + q) / bk;
p = p / (ak - dnu);
q = q / (ak + dnu);
let rak = one / ak;
ck = ck * cz * rak;
s1 = mul_add(ck, f, s1);
s2 = mul_add(ck, p - f * ak, s2);
a1 = a1 * t1_sq * rak;
bk = bk + ak + ak + one;
ak = ak + one;
if a1 <= tol {
break;
}
}
}
kflag = 1; let a1_check = (fnu + one) * smu.re.abs();
if a1_check > alim {
kflag = 2; }
let scale = cssr[kflag];
let p2 = s2 * scale;
s2 = p2 * rz;
s1 = s1 * scale;
if koded == Scaling::Exponential {
let ez = z.exp();
s1 = s1 * ez;
s2 = s2 * ez;
}
} else {
if caz >= tol {
let cz = z * z * T::from_f64(0.25);
let t1_sq = T::from_f64(0.25) * caz * caz;
loop {
f = (f * ak + p + q) / bk;
p = p / (ak - dnu);
q = q / (ak + dnu);
let rak = one / ak;
ck = ck * cz * rak;
s1 = mul_add(ck, f, s1);
a1 = a1 * t1_sq * rak;
bk = bk + ak + ak + one;
ak = ak + one;
if a1 <= tol {
break;
}
}
}
y[0] = s1;
if koded == Scaling::Exponential {
let ez = z.exp();
y[0] = s1 * ez;
}
return Ok(nz);
}
return forward_recurrence(
z, fnu, &koded, n, y, &mut nz, tol, elim, alim, iflag, s1, s2, rz, inu, kflag, &cssr,
&csrr, &bry,
);
}
let sz = z.sqrt();
let coef_base = zdiv(Complex::from(T::from_f64(RTHPI)), sz);
let mut kflag: usize = 1;
let coef;
if koded == Scaling::Unscaled {
if z.re > alim {
koded = Scaling::Exponential;
iflag = 1;
kflag = 1; coef = coef_base; } else {
coef = coef_base * (-z).exp() * cssr[kflag];
}
} else {
coef = coef_base;
}
if dnu.abs() == half {
let s1 = coef;
let s2 = coef;
return forward_recurrence(
z, fnu, &koded, n, y, &mut nz, tol, elim, alim, iflag, s1, s2, rz, inu, kflag, &cssr,
&csrr, &bry,
);
}
let ak_cos = T::from_f64(PI) * dnu;
let ak_val = ak_cos.cos().abs();
let fhs = (T::from_f64(0.25) - dnu2).abs();
if ak_val == zero || fhs == zero {
let s1 = coef;
let s2 = coef;
return forward_recurrence(
z, fnu, &koded, n, y, &mut nz, tol, elim, alim, iflag, s1, s2, rz, inu, kflag, &cssr,
&csrr, &bry,
);
}
let r1m5 = T::from_f64(R1M5);
let t1_raw = T::from_f64((T::MACH_DIGITS - 1) as f64) * r1m5 * T::from_f64(3.321928094);
let t1_clamped = t1_raw.max(T::from_f64(12.0)).min(T::from_f64(60.0));
let t2_miller = T::from_f64(TTH) * t1_clamped - T::from_f64(6.0);
let t1_angle = if z.re != zero {
(z.im / z.re).abs().atan()
} else {
T::from_f64(HPI)
};
let fk: T;
let mut fhs_miller = fhs;
if t2_miller <= caz {
let etest = ak_val / (T::from_f64(PI) * caz * tol);
let mut fk_val = one;
if etest >= one {
let mut fks = two;
let mut ckr_fwd = caz + caz + two;
let mut p1r = zero;
let mut p2r = one;
let mut converged = false;
for _i in 0..KMAX {
let ak_inner = fhs_miller / fks;
let cbr = ckr_fwd / (fk_val + one);
let ptr = p2r;
p2r = cbr.fma(p2r, -(p1r * ak_inner));
p1r = ptr;
ckr_fwd = ckr_fwd + two;
fks = fks + fk_val + fk_val + two;
fhs_miller = fhs_miller + fk_val + fk_val;
fk_val = fk_val + one;
let str_test = p2r.abs() * fk_val;
if etest < str_test {
converged = true;
break;
}
}
if !converged {
return Err(Error::ConvergenceFailure);
}
fk_val = fk_val + T::from_f64(SPI) * t1_angle * (t2_miller / caz).sqrt();
fhs_miller = (T::from_f64(0.25) - dnu2).abs();
}
fk = fk_val;
} else {
let a2 = caz.sqrt();
let ak_inner = T::from_f64(FPI) * ak_val / (tol * a2.sqrt());
let three = T::from_f64(3.0);
let aa = three * t1_angle / (one + caz);
let bb = T::from_f64(14.7) * t1_angle / (T::from_f64(28.0) + caz);
let ak_log = (ak_inner.ln() + caz * aa.cos() / (one + T::from_f64(0.008) * caz)) / bb.cos();
fk = T::from_f64(0.12125) * ak_log * ak_log / caz + T::from_f64(1.5);
}
let k = fk.floor().to_i32().unwrap();
let fk_int = T::from_f64(k as f64);
let mut fks = fk_int * fk_int;
let mut p1 = czero;
let mut p2 = Complex::new(tol, zero);
let mut cs = p2;
let mut fk_cur = fk_int;
for _i in 0..k {
let a1 = fks - fk_cur;
let ak_inner = (fks + fk_cur) / (a1 + fhs_miller);
let rak = two / (fk_cur + one);
let cb = Complex::new((fk_cur + z.re) * rak, z.im * rak);
let pt = p2;
p2 = (cb * pt - p1) * ak_inner;
p1 = pt;
cs = cs + p2;
fks = a1 - fk_cur + one;
fk_cur = fk_cur - one;
}
let tm = zabs(cs);
let ptr = one / tm;
let s1_raw = p2 * ptr;
let cs_conj = cs.conj() * ptr;
let s1 = coef * s1_raw * cs_conj;
if inu == 0 && n == 1 {
if iflag == 1 {
return handle_iflag1_final(
z, fnu, n, y, &mut nz, tol, elim, alim, s1, s1, rz, &cssr, &csrr, &bry,
);
}
let str_scale = csrr[kflag];
y[0] = s1 * str_scale;
return Ok(nz);
}
let tm2 = zabs(p2);
let ptr2 = one / tm2;
let p1_scaled = p1 * ptr2;
let p2_conj = p2.conj() * ptr2;
let pt_ratio = p1_scaled * p2_conj;
let ratio = Complex::new(dnu + half - pt_ratio.re, -pt_ratio.im);
let ratio_div_z = zdiv(ratio, z) + Complex::from(one);
let s2 = ratio_div_z * s1;
forward_recurrence(
z, fnu, &koded, n, y, &mut nz, tol, elim, alim, iflag, s1, s2, rz, inu, kflag, &cssr,
&csrr, &bry,
)
}
#[allow(clippy::too_many_arguments, clippy::needless_range_loop)]
fn forward_recurrence<T: BesselFloat>(
z: Complex<T>,
fnu: T,
_koded: &Scaling,
n: usize,
y: &mut [Complex<T>],
nz: &mut usize,
tol: T,
elim: T,
alim: T,
iflag: i32,
mut s1: Complex<T>,
mut s2: Complex<T>,
rz: Complex<T>,
inu: i32,
mut kflag: usize,
cssr: &[T; 3],
csrr: &[T; 3],
bry: &[T; 3],
) -> Result<usize, Error> {
let one = T::one();
let str_init = fnu - T::from_f64(inu as f64) + one;
let mut ck = rz * str_init;
let mut inu_adj = inu;
if n == 1 {
inu_adj -= 1;
}
if inu_adj > 0 {
if iflag == 1 {
return iflag1_recurrence(
z, fnu, n, y, nz, tol, elim, alim, s1, s2, rz, ck, inu_adj, cssr, csrr, bry,
);
}
let inub = 0_i32; let mut p1r = csrr[kflag];
let mut ascle = bry[kflag];
for _i in inub..inu_adj {
let st = s2;
s2 = mul_add(ck, st, s1);
s1 = st;
ck = ck + rz;
if kflag < 2 {
let p2_test = s2 * p1r;
let p2m = p2_test.re.abs().max(p2_test.im.abs());
if p2m > ascle {
kflag += 1;
ascle = bry[kflag];
s1 = s1 * p1r;
s2 = p2_test;
let str_scale = cssr[kflag];
s1 = s1 * str_scale;
s2 = s2 * str_scale;
p1r = csrr[kflag];
}
}
}
if n == 1 {
s1 = s2;
}
} else {
if n == 1 {
s1 = s2;
}
if iflag == 1 {
return handle_iflag1_final(
z, fnu, n, y, nz, tol, elim, alim, s1, s2, rz, cssr, csrr, bry,
);
}
}
let str_scale = csrr[kflag];
y[0] = s1 * str_scale;
if n == 1 {
return Ok(*nz);
}
y[1] = s2 * str_scale;
if n == 2 {
return Ok(*nz);
}
let mut kk = 2_usize; loop {
if kk >= n {
return Ok(*nz);
}
let p1r = csrr[kflag];
let ascle = bry[kflag];
for i in kk..n {
let p2_val = s2;
s2 = mul_add(ck, p2_val, s1);
s1 = p2_val;
ck = ck + rz;
let p2_scaled = s2 * p1r;
y[i] = p2_scaled;
if kflag < 2 {
let p2m = p2_scaled.re.abs().max(p2_scaled.im.abs());
if p2m > ascle {
kflag += 1;
s1 = s1 * p1r;
s2 = p2_scaled;
let str_s = cssr[kflag];
s1 = s1 * str_s;
s2 = s2 * str_s;
kk = i + 1;
break;
}
}
if i == n - 1 {
return Ok(*nz);
}
}
if kk >= n {
return Ok(*nz);
}
}
}
#[allow(clippy::too_many_arguments, clippy::needless_range_loop)]
fn iflag1_recurrence<T: BesselFloat>(
z: Complex<T>,
fnu: T,
n: usize,
y: &mut [Complex<T>],
nz: &mut usize,
tol: T,
elim: T,
alim: T,
mut s1: Complex<T>,
mut s2: Complex<T>,
rz: Complex<T>,
mut ck: Complex<T>,
inu: i32,
cssr: &[T; 3],
csrr: &[T; 3],
bry: &[T; 3],
) -> Result<usize, Error> {
let zero = T::zero();
let czero = Complex::new(zero, zero);
let half = T::from_f64(0.5);
let helim = half * elim;
let elm = (-elim).exp();
let celmr = elm;
let ascle = bry[0]; let mut zd = z;
let mut ic: i32 = -2;
let mut j: usize = 1; let mut cy = [czero; 2];
let mut found_two = false;
let mut inub_resume = 0_i32;
for i in 0..inu {
let st = s2;
s2 = mul_add(ck, st, s1);
s1 = st;
ck = ck + rz;
let az = zabs(s2);
let alas = az.ln();
let p2r = -zd.re + alas;
if p2r >= -elim {
let s2_log = s2.ln();
let p2 = s2_log - zd;
let p1_test = p2.exp() / tol;
if !zuchk(p1_test, ascle, tol) {
j = 1 - j; cy[j] = p1_test;
if ic == i - 1 {
found_two = true;
inub_resume = i + 1;
break;
}
ic = i;
continue;
}
}
if alas >= helim {
zd = zd - elim;
s1 = s1 * celmr;
s2 = s2 * celmr;
}
}
if found_two {
let kflag: usize = 0; s2 = cy[j];
j = 1 - j;
s1 = cy[j];
let inub = inub_resume;
if inub < inu {
let mut p1r = csrr[kflag];
let mut ascle_inner = bry[kflag];
let mut kflag_inner = kflag;
for _i in inub..inu {
let st = s2;
s2 = mul_add(ck, st, s1);
s1 = st;
ck = ck + rz;
if kflag_inner < 2 {
let p2_test = s2 * p1r;
let p2m = p2_test.re.abs().max(p2_test.im.abs());
if p2m > ascle_inner {
kflag_inner += 1;
ascle_inner = bry[kflag_inner];
s1 = s1 * p1r;
s2 = p2_test;
let str_s = cssr[kflag_inner];
s1 = s1 * str_s;
s2 = s2 * str_s;
p1r = csrr[kflag_inner];
}
}
}
if n == 1 {
s1 = s2;
}
let str_scale = csrr[kflag_inner];
y[0] = s1 * str_scale;
if n == 1 {
return Ok(*nz);
}
y[1] = s2 * str_scale;
if n == 2 {
return Ok(*nz);
}
let t2_val = fnu + T::from_f64(2.0); ck = rz * t2_val;
let mut kk_idx = 2_usize;
loop {
if kk_idx >= n {
return Ok(*nz);
}
let p1r = csrr[kflag_inner];
let ascle_loop = bry[kflag_inner];
for i in kk_idx..n {
let p2_val = s2;
s2 = mul_add(ck, p2_val, s1);
s1 = p2_val;
ck = ck + rz;
let p2_scaled = s2 * p1r;
y[i] = p2_scaled;
if kflag_inner < 2 {
let p2m = p2_scaled.re.abs().max(p2_scaled.im.abs());
if p2m > ascle_loop {
kflag_inner += 1;
s1 = s1 * p1r;
s2 = p2_scaled;
let str_s = cssr[kflag_inner];
s1 = s1 * str_s;
s2 = s2 * str_s;
kk_idx = i + 1;
break;
}
}
if i == n - 1 {
return Ok(*nz);
}
}
if kk_idx >= n {
return Ok(*nz);
}
}
}
if n == 1 {
s1 = s2;
}
let str_scale = csrr[kflag];
y[0] = s1 * str_scale;
if n == 1 {
return Ok(*nz);
}
y[1] = s2 * str_scale;
if n == 2 {
return Ok(*nz);
}
return Ok(*nz);
}
if n == 1 {
s1 = s2;
}
handle_iflag1_final(
zd, fnu, n, y, nz, tol, elim, alim, s1, s2, rz, cssr, csrr, bry,
)
}
#[allow(clippy::too_many_arguments, clippy::needless_range_loop)]
fn handle_iflag1_final<T: BesselFloat>(
zd: Complex<T>,
fnu: T,
n: usize,
y: &mut [Complex<T>],
nz: &mut usize,
tol: T,
elim: T,
_alim: T,
s1: Complex<T>,
s2: Complex<T>,
rz: Complex<T>,
cssr: &[T; 3],
csrr: &[T; 3],
bry: &[T; 3],
) -> Result<usize, Error> {
y[0] = s1;
if n > 1 {
y[1] = s2;
}
let ascle = bry[0];
*nz = zkscl(zd, fnu, y, rz, ascle, tol, elim);
let inu_remaining = n as i32 - *nz as i32;
if inu_remaining <= 0 {
return Ok(*nz);
}
let kk = *nz; let mut s1_local = y[kk];
y[kk] = s1_local * csrr[0];
if inu_remaining == 1 {
return Ok(*nz);
}
let kk2 = *nz + 1;
let mut s2_local = y[kk2];
y[kk2] = s2_local * csrr[0];
if inu_remaining == 2 {
return Ok(*nz);
}
let t2_val = fnu + T::from_f64(kk2 as f64);
let mut ck = rz * t2_val;
let mut kflag: usize = 0;
let mut kk_idx = kk2 + 1; loop {
if kk_idx >= n {
return Ok(*nz);
}
let p1r = csrr[kflag];
let ascle = bry[kflag];
for i in kk_idx..n {
let p2_val = s2_local;
s2_local = mul_add(ck, p2_val, s1_local);
s1_local = p2_val;
ck = ck + rz;
let p2_scaled = s2_local * p1r;
y[i] = p2_scaled;
if kflag < 2 {
let p2m = p2_scaled.re.abs().max(p2_scaled.im.abs());
if p2m > ascle {
kflag += 1;
s1_local = s1_local * p1r;
s2_local = p2_scaled;
let str_s = cssr[kflag];
s1_local = s1_local * str_s;
s2_local = s2_local * str_s;
kk_idx = i + 1;
break;
}
}
if i == n - 1 {
return Ok(*nz);
}
}
if kk_idx >= n {
return Ok(*nz);
}
}
}