n_circular_array
N Circular Array
An n-dimensional circular array.
Features
- Fixed dimension arrays of any size.
- Element retrieval by
Ndimensional index, range or slice. - Element insertion to the front or back of any axis.
Ndimensional translation over a source array.- Support for external types through
AsRef<[T]>andAsMut<[T]>. - Optimized for contiguous memory.
- Thorough testing for arrays of smaller dimensionality.
- No external dependencies.
Mutation
n_circular_array supports the following mutating operations:
- Insert elements to either side of an axis.
- Translate over a source array.
- Mutate individual elements.
Insertion
Elements are inserted by providing a row-major slice or iterator with a length equal to an exact multiple of the given axis length. That is, a call to insert two rows must be provided exactly two rows of elements.
// A 2-dimensional circular array of 3*3 elements.
let mut array = new;
// Push the elements 9..12 (one row) to the front of axis 1.
array.push_front;
assert_eq!;
// Push the elements 12..18 (two columns) to the front of axis 0.
let axis_len = array.shape;
array.push_front;
assert_eq!;
// Push the elements 19..22 (one row) to the back of axis 1.
array.push_back;
assert_eq!;
Translation
Translation methods simplify mapping the elements of a source array to the circular
array. Translation methods expect the array origin, or the position of the
[0; N] element within the source array, and a translation on an axis. The provided
el_fn function will recieve contiguous [Range<usize>; N] slices for mapping
the new elements from the source to the circular array. CircularArray only
handles slicing and mutation, and translation logic (the current translation, out of
bound translation etc.) must be maintained by the user.
In the following example, rather than passing the [Range<usize>; N] slice to a
3rd-party crate, we define the source array [Strides], then call [Strides::flatten_range]
to get a single contiguous range for slicing (requires feature strides).
// A [5, 5] source array.
let src = ;
// Strides used for flattening `N` dimensional indices.
let src_strides = new;
// Slice function.
let el_fn = ;
// A [3, 3] circular array positioned at `[0, 0]`.
let mut origin = ;
let mut dst = new;
// Translate by 2 on axis 0 (Pushes 2 columns to front of axis 0).
let axis = 0;
let n = 2;
dst.translate_front;
origin += n as usize;
assert_eq!;
// Translate by 1 on axis 1 (Pushes 1 row to front of axis 1).
let axis = 1;
let n = 1;
dst.translate_front;
origin += n as usize;
assert_eq!;
assert_eq!;
Indexing and Slicing
n_circular_array supports the following indexing operations:
- Access elements by axis slice.
- Access elements by
Ndimensional slice. - Access individual elements by index.
Slicing an axis
All elements of an axis can be iterated over by index or range. Calling
[CircularIndex::iter_index] returns an iterator of elements of a shape
equal to the shape of the circular array, with the specified axis set to 1.
Calling [CircularIndex::iter_range] returns an iterator of elements of a
shape equal to the shape of the circular array, with the specified axis set to
the length of the given range.
// A 3-dimensional circular array of 3*3*2 elements.
let array = new;
// Iterate over index 1 of axis 0 (shape [1, 3, 2]).
assert_eq!;
// Iterate over indices 1..3 of axis 1 (shape [3, 2, 2]).
assert_eq!;
Slicing the array
Calling [CircularIndex::iter_slice] can be used to iterate over an N
dimensional slice of the array. This can be used to limit iteration to an
exact subset of elements.
// A 3-dimensional circular array of 3*3*2 elements.
let array = new;
// Iterate over:
// - index 1 of axis 0,
// - range 0..3 of axis 1 (all elements),
// - index 1 of axis 2.
// (shape [1, 2, 1], equivalent to [2]).
assert_eq!;
// Iterate over:
// - range 0..2 of axis 0,
// - range 1..3 of axis 1,
// - index 0 of axis 2.
// (shape [2, 2, 1], equivalent to [2, 2]).
assert_eq!;
n_circular_array resizing or reshaping functionality can be achieved by using
[CircularIndex::iter_slice] and collecting into a new array.
// A 3-dimensional circular array of 3*3*2 elements.
let array3 = new;
// Iterate over:
// - range 0..2 of axis 0,
// - range 1..3 of axis 1,
// - index 0 of axis 2.
// (shape [2, 2, 1], equivalent to [2, 2]).
let iter = array3.iter_slice.cloned;
// A 2-dimensional circular array of 3*2 elements.
let array2 = from_iter;
assert_eq!;
Index and IndexMut
Finally, n_circular_array supports [std::ops::Index] and [std::ops::IndexMut]
taking an N dimensional index ([usize; N]) as argument.
// A 2-dimensional circular array of 3*3 elements.
let mut array = new;
array += 10;
assert_eq!;
Features
Feature | Description
---|---|---
strides | Exports [Strides] for flattening N dimensional indices during translation.
Performance
Wrapping contigous slices over the bounds of an axis reduces cache locality,
especially for the innermost dimensions of any n > 1 array. Where possible,
an array should be oriented where the majority of operations are performed on the
outermost dimension(s). This will allow n_circular_array to take contiguous
slices of memory where possible, which can result in operations being reduced to
as little as a single iteration over a contiguous slice, or a single call to
copy_from_slice during mutation.
External types implementing AsRef<[T]> and AsMut<[T]> may also improve performance
over Vec<T> or Box<T>. If necessary, AsRef<[T]> and AsMut<[T]> can be delegated
to unsafe methods, although this is discouraged.
Finally, for smaller arrays, avoiding a circular array and simply copying (or cloning)
an array window may outperform n_circular_array. Benchmark if unsure whether
your use case benefits from n_circular_array.
License: MIT OR Apache-2.0