minmax_heap.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. // Boost.Geometry
  2. // Copyright (c) 2021-2023, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Licensed under the Boost Software License version 1.0.
  6. // http://www.boost.org/users/license.html
  7. #ifndef BOOST_GEOMETRY_INDEX_DETAIL_MINMAX_HEAP_HPP
  8. #define BOOST_GEOMETRY_INDEX_DETAIL_MINMAX_HEAP_HPP
  9. #include <iterator>
  10. #include <limits.h>
  11. #include <type_traits>
  12. #include <utility>
  13. #ifdef _MSC_VER // msvc and clang-win
  14. #include <intrin.h>
  15. #endif
  16. namespace boost { namespace geometry { namespace index { namespace detail
  17. {
  18. // Resources:
  19. // https://en.wikipedia.org/wiki/Min-max_heap
  20. // http://akira.ruc.dk/~keld/teaching/algoritmedesign_f03/Artikler/02/Atkinson86.pdf
  21. // https://probablydance.com/2020/08/31/on-modern-hardware-the-min-max-heap-beats-a-binary-heap/
  22. // https://stackoverflow.com/questions/6531543/efficient-implementation-of-binary-heaps
  23. // https://stackoverflow.com/questions/994593/how-to-do-an-integer-log2-in-c
  24. namespace minmax_heap_detail
  25. {
  26. template <typename T>
  27. using bitsize = std::integral_constant<std::size_t, sizeof(T) * CHAR_BIT>;
  28. template <typename It>
  29. using diff_t = typename std::iterator_traits<It>::difference_type;
  30. template <typename It>
  31. using val_t = typename std::iterator_traits<It>::value_type;
  32. // TODO: In C++20 use std::bit_width()
  33. template <typename T, std::enable_if_t<!std::is_integral<T>::value || (bitsize<T>::value != 32 && bitsize<T>::value != 64), int> = 0>
  34. inline int level(T i)
  35. {
  36. ++i;
  37. int r = 0;
  38. while (i >>= 1) { ++r; }
  39. return r;
  40. }
  41. //template <typename T>
  42. //inline int level(T i)
  43. //{
  44. // using std::log2;
  45. // return int(log2(i + 1));
  46. //}
  47. #ifdef _MSC_VER // msvc and clang-win
  48. template <typename T, std::enable_if_t<std::is_integral<T>::value && bitsize<T>::value == 32, int> = 0>
  49. inline int level(T i)
  50. {
  51. unsigned long r = 0;
  52. _BitScanReverse(&r, (unsigned long)(i + 1));
  53. return int(r);
  54. }
  55. template <typename T, std::enable_if_t<std::is_integral<T>::value && bitsize<T>::value == 64, int> = 0>
  56. inline int level(T i)
  57. {
  58. unsigned long r = 0;
  59. #ifdef _WIN64
  60. _BitScanReverse64(&r, (unsigned long long)(i + 1));
  61. #else
  62. if (_BitScanReverse(&r, (unsigned long)((i + 1) >> 32))) { r += 32; }
  63. else { _BitScanReverse(&r, (unsigned long)(i + 1)); }
  64. #endif
  65. return int(r);
  66. }
  67. #elif defined(__clang__) || defined(__GNUC__)
  68. // Only available in gcc-10 and clang-10
  69. //#elif defined(__has_builtin) && __has_builtin(__builtin_clzl) && __has_builtin(__builtin_clzll)
  70. template <typename T, std::enable_if_t<std::is_integral<T>::value && bitsize<T>::value == 32, int> = 0>
  71. inline int level(T i)
  72. {
  73. return 31 - __builtin_clzl((unsigned long)(i + 1));
  74. }
  75. template <typename T, std::enable_if_t<std::is_integral<T>::value && bitsize<T>::value == 64, int> = 0>
  76. inline int level(T i)
  77. {
  78. return 63 - __builtin_clzll((unsigned long long)(i + 1));
  79. }
  80. #else
  81. template <typename T, std::enable_if_t<std::is_integral<T>::value && bitsize<T>::value == 32, int> = 0>
  82. inline int level(T i)
  83. {
  84. ++i;
  85. int r = 0;
  86. if (i >= 65536) { r += 16; i >>= 16; }
  87. if (i >= 256) { r += 8; i >>= 8; }
  88. if (i >= 16) { r += 4; i >>= 4; }
  89. if (i >= 4) { r += 2; i >>= 2; }
  90. if (i >= 2) { r += 1; i >>= 1; }
  91. return r;
  92. }
  93. template <typename T, std::enable_if_t<std::is_integral<T>::value && bitsize<T>::value == 64, int> = 0>
  94. inline int level(T i)
  95. {
  96. ++i;
  97. int r = 0;
  98. if (i >= 4294967296ll) { r += 32; i >>= 32; }
  99. if (i >= 65536ll) { r += 16; i >>= 16; }
  100. if (i >= 256ll) { r += 8; i >>= 8; }
  101. if (i >= 16ll) { r += 4; i >>= 4; }
  102. if (i >= 4ll) { r += 2; i >>= 2; }
  103. if (i >= 2ll) { r += 1; i >>= 1; }
  104. return r;
  105. }
  106. #endif
  107. // min/max functions only differ in the order of arguments in comp
  108. struct min_call
  109. {
  110. template <typename Compare, typename T1, typename T2>
  111. bool operator()(Compare&& comp, T1 const& v1, T2 const& v2) const
  112. {
  113. return comp(v1, v2);
  114. }
  115. };
  116. struct max_call
  117. {
  118. template <typename Compare, typename T1, typename T2>
  119. bool operator()(Compare&& comp, T1 const& v1, T2 const& v2) const
  120. {
  121. return comp(v2, v1);
  122. }
  123. };
  124. template <typename Call, typename It, typename Compare>
  125. inline void push_heap2(It first, diff_t<It> c, val_t<It> val, Compare comp)
  126. {
  127. while (c > 2)
  128. {
  129. diff_t<It> const g = (c - 3) >> 2; // grandparent index
  130. if (! Call()(comp, val, *(first + g)))
  131. {
  132. break;
  133. }
  134. *(first + c) = std::move(*(first + g));
  135. c = g;
  136. }
  137. *(first + c) = std::move(val);
  138. }
  139. template <typename MinCall, typename MaxCall, typename It, typename Compare>
  140. inline void push_heap1(It first, diff_t<It> c, val_t<It> val, Compare comp)
  141. {
  142. diff_t<It> const p = (c - 1) >> 1; // parent index
  143. if (MinCall()(comp, *(first + p), val))
  144. {
  145. *(first + c) = std::move(*(first + p));
  146. return push_heap2<MaxCall>(first, p, std::move(val), comp);
  147. }
  148. else
  149. {
  150. return push_heap2<MinCall>(first, c, std::move(val), comp);
  151. }
  152. }
  153. template <typename MinCall, typename MaxCall, typename It, typename Compare>
  154. inline void push_heap(It first, It last, Compare comp)
  155. {
  156. diff_t<It> const size = last - first;
  157. if (size < 2)
  158. {
  159. return;
  160. }
  161. diff_t<It> c = size - 1; // back index
  162. val_t<It> val = std::move(*(first + c));
  163. if (level(c) % 2 == 0) // is min level
  164. {
  165. push_heap1<MinCall, MaxCall>(first, c, std::move(val), comp);
  166. }
  167. else
  168. {
  169. push_heap1<MaxCall, MinCall>(first, c, std::move(val), comp);
  170. }
  171. }
  172. template <typename Call, typename It, typename Compare>
  173. inline diff_t<It> pick_grandchild4(It first, diff_t<It> f, Compare comp)
  174. {
  175. It it = first + f;
  176. diff_t<It> m1 = Call()(comp, *(it), *(it + 1)) ? f : f + 1;
  177. diff_t<It> m2 = Call()(comp, *(it + 2), *(it + 3)) ? f + 2 : f + 3;
  178. return Call()(comp, *(first + m1), *(first + m2)) ? m1 : m2;
  179. }
  180. //template <typename Call, typename It, typename Compare>
  181. //inline diff_t<It> pick_descendant(It first, diff_t<It> f, diff_t<It> l, Compare comp)
  182. //{
  183. // diff_t<It> m = f;
  184. // for (++f; f != l; ++f)
  185. // {
  186. // if (Call()(comp, *(first + f), *(first + m)))
  187. // {
  188. // m = f;
  189. // }
  190. // }
  191. // return m;
  192. //}
  193. template <typename Call, typename It, typename Compare>
  194. inline void pop_heap1(It first, diff_t<It> p, diff_t<It> size, val_t<It> val, Compare comp)
  195. {
  196. if (size >= 7) // grandparent of 4 grandchildren is possible
  197. {
  198. diff_t<It> const last_g = (size - 3) >> 2; // grandparent of the element behind back
  199. while (p < last_g) // p is grandparent of 4 grandchildren
  200. {
  201. diff_t<It> const ll = 4 * p + 3;
  202. diff_t<It> const m = pick_grandchild4<Call>(first, ll, comp);
  203. if (! Call()(comp, *(first + m), val))
  204. {
  205. break;
  206. }
  207. *(first + p) = std::move(*(first + m));
  208. diff_t<It> par = (m - 1) >> 1;
  209. if (Call()(comp, *(first + par), val))
  210. {
  211. using std::swap;
  212. swap(*(first + par), val);
  213. }
  214. p = m;
  215. }
  216. }
  217. if (size >= 2 && p <= ((size - 2) >> 1)) // at least one child
  218. {
  219. diff_t<It> const l = 2 * p + 1;
  220. diff_t<It> m = l; // left child
  221. if (size >= 3 && p <= ((size - 3) >> 1)) // at least two children
  222. {
  223. // m = left child
  224. diff_t<It> m2 = l + 1; // right child
  225. if (size >= 4 && p <= ((size - 4) >> 2)) // at least two children and one grandchild
  226. {
  227. diff_t<It> const ll = 2 * l + 1;
  228. m = ll; // left most grandchild
  229. // m2 = right child
  230. if (size >= 5 && p <= ((size - 5) >> 2)) // at least two children and two grandchildren
  231. {
  232. m = Call()(comp, *(first + ll), *(first + ll + 1)) ? ll : (ll + 1); // greater of the left grandchildren
  233. // m2 = right child
  234. if (size >= 6 && p <= ((size - 6) >> 2)) // at least two children and three grandchildren
  235. {
  236. // m = greater of the left grandchildren
  237. m2 = ll + 2; // third grandchild
  238. }
  239. }
  240. }
  241. m = Call()(comp, *(first + m), *(first + m2)) ? m : m2;
  242. }
  243. if (Call()(comp, *(first + m), val))
  244. {
  245. *(first + p) = std::move(*(first + m));
  246. if (m >= 3 && p <= ((m - 3) >> 2)) // is p grandparent of m
  247. {
  248. diff_t<It> par = (m - 1) >> 1;
  249. if (Call()(comp, *(first + par), val))
  250. {
  251. using std::swap;
  252. swap(*(first + par), val);
  253. }
  254. }
  255. p = m;
  256. }
  257. }
  258. *(first + p) = std::move(val);
  259. }
  260. template <typename MinCall, typename MaxCall, typename It, typename Compare>
  261. inline void pop_heap(It first, It el, It last, Compare comp)
  262. {
  263. diff_t<It> size = last - first;
  264. if (size < 2)
  265. {
  266. return;
  267. }
  268. --last;
  269. val_t<It> val = std::move(*last);
  270. *last = std::move(*el);
  271. // Ignore the last element
  272. --size;
  273. diff_t<It> p = el - first;
  274. if (level(p) % 2 == 0) // is min level
  275. {
  276. pop_heap1<MinCall>(first, p, size, std::move(val), comp);
  277. }
  278. else
  279. {
  280. pop_heap1<MaxCall>(first, p, size, std::move(val), comp);
  281. }
  282. }
  283. template <typename MinCall, typename MaxCall, typename It, typename Compare>
  284. inline void make_heap(It first, It last, Compare comp)
  285. {
  286. diff_t<It> size = last - first;
  287. diff_t<It> p = size / 2;
  288. if (p <= 0)
  289. {
  290. return;
  291. }
  292. int level_p = level(p - 1);
  293. diff_t<It> level_f = (diff_t<It>(1) << level_p) - 1;
  294. while (p > 0)
  295. {
  296. --p;
  297. val_t<It> val = std::move(*(first + p));
  298. if (level_p % 2 == 0) // is min level
  299. {
  300. pop_heap1<MinCall>(first, p, size, std::move(val), comp);
  301. }
  302. else
  303. {
  304. pop_heap1<MaxCall>(first, p, size, std::move(val), comp);
  305. }
  306. if (p == level_f)
  307. {
  308. --level_p;
  309. level_f >>= 1;
  310. }
  311. }
  312. }
  313. template <typename Call, typename It, typename Compare>
  314. inline bool is_heap(It first, It last, Compare comp)
  315. {
  316. diff_t<It> const size = last - first;
  317. diff_t<It> pow2 = 4;
  318. bool is_min_level = false;
  319. for (diff_t<It> i = 1; i < size; ++i)
  320. {
  321. if (i == pow2 - 1)
  322. {
  323. pow2 *= 2;
  324. is_min_level = ! is_min_level;
  325. }
  326. diff_t<It> const p = (i - 1) >> 1;
  327. if (is_min_level)
  328. {
  329. if (Call()(comp, *(first + p), *(first + i)))
  330. {
  331. return false;
  332. }
  333. }
  334. else
  335. {
  336. if (Call()(comp, *(first + i), *(first + p)))
  337. {
  338. return false;
  339. }
  340. }
  341. if (i >= 3)
  342. {
  343. diff_t<It> const g = (p - 1) >> 1;
  344. if (is_min_level)
  345. {
  346. if (Call()(comp, *(first + i), *(first + g)))
  347. {
  348. return false;
  349. }
  350. }
  351. else
  352. {
  353. if (Call()(comp, *(first + g), *(first + i)))
  354. {
  355. return false;
  356. }
  357. }
  358. }
  359. }
  360. return true;
  361. }
  362. template <typename Call, typename It, typename Compare>
  363. inline It bottom_heap(It first, It last, Compare comp)
  364. {
  365. diff_t<It> const size = last - first;
  366. return size <= 1 ? first :
  367. size == 2 ? (first + 1) :
  368. Call()(comp, *(first + 1), *(first + 2)) ? (first + 2) : (first + 1);
  369. }
  370. } // namespace minmax_heap_detail
  371. template <typename It, typename Compare>
  372. inline void push_minmax_heap(It first, It last, Compare comp)
  373. {
  374. using namespace minmax_heap_detail;
  375. minmax_heap_detail::push_heap<min_call, max_call>(first, last, comp);
  376. }
  377. template <typename It>
  378. inline void push_minmax_heap(It first, It last)
  379. {
  380. using namespace minmax_heap_detail;
  381. minmax_heap_detail::push_heap<min_call, max_call>(first, last, std::less<>());
  382. }
  383. template <typename It, typename Compare>
  384. inline void pop_top_minmax_heap(It first, It last, Compare comp)
  385. {
  386. using namespace minmax_heap_detail;
  387. pop_heap<min_call, max_call>(first, first, last, comp);
  388. }
  389. template <typename It>
  390. inline void pop_top_minmax_heap(It first, It last)
  391. {
  392. using namespace minmax_heap_detail;
  393. pop_heap<min_call, max_call>(first, first, last, std::less<>());
  394. }
  395. template <typename It, typename Compare>
  396. inline void pop_bottom_minmax_heap(It first, It last, Compare comp)
  397. {
  398. using namespace minmax_heap_detail;
  399. It bottom = minmax_heap_detail::bottom_heap<min_call>(first, last, comp);
  400. pop_heap<min_call, max_call>(first, bottom, last, comp);
  401. }
  402. template <typename It>
  403. inline void pop_bottom_minmax_heap(It first, It last)
  404. {
  405. using namespace minmax_heap_detail;
  406. auto&& comp = std::less<>();
  407. It bottom = minmax_heap_detail::bottom_heap<min_call>(first, last, comp);
  408. pop_heap<min_call, max_call>(first, bottom, last, comp);
  409. }
  410. template <typename It, typename Compare>
  411. inline void make_minmax_heap(It first, It last, Compare comp)
  412. {
  413. using namespace minmax_heap_detail;
  414. return minmax_heap_detail::make_heap<min_call, max_call>(first, last, comp);
  415. }
  416. template <typename It>
  417. inline void make_minmax_heap(It first, It last)
  418. {
  419. using namespace minmax_heap_detail;
  420. return minmax_heap_detail::make_heap<min_call, max_call>(first, last, std::less<>());
  421. }
  422. template <typename It, typename Compare>
  423. inline bool is_minmax_heap(It first, It last, Compare comp)
  424. {
  425. using namespace minmax_heap_detail;
  426. return minmax_heap_detail::is_heap<min_call>(first, last, comp);
  427. }
  428. template <typename It>
  429. inline bool is_minmax_heap(It first, It last)
  430. {
  431. using namespace minmax_heap_detail;
  432. return minmax_heap_detail::is_heap<min_call>(first, last, std::less<>());
  433. }
  434. template <typename It, typename Compare>
  435. inline decltype(auto) bottom_minmax_heap(It first, It last, Compare comp)
  436. {
  437. using namespace minmax_heap_detail;
  438. return *minmax_heap_detail::bottom_heap<min_call>(first, last, comp);
  439. }
  440. template <typename It>
  441. inline decltype(auto) bottom_minmax_heap(It first, It last)
  442. {
  443. using namespace minmax_heap_detail;
  444. return *minmax_heap_detail::bottom_heap<min_call>(first, last, std::less<>());
  445. }
  446. }}}} // namespace boost::geometry::index::detail
  447. #endif // BOOST_GEOMETRY_INDEX_DETAIL_MINMAX_HEAP_HPP