use num_traits::Float;
use crate::{carlson::elliprd_unchecked, crate_util::check, StrErr};
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub fn ellipd<T: Float>(m: T) -> Result<T, StrErr> {
if m >= 1.0 {
if m == 1.0 {
return Ok(inf!());
}
return Err("ellipd: m must not be greater than 1.");
}
if m.abs() <= epsilon!() {
return Ok(pi!() / 4.0);
}
if m <= min_val!() {
return Ok(1.0 / (-m).sqrt());
}
let ans = elliprd_unchecked(0.0, 1.0 - m, 1.0) / 3.0;
#[cfg(feature = "test_force_fail")]
let ans = nan!();
if ans.is_finite() {
return Ok(ans);
}
check!(@nan, ellipd, [m]);
Err("ellipd: Unexpected error.")
}
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use super::*;
use crate::{compare_test_data_boost, compare_test_data_wolfram};
#[test]
fn test_ellipd() {
compare_test_data_boost!("ellipd_data.txt", ellipd, 1, 2.9e-16);
}
#[test]
fn test_ellipd_wolfram() {
compare_test_data_wolfram!("ellipd_data.csv", ellipd, 1, 8e-16);
compare_test_data_wolfram!("ellipd_neg.csv", ellipd, 1, 8e-16);
}
#[test]
fn test_ellipd_special_cases() {
use std::f64::{consts::FRAC_PI_4, INFINITY, MAX, NAN, NEG_INFINITY};
assert_eq!(ellipd(0.0).unwrap(), FRAC_PI_4);
assert_eq!(ellipd(1.0).unwrap(), INFINITY);
assert!(ellipd(-1.0).unwrap().is_finite());
assert_eq!(ellipd(1.1), Err("ellipd: m must not be greater than 1."));
assert_eq!(ellipd(NAN), Err("ellipd: Arguments cannot be NAN."));
assert_eq!(
ellipd(INFINITY),
Err("ellipd: m must not be greater than 1.")
);
assert_eq!(ellipd(-MAX).unwrap(), 1.0 / MAX.sqrt());
assert_eq!(ellipd(NEG_INFINITY).unwrap(), 0.0);
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(ellipd(0.5), Err("ellipd: Unexpected error."));
}