use num_traits::Float;
use crate::{
crate_util::{check, declare},
polyeval, StrErr,
};
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub fn ellipk<T: Float>(m: T) -> Result<T, StrErr> {
match (m * 20.0).to_i64() {
Some(0) | Some(1) => {
let coeffs = [
1.591003453790792180,
0.416000743991786912,
0.245791514264103415,
0.179481482914906162,
0.144556057087555150,
0.123200993312427711,
0.108938811574293531,
0.098853409871592910,
0.091439629201749751,
0.085842591595413900,
0.081541118718303215,
0.078199656811256481910,
];
Ok(polyeval(m - 0.05, &coeffs))
}
Some(2) | Some(3) => {
let coeffs = [
1.635256732264579992,
0.471190626148732291,
0.309728410831499587,
0.252208311773135699,
0.226725623219684650,
0.215774446729585976,
0.213108771877348910,
0.216029124605188282,
0.223255831633057896,
0.234180501294209925,
0.248557682972264071,
0.266363809892617521,
];
Ok(polyeval(m - 0.15, &coeffs))
}
Some(4) | Some(5) => {
let coeffs = [
1.685750354812596043,
0.541731848613280329,
0.401524438390690257,
0.369642473420889090,
0.376060715354583645,
0.405235887085125919,
0.453294381753999079,
0.520518947651184205,
0.609426039204995055,
0.724263522282908870,
0.871013847709812357,
1.057652872753547036,
];
Ok(polyeval(m - 0.25, &coeffs))
}
Some(6) | Some(7) => {
let coeffs = [
1.744350597225613243,
0.634864275371935304,
0.539842564164445538,
0.571892705193787391,
0.670295136265406100,
0.832586590010977199,
1.073857448247933265,
1.422091460675497751,
1.920387183402304829,
2.632552548331654201,
3.652109747319039160,
5.115867135558865806,
7.224080007363877411,
];
Ok(polyeval(m - 0.35, &coeffs))
}
Some(8) | Some(9) => {
let coeffs = [
1.813883936816982644,
0.763163245700557246,
0.761928605321595831,
0.951074653668427927,
1.315180671703161215,
1.928560693477410941,
2.937509342531378755,
4.594894405442878062,
7.330071221881720772,
11.87151259742530180,
19.45851374822937738,
32.20638657246426863,
53.73749198700554656,
90.27388602940998849,
];
Ok(polyeval(m - 0.45, &coeffs))
}
Some(10) | Some(11) => {
let coeffs = [
1.898924910271553526,
0.950521794618244435,
1.151077589959015808,
1.750239106986300540,
2.952676812636875180,
5.285800396121450889,
9.832485716659979747,
18.78714868327559562,
36.61468615273698145,
72.45292395127771801,
145.1079577347069102,
293.4786396308497026,
598.3851815055010179,
1228.420013075863451,
2536.529755382764488,
];
Ok(polyeval(m - 0.55, &coeffs))
}
Some(12) | Some(13) => {
let coeffs = [
2.007598398424376302,
1.248457231212347337,
1.926234657076479729,
3.751289640087587680,
8.119944554932045802,
18.66572130873555361,
44.60392484291437063,
109.5092054309498377,
274.2779548232413480,
697.5598008606326163,
1795.716014500247129,
4668.381716790389910,
12235.76246813664335,
32290.17809718320818,
85713.07608195964685,
228672.1890493117096,
612757.2711915852774,
];
Ok(polyeval(m - 0.65, &coeffs))
}
Some(14) | Some(15) => {
let coeffs = [
2.156515647499643235,
1.791805641849463243,
3.826751287465713147,
10.38672468363797208,
31.40331405468070290,
100.9237039498695416,
337.3268282632272897,
1158.707930567827917,
4060.990742193632092,
14454.00184034344795,
52076.66107599404803,
189493.6591462156887,
695184.5762413896145,
2567994.048255284686,
9541921.966748386322,
35634927.44218076174,
133669298.4612040871,
503352186.6866284541,
1901975729.538660119,
7208915015.330103756,
];
Ok(polyeval(m - 0.75, &coeffs))
}
Some(16) => {
let coeffs = [
2.318122621712510589,
2.616920150291232841,
7.897935075731355823,
30.50239715446672327,
131.4869365523528456,
602.9847637356491617,
2877.024617809972641,
14110.51991915180325,
70621.44088156540229,
358977.2665825309926,
1847238.263723971684,
9600515.416049214109,
50307677.08502366879,
265444188.6527127967,
1408862325.028702687,
7515687935.373774627,
];
Ok(polyeval(m - 0.825, &coeffs))
}
Some(17) => {
let coeffs = [
2.473596173751343912,
3.727624244118099310,
15.60739303554930496,
84.12850842805887747,
506.9818197040613935,
3252.277058145123644,
21713.24241957434256,
149037.0451890932766,
1043999.331089990839,
7427974.817042038995,
53503839.67558661151,
389249886.9948708474,
2855288351.100810619,
21090077038.76684053,
156699833947.7902014,
1170222242422.439893,
8777948323668.937971,
66101242752484.95041,
499488053713388.7989,
37859743397240299.20,
];
Ok(polyeval(m - 0.875, &coeffs))
}
Some(_) => ellipk_precise(m),
None => {
check!(@nan, ellipk, [m]);
if m == neg_inf!() {
return Ok(0.0);
}
#[cfg(not(feature = "test_force_fail"))]
if m > 1.0 {
return Err("ellipk: m must not be greater than 1.");
}
Err("ellipk: Unexpected error.")
}
}
}
#[inline]
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
pub(crate) fn ellipk_precise<T: Float>(m: T) -> Result<T, StrErr> {
if m >= 1.0 {
if m == 1.0 {
return Ok(inf!());
}
return Err("ellipk: m must not be greater than 1.");
}
Ok(ellipk_precise_unchecked(m))
}
#[numeric_literals::replace_float_literals(T::from(literal).unwrap())]
#[inline]
pub fn ellipk_precise_unchecked<T: Float>(m: T) -> T {
declare!(mut [xn = T::one(), yn = (T::one() - m).sqrt(), t]);
for _ in 0..MAX_ITERATION {
if (xn - yn).abs() >= 2.7 * epsilon!() * xn.abs() {
t = (xn * yn).sqrt();
xn = (xn + yn) / 2.0;
yn = t;
continue;
}
break;
}
pi!() / (xn + yn)
}
const MAX_ITERATION: usize = 10;
#[cfg(not(feature = "test_force_fail"))]
#[cfg(test)]
mod tests {
use super::*;
use crate::compare_test_data_boost;
use crate::compare_test_data_wolfram;
#[test]
fn test_ellipk_boost() {
compare_test_data_boost!("ellipk_data.txt", ellipk, 1, f64::EPSILON);
}
#[test]
fn test_ellipk_wolfram() {
compare_test_data_wolfram!("./tests/data/coverage", "ellipk_cov.csv", ellipk, 1, 2e-12);
compare_test_data_wolfram!("ellipk_data.csv", ellipk, 1, 5e-15);
}
#[test]
fn test_ellipk_special_cases() {
use std::f64::{consts::FRAC_PI_2, INFINITY, NAN, NEG_INFINITY};
assert_eq!(ellipk(0.0).unwrap(), FRAC_PI_2);
assert_eq!(ellipk(1.0).unwrap(), INFINITY);
assert!(ellipk(-1.0).unwrap().is_finite());
assert_eq!(ellipk(1.1), Err("ellipk: m must not be greater than 1."));
assert_eq!(ellipk(NAN), Err("ellipk: Arguments cannot be NAN."));
assert_eq!(
ellipk(INFINITY),
Err("ellipk: m must not be greater than 1.")
);
assert_eq!(ellipk(NEG_INFINITY).unwrap(), 0.0);
}
}
#[cfg(feature = "test_force_fail")]
crate::test_force_unreachable! {
assert_eq!(ellipk(f64::INFINITY), Err("ellipk: Unexpected error."));
}