mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-10 12:51:57 -10:00
AK+LibJS+LibWeb: Make binary_search() return the first occurrence
Given `{1, 2, 2, 2, 2, 2, 2, 3}` and the needle being `2`, the function
used to return the first matching element that it found (at index 3). It
now continues the search until it founds the first one, here at index 1.
Note that one side effect of that change is that when the binary search
fails, `nearby_index` now gives you the index right after instead of the
one before. The changes in LibJS and LibWeb are needed because of that.
Co-authored-by: Nico Weber <[email protected]>
This commit is contained in:
1 parent
0954c95a5c
commit
d012289263
4 files changed
+28
-18
No files matched your search
+7
-16
@@ -41,28 +41,19 @@ constexpr auto binary_search(
|
||||
|
||||
size_t low = 0;
|
||||
size_t high = haystack.size() - 1;
|
||||
while (low <= high) {
|
||||
while (low != high) {
|
||||
size_t middle = low + (high - low) / 2;
|
||||
|
||||
int comparison = comparator(needle, haystack[middle]);
|
||||
|
||||
if (comparison < 0)
|
||||
if (middle != 0)
|
||||
high = middle - 1;
|
||||
else
|
||||
break;
|
||||
else if (comparison > 0)
|
||||
if (comparison <= 0)
|
||||
high = middle;
|
||||
else
|
||||
low = middle + 1;
|
||||
else {
|
||||
if (nearby_index)
|
||||
*nearby_index = middle;
|
||||
return &haystack[middle];
|
||||
}
|
||||
}
|
||||
|
||||
if (nearby_index)
|
||||
*nearby_index = min(low, high);
|
||||
|
||||
*nearby_index = high;
|
||||
if (comparator(needle, haystack[high]) == 0)
|
||||
return &haystack[high];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,19 @@ TEST_CASE(unsigned_to_signed_regression)
|
||||
EXPECT_EQ(nearby_index, 1u);
|
||||
}
|
||||
|
||||
TEST_CASE(always_find_first_occurrence)
|
||||
{
|
||||
Array const input { 1, 2, 2, 2, 2, 2, 2, 3 };
|
||||
|
||||
size_t nearby_index {};
|
||||
EXPECT_EQ(binary_search(input, 1, &nearby_index), input.data());
|
||||
EXPECT_EQ(nearby_index, 0u);
|
||||
EXPECT_EQ(binary_search(input, 2, &nearby_index), input.data() + 1);
|
||||
EXPECT_EQ(nearby_index, 1u);
|
||||
EXPECT_EQ(binary_search(input, 3, &nearby_index), input.data() + 7);
|
||||
EXPECT_EQ(nearby_index, 7u);
|
||||
}
|
||||
|
||||
RANDOMIZED_TEST_CASE(finds_number_that_is_present)
|
||||
{
|
||||
GEN(vec, Gen::vector(1, 16, []() { return Gen::number_u64(); }));
|
||||
|
||||
@@ -87,6 +87,8 @@ SourceRange SourceCode::range_from_offsets(u32 start_offset, u32 end_offset) con
|
||||
Position current { .line = 1, .column = 1, .offset = 0 };
|
||||
|
||||
if (!m_cached_positions.is_empty()) {
|
||||
// FIXME: Use AK::lower_bound instead of relying on the behavior
|
||||
// of nearby_index.
|
||||
Position const dummy;
|
||||
size_t nearest_index = 0;
|
||||
binary_search(m_cached_positions, dummy, &nearest_index,
|
||||
@@ -94,6 +96,9 @@ SourceRange SourceCode::range_from_offsets(u32 start_offset, u32 end_offset) con
|
||||
return start_offset - starting_point.offset;
|
||||
});
|
||||
|
||||
if (nearest_index > 0 && m_cached_positions[nearest_index].offset != start_offset)
|
||||
nearest_index--;
|
||||
|
||||
current = m_cached_positions[nearest_index];
|
||||
}
|
||||
|
||||
|
||||
@@ -156,8 +156,9 @@ double EasingStyleValue::Function::evaluate_at(double input_progress, bool befor
|
||||
}
|
||||
|
||||
// We have two samples on either side of the x value we want, so we can linearly interpolate between them.
|
||||
auto& sample1 = cached_x_samples[nearby_index];
|
||||
auto& sample2 = cached_x_samples[nearby_index + 1];
|
||||
VERIFY(nearby_index > 0);
|
||||
auto& sample1 = cached_x_samples[nearby_index - 1];
|
||||
auto& sample2 = cached_x_samples[nearby_index];
|
||||
auto factor = (x - sample1.x) / (sample2.x - sample1.x);
|
||||
return sample1.y + factor * (sample2.y - sample1.y);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user