1
0
Fork 0

Split datastruct.h

Split into separate files concatenable_queue.hpp and priority_queue.hpp.
Move classes into namespace.
Separate unit testse.
This commit is contained in:
Joris van Rantwijk 2024-11-24 14:22:26 +01:00
parent 087799cdca
commit 0f18b7b05a
7 changed files with 553 additions and 511 deletions

View File

@ -6,27 +6,34 @@ CXXFLAGS = -std=c++11 -Wall -Wextra $(OPTFLAGS) $(DBGFLAGS)
LIB_BOOST_TEST = -l:libboost_unit_test_framework.a
.PHONY: all
all: run_matching run_matching_dbg test_mwmatching
all: run_matching run_matching_dbg test_mwmatching test_concatenable_queue test_priority_queue
run_matching: run_matching.cpp mwmatching.h
run_matching: run_matching.cpp mwmatching.h concatenable_queue.hpp priority_queue.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $<
run_matching_dbg: OPTFLAGS = -Og
run_matching_dbg: DBGFLAGS = -g -fsanitize=address -fsanitize=undefined
run_matching_dbg: run_matching.cpp mwmatching.h
run_matching_dbg: run_matching.cpp mwmatching.h concatenable_queue.hpp priority_queue.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $<
test_mwmatching: OPTFLAGS = -O1
test_mwmatching: DBGFLAGS = -fsanitize=address -fsanitize=undefined
test_mwmatching: test_mwmatching.cpp mwmatching.h
test_mwmatching: test_mwmatching.cpp mwmatching.h concatenable_queue.hpp priority_queue.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $< $(LIB_BOOST_TEST)
test_datastruct: OPTFLAGS = -O1
test_datastruct: DBGFLAGS = -fsanitize=address -fsanitize=undefined
test_datastruct: test_datastruct.cpp datastruct.h
test_concatenable_queue: OPTFLAGS = -O1
test_concatenable_queue: DBGFLAGS = -fsanitize=address -fsanitize=undefined
test_concatenable_queue: test_concatenable_queue.cpp concatenable_queue.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $< $(LIB_BOOST_TEST)
test_priority_queue: OPTFLAGS = -O1
test_priority_queue: DBGFLAGS = -fsanitize=address -fsanitize=undefined
test_priority_queue: test_priority_queue.cpp priority_queue.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $< $(LIB_BOOST_TEST)
.PHONY: clean
clean:
$(RM) run_matching run_matching_dbg test_mwmatching
$(RM) run_matching run_matching_dbg
$(RM) test_mwmatching
$(RM) test_concatenable_queue test_priority_queue

View File

