use super::LN2;
const A0: f64 = 1.1975323115670912564578e0;
const A1: f64 = 4.7072688112383978012285e1;
const A2: f64 = 6.9706266534389598238465e2;
const A3: f64 = 4.8548868893843886794648e3;
const A4: f64 = 1.6235862515167575384252e4;
const A5: f64 = 2.3782041382114385731252e4;
const A6: f64 = 1.1819493347062294404278e4;
const A7: f64 = 8.8709406962545514830200e2;
const B0: f64 = 1.0000000000000000000e0;
const B1: f64 = 4.2313330701600911252e1;
const B2: f64 = 6.8718700749205790830e2;
const B3: f64 = 5.3941960214247511077e3;
const B4: f64 = 2.1213794301586595867e4;
const B5: f64 = 3.9307895800092710610e4;
const B6: f64 = 2.8729085735721942674e4;
const B7: f64 = 5.2264952788528545610e3;
const C0: f64 = 1.42343711074968357734e0;
const C1: f64 = 4.63033784615654529590e0;
const C2: f64 = 5.76949722146069140550e0;
const C3: f64 = 3.64784832476320460504e0;
const C4: f64 = 1.27045825245236838258e0;
const C5: f64 = 2.41780725177450611770e-1;
const C6: f64 = 2.27238449892691845833e-2;
const C7: f64 = 7.74545014278341407640e-4;
const D0: f64 = 1.4142135623730950488016887e0;
const D1: f64 = 2.9036514445419946173133295e0;
const D2: f64 = 2.3707661626024532365971225e0;
const D3: f64 = 9.7547832001787427186894837e-1;
const D4: f64 = 2.0945065210512749128288442e-1;
const D5: f64 = 2.1494160384252876777097297e-2;
const D6: f64 = 7.7441459065157709165577218e-4;
const D7: f64 = 1.4859850019840355905497876e-9;
const E0: f64 = 6.65790464350110377720e0;
const E1: f64 = 5.46378491116411436990e0;
const E2: f64 = 1.78482653991729133580e0;
const E3: f64 = 2.96560571828504891230e-1;
const E4: f64 = 2.65321895265761230930e-2;
const E5: f64 = 1.24266094738807843860e-3;
const E6: f64 = 2.71155556874348757815e-5;
const E7: f64 = 2.01033439929228813265e-7;
const F0: f64 = 1.414213562373095048801689e0;
const F1: f64 = 8.482908416595164588112026e-1;
const F2: f64 = 1.936480946950659106176712e-1;
const F3: f64 = 2.103693768272068968719679e-2;
const F4: f64 = 1.112800997078859844711555e-3;
const F5: f64 = 2.611088405080593625138020e-5;
const F6: f64 = 2.010321207683943062279931e-7;
const F7: f64 = 2.891024605872965461538222e-15;
pub fn erf_inv(x: f64) -> f64 {
if f64::is_nan(x) {
return f64::NAN;
} else if x == -1.0 {
return f64::NEG_INFINITY;
} else if x == 1.0 {
return f64::INFINITY;
} else if x < -1.0 || x > 1.0 {
return f64::NAN;
}
let mut xx = x;
let mut negative = false;
if xx < 0.0 {
xx = -x;
negative = true;
}
let ans = if xx <= 0.85 {
let r = 0.180625 - 0.25 * xx * xx;
let z1 = ((((((A7 * r + A6) * r + A5) * r + A4) * r + A3) * r + A2) * r + A1) * r + A0;
let z2 = ((((((B7 * r + B6) * r + B5) * r + B4) * r + B3) * r + B2) * r + B1) * r + B0;
(xx * z1) / z2
} else {
let mut r = f64::sqrt(LN2 - f64::ln(1.0 - xx));
let (z1, z2) = if r <= 5.0 {
r -= 1.6;
(
((((((C7 * r + C6) * r + C5) * r + C4) * r + C3) * r + C2) * r + C1) * r + C0,
((((((D7 * r + D6) * r + D5) * r + D4) * r + D3) * r + D2) * r + D1) * r + D0,
)
} else {
r -= 5.0;
(
((((((E7 * r + E6) * r + E5) * r + E4) * r + E3) * r + E2) * r + E1) * r + E0,
((((((F7 * r + F6) * r + F5) * r + F4) * r + F3) * r + F2) * r + F1) * r + F0,
)
};
z1 / z2
};
if negative {
return -ans;
}
ans
}
pub fn erfc_inv(x: f64) -> f64 {
erf_inv(1.0 - x)
}
#[cfg(test)]
mod tests {
use super::{erf_inv, erfc_inv};
use crate::math::{erf, erfc};
use crate::{approx_eq, assert_alike};
#[test]
fn erf_inv_works_1() {
assert_eq!(erf_inv(-1.0), f64::NEG_INFINITY);
assert_eq!(erf_inv(0.0), 0.0);
assert_eq!(erf_inv(1.0), f64::INFINITY);
#[rustfmt::skip]
let mathematica =[
(-0.95 , 1e-15, -1.3859038243496777),
(-0.9 , 1e-15, -1.1630871536766745),
(-0.85 , 1e-50, -1.0179024648320276),
(-0.8 , 1e-15, -0.9061938024368232),
(-0.75 , 1e-15, -0.8134198475976185),
(-0.7 , 1e-50, -0.7328690779592167),
(-0.6499999999999999 , 1e-15, -0.6608544253423858),
(-0.6 , 1e-50, -0.5951160814499948),
(-0.55 , 1e-15, -0.5341590877497026),
(-0.5 , 1e-50, -0.4769362762044699),
(-0.44999999999999996 , 1e-16, -0.4226802386475618),
(-0.3999999999999999 , 1e-16, -0.3708071585935579),
(-0.35 , 1e-15, -0.3208583217151815),
(-0.29999999999999993 , 1e-16, -0.2724627147267543),
(-0.25 , 1e-16, -0.2253120550121781),
(-0.19999999999999996 , 1e-16, -0.17914345462129166),
(-0.1499999999999999 , 1e-50, -0.13372692166481961),
(-0.09999999999999998 , 1e-16, -0.08885599049425766),
(-0.04999999999999993 , 1e-17, -0.044340387910005434),
(0. , 1e-50, 0.0),
(0.050000000000000044 , 1e-50, 0.04434038791000553),
(0.10000000000000009 , 1e-16, 0.08885599049425776),
(0.15000000000000013 , 1e-16, 0.13372692166481984),
(0.20000000000000018 , 1e-16, 0.17914345462129186),
(0.25 , 1e-16, 0.2253120550121781),
(0.30000000000000004 , 1e-16, 0.27246271472675443),
(0.3500000000000001 , 1e-16, 0.32085832171518164),
(0.40000000000000013 , 1e-16, 0.370807158593558),
(0.4500000000000002 , 1e-50, 0.4226802386475621),
(0.5 , 1e-50, 0.4769362762044699),
(0.55 , 1e-15, 0.5341590877497026),
(0.6000000000000001 , 1e-15, 0.595116081449995),
(0.6500000000000001 , 1e-15, 0.6608544253423861),
(0.7000000000000002 , 1e-15, 0.7328690779592172),
(0.75 , 1e-15, 0.8134198475976185),
(0.8 , 1e-15, 0.9061938024368232),
(0.8500000000000001 , 1e-15, 1.0179024648320278),
(0.9000000000000001 , 1e-15, 1.1630871536766747),
(0.9500000000000002 , 1e-15, 1.385903824349679),
];
for (x, tol, reference) in mathematica {
approx_eq(erf_inv(x), reference, tol);
}
}
#[test]
fn erf_inv_works_2() {
let x = 1.0 - 2.0 * f64::exp(-25.0);
let xm = 0.99999999997222411227007195881067647250782628617920;
assert_eq!(x, xm);
let ym = 4.7078495219130335562325296830779236928390597996186;
let y5 = erf_inv(x);
approx_eq(y5, ym, 1e-7); let x = x + 1e-16;
let y5plus = erf_inv(x);
approx_eq(y5plus, y5, 1e-6);
approx_eq(erf_inv(0.99999999999), 4.812924058944833, 1e-15);
}
#[test]
fn erfc_inv_works_1() {
#[rustfmt::skip]
let mathematica =[
(0.05 , 1e-15, 1.3859038243496782),
(0.1 , 1e-15, 1.1630871536766743),
(0.15000000000000002 , 1e-50, 1.0179024648320276),
(0.2 , 1e-15, 0.9061938024368236),
(0.25 , 1e-15, 0.8134198475976185),
(0.30000000000000004 , 1e-15, 0.732869077959217),
(0.35000000000000003 , 1e-15, 0.6608544253423858),
(0.4 , 1e-15, 0.5951160814499951),
(0.45 , 1e-50, 0.5341590877497024),
(0.5 , 1e-16, 0.47693627620446993),
(0.55 , 1e-16, 0.42268023864756193),
(0.6000000000000001 , 1e-16, 0.3708071585935579),
(0.65 , 1e-16, 0.32085832171518147),
(0.7000000000000001 , 1e-16, 0.2724627147267543),
(0.75 , 1e-15, 0.22531205501217816),
(0.8 , 1e-16, 0.17914345462129166),
(0.8500000000000001 , 1e-50, 0.13372692166481961),
(0.9 , 1e-16, 0.08885599049425766),
(0.9500000000000001 , 1e-16, 0.044340387910005455),
(1. , 1e-50, 0.),
(1.05 , 1e-16, -0.04434038791000555),
(1.1 , 1e-16, -0.08885599049425776),
(1.1500000000000001 , 1e-16, -0.1337269216648198),
(1.2000000000000002 , 1e-16, -0.17914345462129186),
(1.25 , 1e-15, -0.22531205501217816),
(1.3 , 1e-16, -0.27246271472675443),
(1.35 , 1e-50, -0.3208583217151816),
(1.4000000000000001 , 1e-15, -0.3708071585935582),
(1.4500000000000002 , 1e-50, -0.4226802386475621),
(1.5 , 1e-16, -0.47693627620446993),
(1.55 , 1e-50, -0.5341590877497024),
(1.6 , 1e-50, -0.5951160814499951),
(1.6500000000000001 , 1e-50, -0.660854425342386),
(1.7000000000000002 , 1e-50, -0.7328690779592172),
(1.75 , 1e-15, -0.8134198475976185),
(1.8 , 1e-15, -0.9061938024368236),
(1.85 , 1e-15, -1.017902464832028),
(1.9000000000000001 , 1e-15, -1.1630871536766745),
(1.9500000000000002 , 1e-15, -1.385903824349679),
];
for (x, tol, reference) in mathematica {
approx_eq(erfc_inv(x), reference, tol);
}
}
const VALUES: [f64; 10] = [
4.9790119248836735e+00,
7.7388724745781045e+00,
-2.7688005719200159e-01,
-5.0106036182710749e+00,
9.6362937071984173e+00,
2.9263772392439646e+00,
5.2290834314593066e+00,
2.7279399104360102e+00,
1.8253080916808550e+00,
-8.6859247685756013e+00,
];
const SOLUTION_ERF_INV: [f64; 10] = [
4.746037673358033586786350696e-01,
8.559054432692110956388764172e-01,
-2.45427830571707336251331946e-02,
-4.78116683518973366268905506e-01,
1.479804430319470983648120853e+00,
2.654485787128896161882650211e-01,
5.027444534221520197823192493e-01,
2.466703532707627818954585670e-01,
1.632011465103005426240343116e-01,
-1.06672334642196900710000389e+00,
];
const SC_VALUES_ERF_INV: [f64; 6] = [1.0, -1.0, 0.0, f64::NEG_INFINITY, f64::INFINITY, f64::NAN];
const SC_SOLUTION_ERF_INV: [f64; 6] = [f64::INFINITY, f64::NEG_INFINITY, 0.0, f64::NAN, f64::NAN, f64::NAN];
const SC_VALUES_ERFC_INV: [f64; 6] = [0.0, 2.0, 1.0, f64::INFINITY, f64::NEG_INFINITY, f64::NAN];
const SC_SOLUTION_ERFC_INV: [f64; 6] = [f64::INFINITY, f64::NEG_INFINITY, 0.0, f64::NAN, f64::NAN, f64::NAN];
#[test]
fn test_erf_inv() {
for (i, v) in VALUES.iter().enumerate() {
let a = *v / 10.0;
let f = erf_inv(a);
approx_eq(SOLUTION_ERF_INV[i], f, 1e-15);
}
for (i, v) in SC_VALUES_ERF_INV.iter().enumerate() {
let f = erf_inv(*v);
assert_alike(SC_SOLUTION_ERF_INV[i], f);
}
let mut x = -0.9;
let dx = 0.3;
while x <= 0.90 {
let f = erf(erf_inv(x));
approx_eq(x, f, 1e-15);
x += dx;
}
let mut x = -0.9;
while x <= 0.90 {
let f = erf_inv(erf(x));
approx_eq(x, f, 1e-15);
x += dx;
}
}
#[test]
fn test_erfc_inv() {
for (i, v) in VALUES.iter().enumerate() {
let a = 1.0 - (*v / 10.0);
let f = erfc_inv(a);
approx_eq(SOLUTION_ERF_INV[i], f, 1e-15);
}
for (i, v) in SC_VALUES_ERFC_INV.iter().enumerate() {
let f = erfc_inv(*v);
assert_alike(SC_SOLUTION_ERFC_INV[i], f);
}
let mut x = 0.1;
let dx = 0.3;
while x <= 1.9 {
let f = erfc(erfc_inv(x));
approx_eq(x, f, 1e-15);
x += dx;
}
let mut x = 0.1;
while x <= 1.9 {
let f = erfc_inv(erfc(x));
approx_eq(x, f, 1e-14);
x += dx;
}
}
}