1use crate::common::util::round_p;
8use crate::complex_special::nan_pair;
9use crate::complex_special::pi_c;
10use crate::complex_special::ziv_complex;
11use crate::Consts;
12use crate::Error;
13use crate::ExactComplex;
14use crate::ExactNum;
15use crate::RoundingMode;
16use crate::INF_POS;
17
18const CARLSON_DUPE_MAX: u32 = 128;
20
21fn rm() -> RoundingMode {
22 RoundingMode::None
23}
24
25fn c_u32(n: u32, p: usize) -> ExactComplex {
26 ExactComplex::from_real(ExactNum::from_u32(n, p), p)
27}
28
29fn is_c_zero(z: &ExactComplex) -> bool {
30 z.re().is_zero() && z.im().is_zero()
31}
32
33fn is_c_one(z: &ExactComplex, p: usize) -> bool {
34 z.im().is_zero() && z.re().cmp(&ExactNum::from_u8(1, p)) == Some(0)
35}
36
37fn c_abs(z: &ExactComplex, p: usize) -> ExactNum {
38 z.abs(p, rm())
39}
40
41fn max_abs(a: &ExactNum, b: &ExactNum, _p: usize) -> ExactNum {
42 if matches!(a.cmp(b), Some(c) if c >= 0) {
43 a.clone()
44 } else {
45 b.clone()
46 }
47}
48
49fn tiny_spread(p: usize) -> ExactNum {
50 ExactNum::from_u8(1, p).ldexp(-((p as i32) / 3 + 16), p, rm())
51}
52
53fn close_enough(dev: &ExactNum, an: &ExactComplex, p: usize) -> bool {
54 let one = ExactNum::from_u8(1, p);
55 let scale = max_abs(&c_abs(an, p), &one, p);
56 let thresh = tiny_spread(p).mul(&scale, p, rm());
57 matches!(dev.cmp(&thresh), Some(c) if c < 0)
58}
59
60fn max_dev3(
61 an: &ExactComplex,
62 x: &ExactComplex,
63 y: &ExactComplex,
64 z: &ExactComplex,
65 p: usize,
66) -> ExactNum {
67 let dx = c_abs(&an.sub(x, p, rm()), p);
68 let dy = c_abs(&an.sub(y, p, rm()), p);
69 let dz = c_abs(&an.sub(z, p, rm()), p);
70 max_abs(&max_abs(&dx, &dy, p), &dz, p)
71}
72
73fn any_nan(args: &[&ExactComplex]) -> bool {
74 args.iter().any(|z| z.is_nan())
75}
76
77fn carlson_rc(x: &ExactComplex, y: &ExactComplex, p: usize, cc: &mut Consts) -> ExactComplex {
79 if any_nan(&[x, y]) {
80 return ExactComplex::new(x.re().clone(), y.im().clone());
81 }
82 if is_c_zero(y) {
83 return nan_pair(Error::InvalidArgument);
84 }
85 let dxy = x.sub(y, p, rm());
86 if close_enough(&c_abs(&dxy, p), x, p) {
87 return c_u32(1, p).div(&x.sqrt(p, rm(), cc), p, rm());
88 }
89 if is_c_zero(x) {
90 let mut hp = pi_c(p, cc);
91 hp = hp.div(&c_u32(2, p), p, rm());
92 return hp.div(&y.sqrt(p, rm(), cc), p, rm());
93 }
94 carlson_rf(x, y, y, p, cc)
95}
96
97fn carlson_rf(
99 x0: &ExactComplex,
100 y0: &ExactComplex,
101 z0: &ExactComplex,
102 p: usize,
103 cc: &mut Consts,
104) -> ExactComplex {
105 if any_nan(&[x0, y0, z0]) {
106 return nan_pair(Error::InvalidArgument);
107 }
108 let zeros =
109 usize::from(is_c_zero(x0)) + usize::from(is_c_zero(y0)) + usize::from(is_c_zero(z0));
110 if zeros > 1 {
111 return nan_pair(Error::InvalidArgument);
112 }
113 let four = c_u32(4, p);
114 let three = c_u32(3, p);
115 let mut x = x0.clone();
116 let mut y = y0.clone();
117 let mut z = z0.clone();
118 for _ in 0..CARLSON_DUPE_MAX {
119 let an = x.add(&y, p, rm()).add(&z, p, rm()).div(&three, p, rm());
120 if close_enough(&max_dev3(&an, &x, &y, &z, p), &an, p) {
121 return rf_series(&an, &x, &y, &z, p, cc);
122 }
123 let sx = x.sqrt(p, rm(), cc);
124 let sy = y.sqrt(p, rm(), cc);
125 let sz = z.sqrt(p, rm(), cc);
126 let lam = sx
127 .mul(&sy, p, rm())
128 .add(&sy.mul(&sz, p, rm()), p, rm())
129 .add(&sz.mul(&sx, p, rm()), p, rm());
130 x = x.add(&lam, p, rm()).div(&four, p, rm());
131 y = y.add(&lam, p, rm()).div(&four, p, rm());
132 z = z.add(&lam, p, rm()).div(&four, p, rm());
133 }
134 nan_pair(Error::InvalidArgument)
135}
136
137fn rf_series(
138 an: &ExactComplex,
139 x: &ExactComplex,
140 y: &ExactComplex,
141 z: &ExactComplex,
142 p: usize,
143 cc: &mut Consts,
144) -> ExactComplex {
145 let xx = an.sub(x, p, rm()).div(an, p, rm());
146 let yy = an.sub(y, p, rm()).div(an, p, rm());
147 let zz = an.sub(z, p, rm()).div(an, p, rm());
148 let e2 = xx.mul(&yy, p, rm()).sub(&zz.mul(&zz, p, rm()), p, rm());
149 let e3 = xx.mul(&yy, p, rm()).mul(&zz, p, rm());
150 let e2s = e2.mul(&e2, p, rm());
151 let one = c_u32(1, p);
152 let w = |n: u32| c_u32(n, p);
153 let s = one
154 .sub(&e2.div(&w(10), p, rm()), p, rm())
155 .add(&e3.div(&w(14), p, rm()), p, rm())
156 .add(&e2s.div(&w(24), p, rm()), p, rm())
157 .sub(
158 &w(3)
159 .mul(&e2, p, rm())
160 .mul(&e3, p, rm())
161 .div(&w(44), p, rm()),
162 p,
163 rm(),
164 )
165 .sub(
166 &w(5)
167 .mul(&e2, p, rm())
168 .mul(&e2s, p, rm())
169 .div(&w(208), p, rm()),
170 p,
171 rm(),
172 )
173 .add(
174 &w(3)
175 .mul(&e2s, p, rm())
176 .mul(&e3, p, rm())
177 .div(&w(104), p, rm()),
178 p,
179 rm(),
180 );
181 s.div(&an.sqrt(p, rm(), cc), p, rm())
182}
183
184fn carlson_rd(
186 x0: &ExactComplex,
187 y0: &ExactComplex,
188 z0: &ExactComplex,
189 p: usize,
190 cc: &mut Consts,
191) -> ExactComplex {
192 if any_nan(&[x0, y0, z0]) {
193 return nan_pair(Error::InvalidArgument);
194 }
195 if is_c_zero(z0) || (is_c_zero(x0) && is_c_zero(y0)) {
196 return nan_pair(Error::InvalidArgument);
197 }
198 let four = c_u32(4, p);
199 let three = c_u32(3, p);
200 let five = c_u32(5, p);
201 let mut x = x0.clone();
202 let mut y = y0.clone();
203 let mut z = z0.clone();
204 let mut sum = ExactComplex::zero(p);
205 let mut fac = c_u32(1, p);
206 for _ in 0..CARLSON_DUPE_MAX {
207 let an = x
208 .add(&y, p, rm())
209 .add(&three.mul(&z, p, rm()), p, rm())
210 .div(&five, p, rm());
211 if close_enough(&max_dev3(&an, &x, &y, &z, p), &an, p) {
212 let series = rd_series(&an, &x, &y, &z, p, cc);
213 return three
214 .mul(&sum, p, rm())
215 .add(&fac.mul(&series, p, rm()), p, rm());
216 }
217 let sx = x.sqrt(p, rm(), cc);
218 let sy = y.sqrt(p, rm(), cc);
219 let sz = z.sqrt(p, rm(), cc);
220 let lam = sx
221 .mul(&sy, p, rm())
222 .add(&sy.mul(&sz, p, rm()), p, rm())
223 .add(&sz.mul(&sx, p, rm()), p, rm());
224 sum = sum.add(
225 &fac.div(&sz.mul(&z.add(&lam, p, rm()), p, rm()), p, rm()),
226 p,
227 rm(),
228 );
229 fac = fac.div(&four, p, rm());
230 x = x.add(&lam, p, rm()).div(&four, p, rm());
231 y = y.add(&lam, p, rm()).div(&four, p, rm());
232 z = z.add(&lam, p, rm()).div(&four, p, rm());
233 }
234 nan_pair(Error::InvalidArgument)
235}
236
237fn rd_series(
238 an: &ExactComplex,
239 x: &ExactComplex,
240 y: &ExactComplex,
241 z: &ExactComplex,
242 p: usize,
243 cc: &mut Consts,
244) -> ExactComplex {
245 let xx = an.sub(x, p, rm()).div(an, p, rm());
246 let yy = an.sub(y, p, rm()).div(an, p, rm());
247 let zz = an.sub(z, p, rm()).div(an, p, rm());
248 let e2 = xx.mul(&yy, p, rm()).sub(&zz.mul(&zz, p, rm()), p, rm());
249 let e3 = xx.mul(&yy, p, rm()).mul(&zz, p, rm());
250 let e2s = e2.mul(&e2, p, rm());
251 let one = c_u32(1, p);
252 let w = |n: u32| c_u32(n, p);
253 let s = one
254 .sub(&w(3).mul(&e2, p, rm()).div(&w(14), p, rm()), p, rm())
255 .add(&e3.div(&w(6), p, rm()), p, rm())
256 .add(&w(9).mul(&e2s, p, rm()).div(&w(88), p, rm()), p, rm())
257 .sub(
258 &w(3)
259 .mul(&e2, p, rm())
260 .mul(&e3, p, rm())
261 .div(&w(22), p, rm()),
262 p,
263 rm(),
264 )
265 .add(
266 &w(9)
267 .mul(&e3, p, rm())
268 .mul(&e3, p, rm())
269 .div(&w(52), p, rm()),
270 p,
271 rm(),
272 )
273 .sub(
274 &w(3)
275 .mul(&e2, p, rm())
276 .mul(&e2s, p, rm())
277 .div(&w(26), p, rm()),
278 p,
279 rm(),
280 );
281 s.div(&an.mul(&an.sqrt(p, rm(), cc), p, rm()), p, rm())
282}
283
284fn carlson_rj(
286 x0: &ExactComplex,
287 y0: &ExactComplex,
288 z0: &ExactComplex,
289 p0: &ExactComplex,
290 p: usize,
291 cc: &mut Consts,
292) -> ExactComplex {
293 if any_nan(&[x0, y0, z0, p0]) {
294 return nan_pair(Error::InvalidArgument);
295 }
296 if is_c_zero(p0) {
297 return nan_pair(Error::InvalidArgument);
298 }
299 let zeros =
300 usize::from(is_c_zero(x0)) + usize::from(is_c_zero(y0)) + usize::from(is_c_zero(z0));
301 if zeros > 1 {
302 return nan_pair(Error::InvalidArgument);
303 }
304 let two = c_u32(2, p);
305 let three = c_u32(3, p);
306 let four = c_u32(4, p);
307 let five = c_u32(5, p);
308 let mut x = x0.clone();
309 let mut y = y0.clone();
310 let mut z = z0.clone();
311 let mut pv = p0.clone();
312 let mut sum = ExactComplex::zero(p);
313 let mut fac = c_u32(1, p);
314 for _ in 0..CARLSON_DUPE_MAX {
315 let an = x
316 .add(&y, p, rm())
317 .add(&z, p, rm())
318 .add(&two.mul(&pv, p, rm()), p, rm())
319 .div(&five, p, rm());
320 let d4 = max_dev3(&an, &x, &y, &z, p);
321 let dp = c_abs(&an.sub(&pv, p, rm()), p);
322 let dmax = max_abs(&d4, &dp, p);
323 if close_enough(&dmax, &an, p) {
324 let series = rj_series(&an, &x, &y, &z, &pv, p, cc);
325 return three
326 .mul(&sum, p, rm())
327 .add(&fac.mul(&series, p, rm()), p, rm());
328 }
329 let sx = x.sqrt(p, rm(), cc);
330 let sy = y.sqrt(p, rm(), cc);
331 let sz = z.sqrt(p, rm(), cc);
332 let lam = sx
333 .mul(&sy, p, rm())
334 .add(&sy.mul(&sz, p, rm()), p, rm())
335 .add(&sz.mul(&sx, p, rm()), p, rm());
336 let alpha = pv
337 .mul(&sx.add(&sy, p, rm()).add(&sz, p, rm()), p, rm())
338 .add(&sx.mul(&sy, p, rm()).mul(&sz, p, rm()), p, rm());
339 let alpha = alpha.mul(&alpha, p, rm());
340 let pl = pv.add(&lam, p, rm());
341 let beta = pv.mul(&pl, p, rm()).mul(&pl, p, rm());
342 sum = sum.add(
343 &fac.mul(&carlson_rc(&alpha, &beta, p, cc), p, rm()),
344 p,
345 rm(),
346 );
347 fac = fac.div(&four, p, rm());
348 x = x.add(&lam, p, rm()).div(&four, p, rm());
349 y = y.add(&lam, p, rm()).div(&four, p, rm());
350 z = z.add(&lam, p, rm()).div(&four, p, rm());
351 pv = pv.add(&lam, p, rm()).div(&four, p, rm());
352 }
353 nan_pair(Error::InvalidArgument)
354}
355
356fn rj_series(
357 an: &ExactComplex,
358 x: &ExactComplex,
359 y: &ExactComplex,
360 z: &ExactComplex,
361 pv: &ExactComplex,
362 p: usize,
363 cc: &mut Consts,
364) -> ExactComplex {
365 let xx = an.sub(x, p, rm()).div(an, p, rm());
366 let yy = an.sub(y, p, rm()).div(an, p, rm());
367 let zz = an.sub(z, p, rm()).div(an, p, rm());
368 let pp = an.sub(pv, p, rm()).div(an, p, rm());
369 let xyz = xx.mul(&yy, p, rm()).mul(&zz, p, rm());
370 let xy_xz_yz = xx
371 .mul(&yy, p, rm())
372 .add(&xx.mul(&zz, p, rm()), p, rm())
373 .add(&yy.mul(&zz, p, rm()), p, rm());
374 let p2 = pp.mul(&pp, p, rm());
375 let p3 = p2.mul(&pp, p, rm());
376 let two = c_u32(2, p);
377 let three = c_u32(3, p);
378 let e2 = xy_xz_yz.sub(&three.mul(&p2, p, rm()), p, rm());
379 let e3 = xyz
380 .add(&two.mul(&p3, p, rm()), p, rm())
381 .sub(&pp.mul(&xy_xz_yz, p, rm()), p, rm());
382 let e2s = e2.mul(&e2, p, rm());
383 let one = c_u32(1, p);
384 let w = |n: u32| c_u32(n, p);
385 let s = one
386 .sub(&w(3).mul(&e2, p, rm()).div(&w(14), p, rm()), p, rm())
387 .add(&e3.div(&w(6), p, rm()), p, rm())
388 .add(&w(9).mul(&e2s, p, rm()).div(&w(88), p, rm()), p, rm())
389 .sub(
390 &w(3)
391 .mul(&e2, p, rm())
392 .mul(&e3, p, rm())
393 .div(&w(22), p, rm()),
394 p,
395 rm(),
396 )
397 .add(
398 &w(9)
399 .mul(&e3, p, rm())
400 .mul(&e3, p, rm())
401 .div(&w(52), p, rm()),
402 p,
403 rm(),
404 )
405 .sub(
406 &w(3)
407 .mul(&e2, p, rm())
408 .mul(&e2s, p, rm())
409 .div(&w(26), p, rm()),
410 p,
411 rm(),
412 );
413 s.div(&an.mul(&an.sqrt(p, rm(), cc), p, rm()), p, rm())
414}
415
416impl ExactComplex {
417 pub fn elliptic_k(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
425 if self.is_nan() {
426 return ExactComplex::new(self.re().clone(), self.im().clone());
427 }
428 let dest = round_p(p);
429 if is_c_one(self, dest) {
430 return ExactComplex::from_real(INF_POS.clone(), dest);
431 }
432 if is_c_zero(self) {
433 let hp = pi_c(dest, cc);
434 return hp.div(&c_u32(2, dest), dest, rm);
435 }
436 ziv_complex(dest, rm, |pw| self.elliptic_k_at(pw, cc))
437 }
438
439 fn elliptic_k_at(&self, p: usize, cc: &mut Consts) -> Self {
440 let zero = ExactComplex::zero(p);
441 let one = ExactComplex::one(p);
442 let om = one.sub(self, p, rm());
443 carlson_rf(&zero, &om, &one, p, cc)
444 }
445
446 pub fn elliptic_e_complete(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
454 if self.is_nan() {
455 return ExactComplex::new(self.re().clone(), self.im().clone());
456 }
457 let dest = round_p(p);
458 if is_c_zero(self) {
459 let hp = pi_c(dest, cc);
460 return hp.div(&c_u32(2, dest), dest, rm);
461 }
462 if is_c_one(self, dest) {
463 return ExactComplex::one(dest);
464 }
465 ziv_complex(dest, rm, |pw| self.elliptic_e_complete_at(pw, cc))
466 }
467
468 fn elliptic_e_complete_at(&self, p: usize, cc: &mut Consts) -> Self {
469 let zero = ExactComplex::zero(p);
470 let one = ExactComplex::one(p);
471 let three = c_u32(3, p);
472 let om = one.sub(self, p, rm());
473 let rf = carlson_rf(&zero, &om, &one, p, cc);
474 let rd = carlson_rd(&zero, &om, &one, p, cc);
475 rf.sub(&self.div(&three, p, rm()).mul(&rd, p, rm()), p, rm())
476 }
477
478 pub fn elliptic_f(&self, m: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
488 if self.is_nan() || m.is_nan() {
489 return nan_pair(Error::InvalidArgument);
490 }
491 let dest = round_p(p);
492 if is_c_zero(self) {
493 return ExactComplex::zero(dest);
494 }
495 if is_c_zero(m) {
496 return self.asin(dest, rm, cc);
497 }
498 if is_c_one(m, dest) {
499 return self.atanh(dest, rm, cc);
500 }
501 ziv_complex(dest, rm, |pw| self.elliptic_f_at(m, pw, cc))
502 }
503
504 fn elliptic_f_at(&self, m: &Self, p: usize, cc: &mut Consts) -> Self {
505 let one = ExactComplex::one(p);
506 let x2 = self.mul(self, p, rm());
507 let a = one.sub(&x2, p, rm());
508 let b = one.sub(&m.mul(&x2, p, rm()), p, rm());
509 let rf = carlson_rf(&a, &b, &one, p, cc);
510 self.mul(&rf, p, rm())
511 }
512
513 pub fn elliptic_e(&self, m: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
521 if self.is_nan() || m.is_nan() {
522 return nan_pair(Error::InvalidArgument);
523 }
524 let dest = round_p(p);
525 if is_c_zero(self) {
526 return ExactComplex::zero(dest);
527 }
528 if is_c_zero(m) {
529 return self.asin(dest, rm, cc);
530 }
531 if is_c_one(m, dest) {
532 return self.clone();
533 }
534 ziv_complex(dest, rm, |pw| self.elliptic_e_at(m, pw, cc))
535 }
536
537 fn elliptic_e_at(&self, m: &Self, p: usize, cc: &mut Consts) -> Self {
538 let one = ExactComplex::one(p);
539 let three = c_u32(3, p);
540 let x2 = self.mul(self, p, rm());
541 let a = one.sub(&x2, p, rm());
542 let b = one.sub(&m.mul(&x2, p, rm()), p, rm());
543 let rf = carlson_rf(&a, &b, &one, p, cc);
544 let rd = carlson_rd(&a, &b, &one, p, cc);
545 self.mul(&rf, p, rm()).sub(
546 &m.mul(self, p, rm())
547 .mul(&x2, p, rm())
548 .div(&three, p, rm())
549 .mul(&rd, p, rm()),
550 p,
551 rm(),
552 )
553 }
554
555 pub fn elliptic_pi_complete(
562 &self,
563 m: &Self,
564 p: usize,
565 rm: RoundingMode,
566 cc: &mut Consts,
567 ) -> Self {
568 if self.is_nan() || m.is_nan() {
569 return nan_pair(Error::InvalidArgument);
570 }
571 let dest = round_p(p);
572 if is_c_one(self, dest) {
573 return nan_pair(Error::InvalidArgument);
574 }
575 if is_c_zero(self) {
576 return m.elliptic_k(dest, rm, cc);
577 }
578 ziv_complex(dest, rm, |pw| self.elliptic_pi_complete_at(m, pw, cc))
579 }
580
581 fn elliptic_pi_complete_at(&self, m: &Self, p: usize, cc: &mut Consts) -> Self {
582 let zero = ExactComplex::zero(p);
583 let one = ExactComplex::one(p);
584 let three = c_u32(3, p);
585 let om = one.sub(m, p, rm());
586 let on = one.sub(self, p, rm());
587 let rf = carlson_rf(&zero, &om, &one, p, cc);
588 let rj = carlson_rj(&zero, &om, &one, &on, p, cc);
589 rf.add(&self.div(&three, p, rm()).mul(&rj, p, rm()), p, rm())
590 }
591
592 pub fn elliptic_pi(
602 &self,
603 x: &Self,
604 m: &Self,
605 p: usize,
606 rm: RoundingMode,
607 cc: &mut Consts,
608 ) -> Self {
609 if self.is_nan() || x.is_nan() || m.is_nan() {
610 return nan_pair(Error::InvalidArgument);
611 }
612 let dest = round_p(p);
613 if is_c_zero(x) {
614 return ExactComplex::zero(dest);
615 }
616 if is_c_zero(self) {
617 return x.elliptic_f(m, dest, rm, cc);
618 }
619 ziv_complex(dest, rm, |pw| self.elliptic_pi_at(x, m, pw, cc))
620 }
621
622 fn elliptic_pi_at(&self, x: &Self, m: &Self, p: usize, cc: &mut Consts) -> Self {
623 let one = ExactComplex::one(p);
624 let three = c_u32(3, p);
625 let x2 = x.mul(x, p, rm());
626 let a = one.sub(&x2, p, rm());
627 let b = one.sub(&m.mul(&x2, p, rm()), p, rm());
628 let pv = one.sub(&self.mul(&x2, p, rm()), p, rm());
629 let rf = carlson_rf(&a, &b, &one, p, cc);
630 let rj = carlson_rj(&a, &b, &one, &pv, p, cc);
631 x.mul(&rf, p, rm()).add(
632 &self
633 .mul(x, p, rm())
634 .mul(&x2, p, rm())
635 .div(&three, p, rm())
636 .mul(&rj, p, rm()),
637 p,
638 rm(),
639 )
640 }
641}
642
643#[cfg(test)]
644mod tests {
645 use super::*;
646
647 fn near(a: &ExactNum, b: &ExactNum, p: usize, slack: i32) -> bool {
648 let d = a.sub(b, p, RoundingMode::None).abs();
649 d.is_zero() || d.exponent().unwrap_or(0) < -((p as i32) - slack)
650 }
651
652 fn tiny(x: &ExactNum, p: usize) -> bool {
653 x.is_zero() || x.exponent().unwrap_or(0) < -((p as i32) / 4)
654 }
655
656 #[test]
657 fn complex_elliptic_golds() {
658 let p = 256;
659 let r = RoundingMode::ToEven;
660 let mut cc = Consts::new().unwrap();
661 let z0 = ExactComplex::zero(p);
662 let half = ExactNum::from_u8(1, p).div(&ExactNum::from_u8(2, p), p, r);
663 let mh = ExactComplex::from_real(half.clone(), p);
664
665 let k0 = z0.elliptic_k(p, r, &mut cc);
666 let mut hp = cc.pi(p, r);
667 hp = hp.div(&ExactNum::from_u8(2, p), p, r);
668 assert!(near(k0.re(), &hp, p, 40), "K(0) re");
669 assert!(tiny(k0.im(), p), "K(0) im");
670
671 let e0 = z0.elliptic_e_complete(p, r, &mut cc);
672 assert!(near(e0.re(), &hp, p, 40), "E(0) re");
673 assert!(tiny(e0.im(), p), "E(0) im");
674
675 let k_real = half.elliptic_k(p, r, &mut cc);
676 let k_c = mh.elliptic_k(p, r, &mut cc);
677 assert!(near(k_c.re(), &k_real, p, 40), "K(1/2) matches real");
678 assert!(tiny(k_c.im(), p), "K(1/2) im");
679
680 let e1 = ExactComplex::one(p).elliptic_e_complete(p, r, &mut cc);
681 assert!(near(e1.re(), &ExactNum::from_u8(1, p), p, 40), "E(1)");
682 assert!(tiny(e1.im(), p));
683
684 let k1 = ExactComplex::one(p).elliptic_k(p, r, &mut cc);
685 assert!(k1.re().is_inf_pos(), "K(1)=+∞");
686
687 let x = mh.clone();
688 let f0 = x.elliptic_f(&z0, p, r, &mut cc);
689 let asin = x.asin(p, r, &mut cc);
690 assert!(near(f0.re(), asin.re(), p, 8), "F(x,0)=arcsin");
691 assert!(tiny(f0.im(), p) && tiny(asin.im(), p));
692
693 let n0 = z0.elliptic_pi_complete(&mh, p, r, &mut cc);
694 assert!(near(n0.re(), k_c.re(), p, 40), "Π(0,m)=K(m)");
695
696 let m = ExactComplex::new(
698 ExactNum::from_u8(3, p).div(&ExactNum::from_u8(10, p), p, r),
699 ExactNum::from_u8(1, p).div(&ExactNum::from_u8(10, p), p, r),
700 );
701 let mp = ExactComplex::one(p).sub(&m, p, r);
702 let km = m.elliptic_k(p, r, &mut cc);
703 let kp = mp.elliptic_k(p, r, &mut cc);
704 let em = m.elliptic_e_complete(p, r, &mut cc);
705 let ep = mp.elliptic_e_complete(p, r, &mut cc);
706 let lhs = em
707 .mul(&kp, p, r)
708 .add(&ep.mul(&km, p, r), p, r)
709 .sub(&km.mul(&kp, p, r), p, r);
710 let want = ExactComplex::from_real(hp, p);
711 assert!(
712 near(lhs.re(), want.re(), p, 30),
713 "Legendre re {:?}",
714 lhs.re()
715 );
716 assert!(
717 tiny(lhs.im(), p) || near(lhs.im(), want.im(), p, 30),
718 "Legendre im"
719 );
720
721 let two = ExactNum::from_u8(2, p);
723 let eps = ExactNum::from_u8(1, p).ldexp(-40, p, RoundingMode::None);
724 let above = ExactComplex::new(two.clone(), eps.clone());
725 let below = ExactComplex::new(two, eps.neg());
726 let ka = above.elliptic_k(p, r, &mut cc);
727 let kb = below.elliptic_k(p, r, &mut cc);
728 assert!(near(ka.re(), kb.re(), p, 20), "K cut Re");
729 assert!(ka.im().is_positive() != kb.im().is_positive() || !tiny(ka.im(), p));
730 assert!(near(&ka.im().abs(), &kb.im().abs(), p, 20), "K cut |Im|");
731 assert_ne!(ka.im().cmp(kb.im()), Some(0));
732
733 let three_half = ExactNum::from_u8(3, p).div(&ExactNum::from_u8(2, p), p, r);
735 let xa = ExactComplex::new(three_half.clone(), eps.clone());
736 let xb = ExactComplex::new(three_half, eps.neg());
737 let fa = xa.elliptic_f(&mh, p, r, &mut cc);
738 let fb = xb.elliptic_f(&mh, p, r, &mut cc);
739 assert!(!fa.is_nan() && !fb.is_nan(), "F cut defined");
740 assert_ne!(fa.im().cmp(fb.im()), Some(0), "F cut Im differs");
741 }
742}