use num_traits::Float;
use crate::{
carlson::elliprf_unchecked,
crate_util::{case, check},
legendre::ellipk::ellipk_precise_unchecked,
StrErr,
};
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub fn ellipf<T: Float>(phi: T, m: T) -> Result<T, StrErr> {
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 * ellipk_precise_unchecked(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 sphi = rphi.sin();
let s2p = sphi * sphi;
if m * s2p >= 1.0 {
return Err("ellipf: m sin²φ must be smaller than one.");
}
let cphi = rphi.cos();
let c2p = cphi * cphi;
let mut ans;
debug_assert!(s2p > min_val!() || !s2p.is_normal());
let c = s2p.recip();
let c_minus_one = c2p / s2p;
let arg2 = if m != 0.0 {
let cross = (c / m).abs();
if cross > 0.9 && cross < 1.1 {
c_minus_one + 1.0 - m
} else {
c - m
}
} else {
c
};
ans = s * elliprf_unchecked(c_minus_one, arg2, c);
if mm != 0.0 {
ans = ans + mm * ellipk_precise_unchecked(m);
}
ans = sign * ans;
if ans.is_finite() {
#[cfg(not(feature = "test_force_fail"))]
return Ok(ans);
}
check!(@nan, ellipf, [phi, m]);
case!(m == neg_inf!(), T::zero());
Err("ellipf: Unexpected error.")
}
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use super::*;
use crate::compare_test_data_boost;
#[test]
fn test_ellipf() {
compare_test_data_boost!("ellipf_data.txt", ellipf, 2, 5.1e-16);
}
#[test]
fn test_ellipf_special_cases() {
use crate::ellipk;
use std::f64::{
consts::{FRAC_PI_2, PI},
INFINITY, NAN, NEG_INFINITY,
};
assert_eq!(
ellipf(FRAC_PI_2, 1.0),
Err("ellipf: m sin²φ must be smaller than one.")
);
assert_eq!(
ellipf(FRAC_PI_2, 2.0),
Err("ellipf: m sin²φ must be smaller than one.")
);
assert_eq!(ellipf(0.0, 0.5).unwrap(), 0.0);
assert_eq!(ellipf(FRAC_PI_2, 0.0).unwrap(), FRAC_PI_2);
assert!(ellipf(FRAC_PI_2, -1.0).unwrap().is_finite());
assert_eq!(ellipf(NAN, 0.5), Err("ellipf: Arguments cannot be NAN."));
assert_eq!(ellipf(0.5, NAN), Err("ellipf: Arguments cannot be NAN."));
assert_eq!(ellipf(INFINITY, 0.5).unwrap(), INFINITY);
assert_eq!(ellipf(NEG_INFINITY, 0.5).unwrap(), NEG_INFINITY);
assert_eq!(
ellipf(1e16, 0.4).unwrap(),
2.0 * 1e16 * ellipk(0.4).unwrap() / PI
);
assert_eq!(ellipf(1e-100, 0.4).unwrap(), (1e-100).sin());
assert_eq!(ellipf(-1e-100, 0.4).unwrap(), (-1e-100).sin());
assert_eq!(
ellipf(0.5, INFINITY),
Err("ellipf: m sin²φ must be smaller than one.")
);
assert_eq!(ellipf(0.5, NEG_INFINITY).unwrap(), 0.0);
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(ellipf(0.5, 0.5), Err("ellipf: Unexpected error."));
}