use crate::FloatScalar;
use super::gamma_fn::lgamma;
pub fn erf<T: FloatScalar>(x: T) -> T {
if x.is_nan() {
return x;
}
let one = T::one();
let zero = T::zero();
let ax = x.abs();
let sign = if x < zero { -one } else { one };
if ax > T::from(6.0).unwrap() {
return sign;
}
let a = T::from(0.5).unwrap();
let x2 = ax * ax;
match inc_gamma_p(a, x2) {
Some(p) => sign * p,
None => sign, }
}
pub fn erfc<T: FloatScalar>(x: T) -> T {
if x.is_nan() {
return x;
}
let one = T::one();
let zero = T::zero();
let two = T::from(2.0).unwrap();
let ax = x.abs();
if ax > T::from(27.0).unwrap() {
return if x > zero { zero } else { two };
}
let a = T::from(0.5).unwrap();
let x2 = ax * ax;
match inc_gamma_pair(a, x2) {
Some((p, q)) => {
if x >= zero {
q
} else {
one + p
}
}
None => {
if x >= zero { zero } else { two }
}
}
}
const MAX_ITER: usize = 200;
fn inc_gamma_p<T: FloatScalar>(a: T, x: T) -> Option<T> {
let (p, _) = inc_gamma_pair(a, x)?;
Some(p)
}
fn inc_gamma_pair<T: FloatScalar>(a: T, x: T) -> Option<(T, T)> {
let zero = T::zero();
let one = T::one();
if x == zero {
return Some((zero, one));
}
let log_pf = -x + a * x.ln() - lgamma(a);
let pf = log_pf.exp();
if x < a + one {
let p = series_p(a, x, pf)?;
Some((p, one - p))
} else {
let q = cf_q(a, x, pf)?;
Some((one - q, q))
}
}
fn series_p<T: FloatScalar>(a: T, x: T, pf: T) -> Option<T> {
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 Some(pf * sum);
}
}
None
}
fn cf_q<T: FloatScalar>(a: T, x: T, pf: T) -> Option<T> {
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 Some(pf * f.recip());
}
}
None
}