Skip to main content

zenith_float_num/ieee_soft/
array.rs

1//! Dense row-major arrays of software IEEE values and `ExactNum`.
2//! A 1-D vector is stored as shape `(1, n)`.
3
4use 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
15/// QR sweeps allowed per singular value before [`ExactNumArray::svd_decomp`]
16/// returns `None`.
17const SVD_ITER_MAX: u32 = 64;
18/// QR sweeps allowed per eigenvalue before [`ExactNumArray::eigen_decomp`]
19/// returns `None`.
20const EIGEN_ITER_MAX: u32 = 64;
21/// Maximum length of [`ExactNumArray::fft`] / [`ExactNumArray::ifft`].
22const FFT_MAX_POINTS: usize = 4096;
23/// Bits of working precision reserved when a superdiagonal is compared to
24/// its neighboring diagonals.
25const 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/// Contiguous binary32 lanes (`u32` bits), row-major.
79#[derive(Clone, Debug)]
80pub struct Ieee32Array {
81    bits: Vec<u32>,
82    rows: usize,
83    cols: usize,
84}
85
86/// Contiguous binary64 lanes (`u64` bits), row-major.
87#[derive(Clone, Debug)]
88pub struct Ieee64Array {
89    bits: Vec<u64>,
90    rows: usize,
91    cols: usize,
92}
93
94/// Batch of `ExactNum` values at a shared precision `p`, row-major.
95#[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            /// Empty 0×0 array.
107            pub fn new() -> Self {
108                Self {
109                    bits: Vec::new(),
110                    rows: 0,
111                    cols: 0,
112                }
113            }
114
115            /// Row vector: `n` copies of `fill` (shape `(1, n)`).
116            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            /// `rows×cols` filled with `fill`.
125            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            /// From a slice of IEEE bit patterns as a row vector.
135            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            /// Row-major `rows×cols`. Length must be `rows*cols`.
144            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            /// From scalars as a row vector (shape `(1, n)`).
157            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            /// `(rows, cols)`.
166            pub fn shape(&self) -> (usize, usize) {
167                (self.rows, self.cols)
168            }
169
170            /// Reinterpret the same buffer as `rows×cols` when the product matches.
171            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            /// Number of lanes.
184            pub fn len(&self) -> usize {
185                self.bits.len()
186            }
187
188            /// True if there are no lanes.
189            pub fn is_empty(&self) -> bool {
190                self.bits.is_empty()
191            }
192
193            /// IEEE bit patterns.
194            pub fn as_bits(&self) -> &[$bits] {
195                &self.bits
196            }
197
198            /// Lane `i` in storage order, or `None` if out of range.
199            pub fn get(&self, i: usize) -> Option<$scalar> {
200                self.bits.get(i).copied().map($scalar::from_bits)
201            }
202
203            /// Entry `(i, j)`, or `None` if out of range.
204            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            /// Elementwise add. Shapes must match. Uses integer SIMD when the
212            /// architecture provides it (still the software IEEE kernel).
213            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            /// Add a scalar to every lane.
225            pub fn add_scalar(&self, s: $scalar) -> Self {
226                self.map(|x| x.add(s))
227            }
228
229            /// Elementwise sub. Shapes must match. Integer SIMD via sign-bit
230            /// flip then add.
231            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            /// Elementwise mul. Shapes must match. Integer SIMD significand
243            /// products on the all-normal path.
244            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            /// Multiply every lane by a scalar.
256            pub fn mul_scalar(&self, s: $scalar) -> Self {
257                self.map(|x| x.mul(s))
258            }
259
260            /// Elementwise div. Shapes must match. Integer SIMD unpack on the
261            /// all-normal path; significand quotient is integer `/` per lane.
262            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            /// Elementwise sqrt. Integer SIMD unpack on non-negative normals.
274            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            /// Elementwise fused multiply-add \(a\cdot b + c\). Shapes must match.
283            /// Integer SIMD significand products on the all-normal path.
284            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            /// Sequential IEEE sum (one rounding per add).
300            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            /// Sequential dot product (mul then add, two rounds per term).
309            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            /// Software matmul: `(m×k)(k×n) → (m×n)`. Sequential IEEE mul-then-add per term.
321            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            /// Widen each lane, apply `op`, IEEE-round back.
360            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            /// Elementwise complete `E(m)` via `ExactNum`.
411            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            /// Elementwise `cbrt` via `ExactNum`.
416            pub fn cbrt(&self) -> Self {
417                self.map_exact($p, |x| x.cbrt($p, RoundingMode::ToEven))
418            }
419
420            /// Elementwise `atan2(self, x)` via `ExactNum`.
421            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            /// Elementwise `hypot(self, other)` via `ExactNum`.
427            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            /// Elementwise `pow(self, n)` via `ExactNum`.
433            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            /// Elementwise `log(self, base)` via `ExactNum`.
439            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            /// Elementwise `γ(self, x)` via `ExactNum`.
445            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            /// Elementwise `Γ(self, x)` via `ExactNum`.
451            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            /// Elementwise integer-order `J_n(self)`.
457            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            /// Elementwise `J_ν(self)` for a shared real order.
462            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            /// Elementwise `Y_ν(self)`.
468            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            /// Elementwise `I_ν(self)`.
474            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            /// Elementwise `K_ν(self)`.
480            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            /// Elementwise `P_n(self)`.
486            pub fn legendre_p(&self, n: u32) -> Self {
487                self.map_exact($p, |x| x.legendre_p(n, $p, RoundingMode::ToEven))
488            }
489
490            /// Elementwise `P_n^m(self)`.
491            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            /// Elementwise `F(self | m)`.
496            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            /// Elementwise incomplete `E(self | m)`.
502            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            /// Elementwise complete `Π(n, m)` with `self = n`.
508            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            /// Elementwise `Π(self; x | m)`.
516            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            /// Elementwise `{}_2F_1(self, b; c; z)`.
525            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            /// Elementwise `I_x(self, b)`.
541            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    /// Empty 0×0 array at precision `p`.
576    pub fn new(p: usize) -> Self {
577        Self {
578            p,
579            vals: Vec::new(),
580            rows: 0,
581            cols: 0,
582        }
583    }
584
585    /// Row vector: `n` copies of `fill` (rounded to `p`).
586    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    /// `rows×cols` filled with `fill` (rounded to `p`).
598    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    /// From values as a row vector; each is rounded to `p`.
611    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    /// Row-major `rows×cols`. Length must be `rows*cols`.
628    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    /// Fill `shape` from `dist` at `(p, rm)`. `None` if the shape product overflows.
649    #[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    /// Shared precision.
699    pub fn precision(&self) -> usize {
700        self.p
701    }
702
703    /// `(rows, cols)`.
704    pub fn shape(&self) -> (usize, usize) {
705        (self.rows, self.cols)
706    }
707
708    /// Reinterpret the same buffer as `rows×cols` when the product matches.
709    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    /// Number of lanes.
723    pub fn len(&self) -> usize {
724        self.vals.len()
725    }
726
727    /// True if there are no lanes.
728    pub fn is_empty(&self) -> bool {
729        self.vals.is_empty()
730    }
731
732    /// Lane `i` in storage order.
733    pub fn get(&self, i: usize) -> Option<&ExactNum> {
734        self.vals.get(i)
735    }
736
737    /// Entry `(i, j)`, or `None` if out of range.
738    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    /// All values in row-major order.
746    pub fn as_slice(&self) -> &[ExactNum] {
747        &self.vals
748    }
749
750    /// Elementwise add at `p`.
751    pub fn add(&self, rhs: &Self) -> Option<Self> {
752        self.zip(rhs, |a, b| a.add(b, self.p, RoundingMode::ToEven))
753    }
754
755    /// Add a scalar to every lane.
756    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    /// Elementwise sub.
770    pub fn sub(&self, rhs: &Self) -> Option<Self> {
771        self.zip(rhs, |a, b| a.sub(b, self.p, RoundingMode::ToEven))
772    }
773
774    /// Elementwise mul.
775    pub fn mul(&self, rhs: &Self) -> Option<Self> {
776        self.zip(rhs, |a, b| a.mul(b, self.p, RoundingMode::ToEven))
777    }
778
779    /// Multiply every lane by a scalar.
780    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    /// Elementwise div.
794    pub fn div(&self, rhs: &Self) -> Option<Self> {
795        self.zip(rhs, |a, b| a.div(b, self.p, RoundingMode::ToEven))
796    }
797
798    /// Sequential sum at `p`.
799    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    /// Sequential dot product at `p`.
808    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    /// Software matmul: `(m×k)(k×n) → (m×n)`. Sequential mul-then-add at `p`.
821    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    /// Elementwise integer part.
852    pub fn int(&self) -> Self {
853        self.map_at(self.p, |x| x.int())
854    }
855    /// Elementwise fractional part.
856    pub fn fract(&self) -> Self {
857        self.map_at(self.p, |x| x.fract())
858    }
859    /// Elementwise `ceil`.
860    pub fn ceil(&self) -> Self {
861        self.map_at(self.p, |x| x.ceil())
862    }
863    /// Elementwise `floor`.
864    pub fn floor(&self) -> Self {
865        self.map_at(self.p, |x| x.floor())
866    }
867    /// Elementwise `round` with `n` binary fractional bits.
868    pub fn round(&self, n: usize, rm: RoundingMode) -> Self {
869        self.map_at(self.p, |x| x.round(n, rm))
870    }
871    /// Elementwise absolute value.
872    pub fn abs(&self) -> Self {
873        self.map_at(self.p, |x| x.abs())
874    }
875    /// Elementwise signum.
876    pub fn signum(&self) -> Self {
877        self.map_at(self.p, |x| x.signum())
878    }
879    /// Elementwise negation.
880    pub fn neg(&self) -> Self {
881        self.map_at(self.p, |x| x.neg())
882    }
883    /// Elementwise reciprocal.
884    pub fn reciprocal(&self, p: usize, rm: RoundingMode) -> Self {
885        self.map_at(p, |x| x.reciprocal(p, rm))
886    }
887    /// Elementwise `nth_root`.
888    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    /// Elementwise `powi`.
892    pub fn powi(&self, n: usize, p: usize, rm: RoundingMode) -> Self {
893        self.map_at(p, |x| x.powi(n, p, rm))
894    }
895    /// Elementwise `powsi`.
896    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    /// Elementwise `(sin, cos)` with a shared argument reduction.
940    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    /// Elementwise `(sinh, cosh)` with a shared evaluation.
965    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    /// Elementwise `sqrt`.
990    pub fn sqrt(&self, p: usize, rm: RoundingMode) -> Self {
991        self.map_at(p, |x| x.sqrt(p, rm))
992    }
993
994    /// Elementwise `cbrt`.
995    pub fn cbrt(&self, p: usize, rm: RoundingMode) -> Self {
996        self.map_at(p, |x| x.cbrt(p, rm))
997    }
998
999    /// Elementwise `P_n(self)`.
1000    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    /// Elementwise `P_n^m(self)`.
1005    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    /// Elementwise `hypot(self, other)`.
1010    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    /// Elementwise `atan2(self, x)`. `cc` is the constants cache, not a global.
1015    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    /// Elementwise `pow(self, n)`.
1020    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    /// Elementwise `log(self, base)`.
1025    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    /// Elementwise `γ(self, x)`.
1030    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    /// Elementwise `Γ(self, x)`.
1035    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    /// Elementwise integer-order `J_n(self)`.
1046    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    /// Elementwise `J_ν(self)`.
1051    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    /// Elementwise `Y_ν(self)`.
1056    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    /// Elementwise `I_ν(self)`.
1061    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    /// Elementwise `K_ν(self)`.
1066    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    /// Elementwise `F(self | m)`.
1071    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    /// Elementwise incomplete `E(self | m)`.
1076    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    /// Elementwise complete `Π(n, m)` with `self = n`.
1081    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    /// Elementwise `Π(self; x | m)`.
1092    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    /// Elementwise `{}_2F_1(self, b; c; z)`.
1104    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    /// Elementwise `I_x(self, b)`.
1117    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    /// LU with partial pivoting: `(L, U, P)` such that row `i` of `P·A` is
1129    /// original row `P[i]`, and `P·A = L·U` at `(p, rm)`.
1130    ///
1131    /// `L` is unit lower (`n×n`). `U` is upper (`n×m`). A zero pivot
1132    /// (singular) or a failed heap reserve (`MemoryAllocation`) returns `None`.
1133    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    /// Row–column transpose.
1204    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    /// Modified Gram–Schmidt QR at `(p, rm)`.
1220    ///
1221    /// Returns `(Q, R)` with `Q` `m×k` having orthonormal columns, `R` `k×n`
1222    /// upper triangular, `k = min(m, n)`. A rank-deficient column is a zero
1223    /// column of `Q` and a zero diagonal entry of `R` — not a panic.
1224    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    /// Golub–Reinsch SVD at `(p, rm)`.
1284    ///
1285    /// Returns `(U, Σ, V^T)` with `U` `m×k` (orthonormal columns), `Σ` `k×k`
1286    /// diagonal (non-negative, descending), `V^T` `k×n` (orthonormal rows),
1287    /// `k = min(m, n)`, so that `U · Σ · V^T = A` at working precision.
1288    /// Empty input, a NaN/Inf entry, a failed heap reserve, or failure to
1289    /// converge within `SVD_ITER_MAX` sweeps per singular value returns `None`.
1290    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    /// Symmetric QR eigendecomposition at `(p, rm)`.
1309    ///
1310    /// Returns `(Λ, V)` where `Λ` is a `1×n` row of eigenvalues (descending)
1311    /// and `V` is `n×n` with orthonormal columns, so `A V = V diag(Λ)` and
1312    /// `V diag(Λ) V^T = A` at working precision.
1313    /// Non-square, non-symmetric, empty, non-finite, or failure to converge
1314    /// within `EIGEN_ITER_MAX` sweeps per value returns `None`.
1315    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    /// Radix-2 Cooley–Tukey DFT at `(p, rm, cc)`.
1338    ///
1339    /// A `(1, n)` or `(n, 1)` array is real. A `(2, n)` array is complex
1340    /// (row 0 real, row 1 imaginary). `n` must be a power of two and at most
1341    /// `FFT_MAX_POINTS`. Returns a `(2, n)` spectrum (unnormalized).
1342    /// Empty, non-finite, or a bad shape returns `None`.
1343    pub fn fft(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Option<Self> {
1344        fft_dit(self, false, p, rm, cc)
1345    }
1346
1347    /// Inverse radix-2 DFT at `(p, rm, cc)`. Same layout as [`Self::fft`].
1348    /// The result is divided by `n` (unitary inverse of the unnormalized DFT).
1349    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}