@ -1,17 +1,17 @@
/*
* Data structures for matching.
/**
* Concatenable queue data structure.
*/
#ifndef MWMATCHING_CONCATENABLE_QUEUE_H_
#define MWMATCHING_CONCATENABLE_QUEUE_H_
#include <algorithm>
#include <cassert>
#include <limits>
#include <tuple>
#include <vector>
/* **************************************************
* ** class ConcatenableQueue **
* ************************************************** */
namespace mwmatching {
/**
* Priority queue supporting efficient merge and split operations.
@ -836,253 +836,6 @@ private:
};
/* **************************************************
* ** class PriorityQueue **
* ************************************************** */
} // namespace mwmatching
/**
* Min-priority queue implemented as a binary heap.
*
* Elements in a heap have a priority and associated "data".
*
* The following operations can be done efficiently:
* - Insert an element into the queue.
* - Remove an element from the queue.
* - Change the priority of a given element.
* - Find the element with lowest priority in the queue.
*/
template <typename PrioType,
typename DataType>
class PriorityQueue
{
public:
typedef unsigned int IndexType;
static constexpr IndexType INVALID_INDEX =
std::numeric_limits<IndexType>::max();
/**
* A Node instance represents an element in a PriorityQueue.
*
* A Node instance must remain valid while it is contained in a queue.
* The containing queue holds a pointer to the Node.
*
* A Node instance may be destructed if it is not contained in any queue.
* Alternatively, a Node instance may be destructed after its containing
* queue instance has been destructed.
*/
class Node
{
public:
/** Construct an invalid node, not contained in any queue. */
Node()
: index_(INVALID_INDEX)
{ }
// Prevent copying.
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
/** Return true if this node is contained in a queue. */
bool valid() const
{
return (index_ != INVALID_INDEX);
}
/**
* Return the priority of this node in the queue.
*
* The node must be contained in a queue.
* This function takes time O(1).
*/
PrioType prio() const
{
assert(index_ != INVALID_INDEX);
return prio_;
}
private:
IndexType index_;
PrioType prio_;
DataType data_;
friend class PriorityQueue;
};
/** Construct an empty queue. */
PriorityQueue()
{ }
// Prevent copying.
PriorityQueue(const PriorityQueue&) = delete;
PriorityQueue& operator=(const PriorityQueue&) = delete;
/**
* Remove all elements from the queue.
*
* This function takes time O(n).
*/
void clear()
{
for (Node* node : heap_) {
node->index_ = INVALID_INDEX;
}
heap_.clear();
}
/** Return true if the queue is empty. */
bool empty() const
{
return heap_.empty();
}
/**
* Return the minimum priority of any element in the queue.
*
* The queue must be non-empty.
* This function takes time O(1).
*/
PrioType min_prio() const
{
assert(! heap_.empty());
Node* top = heap_.front();
return top->prio_;
}
/**
* Return the element with minimum priority.
*
* The queue must be non-empty.
* This function takes time O(1).
*/
DataType min_elem() const
{
assert(! heap_.empty());
Node* top = heap_.front();
return top->data_;
}
/**
* Insert the given node into the queue with associated data.
*
* The node must not currently be contained in any queue.
* This function takes amortized time O(log(n)).
*/
void insert(Node* node, PrioType prio, const DataType& data)
{
assert(node->index_ == INVALID_INDEX);
node->index_ = heap_.size();
node->prio_ = prio;
node->data_ = data;
heap_.push_back(node);
sift_up(node->index_);
}
/**
* Update priority of an existing node.
*
* This function takes time O(log(n)).
*/
void set_prio(Node* node, PrioType prio)
{
IndexType index = node->index_;
assert(index != INVALID_INDEX);
assert(heap_[index] == node);
PrioType prev_prio = node->prio_;
node->prio_ = prio;
if (prio < prev_prio) {
sift_up(index);
} else if (prio > prev_prio) {
sift_down(index);
}
}
/**
* Remove the specified element from the queue.
*
* This function takes time O(log(n)).
*/
void remove(Node* node)
{
IndexType index = node->index_;
assert(index != INVALID_INDEX);
assert(heap_[index] == node);
node->index_ = INVALID_INDEX;
Node* move_node = heap_.back();
heap_.pop_back();
if (index < heap_.size()) {
heap_[index] = move_node;
move_node->index_ = index;
if (move_node->prio_ < node->prio_) {
sift_up(index);
} else if (move_node->prio_ > node->prio_) {
sift_down(index);
}
}
}
private:
/** Repair the heap along an ascending path to the root. */
void sift_up(IndexType index)
{
Node* node = heap_[index];
PrioType prio = node->prio_;
while (index > 0) {
IndexType next_index = (index - 1) / 2;
Node* next_node = heap_[next_index];
if (next_node->prio_ <= prio) {
break;
}
heap_[index] = next_node;
next_node->index_ = index;
index = next_index;
}
node->index_ = index;
heap_[index] = node;
}
/** Repair the heap along a descending path. */
void sift_down(IndexType index)
{
Node* node = heap_[index];
PrioType prio = node->prio_;
IndexType num_elem = heap_.size();
while (index < num_elem / 2) {
IndexType next_index = 2 * index + 1;
Node* next_node = heap_[next_index];
if (next_index + 1 < num_elem) {
Node* tmp_node = heap_[next_index + 1];
if (tmp_node->prio_ <= next_node->prio_) {
++next_index;
next_node = tmp_node;
}
}
if (next_node->prio_ >= prio) {
break;
}
heap_[index] = next_node;
next_node->index_ = index;
index = next_index;
}
heap_[index] = node;
node->index_ = index;
}
/** Heap data structure. */
std::vector<Node*> heap_;
};
#endif // MWMATCHING_CONCATENABLE_QUEUE_H_

View File

