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]
    }
}