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 131 132 133 134 135 136
use std::collections::HashMap;
use fnv::FnvHashMap;
use crate::config::{Entity, Position};
/// A structure to keep information for [`Entity`] as a key.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct EntityMap<T> {
// we have a global type to allocate in on stack.
// because most of the time no changes are made to the [`EntityMap`].
global: T,
columns: FnvHashMap<usize, T>,
rows: FnvHashMap<usize, T>,
cells: FnvHashMap<Position, T>,
}
impl<T> EntityMap<T> {
/// Creates an empty [`EntityMap`].
pub fn new(global: T) -> Self {
Self {
global,
rows: FnvHashMap::default(),
columns: FnvHashMap::default(),
cells: FnvHashMap::default(),
}
}
/// Verifies whether anything was set beside a global entry.
pub fn is_empty(&self) -> bool {
self.columns.is_empty() && self.rows.is_empty() && self.cells.is_empty()
}
/// Get a value for an [`Entity`].
pub fn get(&self, entity: Entity) -> &T {
if self.rows.is_empty() && self.columns.is_empty() && self.cells.is_empty() {
return &self.global;
}
match entity {
Entity::Column(col) => self.columns.get(&col).unwrap_or(&self.global),
Entity::Row(row) => self.rows.get(&row).unwrap_or(&self.global),
Entity::Cell(row, col) => {
// todo: optimize;
//
// Cause we can change rows/columns/cells separately we need to check them separately.
// But we often doing this checks in `Grid::fmt` and I believe if we could optimize it it could be beneficial.
//
// Haven't found a solution for that yet.
//
// I was wondering if there is a hash function like.
// Apparently it doesn't make sense cause we will reset columns/rows on cell insert which is not what we want.
//
// ```
// hash(column, row) == hash(column) == hash(row)
// ```
//
// ref: https://opendsa-server.cs.vt.edu/ODSA/Books/Everything/html/Sparse.html
// ref: https://users.rust-lang.org/t/make-hash-return-same-value-whather-the-order-of-element-of-a-tuple/69932/13
self.cells
.get(&(row, col))
.or_else(|| self.columns.get(&col))
.or_else(|| self.rows.get(&row))
.unwrap_or(&self.global)
}
Entity::Global => &self.global,
}
}
/// Removes a value for an [`Entity`].
pub fn remove(&mut self, entity: Entity) {
match entity {
Entity::Global => {
self.cells.clear();
self.rows.clear();
self.columns.clear();
}
Entity::Column(col) => self.cells.retain(|&(_, c), _| c != col),
Entity::Row(row) => self.cells.retain(|&(r, _), _| r != row),
Entity::Cell(row, col) => {
self.cells.remove(&(row, col));
}
}
}
}
impl<T: Clone> EntityMap<T> {
/// Set a value for an [`Entity`].
pub fn insert(&mut self, entity: Entity, value: T) {
match entity {
Entity::Column(col) => {
for &row in self.rows.keys() {
self.cells.insert((row, col), value.clone());
}
self.columns.insert(col, value);
}
Entity::Row(row) => {
for &col in self.columns.keys() {
self.cells.insert((row, col), value.clone());
}
self.rows.insert(row, value);
}
Entity::Cell(row, col) => {
self.cells.insert((row, col), value);
}
Entity::Global => {
self.remove(Entity::Global);
self.global = value
}
}
}
}
impl<T> From<EntityMap<T>> for HashMap<Entity, T> {
fn from(value: EntityMap<T>) -> Self {
let mut m = HashMap::new();
m.insert(Entity::Global, value.global);
for ((row, col), value) in value.cells {
m.insert(Entity::Cell(row, col), value);
}
for (row, value) in value.rows {
m.insert(Entity::Row(row), value);
}
for (col, value) in value.columns {
m.insert(Entity::Column(col), value);
}
m
}
}