On 05/05/21 10:39 +0100, Jonathan Wakely wrote:
On 04/05/21 21:42 -0400, Patrick Palka via Libstdc++ wrote:
This rewrites ranges::minmax and ranges::minmax_element so that it
performs at most 3*N/2 many comparisons, as required by the standard.
In passing, this also fixes PR100387 by avoiding a premature std::move
in ranges::minmax and in std::shift_right.

Tested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps
10/11?

libstdc++-v3/ChangeLog:

        PR libstdc++/100387
        * include/bits/ranges_algo.h (__minmax_fn::operator()): Rewrite
        to limit comparison complexity to 3*N/2.  Avoid premature std::move.
        (__minmax_element_fn::operator()): Likewise.
        (shift_right): Avoid premature std::move of __result.
        * testsuite/25_algorithms/minmax/constrained.cc (test04, test05):
        New tests.
        * testsuite/25_algorithms/minmax_element/constrained.cc (test02):
        Likewise.
---
libstdc++-v3/include/bits/ranges_algo.h       | 87 ++++++++++++++-----
.../25_algorithms/minmax/constrained.cc       | 31 +++++++
.../minmax_element/constrained.cc             | 19 ++++
3 files changed, 113 insertions(+), 24 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_algo.h 
b/libstdc++-v3/include/bits/ranges_algo.h
index cda3042c11f..bbd29127e89 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -3291,18 +3291,39 @@ namespace ranges
        auto __first = ranges::begin(__r);
        auto __last = ranges::end(__r);
        __glibcxx_assert(__first != __last);
+       auto __comp_proj = __detail::__make_comp_proj(__comp, __proj);
        minmax_result<range_value_t<_Range>> __result = {*__first, *__first};
        while (++__first != __last)
          {
-           auto __tmp = *__first;
-           if (std::__invoke(__comp,
-                             std::__invoke(__proj, __tmp),
-                             std::__invoke(__proj, __result.min)))
-             __result.min = std::move(__tmp);
-           if (!(bool)std::__invoke(__comp,
-                                    std::__invoke(__proj, __tmp),
-                                    std::__invoke(__proj, __result.max)))
-             __result.max = std::move(__tmp);
+           // Process two elements at a time so that we perform at most
+           // 3*N/2 many comparisons in total (each of the N/2 iterations

Is "many" a typo here?

+           // of this loop performs three comparisions).
+           auto __val1 = *__first;

Can we avoid making this copy if the range satisfies forward_range, by
keeping copies of the min/max iterators, or just forwarding to
ranges::minmax_element?

Hmm, on the other hand, for a forward range of ints we're probably
better off jsut making the copy here and not going through the
indirections done by minmax_element.

Maybe something like:

  if constexpr (forward_range<_Range> && is_scalar_v<range_value_t<_Range>>)


Reply via email to