use crate::FloatScalar;
use super::SpecialError;
use super::beta_fn::lbeta;
const MAX_ITER: usize = 200;
pub fn betainc<T: FloatScalar>(a: T, b: T, x: T) -> Result<T, SpecialError> {
let zero = T::zero();
let one = T::one();
if a <= zero || b <= zero {
return Err(SpecialError::DomainError);
}
if x < zero || x > one {
return Err(SpecialError::DomainError);
}
if x == zero {
return Ok(zero);
}
if x == one {
return Ok(one);
}
let two = one + one;
if x > (a + one) / (a + b + two) {
Ok(one - betainc_cf(b, a, one - x)?)
} else {
betainc_cf(a, b, x)
}
}
fn betainc_cf<T: FloatScalar>(a: T, b: T, x: T) -> Result<T, SpecialError> {
let one = T::one();
let two = one + one;
let eps = T::epsilon();
let tiny = T::from(1e-30).unwrap();
let ln_prefix = a * x.ln() + b * (one - x).ln() - lbeta(a, b);
let prefix = ln_prefix.exp() / a;
let qab = a + b;
let qap = a + one;
let qam = a - one;
let mut c = one;
let mut d = one - qab * x / qap;
if d.abs() < tiny {
d = tiny;
}
d = one / d;
let mut f = d;
for m in 1..=MAX_ITER {
let fm = T::from(m).unwrap();
let m2 = two * fm;
let aa_even = fm * (b - fm) * x / ((qam + m2) * (a + m2));
d = one + aa_even * d;
if d.abs() < tiny {
d = tiny;
}
c = one + aa_even / c;
if c.abs() < tiny {
c = tiny;
}
d = one / d;
f = f * d * c;
let aa_odd = -((a + fm) * (qab + fm) * x) / ((a + m2) * (qap + m2));
d = one + aa_odd * d;
if d.abs() < tiny {
d = tiny;
}
c = one + aa_odd / c;
if c.abs() < tiny {
c = tiny;
}
d = one / d;
let delta = d * c;
f = f * delta;
if (delta - one).abs() < eps {
return Ok(prefix * f);
}
}
Err(SpecialError::ConvergenceFailure)
}