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::lexicographical_compare #1081

Merged
merged 3 commits into from
Aug 9, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
53 changes: 53 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -9935,6 +9935,59 @@ namespace ranges {
};

inline constexpr _Clamp_fn clamp{_Not_quite_object::_Construct_tag{}};

// VARIABLE ranges::lexicographical_compare
class _Lexicographical_compare_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <input_iterator _It1, sentinel_for<_It1> _Se1, input_iterator _It2, sentinel_for<_It2> _Se2,
class _Pj1 = identity, class _Pj2 = identity,
indirect_strict_weak_order<projected<_It1, _Pj1>, projected<_It2, _Pj2>> _Pr = ranges::less>
_NODISCARD constexpr bool operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred = {},
_Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
_Adl_verify_range(_First1, _Last1);
_Adl_verify_range(_First2, _Last2);

return _Lexicographical_compare_unchecked(_Get_unwrapped(_STD move(_First1)),
_Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)),
_Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
}

template <input_range _Rng1, input_range _Rng2, class _Pj1 = identity, class _Pj2 = identity,
indirect_strict_weak_order<projected<iterator_t<_Rng1>, _Pj1>, projected<iterator_t<_Rng2>, _Pj2>> _Pr =
ranges::less>
_NODISCARD constexpr bool operator()(
_Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
return _Lexicographical_compare_unchecked(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2),
_Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
}

private:
template <class _It1, class _Se1, class _It2, class _Se2, class _Pr, class _Pj1, class _Pj2>
_NODISCARD static constexpr bool _Lexicographical_compare_unchecked(
_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>);
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>);
_STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>);

for (;; ++_First1, (void) ++_First2) {
if (_First2 == _Last2) {
return false;
} else if (_First1 == _Last1) {
return true;
} else if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) {
return true;
} else if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) {
return false;
}
}
}
};

inline constexpr _Lexicographical_compare_fn lexicographical_compare{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ 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_lexicographical_compare
tests\P0896R4_ranges_alg_merge
tests\P0896R4_ranges_alg_minmax
tests\P0896R4_ranges_alg_mismatch
Expand Down
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
224 changes: 224 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// 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>;

struct instantiator {
static constexpr P left[] = {{0, 10}, {1, 20}, {2, 30}};
static constexpr P right_shorter_less[] = {{300, 0}, {200, 0}};
static constexpr P right_shorter_same[] = {{300, 0}, {200, 1}};
static constexpr P right_shorter_greater[] = {{300, 0}, {200, 2}};
static constexpr P right_less[] = {{300, 0}, {200, 1}, {100, 1}};
static constexpr P right_equal[] = {{300, 0}, {200, 1}, {100, 2}};
static constexpr P right_greater[] = {{300, 0}, {200, 1}, {100, 3}};
static constexpr P right_longer_less[] = {{300, 0}, {200, 1}, {100, 1}, {0, 3}};
static constexpr P right_longer_same[] = {{300, 0}, {200, 1}, {100, 2}, {0, 3}};
static constexpr P right_longer_greater[] = {{300, 0}, {200, 1}, {100, 3}, {0, 3}};

template <class In1, class In2>
static constexpr void call() {
using ranges::lexicographical_compare, ranges::less;

// Validate range overload
{
In1 range1{left};
In2 range2{right_shorter_less};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_shorter_same};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_shorter_greater};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(result);
}

{
In1 range1{left};
In2 range2{right_less};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_equal};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_greater};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(result);
}

{
In1 range1{left};
In2 range2{right_longer_less};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_longer_same};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(result);
}
{
In1 range1{left};
In2 range2{right_longer_greater};
const same_as<bool> auto result = lexicographical_compare(range1, range2, less{}, get_first, get_second);
assert(result);
}

{
In1 empty1{};
In2 range2{right_equal};
const same_as<bool> auto result = lexicographical_compare(empty1, range2, less{}, get_first, get_second);
assert(result);
}
{
In1 range1{left};
In2 empty2{};
const same_as<bool> auto result = lexicographical_compare(range1, empty2, less{}, get_first, get_second);
assert(!result);
}
{
In1 empty1{};
In2 empty2{};
const same_as<bool> auto result = lexicographical_compare(empty1, empty2, less{}, get_first, get_second);
assert(!result);
}

// Validate iterator overload
{
In1 range1{left};
In2 range2{right_shorter_less};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_shorter_same};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_shorter_greater};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(result);
}

{
In1 range1{left};
In2 range2{right_less};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_equal};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_greater};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(result);
}

{
In1 range1{left};
In2 range2{right_longer_less};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(!result);
}
{
In1 range1{left};
In2 range2{right_longer_same};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(result);
}
{
In1 range1{left};
In2 range2{right_longer_greater};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(result);
}

{
In1 empty1{};
In2 range2{right_equal};
const same_as<bool> auto result = lexicographical_compare(
empty1.begin(), empty1.end(), range2.begin(), range2.end(), less{}, get_first, get_second);
assert(result);
}
{
In1 range1{left};
In2 empty2{};
const same_as<bool> auto result = lexicographical_compare(
range1.begin(), range1.end(), empty2.begin(), empty2.end(), less{}, get_first, get_second);
assert(!result);
}
{
In1 empty1{};
In2 empty2{};
const same_as<bool> auto result = lexicographical_compare(
empty1.begin(), empty1.end(), empty2.begin(), empty2.end(), less{}, get_first, get_second);
assert(!result);
}
}
};

#ifdef TEST_EVERYTHING
int main() {
// No constexpr tests here: we hit the constexpr step limits too quickly.
test_in_in<instantiator, const P, const P>();
}
#else // ^^^ test all range combinations / test only interesting combinations vvv
template <test::ProxyRef IsProxy>
using range_type = test::range<input_iterator_tag, const P, test::Sized::no, test::CanDifference::no, test::Common::no,
test::CanCompare::no, IsProxy>;

constexpr void run_tests() {
// The algorithm is very much oblivious to any properties above the minimum requirements: category, size,
// difference, none of it matters. Let's test input ranges with and without proxy references.
using test::ProxyRef;

instantiator::call<range_type<ProxyRef::yes>, range_type<ProxyRef::yes>>();
instantiator::call<range_type<ProxyRef::yes>, range_type<ProxyRef::no>>();
instantiator::call<range_type<ProxyRef::no>, range_type<ProxyRef::yes>>();
instantiator::call<range_type<ProxyRef::no>, range_type<ProxyRef::no>>();
}

int main() {
STATIC_ASSERT((run_tests(), true));
run_tests();
}
#endif // TEST_EVERYTHING