use std::f64::consts::PI;
const EULER_MASCHERONI: f64 = 0.577_215_664_901_532_9;
pub fn bessel_i0(x: f64) -> f64 {
let x = x.abs();
if x < 3.75 {
let t = x / 3.75;
let t2 = t * t;
1.0 + 3.5156229 * t2
+ 3.0899424 * t2 * t2
+ 1.2067492 * t2 * t2 * t2
+ 0.2659732 * t2 * t2 * t2 * t2
+ 0.0360768 * t2 * t2 * t2 * t2 * t2
+ 0.0045813 * t2 * t2 * t2 * t2 * t2 * t2
} else {
let t = 3.75 / x;
let ax = x.exp() / x.sqrt();
let c = 0.39894228 + 0.01328592 * t + 0.00225319 * t * t - 0.00157565 * t * t * t
+ 0.00916281 * t * t * t * t
- 0.02057706 * t * t * t * t * t
+ 0.02635537 * t * t * t * t * t * t
- 0.01647633 * t * t * t * t * t * t * t
+ 0.00392377 * t * t * t * t * t * t * t * t;
ax * c
}
}
pub fn bessel_i1(x: f64) -> f64 {
let x = x.abs();
if x < 3.75 {
let t = x / 3.75;
let t2 = t * t;
x * (0.5
+ 0.87890594 * t2
+ 0.51498869 * t2 * t2
+ 0.15084934 * t2 * t2 * t2
+ 0.02658733 * t2 * t2 * t2 * t2
+ 0.00301532 * t2 * t2 * t2 * t2 * t2
+ 0.00032411 * t2 * t2 * t2 * t2 * t2 * t2)
} else {
let t = 3.75 / x;
let ax = x.exp() / x.sqrt();
let c = 0.39894228 - 0.03988024 * t - 0.00362018 * t * t + 0.00163801 * t * t * t
- 0.01031555 * t * t * t * t
+ 0.02282967 * t * t * t * t * t
- 0.02895312 * t * t * t * t * t * t
+ 0.01787654 * t * t * t * t * t * t * t
- 0.00420059 * t * t * t * t * t * t * t * t;
ax * c
}
}
pub fn bessel_i0_scaled(x: f64) -> f64 {
let x = x.abs();
if x < 50.0 {
bessel_i0(x) * (-x).exp()
} else {
let s = 1.0 / (2.0 * PI * x).sqrt();
s * (1.0 + 1.0 / (8.0 * x) + 9.0 / (128.0 * x * x))
}
}
pub fn bessel_i1_scaled(x: f64) -> f64 {
let x = x.abs();
if x < 50.0 {
bessel_i1(x) * (-x).exp()
} else {
let s = 1.0 / (2.0 * PI * x).sqrt();
s * (1.0 - 3.0 / (8.0 * x) - 15.0 / (128.0 * x * x))
}
}
pub fn exp_int_e1(x: f64) -> f64 {
debug_assert!(x > 0.0, "E1 requires x > 0");
if x <= 1.0 {
let mut sum = 0.0;
let mut a = -x; let mut k = 1.0;
loop {
let term = a / k;
sum += term;
if term.abs() < 1e-16 * sum.abs().max(1.0) {
break;
}
a = a * (-x) / (k + 1.0); k += 1.0;
if k > 1000.0 {
break;
}
}
-EULER_MASCHERONI - x.ln() - sum
} else {
let tiny = 1e-30;
let mut b = x + 1.0;
let mut c = 1.0 / tiny;
let mut d = 1.0 / b;
let mut h = d;
for i in 1..=1000 {
let an = -(i as f64) * (i as f64);
b += 2.0;
d = an * d + b;
if d.abs() < tiny {
d = tiny;
}
c = b + an / c;
if c.abs() < tiny {
c = tiny;
}
d = 1.0 / d;
let del = d * c;
h *= del;
if (del - 1.0).abs() < 1e-16 {
break;
}
}
h * (-x).exp()
}
}
pub const SQRT_2_OVER_PI: f64 = 0.7978845608028654;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn i0_known_values() {
assert!((bessel_i0(0.0) - 1.0).abs() < 1e-9);
assert!((bessel_i0(1.0) - 1.2660658777520082).abs() < 1e-6);
assert!((bessel_i0(5.0) - 27.23987182361646).abs() < 1e-4);
let v = bessel_i0(20.0);
assert!((v.ln() - 17.590).abs() < 0.01, "ln(I0(20))={}", v.ln());
}
#[test]
fn i1_known_values() {
assert!(bessel_i1(0.0).abs() < 1e-9);
assert!((bessel_i1(1.0) - 0.565159103992485).abs() < 1e-6);
assert!((bessel_i1(5.0) - 24.33564214245033).abs() < 1e-4);
}
#[test]
fn e1_known_values() {
assert!((exp_int_e1(0.1) - 1.822923958449177).abs() < 1e-7);
assert!((exp_int_e1(1.0) - 0.2193839343955203).abs() < 1e-7);
assert!((exp_int_e1(5.0) - 0.0011482955912753457).abs() < 1e-9);
}
#[test]
fn scaled_bessels_stable_and_accurate() {
for &x in &[0.0, 0.5, 1.0, 5.0, 10.0, 49.0] {
let direct0 = bessel_i0(x) * (-x).exp();
let direct1 = bessel_i1(x) * (-x).exp();
assert!((bessel_i0_scaled(x) - direct0).abs() < 1e-9 * direct0.abs().max(1.0));
assert!((bessel_i1_scaled(x) - direct1).abs() < 1e-9 * direct1.abs().max(1.0));
}
for &x in &[500.0, 5000.0, 1e6] {
let v0 = bessel_i0_scaled(x);
let v1 = bessel_i1_scaled(x);
assert!(v0.is_finite() && v0 > 0.0, "i0_scaled({x})={v0}");
assert!(v1.is_finite() && v1 > 0.0, "i1_scaled({x})={v1}");
let asymp = 1.0 / (2.0 * PI * x).sqrt();
assert!((v0 - asymp).abs() / asymp < 0.01, "i0_scaled({x}) off");
}
assert!((bessel_i0_scaled(0.0) - 1.0).abs() < 1e-12);
assert!(bessel_i1_scaled(0.0).abs() < 1e-12);
}
}