diff --git a/libcxx/test/support/test_algorithm.h b/libcxx/test/support/test_algorithm.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/test_algorithm.h @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#ifndef LIBCXX_TEST_SUPPORT_TEST_ALGORITHM_H +#define LIBCXX_TEST_SUPPORT_TEST_ALGORITHM_H + +#include "test_macros.h" + +#if TEST_STD_VER < 20 +#error This file requires -std=c++20 or later. +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "counting_predicates.h" +#include "test_iterators.h" + +template +class complexity_invocable { +public: + constexpr complexity_invocable(F f) + : f_(std::move(f)) + {} + + template + requires std::invocable + constexpr decltype(auto) operator()(Args&&... args) noexcept(std::is_nothrow_invocable_v) + { + ++count_; + return std::invoke(f_, std::forward(args)...); + } + + constexpr std::ptrdiff_t count() const + { + return count_; + } +private: + F f_; + std::ptrdiff_t count_ = 0; +}; + +// This must be defined in each test case. +template +constexpr void check_complexity(R&& r, std::ptrdiff_t x); + +template class I, std::ranges::range R, class Case> +constexpr void test_algorithm(R&& r, Case test_case) { + auto input = std::ranges::subrange(complexity_iterator(I(r.begin())), sentinel_wrapper(r.end())); + test_case(input); + + check_complexity(r, input.begin().count()); +} + +template class I, std::ranges::range R, class F1, class Case> +constexpr void test_algorithm(R&& r, F1 f1, Case test_case) { + auto input = std::ranges::subrange(I(r.begin()), sentinel_wrapper(r.end())); + auto instrumented_f1 = complexity_invocable(f1); + test_case(input, instrumented_f1); + + check_complexity(r, instrumented_f1.count()); +} + +template class I, std::ranges::range R, class F1, class Proj, class Case> +constexpr void test_algorithm(R&& r, F1 f1, Proj proj, Case test_case) { + auto input = std::ranges::subrange(I(r.begin()), sentinel_wrapper(r.end())); + auto instrumented_f1 = complexity_invocable(f1); + auto instrumented_proj = complexity_invocable(proj); + test_case(input, instrumented_f1, instrumented_proj); + + check_complexity(r, instrumented_f1.count()); + check_complexity(r, instrumented_proj.count()); +} + +constexpr std::ptrdiff_t libcxx_log2(std::ptrdiff_t const x) { + assert(x > 0); + auto n = std::ptrdiff_t(0); + for (auto i = std::ptrdiff_t(1); i <= x; i *= 2) { + ++n; + } + return n - 1; +} +static_assert(libcxx_log2(1) == 0); +static_assert(libcxx_log2(2) == 1); +static_assert(libcxx_log2(3) == 1); +static_assert(libcxx_log2(4) == 2); +static_assert(libcxx_log2(6) == 2); +static_assert(libcxx_log2(7) == 2); +static_assert(libcxx_log2(8) == 3); +static_assert(libcxx_log2(13) == 3); +static_assert(libcxx_log2(15) == 3); +static_assert(libcxx_log2(16) == 4); +static_assert(libcxx_log2(22) == 4); +static_assert(libcxx_log2(31) == 4); +static_assert(libcxx_log2(32) == 5); +static_assert(libcxx_log2(54) == 5); +static_assert(libcxx_log2(63) == 5); + +#endif // LIBCXX_TEST_SUPPORT_TEST_ALGORITHM_H diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -931,6 +931,158 @@ friend auto operator<=>(const three_way_contiguous_iterator&, const three_way_contiguous_iterator&) = default; }; +template +class complexity_iterator { +public: + using value_type = std::iter_value_t; + using difference_type = std::iter_difference_t; + using iterator_concept = iterator_concept_t; + + constexpr complexity_iterator() requires std::default_initializable = default; + + constexpr explicit complexity_iterator(I i) + : base_(std::move(i)) + {} + + [[nodiscard]] constexpr difference_type count() const + { + return dereference_count_; + } + + constexpr I const& base() const& + { + return base_; + } + + constexpr I base() && + { + return std::move(base_); + } + + constexpr std::iter_reference_t operator*() const + { + ++dereference_count_; + return *base_; + } + + constexpr std::iter_reference_t operator[](difference_type const n) const + requires std::random_access_iterator + { + ++dereference_count_; + return base_[n]; + } + + constexpr complexity_iterator& operator++() + { + ++base_; + return *this; + } + + constexpr void operator++(int) { ++*this; } + + constexpr complexity_iterator operator++(int) + requires std::forward_iterator + { + auto temp = *this; + ++*this; + return temp; + } + + constexpr complexity_iterator& operator--() + requires std::bidirectional_iterator + { + --base_; + return *this; + } + + constexpr complexity_iterator operator--(int) + requires std::bidirectional_iterator + { + auto temp = *this; + --*this; + return temp; + } + + constexpr complexity_iterator& operator+=(difference_type const n) + requires std::random_access_iterator + { + base_ += n; + return *this; + } + + constexpr complexity_iterator& operator-=(difference_type const n) + requires std::random_access_iterator + { + base_ -= n; + return *this; + } + + constexpr friend complexity_iterator operator+(complexity_iterator i, difference_type const n) + requires std::random_access_iterator + { + return i += n; + } + + constexpr friend complexity_iterator operator+(difference_type const n, complexity_iterator i) + requires std::random_access_iterator + { + return i += n; + } + + constexpr friend complexity_iterator operator-(complexity_iterator i, difference_type const n) + requires std::random_access_iterator + { + return i -= n; + } + + constexpr friend difference_type operator-(complexity_iterator const& x, complexity_iterator const& y) + requires std::sized_sentinel_for + { + return x - y; + } + + constexpr bool operator==(complexity_iterator const& other) const requires std::sentinel_for + { + return base_ == other.base_; + } + + template S> + constexpr bool operator==(S const last) const + { + return base_ == last; + } + + [[nodiscard]] constexpr friend bool operator<(complexity_iterator const& x, complexity_iterator const& y) + requires std::random_access_iterator + { + return x.base_ < y.base_; + } + + constexpr friend bool operator>(complexity_iterator const& x, complexity_iterator const& y) + requires std::random_access_iterator + { + return y < x; + } + + constexpr friend bool operator<=(complexity_iterator const& x, complexity_iterator const& y) + requires std::random_access_iterator + { + return !(y < x); + } + + constexpr friend bool operator>=(complexity_iterator const& x, complexity_iterator const& y) + requires std::random_access_iterator + { + return !(x < y); + } +private: + I base_; + mutable difference_type dereference_count_ = 0; +}; + +template +complexity_iterator(I) -> complexity_iterator; + // clang-format on #endif // TEST_STD_VER > 17 && defined(__cpp_lib_concepts)