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
This commit is contained in:
Iliya Katueshenock
2024-11-19 11:05:57 +01:00
committed by Jacques Lucke
parent e16cc94c5c
commit ae0ab14716
6 changed files with 67 additions and 58 deletions

View File

@@ -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<typename Iterator, typename Predicate>
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<typename Range, typename Predicate>
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<typename Iterator, typename Predicate>
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<typename Range, typename Predicate>
int64_t first_if(const Range &range, Predicate &&predicate)
{
return first_if(range.begin(), range.end(), predicate);
}
template<typename Range, typename Predicate>
int64_t last_if(const Range &range, Predicate &&predicate)
{
return last_if(range.begin(), range.end(), predicate);
}
} // namespace blender::binary_search

View File

@@ -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;
}

View File

@@ -71,11 +71,10 @@ template<typename T> inline std::optional<IndexRange> non_empty_as_range_try(con
template<typename T> inline int64_t find_size_of_next_range(const Span<T> 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;
});
}
/**

View File

@@ -354,7 +354,7 @@ static void segments_from_indices(const Span<T> 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<RawMaskIterator> IndexMask::find(const int64_t query_index) const
std::optional<RawMaskIterator> 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<RawMaskIterator> 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);

View File

@@ -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<int> vec;
const int64_t index = find_predicate_begin(vec, [](const int /*value*/) { return true; });
EXPECT_EQ(index, 0);
EXPECT_EQ(first_if(Span<bool>{}, value_pass), 0);
EXPECT_EQ(last_if(Span<bool>{}, value_pass), -1);
}
TEST(binary_search, One)
{
const Vector<int> 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<int> 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

View File

@@ -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<bake::FrameCache> &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<bake::FrameCache> &frame_cache) {
return frame_cache->frame > current_frame;
});