#![allow(dead_code)]
use std::f64;
pub mod properties {
use super::*;
pub fn test_gamma_properties(values: &[f64]) -> Vec<String> {
let mut errors = Vec::new();
for &x in values {
if x > 0.0 && x < 100.0 {
if x > 1e-12 {
let gamma_x = crate::gamma::gamma(x);
let gamma_x_plus_1 = crate::gamma::gamma(x + 1.0);
let expected = x * gamma_x;
let tolerance = if x < 1e-6 {
1e-1
} else if x > 50.0 {
1e-6
} else {
1e-10
};
if gamma_x.is_finite()
&& gamma_x_plus_1.is_finite()
&& expected.is_finite()
&& (gamma_x_plus_1 - expected).abs() > tolerance * expected.abs()
{
errors.push(format!(
"Gamma recurrence failed for x={x}: Γ(x+1)={gamma_x_plus_1}, x*Γ(x)={expected}"
));
}
}
if x < 1.0 && x > 1e-6 {
let gamma_x = crate::gamma::gamma(x);
let gamma_1minus_x = crate::gamma::gamma(1.0 - x);
let sin_pi_x = (std::f64::consts::PI * x).sin();
if sin_pi_x.abs() > 1e-10 && gamma_x.is_finite() && gamma_1minus_x.is_finite() {
let product = gamma_x * gamma_1minus_x * sin_pi_x;
let expected = std::f64::consts::PI;
if (product - expected).abs() > 1e-4 * expected {
errors.push(format!(
"Gamma reflection formula failed for x={x}: Γ(x)*Γ(1-x)*sin(πx)={product}, π={expected}"
));
}
}
}
}
}
errors
}
pub fn test_bessel_properties(values: &[f64]) -> Vec<String> {
let mut errors = Vec::new();
for &x in values {
if x > 0.0 && x < 50.0 {
if x.abs() < 1e-10 {
let j0_zero: f64 = crate::bessel::j0(0.0);
if (j0_zero - 1.0).abs() > 1e-10 {
errors.push(format!("J₀(0) should be 1, got {j0_zero}"));
}
}
let j0_prime = crate::bessel::j0_prime(x);
let j1_x = crate::bessel::j1(x);
if (j0_prime + j1_x).abs() > 1e-8 {
errors.push(format!(
"J₀'(x) = -J₁(x) failed for x={x}: J₀'({x})={j0_prime}, -J₁({x})={neg_j1}",
neg_j1 = -j1_x
));
}
}
}
errors
}
pub fn test_erf_properties(values: &[f64]) -> Vec<String> {
let mut errors = Vec::new();
for &x in values {
let erf_x = crate::erf::erf(x);
let erf_neg_x = crate::erf::erf(-x);
if (erf_x + erf_neg_x).abs() > 1e-12 {
errors.push(format!(
"erf(-x) = -erf(x) failed for x={x}: erf({x})={erf_x}, erf({neg_x})={erf_neg_x}",
neg_x = -x
));
}
let erfc_x = crate::erf::erfc(x);
let sum = erf_x + erfc_x;
if (sum - 1.0).abs() > 1e-12 {
errors.push(format!("erf(x) + erfc(x) = 1 failed for x={x}: sum={sum}"));
}
if !(-1.0..=1.0).contains(&erf_x) {
errors.push(format!("erf(x) out of bounds for x={x}: erf({x})={erf_x}"));
}
}
errors
}
pub fn test_combinatorial_properties() -> Vec<String> {
let mut errors = Vec::new();
for n in 0..=20 {
for k in 0..=n {
let binom_nk = crate::combinatorial::binomial(n, k).expect("Operation failed");
let binom_n_nminus_k =
crate::combinatorial::binomial(n, n - k).expect("Operation failed");
if (binom_nk - binom_n_nminus_k).abs() > 1e-10 {
let nminus_k = n - k;
errors.push(format!(
"Binomial symmetry failed: C({n},{k})={binom_nk}, C({n},{nminus_k})={binom_n_nminus_k}"
));
}
if n > 0 && k > 0 && k < n {
let pascal_left =
crate::combinatorial::binomial(n - 1, k - 1).expect("Operation failed");
let pascal_right =
crate::combinatorial::binomial(n - 1, k).expect("Operation failed");
let pascal_sum = pascal_left + pascal_right;
if (binom_nk - pascal_sum).abs() > 1e-10 {
let nminus_1 = n - 1;
let kminus_1 = k - 1;
errors.push(format!(
"Pascal's triangle failed: C({n},{k})={binom_nk}, C({nminus_1},{kminus_1}) + C({nminus_1},{k})={pascal_sum}"
));
}
}
}
}
errors
}
pub fn test_statistical_properties(values: &[f64]) -> Vec<String> {
let mut errors = Vec::new();
for &x in values {
let logistic_x = crate::statistical::logistic(x);
if logistic_x < 0.0 || (logistic_x >= 1.0 && x < 20.0) {
errors.push(format!(
"Logistic function out of bounds for x={x}: σ({x})={logistic_x}"
));
}
let logistic_neg_x = crate::statistical::logistic(-x);
let symmetry_check = logistic_x + logistic_neg_x;
if (symmetry_check - 1.0).abs() > 1e-12 {
let neg_x = -x;
errors.push(format!(
"Logistic symmetry failed for x={x}: σ({x}) + σ({neg_x})={symmetry_check}"
));
}
}
errors
}
}
pub mod edge_cases {
use super::*;
pub fn test_boundary_values() -> Vec<String> {
let mut errors = Vec::new();
let gamma_zero_plus: f64 = crate::gamma::gamma(1e-15);
if !gamma_zero_plus.is_infinite() && gamma_zero_plus < 1e10 {
errors.push("Gamma function should be very large near zero".to_string());
}
let erf_large_pos: f64 = crate::erf::erf(10.0);
if (erf_large_pos - 1.0).abs() > 1e-10 {
errors.push(format!("erf(10) should be ≈ 1, got {erf_large_pos}"));
}
let erf_large_neg: f64 = crate::erf::erf(-10.0);
if (erf_large_neg + 1.0).abs() > 1e-10 {
errors.push(format!("erf(-10) should be ≈ -1, got {erf_large_neg}"));
}
let j1_zero: f64 = crate::bessel::j1(0.0);
if j1_zero.abs() > 1e-15 {
errors.push(format!("J₁(0) should be 0, got {j1_zero}"));
}
errors
}
pub fn test_near_singularities() -> Vec<String> {
let mut errors = Vec::new();
for n in 1..5 {
let near_neg_int = -(n as f64) + 1e-15;
let gamma_val = crate::gamma::gamma(near_neg_int);
if !gamma_val.is_nan() && !gamma_val.is_infinite() && gamma_val.abs() < 1e10 {
errors.push(format!(
"Gamma function should blow up near -{n}, got {gamma_val}"
));
}
}
let small_values = [1e-15, 1e-10, 1e-5];
for &x in &small_values {
let erf_small: f64 = crate::erf::erf(x);
if !erf_small.is_finite() {
errors.push(format!("erf({x}) should be finite, got {erf_small}"));
}
}
errors
}
pub fn test_overflow_underflow() -> Vec<String> {
let mut errors = Vec::new();
let large_values = [100.0, 500.0, 1000.0];
for &x in &large_values {
let gamma_large: f64 = crate::gamma::gamma(x);
if gamma_large.is_nan() {
errors.push(format!("Gamma function returned NaN for large value {x}"));
}
let erf_large: f64 = crate::erf::erf(x);
if (erf_large - 1.0).abs() > 1e-12 {
errors.push(format!("erf({x}) should be ≈ 1, got {erf_large}"));
}
}
errors
}
}
pub mod regression {
pub fn test_known_issues() -> Vec<String> {
let mut errors = Vec::new();
let test_cases = [
(0.5, (std::f64::consts::PI).sqrt()),
(1.0, 1.0),
(2.0, 1.0),
(3.0, 2.0),
(4.0, 6.0),
(5.0, 24.0),
];
for &(x, expected) in &test_cases {
let gamma_val = crate::gamma::gamma(x);
if (gamma_val - expected).abs() > 1e-12 * expected.abs() {
errors.push(format!(
"Gamma regression test failed: Γ({x}) = {gamma_val}, expected {expected}"
));
}
}
let j0_at_zero: f64 = crate::bessel::j0(0.0);
if (j0_at_zero - 1.0).abs() > 1e-10 {
errors.push(format!("J₀(0) should be 1, got {j0_at_zero}"));
}
errors
}
}
#[allow(dead_code)]
pub fn run_comprehensive_tests() -> Vec<String> {
let mut all_errors = Vec::new();
let mut test_values = Vec::new();
test_values.extend_from_slice(&[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 5.0, 10.0]);
test_values.extend_from_slice(&[1e-15, 1e-10, 1e-5, 1e-3, 0.01, 0.1]);
test_values.extend_from_slice(&[-0.1, -0.5, -1.5, -2.5, -5.0]);
test_values.extend_from_slice(&[20.0, 50.0, 100.0]);
all_errors.extend(properties::test_gamma_properties(&test_values));
all_errors.extend(properties::test_bessel_properties(&test_values));
all_errors.extend(properties::test_erf_properties(&test_values));
all_errors.extend(properties::test_combinatorial_properties());
all_errors.extend(properties::test_statistical_properties(&test_values));
all_errors.extend(edge_cases::test_boundary_values());
all_errors.extend(edge_cases::test_near_singularities());
all_errors.extend(edge_cases::test_overflow_underflow());
all_errors.extend(regression::test_known_issues());
all_errors
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gamma_properties() {
let test_values = vec![0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 5.0];
let errors = properties::test_gamma_properties(&test_values);
if !errors.is_empty() {
panic!("Gamma property tests failed:\n{}", errors.join("\n"));
}
}
#[test]
fn test_bessel_properties() {
let test_values = vec![0.0, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0];
let errors = properties::test_bessel_properties(&test_values);
if !errors.is_empty() {
panic!("Bessel property tests failed:\n{}", errors.join("\n"));
}
}
#[test]
fn test_erf_properties() {
let test_values = vec![-5.0, -2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 5.0];
let errors = properties::test_erf_properties(&test_values);
if !errors.is_empty() {
panic!(
"Error function property tests failed:\n{}",
errors.join("\n")
);
}
}
#[test]
fn test_combinatorial_properties() {
let errors = properties::test_combinatorial_properties();
if !errors.is_empty() {
panic!(
"Combinatorial property tests failed:\n{}",
errors.join("\n")
);
}
}
#[test]
fn test_statistical_properties() {
let test_values = vec![-10.0, -2.0, -1.0, 0.0, 1.0, 2.0, 10.0];
let errors = properties::test_statistical_properties(&test_values);
if !errors.is_empty() {
panic!("Statistical property tests failed:\n{}", errors.join("\n"));
}
}
#[test]
fn test_edge_cases() {
let mut all_errors = Vec::new();
all_errors.extend(edge_cases::test_boundary_values());
all_errors.extend(edge_cases::test_near_singularities());
all_errors.extend(edge_cases::test_overflow_underflow());
if !all_errors.is_empty() {
panic!("Edge case tests failed:\n{}", all_errors.join("\n"));
}
}
#[test]
fn test_regression_cases() {
let errors = regression::test_known_issues();
if !errors.is_empty() {
panic!("Regression tests failed:\n{}", errors.join("\n"));
}
}
#[test]
fn test_comprehensive_suite() {
let errors = run_comprehensive_tests();
if errors.len() > 15 {
panic!(
"Too many comprehensive test failures ({}):\n{}",
errors.len(),
errors.join("\n")
);
}
if !errors.is_empty() {
println!(
"Warning: Some comprehensive tests had minor issues:\n{}",
errors.join("\n")
);
}
}
}