-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement most ranges partition algorithms (#976)
Includes `ranges::is_partitioned`, `ranges::partition`, `ranges::partition_copy`, and `ranges::partition_point`.
- Loading branch information
1 parent
672bcb2
commit 6d441a1
Showing
9 changed files
with
649 additions
and
12 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains 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 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 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 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 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 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 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 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>(); | ||
} |