n_circular_array 0.4.2

An n-dimensional circular array.
Documentation
//! # N Circular Array
//! An n-dimensional circular array.
//!
//! ## Features
//!
//! - Fixed dimension arrays of any size.
//! - Element retrieval by `N` dimensional index, range or slice.
//! - Element insertion to the front or back of any axis.
//! - `N` dimensional translation over a source array.
//! - Element iteration in sequentual or contiguous order.
//! - Support for external types through `AsRef<[T]>` and `AsMut<[T]>`.
//! - 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.
//!
//! ```
//! # use n_circular_array::{CircularArrayVec, CircularIndex, CircularMut};
//! // A 2-dimensional circular array of 3*3 elements.
//! let mut array = CircularArrayVec::new([3, 3], vec![
//!     0, 1, 2,
//!     3, 4, 5,
//!     6, 7, 8
//! ]);
//!
//! // Push the elements 9..12 (one row) to the front of axis 1.
//! array.push_front(1, &[
//!      9, 10, 11,
//! ]);
//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
//!      3,  4,  5,
//!      6,  7,  8,
//!      9, 10, 11,
//! ]);
//!
//! // Push the elements 12..18 (two columns) to the front of axis 0.
//! let axis_len = array.shape()[0];
//! array.push_front(0, &[12, 13, 14, 15, 16, 17]);
//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
//!       5, 12, 13,
//!       8, 14, 15,
//!      11, 16, 17,
//! ]);
//!
//! // Push the elements 19..22 (one row) to the back of axis 1.
//! array.push_back(1, &[
//!      19, 20, 21,
//! ]);
//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
//!      19, 20, 21,
//!       5, 12, 13,
//!       8, 14, 15,
//! ]);
//! ```
//!
//! ### 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`](strides::Strides),
//! then call [`Strides::flatten_range`](strides::Strides::flatten_range) to get
//! a single contiguous range for slicing (requires feature `strides`).
//! ```
//! # #[cfg(feature = "strides")] {
//! # use std::ops::Range;
//! # use n_circular_array::{CircularArray, CircularIndex, CircularMut, Strides};
//! // A [5, 5] source array.
//! let src = [
//!      0,  1,  2,  3,  4,
//!      5,  6,  7,  8,  9,
//!     10, 11, 12, 13, 14,
//!     15, 16, 17, 18, 19,
//!     20, 21, 22, 23, 24,
//! ];
//! // Strides used for flattening `N` dimensional indices.
//! let src_strides = Strides::new(&[5, 5]);
//!
//! // Slice function.
//! let el_fn = |mut index: [Range<usize>; 2]| {
//!     &src[src_strides.flatten_range(index)]
//! };
//!
//! // A [3, 3] circular array positioned at `[0, 0]`.
//! let mut origin = [0, 0];
//! let mut dst = CircularArray::new([3, 3], vec![
//!      0,  1,  2,
//!      5,  6,  7,
//!     10, 11, 12
//! ]);
//!
//! // Translate by 2 on axis 0 (Pushes 2 columns to front of axis 0).
//! let axis = 0;
//! let n = 2;
//! dst.translate_front(axis, n, origin, el_fn);
//! origin[axis] += n as usize;
//!
//! assert_eq!(dst.iter().cloned().collect::<Vec<usize>>(), &[
//!      2,  3,  4,
//!      7,  8,  9,
//!     12, 13, 14,
//! ]);
//!
//! // Translate by 1 on axis 1 (Pushes 1 row to front of axis 1).
//! let axis = 1;
//! let n = 1;
//! dst.translate_front(axis, n, origin, el_fn);
//! origin[axis] += n as usize;
//!
//! assert_eq!(dst.iter().cloned().collect::<Vec<usize>>(), &[
//!      7,  8,  9,
//!     12, 13, 14,
//!     17, 18, 19,
//! ]);
//!
//! assert_eq!(origin, [2, 1]);
//! # }
//! ```
//!
//! ## Indexing and Slicing
//!
//! `n_circular_array` supports the following indexing operations:
//! - Access elements by axis slice.
//! - Access elements by `N` dimensional 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.
//!
//! ```
//! # use n_circular_array::{CircularArrayVec, CircularIndex};
//! // A 3-dimensional circular array of 3*3*2 elements.
//! let array = CircularArrayVec::new([3, 3, 2], vec![
//!      0,  1,  2,
//!      3,  4,  5,
//!      6,  7,  8,
//!
//!      9, 10, 11,
//!     12, 13, 14,
//!     15, 16, 17,
//! ]);
//!
//! // Iterate over index 1 of axis 0 (shape [1, 3, 2]).
//! assert_eq!(array.iter_index(0, 1).cloned().collect::<Vec<usize>>(), &[
//!      1,
//!      4,
//!      7,
//!
//!     10,
//!     13,
//!     16,
//! ]);
//! // Iterate over indices 1..3 of axis 1 (shape [3, 2, 2]).
//! assert_eq!(array.iter_range(1, 1..3).cloned().collect::<Vec<usize>>(), &[
//!      3,  4,  5,
//!      6,  7,  8,
//!
//!     12, 13, 14,
//!     15, 16, 17,
//! ]);
//! ```
//!
//! ### 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.
//!
//! ```
//! # use n_circular_array::{CircularArrayVec, CircularIndex};
//! // A 3-dimensional circular array of 3*3*2 elements.
//! let array = CircularArrayVec::new([3, 3, 2], vec![
//!      0,  1,  2,
//!      3,  4,  5,
//!      6,  7,  8,
//!
//!      9, 10, 11,
//!     12, 13, 14,
//!     15, 16, 17,
//! ]);
//!
//! // 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!(array.iter_slice([1..2, 0..3, 1..2]).cloned().collect::<Vec<usize>>(), &[
//!     10,
//!     13,
//!     16,
//! ]);
//! // 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!(array.iter_slice([0..2, 1..3, 0..1]).cloned().collect::<Vec<usize>>(), &[
//!      3,  4,
//!      6,  7,
//! ]);
//! ```
//!
//! `n_circular_array` resizing or reshaping functionality can be achieved by using
//! [`CircularIndex::iter_slice`] and collecting into a new array.
//!
//! ```
//! # use n_circular_array::{CircularArrayVec, CircularIndex};
//! // A 3-dimensional circular array of 3*3*2 elements.
//! let array3 = CircularArrayVec::new([3, 3, 2], vec![
//!      0,  1,  2,
//!      3,  4,  5,
//!      6,  7,  8,
//!
//!      9, 10, 11,
//!     12, 13, 14,
//!     15, 16, 17,
//! ]);
//!
//! // 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([0..2, 1..3, 0..1]).cloned();
//!
//! // A 2-dimensional circular array of 3*2 elements.
//! let array2 = CircularArrayVec::from_iter([2, 2], iter);
//!
//! assert_eq!(array2.iter().cloned().collect::<Vec<usize>>(), &[
//!      3,  4,
//!      6,  7,
//! ]);
//! ```
//!
//! ### Index and IndexMut
//!
//! Finally, `n_circular_array` supports [`std::ops::Index`] and [`std::ops::IndexMut`]
//! taking an `N` dimensional index (`[usize; N]`) as argument.
//!
//! ```
//! # use n_circular_array::{CircularArrayVec, CircularIndex, CircularMut};
//! // A 2-dimensional circular array of 3*3 elements.
//! let mut array = CircularArrayVec::new([3, 3], vec![
//!     0, 1, 2,
//!     3, 4, 5,
//!     6, 7, 8
//! ]);
//!
//! array[[1, 1]] += 10;
//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
//!     0,  1, 2,
//!     3, 14, 5,
//!     6,  7, 8
//! ]);
//! ```
//!
//! ## Raw and Contiguous Operations
//!
//! `n_circular_array` also include operations for iterating over elements of the
//! array while only offsetting the data of a subset of axes. `_contiguous` suffixed
//! operations return identical elements to their offset counterpart, however element
//! order is contiguous (arbitrary for most cases). `_raw` suffixed operations consider
//! data as having no offset and therefore all elements are contiguous. Both `_contiguous`
//! and `_raw` operations are more performant than their fully offset counterparts,
//! although the difference is negligable for smaller arrays.
//!
//! ```
//! # use n_circular_array::{CircularArrayVec, CircularIndex, CircularMut};
//! // A 2-dimensional circular array of 3*3 elements.
//! let mut array = CircularArrayVec::new([3, 3, 2], vec![
//!      0,  1,  2,
//!      3,  4,  5,
//!      6,  7,  8,
//!
//!      9, 10, 11,
//!     12, 13, 14,
//!     15, 16, 17,
//! ]);
//!
//! array.push_front(0, &[100].repeat(6));
//! array.push_front(1, &[100].repeat(6));
//!
//! assert_eq!(array.iter().cloned().collect::<Vec<usize>>(), &[
//!       4,   5, 100,
//!       7,   8, 100,
//!     100, 100, 100,
//!
//!      13,  14, 100,
//!      16,  17, 100,
//!     100, 100, 100
//! ]);
//! // All axes are offset.
//! assert_eq!(array.iter_index(1, 0).cloned().collect::<Vec<usize>>(), &[
//!       4,   5, 100,
//!      13,  14, 100,
//! ]);
//! // Identical to above, however element order is arbitrary.
//! assert_eq!(array.iter_index_contiguous(1, 0).cloned().collect::<Vec<usize>>(), &[
//!      100,  4,   5,
//!      100, 13,  14,
//! ]);
//!
//! // Our operation does not care about order.
//! assert_eq!(array.iter_index_contiguous(0, 0).filter(|val| **val >= 100).count(), 2);
//! ```
//!
//! ## Feature Flags
//!
//! Feature | Description
//! ---|---
//! `strides` | Exports [`Strides`](strides::Strides) for flattening `N` dimensional indices during translation.
//!
//! ## Performance
//!
//! Initial version of `n_circular_array` prioritized functionality over performance.
//! In the current state, performance can be significantly improved. If performance is
//! found to make `n_circular_array` impractical, please open an issue and optimization
//! can be prioritized.
//!
//! Circular arrays fragment sequentual data over axis bound(s). The following can
//! improve performance:
//! - If possible, orient an array 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]>` can 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.
//! - 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`.
//!
#[macro_use]
mod assertions;

mod array;
mod array_iter;

mod array_index;
mod array_mut;

mod index;
mod index_iter;

mod span;
mod span_iter;

mod strides;

pub use array::{CircularArray, CircularArrayBox, CircularArrayVec};
pub use array_index::CircularIndex;
pub use array_mut::CircularMut;

#[cfg(feature = "strides")]
pub use strides::Strides;