use crate::common::f_fmla;
use crate::gamma::lnbetaf::lnbetaf_core;
use crate::{f_exp, f_log, f_log1p, f_pow, f_powf};
pub fn f_betainc_regf(a: f32, b: f32, x: f32) -> f32 {
let aa = a.to_bits();
let ab = b.to_bits();
let ax = x.to_bits();
if aa >= 0xffu32 << 23
|| aa == 0
|| ab >= 0xffu32 << 23
|| ab == 0
|| ax == 0
|| ax >= 0x3f800000
{
if (aa >> 31) != 0 || (ab >> 31) != 0 || (ax >> 31) != 0 {
return f32::NAN;
}
if ax >= 0x3f800000 {
if ax == 0x3f800000 {
return 1.;
}
return f32::NAN;
}
if aa.wrapping_shl(1) == 0 {
return 1.0;
}
if ab.wrapping_shl(1) == 0 {
return 0.;
}
if ax.wrapping_shl(1) == 0 {
return 0.;
}
if a.is_infinite() {
return 0.;
}
if b.is_infinite() {
return 1.;
}
return a + f32::NAN; }
if aa == 0x3f800000 {
return (1. - f_pow(1. - x as f64, b as f64)) as f32;
}
if ab == 0x3f800000 {
return f_powf(x, a);
}
let mut return_inverse = false;
let mut dx = x as f64;
let mut a = a;
let mut b = b;
if x > (a + 1.0) / (a + b + 2.0) {
std::mem::swap(&mut a, &mut b);
dx = 1.0 - dx;
return_inverse = true;
}
let da = a as f64;
let db = b as f64;
let w0 = f_fmla(f_log(dx), da, f_fmla(f_log1p(-dx), db, -lnbetaf_core(a, b)));
let front = f_exp(w0) / da;
let mut f = 1.0;
let mut c = 1.0;
let mut d = 0.0;
const TINY: f64 = 1.0e-30;
const STOP: f64 = 1.0e-8;
for i in 0..200 {
let m = i / 2;
let numerator: f64 = if i == 0 {
1.0
} else if i % 2 == 0 {
let m = m as f64;
let c0 = f_fmla(2.0, m, da);
(m * (db - m) * dx) / ((c0 - 1.0) * c0)
} else {
let m = m as f64;
let c0 = f_fmla(2.0, m, da);
-((da + m) * (da + db + m) * dx) / (c0 * (c0 + 1.))
};
d = f_fmla(numerator, d, 1.0);
if d.abs() < TINY {
d = TINY;
}
d = 1.0 / d;
c = 1.0 + numerator / c;
if c.abs() < TINY {
c = TINY;
}
let cd = c * d;
f *= cd;
if (1.0 - cd).abs() < STOP {
return if return_inverse {
f_fmla(-front, f - 1.0, 1.) as f32
} else {
(front * (f - 1.0)) as f32
};
}
}
if return_inverse {
f_fmla(-front, f - 1.0, 1.) as f32
} else {
(front * (f - 1.0)) as f32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_betaincf() {
assert_eq!(f_betainc_regf(0.5, 2.5, 0.5), 0.9244131815783875);
assert_eq!(f_betainc_regf(2.5, 1.0, 0.5), 0.1767766952966368811);
assert_eq!(f_betainc_regf(0.5, 0.5, 0.5), 0.5);
assert_eq!(f_betainc_regf(54221., 23124., 0.64534), 0.0);
assert_eq!(f_betainc_regf(5., 1.4324, 0.1312), 8.872578e-5);
assert_eq!(f_betainc_regf(7., 42., 0.4324), 0.99999547);
assert_eq!(f_betainc_regf(0.5, 0., 1.), 1.);
assert_eq!(f_betainc_regf(5., 2., 1.), 1.);
assert_eq!(f_betainc_regf(5., 2., 0.), 0.);
assert_eq!(f_betainc_regf(5., 2., 0.5), 0.109375);
assert!(f_betainc_regf(5., 2., -1.).is_nan());
assert!(f_betainc_regf(5., 2., 1.1).is_nan());
assert!(f_betainc_regf(5., 2., f32::INFINITY).is_nan());
assert!(f_betainc_regf(5., 2., f32::NEG_INFINITY).is_nan());
assert!(f_betainc_regf(5., 2., f32::NAN).is_nan());
assert!(f_betainc_regf(-5., 2., 0.432).is_nan());
assert!(f_betainc_regf(5., -2., 0.432).is_nan());
assert!(f_betainc_regf(5., 2., -0.432).is_nan());
}
}