1use crate::Matrix;
2use crate::row_index_mapped::{RowIndexMap, RowIndexMappedView};
3
4#[derive(Debug)]
9pub struct VerticallyStridedRowIndexMap {
10 height: usize,
12 stride: usize,
14 offset: usize,
16}
17
18pub type VerticallyStridedMatrixView<Inner> =
19 RowIndexMappedView<VerticallyStridedRowIndexMap, Inner>;
20
21impl VerticallyStridedRowIndexMap {
22 pub fn new_view<T: Send + Sync + Clone, Inner: Matrix<T>>(
36 inner: Inner,
37 stride: usize,
38 offset: usize,
39 ) -> VerticallyStridedMatrixView<Inner> {
40 let height = inner.height().saturating_sub(offset).div_ceil(stride);
43 RowIndexMappedView {
44 index_map: Self {
45 height,
46 stride,
47 offset,
48 },
49 inner,
50 }
51 }
52}
53
54impl RowIndexMap for VerticallyStridedRowIndexMap {
55 fn height(&self) -> usize {
56 self.height
57 }
58
59 fn map_row_index(&self, r: usize) -> usize {
60 r * self.stride + self.offset
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use alloc::vec;
67
68 use super::*;
69 use crate::{Matrix, RowMajorMatrix};
70
71 fn sample_matrix() -> RowMajorMatrix<i32> {
72 RowMajorMatrix::new(
79 vec![10, 11, 12, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52],
80 3,
81 )
82 }
83
84 #[test]
85 fn test_vertically_strided_view_stride_1_offset_0() {
86 let matrix = sample_matrix();
87 let view = VerticallyStridedRowIndexMap::new_view(matrix, 1, 0);
88
89 assert_eq!(view.height(), 5);
90 assert_eq!(view.width(), 3);
91
92 assert_eq!(view.get(0, 0), Some(10));
93 assert_eq!(view.get(1, 1), Some(21));
94 unsafe {
95 assert_eq!(view.get_unchecked(4, 2), 52);
96 }
97 assert_eq!(view.get(5, 0), None); assert_eq!(view.get(0, 3), None); }
100
101 #[test]
102 fn test_vertically_strided_view_stride_2_offset_0() {
103 let matrix = sample_matrix();
104 let view = VerticallyStridedRowIndexMap::new_view(matrix, 2, 0);
105
106 assert_eq!(view.height(), 3);
107 assert_eq!(view.get(0, 0), Some(10)); unsafe {
109 assert_eq!(view.get_unchecked(1, 1), 31); assert_eq!(view.get_unchecked(2, 2), 52); }
112 assert_eq!(view.get(0, 3), None); }
114
115 #[test]
116 fn test_vertically_strided_view_stride_2_offset_1() {
117 let matrix = sample_matrix();
118 let view = VerticallyStridedRowIndexMap::new_view(matrix, 2, 1);
119
120 assert_eq!(view.height(), 2);
121 assert_eq!(view.get(0, 0), Some(20)); unsafe {
123 assert_eq!(view.get_unchecked(1, 1), 41);
124 } }
126
127 #[test]
128 fn test_vertically_strided_view_stride_3_offset_0() {
129 let matrix = sample_matrix();
130 let view = VerticallyStridedRowIndexMap::new_view(matrix, 3, 0);
131
132 assert_eq!(view.height(), 2);
133 assert_eq!(view.get(0, 0), Some(10)); assert_eq!(view.get(1, 1), Some(41)); }
136
137 #[test]
138 fn test_vertically_strided_view_stride_3_offset_1() {
139 let matrix = sample_matrix();
140 let view = VerticallyStridedRowIndexMap::new_view(matrix, 3, 1);
141
142 assert_eq!(view.height(), 2);
143 unsafe {
144 assert_eq!(view.get_unchecked(0, 0), 20); assert_eq!(view.get_unchecked(1, 1), 51); }
147 }
148
149 #[test]
150 fn test_vertically_strided_view_stride_3_offset_2() {
151 let matrix = sample_matrix();
152 let view = VerticallyStridedRowIndexMap::new_view(matrix, 3, 2);
153
154 assert_eq!(view.height(), 1);
155 assert_eq!(view.get(0, 2), Some(32)); }
157
158 #[test]
159 fn test_vertically_strided_view_stride_greater_than_height() {
160 let matrix = sample_matrix();
161 let view = VerticallyStridedRowIndexMap::new_view(matrix, 10, 0);
162
163 assert_eq!(view.height(), 1);
164 assert_eq!(view.get(0, 0), Some(10)); }
166
167 #[test]
168 fn test_vertically_strided_view_stride_greater_than_height_with_valid_offset() {
169 let matrix = sample_matrix(); let view = VerticallyStridedRowIndexMap::new_view(matrix, 10, 4);
171
172 assert_eq!(view.height(), 1);
174 assert_eq!(view.get(0, 2), Some(52)); }
176
177 #[test]
178 fn test_vertically_strided_view_stride_greater_than_height_with_offset_beyond_height() {
179 let matrix = sample_matrix(); let view = VerticallyStridedRowIndexMap::new_view(matrix, 10, 6);
181
182 assert_eq!(view.height(), 0);
184 assert_eq!(view.get(0, 0), None); }
186
187 #[test]
188 fn test_vertically_strided_view_offset_greater_than_stride() {
189 let matrix = sample_matrix();
194 let view = VerticallyStridedRowIndexMap::new_view(matrix, 2, 3);
195
196 assert_eq!(view.height(), 1);
197 assert_eq!(view.get(0, 0), Some(40));
199 assert_eq!(view.get(1, 0), None);
201 }
202
203 #[test]
204 fn test_vertically_strided_view_offset_equal_to_stride() {
205 let matrix = RowMajorMatrix::new(vec![10, 20, 30, 40, 50, 60], 1);
207 let view = VerticallyStridedRowIndexMap::new_view(matrix, 2, 2);
208
209 assert_eq!(view.height(), 2);
210 assert_eq!(view.get(0, 0), Some(30)); assert_eq!(view.get(1, 0), Some(50)); assert_eq!(view.get(2, 0), None);
214 }
215
216 #[test]
217 fn test_vertically_strided_view_exhaustive_height_and_bounds() {
218 for stride in 1..=7usize {
221 for offset in 0..=12usize {
223 let matrix = sample_matrix();
224 let h = matrix.height();
225 let view = VerticallyStridedRowIndexMap::new_view(matrix, stride, offset);
226
227 let expected_height = (offset..h).step_by(stride).count();
229 assert_eq!(
230 view.height(),
231 expected_height,
232 "height mismatch for stride={stride}, offset={offset}"
233 );
234
235 for (i, inner_row) in (offset..h).step_by(stride).enumerate() {
238 assert_eq!(
239 view.get(i, 0),
240 Some(10 * (inner_row as i32 + 1)),
241 "wrong row for stride={stride}, offset={offset}, view row {i}"
242 );
243 }
244
245 assert_eq!(view.get(expected_height, 0), None);
247 }
248 }
249 }
250}