use crate::array::Array;
use num_traits::Float;
use std::fmt::Debug;
use super::gamma::gamma_scalar;
pub fn bessel_j<T>(n: i32, x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| bessel_j_scalar(n, v))
}
pub fn bessel_y<T>(n: i32, x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| bessel_y_scalar(n, v))
}
pub fn bessel_i<T>(n: i32, x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| bessel_i_scalar(n, v))
}
pub fn bessel_k<T>(n: i32, x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| bessel_k_scalar(n, v))
}
fn bessel_j_scalar<T>(n: i32, x: T) -> T
where
T: Float + Debug,
{
if n < 0 {
let factor = if n % 2 == 0 {
T::one()
} else {
T::from(-1.0).expect("-1.0 should convert to float type")
};
return factor * bessel_j_scalar(-n, x);
}
if x == T::zero() {
return if n == 0 { T::one() } else { T::zero() };
}
let mut result = T::zero();
let x_half = x / T::from(2.0).expect("2.0 should convert to float type");
let n_t = T::from(n).expect("n should convert to float type");
for m in 0..20 {
let m_t = T::from(m).expect("m should convert to float type");
let sign = if m % 2 == 0 {
T::one()
} else {
T::from(-1.0).expect("-1.0 should convert to float type")
};
let power = x_half.powf(n_t + m_t + m_t);
let m_factorial = gamma_scalar(m_t + T::one());
let n_plus_m_factorial = gamma_scalar(n_t + m_t + T::one());
let term = sign * power / (m_factorial * n_plus_m_factorial);
result = result + term;
if term.abs() < result.abs() * T::from(1e-10).expect("1e-10 should convert to float type") {
break;
}
}
result
}
fn bessel_y_scalar<T>(n: i32, x: T) -> T
where
T: Float + Debug,
{
if x <= T::zero() {
return T::nan();
}
let pi = T::from(std::f64::consts::PI).expect("PI should convert to float type");
let n_pi = T::from(n).expect("n should convert to float type") * pi;
if n % 2 == 0 {
let j_n = bessel_j_scalar(n, x);
let j_n_plus_1 = bessel_j_scalar(n + 1, x);
return T::from(2.0).expect("2.0 should convert to float type") / pi * j_n.ln()
- T::from(2.0).expect("2.0 should convert to float type") * j_n / (pi * x)
- j_n_plus_1
+ j_n / (pi * x);
}
(bessel_j_scalar(n, x) * n_pi.cos() - bessel_j_scalar(-n, x)) / n_pi.sin()
}
fn bessel_i_scalar<T>(n: i32, x: T) -> T
where
T: Float + Debug,
{
if n < 0 {
return bessel_i_scalar(-n, x);
}
if x == T::zero() {
return if n == 0 { T::one() } else { T::zero() };
}
let mut result = T::zero();
let x_half = x / T::from(2.0).expect("2.0 should convert to float type");
let n_t = T::from(n).expect("n should convert to float type");
for m in 0..20 {
let m_t = T::from(m).expect("m should convert to float type");
let power = x_half.powf(n_t + m_t + m_t);
let m_factorial = gamma_scalar(m_t + T::one());
let n_plus_m_factorial = gamma_scalar(n_t + m_t + T::one());
let term = power / (m_factorial * n_plus_m_factorial);
result = result + term;
if term.abs() < result.abs() * T::from(1e-10).expect("1e-10 should convert to float type") {
break;
}
}
result
}
fn bessel_k_scalar<T>(n: i32, x: T) -> T
where
T: Float + Debug,
{
if x <= T::zero() {
return T::infinity();
}
if n < 0 {
return bessel_k_scalar(-n, x);
}
let pi = T::from(std::f64::consts::PI).expect("PI should convert to float type");
let n_t = T::from(n).expect("n should convert to float type");
if x < T::one() {
if n == 0 {
let gamma = T::from(0.577_215_664_901_533)
.expect("Euler constant should convert to float type"); let mut sum = T::zero();
let x_sq_4 = x * x / T::from(4.0).expect("4.0 should convert to float type");
let mut term = T::one();
let mut fact = T::one();
let mut psi = -gamma;
for k in 1..16 {
let k_t = T::from(k).expect("k should convert to float type");
fact = fact * k_t; psi = psi + T::one() / k_t; term = term * x_sq_4 / (k_t * k_t); let term_contribution = term * (psi + psi); sum = sum + term_contribution;
if term_contribution.abs() < sum.abs() * T::epsilon() {
break;
}
}
-sum - x.ln() * bessel_i_scalar(0, x)
}
else {
let k0 = bessel_k_scalar(0, x);
let x_inv = T::one() / x;
let half_x = x / T::from(2.0).expect("2.0 should convert to float type");
let mut k1 = x_inv;
if n == 1 {
k1 = x_inv
+ half_x
* (x / T::from(2.0).expect("2.0 should convert to float type")).ln()
* bessel_i_scalar(1, x);
for k in 1..16 {
let k_t = T::from(k).expect("k should convert to float type");
let term = T::from(0.5).expect("0.5 should convert to float type") * x * x
/ T::from(4.0).expect("4.0 should convert to float type")
* T::from(k).expect("k should convert to float type")
/ (k_t * k_t * (k_t + T::one()));
k1 = k1 + term;
if term.abs() < k1.abs() * T::epsilon() {
break;
}
}
return k1;
}
let mut k_prev = k0;
let mut k_curr = k1;
for i in 1..n {
let i_t = T::from(i).expect("i should convert to float type");
let k_next = (T::from(2.0).expect("2.0 should convert to float type") * i_t / x)
* k_curr
+ k_prev;
k_prev = k_curr;
k_curr = k_next;
}
k_curr
}
}
else if x < T::from(8.0).expect("8.0 should convert to float type") * n_t {
let pi_half = pi / T::from(2.0).expect("2.0 should convert to float type");
if n == 0 {
let i0 = bessel_i_scalar(0, x);
let i1 = bessel_i_scalar(1, x);
let k1_approx = num_traits::Float::sqrt(
pi / (T::from(2.0).expect("2.0 should convert to float type") * x),
) * (-x).exp();
return (T::one() / x - i1 * k1_approx) / i0;
}
else if n == 1 || n == 2 {
if n == 1 {
let factor = num_traits::Float::sqrt(
pi / (T::from(2.0).expect("2.0 should convert to float type") * x),
) * (-x).exp();
let correction = T::one()
+ T::from(3.0).expect("3.0 should convert to float type")
/ (T::from(8.0).expect("8.0 should convert to float type") * x);
return factor * correction;
}
let k0 = bessel_k_scalar(0, x);
let k1 = bessel_k_scalar(1, x);
let k2 =
(T::from(2.0).expect("2.0 should convert to float type") * T::one() / x) * k1 + k0;
return k2;
}
let i_n = bessel_i_scalar(n, x);
let i_minus_n = bessel_i_scalar(-n, x);
let sin_term = (n_t * pi).sin();
if sin_term.abs() < T::from(1e-10).expect("1e-10 should convert to float type") {
return pi_half * (bessel_i_scalar(-n - 1, x) + bessel_i_scalar(n - 1, x));
}
pi_half * (i_minus_n - i_n) / sin_term
}
else {
let factor = num_traits::Float::sqrt(
pi / (T::from(2.0).expect("2.0 should convert to float type") * x),
) * (-x).exp();
let mut sum = T::one();
let n_sq = n_t * n_t;
let mut a = T::from(4.0).expect("4.0 should convert to float type") * n_sq - T::one();
let mut term = T::one();
for k in 1..5 {
let k_t = T::from(k).expect("k should convert to float type");
term = term * a / (T::from(8.0).expect("8.0 should convert to float type") * k_t * x);
sum = sum + term;
a = a - T::from(8.0).expect("8.0 should convert to float type") * k_t;
if term.abs() < sum.abs() * T::epsilon() {
break;
}
}
factor * sum
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_bessel_j() {
let values = Array::from_vec(vec![0.0, 1.0, 2.0, 5.0]);
let j0 = bessel_j(0, &values);
assert_relative_eq!(j0.to_vec()[0], 1.0, epsilon = 1e-10);
assert_relative_eq!(j0.to_vec()[1], 0.7651976865579666, epsilon = 1e-4);
let j1 = bessel_j(1, &values);
assert_relative_eq!(j1.to_vec()[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(j1.to_vec()[1], 0.44005058574493355, epsilon = 1e-4);
}
}