use crate::orthogonal::{chebyshev, gegenbauer, hermite, hermite_prob, jacobi, laguerre, laguerre_generalized, legendre, legendre_assoc};
use scirs2_core::numeric::{Float, FromPrimitive};
use std::fmt::Debug;
#[inline(always)]
fn const_f64<F: Float + FromPrimitive>(value: f64) -> F {
F::from(value).expect("Failed to convert constant to target float type")
}
pub fn legendre_derivative<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
if n == 0 {
return F::zero();
}
let one = F::one();
let x2_minus_1 = x * x - one;
if x2_minus_1.abs() < F::from(1e-14).expect("Failed to convert constant") {
let n_f = F::from(n).expect("Failed to convert n");
let val = n_f * (n_f + one) / const_f64::<F>(2.0);
if x > F::zero() {
return val;
} else {
return if (n + 1) % 2 == 0 { val } else { -val };
}
}
let n_f = F::from(n).expect("Failed to convert n");
let p_n = legendre(n, x);
let p_nm1 = if n > 0 { legendre(n - 1, x) } else { F::zero() };
n_f * (x * p_n - p_nm1) / x2_minus_1
}
pub fn chebyshev_t_derivative<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
if n == 0 {
return F::zero();
}
let n_f = F::from(n).expect("Failed to convert n");
let u_nm1 = chebyshev(n - 1, x, false); n_f * u_nm1
}
pub fn chebyshev_u_derivative<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
if n == 0 {
return F::zero();
}
let one = F::one();
let x2_minus_1 = x * x - one;
if x2_minus_1.abs() < F::from(1e-14).expect("Failed to convert constant") {
let n_f = F::from(n).expect("Failed to convert n");
let val = n_f * (n_f + one) * (n_f + const_f64::<F>(2.0)) / const_f64::<F>(3.0);
if x > F::zero() {
return val;
} else {
return if n % 2 == 0 { -val } else { val };
}
}
let n_f = F::from(n).expect("Failed to convert n");
let u_n = chebyshev(n, x, false);
let t_np1 = chebyshev(n + 1, x, true);
((n_f + one) * t_np1 - x * u_n) / (one - x * x)
}
pub fn hermite_derivative<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
if n == 0 {
return F::zero();
}
let n_f = F::from(n).expect("Failed to convert n");
const_f64::<F>(2.0) * n_f * hermite(n - 1, x)
}
pub fn hermite_prob_derivative<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
if n == 0 {
return F::zero();
}
let n_f = F::from(n).expect("Failed to convert n");
n_f * hermite_prob(n - 1, x)
}
pub fn laguerre_derivative<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
if n == 0 {
return F::zero();
}
-laguerre_generalized(n - 1, F::one(), x)
}
pub fn laguerre_generalized_derivative<F: Float + FromPrimitive + Debug>(
n: usize,
alpha: F,
x: F,
) -> F {
if n == 0 {
return F::zero();
}
-laguerre_generalized(n - 1, alpha + F::one(), x)
}
pub fn jacobi_derivative<F: Float + FromPrimitive + Debug>(
n: usize,
alpha: F,
beta: F,
x: F,
) -> F {
if n == 0 {
return F::zero();
}
let n_f = F::from(n).expect("Failed to convert n");
let factor = (n_f + alpha + beta + F::one()) / const_f64::<F>(2.0);
factor * jacobi(n - 1, alpha + F::one(), beta + F::one(), x)
}
pub fn gegenbauer_derivative<F: Float + FromPrimitive + Debug>(
n: usize,
lambda: F,
x: F,
) -> F {
if n == 0 {
return F::zero();
}
const_f64::<F>(2.0) * lambda * gegenbauer(n - 1, lambda + F::one(), x)
}
pub fn legendre_assoc_derivative<F: Float + FromPrimitive + Debug>(
n: usize,
m: i32,
x: F,
) -> F {
let one = F::one();
let one_minus_x2 = one - x * x;
if one_minus_x2.abs() < F::from(1e-14).expect("Failed to convert constant") {
if m == 0 {
return legendre_derivative(n, x);
}
return if x > F::zero() { F::infinity() } else { F::neg_infinity() };
}
let n_f = F::from(n).expect("Failed to convert n");
let m_abs = m.unsigned_abs() as usize;
let p_n_m = legendre_assoc(n, m, x);
let p_np1_m = legendre_assoc(n + 1, m, x);
let n_minus_m_plus_1 = F::from(n - m_abs + 1 + if m < 0 { 2 * m_abs } else { 0 })
.expect("Failed to convert");
((n_f + one) * x * p_n_m - n_minus_m_plus_1 * p_np1_m) / one_minus_x2
}
pub fn eval_legendre<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
legendre(n, x)
}
pub fn eval_legendre_assoc<F: Float + FromPrimitive + Debug>(n: usize, m: i32, x: F) -> F {
legendre_assoc(n, m, x)
}
pub fn eval_chebyt<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
chebyshev(n, x, true)
}
pub fn eval_chebyu<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
chebyshev(n, x, false)
}
pub fn eval_hermite<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
hermite(n, x)
}
pub fn eval_hermitenorm<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
hermite_prob(n, x)
}
pub fn eval_laguerre<F: Float + FromPrimitive + Debug>(n: usize, x: F) -> F {
laguerre(n, x)
}
pub fn eval_genlaguerre<F: Float + FromPrimitive + Debug>(n: usize, alpha: F, x: F) -> F {
laguerre_generalized(n, alpha, x)
}
pub fn eval_jacobi<F: Float + FromPrimitive + Debug>(
n: usize,
alpha: F,
beta: F,
x: F,
) -> F {
jacobi(n, alpha, beta, x)
}
pub fn eval_gegenbauer<F: Float + FromPrimitive + Debug>(n: usize, lambda: F, x: F) -> F {
gegenbauer(n, lambda, x)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_legendre_derivative_n0() {
assert_relative_eq!(legendre_derivative(0, 0.5f64), 0.0, epsilon = 1e-10);
}
#[test]
fn test_legendre_derivative_n1() {
assert_relative_eq!(legendre_derivative(1, 0.5f64), 1.0, epsilon = 1e-10);
assert_relative_eq!(legendre_derivative(1, 0.0f64), 1.0, epsilon = 1e-10);
assert_relative_eq!(legendre_derivative(1, -0.3f64), 1.0, epsilon = 1e-10);
}
#[test]
fn test_legendre_derivative_n2() {
assert_relative_eq!(legendre_derivative(2, 0.5f64), 1.5, epsilon = 1e-10);
assert_relative_eq!(legendre_derivative(2, 0.0f64), 0.0, epsilon = 1e-10);
}
#[test]
fn test_legendre_derivative_n3() {
let expected = (15.0 * 0.5 * 0.5 - 3.0) / 2.0;
assert_relative_eq!(legendre_derivative(3, 0.5f64), expected, epsilon = 1e-10);
}
#[test]
fn test_legendre_derivative_at_one() {
assert_relative_eq!(legendre_derivative(3, 1.0f64), 6.0, epsilon = 1e-10);
assert_relative_eq!(legendre_derivative(4, 1.0f64), 10.0, epsilon = 1e-10);
}
#[test]
fn test_chebyshev_t_derivative_n0() {
assert_relative_eq!(chebyshev_t_derivative(0, 0.5f64), 0.0, epsilon = 1e-10);
}
#[test]
fn test_chebyshev_t_derivative_n1() {
assert_relative_eq!(chebyshev_t_derivative(1, 0.5f64), 1.0, epsilon = 1e-10);
}
#[test]
fn test_chebyshev_t_derivative_n2() {
assert_relative_eq!(chebyshev_t_derivative(2, 0.5f64), 2.0, epsilon = 1e-10);
}
#[test]
fn test_chebyshev_t_derivative_n3() {
let expected = 12.0 * 0.5 * 0.5 - 3.0;
assert_relative_eq!(chebyshev_t_derivative(3, 0.5f64), expected, epsilon = 1e-10);
}
#[test]
fn test_chebyshev_t_derivative_identity() {
let x = 0.3f64;
let deriv = chebyshev_t_derivative(4, x);
let u3 = chebyshev(3, x, false);
assert_relative_eq!(deriv, 4.0 * u3, epsilon = 1e-10);
}
#[test]
fn test_hermite_derivative_n0() {
assert_relative_eq!(hermite_derivative(0, 0.5f64), 0.0, epsilon = 1e-10);
}
#[test]
fn test_hermite_derivative_n1() {
assert_relative_eq!(hermite_derivative(1, 0.5f64), 2.0, epsilon = 1e-10);
}
#[test]
fn test_hermite_derivative_n2() {
assert_relative_eq!(hermite_derivative(2, 0.5f64), 4.0, epsilon = 1e-10);
}
#[test]
fn test_hermite_derivative_n3() {
let expected = 24.0 * 0.25 - 12.0;
assert_relative_eq!(hermite_derivative(3, 0.5f64), expected, epsilon = 1e-10);
}
#[test]
fn test_hermite_prob_derivative_n2() {
assert_relative_eq!(hermite_prob_derivative(2, 0.5f64), 1.0, epsilon = 1e-10);
}
#[test]
fn test_laguerre_derivative_n0() {
assert_relative_eq!(laguerre_derivative(0, 0.5f64), 0.0, epsilon = 1e-10);
}
#[test]
fn test_laguerre_derivative_n1() {
assert_relative_eq!(laguerre_derivative(1, 0.5f64), -1.0, epsilon = 1e-10);
}
#[test]
fn test_laguerre_derivative_n2() {
assert_relative_eq!(laguerre_derivative(2, 0.5f64), -1.5, epsilon = 1e-10);
}
#[test]
fn test_laguerre_generalized_derivative_n1() {
assert_relative_eq!(
laguerre_generalized_derivative(1, 0.0f64, 0.5f64),
-1.0,
epsilon = 1e-10
);
}
#[test]
fn test_laguerre_generalized_derivative_identity() {
let x = 0.7f64;
let deriv = laguerre_generalized_derivative(3, 1.0, x);
let expected = -laguerre_generalized(2, 2.0, x);
assert_relative_eq!(deriv, expected, epsilon = 1e-10);
}
#[test]
fn test_jacobi_derivative_n0() {
assert_relative_eq!(
jacobi_derivative(0, 1.0f64, 1.0f64, 0.5f64),
0.0,
epsilon = 1e-10
);
}
#[test]
fn test_jacobi_derivative_legendre() {
let x = 0.3f64;
let j_deriv = jacobi_derivative(3, 0.0, 0.0, x);
let l_deriv = legendre_derivative(3, x);
assert_relative_eq!(j_deriv, l_deriv, epsilon = 1e-8);
}
#[test]
fn test_jacobi_derivative_n1() {
let a = 2.0f64;
let b = 3.0f64;
let expected = (a + b + 2.0) / 2.0;
assert_relative_eq!(
jacobi_derivative(1, a, b, 0.5),
expected,
epsilon = 1e-10
);
}
#[test]
fn test_jacobi_derivative_identity() {
let x = 0.4f64;
let deriv = jacobi_derivative(3, 1.0, 2.0, x);
let expected = (3.0 + 1.0 + 2.0 + 1.0) / 2.0 * jacobi(2, 2.0, 3.0, x);
assert_relative_eq!(deriv, expected, epsilon = 1e-8);
}
#[test]
fn test_gegenbauer_derivative_n1() {
assert_relative_eq!(
gegenbauer_derivative(1, 1.5f64, 0.5f64),
3.0,
epsilon = 1e-10
);
}
#[test]
fn test_eval_legendre_matches() {
assert_relative_eq!(
eval_legendre(3, 0.5f64),
legendre(3, 0.5f64),
epsilon = 1e-14
);
}
#[test]
fn test_eval_chebyt_matches() {
assert_relative_eq!(
eval_chebyt(4, 0.3f64),
chebyshev(4, 0.3f64, true),
epsilon = 1e-14
);
}
#[test]
fn test_eval_chebyu_matches() {
assert_relative_eq!(
eval_chebyu(3, 0.4f64),
chebyshev(3, 0.4f64, false),
epsilon = 1e-14
);
}
#[test]
fn test_eval_hermite_matches() {
assert_relative_eq!(
eval_hermite(4, 1.0f64),
hermite(4, 1.0f64),
epsilon = 1e-14
);
}
#[test]
fn test_eval_hermitenorm_matches() {
assert_relative_eq!(
eval_hermitenorm(3, 1.0f64),
hermite_prob(3, 1.0f64),
epsilon = 1e-14
);
}
#[test]
fn test_eval_laguerre_matches() {
assert_relative_eq!(
eval_laguerre(3, 1.5f64),
laguerre(3, 1.5f64),
epsilon = 1e-14
);
}
#[test]
fn test_eval_genlaguerre_matches() {
assert_relative_eq!(
eval_genlaguerre(2, 1.0f64, 1.5f64),
laguerre_generalized(2, 1.0, 1.5),
epsilon = 1e-14
);
}
#[test]
fn test_eval_jacobi_matches() {
assert_relative_eq!(
eval_jacobi(2, 1.0f64, 2.0f64, 0.5f64),
jacobi(2, 1.0, 2.0, 0.5),
epsilon = 1e-14
);
}
#[test]
fn test_eval_gegenbauer_matches() {
assert_relative_eq!(
eval_gegenbauer(3, 1.5f64, 0.5f64),
gegenbauer(3, 1.5, 0.5),
epsilon = 1e-14
);
}
}