xref: /freebsd-src/contrib/llvm-project/libcxx/include/__algorithm/ranges_minmax.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
181ad6265SDimitry Andric //===----------------------------------------------------------------------===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric 
981ad6265SDimitry Andric #ifndef _LIBCPP___ALGORITHM_RANGES_MINMAX_H
1081ad6265SDimitry Andric #define _LIBCPP___ALGORITHM_RANGES_MINMAX_H
1181ad6265SDimitry Andric 
1281ad6265SDimitry Andric #include <__algorithm/min_max_result.h>
1381ad6265SDimitry Andric #include <__algorithm/minmax_element.h>
1481ad6265SDimitry Andric #include <__assert>
1581ad6265SDimitry Andric #include <__concepts/copyable.h>
1606c3fb27SDimitry Andric #include <__concepts/same_as.h>
1781ad6265SDimitry Andric #include <__config>
1881ad6265SDimitry Andric #include <__functional/identity.h>
1981ad6265SDimitry Andric #include <__functional/invoke.h>
2081ad6265SDimitry Andric #include <__functional/ranges_operations.h>
2181ad6265SDimitry Andric #include <__iterator/concepts.h>
2206c3fb27SDimitry Andric #include <__iterator/next.h>
2381ad6265SDimitry Andric #include <__iterator/projected.h>
2481ad6265SDimitry Andric #include <__ranges/access.h>
2581ad6265SDimitry Andric #include <__ranges/concepts.h>
26*0fca6ea1SDimitry Andric #include <__type_traits/desugars_to.h>
2706c3fb27SDimitry Andric #include <__type_traits/is_reference.h>
28*0fca6ea1SDimitry Andric #include <__type_traits/is_trivially_copyable.h>
2906c3fb27SDimitry Andric #include <__type_traits/remove_cvref.h>
3081ad6265SDimitry Andric #include <__utility/forward.h>
3181ad6265SDimitry Andric #include <__utility/move.h>
32bdd1243dSDimitry Andric #include <__utility/pair.h>
3381ad6265SDimitry Andric #include <initializer_list>
3481ad6265SDimitry Andric 
3581ad6265SDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
3681ad6265SDimitry Andric #  pragma GCC system_header
3781ad6265SDimitry Andric #endif
3881ad6265SDimitry Andric 
3906c3fb27SDimitry Andric #if _LIBCPP_STD_VER >= 20
4081ad6265SDimitry Andric 
4181ad6265SDimitry Andric _LIBCPP_PUSH_MACROS
4281ad6265SDimitry Andric #  include <__undef_macros>
4381ad6265SDimitry Andric 
4481ad6265SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
4581ad6265SDimitry Andric 
4681ad6265SDimitry Andric namespace ranges {
4781ad6265SDimitry Andric template <class _T1>
4881ad6265SDimitry Andric using minmax_result = min_max_result<_T1>;
4981ad6265SDimitry Andric 
5081ad6265SDimitry Andric namespace __minmax {
5181ad6265SDimitry Andric struct __fn {
5206c3fb27SDimitry Andric   template <class _Type,
5306c3fb27SDimitry Andric             class _Proj                                                      = identity,
5481ad6265SDimitry Andric             indirect_strict_weak_order<projected<const _Type*, _Proj>> _Comp = ranges::less>
55*0fca6ea1SDimitry Andric   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr ranges::minmax_result<const _Type&>
5606c3fb27SDimitry Andric   operator()(_LIBCPP_LIFETIMEBOUND const _Type& __a,
5706c3fb27SDimitry Andric              _LIBCPP_LIFETIMEBOUND const _Type& __b,
5806c3fb27SDimitry Andric              _Comp __comp = {},
5906c3fb27SDimitry Andric              _Proj __proj = {}) const {
6081ad6265SDimitry Andric     if (std::invoke(__comp, std::invoke(__proj, __b), std::invoke(__proj, __a)))
6181ad6265SDimitry Andric       return {__b, __a};
6281ad6265SDimitry Andric     return {__a, __b};
6381ad6265SDimitry Andric   }
6481ad6265SDimitry Andric 
6506c3fb27SDimitry Andric   template <copyable _Type,
6606c3fb27SDimitry Andric             class _Proj                                                      = identity,
6781ad6265SDimitry Andric             indirect_strict_weak_order<projected<const _Type*, _Proj>> _Comp = ranges::less>
68*0fca6ea1SDimitry Andric   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr ranges::minmax_result<_Type>
6906c3fb27SDimitry Andric   operator()(initializer_list<_Type> __il, _Comp __comp = {}, _Proj __proj = {}) const {
70cb14a3feSDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
71cb14a3feSDimitry Andric         __il.begin() != __il.end(), "initializer_list has to contain at least one element");
7281ad6265SDimitry Andric     auto __iters = std::__minmax_element_impl(__il.begin(), __il.end(), __comp, __proj);
7381ad6265SDimitry Andric     return ranges::minmax_result<_Type>{*__iters.first, *__iters.second};
7481ad6265SDimitry Andric   }
7581ad6265SDimitry Andric 
7606c3fb27SDimitry Andric   template <input_range _Range,
7706c3fb27SDimitry Andric             class _Proj                                                            = identity,
7881ad6265SDimitry Andric             indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
7981ad6265SDimitry Andric     requires indirectly_copyable_storable<iterator_t<_Range>, range_value_t<_Range>*>
80*0fca6ea1SDimitry Andric   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr ranges::minmax_result<range_value_t<_Range>>
8106c3fb27SDimitry Andric   operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const {
8281ad6265SDimitry Andric     auto __first  = ranges::begin(__r);
8381ad6265SDimitry Andric     auto __last   = ranges::end(__r);
8481ad6265SDimitry Andric     using _ValueT = range_value_t<_Range>;
8581ad6265SDimitry Andric 
86cb14a3feSDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__first != __last, "range has to contain at least one element");
8781ad6265SDimitry Andric 
88*0fca6ea1SDimitry Andric     // This optimiation is not in minmax_element because clang doesn't see through the pointers and as a result doesn't
89*0fca6ea1SDimitry Andric     // vectorize the code.
90*0fca6ea1SDimitry Andric     if constexpr (contiguous_range<_Range> && is_integral_v<_ValueT> &&
91*0fca6ea1SDimitry Andric                   __is_cheap_to_copy<_ValueT> & __is_identity<_Proj>::value &&
92*0fca6ea1SDimitry Andric                   __desugars_to_v<__less_tag, _Comp, _ValueT, _ValueT>) {
93*0fca6ea1SDimitry Andric       minmax_result<_ValueT> __result = {__r[0], __r[0]};
94*0fca6ea1SDimitry Andric       for (auto __e : __r) {
95*0fca6ea1SDimitry Andric         if (__e < __result.min)
96*0fca6ea1SDimitry Andric           __result.min = __e;
97*0fca6ea1SDimitry Andric         if (__result.max < __e)
98*0fca6ea1SDimitry Andric           __result.max = __e;
99*0fca6ea1SDimitry Andric       }
100*0fca6ea1SDimitry Andric       return __result;
101*0fca6ea1SDimitry Andric     } else if constexpr (forward_range<_Range>) {
10206c3fb27SDimitry Andric       // Special-case the one element case. Avoid repeatedly initializing objects from the result of an iterator
10306c3fb27SDimitry Andric       // dereference when doing so might not be idempotent. The `if constexpr` avoids the extra branch in cases where
10406c3fb27SDimitry Andric       // it's not needed.
10506c3fb27SDimitry Andric       if constexpr (!same_as<remove_cvref_t<range_reference_t<_Range>>, _ValueT> ||
10606c3fb27SDimitry Andric                     is_rvalue_reference_v<range_reference_t<_Range>>) {
10706c3fb27SDimitry Andric         if (ranges::next(__first) == __last) {
10806c3fb27SDimitry Andric           // During initialization, members are allowed to refer to already initialized members
10906c3fb27SDimitry Andric           // (see http://eel.is/c++draft/dcl.init.aggr#6)
11006c3fb27SDimitry Andric           minmax_result<_ValueT> __result = {*__first, __result.min};
11106c3fb27SDimitry Andric           return __result;
11206c3fb27SDimitry Andric         }
11306c3fb27SDimitry Andric       }
11481ad6265SDimitry Andric       auto __result = std::__minmax_element_impl(__first, __last, __comp, __proj);
11581ad6265SDimitry Andric       return {*__result.first, *__result.second};
11681ad6265SDimitry Andric     } else {
11781ad6265SDimitry Andric       // input_iterators can't be copied, so the implementation for input_iterators has to store
11881ad6265SDimitry Andric       // the values instead of a pointer to the correct values
11981ad6265SDimitry Andric       auto __less = [&](auto&& __a, auto&& __b) -> bool {
12006c3fb27SDimitry Andric         return std::invoke(__comp,
12106c3fb27SDimitry Andric                            std::invoke(__proj, std::forward<decltype(__a)>(__a)),
12281ad6265SDimitry Andric                            std::invoke(__proj, std::forward<decltype(__b)>(__b)));
12381ad6265SDimitry Andric       };
12481ad6265SDimitry Andric 
12506c3fb27SDimitry Andric       // During initialization, members are allowed to refer to already initialized members
12606c3fb27SDimitry Andric       // (see http://eel.is/c++draft/dcl.init.aggr#6)
12781ad6265SDimitry Andric       ranges::minmax_result<_ValueT> __result = {*__first, __result.min};
12881ad6265SDimitry Andric       if (__first == __last || ++__first == __last)
12981ad6265SDimitry Andric         return __result;
13081ad6265SDimitry Andric 
13181ad6265SDimitry Andric       if (__less(*__first, __result.min))
13281ad6265SDimitry Andric         __result.min = *__first;
13381ad6265SDimitry Andric       else
13481ad6265SDimitry Andric         __result.max = *__first;
13581ad6265SDimitry Andric 
13681ad6265SDimitry Andric       while (++__first != __last) {
13781ad6265SDimitry Andric         _ValueT __i = *__first;
13881ad6265SDimitry Andric         if (++__first == __last) {
13981ad6265SDimitry Andric           if (__less(__i, __result.min))
14081ad6265SDimitry Andric             __result.min = __i;
14181ad6265SDimitry Andric           else if (!__less(__i, __result.max))
14281ad6265SDimitry Andric             __result.max = __i;
14381ad6265SDimitry Andric           return __result;
14481ad6265SDimitry Andric         }
14581ad6265SDimitry Andric 
14681ad6265SDimitry Andric         if (__less(*__first, __i)) {
14781ad6265SDimitry Andric           if (__less(*__first, __result.min))
14881ad6265SDimitry Andric             __result.min = *__first;
14981ad6265SDimitry Andric           if (!__less(__i, __result.max))
15081ad6265SDimitry Andric             __result.max = std::move(__i);
15181ad6265SDimitry Andric         } else {
15281ad6265SDimitry Andric           if (__less(__i, __result.min))
15381ad6265SDimitry Andric             __result.min = std::move(__i);
15481ad6265SDimitry Andric           if (!__less(*__first, __result.max))
15581ad6265SDimitry Andric             __result.max = *__first;
15681ad6265SDimitry Andric         }
15781ad6265SDimitry Andric       }
15881ad6265SDimitry Andric       return __result;
15981ad6265SDimitry Andric     }
16081ad6265SDimitry Andric   }
16181ad6265SDimitry Andric };
16281ad6265SDimitry Andric } // namespace __minmax
16381ad6265SDimitry Andric 
16481ad6265SDimitry Andric inline namespace __cpo {
16581ad6265SDimitry Andric inline constexpr auto minmax = __minmax::__fn{};
16681ad6265SDimitry Andric } // namespace __cpo
16781ad6265SDimitry Andric } // namespace ranges
16881ad6265SDimitry Andric 
16981ad6265SDimitry Andric _LIBCPP_END_NAMESPACE_STD
17081ad6265SDimitry Andric 
17181ad6265SDimitry Andric _LIBCPP_POP_MACROS
17281ad6265SDimitry Andric 
17306c3fb27SDimitry Andric #endif // _LIBCPP_STD_VER >= 20
17481ad6265SDimitry Andric 
17581ad6265SDimitry Andric #endif // _LIBCPP___ALGORITHM_RANGES_MINMAX_H
176