/**************************************************************************** * Copyright (c) 2017-2022 by the ArborX authors * * All rights reserved. * * * * This file is part of the ArborX library. ArborX is * * distributed under a BSD 3-clause license. For the licensing terms see * * the LICENSE file in the top-level directory. * * * * SPDX-License-Identifier: BSD-3-Clause * ****************************************************************************/ #include "ArborX_EnableViewComparison.hpp" #include #include // Less, Greater #include #include using namespace ArborX::Details; namespace tt = boost::test_tools; BOOST_AUTO_TEST_SUITE(HeapOperations) BOOST_AUTO_TEST_CASE(push_heap) { // Here checking against the example of binary heap insertion from // https://en.wikipedia.org/wiki/Binary_heap#Insert Kokkos::Array a = {11, 5, 8, 3, 4, 15}; BOOST_TEST(isHeap(a.data(), a.data() + 5, Less())); BOOST_TEST(!isHeap(a.data(), a.data() + 6, Less())); pushHeap(a.data(), a.data() + 6, Less()); BOOST_TEST(isHeap(a.data(), a.data() + 6, Less())); Kokkos::Array ref = {15, 5, 11, 3, 4, 8}; BOOST_TEST(a == ref, tt::per_element()); } BOOST_AUTO_TEST_CASE(pop_heap) { // See https://en.wikipedia.org/wiki/Binary_heap#Extract Kokkos::Array a = {11, 5, 8, 3, 4}; BOOST_TEST(isHeap(a.data(), a.data() + 5, Less())); popHeap(a.data(), a.data() + 5, Less()); BOOST_TEST(isHeap(a.data(), a.data() + 4, Less())); BOOST_TEST(!isHeap(a.data(), a.data() + 5, Less())); Kokkos::Array ref = {8, 5, 4, 3, 11}; BOOST_TEST(a == ref, tt::per_element()); } BOOST_AUTO_TEST_CASE(max_heap) { // Here attempting to reproduce examples code from cppreference.com for // std::push_heap() and std::pop_heap(). It turns out that calling // std::make_heap() on { 3, 1, 4, 1, 5, 9 } is not equivalent to inserting // one element at a time (I also tried to insert them in reverse order out // of curiosity) and yields 9 5 4 1 1 3 :/ // cpluplus.com does make a note that the order of the elements will depend // on implementation. // Nevertheless, I thought it helps understanding how the algorithm works so // I decided to keep it as a unit test. Kokkos::Array a = {3, 1, 4, 1, 5, 9, 6}; Kokkos::Array ref; pushHeap(a.data(), a.data() + 0, Less()); ref = {3, 1, 4, 1, 5, 9, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 1, Less()); ref = {3, 1, 4, 1, 5, 9, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 2, Less()); ref = {3, 1, 4, 1, 5, 9, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 3, Less()); ref = {4, 1, 3, 1, 5, 9, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 4, Less()); ref = {4, 1, 3, 1, 5, 9, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 5, Less()); ref = {5, 4, 3, 1, 1, 9, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 6, Less()); ref = {9, 4, 5, 1, 1, 3, 6}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 6, Less()); ref = {5, 4, 3, 1, 1, 9, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 6, Less()); ref = {9, 4, 5, 1, 1, 3, 6}; BOOST_TEST(a == ref, tt::per_element()); pushHeap(a.data(), a.data() + 7, Less()); ref = {9, 4, 6, 1, 1, 3, 5}; BOOST_TEST(a == ref, tt::per_element()); } BOOST_AUTO_TEST_CASE(min_heap) { // Here reproducing the example of a binary min heap from Wikipedia and then // popping all elements one by one. // This helped resolving an issue with ProprityQueue::pop() that was not // exposed by other unit tests in this file, partly because they were too // trivial. The bug was only showing with real kNN search problems when // comparing results from BoundingVolumeHierarchy with boost::rtree. Kokkos::Array a = {1, 2, 3, 17, 19, 36, 7, 25, 100}; Kokkos::Array ref; for (int i = 0; i < 9; ++i) { pushHeap(a.data(), a.data() + i, Greater()); ref = {1, 2, 3, 17, 19, 36, 7, 25, 100}; BOOST_TEST(a == ref, tt::per_element()); } popHeap(a.data(), a.data() + 9, Greater()); ref = {2, 17, 3, 25, 19, 36, 7, 100, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 8, Greater()); ref = {3, 17, 7, 25, 19, 36, 100, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 7, Greater()); ref = {7, 17, 36, 25, 19, 100, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 6, Greater()); ref = {17, 19, 36, 25, 100, 7, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 5, Greater()); ref = {19, 25, 36, 100, 17, 7, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 4, Greater()); ref = {25, 100, 36, 19, 17, 7, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 3, Greater()); ref = {36, 100, 25, 19, 17, 7, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 2, Greater()); ref = {100, 36, 25, 19, 17, 7, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 1, Greater()); ref = {100, 36, 25, 19, 17, 7, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); popHeap(a.data(), a.data() + 0, Greater()); ref = {100, 36, 25, 19, 17, 7, 3, 2, 1}; BOOST_TEST(a == ref, tt::per_element()); } BOOST_AUTO_TEST_CASE(make_heap) { for (auto v : { std::vector{}, std::vector{6}, std::vector{2, 1}, std::vector{1, 6, 2, 2, 9, 4, 16}, std::vector{8, 6, 7, 2, 0}, std::vector{3, 3, 3, 3, 3, 1}, std::vector{36, 19, 25, 17, 3, 7, 1, 2, 9}, // <- already a heap }) { makeHeap(v.data(), v.data() + v.size(), Less()); BOOST_TEST(std::is_heap(v.begin(), v.end())); } } BOOST_AUTO_TEST_CASE(sort_heap) { for (auto heap : {std::vector{}, std::vector{3}, std::vector{36, 19, 25, 17, 3, 7, 1, 2, 9}, std::vector{36, 19, 25, 17, 3, 9, 1, 2, 7}, std::vector{100, 19, 36, 17, 3, 25, 1, 2, 7}, std::vector{15, 5, 11, 3, 4, 8}}) { sortHeap(heap.data(), heap.data() + heap.size(), Less()); // std::sort_heap( heap.begin(), heap.end() ); BOOST_TEST(std::is_sorted(heap.begin(), heap.end())); } } BOOST_AUTO_TEST_CASE(is_heap) { for (auto heap : {std::vector{36, 19, 25, 17, 3, 7, 1, 2, 9}, std::vector{36, 19, 25, 17, 3, 9, 1, 2, 7}, std::vector{100, 19, 36, 17, 3, 25, 1, 2, 7}, std::vector{15, 5, 11, 3, 4, 8}}) { BOOST_TEST(isHeap(heap.data(), heap.data() + heap.size(), Less())); } for (auto not_heap : {std::vector{0, 1, 2, 3, 4, 3, 2, 1, 0}, std::vector{2, 1, 0, 1, 2}}) { BOOST_TEST(!isHeap(not_heap.data(), not_heap.data() + not_heap.size(), Less())); } } BOOST_AUTO_TEST_SUITE_END()