From ae0ab14716f9560514379ab80758cff20c6349c8 Mon Sep 17 00:00:00 2001 From: Iliya Katueshenock Date: Tue, 19 Nov 2024 11:05:57 +0100 Subject: [PATCH] Cleanup: BLI: Binary search first_if and last_if utils Cleanup to simplify code by using common terms like _first_ and _last_ in context of predicate applying in a range just like we do for spans and range containers. Pull Request: https://projects.blender.org/blender/blender/pulls/130380 --- source/blender/blenlib/BLI_binary_search.hh | 30 +++++++-- source/blender/blenlib/BLI_index_mask.hh | 9 ++- .../blenlib/BLI_unique_sorted_indices.hh | 9 ++- source/blender/blenlib/intern/index_mask.cc | 6 +- .../blenlib/tests/BLI_binary_search_test.cc | 67 ++++++++----------- source/blender/modifiers/intern/MOD_nodes.cc | 4 +- 6 files changed, 67 insertions(+), 58 deletions(-) diff --git a/source/blender/blenlib/BLI_binary_search.hh b/source/blender/blenlib/BLI_binary_search.hh index 586f0ebe3ca..4f37a1f1c31 100644 --- a/source/blender/blenlib/BLI_binary_search.hh +++ b/source/blender/blenlib/BLI_binary_search.hh @@ -1,4 +1,4 @@ -/* SPDX-FileCopyrightText: 2023 Blender Authors +/* SPDX-FileCopyrightText: 2024 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -20,7 +20,7 @@ namespace blender::binary_search { * range is returned. */ template -int64_t find_predicate_begin(Iterator begin, Iterator end, Predicate &&predicate) +static int64_t first_if(Iterator begin, Iterator end, Predicate &&predicate) { return std::lower_bound(begin, end, @@ -29,10 +29,30 @@ int64_t find_predicate_begin(Iterator begin, Iterator end, Predicate &&predicate begin; } -template -int64_t find_predicate_begin(const Range &range, Predicate &&predicate) +/** + * Find the index of the last element where the predicate is true. The predicate must also be + * true for all previous elements. If the predicate is false for all elements, the -1 is returned. + */ +template +static int64_t last_if(Iterator begin, Iterator end, Predicate &&predicate) { - return find_predicate_begin(range.begin(), range.end(), predicate); + return std::upper_bound(begin, + end, + nullptr, + [&](void * /*dummy*/, const auto &value) { return !predicate(value); }) - + begin - 1; +} + +template +int64_t first_if(const Range &range, Predicate &&predicate) +{ + return first_if(range.begin(), range.end(), predicate); +} + +template +int64_t last_if(const Range &range, Predicate &&predicate) +{ + return last_if(range.begin(), range.end(), predicate); } } // namespace blender::binary_search diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index bad37aa1c2f..5075fd3d1ed 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -727,11 +727,10 @@ inline RawMaskIterator IndexMask::index_to_iterator(const int64_t index) const BLI_assert(index < indices_num_); RawMaskIterator it; const int64_t full_index = index + cumulative_segment_sizes_[0] + begin_index_in_segment_; - it.segment_i = -1 + - binary_search::find_predicate_begin( - cumulative_segment_sizes_, - cumulative_segment_sizes_ + segments_num_ + 1, - [&](const int64_t cumulative_size) { return cumulative_size > full_index; }); + it.segment_i = binary_search::last_if( + cumulative_segment_sizes_, + cumulative_segment_sizes_ + segments_num_ + 1, + [&](const int64_t cumulative_size) { return cumulative_size <= full_index; }); it.index_in_segment = full_index - cumulative_segment_sizes_[it.segment_i]; return it; } diff --git a/source/blender/blenlib/BLI_unique_sorted_indices.hh b/source/blender/blenlib/BLI_unique_sorted_indices.hh index 06a096abc5a..c36cc2c4ee6 100644 --- a/source/blender/blenlib/BLI_unique_sorted_indices.hh +++ b/source/blender/blenlib/BLI_unique_sorted_indices.hh @@ -71,11 +71,10 @@ template inline std::optional non_empty_as_range_try(con template inline int64_t find_size_of_next_range(const Span indices) { BLI_assert(!indices.is_empty()); - return binary_search::find_predicate_begin(indices, - [indices, offset = indices[0]](const T &value) { - const int64_t index = &value - indices.begin(); - return value - offset > index; - }); + return binary_search::first_if(indices, [indices, offset = indices[0]](const T &value) { + const int64_t index = &value - indices.begin(); + return value - offset > index; + }); } /** diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc index a0aade1427c..730ef73dd26 100644 --- a/source/blender/blenlib/intern/index_mask.cc +++ b/source/blender/blenlib/intern/index_mask.cc @@ -354,7 +354,7 @@ static void segments_from_indices(const Span indices, segment_indices.size()); while (!segment_indices.is_empty()) { const int64_t offset = segment_indices[0]; - const int64_t next_segment_size = binary_search::find_predicate_begin( + const int64_t next_segment_size = binary_search::first_if( segment_indices.take_front(max_segment_size), [&](const T value) { return value - offset >= max_segment_size; }); for (const int64_t i : IndexRange(next_segment_size)) { @@ -836,7 +836,7 @@ std::optional IndexMask::find(const int64_t query_index) const std::optional IndexMask::find_larger_equal(const int64_t query_index) const { - const int64_t segment_i = binary_search::find_predicate_begin( + const int64_t segment_i = binary_search::first_if( IndexRange(segments_num_), [&](const int64_t seg_i) { return this->segment(seg_i).last() >= query_index; }); if (segment_i == segments_num_) { @@ -853,7 +853,7 @@ std::optional IndexMask::find_larger_equal(const int64_t query_ } /* The query index is somewhere within this segment. */ const int64_t local_index = query_index - segment.offset(); - const int64_t index_in_segment = binary_search::find_predicate_begin( + const int64_t index_in_segment = binary_search::first_if( segment.base_span(), [&](const int16_t i) { return i >= local_index; }); const int64_t actual_index_in_segment = index_in_segment + segment_begin_index; BLI_assert(actual_index_in_segment < max_segment_size); diff --git a/source/blender/blenlib/tests/BLI_binary_search_test.cc b/source/blender/blenlib/tests/BLI_binary_search_test.cc index 5503f569afa..54237d77e9d 100644 --- a/source/blender/blenlib/tests/BLI_binary_search_test.cc +++ b/source/blender/blenlib/tests/BLI_binary_search_test.cc @@ -1,61 +1,52 @@ -/* SPDX-FileCopyrightText: 2023 Blender Authors +/* SPDX-FileCopyrightText: 2024 Blender Authors * * SPDX-License-Identifier: Apache-2.0 */ #include "BLI_binary_search.hh" +#include "BLI_span.hh" #include "BLI_vector.hh" #include "testing/testing.h" namespace blender::binary_search::tests { +static bool value_pass(const bool value) +{ + return value; +} + TEST(binary_search, Empty) { - const Vector vec; - const int64_t index = find_predicate_begin(vec, [](const int /*value*/) { return true; }); - EXPECT_EQ(index, 0); + EXPECT_EQ(first_if(Span{}, value_pass), 0); + EXPECT_EQ(last_if(Span{}, value_pass), -1); } TEST(binary_search, One) { - const Vector vec = {5}; - { - const int64_t index = find_predicate_begin(vec, [](const int /*value*/) { return false; }); - EXPECT_EQ(index, 1); - } - { - const int64_t index = find_predicate_begin(vec, [](const int /*value*/) { return true; }); - EXPECT_EQ(index, 0); - } + EXPECT_EQ(first_if(Span{true}, value_pass), 0); + EXPECT_EQ(last_if(Span{true}, value_pass), 0); + + EXPECT_EQ(first_if(Span{false}, value_pass), 1); + EXPECT_EQ(last_if(Span{false}, value_pass), -1); } TEST(binary_search, Multiple) { - const Vector vec{4, 5, 7, 9, 10, 20, 30}; - { - const int64_t index = find_predicate_begin(vec, [](const int value) { return value > 0; }); - EXPECT_EQ(index, 0); - } - { - const int64_t index = find_predicate_begin(vec, [](const int value) { return value > 4; }); - EXPECT_EQ(index, 1); - } - { - const int64_t index = find_predicate_begin(vec, [](const int value) { return value > 10; }); - EXPECT_EQ(index, 5); - } - { - const int64_t index = find_predicate_begin(vec, [](const int value) { return value >= 25; }); - EXPECT_EQ(index, 6); - } - { - const int64_t index = find_predicate_begin(vec, [](const int value) { return value >= 30; }); - EXPECT_EQ(index, 6); - } - { - const int64_t index = find_predicate_begin(vec, [](const int value) { return value > 30; }); - EXPECT_EQ(index, 7); - } + EXPECT_EQ(first_if(Span{true, true, true, true, true, true}, value_pass), 0); + EXPECT_EQ(first_if(Span{false, true, true, true, true, true}, value_pass), 1); + EXPECT_EQ(first_if(Span{false, false, true, true, true, true}, value_pass), 2); + EXPECT_EQ(first_if(Span{false, false, false, true, true, true}, value_pass), 3); + EXPECT_EQ(first_if(Span{false, false, false, false, true, true}, value_pass), 4); + EXPECT_EQ(first_if(Span{false, false, false, false, false, true}, value_pass), 5); + EXPECT_EQ(first_if(Span{false, false, false, false, false, false}, value_pass), 6); + + EXPECT_EQ(last_if(Span{false, false, false, false, false, false}, value_pass), -1); + EXPECT_EQ(last_if(Span{true, false, false, false, false, false}, value_pass), 0); + EXPECT_EQ(last_if(Span{true, true, false, false, false, false}, value_pass), 1); + EXPECT_EQ(last_if(Span{true, true, true, false, false, false}, value_pass), 2); + EXPECT_EQ(last_if(Span{true, true, true, true, false, false}, value_pass), 3); + EXPECT_EQ(last_if(Span{true, true, true, true, true, false}, value_pass), 4); + EXPECT_EQ(last_if(Span{true, true, true, true, true, true}, value_pass), 5); } } // namespace blender::binary_search::tests diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 79239c92717..5d3e550e7df 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -965,7 +965,7 @@ static BakeFrameIndices get_bake_frame_indices( { BakeFrameIndices frame_indices; if (!frame_caches.is_empty()) { - const int first_future_frame_index = binary_search::find_predicate_begin( + const int first_future_frame_index = binary_search::first_if( frame_caches, [&](const std::unique_ptr &value) { return value->frame > frame; }); frame_indices.next = (first_future_frame_index == frame_caches.size()) ? @@ -1505,7 +1505,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams { frame_cache->frame = current_frame; frame_cache->state = std::move(state); auto &frames = node_cache->bake.frames; - const int insert_index = binary_search::find_predicate_begin( + const int insert_index = binary_search::first_if( frames, [&](const std::unique_ptr &frame_cache) { return frame_cache->frame > current_frame; });