-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implement most ranges partition algorithms #976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| RUNALL_INCLUDE ..\concepts_matrix.lst |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| // Covers ranges::is_partitioned and ranges::partition (and bits of ranges::partition_point) | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <cassert> | ||
| #include <concepts> | ||
| #include <numeric> | ||
| #include <ranges> | ||
| #include <utility> | ||
|
|
||
| #include <range_algorithm_support.hpp> | ||
|
|
||
| using namespace std; | ||
|
|
||
| #define ASSERT(...) assert((__VA_ARGS__)) | ||
|
|
||
| using P = pair<int, int>; | ||
|
|
||
| constexpr auto is_even = [](int i) { return i % 2 == 0; }; | ||
|
|
||
| // Validate dangling story | ||
| STATIC_ASSERT(same_as<decltype(ranges::partition(borrowed<false>{}, is_even)), ranges::dangling>); | ||
| STATIC_ASSERT(same_as<decltype(ranges::partition(borrowed<true>{}, is_even)), ranges::subrange<int*>>); | ||
|
|
||
| struct empty_test { | ||
| template <ranges::input_range Range> | ||
| static constexpr void call() { | ||
| // Validate empty ranges | ||
| using ranges::is_partitioned, ranges::partition, ranges::partition_point; | ||
| { | ||
| Range range{}; | ||
| ASSERT(is_partitioned(range, is_even, get_first)); | ||
| } | ||
| { | ||
| Range range{}; | ||
| ASSERT(is_partitioned(range.begin(), range.end(), is_even, get_first)); | ||
| } | ||
|
|
||
| if constexpr (ranges::forward_range<Range>) { | ||
| const Range range{}; | ||
| { | ||
| const auto result = partition(range, is_even, get_first); | ||
| ASSERT(result.begin() == range.end()); | ||
| ASSERT(result.end() == range.end()); | ||
| } | ||
| { | ||
| const auto result = partition(range.begin(), range.end(), is_even, get_first); | ||
| ASSERT(result.begin() == range.end()); | ||
| ASSERT(result.end() == range.end()); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| struct partition_test { | ||
| static constexpr array elements = {P{0, 42}, P{1, 42}, P{2, 42}, P{3, 42}, P{4, 42}, P{5, 42}, P{6, 42}, P{7, 42}}; | ||
|
|
||
| template <ranges::input_range Range> | ||
| static constexpr void call() { | ||
| using ranges::is_partitioned, ranges::partition, ranges::partition_point, ranges::is_permutation, | ||
| ranges::subrange; | ||
|
|
||
| auto pairs = elements; | ||
|
|
||
| { | ||
| Range range{pairs}; | ||
| ASSERT(!is_partitioned(range, is_even, get_first)); | ||
| } | ||
| { | ||
| Range range{pairs}; | ||
| ASSERT(!is_partitioned(range.begin(), range.end(), is_even, get_first)); | ||
| } | ||
|
|
||
| #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 | ||
| if (!is_constant_evaluated()) | ||
| #endif // TRANSITION, VSO-938163 | ||
| { | ||
| if constexpr (ranges::forward_range<Range>) { | ||
| const Range range{pairs}; | ||
| const auto mid = ranges::next(range.begin(), 4); | ||
|
|
||
| { | ||
| auto result = partition(range, is_even, get_first); | ||
| ASSERT(result.begin() == mid); | ||
| ASSERT(result.end() == range.end()); | ||
| } | ||
| ASSERT(is_permutation(subrange{range.begin(), mid}, array{0, 2, 4, 6}, ranges::equal_to{}, get_first)); | ||
| ASSERT(is_partitioned(range, is_even, get_first)); | ||
| ASSERT(partition_point(range, is_even, get_first) == mid); | ||
|
|
||
| pairs = elements; | ||
|
|
||
| { | ||
| auto result = partition(range.begin(), range.end(), is_even, get_first); | ||
| ASSERT(result.begin() == mid); | ||
| ASSERT(result.end() == range.end()); | ||
| } | ||
| ASSERT(is_permutation(subrange{range.begin(), mid}, array{0, 2, 4, 6}, ranges::equal_to{}, get_first)); | ||
| ASSERT(is_partitioned(range.begin(), range.end(), is_even, get_first)); | ||
| ASSERT(partition_point(range.begin(), range.end(), is_even, get_first) == mid); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| int main() { | ||
| STATIC_ASSERT((test_in<empty_test, P>(), true)); | ||
| test_in<empty_test, P>(); | ||
|
|
||
| STATIC_ASSERT((test_in<partition_test, P>(), true)); | ||
| test_in<partition_test, P>(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| RUNALL_INCLUDE ..\concepts_matrix.lst |
134 changes: 134 additions & 0 deletions
134
tests/std/tests/P0896R4_ranges_alg_partition_copy/test.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <cassert> | ||
| #include <concepts> | ||
| #include <numeric> | ||
| #include <ranges> | ||
| #include <utility> | ||
|
|
||
| #include <range_algorithm_support.hpp> | ||
|
|
||
| using namespace std; | ||
|
|
||
| #define ASSERT(...) assert((__VA_ARGS__)) | ||
|
|
||
| using P = pair<int, int>; | ||
|
|
||
| constexpr auto is_even = [](int i) { return i % 2 == 0; }; | ||
|
|
||
| // Validate that partition_copy_result aliases in_out_out_result | ||
| STATIC_ASSERT( | ||
| same_as<ranges::partition_copy_result<int, double, void*>, ranges::in_out_out_result<int, double, void*>>); | ||
|
|
||
| // Validate dangling story | ||
| STATIC_ASSERT(same_as<decltype(ranges::partition_copy(borrowed<false>{}, nullptr_to<int>, nullptr_to<long>, is_even)), | ||
| ranges::partition_copy_result<ranges::dangling, int*, long*>>); | ||
| STATIC_ASSERT(same_as<decltype(ranges::partition_copy(borrowed<true>{}, nullptr_to<int>, nullptr_to<long>, is_even)), | ||
| ranges::partition_copy_result<int*, int*, long*>>); | ||
|
|
||
| struct empty_test { | ||
| template <ranges::input_range Range, indirectly_writable<ranges::range_reference_t<Range>> Out1, | ||
| indirectly_writable<ranges::range_reference_t<Range>> Out2> | ||
| static constexpr void call() { | ||
| // Validate empty ranges | ||
| using ranges::partition_copy, ranges::partition_copy_result, ranges::iterator_t; | ||
|
|
||
| { | ||
| Range range{}; | ||
| auto result = partition_copy(range, Out1{}, Out2{}, is_even, get_first); | ||
| STATIC_ASSERT(same_as<decltype(partition_copy(range, Out1{}, Out2{}, is_even, get_first)), | ||
| partition_copy_result<iterator_t<Range>, Out1, Out2>>); | ||
| ASSERT(result.in == range.end()); | ||
| ASSERT(result.out1.peek() == nullptr); | ||
| ASSERT(result.out2.peek() == nullptr); | ||
| } | ||
| { | ||
| Range range{}; | ||
| auto result = partition_copy(range.begin(), range.end(), Out1{}, Out2{}, is_even, get_first); | ||
| STATIC_ASSERT( | ||
| same_as<decltype(partition_copy(range.begin(), range.end(), Out1{}, Out2{}, is_even, get_first)), | ||
| partition_copy_result<iterator_t<Range>, Out1, Out2>>); | ||
| ASSERT(result.in == range.end()); | ||
| ASSERT(result.out1.peek() == nullptr); | ||
| ASSERT(result.out2.peek() == nullptr); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| struct partition_copy_test { | ||
| static constexpr int N = 32; | ||
|
|
||
| template <ranges::input_range R, indirectly_writable<ranges::range_reference_t<R>> O1, | ||
| indirectly_writable<ranges::range_reference_t<R>> O2> | ||
| static constexpr void call() { | ||
| using ranges::partition_copy; | ||
|
|
||
| P source[N]; | ||
| for (int i = 0; i < N; ++i) { | ||
| source[i] = {i, 42}; | ||
| } | ||
|
|
||
| for (int i = 0; i < N; ++i) { | ||
| P dest[N]; | ||
| ranges::fill(dest, P{-1, 13}); | ||
|
|
||
| const R range{source}; | ||
| auto result = partition_copy( | ||
| range, O1{dest}, O2{dest + i}, [i](int x) { return x < i; }, get_first); | ||
| assert(result.in == range.end()); | ||
| assert(result.out1.peek() == dest + i); | ||
| assert(result.out2.peek() == dest + N); | ||
| assert(ranges::equal(source, dest)); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| template <class Instantiator, class Elem> | ||
| constexpr void run_tests() { | ||
| // Call Instantiator::template call</*...stuff...*/>() with a range whose element type is Elem, and two iterators to | ||
| // which Elem is writable, whose properties are "interesting" for ranges::partition_copy. What combinations of | ||
| // properties are "interesting"? | ||
|
|
||
| // For the input range, the algorithm simply unwraps iterators and chugs through looking for the end. It doesn't | ||
| // * take advantage of any capabilities provided by stronger-than-input categories, | ||
| // * care if the sentinel and iterator have the same type, | ||
| // * care if it can difference iterators with sentinels or each other, or | ||
| // * care about the size of the input range at all. (It can't even use size info to check the outputs, because we | ||
| // don't how many of the input elements will be written through each output.) | ||
| // TLDR: One input range with a proxy reference type and no other notable properties (the so-called "weakest" input | ||
| // range) suffices. | ||
|
|
||
| // For the outputs, both of which are treated equivalently, the algorithm is similarly oblivious to properties other | ||
| // than reference type and the ability to unwrap/rewrap. These could simply be the "weakest" writable iterator type | ||
| // in with_writable_iterators. | ||
|
|
||
| // Out of simple paranoia, let's permute ProxyRef; seven extra pointless tests won't hurt. | ||
|
|
||
| using test::range, test::iterator, test::input, test::output, test::CanCompare, test::CanDifference, test::Common, | ||
| test::ProxyRef, test::Sized; | ||
|
|
||
| using proxy_range = range<input, Elem, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::yes>; | ||
| using non_proxy_range = range<input, Elem, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::no>; | ||
| using proxy_iterator = iterator<output, remove_const_t<Elem>, CanDifference::no, CanCompare::no, ProxyRef::yes>; | ||
| using non_proxy_iterator = iterator<output, remove_const_t<Elem>, CanDifference::no, CanCompare::no, ProxyRef::no>; | ||
|
|
||
| Instantiator::template call<proxy_range, proxy_iterator, proxy_iterator>(); | ||
| Instantiator::template call<proxy_range, proxy_iterator, non_proxy_iterator>(); | ||
| Instantiator::template call<proxy_range, non_proxy_iterator, proxy_iterator>(); | ||
| Instantiator::template call<proxy_range, non_proxy_iterator, non_proxy_iterator>(); | ||
| Instantiator::template call<non_proxy_range, proxy_iterator, proxy_iterator>(); | ||
| Instantiator::template call<non_proxy_range, proxy_iterator, non_proxy_iterator>(); | ||
| Instantiator::template call<non_proxy_range, non_proxy_iterator, proxy_iterator>(); | ||
| Instantiator::template call<non_proxy_range, non_proxy_iterator, non_proxy_iterator>(); | ||
| } | ||
|
|
||
| int main() { | ||
| STATIC_ASSERT((run_tests<empty_test, const P>(), true)); | ||
| run_tests<empty_test, const P>(); | ||
|
|
||
| STATIC_ASSERT((run_tests<partition_copy_test, const P>(), true)); | ||
| run_tests<partition_copy_test, const P>(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| RUNALL_INCLUDE ..\concepts_matrix.lst |
64 changes: 64 additions & 0 deletions
64
tests/std/tests/P0896R4_ranges_alg_partition_point/test.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <cassert> | ||
| #include <concepts> | ||
| #include <numeric> | ||
| #include <ranges> | ||
| #include <span> | ||
| #include <utility> | ||
|
|
||
| #include <range_algorithm_support.hpp> | ||
|
|
||
| using namespace std; | ||
|
|
||
| #define ASSERT(...) assert((__VA_ARGS__)) | ||
|
|
||
| using P = pair<int, int>; | ||
|
|
||
| constexpr auto is_even = [](int i) { return i % 2 == 0; }; | ||
|
|
||
| // Validate dangling story | ||
| STATIC_ASSERT(same_as<decltype(ranges::partition_point(borrowed<false>{}, is_even)), ranges::dangling>); | ||
| STATIC_ASSERT(same_as<decltype(ranges::partition_point(borrowed<true>{}, is_even)), int*>); | ||
|
|
||
| struct empty_test { | ||
| template <ranges::forward_range Range> | ||
| static constexpr void call() { | ||
| // Validate empty ranges | ||
| using ranges::partition_point; | ||
|
|
||
| const Range range{}; | ||
| ASSERT(partition_point(range, is_even, get_first) == range.end()); | ||
| ASSERT(partition_point(range.begin(), range.end(), is_even, get_first) == range.end()); | ||
| } | ||
| }; | ||
|
|
||
| struct partition_point_test { | ||
| template <ranges::forward_range R> | ||
| static constexpr void call() { | ||
| using ranges::partition_point; | ||
| int elements[200]; | ||
| iota(ranges::begin(elements), ranges::end(elements), 0); | ||
|
|
||
| // to avoid constant expression step limits | ||
| const size_t bound = elements[0] + (is_constant_evaluated() ? 10 : ranges::size(elements)); | ||
|
|
||
| for (size_t i = 0; i < bound; ++i) { | ||
| const R range{span{elements}.first(i)}; | ||
| for (size_t j = 0; j < i; ++j) { | ||
| assert(partition_point(range, [j](int x) { return x < static_cast<int>(j); }).peek() == elements + j); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| int main() { | ||
| STATIC_ASSERT((test_fwd<empty_test, P>(), true)); | ||
| test_fwd<empty_test, P>(); | ||
|
|
||
| STATIC_ASSERT((test_fwd<partition_point_test, const int>(), true)); | ||
| test_fwd<partition_point_test, const int>(); | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.