C++ datastructures for O(n*m*log(n))
This commit is contained in:
		
							parent
							
								
									e103a493fc
								
							
						
					
					
						commit
						efb238ff8e
					
				|  | @ -21,6 +21,11 @@ test_mwmatching: DBGFLAGS = -fsanitize=address -fsanitize=undefined | |||
| test_mwmatching: test_mwmatching.cpp mwmatching.h | ||||
| 	$(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 | ||||
| 	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $< $(LIB_BOOST_TEST) | ||||
| 
 | ||||
| .PHONY: clean | ||||
| clean: | ||||
| 	$(RM) run_matching run_matching_dbg test_mwmatching | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,660 @@ | |||
| /*
 | ||||
|  * Unit tests for data structures. | ||||
|  * | ||||
|  * Depends on the Boost.Test unit test framework. | ||||
|  * Tested with Boost v1.74, available from https://www.boost.org/
 | ||||
|  */ | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <climits> | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <random> | ||||
| #include <string> | ||||
| #include <tuple> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| 
 | ||||
| #define BOOST_TEST_MODULE datastruct | ||||
| #include <boost/test/unit_test.hpp> | ||||
| 
 | ||||
| #include "datastruct.h" | ||||
| 
 | ||||
| 
 | ||||
| /* **********  Test DisjointSetNode  ********** */ | ||||
| 
 | ||||
| BOOST_AUTO_TEST_SUITE(test_disjoint_set) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_single) | ||||
| { | ||||
|     using Node = DisjointSetNode<int>; | ||||
|     Node a(1); | ||||
|     BOOST_TEST(a.find() == 1); | ||||
|     a.set_label(2); | ||||
|     BOOST_TEST(a.find() == 2); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_simple) | ||||
| { | ||||
|     using Node = DisjointSetNode<int>; | ||||
|     Node a(1); | ||||
|     Node b(2); | ||||
|     Node c(3); | ||||
|     Node* m = a.merge(&b); | ||||
|     m->set_label(10); | ||||
|     BOOST_TEST(a.find() == 10); | ||||
|     BOOST_TEST(b.find() == 10); | ||||
|     BOOST_TEST(c.find() == 3); | ||||
|     m = m->merge(&c); | ||||
|     m->set_label(11); | ||||
|     BOOST_TEST(a.find() == 11); | ||||
|     BOOST_TEST(c.find() == 11); | ||||
|     a.detach(1); | ||||
|     b.detach(2); | ||||
|     c.detach(3); | ||||
|     BOOST_TEST(a.find() == 1); | ||||
|     BOOST_TEST(b.find() == 2); | ||||
|     BOOST_TEST(c.find() == 3); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_multilevel) | ||||
| { | ||||
|     using Node = DisjointSetNode<int>; | ||||
| 
 | ||||
|     std::unique_ptr<Node> nodes[27]; | ||||
|     for (int i = 0; i < 27; ++i) { | ||||
|         nodes[i].reset(new Node(i)); | ||||
|     } | ||||
| 
 | ||||
|     std::vector<Node*> level1; | ||||
|     for (int i = 0; i < 9; ++i) { | ||||
|         Node* m = nodes[3*i].get(); | ||||
|         for (int k = 1; k < 3; ++k) { | ||||
|             m = m->merge(nodes[3*i+k].get()); | ||||
|         } | ||||
|         m->set_label(100 + i); | ||||
|         level1.push_back(m); | ||||
|     } | ||||
| 
 | ||||
|     std::vector<Node*> level2; | ||||
|     for (int i = 0; i < 3; ++i) { | ||||
|         Node* m = level1[3*i]; | ||||
|         for (int k = 1; k < 3; ++k) { | ||||
|             m = m->merge(level1[3*i+k]); | ||||
|         } | ||||
|         m->set_label(200 + i); | ||||
|         level2.push_back(m); | ||||
|     } | ||||
| 
 | ||||
|     Node* m = level2[0]; | ||||
|     for (int k = 1; k < 3; ++k) { | ||||
|         m = m->merge(level2[k]); | ||||
|     } | ||||
|     m->set_label(300); | ||||
| 
 | ||||
|     for (int i = 0; i < 27; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == 300); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 3; ++i) { | ||||
|         level2[i]->detach(200 + i); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 27; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == 200 + i / 9); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 6; ++i) { | ||||
|         level1[i]->detach(100 + i); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 18; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == 100 + i / 3); | ||||
|     } | ||||
|     for (int i = 18; i < 27; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == 202); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 6; i < 9; ++i) { | ||||
|         level1[i]->detach(100 + i); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 27; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == 100 + i / 3); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 6; i < 27; ++i) { | ||||
|         nodes[i]->detach(i); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 6; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == 100 + i / 3); | ||||
|     } | ||||
|     for (int i = 6; i < 27; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == i); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 6; i++) { | ||||
|         nodes[i]->detach(i); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < 27; ++i) { | ||||
|         BOOST_TEST(nodes[i]->find() == i); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_random) | ||||
| { | ||||
|     using Node = DisjointSetNode<int>; | ||||
| 
 | ||||
|     std::mt19937 rng(12345); | ||||
| 
 | ||||
|     const int num_nodes = 1000; | ||||
|     std::unique_ptr<Node> nodes[num_nodes]; | ||||
|     for (int i = 0; i < num_nodes; ++i) { | ||||
|         nodes[i].reset(new Node(i)); | ||||
|     } | ||||
| 
 | ||||
|     std::unordered_map<int, Node*> blossoms; | ||||
|     std::unordered_map<int, std::vector<int>> sub_blossoms; | ||||
|     std::vector<int> top_blossoms; | ||||
| 
 | ||||
|     for (int i = 0; i < num_nodes; ++i) { | ||||
|         blossoms[i] = nodes[i].get(); | ||||
|         top_blossoms.push_back(i); | ||||
|     } | ||||
| 
 | ||||
|     int next_blossom = num_nodes; | ||||
| 
 | ||||
|     auto make_blossom = [&]() { | ||||
|         int b = next_blossom; | ||||
|         ++next_blossom; | ||||
| 
 | ||||
|         int nsub = 1 + 2 * std::uniform_int_distribution<>(1, 4)(rng); | ||||
|         std::vector<int> subs(nsub); | ||||
| 
 | ||||
|         for (int i = 0; i < nsub; ++i) { | ||||
|             int p = std::uniform_int_distribution<>(0, top_blossoms.size() - 1)(rng); | ||||
|             subs[i] = top_blossoms[p]; | ||||
|             top_blossoms.erase(top_blossoms.begin() + p); | ||||
|         } | ||||
| 
 | ||||
|         Node *m = blossoms[subs[0]]; | ||||
|         for (int i = 1; i < nsub; ++i) { | ||||
|             m = m->merge(blossoms[subs[i]]); | ||||
|         } | ||||
|         m->set_label(b); | ||||
| 
 | ||||
|         blossoms[b] = m; | ||||
|         sub_blossoms[b] = std::move(subs); | ||||
|         top_blossoms.push_back(b); | ||||
|     }; | ||||
| 
 | ||||
|     auto expand_blossom = [&](int b) { | ||||
|         top_blossoms.erase( | ||||
|             std::find(top_blossoms.begin(), top_blossoms.end(), b)); | ||||
|         for (int t : sub_blossoms[b]) { | ||||
|             blossoms[t]->detach(t); | ||||
|             top_blossoms.push_back(t); | ||||
|         } | ||||
|         blossoms.erase(b); | ||||
|         sub_blossoms.erase(b); | ||||
|     }; | ||||
| 
 | ||||
|     auto check_membership = [&](int b, int label) { | ||||
|         std::vector<int> q; | ||||
|         q.push_back(b); | ||||
|         while (! q.empty()) { | ||||
|             b = q.back(); | ||||
|             q.pop_back(); | ||||
|             if (b < num_nodes) { | ||||
|                 BOOST_TEST(nodes[b]->find() == label); | ||||
|             } else { | ||||
|                 for (int t : sub_blossoms[b]) { | ||||
|                     q.push_back(t); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     for (int k = 0; k < 100; ++k) { | ||||
|         make_blossom(); | ||||
|     } | ||||
| 
 | ||||
|     for (int b : top_blossoms) { | ||||
|         check_membership(b, b); | ||||
|     } | ||||
| 
 | ||||
|     std::vector<int> top_groups; | ||||
|     for (int b : top_blossoms) { | ||||
|         if (b >= num_nodes) { | ||||
|             top_groups.push_back(b); | ||||
|         } | ||||
|     } | ||||
|     std::shuffle(top_groups.begin(), top_groups.end(), rng); | ||||
|     for (int k = 0; k < 50; ++k) { | ||||
|         expand_blossom(top_groups[k]); | ||||
|     } | ||||
|     top_groups.clear(); | ||||
| 
 | ||||
|     for (int b : top_blossoms) { | ||||
|         check_membership(b, b); | ||||
|     } | ||||
| 
 | ||||
|     for (int k = 0; k < 50; ++k) { | ||||
|         make_blossom(); | ||||
|     } | ||||
| 
 | ||||
|     for (int b : top_blossoms) { | ||||
|         check_membership(b, b); | ||||
|     } | ||||
| 
 | ||||
|     for (int b : top_blossoms) { | ||||
|         if (b >= num_nodes) { | ||||
|             top_groups.push_back(b); | ||||
|         } | ||||
|     } | ||||
|     std::shuffle(top_groups.begin(), top_groups.end(), rng); | ||||
|     for (int b : top_groups) { | ||||
|         expand_blossom(b); | ||||
|     } | ||||
| 
 | ||||
|     for (int b : top_blossoms) { | ||||
|         check_membership(b, b); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_SUITE_END() | ||||
| 
 | ||||
| 
 | ||||
| /* **********  Test SplittablePriorityQueue  ********** */ | ||||
| 
 | ||||
| template <typename PrioType, typename DataType> | ||||
| static void check_min_elem(const std::pair<PrioType, DataType>& pair, | ||||
|                            PrioType prio, | ||||
|                            const DataType& data) | ||||
| { | ||||
|     BOOST_TEST(pair.first == prio); | ||||
|     BOOST_TEST(pair.second == data); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| BOOST_AUTO_TEST_SUITE(test_splittable_queue) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_single) | ||||
| { | ||||
|     using Queue = SplittablePriorityQueue<int, int>; | ||||
|     Queue q; | ||||
| 
 | ||||
|     BOOST_TEST(q.empty() == true); | ||||
| 
 | ||||
|     Queue::Node n; | ||||
|     q.insert(&n, 3, 4, 101); | ||||
| 
 | ||||
|     BOOST_TEST(q.empty() == false); | ||||
|     check_min_elem(q.find_min(), 4, 101); | ||||
| 
 | ||||
|     q.update(&n, 5, 102); | ||||
|     check_min_elem(q.find_min(), 4, 101); | ||||
| 
 | ||||
|     q.update(&n, 3, 103); | ||||
|     check_min_elem(q.find_min(), 3, 103); | ||||
| 
 | ||||
|     q.clear(); | ||||
|     BOOST_TEST(q.empty() == true); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_simple) | ||||
| { | ||||
|     using Queue = SplittablePriorityQueue<int, std::string>; | ||||
|     Queue q; | ||||
|     Queue::Node n1, n2, n3, n4, n5, nx; | ||||
| 
 | ||||
|     q.insert(&n1, 1, 5, "a"); | ||||
|     q.insert(&n2, 2, 6, "b"); | ||||
|     q.insert(&n3, 3, 7, "c"); | ||||
|     q.insert(&n4, 4, 4, "d"); | ||||
|     q.insert(&n5, 5, 3, "e"); | ||||
|     check_min_elem(q.find_min(), 3, std::string("e")); | ||||
| 
 | ||||
|     q.update(&n1, 4, "f"); | ||||
|     check_min_elem(q.find_min(), 3, std::string("e")); | ||||
| 
 | ||||
|     q.update(&n3, 2, "h"); | ||||
|     check_min_elem(q.find_min(), 2, std::string("h")); | ||||
| 
 | ||||
|     Queue q2 = q.split(3); | ||||
|     check_min_elem(q.find_min(), 4, std::string("f")); | ||||
|     check_min_elem(q2.find_min(), 2, std::string("h")); | ||||
| 
 | ||||
|     q.insert(&nx, 3, 1, "x"); | ||||
|     check_min_elem(q.find_min(), 1, std::string("x")); | ||||
|     check_min_elem(q2.find_min(), 2, std::string("h")); | ||||
| 
 | ||||
|     q.clear(); | ||||
|     q2.clear(); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_split_empty) | ||||
| { | ||||
|     using Queue = SplittablePriorityQueue<int, std::string>; | ||||
|     Queue q; | ||||
|     Queue q2 = q.split(10); | ||||
|     BOOST_TEST(q.empty() == true); | ||||
|     BOOST_TEST(q2.empty() == true); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_split_oneway) | ||||
| { | ||||
|     using Queue = SplittablePriorityQueue<int, std::string>; | ||||
|     Queue q; | ||||
|     Queue::Node n4, n5, n6; | ||||
|     q.insert(&n4, 4, 3, "a"); | ||||
|     q.insert(&n5, 5, 4, "b"); | ||||
|     q.insert(&n6, 6, 2, "c"); | ||||
|     Queue q2 = q.split(7); | ||||
|     BOOST_TEST(q.empty() == false); | ||||
|     BOOST_TEST(q2.empty() == true); | ||||
|     check_min_elem(q.find_min(), 2, std::string("c")); | ||||
|     q.clear(); | ||||
| 
 | ||||
|     q.insert(&n4, 4, 3, "a"); | ||||
|     q.insert(&n5, 5, 4, "b"); | ||||
|     q.insert(&n6, 6, 2, "c"); | ||||
|     q2 = q.split(4); | ||||
|     BOOST_TEST(q.empty() == true); | ||||
|     BOOST_TEST(q2.empty() == false); | ||||
|     check_min_elem(q2.find_min(), 2, std::string("c")); | ||||
|     q2.clear(); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_larger) | ||||
| { | ||||
|     using Queue = SplittablePriorityQueue<int, std::string>; | ||||
|     Queue q; | ||||
|     Queue::Node nodes[15]; | ||||
| 
 | ||||
|     q.insert(&nodes[7],   7, 5, "h"); | ||||
|     q.insert(&nodes[6],   6, 4, "g"); | ||||
|     q.insert(&nodes[8],   8, 2, "i"); | ||||
|     q.insert(&nodes[5],   5, 4, "f"); | ||||
|     q.insert(&nodes[9],   9, 6, "j"); | ||||
|     q.insert(&nodes[4],   4, 8, "e"); | ||||
|     q.insert(&nodes[10], 10, 4, "k"); | ||||
|     q.insert(&nodes[3],   3, 5, "d"); | ||||
|     q.insert(&nodes[11], 11, 6, "l"); | ||||
|     q.insert(&nodes[2],   2, 7, "c"); | ||||
|     q.insert(&nodes[12], 12, 8, "m"); | ||||
|     q.insert(&nodes[1],   1, 3, "b"); | ||||
|     q.insert(&nodes[13], 13, 1, "n"); | ||||
|     q.insert(&nodes[0],   0, 9, "a"); | ||||
|     q.insert(&nodes[14], 14, 7, "o"); | ||||
| 
 | ||||
|     check_min_elem(q.find_min(), 1, std::string("n")); | ||||
| 
 | ||||
|     Queue q2 = q.split(10); | ||||
|     check_min_elem(q.find_min(), 2, std::string("i")); | ||||
|     check_min_elem(q2.find_min(), 1, std::string("n")); | ||||
| 
 | ||||
|     Queue q1 = q.split(5); | ||||
|     check_min_elem(q.find_min(), 3, std::string("b")); | ||||
|     check_min_elem(q1.find_min(), 2, std::string("i")); | ||||
|     check_min_elem(q2.find_min(), 1, std::string("n")); | ||||
| 
 | ||||
|     q.clear(); | ||||
|     q1.clear(); | ||||
|     q2.clear(); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(test_random) | ||||
| { | ||||
|     using Queue = SplittablePriorityQueue<int, int>; | ||||
| 
 | ||||
|     std::mt19937 rng(23456); | ||||
|     std::uniform_int_distribution<> index_distribution(0, 1000000); | ||||
|     std::uniform_int_distribution<> prio_distribution(0, 1000000); | ||||
| 
 | ||||
|     Queue q; | ||||
|     std::map<int, std::tuple<std::unique_ptr<Queue::Node>, int, int>> elems; | ||||
|     int next_data = 1; | ||||
| 
 | ||||
|     for (int i = 0; i < 200; ++i) { | ||||
| 
 | ||||
|         // Insert stuff into the queue.
 | ||||
|         for (int k = 0; k < 1000; ++k) { | ||||
|             int idx = index_distribution(rng); | ||||
|             int prio = prio_distribution(rng); | ||||
|             int data = next_data; | ||||
|             ++next_data; | ||||
|             auto it = elems.find(idx); | ||||
|             if (it != elems.end()) { | ||||
|                 auto& v = it->second; | ||||
|                 Queue::Node* nptr = std::get<0>(v).get(); | ||||
|                 int pprio = std::get<1>(v); | ||||
|                 q.update(nptr, prio, data); | ||||
|                 if (prio < pprio) { | ||||
|                     std::get<1>(v) = prio; | ||||
|                     std::get<2>(v) = data; | ||||
|                 } | ||||
|             } else { | ||||
|                 std::unique_ptr<Queue::Node> nptr(new Queue::Node); | ||||
|                 q.insert(nptr.get(), idx, prio, data); | ||||
|                 elems[idx] = std::make_tuple(std::move(nptr), prio, data); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Check min element.
 | ||||
|         int min_prio = INT_MAX; | ||||
|         int min_data = 0; | ||||
|         for (const auto& v : elems) { | ||||
|             int prio = std::get<1>(v.second); | ||||
|             int data = std::get<2>(v.second); | ||||
|             if (prio < min_prio) { | ||||
|                 min_prio = prio; | ||||
|                 min_data = data; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         check_min_elem(q.find_min(), min_prio, min_data); | ||||
| 
 | ||||
|         // Split the queue.
 | ||||
|         int threshold = index_distribution(rng); | ||||
|         Queue q2 = q.split(threshold); | ||||
| 
 | ||||
|         // Keep one queue and discard the other.
 | ||||
|         if (rng() % 2 == 0) { | ||||
|             q.clear(); | ||||
|             q = std::move(q2); | ||||
|             elems.erase(elems.begin(), elems.lower_bound(threshold)); | ||||
|         } else { | ||||
|             q2.clear(); | ||||
|             elems.erase(elems.lower_bound(threshold), elems.end()); | ||||
|         } | ||||
| 
 | ||||
|         // Check min element.
 | ||||
|         min_prio = INT_MAX; | ||||
|         min_data = 0; | ||||
|         for (const auto& v : elems) { | ||||
|             int prio = std::get<1>(v.second); | ||||
|             int data = std::get<2>(v.second); | ||||
|             if (prio < min_prio) { | ||||
|                 min_prio = prio; | ||||
|                 min_data = data; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (min_prio < INT_MAX) { | ||||
|             check_min_elem(q.find_min(), min_prio, min_data); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     q.clear(); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     q.insert(&n1, 5, "a"); | ||||
| 
 | ||||
|     BOOST_TEST(q.empty() == false); | ||||
|     check_min_elem(q.find_min(), 5, std::string("a")); | ||||
| 
 | ||||
|     q.update(&n1, 3, "a"); | ||||
|     check_min_elem(q.find_min(), 3, std::string("a")); | ||||
| 
 | ||||
|     q.remove(&n1); | ||||
|     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'); | ||||
|     check_min_elem(q.find_min(), 9, 'a'); | ||||
| 
 | ||||
|     q.insert(&nodes[1], 4, 'b'); | ||||
|     check_min_elem(q.find_min(), 4, 'b'); | ||||
| 
 | ||||
|     q.insert(&nodes[2], 7, 'c'); | ||||
|     check_min_elem(q.find_min(), 4, 'b'); | ||||
| 
 | ||||
|     q.insert(&nodes[3], 5, 'd'); | ||||
|     check_min_elem(q.find_min(), 4, 'b'); | ||||
| 
 | ||||
|     q.insert(&nodes[4], 8, 'e'); | ||||
|     check_min_elem(q.find_min(), 4, 'b'); | ||||
| 
 | ||||
|     q.insert(&nodes[5], 6, 'f'); | ||||
|     check_min_elem(q.find_min(), 4, 'b'); | ||||
| 
 | ||||
|     q.insert(&nodes[6], 4, 'g'); | ||||
|     q.insert(&nodes[7], 5, 'h'); | ||||
|     q.insert(&nodes[8], 2, 'i'); | ||||
|     check_min_elem(q.find_min(), 2, 'i'); | ||||
| 
 | ||||
|     q.insert(&nodes[9], 6, 'j'); | ||||
|     check_min_elem(q.find_min(), 2, 'i'); | ||||
| 
 | ||||
|     q.update(&nodes[2], 1, 'c'); | ||||
|     check_min_elem(q.find_min(), 1, 'c'); | ||||
| 
 | ||||
|     q.update(&nodes[4], 3, 'e'); | ||||
|     check_min_elem(q.find_min(), 1, 'c'); | ||||
| 
 | ||||
|     q.remove(&nodes[2]); | ||||
|     check_min_elem(q.find_min(), 2, 'i'); | ||||
| 
 | ||||
|     q.remove(&nodes[8]); | ||||
|     check_min_elem(q.find_min(), 3, 'e'); | ||||
| 
 | ||||
|     q.remove(&nodes[4]); | ||||
|     q.remove(&nodes[1]); | ||||
|     check_min_elem(q.find_min(), 4, 'g'); | ||||
| 
 | ||||
|     q.remove(&nodes[3]); | ||||
|     q.remove(&nodes[9]); | ||||
|     check_min_elem(q.find_min(), 4, 'g'); | ||||
| 
 | ||||
|     q.remove(&nodes[6]); | ||||
|     check_min_elem(q.find_min(), 5, 'h'); | ||||
| 
 | ||||
|     BOOST_TEST(q.empty() == false); | ||||
|     q.clear(); | ||||
|     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, min_data; | ||||
|         std::tie(min_prio, min_data) = q.find_min(); | ||||
|         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]); | ||||
|         int data = std::get<2>(elems[p]); | ||||
|         prio = std::uniform_int_distribution<>(0, prio)(rng); | ||||
|         q.update(node, prio, data); | ||||
|         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() | ||||
		Loading…
	
		Reference in New Issue