diff --git a/lib/Basics/Traverser.h b/lib/Basics/Traverser.h index 57cc6d71e4..66cb8580e4 100644 --- a/lib/Basics/Traverser.h +++ b/lib/Basics/Traverser.h @@ -45,11 +45,16 @@ namespace triagens { // store with the additional property that every Value has a // positive Weight (provided by the weight() and setWeight(w) // methods), which is a numerical type, and for which operator< - // is defined. Furthermore, the Value type must be copyable and - // movable. Finally, the Value type must have a method getKey that - // returns a Key const&. + // is defined. With respect to this weight the data structure + // is at the same time a priority queue in that it is possible + // to ask for (one of) the value(s) with the smallest weight and + // remove this efficiently. + // The Value type must be copyable and should be movable. Finally, + // the Value type must have a method getKey that returns a Key + // const&. // This data structure makes the following complexity promises - // (amortized), where n is the number of key/value pairs stored: + // (amortized), where n is the number of key/value pairs stored + // in the queue: // insert: O(log(n)) (but see below) // lookup value by key: O(1) // get smallest: O(1) @@ -60,6 +65,10 @@ namespace triagens { // and if we do not use lower weight by key, then we even get: // insert: O(1) // get and erase smallest: O(1) + // With the "get and erase smallest" operation one has the option + // of retaining the erased value in the key/value store. It can then + // still be looked up but will no longer be considered for the + // priority queue. public: @@ -119,7 +128,8 @@ namespace triagens { } _heap.push_back(v); try { - _lookup.insert(std::make_pair(k, _heap.size()-1 + _popped)); + _lookup.insert(std::make_pair(k, + static_cast(_heap.size()-1 + _popped))); } catch (...) { _heap.pop_back(); @@ -132,7 +142,8 @@ namespace triagens { _heap.push_back(v); try { size_t newpos = _heap.size() - 1; - _lookup.insert(std::make_pair(k, newpos + _popped)); + _lookup.insert(std::make_pair(k, + static_cast(newpos + _popped))); repairUp(newpos); } catch (...) { @@ -151,7 +162,14 @@ namespace triagens { if (it == _lookup.end()) { return nullptr; } - return const_cast(&(_heap[it->second - _popped])); + if (it->second >= 0) { // still in the queue + return const_cast + (&(_heap[static_cast(it->second) - _popped])); + } + else { // already in the history + return const_cast + (&(_history[static_cast(-it->second) - 1])); + } } //////////////////////////////////////////////////////////////////////////////// @@ -166,9 +184,15 @@ namespace triagens { if (it == _lookup.end()) { return false; } - size_t pos = it->second - _popped; - _heap[pos].setWeight(newWeight); - repairUp(pos); + if (it->second >= 0) { // still in the queue + size_t pos = static_cast(it->second) - _popped; + _heap[pos].setWeight(newWeight); + repairUp(pos); + } + else { // already in the history + size_t pos = static_cast(-it->second) - 1; + _history[pos].setWeight(newWeight); + } return true; } @@ -188,18 +212,28 @@ namespace triagens { /// if the structure is empty. Key and Value are stored in k and v. //////////////////////////////////////////////////////////////////////////////// - bool popMinimal (Key& k, Value& v) { + bool popMinimal (Key& k, Value& v, bool keepForLookup = false) { if (_heap.empty()) { return false; } k = _heap[0].getKey(); v = _heap[0]; if (! _isHeap) { + auto it = _lookup.find(k); + TRI_ASSERT(it != _lookup.end()); + if (keepForLookup) { + _history.push_back(_heap[0]); + it->second = -static_cast(_history.size()); + // Note: This is intentionally one too large to shift by 1 + } + else { + _lookup.erase(it); + } _heap.pop_front(); _popped++; } else { - removeFromHeap(); + removeFromHeap(keepForLookup); } return true; } @@ -319,7 +353,7 @@ namespace triagens { /// @brief removeFromHeap, remove first position in the heap //////////////////////////////////////////////////////////////////////////////// - void removeFromHeap () { + void removeFromHeap (bool keepForLookup) { if (_heap.size() == 1) { _heap.clear(); _popped = 0; @@ -331,12 +365,19 @@ namespace triagens { auto it = _lookup.find(_heap[0].getKey()); TRI_ASSERT(it != _lookup.end()); - _lookup.erase(it); + if (keepForLookup) { + _history.push_back(_heap[0]); + it->second = -static_cast(_history.size()); + // Note: This is intentionally one too large to shift by 1 + } + else { + _lookup.erase(it); + } _heap[0] = _heap.back(); _heap.pop_back(); it = _lookup.find(_heap[0].getKey()); TRI_ASSERT(it != _lookup.end()); - it->second = _popped; + it->second = static_cast(_popped); repairDown(); } @@ -357,7 +398,7 @@ namespace triagens { /// @brief _lookup, this provides O(1) lookup by Key //////////////////////////////////////////////////////////////////////////////// - std::unordered_map _lookup; + std::unordered_map _lookup; //////////////////////////////////////////////////////////////////////////////// /// @brief _isHeap, starts as false, in which case we only use a deque, @@ -379,6 +420,12 @@ namespace triagens { Weight _maxWeight; +//////////////////////////////////////////////////////////////////////////////// +/// @brief _history, the actual data that is only in the key/value store +//////////////////////////////////////////////////////////////////////////////// + + std::vector _history; + }; class Traverser {