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 as_ptr(&self) -> *const T::Elem {
511 self.as_slice().as_ptr()
512 }
513
514 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 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 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
564pub type Matrix<T> = MatrixBase<Box<[T]>>;
567
568pub type MatrixView<'a, T> = MatrixBase<&'a [T]>;
575
576pub type MutMatrixView<'a, T> = MatrixBase<&'a mut [T]>;
583
584impl<'a, T> From<MatrixView<'a, T>> for &'a [T] {
586 fn from(view: MatrixView<'a, T>) -> Self {
587 view.data
588 }
589}
590
591impl<'a, T> From<MutMatrixView<'a, T>> for &'a [T] {
593 fn from(view: MutMatrixView<'a, T>) -> Self {
594 view.data
595 }
596}
597
598impl<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 unsafe { self.get_unchecked(row, col) }
623 }
624}
625
626impl<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 unsafe { self.get_unchecked_mut(row, col) }
649 }
650}
651
652#[cfg(test)]
657mod tests {
658 use super::*;
659 use crate::lazy_format;
660
661 fn is_copyable<T: Copy>(_x: T) -> bool {
665 true
666 }
667
668 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 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 #[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 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 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 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 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 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 assert_eq!(submatrix[(0, 0)], 3);
824 assert_eq!(submatrix[(0, 1)], 4);
825 assert_eq!(submatrix[(0, 2)], 5);
826 }
827 });
828
829 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 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 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 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 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 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 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 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 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 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 for i in 0..view.nrows() {
998 for j in 0..view.ncols() {
999 view[(i, j)] = i + j;
1000 }
1001 }
1002
1003 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 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 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 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 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 #[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 #[test]
1177 fn test_box_slice_dense_data_impls() {
1178 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_dense_data_repr(ptr, len, data, &lazy_format!("Box<[T]> DenseData"));
1185
1186 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 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 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 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 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 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 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 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 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 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 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 assert_ne!(owned.as_ptr(), view.as_ptr());
1286
1287 test_basic_indexing(&owned);
1289 }
1290
1291 #[test]
1292 fn test_generator_trait_impls() {
1293 let mut gen = 42i32;
1295 assert_eq!(gen.generate(), 42);
1296 assert_eq!(gen.generate(), 42); 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 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 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 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 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 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 let data = vec![10, 20];
1356
1357 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 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 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 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 let mut m2 = Matrix::new(0, 4, 3);
1395
1396 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 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 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 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 let empty_data: Vec<i32> = vec![];
1441
1442 let _empty_matrix = MatrixView::try_from(empty_data.as_slice(), 0, 5).unwrap();
1444
1445 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 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 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 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 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 #[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 #[test]
1504 #[cfg(feature = "rayon")]
1505 fn test_par_window_iter_comprehensive() {
1506 use rayon::prelude::*;
1507
1508 let data: Vec<usize> = (0..24).collect(); let m = MatrixView::try_from(data.as_slice(), 6, 4).unwrap();
1511
1512 for batchsize in 1..=8 {
1514 let context = lazy_format!("batchsize = {}", batchsize);
1515 let windows: Vec<_> = m.par_window_iter(batchsize).collect();
1516
1517 let expected_windows = (m.nrows()).div_ceil(batchsize);
1519 assert_eq!(windows.len(), expected_windows, "{}", context);
1520
1521 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let counter = AtomicUsize::new(0);
1717 m.par_row_iter_mut().for_each(|row| {
1718 counter.fetch_add(1, Ordering::Relaxed);
1719 for elem in row {
1721 *elem *= 2;
1722 }
1723 });
1724
1725 assert_eq!(counter.load(Ordering::Relaxed), nrows);
1726
1727 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 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 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); assert_eq!(windows[0].nrows(), 2);
1761 assert_eq!(windows[1].nrows(), 2);
1762 assert_eq!(windows[2].nrows(), 1); 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 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 let data: Vec<usize> = (0..30).collect();
1790 let m = MatrixView::try_from(data.as_slice(), 6, 5).unwrap();
1791
1792 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 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 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 let nrows = 100;
1854 let ncols = 10;
1855 let mut m = Matrix::new(0usize, nrows, ncols);
1856
1857 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 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 assert_eq!(work_counter.load(Ordering::Relaxed), 10);
1879
1880 for row in 0..nrows {
1882 for col in 0..ncols {
1883 assert_eq!(m[(row, col)], row * ncols + col);
1884 }
1885 }
1886
1887 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 let data: Vec<u64> = (0..20).collect();
1909 let m = MatrixView::try_from(data.as_slice(), 4, 5).unwrap();
1910
1911 let _: Vec<_> = m.par_window_iter(2).collect();
1913 let _: Vec<_> = m.par_row_iter().collect();
1914
1915 let mut m = Matrix::new(0u64, 4, 5);
1917
1918 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 assert!(m.as_slice().iter().all(|&x| x == 43));
1935 }
1936}