use std::collections::HashMap;
use std::fmt::Debug;
use std::iter::FusedIterator;
use std::ops::{Index, IndexMut};
use gridly::prelude::*;
#[derive(Debug, Clone)]
pub struct SparseGrid<T: Clone + PartialEq> {
root: Location,
dimensions: Vector,
default: T,
storage: HashMap<Location, T>,
}
impl<T: Clone + PartialEq> SparseGrid<T> {
pub fn new_default(dimensions: impl VectorLike, default: T) -> Self {
Self::new_rooted_default(Location::zero(), dimensions, default)
}
pub fn new_rooted_default(
root: impl LocationLike,
dimensions: impl VectorLike,
default: T,
) -> Self {
Self {
root: root.as_location(),
dimensions: dimensions.as_vector(),
default,
storage: HashMap::new(),
}
}
pub fn get_default(&self) -> &T {
&self.default
}
pub fn clean(&mut self) {
let default = &self.default;
self.storage.retain(move |_, value| value != default);
}
pub fn clear(&mut self) {
self.storage.clear();
}
pub fn occupied_entries(
&self,
) -> impl Iterator<Item = (&Location, &T)> + FusedIterator + Clone {
let default = &self.default;
self.storage
.iter()
.filter(move |(_, value)| *value != default)
}
pub fn occupied_entries_mut(
&mut self,
) -> impl Iterator<Item = (&Location, &mut T)> + FusedIterator {
let default = &self.default;
self.storage
.iter_mut()
.filter(move |(_, value)| value != &default)
}
pub fn occupied_entries_mut_cleaned(
&mut self,
) -> impl Iterator<Item = (&Location, &mut T)> + FusedIterator + ExactSizeIterator {
self.clean();
self.storage.iter_mut()
}
#[inline]
pub fn insert(&mut self, location: impl LocationLike, value: T) -> T {
let location = location.as_location();
let outer_row = self.root.row + self.dimensions.rows;
let outer_column = self.root.column + self.dimensions.columns;
if location.row < self.root.row {
let diff = self.root.row - location.row;
self.root.row = location.row;
self.dimensions.rows += diff;
} else if location.row >= outer_row {
self.dimensions.rows = (location.row - self.root.row) + 1;
}
if location.column < self.root.column {
let diff = self.root.column - location.column;
self.root.column = location.column;
self.dimensions.columns += diff;
} else if location.column >= outer_column {
self.dimensions.columns = (location.column - self.root.column) + 1;
}
unsafe { self.replace_unchecked(location, value) }
}
}
impl<T: Clone + PartialEq + Default> SparseGrid<T> {
pub fn new_rooted(root: impl LocationLike, dimensions: impl VectorLike) -> Self {
Self::new_rooted_default(root, dimensions, T::default())
}
pub fn new(dimensions: impl VectorLike) -> Self {
Self::new_default(dimensions, T::default())
}
}
impl<T: Clone + PartialEq> GridBounds for SparseGrid<T> {
fn dimensions(&self) -> Vector {
self.dimensions
}
fn root(&self) -> Location {
self.root
}
}
impl<T: Clone + PartialEq> Grid for SparseGrid<T> {
type Item = T;
unsafe fn get_unchecked(&self, loc: Location) -> &T {
self.storage.get(&loc).unwrap_or(&self.default)
}
}
impl<T: Clone + PartialEq, L: LocationLike> Index<L> for SparseGrid<T> {
type Output = T;
fn index(&self, location: L) -> &T {
self.get(&location).unwrap_or_else(|bounds_err| {
panic!("{:?} out of bounds: {}", location.as_location(), bounds_err)
})
}
}
impl<T: Clone + PartialEq> GridSetter for SparseGrid<T> {
unsafe fn replace_unchecked(&mut self, location: Location, value: Self::Item) -> Self::Item {
if value == self.default {
self.storage.remove(&location).unwrap_or(value)
} else {
self.storage
.insert(location, value)
.unwrap_or_else(move || self.default.clone())
}
}
unsafe fn set_unchecked(&mut self, location: Location, value: T) {
if value == self.default {
self.storage.remove(&location);
} else {
self.storage.insert(location, value);
}
}
}
impl<T: Clone + PartialEq> GridMut for SparseGrid<T> {
unsafe fn get_unchecked_mut(&mut self, location: Location) -> &mut T {
let default = &self.default;
self.storage
.entry(location)
.or_insert_with(move || default.clone())
}
}
impl<T: Clone + PartialEq, L: LocationLike> IndexMut<L> for SparseGrid<T> {
fn index_mut(&mut self, location: L) -> &mut T {
self.get_mut(&location).unwrap_or_else(|bounds_err| {
panic!("{:?} out of bounds: {}", location.as_location(), bounds_err)
})
}
}