use std::f64::consts::{FRAC_2_PI, PI};
use crate::bessel::{j0, j1, y0, y1, yn};
use crate::error::{SpecialError, SpecialResult};
use crate::gamma::gamma;
const LN_PI: f64 = 1.1447298858494002;
#[allow(dead_code)]
pub fn struve(v: f64, x: f64) -> SpecialResult<f64> {
if x.is_nan() || v.is_nan() {
return Err(SpecialError::DomainError("NaN input to struve".to_string()));
}
if x == 0.0 {
if v > -1.0 {
return Ok(0.0);
} else if v == -1.0 {
return Ok(FRAC_2_PI); } else {
return Ok(f64::INFINITY);
}
}
if v == 0.0 {
return struve_h0(x);
}
if v == 1.0 {
return struve_h1(x);
}
if x.abs() < 20.0 {
return struve_series(v, x);
}
struve_asymptotic(v, x)
}
#[allow(dead_code)]
fn struve_h0(x: f64) -> SpecialResult<f64> {
if x.abs() < 20.0 {
return struve_series(0.0, x);
}
let y0_val = y0(x);
Ok(y0_val + 2.0 / (PI * x))
}
#[allow(dead_code)]
fn struve_h1(x: f64) -> SpecialResult<f64> {
if x.abs() < 20.0 {
return struve_series(1.0, x);
}
let y1_val = y1(x);
Ok(y1_val + 2.0 / PI - 2.0 / (PI * x * x))
}
#[allow(dead_code)]
fn struve_series(v: f64, x: f64) -> SpecialResult<f64> {
let z = 0.5 * x;
let z_squared = z * z;
if z.abs() > 100.0 && v > 10.0 {
return struve_asymptotic(v, x);
}
if z.abs() < 1e-15 {
if v > -1.0 {
return Ok(0.0);
}
if v == -1.0 {
return Ok(FRAC_2_PI);
}
return Ok(f64::INFINITY);
}
let g1_0: f64 = gamma(1.5); let g2_0: f64 = gamma(v + 1.5); if g2_0 == 0.0 || !g2_0.is_finite() {
return Err(SpecialError::ComputationError(
"Gamma function overflow in struve_series".to_string(),
));
}
let mut term = 1.0 / (g1_0 * g2_0); let mut sum = term;
for k in 1..200 {
let k_f = k as f64;
let ratio = z_squared / ((k_f + 0.5) * (k_f + v + 0.5));
term *= ratio;
if !term.is_finite() {
break;
}
let sign = if k % 2 == 0 { 1.0 } else { -1.0 };
sum += sign * term;
if term < 1e-15 * sum.abs().max(1e-300) && k > 3 {
break;
}
}
let log_z = z.ln();
let z_pow_v_plus_1 = if log_z > 0.0 && (v + 1.0) * log_z > 700.0 {
f64::INFINITY
} else {
z.powf(v + 1.0)
};
Ok(sum * z_pow_v_plus_1)
}
#[allow(dead_code)]
fn struve_asymptotic(v: f64, x: f64) -> SpecialResult<f64> {
if x.is_nan() || v.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to struve_asymptotic".to_string(),
));
}
if x <= 0.0 {
return Err(SpecialError::DomainError(
"Positive argument expected for asymptotic approximation".to_string(),
));
}
if x > 50.0 && v.abs() < x / 4.0 {
struve_asymptotic_series(v, x)
} else if x > 20.0 {
struve_asymptotic_bessel_based(v, x)
} else {
struve_series(v, x)
}
}
#[allow(dead_code)]
fn struve_asymptotic_series(v: f64, x: f64) -> SpecialResult<f64> {
let bessel_y = if v == 0.0 {
y0(x)
} else if v == 1.0 {
y1(x)
} else if v.fract() == 0.0 && v.abs() < 20.0 {
yn(v as i32, x)
} else {
struve_bessel_y_asymptotic(v, x)?
};
let mut correction = 0.0;
let x_inv = 1.0 / x;
let x_inv_sq = x_inv * x_inv;
let mut x_power = x_inv;
for k in 0..8 {
let u_k = struve_asymptotic_coefficient(k, v);
let term = u_k * x_power;
correction += if k % 2 == 0 { term } else { -term };
if term.abs() < 1e-15 * correction.abs() || term.abs() < 1e-15 {
break;
}
x_power *= x_inv_sq; }
let result = bessel_y + (2.0 / PI) * correction;
if result.is_nan() {
Err(SpecialError::DomainError(
"Result is NaN in asymptotic approximation".to_string(),
))
} else {
Ok(result)
}
}
#[allow(dead_code)]
fn struve_asymptotic_bessel_based(v: f64, x: f64) -> SpecialResult<f64> {
let bessel_y = if v == 0.0 {
y0(x)
} else if v == 1.0 {
y1(x)
} else if v.fract() == 0.0 && v.abs() < 20.0 {
yn(v as i32, x)
} else {
struve_bessel_y_asymptotic(v, x)?
};
let correction = struve_correction_term_enhanced(v, x)?;
Ok(bessel_y + correction)
}
#[allow(dead_code)]
fn struve_bessel_y_asymptotic(v: f64, x: f64) -> SpecialResult<f64> {
if x < 10.0 {
return Err(SpecialError::DomainError(
"x too small for Y_v asymptotic approximation".to_string(),
));
}
let phase = x - v * PI / 2.0 - PI / 4.0;
let amplitude = (2.0 / (PI * x)).sqrt();
let correction = -(v * v - 0.25) / (8.0 * x);
Ok(amplitude * (phase.sin() + correction * phase.cos()))
}
#[allow(dead_code)]
fn struve_asymptotic_coefficient(k: usize, v: f64) -> f64 {
match k {
0 => {
(4.0 * v * v - 1.0) / 8.0
}
1 => {
let v2 = v * v;
(4.0 * v2 - 1.0) * (4.0 * v2 - 9.0) / 128.0
}
2 => {
let v2 = v * v;
(4.0 * v2 - 1.0) * (4.0 * v2 - 9.0) * (4.0 * v2 - 25.0) / 3072.0
}
3 => {
let v2 = v * v;
let factor1 = 4.0 * v2 - 1.0;
let factor2 = 4.0 * v2 - 9.0;
let factor3 = 4.0 * v2 - 25.0;
let factor4 = 4.0 * v2 - 49.0;
factor1 * factor2 * factor3 * factor4 / 73728.0
}
_ => {
let mut product = 1.0;
let v2 = v * v;
for n in 1..=(k + 1) {
let odd_num = (2 * n - 1) as f64;
let odd_square = odd_num * odd_num;
product *= 4.0 * v2 - odd_square;
}
let denominator = 8.0_f64.powi(k as i32 + 1) * factorial_ratio(2 * k + 1, k);
product / denominator
}
}
}
#[allow(dead_code)]
fn struve_correction_term_enhanced(v: f64, x: f64) -> SpecialResult<f64> {
if v == 0.0 {
let leading = 2.0 / (PI * x);
let correction = -1.0 / (2.0 * PI * x.powi(3)); Ok(leading + correction)
} else if v == 1.0 {
let leading = -2.0 / (PI * x) * (1.0 - 2.0 / (x * x));
let correction = 8.0 / (3.0 * PI * x.powi(5)); Ok(leading + correction)
} else {
let log_gamma_v_plus_half = log_gamma(v + 0.5)?;
let log_sqrt_pi = 0.5 * PI.ln();
let log_x_half_pow = (v - 1.0) * (0.5 * x).ln();
let log_leading = (2.0 / PI).ln() + log_x_half_pow - log_gamma_v_plus_half - log_sqrt_pi;
if log_leading > 700.0 {
return Ok(f64::INFINITY);
} else if log_leading < -700.0 {
return Ok(0.0);
}
let leading_term = log_leading.exp();
let correction_factor = 1.0 - (v * v - 0.25) / (2.0 * x * x);
Ok(leading_term * correction_factor)
}
}
#[allow(dead_code)]
fn factorial_ratio(n: usize, k: usize) -> f64 {
if k > n {
return 0.0;
}
let mut result = 1.0;
for i in (k + 1)..=n {
result *= i as f64;
}
result
}
#[allow(dead_code)]
fn it_mod_struve_1_asymptotic(x: f64) -> SpecialResult<f64> {
if x < 5.0 {
return Err(SpecialError::DomainError(
"x too small for asymptotic approximation".to_string(),
));
}
let mod_struve_0 = mod_struve(0.0, x)?;
let it_mod_struve_0 = it_mod_struve0(x)?;
let main_term = x * mod_struve_0 - it_mod_struve_0;
let correction1 = 2.0 / PI; let correction2 = -1.0 / (PI * x); let correction3 = 1.0 / (2.0 * PI * x * x);
Ok(main_term + correction1 + correction2 + correction3)
}
#[allow(dead_code)]
fn log_gamma(x: f64) -> SpecialResult<f64> {
if x <= 0.0 {
return Err(SpecialError::DomainError(
"log_gamma requires positive argument".to_string(),
));
}
if x > 170.0 {
let log_2pi = PI.ln() + 2.0_f64.ln();
let result = (x - 0.5) * x.ln() - x + 0.5 * log_2pi + 1.0 / (12.0 * x);
Ok(result)
} else {
let g = gamma(x);
Ok(g.ln())
}
}
#[allow(dead_code)]
pub fn mod_struve(v: f64, x: f64) -> SpecialResult<f64> {
if x.is_nan() || v.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to mod_struve".to_string(),
));
}
if x == 0.0 {
if v > -1.0 {
return Ok(0.0);
} else if v == -1.0 {
return Ok(FRAC_2_PI); } else {
return Ok(f64::INFINITY);
}
}
if x.abs() < 20.0 {
let z = 0.5 * x;
let z_squared = z * z;
if z.abs() < 1e-15 {
if v > -1.0 {
return Ok(0.0);
}
if v == -1.0 {
return Ok(FRAC_2_PI);
}
return Ok(f64::INFINITY);
}
let g1_0: f64 = gamma(1.5);
let g2_0: f64 = gamma(v + 1.5);
if g2_0 == 0.0 || !g2_0.is_finite() || !g1_0.is_finite() {
return Err(SpecialError::ComputationError(
"Gamma function overflow in mod_struve series".to_string(),
));
}
let mut term = 1.0 / (g1_0 * g2_0);
let mut sum = term;
for k in 1..200 {
let k_f = k as f64;
let ratio = z_squared / ((k_f + 0.5) * (k_f + v + 0.5));
term *= ratio;
if !term.is_finite() {
break;
}
sum += term;
if term < 1e-15 * sum && k > 3 {
break;
}
}
let log_z = z.ln();
let z_pow_v_plus_1 = if log_z > 0.0 && (v + 1.0) * log_z > 700.0 {
f64::INFINITY
} else {
z.powf(v + 1.0)
};
Ok(sum * z_pow_v_plus_1)
} else {
if x > 100.0 && v > 20.0 {
let z = 0.5 * x;
let mut log_sum = f64::NEG_INFINITY; let mut k = 0;
while k < 50 {
let k_f64 = k as f64;
let log_g1 = log_gamma(k_f64 + 1.5)?;
let log_g2 = log_gamma(k_f64 + v + 1.5)?;
let log_term = 2.0 * k as f64 * z.ln() - log_g1 - log_g2;
if log_sum == f64::NEG_INFINITY {
log_sum = log_term;
} else {
let max_log = log_sum.max(log_term);
log_sum = max_log
+ (log_sum - max_log).exp().ln_1p()
+ (log_term - max_log).exp().ln();
}
if (log_term - log_sum).exp() < 1e-15 && k > 10 {
break;
}
k += 1;
}
let log_result = log_sum + (v + 1.0) * z.ln();
if log_result > 700.0 {
return Ok(f64::INFINITY);
}
return Ok(log_result.exp());
}
if v == 0.0
|| v == 1.0
|| (v.fract() == 0.0 && v.abs() < 20.0)
|| (v > 0.0 && v < 100.0 && x > 10.0)
{
let i_v = bessel_i_approximation(v, x)?;
let correction_term = if v == 0.0 {
2.0 / (PI * x)
} else if v == 1.0 {
let base_term = 2.0 / (PI * x);
if x > 10.0 {
base_term * (1.0 + 2.0 / (x * x))
} else {
let x_sq = x * x;
base_term * ((x_sq + 2.0) / x_sq)
}
} else {
let log_gamma_v_plus_half = log_gamma(v + 0.5)?;
let log_sqrt_pi = 0.5 * PI.ln();
let log_x_half_pow = (v - 1.0) * (0.5 * x).ln();
let log_correction =
(2.0 / PI).ln() + log_x_half_pow - log_gamma_v_plus_half - log_sqrt_pi;
if log_correction > 700.0 {
f64::INFINITY
} else if log_correction < -700.0 {
0.0 } else {
log_correction.exp()
}
};
if i_v.is_infinite() && correction_term.is_infinite() {
Err(SpecialError::DomainError(
"Indeterminate result (∞ - ∞) in mod_struve".to_string(),
))
} else if i_v.is_infinite() {
Ok(i_v) } else {
let result = i_v - correction_term;
if result.is_nan() {
Err(SpecialError::DomainError(
"Result is NaN in mod_struve calculation".to_string(),
))
} else {
Ok(result)
}
}
} else {
mod_struve(v, x)
}
}
}
#[allow(dead_code)]
fn bessel_i_approximation(v: f64, x: f64) -> SpecialResult<f64> {
if v.is_nan() || x.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to bessel_i_approximation".to_string(),
));
}
if x <= 0.0 {
return Err(SpecialError::DomainError(
"Argument must be positive in bessel_i_approximation".to_string(),
));
}
if x < 5.0 {
let mut sum = 1.0; let x_half = x / 2.0;
let x_half_sq = x_half * x_half;
if v == 0.0 {
let mut term: f64 = 1.0;
for k in 1..40 {
term *= x_half_sq / (k as f64 * k as f64);
sum += term;
if term < 1e-15 * sum && k > 10 {
break;
}
}
return Ok(sum);
} else if v == 1.0 {
sum = 0.0;
let mut term = x_half;
sum += term;
for k in 1..40 {
term *= x_half_sq / (k as f64 * (k + 1) as f64);
sum += term;
if term < 1e-15 * sum && k > 10 {
break;
}
}
return Ok(sum);
}
}
let _one_over_x = 1.0 / x;
let v_squared = v * v;
let mu = 4.0 * v_squared - 1.0;
let mut series = 1.0;
if x > 10.0 {
series += mu / (8.0 * x);
if x > 20.0 {
let term2 = mu * (mu - 8.0) / (2.0 * 64.0 * x * x);
series += term2;
if x > 40.0 {
let term3 = mu * (mu - 8.0) * (mu - 24.0) / (6.0 * 512.0 * x * x * x);
series += term3;
}
}
}
let result = if x > 700.0 {
let log_result = x - 0.5 * (2.0 * PI * x).ln() + series.ln();
if log_result > 700.0 {
f64::INFINITY
} else {
log_result.exp()
}
} else {
(x.exp() / (2.0 * PI * x).sqrt()) * series
};
Ok(result)
}
#[allow(dead_code)]
fn fact_squared(n: usize) -> f64 {
if n == 0 {
return 1.0;
}
if n <= 10 {
let mut result = 1.0;
for i in 1..=n {
let i_f64 = i as f64;
result *= i_f64 * i_f64;
}
return result;
}
let mut log_result = 0.0;
for i in 1..=n {
log_result += 2.0 * (i as f64).ln();
}
if log_result > 700.0 {
return f64::INFINITY;
}
log_result.exp()
}
#[allow(dead_code)]
pub fn it_struve0(x: f64) -> SpecialResult<f64> {
if x.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to it_struve0".to_string(),
));
}
if x == 0.0 {
return Ok(0.0);
}
if x.abs() < 1e-15 {
return Ok(0.0);
}
if x.abs() < 20.0 {
let mut sum: f64 = 0.0;
let x2 = x * x;
let mut prev_sum = 0.0;
let mut num_equal_terms = 0;
for k in 0..50 {
let factor = if k % 2 == 0 { 1.0 } else { -1.0 };
let k_f64 = k as f64;
let denom1 = 2.0 * k_f64 + 1.0;
let denom2 = 2.0 * k_f64 + 3.0;
let term = if k > 15 {
let log_x2k = 2.0 * k_f64 * x2.ln();
let log_fact_squared = fact_squared_log(k);
let log_term = log_x2k - (denom1 * denom2).ln() - log_fact_squared;
factor * log_term.exp()
} else {
let fac_sq = fact_squared(k);
factor * x2.powi(k as i32) / (denom1 * denom2 * fac_sq)
};
if !term.is_finite() {
break;
}
sum += term;
let abs_term = term.abs();
let abs_sum = sum.abs().max(1e-300);
if (abs_term < 1e-15) || (abs_term < 1e-15 * abs_sum) {
break;
}
if (sum - prev_sum).abs() < 1e-15 * abs_sum {
num_equal_terms += 1;
if num_equal_terms > 3 {
break;
}
} else {
num_equal_terms = 0;
}
prev_sum = sum;
}
Ok(sum * 2.0 * x * x / PI)
} else {
let struve_0 = struve(0.0, x)?;
let bessel_j1 = j1(x);
if x > 100.0 {
let term1 = struve_0 * x;
let term2 = bessel_j1 * x;
let term3 = 1.0 - 2.0 / PI;
let result = term3 + (term1 - term2);
if result.is_nan() {
Err(SpecialError::DomainError(
"NaN result in it_struve0 calculation".to_string(),
))
} else {
Ok(result)
}
} else {
Ok(struve_0 * x - bessel_j1 * x + 1.0 - 2.0 / PI)
}
}
}
#[allow(dead_code)]
fn fact_squared_log(n: usize) -> f64 {
if n == 0 {
return 0.0; }
let mut result = 0.0;
for i in 1..=n {
result += 2.0 * (i as f64).ln();
}
result
}
#[allow(dead_code)]
pub fn it2_struve0(x: f64) -> SpecialResult<f64> {
if x.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to it2_struve0".to_string(),
));
}
if x == 0.0 {
return Ok(0.0);
}
if x.abs() < 20.0 {
let mut sum: f64 = 0.0;
let x2 = x * x;
for k in 0..30 {
let factor = if k % 2 == 0 { 1.0 } else { -1.0 };
let term = factor * x2.powi(k as i32)
/ ((2.0 * k as f64 + 1.0)
* (2.0 * k as f64 + 3.0)
* (2.0 * k as f64 + 5.0)
* fact_squared(k));
sum += term;
if term.abs() < 1e-15 * sum.abs() {
break;
}
}
Ok(sum * 2.0 * x * x * x / PI)
} else {
it_mod_struve_1_asymptotic(x)
}
}
#[allow(dead_code)]
pub fn it_mod_struve0(x: f64) -> SpecialResult<f64> {
if x.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to it_mod_struve0".to_string(),
));
}
if x == 0.0 {
return Ok(0.0);
}
if x.abs() < 20.0 {
let mut sum: f64 = 0.0;
let x2 = x * x;
for k in 0..30 {
let term = x2.powi(k as i32)
/ ((2.0 * k as f64 + 1.0) * (2.0 * k as f64 + 3.0) * fact_squared(k));
sum += term;
if term.abs() < 1e-15 * sum.abs() {
break;
}
}
Ok(sum * 2.0 * x * x / PI)
} else {
let mod_struve_0 = mod_struve(0.0, x)?;
let bessel_i1 = bessel_i_approximation(1.0, x)?;
Ok(mod_struve_0 * x - bessel_i1 * x + 2.0 / PI * (x.exp() - 1.0))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_struve_h0_at_zero() {
let result = struve(0.0, 0.0).expect("struve(0,0) failed");
assert!(
(result - 0.0).abs() < 1e-14,
"H_0(0) should be 0, got {result}"
);
}
#[test]
fn test_struve_h0_small_x() {
let result = struve(0.0, 1.0).expect("struve(0,1) failed");
assert!(
(result - 0.568_656_627_04).abs() < 1e-6,
"H_0(1) ~ 0.568657, got {result}"
);
}
#[test]
fn test_struve_h0_moderate_x() {
let result = struve(0.0, 5.0).expect("struve(0,5) failed");
assert!(
(result - (-0.185_216_815_776_684_9)).abs() < 1e-6,
"H_0(5) ~ -0.18522, got {result}"
);
}
#[test]
fn test_struve_h1_at_zero() {
let result = struve(1.0, 0.0).expect("struve(1,0) failed");
assert!(
(result - 0.0).abs() < 1e-14,
"H_1(0) should be 0, got {result}"
);
}
#[test]
fn test_struve_h1_small_x() {
let result = struve(1.0, 1.0).expect("struve(1,1) failed");
assert!(
(result - 0.198_457_336_201_944_42).abs() < 1e-6,
"H_1(1) ~ 0.19846, got {result}"
);
}
#[test]
fn test_struve_fractional_order() {
let result = struve(0.5, 1.0).expect("struve(0.5,1) failed");
assert!(
result.is_finite(),
"H_0.5(1) should be finite, got {result}"
);
assert!(result > 0.0, "H_0.5(1) should be positive");
}
#[test]
fn test_struve_nan_input() {
let result = struve(0.0, f64::NAN);
assert!(result.is_err(), "struve with NaN input should return error");
}
#[test]
fn test_struve_negative_order_at_zero() {
let result = struve(-1.0, 0.0).expect("struve(-1,0) failed");
assert!(
(result - FRAC_2_PI).abs() < 1e-14,
"H_{{-1}}(0) should be 2/pi, got {result}"
);
}
#[test]
fn test_mod_struve_l0_at_zero() {
let result = mod_struve(0.0, 0.0).expect("mod_struve(0,0) failed");
assert!(
(result - 0.0).abs() < 1e-14,
"L_0(0) should be 0, got {result}"
);
}
#[test]
fn test_mod_struve_l0_small_x() {
let result = mod_struve(0.0, 1.0).expect("mod_struve(0,1) failed");
assert!(
(result - 0.710_243_185_937_891).abs() < 1e-4,
"L_0(1) ~ 0.71024, got {result}"
);
}
#[test]
fn test_mod_struve_l1_small_x() {
let result = mod_struve(1.0, 1.0).expect("mod_struve(1,1) failed");
assert!(
(result - 0.226_764_381_055_808_65).abs() < 1e-4,
"L_1(1) ~ 0.22676, got {result}"
);
}
#[test]
fn test_mod_struve_nan_input() {
let result = mod_struve(0.0, f64::NAN);
assert!(
result.is_err(),
"mod_struve with NaN input should return error"
);
}
#[test]
fn test_mod_struve_l0_moderate_x() {
let result = mod_struve(0.0, 5.0).expect("mod_struve(0,5) failed");
assert!(result > 10.0, "L_0(5) should be > 10, got {result}");
assert!(result.is_finite(), "L_0(5) should be finite");
}
#[test]
fn test_it_struve0_at_zero() {
let result = it_struve0(0.0).expect("it_struve0(0) failed");
assert!(
(result - 0.0).abs() < 1e-14,
"integral of H_0 at 0 should be 0, got {result}"
);
}
#[test]
fn test_it_struve0_small_x() {
let result = it_struve0(1.0).expect("it_struve0(1) failed");
assert!(
result > 0.0,
"integral of H_0 from 0 to 1 should be positive"
);
assert!(result.is_finite(), "integral of H_0 at 1 should be finite");
}
#[test]
fn test_it_struve0_moderate_x() {
let result = it_struve0(5.0).expect("it_struve0(5) failed");
assert!(result.is_finite(), "integral of H_0 at 5 should be finite");
}
#[test]
fn test_it_struve0_nan_input() {
let result = it_struve0(f64::NAN);
assert!(
result.is_err(),
"it_struve0 with NaN input should return error"
);
}
#[test]
fn test_it_struve0_negative_x() {
let pos = it_struve0(2.0).expect("it_struve0(2) failed");
let neg = it_struve0(-2.0).expect("it_struve0(-2) failed");
assert!(pos.is_finite());
assert!(neg.is_finite());
}
#[test]
fn test_it2_struve0_at_zero() {
let result = it2_struve0(0.0).expect("it2_struve0(0) failed");
assert!(
(result - 0.0).abs() < 1e-14,
"second integral of H_0 at 0 should be 0, got {result}"
);
}
#[test]
fn test_it2_struve0_small_x() {
let result = it2_struve0(1.0).expect("it2_struve0(1) failed");
assert!(
result.is_finite(),
"second integral of H_0 at 1 should be finite"
);
}
#[test]
fn test_it2_struve0_moderate_x() {
let result = it2_struve0(5.0).expect("it2_struve0(5) failed");
assert!(
result.is_finite(),
"second integral of H_0 at 5 should be finite"
);
}
#[test]
fn test_it2_struve0_nan_input() {
let result = it2_struve0(f64::NAN);
assert!(
result.is_err(),
"it2_struve0 with NaN input should return error"
);
}
#[test]
fn test_it2_struve0_consistency() {
let r1 = it2_struve0(2.0).expect("it2_struve0(2) failed");
let r2 = it2_struve0(2.1).expect("it2_struve0(2.1) failed");
assert!(
(r2 - r1).abs() < 1.0,
"second integral should be smooth: {r1} vs {r2}"
);
}
#[test]
fn test_it_mod_struve0_at_zero() {
let result = it_mod_struve0(0.0).expect("it_mod_struve0(0) failed");
assert!(
(result - 0.0).abs() < 1e-14,
"integral of L_0 at 0 should be 0, got {result}"
);
}
#[test]
fn test_it_mod_struve0_small_x() {
let result = it_mod_struve0(1.0).expect("it_mod_struve0(1) failed");
assert!(
result > 0.0,
"integral of L_0 from 0 to 1 should be positive"
);
assert!(result.is_finite(), "integral of L_0 at 1 should be finite");
}
#[test]
fn test_it_mod_struve0_moderate_x() {
let result = it_mod_struve0(5.0).expect("it_mod_struve0(5) failed");
assert!(
result > 0.0,
"integral of L_0 from 0 to 5 should be positive"
);
assert!(result.is_finite(), "integral of L_0 at 5 should be finite");
}
#[test]
fn test_it_mod_struve0_nan_input() {
let result = it_mod_struve0(f64::NAN);
assert!(
result.is_err(),
"it_mod_struve0 with NaN input should return error"
);
}
#[test]
fn test_it_mod_struve0_monotonic() {
let r1 = it_mod_struve0(1.0).expect("it_mod_struve0(1) failed");
let r2 = it_mod_struve0(2.0).expect("it_mod_struve0(2) failed");
let r3 = it_mod_struve0(3.0).expect("it_mod_struve0(3) failed");
assert!(r2 > r1, "integral of L_0 should be increasing: {r1} < {r2}");
assert!(r3 > r2, "integral of L_0 should be increasing: {r2} < {r3}");
}
}