#![allow(dead_code)]
use scirs2_core::numeric::Complex64;
use std::f64::consts::PI;
#[allow(dead_code)]
pub fn j0_complex(z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 && z.re >= 0.0 {
let real_result = crate::bessel::j0(z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() == 0.0 {
return Complex64::new(1.0, 0.0);
}
if z.norm() < 8.0 {
return j0_series_complex(z);
}
j0_asymptotic_complex(z)
}
#[allow(dead_code)]
pub fn j1_complex(z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 && z.re >= 0.0 {
let real_result = crate::bessel::j1(z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() == 0.0 {
return Complex64::new(0.0, 0.0);
}
if z.norm() < 8.0 {
return j1_series_complex(z);
}
j1_asymptotic_complex(z)
}
#[allow(dead_code)]
pub fn jn_complex(n: i32, z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 && z.re >= 0.0 {
let real_result = crate::bessel::jn(n, z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() == 0.0 {
return if n == 0 {
Complex64::new(1.0, 0.0)
} else {
Complex64::new(0.0, 0.0)
};
}
match n {
0 => j0_complex(z),
1 => j1_complex(z),
-1 => -j1_complex(z),
_ => {
if n.abs() <= 50 && z.norm() < 8.0 {
jn_series_complex(n, z)
} else {
jn_asymptotic_complex(n, z)
}
}
}
}
#[allow(dead_code)]
pub fn jv_complex(v: f64, z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 && z.re >= 0.0 {
let real_result = crate::bessel::jv(v, z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() == 0.0 {
return if v == 0.0 {
Complex64::new(1.0, 0.0)
} else if v > 0.0 {
Complex64::new(0.0, 0.0)
} else {
Complex64::new(f64::INFINITY, 0.0)
};
}
if v.fract() == 0.0 && v.abs() < i32::MAX as f64 {
return jn_complex(v as i32, z);
}
if (v - 0.5).fract() == 0.0 {
return jv_half_integer_complex(v, z);
}
if z.norm() < 8.0 {
return jv_series_complex(v, z);
}
jv_asymptotic_complex(v, z)
}
#[allow(dead_code)]
pub fn i0_complex(z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 && z.re >= 0.0 {
let real_result = crate::bessel::i0(z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() == 0.0 {
return Complex64::new(1.0, 0.0);
}
if z.norm() < 8.0 {
return i0_series_complex(z);
}
i0_asymptotic_complex(z)
}
#[allow(dead_code)]
pub fn k0_complex(z: Complex64) -> Complex64 {
if z.im.abs() < 1e-15 && z.re > 0.0 {
let real_result = crate::bessel::k0(z.re);
return Complex64::new(real_result, 0.0);
}
if z.norm() == 0.0 {
return Complex64::new(f64::INFINITY, 0.0);
}
if z.norm() < 8.0 {
return k0_series_complex(z);
}
k0_asymptotic_complex(z)
}
#[allow(dead_code)]
fn j0_series_complex(z: Complex64) -> Complex64 {
let mut result = Complex64::new(1.0, 0.0);
let z2 = z * z;
let mut term = Complex64::new(1.0, 0.0);
for k in 1..=50 {
term *= -z2 / Complex64::new((4 * k * k) as f64, 0.0);
result += term;
if term.norm() < 1e-15 * result.norm() {
break;
}
}
result
}
#[allow(dead_code)]
fn j1_series_complex(z: Complex64) -> Complex64 {
let mut result = z / Complex64::new(2.0, 0.0);
let z2 = z * z;
let mut term = result;
for k in 1..=50 {
term *= -z2 / Complex64::new((4 * k * (k + 1)) as f64, 0.0);
result += term;
if term.norm() < 1e-15 * result.norm() {
break;
}
}
result
}
#[allow(dead_code)]
fn jn_series_complex(n: i32, z: Complex64) -> Complex64 {
if n == 0 {
return j0_series_complex(z);
}
if n == 1 {
return j1_series_complex(z);
}
if n == -1 {
return -j1_series_complex(z);
}
if n > 1 {
let mut j_nm1 = j0_series_complex(z);
let mut j_n = j1_series_complex(z);
for k in 1..n {
let j_np1 = Complex64::new(2.0 * k as f64, 0.0) / z * j_n - j_nm1;
j_nm1 = j_n;
j_n = j_np1;
}
j_n
} else {
let result = jn_series_complex(-n, z);
if n % 2 == 0 {
result
} else {
-result
}
}
}
#[allow(dead_code)]
fn jv_series_complex(v: f64, z: Complex64) -> Complex64 {
use crate::gamma::gamma;
let z_half = z / Complex64::new(2.0, 0.0);
let z_half_pow_v = z_half.powf(v);
let gamma_v_plus_1 = gamma(v + 1.0);
let mut result = z_half_pow_v / Complex64::new(gamma_v_plus_1, 0.0);
let z2 = z * z;
let mut term = result;
for k in 1..=50 {
term *= -z2 / Complex64::new((4 * k) as f64 * (v + k as f64), 0.0);
result += term;
if term.norm() < 1e-15 * result.norm() {
break;
}
}
result
}
#[allow(dead_code)]
fn jv_half_integer_complex(v: f64, z: Complex64) -> Complex64 {
let sqrt_2_over_pi_z = (Complex64::new(2.0 / PI, 0.0) / z).sqrt();
let n = (v - 0.5) as i32;
if n == 0 {
sqrt_2_over_pi_z * z.sin()
} else if n == -1 {
sqrt_2_over_pi_z * z.cos()
} else {
spherical_bessel_jn_complex(n, z) * sqrt_2_over_pi_z
}
}
#[allow(dead_code)]
fn spherical_bessel_jn_complex(n: i32, z: Complex64) -> Complex64 {
if n == 0 {
if z.norm() < 1e-8 {
Complex64::new(1.0, 0.0)
} else {
z.sin() / z
}
} else if n == 1 {
if z.norm() < 1e-8 {
Complex64::new(0.0, 0.0)
} else {
z.sin() / (z * z) - z.cos() / z
}
} else {
let mut j_nm1 = spherical_bessel_jn_complex(0, z);
let mut j_n = spherical_bessel_jn_complex(1, z);
for k in 1..n {
let j_np1 = Complex64::new((2 * k + 1) as f64, 0.0) / z * j_n - j_nm1;
j_nm1 = j_n;
j_n = j_np1;
}
j_n
}
}
#[allow(dead_code)]
fn i0_series_complex(z: Complex64) -> Complex64 {
let mut result = Complex64::new(1.0, 0.0);
let z2 = z * z;
let mut term = Complex64::new(1.0, 0.0);
for k in 1..=50 {
term *= z2 / Complex64::new((4 * k * k) as f64, 0.0);
result += term;
if term.norm() < 1e-15 * result.norm() {
break;
}
}
result
}
#[allow(dead_code)]
fn k0_series_complex(z: Complex64) -> Complex64 {
let i0_z = i0_series_complex(z);
let ln_z_half = (z / Complex64::new(2.0, 0.0)).ln();
let mut series_part = Complex64::new(0.0, 0.0);
let z2 = z * z;
let mut term = Complex64::new(1.0, 0.0);
let psi_values = [
0.0,
-0.5772156649015329,
0.4227843350984671,
0.9227843350984671,
];
for k in 1..=50 {
term *= z2 / Complex64::new((4 * k * k) as f64, 0.0);
let psi_k = if k < psi_values.len() {
psi_values[k]
} else {
harmonic_number(k) - 0.5772156649015329 };
series_part += term * Complex64::new(psi_k, 0.0);
if term.norm() < 1e-15 {
break;
}
}
-ln_z_half * i0_z + series_part
}
#[allow(dead_code)]
fn harmonic_number(n: usize) -> f64 {
(1..=n).map(|k| 1.0 / k as f64).sum()
}
#[allow(dead_code)]
fn j0_asymptotic_complex(z: Complex64) -> Complex64 {
let sqrt_2_over_pi_z = (Complex64::new(2.0 / PI, 0.0) / z).sqrt();
let phase = z - Complex64::new(PI / 4.0, 0.0);
sqrt_2_over_pi_z * phase.cos()
}
#[allow(dead_code)]
fn j1_asymptotic_complex(z: Complex64) -> Complex64 {
let sqrt_2_over_pi_z = (Complex64::new(2.0 / PI, 0.0) / z).sqrt();
let phase = z - Complex64::new(3.0 * PI / 4.0, 0.0);
sqrt_2_over_pi_z * phase.cos()
}
#[allow(dead_code)]
fn jn_asymptotic_complex(n: i32, z: Complex64) -> Complex64 {
let sqrt_2_over_pi_z = (Complex64::new(2.0 / PI, 0.0) / z).sqrt();
let phase = z - Complex64::new((n as f64 + 0.5) * PI / 2.0, 0.0);
sqrt_2_over_pi_z * phase.cos()
}
#[allow(dead_code)]
fn jv_asymptotic_complex(v: f64, z: Complex64) -> Complex64 {
let sqrt_2_over_pi_z = (Complex64::new(2.0 / PI, 0.0) / z).sqrt();
let phase = z - Complex64::new((v + 0.5) * PI / 2.0, 0.0);
sqrt_2_over_pi_z * phase.cos()
}
#[allow(dead_code)]
fn i0_asymptotic_complex(z: Complex64) -> Complex64 {
let sqrt_2_pi_z = (Complex64::new(2.0 * PI, 0.0) * z).sqrt();
z.exp() / sqrt_2_pi_z
}
#[allow(dead_code)]
fn k0_asymptotic_complex(z: Complex64) -> Complex64 {
let sqrt_pi_over_2z = (Complex64::new(PI / 2.0, 0.0) / z).sqrt();
sqrt_pi_over_2z * (-z).exp()
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_j0_complex_real_values() {
let test_values = [0.0, 1.0, 2.0, 5.0, 10.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let complex_result = j0_complex(z);
let real_result = crate::bessel::j0(x);
assert_relative_eq!(complex_result.re, real_result, epsilon = 1e-10);
assert!(complex_result.im.abs() < 1e-12);
}
}
#[test]
fn test_j1_complex_real_values() {
let test_values = [0.0, 1.0, 2.0, 5.0, 10.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let complex_result = j1_complex(z);
let real_result = crate::bessel::j1(x);
assert_relative_eq!(complex_result.re, real_result, epsilon = 1e-10);
assert!(complex_result.im.abs() < 1e-12);
}
}
#[test]
fn test_jn_complex_real_values() {
let test_values = [0.0, 1.0, 2.0, 5.0];
let orders = [0, 1, 2, 3, 5];
for &n in &orders {
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let complex_result = jn_complex(n, z);
let real_result = crate::bessel::jn(n, x);
assert_relative_eq!(complex_result.re, real_result, epsilon = 1e-8);
assert!(complex_result.im.abs() < 1e-10);
}
}
}
#[test]
fn test_i0_complex_real_values() {
let test_values = [0.0, 1.0, 2.0, 5.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let complex_result = i0_complex(z);
let real_result = crate::bessel::i0(x);
assert_relative_eq!(complex_result.re, real_result, epsilon = 1e-10);
assert!(complex_result.im.abs() < 1e-12);
}
}
#[test]
fn test_k0_complex_real_values() {
let test_values = [0.1, 1.0, 2.0, 5.0];
for &x in &test_values {
let z = Complex64::new(x, 0.0);
let complex_result = k0_complex(z);
let real_result = crate::bessel::k0(x);
assert_relative_eq!(complex_result.re, real_result, epsilon = 1e-8);
assert!(complex_result.im.abs() < 1e-10);
}
}
#[test]
fn test_bessel_recurrence_relation() {
let z = Complex64::new(2.0, 1.0);
let n = 2;
let j_nm1 = jn_complex(n - 1, z);
let j_n = jn_complex(n, z);
let j_np1 = jn_complex(n + 1, z);
let lhs = j_nm1 + j_np1;
let rhs = Complex64::new(2.0 * n as f64, 0.0) / z * j_n;
let error = (lhs - rhs).norm();
assert!(error < 1e-10);
}
#[test]
fn test_modified_bessel_properties() {
let z = Complex64::new(0.0, 0.0);
let i0_result = i0_complex(z);
assert_relative_eq!(i0_result.re, 1.0, epsilon = 1e-10);
assert!(i0_result.im.abs() < 1e-12);
}
#[test]
fn test_half_integer_bessel() {
let x = 2.0;
let z = Complex64::new(x, 0.0);
let j_half = jv_complex(0.5, z);
let expected = (2.0 / (PI * x)).sqrt() * x.sin();
assert_relative_eq!(j_half.re, expected, epsilon = 1e-8);
assert!(j_half.im.abs() < 1e-10);
}
}