use crate::FloatScalar;
use super::SpecialError;
use super::gamma_fn::lgamma;
const MAX_ITER: usize = 200;
pub fn gamma_inc<T: FloatScalar>(a: T, x: T) -> Result<T, SpecialError> {
let (p, _q) = gamma_inc_pair(a, x)?;
Ok(p)
}
pub fn gamma_inc_upper<T: FloatScalar>(a: T, x: T) -> Result<T, SpecialError> {
let (_p, q) = gamma_inc_pair(a, x)?;
Ok(q)
}
fn gamma_inc_pair<T: FloatScalar>(a: T, x: T) -> Result<(T, T), SpecialError> {
let zero = T::zero();
let one = T::one();
if a <= zero || x < zero {
return Err(SpecialError::DomainError);
}
if x == zero {
return Ok((zero, one));
}
let log_prefactor = -x + a * x.ln() - lgamma(a);
let prefactor = log_prefactor.exp();
if x < a + one {
let p = series_p(a, x, prefactor)?;
Ok((p, one - p))
} else {
let q = cf_q(a, x, prefactor)?;
Ok((one - q, q))
}
}
fn series_p<T: FloatScalar>(a: T, x: T, prefactor: T) -> Result<T, SpecialError> {
let one = T::one();
let eps = T::epsilon();
let mut term = one / a;
let mut sum = term;
let mut ap = a;
for _ in 0..MAX_ITER {
ap = ap + one;
term = term * x / ap;
sum = sum + term;
if term.abs() < sum.abs() * eps {
return Ok(prefactor * sum);
}
}
Err(SpecialError::ConvergenceFailure)
}
fn cf_q<T: FloatScalar>(a: T, x: T, prefactor: T) -> Result<T, SpecialError> {
let one = T::one();
let eps = T::epsilon();
let tiny = T::from(1e-30).unwrap();
let b0 = x + one - a;
let mut f = if b0.abs() < tiny { tiny } else { b0 };
let mut c = f;
let mut d = T::zero();
for n in 1..=MAX_ITER {
let nf = T::from(n).unwrap();
let an = nf * (a - nf); let bn = x + T::from(2 * n + 1).unwrap() - a;
d = bn + an * d;
if d.abs() < tiny {
d = tiny;
}
d = one / d;
c = bn + an / c;
if c.abs() < tiny {
c = tiny;
}
let delta = c * d;
f = f * delta;
if (delta - one).abs() < eps {
return Ok(prefactor * f.recip());
}
}
Err(SpecialError::ConvergenceFailure)
}