use crate::betaln;
pub fn betainc(x: f64, z: f64, w: f64, lower: bool) -> f64 {
if x.is_nan() || z.is_nan() || w.is_nan() || x < 0.0 || x > 1.0 || z <= 0.0 || w <= 0.0 {
return f64::NAN;
}
if x == 0.0 {
return if lower { 0.0 } else { 1.0 };
}
if x == 1.0 { return if lower { 1.0 } else { 0.0 };
}
if x > (z + 1.0) / (z + w + 2.0) {
return if lower {
1.0 - betainc_cf(1.0 - x, w, z)
} else {
betainc_cf(1.0 - x, w, z)
};
}
let val = betainc_cf(x, z, w);
if lower { val } else { 1.0 - val }
}
fn betainc_cf(x: f64, z: f64, w: f64) -> f64 {
let ln_beta = betaln(z, w);
let front = (z * x.ln() + w * (1.0 - x).ln() - ln_beta).exp() / z;
let mut c = 1.0;
let mut d = 1.0 - (z + w) * x / (z + 1.0);
let tiny = 1e-30;
if d.abs() < tiny { d = tiny; }
d = 1.0 / d;
let mut h = d;
for m in 1..200 {
let m_f = m as f64;
let m2 = 2.0 * m_f;
let aa = m_f * (w - m_f) * x / ((z + m2 - 1.0) * (z + m2));
d = 1.0 + aa * d;
if d.abs() < tiny { d = tiny; }
c = 1.0 + aa / c;
if c.abs() < tiny { c = tiny; }
d = 1.0 / d;
h *= d * c;
let aa = -(z + m_f) * (z + w + m_f) * x / ((z + m2) * (z + m2 + 1.0));
d = 1.0 + aa * d;
if d.abs() < tiny { d = tiny; }
c = 1.0 + aa / c;
if c.abs() < tiny { c = tiny; }
d = 1.0 / d;
let delta = d * c;
h *= delta;
if (delta - 1.0).abs() < 1e-16 {
break;
}
}
front * h
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_betainc_basic() {
assert!((betainc(0.5, 1.0, 1.0, true) - 0.5).abs() < 1e-15);
assert!((betainc(0.5, 2.0, 2.0, true) - 0.5).abs() < 1e-15);
assert!((betainc(0.2, 1.0, 3.0, true) - 0.488).abs() < 1e-15);
}
#[test]
fn test_betainc_symmetry() {
let x = 0.3;
let z = 2.5;
let w = 1.5;
let lower = betainc(x, z, w, true);
let upper = betainc(x, z, w, false);
assert!((lower + upper - 1.0).abs() < 1e-15);
}
#[test]
fn test_betainc_boundaries() {
assert_eq!(betainc(0.0, 1.0, 1.0, true), 0.0);
assert_eq!(betainc(1.0, 1.0, 1.0, true), 1.0);
}
}