use crate::bessel::j1_coeffs::{J1_ZEROS, J1_ZEROS_VALUE};
use crate::bessel::j1f_coeffs::J1F_COEFFS;
use crate::bessel::trigo_bessel::sin_small;
use crate::double_double::DoubleDouble;
use crate::polyeval::{f_polyeval7, f_polyeval10, f_polyeval12, f_polyeval14};
use crate::rounding::CpuCeil;
use crate::sincos_reduce::rem2pif_any;
pub fn f_j1f(x: f32) -> f32 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0xffu32 << 24 || ux == 0 {
if ux == 0 {
return x;
}
if x.is_infinite() {
return 0.;
}
return x + f32::NAN; }
let ax = x.to_bits() & 0x7fff_ffff;
if ax < 0x429533c2u32 {
if ax < 0x3e800000u32 {
if ax <= 0x34000000u32 {
return x * 0.5;
}
return poly_near_zero(x);
}
return small_argument_path(x);
}
if ax == 0x6ef9be45 {
return if x.is_sign_negative() {
f32::from_bits(0x187d8a8f)
} else {
-f32::from_bits(0x187d8a8f)
};
} else if ax == 0x7f0e5a38 {
return if x.is_sign_negative() {
-f32::from_bits(0x131f680b)
} else {
f32::from_bits(0x131f680b)
};
}
j1f_asympt(x) as f32
}
#[inline]
fn j1f_rsqrt(x: f64) -> f64 {
(1. / x) * x.sqrt()
}
#[inline]
fn j1f_asympt(x: f32) -> f64 {
static SGN: [f64; 2] = [1., -1.];
let sign_scale = SGN[x.is_sign_negative() as usize];
let x = f32::from_bits(x.to_bits() & 0x7fff_ffff);
let dx = x as f64;
let alpha = j1f_asympt_alpha(dx);
let beta = j1f_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_sin = sin_small(r0);
let z0 = beta * m_sin;
let scale = SQRT_2_OVER_PI * j1f_rsqrt(dx);
scale * z0 * sign_scale
}
#[inline]
pub(crate) fn j1f_asympt_alpha(x: f64) -> f64 {
const C: [u64; 12] = [
0xbfd8000000000000,
0x3fc5000000000000,
0xbfd7bccccccccccd,
0x4002f486db6db6db,
0xc03e9fbf40000000,
0x4084997b55945d17,
0xc0d4a914195269d9,
0x412cd1b53816aec1,
0xc18aa4095d419351,
0x41ef809305f11b9d,
0xc2572e6809ed618b,
0x42c4c5b6057839f9,
];
let recip = 1. / x;
let x2 = recip * recip;
let p = f_polyeval12(
x2,
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]),
);
p * recip
}
#[inline]
pub(crate) fn j1f_asympt_beta(x: f64) -> f64 {
const C: [u64; 10] = [
0x3ff0000000000000,
0x3fc8000000000000,
0xbfc8c00000000000,
0x3fe9c50000000000,
0xc01ef5b680000000,
0x40609860dd400000,
0xc0abae9b7a06e000,
0x41008711d41c1428,
0xc15ab70164c8be6e,
0x41bc1055e24f297f,
];
let recip = 1. / x;
let x2 = recip * recip;
f_polyeval10(
x2,
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]),
)
}
#[inline]
fn poly_near_zero(x: f32) -> f32 {
let dx = x as f64;
let x2 = dx * dx;
let p = f_polyeval7(
x2,
f64::from_bits(0x3fe0000000000000),
f64::from_bits(0xbfaffffffffffffc),
f64::from_bits(0x3f65555555554089),
f64::from_bits(0xbf0c71c71c2a74ae),
f64::from_bits(0x3ea6c16bbd1dc5c1),
f64::from_bits(0xbe384562afb69e7d),
f64::from_bits(0x3dc248d0d0221cd0),
);
(p * dx) as f32
}
#[inline]
fn small_argument_path(x: f32) -> f32 {
static SIGN: [f64; 2] = [1., -1.];
let sign_scale = SIGN[x.is_sign_negative() as usize];
let x_abs = f32::from_bits(x.to_bits() & 0x7fff_ffff) as f64;
const INV_STEP: f64 = 0.6300176043004198;
let fx = x_abs * INV_STEP;
const J1_ZEROS_COUNT: f64 = (J1_ZEROS.len() - 1) as f64;
let idx0 = unsafe { fx.min(J1_ZEROS_COUNT).to_int_unchecked::<usize>() };
let idx1 = unsafe {
fx.cpu_ceil()
.min(J1_ZEROS_COUNT)
.to_int_unchecked::<usize>()
};
let found_zero0 = DoubleDouble::from_bit_pair(J1_ZEROS[idx0]);
let found_zero1 = DoubleDouble::from_bit_pair(J1_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 poly_near_zero(x);
}
if dist == 0. {
return (f64::from_bits(J1_ZEROS_VALUE[idx]) * sign_scale) as f32;
}
let c = &J1F_COEFFS[idx - 1];
let r = (x_abs - found_zero.hi) - found_zero.lo;
let p = f_polyeval14(
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]),
);
(p * sign_scale) as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_f_j1f() {
assert_eq!(
f_j1f(77.743162408196766932633181568235159),
0.09049267898021947
);
assert_eq!(
f_j1f(-0.000000000000000000000000000000000000008827127),
-0.0000000000000000000000000000000000000044135635
);
assert_eq!(
f_j1f(0.000000000000000000000000000000000000008827127),
0.0000000000000000000000000000000000000044135635
);
assert_eq!(f_j1f(5.4), -0.3453447907795863);
assert_eq!(
f_j1f(84.027189586293545175976760219782591),
0.0870430264022591
);
assert_eq!(f_j1f(f32::INFINITY), 0.);
assert_eq!(f_j1f(f32::NEG_INFINITY), 0.);
assert!(f_j1f(f32::NAN).is_nan());
assert_eq!(f_j1f(-1.7014118e38), 0.000000000000000000006856925);
}
}