use crate::Float;
use crate::Float106;
const SERIES_TAIL_CROSSOVER: f64 = 1.5;
impl Float106 {
pub fn erf(self) -> Self {
if self.is_nan() {
return Self::nan();
}
if self.hi == 0.0 && self.lo == 0.0 {
return Self::from_f64(0.0);
}
let ax = self.abs();
let magnitude = if ax.hi < SERIES_TAIL_CROSSOVER {
erf_series(ax)
} else {
Self::from_f64(1.0) - erfc_tail(ax)
};
if self.is_sign_negative() {
-magnitude
} else {
magnitude
}
}
pub fn erfc(self) -> Self {
if self.is_nan() {
return Self::nan();
}
if self.hi <= 0.0 {
return Self::from_f64(1.0) - self.erf();
}
if self.hi < SERIES_TAIL_CROSSOVER {
Self::from_f64(1.0) - erf_series(self)
} else {
erfc_tail(self)
}
}
}
#[inline]
fn two_over_sqrt_pi() -> Float106 {
Float106::from_f64(2.0) / Float106::PI.sqrt()
}
#[inline]
fn inv_sqrt_pi() -> Float106 {
Float106::from_f64(1.0) / Float106::PI.sqrt()
}
fn erf_series(ax: Float106) -> Float106 {
let x2 = ax * ax;
let two_x2 = x2 * Float106::from_f64(2.0);
let mut term = ax;
let mut sum = ax;
let mut n: u32 = 1;
loop {
term = term * two_x2 / Float106::from_f64((2 * n + 1) as f64);
sum += term;
if term.abs().hi < sum.abs().hi * 1e-34 {
break;
}
n += 1;
if n > 400 {
break;
}
}
two_over_sqrt_pi() * (-x2).exp() * sum
}
fn erfc_tail(x: Float106) -> Float106 {
let tiny = Float106::from_f64(1e-300);
let one = Float106::from_f64(1.0);
let mut f = x;
let mut c = f;
let mut d = Float106::from_f64(0.0);
let mut i: u32 = 1;
loop {
let a = Float106::from_f64(i as f64 * 0.5);
d = x + a * d;
if d.hi == 0.0 {
d = tiny;
}
d = d.recip();
c = x + a / c;
if c.hi == 0.0 {
c = tiny;
}
let delta = c * d;
f *= delta;
if (delta - one).abs().hi < 1e-32 {
break;
}
i += 1;
if i > 20_000 {
break;
}
}
let x2 = x * x;
(-x2).exp() * inv_sqrt_pi() / f
}