devela/data/layout/array/
coord.rs1use crate::{ArrayShape, IteratorFused, is, whilst};
8
9#[doc = crate::_tags!(data_structure iterator)]
10#[doc = crate::_doc_meta!{
12 location("data/layout/array", struct ArrayCoordIter),
13 #[cfg(target_pointer_width = "32")]
14 test_size_of(ArrayCoordIter<2> = 28|224; niche !Option),
15 #[cfg(target_pointer_width = "64")]
16 test_size_of(ArrayCoordIter<2> = 56|448; niche !Option),
17}]
18#[must_use]
45#[derive(Clone, Debug, PartialEq, Eq, Hash)]
46pub struct ArrayCoordIter<const RANK: usize> {
47 shape: ArrayShape<RANK>,
48 front: [usize; RANK],
49 back: [usize; RANK],
50 remaining: usize,
51}
52
53impl<const RANK: usize> ArrayCoordIter<RANK> {
54 pub(crate) const fn new(shape: ArrayShape<RANK>, remaining: usize) -> Self {
56 let mut back = [0; RANK];
57 if remaining != 0 {
58 let lengths = shape.lengths();
59 whilst! { axis in 0..RANK; {
60 back[axis] = lengths[axis] - 1;
61 }}
62 }
63 Self { shape, front: [0; RANK], back, remaining }
64 }
65
66 #[must_use]
68 pub const fn len(&self) -> usize {
69 self.remaining
70 }
71 #[must_use]
73 pub const fn is_empty(&self) -> bool {
74 self.remaining == 0
75 }
76
77 pub const fn shape(&self) -> ArrayShape<RANK> {
79 self.shape
80 }
81
82 #[must_use]
84 pub const fn next(&mut self) -> Option<[usize; RANK]> {
85 is! { self.remaining == 0, return None }
86 let coord = self.front;
87 self.remaining -= 1;
88 is! { self.remaining != 0, self.advance_front() }
89 Some(coord)
90 }
91
92 #[must_use]
94 pub const fn next_back(&mut self) -> Option<[usize; RANK]> {
95 is! { self.remaining == 0, return None }
96 let coord = self.back;
97 self.remaining -= 1;
98 is! { self.remaining != 0, self.advance_back() }
99 Some(coord)
100 }
101
102 pub const fn peek(&self) -> Option<[usize; RANK]> {
104 is! { self.remaining == 0, None, Some(self.front) }
105 }
106 pub const fn peek_back(&self) -> Option<[usize; RANK]> {
108 is! { self.remaining == 0, None, Some(self.back) }
109 }
110
111 const fn advance_front(&mut self) {
112 let lengths = self.shape.lengths();
113 whilst! { axis in 0..RANK; {
114 self.front[axis] += 1;
115 if self.front[axis] < lengths[axis] { return; }
116 self.front[axis] = 0;
117 }}
118 }
119 const fn advance_back(&mut self) {
120 let lengths = self.shape.lengths();
121 whilst! { axis in 0..RANK; {
122 if self.back[axis] != 0 {
123 self.back[axis] -= 1;
124 return;
125 }
126 self.back[axis] = lengths[axis] - 1;
127 }}
128 }
129}
130
131impl<const RANK: usize> Iterator for ArrayCoordIter<RANK> {
134 type Item = [usize; RANK];
135
136 fn next(&mut self) -> Option<Self::Item> {
137 Self::next(self)
138 }
139 fn count(self) -> usize {
140 self.remaining
141 }
142 fn size_hint(&self) -> (usize, Option<usize>) {
143 let len = self.remaining;
144 (len, Some(len))
145 }
146}
147impl<const RANK: usize> DoubleEndedIterator for ArrayCoordIter<RANK> {
148 fn next_back(&mut self) -> Option<Self::Item> {
149 Self::next_back(self)
150 }
151}
152impl<const RANK: usize> ExactSizeIterator for ArrayCoordIter<RANK> {
153 fn len(&self) -> usize {
154 Self::len(self)
155 }
156}
157impl<const RANK: usize> IteratorFused for ArrayCoordIter<RANK> {}
158
159#[cfg(test)]
160mod _test {
161 use super::*;
162 #[cfg(feature = "alloc")]
163 use crate::Vec;
164 use crate::{Array, ArrayLayout, const_assert};
165
166 const COORDS_2_3: [[usize; 2]; 6] = {
167 let shape = ArrayShape::new([2, 3]);
168 let layout = match ArrayLayout::dense_first(shape) {
169 Ok(layout) => layout,
170 Err(_) => panic!("unexpected layout overflow"),
171 };
172 let mut iter = layout.coords();
173 let mut output = [[0; 2]; 6];
174 let mut index = 0;
175 while let Some(coord) = iter.next() {
176 output[index] = coord;
177 index += 1;
178 }
179 assert!(index == 6);
180 assert!(iter.is_empty());
181 output
182 };
183 const LAST_2_3: [usize; 2] = {
184 let shape = ArrayShape::new([2, 3]);
185 let layout = match ArrayLayout::dense_first(shape) {
186 Ok(layout) => layout,
187 Err(_) => panic!("unexpected layout overflow"),
188 };
189 let mut iter = layout.coords();
190 match iter.next_back() {
191 Some(coord) => coord,
192 None => panic!("missing coordinate"),
193 }
194 };
195 #[test]
196 const fn const_coordinate_iteration() {
197 const_assert!(eq COORDS_2_3[0][0], 0);
198 const_assert!(eq COORDS_2_3[0][1], 0);
199 const_assert!(eq COORDS_2_3[1][0], 1);
200 const_assert!(eq COORDS_2_3[1][1], 0);
201 const_assert!(eq COORDS_2_3[2][0], 0);
202 const_assert!(eq COORDS_2_3[2][1], 1);
203 const_assert!(eq COORDS_2_3[5][0], 1);
204 const_assert!(eq COORDS_2_3[5][1], 2);
205 }
206 #[test]
207 const fn const_back_coordinate_iteration() {
208 const_assert!(eq LAST_2_3[0], 1);
209 const_assert!(eq LAST_2_3[1], 2);
210 }
211
212 #[test]
213 #[cfg(feature = "alloc")]
214 fn shape_coordinate_sequence() {
215 let coords: Vec<_> = ArrayShape::new([2, 3]).try_coords().unwrap().collect();
216 assert_eq!(coords, [[0, 0], [1, 0], [0, 1], [1, 1], [0, 2], [1, 2],]);
217 }
218 #[test]
219 #[cfg(feature = "alloc")]
220 fn coordinate_order_is_layout_independent() {
221 let shape = ArrayShape::new([2, 3]);
222 let first: Vec<_> = ArrayLayout::dense_first(shape).unwrap().coords().collect();
223 let last: Vec<_> = ArrayLayout::dense_last(shape).unwrap().coords().collect();
224 assert_eq!(first, last);
225 }
226 #[test]
227 fn scalar_coordinate() {
228 let mut iter = ArrayShape::<0>::new([]).try_coords().unwrap();
229 assert_eq!(iter.len(), 1);
230 assert_eq!(iter.next(), Some([]));
231 assert_eq!(iter.next(), None);
232 assert_eq!(iter.next(), None);
233 }
234 #[test]
235 fn empty_shape_has_no_coordinates() {
236 let mut iter = ArrayShape::new([4, 0, 8]).try_coords().unwrap();
237 assert!(iter.is_empty());
238 assert_eq!(iter.next(), None);
239 }
240 #[test]
241 fn overflowing_shape_rejects_exact_iteration() {
242 let shape = ArrayShape::new([usize::MAX, 2]);
243 assert!(shape.try_coords().is_err());
244 }
245 #[test]
246 fn exact_size_tracks_remaining_coordinates() {
247 let mut iter = ArrayShape::new([2, 2]).try_coords().unwrap();
248 assert_eq!(iter.len(), 4);
249 assert_eq!(iter.size_hint(), (4, Some(4)));
250 assert_eq!(iter.next(), Some([0, 0]));
251 assert_eq!(iter.len(), 3);
252 assert_eq!(iter.next(), Some([1, 0]));
253 assert_eq!(iter.len(), 2);
254 }
255 #[test]
256 fn array_coords_do_not_depend_on_backing() {
257 let layout = ArrayLayout::dense_first(ArrayShape::new([2, 2])).unwrap();
258 let array = Array::try_from_array([0, 1, 2, 3], layout).unwrap();
259 let mut coords = array.coords();
260
261 assert_eq!(coords.next(), Some([0, 0]));
262 assert_eq!(coords.next(), Some([1, 0]));
263 assert_eq!(coords.next(), Some([0, 1]));
264 assert_eq!(coords.next(), Some([1, 1]));
265 assert_eq!(coords.next(), None);
266 }
267}