#[inline]
pub fn binomial_coefficient_f64(n: usize, k: usize) -> f64 {
if k > n {
return 0.0;
}
if k == 0 || k == n {
return 1.0;
}
let k_eff = k.min(n - k);
let mut num: u128 = 1;
for j in 0..k_eff {
match num.checked_mul((n - j) as u128) {
Some(scaled) => num = scaled / (j as u128 + 1),
None => {
let mut out = num as f64;
for jj in j..k_eff {
out = out * (n - jj) as f64 / (jj + 1) as f64;
}
return out;
}
}
}
num as f64
}
#[inline]
fn horner_polynomial(x: f64, coeffs: &[f64]) -> f64 {
coeffs.iter().rev().fold(0.0, |acc, &c| acc * x + c)
}
#[inline]
pub fn stable_polynomial_times_exp_neg(x: f64, coeffs: &[f64]) -> f64 {
if coeffs.is_empty() || !x.is_finite() {
return 0.0;
}
const DIRECT_EXP_SWITCH: f64 = 600.0;
if x <= DIRECT_EXP_SWITCH {
return horner_polynomial(x, coeffs) * (-x).exp();
}
let inv_x = x.recip();
let mut tail = 0.0;
for &c in coeffs {
tail = tail * inv_x + c;
}
let degree = (coeffs.len() - 1) as f64;
let scale = (degree * x.ln() - x).exp();
scale * tail
}