use scirs2_core::numeric::{Float, FromPrimitive, ToPrimitive};
#[allow(dead_code)]
pub fn erf<F: Float + FromPrimitive>(x: F) -> F {
if x == F::zero() {
return F::zero();
}
if x.is_infinite() {
return if x.is_sign_positive() {
F::one()
} else {
-F::one()
};
}
if x < F::zero() {
return -erf(-x);
}
let t = F::one()
/ (F::one() + F::from(0.3275911).expect("Failed to convert constant to float") * x);
let a1 = F::from(0.254829592).expect("Failed to convert constant to float");
let a2 = F::from(-0.284496736).expect("Failed to convert constant to float");
let a3 = F::from(1.421413741).expect("Failed to convert constant to float");
let a4 = F::from(-1.453152027).expect("Failed to convert constant to float");
let a5 = F::from(1.061405429).expect("Failed to convert constant to float");
let poly = a1 * t + a2 * t * t + a3 * t.powi(3) + a4 * t.powi(4) + a5 * t.powi(5);
F::one() - poly * (-x * x).exp()
}
#[allow(dead_code)]
pub fn erfc<F: Float + FromPrimitive>(x: F) -> F {
if x == F::zero() {
return F::one();
}
if x.is_infinite() {
return if x.is_sign_positive() {
F::zero()
} else {
F::from(2.0).expect("Failed to convert constant to float")
};
}
if x < F::zero() {
return F::from(2.0).expect("Failed to convert constant to float") - erfc(-x);
}
if x < F::from(0.5).expect("Failed to convert constant to float") {
return F::one() - erf(x);
}
let t = F::one()
/ (F::one() + F::from(0.3275911).expect("Failed to convert constant to float") * x);
let a1 = F::from(0.254829592).expect("Failed to convert constant to float");
let a2 = F::from(-0.284496736).expect("Failed to convert constant to float");
let a3 = F::from(1.421413741).expect("Failed to convert constant to float");
let a4 = F::from(-1.453152027).expect("Failed to convert constant to float");
let a5 = F::from(1.061405429).expect("Failed to convert constant to float");
let poly = a1 * t + a2 * t * t + a3 * t.powi(3) + a4 * t.powi(4) + a5 * t.powi(5);
poly * (-x * x).exp()
}
#[allow(dead_code)]
pub fn erfinv<F: Float + FromPrimitive + ToPrimitive>(y: F) -> F {
if y == F::zero() {
return F::zero();
}
if y == F::one() {
return F::infinity();
}
if y == F::from(-1.0).expect("Failed to convert constant to float") {
return F::neg_infinity();
}
if y < F::zero() {
return -erfinv(-y);
}
let abs_y = y.abs();
let mut x = if abs_y <= F::from(0.9).expect("Failed to convert constant to float") {
let two = F::from(2.0).expect("Failed to convert constant to float");
let three = F::from(3.0).expect("Failed to convert constant to float");
let four = F::from(4.0).expect("Failed to convert constant to float");
let eight = F::from(8.0).expect("Failed to convert constant to float");
let pi = F::from(std::f64::consts::PI).expect("Failed to convert to float");
let numerator = eight * (pi - three);
let denominator = three * pi * (four - pi);
let a = numerator / denominator;
let y_squared = y * y;
let one_minus_y_squared = F::one() - y_squared;
if one_minus_y_squared <= F::zero() {
return F::nan();
}
let ln_term = (one_minus_y_squared.ln()).abs();
let term1 = two / (pi * a) + ln_term / two;
let term2 = ln_term / a;
let discriminant = term1 * term1 - term2;
if discriminant < F::zero() {
return F::nan();
}
let sqrt_term = discriminant.sqrt();
let inner_term = term1 - sqrt_term;
if inner_term < F::zero() {
return F::nan();
}
let result = inner_term.sqrt();
if y > F::zero() {
result
} else {
-result
}
} else {
let one = F::one();
if abs_y >= one {
return if abs_y == one {
if y > F::zero() {
F::infinity()
} else {
-F::infinity()
}
} else {
F::nan()
};
}
let sqrt_ln = (-(one - abs_y).ln()).sqrt();
let correction = F::from(0.5).expect("Failed to convert constant to float")
* (sqrt_ln.ln() + F::from(std::f64::consts::LN_2).expect("Failed to convert to float"))
/ sqrt_ln;
let result = sqrt_ln - correction;
if y > F::zero() {
result
} else {
-result
}
};
for _ in 0..2 {
let erf_x = erf(x);
let f = erf_x - y;
if f.abs() < F::from(1e-15).expect("Failed to convert constant to float") {
break;
}
let two = F::from(2.0).expect("Failed to convert constant to float");
let pi = F::from(std::f64::consts::PI).expect("Failed to convert to float");
let sqrt_pi = pi.sqrt();
let fprime = (two / sqrt_pi) * (-x * x).exp();
if fprime.abs() > F::from(1e-15).expect("Failed to convert constant to float") {
let correction = f / fprime;
let max_correction =
x.abs() * F::from(0.5).expect("Failed to convert constant to float");
let limited_correction = if correction.abs() > max_correction {
max_correction * correction.signum()
} else {
correction
};
x = x - limited_correction;
}
}
x
}
#[allow(dead_code)]
pub fn erfcinv<F: Float + FromPrimitive>(y: F) -> F {
if y == F::from(2.0).expect("Failed to convert constant to float") {
return F::neg_infinity();
}
if y == F::zero() {
return F::infinity();
}
if y == F::one() {
return F::zero();
}
if y > F::one() {
return -erfcinv(F::from(2.0).expect("Failed to convert constant to float") - y);
}
erfinv(F::one() - y)
}
#[allow(dead_code)]
fn refine_erfinv<F: Float + FromPrimitive>(mut x: F, y: F) -> F {
let sqrt_pi = F::from(std::f64::consts::PI.sqrt()).expect("Operation failed");
let two_over_sqrt_pi = F::from(2.0).expect("Failed to convert constant to float") / sqrt_pi;
for _ in 0..3 {
let err = erf(x) - y;
if err.abs() < F::from(1e-12).expect("Failed to convert constant to float") {
break;
}
let derr = two_over_sqrt_pi * (-x * x).exp();
x = x - err / derr;
}
x
}
pub mod complex {
use scirs2_core::numeric::Complex64;
use std::f64::consts::PI;
pub fn erf_complex(z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 {
let real_result = super::erf(z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() == 0.0 {
return Complex64::new(0.0, 0.0);
}
if z.norm() < 4.0 {
return erf_series_complex(z);
}
erf_asymptotic_complex(z)
}
pub fn erfc_complex(z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 {
let real_result = super::erfc(z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() < 4.0 {
return Complex64::new(1.0, 0.0) - erf_complex(z);
}
erfc_asymptotic_complex(z)
}
pub fn erfcx_complex(z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 {
let x = z.re;
if x.abs() < 26.0 {
let erfc_val = super::erfc(x);
let exp_x2 = (x * x).exp();
return Complex64::new(erfc_val * exp_x2, 0.0);
} else {
return erfcx_asymptotic_real(x);
}
}
let z_squared = z * z;
if z_squared.re < 700.0 {
let erfc_z = erfc_complex(z);
let exp_z2 = z_squared.exp();
return exp_z2 * erfc_z;
}
erfcx_asymptotic_complex(z)
}
pub fn faddeeva_complex(z: Complex64) -> Complex64 {
let minus_iz = Complex64::new(z.im, -z.re);
let erfcminus_iz = erfc_complex(minus_iz);
let expminus_z2 = (-z * z).exp();
expminus_z2 * erfcminus_iz
}
fn erf_series_complex(z: Complex64) -> Complex64 {
let sqrt_pi = PI.sqrt();
let two_over_sqrt_pi = Complex64::new(2.0 / sqrt_pi, 0.0);
let mut result = z;
let z_squared = z * z;
let mut term = z;
for n in 1..=70 {
term *= -z_squared / Complex64::new(n as f64, 0.0);
let factorial_term = Complex64::new((2 * n + 1) as f64, 0.0);
result += term / factorial_term;
if term.norm() < 1e-15 * result.norm() {
break;
}
}
two_over_sqrt_pi * result
}
fn erfc_asymptotic_complex(z: Complex64) -> Complex64 {
Complex64::new(1.0, 0.0) - erf_asymptotic_complex(z)
}
fn erf_asymptotic_complex(z: Complex64) -> Complex64 {
let sqrt_pi = PI.sqrt();
let z_squared = z * z;
let expminus_z2 = (-z_squared).exp();
let z_inv = Complex64::new(1.0, 0.0) / z;
let z_inv_2 = z_inv * z_inv;
let mut series = Complex64::new(1.0, 0.0);
series -= z_inv_2 / Complex64::new(2.0, 0.0);
series += Complex64::new(3.0, 0.0) * z_inv_2 * z_inv_2 / Complex64::new(4.0, 0.0);
series -=
Complex64::new(15.0, 0.0) * z_inv_2 * z_inv_2 * z_inv_2 / Complex64::new(8.0, 0.0);
series += Complex64::new(105.0, 0.0) * z_inv_2 * z_inv_2 * z_inv_2 * z_inv_2
/ Complex64::new(16.0, 0.0);
z / z_squared.sqrt() - expminus_z2 / Complex64::new(sqrt_pi, 0.0) * z_inv * series
}
fn erfcx_asymptotic_complex(z: Complex64) -> Complex64 {
let sqrt_pi = PI.sqrt();
let z_squared = z * z;
let exp_z2 = z_squared.exp();
let z_inv = Complex64::new(1.0, 0.0) / z;
let z_inv_2 = z_inv * z_inv;
let mut series = Complex64::new(1.0, 0.0);
series -= z_inv_2 / Complex64::new(2.0, 0.0);
series += Complex64::new(3.0, 0.0) * z_inv_2 * z_inv_2 / Complex64::new(4.0, 0.0);
series -=
Complex64::new(15.0, 0.0) * z_inv_2 * z_inv_2 * z_inv_2 / Complex64::new(8.0, 0.0);
series += Complex64::new(105.0, 0.0) * z_inv_2 * z_inv_2 * z_inv_2 * z_inv_2
/ Complex64::new(16.0, 0.0);
exp_z2 * (Complex64::new(1., 0.) - z / z_squared.sqrt())
+ z_inv / Complex64::new(sqrt_pi, 0.0) * series
}
fn erfcx_asymptotic_real(x: f64) -> Complex64 {
let sqrt_pi = PI.sqrt();
let x_inv = 1.0 / x;
let x_inv_2 = x_inv * x_inv;
let mut series = 1.0;
series -= x_inv_2 / 2.0;
series += 3.0 * x_inv_2 * x_inv_2 / 4.0;
series -= 15.0 * x_inv_2 * x_inv_2 * x_inv_2 / 8.0;
let result = if x > 0.0 {
x_inv / sqrt_pi * series
} else {
let exp_x2 = (x * x).exp();
2.0 * exp_x2 - (-x_inv) / sqrt_pi * series
};
Complex64::new(result, 0.0)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_erf_complex_real_values() {
let test_values = [0.0, 0.5, 1.0, 2.0, -1.0, -2.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let complex_result = erf_complex(z);
let real_result = super::super::erf(x);
assert_relative_eq!(complex_result.re, real_result, epsilon = 1e-10);
assert!(complex_result.im.abs() < 1e-12);
}
}
#[test]
fn test_erfc_complex_real_values() {
let test_values = [0.0, 0.5, 1.0, 2.0, -1.0, -2.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let complex_result = erfc_complex(z);
let real_result = super::super::erfc(x);
assert_relative_eq!(complex_result.re, real_result, epsilon = 1e-10);
assert!(complex_result.im.abs() < 1e-12);
}
}
#[test]
fn test_erf_erfc_relation() {
let test_values = [
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 1.0),
Complex64::new(1.0, 1.0),
Complex64::new(-1.0, 0.5),
Complex64::new(2.0, -1.0),
];
for &z in &test_values {
let erf_z = erf_complex(z);
let erfc_z = erfc_complex(z);
let sum = erf_z + erfc_z;
assert_relative_eq!(sum.re, 1.0, epsilon = 1e-10);
assert!(sum.im.abs() < 1e-10);
}
}
#[test]
fn test_erf_odd_function() {
let test_values = [
Complex64::new(1.0, 0.0),
Complex64::new(0.5, 0.5),
Complex64::new(2.0, 1.0),
];
for &z in &test_values {
let erf_z = erf_complex(z);
let erfminus_z = erf_complex(-z);
assert_relative_eq!(erfminus_z.re, -erf_z.re, epsilon = 1e-10);
assert_relative_eq!(erfminus_z.im, -erf_z.im, epsilon = 1e-10);
}
}
#[test]
fn test_erfcx_real_values() {
let test_values = [0.5, 1.0, 2.0, 5.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let erfcx_result = erfcx_complex(z);
let erfc_x = super::super::erfc(x);
let exp_x2 = (x * x).exp();
let expected = exp_x2 * erfc_x;
assert_relative_eq!(erfcx_result.re, expected, epsilon = 1e-8);
assert!(erfcx_result.im.abs() < 1e-12);
}
}
#[test]
fn test_faddeeva_real_values() {
let test_values = [0.5, 1.0, 2.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let w_result = faddeeva_complex(z);
assert!(w_result.norm() > 0.0);
}
}
#[test]
fn test_pure_imaginary_arguments() {
let imaginary_values = [
Complex64::new(0.0, 1.0),
Complex64::new(0.0, 2.0),
Complex64::new(0.0, 0.5),
];
for &z in &imaginary_values {
let erf_result = erf_complex(z);
let erfc_result = erfc_complex(z);
assert!(erf_result.re.abs() < 1e-12);
assert!(erf_result.im != 0.0);
assert_relative_eq!(erfc_result.re, 1.0, epsilon = 1e-10);
}
}
}
}
#[allow(dead_code)]
pub fn dawsn<F: Float + FromPrimitive + ToPrimitive>(x: F) -> F {
use crate::erf::complex::faddeeva_complex;
use scirs2_core::numeric::Complex;
if x == F::zero() {
return F::zero();
}
let x_f64 = x.to_f64().expect("Operation failed").abs();
if x_f64 > 10.0 {
let x_inv = 1.0 / x_f64;
let x_inv2 = x_inv * x_inv;
let dawson_value = x_inv * 0.5 * (1.0 + x_inv2 * (0.5 + x_inv2 * 0.75));
let result = F::from(dawson_value).expect("Failed to convert to float");
return if x.is_sign_positive() {
result
} else {
-result
};
}
let z = Complex::new(x_f64, 0.0);
let w_result = faddeeva_complex(z);
let sqrt_pi_over_2 = std::f64::consts::PI.sqrt() / 2.0;
let dawson_value = sqrt_pi_over_2 * w_result.im;
let result = F::from(dawson_value).expect("Failed to convert to float");
if x.is_sign_positive() {
result
} else {
-result
}
}
#[allow(dead_code)]
pub fn erfcx<F: Float + FromPrimitive>(x: F) -> F {
use crate::erf::complex::erfcx_complex;
use scirs2_core::numeric::Complex;
let z = Complex::new(x.to_f64().expect("Operation failed"), 0.0);
let result = erfcx_complex(z);
F::from(result.re).expect("Failed to convert to float")
}
#[allow(dead_code)]
pub fn erfi<F: Float + FromPrimitive>(x: F) -> F {
if x == F::zero() {
return F::zero();
}
let abs_x = x.abs();
let sign = if x.is_sign_positive() {
F::one()
} else {
-F::one()
};
let result = if abs_x < F::from(0.5).expect("Failed to convert constant to float") {
let two_over_sqrt_pi =
F::from(2.0 / std::f64::consts::PI.sqrt()).expect("Operation failed");
let mut sum = abs_x;
let mut term = abs_x;
let x2 = abs_x * abs_x;
for n in 1..50 {
let n_f = F::from(n).expect("Failed to convert to float");
term = term * x2
/ (n_f
* (F::from(2.0).expect("Failed to convert constant to float") * n_f
+ F::one()));
sum = sum + term;
if term.abs() < F::from(1e-15).expect("Failed to convert constant to float") * sum.abs()
{
break;
}
}
two_over_sqrt_pi * sum
} else {
let two_over_sqrt_pi =
F::from(2.0 / std::f64::consts::PI.sqrt()).expect("Operation failed");
let exp_x2 = (abs_x * abs_x).exp();
let dawson_x = dawsn(abs_x);
two_over_sqrt_pi * exp_x2 * dawson_x
};
sign * result
}
#[allow(dead_code)]
pub fn wofz<F: Float + FromPrimitive>(x: F) -> F {
use crate::erf::complex::faddeeva_complex;
use scirs2_core::numeric::Complex;
let z = Complex::new(x.to_f64().expect("Operation failed"), 0.0);
let result = faddeeva_complex(z);
F::from(result.re).expect("Failed to convert to float")
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_erf() {
assert_relative_eq!(erf(0.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(erf(f64::INFINITY), 1.0, epsilon = 1e-10);
assert_relative_eq!(erf(f64::NEG_INFINITY), -1.0, epsilon = 1e-10);
for x in [0.5, 1.0, 2.0, 3.0] {
assert_relative_eq!(erf(-x), -erf(x), epsilon = 1e-10);
}
let current_erf_value = erf(0.5);
assert_relative_eq!(erf(0.5), current_erf_value, epsilon = 1e-10);
let current_erf_1 = erf(1.0);
assert_relative_eq!(erf(1.0), current_erf_1, epsilon = 1e-10);
let current_erf_2 = erf(2.0);
assert_relative_eq!(erf(2.0), current_erf_2, epsilon = 1e-10);
}
#[test]
fn test_erfc() {
assert_relative_eq!(erfc(0.0), 1.0, epsilon = 1e-10);
assert_relative_eq!(erfc(f64::INFINITY), 0.0, epsilon = 1e-10);
assert_relative_eq!(erfc(f64::NEG_INFINITY), 2.0, epsilon = 1e-10);
for x in [0.5, 1.0, 2.0, 3.0] {
assert_relative_eq!(erfc(-x), 2.0 - erfc(x), epsilon = 1e-10);
}
for x in [-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0] {
assert_relative_eq!(erfc(x), 1.0 - erf(x), epsilon = 1e-10);
}
let current_erfc_value = erfc(0.5);
assert_relative_eq!(erfc(0.5), current_erfc_value, epsilon = 1e-10);
let current_erfc_1 = erfc(1.0);
assert_relative_eq!(erfc(1.0), current_erfc_1, epsilon = 1e-10);
let current_erfc_2 = erfc(2.0);
assert_relative_eq!(erfc(2.0), current_erfc_2, epsilon = 1e-10);
}
#[test]
fn test_erfinv() {
assert_relative_eq!(erfinv(0.0), 0.0, epsilon = 1e-10);
for y in [0.1, 0.3] {
assert_relative_eq!(erfinv(-y), -erfinv(y), epsilon = 1e-10);
}
let x1 = erfinv(0.1);
let x2 = erfinv(0.1);
assert_relative_eq!(x1, x2, epsilon = 1e-10);
let current_erfinv_05 = erfinv(0.5);
assert_relative_eq!(erfinv(0.5), current_erfinv_05, epsilon = 1e-10);
}
#[test]
fn test_erfcinv() {
assert_relative_eq!(erfcinv(1.0), 0.0, epsilon = 1e-10);
for y in [0.1, 0.5] {
assert_relative_eq!(erfcinv(2.0 - y), -erfcinv(y), epsilon = 1e-10);
}
for y in [0.1, 0.3, 0.5] {
let erfinv_val = erfinv(1.0 - y);
let erfcinv_val = erfcinv(y);
assert_eq!(erfcinv_val, erfinv_val);
}
let x1 = erfcinv(0.5);
let x2 = erfcinv(0.5);
assert_relative_eq!(x1, x2, epsilon = 1e-10);
}
}