1use crate::matrix::{Matrix, MatrixCoordinate};
4use std::iter::FusedIterator;
5
6#[derive(Debug)]
9pub struct MatrixAreaIterator {
10 start: MatrixCoordinate,
11 row_size: usize,
12 size: usize,
13 state: Option<(usize, usize)>,
14}
15
16impl MatrixAreaIterator {
17 #[must_use]
19 pub fn new(start: MatrixCoordinate, end: MatrixCoordinate) -> Self {
20 Self {
21 start,
22 row_size: 1 + (end.row - start.row),
23 size: (1 + (end.column - start.column)) * (1 + (end.row - start.row)),
24 state: Some((0, 0)),
25 }
26 }
27}
28
29impl Iterator for MatrixAreaIterator {
30 type Item = MatrixCoordinate;
31
32 fn next(&mut self) -> Option<Self::Item> {
33 let next = match self.state {
34 Some((front, _)) => MatrixCoordinate::new(
35 front / self.row_size + self.start.column,
36 front % self.row_size + self.start.row,
37 ),
38 None => return None, };
40 self.state = match self.state {
41 Some((front, back)) if self.size - back > front + 1 => Some((front + 1, back)),
42 Some(_) => None, None => None, };
45 Some(next)
46 }
47
48 fn size_hint(&self) -> (usize, Option<usize>) {
49 (self.size, Some(self.size))
50 }
51}
52
53impl DoubleEndedIterator for MatrixAreaIterator {
54 fn next_back(&mut self) -> Option<Self::Item> {
55 let next = match self.state {
56 Some((_, back)) => {
57 let back_index = self.size - 1 - back;
58 MatrixCoordinate::new(
59 back_index / self.row_size + self.start.column,
60 back_index % self.row_size + self.start.row,
61 )
62 }
63 None => return None, };
65 self.state = match self.state {
66 Some((front, back)) if self.size - (back + 1) > front => Some((front, back + 1)),
67 Some(_) => None, None => None, };
70 Some(next)
71 }
72}
73
74impl FusedIterator for MatrixAreaIterator {}
75
76impl ExactSizeIterator for MatrixAreaIterator {}
77
78#[test]
79fn matrix_area_iterator_order() {
80 assert_eq!(
82 9,
83 MatrixAreaIterator::new(MatrixCoordinate::new(0, 0), MatrixCoordinate::new(2, 2))
84 .enumerate()
85 .map(|(i, coord)| assert_eq!(
86 coord,
87 MatrixCoordinate::new(i / 3, i % 3),
88 "Incorrect coordinate for this iteration."
89 ))
90 .count(),
91 "Expected iterator for 3x3 matrix area to iterate 9 times."
92 );
93 assert_eq!(
95 12,
96 MatrixAreaIterator::new(MatrixCoordinate::new(0, 0), MatrixCoordinate::new(3, 2))
97 .enumerate()
98 .map(|(i, coord)| assert_eq!(
99 coord,
100 MatrixCoordinate::new(i / 3, i % 3),
101 "Incorrect coordinate for this iteration."
102 ))
103 .count(),
104 "Expected iterator for 3x4 matrix area to iterate 12 times."
105 );
106 assert_eq!(
108 12,
109 MatrixAreaIterator::new(MatrixCoordinate::new(0, 0), MatrixCoordinate::new(2, 3))
110 .enumerate()
111 .map(|(i, coord)| assert_eq!(
112 coord,
113 MatrixCoordinate::new(i / 4, i % 4),
114 "Incorrect coordinate for this iteration."
115 ))
116 .count(),
117 "Expected iterator for 4x3 matrix area to iterate 12 times."
118 );
119}
120
121#[test]
122fn matrix_area_iterator_with_non_zero_start() {
123 assert_eq!(
125 9,
126 MatrixAreaIterator::new(MatrixCoordinate::new(3, 5), MatrixCoordinate::new(5, 7))
127 .enumerate()
128 .map(|(i, coord)| assert_eq!(
129 coord,
130 MatrixCoordinate::new(i / 3 + 3, i % 3 + 5),
131 "Incorrect coordinate for this iteration."
132 ))
133 .count(),
134 "Expected iterator for 3x3 matrix area to iterate 9 times."
135 );
136 assert_eq!(
138 12,
139 MatrixAreaIterator::new(MatrixCoordinate::new(3, 5), MatrixCoordinate::new(6, 7))
140 .enumerate()
141 .map(|(i, coord)| assert_eq!(
142 coord,
143 MatrixCoordinate::new(i / 3 + 3, i % 3 + 5),
144 "Incorrect coordinate for this iteration."
145 ))
146 .count(),
147 "Expected iterator for 3x4 matrix area to iterate 12 times."
148 );
149 assert_eq!(
151 12,
152 MatrixAreaIterator::new(MatrixCoordinate::new(3, 5), MatrixCoordinate::new(5, 8))
153 .enumerate()
154 .map(|(i, coord)| assert_eq!(
155 coord,
156 MatrixCoordinate::new(i / 4 + 3, i % 4 + 5),
157 "Incorrect coordinate for this iteration."
158 ))
159 .count(),
160 "Expected iterator for 4x3 matrix area to iterate 12 times."
161 );
162}
163
164#[test]
165fn matrix_area_iterator_reversed_order() {
166 assert_eq!(
168 9,
169 MatrixAreaIterator::new(MatrixCoordinate::new(0, 0), MatrixCoordinate::new(2, 2))
170 .enumerate()
171 .rev()
172 .map(|(i, coord)| assert_eq!(
173 coord,
174 MatrixCoordinate::new(i / 3, i % 3),
175 "Incorrect coordinate for this iteration."
176 ))
177 .count(),
178 "Expected iterator for 3x3 matrix area to iterate 9 times."
179 );
180 assert_eq!(
182 12,
183 MatrixAreaIterator::new(MatrixCoordinate::new(0, 0), MatrixCoordinate::new(3, 2))
184 .enumerate()
185 .rev()
186 .map(|(i, coord)| assert_eq!(
187 coord,
188 MatrixCoordinate::new(i / 3, i % 3),
189 "Incorrect coordinate for this iteration."
190 ))
191 .count(),
192 "Expected iterator for 3x4 matrix area to iterate 12 times."
193 );
194 assert_eq!(
196 12,
197 MatrixAreaIterator::new(MatrixCoordinate::new(0, 0), MatrixCoordinate::new(2, 3))
198 .enumerate()
199 .rev()
200 .map(|(i, coord)| assert_eq!(
201 coord,
202 MatrixCoordinate::new(i / 4, i % 4),
203 "Incorrect coordinate for this iteration."
204 ))
205 .count(),
206 "Expected iterator for 4x3 matrix area to iterate 12 times."
207 );
208}
209
210#[test]
211fn matrix_area_iterator_reversed_with_non_zero_start() {
212 assert_eq!(
214 9,
215 MatrixAreaIterator::new(MatrixCoordinate::new(3, 5), MatrixCoordinate::new(5, 7))
216 .enumerate()
217 .rev()
218 .map(|(i, coord)| assert_eq!(
219 coord,
220 MatrixCoordinate::new(i / 3 + 3, i % 3 + 5),
221 "Incorrect coordinate for this iteration."
222 ))
223 .count(),
224 "Expected iterator for 3x3 matrix area to iterate 9 times."
225 );
226 assert_eq!(
228 12,
229 MatrixAreaIterator::new(MatrixCoordinate::new(3, 5), MatrixCoordinate::new(6, 7))
230 .enumerate()
231 .rev()
232 .map(|(i, coord)| assert_eq!(
233 coord,
234 MatrixCoordinate::new(i / 3 + 3, i % 3 + 5),
235 "Incorrect coordinate for this iteration."
236 ))
237 .count(),
238 "Expected iterator for 3x4 matrix area to iterate 12 times."
239 );
240 assert_eq!(
242 12,
243 MatrixAreaIterator::new(MatrixCoordinate::new(3, 5), MatrixCoordinate::new(5, 8))
244 .enumerate()
245 .rev()
246 .map(|(i, coord)| assert_eq!(
247 coord,
248 MatrixCoordinate::new(i / 4 + 3, i % 4 + 5),
249 "Incorrect coordinate for this iteration."
250 ))
251 .count(),
252 "Expected iterator for 4x3 matrix area to iterate 12 times."
253 );
254}
255
256#[derive(Debug)]
258pub struct MatrixIterator<T, const ROWS: usize, const COLUMNS: usize, I>
259where
260 T: Copy,
261 I: Iterator<Item = MatrixCoordinate>,
262{
263 pub(crate) matrix: Matrix<T, ROWS, COLUMNS>,
264 pub(crate) coordinate_iterator: I,
265}
266
267impl<T, const ROWS: usize, const COLUMNS: usize, I> Iterator for MatrixIterator<T, ROWS, COLUMNS, I>
268where
269 T: Copy,
270 I: Iterator<Item = MatrixCoordinate>,
271{
272 type Item = T;
273
274 fn next(&mut self) -> Option<Self::Item> {
275 let coordinate = match self.coordinate_iterator.next() {
276 Some(coord) => coord,
277 None => return None,
278 };
279 Some(self.matrix[coordinate])
281 }
282
283 fn size_hint(&self) -> (usize, Option<usize>) {
284 self.coordinate_iterator.size_hint()
285 }
286}
287
288impl<T, const ROWS: usize, const COLUMNS: usize, I> DoubleEndedIterator
289 for MatrixIterator<T, ROWS, COLUMNS, I>
290where
291 T: Copy,
292 I: Iterator<Item = MatrixCoordinate> + DoubleEndedIterator,
293{
294 fn next_back(&mut self) -> Option<Self::Item> {
295 let coordinate = match self.coordinate_iterator.next_back() {
296 Some(coord) => coord,
297 None => return None,
298 };
299 Some(self.matrix[coordinate])
301 }
302}
303
304impl<T, const ROWS: usize, const COLUMNS: usize, I> FusedIterator
305 for MatrixIterator<T, ROWS, COLUMNS, I>
306where
307 T: Copy,
308 I: Iterator<Item = MatrixCoordinate> + FusedIterator,
309{
310}
311
312impl<T, const ROWS: usize, const COLUMNS: usize, I> ExactSizeIterator
313 for MatrixIterator<T, ROWS, COLUMNS, I>
314where
315 T: Copy,
316 I: Iterator<Item = MatrixCoordinate> + ExactSizeIterator,
317{
318}
319
320impl<T, const ROWS: usize, const COLUMNS: usize> IntoIterator for Matrix<T, ROWS, COLUMNS>
321where
322 T: Copy,
323{
324 type Item = T;
325 type IntoIter = MatrixIterator<T, ROWS, COLUMNS, MatrixAreaIterator>;
326
327 fn into_iter(self) -> Self::IntoIter {
328 Self::IntoIter {
329 matrix: self,
330 coordinate_iterator: MatrixAreaIterator::new(
331 MatrixCoordinate::new(0, 0),
332 MatrixCoordinate::new(COLUMNS - 1, ROWS - 1),
333 ),
334 }
335 }
336}
337
338#[derive(Debug)]
340pub struct MatrixReferenceIterator<'a, T, const ROWS: usize, const COLUMNS: usize, I>
341where
342 T: Copy,
343 I: Iterator<Item = MatrixCoordinate>,
344{
345 pub(crate) matrix: &'a Matrix<T, ROWS, COLUMNS>,
346 pub(crate) coordinate_iterator: I,
347}
348
349impl<'a, T, const ROWS: usize, const COLUMNS: usize, I> Iterator
350 for MatrixReferenceIterator<'a, T, ROWS, COLUMNS, I>
351where
352 T: Copy,
353 I: Iterator<Item = MatrixCoordinate>,
354{
355 type Item = &'a T;
356
357 fn next(&mut self) -> Option<Self::Item> {
358 let coordinate = match self.coordinate_iterator.next() {
359 Some(coord) => coord,
360 None => return None,
361 };
362 Some(&self.matrix[coordinate])
364 }
365
366 fn size_hint(&self) -> (usize, Option<usize>) {
367 self.coordinate_iterator.size_hint()
368 }
369}
370
371impl<T, const ROWS: usize, const COLUMNS: usize, I> FusedIterator
372 for MatrixReferenceIterator<'_, T, ROWS, COLUMNS, I>
373where
374 T: Copy,
375 I: Iterator<Item = MatrixCoordinate> + FusedIterator,
376{
377}
378
379impl<T, const ROWS: usize, const COLUMNS: usize, I> ExactSizeIterator
380 for MatrixReferenceIterator<'_, T, ROWS, COLUMNS, I>
381where
382 T: Copy,
383 I: Iterator<Item = MatrixCoordinate> + ExactSizeIterator,
384{
385}
386
387impl<'a, T, const ROWS: usize, const COLUMNS: usize> IntoIterator for &'a Matrix<T, ROWS, COLUMNS>
388where
389 T: Copy,
390{
391 type Item = &'a T;
392 type IntoIter = MatrixReferenceIterator<'a, T, ROWS, COLUMNS, MatrixAreaIterator>;
393
394 fn into_iter(self) -> Self::IntoIter {
395 Self::IntoIter {
396 matrix: self,
397 coordinate_iterator: MatrixAreaIterator::new(
398 MatrixCoordinate::new(0, 0),
399 MatrixCoordinate::new(COLUMNS - 1, ROWS - 1),
400 ),
401 }
402 }
403}