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:
parent
087799cdca
commit
0f18b7b05a
23
cpp/Makefile
23
cpp/Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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_
|
|
@ -19,7 +19,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "datastruct.h"
|
||||
#include "concatenable_queue.hpp"
|
||||
#include "priority_queue.hpp"
|
||||
|
||||
|
||||
namespace mwmatching {
|
||||
|
|
|
@ -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_
|
|
@ -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()
|
|
@ -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()
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue