/* Copyright (c) 2014, 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 as published by the Free Software Foundation; version 2 of the License. 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 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 "my_config.h" #include #include #include #include #include #include #include #include "priority_queue.h" namespace priority_queue_unittest { // random integer generator -- start template 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 struct handle { T* ptr; handle() : ptr(NULL) {} handle(T& t) : ptr(&t) {} }; template inline std::ostream& operator<<(std::ostream& os, handle const& h) { os << *h.ptr; return os; } template < typename Handle, typename Value, typename Less = std::less > 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 inline null_stream& operator<<(null_stream& ns, T const&) { return ns; } //-------------------------------------------------------- // i/o support -- start template inline void print_range(std::ostream& os, InputIterator first, InputIterator last) { for (InputIterator it = first; it != last; ++it) { os << " " << *it; } os << std::endl; } template 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 struct min_k_elements { template static inline OutputIterator apply(InputIterator first, InputIterator last, size_t k, OutputIterator oit) { SCOPED_TRACE(""); assert_true_helper(static_cast(std::distance(first, last)) > k); typedef typename std::iterator_traits < InputIterator >::value_type value_type; std::vector 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 { template static inline OutputIterator apply(RandomAccessIterator first, RandomAccessIterator last, size_t k, OutputIterator oit) { SCOPED_TRACE(""); assert_true_helper(static_cast(std::distance(first, last)) > k); typedef typename std::iterator_traits < RandomAccessIterator >::value_type value_type; typedef handle handle_type; typedef handle_less < handle_type, value_type, std::less > handle_less_type; typedef Priority_queue < handle_type, std::vector, handle_less_type > queue_type; typedef typename queue_type::const_iterator queue_iterator; // copy k + 1 values locally std::vector 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 inline void test_min_k_elements(RandomAccessIterator first, RandomAccessIterator last, size_t k) { std::vector 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 min_elements_sort, min_elements_heap; // using the heap min_k_elements::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::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 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 inline void test_heap(RandomAccessIterator first, RandomAccessIterator last) { typedef typename std::iterator_traits < RandomAccessIterator >::value_type value_type; typedef Priority_queue 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(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(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 inline void test_heap_of_handles(RandomAccessIterator first, RandomAccessIterator last) { typedef typename std::iterator_traits < RandomAccessIterator >::value_type value_type; typedef handle handle_type; typedef handle_less < handle_type, value_type, std::greater > handle_greater_type; typedef Priority_queue < handle_type, std::vector, handle_less > 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(std::distance(first, last)); std::vector 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(xkeys, xkeys + 10); keys2.assign(5, 50); } size_t parent(size_t i) { return Priority_queue::parent(i); } size_t left(size_t i) { return Priority_queue::left(i); } size_t right(size_t i) { return Priority_queue::right(i); } int keys[10]; Priority_queue pq; std::vector 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, My_less> pql; Priority_queue, 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, My_less> pql; std::vector intvec; for (int ix= 0; ix < 10; ++ix) { Priority_queue 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, 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 pq(keys, keys + 10); std::stringstream ss1, ss2; ss1 << pq; Priority_queue 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 pqcopy= pq; const int old_priority= pq[3]; const int new_priority= old_priority - 10; 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 pqcopy= pq; const int old_priority= pq[4]; const int new_priority= old_priority + 10; 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 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 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 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 singleton; EXPECT_FALSE(singleton.push(10)); singleton.pop(); EXPECT_TRUE(singleton.empty()); EXPECT_TRUE(singleton.is_valid()); } TEST_F(PriorityQueueTest, Iterators) { Priority_queue::iterator it; Priority_queue::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 pqcopy= pq; std::vector 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 g; std::vector 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); } }