diff --git a/include/aare/algorithm.hpp b/include/aare/algorithm.hpp index 288bec5..e6f5537 100644 --- a/include/aare/algorithm.hpp +++ b/include/aare/algorithm.hpp @@ -125,4 +125,58 @@ inline double linear_interpolation(const std::pair &bin_edge, bin_values.second * (coord - bin_edge.first) / bin_width; } +/// @brief XOR operator +inline bool XOR(const bool a, const bool b) { return (a || b) && !(a && b); } + +/// @brief range struct +template struct range { + /// @brief start of the range + Iterator start{}; + /// @brief end of the range + Iterator end{}; +}; + +/** + * @brief partition range of elements into contiguous subranges for which the + * partition criteria is the same. + * @param start pointer to the first element of the range + * @param end pointer to the last element of the range + * @param partition_criteria function that returns true or false for a given + * element + * @return vector of ranges that are contiguous and have the same partition + * criteria + */ +template +std::vector> partition( + Iterator start, Iterator end, + std::function::value_type + &)> &partition_criteria) { + std::vector> partitions; + partitions.reserve(std::distance(start, end)); + + auto partition_start = start; + auto partition_end = end; + + bool chunk_fulfills_criteria = partition_criteria( + *partition_start); // starts with range fulfilling criteria + + auto chunk_criteria = + [&partition_criteria, &chunk_fulfills_criteria]( + const typename std::iterator_traits::value_type &value) { + return XOR(partition_criteria(value), chunk_fulfills_criteria); + }; // exclusive or + + while (partition_start < end) { + partition_end = std::find_if(partition_start, end, chunk_criteria); + + partitions.push_back({partition_start, partition_end}); + partition_start = partition_end; + + chunk_fulfills_criteria = + !chunk_fulfills_criteria; // flip criteria for next chunk + } + + return partitions; +} + } // namespace aare \ No newline at end of file diff --git a/src/algorithm.test.cpp b/src/algorithm.test.cpp index daa0616..69d7bf7 100644 --- a/src/algorithm.test.cpp +++ b/src/algorithm.test.cpp @@ -232,3 +232,80 @@ TEST_CASE("Bilinear interpolation", "[algorithm]") { REQUIRE(interpolated_value == 5.25); } } + +TEST_CASE("partition range for vector", "[algorithm]") { + + std::function partition_criteria = + [](const bool &value) { return value; }; + + SECTION("partition criteria is always false") { + std::vector vec = {false, false, false, false, false}; + auto partitions = + aare::partition(vec.begin(), vec.end(), partition_criteria); + REQUIRE(partitions.size() == 1); + REQUIRE(partitions[0].start == vec.begin()); + REQUIRE(partitions[0].end == vec.end()); + } + + SECTION("partition criteria is always true") { + std::vector vec = {true, true, true, true, true}; + auto partitions = + aare::partition(vec.begin(), vec.end(), partition_criteria); + REQUIRE(partitions.size() == 1); + REQUIRE(partitions[0].start == vec.begin()); + REQUIRE(partitions[0].end == vec.end()); + } + + SECTION("range starts with partition criteria true") { + std::vector vec = {true, true, false, false, true, true}; + auto partitions = + aare::partition(vec.begin(), vec.end(), partition_criteria); + REQUIRE(partitions.size() == 3); + REQUIRE(partitions[0].start == vec.begin()); + REQUIRE(partitions[0].end == vec.begin() + 2); + REQUIRE(partitions[1].start == vec.begin() + 2); + REQUIRE(partitions[1].end == vec.begin() + 4); + REQUIRE(partitions[2].start == vec.begin() + 4); + REQUIRE(partitions[2].end == vec.end()); + } + + SECTION("range starts with partition criteria false") { + std::vector vec = {false, false, true, true, false}; + auto partitions = + aare::partition(vec.begin(), vec.end(), partition_criteria); + REQUIRE(partitions.size() == 3); + REQUIRE(partitions[0].start == vec.begin()); + REQUIRE(partitions[0].end == vec.begin() + 2); + REQUIRE(partitions[1].start == vec.begin() + 2); + REQUIRE(partitions[1].end == vec.begin() + 4); + REQUIRE(partitions[2].start == vec.begin() + 4); + REQUIRE(partitions[2].end == vec.end()); + } + SECTION("empty vector") { + std::vector vec = {}; + auto partitions = + aare::partition(vec.begin(), vec.end(), partition_criteria); + REQUIRE(partitions.size() == 0); + } +} + +TEST_CASE("partition range for NDArray", "[algorithm]") { + + std::function partition_criteria = + [](const bool &value) { return value; }; + + aare::NDArray array( + std::array{true, true, false, true, true, false, false}); + + auto partitions = + aare::partition(array.begin(), array.end(), partition_criteria); + REQUIRE(partitions.size() == 4); + REQUIRE(partitions[0].start == array.begin()); + REQUIRE(partitions[0].end == array.begin() + 2); + REQUIRE(partitions[1].start == array.begin() + 2); + REQUIRE(partitions[1].end == array.begin() + 3); + REQUIRE(partitions[2].start == array.begin() + 3); + REQUIRE(partitions[2].end == array.begin() + 5); + REQUIRE(partitions[3].start == array.begin() + 5); + REQUIRE(partitions[3].end == array.end()); +}