From 363e77194122506bae15f5fa774e3408de2c2b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Fri, 19 Jul 2019 15:50:52 +0800 Subject: [PATCH 01/13] min-max heap: basic functionalities --- include/boost/heap/d_ary_heap.hpp | 7 - include/boost/heap/detail/ilog2.hpp | 4 +- include/boost/heap/detail/mutable_heap.hpp | 7 + include/boost/heap/min_max_heap.hpp | 1161 ++++++++++++++++++++ test/min_max_heap_test.cpp | 110 ++ 5 files changed, 1280 insertions(+), 9 deletions(-) create mode 100644 include/boost/heap/min_max_heap.hpp create mode 100644 test/min_max_heap_test.cpp diff --git a/include/boost/heap/d_ary_heap.hpp b/include/boost/heap/d_ary_heap.hpp index 12ef99e..4aaf7ae 100644 --- a/include/boost/heap/d_ary_heap.hpp +++ b/include/boost/heap/d_ary_heap.hpp @@ -38,13 +38,6 @@ namespace boost { namespace heap { namespace detail { -struct nop_index_updater -{ - template - static void run(T &, std::size_t) - {} -}; - typedef parameter::parameters, boost::parameter::optional, boost::parameter::optional, diff --git a/include/boost/heap/detail/ilog2.hpp b/include/boost/heap/detail/ilog2.hpp index c6b65c6..791ca95 100644 --- a/include/boost/heap/detail/ilog2.hpp +++ b/include/boost/heap/detail/ilog2.hpp @@ -33,7 +33,7 @@ struct log2 { unsigned int operator()(unsigned int value) { - return sizeof(unsigned int)*8 - __builtin_clz(value - 1); + return sizeof(unsigned int)*8 - __builtin_clz(value) - 1; } }; @@ -42,7 +42,7 @@ struct log2 { unsigned long operator()(unsigned long value) { - return sizeof(unsigned long)*8 - __builtin_clzl(value - 1); + return sizeof(unsigned long)*8 - __builtin_clzl(value) - 1; } }; diff --git a/include/boost/heap/detail/mutable_heap.hpp b/include/boost/heap/detail/mutable_heap.hpp index f0d2d69..13d7322 100644 --- a/include/boost/heap/detail/mutable_heap.hpp +++ b/include/boost/heap/detail/mutable_heap.hpp @@ -23,6 +23,13 @@ namespace boost { namespace heap { namespace detail { +struct nop_index_updater +{ + template + static void run(T &, std::size_t) + {} +}; + /* wrapper for a mutable heap container adaptors * * this wrapper introduces an additional indirection. the heap is not constructed from objects, diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp new file mode 100644 index 0000000..a6215b4 --- /dev/null +++ b/include/boost/heap/min_max_heap.hpp @@ -0,0 +1,1161 @@ +// // boost heap: min-max heap +// +// The majority of this file comes from d_ary_heap.hpp +// Copyright (C) 2010 Tim Blechmann +// +// The parts related to the implementation of the min-max heap +// are however new. +// Copyright (C) 2019 Grégoire Scano +// +// Distributed under 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) + +/* Implementation of + @article{Atkinson:1986:MHG:6617.6621, + author = {Atkinson, M. D. and Sack, J.-R. and Santoro, N. and Strothotte, T.}, + title = {Min-max Heaps and Generalized Priority Queues}, + journal = {Commun. ACM}, + issue_date = {Oct. 1986}, + volume = {29}, + number = {10}, + month = {oct}, + year = {1986}, + issn = {0001-0782}, + pages = {996--1000}, + numpages = {5}, + url = {http://doi.acm.org/10.1145/6617.6621}, + doi = {http://dx.doi.org/10.1145/6617.6621}, + acmid = {6621}, + publisher = {ACM}, + address = {New York, NY, USA} + } +*/ + +#ifndef BOOST_HEAP_MIN_MAX_HEAP_HPP +#define BOOST_HEAP_MIN_MAX_HEAP_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#ifndef BOOST_DOXYGEN_INVOKED +#ifdef BOOST_HEAP_SANITYCHECKS +#define BOOST_HEAP_ASSERT BOOST_ASSERT +#else +#define BOOST_HEAP_ASSERT(expression) +#endif +#endif + +namespace boost { +namespace heap { +namespace detail { + +template +struct tree_depth; + +typedef parameter::parameters, + boost::parameter::optional, + boost::parameter::optional, + boost::parameter::optional, + boost::parameter::optional + > min_max_heap_signature; + +/* base class for min-max heap */ +template +class min_max_heap: + private make_heap_base::type +{ + typedef make_heap_base heap_base_maker; + + typedef typename heap_base_maker::type super_t; + typedef typename super_t::internal_type internal_type; + + typedef IndexUpdater index_updater; + +#ifdef BOOST_NO_CXX11_ALLOCATOR + typedef typename heap_base_maker::allocator_argument::template rebind::other internal_type_allocator; +#else + typedef typename std::allocator_traits::template rebind_alloc internal_type_allocator; +#endif + typedef std::vector container_type; + + container_type q_; + + static const unsigned int D = parameter::binding >::type::value; + + template + friend struct heap_merge_emulate; + +public: + typedef T value_type; + + struct implementation_defined: extract_allocator_types + {}; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + typedef typename implementation_defined::reference reference; + typedef typename implementation_defined::const_reference const_reference; + typedef typename implementation_defined::pointer pointer; + typedef typename implementation_defined::const_pointer const_pointer; + + typedef typename heap_base_maker::compare_argument value_compare; + typedef typename heap_base_maker::allocator_argument allocator_type; + + typedef void * handle_type; + + static const bool is_stable = extract_stable::value; + + /* xtors */ +public: + explicit min_max_heap(const value_compare & cmp = value_compare()) : + super_t(cmp) + {} + + min_max_heap(const min_max_heap & rhs) : + super_t(rhs), q_(rhs.q_) + {} + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + min_max_heap(min_max_heap && rhs) : + super_t(std::move(rhs)), q_(std::move(rhs.q_)) + {} + + min_max_heap & operator = (min_max_heap && rhs) + { + super_t::operator = (std::move(rhs)); + q_ = std::move(rhs.q_); + return *this; + } +#endif + + min_max_heap & operator = (min_max_heap const & rhs) + { + static_cast(*this) = static_cast(rhs); + q_ = rhs.q_; + return *this; + } + + void swap(min_max_heap & rhs) + { + super_t::swap(rhs); + std::swap(q_, rhs.q_); + } + /* xtors */ + + /* allocator */ + allocator_type get_allocator(void) const + { + return q_.get_allocator(); + } + /* allocator */ + + /* compare */ + value_compare const & value_comp(void) const + { + return super_t::value_comp(); + } + + template + bool compare(size_type i, size_type j) const + { + BOOST_ASSERT(i < this->size()); + BOOST_ASSERT(j < this->size()); + // use > or std::greater for min-max heap + // use < or std::less for a max-min heap + if (!Regular) + return super_t::operator () (q_[i], q_[j]); + else + return super_t::operator () (q_[j], q_[i]); + } + /* compare */ + + /* container */ + bool empty(void) const + { + return q_.empty(); + } + + size_type size(void) const + { + return q_.size(); + } + + size_type max_size(void) const + { + return q_.max_size(); + } + + void reserve(size_t size) + { + q_.reserve(size); + } + + void clear(void) + { + q_.clear(); + } + /* container */ + + /* indexes */ + void reset_index(size_type index, size_type new_index) + { + BOOST_HEAP_ASSERT(index < q_.size()); + index_updater::run(q_[index], new_index); + } + + bool is_on_compare_level(size_type index) const + { + tree_depth depth; + return !(depth(index) % 2); + } + + size_type root(void) const + { + return 0; + } + + std::pair get_child_nodes(size_type index) const + { + return std::make_pair(first_child(index), last_child(index)); + } + + size_type first_child(size_type index) const + { + return D * index + 1; + } + + size_type last_child(size_type index) const + { + return D * (index + 1); + } + + std::pair get_grandchild_nodes(size_type index) const + { + return std::make_pair(first_grandchild(index), last_grandchild(index)); + } + + size_type first_grandchild(size_type index) const + { + return first_child(first_child(index)); + } + + size_type last_grandchild(size_type index) const + { + return last_child(last_child(index)); + } + + size_type last(void) const + { + return q_.size() - 1; + } + + size_type npos(void) const + { + return -1; + } + + bool is_leaf(size_type index) const + { + return q_.size() <= first_child(index); + } + + bool has_parent(size_type index) const + { + return index != root(); + } + + size_type parent(size_type index) const + { + if (index == root() || index == npos()) + return npos(); + else + return (index - 1) / D; + } + + size_type grandparent(size_type index) const + { + return parent(parent(index)); + } + + template + bool best_between(size_type & best, size_type current, size_type theorical_last) const + { + bool found = false; + + for (const size_type last = std::min(theorical_last, this->last()); current <= last; ++current) + if (compare(current, best)) { + best = current; + found = true; + } + + return found; + } + + template + size_type best_child_or_grandchild(size_type index, bool & is_grandchild) const + { + const size_type first_child = this->first_child(index); + + if(last() < first_child) return npos(); + + const size_type last_child = this->last_child(index); + const size_type first_grandchild = this->first_child(first_child); + const size_type last_grandchild = this->last_child(last_child); + + size_type best_child = first_child; + + best_between(best_child, first_child + 1, last_child); + is_grandchild = best_between(best_child, first_grandchild, last_grandchild); + + return best_child; + } + /* indexes */ + + /* moves */ + void trickle_down(size_type index) + { + if (is_on_compare_level(index)) + trickle_down_impl(index); + else + trickle_down_impl(index); + } + + template + void trickle_down_impl(size_type i) + { + bool is_grandchild; + size_type m = best_child_or_grandchild(i, is_grandchild); + + if (m < npos()) { + if (is_grandchild) { + if (compare(m, i)) { + swap(i, m); + + size_type parent = this->parent(m); + + if (compare(parent, m)) + swap(m, parent); + + trickle_down_impl(m); + } + } + else + if (compare(m, i)) + swap(i, m); + } + } + + void bubble_up(size_type i) + { + if (is_on_compare_level(i)) + bubble_up_impl(i); + else + bubble_up_impl(i); + } + + template + void bubble_up_impl(size_type i) + { + size_type parent = this->parent(i); + + if (parent != npos()) { + if (compare(parent, i)) { + swap(i, parent); + bubble_up_impl_(parent); + } + else + bubble_up_impl_(i); + } + } + + template + void bubble_up_impl_(size_type i) + { + size_type grandparent = this->grandparent(i); + + if(grandparent != npos() + && compare(i, grandparent)) { + swap(i, grandparent); + bubble_up_impl_(grandparent); + } + } + /* moves */ + + /* operations */ + void swap(size_type i, size_type j) + { + BOOST_ASSERT(i < size()); + BOOST_ASSERT(j < size()); + + reset_index(i, j); + reset_index(j, i); + + std::swap(q_[i], q_[j]); + } + +protected: + size_type index_of_min(void) const + { + return root(); + } + + size_type index_of_max(void) const + { + size_type best = root(); + + best_between(best, 1, D); + + return best; + } + /* operations */ + + /* interface */ + void push(const value_type & v) + { + q_.push_back(super_t::make_node(v)); + + size_type index = last(); + reset_index(index, index); + + bubble_up(index); + } + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template + void emplace(Args&&... args) + { + q_.emplace_back(super_t::make_node(std::forward(args)...)); + reset_index(last(), last()); + bubble_up(last()); + } +#endif + + value_type const & min(void) const + { + BOOST_ASSERT(!empty()); + + return super_t::get_value(q_[index_of_min()]); + } + + value_type const & max(void) const + { + BOOST_ASSERT(!empty()); + + return super_t::get_value(q_[index_of_max()]); + } + + void pop_min(void) + { + erase(index_of_min()); + } + + void pop_max(void) + { + erase(index_of_max()); + } + +public: + void update(size_type index) + { + if(index == root()) + { + return trickle_down(index); + } + + size_type parent = parent(index); + + if(compare(parent, index)) + { + return decrease(index); + } + + size_type grandparent = grandparent(index); + + if(grandparent < last() + && compare(index, grandparent)) + { + return increase(index); + } + } + + void increase(size_type index) + { + if(index == root() + || !is_on_compare_level(index)) + { + trickle_down(index); + } + else + { + size_type parent = parent(index); + + if(compare(parent, index)) + { + trickle_down(index); + } + + bubble_up(index); + } + } + + void decrease(size_type index) + { + if(is_on_compare_level(index)) + { + bubble_up(index); + } + else + { + size_type parent = parent(index); + + if(compare(parent, index)) + { + bubble_up(index); + } + + trickle_down(index); + } + } + + void erase(size_type index) + { + BOOST_ASSERT(!empty()); + BOOST_ASSERT(index < size()); + + swap(index, last());//TODO last() + q_.pop_back(); + + if(!empty() && index != size()) + { + trickle_down(index); + } + } + /* interface */ + + /* iterators */ +public: + typedef detail::stable_heap_iterator iterator; + typedef iterator const_iterator; + +public: + iterator begin(void) + { + return iterator(q_.begin()); + } + + iterator end(void) + { + return iterator(q_.end()); + } + + const_iterator begin(void) const + { + return const_iterator(q_.begin()); + } + + const_iterator end(void) const + { + return const_iterator(q_.end()); + } + +public: + template + struct iterator_dispatcher + { + static size_type max_index(const min_max_heap * heap) + { + return heap->last(); + } + + bool is_leaf(const min_max_heap * heap, size_type index) const + { + return false; + } + + std::pair get_child_nodes(const min_max_heap * heap, size_type index) + { + return std::make_pair(1, 0); + } + + static internal_type const & get_internal_value(const min_max_heap * heap, size_type index) + { + return heap->q_[index]; + } + + static value_type const & get_value(internal_type const & arg) + { + return super_t::get_value(arg); + } + }; + +public: + struct ordered_iterator_dispatcher : iterator_dispatcher + { + + }; + +public: + typedef detail::ordered_adaptor_iterator + ordered_iterator; + +public: + ordered_iterator ordered_begin(void) const + { + return ordered_iterator(root(), this, super_t::get_internal_cmp()); + } + + ordered_iterator ordered_end(void) const + { + return ordered_iterator(q_.size(), this, super_t::get_internal_cmp()); + } + +public: + struct reverse_ordered_iterator_dispatcher : iterator_dispatcher + { + + }; + +public: + struct reverse_internal_compare : super_t::internal_compare + { + reverse_internal_compare(value_compare const & cmp = value_compare()) : + super_t::internal_compare(cmp) + {} + + bool operator () (typename super_t::internal_type const & lhs, typename super_t::internal_type const & rhs) const + { + return super_t::internal_compare(rhs, lhs); + } + }; + + typedef detail::ordered_adaptor_iterator + reverse_ordered_iterator; + +public: + reverse_ordered_iterator reverse_ordered_begin(void) const + { + return revese_ordered_iterator(root(), this, super_t::get_internal_cmp()); + } + + reverse_ordered_iterator reverse_ordered_end(void) const + { + return revese_ordered_iterator(q_.size(), this, super_t::get_internal_cmp()); + } + /* iterators */ +}; + +template +struct tree_depth +{ + IntType operator () (IntType index) const + { + IntType power = Base; + IntType count = 1; + IntType depth = 0; + + while (count <= index) { + count += power; + power *= Base; + ++depth; + } + + return depth; + } + + /* Alternatively, let f be a function mapping an index n to its + corresponding depth (or row) r in a D tree, f_D(n)=r and 1 < D + If n is on row r, then $\sum_{i=0}^{r} D^{i} <= n < \sum_{i=0}^{r+1} D^{i}$ + hence $D^{r+1} <= (D-1) * n + 1 < D^{r+2}$ then + $r + 1 <= \frac{log_2((D-1) * n + 1)}{log_2(D)} < r+2$ + since log_2 is strictly increasing and because an array index starts at 0 (r:=r-1) + $r = log_2((Base - 1) * index + 1) / log_2(Base)$ + which is slower than the above iterative method for indexes up to 10^10. + */ +}; + +template +struct tree_depth<2, IntType> +{ + IntType operator () (IntType index) const + { + return ::boost::heap::log2(index + 1); + } +}; + +template +struct select_minmax_heap +{ + static const bool is_mutable = extract_mutable::value; + + typedef typename mpl::if_c >, + min_max_heap + >::type type; +}; + +} /* namespace detail */ + +/** + * \class min_max_heap + * \brief min-max heap class + * + * This class implements an immutable priority queue. Internally, the min-max heap is representated + * as a dynamically sized array (std::vector), that directly stores the values. + * + * The template parameter T is the type to be managed by the container. + * The user can specify additional options and if no options are provided default options are used. + * + * The container supports the following options: + * - \c boost::heap::arity<>, defaults to \c arity<2> + * - \c boost::heap::compare<>, defaults to \c compare > + * - \c boost::heap::stable<>, defaults to \c stable + * - \c boost::heap::stability_counter_type<>, defaults to \c stability_counter_type + * - \c boost::heap::allocator<>, defaults to \c allocator > + * - \c boost::heap::mutable_<>, defaults to \c mutable_ + */ +#ifdef BOOST_DOXYGEN_INVOKED +template +#else +template +#endif +class min_max_heap: + public detail::select_minmax_heap::type>::type +{ +typedef typename detail::min_max_heap_signature::bind::type bound_args; + typedef typename detail::select_minmax_heap::type super_t; + + template + friend struct heap_merge_emulate; + +#ifndef BOOST_DOXYGEN_INVOKED + static const bool is_mutable = detail::extract_mutable::value; + +#define BOOST_HEAP_TYPEDEF_FROM_SUPER_T(NAME) \ + typedef typename super_t::NAME NAME; + + struct implementation_defined + { + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(size_type) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(difference_type) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(value_compare) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(allocator_type) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(reference) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(const_reference) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(pointer) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(const_pointer) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(iterator) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(const_iterator) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(ordered_iterator) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(handle_type) + }; +#undef BOOST_HEAP_TYPEDEF_FROM_SUPER_T + +#endif +public: + static const bool constant_time_size = true; + static const bool has_ordered_iterators = true; + static const bool is_mergable = false; + static const bool has_reserve = true; + static const bool is_stable = super_t::is_stable; + + typedef T value_type; + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + typedef typename implementation_defined::value_compare value_compare; + typedef typename implementation_defined::allocator_type allocator_type; + typedef typename implementation_defined::reference reference; + typedef typename implementation_defined::const_reference const_reference; + typedef typename implementation_defined::pointer pointer; + typedef typename implementation_defined::const_pointer const_pointer; + /// \copydoc boost::heap::priority_queue::iterator + typedef typename implementation_defined::iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::ordered_iterator ordered_iterator; + typedef typename implementation_defined::handle_type handle_type; + +public: + /// \copydoc boost::heap::priority_queue::priority_queue(value_compare const &) + explicit min_max_heap(value_compare const & cmp = value_compare()): + super_t(cmp) + {} + + /// \copydoc boost::heap::priority_queue::priority_queue(priority_queue const &) + min_max_heap(min_max_heap const & rhs): + super_t(rhs) + {} + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + /// \copydoc boost::heap::priority_queue::priority_queue(priority_queue &&) + min_max_heap(min_max_heap && rhs): + super_t(std::move(rhs)) + {} + + /// \copydoc boost::heap::priority_queue::operator=(priority_queue &&) + min_max_heap & operator=(min_max_heap && rhs) + { + super_t::operator=(std::move(rhs)); + return *this; + } +#endif + + /// \copydoc boost::heap::priority_queue::operator=(priority_queue const &) + min_max_heap & operator=(min_max_heap const & rhs) + { + super_t::operator=(rhs); + return *this; + } + +public: + /// \copydoc boost::heap::priority_queue::empty + bool empty(void) const + { + return super_t::empty(); + } + + /// \copydoc boost::heap::priority_queue::size + size_type size(void) const + { + return super_t::size(); + } + + /// \copydoc boost::heap::priority_queue::max_size + size_type max_size(void) const + { + return super_t::max_size(); + } + + /// \copydoc boost::heap::priority_queue::clear + void clear(void) + { + super_t::clear(); + } + +public: + /// \copydoc boost::heap::priority_queue::get_allocator + allocator_type get_allocator(void) const + { + return super_t::get_allocator(); + } + +public: + /// \copydoc boost::heap::priority_queue::top + value_type const & top(void) const + { + return super_t::min(); + } + + /** + * \b Effects: Returns a const_reference to the minimum element. + * + * \b Complexity: + * + * */ + value_type const & min(void) const + { + return super_t::min(); + } + + /** + * \b Effects: Returns a const_reference to the maximum element. + * + * \b Complexity: + * + * */ + value_type const & max(void) const + { + return super_t::max(); + } + + /// \copydoc boost::heap::priority_queue::push + typename boost::conditional::type push(value_type const & v) + { + return super_t::push(v); + } + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + /// \copydoc boost::heap::priority_queue::emplace + template + typename boost::conditional::type emplace(Args&&... args) + { + return super_t::emplace(std::forward(args)...); + } +#endif + + /// \copydoc boost::heap::priority_queue::operator<(HeapType const & rhs) const + template + bool operator<(HeapType const & rhs) const + { + return detail::heap_compare(*this, rhs); + } + + /// \copydoc boost::heap::priority_queue::operator>(HeapType const & rhs) const + template + bool operator>(HeapType const & rhs) const + { + return detail::heap_compare(rhs, *this); + } + + /// \copydoc boost::heap::priority_queue::operator>=(HeapType const & rhs) const + template + bool operator>=(HeapType const & rhs) const + { + return !operator<(rhs); + } + + /// \copydoc boost::heap::priority_queue::operator<=(HeapType const & rhs) const + template + bool operator<=(HeapType const & rhs) const + { + return !operator>(rhs); + } + + /// \copydoc boost::heap::priority_queue::operator==(HeapType const & rhs) const + template + bool operator==(HeapType const & rhs) const + { + return detail::heap_equality(*this, rhs); + } + + /// \copydoc boost::heap::priority_queue::operator!=(HeapType const & rhs) const + template + bool operator!=(HeapType const & rhs) const + { + return !(*this == rhs); + } + + /** + * \b Effects: Assigns \c v to the element handled by \c handle & updates the priority queue. + * + * \b Complexity: Logarithmic. + * + * \b Requirement: data structure must be configured as mutable + * */ + void update(handle_type handle, const_reference v) + { + BOOST_STATIC_ASSERT(is_mutable); + super_t::update(handle, v); + } + + /** + * \b Effects: Updates the heap after the element handled by \c handle has been changed. + * + * \b Complexity: Logarithmic. + * + * \b Note: If this is not called, after a handle has been updated, the behavior of the data structure is undefined! + * + * \b Requirement: data structure must be configured as mutable + * */ + void update(handle_type handle) + { + BOOST_STATIC_ASSERT(is_mutable); + super_t::update(handle); + } + + /** + * \b Effects: Assigns \c v to the element handled by \c handle & updates the priority queue. + * + * \b Complexity: Logarithmic. + * + * \b Note: The new value is expected to be greater than the current one + * + * \b Requirement: data structure must be configured as mutable + * */ + void increase(handle_type handle, const_reference v) + { + BOOST_STATIC_ASSERT(is_mutable); + super_t::increase(handle, v); + } + + /** + * \b Effects: Updates the heap after the element handled by \c handle has been changed. + * + * \b Complexity: Logarithmic. + * + * \b Note: The new value is expected to be greater than the current one. If this is not called, after a handle has been updated, the behavior of the data structure is undefined! + * + * \b Requirement: data structure must be configured as mutable + * */ + void increase(handle_type handle) + { + BOOST_STATIC_ASSERT(is_mutable); + super_t::increase(handle); + } + + /** + * \b Effects: Assigns \c v to the element handled by \c handle & updates the priority queue. + * + * \b Complexity: Logarithmic. + * + * \b Note: The new value is expected to be less than the current one + * + * \b Requirement: data structure must be configured as mutable + * */ + void decrease(handle_type handle, const_reference v) + { + BOOST_STATIC_ASSERT(is_mutable); + super_t::decrease(handle, v); + } + + /** + * \b Effects: Updates the heap after the element handled by \c handle has been changed. + * + * \b Complexity: Logarithmic. + * + * \b Note: The new value is expected to be less than the current one. If this is not called, after a handle has been updated, the behavior of the data structure is undefined! + * + * \b Requirement: data structure must be configured as mutable + * */ + void decrease(handle_type handle) + { + BOOST_STATIC_ASSERT(is_mutable); + super_t::decrease(handle); + } + + /** + * \b Effects: Removes the element handled by \c handle from the priority_queue. + * + * \b Complexity: Logarithmic. + * + * \b Requirement: data structure must be configured as mutable + * */ + void erase(handle_type handle) + { + BOOST_STATIC_ASSERT(is_mutable); + super_t::erase(handle); + } + + /** + * \b Effects: Casts an iterator to a node handle. + * + * \b Complexity: Constant. + * + * \b Requirement: data structure must be configured as mutable + * */ + static handle_type s_handle_from_iterator(iterator const & it) + { + BOOST_STATIC_ASSERT(is_mutable); + return super_t::s_handle_from_iterator(it); + } + + /// \copydoc boost::heap::priority_queue::pop + void pop(void) + { + super_t::pop_min(); + } + + /** + * \b Effects: Removes the element with the highest priority from the priority queue. + * + * \b Complexity: + * + * \b Note: Same as pop + * */ + void pop_min(void) + { + super_t::pop_min(); + } + + /** + * \b Effects: Removes the element with the lowest priority from the priority queue. + * + * \b Complexity: + * + * */ + void pop_max(void) + { + super_t::pop_max(); + } + + /// \copydoc boost::heap::priority_queue::swap + void swap(min_max_heap & rhs) + { + super_t::swap(rhs); + } + + /// \copydoc boost::heap::priority_queue::begin + const_iterator begin(void) const + { + return super_t::begin(); + } + + /// \copydoc boost::heap::priority_queue::begin + iterator begin(void) + { + return super_t::begin(); + } + + /// \copydoc boost::heap::priority_queue::end + iterator end(void) + { + return super_t::end(); + } + + /// \copydoc boost::heap::priority_queue::end + const_iterator end(void) const + { + return super_t::end(); + } + + /// \copydoc boost::heap::fibonacci_heap::ordered_begin + ordered_iterator ordered_begin(void) const + { + return super_t::ordered_begin(); + } + + /// \copydoc boost::heap::fibonacci_heap::ordered_end + ordered_iterator ordered_end(void) const + { + return super_t::ordered_end(); + } + + /// \copydoc boost::heap::priority_queue::reserve + void reserve (size_type element_count) + { + super_t::reserve(element_count); + } + + /// \copydoc boost::heap::priority_queue::value_comp + value_compare const & value_comp(void) const + { + return super_t::value_comp(); + } +}; + +} /* namespace heap */ +} /* namespace boost */ + +#undef BOOST_HEAP_ASSERT + +#endif /* BOOST_HEAP_MIN_MAX_HEAP_HPP */ diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp new file mode 100644 index 0000000..947e564 --- /dev/null +++ b/test/min_max_heap_test.cpp @@ -0,0 +1,110 @@ +// boost heap: min-max heap +// +// Copyright (C) 2019 Grégoire Scano +// +// Distributed under 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 "common_heap_tests.hpp" +#include "stable_heap_tests.hpp" +#include "mutable_heap_tests.hpp" +#include "merge_heap_tests.hpp" + +template +void run_tree_depth_test(void) +{ + boost::heap::detail::tree_depth depth; + + BOOST_REQUIRE(depth(0) == 0); + + unsigned int index = 1; + for (unsigned int i = 1, count = D; i < 32/D; ++i, count *= D) { + for (unsigned int j = 0; j < count; ++j, ++index) { + BOOST_REQUIRE(depth(index) == i); + } + } +} + +BOOST_AUTO_TEST_CASE( tree_depth_test ) +{ + BOOST_REQUIRE(boost::heap::log2(1) == 0); + BOOST_REQUIRE(boost::heap::log2(2) == 1); + + run_tree_depth_test<1>(); + run_tree_depth_test<2>(); + run_tree_depth_test<3>(); + run_tree_depth_test<4>(); + run_tree_depth_test<5>(); +} + +BOOST_AUTO_TEST_CASE( min_max_heap_paper_test ) +{ + //int buffer[] = {5, 65, 80, 25, 37, 8, 15, 57, 36, 45, 59, 20, 14, 32, 18, 28, 30, 34, 27, 39, 38, 45, 50, 15, 12, 13, 10, 30, 31, 16, 17}; + + typedef boost::heap::min_max_heap, + boost::heap::stable, + boost::heap::compare >, + boost::heap::allocator > > pri_queue; + + pri_queue q; + + q.push(31); + q.push(17); + q.push(24); + q.push(25); + q.push(37); + q.push(8); + q.push(5); +} + +template +void run_min_max_heap_test(void) +{ + typedef boost::heap::min_max_heap, + boost::heap::stable, + boost::heap::compare >, + boost::heap::allocator > > pri_queue; + + BOOST_CONCEPT_ASSERT((boost::heap::PriorityQueue)); + + run_concept_check(); + run_common_heap_tests(); + + run_iterator_heap_tests(); + run_copyable_heap_tests(); + run_moveable_heap_tests(); + run_reserve_heap_tests(); + run_merge_tests(); + + //run_ordered_iterator_tests(); +#if 0 + if (stable) { + typedef boost::heap::min_max_heap, + boost::heap::stable + > stable_pri_queue; + + run_stable_heap_tests(); + } +#endif +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + cmpthings ord; + boost::heap::min_max_heap, boost::heap::compare, boost::heap::stable > vpq(ord); + vpq.emplace(5, 6, 7); +#endif +} + +BOOST_AUTO_TEST_CASE( min_max_heap_test ) +{ + run_min_max_heap_test<2, false>(); + run_min_max_heap_test<3, false>(); + run_min_max_heap_test<4, false>(); + run_min_max_heap_test<5, false>(); +} From 6619c9532432e19c30e6f3db69b95b7d1fe88fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Mon, 22 Jul 2019 10:02:17 +0800 Subject: [PATCH 02/13] min-max heap: preparing ordered iterator --- .../heap/detail/ordered_adaptor_iterator.hpp | 8 + include/boost/heap/min_max_heap.hpp | 233 +++++++++++++++++- test/min_max_heap_test.cpp | 206 +++++++++++++++- 3 files changed, 433 insertions(+), 14 deletions(-) diff --git a/include/boost/heap/detail/ordered_adaptor_iterator.hpp b/include/boost/heap/detail/ordered_adaptor_iterator.hpp index e050e1b..6f7f301 100644 --- a/include/boost/heap/detail/ordered_adaptor_iterator.hpp +++ b/include/boost/heap/detail/ordered_adaptor_iterator.hpp @@ -92,6 +92,14 @@ class ordered_adaptor_iterator: discover_nodes(initial_index); } + ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): + Dispatcher(dispatcher), + container(container), current_index(initial_index), + unvisited_nodes(compare_by_heap_value(container, cmp)) + { + discover_nodes(initial_index); + } + private: bool equal (ordered_adaptor_iterator const & rhs) const { diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index a6215b4..d2d4fd2 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -36,7 +36,10 @@ #define BOOST_HEAP_MIN_MAX_HEAP_HPP #include -#include +#include // memset +#include //TODO remove + +#include #include @@ -65,6 +68,9 @@ namespace detail { template struct tree_depth; +template +struct min_max_ordered_iterator_status; + typedef parameter::parameters, boost::parameter::optional, boost::parameter::optional, @@ -537,7 +543,7 @@ class min_max_heap: BOOST_ASSERT(!empty()); BOOST_ASSERT(index < size()); - swap(index, last());//TODO last() + swap(index, last()); q_.pop_back(); if(!empty() && index != size()) @@ -577,6 +583,12 @@ class min_max_heap: template struct iterator_dispatcher { + iterator_dispatcher(size_type max = 0) : + status(max) + {} + + min_max_ordered_iterator_status status; + static size_type max_index(const min_max_heap * heap) { return heap->last(); @@ -606,7 +618,9 @@ class min_max_heap: public: struct ordered_iterator_dispatcher : iterator_dispatcher { - + ordered_iterator_dispatcher(size_type max): + iterator_dispatcher(max) + {} }; public: @@ -621,12 +635,12 @@ class min_max_heap: public: ordered_iterator ordered_begin(void) const { - return ordered_iterator(root(), this, super_t::get_internal_cmp()); + return ordered_iterator(root(), this, super_t::get_internal_cmp(), ordered_iterator_dispatcher(this->last())); } ordered_iterator ordered_end(void) const { - return ordered_iterator(q_.size(), this, super_t::get_internal_cmp()); + return ordered_iterator(q_.size(), this, super_t::get_internal_cmp(), ordered_iterator_dispatcher(this->last())); } public: @@ -707,6 +721,215 @@ struct tree_depth<2, IntType> } }; +template +struct ipower +{ + ipower(IntType max) + { + if (1 <= max) { + power.resize(max + 1); + power[0] = 1; + + for (IntType exp = 1; exp <= max; ++exp) { + power[exp] = power[exp-1] * Base; + } + } + } + + std::vector power; + + IntType operator () (IntType exp) const + { + return power[exp]; + } + + static IntType pow(IntType exp, IntType res = 1) + { + return exp == 0 ? res : pow(exp - 1, res * Base); + } +}; + +template +struct ipower<2, IntType> +{ + ipower(IntType) {} + + IntType operator () (IntType exp) const + { + return 1 << exp; + } + + static IntType pow(IntType exp) + { + return 1 << exp; + } +}; + +template +struct min_max_ordered_iterator_status_base +{ + min_max_ordered_iterator_status_base(IntType max_index = 0) : + max_depth(boost::heap::detail::tree_depth()(max_index)), + power(max_depth), + candidates(std::max(ipower::pow(max_depth) / 8, (IntType)1), 0) + {} + + const IntType max_depth; + const ipower power; + std::vector candidates; + + IntType number_of_final_heirs_for(IntType current_depth) const + { + return power(max_depth - current_depth); + } + + void positions(IntType index, IntType & chunk, IntType & offset, IntType & heirs) + { + IntType depth = boost::heap::detail::tree_depth()(index); + + const IntType local_index = index - (power(depth) - 1)/(Base - 1); + + heirs = number_of_final_heirs_for(depth); + + const IntType candidate_index = local_index * heirs; + + chunk = candidate_index / 8; + offset = candidate_index % 8; + } + + void positions_by_8(IntType index, IntType & chunk, IntType & offset, IntType & heirs_oct, IntType & heirs_left) + { + IntType heirs; + positions(index, chunk, offset, heirs); + + heirs_oct = heirs / 8; + heirs_left = heirs % 8; + } +}; + +template +struct min_max_ordered_iterator_status : min_max_ordered_iterator_status_base +{ + typedef min_max_ordered_iterator_status_base base; + + min_max_ordered_iterator_status(IntType max_index = 0) : + base(max_index) + {} + + void set(IntType index) + { + IntType chunk, offset, heirs; + base::positions(index, chunk, offset, heirs); + + const IntType first_heirs = std::min(heirs, 8 - offset); + if (first_heirs < 8) { + base::candidates[chunk] |= (0xFF >> (offset + (8 - offset - first_heirs))) << (8 - offset - first_heirs); + ++chunk; + heirs -= first_heirs; + } + + const IntType heirs_octuple = heirs / 8; + const IntType last_heirs = heirs % 8; + + std::memset(base::candidates.data() + chunk, 0xFF, heirs_octuple); + chunk += heirs_octuple; + + base::candidates[chunk] |= 0xFF << (8 - last_heirs); + } + + void reset(IntType index) + { + IntType chunk, offset, heirs; + base::positions(index, chunk, offset, heirs); + + const IntType first_heirs = std::min(heirs, 8 - offset); + if (first_heirs < 8) { + base::candidates[chunk] &= ~((0xFF >> (offset + (8 - offset - first_heirs))) << (8 - offset - first_heirs)); + ++chunk; + heirs -= first_heirs; + } + + const IntType heirs_octuple = heirs / 8; + const IntType last_heirs = heirs % 8; + + std::memset(base::candidates.data() + chunk, 0, heirs_octuple); + chunk += heirs_octuple; + + base::candidates[chunk] &= ~(0xFF << (8 - last_heirs)); + } + + bool is_complete(IntType index) + { + IntType chunk, offset, heirs; + base::positions(index, chunk, offset, heirs); + + const IntType first_heirs = std::min(heirs, 8 - offset); + if (first_heirs < 8) { + if(!(base::candidates[chunk] & ((0xFF >> (offset + (8 - offset - first_heirs))) << (8 - offset - first_heirs)))) return false; + ++chunk; + heirs -= first_heirs; + } + + while (8 <= heirs) { + if (base::candidates[chunk] != 0xFF) + return false; + ++chunk; + heirs -= 8; + } + + return (heirs && base::candidates[chunk] & (0xFF << (8 - heirs))) || !heirs; + } +}; + +template +struct min_max_ordered_iterator_status<2, IntType> : min_max_ordered_iterator_status_base<2, IntType> +{ + typedef min_max_ordered_iterator_status_base<2, IntType> base; + + min_max_ordered_iterator_status(IntType max_index = 0) : + base(max_index), + masks{0, 0x01, 0x03, 0, 0x0F, 0, 0, 0} + {} + + const uint8_t masks[8]; + + void set(IntType index) + { + IntType chunk, offset, heirs_octuple, heirs_left; + base::positions_by_8(index, chunk, offset, heirs_octuple, heirs_left); + + std::memset(base::candidates.data() + chunk, 0xFF, heirs_octuple); + // disjoint heirs + base::candidates[chunk] |= masks[heirs_left] << (8 - heirs_left - offset); + } + + void reset(IntType index) + { + IntType chunk, offset, heirs_octuple, heirs_left; + base::positions_by_8(index, chunk, offset, heirs_octuple, heirs_left); + + std::memset(base::candidates.data() + chunk, 0, heirs_octuple); + // disjoint heirs + base::candidates[chunk] &= ~(masks[heirs_left] << (8 - heirs_left - offset)); + } + + bool is_complete(IntType index) + { + IntType chunk, offset, heirs; + base::positions(index, chunk, offset, heirs); + + while (8 <= heirs) { + if (base::candidates[chunk] != 0xFF) + return false; + ++chunk; + heirs -= 8; + } + //disjoint heirs + const uint8_t mask = masks[heirs] << (8 - heirs - offset); + return (mask && base::candidates[chunk] & mask) || !mask; + } +}; + template struct select_minmax_heap { diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index 947e564..34fdbea 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -7,6 +7,9 @@ // http://www.boost.org/LICENSE_1_0.txt) #define BOOST_TEST_MAIN + +#define BOOST_HEAP_SANITYCHECKS + #include #include @@ -16,6 +19,10 @@ #include "mutable_heap_tests.hpp" #include "merge_heap_tests.hpp" +#include +#include +#include + template void run_tree_depth_test(void) { @@ -43,6 +50,178 @@ BOOST_AUTO_TEST_CASE( tree_depth_test ) run_tree_depth_test<5>(); } +void print(const std::vector & v) +{ + for (std::vector::const_iterator it = v.begin(); it != v.end(); ++it) { + std::cerr << std::bitset<8>(*it) << " "; + } + std::cerr << std::endl; +} + +unsigned int last_line_index(unsigned int base, unsigned int max, unsigned int & depth) +{ + depth = 0; + unsigned int first = 0; + unsigned int local = 1; + unsigned int count = 1; + while (count <= max) { + first = count; + local *= base; + count += local; + ++depth; + } + + return first; +} + +void masks(unsigned int base, unsigned int max, unsigned int index, std::set & mask, std::set & umask) +{ + BOOST_REQUIRE(max != 0); + + unsigned int depth; + const unsigned int last_line_left_index = last_line_index(base, max, depth); + mask.clear(); + + std::queue queue; + queue.push(index); + + while (!queue.empty()) { + const unsigned int child = queue.front(); + queue.pop(); + + if (last_line_left_index <= child) + mask.insert(child); + else { + unsigned int current = base * child + 1; + const unsigned int right = base * (child + 1); + + while (current <= right) { + if (current <= max) + queue.push(current); + else + mask.insert(current); + + ++current; + } + } + } + + umask.clear(); + + const unsigned int last_line_right_index = last_line_left_index + std::pow(base, depth) - 1; + + for (unsigned int i = last_line_left_index; i <= last_line_right_index; ++i) + if (mask.find(i) == mask.end()) + umask.insert(i); +} + +BOOST_AUTO_TEST_CASE( run_expected_mask_test ) +{ + std::set mask; + std::set umask; + +#define LOCALTEST(base, max, index, ...) \ + masks(base, max, index, mask, umask); \ + BOOST_REQUIRE(mask == std::set(__VA_ARGS__)); + + LOCALTEST(2, 1, 0, {1,2}); + LOCALTEST(2, 1, 1, {1}); + LOCALTEST(2, 2, 0, {1,2}); + LOCALTEST(2, 2, 1, {1}); + LOCALTEST(2, 2, 2, {2}); + LOCALTEST(2, 3, 0, {3,4,5,6}); + LOCALTEST(2, 3, 1, {3,4}); + LOCALTEST(2, 3, 2, {5,6}); + LOCALTEST(2, 3, 3, {3}); + LOCALTEST(2, 4, 0, {3,4,5,6}); + LOCALTEST(2, 4, 1, {3,4}); + LOCALTEST(2, 4, 2, {5,6}); + LOCALTEST(2, 4, 3, {3}); + LOCALTEST(2, 4, 4, {4}); + + LOCALTEST(3, 1, 0, {1,2,3}); + LOCALTEST(3, 1, 1, {1}); + LOCALTEST(3, 2, 0, {1,2,3}); + LOCALTEST(3, 2, 1, {1}); + LOCALTEST(3, 2, 2, {2}); + LOCALTEST(3, 3, 0, {1,2,3}); + LOCALTEST(3, 3, 1, {1}); + LOCALTEST(3, 3, 2, {2}); + LOCALTEST(3, 3, 3, {3}); + LOCALTEST(3, 4, 0, {4,5,6,7,8,9,10,11,12}); + LOCALTEST(3, 4, 1, {4,5,6}); + LOCALTEST(3, 4, 2, {7,8,9}); + LOCALTEST(3, 4, 3, {10,11,12}); + LOCALTEST(3, 4, 4, {4}); + LOCALTEST(3, 5, 0, {4,5,6,7,8,9,10,11,12}); + LOCALTEST(3, 5, 1, {4,5,6}); + LOCALTEST(3, 5, 2, {7,8,9}); + LOCALTEST(3, 5, 3, {10,11,12}); + LOCALTEST(3, 5, 4, {4}); + LOCALTEST(3, 5, 5, {5}); +#undef LOCALTEST +} + +template +void run_min_max_ordered_iterator_status(unsigned int max) +{ + std::set mask; + std::set umask; + + for (unsigned int max_index = 1; max_index <= std::pow(D, max) + 1; ++max_index) { + for (unsigned int index = 0; index <= max_index; ++index) { + masks(D, max_index, index, mask, umask); + + { + boost::heap::detail::min_max_ordered_iterator_status status(max_index); + + for (std::set::const_iterator it = mask.begin(); it != mask.end(); ++it) { + status.set(*it); + BOOST_REQUIRE(status.is_complete(*it)); + } + + BOOST_REQUIRE(status.is_complete(index)); + + for (std::set::const_iterator it = umask.begin(); it != umask.end(); ++it) + BOOST_REQUIRE(!status.is_complete(*it)); + + for (std::set::const_iterator it = mask.begin(); it != mask.end(); ++it) { + status.reset(*it); + BOOST_REQUIRE(!status.is_complete(*it)); + } + + BOOST_REQUIRE(!status.is_complete(index)); + } + + { + boost::heap::detail::min_max_ordered_iterator_status status(max_index); + + status.set(index); + + for (std::set::const_iterator it = mask.begin(); it != mask.end(); ++it) + BOOST_REQUIRE(status.is_complete(*it)); + + for (std::set::const_iterator it = umask.begin(); it != umask.end(); ++it) + BOOST_REQUIRE(!status.is_complete(*it)); + + status.reset(index); + BOOST_REQUIRE(!status.is_complete(index)); + + for (std::set::const_iterator it = mask.begin(); it != mask.end(); ++it) + BOOST_REQUIRE(!status.is_complete(*it)); + } + } + } +} + +BOOST_AUTO_TEST_CASE( run_min_max_ordered_iterator_status_test ) +{ + run_min_max_ordered_iterator_status<2>(7); + run_min_max_ordered_iterator_status<3>(3); + run_min_max_ordered_iterator_status<4>(2); + run_min_max_ordered_iterator_status<5>(2); +} + BOOST_AUTO_TEST_CASE( min_max_heap_paper_test ) { //int buffer[] = {5, 65, 80, 25, 37, 8, 15, 57, 36, 45, 59, 20, 14, 32, 18, 28, 30, 34, 27, 39, 38, 45, 50, 15, 12, 13, 10, 30, 31, 16, 17}; @@ -76,13 +255,22 @@ void run_min_max_heap_test(void) BOOST_CONCEPT_ASSERT((boost::heap::PriorityQueue)); run_concept_check(); - run_common_heap_tests(); + //run_common_heap_tests(); + pri_queue_test_sequential_push(); + pri_queue_test_sequential_reverse_push(); + pri_queue_test_random_push(); + pri_queue_test_equality(); + pri_queue_test_inequality(); + pri_queue_test_less(); + pri_queue_test_clear(); - run_iterator_heap_tests(); - run_copyable_heap_tests(); - run_moveable_heap_tests(); - run_reserve_heap_tests(); - run_merge_tests(); + pri_queue_test_emplace(); + // + //run_iterator_heap_tests(); + //run_copyable_heap_tests(); + //run_moveable_heap_tests(); + //run_reserve_heap_tests(); + //run_merge_tests(); //run_ordered_iterator_tests(); #if 0 @@ -104,7 +292,7 @@ void run_min_max_heap_test(void) BOOST_AUTO_TEST_CASE( min_max_heap_test ) { run_min_max_heap_test<2, false>(); - run_min_max_heap_test<3, false>(); - run_min_max_heap_test<4, false>(); - run_min_max_heap_test<5, false>(); + //run_min_max_heap_test<3, false>(); + //run_min_max_heap_test<4, false>(); + //run_min_max_heap_test<5, false>(); } From 23e8ac9a83ce517169e1e6952c31c7abdc30b9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Mon, 22 Jul 2019 10:06:22 +0800 Subject: [PATCH 03/13] min-max heap: ordered iterator --- .../heap/detail/ordered_adaptor_iterator.hpp | 186 ++++++++++--- include/boost/heap/min_max_heap.hpp | 248 ++++++++++++------ test/common_heap_tests.hpp | 4 +- test/min_max_heap_test.cpp | 192 ++++++++++---- 4 files changed, 455 insertions(+), 175 deletions(-) diff --git a/include/boost/heap/detail/ordered_adaptor_iterator.hpp b/include/boost/heap/detail/ordered_adaptor_iterator.hpp index 6f7f301..465a53b 100644 --- a/include/boost/heap/detail/ordered_adaptor_iterator.hpp +++ b/include/boost/heap/detail/ordered_adaptor_iterator.hpp @@ -32,24 +32,19 @@ namespace detail { * * static value_type const & get_value(internal_type const & arg) const; // get value_type from internal_type * * */ -template -class ordered_adaptor_iterator: - public boost::iterator_facade, - ValueType, - boost::forward_traversal_tag - >, - Dispatcher + > +class ordered_adaptor_iterator_base : + public boost::iterator_facade, + public Dispatcher { friend class boost::iterator_core_access; @@ -71,37 +66,38 @@ class ordered_adaptor_iterator: } }; +public: const ContainerType * container; size_t current_index; // current index: special value -1 denotes `end' iterator public: - ordered_adaptor_iterator(void): + ordered_adaptor_iterator_base(void): container(NULL), current_index((std::numeric_limits::max)()), unvisited_nodes(compare_by_heap_value(NULL, ValueCompare())) {} - ordered_adaptor_iterator(const ContainerType * container, ValueCompare const & cmp): + ordered_adaptor_iterator_base(const ContainerType * container, ValueCompare const & cmp): container(container), current_index(container->size()), unvisited_nodes(compare_by_heap_value(container, ValueCompare())) {} - ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): + ordered_adaptor_iterator_base(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): container(container), current_index(initial_index), unvisited_nodes(compare_by_heap_value(container, cmp)) { - discover_nodes(initial_index); + static_cast(this)->discover_nodes(initial_index); } - ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): + ordered_adaptor_iterator_base(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): Dispatcher(dispatcher), container(container), current_index(initial_index), unvisited_nodes(compare_by_heap_value(container, cmp)) { - discover_nodes(initial_index); + static_cast(this)->discover_nodes(initial_index); } -private: - bool equal (ordered_adaptor_iterator const & rhs) const +public: + bool equal (ordered_adaptor_iterator_base const & rhs) const { if (current_index != rhs.current_index) return false; @@ -119,7 +115,7 @@ class ordered_adaptor_iterator: else { current_index = unvisited_nodes.top(); unvisited_nodes.pop(); - discover_nodes(current_index); + static_cast(this)->discover_nodes(current_index); } } @@ -129,17 +125,6 @@ class ordered_adaptor_iterator: return Dispatcher::get_value(Dispatcher::get_internal_value(container, current_index)); } - void discover_nodes(size_t index) - { - if (Dispatcher::is_leaf(container, index)) - return; - - std::pair child_range = Dispatcher::get_child_nodes(container, index); - - for (size_t i = child_range.first; i <= child_range.second; ++i) - unvisited_nodes.push(i); - } - std::priority_queue::other >, @@ -151,6 +136,139 @@ class ordered_adaptor_iterator: }; +template +class ordered_adaptor_iterator: + public ordered_adaptor_iterator_base, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher> +{ + typedef ordered_adaptor_iterator_base, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher> + base; + + friend class boost::iterator_core_access; + +public: + ordered_adaptor_iterator(void) + {} + + ordered_adaptor_iterator(const ContainerType * container, ValueCompare const & cmp): + base(container, cmp) + {} + + ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): + base(initial_index, container, cmp) + {} + + ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): + base(initial_index, container, cmp, dispatcher) + {} + +public: + void discover_nodes(size_t index) + { + if (Dispatcher::is_leaf(base::container, index)) + return; + + std::pair child_range = Dispatcher::get_child_nodes(base::container, index); + + for (size_t i = child_range.first; i <= child_range.second; ++i) + base::unvisited_nodes.push(i); + } +}; + +template +class extended_ordered_adaptor_iterator: + public ordered_adaptor_iterator_base, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher + > +{ + typedef ordered_adaptor_iterator_base, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher + > + base; + + friend class boost::iterator_core_access; + +public: + extended_ordered_adaptor_iterator(void) + {} + + extended_ordered_adaptor_iterator(const ContainerType * container, ValueCompare const & cmp): + base(container, cmp) + {} + + extended_ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): + base(initial_index, container, cmp) + {} + + extended_ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): + base(initial_index, container, cmp, dispatcher) + {} + + void discover_nodes(size_t index) + { + if (Dispatcher::is_leaf(base::container, index)) + return; + + std::pair extra_child_range = std::make_pair(1, 0); + std::pair child_range = Dispatcher::get_child_nodes(base::container, index, extra_child_range); + + for (size_t i = extra_child_range.first; i <= extra_child_range.second; ++i) + base::unvisited_nodes.push(i); + + for (size_t i = child_range.first; i <= child_range.second; ++i) + base::unvisited_nodes.push(i); + } +}; + } /* namespace detail */ } /* namespace heap */ } /* namespace boost */ diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index d2d4fd2..0791cce 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -37,12 +37,11 @@ #include #include // memset -#include //TODO remove #include +#include #include - #include #include #include @@ -65,10 +64,10 @@ namespace boost { namespace heap { namespace detail { -template +template struct tree_depth; -template +template struct min_max_ordered_iterator_status; typedef parameter::parameters, @@ -176,7 +175,7 @@ class min_max_heap: return super_t::value_comp(); } - template + template bool compare(size_type i, size_type j) const { BOOST_ASSERT(i < this->size()); @@ -235,11 +234,6 @@ class min_max_heap: return 0; } - std::pair get_child_nodes(size_type index) const - { - return std::make_pair(first_child(index), last_child(index)); - } - size_type first_child(size_type index) const { return D * index + 1; @@ -250,19 +244,26 @@ class min_max_heap: return D * (index + 1); } - std::pair get_grandchild_nodes(size_type index) const + std::pair children(size_type index) const { - return std::make_pair(first_grandchild(index), last_grandchild(index)); + size_type child = first_child(index); + return std::make_pair(child, child + D - 1); } size_type first_grandchild(size_type index) const { - return first_child(first_child(index)); + return D * D * index + D + 1; } size_type last_grandchild(size_type index) const { - return last_child(last_child(index)); + return D * D * index + D * (D + 1); + } + + std::pair grandchildren(size_type index) const + { + size_type grandchild = first_grandchild(index); + return std::make_pair(grandchild, grandchild + D * D - 1); } size_type last(void) const @@ -298,7 +299,7 @@ class min_max_heap: return parent(parent(index)); } - template + template bool best_between(size_type & best, size_type current, size_type theorical_last) const { bool found = false; @@ -312,21 +313,19 @@ class min_max_heap: return found; } - template + template size_type best_child_or_grandchild(size_type index, bool & is_grandchild) const { - const size_type first_child = this->first_child(index); + const std::pair children = this->children(index); - if(last() < first_child) return npos(); + if(last() < children.first) return npos(); - const size_type last_child = this->last_child(index); - const size_type first_grandchild = this->first_child(first_child); - const size_type last_grandchild = this->last_child(last_child); + const std::pair grandchildren = this->grandchildren(index); - size_type best_child = first_child; + size_type best_child = children.first; - best_between(best_child, first_child + 1, last_child); - is_grandchild = best_between(best_child, first_grandchild, last_grandchild); + best_between(best_child, children.first + 1, children.second); + is_grandchild = best_between(best_child, grandchildren.first, grandchildren.second); return best_child; } @@ -341,7 +340,7 @@ class min_max_heap: trickle_down_impl(index); } - template + template void trickle_down_impl(size_type i) { bool is_grandchild; @@ -374,7 +373,7 @@ class min_max_heap: bubble_up_impl(i); } - template + template void bubble_up_impl(size_type i) { size_type parent = this->parent(i); @@ -389,7 +388,7 @@ class min_max_heap: } } - template + template void bubble_up_impl_(size_type i) { size_type grandparent = this->grandparent(i); @@ -580,11 +579,10 @@ class min_max_heap: } public: - template struct iterator_dispatcher { - iterator_dispatcher(size_type max = 0) : - status(max) + iterator_dispatcher(size_type max_index = 0) : + status(max_index) {} min_max_ordered_iterator_status status; @@ -594,13 +592,96 @@ class min_max_heap: return heap->last(); } - bool is_leaf(const min_max_heap * heap, size_type index) const + static bool is_leaf(const min_max_heap * heap, size_type index) { - return false; + return (heap->root() < index && index <= D) || index == heap->last() + 1; } - std::pair get_child_nodes(const min_max_heap * heap, size_type index) + std::pair get_child_nodes(const min_max_heap * heap, size_type index, std::pair & extra_child_nodes) { + /* As stated in the article, the Hasse diagram is divided in two parts. + * The first one consists of even levels and can be explored like a + * regular binary tree except that the number of children of a node + * is D^2 instead of D because odd levels are skipped. + * The second part consists of odd levels and is a bit more complicated + * to explore. The search has to go back up in the tree from the leaves + * but since a node on an odd level is being pointed to by D^2 grandchildren + * it cannot be visited until all of its heirs have been visited. + * + * The 'min_max_ordered_iterator_status' structure is used to keep track + * of this. It indicates for each node on an odd level whether its heirs + * have been visited. When the last heir is being visited, it adds the + * node to the potential candidates to be visited next. Furthermore, when + * this happens, the markers for its heirs are reset and will then be + * reused to indicate to its own grandfather that it has been visited while + * its siblings may not have yet been visited. + * + * This method requires $O(log_D((D - 1) * (size() - 1) + 1))$ bytes. + * + * The following specific cases must be addressed (D = 3): + * 0 1) when 0 is visited, it must add nodes 4-8 to the list + * /|\ of potential candidates to be visited next, as well + * / | \ as node 3. This explains the 'extra_child_nodes' as + * / | \ node denoted 4-8 and 3 might not be consecutive in a + * / | \ larger tree where the example would be a subtree. + * / | \ 2) when 8 is visited, it must set its indicator and + * 1 2 3 set the indicator for the non existing node 9 (in + * /|\ /| general for all its right siblings) and then add 2 + * 4 5 6 7 8 if 7 has already been visited. + */ + const size_type last = heap->last(); + const bool on_compare_level = heap->is_on_compare_level(index); + + if (on_compare_level) { + std::pair children = heap->children(index); + + if (children.first <= last) { + std::pair grandchildren = heap->grandchildren(index); + + if (grandchildren.first <= last) { + if (last < grandchildren.second) { + grandchildren.second = last; + + extra_child_nodes.first = heap->parent(grandchildren.second) + 1; + extra_child_nodes.second = children.second; + } + + return grandchildren; + } + + children.second = std::min(children.second, last); + + return children; + } + }// else is leaf or not on compare level + + status.set(index); + size_type parent = heap->parent(index); + + if (BOOST_LIKELY(parent != heap->npos())) { + size_type rightmost_sibling = heap->last_child(parent); + + // if (last < rightmost_sibling) + for(size_type i = last + 1; i <= rightmost_sibling; ++i) + status.set(i); + + if (on_compare_level) { + if(status.is_complete(parent)) { + status.reset(parent); + return std::make_pair(parent, parent); + } + } + else { + size_type grandparent = heap->parent(parent); + + if (BOOST_LIKELY(grandparent != heap->npos()) + && status.is_complete(grandparent)) { + status.reset(grandparent); + return std::make_pair(grandparent, grandparent); + } + } + } + return std::make_pair(1, 0); } @@ -616,35 +697,35 @@ class min_max_heap: }; public: - struct ordered_iterator_dispatcher : iterator_dispatcher + struct ordered_iterator_dispatcher : iterator_dispatcher { ordered_iterator_dispatcher(size_type max): - iterator_dispatcher(max) + iterator_dispatcher(max) {} }; public: - typedef detail::ordered_adaptor_iterator + typedef detail::extended_ordered_adaptor_iterator ordered_iterator; public: ordered_iterator ordered_begin(void) const { - return ordered_iterator(root(), this, super_t::get_internal_cmp(), ordered_iterator_dispatcher(this->last())); + return ordered_iterator(root(), this, super_t::get_internal_cmp(), ordered_iterator_dispatcher(size())); } ordered_iterator ordered_end(void) const { - return ordered_iterator(q_.size(), this, super_t::get_internal_cmp(), ordered_iterator_dispatcher(this->last())); + return ordered_iterator(size(), this, super_t::get_internal_cmp(), ordered_iterator_dispatcher(root())); } public: - struct reverse_ordered_iterator_dispatcher : iterator_dispatcher + struct reverse_ordered_iterator_dispatcher : iterator_dispatcher { }; @@ -662,12 +743,12 @@ class min_max_heap: } }; - typedef detail::ordered_adaptor_iterator + typedef detail::extended_ordered_adaptor_iterator reverse_ordered_iterator; public: @@ -683,7 +764,7 @@ class min_max_heap: /* iterators */ }; -template +template struct tree_depth { IntType operator () (IntType index) const @@ -701,18 +782,18 @@ struct tree_depth return depth; } - /* Alternatively, let f be a function mapping an index n to its - corresponding depth (or row) r in a D tree, f_D(n)=r and 1 < D - If n is on row r, then $\sum_{i=0}^{r} D^{i} <= n < \sum_{i=0}^{r+1} D^{i}$ - hence $D^{r+1} <= (D-1) * n + 1 < D^{r+2}$ then - $r + 1 <= \frac{log_2((D-1) * n + 1)}{log_2(D)} < r+2$ - since log_2 is strictly increasing and because an array index starts at 0 (r:=r-1) - $r = log_2((Base - 1) * index + 1) / log_2(Base)$ - which is slower than the above iterative method for indexes up to 10^10. - */ + /* Alternatively, let f be a function mapping an index n to its corresponding + * depth (or row) r in a D tree, f_D(n)=r and 1 < D. If n is on row r, then + * $\sum_{i=0}^{r} D^{i} <= n < \sum_{i=0}^{r+1} D^{i}$, hence + * $D^{r+1} <= (D-1) * n + 1 < D^{r+2}$ and since log_D is strictly increasing + * $r + 1 <= log_D((D-1) * n + 1) < r + 2$ and then + * because an array index is an integer starting at 0 (r:=r-1); + * $r = log_D((D - 1) * index + 1)$ + * which is slower than the above iterative method for indexes up to 10^10. + */ }; -template +template struct tree_depth<2, IntType> { IntType operator () (IntType index) const @@ -721,7 +802,7 @@ struct tree_depth<2, IntType> } }; -template +template struct ipower { ipower(IntType max) @@ -749,7 +830,7 @@ struct ipower } }; -template +template struct ipower<2, IntType> { ipower(IntType) {} @@ -765,14 +846,15 @@ struct ipower<2, IntType> } }; -template +template struct min_max_ordered_iterator_status_base { min_max_ordered_iterator_status_base(IntType max_index = 0) : max_depth(boost::heap::detail::tree_depth()(max_index)), - power(max_depth), - candidates(std::max(ipower::pow(max_depth) / 8, (IntType)1), 0) - {} + power(max_depth) + { + candidates.resize(1 + (power.pow(max_depth) + 1) / 8, 0); + } const IntType max_depth; const ipower power; @@ -807,7 +889,7 @@ struct min_max_ordered_iterator_status_base } }; -template +template struct min_max_ordered_iterator_status : min_max_ordered_iterator_status_base { typedef min_max_ordered_iterator_status_base base; @@ -865,7 +947,8 @@ struct min_max_ordered_iterator_status : min_max_ordered_iterator_status_base> (offset + (8 - offset - first_heirs))) << (8 - offset - first_heirs)))) return false; + IntType mask = (0xFF >> (offset + (8 - offset - first_heirs))) << (8 - offset - first_heirs); + if((base::candidates[chunk] & mask) != mask) return false; ++chunk; heirs -= first_heirs; } @@ -877,11 +960,12 @@ struct min_max_ordered_iterator_status : min_max_ordered_iterator_status_base +template struct min_max_ordered_iterator_status<2, IntType> : min_max_ordered_iterator_status_base<2, IntType> { typedef min_max_ordered_iterator_status_base<2, IntType> base; @@ -926,11 +1010,11 @@ struct min_max_ordered_iterator_status<2, IntType> : min_max_ordered_iterator_st } //disjoint heirs const uint8_t mask = masks[heirs] << (8 - heirs - offset); - return (mask && base::candidates[chunk] & mask) || !mask; + return (mask && (base::candidates[chunk] & mask) == mask) || !mask; } }; -template +template struct select_minmax_heap { static const bool is_mutable = extract_mutable::value; @@ -964,19 +1048,19 @@ struct select_minmax_heap #ifdef BOOST_DOXYGEN_INVOKED template #else -template +template #endif class min_max_heap: public detail::select_minmax_heap::type>::type { -typedef typename detail::min_max_heap_signature::bind::type bound_args; + typedef typename detail::min_max_heap_signature::bind::type bound_args; typedef typename detail::select_minmax_heap::type super_t; template diff --git a/test/common_heap_tests.hpp b/test/common_heap_tests.hpp index 4454c2a..2676fbc 100644 --- a/test/common_heap_tests.hpp +++ b/test/common_heap_tests.hpp @@ -30,14 +30,14 @@ void random_shuffle(RandomIt first, RandomIt last) for (difference_type i = n-1; i > 0; --i) { difference_type j = std::rand() % (i + 1); if (j != i) { - using std::swap; + using std::swap; swap(first[i], first[j]); } } } #else - + using std::random_shuffle; #endif diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index 34fdbea..3511a4e 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -23,7 +23,7 @@ #include #include -template +template void run_tree_depth_test(void) { boost::heap::detail::tree_depth depth; @@ -31,11 +31,9 @@ void run_tree_depth_test(void) BOOST_REQUIRE(depth(0) == 0); unsigned int index = 1; - for (unsigned int i = 1, count = D; i < 32/D; ++i, count *= D) { - for (unsigned int j = 0; j < count; ++j, ++index) { + for (unsigned int i = 1, count = D; i < 32/D; ++i, count *= D) + for (unsigned int j = 0; j < count; ++j, ++index) BOOST_REQUIRE(depth(index) == i); - } - } } BOOST_AUTO_TEST_CASE( tree_depth_test ) @@ -50,14 +48,6 @@ BOOST_AUTO_TEST_CASE( tree_depth_test ) run_tree_depth_test<5>(); } -void print(const std::vector & v) -{ - for (std::vector::const_iterator it = v.begin(); it != v.end(); ++it) { - std::cerr << std::bitset<8>(*it) << " "; - } - std::cerr << std::endl; -} - unsigned int last_line_index(unsigned int base, unsigned int max, unsigned int & depth) { depth = 0; @@ -115,7 +105,7 @@ void masks(unsigned int base, unsigned int max, unsigned int index, std::set mask; std::set umask; @@ -162,8 +152,8 @@ BOOST_AUTO_TEST_CASE( run_expected_mask_test ) #undef LOCALTEST } -template -void run_min_max_ordered_iterator_status(unsigned int max) +template +void run_min_max_heap_ordered_iterator_status_test(unsigned int max) { std::set mask; std::set umask; @@ -176,6 +166,7 @@ void run_min_max_ordered_iterator_status(unsigned int max) boost::heap::detail::min_max_ordered_iterator_status status(max_index); for (std::set::const_iterator it = mask.begin(); it != mask.end(); ++it) { + BOOST_REQUIRE(!status.is_complete(index)); status.set(*it); BOOST_REQUIRE(status.is_complete(*it)); } @@ -197,6 +188,7 @@ void run_min_max_ordered_iterator_status(unsigned int max) boost::heap::detail::min_max_ordered_iterator_status status(max_index); status.set(index); + BOOST_REQUIRE(status.is_complete(index)); for (std::set::const_iterator it = mask.begin(); it != mask.end(); ++it) BOOST_REQUIRE(status.is_complete(*it)); @@ -214,36 +206,131 @@ void run_min_max_ordered_iterator_status(unsigned int max) } } -BOOST_AUTO_TEST_CASE( run_min_max_ordered_iterator_status_test ) +BOOST_AUTO_TEST_CASE( min_max_heap_ordered_iterator_status_test ) { - run_min_max_ordered_iterator_status<2>(7); - run_min_max_ordered_iterator_status<3>(3); - run_min_max_ordered_iterator_status<4>(2); - run_min_max_ordered_iterator_status<5>(2); + run_min_max_heap_ordered_iterator_status_test<2>(7); + run_min_max_heap_ordered_iterator_status_test<3>(3); + run_min_max_heap_ordered_iterator_status_test<4>(2); + run_min_max_heap_ordered_iterator_status_test<5>(2); } -BOOST_AUTO_TEST_CASE( min_max_heap_paper_test ) +template +struct dispatcher_queue : boost::heap::detail::min_max_heap { - //int buffer[] = {5, 65, 80, 25, 37, 8, 15, 57, 36, 45, 59, 20, 14, 32, 18, 28, 30, 34, 27, 39, 38, 45, 50, 15, 12, 13, 10, 30, 31, 16, 17}; + typedef boost::heap::detail::min_max_heap base; - typedef boost::heap::min_max_heap, - boost::heap::stable, - boost::heap::compare >, - boost::heap::allocator > > pri_queue; + void clear() + { + base::clear(); + } + + void set(unsigned int i) + { + base::clear(); + + while (0 < i) { + base::push(i); + --i; + } + } +}; + +template +void run_min_max_heap_iterator_dispatcher_upward_test() +{ + typedef typename boost::heap::detail::min_max_heap_signature::bind< + boost::heap::arity, + boost::parameter::void_, + boost::parameter::void_, + boost::parameter::void_, + boost::parameter::void_, + boost::parameter::void_>::type signature; + typedef dispatcher_queue pri_queue; + typedef typename pri_queue::iterator_dispatcher iterator_dispatcher; + + pri_queue q; + std::pair regular; + std::pair extra; + + { + q.set(D * D + 2); extra.first = 1; extra.second = 0; + iterator_dispatcher dispatcher = iterator_dispatcher(q.size() - 1); + + regular = dispatcher.get_child_nodes(&q, D * D + 1, extra); + BOOST_REQUIRE(regular.first == D && regular.second == D); + BOOST_REQUIRE(extra.first == 1 && extra.second == 0); + } + + q.clear(); + + { + q.set(D * (D * D + 1) + 2); extra.first = 1; extra.second = 0; + iterator_dispatcher dispatcher = iterator_dispatcher(q.size() - 1); + + for (unsigned int i = 1; i <= D; ++i) { + regular = dispatcher.get_child_nodes(&q, D * D + 1 + i, extra); + BOOST_REQUIRE(regular.first == 1 && regular.second == 0); + BOOST_REQUIRE(extra.first == 1 && extra.second == 0); + } + + regular = dispatcher.get_child_nodes(&q, D * (D * D + 1) + 1, extra); + BOOST_REQUIRE(regular.first == D && regular.second == D); + BOOST_REQUIRE(extra.first == 1 && extra.second == 0); + } +} + +BOOST_AUTO_TEST_CASE( min_max_heap_iterator_dispatcher_test ) +{ + typedef typename boost::heap::detail::min_max_heap_signature::bind< + boost::parameter::void_, + boost::parameter::void_, + boost::parameter::void_, + boost::parameter::void_, + boost::parameter::void_, + boost::parameter::void_>::type signature; + typedef dispatcher_queue pri_queue; + typedef typename pri_queue::iterator_dispatcher iterator_dispatcher; pri_queue q; + std::pair regular; + std::pair extra; + +#define CHECK(size_, index, xr,yr,xe,ye) { \ + q.set(size_); extra.first = 1; extra.second = 0; \ + iterator_dispatcher dispatcher = iterator_dispatcher(q.size()-1); \ + regular = dispatcher.get_child_nodes(&q, index, extra); \ + BOOST_REQUIRE(regular.first == xr); \ + BOOST_REQUIRE(regular.second == yr); \ + BOOST_REQUIRE((!(xe < ye) || (extra.first == xe && extra.second == ye)) \ + || (!(ye < xe) || extra.second < extra.first)); \ + } + + CHECK(1,0,1,0,1,0); + CHECK(2,0,1,1,1,0); + CHECK(3,0,1,2,1,0); + CHECK(4,0,3,3,2,2); + CHECK(5,0,3,4,2,2); + CHECK(6,0,3,5,1,0); + CHECK(7,0,3,6,1,0); + + CHECK(8,3,7,7,1,0); + CHECK(9,3,7,8,1,0); + CHECK(10,3,7,8,1,0); + CHECK(10,4,9,9,1,0); - q.push(31); - q.push(17); - q.push(24); - q.push(25); - q.push(37); - q.push(8); - q.push(5); + CHECK(13,5,11,12,1,0); + +#undef CHECK + + run_min_max_heap_iterator_dispatcher_upward_test<2>(); + run_min_max_heap_iterator_dispatcher_upward_test<3>(); + run_min_max_heap_iterator_dispatcher_upward_test<4>(); + run_min_max_heap_iterator_dispatcher_upward_test<5>(); } -template +template void run_min_max_heap_test(void) { typedef boost::heap::min_max_heap)); run_concept_check(); - //run_common_heap_tests(); - pri_queue_test_sequential_push(); - pri_queue_test_sequential_reverse_push(); - pri_queue_test_random_push(); - pri_queue_test_equality(); - pri_queue_test_inequality(); - pri_queue_test_less(); - pri_queue_test_clear(); - - pri_queue_test_emplace(); - // - //run_iterator_heap_tests(); - //run_copyable_heap_tests(); - //run_moveable_heap_tests(); - //run_reserve_heap_tests(); - //run_merge_tests(); - - //run_ordered_iterator_tests(); + run_common_heap_tests(); + + run_iterator_heap_tests(); + run_copyable_heap_tests(); + run_moveable_heap_tests(); + run_reserve_heap_tests(); + run_merge_tests(); + + run_ordered_iterator_tests(); #if 0 if (stable) { typedef boost::heap::min_max_heap, @@ -292,7 +370,7 @@ void run_min_max_heap_test(void) BOOST_AUTO_TEST_CASE( min_max_heap_test ) { run_min_max_heap_test<2, false>(); - //run_min_max_heap_test<3, false>(); - //run_min_max_heap_test<4, false>(); - //run_min_max_heap_test<5, false>(); + run_min_max_heap_test<3, false>(); + run_min_max_heap_test<4, false>(); + run_min_max_heap_test<5, false>(); } From 74f6ef650ec587943b40d66ebec086aafcf3cf47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Mon, 22 Jul 2019 14:54:37 +0800 Subject: [PATCH 04/13] min-max heap: reverse ordered iterator --- .../heap/detail/ordered_adaptor_iterator.hpp | 12 ++++ include/boost/heap/min_max_heap.hpp | 65 +++++++++++++++---- test/common_heap_tests.hpp | 26 ++++++++ test/min_max_heap_test.cpp | 12 +++- test/mutable_heap_tests.hpp | 6 ++ 5 files changed, 107 insertions(+), 14 deletions(-) diff --git a/include/boost/heap/detail/ordered_adaptor_iterator.hpp b/include/boost/heap/detail/ordered_adaptor_iterator.hpp index 465a53b..5a4a0c1 100644 --- a/include/boost/heap/detail/ordered_adaptor_iterator.hpp +++ b/include/boost/heap/detail/ordered_adaptor_iterator.hpp @@ -31,6 +31,10 @@ namespace detail { * * static internal_type const & get_internal_value(const ContainerType * heap, size_type index); // get internal value at index * * static value_type const & get_value(internal_type const & arg) const; // get value_type from internal_type * + * Requirements for Derived: + * + * * void discover_nodes(size_t index); // return children + * * */ template initial_indexes, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): + base(initial_index, container, cmp, dispatcher) + { + for (size_t i = initial_indexes.first; i <= initial_indexes.second; ++i) + if (i != initial_index) + base::unvisited_nodes.push(i); + } + void discover_nodes(size_t index) { if (Dispatcher::is_leaf(base::container, index)) diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index 0791cce..b75da7a 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -579,6 +579,7 @@ class min_max_heap: } public: + template struct iterator_dispatcher { iterator_dispatcher(size_type max_index = 0) : @@ -592,11 +593,6 @@ class min_max_heap: return heap->last(); } - static bool is_leaf(const min_max_heap * heap, size_type index) - { - return (heap->root() < index && index <= D) || index == heap->last() + 1; - } - std::pair get_child_nodes(const min_max_heap * heap, size_type index, std::pair & extra_child_nodes) { /* As stated in the article, the Hasse diagram is divided in two parts. @@ -630,7 +626,7 @@ class min_max_heap: * 4 5 6 7 8 if 7 has already been visited. */ const size_type last = heap->last(); - const bool on_compare_level = heap->is_on_compare_level(index); + const bool on_compare_level = !(Forward ^ heap->is_on_compare_level(index)); if (on_compare_level) { std::pair children = heap->children(index); @@ -697,11 +693,16 @@ class min_max_heap: }; public: - struct ordered_iterator_dispatcher : iterator_dispatcher + struct ordered_iterator_dispatcher : iterator_dispatcher { ordered_iterator_dispatcher(size_type max): - iterator_dispatcher(max) + iterator_dispatcher(max) {} + + static bool is_leaf(const min_max_heap * heap, size_type index) + { + return (heap->root() < index && index <= D) || index == heap->last() + 1; + } }; public: @@ -725,9 +726,16 @@ class min_max_heap: } public: - struct reverse_ordered_iterator_dispatcher : iterator_dispatcher + struct reverse_ordered_iterator_dispatcher : iterator_dispatcher { + reverse_ordered_iterator_dispatcher(size_type max) : + iterator_dispatcher(max) + {} + static bool is_leaf(const min_max_heap * heap, size_type index) + { + return index == heap->root() || index == heap->last() + 1; + } }; public: @@ -739,7 +747,7 @@ class min_max_heap: bool operator () (typename super_t::internal_type const & lhs, typename super_t::internal_type const & rhs) const { - return super_t::internal_compare(rhs, lhs); + return super_t::internal_compare::operator () (rhs, lhs); } }; @@ -754,12 +762,23 @@ class min_max_heap: public: reverse_ordered_iterator reverse_ordered_begin(void) const { - return revese_ordered_iterator(root(), this, super_t::get_internal_cmp()); + size_type max_index = root(); + std::pair initial_indexes = std::make_pair(1, 0); + + if (1 < size()) { + max_index = index_of_max(); + // The initial_indexes range will contain max_index and this is fine + // as long as the adaptor is storing upcoming indexes in a set. + initial_indexes.first = 1; + initial_indexes.second = std::min(D, last()); + } + + return reverse_ordered_iterator(max_index, initial_indexes, this, reverse_internal_compare(super_t::get_internal_cmp()), reverse_ordered_iterator_dispatcher(size())); } reverse_ordered_iterator reverse_ordered_end(void) const { - return revese_ordered_iterator(q_.size(), this, super_t::get_internal_cmp()); + return reverse_ordered_iterator(size(), this, reverse_internal_compare(super_t::get_internal_cmp()), reverse_ordered_iterator_dispatcher(root())); } /* iterators */ }; @@ -1085,6 +1104,7 @@ class min_max_heap: BOOST_HEAP_TYPEDEF_FROM_SUPER_T(iterator) BOOST_HEAP_TYPEDEF_FROM_SUPER_T(const_iterator) BOOST_HEAP_TYPEDEF_FROM_SUPER_T(ordered_iterator) + BOOST_HEAP_TYPEDEF_FROM_SUPER_T(reverse_ordered_iterator) BOOST_HEAP_TYPEDEF_FROM_SUPER_T(handle_type) }; #undef BOOST_HEAP_TYPEDEF_FROM_SUPER_T @@ -1110,6 +1130,7 @@ class min_max_heap: typedef typename implementation_defined::iterator iterator; typedef typename implementation_defined::const_iterator const_iterator; typedef typename implementation_defined::ordered_iterator ordered_iterator; + typedef typename implementation_defined::reverse_ordered_iterator reverse_ordered_iterator; typedef typename implementation_defined::handle_type handle_type; public: @@ -1447,6 +1468,26 @@ class min_max_heap: return super_t::ordered_end(); } + /** + * \b Effects: Returns a reverse ordered iterator to the last element contained in the priority queue. + * + * \b Note: Reverse ordered iterators traverse the priority queue in heap reverse order. + * */ + reverse_ordered_iterator reverse_ordered_begin(void) const + { + return super_t::reverse_ordered_begin(); + } + + /** + * \b Effects: Returns a reverse ordered iterator to the beginning of the priority queue. + * + * \b Note: Reverse ordered iterators traverse the priority queue in heap reverse order. + * */ + reverse_ordered_iterator reverse_ordered_end(void) const + { + return super_t::reverse_ordered_end(); + } + /// \copydoc boost::heap::priority_queue::reserve void reserve (size_type element_count) { diff --git a/test/common_heap_tests.hpp b/test/common_heap_tests.hpp index 2676fbc..276e62d 100644 --- a/test/common_heap_tests.hpp +++ b/test/common_heap_tests.hpp @@ -303,6 +303,32 @@ void pri_queue_test_ordered_iterators(void) } } +template +void pri_queue_test_reverse_ordered_iterators(void) +{ + for (int i = 6; i != test_size; ++i) { + test_data data = make_test_data(i); + test_data shuffled (data); + random_shuffle(shuffled.begin(), shuffled.end()); + pri_queue q; + BOOST_REQUIRE(q.reverse_ordered_begin() == q.reverse_ordered_end()); + fill_q(q, shuffled); + + test_data data_from_queue(q.reverse_ordered_begin(), q.reverse_ordered_end()); + BOOST_REQUIRE(data == data_from_queue); + + for (unsigned long j = 0; j != data.size(); ++j) + BOOST_REQUIRE(std::find(q.reverse_ordered_begin(), q.reverse_ordered_end(), data[j]) != q.reverse_ordered_end()); + + for (unsigned long j = 0; j != data.size(); ++j) + BOOST_REQUIRE(std::find(q.reverse_ordered_begin(), q.reverse_ordered_end(), data[j] + data.size()) == q.reverse_ordered_end()); + + for (unsigned long j = 0; j != data.size(); ++j) { + BOOST_REQUIRE_EQUAL((long)std::distance(q.begin(), q.end()), (long)(data.size() - j)); + q.pop(); + } + } +} template void pri_queue_test_equality(void) diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index 3511a4e..074f986 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -237,6 +237,12 @@ struct dispatcher_queue : boost::heap::detail::min_max_heap void run_min_max_heap_iterator_dispatcher_upward_test() { @@ -248,7 +254,7 @@ void run_min_max_heap_iterator_dispatcher_upward_test() boost::parameter::void_, boost::parameter::void_>::type signature; typedef dispatcher_queue pri_queue; - typedef typename pri_queue::iterator_dispatcher iterator_dispatcher; + typedef typename pri_queue::template iterator_dispatcher iterator_dispatcher; pri_queue q; std::pair regular; @@ -291,7 +297,7 @@ BOOST_AUTO_TEST_CASE( min_max_heap_iterator_dispatcher_test ) boost::parameter::void_, boost::parameter::void_>::type signature; typedef dispatcher_queue pri_queue; - typedef typename pri_queue::iterator_dispatcher iterator_dispatcher; + typedef typename pri_queue::template iterator_dispatcher iterator_dispatcher; pri_queue q; std::pair regular; @@ -351,6 +357,8 @@ void run_min_max_heap_test(void) run_merge_tests(); run_ordered_iterator_tests(); + run_reverse_ordered_iterator_tests(); + #if 0 if (stable) { typedef boost::heap::min_max_heap, diff --git a/test/mutable_heap_tests.hpp b/test/mutable_heap_tests.hpp index 3df1d30..a56fe3a 100644 --- a/test/mutable_heap_tests.hpp +++ b/test/mutable_heap_tests.hpp @@ -323,3 +323,9 @@ void run_ordered_iterator_tests() { pri_queue_test_ordered_iterators(); } + +template +void run_reverse_ordered_iterator_tests() +{ + pri_queue_test_reverse_ordered_iterators(); +} From a64fa905c32dd3b325ddd2a0b35c0ad626900bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Mon, 22 Jul 2019 18:01:19 +0800 Subject: [PATCH 05/13] min-max heap: stability --- test/min_max_heap_test.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index 074f986..7626e28 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -359,7 +359,6 @@ void run_min_max_heap_test(void) run_ordered_iterator_tests(); run_reverse_ordered_iterator_tests(); -#if 0 if (stable) { typedef boost::heap::min_max_heap, boost::heap::stable @@ -367,7 +366,7 @@ void run_min_max_heap_test(void) run_stable_heap_tests(); } -#endif + #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) cmpthings ord; boost::heap::min_max_heap, boost::heap::compare, boost::heap::stable > vpq(ord); @@ -382,3 +381,11 @@ BOOST_AUTO_TEST_CASE( min_max_heap_test ) run_min_max_heap_test<4, false>(); run_min_max_heap_test<5, false>(); } + +BOOST_AUTO_TEST_CASE( min_max_heap_stable_test ) +{ + run_min_max_heap_test<2, true>(); + run_min_max_heap_test<3, true>(); + run_min_max_heap_test<4, true>(); + run_min_max_heap_test<5, true>(); +} From 38e635fbf98bd21be96a021f74a02aaff98f7951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Wed, 24 Jul 2019 08:48:00 +0800 Subject: [PATCH 06/13] min-max heap: mutability --- include/boost/heap/detail/mutable_heap.hpp | 352 +++++++++++++++++++-- include/boost/heap/min_max_heap.hpp | 107 +++---- test/min_max_heap_test.cpp | 47 +++ 3 files changed, 413 insertions(+), 93 deletions(-) diff --git a/include/boost/heap/detail/mutable_heap.hpp b/include/boost/heap/detail/mutable_heap.hpp index 13d7322..97d7718 100644 --- a/include/boost/heap/detail/mutable_heap.hpp +++ b/include/boost/heap/detail/mutable_heap.hpp @@ -51,7 +51,7 @@ class priority_queue_mutable_wrapper typedef typename PriorityQueueType::const_pointer const_pointer; static const bool is_stable = PriorityQueueType::is_stable; -private: +protected: typedef std::pair node_type; #ifdef BOOST_NO_CXX11_ALLOCATOR @@ -122,7 +122,7 @@ class priority_queue_mutable_wrapper friend class priority_queue_mutable_wrapper; }; -private: +protected: struct indirect_cmp: public value_compare { @@ -225,52 +225,61 @@ class priority_queue_mutable_wrapper typedef typename object_list::difference_type difference_type; - class ordered_iterator: - public boost::iterator_adaptor + class ordered_iterator_base: + public boost::iterator_adaptor, - q_type::ordered_iterator_dispatcher + >, + public Dispatcher { - typedef boost::iterator_adaptor adaptor_type; typedef const_list_iterator iterator; - typedef typename q_type::ordered_iterator_dispatcher ordered_iterator_dispatcher; + typedef Dispatcher iterator_dispatcher; friend class boost::iterator_core_access; public: - ordered_iterator(void): - adaptor_type(0), unvisited_nodes(indirect_cmp()), q_(NULL) + ordered_iterator_base(void): + adaptor_type(0), unvisited_nodes(IndirectCmp()), q_(NULL) {} - ordered_iterator(const priority_queue_mutable_wrapper * q, indirect_cmp const & cmp): + ordered_iterator_base(const Wrapper * q, IndirectCmp const & cmp): adaptor_type(0), unvisited_nodes(cmp), q_(q) {} - ordered_iterator(const_list_iterator it, const priority_queue_mutable_wrapper * q, indirect_cmp const & cmp): + ordered_iterator_base(const_list_iterator it, const Wrapper * q, IndirectCmp const & cmp): adaptor_type(it), unvisited_nodes(cmp), q_(q) { if (it != q->objects.end()) - discover_nodes(it); + static_cast(this)->discover_nodes(it); + } + + ordered_iterator_base(const_list_iterator it, const Wrapper * q, IndirectCmp const & cmp, Dispatcher const & dispatcher): + adaptor_type(it), Dispatcher(dispatcher), unvisited_nodes(cmp), q_(q) + { + if (it != q->objects.end()) + static_cast(this)->discover_nodes(it); } - bool operator!=(ordered_iterator const & rhs) const + bool operator!=(ordered_iterator_base const & rhs) const { return adaptor_type::base() != rhs.base(); } - bool operator==(ordered_iterator const & rhs) const + bool operator==(ordered_iterator_base const & rhs) const { return !operator!=(rhs); } - private: + protected: void increment(void) { if (unvisited_nodes.empty()) @@ -278,7 +287,7 @@ class priority_queue_mutable_wrapper else { iterator next = unvisited_nodes.top(); unvisited_nodes.pop(); - discover_nodes(next); + static_cast(this)->discover_nodes(next); adaptor_type::base_reference() = next; } } @@ -287,34 +296,60 @@ class priority_queue_mutable_wrapper { return adaptor_type::base()->first; } + + std::priority_queue::other >, +#else + std::vector::template rebind_alloc >, +#endif + IndirectCmp + > unvisited_nodes; + const Wrapper * q_; + }; - void discover_nodes(iterator current) + class ordered_iterator : public ordered_iterator_base + { + typedef ordered_iterator_base super_t; + + public: + ordered_iterator(void): + super_t() + {} + + ordered_iterator(const priority_queue_mutable_wrapper * q, indirect_cmp const & cmp): + super_t(q, cmp) + {} + + ordered_iterator(const_list_iterator it, const priority_queue_mutable_wrapper * q, indirect_cmp const & cmp): + super_t(it, q, cmp) + {} + + void discover_nodes(typename super_t::iterator current) { size_type current_index = current->second; - const q_type * q = &(q_->q_); + const q_type * q = &(super_t::q_->q_); - if (ordered_iterator_dispatcher::is_leaf(q, current_index)) + if (super_t::iterator_dispatcher::is_leaf(q, current_index)) return; - std::pair child_range = ordered_iterator_dispatcher::get_child_nodes(q, current_index); + std::pair child_range = super_t::iterator_dispatcher::get_child_nodes(q, current_index); for (size_type i = child_range.first; i <= child_range.second; ++i) { - typename q_type::internal_type const & internal_value_at_index = ordered_iterator_dispatcher::get_internal_value(q, i); - typename q_type::value_type const & value_at_index = q_->q_.get_value(internal_value_at_index); + typename q_type::internal_type const & internal_value_at_index = super_t::iterator_dispatcher::get_internal_value(q, i); + typename q_type::value_type const & value_at_index = super_t::q_->q_.get_value(internal_value_at_index); - unvisited_nodes.push(value_at_index); + super_t::unvisited_nodes.push(value_at_index); } } - - std::priority_queue::other >, -#else - std::vector::template rebind_alloc >, -#endif - indirect_cmp - > unvisited_nodes; - const priority_queue_mutable_wrapper * q_; }; bool empty(void) const @@ -531,6 +566,255 @@ class priority_queue_mutable_wrapper } }; +template +class double_ended_priority_queue_mutable_wrapper: + public priority_queue_mutable_wrapper +{ + typedef priority_queue_mutable_wrapper super_t; + + struct indirect_reverse_cmp: + public super_t::value_compare + { + indirect_reverse_cmp(typename super_t::value_compare const & cmp = super_t::value_compare()): + super_t::value_compare(cmp) + {} + + bool operator()(typename super_t::const_list_iterator const & lhs, typename super_t::const_list_iterator const & rhs) const + { + return super_t::value_compare::operator()(rhs->first, lhs->first); + } + }; + +public: + double_ended_priority_queue_mutable_wrapper(typename super_t::value_compare const & cmp = super_t::value_compare()): + super_t(cmp) + {} + + double_ended_priority_queue_mutable_wrapper(double_ended_priority_queue_mutable_wrapper const & rhs): + super_t(rhs) + {} + + double_ended_priority_queue_mutable_wrapper & operator=(double_ended_priority_queue_mutable_wrapper const & rhs) + { + super_t::operator=(rhs); + return *this; + } + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + double_ended_priority_queue_mutable_wrapper (double_ended_priority_queue_mutable_wrapper && rhs): + super_t(std::move(rhs)) + {} + + double_ended_priority_queue_mutable_wrapper & operator=(double_ended_priority_queue_mutable_wrapper && rhs) + { + super_t::operator=(std::move(rhs)); + return *this; + } +#endif + +public: + typename super_t::const_reference min(void) const + { + BOOST_ASSERT(!super_t::empty()); + return super_t::q_.min()->first; + } + + typename super_t::const_reference max(void) const + { + BOOST_ASSERT(!super_t::empty()); + return super_t::q_.max()->first; + } + + void pop_min(void) + { + BOOST_ASSERT(!super_t::empty()); + typename super_t::list_iterator q_top = super_t::q_.min(); + super_t::q_.pop_min(); + super_t::objects.erase(q_top); + } + + void pop_max(void) + { + BOOST_ASSERT(!super_t::empty()); + typename super_t::list_iterator q_top = super_t::q_.max(); + super_t::q_.pop_max(); + super_t::objects.erase(q_top); + } + +public: + template + class ordered_iterator_base : + public super_t::template ordered_iterator_base + { + typedef typename super_t::template ordered_iterator_base iterator_super_t; + + public: + ordered_iterator_base(void): + iterator_super_t() + {} + + ordered_iterator_base(const double_ended_priority_queue_mutable_wrapper * q, IndirectCmp const & cmp): + iterator_super_t(q, cmp) + {} + + ordered_iterator_base(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp): + iterator_super_t(it, q, cmp) + {} + + ordered_iterator_base(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename iterator_super_t::iterator_dispatcher const & dispatcher): + iterator_super_t(it, q, cmp, dispatcher) + {} + + void discover_nodes(typename iterator_super_t::iterator current) + { + typedef typename super_t::size_type size_type; + typedef typename super_t::q_type q_type; + + size_type current_index = current->second; + const q_type * q = &(iterator_super_t::q_->q_); + + if (iterator_super_t::iterator_dispatcher::is_leaf(q, current_index)) + return; + + std::pair extra_child_range = std::make_pair(1, 0); + std::pair child_range = iterator_super_t::iterator_dispatcher::get_child_nodes(q, current_index, extra_child_range); + + for (size_type i = extra_child_range.first; i <= extra_child_range.second; ++i) { + typename q_type::internal_type const & internal_value_at_index = iterator_super_t::iterator_dispatcher::get_internal_value(q, i); + typename q_type::value_type const & value_at_index = iterator_super_t::q_->q_.get_value(internal_value_at_index); + + iterator_super_t::unvisited_nodes.push(value_at_index); + } + + for (size_type i = child_range.first; i <= child_range.second; ++i) { + typename q_type::internal_type const & internal_value_at_index = iterator_super_t::iterator_dispatcher::get_internal_value(q, i); + typename q_type::value_type const & value_at_index = iterator_super_t::q_->q_.get_value(internal_value_at_index); + + iterator_super_t::unvisited_nodes.push(value_at_index); + } + } + }; + +public: + class ordered_iterator : public ordered_iterator_base + { + typedef ordered_iterator_base + iterator_base_super_t; + + public: + ordered_iterator(void): + iterator_base_super_t() + {} + + ordered_iterator(const double_ended_priority_queue_mutable_wrapper * q, typename super_t::indirect_cmp const & cmp): + iterator_base_super_t(q, cmp) + {} + + ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, typename super_t::indirect_cmp const & cmp): + iterator_base_super_t(it, q, cmp) + {} + + ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, typename super_t::indirect_cmp const & cmp, typename iterator_base_super_t::iterator_dispatcher const & dispatcher): + iterator_base_super_t(it, q, cmp, dispatcher) + {} + }; + + ordered_iterator ordered_begin(void) const + { + if (!super_t::empty()) + return ordered_iterator(super_t::q_.min(), this, typename super_t::indirect_cmp(super_t::q_.value_comp()), typename super_t::q_type::ordered_iterator_dispatcher(super_t::q_.size())); + else + return ordered_end(); + } + + ordered_iterator ordered_end(void) const + { + return ordered_iterator(super_t::objects.end(), this, typename super_t::indirect_cmp(super_t::q_.value_comp()), typename super_t::q_type::ordered_iterator_dispatcher(0)); + } + +public: + class reverse_ordered_iterator : public ordered_iterator_base + { + typedef ordered_iterator_base + iterator_base_super_t; + + public: + reverse_ordered_iterator(void): + iterator_base_super_t() + {} + + reverse_ordered_iterator(const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp): + iterator_base_super_t(q, cmp) + {} + + reverse_ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp): + iterator_base_super_t(it, q, cmp) + {} + + reverse_ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename iterator_base_super_t::iterator_dispatcher const & dispatcher): + iterator_base_super_t(it, q, cmp, dispatcher) + {} + + reverse_ordered_iterator(typename super_t::const_list_iterator it, std::pair initial_indexes, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename iterator_base_super_t::iterator_dispatcher const & dispatcher): + iterator_base_super_t(it, q, cmp, dispatcher) + { + const typename super_t::q_type * q_ = &(iterator_base_super_t::q_->q_); + + for (size_t i = initial_indexes.first; i <= initial_indexes.second; ++i) + if (i != it->second) { + typename super_t::q_type::internal_type const & internal_value_at_index = iterator_base_super_t::iterator_dispatcher::get_internal_value(q_, i); + typename super_t::q_type::value_type const & value_at_index = iterator_base_super_t::q_->q_.get_value(internal_value_at_index); + + iterator_base_super_t::unvisited_nodes.push(value_at_index); + } + } + }; + + reverse_ordered_iterator reverse_ordered_begin(void) const + { + if (!super_t::empty()) { + if (1 < super_t::size()) { + typename super_t::const_list_iterator it = super_t::q_.max(); + std::pair initial_indexes; + initial_indexes.first = 1; + initial_indexes.second = std::min(PriorityQueueType::D, super_t::size()); + + return reverse_ordered_iterator(it, initial_indexes, this, indirect_reverse_cmp(super_t::q_.value_comp()), typename super_t::q_type::reverse_ordered_iterator_dispatcher(super_t::q_.size())); + } + else + return reverse_ordered_iterator(super_t::q_.max(), this, indirect_reverse_cmp(super_t::q_.value_comp()), typename super_t::q_type::reverse_ordered_iterator_dispatcher(super_t::q_.size())); + } + else + return reverse_ordered_end(); + } + + reverse_ordered_iterator reverse_ordered_end(void) const + { + return reverse_ordered_iterator(super_t::objects.end(), this, indirect_reverse_cmp(super_t::q_.value_comp()), typename super_t::q_type::reverse_ordered_iterator_dispatcher(super_t::q_.size())); + } +}; } /* namespace detail */ } /* namespace heap */ diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index b75da7a..c76f3c6 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -89,8 +89,6 @@ class min_max_heap: typedef typename heap_base_maker::type super_t; typedef typename super_t::internal_type internal_type; - typedef IndexUpdater index_updater; - #ifdef BOOST_NO_CXX11_ALLOCATOR typedef typename heap_base_maker::allocator_argument::template rebind::other internal_type_allocator; #else @@ -98,6 +96,8 @@ class min_max_heap: #endif typedef std::vector container_type; + typedef IndexUpdater index_updater; + container_type q_; static const unsigned int D = parameter::binding >::type::value; @@ -175,7 +175,7 @@ class min_max_heap: return super_t::value_comp(); } - template + template bool compare(size_type i, size_type j) const { BOOST_ASSERT(i < this->size()); @@ -332,12 +332,12 @@ class min_max_heap: /* indexes */ /* moves */ - void trickle_down(size_type index) + void trickle_down(size_type i) { - if (is_on_compare_level(index)) - trickle_down_impl(index); + if (is_on_compare_level(i)) + trickle_down_impl(i); else - trickle_down_impl(index); + trickle_down_impl(i); } template @@ -450,6 +450,11 @@ class min_max_heap: } #endif + value_type const & top(void) const + { + return min(); + } + value_type const & min(void) const { BOOST_ASSERT(!empty()); @@ -464,6 +469,11 @@ class min_max_heap: return super_t::get_value(q_[index_of_max()]); } + void pop(void) + { + pop_min(); + } + void pop_min(void) { erase(index_of_min()); @@ -475,66 +485,45 @@ class min_max_heap: } public: + template + struct rebind { + typedef min_max_heap, + boost::heap::stability_counter_type, + boost::heap::arity, + boost::heap::compare, + boost::heap::allocator + >::type, + X + > other; + }; + + template friend class priority_queue_mutable_wrapper; + template friend class double_ended_priority_queue_mutable_wrapper; + void update(size_type index) { - if(index == root()) - { - return trickle_down(index); - } + if (index == root()) + return trickle_down(index); - size_type parent = parent(index); - - if(compare(parent, index)) - { - return decrease(index); - } - - size_type grandparent = grandparent(index); - - if(grandparent < last() - && compare(index, grandparent)) - { - return increase(index); - } + size_type parent = this->parent(index); + + if (compare(parent, index)) + decrease(index); + else + increase(index); } void increase(size_type index) { - if(index == root() - || !is_on_compare_level(index)) - { - trickle_down(index); - } - else - { - size_type parent = parent(index); - - if(compare(parent, index)) - { - trickle_down(index); - } - - bubble_up(index); - } + bubble_up(index); } void decrease(size_type index) { - if(is_on_compare_level(index)) - { - bubble_up(index); - } - else - { - size_type parent = parent(index); - - if(compare(parent, index)) - { - bubble_up(index); - } - - trickle_down(index); - } + trickle_down(index); } void erase(size_type index) @@ -545,8 +534,8 @@ class min_max_heap: swap(index, last()); q_.pop_back(); - if(!empty() && index != size()) - { + if (!empty() && index != size()) { + bubble_up(index); trickle_down(index); } } @@ -1039,7 +1028,7 @@ struct select_minmax_heap static const bool is_mutable = extract_mutable::value; typedef typename mpl::if_c >, + double_ended_priority_queue_mutable_wrapper >, min_max_heap >::type type; }; diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index 7626e28..0febde0 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -389,3 +389,50 @@ BOOST_AUTO_TEST_CASE( min_max_heap_stable_test ) run_min_max_heap_test<4, true>(); run_min_max_heap_test<5, true>(); } + +template +void run_min_max_heap_mutable_test(void) +{ + typedef boost::heap::min_max_heap, + boost::heap::arity, + boost::heap::stable + > pri_queue; + + BOOST_CONCEPT_ASSERT((boost::heap::MutablePriorityQueue)); + + run_common_heap_tests(); + run_moveable_heap_tests(); + run_reserve_heap_tests(); + run_mutable_heap_tests(); + + run_merge_tests(); + + run_ordered_iterator_tests(); + run_reverse_ordered_iterator_tests(); + + if (stable) { + typedef boost::heap::min_max_heap, + boost::heap::arity, + boost::heap::stable + > stable_pri_queue; + run_stable_heap_tests(); + } +} + +BOOST_AUTO_TEST_CASE( min_max_heap_mutable_test ) +{ + run_min_max_heap_mutable_test<2, false>(); + run_min_max_heap_mutable_test<3, false>(); + run_min_max_heap_mutable_test<4, false>(); + run_min_max_heap_mutable_test<5, false>(); +} + +BOOST_AUTO_TEST_CASE( min_max_heap_mutable_stable_test ) +{ + run_min_max_heap_mutable_test<2, true>(); + run_min_max_heap_mutable_test<3, true>(); + run_min_max_heap_mutable_test<4, true>(); + run_min_max_heap_mutable_test<5, true>(); +} + From 84b34af93f77dea4904b5087d8a340955106f3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Wed, 24 Jul 2019 10:46:34 +0800 Subject: [PATCH 07/13] min-max heap: documentation --- doc/heap.qbk | 9 ++++++- include/boost/heap/min_max_heap.hpp | 39 ++++++++++++++--------------- test/min_max_heap_test.cpp | 14 ++++++++++- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/doc/heap.qbk b/doc/heap.qbk index 827aeda..7354268 100644 --- a/doc/heap.qbk +++ b/doc/heap.qbk @@ -342,6 +342,12 @@ _heap_ provides the following data structures: constraints for the tree structure, all heap operations can be performed in O(log n). ] ] + + [[[classref boost::heap::min_max_heap]] + [ + [@https://en.wikipedia.org/wiki/Min-max_heap Min-max heaps] are a combination of a min heap and max heap in the same tree structure that allow to retrieve both the maximum and the minimum in constant time. + ] + ] ] [table Comparison of amortized complexity @@ -355,6 +361,7 @@ _heap_ provides the following data structures: [[[classref boost::heap::pairing_heap]] [[^O(1)]] [O(2**2*log(log(N)))] [O(log(N))] [O(2**2*log(log(N)))] [O(2**2*log(log(N)))] [O(2**2*log(log(N)))] [O(2**2*log(log(N)))] [O(2**2*log(log(N)))]] [[[classref boost::heap::skew_heap]] [[^O(1)]] [O(log(N))] [O(log(N))] [O(log(N))] [O(log(N))] [O(log(N))] [O(log(N))] [O(log(N+M))]] + [[[classref boost::heap::min_max_heap]] [[^O(1)]] [O(log(N))] [O(log(N))] [O(log(N))] [O(log(N))] [O(log(N))] [O(log(N))] [O((N+M)*log(N+M))]] ] @@ -392,7 +399,7 @@ The data structures can be configured with [@boost:/libs/parameter/doc/html/inde ] [[[classref boost::heap::arity]] - [Specifies the arity of a d-ary heap. For details, please consult the class reference of [classref boost::heap::d_ary_heap]] + [Specifies the arity of a d-ary or a min-max heap. For details, please consult the class reference of [classref boost::heap::d_ary_heap] and [classref boost::heap::min_max_heap].] ] [[[classref boost::heap::store_parent_pointer]] diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index c76f3c6..5fab02e 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -322,12 +322,12 @@ class min_max_heap: const std::pair grandchildren = this->grandchildren(index); - size_type best_child = children.first; + size_type best = children.first; - best_between(best_child, children.first + 1, children.second); - is_grandchild = best_between(best_child, grandchildren.first, grandchildren.second); + best_between(best, children.first + 1, children.second); + is_grandchild = best_between(best, grandchildren.first, grandchildren.second); - return best_child; + return best; } /* indexes */ @@ -359,9 +359,8 @@ class min_max_heap: trickle_down_impl(m); } } - else - if (compare(m, i)) - swap(i, m); + else if (compare(m, i)) + swap(i, m); } } @@ -393,8 +392,7 @@ class min_max_heap: { size_type grandparent = this->grandparent(i); - if(grandparent != npos() - && compare(i, grandparent)) { + if (grandparent != npos() && compare(i, grandparent)) { swap(i, grandparent); bubble_up_impl_(grandparent); } @@ -591,7 +589,8 @@ class min_max_heap: * The second part consists of odd levels and is a bit more complicated * to explore. The search has to go back up in the tree from the leaves * but since a node on an odd level is being pointed to by D^2 grandchildren - * it cannot be visited until all of its heirs have been visited. + * in the Hasse diagram, it cannot be visited until all of its heirs have + * previously been visited. * * The 'min_max_ordered_iterator_status' structure is used to keep track * of this. It indicates for each node on an odd level whether its heirs @@ -601,7 +600,7 @@ class min_max_heap: * reused to indicate to its own grandfather that it has been visited while * its siblings may not have yet been visited. * - * This method requires $O(log_D((D - 1) * (size() - 1) + 1))$ bytes. + * This method requires $O(\frac{(D - 1) * (size() - 1) + 1}{D})$ bytes. * * The following specific cases must be addressed (D = 3): * 0 1) when 0 is visited, it must add nodes 4-8 to the list @@ -611,8 +610,8 @@ class min_max_heap: * / | \ larger tree where the example would be a subtree. * / | \ 2) when 8 is visited, it must set its indicator and * 1 2 3 set the indicator for the non existing node 9 (in - * /|\ /| general for all its right siblings) and then add 2 - * 4 5 6 7 8 if 7 has already been visited. + * /|\ /| general for all its right non existing siblings) and + * 4 5 6 7 8 then add 2 if 7 has already been visited. */ const size_type last = heap->last(); const bool on_compare_level = !(Forward ^ heap->is_on_compare_level(index)); @@ -1039,7 +1038,7 @@ struct select_minmax_heap * \class min_max_heap * \brief min-max heap class * - * This class implements an immutable priority queue. Internally, the min-max heap is representated + * This class implements an double-ended priority queue. Internally, the min-max heap is represented * as a dynamically sized array (std::vector), that directly stores the values. * * The template parameter T is the type to be managed by the container. @@ -1194,9 +1193,9 @@ class min_max_heap: } /** - * \b Effects: Returns a const_reference to the minimum element. + * \b Effects: Returns a const_reference to the min element. * - * \b Complexity: + * \b Complexity: Constant. * * */ value_type const & min(void) const @@ -1205,9 +1204,9 @@ class min_max_heap: } /** - * \b Effects: Returns a const_reference to the maximum element. + * \b Effects: Returns a const_reference to the max element. * - * \b Complexity: + * \b Complexity: Constant. * * */ value_type const & max(void) const @@ -1395,7 +1394,7 @@ class min_max_heap: /** * \b Effects: Removes the element with the highest priority from the priority queue. * - * \b Complexity: + * \b Complexity: Logarithmic. * * \b Note: Same as pop * */ @@ -1407,7 +1406,7 @@ class min_max_heap: /** * \b Effects: Removes the element with the lowest priority from the priority queue. * - * \b Complexity: + * \b Complexity: Logarithmic. * * */ void pop_max(void) diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index 0febde0..3a866df 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -19,7 +19,6 @@ #include "mutable_heap_tests.hpp" #include "merge_heap_tests.hpp" -#include #include #include @@ -436,3 +435,16 @@ BOOST_AUTO_TEST_CASE( min_max_heap_mutable_stable_test ) run_min_max_heap_mutable_test<5, true>(); } +BOOST_AUTO_TEST_CASE( min_max_heap_compare_lookup_test ) +{ + typedef boost::heap::min_max_heap, + boost::heap::compare, + boost::heap::allocator > > pri_queue; + run_common_heap_tests(); +} + +BOOST_AUTO_TEST_CASE( min_max_heap_leak_test ) +{ + typedef boost::heap::min_max_heap, boost::heap::arity<2> > pri_queue; + run_leak_check_test(); +} From 104bb7f160589305457acc84bf387c44d60657db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Wed, 24 Jul 2019 14:43:42 +0800 Subject: [PATCH 08/13] min-max heap: make max-min heap the default --- include/boost/heap/detail/mutable_heap.hpp | 14 +-- include/boost/heap/min_max_heap.hpp | 132 ++++++++++++++------- test/min_max_heap_test.cpp | 5 +- 3 files changed, 98 insertions(+), 53 deletions(-) diff --git a/include/boost/heap/detail/mutable_heap.hpp b/include/boost/heap/detail/mutable_heap.hpp index 97d7718..4b69c28 100644 --- a/include/boost/heap/detail/mutable_heap.hpp +++ b/include/boost/heap/detail/mutable_heap.hpp @@ -628,17 +628,17 @@ class double_ended_priority_queue_mutable_wrapper: void pop_min(void) { BOOST_ASSERT(!super_t::empty()); - typename super_t::list_iterator q_top = super_t::q_.min(); + typename super_t::list_iterator q_min = super_t::q_.min(); super_t::q_.pop_min(); - super_t::objects.erase(q_top); + super_t::objects.erase(q_min); } void pop_max(void) { BOOST_ASSERT(!super_t::empty()); - typename super_t::list_iterator q_top = super_t::q_.max(); + typename super_t::list_iterator q_max = super_t::q_.max(); super_t::q_.pop_max(); - super_t::objects.erase(q_top); + super_t::objects.erase(q_max); } public: @@ -739,7 +739,7 @@ class double_ended_priority_queue_mutable_wrapper: ordered_iterator ordered_begin(void) const { if (!super_t::empty()) - return ordered_iterator(super_t::q_.min(), this, typename super_t::indirect_cmp(super_t::q_.value_comp()), typename super_t::q_type::ordered_iterator_dispatcher(super_t::q_.size())); + return ordered_iterator(super_t::q_.max(), this, typename super_t::indirect_cmp(super_t::q_.value_comp()), typename super_t::q_type::ordered_iterator_dispatcher(super_t::q_.size())); else return ordered_end(); } @@ -796,7 +796,7 @@ class double_ended_priority_queue_mutable_wrapper: { if (!super_t::empty()) { if (1 < super_t::size()) { - typename super_t::const_list_iterator it = super_t::q_.max(); + const typename super_t::const_list_iterator it = super_t::q_.min(); std::pair initial_indexes; initial_indexes.first = 1; initial_indexes.second = std::min(PriorityQueueType::D, super_t::size()); @@ -804,7 +804,7 @@ class double_ended_priority_queue_mutable_wrapper: return reverse_ordered_iterator(it, initial_indexes, this, indirect_reverse_cmp(super_t::q_.value_comp()), typename super_t::q_type::reverse_ordered_iterator_dispatcher(super_t::q_.size())); } else - return reverse_ordered_iterator(super_t::q_.max(), this, indirect_reverse_cmp(super_t::q_.value_comp()), typename super_t::q_type::reverse_ordered_iterator_dispatcher(super_t::q_.size())); + return reverse_ordered_iterator(super_t::q_.min(), this, indirect_reverse_cmp(super_t::q_.value_comp()), typename super_t::q_type::reverse_ordered_iterator_dispatcher(super_t::q_.size())); } else return reverse_ordered_end(); diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index 5fab02e..b715143 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -180,9 +180,8 @@ class min_max_heap: { BOOST_ASSERT(i < this->size()); BOOST_ASSERT(j < this->size()); - // use > or std::greater for min-max heap - // use < or std::less for a max-min heap - if (!Regular) + + if (Regular) return super_t::operator () (q_[i], q_[j]); else return super_t::operator () (q_[j], q_[i]); @@ -246,7 +245,7 @@ class min_max_heap: std::pair children(size_type index) const { - size_type child = first_child(index); + const size_type child = first_child(index); return std::make_pair(child, child + D - 1); } @@ -262,7 +261,7 @@ class min_max_heap: std::pair grandchildren(size_type index) const { - size_type grandchild = first_grandchild(index); + const size_type grandchild = first_grandchild(index); return std::make_pair(grandchild, grandchild + D * D - 1); } @@ -335,23 +334,23 @@ class min_max_heap: void trickle_down(size_type i) { if (is_on_compare_level(i)) - trickle_down_impl(i); - else trickle_down_impl(i); + else + trickle_down_impl(i); } template void trickle_down_impl(size_type i) { bool is_grandchild; - size_type m = best_child_or_grandchild(i, is_grandchild); + const size_type m = best_child_or_grandchild(i, is_grandchild); if (m < npos()) { if (is_grandchild) { if (compare(m, i)) { swap(i, m); - size_type parent = this->parent(m); + const size_type parent = this->parent(m); if (compare(parent, m)) swap(m, parent); @@ -360,22 +359,22 @@ class min_max_heap: } } else if (compare(m, i)) - swap(i, m); + swap(i, m); } } void bubble_up(size_type i) { if (is_on_compare_level(i)) - bubble_up_impl(i); - else bubble_up_impl(i); + else + bubble_up_impl(i); } template void bubble_up_impl(size_type i) { - size_type parent = this->parent(i); + const size_type parent = this->parent(i); if (parent != npos()) { if (compare(parent, i)) { @@ -390,7 +389,7 @@ class min_max_heap: template void bubble_up_impl_(size_type i) { - size_type grandparent = this->grandparent(i); + const size_type grandparent = this->grandparent(i); if (grandparent != npos() && compare(i, grandparent)) { swap(i, grandparent); @@ -412,16 +411,16 @@ class min_max_heap: } protected: - size_type index_of_min(void) const + size_type index_of_max(void) const { return root(); } - size_type index_of_max(void) const + size_type index_of_min(void) const { size_type best = root(); - best_between(best, 1, D); + best_between(best, 1, D); return best; } @@ -432,7 +431,7 @@ class min_max_heap: { q_.push_back(super_t::make_node(v)); - size_type index = last(); + const size_type index = last(); reset_index(index, index); bubble_up(index); @@ -450,7 +449,7 @@ class min_max_heap: value_type const & top(void) const { - return min(); + return max(); } value_type const & min(void) const @@ -469,7 +468,7 @@ class min_max_heap: void pop(void) { - pop_min(); + pop_max(); } void pop_min(void) @@ -508,7 +507,7 @@ class min_max_heap: size_type parent = this->parent(index); - if (compare(parent, index)) + if (compare(index, parent)) decrease(index); else increase(index); @@ -590,7 +589,7 @@ class min_max_heap: * to explore. The search has to go back up in the tree from the leaves * but since a node on an odd level is being pointed to by D^2 grandchildren * in the Hasse diagram, it cannot be visited until all of its heirs have - * previously been visited. + * previously been visited. * * The 'min_max_ordered_iterator_status' structure is used to keep track * of this. It indicates for each node on an odd level whether its heirs @@ -640,10 +639,10 @@ class min_max_heap: }// else is leaf or not on compare level status.set(index); - size_type parent = heap->parent(index); + const size_type parent = heap->parent(index); if (BOOST_LIKELY(parent != heap->npos())) { - size_type rightmost_sibling = heap->last_child(parent); + const size_type rightmost_sibling = heap->last_child(parent); // if (last < rightmost_sibling) for(size_type i = last + 1; i <= rightmost_sibling; ++i) @@ -656,7 +655,7 @@ class min_max_heap: } } else { - size_type grandparent = heap->parent(parent); + const size_type grandparent = heap->parent(parent); if (BOOST_LIKELY(grandparent != heap->npos()) && status.is_complete(grandparent)) { @@ -738,7 +737,8 @@ class min_max_heap: return super_t::internal_compare::operator () (rhs, lhs); } }; - + +public: typedef detail::extended_ordered_adaptor_iterator initial_indexes = std::make_pair(1, 0); if (1 < size()) { - max_index = index_of_max(); - // The initial_indexes range will contain max_index and this is fine + index_of_min = this->index_of_min(); + // The initial_indexes range will contain index_of_min and this is fine // as long as the adaptor is storing upcoming indexes in a set. initial_indexes.first = 1; initial_indexes.second = std::min(D, last()); } - return reverse_ordered_iterator(max_index, initial_indexes, this, reverse_internal_compare(super_t::get_internal_cmp()), reverse_ordered_iterator_dispatcher(size())); + return reverse_ordered_iterator(index_of_min, initial_indexes, this, reverse_internal_compare(super_t::get_internal_cmp()), reverse_ordered_iterator_dispatcher(size())); } reverse_ordered_iterator reverse_ordered_end(void) const @@ -1038,7 +1038,7 @@ struct select_minmax_heap * \class min_max_heap * \brief min-max heap class * - * This class implements an double-ended priority queue. Internally, the min-max heap is represented + * This class implements a double-ended priority queue. Internally, the min-max heap is represented * as a dynamically sized array (std::vector), that directly stores the values. * * The template parameter T is the type to be managed by the container. @@ -1189,9 +1189,20 @@ class min_max_heap: /// \copydoc boost::heap::priority_queue::top value_type const & top(void) const { - return super_t::min(); + return super_t::max(); } + /** + * \b Effects: Returns a const_reference to the max element. + * + * \b Complexity: Constant. + * + * */ + value_type const & max(void) const + { + return super_t::max(); + } + /** * \b Effects: Returns a const_reference to the min element. * @@ -1209,19 +1220,40 @@ class min_max_heap: * \b Complexity: Constant. * * */ - value_type const & max(void) const + value_type const & best(void) const { return super_t::max(); } - /// \copydoc boost::heap::priority_queue::push + /** + * \b Effects: Returns a const_reference to the min element. + * + * \b Complexity: Constant. + * + * */ + value_type const & worst(void) const + { + return super_t::min(); + } + + /** + * \b Effects: Adds a new element to the priority queue. Returns handle to element + * + * \b Complexity: Logarithmic. + * + * */ typename boost::conditional::type push(value_type const & v) { return super_t::push(v); } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - /// \copydoc boost::heap::priority_queue::emplace + /** + * \b Effects: Adds a new element to the priority queue. The element is directly constructed in-place. Returns handle to element. + * + * \b Complexity: Logarithmic. + * + * */ template typename boost::conditional::type emplace(Args&&... args) { @@ -1385,10 +1417,15 @@ class min_max_heap: return super_t::s_handle_from_iterator(it); } - /// \copydoc boost::heap::priority_queue::pop + /** + * \b Effects: Removes the top element from the priority queue. + * + * \b Complexity: Logarithmic. + * + * */ void pop(void) { - super_t::pop_min(); + super_t::pop_max(); } /** @@ -1396,22 +1433,21 @@ class min_max_heap: * * \b Complexity: Logarithmic. * - * \b Note: Same as pop * */ - void pop_min(void) + void pop_max(void) { - super_t::pop_min(); + super_t::pop_max(); } - + /** * \b Effects: Removes the element with the lowest priority from the priority queue. * * \b Complexity: Logarithmic. * * */ - void pop_max(void) + void pop_min(void) { - super_t::pop_max(); + super_t::pop_min(); } /// \copydoc boost::heap::priority_queue::swap @@ -1444,7 +1480,13 @@ class min_max_heap: return super_t::end(); } - /// \copydoc boost::heap::fibonacci_heap::ordered_begin + /** + * \b Effects: Returns an ordered iterator to the first element contained in the priority queue. + * + * \b Spatial complexity: Requires an additional ((D - 1) * (size() - 1) + 1/(8*D) bytes. + * + * \b Note: Ordered iterators traverse the priority queue in heap order. + * */ ordered_iterator ordered_begin(void) const { return super_t::ordered_begin(); @@ -1459,6 +1501,8 @@ class min_max_heap: /** * \b Effects: Returns a reverse ordered iterator to the last element contained in the priority queue. * + * \b Spatial complexity: Requires an additional ((D - 1) * (size() - 1) + 1/(8*D) bytes. + * * \b Note: Reverse ordered iterators traverse the priority queue in heap reverse order. * */ reverse_ordered_iterator reverse_ordered_begin(void) const diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index 3a866df..b0ee297 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -438,8 +438,8 @@ BOOST_AUTO_TEST_CASE( min_max_heap_mutable_stable_test ) BOOST_AUTO_TEST_CASE( min_max_heap_compare_lookup_test ) { typedef boost::heap::min_max_heap, - boost::heap::compare, - boost::heap::allocator > > pri_queue; + boost::heap::compare, + boost::heap::allocator > > pri_queue; run_common_heap_tests(); } @@ -448,3 +448,4 @@ BOOST_AUTO_TEST_CASE( min_max_heap_leak_test ) typedef boost::heap::min_max_heap, boost::heap::arity<2> > pri_queue; run_leak_check_test(); } + From 969a6a722d73f8e8f9f01882a1fd728e9f80bc36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Wed, 24 Jul 2019 21:40:21 +0800 Subject: [PATCH 09/13] min-max heap: polishing code --- include/boost/heap/detail/mutable_heap.hpp | 188 ++++++++---------- .../heap/detail/ordered_adaptor_iterator.hpp | 142 +++++++------ include/boost/heap/min_max_heap.hpp | 2 +- 3 files changed, 154 insertions(+), 178 deletions(-) diff --git a/include/boost/heap/detail/mutable_heap.hpp b/include/boost/heap/detail/mutable_heap.hpp index 4b69c28..98906f6 100644 --- a/include/boost/heap/detail/mutable_heap.hpp +++ b/include/boost/heap/detail/mutable_heap.hpp @@ -226,7 +226,7 @@ class priority_queue_mutable_wrapper typedef typename object_list::difference_type difference_type; template - class ordered_iterator_base: + class ordered_iterator_facade: public boost::iterator_adaptorobjects.end()) static_cast(this)->discover_nodes(it); } - ordered_iterator_base(const_list_iterator it, const Wrapper * q, IndirectCmp const & cmp, Dispatcher const & dispatcher): + ordered_iterator_facade(const_list_iterator it, const Wrapper * q, IndirectCmp const & cmp, Dispatcher const & dispatcher): adaptor_type(it), Dispatcher(dispatcher), unvisited_nodes(cmp), q_(q) { if (it != q->objects.end()) static_cast(this)->discover_nodes(it); } - bool operator!=(ordered_iterator_base const & rhs) const + bool operator!=(ordered_iterator_facade const & rhs) const { return adaptor_type::base() != rhs.base(); } - bool operator==(ordered_iterator_base const & rhs) const + bool operator==(ordered_iterator_facade const & rhs) const { return !operator!=(rhs); } @@ -308,46 +308,46 @@ class priority_queue_mutable_wrapper const Wrapper * q_; }; - class ordered_iterator : public ordered_iterator_base - { - typedef ordered_iterator_base super_t; + class ordered_iterator : public ordered_iterator_facade + { + typedef ordered_iterator_facade facade; public: ordered_iterator(void): - super_t() + facade() {} ordered_iterator(const priority_queue_mutable_wrapper * q, indirect_cmp const & cmp): - super_t(q, cmp) + facade(q, cmp) {} ordered_iterator(const_list_iterator it, const priority_queue_mutable_wrapper * q, indirect_cmp const & cmp): - super_t(it, q, cmp) + facade(it, q, cmp) {} - void discover_nodes(typename super_t::iterator current) + void discover_nodes(typename facade::iterator current) { size_type current_index = current->second; - const q_type * q = &(super_t::q_->q_); + const q_type * q = &(facade::q_->q_); - if (super_t::iterator_dispatcher::is_leaf(q, current_index)) + if (facade::iterator_dispatcher::is_leaf(q, current_index)) return; - std::pair child_range = super_t::iterator_dispatcher::get_child_nodes(q, current_index); + std::pair child_range = facade::iterator_dispatcher::get_child_nodes(q, current_index); for (size_type i = child_range.first; i <= child_range.second; ++i) { - typename q_type::internal_type const & internal_value_at_index = super_t::iterator_dispatcher::get_internal_value(q, i); - typename q_type::value_type const & value_at_index = super_t::q_->q_.get_value(internal_value_at_index); + typename q_type::internal_type const & internal_value_at_index = facade::iterator_dispatcher::get_internal_value(q, i); + typename q_type::value_type const & value_at_index = facade::q_->q_.get_value(internal_value_at_index); - super_t::unvisited_nodes.push(value_at_index); + facade::unvisited_nodes.push(value_at_index); } } }; @@ -646,93 +646,85 @@ class double_ended_priority_queue_mutable_wrapper: typename Dispatcher, typename IndirectCmp > - class ordered_iterator_base : - public super_t::template ordered_iterator_base - { - typedef typename super_t::template ordered_iterator_base iterator_super_t; + class ordered_iterator_facade : + public super_t::template ordered_iterator_facade + { + typedef typename super_t::template ordered_iterator_facade facade; public: - ordered_iterator_base(void): - iterator_super_t() + ordered_iterator_facade(void): + facade() {} - ordered_iterator_base(const double_ended_priority_queue_mutable_wrapper * q, IndirectCmp const & cmp): - iterator_super_t(q, cmp) - {} - - ordered_iterator_base(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp): - iterator_super_t(it, q, cmp) - {} - - ordered_iterator_base(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename iterator_super_t::iterator_dispatcher const & dispatcher): - iterator_super_t(it, q, cmp, dispatcher) + ordered_iterator_facade(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename facade::iterator_dispatcher const & dispatcher): + facade(it, q, cmp, dispatcher) {} - void discover_nodes(typename iterator_super_t::iterator current) + void discover_nodes(typename facade::iterator current) { typedef typename super_t::size_type size_type; typedef typename super_t::q_type q_type; size_type current_index = current->second; - const q_type * q = &(iterator_super_t::q_->q_); + const q_type * q = &(facade::q_->q_); - if (iterator_super_t::iterator_dispatcher::is_leaf(q, current_index)) + if (facade::iterator_dispatcher::is_leaf(q, current_index)) return; std::pair extra_child_range = std::make_pair(1, 0); - std::pair child_range = iterator_super_t::iterator_dispatcher::get_child_nodes(q, current_index, extra_child_range); + std::pair child_range = facade::iterator_dispatcher::get_child_nodes(q, current_index, extra_child_range); for (size_type i = extra_child_range.first; i <= extra_child_range.second; ++i) { - typename q_type::internal_type const & internal_value_at_index = iterator_super_t::iterator_dispatcher::get_internal_value(q, i); - typename q_type::value_type const & value_at_index = iterator_super_t::q_->q_.get_value(internal_value_at_index); + typename q_type::internal_type const & internal_value_at_index = facade::iterator_dispatcher::get_internal_value(q, i); + typename q_type::value_type const & value_at_index = facade::q_->q_.get_value(internal_value_at_index); - iterator_super_t::unvisited_nodes.push(value_at_index); + facade::unvisited_nodes.push(value_at_index); } for (size_type i = child_range.first; i <= child_range.second; ++i) { - typename q_type::internal_type const & internal_value_at_index = iterator_super_t::iterator_dispatcher::get_internal_value(q, i); - typename q_type::value_type const & value_at_index = iterator_super_t::q_->q_.get_value(internal_value_at_index); + typename q_type::internal_type const & internal_value_at_index = facade::iterator_dispatcher::get_internal_value(q, i); + typename q_type::value_type const & value_at_index = facade::q_->q_.get_value(internal_value_at_index); - iterator_super_t::unvisited_nodes.push(value_at_index); + facade::unvisited_nodes.push(value_at_index); } } }; public: - class ordered_iterator : public ordered_iterator_base + class ordered_iterator : public ordered_iterator_facade { - typedef ordered_iterator_base - iterator_base_super_t; + typedef ordered_iterator_facade + facade; public: ordered_iterator(void): - iterator_base_super_t() + facade() {} ordered_iterator(const double_ended_priority_queue_mutable_wrapper * q, typename super_t::indirect_cmp const & cmp): - iterator_base_super_t(q, cmp) + facade(q, cmp) {} ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, typename super_t::indirect_cmp const & cmp): - iterator_base_super_t(it, q, cmp) + facade(it, q, cmp) {} - ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, typename super_t::indirect_cmp const & cmp, typename iterator_base_super_t::iterator_dispatcher const & dispatcher): - iterator_base_super_t(it, q, cmp, dispatcher) + ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, typename super_t::indirect_cmp const & cmp, typename facade::iterator_dispatcher const & dispatcher): + facade(it, q, cmp, dispatcher) {} }; @@ -750,48 +742,40 @@ class double_ended_priority_queue_mutable_wrapper: } public: - class reverse_ordered_iterator : public ordered_iterator_base + class reverse_ordered_iterator : public ordered_iterator_facade { - typedef ordered_iterator_base - iterator_base_super_t; + typedef ordered_iterator_facade + facade; public: reverse_ordered_iterator(void): - iterator_base_super_t() - {} - - reverse_ordered_iterator(const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp): - iterator_base_super_t(q, cmp) - {} - - reverse_ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp): - iterator_base_super_t(it, q, cmp) + facade() {} - reverse_ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename iterator_base_super_t::iterator_dispatcher const & dispatcher): - iterator_base_super_t(it, q, cmp, dispatcher) + reverse_ordered_iterator(typename super_t::const_list_iterator it, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename facade::iterator_dispatcher const & dispatcher): + facade(it, q, cmp, dispatcher) {} - reverse_ordered_iterator(typename super_t::const_list_iterator it, std::pair initial_indexes, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename iterator_base_super_t::iterator_dispatcher const & dispatcher): - iterator_base_super_t(it, q, cmp, dispatcher) + reverse_ordered_iterator(typename super_t::const_list_iterator it, std::pair initial_indexes, const double_ended_priority_queue_mutable_wrapper * q, indirect_reverse_cmp const & cmp, typename facade::iterator_dispatcher const & dispatcher): + facade(it, q, cmp, dispatcher) { - const typename super_t::q_type * q_ = &(iterator_base_super_t::q_->q_); + const typename super_t::q_type * q_ = &(facade::q_->q_); for (size_t i = initial_indexes.first; i <= initial_indexes.second; ++i) if (i != it->second) { - typename super_t::q_type::internal_type const & internal_value_at_index = iterator_base_super_t::iterator_dispatcher::get_internal_value(q_, i); - typename super_t::q_type::value_type const & value_at_index = iterator_base_super_t::q_->q_.get_value(internal_value_at_index); + typename super_t::q_type::internal_type const & internal_value_at_index = facade::iterator_dispatcher::get_internal_value(q_, i); + typename super_t::q_type::value_type const & value_at_index = facade::q_->q_.get_value(internal_value_at_index); - iterator_base_super_t::unvisited_nodes.push(value_at_index); + facade::unvisited_nodes.push(value_at_index); } } }; - + reverse_ordered_iterator reverse_ordered_begin(void) const { if (!super_t::empty()) { diff --git a/include/boost/heap/detail/ordered_adaptor_iterator.hpp b/include/boost/heap/detail/ordered_adaptor_iterator.hpp index 5a4a0c1..2cd5e0f 100644 --- a/include/boost/heap/detail/ordered_adaptor_iterator.hpp +++ b/include/boost/heap/detail/ordered_adaptor_iterator.hpp @@ -43,7 +43,7 @@ template -class ordered_adaptor_iterator_base : +class ordered_adaptor_iterator_facade : public boost::iterator_facade::max)()), unvisited_nodes(compare_by_heap_value(NULL, ValueCompare())) {} - ordered_adaptor_iterator_base(const ContainerType * container, ValueCompare const & cmp): + ordered_adaptor_iterator_facade(const ContainerType * container, ValueCompare const & cmp): container(container), current_index(container->size()), unvisited_nodes(compare_by_heap_value(container, ValueCompare())) {} - ordered_adaptor_iterator_base(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): + ordered_adaptor_iterator_facade(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): container(container), current_index(initial_index), unvisited_nodes(compare_by_heap_value(container, cmp)) { static_cast(this)->discover_nodes(initial_index); } - ordered_adaptor_iterator_base(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): + ordered_adaptor_iterator_facade(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): Dispatcher(dispatcher), container(container), current_index(initial_index), unvisited_nodes(compare_by_heap_value(container, cmp)) @@ -101,7 +101,7 @@ class ordered_adaptor_iterator_base : } public: - bool equal (ordered_adaptor_iterator_base const & rhs) const + bool equal (ordered_adaptor_iterator_facade const & rhs) const { if (current_index != rhs.current_index) return false; @@ -148,30 +148,30 @@ template class ordered_adaptor_iterator: - public ordered_adaptor_iterator_base, - ValueType, - ContainerType, - Alloc, - ValueCompare, - Dispatcher> + public ordered_adaptor_iterator_facade, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher> { - typedef ordered_adaptor_iterator_base, - ValueType, - ContainerType, - Alloc, - ValueCompare, - Dispatcher> - base; + typedef ordered_adaptor_iterator_facade, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher> + facade; friend class boost::iterator_core_access; @@ -180,27 +180,27 @@ class ordered_adaptor_iterator: {} ordered_adaptor_iterator(const ContainerType * container, ValueCompare const & cmp): - base(container, cmp) + facade(container, cmp) {} ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): - base(initial_index, container, cmp) + facade(initial_index, container, cmp) {} ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): - base(initial_index, container, cmp, dispatcher) + facade(initial_index, container, cmp, dispatcher) {} public: void discover_nodes(size_t index) { - if (Dispatcher::is_leaf(base::container, index)) + if (Dispatcher::is_leaf(facade::container, index)) return; - std::pair child_range = Dispatcher::get_child_nodes(base::container, index); + std::pair child_range = Dispatcher::get_child_nodes(facade::container, index); for (size_t i = child_range.first; i <= child_range.second; ++i) - base::unvisited_nodes.push(i); + facade::unvisited_nodes.push(i); } }; @@ -212,32 +212,32 @@ template class extended_ordered_adaptor_iterator: - public ordered_adaptor_iterator_base, - ValueType, - ContainerType, - Alloc, - ValueCompare, - Dispatcher - > + public ordered_adaptor_iterator_facade, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher + > { - typedef ordered_adaptor_iterator_base, - ValueType, - ContainerType, - Alloc, - ValueCompare, - Dispatcher - > - base; + typedef ordered_adaptor_iterator_facade, + ValueType, + ContainerType, + Alloc, + ValueCompare, + Dispatcher + > + facade; friend class boost::iterator_core_access; @@ -245,39 +245,31 @@ class extended_ordered_adaptor_iterator: extended_ordered_adaptor_iterator(void) {} - extended_ordered_adaptor_iterator(const ContainerType * container, ValueCompare const & cmp): - base(container, cmp) - {} - - extended_ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp): - base(initial_index, container, cmp) - {} - extended_ordered_adaptor_iterator(size_t initial_index, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): - base(initial_index, container, cmp, dispatcher) + facade(initial_index, container, cmp, dispatcher) {} extended_ordered_adaptor_iterator(size_t initial_index, std::pair initial_indexes, const ContainerType * container, ValueCompare const & cmp, const Dispatcher & dispatcher): - base(initial_index, container, cmp, dispatcher) + facade(initial_index, container, cmp, dispatcher) { for (size_t i = initial_indexes.first; i <= initial_indexes.second; ++i) if (i != initial_index) - base::unvisited_nodes.push(i); + facade::unvisited_nodes.push(i); } void discover_nodes(size_t index) { - if (Dispatcher::is_leaf(base::container, index)) + if (Dispatcher::is_leaf(facade::container, index)) return; std::pair extra_child_range = std::make_pair(1, 0); - std::pair child_range = Dispatcher::get_child_nodes(base::container, index, extra_child_range); + std::pair child_range = Dispatcher::get_child_nodes(facade::container, index, extra_child_range); for (size_t i = extra_child_range.first; i <= extra_child_range.second; ++i) - base::unvisited_nodes.push(i); + facade::unvisited_nodes.push(i); for (size_t i = child_range.first; i <= child_range.second; ++i) - base::unvisited_nodes.push(i); + facade::unvisited_nodes.push(i); } }; diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index b715143..17e0dec 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -766,7 +766,7 @@ class min_max_heap: reverse_ordered_iterator reverse_ordered_end(void) const { - return reverse_ordered_iterator(size(), this, reverse_internal_compare(super_t::get_internal_cmp()), reverse_ordered_iterator_dispatcher(root())); + return reverse_ordered_iterator(size(), this, reverse_internal_compare(super_t::get_internal_cmp()), reverse_ordered_iterator_dispatcher(0)); } /* iterators */ }; From 8990b581b9a0800d71294ceddb476ab52e18f6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Thu, 25 Jul 2019 10:35:59 +0800 Subject: [PATCH 10/13] min-max heap: in compliance with clang; 03, 98 --- include/boost/heap/min_max_heap.hpp | 21 +++++-- test/min_max_heap_test.cpp | 85 +++++++++++++++-------------- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index 17e0dec..583f3e5 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -175,7 +175,7 @@ class min_max_heap: return super_t::value_comp(); } - template + template bool compare(size_type i, size_type j) const { BOOST_ASSERT(i < this->size()); @@ -507,7 +507,7 @@ class min_max_heap: size_type parent = this->parent(index); - if (compare(index, parent)) + if (compare(index, parent)) decrease(index); else increase(index); @@ -978,11 +978,20 @@ struct min_max_ordered_iterator_status<2, IntType> : min_max_ordered_iterator_st typedef min_max_ordered_iterator_status_base<2, IntType> base; min_max_ordered_iterator_status(IntType max_index = 0) : - base(max_index), - masks{0, 0x01, 0x03, 0, 0x0F, 0, 0, 0} - {} + base(max_index) +#ifndef BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX + ,masks{0, 0x01, 0x03, 0, 0x0F, 0, 0, 0} {} + const +#else + { + std::memset(masks, 0, 8); + masks[1] = 0x01; + masks[2] = 0x03; + masks[4] = 0x0F; + } +#endif - const uint8_t masks[8]; + uint8_t masks[8]; void set(IntType index) { diff --git a/test/min_max_heap_test.cpp b/test/min_max_heap_test.cpp index b0ee297..33c8f18 100644 --- a/test/min_max_heap_test.cpp +++ b/test/min_max_heap_test.cpp @@ -109,45 +109,50 @@ BOOST_AUTO_TEST_CASE( min_max_heap_expected_mask_test ) std::set mask; std::set umask; -#define LOCALTEST(base, max, index, ...) \ + std::vector values; + for (unsigned int i = 0; i <= 12; ++i) + values.push_back(i); + const std::vector::const_iterator it = values.begin(); + +#define LOCALTEST(base, max, index, begin, end) \ masks(base, max, index, mask, umask); \ - BOOST_REQUIRE(mask == std::set(__VA_ARGS__)); - - LOCALTEST(2, 1, 0, {1,2}); - LOCALTEST(2, 1, 1, {1}); - LOCALTEST(2, 2, 0, {1,2}); - LOCALTEST(2, 2, 1, {1}); - LOCALTEST(2, 2, 2, {2}); - LOCALTEST(2, 3, 0, {3,4,5,6}); - LOCALTEST(2, 3, 1, {3,4}); - LOCALTEST(2, 3, 2, {5,6}); - LOCALTEST(2, 3, 3, {3}); - LOCALTEST(2, 4, 0, {3,4,5,6}); - LOCALTEST(2, 4, 1, {3,4}); - LOCALTEST(2, 4, 2, {5,6}); - LOCALTEST(2, 4, 3, {3}); - LOCALTEST(2, 4, 4, {4}); - - LOCALTEST(3, 1, 0, {1,2,3}); - LOCALTEST(3, 1, 1, {1}); - LOCALTEST(3, 2, 0, {1,2,3}); - LOCALTEST(3, 2, 1, {1}); - LOCALTEST(3, 2, 2, {2}); - LOCALTEST(3, 3, 0, {1,2,3}); - LOCALTEST(3, 3, 1, {1}); - LOCALTEST(3, 3, 2, {2}); - LOCALTEST(3, 3, 3, {3}); - LOCALTEST(3, 4, 0, {4,5,6,7,8,9,10,11,12}); - LOCALTEST(3, 4, 1, {4,5,6}); - LOCALTEST(3, 4, 2, {7,8,9}); - LOCALTEST(3, 4, 3, {10,11,12}); - LOCALTEST(3, 4, 4, {4}); - LOCALTEST(3, 5, 0, {4,5,6,7,8,9,10,11,12}); - LOCALTEST(3, 5, 1, {4,5,6}); - LOCALTEST(3, 5, 2, {7,8,9}); - LOCALTEST(3, 5, 3, {10,11,12}); - LOCALTEST(3, 5, 4, {4}); - LOCALTEST(3, 5, 5, {5}); + BOOST_REQUIRE(mask == std::set(it + begin, it + end + 1)); + + LOCALTEST(2, 1, 0, 1, 2); + LOCALTEST(2, 1, 1, 1, 1); + LOCALTEST(2, 2, 0, 1, 2); + LOCALTEST(2, 2, 1, 1, 1); + LOCALTEST(2, 2, 2, 2, 2); + LOCALTEST(2, 3, 0, 3, 6); + LOCALTEST(2, 3, 1, 3, 4); + LOCALTEST(2, 3, 2, 5, 6); + LOCALTEST(2, 3, 3, 3, 3); + LOCALTEST(2, 4, 0, 3, 6); + LOCALTEST(2, 4, 1, 3, 4); + LOCALTEST(2, 4, 2, 5, 6); + LOCALTEST(2, 4, 3, 3, 3); + LOCALTEST(2, 4, 4, 4, 4); + + LOCALTEST(3, 1, 0, 1, 3); + LOCALTEST(3, 1, 1, 1, 1); + LOCALTEST(3, 2, 0, 1, 3); + LOCALTEST(3, 2, 1, 1, 1); + LOCALTEST(3, 2, 2, 2, 2); + LOCALTEST(3, 3, 0, 1, 3); + LOCALTEST(3, 3, 1, 1, 1); + LOCALTEST(3, 3, 2, 2, 2); + LOCALTEST(3, 3, 3, 3, 3); + LOCALTEST(3, 4, 0, 4, 12); + LOCALTEST(3, 4, 1, 4, 6); + LOCALTEST(3, 4, 2, 7, 9); + LOCALTEST(3, 4, 3, 10, 12); + LOCALTEST(3, 4, 4, 4, 4); + LOCALTEST(3, 5, 0, 4, 12); + LOCALTEST(3, 5, 1, 4, 6); + LOCALTEST(3, 5, 2, 7, 9); + LOCALTEST(3, 5, 3, 10, 12); + LOCALTEST(3, 5, 4, 4, 4); + LOCALTEST(3, 5, 5, 5, 5); #undef LOCALTEST } @@ -288,7 +293,7 @@ void run_min_max_heap_iterator_dispatcher_upward_test() BOOST_AUTO_TEST_CASE( min_max_heap_iterator_dispatcher_test ) { - typedef typename boost::heap::detail::min_max_heap_signature::bind< + typedef boost::heap::detail::min_max_heap_signature::bind< boost::parameter::void_, boost::parameter::void_, boost::parameter::void_, @@ -296,7 +301,7 @@ BOOST_AUTO_TEST_CASE( min_max_heap_iterator_dispatcher_test ) boost::parameter::void_, boost::parameter::void_>::type signature; typedef dispatcher_queue pri_queue; - typedef typename pri_queue::template iterator_dispatcher iterator_dispatcher; + typedef pri_queue::iterator_dispatcher iterator_dispatcher; pri_queue q; std::pair regular; From 98845759706b1f39ea7115a6f134eae502908e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Thu, 25 Jul 2019 22:59:45 +0800 Subject: [PATCH 11/13] min-max heap: fix mutability --- include/boost/heap/detail/mutable_heap.hpp | 10 +++++----- include/boost/heap/min_max_heap.hpp | 21 ++++++--------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/include/boost/heap/detail/mutable_heap.hpp b/include/boost/heap/detail/mutable_heap.hpp index 98906f6..d7d89c9 100644 --- a/include/boost/heap/detail/mutable_heap.hpp +++ b/include/boost/heap/detail/mutable_heap.hpp @@ -571,12 +571,12 @@ class double_ended_priority_queue_mutable_wrapper: public priority_queue_mutable_wrapper { typedef priority_queue_mutable_wrapper super_t; + typedef typename super_t::value_compare base_value_compare; - struct indirect_reverse_cmp: - public super_t::value_compare + struct indirect_reverse_cmp: public base_value_compare { - indirect_reverse_cmp(typename super_t::value_compare const & cmp = super_t::value_compare()): - super_t::value_compare(cmp) + indirect_reverse_cmp(base_value_compare const & cmp = base_value_compare()): + base_value_compare(cmp) {} bool operator()(typename super_t::const_list_iterator const & lhs, typename super_t::const_list_iterator const & rhs) const @@ -586,7 +586,7 @@ class double_ended_priority_queue_mutable_wrapper: }; public: - double_ended_priority_queue_mutable_wrapper(typename super_t::value_compare const & cmp = super_t::value_compare()): + double_ended_priority_queue_mutable_wrapper(base_value_compare const & cmp = base_value_compare()): super_t(cmp) {} diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index 583f3e5..e0f2060 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -502,25 +502,18 @@ class min_max_heap: void update(size_type index) { - if (index == root()) - return trickle_down(index); - - size_type parent = this->parent(index); - - if (compare(index, parent)) - decrease(index); - else - increase(index); + bubble_up(index); + trickle_down(index); } void increase(size_type index) { - bubble_up(index); + update(index); } void decrease(size_type index) { - trickle_down(index); + update(index); } void erase(size_type index) @@ -531,10 +524,8 @@ class min_max_heap: swap(index, last()); q_.pop_back(); - if (!empty() && index != size()) { - bubble_up(index); - trickle_down(index); - } + if (!empty() && index != size()) + update(index); } /* interface */ From 2d9a3fb31920e5bd860754ec3eb65171fa6e5971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Thu, 15 Aug 2019 10:37:23 +0800 Subject: [PATCH 12/13] min-max heap: explicit disjoint heirs --- include/boost/heap/min_max_heap.hpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index e0f2060..b05ab3c 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -989,9 +989,10 @@ struct min_max_ordered_iterator_status<2, IntType> : min_max_ordered_iterator_st IntType chunk, offset, heirs_octuple, heirs_left; base::positions_by_8(index, chunk, offset, heirs_octuple, heirs_left); - std::memset(base::candidates.data() + chunk, 0xFF, heirs_octuple); - // disjoint heirs - base::candidates[chunk] |= masks[heirs_left] << (8 - heirs_left - offset); + if (0 < heirs_octuple) + std::memset(base::candidates.data() + chunk, 0xFF, heirs_octuple); + else + base::candidates[chunk] |= masks[heirs_left] << (8 - heirs_left - offset); } void reset(IntType index) @@ -999,9 +1000,10 @@ struct min_max_ordered_iterator_status<2, IntType> : min_max_ordered_iterator_st IntType chunk, offset, heirs_octuple, heirs_left; base::positions_by_8(index, chunk, offset, heirs_octuple, heirs_left); - std::memset(base::candidates.data() + chunk, 0, heirs_octuple); - // disjoint heirs - base::candidates[chunk] &= ~(masks[heirs_left] << (8 - heirs_left - offset)); + if (0 < heirs_octuple) + std::memset(base::candidates.data() + chunk, 0, heirs_octuple); + else + base::candidates[chunk] &= ~(masks[heirs_left] << (8 - heirs_left - offset)); } bool is_complete(IntType index) @@ -1009,15 +1011,21 @@ struct min_max_ordered_iterator_status<2, IntType> : min_max_ordered_iterator_st IntType chunk, offset, heirs; base::positions(index, chunk, offset, heirs); - while (8 <= heirs) { + if (8 <= heirs) { + do { if (base::candidates[chunk] != 0xFF) return false; ++chunk; heirs -= 8; + } + while (8 <= heirs); + + return true; + } + else { + const uint8_t mask = masks[heirs] << (8 - heirs - offset); + return (mask && (base::candidates[chunk] & mask) == mask) || !mask; } - //disjoint heirs - const uint8_t mask = masks[heirs] << (8 - heirs - offset); - return (mask && (base::candidates[chunk] & mask) == mask) || !mask; } }; From c70c7a34b0fb86ba8a620e4b8567286b67a06c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Scano?= Date: Sat, 28 Aug 2021 00:32:30 +0800 Subject: [PATCH 13/13] Clarify some code and comments --- include/boost/heap/min_max_heap.hpp | 43 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/include/boost/heap/min_max_heap.hpp b/include/boost/heap/min_max_heap.hpp index 835eb69..63aa193 100644 --- a/include/boost/heap/min_max_heap.hpp +++ b/include/boost/heap/min_max_heap.hpp @@ -241,18 +241,17 @@ class min_max_heap: std::pair children(size_type index) const { - const size_type child = first_child(index); - return std::make_pair(child, child + D - 1); + return std::make_pair(first_child(index), last_child(index)); } size_type first_grandchild(size_type index) const { - return D * D * index + D + 1; + return first_child(first_child(index)); } size_type last_grandchild(size_type index) const { - return D * D * index + D * (D + 1); + return last_child(last_child(index)); } std::pair grandchildren(size_type index) const @@ -569,27 +568,32 @@ class min_max_heap: std::pair get_child_nodes(const min_max_heap * heap, size_type index, std::pair & extra_child_nodes) { /* As stated in the article, the Hasse diagram is divided in two parts. - * The first one consists of even levels and can be explored like a - * regular binary tree except that the number of children of a node - * is D^2 instead of D because odd levels are skipped. - * The second part consists of odd levels and is a bit more complicated - * to explore. The search has to go back up in the tree from the leaves - * but since a node on an odd level is being pointed to by D^2 grandchildren - * in the Hasse diagram, it cannot be visited until all of its heirs have - * previously been visited. + * + * The first one starts from the root and consists of even level nodes. + * It can be explored like a regular tree, by skipping nodes on odd levels. + * Thus, the number of children of such nodes is at most D^2 instead of D. + * + * Conversely, successors of odd level nodes converge back to the opposite + * extremum of the root. Such nodes are being pointed to by at most D^2 + * grandchildren. Additionally, they cannot be visited until all of their heirs + * have previously been visited, effectively 'reversing' the typical exploration + * denoted for even level nodes. * * The 'min_max_ordered_iterator_status' structure is used to keep track * of this. It indicates for each node on an odd level whether its heirs - * have been visited. When the last heir is being visited, it adds the - * node to the potential candidates to be visited next. Furthermore, when - * this happens, the markers for its heirs are reset and will then be + * have been visited. When the last heir is being visited, it adds its + * grandparent to the potential candidates to be visited next. Furthermore, + * when this happens, the markers for its heirs are reset and will then be * reused to indicate to its own grandfather that it has been visited while * its siblings may not have yet been visited. + * + * Additionally, if a node on an odd level does not have any child, it must be + * added by its parent. * - * This method requires $O(\frac{(D - 1) * (size() - 1) + 1}{D})$ bytes. + * This method requires O(((D - 1) * (size() - 1) + 1) / D) bytes. * - * The following specific cases must be addressed (D = 3): - * 0 1) when 0 is visited, it must add nodes 4-8 to the list + * The following specific cases must be addressed (for D = 3): + * 0 1) when 0 is visited, nodes 4-8 are added to the list * /|\ of potential candidates to be visited next, as well * / | \ as node 3. This explains the 'extra_child_nodes' as * / | \ node denoted 4-8 and 3 might not be consecutive in a @@ -597,7 +601,8 @@ class min_max_heap: * / | \ 2) when 8 is visited, it must set its indicator and * 1 2 3 set the indicator for the non existing node 9 (in * /|\ /| general for all its right non existing siblings) and - * 4 5 6 7 8 then add 2 if 7 has already been visited. + * 4 5 6 7 8 then add node 2 if node 7 has already been visited. If + * not, node 7 will add node 2 when it is visited later on. */ const size_type last = heap->last(); const bool on_compare_level = !(Forward ^ heap->is_on_compare_level(index));