#![forbid(missing_docs, unsafe_code)]
use std::collections::{HashMap, hash_map};
use std::hash::{BuildHasher, RandomState};
use std::ops::{Index, IndexMut};
pub struct TableNd<T, const N: usize> {
size: [usize; N],
matrix: Box<[T]>,
}
pub type Table2d<T> = TableNd<T, 2>;
pub type Table3d<T> = TableNd<T, 3>;
pub type Table4d<T> = TableNd<T, 4>;
pub type Table5d<T> = TableNd<T, 5>;
pub type Table6d<T> = TableNd<T, 6>;
pub type Table7d<T> = TableNd<T, 7>;
pub type Table8d<T> = TableNd<T, 8>;
pub type Table9d<T> = TableNd<T, 9>;
pub type Table10d<T> = TableNd<T, 10>;
pub type Table11d<T> = TableNd<T, 11>;
pub type Table12d<T> = TableNd<T, 12>;
impl<T: Default + Clone, const N: usize> TableNd<T, N> {
pub fn new(size: [usize; N]) -> Self {
Self {
size,
matrix: vec![T::default(); product(&size)].into_boxed_slice(),
}
}
}
impl<T: Clone, const N: usize> TableNd<T, N> {
pub fn init(value: T, size: [usize; N]) -> Self {
Self {
size,
matrix: vec![value; product(&size)].into_boxed_slice(),
}
}
pub fn fill(&mut self, value: T) {
self.matrix.fill(value);
}
}
impl<T, const N: usize> TableNd<T, N> {
pub fn size(&self) -> &[usize; N] {
&self.size
}
}
impl<T, const N: usize> TableNd<Option<T>, N> {
pub fn capacity(&self) -> usize {
product(&self.size)
}
pub fn len(&self) -> usize {
self.matrix.iter().filter(|x| x.is_some()).count()
}
pub fn is_empty(&self) -> bool {
self.matrix.iter().all(|x| x.is_none())
}
}
impl<T, const N: usize> Index<[usize; N]> for TableNd<T, N> {
type Output = T;
fn index(&self, index: [usize; N]) -> &Self::Output {
&self.matrix[flatten_index(&index, &self.size)]
}
}
impl<T, const N: usize> IndexMut<[usize; N]> for TableNd<T, N> {
fn index_mut(&mut self, index: [usize; N]) -> &mut Self::Output {
&mut self.matrix[flatten_index(&index, &self.size)]
}
}
pub struct SparseTableNd<T, const N: usize, S = RandomState> {
size: [usize; N],
matrix: HashMap<usize, T, S>,
}
pub type SparseTable2d<T, S = RandomState> = SparseTableNd<T, 2, S>;
pub type SparseTable3d<T, S = RandomState> = SparseTableNd<T, 3, S>;
pub type SparseTable4d<T, S = RandomState> = SparseTableNd<T, 4, S>;
pub type SparseTable5d<T, S = RandomState> = SparseTableNd<T, 5, S>;
pub type SparseTable6d<T, S = RandomState> = SparseTableNd<T, 6, S>;
pub type SparseTable7d<T, S = RandomState> = SparseTableNd<T, 7, S>;
pub type SparseTable8d<T, S = RandomState> = SparseTableNd<T, 8, S>;
pub type SparseTable9d<T, S = RandomState> = SparseTableNd<T, 9, S>;
pub type SparseTable10d<T, S = RandomState> = SparseTableNd<T, 10, S>;
pub type SparseTable11d<T, S = RandomState> = SparseTableNd<T, 11, S>;
pub type SparseTable12d<T, S = RandomState> = SparseTableNd<T, 12, S>;
impl<T, const N: usize> SparseTableNd<T, N, RandomState> {
pub fn new(size: [usize; N]) -> Self {
product(&size);
Self {
size,
matrix: HashMap::new(),
}
}
}
impl<T, const N: usize, S> SparseTableNd<T, N, S> {
pub fn size(&self) -> &[usize; N] {
&self.size
}
pub fn capacity(&self) -> usize {
product(&self.size)
}
pub fn len(&self) -> usize {
self.matrix.len()
}
pub fn is_empty(&self) -> bool {
self.matrix.is_empty()
}
}
impl<T, const N: usize, S: BuildHasher> SparseTableNd<T, N, S> {
pub fn with_hasher(size: [usize; N], hash_builder: S) -> Self {
product(&size);
Self {
size,
matrix: HashMap::with_hasher(hash_builder),
}
}
pub fn entry(&mut self, index: [usize; N]) -> Entry<'_, T> {
Entry::new(self.matrix.entry(flatten_index(&index, &self.size)))
}
pub fn insert(&mut self, index: [usize; N], value: T) -> Option<T> {
self.matrix.insert(flatten_index(&index, &self.size), value)
}
pub fn remove(&mut self, index: [usize; N]) -> Option<T> {
self.matrix.remove(&flatten_index(&index, &self.size))
}
pub fn get(&self, index: [usize; N]) -> Option<&T> {
self.matrix.get(&flatten_index(&index, &self.size))
}
pub fn get_mut(&mut self, index: [usize; N]) -> Option<&mut T> {
self.matrix.get_mut(&flatten_index(&index, &self.size))
}
}
pub enum Entry<'a, T> {
Occupied(OccupiedEntry<'a, T>),
Vacant(VacantEntry<'a, T>),
}
impl<'a, T> Entry<'a, T> {
fn new(entry: hash_map::Entry<'a, usize, T>) -> Self {
match entry {
hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry(e)),
hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry(e)),
}
}
pub fn or_insert(self, default: T) -> &'a mut T {
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(default),
}
}
pub fn or_insert_with(self, default: impl FnOnce() -> T) -> &'a mut T {
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(default()),
}
}
pub fn and_modify(self, f: impl FnOnce(&mut T)) -> Self {
match self {
Entry::Occupied(mut entry) => {
f(entry.get_mut());
Entry::Occupied(entry)
}
Entry::Vacant(entry) => Entry::Vacant(entry),
}
}
}
pub struct OccupiedEntry<'a, T>(hash_map::OccupiedEntry<'a, usize, T>);
impl<'a, T> OccupiedEntry<'a, T> {
pub fn get(&self) -> &T {
self.0.get()
}
pub fn get_mut(&mut self) -> &mut T {
self.0.get_mut()
}
pub fn into_mut(self) -> &'a mut T {
self.0.into_mut()
}
pub fn insert(&mut self, value: T) -> T {
self.0.insert(value)
}
pub fn remove(self) -> T {
self.0.remove()
}
}
pub struct VacantEntry<'a, T>(hash_map::VacantEntry<'a, usize, T>);
impl<'a, T> VacantEntry<'a, T> {
pub fn insert(self, value: T) -> &'a mut T {
self.0.insert(value)
}
pub fn insert_entry(self, value: T) -> OccupiedEntry<'a, T> {
OccupiedEntry(self.0.insert_entry(value))
}
}
#[track_caller]
fn product<const N: usize>(size: &[usize; N]) -> usize {
size.iter().fold(1, |acc, x| acc.strict_mul(*x))
}
#[track_caller]
fn flatten_index<const N: usize>(index: &[usize; N], size: &[usize; N]) -> usize {
for (i, &x) in index.iter().enumerate() {
assert!(x < size[i], "{x} < {} for index[{i}]", size[i]);
}
let mut idx = 0;
for (i, x) in index.iter().enumerate() {
if i != 0 {
idx *= size[i];
}
idx += x;
}
idx
}
#[cfg(test)]
mod tests {
use super::*;
use std::ops::Deref;
#[test]
fn table_init() {
let table2d = Table2d::init(1, [2, 3]);
assert_eq!(table2d[[0, 0]], 1);
assert_eq!(table2d[[0, 1]], 1);
assert_eq!(table2d[[0, 2]], 1);
assert_eq!(table2d[[1, 0]], 1);
assert_eq!(table2d[[1, 1]], 1);
assert_eq!(table2d[[1, 2]], 1);
}
#[test]
#[should_panic]
fn table_init_overflow() {
Table2d::init(1, [usize::MAX, usize::MAX]);
}
#[test]
fn table_fill() {
let mut table2d = Table2d::new([2, 3]);
table2d.fill(7);
assert_eq!(table2d[[0, 0]], 7);
assert_eq!(table2d[[0, 1]], 7);
assert_eq!(table2d[[0, 2]], 7);
assert_eq!(table2d[[1, 0]], 7);
assert_eq!(table2d[[1, 1]], 7);
assert_eq!(table2d[[1, 2]], 7);
}
#[test]
fn table_index() {
let table2d = Table2d {
size: [2, 4],
matrix: vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice(),
};
assert_eq!(table2d[[0, 0]], 0);
assert_eq!(table2d[[0, 1]], 1);
assert_eq!(table2d[[0, 2]], 2);
assert_eq!(table2d[[0, 3]], 3);
assert_eq!(table2d[[1, 0]], 4);
assert_eq!(table2d[[1, 1]], 5);
assert_eq!(table2d[[1, 2]], 6);
assert_eq!(table2d[[1, 3]], 7);
}
#[test]
fn table_index_mut() {
let mut table2d = Table2d {
size: [2, 4],
matrix: vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice(),
};
table2d[[1, 1]] = 9;
assert_eq!(table2d.matrix.deref(), &[0, 1, 2, 3, 4, 9, 6, 7]);
}
}