1use super::simd::{
5 add_u32_lanes, add_u64_lanes, div_u32_lanes, div_u64_lanes, fma_u32_lanes, fma_u64_lanes,
6 mul_u32_lanes, mul_u64_lanes, sqrt_u32_lanes, sqrt_u64_lanes, sub_u32_lanes, sub_u64_lanes,
7};
8use super::{Ieee32, Ieee64};
9use crate::defs::RoundingMode;
10use crate::Consts;
11use crate::Error;
12use crate::ExactNum;
13use alloc::vec::Vec;
14
15const SVD_ITER_MAX: u32 = 64;
18const EIGEN_ITER_MAX: u32 = 64;
21const FFT_MAX_POINTS: usize = 4096;
23const SVD_CONV_GUARD_BITS: i32 = 4;
26
27trait LaneBits: Copy {
28 fn add_lanes(a: &[Self], b: &[Self]) -> Vec<Self>;
29 fn mul_lanes(a: &[Self], b: &[Self]) -> Vec<Self>;
30 fn sub_lanes(a: &[Self], b: &[Self]) -> Vec<Self>;
31 fn div_lanes(a: &[Self], b: &[Self]) -> Vec<Self>;
32 fn sqrt_lanes(a: &[Self]) -> Vec<Self>;
33 fn fma_lanes(a: &[Self], b: &[Self], c: &[Self]) -> Vec<Self>;
34}
35
36impl LaneBits for u32 {
37 fn add_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
38 add_u32_lanes(a, b)
39 }
40 fn mul_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
41 mul_u32_lanes(a, b)
42 }
43 fn sub_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
44 sub_u32_lanes(a, b)
45 }
46 fn div_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
47 div_u32_lanes(a, b)
48 }
49 fn sqrt_lanes(a: &[Self]) -> Vec<Self> {
50 sqrt_u32_lanes(a)
51 }
52 fn fma_lanes(a: &[Self], b: &[Self], c: &[Self]) -> Vec<Self> {
53 fma_u32_lanes(a, b, c)
54 }
55}
56
57impl LaneBits for u64 {
58 fn add_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
59 add_u64_lanes(a, b)
60 }
61 fn mul_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
62 mul_u64_lanes(a, b)
63 }
64 fn sub_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
65 sub_u64_lanes(a, b)
66 }
67 fn div_lanes(a: &[Self], b: &[Self]) -> Vec<Self> {
68 div_u64_lanes(a, b)
69 }
70 fn sqrt_lanes(a: &[Self]) -> Vec<Self> {
71 sqrt_u64_lanes(a)
72 }
73 fn fma_lanes(a: &[Self], b: &[Self], c: &[Self]) -> Vec<Self> {
74 fma_u64_lanes(a, b, c)
75 }
76}
77
78#[derive(Clone, Debug)]
80pub struct Ieee32Array {
81 bits: Vec<u32>,
82 rows: usize,
83 cols: usize,
84}
85
86#[derive(Clone, Debug)]
88pub struct Ieee64Array {
89 bits: Vec<u64>,
90 rows: usize,
91 cols: usize,
92}
93
94#[derive(Clone, Debug)]
96pub struct ExactNumArray {
97 p: usize,
98 vals: Vec<ExactNum>,
99 rows: usize,
100 cols: usize,
101}
102
103macro_rules! impl_ieee_array {
104 ($arr:ident, $scalar:ident, $bits:ty) => {
105 impl $arr {
106 pub fn new() -> Self {
108 Self {
109 bits: Vec::new(),
110 rows: 0,
111 cols: 0,
112 }
113 }
114
115 pub fn filled(n: usize, fill: $scalar) -> Self {
117 Self {
118 bits: alloc::vec![fill.to_bits(); n],
119 rows: 1,
120 cols: n,
121 }
122 }
123
124 pub fn filled_2d(rows: usize, cols: usize, fill: $scalar) -> Option<Self> {
126 let n = rows.checked_mul(cols)?;
127 Some(Self {
128 bits: alloc::vec![fill.to_bits(); n],
129 rows,
130 cols,
131 })
132 }
133
134 pub fn from_bits(bits: &[$bits]) -> Self {
136 Self {
137 bits: bits.to_vec(),
138 rows: 1,
139 cols: bits.len(),
140 }
141 }
142
143 pub fn from_shape(rows: usize, cols: usize, vals: &[$scalar]) -> Option<Self> {
145 let n = rows.checked_mul(cols)?;
146 if n != vals.len() {
147 return None;
148 }
149 Some(Self {
150 bits: vals.iter().map(|v| v.to_bits()).collect(),
151 rows,
152 cols,
153 })
154 }
155
156 pub fn from_values(vals: &[$scalar]) -> Self {
158 Self {
159 bits: vals.iter().map(|v| v.to_bits()).collect(),
160 rows: 1,
161 cols: vals.len(),
162 }
163 }
164
165 pub fn shape(&self) -> (usize, usize) {
167 (self.rows, self.cols)
168 }
169
170 pub fn reshape(&self, rows: usize, cols: usize) -> Option<Self> {
172 let n = rows.checked_mul(cols)?;
173 if n != self.bits.len() {
174 return None;
175 }
176 Some(Self {
177 bits: self.bits.clone(),
178 rows,
179 cols,
180 })
181 }
182
183 pub fn len(&self) -> usize {
185 self.bits.len()
186 }
187
188 pub fn is_empty(&self) -> bool {
190 self.bits.is_empty()
191 }
192
193 pub fn as_bits(&self) -> &[$bits] {
195 &self.bits
196 }
197
198 pub fn get(&self, i: usize) -> Option<$scalar> {
200 self.bits.get(i).copied().map($scalar::from_bits)
201 }
202
203 pub fn get2(&self, i: usize, j: usize) -> Option<$scalar> {
205 if i >= self.rows || j >= self.cols {
206 return None;
207 }
208 self.get(i * self.cols + j)
209 }
210
211 pub fn add(&self, rhs: &Self) -> Option<Self> {
214 if self.rows != rhs.rows || self.cols != rhs.cols {
215 return None;
216 }
217 Some(Self {
218 bits: <$bits>::add_lanes(&self.bits, &rhs.bits),
219 rows: self.rows,
220 cols: self.cols,
221 })
222 }
223
224 pub fn add_scalar(&self, s: $scalar) -> Self {
226 self.map(|x| x.add(s))
227 }
228
229 pub fn sub(&self, rhs: &Self) -> Option<Self> {
232 if self.rows != rhs.rows || self.cols != rhs.cols {
233 return None;
234 }
235 Some(Self {
236 bits: <$bits>::sub_lanes(&self.bits, &rhs.bits),
237 rows: self.rows,
238 cols: self.cols,
239 })
240 }
241
242 pub fn mul(&self, rhs: &Self) -> Option<Self> {
245 if self.rows != rhs.rows || self.cols != rhs.cols {
246 return None;
247 }
248 Some(Self {
249 bits: <$bits>::mul_lanes(&self.bits, &rhs.bits),
250 rows: self.rows,
251 cols: self.cols,
252 })
253 }
254
255 pub fn mul_scalar(&self, s: $scalar) -> Self {
257 self.map(|x| x.mul(s))
258 }
259
260 pub fn div(&self, rhs: &Self) -> Option<Self> {
263 if self.rows != rhs.rows || self.cols != rhs.cols {
264 return None;
265 }
266 Some(Self {
267 bits: <$bits>::div_lanes(&self.bits, &rhs.bits),
268 rows: self.rows,
269 cols: self.cols,
270 })
271 }
272
273 pub fn sqrt(&self) -> Self {
275 Self {
276 bits: <$bits>::sqrt_lanes(&self.bits),
277 rows: self.rows,
278 cols: self.cols,
279 }
280 }
281
282 pub fn fma(&self, b: &Self, c: &Self) -> Option<Self> {
285 if self.rows != b.rows
286 || self.cols != b.cols
287 || self.rows != c.rows
288 || self.cols != c.cols
289 {
290 return None;
291 }
292 Some(Self {
293 bits: <$bits>::fma_lanes(&self.bits, &b.bits, &c.bits),
294 rows: self.rows,
295 cols: self.cols,
296 })
297 }
298
299 pub fn sum(&self) -> $scalar {
301 let mut acc = $scalar::ZERO;
302 for &b in &self.bits {
303 acc = acc.add($scalar::from_bits(b));
304 }
305 acc
306 }
307
308 pub fn dot(&self, rhs: &Self) -> Option<$scalar> {
310 if self.len() != rhs.len() {
311 return None;
312 }
313 let mut acc = $scalar::ZERO;
314 for (a, b) in self.bits.iter().zip(rhs.bits.iter()) {
315 acc = acc.add($scalar::from_bits(*a).mul($scalar::from_bits(*b)));
316 }
317 Some(acc)
318 }
319
320 pub fn matmul(&self, rhs: &Self) -> Option<Self> {
322 if self.cols != rhs.rows {
323 return None;
324 }
325 let m = self.rows;
326 let k = self.cols;
327 let n = rhs.cols;
328 let mut bits = alloc::vec![$scalar::ZERO.to_bits(); m.checked_mul(n)?];
329 for i in 0..m {
330 for j in 0..n {
331 let mut acc = $scalar::ZERO;
332 for t in 0..k {
333 let a = $scalar::from_bits(self.bits[i * k + t]);
334 let b = $scalar::from_bits(rhs.bits[t * n + j]);
335 acc = acc.add(a.mul(b));
336 }
337 bits[i * n + j] = acc.to_bits();
338 }
339 }
340 Some(Self {
341 bits,
342 rows: m,
343 cols: n,
344 })
345 }
346
347 fn map(&self, op: impl Fn($scalar) -> $scalar) -> Self {
348 Self {
349 bits: self
350 .bits
351 .iter()
352 .map(|&b| op($scalar::from_bits(b)).to_bits())
353 .collect(),
354 rows: self.rows,
355 cols: self.cols,
356 }
357 }
358
359 pub fn map_exact<F>(&self, p: usize, mut op: F) -> Self
361 where
362 F: FnMut(&ExactNum) -> ExactNum,
363 {
364 Self {
365 bits: self
366 .bits
367 .iter()
368 .map(|&b| {
369 let x = $scalar::from_bits(b).to_exact(p);
370 $scalar::from_exact(&op(&x)).to_bits()
371 })
372 .collect(),
373 rows: self.rows,
374 cols: self.cols,
375 }
376 }
377 }
378
379 impl Default for $arr {
380 fn default() -> Self {
381 Self::new()
382 }
383 }
384 };
385}
386
387impl_ieee_array!(Ieee32Array, Ieee32, u32);
388impl_ieee_array!(Ieee64Array, Ieee64, u64);
389
390macro_rules! ieee_unary_exact {
391 ($p:expr, $($name:ident),+ $(,)?) => {
392 $(
393 #[doc = concat!("Elementwise `", stringify!($name), "` via `ExactNum`.")]
394 pub fn $name(&self, cc: &mut Consts) -> Self {
395 self.map_exact($p, |x| x.$name($p, RoundingMode::ToEven, cc))
396 }
397 )+
398 };
399}
400
401macro_rules! ieee_array_specials {
402 ($arr:ident, $scalar:ident, $p:expr) => {
403 impl $arr {
404 ieee_unary_exact!(
405 $p, exp, exp2, exp10, expm1, ln, log2, log10, log1p, sin, cos, tan, asin, acos,
406 atan, sinh, cosh, tanh, asinh, acosh, atanh, erf, erfc, gamma, ln_gamma, digamma,
407 ei, si, ci, li, fresnel_s, fresnel_c, ai, bi, elliptic_k, rem_pi,
408 );
409
410 pub fn elliptic_e_complete(&self, cc: &mut Consts) -> Self {
412 self.map_exact($p, |x| x.elliptic_e_complete($p, RoundingMode::ToEven, cc))
413 }
414
415 pub fn cbrt(&self) -> Self {
417 self.map_exact($p, |x| x.cbrt($p, RoundingMode::ToEven))
418 }
419
420 pub fn atan2(&self, x: $scalar, cc: &mut Consts) -> Self {
422 let xe = x.to_exact($p);
423 self.map_exact($p, |y| y.atan2(&xe, $p, RoundingMode::ToEven, cc))
424 }
425
426 pub fn hypot(&self, other: $scalar) -> Self {
428 let oe = other.to_exact($p);
429 self.map_exact($p, |x| x.hypot(&oe, $p, RoundingMode::ToEven))
430 }
431
432 pub fn pow(&self, n: $scalar, cc: &mut Consts) -> Self {
434 let ne = n.to_exact($p);
435 self.map_exact($p, |x| x.pow(&ne, $p, RoundingMode::ToEven, cc))
436 }
437
438 pub fn log(&self, base: $scalar, cc: &mut Consts) -> Self {
440 let be = base.to_exact($p);
441 self.map_exact($p, |x| x.log(&be, $p, RoundingMode::ToEven, cc))
442 }
443
444 pub fn gammainc(&self, x: $scalar, cc: &mut Consts) -> Self {
446 let xe = x.to_exact($p);
447 self.map_exact($p, |s| s.gammainc(&xe, $p, RoundingMode::ToEven, cc))
448 }
449
450 pub fn gammainc_upper(&self, x: $scalar, cc: &mut Consts) -> Self {
452 let xe = x.to_exact($p);
453 self.map_exact($p, |s| s.gammainc_upper(&xe, $p, RoundingMode::ToEven, cc))
454 }
455
456 pub fn bessel_j(&self, n: usize, cc: &mut Consts) -> Self {
458 self.map_exact($p, |x| x.bessel_j(n, $p, RoundingMode::ToEven, cc))
459 }
460
461 pub fn bessel_j_nu(&self, nu: $scalar, cc: &mut Consts) -> Self {
463 let n = nu.to_exact($p);
464 self.map_exact($p, |x| x.bessel_j_nu(&n, $p, RoundingMode::ToEven, cc))
465 }
466
467 pub fn bessel_y(&self, nu: $scalar, cc: &mut Consts) -> Self {
469 let n = nu.to_exact($p);
470 self.map_exact($p, |x| x.bessel_y(&n, $p, RoundingMode::ToEven, cc))
471 }
472
473 pub fn bessel_i(&self, nu: $scalar, cc: &mut Consts) -> Self {
475 let n = nu.to_exact($p);
476 self.map_exact($p, |x| x.bessel_i(&n, $p, RoundingMode::ToEven, cc))
477 }
478
479 pub fn bessel_k(&self, nu: $scalar, cc: &mut Consts) -> Self {
481 let n = nu.to_exact($p);
482 self.map_exact($p, |x| x.bessel_k(&n, $p, RoundingMode::ToEven, cc))
483 }
484
485 pub fn legendre_p(&self, n: u32) -> Self {
487 self.map_exact($p, |x| x.legendre_p(n, $p, RoundingMode::ToEven))
488 }
489
490 pub fn assoc_legendre_p(&self, n: u32, m: i32) -> Self {
492 self.map_exact($p, |x| x.assoc_legendre_p(n, m, $p, RoundingMode::ToEven))
493 }
494
495 pub fn elliptic_f(&self, m: $scalar, cc: &mut Consts) -> Self {
497 let me = m.to_exact($p);
498 self.map_exact($p, |x| x.elliptic_f(&me, $p, RoundingMode::ToEven, cc))
499 }
500
501 pub fn elliptic_e(&self, m: $scalar, cc: &mut Consts) -> Self {
503 let me = m.to_exact($p);
504 self.map_exact($p, |x| x.elliptic_e(&me, $p, RoundingMode::ToEven, cc))
505 }
506
507 pub fn elliptic_pi_complete(&self, m: $scalar, cc: &mut Consts) -> Self {
509 let me = m.to_exact($p);
510 self.map_exact($p, |n| {
511 n.elliptic_pi_complete(&me, $p, RoundingMode::ToEven, cc)
512 })
513 }
514
515 pub fn elliptic_pi(&self, x: $scalar, m: $scalar, cc: &mut Consts) -> Self {
517 let xe = x.to_exact($p);
518 let me = m.to_exact($p);
519 self.map_exact($p, |n| {
520 n.elliptic_pi(&xe, &me, $p, RoundingMode::ToEven, cc)
521 })
522 }
523
524 pub fn hypergeom_2f1(
526 &self,
527 b: $scalar,
528 c: $scalar,
529 z: $scalar,
530 cc: &mut Consts,
531 ) -> Self {
532 let be = b.to_exact($p);
533 let ce = c.to_exact($p);
534 let ze = z.to_exact($p);
535 self.map_exact($p, |a| {
536 a.hypergeom_2f1(&be, &ce, &ze, $p, RoundingMode::ToEven, cc)
537 })
538 }
539
540 pub fn betainc(&self, b: $scalar, x: $scalar, cc: &mut Consts) -> Self {
542 let be = b.to_exact($p);
543 let xe = x.to_exact($p);
544 self.map_exact($p, |a| a.betainc(&be, &xe, $p, RoundingMode::ToEven, cc))
545 }
546 }
547 };
548}
549
550ieee_array_specials!(Ieee32Array, Ieee32, 64);
551ieee_array_specials!(Ieee64Array, Ieee64, 128);
552
553impl Ieee64Array {
554 pub(crate) fn from_parts(rows: usize, cols: usize, bits: Vec<u64>) -> Result<Self, Error> {
555 let n = rows.checked_mul(cols).ok_or(Error::InvalidArgument)?;
556 if n != bits.len() {
557 return Err(Error::InvalidArgument);
558 }
559 Ok(Self { bits, rows, cols })
560 }
561}
562
563macro_rules! exact_arr_p_rm_cc {
564 ($($name:ident),+ $(,)?) => {
565 $(
566 #[doc = concat!("Elementwise [`ExactNum::", stringify!($name), "`]. `cc` is the constants cache, not a global.")]
567 pub fn $name(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
568 self.map_at(p, |x| x.$name(p, rm, cc))
569 }
570 )+
571 };
572}
573
574impl ExactNumArray {
575 pub fn new(p: usize) -> Self {
577 Self {
578 p,
579 vals: Vec::new(),
580 rows: 0,
581 cols: 0,
582 }
583 }
584
585 pub fn filled(p: usize, n: usize, fill: &ExactNum) -> Self {
587 let mut v = fill.clone();
588 let _ = v.set_precision(p, RoundingMode::ToEven);
589 Self {
590 p,
591 vals: alloc::vec![v; n],
592 rows: 1,
593 cols: n,
594 }
595 }
596
597 pub fn filled_2d(p: usize, rows: usize, cols: usize, fill: &ExactNum) -> Option<Self> {
599 let n = rows.checked_mul(cols)?;
600 let mut v = fill.clone();
601 let _ = v.set_precision(p, RoundingMode::ToEven);
602 Some(Self {
603 p,
604 vals: alloc::vec![v; n],
605 rows,
606 cols,
607 })
608 }
609
610 pub fn from_values(p: usize, vals: &[ExactNum]) -> Self {
612 Self {
613 p,
614 vals: vals
615 .iter()
616 .map(|x| {
617 let mut y = x.clone();
618 let _ = y.set_precision(p, RoundingMode::ToEven);
619 y
620 })
621 .collect(),
622 rows: 1,
623 cols: vals.len(),
624 }
625 }
626
627 pub fn from_shape(p: usize, rows: usize, cols: usize, vals: &[ExactNum]) -> Option<Self> {
629 let n = rows.checked_mul(cols)?;
630 if n != vals.len() {
631 return None;
632 }
633 Some(Self {
634 p,
635 vals: vals
636 .iter()
637 .map(|x| {
638 let mut y = x.clone();
639 let _ = y.set_precision(p, RoundingMode::ToEven);
640 y
641 })
642 .collect(),
643 rows,
644 cols,
645 })
646 }
647
648 #[cfg(any(test, feature = "random"))]
650 pub fn random_fill(
651 shape: (usize, usize),
652 dist: &crate::RandomDist,
653 p: usize,
654 rm: RoundingMode,
655 cc: &mut Consts,
656 ) -> Option<Self> {
657 let (rows, cols) = shape;
658 let n = rows.checked_mul(cols)?;
659 let mut vals = Vec::with_capacity(n);
660 for _ in 0..n {
661 let v = match dist {
662 crate::RandomDist::Uniform(a, b) => ExactNum::random_uniform(a, b, p, rm),
663 crate::RandomDist::Normal(mu, sigma) => {
664 ExactNum::random_gaussian(mu, sigma, p, rm, cc)
665 }
666 crate::RandomDist::Exponential(lambda) => {
667 ExactNum::random_exponential(lambda, p, rm, cc)
668 }
669 };
670 vals.push(v);
671 }
672 Some(Self {
673 p,
674 vals,
675 rows,
676 cols,
677 })
678 }
679
680 pub(crate) fn from_parts(
681 p: usize,
682 rows: usize,
683 cols: usize,
684 vals: Vec<ExactNum>,
685 ) -> Result<Self, Error> {
686 let n = rows.checked_mul(cols).ok_or(Error::InvalidArgument)?;
687 if n != vals.len() {
688 return Err(Error::InvalidArgument);
689 }
690 Ok(Self {
691 p,
692 vals,
693 rows,
694 cols,
695 })
696 }
697
698 pub fn precision(&self) -> usize {
700 self.p
701 }
702
703 pub fn shape(&self) -> (usize, usize) {
705 (self.rows, self.cols)
706 }
707
708 pub fn reshape(&self, rows: usize, cols: usize) -> Option<Self> {
710 let n = rows.checked_mul(cols)?;
711 if n != self.vals.len() {
712 return None;
713 }
714 Some(Self {
715 p: self.p,
716 vals: self.vals.clone(),
717 rows,
718 cols,
719 })
720 }
721
722 pub fn len(&self) -> usize {
724 self.vals.len()
725 }
726
727 pub fn is_empty(&self) -> bool {
729 self.vals.is_empty()
730 }
731
732 pub fn get(&self, i: usize) -> Option<&ExactNum> {
734 self.vals.get(i)
735 }
736
737 pub fn get2(&self, i: usize, j: usize) -> Option<&ExactNum> {
739 if i >= self.rows || j >= self.cols {
740 return None;
741 }
742 self.get(i * self.cols + j)
743 }
744
745 pub fn as_slice(&self) -> &[ExactNum] {
747 &self.vals
748 }
749
750 pub fn add(&self, rhs: &Self) -> Option<Self> {
752 self.zip(rhs, |a, b| a.add(b, self.p, RoundingMode::ToEven))
753 }
754
755 pub fn add_scalar(&self, s: &ExactNum) -> Self {
757 Self {
758 p: self.p,
759 vals: self
760 .vals
761 .iter()
762 .map(|x| x.add(s, self.p, RoundingMode::ToEven))
763 .collect(),
764 rows: self.rows,
765 cols: self.cols,
766 }
767 }
768
769 pub fn sub(&self, rhs: &Self) -> Option<Self> {
771 self.zip(rhs, |a, b| a.sub(b, self.p, RoundingMode::ToEven))
772 }
773
774 pub fn mul(&self, rhs: &Self) -> Option<Self> {
776 self.zip(rhs, |a, b| a.mul(b, self.p, RoundingMode::ToEven))
777 }
778
779 pub fn mul_scalar(&self, s: &ExactNum) -> Self {
781 Self {
782 p: self.p,
783 vals: self
784 .vals
785 .iter()
786 .map(|x| x.mul(s, self.p, RoundingMode::ToEven))
787 .collect(),
788 rows: self.rows,
789 cols: self.cols,
790 }
791 }
792
793 pub fn div(&self, rhs: &Self) -> Option<Self> {
795 self.zip(rhs, |a, b| a.div(b, self.p, RoundingMode::ToEven))
796 }
797
798 pub fn sum(&self) -> ExactNum {
800 let mut acc = ExactNum::from_u8(0, self.p);
801 for v in &self.vals {
802 acc = acc.add(v, self.p, RoundingMode::ToEven);
803 }
804 acc
805 }
806
807 pub fn dot(&self, rhs: &Self) -> Option<ExactNum> {
809 if self.len() != rhs.len() {
810 return None;
811 }
812 let mut acc = ExactNum::from_u8(0, self.p);
813 for (a, b) in self.vals.iter().zip(rhs.vals.iter()) {
814 let t = a.mul(b, self.p, RoundingMode::ToEven);
815 acc = acc.add(&t, self.p, RoundingMode::ToEven);
816 }
817 Some(acc)
818 }
819
820 pub fn matmul(&self, rhs: &Self) -> Option<Self> {
822 if self.cols != rhs.rows {
823 return None;
824 }
825 let m = self.rows;
826 let k = self.cols;
827 let n = rhs.cols;
828 let mut vals = Vec::with_capacity(m.checked_mul(n)?);
829 for i in 0..m {
830 for j in 0..n {
831 let mut acc = ExactNum::from_u8(0, self.p);
832 for t in 0..k {
833 let prod = self.vals[i * k + t].mul(
834 &rhs.vals[t * n + j],
835 self.p,
836 RoundingMode::ToEven,
837 );
838 acc = acc.add(&prod, self.p, RoundingMode::ToEven);
839 }
840 vals.push(acc);
841 }
842 }
843 Some(Self {
844 p: self.p,
845 vals,
846 rows: m,
847 cols: n,
848 })
849 }
850
851 pub fn int(&self) -> Self {
853 self.map_at(self.p, |x| x.int())
854 }
855 pub fn fract(&self) -> Self {
857 self.map_at(self.p, |x| x.fract())
858 }
859 pub fn ceil(&self) -> Self {
861 self.map_at(self.p, |x| x.ceil())
862 }
863 pub fn floor(&self) -> Self {
865 self.map_at(self.p, |x| x.floor())
866 }
867 pub fn round(&self, n: usize, rm: RoundingMode) -> Self {
869 self.map_at(self.p, |x| x.round(n, rm))
870 }
871 pub fn abs(&self) -> Self {
873 self.map_at(self.p, |x| x.abs())
874 }
875 pub fn signum(&self) -> Self {
877 self.map_at(self.p, |x| x.signum())
878 }
879 pub fn neg(&self) -> Self {
881 self.map_at(self.p, |x| x.neg())
882 }
883 pub fn reciprocal(&self, p: usize, rm: RoundingMode) -> Self {
885 self.map_at(p, |x| x.reciprocal(p, rm))
886 }
887 pub fn nth_root(&self, n: usize, p: usize, rm: RoundingMode) -> Self {
889 self.map_at(p, |x| x.nth_root(n, p, rm))
890 }
891 pub fn powi(&self, n: usize, p: usize, rm: RoundingMode) -> Self {
893 self.map_at(p, |x| x.powi(n, p, rm))
894 }
895 pub fn powsi(&self, n: isize, p: usize, rm: RoundingMode) -> Self {
897 self.map_at(p, |x| x.powsi(n, p, rm))
898 }
899
900 exact_arr_p_rm_cc!(
901 sin,
902 cos,
903 tan,
904 asin,
905 acos,
906 atan,
907 sinh,
908 cosh,
909 tanh,
910 asinh,
911 acosh,
912 atanh,
913 exp,
914 exp2,
915 exp10,
916 expm1,
917 ln,
918 log2,
919 log10,
920 log1p,
921 erf,
922 erfc,
923 gamma,
924 ln_gamma,
925 digamma,
926 ei,
927 si,
928 ci,
929 li,
930 fresnel_s,
931 fresnel_c,
932 ai,
933 bi,
934 elliptic_k,
935 elliptic_e_complete,
936 rem_pi,
937 );
938
939 pub fn sin_cos(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> (Self, Self) {
941 let mut s = Vec::with_capacity(self.vals.len());
942 let mut c = Vec::with_capacity(self.vals.len());
943 for x in &self.vals {
944 let (sv, cv) = x.sin_cos(p, rm, cc);
945 s.push(sv);
946 c.push(cv);
947 }
948 (
949 Self {
950 p,
951 vals: s,
952 rows: self.rows,
953 cols: self.cols,
954 },
955 Self {
956 p,
957 vals: c,
958 rows: self.rows,
959 cols: self.cols,
960 },
961 )
962 }
963
964 pub fn sinh_cosh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> (Self, Self) {
966 let mut s = Vec::with_capacity(self.vals.len());
967 let mut c = Vec::with_capacity(self.vals.len());
968 for x in &self.vals {
969 let (sv, cv) = x.sinh_cosh(p, rm, cc);
970 s.push(sv);
971 c.push(cv);
972 }
973 (
974 Self {
975 p,
976 vals: s,
977 rows: self.rows,
978 cols: self.cols,
979 },
980 Self {
981 p,
982 vals: c,
983 rows: self.rows,
984 cols: self.cols,
985 },
986 )
987 }
988
989 pub fn sqrt(&self, p: usize, rm: RoundingMode) -> Self {
991 self.map_at(p, |x| x.sqrt(p, rm))
992 }
993
994 pub fn cbrt(&self, p: usize, rm: RoundingMode) -> Self {
996 self.map_at(p, |x| x.cbrt(p, rm))
997 }
998
999 pub fn legendre_p(&self, n: u32, p: usize, rm: RoundingMode) -> Self {
1001 self.map_at(p, |x| x.legendre_p(n, p, rm))
1002 }
1003
1004 pub fn assoc_legendre_p(&self, n: u32, m: i32, p: usize, rm: RoundingMode) -> Self {
1006 self.map_at(p, |x| x.assoc_legendre_p(n, m, p, rm))
1007 }
1008
1009 pub fn hypot(&self, other: &ExactNum, p: usize, rm: RoundingMode) -> Self {
1011 self.map_at(p, |x| x.hypot(other, p, rm))
1012 }
1013
1014 pub fn atan2(&self, x: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1016 self.map_at(p, |y| y.atan2(x, p, rm, cc))
1017 }
1018
1019 pub fn pow(&self, n: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1021 self.map_at(p, |x| x.pow(n, p, rm, cc))
1022 }
1023
1024 pub fn log(&self, base: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1026 self.map_at(p, |x| x.log(base, p, rm, cc))
1027 }
1028
1029 pub fn gammainc(&self, x: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1031 self.map_at(p, |s| s.gammainc(x, p, rm, cc))
1032 }
1033
1034 pub fn gammainc_upper(
1036 &self,
1037 x: &ExactNum,
1038 p: usize,
1039 rm: RoundingMode,
1040 cc: &mut Consts,
1041 ) -> Self {
1042 self.map_at(p, |s| s.gammainc_upper(x, p, rm, cc))
1043 }
1044
1045 pub fn bessel_j(&self, n: usize, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1047 self.map_at(p, |x| x.bessel_j(n, p, rm, cc))
1048 }
1049
1050 pub fn bessel_j_nu(&self, nu: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1052 self.map_at(p, |x| x.bessel_j_nu(nu, p, rm, cc))
1053 }
1054
1055 pub fn bessel_y(&self, nu: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1057 self.map_at(p, |x| x.bessel_y(nu, p, rm, cc))
1058 }
1059
1060 pub fn bessel_i(&self, nu: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1062 self.map_at(p, |x| x.bessel_i(nu, p, rm, cc))
1063 }
1064
1065 pub fn bessel_k(&self, nu: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1067 self.map_at(p, |x| x.bessel_k(nu, p, rm, cc))
1068 }
1069
1070 pub fn elliptic_f(&self, m: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1072 self.map_at(p, |x| x.elliptic_f(m, p, rm, cc))
1073 }
1074
1075 pub fn elliptic_e(&self, m: &ExactNum, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
1077 self.map_at(p, |x| x.elliptic_e(m, p, rm, cc))
1078 }
1079
1080 pub fn elliptic_pi_complete(
1082 &self,
1083 m: &ExactNum,
1084 p: usize,
1085 rm: RoundingMode,
1086 cc: &mut Consts,
1087 ) -> Self {
1088 self.map_at(p, |n| n.elliptic_pi_complete(m, p, rm, cc))
1089 }
1090
1091 pub fn elliptic_pi(
1093 &self,
1094 x: &ExactNum,
1095 m: &ExactNum,
1096 p: usize,
1097 rm: RoundingMode,
1098 cc: &mut Consts,
1099 ) -> Self {
1100 self.map_at(p, |n| n.elliptic_pi(x, m, p, rm, cc))
1101 }
1102
1103 pub fn hypergeom_2f1(
1105 &self,
1106 b: &ExactNum,
1107 c: &ExactNum,
1108 z: &ExactNum,
1109 p: usize,
1110 rm: RoundingMode,
1111 cc: &mut Consts,
1112 ) -> Self {
1113 self.map_at(p, |a| a.hypergeom_2f1(b, c, z, p, rm, cc))
1114 }
1115
1116 pub fn betainc(
1118 &self,
1119 b: &ExactNum,
1120 x: &ExactNum,
1121 p: usize,
1122 rm: RoundingMode,
1123 cc: &mut Consts,
1124 ) -> Self {
1125 self.map_at(p, |a| a.betainc(b, x, p, rm, cc))
1126 }
1127
1128 pub fn lu_decomp(&self, p: usize, rm: RoundingMode) -> Option<(Self, Self, Vec<usize>)> {
1134 let n = self.rows;
1135 let m = self.cols;
1136 if n == 0 || m == 0 {
1137 return None;
1138 }
1139 let kmax = n.min(m);
1140 let mut a = try_clone_vals(&self.vals)?;
1141 for v in &mut a {
1142 let _ = v.set_precision(p, rm);
1143 }
1144 let mut perm = try_alloc_vec(n, 0usize)?;
1145 for (i, slot) in perm.iter_mut().enumerate() {
1146 *slot = i;
1147 }
1148 let mut lvals = try_alloc_vec(n.checked_mul(n)?, ExactNum::from_u8(0, p))?;
1149 for i in 0..n {
1150 lvals[i * n + i] = ExactNum::from_u8(1, p);
1151 }
1152 let ix = |r: usize, c: usize| r * m + c;
1153 for k in 0..kmax {
1154 let mut piv = k;
1155 let mut best = a[ix(k, k)].abs();
1156 for r in (k + 1)..n {
1157 let t = a[ix(r, k)].abs();
1158 if matches!(t.cmp(&best), Some(c) if c > 0) {
1159 best = t;
1160 piv = r;
1161 }
1162 }
1163 if a[ix(piv, k)].is_zero() {
1164 return None;
1165 }
1166 if piv != k {
1167 for c in 0..m {
1168 a.swap(ix(k, c), ix(piv, c));
1169 }
1170 for c in 0..k {
1171 lvals.swap(k * n + c, piv * n + c);
1172 }
1173 perm.swap(k, piv);
1174 }
1175 let akk = a[ix(k, k)].clone();
1176 for i in (k + 1)..n {
1177 let lik = a[ix(i, k)].div(&akk, p, rm);
1178 lvals[i * n + k] = lik.clone();
1179 a[ix(i, k)] = ExactNum::from_u8(0, p);
1180 for j in (k + 1)..m {
1181 let t = lik.mul(&a[ix(k, j)], p, rm);
1182 a[ix(i, j)] = a[ix(i, j)].sub(&t, p, rm);
1183 }
1184 }
1185 }
1186 Some((
1187 Self {
1188 p,
1189 vals: lvals,
1190 rows: n,
1191 cols: n,
1192 },
1193 Self {
1194 p,
1195 vals: a,
1196 rows: n,
1197 cols: m,
1198 },
1199 perm,
1200 ))
1201 }
1202
1203 pub fn transpose(&self) -> Self {
1205 let mut vals = Vec::with_capacity(self.vals.len());
1206 for j in 0..self.cols {
1207 for i in 0..self.rows {
1208 vals.push(self.vals[i * self.cols + j].clone());
1209 }
1210 }
1211 Self {
1212 p: self.p,
1213 vals,
1214 rows: self.cols,
1215 cols: self.rows,
1216 }
1217 }
1218
1219 pub fn qr_decomp(&self, p: usize, rm: RoundingMode) -> Option<(Self, Self)> {
1225 let m = self.rows;
1226 let n = self.cols;
1227 if m == 0 || n == 0 {
1228 return None;
1229 }
1230 let k = m.min(n);
1231 let mut q = try_alloc_vec(m.checked_mul(k)?, ExactNum::from_u8(0, p))?;
1232 let mut r = try_alloc_vec(k.checked_mul(n)?, ExactNum::from_u8(0, p))?;
1233 let a = |row: usize, col: usize| -> ExactNum {
1234 let mut v = self.vals[row * n + col].clone();
1235 let _ = v.set_precision(p, rm);
1236 v
1237 };
1238 for j in 0..n {
1239 let mut v: Vec<ExactNum> = (0..m).map(|i| a(i, j)).collect();
1240 let jlim = j.min(k);
1241 for i in 0..jlim {
1242 let mut dot = ExactNum::from_u8(0, p);
1243 for t in 0..m {
1244 let qi = q[t * k + i].clone();
1245 dot = dot.add(&qi.mul(&v[t], p, rm), p, rm);
1246 }
1247 r[i * n + j] = dot.clone();
1248 for t in 0..m {
1249 let qi = q[t * k + i].clone();
1250 v[t] = v[t].sub(&dot.mul(&qi, p, rm), p, rm);
1251 }
1252 }
1253 if j < k {
1254 let mut nrm = ExactNum::from_u8(0, p);
1255 for t in 0..m {
1256 nrm = nrm.add(&v[t].mul(&v[t], p, rm), p, rm);
1257 }
1258 nrm = nrm.sqrt(p, rm);
1259 r[j * n + j] = nrm.clone();
1260 if !nrm.is_zero() {
1261 for t in 0..m {
1262 q[t * k + j] = v[t].div(&nrm, p, rm);
1263 }
1264 }
1265 }
1266 }
1267 Some((
1268 Self {
1269 p,
1270 vals: q,
1271 rows: m,
1272 cols: k,
1273 },
1274 Self {
1275 p,
1276 vals: r,
1277 rows: k,
1278 cols: n,
1279 },
1280 ))
1281 }
1282
1283 pub fn svd_decomp(&self, p: usize, rm: RoundingMode) -> Option<(Self, Self, Self)> {
1291 let m = self.rows;
1292 let n = self.cols;
1293 if m == 0 || n == 0 {
1294 return None;
1295 }
1296 for v in &self.vals {
1297 if v.is_nan() || v.is_inf() {
1298 return None;
1299 }
1300 }
1301 if m < n {
1302 let (ut, s, vtt) = self.transpose().svd_decomp(p, rm)?;
1303 return Some((vtt.transpose(), s, ut.transpose()));
1304 }
1305 svd_decomp_tall(self, p, rm)
1306 }
1307
1308 pub fn eigen_decomp(&self, p: usize, rm: RoundingMode) -> Option<(Self, Self)> {
1316 let n = self.rows;
1317 if n == 0 || n != self.cols {
1318 return None;
1319 }
1320 for v in &self.vals {
1321 if v.is_nan() || v.is_inf() {
1322 return None;
1323 }
1324 }
1325 for i in 0..n {
1326 for j in 0..i {
1327 let aij = svd_copy_prec(&self.vals[i * n + j], p, rm);
1328 let aji = svd_copy_prec(&self.vals[j * n + i], p, rm);
1329 if aij.cmp(&aji) != Some(0) {
1330 return None;
1331 }
1332 }
1333 }
1334 eigen_decomp_sym(self, p, rm)
1335 }
1336
1337 pub fn fft(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Option<Self> {
1344 fft_dit(self, false, p, rm, cc)
1345 }
1346
1347 pub fn ifft(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Option<Self> {
1350 fft_dit(self, true, p, rm, cc)
1351 }
1352
1353 fn zip(&self, rhs: &Self, op: impl Fn(&ExactNum, &ExactNum) -> ExactNum) -> Option<Self> {
1354 if self.rows != rhs.rows || self.cols != rhs.cols {
1355 return None;
1356 }
1357 Some(Self {
1358 p: self.p,
1359 vals: self
1360 .vals
1361 .iter()
1362 .zip(rhs.vals.iter())
1363 .map(|(a, b)| op(a, b))
1364 .collect(),
1365 rows: self.rows,
1366 cols: self.cols,
1367 })
1368 }
1369
1370 fn map_at(&self, p: usize, mut op: impl FnMut(&ExactNum) -> ExactNum) -> Self {
1371 Self {
1372 p,
1373 vals: self.vals.iter().map(|x| op(x)).collect(),
1374 rows: self.rows,
1375 cols: self.cols,
1376 }
1377 }
1378}
1379
1380fn try_alloc_vec<T: Clone>(n: usize, fill: T) -> Option<Vec<T>> {
1381 let mut v = Vec::new();
1382 v.try_reserve_exact(n).ok()?;
1383 v.resize(n, fill);
1384 Some(v)
1385}
1386
1387fn try_clone_vals(src: &[ExactNum]) -> Option<Vec<ExactNum>> {
1388 let mut v = Vec::new();
1389 v.try_reserve_exact(src.len()).ok()?;
1390 v.extend(src.iter().cloned());
1391 Some(v)
1392}
1393
1394fn svd_zero(p: usize) -> ExactNum {
1395 ExactNum::from_u8(0, p)
1396}
1397
1398fn svd_one(p: usize) -> ExactNum {
1399 ExactNum::from_u8(1, p)
1400}
1401
1402fn svd_copy_prec(x: &ExactNum, p: usize, rm: RoundingMode) -> ExactNum {
1403 let mut y = x.clone();
1404 let _ = y.set_precision(p, rm);
1405 y
1406}
1407
1408fn svd_identity(n: usize, p: usize) -> Option<Vec<ExactNum>> {
1409 let mut v = try_alloc_vec(n.checked_mul(n)?, svd_zero(p))?;
1410 for i in 0..n {
1411 v[i * n + i] = svd_one(p);
1412 }
1413 Some(v)
1414}
1415
1416fn svd_norm(xs: &[ExactNum], p: usize, rm: RoundingMode) -> ExactNum {
1417 let mut n = svd_zero(p);
1418 for x in xs {
1419 n = n.hypot(x, p, rm);
1420 }
1421 n
1422}
1423
1424fn svd_householder(
1425 x: &[ExactNum],
1426 p: usize,
1427 rm: RoundingMode,
1428) -> Option<(Vec<ExactNum>, ExactNum)> {
1429 if x.is_empty() {
1430 return None;
1431 }
1432 let norm = svd_norm(x, p, rm);
1433 if norm.is_zero() {
1434 return None;
1435 }
1436 let mut v = x.to_vec();
1437 let signed = if v[0].is_negative() { norm.neg() } else { norm };
1438 v[0] = v[0].add(&signed, p, rm);
1439 let mut vtv = svd_zero(p);
1440 for vi in &v {
1441 vtv = vtv.add(&vi.mul(vi, p, rm), p, rm);
1442 }
1443 if vtv.is_zero() {
1444 return None;
1445 }
1446 let beta = ExactNum::from_u8(2, p).div(&vtv, p, rm);
1447 Some((v, beta))
1448}
1449
1450fn svd_apply_house_left(
1451 a: &mut [ExactNum],
1452 cols: usize,
1453 row0: usize,
1454 col0: usize,
1455 v: &[ExactNum],
1456 beta: &ExactNum,
1457 p: usize,
1458 rm: RoundingMode,
1459) {
1460 let vlen = v.len();
1461 for j in col0..cols {
1462 let mut s = svd_zero(p);
1463 for i in 0..vlen {
1464 s = s.add(&v[i].mul(&a[(row0 + i) * cols + j], p, rm), p, rm);
1465 }
1466 s = s.mul(beta, p, rm);
1467 for i in 0..vlen {
1468 let t = s.mul(&v[i], p, rm);
1469 let idx = (row0 + i) * cols + j;
1470 a[idx] = a[idx].sub(&t, p, rm);
1471 }
1472 }
1473}
1474
1475fn svd_apply_house_right(
1476 a: &mut [ExactNum],
1477 rows: usize,
1478 cols: usize,
1479 row0: usize,
1480 col0: usize,
1481 v: &[ExactNum],
1482 beta: &ExactNum,
1483 p: usize,
1484 rm: RoundingMode,
1485) {
1486 let vlen = v.len();
1487 for i in row0..rows {
1488 let mut s = svd_zero(p);
1489 for t in 0..vlen {
1490 s = s.add(&a[i * cols + col0 + t].mul(&v[t], p, rm), p, rm);
1491 }
1492 s = s.mul(beta, p, rm);
1493 for t in 0..vlen {
1494 let tt = s.mul(&v[t], p, rm);
1495 let idx = i * cols + col0 + t;
1496 a[idx] = a[idx].sub(&tt, p, rm);
1497 }
1498 }
1499}
1500
1501fn svd_rotg(
1502 a: &ExactNum,
1503 b: &ExactNum,
1504 p: usize,
1505 rm: RoundingMode,
1506) -> (ExactNum, ExactNum, ExactNum) {
1507 let r = a.hypot(b, p, rm);
1508 if r.is_zero() {
1509 return (svd_one(p), svd_zero(p), r);
1510 }
1511 (a.div(&r, p, rm), b.div(&r, p, rm), r)
1512}
1513
1514fn svd_apply_givens_cols(
1515 mat: &mut [ExactNum],
1516 rows: usize,
1517 cols: usize,
1518 j0: usize,
1519 j1: usize,
1520 cs: &ExactNum,
1521 sn: &ExactNum,
1522 p: usize,
1523 rm: RoundingMode,
1524) {
1525 for i in 0..rows {
1526 let a = mat[i * cols + j0].clone();
1527 let b = mat[i * cols + j1].clone();
1528 mat[i * cols + j0] = cs.mul(&a, p, rm).add(&sn.mul(&b, p, rm), p, rm);
1529 mat[i * cols + j1] = cs.mul(&b, p, rm).sub(&sn.mul(&a, p, rm), p, rm);
1530 }
1531}
1532
1533fn svd_negligible(e: &ExactNum, d0: &ExactNum, d1: &ExactNum, p: usize, rm: RoundingMode) -> bool {
1534 if e.is_zero() {
1535 return true;
1536 }
1537 if e.is_nan() || e.is_inf() {
1538 return false;
1539 }
1540 let scale = d0.abs().add(&d1.abs(), p, rm);
1541 if scale.is_zero() {
1542 return e.is_zero();
1543 }
1544 match (e.abs().exponent(), scale.exponent()) {
1545 (Some(ee), Some(se)) => ee < se - (p as i32 - SVD_CONV_GUARD_BITS),
1546 _ => false,
1547 }
1548}
1549
1550fn svd_wilkinson_shift(
1551 d_prev: &ExactNum,
1552 d_last: &ExactNum,
1553 e_prev: &ExactNum,
1554 e_last: &ExactNum,
1555 p: usize,
1556 rm: RoundingMode,
1557) -> ExactNum {
1558 let two = ExactNum::from_u8(2, p);
1559 let b = d_prev
1560 .add(d_last, p, rm)
1561 .mul(&d_prev.sub(d_last, p, rm), p, rm)
1562 .add(&e_prev.mul(e_prev, p, rm), p, rm)
1563 .div(&two, p, rm);
1564 let t = d_last.mul(e_last, p, rm);
1565 let c = t.mul(&t, p, rm);
1566 if b.is_zero() && c.is_zero() {
1567 return svd_zero(p);
1568 }
1569 let disc = b.mul(&b, p, rm).add(&c, p, rm).sqrt(p, rm);
1570 let signed = if b.is_negative() { disc.neg() } else { disc };
1571 let denom = b.add(&signed, p, rm);
1572 if denom.is_zero() {
1573 return svd_zero(p);
1574 }
1575 c.div(&denom, p, rm)
1576}
1577
1578fn svd_qr_sweep(
1579 d: &mut [ExactNum],
1580 e: &mut [ExactNum],
1581 u: &mut [ExactNum],
1582 v: &mut [ExactNum],
1583 m: usize,
1584 n: usize,
1585 p_blk: usize,
1586 q_blk: usize,
1587 p: usize,
1588 rm: RoundingMode,
1589) {
1590 let last = q_blk - 1;
1591 let e_prev = if last >= p_blk + 2 { e[last - 2].clone() } else { svd_zero(p) };
1592 let shift = svd_wilkinson_shift(&d[last - 1], &d[last], &e_prev, &e[last - 1], p, rm);
1593 let mut f = d[p_blk]
1594 .add(&d[last], p, rm)
1595 .mul(&d[p_blk].sub(&d[last], p, rm), p, rm)
1596 .add(&shift, p, rm);
1597 let mut g = d[p_blk].mul(&e[p_blk], p, rm);
1598 for j in p_blk..last {
1599 let (cs, sn, r) = svd_rotg(&f, &g, p, rm);
1600 if j > p_blk {
1601 e[j - 1] = r;
1602 }
1603 let dj = d[j].clone();
1604 let ej = e[j].clone();
1605 let dj1 = d[j + 1].clone();
1606 f = cs.mul(&dj, p, rm).add(&sn.mul(&ej, p, rm), p, rm);
1607 e[j] = cs.mul(&ej, p, rm).sub(&sn.mul(&dj, p, rm), p, rm);
1608 g = sn.mul(&dj1, p, rm);
1609 d[j + 1] = cs.mul(&dj1, p, rm);
1610 svd_apply_givens_cols(v, n, n, j, j + 1, &cs, &sn, p, rm);
1611
1612 let (cs, sn, r) = svd_rotg(&f, &g, p, rm);
1613 d[j] = r;
1614 let ej = e[j].clone();
1615 let dj1 = d[j + 1].clone();
1616 f = cs.mul(&ej, p, rm).add(&sn.mul(&dj1, p, rm), p, rm);
1617 d[j + 1] = cs.mul(&dj1, p, rm).sub(&sn.mul(&ej, p, rm), p, rm);
1618 if j + 1 < last {
1619 g = sn.mul(&e[j + 1], p, rm);
1620 e[j + 1] = cs.mul(&e[j + 1], p, rm);
1621 }
1622 svd_apply_givens_cols(u, m, m, j, j + 1, &cs, &sn, p, rm);
1623 }
1624 e[last - 1] = f;
1625}
1626
1627fn svd_zero_last_super(
1628 d: &mut [ExactNum],
1629 e: &mut [ExactNum],
1630 v: &mut [ExactNum],
1631 n: usize,
1632 p_blk: usize,
1633 q_blk: usize,
1634 p: usize,
1635 rm: RoundingMode,
1636) {
1637 let k = q_blk - 1;
1638 let mut f = e[k - 1].clone();
1639 e[k - 1] = svd_zero(p);
1640 for j in (p_blk..k).rev() {
1641 let (cs, sn, t) = svd_rotg(&d[j], &f, p, rm);
1642 d[j] = t;
1643 if j > p_blk {
1644 f = sn.neg().mul(&e[j - 1], p, rm);
1645 e[j - 1] = cs.mul(&e[j - 1], p, rm);
1646 }
1647 svd_apply_givens_cols(v, n, n, j, k, &cs, &sn, p, rm);
1648 }
1649}
1650
1651fn svd_zero_first_super(
1652 d: &mut [ExactNum],
1653 e: &mut [ExactNum],
1654 u: &mut [ExactNum],
1655 m: usize,
1656 p_blk: usize,
1657 q_blk: usize,
1658 p: usize,
1659 rm: RoundingMode,
1660) {
1661 let mut f = e[p_blk].clone();
1662 e[p_blk] = svd_zero(p);
1663 for j in (p_blk + 1)..q_blk {
1664 let (cs, sn, t) = svd_rotg(&d[j], &f, p, rm);
1665 d[j] = t;
1666 if j + 1 < q_blk {
1667 f = sn.neg().mul(&e[j], p, rm);
1668 e[j] = cs.mul(&e[j], p, rm);
1669 }
1670 svd_apply_givens_cols(u, m, m, p_blk, j, &cs, &sn, p, rm);
1671 }
1672}
1673
1674fn svd_take_cols(vals: &[ExactNum], rows: usize, cols: usize, k: usize, p: usize) -> ExactNumArray {
1675 let mut out = Vec::with_capacity(rows * k);
1676 for i in 0..rows {
1677 for j in 0..k {
1678 out.push(vals[i * cols + j].clone());
1679 }
1680 }
1681 ExactNumArray {
1682 p,
1683 vals: out,
1684 rows,
1685 cols: k,
1686 }
1687}
1688
1689fn svd_vt_from_v(v: &[ExactNum], n: usize, k: usize, p: usize) -> ExactNumArray {
1690 let mut out = Vec::with_capacity(k * n);
1691 for j in 0..k {
1692 for i in 0..n {
1693 out.push(v[i * n + j].clone());
1694 }
1695 }
1696 ExactNumArray {
1697 p,
1698 vals: out,
1699 rows: k,
1700 cols: n,
1701 }
1702}
1703
1704fn svd_decomp_tall(
1705 a0: &ExactNumArray,
1706 p: usize,
1707 rm: RoundingMode,
1708) -> Option<(ExactNumArray, ExactNumArray, ExactNumArray)> {
1709 let m = a0.rows;
1710 let n = a0.cols;
1711 let mut a: Vec<ExactNum> = a0.vals.iter().map(|x| svd_copy_prec(x, p, rm)).collect();
1712 let mut u = svd_identity(m, p)?;
1713 let mut v = svd_identity(n, p)?;
1714
1715 for k in 0..n {
1716 let x: Vec<ExactNum> = (k..m).map(|i| a[i * n + k].clone()).collect();
1717 if let Some((hv, beta)) = svd_householder(&x, p, rm) {
1718 svd_apply_house_left(&mut a, n, k, k, &hv, &beta, p, rm);
1719 svd_apply_house_right(&mut u, m, m, 0, k, &hv, &beta, p, rm);
1720 }
1721 if k + 1 < n {
1722 let x: Vec<ExactNum> = ((k + 1)..n).map(|j| a[k * n + j].clone()).collect();
1723 if let Some((hv, beta)) = svd_householder(&x, p, rm) {
1724 svd_apply_house_right(&mut a, m, n, k, k + 1, &hv, &beta, p, rm);
1725 svd_apply_house_right(&mut v, n, n, 0, k + 1, &hv, &beta, p, rm);
1726 }
1727 }
1728 }
1729
1730 let mut d: Vec<ExactNum> = (0..n).map(|i| a[i * n + i].clone()).collect();
1731 let mut e: Vec<ExactNum> = if n >= 2 {
1732 (0..n - 1).map(|i| a[i * n + i + 1].clone()).collect()
1733 } else {
1734 Vec::new()
1735 };
1736
1737 let max_sweeps = SVD_ITER_MAX.saturating_mul(n.max(1) as u32);
1738 let mut sweeps = 0u32;
1739 loop {
1740 if n == 1 {
1741 break;
1742 }
1743 for i in 0..n - 1 {
1744 if svd_negligible(&e[i], &d[i], &d[i + 1], p, rm) {
1745 e[i] = svd_zero(p);
1746 }
1747 }
1748 if e.iter().all(|x| x.is_zero()) {
1749 break;
1750 }
1751 if sweeps >= max_sweeps {
1752 return None;
1753 }
1754 let mut q = n;
1755 while q > 1 && e[q - 2].is_zero() {
1756 q -= 1;
1757 }
1758 let mut p_blk = q - 1;
1759 while p_blk > 0 && !e[p_blk - 1].is_zero() {
1760 p_blk -= 1;
1761 }
1762 if q - p_blk < 2 {
1763 break;
1764 }
1765
1766 let mut did_split = false;
1767 for i in p_blk..q {
1768 let el = if i > p_blk { e[i - 1].clone() } else { svd_zero(p) };
1769 let er = if i + 1 < q { e[i].clone() } else { svd_zero(p) };
1770 if svd_negligible(&d[i], &el, &er, p, rm) {
1771 d[i] = svd_zero(p);
1772 if i == q - 1 && i > p_blk {
1773 svd_zero_last_super(&mut d, &mut e, &mut v, n, p_blk, q, p, rm);
1774 } else if i < q - 1 {
1775 svd_zero_first_super(&mut d, &mut e, &mut u, m, i, q, p, rm);
1776 }
1777 did_split = true;
1778 break;
1779 }
1780 }
1781 if did_split {
1782 sweeps += 1;
1783 continue;
1784 }
1785 for di in d.iter().chain(e.iter()) {
1786 if di.is_nan() || di.is_inf() {
1787 return None;
1788 }
1789 }
1790 svd_qr_sweep(&mut d, &mut e, &mut u, &mut v, m, n, p_blk, q, p, rm);
1791 sweeps += 1;
1792 }
1793
1794 for i in 0..n {
1795 if d[i].is_negative() {
1796 d[i] = d[i].neg();
1797 for r in 0..m {
1798 let idx = r * m + i;
1799 u[idx] = u[idx].neg();
1800 }
1801 }
1802 }
1803 for i in 0..n {
1804 let mut best = i;
1805 for j in (i + 1)..n {
1806 if matches!(d[j].cmp(&d[best]), Some(c) if c > 0) {
1807 best = j;
1808 }
1809 }
1810 if best != i {
1811 d.swap(i, best);
1812 for r in 0..m {
1813 u.swap(r * m + i, r * m + best);
1814 }
1815 for r in 0..n {
1816 v.swap(r * n + i, r * n + best);
1817 }
1818 }
1819 }
1820
1821 let k = n;
1822 let mut sigma = try_alloc_vec(k.checked_mul(k)?, svd_zero(p))?;
1823 for i in 0..k {
1824 sigma[i * k + i] = d[i].clone();
1825 }
1826 Some((
1827 svd_take_cols(&u, m, m, k, p),
1828 ExactNumArray {
1829 p,
1830 vals: sigma,
1831 rows: k,
1832 cols: k,
1833 },
1834 svd_vt_from_v(&v, n, k, p),
1835 ))
1836}
1837
1838fn eigen_wilkinson(
1839 a: &ExactNum,
1840 b: &ExactNum,
1841 c: &ExactNum,
1842 p: usize,
1843 rm: RoundingMode,
1844) -> ExactNum {
1845 let half = svd_one(p).div(&ExactNum::from_u8(2, p), p, rm);
1846 let delta = a.sub(c, p, rm).mul(&half, p, rm);
1847 if delta.is_zero() && b.is_zero() {
1848 return c.clone();
1849 }
1850 let h = delta.hypot(b, p, rm);
1851 let signed = if delta.is_negative() { h.neg() } else { h };
1852 let denom = delta.add(&signed, p, rm);
1853 if denom.is_zero() {
1854 return c.sub(&b.abs(), p, rm);
1855 }
1856 c.sub(&b.mul(b, p, rm).div(&denom, p, rm), p, rm)
1857}
1858
1859fn eigen_qr_sweep(
1860 d: &mut [ExactNum],
1861 e: &mut [ExactNum],
1862 q: &mut [ExactNum],
1863 n: usize,
1864 p_blk: usize,
1865 q_blk: usize,
1866 p: usize,
1867 rm: RoundingMode,
1868) {
1869 let last = q_blk - 1;
1870 let mu = eigen_wilkinson(&d[last - 1], &e[last - 1], &d[last], p, rm);
1871 let mut f = d[p_blk].sub(&mu, p, rm);
1872 let mut g = e[p_blk].clone();
1873 let two = ExactNum::from_u8(2, p);
1874 for k in p_blk..last {
1875 let (cs, sn, r) = svd_rotg(&f, &g, p, rm);
1876 if k > p_blk {
1877 e[k - 1] = r;
1878 }
1879 let d0 = d[k].clone();
1880 let ee = e[k].clone();
1881 let d1 = d[k + 1].clone();
1882 let c2 = cs.mul(&cs, p, rm);
1883 let s2 = sn.mul(&sn, p, rm);
1884 let cs2 = cs.mul(&sn, p, rm);
1885 let two_cse = two.mul(&cs2.mul(&ee, p, rm), p, rm);
1886 d[k] = c2
1887 .mul(&d0, p, rm)
1888 .add(&two_cse, p, rm)
1889 .add(&s2.mul(&d1, p, rm), p, rm);
1890 d[k + 1] = s2
1891 .mul(&d0, p, rm)
1892 .sub(&two_cse, p, rm)
1893 .add(&c2.mul(&d1, p, rm), p, rm);
1894 e[k] = cs2
1895 .mul(&d1.sub(&d0, p, rm), p, rm)
1896 .add(&c2.sub(&s2, p, rm).mul(&ee, p, rm), p, rm);
1897 svd_apply_givens_cols(q, n, n, k, k + 1, &cs, &sn, p, rm);
1898 if k + 1 < last {
1899 let ek1 = e[k + 1].clone();
1900 f = e[k].clone();
1901 g = sn.mul(&ek1, p, rm);
1902 e[k + 1] = cs.mul(&ek1, p, rm);
1903 }
1904 }
1905}
1906
1907fn eigen_decomp_sym(
1908 a0: &ExactNumArray,
1909 p: usize,
1910 rm: RoundingMode,
1911) -> Option<(ExactNumArray, ExactNumArray)> {
1912 let n = a0.rows;
1913 let mut a: Vec<ExactNum> = a0.vals.iter().map(|x| svd_copy_prec(x, p, rm)).collect();
1914 let mut q = svd_identity(n, p)?;
1915 for k in 0..n.saturating_sub(2) {
1916 let x: Vec<ExactNum> = ((k + 1)..n).map(|i| a[i * n + k].clone()).collect();
1917 if let Some((hv, beta)) = svd_householder(&x, p, rm) {
1918 svd_apply_house_left(&mut a, n, k + 1, k, &hv, &beta, p, rm);
1919 svd_apply_house_right(&mut a, n, n, 0, k + 1, &hv, &beta, p, rm);
1920 svd_apply_house_right(&mut q, n, n, 0, k + 1, &hv, &beta, p, rm);
1921 }
1922 }
1923 let mut d: Vec<ExactNum> = (0..n).map(|i| a[i * n + i].clone()).collect();
1924 let mut e: Vec<ExactNum> = if n >= 2 {
1925 (0..n - 1).map(|i| a[i * n + i + 1].clone()).collect()
1926 } else {
1927 Vec::new()
1928 };
1929
1930 let max_sweeps = EIGEN_ITER_MAX.saturating_mul(n.max(1) as u32);
1931 let mut sweeps = 0u32;
1932 loop {
1933 if n == 1 {
1934 break;
1935 }
1936 for i in 0..n - 1 {
1937 if svd_negligible(&e[i], &d[i], &d[i + 1], p, rm) {
1938 e[i] = svd_zero(p);
1939 }
1940 }
1941 if e.iter().all(|x| x.is_zero()) {
1942 break;
1943 }
1944 if sweeps >= max_sweeps {
1945 return None;
1946 }
1947 let mut q_blk = n;
1948 while q_blk > 1 && e[q_blk - 2].is_zero() {
1949 q_blk -= 1;
1950 }
1951 let mut p_blk = q_blk - 1;
1952 while p_blk > 0 && !e[p_blk - 1].is_zero() {
1953 p_blk -= 1;
1954 }
1955 if q_blk - p_blk < 2 {
1956 break;
1957 }
1958 for di in d.iter().chain(e.iter()) {
1959 if di.is_nan() || di.is_inf() {
1960 return None;
1961 }
1962 }
1963 eigen_qr_sweep(&mut d, &mut e, &mut q, n, p_blk, q_blk, p, rm);
1964 sweeps += 1;
1965 }
1966
1967 for i in 0..n {
1968 let mut best = i;
1969 for j in (i + 1)..n {
1970 if matches!(d[j].cmp(&d[best]), Some(c) if c > 0) {
1971 best = j;
1972 }
1973 }
1974 if best != i {
1975 d.swap(i, best);
1976 for r in 0..n {
1977 q.swap(r * n + i, r * n + best);
1978 }
1979 }
1980 }
1981 Some((
1982 ExactNumArray {
1983 p,
1984 vals: d,
1985 rows: 1,
1986 cols: n,
1987 },
1988 ExactNumArray {
1989 p,
1990 vals: q,
1991 rows: n,
1992 cols: n,
1993 },
1994 ))
1995}
1996
1997fn fft_from_len(n: usize, p: usize) -> ExactNum {
1998 ExactNum::from_word(n as crate::defs::Word, p)
1999}
2000
2001fn fft_bitrev(mut i: usize, logn: u32) -> usize {
2002 let mut r = 0usize;
2003 for _ in 0..logn {
2004 r = (r << 1) | (i & 1);
2005 i >>= 1;
2006 }
2007 r
2008}
2009
2010fn fft_split(
2011 a: &ExactNumArray,
2012 p: usize,
2013 rm: RoundingMode,
2014) -> Option<(usize, Vec<ExactNum>, Vec<ExactNum>)> {
2015 let (rows, cols) = a.shape();
2016 let pack = |n: usize,
2017 re: Vec<ExactNum>,
2018 im: Vec<ExactNum>|
2019 -> Option<(usize, Vec<ExactNum>, Vec<ExactNum>)> {
2020 if n == 0 || !n.is_power_of_two() || n > FFT_MAX_POINTS {
2021 return None;
2022 }
2023 Some((n, re, im))
2024 };
2025 if rows == 2 && cols > 0 {
2026 let mut re = Vec::with_capacity(cols);
2027 let mut im = Vec::with_capacity(cols);
2028 for j in 0..cols {
2029 let r = svd_copy_prec(&a.vals[j], p, rm);
2030 let i = svd_copy_prec(&a.vals[cols + j], p, rm);
2031 if r.is_nan() || r.is_inf() || i.is_nan() || i.is_inf() {
2032 return None;
2033 }
2034 re.push(r);
2035 im.push(i);
2036 }
2037 pack(cols, re, im)
2038 } else if (rows == 1 && cols > 0) || (cols == 1 && rows > 0) {
2039 let n = a.vals.len();
2040 let mut re = Vec::with_capacity(n);
2041 for v in &a.vals {
2042 let r = svd_copy_prec(v, p, rm);
2043 if r.is_nan() || r.is_inf() {
2044 return None;
2045 }
2046 re.push(r);
2047 }
2048 pack(n, re, alloc::vec![svd_zero(p); n])
2049 } else {
2050 None
2051 }
2052}
2053
2054fn fft_dit(
2055 a: &ExactNumArray,
2056 inverse: bool,
2057 p: usize,
2058 rm: RoundingMode,
2059 cc: &mut Consts,
2060) -> Option<ExactNumArray> {
2061 let (n, mut re, mut im) = fft_split(a, p, rm)?;
2062 let logn = n.trailing_zeros();
2063 for i in 0..n {
2064 let j = fft_bitrev(i, logn);
2065 if j > i {
2066 re.swap(i, j);
2067 im.swap(i, j);
2068 }
2069 }
2070 let two_pi = ExactNum::from_u8(2, p).mul(&cc.pi(p, rm), p, rm);
2071 let mut m = 2usize;
2072 while m <= n {
2073 let ang = two_pi.div(&fft_from_len(m, p), p, rm);
2074 let (sn, cs) = ang.sin_cos(p, rm, cc);
2075 let wm_re = cs;
2076 let wm_im = if inverse { sn } else { sn.neg() };
2077 let half = m / 2;
2078 let mut k = 0usize;
2079 while k < n {
2080 let mut w_re = svd_one(p);
2081 let mut w_im = svd_zero(p);
2082 for j in 0..half {
2083 let t = k + j + half;
2084 let u = k + j;
2085 let tr = w_re.mul(&re[t], p, rm).sub(&w_im.mul(&im[t], p, rm), p, rm);
2086 let ti = w_re.mul(&im[t], p, rm).add(&w_im.mul(&re[t], p, rm), p, rm);
2087 let ur = re[u].clone();
2088 let ui = im[u].clone();
2089 re[u] = ur.add(&tr, p, rm);
2090 im[u] = ui.add(&ti, p, rm);
2091 re[t] = ur.sub(&tr, p, rm);
2092 im[t] = ui.sub(&ti, p, rm);
2093 let nr = w_re.mul(&wm_re, p, rm).sub(&w_im.mul(&wm_im, p, rm), p, rm);
2094 let ni = w_re.mul(&wm_im, p, rm).add(&w_im.mul(&wm_re, p, rm), p, rm);
2095 w_re = nr;
2096 w_im = ni;
2097 }
2098 k += m;
2099 }
2100 m *= 2;
2101 }
2102 if inverse {
2103 let inv_n = svd_one(p).div(&fft_from_len(n, p), p, rm);
2104 for i in 0..n {
2105 re[i] = re[i].mul(&inv_n, p, rm);
2106 im[i] = im[i].mul(&inv_n, p, rm);
2107 }
2108 }
2109 let mut vals = Vec::with_capacity(n.checked_mul(2)?);
2110 vals.extend(re);
2111 vals.extend(im);
2112 Some(ExactNumArray {
2113 p,
2114 vals,
2115 rows: 2,
2116 cols: n,
2117 })
2118}
2119
2120#[cfg(test)]
2121mod tests {
2122 use super::*;
2123
2124 #[test]
2125 fn bin64_array_add_dot() {
2126 let one = Ieee64::from_i32(1);
2127 let two = Ieee64::from_i32(2);
2128 let a = Ieee64Array::from_values(&[one, two]);
2129 let b = Ieee64Array::from_values(&[two, one]);
2130 let s = a.add(&b).unwrap();
2131 assert_eq!(s.get(0).unwrap().to_bits(), Ieee64::from_i32(3).to_bits());
2132 assert_eq!(s.get(1).unwrap().to_bits(), Ieee64::from_i32(3).to_bits());
2133 let d = a.dot(&b).unwrap();
2134 assert_eq!(d.to_bits(), Ieee64::from_i32(4).to_bits());
2135 assert_eq!(a.sum().to_bits(), Ieee64::from_i32(3).to_bits());
2136 let scaled = a.mul_scalar(two);
2137 assert_eq!(scaled.get(0).unwrap().to_bits(), two.to_bits());
2138 }
2139
2140 #[test]
2141 fn bin32_array_sqrt() {
2142 let four = Ieee32::from_i32(4);
2143 let a = Ieee32Array::filled(3, four);
2144 let r = a.sqrt();
2145 assert_eq!(r.get(0).unwrap().to_bits(), Ieee32::from_i32(2).to_bits());
2146 assert_eq!(r.len(), 3);
2147 }
2148
2149 #[test]
2150 fn bin64_array_exp_sin() {
2151 let mut cc = Consts::new().unwrap();
2152 let z = Ieee64Array::from_values(&[Ieee64::ZERO]);
2153 let e = z.exp(&mut cc);
2154 assert_eq!(e.get(0).unwrap().to_bits(), Ieee64::from_i32(1).to_bits());
2155 let s = z.sin(&mut cc);
2156 assert!(s.get(0).unwrap().is_zero());
2157 }
2158
2159 #[test]
2160 fn exact_array_add() {
2161 let p = 64;
2162 let one = ExactNum::from_u8(1, p);
2163 let two = ExactNum::from_u8(2, p);
2164 let a = ExactNumArray::from_values(p, &[one.clone(), two.clone()]);
2165 let b = ExactNumArray::from_values(p, &[two.clone(), one.clone()]);
2166 let s = a.add(&b).unwrap();
2167 assert_eq!(s.get(0).unwrap().cmp(&ExactNum::from_u8(3, p)), Some(0));
2168 let d = a.dot(&b).unwrap();
2169 assert_eq!(d.cmp(&ExactNum::from_u8(4, p)), Some(0));
2170 }
2171
2172 #[test]
2173 fn bin64_matmul_2x2() {
2174 let a = Ieee64Array::from_shape(
2175 2,
2176 2,
2177 &[
2178 Ieee64::from_i32(1),
2179 Ieee64::from_i32(2),
2180 Ieee64::from_i32(3),
2181 Ieee64::from_i32(4),
2182 ],
2183 )
2184 .unwrap();
2185 let b = Ieee64Array::from_shape(
2186 2,
2187 2,
2188 &[
2189 Ieee64::from_i32(5),
2190 Ieee64::from_i32(6),
2191 Ieee64::from_i32(7),
2192 Ieee64::from_i32(8),
2193 ],
2194 )
2195 .unwrap();
2196 let c = a.matmul(&b).unwrap();
2197 assert_eq!(c.shape(), (2, 2));
2198 assert_eq!(
2199 c.get2(0, 0).unwrap().to_bits(),
2200 Ieee64::from_i32(19).to_bits()
2201 );
2202 assert_eq!(
2203 c.get2(0, 1).unwrap().to_bits(),
2204 Ieee64::from_i32(22).to_bits()
2205 );
2206 assert_eq!(
2207 c.get2(1, 0).unwrap().to_bits(),
2208 Ieee64::from_i32(43).to_bits()
2209 );
2210 assert_eq!(
2211 c.get2(1, 1).unwrap().to_bits(),
2212 Ieee64::from_i32(50).to_bits()
2213 );
2214 }
2215
2216 #[test]
2217 fn bin64_matmul_identity() {
2218 let i2 = Ieee64Array::from_shape(
2219 2,
2220 2,
2221 &[Ieee64::from_i32(1), Ieee64::ZERO, Ieee64::ZERO, Ieee64::from_i32(1)],
2222 )
2223 .unwrap();
2224 let a = Ieee64Array::from_shape(
2225 2,
2226 2,
2227 &[
2228 Ieee64::from_i32(1),
2229 Ieee64::from_i32(2),
2230 Ieee64::from_i32(3),
2231 Ieee64::from_i32(4),
2232 ],
2233 )
2234 .unwrap();
2235 let c = i2.matmul(&a).unwrap();
2236 assert_eq!(
2237 c.get2(0, 0).unwrap().to_bits(),
2238 Ieee64::from_i32(1).to_bits()
2239 );
2240 assert_eq!(
2241 c.get2(0, 1).unwrap().to_bits(),
2242 Ieee64::from_i32(2).to_bits()
2243 );
2244 assert_eq!(
2245 c.get2(1, 0).unwrap().to_bits(),
2246 Ieee64::from_i32(3).to_bits()
2247 );
2248 assert_eq!(
2249 c.get2(1, 1).unwrap().to_bits(),
2250 Ieee64::from_i32(4).to_bits()
2251 );
2252 }
2253
2254 #[test]
2255 fn bin64_matmul_shape_mismatch() {
2256 let a = Ieee64Array::from_shape(2, 2, &[Ieee64::from_i32(1); 4]).unwrap();
2257 let b = Ieee64Array::from_shape(3, 1, &[Ieee64::from_i32(1); 3]).unwrap();
2258 assert!(a.matmul(&b).is_none());
2259 assert!(Ieee64Array::from_shape(2, 2, &[Ieee64::from_i32(1)]).is_none());
2260 let row = Ieee64Array::from_values(&[Ieee64::from_i32(1); 4]);
2261 assert_eq!(row.shape(), (1, 4));
2262 let sq = row.reshape(2, 2).unwrap();
2263 assert_eq!(sq.shape(), (2, 2));
2264 assert!(row.add(&sq).is_none());
2265 }
2266
2267 #[test]
2268 fn exact_matmul_2x2() {
2269 let p = 64;
2270 let n = |k: u8| ExactNum::from_u8(k, p);
2271 let a = ExactNumArray::from_shape(p, 2, 2, &[n(1), n(2), n(3), n(4)]).unwrap();
2272 let b = ExactNumArray::from_shape(p, 2, 2, &[n(5), n(6), n(7), n(8)]).unwrap();
2273 let c = a.matmul(&b).unwrap();
2274 assert_eq!(c.shape(), (2, 2));
2275 assert_eq!(c.get2(0, 0).unwrap().cmp(&n(19)), Some(0));
2276 assert_eq!(c.get2(0, 1).unwrap().cmp(&n(22)), Some(0));
2277 assert_eq!(c.get2(1, 0).unwrap().cmp(&n(43)), Some(0));
2278 assert_eq!(c.get2(1, 1).unwrap().cmp(&n(50)), Some(0));
2279 }
2280
2281 #[test]
2282 fn bin32_simd_add_mul_golds() {
2283 let one = Ieee32::from_i32(1);
2284 let two = Ieee32::from_i32(2);
2285 let four = Ieee32::from_i32(4);
2286 let a = Ieee32Array::from_values(&[one, two, one, two, one]);
2287 let b = Ieee32Array::from_values(&[one, two, two, one, one]);
2288 let s = a.add(&b).unwrap();
2289 assert_eq!(s.get(0).unwrap().to_bits(), two.to_bits());
2290 assert_eq!(s.get(1).unwrap().to_bits(), four.to_bits());
2291 assert_eq!(s.get(4).unwrap().to_bits(), two.to_bits());
2292 let half = Ieee32::from_bits(0x3F00_0000);
2293 let t = Ieee32Array::filled(4, two);
2294 let h = Ieee32Array::filled(4, half);
2295 let p = t.mul(&h).unwrap();
2296 assert_eq!(p.get(0).unwrap().to_bits(), one.to_bits());
2297 assert_eq!(p.get(3).unwrap().to_bits(), one.to_bits());
2298 }
2299
2300 #[test]
2301 fn ieee64_simd_1000_add_mul_div_sqrt() {
2302 const N: usize = 1000;
2303 let p = 128;
2304 let rm = RoundingMode::ToEven;
2305 let vals: Vec<Ieee64> = (1..=N as i32).map(Ieee64::from_i32).collect();
2306 let ones: Vec<Ieee64> = (0..N).map(|_| Ieee64::from_i32(1)).collect();
2307 let twos: Vec<Ieee64> = (0..N).map(|_| Ieee64::from_i32(2)).collect();
2308 let a = Ieee64Array::from_values(&vals);
2309 let one = Ieee64Array::from_values(&ones);
2310 let two = Ieee64Array::from_values(&twos);
2311 let add = a.add(&one).unwrap();
2312 let mul = a.mul(&two).unwrap();
2313 let div = a.div(&a).unwrap();
2314 let squares = a.mul(&a).unwrap();
2315 let sq = squares.sqrt();
2316 let sub = a.sub(&one).unwrap();
2317 let fma = a.fma(&one, &one).unwrap();
2318 assert_eq!(add.len(), N);
2319 for i in 0..N {
2320 let ai = a.get(i).unwrap();
2321 let oi = one.get(i).unwrap();
2322 let ti = two.get(i).unwrap();
2323 assert_eq!(add.get(i).unwrap().to_bits(), ai.add(oi).to_bits());
2324 assert_eq!(mul.get(i).unwrap().to_bits(), ai.mul(ti).to_bits());
2325 assert_eq!(div.get(i).unwrap().to_bits(), ai.div(ai).to_bits());
2326 assert_eq!(
2327 sq.get(i).unwrap().to_bits(),
2328 ai.mul(ai).sqrt().to_bits()
2329 );
2330 assert_eq!(sub.get(i).unwrap().to_bits(), ai.sub(oi).to_bits());
2331 assert_eq!(
2332 fma.get(i).unwrap().to_bits(),
2333 ai.mul_add(oi, oi).to_bits()
2334 );
2335 let xa = ai.to_exact(p);
2336 let x1 = oi.to_exact(p);
2337 let x2 = ti.to_exact(p);
2338 assert_eq!(
2339 add.get(i).unwrap().to_bits(),
2340 Ieee64::from_exact(&xa.add(&x1, p, rm)).to_bits()
2341 );
2342 assert_eq!(
2343 mul.get(i).unwrap().to_bits(),
2344 Ieee64::from_exact(&xa.mul(&x2, p, rm)).to_bits()
2345 );
2346 assert_eq!(
2347 div.get(i).unwrap().to_bits(),
2348 Ieee64::from_exact(&xa.div(&xa, p, rm)).to_bits()
2349 );
2350 let sqe = xa.mul(&xa, p, rm).sqrt(p, rm);
2351 assert_eq!(sq.get(i).unwrap().to_bits(), Ieee64::from_exact(&sqe).to_bits());
2352 }
2353 assert!(a.fma(&one, &Ieee64Array::from_values(&vals[..10])).is_none());
2354 }
2355
2356 #[test]
2357 fn array_ufunc_identities() {
2358 let mut cc = Consts::new().unwrap();
2359 let z32 = Ieee32Array::from_values(&[Ieee32::ZERO]);
2360 assert!(z32.asin(&mut cc).get(0).unwrap().is_zero());
2361 assert!(z32.expm1(&mut cc).get(0).unwrap().is_zero());
2362 assert!(z32.log1p(&mut cc).get(0).unwrap().is_zero());
2363 assert_eq!(
2364 z32.bessel_j(0, &mut cc).get(0).unwrap().to_bits(),
2365 Ieee32::from_i32(1).to_bits()
2366 );
2367 let one64 = Ieee64Array::from_values(&[Ieee64::from_i32(1)]);
2368 assert!(one64.ln_gamma(&mut cc).get(0).unwrap().is_zero());
2369 let p = 64;
2370 let rm = RoundingMode::ToEven;
2371 let z = ExactNumArray::from_values(p, &[ExactNum::from_u8(0, p)]);
2372 assert!(z.sinh(p, rm, &mut cc).get(0).unwrap().is_zero());
2373 let one = ExactNumArray::from_values(p, &[ExactNum::from_u8(1, p)]);
2374 assert_eq!(
2375 one.ln_gamma(p, rm, &mut cc)
2376 .get(0)
2377 .unwrap()
2378 .cmp(&ExactNum::from_u8(0, p)),
2379 Some(0)
2380 );
2381 let x = ExactNumArray::from_values(p, &[ExactNum::from_u8(3, p)]);
2382 assert_eq!(
2383 x.legendre_p(0, p, rm)
2384 .get(0)
2385 .unwrap()
2386 .cmp(&ExactNum::from_u8(1, p)),
2387 Some(0)
2388 );
2389 assert_eq!(
2390 x.floor().get(0).unwrap().cmp(&ExactNum::from_u8(3, p)),
2391 Some(0)
2392 );
2393 }
2394
2395 #[test]
2396 fn exact_array_sin_2x3_matches_scalar() {
2397 let p = 128;
2398 let rm = RoundingMode::ToEven;
2399 let mut cc = Consts::new().unwrap();
2400 let n = |k: u8| ExactNum::from_u8(k, p);
2401 let vals = [n(1), n(2), n(3), n(4), n(5), n(6)];
2402 let a = ExactNumArray::from_shape(p, 2, 3, &vals).unwrap();
2403 let s = a.sin(p, rm, &mut cc);
2404 assert_eq!(s.shape(), (2, 3));
2405 for i in 0..2 {
2406 for j in 0..3 {
2407 let want = vals[i * 3 + j].sin(p, rm, &mut cc);
2408 assert_eq!(s.get2(i, j).unwrap().cmp(&want), Some(0));
2409 }
2410 }
2411 let row = ExactNumArray::from_values(p, &[n(1), n(2)]);
2412 assert!(a.add(&row).is_none());
2413 }
2414
2415 #[test]
2416 fn exact_array_bessel_j_nu_matches_scalar() {
2417 let p = 128;
2418 let rm = RoundingMode::ToEven;
2419 let mut cc = Consts::new().unwrap();
2420 let half = ExactNum::from_u8(1, p).div(&ExactNum::from_u8(2, p), p, rm);
2421 let xs = [ExactNum::from_u8(1, p), ExactNum::from_u8(2, p), ExactNum::from_u8(3, p)];
2422 let a = ExactNumArray::from_values(p, &xs);
2423 let out = a.bessel_j_nu(&half, p, rm, &mut cc);
2424 for (i, x) in xs.iter().enumerate() {
2425 let want = x.bessel_j_nu(&half, p, rm, &mut cc);
2426 assert_eq!(out.get(i).unwrap().cmp(&want), Some(0));
2427 }
2428 let nan_in = ExactNumArray::from_values(p, &[ExactNum::nan(None)]);
2429 assert!(nan_in.sin(p, rm, &mut cc).get(0).unwrap().is_nan());
2430 }
2431
2432 fn perm_rows(a: &ExactNumArray, perm: &[usize]) -> ExactNumArray {
2433 let (n, m) = a.shape();
2434 let mut vals = Vec::with_capacity(n * m);
2435 for &r in perm {
2436 for c in 0..m {
2437 vals.push(a.get2(r, c).unwrap().clone());
2438 }
2439 }
2440 ExactNumArray::from_shape(a.precision(), n, m, &vals).unwrap()
2441 }
2442
2443 #[test]
2444 fn exact_lu_2x2_and_singular() {
2445 let p = 256;
2446 let rm = RoundingMode::ToEven;
2447 let n = |k: u8| ExactNum::from_u8(k, p);
2448 let a = ExactNumArray::from_shape(p, 2, 2, &[n(2), n(1), n(4), n(3)]).unwrap();
2449 let (l, u, perm) = a.lu_decomp(p, rm).expect("LU");
2450 let pa = perm_rows(&a, &perm);
2451 let lu = l.matmul(&u).expect("L*U");
2452 assert_eq!(lu.shape(), (2, 2));
2453 for i in 0..2 {
2454 for j in 0..2 {
2455 assert_eq!(
2456 lu.get2(i, j).unwrap().cmp(pa.get2(i, j).unwrap()),
2457 Some(0),
2458 "PA=LU at {i},{j}"
2459 );
2460 }
2461 }
2462 let sing = ExactNumArray::from_shape(p, 2, 2, &[n(1), n(2), n(2), n(4)]).unwrap();
2463 assert!(sing.lu_decomp(p, rm).is_none());
2464 }
2465
2466 fn near_num(a: &ExactNum, b: &ExactNum, p: usize) -> bool {
2467 let d = a.sub(b, p, RoundingMode::None).abs();
2468 d.is_zero() || d.exponent().unwrap_or(0) < -((p as i32) - 40)
2469 }
2470
2471 #[test]
2472 fn exact_qr_recon_orthog_rankdef() {
2473 let p = 256;
2474 let rm = RoundingMode::ToEven;
2475 let n = |k: u8| ExactNum::from_u8(k, p);
2476 let a = ExactNumArray::from_shape(p, 2, 2, &[n(2), n(1), n(4), n(3)]).unwrap();
2477 let (q, r) = a.qr_decomp(p, rm).expect("QR");
2478 let qr = q.matmul(&r).expect("Q*R");
2479 for i in 0..2 {
2480 for j in 0..2 {
2481 assert!(
2482 near_num(qr.get2(i, j).unwrap(), a.get2(i, j).unwrap(), p),
2483 "QR=A at {i},{j}"
2484 );
2485 }
2486 }
2487 let qtq = q.transpose().matmul(&q).expect("Q^T Q");
2488 let one = n(1);
2489 let zero = n(0);
2490 assert!(near_num(qtq.get2(0, 0).unwrap(), &one, p));
2491 assert!(near_num(qtq.get2(1, 1).unwrap(), &one, p));
2492 assert!(near_num(qtq.get2(0, 1).unwrap(), &zero, p));
2493 assert!(near_num(qtq.get2(1, 0).unwrap(), &zero, p));
2494
2495 let def = ExactNumArray::from_shape(p, 2, 2, &[n(1), n(2), n(2), n(4)]).unwrap();
2496 let (qd, rd) = def.qr_decomp(p, rm).expect("rank-def QR");
2497 let _ = qd;
2498 assert!(rd.get2(1, 1).unwrap().is_zero() || near_num(rd.get2(1, 1).unwrap(), &zero, p));
2499 let recon = qd.matmul(&rd).expect("Qd Rd");
2500 for i in 0..2 {
2501 for j in 0..2 {
2502 assert!(near_num(
2503 recon.get2(i, j).unwrap(),
2504 def.get2(i, j).unwrap(),
2505 p
2506 ));
2507 }
2508 }
2509 }
2510
2511 #[test]
2512 fn exact_svd_diag_3_2() {
2513 let p = 256;
2514 let rm = RoundingMode::ToEven;
2515 let n = |k: u8| ExactNum::from_u8(k, p);
2516 let a = ExactNumArray::from_shape(p, 2, 2, &[n(3), n(0), n(0), n(2)]).unwrap();
2517 let (u, s, vt) = a.svd_decomp(p, rm).expect("SVD");
2518 assert_eq!(s.get2(0, 0).unwrap().cmp(&n(3)), Some(0));
2519 assert_eq!(s.get2(1, 1).unwrap().cmp(&n(2)), Some(0));
2520 assert!(near_num(s.get2(0, 1).unwrap(), &n(0), p));
2521 assert!(near_num(s.get2(1, 0).unwrap(), &n(0), p));
2522 let us = u.matmul(&s).expect("U Σ");
2523 let recon = us.matmul(&vt).expect("U Σ V^T");
2524 for i in 0..2 {
2525 for j in 0..2 {
2526 assert!(
2527 near_num(recon.get2(i, j).unwrap(), a.get2(i, j).unwrap(), p),
2528 "UΣV^T=A at {i},{j}"
2529 );
2530 }
2531 }
2532 }
2533
2534 #[test]
2535 fn exact_svd_recon_orthog() {
2536 let p = 256;
2537 let rm = RoundingMode::ToEven;
2538 let n = |k: u8| ExactNum::from_u8(k, p);
2539 let a = ExactNumArray::from_shape(p, 2, 2, &[n(2), n(1), n(4), n(3)]).unwrap();
2540 let (u, s, vt) = a.svd_decomp(p, rm).expect("SVD");
2541 let us = u.matmul(&s).expect("U Σ");
2542 let recon = us.matmul(&vt).expect("U Σ V^T");
2543 for i in 0..2 {
2544 for j in 0..2 {
2545 assert!(
2546 near_num(recon.get2(i, j).unwrap(), a.get2(i, j).unwrap(), p),
2547 "UΣV^T=A at {i},{j}"
2548 );
2549 }
2550 }
2551 let utu = u.transpose().matmul(&u).expect("U^T U");
2552 let v = vt.transpose();
2553 let vtv = vt.matmul(&v).expect("V^T V");
2554 let one = n(1);
2555 let zero = n(0);
2556 for (name, g) in [("U^T U", &utu), ("V^T V", &vtv)] {
2557 assert!(near_num(g.get2(0, 0).unwrap(), &one, p), "{name}[0,0]");
2558 assert!(near_num(g.get2(1, 1).unwrap(), &one, p), "{name}[1,1]");
2559 assert!(near_num(g.get2(0, 1).unwrap(), &zero, p), "{name}[0,1]");
2560 assert!(near_num(g.get2(1, 0).unwrap(), &zero, p), "{name}[1,0]");
2561 }
2562 assert!(ExactNumArray::from_shape(p, 0, 0, &[])
2563 .unwrap()
2564 .svd_decomp(p, rm)
2565 .is_none());
2566
2567 let wide =
2568 ExactNumArray::from_shape(p, 2, 3, &[n(1), n(0), n(0), n(0), n(2), n(0)]).unwrap();
2569 let (uw, sw, vtw) = wide.svd_decomp(p, rm).expect("wide SVD");
2570 assert_eq!(sw.get2(0, 0).unwrap().cmp(&n(2)), Some(0));
2571 assert_eq!(sw.get2(1, 1).unwrap().cmp(&n(1)), Some(0));
2572 let usw = uw.matmul(&sw).expect("Uw Σw");
2573 let recw = usw.matmul(&vtw).expect("wide recon");
2574 for i in 0..2 {
2575 for j in 0..3 {
2576 assert!(near_num(
2577 recw.get2(i, j).unwrap(),
2578 wide.get2(i, j).unwrap(),
2579 p
2580 ));
2581 }
2582 }
2583 }
2584
2585 fn eigen_diag(evals: &ExactNumArray, p: usize) -> ExactNumArray {
2586 let n = evals.cols;
2587 let mut vals = Vec::with_capacity(n * n);
2588 let z = ExactNum::from_u8(0, p);
2589 for i in 0..n {
2590 for j in 0..n {
2591 if i == j {
2592 vals.push(evals.get(i).unwrap().clone());
2593 } else {
2594 vals.push(z.clone());
2595 }
2596 }
2597 }
2598 ExactNumArray::from_shape(p, n, n, &vals).unwrap()
2599 }
2600
2601 #[test]
2602 fn exact_eigen_sym_2x2() {
2603 let p = 256;
2604 let rm = RoundingMode::ToEven;
2605 let n = |k: u8| ExactNum::from_u8(k, p);
2606 let a = ExactNumArray::from_shape(p, 2, 2, &[n(2), n(1), n(1), n(2)]).unwrap();
2607 let (evals, v) = a.eigen_decomp(p, rm).expect("eigen");
2608 assert_eq!(evals.shape(), (1, 2));
2609 assert!(near_num(evals.get(0).unwrap(), &n(3), p));
2610 assert!(near_num(evals.get(1).unwrap(), &n(1), p));
2611 let vtv = v.transpose().matmul(&v).expect("V^T V");
2612 let one = n(1);
2613 let zero = n(0);
2614 assert!(near_num(vtv.get2(0, 0).unwrap(), &one, p));
2615 assert!(near_num(vtv.get2(1, 1).unwrap(), &one, p));
2616 assert!(near_num(vtv.get2(0, 1).unwrap(), &zero, p));
2617 assert!(near_num(vtv.get2(1, 0).unwrap(), &zero, p));
2618 let av = a.matmul(&v).expect("A V");
2619 let lam = eigen_diag(&evals, p);
2620 let vl = v.matmul(&lam).expect("V Λ");
2621 for i in 0..2 {
2622 for j in 0..2 {
2623 assert!(
2624 near_num(av.get2(i, j).unwrap(), vl.get2(i, j).unwrap(), p),
2625 "Av=λv at {i},{j}"
2626 );
2627 }
2628 }
2629 let vlvt = vl.matmul(&v.transpose()).expect("V Λ V^T");
2630 for i in 0..2 {
2631 for j in 0..2 {
2632 assert!(near_num(vlvt.get2(i, j).unwrap(), a.get2(i, j).unwrap(), p));
2633 }
2634 }
2635 let nosym = ExactNumArray::from_shape(p, 2, 2, &[n(1), n(2), n(0), n(1)]).unwrap();
2636 assert!(nosym.eigen_decomp(p, rm).is_none());
2637
2638 let a3 = ExactNumArray::from_shape(
2639 p,
2640 3,
2641 3,
2642 &[n(2), n(1), n(0), n(1), n(2), n(1), n(0), n(1), n(2)],
2643 )
2644 .unwrap();
2645 let (w3, v3) = a3.eigen_decomp(p, rm).expect("eigen 3");
2646 let s2 = n(2).sqrt(p, rm);
2647 let want = [n(2).add(&s2, p, rm), n(2), n(2).sub(&s2, p, rm)];
2648 for (i, wi) in want.iter().enumerate() {
2649 assert!(near_num(w3.get(i).unwrap(), wi, p), "λ[{i}]");
2650 }
2651 let av3 = a3.matmul(&v3).expect("A3 V");
2652 let vl3 = v3.matmul(&eigen_diag(&w3, p)).expect("V3 Λ");
2653 for i in 0..3 {
2654 for j in 0..3 {
2655 assert!(near_num(
2656 av3.get2(i, j).unwrap(),
2657 vl3.get2(i, j).unwrap(),
2658 p
2659 ));
2660 }
2661 }
2662 }
2663
2664 #[test]
2665 fn exact_fft_impulse_cosine_parseval() {
2666 let p = 256;
2667 let rm = RoundingMode::ToEven;
2668 let mut cc = Consts::new().unwrap();
2669 let n = |k: u8| ExactNum::from_u8(k, p);
2670 let impulse = ExactNumArray::from_values(p, &[n(1), n(0), n(0), n(0)]);
2671 let spec = impulse.fft(p, rm, &mut cc).expect("FFT impulse");
2672 assert_eq!(spec.shape(), (2, 4));
2673 for j in 0..4 {
2674 assert!(near_num(spec.get2(0, j).unwrap(), &n(1), p), "re[{j}]");
2675 assert!(near_num(spec.get2(1, j).unwrap(), &n(0), p), "im[{j}]");
2676 }
2677
2678 let n8 = ExactNum::from_u8(8, p);
2679 let two_pi = n(2).mul(&cc.pi(p, rm), p, rm);
2680 let mut cos_vals = Vec::with_capacity(8);
2681 for k in 0..8u8 {
2682 let kn = ExactNum::from_u8(k, p);
2683 let ang = two_pi.mul(&kn, p, rm).div(&n8, p, rm);
2684 cos_vals.push(ang.cos(p, rm, &mut cc));
2685 }
2686 let cosine = ExactNumArray::from_values(p, &cos_vals);
2687 let cspec = cosine.fft(p, rm, &mut cc).expect("FFT cos");
2688 let four = n(4);
2689 let zero = n(0);
2690 for j in 0..8 {
2691 let re = cspec.get2(0, j).unwrap();
2692 let im = cspec.get2(1, j).unwrap();
2693 if j == 1 || j == 7 {
2694 assert!(near_num(re, &four, p), "cos bin {j} re");
2695 } else {
2696 assert!(near_num(re, &zero, p), "cos bin {j} re");
2697 }
2698 assert!(near_num(im, &zero, p), "cos bin {j} im");
2699 }
2700
2701 let back = cspec.ifft(p, rm, &mut cc).expect("IFFT");
2702 for j in 0..8 {
2703 assert!(near_num(
2704 back.get2(0, j).unwrap(),
2705 cosine.get(j).unwrap(),
2706 p
2707 ));
2708 assert!(near_num(back.get2(1, j).unwrap(), &zero, p));
2709 }
2710
2711 let mut e_t = ExactNum::from_u8(0, p);
2712 let mut e_f = ExactNum::from_u8(0, p);
2713 for j in 0..8 {
2714 let x = cosine.get(j).unwrap();
2715 e_t = e_t.add(&x.mul(x, p, rm), p, rm);
2716 let xr = cspec.get2(0, j).unwrap();
2717 let xi = cspec.get2(1, j).unwrap();
2718 e_f = e_f
2719 .add(&xr.mul(xr, p, rm), p, rm)
2720 .add(&xi.mul(xi, p, rm), p, rm);
2721 }
2722 let parseval = e_f.div(&n8, p, rm);
2723 assert!(near_num(&parseval, &e_t, p));
2724 assert!(ExactNumArray::from_values(p, &[n(1), n(2), n(3)])
2725 .fft(p, rm, &mut cc)
2726 .is_none());
2727 }
2728}