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:
committed by
Jacques Lucke
parent
e16cc94c5c
commit
ae0ab14716
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user