wilderfield
priority_map.hpp
Go to the documentation of this file.
1 
9 #ifndef WILDERFIELD_PRIORITY_MAP_HPP
10 #define WILDERFIELD_PRIORITY_MAP_HPP
11 
12 #include <unordered_map>
13 #include <list>
14 #include <unordered_set>
15 #include <iterator>
16 #include <functional>
17 #include <type_traits>
18 #include <algorithm>
19 
20 namespace wilderfield {
21 
34 template<
35  typename KeyType,
36  typename ValType,
37  typename Compare = std::greater<ValType>,
38  typename Hash = std::hash<KeyType>
39 >
40 class priority_map final {
41 
42 static_assert(std::is_arithmetic<ValType>::value, "ValType must be a numeric type.");
43 
44 private:
45  Compare comp_;
46 
47  std::list<ValType> vals_;
48 
49  std::unordered_map<KeyType, typename std::list<ValType>::iterator, Hash> keys_;
50 
51  std::unordered_map<ValType, std::unordered_set<KeyType>> valToKeys_;
52 
53  // Private member functions
54 
55  // Insert new key
56  void insert(const KeyType& key, const ValType& newVal);
57 
58  // Update Key with Val
59  // This function can be used for increment, decrement, or assigning a new val
60  void update(const KeyType& key, const ValType& newVal);
61 
62  // Get the value associated with a key.
63  ValType getVal(const KeyType& key) const { return *(keys_.at(key)); }
64 
65 public:
66 
67  size_t size() const { return keys_.size(); }
68 
69  bool empty() const { return keys_.empty(); }
70 
71  size_t count(const KeyType& key) const { return keys_.count(key); }
72 
73  std::pair<KeyType, ValType> top() const;
74 
75  size_t erase(const KeyType& key);
76 
77  void pop();
78 
79  class Proxy;
80  Proxy operator[](const KeyType& key);
81 
82  // Proxy class to handle the increment operation.
83  class Proxy {
84  private:
85  priority_map* pm;
86  KeyType key;
87 
88  public:
89  Proxy(priority_map* pm, const KeyType& key) : pm(pm), key(key) {}
90 
91  Proxy& operator++() {
92  pm->update(key, pm->getVal(key)+1);
93  return *this;
94 
95  }
96 
97  Proxy operator++(int) {
98  Proxy temp = *this;
99  ++(*this);
100  return temp;
101  }
102 
103  Proxy& operator--() {
104  pm->update(key, pm->getVal(key)-1);
105  return *this;
106  }
107 
108  Proxy operator--(int) {
109  Proxy temp = *this;
110  --(*this);
111  return temp;
112  }
113 
114  void operator=(const ValType& val) {pm->update(key, val);}
115 
116  operator ValType() const {return pm->getVal(key);}
117  };
118 
119 };
120 
121 // Out-of-line implementation of priority_map methods
122 
123 template<
124  typename KeyType,
125  typename ValType,
126  typename Compare,
127  typename Hash
128 >
130  if (keys_.find(key) != keys_.end()) {
131  auto oldIt = keys_[key];
132  valToKeys_[*oldIt].erase(key);
133  if (valToKeys_[*oldIt].empty()) {
134  valToKeys_.erase(*oldIt); // For now avoid memory bloat
135  vals_.erase(oldIt);
136  }
137  }
138  return keys_.erase(key);
139 }
140 
141 template<
142  typename KeyType,
143  typename ValType,
144  typename Compare,
145  typename Hash
146 >
147 std::pair<KeyType, ValType> priority_map<KeyType, ValType, Compare, Hash>::top() const {
148  if (vals_.empty()) {
149  throw std::out_of_range("Can't access top on an empty priority_map.");
150  }
151  const auto val = vals_.front();
152  if (valToKeys_.at(val).empty()) {
153  throw std::logic_error("Inconsistent state: Val with no keys.");
154  }
155  // Return a pair consisting of one of the keys and the value.
156  return {*(valToKeys_.at(val).begin()), val};
157 }
158 
159 template<
160  typename KeyType,
161  typename ValType,
162  typename Compare,
163  typename Hash
164 >
166  if (vals_.empty()) {
167  throw std::out_of_range("Can't pop from empty priority_map.");
168  }
169 
170  auto oldIt = vals_.begin();
171  if (valToKeys_[*oldIt].empty()) {
172  throw std::logic_error("Inconsistent state: Val with no keys.");
173  }
174  KeyType key = *(valToKeys_[*oldIt].begin());
175 
176  valToKeys_[*oldIt].erase(key);
177  keys_.erase(key);
178 
179  // Remove node if it's empty
180  if (valToKeys_[*oldIt].empty()) {
181  valToKeys_.erase(*oldIt); // For now avoid memory bloat
182  vals_.erase(oldIt);
183  }
184 }
185 
186 template<
187  typename KeyType,
188  typename ValType,
189  typename Compare,
190  typename Hash
191 >
192 void priority_map<KeyType, ValType, Compare, Hash>::insert(const KeyType& key, const ValType& newVal) {
193 
194  if (keys_.find(key) == keys_.end()) {
195 
196  valToKeys_[0].insert(key);
197 
198  // True if minHeap
199  if (comp_(0, 1)) {
200 
201  // Linear search towards end
202  auto insertionPoint = std::find_if(vals_.begin(), vals_.end(),
203  [&](const auto val) {
204  return val >= 0;
205  });
206 
207  if (insertionPoint == vals_.end() || (*insertionPoint) != 0) {
208  insertionPoint = vals_.insert(insertionPoint, 0);
209  }
210 
211  keys_[key] = insertionPoint;
212 
213  }
214  // maxHeap
215  else {
216 
217  // Linear search towards begin
218  auto insertionPoint = std::find_if(vals_.rbegin(), vals_.rend(),
219  [&](const auto val) {
220  return val >= 0;
221  });
222 
223  if (insertionPoint == vals_.rend() || (*insertionPoint) != 0) {
224  auto newIt = vals_.insert(insertionPoint.base(), 0);
225  keys_[key] = newIt;
226  }
227  else {
228  keys_[key] = std::next(insertionPoint).base();
229  }
230  }
231 
232  }
233 
234  update(key, newVal);
235 
236 }
237 
238 template<
239  typename KeyType,
240  typename ValType,
241  typename Compare,
242  typename Hash
243 >
244 void priority_map<KeyType, ValType, Compare, Hash>::update(const KeyType& key, const ValType& newVal) {
245 
246  auto oldIt = keys_[key];
247 
248  // Save Old Value
249  ValType oldVal = *oldIt;
250 
251  if (oldVal == newVal) return;
252 
253  // Remove the key from the old association
254  keys_.erase(key);
255  valToKeys_[oldVal].erase(key);
256 
257  // Make new association
258  valToKeys_[newVal].insert(key);
259 
260  // True if minHeap and oldVal < newVal or if maxHeap and oldVal > newVal
261  if (comp_(oldVal, newVal)) {
262 
263  // Linear search towards end
264  auto insertionPoint = std::find_if(oldIt, vals_.end(),
265  [&](const auto val) {
266  if (comp_(1,0)) {
267  return val <= newVal;
268  }
269  return val >= newVal;
270  });
271 
272  if (insertionPoint == vals_.end() || (*insertionPoint) != newVal) {
273  insertionPoint = vals_.insert(insertionPoint, newVal);
274  }
275 
276  keys_[key] = insertionPoint;
277 
278  }
279  // minHeap and oldVal > newVal or maxHeap and oldVal < newVal
280  else {
281  // Need to search in reverse
282  typename std::list<ValType>::reverse_iterator oldRit(oldIt); // oldRit will point one element closer to begin
283 
284  // Linear search towards begin
285  auto insertionPoint = std::find_if(oldRit, vals_.rend(),
286  [&](const auto val) {
287  if (comp_(0,1)) {
288  return val <= newVal;
289  }
290  return val >= newVal;
291  });
292 
293  if (insertionPoint == vals_.rend() || (*insertionPoint) != newVal) {
294  auto newIt = vals_.insert(insertionPoint.base(), newVal);
295  keys_[key] = newIt;
296  keys_[key] = std::next(insertionPoint).base();
297  }
298  else {
299  keys_[key] = std::next(insertionPoint).base();
300  }
301  }
302 
303  // Remove the old node if it's empty
304  if (valToKeys_[oldVal].empty()) {
305  valToKeys_.erase(*oldIt); // For now avoid memory bloat
306  vals_.erase(oldIt);
307  }
308 
309 }
310 
311 template<
312  typename KeyType,
313  typename ValType,
314  typename Compare,
315  typename Hash
316 >
317 typename priority_map<KeyType, ValType, Compare, Hash>::Proxy priority_map<KeyType, ValType, Compare, Hash>::operator[](const KeyType& key) {
318 
319  // If the key doesn't exist, create a new node with value 0
320  if (keys_.find(key) == keys_.end()) {
321  insert(key, 0);
322  }
323  return Proxy(this, key);
324 }
325 
326 } // namespace
327 
328 #endif // WILDERFIELD_PRIORITY_MAP_HPP
Definition: priority_map.hpp:83
Priority map class.
Definition: priority_map.hpp:40
bool empty() const
Checks whether the priority map is empty.
Definition: priority_map.hpp:69
size_t count(const KeyType &key) const
Returns the count of a particular key in the map.
Definition: priority_map.hpp:71
void pop()
Removes the top element from the priority map.
Definition: priority_map.hpp:165
std::pair< KeyType, ValType > top() const
Returns the top element (key-value pair) in the priority map.
Definition: priority_map.hpp:147
size_t erase(const KeyType &key)
Erases key from the priority map. Returns the number of elements removed (0 or 1).
Definition: priority_map.hpp:129
size_t size() const
Returns the number of unique keys in the priority map.
Definition: priority_map.hpp:67