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 pub fn transpose(&self) -> Matrix<T::Elem>
643 where
644 T::Elem: Clone,
645 {
646 let mut row = 0;
647 let mut col = 0;
648
649 let f = Init(|| {
650 let v = unsafe { self.get_unchecked(row, col) }.clone();
653 row += 1;
654 if row == self.nrows() {
655 row = 0;
656 col += 1;
657 if col == self.ncols() {
658 col = 0;
659 }
660 }
661 v
662 });
663
664 Matrix::new(f, self.ncols(), self.nrows())
665 }
666}
667
668pub type Matrix<T> = MatrixBase<Box<[T]>>;
671
672pub type MatrixView<'a, T> = MatrixBase<&'a [T]>;
679
680pub type MutMatrixView<'a, T> = MatrixBase<&'a mut [T]>;
687
688impl<'a, T> From<MatrixView<'a, T>> for &'a [T] {
690 fn from(view: MatrixView<'a, T>) -> Self {
691 view.data
692 }
693}
694
695impl<'a, T> From<MutMatrixView<'a, T>> for &'a [T] {
697 fn from(view: MutMatrixView<'a, T>) -> Self {
698 view.data
699 }
700}
701
702impl<T> Index<(usize, usize)> for MatrixBase<T>
708where
709 T: DenseData,
710{
711 type Output = T::Elem;
712
713 fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
714 assert!(
715 row < self.nrows(),
716 "row {row} is out of bounds (max: {})",
717 self.nrows()
718 );
719 assert!(
720 col < self.ncols(),
721 "col {col} is out of bounds (max: {})",
722 self.ncols()
723 );
724
725 unsafe { self.get_unchecked(row, col) }
727 }
728}
729
730impl<T> IndexMut<(usize, usize)> for MatrixBase<T>
736where
737 T: MutDenseData,
738{
739 fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
740 assert!(
741 row < self.nrows(),
742 "row {row} is out of bounds (max: {})",
743 self.nrows()
744 );
745 assert!(
746 col < self.ncols(),
747 "col {col} is out of bounds (max: {})",
748 self.ncols()
749 );
750
751 unsafe { self.get_unchecked_mut(row, col) }
753 }
754}
755
756#[cfg(test)]
761mod tests {
762 use super::*;
763 use crate::lazy_format;
764
765 fn is_copyable<T: Copy>(_x: T) -> bool {
769 true
770 }
771
772 fn test_dense_data_repr<T, Repr>(
775 ptr: *const T,
776 len: usize,
777 repr: Repr,
778 context: &dyn std::fmt::Display,
779 ) where
780 T: Copy,
781 Repr: DenseData<Elem = T>,
782 {
783 let retrieved = repr.as_slice();
784 assert_eq!(retrieved.len(), len, "{}", context);
785 assert_eq!(retrieved.as_ptr(), ptr, "{}", context);
786 }
787
788 fn set_mut_dense_data_repr<T, Repr>(repr: &mut Repr, base: T, increment: T)
792 where
793 T: Copy + std::ops::Add<Output = T>,
794 Repr: DenseData<Elem = T> + MutDenseData,
795 {
796 let slice = repr.as_mut_slice();
797 for i in 0..slice.len() {
798 if i == 0 {
799 slice[i] = base;
800 } else {
801 slice[i] = slice[i - 1] + increment;
802 }
803 }
804 }
805
806 #[test]
807 fn slice_implements_dense_data_repr() {
808 for len in 0..10 {
809 let context = lazy_format!("len = {}", len);
810 let data: Vec<f32> = vec![0.0; len];
811 let slice = data.as_slice();
812 test_dense_data_repr(slice.as_ptr(), slice.len(), slice, &context);
813 }
814 }
815
816 #[test]
817 fn mut_slice_mplements_dense_data_repr() {
818 for len in 0..10 {
819 let context = lazy_format!("len = {}", len);
820 let mut data: Vec<f32> = vec![0.0; len];
821 let slice = data.as_mut_slice();
822
823 let ptr = slice.as_ptr();
824 let len = slice.len();
825 test_dense_data_repr(ptr, len, slice, &context);
826 }
827 }
828
829 #[test]
830 fn mut_slice_implements_mut_dense_data_repr() {
831 for len in 0..10 {
832 let context = lazy_format!("len = {}", len);
833 let mut data: Vec<f32> = vec![0.0; len];
834 let mut slice = data.as_mut_slice();
835
836 let base = 2.0;
837 let increment = 1.0;
838 set_mut_dense_data_repr(&mut slice, base, increment);
839
840 for (i, &v) in slice.iter().enumerate() {
841 let context = lazy_format!("entry {}, {}", i, context);
842 assert_eq!(v, base + increment * (i as f32), "{}", context);
843 }
844 }
845 }
846
847 #[test]
852 fn try_from_error_misc() {
853 let x = TryFromError::<&[f32]> {
854 data: &[],
855 nrows: 1,
856 ncols: 2,
857 };
858
859 let debug = format!("{:?}", x);
860 println!("debug = {}", debug);
861 assert!(debug.contains("TryFromError"));
862 assert!(debug.contains("data_len: 0"));
863 assert!(debug.contains("nrows: 1"));
864 assert!(debug.contains("ncols: 2"));
865 }
866
867 fn make_test_matrix() -> Vec<usize> {
868 vec![0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]
877 }
878
879 #[cfg(feature = "rayon")]
880 fn test_basic_indexing_parallel(m: MatrixView<'_, usize>) {
881 let batchsize = 2;
883 m.par_window_iter(batchsize)
884 .enumerate()
885 .for_each(|(i, submatrix)| {
886 assert_eq!(submatrix.nrows(), batchsize);
887 assert_eq!(submatrix.ncols(), m.ncols());
888
889 let base = i * batchsize;
891 assert_eq!(submatrix[(0, 0)], base);
892 assert_eq!(submatrix[(0, 1)], base + 1);
893 assert_eq!(submatrix[(0, 2)], base + 2);
894
895 assert_eq!(submatrix[(1, 0)], base + 1);
896 assert_eq!(submatrix[(1, 1)], base + 2);
897 assert_eq!(submatrix[(1, 2)], base + 3);
898 });
899
900 let batchsize = 3;
903 m.par_window_iter(batchsize)
904 .enumerate()
905 .for_each(|(i, submatrix)| {
906 if i == 0 {
907 assert_eq!(submatrix.nrows(), batchsize);
908 assert_eq!(submatrix.ncols(), m.ncols());
909
910 assert_eq!(submatrix[(0, 0)], 0);
912 assert_eq!(submatrix[(0, 1)], 1);
913 assert_eq!(submatrix[(0, 2)], 2);
914
915 assert_eq!(submatrix[(1, 0)], 1);
916 assert_eq!(submatrix[(1, 1)], 2);
917 assert_eq!(submatrix[(1, 2)], 3);
918
919 assert_eq!(submatrix[(2, 0)], 2);
920 assert_eq!(submatrix[(2, 1)], 3);
921 assert_eq!(submatrix[(2, 2)], 4);
922 } else {
923 assert_eq!(submatrix.nrows(), 1);
924 assert_eq!(submatrix.ncols(), m.ncols());
925
926 assert_eq!(submatrix[(0, 0)], 3);
928 assert_eq!(submatrix[(0, 1)], 4);
929 assert_eq!(submatrix[(0, 2)], 5);
930 }
931 });
932
933 let seen_rows: Box<[usize]> = m
935 .par_row_iter()
936 .enumerate()
937 .map(|(i, row)| {
938 let expected: Box<[usize]> = (0..m.ncols()).map(|j| j + i).collect();
939 assert_eq!(row, &*expected);
940 i
941 })
942 .collect();
943
944 let expected: Box<[usize]> = (0..m.nrows()).collect();
945 assert_eq!(seen_rows, expected);
946 }
947
948 fn test_basic_indexing<T>(m: &MatrixBase<T>)
949 where
950 T: DenseData<Elem = usize> + Sync,
951 {
952 assert_eq!(m.nrows(), 4);
953 assert_eq!(m.ncols(), 3);
954
955 assert_eq!(m[(0, 0)], 0);
957 assert_eq!(m[(0, 1)], 1);
958 assert_eq!(m[(0, 2)], 2);
959
960 assert_eq!(m[(1, 0)], 1);
961 assert_eq!(m[(1, 1)], 2);
962 assert_eq!(m[(1, 2)], 3);
963
964 assert_eq!(m[(2, 0)], 2);
965 assert_eq!(m[(2, 1)], 3);
966 assert_eq!(m[(2, 2)], 4);
967
968 assert_eq!(m[(3, 0)], 3);
969 assert_eq!(m[(3, 1)], 4);
970 assert_eq!(m[(3, 2)], 5);
971
972 assert_eq!(m.row(0), &[0, 1, 2]);
974 assert_eq!(m.row(1), &[1, 2, 3]);
975 assert_eq!(m.row(2), &[2, 3, 4]);
976 assert_eq!(m.row(3), &[3, 4, 5]);
977
978 let rows: Vec<Vec<usize>> = m.row_iter().map(|x| x.to_vec()).collect();
979 assert_eq!(m.row(0), &rows[0]);
980 assert_eq!(m.row(1), &rows[1]);
981 assert_eq!(m.row(2), &rows[2]);
982 assert_eq!(m.row(3), &rows[3]);
983
984 let batchsize = 2;
986 m.window_iter(batchsize)
987 .enumerate()
988 .for_each(|(i, submatrix)| {
989 assert_eq!(submatrix.nrows(), batchsize);
990 assert_eq!(submatrix.ncols(), m.ncols());
991
992 let base = i * batchsize;
994 assert_eq!(submatrix[(0, 0)], base);
995 assert_eq!(submatrix[(0, 1)], base + 1);
996 assert_eq!(submatrix[(0, 2)], base + 2);
997
998 assert_eq!(submatrix[(1, 0)], base + 1);
999 assert_eq!(submatrix[(1, 1)], base + 2);
1000 assert_eq!(submatrix[(1, 2)], base + 3);
1001 });
1002
1003 let batchsize = 3;
1006 m.window_iter(batchsize)
1007 .enumerate()
1008 .for_each(|(i, submatrix)| {
1009 if i == 0 {
1010 assert_eq!(submatrix.nrows(), batchsize);
1011 assert_eq!(submatrix.ncols(), m.ncols());
1012
1013 assert_eq!(submatrix[(0, 0)], 0);
1015 assert_eq!(submatrix[(0, 1)], 1);
1016 assert_eq!(submatrix[(0, 2)], 2);
1017
1018 assert_eq!(submatrix[(1, 0)], 1);
1019 assert_eq!(submatrix[(1, 1)], 2);
1020 assert_eq!(submatrix[(1, 2)], 3);
1021
1022 assert_eq!(submatrix[(2, 0)], 2);
1023 assert_eq!(submatrix[(2, 1)], 3);
1024 assert_eq!(submatrix[(2, 2)], 4);
1025 } else {
1026 assert_eq!(submatrix.nrows(), 1);
1027 assert_eq!(submatrix.ncols(), m.ncols());
1028
1029 assert_eq!(submatrix[(0, 0)], 3);
1031 assert_eq!(submatrix[(0, 1)], 4);
1032 assert_eq!(submatrix[(0, 2)], 5);
1033 }
1034 });
1035
1036 #[cfg(all(not(miri), feature = "rayon"))]
1037 test_basic_indexing_parallel(m.as_view());
1038 }
1039
1040 #[test]
1041 fn matrix_happy_path() {
1042 let data = make_test_matrix();
1043 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1044 test_basic_indexing(&m);
1045
1046 let ptr = m.as_ptr();
1049 let view = m.as_view();
1050 assert!(is_copyable(view));
1051 assert_eq!(view.as_ptr(), ptr);
1052 assert_eq!(view.nrows(), m.nrows());
1053 assert_eq!(view.ncols(), m.ncols());
1054 test_basic_indexing(&view);
1055 }
1056
1057 #[test]
1058 fn matrix_try_from_construction_error() {
1059 let data = make_test_matrix();
1060 let ptr = data.as_ptr();
1061 let len = data.len();
1062
1063 let m = Matrix::try_from(data.into(), 5, 4);
1064 assert!(m.is_err());
1065 let err = m.unwrap_err();
1066 assert_eq!(
1067 err.to_string(),
1068 "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1069 );
1070
1071 let data = err.into_inner();
1073 assert_eq!(data.as_ptr(), ptr);
1074 assert_eq!(data.len(), len);
1075
1076 let m = MatrixView::try_from(&data, 5, 4);
1077 assert!(m.is_err());
1078 assert_eq!(
1079 m.unwrap_err().to_string(),
1080 "tried to construct a matrix view with 5 rows and 4 columns over a slice of length 12"
1081 );
1082 }
1083
1084 #[test]
1085 fn matrix_mut_view() {
1086 let mut m = Matrix::<usize>::new(0, 4, 3);
1087 assert_eq!(m.nrows(), 4);
1088 assert_eq!(m.ncols(), 3);
1089 assert!(m.as_slice().iter().all(|&i| i == 0));
1090 let ptr = m.as_ptr();
1091 let mut_ptr = m.as_mut_ptr();
1092 assert_eq!(ptr, mut_ptr);
1093
1094 let mut view = m.as_mut_view();
1095 assert_eq!(view.nrows(), 4);
1096 assert_eq!(view.ncols(), 3);
1097 assert_eq!(view.as_ptr(), ptr);
1098 assert_eq!(view.as_mut_ptr(), mut_ptr);
1099
1100 for i in 0..view.nrows() {
1102 for j in 0..view.ncols() {
1103 view[(i, j)] = i + j;
1104 }
1105 }
1106
1107 test_basic_indexing(&m);
1109
1110 let inner = m.into_inner();
1111 assert_eq!(inner.as_ptr(), ptr);
1112 assert_eq!(inner.len(), 4 * 3);
1113 }
1114
1115 #[test]
1116 fn matrix_view_zero_sizes() {
1117 let data: Vec<usize> = vec![];
1118 let m = MatrixView::try_from(data.as_slice(), 0, 10).unwrap();
1120 assert_eq!(m.nrows(), 0);
1121 assert_eq!(m.ncols(), 10);
1122
1123 let m = MatrixView::try_from(data.as_slice(), 3, 0).unwrap();
1125 assert_eq!(m.nrows(), 3);
1126 assert_eq!(m.ncols(), 0);
1127 let empty: &[usize] = &[];
1128 assert_eq!(m.row(0), empty);
1129 assert_eq!(m.row(1), empty);
1130 assert_eq!(m.row(2), empty);
1131
1132 let m = MatrixView::try_from(data.as_slice(), 0, 0).unwrap();
1134 assert_eq!(m.nrows(), 0);
1135 assert_eq!(m.ncols(), 0);
1136 }
1137
1138 #[test]
1139 fn matrix_view_construction_elementwise() {
1140 let mut m = Matrix::<usize>::new(0, 4, 3);
1141
1142 for i in 0..m.nrows() {
1144 for j in 0..m.ncols() {
1145 m[(i, j)] = i + j;
1146 }
1147 }
1148 test_basic_indexing(&m);
1149 }
1150
1151 #[test]
1152 fn matrix_construction_by_row() {
1153 let mut m = Matrix::<usize>::new(0, 4, 3);
1154 assert!(m.as_slice().iter().all(|i| *i == 0));
1155
1156 let ncols = m.ncols();
1157 for i in 0..m.nrows() {
1158 let row = m.row_mut(i);
1159 assert_eq!(row.len(), ncols);
1160 row[0] = i;
1161 row[1] = i + 1;
1162 row[2] = i + 2;
1163 }
1164 test_basic_indexing(&m);
1165 }
1166
1167 #[test]
1168 fn matrix_construction_by_rowiter() {
1169 let mut m = Matrix::<usize>::new(0, 4, 3);
1170 assert!(m.as_slice().iter().all(|i| *i == 0));
1171
1172 let ncols = m.ncols();
1173 m.row_iter_mut().enumerate().for_each(|(i, row)| {
1174 assert_eq!(row.len(), ncols);
1175 row[0] = i;
1176 row[1] = i + 1;
1177 row[2] = i + 2;
1178 });
1179 test_basic_indexing(&m);
1180 }
1181
1182 #[cfg(all(not(miri), feature = "rayon"))]
1183 #[test]
1184 fn matrix_construction_by_par_windows() {
1185 let mut m = Matrix::<usize>::new(0, 4, 3);
1186 assert!(m.as_slice().iter().all(|i| *i == 0));
1187
1188 let ncols = m.ncols();
1189 for batchsize in 1..=4 {
1190 m.par_window_iter_mut(batchsize)
1191 .enumerate()
1192 .for_each(|(i, mut submatrix)| {
1193 let base = i * batchsize;
1194 submatrix.row_iter_mut().enumerate().for_each(|(j, row)| {
1195 assert_eq!(row.len(), ncols);
1196 row[0] = base + j;
1197 row[1] = base + j + 1;
1198 row[2] = base + j + 2;
1199 });
1200 });
1201 test_basic_indexing(&m);
1202 }
1203 }
1204
1205 #[test]
1206 fn matrix_construction_happens_in_memory_order() {
1207 let mut i = 0;
1208 let ncols = 3;
1209 let initializer = Init(|| {
1210 let value = (i % ncols) + (i / ncols);
1211 i += 1;
1212 value
1213 });
1214
1215 let m = Matrix::new(initializer, 4, 3);
1216 test_basic_indexing(&m);
1217 }
1218
1219 #[test]
1221 #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1222 fn test_get_row_panics() {
1223 let m = Matrix::<usize>::new(0, 3, 7);
1224 m.row(3);
1225 }
1226
1227 #[test]
1228 #[should_panic(expected = "tried to access row 3 of a matrix with 3 rows")]
1229 fn test_get_row_mut_panics() {
1230 let mut m = Matrix::<usize>::new(0, 3, 7);
1231 m.row_mut(3);
1232 }
1233
1234 #[test]
1235 #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1236 fn test_index_panics_row() {
1237 let m = Matrix::<usize>::new(0, 3, 7);
1238 assert!(m.try_get(3, 2).is_none());
1239 let _ = m[(3, 2)];
1240 }
1241
1242 #[test]
1243 #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1244 fn test_index_panics_col() {
1245 let m = Matrix::<usize>::new(0, 3, 7);
1246 assert!(m.try_get(2, 7).is_none());
1247 let _ = m[(2, 7)];
1248 }
1249
1250 #[test]
1251 #[should_panic(expected = "row 3 is out of bounds (max: 3)")]
1252 fn test_index_mut_panics_row() {
1253 let mut m = Matrix::<usize>::new(0, 3, 7);
1254 m[(3, 2)] = 1;
1255 }
1256
1257 #[test]
1258 #[should_panic(expected = "col 7 is out of bounds (max: 7)")]
1259 fn test_index_mut_panics_col() {
1260 let mut m = Matrix::<usize>::new(0, 3, 7);
1261 m[(2, 7)] = 1;
1262 }
1263
1264 #[test]
1265 #[cfg(feature = "rayon")]
1266 #[should_panic(expected = "par_window_iter batchsize cannot be zero")]
1267 fn test_par_window_iter_panics() {
1268 let m = Matrix::<usize>::new(0, 4, 4);
1269 let _ = m.par_window_iter(0);
1270 }
1271
1272 #[test]
1273 #[cfg(feature = "rayon")]
1274 #[should_panic(expected = "par_window_iter_mut batchsize cannot be zero")]
1275 fn test_par_window_iter_mut_panics() {
1276 let mut m = Matrix::<usize>::new(0, 4, 4);
1277 let _ = m.par_window_iter_mut(0);
1278 }
1279
1280 #[test]
1283 fn test_box_slice_dense_data_impls() {
1284 let data: Box<[f32]> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0].into();
1286 let ptr = data.as_ptr();
1287 let len = data.len();
1288
1289 test_dense_data_repr(ptr, len, data, &lazy_format!("Box<[T]> DenseData"));
1291
1292 let mut data: Box<[f32]> = vec![0.0; 6].into();
1294 set_mut_dense_data_repr(&mut data, 1.0, 2.0);
1295 for (i, &v) in data.iter().enumerate() {
1296 assert_eq!(
1297 v,
1298 1.0 + 2.0 * (i as f32),
1299 "Box<[T]> MutDenseData at index {}",
1300 i
1301 );
1302 }
1303 }
1304
1305 #[test]
1306 fn test_try_from_error_light() {
1307 let data = vec![1, 2, 3];
1308 let err = MatrixView::try_from(data.as_slice(), 2, 3).unwrap_err();
1309
1310 let static_err = err.as_static();
1312 assert_eq!(static_err.len, 3);
1313 assert_eq!(static_err.nrows, 2);
1314 assert_eq!(static_err.ncols, 3);
1315
1316 let display_msg = format!("{}", static_err);
1318 assert!(display_msg.contains("tried to construct a matrix view with 2 rows and 3 columns"));
1319 assert!(display_msg.contains("slice of length 3"));
1320
1321 let recovered_data = err.into_inner();
1323 assert_eq!(recovered_data, data.as_slice());
1324 }
1325
1326 #[test]
1327 fn test_get_row_optional() {
1328 let data = make_test_matrix();
1329 let m = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1330
1331 assert_eq!(m.get_row(0), Some(&[0, 1, 2][..]));
1333 assert_eq!(m.get_row(1), Some(&[1, 2, 3][..]));
1334 assert_eq!(m.get_row(3), Some(&[3, 4, 5][..]));
1335
1336 assert_eq!(m.get_row(4), None);
1338 assert_eq!(m.get_row(100), None);
1339 }
1340
1341 #[test]
1342 fn test_unsafe_get_unchecked_methods() {
1343 let data = make_test_matrix();
1344 let mut m = Matrix::try_from(data.into(), 4, 3).unwrap();
1345
1346 unsafe {
1348 assert_eq!(*m.get_unchecked(0, 0), 0);
1349 assert_eq!(*m.get_unchecked(1, 2), 3);
1350 assert_eq!(*m.get_unchecked(3, 1), 4);
1351 }
1352
1353 unsafe {
1355 *m.get_unchecked_mut(0, 0) = 100;
1356 *m.get_unchecked_mut(1, 2) = 200;
1357 }
1358
1359 assert_eq!(m[(0, 0)], 100);
1360 assert_eq!(m[(1, 2)], 200);
1361
1362 unsafe {
1364 let row0 = m.get_row_unchecked(0);
1365 assert_eq!(row0[0], 100);
1366 assert_eq!(row0[1], 1);
1367 assert_eq!(row0[2], 2);
1368 }
1369
1370 unsafe {
1372 let row1 = m.get_row_unchecked_mut(1);
1373 row1[0] = 300;
1374 }
1375
1376 assert_eq!(m[(1, 0)], 300);
1377 }
1378
1379 #[test]
1380 fn test_to_owned() {
1381 let data = make_test_matrix();
1382 let view = MatrixView::try_from(data.as_slice(), 4, 3).unwrap();
1383
1384 let owned = view.to_owned();
1386 assert_eq!(owned.nrows(), view.nrows());
1387 assert_eq!(owned.ncols(), view.ncols());
1388 assert_eq!(owned.as_slice(), view.as_slice());
1389
1390 assert_ne!(owned.as_ptr(), view.as_ptr());
1392
1393 test_basic_indexing(&owned);
1395 }
1396
1397 #[test]
1398 fn test_generator_trait_impls() {
1399 let mut gen = 42i32;
1401 assert_eq!(gen.generate(), 42);
1402 assert_eq!(gen.generate(), 42); let mut counter = 0;
1406 let mut gen = Init(|| {
1407 counter += 1;
1408 counter
1409 });
1410 assert_eq!(gen.generate(), 1);
1411 assert_eq!(gen.generate(), 2);
1412 assert_eq!(gen.generate(), 3);
1413 }
1414
1415 #[test]
1416 fn test_matrix_from_conversions() {
1417 let data = make_test_matrix();
1418 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1419
1420 let view = m.as_view();
1422 let slice: &[usize] = view.into();
1423 assert_eq!(slice.len(), 12);
1424 assert_eq!(slice[0], 0);
1425 assert_eq!(slice[11], 5);
1426
1427 let data2 = make_test_matrix();
1429 let mut m2 = Matrix::try_from(data2.into(), 4, 3).unwrap();
1430 let mut_view = m2.as_mut_view();
1431 let slice2: &[usize] = mut_view.into();
1432 assert_eq!(slice2.len(), 12);
1433 assert_eq!(slice2[0], 0);
1434 assert_eq!(slice2[11], 5);
1435 }
1436
1437 #[test]
1438 fn test_matrix_construction_edge_cases() {
1439 let m = Matrix::new(42, 1, 1);
1441 assert_eq!(m.nrows(), 1);
1442 assert_eq!(m.ncols(), 1);
1443 assert_eq!(m[(0, 0)], 42);
1444 assert_eq!(*m.try_get(0, 0).unwrap(), 42);
1445
1446 let m = Matrix::new(7, 1, 5);
1448 assert_eq!(m.nrows(), 1);
1449 assert_eq!(m.ncols(), 5);
1450 assert!(m.as_slice().iter().all(|&x| x == 7));
1451
1452 let m = Matrix::new(9, 5, 1);
1454 assert_eq!(m.nrows(), 5);
1455 assert_eq!(m.ncols(), 1);
1456 assert!(m.as_slice().iter().all(|&x| x == 9));
1457 }
1458
1459 #[test]
1460 fn test_matrix_view_edge_cases_with_data() {
1461 let data = vec![10, 20];
1463
1464 let m = MatrixView::try_from(data.as_slice(), 2, 1).unwrap();
1466 assert_eq!(m.nrows(), 2);
1467 assert_eq!(m.ncols(), 1);
1468 assert_eq!(m[(0, 0)], 10);
1469 assert_eq!(m[(1, 0)], 20);
1470 assert_eq!(*m.try_get(0, 0).unwrap(), 10);
1471 assert_eq!(*m.try_get(1, 0).unwrap(), 20);
1472 assert_eq!(m.row(0), &[10]);
1473 assert_eq!(m.row(1), &[20]);
1474
1475 let m = MatrixView::try_from(data.as_slice(), 1, 2).unwrap();
1477 assert_eq!(m.nrows(), 1);
1478 assert_eq!(m.ncols(), 2);
1479 assert_eq!(m[(0, 0)], 10);
1480 assert_eq!(m[(0, 1)], 20);
1481 assert_eq!(*m.try_get(0, 0).unwrap(), 10);
1482 assert_eq!(*m.try_get(0, 1).unwrap(), 20);
1483 assert_eq!(m.row(0), &[10, 20]);
1484 }
1485
1486 #[test]
1487 fn test_row_vector() {
1488 let data = vec![1, 2, 3];
1489 let m = MatrixView::row_vector(data.as_slice());
1490 assert_eq!(m.nrows(), 1);
1491 assert_eq!(m.ncols(), 3);
1492 assert_eq!(m.as_slice(), &[1, 2, 3]);
1493 assert_eq!(m.row(0), &[1, 2, 3]);
1494
1495 let empty: &[i32] = &[];
1497 let m = MatrixView::row_vector(empty);
1498 assert_eq!(m.nrows(), 1);
1499 assert_eq!(m.ncols(), 0);
1500
1501 let m = Matrix::row_vector(vec![10u64, 20].into_boxed_slice());
1503 assert_eq!(m.nrows(), 1);
1504 assert_eq!(m.ncols(), 2);
1505 assert_eq!(m[(0, 0)], 10);
1506 assert_eq!(m[(0, 1)], 20);
1507 }
1508
1509 #[test]
1510 fn test_column_vector() {
1511 let data = vec![1, 2, 3];
1512 let m = MatrixView::column_vector(data.as_slice());
1513 assert_eq!(m.nrows(), 3);
1514 assert_eq!(m.ncols(), 1);
1515 assert_eq!(m.as_slice(), &[1, 2, 3]);
1516 assert_eq!(m[(0, 0)], 1);
1517 assert_eq!(m[(1, 0)], 2);
1518 assert_eq!(m[(2, 0)], 3);
1519 assert_eq!(m.row(0), &[1]);
1520 assert_eq!(m.row(1), &[2]);
1521 assert_eq!(m.row(2), &[3]);
1522
1523 let empty: &[i32] = &[];
1525 let m = MatrixView::column_vector(empty);
1526 assert_eq!(m.nrows(), 0);
1527 assert_eq!(m.ncols(), 1);
1528
1529 let m = Matrix::column_vector(vec![10u64, 20].into_boxed_slice());
1531 assert_eq!(m.nrows(), 2);
1532 assert_eq!(m.ncols(), 1);
1533 assert_eq!(m[(0, 0)], 10);
1534 assert_eq!(m[(1, 0)], 20);
1535 }
1536
1537 #[test]
1538 fn test_map() {
1539 let m = Matrix::try_from(vec![1u32, 2, 3, 4].into(), 2, 2).unwrap();
1540 let doubled = m.map(|&x| x * 2);
1541 assert_eq!(doubled.as_slice(), &[2, 4, 6, 8]);
1542 assert_eq!(doubled.nrows(), 2);
1543 assert_eq!(doubled.ncols(), 2);
1544
1545 let as_f64 = m.map(|&x| x as f64);
1547 assert_eq!(as_f64.as_slice(), &[1.0, 2.0, 3.0, 4.0]);
1548 }
1549
1550 #[test]
1551 fn test_try_get() {
1552 let m = Matrix::try_from(vec![1, 2, 3, 4, 5, 6].into(), 2, 3).unwrap();
1553 assert_eq!(m.try_get(0, 0), Some(&1));
1554 assert_eq!(m.try_get(1, 2), Some(&6));
1555 assert_eq!(m.try_get(2, 0), None);
1556 assert_eq!(m.try_get(0, 3), None);
1557 }
1558
1559 #[test]
1560 fn test_subview() {
1561 let data = make_test_matrix();
1562 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1563
1564 {
1566 let subview = m.subview(0..4).unwrap();
1567 assert_eq!(subview.nrows(), 4);
1568 assert_eq!(subview.ncols(), 3);
1569
1570 assert_eq!(subview.row(0), &[0, 1, 2]);
1571 assert_eq!(subview.row(1), &[1, 2, 3]);
1572 assert_eq!(subview.row(2), &[2, 3, 4]);
1573 assert_eq!(subview.row(3), &[3, 4, 5]);
1574 assert!(subview.get_row(4).is_none());
1575 }
1576
1577 {
1579 let subview = m.subview(1..4).unwrap();
1580 assert_eq!(subview.nrows(), 3);
1581 assert_eq!(subview.ncols(), 3);
1582
1583 assert_eq!(subview.row(0), &[1, 2, 3]);
1584 assert_eq!(subview.row(1), &[2, 3, 4]);
1585 assert_eq!(subview.row(2), &[3, 4, 5]);
1586 assert!(subview.get_row(3).is_none());
1587 }
1588
1589 {
1591 let subview = m.subview(1..3).unwrap();
1592 assert_eq!(subview.nrows(), 2);
1593 assert_eq!(subview.ncols(), 3);
1594
1595 assert_eq!(subview.row(0), &[1, 2, 3]);
1596 assert_eq!(subview.row(1), &[2, 3, 4]);
1597 assert!(subview.get_row(2).is_none());
1598 }
1599
1600 {
1602 let subview = m.subview(2..2).unwrap();
1603 assert_eq!(subview.nrows(), 0);
1604 assert_eq!(subview.ncols(), 3);
1605 }
1606
1607 {
1609 let subview = m.subview(0..0).unwrap();
1610 assert_eq!(subview.nrows(), 0);
1611 assert_eq!(subview.ncols(), 3);
1612
1613 let subview = m.subview(4..4).unwrap();
1614 assert_eq!(subview.nrows(), 0);
1615 assert_eq!(subview.ncols(), 3);
1616 }
1617
1618 assert!(m.subview(5..5).is_none());
1620
1621 assert!(m.subview(0..6).is_none());
1623 assert!(m.subview(2..10).is_none());
1624
1625 assert!(m.subview(10..100).is_none());
1627
1628 #[expect(
1630 clippy::reversed_empty_ranges,
1631 reason = "we want to make sure it doesn't work"
1632 )]
1633 let empty = 3..2;
1634 assert!(m.subview(empty).is_none());
1635
1636 #[expect(
1637 clippy::reversed_empty_ranges,
1638 reason = "we want to make sure it doesn't work"
1639 )]
1640 let empty = 3..1;
1641 assert!(m.subview(empty).is_none());
1642
1643 assert!(m.subview(usize::MAX - 1..usize::MAX).is_none());
1645 assert!(m.subview(0..usize::MAX).is_none());
1646 }
1647
1648 #[test]
1649 #[cfg(all(not(miri), feature = "rayon"))]
1650 fn test_parallel_methods_edge_cases() {
1651 let data = make_test_matrix();
1652 let m = Matrix::try_from(data.into(), 4, 3).unwrap();
1653
1654 let windows: Vec<_> = m.par_window_iter(10).collect();
1656 assert_eq!(windows.len(), 1);
1657 assert_eq!(windows[0].nrows(), 4);
1658 assert_eq!(windows[0].ncols(), 3);
1659
1660 let rows: Vec<_> = m.par_row_iter().collect();
1662 assert_eq!(rows.len(), 4);
1663 assert_eq!(rows[0], &[0, 1, 2]);
1664 assert_eq!(rows[3], &[3, 4, 5]);
1665
1666 let mut m2 = Matrix::new(0, 4, 3);
1668
1669 m2.par_row_iter_mut().enumerate().for_each(|(i, row)| {
1671 for (j, elem) in row.iter_mut().enumerate() {
1672 *elem = i + j;
1673 }
1674 });
1675 test_basic_indexing(&m2);
1676
1677 let mut m3 = Matrix::new(0, 4, 3);
1679 m3.par_window_iter_mut(10)
1680 .enumerate()
1681 .for_each(|(_, mut window)| {
1682 window.row_iter_mut().enumerate().for_each(|(i, row)| {
1683 for (j, elem) in row.iter_mut().enumerate() {
1684 *elem = i + j;
1685 }
1686 });
1687 });
1688 test_basic_indexing(&m3);
1689 }
1690
1691 #[test]
1692 fn test_matrix_pointers() {
1693 let mut m = Matrix::new(42, 3, 4);
1694
1695 let const_ptr = m.as_ptr();
1697 let mut_ptr = m.as_mut_ptr();
1698 assert_eq!(const_ptr, mut_ptr as *const _);
1699
1700 let view = m.as_view();
1702 assert_eq!(view.as_ptr(), const_ptr);
1703
1704 let mut mut_view = m.as_mut_view();
1705 assert_eq!(mut_view.as_ptr(), const_ptr);
1706 assert_eq!(mut_view.as_mut_ptr(), mut_ptr);
1707 }
1708
1709 #[test]
1710 fn test_matrix_iteration_empty_cases() {
1711 let empty_data: Vec<i32> = vec![];
1714
1715 let _empty_matrix = MatrixView::try_from(empty_data.as_slice(), 0, 5).unwrap();
1717
1718 let data = vec![1, 2, 3];
1720 let single_row = MatrixView::try_from(data.as_slice(), 1, 3).unwrap();
1721 let rows: Vec<_> = single_row.row_iter().collect();
1722 assert_eq!(rows.len(), 1);
1723 assert_eq!(rows[0], &[1, 2, 3]);
1724
1725 let data = vec![1, 2, 3];
1727 let single_col = MatrixView::try_from(data.as_slice(), 3, 1).unwrap();
1728 let rows: Vec<_> = single_col.row_iter().collect();
1729 assert_eq!(rows.len(), 3);
1730 assert_eq!(rows[0], &[1]);
1731 assert_eq!(rows[1], &[2]);
1732 assert_eq!(rows[2], &[3]);
1733 }
1734
1735 #[test]
1736 fn test_matrix_init_generator_various_types() {
1737 use std::sync::atomic::{AtomicUsize, Ordering};
1739
1740 let counter = AtomicUsize::new(0);
1741 let m = Matrix::new(Init(|| counter.fetch_add(1, Ordering::SeqCst)), 2, 3);
1742
1743 assert_eq!(m[(0, 0)], 0);
1745 assert_eq!(m[(0, 1)], 1);
1746 assert_eq!(m[(0, 2)], 2);
1747 assert_eq!(m[(1, 0)], 3);
1748 assert_eq!(m[(1, 1)], 4);
1749 assert_eq!(m[(1, 2)], 5);
1750 }
1751
1752 #[test]
1753 fn test_transpose() {
1754 {
1755 let v = Matrix::new(0, 0, 0);
1756 let t = v.transpose();
1757 assert_eq!(t.nrows(), 0);
1758 assert_eq!(t.ncols(), 0);
1759 }
1760
1761 {
1762 let v = Matrix::new(0, 0, 10);
1763 let t = v.transpose();
1764 assert_eq!(t.nrows(), 10);
1765 assert_eq!(t.ncols(), 0);
1766 }
1767
1768 {
1769 let v = Matrix::new(0, 10, 0);
1770 let t = v.transpose();
1771 assert_eq!(t.nrows(), 0);
1772 assert_eq!(t.ncols(), 10);
1773 }
1774
1775 {
1776 let v = Matrix::<usize>::try_from(Box::new([1, 2, 3, 4, 5, 6]), 2, 3).unwrap();
1777 let t = v.transpose();
1778
1779 assert_eq!(t.row(0), &[1, 4]);
1780 assert_eq!(t.row(1), &[2, 5]);
1781 assert_eq!(t.row(2), &[3, 6]);
1782 }
1783 }
1784
1785 #[test]
1786 fn test_debug_error_formatting() {
1787 let data = vec![1, 2, 3];
1789 let err = Matrix::try_from(data.into(), 2, 3).unwrap_err();
1790
1791 let debug_str = format!("{:?}", err);
1792 assert!(debug_str.contains("TryFromError"));
1793 assert!(debug_str.contains("data_len: 3"));
1794 assert!(debug_str.contains("nrows: 2"));
1795 assert!(debug_str.contains("ncols: 3"));
1796
1797 #[derive(Clone, Debug)]
1799 struct NonDebug(#[allow(dead_code)] i32);
1800
1801 let non_debug_data: Box<[NonDebug]> = vec![NonDebug(1), NonDebug(2)].into();
1802 let non_debug_err = Matrix::try_from(non_debug_data, 1, 3).unwrap_err();
1803 let debug_str = format!("{:?}", non_debug_err);
1804 assert!(debug_str.contains("TryFromError"));
1805 }
1806
1807 #[test]
1810 #[cfg(feature = "rayon")]
1811 fn test_par_window_iter_comprehensive() {
1812 use rayon::prelude::*;
1813
1814 let data: Vec<usize> = (0..24).collect(); let m = MatrixView::try_from(data.as_slice(), 6, 4).unwrap();
1817
1818 for batchsize in 1..=8 {
1820 let context = lazy_format!("batchsize = {}", batchsize);
1821 let windows: Vec<_> = m.par_window_iter(batchsize).collect();
1822
1823 let expected_windows = (m.nrows()).div_ceil(batchsize);
1825 assert_eq!(windows.len(), expected_windows, "{}", context);
1826
1827 let mut total_rows_seen = 0;
1829 for (window_idx, window) in windows.iter().enumerate() {
1830 let expected_rows = if window_idx == windows.len() - 1 {
1831 m.nrows() - (windows.len() - 1) * batchsize
1833 } else {
1834 batchsize
1835 };
1836
1837 assert_eq!(
1838 window.nrows(),
1839 expected_rows,
1840 "window {} - {}",
1841 window_idx,
1842 context
1843 );
1844 assert_eq!(
1845 window.ncols(),
1846 m.ncols(),
1847 "window {} - {}",
1848 window_idx,
1849 context
1850 );
1851
1852 for (row_idx, row) in window.row_iter().enumerate() {
1854 let global_row = window_idx * batchsize + row_idx;
1855 let expected: Vec<usize> =
1856 (0..m.ncols()).map(|j| global_row * m.ncols() + j).collect();
1857 assert_eq!(
1858 row,
1859 expected.as_slice(),
1860 "window {}, row {} - {}",
1861 window_idx,
1862 row_idx,
1863 context
1864 );
1865 }
1866
1867 total_rows_seen += window.nrows();
1868 }
1869
1870 assert_eq!(total_rows_seen, m.nrows(), "{}", context);
1871 }
1872
1873 let windows: Vec<_> = m.par_window_iter(m.nrows()).collect();
1875 assert_eq!(windows.len(), 1);
1876 assert_eq!(windows[0].nrows(), m.nrows());
1877 assert_eq!(windows[0].ncols(), m.ncols());
1878
1879 let windows: Vec<_> = m.par_window_iter(m.nrows() * 2).collect();
1881 assert_eq!(windows.len(), 1);
1882 assert_eq!(windows[0].nrows(), m.nrows());
1883 assert_eq!(windows[0].ncols(), m.ncols());
1884 }
1885
1886 #[test]
1887 #[cfg(feature = "rayon")]
1888 fn test_par_window_iter_mut_comprehensive() {
1889 use rayon::prelude::*;
1890
1891 for nrows in [1, 2, 3, 5, 8, 10] {
1893 for ncols in [1, 3, 4] {
1894 for batchsize in [1, 2, 3, 7] {
1895 let context = lazy_format!("{}x{}, batchsize={}", nrows, ncols, batchsize);
1896
1897 let mut m = Matrix::new(0usize, nrows, ncols);
1898
1899 m.par_window_iter_mut(batchsize).enumerate().for_each(
1901 |(window_idx, mut window)| {
1902 let base_row = window_idx * batchsize;
1903 window
1904 .row_iter_mut()
1905 .enumerate()
1906 .for_each(|(row_offset, row)| {
1907 let global_row = base_row + row_offset;
1908 for (col, elem) in row.iter_mut().enumerate() {
1909 *elem = global_row * ncols + col;
1910 }
1911 });
1912 },
1913 );
1914
1915 for row in 0..nrows {
1917 for col in 0..ncols {
1918 let expected = row * ncols + col;
1919 assert_eq!(
1920 m[(row, col)],
1921 expected,
1922 "pos ({}, {}) - {}",
1923 row,
1924 col,
1925 context
1926 );
1927 }
1928 }
1929 }
1930 }
1931 }
1932 }
1933
1934 #[test]
1935 #[cfg(feature = "rayon")]
1936 fn test_par_row_iter_comprehensive() {
1937 use rayon::prelude::*;
1938
1939 let nrows = 7;
1941 let ncols = 5;
1942 let data: Vec<i32> = (0..(nrows * ncols) as i32).collect();
1943 let m = MatrixView::try_from(data.as_slice(), nrows, ncols).unwrap();
1944
1945 let collected_rows: Vec<Vec<i32>> = m.par_row_iter().map(|row| row.to_vec()).collect();
1947
1948 assert_eq!(collected_rows.len(), nrows);
1949
1950 for (row_idx, row) in collected_rows.iter().enumerate() {
1951 assert_eq!(row.len(), ncols);
1952 let expected: Vec<i32> = ((row_idx * ncols)..((row_idx + 1) * ncols))
1953 .map(|x| x as i32)
1954 .collect();
1955 assert_eq!(row, &expected, "row {} mismatch", row_idx);
1956 }
1957
1958 let enumerated_rows: Vec<(usize, Vec<i32>)> = m
1960 .par_row_iter()
1961 .enumerate()
1962 .map(|(idx, row)| (idx, row.to_vec()))
1963 .collect();
1964
1965 let mut sorted_rows = enumerated_rows;
1967 sorted_rows.sort_by_key(|(idx, _)| *idx);
1968
1969 assert_eq!(sorted_rows.len(), nrows);
1970 for (expected_idx, (actual_idx, row)) in sorted_rows.iter().enumerate() {
1971 assert_eq!(*actual_idx, expected_idx);
1972 assert_eq!(row.len(), ncols);
1973 }
1974
1975 let sum: i32 = m.par_row_iter().map(|row| row.iter().sum::<i32>()).sum();
1977
1978 let expected_sum: i32 = data.iter().sum();
1979 assert_eq!(sum, expected_sum);
1980
1981 let target_row = 3;
1983 let found_row = m
1984 .par_row_iter()
1985 .enumerate()
1986 .find_any(|(idx, _)| *idx == target_row)
1987 .map(|(_, row)| row.to_vec());
1988
1989 assert!(found_row.is_some());
1990 let expected_row: Vec<i32> = ((target_row * ncols)..((target_row + 1) * ncols))
1991 .map(|x| x as i32)
1992 .collect();
1993 assert_eq!(found_row.unwrap(), expected_row);
1994 }
1995
1996 #[test]
1997 #[cfg(feature = "rayon")]
1998 fn test_par_row_iter_mut_comprehensive() {
1999 use rayon::prelude::*;
2000 use std::sync::atomic::{AtomicUsize, Ordering};
2001
2002 let nrows = 6;
2003 let ncols = 4;
2004 let mut m = Matrix::new(0u32, nrows, ncols);
2005
2006 m.par_row_iter_mut().enumerate().for_each(|(row_idx, row)| {
2008 for (col_idx, elem) in row.iter_mut().enumerate() {
2009 *elem = (row_idx * ncols + col_idx) as u32;
2010 }
2011 });
2012
2013 for row in 0..nrows {
2015 for col in 0..ncols {
2016 let expected = (row * ncols + col) as u32;
2017 assert_eq!(m[(row, col)], expected, "pos ({}, {})", row, col);
2018 }
2019 }
2020
2021 let counter = AtomicUsize::new(0);
2023 m.par_row_iter_mut().for_each(|row| {
2024 counter.fetch_add(1, Ordering::Relaxed);
2025 for elem in row {
2027 *elem *= 2;
2028 }
2029 });
2030
2031 assert_eq!(counter.load(Ordering::Relaxed), nrows);
2032
2033 for row in 0..nrows {
2035 for col in 0..ncols {
2036 let expected = ((row * ncols + col) * 2) as u32;
2037 assert_eq!(m[(row, col)], expected, "doubled pos ({}, {})", row, col);
2038 }
2039 }
2040 }
2041
2042 #[test]
2043 #[cfg(feature = "rayon")]
2044 fn test_parallel_iterators_with_single_dimensions() {
2045 use rayon::prelude::*;
2046
2047 let data = vec![1, 2, 3, 4, 5];
2049 let single_row = MatrixView::try_from(data.as_slice(), 1, 5).unwrap();
2050
2051 let windows: Vec<_> = single_row.par_window_iter(1).collect();
2052 assert_eq!(windows.len(), 1);
2053 assert_eq!(windows[0].nrows(), 1);
2054 assert_eq!(windows[0].ncols(), 5);
2055
2056 let rows: Vec<_> = single_row.par_row_iter().collect();
2057 assert_eq!(rows.len(), 1);
2058 assert_eq!(rows[0], &[1, 2, 3, 4, 5]);
2059
2060 let data = vec![1, 2, 3, 4, 5];
2062 let single_col = MatrixView::try_from(data.as_slice(), 5, 1).unwrap();
2063
2064 let windows: Vec<_> = single_col.par_window_iter(2).collect();
2065 assert_eq!(windows.len(), 3); assert_eq!(windows[0].nrows(), 2);
2067 assert_eq!(windows[1].nrows(), 2);
2068 assert_eq!(windows[2].nrows(), 1); let rows: Vec<_> = single_col.par_row_iter().collect();
2071 assert_eq!(rows.len(), 5);
2072 for (i, row) in rows.iter().enumerate() {
2073 assert_eq!(row, &[i + 1]);
2074 }
2075
2076 let data = vec![42];
2078 let tiny = MatrixView::try_from(data.as_slice(), 1, 1).unwrap();
2079
2080 let windows: Vec<_> = tiny.par_window_iter(1).collect();
2081 assert_eq!(windows.len(), 1);
2082 assert_eq!(windows[0][(0, 0)], 42);
2083
2084 let rows: Vec<_> = tiny.par_row_iter().collect();
2085 assert_eq!(rows.len(), 1);
2086 assert_eq!(rows[0], &[42]);
2087 }
2088
2089 #[test]
2090 #[cfg(feature = "rayon")]
2091 fn test_parallel_window_properties() {
2092 use rayon::prelude::*;
2093
2094 let data: Vec<usize> = (0..30).collect();
2096 let m = MatrixView::try_from(data.as_slice(), 6, 5).unwrap();
2097
2098 m.par_window_iter(2)
2100 .enumerate()
2101 .for_each(|(window_idx, window)| {
2102 for row_idx in 0..window.nrows() {
2103 for col_idx in 0..window.ncols() {
2104 let global_row = window_idx * 2 + row_idx;
2105 let expected = global_row * 5 + col_idx;
2106 assert_eq!(
2107 window[(row_idx, col_idx)],
2108 expected,
2109 "window {}, pos ({}, {})",
2110 window_idx,
2111 row_idx,
2112 col_idx
2113 );
2114 }
2115 }
2116 });
2117
2118 m.par_window_iter(3)
2120 .enumerate()
2121 .for_each(|(window_idx, window)| {
2122 let slice = window.as_slice();
2123 assert_eq!(slice.len(), window.nrows() * window.ncols());
2124
2125 for (slice_idx, &value) in slice.iter().enumerate() {
2126 let row = slice_idx / window.ncols();
2127 let col = slice_idx % window.ncols();
2128 assert_eq!(
2129 value,
2130 window[(row, col)],
2131 "window {}, slice_idx {}",
2132 window_idx,
2133 slice_idx
2134 );
2135 }
2136 });
2137
2138 m.par_window_iter(2).for_each(|window| {
2140 let rows_via_iter: Vec<_> = window.row_iter().collect();
2141 assert_eq!(rows_via_iter.len(), window.nrows());
2142
2143 for (row_idx, row) in rows_via_iter.iter().enumerate() {
2144 assert_eq!(row.len(), window.ncols());
2145 for (col_idx, &value) in row.iter().enumerate() {
2146 assert_eq!(value, window[(row_idx, col_idx)]);
2147 }
2148 }
2149 });
2150 }
2151
2152 #[test]
2153 #[cfg(feature = "rayon")]
2154 fn test_parallel_performance_characteristics() {
2155 use rayon::prelude::*;
2156 use std::sync::atomic::{AtomicUsize, Ordering};
2157
2158 let nrows = 100;
2160 let ncols = 10;
2161 let mut m = Matrix::new(0usize, nrows, ncols);
2162
2163 let work_counter = AtomicUsize::new(0);
2165
2166 m.par_window_iter_mut(10)
2167 .enumerate()
2168 .for_each(|(window_idx, mut window)| {
2169 work_counter.fetch_add(1, Ordering::Relaxed);
2170
2171 window
2173 .row_iter_mut()
2174 .enumerate()
2175 .for_each(|(row_offset, row)| {
2176 let global_row = window_idx * 10 + row_offset;
2177 for (col, elem) in row.iter_mut().enumerate() {
2178 *elem = global_row * ncols + col;
2179 }
2180 });
2181 });
2182
2183 assert_eq!(work_counter.load(Ordering::Relaxed), 10);
2185
2186 for row in 0..nrows {
2188 for col in 0..ncols {
2189 assert_eq!(m[(row, col)], row * ncols + col);
2190 }
2191 }
2192
2193 let total_sum: usize = m
2195 .par_window_iter(15)
2196 .map(|window| {
2197 window
2198 .row_iter()
2199 .map(|row| row.iter().sum::<usize>())
2200 .sum::<usize>()
2201 })
2202 .sum();
2203
2204 let expected_sum: usize = (0..(nrows * ncols)).sum();
2205 assert_eq!(total_sum, expected_sum);
2206 }
2207
2208 #[test]
2209 #[cfg(feature = "rayon")]
2210 fn test_rayon_trait_bounds_validation() {
2211 use rayon::prelude::*;
2212
2213 let data: Vec<u64> = (0..20).collect();
2215 let m = MatrixView::try_from(data.as_slice(), 4, 5).unwrap();
2216
2217 let _: Vec<_> = m.par_window_iter(2).collect();
2219 let _: Vec<_> = m.par_row_iter().collect();
2220
2221 let mut m = Matrix::new(0u64, 4, 5);
2223
2224 m.par_window_iter_mut(2).for_each(|mut window| {
2226 window.row_iter_mut().for_each(|row| {
2227 for elem in row {
2228 *elem = 42;
2229 }
2230 });
2231 });
2232
2233 m.par_row_iter_mut().for_each(|row| {
2234 for elem in row {
2235 *elem += 1;
2236 }
2237 });
2238
2239 assert!(m.as_slice().iter().all(|&x| x == 43));
2241 }
2242}