Skip to main content

diskann_utils/
views.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use std::{
7    fmt,
8    ops::{Index, IndexMut},
9};
10
11#[cfg(feature = "rayon")]
12use rayon::prelude::{IndexedParallelIterator, ParallelIterator, ParallelSlice, ParallelSliceMut};
13use thiserror::Error;
14
15/// Various view types (types such as [`MatrixView`] that add semantic meaning to blobs
16/// of data) need both immutable and mutable variants.
17///
18/// This trait can be implemented by wrappers for immutable and mutable slice references,
19/// allowing for a common code path for immutable and mutable view types.
20///
21/// The main goal is to provide a way of retrieving an underlying dense slice, which can
22/// then be used as the building block for higher level abstractions.
23///
24/// # Safety
25///
26/// This trait is unsafe because it requires `as_slice` to be idempotent (and unsafe code
27/// relies on this).
28///
29/// In other words: `as_slice` must **always** return the same slice with the same length.
30pub unsafe trait DenseData {
31    type Elem;
32
33    /// Return the underlying data as a slice.
34    fn as_slice(&self) -> &[Self::Elem];
35}
36
37/// A mutable companion to `DenseData`.
38///
39/// This trait allows mutable methods on view types to be selectively enabled when data
40/// underlying the type is mutable.
41///
42/// # Safety
43///
44/// This trait is unsafe because it requires `as_slice` to be idempotent (and unsafe code
45/// relies on this).
46///
47/// In other words: `as_slice` must **always** return the same slice with the same length.
48///
49/// Additionally, the returned slice must span the exact same memory as `as_slice`.
50pub unsafe trait MutDenseData: DenseData {
51    fn as_mut_slice(&mut self) -> &mut [Self::Elem];
52}
53
54// SAFETY: This fulfills the idempotency requirement.
55unsafe impl<T> DenseData for &[T] {
56    type Elem = T;
57    fn as_slice(&self) -> &[Self::Elem] {
58        self
59    }
60}
61
62// SAFETY: This fulfills the idempotency requirement.
63unsafe impl<T> DenseData for &mut [T] {
64    type Elem = T;
65    fn as_slice(&self) -> &[Self::Elem] {
66        self
67    }
68}
69
70// SAFETY: This fulfills the idempotency requirement and returns a slice spanning the same
71// range as `as_slice`.
72unsafe impl<T> MutDenseData for &mut [T] {
73    fn as_mut_slice(&mut self) -> &mut [Self::Elem] {
74        self
75    }
76}
77
78// SAFETY: This fulfills the idempotency requirement.
79unsafe impl<T> DenseData for Box<[T]> {
80    type Elem = T;
81    fn as_slice(&self) -> &[Self::Elem] {
82        self
83    }
84}
85
86// SAFETY: This fulfills the idempotency requirement and returns a slice spanning the same
87// memory as `as_slice`.
88unsafe impl<T> MutDenseData for Box<[T]> {
89    fn as_mut_slice(&mut self) -> &mut [Self::Elem] {
90        self
91    }
92}
93
94////////////
95// Matrix //
96////////////
97
98/// A view over dense chunk of memory, interpreting that memory as a 2-dimensional matrix
99/// laid out in row-major order.
100///
101/// When this class view immutable memory, it is `Copy`.
102#[derive(Debug, Clone, Copy, PartialEq)]
103pub struct MatrixBase<T>
104where
105    T: DenseData,
106{
107    data: T,
108    nrows: usize,
109    ncols: usize,
110}
111
112#[derive(Debug, Error)]
113#[non_exhaustive]
114#[error(
115    "tried to construct a matrix view with {nrows} rows and {ncols} columns over a slice \
116     of length {len}"
117)]
118pub struct TryFromErrorLight {
119    len: usize,
120    nrows: usize,
121    ncols: usize,
122}
123
124#[derive(Error)]
125#[non_exhaustive]
126#[error(
127    "tried to construct a matrix view with {nrows} rows and {ncols} columns over a slice \
128     of length {}", data.as_slice().len()
129)]
130pub struct TryFromError<T: DenseData> {
131    data: T,
132    nrows: usize,
133    ncols: usize,
134}
135
136// Manually implement `fmt::Debug` so we don't require `T::Debug`.
137impl<T: DenseData> fmt::Debug for TryFromError<T> {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        f.debug_struct("TryFromError")
140            .field("data_len", &self.data.as_slice().len())
141            .field("nrows", &self.nrows)
142            .field("ncols", &self.ncols)
143            .finish()
144    }
145}
146
147impl<T: DenseData> TryFromError<T> {
148    /// Consume the error and return the base data.
149    pub fn into_inner(self) -> T {
150        self.data
151    }
152
153    /// Return a variation of `Self` that is guaranteed to be `'static` by removing the
154    /// data that was passed to the original constructor.
155    pub fn as_static(&self) -> TryFromErrorLight {
156        TryFromErrorLight {
157            len: self.data.as_slice().len(),
158            nrows: self.nrows,
159            ncols: self.ncols,
160        }
161    }
162}
163
164/// A generator for initializing the entries in a matrix via `Matrix::new`.
165pub trait Generator<T> {
166    fn generate(&mut self) -> T;
167}
168
169impl<T> Generator<T> for T
170where
171    T: Clone,
172{
173    fn generate(&mut self) -> T {
174        self.clone()
175    }
176}
177
178/// A matrix initializer that invokes the provided lambda to initialize each element.
179pub struct Init<F>(pub F);
180
181impl<T, F> Generator<T> for Init<F>
182where
183    F: FnMut() -> T,
184{
185    fn generate(&mut self) -> T {
186        (self.0)()
187    }
188}
189
190impl<T> MatrixBase<Box<[T]>> {
191    /// Construct a new Matrix initialized with the contents of the generator.
192    ///
193    /// Elements are initialized in memory order.
194    pub fn new<U>(mut generator: U, nrows: usize, ncols: usize) -> Self
195    where
196        U: Generator<T>,
197    {
198        let data: Box<[T]> = (0..nrows * ncols).map(|_| generator.generate()).collect();
199        debug_assert_eq!(data.len(), nrows * ncols);
200        Self { data, nrows, ncols }
201    }
202}
203
204impl<T> MatrixBase<T>
205where
206    T: DenseData,
207{
208    /// Try to construct a `MatrixBase` over the provided base. If the size of the base
209    /// is incorrect, return a `TryFromError` containing the base.
210    ///
211    /// The length of the base must be equal to `nrows * ncols`.
212    pub fn try_from(data: T, nrows: usize, ncols: usize) -> Result<Self, TryFromError<T>> {
213        let len = data.as_slice().len();
214        if len != nrows * ncols {
215            Err(TryFromError { data, nrows, ncols })
216        } else {
217            Ok(Self { data, nrows, ncols })
218        }
219    }
220
221    /// Return the number of columns in the matrix.
222    pub fn ncols(&self) -> usize {
223        self.ncols
224    }
225
226    /// Return the number of rows in the matrix.
227    pub fn nrows(&self) -> usize {
228        self.nrows
229    }
230
231    /// Return the underlying data as a slice.
232    pub fn as_slice(&self) -> &[T::Elem] {
233        self.data.as_slice()
234    }
235
236    /// Return the underlying data as a mutable slice.
237    pub fn as_mut_slice(&mut self) -> &mut [T::Elem]
238    where
239        T: MutDenseData,
240    {
241        self.data.as_mut_slice()
242    }
243
244    /// Return row `row` as a slice.
245    ///
246    /// # Panic
247    ///
248    /// Panics if `row >= self.nrows()`.
249    pub fn row(&self, row: usize) -> &[T::Elem] {
250        assert!(
251            row < self.nrows(),
252            "tried to access row {row} of a matrix with {} rows",
253            self.nrows()
254        );
255
256        // SAFETY: `row` is in-bounds.
257        unsafe { self.get_row_unchecked(row) }
258    }
259
260    /// Construct a new `MatrixBase` over the raw data.
261    ///
262    /// The returned `MatrixBase` will only have a single row with contents equal to `data`.
263    pub fn row_vector(data: T) -> Self {
264        let ncols = data.as_slice().len();
265        Self {
266            data,
267            nrows: 1,
268            ncols,
269        }
270    }
271    /// Return row `row` if `row < self.nrows()`. Otherwise, return `None`.
272    pub fn get_row(&self, row: usize) -> Option<&[T::Elem]> {
273        if row < self.nrows() {
274            // SAFETY: `row` is in-bounds.
275            Some(unsafe { self.get_row_unchecked(row) })
276        } else {
277            None
278        }
279    }
280
281    /// Returns the requested row without boundschecking.
282    ///
283    /// # Safety
284    ///
285    /// The following conditions must hold to avoid undefined behavior:
286    /// * `row < self.nrows()`.
287    pub unsafe fn get_row_unchecked(&self, row: usize) -> &[T::Elem] {
288        debug_assert!(row < self.nrows);
289        let ncols = self.ncols;
290        let start = row * ncols;
291
292        debug_assert!(start + ncols <= self.as_slice().len());
293        // SAFETY: The idempotency requirement of `as_slice` and our audited constructors
294        // mean that `self.as_slice()` has a length of `self.nrows * self.ncols`.
295        //
296        // Therefore, this access is in-bounds.
297        unsafe { self.as_slice().get_unchecked(start..start + ncols) }
298    }
299
300    /// Return row `row` as a mutable slice.
301    ///
302    /// # Panics
303    ///
304    /// Panics if `row >= self.nrows()`.
305    pub fn row_mut(&mut self, row: usize) -> &mut [T::Elem]
306    where
307        T: MutDenseData,
308    {
309        assert!(
310            row < self.nrows(),
311            "tried to access row {row} of a matrix with {} rows",
312            self.nrows()
313        );
314
315        // SAFETY: `row` is in-bounds.
316        unsafe { self.get_row_unchecked_mut(row) }
317    }
318
319    /// Returns the requested row without boundschecking.
320    ///
321    /// # Safety
322    ///
323    /// The following conditions must hold to avoid undefined behavior:
324    /// * `row < self.nrows()`.
325    pub unsafe fn get_row_unchecked_mut(&mut self, row: usize) -> &mut [T::Elem]
326    where
327        T: MutDenseData,
328    {
329        debug_assert!(row < self.nrows);
330        let ncols = self.ncols;
331        let start = row * ncols;
332
333        debug_assert!(start + ncols <= self.as_slice().len());
334        // SAFETY: The idempotency requirement of `as_mut_slice` and our audited constructors
335        // mean that `self.as_mut_slice()` has a length of `self.nrows * self.ncols`.
336        //
337        // Therefore, this access is in-bounds.
338        unsafe {
339            self.data
340                .as_mut_slice()
341                .get_unchecked_mut(start..start + ncols)
342        }
343    }
344
345    /// Return a iterator over all rows in the matrix.
346    ///
347    /// Rows are yielded sequentially beginning with row 0.
348    pub fn row_iter(&self) -> impl ExactSizeIterator<Item = &[T::Elem]> {
349        self.data.as_slice().chunks_exact(self.ncols())
350    }
351
352    /// Return a mutable iterator over all rows in the matrix.
353    ///
354    /// Rows are yielded sequentially beginning with row 0.
355    pub fn row_iter_mut(&mut self) -> impl ExactSizeIterator<Item = &mut [T::Elem]>
356    where
357        T: MutDenseData,
358    {
359        let ncols = self.ncols();
360        self.data.as_mut_slice().chunks_exact_mut(ncols)
361    }
362
363    /// Return an iterator that divides the matrix into sub-matrices with (up to)
364    /// `batchsize` rows with `self.ncols()` columns.
365    ///
366    /// It is possible for yielded sub-matrices to have fewer than `batchsize` rows if the
367    /// number of rows in the parent matrix is not evenly divisible by `batchsize`.
368    ///
369    /// # Panics
370    ///
371    /// Panics if `batchsize = 0`.
372    pub fn window_iter(&self, batchsize: usize) -> impl Iterator<Item = MatrixView<'_, T::Elem>>
373    where
374        T::Elem: Sync,
375    {
376        assert!(batchsize != 0, "window_iter batchsize cannot be zero");
377        let ncols = self.ncols();
378        self.data
379            .as_slice()
380            .chunks(ncols * batchsize)
381            .map(move |data| {
382                let blobsize = data.len();
383                let nrows = blobsize / ncols;
384                assert_eq!(blobsize % ncols, 0);
385                MatrixView { data, nrows, ncols }
386            })
387    }
388
389    /// Return a parallel iterator that divides the matrix into sub-matrices with (up to)
390    /// `batchsize` rows with `self.ncols()` columns.
391    ///
392    /// This allows workers in parallel algorithms to work on dense subsets of the whole
393    /// matrix for better locality.
394    ///
395    /// It is possible for yielded sub-matrices to have fewer than `batchsize` rows if the
396    /// number of rows in the parent matrix is not evenly divisible by `batchsize`.
397    ///
398    /// # Panics
399    ///
400    /// Panics if `batchsize = 0`.
401    #[cfg(feature = "rayon")]
402    pub fn par_window_iter(
403        &self,
404        batchsize: usize,
405    ) -> impl IndexedParallelIterator<Item = MatrixView<'_, T::Elem>>
406    where
407        T::Elem: Sync,
408    {
409        assert!(batchsize != 0, "par_window_iter batchsize cannot be zero");
410        let ncols = self.ncols();
411        self.data
412            .as_slice()
413            .par_chunks(ncols * batchsize)
414            .map(move |data| {
415                let blobsize = data.len();
416                let nrows = blobsize / ncols;
417                assert_eq!(blobsize % ncols, 0);
418                MatrixView { data, nrows, ncols }
419            })
420    }
421
422    /// Return a parallel iterator that divides the matrix into mutable sub-matrices with
423    /// (up to) `batchsize` rows with `self.ncols()` columns.
424    ///
425    /// This allows workers in parallel algorithms to work on dense subsets of the whole
426    /// matrix for better locality.
427    ///
428    /// It is possible for yielded sub-matrices to have fewer than `batchsize` rows if the
429    /// number of rows in the parent matrix is not evenly divisible by `batchsize`.
430    ///
431    /// # Panics
432    ///
433    /// Panics if `batchsize = 0`.
434    #[cfg(feature = "rayon")]
435    pub fn par_window_iter_mut(
436        &mut self,
437        batchsize: usize,
438    ) -> impl IndexedParallelIterator<Item = MutMatrixView<'_, T::Elem>>
439    where
440        T: MutDenseData,
441        T::Elem: Send,
442    {
443        assert!(
444            batchsize != 0,
445            "par_window_iter_mut batchsize cannot be zero"
446        );
447        let ncols = self.ncols();
448        self.data
449            .as_mut_slice()
450            .par_chunks_mut(ncols * batchsize)
451            .map(move |data| {
452                let blobsize = data.len();
453                let nrows = blobsize / ncols;
454                assert_eq!(blobsize % ncols, 0);
455                MutMatrixView { data, nrows, ncols }
456            })
457    }
458
459    /// Return a parallel iterator over the rows of the matrix.
460    #[cfg(feature = "rayon")]
461    pub fn par_row_iter(&self) -> impl IndexedParallelIterator<Item = &[T::Elem]>
462    where
463        T::Elem: Sync,
464    {
465        self.as_slice().par_chunks_exact(self.ncols())
466    }
467
468    /// Return a parallel iterator over the rows of the matrix.
469    #[cfg(feature = "rayon")]
470    pub fn par_row_iter_mut(&mut self) -> impl IndexedParallelIterator<Item = &mut [T::Elem]>
471    where
472        T: MutDenseData,
473        T::Elem: Send,
474    {
475        let ncols = self.ncols();
476        self.as_mut_slice().par_chunks_exact_mut(ncols)
477    }
478
479    /// Consume the matrix, returning the inner representation.
480    ///
481    /// This loses the information about the number of rows and columnts.
482    pub fn into_inner(self) -> T {
483        self.data
484    }
485
486    /// Return a view over the matrix.
487    pub fn as_view(&self) -> MatrixView<'_, T::Elem> {
488        MatrixBase {
489            data: self.as_slice(),
490            nrows: self.nrows(),
491            ncols: self.ncols(),
492        }
493    }
494
495    /// Return a mutable view over the matrix.
496    pub fn as_mut_view(&mut self) -> MutMatrixView<'_, T::Elem>
497    where
498        T: MutDenseData,
499    {
500        let nrows = self.nrows();
501        let ncols = self.ncols();
502        MatrixBase {
503            data: self.as_mut_slice(),
504            nrows,
505            ncols,
506        }
507    }
508
509    /// Return a view over the specified rows of the matrix.
510    ///
511    /// If the specified range is out of bounds, return `None`.
512    ///
513    /// ```rust
514    /// use diskann_utils::views::Matrix;
515    ///
516    /// let mut mat = Matrix::new(0usize, 4, 3);
517    ///
518    /// // Fill the matrix with some data.
519    /// mat.row_iter_mut().enumerate().for_each(|(i, row)| row.fill(i));
520    ///
521    /// // Creating a subview into an offset portion of the matrix.
522    /// let subview = mat.subview(1..3).unwrap();
523    /// assert_eq!(subview.nrows(), 2);
524    /// assert_eq!(subview.row(0), &[1, 1, 1]);
525    /// assert_eq!(subview.row(1), &[2, 2, 2]);
526    ///
527    /// // A trying to access out-of-bounds returns `None`
528    /// assert!(mat.subview(3..5).is_none());
529    /// ```
530    pub fn subview(&self, rows: std::ops::Range<usize>) -> Option<MatrixView<'_, T::Elem>> {
531        let ncols = self.ncols();
532
533        let lower = rows.start.checked_mul(ncols)?;
534        let upper = rows.end.checked_mul(ncols)?;
535
536        if let Some(data) = self.as_slice().get(lower..upper) {
537            Some(MatrixBase {
538                data,
539                nrows: rows.len(),
540                ncols: self.ncols(),
541            })
542        } else {
543            None
544        }
545    }
546
547    /// Return a pointer to the base of the matrix.
548    pub fn as_ptr(&self) -> *const T::Elem {
549        self.as_slice().as_ptr()
550    }
551
552    /// Return a pointer to the base of the matrix.
553    pub fn as_mut_ptr(&mut self) -> *mut T::Elem
554    where
555        T: MutDenseData,
556    {
557        self.as_mut_slice().as_mut_ptr()
558    }
559
560    /// Returns a reference to an element without boundschecking.
561    ///
562    /// # Safety
563    ///
564    /// The following conditions must hold to avoid undefined behavior:
565    /// * `row < self.nrows()`.
566    /// * `col < self.ncols()`.
567    pub unsafe fn get_unchecked(&self, row: usize, col: usize) -> &T::Elem {
568        debug_assert!(row < self.nrows);
569        debug_assert!(col < self.ncols);
570        self.as_slice().get_unchecked(row * self.ncols + col)
571    }
572
573    /// Returns a mutable reference to an element without boundschecking.
574    ///
575    /// # Safety
576    ///
577    /// The following conditions must hold to avoid undefined behavior:
578    /// * `row < self.nrows()`.
579    /// * `col < self.ncols()`.
580    pub unsafe fn get_unchecked_mut(&mut self, row: usize, col: usize) -> &mut T::Elem
581    where
582        T: MutDenseData,
583    {
584        let ncols = self.ncols;
585        debug_assert!(row < self.nrows);
586        debug_assert!(col < self.ncols);
587        self.as_mut_slice().get_unchecked_mut(row * ncols + col)
588    }
589
590    pub fn to_owned(&self) -> Matrix<T::Elem>
591    where
592        T::Elem: Clone,
593    {
594        Matrix {
595            data: self.data.as_slice().into(),
596            nrows: self.nrows,
597            ncols: self.ncols,
598        }
599    }
600}
601
602/// Represents an owning, 2-dimensional view of a contiguous block of memory,
603/// interpreted as a matrix in row-major order.
604pub type Matrix<T> = MatrixBase<Box<[T]>>;
605
606/// Represents a non-owning, 2-dimensional view of a contiguous block of memory,
607/// interpreted as a matrix in row-major order.
608///
609/// This type is useful for functions that need to read matrix data without taking ownership.
610/// By accepting a `MatrixView`, such functions can operate on both owned matrices (by converting them
611/// to a `MatrixView`) and existing non-owning views.
612pub type MatrixView<'a, T> = MatrixBase<&'a [T]>;
613
614/// Represents a mutable non-owning, 2-dimensional view of a contiguous block of memory,
615/// interpreted as a matrix in row-major order.
616///
617/// This type is useful for functions that need to modify matrix data without taking ownership.
618/// By accepting a `MutMatrixView`, such functions can operate on both owned matrices (by converting them
619/// to a `MutMatrixView`) and existing non-owning mutable views.
620pub type MutMatrixView<'a, T> = MatrixBase<&'a mut [T]>;
621
622/// Allow matrix views to be converted directly to slices.
623impl<'a, T> From<MatrixView<'a, T>> for &'a [T] {
624    fn from(view: MatrixView<'a, T>) -> Self {
625        view.data
626    }
627}
628
629/// Allow mutable matrix views to be converted directly to slices.
630impl<'a, T> From<MutMatrixView<'a, T>> for &'a [T] {
631    fn from(view: MutMatrixView<'a, T>) -> Self {
632        view.data
633    }
634}
635
636/// Return a reference to the item at entry `(row, col)` in the matrix.
637///
638/// # Panics
639///
640/// Panics if `row >= self.nrows()` or `col >= self.ncols()`.
641impl<T> Index<(usize, usize)> for MatrixBase<T>
642where
643    T: DenseData,
644{
645    type Output = T::Elem;
646
647    fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
648        assert!(
649            row < self.nrows(),
650            "row {row} is out of bounds (max: {})",
651            self.nrows()
652        );
653        assert!(
654            col < self.ncols(),
655            "col {col} is out of bounds (max: {})",
656            self.ncols()
657        );
658
659        // SAFETY: We have checked that `row` and `col` are in-bounds.
660        unsafe { self.get_unchecked(row, col) }
661    }
662}
663
664/// Return a mutable reference to the item at entry `(row, col)` in the matrix.
665///
666/// # Panics
667///
668/// Panics if `row >= self.nrows()` or `col >= self.ncols()`.
669impl<T> IndexMut<(usize, usize)> for MatrixBase<T>
670where
671    T: MutDenseData,
672{
673    fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
674        assert!(
675            row < self.nrows(),
676            "row {row} is out of bounds (max: {})",
677            self.nrows()
678        );
679        assert!(
680            col < self.ncols(),
681            "col {col} is out of bounds (max: {})",
682            self.ncols()
683        );
684
685        // SAFETY: We have checked that `row` and `col` are in-bounds.
686        unsafe { self.get_unchecked_mut(row, col) }
687    }
688}
689
690///////////
691// Tests //
692///////////
693
694#[cfg(test)]
695mod tests {
696    use super::*;
697    use crate::lazy_format;
698
699    /// This function is only callable with copyable types.
700    ///
701    /// This lets us test for types we expect to be `Copy`.
702    fn is_copyable<T: Copy>(_x: T) -> bool {
703        true
704    }
705
706    /// Test the that provided representation yields a slice with the expected base pointer
707    /// and length.
708    fn test_dense_data_repr<T, Repr>(
709        ptr: *const T,
710        len: usize,
711        repr: Repr,
712        context: &dyn std::fmt::Display,
713    ) where
714        T: Copy,
715        Repr: DenseData<Elem = T>,
716    {
717        let retrieved = repr.as_slice();
718        assert_eq!(retrieved.len(), len, "{}", context);
719        assert_eq!(retrieved.as_ptr(), ptr, "{}", context);
720    }
721
722    /// Set the underlying data for the provided representation to the following:
723    ///
724    /// [base, base + increment, base + increment + increment, ...]
725    fn set_mut_dense_data_repr<T, Repr>(repr: &mut Repr, base: T, increment: T)
726    where
727        T: Copy + std::ops::Add<Output = T>,
728        Repr: DenseData<Elem = T> + MutDenseData,
729    {
730        let slice = repr.as_mut_slice();
731        for i in 0..slice.len() {
732            if i == 0 {
733                slice[i] = base;
734            } else {
735                slice[i] = slice[i - 1] + increment;
736            }
737        }
738    }
739
740    #[test]
741    fn slice_implements_dense_data_repr() {
742        for len in 0..10 {
743            let context = lazy_format!("len = {}", len);
744            let data: Vec<f32> = vec![0.0; len];
745            let slice = data.as_slice();
746            test_dense_data_repr(slice.as_ptr(), slice.len(), slice, &context);
747        }
748    }
749
750    #[test]
751    fn mut_slice_mplements_dense_data_repr() {
752        for len in 0..10 {
753            let context = lazy_format!("len = {}", len);
754            let mut data: Vec<f32> = vec![0.0; len];
755            let slice = data.as_mut_slice();
756
757            let ptr = slice.as_ptr();
758            let len = slice.len();
759            test_dense_data_repr(ptr, len, slice, &context);
760        }
761    }
762
763    #[test]
764    fn mut_slice_implements_mut_dense_data_repr() {
765        for len in 0..10 {
766            let context = lazy_format!("len = {}", len);
767            let mut data: Vec<f32> = vec![0.0; len];
768            let mut slice = data.as_mut_slice();
769
770            let base = 2.0;
771            let increment = 1.0;
772            set_mut_dense_data_repr(&mut slice, base, increment);
773
774            for (i, &v) in slice.iter().enumerate() {
775                let context = lazy_format!("entry {}, {}", i, context);
776                assert_eq!(v, base + increment * (i as f32), "{}", context);
777            }
778        }
779    }
780
781    /////////////////
782    // Matrix View //
783    /////////////////
784
785    #[test]
786    fn try_from_error_misc() {
787        let x = TryFromError::<&[f32]> {
788            data: &[],
789            nrows: 1,
790            ncols: 2,
791        };
792
793        let debug = format!("{:?}", x);
794        println!("debug = {}", debug);
795        assert!(debug.contains("TryFromError"));
796        assert!(debug.contains("data_len: 0"));
797        assert!(debug.contains("nrows: 1"));
798        assert!(debug.contains("ncols: 2"));
799    }
800
801    fn make_test_matrix() -> Vec<usize> {
802        // Construct a matrix with 4 rows of length 3.
803        // The expected layout is as follows:
804        //
805        // 0, 1, 2,
806        // 1, 2, 3,
807        // 2, 3, 4,
808        // 3, 4, 5
809        //
810        vec![0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]
811    }
812
813    #[cfg(feature = "rayon")]
814    fn test_basic_indexing_parallel(m: MatrixView<'_, usize>) {
815        // Par window iters.
816        let batchsize = 2;
817        m.par_window_iter(batchsize)
818            .enumerate()
819            .for_each(|(i, submatrix)| {
820                assert_eq!(submatrix.nrows(), batchsize);
821                assert_eq!(submatrix.ncols(), m.ncols());
822
823                // Make sure we are in the correct window of the original matrix.
824                let base = i * batchsize;
825                assert_eq!(submatrix[(0, 0)], base);
826                assert_eq!(submatrix[(0, 1)], base + 1);
827                assert_eq!(submatrix[(0, 2)], base + 2);
828
829                assert_eq!(submatrix[(1, 0)], base + 1);
830                assert_eq!(submatrix[(1, 1)], base + 2);
831                assert_eq!(submatrix[(1, 2)], base + 3);
832            });
833
834        // Try again, but with a batch size of 3 to ensure that we correctly handle cases
835        // where the last block is under-sized.
836        let batchsize = 3;
837        m.par_window_iter(batchsize)
838            .enumerate()
839            .for_each(|(i, submatrix)| {
840                if i == 0 {
841                    assert_eq!(submatrix.nrows(), batchsize);
842                    assert_eq!(submatrix.ncols(), m.ncols());
843
844                    // Check indexing
845                    assert_eq!(submatrix[(0, 0)], 0);
846                    assert_eq!(submatrix[(0, 1)], 1);
847                    assert_eq!(submatrix[(0, 2)], 2);
848
849                    assert_eq!(submatrix[(1, 0)], 1);
850                    assert_eq!(submatrix[(1, 1)], 2);
851                    assert_eq!(submatrix[(1, 2)], 3);
852
853                    assert_eq!(submatrix[(2, 0)], 2);
854                    assert_eq!(submatrix[(2, 1)], 3);
855                    assert_eq!(submatrix[(2, 2)], 4);
856                } else {
857                    assert_eq!(submatrix.nrows(), 1);
858                    assert_eq!(submatrix.ncols(), m.ncols());
859
860                    // Check indexing
861                    assert_eq!(submatrix[(0, 0)], 3);
862                    assert_eq!(submatrix[(0, 1)], 4);
863                    assert_eq!(submatrix[(0, 2)], 5);
864                }
865            });
866
867        // par-row-iter
868        let seen_rows: Box<[usize]> = m
869            .par_row_iter()
870            .enumerate()
871            .map(|(i, row)| {
872                let expected: Box<[usize]> = (0..m.ncols()).map(|j| j + i).collect();
873                assert_eq!(row, &*expected);
874                i
875            })
876            .collect();
877
878        let expected: Box<[usize]> = (0..m.nrows()).collect();
879        assert_eq!(seen_rows, expected);
880    }
881
882    fn test_basic_indexing<T>(m: &MatrixBase<T>)
883    where
884        T: DenseData<Elem = usize> + Sync,
885    {
886        assert_eq!(m.nrows(), 4);
887        assert_eq!(m.ncols(), 3);
888
889        // Basic indexing
890        assert_eq!(m[(0, 0)], 0);
891        assert_eq!(m[(0, 1)], 1);
892        assert_eq!(m[(0, 2)], 2);
893
894        assert_eq!(m[(1, 0)], 1);
895        assert_eq!(m[(1, 1)], 2);
896        assert_eq!(m[(1, 2)], 3);
897
898        assert_eq!(m[(2, 0)], 2);
899        assert_eq!(m[(2, 1)], 3);
900        assert_eq!(m[(2, 2)], 4);
901
902        assert_eq!(m[(3, 0)], 3);
903        assert_eq!(m[(3, 1)], 4);
904        assert_eq!(m[(3, 2)], 5);
905
906        // Row indexing.
907        assert_eq!(m.row(0), &[0, 1, 2]);
908        assert_eq!(m.row(1), &[1, 2, 3]);
909        assert_eq!(m.row(2), &[2, 3, 4]);
910        assert_eq!(m.row(3), &[3, 4, 5]);
911
912        let rows: Vec<Vec<usize>> = m.row_iter().map(|x| x.to_vec()).collect();
913        assert_eq!(m.row(0), &rows[0]);
914        assert_eq!(m.row(1), &rows[1]);
915        assert_eq!(m.row(2), &rows[2]);
916        assert_eq!(m.row(3), &rows[3]);
917
918        // Window Iters.
919        let batchsize = 2;
920        m.window_iter(batchsize)
921            .enumerate()
922            .for_each(|(i, submatrix)| {
923                assert_eq!(submatrix.nrows(), batchsize);
924                assert_eq!(submatrix.ncols(), m.ncols());
925
926                // Make sure we are in the correct window of the original matrix.
927                let base = i * batchsize;
928                assert_eq!(submatrix[(0, 0)], base);
929                assert_eq!(submatrix[(0, 1)], base + 1);
930                assert_eq!(submatrix[(0, 2)], base + 2);
931
932                assert_eq!(submatrix[(1, 0)], base + 1);
933                assert_eq!(submatrix[(1, 1)], base + 2);
934                assert_eq!(submatrix[(1, 2)], base + 3);
935            });
936
937        // Try again, but with a batch size of 3 to ensure that we correctly handle cases
938        // where the last block is under-sized.
939        let batchsize = 3;
940        m.window_iter(batchsize)
941            .enumerate()
942            .for_each(|(i, submatrix)| {
943                if i == 0 {
944                    assert_eq!(submatrix.nrows(), batchsize);
945                    assert_eq!(submatrix.ncols(), m.ncols());
946
947                    // Check indexing
948                    assert_eq!(submatrix[(0, 0)], 0);
949                    assert_eq!(submatrix[(0, 1)], 1);
950                    assert_eq!(submatrix[(0, 2)], 2);
951
952                    assert_eq!(submatrix[(1, 0)], 1);
953                    assert_eq!(submatrix[(1, 1)], 2);
954                    assert_eq!(submatrix[(1, 2)], 3);
955
956                    assert_eq!(submatrix[(2, 0)], 2);
957                    assert_eq!(submatrix[(2, 1)], 3);
958                    assert_eq!(submatrix[(2, 2)], 4);
959                } else {
960                    assert_eq!(submatrix.nrows(), 1);
961                    assert_eq!(submatrix.ncols(), m.ncols());
962
963                    // Check indexing
964                    assert_eq!(submatrix[(0, 0)], 3);
965                    assert_eq!(submatrix[(0, 1)], 4);
966                    assert_eq!(submatrix[(0, 2)], 5);
967                }
968            });
969
970        #[cfg(all(not(miri), feature = "rayon"))]
971        test_basic_indexing_parallel(m.as_view());
972    }
973
974    #[test]
975    fn matrix_happy_path() {
976        let data = make_test_matrix();
977        let m = Matrix::try_from(data.into(), 4, 3).unwrap();
978        test_basic_indexing(&m);
979
980        // Get the base pointer of the matrix and make sure view-conversion preserves this
981        // value.
982        let ptr = m.as_ptr();
983        let view = m.as_view();
984        assert!(is_copyable(view));
985        assert_eq!(view.as_ptr(), ptr);
986        assert_eq!(view.nrows(), m.nrows());
987        assert_eq!(view.ncols(), m.ncols());
988        test_basic_indexing(&view);
989    }
990
991    #[test]
992    fn matrix_try_from_construction_error() {
993        let data = make_test_matrix();
994        let ptr = data.as_ptr();
995        let len = data.len();
996
997        let m = Matrix::try_from(data.into(), 5, 4);
998        assert!(m.is_err());
999        let err = m.unwrap_err();
1000        assert_eq!(
1001            err.to_string(),
1002            "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1003        );
1004
1005        // Make sure that we can retrieve the original allocation from the interior.
1006        let data = err.into_inner();
1007        assert_eq!(data.as_ptr(), ptr);
1008        assert_eq!(data.len(), len);
1009
1010        let m = MatrixView::try_from(&data, 5, 4);
1011        assert!(m.is_err());
1012        assert_eq!(
1013            m.unwrap_err().to_string(),
1014            "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1015        );
1016    }
1017
1018    #[test]
1019    fn matrix_mut_view() {
1020        let mut m = Matrix::<usize>::new(0, 4, 3);
1021        assert_eq!(m.nrows(), 4);
1022        assert_eq!(m.ncols(), 3);
1023        assert!(m.as_slice().iter().all(|&i| i == 0));
1024        let ptr = m.as_ptr();
1025        let mut_ptr = m.as_mut_ptr();
1026        assert_eq!(ptr, mut_ptr);
1027
1028        let mut view = m.as_mut_view();
1029        assert_eq!(view.nrows(), 4);
1030        assert_eq!(view.ncols(), 3);
1031        assert_eq!(view.as_ptr(), ptr);
1032        assert_eq!(view.as_mut_ptr(), mut_ptr);
1033
1034        // Construct the test matrix manually.
1035        for i in 0..view.nrows() {
1036            for j in 0..view.ncols() {
1037                view[(i, j)] = i + j;
1038            }
1039        }
1040
1041        // Drop the view and test the original matrix.
1042        test_basic_indexing(&m);
1043
1044        let inner = m.into_inner();
1045        assert_eq!(inner.as_ptr(), ptr);
1046        assert_eq!(inner.len(), 4 * 3);
1047    }
1048
1049    #[test]
1050    fn matrix_view_zero_sizes() {
1051        let data: Vec<usize> = vec![];
1052        // Zero rows, but non-zero columns.
1053        let m = MatrixView::try_from(data.as_slice(), 0, 10).unwrap();
1054        assert_eq!(m.nrows(), 0);
1055        assert_eq!(m.ncols(), 10);
1056
1057        // Non-zero rows, but zero columns.
1058        let m = MatrixView::try_from(data.as_slice(), 3, 0).unwrap();
1059        assert_eq!(m.nrows(), 3);
1060        assert_eq!(m.ncols(), 0);
1061        let empty: &[usize] = &[];
1062        assert_eq!(m.row(0), empty);
1063        assert_eq!(m.row(1), empty);
1064        assert_eq!(m.row(2), empty);
1065
1066        // Zero rows and columns.
1067        let m = MatrixView::try_from(data.as_slice(), 0, 0).unwrap();
1068        assert_eq!(m.nrows(), 0);
1069        assert_eq!(m.ncols(), 0);
1070    }
1071
1072    #[test]
1073    fn matrix_view_construction_elementwise() {
1074        let mut m = Matrix::<usize>::new(0, 4, 3);
1075
1076        // Construct the test matrix manually.
1077        for i in 0..m.nrows() {
1078            for j in 0..m.ncols() {
1079                m[(i, j)] = i + j;
1080            }
1081        }
1082        test_basic_indexing(&m);
1083    }
1084
1085    #[test]
1086    fn matrix_construction_by_row() {
1087        let mut m = Matrix::<usize>::new(0, 4, 3);
1088        assert!(m.as_slice().iter().all(|i| *i == 0));
1089
1090        let ncols = m.ncols();
1091        for i in 0..m.nrows() {
1092            let row = m.row_mut(i);
1093            assert_eq!(row.len(), ncols);
1094            row[0] = i;
1095            row[1] = i + 1;
1096            row[2] = i + 2;
1097        }
1098        test_basic_indexing(&m);
1099    }
1100
1101    #[test]
1102    fn matrix_construction_by_rowiter() {
1103        let mut m = Matrix::<usize>::new(0, 4, 3);
1104        assert!(m.as_slice().iter().all(|i| *i == 0));
1105
1106        let ncols = m.ncols();
1107        m.row_iter_mut().enumerate().for_each(|(i, row)| {
1108            assert_eq!(row.len(), ncols);
1109            row[0] = i;
1110            row[1] = i + 1;
1111            row[2] = i + 2;
1112        });
1113        test_basic_indexing(&m);
1114    }
1115
1116    #[cfg(all(not(miri), feature = "rayon"))]
1117    #[test]
1118    fn matrix_construction_by_par_windows() {
1119        let mut m = Matrix::<usize>::new(0, 4, 3);
1120        assert!(m.as_slice().iter().all(|i| *i == 0));
1121
1122        let ncols = m.ncols();
1123        for batchsize in 1..=4 {
1124            m.par_window_iter_mut(batchsize)
1125                .enumerate()
1126                .for_each(|(i, mut submatrix)| {
1127                    let base = i * batchsize;
1128                    submatrix.row_iter_mut().enumerate().for_each(|(j, row)| {
1129                        assert_eq!(row.len(), ncols);
1130                        row[0] = base + j;
1131                        row[1] = base + j + 1;
1132                        row[2] = base + j + 2;
1133                    });
1134                });
1135            test_basic_indexing(&m);
1136        }
1137    }
1138
1139    #[test]
1140    fn matrix_construction_happens_in_memory_order() {
1141        let mut i = 0;
1142        let ncols = 3;
1143        let initializer = Init(|| {
1144            let value = (i % ncols) + (i / ncols);
1145            i += 1;
1146            value
1147        });
1148
1149        let m = Matrix::new(initializer, 4, 3);
1150        test_basic_indexing(&m);
1151    }
1152
1153    // Panics
1154    #[test]
1155    #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1156    fn test_get_row_panics() {
1157        let m = Matrix::<usize>::new(0, 3, 7);
1158        m.row(3);
1159    }
1160
1161    #[test]
1162    #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1163    fn test_get_row_mut_panics() {
1164        let mut m = Matrix::<usize>::new(0, 3, 7);
1165        m.row_mut(3);
1166    }
1167
1168    #[test]
1169    #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1170    fn test_index_panics_row() {
1171        let m = Matrix::<usize>::new(0, 3, 7);
1172        let _ = m[(3, 2)];
1173    }
1174
1175    #[test]
1176    #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1177    fn test_index_panics_col() {
1178        let m = Matrix::<usize>::new(0, 3, 7);
1179        let _ = m[(2, 7)];
1180    }
1181
1182    #[test]
1183    #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1184    fn test_index_mut_panics_row() {
1185        let mut m = Matrix::<usize>::new(0, 3, 7);
1186        m[(3, 2)] = 1;
1187    }
1188
1189    #[test]
1190    #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1191    fn test_index_mut_panics_col() {
1192        let mut m = Matrix::<usize>::new(0, 3, 7);
1193        m[(2, 7)] = 1;
1194    }
1195
1196    #[test]
1197    #[cfg(feature = "rayon")]
1198    #[should_panic(expected = "par_window_iter batchsize cannot be zero")]
1199    fn test_par_window_iter_panics() {
1200        let m = Matrix::<usize>::new(0, 4, 4);
1201        let _ = m.par_window_iter(0);
1202    }
1203
1204    #[test]
1205    #[cfg(feature = "rayon")]
1206    #[should_panic(expected = "par_window_iter_mut batchsize cannot be zero")]
1207    fn test_par_window_iter_mut_panics() {
1208        let mut m = Matrix::<usize>::new(0, 4, 4);
1209        let _ = m.par_window_iter_mut(0);
1210    }
1211
1212    // Additional tests for better coverage
1213
1214    #[test]
1215    fn test_box_slice_dense_data_impls() {
1216        // Test Box<[T]> implementations
1217        let data: Box<[f32]> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0].into();
1218        let ptr = data.as_ptr();
1219        let len = data.len();
1220
1221        // Test DenseData impl for Box<[T]>
1222        test_dense_data_repr(ptr, len, data, &lazy_format!("Box<[T]> DenseData"));
1223
1224        // Test MutDenseData impl for Box<[T]>
1225        let mut data: Box<[f32]> = vec![0.0; 6].into();
1226        set_mut_dense_data_repr(&mut data, 1.0, 2.0);
1227        for (i, &v) in data.iter().enumerate() {
1228            assert_eq!(
1229                v,
1230                1.0 + 2.0 * (i as f32),
1231                "Box<[T]> MutDenseData at index {}",
1232                i
1233            );
1234        }
1235    }
1236
1237    #[test]
1238    fn test_try_from_error_light() {
1239        let data = vec![1, 2, 3];
1240        let err = MatrixView::try_from(data.as_slice(), 2, 3).unwrap_err();
1241
1242        // Test as_static method
1243        let static_err = err.as_static();
1244        assert_eq!(static_err.len, 3);
1245        assert_eq!(static_err.nrows, 2);
1246        assert_eq!(static_err.ncols, 3);
1247
1248        // Test Display for TryFromErrorLight
1249        let display_msg = format!("{}", static_err);
1250        assert!(display_msg.contains("tried to construct a matrix view with 2 rows and 3 columns"));
1251        assert!(display_msg.contains("slice of length 3"));
1252
1253        // Test into_inner method
1254        let recovered_data = err.into_inner();
1255        assert_eq!(recovered_data, data.as_slice());
1256    }
1257
1258    #[test]
1259    fn test_get_row_optional() {
1260        let data = make_test_matrix();
1261        let m = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1262
1263        // Test successful get_row
1264        assert_eq!(m.get_row(0), Some(&[0, 1, 2][..]));
1265        assert_eq!(m.get_row(1), Some(&[1, 2, 3][..]));
1266        assert_eq!(m.get_row(3), Some(&[3, 4, 5][..]));
1267
1268        // Test out-of-bounds get_row
1269        assert_eq!(m.get_row(4), None);
1270        assert_eq!(m.get_row(100), None);
1271    }
1272
1273    #[test]
1274    fn test_unsafe_get_unchecked_methods() {
1275        let data = make_test_matrix();
1276        let mut m = Matrix::try_from(data.into(), 4, 3).unwrap();
1277
1278        // Safety: derives from known size of matrix and access element ids
1279        unsafe {
1280            assert_eq!(*m.get_unchecked(0, 0), 0);
1281            assert_eq!(*m.get_unchecked(1, 2), 3);
1282            assert_eq!(*m.get_unchecked(3, 1), 4);
1283        }
1284
1285        // Safety: derives from known size of matrix and access element ids
1286        unsafe {
1287            *m.get_unchecked_mut(0, 0) = 100;
1288            *m.get_unchecked_mut(1, 2) = 200;
1289        }
1290
1291        assert_eq!(m[(0, 0)], 100);
1292        assert_eq!(m[(1, 2)], 200);
1293
1294        // Safety: derives from known size of matrix and access element ids
1295        unsafe {
1296            let row0 = m.get_row_unchecked(0);
1297            assert_eq!(row0[0], 100);
1298            assert_eq!(row0[1], 1);
1299            assert_eq!(row0[2], 2);
1300        }
1301
1302        // Safety: derives from known size of matrix and access element ids
1303        unsafe {
1304            let row1 = m.get_row_unchecked_mut(1);
1305            row1[0] = 300;
1306        }
1307
1308        assert_eq!(m[(1, 0)], 300);
1309    }
1310
1311    #[test]
1312    fn test_to_owned() {
1313        let data = make_test_matrix();
1314        let view = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1315
1316        // Test to_owned creates a proper clone
1317        let owned = view.to_owned();
1318        assert_eq!(owned.nrows(), view.nrows());
1319        assert_eq!(owned.ncols(), view.ncols());
1320        assert_eq!(owned.as_slice(), view.as_slice());
1321
1322        // Verify it's actually owned (different memory location)
1323        assert_ne!(owned.as_ptr(), view.as_ptr());
1324
1325        // Test the owned matrix works properly
1326        test_basic_indexing(&owned);
1327    }
1328
1329    #[test]
1330    fn test_generator_trait_impls() {
1331        // Test Generator impl for T where T: Clone
1332        let mut gen = 42i32;
1333        assert_eq!(gen.generate(), 42);
1334        assert_eq!(gen.generate(), 42); // Should be same value since it's cloned
1335
1336        // Test Generator impl for Init<F>
1337        let mut counter = 0;
1338        let mut gen = Init(|| {
1339            counter += 1;
1340            counter
1341        });
1342        assert_eq!(gen.generate(), 1);
1343        assert_eq!(gen.generate(), 2);
1344        assert_eq!(gen.generate(), 3);
1345    }
1346
1347    #[test]
1348    fn test_matrix_from_conversions() {
1349        let data = make_test_matrix();
1350        let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1351
1352        // Test MatrixView to slice conversion
1353        let view = m.as_view();
1354        let slice: &[usize] = view.into();
1355        assert_eq!(slice.len(), 12);
1356        assert_eq!(slice[0], 0);
1357        assert_eq!(slice[11], 5);
1358
1359        // Test MutMatrixView to slice conversion
1360        let data2 = make_test_matrix();
1361        let mut m2 = Matrix::try_from(data2.into(), 4, 3).unwrap();
1362        let mut_view = m2.as_mut_view();
1363        let slice2: &[usize] = mut_view.into();
1364        assert_eq!(slice2.len(), 12);
1365        assert_eq!(slice2[0], 0);
1366        assert_eq!(slice2[11], 5);
1367    }
1368
1369    #[test]
1370    fn test_matrix_construction_edge_cases() {
1371        // Test 1x1 matrix
1372        let m = Matrix::new(42, 1, 1);
1373        assert_eq!(m.nrows(), 1);
1374        assert_eq!(m.ncols(), 1);
1375        assert_eq!(m[(0, 0)], 42);
1376
1377        // Test single row matrix
1378        let m = Matrix::new(7, 1, 5);
1379        assert_eq!(m.nrows(), 1);
1380        assert_eq!(m.ncols(), 5);
1381        assert!(m.as_slice().iter().all(|&x| x == 7));
1382
1383        // Test single column matrix
1384        let m = Matrix::new(9, 5, 1);
1385        assert_eq!(m.nrows(), 5);
1386        assert_eq!(m.ncols(), 1);
1387        assert!(m.as_slice().iter().all(|&x| x == 9));
1388    }
1389
1390    #[test]
1391    fn test_matrix_view_edge_cases_with_data() {
1392        // Test matrix with actual data for edge cases
1393        let data = vec![10, 20];
1394
1395        // 2x1 matrix
1396        let m = MatrixView::try_from(data.as_slice(), 2, 1).unwrap();
1397        assert_eq!(m.nrows(), 2);
1398        assert_eq!(m.ncols(), 1);
1399        assert_eq!(m[(0, 0)], 10);
1400        assert_eq!(m[(1, 0)], 20);
1401        assert_eq!(m.row(0), &[10]);
1402        assert_eq!(m.row(1), &[20]);
1403
1404        // 1x2 matrix
1405        let m = MatrixView::try_from(data.as_slice(), 1, 2).unwrap();
1406        assert_eq!(m.nrows(), 1);
1407        assert_eq!(m.ncols(), 2);
1408        assert_eq!(m[(0, 0)], 10);
1409        assert_eq!(m[(0, 1)], 20);
1410        assert_eq!(m.row(0), &[10, 20]);
1411    }
1412
1413    #[test]
1414    fn test_subview() {
1415        let data = make_test_matrix();
1416        let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1417
1418        // Create a subview of the first two rows
1419        {
1420            let subview = m.subview(0..4).unwrap();
1421            assert_eq!(subview.nrows(), 4);
1422            assert_eq!(subview.ncols(), 3);
1423
1424            assert_eq!(subview.row(0), &[0, 1, 2]);
1425            assert_eq!(subview.row(1), &[1, 2, 3]);
1426            assert_eq!(subview.row(2), &[2, 3, 4]);
1427            assert_eq!(subview.row(3), &[3, 4, 5]);
1428            assert!(subview.get_row(4).is_none());
1429        }
1430
1431        // Sub view over a subset that touches the end.
1432        {
1433            let subview = m.subview(1..4).unwrap();
1434            assert_eq!(subview.nrows(), 3);
1435            assert_eq!(subview.ncols(), 3);
1436
1437            assert_eq!(subview.row(0), &[1, 2, 3]);
1438            assert_eq!(subview.row(1), &[2, 3, 4]);
1439            assert_eq!(subview.row(2), &[3, 4, 5]);
1440            assert!(subview.get_row(3).is_none());
1441        }
1442
1443        // Sub view over a subset that is in the middle
1444        {
1445            let subview = m.subview(1..3).unwrap();
1446            assert_eq!(subview.nrows(), 2);
1447            assert_eq!(subview.ncols(), 3);
1448
1449            assert_eq!(subview.row(0), &[1, 2, 3]);
1450            assert_eq!(subview.row(1), &[2, 3, 4]);
1451            assert!(subview.get_row(2).is_none());
1452        }
1453
1454        // Empty sub-view.
1455        {
1456            let subview = m.subview(2..2).unwrap();
1457            assert_eq!(subview.nrows(), 0);
1458            assert_eq!(subview.ncols(), 3);
1459        }
1460
1461        // Empty subview in bounds
1462        {
1463            let subview = m.subview(0..0).unwrap();
1464            assert_eq!(subview.nrows(), 0);
1465            assert_eq!(subview.ncols(), 3);
1466
1467            let subview = m.subview(4..4).unwrap();
1468            assert_eq!(subview.nrows(), 0);
1469            assert_eq!(subview.ncols(), 3);
1470        }
1471
1472        // Empty out-of-bounds subview
1473        assert!(m.subview(5..5).is_none());
1474
1475        // View too-large
1476        assert!(m.subview(0..6).is_none());
1477        assert!(m.subview(2..10).is_none());
1478
1479        // View disjoint.
1480        assert!(m.subview(10..100).is_none());
1481
1482        // Negative bounds
1483        #[expect(
1484            clippy::reversed_empty_ranges,
1485            reason = "we want to make sure it doesn't work"
1486        )]
1487        let empty = 3..2;
1488        assert!(m.subview(empty).is_none());
1489
1490        #[expect(
1491            clippy::reversed_empty_ranges,
1492            reason = "we want to make sure it doesn't work"
1493        )]
1494        let empty = 3..1;
1495        assert!(m.subview(empty).is_none());
1496
1497        // Bounds that overflow.
1498        assert!(m.subview(usize::MAX - 1..usize::MAX).is_none());
1499        assert!(m.subview(0..usize::MAX).is_none());
1500    }
1501
1502    #[test]
1503    #[cfg(all(not(miri), feature = "rayon"))]
1504    fn test_parallel_methods_edge_cases() {
1505        let data = make_test_matrix();
1506        let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1507
1508        // Test par_window_iter with batchsize larger than matrix
1509        let windows: Vec<_> = m.par_window_iter(10).collect();
1510        assert_eq!(windows.len(), 1);
1511        assert_eq!(windows[0].nrows(), 4);
1512        assert_eq!(windows[0].ncols(), 3);
1513
1514        // Test par_row_iter
1515        let rows: Vec<_> = m.par_row_iter().collect();
1516        assert_eq!(rows.len(), 4);
1517        assert_eq!(rows[0], &[0, 1, 2]);
1518        assert_eq!(rows[3], &[3, 4, 5]);
1519
1520        // Test par_window_iter_mut and par_row_iter_mut
1521        let mut m2 = Matrix::new(0, 4, 3);
1522
1523        // Use par_row_iter_mut to set values
1524        m2.par_row_iter_mut().enumerate().for_each(|(i, row)| {
1525            for (j, elem) in row.iter_mut().enumerate() {
1526                *elem = i + j;
1527            }
1528        });
1529        test_basic_indexing(&m2);
1530
1531        // Test par_window_iter_mut with larger batchsize
1532        let mut m3 = Matrix::new(0, 4, 3);
1533        m3.par_window_iter_mut(10)
1534            .enumerate()
1535            .for_each(|(_, mut window)| {
1536                window.row_iter_mut().enumerate().for_each(|(i, row)| {
1537                    for (j, elem) in row.iter_mut().enumerate() {
1538                        *elem = i + j;
1539                    }
1540                });
1541            });
1542        test_basic_indexing(&m3);
1543    }
1544
1545    #[test]
1546    fn test_matrix_pointers() {
1547        let mut m = Matrix::new(42, 3, 4);
1548
1549        // Test as_ptr and as_mut_ptr return the same address
1550        let const_ptr = m.as_ptr();
1551        let mut_ptr = m.as_mut_ptr();
1552        assert_eq!(const_ptr, mut_ptr as *const _);
1553
1554        // Test that view pointers match original
1555        let view = m.as_view();
1556        assert_eq!(view.as_ptr(), const_ptr);
1557
1558        let mut mut_view = m.as_mut_view();
1559        assert_eq!(mut_view.as_ptr(), const_ptr);
1560        assert_eq!(mut_view.as_mut_ptr(), mut_ptr);
1561    }
1562
1563    #[test]
1564    fn test_matrix_iteration_empty_cases() {
1565        // Test construction of empty matrices (we don't iterate over 0x0 matrices
1566        // since chunks_exact requires non-zero chunk size)
1567        let empty_data: Vec<i32> = vec![];
1568
1569        // Matrix with 0 rows but non-zero cols can be constructed
1570        let _empty_matrix = MatrixView::try_from(empty_data.as_slice(), 0, 5).unwrap();
1571
1572        // Test with actual single row to verify iterator works normally
1573        let data = vec![1, 2, 3];
1574        let single_row = MatrixView::try_from(data.as_slice(), 1, 3).unwrap();
1575        let rows: Vec<_> = single_row.row_iter().collect();
1576        assert_eq!(rows.len(), 1);
1577        assert_eq!(rows[0], &[1, 2, 3]);
1578
1579        // Test iteration over matrix with multiple rows but single column
1580        let data = vec![1, 2, 3];
1581        let single_col = MatrixView::try_from(data.as_slice(), 3, 1).unwrap();
1582        let rows: Vec<_> = single_col.row_iter().collect();
1583        assert_eq!(rows.len(), 3);
1584        assert_eq!(rows[0], &[1]);
1585        assert_eq!(rows[1], &[2]);
1586        assert_eq!(rows[2], &[3]);
1587    }
1588
1589    #[test]
1590    fn test_matrix_init_generator_various_types() {
1591        // Test with different types and generators
1592        use std::sync::atomic::{AtomicUsize, Ordering};
1593
1594        let counter = AtomicUsize::new(0);
1595        let m = Matrix::new(Init(|| counter.fetch_add(1, Ordering::SeqCst)), 2, 3);
1596
1597        // Should be filled in memory order
1598        assert_eq!(m[(0, 0)], 0);
1599        assert_eq!(m[(0, 1)], 1);
1600        assert_eq!(m[(0, 2)], 2);
1601        assert_eq!(m[(1, 0)], 3);
1602        assert_eq!(m[(1, 1)], 4);
1603        assert_eq!(m[(1, 2)], 5);
1604    }
1605
1606    #[test]
1607    fn test_debug_error_formatting() {
1608        // Test Debug implementation for TryFromError
1609        let data = vec![1, 2, 3];
1610        let err = Matrix::try_from(data.into(), 2, 3).unwrap_err();
1611
1612        let debug_str = format!("{:?}", err);
1613        assert!(debug_str.contains("TryFromError"));
1614        assert!(debug_str.contains("data_len: 3"));
1615        assert!(debug_str.contains("nrows: 2"));
1616        assert!(debug_str.contains("ncols: 3"));
1617
1618        // Ensure Debug doesn't require T: Debug by using a non-Debug type
1619        #[derive(Clone, Debug)]
1620        struct NonDebug(#[allow(dead_code)] i32);
1621
1622        let non_debug_data: Box<[NonDebug]> = vec![NonDebug(1), NonDebug(2)].into();
1623        let non_debug_err = Matrix::try_from(non_debug_data, 1, 3).unwrap_err();
1624        let debug_str = format!("{:?}", non_debug_err);
1625        assert!(debug_str.contains("TryFromError"));
1626    }
1627
1628    // Comprehensive tests for rayon-specific functionality
1629
1630    #[test]
1631    #[cfg(feature = "rayon")]
1632    fn test_par_window_iter_comprehensive() {
1633        use rayon::prelude::*;
1634
1635        // Create a larger test matrix for more comprehensive testing
1636        let data: Vec<usize> = (0..24).collect(); // 6x4 matrix
1637        let m = MatrixView::try_from(data.as_slice(), 6, 4).unwrap();
1638
1639        // Test various batch sizes
1640        for batchsize in 1..=8 {
1641            let context = lazy_format!("batchsize = {}", batchsize);
1642            let windows: Vec<_> = m.par_window_iter(batchsize).collect();
1643
1644            // Calculate expected number of windows
1645            let expected_windows = (m.nrows()).div_ceil(batchsize);
1646            assert_eq!(windows.len(), expected_windows, "{}", context);
1647
1648            // Verify each window's properties
1649            let mut total_rows_seen = 0;
1650            for (window_idx, window) in windows.iter().enumerate() {
1651                let expected_rows = if window_idx == windows.len() - 1 {
1652                    // Last window may have fewer rows
1653                    m.nrows() - (windows.len() - 1) * batchsize
1654                } else {
1655                    batchsize
1656                };
1657
1658                assert_eq!(
1659                    window.nrows(),
1660                    expected_rows,
1661                    "window {} - {}",
1662                    window_idx,
1663                    context
1664                );
1665                assert_eq!(
1666                    window.ncols(),
1667                    m.ncols(),
1668                    "window {} - {}",
1669                    window_idx,
1670                    context
1671                );
1672
1673                // Verify data integrity
1674                for (row_idx, row) in window.row_iter().enumerate() {
1675                    let global_row = window_idx * batchsize + row_idx;
1676                    let expected: Vec<usize> =
1677                        (0..m.ncols()).map(|j| global_row * m.ncols() + j).collect();
1678                    assert_eq!(
1679                        row,
1680                        expected.as_slice(),
1681                        "window {}, row {} - {}",
1682                        window_idx,
1683                        row_idx,
1684                        context
1685                    );
1686                }
1687
1688                total_rows_seen += window.nrows();
1689            }
1690
1691            assert_eq!(total_rows_seen, m.nrows(), "{}", context);
1692        }
1693
1694        // Test with batchsize equal to matrix rows
1695        let windows: Vec<_> = m.par_window_iter(m.nrows()).collect();
1696        assert_eq!(windows.len(), 1);
1697        assert_eq!(windows[0].nrows(), m.nrows());
1698        assert_eq!(windows[0].ncols(), m.ncols());
1699
1700        // Test with batchsize larger than matrix rows
1701        let windows: Vec<_> = m.par_window_iter(m.nrows() * 2).collect();
1702        assert_eq!(windows.len(), 1);
1703        assert_eq!(windows[0].nrows(), m.nrows());
1704        assert_eq!(windows[0].ncols(), m.ncols());
1705    }
1706
1707    #[test]
1708    #[cfg(feature = "rayon")]
1709    fn test_par_window_iter_mut_comprehensive() {
1710        use rayon::prelude::*;
1711
1712        // Test various matrix sizes and batch sizes
1713        for nrows in [1, 2, 3, 5, 8, 10] {
1714            for ncols in [1, 3, 4] {
1715                for batchsize in [1, 2, 3, 7] {
1716                    let context = lazy_format!("{}x{}, batchsize={}", nrows, ncols, batchsize);
1717
1718                    let mut m = Matrix::new(0usize, nrows, ncols);
1719
1720                    // Use par_window_iter_mut to fill matrix
1721                    m.par_window_iter_mut(batchsize).enumerate().for_each(
1722                        |(window_idx, mut window)| {
1723                            let base_row = window_idx * batchsize;
1724                            window
1725                                .row_iter_mut()
1726                                .enumerate()
1727                                .for_each(|(row_offset, row)| {
1728                                    let global_row = base_row + row_offset;
1729                                    for (col, elem) in row.iter_mut().enumerate() {
1730                                        *elem = global_row * ncols + col;
1731                                    }
1732                                });
1733                        },
1734                    );
1735
1736                    // Verify the matrix was filled correctly
1737                    for row in 0..nrows {
1738                        for col in 0..ncols {
1739                            let expected = row * ncols + col;
1740                            assert_eq!(
1741                                m[(row, col)],
1742                                expected,
1743                                "pos ({}, {}) - {}",
1744                                row,
1745                                col,
1746                                context
1747                            );
1748                        }
1749                    }
1750                }
1751            }
1752        }
1753    }
1754
1755    #[test]
1756    #[cfg(feature = "rayon")]
1757    fn test_par_row_iter_comprehensive() {
1758        use rayon::prelude::*;
1759
1760        // Create test matrix with predictable pattern
1761        let nrows = 7;
1762        let ncols = 5;
1763        let data: Vec<i32> = (0..(nrows * ncols) as i32).collect();
1764        let m = MatrixView::try_from(data.as_slice(), nrows, ncols).unwrap();
1765
1766        // Test that par_row_iter preserves order and data
1767        let collected_rows: Vec<Vec<i32>> = m.par_row_iter().map(|row| row.to_vec()).collect();
1768
1769        assert_eq!(collected_rows.len(), nrows);
1770
1771        for (row_idx, row) in collected_rows.iter().enumerate() {
1772            assert_eq!(row.len(), ncols);
1773            let expected: Vec<i32> = ((row_idx * ncols)..((row_idx + 1) * ncols))
1774                .map(|x| x as i32)
1775                .collect();
1776            assert_eq!(row, &expected, "row {} mismatch", row_idx);
1777        }
1778
1779        // Test parallel enumeration
1780        let enumerated_rows: Vec<(usize, Vec<i32>)> = m
1781            .par_row_iter()
1782            .enumerate()
1783            .map(|(idx, row)| (idx, row.to_vec()))
1784            .collect();
1785
1786        // Sort by index to ensure we got all indices
1787        let mut sorted_rows = enumerated_rows;
1788        sorted_rows.sort_by_key(|(idx, _)| *idx);
1789
1790        assert_eq!(sorted_rows.len(), nrows);
1791        for (expected_idx, (actual_idx, row)) in sorted_rows.iter().enumerate() {
1792            assert_eq!(*actual_idx, expected_idx);
1793            assert_eq!(row.len(), ncols);
1794        }
1795
1796        // Test parallel reduction operations
1797        let sum: i32 = m.par_row_iter().map(|row| row.iter().sum::<i32>()).sum();
1798
1799        let expected_sum: i32 = data.iter().sum();
1800        assert_eq!(sum, expected_sum);
1801
1802        // Test parallel find operations
1803        let target_row = 3;
1804        let found_row = m
1805            .par_row_iter()
1806            .enumerate()
1807            .find_any(|(idx, _)| *idx == target_row)
1808            .map(|(_, row)| row.to_vec());
1809
1810        assert!(found_row.is_some());
1811        let expected_row: Vec<i32> = ((target_row * ncols)..((target_row + 1) * ncols))
1812            .map(|x| x as i32)
1813            .collect();
1814        assert_eq!(found_row.unwrap(), expected_row);
1815    }
1816
1817    #[test]
1818    #[cfg(feature = "rayon")]
1819    fn test_par_row_iter_mut_comprehensive() {
1820        use rayon::prelude::*;
1821        use std::sync::atomic::{AtomicUsize, Ordering};
1822
1823        let nrows = 6;
1824        let ncols = 4;
1825        let mut m = Matrix::new(0u32, nrows, ncols);
1826
1827        // Test parallel modification
1828        m.par_row_iter_mut().enumerate().for_each(|(row_idx, row)| {
1829            for (col_idx, elem) in row.iter_mut().enumerate() {
1830                *elem = (row_idx * ncols + col_idx) as u32;
1831            }
1832        });
1833
1834        // Verify modifications were applied correctly
1835        for row in 0..nrows {
1836            for col in 0..ncols {
1837                let expected = (row * ncols + col) as u32;
1838                assert_eq!(m[(row, col)], expected, "pos ({}, {})", row, col);
1839            }
1840        }
1841
1842        // Test parallel accumulation with atomic counter
1843        let counter = AtomicUsize::new(0);
1844        m.par_row_iter_mut().for_each(|row| {
1845            counter.fetch_add(1, Ordering::Relaxed);
1846            // Multiply each element by 2
1847            for elem in row {
1848                *elem *= 2;
1849            }
1850        });
1851
1852        assert_eq!(counter.load(Ordering::Relaxed), nrows);
1853
1854        // Verify all elements were doubled
1855        for row in 0..nrows {
1856            for col in 0..ncols {
1857                let expected = ((row * ncols + col) * 2) as u32;
1858                assert_eq!(m[(row, col)], expected, "doubled pos ({}, {})", row, col);
1859            }
1860        }
1861    }
1862
1863    #[test]
1864    #[cfg(feature = "rayon")]
1865    fn test_parallel_iterators_with_single_dimensions() {
1866        use rayon::prelude::*;
1867
1868        // Test single row matrix
1869        let data = vec![1, 2, 3, 4, 5];
1870        let single_row = MatrixView::try_from(data.as_slice(), 1, 5).unwrap();
1871
1872        let windows: Vec<_> = single_row.par_window_iter(1).collect();
1873        assert_eq!(windows.len(), 1);
1874        assert_eq!(windows[0].nrows(), 1);
1875        assert_eq!(windows[0].ncols(), 5);
1876
1877        let rows: Vec<_> = single_row.par_row_iter().collect();
1878        assert_eq!(rows.len(), 1);
1879        assert_eq!(rows[0], &[1, 2, 3, 4, 5]);
1880
1881        // Test single column matrix
1882        let data = vec![1, 2, 3, 4, 5];
1883        let single_col = MatrixView::try_from(data.as_slice(), 5, 1).unwrap();
1884
1885        let windows: Vec<_> = single_col.par_window_iter(2).collect();
1886        assert_eq!(windows.len(), 3); // ceil(5/2) = 3
1887        assert_eq!(windows[0].nrows(), 2);
1888        assert_eq!(windows[1].nrows(), 2);
1889        assert_eq!(windows[2].nrows(), 1); // Last window has remainder
1890
1891        let rows: Vec<_> = single_col.par_row_iter().collect();
1892        assert_eq!(rows.len(), 5);
1893        for (i, row) in rows.iter().enumerate() {
1894            assert_eq!(row, &[i + 1]);
1895        }
1896
1897        // Test 1x1 matrix
1898        let data = vec![42];
1899        let tiny = MatrixView::try_from(data.as_slice(), 1, 1).unwrap();
1900
1901        let windows: Vec<_> = tiny.par_window_iter(1).collect();
1902        assert_eq!(windows.len(), 1);
1903        assert_eq!(windows[0][(0, 0)], 42);
1904
1905        let rows: Vec<_> = tiny.par_row_iter().collect();
1906        assert_eq!(rows.len(), 1);
1907        assert_eq!(rows[0], &[42]);
1908    }
1909
1910    #[test]
1911    #[cfg(feature = "rayon")]
1912    fn test_parallel_window_properties() {
1913        use rayon::prelude::*;
1914
1915        // Test that windows maintain proper matrix properties
1916        let data: Vec<usize> = (0..30).collect();
1917        let m = MatrixView::try_from(data.as_slice(), 6, 5).unwrap();
1918
1919        // Test window indexing works correctly
1920        m.par_window_iter(2)
1921            .enumerate()
1922            .for_each(|(window_idx, window)| {
1923                for row_idx in 0..window.nrows() {
1924                    for col_idx in 0..window.ncols() {
1925                        let global_row = window_idx * 2 + row_idx;
1926                        let expected = global_row * 5 + col_idx;
1927                        assert_eq!(
1928                            window[(row_idx, col_idx)],
1929                            expected,
1930                            "window {}, pos ({}, {})",
1931                            window_idx,
1932                            row_idx,
1933                            col_idx
1934                        );
1935                    }
1936                }
1937            });
1938
1939        // Test window as_slice consistency
1940        m.par_window_iter(3)
1941            .enumerate()
1942            .for_each(|(window_idx, window)| {
1943                let slice = window.as_slice();
1944                assert_eq!(slice.len(), window.nrows() * window.ncols());
1945
1946                for (slice_idx, &value) in slice.iter().enumerate() {
1947                    let row = slice_idx / window.ncols();
1948                    let col = slice_idx % window.ncols();
1949                    assert_eq!(
1950                        value,
1951                        window[(row, col)],
1952                        "window {}, slice_idx {}",
1953                        window_idx,
1954                        slice_idx
1955                    );
1956                }
1957            });
1958
1959        // Test window row iteration
1960        m.par_window_iter(2).for_each(|window| {
1961            let rows_via_iter: Vec<_> = window.row_iter().collect();
1962            assert_eq!(rows_via_iter.len(), window.nrows());
1963
1964            for (row_idx, row) in rows_via_iter.iter().enumerate() {
1965                assert_eq!(row.len(), window.ncols());
1966                for (col_idx, &value) in row.iter().enumerate() {
1967                    assert_eq!(value, window[(row_idx, col_idx)]);
1968                }
1969            }
1970        });
1971    }
1972
1973    #[test]
1974    #[cfg(feature = "rayon")]
1975    fn test_parallel_performance_characteristics() {
1976        use rayon::prelude::*;
1977        use std::sync::atomic::{AtomicUsize, Ordering};
1978
1979        // Create a larger matrix to test parallelism benefits
1980        let nrows = 100;
1981        let ncols = 10;
1982        let mut m = Matrix::new(0usize, nrows, ncols);
1983
1984        // Test that parallel operations can be chained
1985        let work_counter = AtomicUsize::new(0);
1986
1987        m.par_window_iter_mut(10)
1988            .enumerate()
1989            .for_each(|(window_idx, mut window)| {
1990                work_counter.fetch_add(1, Ordering::Relaxed);
1991
1992                // Nested parallel operation within window
1993                window
1994                    .row_iter_mut()
1995                    .enumerate()
1996                    .for_each(|(row_offset, row)| {
1997                        let global_row = window_idx * 10 + row_offset;
1998                        for (col, elem) in row.iter_mut().enumerate() {
1999                            *elem = global_row * ncols + col;
2000                        }
2001                    });
2002            });
2003
2004        // Should have processed 10 windows (100 rows / 10 batch size)
2005        assert_eq!(work_counter.load(Ordering::Relaxed), 10);
2006
2007        // Verify correctness
2008        for row in 0..nrows {
2009            for col in 0..ncols {
2010                assert_eq!(m[(row, col)], row * ncols + col);
2011            }
2012        }
2013
2014        // Test parallel reduction across windows
2015        let total_sum: usize = m
2016            .par_window_iter(15)
2017            .map(|window| {
2018                window
2019                    .row_iter()
2020                    .map(|row| row.iter().sum::<usize>())
2021                    .sum::<usize>()
2022            })
2023            .sum();
2024
2025        let expected_sum: usize = (0..(nrows * ncols)).sum();
2026        assert_eq!(total_sum, expected_sum);
2027    }
2028
2029    #[test]
2030    #[cfg(feature = "rayon")]
2031    fn test_rayon_trait_bounds_validation() {
2032        use rayon::prelude::*;
2033
2034        // Test that the Sync/Send bounds work correctly
2035        let data: Vec<u64> = (0..20).collect();
2036        let m = MatrixView::try_from(data.as_slice(), 4, 5).unwrap();
2037
2038        // This should compile because u64 is Sync
2039        let _: Vec<_> = m.par_window_iter(2).collect();
2040        let _: Vec<_> = m.par_row_iter().collect();
2041
2042        // Test with mutable matrix
2043        let mut m = Matrix::new(0u64, 4, 5);
2044
2045        // This should compile because u64 is Send
2046        m.par_window_iter_mut(2).for_each(|mut window| {
2047            window.row_iter_mut().for_each(|row| {
2048                for elem in row {
2049                    *elem = 42;
2050                }
2051            });
2052        });
2053
2054        m.par_row_iter_mut().for_each(|row| {
2055            for elem in row {
2056                *elem += 1;
2057            }
2058        });
2059
2060        // Verify all elements are 43
2061        assert!(m.as_slice().iter().all(|&x| x == 43));
2062    }
2063}