use crate::bessel::j0f::j1f_rsqrt;
use crate::common::f_fmla;
use crate::exponents::core_expf;
use crate::polyeval::{
f_estrin_polyeval5, f_estrin_polyeval7, f_estrin_polyeval8, f_polyeval6, f_polyeval10,
};
pub fn f_i0ef(x: f32) -> f32 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0xffu32 << 24 || ux == 0 {
if ux == 0 {
return 1.;
}
if x.is_infinite() {
return 0.;
}
return x + f32::NAN; }
let xb = x.to_bits() & 0x7fff_ffff;
if xb <= 0x40f00000u32 {
let core_expf = core_expf(-f32::from_bits(xb));
if xb < 0x3f800000u32 {
if xb <= 0x34000000u32 {
return 1. - x;
}
return i0f_small(f32::from_bits(xb), core_expf);
} else if xb <= 0x40600000u32 {
return i0ef_1_to_3p5(f32::from_bits(xb), core_expf);
} else if xb <= 0x40c00000u32 {
return i0f_3p5_to_6(f32::from_bits(xb), core_expf);
}
return i0f_6_to_7p5(f32::from_bits(xb), core_expf);
}
i0ef_asympt(f32::from_bits(xb))
}
#[inline]
pub(crate) fn i0f_small(x: f32, v_exp: f64) -> f32 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p = f_estrin_polyeval7(
eval_x,
f64::from_bits(0x3ff000000000013a),
f64::from_bits(0x3fcffffffffc20b6),
f64::from_bits(0x3f9c71c71e6cd6a2),
f64::from_bits(0x3f5c71c65b0af15f),
f64::from_bits(0x3f1234796fceb081),
f64::from_bits(0x3ec0280faf31678c),
f64::from_bits(0x3e664fd494223545),
);
(f_fmla(p, eval_x, 1.) * v_exp) as f32
}
#[inline]
fn i0ef_1_to_3p5(x: f32, v_exp: f64) -> f32 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p_num = f_polyeval6(
eval_x,
f64::from_bits(0x3feffffffffffb69),
f64::from_bits(0x3fc9ed7bd9dc97a7),
f64::from_bits(0x3f915c14693c842e),
f64::from_bits(0x3f45c6dc6a719e42),
f64::from_bits(0x3eeacb79eba725f7),
f64::from_bits(0x3e7b51e2acfc4355),
);
let p_den = f_estrin_polyeval5(
eval_x,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xbfa84a10988f28eb),
f64::from_bits(0x3f50f5599197a4be),
f64::from_bits(0xbeea420cf9b13b1b),
f64::from_bits(0x3e735d0c1eb6ed7d),
);
(f_fmla(p_num / p_den, eval_x, 1.) * v_exp) as f32
}
#[inline]
fn i0f_6_to_7p5(x: f32, v_exp: f64) -> f32 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p_num = f_estrin_polyeval8(
eval_x,
f64::from_bits(0x3fefffffffffff7d),
f64::from_bits(0x3fcb373b00569ccf),
f64::from_bits(0x3f939069c3363b81),
f64::from_bits(0x3f4c2095c90c66b3),
f64::from_bits(0x3ef6713f648413db),
f64::from_bits(0x3e947efa2f9936b4),
f64::from_bits(0x3e2486a182f49420),
f64::from_bits(0x3da213034a33de33),
);
let p_den = f_estrin_polyeval7(
eval_x,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xbfa32313fea59d9e),
f64::from_bits(0x3f460594c2ec6706),
f64::from_bits(0xbedf725fb714690f),
f64::from_bits(0x3e6d9cb39b19555c),
f64::from_bits(0xbdf1900e3abcb7a6),
f64::from_bits(0x3d64a21a2ea78ef6),
);
(f_fmla(p_num / p_den, eval_x, 1.) * v_exp) as f32
}
#[inline]
fn i0f_3p5_to_6(x: f32, v_exp: f64) -> f32 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p_num = f_polyeval6(
eval_x,
f64::from_bits(0x3feffffffffd9550),
f64::from_bits(0x3fc97e18ee033fb4),
f64::from_bits(0x3f90b3199079bce1),
f64::from_bits(0x3f442c300a425372),
f64::from_bits(0x3ee7831030ae18ca),
f64::from_bits(0x3e76387d67354932),
);
let p_den = f_polyeval6(
eval_x,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xbfaa079c484e406a),
f64::from_bits(0x3f5452098f1556fb),
f64::from_bits(0xbef33efb4a8128ac),
f64::from_bits(0x3e865996e19448ca),
f64::from_bits(0xbe09acbb64533c3e),
);
(f_fmla(p_num / p_den, eval_x, 1.) * v_exp) as f32
}
#[inline]
fn i0ef_asympt(x: f32) -> f32 {
let dx = x as f64;
let recip = 1. / dx;
let p_num = f_polyeval10(
recip,
f64::from_bits(0x3fd9884533d4364f),
f64::from_bits(0xc02ed6c9269921a7),
f64::from_bits(0x4070ee77ffed64a5),
f64::from_bits(0xc0a4ffd558b06889),
f64::from_bits(0x40cf2633e2840f6f),
f64::from_bits(0xc0ea813a9ba42b84),
f64::from_bits(0x40f569bf5d63eb8c),
f64::from_bits(0xc0b3138874cdd180),
f64::from_bits(0xc0fa3152ed485937),
f64::from_bits(0x40ddaccbed454f47),
);
let p_den = f_polyeval10(
recip,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xc0436352c350b88c),
f64::from_bits(0x40855eaa17b05edd),
f64::from_bits(0xc0baa46f155bd266),
f64::from_bits(0x40e3e9fd90a2e695),
f64::from_bits(0xc1012dc621dfc1e8),
f64::from_bits(0x410cafeea713e8ce),
f64::from_bits(0xc0e0a3ee0077d7f7),
f64::from_bits(0xc110bcced6a39e9e),
f64::from_bits(0x40f9a1e4a91be4d6),
);
let z = p_num / p_den;
let r_sqrt = j1f_rsqrt(dx);
(z * r_sqrt) as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i0f() {
assert!(f_i0ef(f32::NAN).is_nan());
assert_eq!(f_i0ef(f32::NEG_INFINITY), 0.);
assert_eq!(f_i0ef(f32::INFINITY), 0.);
assert_eq!(f_i0ef(1.), 0.4657596);
assert_eq!(f_i0ef(5.), 0.1835408);
assert_eq!(f_i0ef(16.), 0.100544125);
assert_eq!(f_i0ef(32.), 0.070804186);
assert_eq!(f_i0ef(92.0), 0.04164947);
assert_eq!(f_i0ef(0.), 1.0);
assert_eq!(f_i0ef(28.), 0.075736605);
assert_eq!(f_i0ef(-28.), 0.075736605);
assert_eq!(f_i0ef(-32.), 0.070804186);
assert_eq!(f_i0ef(-92.0), 0.04164947);
assert_eq!(f_i0ef(-0.), 1.0);
}
}