devela/data/list/array/d2/methods/coords.rs
1// devela::data::list::array::d2::methods::coords
2//
3//! 2-dimensional array coordinates and indexing methods
4//
5
6use crate::{iif, Array2d, IndexOutOfBounds, Storage};
7
8/// Helper macro for implementing common methods generic on storage order.
9macro_rules! impl_maj {
10 () => {
11 impl_maj![true:r:"row", false:c:"column"];
12 impl_maj![false:c:"column", true:r:"row"];
13 };
14
15 // $RMAJ: true for row-major order
16 // $D1: the major dimension name (not used)
17 // $D1long: the major dimension long name
18 // $D2: the other dimension name
19 // $D2long: the other dimension long name
20 // $CMAJ: false for row-major order
21 ( $RMAJ:literal:$D1:ident:$D1long:literal,
22 $CMAJ:literal:$D2:ident:$D2long:literal) => { $crate::paste! {
23
24 // T, S
25 #[doc = "# Single element indexing (" $D1long "-major order)"]
26 impl<T, const C: usize, const R: usize, const CR: usize, S: Storage>
27 Array2d<T, C, R, CR, $RMAJ, S>
28 {
29 /* get_ref */
30
31 /// Returns a reference to the element at the given 2D coordinates
32 #[doc = "in the current " $D1long "-major order."]
33 /// # Errors
34 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
35 pub fn get_ref(&self, col_row: [usize; 2]) -> Result<&T, IndexOutOfBounds> {
36 Self::get_index(col_row).map(|idx| &self.data[idx])
37 }
38 /// Returns a reference to the element at the given 2D coordinates
39 #[doc = "in the current " $D1long "-major order."]
40 /// # Panics
41 /// Panics if the coordinates are out of bounds.
42 #[must_use]
43 pub fn get_ref_unchecked(&self, col_row: [usize; 2]) -> &T {
44 &self.data.data[Self::get_index_unchecked(col_row)]
45 }
46
47 /* get_mut */
48
49 /// Returns an exclusive reference to the element at the given 2D coordinates
50 #[doc = "in the current " $D1long "-major order."]
51 /// # Errors
52 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
53 pub fn get_mut(&mut self, col_row: [usize; 2]) -> Result<&mut T, IndexOutOfBounds> {
54 Self::get_index(col_row).map(|idx| &mut self.data[idx])
55 }
56 /// Returns an exclusive reference to the element at the given 2D coordinates
57 #[doc = "in the current " $D1long "-major order."]
58 /// # Panics
59 /// Panics if the coordinates are out of bounds.
60 #[must_use]
61 pub fn get_mut_unchecked(&mut self, col_row: [usize; 2]) -> &mut T {
62 &mut self.data.data[Self::get_index_unchecked(col_row)]
63 }
64
65 /* set */
66
67 /// Sets the element at the given 2D coordinates
68 #[doc = "in the current " $D1long "-major order."]
69 /// # Errors
70 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
71 pub fn set(&mut self, element: T, col_row: [usize; 2]) -> Result<(), IndexOutOfBounds> {
72 self.get_mut(col_row).map(|e| {
73 *e = element;
74 })
75 }
76 /// Sets the element at the given 2D coordinates
77 #[doc = "in the current " $D1long "-major order."]
78 /// # Panics
79 /// Panics if the coordinates are out of bounds.
80 pub fn set_unchecked(&mut self, element: T, col_row: [usize; 2]) {
81 let e = self.get_mut_unchecked(col_row);
82 *e = element;
83 }
84 }
85
86 // T: Clone, S
87 // # Single element clone
88 impl<T: Clone, const C: usize, const R: usize, const CR: usize, S: Storage>
89 Array2d<T, C, R, CR, $RMAJ, S>
90 {
91 /* get */
92
93 /// Returns a clone of the element at the given 2D coordinates
94 #[doc = "in the current " $D1long "-major order."]
95 /// # Errors
96 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
97 pub fn get(&self, col_row: [usize; 2]) -> Result<T, IndexOutOfBounds> {
98 Self::get_index(col_row).map(|idx| self.data[idx].clone())
99 }
100 /// Returns a clone of the element at the given 2D coordinates
101 #[doc = "in the current " $D1long "-major order."]
102 /// # Panics
103 /// Panics if the coordinates are out of bounds.
104 #[must_use]
105 pub fn get_unchecked(&self, col_row: [usize; 2]) -> T {
106 self.data.data[Self::get_index_unchecked(col_row)].clone()
107 }
108 }
109
110 /* methods in opposite-order */
111
112 // T, S
113 #[doc = "# Single element indexing (using opposite " $D2long "-major order)"]
114 impl<T, const C: usize, const R: usize, const CR: usize, S: Storage>
115 Array2d<T, C, R, CR, $RMAJ, S>
116 {
117 /* get_ref (opposite order) */
118
119 /// Returns a reference to the element at the given 2D coordinates
120 #[doc = "in the opposite " $D2long "-major order."]
121 /// # Errors
122 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
123 pub fn [<get_ref_ $D2 maj>](&self, col_row: [usize; 2])
124 -> Result<&T, IndexOutOfBounds> {
125 Self::[<get_index_ $D2 maj>](col_row).map(|idx| &self.data[idx])
126 }
127 /// Returns a reference to the element at the given 2D coordinates
128 #[doc = "in the opposite " $D2long "-major order."]
129 /// # Panics
130 /// Panics if the coordinates are out of bounds.
131 #[must_use]
132 pub fn [<get_ref_ $D2 maj_unchecked>](&self, col_row: [usize; 2]) -> &T {
133 &self.data.data[Self::[<get_index_ $D2 maj_unchecked>](col_row)]
134 }
135
136 /* get_mut (opposite order) */
137
138 /// Returns an exclusive reference to the element at the given 2D coordinates
139 #[doc = "in the opposite " $D2long "-major order."]
140 /// # Errors
141 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
142 pub fn [<get_mut_ $D2 maj>](&mut self, col_row: [usize; 2])
143 -> Result<&mut T, IndexOutOfBounds> {
144 Self::[<get_index_ $D2 maj>](col_row).map(|idx| &mut self.data[idx])
145 }
146 /// Returns an exclusive reference to the element at the given 2D coordinates
147 #[doc = "in the opposite " $D2long "-major order."]
148 /// # Panics
149 /// Panics if the coordinates are out of bounds.
150 #[must_use]
151 pub fn [<get_mut_ $D2 maj_unchecked>](&mut self, col_row: [usize; 2]) -> &mut T {
152 &mut self.data.data[Self::[<get_index_ $D2 maj_unchecked>](col_row)]
153 }
154
155 /* set (opposite order) */
156
157 /// Sets the element at the given 2D coordinates
158 #[doc = "in the opposite " $D2long "-major order."]
159 /// # Errors
160 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
161 pub fn [<set_ $D2 maj>](&mut self, element: T, col_row: [usize; 2])
162 -> Result<(), IndexOutOfBounds> {
163 self.[<get_mut_ $D2 maj>](col_row).map(|e| {
164 *e = element;
165 })
166 }
167 /// Sets the element at the given 2D coordinates
168 #[doc = "in the opposite " $D2long "-major order."]
169 /// # Panics
170 /// Panics if the coordinates are out of bounds.
171 pub fn [<set_ $D2 maj_unchecked>](&mut self, element: T, col_row: [usize; 2]) {
172 let e = self.[<get_mut_ $D2 maj_unchecked>](col_row);
173 *e = element;
174 }
175
176 /* indexing (opposite order) */
177
178 /// Calculates the 1D array index from the given 2D coordinates
179 #[doc = "in the opposite " $D2long "-major order."]
180 /// # Errors
181 /// Returns [`IndexOutOfBounds`] if the resulting index is `> CR`.
182 pub const fn [<get_index_ $D2 maj>](col_row: [usize; 2])
183 -> Result<usize, IndexOutOfBounds> {
184 Array2d::<T, C, R, CR, $CMAJ, S>::[<get_index>](col_row)
185 }
186 /// Calculates the 1D array index from the given 2D coordinates
187 #[doc = "in the opposite " $D2long "-major order."]
188 #[must_use]
189 pub const fn [<get_index_ $D2 maj_unchecked>](col_row: [usize; 2]) -> usize {
190 Array2d::<T, C, R, CR, $CMAJ, S>::[<get_index_unchecked>](col_row)
191 }
192
193 /// Calculates the 2D coordinates from the given 1D array index
194 #[doc = "in the opposite " $D2long "-major order."]
195 /// # Errors
196 /// Returns [`IndexOutOfBounds`] if `index` is `> CR`.
197 pub const fn [<get_coords_ $D2 maj>](index: usize)
198 -> Result<[usize; 2], IndexOutOfBounds> {
199 Array2d::<T, C, R, CR, $CMAJ, S>::[<get_coords>](index)
200 }
201 /// Calculates the 2D coordinates from the given 1D array index
202 #[doc = "in the opposite " $D2long "-major order."]
203 pub const fn [<get_coords_ $D2 maj_unchecked>](index: usize) -> [usize; 2] {
204 Array2d::<T, C, R, CR, $CMAJ, S>::[<get_coords_unchecked>](index)
205 }
206 }
207
208 // T: Clone, S
209 impl<T: Clone, const C: usize, const R: usize, const CR: usize, S: Storage>
210 Array2d<T, C, R, CR, $RMAJ, S>
211 {
212 /* get (opposite order) */
213
214 /// Returns a clone of the element at the given 2D coordinates
215 #[doc = "in the opposite " $D2long "-major order."]
216 /// # Errors
217 /// Returns [`IndexOutOfBounds`] if the coordinates are out of bounds.
218 pub fn [<get_ $D2 maj>](&self, col_row: [usize; 2]) -> Result<T, IndexOutOfBounds> {
219 Self::[<get_index_ $D2 maj>](col_row).map(|idx| self.data[idx].clone())
220 }
221 /// Returns a clone of the element at the given 2D coordinates
222 #[doc = "in the opposite " $D2long "-major order."]
223 /// # Panics
224 /// Panics if the coordinates are out of bounds.
225 #[must_use]
226 pub fn [<get_ $D2 maj_unchecked>](&self, col_row: [usize; 2]) -> T {
227 self.data.data[Self::[<get_index_ $D2 maj_unchecked>](col_row)].clone()
228 }
229 }
230 }};
231}
232impl_maj![];
233
234/* storage order specific implementations */
235
236// T, S
237/// # Fundamental indexing methods in row-major order.
238impl<T, const C: usize, const R: usize, const CR: usize, S: Storage> Array2d<T, C, R, CR, true, S> {
239 /// Calculates the 1D array index from the given 2D coordinates
240 /// in the current row-major order.
241 /// # Errors
242 /// Returns [`IndexOutOfBounds`] if the resulting index is `>= CR`.
243 pub const fn get_index(col_row: [usize; 2]) -> Result<usize, IndexOutOfBounds> {
244 let idx = Self::get_index_unchecked(col_row);
245 iif![idx < CR; Ok(idx); Err(IndexOutOfBounds(Some(idx)))]
246 }
247 /// Calculates the 1D array index from the given 2D coordinates
248 /// in the current row-major order.
249 // # Performance
250 // This function seems to be 2x faster than
251 // [`get_coords_unchecked`](#method.get_coords_unchecked).
252 // BENCH: 0.63 ns
253 #[must_use]
254 pub const fn get_index_unchecked(col_row: [usize; 2]) -> usize {
255 col_row[1] * R + col_row[0]
256 }
257
258 /// Calculates the 2D coordinates from the given 1D array index
259 /// in the current row-major order.
260 /// # Errors
261 /// Returns [`IndexOutOfBounds`] if `index` is `>= CR`.
262 pub const fn get_coords(index: usize) -> Result<[usize; 2], IndexOutOfBounds> {
263 iif![index < CR; Ok(Self::get_coords_unchecked(index)); Err(IndexOutOfBounds(Some(index)))]
264 }
265 /// Calculates the 2D coordinates from the given 1D array index
266 /// in the current row-major order.
267 // # Performance
268 // This function seems to be 2x slower than
269 // [`get_index_unchecked`](#method.get_index_unchecked).
270 // BENCH: 1.4 ns
271 #[must_use]
272 pub const fn get_coords_unchecked(index: usize) -> [usize; 2] {
273 [index % R, index / R]
274 }
275}
276
277// T, S
278/// # Fundamental indexing methods in column-major order.
279impl<T, const C: usize, const R: usize, const CR: usize, S: Storage>
280 Array2d<T, C, R, CR, false, S>
281{
282 /// Calculates the 1D array index from the given 2D coordinates
283 /// in the current column-major order.
284 /// # Errors
285 /// Returns [`IndexOutOfBounds`] if the resulting index is `>= CR`.
286 pub const fn get_index(col_row: [usize; 2]) -> Result<usize, IndexOutOfBounds> {
287 let idx = Self::get_index_unchecked(col_row);
288 iif![idx < CR; Ok(idx); Err(IndexOutOfBounds(Some(idx)))]
289 }
290 /// Calculates the 1D array index from the given 2D coordinates
291 /// in the current column-major order.
292 // # Performance
293 // This function seems to be 1.5x faster than
294 // [`get_coords_unchecked`](#method.get_coords_unchecked-1).
295 // BENCH: 0.62 ns
296 #[must_use]
297 pub const fn get_index_unchecked(col_row: [usize; 2]) -> usize {
298 col_row[0] * C + col_row[1]
299 }
300
301 /// Calculates the 2D coordinates from the given 1D array index
302 /// in the current column-major order.
303 /// # Errors
304 /// Returns [`IndexOutOfBounds`] if `index` is `>= CR`.
305 pub const fn get_coords(index: usize) -> Result<[usize; 2], IndexOutOfBounds> {
306 iif![index < CR; Ok(Self::get_coords_unchecked(index)); Err(IndexOutOfBounds(Some(index)))]
307 }
308 /// Calculates the 2D coordinates from the given 1D array index
309 /// in the current column-major order.
310 // # Performance
311 // This function seems to be 1.5x slower than
312 // [`get_index_unchecked`](#method.get_index_unchecked-1).
313 // BENCH: 0.94 ns
314 #[must_use]
315 pub const fn get_coords_unchecked(index: usize) -> [usize; 2] {
316 [index / C, index % C]
317 }
318}