use num_traits::Float;
use crate::{
bulirsch::{constants::BulirschConst, MAX_ITERATION},
crate_util::{check, declare},
StrErr,
};
pub fn cel2<T: Float>(kc: T, a: T, b: T) -> Result<T, StrErr> {
if core::mem::size_of::<T>() <= 4 {
cel2_with_const::<T, f32>(kc, a, b)
} else {
cel2_with_const::<T, f64>(kc, a, b)
}
}
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
#[inline]
pub fn cel2_with_const<T: Float, C: BulirschConst<T>>(kc: T, a: T, b: T) -> Result<T, StrErr> {
declare!(mut [kc = kc.abs(), aa = a, bb = b, m = T::one(), c = aa, ans = T::nan(), m0]);
aa = bb + aa;
for _ in 0..MAX_ITERATION {
bb = (c * kc + bb) * 2.0;
c = aa;
m0 = m;
m = kc + m;
aa = bb / m + aa;
if (m0 - kc).abs() > C::ca() * m0 {
kc = (kc * m0).sqrt() * 2.0;
continue;
}
ans = pi!() / 4.0 * aa / m;
break;
}
if ans.is_finite() {
return Ok(ans);
}
check!(@nan, cel2, [kc, a, b]);
check!(@zero, cel2, [kc]);
check!(@multi, cel2, "infinite", is_infinite, [kc, a, b]);
if kc.is_infinite() {
return Ok(0.0);
}
if a.is_infinite() {
return Ok(a.signum() * inf!());
}
if b.is_infinite() {
return Ok(b.signum() * inf!());
}
Err("cel2: Failed to converge.")
}
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_close, ellipe, test_util::linspace};
#[test]
fn test_cel2() {
fn test_kc(kc: f64) {
let m = 1.0 - kc * kc;
assert_close!(ellipe(m).unwrap(), cel2(kc, 1.0, kc * kc).unwrap(), 7.7e-16);
}
let linsp_neg = linspace(-1.0, -1e-3, 100);
linsp_neg.iter().for_each(|kc| test_kc(*kc));
let linsp_pos = linspace(1e-3, 1.0, 100);
linsp_pos.iter().for_each(|kc| test_kc(*kc));
}
#[test]
fn test_cel2_special_cases() {
use std::f64::{INFINITY, NAN, NEG_INFINITY};
assert_eq!(cel2(0.0, 1.0, 1.0), Err("cel2: kc cannot be zero."));
assert_eq!(cel2(INFINITY, 1.0, 1.0).unwrap(), 0.0);
assert_eq!(cel2(NEG_INFINITY, 1.0, 1.0).unwrap(), 0.0);
assert_eq!(cel2(0.5, 0.0, 0.0).unwrap(), 0.0);
assert_eq!(cel2(0.5, INFINITY, 1.0).unwrap(), INFINITY);
assert_eq!(cel2(0.5, 1.0, INFINITY).unwrap(), INFINITY);
assert_eq!(cel2(0.5, NEG_INFINITY, 1.0).unwrap(), NEG_INFINITY);
assert_eq!(cel2(0.5, 1.0, NEG_INFINITY).unwrap(), NEG_INFINITY);
assert_eq!(cel2(NAN, 1.0, 1.0), Err("cel2: Arguments cannot be NAN."));
assert_eq!(cel2(0.5, NAN, 1.0), Err("cel2: Arguments cannot be NAN."));
assert_eq!(cel2(0.5, 1.0, NAN), Err("cel2: Arguments cannot be NAN."));
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(cel2(1e300, 0.5, 0.5), Err("cel2: Failed to converge."));
}