n_circular_array/
lib.rs

1//! # N Circular Array
2//! An n-dimensional circular array.
3//!
4//! ## Features
5//!
6//! - Fixed dimension arrays of any size.
7//! - Element retrieval by `N` dimensional index, range or slice.
8//! - Element insertion to the front or back of any axis.
9//! - `N` dimensional translation over a source array.
10//! - Support for external types through `AsRef<[T]>` and `AsMut<[T]>`.
11//! - Optimized for contiguous memory.
12//! - Thorough testing for arrays of smaller dimensionality.
13//! - No external dependencies.
14//!
15//! ## Mutation
16//!
17//! `n_circular_array` supports the following mutating operations:
18//! - Insert elements to either side of an axis.
19//! - Translate over a source array.
20//! - Mutate individual elements.
21//!
22//! ### Insertion
23//!
24//! Elements are inserted by providing a **row-major** slice or iterator with a length
25//! equal to an **exact** multiple of the given axis length. That is, a call to insert
26//! two rows must be provided **exactly** two rows of elements.
27//!
28//! ```
29//! # use n_circular_array::CircularArrayVec;
30//! # use n_circular_array::CircularMut;
31//! # use n_circular_array::CircularIndex;
32//!
33//! // A 2-dimensional circular array of 3*3 elements.
34//! let mut array = CircularArrayVec::new([3, 3], vec![
35//!     0, 1, 2,
36//!     3, 4, 5,
37//!     6, 7, 8
38//! ]);
39//!
40//! // Push the elements 9..12 (one row) to the front of axis 1.
41//! array.push_front(1, &[
42//!      9, 10, 11,
43//! ]);
44//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
45//!      3,  4,  5,
46//!      6,  7,  8,
47//!      9, 10, 11,
48//! ]);
49//!
50//! // Push the elements 12..18 (two columns) to the front of axis 0.
51//! let axis_len = array.shape()[0];
52//! array.push_front(0, &[12, 13, 14, 15, 16, 17]);
53//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
54//!       5, 12, 13,
55//!       8, 14, 15,
56//!      11, 16, 17,
57//! ]);
58//!
59//! // Push the elements 19..22 (one row) to the back of axis 1.
60//! array.push_back(1, &[
61//!      19, 20, 21,
62//! ]);
63//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
64//!      19, 20, 21,
65//!       5, 12, 13,
66//!       8, 14, 15,
67//! ]);
68//! ```
69//!
70//! ### Translation
71//!
72//! Translation methods simplify mapping the elements of a *source* array to the circular
73//! array. Translation methods expect the array `origin`, or the position of the
74//! `[0; N]` element within the source array, and a translation on an axis. The provided
75//! `el_fn` function will recieve contiguous `[Range<usize>; N]` slices for mapping
76//! the new elements from the source to the circular array. `CircularArray` **only**
77//! handles slicing and mutation, and translation logic (the current translation, out of
78//! bound translation etc.) must be maintained by the user.
79//!
80//! In the following example, rather than passing the `[Range<usize>; N]` slice to a
81//! 3rd-party crate, we define the source array [`Strides`], then call [`Strides::flatten_range`]
82//! to get a single contiguous range for slicing (requires feature `strides`).
83//! ```
84//! # #[cfg(feature = "strides")] {
85//! # use std::ops::Range;
86//! # use n_circular_array::{CircularArray, CircularIndex, CircularMut, Strides};
87//! // A [5, 5] source array.
88//! let src = [
89//!      0,  1,  2,  3,  4,
90//!      5,  6,  7,  8,  9,
91//!     10, 11, 12, 13, 14,
92//!     15, 16, 17, 18, 19,
93//!     20, 21, 22, 23, 24,
94//! ];
95//! // Strides used for flattening `N` dimensional indices.
96//! let src_strides = Strides::new(&[5, 5]);
97//!
98//! // Slice function.
99//! let el_fn = |mut index: [Range<usize>; 2]| {
100//!     &src[src_strides.flatten_range(index)]
101//! };
102//!
103//! // A [3, 3] circular array positioned at `[0, 0]`.
104//! let mut origin = [0, 0];
105//! let mut dst = CircularArray::new([3, 3], vec![
106//!      0,  1,  2,
107//!      5,  6,  7,
108//!     10, 11, 12
109//! ]);
110//!
111//! // Translate by 2 on axis 0 (Pushes 2 columns to front of axis 0).
112//! let axis = 0;
113//! let n = 2;
114//! dst.translate_front(axis, n, origin, el_fn);
115//! origin[axis] += n as usize;
116//!
117//! assert_eq!(dst.iter().cloned().collect::<Vec<usize>>(), &[
118//!      2,  3,  4,
119//!      7,  8,  9,
120//!     12, 13, 14,
121//! ]);
122//!
123//! // Translate by 1 on axis 1 (Pushes 1 row to front of axis 1).
124//! let axis = 1;
125//! let n = 1;
126//! dst.translate_front(axis, n, origin, el_fn);
127//! origin[axis] += n as usize;
128//!
129//! assert_eq!(dst.iter().cloned().collect::<Vec<usize>>(), &[
130//!      7,  8,  9,
131//!     12, 13, 14,
132//!     17, 18, 19,
133//! ]);
134//!
135//! assert_eq!(origin, [2, 1]);
136//! # }
137//! ```
138//!
139//! ## Indexing and Slicing
140//!
141//! `n_circular_array` supports the following indexing operations:
142//! - Access elements by axis slice.
143//! - Access elements by `N` dimensional slice.
144//! - Access individual elements by index.
145//!
146//! ## Slicing an axis
147//!
148//! All elements of an axis can be iterated over by index or range. Calling
149//! [`CircularIndex::iter_index`] returns an iterator of elements of a shape
150//! equal to the shape of the circular array, with the specified axis set to `1`.
151//! Calling [`CircularIndex::iter_range`] returns an iterator of elements of a
152//! shape equal to the shape of the circular array, with the specified axis set to
153//! the length of the given range.
154//!
155//! ```
156//! # use n_circular_array::CircularArrayVec;
157//! # use n_circular_array::CircularIndex;
158//!
159//! // A 3-dimensional circular array of 3*3*2 elements.
160//! let array = CircularArrayVec::new([3, 3, 2], vec![
161//!      0,  1,  2,
162//!      3,  4,  5,
163//!      6,  7,  8,
164//!
165//!      9, 10, 11,
166//!     12, 13, 14,
167//!     15, 16, 17,
168//! ]);
169//!
170//! // Iterate over index 1 of axis 0 (shape [1, 3, 2]).
171//! assert_eq!(array.iter_index(0, 1).cloned().collect::<Vec<usize>>(), &[
172//!      1,
173//!      4,
174//!      7,
175//!
176//!     10,
177//!     13,
178//!     16,
179//! ]);
180//! // Iterate over indices 1..3 of axis 1 (shape [3, 2, 2]).
181//! assert_eq!(array.iter_range(1, 1..3).cloned().collect::<Vec<usize>>(), &[
182//!      3,  4,  5,
183//!      6,  7,  8,
184//!
185//!     12, 13, 14,
186//!     15, 16, 17,
187//! ]);
188//! ```
189//!
190//! ## Slicing the array
191//!
192//! Calling [`CircularIndex::iter_slice`] can be used to iterate over an `N`
193//! dimensional slice of the array. This can be used to limit iteration to an
194//! exact subset of elements.
195//!
196//! ```
197//! # use n_circular_array::CircularArrayVec;
198//! # use n_circular_array::CircularIndex;
199//!
200//! // A 3-dimensional circular array of 3*3*2 elements.
201//! let array = CircularArrayVec::new([3, 3, 2], vec![
202//!      0,  1,  2,
203//!      3,  4,  5,
204//!      6,  7,  8,
205//!
206//!      9, 10, 11,
207//!     12, 13, 14,
208//!     15, 16, 17,
209//! ]);
210//!
211//! // Iterate over:
212//! //     - index 1 of axis 0,
213//! //     - range 0..3 of axis 1 (all elements),
214//! //     - index 1 of axis 2.
215//! // (shape [1, 2, 1], equivalent to [2]).
216//! assert_eq!(array.iter_slice([1..2, 0..3, 1..2]).cloned().collect::<Vec<usize>>(), &[
217//!     10,
218//!     13,
219//!     16,
220//! ]);
221//! // Iterate over:
222//! //     - range 0..2 of axis 0,
223//! //     - range 1..3 of axis 1,
224//! //     - index 0 of axis 2.
225//! // (shape [2, 2, 1], equivalent to [2, 2]).
226//! assert_eq!(array.iter_slice([0..2, 1..3, 0..1]).cloned().collect::<Vec<usize>>(), &[
227//!      3,  4,
228//!      6,  7,
229//! ]);
230//! ```
231//!
232//! `n_circular_array` resizing or reshaping functionality can be achieved by using
233//! [`CircularIndex::iter_slice`] and collecting into a new array.
234//!
235//! ```
236//! # use n_circular_array::CircularArrayVec;
237//! # use n_circular_array::CircularIndex;
238//!
239//! // A 3-dimensional circular array of 3*3*2 elements.
240//! let array3 = CircularArrayVec::new([3, 3, 2], vec![
241//!      0,  1,  2,
242//!      3,  4,  5,
243//!      6,  7,  8,
244//!
245//!      9, 10, 11,
246//!     12, 13, 14,
247//!     15, 16, 17,
248//! ]);
249//!
250//! // Iterate over:
251//! //     - range 0..2 of axis 0,
252//! //     - range 1..3 of axis 1,
253//! //     - index 0 of axis 2.
254//! // (shape [2, 2, 1], equivalent to [2, 2]).
255//! let iter = array3.iter_slice([0..2, 1..3, 0..1]).cloned();
256//!
257//! // A 2-dimensional circular array of 3*2 elements.
258//! let array2 = CircularArrayVec::from_iter([2, 2], iter);
259//!
260//! assert_eq!(array2.iter().cloned().collect::<Vec<usize>>(), &[
261//!      3,  4,
262//!      6,  7,
263//! ]);
264//! ```
265//!
266//! ### Index and IndexMut
267//!
268//! Finally, `n_circular_array` supports [`std::ops::Index`] and [`std::ops::IndexMut`]
269//! taking an `N` dimensional index (`[usize; N]`) as argument.
270//!
271//! ```
272//! # use n_circular_array::CircularArrayVec;
273//! # use n_circular_array::CircularMut;
274//! # use n_circular_array::CircularIndex;
275//!
276//! // A 2-dimensional circular array of 3*3 elements.
277//! let mut array = CircularArrayVec::new([3, 3], vec![
278//!     0, 1, 2,
279//!     3, 4, 5,
280//!     6, 7, 8
281//! ]);
282//!
283//! array[[1, 1]] += 10;
284//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
285//!     0,  1, 2,
286//!     3, 14, 5,
287//!     6,  7, 8
288//! ]);
289//! ```
290//!
291//! # Features
292//!
293//! Feature | Description
294//! ---|---|---
295//! `strides` | Exports [`Strides`] for flattening `N` dimensional indices during translation.
296//!
297//! # Performance
298//!
299//! Wrapping contigous slices over the bounds of an axis reduces cache locality,
300//! especially for the innermost dimensions of any `n > 1` array. Where possible,
301//! an array should be oriented where the majority of operations are performed on the
302//! outermost dimension(s). This will allow `n_circular_array` to take contiguous
303//! slices of memory where possible, which can result in operations being reduced to
304//! as little as a single iteration over a contiguous slice, or a single call to
305//! `copy_from_slice` during mutation.
306//!
307//! External types implementing `AsRef<[T]>` and `AsMut<[T]>` may also improve performance
308//! over `Vec<T>` or `Box<T>`. If necessary, `AsRef<[T]>` and `AsMut<[T]>` can be delegated
309//! to `unsafe` methods, although this is discouraged.
310//!
311//! Finally, for smaller arrays, avoiding a circular array and simply copying (or cloning)
312//! an array window may outperform `n_circular_array`. Benchmark if unsure whether
313//! your use case benefits from `n_circular_array`.
314#[macro_use]
315mod assertions;
316
317mod array;
318mod array_iter;
319
320mod array_index;
321mod array_mut;
322
323mod index;
324mod index_iter;
325
326mod span;
327mod span_iter;
328
329mod strides;
330
331pub use array::{CircularArray, CircularArrayBox, CircularArrayVec};
332pub use array_index::CircularIndex;
333pub use array_mut::CircularMut;
334
335#[cfg(feature = "strides")]
336pub use strides::Strides;