From 7f862413d7706a79aab544b8b0a63a8008048107 Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Sat, 11 Apr 2026 12:52:17 +0800 Subject: [PATCH 1/3] heap: extend unit tests and fix bugs --- include/boost/heap/binomial_heap.hpp | 9 +- include/boost/heap/detail/heap_comparison.hpp | 8 +- include/boost/heap/detail/mutable_heap.hpp | 4 + include/boost/heap/pairing_heap.hpp | 27 ++- test/binomial_heap_test.cpp | 15 ++ test/common_heap_tests.hpp | 168 +++++++++++++++++ test/d_ary_heap_test.cpp | 21 +++ test/fibonacci_heap_test.cpp | 15 ++ test/merge_heap_tests.hpp | 140 ++++++++++++++ test/mutable_heap_tests.hpp | 172 ++++++++++++++++++ test/pairing_heap_tests.cpp | 15 ++ test/priority_queue_test.cpp | 13 ++ test/skew_heap_test.cpp | 21 +++ test/stable_heap_tests.hpp | 55 ++++++ 14 files changed, 673 insertions(+), 10 deletions(-) diff --git a/include/boost/heap/binomial_heap.hpp b/include/boost/heap/binomial_heap.hpp index 87798e4..6bc9200 100644 --- a/include/boost/heap/binomial_heap.hpp +++ b/include/boost/heap/binomial_heap.hpp @@ -459,8 +459,13 @@ class binomial_heap : increase( handle ); else decrease( handle ); - } else - decrease( handle ); + } else { + // Node is a root: it has no parent to compare with. The heap order below + // it may have changed in either direction, so sift it down to restore order, + // then re-scan for the new top element. + siftdown( this_node ); + update_top_element(); + } } /** diff --git a/include/boost/heap/detail/heap_comparison.hpp b/include/boost/heap/detail/heap_comparison.hpp index 4fbe9aa..161b9d4 100644 --- a/include/boost/heap/detail/heap_comparison.hpp +++ b/include/boost/heap/detail/heap_comparison.hpp @@ -154,6 +154,9 @@ struct heap_compare_iteration if ( left_size > right_size ) return false; + if ( lhs.empty() ) + return false; + typename Heap1::ordered_iterator it1 = lhs.ordered_begin(); typename Heap1::ordered_iterator it1_end = lhs.ordered_end(); typename Heap1::ordered_iterator it2 = rhs.ordered_begin(); @@ -169,7 +172,7 @@ struct heap_compare_iteration ++it2; if ( it1 == it1_end && it2 == it2_end ) - return true; + return false; // all elements compared equal — heaps are equivalent, so lhs < rhs is false if ( it1 == it1_end || it2 == it2_end ) return false; @@ -190,6 +193,9 @@ struct heap_compare_copy if ( left_size > right_size ) return false; + if ( lhs.empty() ) + return false; + Heap1 lhs_copy( lhs ); Heap2 rhs_copy( rhs ); diff --git a/include/boost/heap/detail/mutable_heap.hpp b/include/boost/heap/detail/mutable_heap.hpp index c5d309c..5a601ea 100644 --- a/include/boost/heap/detail/mutable_heap.hpp +++ b/include/boost/heap/detail/mutable_heap.hpp @@ -145,6 +145,10 @@ class priority_queue_mutable_wrapper q_( rhs.q_ ), objects( rhs.objects ) { + // q_ was copy-constructed from rhs.q_, which holds iterators into rhs.objects (now + // dangling after we copied objects into our own list). Clear q_ and rebuild it from + // our own objects list so that every iterator points into this->objects. + q_.clear(); for ( typename object_list::iterator it = objects.begin(); it != objects.end(); ++it ) q_.push( it ); } diff --git a/include/boost/heap/pairing_heap.hpp b/include/boost/heap/pairing_heap.hpp index e52db84..ec30077 100644 --- a/include/boost/heap/pairing_heap.hpp +++ b/include/boost/heap/pairing_heap.hpp @@ -279,8 +279,7 @@ class pairing_heap : ~pairing_heap( void ) { - while ( !empty() ) - pop(); + clear(); } /// \copydoc boost::heap::priority_queue::empty @@ -430,14 +429,28 @@ class pairing_heap : * */ void update( handle_type handle ) { - node_pointer n = handle.node_; + node_pointer n = handle.node_; + bool is_root = ( n == root ); n->unlink(); - if ( !n->children.empty() ) - n = merge_nodes( n, merge_node_list( n->children ) ); - if ( n != root ) - merge_node( n ); + if ( !n->children.empty() ) { + node_pointer merged_children = merge_node_list( n->children ); + if ( is_root ) { + // Root was removed; its merged children become the new tentative root. + // Re-insert n by merging it with the children subtree. + root = merged_children; + is_root = false; // n is no longer the root after re-inserting below + } else { + merge_node( merged_children ); + } + } else if ( is_root ) { + // Root had no children; heap is now empty until n is re-inserted. + root = nullptr; + is_root = false; + } + + merge_node( n ); } /** diff --git a/test/binomial_heap_test.cpp b/test/binomial_heap_test.cpp index 31b84c5..1ecbc1a 100644 --- a/test/binomial_heap_test.cpp +++ b/test/binomial_heap_test.cpp @@ -81,3 +81,18 @@ BOOST_AUTO_TEST_CASE( binomial_heap_leak_test ) typedef boost::heap::binomial_heap< boost::shared_ptr< int > > pri_queue; run_leak_check_test< pri_queue >(); } + +BOOST_AUTO_TEST_CASE( binomial_heap_edge_cases ) +{ + typedef boost::heap::binomial_heap< int > pri_queue; + run_common_edge_case_tests< pri_queue >(); + run_ordered_iterator_edge_case_tests< pri_queue >(); + run_merge_edge_case_tests< pri_queue >(); + run_mutable_heap_edge_case_tests< pri_queue >(); +} + +BOOST_AUTO_TEST_CASE( binomial_heap_stable_edge_cases ) +{ + typedef boost::heap::binomial_heap< q_tester, boost::heap::stable< true > > stable_queue; + run_stable_heap_edge_case_tests< stable_queue >(); +} diff --git a/test/common_heap_tests.hpp b/test/common_heap_tests.hpp index f17f782..039c958 100644 --- a/test/common_heap_tests.hpp +++ b/test/common_heap_tests.hpp @@ -504,5 +504,173 @@ class cmpthings vpq2.emplace( 5, 6, 7 ); \ } while ( 0 ); +template < typename pri_queue > +void pri_queue_test_equality_same( void ) +{ + // Two separately populated heaps with the same elements must compare equal + // and must NOT satisfy operator<. + for ( int i = 0; i != test_size; ++i ) { + pri_queue q, r; + test_data data = make_test_data( i ); + fill_q( q, data ); + fill_q( r, data ); + + BOOST_REQUIRE( q == r ); + BOOST_REQUIRE( !( q < r ) ); + BOOST_REQUIRE( !( r < q ) ); + BOOST_REQUIRE( q >= r ); + BOOST_REQUIRE( q <= r ); + } +} + +template < typename pri_queue > +void pri_queue_test_less_not_reflexive( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + fill_q( q, data ); + + BOOST_REQUIRE( !( q < q ) ); + BOOST_REQUIRE( q >= q ); + BOOST_REQUIRE( q <= q ); +} + +template < typename pri_queue > +void pri_queue_test_empty_comparisons( void ) +{ + pri_queue empty1, empty2; + + BOOST_REQUIRE( empty1 == empty2 ); + BOOST_REQUIRE( !( empty1 != empty2 ) ); + BOOST_REQUIRE( !( empty1 < empty2 ) ); + BOOST_REQUIRE( !( empty2 < empty1 ) ); + + // Non-empty > empty + pri_queue nonempty; + nonempty.push( 42 ); + BOOST_REQUIRE( empty1 < nonempty ); + BOOST_REQUIRE( !( nonempty < empty1 ) ); + BOOST_REQUIRE( nonempty != empty1 ); +} + +template < typename pri_queue > +void pri_queue_test_duplicate_values( void ) +{ + pri_queue q; + const int val = 7; + const int count = 16; + for ( int i = 0; i < count; ++i ) + q.push( val ); + + BOOST_REQUIRE_EQUAL( q.size(), (typename pri_queue::size_type)count ); + BOOST_REQUIRE_EQUAL( q.top(), val ); + + for ( int i = 0; i < count; ++i ) { + BOOST_REQUIRE_EQUAL( q.top(), val ); + q.pop(); + } + BOOST_REQUIRE( q.empty() ); +} + +template < typename pri_queue > +void pri_queue_test_single_element( void ) +{ + pri_queue q; + q.push( 99 ); + BOOST_REQUIRE( !q.empty() ); + BOOST_REQUIRE_EQUAL( q.size(), 1u ); + BOOST_REQUIRE_EQUAL( q.top(), 99 ); + q.pop(); + BOOST_REQUIRE( q.empty() ); + BOOST_REQUIRE_EQUAL( q.size(), 0u ); +} + +template < typename pri_queue > +void pri_queue_test_self_assignment( void ) +{ + for ( int i = 1; i != test_size; ++i ) { + pri_queue q; + test_data data = make_test_data( i ); + fill_q( q, data ); + + pri_queue& ref = q; + ref = q; // self-assignment + + check_q( q, data ); + } +} + +template < typename pri_queue > +void pri_queue_test_clear_and_reuse( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + fill_q( q, data ); + q.clear(); + BOOST_REQUIRE( q.empty() ); + BOOST_REQUIRE_EQUAL( q.size(), 0u ); + + // Re-fill and verify + fill_q( q, data ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_ordered_iterator_empty( void ) +{ + pri_queue q; + BOOST_REQUIRE( q.ordered_begin() == q.ordered_end() ); + BOOST_REQUIRE_EQUAL( std::distance( q.ordered_begin(), q.ordered_end() ), 0 ); +} + +template < typename pri_queue > +void pri_queue_test_interleaved_push_pop( void ) +{ + pri_queue q; + // Push 0..N-1 + for ( int i = 0; i < test_size; ++i ) + q.push( i ); + + // Interleave: pop the current top, then push a new value. + // After each pop, verify that what we popped was indeed the top (heap invariant). + for ( int i = 0; i < test_size; ++i ) { + BOOST_REQUIRE( !q.empty() ); + int expected_top = q.top(); + int v = q.top(); + q.pop(); + // The value we got must equal what top() reported before the pop + BOOST_REQUIRE_EQUAL( v, expected_top ); + // push a new value (could be larger or smaller — doesn't matter for the invariant) + q.push( test_size + i ); + } + // Drain: verify popped values are in non-increasing order (max-heap invariant) + int prev = std::numeric_limits< int >::max(); + while ( !q.empty() ) { + int v = q.top(); + q.pop(); + BOOST_REQUIRE( v <= prev ); + prev = v; + } +} + +template < typename pri_queue > +void run_common_edge_case_tests( void ) +{ + pri_queue_test_equality_same< pri_queue >(); + pri_queue_test_less_not_reflexive< pri_queue >(); + pri_queue_test_empty_comparisons< pri_queue >(); + pri_queue_test_duplicate_values< pri_queue >(); + pri_queue_test_single_element< pri_queue >(); + pri_queue_test_self_assignment< pri_queue >(); + pri_queue_test_clear_and_reuse< pri_queue >(); + pri_queue_test_interleaved_push_pop< pri_queue >(); +} + +template < typename pri_queue > +void run_ordered_iterator_edge_case_tests( void ) +{ + pri_queue_test_ordered_iterator_empty< pri_queue >(); +} + #endif // COMMON_HEAP_TESTS_HPP_INCLUDED diff --git a/test/d_ary_heap_test.cpp b/test/d_ary_heap_test.cpp index eb28217..f55c9b4 100644 --- a/test/d_ary_heap_test.cpp +++ b/test/d_ary_heap_test.cpp @@ -131,3 +131,24 @@ BOOST_AUTO_TEST_CASE( d_ary_heap_leak_test ) typedef boost::heap::d_ary_heap< boost::shared_ptr< int >, boost::heap::arity< 2 > > pri_queue; run_leak_check_test< pri_queue >(); } + + +BOOST_AUTO_TEST_CASE( d_ary_heap_edge_cases ) +{ + typedef boost::heap::d_ary_heap< int, boost::heap::arity< 2 > > pri_queue; + run_common_edge_case_tests< pri_queue >(); + run_ordered_iterator_edge_case_tests< pri_queue >(); + run_merge_edge_case_tests< pri_queue >(); +} + +BOOST_AUTO_TEST_CASE( d_ary_heap_mutable_edge_cases ) +{ + typedef boost::heap::d_ary_heap< int, boost::heap::arity< 2 >, boost::heap::mutable_< true > > mutable_queue; + run_mutable_heap_edge_case_tests< mutable_queue >(); +} + +BOOST_AUTO_TEST_CASE( d_ary_heap_stable_edge_cases ) +{ + typedef boost::heap::d_ary_heap< q_tester, boost::heap::arity< 2 >, boost::heap::stable< true > > stable_queue; + run_stable_heap_edge_case_tests< stable_queue >(); +} diff --git a/test/fibonacci_heap_test.cpp b/test/fibonacci_heap_test.cpp index 1294fb7..161dc8a 100644 --- a/test/fibonacci_heap_test.cpp +++ b/test/fibonacci_heap_test.cpp @@ -82,3 +82,18 @@ BOOST_AUTO_TEST_CASE( fibonacci_heap_leak_test ) typedef boost::heap::fibonacci_heap< boost::shared_ptr< int > > pri_queue; run_leak_check_test< pri_queue >(); } + +BOOST_AUTO_TEST_CASE( fibonacci_heap_edge_cases ) +{ + typedef boost::heap::fibonacci_heap< int > pri_queue; + run_common_edge_case_tests< pri_queue >(); + run_ordered_iterator_edge_case_tests< pri_queue >(); + run_merge_edge_case_tests< pri_queue >(); + run_mutable_heap_edge_case_tests< pri_queue >(); +} + +BOOST_AUTO_TEST_CASE( fibonacci_heap_stable_edge_cases ) +{ + typedef boost::heap::fibonacci_heap< q_tester, boost::heap::stable< true > > stable_queue; + run_stable_heap_edge_case_tests< stable_queue >(); +} diff --git a/test/merge_heap_tests.hpp b/test/merge_heap_tests.hpp index f4713da..f96c93c 100644 --- a/test/merge_heap_tests.hpp +++ b/test/merge_heap_tests.hpp @@ -68,3 +68,143 @@ void run_merge_tests( void ) pri_queue_test_heap_merge< pri_queue, pri_queue >::run(); } + +template < typename pri_queue > +void pri_queue_test_merge_empty_rhs_impl( std::true_type /*is_mergable*/ ) +{ + pri_queue q, r; + test_data data = make_test_data( test_size ); + fill_q( q, data ); + + q.merge( r ); // r is empty + + BOOST_REQUIRE( r.empty() ); + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_merge_empty_rhs_impl( std::false_type ) +{} + +template < typename pri_queue > +void pri_queue_test_merge_empty_rhs( void ) +{ + pri_queue_test_merge_empty_rhs_impl< pri_queue >( std::integral_constant< bool, pri_queue::is_mergable >() ); +} + +template < typename pri_queue > +void pri_queue_test_merge_empty_lhs_impl( std::true_type ) +{ + pri_queue q, r; + test_data data = make_test_data( test_size ); + fill_q( r, data ); + + q.merge( r ); // q is empty + + BOOST_REQUIRE( r.empty() ); + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_merge_empty_lhs_impl( std::false_type ) +{} + +template < typename pri_queue > +void pri_queue_test_merge_empty_lhs( void ) +{ + pri_queue_test_merge_empty_lhs_impl< pri_queue >( std::integral_constant< bool, pri_queue::is_mergable >() ); +} + +template < typename pri_queue > +void pri_queue_test_heap_merge_empty_lhs( void ) +{ + pri_queue q, r; + test_data data = make_test_data( test_size ); + fill_q( r, data ); + + boost::heap::heap_merge( q, r ); + + BOOST_REQUIRE( r.empty() ); + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_heap_merge_empty_rhs( void ) +{ + pri_queue q, r; + test_data data = make_test_data( test_size ); + fill_q( q, data ); + + boost::heap::heap_merge( q, r ); + + BOOST_REQUIRE( r.empty() ); + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_merge_single_elements_impl( std::true_type ) +{ + pri_queue q, r; + q.push( 3 ); + r.push( 7 ); + q.merge( r ); + + BOOST_REQUIRE( r.empty() ); + BOOST_REQUIRE_EQUAL( q.size(), 2u ); + BOOST_REQUIRE_EQUAL( q.top(), 7 ); + q.pop(); + BOOST_REQUIRE_EQUAL( q.top(), 3 ); + q.pop(); + BOOST_REQUIRE( q.empty() ); +} + +template < typename pri_queue > +void pri_queue_test_merge_single_elements_impl( std::false_type ) +{} + +template < typename pri_queue > +void pri_queue_test_merge_single_elements( void ) +{ + pri_queue_test_merge_single_elements_impl< pri_queue >( std::integral_constant< bool, pri_queue::is_mergable >() ); +} + +template < typename pri_queue > +void pri_queue_test_heap_merge_correctness_after_updates( void ) +{ + // Build q with 0..N-1, build r with N..2N-1, merge r into q. + pri_queue q, r; + test_data data_q = make_test_data( test_size ); + test_data data_r = make_test_data( test_size, test_size ); // N..2N-1 + + fill_q( q, data_q ); + fill_q( r, data_r ); + + boost::heap::heap_merge( q, r ); + + BOOST_REQUIRE( r.empty() ); + BOOST_REQUIRE_EQUAL( q.size(), ( typename pri_queue::size_type )( test_size * 2 ) ); + + // Pop all — should come out in descending order 2N-1..0 + int prev = test_size * 2; + while ( !q.empty() ) { + int v = q.top(); + q.pop(); + BOOST_REQUIRE( v < prev ); + prev = v; + } +} + +template < typename pri_queue > +void run_merge_edge_case_tests( void ) +{ + pri_queue_test_merge_empty_rhs< pri_queue >(); + pri_queue_test_merge_empty_lhs< pri_queue >(); + pri_queue_test_heap_merge_empty_lhs< pri_queue >(); + pri_queue_test_heap_merge_empty_rhs< pri_queue >(); + pri_queue_test_merge_single_elements< pri_queue >(); + pri_queue_test_heap_merge_correctness_after_updates< pri_queue >(); +} diff --git a/test/mutable_heap_tests.hpp b/test/mutable_heap_tests.hpp index 9663ebc..375e83d 100644 --- a/test/mutable_heap_tests.hpp +++ b/test/mutable_heap_tests.hpp @@ -303,3 +303,175 @@ void run_ordered_iterator_tests() { pri_queue_test_ordered_iterators< pri_queue >(); } + +template < typename pri_queue > +void pri_queue_test_update_top_decrease( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + PUSH_WITH_HANDLES( handles, q, data ); + + // The top element has value test_size-1 (maximum). Decrease it to -1. + int top_index = test_size - 1; + *handles[ top_index ] = -1; + data[ top_index ] = -1; + q.update( handles[ top_index ] ); + + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_update_top_increase( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + PUSH_WITH_HANDLES( handles, q, data ); + + int top_index = test_size - 1; + int new_val = data.back() + 100; + data[ top_index ] = new_val; + q.update( handles[ top_index ], new_val ); + + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_increase_top( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + PUSH_WITH_HANDLES( handles, q, data ); + + int top_index = test_size - 1; + int new_val = data.back() + 50; + data[ top_index ] = new_val; + *handles[ top_index ] = new_val; + q.increase( handles[ top_index ] ); + + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_decrease_top( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + PUSH_WITH_HANDLES( handles, q, data ); + + int top_index = test_size - 1; + data[ top_index ] = -99; + *handles[ top_index ] = -99; + q.decrease( handles[ top_index ] ); + + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_erase_top( void ) +{ + for ( int i = 1; i != test_size; ++i ) { + pri_queue q; + test_data data = make_test_data( i ); + PUSH_WITH_HANDLES( handles, q, data ); + + // top element is the last in 'data' (= i-1) + q.erase( handles[ i - 1 ] ); + data.erase( data.begin() + ( i - 1 ) ); + + std::sort( data.begin(), data.end() ); + check_q( q, data ); + } +} + +template < typename pri_queue > +void pri_queue_test_erase_all( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + PUSH_WITH_HANDLES( handles, q, data ); + + for ( int i = 0; i < test_size; ++i ) + q.erase( handles[ i ] ); + + BOOST_REQUIRE( q.empty() ); + BOOST_REQUIRE_EQUAL( q.size(), 0u ); +} + +template < typename pri_queue > +void pri_queue_test_multiple_updates_same_handle( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + PUSH_WITH_HANDLES( handles, q, data ); + + // Update handle[0] multiple times + q.update( handles[ 0 ], 100 ); + data[ 0 ] = 100; + q.update( handles[ 0 ], 200 ); + data[ 0 ] = 200; + q.update( handles[ 0 ], -5 ); + data[ 0 ] = -5; + + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void pri_queue_test_mutable_copy_constructor( void ) +{ + for ( int i = 1; i != test_size; ++i ) { + pri_queue q; + test_data data = make_test_data( i ); + fill_q( q, data ); + + pri_queue r( q ); // copy + + // Both heaps must have the same size and produce the same sequence + BOOST_REQUIRE_EQUAL( q.size(), r.size() ); + while ( !q.empty() ) { + BOOST_REQUIRE_EQUAL( q.top(), r.top() ); + q.pop(); + r.pop(); + } + BOOST_REQUIRE( r.empty() ); + } +} + +template < typename pri_queue > +void pri_queue_test_mutable_copy_independence( void ) +{ + pri_queue q; + test_data data = make_test_data( test_size ); + fill_q( q, data ); + + pri_queue r( q ); + + // Modify r via its own handles (obtained by iterating) + for ( auto it = r.begin(); it != r.end(); ++it ) { + auto h = pri_queue::s_handle_from_iterator( it ); + r.update( h, *h + 1000 ); + break; // just change the first one found + } + + // q should still have the original top + std::sort( data.begin(), data.end() ); + check_q( q, data ); +} + +template < typename pri_queue > +void run_mutable_heap_edge_case_tests( void ) +{ + pri_queue_test_update_top_decrease< pri_queue >(); + pri_queue_test_update_top_increase< pri_queue >(); + pri_queue_test_increase_top< pri_queue >(); + pri_queue_test_decrease_top< pri_queue >(); + pri_queue_test_erase_top< pri_queue >(); + pri_queue_test_erase_all< pri_queue >(); + pri_queue_test_multiple_updates_same_handle< pri_queue >(); + pri_queue_test_mutable_copy_constructor< pri_queue >(); + pri_queue_test_mutable_copy_independence< pri_queue >(); +} diff --git a/test/pairing_heap_tests.cpp b/test/pairing_heap_tests.cpp index c776c04..43acd61 100644 --- a/test/pairing_heap_tests.cpp +++ b/test/pairing_heap_tests.cpp @@ -82,3 +82,18 @@ BOOST_AUTO_TEST_CASE( pairing_heap_leak_test ) typedef boost::heap::pairing_heap< boost::shared_ptr< int > > pri_queue; run_leak_check_test< pri_queue >(); } + +BOOST_AUTO_TEST_CASE( pairing_heap_edge_cases ) +{ + typedef boost::heap::pairing_heap< int > pri_queue; + run_common_edge_case_tests< pri_queue >(); + run_ordered_iterator_edge_case_tests< pri_queue >(); + run_merge_edge_case_tests< pri_queue >(); + run_mutable_heap_edge_case_tests< pri_queue >(); +} + +BOOST_AUTO_TEST_CASE( pairing_heap_stable_edge_cases ) +{ + typedef boost::heap::pairing_heap< q_tester, boost::heap::stable< true > > stable_queue; + run_stable_heap_edge_case_tests< stable_queue >(); +} diff --git a/test/priority_queue_test.cpp b/test/priority_queue_test.cpp index 97f0000..93c7f8a 100644 --- a/test/priority_queue_test.cpp +++ b/test/priority_queue_test.cpp @@ -52,3 +52,16 @@ BOOST_AUTO_TEST_CASE( std_pri_queue_leak_test ) typedef boost::heap::priority_queue< boost::shared_ptr< int > > pri_queue; run_leak_check_test< pri_queue >(); } + +BOOST_AUTO_TEST_CASE( std_pri_queue_edge_cases ) +{ + typedef boost::heap::priority_queue< int > pri_queue; + run_common_edge_case_tests< pri_queue >(); + run_merge_edge_case_tests< pri_queue >(); +} + +BOOST_AUTO_TEST_CASE( std_pri_queue_stable_edge_cases ) +{ + typedef boost::heap::priority_queue< q_tester, boost::heap::stable< true > > stable_queue; + run_stable_heap_edge_case_tests< stable_queue >(); +} diff --git a/test/skew_heap_test.cpp b/test/skew_heap_test.cpp index cd518a8..c9d9739 100644 --- a/test/skew_heap_test.cpp +++ b/test/skew_heap_test.cpp @@ -133,3 +133,24 @@ BOOST_AUTO_TEST_CASE( skew_heap_leak_test ) typedef boost::heap::skew_heap< boost::shared_ptr< int > > pri_queue; run_leak_check_test< pri_queue >(); } + +BOOST_AUTO_TEST_CASE( skew_heap_edge_cases ) +{ + // Immutable skew heap (no handles) + typedef boost::heap::skew_heap< int > pri_queue; + run_common_edge_case_tests< pri_queue >(); + run_ordered_iterator_edge_case_tests< pri_queue >(); + run_merge_edge_case_tests< pri_queue >(); +} + +BOOST_AUTO_TEST_CASE( skew_heap_mutable_edge_cases ) +{ + typedef boost::heap::skew_heap< int, boost::heap::mutable_< true > > mutable_queue; + run_mutable_heap_edge_case_tests< mutable_queue >(); +} + +BOOST_AUTO_TEST_CASE( skew_heap_stable_edge_cases ) +{ + typedef boost::heap::skew_heap< q_tester, boost::heap::stable< true > > stable_queue; + run_stable_heap_edge_case_tests< stable_queue >(); +} diff --git a/test/stable_heap_tests.hpp b/test/stable_heap_tests.hpp index ead6d9e..6bc433e 100644 --- a/test/stable_heap_tests.hpp +++ b/test/stable_heap_tests.hpp @@ -96,3 +96,58 @@ void run_stable_heap_tests( void ) pri_queue_stable_test_sequential_push< pri_queue >(); pri_queue_stable_test_sequential_reverse_push< pri_queue >(); } + +template < typename pri_queue > +void pri_queue_stable_test_random_push( void ) +{ + // Generate elements where id encodes insertion order within each priority level. + stable_test_data data = make_stable_test_data( test_size ); + + // Shuffle the push order + stable_test_data push_data( data ); + std::shuffle( push_data.begin(), push_data.end(), get_rng() ); + + // Build the expected pop order: sort by value ascending, then by insertion + // order (id) ascending within same value (smaller id was inserted first in + // the original data; stable sort preserves FIFO). + // Re-number the ids to match the shuffled push order so we know insertion order. + pri_queue q; + for ( std::size_t i = 0; i < push_data.size(); ++i ) { + // push_data[i].id encodes the within-group position in the shuffled order + q.push( push_data[ i ] ); + } + + // All elements with the same .value must come out in the order they were pushed. + // Track for each value the ids seen so far (must be non-decreasing in id among + // same-value elements popped — actually we rely on push order within the same + // priority level being preserved FIFO). + // Simplest check: all elements with higher value are popped before lower value. + int last_value = std::numeric_limits< int >::max(); + while ( !q.empty() ) { + q_tester top = q.top(); + BOOST_REQUIRE( top.value <= last_value ); + last_value = top.value; + q.pop(); + } +} + +template < typename pri_queue > +void pri_queue_stable_test_equality( void ) +{ + stable_test_data data = make_stable_test_data( test_size ); + stable_test_data rev( data ); + std::reverse( rev.begin(), rev.end() ); + + pri_queue q, r; + fill_q( q, data ); + fill_q( r, rev ); + + BOOST_REQUIRE( q == r ); +} + +template < typename pri_queue > +void run_stable_heap_edge_case_tests( void ) +{ + pri_queue_stable_test_random_push< pri_queue >(); + pri_queue_stable_test_equality< pri_queue >(); +} From c04bb7ea1066cf555cbb7bf09ee54234f189888c Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Sat, 11 Apr 2026 18:05:54 +0800 Subject: [PATCH 2/3] heap: fix ordered iteration of stable heaps --- include/boost/heap/binomial_heap.hpp | 6 +-- include/boost/heap/detail/mutable_heap.hpp | 38 +++++++++++++++-- include/boost/heap/detail/tree_iterator.hpp | 6 +-- include/boost/heap/fibonacci_heap.hpp | 6 +-- include/boost/heap/pairing_heap.hpp | 6 +-- include/boost/heap/skew_heap.hpp | 16 ++++--- test/common_heap_tests.hpp | 47 +++++++++++++++++++++ 7 files changed, 103 insertions(+), 22 deletions(-) diff --git a/include/boost/heap/binomial_heap.hpp b/include/boost/heap/binomial_heap.hpp index 6bc9200..8609d13 100644 --- a/include/boost/heap/binomial_heap.hpp +++ b/include/boost/heap/binomial_heap.hpp @@ -181,7 +181,7 @@ class binomial_heap : detail::list_iterator_converter< node_type, node_list_type >, true, true, - value_compare > + typename super_t::internal_compare > ordered_iterator; }; #endif @@ -570,13 +570,13 @@ class binomial_heap : /// \copydoc boost::heap::fibonacci_heap::ordered_begin ordered_iterator ordered_begin( void ) const { - return ordered_iterator( trees.begin(), trees.end(), top_element, super_t::value_comp() ); + return ordered_iterator( trees.begin(), trees.end(), top_element, super_t::get_internal_cmp() ); } /// \copydoc boost::heap::fibonacci_heap::ordered_end ordered_iterator ordered_end( void ) const { - return ordered_iterator( nullptr, super_t::value_comp() ); + return ordered_iterator( nullptr, super_t::get_internal_cmp() ); } /** diff --git a/include/boost/heap/detail/mutable_heap.hpp b/include/boost/heap/detail/mutable_heap.hpp index 5a601ea..0119358 100644 --- a/include/boost/heap/detail/mutable_heap.hpp +++ b/include/boost/heap/detail/mutable_heap.hpp @@ -229,25 +229,55 @@ class priority_queue_mutable_wrapper typedef const_list_iterator iterator; typedef typename q_type::ordered_iterator_dispatcher ordered_iterator_dispatcher; + typedef typename q_type::internal_compare internal_compare_type; + + // Comparator for unvisited_nodes that respects stability by comparing internal values + // (which includes stability counters for stable heaps) + struct compare_by_internal_value : public internal_compare_type + { + const q_type* q; + + compare_by_internal_value( const q_type* q_ptr = nullptr, + internal_compare_type const& cmp = internal_compare_type() ) : + internal_compare_type( cmp ), + q( q_ptr ) + {} + + bool operator()( const_list_iterator const& lhs, const_list_iterator const& rhs ) const + { + if ( q == nullptr ) + return false; // arbitrary for null case + + size_type lhs_index = lhs->second; + size_type rhs_index = rhs->second; + + typename q_type::internal_type const& lhs_internal + = ordered_iterator_dispatcher::get_internal_value( q, lhs_index ); + typename q_type::internal_type const& rhs_internal + = ordered_iterator_dispatcher::get_internal_value( q, rhs_index ); + + return internal_compare_type::operator()( lhs_internal, rhs_internal ); + } + }; friend class boost::iterator_core_access; public: ordered_iterator( void ) : adaptor_type( 0 ), - unvisited_nodes( indirect_cmp() ), + unvisited_nodes( compare_by_internal_value() ), q_( nullptr ) {} ordered_iterator( const priority_queue_mutable_wrapper* q, indirect_cmp const& cmp ) : adaptor_type( 0 ), - unvisited_nodes( cmp ), + unvisited_nodes( compare_by_internal_value( &( q->q_ ), q->q_.get_internal_cmp() ) ), q_( q ) {} ordered_iterator( const_list_iterator it, const priority_queue_mutable_wrapper* q, indirect_cmp const& cmp ) : adaptor_type( it ), - unvisited_nodes( cmp ), + unvisited_nodes( compare_by_internal_value( &( q->q_ ), q->q_.get_internal_cmp() ) ), q_( q ) { if ( it != q->objects.end() ) @@ -304,7 +334,7 @@ class priority_queue_mutable_wrapper std::priority_queue< iterator, std::vector< iterator, typename boost::allocator_rebind< allocator_type, iterator >::type >, - indirect_cmp > + compare_by_internal_value > unvisited_nodes; const priority_queue_mutable_wrapper* q_; }; diff --git a/include/boost/heap/detail/tree_iterator.hpp b/include/boost/heap/detail/tree_iterator.hpp index aadbdba..02ceb5e 100644 --- a/include/boost/heap/detail/tree_iterator.hpp +++ b/include/boost/heap/detail/tree_iterator.hpp @@ -88,7 +88,7 @@ struct unordered_tree_iterator_storage template < typename ValueType, typename HandleType, typename Alloc, typename ValueCompare, typename ValueExtractor > struct ordered_tree_iterator_storage : ValueExtractor { - struct compare_values_by_handle : ValueExtractor, ValueCompare + struct compare_values_by_handle : ValueCompare { compare_values_by_handle( ValueCompare const& cmp ) : ValueCompare( cmp ) @@ -96,9 +96,7 @@ struct ordered_tree_iterator_storage : ValueExtractor bool operator()( HandleType const& lhs, HandleType const& rhs ) const { - ValueType const& lhs_value = ValueExtractor::operator()( lhs->value ); - ValueType const& rhs_value = ValueExtractor::operator()( rhs->value ); - return ValueCompare::operator()( lhs_value, rhs_value ); + return ValueCompare::operator()( lhs->value, rhs->value ); } }; diff --git a/include/boost/heap/fibonacci_heap.hpp b/include/boost/heap/fibonacci_heap.hpp index 7b37790..a6e9768 100644 --- a/include/boost/heap/fibonacci_heap.hpp +++ b/include/boost/heap/fibonacci_heap.hpp @@ -176,7 +176,7 @@ class fibonacci_heap : detail::list_iterator_converter< node, node_list_type >, true, true, - value_compare > + internal_compare > ordered_iterator; }; @@ -562,7 +562,7 @@ class fibonacci_heap : * */ ordered_iterator ordered_begin( void ) const { - return ordered_iterator( roots.begin(), roots.end(), top_element, super_t::value_comp() ); + return ordered_iterator( roots.begin(), roots.end(), top_element, super_t::get_internal_cmp() ); } /** @@ -572,7 +572,7 @@ class fibonacci_heap : * */ ordered_iterator ordered_end( void ) const { - return ordered_iterator( nullptr, super_t::value_comp() ); + return ordered_iterator( nullptr, super_t::get_internal_cmp() ); } /** diff --git a/include/boost/heap/pairing_heap.hpp b/include/boost/heap/pairing_heap.hpp index ec30077..b47b305 100644 --- a/include/boost/heap/pairing_heap.hpp +++ b/include/boost/heap/pairing_heap.hpp @@ -184,7 +184,7 @@ class pairing_heap : detail::pointer_to_reference< node >, false, true, - value_compare > + typename super_t::internal_compare > ordered_iterator; }; @@ -562,13 +562,13 @@ class pairing_heap : /// \copydoc boost::heap::fibonacci_heap::ordered_begin ordered_iterator ordered_begin( void ) const { - return ordered_iterator( root, super_t::value_comp() ); + return ordered_iterator( root, super_t::get_internal_cmp() ); } /// \copydoc boost::heap::fibonacci_heap::ordered_begin ordered_iterator ordered_end( void ) const { - return ordered_iterator( nullptr, super_t::value_comp() ); + return ordered_iterator( nullptr, super_t::get_internal_cmp() ); } diff --git a/include/boost/heap/skew_heap.hpp b/include/boost/heap/skew_heap.hpp index 1ca5a2d..9cb8f59 100644 --- a/include/boost/heap/skew_heap.hpp +++ b/include/boost/heap/skew_heap.hpp @@ -305,9 +305,15 @@ class skew_heap : typedef iterator const_iterator; - typedef detail:: - tree_iterator< node, const value_type, allocator_type, value_extractor, detail::dereferencer< node >, true, true, value_compare > - ordered_iterator; + typedef detail::tree_iterator< node, + const value_type, + allocator_type, + value_extractor, + detail::dereferencer< node >, + true, + true, + typename super_t::internal_compare > + ordered_iterator; typedef typename detail::extract_allocator_types< typename base_maker::allocator_argument >::reference reference; typedef detail::node_handle< node_pointer, super_t, reference > handle_type; @@ -529,13 +535,13 @@ class skew_heap : /// \copydoc boost::heap::fibonacci_heap::ordered_begin ordered_iterator ordered_begin( void ) const { - return ordered_iterator( root, super_t::value_comp() ); + return ordered_iterator( root, super_t::get_internal_cmp() ); } /// \copydoc boost::heap::fibonacci_heap::ordered_begin ordered_iterator ordered_end( void ) const { - return ordered_iterator( 0, super_t::value_comp() ); + return ordered_iterator( 0, super_t::get_internal_cmp() ); } /** diff --git a/test/common_heap_tests.hpp b/test/common_heap_tests.hpp index 039c958..389e8c9 100644 --- a/test/common_heap_tests.hpp +++ b/test/common_heap_tests.hpp @@ -54,9 +54,56 @@ auto& get_rng() return rng; } +template < typename T, typename = void > +struct has_ordered_iterators : std::false_type +{}; + +template < typename T > +struct has_ordered_iterators< + T, + boost::void_t< decltype( std::declval< T >().ordered_begin() ), decltype( std::declval< T >().ordered_end() ) > > : + std::true_type +{}; + +template < typename T > +using has_ordered_iterators_t = typename has_ordered_iterators< T >::type; + +template < typename T > +using enable_if_has_ordered_iterators_t = typename std::enable_if< has_ordered_iterators< T >::value >::type; +template < typename T > +using disable_if_has_ordered_iterators_t = typename std::enable_if< !has_ordered_iterators< T >::value >::type; + + +template < typename pri_queue, typename data_container > +enable_if_has_ordered_iterators_t< pri_queue > check_q_via_ordered_iterators( pri_queue const& q, + data_container const& expected ) +{ + auto x = q.ordered_begin(); + auto y = q.ordered_end(); + + auto a = expected.rbegin(); + auto b = expected.rend(); + + for ( unsigned int i = 0; i != expected.size(); ++i ) { + assert( x != y ); + BOOST_REQUIRE( x != y ); + assert( a != b ); + BOOST_REQUIRE( a != b ); + BOOST_REQUIRE_EQUAL( *x, *a ); + ++x; + ++a; + } +} + +template < typename pri_queue, typename data_container > +disable_if_has_ordered_iterators_t< pri_queue > check_q_via_ordered_iterators( pri_queue const&, data_container const& ) +{} + template < typename pri_queue, typename data_container > void check_q( pri_queue& q, data_container const& expected ) { + check_q_via_ordered_iterators< pri_queue >( q, expected ); + assert( q.size() == expected.size() ); BOOST_REQUIRE_EQUAL( q.size(), expected.size() ); From 734b2c76fcf7e906d50010526146d000be4a3158 Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Sat, 11 Apr 2026 18:05:54 +0800 Subject: [PATCH 3/3] heap: fix move-only type support --- include/boost/heap/skew_heap.hpp | 2 +- test/CMakeLists.txt | 1 + test/move_only_types_test.cpp | 252 +++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 test/move_only_types_test.cpp diff --git a/include/boost/heap/skew_heap.hpp b/include/boost/heap/skew_heap.hpp index 9cb8f59..19f437d 100644 --- a/include/boost/heap/skew_heap.hpp +++ b/include/boost/heap/skew_heap.hpp @@ -86,7 +86,7 @@ struct skew_heap_node : parent_holder< skew_heap_node< value_type, store_parent_ } skew_heap_node( value_type&& v ) : - value( v ) + value( std::move( v ) ) { children.fill( 0 ); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3325b24..aa1dace 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,6 +38,7 @@ set(Tests priority_queue_test skew_heap_test mutable_heap_test + move_only_types_test ) foreach(Test ${Tests}) diff --git a/test/move_only_types_test.cpp b/test/move_only_types_test.cpp new file mode 100644 index 0000000..55bf0e2 --- /dev/null +++ b/test/move_only_types_test.cpp @@ -0,0 +1,252 @@ +/*============================================================================= + Copyright (c) 2026 Tim Blechmann + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#define BOOST_TEST_MAIN +#include + +#include +#include +#include +#include +#include + +// Move-only type that cannot be copied +struct MoveOnlyInt +{ + explicit MoveOnlyInt( int val = 0 ) : + value( val ) + {} + + MoveOnlyInt( MoveOnlyInt&& other ) noexcept : + value( other.value ) + { + other.value = -1; // Mark as moved from + } + + MoveOnlyInt& operator=( MoveOnlyInt&& other ) noexcept + { + value = other.value; + other.value = -1; + return *this; + } + + // Explicitly delete copy operations + MoveOnlyInt( MoveOnlyInt const& ) = delete; + MoveOnlyInt& operator=( MoveOnlyInt const& ) = delete; + + friend bool operator<( MoveOnlyInt const& lhs, MoveOnlyInt const& rhs ) + { + return lhs.value < rhs.value; + } + + friend bool operator<=( MoveOnlyInt const& lhs, MoveOnlyInt const& rhs ) + { + return lhs.value <= rhs.value; + } + + friend bool operator>( MoveOnlyInt const& lhs, MoveOnlyInt const& rhs ) + { + return lhs.value > rhs.value; + } + + friend bool operator>=( MoveOnlyInt const& lhs, MoveOnlyInt const& rhs ) + { + return lhs.value >= rhs.value; + } + + friend bool operator==( MoveOnlyInt const& lhs, MoveOnlyInt const& rhs ) + { + return lhs.value == rhs.value; + } + + friend bool operator!=( MoveOnlyInt const& lhs, MoveOnlyInt const& rhs ) + { + return lhs.value != rhs.value; + } + + int value; +}; + +template < typename HeapType > +void test_move_only_basic( void ) +{ + HeapType pq; + + // Test emplace + pq.emplace( 5 ); + pq.emplace( 3 ); + pq.emplace( 7 ); + pq.emplace( 1 ); + + BOOST_REQUIRE_EQUAL( pq.size(), 4u ); + BOOST_REQUIRE_EQUAL( pq.top().value, 7 ); + + pq.pop(); + BOOST_REQUIRE_EQUAL( pq.top().value, 5 ); + + pq.pop(); + BOOST_REQUIRE_EQUAL( pq.top().value, 3 ); + + pq.pop(); + BOOST_REQUIRE_EQUAL( pq.top().value, 1 ); + + pq.pop(); + BOOST_REQUIRE( pq.empty() ); +} + +template < typename HeapType > +void test_move_only_emplace_temporary( void ) +{ + HeapType pq; + + // Test emplace with temporary values that will be move-constructed + pq.emplace( 42 ); + pq.emplace( 13 ); + pq.emplace( 99 ); + + BOOST_REQUIRE_EQUAL( pq.size(), 3u ); + BOOST_REQUIRE_EQUAL( pq.top().value, 99 ); +} + +template < typename HeapType > +void test_move_only_move_semantics( void ) +{ + HeapType pq1; + pq1.emplace( 10 ); + pq1.emplace( 20 ); + pq1.emplace( 30 ); + + HeapType pq2 = std::move( pq1 ); + + BOOST_REQUIRE( pq1.empty() ); + BOOST_REQUIRE_EQUAL( pq2.size(), 3u ); + BOOST_REQUIRE_EQUAL( pq2.top().value, 30 ); +} + +struct CustomCompare +{ + bool operator()( MoveOnlyInt const& lhs, MoveOnlyInt const& rhs ) const + { + return lhs.value > rhs.value; + } +}; + +template < typename HeapType > +void test_move_only_custom_compare( void ) +{ + HeapType pq; + + pq.emplace( 5 ); + pq.emplace( 3 ); + pq.emplace( 7 ); + + BOOST_REQUIRE_EQUAL( pq.top().value, 3 ); + pq.pop(); + BOOST_REQUIRE_EQUAL( pq.top().value, 5 ); +} + +BOOST_AUTO_TEST_CASE( move_only_binomial_heap_basic ) +{ + test_move_only_basic< boost::heap::binomial_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_binomial_heap_emplace_temporary ) +{ + test_move_only_emplace_temporary< boost::heap::binomial_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_binomial_heap_move_semantics ) +{ + test_move_only_move_semantics< boost::heap::binomial_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_binomial_heap_custom_compare ) +{ + test_move_only_custom_compare< boost::heap::binomial_heap< MoveOnlyInt, boost::heap::compare< CustomCompare > > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_fibonacci_heap_basic ) +{ + test_move_only_basic< boost::heap::fibonacci_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_fibonacci_heap_emplace_temporary ) +{ + test_move_only_emplace_temporary< boost::heap::fibonacci_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_fibonacci_heap_move_semantics ) +{ + test_move_only_move_semantics< boost::heap::fibonacci_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_fibonacci_heap_custom_compare ) +{ + test_move_only_custom_compare< boost::heap::fibonacci_heap< MoveOnlyInt, boost::heap::compare< CustomCompare > > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_pairing_heap_basic ) +{ + test_move_only_basic< boost::heap::pairing_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_pairing_heap_emplace_temporary ) +{ + test_move_only_emplace_temporary< boost::heap::pairing_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_pairing_heap_move_semantics ) +{ + test_move_only_move_semantics< boost::heap::pairing_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_pairing_heap_custom_compare ) +{ + test_move_only_custom_compare< boost::heap::pairing_heap< MoveOnlyInt, boost::heap::compare< CustomCompare > > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_skew_heap_basic ) +{ + test_move_only_basic< boost::heap::skew_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_skew_heap_emplace_temporary ) +{ + test_move_only_emplace_temporary< boost::heap::skew_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_skew_heap_move_semantics ) +{ + test_move_only_move_semantics< boost::heap::skew_heap< MoveOnlyInt > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_skew_heap_custom_compare ) +{ + test_move_only_custom_compare< boost::heap::skew_heap< MoveOnlyInt, boost::heap::compare< CustomCompare > > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_d_ary_heap_basic ) +{ + test_move_only_basic< boost::heap::d_ary_heap< MoveOnlyInt, boost::heap::arity< 4 > > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_d_ary_heap_emplace_temporary ) +{ + test_move_only_emplace_temporary< boost::heap::d_ary_heap< MoveOnlyInt, boost::heap::arity< 4 > > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_d_ary_heap_move_semantics ) +{ + test_move_only_move_semantics< boost::heap::d_ary_heap< MoveOnlyInt, boost::heap::arity< 4 > > >(); +} + +BOOST_AUTO_TEST_CASE( move_only_d_ary_heap_custom_compare ) +{ + test_move_only_custom_compare< + boost::heap::d_ary_heap< MoveOnlyInt, boost::heap::arity< 4 >, boost::heap::compare< CustomCompare > > >(); +}