1use crate::common::util::round_p;
4use crate::complex_special::half_c;
5use crate::complex_special::nan_pair;
6use crate::complex_special::neg_c;
7use crate::complex_special::pi_c;
8use crate::complex_special::series_term_cap;
9use crate::complex_special::term_negligible;
10use crate::complex_special::two_c;
11use crate::complex_special::ziv_complex;
12use crate::Consts;
13use crate::Error;
14use crate::ExactComplex;
15use crate::ExactNum;
16use crate::RoundingMode;
17
18const BESSEL_SERIES_THRESHOLD: u32 = 16;
21
22const BESSEL_INTEGER_MAX: i32 = 64;
24
25fn abs_below(z: &ExactComplex, bound: u32, p: usize) -> bool {
26 let a = z.abs(p, RoundingMode::None);
27 let b = ExactNum::from_u32(bound, p);
28 matches!(a.cmp(&b), Some(c) if c < 0)
29}
30
31fn use_bessel_series(z: &ExactComplex, dest_p: usize) -> bool {
32 if abs_below(z, BESSEL_SERIES_THRESHOLD, dest_p) {
33 return true;
34 }
35 let az = z.abs(dest_p, RoundingMode::None);
36 let az2 = az.mul(&az, dest_p, RoundingMode::None);
37 let thresh = ExactNum::from_u32(dest_p.min(u32::MAX as usize) as u32, dest_p);
38 matches!(az2.cmp(&thresh), Some(c) if c < 0)
39}
40
41fn integer_nu(nu: &ExactComplex, p: usize) -> Option<i32> {
42 if !nu.im().is_zero() || !nu.re().is_int() {
43 return None;
44 }
45 for n in -BESSEL_INTEGER_MAX..=BESSEL_INTEGER_MAX {
46 let w = ExactNum::from_i32(n, p);
47 if nu.re().cmp(&w) == Some(0) {
48 return Some(n);
49 }
50 }
51 None
52}
53
54fn harmonic(k: usize, p: usize) -> ExactNum {
55 let mut h = ExactNum::new(p);
56 for i in 1..=k {
57 let t =
58 ExactNum::from_u8(1, p).div(&ExactNum::from_u32(i as u32, p), p, RoundingMode::None);
59 h = h.add(&t, p, RoundingMode::None);
60 }
61 h
62}
63
64fn four_c(p: usize) -> ExactComplex {
65 ExactComplex::from_real(ExactNum::from_u8(4, p), p)
66}
67
68fn eight_c(p: usize) -> ExactComplex {
69 ExactComplex::from_real(ExactNum::from_u8(8, p), p)
70}
71
72impl ExactComplex {
73 pub fn bessel_j_nu(&self, nu: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
82 if self.is_nan() || nu.is_nan() {
83 return nan_pair(Error::InvalidArgument);
84 }
85 let dest = round_p(p);
86 ziv_complex(dest, rm, |pw| self.bessel_j_at(nu, pw, dest, cc))
87 }
88
89 pub fn bessel_y(&self, nu: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
97 if self.is_nan() || nu.is_nan() {
98 return nan_pair(Error::InvalidArgument);
99 }
100 if self.re().is_zero() && self.im().is_zero() {
101 return nan_pair(Error::InvalidArgument);
102 }
103 let dest = round_p(p);
104 ziv_complex(dest, rm, |pw| self.bessel_y_at(nu, pw, dest, cc))
105 }
106
107 pub fn bessel_i(&self, nu: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
115 if self.is_nan() || nu.is_nan() {
116 return nan_pair(Error::InvalidArgument);
117 }
118 let dest = round_p(p);
119 ziv_complex(dest, rm, |pw| self.bessel_i_at(nu, pw, dest, cc))
120 }
121
122 pub fn bessel_k(&self, nu: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
130 if self.is_nan() || nu.is_nan() {
131 return nan_pair(Error::InvalidArgument);
132 }
133 if self.re().is_zero() && self.im().is_zero() {
134 return nan_pair(Error::InvalidArgument);
135 }
136 let dest = round_p(p);
137 ziv_complex(dest, rm, |pw| self.bessel_k_at(nu, pw, dest, cc))
138 }
139
140 fn bessel_j_at(&self, nu: &Self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
141 if self.re().is_zero() && self.im().is_zero() {
142 return match integer_nu(nu, work_p) {
143 Some(0) => ExactComplex::one(work_p),
144 Some(_) => ExactComplex::zero(work_p),
145 None => nan_pair(Error::InvalidArgument),
146 };
147 }
148 if use_bessel_series(self, dest_p) {
149 self.bessel_j_series(nu, work_p, cc)
150 } else {
151 self.bessel_hankel_j(nu, work_p, cc)
152 }
153 }
154
155 fn bessel_y_at(&self, nu: &Self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
156 if let Some(n) = integer_nu(nu, work_p) {
157 return self.bessel_y_int(n, work_p, dest_p, cc);
158 }
159 if use_bessel_series(self, dest_p) {
160 self.bessel_y_nonint(nu, work_p, dest_p, cc)
161 } else {
162 self.bessel_hankel_y(nu, work_p, cc)
163 }
164 }
165
166 fn bessel_i_at(&self, nu: &Self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
167 if self.re().is_zero() && self.im().is_zero() {
168 return match integer_nu(nu, work_p) {
169 Some(0) => ExactComplex::one(work_p),
170 Some(n) if n > 0 => ExactComplex::zero(work_p),
171 _ => nan_pair(Error::InvalidArgument),
172 };
173 }
174 let iz = ExactComplex::i(work_p).mul(self, work_p, RoundingMode::None);
175 let j = iz.bessel_j_at(nu, work_p, dest_p, cc);
176 let ln_i = ExactComplex::i(work_p).ln(work_p, RoundingMode::None, cc);
177 let scale =
178 neg_c(nu)
179 .mul(&ln_i, work_p, RoundingMode::None)
180 .exp(work_p, RoundingMode::None, cc);
181 scale.mul(&j, work_p, RoundingMode::None)
182 }
183
184 fn bessel_k_at(&self, nu: &Self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
185 let iz = ExactComplex::i(work_p).mul(self, work_p, RoundingMode::None);
186 let j = iz.bessel_j_at(nu, work_p, dest_p, cc);
187 let y = iz.bessel_y_at(nu, work_p, dest_p, cc);
188 let h1 = j.add(
189 &ExactComplex::i(work_p).mul(&y, work_p, RoundingMode::None),
190 work_p,
191 RoundingMode::None,
192 );
193 let ln_i = ExactComplex::i(work_p).ln(work_p, RoundingMode::None, cc);
194 let nu_p1 = nu.add(&ExactComplex::one(work_p), work_p, RoundingMode::None);
195 let i_pow =
196 nu_p1
197 .mul(&ln_i, work_p, RoundingMode::None)
198 .exp(work_p, RoundingMode::None, cc);
199 let half_pi = pi_c(work_p, cc).mul(&half_c(work_p), work_p, RoundingMode::None);
200 half_pi
201 .mul(&i_pow, work_p, RoundingMode::None)
202 .mul(&h1, work_p, RoundingMode::None)
203 }
204
205 fn bessel_j_series(&self, nu: &Self, p: usize, cc: &mut Consts) -> Self {
206 let half = self.mul(&half_c(p), p, RoundingMode::None);
207 let pow = half.pow(nu, p, RoundingMode::None, cc);
208 let g =
209 nu.add(&ExactComplex::one(p), p, RoundingMode::None)
210 .gamma(p, RoundingMode::None, cc);
211 let mut term = pow.div(&g, p, RoundingMode::None);
212 let mut sum = term.clone();
213 let hh = half.mul(&half, p, RoundingMode::None);
214 for k in 1..=series_term_cap(p) {
215 let kk = ExactComplex::from_real(ExactNum::from_u32(k as u32, p), p);
216 let den = kk
217 .add(nu, p, RoundingMode::None)
218 .mul(&kk, p, RoundingMode::None);
219 term = term
220 .mul(&hh, p, RoundingMode::None)
221 .div(&den, p, RoundingMode::None);
222 term = neg_c(&term);
223 sum = sum.add(&term, p, RoundingMode::None);
224 if term_negligible(&term, p) {
225 break;
226 }
227 }
228 sum
229 }
230
231 fn bessel_y_nonint(&self, nu: &Self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
232 let nupi = nu.mul(&pi_c(work_p, cc), work_p, RoundingMode::None);
233 let s = nupi.sin(work_p, RoundingMode::None, cc);
234 if term_negligible(&s, work_p) {
235 return nan_pair(Error::InvalidArgument);
236 }
237 let jp = self.bessel_j_at(nu, work_p, dest_p, cc);
238 let jm = self.bessel_j_at(&neg_c(nu), work_p, dest_p, cc);
239 let c = nupi.cos(work_p, RoundingMode::None, cc);
240 jp.mul(&c, work_p, RoundingMode::None)
241 .sub(&jm, work_p, RoundingMode::None)
242 .div(&s, work_p, RoundingMode::None)
243 }
244
245 fn bessel_y_int(&self, n: i32, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
246 let an = n.unsigned_abs();
247 let y = if use_bessel_series(self, dest_p) {
248 match an {
249 0 => self.bessel_y0_series(work_p, dest_p, cc),
250 1 => self.bessel_y1_series(work_p, dest_p, cc),
251 _ => self.bessel_y_recurrence(an, work_p, dest_p, cc),
252 }
253 } else {
254 let nu = ExactComplex::from_real(ExactNum::from_u32(an, work_p), work_p);
255 self.bessel_hankel_y(&nu, work_p, cc)
256 };
257 if n < 0 && an % 2 == 1 {
258 neg_c(&y)
259 } else {
260 y
261 }
262 }
263
264 fn bessel_y_recurrence(&self, n: u32, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
265 let mut ym2 = self.bessel_y0_series(work_p, dest_p, cc);
266 let mut ym1 = self.bessel_y1_series(work_p, dest_p, cc);
267 for m in 1..n {
268 let two_m = ExactComplex::from_real(ExactNum::from_u32(2 * m, work_p), work_p);
269 let ym = two_m
270 .div(self, work_p, RoundingMode::None)
271 .mul(&ym1, work_p, RoundingMode::None)
272 .sub(&ym2, work_p, RoundingMode::None);
273 ym2 = ym1;
274 ym1 = ym;
275 }
276 ym1
277 }
278
279 fn bessel_y0_series(&self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
280 let two_pi = two_c(work_p).div(&pi_c(work_p, cc), work_p, RoundingMode::None);
281 let half = self.mul(&half_c(work_p), work_p, RoundingMode::None);
282 let j0 = self.bessel_j_at(&ExactComplex::zero(work_p), work_p, dest_p, cc);
283 let g = ExactComplex::from_real(cc.euler_gamma(work_p, RoundingMode::None), work_p);
284 let prefix = g.add(
285 &half.ln(work_p, RoundingMode::None, cc),
286 work_p,
287 RoundingMode::None,
288 );
289 let z2 = half.mul(&half, work_p, RoundingMode::None);
290 let mut fact = ExactNum::from_u8(1, work_p);
291 let mut zk = ExactComplex::one(work_p);
292 let mut sum = ExactComplex::zero(work_p);
293 for m in 1..=series_term_cap(work_p) {
294 fact = fact.mul(
295 &ExactNum::from_u32(m as u32, work_p),
296 work_p,
297 RoundingMode::None,
298 );
299 zk = zk.mul(&z2, work_p, RoundingMode::None);
300 let h = harmonic(m as usize, work_p);
301 let den = fact.mul(&fact, work_p, RoundingMode::None);
302 let mut term = ExactComplex::from_real(h.div(&den, work_p, RoundingMode::None), work_p)
303 .mul(&zk, work_p, RoundingMode::None);
304 if m % 2 == 0 {
305 term = neg_c(&term);
306 }
307 sum = sum.add(&term, work_p, RoundingMode::None);
308 if term_negligible(&term, work_p) {
309 break;
310 }
311 }
312 two_pi.mul(
313 &prefix
314 .mul(&j0, work_p, RoundingMode::None)
315 .add(&sum, work_p, RoundingMode::None),
316 work_p,
317 RoundingMode::None,
318 )
319 }
320
321 fn bessel_y1_series(&self, work_p: usize, dest_p: usize, cc: &mut Consts) -> Self {
322 let nu1 = ExactComplex::one(work_p);
323 let two_pi = two_c(work_p).div(&pi_c(work_p, cc), work_p, RoundingMode::None);
324 let half = self.mul(&half_c(work_p), work_p, RoundingMode::None);
325 let j1 = self.bessel_j_at(&nu1, work_p, dest_p, cc);
326 let g = ExactComplex::from_real(cc.euler_gamma(work_p, RoundingMode::None), work_p);
327 let prefix = g.add(
328 &half.ln(work_p, RoundingMode::None, cc),
329 work_p,
330 RoundingMode::None,
331 );
332 let z2 = half.mul(&half, work_p, RoundingMode::None);
333 let mut kfact = ExactNum::from_u8(1, work_p);
334 let mut kp1fact = ExactNum::from_u8(1, work_p);
335 let mut zk = ExactComplex::one(work_p);
336 let mut sum = ExactComplex::zero(work_p);
337 for k in 0..=series_term_cap(work_p) {
338 let hk = harmonic(k as usize, work_p);
339 let rec = ExactNum::from_u8(1, work_p).div(
340 &ExactNum::from_u32((k + 1) as u32, work_p),
341 work_p,
342 RoundingMode::None,
343 );
344 let hkp1 = hk.add(&rec, work_p, RoundingMode::None);
345 let den = kfact.mul(&kp1fact, work_p, RoundingMode::None);
346 let mut term = ExactComplex::from_real(
347 hk.add(&hkp1, work_p, RoundingMode::None)
348 .div(&den, work_p, RoundingMode::None),
349 work_p,
350 )
351 .mul(&zk, work_p, RoundingMode::None);
352 if k % 2 == 1 {
353 term = neg_c(&term);
354 }
355 sum = sum.add(&term, work_p, RoundingMode::None);
356 if k > 0 && term_negligible(&term, work_p) {
357 break;
358 }
359 let kp = k + 1;
360 kfact = kfact.mul(
361 &ExactNum::from_u32(kp as u32, work_p),
362 work_p,
363 RoundingMode::None,
364 );
365 kp1fact = kp1fact.mul(
366 &ExactNum::from_u32((kp + 1) as u32, work_p),
367 work_p,
368 RoundingMode::None,
369 );
370 zk = zk.mul(&z2, work_p, RoundingMode::None);
371 }
372 let a = two_pi.mul(
373 &prefix.mul(&j1, work_p, RoundingMode::None),
374 work_p,
375 RoundingMode::None,
376 );
377 let b = two_c(work_p).div(
378 &pi_c(work_p, cc).mul(self, work_p, RoundingMode::None),
379 work_p,
380 RoundingMode::None,
381 );
382 let c = self
383 .div(
384 &two_c(work_p).mul(&pi_c(work_p, cc), work_p, RoundingMode::None),
385 work_p,
386 RoundingMode::None,
387 )
388 .mul(&sum, work_p, RoundingMode::None);
389 a.sub(&b, work_p, RoundingMode::None)
390 .sub(&c, work_p, RoundingMode::None)
391 }
392
393 fn hankel_chi_omega(&self, nu: &Self, p: usize, cc: &mut Consts) -> (Self, Self) {
394 let two_nu_1 = two_c(p).mul(nu, p, RoundingMode::None).add(
395 &ExactComplex::one(p),
396 p,
397 RoundingMode::None,
398 );
399 let chi = self.sub(
400 &two_nu_1.mul(&pi_c(p, cc), p, RoundingMode::None).div(
401 &four_c(p),
402 p,
403 RoundingMode::None,
404 ),
405 p,
406 RoundingMode::None,
407 );
408 let two_over = two_c(p).div(
409 &pi_c(p, cc).mul(self, p, RoundingMode::None),
410 p,
411 RoundingMode::None,
412 );
413 let omega = two_over.sqrt(p, RoundingMode::None, cc);
414 (chi, omega)
415 }
416
417 fn hankel_pq(&self, nu: &Self, p: usize) -> (Self, Self) {
418 let two_nu = two_c(p).mul(nu, p, RoundingMode::None);
419 let mu = two_nu.mul(&two_nu, p, RoundingMode::None);
420 let eight_z = eight_c(p).mul(self, p, RoundingMode::None);
421 let mut prod = ExactComplex::one(p);
422 let mut kf = ExactNum::from_u8(1, p);
423 let mut pz = ExactComplex::one(p);
424 let mut psum = ExactComplex::one(p);
425 let mut qsum = ExactComplex::zero(p);
426 for k in 1..=series_term_cap(p) {
427 let odd = ExactComplex::from_real(ExactNum::from_u32((2 * k - 1) as u32, p), p);
428 let odd2 = odd.mul(&odd, p, RoundingMode::None);
429 prod = prod.mul(&mu.sub(&odd2, p, RoundingMode::None), p, RoundingMode::None);
430 kf = kf.mul(&ExactNum::from_u32(k as u32, p), p, RoundingMode::None);
431 pz = pz.mul(&eight_z, p, RoundingMode::None);
432 let term = prod.div(
433 &ExactComplex::from_real(kf.clone(), p).mul(&pz, p, RoundingMode::None),
434 p,
435 RoundingMode::None,
436 );
437 if k % 2 == 0 {
438 let signed = if (k / 2) % 2 == 1 { neg_c(&term) } else { term.clone() };
439 psum = psum.add(&signed, p, RoundingMode::None);
440 } else {
441 let signed = if ((k - 1) / 2) % 2 == 1 { neg_c(&term) } else { term.clone() };
442 qsum = qsum.add(&signed, p, RoundingMode::None);
443 }
444 if term_negligible(&term, p) {
445 break;
446 }
447 }
448 (psum, qsum)
449 }
450
451 fn bessel_hankel_j(&self, nu: &Self, p: usize, cc: &mut Consts) -> Self {
452 let (chi, omega) = self.hankel_chi_omega(nu, p, cc);
453 let (pp, qq) = self.hankel_pq(nu, p);
454 let (sn, cs) = {
455 let s = chi.sin(p, RoundingMode::None, cc);
456 let c = chi.cos(p, RoundingMode::None, cc);
457 (s, c)
458 };
459 omega.mul(
460 &pp.mul(&cs, p, RoundingMode::None).sub(
461 &qq.mul(&sn, p, RoundingMode::None),
462 p,
463 RoundingMode::None,
464 ),
465 p,
466 RoundingMode::None,
467 )
468 }
469
470 fn bessel_hankel_y(&self, nu: &Self, p: usize, cc: &mut Consts) -> Self {
471 let (chi, omega) = self.hankel_chi_omega(nu, p, cc);
472 let (pp, qq) = self.hankel_pq(nu, p);
473 let s = chi.sin(p, RoundingMode::None, cc);
474 let c = chi.cos(p, RoundingMode::None, cc);
475 omega.mul(
476 &pp.mul(&s, p, RoundingMode::None).add(
477 &qq.mul(&c, p, RoundingMode::None),
478 p,
479 RoundingMode::None,
480 ),
481 p,
482 RoundingMode::None,
483 )
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490 use crate::complex_special::neg_c;
491 use crate::complex_special::pi_c;
492 use crate::complex_special::two_c;
493
494 fn near(a: &ExactNum, b: &ExactNum, p: usize) -> bool {
495 let d = a.sub(b, p, RoundingMode::None).abs();
496 d.is_zero() || d.exponent().is_some_and(|e| e < -((p as i32) / 4))
497 }
498
499 fn cnear(a: &ExactComplex, b: &ExactComplex, p: usize) -> bool {
500 near(a.re(), b.re(), p) && near(a.im(), b.im(), p)
501 }
502
503 fn cnear_bits(a: &ExactComplex, b: &ExactComplex, p: usize, slack: i32) -> bool {
504 let dr = a.re().sub(b.re(), p, RoundingMode::None).abs();
505 let di = a.im().sub(b.im(), p, RoundingMode::None).abs();
506 (dr.is_zero() || dr.exponent().is_some_and(|e| e < -((p as i32) / slack)))
507 && (di.is_zero() || di.exponent().is_some_and(|e| e < -((p as i32) / slack)))
508 }
509
510 fn tiny(x: &ExactNum, p: usize) -> bool {
511 x.is_zero() || x.exponent().is_some_and(|e| e < -((p as i32) / 4))
512 }
513
514 #[test]
515 fn test_complex_bessel_golds() {
516 let p = 256;
517 let rm = RoundingMode::ToEven;
518 let mut cc = Consts::new().unwrap();
519
520 let one = ExactComplex::one(p);
521 let z1 = one.clone();
522 let nu0 = ExactComplex::zero(p);
523 let nu1 = ExactComplex::one(p);
524 let j0 = z1.bessel_j_nu(&nu0, p, rm, &mut cc);
525 let rj0 = ExactNum::from_u8(1, p).bessel_j_nu(&ExactNum::from_u8(0, p), p, rm, &mut cc);
526 assert!(near(j0.re(), &rj0, p));
527 assert!(tiny(j0.im(), p));
528 let j1 = z1.bessel_j_nu(&nu1, p, rm, &mut cc);
529 let rj1 = ExactNum::from_u8(1, p).bessel_j_nu(&ExactNum::from_u8(1, p), p, rm, &mut cc);
530 assert!(near(j1.re(), &rj1, p));
531 assert!(tiny(j1.im(), p));
532
533 let z = ExactComplex::new(ExactNum::from_u8(1, p), half_c(p).re().clone());
534 let jn = z.bessel_j_nu(&nu0, p, rm, &mut cc);
535 let yn1 = z.bessel_y(&nu1, p, rm, &mut cc);
536 let jn1 = z.bessel_j_nu(&nu1, p, rm, &mut cc);
537 let yn = z.bessel_y(&nu0, p, rm, &mut cc);
538 let lhs = jn.mul(&yn1, p, rm).sub(&jn1.mul(&yn, p, rm), p, rm);
539 let rhs = neg_c(&two_c(p)).div(&pi_c(p, &mut cc).mul(&z, p, rm), p, rm);
540 assert!(cnear_bits(&lhs, &rhs, p, 8));
541
542 let i0 = z1.bessel_i(&nu0, p, rm, &mut cc);
543 let ri0 = ExactNum::from_u8(1, p).bessel_i(&ExactNum::from_u8(0, p), p, rm, &mut cc);
544 assert!(near(i0.re(), &ri0, p));
545 assert!(tiny(i0.im(), p));
546
547 let k0 = z1.bessel_k(&nu0, p, rm, &mut cc);
548 let rk0 = ExactNum::from_u8(1, p).bessel_k(&ExactNum::from_u8(0, p), p, rm, &mut cc);
549 assert!(near(k0.re(), &rk0, p));
550 assert!(tiny(k0.im(), p));
551
552 let iz = ExactComplex::i(p).mul(&z, p, rm);
553 let j_iz = iz.bessel_j_nu(&nu0, p, rm, &mut cc);
554 let ln_i = ExactComplex::i(p).ln(p, rm, &mut cc);
555 let scale = neg_c(&nu0).mul(&ln_i, p, rm).exp(p, rm, &mut cc);
556 let via_j = scale.mul(&j_iz, p, rm);
557 let i_z = z.bessel_i(&nu0, p, rm, &mut cc);
558 assert!(cnear(&i_z, &via_j, p));
559
560 let h = ExactNum::from_u8(2, p).powsi(-((p as isize) / 8), p, rm);
561 let hc = ExactComplex::from_real(h, p);
562 let num = z.add(&hc, p, rm).bessel_j_nu(&nu0, p, rm, &mut cc).sub(
563 &z.sub(&hc, p, rm).bessel_j_nu(&nu0, p, rm, &mut cc),
564 p,
565 rm,
566 );
567 let deriv = num.div(&hc.mul(&two_c(p), p, rm), p, rm);
568 let expect = neg_c(&z.bessel_j_nu(&nu1, p, rm, &mut cc));
569 assert!(cnear_bits(&deriv, &expect, p, 8));
570
571 let half = half_c(p);
572 let z0 = ExactComplex::zero(p);
573 assert!(z0.bessel_j_nu(&half, p, rm, &mut cc).is_nan());
574
575 let eps = ExactNum::from_u8(2, p).powsi(-((p as isize) / 8), p, rm);
576 let above = ExactComplex::new(ExactNum::from_i8(-1, p), eps.clone());
577 let below = ExactComplex::new(
578 ExactNum::from_i8(-1, p),
579 ExactNum::from_i8(-1, p).mul(&eps, p, rm),
580 );
581 let ja = above.bessel_j_nu(&half, p, rm, &mut cc);
582 let jb = below.bessel_j_nu(&half, p, rm, &mut cc);
583 assert!(!cnear(&ja, &jb, p));
584 assert!(cnear_bits(&ja, &jb.conj(), p, 8) || !tiny(ja.im(), p) || !tiny(jb.im(), p));
585 }
586}