use crate::{carlson::elliprd_unchecked, crate_util::check, ellipd, StrErr};
use num_traits::Float;
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub fn ellipdinc<T: Float>(phi: T, m: T) -> Result<T, StrErr> {
if m < 1e-2 * min_val!() {
return Ok(0.0);
}
let sign = phi.signum();
let phi = phi.abs();
if phi > 1.0 / epsilon!() {
if phi >= max_val!() {
return Ok(sign * inf!());
}
return Ok(sign * 2.0 * phi * ellipd(m)? / pi!());
}
let mut rphi = phi % pi_2!();
let mut mm = ((phi - rphi) / pi_2!()).round();
let mut s = 1.0;
if mm % 2.0 > 0.5 {
mm = mm + 1.0;
s = -1.0;
rphi = pi_2!() - rphi;
}
let sinp = rphi.sin();
let cosp = rphi.cos();
let sinp2 = sinp * sinp;
let cosp2 = cosp * cosp;
let c = 1.0 / sinp2;
let cm1 = cosp2 / sinp2;
if m * sinp2 > 1.0 {
return Err("ellipdinc: m sin²φ must be smaller than one.");
}
let mut result = 0.0;
if rphi != 0.0 {
result = s * elliprd_unchecked(cm1, c - m, c) / 3.0;
}
if mm != 0.0 {
result = result + mm * ellipd(m)?;
}
let ans = sign * result;
#[cfg(feature = "test_force_fail")]
let ans = nan!();
if !ans.is_nan() {
return Ok(ans);
}
check!(@nan, ellipdinc, [phi, m]);
Err("ellipdinc: Unexpected error.")
}
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use core::f64;
use super::*;
use crate::{compare_test_data_boost, compare_test_data_wolfram};
#[test]
fn test_ellipdinc() {
compare_test_data_boost!("ellipdinc_data.txt", ellipdinc, 2, 6.4e-16);
}
#[test]
fn test_ellipdinc_wolfram() {
compare_test_data_wolfram!("ellipdinc_data.csv", ellipdinc, 2, 2e-15);
compare_test_data_wolfram!("ellipdinc_neg.csv", ellipdinc, 2, 1e-15);
}
#[test]
fn test_ellipdinc_special_cases() {
use std::f64::{
consts::{FRAC_PI_2, FRAC_PI_4, PI},
INFINITY, NAN, NEG_INFINITY,
};
assert_eq!(ellipdinc(FRAC_PI_2, 1.0).unwrap(), INFINITY);
assert_eq!(
ellipdinc(FRAC_PI_2, 2.0),
Err("ellipdinc: m sin²φ must be smaller than one.")
);
assert_eq!(ellipdinc(0.0, 0.5).unwrap(), 0.0);
assert_eq!(ellipdinc(FRAC_PI_2, 0.0).unwrap(), FRAC_PI_4);
assert!(ellipdinc(FRAC_PI_2, -1.0).unwrap().is_finite());
assert_eq!(
ellipdinc(1e16, 0.4).unwrap(),
2.0 * 1e16 * ellipd(0.4).unwrap() / PI
);
assert_eq!(
ellipdinc(NAN, 0.5),
Err("ellipdinc: Arguments cannot be NAN.")
);
assert_eq!(
ellipdinc(0.5, NAN),
Err("ellipdinc: Arguments cannot be NAN.")
);
assert_eq!(ellipdinc(INFINITY, 0.5).unwrap(), INFINITY);
assert_eq!(ellipdinc(NEG_INFINITY, 0.5).unwrap(), NEG_INFINITY);
assert_eq!(
ellipdinc(0.5, INFINITY),
Err("ellipdinc: m sin²φ must be smaller than one.")
);
assert_eq!(ellipdinc(0.5, NEG_INFINITY).unwrap(), 0.0);
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(ellipdinc(0.5, 0.5), Err("ellipdinc: Unexpected error."));
}