use crate::bessel::j0f::{j0f_asympt_alpha, j0f_asympt_beta, j1f_rsqrt};
use crate::bessel::trigo_bessel::sin_small;
use crate::bessel::y0f_coeffs::{Y0_ZEROS, Y0_ZEROS_VALUES, Y0F_COEFFS};
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::logs::fast_logf;
use crate::polyeval::{f_polyeval10, f_polyeval18};
use crate::rounding::CpuCeil;
use crate::sincos_reduce::rem2pif_any;
pub fn f_y0f(x: f32) -> f32 {
let ux = x.to_bits();
if ux >= 0xffu32 << 23 || ux == 0 {
if ux.wrapping_shl(1) == 0 {
return f32::NEG_INFINITY;
}
if x.is_infinite() {
if x.is_sign_negative() {
return f32::NAN;
}
return 0.;
}
return x + f32::NAN; }
let xb = x.to_bits();
if xb <= 0x4296999au32 {
if xb <= 0x40000000u32 {
if xb <= 0x3faccccdu32 {
return y0f_near_zero(f32::from_bits(xb));
}
return y0_transient_area(x);
}
return y0f_small_argument_path(f32::from_bits(xb));
}
let xb = x.to_bits();
if xb == 0x5023e87f {
return f32::from_bits(0x28085b2d);
} else if xb == 0x48171521 {
return f32::from_bits(0x2bd244ba);
} else if xb == 0x4398c299 {
return f32::from_bits(0x32c730db);
} else if xb == 0x7f0e5a38 {
return f32::from_bits(0x131f680b);
} else if xb == 0x6ef9be45 {
return f32::from_bits(0x987d8a8f);
}
y0f_asympt(x)
}
#[inline]
fn y0f_near_zero(x: f32) -> f32 {
const W: [u64; 10] = [
0x3fe45f306dc9c883,
0xbfc45f306dc9c883,
0x3f845f306dc9c883,
0xbf321bb945252402,
0x3ed21bb945252402,
0xbe672db9f21b0f5f,
0x3df49a6c656d62ff,
0xbd7ae90af76a4d0f,
0x3cfae90af76a4d0f,
0xbc754331c053fdad,
];
let dx = x as f64;
let x2 = dx * dx;
let w0 = f_polyeval10(
x2,
f64::from_bits(W[0]),
f64::from_bits(W[1]),
f64::from_bits(W[2]),
f64::from_bits(W[3]),
f64::from_bits(W[4]),
f64::from_bits(W[5]),
f64::from_bits(W[6]),
f64::from_bits(W[7]),
f64::from_bits(W[8]),
f64::from_bits(W[9]),
);
const Z: [u64; 10] = [
0x3fb2e4d699cbd01f,
0xbfc6bbcb41034286,
0x3f9075b1bbf41364,
0xbf41a6206b7b973d,
0x3ee3e99794203bbd,
0xbe7bce4a600d3ea4,
0x3e0a6ee796b871b6,
0xbd92393d82c6b2e4,
0x3d131085da82054c,
0xbc8f4ed4b492ebcc,
];
let z0 = f_polyeval10(
x2,
f64::from_bits(Z[0]),
f64::from_bits(Z[1]),
f64::from_bits(Z[2]),
f64::from_bits(Z[3]),
f64::from_bits(Z[4]),
f64::from_bits(Z[5]),
f64::from_bits(Z[6]),
f64::from_bits(Z[7]),
f64::from_bits(Z[8]),
f64::from_bits(Z[9]),
);
let w_log = fast_logf(x);
f_fmla(w0, w_log, -z0) as f32
}
#[inline]
fn y0_transient_area(x: f32) -> f32 {
let dx = x as f64;
const ZERO: DoubleDouble =
DoubleDouble::from_bit_pair((0xbc8bd1e50d219bfd, 0x400193bed4dff243));
let r = (dx - ZERO.hi) - ZERO.lo;
let p = f_polyeval18(
r,
f64::from_bits(0x3fe0aa48442f8375),
f64::from_bits(0x3de601d3b959b8d8),
f64::from_bits(0xbfd0aa4840bb8529),
f64::from_bits(0x3fa439fc16d4835e),
f64::from_bits(0x3f80d2dcd97d2b4f),
f64::from_bits(0x3f4f833368f9f047),
f64::from_bits(0xbf541a702ee92277),
f64::from_bits(0x3f3abc113cf0f4da),
f64::from_bits(0xbefac1ded6f17ba8),
f64::from_bits(0x3f33ef372e24df82),
f64::from_bits(0x3f3bf8b42322df40),
f64::from_bits(0x3f4582f9daec9ca7),
f64::from_bits(0x3f479fc07175494e),
f64::from_bits(0x3f4477a5e32b723a),
f64::from_bits(0x3f39fbfd6a6d6f0c),
f64::from_bits(0x3f2760a66816527b),
f64::from_bits(0x3f0a68fdeeba224f),
f64::from_bits(0x3edd78c6c87089e1),
);
p as f32
}
#[inline]
fn y0f_small_argument_path(x: f32) -> f32 {
let x_abs = x as f64;
const INV_STEP: f64 = 0.6299609508652038;
let fx = x_abs * INV_STEP;
const Y0_ZEROS_COUNT: f64 = (Y0_ZEROS.len() - 1) as f64;
let idx0 = unsafe { fx.min(Y0_ZEROS_COUNT).to_int_unchecked::<usize>() };
let idx1 = unsafe {
fx.cpu_ceil()
.min(Y0_ZEROS_COUNT)
.to_int_unchecked::<usize>()
};
let found_zero0 = DoubleDouble::from_bit_pair(Y0_ZEROS[idx0]);
let found_zero1 = DoubleDouble::from_bit_pair(Y0_ZEROS[idx1]);
let dist0 = (found_zero0.hi - x_abs).abs();
let dist1 = (found_zero1.hi - x_abs).abs();
let (found_zero, idx, dist) = if dist0 < dist1 {
(found_zero0, idx0, dist0)
} else {
(found_zero1, idx1, dist1)
};
if idx == 0 {
return y0f_near_zero(x);
}
if dist == 0. {
return f64::from_bits(Y0_ZEROS_VALUES[idx]) as f32;
}
let c = &Y0F_COEFFS[idx - 1];
let r = (x_abs - found_zero.hi) - found_zero.lo;
let p = f_polyeval18(
r,
f64::from_bits(c[0]),
f64::from_bits(c[1]),
f64::from_bits(c[2]),
f64::from_bits(c[3]),
f64::from_bits(c[4]),
f64::from_bits(c[5]),
f64::from_bits(c[6]),
f64::from_bits(c[7]),
f64::from_bits(c[8]),
f64::from_bits(c[9]),
f64::from_bits(c[10]),
f64::from_bits(c[11]),
f64::from_bits(c[12]),
f64::from_bits(c[13]),
f64::from_bits(c[14]),
f64::from_bits(c[15]),
f64::from_bits(c[16]),
f64::from_bits(c[17]),
);
p as f32
}
#[inline]
fn y0f_asympt(x: f32) -> f32 {
let dx = x as f64;
let alpha = j0f_asympt_alpha(dx);
let beta = j0f_asympt_beta(dx);
let angle = rem2pif_any(x);
const SQRT_2_OVER_PI: f64 = f64::from_bits(0x3fe9884533d43651);
const MPI_OVER_4: f64 = f64::from_bits(0xbfe921fb54442d18);
let x0pi34 = MPI_OVER_4 - alpha;
let r0 = angle + x0pi34;
let m_cos = sin_small(r0);
let z0 = beta * m_cos;
let scale = SQRT_2_OVER_PI * j1f_rsqrt(dx);
(scale * z0) as f32
}
#[cfg(test)]
mod tests {
use crate::f_y0f;
#[test]
fn test_y0f() {
assert_eq!(f_y0f(90.5), 0.08254846);
assert_eq!(f_y0f(77.5), 0.087678276);
assert_eq!(f_y0f(1.5), 0.3824489);
assert_eq!(f_y0f(0.5), -0.44451874);
assert!(f_y0f(-1.).is_nan());
assert_eq!(f_y0f(0.), f32::NEG_INFINITY);
assert_eq!(f_y0f(-0.), f32::NEG_INFINITY);
assert_eq!(f_y0f(f32::INFINITY), 0.);
assert!(f_y0f(f32::NEG_INFINITY).is_nan());
}
}