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}