1
0
Fork 0

USe smart-pointer in Priority Queue, it seems the usages have been correct everywhere (so no leaks) but memory responibility was hard to understand, should now be much simpler. (#8768)

This commit is contained in:
Michael Hackstein 2019-04-16 12:06:42 +02:00 committed by GitHub
parent 7fd456a83b
commit b3205dada8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 425 additions and 420 deletions

View File

@ -52,13 +52,14 @@ AttributeWeightShortestPathFinder::Searcher::Searcher(
_start(start),
_isBackward(isBackward) {}
void AttributeWeightShortestPathFinder::Searcher::insertNeighbor(Step* step, double newWeight) {
void AttributeWeightShortestPathFinder::Searcher::insertNeighbor(std::unique_ptr<Step>&& step,
double newWeight) {
Step* s = _myInfo._pq.find(step->_vertex);
// Not found, so insert it:
if (s == nullptr) {
step->setWeight(newWeight);
_myInfo._pq.insert(step->_vertex, step);
_myInfo._pq.insert(step->_vertex, std::move(step));
return;
}
if (!s->_done && s->weight() > newWeight) {
@ -66,7 +67,6 @@ void AttributeWeightShortestPathFinder::Searcher::insertNeighbor(Step* step, dou
std::swap(s->_edge, step->_edge);
_myInfo._pq.lowerWeight(s->_vertex, newWeight);
}
delete step;
}
void AttributeWeightShortestPathFinder::Searcher::lookupPeer(arangodb::velocypack::StringRef& vertex,
@ -119,8 +119,9 @@ void AttributeWeightShortestPathFinder::Searcher::lookupPeer(arangodb::velocypac
bool AttributeWeightShortestPathFinder::Searcher::oneStep() {
arangodb::velocypack::StringRef v;
// Do not steal responsibility.
Step* s = nullptr;
bool b = _myInfo._pq.popMinimal(v, s, true);
bool b = _myInfo._pq.popMinimal(v, s);
if (_pathFinder->_bingo || !b) {
// We can leave this functino only under 2 conditions:
@ -134,11 +135,13 @@ bool AttributeWeightShortestPathFinder::Searcher::oneStep() {
TRI_ASSERT(s != nullptr);
std::vector<Step*> neighbors;
std::vector<std::unique_ptr<Step>> neighbors;
_pathFinder->expandVertex(_isBackward, v, neighbors);
for (Step* neighbor : neighbors) {
insertNeighbor(neighbor, s->weight() + neighbor->weight());
for (std::unique_ptr<Step>& neighbor : neighbors) {
insertNeighbor(std::move(neighbor), s->weight() + neighbor->weight());
}
// All neighbours are moved out.
neighbors.clear();
lookupPeer(v, s->weight());
Step* s2 = _myInfo._pq.find(v);
@ -170,17 +173,21 @@ bool AttributeWeightShortestPathFinder::shortestPath(arangodb::velocypack::Slice
_bingo = false;
_intermediateSet = false;
arangodb::velocypack::StringRef start = _options.cache()->persistString(arangodb::velocypack::StringRef(st));
arangodb::velocypack::StringRef target = _options.cache()->persistString(arangodb::velocypack::StringRef(ta));
arangodb::velocypack::StringRef start =
_options.cache()->persistString(arangodb::velocypack::StringRef(st));
arangodb::velocypack::StringRef target =
_options.cache()->persistString(arangodb::velocypack::StringRef(ta));
// Forward with initialization:
arangodb::velocypack::StringRef emptyVertex;
ThreadInfo forward;
forward._pq.insert(start, new Step(start, emptyVertex, 0, EdgeDocumentToken()));
forward._pq.insert(start, std::make_unique<Step>(start, emptyVertex, 0,
EdgeDocumentToken()));
// backward with initialization:
ThreadInfo backward;
backward._pq.insert(target, new Step(target, emptyVertex, 0, EdgeDocumentToken()));
backward._pq.insert(target, std::make_unique<Step>(target, emptyVertex, 0,
EdgeDocumentToken()));
// Now the searcher threads:
Searcher forwardSearcher(this, forward, backward, start, false);
@ -266,17 +273,17 @@ bool AttributeWeightShortestPathFinder::shortestPath(arangodb::velocypack::Slice
void AttributeWeightShortestPathFinder::inserter(
std::unordered_map<arangodb::velocypack::StringRef, size_t>& candidates,
std::vector<Step*>& result, arangodb::velocypack::StringRef const& s, arangodb::velocypack::StringRef const& t,
std::vector<std::unique_ptr<Step>>& result,
arangodb::velocypack::StringRef const& s, arangodb::velocypack::StringRef const& t,
double currentWeight, EdgeDocumentToken&& edge) {
auto cand = candidates.find(t);
if (cand == candidates.end()) {
// Add weight
auto step = std::make_unique<Step>(t, s, currentWeight, std::move(edge));
result.emplace_back(step.release());
result.emplace_back(std::make_unique<Step>(t, s, currentWeight, std::move(edge)));
candidates.emplace(t, result.size() - 1);
} else {
// Compare weight
auto old = result[cand->second];
auto& old = result[cand->second];
auto oldWeight = old->weight();
if (currentWeight < oldWeight) {
old->setWeight(currentWeight);
@ -286,9 +293,9 @@ void AttributeWeightShortestPathFinder::inserter(
}
}
void AttributeWeightShortestPathFinder::expandVertex(bool isBackward,
arangodb::velocypack::StringRef const& vertex,
std::vector<Step*>& result) {
void AttributeWeightShortestPathFinder::expandVertex(
bool isBackward, arangodb::velocypack::StringRef const& vertex,
std::vector<std::unique_ptr<Step>>& result) {
std::unique_ptr<EdgeCursor> edgeCursor;
if (isBackward) {
edgeCursor.reset(_options.nextReverseCursor(vertex));
@ -301,14 +308,16 @@ void AttributeWeightShortestPathFinder::expandVertex(bool isBackward,
if (edge.isString()) {
VPackSlice doc = _options.cache()->lookupToken(eid);
double currentWeight = _options.weightEdge(doc);
arangodb::velocypack::StringRef other = _options.cache()->persistString(arangodb::velocypack::StringRef(edge));
arangodb::velocypack::StringRef other =
_options.cache()->persistString(arangodb::velocypack::StringRef(edge));
if (other.compare(vertex) != 0) {
inserter(candidates, result, vertex, other, currentWeight, std::move(eid));
} else {
inserter(candidates, result, other, vertex, currentWeight, std::move(eid));
}
} else {
arangodb::velocypack::StringRef fromTmp(transaction::helpers::extractFromFromDocument(edge));
arangodb::velocypack::StringRef fromTmp(
transaction::helpers::extractFromFromDocument(edge));
arangodb::velocypack::StringRef toTmp(transaction::helpers::extractToFromDocument(edge));
arangodb::velocypack::StringRef from = _options.cache()->persistString(fromTmp);
arangodb::velocypack::StringRef to = _options.cache()->persistString(toTmp);

View File

@ -58,8 +58,9 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
arangodb::graph::EdgeDocumentToken _edge;
bool _done;
Step(arangodb::velocypack::StringRef const& vert, arangodb::velocypack::StringRef const& pred,
double weig, EdgeDocumentToken&& edge);
Step(arangodb::velocypack::StringRef const& vert,
arangodb::velocypack::StringRef const& pred, double weig,
EdgeDocumentToken&& edge);
double weight() const { return _weight; }
@ -152,7 +153,8 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
class Searcher {
public:
Searcher(AttributeWeightShortestPathFinder* pathFinder, ThreadInfo& myInfo,
ThreadInfo& peerInfo, arangodb::velocypack::StringRef const& start, bool isBackward);
ThreadInfo& peerInfo, arangodb::velocypack::StringRef const& start,
bool isBackward);
public:
//////////////////////////////////////////////////////////////////////////////
@ -166,7 +168,7 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
/// @brief Insert a neighbor to the todo list.
////////////////////////////////////////////////////////////////////////////////
void insertNeighbor(Step* step, double newWeight);
void insertNeighbor(std::unique_ptr<Step>&& step, double newWeight);
////////////////////////////////////////////////////////////////////////////////
/// @brief Lookup our current vertex in the data of our peer.
@ -211,12 +213,13 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
arangodb::graph::ShortestPathResult& result) override;
void inserter(std::unordered_map<arangodb::velocypack::StringRef, size_t>& candidates,
std::vector<Step*>& result, arangodb::velocypack::StringRef const& s,
std::vector<std::unique_ptr<Step>>& result,
arangodb::velocypack::StringRef const& s,
arangodb::velocypack::StringRef const& t, double currentWeight,
graph::EdgeDocumentToken&& edge);
void expandVertex(bool isBackward, arangodb::velocypack::StringRef const& source,
std::vector<Step*>& result);
std::vector<std::unique_ptr<Step>>& result);
//////////////////////////////////////////////////////////////////////////////
/// @brief return the shortest path between the start and target vertex,

View File

@ -64,10 +64,10 @@ bool KShortestPathsFinder::startKShortestPathsTraversal(
return true;
}
bool KShortestPathsFinder::computeShortestPath(
VertexRef const& start, VertexRef const& end,
std::unordered_set<VertexRef> const& forbiddenVertices,
std::unordered_set<Edge> const& forbiddenEdges, Path& result) {
bool KShortestPathsFinder::computeShortestPath(VertexRef const& start, VertexRef const& end,
std::unordered_set<VertexRef> const& forbiddenVertices,
std::unordered_set<Edge> const& forbiddenEdges,
Path& result) {
bool found = false;
Ball left(start, FORWARD);
Ball right(end, BACKWARD);
@ -91,8 +91,8 @@ bool KShortestPathsFinder::computeShortestPath(
return found;
}
void KShortestPathsFinder::computeNeighbourhoodOfVertex(
VertexRef vertex, Direction direction, std::vector<Step>& steps) {
void KShortestPathsFinder::computeNeighbourhoodOfVertex(VertexRef vertex, Direction direction,
std::vector<Step>& steps) {
std::unique_ptr<EdgeCursor> edgeCursor;
switch (direction) {
@ -150,13 +150,14 @@ void KShortestPathsFinder::computeNeighbourhoodOfVertex(
}
}
bool KShortestPathsFinder::advanceFrontier(
Ball& source, Ball const& target, std::unordered_set<VertexRef> const& forbiddenVertices,
std::unordered_set<Edge> const& forbiddenEdges, VertexRef& join) {
bool KShortestPathsFinder::advanceFrontier(Ball& source, Ball const& target,
std::unordered_set<VertexRef> const& forbiddenVertices,
std::unordered_set<Edge> const& forbiddenEdges,
VertexRef& join) {
VertexRef vr;
FoundVertex* v;
bool success = source._frontier.popMinimal(vr, v, true);
bool success = source._frontier.popMinimal(vr, v);
if (!success) {
return false;
}
@ -178,7 +179,8 @@ bool KShortestPathsFinder::advanceFrontier(
}
} else {
source._frontier.insert(s._vertex,
new FoundVertex(s._vertex, vr, std::move(s._edge), weight));
std::make_unique<FoundVertex>(s._vertex, vr,
std::move(s._edge), weight));
auto found = target._frontier.find(s._vertex);
if (found != nullptr) {
@ -192,8 +194,7 @@ bool KShortestPathsFinder::advanceFrontier(
}
void KShortestPathsFinder::reconstructPath(Ball const& left, Ball const& right,
VertexRef const& join,
Path& result) {
VertexRef const& join, Path& result) {
result.clear();
TRI_ASSERT(!join.empty());
result._vertices.emplace_back(join);
@ -276,13 +277,15 @@ bool KShortestPathsFinder::computeNextShortestPath(Path& result) {
// indeed one that removes duplicates automatically
// Sorted in reverse to have pop_back
if (_options.useWeight()) {
std::sort(_candidatePaths.begin(), _candidatePaths.end(), [](Path const& p1, Path const& p2) {
return p1._weight > p2._weight;
});
std::sort(_candidatePaths.begin(), _candidatePaths.end(),
[](Path const& p1, Path const& p2) {
return p1._weight > p2._weight;
});
} else {
std::sort(_candidatePaths.begin(), _candidatePaths.end(), [](Path const& p1, Path const& p2) {
return p1._vertices.size() > p2._vertices.size();
});
std::sort(_candidatePaths.begin(), _candidatePaths.end(),
[](Path const& p1, Path const& p2) {
return p1._vertices.size() > p2._vertices.size();
});
}
// FIXME: this is of course bad.

View File

@ -126,7 +126,8 @@ class KShortestPathsFinder : public ShortestPathFinder {
double _weight;
// Using negative weight to signifiy start/end vertex
explicit FoundVertex(VertexRef const& vertex) : _vertex(vertex), _weight(0) {}
explicit FoundVertex(VertexRef const& vertex)
: _vertex(vertex), _weight(0) {}
FoundVertex(VertexRef const& vertex, VertexRef const& pred, Edge&& edge, double weight)
: _vertex(vertex), _pred(pred), _edge(std::move(edge)), _weight(weight) {}
double weight() const { return _weight; }
@ -150,13 +151,8 @@ class KShortestPathsFinder : public ShortestPathFinder {
Ball(void) {}
Ball(VertexRef const& centre, Direction direction)
: _centre(centre), _direction(direction) {
auto v = new FoundVertex(centre);
try {
_frontier.insert(centre, v);
} catch (...) {
delete v;
throw;
}
auto v = std::make_unique<FoundVertex>(centre);
_frontier.insert(centre, std::move(v));
}
~Ball() {
// TODO free all vertices

View File

@ -66,12 +66,8 @@ class ShortestPathPriorityQueue {
ShortestPathPriorityQueue() : _popped(0), _isHeap(false), _maxWeight(0) {}
~ShortestPathPriorityQueue() {
for (Value* v : _heap) {
delete v;
}
for (Value* v : _history) {
delete v;
}
_heap.clear();
_history.clear();
}
//////////////////////////////////////////////////////////////////////////////
@ -91,7 +87,7 @@ class ShortestPathPriorityQueue {
/// yet exist, and false, in which case nothing else is changed.
//////////////////////////////////////////////////////////////////////////////
bool insert(Key const& k, Value* v) {
bool insert(Key const& k, std::unique_ptr<Value>&& v) {
auto it = _lookup.find(k);
if (it != _lookup.end()) {
return false;
@ -108,7 +104,7 @@ class ShortestPathPriorityQueue {
if (w > _maxWeight) {
_maxWeight = w;
}
_heap.push_back(v);
_heap.push_back(std::move(v));
try {
_lookup.insert(std::make_pair(k, static_cast<ssize_t>(_heap.size() - 1 + _popped)));
} catch (...) {
@ -119,7 +115,7 @@ class ShortestPathPriorityQueue {
}
}
// If we get here, we have to insert into a proper binary heap:
_heap.push_back(v);
_heap.push_back(std::move(v));
try {
size_t newpos = _heap.size() - 1;
_lookup.insert(std::make_pair(k, static_cast<ssize_t>(newpos + _popped)));
@ -146,9 +142,9 @@ class ShortestPathPriorityQueue {
}
if (it->second >= 0) { // still in the queue
return _heap.at(static_cast<size_t>(it->second) - _popped);
return _heap.at(static_cast<size_t>(it->second) - _popped).get();
} else { // already in the history
return _history.at(static_cast<size_t>(-it->second) - 1);
return _history.at(static_cast<size_t>(-it->second) - 1).get();
}
}
@ -186,36 +182,73 @@ class ShortestPathPriorityQueue {
if (_heap.empty()) {
return nullptr;
}
return _heap[0];
return _heap[0].get();
}
//////////////////////////////////////////////////////////////////////////////
/// @brief popMinimal, returns true if something was returned and false
/// if the structure is empty. Key and Value are stored in k and v.
/// If keepForLookup is true then the Value is kept for lookup in the
/// hash table but removed from the priority queue.
/// This will keep the unique_ptr inside the history for further lookup.
/// In case you doe not want to lookup the value, you need to call stealMinimal
//////////////////////////////////////////////////////////////////////////////
bool popMinimal(Key& k, Value*& v, bool keepForLookup = false) {
bool popMinimal(Key& k, Value*& v) {
if (_heap.empty()) {
v = nullptr;
return false;
}
k = _heap[0]->getKey();
// Note: the pointer of _heap[0] stays valid.
// The unique-responsiblity is handed over to history.
v = _heap[0].get();
auto it = _lookup.find(k);
TRI_ASSERT(it != _lookup.end());
// move val into history and hand over responsibility
_history.push_back(std::move(_heap[0]));
it->second = -static_cast<ssize_t>(_history.size());
if (!_isHeap) {
// remove nullptr from heap
_heap.pop_front();
// Note: This is intentionally one too large to shift by 1
_popped++;
} else {
repairHeapAfterPop();
}
return true;
}
//////////////////////////////////////////////////////////////////////////////
/// @brief stealMinimal, returns true if something was returned and false
/// if the structure is empty. Key and Value are stored in k and v.
/// This will hand over responsiblity to the caller, and will remove
/// the value from further lookups. If you need it within the history here
/// you need to call popMinimal.
//////////////////////////////////////////////////////////////////////////////
bool stealMinimal(Key& k, std::unique_ptr<Value>& v) {
if (_heap.empty()) {
return false;
}
k = _heap[0]->getKey();
v = _heap[0];
auto it = _lookup.find(k);
TRI_ASSERT(it != _lookup.end());
_lookup.erase(it);
// Responsibility handed over to v
// Note: _heap[0] is nullptr now.
// If we crash now this is save.
// The code below is not allowed to use _heap[0]
// anymore
v = std::make_unique<Value>(_heap[0].release());
TRI_ASSERT(v != nullptr);
if (!_isHeap) {
auto it = _lookup.find(k);
TRI_ASSERT(it != _lookup.end());
if (keepForLookup) {
_history.push_back(_heap[0]);
it->second = -static_cast<ssize_t>(_history.size());
// Note: This is intentionally one too large to shift by 1
} else {
_lookup.erase(it);
}
// Remove it from heap asap.
_heap.pop_front();
_popped++;
} else {
removeFromHeap(keepForLookup);
repairHeapAfterPop();
}
return true;
}
@ -226,9 +259,7 @@ class ShortestPathPriorityQueue {
//////////////////////////////////////////////////////////////////////////////
void swap(size_t p, size_t q) {
Value* v = _heap[p];
_heap[p] = _heap[q];
_heap[q] = v;
_heap[p].swap(_heap[q]);
// Now fix the lookup:
Key const& keyp(_heap[p]->getKey());
@ -319,19 +350,10 @@ class ShortestPathPriorityQueue {
}
//////////////////////////////////////////////////////////////////////////////
/// @brief removeFromHeap, remove first position in the heap
/// @brief repairHeapAfterPop, remove first position in the heap and repair it
//////////////////////////////////////////////////////////////////////////////
void removeFromHeap(bool keepForLookup) {
auto it = _lookup.find(_heap[0]->getKey());
TRI_ASSERT(it != _lookup.end());
if (keepForLookup) {
_history.push_back(_heap[0]);
it->second = -static_cast<ssize_t>(_history.size());
// Note: This is intentionally one too large to shift by 1
} else {
_lookup.erase(it);
}
void repairHeapAfterPop() {
if (_heap.size() == 1) {
_heap.clear();
_popped = 0;
@ -340,9 +362,11 @@ class ShortestPathPriorityQueue {
return;
}
// Move one in front:
_heap[0] = _heap.back();
_heap[0].swap(_heap.back());
// Throw away the POINTER (might be moved before).
_heap.pop_back();
it = _lookup.find(_heap[0]->getKey());
// Find the lokkup of new value
auto it = _lookup.find(_heap[0]->getKey());
TRI_ASSERT(it != _lookup.end());
it->second = static_cast<ssize_t>(_popped);
repairDown();
@ -376,7 +400,7 @@ class ShortestPathPriorityQueue {
/// @brief _heap, the actual data
//////////////////////////////////////////////////////////////////////////////
std::deque<Value*> _heap;
std::deque<std::unique_ptr<Value>> _heap;
//////////////////////////////////////////////////////////////////////////////
/// @brief _maxWeight, the current maximal weight ever seen
@ -388,7 +412,7 @@ class ShortestPathPriorityQueue {
/// @brief _history, the actual data that is only in the key/value store
//////////////////////////////////////////////////////////////////////////////
std::vector<Value*> _history;
std::vector<std::unique_ptr<Value>> _history;
};
} // namespace graph

View File

@ -43,395 +43,365 @@ using namespace std;
struct MyValue {
std::string _key;
unsigned int _weight;
unsigned int weight() const {
return _weight;
}
std::string const& getKey() const {
return _key;
}
void setWeight(unsigned int const w) {
_weight = w;
}
MyValue (std::string k, unsigned int w) : _key(k), _weight(w) {
}
unsigned int weight() const { return _weight; }
std::string const& getKey() const { return _key; }
void setWeight(unsigned int const w) { _weight = w; }
MyValue(std::string k, unsigned int w) : _key(k), _weight(w) {}
};
TEST_CASE("CPriorityQueueTest", "[cpriorityqueue]") {
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in ascending weight order
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_deque_case") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int) pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v;
b = pq.insert("a", new MyValue("a", 1));
CHECK(b == true);
b = pq.insert("b", new MyValue("b", 2));
CHECK(b == true);
b = pq.insert("c", new MyValue("c", 2));
CHECK(b == true);
b = pq.insert("d", new MyValue("d", 4));
CHECK(b == true);
v = new MyValue("c", 5);
b = pq.insert("c", v);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size());
CHECK(false == pq.empty());
MyValue const* p;
p = pq.find("a");
CHECK((int) p->_weight == 1);
p = pq.find("b");
CHECK((int) p->_weight == 2);
p = pq.find("c");
CHECK((int) p->_weight == 2);
p = pq.find("d");
CHECK((int) p->_weight == 4);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int) v->_weight == 2);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int) v->_weight == 2);
delete v;
CHECK((int) pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int) v->_weight == 4);
delete v;
CHECK((int) pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in random weight order
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_heap_case") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int) pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v;
b = pq.insert("a", new MyValue("a", 4));
CHECK(b == true);
b = pq.insert("b", new MyValue("b", 1));
CHECK(b == true);
b = pq.insert("c", new MyValue("c", 2));
CHECK(b == true);
b = pq.insert("d", new MyValue("d", 2));
CHECK(b == true);
v = new MyValue("c", 5);
b = pq.insert("c", v);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size());
CHECK(false == pq.empty());
MyValue const* p;
p = pq.find("a");
CHECK((int) p->_weight == 4);
p = pq.find("b");
CHECK((int) p->_weight == 1);
p = pq.find("c");
CHECK((int) p->_weight == 2);
p = pq.find("d");
CHECK((int) p->_weight == 2);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int) v->_weight == 2);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int) v->_weight == 2);
delete v;
CHECK((int) pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int) v->_weight == 4);
delete v;
CHECK((int) pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in ascending weight order, but then doing lowerWeight
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_deque_case_with_lowering") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int) pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v;
b = pq.insert("a", new MyValue("a", 1));
CHECK(b == true);
b = pq.insert("b", new MyValue("b", 2));
CHECK(b == true);
b = pq.insert("c", new MyValue("c", 2));
CHECK(b == true);
b = pq.insert("d", new MyValue("d", 4));
CHECK(b == true);
v = new MyValue("c", 5);
b = pq.insert("c", v);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size());
CHECK(false == pq.empty());
pq.lowerWeight("d", 1); // This moves "d" before "b" and "c"
MyValue const* p;
p = pq.find("a");
CHECK((int) p->_weight == 1);
p = pq.find("b");
CHECK((int) p->_weight == 2);
p = pq.find("c");
CHECK((int) p->_weight == 2);
p = pq.find("d");
CHECK((int) p->_weight == 1);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int) v->_weight == 2);
delete v;
CHECK((int) pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int) v->_weight == 2);
delete v;
CHECK((int) pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in random weight order, and later lowering some weight
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_heap_case_with_lowering") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int) pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v;
b = pq.insert("a", new MyValue("a", 4));
CHECK(b == true);
b = pq.insert("b", new MyValue("b", 2));
CHECK(b == true);
b = pq.insert("c", new MyValue("c", 3));
CHECK(b == true);
b = pq.insert("d", new MyValue("d", 3));
CHECK(b == true);
v = new MyValue("c", 5);
b = pq.insert("c", v);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size());
CHECK(false == pq.empty());
pq.lowerWeight("a", 1); // This moves "a" before all others
MyValue const* p;
p = pq.find("a");
CHECK((int) p->_weight == 1);
p = pq.find("b");
CHECK((int) p->_weight == 2);
p = pq.find("c");
CHECK((int) p->_weight == 3);
p = pq.find("d");
CHECK((int) p->_weight == 3);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int) v->_weight == 2);
delete v;
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int) v->_weight == 3);
delete v;
CHECK((int) pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int) v->_weight == 3);
delete v;
CHECK((int) pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in ascending weight order
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_deque_case") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int)pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v = nullptr;
b = pq.insert("a", std::make_unique<MyValue>("a", 1));
CHECK(b == true);
b = pq.insert("b", std::make_unique<MyValue>("b", 2));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 2));
CHECK(b == true);
b = pq.insert("d", std::make_unique<MyValue>("d", 4));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 5));
CHECK(b == false);
CHECK(4 == (int)pq.size());
CHECK(false == pq.empty());
MyValue const* p;
p = pq.find("a");
CHECK((int)p->_weight == 1);
p = pq.find("b");
CHECK((int)p->_weight == 2);
p = pq.find("c");
CHECK((int)p->_weight == 2);
p = pq.find("d");
CHECK((int)p->_weight == 4);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
REQUIRE(v != nullptr);
CHECK(v->_key == "a");
CHECK((int)v->_weight == 1);
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int)v->_weight == 2);
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int)v->_weight == 2);
CHECK((int)pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int)v->_weight == 4);
CHECK((int)pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in random weight order
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_heap_case") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int)pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v = nullptr;
b = pq.insert("a", std::make_unique<MyValue>("a", 4));
CHECK(b == true);
b = pq.insert("b", std::make_unique<MyValue>("b", 1));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 2));
CHECK(b == true);
b = pq.insert("d", std::make_unique<MyValue>("d", 2));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 5));
CHECK(b == false);
CHECK(4 == (int)pq.size());
CHECK(false == pq.empty());
MyValue const* p;
p = pq.find("a");
CHECK((int)p->_weight == 4);
p = pq.find("b");
CHECK((int)p->_weight == 1);
p = pq.find("c");
CHECK((int)p->_weight == 2);
p = pq.find("d");
CHECK((int)p->_weight == 2);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int)v->_weight == 1);
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int)v->_weight == 2);
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int)v->_weight == 2);
CHECK((int)pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int)v->_weight == 4);
CHECK((int)pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in ascending weight order, but then doing lowerWeight
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_deque_case_with_lowering") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int)pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v = nullptr;
b = pq.insert("a", std::make_unique<MyValue>("a", 1));
CHECK(b == true);
b = pq.insert("b", std::make_unique<MyValue>("b", 2));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 2));
CHECK(b == true);
b = pq.insert("d", std::make_unique<MyValue>("d", 4));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 5));
CHECK(b == false);
CHECK(4 == (int)pq.size());
CHECK(false == pq.empty());
pq.lowerWeight("d", 1); // This moves "d" before "b" and "c"
MyValue const* p;
p = pq.find("a");
CHECK((int)p->_weight == 1);
p = pq.find("b");
CHECK((int)p->_weight == 2);
p = pq.find("c");
CHECK((int)p->_weight == 2);
p = pq.find("d");
CHECK((int)p->_weight == 1);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int)v->_weight == 1);
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int)v->_weight == 1);
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int)v->_weight == 2);
CHECK((int)pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int)v->_weight == 2);
CHECK((int)pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in random weight order, and later lowering some weight
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_heap_case_with_lowering") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int)pq.size());
CHECK(true == pq.empty());
bool b;
MyValue* v = nullptr;
b = pq.insert("a", std::make_unique<MyValue>("a", 4));
CHECK(b == true);
b = pq.insert("b", std::make_unique<MyValue>("b", 2));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 3));
CHECK(b == true);
b = pq.insert("d", std::make_unique<MyValue>("d", 3));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 5));
CHECK(b == false);
delete v;
CHECK(4 == (int)pq.size());
CHECK(false == pq.empty());
pq.lowerWeight("a", 1); // This moves "a" before all others
MyValue const* p;
p = pq.find("a");
CHECK((int)p->_weight == 1);
p = pq.find("b");
CHECK((int)p->_weight == 2);
p = pq.find("c");
CHECK((int)p->_weight == 3);
p = pq.find("d");
CHECK((int)p->_weight == 3);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k;
p = pq.getMinimal();
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int)v->_weight == 1);
p = pq.getMinimal();
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int)v->_weight == 2);
p = pq.getMinimal();
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int)v->_weight == 3);
CHECK((int)pq.size() == 1);
CHECK(pq.empty() == false);
p = pq.getMinimal();
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int)v->_weight == 3);
CHECK((int)pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
}
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End:
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|//
// --SECTION--\\|/// @\\}\\)" End: