Skip to content
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

Implement Ranges Set Operations #1044

Merged
merged 5 commits into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
406 changes: 406 additions & 0 deletions stl/inc/algorithm

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -1106,8 +1106,16 @@ concept permutable = forward_iterator<_It>
&& indirectly_movable_storable<_It, _It>
&& indirectly_swappable<_It, _It>;

// CONCEPT sortable
// CONCEPT mergeable
namespace ranges { struct less; }
template <class _It1, class _It2, class _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity>
concept mergeable = input_iterator<_It1> && input_iterator<_It2>
&& weakly_incrementable<_Out>
&& indirectly_copyable<_It1, _Out>
&& indirectly_copyable<_It2, _Out>
&& indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>;

// CONCEPT sortable
template <class _It, class _Pr = ranges::less, class _Proj = identity>
concept sortable = permutable<_It> && indirect_strict_weak_order<_Pr, projected<_It, _Proj>>;
// clang-format on
Expand Down
5 changes: 5 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ tests\P0896R4_ranges_alg_for_each_n
tests\P0896R4_ranges_alg_generate
tests\P0896R4_ranges_alg_generate_n
tests\P0896R4_ranges_alg_heap
tests\P0896R4_ranges_alg_includes
tests\P0896R4_ranges_alg_is_permutation
tests\P0896R4_ranges_alg_is_sorted
tests\P0896R4_ranges_alg_minmax
Expand All @@ -272,6 +273,10 @@ tests\P0896R4_ranges_alg_replace_copy_if
tests\P0896R4_ranges_alg_replace_if
tests\P0896R4_ranges_alg_search
tests\P0896R4_ranges_alg_search_n
tests\P0896R4_ranges_alg_set_difference
tests\P0896R4_ranges_alg_set_intersection
tests\P0896R4_ranges_alg_set_symmetric_difference
tests\P0896R4_ranges_alg_set_union
tests\P0896R4_ranges_alg_swap_ranges
tests\P0896R4_ranges_iterator_machinery
tests\P0896R4_ranges_range_machinery
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_includes/env.lst
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
104 changes: 104 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_includes/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>

using namespace std;
using P = pair<int, int>;

CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
struct instantiator {
static constexpr P haystack_elements[] = {{0, 0}, {0, 1}, {1, 0}, {2, 0}, {2, 1}, {4, 0}, {4, 1}};
static constexpr int needle_elements[] = {-1, 0, 3, 3};
static constexpr int bad_needle_elements[] = {-1, 0, 2, 2};

static constexpr auto add_one = [](const int x) { return x + 1; };

template <ranges::input_range Haystack, ranges::input_range Needle>
static constexpr void call() {
using ranges::includes, ranges::less;

{ // Validate range overload
Haystack haystack{haystack_elements};
Needle needle{needle_elements};
const same_as<bool> auto result = includes(haystack, needle, ranges::less{}, get_first, add_one);
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
assert(result);
}
{ // Validate iterator overload
Haystack haystack{haystack_elements};
Needle needle{needle_elements};
const same_as<bool> auto result = includes(
haystack.begin(), haystack.end(), needle.begin(), needle.end(), ranges::less{}, get_first, add_one);
assert(result);
}

{ // Validate range overload, empty haystack
Haystack haystack{};
Needle needle{needle_elements};
const same_as<bool> auto result = includes(haystack, needle, ranges::less{}, get_first, add_one);
assert(!result);
}
{ // Validate iterator overload, empty needle
Haystack haystack{haystack_elements};
Needle needle{};
const same_as<bool> auto result = includes(
haystack.begin(), haystack.end(), needle.begin(), needle.end(), ranges::less{}, get_first, add_one);
assert(result);
}

{ // Validate range overload, needle not found
Haystack haystack{haystack_elements};
Needle needle{bad_needle_elements};
const same_as<bool> auto result = includes(haystack, needle, ranges::less{}, get_first, add_one);
assert(!result);
}
}
};

#ifdef TEST_EVERYTHING
int main() {
STATIC_ASSERT((test_in_in<instantiator, const P, const int>(), true));
test_in_in<instantiator, const P, const int>();
}
#else // ^^^ test all permutations of range properties / test only interesting permutations vvv
template <class Category, class Element, test::ProxyRef IsProxyRef>
using test_range = test::range<Category, Element, test::Sized::no, test::CanDifference::no, test::Common::no,
test::CanCompare{derived_from<Category, std::forward_iterator_tag>}, IsProxyRef>;

constexpr void run_tests() {
using namespace test;
using test::iterator, test::range;

// The algorithm is completely oblivious to:
// * categories stronger than input
// * whether the end sentinel is an iterator
// * size information
// * iterator and/or sentinel differencing
// so let's vary proxyness for coverage and add a range of each category out of paranoia.

instantiator::call<test_range<input, const P, ProxyRef::yes>, test_range<input, const int, ProxyRef::yes>>();
instantiator::call<test_range<input, const P, ProxyRef::yes>, test_range<input, const int, ProxyRef::no>>();
instantiator::call<test_range<input, const P, ProxyRef::no>, test_range<input, const int, ProxyRef::yes>>();
instantiator::call<test_range<input, const P, ProxyRef::no>, test_range<input, const int, ProxyRef::no>>();

instantiator::call<test_range<input, const P, ProxyRef::yes>, test_range<fwd, const int, ProxyRef::yes>>();
instantiator::call<test_range<input, const P, ProxyRef::yes>, test_range<bidi, const int, ProxyRef::yes>>();
instantiator::call<test_range<input, const P, ProxyRef::yes>, test_range<random, const int, ProxyRef::yes>>();
instantiator::call<test_range<input, const P, ProxyRef::yes>, test_range<contiguous, const int, ProxyRef::no>>();

instantiator::call<test_range<fwd, const P, ProxyRef::yes>, test_range<input, const int, ProxyRef::yes>>();
instantiator::call<test_range<bidi, const P, ProxyRef::yes>, test_range<input, const int, ProxyRef::yes>>();
instantiator::call<test_range<random, const P, ProxyRef::yes>, test_range<input, const int, ProxyRef::yes>>();
instantiator::call<test_range<contiguous, const P, ProxyRef::no>, test_range<input, const int, ProxyRef::yes>>();
}

int main() {
STATIC_ASSERT((run_tests(), true));
run_tests();
}
#endif // TEST_EVERYTHING
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_set_difference/env.lst
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
128 changes: 128 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_set_difference/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <concepts>
#include <ranges>
#include <span>
#include <utility>

#include <range_algorithm_support.hpp>

using namespace std;
using P = pair<int, int>;

// Validate that set_difference_result aliases in_out_result
STATIC_ASSERT(same_as<ranges::set_difference_result<int, double>, ranges::in_out_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::set_difference(borrowed<false>{}, borrowed<false>{}, nullptr_to<int>,
ranges::less{}, identity{}, identity{})),
ranges::set_difference_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::set_difference(borrowed<true>{}, borrowed<false>{}, nullptr_to<int>,
ranges::less{}, identity{}, identity{})),
ranges::set_difference_result<int*, int*>>);

struct instantiator {
static constexpr P elements1[] = {{0, 10}, {0, 11}, {0, 12}, {1, 10}, {1, 11}, {3, 10}};
static constexpr P elements2[] = {{13, 0}, {14, 0}, {10, 2}, {11, 3}, {12, 3}};
static constexpr P expected[] = {{0, 12}, {1, 10}, {1, 11}};

template <ranges::input_range R1, ranges::input_range R2, weakly_incrementable O>
static constexpr void call() {
using ranges::set_difference, ranges::set_difference_result, ranges::equal, ranges::iterator_t, ranges::less;

constexpr auto osize = ranges::size(elements1) + ranges::size(elements2);

{ // Validate range overload
P output[osize]{};
R1 range1{elements1};
R2 range2{elements2};
const same_as<set_difference_result<iterator_t<R1>, O>> auto result =
set_difference(range1, range2, O{output}, ranges::less{}, get_first, get_second);
assert(result.in == range1.end());
assert(result.out.peek() == output + ranges::size(expected));
assert(equal(span{output}.first<ranges::size(expected)>(), expected));
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
}
{ // Validate iterator overload
P output[osize]{};
R1 range1{elements1};
R2 range2{elements2};
const same_as<set_difference_result<iterator_t<R1>, O>> auto result = set_difference(range1.begin(),
range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, get_first, get_second);
assert(result.in == range1.end());
assert(result.out.peek() == output + ranges::size(expected));
assert(equal(span{output}.first<ranges::size(expected)>(), expected));
}

{ // Validate range overload, empty range1
P output[osize]{};
R1 range1{};
R2 range2{elements2};
const same_as<set_difference_result<iterator_t<R1>, O>> auto result =
set_difference(range1, range2, O{output}, ranges::less{}, get_first, get_second);
assert(result.in == range1.end());
assert(result.out.peek() == output);
}
{ // Validate iterator overload, empty range2
P output[osize]{};
R1 range1{elements1};
R2 range2{};
const same_as<set_difference_result<iterator_t<R1>, O>> auto result = set_difference(range1.begin(),
range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, get_first, get_second);
assert(result.in == range1.end());
assert(result.out.peek() == output + ranges::size(elements1));
assert(equal(span{output}.first<ranges::size(elements1)>(), elements1));
}
}
};

template <class Continuation>
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
struct generate_readable_ranges {
template <class... Args>
static constexpr void call() {
using namespace test;
using test::range;

// The algorithm is completely oblivious to:
// * categories stronger than input
// * whether the end sentinel is an iterator
// * size information
// * iterator and/or sentinel differencing
// so let's vary proxyness for coverage and call it good.

Continuation::template call<Args...,
range<input, const P, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, const P, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::yes>>();
}
};

template <class Continuation>
struct generate_writable_iterators {
template <class... Args>
static constexpr void call() {
using namespace test;
using test::iterator;

// The algorithm is completely oblivious to all properties except for proxyness,
// so again we'll vary that property, and we'll also get coverage from input iterators to ensure the algorithm
// doesn't inadvertently depend on the output_iterator-only `*i++ = meow` expression.

Continuation::template call<Args..., iterator<output, P, CanDifference::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args..., iterator<output, P, CanDifference::no, CanCompare::no, ProxyRef::yes>>();

Continuation::template call<Args..., iterator<input, P, CanDifference::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args..., iterator<input, P, CanDifference::no, CanCompare::no, ProxyRef::yes>>();
}
};

constexpr void run_tests() {
generate_readable_ranges<generate_readable_ranges<generate_writable_iterators<instantiator>>>::call();
}

int main() {
STATIC_ASSERT((run_tests(), true));
run_tests();
}
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_set_intersection/env.lst
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
Loading