use num_traits::Float;
use crate::{
bulirsch::{constants::BulirschConst, MAX_ITERATION},
crate_util::{check, declare},
StrErr,
};
pub fn cel1<T: Float>(kc: T) -> Result<T, StrErr> {
if core::mem::size_of::<T>() <= 4 {
cel1_with_const::<T, f32>(kc)
} else {
cel1_with_const::<T, f64>(kc)
}
}
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
#[inline]
pub fn cel1_with_const<T: Float, C: BulirschConst<T>>(kc: T) -> Result<T, StrErr> {
declare!(mut [kc = kc.abs(), m = T::one(), ans = T::nan(), h]);
for _ in 0..MAX_ITERATION {
h = m;
m = kc + m;
if (h - kc).abs() > C::ca() * h {
kc = (h * kc).sqrt();
m = m / 2.0;
continue;
}
ans = pi!() / m;
break;
}
if ans.is_finite() {
return Ok(ans);
}
check!(@nan, cel1, [kc]);
check!(@zero, cel1, [kc]);
Err("cel1: Failed to converge.")
}
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_close, ellipk, test_util::linspace};
#[test]
fn test_cel1() {
fn test_kc(kc: f64) {
let m = 1.0 - kc * kc;
assert_close!(ellipk(m).unwrap(), cel1(kc).unwrap(), 2e-12);
}
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_cel1_special_cases() {
use std::f64::{INFINITY, NAN, NEG_INFINITY};
assert_eq!(cel1(0.0), Err("cel1: kc cannot be zero."));
assert_eq!(cel1(INFINITY).unwrap(), 0.0);
assert_eq!(cel1(NEG_INFINITY).unwrap(), 0.0);
assert_eq!(cel1(NAN), Err("cel1: Arguments cannot be NAN."));
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(cel1(1e300), Err("cel1: Failed to converge."));
}