#![allow(clippy::excessive_precision)]
use crate::bessel::alpha1::bessel_1_asympt_alpha_fast;
use crate::bessel::beta1::bessel_1_asympt_beta_fast;
use crate::bessel::j1_coeffs::{J1_COEFFS, J1_ZEROS, J1_ZEROS_VALUE};
use crate::bessel::j1_coeffs_taylor::J1_COEFFS_TAYLOR;
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::polyeval::{f_polyeval9, f_polyeval19};
use crate::rounding::CpuCeil;
use crate::rounding::CpuRound;
use crate::sin_helper::sin_dd_small_fast;
pub fn f_jincpi(x: f64) -> f64 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0x7ffu64 << 53 || ux <= 0x7960000000000000u64 {
if ux <= 0x7960000000000000u64 {
return 1.0;
}
if x.is_infinite() {
return 0.;
}
return x + f64::NAN; }
let ax: u64 = x.to_bits() & 0x7fff_ffff_ffff_ffff;
if ax < 0x4052a6784230fcf8u64 {
if ax < 0x3fd3333333333333 {
return jincpi_near_zero(f64::from_bits(ax));
}
let scaled_pix = f64::from_bits(ax) * std::f64::consts::PI; if scaled_pix < 74.60109 {
return jinc_small_argument_fast(f64::from_bits(ax));
}
}
jinc_asympt_fast(f64::from_bits(ax))
}
#[inline]
fn jinc_asympt_fast(ox: f64) -> f64 {
const PI: DoubleDouble = DoubleDouble::new(
f64::from_bits(0x3ca1a62633145c07),
f64::from_bits(0x400921fb54442d18),
);
let px = DoubleDouble::quick_mult_f64(PI, ox);
const Z2_3_2_OVER_PI_SQR: DoubleDouble =
DoubleDouble::from_bit_pair((0xbc76213a285b8094, 0x3fd25751e5614413));
const MPI_OVER_4: DoubleDouble = DoubleDouble::new(
f64::from_bits(0xbc81a62633145c07),
f64::from_bits(0xbfe921fb54442d18),
);
let kd = (ox * 0.5).cpu_round();
let rem = f_fmla(kd, -2., ox);
let angle = DoubleDouble::quick_mult_f64(PI, rem);
let recip = px.recip();
let alpha = bessel_1_asympt_alpha_fast(recip);
let beta = bessel_1_asympt_beta_fast(recip);
let x0pi34 = DoubleDouble::full_dd_sub(MPI_OVER_4, alpha);
let r0 = DoubleDouble::full_dd_add(angle, x0pi34);
let m_sin = sin_dd_small_fast(r0);
let z0 = DoubleDouble::quick_mult(beta, m_sin);
let ox_recip = DoubleDouble::from_quick_recip(ox);
let dx_sqrt = ox_recip.fast_sqrt();
let scale = DoubleDouble::quick_mult(Z2_3_2_OVER_PI_SQR, dx_sqrt);
let p = DoubleDouble::quick_mult(scale, z0);
DoubleDouble::quick_mult(p, ox_recip).to_f64()
}
#[inline]
pub(crate) fn jincpi_near_zero(x: f64) -> f64 {
const P: [(u64, u64); 8] = [
(0xbb3bddffe9450ca6, 0x3ff0000000000000),
(0x3c4b0b0a7393eccb, 0xbfde4cd3c3c87615),
(0xbc8f9f784e0594a6, 0xbff043283b1e383f),
(0xbc7af77bca466875, 0x3fdee46673cf919f),
(0xbc1b62837b038ea8, 0x3fd0b7cc55c9a4af),
(0x3c6c08841871f124, 0xbfc002b65231dcdd),
(0xbc36cf2d89ea63bc, 0xbf949022a7a0712b),
(0xbbf535d492c0ac1c, 0x3f840b48910d5105),
];
const Q: [(u64, u64); 8] = [
(0x0000000000000000, 0x3ff0000000000000),
(0x3c4aba6577f3253e, 0xbfde4cd3c3c87615),
(0x3c52f58f82e3438c, 0x3fcbd0a475006cf9),
(0x3c36e496237d6b49, 0xbfb9f4cea13b06e9),
(0xbbbbf3e4ef3a28fe, 0x3f967ed0cee85392),
(0x3c267ac442bb3bcf, 0xbf846e192e22f862),
(0x3bd84e9888993cb0, 0x3f51e0fff3cfddee),
(0x3bd7c0285797bd8e, 0xbf3ea7a621fa1c8c),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let x4 = x2 * x2;
let p0 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[1]),
x,
DoubleDouble::from_bit_pair(P[0]),
);
let p1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[3]),
x,
DoubleDouble::from_bit_pair(P[2]),
);
let p2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[5]),
x,
DoubleDouble::from_bit_pair(P[4]),
);
let p3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[7]),
x,
DoubleDouble::from_bit_pair(P[6]),
);
let q0 = DoubleDouble::mul_add(x2, p1, p0);
let q1 = DoubleDouble::mul_add(x2, p3, p2);
let p_num = DoubleDouble::mul_add(x4, q1, q0);
let p0 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[1]),
x,
DoubleDouble::from_bit_pair(Q[0]),
);
let p1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[3]),
x,
DoubleDouble::from_bit_pair(Q[2]),
);
let p2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[5]),
x,
DoubleDouble::from_bit_pair(Q[4]),
);
let p3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[7]),
x,
DoubleDouble::from_bit_pair(Q[6]),
);
let q0 = DoubleDouble::mul_add(x2, p1, p0);
let q1 = DoubleDouble::mul_add(x2, p3, p2);
let p_den = DoubleDouble::mul_add(x4, q1, q0);
DoubleDouble::div(p_num, p_den).to_f64()
}
#[inline]
pub(crate) fn jinc_small_argument_fast(x: f64) -> f64 {
const PI: DoubleDouble = DoubleDouble::new(
f64::from_bits(0x3ca1a62633145c07),
f64::from_bits(0x400921fb54442d18),
);
let dx = DoubleDouble::quick_mult_f64(PI, x);
const INV_STEP: f64 = 0.6300176043004198;
let fx = dx.hi * 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 - dx.hi).abs();
let dist1 = (found_zero1.hi - dx.hi).abs();
let (found_zero, idx, dist) = if dist0 < dist1 {
(found_zero0, idx0, dist0)
} else {
(found_zero1, idx1, dist1)
};
if idx == 0 {
return jincpi_near_zero(x);
}
let r = DoubleDouble::quick_dd_sub(dx, found_zero);
if dist == 0. {
return DoubleDouble::quick_mult_f64(
DoubleDouble::from_f64_div_dd(f64::from_bits(J1_ZEROS_VALUE[idx]), dx),
2.,
)
.to_f64();
}
let is_zero_too_close = dist.abs() < 1e-3;
let c = if is_zero_too_close {
&J1_COEFFS_TAYLOR[idx - 1]
} else {
&J1_COEFFS[idx - 1]
};
let p = f_polyeval19(
r.hi,
f64::from_bits(c[5].1),
f64::from_bits(c[6].1),
f64::from_bits(c[7].1),
f64::from_bits(c[8].1),
f64::from_bits(c[9].1),
f64::from_bits(c[10].1),
f64::from_bits(c[11].1),
f64::from_bits(c[12].1),
f64::from_bits(c[13].1),
f64::from_bits(c[14].1),
f64::from_bits(c[15].1),
f64::from_bits(c[16].1),
f64::from_bits(c[17].1),
f64::from_bits(c[18].1),
f64::from_bits(c[19].1),
f64::from_bits(c[20].1),
f64::from_bits(c[21].1),
f64::from_bits(c[22].1),
f64::from_bits(c[23].1),
);
let mut z = DoubleDouble::mul_f64_add(r, p, DoubleDouble::from_bit_pair(c[4]));
z = DoubleDouble::mul_add(z, r, DoubleDouble::from_bit_pair(c[3]));
z = DoubleDouble::mul_add(z, r, DoubleDouble::from_bit_pair(c[2]));
z = DoubleDouble::mul_add(z, r, DoubleDouble::from_bit_pair(c[1]));
z = DoubleDouble::mul_add(z, r, DoubleDouble::from_bit_pair(c[0]));
z = DoubleDouble::div(z, dx);
z.hi *= 2.;
z.lo *= 2.;
let err = f_fmla(
z.hi,
f64::from_bits(0x3c70000000000000), f64::from_bits(0x3bf0000000000000), );
let ub = z.hi + (z.lo + err);
let lb = z.hi + (z.lo - err);
if ub == lb {
return z.to_f64();
}
j1_small_argument_dd(r, c, dx)
}
fn j1_small_argument_dd(r: DoubleDouble, c0: &[(u64, u64); 24], inv_scale: DoubleDouble) -> f64 {
let c = &c0[15..];
let p0 = f_polyeval9(
r.to_f64(),
f64::from_bits(c[0].1),
f64::from_bits(c[1].1),
f64::from_bits(c[2].1),
f64::from_bits(c[3].1),
f64::from_bits(c[4].1),
f64::from_bits(c[5].1),
f64::from_bits(c[6].1),
f64::from_bits(c[7].1),
f64::from_bits(c[8].1),
);
let c = c0;
let mut p_e = DoubleDouble::mul_f64_add(r, p0, DoubleDouble::from_bit_pair(c[14]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[13]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[12]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[11]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[10]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[9]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[8]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[7]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[6]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[5]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[4]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[3]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[2]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[1]));
p_e = DoubleDouble::mul_add(p_e, r, DoubleDouble::from_bit_pair(c[0]));
let p = DoubleDouble::from_exact_add(p_e.hi, p_e.lo);
let mut z = DoubleDouble::div(p, inv_scale);
z.hi *= 2.;
z.lo *= 2.;
z.to_f64()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_jincpi() {
assert_eq!(f_jincpi(f64::EPSILON), 1.0);
assert_eq!(f_jincpi(0.000043242121), 0.9999999976931268);
assert_eq!(f_jincpi(0.5000000000020244), 0.7217028449014163);
assert_eq!(f_jincpi(73.81695991658546), -0.0004417546638317049);
assert_eq!(f_jincpi(0.01), 0.9998766350182722);
assert_eq!(f_jincpi(0.9), 0.28331697846510623);
assert_eq!(f_jincpi(3.831705970207517), -0.036684415010255086);
assert_eq!(f_jincpi(-3.831705970207517), -0.036684415010255086);
assert_eq!(
f_jincpi(0.000000000000000000000000000000000000008827127),
1.0
);
assert_eq!(
f_jincpi(-0.000000000000000000000000000000000000008827127),
1.0
);
assert_eq!(f_jincpi(5.4), -0.010821736808448256);
assert_eq!(
f_jincpi(77.743162408196766932633181568235159),
-0.00041799098646950523
);
assert_eq!(
f_jincpi(84.027189586293545175976760219782591),
-0.00023927934929850555
);
assert_eq!(f_jincpi(f64::NEG_INFINITY), 0.0);
assert_eq!(f_jincpi(f64::INFINITY), 0.0);
assert!(f_jincpi(f64::NAN).is_nan());
}
}