Skip to main content

p3_matrix/
dense.rs

1use alloc::borrow::Cow;
2use alloc::vec;
3use alloc::vec::Vec;
4use core::borrow::{Borrow, BorrowMut};
5use core::marker::PhantomData;
6use core::ops::Deref;
7
8use p3_field::{
9    ExtensionField, Field, PackedValue, par_scale_slice_in_place, scale_slice_in_place_single_core,
10};
11use p3_maybe_rayon::prelude::*;
12use rand::distr::{Distribution, StandardUniform};
13use rand::{Rng, RngExt};
14use serde::{Deserialize, Serialize};
15use tracing::instrument;
16
17use crate::Matrix;
18
19/// A dense matrix in row-major format, with customizable backing storage.
20///
21/// The data is stored as a flat buffer, where rows are laid out consecutively.
22#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
23pub struct DenseMatrix<T, V = Vec<T>> {
24    /// Flat buffer of matrix values in row-major order.
25    pub values: V,
26    /// Number of columns in the matrix.
27    ///
28    /// The number of rows is implicitly determined as `values.len() / width`.
29    pub width: usize,
30    /// Marker for the element type `T`, unused directly.
31    ///
32    /// Required to retain type information when `V` does not own or contain `T`.
33    _phantom: PhantomData<T>,
34}
35
36pub type RowMajorMatrix<T> = DenseMatrix<T>;
37pub type RowMajorMatrixView<'a, T> = DenseMatrix<T, &'a [T]>;
38pub type RowMajorMatrixViewMut<'a, T> = DenseMatrix<T, &'a mut [T]>;
39pub type RowMajorMatrixCow<'a, T> = DenseMatrix<T, Cow<'a, [T]>>;
40
41pub trait DenseStorage<T>: Borrow<[T]> + Send + Sync {
42    fn to_vec(self) -> Vec<T>;
43}
44
45// Cow doesn't impl IntoOwned so we can't blanket it
46impl<T: Clone + Send + Sync> DenseStorage<T> for Vec<T> {
47    fn to_vec(self) -> Self {
48        self
49    }
50}
51
52impl<T: Clone + Send + Sync> DenseStorage<T> for &[T] {
53    fn to_vec(self) -> Vec<T> {
54        <[T]>::to_vec(self)
55    }
56}
57
58impl<T: Clone + Send + Sync> DenseStorage<T> for &mut [T] {
59    fn to_vec(self) -> Vec<T> {
60        <[T]>::to_vec(self)
61    }
62}
63
64impl<T: Clone + Send + Sync> DenseStorage<T> for Cow<'_, [T]> {
65    fn to_vec(self) -> Vec<T> {
66        self.into_owned()
67    }
68}
69
70impl<T: Clone + Send + Sync + Default> DenseMatrix<T> {
71    /// Create a new dense matrix of the given dimensions, backed by a `Vec`, and filled with
72    /// default values.
73    #[must_use]
74    pub fn default(width: usize, height: usize) -> Self {
75        Self::new(vec![T::default(); width * height], width)
76    }
77}
78
79impl<T: Clone + Send + Sync, S: DenseStorage<T>> DenseMatrix<T, S> {
80    /// Create a new dense matrix of the given dimensions, backed by the given storage.
81    ///
82    /// # Panics
83    /// Panics in debug builds if `values.len() % width != 0`. Release builds silently
84    /// construct a matrix whose row/column indexing is inconsistent with the storage —
85    /// callers must validate dimensions before calling.
86    #[must_use]
87    pub fn new(values: S, width: usize) -> Self {
88        debug_assert!(values.borrow().len().is_multiple_of(width));
89        Self {
90            values,
91            width,
92            _phantom: PhantomData,
93        }
94    }
95
96    /// Create a new RowMajorMatrix containing a single row.
97    #[must_use]
98    pub fn new_row(values: S) -> Self {
99        let width = values.borrow().len();
100        Self::new(values, width)
101    }
102
103    /// Create a new RowMajorMatrix containing a single column.
104    #[must_use]
105    pub fn new_col(values: S) -> Self {
106        Self::new(values, 1)
107    }
108
109    /// Get a view of the matrix, i.e. a reference to the underlying data.
110    pub fn as_view(&self) -> RowMajorMatrixView<'_, T> {
111        RowMajorMatrixView::new(self.values.borrow(), self.width)
112    }
113
114    /// Get a mutable view of the matrix, i.e. a mutable reference to the underlying data.
115    pub fn as_view_mut(&mut self) -> RowMajorMatrixViewMut<'_, T>
116    where
117        S: BorrowMut<[T]>,
118    {
119        RowMajorMatrixViewMut::new(self.values.borrow_mut(), self.width)
120    }
121
122    /// Copy the values from the given matrix into this matrix.
123    pub fn copy_from<S2>(&mut self, source: &DenseMatrix<T, S2>)
124    where
125        T: Copy,
126        S: BorrowMut<[T]>,
127        S2: DenseStorage<T>,
128    {
129        assert_eq!(self.dimensions(), source.dimensions());
130        // Equivalent to:
131        // self.values.borrow_mut().copy_from_slice(source.values.borrow());
132        self.par_rows_mut()
133            .zip(source.par_row_slices())
134            .for_each(|(dst, src)| {
135                dst.copy_from_slice(src);
136            });
137    }
138
139    /// Flatten an extension field matrix to a base field matrix.
140    pub fn flatten_to_base<F: Field>(self) -> RowMajorMatrix<F>
141    where
142        T: ExtensionField<F>,
143    {
144        let width = self.width * T::DIMENSION;
145        let values = T::flatten_to_base(self.values.to_vec());
146        RowMajorMatrix::new(values, width)
147    }
148
149    /// Get an iterator over the rows of the matrix.
150    pub fn row_slices(&self) -> impl DoubleEndedIterator<Item = &[T]> {
151        self.values.borrow().chunks_exact(self.width)
152    }
153
154    /// Get a parallel iterator over the rows of the matrix.
155    pub fn par_row_slices(&self) -> impl IndexedParallelIterator<Item = &[T]>
156    where
157        T: Sync,
158    {
159        self.values.borrow().par_chunks_exact(self.width)
160    }
161
162    /// Returns a slice of the given row.
163    ///
164    /// # Panics
165    /// Panics if `r` larger than self.height().
166    pub fn row_mut(&mut self, r: usize) -> &mut [T]
167    where
168        S: BorrowMut<[T]>,
169    {
170        &mut self.values.borrow_mut()[r * self.width..(r + 1) * self.width]
171    }
172
173    /// Get a mutable iterator over the rows of the matrix.
174    pub fn rows_mut(&mut self) -> impl Iterator<Item = &mut [T]>
175    where
176        S: BorrowMut<[T]>,
177    {
178        self.values.borrow_mut().chunks_exact_mut(self.width)
179    }
180
181    /// Get a mutable parallel iterator over the rows of the matrix.
182    pub fn par_rows_mut<'a>(&'a mut self) -> impl IndexedParallelIterator<Item = &'a mut [T]>
183    where
184        T: 'a + Send,
185        S: BorrowMut<[T]>,
186    {
187        self.values.borrow_mut().par_chunks_exact_mut(self.width)
188    }
189
190    /// Get a mutable iterator over the rows of the matrix which packs the rows into packed values.
191    ///
192    /// If `P::WIDTH` does not divide `self.width`, the remainder of the row will be returned as a
193    /// base slice.
194    pub fn horizontally_packed_row_mut<P>(&mut self, r: usize) -> (&mut [P], &mut [T])
195    where
196        P: PackedValue<Value = T>,
197        S: BorrowMut<[T]>,
198    {
199        P::pack_slice_with_suffix_mut(self.row_mut(r))
200    }
201
202    /// Scale the given row by the given value.
203    ///
204    /// # Panics
205    /// Panics if `r` larger than `self.height()`.
206    pub fn scale_row(&mut self, r: usize, scale: T)
207    where
208        T: Field,
209        S: BorrowMut<[T]>,
210    {
211        scale_slice_in_place_single_core(self.row_mut(r), scale);
212    }
213
214    /// Scale the given row by the given value.
215    ///
216    /// # Performance
217    /// This function is parallelized, which may introduce some overhead compared to
218    /// [`Self::scale_row`] when the width is small.
219    ///
220    /// # Panics
221    /// Panics if `r` larger than `self.height()`.
222    pub fn par_scale_row(&mut self, r: usize, scale: T)
223    where
224        T: Field,
225        S: BorrowMut<[T]>,
226    {
227        par_scale_slice_in_place(self.row_mut(r), scale);
228    }
229
230    /// Scale the entire matrix by the given value.
231    pub fn scale(&mut self, scale: T)
232    where
233        T: Field,
234        S: BorrowMut<[T]>,
235    {
236        par_scale_slice_in_place(self.values.borrow_mut(), scale);
237    }
238
239    /// Split the matrix into two matrix views, one with the first `r` rows and one with the remaining rows.
240    ///
241    /// # Panics
242    /// Panics if `r` larger than `self.height()`.
243    pub fn split_rows(&self, r: usize) -> (RowMajorMatrixView<'_, T>, RowMajorMatrixView<'_, T>) {
244        let (lo, hi) = self.values.borrow().split_at(r * self.width);
245        (
246            DenseMatrix::new(lo, self.width),
247            DenseMatrix::new(hi, self.width),
248        )
249    }
250
251    /// Split the matrix into two mutable matrix views, one with the first `r` rows and one with the remaining rows.
252    ///
253    /// # Panics
254    /// Panics if `r` larger than `self.height()`.
255    pub fn split_rows_mut(
256        &mut self,
257        r: usize,
258    ) -> (RowMajorMatrixViewMut<'_, T>, RowMajorMatrixViewMut<'_, T>)
259    where
260        S: BorrowMut<[T]>,
261    {
262        let (lo, hi) = self.values.borrow_mut().split_at_mut(r * self.width);
263        (
264            DenseMatrix::new(lo, self.width),
265            DenseMatrix::new(hi, self.width),
266        )
267    }
268
269    /// Get an iterator over the rows of the matrix which takes `chunk_rows` rows at a time.
270    ///
271    /// If `chunk_rows` does not divide the height of the matrix, the last chunk will be smaller.
272    pub fn par_row_chunks(
273        &self,
274        chunk_rows: usize,
275    ) -> impl IndexedParallelIterator<Item = RowMajorMatrixView<'_, T>>
276    where
277        T: Send,
278    {
279        self.values
280            .borrow()
281            .par_chunks(self.width * chunk_rows)
282            .map(|slice| RowMajorMatrixView::new(slice, self.width))
283    }
284
285    /// Get a parallel iterator over the rows of the matrix which takes `chunk_rows` rows at a time.
286    ///
287    /// If `chunk_rows` does not divide the height of the matrix, the last chunk will be smaller.
288    pub fn par_row_chunks_exact(
289        &self,
290        chunk_rows: usize,
291    ) -> impl IndexedParallelIterator<Item = RowMajorMatrixView<'_, T>>
292    where
293        T: Send,
294    {
295        self.values
296            .borrow()
297            .par_chunks_exact(self.width * chunk_rows)
298            .map(|slice| RowMajorMatrixView::new(slice, self.width))
299    }
300
301    /// Get a mutable iterator over the rows of the matrix which takes `chunk_rows` rows at a time.
302    ///
303    /// If `chunk_rows` does not divide the height of the matrix, the last chunk will be smaller.
304    pub fn par_row_chunks_mut(
305        &mut self,
306        chunk_rows: usize,
307    ) -> impl IndexedParallelIterator<Item = RowMajorMatrixViewMut<'_, T>>
308    where
309        T: Send,
310        S: BorrowMut<[T]>,
311    {
312        self.values
313            .borrow_mut()
314            .par_chunks_mut(self.width * chunk_rows)
315            .map(|slice| RowMajorMatrixViewMut::new(slice, self.width))
316    }
317
318    /// Get a mutable iterator over the rows of the matrix which takes `chunk_rows` rows at a time.
319    ///
320    /// If `chunk_rows` does not divide the height of the matrix, the last up to `chunk_rows - 1` rows
321    /// of the matrix will be omitted.
322    pub fn row_chunks_exact_mut(
323        &mut self,
324        chunk_rows: usize,
325    ) -> impl Iterator<Item = RowMajorMatrixViewMut<'_, T>>
326    where
327        T: Send,
328        S: BorrowMut<[T]>,
329    {
330        self.values
331            .borrow_mut()
332            .chunks_exact_mut(self.width * chunk_rows)
333            .map(|slice| RowMajorMatrixViewMut::new(slice, self.width))
334    }
335
336    /// Get a parallel mutable iterator over the rows of the matrix which takes `chunk_rows` rows at a time.
337    ///
338    /// If `chunk_rows` does not divide the height of the matrix, the last up to `chunk_rows - 1` rows
339    /// of the matrix will be omitted.
340    pub fn par_row_chunks_exact_mut(
341        &mut self,
342        chunk_rows: usize,
343    ) -> impl IndexedParallelIterator<Item = RowMajorMatrixViewMut<'_, T>>
344    where
345        T: Send,
346        S: BorrowMut<[T]>,
347    {
348        self.values
349            .borrow_mut()
350            .par_chunks_exact_mut(self.width * chunk_rows)
351            .map(|slice| RowMajorMatrixViewMut::new(slice, self.width))
352    }
353
354    /// Get a pair of mutable slices of the given rows.
355    ///
356    /// # Panics
357    /// Panics if `row_1` or `row_2` are out of bounds or if `row_1 >= row_2`.
358    pub fn row_pair_mut(&mut self, row_1: usize, row_2: usize) -> (&mut [T], &mut [T])
359    where
360        S: BorrowMut<[T]>,
361    {
362        debug_assert_ne!(row_1, row_2);
363        let start_1 = row_1 * self.width;
364        let start_2 = row_2 * self.width;
365        let (lo, hi) = self.values.borrow_mut().split_at_mut(start_2);
366        (&mut lo[start_1..][..self.width], &mut hi[..self.width])
367    }
368
369    /// Get a pair of mutable slices of the given rows, both packed into packed field elements.
370    ///
371    /// If `P:WIDTH` does not divide `self.width`, the remainder of the row will be returned as a base slice.
372    ///
373    /// # Panics
374    /// Panics if `row_1` or `row_2` are out of bounds or if `row_1 >= row_2`.
375    #[allow(clippy::type_complexity)]
376    pub fn packed_row_pair_mut<P>(
377        &mut self,
378        row_1: usize,
379        row_2: usize,
380    ) -> ((&mut [P], &mut [T]), (&mut [P], &mut [T]))
381    where
382        S: BorrowMut<[T]>,
383        P: PackedValue<Value = T>,
384    {
385        let (slice_1, slice_2) = self.row_pair_mut(row_1, row_2);
386        (
387            P::pack_slice_with_suffix_mut(slice_1),
388            P::pack_slice_with_suffix_mut(slice_2),
389        )
390    }
391
392    /// Append zeros to the "end" of the given matrix, except that the matrix is in bit-reversed order,
393    /// so in actuality we're interleaving zero rows.
394    #[instrument(level = "debug", skip_all)]
395    pub fn bit_reversed_zero_pad(self, added_bits: usize) -> RowMajorMatrix<T>
396    where
397        T: Field,
398    {
399        if added_bits == 0 {
400            return self.to_row_major_matrix();
401        }
402
403        // This is equivalent to:
404        //     reverse_matrix_index_bits(mat);
405        //     mat
406        //         .values
407        //         .resize(mat.values.len() << added_bits, F::ZERO);
408        //     reverse_matrix_index_bits(mat);
409        // But rather than implement it with bit reversals, we directly construct the resulting matrix,
410        // whose rows are zero except for rows whose low `added_bits` bits are zero.
411
412        let w = self.width;
413        let mut padded =
414            RowMajorMatrix::new(T::zero_vec(self.values.borrow().len() << added_bits), w);
415        padded
416            .par_row_chunks_exact_mut(1 << added_bits)
417            .zip(self.par_row_slices())
418            .for_each(|(mut ch, r)| ch.row_mut(0).copy_from_slice(r));
419
420        padded
421    }
422}
423
424impl<T: Clone + Send + Sync, S: DenseStorage<T>> Matrix<T> for DenseMatrix<T, S> {
425    #[inline]
426    fn width(&self) -> usize {
427        self.width
428    }
429
430    #[inline]
431    fn height(&self) -> usize {
432        self.values
433            .borrow()
434            .len()
435            .checked_div(self.width)
436            .unwrap_or(0)
437    }
438
439    #[inline]
440    unsafe fn get_unchecked(&self, r: usize, c: usize) -> T {
441        unsafe {
442            // Safety: The caller must ensure that r < self.height() and c < self.width().
443            self.values
444                .borrow()
445                .get_unchecked(r * self.width + c)
446                .clone()
447        }
448    }
449
450    #[inline]
451    unsafe fn row_subseq_unchecked(
452        &self,
453        r: usize,
454        start: usize,
455        end: usize,
456    ) -> impl IntoIterator<Item = T, IntoIter = impl Iterator<Item = T> + Send + Sync> {
457        unsafe {
458            // Safety: The caller must ensure that r < self.height() and start <= end <= self.width().
459            self.values
460                .borrow()
461                .get_unchecked(r * self.width + start..r * self.width + end)
462                .iter()
463                .cloned()
464        }
465    }
466
467    #[inline]
468    unsafe fn row_subslice_unchecked(
469        &self,
470        r: usize,
471        start: usize,
472        end: usize,
473    ) -> impl Deref<Target = [T]> {
474        unsafe {
475            // Safety: The caller must ensure that r < self.height()
476            self.values
477                .borrow()
478                .get_unchecked(r * self.width + start..r * self.width + end)
479        }
480    }
481
482    fn to_row_major_matrix(self) -> RowMajorMatrix<T>
483    where
484        Self: Sized,
485        T: Clone,
486    {
487        RowMajorMatrix::new(self.values.to_vec(), self.width)
488    }
489
490    #[inline]
491    fn horizontally_packed_row<'a, P>(
492        &'a self,
493        r: usize,
494    ) -> (
495        impl Iterator<Item = P> + Send + Sync,
496        impl Iterator<Item = T> + Send + Sync,
497    )
498    where
499        P: PackedValue<Value = T>,
500        T: Clone + 'a,
501    {
502        let buf = &self.values.borrow()[r * self.width..(r + 1) * self.width];
503        let (packed, sfx) = P::pack_slice_with_suffix(buf);
504        (packed.iter().copied(), sfx.iter().cloned())
505    }
506
507    #[inline]
508    fn padded_horizontally_packed_row<'a, P>(
509        &'a self,
510        r: usize,
511    ) -> impl Iterator<Item = P> + Send + Sync
512    where
513        P: PackedValue<Value = T>,
514        T: Clone + Default + 'a,
515    {
516        let buf = &self.values.borrow()[r * self.width..(r + 1) * self.width];
517        let (packed, sfx) = P::pack_slice_with_suffix(buf);
518        packed.iter().copied().chain(
519            (!sfx.is_empty()).then(|| P::from_fn(|i| sfx.get(i).cloned().unwrap_or_default())),
520        )
521    }
522
523    #[inline]
524    fn vertically_packed_row<P>(&self, r: usize) -> impl Iterator<Item = P>
525    where
526        T: Copy,
527        P: PackedValue<Value = T>,
528    {
529        let values = self.values.borrow();
530        let width = self.width;
531        let height = self.height();
532        let row = r % height;
533        let no_wrap = P::WIDTH != 1 && r + P::WIDTH <= height;
534        let rows = (!no_wrap && P::WIDTH != 1).then(|| self.wrapping_row_slices(r, P::WIDTH));
535
536        (0..width).map(move |c| {
537            if P::WIDTH == 1 {
538                // SAFETY: row < height (from the `%` above) and c < width (loop bound).
539                unsafe { P::broadcast(*values.get_unchecked(row * width + c)) }
540            } else if no_wrap {
541                // SAFETY: for i in 0..P::WIDTH, r + i < height (fast-path guard) and c < width.
542                P::from_fn(|i| unsafe { *values.get_unchecked((r + i) * width + c) })
543            } else {
544                let rows = rows.as_ref().unwrap();
545                P::from_fn(|i| rows[i][c])
546            }
547        })
548    }
549
550    #[inline]
551    fn vertically_packed_row_pair<P>(&self, r: usize, step: usize) -> Vec<P>
552    where
553        T: Copy,
554        P: PackedValue<Value = T>,
555    {
556        let values = self.values.borrow();
557        let width = self.width;
558        let height = self.height();
559
560        if P::WIDTH == 1 {
561            let row = r % height;
562            let next_row = (r + step) % height;
563            let mut out = Vec::with_capacity(width * 2);
564            out.extend(
565                // SAFETY: row < height and c < width (loop bound).
566                (0..width).map(|c| unsafe { P::broadcast(*values.get_unchecked(row * width + c)) }),
567            );
568            out.extend(
569                // SAFETY: next_row < height and c < width.
570                (0..width)
571                    .map(|c| unsafe { P::broadcast(*values.get_unchecked(next_row * width + c)) }),
572            );
573            out
574        } else if r + P::WIDTH <= height && r + step + P::WIDTH <= height {
575            // SAFETY: for i in 0..P::WIDTH, both r+i < height and r+step+i < height (fast-path
576            // guard), and c < width (loop bound).
577            (0..width)
578                .map(|c| P::from_fn(|i| unsafe { *values.get_unchecked((r + i) * width + c) }))
579                .chain((0..width).map(|c| {
580                    P::from_fn(|i| unsafe { *values.get_unchecked((r + step + i) * width + c) })
581                }))
582                .collect::<Vec<_>>()
583        } else {
584            let rows = self.wrapping_row_slices(r, P::WIDTH);
585            let next_rows = self.wrapping_row_slices(r + step, P::WIDTH);
586            (0..width)
587                .map(|c| P::from_fn(|i| rows[i][c]))
588                .chain((0..width).map(|c| P::from_fn(|i| next_rows[i][c])))
589                .collect::<Vec<_>>()
590        }
591    }
592}
593
594impl<T: Clone + Default + Send + Sync> DenseMatrix<T> {
595    pub fn as_cow<'a>(self) -> RowMajorMatrixCow<'a, T> {
596        RowMajorMatrixCow::new(Cow::Owned(self.values), self.width)
597    }
598
599    pub fn rand<R: Rng>(rng: &mut R, rows: usize, cols: usize) -> Self
600    where
601        StandardUniform: Distribution<T>,
602    {
603        let values = rng.sample_iter(StandardUniform).take(rows * cols).collect();
604        Self::new(values, cols)
605    }
606
607    pub fn rand_nonzero<R: Rng>(rng: &mut R, rows: usize, cols: usize) -> Self
608    where
609        T: Field,
610        StandardUniform: Distribution<T>,
611    {
612        let values = rng
613            .sample_iter(StandardUniform)
614            .filter(|x| !x.is_zero())
615            .take(rows * cols)
616            .collect();
617        Self::new(values, cols)
618    }
619
620    /// Return a copy of this matrix with additional columns filled with random
621    /// values appended on the right.
622    ///
623    /// The original columns are preserved unchanged and the new trailing
624    /// columns in each row are populated independently from the provided
625    /// random number generator.
626    ///
627    /// # Memory Layout
628    ///
629    /// ```text
630    ///     Original (h × w):          Result (h × (w + num_cols)):
631    ///     [ a00  a01  …  a0w ]  →    [ a00  a01  …  a0w | r0  r1  …  rN ]
632    ///     [ a10  a11  …  a1w ]       [ a10  a11  …  a1w | r0  r1  …  rN ]
633    ///     …                          …
634    /// ```
635    ///
636    /// # Arguments
637    ///
638    /// - `num_cols`: number of random columns to append.
639    /// - `rng`: random number generator used to sample each new element.
640    ///
641    /// # Returns
642    ///
643    /// A new matrix with width equal to `self.width() + num_cols`.
644    #[instrument(level = "debug", skip_all)]
645    pub fn with_random_cols<R>(&self, num_cols: usize, mut rng: R) -> Self
646    where
647        T: Field,
648        R: Rng + Send + Sync,
649        StandardUniform: Distribution<T>,
650    {
651        // Record the original width so we know where to split each row.
652        let old_w = self.width();
653        let new_w = old_w + num_cols;
654
655        // Allocate a zero-initialized buffer for the widened matrix.
656        let new_values = T::zero_vec(new_w * self.height());
657        let mut result = Self::new(new_values, new_w);
658
659        // Copy original data into the left portion of each row in parallel; this is a plain
660        // memcpy per row with no dependency on the (necessarily serial) RNG stream below.
661        result
662            .par_rows_mut()
663            .zip(self.par_row_slices())
664            .for_each(|(new_row, old_row)| {
665                new_row[..old_w].copy_from_slice(old_row);
666            });
667
668        // Fill the trailing random columns as a separate serial pass, since `rng` is a single
669        // sequential stream.
670        result.rows_mut().for_each(|new_row| {
671            new_row[old_w..].iter_mut().for_each(|v| *v = rng.random());
672        });
673
674        result
675    }
676
677    /// Return a copy of this matrix with additional zero-filled columns
678    /// appended on the right.
679    ///
680    /// Delegates to cloning the matrix and calling the in-place widening
681    /// method with a zero fill value.
682    ///
683    /// # Memory Layout
684    ///
685    /// ```text
686    ///     Original (h × w):          Result (h × (w + num_cols)):
687    ///     [ a00  a01  …  a0w ]  →    [ a00  a01  …  a0w | 0  0  …  0 ]
688    ///     [ a10  a11  …  a1w ]       [ a10  a11  …  a1w | 0  0  …  0 ]
689    ///     …                          …
690    /// ```
691    ///
692    /// # Arguments
693    ///
694    /// - `num_cols`: number of zero columns to append.
695    ///
696    /// # Returns
697    ///
698    /// A new matrix with width equal to `self.width() + num_cols`.
699    #[instrument(level = "debug", skip_all)]
700    pub fn with_zero_cols(&self, num_cols: usize) -> Self
701    where
702        T: Field,
703    {
704        // Clone the original matrix and widen it in-place with zero fill.
705        let mut result = self.clone();
706        result.widen_right(num_cols, T::ZERO);
707        result
708    }
709
710    pub fn pad_to_height(&mut self, new_height: usize, fill: T) {
711        assert!(new_height >= self.height());
712        self.values.resize(self.width * new_height, fill);
713    }
714
715    /// Pad the matrix height to the next power of two by appending rows filled with `fill`.
716    ///
717    /// This is commonly used in proof systems where trace matrices must have power-of-two heights.
718    ///
719    /// # Behavior
720    ///
721    /// - If the matrix is empty (height = 0), it is padded to have exactly one row of `fill` values.
722    /// - If the height is already a power of two, the matrix is unchanged.
723    /// - Otherwise, the matrix is padded to the next power of two height.
724    pub fn pad_to_power_of_two_height(&mut self, fill: T) {
725        // Compute the target height as the next power of two.
726        let target_height = self.height().next_power_of_two();
727
728        // If target_height == height, resize will have no effect.
729        // Otherwise we pad the matrix to a power of two height by filling with the supplied value.
730        self.values.resize(self.width * target_height, fill);
731    }
732
733    /// Pad the matrix height to at least a given minimum, rounded up to the next power of two.
734    ///
735    /// Appends rows filled with the provided fill value.
736    /// Useful in batch proving where multiple trace matrices must share a minimum height
737    /// while still satisfying the power-of-two requirement.
738    ///
739    /// # Logic
740    ///
741    /// - Round both the current height and the minimum up to the next power of two.
742    /// - Take the maximum of those two values as the target.
743    /// - Append fill rows until the target is reached.
744    ///
745    /// # Behavior
746    ///
747    /// - If the matrix already meets or exceeds the target, it is unchanged.
748    /// - If the minimum is 0, this reduces to padding to the next power of two.
749    /// - If the matrix is empty (height = 0), it is padded entirely with fill values.
750    pub fn pad_to_min_power_of_two_height(&mut self, min_height: usize, fill: T) {
751        // Compute the target as the larger of the two power-of-two ceilings.
752        let target_height = self
753            .height()
754            .next_power_of_two()
755            .max(min_height.next_power_of_two());
756
757        // Extend with fill values to reach the target height. No-op if already there.
758        self.values.resize(self.width * target_height, fill);
759    }
760
761    /// Build a matrix from a flat buffer whose length may not be a multiple of the
762    /// requested width.
763    ///
764    /// Useful when constructing trace matrices from a stream of values where the
765    /// final row may be incomplete.
766    ///
767    /// # Arguments
768    ///
769    /// - `values`: flat row-major data, ownership transferred to avoid a copy.
770    /// - `width`: number of columns (must be > 0).
771    /// - `fill`: value used to complete the last row or to create an empty row.
772    ///
773    /// # Returns
774    ///
775    /// A dense matrix with `ceil(values.len() / width)` rows (at least 1).
776    ///
777    /// # Panics
778    ///
779    /// Panics if `width` is zero.
780    #[must_use]
781    pub fn from_flat_padded(mut values: Vec<T>, width: usize, fill: T) -> Self {
782        // Zero width would cause a division-by-zero when computing height.
783        assert!(width > 0, "width must be positive");
784
785        // How many elements the last row is missing.
786        let len = values.len();
787        let rem = len % width;
788
789        // Complete the partial trailing row.
790        //
791        // `resize` is a single capacity check + contiguous fill,
792        // faster than `extend(repeat_n(..))` which uses iterator machinery.
793        if rem != 0 {
794            values.resize(len + (width - rem), fill.clone());
795        }
796
797        // Guarantee at least one row so callers never get a zero-height matrix.
798        if values.is_empty() {
799            values.resize(width, fill);
800        }
801
802        Self::new(values, width)
803    }
804
805    /// Return a new matrix with additional columns appended to the right of
806    /// every row, filled with a constant value.
807    ///
808    /// Useful when a trace matrix needs extra selector or flag columns that are
809    /// initialised to a default.
810    ///
811    /// # Memory Layout
812    ///
813    /// ```text
814    ///  Before (width = W):          After (width = W + extra):
815    ///  [ d0  d1 ... d_{W-1} ]       [ d0  d1 ... d_{W-1}  fill ... fill ]
816    ///  [ ..                 ]       [ ..                                ]
817    /// ```
818    ///
819    /// # Algorithm
820    ///
821    /// Rows are relocated back-to-front so that earlier (lower-address) source
822    /// data is never overwritten before it is read.
823    ///
824    /// - Grow the backing buffer to `height * new_width`.
825    /// - Walk rows from the last to the first.
826    /// - For each row, move its elements to the new position and fill the
827    ///   trailing gap with the provided value.
828    ///
829    /// # Arguments
830    ///
831    /// - `extra_cols`: number of columns to append (0 is a no-op).
832    /// - `fill`: value written into every new column.
833    pub fn widen_right(&mut self, extra_cols: usize, fill: T)
834    where
835        T: Copy,
836    {
837        // No columns to add.
838        if extra_cols == 0 {
839            return;
840        }
841
842        let old_w = self.width;
843        let new_w = old_w + extra_cols;
844        let h = self.height();
845
846        // Grow the buffer to the widened size.
847        //
848        // The last row's trailing columns are filled for free by `resize`.
849        // Interior gaps still contain stale data — fixed up below.
850        self.values.resize(h * new_w, fill);
851
852        // Reverse iteration prevents clobbering: each row moves to a
853        // higher offset than its source.
854        //
855        // After relocating row r, fill the trailing columns of row r-1.
856        for r in (1..h).rev() {
857            // Source offset in the old (compact) layout.
858            let src_start = r * old_w;
859
860            // Destination offset in the new (widened) layout.
861            let dst_start = r * new_w;
862
863            // Move the row data. Compiles to a single `memmove`.
864            self.values
865                .copy_within(src_start..src_start + old_w, dst_start);
866
867            // Fill row (r-1)'s trailing columns, right before this row.
868            self.values[dst_start - extra_cols..dst_start].fill(fill);
869        }
870
871        // - h >= 2: the r == 1 iteration already filled row 0's gap.
872        // - h == 1: the loop never ran, so row 0's trailing columns are stale.
873        // - h == 0: the buffer is empty — nothing to do.
874        if h == 1 {
875            self.values[old_w..new_w].fill(fill);
876        }
877
878        // Commit the new width so subsequent accesses use the widened stride.
879        self.width = new_w;
880    }
881}
882
883impl<T: Copy + Default + Send + Sync, V: DenseStorage<T>> DenseMatrix<T, V> {
884    /// Return the transpose of this matrix.
885    pub fn transpose(&self) -> RowMajorMatrix<T> {
886        let nelts = self.height() * self.width();
887        let mut values = vec![T::default(); nelts];
888        p3_util::transpose::transpose(
889            self.values.borrow(),
890            &mut values,
891            self.width(),
892            self.height(),
893        );
894        RowMajorMatrix::new(values, self.height())
895    }
896
897    /// Transpose the matrix returning the result in `other` without intermediate allocation.
898    pub fn transpose_into<W: DenseStorage<T> + BorrowMut<[T]>>(
899        &self,
900        other: &mut DenseMatrix<T, W>,
901    ) {
902        assert_eq!(self.height(), other.width());
903        assert_eq!(other.height(), self.width());
904        p3_util::transpose::transpose(
905            self.values.borrow(),
906            other.values.borrow_mut(),
907            self.width(),
908            self.height(),
909        );
910    }
911}
912
913impl<'a, T: Clone + Default + Send + Sync> RowMajorMatrixView<'a, T> {
914    pub fn as_cow(self) -> RowMajorMatrixCow<'a, T> {
915        RowMajorMatrixCow::new(Cow::Borrowed(self.values), self.width)
916    }
917}
918
919#[cfg(test)]
920mod tests {
921    use p3_baby_bear::BabyBear;
922    use p3_field::{FieldArray, PrimeCharacteristicRing};
923    use rand::SeedableRng;
924    use rand::rngs::SmallRng;
925
926    use super::*;
927
928    #[test]
929    fn test_new() {
930        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
931        assert_eq!(matrix.width, 2);
932        assert_eq!(matrix.height(), 3);
933        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6]);
934    }
935
936    #[test]
937    fn test_new_row() {
938        let matrix = RowMajorMatrix::new_row(vec![1, 2, 3]);
939        assert_eq!(matrix.width, 3);
940        assert_eq!(matrix.height(), 1);
941    }
942
943    #[test]
944    fn test_new_col() {
945        let matrix = RowMajorMatrix::new_col(vec![1, 2, 3]);
946        assert_eq!(matrix.width, 1);
947        assert_eq!(matrix.height(), 3);
948    }
949
950    #[test]
951    fn test_height_with_zero_width() {
952        let matrix: DenseMatrix<i32> = RowMajorMatrix::new(vec![], 0);
953        assert_eq!(matrix.height(), 0);
954    }
955
956    #[test]
957    fn test_get_methods() {
958        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); // Height = 3, Width = 2
959        assert_eq!(matrix.get(0, 0), Some(1));
960        assert_eq!(matrix.get(1, 1), Some(4));
961        assert_eq!(matrix.get(2, 0), Some(5));
962        unsafe {
963            assert_eq!(matrix.get_unchecked(0, 1), 2);
964            assert_eq!(matrix.get_unchecked(1, 0), 3);
965            assert_eq!(matrix.get_unchecked(2, 1), 6);
966        }
967        assert_eq!(matrix.get(3, 0), None); // Height out of bounds
968        assert_eq!(matrix.get(0, 2), None); // Width out of bounds
969    }
970
971    #[test]
972    fn test_row_methods() {
973        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 4); // Height = 2, Width = 4
974        let row: Vec<_> = matrix.row(1).unwrap().into_iter().collect();
975        assert_eq!(row, vec![5, 6, 7, 8]);
976        unsafe {
977            let row: Vec<_> = matrix.row_unchecked(0).into_iter().collect();
978            assert_eq!(row, vec![1, 2, 3, 4]);
979            let row: Vec<_> = matrix.row_subseq_unchecked(0, 0, 3).into_iter().collect();
980            assert_eq!(row, vec![1, 2, 3]);
981            let row: Vec<_> = matrix.row_subseq_unchecked(0, 1, 3).into_iter().collect();
982            assert_eq!(row, vec![2, 3]);
983            let row: Vec<_> = matrix.row_subseq_unchecked(0, 2, 4).into_iter().collect();
984            assert_eq!(row, vec![3, 4]);
985        }
986        assert!(matrix.row(2).is_none()); // Height out of bounds
987    }
988
989    #[test]
990    fn test_row_slice_methods() {
991        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9], 3); // Height = 3, Width = 3
992        let slice0 = matrix.row_slice(0);
993        let slice2 = matrix.row_slice(2);
994        assert_eq!(slice0.unwrap().deref(), &[1, 2, 3]);
995        assert_eq!(slice2.unwrap().deref(), &[7, 8, 9]);
996        unsafe {
997            assert_eq!(&[1, 2, 3], matrix.row_slice_unchecked(0).deref());
998            assert_eq!(&[7, 8, 9], matrix.row_slice_unchecked(2).deref());
999
1000            assert_eq!(&[1, 2, 3], matrix.row_subslice_unchecked(0, 0, 3).deref());
1001            assert_eq!(&[8], matrix.row_subslice_unchecked(2, 1, 2).deref());
1002        }
1003        assert!(matrix.row_slice(3).is_none()); // Height out of bounds
1004    }
1005
1006    #[test]
1007    fn test_as_view() {
1008        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4], 2);
1009        let view = matrix.as_view();
1010        assert_eq!(view.values, &[1, 2, 3, 4]);
1011        assert_eq!(view.width, 2);
1012    }
1013
1014    #[test]
1015    fn test_as_view_mut() {
1016        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4], 2);
1017        let view = matrix.as_view_mut();
1018        view.values[0] = 10;
1019        assert_eq!(matrix.values, vec![10, 2, 3, 4]);
1020    }
1021
1022    #[test]
1023    fn test_copy_from() {
1024        let mut matrix1 = RowMajorMatrix::new(vec![0, 0, 0, 0], 2);
1025        let matrix2 = RowMajorMatrix::new(vec![1, 2, 3, 4], 2);
1026        matrix1.copy_from(&matrix2);
1027        assert_eq!(matrix1.values, vec![1, 2, 3, 4]);
1028    }
1029
1030    #[test]
1031    fn test_split_rows() {
1032        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1033        let (top, bottom) = matrix.split_rows(1);
1034        assert_eq!(top.values, vec![1, 2]);
1035        assert_eq!(bottom.values, vec![3, 4, 5, 6]);
1036    }
1037
1038    #[test]
1039    fn test_split_rows_mut() {
1040        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1041        let (top, bottom) = matrix.split_rows_mut(1);
1042        assert_eq!(top.values, vec![1, 2]);
1043        assert_eq!(bottom.values, vec![3, 4, 5, 6]);
1044    }
1045
1046    #[test]
1047    fn test_row_mut() {
1048        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1049        matrix.row_mut(1)[0] = 10;
1050        assert_eq!(matrix.values, vec![1, 2, 10, 4, 5, 6]);
1051    }
1052
1053    #[test]
1054    fn test_bit_reversed_zero_pad() {
1055        let matrix = RowMajorMatrix::new(
1056            vec![
1057                BabyBear::new(1),
1058                BabyBear::new(2),
1059                BabyBear::new(3),
1060                BabyBear::new(4),
1061            ],
1062            2,
1063        );
1064        let padded = matrix.bit_reversed_zero_pad(1);
1065        assert_eq!(padded.width, 2);
1066        assert_eq!(
1067            padded.values,
1068            vec![
1069                BabyBear::new(1),
1070                BabyBear::new(2),
1071                BabyBear::new(0),
1072                BabyBear::new(0),
1073                BabyBear::new(3),
1074                BabyBear::new(4),
1075                BabyBear::new(0),
1076                BabyBear::new(0)
1077            ]
1078        );
1079    }
1080
1081    #[test]
1082    fn test_bit_reversed_zero_pad_no_change() {
1083        let matrix = RowMajorMatrix::new(
1084            vec![
1085                BabyBear::new(1),
1086                BabyBear::new(2),
1087                BabyBear::new(3),
1088                BabyBear::new(4),
1089            ],
1090            2,
1091        );
1092        let padded = matrix.bit_reversed_zero_pad(0);
1093
1094        assert_eq!(padded.width, 2);
1095        assert_eq!(
1096            padded.values,
1097            vec![
1098                BabyBear::new(1),
1099                BabyBear::new(2),
1100                BabyBear::new(3),
1101                BabyBear::new(4),
1102            ]
1103        );
1104    }
1105
1106    #[test]
1107    fn test_scale() {
1108        let mut matrix = RowMajorMatrix::new(
1109            vec![
1110                BabyBear::new(1),
1111                BabyBear::new(2),
1112                BabyBear::new(3),
1113                BabyBear::new(4),
1114                BabyBear::new(5),
1115                BabyBear::new(6),
1116            ],
1117            2,
1118        );
1119        matrix.scale(BabyBear::new(2));
1120        assert_eq!(
1121            matrix.values,
1122            vec![
1123                BabyBear::new(2),
1124                BabyBear::new(4),
1125                BabyBear::new(6),
1126                BabyBear::new(8),
1127                BabyBear::new(10),
1128                BabyBear::new(12)
1129            ]
1130        );
1131    }
1132
1133    #[test]
1134    fn test_scale_row() {
1135        let mut matrix = RowMajorMatrix::new(
1136            vec![
1137                BabyBear::new(1),
1138                BabyBear::new(2),
1139                BabyBear::new(3),
1140                BabyBear::new(4),
1141                BabyBear::new(5),
1142                BabyBear::new(6),
1143            ],
1144            2,
1145        );
1146        matrix.scale_row(1, BabyBear::new(3));
1147        assert_eq!(
1148            matrix.values,
1149            vec![
1150                BabyBear::new(1),
1151                BabyBear::new(2),
1152                BabyBear::new(9),
1153                BabyBear::new(12),
1154                BabyBear::new(5),
1155                BabyBear::new(6),
1156            ]
1157        );
1158    }
1159
1160    #[test]
1161    fn test_to_row_major_matrix() {
1162        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1163        let converted = matrix.to_row_major_matrix();
1164
1165        // The converted matrix should have the same values and width
1166        assert_eq!(converted.width, 2);
1167        assert_eq!(converted.height(), 3);
1168        assert_eq!(converted.values, vec![1, 2, 3, 4, 5, 6]);
1169    }
1170
1171    #[test]
1172    fn test_horizontally_packed_row() {
1173        type Packed = FieldArray<BabyBear, 2>;
1174
1175        let matrix = RowMajorMatrix::new(
1176            vec![
1177                BabyBear::new(1),
1178                BabyBear::new(2),
1179                BabyBear::new(3),
1180                BabyBear::new(4),
1181                BabyBear::new(5),
1182                BabyBear::new(6),
1183            ],
1184            3,
1185        );
1186
1187        let (packed_iter, suffix_iter) = matrix.horizontally_packed_row::<Packed>(1);
1188
1189        let packed: Vec<_> = packed_iter.collect();
1190        let suffix: Vec<_> = suffix_iter.collect();
1191
1192        assert_eq!(
1193            packed,
1194            vec![Packed::from([BabyBear::new(4), BabyBear::new(5)])]
1195        );
1196        assert_eq!(suffix, vec![BabyBear::new(6)]);
1197    }
1198
1199    #[test]
1200    fn test_padded_horizontally_packed_row() {
1201        use p3_baby_bear::BabyBear;
1202
1203        type Packed = FieldArray<BabyBear, 2>;
1204
1205        let matrix = RowMajorMatrix::new(
1206            vec![
1207                BabyBear::new(1),
1208                BabyBear::new(2),
1209                BabyBear::new(3),
1210                BabyBear::new(4),
1211                BabyBear::new(5),
1212                BabyBear::new(6),
1213            ],
1214            3,
1215        );
1216
1217        let packed_iter = matrix.padded_horizontally_packed_row::<Packed>(1);
1218        let packed: Vec<_> = packed_iter.collect();
1219
1220        assert_eq!(
1221            packed,
1222            vec![
1223                Packed::from([BabyBear::new(4), BabyBear::new(5)]),
1224                Packed::from([BabyBear::new(6), BabyBear::new(0)])
1225            ]
1226        );
1227    }
1228
1229    #[test]
1230    fn test_padded_horizontally_packed_row_exact_width() {
1231        type Packed = FieldArray<BabyBear, 2>;
1232
1233        // Width = 4 is exactly divisible by P::WIDTH = 2.
1234        // The iterator must return exactly div_ceil(4, 2) = 2 packed elements,
1235        // with no extra zero-filled element appended.
1236        let matrix = RowMajorMatrix::new(
1237            vec![
1238                BabyBear::new(1),
1239                BabyBear::new(2),
1240                BabyBear::new(3),
1241                BabyBear::new(4),
1242                BabyBear::new(5),
1243                BabyBear::new(6),
1244                BabyBear::new(7),
1245                BabyBear::new(8),
1246            ],
1247            4,
1248        );
1249
1250        let packed: Vec<_> = matrix.padded_horizontally_packed_row::<Packed>(1).collect();
1251
1252        assert_eq!(packed.len(), 2);
1253        assert_eq!(
1254            packed,
1255            vec![
1256                Packed::from([BabyBear::new(5), BabyBear::new(6)]),
1257                Packed::from([BabyBear::new(7), BabyBear::new(8)]),
1258            ]
1259        );
1260    }
1261
1262    #[test]
1263    fn test_pad_to_height() {
1264        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 3);
1265
1266        // Original matrix:
1267        // [ 1  2  3 ]
1268        // [ 4  5  6 ] (height = 2)
1269
1270        matrix.pad_to_height(4, 9);
1271
1272        // Expected matrix after padding:
1273        // [ 1  2  3 ]
1274        // [ 4  5  6 ]
1275        // [ 9  9  9 ]  <-- Newly added row
1276        // [ 9  9  9 ]  <-- Newly added row
1277
1278        assert_eq!(matrix.height(), 4);
1279        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6, 9, 9, 9, 9, 9, 9]);
1280    }
1281
1282    #[test]
1283    fn test_pad_to_power_of_two_height() {
1284        // Test 1: Non-power-of-two height (3 rows -> 4 rows) with fill value 0.
1285        //
1286        // - Original matrix has 3 rows, which is not a power of two.
1287        // - After padding, it should have 4 rows (next power of two).
1288        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1289        assert_eq!(matrix.height(), 3);
1290        matrix.pad_to_power_of_two_height(0);
1291        assert_eq!(matrix.height(), 4);
1292        // Original values preserved, new row filled with 0.
1293        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6, 0, 0]);
1294
1295        // Test 2: Already power-of-two height (4 rows -> 4 rows, unchanged).
1296        //
1297        // Matrix height is already a power of two, so no padding occurs.
1298        // Fill value is ignored when no padding is needed.
1299        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 2);
1300        assert_eq!(matrix.height(), 4);
1301        matrix.pad_to_power_of_two_height(99);
1302        assert_eq!(matrix.height(), 4);
1303        // Values unchanged (fill value not used).
1304        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6, 7, 8]);
1305
1306        // Test 3: Single row matrix (1 row -> 1 row, unchanged).
1307        //
1308        // Height of 1 is a power of two (2^0 = 1).
1309        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3], 3);
1310        assert_eq!(matrix.height(), 1);
1311        matrix.pad_to_power_of_two_height(42);
1312        assert_eq!(matrix.height(), 1);
1313        assert_eq!(matrix.values, vec![1, 2, 3]);
1314
1315        // Test 4: 5 rows -> 8 rows with custom fill value (-1).
1316        //
1317        // Demonstrates padding across a larger gap with a non-zero fill value.
1318        let mut matrix = RowMajorMatrix::new(vec![1; 10], 2);
1319        assert_eq!(matrix.height(), 5);
1320        matrix.pad_to_power_of_two_height(-1);
1321        assert_eq!(matrix.height(), 8);
1322        // Original 10 values plus 6 fill values (3 new rows * 2 width).
1323        assert_eq!(matrix.values.len(), 16);
1324        assert!(matrix.values[..10].iter().all(|&v| v == 1));
1325        assert!(matrix.values[10..].iter().all(|&v| v == -1));
1326    }
1327
1328    #[test]
1329    fn test_pad_to_power_of_two_height_empty_matrix() {
1330        // Empty matrix (0 rows) should be padded to 1 row of fill values.
1331        // This ensures the matrix is valid for downstream operations.
1332        let mut matrix: RowMajorMatrix<i32> = RowMajorMatrix::new(vec![], 3);
1333        assert_eq!(matrix.height(), 0);
1334        assert_eq!(matrix.width, 3);
1335        matrix.pad_to_power_of_two_height(7);
1336        // After padding: 1 row with 3 columns, all filled with 7.
1337        assert_eq!(matrix.height(), 1);
1338        assert_eq!(matrix.values, vec![7, 7, 7]);
1339    }
1340
1341    #[test]
1342    fn test_pad_to_min_power_of_two_height() {
1343        // Test 1: min_height dominates (3 rows, min_height = 5 -> 8 rows).
1344        //
1345        // - Current height 3 rounds to 4.
1346        // - min_height 5 rounds to 8.
1347        // - Target is max(4, 8) = 8.
1348        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1349        assert_eq!(matrix.height(), 3);
1350        matrix.pad_to_min_power_of_two_height(5, 0);
1351        assert_eq!(matrix.height(), 8);
1352        assert_eq!(matrix.values[..6], [1, 2, 3, 4, 5, 6]);
1353        assert!(matrix.values[6..].iter().all(|&v| v == 0));
1354
1355        // Test 2: Current height dominates (5 rows, min_height = 2 -> 8 rows).
1356        //
1357        // - Current height 5 rounds to 8.
1358        // - min_height 2 rounds to 2.
1359        // - Target is max(8, 2) = 8.
1360        let mut matrix = RowMajorMatrix::new(vec![1; 10], 2);
1361        assert_eq!(matrix.height(), 5);
1362        matrix.pad_to_min_power_of_two_height(2, -1);
1363        assert_eq!(matrix.height(), 8);
1364        assert!(matrix.values[..10].iter().all(|&v| v == 1));
1365        assert!(matrix.values[10..].iter().all(|&v| v == -1));
1366
1367        // Test 3: Already at target (4 rows, min_height = 3 -> 4 rows, unchanged).
1368        //
1369        // - Current height 4 is already a power of two.
1370        // - min_height 3 rounds to 4.
1371        // - Target is max(4, 4) = 4, no padding needed.
1372        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 2);
1373        assert_eq!(matrix.height(), 4);
1374        matrix.pad_to_min_power_of_two_height(3, 99);
1375        assert_eq!(matrix.height(), 4);
1376        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6, 7, 8]);
1377
1378        // Test 4: min_height = 0 behaves like pad_to_power_of_two_height.
1379        //
1380        // - min_height 0 rounds to 1.
1381        // - Current height 3 rounds to 4.
1382        // - Target is max(4, 1) = 4.
1383        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1384        assert_eq!(matrix.height(), 3);
1385        matrix.pad_to_min_power_of_two_height(0, 0);
1386        assert_eq!(matrix.height(), 4);
1387        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6, 0, 0]);
1388
1389        // Test 5: min_height is already a power of two (2 rows, min_height = 8 -> 8 rows).
1390        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 3);
1391        assert_eq!(matrix.height(), 2);
1392        matrix.pad_to_min_power_of_two_height(8, 7);
1393        assert_eq!(matrix.height(), 8);
1394        assert_eq!(matrix.values[..6], [1, 2, 3, 4, 5, 6]);
1395        assert!(matrix.values[6..].iter().all(|&v| v == 7));
1396        assert_eq!(matrix.values.len(), 24); // 8 rows * 3 width
1397    }
1398
1399    #[test]
1400    fn test_pad_to_min_power_of_two_height_empty_matrix() {
1401        // Empty matrix (0 rows) with min_height = 5 should pad to 8 rows.
1402        let mut matrix: RowMajorMatrix<i32> = RowMajorMatrix::new(vec![], 3);
1403        assert_eq!(matrix.height(), 0);
1404        matrix.pad_to_min_power_of_two_height(5, 7);
1405        assert_eq!(matrix.height(), 8);
1406        assert_eq!(matrix.values.len(), 24);
1407        assert!(matrix.values.iter().all(|&v| v == 7));
1408    }
1409
1410    #[test]
1411    fn test_from_flat_padded() {
1412        // Test 1: Buffer length is an exact multiple of width (no padding needed).
1413        //
1414        // 6 values with width 3 -> 2 complete rows, no fill appended.
1415        let matrix = RowMajorMatrix::from_flat_padded(vec![1, 2, 3, 4, 5, 6], 3, 0);
1416        assert_eq!(matrix.height(), 2);
1417        assert_eq!(matrix.width, 3);
1418        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6]);
1419
1420        // Test 2: Partial last row is padded with fill.
1421        //
1422        // 5 values with width 3 -> 1 complete row + 1 partial row (2 values + 1 fill).
1423        let matrix = RowMajorMatrix::from_flat_padded(vec![1, 2, 3, 4, 5], 3, 99);
1424        assert_eq!(matrix.height(), 2);
1425        assert_eq!(matrix.width, 3);
1426        assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 99]);
1427
1428        // Test 3: Single value with width 3 -> padded to one full row.
1429        let matrix = RowMajorMatrix::from_flat_padded(vec![42], 3, 0);
1430        assert_eq!(matrix.height(), 1);
1431        assert_eq!(matrix.values, vec![42, 0, 0]);
1432
1433        // Test 4: Empty buffer -> one row filled entirely with fill.
1434        let matrix = RowMajorMatrix::from_flat_padded(vec![], 4, 7);
1435        assert_eq!(matrix.height(), 1);
1436        assert_eq!(matrix.width, 4);
1437        assert_eq!(matrix.values, vec![7, 7, 7, 7]);
1438
1439        // Test 5: Width of 1 never needs padding.
1440        let matrix = RowMajorMatrix::from_flat_padded(vec![10, 20, 30], 1, 0);
1441        assert_eq!(matrix.height(), 3);
1442        assert_eq!(matrix.values, vec![10, 20, 30]);
1443    }
1444
1445    #[test]
1446    #[should_panic(expected = "width must be positive")]
1447    fn test_from_flat_padded_zero_width_panics() {
1448        let _ = RowMajorMatrix::from_flat_padded(vec![1, 2, 3], 0, 0);
1449    }
1450
1451    #[test]
1452    fn test_widen_right() {
1453        // Test 1: Widen a 2x2 matrix by 1 column.
1454        //
1455        // Original:        Widened:
1456        // [ 1  2 ]    ->   [ 1  2  0 ]
1457        // [ 3  4 ]         [ 3  4  0 ]
1458        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4], 2);
1459        matrix.widen_right(1, 0);
1460        assert_eq!(matrix.width, 3);
1461        assert_eq!(matrix.height(), 2);
1462        assert_eq!(matrix.values, vec![1, 2, 0, 3, 4, 0]);
1463
1464        // Test 2: Widen by 3 columns with a non-zero fill.
1465        //
1466        // Original:             Widened:
1467        // [ 1  2 ]    ->       [ 1  2  -1  -1  -1 ]
1468        // [ 3  4 ]             [ 3  4  -1  -1  -1 ]
1469        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4], 2);
1470        matrix.widen_right(3, -1);
1471        assert_eq!(matrix.width, 5);
1472        assert_eq!(matrix.height(), 2);
1473        assert_eq!(matrix.values, vec![1, 2, -1, -1, -1, 3, 4, -1, -1, -1]);
1474
1475        // Test 3: extra_cols = 0 leaves the matrix unchanged.
1476        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4], 2);
1477        matrix.widen_right(0, 99);
1478        assert_eq!(matrix.width, 2);
1479        assert_eq!(matrix.values, vec![1, 2, 3, 4]);
1480
1481        // Test 4: Single-row matrix.
1482        let mut matrix = RowMajorMatrix::new(vec![10, 20, 30], 3);
1483        matrix.widen_right(2, 0);
1484        assert_eq!(matrix.width, 5);
1485        assert_eq!(matrix.height(), 1);
1486        assert_eq!(matrix.values, vec![10, 20, 30, 0, 0]);
1487
1488        // Test 5: Single-column matrix widened to 3 columns.
1489        //
1490        // Original:    Widened:
1491        // [ 1 ]   ->   [ 1  0  0 ]
1492        // [ 2 ]        [ 2  0  0 ]
1493        // [ 3 ]        [ 3  0  0 ]
1494        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3], 1);
1495        matrix.widen_right(2, 0);
1496        assert_eq!(matrix.width, 3);
1497        assert_eq!(matrix.height(), 3);
1498        assert_eq!(matrix.values, vec![1, 0, 0, 2, 0, 0, 3, 0, 0]);
1499    }
1500
1501    #[test]
1502    fn test_widen_right_empty_matrix() {
1503        // Empty matrix (0 rows) widened should remain empty with updated width.
1504        let mut matrix: RowMajorMatrix<i32> = RowMajorMatrix::new(vec![], 3);
1505        matrix.widen_right(2, 0);
1506        assert_eq!(matrix.width, 5);
1507        assert_eq!(matrix.height(), 0);
1508        assert!(matrix.values.is_empty());
1509    }
1510
1511    #[test]
1512    fn test_transpose_into() {
1513        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 3);
1514
1515        // Original matrix:
1516        // [ 1  2  3 ]
1517        // [ 4  5  6 ]
1518
1519        let mut transposed = RowMajorMatrix::new(vec![0; 6], 2);
1520
1521        matrix.transpose_into(&mut transposed);
1522
1523        // Expected transposed matrix:
1524        // [ 1  4 ]
1525        // [ 2  5 ]
1526        // [ 3  6 ]
1527
1528        assert_eq!(transposed.width, 2);
1529        assert_eq!(transposed.height(), 3);
1530        assert_eq!(transposed.values, vec![1, 4, 2, 5, 3, 6]);
1531    }
1532
1533    #[test]
1534    fn test_flatten_to_base() {
1535        let matrix = RowMajorMatrix::new(
1536            vec![
1537                BabyBear::new(2),
1538                BabyBear::new(3),
1539                BabyBear::new(4),
1540                BabyBear::new(5),
1541            ],
1542            2,
1543        );
1544
1545        let flattened: RowMajorMatrix<BabyBear> = matrix.flatten_to_base();
1546
1547        assert_eq!(flattened.width, 2);
1548        assert_eq!(
1549            flattened.values,
1550            vec![
1551                BabyBear::new(2),
1552                BabyBear::new(3),
1553                BabyBear::new(4),
1554                BabyBear::new(5),
1555            ]
1556        );
1557    }
1558
1559    #[test]
1560    fn test_horizontally_packed_row_mut() {
1561        type Packed = FieldArray<BabyBear, 2>;
1562
1563        let mut matrix = RowMajorMatrix::new(
1564            vec![
1565                BabyBear::new(1),
1566                BabyBear::new(2),
1567                BabyBear::new(3),
1568                BabyBear::new(4),
1569                BabyBear::new(5),
1570                BabyBear::new(6),
1571            ],
1572            3,
1573        );
1574
1575        let (packed, suffix) = matrix.horizontally_packed_row_mut::<Packed>(1);
1576        packed[0] = Packed::from([BabyBear::new(9), BabyBear::new(10)]);
1577        suffix[0] = BabyBear::new(11);
1578
1579        assert_eq!(
1580            matrix.values,
1581            vec![
1582                BabyBear::new(1),
1583                BabyBear::new(2),
1584                BabyBear::new(3),
1585                BabyBear::new(9),
1586                BabyBear::new(10),
1587                BabyBear::new(11),
1588            ]
1589        );
1590    }
1591
1592    #[test]
1593    fn test_par_row_chunks() {
1594        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 2);
1595
1596        let chunks: Vec<_> = matrix.par_row_chunks(2).collect();
1597
1598        assert_eq!(chunks.len(), 2);
1599        assert_eq!(chunks[0].values, vec![1, 2, 3, 4]);
1600        assert_eq!(chunks[1].values, vec![5, 6, 7, 8]);
1601    }
1602
1603    #[test]
1604    fn test_par_row_chunks_exact() {
1605        let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1606
1607        let chunks: Vec<_> = matrix.par_row_chunks_exact(1).collect();
1608
1609        assert_eq!(chunks.len(), 3);
1610        assert_eq!(chunks[0].values, vec![1, 2]);
1611        assert_eq!(chunks[1].values, vec![3, 4]);
1612        assert_eq!(chunks[2].values, vec![5, 6]);
1613    }
1614
1615    #[test]
1616    fn test_par_row_chunks_mut() {
1617        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 2);
1618
1619        matrix
1620            .par_row_chunks_mut(2)
1621            .for_each(|chunk| chunk.values.iter_mut().for_each(|x| *x += 10));
1622
1623        assert_eq!(matrix.values, vec![11, 12, 13, 14, 15, 16, 17, 18]);
1624    }
1625
1626    #[test]
1627    fn test_row_chunks_exact_mut() {
1628        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1629
1630        for chunk in matrix.row_chunks_exact_mut(1) {
1631            chunk.values.iter_mut().for_each(|x| *x *= 2);
1632        }
1633
1634        assert_eq!(matrix.values, vec![2, 4, 6, 8, 10, 12]);
1635    }
1636
1637    #[test]
1638    fn test_par_row_chunks_exact_mut() {
1639        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1640
1641        matrix
1642            .par_row_chunks_exact_mut(1)
1643            .for_each(|chunk| chunk.values.iter_mut().for_each(|x| *x += 5));
1644
1645        assert_eq!(matrix.values, vec![6, 7, 8, 9, 10, 11]);
1646    }
1647
1648    #[test]
1649    fn test_row_pair_mut() {
1650        let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2);
1651
1652        let (row1, row2) = matrix.row_pair_mut(0, 2);
1653        row1[0] = 9;
1654        row2[1] = 10;
1655
1656        assert_eq!(matrix.values, vec![9, 2, 3, 4, 5, 10]);
1657    }
1658
1659    #[test]
1660    fn test_packed_row_pair_mut() {
1661        type Packed = FieldArray<BabyBear, 2>;
1662
1663        let mut matrix = RowMajorMatrix::new(
1664            vec![
1665                BabyBear::new(1),
1666                BabyBear::new(2),
1667                BabyBear::new(3),
1668                BabyBear::new(4),
1669                BabyBear::new(5),
1670                BabyBear::new(6),
1671            ],
1672            3,
1673        );
1674
1675        let ((packed1, sfx1), (packed2, sfx2)) = matrix.packed_row_pair_mut::<Packed>(0, 1);
1676        packed1[0] = Packed::from([BabyBear::new(7), BabyBear::new(8)]);
1677        packed2[0] = Packed::from([BabyBear::new(33), BabyBear::new(44)]);
1678        sfx1[0] = BabyBear::new(99);
1679        sfx2[0] = BabyBear::new(9);
1680
1681        assert_eq!(
1682            matrix.values,
1683            vec![
1684                BabyBear::new(7),
1685                BabyBear::new(8),
1686                BabyBear::new(99),
1687                BabyBear::new(33),
1688                BabyBear::new(44),
1689                BabyBear::new(9),
1690            ]
1691        );
1692    }
1693
1694    #[test]
1695    fn test_transpose_square_matrix() {
1696        const START_INDEX: usize = 1;
1697        const VALUE_LEN: usize = 9;
1698        const WIDTH: usize = 3;
1699        const HEIGHT: usize = 3;
1700
1701        let matrix_values = (START_INDEX..=VALUE_LEN).collect::<Vec<_>>();
1702        let matrix = RowMajorMatrix::new(matrix_values, WIDTH);
1703        let transposed = matrix.transpose();
1704        let should_be_transposed_values = vec![1, 4, 7, 2, 5, 8, 3, 6, 9];
1705        let should_be_transposed = RowMajorMatrix::new(should_be_transposed_values, HEIGHT);
1706        assert_eq!(transposed, should_be_transposed);
1707    }
1708
1709    #[test]
1710    fn test_transpose_row_matrix() {
1711        const START_INDEX: usize = 1;
1712        const VALUE_LEN: usize = 30;
1713        const WIDTH: usize = 1;
1714        const HEIGHT: usize = 30;
1715
1716        let matrix_values = (START_INDEX..=VALUE_LEN).collect::<Vec<_>>();
1717        let matrix = RowMajorMatrix::new(matrix_values.clone(), WIDTH);
1718        let transposed = matrix.transpose();
1719        let should_be_transposed = RowMajorMatrix::new(matrix_values, HEIGHT);
1720        assert_eq!(transposed, should_be_transposed);
1721    }
1722
1723    #[test]
1724    fn test_transpose_rectangular_matrix() {
1725        const START_INDEX: usize = 1;
1726        const VALUE_LEN: usize = 30;
1727        const WIDTH: usize = 5;
1728        const HEIGHT: usize = 6;
1729
1730        let matrix_values = (START_INDEX..=VALUE_LEN).collect::<Vec<_>>();
1731        let matrix = RowMajorMatrix::new(matrix_values, WIDTH);
1732        let transposed = matrix.transpose();
1733        let should_be_transposed_values = vec![
1734            1, 6, 11, 16, 21, 26, 2, 7, 12, 17, 22, 27, 3, 8, 13, 18, 23, 28, 4, 9, 14, 19, 24, 29,
1735            5, 10, 15, 20, 25, 30,
1736        ];
1737        let should_be_transposed = RowMajorMatrix::new(should_be_transposed_values, HEIGHT);
1738        assert_eq!(transposed, should_be_transposed);
1739    }
1740
1741    #[test]
1742    fn test_transpose_larger_rectangular_matrix() {
1743        const START_INDEX: usize = 1;
1744        const VALUE_LEN: usize = 131072; // 512 * 256
1745        const WIDTH: usize = 256;
1746        const HEIGHT: usize = 512;
1747
1748        let matrix_values = (START_INDEX..=VALUE_LEN).collect::<Vec<_>>();
1749        let matrix = RowMajorMatrix::new(matrix_values, WIDTH);
1750        let transposed = matrix.transpose();
1751
1752        assert_eq!(transposed.width(), HEIGHT);
1753        assert_eq!(transposed.height(), WIDTH);
1754
1755        for col_index in 0..WIDTH {
1756            for row_index in 0..HEIGHT {
1757                assert_eq!(
1758                    matrix.values[row_index * WIDTH + col_index],
1759                    transposed.values[col_index * HEIGHT + row_index]
1760                );
1761            }
1762        }
1763    }
1764
1765    #[test]
1766    fn test_transpose_very_large_rectangular_matrix() {
1767        const START_INDEX: usize = 1;
1768        const VALUE_LEN: usize = 1048576; // 512 * 256
1769        const WIDTH: usize = 1024;
1770        const HEIGHT: usize = 1024;
1771
1772        let matrix_values = (START_INDEX..=VALUE_LEN).collect::<Vec<_>>();
1773        let matrix = RowMajorMatrix::new(matrix_values, WIDTH);
1774        let transposed = matrix.transpose();
1775
1776        assert_eq!(transposed.width(), HEIGHT);
1777        assert_eq!(transposed.height(), WIDTH);
1778
1779        for col_index in 0..WIDTH {
1780            for row_index in 0..HEIGHT {
1781                assert_eq!(
1782                    matrix.values[row_index * WIDTH + col_index],
1783                    transposed.values[col_index * HEIGHT + row_index]
1784                );
1785            }
1786        }
1787    }
1788
1789    #[test]
1790    fn test_vertically_packed_row_scalar_width_1() {
1791        type Packed = BabyBear;
1792
1793        let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::<Vec<_>>(), 4);
1794        let packed = matrix
1795            .vertically_packed_row::<Packed>(2)
1796            .collect::<Vec<_>>();
1797
1798        assert_eq!(
1799            packed,
1800            vec![
1801                BabyBear::new(9),
1802                BabyBear::new(10),
1803                BabyBear::new(11),
1804                BabyBear::new(12),
1805            ]
1806        );
1807    }
1808
1809    #[test]
1810    fn test_vertically_packed_row_pair() {
1811        type Packed = FieldArray<BabyBear, 2>;
1812
1813        let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::<Vec<_>>(), 4);
1814
1815        // Calling the function with r = 0 and step = 2
1816        let packed = matrix.vertically_packed_row_pair::<Packed>(0, 2);
1817
1818        // Matrix visualization:
1819        //
1820        // [  1   2   3   4  ]  <-- Row 0
1821        // [  5   6   7   8  ]  <-- Row 1
1822        // [  9  10  11  12  ]  <-- Row 2
1823        // [ 13  14  15  16  ]  <-- Row 3
1824        //
1825        // Packing rows 0-1 together, then rows 2-3 together:
1826        //
1827        // Packed result:
1828        // [
1829        //   (1, 5), (2, 6), (3, 7), (4, 8),   // First packed row (Row 0 & Row 1)
1830        //   (9, 13), (10, 14), (11, 15), (12, 16),   // Second packed row (Row 2 & Row 3)
1831        // ]
1832
1833        assert_eq!(
1834            packed,
1835            (1..5)
1836                .chain(9..13)
1837                .map(|i| [BabyBear::new(i), BabyBear::new(i + 4)].into())
1838                .collect::<Vec<_>>(),
1839        );
1840    }
1841
1842    #[test]
1843    fn test_vertically_packed_row_pair_scalar_width_1() {
1844        type Packed = BabyBear;
1845
1846        let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::<Vec<_>>(), 4);
1847        let packed = matrix.vertically_packed_row_pair::<Packed>(1, 2);
1848
1849        assert_eq!(
1850            packed,
1851            vec![
1852                BabyBear::new(5),
1853                BabyBear::new(6),
1854                BabyBear::new(7),
1855                BabyBear::new(8),
1856                BabyBear::new(13),
1857                BabyBear::new(14),
1858                BabyBear::new(15),
1859                BabyBear::new(16),
1860            ]
1861        );
1862    }
1863
1864    #[test]
1865    fn test_vertically_packed_row_pair_overlap() {
1866        type Packed = FieldArray<BabyBear, 2>;
1867
1868        let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::<Vec<_>>(), 4);
1869
1870        // Original matrix visualization:
1871        //
1872        // [  1   2   3   4  ]  <-- Row 0
1873        // [  5   6   7   8  ]  <-- Row 1
1874        // [  9  10  11  12  ]  <-- Row 2
1875        // [ 13  14  15  16  ]  <-- Row 3
1876        //
1877        // Packing rows 0-1 together, then rows 1-2 together:
1878        //
1879        // Expected packed result:
1880        // [
1881        //   (1, 5), (2, 6), (3, 7), (4, 8),   // First packed row (Row 0 & Row 1)
1882        //   (5, 9), (6, 10), (7, 11), (8, 12) // Second packed row (Row 1 & Row 2)
1883        // ]
1884
1885        // Calling the function with overlapping rows (r = 0 and step = 1)
1886        let packed = matrix.vertically_packed_row_pair::<Packed>(0, 1);
1887
1888        assert_eq!(
1889            packed,
1890            (1..5)
1891                .chain(5..9)
1892                .map(|i| [BabyBear::new(i), BabyBear::new(i + 4)].into())
1893                .collect::<Vec<_>>(),
1894        );
1895    }
1896
1897    #[test]
1898    fn test_vertically_packed_row_pair_wraparound_start_1() {
1899        use p3_baby_bear::BabyBear;
1900        use p3_field::FieldArray;
1901
1902        type Packed = FieldArray<BabyBear, 2>;
1903
1904        let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::<Vec<_>>(), 4);
1905
1906        // Original matrix visualization:
1907        //
1908        // [  1   2   3   4  ]  <-- Row 0
1909        // [  5   6   7   8  ]  <-- Row 1
1910        // [  9  10  11  12  ]  <-- Row 2
1911        // [ 13  14  15  16  ]  <-- Row 3
1912        //
1913        // Packing starts from row 1, skipping 2 rows (step = 2):
1914        // - The first packed row should contain row 1 & row 2.
1915        // - The second packed row should contain row 3 & row 1 (wraparound case).
1916        //
1917        // Expected packed result:
1918        // [
1919        //   (5, 9), (6, 10), (7, 11), (8, 12),   // Packed row (Row 1 & Row 2)
1920        //   (13, 1), (14, 2), (15, 3), (16, 4)    // Packed row (Row 3 & Row 1)
1921        // ]
1922
1923        // Calling the function with wraparound scenario (starting at r = 1 with step = 2)
1924        let packed = matrix.vertically_packed_row_pair::<Packed>(1, 2);
1925
1926        assert_eq!(
1927            packed,
1928            vec![
1929                Packed::from([BabyBear::new(5), BabyBear::new(9)]),
1930                Packed::from([BabyBear::new(6), BabyBear::new(10)]),
1931                Packed::from([BabyBear::new(7), BabyBear::new(11)]),
1932                Packed::from([BabyBear::new(8), BabyBear::new(12)]),
1933                Packed::from([BabyBear::new(13), BabyBear::new(1)]),
1934                Packed::from([BabyBear::new(14), BabyBear::new(2)]),
1935                Packed::from([BabyBear::new(15), BabyBear::new(3)]),
1936                Packed::from([BabyBear::new(16), BabyBear::new(4)]),
1937            ]
1938        );
1939    }
1940
1941    #[test]
1942    fn test_with_zero_cols() {
1943        // Test 1: Append 2 zero columns to a 2×3 matrix.
1944        //
1945        //     Original:             Result:
1946        //     [ 1  2  3 ]    →      [ 1  2  3  0  0 ]
1947        //     [ 4  5  6 ]           [ 4  5  6  0  0 ]
1948        let mat: RowMajorMatrix<BabyBear> =
1949            RowMajorMatrix::new((1..=6).map(BabyBear::new).collect(), 3);
1950        let widened = mat.with_zero_cols(2);
1951
1952        // Verify the new dimensions: width grows by 2, height stays the same.
1953        assert_eq!(widened.width(), 5);
1954        assert_eq!(widened.height(), 2);
1955
1956        // Row 0: original values followed by zeros.
1957        assert_eq!(
1958            widened.row_slices().next().unwrap(),
1959            &[
1960                BabyBear::new(1),
1961                BabyBear::new(2),
1962                BabyBear::new(3),
1963                BabyBear::ZERO,
1964                BabyBear::ZERO,
1965            ]
1966        );
1967
1968        // Row 1: original values followed by zeros.
1969        assert_eq!(
1970            widened.row_slices().nth(1).unwrap(),
1971            &[
1972                BabyBear::new(4),
1973                BabyBear::new(5),
1974                BabyBear::new(6),
1975                BabyBear::ZERO,
1976                BabyBear::ZERO,
1977            ]
1978        );
1979
1980        // Test 2: Appending 0 columns returns an identical copy.
1981        let same = mat.with_zero_cols(0);
1982        assert_eq!(same.width(), mat.width());
1983        assert_eq!(same.values, mat.values);
1984
1985        // Test 3: Single-row matrix.
1986        //
1987        //     [ 7  8 ]  →  [ 7  8  0  0  0 ]
1988        let single_row: RowMajorMatrix<BabyBear> =
1989            RowMajorMatrix::new(vec![BabyBear::new(7), BabyBear::new(8)], 2);
1990        let widened = single_row.with_zero_cols(3);
1991        assert_eq!(widened.width(), 5);
1992        assert_eq!(widened.height(), 1);
1993        assert_eq!(
1994            widened.row_slices().next().unwrap(),
1995            &[
1996                BabyBear::new(7),
1997                BabyBear::new(8),
1998                BabyBear::ZERO,
1999                BabyBear::ZERO,
2000                BabyBear::ZERO,
2001            ]
2002        );
2003
2004        // Test 4: Single-column matrix widened to 3 columns.
2005        //
2006        //     [ 1 ]        [ 1  0  0 ]
2007        //     [ 2 ]   →    [ 2  0  0 ]
2008        //     [ 3 ]        [ 3  0  0 ]
2009        let single_col: RowMajorMatrix<BabyBear> = RowMajorMatrix::new(
2010            vec![BabyBear::new(1), BabyBear::new(2), BabyBear::new(3)],
2011            1,
2012        );
2013        let widened = single_col.with_zero_cols(2);
2014        assert_eq!(widened.width(), 3);
2015        assert_eq!(widened.height(), 3);
2016        for (i, row) in widened.row_slices().enumerate() {
2017            // Each row has the original value followed by two zeros.
2018            assert_eq!(row[0], BabyBear::new((i + 1) as u32));
2019            assert_eq!(row[1], BabyBear::ZERO);
2020            assert_eq!(row[2], BabyBear::ZERO);
2021        }
2022
2023        // Test 5: Empty matrix stays empty with updated width.
2024        let empty: RowMajorMatrix<BabyBear> = RowMajorMatrix::new(vec![], 3);
2025        let widened = empty.with_zero_cols(2);
2026        assert_eq!(widened.width(), 5);
2027        assert_eq!(widened.height(), 0);
2028        assert!(widened.values.is_empty());
2029    }
2030
2031    #[test]
2032    fn test_with_zero_cols_matches_widen_right() {
2033        // Both paths must produce identical results: cloning + in-place widen
2034        // with zero fill versus the dedicated method.
2035        //
2036        //     Original (3×4):
2037        //     [  1   2   3   4 ]
2038        //     [  5   6   7   8 ]
2039        //     [  9  10  11  12 ]
2040        let mat: RowMajorMatrix<BabyBear> =
2041            RowMajorMatrix::new((1..=12).map(BabyBear::new).collect(), 4);
2042
2043        // Produce the result via the functional method.
2044        let via_method = mat.with_zero_cols(3);
2045
2046        // Produce the result via move + in-place widen.
2047        let mut via_widen = mat;
2048        via_widen.widen_right(3, BabyBear::ZERO);
2049
2050        // Both matrices must be identical in dimensions and content.
2051        assert_eq!(via_method.width(), via_widen.width());
2052        assert_eq!(via_method.height(), via_widen.height());
2053        assert_eq!(via_method.values, via_widen.values);
2054    }
2055
2056    #[test]
2057    fn test_with_random_cols() {
2058        // Append 3 random columns to a 2×2 matrix using a seeded RNG.
2059        // We replay the same seed independently to build the exact expected
2060        // matrix, so the assertion is fully deterministic.
2061        //
2062        //     Original:          Result:
2063        //     [ 1  2 ]    →      [ 1  2  r00  r01  r02 ]
2064        //     [ 3  4 ]           [ 3  4  r10  r11  r12 ]
2065        let mat: RowMajorMatrix<BabyBear> =
2066            RowMajorMatrix::new((1..=4).map(BabyBear::new).collect(), 2);
2067
2068        let seed = 42u64;
2069        let widened = mat.with_random_cols(3, SmallRng::seed_from_u64(seed));
2070
2071        // Verify dimensions: width grows by 3, height unchanged.
2072        assert_eq!(widened.width(), 5);
2073        assert_eq!(widened.height(), 2);
2074
2075        // Replay the same seed to produce the expected random values in the
2076        // exact same order the method consumes them: row-by-row, left to right
2077        // within the appended portion.
2078        let mut reference_rng = SmallRng::seed_from_u64(seed);
2079        for (new_row, old_row) in widened.row_slices().zip(mat.row_slices()) {
2080            // Left portion must be the original data, unchanged.
2081            assert_eq!(&new_row[..2], old_row);
2082
2083            // Right portion must match the reference RNG output exactly.
2084            for val in &new_row[2..] {
2085                let expected: BabyBear = reference_rng.random();
2086                assert_eq!(*val, expected);
2087            }
2088        }
2089    }
2090
2091    #[test]
2092    fn test_with_random_cols_zero_extra() {
2093        // Appending 0 random columns returns an exact copy of the original.
2094        let mat: RowMajorMatrix<BabyBear> =
2095            RowMajorMatrix::new((1..=6).map(BabyBear::new).collect(), 3);
2096        let same = mat.with_random_cols(0, SmallRng::seed_from_u64(0));
2097        assert_eq!(same.width(), mat.width());
2098        assert_eq!(same.values, mat.values);
2099    }
2100
2101    #[test]
2102    fn test_with_random_cols_empty_matrix() {
2103        // An empty matrix (0 rows) remains empty with updated width.
2104        let empty: RowMajorMatrix<BabyBear> = RowMajorMatrix::new(vec![], 3);
2105        let widened = empty.with_random_cols(2, SmallRng::seed_from_u64(0));
2106        assert_eq!(widened.width(), 5);
2107        assert_eq!(widened.height(), 0);
2108        assert!(widened.values.is_empty());
2109    }
2110
2111    #[test]
2112    fn test_with_random_cols_different_seeds() {
2113        // Verify that two different seeds each produce the correct output by
2114        // replaying both seeds independently. This is fully deterministic.
2115        let mat: RowMajorMatrix<BabyBear> =
2116            RowMajorMatrix::new((1..=4).map(BabyBear::new).collect(), 2);
2117
2118        let num_random = 4;
2119        let seed_a = 1u64;
2120        let seed_b = 2u64;
2121
2122        let result_a = mat.with_random_cols(num_random, SmallRng::seed_from_u64(seed_a));
2123        let result_b = mat.with_random_cols(num_random, SmallRng::seed_from_u64(seed_b));
2124
2125        // Replay each seed and verify every element exactly.
2126        for (seed, result) in [(seed_a, &result_a), (seed_b, &result_b)] {
2127            let mut reference_rng = SmallRng::seed_from_u64(seed);
2128            for (new_row, old_row) in result.row_slices().zip(mat.row_slices()) {
2129                // Left portion must be the original data, unchanged.
2130                assert_eq!(&new_row[..2], old_row);
2131
2132                // Right portion must match the reference RNG output exactly.
2133                for val in &new_row[2..] {
2134                    let expected: BabyBear = reference_rng.random();
2135                    assert_eq!(*val, expected);
2136                }
2137            }
2138        }
2139    }
2140}