use crate::bessel::first_kind::{j0, j1, jn, jv};
use crate::bessel::modified::{i0, i1, iv, k0, k1, kv};
use crate::bessel::second_kind::{y0, y1, yn};
use scirs2_core::numeric::{Float, FromPrimitive};
use std::fmt::Debug;
#[allow(dead_code)]
pub fn j0_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
-j1(x)
}
#[allow(dead_code)]
pub fn j1_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
if x == F::zero() {
return F::from(0.5).expect("Failed to convert constant to float"); }
j0(x) - j1(x) / x
}
#[allow(dead_code)]
pub fn jn_prime<F: Float + FromPrimitive + Debug + std::ops::AddAssign>(n: i32, x: F) -> F {
if n == 0 {
return -j1(x);
}
if x == F::zero() {
if n == 1 {
return F::from(0.5).expect("Failed to convert constant to float");
} else if n % 2 == 0 {
return F::zero();
} else {
return F::neg_infinity();
}
}
(jn(n - 1, x) - jn(n + 1, x)) / F::from(2.0).expect("Failed to convert constant to float")
}
#[allow(dead_code)]
pub fn jv_prime<F: Float + FromPrimitive + Debug + std::ops::AddAssign>(v: F, x: F) -> F {
if v == F::zero() {
return -j1(x);
}
if x == F::zero() {
if v == F::one() {
return F::from(0.5).expect("Failed to convert constant to float");
} else if v > F::one() {
return F::zero();
} else if v < F::zero() {
return F::infinity();
} else {
return F::zero();
}
}
(jv(v - F::one(), x) - jv(v + F::one(), x))
/ F::from(2.0).expect("Failed to convert constant to float")
}
#[allow(dead_code)]
pub fn y0_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
if x <= F::zero() {
return F::nan();
}
-y1(x)
}
#[allow(dead_code)]
pub fn y1_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
if x <= F::zero() {
return F::nan();
}
y0(x) - y1(x) / x
}
#[allow(dead_code)]
pub fn yn_prime<F: Float + FromPrimitive + Debug>(n: i32, x: F) -> F {
if x <= F::zero() {
return F::nan();
}
if n == 0 {
return -y1(x);
}
(yn(n - 1, x) - yn(n + 1, x)) / F::from(2.0).expect("Failed to convert constant to float")
}
#[allow(dead_code)]
pub fn i0_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
i1(x)
}
#[allow(dead_code)]
pub fn i1_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
if x == F::zero() {
return F::from(0.5).expect("Failed to convert constant to float"); }
i0(x) - i1(x) / x
}
#[allow(dead_code)]
pub fn iv_prime<F: Float + FromPrimitive + Debug + std::ops::AddAssign>(v: F, x: F) -> F {
if v == F::zero() {
return i1(x);
}
if x == F::zero() {
if v == F::one() {
return F::from(0.5).expect("Failed to convert constant to float");
} else if v > F::one() {
return F::zero();
} else if v < F::zero() {
return F::infinity();
} else {
return F::zero();
}
}
(iv(v - F::one(), x) + iv(v + F::one(), x))
/ F::from(2.0).expect("Failed to convert constant to float")
}
#[allow(dead_code)]
pub fn k0_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
if x <= F::zero() {
return F::nan();
}
-k1(x)
}
#[allow(dead_code)]
pub fn k1_prime<F: Float + FromPrimitive + Debug>(x: F) -> F {
if x <= F::zero() {
return F::nan();
}
-k0(x) - k1(x) / x
}
#[allow(dead_code)]
pub fn kv_prime<F: Float + FromPrimitive + Debug + std::ops::AddAssign>(v: F, x: F) -> F {
if x <= F::zero() {
return F::nan();
}
if v == F::zero() {
return -k1(x);
}
-(kv(v - F::one(), x) + kv(v + F::one(), x))
/ F::from(2.0).expect("Failed to convert constant to float")
}
#[allow(dead_code)]
pub fn jvp<F>(v: F, x: F, n: Option<i32>) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
{
let n = n.unwrap_or(1);
if n < 0 {
return F::nan();
}
match n {
0 => jv(v, x),
1 => jv_prime(v, x),
_ => {
bessel_derivative_alternating(jv, v, x, n)
}
}
}
fn bessel_derivative_alternating<F, B>(base: B, v: F, x: F, n: i32) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
B: Fn(F, F) -> F,
{
let n_usize = n as usize;
let mut acc = F::zero();
for k in 0..=n_usize {
let coeff = binomial_coefficient::<F>(n_usize, k);
let order = v - F::from(n).expect("convert n") + F::from(2 * k).expect("convert 2k");
let term = coeff * base(order, x);
if k % 2 == 0 {
acc += term;
} else {
acc += -term;
}
}
acc / F::from(2.0_f64.powi(n)).expect("convert 2^n")
}
fn bessel_derivative_modified<F, B>(base: B, v: F, x: F, n: i32, sign: F) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
B: Fn(F, F) -> F,
{
let n_usize = n as usize;
let mut acc = F::zero();
for k in 0..=n_usize {
let coeff = binomial_coefficient::<F>(n_usize, k);
let order = v - F::from(n).expect("convert n") + F::from(2 * k).expect("convert 2k");
acc += coeff * base(order, x);
}
let overall_sign = sign.powi(n);
overall_sign * acc / F::from(2.0_f64.powi(n)).expect("convert 2^n")
}
fn binomial_coefficient<F: FromPrimitive>(n: usize, k: usize) -> F {
if k > n {
return F::from_f64(0.0).expect("convert 0");
}
let k = k.min(n - k);
let mut result = 1.0_f64;
for i in 0..k {
result = result * (n - i) as f64 / (i + 1) as f64;
}
F::from_f64(result.round()).expect("convert binomial coefficient")
}
#[allow(dead_code)]
pub fn yvp<F>(v: F, x: F, n: Option<i32>) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
{
let n = n.unwrap_or(1);
if n < 0 {
return F::nan();
}
let v_f64 = v.to_f64().expect("convert order to f64");
let is_integer_order = v_f64.fract() == 0.0;
match n {
0 => {
if v == F::zero() {
y0(x)
} else if v == F::one() {
y1(x)
} else if is_integer_order {
yn(v_f64 as i32, x)
} else {
F::nan()
}
}
1 => {
if v == F::zero() {
y0_prime(x)
} else if v == F::one() {
y1_prime(x)
} else if is_integer_order {
let m = v_f64 as i32;
(yn(m - 1, x) - yn(m + 1, x)) / F::from(2.0).expect("convert 2")
} else {
F::nan()
}
}
_ => {
if is_integer_order {
let yn_base = |order: F, arg: F| -> F {
let order_i = order.to_f64().expect("convert order to f64").round() as i32;
yn(order_i, arg)
};
bessel_derivative_alternating(yn_base, v, x, n)
} else {
F::nan()
}
}
}
}
#[allow(dead_code)]
pub fn ivp<F>(v: F, x: F, n: Option<i32>) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
{
let n = n.unwrap_or(1);
if n < 0 {
return F::nan();
}
match n {
0 => iv(v, x),
1 => iv_prime(v, x),
_ => {
bessel_derivative_modified(iv_reflected, v, x, n, F::one())
}
}
}
fn iv_reflected<F: Float + FromPrimitive + Debug + std::ops::AddAssign>(order: F, x: F) -> F {
let order_f64 = order.to_f64().expect("convert order to f64");
if order_f64 < 0.0 && order_f64.fract() == 0.0 {
iv(-order, x)
} else {
iv(order, x)
}
}
#[allow(dead_code)]
pub fn kvp<F>(v: F, x: F, n: Option<i32>) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
{
let n = n.unwrap_or(1);
if n < 0 {
return F::nan();
}
match n {
0 => kv(v, x),
1 => kv_prime(v, x),
_ => {
bessel_derivative_modified(kv, v, x, n, -F::one())
}
}
}
#[allow(dead_code)]
pub fn h1vp<F>(_v: F, _x: F, n: Option<i32>) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
{
let _n = n.unwrap_or(1);
F::nan()
}
#[allow(dead_code)]
pub fn h2vp<F>(_v: F, _x: F, n: Option<i32>) -> F
where
F: Float + FromPrimitive + Debug + std::ops::AddAssign,
{
let _n = n.unwrap_or(1);
F::nan()
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_j0_prime() {
let x = 2.0;
assert_relative_eq!(j0_prime(x), -j1(x), epsilon = 1e-10);
}
#[test]
fn test_j1_prime() {
let x = 2.0;
let expected = j0(x) - j1(x) / x;
assert_relative_eq!(j1_prime(x), expected, epsilon = 1e-10);
}
#[test]
fn test_jn_prime() {
let x = 2.0;
let expected = (jn(1, x) - jn(3, x)) / 2.0;
assert_relative_eq!(jn_prime(2, x), expected, epsilon = 1e-10);
}
#[test]
fn test_jvp_second_derivative_against_finite_difference() {
let x = 3.0_f64;
let v = 0.0_f64;
let h = 1e-4;
let fd = (jvp(v, x + h, Some(1)) - jvp(v, x - h, Some(1))) / (2.0 * h);
assert_relative_eq!(jvp(v, x, Some(2)), fd, epsilon = 1e-6);
}
#[test]
fn test_jvp_second_derivative_closed_form() {
let x = 2.5_f64;
let v = 2.0_f64;
let expected = 0.25 * (jv(v - 2.0, x) - 2.0 * jv(v, x) + jv(v + 2.0, x));
assert_relative_eq!(jvp(v, x, Some(2)), expected, epsilon = 1e-10);
}
#[test]
fn test_jvp_higher_order_differs_from_first() {
let x = 2.0_f64;
let v = 1.0_f64;
let d1 = jvp(v, x, Some(1));
let d3 = jvp(v, x, Some(3));
assert!(
(d1 - d3).abs() > 1e-3,
"3rd derivative should differ from 1st: d1={}, d3={}",
d1,
d3
);
let expected =
(jv(v - 3.0, x) - 3.0 * jv(v - 1.0, x) + 3.0 * jv(v + 1.0, x) - jv(v + 3.0, x)) / 8.0;
assert_relative_eq!(d3, expected, epsilon = 1e-10);
}
#[test]
fn test_ivp_second_derivative_closed_form() {
let x = 1.5_f64;
let v = 2.0_f64;
let expected = 0.25 * (iv(v - 2.0, x) + 2.0 * iv(v, x) + iv(v + 2.0, x));
assert_relative_eq!(ivp(v, x, Some(2)), expected, epsilon = 1e-9);
}
#[test]
fn test_ivp_reflection_negative_integer_order() {
let x = 1.5_f64;
let v = 0.0_f64;
let expected = 0.25 * (iv(2.0, x) + 2.0 * iv(0.0, x) + iv(2.0, x));
assert_relative_eq!(ivp(v, x, Some(2)), expected, epsilon = 1e-9);
}
#[test]
fn test_ivp_second_derivative_against_finite_difference() {
let x = 2.0_f64;
let v = 0.0_f64;
let h = 1e-4;
let fd = (ivp(v, x + h, Some(1)) - ivp(v, x - h, Some(1))) / (2.0 * h);
assert_relative_eq!(ivp(v, x, Some(2)), fd, epsilon = 1e-5);
}
#[test]
fn test_kvp_second_derivative_closed_form() {
let x = 1.5_f64;
let v = 1.0_f64;
let expected = 0.25 * (kv(v - 2.0, x) + 2.0 * kv(v, x) + kv(v + 2.0, x));
assert_relative_eq!(kvp(v, x, Some(2)), expected, epsilon = 1e-9);
}
#[test]
fn test_kvp_second_derivative_against_finite_difference() {
let x = 2.0_f64;
let v = 0.0_f64;
let h = 1e-4;
let fd = (kvp(v, x + h, Some(1)) - kvp(v, x - h, Some(1))) / (2.0 * h);
assert_relative_eq!(kvp(v, x, Some(2)), fd, epsilon = 1e-5);
}
#[test]
fn test_yvp_integer_second_derivative_closed_form() {
let x = 2.5_f64;
let v = 1.0_f64;
let expected = 0.25 * (yn(-1, x) - 2.0 * yn(1, x) + yn(3, x));
assert_relative_eq!(yvp(v, x, Some(2)), expected, epsilon = 1e-9);
}
#[test]
fn test_yvp_noninteger_returns_nan() {
let x = 2.0_f64;
assert!(yvp(0.5_f64, x, Some(2)).is_nan());
assert!(yvp(1.5_f64, x, Some(1)).is_nan());
}
#[test]
fn test_derivative_negative_order_returns_nan() {
let x = 2.0_f64;
assert!(jvp(0.0_f64, x, Some(-1)).is_nan());
assert!(ivp(0.0_f64, x, Some(-1)).is_nan());
assert!(kvp(0.0_f64, x, Some(-1)).is_nan());
assert!(yvp(0.0_f64, x, Some(-1)).is_nan());
}
#[test]
fn test_binomial_coefficient_values() {
assert_relative_eq!(binomial_coefficient::<f64>(4, 0), 1.0, epsilon = 1e-12);
assert_relative_eq!(binomial_coefficient::<f64>(4, 2), 6.0, epsilon = 1e-12);
assert_relative_eq!(binomial_coefficient::<f64>(5, 2), 10.0, epsilon = 1e-12);
assert_relative_eq!(binomial_coefficient::<f64>(3, 5), 0.0, epsilon = 1e-12);
}
}