#![deny(missing_docs)]
use std::ops::{Index, IndexMut};
#[derive(Debug, Eq, PartialEq)]
pub struct Array2D<T: Clone> {
array: Vec<T>,
num_rows: usize,
num_columns: usize,
}
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
IndexOutOfBounds(usize, usize),
}
impl<T: Clone> Array2D<T> {
pub fn fill_with(element: T, num_rows: usize, num_columns: usize) -> Self {
let total_len = num_rows * num_columns;
let array = vec![element; total_len];
Array2D {
array,
num_rows,
num_columns,
}
}
pub fn from_rows(elements: &[Vec<T>]) -> Self {
let row_len = elements.get(0).map(Vec::len).unwrap_or(0);
if !elements.iter().all(|row| row.len() == row_len) {
panic!("Rows were not all {} elements long", row_len);
}
Array2D {
array: flatten(elements),
num_rows: elements.len(),
num_columns: row_len,
}
}
pub fn from_columns(elements: &[Vec<T>]) -> Self {
let column_len = elements.get(0).map(Vec::len).unwrap_or(0);
if !elements.iter().all(|column| column.len() == column_len) {
panic!("Columns were not all {} elements long", column_len);
}
let num_rows = column_len;
let num_columns = elements.len();
let indices_row_major =
(0..num_rows).flat_map(move |row| (0..num_columns).map(move |column| (row, column)));
let array = indices_row_major
.map(|(row, column)| elements[column][row].clone())
.collect();
Array2D {
array,
num_rows,
num_columns,
}
}
pub fn from_row_major(elements: &[T], num_rows: usize, num_columns: usize) -> Self {
let total_len = num_rows * num_columns;
if total_len != elements.len() {
panic!(
"The number of elements ({}) did not match the expected size ({})",
elements.len(),
total_len
);
}
Array2D {
array: elements.to_vec(),
num_rows,
num_columns,
}
}
pub fn from_column_major(elements: &[T], num_rows: usize, num_columns: usize) -> Self {
let total_len = num_rows * num_columns;
if total_len != elements.len() {
panic!(
"The number of elements ({}) did not match the expected size ({})",
elements.len(),
total_len
);
}
let indices_row_major =
(0..num_rows).flat_map(move |row| (0..num_columns).map(move |column| (row, column)));
let array = indices_row_major
.map(|(row, column)| {
let index = column * num_rows + row;
elements[index].clone()
})
.collect();
Array2D {
array,
num_rows,
num_columns,
}
}
pub fn num_rows(&self) -> usize {
self.num_rows
}
pub fn num_columns(&self) -> usize {
self.num_columns
}
pub fn num_elements(&self) -> usize {
self.num_rows * self.num_columns
}
pub fn row_len(&self) -> usize {
self.num_columns
}
pub fn column_len(&self) -> usize {
self.num_rows
}
pub fn get(&self, row: usize, column: usize) -> Option<&T> {
self.get_index(row, column).map(|index| &self.array[index])
}
pub fn get_mut(&mut self, row: usize, column: usize) -> Option<&mut T> {
self.get_index(row, column)
.map(move |index| &mut self.array[index])
}
pub fn set(&mut self, row: usize, column: usize, element: T) -> Result<(), Error> {
self.get_index(row, column)
.map(|index| self.array[index] = element)
.ok_or_else(|| Error::IndexOutOfBounds(row, column))
}
pub fn elements_row_major_iter(&self) -> impl Iterator<Item = &T> {
self.array.iter()
}
pub fn elements_column_major_iter(&self) -> impl Iterator<Item = &T> {
(0..self.num_columns)
.flat_map(move |column| (0..self.num_rows).map(move |row| &self[(row, column)]))
}
pub fn row_iter(&self, row_index: usize) -> impl Iterator<Item = &T> {
let start = self.get_index(row_index, 0).expect(&format!(
"Row index, {}, was out of bounds (>= number of rows, {})",
row_index, self.num_rows,
));
let end = start + self.row_len();
self.array[start..end].iter()
}
pub fn column_iter(&self, column_index: usize) -> impl Iterator<Item = &T> {
if column_index > self.num_columns {
panic!(
"Column index, {}, was out of bounds (>= number of columns, {})",
column_index, self.num_columns,
);
}
(0..self.column_len()).map(move |row_index| &self[(row_index, column_index)])
}
pub fn rows_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &T>> {
(0..self.num_rows()).map(move |row_index| self.row_iter(row_index))
}
pub fn columns_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &T>> {
(0..self.num_columns).map(move |column_index| self.column_iter(column_index))
}
pub fn as_rows(&self) -> Vec<Vec<T>> {
self.rows_iter()
.map(|row_iter| row_iter.cloned().collect())
.collect()
}
pub fn as_columns(&self) -> Vec<Vec<T>> {
self.columns_iter()
.map(|column_iter| column_iter.cloned().collect())
.collect()
}
pub fn as_row_major(&self) -> Vec<T> {
self.elements_row_major_iter().cloned().collect()
}
pub fn as_column_major(&self) -> Vec<T> {
self.elements_column_major_iter().cloned().collect()
}
fn get_index(&self, row: usize, column: usize) -> Option<usize> {
if row < self.num_rows && column < self.num_columns {
Some(row * self.row_len() + column)
} else {
None
}
}
}
impl<T: Clone> Index<(usize, usize)> for Array2D<T> {
type Output = T;
fn index(&self, indices: (usize, usize)) -> &Self::Output {
let (row, column) = indices;
self.get(row, column).unwrap()
}
}
impl<T: Clone> IndexMut<(usize, usize)> for Array2D<T> {
fn index_mut(&mut self, indices: (usize, usize)) -> &mut Self::Output {
let (row, column) = indices;
self.get_mut(row, column).unwrap()
}
}
fn flatten<T: Clone>(nested: &[Vec<T>]) -> Vec<T> {
nested.iter().flat_map(|row| row.clone()).collect()
}