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 map<F, R>(&self, f: F) -> Matrix<R>
235 where
236 F: FnMut(&T::Elem) -> R,
237 {
238 let data: Box<[_]> = self.as_slice().iter().map(f).collect();
239 Matrix {
240 data,
241 nrows: self.nrows(),
242 ncols: self.ncols(),
243 }
244 }
245
246 pub fn as_slice(&self) -> &[T::Elem] {
248 self.data.as_slice()
249 }
250
251 pub fn as_mut_slice(&mut self) -> &mut [T::Elem]
253 where
254 T: MutDenseData,
255 {
256 self.data.as_mut_slice()
257 }
258
259 pub fn row(&self, row: usize) -> &[T::Elem] {
265 assert!(
266 row < self.nrows(),
267 "tried to access row {row} of a matrix with {} rows",
268 self.nrows()
269 );
270
271 unsafe { self.get_row_unchecked(row) }
273 }
274
275 pub fn row_vector(data: T) -> Self {
279 let ncols = data.as_slice().len();
280 Self {
281 data,
282 nrows: 1,
283 ncols,
284 }
285 }
286
287 pub fn column_vector(data: T) -> Self {
291 let nrows = data.as_slice().len();
292 Self {
293 data,
294 nrows,
295 ncols: 1,
296 }
297 }
298
299 pub fn get_row(&self, row: usize) -> Option<&[T::Elem]> {
301 if row < self.nrows() {
302 Some(unsafe { self.get_row_unchecked(row) })
304 } else {
305 None
306 }
307 }
308
309 pub unsafe fn get_row_unchecked(&self, row: usize) -> &[T::Elem] {
316 debug_assert!(row < self.nrows);
317 let ncols = self.ncols;
318 let start = row * ncols;
319
320 debug_assert!(start + ncols <= self.as_slice().len());
321 unsafe { self.as_slice().get_unchecked(start..start + ncols) }
326 }
327
328 pub fn row_mut(&mut self, row: usize) -> &mut [T::Elem]
334 where
335 T: MutDenseData,
336 {
337 assert!(
338 row < self.nrows(),
339 "tried to access row {row} of a matrix with {} rows",
340 self.nrows()
341 );
342
343 unsafe { self.get_row_unchecked_mut(row) }
345 }
346
347 pub unsafe fn get_row_unchecked_mut(&mut self, row: usize) -> &mut [T::Elem]
354 where
355 T: MutDenseData,
356 {
357 debug_assert!(row < self.nrows);
358 let ncols = self.ncols;
359 let start = row * ncols;
360
361 debug_assert!(start + ncols <= self.as_slice().len());
362 unsafe {
367 self.data
368 .as_mut_slice()
369 .get_unchecked_mut(start..start + ncols)
370 }
371 }
372
373 pub fn row_iter(&self) -> impl ExactSizeIterator<Item = &[T::Elem]> {
377 self.data.as_slice().chunks_exact(self.ncols())
378 }
379
380 pub fn row_iter_mut(&mut self) -> impl ExactSizeIterator<Item = &mut [T::Elem]>
384 where
385 T: MutDenseData,
386 {
387 let ncols = self.ncols();
388 self.data.as_mut_slice().chunks_exact_mut(ncols)
389 }
390
391 pub fn window_iter(&self, batchsize: usize) -> impl Iterator<Item = MatrixView<'_, T::Elem>>
401 where
402 T::Elem: Sync,
403 {
404 assert!(batchsize != 0, "window_iter batchsize cannot be zero");
405 let ncols = self.ncols();
406 self.data
407 .as_slice()
408 .chunks(ncols * batchsize)
409 .map(move |data| {
410 let blobsize = data.len();
411 let nrows = blobsize / ncols;
412 assert_eq!(blobsize % ncols, 0);
413 MatrixView { data, nrows, ncols }
414 })
415 }
416
417 #[cfg(feature = "rayon")]
430 pub fn par_window_iter(
431 &self,
432 batchsize: usize,
433 ) -> impl IndexedParallelIterator<Item = MatrixView<'_, T::Elem>>
434 where
435 T::Elem: Sync,
436 {
437 assert!(batchsize != 0, "par_window_iter batchsize cannot be zero");
438 let ncols = self.ncols();
439 self.data
440 .as_slice()
441 .par_chunks(ncols * batchsize)
442 .map(move |data| {
443 let blobsize = data.len();
444 let nrows = blobsize / ncols;
445 assert_eq!(blobsize % ncols, 0);
446 MatrixView { data, nrows, ncols }
447 })
448 }
449
450 #[cfg(feature = "rayon")]
463 pub fn par_window_iter_mut(
464 &mut self,
465 batchsize: usize,
466 ) -> impl IndexedParallelIterator<Item = MutMatrixView<'_, T::Elem>>
467 where
468 T: MutDenseData,
469 T::Elem: Send,
470 {
471 assert!(
472 batchsize != 0,
473 "par_window_iter_mut batchsize cannot be zero"
474 );
475 let ncols = self.ncols();
476 self.data
477 .as_mut_slice()
478 .par_chunks_mut(ncols * batchsize)
479 .map(move |data| {
480 let blobsize = data.len();
481 let nrows = blobsize / ncols;
482 assert_eq!(blobsize % ncols, 0);
483 MutMatrixView { data, nrows, ncols }
484 })
485 }
486
487 #[cfg(feature = "rayon")]
489 pub fn par_row_iter(&self) -> impl IndexedParallelIterator<Item = &[T::Elem]>
490 where
491 T::Elem: Sync,
492 {
493 self.as_slice().par_chunks_exact(self.ncols())
494 }
495
496 #[cfg(feature = "rayon")]
498 pub fn par_row_iter_mut(&mut self) -> impl IndexedParallelIterator<Item = &mut [T::Elem]>
499 where
500 T: MutDenseData,
501 T::Elem: Send,
502 {
503 let ncols = self.ncols();
504 self.as_mut_slice().par_chunks_exact_mut(ncols)
505 }
506
507 pub fn into_inner(self) -> T {
511 self.data
512 }
513
514 pub fn as_view(&self) -> MatrixView<'_, T::Elem> {
516 MatrixBase {
517 data: self.as_slice(),
518 nrows: self.nrows(),
519 ncols: self.ncols(),
520 }
521 }
522
523 pub fn as_mut_view(&mut self) -> MutMatrixView<'_, T::Elem>
525 where
526 T: MutDenseData,
527 {
528 let nrows = self.nrows();
529 let ncols = self.ncols();
530 MatrixBase {
531 data: self.as_mut_slice(),
532 nrows,
533 ncols,
534 }
535 }
536
537 pub fn subview(&self, rows: std::ops::Range<usize>) -> Option<MatrixView<'_, T::Elem>> {
559 let ncols = self.ncols();
560
561 let lower = rows.start.checked_mul(ncols)?;
562 let upper = rows.end.checked_mul(ncols)?;
563
564 if let Some(data) = self.as_slice().get(lower..upper) {
565 Some(MatrixBase {
566 data,
567 nrows: rows.len(),
568 ncols: self.ncols(),
569 })
570 } else {
571 None
572 }
573 }
574
575 pub fn as_ptr(&self) -> *const T::Elem {
577 self.as_slice().as_ptr()
578 }
579
580 pub fn as_mut_ptr(&mut self) -> *mut T::Elem
582 where
583 T: MutDenseData,
584 {
585 self.as_mut_slice().as_mut_ptr()
586 }
587
588 pub fn try_get(&self, row: usize, col: usize) -> Option<&T::Elem> {
592 if row >= self.nrows() || col >= self.ncols() {
593 None
594 } else {
595 Some(unsafe { self.get_unchecked(row, col) })
597 }
598 }
599
600 pub unsafe fn get_unchecked(&self, row: usize, col: usize) -> &T::Elem {
608 debug_assert!(row < self.nrows);
609 debug_assert!(col < self.ncols);
610 self.as_slice().get_unchecked(row * self.ncols + col)
611 }
612
613 pub unsafe fn get_unchecked_mut(&mut self, row: usize, col: usize) -> &mut T::Elem
621 where
622 T: MutDenseData,
623 {
624 let ncols = self.ncols;
625 debug_assert!(row < self.nrows);
626 debug_assert!(col < self.ncols);
627 self.as_mut_slice().get_unchecked_mut(row * ncols + col)
628 }
629
630 pub fn to_owned(&self) -> Matrix<T::Elem>
631 where
632 T::Elem: Clone,
633 {
634 Matrix {
635 data: self.data.as_slice().into(),
636 nrows: self.nrows,
637 ncols: self.ncols,
638 }
639 }
640}
641
642pub type Matrix<T> = MatrixBase<Box<[T]>>;
645
646pub type MatrixView<'a, T> = MatrixBase<&'a [T]>;
653
654pub type MutMatrixView<'a, T> = MatrixBase<&'a mut [T]>;
661
662impl<'a, T> From<MatrixView<'a, T>> for &'a [T] {
664 fn from(view: MatrixView<'a, T>) -> Self {
665 view.data
666 }
667}
668
669impl<'a, T> From<MutMatrixView<'a, T>> for &'a [T] {
671 fn from(view: MutMatrixView<'a, T>) -> Self {
672 view.data
673 }
674}
675
676impl<T> Index<(usize, usize)> for MatrixBase<T>
682where
683 T: DenseData,
684{
685 type Output = T::Elem;
686
687 fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
688 assert!(
689 row < self.nrows(),
690 "row {row} is out of bounds (max: {})",
691 self.nrows()
692 );
693 assert!(
694 col < self.ncols(),
695 "col {col} is out of bounds (max: {})",
696 self.ncols()
697 );
698
699 unsafe { self.get_unchecked(row, col) }
701 }
702}
703
704impl<T> IndexMut<(usize, usize)> for MatrixBase<T>
710where
711 T: MutDenseData,
712{
713 fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
714 assert!(
715 row < self.nrows(),
716 "row {row} is out of bounds (max: {})",
717 self.nrows()
718 );
719 assert!(
720 col < self.ncols(),
721 "col {col} is out of bounds (max: {})",
722 self.ncols()
723 );
724
725 unsafe { self.get_unchecked_mut(row, col) }
727 }
728}
729
730#[cfg(test)]
735mod tests {
736 use super::*;
737 use crate::lazy_format;
738
739 fn is_copyable<T: Copy>(_x: T) -> bool {
743 true
744 }
745
746 fn test_dense_data_repr<T, Repr>(
749 ptr: *const T,
750 len: usize,
751 repr: Repr,
752 context: &dyn std::fmt::Display,
753 ) where
754 T: Copy,
755 Repr: DenseData<Elem = T>,
756 {
757 let retrieved = repr.as_slice();
758 assert_eq!(retrieved.len(), len, "{}", context);
759 assert_eq!(retrieved.as_ptr(), ptr, "{}", context);
760 }
761
762 fn set_mut_dense_data_repr<T, Repr>(repr: &mut Repr, base: T, increment: T)
766 where
767 T: Copy + std::ops::Add<Output = T>,
768 Repr: DenseData<Elem = T> + MutDenseData,
769 {
770 let slice = repr.as_mut_slice();
771 for i in 0..slice.len() {
772 if i == 0 {
773 slice[i] = base;
774 } else {
775 slice[i] = slice[i - 1] + increment;
776 }
777 }
778 }
779
780 #[test]
781 fn slice_implements_dense_data_repr() {
782 for len in 0..10 {
783 let context = lazy_format!("len = {}", len);
784 let data: Vec<f32> = vec![0.0; len];
785 let slice = data.as_slice();
786 test_dense_data_repr(slice.as_ptr(), slice.len(), slice, &context);
787 }
788 }
789
790 #[test]
791 fn mut_slice_mplements_dense_data_repr() {
792 for len in 0..10 {
793 let context = lazy_format!("len = {}", len);
794 let mut data: Vec<f32> = vec![0.0; len];
795 let slice = data.as_mut_slice();
796
797 let ptr = slice.as_ptr();
798 let len = slice.len();
799 test_dense_data_repr(ptr, len, slice, &context);
800 }
801 }
802
803 #[test]
804 fn mut_slice_implements_mut_dense_data_repr() {
805 for len in 0..10 {
806 let context = lazy_format!("len = {}", len);
807 let mut data: Vec<f32> = vec![0.0; len];
808 let mut slice = data.as_mut_slice();
809
810 let base = 2.0;
811 let increment = 1.0;
812 set_mut_dense_data_repr(&mut slice, base, increment);
813
814 for (i, &v) in slice.iter().enumerate() {
815 let context = lazy_format!("entry {}, {}", i, context);
816 assert_eq!(v, base + increment * (i as f32), "{}", context);
817 }
818 }
819 }
820
821 #[test]
826 fn try_from_error_misc() {
827 let x = TryFromError::<&[f32]> {
828 data: &[],
829 nrows: 1,
830 ncols: 2,
831 };
832
833 let debug = format!("{:?}", x);
834 println!("debug = {}", debug);
835 assert!(debug.contains("TryFromError"));
836 assert!(debug.contains("data_len: 0"));
837 assert!(debug.contains("nrows: 1"));
838 assert!(debug.contains("ncols: 2"));
839 }
840
841 fn make_test_matrix() -> Vec<usize> {
842 vec![0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]
851 }
852
853 #[cfg(feature = "rayon")]
854 fn test_basic_indexing_parallel(m: MatrixView<'_, usize>) {
855 let batchsize = 2;
857 m.par_window_iter(batchsize)
858 .enumerate()
859 .for_each(|(i, submatrix)| {
860 assert_eq!(submatrix.nrows(), batchsize);
861 assert_eq!(submatrix.ncols(), m.ncols());
862
863 let base = i * batchsize;
865 assert_eq!(submatrix[(0, 0)], base);
866 assert_eq!(submatrix[(0, 1)], base + 1);
867 assert_eq!(submatrix[(0, 2)], base + 2);
868
869 assert_eq!(submatrix[(1, 0)], base + 1);
870 assert_eq!(submatrix[(1, 1)], base + 2);
871 assert_eq!(submatrix[(1, 2)], base + 3);
872 });
873
874 let batchsize = 3;
877 m.par_window_iter(batchsize)
878 .enumerate()
879 .for_each(|(i, submatrix)| {
880 if i == 0 {
881 assert_eq!(submatrix.nrows(), batchsize);
882 assert_eq!(submatrix.ncols(), m.ncols());
883
884 assert_eq!(submatrix[(0, 0)], 0);
886 assert_eq!(submatrix[(0, 1)], 1);
887 assert_eq!(submatrix[(0, 2)], 2);
888
889 assert_eq!(submatrix[(1, 0)], 1);
890 assert_eq!(submatrix[(1, 1)], 2);
891 assert_eq!(submatrix[(1, 2)], 3);
892
893 assert_eq!(submatrix[(2, 0)], 2);
894 assert_eq!(submatrix[(2, 1)], 3);
895 assert_eq!(submatrix[(2, 2)], 4);
896 } else {
897 assert_eq!(submatrix.nrows(), 1);
898 assert_eq!(submatrix.ncols(), m.ncols());
899
900 assert_eq!(submatrix[(0, 0)], 3);
902 assert_eq!(submatrix[(0, 1)], 4);
903 assert_eq!(submatrix[(0, 2)], 5);
904 }
905 });
906
907 let seen_rows: Box<[usize]> = m
909 .par_row_iter()
910 .enumerate()
911 .map(|(i, row)| {
912 let expected: Box<[usize]> = (0..m.ncols()).map(|j| j + i).collect();
913 assert_eq!(row, &*expected);
914 i
915 })
916 .collect();
917
918 let expected: Box<[usize]> = (0..m.nrows()).collect();
919 assert_eq!(seen_rows, expected);
920 }
921
922 fn test_basic_indexing<T>(m: &MatrixBase<T>)
923 where
924 T: DenseData<Elem = usize> + Sync,
925 {
926 assert_eq!(m.nrows(), 4);
927 assert_eq!(m.ncols(), 3);
928
929 assert_eq!(m[(0, 0)], 0);
931 assert_eq!(m[(0, 1)], 1);
932 assert_eq!(m[(0, 2)], 2);
933
934 assert_eq!(m[(1, 0)], 1);
935 assert_eq!(m[(1, 1)], 2);
936 assert_eq!(m[(1, 2)], 3);
937
938 assert_eq!(m[(2, 0)], 2);
939 assert_eq!(m[(2, 1)], 3);
940 assert_eq!(m[(2, 2)], 4);
941
942 assert_eq!(m[(3, 0)], 3);
943 assert_eq!(m[(3, 1)], 4);
944 assert_eq!(m[(3, 2)], 5);
945
946 assert_eq!(m.row(0), &[0, 1, 2]);
948 assert_eq!(m.row(1), &[1, 2, 3]);
949 assert_eq!(m.row(2), &[2, 3, 4]);
950 assert_eq!(m.row(3), &[3, 4, 5]);
951
952 let rows: Vec<Vec<usize>> = m.row_iter().map(|x| x.to_vec()).collect();
953 assert_eq!(m.row(0), &rows[0]);
954 assert_eq!(m.row(1), &rows[1]);
955 assert_eq!(m.row(2), &rows[2]);
956 assert_eq!(m.row(3), &rows[3]);
957
958 let batchsize = 2;
960 m.window_iter(batchsize)
961 .enumerate()
962 .for_each(|(i, submatrix)| {
963 assert_eq!(submatrix.nrows(), batchsize);
964 assert_eq!(submatrix.ncols(), m.ncols());
965
966 let base = i * batchsize;
968 assert_eq!(submatrix[(0, 0)], base);
969 assert_eq!(submatrix[(0, 1)], base + 1);
970 assert_eq!(submatrix[(0, 2)], base + 2);
971
972 assert_eq!(submatrix[(1, 0)], base + 1);
973 assert_eq!(submatrix[(1, 1)], base + 2);
974 assert_eq!(submatrix[(1, 2)], base + 3);
975 });
976
977 let batchsize = 3;
980 m.window_iter(batchsize)
981 .enumerate()
982 .for_each(|(i, submatrix)| {
983 if i == 0 {
984 assert_eq!(submatrix.nrows(), batchsize);
985 assert_eq!(submatrix.ncols(), m.ncols());
986
987 assert_eq!(submatrix[(0, 0)], 0);
989 assert_eq!(submatrix[(0, 1)], 1);
990 assert_eq!(submatrix[(0, 2)], 2);
991
992 assert_eq!(submatrix[(1, 0)], 1);
993 assert_eq!(submatrix[(1, 1)], 2);
994 assert_eq!(submatrix[(1, 2)], 3);
995
996 assert_eq!(submatrix[(2, 0)], 2);
997 assert_eq!(submatrix[(2, 1)], 3);
998 assert_eq!(submatrix[(2, 2)], 4);
999 } else {
1000 assert_eq!(submatrix.nrows(), 1);
1001 assert_eq!(submatrix.ncols(), m.ncols());
1002
1003 assert_eq!(submatrix[(0, 0)], 3);
1005 assert_eq!(submatrix[(0, 1)], 4);
1006 assert_eq!(submatrix[(0, 2)], 5);
1007 }
1008 });
1009
1010 #[cfg(all(not(miri), feature = "rayon"))]
1011 test_basic_indexing_parallel(m.as_view());
1012 }
1013
1014 #[test]
1015 fn matrix_happy_path() {
1016 let data = make_test_matrix();
1017 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1018 test_basic_indexing(&m);
1019
1020 let ptr = m.as_ptr();
1023 let view = m.as_view();
1024 assert!(is_copyable(view));
1025 assert_eq!(view.as_ptr(), ptr);
1026 assert_eq!(view.nrows(), m.nrows());
1027 assert_eq!(view.ncols(), m.ncols());
1028 test_basic_indexing(&view);
1029 }
1030
1031 #[test]
1032 fn matrix_try_from_construction_error() {
1033 let data = make_test_matrix();
1034 let ptr = data.as_ptr();
1035 let len = data.len();
1036
1037 let m = Matrix::try_from(data.into(), 5, 4);
1038 assert!(m.is_err());
1039 let err = m.unwrap_err();
1040 assert_eq!(
1041 err.to_string(),
1042 "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1043 );
1044
1045 let data = err.into_inner();
1047 assert_eq!(data.as_ptr(), ptr);
1048 assert_eq!(data.len(), len);
1049
1050 let m = MatrixView::try_from(&data, 5, 4);
1051 assert!(m.is_err());
1052 assert_eq!(
1053 m.unwrap_err().to_string(),
1054 "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1055 );
1056 }
1057
1058 #[test]
1059 fn matrix_mut_view() {
1060 let mut m = Matrix::<usize>::new(0, 4, 3);
1061 assert_eq!(m.nrows(), 4);
1062 assert_eq!(m.ncols(), 3);
1063 assert!(m.as_slice().iter().all(|&i| i == 0));
1064 let ptr = m.as_ptr();
1065 let mut_ptr = m.as_mut_ptr();
1066 assert_eq!(ptr, mut_ptr);
1067
1068 let mut view = m.as_mut_view();
1069 assert_eq!(view.nrows(), 4);
1070 assert_eq!(view.ncols(), 3);
1071 assert_eq!(view.as_ptr(), ptr);
1072 assert_eq!(view.as_mut_ptr(), mut_ptr);
1073
1074 for i in 0..view.nrows() {
1076 for j in 0..view.ncols() {
1077 view[(i, j)] = i + j;
1078 }
1079 }
1080
1081 test_basic_indexing(&m);
1083
1084 let inner = m.into_inner();
1085 assert_eq!(inner.as_ptr(), ptr);
1086 assert_eq!(inner.len(), 4 * 3);
1087 }
1088
1089 #[test]
1090 fn matrix_view_zero_sizes() {
1091 let data: Vec<usize> = vec![];
1092 let m = MatrixView::try_from(data.as_slice(), 0, 10).unwrap();
1094 assert_eq!(m.nrows(), 0);
1095 assert_eq!(m.ncols(), 10);
1096
1097 let m = MatrixView::try_from(data.as_slice(), 3, 0).unwrap();
1099 assert_eq!(m.nrows(), 3);
1100 assert_eq!(m.ncols(), 0);
1101 let empty: &[usize] = &[];
1102 assert_eq!(m.row(0), empty);
1103 assert_eq!(m.row(1), empty);
1104 assert_eq!(m.row(2), empty);
1105
1106 let m = MatrixView::try_from(data.as_slice(), 0, 0).unwrap();
1108 assert_eq!(m.nrows(), 0);
1109 assert_eq!(m.ncols(), 0);
1110 }
1111
1112 #[test]
1113 fn matrix_view_construction_elementwise() {
1114 let mut m = Matrix::<usize>::new(0, 4, 3);
1115
1116 for i in 0..m.nrows() {
1118 for j in 0..m.ncols() {
1119 m[(i, j)] = i + j;
1120 }
1121 }
1122 test_basic_indexing(&m);
1123 }
1124
1125 #[test]
1126 fn matrix_construction_by_row() {
1127 let mut m = Matrix::<usize>::new(0, 4, 3);
1128 assert!(m.as_slice().iter().all(|i| *i == 0));
1129
1130 let ncols = m.ncols();
1131 for i in 0..m.nrows() {
1132 let row = m.row_mut(i);
1133 assert_eq!(row.len(), ncols);
1134 row[0] = i;
1135 row[1] = i + 1;
1136 row[2] = i + 2;
1137 }
1138 test_basic_indexing(&m);
1139 }
1140
1141 #[test]
1142 fn matrix_construction_by_rowiter() {
1143 let mut m = Matrix::<usize>::new(0, 4, 3);
1144 assert!(m.as_slice().iter().all(|i| *i == 0));
1145
1146 let ncols = m.ncols();
1147 m.row_iter_mut().enumerate().for_each(|(i, row)| {
1148 assert_eq!(row.len(), ncols);
1149 row[0] = i;
1150 row[1] = i + 1;
1151 row[2] = i + 2;
1152 });
1153 test_basic_indexing(&m);
1154 }
1155
1156 #[cfg(all(not(miri), feature = "rayon"))]
1157 #[test]
1158 fn matrix_construction_by_par_windows() {
1159 let mut m = Matrix::<usize>::new(0, 4, 3);
1160 assert!(m.as_slice().iter().all(|i| *i == 0));
1161
1162 let ncols = m.ncols();
1163 for batchsize in 1..=4 {
1164 m.par_window_iter_mut(batchsize)
1165 .enumerate()
1166 .for_each(|(i, mut submatrix)| {
1167 let base = i * batchsize;
1168 submatrix.row_iter_mut().enumerate().for_each(|(j, row)| {
1169 assert_eq!(row.len(), ncols);
1170 row[0] = base + j;
1171 row[1] = base + j + 1;
1172 row[2] = base + j + 2;
1173 });
1174 });
1175 test_basic_indexing(&m);
1176 }
1177 }
1178
1179 #[test]
1180 fn matrix_construction_happens_in_memory_order() {
1181 let mut i = 0;
1182 let ncols = 3;
1183 let initializer = Init(|| {
1184 let value = (i % ncols) + (i / ncols);
1185 i += 1;
1186 value
1187 });
1188
1189 let m = Matrix::new(initializer, 4, 3);
1190 test_basic_indexing(&m);
1191 }
1192
1193 #[test]
1195 #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1196 fn test_get_row_panics() {
1197 let m = Matrix::<usize>::new(0, 3, 7);
1198 m.row(3);
1199 }
1200
1201 #[test]
1202 #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1203 fn test_get_row_mut_panics() {
1204 let mut m = Matrix::<usize>::new(0, 3, 7);
1205 m.row_mut(3);
1206 }
1207
1208 #[test]
1209 #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1210 fn test_index_panics_row() {
1211 let m = Matrix::<usize>::new(0, 3, 7);
1212 assert!(m.try_get(3, 2).is_none());
1213 let _ = m[(3, 2)];
1214 }
1215
1216 #[test]
1217 #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1218 fn test_index_panics_col() {
1219 let m = Matrix::<usize>::new(0, 3, 7);
1220 assert!(m.try_get(2, 7).is_none());
1221 let _ = m[(2, 7)];
1222 }
1223
1224 #[test]
1225 #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1226 fn test_index_mut_panics_row() {
1227 let mut m = Matrix::<usize>::new(0, 3, 7);
1228 m[(3, 2)] = 1;
1229 }
1230
1231 #[test]
1232 #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1233 fn test_index_mut_panics_col() {
1234 let mut m = Matrix::<usize>::new(0, 3, 7);
1235 m[(2, 7)] = 1;
1236 }
1237
1238 #[test]
1239 #[cfg(feature = "rayon")]
1240 #[should_panic(expected = "par_window_iter batchsize cannot be zero")]
1241 fn test_par_window_iter_panics() {
1242 let m = Matrix::<usize>::new(0, 4, 4);
1243 let _ = m.par_window_iter(0);
1244 }
1245
1246 #[test]
1247 #[cfg(feature = "rayon")]
1248 #[should_panic(expected = "par_window_iter_mut batchsize cannot be zero")]
1249 fn test_par_window_iter_mut_panics() {
1250 let mut m = Matrix::<usize>::new(0, 4, 4);
1251 let _ = m.par_window_iter_mut(0);
1252 }
1253
1254 #[test]
1257 fn test_box_slice_dense_data_impls() {
1258 let data: Box<[f32]> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0].into();
1260 let ptr = data.as_ptr();
1261 let len = data.len();
1262
1263 test_dense_data_repr(ptr, len, data, &lazy_format!("Box<[T]> DenseData"));
1265
1266 let mut data: Box<[f32]> = vec![0.0; 6].into();
1268 set_mut_dense_data_repr(&mut data, 1.0, 2.0);
1269 for (i, &v) in data.iter().enumerate() {
1270 assert_eq!(
1271 v,
1272 1.0 + 2.0 * (i as f32),
1273 "Box<[T]> MutDenseData at index {}",
1274 i
1275 );
1276 }
1277 }
1278
1279 #[test]
1280 fn test_try_from_error_light() {
1281 let data = vec![1, 2, 3];
1282 let err = MatrixView::try_from(data.as_slice(), 2, 3).unwrap_err();
1283
1284 let static_err = err.as_static();
1286 assert_eq!(static_err.len, 3);
1287 assert_eq!(static_err.nrows, 2);
1288 assert_eq!(static_err.ncols, 3);
1289
1290 let display_msg = format!("{}", static_err);
1292 assert!(display_msg.contains("tried to construct a matrix view with 2 rows and 3 columns"));
1293 assert!(display_msg.contains("slice of length 3"));
1294
1295 let recovered_data = err.into_inner();
1297 assert_eq!(recovered_data, data.as_slice());
1298 }
1299
1300 #[test]
1301 fn test_get_row_optional() {
1302 let data = make_test_matrix();
1303 let m = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1304
1305 assert_eq!(m.get_row(0), Some(&[0, 1, 2][..]));
1307 assert_eq!(m.get_row(1), Some(&[1, 2, 3][..]));
1308 assert_eq!(m.get_row(3), Some(&[3, 4, 5][..]));
1309
1310 assert_eq!(m.get_row(4), None);
1312 assert_eq!(m.get_row(100), None);
1313 }
1314
1315 #[test]
1316 fn test_unsafe_get_unchecked_methods() {
1317 let data = make_test_matrix();
1318 let mut m = Matrix::try_from(data.into(), 4, 3).unwrap();
1319
1320 unsafe {
1322 assert_eq!(*m.get_unchecked(0, 0), 0);
1323 assert_eq!(*m.get_unchecked(1, 2), 3);
1324 assert_eq!(*m.get_unchecked(3, 1), 4);
1325 }
1326
1327 unsafe {
1329 *m.get_unchecked_mut(0, 0) = 100;
1330 *m.get_unchecked_mut(1, 2) = 200;
1331 }
1332
1333 assert_eq!(m[(0, 0)], 100);
1334 assert_eq!(m[(1, 2)], 200);
1335
1336 unsafe {
1338 let row0 = m.get_row_unchecked(0);
1339 assert_eq!(row0[0], 100);
1340 assert_eq!(row0[1], 1);
1341 assert_eq!(row0[2], 2);
1342 }
1343
1344 unsafe {
1346 let row1 = m.get_row_unchecked_mut(1);
1347 row1[0] = 300;
1348 }
1349
1350 assert_eq!(m[(1, 0)], 300);
1351 }
1352
1353 #[test]
1354 fn test_to_owned() {
1355 let data = make_test_matrix();
1356 let view = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1357
1358 let owned = view.to_owned();
1360 assert_eq!(owned.nrows(), view.nrows());
1361 assert_eq!(owned.ncols(), view.ncols());
1362 assert_eq!(owned.as_slice(), view.as_slice());
1363
1364 assert_ne!(owned.as_ptr(), view.as_ptr());
1366
1367 test_basic_indexing(&owned);
1369 }
1370
1371 #[test]
1372 fn test_generator_trait_impls() {
1373 let mut gen = 42i32;
1375 assert_eq!(gen.generate(), 42);
1376 assert_eq!(gen.generate(), 42); let mut counter = 0;
1380 let mut gen = Init(|| {
1381 counter += 1;
1382 counter
1383 });
1384 assert_eq!(gen.generate(), 1);
1385 assert_eq!(gen.generate(), 2);
1386 assert_eq!(gen.generate(), 3);
1387 }
1388
1389 #[test]
1390 fn test_matrix_from_conversions() {
1391 let data = make_test_matrix();
1392 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1393
1394 let view = m.as_view();
1396 let slice: &[usize] = view.into();
1397 assert_eq!(slice.len(), 12);
1398 assert_eq!(slice[0], 0);
1399 assert_eq!(slice[11], 5);
1400
1401 let data2 = make_test_matrix();
1403 let mut m2 = Matrix::try_from(data2.into(), 4, 3).unwrap();
1404 let mut_view = m2.as_mut_view();
1405 let slice2: &[usize] = mut_view.into();
1406 assert_eq!(slice2.len(), 12);
1407 assert_eq!(slice2[0], 0);
1408 assert_eq!(slice2[11], 5);
1409 }
1410
1411 #[test]
1412 fn test_matrix_construction_edge_cases() {
1413 let m = Matrix::new(42, 1, 1);
1415 assert_eq!(m.nrows(), 1);
1416 assert_eq!(m.ncols(), 1);
1417 assert_eq!(m[(0, 0)], 42);
1418 assert_eq!(*m.try_get(0, 0).unwrap(), 42);
1419
1420 let m = Matrix::new(7, 1, 5);
1422 assert_eq!(m.nrows(), 1);
1423 assert_eq!(m.ncols(), 5);
1424 assert!(m.as_slice().iter().all(|&x| x == 7));
1425
1426 let m = Matrix::new(9, 5, 1);
1428 assert_eq!(m.nrows(), 5);
1429 assert_eq!(m.ncols(), 1);
1430 assert!(m.as_slice().iter().all(|&x| x == 9));
1431 }
1432
1433 #[test]
1434 fn test_matrix_view_edge_cases_with_data() {
1435 let data = vec![10, 20];
1437
1438 let m = MatrixView::try_from(data.as_slice(), 2, 1).unwrap();
1440 assert_eq!(m.nrows(), 2);
1441 assert_eq!(m.ncols(), 1);
1442 assert_eq!(m[(0, 0)], 10);
1443 assert_eq!(m[(1, 0)], 20);
1444 assert_eq!(*m.try_get(0, 0).unwrap(), 10);
1445 assert_eq!(*m.try_get(1, 0).unwrap(), 20);
1446 assert_eq!(m.row(0), &[10]);
1447 assert_eq!(m.row(1), &[20]);
1448
1449 let m = MatrixView::try_from(data.as_slice(), 1, 2).unwrap();
1451 assert_eq!(m.nrows(), 1);
1452 assert_eq!(m.ncols(), 2);
1453 assert_eq!(m[(0, 0)], 10);
1454 assert_eq!(m[(0, 1)], 20);
1455 assert_eq!(*m.try_get(0, 0).unwrap(), 10);
1456 assert_eq!(*m.try_get(0, 1).unwrap(), 20);
1457 assert_eq!(m.row(0), &[10, 20]);
1458 }
1459
1460 #[test]
1461 fn test_row_vector() {
1462 let data = vec![1, 2, 3];
1463 let m = MatrixView::row_vector(data.as_slice());
1464 assert_eq!(m.nrows(), 1);
1465 assert_eq!(m.ncols(), 3);
1466 assert_eq!(m.as_slice(), &[1, 2, 3]);
1467 assert_eq!(m.row(0), &[1, 2, 3]);
1468
1469 let empty: &[i32] = &[];
1471 let m = MatrixView::row_vector(empty);
1472 assert_eq!(m.nrows(), 1);
1473 assert_eq!(m.ncols(), 0);
1474
1475 let m = Matrix::row_vector(vec![10u64, 20].into_boxed_slice());
1477 assert_eq!(m.nrows(), 1);
1478 assert_eq!(m.ncols(), 2);
1479 assert_eq!(m[(0, 0)], 10);
1480 assert_eq!(m[(0, 1)], 20);
1481 }
1482
1483 #[test]
1484 fn test_column_vector() {
1485 let data = vec![1, 2, 3];
1486 let m = MatrixView::column_vector(data.as_slice());
1487 assert_eq!(m.nrows(), 3);
1488 assert_eq!(m.ncols(), 1);
1489 assert_eq!(m.as_slice(), &[1, 2, 3]);
1490 assert_eq!(m[(0, 0)], 1);
1491 assert_eq!(m[(1, 0)], 2);
1492 assert_eq!(m[(2, 0)], 3);
1493 assert_eq!(m.row(0), &[1]);
1494 assert_eq!(m.row(1), &[2]);
1495 assert_eq!(m.row(2), &[3]);
1496
1497 let empty: &[i32] = &[];
1499 let m = MatrixView::column_vector(empty);
1500 assert_eq!(m.nrows(), 0);
1501 assert_eq!(m.ncols(), 1);
1502
1503 let m = Matrix::column_vector(vec![10u64, 20].into_boxed_slice());
1505 assert_eq!(m.nrows(), 2);
1506 assert_eq!(m.ncols(), 1);
1507 assert_eq!(m[(0, 0)], 10);
1508 assert_eq!(m[(1, 0)], 20);
1509 }
1510
1511 #[test]
1512 fn test_map() {
1513 let m = Matrix::try_from(vec![1u32, 2, 3, 4].into(), 2, 2).unwrap();
1514 let doubled = m.map(|&x| x * 2);
1515 assert_eq!(doubled.as_slice(), &[2, 4, 6, 8]);
1516 assert_eq!(doubled.nrows(), 2);
1517 assert_eq!(doubled.ncols(), 2);
1518
1519 let as_f64 = m.map(|&x| x as f64);
1521 assert_eq!(as_f64.as_slice(), &[1.0, 2.0, 3.0, 4.0]);
1522 }
1523
1524 #[test]
1525 fn test_try_get() {
1526 let m = Matrix::try_from(vec![1, 2, 3, 4, 5, 6].into(), 2, 3).unwrap();
1527 assert_eq!(m.try_get(0, 0), Some(&1));
1528 assert_eq!(m.try_get(1, 2), Some(&6));
1529 assert_eq!(m.try_get(2, 0), None);
1530 assert_eq!(m.try_get(0, 3), None);
1531 }
1532
1533 #[test]
1534 fn test_subview() {
1535 let data = make_test_matrix();
1536 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1537
1538 {
1540 let subview = m.subview(0..4).unwrap();
1541 assert_eq!(subview.nrows(), 4);
1542 assert_eq!(subview.ncols(), 3);
1543
1544 assert_eq!(subview.row(0), &[0, 1, 2]);
1545 assert_eq!(subview.row(1), &[1, 2, 3]);
1546 assert_eq!(subview.row(2), &[2, 3, 4]);
1547 assert_eq!(subview.row(3), &[3, 4, 5]);
1548 assert!(subview.get_row(4).is_none());
1549 }
1550
1551 {
1553 let subview = m.subview(1..4).unwrap();
1554 assert_eq!(subview.nrows(), 3);
1555 assert_eq!(subview.ncols(), 3);
1556
1557 assert_eq!(subview.row(0), &[1, 2, 3]);
1558 assert_eq!(subview.row(1), &[2, 3, 4]);
1559 assert_eq!(subview.row(2), &[3, 4, 5]);
1560 assert!(subview.get_row(3).is_none());
1561 }
1562
1563 {
1565 let subview = m.subview(1..3).unwrap();
1566 assert_eq!(subview.nrows(), 2);
1567 assert_eq!(subview.ncols(), 3);
1568
1569 assert_eq!(subview.row(0), &[1, 2, 3]);
1570 assert_eq!(subview.row(1), &[2, 3, 4]);
1571 assert!(subview.get_row(2).is_none());
1572 }
1573
1574 {
1576 let subview = m.subview(2..2).unwrap();
1577 assert_eq!(subview.nrows(), 0);
1578 assert_eq!(subview.ncols(), 3);
1579 }
1580
1581 {
1583 let subview = m.subview(0..0).unwrap();
1584 assert_eq!(subview.nrows(), 0);
1585 assert_eq!(subview.ncols(), 3);
1586
1587 let subview = m.subview(4..4).unwrap();
1588 assert_eq!(subview.nrows(), 0);
1589 assert_eq!(subview.ncols(), 3);
1590 }
1591
1592 assert!(m.subview(5..5).is_none());
1594
1595 assert!(m.subview(0..6).is_none());
1597 assert!(m.subview(2..10).is_none());
1598
1599 assert!(m.subview(10..100).is_none());
1601
1602 #[expect(
1604 clippy::reversed_empty_ranges,
1605 reason = "we want to make sure it doesn't work"
1606 )]
1607 let empty = 3..2;
1608 assert!(m.subview(empty).is_none());
1609
1610 #[expect(
1611 clippy::reversed_empty_ranges,
1612 reason = "we want to make sure it doesn't work"
1613 )]
1614 let empty = 3..1;
1615 assert!(m.subview(empty).is_none());
1616
1617 assert!(m.subview(usize::MAX - 1..usize::MAX).is_none());
1619 assert!(m.subview(0..usize::MAX).is_none());
1620 }
1621
1622 #[test]
1623 #[cfg(all(not(miri), feature = "rayon"))]
1624 fn test_parallel_methods_edge_cases() {
1625 let data = make_test_matrix();
1626 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1627
1628 let windows: Vec<_> = m.par_window_iter(10).collect();
1630 assert_eq!(windows.len(), 1);
1631 assert_eq!(windows[0].nrows(), 4);
1632 assert_eq!(windows[0].ncols(), 3);
1633
1634 let rows: Vec<_> = m.par_row_iter().collect();
1636 assert_eq!(rows.len(), 4);
1637 assert_eq!(rows[0], &[0, 1, 2]);
1638 assert_eq!(rows[3], &[3, 4, 5]);
1639
1640 let mut m2 = Matrix::new(0, 4, 3);
1642
1643 m2.par_row_iter_mut().enumerate().for_each(|(i, row)| {
1645 for (j, elem) in row.iter_mut().enumerate() {
1646 *elem = i + j;
1647 }
1648 });
1649 test_basic_indexing(&m2);
1650
1651 let mut m3 = Matrix::new(0, 4, 3);
1653 m3.par_window_iter_mut(10)
1654 .enumerate()
1655 .for_each(|(_, mut window)| {
1656 window.row_iter_mut().enumerate().for_each(|(i, row)| {
1657 for (j, elem) in row.iter_mut().enumerate() {
1658 *elem = i + j;
1659 }
1660 });
1661 });
1662 test_basic_indexing(&m3);
1663 }
1664
1665 #[test]
1666 fn test_matrix_pointers() {
1667 let mut m = Matrix::new(42, 3, 4);
1668
1669 let const_ptr = m.as_ptr();
1671 let mut_ptr = m.as_mut_ptr();
1672 assert_eq!(const_ptr, mut_ptr as *const _);
1673
1674 let view = m.as_view();
1676 assert_eq!(view.as_ptr(), const_ptr);
1677
1678 let mut mut_view = m.as_mut_view();
1679 assert_eq!(mut_view.as_ptr(), const_ptr);
1680 assert_eq!(mut_view.as_mut_ptr(), mut_ptr);
1681 }
1682
1683 #[test]
1684 fn test_matrix_iteration_empty_cases() {
1685 let empty_data: Vec<i32> = vec![];
1688
1689 let _empty_matrix = MatrixView::try_from(empty_data.as_slice(), 0, 5).unwrap();
1691
1692 let data = vec![1, 2, 3];
1694 let single_row = MatrixView::try_from(data.as_slice(), 1, 3).unwrap();
1695 let rows: Vec<_> = single_row.row_iter().collect();
1696 assert_eq!(rows.len(), 1);
1697 assert_eq!(rows[0], &[1, 2, 3]);
1698
1699 let data = vec![1, 2, 3];
1701 let single_col = MatrixView::try_from(data.as_slice(), 3, 1).unwrap();
1702 let rows: Vec<_> = single_col.row_iter().collect();
1703 assert_eq!(rows.len(), 3);
1704 assert_eq!(rows[0], &[1]);
1705 assert_eq!(rows[1], &[2]);
1706 assert_eq!(rows[2], &[3]);
1707 }
1708
1709 #[test]
1710 fn test_matrix_init_generator_various_types() {
1711 use std::sync::atomic::{AtomicUsize, Ordering};
1713
1714 let counter = AtomicUsize::new(0);
1715 let m = Matrix::new(Init(|| counter.fetch_add(1, Ordering::SeqCst)), 2, 3);
1716
1717 assert_eq!(m[(0, 0)], 0);
1719 assert_eq!(m[(0, 1)], 1);
1720 assert_eq!(m[(0, 2)], 2);
1721 assert_eq!(m[(1, 0)], 3);
1722 assert_eq!(m[(1, 1)], 4);
1723 assert_eq!(m[(1, 2)], 5);
1724 }
1725
1726 #[test]
1727 fn test_debug_error_formatting() {
1728 let data = vec![1, 2, 3];
1730 let err = Matrix::try_from(data.into(), 2, 3).unwrap_err();
1731
1732 let debug_str = format!("{:?}", err);
1733 assert!(debug_str.contains("TryFromError"));
1734 assert!(debug_str.contains("data_len: 3"));
1735 assert!(debug_str.contains("nrows: 2"));
1736 assert!(debug_str.contains("ncols: 3"));
1737
1738 #[derive(Clone, Debug)]
1740 struct NonDebug(#[allow(dead_code)] i32);
1741
1742 let non_debug_data: Box<[NonDebug]> = vec![NonDebug(1), NonDebug(2)].into();
1743 let non_debug_err = Matrix::try_from(non_debug_data, 1, 3).unwrap_err();
1744 let debug_str = format!("{:?}", non_debug_err);
1745 assert!(debug_str.contains("TryFromError"));
1746 }
1747
1748 #[test]
1751 #[cfg(feature = "rayon")]
1752 fn test_par_window_iter_comprehensive() {
1753 use rayon::prelude::*;
1754
1755 let data: Vec<usize> = (0..24).collect(); let m = MatrixView::try_from(data.as_slice(), 6, 4).unwrap();
1758
1759 for batchsize in 1..=8 {
1761 let context = lazy_format!("batchsize = {}", batchsize);
1762 let windows: Vec<_> = m.par_window_iter(batchsize).collect();
1763
1764 let expected_windows = (m.nrows()).div_ceil(batchsize);
1766 assert_eq!(windows.len(), expected_windows, "{}", context);
1767
1768 let mut total_rows_seen = 0;
1770 for (window_idx, window) in windows.iter().enumerate() {
1771 let expected_rows = if window_idx == windows.len() - 1 {
1772 m.nrows() - (windows.len() - 1) * batchsize
1774 } else {
1775 batchsize
1776 };
1777
1778 assert_eq!(
1779 window.nrows(),
1780 expected_rows,
1781 "window {} - {}",
1782 window_idx,
1783 context
1784 );
1785 assert_eq!(
1786 window.ncols(),
1787 m.ncols(),
1788 "window {} - {}",
1789 window_idx,
1790 context
1791 );
1792
1793 for (row_idx, row) in window.row_iter().enumerate() {
1795 let global_row = window_idx * batchsize + row_idx;
1796 let expected: Vec<usize> =
1797 (0..m.ncols()).map(|j| global_row * m.ncols() + j).collect();
1798 assert_eq!(
1799 row,
1800 expected.as_slice(),
1801 "window {}, row {} - {}",
1802 window_idx,
1803 row_idx,
1804 context
1805 );
1806 }
1807
1808 total_rows_seen += window.nrows();
1809 }
1810
1811 assert_eq!(total_rows_seen, m.nrows(), "{}", context);
1812 }
1813
1814 let windows: Vec<_> = m.par_window_iter(m.nrows()).collect();
1816 assert_eq!(windows.len(), 1);
1817 assert_eq!(windows[0].nrows(), m.nrows());
1818 assert_eq!(windows[0].ncols(), m.ncols());
1819
1820 let windows: Vec<_> = m.par_window_iter(m.nrows() * 2).collect();
1822 assert_eq!(windows.len(), 1);
1823 assert_eq!(windows[0].nrows(), m.nrows());
1824 assert_eq!(windows[0].ncols(), m.ncols());
1825 }
1826
1827 #[test]
1828 #[cfg(feature = "rayon")]
1829 fn test_par_window_iter_mut_comprehensive() {
1830 use rayon::prelude::*;
1831
1832 for nrows in [1, 2, 3, 5, 8, 10] {
1834 for ncols in [1, 3, 4] {
1835 for batchsize in [1, 2, 3, 7] {
1836 let context = lazy_format!("{}x{}, batchsize={}", nrows, ncols, batchsize);
1837
1838 let mut m = Matrix::new(0usize, nrows, ncols);
1839
1840 m.par_window_iter_mut(batchsize).enumerate().for_each(
1842 |(window_idx, mut window)| {
1843 let base_row = window_idx * batchsize;
1844 window
1845 .row_iter_mut()
1846 .enumerate()
1847 .for_each(|(row_offset, row)| {
1848 let global_row = base_row + row_offset;
1849 for (col, elem) in row.iter_mut().enumerate() {
1850 *elem = global_row * ncols + col;
1851 }
1852 });
1853 },
1854 );
1855
1856 for row in 0..nrows {
1858 for col in 0..ncols {
1859 let expected = row * ncols + col;
1860 assert_eq!(
1861 m[(row, col)],
1862 expected,
1863 "pos ({}, {}) - {}",
1864 row,
1865 col,
1866 context
1867 );
1868 }
1869 }
1870 }
1871 }
1872 }
1873 }
1874
1875 #[test]
1876 #[cfg(feature = "rayon")]
1877 fn test_par_row_iter_comprehensive() {
1878 use rayon::prelude::*;
1879
1880 let nrows = 7;
1882 let ncols = 5;
1883 let data: Vec<i32> = (0..(nrows * ncols) as i32).collect();
1884 let m = MatrixView::try_from(data.as_slice(), nrows, ncols).unwrap();
1885
1886 let collected_rows: Vec<Vec<i32>> = m.par_row_iter().map(|row| row.to_vec()).collect();
1888
1889 assert_eq!(collected_rows.len(), nrows);
1890
1891 for (row_idx, row) in collected_rows.iter().enumerate() {
1892 assert_eq!(row.len(), ncols);
1893 let expected: Vec<i32> = ((row_idx * ncols)..((row_idx + 1) * ncols))
1894 .map(|x| x as i32)
1895 .collect();
1896 assert_eq!(row, &expected, "row {} mismatch", row_idx);
1897 }
1898
1899 let enumerated_rows: Vec<(usize, Vec<i32>)> = m
1901 .par_row_iter()
1902 .enumerate()
1903 .map(|(idx, row)| (idx, row.to_vec()))
1904 .collect();
1905
1906 let mut sorted_rows = enumerated_rows;
1908 sorted_rows.sort_by_key(|(idx, _)| *idx);
1909
1910 assert_eq!(sorted_rows.len(), nrows);
1911 for (expected_idx, (actual_idx, row)) in sorted_rows.iter().enumerate() {
1912 assert_eq!(*actual_idx, expected_idx);
1913 assert_eq!(row.len(), ncols);
1914 }
1915
1916 let sum: i32 = m.par_row_iter().map(|row| row.iter().sum::<i32>()).sum();
1918
1919 let expected_sum: i32 = data.iter().sum();
1920 assert_eq!(sum, expected_sum);
1921
1922 let target_row = 3;
1924 let found_row = m
1925 .par_row_iter()
1926 .enumerate()
1927 .find_any(|(idx, _)| *idx == target_row)
1928 .map(|(_, row)| row.to_vec());
1929
1930 assert!(found_row.is_some());
1931 let expected_row: Vec<i32> = ((target_row * ncols)..((target_row + 1) * ncols))
1932 .map(|x| x as i32)
1933 .collect();
1934 assert_eq!(found_row.unwrap(), expected_row);
1935 }
1936
1937 #[test]
1938 #[cfg(feature = "rayon")]
1939 fn test_par_row_iter_mut_comprehensive() {
1940 use rayon::prelude::*;
1941 use std::sync::atomic::{AtomicUsize, Ordering};
1942
1943 let nrows = 6;
1944 let ncols = 4;
1945 let mut m = Matrix::new(0u32, nrows, ncols);
1946
1947 m.par_row_iter_mut().enumerate().for_each(|(row_idx, row)| {
1949 for (col_idx, elem) in row.iter_mut().enumerate() {
1950 *elem = (row_idx * ncols + col_idx) as u32;
1951 }
1952 });
1953
1954 for row in 0..nrows {
1956 for col in 0..ncols {
1957 let expected = (row * ncols + col) as u32;
1958 assert_eq!(m[(row, col)], expected, "pos ({}, {})", row, col);
1959 }
1960 }
1961
1962 let counter = AtomicUsize::new(0);
1964 m.par_row_iter_mut().for_each(|row| {
1965 counter.fetch_add(1, Ordering::Relaxed);
1966 for elem in row {
1968 *elem *= 2;
1969 }
1970 });
1971
1972 assert_eq!(counter.load(Ordering::Relaxed), nrows);
1973
1974 for row in 0..nrows {
1976 for col in 0..ncols {
1977 let expected = ((row * ncols + col) * 2) as u32;
1978 assert_eq!(m[(row, col)], expected, "doubled pos ({}, {})", row, col);
1979 }
1980 }
1981 }
1982
1983 #[test]
1984 #[cfg(feature = "rayon")]
1985 fn test_parallel_iterators_with_single_dimensions() {
1986 use rayon::prelude::*;
1987
1988 let data = vec![1, 2, 3, 4, 5];
1990 let single_row = MatrixView::try_from(data.as_slice(), 1, 5).unwrap();
1991
1992 let windows: Vec<_> = single_row.par_window_iter(1).collect();
1993 assert_eq!(windows.len(), 1);
1994 assert_eq!(windows[0].nrows(), 1);
1995 assert_eq!(windows[0].ncols(), 5);
1996
1997 let rows: Vec<_> = single_row.par_row_iter().collect();
1998 assert_eq!(rows.len(), 1);
1999 assert_eq!(rows[0], &[1, 2, 3, 4, 5]);
2000
2001 let data = vec![1, 2, 3, 4, 5];
2003 let single_col = MatrixView::try_from(data.as_slice(), 5, 1).unwrap();
2004
2005 let windows: Vec<_> = single_col.par_window_iter(2).collect();
2006 assert_eq!(windows.len(), 3); assert_eq!(windows[0].nrows(), 2);
2008 assert_eq!(windows[1].nrows(), 2);
2009 assert_eq!(windows[2].nrows(), 1); let rows: Vec<_> = single_col.par_row_iter().collect();
2012 assert_eq!(rows.len(), 5);
2013 for (i, row) in rows.iter().enumerate() {
2014 assert_eq!(row, &[i + 1]);
2015 }
2016
2017 let data = vec![42];
2019 let tiny = MatrixView::try_from(data.as_slice(), 1, 1).unwrap();
2020
2021 let windows: Vec<_> = tiny.par_window_iter(1).collect();
2022 assert_eq!(windows.len(), 1);
2023 assert_eq!(windows[0][(0, 0)], 42);
2024
2025 let rows: Vec<_> = tiny.par_row_iter().collect();
2026 assert_eq!(rows.len(), 1);
2027 assert_eq!(rows[0], &[42]);
2028 }
2029
2030 #[test]
2031 #[cfg(feature = "rayon")]
2032 fn test_parallel_window_properties() {
2033 use rayon::prelude::*;
2034
2035 let data: Vec<usize> = (0..30).collect();
2037 let m = MatrixView::try_from(data.as_slice(), 6, 5).unwrap();
2038
2039 m.par_window_iter(2)
2041 .enumerate()
2042 .for_each(|(window_idx, window)| {
2043 for row_idx in 0..window.nrows() {
2044 for col_idx in 0..window.ncols() {
2045 let global_row = window_idx * 2 + row_idx;
2046 let expected = global_row * 5 + col_idx;
2047 assert_eq!(
2048 window[(row_idx, col_idx)],
2049 expected,
2050 "window {}, pos ({}, {})",
2051 window_idx,
2052 row_idx,
2053 col_idx
2054 );
2055 }
2056 }
2057 });
2058
2059 m.par_window_iter(3)
2061 .enumerate()
2062 .for_each(|(window_idx, window)| {
2063 let slice = window.as_slice();
2064 assert_eq!(slice.len(), window.nrows() * window.ncols());
2065
2066 for (slice_idx, &value) in slice.iter().enumerate() {
2067 let row = slice_idx / window.ncols();
2068 let col = slice_idx % window.ncols();
2069 assert_eq!(
2070 value,
2071 window[(row, col)],
2072 "window {}, slice_idx {}",
2073 window_idx,
2074 slice_idx
2075 );
2076 }
2077 });
2078
2079 m.par_window_iter(2).for_each(|window| {
2081 let rows_via_iter: Vec<_> = window.row_iter().collect();
2082 assert_eq!(rows_via_iter.len(), window.nrows());
2083
2084 for (row_idx, row) in rows_via_iter.iter().enumerate() {
2085 assert_eq!(row.len(), window.ncols());
2086 for (col_idx, &value) in row.iter().enumerate() {
2087 assert_eq!(value, window[(row_idx, col_idx)]);
2088 }
2089 }
2090 });
2091 }
2092
2093 #[test]
2094 #[cfg(feature = "rayon")]
2095 fn test_parallel_performance_characteristics() {
2096 use rayon::prelude::*;
2097 use std::sync::atomic::{AtomicUsize, Ordering};
2098
2099 let nrows = 100;
2101 let ncols = 10;
2102 let mut m = Matrix::new(0usize, nrows, ncols);
2103
2104 let work_counter = AtomicUsize::new(0);
2106
2107 m.par_window_iter_mut(10)
2108 .enumerate()
2109 .for_each(|(window_idx, mut window)| {
2110 work_counter.fetch_add(1, Ordering::Relaxed);
2111
2112 window
2114 .row_iter_mut()
2115 .enumerate()
2116 .for_each(|(row_offset, row)| {
2117 let global_row = window_idx * 10 + row_offset;
2118 for (col, elem) in row.iter_mut().enumerate() {
2119 *elem = global_row * ncols + col;
2120 }
2121 });
2122 });
2123
2124 assert_eq!(work_counter.load(Ordering::Relaxed), 10);
2126
2127 for row in 0..nrows {
2129 for col in 0..ncols {
2130 assert_eq!(m[(row, col)], row * ncols + col);
2131 }
2132 }
2133
2134 let total_sum: usize = m
2136 .par_window_iter(15)
2137 .map(|window| {
2138 window
2139 .row_iter()
2140 .map(|row| row.iter().sum::<usize>())
2141 .sum::<usize>()
2142 })
2143 .sum();
2144
2145 let expected_sum: usize = (0..(nrows * ncols)).sum();
2146 assert_eq!(total_sum, expected_sum);
2147 }
2148
2149 #[test]
2150 #[cfg(feature = "rayon")]
2151 fn test_rayon_trait_bounds_validation() {
2152 use rayon::prelude::*;
2153
2154 let data: Vec<u64> = (0..20).collect();
2156 let m = MatrixView::try_from(data.as_slice(), 4, 5).unwrap();
2157
2158 let _: Vec<_> = m.par_window_iter(2).collect();
2160 let _: Vec<_> = m.par_row_iter().collect();
2161
2162 let mut m = Matrix::new(0u64, 4, 5);
2164
2165 m.par_window_iter_mut(2).for_each(|mut window| {
2167 window.row_iter_mut().for_each(|row| {
2168 for elem in row {
2169 *elem = 42;
2170 }
2171 });
2172 });
2173
2174 m.par_row_iter_mut().for_each(|row| {
2175 for elem in row {
2176 *elem += 1;
2177 }
2178 });
2179
2180 assert!(m.as_slice().iter().all(|&x| x == 43));
2182 }
2183}