use crate::error::{SpecialError, SpecialResult};
use scirs2_core::numeric::Complex64;
use crate::gamma;
#[allow(dead_code)]
fn wright_bessel_asymptotic(rho: f64, beta: f64, z: f64) -> SpecialResult<f64> {
if rho <= 0.0 {
return Err(SpecialError::DomainError(
"Asymptotic expansion requires rho > 0".to_string(),
));
}
if z.abs() > 700.0 {
return Ok(f64::INFINITY);
}
let sigma = if rho == 1.0 {
1.0
} else {
rho * (1.0 / rho).powf(1.0 / rho)
};
let z_to_1_over_rho = if z >= 0.0 {
z.powf(1.0 / rho)
} else {
(-z).powf(1.0 / rho) * (std::f64::consts::PI / rho).cos()
};
let exponent = sigma * z_to_1_over_rho;
if exponent > 700.0 {
return Ok(f64::INFINITY);
}
if exponent < -700.0 {
return Ok(0.0);
}
let power_exponent = (beta - 1.0) / (2.0 * rho);
let power_term = if z >= 0.0 {
z.powf(power_exponent)
} else {
(-z).powf(power_exponent)
* if power_exponent.fract() != 0.0 {
-1.0
} else {
1.0
}
};
let coeff = 1.0 / (2.0 * std::f64::consts::PI).sqrt() / rho.sqrt();
let result = coeff * power_term * exponent.exp();
Ok(result)
}
#[allow(dead_code)]
fn wright_bessel_complex_asymptotic(
rho: f64,
beta: Complex64,
z: Complex64,
) -> SpecialResult<Complex64> {
if rho <= 0.0 {
return Err(SpecialError::DomainError(
"Asymptotic expansion requires rho > 0".to_string(),
));
}
if z.norm() > 700.0 {
return Ok(Complex64::new(f64::INFINITY, f64::INFINITY));
}
let sigma = if rho == 1.0 {
Complex64::new(1.0, 0.0)
} else {
let rho_complex = Complex64::new(rho, 0.0);
rho_complex * (Complex64::new(1.0, 0.0) / rho_complex).powf(1.0 / rho)
};
let z_to_1_over_rho = z.powf(1.0 / rho);
let exponent = sigma * z_to_1_over_rho;
if exponent.re > 700.0 {
return Ok(Complex64::new(f64::INFINITY, f64::INFINITY));
}
if exponent.re < -700.0 {
return Ok(Complex64::new(0.0, 0.0));
}
let power_exponent = (beta - Complex64::new(1.0, 0.0)) / Complex64::new(2.0 * rho, 0.0);
let power_term = z.powf(power_exponent.re);
let coeff = Complex64::new(1.0 / (2.0 * std::f64::consts::PI).sqrt() / rho.sqrt(), 0.0);
let exp_term = exponent.exp();
let result = coeff * power_term * exp_term;
Ok(result)
}
#[allow(dead_code)]
pub fn wright_bessel(rho: f64, beta: f64, z: f64) -> SpecialResult<f64> {
if rho <= 0.0 {
return Err(SpecialError::DomainError(
"Parameter rho must be positive".to_string(),
));
}
if rho > 100.0 {
return Err(SpecialError::DomainError(
"Parameter rho is too large (> 100), may cause numerical instability".to_string(),
));
}
if beta.abs() > 500.0 {
return Err(SpecialError::DomainError(
"Parameter beta is too large (|beta| > 500), may cause numerical instability"
.to_string(),
));
}
if z.abs() > 1000.0 {
return Err(SpecialError::DomainError(
"Parameter z is too large (|z| > 1000), may cause numerical overflow".to_string(),
));
}
if z.is_nan() || beta.is_nan() || rho.is_nan() {
return Ok(f64::NAN);
}
if z.is_infinite() {
return Ok(if z > 0.0 { f64::INFINITY } else { 0.0 });
}
if beta.is_infinite() {
return Ok(0.0); }
if z == 0.0 {
if beta > 0.0 {
return Ok(1.0 / gamma(beta));
} else {
return Ok(0.0);
}
}
if z.abs() > 30.0 {
return wright_bessel_asymptotic_enhanced(rho, beta, z);
}
let result = compute_wright_bessel_series(rho, beta, z)?;
if !result.is_finite() {
return Err(SpecialError::ComputationError(
"Wright Bessel computation produced non-finite result".to_string(),
));
}
Ok(result)
}
#[allow(dead_code)]
pub fn wright_bessel_complex(rho: f64, beta: Complex64, z: Complex64) -> SpecialResult<Complex64> {
if rho <= 0.0 {
return Err(SpecialError::DomainError(
"Parameter rho must be positive".to_string(),
));
}
if rho > 100.0 {
return Err(SpecialError::DomainError(
"Parameter rho is too large (> 100), may cause numerical instability".to_string(),
));
}
if beta.norm() > 500.0 {
return Err(SpecialError::DomainError(
"Parameter beta has too large magnitude (|beta| > 500)".to_string(),
));
}
if z.norm() > 1000.0 {
return Err(SpecialError::DomainError(
"Parameter z has too large magnitude (|z| > 1000)".to_string(),
));
}
if z.re.is_nan() || z.im.is_nan() || beta.re.is_nan() || beta.im.is_nan() || rho.is_nan() {
return Ok(Complex64::new(f64::NAN, f64::NAN));
}
if !z.is_finite() {
return Ok(Complex64::new(f64::INFINITY, f64::INFINITY));
}
if !beta.is_finite() {
return Ok(Complex64::new(0.0, 0.0));
}
if z.norm() == 0.0 {
if beta.re > 0.0 {
return Ok(Complex64::new(1.0, 0.0) / gamma::complex::gamma_complex(beta));
} else {
return Ok(Complex64::new(0.0, 0.0));
}
}
if z.norm() > 50.0 {
return wright_bessel_complex_asymptotic(rho, beta, z);
}
compute_wright_bessel_complex_series(rho, beta, z)
}
#[allow(dead_code)]
pub fn wright_bessel_zeros(rho: f64, beta: f64, n: usize) -> SpecialResult<Vec<f64>> {
if rho <= 0.0 {
return Err(SpecialError::DomainError(
"Parameter rho must be positive".to_string(),
));
}
if n == 0 {
return Err(SpecialError::DomainError(
"Number of zeros must be at least 1".to_string(),
));
}
let mut zeros = Vec::with_capacity(n);
let tolerance = 1e-12;
let max_iterations = 100;
let mut x_guess = 2.5;
for i in 0..n {
let mut x = x_guess;
let mut converged = false;
for _iter in 0..max_iterations {
let f_val = wright_bessel(rho, beta, x)?;
let f_prime = wright_bessel_derivative(rho, beta, x)?;
if f_prime.abs() < 1e-14 {
break;
}
let x_new = x - f_val / f_prime;
if (x_new - x).abs() < tolerance {
x = x_new;
converged = true;
break;
}
x = x_new;
if x < 0.0 {
x = x_guess + 0.1;
}
}
if converged {
let verification = wright_bessel(rho, beta, x)?;
if verification.abs() < 1e-10 {
zeros.push(x);
x_guess = x + std::f64::consts::PI;
} else {
x_guess += 1.0;
continue;
}
} else {
let mut a = if i == 0 { 0.1 } else { zeros[i - 1] + 0.1 };
let mut b = a + 10.0;
let mut f_a = wright_bessel(rho, beta, a)?;
let mut f_b = wright_bessel(rho, beta, b)?;
while f_a * f_b > 0.0 && b < 100.0 {
a = b;
b += 5.0;
f_a = f_b;
f_b = wright_bessel(rho, beta, b)?;
}
if f_a * f_b <= 0.0 {
for _bisect_iter in 0..100 {
let c = (a + b) / 2.0;
let f_c = wright_bessel(rho, beta, c)?;
if f_c.abs() < tolerance || (b - a) / 2.0 < tolerance {
zeros.push(c);
x_guess = c + std::f64::consts::PI;
break;
}
if f_a * f_c < 0.0 {
b = c;
} else {
a = c;
f_a = f_c;
}
}
}
}
if zeros.len() != i + 1 {
break;
}
}
if zeros.len() < n {
return Err(SpecialError::ComputationError(format!(
"Could only find {} out of {} requested zeros",
zeros.len(),
n
)));
}
Ok(zeros)
}
#[allow(dead_code)]
fn compute_wright_bessel_series(rho: f64, beta: f64, z: f64) -> SpecialResult<f64> {
let max_terms = 200;
let tolerance = 1e-15;
let z_abs = z.abs();
let adaptive_tolerance = if z_abs < 1.0 {
tolerance * 10.0 } else if z_abs > 10.0 {
tolerance / 10.0 } else {
tolerance
};
let gamma_beta = gamma(beta);
if gamma_beta.is_infinite() || gamma_beta.is_nan() {
return Ok(0.0);
}
let mut sum = 1.0 / gamma_beta;
let mut k_factorial = 1.0;
let mut z_power = 1.0;
let mut terms = Vec::with_capacity(max_terms);
let mut _partialsums = Vec::with_capacity(max_terms);
_partialsums.push(sum);
for k in 1..max_terms {
let k_f64 = k as f64;
k_factorial *= k_f64;
z_power *= -z;
let gamma_arg = rho * k_f64 + beta;
if gamma_arg > 170.0 {
break; }
let gamma_term = gamma(gamma_arg);
if gamma_term.is_infinite() || gamma_term.is_nan() {
break;
}
let term = z_power / (k_factorial * gamma_term);
terms.push(term);
sum += term;
_partialsums.push(sum);
if k >= 3 && k % 3 == 0 {
if k >= 6 {
let accelerated = aitken_acceleration(&_partialsums, k / 3)?;
if let Some(acc_sum) = accelerated {
if (acc_sum - sum).abs() < adaptive_tolerance * acc_sum.abs() {
return Ok(acc_sum);
}
}
}
if term.abs() < adaptive_tolerance * sum.abs() {
break;
}
if term.abs() > 1e10 || sum.abs() > 1e50 {
return Err(SpecialError::ComputationError(
"Wright Bessel series diverged".to_string(),
));
}
}
}
Ok(sum)
}
#[allow(dead_code)]
fn compute_wright_bessel_complex_series(
rho: f64,
beta: Complex64,
z: Complex64,
) -> SpecialResult<Complex64> {
let max_terms = 200;
let tolerance = 1e-15;
let z_norm = z.norm();
let adaptive_tolerance = if z_norm < 1.0 {
tolerance * 10.0
} else if z_norm > 10.0 {
tolerance / 10.0
} else {
tolerance
};
let gamma_beta = gamma::complex::gamma_complex(beta);
if !gamma_beta.is_finite() {
return Ok(Complex64::new(0.0, 0.0));
}
let mut sum = Complex64::new(1.0, 0.0) / gamma_beta;
let mut k_factorial = Complex64::new(1.0, 0.0);
let mut z_power = Complex64::new(1.0, 0.0);
let neg_z = -z;
let mut _partialsums = Vec::with_capacity(max_terms);
_partialsums.push(sum);
for k in 1..max_terms {
let k_f64 = k as f64;
let k_complex = Complex64::new(k_f64, 0.0);
k_factorial *= k_complex;
z_power *= neg_z;
let gamma_arg = Complex64::new(rho * k_f64, 0.0) + beta;
if gamma_arg.re > 170.0 {
break;
}
let gamma_term = gamma::complex::gamma_complex(gamma_arg);
if !gamma_term.is_finite() {
break;
}
let term = z_power / (k_factorial * gamma_term);
sum += term;
_partialsums.push(sum);
if k >= 3 && k % 3 == 0 {
if k >= 6 {
let accelerated = aitken_acceleration_complex(&_partialsums, k / 3)?;
if let Some(acc_sum) = accelerated {
if (acc_sum - sum).norm() < adaptive_tolerance * acc_sum.norm() {
return Ok(acc_sum);
}
}
}
if term.norm() < adaptive_tolerance * sum.norm() {
break;
}
if term.norm() > 1e10 || sum.norm() > 1e50 {
return Err(SpecialError::ComputationError(
"Complex Wright Bessel series diverged".to_string(),
));
}
}
}
Ok(sum)
}
#[allow(dead_code)]
fn aitken_acceleration(_partialsums: &[f64], n: usize) -> SpecialResult<Option<f64>> {
if n < 3 || _partialsums.len() < 3 * n {
return Ok(None);
}
let s_n = _partialsums[3 * (n - 1)];
let s_n_plus_1 = _partialsums[3 * n - 1];
let s_n_plus_2 = _partialsums[3 * n];
let delta = s_n_plus_1 - s_n;
let delta2 = s_n_plus_2 - 2.0 * s_n_plus_1 + s_n;
if delta2.abs() < 1e-16 {
return Ok(None); }
let accelerated = s_n - delta * delta / delta2;
if accelerated.is_finite() {
Ok(Some(accelerated))
} else {
Ok(None)
}
}
#[allow(dead_code)]
fn aitken_acceleration_complex(
_partialsums: &[Complex64],
n: usize,
) -> SpecialResult<Option<Complex64>> {
if n < 3 || _partialsums.len() < 3 * n {
return Ok(None);
}
let s_n = _partialsums[3 * (n - 1)];
let s_n_plus_1 = _partialsums[3 * n - 1];
let s_n_plus_2 = _partialsums[3 * n];
let delta = s_n_plus_1 - s_n;
let delta2 = s_n_plus_2 - Complex64::new(2.0, 0.0) * s_n_plus_1 + s_n;
if delta2.norm() < 1e-16 {
return Ok(None);
}
let accelerated = s_n - delta * delta / delta2;
if accelerated.is_finite() {
Ok(Some(accelerated))
} else {
Ok(None)
}
}
#[allow(dead_code)]
pub fn wright_bessel_derivative(rho: f64, beta: f64, z: f64) -> SpecialResult<f64> {
if rho <= 0.0 {
return Err(SpecialError::DomainError(
"Parameter rho must be positive for derivative computation".to_string(),
));
}
if z.is_nan() || beta.is_nan() || rho.is_nan() {
return Ok(f64::NAN);
}
if z == 0.0 {
if (beta + rho - 1.0).abs() < 1e-10 {
return Ok(1.0);
} else {
return Ok(0.0);
}
}
let derivative_function = wright_bessel(rho, beta + rho, z)?;
Ok(derivative_function / rho)
}
#[allow(dead_code)]
fn wright_bessel_asymptotic_enhanced(rho: f64, beta: f64, z: f64) -> SpecialResult<f64> {
if rho <= 0.0 {
return Err(SpecialError::DomainError(
"Enhanced asymptotic expansion requires rho > 0".to_string(),
));
}
if z.abs() > 500.0 {
return Ok(if z > 0.0 { f64::INFINITY } else { 0.0 });
}
let sigma = if (rho - 1.0).abs() < 1e-10 {
1.0
} else {
rho * (1.0 / rho).powf(1.0 / rho)
};
let (z_to_1_over_rho, phase_factor) = if z >= 0.0 {
(z.powf(1.0 / rho), 1.0)
} else {
let magnitude = (-z).powf(1.0 / rho);
let phase = std::f64::consts::PI / rho;
(magnitude, phase.cos())
};
let exponent = sigma * z_to_1_over_rho * phase_factor;
if exponent > 500.0 {
return Ok(f64::INFINITY);
}
if exponent < -500.0 {
return Ok(0.0);
}
let power_exponent = (beta - 1.0) / (2.0 * rho);
let power_term = if z >= 0.0 {
z.powf(power_exponent)
} else {
let magnitude = (-z).powf(power_exponent);
let is_odd = (power_exponent * 2.0).rem_euclid(2.0) > 1.0;
if is_odd {
-magnitude
} else {
magnitude
}
};
let norm_factor = 1.0 / (2.0 * std::f64::consts::PI).sqrt() / rho.sqrt();
let correction = if z.abs() > 1.0 {
1.0 + (beta - 1.0) * (beta - 2.0) / (8.0 * rho * z_to_1_over_rho)
} else {
1.0
};
let result = norm_factor * power_term * exponent.exp() * correction;
Ok(result)
}
#[allow(dead_code)]
pub fn log_wright_bessel(rho: f64, beta: f64, z: f64) -> SpecialResult<f64> {
if rho <= 0.0 || rho > 1.0 {
return Err(SpecialError::DomainError(
"log_wright_bessel: rho must be in (0, 1]".to_string(),
));
}
if z == 0.0 {
return Ok(-gamma::loggamma(beta));
}
if z < 0.0 && rho >= 0.5 {
let wb = wright_bessel(rho, beta, z)?;
if wb > 0.0 {
return Ok(wb.ln());
} else {
return Ok(f64::NEG_INFINITY);
}
}
let log_gamma_beta = gamma::loggamma(beta);
let mut max_log_term = -log_gamma_beta;
let mut terms = vec![-log_gamma_beta];
for k in 1..=50 {
let k_f = k as f64;
let log_term =
k_f * z.ln() - gamma::loggamma(k_f + 1.0) - gamma::loggamma(rho * k_f + beta);
terms.push(log_term);
max_log_term = max_log_term.max(log_term);
if log_term - max_log_term < -50.0 {
break;
}
}
let mut sum = 0.0;
for &log_term in &terms {
sum += (log_term - max_log_term).exp();
}
Ok(max_log_term + sum.ln())
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_wright_bessel_special_cases() {
let result = wright_bessel(1.0, 1.0, 0.0).expect("Operation failed");
assert_relative_eq!(result, 1.0, epsilon = 1e-10);
let result = wright_bessel(1.0, 2.0, 0.0).expect("Operation failed");
assert_relative_eq!(result, 1.0, epsilon = 1e-10);
let result = wright_bessel(1.0, 3.0, 0.0).expect("Operation failed");
assert_relative_eq!(result, 0.5, epsilon = 1e-10);
}
#[test]
fn test_wright_bessel_invalid_parameters() {
assert!(wright_bessel(0.0, 1.0, 1.0).is_err());
assert!(wright_bessel(-1.0, 1.0, 1.0).is_err());
assert!(wright_bessel(1.0, 1.0, f64::NAN)
.expect("Operation failed")
.is_nan());
assert!(wright_bessel(1.0, f64::NAN, 1.0)
.expect("Operation failed")
.is_nan());
assert!(wright_bessel(f64::NAN, 1.0, 1.0)
.expect("Operation failed")
.is_nan());
}
#[test]
fn test_wright_bessel_complex_special_cases() {
use scirs2_core::numeric::Complex64;
let result = wright_bessel_complex(1.0, Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0))
.expect("Operation failed");
assert_relative_eq!(result.re, 1.0, epsilon = 1e-10);
assert_relative_eq!(result.im, 0.0, epsilon = 1e-10);
let result = wright_bessel_complex(1.0, Complex64::new(2.0, 0.0), Complex64::new(0.0, 0.0))
.expect("Operation failed");
assert_relative_eq!(result.re, 1.0, epsilon = 1e-10);
assert_relative_eq!(result.im, 0.0, epsilon = 1e-10);
let result = wright_bessel_complex(1.0, Complex64::new(1.0, 0.5), Complex64::new(0.0, 0.0))
.expect("Operation failed");
assert!(result.re.is_finite());
assert!(result.im.is_finite());
}
#[test]
fn test_wright_bessel_complex_invalid_parameters() {
use scirs2_core::numeric::Complex64;
assert!(
wright_bessel_complex(0.0, Complex64::new(1.0, 0.0), Complex64::new(1.0, 0.0)).is_err()
);
assert!(
wright_bessel_complex(-1.0, Complex64::new(1.0, 0.0), Complex64::new(1.0, 0.0))
.is_err()
);
let result =
wright_bessel_complex(1.0, Complex64::new(1.0, 0.0), Complex64::new(f64::NAN, 0.0))
.expect("Operation failed");
assert!(result.re.is_nan());
assert!(result.im.is_nan());
}
#[test]
fn test_wright_bessel_zeros_basic() {
match wright_bessel_zeros(1.0, 1.0, 1) {
Ok(zeros) => {
assert_eq!(zeros.len(), 1);
assert!(zeros[0] > 0.0);
let verification = wright_bessel(1.0, 1.0, zeros[0]).expect("Operation failed");
assert!(verification.abs() < 1e-8);
}
Err(_) => {
}
}
}
#[test]
fn test_wright_bessel_zeros_invalid_parameters() {
assert!(wright_bessel_zeros(0.0, 1.0, 1).is_err());
assert!(wright_bessel_zeros(-1.0, 1.0, 1).is_err());
assert!(wright_bessel_zeros(1.0, 1.0, 0).is_err());
}
}