1use crate::common::util::bump_prec_retry;
7use crate::common::util::round_p;
8use crate::Consts;
9use crate::Error;
10use crate::ExactComplex;
11use crate::ExactNum;
12use crate::RoundingMode;
13use crate::WORD_BIT_SIZE;
14use alloc::vec::Vec;
15
16const FADDEEVA_SERIES_L1: u32 = 8;
19
20const GAMMA_STIRLING_TERMS: usize = 64;
22
23const GAMMA_STIRLING_MIN_ABS_EXP: i32 = 6;
26
27const DIGAMMA_STIRLING_MIN_ABS_EXP: i32 = 8;
29
30const GAMMA_FACTORIAL_MAX: u32 = 64;
33
34pub(crate) fn nan_pair(e: Error) -> ExactComplex {
35 ExactComplex::new(ExactNum::nan(Some(e)), ExactNum::nan(Some(e)))
36}
37
38pub(crate) fn neg_c(z: &ExactComplex) -> ExactComplex {
39 ExactComplex::new(z.re().neg(), z.im().neg())
40}
41
42pub(crate) fn two_c(p: usize) -> ExactComplex {
43 ExactComplex::from_real(ExactNum::from_u8(2, p), p)
44}
45
46pub(crate) fn half_c(p: usize) -> ExactComplex {
47 let h = ExactNum::from_u8(1, p).div(&ExactNum::from_u8(2, p), p, RoundingMode::None);
48 ExactComplex::from_real(h, p)
49}
50
51pub(crate) fn pi_c(p: usize, cc: &mut Consts) -> ExactComplex {
52 ExactComplex::from_real(cc.pi(p, RoundingMode::None), p)
53}
54
55pub(crate) fn term_negligible(t: &ExactComplex, p: usize) -> bool {
56 let m = t.abs(p, RoundingMode::None);
57 m.is_zero()
58 || m.exponent()
59 .is_some_and(|e| (e as isize) + (p as isize) < 0)
60}
61
62fn l1_below_series_bound(z: &ExactComplex, p: usize) -> bool {
63 let s = z.re().abs().add(&z.im().abs(), p, RoundingMode::None);
64 let bound = ExactNum::from_u32(FADDEEVA_SERIES_L1, p);
65 matches!(s.cmp(&bound), Some(c) if c < 0)
66}
67
68fn use_faddeeva_series(z: &ExactComplex, p: usize) -> bool {
71 if l1_below_series_bound(z, p) {
72 return true;
73 }
74 let az = z.abs(p, RoundingMode::None);
75 let az2 = az.mul(&az, p, RoundingMode::None);
76 let thresh = ExactNum::from_u32(p.min(u32::MAX as usize) as u32, p);
77 matches!(az2.cmp(&thresh), Some(c) if c < 0)
78}
79
80fn re_positive(z: &ExactComplex) -> bool {
81 z.re().is_positive() && !z.re().is_zero()
82}
83
84fn im_negative_or_neg_real(z: &ExactComplex) -> bool {
85 z.im().is_negative() || (z.im().is_zero() && z.re().is_negative())
86}
87
88pub(crate) fn is_nonpos_integer(z: &ExactComplex) -> bool {
89 z.im().is_zero() && z.re().is_int() && (z.re().is_zero() || z.re().is_negative())
90}
91
92fn abs_needs_shift(z: &ExactComplex, p: usize, min_exp: i32) -> bool {
93 match z.abs(p, RoundingMode::None).exponent() {
94 Some(e) => e < min_exp,
95 None => false,
96 }
97}
98
99pub(crate) fn series_term_cap(p: usize) -> usize {
100 p.saturating_add(WORD_BIT_SIZE)
101}
102
103pub(crate) fn ziv_complex<F>(p: usize, rm: RoundingMode, mut compute: F) -> ExactComplex
104where
105 F: FnMut(usize) -> ExactComplex,
106{
107 let p = round_p(p);
108 let mut p_inc = WORD_BIT_SIZE;
109 let Some(mut p_wrk) = p.checked_add(p_inc) else {
110 return nan_pair(Error::InvalidArgument);
111 };
112 p_wrk = round_p(p_wrk);
113 loop {
114 let p_x = match p_wrk.checked_add(WORD_BIT_SIZE.saturating_mul(2)) {
115 Some(v) => v,
116 None => return nan_pair(Error::InvalidArgument),
117 };
118 let z = compute(p_x);
119 let mut re = z.re().clone();
120 let mut im = z.im().clone();
121 let ok_re = re.try_set_precision(p, rm, p_wrk);
122 let ok_im = im.try_set_precision(p, rm, p_wrk);
123 if ok_re && ok_im {
124 return ExactComplex::new(re, im);
125 }
126 if bump_prec_retry(&mut p_wrk, &mut p_inc, p).is_err() {
127 return nan_pair(Error::PrecisionRetryExhausted);
128 }
129 }
130}
131
132fn even_bernoulli_numbers(kmax: usize, p: usize) -> Vec<ExactNum> {
134 let m = 2 * kmax;
135 let mut a: Vec<ExactNum> = Vec::new();
136 for i in 0..=m {
137 let num = ExactNum::from_u8(1, p);
138 let den = ExactNum::from_u32((i + 1) as u32, p);
139 a.push(num.div(&den, p, RoundingMode::None));
140 }
141 let mut evens = Vec::new();
142 for j in 1..=m {
143 for i in 0..=(m - j) {
144 let diff = a[i].sub(&a[i + 1], p, RoundingMode::None);
145 let fac = ExactNum::from_u32((i + 1) as u32, p);
146 a[i] = fac.mul(&diff, p, RoundingMode::None);
147 }
148 if j % 2 == 0 {
149 evens.push(a[0].clone());
150 }
151 }
152 evens
153}
154
155fn factorial_um1(n: u32, p: usize) -> ExactComplex {
156 let mut acc = ExactNum::from_u8(1, p);
157 if n >= 2 {
158 for k in 2..n {
159 acc = acc.mul(&ExactNum::from_u32(k, p), p, RoundingMode::None);
160 }
161 }
162 ExactComplex::from_real(acc, p)
163}
164
165impl ExactComplex {
166 pub fn erf(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
174 if self.is_nan() {
175 return ExactComplex::new(self.re().clone(), self.im().clone());
176 }
177 let dest = round_p(p);
178 ziv_complex(dest, rm, |pw| self.erf_at(pw, dest, cc))
179 }
180
181 pub fn erfc(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
190 if self.is_nan() {
191 return ExactComplex::new(self.re().clone(), self.im().clone());
192 }
193 let dest = round_p(p);
194 ziv_complex(dest, rm, |pw| self.erfc_at(pw, dest, cc))
195 }
196
197 pub fn gamma(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
205 if self.is_nan() {
206 return ExactComplex::new(self.re().clone(), self.im().clone());
207 }
208 if is_nonpos_integer(self) {
209 return nan_pair(Error::InvalidArgument);
210 }
211 ziv_complex(round_p(p), rm, |pw| self.gamma_at(pw, cc))
212 }
213
214 pub fn ln_gamma(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
223 if self.is_nan() {
224 return ExactComplex::new(self.re().clone(), self.im().clone());
225 }
226 if is_nonpos_integer(self) {
227 return nan_pair(Error::InvalidArgument);
228 }
229 ziv_complex(round_p(p), rm, |pw| self.ln_gamma_at(pw, cc))
230 }
231
232 pub fn digamma(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
240 if self.is_nan() {
241 return ExactComplex::new(self.re().clone(), self.im().clone());
242 }
243 if is_nonpos_integer(self) {
244 return nan_pair(Error::InvalidArgument);
245 }
246 ziv_complex(round_p(p), rm, |pw| self.digamma_at(pw, cc))
247 }
248
249 fn erf_at(&self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
250 if use_faddeeva_series(self, dest_p) {
251 Self::erf_power_series(self, work_p, cc)
252 } else {
253 ExactComplex::one(work_p).sub(
254 &self.erfc_via_faddeeva(work_p, dest_p, cc),
255 work_p,
256 RoundingMode::None,
257 )
258 }
259 }
260
261 fn erfc_at(&self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
262 if use_faddeeva_series(self, dest_p) {
263 ExactComplex::one(work_p).sub(
264 &Self::erf_power_series(self, work_p, cc),
265 work_p,
266 RoundingMode::None,
267 )
268 } else {
269 self.erfc_via_faddeeva(work_p, dest_p, cc)
270 }
271 }
272
273 fn erfc_via_faddeeva(&self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
275 let z2 = self.mul(self, work_p, RoundingMode::None);
276 let em = neg_c(&z2).exp(work_p, RoundingMode::None, cc);
277 let iz = ExactComplex::i(work_p).mul(self, work_p, RoundingMode::None);
278 if re_positive(self) {
279 em.mul(
280 &Self::faddeeva(&iz, work_p, dest_p, cc),
281 work_p,
282 RoundingMode::None,
283 )
284 } else {
285 two_c(work_p).sub(
286 &em.mul(
287 &Self::faddeeva(&neg_c(&iz), work_p, dest_p, cc),
288 work_p,
289 RoundingMode::None,
290 ),
291 work_p,
292 RoundingMode::None,
293 )
294 }
295 }
296
297 fn faddeeva(z: &Self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
299 if im_negative_or_neg_real(z) {
300 let z2 = z.mul(z, work_p, RoundingMode::None);
301 let two_exp = two_c(work_p).mul(
302 &neg_c(&z2).exp(work_p, RoundingMode::None, cc),
303 work_p,
304 RoundingMode::None,
305 );
306 return two_exp.sub(
307 &Self::faddeeva_upper(&neg_c(z), work_p, dest_p, cc),
308 work_p,
309 RoundingMode::None,
310 );
311 }
312 Self::faddeeva_upper(z, work_p, dest_p, cc)
313 }
314
315 fn faddeeva_upper(z: &Self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
316 if use_faddeeva_series(z, dest_p) {
317 Self::faddeeva_series(z, work_p, cc)
318 } else {
319 Self::faddeeva_asymp(z, work_p, cc)
320 }
321 }
322
323 fn faddeeva_series(z: &Self, p: usize, cc: &mut Consts) -> Self {
325 let u = neg_c(&ExactComplex::i(p)).mul(z, p, RoundingMode::None);
326 let erf_u = Self::erf_power_series(&u, p, cc);
327 let erfc_u = ExactComplex::one(p).sub(&erf_u, p, RoundingMode::None);
328 let z2 = z.mul(z, p, RoundingMode::None);
329 neg_c(&z2)
330 .exp(p, RoundingMode::None, cc)
331 .mul(&erfc_u, p, RoundingMode::None)
332 }
333
334 fn erf_power_series(u: &Self, p: usize, cc: &mut Consts) -> Self {
336 let pi = cc.pi(p, RoundingMode::None);
337 let sqrt_pi = pi.sqrt(p, RoundingMode::None);
338 let scale = ExactNum::from_u8(2, p).div(&sqrt_pi, p, RoundingMode::None);
339 let u2 = u.mul(u, p, RoundingMode::None);
340 let mut upow = u.clone();
341 let mut nfact = ExactNum::from_u8(1, p);
342 let mut sum = u.clone();
343 for n in 1..=series_term_cap(p) {
344 nfact = nfact.mul(&ExactNum::from_u32(n as u32, p), p, RoundingMode::None);
345 upow = upow.mul(&u2, p, RoundingMode::None);
346 let two_n_1 = ExactNum::from_u32((2 * n + 1) as u32, p);
347 let den = nfact.mul(&two_n_1, p, RoundingMode::None);
348 let mut t = upow.div(&ExactComplex::from_real(den, p), p, RoundingMode::None);
349 if n % 2 == 1 {
350 t = neg_c(&t);
351 }
352 sum = sum.add(&t, p, RoundingMode::None);
353 if term_negligible(&t, p) {
354 break;
355 }
356 }
357 ExactComplex::from_real(scale, p).mul(&sum, p, RoundingMode::None)
358 }
359
360 fn faddeeva_asymp(z: &Self, p: usize, cc: &mut Consts) -> Self {
362 let z2 = z.mul(z, p, RoundingMode::None);
363 let two_z2 = two_c(p).mul(&z2, p, RoundingMode::None);
364 let mut term = ExactComplex::one(p);
365 let mut s = ExactComplex::one(p);
366 let mut prev_e = i32::MIN;
367 for m in 1..=series_term_cap(p) {
368 let odd = ExactComplex::from_real(ExactNum::from_u32((2 * m - 1) as u32, p), p);
369 term = term
370 .mul(&odd, p, RoundingMode::None)
371 .div(&two_z2, p, RoundingMode::None);
372 s = s.add(&term, p, RoundingMode::None);
373 let e = term
374 .abs(p, RoundingMode::None)
375 .exponent()
376 .unwrap_or(i32::MIN);
377 if term_negligible(&term, p) {
378 break;
379 }
380 if m > 1 && e > prev_e {
381 break;
382 }
383 prev_e = e;
384 }
385 let sqrt_pi =
386 ExactComplex::from_real(cc.pi(p, RoundingMode::None).sqrt(p, RoundingMode::None), p);
387 let den = z.mul(&sqrt_pi, p, RoundingMode::None);
388 ExactComplex::i(p)
389 .div(&den, p, RoundingMode::None)
390 .mul(&s, p, RoundingMode::None)
391 }
392
393 pub(crate) fn gamma_at(&self, p: usize, cc: &mut Consts) -> Self {
394 if let Some(n) = self.small_pos_int(p) {
395 return factorial_um1(n, p);
396 }
397 if !re_positive(self) && !self.re().is_zero() {
398 let pi = pi_c(p, cc);
399 let piz = pi.mul(self, p, RoundingMode::None);
400 let s = piz.sin(p, RoundingMode::None, cc);
401 let omz = ExactComplex::one(p).sub(self, p, RoundingMode::None);
402 let g = omz.gamma_positive(p, cc);
403 return pi.div(&s.mul(&g, p, RoundingMode::None), p, RoundingMode::None);
404 }
405 self.gamma_positive(p, cc)
406 }
407
408 fn small_pos_int(&self, p: usize) -> Option<u32> {
409 if !self.im().is_zero() || !self.re().is_int() || !self.re().is_positive() {
410 return None;
411 }
412 for n in 1u32..=GAMMA_FACTORIAL_MAX {
413 let w = ExactNum::from_u32(n, p);
414 if self.re().cmp(&w) == Some(0) {
415 return Some(n);
416 }
417 }
418 None
419 }
420
421 fn gamma_positive(&self, p: usize, cc: &mut Consts) -> Self {
422 let one = ExactComplex::one(p);
423 let mut z = self.clone();
424 let mut acc = ExactComplex::one(p);
425 while abs_needs_shift(&z, p, GAMMA_STIRLING_MIN_ABS_EXP) {
426 acc = acc.mul(&z, p, RoundingMode::None);
427 z = z.add(&one, p, RoundingMode::None);
428 }
429 let lg = z.ln_gamma_stirling(p, cc);
430 let g = lg.exp(p, RoundingMode::None, cc);
431 g.div(&acc, p, RoundingMode::None)
432 }
433
434 fn ln_gamma_at(&self, p: usize, cc: &mut Consts) -> Self {
435 if let Some(n) = self.small_pos_int(p) {
436 return factorial_um1(n, p).ln(p, RoundingMode::None, cc);
437 }
438 if !re_positive(self) && !self.re().is_zero() {
439 let piz = pi_c(p, cc).mul(self, p, RoundingMode::None);
440 let ln_sin = piz
441 .sin(p, RoundingMode::None, cc)
442 .ln(p, RoundingMode::None, cc);
443 let omz = ExactComplex::one(p).sub(self, p, RoundingMode::None);
444 return pi_c(p, cc)
445 .ln(p, RoundingMode::None, cc)
446 .sub(&ln_sin, p, RoundingMode::None)
447 .sub(&omz.ln_gamma_positive(p, cc), p, RoundingMode::None);
448 }
449 self.ln_gamma_positive(p, cc)
450 }
451
452 fn ln_gamma_positive(&self, p: usize, cc: &mut Consts) -> Self {
453 let one = ExactComplex::one(p);
454 let mut z = self.clone();
455 let mut ln_acc = ExactComplex::zero(p);
456 while abs_needs_shift(&z, p, GAMMA_STIRLING_MIN_ABS_EXP) {
457 ln_acc = ln_acc.add(&z.ln(p, RoundingMode::None, cc), p, RoundingMode::None);
458 z = z.add(&one, p, RoundingMode::None);
459 }
460 z.ln_gamma_stirling(p, cc)
461 .sub(&ln_acc, p, RoundingMode::None)
462 }
463
464 fn ln_gamma_stirling(&self, p: usize, cc: &mut Consts) -> Self {
465 let ln_z = self.ln(p, RoundingMode::None, cc);
466 let zmh = self.sub(&half_c(p), p, RoundingMode::None);
467 let mut s = zmh.mul(&ln_z, p, RoundingMode::None);
468 s = s.sub(self, p, RoundingMode::None);
469 let two_pi = two_c(p).mul(&pi_c(p, cc), p, RoundingMode::None);
470 let ln_two_pi = two_pi.ln(p, RoundingMode::None, cc);
471 s = s.add(
472 &ln_two_pi.mul(&half_c(p), p, RoundingMode::None),
473 p,
474 RoundingMode::None,
475 );
476 let bs = even_bernoulli_numbers(GAMMA_STIRLING_TERMS, p);
477 let mut zpow = self.clone();
478 let mut prev_e = i32::MIN;
479 for (k, b) in bs.iter().enumerate() {
480 let k = k + 1;
481 let two_k = ExactNum::from_u32((2 * k) as u32, p);
482 let two_k_m1 = ExactNum::from_u32((2 * k - 1) as u32, p);
483 let den_r = two_k.mul(&two_k_m1, p, RoundingMode::None);
484 let den = ExactComplex::from_real(den_r, p).mul(&zpow, p, RoundingMode::None);
485 let term = ExactComplex::from_real(b.clone(), p).div(&den, p, RoundingMode::None);
486 s = s.add(&term, p, RoundingMode::None);
487 let e = term
488 .abs(p, RoundingMode::None)
489 .exponent()
490 .unwrap_or(i32::MIN);
491 if term_negligible(&term, p) {
492 break;
493 }
494 if k > 2 && e > prev_e {
495 break;
496 }
497 prev_e = e;
498 zpow = zpow
499 .mul(self, p, RoundingMode::None)
500 .mul(self, p, RoundingMode::None);
501 }
502 s
503 }
504
505 fn digamma_at(&self, p: usize, cc: &mut Consts) -> Self {
506 if !re_positive(self) && !self.re().is_zero() {
507 let one = ExactComplex::one(p);
508 let omz = one.sub(self, p, RoundingMode::None);
509 let psi = omz.digamma_positive(p, cc);
510 let piz = pi_c(p, cc).mul(self, p, RoundingMode::None);
511 let cot = piz.cos(p, RoundingMode::None, cc).div(
512 &piz.sin(p, RoundingMode::None, cc),
513 p,
514 RoundingMode::None,
515 );
516 return psi.sub(
517 &pi_c(p, cc).mul(&cot, p, RoundingMode::None),
518 p,
519 RoundingMode::None,
520 );
521 }
522 self.digamma_positive(p, cc)
523 }
524
525 fn digamma_positive(&self, p: usize, cc: &mut Consts) -> Self {
526 let one = ExactComplex::one(p);
527 let mut z = self.clone();
528 let mut acc = ExactComplex::zero(p);
529 while abs_needs_shift(&z, p, DIGAMMA_STIRLING_MIN_ABS_EXP) {
530 let rec = one.div(&z, p, RoundingMode::None);
531 acc = acc.sub(&rec, p, RoundingMode::None);
532 z = z.add(&one, p, RoundingMode::None);
533 }
534 acc.add(&z.digamma_asymp(p, cc), p, RoundingMode::None)
535 }
536
537 fn digamma_asymp(&self, p: usize, cc: &mut Consts) -> Self {
538 let ln_z = self.ln(p, RoundingMode::None, cc);
539 let two_z = two_c(p).mul(self, p, RoundingMode::None);
540 let half_inv = ExactComplex::one(p).div(&two_z, p, RoundingMode::None);
541 let mut s = ln_z.sub(&half_inv, p, RoundingMode::None);
542 let z2 = self.mul(self, p, RoundingMode::None);
543 let mut zp = ExactComplex::one(p);
544 let bs = even_bernoulli_numbers(GAMMA_STIRLING_TERMS, p);
545 let mut prev_e = i32::MIN;
546 for (k, b) in bs.iter().enumerate() {
547 let k = k + 1;
548 zp = zp.mul(&z2, p, RoundingMode::None);
549 let two_k = ExactComplex::from_real(ExactNum::from_u32((2 * k) as u32, p), p);
550 let den = two_k.mul(&zp, p, RoundingMode::None);
551 let term = ExactComplex::from_real(b.clone(), p).div(&den, p, RoundingMode::None);
552 s = s.sub(&term, p, RoundingMode::None);
553 let e = term
554 .abs(p, RoundingMode::None)
555 .exponent()
556 .unwrap_or(i32::MIN);
557 if term_negligible(&term, p) {
558 break;
559 }
560 if k > 2 && e > prev_e {
561 break;
562 }
563 prev_e = e;
564 }
565 s
566 }
567}
568
569#[cfg(test)]
570mod tests {
571 use super::*;
572
573 fn near_bits(a: &ExactNum, b: &ExactNum, p: usize, slack: i32) -> bool {
574 let d = a.sub(b, p, RoundingMode::None).abs();
575 d.is_zero() || d.exponent().is_some_and(|e| e < -((p as i32) / slack))
576 }
577
578 fn near(a: &ExactNum, b: &ExactNum, p: usize) -> bool {
579 near_bits(a, b, p, 4)
580 }
581
582 fn cnear(a: &ExactComplex, b: &ExactComplex, p: usize) -> bool {
583 near(a.re(), b.re(), p) && near(a.im(), b.im(), p)
584 }
585
586 fn cnear_bits(a: &ExactComplex, b: &ExactComplex, p: usize, slack: i32) -> bool {
587 near_bits(a.re(), b.re(), p, slack) && near_bits(a.im(), b.im(), p, slack)
588 }
589
590 fn tiny(x: &ExactNum, p: usize) -> bool {
591 x.is_zero() || x.exponent().is_some_and(|e| e < -((p as i32) / 4))
592 }
593
594 fn erfi_real(x: &ExactNum, p: usize, cc: &mut Consts) -> ExactNum {
596 let pi = cc.pi(p, RoundingMode::None);
597 let sqrt_pi = pi.sqrt(p, RoundingMode::None);
598 let scale = ExactNum::from_u8(2, p).div(&sqrt_pi, p, RoundingMode::None);
599 let x2 = x.mul(x, p, RoundingMode::None);
600 let mut xpow = x.clone();
601 let mut nfact = ExactNum::from_u8(1, p);
602 let mut sum = xpow.clone();
603 for n in 1..=series_term_cap(p) {
604 nfact = nfact.mul(&ExactNum::from_u32(n as u32, p), p, RoundingMode::None);
605 xpow = xpow.mul(&x2, p, RoundingMode::None);
606 let den = nfact.mul(
607 &ExactNum::from_u32((2 * n + 1) as u32, p),
608 p,
609 RoundingMode::None,
610 );
611 let t = xpow.div(&den, p, RoundingMode::None);
612 sum = sum.add(&t, p, RoundingMode::None);
613 if t.is_zero()
614 || t.exponent()
615 .is_some_and(|e| (e as isize) + (p as isize) < 0)
616 {
617 break;
618 }
619 }
620 scale.mul(&sum, p, RoundingMode::None)
621 }
622
623 #[test]
624 fn test_complex_erf_golds() {
625 let p = 256;
626 let rm = RoundingMode::ToEven;
627 let mut cc = Consts::new().unwrap();
628
629 let z0 = ExactComplex::zero(p);
630 let e0 = z0.erf(p, rm, &mut cc);
631 assert!(tiny(e0.re(), p) && tiny(e0.im(), p));
632
633 let one = ExactComplex::one(p);
634 let e1 = one.erf(p, rm, &mut cc);
635 let r1 = ExactNum::from_u8(1, p).erf(p, rm, &mut cc);
636 assert!(near(e1.re(), &r1, p));
637 assert!(tiny(e1.im(), p));
638
639 let z = ExactComplex::new(ExactNum::from_u8(1, p), ExactNum::from_u8(1, p));
640 let ez = z.erf(p, rm, &mut cc);
641 let em = neg_c(&z).erf(p, rm, &mut cc);
642 assert!(cnear(&ez, &neg_c(&em), p));
643
644 let one_c = ExactComplex::one(p);
645 let erfc_z = z.erfc(p, rm, &mut cc);
646 let id = one_c.sub(&ez, p, rm);
647 assert!(cnear(&erfc_z, &id, p));
648
649 let i = ExactComplex::i(p);
650 let ei = i.erf(p, rm, &mut cc);
651 let erfi1 = erfi_real(&ExactNum::from_u8(1, p), p, &mut cc);
652 assert!(tiny(ei.re(), p));
653 assert!(near(ei.im(), &erfi1, p));
654
655 let h = ExactNum::from_u8(2, p).powsi(-((p as isize) / 8), p, rm);
656 let hc = ExactComplex::from_real(h.clone(), p);
657 let zp = z.add(&hc, p, rm);
658 let zm = z.sub(&hc, p, rm);
659 let num = zp.erf(p, rm, &mut cc).sub(&zm.erf(p, rm, &mut cc), p, rm);
660 let two_h = hc.mul(&two_c(p), p, rm);
661 let deriv = num.div(&two_h, p, rm);
662 let z2 = z.mul(&z, p, rm);
663 let expm = neg_c(&z2).exp(p, rm, &mut cc);
664 let two_over = ExactNum::from_u8(2, p).div(&cc.pi(p, rm).sqrt(p, rm), p, rm);
665 let expect = ExactComplex::from_real(two_over, p).mul(&expm, p, rm);
666 assert!(cnear_bits(&deriv, &expect, p, 8));
667
668 let nan = ExactComplex::new(crate::NAN.clone(), ExactNum::new(p));
669 assert!(nan.erf(p, rm, &mut cc).is_nan());
670 assert!(nan.erfc(p, rm, &mut cc).is_nan());
671
672 let big = ExactComplex::from_real(ExactNum::from_u8(10, p), p);
673 let eb = big.erf(p, rm, &mut cc);
674 assert!(near(eb.re(), &ExactNum::from_u8(1, p), p));
675 assert!(tiny(eb.im(), p));
676 assert!(cnear(&eb, &neg_c(&neg_c(&big).erf(p, rm, &mut cc)), p));
677 let far = ExactComplex::from_real(ExactNum::from_u8(20, p), p);
678 let ef = far.erf(p, rm, &mut cc);
679 assert!(near(ef.re(), &ExactNum::from_u8(1, p), p));
680 assert!(tiny(ef.im(), p));
681 }
682
683 #[test]
684 fn test_complex_gamma_golds() {
685 let p = 256;
686 let rm = RoundingMode::ToEven;
687 let mut cc = Consts::new().unwrap();
688
689 let one = ExactComplex::one(p);
690 let g1 = one.gamma(p, rm, &mut cc);
691 assert!(cnear(&g1, &one, p));
692
693 let two = two_c(p);
694 let g2 = two.gamma(p, rm, &mut cc);
695 assert!(cnear(&g2, &one, p));
696
697 let half = half_c(p);
698 let ghalf = half.gamma(p, rm, &mut cc);
699 let sqrt_pi = cc.pi(p, rm).sqrt(p, rm);
700 assert!(near(ghalf.re(), &sqrt_pi, p));
701 assert!(tiny(ghalf.im(), p));
702
703 let five = ExactComplex::from_real(ExactNum::from_u8(5, p), p);
704 let g5 = five.gamma(p, rm, &mut cc);
705 let tf = ExactComplex::from_real(ExactNum::from_u8(24, p), p);
706 assert!(cnear(&g5, &tf, p));
707
708 let z = ExactComplex::new(
709 ExactNum::from_u8(1, p).div(&ExactNum::from_u8(3, p), p, rm),
710 ExactNum::from_u8(2, p).div(&ExactNum::from_u8(5, p), p, rm),
711 );
712 let gz = z.gamma(p, rm, &mut cc);
713 let omz = one.sub(&z, p, rm);
714 let gom = omz.gamma(p, rm, &mut cc);
715 let lhs = gz.mul(&gom, p, rm);
716 let piz = pi_c(p, &mut cc).mul(&z, p, rm);
717 let rhs = pi_c(p, &mut cc).div(&piz.sin(p, rm, &mut cc), p, rm);
718 assert!(cnear(&lhs, &rhs, p));
719
720 let lg1 = one.ln_gamma(p, rm, &mut cc);
721 assert!(tiny(lg1.re(), p) && tiny(lg1.im(), p));
722
723 let lgh = half.ln_gamma(p, rm, &mut cc);
724 let ln_sqrt_pi = sqrt_pi.ln(p, rm, &mut cc);
725 assert!(near(lgh.re(), &ln_sqrt_pi, p));
726 assert!(tiny(lgh.im(), p));
727
728 let psi1 = one.digamma(p, rm, &mut cc);
729 let neg_g = cc.euler_gamma(p, rm).neg();
730 assert!(near(psi1.re(), &neg_g, p));
731 assert!(tiny(psi1.im(), p));
732
733 let zp1 = z.add(&one, p, rm);
734 let dpsi = zp1
735 .digamma(p, rm, &mut cc)
736 .sub(&z.digamma(p, rm, &mut cc), p, rm);
737 let rec = one.div(&z, p, rm);
738 assert!(cnear(&dpsi, &rec, p));
739
740 for n in [0i8, -1, -2] {
741 let pole = ExactComplex::from_real(ExactNum::from_i8(n, p), p);
742 assert!(pole.gamma(p, rm, &mut cc).is_nan(), "gamma pole {n}");
743 assert!(pole.ln_gamma(p, rm, &mut cc).is_nan(), "ln_gamma pole {n}");
744 assert!(pole.digamma(p, rm, &mut cc).is_nan(), "digamma pole {n}");
745 }
746
747 let h = ExactNum::from_u8(2, p).powsi(-((p as isize) / 8), p, rm);
748 let hc = ExactComplex::from_real(h, p);
749 let num = z.add(&hc, p, rm).ln_gamma(p, rm, &mut cc).sub(
750 &z.sub(&hc, p, rm).ln_gamma(p, rm, &mut cc),
751 p,
752 rm,
753 );
754 let deriv = num.div(&hc.mul(&two_c(p), p, rm), p, rm);
755 let psi = z.digamma(p, rm, &mut cc);
756 assert!(cnear_bits(&deriv, &psi, p, 8));
757
758 let nan = ExactComplex::new(crate::NAN.clone(), ExactNum::new(p));
759 assert!(nan.gamma(p, rm, &mut cc).is_nan());
760 }
761}