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 pointer to the base of the matrix.
510    pub fn as_ptr(&self) -> *const T::Elem {
511        self.as_slice().as_ptr()
512    }
513
514    /// Return a pointer to the base of the matrix.
515    pub fn as_mut_ptr(&mut self) -> *mut T::Elem
516    where
517        T: MutDenseData,
518    {
519        self.as_mut_slice().as_mut_ptr()
520    }
521
522    /// Returns a reference to an element without boundschecking.
523    ///
524    /// # Safety
525    ///
526    /// The following conditions must hold to avoid undefined behavior:
527    /// * `row < self.nrows()`.
528    /// * `col < self.ncols()`.
529    pub unsafe fn get_unchecked(&self, row: usize, col: usize) -> &T::Elem {
530        debug_assert!(row < self.nrows);
531        debug_assert!(col < self.ncols);
532        self.as_slice().get_unchecked(row * self.ncols + col)
533    }
534
535    /// Returns a mutable reference to an element without boundschecking.
536    ///
537    /// # Safety
538    ///
539    /// The following conditions must hold to avoid undefined behavior:
540    /// * `row < self.nrows()`.
541    /// * `col < self.ncols()`.
542    pub unsafe fn get_unchecked_mut(&mut self, row: usize, col: usize) -> &mut T::Elem
543    where
544        T: MutDenseData,
545    {
546        let ncols = self.ncols;
547        debug_assert!(row < self.nrows);
548        debug_assert!(col < self.ncols);
549        self.as_mut_slice().get_unchecked_mut(row * ncols + col)
550    }
551
552    pub fn to_owned(&self) -> Matrix<T::Elem>
553    where
554        T::Elem: Clone,
555    {
556        Matrix {
557            data: self.data.as_slice().into(),
558            nrows: self.nrows,
559            ncols: self.ncols,
560        }
561    }
562}
563
564/// Represents an owning, 2-dimensional view of a contiguous block of memory,
565/// interpreted as a matrix in row-major order.
566pub type Matrix<T> = MatrixBase<Box<[T]>>;
567
568/// Represents a non-owning, 2-dimensional view of a contiguous block of memory,
569/// interpreted as a matrix in row-major order.
570///
571/// This type is useful for functions that need to read matrix data without taking ownership.
572/// By accepting a `MatrixView`, such functions can operate on both owned matrices (by converting them
573/// to a `MatrixView`) and existing non-owning views.
574pub type MatrixView<'a, T> = MatrixBase<&'a [T]>;
575
576/// Represents a mutable non-owning, 2-dimensional view of a contiguous block of memory,
577/// interpreted as a matrix in row-major order.
578///
579/// This type is useful for functions that need to modify matrix data without taking ownership.
580/// By accepting a `MutMatrixView`, such functions can operate on both owned matrices (by converting them
581/// to a `MutMatrixView`) and existing non-owning mutable views.
582pub type MutMatrixView<'a, T> = MatrixBase<&'a mut [T]>;
583
584/// Allow matrix views to be converted directly to slices.
585impl<'a, T> From<MatrixView<'a, T>> for &'a [T] {
586    fn from(view: MatrixView<'a, T>) -> Self {
587        view.data
588    }
589}
590
591/// Allow mutable matrix views to be converted directly to slices.
592impl<'a, T> From<MutMatrixView<'a, T>> for &'a [T] {
593    fn from(view: MutMatrixView<'a, T>) -> Self {
594        view.data
595    }
596}
597
598/// Return a reference to the item at entry `(row, col)` in the matrix.
599///
600/// # Panics
601///
602/// Panics if `row >= self.nrows()` or `col >= self.ncols()`.
603impl<T> Index<(usize, usize)> for MatrixBase<T>
604where
605    T: DenseData,
606{
607    type Output = T::Elem;
608
609    fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
610        assert!(
611            row < self.nrows(),
612            "row {row} is out of bounds (max: {})",
613            self.nrows()
614        );
615        assert!(
616            col < self.ncols(),
617            "col {col} is out of bounds (max: {})",
618            self.ncols()
619        );
620
621        // SAFETY: We have checked that `row` and `col` are in-bounds.
622        unsafe { self.get_unchecked(row, col) }
623    }
624}
625
626/// Return a mutable reference to the item at entry `(row, col)` in the matrix.
627///
628/// # Panics
629///
630/// Panics if `row >= self.nrows()` or `col >= self.ncols()`.
631impl<T> IndexMut<(usize, usize)> for MatrixBase<T>
632where
633    T: MutDenseData,
634{
635    fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
636        assert!(
637            row < self.nrows(),
638            "row {row} is out of bounds (max: {})",
639            self.nrows()
640        );
641        assert!(
642            col < self.ncols(),
643            "col {col} is out of bounds (max: {})",
644            self.ncols()
645        );
646
647        // SAFETY: We have checked that `row` and `col` are in-bounds.
648        unsafe { self.get_unchecked_mut(row, col) }
649    }
650}
651
652///////////
653// Tests //
654///////////
655
656#[cfg(test)]
657mod tests {
658    use super::*;
659    use crate::lazy_format;
660
661    /// This function is only callable with copyable types.
662    ///
663    /// This lets us test for types we expect to be `Copy`.
664    fn is_copyable<T: Copy>(_x: T) -> bool {
665        true
666    }
667
668    /// Test the that provided representation yields a slice with the expected base pointer
669    /// and length.
670    fn test_dense_data_repr<T, Repr>(
671        ptr: *const T,
672        len: usize,
673        repr: Repr,
674        context: &dyn std::fmt::Display,
675    ) where
676        T: Copy,
677        Repr: DenseData<Elem = T>,
678    {
679        let retrieved = repr.as_slice();
680        assert_eq!(retrieved.len(), len, "{}", context);
681        assert_eq!(retrieved.as_ptr(), ptr, "{}", context);
682    }
683
684    /// Set the underlying data for the provided representation to the following:
685    ///
686    /// [base, base + increment, base + increment + increment, ...]
687    fn set_mut_dense_data_repr<T, Repr>(repr: &mut Repr, base: T, increment: T)
688    where
689        T: Copy + std::ops::Add<Output = T>,
690        Repr: DenseData<Elem = T> + MutDenseData,
691    {
692        let slice = repr.as_mut_slice();
693        for i in 0..slice.len() {
694            if i == 0 {
695                slice[i] = base;
696            } else {
697                slice[i] = slice[i - 1] + increment;
698            }
699        }
700    }
701
702    #[test]
703    fn slice_implements_dense_data_repr() {
704        for len in 0..10 {
705            let context = lazy_format!("len = {}", len);
706            let data: Vec<f32> = vec![0.0; len];
707            let slice = data.as_slice();
708            test_dense_data_repr(slice.as_ptr(), slice.len(), slice, &context);
709        }
710    }
711
712    #[test]
713    fn mut_slice_mplements_dense_data_repr() {
714        for len in 0..10 {
715            let context = lazy_format!("len = {}", len);
716            let mut data: Vec<f32> = vec![0.0; len];
717            let slice = data.as_mut_slice();
718
719            let ptr = slice.as_ptr();
720            let len = slice.len();
721            test_dense_data_repr(ptr, len, slice, &context);
722        }
723    }
724
725    #[test]
726    fn mut_slice_implements_mut_dense_data_repr() {
727        for len in 0..10 {
728            let context = lazy_format!("len = {}", len);
729            let mut data: Vec<f32> = vec![0.0; len];
730            let mut slice = data.as_mut_slice();
731
732            let base = 2.0;
733            let increment = 1.0;
734            set_mut_dense_data_repr(&mut slice, base, increment);
735
736            for (i, &v) in slice.iter().enumerate() {
737                let context = lazy_format!("entry {}, {}", i, context);
738                assert_eq!(v, base + increment * (i as f32), "{}", context);
739            }
740        }
741    }
742
743    /////////////////
744    // Matrix View //
745    /////////////////
746
747    #[test]
748    fn try_from_error_misc() {
749        let x = TryFromError::<&[f32]> {
750            data: &[],
751            nrows: 1,
752            ncols: 2,
753        };
754
755        let debug = format!("{:?}", x);
756        println!("debug = {}", debug);
757        assert!(debug.contains("TryFromError"));
758        assert!(debug.contains("data_len: 0"));
759        assert!(debug.contains("nrows: 1"));
760        assert!(debug.contains("ncols: 2"));
761    }
762
763    fn make_test_matrix() -> Vec<usize> {
764        // Construct a matrix with 4 rows of length 3.
765        // The expected layout is as follows:
766        //
767        // 0, 1, 2,
768        // 1, 2, 3,
769        // 2, 3, 4,
770        // 3, 4, 5
771        //
772        vec![0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]
773    }
774
775    #[cfg(feature = "rayon")]
776    fn test_basic_indexing_parallel(m: MatrixView<'_, usize>) {
777        // Par window iters.
778        let batchsize = 2;
779        m.par_window_iter(batchsize)
780            .enumerate()
781            .for_each(|(i, submatrix)| {
782                assert_eq!(submatrix.nrows(), batchsize);
783                assert_eq!(submatrix.ncols(), m.ncols());
784
785                // Make sure we are in the correct window of the original matrix.
786                let base = i * batchsize;
787                assert_eq!(submatrix[(0, 0)], base);
788                assert_eq!(submatrix[(0, 1)], base + 1);
789                assert_eq!(submatrix[(0, 2)], base + 2);
790
791                assert_eq!(submatrix[(1, 0)], base + 1);
792                assert_eq!(submatrix[(1, 1)], base + 2);
793                assert_eq!(submatrix[(1, 2)], base + 3);
794            });
795
796        // Try again, but with a batch size of 3 to ensure that we correctly handle cases
797        // where the last block is under-sized.
798        let batchsize = 3;
799        m.par_window_iter(batchsize)
800            .enumerate()
801            .for_each(|(i, submatrix)| {
802                if i == 0 {
803                    assert_eq!(submatrix.nrows(), batchsize);
804                    assert_eq!(submatrix.ncols(), m.ncols());
805
806                    // Check indexing
807                    assert_eq!(submatrix[(0, 0)], 0);
808                    assert_eq!(submatrix[(0, 1)], 1);
809                    assert_eq!(submatrix[(0, 2)], 2);
810
811                    assert_eq!(submatrix[(1, 0)], 1);
812                    assert_eq!(submatrix[(1, 1)], 2);
813                    assert_eq!(submatrix[(1, 2)], 3);
814
815                    assert_eq!(submatrix[(2, 0)], 2);
816                    assert_eq!(submatrix[(2, 1)], 3);
817                    assert_eq!(submatrix[(2, 2)], 4);
818                } else {
819                    assert_eq!(submatrix.nrows(), 1);
820                    assert_eq!(submatrix.ncols(), m.ncols());
821
822                    // Check indexing
823                    assert_eq!(submatrix[(0, 0)], 3);
824                    assert_eq!(submatrix[(0, 1)], 4);
825                    assert_eq!(submatrix[(0, 2)], 5);
826                }
827            });
828
829        // par-row-iter
830        let seen_rows: Box<[usize]> = m
831            .par_row_iter()
832            .enumerate()
833            .map(|(i, row)| {
834                let expected: Box<[usize]> = (0..m.ncols()).map(|j| j + i).collect();
835                assert_eq!(row, &*expected);
836                i
837            })
838            .collect();
839
840        let expected: Box<[usize]> = (0..m.nrows()).collect();
841        assert_eq!(seen_rows, expected);
842    }
843
844    fn test_basic_indexing<T>(m: &MatrixBase<T>)
845    where
846        T: DenseData<Elem = usize> + Sync,
847    {
848        assert_eq!(m.nrows(), 4);
849        assert_eq!(m.ncols(), 3);
850
851        // Basic indexing
852        assert_eq!(m[(0, 0)], 0);
853        assert_eq!(m[(0, 1)], 1);
854        assert_eq!(m[(0, 2)], 2);
855
856        assert_eq!(m[(1, 0)], 1);
857        assert_eq!(m[(1, 1)], 2);
858        assert_eq!(m[(1, 2)], 3);
859
860        assert_eq!(m[(2, 0)], 2);
861        assert_eq!(m[(2, 1)], 3);
862        assert_eq!(m[(2, 2)], 4);
863
864        assert_eq!(m[(3, 0)], 3);
865        assert_eq!(m[(3, 1)], 4);
866        assert_eq!(m[(3, 2)], 5);
867
868        // Row indexing.
869        assert_eq!(m.row(0), &[0, 1, 2]);
870        assert_eq!(m.row(1), &[1, 2, 3]);
871        assert_eq!(m.row(2), &[2, 3, 4]);
872        assert_eq!(m.row(3), &[3, 4, 5]);
873
874        let rows: Vec<Vec<usize>> = m.row_iter().map(|x| x.to_vec()).collect();
875        assert_eq!(m.row(0), &rows[0]);
876        assert_eq!(m.row(1), &rows[1]);
877        assert_eq!(m.row(2), &rows[2]);
878        assert_eq!(m.row(3), &rows[3]);
879
880        // Window Iters.
881        let batchsize = 2;
882        m.window_iter(batchsize)
883            .enumerate()
884            .for_each(|(i, submatrix)| {
885                assert_eq!(submatrix.nrows(), batchsize);
886                assert_eq!(submatrix.ncols(), m.ncols());
887
888                // Make sure we are in the correct window of the original matrix.
889                let base = i * batchsize;
890                assert_eq!(submatrix[(0, 0)], base);
891                assert_eq!(submatrix[(0, 1)], base + 1);
892                assert_eq!(submatrix[(0, 2)], base + 2);
893
894                assert_eq!(submatrix[(1, 0)], base + 1);
895                assert_eq!(submatrix[(1, 1)], base + 2);
896                assert_eq!(submatrix[(1, 2)], base + 3);
897            });
898
899        // Try again, but with a batch size of 3 to ensure that we correctly handle cases
900        // where the last block is under-sized.
901        let batchsize = 3;
902        m.window_iter(batchsize)
903            .enumerate()
904            .for_each(|(i, submatrix)| {
905                if i == 0 {
906                    assert_eq!(submatrix.nrows(), batchsize);
907                    assert_eq!(submatrix.ncols(), m.ncols());
908
909                    // Check indexing
910                    assert_eq!(submatrix[(0, 0)], 0);
911                    assert_eq!(submatrix[(0, 1)], 1);
912                    assert_eq!(submatrix[(0, 2)], 2);
913
914                    assert_eq!(submatrix[(1, 0)], 1);
915                    assert_eq!(submatrix[(1, 1)], 2);
916                    assert_eq!(submatrix[(1, 2)], 3);
917
918                    assert_eq!(submatrix[(2, 0)], 2);
919                    assert_eq!(submatrix[(2, 1)], 3);
920                    assert_eq!(submatrix[(2, 2)], 4);
921                } else {
922                    assert_eq!(submatrix.nrows(), 1);
923                    assert_eq!(submatrix.ncols(), m.ncols());
924
925                    // Check indexing
926                    assert_eq!(submatrix[(0, 0)], 3);
927                    assert_eq!(submatrix[(0, 1)], 4);
928                    assert_eq!(submatrix[(0, 2)], 5);
929                }
930            });
931
932        #[cfg(all(not(miri), feature = "rayon"))]
933        test_basic_indexing_parallel(m.as_view());
934    }
935
936    #[test]
937    fn matrix_happy_path() {
938        let data = make_test_matrix();
939        let m = Matrix::try_from(data.into(), 4, 3).unwrap();
940        test_basic_indexing(&m);
941
942        // Get the base pointer of the matrix and make sure view-conversion preserves this
943        // value.
944        let ptr = m.as_ptr();
945        let view = m.as_view();
946        assert!(is_copyable(view));
947        assert_eq!(view.as_ptr(), ptr);
948        assert_eq!(view.nrows(), m.nrows());
949        assert_eq!(view.ncols(), m.ncols());
950        test_basic_indexing(&view);
951    }
952
953    #[test]
954    fn matrix_try_from_construction_error() {
955        let data = make_test_matrix();
956        let ptr = data.as_ptr();
957        let len = data.len();
958
959        let m = Matrix::try_from(data.into(), 5, 4);
960        assert!(m.is_err());
961        let err = m.unwrap_err();
962        assert_eq!(
963            err.to_string(),
964            "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
965        );
966
967        // Make sure that we can retrieve the original allocation from the interior.
968        let data = err.into_inner();
969        assert_eq!(data.as_ptr(), ptr);
970        assert_eq!(data.len(), len);
971
972        let m = MatrixView::try_from(&data, 5, 4);
973        assert!(m.is_err());
974        assert_eq!(
975            m.unwrap_err().to_string(),
976            "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
977        );
978    }
979
980    #[test]
981    fn matrix_mut_view() {
982        let mut m = Matrix::<usize>::new(0, 4, 3);
983        assert_eq!(m.nrows(), 4);
984        assert_eq!(m.ncols(), 3);
985        assert!(m.as_slice().iter().all(|&i| i == 0));
986        let ptr = m.as_ptr();
987        let mut_ptr = m.as_mut_ptr();
988        assert_eq!(ptr, mut_ptr);
989
990        let mut view = m.as_mut_view();
991        assert_eq!(view.nrows(), 4);
992        assert_eq!(view.ncols(), 3);
993        assert_eq!(view.as_ptr(), ptr);
994        assert_eq!(view.as_mut_ptr(), mut_ptr);
995
996        // Construct the test matrix manually.
997        for i in 0..view.nrows() {
998            for j in 0..view.ncols() {
999                view[(i, j)] = i + j;
1000            }
1001        }
1002
1003        // Drop the view and test the original matrix.
1004        test_basic_indexing(&m);
1005
1006        let inner = m.into_inner();
1007        assert_eq!(inner.as_ptr(), ptr);
1008        assert_eq!(inner.len(), 4 * 3);
1009    }
1010
1011    #[test]
1012    fn matrix_view_zero_sizes() {
1013        let data: Vec<usize> = vec![];
1014        // Zero rows, but non-zero columns.
1015        let m = MatrixView::try_from(data.as_slice(), 0, 10).unwrap();
1016        assert_eq!(m.nrows(), 0);
1017        assert_eq!(m.ncols(), 10);
1018
1019        // Non-zero rows, but zero columns.
1020        let m = MatrixView::try_from(data.as_slice(), 3, 0).unwrap();
1021        assert_eq!(m.nrows(), 3);
1022        assert_eq!(m.ncols(), 0);
1023        let empty: &[usize] = &[];
1024        assert_eq!(m.row(0), empty);
1025        assert_eq!(m.row(1), empty);
1026        assert_eq!(m.row(2), empty);
1027
1028        // Zero rows and columns.
1029        let m = MatrixView::try_from(data.as_slice(), 0, 0).unwrap();
1030        assert_eq!(m.nrows(), 0);
1031        assert_eq!(m.ncols(), 0);
1032    }
1033
1034    #[test]
1035    fn matrix_view_construction_elementwise() {
1036        let mut m = Matrix::<usize>::new(0, 4, 3);
1037
1038        // Construct the test matrix manually.
1039        for i in 0..m.nrows() {
1040            for j in 0..m.ncols() {
1041                m[(i, j)] = i + j;
1042            }
1043        }
1044        test_basic_indexing(&m);
1045    }
1046
1047    #[test]
1048    fn matrix_construction_by_row() {
1049        let mut m = Matrix::<usize>::new(0, 4, 3);
1050        assert!(m.as_slice().iter().all(|i| *i == 0));
1051
1052        let ncols = m.ncols();
1053        for i in 0..m.nrows() {
1054            let row = m.row_mut(i);
1055            assert_eq!(row.len(), ncols);
1056            row[0] = i;
1057            row[1] = i + 1;
1058            row[2] = i + 2;
1059        }
1060        test_basic_indexing(&m);
1061    }
1062
1063    #[test]
1064    fn matrix_construction_by_rowiter() {
1065        let mut m = Matrix::<usize>::new(0, 4, 3);
1066        assert!(m.as_slice().iter().all(|i| *i == 0));
1067
1068        let ncols = m.ncols();
1069        m.row_iter_mut().enumerate().for_each(|(i, row)| {
1070            assert_eq!(row.len(), ncols);
1071            row[0] = i;
1072            row[1] = i + 1;
1073            row[2] = i + 2;
1074        });
1075        test_basic_indexing(&m);
1076    }
1077
1078    #[cfg(all(not(miri), feature = "rayon"))]
1079    #[test]
1080    fn matrix_construction_by_par_windows() {
1081        let mut m = Matrix::<usize>::new(0, 4, 3);
1082        assert!(m.as_slice().iter().all(|i| *i == 0));
1083
1084        let ncols = m.ncols();
1085        for batchsize in 1..=4 {
1086            m.par_window_iter_mut(batchsize)
1087                .enumerate()
1088                .for_each(|(i, mut submatrix)| {
1089                    let base = i * batchsize;
1090                    submatrix.row_iter_mut().enumerate().for_each(|(j, row)| {
1091                        assert_eq!(row.len(), ncols);
1092                        row[0] = base + j;
1093                        row[1] = base + j + 1;
1094                        row[2] = base + j + 2;
1095                    });
1096                });
1097            test_basic_indexing(&m);
1098        }
1099    }
1100
1101    #[test]
1102    fn matrix_construction_happens_in_memory_order() {
1103        let mut i = 0;
1104        let ncols = 3;
1105        let initializer = Init(|| {
1106            let value = (i % ncols) + (i / ncols);
1107            i += 1;
1108            value
1109        });
1110
1111        let m = Matrix::new(initializer, 4, 3);
1112        test_basic_indexing(&m);
1113    }
1114
1115    // Panics
1116    #[test]
1117    #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1118    fn test_get_row_panics() {
1119        let m = Matrix::<usize>::new(0, 3, 7);
1120        m.row(3);
1121    }
1122
1123    #[test]
1124    #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1125    fn test_get_row_mut_panics() {
1126        let mut m = Matrix::<usize>::new(0, 3, 7);
1127        m.row_mut(3);
1128    }
1129
1130    #[test]
1131    #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1132    fn test_index_panics_row() {
1133        let m = Matrix::<usize>::new(0, 3, 7);
1134        let _ = m[(3, 2)];
1135    }
1136
1137    #[test]
1138    #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1139    fn test_index_panics_col() {
1140        let m = Matrix::<usize>::new(0, 3, 7);
1141        let _ = m[(2, 7)];
1142    }
1143
1144    #[test]
1145    #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1146    fn test_index_mut_panics_row() {
1147        let mut m = Matrix::<usize>::new(0, 3, 7);
1148        m[(3, 2)] = 1;
1149    }
1150
1151    #[test]
1152    #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1153    fn test_index_mut_panics_col() {
1154        let mut m = Matrix::<usize>::new(0, 3, 7);
1155        m[(2, 7)] = 1;
1156    }
1157
1158    #[test]
1159    #[cfg(feature = "rayon")]
1160    #[should_panic(expected = "par_window_iter batchsize cannot be zero")]
1161    fn test_par_window_iter_panics() {
1162        let m = Matrix::<usize>::new(0, 4, 4);
1163        let _ = m.par_window_iter(0);
1164    }
1165
1166    #[test]
1167    #[cfg(feature = "rayon")]
1168    #[should_panic(expected = "par_window_iter_mut batchsize cannot be zero")]
1169    fn test_par_window_iter_mut_panics() {
1170        let mut m = Matrix::<usize>::new(0, 4, 4);
1171        let _ = m.par_window_iter_mut(0);
1172    }
1173
1174    // Additional tests for better coverage
1175
1176    #[test]
1177    fn test_box_slice_dense_data_impls() {
1178        // Test Box<[T]> implementations
1179        let data: Box<[f32]> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0].into();
1180        let ptr = data.as_ptr();
1181        let len = data.len();
1182
1183        // Test DenseData impl for Box<[T]>
1184        test_dense_data_repr(ptr, len, data, &lazy_format!("Box<[T]> DenseData"));
1185
1186        // Test MutDenseData impl for Box<[T]>
1187        let mut data: Box<[f32]> = vec![0.0; 6].into();
1188        set_mut_dense_data_repr(&mut data, 1.0, 2.0);
1189        for (i, &v) in data.iter().enumerate() {
1190            assert_eq!(
1191                v,
1192                1.0 + 2.0 * (i as f32),
1193                "Box<[T]> MutDenseData at index {}",
1194                i
1195            );
1196        }
1197    }
1198
1199    #[test]
1200    fn test_try_from_error_light() {
1201        let data = vec![1, 2, 3];
1202        let err = MatrixView::try_from(data.as_slice(), 2, 3).unwrap_err();
1203
1204        // Test as_static method
1205        let static_err = err.as_static();
1206        assert_eq!(static_err.len, 3);
1207        assert_eq!(static_err.nrows, 2);
1208        assert_eq!(static_err.ncols, 3);
1209
1210        // Test Display for TryFromErrorLight
1211        let display_msg = format!("{}", static_err);
1212        assert!(display_msg.contains("tried to construct a matrix view with 2 rows and 3 columns"));
1213        assert!(display_msg.contains("slice of length 3"));
1214
1215        // Test into_inner method
1216        let recovered_data = err.into_inner();
1217        assert_eq!(recovered_data, data.as_slice());
1218    }
1219
1220    #[test]
1221    fn test_get_row_optional() {
1222        let data = make_test_matrix();
1223        let m = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1224
1225        // Test successful get_row
1226        assert_eq!(m.get_row(0), Some(&[0, 1, 2][..]));
1227        assert_eq!(m.get_row(1), Some(&[1, 2, 3][..]));
1228        assert_eq!(m.get_row(3), Some(&[3, 4, 5][..]));
1229
1230        // Test out-of-bounds get_row
1231        assert_eq!(m.get_row(4), None);
1232        assert_eq!(m.get_row(100), None);
1233    }
1234
1235    #[test]
1236    fn test_unsafe_get_unchecked_methods() {
1237        let data = make_test_matrix();
1238        let mut m = Matrix::try_from(data.into(), 4, 3).unwrap();
1239
1240        // Safety: derives from known size of matrix and access element ids
1241        unsafe {
1242            assert_eq!(*m.get_unchecked(0, 0), 0);
1243            assert_eq!(*m.get_unchecked(1, 2), 3);
1244            assert_eq!(*m.get_unchecked(3, 1), 4);
1245        }
1246
1247        // Safety: derives from known size of matrix and access element ids
1248        unsafe {
1249            *m.get_unchecked_mut(0, 0) = 100;
1250            *m.get_unchecked_mut(1, 2) = 200;
1251        }
1252
1253        assert_eq!(m[(0, 0)], 100);
1254        assert_eq!(m[(1, 2)], 200);
1255
1256        // Safety: derives from known size of matrix and access element ids
1257        unsafe {
1258            let row0 = m.get_row_unchecked(0);
1259            assert_eq!(row0[0], 100);
1260            assert_eq!(row0[1], 1);
1261            assert_eq!(row0[2], 2);
1262        }
1263
1264        // Safety: derives from known size of matrix and access element ids
1265        unsafe {
1266            let row1 = m.get_row_unchecked_mut(1);
1267            row1[0] = 300;
1268        }
1269
1270        assert_eq!(m[(1, 0)], 300);
1271    }
1272
1273    #[test]
1274    fn test_to_owned() {
1275        let data = make_test_matrix();
1276        let view = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1277
1278        // Test to_owned creates a proper clone
1279        let owned = view.to_owned();
1280        assert_eq!(owned.nrows(), view.nrows());
1281        assert_eq!(owned.ncols(), view.ncols());
1282        assert_eq!(owned.as_slice(), view.as_slice());
1283
1284        // Verify it's actually owned (different memory location)
1285        assert_ne!(owned.as_ptr(), view.as_ptr());
1286
1287        // Test the owned matrix works properly
1288        test_basic_indexing(&owned);
1289    }
1290
1291    #[test]
1292    fn test_generator_trait_impls() {
1293        // Test Generator impl for T where T: Clone
1294        let mut gen = 42i32;
1295        assert_eq!(gen.generate(), 42);
1296        assert_eq!(gen.generate(), 42); // Should be same value since it's cloned
1297
1298        // Test Generator impl for Init<F>
1299        let mut counter = 0;
1300        let mut gen = Init(|| {
1301            counter += 1;
1302            counter
1303        });
1304        assert_eq!(gen.generate(), 1);
1305        assert_eq!(gen.generate(), 2);
1306        assert_eq!(gen.generate(), 3);
1307    }
1308
1309    #[test]
1310    fn test_matrix_from_conversions() {
1311        let data = make_test_matrix();
1312        let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1313
1314        // Test MatrixView to slice conversion
1315        let view = m.as_view();
1316        let slice: &[usize] = view.into();
1317        assert_eq!(slice.len(), 12);
1318        assert_eq!(slice[0], 0);
1319        assert_eq!(slice[11], 5);
1320
1321        // Test MutMatrixView to slice conversion
1322        let data2 = make_test_matrix();
1323        let mut m2 = Matrix::try_from(data2.into(), 4, 3).unwrap();
1324        let mut_view = m2.as_mut_view();
1325        let slice2: &[usize] = mut_view.into();
1326        assert_eq!(slice2.len(), 12);
1327        assert_eq!(slice2[0], 0);
1328        assert_eq!(slice2[11], 5);
1329    }
1330
1331    #[test]
1332    fn test_matrix_construction_edge_cases() {
1333        // Test 1x1 matrix
1334        let m = Matrix::new(42, 1, 1);
1335        assert_eq!(m.nrows(), 1);
1336        assert_eq!(m.ncols(), 1);
1337        assert_eq!(m[(0, 0)], 42);
1338
1339        // Test single row matrix
1340        let m = Matrix::new(7, 1, 5);
1341        assert_eq!(m.nrows(), 1);
1342        assert_eq!(m.ncols(), 5);
1343        assert!(m.as_slice().iter().all(|&x| x == 7));
1344
1345        // Test single column matrix
1346        let m = Matrix::new(9, 5, 1);
1347        assert_eq!(m.nrows(), 5);
1348        assert_eq!(m.ncols(), 1);
1349        assert!(m.as_slice().iter().all(|&x| x == 9));
1350    }
1351
1352    #[test]
1353    fn test_matrix_view_edge_cases_with_data() {
1354        // Test matrix with actual data for edge cases
1355        let data = vec![10, 20];
1356
1357        // 2x1 matrix
1358        let m = MatrixView::try_from(data.as_slice(), 2, 1).unwrap();
1359        assert_eq!(m.nrows(), 2);
1360        assert_eq!(m.ncols(), 1);
1361        assert_eq!(m[(0, 0)], 10);
1362        assert_eq!(m[(1, 0)], 20);
1363        assert_eq!(m.row(0), &[10]);
1364        assert_eq!(m.row(1), &[20]);
1365
1366        // 1x2 matrix
1367        let m = MatrixView::try_from(data.as_slice(), 1, 2).unwrap();
1368        assert_eq!(m.nrows(), 1);
1369        assert_eq!(m.ncols(), 2);
1370        assert_eq!(m[(0, 0)], 10);
1371        assert_eq!(m[(0, 1)], 20);
1372        assert_eq!(m.row(0), &[10, 20]);
1373    }
1374
1375    #[test]
1376    #[cfg(all(not(miri), feature = "rayon"))]
1377    fn test_parallel_methods_edge_cases() {
1378        let data = make_test_matrix();
1379        let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1380
1381        // Test par_window_iter with batchsize larger than matrix
1382        let windows: Vec<_> = m.par_window_iter(10).collect();
1383        assert_eq!(windows.len(), 1);
1384        assert_eq!(windows[0].nrows(), 4);
1385        assert_eq!(windows[0].ncols(), 3);
1386
1387        // Test par_row_iter
1388        let rows: Vec<_> = m.par_row_iter().collect();
1389        assert_eq!(rows.len(), 4);
1390        assert_eq!(rows[0], &[0, 1, 2]);
1391        assert_eq!(rows[3], &[3, 4, 5]);
1392
1393        // Test par_window_iter_mut and par_row_iter_mut
1394        let mut m2 = Matrix::new(0, 4, 3);
1395
1396        // Use par_row_iter_mut to set values
1397        m2.par_row_iter_mut().enumerate().for_each(|(i, row)| {
1398            for (j, elem) in row.iter_mut().enumerate() {
1399                *elem = i + j;
1400            }
1401        });
1402        test_basic_indexing(&m2);
1403
1404        // Test par_window_iter_mut with larger batchsize
1405        let mut m3 = Matrix::new(0, 4, 3);
1406        m3.par_window_iter_mut(10)
1407            .enumerate()
1408            .for_each(|(_, mut window)| {
1409                window.row_iter_mut().enumerate().for_each(|(i, row)| {
1410                    for (j, elem) in row.iter_mut().enumerate() {
1411                        *elem = i + j;
1412                    }
1413                });
1414            });
1415        test_basic_indexing(&m3);
1416    }
1417
1418    #[test]
1419    fn test_matrix_pointers() {
1420        let mut m = Matrix::new(42, 3, 4);
1421
1422        // Test as_ptr and as_mut_ptr return the same address
1423        let const_ptr = m.as_ptr();
1424        let mut_ptr = m.as_mut_ptr();
1425        assert_eq!(const_ptr, mut_ptr as *const _);
1426
1427        // Test that view pointers match original
1428        let view = m.as_view();
1429        assert_eq!(view.as_ptr(), const_ptr);
1430
1431        let mut mut_view = m.as_mut_view();
1432        assert_eq!(mut_view.as_ptr(), const_ptr);
1433        assert_eq!(mut_view.as_mut_ptr(), mut_ptr);
1434    }
1435
1436    #[test]
1437    fn test_matrix_iteration_empty_cases() {
1438        // Test construction of empty matrices (we don't iterate over 0x0 matrices
1439        // since chunks_exact requires non-zero chunk size)
1440        let empty_data: Vec<i32> = vec![];
1441
1442        // Matrix with 0 rows but non-zero cols can be constructed
1443        let _empty_matrix = MatrixView::try_from(empty_data.as_slice(), 0, 5).unwrap();
1444
1445        // Test with actual single row to verify iterator works normally
1446        let data = vec![1, 2, 3];
1447        let single_row = MatrixView::try_from(data.as_slice(), 1, 3).unwrap();
1448        let rows: Vec<_> = single_row.row_iter().collect();
1449        assert_eq!(rows.len(), 1);
1450        assert_eq!(rows[0], &[1, 2, 3]);
1451
1452        // Test iteration over matrix with multiple rows but single column
1453        let data = vec![1, 2, 3];
1454        let single_col = MatrixView::try_from(data.as_slice(), 3, 1).unwrap();
1455        let rows: Vec<_> = single_col.row_iter().collect();
1456        assert_eq!(rows.len(), 3);
1457        assert_eq!(rows[0], &[1]);
1458        assert_eq!(rows[1], &[2]);
1459        assert_eq!(rows[2], &[3]);
1460    }
1461
1462    #[test]
1463    fn test_matrix_init_generator_various_types() {
1464        // Test with different types and generators
1465        use std::sync::atomic::{AtomicUsize, Ordering};
1466
1467        let counter = AtomicUsize::new(0);
1468        let m = Matrix::new(Init(|| counter.fetch_add(1, Ordering::SeqCst)), 2, 3);
1469
1470        // Should be filled in memory order
1471        assert_eq!(m[(0, 0)], 0);
1472        assert_eq!(m[(0, 1)], 1);
1473        assert_eq!(m[(0, 2)], 2);
1474        assert_eq!(m[(1, 0)], 3);
1475        assert_eq!(m[(1, 1)], 4);
1476        assert_eq!(m[(1, 2)], 5);
1477    }
1478
1479    #[test]
1480    fn test_debug_error_formatting() {
1481        // Test Debug implementation for TryFromError
1482        let data = vec![1, 2, 3];
1483        let err = Matrix::try_from(data.into(), 2, 3).unwrap_err();
1484
1485        let debug_str = format!("{:?}", err);
1486        assert!(debug_str.contains("TryFromError"));
1487        assert!(debug_str.contains("data_len: 3"));
1488        assert!(debug_str.contains("nrows: 2"));
1489        assert!(debug_str.contains("ncols: 3"));
1490
1491        // Ensure Debug doesn't require T: Debug by using a non-Debug type
1492        #[derive(Clone, Debug)]
1493        struct NonDebug(#[allow(dead_code)] i32);
1494
1495        let non_debug_data: Box<[NonDebug]> = vec![NonDebug(1), NonDebug(2)].into();
1496        let non_debug_err = Matrix::try_from(non_debug_data, 1, 3).unwrap_err();
1497        let debug_str = format!("{:?}", non_debug_err);
1498        assert!(debug_str.contains("TryFromError"));
1499    }
1500
1501    // Comprehensive tests for rayon-specific functionality
1502
1503    #[test]
1504    #[cfg(feature = "rayon")]
1505    fn test_par_window_iter_comprehensive() {
1506        use rayon::prelude::*;
1507
1508        // Create a larger test matrix for more comprehensive testing
1509        let data: Vec<usize> = (0..24).collect(); // 6x4 matrix
1510        let m = MatrixView::try_from(data.as_slice(), 6, 4).unwrap();
1511
1512        // Test various batch sizes
1513        for batchsize in 1..=8 {
1514            let context = lazy_format!("batchsize = {}", batchsize);
1515            let windows: Vec<_> = m.par_window_iter(batchsize).collect();
1516
1517            // Calculate expected number of windows
1518            let expected_windows = (m.nrows()).div_ceil(batchsize);
1519            assert_eq!(windows.len(), expected_windows, "{}", context);
1520
1521            // Verify each window's properties
1522            let mut total_rows_seen = 0;
1523            for (window_idx, window) in windows.iter().enumerate() {
1524                let expected_rows = if window_idx == windows.len() - 1 {
1525                    // Last window may have fewer rows
1526                    m.nrows() - (windows.len() - 1) * batchsize
1527                } else {
1528                    batchsize
1529                };
1530
1531                assert_eq!(
1532                    window.nrows(),
1533                    expected_rows,
1534                    "window {} - {}",
1535                    window_idx,
1536                    context
1537                );
1538                assert_eq!(
1539                    window.ncols(),
1540                    m.ncols(),
1541                    "window {} - {}",
1542                    window_idx,
1543                    context
1544                );
1545
1546                // Verify data integrity
1547                for (row_idx, row) in window.row_iter().enumerate() {
1548                    let global_row = window_idx * batchsize + row_idx;
1549                    let expected: Vec<usize> =
1550                        (0..m.ncols()).map(|j| global_row * m.ncols() + j).collect();
1551                    assert_eq!(
1552                        row,
1553                        expected.as_slice(),
1554                        "window {}, row {} - {}",
1555                        window_idx,
1556                        row_idx,
1557                        context
1558                    );
1559                }
1560
1561                total_rows_seen += window.nrows();
1562            }
1563
1564            assert_eq!(total_rows_seen, m.nrows(), "{}", context);
1565        }
1566
1567        // Test with batchsize equal to matrix rows
1568        let windows: Vec<_> = m.par_window_iter(m.nrows()).collect();
1569        assert_eq!(windows.len(), 1);
1570        assert_eq!(windows[0].nrows(), m.nrows());
1571        assert_eq!(windows[0].ncols(), m.ncols());
1572
1573        // Test with batchsize larger than matrix rows
1574        let windows: Vec<_> = m.par_window_iter(m.nrows() * 2).collect();
1575        assert_eq!(windows.len(), 1);
1576        assert_eq!(windows[0].nrows(), m.nrows());
1577        assert_eq!(windows[0].ncols(), m.ncols());
1578    }
1579
1580    #[test]
1581    #[cfg(feature = "rayon")]
1582    fn test_par_window_iter_mut_comprehensive() {
1583        use rayon::prelude::*;
1584
1585        // Test various matrix sizes and batch sizes
1586        for nrows in [1, 2, 3, 5, 8, 10] {
1587            for ncols in [1, 3, 4] {
1588                for batchsize in [1, 2, 3, 7] {
1589                    let context = lazy_format!("{}x{}, batchsize={}", nrows, ncols, batchsize);
1590
1591                    let mut m = Matrix::new(0usize, nrows, ncols);
1592
1593                    // Use par_window_iter_mut to fill matrix
1594                    m.par_window_iter_mut(batchsize).enumerate().for_each(
1595                        |(window_idx, mut window)| {
1596                            let base_row = window_idx * batchsize;
1597                            window
1598                                .row_iter_mut()
1599                                .enumerate()
1600                                .for_each(|(row_offset, row)| {
1601                                    let global_row = base_row + row_offset;
1602                                    for (col, elem) in row.iter_mut().enumerate() {
1603                                        *elem = global_row * ncols + col;
1604                                    }
1605                                });
1606                        },
1607                    );
1608
1609                    // Verify the matrix was filled correctly
1610                    for row in 0..nrows {
1611                        for col in 0..ncols {
1612                            let expected = row * ncols + col;
1613                            assert_eq!(
1614                                m[(row, col)],
1615                                expected,
1616                                "pos ({}, {}) - {}",
1617                                row,
1618                                col,
1619                                context
1620                            );
1621                        }
1622                    }
1623                }
1624            }
1625        }
1626    }
1627
1628    #[test]
1629    #[cfg(feature = "rayon")]
1630    fn test_par_row_iter_comprehensive() {
1631        use rayon::prelude::*;
1632
1633        // Create test matrix with predictable pattern
1634        let nrows = 7;
1635        let ncols = 5;
1636        let data: Vec<i32> = (0..(nrows * ncols) as i32).collect();
1637        let m = MatrixView::try_from(data.as_slice(), nrows, ncols).unwrap();
1638
1639        // Test that par_row_iter preserves order and data
1640        let collected_rows: Vec<Vec<i32>> = m.par_row_iter().map(|row| row.to_vec()).collect();
1641
1642        assert_eq!(collected_rows.len(), nrows);
1643
1644        for (row_idx, row) in collected_rows.iter().enumerate() {
1645            assert_eq!(row.len(), ncols);
1646            let expected: Vec<i32> = ((row_idx * ncols)..((row_idx + 1) * ncols))
1647                .map(|x| x as i32)
1648                .collect();
1649            assert_eq!(row, &expected, "row {} mismatch", row_idx);
1650        }
1651
1652        // Test parallel enumeration
1653        let enumerated_rows: Vec<(usize, Vec<i32>)> = m
1654            .par_row_iter()
1655            .enumerate()
1656            .map(|(idx, row)| (idx, row.to_vec()))
1657            .collect();
1658
1659        // Sort by index to ensure we got all indices
1660        let mut sorted_rows = enumerated_rows;
1661        sorted_rows.sort_by_key(|(idx, _)| *idx);
1662
1663        assert_eq!(sorted_rows.len(), nrows);
1664        for (expected_idx, (actual_idx, row)) in sorted_rows.iter().enumerate() {
1665            assert_eq!(*actual_idx, expected_idx);
1666            assert_eq!(row.len(), ncols);
1667        }
1668
1669        // Test parallel reduction operations
1670        let sum: i32 = m.par_row_iter().map(|row| row.iter().sum::<i32>()).sum();
1671
1672        let expected_sum: i32 = data.iter().sum();
1673        assert_eq!(sum, expected_sum);
1674
1675        // Test parallel find operations
1676        let target_row = 3;
1677        let found_row = m
1678            .par_row_iter()
1679            .enumerate()
1680            .find_any(|(idx, _)| *idx == target_row)
1681            .map(|(_, row)| row.to_vec());
1682
1683        assert!(found_row.is_some());
1684        let expected_row: Vec<i32> = ((target_row * ncols)..((target_row + 1) * ncols))
1685            .map(|x| x as i32)
1686            .collect();
1687        assert_eq!(found_row.unwrap(), expected_row);
1688    }
1689
1690    #[test]
1691    #[cfg(feature = "rayon")]
1692    fn test_par_row_iter_mut_comprehensive() {
1693        use rayon::prelude::*;
1694        use std::sync::atomic::{AtomicUsize, Ordering};
1695
1696        let nrows = 6;
1697        let ncols = 4;
1698        let mut m = Matrix::new(0u32, nrows, ncols);
1699
1700        // Test parallel modification
1701        m.par_row_iter_mut().enumerate().for_each(|(row_idx, row)| {
1702            for (col_idx, elem) in row.iter_mut().enumerate() {
1703                *elem = (row_idx * ncols + col_idx) as u32;
1704            }
1705        });
1706
1707        // Verify modifications were applied correctly
1708        for row in 0..nrows {
1709            for col in 0..ncols {
1710                let expected = (row * ncols + col) as u32;
1711                assert_eq!(m[(row, col)], expected, "pos ({}, {})", row, col);
1712            }
1713        }
1714
1715        // Test parallel accumulation with atomic counter
1716        let counter = AtomicUsize::new(0);
1717        m.par_row_iter_mut().for_each(|row| {
1718            counter.fetch_add(1, Ordering::Relaxed);
1719            // Multiply each element by 2
1720            for elem in row {
1721                *elem *= 2;
1722            }
1723        });
1724
1725        assert_eq!(counter.load(Ordering::Relaxed), nrows);
1726
1727        // Verify all elements were doubled
1728        for row in 0..nrows {
1729            for col in 0..ncols {
1730                let expected = ((row * ncols + col) * 2) as u32;
1731                assert_eq!(m[(row, col)], expected, "doubled pos ({}, {})", row, col);
1732            }
1733        }
1734    }
1735
1736    #[test]
1737    #[cfg(feature = "rayon")]
1738    fn test_parallel_iterators_with_single_dimensions() {
1739        use rayon::prelude::*;
1740
1741        // Test single row matrix
1742        let data = vec![1, 2, 3, 4, 5];
1743        let single_row = MatrixView::try_from(data.as_slice(), 1, 5).unwrap();
1744
1745        let windows: Vec<_> = single_row.par_window_iter(1).collect();
1746        assert_eq!(windows.len(), 1);
1747        assert_eq!(windows[0].nrows(), 1);
1748        assert_eq!(windows[0].ncols(), 5);
1749
1750        let rows: Vec<_> = single_row.par_row_iter().collect();
1751        assert_eq!(rows.len(), 1);
1752        assert_eq!(rows[0], &[1, 2, 3, 4, 5]);
1753
1754        // Test single column matrix
1755        let data = vec![1, 2, 3, 4, 5];
1756        let single_col = MatrixView::try_from(data.as_slice(), 5, 1).unwrap();
1757
1758        let windows: Vec<_> = single_col.par_window_iter(2).collect();
1759        assert_eq!(windows.len(), 3); // ceil(5/2) = 3
1760        assert_eq!(windows[0].nrows(), 2);
1761        assert_eq!(windows[1].nrows(), 2);
1762        assert_eq!(windows[2].nrows(), 1); // Last window has remainder
1763
1764        let rows: Vec<_> = single_col.par_row_iter().collect();
1765        assert_eq!(rows.len(), 5);
1766        for (i, row) in rows.iter().enumerate() {
1767            assert_eq!(row, &[i + 1]);
1768        }
1769
1770        // Test 1x1 matrix
1771        let data = vec![42];
1772        let tiny = MatrixView::try_from(data.as_slice(), 1, 1).unwrap();
1773
1774        let windows: Vec<_> = tiny.par_window_iter(1).collect();
1775        assert_eq!(windows.len(), 1);
1776        assert_eq!(windows[0][(0, 0)], 42);
1777
1778        let rows: Vec<_> = tiny.par_row_iter().collect();
1779        assert_eq!(rows.len(), 1);
1780        assert_eq!(rows[0], &[42]);
1781    }
1782
1783    #[test]
1784    #[cfg(feature = "rayon")]
1785    fn test_parallel_window_properties() {
1786        use rayon::prelude::*;
1787
1788        // Test that windows maintain proper matrix properties
1789        let data: Vec<usize> = (0..30).collect();
1790        let m = MatrixView::try_from(data.as_slice(), 6, 5).unwrap();
1791
1792        // Test window indexing works correctly
1793        m.par_window_iter(2)
1794            .enumerate()
1795            .for_each(|(window_idx, window)| {
1796                for row_idx in 0..window.nrows() {
1797                    for col_idx in 0..window.ncols() {
1798                        let global_row = window_idx * 2 + row_idx;
1799                        let expected = global_row * 5 + col_idx;
1800                        assert_eq!(
1801                            window[(row_idx, col_idx)],
1802                            expected,
1803                            "window {}, pos ({}, {})",
1804                            window_idx,
1805                            row_idx,
1806                            col_idx
1807                        );
1808                    }
1809                }
1810            });
1811
1812        // Test window as_slice consistency
1813        m.par_window_iter(3)
1814            .enumerate()
1815            .for_each(|(window_idx, window)| {
1816                let slice = window.as_slice();
1817                assert_eq!(slice.len(), window.nrows() * window.ncols());
1818
1819                for (slice_idx, &value) in slice.iter().enumerate() {
1820                    let row = slice_idx / window.ncols();
1821                    let col = slice_idx % window.ncols();
1822                    assert_eq!(
1823                        value,
1824                        window[(row, col)],
1825                        "window {}, slice_idx {}",
1826                        window_idx,
1827                        slice_idx
1828                    );
1829                }
1830            });
1831
1832        // Test window row iteration
1833        m.par_window_iter(2).for_each(|window| {
1834            let rows_via_iter: Vec<_> = window.row_iter().collect();
1835            assert_eq!(rows_via_iter.len(), window.nrows());
1836
1837            for (row_idx, row) in rows_via_iter.iter().enumerate() {
1838                assert_eq!(row.len(), window.ncols());
1839                for (col_idx, &value) in row.iter().enumerate() {
1840                    assert_eq!(value, window[(row_idx, col_idx)]);
1841                }
1842            }
1843        });
1844    }
1845
1846    #[test]
1847    #[cfg(feature = "rayon")]
1848    fn test_parallel_performance_characteristics() {
1849        use rayon::prelude::*;
1850        use std::sync::atomic::{AtomicUsize, Ordering};
1851
1852        // Create a larger matrix to test parallelism benefits
1853        let nrows = 100;
1854        let ncols = 10;
1855        let mut m = Matrix::new(0usize, nrows, ncols);
1856
1857        // Test that parallel operations can be chained
1858        let work_counter = AtomicUsize::new(0);
1859
1860        m.par_window_iter_mut(10)
1861            .enumerate()
1862            .for_each(|(window_idx, mut window)| {
1863                work_counter.fetch_add(1, Ordering::Relaxed);
1864
1865                // Nested parallel operation within window
1866                window
1867                    .row_iter_mut()
1868                    .enumerate()
1869                    .for_each(|(row_offset, row)| {
1870                        let global_row = window_idx * 10 + row_offset;
1871                        for (col, elem) in row.iter_mut().enumerate() {
1872                            *elem = global_row * ncols + col;
1873                        }
1874                    });
1875            });
1876
1877        // Should have processed 10 windows (100 rows / 10 batch size)
1878        assert_eq!(work_counter.load(Ordering::Relaxed), 10);
1879
1880        // Verify correctness
1881        for row in 0..nrows {
1882            for col in 0..ncols {
1883                assert_eq!(m[(row, col)], row * ncols + col);
1884            }
1885        }
1886
1887        // Test parallel reduction across windows
1888        let total_sum: usize = m
1889            .par_window_iter(15)
1890            .map(|window| {
1891                window
1892                    .row_iter()
1893                    .map(|row| row.iter().sum::<usize>())
1894                    .sum::<usize>()
1895            })
1896            .sum();
1897
1898        let expected_sum: usize = (0..(nrows * ncols)).sum();
1899        assert_eq!(total_sum, expected_sum);
1900    }
1901
1902    #[test]
1903    #[cfg(feature = "rayon")]
1904    fn test_rayon_trait_bounds_validation() {
1905        use rayon::prelude::*;
1906
1907        // Test that the Sync/Send bounds work correctly
1908        let data: Vec<u64> = (0..20).collect();
1909        let m = MatrixView::try_from(data.as_slice(), 4, 5).unwrap();
1910
1911        // This should compile because u64 is Sync
1912        let _: Vec<_> = m.par_window_iter(2).collect();
1913        let _: Vec<_> = m.par_row_iter().collect();
1914
1915        // Test with mutable matrix
1916        let mut m = Matrix::new(0u64, 4, 5);
1917
1918        // This should compile because u64 is Send
1919        m.par_window_iter_mut(2).for_each(|mut window| {
1920            window.row_iter_mut().for_each(|row| {
1921                for elem in row {
1922                    *elem = 42;
1923                }
1924            });
1925        });
1926
1927        m.par_row_iter_mut().for_each(|row| {
1928            for elem in row {
1929                *elem += 1;
1930            }
1931        });
1932
1933        // Verify all elements are 43
1934        assert!(m.as_slice().iter().all(|&x| x == 43));
1935    }
1936}