diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -7,7 +7,7 @@ Search,find_if_not,Nikolas Klauser,`D121248 `_,✅ Search,find_first_of,Not assigned,n/a,Not started Search,adjacent_find,Not assigned,n/a,Not started -Search,mismatch,Nikolas Klauser,`D117817 `,Complete +Search,mismatch,Nikolas Klauser,`D117817 `_,✅ Search,equal,Not assigned,n/a,Not started Search,lexicographical_compare,Not assigned,n/a,Not started Search,partition_point,Christopher Di Bella,`D105794 `_,Under review @@ -16,7 +16,7 @@ Search,equal_range,Christopher Di Bella,n/a,Not started Search,binary_search,Christopher Di Bella,n/a,Not started Search,min,Nikolas Klauser,`D119589 `_,✅ -Search,max,Not assigned,n/a,Not started +Search,max,Nikolas Klauser,n/a,✅ Search,minmax,Not assigned,n/a,Not started Search,min_element,Nikolas Klauser,`D117025 `_,✅ Search,max_element,Nikolas Klauser,`D117523 `_,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -69,6 +69,7 @@ __algorithm/ranges_find.h __algorithm/ranges_find_if.h __algorithm/ranges_find_if_not.h + __algorithm/ranges_max.h __algorithm/ranges_max_element.h __algorithm/ranges_min.h __algorithm/ranges_min_element.h diff --git a/libcxx/include/__algorithm/ranges_max.h b/libcxx/include/__algorithm/ranges_max.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_max.h @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_MAX_H +#define _LIBCPP___ALGORITHM_RANGES_MAX_H + +#include <__algorithm/ranges_min_element.h> +#include <__assert> +#include <__concepts/copyable.h> +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +namespace __max { +struct __fn { + template > _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + const _Tp& operator()(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {}) const { + return std::invoke(__comp, std::invoke(__proj, __a), std::invoke(__proj, __b)) ? __b : __a; + } + + template > _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + _Tp operator()(initializer_list<_Tp> __il, _Comp __comp = {}, _Proj __proj = {}) const { + _LIBCPP_ASSERT(__il.begin() != __il.end(), "initializer_list must contain at least one element"); + auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); }; + return *ranges::__min_element_impl(__il.begin(), __il.end(), __comp_lhs_rhs_swapped, __proj); + } + + template , _Proj>> _Comp = ranges::less> + requires indirectly_copyable_storable, range_value_t<_Rp>*> + _LIBCPP_HIDE_FROM_ABI constexpr + range_value_t<_Rp> operator()(_Rp&& __r, _Comp __comp = {}, _Proj __proj = {}) const { + auto __first = ranges::begin(__r); + auto __last = ranges::end(__r); + + _LIBCPP_ASSERT(__first != __last, "range must contain at least one element"); + + if constexpr (forward_range<_Rp>) { + auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); }; + return *ranges::__min_element_impl(__first, __last, __comp_lhs_rhs_swapped, __proj); + } else { + range_value_t<_Rp> __result = *__first; + while (++__first != __last) { + if (std::invoke(__comp, std::invoke(__proj, __result), std::invoke(__proj, *__first))) + __result = *__first; + } + return __result; + } + } +}; +} // namespace __max + +inline namespace __cpo { + inline constexpr auto max = __max::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP_STD_VER > 17 && && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_MAX_H diff --git a/libcxx/include/__algorithm/ranges_max_element.h b/libcxx/include/__algorithm/ranges_max_element.h --- a/libcxx/include/__algorithm/ranges_max_element.h +++ b/libcxx/include/__algorithm/ranges_max_element.h @@ -9,6 +9,7 @@ #ifndef _LIBCPP___ALGORITHM_RANGES_MAX_ELEMENT_H #define _LIBCPP___ALGORITHM_RANGES_MAX_ELEMENT_H +#include <__algorithm/ranges_min_element.h> #include <__config> #include <__functional/identity.h> #include <__functional/invoke.h> @@ -30,31 +31,20 @@ namespace ranges { namespace __max_element { struct __fn { - template - _LIBCPP_HIDE_FROM_ABI static constexpr - _Ip __go(_Ip __first, _Sp __last, _Comp& __comp, _Proj& __proj) { - if (__first == __last) - return __first; - - _Ip __i = __first; - while (++__i != __last) - if (std::invoke(__comp, std::invoke(__proj, *__first), std::invoke(__proj, *__i))) - __first = __i; - return __first; - } - template _Sp, class _Proj = identity, indirect_strict_weak_order> _Comp = ranges::less> _LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __first, _Sp __last, _Comp __comp = {}, _Proj __proj = {}) const { - return __go(__first, __last, __comp, __proj); + auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); }; + return __min_element_impl(__first, __last, __comp_lhs_rhs_swapped, __proj); } template , _Proj>> _Comp = ranges::less> _LIBCPP_HIDE_FROM_ABI constexpr borrowed_iterator_t<_Rp> operator()(_Rp&& __r, _Comp __comp = {}, _Proj __proj = {}) const { - return __go(ranges::begin(__r), ranges::end(__r), __comp, __proj); + auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); }; + return __min_element_impl(ranges::begin(__r), ranges::end(__r), __comp_lhs_rhs_swapped, __proj); } }; } // namespace __max_element diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -83,19 +83,33 @@ constexpr borrowed_iterator_t find_if_not(R&& r, Pred pred, Proj proj = {}); // since C++20 - template> Comp = ranges::less> - constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // since C++20 + template> Comp = ranges::less> + constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // since C++20 - template> Comp = ranges::less> - constexpr T min(initializer_list r, Comp comp = {}, Proj proj = {}); // since C++20 + template> Comp = ranges::less> + constexpr T min(initializer_list r, Comp comp = {}, Proj proj = {}); // since C++20 template, Proj>> Comp = ranges::less> requires indirectly_copyable_storable, range_value_t*> constexpr range_value_t min(R&& r, Comp comp = {}, Proj proj = {}); // since C++20 + + template> Comp = ranges::less> + constexpr const T& max(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // since C++20 + + template> Comp = ranges::less> + constexpr T max(initializer_list r, Comp comp = {}, Proj proj = {}); // since C++20 + + template, Proj>> Comp = ranges::less> + requires indirectly_copyable_storable, range_value_t*> + constexpr range_value_t + max(R&& r, Comp comp = {}, Proj proj = {}); // since C++20 } constexpr bool // constexpr in C++20 @@ -815,6 +829,7 @@ #include <__algorithm/ranges_find.h> #include <__algorithm/ranges_find_if.h> #include <__algorithm/ranges_find_if_not.h> +#include <__algorithm/ranges_max.h> #include <__algorithm/ranges_max_element.h> #include <__algorithm/ranges_min.h> #include <__algorithm/ranges_min_element.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -297,6 +297,7 @@ module ranges_find { private header "__algorithm/ranges_find.h" } module ranges_find_if { private header "__algorithm/ranges_find_if.h" } module ranges_find_if_not { private header "__algorithm/ranges_find_if_not.h" } + module ranges_max { private header "__algorithm/ranges_max.h" } module ranges_max_element { private header "__algorithm/ranges_max_element.h" } module ranges_min { private header "__algorithm/ranges_min.h" } module ranges_min_element { private header "__algorithm/ranges_min_element.h" } diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_max.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_max.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_max.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_max.h'}} +#include <__algorithm/ranges_max.h> diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.max.pass.cpp copy from libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp copy to libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.max.pass.cpp --- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp +++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.max.pass.cpp @@ -9,20 +9,21 @@ // // UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: libcpp-no-concepts // UNSUPPORTED: libcpp-has-no-incomplete-ranges // template> Comp = ranges::less> -// constexpr const T& ranges::min(const T& a, const T& b, Comp comp = {}, Proj proj = {}); +// constexpr const T& ranges::max(const T& a, const T& b, Comp comp = {}, Proj proj = {}); +// // template> Comp = ranges::less> -// constexpr T ranges::min(initializer_list r, Comp comp = {}, Proj proj = {}); +// constexpr T ranges::max(initializer_list r, Comp comp = {}, Proj proj = {}); +// // template, Proj>> Comp = ranges::less> // requires indirectly_copyable_storable, range_value_t*> // constexpr range_value_t -// ranges::min(R&& r, Comp comp = {}, Proj proj = {}); +// ranges::max(R&& r, Comp comp = {}, Proj proj = {}); #include #include @@ -34,7 +35,7 @@ #include "test_macros.h" template -concept HasMinR = requires { std::ranges::min(std::declval()); }; +concept HasMaxR = requires { std::ranges::max(std::declval()); }; struct NoLessThanOp {}; struct NotTotallyOrdered { @@ -48,54 +49,54 @@ Movable(const Movable&) = delete; }; -static_assert(!HasMinR); +static_assert(!HasMaxR); -static_assert(HasMinR); -static_assert(HasMinR); -static_assert(!HasMinR); -static_assert(!HasMinR); -static_assert(!HasMinR); +static_assert(HasMaxR); +static_assert(HasMaxR); +static_assert(!HasMaxR); +static_assert(!HasMaxR); +static_assert(!HasMaxR); -static_assert(HasMinR>); -static_assert(!HasMinR>); -static_assert(!HasMinR>); -static_assert(!HasMinR>); -static_assert(!HasMinR); -static_assert(!HasMinR); -static_assert(!HasMinR); -static_assert(!HasMinR); -static_assert(!HasMinR); +static_assert(HasMaxR>); +static_assert(!HasMaxR>); +static_assert(!HasMaxR>); +static_assert(!HasMaxR>); +static_assert(!HasMaxR); +static_assert(!HasMaxR); +static_assert(!HasMaxR); +static_assert(!HasMaxR); +static_assert(!HasMaxR); template -concept HasMin2 = requires { std::ranges::min(std::declval(), std::declval()); }; +concept HasMax2 = requires { std::ranges::max(std::declval(), std::declval()); }; -static_assert(HasMin2); -static_assert(!HasMin2); +static_assert(HasMax2); +static_assert(!HasMax2); -static_assert(std::is_same_v); +static_assert(std::is_same_v); constexpr void test_2_arguments() { - assert(std::ranges::min(1, 2) == 1); - assert(std::ranges::min(2, 1) == 1); + assert(std::ranges::max(1, 2) == 2); + assert(std::ranges::max(2, 1) == 2); // test comparator - assert(std::ranges::min(1, 2, std::ranges::greater{}) == 2); + assert(std::ranges::max(1, 2, std::ranges::greater{}) == 1); // test projection - assert(std::ranges::min(1, 2, std::ranges::less{}, [](int i){ return i == 1 ? 10 : i; }) == 2); + assert(std::ranges::max(1, 2, std::ranges::less{}, [](int i){ return i == 1 ? 10 : i; }) == 1); { // check that std::invoke is used struct S { int i; }; S a[3] = { S{2}, S{1}, S{3} }; - decltype(auto) ret = std::ranges::min(a[0], a[1], {}, &S::i); + decltype(auto) ret = std::ranges::max(a[0], a[1], {}, &S::i); ASSERT_SAME_TYPE(decltype(ret), const S&); - assert(&ret == &a[1]); - assert(ret.i == 1); + assert(&ret == &a[0]); + assert(ret.i == 2); } { // check that pointers are compared and not a range int i; int* a[] = {&i, &i + 1}; - auto ret = std::ranges::min(a[0], a[1]); - assert(ret == &i); + auto ret = std::ranges::max(a[0], a[1]); + assert(ret == &i + 1); } { // test predicate and projection count @@ -109,34 +110,32 @@ ++projections; return x; }; - auto ret = std::ranges::min(1, 2, comparator, projection); - assert(ret == 1); + auto ret = std::ranges::max(1, 2, comparator, projection); + assert(ret == 2); assert(compares == 1); assert(projections == 2); } { // check that the first argument is returned struct S { int check; int other; }; - auto ret = std::ranges::min(S {0, 1}, S {0, 2}, {}, &S::check); + auto ret = std::ranges::max(S {0, 1}, S {0, 2}, {}, &S::check); assert(ret.other == 1); } } constexpr void test_initializer_list() { - { - // test projection - auto proj = [](int i) { return i == 5 ? -100 : i; }; - int ret = std::ranges::min({7, 6, 9, 3, 5, 1, 2, 4}, std::ranges::less{}, proj); + { // test projection + auto proj = [](int i) { return i == 5 ? 100 : i; }; + int ret = std::ranges::max({7, 6, 9, 3, 5, 1, 2, 4}, {}, proj); assert(ret == 5); } - { - // test comparator - int ret = std::ranges::min({7, 6, 9, 3, 5, 1, 2, 4}, std::ranges::greater{}); - assert(ret == 9); + { // test comparator + int ret = std::ranges::max({7, 6, 9, 3, 5, 1, 2, 4}, std::ranges::greater{}); + assert(ret == 1); } - { + { // check that complexity requirements are met int compares = 0; int projections = 0; auto comparator = [&](int a, int b) { @@ -147,26 +146,16 @@ ++projections; return a; }; - std::same_as decltype(auto) ret = std::ranges::min({1, 2, 3}, comparator, projection); - assert(ret == 1); + std::same_as decltype(auto) ret = std::ranges::max({1, 2, 3}, comparator, projection); + assert(ret == 3); assert(compares == 2); assert(projections == 4); } - { + { // check that std::invoke is used struct S { int i; }; - decltype(auto) ret = std::ranges::min({ S{2}, S{1}, S{3} }, {}, &S::i); - ASSERT_SAME_TYPE(decltype(ret), S); - assert(ret.i == 1); - } - - { - int a[] = {7, 6, 9, 3, 5, 1, 2, 4}; - using It = cpp20_input_iterator; - using Sent = sentinel_wrapper; - auto range = std::ranges::subrange(It(a), Sent(It(a + 8))); - auto ret = std::ranges::min(range); - assert(ret == 1); + std::same_as decltype(auto) ret = std::ranges::max({ S{2}, S{1}, S{3} }, {}, &S::i); + assert(ret.i == 3); } } @@ -174,8 +163,8 @@ constexpr void test_range_types() { int a[] = {7, 6, 9, 3, 5, 1, 2, 4}; auto range = std::ranges::subrange(It(a), Sent(It(a + 8))); - int ret = std::ranges::min(range); - assert(ret == 1); + int ret = std::ranges::max(range); + assert(ret == 9); } constexpr void test_range() { @@ -188,17 +177,15 @@ } int a[] = {7, 6, 9, 3, 5, 1, 2, 4}; - { - // test projection - auto proj = [](int& i) { return i == 5 ? -100 : i; }; - int ret = std::ranges::min(a, std::ranges::less{}, proj); + { // test projection + auto proj = [](int& i) { return i == 5 ? 100 : i; }; + int ret = std::ranges::max(a, std::ranges::less{}, proj); assert(ret == 5); } - { - // test comparator - int ret = std::ranges::min(a, std::ranges::greater{}); - assert(ret == 9); + { // test comparator + int ret = std::ranges::max(a, std::ranges::greater{}); + assert(ret == 1); } { // check that predicate and projection call counts are correct @@ -212,8 +199,8 @@ ++projections; return x; }; - std::same_as decltype(auto) ret = std::ranges::min(std::array{1, 2, 3}, comparator, projection); - assert(ret == 1); + std::same_as decltype(auto) ret = std::ranges::max(std::array{1, 2, 3}, comparator, projection); + assert(ret == 3); assert(compares == 2); assert(projections == 4); } @@ -221,23 +208,23 @@ { // check that std::invoke is used struct S { int i; }; S b[3] = { S{2}, S{1}, S{3} }; - std::same_as decltype(auto) ret = std::ranges::min(b, {}, &S::i); - assert(ret.i == 1); + std::same_as decltype(auto) ret = std::ranges::max(b, {}, &S::i); + assert(ret.i == 3); } - { // check that the first smallest element is returned - { // where the first element is the smallest + { // check that the first largest element is returned + { // where the first element is the largest struct S { int check; int other; }; - S b[] = { S{0, 1}, S{1, 2}, S{0, 3} }; - auto ret = std::ranges::min(b, {}, &S::check); - assert(ret.check == 0); + S b[] = { S{1, 1}, S{0, 2}, S{1, 3} }; + auto ret = std::ranges::max(b, {}, &S::check); + assert(ret.check == 1); assert(ret.other == 1); } - { // where the first element isn't the smallest + { // where the first element isn't the largest struct S { int check; int other; }; - S b[] = { S{2, 1}, S{0, 2}, S{0, 3} }; - auto ret = std::ranges::min(b, {}, &S::check); - assert(ret.check == 0); + S b[] = { S{0, 1}, S{1, 2}, S{1, 3} }; + auto ret = std::ranges::max(b, {}, &S::check); + assert(ret.check == 1); assert(ret.other == 2); } } diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp --- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp +++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp @@ -9,7 +9,6 @@ // // UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: libcpp-no-concepts // UNSUPPORTED: libcpp-has-no-incomplete-ranges // template