use numra_core::Scalar;
pub fn legendre_p<S: Scalar>(n: usize, x: S) -> S {
if n == 0 {
return S::ONE;
}
if n == 1 {
return x;
}
let mut pm1 = S::ONE;
let mut p = x;
for k in 1..n {
let kf = S::from_usize(k);
let pn = (S::from_f64(2.0 * k as f64 + 1.0) * x * p - kf * pm1) / S::from_usize(k + 1);
pm1 = p;
p = pn;
}
p
}
pub fn hermite_h<S: Scalar>(n: usize, x: S) -> S {
if n == 0 {
return S::ONE;
}
if n == 1 {
return S::TWO * x;
}
let mut hm1 = S::ONE;
let mut h = S::TWO * x;
for k in 1..n {
let hn = S::TWO * x * h - S::TWO * S::from_usize(k) * hm1;
hm1 = h;
h = hn;
}
h
}
pub fn laguerre_l<S: Scalar>(n: usize, x: S) -> S {
if n == 0 {
return S::ONE;
}
if n == 1 {
return S::ONE - x;
}
let mut lm1 = S::ONE;
let mut l = S::ONE - x;
for k in 1..n {
let kf = S::from_usize(k);
let ln = (S::from_f64(2.0 * k as f64 + 1.0) - x) * l / S::from_usize(k + 1)
- kf * lm1 / S::from_usize(k + 1);
lm1 = l;
l = ln;
}
l
}
pub fn chebyshev_t<S: Scalar>(n: usize, x: S) -> S {
if n == 0 {
return S::ONE;
}
if n == 1 {
return x;
}
let mut tm1 = S::ONE;
let mut t = x;
for _ in 1..n {
let tn = S::TWO * x * t - tm1;
tm1 = t;
t = tn;
}
t
}
pub fn legendre_plm<S: Scalar>(l: usize, m: usize, x: S) -> S {
if m > l {
return S::ZERO;
}
let xf = x.to_f64();
let mut pmm = 1.0;
if m > 0 {
let somx2 = (1.0 - xf * xf).sqrt();
let mut fact = 1.0;
for _i in 1..=m {
pmm *= -fact * somx2;
fact += 2.0;
}
}
if l == m {
return S::from_f64(pmm);
}
let mut pmmp1 = xf * (2 * m + 1) as f64 * pmm;
if l == m + 1 {
return S::from_f64(pmmp1);
}
let mut pll = 0.0;
for ll in (m + 2)..=l {
pll = ((2 * ll - 1) as f64 * xf * pmmp1 - (ll + m - 1) as f64 * pmm) / (ll - m) as f64;
pmm = pmmp1;
pmmp1 = pll;
}
S::from_f64(pll)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_legendre_p() {
assert_relative_eq!(legendre_p(0, 0.5_f64), 1.0, epsilon = 1e-14);
assert_relative_eq!(legendre_p(1, 0.5_f64), 0.5, epsilon = 1e-14);
assert_relative_eq!(legendre_p(2, 0.5_f64), -0.125, epsilon = 1e-14);
assert_relative_eq!(legendre_p(3, 0.5_f64), -0.4375, epsilon = 1e-14);
}
#[test]
fn test_legendre_at_one() {
for n in 0..10 {
assert_relative_eq!(legendre_p(n, 1.0_f64), 1.0, epsilon = 1e-12);
}
}
#[test]
fn test_hermite_h() {
let x = 1.5_f64;
assert_relative_eq!(hermite_h(0, x), 1.0, epsilon = 1e-14);
assert_relative_eq!(hermite_h(1, x), 3.0, epsilon = 1e-14);
assert_relative_eq!(hermite_h(2, x), 7.0, epsilon = 1e-14);
assert_relative_eq!(hermite_h(3, x), 9.0, epsilon = 1e-14);
}
#[test]
fn test_laguerre_l() {
let x = 2.0_f64;
assert_relative_eq!(laguerre_l(0, x), 1.0, epsilon = 1e-14);
assert_relative_eq!(laguerre_l(1, x), -1.0, epsilon = 1e-14);
assert_relative_eq!(laguerre_l(2, x), -1.0, epsilon = 1e-14);
}
#[test]
fn test_chebyshev_t() {
let x = 0.5_f64;
assert_relative_eq!(chebyshev_t(0, x), 1.0, epsilon = 1e-14);
assert_relative_eq!(chebyshev_t(1, x), 0.5, epsilon = 1e-14);
assert_relative_eq!(chebyshev_t(2, x), -0.5, epsilon = 1e-14);
assert_relative_eq!(chebyshev_t(3, x), -1.0, epsilon = 1e-14);
}
#[test]
fn test_chebyshev_at_one() {
for n in 0..10 {
assert_relative_eq!(chebyshev_t(n, 1.0_f64), 1.0, epsilon = 1e-12);
}
}
#[test]
fn test_chebyshev_trig() {
let theta = 0.7_f64;
let x = theta.cos();
for n in 0..8 {
let expected = (n as f64 * theta).cos();
assert_relative_eq!(chebyshev_t(n, x), expected, epsilon = 1e-12);
}
}
#[test]
fn test_legendre_plm() {
let x = 0.5_f64;
let expected = -(1.0 - x * x).sqrt();
assert_relative_eq!(legendre_plm(1, 1, x), expected, epsilon = 1e-12);
assert_relative_eq!(legendre_plm(2, 0, x), legendre_p(2, x), epsilon = 1e-14);
}
#[test]
fn test_orthogonal_f32() {
assert!((legendre_p(2, 0.5_f32) - (-0.125_f32)).abs() < 1e-6);
assert!((chebyshev_t(2, 0.5_f32) - (-0.5_f32)).abs() < 1e-6);
}
}