1use std::{
7 fmt,
8 ops::{Index, IndexMut},
9};
10
11#[cfg(feature = "rayon")]
12use rayon::prelude::{IndexedParallelIterator, ParallelIterator, ParallelSlice, ParallelSliceMut};
13use thiserror::Error;
14
15pub unsafe trait DenseData {
31 type Elem;
32
33 fn as_slice(&self) -> &[Self::Elem];
35}
36
37pub unsafe trait MutDenseData: DenseData {
51 fn as_mut_slice(&mut self) -> &mut [Self::Elem];
52}
53
54unsafe impl<T> DenseData for &[T] {
56 type Elem = T;
57 fn as_slice(&self) -> &[Self::Elem] {
58 self
59 }
60}
61
62unsafe impl<T> DenseData for &mut [T] {
64 type Elem = T;
65 fn as_slice(&self) -> &[Self::Elem] {
66 self
67 }
68}
69
70unsafe impl<T> MutDenseData for &mut [T] {
73 fn as_mut_slice(&mut self) -> &mut [Self::Elem] {
74 self
75 }
76}
77
78unsafe impl<T> DenseData for Box<[T]> {
80 type Elem = T;
81 fn as_slice(&self) -> &[Self::Elem] {
82 self
83 }
84}
85
86unsafe impl<T> MutDenseData for Box<[T]> {
89 fn as_mut_slice(&mut self) -> &mut [Self::Elem] {
90 self
91 }
92}
93
94#[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
136impl<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 pub fn into_inner(self) -> T {
150 self.data
151 }
152
153 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
164pub 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
178pub 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 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 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 pub fn ncols(&self) -> usize {
223 self.ncols
224 }
225
226 pub fn nrows(&self) -> usize {
228 self.nrows
229 }
230
231 pub fn as_slice(&self) -> &[T::Elem] {
233 self.data.as_slice()
234 }
235
236 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 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 unsafe { self.get_row_unchecked(row) }
258 }
259
260 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 pub fn get_row(&self, row: usize) -> Option<&[T::Elem]> {
273 if row < self.nrows() {
274 Some(unsafe { self.get_row_unchecked(row) })
276 } else {
277 None
278 }
279 }
280
281 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 unsafe { self.as_slice().get_unchecked(start..start + ncols) }
298 }
299
300 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 unsafe { self.get_row_unchecked_mut(row) }
317 }
318
319 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 unsafe {
339 self.data
340 .as_mut_slice()
341 .get_unchecked_mut(start..start + ncols)
342 }
343 }
344
345 pub fn row_iter(&self) -> impl ExactSizeIterator<Item = &[T::Elem]> {
349 self.data.as_slice().chunks_exact(self.ncols())
350 }
351
352 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 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 #[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 #[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 #[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 #[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 pub fn into_inner(self) -> T {
483 self.data
484 }
485
486 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 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 pub fn subview(&self, rows: std::ops::Range<usize>) -> Option<MatrixView<'_, T::Elem>> {
531 let ncols = self.ncols();
532
533 let lower = rows.start.checked_mul(ncols)?;
534 let upper = rows.end.checked_mul(ncols)?;
535
536 if let Some(data) = self.as_slice().get(lower..upper) {
537 Some(MatrixBase {
538 data,
539 nrows: rows.len(),
540 ncols: self.ncols(),
541 })
542 } else {
543 None
544 }
545 }
546
547 pub fn as_ptr(&self) -> *const T::Elem {
549 self.as_slice().as_ptr()
550 }
551
552 pub fn as_mut_ptr(&mut self) -> *mut T::Elem
554 where
555 T: MutDenseData,
556 {
557 self.as_mut_slice().as_mut_ptr()
558 }
559
560 pub unsafe fn get_unchecked(&self, row: usize, col: usize) -> &T::Elem {
568 debug_assert!(row < self.nrows);
569 debug_assert!(col < self.ncols);
570 self.as_slice().get_unchecked(row * self.ncols + col)
571 }
572
573 pub unsafe fn get_unchecked_mut(&mut self, row: usize, col: usize) -> &mut T::Elem
581 where
582 T: MutDenseData,
583 {
584 let ncols = self.ncols;
585 debug_assert!(row < self.nrows);
586 debug_assert!(col < self.ncols);
587 self.as_mut_slice().get_unchecked_mut(row * ncols + col)
588 }
589
590 pub fn to_owned(&self) -> Matrix<T::Elem>
591 where
592 T::Elem: Clone,
593 {
594 Matrix {
595 data: self.data.as_slice().into(),
596 nrows: self.nrows,
597 ncols: self.ncols,
598 }
599 }
600}
601
602pub type Matrix<T> = MatrixBase<Box<[T]>>;
605
606pub type MatrixView<'a, T> = MatrixBase<&'a [T]>;
613
614pub type MutMatrixView<'a, T> = MatrixBase<&'a mut [T]>;
621
622impl<'a, T> From<MatrixView<'a, T>> for &'a [T] {
624 fn from(view: MatrixView<'a, T>) -> Self {
625 view.data
626 }
627}
628
629impl<'a, T> From<MutMatrixView<'a, T>> for &'a [T] {
631 fn from(view: MutMatrixView<'a, T>) -> Self {
632 view.data
633 }
634}
635
636impl<T> Index<(usize, usize)> for MatrixBase<T>
642where
643 T: DenseData,
644{
645 type Output = T::Elem;
646
647 fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
648 assert!(
649 row < self.nrows(),
650 "row {row} is out of bounds (max: {})",
651 self.nrows()
652 );
653 assert!(
654 col < self.ncols(),
655 "col {col} is out of bounds (max: {})",
656 self.ncols()
657 );
658
659 unsafe { self.get_unchecked(row, col) }
661 }
662}
663
664impl<T> IndexMut<(usize, usize)> for MatrixBase<T>
670where
671 T: MutDenseData,
672{
673 fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
674 assert!(
675 row < self.nrows(),
676 "row {row} is out of bounds (max: {})",
677 self.nrows()
678 );
679 assert!(
680 col < self.ncols(),
681 "col {col} is out of bounds (max: {})",
682 self.ncols()
683 );
684
685 unsafe { self.get_unchecked_mut(row, col) }
687 }
688}
689
690#[cfg(test)]
695mod tests {
696 use super::*;
697 use crate::lazy_format;
698
699 fn is_copyable<T: Copy>(_x: T) -> bool {
703 true
704 }
705
706 fn test_dense_data_repr<T, Repr>(
709 ptr: *const T,
710 len: usize,
711 repr: Repr,
712 context: &dyn std::fmt::Display,
713 ) where
714 T: Copy,
715 Repr: DenseData<Elem = T>,
716 {
717 let retrieved = repr.as_slice();
718 assert_eq!(retrieved.len(), len, "{}", context);
719 assert_eq!(retrieved.as_ptr(), ptr, "{}", context);
720 }
721
722 fn set_mut_dense_data_repr<T, Repr>(repr: &mut Repr, base: T, increment: T)
726 where
727 T: Copy + std::ops::Add<Output = T>,
728 Repr: DenseData<Elem = T> + MutDenseData,
729 {
730 let slice = repr.as_mut_slice();
731 for i in 0..slice.len() {
732 if i == 0 {
733 slice[i] = base;
734 } else {
735 slice[i] = slice[i - 1] + increment;
736 }
737 }
738 }
739
740 #[test]
741 fn slice_implements_dense_data_repr() {
742 for len in 0..10 {
743 let context = lazy_format!("len = {}", len);
744 let data: Vec<f32> = vec![0.0; len];
745 let slice = data.as_slice();
746 test_dense_data_repr(slice.as_ptr(), slice.len(), slice, &context);
747 }
748 }
749
750 #[test]
751 fn mut_slice_mplements_dense_data_repr() {
752 for len in 0..10 {
753 let context = lazy_format!("len = {}", len);
754 let mut data: Vec<f32> = vec![0.0; len];
755 let slice = data.as_mut_slice();
756
757 let ptr = slice.as_ptr();
758 let len = slice.len();
759 test_dense_data_repr(ptr, len, slice, &context);
760 }
761 }
762
763 #[test]
764 fn mut_slice_implements_mut_dense_data_repr() {
765 for len in 0..10 {
766 let context = lazy_format!("len = {}", len);
767 let mut data: Vec<f32> = vec![0.0; len];
768 let mut slice = data.as_mut_slice();
769
770 let base = 2.0;
771 let increment = 1.0;
772 set_mut_dense_data_repr(&mut slice, base, increment);
773
774 for (i, &v) in slice.iter().enumerate() {
775 let context = lazy_format!("entry {}, {}", i, context);
776 assert_eq!(v, base + increment * (i as f32), "{}", context);
777 }
778 }
779 }
780
781 #[test]
786 fn try_from_error_misc() {
787 let x = TryFromError::<&[f32]> {
788 data: &[],
789 nrows: 1,
790 ncols: 2,
791 };
792
793 let debug = format!("{:?}", x);
794 println!("debug = {}", debug);
795 assert!(debug.contains("TryFromError"));
796 assert!(debug.contains("data_len: 0"));
797 assert!(debug.contains("nrows: 1"));
798 assert!(debug.contains("ncols: 2"));
799 }
800
801 fn make_test_matrix() -> Vec<usize> {
802 vec![0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]
811 }
812
813 #[cfg(feature = "rayon")]
814 fn test_basic_indexing_parallel(m: MatrixView<'_, usize>) {
815 let batchsize = 2;
817 m.par_window_iter(batchsize)
818 .enumerate()
819 .for_each(|(i, submatrix)| {
820 assert_eq!(submatrix.nrows(), batchsize);
821 assert_eq!(submatrix.ncols(), m.ncols());
822
823 let base = i * batchsize;
825 assert_eq!(submatrix[(0, 0)], base);
826 assert_eq!(submatrix[(0, 1)], base + 1);
827 assert_eq!(submatrix[(0, 2)], base + 2);
828
829 assert_eq!(submatrix[(1, 0)], base + 1);
830 assert_eq!(submatrix[(1, 1)], base + 2);
831 assert_eq!(submatrix[(1, 2)], base + 3);
832 });
833
834 let batchsize = 3;
837 m.par_window_iter(batchsize)
838 .enumerate()
839 .for_each(|(i, submatrix)| {
840 if i == 0 {
841 assert_eq!(submatrix.nrows(), batchsize);
842 assert_eq!(submatrix.ncols(), m.ncols());
843
844 assert_eq!(submatrix[(0, 0)], 0);
846 assert_eq!(submatrix[(0, 1)], 1);
847 assert_eq!(submatrix[(0, 2)], 2);
848
849 assert_eq!(submatrix[(1, 0)], 1);
850 assert_eq!(submatrix[(1, 1)], 2);
851 assert_eq!(submatrix[(1, 2)], 3);
852
853 assert_eq!(submatrix[(2, 0)], 2);
854 assert_eq!(submatrix[(2, 1)], 3);
855 assert_eq!(submatrix[(2, 2)], 4);
856 } else {
857 assert_eq!(submatrix.nrows(), 1);
858 assert_eq!(submatrix.ncols(), m.ncols());
859
860 assert_eq!(submatrix[(0, 0)], 3);
862 assert_eq!(submatrix[(0, 1)], 4);
863 assert_eq!(submatrix[(0, 2)], 5);
864 }
865 });
866
867 let seen_rows: Box<[usize]> = m
869 .par_row_iter()
870 .enumerate()
871 .map(|(i, row)| {
872 let expected: Box<[usize]> = (0..m.ncols()).map(|j| j + i).collect();
873 assert_eq!(row, &*expected);
874 i
875 })
876 .collect();
877
878 let expected: Box<[usize]> = (0..m.nrows()).collect();
879 assert_eq!(seen_rows, expected);
880 }
881
882 fn test_basic_indexing<T>(m: &MatrixBase<T>)
883 where
884 T: DenseData<Elem = usize> + Sync,
885 {
886 assert_eq!(m.nrows(), 4);
887 assert_eq!(m.ncols(), 3);
888
889 assert_eq!(m[(0, 0)], 0);
891 assert_eq!(m[(0, 1)], 1);
892 assert_eq!(m[(0, 2)], 2);
893
894 assert_eq!(m[(1, 0)], 1);
895 assert_eq!(m[(1, 1)], 2);
896 assert_eq!(m[(1, 2)], 3);
897
898 assert_eq!(m[(2, 0)], 2);
899 assert_eq!(m[(2, 1)], 3);
900 assert_eq!(m[(2, 2)], 4);
901
902 assert_eq!(m[(3, 0)], 3);
903 assert_eq!(m[(3, 1)], 4);
904 assert_eq!(m[(3, 2)], 5);
905
906 assert_eq!(m.row(0), &[0, 1, 2]);
908 assert_eq!(m.row(1), &[1, 2, 3]);
909 assert_eq!(m.row(2), &[2, 3, 4]);
910 assert_eq!(m.row(3), &[3, 4, 5]);
911
912 let rows: Vec<Vec<usize>> = m.row_iter().map(|x| x.to_vec()).collect();
913 assert_eq!(m.row(0), &rows[0]);
914 assert_eq!(m.row(1), &rows[1]);
915 assert_eq!(m.row(2), &rows[2]);
916 assert_eq!(m.row(3), &rows[3]);
917
918 let batchsize = 2;
920 m.window_iter(batchsize)
921 .enumerate()
922 .for_each(|(i, submatrix)| {
923 assert_eq!(submatrix.nrows(), batchsize);
924 assert_eq!(submatrix.ncols(), m.ncols());
925
926 let base = i * batchsize;
928 assert_eq!(submatrix[(0, 0)], base);
929 assert_eq!(submatrix[(0, 1)], base + 1);
930 assert_eq!(submatrix[(0, 2)], base + 2);
931
932 assert_eq!(submatrix[(1, 0)], base + 1);
933 assert_eq!(submatrix[(1, 1)], base + 2);
934 assert_eq!(submatrix[(1, 2)], base + 3);
935 });
936
937 let batchsize = 3;
940 m.window_iter(batchsize)
941 .enumerate()
942 .for_each(|(i, submatrix)| {
943 if i == 0 {
944 assert_eq!(submatrix.nrows(), batchsize);
945 assert_eq!(submatrix.ncols(), m.ncols());
946
947 assert_eq!(submatrix[(0, 0)], 0);
949 assert_eq!(submatrix[(0, 1)], 1);
950 assert_eq!(submatrix[(0, 2)], 2);
951
952 assert_eq!(submatrix[(1, 0)], 1);
953 assert_eq!(submatrix[(1, 1)], 2);
954 assert_eq!(submatrix[(1, 2)], 3);
955
956 assert_eq!(submatrix[(2, 0)], 2);
957 assert_eq!(submatrix[(2, 1)], 3);
958 assert_eq!(submatrix[(2, 2)], 4);
959 } else {
960 assert_eq!(submatrix.nrows(), 1);
961 assert_eq!(submatrix.ncols(), m.ncols());
962
963 assert_eq!(submatrix[(0, 0)], 3);
965 assert_eq!(submatrix[(0, 1)], 4);
966 assert_eq!(submatrix[(0, 2)], 5);
967 }
968 });
969
970 #[cfg(all(not(miri), feature = "rayon"))]
971 test_basic_indexing_parallel(m.as_view());
972 }
973
974 #[test]
975 fn matrix_happy_path() {
976 let data = make_test_matrix();
977 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
978 test_basic_indexing(&m);
979
980 let ptr = m.as_ptr();
983 let view = m.as_view();
984 assert!(is_copyable(view));
985 assert_eq!(view.as_ptr(), ptr);
986 assert_eq!(view.nrows(), m.nrows());
987 assert_eq!(view.ncols(), m.ncols());
988 test_basic_indexing(&view);
989 }
990
991 #[test]
992 fn matrix_try_from_construction_error() {
993 let data = make_test_matrix();
994 let ptr = data.as_ptr();
995 let len = data.len();
996
997 let m = Matrix::try_from(data.into(), 5, 4);
998 assert!(m.is_err());
999 let err = m.unwrap_err();
1000 assert_eq!(
1001 err.to_string(),
1002 "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1003 );
1004
1005 let data = err.into_inner();
1007 assert_eq!(data.as_ptr(), ptr);
1008 assert_eq!(data.len(), len);
1009
1010 let m = MatrixView::try_from(&data, 5, 4);
1011 assert!(m.is_err());
1012 assert_eq!(
1013 m.unwrap_err().to_string(),
1014 "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1015 );
1016 }
1017
1018 #[test]
1019 fn matrix_mut_view() {
1020 let mut m = Matrix::<usize>::new(0, 4, 3);
1021 assert_eq!(m.nrows(), 4);
1022 assert_eq!(m.ncols(), 3);
1023 assert!(m.as_slice().iter().all(|&i| i == 0));
1024 let ptr = m.as_ptr();
1025 let mut_ptr = m.as_mut_ptr();
1026 assert_eq!(ptr, mut_ptr);
1027
1028 let mut view = m.as_mut_view();
1029 assert_eq!(view.nrows(), 4);
1030 assert_eq!(view.ncols(), 3);
1031 assert_eq!(view.as_ptr(), ptr);
1032 assert_eq!(view.as_mut_ptr(), mut_ptr);
1033
1034 for i in 0..view.nrows() {
1036 for j in 0..view.ncols() {
1037 view[(i, j)] = i + j;
1038 }
1039 }
1040
1041 test_basic_indexing(&m);
1043
1044 let inner = m.into_inner();
1045 assert_eq!(inner.as_ptr(), ptr);
1046 assert_eq!(inner.len(), 4 * 3);
1047 }
1048
1049 #[test]
1050 fn matrix_view_zero_sizes() {
1051 let data: Vec<usize> = vec![];
1052 let m = MatrixView::try_from(data.as_slice(), 0, 10).unwrap();
1054 assert_eq!(m.nrows(), 0);
1055 assert_eq!(m.ncols(), 10);
1056
1057 let m = MatrixView::try_from(data.as_slice(), 3, 0).unwrap();
1059 assert_eq!(m.nrows(), 3);
1060 assert_eq!(m.ncols(), 0);
1061 let empty: &[usize] = &[];
1062 assert_eq!(m.row(0), empty);
1063 assert_eq!(m.row(1), empty);
1064 assert_eq!(m.row(2), empty);
1065
1066 let m = MatrixView::try_from(data.as_slice(), 0, 0).unwrap();
1068 assert_eq!(m.nrows(), 0);
1069 assert_eq!(m.ncols(), 0);
1070 }
1071
1072 #[test]
1073 fn matrix_view_construction_elementwise() {
1074 let mut m = Matrix::<usize>::new(0, 4, 3);
1075
1076 for i in 0..m.nrows() {
1078 for j in 0..m.ncols() {
1079 m[(i, j)] = i + j;
1080 }
1081 }
1082 test_basic_indexing(&m);
1083 }
1084
1085 #[test]
1086 fn matrix_construction_by_row() {
1087 let mut m = Matrix::<usize>::new(0, 4, 3);
1088 assert!(m.as_slice().iter().all(|i| *i == 0));
1089
1090 let ncols = m.ncols();
1091 for i in 0..m.nrows() {
1092 let row = m.row_mut(i);
1093 assert_eq!(row.len(), ncols);
1094 row[0] = i;
1095 row[1] = i + 1;
1096 row[2] = i + 2;
1097 }
1098 test_basic_indexing(&m);
1099 }
1100
1101 #[test]
1102 fn matrix_construction_by_rowiter() {
1103 let mut m = Matrix::<usize>::new(0, 4, 3);
1104 assert!(m.as_slice().iter().all(|i| *i == 0));
1105
1106 let ncols = m.ncols();
1107 m.row_iter_mut().enumerate().for_each(|(i, row)| {
1108 assert_eq!(row.len(), ncols);
1109 row[0] = i;
1110 row[1] = i + 1;
1111 row[2] = i + 2;
1112 });
1113 test_basic_indexing(&m);
1114 }
1115
1116 #[cfg(all(not(miri), feature = "rayon"))]
1117 #[test]
1118 fn matrix_construction_by_par_windows() {
1119 let mut m = Matrix::<usize>::new(0, 4, 3);
1120 assert!(m.as_slice().iter().all(|i| *i == 0));
1121
1122 let ncols = m.ncols();
1123 for batchsize in 1..=4 {
1124 m.par_window_iter_mut(batchsize)
1125 .enumerate()
1126 .for_each(|(i, mut submatrix)| {
1127 let base = i * batchsize;
1128 submatrix.row_iter_mut().enumerate().for_each(|(j, row)| {
1129 assert_eq!(row.len(), ncols);
1130 row[0] = base + j;
1131 row[1] = base + j + 1;
1132 row[2] = base + j + 2;
1133 });
1134 });
1135 test_basic_indexing(&m);
1136 }
1137 }
1138
1139 #[test]
1140 fn matrix_construction_happens_in_memory_order() {
1141 let mut i = 0;
1142 let ncols = 3;
1143 let initializer = Init(|| {
1144 let value = (i % ncols) + (i / ncols);
1145 i += 1;
1146 value
1147 });
1148
1149 let m = Matrix::new(initializer, 4, 3);
1150 test_basic_indexing(&m);
1151 }
1152
1153 #[test]
1155 #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1156 fn test_get_row_panics() {
1157 let m = Matrix::<usize>::new(0, 3, 7);
1158 m.row(3);
1159 }
1160
1161 #[test]
1162 #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1163 fn test_get_row_mut_panics() {
1164 let mut m = Matrix::<usize>::new(0, 3, 7);
1165 m.row_mut(3);
1166 }
1167
1168 #[test]
1169 #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1170 fn test_index_panics_row() {
1171 let m = Matrix::<usize>::new(0, 3, 7);
1172 let _ = m[(3, 2)];
1173 }
1174
1175 #[test]
1176 #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1177 fn test_index_panics_col() {
1178 let m = Matrix::<usize>::new(0, 3, 7);
1179 let _ = m[(2, 7)];
1180 }
1181
1182 #[test]
1183 #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1184 fn test_index_mut_panics_row() {
1185 let mut m = Matrix::<usize>::new(0, 3, 7);
1186 m[(3, 2)] = 1;
1187 }
1188
1189 #[test]
1190 #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1191 fn test_index_mut_panics_col() {
1192 let mut m = Matrix::<usize>::new(0, 3, 7);
1193 m[(2, 7)] = 1;
1194 }
1195
1196 #[test]
1197 #[cfg(feature = "rayon")]
1198 #[should_panic(expected = "par_window_iter batchsize cannot be zero")]
1199 fn test_par_window_iter_panics() {
1200 let m = Matrix::<usize>::new(0, 4, 4);
1201 let _ = m.par_window_iter(0);
1202 }
1203
1204 #[test]
1205 #[cfg(feature = "rayon")]
1206 #[should_panic(expected = "par_window_iter_mut batchsize cannot be zero")]
1207 fn test_par_window_iter_mut_panics() {
1208 let mut m = Matrix::<usize>::new(0, 4, 4);
1209 let _ = m.par_window_iter_mut(0);
1210 }
1211
1212 #[test]
1215 fn test_box_slice_dense_data_impls() {
1216 let data: Box<[f32]> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0].into();
1218 let ptr = data.as_ptr();
1219 let len = data.len();
1220
1221 test_dense_data_repr(ptr, len, data, &lazy_format!("Box<[T]> DenseData"));
1223
1224 let mut data: Box<[f32]> = vec![0.0; 6].into();
1226 set_mut_dense_data_repr(&mut data, 1.0, 2.0);
1227 for (i, &v) in data.iter().enumerate() {
1228 assert_eq!(
1229 v,
1230 1.0 + 2.0 * (i as f32),
1231 "Box<[T]> MutDenseData at index {}",
1232 i
1233 );
1234 }
1235 }
1236
1237 #[test]
1238 fn test_try_from_error_light() {
1239 let data = vec![1, 2, 3];
1240 let err = MatrixView::try_from(data.as_slice(), 2, 3).unwrap_err();
1241
1242 let static_err = err.as_static();
1244 assert_eq!(static_err.len, 3);
1245 assert_eq!(static_err.nrows, 2);
1246 assert_eq!(static_err.ncols, 3);
1247
1248 let display_msg = format!("{}", static_err);
1250 assert!(display_msg.contains("tried to construct a matrix view with 2 rows and 3 columns"));
1251 assert!(display_msg.contains("slice of length 3"));
1252
1253 let recovered_data = err.into_inner();
1255 assert_eq!(recovered_data, data.as_slice());
1256 }
1257
1258 #[test]
1259 fn test_get_row_optional() {
1260 let data = make_test_matrix();
1261 let m = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1262
1263 assert_eq!(m.get_row(0), Some(&[0, 1, 2][..]));
1265 assert_eq!(m.get_row(1), Some(&[1, 2, 3][..]));
1266 assert_eq!(m.get_row(3), Some(&[3, 4, 5][..]));
1267
1268 assert_eq!(m.get_row(4), None);
1270 assert_eq!(m.get_row(100), None);
1271 }
1272
1273 #[test]
1274 fn test_unsafe_get_unchecked_methods() {
1275 let data = make_test_matrix();
1276 let mut m = Matrix::try_from(data.into(), 4, 3).unwrap();
1277
1278 unsafe {
1280 assert_eq!(*m.get_unchecked(0, 0), 0);
1281 assert_eq!(*m.get_unchecked(1, 2), 3);
1282 assert_eq!(*m.get_unchecked(3, 1), 4);
1283 }
1284
1285 unsafe {
1287 *m.get_unchecked_mut(0, 0) = 100;
1288 *m.get_unchecked_mut(1, 2) = 200;
1289 }
1290
1291 assert_eq!(m[(0, 0)], 100);
1292 assert_eq!(m[(1, 2)], 200);
1293
1294 unsafe {
1296 let row0 = m.get_row_unchecked(0);
1297 assert_eq!(row0[0], 100);
1298 assert_eq!(row0[1], 1);
1299 assert_eq!(row0[2], 2);
1300 }
1301
1302 unsafe {
1304 let row1 = m.get_row_unchecked_mut(1);
1305 row1[0] = 300;
1306 }
1307
1308 assert_eq!(m[(1, 0)], 300);
1309 }
1310
1311 #[test]
1312 fn test_to_owned() {
1313 let data = make_test_matrix();
1314 let view = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1315
1316 let owned = view.to_owned();
1318 assert_eq!(owned.nrows(), view.nrows());
1319 assert_eq!(owned.ncols(), view.ncols());
1320 assert_eq!(owned.as_slice(), view.as_slice());
1321
1322 assert_ne!(owned.as_ptr(), view.as_ptr());
1324
1325 test_basic_indexing(&owned);
1327 }
1328
1329 #[test]
1330 fn test_generator_trait_impls() {
1331 let mut gen = 42i32;
1333 assert_eq!(gen.generate(), 42);
1334 assert_eq!(gen.generate(), 42); let mut counter = 0;
1338 let mut gen = Init(|| {
1339 counter += 1;
1340 counter
1341 });
1342 assert_eq!(gen.generate(), 1);
1343 assert_eq!(gen.generate(), 2);
1344 assert_eq!(gen.generate(), 3);
1345 }
1346
1347 #[test]
1348 fn test_matrix_from_conversions() {
1349 let data = make_test_matrix();
1350 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1351
1352 let view = m.as_view();
1354 let slice: &[usize] = view.into();
1355 assert_eq!(slice.len(), 12);
1356 assert_eq!(slice[0], 0);
1357 assert_eq!(slice[11], 5);
1358
1359 let data2 = make_test_matrix();
1361 let mut m2 = Matrix::try_from(data2.into(), 4, 3).unwrap();
1362 let mut_view = m2.as_mut_view();
1363 let slice2: &[usize] = mut_view.into();
1364 assert_eq!(slice2.len(), 12);
1365 assert_eq!(slice2[0], 0);
1366 assert_eq!(slice2[11], 5);
1367 }
1368
1369 #[test]
1370 fn test_matrix_construction_edge_cases() {
1371 let m = Matrix::new(42, 1, 1);
1373 assert_eq!(m.nrows(), 1);
1374 assert_eq!(m.ncols(), 1);
1375 assert_eq!(m[(0, 0)], 42);
1376
1377 let m = Matrix::new(7, 1, 5);
1379 assert_eq!(m.nrows(), 1);
1380 assert_eq!(m.ncols(), 5);
1381 assert!(m.as_slice().iter().all(|&x| x == 7));
1382
1383 let m = Matrix::new(9, 5, 1);
1385 assert_eq!(m.nrows(), 5);
1386 assert_eq!(m.ncols(), 1);
1387 assert!(m.as_slice().iter().all(|&x| x == 9));
1388 }
1389
1390 #[test]
1391 fn test_matrix_view_edge_cases_with_data() {
1392 let data = vec![10, 20];
1394
1395 let m = MatrixView::try_from(data.as_slice(), 2, 1).unwrap();
1397 assert_eq!(m.nrows(), 2);
1398 assert_eq!(m.ncols(), 1);
1399 assert_eq!(m[(0, 0)], 10);
1400 assert_eq!(m[(1, 0)], 20);
1401 assert_eq!(m.row(0), &[10]);
1402 assert_eq!(m.row(1), &[20]);
1403
1404 let m = MatrixView::try_from(data.as_slice(), 1, 2).unwrap();
1406 assert_eq!(m.nrows(), 1);
1407 assert_eq!(m.ncols(), 2);
1408 assert_eq!(m[(0, 0)], 10);
1409 assert_eq!(m[(0, 1)], 20);
1410 assert_eq!(m.row(0), &[10, 20]);
1411 }
1412
1413 #[test]
1414 fn test_subview() {
1415 let data = make_test_matrix();
1416 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1417
1418 {
1420 let subview = m.subview(0..4).unwrap();
1421 assert_eq!(subview.nrows(), 4);
1422 assert_eq!(subview.ncols(), 3);
1423
1424 assert_eq!(subview.row(0), &[0, 1, 2]);
1425 assert_eq!(subview.row(1), &[1, 2, 3]);
1426 assert_eq!(subview.row(2), &[2, 3, 4]);
1427 assert_eq!(subview.row(3), &[3, 4, 5]);
1428 assert!(subview.get_row(4).is_none());
1429 }
1430
1431 {
1433 let subview = m.subview(1..4).unwrap();
1434 assert_eq!(subview.nrows(), 3);
1435 assert_eq!(subview.ncols(), 3);
1436
1437 assert_eq!(subview.row(0), &[1, 2, 3]);
1438 assert_eq!(subview.row(1), &[2, 3, 4]);
1439 assert_eq!(subview.row(2), &[3, 4, 5]);
1440 assert!(subview.get_row(3).is_none());
1441 }
1442
1443 {
1445 let subview = m.subview(1..3).unwrap();
1446 assert_eq!(subview.nrows(), 2);
1447 assert_eq!(subview.ncols(), 3);
1448
1449 assert_eq!(subview.row(0), &[1, 2, 3]);
1450 assert_eq!(subview.row(1), &[2, 3, 4]);
1451 assert!(subview.get_row(2).is_none());
1452 }
1453
1454 {
1456 let subview = m.subview(2..2).unwrap();
1457 assert_eq!(subview.nrows(), 0);
1458 assert_eq!(subview.ncols(), 3);
1459 }
1460
1461 {
1463 let subview = m.subview(0..0).unwrap();
1464 assert_eq!(subview.nrows(), 0);
1465 assert_eq!(subview.ncols(), 3);
1466
1467 let subview = m.subview(4..4).unwrap();
1468 assert_eq!(subview.nrows(), 0);
1469 assert_eq!(subview.ncols(), 3);
1470 }
1471
1472 assert!(m.subview(5..5).is_none());
1474
1475 assert!(m.subview(0..6).is_none());
1477 assert!(m.subview(2..10).is_none());
1478
1479 assert!(m.subview(10..100).is_none());
1481
1482 #[expect(
1484 clippy::reversed_empty_ranges,
1485 reason = "we want to make sure it doesn't work"
1486 )]
1487 let empty = 3..2;
1488 assert!(m.subview(empty).is_none());
1489
1490 #[expect(
1491 clippy::reversed_empty_ranges,
1492 reason = "we want to make sure it doesn't work"
1493 )]
1494 let empty = 3..1;
1495 assert!(m.subview(empty).is_none());
1496
1497 assert!(m.subview(usize::MAX - 1..usize::MAX).is_none());
1499 assert!(m.subview(0..usize::MAX).is_none());
1500 }
1501
1502 #[test]
1503 #[cfg(all(not(miri), feature = "rayon"))]
1504 fn test_parallel_methods_edge_cases() {
1505 let data = make_test_matrix();
1506 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1507
1508 let windows: Vec<_> = m.par_window_iter(10).collect();
1510 assert_eq!(windows.len(), 1);
1511 assert_eq!(windows[0].nrows(), 4);
1512 assert_eq!(windows[0].ncols(), 3);
1513
1514 let rows: Vec<_> = m.par_row_iter().collect();
1516 assert_eq!(rows.len(), 4);
1517 assert_eq!(rows[0], &[0, 1, 2]);
1518 assert_eq!(rows[3], &[3, 4, 5]);
1519
1520 let mut m2 = Matrix::new(0, 4, 3);
1522
1523 m2.par_row_iter_mut().enumerate().for_each(|(i, row)| {
1525 for (j, elem) in row.iter_mut().enumerate() {
1526 *elem = i + j;
1527 }
1528 });
1529 test_basic_indexing(&m2);
1530
1531 let mut m3 = Matrix::new(0, 4, 3);
1533 m3.par_window_iter_mut(10)
1534 .enumerate()
1535 .for_each(|(_, mut window)| {
1536 window.row_iter_mut().enumerate().for_each(|(i, row)| {
1537 for (j, elem) in row.iter_mut().enumerate() {
1538 *elem = i + j;
1539 }
1540 });
1541 });
1542 test_basic_indexing(&m3);
1543 }
1544
1545 #[test]
1546 fn test_matrix_pointers() {
1547 let mut m = Matrix::new(42, 3, 4);
1548
1549 let const_ptr = m.as_ptr();
1551 let mut_ptr = m.as_mut_ptr();
1552 assert_eq!(const_ptr, mut_ptr as *const _);
1553
1554 let view = m.as_view();
1556 assert_eq!(view.as_ptr(), const_ptr);
1557
1558 let mut mut_view = m.as_mut_view();
1559 assert_eq!(mut_view.as_ptr(), const_ptr);
1560 assert_eq!(mut_view.as_mut_ptr(), mut_ptr);
1561 }
1562
1563 #[test]
1564 fn test_matrix_iteration_empty_cases() {
1565 let empty_data: Vec<i32> = vec![];
1568
1569 let _empty_matrix = MatrixView::try_from(empty_data.as_slice(), 0, 5).unwrap();
1571
1572 let data = vec![1, 2, 3];
1574 let single_row = MatrixView::try_from(data.as_slice(), 1, 3).unwrap();
1575 let rows: Vec<_> = single_row.row_iter().collect();
1576 assert_eq!(rows.len(), 1);
1577 assert_eq!(rows[0], &[1, 2, 3]);
1578
1579 let data = vec![1, 2, 3];
1581 let single_col = MatrixView::try_from(data.as_slice(), 3, 1).unwrap();
1582 let rows: Vec<_> = single_col.row_iter().collect();
1583 assert_eq!(rows.len(), 3);
1584 assert_eq!(rows[0], &[1]);
1585 assert_eq!(rows[1], &[2]);
1586 assert_eq!(rows[2], &[3]);
1587 }
1588
1589 #[test]
1590 fn test_matrix_init_generator_various_types() {
1591 use std::sync::atomic::{AtomicUsize, Ordering};
1593
1594 let counter = AtomicUsize::new(0);
1595 let m = Matrix::new(Init(|| counter.fetch_add(1, Ordering::SeqCst)), 2, 3);
1596
1597 assert_eq!(m[(0, 0)], 0);
1599 assert_eq!(m[(0, 1)], 1);
1600 assert_eq!(m[(0, 2)], 2);
1601 assert_eq!(m[(1, 0)], 3);
1602 assert_eq!(m[(1, 1)], 4);
1603 assert_eq!(m[(1, 2)], 5);
1604 }
1605
1606 #[test]
1607 fn test_debug_error_formatting() {
1608 let data = vec![1, 2, 3];
1610 let err = Matrix::try_from(data.into(), 2, 3).unwrap_err();
1611
1612 let debug_str = format!("{:?}", err);
1613 assert!(debug_str.contains("TryFromError"));
1614 assert!(debug_str.contains("data_len: 3"));
1615 assert!(debug_str.contains("nrows: 2"));
1616 assert!(debug_str.contains("ncols: 3"));
1617
1618 #[derive(Clone, Debug)]
1620 struct NonDebug(#[allow(dead_code)] i32);
1621
1622 let non_debug_data: Box<[NonDebug]> = vec![NonDebug(1), NonDebug(2)].into();
1623 let non_debug_err = Matrix::try_from(non_debug_data, 1, 3).unwrap_err();
1624 let debug_str = format!("{:?}", non_debug_err);
1625 assert!(debug_str.contains("TryFromError"));
1626 }
1627
1628 #[test]
1631 #[cfg(feature = "rayon")]
1632 fn test_par_window_iter_comprehensive() {
1633 use rayon::prelude::*;
1634
1635 let data: Vec<usize> = (0..24).collect(); let m = MatrixView::try_from(data.as_slice(), 6, 4).unwrap();
1638
1639 for batchsize in 1..=8 {
1641 let context = lazy_format!("batchsize = {}", batchsize);
1642 let windows: Vec<_> = m.par_window_iter(batchsize).collect();
1643
1644 let expected_windows = (m.nrows()).div_ceil(batchsize);
1646 assert_eq!(windows.len(), expected_windows, "{}", context);
1647
1648 let mut total_rows_seen = 0;
1650 for (window_idx, window) in windows.iter().enumerate() {
1651 let expected_rows = if window_idx == windows.len() - 1 {
1652 m.nrows() - (windows.len() - 1) * batchsize
1654 } else {
1655 batchsize
1656 };
1657
1658 assert_eq!(
1659 window.nrows(),
1660 expected_rows,
1661 "window {} - {}",
1662 window_idx,
1663 context
1664 );
1665 assert_eq!(
1666 window.ncols(),
1667 m.ncols(),
1668 "window {} - {}",
1669 window_idx,
1670 context
1671 );
1672
1673 for (row_idx, row) in window.row_iter().enumerate() {
1675 let global_row = window_idx * batchsize + row_idx;
1676 let expected: Vec<usize> =
1677 (0..m.ncols()).map(|j| global_row * m.ncols() + j).collect();
1678 assert_eq!(
1679 row,
1680 expected.as_slice(),
1681 "window {}, row {} - {}",
1682 window_idx,
1683 row_idx,
1684 context
1685 );
1686 }
1687
1688 total_rows_seen += window.nrows();
1689 }
1690
1691 assert_eq!(total_rows_seen, m.nrows(), "{}", context);
1692 }
1693
1694 let windows: Vec<_> = m.par_window_iter(m.nrows()).collect();
1696 assert_eq!(windows.len(), 1);
1697 assert_eq!(windows[0].nrows(), m.nrows());
1698 assert_eq!(windows[0].ncols(), m.ncols());
1699
1700 let windows: Vec<_> = m.par_window_iter(m.nrows() * 2).collect();
1702 assert_eq!(windows.len(), 1);
1703 assert_eq!(windows[0].nrows(), m.nrows());
1704 assert_eq!(windows[0].ncols(), m.ncols());
1705 }
1706
1707 #[test]
1708 #[cfg(feature = "rayon")]
1709 fn test_par_window_iter_mut_comprehensive() {
1710 use rayon::prelude::*;
1711
1712 for nrows in [1, 2, 3, 5, 8, 10] {
1714 for ncols in [1, 3, 4] {
1715 for batchsize in [1, 2, 3, 7] {
1716 let context = lazy_format!("{}x{}, batchsize={}", nrows, ncols, batchsize);
1717
1718 let mut m = Matrix::new(0usize, nrows, ncols);
1719
1720 m.par_window_iter_mut(batchsize).enumerate().for_each(
1722 |(window_idx, mut window)| {
1723 let base_row = window_idx * batchsize;
1724 window
1725 .row_iter_mut()
1726 .enumerate()
1727 .for_each(|(row_offset, row)| {
1728 let global_row = base_row + row_offset;
1729 for (col, elem) in row.iter_mut().enumerate() {
1730 *elem = global_row * ncols + col;
1731 }
1732 });
1733 },
1734 );
1735
1736 for row in 0..nrows {
1738 for col in 0..ncols {
1739 let expected = row * ncols + col;
1740 assert_eq!(
1741 m[(row, col)],
1742 expected,
1743 "pos ({}, {}) - {}",
1744 row,
1745 col,
1746 context
1747 );
1748 }
1749 }
1750 }
1751 }
1752 }
1753 }
1754
1755 #[test]
1756 #[cfg(feature = "rayon")]
1757 fn test_par_row_iter_comprehensive() {
1758 use rayon::prelude::*;
1759
1760 let nrows = 7;
1762 let ncols = 5;
1763 let data: Vec<i32> = (0..(nrows * ncols) as i32).collect();
1764 let m = MatrixView::try_from(data.as_slice(), nrows, ncols).unwrap();
1765
1766 let collected_rows: Vec<Vec<i32>> = m.par_row_iter().map(|row| row.to_vec()).collect();
1768
1769 assert_eq!(collected_rows.len(), nrows);
1770
1771 for (row_idx, row) in collected_rows.iter().enumerate() {
1772 assert_eq!(row.len(), ncols);
1773 let expected: Vec<i32> = ((row_idx * ncols)..((row_idx + 1) * ncols))
1774 .map(|x| x as i32)
1775 .collect();
1776 assert_eq!(row, &expected, "row {} mismatch", row_idx);
1777 }
1778
1779 let enumerated_rows: Vec<(usize, Vec<i32>)> = m
1781 .par_row_iter()
1782 .enumerate()
1783 .map(|(idx, row)| (idx, row.to_vec()))
1784 .collect();
1785
1786 let mut sorted_rows = enumerated_rows;
1788 sorted_rows.sort_by_key(|(idx, _)| *idx);
1789
1790 assert_eq!(sorted_rows.len(), nrows);
1791 for (expected_idx, (actual_idx, row)) in sorted_rows.iter().enumerate() {
1792 assert_eq!(*actual_idx, expected_idx);
1793 assert_eq!(row.len(), ncols);
1794 }
1795
1796 let sum: i32 = m.par_row_iter().map(|row| row.iter().sum::<i32>()).sum();
1798
1799 let expected_sum: i32 = data.iter().sum();
1800 assert_eq!(sum, expected_sum);
1801
1802 let target_row = 3;
1804 let found_row = m
1805 .par_row_iter()
1806 .enumerate()
1807 .find_any(|(idx, _)| *idx == target_row)
1808 .map(|(_, row)| row.to_vec());
1809
1810 assert!(found_row.is_some());
1811 let expected_row: Vec<i32> = ((target_row * ncols)..((target_row + 1) * ncols))
1812 .map(|x| x as i32)
1813 .collect();
1814 assert_eq!(found_row.unwrap(), expected_row);
1815 }
1816
1817 #[test]
1818 #[cfg(feature = "rayon")]
1819 fn test_par_row_iter_mut_comprehensive() {
1820 use rayon::prelude::*;
1821 use std::sync::atomic::{AtomicUsize, Ordering};
1822
1823 let nrows = 6;
1824 let ncols = 4;
1825 let mut m = Matrix::new(0u32, nrows, ncols);
1826
1827 m.par_row_iter_mut().enumerate().for_each(|(row_idx, row)| {
1829 for (col_idx, elem) in row.iter_mut().enumerate() {
1830 *elem = (row_idx * ncols + col_idx) as u32;
1831 }
1832 });
1833
1834 for row in 0..nrows {
1836 for col in 0..ncols {
1837 let expected = (row * ncols + col) as u32;
1838 assert_eq!(m[(row, col)], expected, "pos ({}, {})", row, col);
1839 }
1840 }
1841
1842 let counter = AtomicUsize::new(0);
1844 m.par_row_iter_mut().for_each(|row| {
1845 counter.fetch_add(1, Ordering::Relaxed);
1846 for elem in row {
1848 *elem *= 2;
1849 }
1850 });
1851
1852 assert_eq!(counter.load(Ordering::Relaxed), nrows);
1853
1854 for row in 0..nrows {
1856 for col in 0..ncols {
1857 let expected = ((row * ncols + col) * 2) as u32;
1858 assert_eq!(m[(row, col)], expected, "doubled pos ({}, {})", row, col);
1859 }
1860 }
1861 }
1862
1863 #[test]
1864 #[cfg(feature = "rayon")]
1865 fn test_parallel_iterators_with_single_dimensions() {
1866 use rayon::prelude::*;
1867
1868 let data = vec![1, 2, 3, 4, 5];
1870 let single_row = MatrixView::try_from(data.as_slice(), 1, 5).unwrap();
1871
1872 let windows: Vec<_> = single_row.par_window_iter(1).collect();
1873 assert_eq!(windows.len(), 1);
1874 assert_eq!(windows[0].nrows(), 1);
1875 assert_eq!(windows[0].ncols(), 5);
1876
1877 let rows: Vec<_> = single_row.par_row_iter().collect();
1878 assert_eq!(rows.len(), 1);
1879 assert_eq!(rows[0], &[1, 2, 3, 4, 5]);
1880
1881 let data = vec![1, 2, 3, 4, 5];
1883 let single_col = MatrixView::try_from(data.as_slice(), 5, 1).unwrap();
1884
1885 let windows: Vec<_> = single_col.par_window_iter(2).collect();
1886 assert_eq!(windows.len(), 3); assert_eq!(windows[0].nrows(), 2);
1888 assert_eq!(windows[1].nrows(), 2);
1889 assert_eq!(windows[2].nrows(), 1); let rows: Vec<_> = single_col.par_row_iter().collect();
1892 assert_eq!(rows.len(), 5);
1893 for (i, row) in rows.iter().enumerate() {
1894 assert_eq!(row, &[i + 1]);
1895 }
1896
1897 let data = vec![42];
1899 let tiny = MatrixView::try_from(data.as_slice(), 1, 1).unwrap();
1900
1901 let windows: Vec<_> = tiny.par_window_iter(1).collect();
1902 assert_eq!(windows.len(), 1);
1903 assert_eq!(windows[0][(0, 0)], 42);
1904
1905 let rows: Vec<_> = tiny.par_row_iter().collect();
1906 assert_eq!(rows.len(), 1);
1907 assert_eq!(rows[0], &[42]);
1908 }
1909
1910 #[test]
1911 #[cfg(feature = "rayon")]
1912 fn test_parallel_window_properties() {
1913 use rayon::prelude::*;
1914
1915 let data: Vec<usize> = (0..30).collect();
1917 let m = MatrixView::try_from(data.as_slice(), 6, 5).unwrap();
1918
1919 m.par_window_iter(2)
1921 .enumerate()
1922 .for_each(|(window_idx, window)| {
1923 for row_idx in 0..window.nrows() {
1924 for col_idx in 0..window.ncols() {
1925 let global_row = window_idx * 2 + row_idx;
1926 let expected = global_row * 5 + col_idx;
1927 assert_eq!(
1928 window[(row_idx, col_idx)],
1929 expected,
1930 "window {}, pos ({}, {})",
1931 window_idx,
1932 row_idx,
1933 col_idx
1934 );
1935 }
1936 }
1937 });
1938
1939 m.par_window_iter(3)
1941 .enumerate()
1942 .for_each(|(window_idx, window)| {
1943 let slice = window.as_slice();
1944 assert_eq!(slice.len(), window.nrows() * window.ncols());
1945
1946 for (slice_idx, &value) in slice.iter().enumerate() {
1947 let row = slice_idx / window.ncols();
1948 let col = slice_idx % window.ncols();
1949 assert_eq!(
1950 value,
1951 window[(row, col)],
1952 "window {}, slice_idx {}",
1953 window_idx,
1954 slice_idx
1955 );
1956 }
1957 });
1958
1959 m.par_window_iter(2).for_each(|window| {
1961 let rows_via_iter: Vec<_> = window.row_iter().collect();
1962 assert_eq!(rows_via_iter.len(), window.nrows());
1963
1964 for (row_idx, row) in rows_via_iter.iter().enumerate() {
1965 assert_eq!(row.len(), window.ncols());
1966 for (col_idx, &value) in row.iter().enumerate() {
1967 assert_eq!(value, window[(row_idx, col_idx)]);
1968 }
1969 }
1970 });
1971 }
1972
1973 #[test]
1974 #[cfg(feature = "rayon")]
1975 fn test_parallel_performance_characteristics() {
1976 use rayon::prelude::*;
1977 use std::sync::atomic::{AtomicUsize, Ordering};
1978
1979 let nrows = 100;
1981 let ncols = 10;
1982 let mut m = Matrix::new(0usize, nrows, ncols);
1983
1984 let work_counter = AtomicUsize::new(0);
1986
1987 m.par_window_iter_mut(10)
1988 .enumerate()
1989 .for_each(|(window_idx, mut window)| {
1990 work_counter.fetch_add(1, Ordering::Relaxed);
1991
1992 window
1994 .row_iter_mut()
1995 .enumerate()
1996 .for_each(|(row_offset, row)| {
1997 let global_row = window_idx * 10 + row_offset;
1998 for (col, elem) in row.iter_mut().enumerate() {
1999 *elem = global_row * ncols + col;
2000 }
2001 });
2002 });
2003
2004 assert_eq!(work_counter.load(Ordering::Relaxed), 10);
2006
2007 for row in 0..nrows {
2009 for col in 0..ncols {
2010 assert_eq!(m[(row, col)], row * ncols + col);
2011 }
2012 }
2013
2014 let total_sum: usize = m
2016 .par_window_iter(15)
2017 .map(|window| {
2018 window
2019 .row_iter()
2020 .map(|row| row.iter().sum::<usize>())
2021 .sum::<usize>()
2022 })
2023 .sum();
2024
2025 let expected_sum: usize = (0..(nrows * ncols)).sum();
2026 assert_eq!(total_sum, expected_sum);
2027 }
2028
2029 #[test]
2030 #[cfg(feature = "rayon")]
2031 fn test_rayon_trait_bounds_validation() {
2032 use rayon::prelude::*;
2033
2034 let data: Vec<u64> = (0..20).collect();
2036 let m = MatrixView::try_from(data.as_slice(), 4, 5).unwrap();
2037
2038 let _: Vec<_> = m.par_window_iter(2).collect();
2040 let _: Vec<_> = m.par_row_iter().collect();
2041
2042 let mut m = Matrix::new(0u64, 4, 5);
2044
2045 m.par_window_iter_mut(2).for_each(|mut window| {
2047 window.row_iter_mut().for_each(|row| {
2048 for elem in row {
2049 *elem = 42;
2050 }
2051 });
2052 });
2053
2054 m.par_row_iter_mut().for_each(|row| {
2055 for elem in row {
2056 *elem += 1;
2057 }
2058 });
2059
2060 assert!(m.as_slice().iter().all(|&x| x == 43));
2062 }
2063}