// Boost.Geometry // Copyright (c) 2021, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html #ifndef BOOST_GEOMETRY_INDEX_DETAIL_MINMAX_HEAP_HPP #define BOOST_GEOMETRY_INDEX_DETAIL_MINMAX_HEAP_HPP #include #include #include #ifdef _MSC_VER // msvc and clang-win #include #endif namespace boost { namespace geometry { namespace index { namespace detail { // Resources: // https://en.wikipedia.org/wiki/Min-max_heap // http://akira.ruc.dk/~keld/teaching/algoritmedesign_f03/Artikler/02/Atkinson86.pdf // https://probablydance.com/2020/08/31/on-modern-hardware-the-min-max-heap-beats-a-binary-heap/ // https://stackoverflow.com/questions/6531543/efficient-implementation-of-binary-heaps // https://stackoverflow.com/questions/994593/how-to-do-an-integer-log2-in-c namespace minmax_heap_detail { template using bitsize = std::integral_constant; template using diff_t = typename std::iterator_traits::difference_type; template using val_t = typename std::iterator_traits::value_type; // TODO: In C++20 use std::bit_width() template ::value || (bitsize::value != 32 && bitsize::value != 64), int> = 0> inline int level(T i) { ++i; int r = 0; while (i >>= 1) { ++r; } return r; } //template //inline int level(T i) //{ // using std::log2; // return int(log2(i + 1)); //} #ifdef _MSC_VER // msvc and clang-win template ::value && bitsize::value == 32, int> = 0> inline int level(T i) { unsigned long r = 0; _BitScanReverse(&r, (unsigned long)(i + 1)); return int(r); } template ::value && bitsize::value == 64, int> = 0> inline int level(T i) { unsigned long r = 0; #ifdef _WIN64 _BitScanReverse64(&r, (unsigned long long)(i + 1)); #else if (_BitScanReverse(&r, (unsigned long)((i + 1) >> 32))) { r += 32; } else { _BitScanReverse(&r, (unsigned long)(i + 1)); } #endif return int(r); } #elif defined(__clang__) || defined(__GNUC__) // Only available in gcc-10 and clang-10 //#elif defined(__has_builtin) && __has_builtin(__builtin_clzl) && __has_builtin(__builtin_clzll) template ::value && bitsize::value == 32, int> = 0> inline int level(T i) { return 31 - __builtin_clzl((unsigned long)(i + 1)); } template ::value && bitsize::value == 64, int> = 0> inline int level(T i) { return 63 - __builtin_clzll((unsigned long long)(i + 1)); } #else template ::value && bitsize::value == 32, int> = 0> inline int level(T i) { ++i; int r = 0; if (i >= 65536) { r += 16; i >>= 16; } if (i >= 256) { r += 8; i >>= 8; } if (i >= 16) { r += 4; i >>= 4; } if (i >= 4) { r += 2; i >>= 2; } if (i >= 2) { r += 1; i >>= 1; } return r; } template ::value && bitsize::value == 64, int> = 0> inline int level(T i) { ++i; int r = 0; if (i >= 4294967296ll) { r += 32; i >>= 32; } if (i >= 65536ll) { r += 16; i >>= 16; } if (i >= 256ll) { r += 8; i >>= 8; } if (i >= 16ll) { r += 4; i >>= 4; } if (i >= 4ll) { r += 2; i >>= 2; } if (i >= 2ll) { r += 1; i >>= 1; } return r; } #endif // min/max functions only differ in the order of arguments in comp struct min_call { template bool operator()(Compare&& comp, T1 const& v1, T2 const& v2) const { return comp(v1, v2); } }; struct max_call { template bool operator()(Compare&& comp, T1 const& v1, T2 const& v2) const { return comp(v2, v1); } }; template inline void push_heap2(It first, diff_t c, val_t val, Compare comp) { while (c > 2) { diff_t const g = (c - 3) >> 2; // grandparent index if (! Call()(comp, val, *(first + g))) { break; } *(first + c) = std::move(*(first + g)); c = g; } *(first + c) = std::move(val); } template inline void push_heap1(It first, diff_t c, val_t val, Compare comp) { diff_t const p = (c - 1) >> 1; // parent index if (MinCall()(comp, *(first + p), val)) { *(first + c) = std::move(*(first + p)); return push_heap2(first, p, std::move(val), comp); } else { return push_heap2(first, c, std::move(val), comp); } } template inline void push_heap(It first, It last, Compare comp) { diff_t const size = last - first; if (size < 2) { return; } diff_t c = size - 1; // back index val_t val = std::move(*(first + c)); if (level(c) % 2 == 0) // is min level { push_heap1(first, c, std::move(val), comp); } else { push_heap1(first, c, std::move(val), comp); } } template inline diff_t pick_grandchild4(It first, diff_t f, Compare comp) { It it = first + f; diff_t m1 = Call()(comp, *(it), *(it + 1)) ? f : f + 1; diff_t m2 = Call()(comp, *(it + 2), *(it + 3)) ? f + 2 : f + 3; return Call()(comp, *(first + m1), *(first + m2)) ? m1 : m2; } //template //inline diff_t pick_descendant(It first, diff_t f, diff_t l, Compare comp) //{ // diff_t m = f; // for (++f; f != l; ++f) // { // if (Call()(comp, *(first + f), *(first + m))) // { // m = f; // } // } // return m; //} template inline void pop_heap1(It first, diff_t p, diff_t size, val_t val, Compare comp) { if (size >= 7) // grandparent of 4 grandchildren is possible { diff_t const last_g = (size - 3) >> 2; // grandparent of the element behind back while (p < last_g) // p is grandparent of 4 grandchildren { diff_t const ll = 4 * p + 3; diff_t const m = pick_grandchild4(first, ll, comp); if (! Call()(comp, *(first + m), val)) { break; } *(first + p) = std::move(*(first + m)); diff_t par = (m - 1) >> 1; if (Call()(comp, *(first + par), val)) { using std::swap; swap(*(first + par), val); } p = m; } } if (size >= 2 && p <= ((size - 2) >> 1)) // at least one child { diff_t const l = 2 * p + 1; diff_t m = l; // left child if (size >= 3 && p <= ((size - 3) >> 1)) // at least two children { // m = left child diff_t m2 = l + 1; // right child if (size >= 4 && p <= ((size - 4) >> 2)) // at least two children and one grandchild { diff_t const ll = 2 * l + 1; m = ll; // left most grandchild // m2 = right child if (size >= 5 && p <= ((size - 5) >> 2)) // at least two children and two grandchildren { m = Call()(comp, *(first + ll), *(first + ll + 1)) ? ll : (ll + 1); // greater of the left grandchildren // m2 = right child if (size >= 6 && p <= ((size - 6) >> 2)) // at least two children and three grandchildren { // m = greater of the left grandchildren m2 = ll + 2; // third grandchild } } } m = Call()(comp, *(first + m), *(first + m2)) ? m : m2; } if (Call()(comp, *(first + m), val)) { *(first + p) = std::move(*(first + m)); if (m >= 3 && p <= ((m - 3) >> 2)) // is p grandparent of m { diff_t par = (m - 1) >> 1; if (Call()(comp, *(first + par), val)) { using std::swap; swap(*(first + par), val); } } p = m; } } *(first + p) = std::move(val); } template inline void pop_heap(It first, It el, It last, Compare comp) { diff_t size = last - first; if (size < 2) { return; } --last; val_t val = std::move(*last); *last = std::move(*el); // Ignore the last element --size; diff_t p = el - first; if (level(p) % 2 == 0) // is min level { pop_heap1(first, p, size, std::move(val), comp); } else { pop_heap1(first, p, size, std::move(val), comp); } } template inline void make_heap(It first, It last, Compare comp) { diff_t size = last - first; diff_t p = size / 2; if (p <= 0) { return; } int level_p = level(p - 1); diff_t level_f = (diff_t(1) << level_p) - 1; while (p > 0) { --p; val_t val = std::move(*(first + p)); if (level_p % 2 == 0) // is min level { pop_heap1(first, p, size, std::move(val), comp); } else { pop_heap1(first, p, size, std::move(val), comp); } if (p == level_f) { --level_p; level_f >>= 1; } } } template inline bool is_heap(It first, It last, Compare comp) { diff_t const size = last - first; diff_t pow2 = 4; bool is_min_level = false; for (diff_t i = 1; i < size; ++i) { if (i == pow2 - 1) { pow2 *= 2; is_min_level = ! is_min_level; } diff_t const p = (i - 1) >> 1; if (is_min_level) { if (Call()(comp, *(first + p), *(first + i))) { return false; } } else { if (Call()(comp, *(first + i), *(first + p))) { return false; } } if (i >= 3) { diff_t const g = (p - 1) >> 1; if (is_min_level) { if (Call()(comp, *(first + i), *(first + g))) { return false; } } else { if (Call()(comp, *(first + g), *(first + i))) { return false; } } } } return true; } template inline It bottom_heap(It first, It last, Compare comp) { diff_t const size = last - first; return size <= 1 ? first : size == 2 ? (first + 1) : Call()(comp, *(first + 1), *(first + 2)) ? (first + 2) : (first + 1); } } // namespace minmax_heap_detail template inline void push_minmax_heap(It first, It last, Compare comp) { using namespace minmax_heap_detail; minmax_heap_detail::push_heap(first, last, comp); } template inline void push_minmax_heap(It first, It last) { using namespace minmax_heap_detail; minmax_heap_detail::push_heap(first, last, std::less<>()); } template inline void pop_top_minmax_heap(It first, It last, Compare comp) { using namespace minmax_heap_detail; pop_heap(first, first, last, comp); } template inline void pop_top_minmax_heap(It first, It last) { using namespace minmax_heap_detail; pop_heap(first, first, last, std::less<>()); } template inline void pop_bottom_minmax_heap(It first, It last, Compare comp) { using namespace minmax_heap_detail; It bottom = minmax_heap_detail::bottom_heap(first, last, comp); pop_heap(first, bottom, last, comp); } template inline void pop_bottom_minmax_heap(It first, It last) { using namespace minmax_heap_detail; auto&& comp = std::less<>(); It bottom = minmax_heap_detail::bottom_heap(first, last, comp); pop_heap(first, bottom, last, comp); } template inline void make_minmax_heap(It first, It last, Compare comp) { using namespace minmax_heap_detail; return minmax_heap_detail::make_heap(first, last, comp); } template inline void make_minmax_heap(It first, It last) { using namespace minmax_heap_detail; return minmax_heap_detail::make_heap(first, last, std::less<>()); } template inline bool is_minmax_heap(It first, It last, Compare comp) { using namespace minmax_heap_detail; return minmax_heap_detail::is_heap(first, last, comp); } template inline bool is_minmax_heap(It first, It last) { using namespace minmax_heap_detail; return minmax_heap_detail::is_heap(first, last, std::less<>()); } template inline decltype(auto) bottom_minmax_heap(It first, It last, Compare comp) { using namespace minmax_heap_detail; return *minmax_heap_detail::bottom_heap(first, last, comp); } template inline decltype(auto) bottom_minmax_heap(It first, It last) { using namespace minmax_heap_detail; return *minmax_heap_detail::bottom_heap(first, last, std::less<>()); } }}}} // namespace boost::geometry::index::detail #endif // BOOST_GEOMETRY_INDEX_DETAIL_MINMAX_HEAP_HPP