const generic expr based fixed length array manipulation crate
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs394
1 files changed, 394 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..dcc52d9
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,394 @@
+//! a collection of useful features for working with arrays
+#![cfg_attr(not(test), no_std)]
+#![allow(incomplete_features, internal_features)]
+#![feature(
+ generic_const_exprs,
+ core_intrinsics,
+ iter_intersperse,
+ maybe_uninit_array_assume_init,
+ inline_const,
+ array_windows,
+ iter_map_windows
+)]
+#![warn(
+ clippy::undocumented_unsafe_blocks,
+ clippy::missing_const_for_fn,
+ clippy::missing_safety_doc,
+ clippy::suboptimal_flops,
+ unsafe_op_in_unsafe_fn,
+ clippy::dbg_macro,
+ clippy::use_self,
+ missing_docs
+)]
+
+/// The prelude. You should
+/// ```
+/// use atools::prelude::*;
+/// ```
+pub mod prelude {
+ #[doc(inline)]
+ pub use super::{
+ pervasive::prelude::*, range, splat, Array, ArrayTools, Chunked, CollectArray, Couple,
+ DropFront, Join, Pop, Trunc, Tuple,
+ };
+ #[doc(inline)]
+ pub use core::array::from_fn;
+}
+
+use core::{array::from_fn, mem::ManuallyDrop as MD};
+pub mod pervasive;
+mod tuple;
+pub use tuple::*;
+
+/// Convenience function for when clonage is required; prefer `[T; N]` if possible. Also useful if `N` should be inferred.
+pub fn splat<T: Clone, const N: usize>(a: T) -> [T; N] {
+ from_fn(|_| a.clone())
+}
+
+const fn id<T>(x: T) -> T {
+ x
+}
+
+/// Creates a array of indices.
+/// ```
+/// # use atools::prelude::*;
+/// assert_eq!(range::<5>(), [0, 1, 2, 3, 4]);
+/// ```
+pub fn range<const N: usize>() -> [usize; N] {
+ from_fn(id)
+}
+
+/// Collect an iterator into a array.
+pub trait CollectArray<T> {
+ /// Collect an iterator into a array.
+ ///
+ /// # Panics
+ ///
+ /// if the array isn't big enough.
+ fn carr<const N: usize>(&mut self) -> [T; N];
+}
+
+impl<T, I: Iterator<Item = T>> CollectArray<T> for I {
+ fn carr<const N: usize>(&mut self) -> [T; N] {
+ from_fn(|_| self.next().unwrap())
+ }
+}
+
+/// Pop parts of a array.
+/// Use
+/// ```
+/// let [t, arr @ ..] = [1, 2];
+/// ```
+/// when possible. If the length of the array is a const generic, use
+/// ```
+/// # use atools::prelude::*;
+/// let (t, arr) = [1, 2].pop_front();
+/// ```
+pub trait Pop<T, const N: usize> {
+ /// Pop the front of a array.
+ /// ```
+ /// # use atools::prelude::*;
+ /// let (t, arr) = b"abc".pop_front();
+ /// # assert_eq!(t, b'a');
+ /// # assert_eq!(arr, *b"bc");
+ /// ```
+ fn pop_front(self) -> (T, [T; N - 1]);
+ /// Pop the back (end) of a array.
+ /// ```
+ /// # use atools::prelude::*;
+ /// let (arr, t) = [0.1f32, 0.2, 0.3].pop();
+ /// # assert_eq!(arr, [0.1, 0.2]);
+ /// assert_eq!(t, 0.3);
+ /// ```
+ fn pop(self) -> ([T; N - 1], T);
+}
+
+impl<T, const N: usize> Pop<T, N> for [T; N] {
+ fn pop_front(self) -> (T, [T; N - 1]) {
+ // SAFETY: hi crater
+ unsafe { core::intrinsics::transmute_unchecked(self) }
+ }
+
+ fn pop(self) -> ([T; N - 1], T) {
+ // SAFETY: i am evil
+ unsafe { core::intrinsics::transmute_unchecked(self) }
+ }
+}
+
+/// Removes the last element of a array. The opposite of [`DropFront`].
+pub trait Trunc<T, const N: usize> {
+ /// Remove the last element of a array.
+ /// You can think of this like <code>a.[pop()](Pop::pop).0</code>
+ /// ```
+ /// # use atools::prelude::*;
+ /// let a = [1u64, 2].trunc();
+ /// assert_eq!(a, [1]);
+ /// ```
+ fn trunc(self) -> [T; N - 1];
+}
+
+impl<const N: usize, T> Trunc<T, N> for [T; N] {
+ fn trunc(self) -> [T; N - 1] {
+ self.pop().0
+ }
+}
+
+/// Remove the first element of a array. The opposite of [`Trunc`].
+pub trait DropFront<T, const N: usize> {
+ /// Removes the first element.
+ fn drop_front(self) -> [T; N - 1];
+}
+
+impl<const N: usize, T> DropFront<T, N> for [T; N] {
+ fn drop_front(self) -> [T; N - 1] {
+ self.pop_front().1
+ }
+}
+
+/// Join scalars together.
+pub trait Join<T, const N: usize, const O: usize, U> {
+ /// Join a array and an scalar together. For joining two arrays together, see [`Couple`].
+ /// ```
+ /// # use atools::prelude::*;
+ /// let a = [1, 2].join(3);
+ /// let b = 1.join([2, 3]);
+ /// let c = 1.join(2).join(3);
+ /// ```
+ fn join(self, with: U) -> [T; N + O];
+}
+
+/// Couple two arrays together.
+pub trait Couple<T, const N: usize, const O: usize> {
+ /// Couple two arrays together. This could have been [`Join`], but the methods would require disambiguation.
+ /// ```
+ /// # use atools::prelude::*;
+ /// let a = 1.join(2).couple([3, 4]);
+ /// ```
+ fn couple(self, with: [T; O]) -> [T; N + O];
+}
+
+impl<T, const N: usize, const O: usize> Couple<T, N, O> for [T; N] {
+ fn couple(self, with: [T; O]) -> [T; N + O] {
+ self.into_iter().chain(with).carr()
+ }
+}
+
+impl<T, const N: usize> Join<T, N, 1, T> for [T; N] {
+ fn join(self, with: T) -> [T; N + 1] {
+ self.couple([with])
+ }
+}
+
+impl<T> Join<T, 1, 1, T> for T {
+ fn join(self, with: T) -> [T; 2] {
+ [self, with]
+ }
+}
+
+impl<T, const O: usize> Join<T, 1, O, [T; O]> for T {
+ fn join(self, with: [T; O]) -> [T; 1 + O] {
+ [self].couple(with)
+ }
+}
+
+pub(crate) const fn assert_zero(x: usize) -> usize {
+ if x != 0 {
+ panic!("expected zero");
+ } else {
+ 0
+ }
+}
+
+/// 🍪
+#[allow(private_bounds)]
+pub trait Chunked<T, const N: usize> {
+ /// Chunks.
+ /// This will compile fail if `N ∤ (does not divide) C`
+ /// ```
+ /// # use atools::prelude::*;
+ /// assert_eq!(range::<6>().chunked::<3>(), [[0, 1, 2], [3, 4, 5]]);
+ /// ```
+ #[allow(private_bounds)]
+ fn chunked<const C: usize>(self) -> [[T; C]; N / C]
+ where
+ // N % C == 0
+ [(); assert_zero(N % C)]:;
+}
+
+impl<const N: usize, T> Chunked<T, N> for [T; N] {
+ #[allow(private_bounds)]
+ fn chunked<const C: usize>(self) -> [[T; C]; N / C]
+ where
+ [(); assert_zero(N % C)]:,
+ {
+ // SAFETY: N != 0 && wont leak as N % C == 0.
+ unsafe { MD::new(self).as_ptr().cast::<[[T; C]; N / C]>().read() }
+ }
+}
+
+/// Array tools.
+pub trait ArrayTools<T, const N: usize> {
+ /// Skip `BY` elements.
+ fn skip<const BY: usize>(self) -> [T; N - BY];
+ /// Skip every `BY` elements.
+ ///
+ /// ```
+ /// # use atools::prelude::*;
+ /// let x = range::<5>().step::<2>();
+ /// assert_eq!(x, [0, 2, 4]);
+ /// let x = range::<20>().step::<5>();
+ /// assert_eq!(x, [0, 5, 10, 15]);
+ /// assert_eq!(range::<50>().step::<3>(), [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48]);
+ /// ```
+ fn step<const STEP: usize>(self) -> [T; 1 + (N - 1) / (STEP)];
+ /// Zip arrays together.
+ fn zip<U>(self, with: [U; N]) -> [(T, U); N];
+ /// Intersperse a element in between items.
+ /// ```
+ /// # use atools::prelude::*;
+ /// let x = range::<3>().intersperse(5);
+ /// assert_eq!(x, [0, 5, 1, 5, 2]);
+ /// ```
+ fn intersperse(self, with: T) -> [T; (N * 2) - 1]
+ where
+ T: Clone;
+ /// Run a function on every element.
+ fn each(self, apply: impl FnMut(T));
+ /// Embed the index.
+ fn enumerate(self) -> [(T, usize); N];
+ /// Take `M` elements, discarding the rest.
+ /// ```
+ /// # use atools::prelude::*;
+ /// assert_eq!(range::<50>().take::<5>(), range::<5>());
+ /// ```
+ fn take<const M: usize>(self) -> [T; M];
+ /// Get the sliding windows of this array.
+ /// ```
+ /// # use atools::prelude::*;
+ /// assert_eq!(range::<5>().windowed::<2>(), [&[0, 1], &[1, 2], &[2, 3], &[3, 4]]);
+ /// ```
+ fn windowed<const W: usize>(&self) -> [&[T; W]; N - W + 1];
+ /// Inspect every element of this array.
+ fn inspect(self, f: impl FnMut(&T)) -> Self;
+ /// Reverse this array.
+ fn rev(self) -> Self;
+ /// Interleave items from two arrays.
+ /// ```
+ /// # use atools::prelude::*;
+ /// assert_eq!([0u8, 2, 4].interleave([1, 3, 5]), [0, 1, 2, 3, 4, 5]);
+ /// ```
+ fn interleave(self, with: [T; N]) -> [T; N * 2];
+ /// [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) (`A  ×  B`) of two arrays.
+ /// ```
+ /// # use atools::prelude::*;
+ /// assert_eq!([1u64, 2].cartesian_product(&["Π", "Σ"]), [(1, "Π"), (1, "Σ"), (2, "Π"), (2, "Σ")]);
+ /// ```
+ fn cartesian_product<U: Clone, const M: usize>(&self, with: &[U; M]) -> [(T, U); N + M]
+ where
+ T: Clone;
+ /// Sorts it. This uses <code>[[T](slice)]::[sort_unstable](slice::sort_unstable)</code>.
+ fn sort(self) -> Self
+ where
+ T: Ord;
+ /// Sum of the array.
+ fn sum(self) -> T
+ where
+ T: core::iter::Sum<T>;
+ /// Product of the array.
+ fn product(self) -> T
+ where
+ T: core::iter::Product<T>;
+}
+
+impl<T, const N: usize> ArrayTools<T, N> for [T; N] {
+ fn skip<const BY: usize>(self) -> [T; N - BY] {
+ self.into_iter().skip(BY).carr()
+ }
+ fn step<const STEP: usize>(self) -> [T; 1 + (N - 1) / (STEP)] {
+ self.into_iter().step_by(STEP).carr()
+ }
+ fn zip<U>(self, with: [U; N]) -> [(T, U); N] {
+ self.into_iter().zip(with).carr()
+ }
+
+ fn intersperse(self, with: T) -> [T; (N * 2) - 1]
+ where
+ T: Clone,
+ {
+ self.into_iter().intersperse(with).carr()
+ }
+
+ fn each(self, apply: impl FnMut(T)) {
+ self.into_iter().for_each(apply);
+ }
+
+ fn enumerate(self) -> [(T, usize); N] {
+ let mut n = 0;
+ self.map(|x| {
+ let o = n;
+ n += 1;
+ (x, o)
+ })
+ }
+
+ fn take<const M: usize>(self) -> [T; M] {
+ self.into_iter().take(M).carr()
+ }
+
+ fn windowed<const W: usize>(&self) -> [&[T; W]; N - W + 1] {
+ self.array_windows().carr()
+ }
+
+ fn inspect(self, f: impl FnMut(&T)) -> Self {
+ self.iter().for_each(f);
+ self
+ }
+
+ fn rev(self) -> Self {
+ self.into_iter().rev().carr()
+ }
+
+ fn interleave(self, with: [T; N]) -> [T; N * 2] {
+ let mut which = true;
+ let mut a = self.into_iter();
+ let mut b = with.into_iter();
+ from_fn(|_| {
+ which = !which;
+ match which {
+ false => a.next().unwrap(),
+ true => b.next().unwrap(),
+ }
+ })
+ }
+
+ fn cartesian_product<U: Clone, const M: usize>(&self, with: &[U; M]) -> [(T, U); N + M]
+ where
+ T: Clone,
+ {
+ self.iter()
+ .flat_map(|a| with.iter().map(move |b| (a.clone(), b.clone())))
+ .carr()
+ }
+
+ fn sort(mut self) -> Self
+ where
+ T: Ord,
+ {
+ <[T]>::sort_unstable(&mut self);
+ self
+ }
+
+ fn sum(self) -> T
+ where
+ T: core::iter::Sum<T>,
+ {
+ self.into_iter().sum()
+ }
+
+ fn product(self) -> T
+ where
+ T: core::iter::Product<T>,
+ {
+ self.into_iter().product()
+ }
+}