Files
clang-p2996/libcxx/test/std/ranges/range.adaptors/range.drop/begin.pass.cpp
Iuri Chaer a0662176a9 [libc++] Speed up set_intersection() by fast-forwarding over ranges of non-matching elements with one-sided binary search. (#75230)
One-sided binary search, aka meta binary search, has been in the public
domain for decades, and has the general advantage of being constant time
in the best case, with the downside of executing at most 2*log(N)
comparisons vs classic binary search's exact log(N). There are two
scenarios in which it really shines: the first one is when operating
over non-random-access iterators, because the classic algorithm requires
knowing the container's size upfront, which adds N iterator increments
to the complexity. The second one is when traversing the container in
order, trying to fast-forward to the next value: in that case the
classic algorithm requires at least O(N*log(N)) comparisons and, for
non-random-access iterators, O(N^2) iterator increments, whereas the
one-sided version will yield O(N) operations on both counts, with a
best-case of O(log(N)) comparisons which is very common in practice.
2024-07-18 16:11:24 -04:00

162 lines
5.9 KiB
C++

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// constexpr auto begin()
// requires (!(simple-view<V> &&
// random_access_range<const V> && sized_range<const V>));
// constexpr auto begin() const
// requires random_access_range<const V> && sized_range<const V>;
#include <ranges>
#include "test_macros.h"
#include "test_iterators.h"
#include "test_range.h"
#include "types.h"
template<class T>
concept BeginInvocable = requires(std::ranges::drop_view<T> t) { t.begin(); };
template <bool IsSimple>
struct MaybeSimpleView : std::ranges::view_base {
int* num_of_non_const_begin_calls;
int* num_of_const_begin_calls;
constexpr int* begin() {
++(*num_of_non_const_begin_calls);
return nullptr;
}
constexpr std::conditional_t<IsSimple, int*, const int*> begin() const {
++(*num_of_const_begin_calls);
return nullptr;
}
constexpr int* end() const { return nullptr; }
constexpr std::size_t size() const { return 0; }
};
using SimpleView = MaybeSimpleView<true>;
using NonSimpleView = MaybeSimpleView<false>;
constexpr bool test() {
// random_access_range<const V> && sized_range<const V>
std::ranges::drop_view dropView1(MoveOnlyView(), 4);
assert(dropView1.begin() == globalBuff + 4);
// !random_access_range<const V>
std::ranges::drop_view dropView2(ForwardView(), 4);
assert(base(dropView2.begin()) == globalBuff + 4);
// !random_access_range<const V>
std::ranges::drop_view dropView3(InputView(), 4);
assert(base(dropView3.begin()) == globalBuff + 4);
// random_access_range<const V> && sized_range<const V>
std::ranges::drop_view dropView4(MoveOnlyView(), 8);
assert(dropView4.begin() == globalBuff + 8);
// random_access_range<const V> && sized_range<const V>
std::ranges::drop_view dropView5(MoveOnlyView(), 0);
assert(dropView5.begin() == globalBuff);
// random_access_range<const V> && sized_range<const V>
const std::ranges::drop_view dropView6(MoveOnlyView(), 0);
assert(dropView6.begin() == globalBuff);
// random_access_range<const V> && sized_range<const V>
std::ranges::drop_view dropView7(MoveOnlyView(), 10);
assert(dropView7.begin() == globalBuff + 8);
IteratorOpCounts opcounts;
CountedView view8(&opcounts);
;
std::ranges::drop_view dropView8(view8, 5);
assert(base(base(dropView8.begin())) == globalBuff + 5);
assert(opcounts.increments == 5);
static_assert(!BeginInvocable<const ForwardView>);
{
// non-common non-simple view,
// The wording of the standard is:
// Returns: ranges::next(ranges::begin(base_), count_, ranges::end(base_))
// Note that "Returns" is used here, meaning that we don't have to do it this way.
// In fact, this will use ranges::advance that has O(n) on non-common range.
// but [range.range] requires "amortized constant time" for ranges::begin and ranges::end
// Here, we test that begin() is indeed constant time, by creating a customized
// sentinel and counting how many times the sentinel eq function is called.
// It should be 0 times, but since this test (or any test under libcxx/test/std) is
// also used by other implementations, we relax the condition to that
// sentinel_cmp_calls is a constant number.
int sentinel_cmp_calls_1 = 0;
int sentinel_cmp_calls_2 = 0;
using NonCommonView = MaybeSimpleNonCommonView<false>;
static_assert(std::ranges::random_access_range<NonCommonView>);
static_assert(std::ranges::sized_range<NonCommonView>);
std::ranges::drop_view dropView9_1(NonCommonView{{}, 0, &sentinel_cmp_calls_1}, 4);
std::ranges::drop_view dropView9_2(NonCommonView{{}, 0, &sentinel_cmp_calls_2}, 6);
assert(dropView9_1.begin() == globalBuff + 4);
assert(dropView9_2.begin() == globalBuff + 6);
assert(sentinel_cmp_calls_1 == sentinel_cmp_calls_2);
}
{
// non-common simple view, same as above.
int sentinel_cmp_calls_1 = 0;
int sentinel_cmp_calls_2 = 0;
using NonCommonView = MaybeSimpleNonCommonView<true>;
static_assert(std::ranges::random_access_range<NonCommonView>);
static_assert(std::ranges::sized_range<NonCommonView>);
std::ranges::drop_view dropView10_1(NonCommonView{{}, 0, &sentinel_cmp_calls_1}, 4);
std::ranges::drop_view dropView10_2(NonCommonView{{}, 0, &sentinel_cmp_calls_2}, 6);
assert(dropView10_1.begin() == globalBuff + 4);
assert(dropView10_2.begin() == globalBuff + 6);
assert(sentinel_cmp_calls_1 == sentinel_cmp_calls_2);
}
{
static_assert(std::ranges::random_access_range<const SimpleView>);
static_assert(std::ranges::sized_range<const SimpleView>);
static_assert(simple_view<SimpleView>);
int non_const_calls = 0;
int const_calls = 0;
std::ranges::drop_view dropView(SimpleView{{}, &non_const_calls, &const_calls}, 4);
assert(dropView.begin() == nullptr);
assert(non_const_calls == 0);
assert(const_calls == 1);
assert(std::as_const(dropView).begin() == nullptr);
assert(non_const_calls == 0);
assert(const_calls == 2);
}
{
static_assert(std::ranges::random_access_range<const NonSimpleView>);
static_assert(std::ranges::sized_range<const NonSimpleView>);
static_assert(!simple_view<NonSimpleView>);
int non_const_calls = 0;
int const_calls = 0;
std::ranges::drop_view dropView(NonSimpleView{{}, &non_const_calls, &const_calls}, 4);
assert(dropView.begin() == nullptr);
assert(non_const_calls == 1);
assert(const_calls == 0);
assert(std::as_const(dropView).begin() == nullptr);
assert(non_const_calls == 1);
assert(const_calls == 1);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}