//===----------------------------------------------------------------------===// // // 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 // UNSUPPORTED: libcpp-has-no-incomplete-ranges // template> Comp = ranges::less> // constexpr ranges::minmax_result // ranges::minmax(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // template> Comp = ranges::less> // constexpr ranges::minmax_result // ranges::minmax(initializer_list r, Comp comp = {}, Proj proj = {}); // template, Proj>> Comp = ranges::less> // requires indirectly_copyable_storable, range_value_t*> // constexpr ranges::minmax_result> // ranges::minmax(R&& r, Comp comp = {}, Proj proj = {}); #include #include #include #include #include #include "test_iterators.h" template concept HasMinMax = requires { std::ranges::minmax(std::declval()); }; struct NoLessThanOp {}; struct NotTotallyOrdered { int i; bool operator<(const NotTotallyOrdered& o) const { return i < o.i; } }; struct MoveOnly { MoveOnly(MoveOnly&&) = default; MoveOnly& operator=(MoveOnly&&) = default; MoveOnly(const MoveOnly&) = delete; }; static_assert(!HasMinMax); static_assert(HasMinMax); // make sure HasMinMax works with an array static_assert(!HasMinMax); static_assert(!HasMinMax); static_assert(!HasMinMax); static_assert(HasMinMax>); // make sure HasMinMax works with an initializer_list static_assert(!HasMinMax>); static_assert(!HasMinMax>); static_assert(!HasMinMax>); static_assert(std::is_same_v, std::ranges::min_max_result>); static_assert(std::is_same_v>); constexpr void test_2_arguments() { const int one = 1; const int two = 2; { auto result = std::ranges::minmax(one, two); assert(result.min == 1); assert(result.max == 2); } { auto result = std::ranges::minmax(two, one); assert(result.min == 1); assert(result.max == 2); } { // test comparator auto result = std::ranges::minmax(one, two, std::ranges::greater{}); assert(result.min == 2); assert(result.max == 1); } { // test projection auto result = std::ranges::minmax(one, two, std::ranges::less{}, [](int i) { return i == 1 ? 10 : i; }); assert(result.min == 2); assert(result.max == 1); } { // test if std::invoke is used for the predicate struct S { int i; }; S a[3] = {S{2}, S{1}, S{3}}; std::same_as> auto ret = std::ranges::minmax(a[0], a[1], {}, &S::i); assert(&ret.min == &a[1]); assert(&ret.max == &a[0]); assert(ret.min.i == 1); assert(ret.max.i == 2); } { // check that std::invoke is used for the comparator struct S { int i; constexpr bool comp(S rhs) const { return i < rhs.i; } }; S a[] = {{2}, {5}}; auto ret = std::ranges::minmax(a[0], a[1], &S::comp); assert(ret.min.i == 2); assert(ret.max.i == 5); } { // make sure that {a, b} is returned if b is not less than a auto r = std::ranges::minmax(one, two); assert(&r.min == &one); assert(&r.max == &two); } } constexpr void test_initializer_list() { { // test projection auto proj = [](int i) { return i == 5 ? -100 : i; }; auto ret = std::ranges::minmax({7, 6, 9, 3, 5, 1, 2, 4}, std::ranges::less{}, proj); assert(ret.min == 5); assert(ret.max == 9); } { // test comparator auto ret = std::ranges::minmax({7, 6, 9, 3, 5, 1, 2, 4}, std::ranges::greater{}); assert(ret.min == 9); assert(ret.max == 1); } { // check predicate and projection call counts int compares = 0; int projections = 0; auto comparator = [&](int a, int b) { ++compares; return a < b; }; auto projection = [&](int a) { ++projections; return a; }; std::same_as> auto ret = std::ranges::minmax({1, 2, 3}, comparator, projection); assert(ret.min == 1); assert(ret.max == 3); assert(compares == 3); assert(projections == 6); } { // check that std::invoke is used for the predicate struct S { int i; }; std::same_as> auto ret = std::ranges::minmax({S{2}, S{1}, S{3}}, {}, &S::i); assert(ret.min.i == 1); assert(ret.max.i == 3); } { // check that std::invoke is used for the comparator struct S { int i; constexpr bool comp(S rhs) const { return i < rhs.i; } }; auto ret = std::ranges::minmax({S {1}, S {2}, S {3}, S {4}}, &S::comp); assert(ret.min.i == 1); assert(ret.max.i == 4); } { // check that a single element works auto ret = std::ranges::minmax({ 1 }); assert(ret.min == 1); assert(ret.max == 1); } { // check in ascending order auto ret = std::ranges::minmax({1, 2, 3}); assert(ret.min == 1); assert(ret.max == 3); } { // check in descending order auto ret = std::ranges::minmax({3, 2, 1}); assert(ret.min == 1); assert(ret.max == 3); } } template constexpr void test_range_types() { { // test projection int a[] = {7, 6, 9, 3, 5, 1, 2, 4}; auto proj = [](int& i) { return i == 5 ? -100 : i; }; auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 8))); auto ret = std::ranges::minmax(range, std::ranges::less{}, proj); assert(ret.min == 5); assert(ret.max == 9); } { // test comparator int a[] = {7, 6, 9, 3, 5, 1, 2, 4}; auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 8))); auto ret = std::ranges::minmax(range, std::ranges::greater{}); assert(ret.min == 9); assert(ret.max == 1); } { // check that complexity requirements are met int compares = 0; int projections = 0; auto comparator = [&](int x, int y) { ++compares; return x < y; }; auto projection = [&](int x) { ++projections; return x; }; int a[] = {1, 2, 3}; auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 3))); std::same_as> auto ret = std::ranges::minmax(range, comparator, projection); assert(ret.min == 1); assert(ret.max == 3); assert(compares == 3); assert(projections == 6); } { // check that a single element works int a[] = { 1 }; auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 1))); auto ret = std::ranges::minmax(range); assert(ret.min == 1); assert(ret.max == 1); } { // check in ascending order int a[] = {1, 2, 3}; auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 3))); auto ret = std::ranges::minmax(range); assert(ret.min == 1); assert(ret.max == 3); } { // check in descending order int a[] = {3, 2, 1}; auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 3))); auto ret = std::ranges::minmax(range); assert(ret.min == 1); assert(ret.max == 3); } } constexpr void test_range() { test_range_types, sentinel_wrapper>>(); test_range_types>(); test_range_types>(); test_range_types>(); test_range_types>(); { // check that std::invoke is used for the predicate struct S { int i; }; S b[3] = {S{2}, S{1}, S{3}}; std::same_as> auto ret = std::ranges::minmax(b, {}, &S::i); assert(ret.min.i == 1); assert(ret.max.i == 3); } { // check that std::invoke is used for the comparator struct S { int i; constexpr bool comp(S rhs) const { return i < rhs.i; } }; S a[] = {{1}, {2}, {3}, {4}}; auto ret = std::ranges::minmax(a, &S::comp); assert(ret.min.i == 1); assert(ret.max.i == 4); } { // check that the leftmost value for min an rightmost for max are returned struct S { int comp; int other; }; S a[] = { {0, 0}, {0, 2}, {0, 1} }; auto ret = std::ranges::minmax(a, {}, &S::comp); assert(ret.min.comp == 0); assert(ret.max.comp == 0); assert(ret.min.other == 0); assert(ret.max.other == 1); } { // check that an rvalue array works int a[] = {1, 2, 3, 4}; auto ret = std::ranges::minmax(std::move(a)); assert(ret.min == 1); assert(ret.max == 4); } } constexpr bool test() { test_2_arguments(); test_initializer_list(); test_range(); return true; } int main(int, char**) { test(); static_assert(test()); { // check that the iterator isn't moved from multiple times std::shared_ptr a[] = { std::make_shared(42) }; auto range = std::ranges::subrange(std::move_iterator(a), std::move_iterator(a + 1)); auto [min, max] = std::ranges::minmax(range); assert(a[0] == nullptr); assert(min != nullptr); assert(max == min); } return 0; }