toodee/
ops.rs

1use core::ops::{Index, IndexMut};
2use core::ptr;
3use core::mem;
4
5use crate::iter::*;
6use crate::view::*;
7use crate::flattenexact::*;
8
9/// A `(col, row)` coordinate in 2D space.
10pub type Coordinate = (usize, usize);
11
12/// An iterator over each "cell" in a 2D array
13pub type Cells<'a, T> = FlattenExact<Rows<'a, T>>;
14/// A mutable iterator over each "cell" in a 2D array
15pub type CellsMut<'a, T> = FlattenExact<RowsMut<'a, T>>;
16
17/// Defines operations common to both `TooDee` and `TooDeeView`. Default implementations are provided
18/// where possible/practical.
19pub trait TooDeeOps<T> : Index<usize, Output=[T]> + Index<Coordinate, Output=T> {
20    
21    /// The number of columns in the area represented by this object.
22    fn num_cols(&self) -> usize;
23    /// The number of rows in the area represented by this object.
24    fn num_rows(&self) -> usize;
25    
26    /// Returns the size/dimensions of the current object.
27    fn size(&self) -> (usize, usize) {
28        (self.num_cols(), self.num_rows())
29    }
30
31    /// Returns `true` if the array contains no elements.
32    fn is_empty(&self) -> bool {
33        self.num_cols() == 0 || self.num_rows() == 0
34    }
35
36    /// Returns a view (or subset) of the current area based on the coordinates provided.
37    /// 
38    /// # Examples
39    /// 
40    /// ```
41    /// use toodee::{TooDee,TooDeeOps};
42    /// let toodee : TooDee<u32> = TooDee::new(10, 5);
43    /// let view = toodee.view((1, 1), (9, 4));
44    /// assert_eq!(view.num_cols(), 8);
45    /// assert_eq!(view.num_rows(), 3);
46    /// ```
47    fn view(&self, start: Coordinate, end: Coordinate) -> TooDeeView<'_, T>;
48    
49    /// Returns an iterator of slices, where each slice represents an entire row.
50    /// 
51    /// # Examples
52    /// 
53    /// ```
54    /// use toodee::{TooDee,TooDeeOps};
55    /// let toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
56    /// let mut sum = 0u32;
57    /// for r in toodee.rows() {
58    ///     sum += r.iter().sum::<u32>();
59    /// }
60    /// assert_eq!(sum, 42*50);
61    /// ```
62    fn rows(&self) -> Rows<'_, T>;
63    
64    /// Returns an iterator over a single column. Note that the `Col` iterator is indexable.
65    /// 
66    /// # Examples
67    /// 
68    /// ```
69    /// use toodee::{TooDee,TooDeeOps};
70    /// let toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
71    /// let mut sum = 0u32;
72    /// for c in toodee.col(1) {
73    ///     sum += c;
74    /// }
75    /// assert_eq!(sum, 42*5);
76    /// ```
77    fn col(&self, col: usize) -> Col<'_, T>;
78
79    /// Returns an iterator that traverses all cells within the area.
80    /// 
81    /// # Examples
82    /// 
83    /// ```
84    /// use toodee::{TooDee,TooDeeOps};
85    /// let toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
86    /// let mut sum = toodee.cells().sum::<u32>();
87    /// assert_eq!(sum, 42*50);
88    /// ```
89    fn cells(&self) -> Cells<'_, T> {
90        FlattenExact::new(self.rows())
91    }
92    
93    /// Returns a row without checking that the row is valid. Generally it's best to use indexing instead, e.g., toodee\[row\]
94    /// 
95    /// # Safety
96    /// 
97    /// This is generally not recommended, use with caution!
98    /// Calling this method with an invalid row is *[undefined behavior]* even if the resulting reference is not used.
99    unsafe fn get_unchecked_row(&self, row: usize) -> &[T];
100
101    /// Returns a cell without checking that the cell coordinate is valid. Generally it's best to use indexing instead, e.g., toodee\[(col, row)\]
102    /// 
103    /// # Safety
104    /// 
105    /// This is generally not recommended, use with caution!
106    /// Calling this method with an invalid coordinate is *[undefined behavior]* even if the resulting reference is not used.
107    unsafe fn get_unchecked(&self, coord: Coordinate) -> &T;
108
109}
110
111/// Defines operations common to both `TooDee` and `TooDeeViewMut`. Default implementations
112/// are provided where possible/practical.
113pub trait TooDeeOpsMut<T> : TooDeeOps<T> + IndexMut<usize,Output=[T]>  + IndexMut<Coordinate, Output=T> {
114
115    /// Returns a mutable view (or subset) of the current area based on the coordinates provided.
116    /// 
117    /// # Examples
118    /// 
119    /// ```
120    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
121    /// let mut toodee : TooDee<u32> = TooDee::new(10, 5);
122    /// let view = toodee.view_mut((1, 1), (9, 4));
123    /// assert_eq!(view.num_cols(), 8);
124    /// assert_eq!(view.num_rows(), 3);
125    /// ```
126    fn view_mut(&mut self, start: Coordinate, end: Coordinate) -> TooDeeViewMut<'_, T>;
127    
128    /// Returns a mutable iterator of slices, where each slice represents an entire row.
129    /// 
130    /// # Examples
131    /// 
132    /// ```
133    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
134    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
135    /// for (i, r) in toodee.rows_mut().enumerate() {
136    ///    r.iter_mut().for_each(|c| *c -= i as u32);
137    /// }
138    /// assert_eq!(toodee.cells().sum::<u32>(), 42*50 - 10 - 20 - 30 - 40);
139    /// ```
140    fn rows_mut(&mut self) -> RowsMut<'_, T>;
141    
142    /// Returns a mutable iterator over a single column. Note that the `ColMut` iterator is indexable.
143    /// 
144    /// # Examples
145    /// 
146    /// ```
147    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
148    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
149    /// for c in toodee.col_mut(4) {
150    ///     *c /= 2;
151    /// }
152    /// assert_eq!(toodee.cells().sum::<u32>(), 42*45 + 21*5);
153    /// ```
154    fn col_mut(&mut self, col: usize) -> ColMut<'_, T>;
155    
156    /// Returns an iterator that traverses all cells within the area.
157    /// 
158    /// # Examples
159    /// 
160    /// ```
161    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
162    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
163    /// for c in toodee.cells_mut() {
164    ///     *c -= 1;
165    /// }
166    /// assert_eq!(toodee.cells().sum::<u32>(), 41*50);
167    /// ```
168    fn cells_mut(&mut self) -> CellsMut<'_, T> {
169        FlattenExact::new(self.rows_mut())
170    }
171    
172    /// Fills the entire area with the specified value.
173    /// 
174    /// # Examples
175    /// 
176    /// ```
177    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
178    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
179    /// let mut view = toodee.view_mut((1, 1), (9, 4));
180    /// view.fill(0);
181    /// assert_eq!(toodee.cells().sum::<u32>(), 42*(50 - 8*3));
182    /// ```
183    fn fill(&mut self, fill: T)
184    where T: Clone {
185        for r in self.rows_mut() {
186            r.fill(fill.clone());
187        }
188    }
189    
190    /// Swap/exchange the data between two columns.
191    /// 
192    /// # Examples
193    /// 
194    /// ```
195    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
196    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
197    /// for c in toodee.col_mut(2) {
198    ///     *c = 1;
199    /// }
200    /// assert_eq!(toodee[(4, 0)], 42);
201    /// toodee.swap_cols(2, 4);
202    /// assert_eq!(toodee[(4, 0)], 1);
203    /// ```
204    fn swap_cols(&mut self, c1: usize, c2: usize) {
205        let num_cols = self.num_cols();
206        assert!(c1 < num_cols);
207        assert!(c2 < num_cols);
208        for r in self.rows_mut() {
209            // The column indices have been checked with asserts (see above), so we can
210            // safely access and swap the elements using `get_unchecked_mut`.
211            unsafe {
212                let pa: *mut T = r.get_unchecked_mut(c1);
213                let pb: *mut T = r.get_unchecked_mut(c2);
214                ptr::swap(pa, pb);
215            }
216        }
217    }
218
219    /// Swap/exchange two cells in the array.
220    ///
221    /// # Panics
222    ///
223    /// Panics if either cell coordinate is out of bounds.
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
229    /// let mut toodee = TooDee::from_vec(3, 3, (0u32..9).collect());
230    /// toodee.swap((0,0),(2, 2));
231    /// assert_eq!(toodee.data(), &[8, 1, 2, 3, 4, 5, 6, 7, 0]);
232    /// ```
233    fn swap(&mut self, mut cell1: Coordinate, mut cell2: Coordinate) {
234        if cell1.1 > cell2.1 {
235            mem::swap(&mut cell1, &mut cell2);
236        }
237        let num_cols = self.num_cols();
238        assert!(cell1.0 < num_cols && cell2.0 < num_cols);
239        let mut iter = self.rows_mut();
240        let row1 = iter.nth(cell1.1).unwrap();
241        if cell1.1 == cell2.1 {
242            unsafe {
243                let pa: *mut T = row1.get_unchecked_mut(cell1.0);
244                let pb: *mut T = row1.get_unchecked_mut(cell2.0);
245                ptr::swap(pa, pb);
246            }
247        } else {
248            let row2 = iter.nth(cell2.1 - cell1.1 - 1).unwrap();
249            unsafe {
250                let pa: *mut T = row1.get_unchecked_mut(cell1.0);
251                let pb: *mut T = row2.get_unchecked_mut(cell2.0);
252                ptr::swap(pa, pb);
253            }
254        }
255    }
256    
257    /// Swap/exchange the data between two rows. Note that this method is overridden in both `TooDee` and `TooDeeOpsMut`.
258    /// This implementation remains in place for other types that may wish to implement the trait.
259    /// 
260    /// # Panics
261    /// 
262    /// Panics if either row index is out of bounds.
263    /// 
264    /// # Examples
265    /// 
266    /// ```
267    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
268    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
269    /// toodee[0].iter_mut().for_each(|v| *v = 1);
270    /// assert_eq!(toodee[(0, 2)], 42);
271    /// toodee.view_mut((0, 0), (10, 5)).swap_rows(0, 2);
272    /// assert_eq!(toodee[(0, 2)], 1);
273    /// ```
274    fn swap_rows(&mut self, mut r1: usize, mut r2: usize) {
275        if r1 == r2 {
276            return;
277        }
278        if r2 < r1 {
279            mem::swap(&mut r1, &mut r2);
280        }
281        let mut iter = self.rows_mut();
282        let tmp = iter.nth(r1).unwrap();
283        tmp.swap_with_slice(iter.nth(r2-r1-1).unwrap());
284    }
285    
286    /// Return the specified rows as mutable slices.
287    /// 
288    /// # Panics
289    ///
290    /// Will panic if `r1` and `r2` are equal, or if either row index is out of bounds.
291    /// 
292    /// # Examples
293    /// 
294    /// ```
295    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
296    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
297    /// let (r1, r2) = toodee.row_pair_mut(0, 4);
298    /// // do something with the row pair
299    /// r1.swap_with_slice(r2);
300    /// ```
301    fn row_pair_mut(&mut self, r1: usize, r2: usize) -> (&mut [T], &mut [T]) {
302        let num_rows = self.num_rows();
303        assert!(r1 < num_rows);
304        assert!(r2 < num_rows);
305        assert_ne!(r1, r2);
306        if r1 < r2 {
307            let mut iter = self.rows_mut();
308            let tmp = iter.nth(r1).unwrap();
309            (tmp, iter.nth(r2-r1-1).unwrap())
310        } else {
311            let mut iter = self.rows_mut();
312            let tmp = iter.nth(r2).unwrap();
313            (iter.nth(r1-r2-1).unwrap(), tmp)
314        }
315    }
316    
317    /// Returns a mutable row without checking that the row is valid. Generally it's best to use indexing instead, e.g., toodee\[row\]
318    /// 
319    /// # Safety
320    /// 
321    /// This is generally not recommended, use with caution!
322    /// Calling this method with an invalid row is *[undefined behavior]* even if the resulting reference is not used.
323    unsafe fn get_unchecked_row_mut(&mut self, row: usize) -> &mut [T];
324
325    /// Returns a mutable cell without checking that the cell coordinate is valid. Generally it's best to use indexing instead, e.g., toodee\[(col, row)\]
326    /// 
327    /// # Safety
328    /// 
329    /// This is generally not recommended, use with caution!
330    /// Calling this method with an invalid coordinate is *[undefined behavior]* even if the resulting reference is not used.
331    unsafe fn get_unchecked_mut(&mut self, coord: Coordinate) -> &mut T;
332
333}
334