@ -19,7 +19,8 @@
#include <utility>
#include <vector>
#include "datastruct.h"
#include "concatenable_queue.hpp"
#include "priority_queue.hpp"
namespace mwmatching {

266
cpp/priority_queue.hpp Normal file
View File

@ -0,0 +1,266 @@
/**
* Priority queue data structure.
*/
#ifndef MWMATCHING_PRIORITY_QUEUE_H_
#define MWMATCHING_PRIORITY_QUEUE_H_
#include <cassert>
#include <limits>
#include <vector>
namespace mwmatching {
/**
* Min-priority queue implemented as a binary heap.
*
* Elements in a heap have a priority and associated "data".
*
* The following operations can be done efficiently:
* - Insert an element into the queue.
* - Remove an element from the queue.
* - Change the priority of a given element.
* - Find the element with lowest priority in the queue.
*/
template <typename PrioType,
typename DataType>
class PriorityQueue
{
public:
typedef unsigned int IndexType;
static constexpr IndexType INVALID_INDEX =
std::numeric_limits<IndexType>::max();
/**
* A Node instance represents an element in a PriorityQueue.
*
* A Node instance must remain valid while it is contained in a queue.
* The containing queue holds a pointer to the Node.
*
* A Node instance may be destructed if it is not contained in any queue.
* Alternatively, a Node instance may be destructed after its containing
* queue instance has been destructed.
*/
class Node
{
public:
/** Construct an invalid node, not contained in any queue. */
Node()
: index_(INVALID_INDEX)
{ }
// Prevent copying.
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
/** Return true if this node is contained in a queue. */
bool valid() const
{
return (index_ != INVALID_INDEX);
}
/**
* Return the priority of this node in the queue.
*
* The node must be contained in a queue.
* This function takes time O(1).
*/
PrioType prio() const
{
assert(index_ != INVALID_INDEX);
return prio_;
}
private:
IndexType index_;
PrioType prio_;
DataType data_;
friend class PriorityQueue;
};
/** Construct an empty queue. */
PriorityQueue()
{ }
// Prevent copying.
PriorityQueue(const PriorityQueue&) = delete;
PriorityQueue& operator=(const PriorityQueue&) = delete;
/**
* Remove all elements from the queue.
*
* This function takes time O(n).
*/
void clear()
{
for (Node* node : heap_) {
node->index_ = INVALID_INDEX;
}
heap_.clear();
}
/** Return true if the queue is empty. */
bool empty() const
{
return heap_.empty();
}
/**
* Return the minimum priority of any element in the queue.
*
* The queue must be non-empty.
* This function takes time O(1).
*/
PrioType min_prio() const
{
assert(! heap_.empty());
Node* top = heap_.front();
return top->prio_;
}
/**
* Return the element with minimum priority.
*
* The queue must be non-empty.
* This function takes time O(1).
*/
DataType min_elem() const
{
assert(! heap_.empty());
Node* top = heap_.front();
return top->data_;
}
/**
* Insert the given node into the queue with associated data.
*
* The node must not currently be contained in any queue.
* This function takes amortized time O(log(n)).
*/
void insert(Node* node, PrioType prio, const DataType& data)
{
assert(node->index_ == INVALID_INDEX);
node->index_ = heap_.size();
node->prio_ = prio;
node->data_ = data;
heap_.push_back(node);
sift_up(node->index_);
}
/**
* Update priority of an existing node.
*
* This function takes time O(log(n)).
*/
void set_prio(Node* node, PrioType prio)
{
IndexType index = node->index_;
assert(index != INVALID_INDEX);
assert(heap_[index] == node);
PrioType prev_prio = node->prio_;
node->prio_ = prio;
if (prio < prev_prio) {
sift_up(index);
} else if (prio > prev_prio) {
sift_down(index);
}
}
/**
* Remove the specified element from the queue.
*
* This function takes time O(log(n)).
*/
void remove(Node* node)
{
IndexType index = node->index_;
assert(index != INVALID_INDEX);
assert(heap_[index] == node);
node->index_ = INVALID_INDEX;
Node* move_node = heap_.back();
heap_.pop_back();
if (index < heap_.size()) {
heap_[index] = move_node;
move_node->index_ = index;
if (move_node->prio_ < node->prio_) {
sift_up(index);
} else if (move_node->prio_ > node->prio_) {
sift_down(index);
}
}
}
private:
/** Repair the heap along an ascending path to the root. */
void sift_up(IndexType index)
{
Node* node = heap_[index];
PrioType prio = node->prio_;
while (index > 0) {
IndexType next_index = (index - 1) / 2;
Node* next_node = heap_[next_index];
if (next_node->prio_ <= prio) {
break;
}
heap_[index] = next_node;
next_node->index_ = index;
index = next_index;
}
node->index_ = index;
heap_[index] = node;
}
/** Repair the heap along a descending path. */
void sift_down(IndexType index)
{
Node* node = heap_[index];
PrioType prio = node->prio_;
IndexType num_elem = heap_.size();
while (index < num_elem / 2) {
IndexType next_index = 2 * index + 1;
Node* next_node = heap_[next_index];
if (next_index + 1 < num_elem) {
Node* tmp_node = heap_[next_index + 1];
if (tmp_node->prio_ <= next_node->prio_) {
++next_index;
next_node = tmp_node;
}
}
if (next_node->prio_ >= prio) {
break;
}
heap_[index] = next_node;
next_node->index_ = index;
index = next_index;
}
heap_[index] = node;
node->index_ = index;
}
/** Heap data structure. */
std::vector<Node*> heap_;
};
} // namespace mwmatching
#endif // MWMATCHING_PRIORITY_QUEUE_H_

View File

@ -1,33 +1,29 @@
/*
* Unit tests for data structures.
* Unit tests for ConcatenableQueue data structure.
*
* Depends on the Boost.Test unit test framework.
* Tested with Boost v1.74, available from https://www.boost.org/
*/
#include <algorithm>
#include <climits>
#include <memory>
#include <random>
#include <set>
#include <string>
#include <tuple>
#include <unordered_map>
#include <vector>
#define BOOST_TEST_MODULE datastruct
#include <boost/test/unit_test.hpp>
#include "datastruct.h"
#include "concatenable_queue.hpp"
/* ********** Test ConcatenableQueue ********** */
BOOST_AUTO_TEST_SUITE(test_concatenable_queue)
BOOST_AUTO_TEST_CASE(test_single)
{
using Queue = ConcatenableQueue<int, std::string, std::string>;
using Queue = mwmatching::ConcatenableQueue<int, std::string, std::string>;
Queue q("Q");
BOOST_TEST(q.name() == std::string("Q"));
@ -49,7 +45,7 @@ BOOST_AUTO_TEST_CASE(test_single)
BOOST_AUTO_TEST_CASE(test_simple)
{
using Queue = ConcatenableQueue<int, std::string, char>;
using Queue = mwmatching::ConcatenableQueue<int, std::string, char>;
Queue::Node n1, n2, n3, n4, n5;
Queue q1("A");
@ -140,7 +136,7 @@ BOOST_AUTO_TEST_CASE(test_simple)
BOOST_AUTO_TEST_CASE(test_medium)
{
using Queue = ConcatenableQueue<int, char, char>;
using Queue = mwmatching::ConcatenableQueue<int, char, char>;
std::vector<char> queue_names(19);
for (int i = 0; i < 14; i++) {
@ -255,7 +251,7 @@ BOOST_AUTO_TEST_CASE(test_medium)
BOOST_AUTO_TEST_CASE(test_random)
{
using Queue = ConcatenableQueue<double, int, int>;
using Queue = mwmatching::ConcatenableQueue<double, int, int>;
constexpr int NUM_NODES = 4000;
@ -405,238 +401,3 @@ BOOST_AUTO_TEST_CASE(test_random)
}
BOOST_AUTO_TEST_SUITE_END()
/* ********** Test PriorityQueue ********** */
BOOST_AUTO_TEST_SUITE(test_priority_queue)
BOOST_AUTO_TEST_CASE(test_empty)
{
using Queue = PriorityQueue<int, std::string>;
Queue q;
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_single)
{
using Queue = PriorityQueue<int, std::string>;
Queue q;
Queue::Node n1;
BOOST_TEST(n1.valid() == false);
q.insert(&n1, 5, "a");
BOOST_TEST(n1.valid() == true);
BOOST_TEST(n1.prio() == 5);
BOOST_TEST(q.empty() == false);
BOOST_TEST(q.min_prio() == 5);
BOOST_TEST(q.min_elem() == std::string("a"));
q.set_prio(&n1, 3);
BOOST_TEST(n1.prio() == 3);
BOOST_TEST(q.min_prio() == 3);
BOOST_TEST(q.min_elem() == std::string("a"));
q.remove(&n1);
BOOST_TEST(n1.valid() == false);
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_simple)
{
using Queue = PriorityQueue<int, char>;
Queue q;
Queue::Node nodes[10];
q.insert(&nodes[0], 9, 'a');
BOOST_TEST(q.min_prio() == 9);
BOOST_TEST(q.min_elem() == 'a');
q.insert(&nodes[1], 4, 'b');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[2], 7, 'c');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[3], 5, 'd');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[4], 8, 'e');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[5], 6, 'f');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[6], 4, 'g');
q.insert(&nodes[7], 5, 'h');
q.insert(&nodes[8], 2, 'i');
BOOST_TEST(q.min_prio() == 2);
BOOST_TEST(q.min_elem() == 'i');
q.insert(&nodes[9], 6, 'j');
BOOST_TEST(q.min_prio() == 2);
BOOST_TEST(q.min_elem() == 'i');
q.set_prio(&nodes[2], 1);
BOOST_TEST(q.min_prio() == 1);
BOOST_TEST(q.min_elem() == 'c');
q.set_prio(&nodes[4], 3);
BOOST_TEST(q.min_prio() == 1);
BOOST_TEST(q.min_elem() == 'c');
q.remove(&nodes[2]);
BOOST_TEST(q.min_prio() == 2);
BOOST_TEST(q.min_elem() == 'i');
q.remove(&nodes[8]);
BOOST_TEST(q.min_prio() == 3);
BOOST_TEST(q.min_elem() == 'e');
q.remove(&nodes[4]);
q.remove(&nodes[1]);
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'g');
q.remove(&nodes[3]);
q.remove(&nodes[9]);
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'g');
q.remove(&nodes[6]);
BOOST_TEST(q.min_prio() == 5);
BOOST_TEST(q.min_elem() == 'h');
BOOST_TEST(q.empty() == false);
q.clear();
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_increase_prio)
{
using Queue = PriorityQueue<int, char>;
Queue q;
Queue::Node n1;
q.insert(&n1, 5, 'a');
q.set_prio(&n1, 8);
BOOST_TEST(n1.prio() == 8);
BOOST_TEST(q.min_prio() == 8);
q.clear();
Queue::Node n2, n3, n4;
q.insert(&n1, 9, 'A');
q.insert(&n2, 4, 'b');
q.insert(&n3, 7, 'c');
q.insert(&n4, 5, 'd');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.set_prio(&n2, 8);
BOOST_TEST(n2.prio() == 8);
BOOST_TEST(q.min_elem() == 'd');
BOOST_TEST(q.min_prio() == 5);
q.set_prio(&n3, 10);
BOOST_TEST(n3.prio() == 10);
BOOST_TEST(q.min_elem() == 'd');
q.remove(&n4);
BOOST_TEST(q.min_elem() == 'b');
q.remove(&n2);
BOOST_TEST(q.min_prio() == 9);
BOOST_TEST(q.min_elem() == 'A');
q.remove(&n1);
BOOST_TEST(q.min_elem() == 'c');
BOOST_TEST(q.min_prio() == 10);
q.remove(&n3);
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_random)
{
using Queue = PriorityQueue<int, int>;
Queue q;
const int num_elem = 1000;
std::vector<std::tuple<std::unique_ptr<Queue::Node>, int, int>> elems;
int next_data = 0;
std::mt19937 rng(34567);
auto check = [&q,&elems]() {
int min_prio = q.min_prio();
int min_data = q.min_elem();
int best_prio = INT_MAX;
bool found = false;
for (const auto& v : elems) {
int this_prio = std::get<1>(v);
int this_data = std::get<2>(v);
best_prio = std::min(best_prio, this_prio);
if ((this_prio == min_prio) && (this_data == min_data)) {
found = true;
}
}
BOOST_TEST(found == true);
BOOST_TEST(min_prio == best_prio);
};
for (int i = 0; i < num_elem; ++i) {
++next_data;
int prio = std::uniform_int_distribution<>(0, 1000000)(rng);
std::unique_ptr<Queue::Node> nptr(new Queue::Node);
q.insert(nptr.get(), prio, next_data);
elems.push_back(std::make_tuple(std::move(nptr), prio, next_data));
check();
}
for (int i = 0; i < 10000; ++i) {
int p = std::uniform_int_distribution<>(0, num_elem - 1)(rng);
Queue::Node* node = std::get<0>(elems[p]).get();
int prio = std::get<1>(elems[p]);
prio = std::uniform_int_distribution<>(0, 1000000)(rng);
q.set_prio(node, prio);
std::get<1>(elems[p]) = prio;
check();
p = std::uniform_int_distribution<>(0, num_elem - 1)(rng);
node = std::get<0>(elems[p]).get();
q.remove(node);
elems.erase(elems.begin() + p);
check();
++next_data;
prio = std::uniform_int_distribution<>(0, 1000000)(rng);
std::unique_ptr<Queue::Node> nptr(new Queue::Node);
q.insert(nptr.get(), prio, next_data);
elems.push_back(std::make_tuple(std::move(nptr), prio, next_data));
check();
}
for (int i = 0; i < num_elem; ++i) {
int p = std::uniform_int_distribution<>(0, num_elem - 1 - i)(rng);
Queue::Node* node = std::get<0>(elems[p]).get();
q.remove(node);
elems.erase(elems.begin() + p);
if (! elems.empty()) {
check();
}
}
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_SUITE_END()

252
cpp/test_priority_queue.cpp Normal file
View File

@ -0,0 +1,252 @@
/*
* Unit tests for PriorityQueue data structure.
*
* Depends on the Boost.Test unit test framework.
* Tested with Boost v1.74, available from https://www.boost.org/
*/
#include <algorithm>
#include <climits>
#include <memory>
#include <random>
#include <string>
#include <tuple>
#include <vector>
#define BOOST_TEST_MODULE datastruct
#include <boost/test/unit_test.hpp>
#include "priority_queue.hpp"
BOOST_AUTO_TEST_SUITE(test_priority_queue)
BOOST_AUTO_TEST_CASE(test_empty)
{
using Queue = mwmatching::PriorityQueue<int, std::string>;
Queue q;
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_single)
{
using Queue = mwmatching::PriorityQueue<int, std::string>;
Queue q;
Queue::Node n1;
BOOST_TEST(n1.valid() == false);
q.insert(&n1, 5, "a");
BOOST_TEST(n1.valid() == true);
BOOST_TEST(n1.prio() == 5);
BOOST_TEST(q.empty() == false);
BOOST_TEST(q.min_prio() == 5);
BOOST_TEST(q.min_elem() == std::string("a"));
q.set_prio(&n1, 3);
BOOST_TEST(n1.prio() == 3);
BOOST_TEST(q.min_prio() == 3);
BOOST_TEST(q.min_elem() == std::string("a"));
q.remove(&n1);
BOOST_TEST(n1.valid() == false);
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_simple)
{
using Queue = mwmatching::PriorityQueue<int, char>;
Queue q;
Queue::Node nodes[10];
q.insert(&nodes[0], 9, 'a');
BOOST_TEST(q.min_prio() == 9);
BOOST_TEST(q.min_elem() == 'a');
q.insert(&nodes[1], 4, 'b');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[2], 7, 'c');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[3], 5, 'd');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[4], 8, 'e');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[5], 6, 'f');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.insert(&nodes[6], 4, 'g');
q.insert(&nodes[7], 5, 'h');
q.insert(&nodes[8], 2, 'i');
BOOST_TEST(q.min_prio() == 2);
BOOST_TEST(q.min_elem() == 'i');
q.insert(&nodes[9], 6, 'j');
BOOST_TEST(q.min_prio() == 2);
BOOST_TEST(q.min_elem() == 'i');
q.set_prio(&nodes[2], 1);
BOOST_TEST(q.min_prio() == 1);
BOOST_TEST(q.min_elem() == 'c');
q.set_prio(&nodes[4], 3);
BOOST_TEST(q.min_prio() == 1);
BOOST_TEST(q.min_elem() == 'c');
q.remove(&nodes[2]);
BOOST_TEST(q.min_prio() == 2);
BOOST_TEST(q.min_elem() == 'i');
q.remove(&nodes[8]);
BOOST_TEST(q.min_prio() == 3);
BOOST_TEST(q.min_elem() == 'e');
q.remove(&nodes[4]);
q.remove(&nodes[1]);
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'g');
q.remove(&nodes[3]);
q.remove(&nodes[9]);
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'g');
q.remove(&nodes[6]);
BOOST_TEST(q.min_prio() == 5);
BOOST_TEST(q.min_elem() == 'h');
BOOST_TEST(q.empty() == false);
q.clear();
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_increase_prio)
{
using Queue = mwmatching::PriorityQueue<int, char>;
Queue q;
Queue::Node n1;
q.insert(&n1, 5, 'a');
q.set_prio(&n1, 8);
BOOST_TEST(n1.prio() == 8);
BOOST_TEST(q.min_prio() == 8);
q.clear();
Queue::Node n2, n3, n4;
q.insert(&n1, 9, 'A');
q.insert(&n2, 4, 'b');
q.insert(&n3, 7, 'c');
q.insert(&n4, 5, 'd');
BOOST_TEST(q.min_prio() == 4);
BOOST_TEST(q.min_elem() == 'b');
q.set_prio(&n2, 8);
BOOST_TEST(n2.prio() == 8);
BOOST_TEST(q.min_elem() == 'd');
BOOST_TEST(q.min_prio() == 5);
q.set_prio(&n3, 10);
BOOST_TEST(n3.prio() == 10);
BOOST_TEST(q.min_elem() == 'd');
q.remove(&n4);
BOOST_TEST(q.min_elem() == 'b');
q.remove(&n2);
BOOST_TEST(q.min_prio() == 9);
BOOST_TEST(q.min_elem() == 'A');
q.remove(&n1);
BOOST_TEST(q.min_elem() == 'c');
BOOST_TEST(q.min_prio() == 10);
q.remove(&n3);
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_CASE(test_random)
{
using Queue = mwmatching::PriorityQueue<int, int>;
Queue q;
const int num_elem = 1000;
std::vector<std::tuple<std::unique_ptr<Queue::Node>, int, int>> elems;
int next_data = 0;
std::mt19937 rng(34567);
auto check = [&q,&elems]() {
int min_prio = q.min_prio();
int min_data = q.min_elem();
int best_prio = INT_MAX;
bool found = false;
for (const auto& v : elems) {
int this_prio = std::get<1>(v);
int this_data = std::get<2>(v);
best_prio = std::min(best_prio, this_prio);
if ((this_prio == min_prio) && (this_data == min_data)) {
found = true;
}
}
BOOST_TEST(found == true);
BOOST_TEST(min_prio == best_prio);
};
for (int i = 0; i < num_elem; ++i) {
++next_data;
int prio = std::uniform_int_distribution<>(0, 1000000)(rng);
std::unique_ptr<Queue::Node> nptr(new Queue::Node);
q.insert(nptr.get(), prio, next_data);
elems.push_back(std::make_tuple(std::move(nptr), prio, next_data));
check();
}
for (int i = 0; i < 10000; ++i) {
int p = std::uniform_int_distribution<>(0, num_elem - 1)(rng);
Queue::Node* node = std::get<0>(elems[p]).get();
int prio = std::get<1>(elems[p]);
prio = std::uniform_int_distribution<>(0, 1000000)(rng);
q.set_prio(node, prio);
std::get<1>(elems[p]) = prio;
check();
p = std::uniform_int_distribution<>(0, num_elem - 1)(rng);
node = std::get<0>(elems[p]).get();
q.remove(node);
elems.erase(elems.begin() + p);
check();
++next_data;
prio = std::uniform_int_distribution<>(0, 1000000)(rng);
std::unique_ptr<Queue::Node> nptr(new Queue::Node);
q.insert(nptr.get(), prio, next_data);
elems.push_back(std::make_tuple(std::move(nptr), prio, next_data));
check();
}
for (int i = 0; i < num_elem; ++i) {
int p = std::uniform_int_distribution<>(0, num_elem - 1 - i)(rng);
Queue::Node* node = std::get<0>(elems[p]).get();
q.remove(node);
elems.erase(elems.begin() + p);
if (! elems.empty()) {
check();
}
}
BOOST_TEST(q.empty() == true);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -9,8 +9,10 @@ echo
g++ --version
echo
make -C cpp run_matching test_mwmatching
make -C cpp run_matching test_mwmatching test_concatenable_queue test_priority_queue
cpp/test_mwmatching
cpp/test_concatenable_queue
cpp/test_priority_queue
echo
echo ">> Running test graphs"