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