diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -64,6 +64,7 @@ __algorithm/pop_heap.h __algorithm/prev_permutation.h __algorithm/push_heap.h + __algorithm/ranges_min.h __algorithm/ranges_min_element.h __algorithm/ranges_swap_ranges.h __algorithm/remove.h diff --git a/libcxx/include/__algorithm/ranges_min.h b/libcxx/include/__algorithm/ranges_min.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_min.h @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// 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_MIN_H +#define _LIBCPP___ALGORITHM_RANGES_MIN_H + +#include <__algorithm/ranges_min_element.h> +#include <__config> +#include <__functional/identity.h> +#include <__functional/ranges_operations.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#ifndef _LIBCPP_HAS_NO_CONCEPTS + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +namespace __min { +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)) ? __a : __b; + } + + template > _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + _Tp operator()(initializer_list<_Tp> __r, _Comp __comp = {}, _Proj __proj = {}) const { + return *ranges::__min_element::__fn::__go(ranges::begin(__r), ranges::end(__r), __comp, __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 { + return *ranges::__min_element::__fn::__go(ranges::begin(__r), ranges::end(__r), __comp, __proj); + } +}; +} // namespace __min + +inline namespace __cpo { + inline constexpr auto min = __min::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP_HAS_NO_CONCEPTS + +#endif // _LIBCPP___ALGORITHM_RANGES_MIN_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -754,6 +754,7 @@ #include <__algorithm/pop_heap.h> #include <__algorithm/prev_permutation.h> #include <__algorithm/push_heap.h> +#include <__algorithm/ranges_min.h> #include <__algorithm/ranges_min_element.h> #include <__algorithm/ranges_swap_ranges.h> #include <__algorithm/remove.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_min.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_min.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_min.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_min.h'}} +#include <__algorithm/ranges_min.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.min.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.min.pass.cpp @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template S, class Proj = identity, +// indirect_strict_weak_order> Comp = ranges::less> +// constexpr I ranges::min(I first, S last, Comp comp = {}, Proj proj = {}); +// +// template, Proj>> Comp = ranges::less> +// constexpr borrowed_iterator_t ranges::min(R&& r, Comp comp = {}, Proj proj = {}); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +template +concept HasMin = requires (T t) { std::ranges::min(t); }; + +struct NoLessThanOp {}; +struct NotTotallyOrdered { + int i; + bool operator<(const NotTotallyOrdered& o) const { return i < o.i; } +}; + +struct TotallyOrderedRVal { + int i; + bool operator<(const NotTotallyOrdered& o) const && { return i < o.i; } +}; + +static_assert(HasMin); +static_assert(!HasMin); +static_assert(!HasMin); + +static_assert(HasMin>); +static_assert(!HasMin>); +static_assert(!HasMin>); + +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); + // test comparator + assert(std::ranges::min(1, 2, std::ranges::greater{}) == 2); + // test projection + assert(std::ranges::min(1, 2, std::ranges::less{}, [](int i){ return i == 1 ? 10 : i; }) == 2); + + // test if 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); + ASSERT_SAME_TYPE(decltype(ret), const S&); + assert(&ret == &a[1]); + assert(ret.i == 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); + assert(ret == 5); + } + { // test comparator + int ret = std::ranges::min({7, 6, 9, 3, 5, 1, 2, 4}, std::ranges::greater{}); + assert(ret == 9); + } + { + 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 decltype(auto) ret = std::ranges::min({1, 2, 3}, comparator, projection); + assert(ret == 1); + assert(compares == 2); + assert(projections == 4); + } + { + 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); + } +} + +constexpr void test_range() { + 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); + assert(ret == 5); + } + { // test comparator + int ret = std::ranges::min(a, std::ranges::greater{}); + assert(ret == 9); + } + { + 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 decltype(auto) ret = std::ranges::min(std::array{1, 2, 3}, comparator, projection); + assert(ret == 1); + assert(compares == 2); + assert(projections == 4); + } + { + struct S { int i; }; + S b[3] = { S{2}, S{1}, S{3} }; + decltype(auto) ret = std::ranges::min(b, {}, &S::i); + ASSERT_SAME_TYPE(decltype(ret), S); + assert(ret.i == 1); + } +} + +constexpr bool test() { + test_2_arguments(); + test_initializer_list(); + test_range(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}