use num_traits::Float;
use crate::{
carlson::{elliprd_unchecked, elliprf_unchecked},
crate_util::check,
ellipe, StrErr,
};
pub fn ellipeinc<T: Float>(phi: T, m: T) -> Result<T, StrErr> {
let ans = ellipeinc_unchecked(phi, m)?;
if !ans.is_nan() {
return Ok(ans);
}
#[cfg(feature = "test_force_fail")]
let ans = nan!();
check!(@nan, ellipeinc, [phi, m]);
Err("ellipeinc: Unexpected error.")
}
#[inline]
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub fn ellipeinc_unchecked<T: Float>(phi: T, m: T) -> Result<T, StrErr> {
if phi == 0.0 {
return Ok(0.0);
}
if m == 0.0 {
return Ok(phi);
}
let (phi, invert) = if phi < 0.0 { (-phi, -1.0) } else { (phi, 1.0) };
if phi > 1.0 / epsilon!() {
if phi >= max_val!() {
return Ok(invert * inf!());
}
return Ok(invert * 2.0 * phi * ellipe_wrapper(m)? / pi!());
}
if m <= -max_val!() {
return Ok((1.0 - phi.cos()) * (-m).sqrt());
}
if m == 1.0 {
let mm = (phi / pi!()).round();
let remains = phi - mm * pi!();
return Ok(invert * (2.0 * mm + remains.sin()));
}
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 mut result = if m > 0.0 && rphi.powi(3) * m / 6.0 < epsilon!() * rphi.abs() {
s * rphi
} else {
let s2p = rphi.sin() * rphi.sin();
if m * s2p >= 1.0 {
return Err("ellipeinc: m sin²φ must be smaller than one.");
}
let c2p = rphi.cos() * rphi.cos();
let c = 1.0 / s2p;
let cm1 = c2p / s2p;
s * ((1.0 - m) * elliprf_unchecked(cm1, c - m, c)
+ m * (1.0 - m) * elliprd_unchecked(cm1, c, c - m) / 3.0
+ m * (cm1 / (c * (c - m))).sqrt())
};
if mm != 0.0 {
result = result + mm * ellipe_wrapper(m)?;
}
Ok(invert * result)
}
#[inline]
fn ellipe_wrapper<T: Float>(m: T) -> Result<T, StrErr> {
check!(@nan, ellipeinc, [m]);
ellipe(m)
}
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use crate::compare_test_data_boost;
use super::*;
#[test]
fn test_ellipeinc() {
compare_test_data_boost!("ellipeinc_data.txt", ellipeinc, 2, 5e-16);
}
#[test]
fn test_ellipeinc_special_cases() {
use std::f64::{
consts::{FRAC_PI_2, PI},
INFINITY, MAX, MIN_POSITIVE, NAN, NEG_INFINITY,
};
assert_eq!(ellipeinc(FRAC_PI_2, 1.0).unwrap(), 1.0);
assert_eq!(ellipeinc(0.4, 1.0).unwrap(), 0.4.sin());
assert_eq!(
ellipeinc(FRAC_PI_2, 2.0),
Err("ellipeinc: m sin²φ must be smaller than one.")
);
assert_eq!(ellipeinc(0.0, 0.5).unwrap(), 0.0);
assert_eq!(ellipeinc(MIN_POSITIVE, 0.5).unwrap(), MIN_POSITIVE);
assert_eq!(ellipeinc(FRAC_PI_2, 0.0).unwrap(), FRAC_PI_2);
assert!(ellipeinc(FRAC_PI_2, -1.0).unwrap().is_finite());
assert_eq!(
ellipeinc(NAN, 0.5),
Err("ellipeinc: Arguments cannot be NAN.")
);
assert_eq!(
ellipeinc(0.5, NAN),
Err("ellipeinc: Arguments cannot be NAN.")
);
assert_eq!(
ellipeinc(1e16, 0.5).unwrap(),
2.0 * 1e16 * ellipe(0.5).unwrap() / PI
);
assert_eq!(ellipeinc(INFINITY, 0.5).unwrap(), INFINITY);
assert_eq!(ellipeinc(NEG_INFINITY, 0.5).unwrap(), NEG_INFINITY);
assert_eq!(
ellipeinc(0.5, INFINITY),
Err("ellipeinc: m sin²φ must be smaller than one.")
);
assert_eq!(
ellipeinc(0.5, -MAX).unwrap(),
(1.0 - 0.5.cos()) * MAX.sqrt()
);
assert_eq!(ellipeinc(0.5, NEG_INFINITY).unwrap(), INFINITY);
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(ellipeinc(0.5, 0.2), Err("ellipeinc: Unexpected error."));
}