1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
use std::ops::{Index, IndexMut};
/// This struct represents a two-dimensional window into a one-dimensional `Vec`. This is
/// accomplished through taking either a `columns` parameter, and dividing the size of the `Vec`
/// evenly into `rows` based on its length, or by taking `rows` and `columns` directly, trusting
/// that the caller provided correct values. The latter option provides a zero-cost abstraction.
///
/// # Example
/// ```
/// # use ilyvion_util::multi_dimensional::Vec2D;
/// let mut window = Vec2D::from(vec![0u32; 8], 2);
/// window[0][1] = 1;
/// window[1][1] = 2;
/// window[2][1] = 3;
/// window[3][1] = 4;
///
/// let values = window.into_inner();
///
/// assert_eq!(values, [0, 1, 0, 2, 0, 3, 0, 4]);
/// ```
#[derive(Debug)]
pub struct Vec2D<T> {
raw: Vec<T>,
rows: usize,
columns: usize,
}
impl<T> Vec2D<T> {
/// Creates a new `Vec2D` with rows divided into `columns` length (i.e., `w[rows][columns]`)
/// placing the result of `func` in each respective entry.
///
/// # Panics
///
/// If `rows * columns > usize::MAX`.
///
/// # Examples
/// ```
/// # use ilyvion_util::multi_dimensional::Vec2D;
/// let v = Vec2D::new_with(2, 2, |y, x| 100 * y + 10 * x);
/// let values = v.into_inner();
///
/// assert_eq!(values, [0, 10, 100, 110]);
/// ```
pub fn new_with<F>(rows: usize, columns: usize, mut func: F) -> Self
where
F: FnMut(usize, usize) -> T,
{
let raw: Vec<_> = (0..rows
.checked_mul(columns)
.expect("rows * columns > usize::MAX"))
.map(|count| func(count / columns, count % columns))
.collect();
Self { raw, rows, columns }
}
/// Creates a new `Vec2D` with rows divided into `columns` length. (I.e., `w[rows][columns]`)
///
/// # Panics
///
/// If the length of `raw` cannot be divided evenly into `column`s
#[must_use]
pub fn from(raw: Vec<T>, columns: usize) -> Self {
let rows = raw.len() / columns;
if raw.len() % columns != 0 {
panic!("The length of raw must divide evenly into columns.");
}
Self { raw, rows, columns }
}
/// Creates a new `Vec2D` divided into `rows` number of slices with `columns` entries each.
///
/// Providing incorrect values for `rows` and `columns` will most likely lead to run-time panics
/// due to indexing outside the range of the [`Vec`].
///
/// Using this constructor gives you an essentially zero-cost abstraction.
#[must_use]
pub fn from_unchecked(raw: Vec<T>, rows: usize, columns: usize) -> Self {
Self { raw, rows, columns }
}
/// Unwraps this `Vec2D<T>`, returning the underlying [`Vec`].
#[must_use]
pub fn into_inner(self) -> Vec<T> {
self.raw
}
}
impl<T: Default> Vec2D<T> {
/// Creates a new `Vec2D` with rows divided into `columns` length (i.e., `w[rows][columns]`)
/// with `T::default()` in every entry.
///
/// # Panics
///
/// If `rows * columns > usize::MAX`.
#[must_use]
pub fn new(rows: usize, columns: usize) -> Self {
Self::new_with(rows, columns, |_, _| T::default())
}
}
impl<T> Index<usize> for Vec2D<T> {
type Output = [T];
fn index(&self, row: usize) -> &Self::Output {
assert!(row < self.rows);
&self.raw.as_slice()[row * self.columns..][..self.columns]
}
}
impl<T> IndexMut<usize> for Vec2D<T> {
fn index_mut(&mut self, row: usize) -> &mut Self::Output {
assert!(row < self.rows);
&mut self.raw.as_mut_slice()[row * self.columns..][..self.columns]
}
}
impl<T> Index<(usize, usize)> for Vec2D<T> {
type Output = T;
fn index(&self, index: (usize, usize)) -> &Self::Output {
&self[index.0][index.1]
}
}
impl<T> IndexMut<(usize, usize)> for Vec2D<T> {
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
&mut self[index.0][index.1]
}
}