diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index d7c36d6b438fb..6098b3f43fdf9 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -676,6 +676,7 @@ set(files __ranges/access.h __ranges/all.h __ranges/as_rvalue_view.h + __ranges/cartesian_product_view.h __ranges/chunk_by_view.h __ranges/common_view.h __ranges/concepts.h diff --git a/libcxx/include/__ranges/cartesian_product_view.h b/libcxx/include/__ranges/cartesian_product_view.h new file mode 100644 index 0000000000000..4d95de846b3e9 --- /dev/null +++ b/libcxx/include/__ranges/cartesian_product_view.h @@ -0,0 +1,477 @@ +//===----------------------------------------------------------------------===// +// +// 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 _LIBCPP___RANGES_CARTESIAN_PRODUCT_VIEW_H +#define _LIBCPP___RANGES_CARTESIAN_PRODUCT_VIEW_H + +#include <__config> +#include <__iterator/access.h> // begin +#include <__iterator/default_sentinel.h> +#include <__iterator/distance.h> +#include <__iterator/iter_move.h> +#include <__iterator/next.h> +#include <__memory/addressof.h> +#include <__ranges/concepts.h> // forward_range, view, range_size_t, sized_range, ... +#include <__ranges/zip_view.h> // tuple_transform +#include <__type_traits/maybe_const.h> +#include // apply +#include // common_type_t + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +#if _LIBCPP_STD_VER >= 23 + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { + +template +concept cartesian_product_is_random_access = + (random_access_range<__maybe_const> && ... && + (random_access_range<__maybe_const> && sized_range<__maybe_const>)); + +template +concept cartesian_product_common_arg = common_range || (sized_range && random_access_range); + +template +concept cartesian_product_is_bidirectional = + (bidirectional_range<__maybe_const> && ... && + (bidirectional_range<__maybe_const> && cartesian_product_common_arg<__maybe_const>)); + +template +concept cartesian_product_is_common = cartesian_product_common_arg; + +template +concept cartesian_product_is_sized = (sized_range && ...); + +template class FirstSent, class First, class... Vs> +concept cartesian_is_sized_sentinel = + (sized_sentinel_for>, iterator_t<__maybe_const>> && ... && + (sized_range<__maybe_const> && + sized_sentinel_for>, iterator_t<__maybe_const>>)); + +template +constexpr auto cartesian_common_arg_end(R& r) { + if constexpr (common_range) { + return ranges::end(r); + } else { + return ranges::begin(r) + ranges::distance(r); + } +} + +template +concept __cartesian_product_all_random_access = + (random_access_range<__maybe_const> && ... && random_access_range<__maybe_const>); + +template + requires(view && ... && view) +class cartesian_product_view : public view_interface> { +private: + tuple bases_; + + template + class iterator; + +public: + constexpr cartesian_product_view() = default; + constexpr explicit cartesian_product_view(First first_base, Vs... bases) + : bases_{std::move(first_base), std::move(bases)...} {} + + constexpr iterator begin() + requires(!__simple_view || ... || !__simple_view) + { + return iterator(*this, __tuple_transform(ranges::begin, bases_)); + } + + constexpr iterator begin() const + requires(range && ... && range) + { + return iterator(*this, __tuple_transform(ranges::begin, bases_)); + } + + constexpr iterator end() + requires((!__simple_view || ... || !__simple_view) && cartesian_product_is_common) + { + constexpr bool is_const = false; + return end_impl(); + } + + constexpr iterator end() const + requires cartesian_product_is_common + { + constexpr bool is_const = true; + return end_impl(); + } + + constexpr default_sentinel_t end() const noexcept { return {}; } + + constexpr auto size() + requires cartesian_product_is_sized + { + return size_impl(); + } + + constexpr auto size() const + requires cartesian_product_is_sized + { + return size_impl(); + } + +private: + template + constexpr iterator end_impl() const { + bool is_empty = end_is_empty(); + const auto ranges_to_iterators = [is_empty, &b = bases_](std::index_sequence) { + const auto begin_or_first_end = [is_empty](is_first, const auto& rng) { + if constexpr (is_first::value) + return is_empty ? ranges::begin(rng) : cartesian_common_arg_end(rng); + return ranges::begin(rng); + }; + + return std::make_tuple(begin_or_first_end(std::bool_constant{}, std::get(b))...); + }; + iterator it(*this, ranges_to_iterators(std::make_index_sequence<1 + sizeof...(Vs)>{})); + return it; + } + + template + constexpr bool end_is_empty() const { + if constexpr (N == 1 + sizeof...(Vs)) + return false; + else { + if (const auto& v = std::get(bases_); ranges::empty(v)) + return true; + return end_is_empty(); + } + } + + constexpr auto size_impl() const { + return std::apply( + [](auto&&... bases) { + using size_type = std::common_type_t...>; + return (static_cast(std::ranges::size(bases)) * ...); + }, + bases_); + } +}; + +template +cartesian_product_view(Vs&&...) -> cartesian_product_view...>; + +template + requires(view && ... && view) +template +class cartesian_product_view::iterator { + static constexpr auto get_iterator_tag() { + if constexpr (cartesian_product_is_random_access) + return random_access_iterator_tag{}; + else if constexpr (cartesian_product_is_bidirectional) + return bidirectional_iterator_tag{}; + else if constexpr (forward_range<__maybe_const>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + friend cartesian_product_view; + +public: + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(get_iterator_tag()); + using value_type = tuple>, range_value_t<__maybe_const>...>; + using reference = + tuple>, range_reference_t<__maybe_const>...>; + using difference_type = std::common_type_t, range_difference_t...>; + + iterator() = default; + + constexpr iterator(iterator i) + requires Const && (convertible_to, iterator_t> && ... && + convertible_to, iterator_t>) + : parent_(i.parent_), current_(std::move(i.current_)) {} + + constexpr auto operator*() const { + return __tuple_transform([](auto& i) -> decltype(auto) { return *i; }, current_); + } + + constexpr iterator& operator++() { + next(); + return *this; + } + + constexpr void operator++(int) { ++*this; } + + constexpr iterator operator++(int) + requires forward_range<__maybe_const> + { + auto tmp = *this; + ++*this; + return tmp; + } + + constexpr iterator& operator--() + requires cartesian_product_is_bidirectional + { + prev(); + return *this; + } + + constexpr iterator operator--(int) + requires cartesian_product_is_bidirectional + { + auto tmp = *this; + --*this; + return tmp; + } + + constexpr iterator& operator+=(difference_type x) + requires cartesian_product_is_random_access + { + advance(x); + return *this; + } + + constexpr iterator& operator-=(difference_type x) + requires cartesian_product_is_random_access + { + *this += -x; + return *this; + } + + constexpr reference operator[](difference_type n) const + requires cartesian_product_is_random_access + { + return *((*this) + n); + } + + friend constexpr bool operator==(const iterator& x, const iterator& y) + requires equality_comparable>> + { + return x.current_ == y.current_; + } + + friend constexpr bool operator==(const iterator& x, default_sentinel_t) { return x.at_end(); } + + friend constexpr auto operator<=>(const iterator& x, const iterator& y) + requires __cartesian_product_all_random_access + { + return x.current_ <=> y.current_; + } + + friend constexpr iterator operator+(const iterator& x, difference_type y) + requires cartesian_product_is_random_access + { + return iterator(x) += y; + } + + friend constexpr iterator operator+(difference_type x, const iterator& y) + requires cartesian_product_is_random_access + { + return y + x; + } + + friend constexpr iterator operator-(const iterator& x, difference_type y) + requires cartesian_product_is_random_access + { + return iterator(x) -= y; + } + + friend constexpr difference_type operator-(const iterator& x, const iterator& y) + requires cartesian_is_sized_sentinel + { + return x.distance_from(y.current_); + } + + friend constexpr difference_type operator-(const iterator& i, default_sentinel_t) + requires cartesian_is_sized_sentinel + { + tuple end_tuple = [&b = i.parent_->bases_](index_sequence) { + return tuple{ranges::end(std::get<0>(b)), ranges::begin(std::get<1 + I>(b))...}; + }(std::make_index_sequence{}); + return i.distance_from(end_tuple); + } + + friend constexpr difference_type operator-(default_sentinel_t s, const iterator& i) + requires cartesian_is_sized_sentinel + { + return -(i - s); + } + + friend constexpr auto iter_move(const iterator& i) noexcept(iter_move_noexcept_impl(i)) { + return __tuple_transform(ranges::iter_move, i.current_); + } + + friend constexpr void iter_swap(const iterator& l, const iterator& r) noexcept(iter_swap_noexcept_impl(l, r)) + requires(indirectly_swappable>> && ... && + indirectly_swappable>>) + { + iter_swap_impl(l, r); + } + +private: + using Parent = __maybe_const; + Parent* parent_ = nullptr; + using MultiIterator = tuple>, iterator_t<__maybe_const>...>; + MultiIterator current_; + + template + constexpr void next() { + auto& it = std::get(current_); + ++it; + if constexpr (N > 0) { + if (const auto& v = std::get(parent_->bases_); it == ranges::end(v)) { + it = ranges::begin(v); + next(); + } + } + } + + template + constexpr void prev() { + auto& it = std::get(current_); + if constexpr (N > 0) { + if (const auto& v = std::get(parent_->bases_); it == ranges::begin(v)) { + it = cartesian_common_arg_end(v); + prev(); + } + } + --it; + } + + template + constexpr void advance(difference_type x) { + if (x == 0) + return; + + const auto& v = std::get(parent_->bases_); + auto& it = std::get(current_); + const auto sz = static_cast(std::ranges::size(v)); + const auto first = ranges::begin(v); + + if (sz > 0) { + const auto idx = static_cast(std::distance(first, it)); + x += idx; + + difference_type mod; + if constexpr (N > 0) { + difference_type div = x / sz; + mod = x % sz; + if (mod < 0) { + mod += sz; + div--; + } + advance(div); + } else { + mod = (x >= 0 && x < sz) ? x : sz; + } + it = std::next(first, mod); + + } else { + if constexpr (N > 0) { + advance(x); + } + it = first; + } + } + + template + constexpr bool at_end() const { + if (std::get(current_) == ranges::end(std::get(parent_->bases_))) + return true; + if constexpr (N > 0) + return at_end(); + return false; + } + + template + constexpr difference_type distance_from(const Tuple& t) const { + return scaled_sum(t); + } + + template + constexpr difference_type scaled_size() const { + if constexpr (N <= sizeof...(Vs)) + return static_cast(ranges::size(std::get(parent_->bases_))) * scaled_size(); + return static_cast(1); + } + + template + constexpr difference_type scaled_distance(const auto& t) const { + return static_cast(std::get(current_) - std::get(t)) * scaled_size(); + } + + template + constexpr difference_type scaled_sum(const auto& t) const { + if constexpr (N <= sizeof...(Vs)) + return scaled_distance(t) + scaled_sum(t); + return static_cast(0); + } + + constexpr iterator(Parent& parent, MultiIterator current) + : parent_(std::addressof(parent)), current_(std::move(current)) {} + + template + static constexpr bool iter_move_noexcept_impl(const iterator& i) { + if (not noexcept(std::ranges::iter_move(std::get(i.current_)))) + return false; + if constexpr (N > 0) + return iter_move_noexcept_impl(i); + + return std::is_nothrow_move_constructible_v< + std::ranges::range_rvalue_reference_t<__maybe_const>>() and + (std::is_nothrow_move_constructible_v>>() and + ...); + } + + template + static constexpr bool iter_swap_noexcept_impl(const iterator& l, const iterator& r) { + if (not noexcept(std::ranges::iter_swap(std::get(l.current_), std::get(r.current_)))) + return false; + if constexpr (i > 0) + return iter_move_noexcept_impl(i); + return true; + } + + template + static constexpr void iter_swap_impl(const iterator& l, const iterator& r) { + ranges::iter_swap(std::get(l.current_), std::get(r.current_)); + if constexpr (N > 0) + iter_swap_impl(l, r); + } +}; + +namespace views { +namespace __cartesian_product { +struct __fn { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()() { return views::single(tuple()); } + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ranges&&... __rs) + -> decltype(cartesian_product_view...>(std::forward<_Ranges>(__rs)...)) { + return cartesian_product_view...>(std::forward<_Ranges>(__rs)...); + } +}; +} // namespace __cartesian_product +inline namespace __cpo { +inline constexpr auto cartesian_product = __cartesian_product::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_CARTESIAN_PRODUCT_VIEW_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 43072aa0fb0f1..8c20a03faf901 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -1786,6 +1786,7 @@ module std [system] { module access { header "__ranges/access.h" } module all { header "__ranges/all.h" } module as_rvalue_view { header "__ranges/as_rvalue_view.h" } + module cartesian_product_view { header "__ranges/cartesian_product_view.h" } module chunk_by_view { header "__ranges/chunk_by_view.h" export std.functional.bind_back diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 9ef614d21f525..668287ffe02c8 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -425,6 +425,7 @@ namespace std { # if _LIBCPP_STD_VER >= 23 # include <__ranges/as_rvalue_view.h> +# include <__ranges/cartesian_product_view.h> # include <__ranges/chunk_by_view.h> # include <__ranges/from_range.h> # include <__ranges/repeat_view.h> diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/begin.pass.cpp new file mode 100644 index 0000000000000..fac0d39b9f723 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/begin.pass.cpp @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// constexpr iterator begin() requires (!simple-view || ... || !simple-view ); +// constexpr iterator begin() const requires (range && ... && range); + +#include +#include +#include +#include +#include + +#include "../range.zip/types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasNonConstBegin = requires(T& t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = HasConstBegin && HasNonConstBegin && requires(T& t, const T& ct) { + requires !std::same_as; +}; + +template +concept HasOnlyNonConstBegin = HasNonConstBegin && !HasConstBegin; + +template +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +struct NoConstBeginView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { // all underlying iterators should be at the begin position + std::ranges::cartesian_product_view v( + SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.0)); + std::same_as> decltype(auto) mutVal = *v.begin(); + std::same_as> decltype(auto) constVal = + *std::as_const(v).begin(); + assert(mutVal == std::make_tuple(1, 0, 2.0)); + assert(constVal == std::make_tuple(1, 0, 2.0)); + assert(&(std::get<0>(mutVal)) == &buffer[0]); + assert(&(std::get<0>(constVal)) == &buffer[0]); + } + + { // with empty range + std::ranges::cartesian_product_view v(SizedRandomAccessView{buffer}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { // underlying ranges all model simple-view + std::ranges::cartesian_product_view v(SimpleCommon{buffer}, SimpleCommon{buffer}); + static_assert(std::is_same_v); + assert(v.begin() == std::as_const(v).begin()); + auto [x, y] = *std::as_const(v).begin(); + assert(&x == &buffer[0]); + assert(&y == &buffer[0]); + + using View = decltype(v); + static_assert(std::ranges::__simple_view); + static_assert(HasOnlyConstBegin); + static_assert(!HasOnlyNonConstBegin); + static_assert(!HasConstAndNonConstBegin); + } + + { // not all underlying ranges model simple-view + std::ranges::cartesian_product_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer}); + static_assert(!std::is_same_v); + assert(v.begin() == std::as_const(v).begin()); + auto [x, y] = *std::as_const(v).begin(); + assert(&x == &buffer[0]); + assert(&y == &buffer[0]); + + using View = decltype(v); + static_assert(!HasOnlyConstBegin); + static_assert(!HasOnlyNonConstBegin); + static_assert(HasConstAndNonConstBegin); + } + + { // underlying const R is not a range + using View = std::ranges::cartesian_product_view; + static_assert(!HasOnlyConstBegin); + static_assert(HasOnlyNonConstBegin); + static_assert(!HasConstAndNonConstBegin); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctad.compile.pass.cpp new file mode 100644 index 0000000000000..d67e018828c27 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctad.compile.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// template +// cartesian_product_view(Vs&&...) -> cartesian_product_view...>; + +#include +#include +#include + +struct Container { + int* begin() const; + int* end() const; +}; + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +void testCTAD() { + static_assert(std::is_same_v>>); + + static_assert(std::is_same_v, View>>); + + Container c{}; + static_assert( + std::is_same_v< + decltype(std::ranges::cartesian_product_view(Container{}, View{}, c)), + std::ranges:: + cartesian_product_view, View, std::ranges::ref_view>>); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.default.pass.cpp new file mode 100644 index 0000000000000..8691ff2122e30 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.default.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// cartesian_product_view() = default; + +#include + +#include +#include +#include + +constexpr int buff[] = {1, 2, 3}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 3) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +struct NoDefaultCtrView : std::ranges::view_base { + NoDefaultCtrView() = delete; + int* begin() const; + int* end() const; +}; + +// The default constructor requires all underlying views to be default constructible. +// It is implicitly required by the tuple's constructor. If any of the iterators are +// not default constructible, cartesian product iterator's =default would be implicitly deleted. +static_assert(std::is_default_constructible_v>); +static_assert(std::is_default_constructible_v< + std::ranges::cartesian_product_view>); +static_assert( + !std::is_default_constructible_v>); +static_assert( + !std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); + +constexpr bool test() { + { + using View = std::ranges::cartesian_product_view; + View v = View(); // the default constructor is not explicit + assert(v.size() == 9); + auto it = v.begin(); + using Value = std::tuple; + assert(*it++ == Value(1, 1)); + assert(*it++ == Value(1, 2)); + assert(*it++ == Value(1, 3)); + assert(*it++ == Value(2, 1)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.pass.cpp new file mode 100644 index 0000000000000..bf23c913ff9cc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// std::ranges::cartesian_product_view + +#include +#include + +#include "test_iterators.h" + +struct DefaultConstructibleView : std::ranges::view_base { + int i_; + int* begin(); + int* end(); +}; + +constexpr bool test() { + { // view | views::as_rvalue + // DefaultConstructibleView v{{}, 3}; + [[maybe_unused]] std::ranges::cartesian_product_view r1; + [[maybe_unused]] std::ranges::cartesian_product_view r2; + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.views.pass.cpp new file mode 100644 index 0000000000000..0c424acaf3950 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/ctor.views.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// constexpr explicit cartesian_product_view(First first_base, Vs... bases); + +#include +#include + +#include "../range.zip/types.h" + +template +void conversion_test(T); + +template +concept implicitly_constructible_from = requires(Args&&... args) { conversion_test({std::move(args)...}); }; + +// test constructor is explicit +static_assert(std::constructible_from, SimpleCommon>); +static_assert(!implicitly_constructible_from, SimpleCommon>); + +static_assert(std::constructible_from, SimpleCommon, SimpleCommon>); +static_assert( + !implicitly_constructible_from, SimpleCommon, SimpleCommon>); + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +template +constexpr void constructorTest(auto&& buffer1, auto&& buffer2) { + std::ranges::cartesian_product_view v{View1{buffer1}, View2{buffer2}}; + auto [i, j] = *v.begin(); + assert(i == buffer1[0]); + assert(j == buffer2[0]); +} + +constexpr bool test() { + + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + int buffer2[] = {9, 8, 7, 6}; + + { // constructor from views + std::ranges::cartesian_product_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + assert(*v.begin() == std::make_tuple(1, 0, 2.0)); + } + + { // arguments are moved once + MoveAwareView mv; + std::ranges::cartesian_product_view v{std::move(mv), MoveAwareView{}}; + auto [numMoves1, numMoves2] = *v.begin(); + assert(numMoves1 == 2); // one move from the local variable to parameter, one move from parameter to member + assert(numMoves2 == 1); + } + + { // input and forward + constructorTest(buffer, buffer2); + } + + { // bidi and random_access + constructorTest(buffer, buffer2); + } + + { // contiguous + constructorTest(buffer, buffer2); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/end.pass.cpp new file mode 100644 index 0000000000000..1d057d05bc810 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/end.pass.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// constexpr iterator end() requires ((!simple-view || ... || !simple-view ) && cartesian-product-is-common< First, Vs...>); +// constexpr iterator end() const requires cartesian-product-is-common ; + +#include "assert_macros.h" + +#include +#include +#include + +constexpr bool test() { + { // non-empty range + constexpr size_t N = 7; + std::array a; + std::ranges::cartesian_product_view c{a}; + assert(c.end() == c.begin() + N); + } + + { // (non-empty range)^2 + constexpr size_t N0 = 7; + std::array a0; + constexpr size_t N1 = 42; + std::array a1; + std::ranges::cartesian_product_view c{a0, a1}; + assert(c.end() == c.begin() + N0 * N1); + } + + { // (non-empty range)^3 + constexpr size_t N0 = 5, N1 = 42, N2 = 7; + std::array a0; + std::array a1; + std::array a2; + std::ranges::cartesian_product_view c{a0, a1, a2}; + assert(c.end() == c.begin() + N0*N1*N2); + } + + { // empty range + std::ranges::empty_view e; + std::ranges::cartesian_product_view c{e}; + assert(c.end() == c.begin()); + } + + { // (empty range)^2 + std::ranges::empty_view e; + std::ranges::cartesian_product_view c{e, e}; + assert(c.end() == c.begin()); + } + + { // empty range X common range + std::ranges::empty_view e; + constexpr size_t N = 7; + std::array a; + std::ranges::cartesian_product_view c{e, a}; + assert(c.end() == c.begin()); + } + + { // common range X empty range + std::ranges::empty_view e; + constexpr size_t N = 7; + std::array a; + std::ranges::cartesian_product_view c{e, a}; + assert(c.end() == c.begin()); + } + + { // (empty range)^3 + std::ranges::empty_view e; + std::ranges::cartesian_product_view c{e, e, e}; + assert(c.end() == c.begin()); + } + + { // empty range X empty range X common range + std::ranges::empty_view e; + constexpr size_t N = 7; + std::array a; + std::ranges::cartesian_product_view c{e, e, a}; + assert(c.end() == c.begin()); + } + + { // empty range X common range X empty range + std::ranges::empty_view e; + constexpr size_t N = 7; + std::array a; + std::ranges::cartesian_product_view c{e, a, e}; + assert(c.end() == c.begin()); + } + + { // common range X empty range X empty range + std::ranges::empty_view e; + constexpr size_t N = 7; + std::array a; + std::ranges::cartesian_product_view c{a, e, e}; + assert(c.end() == c.begin()); + } + + { // empty range X common range X common range + std::ranges::empty_view e; + constexpr size_t N0 = 7, N1 = 42; + std::array a0; + std::array a1; + std::ranges::cartesian_product_view c{e, a0, a1}; + assert(c.end() == c.begin()); + } + + { // common range X empty range X common range + std::ranges::empty_view e; + constexpr size_t N0 = 7, N1 = 42; + std::array a0; + std::array a1; + std::ranges::cartesian_product_view c{a0, e, a1}; + assert(c.end() == c.begin()); + } + + { // common range X common range X empty range + std::ranges::empty_view e; + constexpr size_t N0 = 7, N1 = 42; + std::array a0; + std::array a1; + std::ranges::cartesian_product_view c{a0, a1, e}; + assert(c.end() == c.begin()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/example_from_std.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/example_from_std.pass.cpp new file mode 100644 index 0000000000000..4d505921b3a77 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/example_from_std.pass.cpp @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +#include +#include +#include +#include + +constexpr bool test() { + struct ConstexprStringStream { + std::string str; + + constexpr ConstexprStringStream& operator<<(int x) { + return *this << char(x + 48); + } + constexpr ConstexprStringStream& operator<<(char c) { + str += c; + return *this; + } + }; + + const std::vector v{0, 1, 2}; + ConstexprStringStream out; + for (auto&& [a, b, c] : std::ranges::views::cartesian_product(v, v, v)) { + out << a << ' ' << b << ' ' << c << '\n'; + } + + const std::string_view expected = + "0 0 0\n" + "0 0 1\n" + "0 0 2\n" + "0 1 0\n" + "0 1 1\n" + "0 1 2\n" + "0 2 0\n" + "0 2 1\n" + "0 2 2\n" + "1 0 0\n" + "1 0 1\n" + "1 0 2\n" + "1 1 0\n" + "1 1 1\n" + "1 1 2\n" + "1 2 0\n" + "1 2 1\n" + "1 2 2\n" + "2 0 0\n" + "2 0 1\n" + "2 0 2\n" + "2 1 0\n" + "2 1 1\n" + "2 1 2\n" + "2 2 0\n" + "2 2 1\n" + "2 2 2\n"; + assert(out.str == expected); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/size.pass.cpp new file mode 100644 index 0000000000000..c29990a205e6a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.cartesian.product.view/size.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// std::ranges::cartesian_product_view::size + +#include +#include +#include +#include + +constexpr bool test() { + { // example taken from: https://en.cppreference.com/w/cpp/ranges/cartesian_product_view/size + constexpr static auto w = {1}; + constexpr static auto x = {2, 3}; + constexpr static auto y = {4, 5, 6}; + constexpr static auto z = {7, 8, 9, 10, 11, 12, 13}; + + constexpr auto ww = std::ranges::views::all(w); + constexpr auto xx = std::ranges::views::all(x); + constexpr auto yy = std::ranges::views::all(y); + constexpr auto zz = std::ranges::views::all(z); + + constexpr auto v = std::ranges::cartesian_product_view(ww, xx, yy, zz); + + assert(v.size() == 42); + assert(v.size() == w.size() * x.size() * y.size() * z.size()); + } + + { // empty range + std::ranges::empty_view e; + auto v = std::ranges::cartesian_product_view(e); + assert(v.size() == 0); + } + + { // 1..3 range(s) + constexpr size_t N0 = 3, N1 = 7, N2 = 42; + std::array a0; + std::array a1; + std::array a2; + assert(std::ranges::cartesian_product_view(a0).size() == N0); + assert(std::ranges::cartesian_product_view(a0, a1).size() == N0 * N1); + assert(std::ranges::cartesian_product_view(a0, a1, a2).size() == N0 * N1 * N2); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/types.h b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h index e084dcfc41b0d..7cd7fa6e68c6d 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.zip/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h @@ -17,7 +17,7 @@ #include "test_range.h" #if TEST_STD_VER <= 20 -# error "range.zip/types.h" can only be included in builds supporting C++20 +# error "range.zip/types.h" can only be included in builds supporting C++23 #endif // TEST_STD_VER <= 20 template