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