use num_traits::Float;
use crate::{carlson::elliprj_unchecked, crate_util::check, ellipk, StrErr};
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub fn jacobi_zeta<T: Float>(phi: T, m: T) -> Result<T, StrErr> {
let ans = jacobi_zeta_unchecked(phi, m)?;
if ans.is_finite() {
return Ok(ans);
}
check!(@nan, jacobi_zeta, [phi, m]);
check!(@inf, jacobi_zeta, [phi, m]);
Err("jacobi_zeta: Unexpected error.")
}
#[inline]
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub fn jacobi_zeta_unchecked<T: Float>(phi: T, m: T) -> Result<T, StrErr> {
let sign = phi.signum();
let phi = phi.abs();
let sinp = phi.sin();
let cosp = phi.cos();
let nphi = (phi / (pi_2!())).round();
Ok(if (phi - nphi * pi_2!()).abs() < epsilon!().sqrt() {
0.0
} else if m >= 1.0 {
if m != 1.0 {
return Err("jacobi_zeta: m must not be greater than 1.");
}
sign * sinp * cosp.signum()
} else {
let mc = 1.0 - m;
let c2p = cosp * cosp;
let one_m_ms2p = mc + m * c2p;
sign * m * sinp * cosp * one_m_ms2p.sqrt() * elliprj_unchecked(0.0, mc, 1.0, one_m_ms2p)
/ (3.0 * ellipk(m).unwrap_or(nan!()))
})
}
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use super::*;
use crate::compare_test_data_wolfram;
#[test]
fn test_jacobi_zeta() {
compare_test_data_wolfram!("jacobi_zeta_data.csv", jacobi_zeta, 2, 3e-15);
}
#[test]
fn test_jacobi_neg() {
compare_test_data_wolfram!("jacobi_zeta_neg.csv", jacobi_zeta, 2, 3e-15);
}
#[test]
fn test_jacobi_zeta_special_cases() {
use std::f64::{
consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_4, FRAC_PI_6, PI},
INFINITY, NAN,
};
assert_eq!(jacobi_zeta(0.0, 0.5).unwrap(), 0.0);
assert_eq!(jacobi_zeta(0.0, 0.0).unwrap(), 0.0);
assert_eq!(jacobi_zeta(0.0, 0.9).unwrap(), 0.0);
assert_eq!(jacobi_zeta(FRAC_PI_4, 0.0).unwrap(), 0.0);
assert_eq!(jacobi_zeta(FRAC_PI_2, 0.0).unwrap(), 0.0);
assert_eq!(jacobi_zeta(PI, 0.0).unwrap(), 0.0);
assert_eq!(jacobi_zeta(FRAC_PI_2, 0.5).unwrap(), 0.0);
assert_eq!(jacobi_zeta(PI, 0.5).unwrap(), 0.0);
assert_eq!(jacobi_zeta(3.0 * FRAC_PI_2, 0.5).unwrap(), 0.0);
assert_eq!(jacobi_zeta(FRAC_PI_3, 1.0).unwrap(), FRAC_PI_3.sin());
assert_eq!(jacobi_zeta(FRAC_PI_4, 1.0).unwrap(), FRAC_PI_4.sin());
assert_eq!(jacobi_zeta(FRAC_PI_6, 1.0).unwrap(), FRAC_PI_6.sin());
assert_eq!(
jacobi_zeta(1.0, 1.5),
Err("jacobi_zeta: m must not be greater than 1.")
);
assert_eq!(
jacobi_zeta(NAN, 1.0),
Err("jacobi_zeta: Arguments cannot be NAN.")
);
assert_eq!(
jacobi_zeta(1.0, NAN),
Err("jacobi_zeta: Arguments cannot be NAN.")
);
assert_eq!(
jacobi_zeta(INFINITY, 1.0),
Err("jacobi_zeta: phi cannot be infinite.")
);
assert_eq!(
jacobi_zeta(-INFINITY, 1.0),
Err("jacobi_zeta: phi cannot be infinite.")
);
assert_eq!(
jacobi_zeta(1.0, -INFINITY),
Err("jacobi_zeta: m cannot be infinite.")
);
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(jacobi_zeta(0.5, 0.5), Err("jacobi_zeta: Unexpected error."));
}