853 lines
24 KiB
C++
853 lines
24 KiB
C++
/* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
// First include (the generated) my_config.h, to get correct platform defines.
|
|
#include <gtest/gtest.h>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include "my_config.h"
|
|
|
|
#include "priority_queue.h"
|
|
|
|
namespace priority_queue_unittest {
|
|
|
|
// random integer generator -- start
|
|
template <typename IntegerType>
|
|
class random_integer_generator {
|
|
public:
|
|
random_integer_generator(unsigned int seed = 0) { srand(seed); }
|
|
|
|
IntegerType operator()(IntegerType const &low, IntegerType const &high) {
|
|
IntegerType i = rand() % (high - low + 1);
|
|
i += low;
|
|
return i;
|
|
}
|
|
};
|
|
// random integer generator -- end
|
|
|
|
//--------------------------------------------------------
|
|
|
|
// handle support -- start
|
|
template <typename T>
|
|
struct handle {
|
|
T *ptr;
|
|
|
|
handle() : ptr(NULL) {}
|
|
handle(T &t) : ptr(&t) {}
|
|
};
|
|
|
|
template <typename T>
|
|
inline std::ostream &operator<<(std::ostream &os, handle<T> const &h) {
|
|
os << *h.ptr;
|
|
return os;
|
|
}
|
|
|
|
template <typename Handle, typename Value, typename Less = std::less<Value>>
|
|
struct handle_less {
|
|
inline bool operator()(Handle const &p1, Handle const &p2) const {
|
|
return Less()(*p1.ptr, *p2.ptr);
|
|
}
|
|
};
|
|
// handle support -- end
|
|
|
|
//--------------------------------------------------------
|
|
|
|
// dummy stream that "eats" all input
|
|
struct null_stream : public std::ostream {
|
|
// Visual Studio needs a default constructor.
|
|
null_stream() : std::ostream(NULL) {}
|
|
};
|
|
|
|
template <typename T>
|
|
inline null_stream &operator<<(null_stream &ns, T const &) {
|
|
return ns;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
|
|
// i/o support -- start
|
|
template <typename InputIterator>
|
|
inline void print_range(std::ostream &os, InputIterator first,
|
|
InputIterator last) {
|
|
for (InputIterator it = first; it != last; ++it) {
|
|
os << " " << *it;
|
|
}
|
|
os << std::endl;
|
|
}
|
|
|
|
template <typename InputIterator>
|
|
inline void print_range(std::ostream &os, std::string const &header,
|
|
InputIterator first, InputIterator last) {
|
|
os << header;
|
|
print_range(os, first, last);
|
|
}
|
|
// i/o support -- end
|
|
|
|
//--------------------------------------------------------
|
|
|
|
// Some template functions below do not play well with gtest internals
|
|
static void assert_true_helper(bool val) { ASSERT_TRUE(val); }
|
|
|
|
// k min elements algorithm -- start
|
|
template <bool UseSorting>
|
|
struct min_k_elements {
|
|
template <typename InputIterator, typename OutputIterator>
|
|
static inline OutputIterator apply(InputIterator first, InputIterator last,
|
|
size_t k, OutputIterator oit) {
|
|
SCOPED_TRACE("");
|
|
assert_true_helper(static_cast<size_t>(std::distance(first, last)) > k);
|
|
|
|
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
|
|
|
std::vector<value_type> values;
|
|
|
|
// copy locally
|
|
std::copy(first, last, std::back_inserter(values));
|
|
|
|
// sort
|
|
std::sort(values.begin(), values.end());
|
|
|
|
// output k smallest values
|
|
std::copy(values.begin(), values.begin() + k, oit);
|
|
return oit;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct min_k_elements<false> {
|
|
template <typename RandomAccessIterator, typename OutputIterator>
|
|
static inline OutputIterator apply(RandomAccessIterator first,
|
|
RandomAccessIterator last, size_t k,
|
|
OutputIterator oit) {
|
|
SCOPED_TRACE("");
|
|
assert_true_helper(static_cast<size_t>(std::distance(first, last)) > k);
|
|
|
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type
|
|
value_type;
|
|
|
|
typedef handle<value_type> handle_type;
|
|
|
|
typedef handle_less<handle_type, value_type, std::less<value_type>>
|
|
handle_less_type;
|
|
|
|
typedef Priority_queue<handle_type, std::vector<handle_type>,
|
|
handle_less_type>
|
|
queue_type;
|
|
|
|
typedef typename queue_type::const_iterator queue_iterator;
|
|
|
|
// copy k + 1 values locally
|
|
std::vector<value_type> values;
|
|
values.reserve(k + 1);
|
|
|
|
std::copy(first, first + k + 1, std::back_inserter(values));
|
|
|
|
// create a queue of k + 1 handles to the local values
|
|
queue_type queue;
|
|
assert_true_helper(!queue.reserve(k + 1));
|
|
|
|
for (size_t i = 0; i < k + 1; ++i) {
|
|
EXPECT_FALSE(queue.push(handle_type(values[i])));
|
|
}
|
|
SCOPED_TRACE("");
|
|
assert_true_helper(queue.is_valid());
|
|
|
|
// for the remaining values update the queue's root node
|
|
// and rebuild the heap
|
|
for (RandomAccessIterator it = first + k + 1; it != last; ++it) {
|
|
*queue[0].ptr = *it;
|
|
queue.update(0);
|
|
}
|
|
|
|
// output the k minimum values (all but the root)
|
|
for (queue_iterator it = ++queue.begin(); it != queue.end(); ++it) {
|
|
*oit++ = *it->ptr;
|
|
}
|
|
|
|
return oit;
|
|
}
|
|
};
|
|
// k min elements algorithm -- end
|
|
|
|
//--------------------------------------------------------
|
|
|
|
template <typename RandomAccessIterator>
|
|
inline void test_min_k_elements(RandomAccessIterator first,
|
|
RandomAccessIterator last, size_t k) {
|
|
std::vector<int> keys_copy(first, last);
|
|
std::sort(keys_copy.begin(), keys_copy.end());
|
|
|
|
#ifdef PRIORITY_QUEUE_TEST_DEBUG
|
|
std::ostream &os = std::cout;
|
|
#else
|
|
null_stream os;
|
|
#endif
|
|
|
|
print_range(os, "elements: ", first, last);
|
|
|
|
print_range(os, "sorted elements: ", keys_copy.begin(), keys_copy.end());
|
|
os << std::endl;
|
|
|
|
std::vector<int> min_elements_sort, min_elements_heap;
|
|
|
|
// using the heap
|
|
min_k_elements<false>::apply(first, last, k,
|
|
std::back_inserter(min_elements_heap));
|
|
|
|
os << "min " << k << " elements (heap): ";
|
|
print_range(os, min_elements_heap.begin(), min_elements_heap.end());
|
|
|
|
std::sort(min_elements_heap.begin(), min_elements_heap.end());
|
|
os << "min " << k << " elements (hp/s): ";
|
|
print_range(os, min_elements_heap.begin(), min_elements_heap.end());
|
|
os << std::endl;
|
|
|
|
// using sorting
|
|
min_k_elements<true>::apply(first, last, k,
|
|
std::back_inserter(min_elements_sort));
|
|
|
|
os << "min " << k << " elements (sort): ";
|
|
print_range(os, min_elements_sort.begin(), min_elements_sort.end());
|
|
os << std::endl;
|
|
|
|
ASSERT_TRUE(std::equal(min_elements_sort.begin(), min_elements_sort.end(),
|
|
min_elements_heap.begin()));
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
|
|
template <typename Queue>
|
|
inline void print_update_msg(std::ostream &os, std::string header,
|
|
Queue const &q, typename Queue::size_type pos,
|
|
typename Queue::value_type const &old_value,
|
|
typename Queue::value_type const &new_value) {
|
|
os << header << " priority of element " << old_value << " at position " << pos
|
|
<< " to new value " << new_value << "; new_queue: " << q << std::endl;
|
|
}
|
|
|
|
template <typename RandomAccessIterator>
|
|
inline void test_heap(RandomAccessIterator first, RandomAccessIterator last) {
|
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type
|
|
value_type;
|
|
|
|
typedef Priority_queue<value_type> queue;
|
|
typedef typename queue::size_type size_type;
|
|
typedef typename queue::iterator iterator;
|
|
typedef typename queue::const_iterator const_iterator;
|
|
|
|
#ifdef PRIORITY_QUEUE_TEST_DEBUG
|
|
std::ostream &os = std::cout;
|
|
#else
|
|
null_stream os;
|
|
#endif
|
|
|
|
queue pq;
|
|
|
|
for (RandomAccessIterator it = first; it != last; ++it) {
|
|
EXPECT_FALSE(pq.push(*it));
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "queue: " << pq << std::endl;
|
|
}
|
|
|
|
// test constructor with iterators
|
|
{
|
|
queue pq2(first, last);
|
|
ASSERT_TRUE(pq2.is_valid());
|
|
os << "queue (constructor with iterators): " << pq2 << std::endl;
|
|
}
|
|
|
|
// test top
|
|
os << "top element: " << pq.top() << std::endl;
|
|
ASSERT_TRUE(pq.top() == pq[0]);
|
|
|
|
// test push
|
|
value_type new_value = *std::min_element(pq.begin(), pq.end()) - 1;
|
|
EXPECT_FALSE(pq.push(new_value));
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "pushed " << new_value << "; new queue: " << pq << std::endl;
|
|
|
|
// test pop
|
|
pq.pop();
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "popped top element; new queue: " << pq << std::endl;
|
|
|
|
// test decrease
|
|
// decrease priority of element at position 3
|
|
{
|
|
size_type pos = 3;
|
|
value_type old_priority = pq[pos];
|
|
value_type new_priority = pq[pos];
|
|
pq.decrease(pos, new_priority);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "decreasing (2)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos];
|
|
*(pq.begin() + pos) = new_priority;
|
|
pq.decrease(pos);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "decreasing (1)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos] - 10;
|
|
pq.decrease(pos, new_priority);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "decreasing (2)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos] - 20;
|
|
pq[pos] = new_priority;
|
|
pq.decrease(pos);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "decreasing (1)", pq, pos, old_priority, new_priority);
|
|
}
|
|
|
|
// test increase
|
|
// increase priority of element at position 4
|
|
{
|
|
size_type pos = 4;
|
|
value_type old_priority = pq[pos];
|
|
value_type new_priority = pq[pos];
|
|
pq.increase(pos, new_priority);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "increasing (2)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos];
|
|
*(pq.begin() + pos) = new_priority;
|
|
pq.increase(pos);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "increasing (1)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos] + 30;
|
|
pq.increase(pos, new_priority);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "increasing (2)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos] + 50;
|
|
pq[pos] = new_priority;
|
|
pq.increase(pos);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "increasing (1)", pq, pos, old_priority, new_priority);
|
|
}
|
|
|
|
// test update
|
|
// update priority of element at position 2
|
|
{
|
|
size_type pos = 2;
|
|
value_type old_priority = pq[pos];
|
|
value_type new_priority = pq[pos];
|
|
pq.update(pos, new_priority);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "updating (2)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos];
|
|
*(pq.begin() + pos) = new_priority;
|
|
pq.update(pos);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "updating (1)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos] - 20;
|
|
*(pq.begin() + pos) = new_priority;
|
|
pq.update(pos);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "updating (2)", pq, pos, old_priority, new_priority);
|
|
|
|
old_priority = pq[pos];
|
|
new_priority = pq[pos] + 100;
|
|
pq[pos] = new_priority;
|
|
pq.update(pos);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "updating (1)", pq, pos, old_priority, new_priority);
|
|
}
|
|
|
|
// test size
|
|
ASSERT_TRUE(pq.size() == static_cast<size_t>(std::distance(first, last)));
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "size: " << pq.size() << std::endl;
|
|
|
|
// test empty
|
|
ASSERT_TRUE(!pq.empty());
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "empty? " << std::boolalpha << pq.empty() << std::noboolalpha
|
|
<< std::endl;
|
|
|
|
// print all elements in queue using operator[]
|
|
os << "queue (using operator[]):";
|
|
for (size_type i = 0; i < pq.size(); ++i) {
|
|
os << " " << pq[i];
|
|
}
|
|
os << std::endl;
|
|
|
|
// print all elements in queue using const/non-const iterators
|
|
os << "queue (using const iterators):";
|
|
for (const_iterator it = pq.begin(); it != pq.end(); ++it) {
|
|
os << " " << *it;
|
|
}
|
|
os << std::endl;
|
|
|
|
os << "queue (using non-const iterators):";
|
|
for (iterator it = pq.begin(); it != pq.end(); ++it) {
|
|
os << " " << *it;
|
|
}
|
|
os << std::endl;
|
|
|
|
// testing swap
|
|
queue other;
|
|
|
|
ASSERT_TRUE(pq.is_valid() && !pq.empty());
|
|
ASSERT_TRUE(other.is_valid() && other.empty());
|
|
os << "before swap: " << pq << "; " << other << std::endl;
|
|
pq.swap(other);
|
|
ASSERT_TRUE(pq.is_valid() && pq.empty());
|
|
ASSERT_TRUE(other.is_valid() && !other.empty());
|
|
os << "after swap: " << pq << "; " << other << std::endl;
|
|
|
|
// clear the heap
|
|
pq.clear();
|
|
ASSERT_TRUE(pq.is_valid());
|
|
ASSERT_TRUE(pq.empty());
|
|
os << "clearing the queue" << std::endl;
|
|
|
|
// reserve 10 heap elements and push 10 elements in the heap
|
|
os << "testing reserve" << std::endl;
|
|
ASSERT_TRUE(pq.empty());
|
|
ASSERT_FALSE(pq.reserve(2 * pq.capacity()));
|
|
ASSERT_TRUE(pq.empty());
|
|
for (size_type i = 0; i < static_cast<size_t>(std::distance(first, last));
|
|
++i) {
|
|
ASSERT_TRUE(pq.size() == i);
|
|
EXPECT_FALSE(pq.push(*(last - 1 - i)));
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "queue: " << pq << std::endl;
|
|
}
|
|
|
|
// now use the non-const top() method to update the root element
|
|
os << "testing non-const top()" << std::endl;
|
|
{
|
|
value_type old_priority = pq.top();
|
|
value_type new_priority = pq.top() - 40;
|
|
pq.top() = pq.top() - 40;
|
|
pq.update(0);
|
|
ASSERT_TRUE(pq.is_valid());
|
|
print_update_msg(os, "updating (using top)", pq, 0, old_priority,
|
|
new_priority);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
|
|
template <typename RandomAccessIterator>
|
|
inline void test_heap_of_handles(RandomAccessIterator first,
|
|
RandomAccessIterator last) {
|
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type
|
|
value_type;
|
|
|
|
typedef handle<value_type> handle_type;
|
|
|
|
typedef handle_less<handle_type, value_type, std::greater<value_type>>
|
|
handle_greater_type;
|
|
|
|
typedef Priority_queue<handle_type, std::vector<handle_type>,
|
|
handle_less<handle_type, value_type>>
|
|
queue;
|
|
|
|
typedef typename queue::const_iterator const_iterator;
|
|
|
|
#ifdef PRIORITY_QUEUE_TEST_DEBUG
|
|
std::ostream &os = std::cout;
|
|
#else
|
|
null_stream os;
|
|
#endif
|
|
|
|
size_t const N = static_cast<size_t>(std::distance(first, last));
|
|
|
|
std::vector<handle_type> handles;
|
|
for (size_t i = 0; i < N; ++i) {
|
|
handles.push_back(handle_type(*(first + i)));
|
|
}
|
|
for (size_t i = 0; i < N; ++i) {
|
|
os << handles[i] << std::endl;
|
|
}
|
|
|
|
queue pq;
|
|
for (size_t i = 0; i < N; ++i) {
|
|
EXPECT_FALSE(pq.push(handles[i]));
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "queue: " << pq << std::endl;
|
|
}
|
|
|
|
{
|
|
queue pq2(handles.begin(), handles.end());
|
|
ASSERT_TRUE(pq2.is_valid());
|
|
os << "queue: " << pq2 << std::endl;
|
|
}
|
|
|
|
const_iterator it_max =
|
|
std::max_element(pq.begin(), pq.end(), handle_greater_type());
|
|
*pq[0].ptr = *it_max->ptr + 1000;
|
|
os << "queue: " << pq << std::endl;
|
|
pq.update_top();
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "queue: " << pq << std::endl;
|
|
|
|
it_max = std::max_element(pq.begin(), pq.end(), handle_greater_type());
|
|
*pq[0].ptr = *it_max->ptr + 500;
|
|
os << "queue: " << pq << std::endl;
|
|
pq.update_top();
|
|
ASSERT_TRUE(pq.is_valid());
|
|
os << "queue: " << pq << std::endl;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
|
|
class PriorityQueueTest : public ::testing::Test {
|
|
protected:
|
|
virtual void SetUp() {
|
|
int xkeys[10] = {10, 4, 7, 8, 21, -5, 6, 10, 7, 9};
|
|
memcpy(keys, xkeys, sizeof(xkeys));
|
|
pq = Priority_queue<int>(xkeys, xkeys + 10);
|
|
keys2.assign(5, 50);
|
|
}
|
|
|
|
size_t parent(size_t i) { return Priority_queue<int>::parent(i); }
|
|
size_t left(size_t i) { return Priority_queue<int>::left(i); }
|
|
size_t right(size_t i) { return Priority_queue<int>::right(i); }
|
|
|
|
int keys[10];
|
|
Priority_queue<int> pq;
|
|
std::vector<unsigned> keys2;
|
|
};
|
|
|
|
TEST_F(PriorityQueueTest, ParentLeftRight) {
|
|
EXPECT_EQ(0U, parent(1));
|
|
EXPECT_EQ(0U, parent(2));
|
|
EXPECT_EQ(1U, left(0));
|
|
EXPECT_EQ(2U, right(0));
|
|
for (size_t ix = 0; ix < 42; ++ix) {
|
|
EXPECT_EQ(ix, parent(left(ix)));
|
|
EXPECT_EQ(ix, parent(right(ix)));
|
|
EXPECT_EQ(1 + left(ix), right(ix));
|
|
}
|
|
}
|
|
|
|
struct My_less {
|
|
bool operator()(int a, int b) const { return a < b; }
|
|
};
|
|
|
|
struct My_greater {
|
|
bool operator()(int a, int b) const { return a > b; }
|
|
};
|
|
|
|
TEST_F(PriorityQueueTest, MaxVsMinHeap) {
|
|
Priority_queue<int, std::vector<int>, My_less> pql;
|
|
Priority_queue<int, std::vector<int>, My_greater> pqg;
|
|
for (int ix = 0; ix < 10; ++ix) {
|
|
EXPECT_FALSE(pql.push(ix));
|
|
EXPECT_FALSE(pqg.push(ix));
|
|
EXPECT_EQ(ix, pql.top());
|
|
EXPECT_EQ(0, pqg.top());
|
|
EXPECT_TRUE(pql.is_valid());
|
|
EXPECT_TRUE(pqg.is_valid());
|
|
}
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pql;
|
|
EXPECT_STREQ("9 8 5 6 7 1 4 0 3 2 ", ss1.str().c_str());
|
|
ss2 << pqg;
|
|
EXPECT_STREQ("0 1 2 3 4 5 6 7 8 9 ", ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, DifferentCtors) {
|
|
Priority_queue<int, std::vector<int>, My_less> pql;
|
|
std::vector<int> intvec;
|
|
for (int ix = 0; ix < 10; ++ix) {
|
|
Priority_queue<int> pq_ix(intvec.begin(), intvec.end());
|
|
EXPECT_TRUE(pq_ix.is_valid());
|
|
EXPECT_FALSE(pql.push(ix));
|
|
EXPECT_EQ(ix, pql.top());
|
|
intvec.push_back(ix);
|
|
}
|
|
Priority_queue<int, std::vector<int>, My_less> pql_range(intvec.begin(),
|
|
intvec.end());
|
|
|
|
EXPECT_EQ(10U, pql.size());
|
|
EXPECT_FALSE(pql.empty());
|
|
EXPECT_TRUE(pql.is_valid());
|
|
EXPECT_EQ(10U, pql_range.size());
|
|
EXPECT_FALSE(pql_range.empty());
|
|
EXPECT_TRUE(pql_range.is_valid());
|
|
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pql;
|
|
ss2 << pql_range;
|
|
// Different heaps, both are valid:
|
|
EXPECT_STREQ("9 8 5 6 7 1 4 0 3 2 ", ss1.str().c_str());
|
|
EXPECT_STREQ("9 8 6 7 4 5 2 0 3 1 ", ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Swap) {
|
|
std::random_shuffle(keys, keys + 10);
|
|
Priority_queue<int> pq(keys, keys + 10);
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
Priority_queue<int> pq_swap;
|
|
pq_swap.swap(pq);
|
|
ss2 << pq_swap;
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
EXPECT_EQ(0U, pq.size());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, DecreaseNoop) {
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
for (size_t ix = 0; ix < 10; ++ix) {
|
|
pq.decrease(ix);
|
|
int val = pq[ix];
|
|
pq.decrease(ix, val);
|
|
*(pq.begin() + ix) = val;
|
|
pq.decrease(ix);
|
|
EXPECT_TRUE(pq.is_valid());
|
|
}
|
|
ss2 << pq;
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, IncreaseNoop) {
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
for (size_t ix = 0; ix < 10; ++ix) {
|
|
pq.increase(ix);
|
|
int val = pq[ix];
|
|
pq.increase(ix, val);
|
|
*(pq.begin() + ix) = val;
|
|
pq.increase(ix);
|
|
EXPECT_TRUE(pq.is_valid());
|
|
}
|
|
ss2 << pq;
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, UpdateNoop) {
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
for (size_t ix = 0; ix < 10; ++ix) {
|
|
pq.update(ix);
|
|
int val = pq[ix];
|
|
pq.update(ix, val);
|
|
*(pq.begin() + ix) = val;
|
|
pq.update(ix);
|
|
EXPECT_TRUE(pq.is_valid());
|
|
}
|
|
ss2 << pq;
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Decrease3) {
|
|
Priority_queue<int> pqcopy = pq;
|
|
const int old_priority = pq[3];
|
|
const int new_priority = old_priority - rand() / 2;
|
|
|
|
pq.decrease(3, new_priority);
|
|
pqcopy[3] = new_priority;
|
|
pqcopy.decrease(3);
|
|
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
ss2 << pqcopy;
|
|
EXPECT_TRUE(pq.is_valid());
|
|
EXPECT_TRUE(pqcopy.is_valid());
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Increase4) {
|
|
Priority_queue<int> pqcopy = pq;
|
|
const int old_priority = pq[4];
|
|
const int new_priority = old_priority + rand() / 2;
|
|
|
|
pq.increase(4, new_priority);
|
|
pqcopy[4] = new_priority;
|
|
pqcopy.increase(4);
|
|
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
ss2 << pqcopy;
|
|
EXPECT_TRUE(pq.is_valid());
|
|
EXPECT_TRUE(pqcopy.is_valid());
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Update2) {
|
|
Priority_queue<int> pqcopy = pq;
|
|
|
|
for (int i = -10; i <= 10; ++i) {
|
|
const int old_priority = pq[2];
|
|
const int new_priority = old_priority + i;
|
|
|
|
pq.update(2, new_priority);
|
|
pqcopy[2] = new_priority;
|
|
pqcopy.update(2);
|
|
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
ss2 << pqcopy;
|
|
EXPECT_TRUE(pq.is_valid()) << "i:" << i << " pq:" << pq;
|
|
EXPECT_TRUE(pqcopy.is_valid());
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, UpdateTop) {
|
|
Priority_queue<int> pqcopy = pq;
|
|
const int old_priority = pq.top();
|
|
const int new_priority = old_priority + 10;
|
|
|
|
pq.top() = new_priority;
|
|
pq.update_top();
|
|
pqcopy.update(0, new_priority);
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pq;
|
|
ss2 << pqcopy;
|
|
EXPECT_TRUE(pq.is_valid());
|
|
EXPECT_TRUE(pqcopy.is_valid());
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, PopAndRemove) {
|
|
Priority_queue<int> pqcopy = pq;
|
|
|
|
EXPECT_TRUE(pqcopy.size() == 10U);
|
|
pqcopy.pop();
|
|
EXPECT_TRUE(pqcopy.is_valid());
|
|
EXPECT_TRUE(pqcopy.size() == 9U);
|
|
pqcopy.remove(3);
|
|
EXPECT_TRUE(pqcopy.is_valid());
|
|
EXPECT_TRUE(pqcopy.size() == 8U);
|
|
pqcopy.remove(pqcopy.size() - 1);
|
|
EXPECT_TRUE(pqcopy.is_valid());
|
|
EXPECT_TRUE(pqcopy.size() == 7U);
|
|
|
|
Priority_queue<int> singleton;
|
|
EXPECT_FALSE(singleton.push(10));
|
|
singleton.pop();
|
|
EXPECT_TRUE(singleton.empty());
|
|
EXPECT_TRUE(singleton.is_valid());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Iterators) {
|
|
Priority_queue<int>::iterator it;
|
|
Priority_queue<int>::const_iterator cit;
|
|
std::stringstream ss1, ss2;
|
|
for (it = pq.begin(); it != pq.end(); ++it) ss1 << *it << " ";
|
|
for (cit = pq.begin(); cit != pq.end(); ++cit) ss2 << *cit << " ";
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Clear) {
|
|
EXPECT_EQ(10U, pq.size());
|
|
EXPECT_TRUE(pq.is_valid());
|
|
pq.clear();
|
|
EXPECT_EQ(0U, pq.size());
|
|
EXPECT_TRUE(pq.is_valid());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Reserve) {
|
|
EXPECT_EQ(10U, pq.capacity());
|
|
EXPECT_EQ(10U, pq.size());
|
|
EXPECT_FALSE(pq.reserve(10));
|
|
EXPECT_EQ(10U, pq.capacity());
|
|
EXPECT_EQ(10U, pq.size());
|
|
EXPECT_FALSE(pq.reserve(5));
|
|
EXPECT_EQ(10U, pq.capacity());
|
|
EXPECT_EQ(10U, pq.size());
|
|
EXPECT_FALSE(pq.reserve(20));
|
|
EXPECT_EQ(20U, pq.capacity());
|
|
EXPECT_EQ(10U, pq.size());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, Sort) {
|
|
Priority_queue<int> pqcopy = pq;
|
|
std::vector<int> keyscopy(keys, keys + 10);
|
|
|
|
pqcopy.sort();
|
|
std::sort(keyscopy.begin(), keyscopy.end());
|
|
|
|
std::stringstream ss1, ss2;
|
|
ss1 << pqcopy;
|
|
for (size_t i = 0; i < keyscopy.size(); ++i) {
|
|
ss2 << keyscopy[i] << " ";
|
|
}
|
|
EXPECT_STREQ(ss1.str().c_str(), ss2.str().c_str());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, TestHeapKeys) {
|
|
SCOPED_TRACE("");
|
|
test_heap(keys, keys + 10);
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, TestHeapKeys2) {
|
|
SCOPED_TRACE("");
|
|
test_heap(keys2.begin(), keys2.end());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, TestHeapOfHandles) {
|
|
SCOPED_TRACE("");
|
|
test_heap_of_handles(keys, keys + 10);
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, TestHeapOfHandles2) {
|
|
SCOPED_TRACE("");
|
|
test_heap_of_handles(keys2.begin(), keys2.end());
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, TestMinKElements) {
|
|
SCOPED_TRACE("");
|
|
test_min_k_elements(keys, keys + 10, 7);
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, TestMinKElements2) {
|
|
SCOPED_TRACE("");
|
|
test_min_k_elements(keys2.begin(), keys2.end(), 4);
|
|
}
|
|
|
|
TEST_F(PriorityQueueTest, RandomIntegerGenerator) {
|
|
random_integer_generator<int> g;
|
|
std::vector<int> many_keys;
|
|
|
|
for (int i = 0; i < 200; ++i) {
|
|
int value = g(0, 300);
|
|
many_keys.push_back(value);
|
|
}
|
|
|
|
SCOPED_TRACE("");
|
|
test_min_k_elements(many_keys.begin(), many_keys.end(), 20);
|
|
}
|
|
|
|
} // namespace priority_queue_unittest
|