diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 7032aaf5d91..ac069d1ab85 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -208,17 +208,70 @@ namespace ranges { // FUNCTION TEMPLATE _Rewrap_subrange template - _NODISCARD constexpr _Result _Rewrap_subrange(_Wrapped& _First, subrange<_Unwrapped>&& _UResult) { - // conditionally computes a wrapped subrange from a wrapped iterator and unwrapped subrange - _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Unwrapped_t<_Wrapped>, _Unwrapped>); + _NODISCARD constexpr _Result _Rewrap_subrange(_Wrapped& _Val, subrange<_Unwrapped>&& _UResult) { + // conditionally computes a wrapped subrange from a wrapped iterator or range and unwrapped subrange if constexpr (is_same_v<_Result, dangling>) { return dangling{}; + } else if constexpr (is_same_v<_Result, subrange<_Unwrapped>>) { + return _STD move(_UResult); + } else if constexpr (range<_Wrapped>) { + _STL_INTERNAL_STATIC_ASSERT(forward_range<_Wrapped>); + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Unwrapped, _Unwrapped_t>>); + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Result, subrange>>); + + auto _First = _RANGES begin(_Val); + auto _Last = _First; + _First._Seek_to(_UResult.begin()); + _Last._Seek_to(_UResult.end()); + return _Result{_STD move(_First), _STD move(_Last)}; } else { + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Unwrapped, _Unwrapped_t<_Wrapped>>); _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Result, subrange<_Wrapped>>); - auto _Last = _First; - _Seek_wrapped(_First, _UResult.begin()); - _Seek_wrapped(_Last, _UResult.end()); - return _Result{_STD move(_First), _STD move(_Last)}; + + auto _Last = _Val; + _Val._Seek_to(_UResult.begin()); + _Last._Seek_to(_UResult.end()); + return _Result{_STD move(_Val), _STD move(_Last)}; + } + } + + // FUNCTION TEMPLATE _Rewrap_iterator + template + _NODISCARD constexpr iterator_t<_Rng> _Rewrap_iterator(_Rng&& _Range, _It&& _Val) { + _STL_INTERNAL_STATIC_ASSERT(is_same_v, _Unwrapped_t>>); + + if constexpr (is_same_v, iterator_t<_Rng>>) { + return _STD forward<_It>(_Val); + } else { + auto _Result = _RANGES begin(_Range); + _Result._Seek_to(_STD forward<_It>(_Val)); + return _Result; + } + } + + // FUNCTION TEMPLATE _Get_final_iterator_unwrapped + template _Se> + _NODISCARD constexpr auto _Get_final_iterator_unwrapped(const _Unwrapped_t<_Wrapped>& _UFirst, _Se&& _Last) { + // find the iterator in [_UFirst, _Get_unwrapped(_Last)) which equals _Get_unwrapped(_Last) [possibly O(N)] + auto _ULast = _Get_unwrapped(_STD move(_Last)); + if constexpr (is_same_v<_Se, _Wrapped>) { + return _ULast; + } else if constexpr (sized_sentinel_for<_Se, _Wrapped>) { + return _RANGES next(_UFirst, _ULast - _UFirst); + } else { + return _RANGES next(_UFirst, _STD move(_ULast)); + } + } + + template + _NODISCARD constexpr auto _Get_final_iterator_unwrapped(_Rng& _Range) { + // find the (unwrapped) iterator in _Range which equals _Uend(_Range) [possibly O(N)] + if constexpr (common_range<_Rng>) { + return _Uend(_Range); + } else if constexpr (sized_range<_Rng>) { + return _RANGES next(_Ubegin(_Range), _RANGES distance(_Range)); + } else { + return _RANGES next(_Ubegin(_Range), _Uend(_Range)); } } @@ -1972,20 +2025,18 @@ namespace ranges { requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> _NODISCARD constexpr borrowed_subrange_t<_Rng1> operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { - auto _First1 = _RANGES begin(_Range1); - if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { const auto _Count1 = _RANGES distance(_Range1); const auto _Count2 = _RANGES distance(_Range2); - auto _UResult = _Search_sized(_Get_unwrapped(_First1), _Uend(_Range1), _Count1, + auto _UResult = _Search_sized(_Ubegin(_Range1), _Uend(_Range1), _Count1, _Ubegin(_Range2), _Uend(_Range2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Rewrap_subrange>(_First1, _STD move(_UResult)); + return _Rewrap_subrange>(_Range1, _STD move(_UResult)); } else { - auto _UResult = _Search_unsized(_Get_unwrapped(_First1), _Uend(_Range1), + auto _UResult = _Search_unsized(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Rewrap_subrange>(_First1, _STD move(_UResult)); + return _Rewrap_subrange>(_Range1, _STD move(_UResult)); } } // clang-format on @@ -2721,20 +2772,19 @@ namespace ranges { requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> _NODISCARD constexpr borrowed_subrange_t<_Rng1> operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { - auto _First1 = _RANGES begin(_Range1); if constexpr (random_access_range<_Rng1> && sized_range<_Rng1> && random_access_range<_Rng2> && sized_range<_Rng2>) { - auto _UResult = _Random_access_sized_ranges(_Get_unwrapped(_First1), _RANGES distance(_Range1), + auto _UResult = _Random_access_sized_ranges(_Ubegin(_Range1), _RANGES distance(_Range1), _Ubegin(_Range2), _RANGES distance(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Rewrap_subrange>(_First1, _STD move(_UResult)); + return _Rewrap_subrange>(_Range1, _STD move(_UResult)); } else if constexpr (_Bidi_common_range<_Rng1> && _Bidi_common_range<_Rng2>) { - auto _UResult = _Bidi_common_ranges(_Get_unwrapped(_First1), _Uend(_Range1), + auto _UResult = _Bidi_common_ranges(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Rewrap_subrange>(_First1, _STD move(_UResult)); + return _Rewrap_subrange>(_Range1, _STD move(_UResult)); } else { - auto _UResult = _Forward_ranges(_Get_unwrapped(_First1), _Uend(_Range1), + auto _UResult = _Forward_ranges(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Rewrap_subrange>(_First1, _STD move(_UResult)); + return _Rewrap_subrange>(_Range1, _STD move(_UResult)); } } // clang-format on @@ -3998,12 +4048,87 @@ _CONSTEXPR20 void push_heap(_RanIt _First, _RanIt _Last) { _STD push_heap(_First, _Last, less<>()); } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::push_heap + template + constexpr void _Push_heap_by_index(const _It _First, iter_difference_t<_It> _Hole, + const iter_difference_t<_It> _Top, iter_value_t<_It>&& _Val, _Pr _Pred, _Pj _Proj) { + // percolate _Hole to _Top or where _Val belongs, using _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + while (_Top < _Hole) { + const auto _Idx = static_cast>((_Hole - 1) >> 1); // shift for codegen + if (!_STD invoke(_Pred, _STD invoke(_Proj, *(_First + _Idx)), _STD invoke(_Proj, _Val))) { + break; + } + + // move _Hole up to parent + *(_First + _Hole) = _RANGES iter_move(_First + _Idx); + _Hole = _Idx; + } + + *(_First + _Hole) = _STD move(_Val); // drop _Val into final hole + } + + class _Push_heap_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + _Push_heap_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + template + requires sortable, _Pr, _Pj> + constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + if constexpr (common_range<_Rng>) { + _Push_heap_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _RANGES end(_Range); + } else { + auto _ULast = _Get_final_iterator_unwrapped(_Range); + _Push_heap_unchecked(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_ULast)); + } + } + // clang-format on + private: + template + static constexpr void _Push_heap_unchecked(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + const auto _Count = _Last - _First; + if (_Count < 2) { + return; + } + + --_Last; + iter_value_t<_It> _Val = _RANGES iter_move(_Last); + _RANGES _Push_heap_by_index(_STD move(_First), _Count - 1, 0, _STD move(_Val), _Pred, _Proj); + } + }; + + inline constexpr _Push_heap_fn push_heap{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE pop_heap template _CONSTEXPR20 void _Pop_heap_hole_by_index( _RanIt _First, _Iter_diff_t<_RanIt> _Hole, _Iter_diff_t<_RanIt> _Bottom, _Ty&& _Val, _Pr _Pred) { // percolate _Hole to _Bottom, then push _Val, using _Pred - _STL_INTERNAL_CHECK(_Bottom != 0); + _STL_INTERNAL_CHECK(_Bottom > 0); + using _Diff = _Iter_diff_t<_RanIt>; const _Diff _Top = _Hole; _Diff _Idx = _Hole; @@ -4061,13 +4186,115 @@ _CONSTEXPR20 void pop_heap(_RanIt _First, _RanIt _Last) { _STD pop_heap(_First, _Last, less<>()); } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::pop_heap + template + constexpr void _Pop_heap_hole_by_index(_It _First, iter_difference_t<_It> _Hole, + const iter_difference_t<_It> _Bottom, iter_value_t<_It>&& _Val, _Pr _Pred, _Pj _Proj) { + // percolate _Hole to _Bottom, then push _Val, using _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_Hole >= 0); + _STL_INTERNAL_CHECK(_Bottom > 0); + + using _Diff = iter_difference_t<_It>; + const _Diff _Top = _Hole; + _Diff _Idx = _Hole; + + // Check whether _Idx can have a child before calculating that child's index, since + // calculating the child's index can trigger integer overflows + const _Diff _Max_sequence_non_leaf = (_Bottom - 1) >> 1; // shift for codegen + while (_Idx < _Max_sequence_non_leaf) { // move _Hole down to larger child + _Idx = 2 * _Idx + 2; + auto _Mid = _First + _Idx; + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_RANGES prev(_Mid)))) { + --_Idx; + --_Mid; + } + *(_First + _Hole) = _RANGES iter_move(_Mid); + _Hole = _Idx; + } + + if (_Idx == _Max_sequence_non_leaf && _Bottom % 2 == 0) { // only child at bottom, move _Hole down to it + *(_First + _Hole) = _RANGES iter_move(_First + (_Bottom - 1)); + _Hole = _Bottom - 1; + } + + _RANGES _Push_heap_by_index(_STD move(_First), _Hole, _Top, _STD move(_Val), _Pred, _Proj); + } + + template + constexpr void _Pop_heap_hole_unchecked( + _It _First, const _It _Last, const _It _Dest, iter_value_t<_It>&& _Val, _Pr _Pred, _Pj _Proj) { + // pop *_First to *_Dest and reheap, using _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_First != _Last); + _STL_INTERNAL_CHECK(_First != _Dest); + + *_Dest = _RANGES iter_move(_First); + const auto _Count = _Last - _First; + _RANGES _Pop_heap_hole_by_index(_STD move(_First), 0, _Count, _STD move(_Val), _Pred, _Proj); + } + + template + constexpr void _Pop_heap_unchecked(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // pop *_First to *(_Last - 1) and reheap, using _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + if (_Last - _First < 2) { + return; + } + + --_Last; + iter_value_t<_It> _Val = _RANGES iter_move(_Last); + _RANGES _Pop_heap_hole_unchecked(_STD move(_First), _Last, _Last, _STD move(_Val), _Pred, _Proj); + } + + class _Pop_heap_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + _RANGES _Pop_heap_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + template + requires sortable, _Pr, _Pj> + constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + if constexpr (common_range<_Rng>) { + _RANGES _Pop_heap_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _RANGES end(_Range); + } else { + auto _ULast = _Get_final_iterator_unwrapped(_Range); + _RANGES _Pop_heap_unchecked(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_ULast)); + } + } + // clang-format on + }; + + inline constexpr _Pop_heap_fn pop_heap{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE make_heap template _CONSTEXPR20 void _Make_heap_unchecked(_RanIt _First, _RanIt _Last, _Pr _Pred) { // make nontrivial [_First, _Last) into a heap, using _Pred using _Diff = _Iter_diff_t<_RanIt>; _Diff _Bottom = _Last - _First; - for (_Diff _Hole = _Bottom >> 1; 0 < _Hole;) { // shift for codegen + for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen // reheap top half, bottom to top --_Hole; _Iter_value_t<_RanIt> _Val = _STD move(*(_First + _Hole)); @@ -4086,6 +4313,60 @@ _CONSTEXPR20 void make_heap(_RanIt _First, _RanIt _Last) { // make [_First, _Las _STD make_heap(_First, _Last, less<>()); } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::make_heap + class _Make_heap_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + _Make_heap_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + template + requires sortable, _Pr, _Pj> + constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + if constexpr (common_range<_Rng>) { + _Make_heap_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _RANGES end(_Range); + } else { + auto _ULast = _Get_final_iterator_unwrapped(_Range); + _Make_heap_unchecked(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_ULast)); + } + } + // clang-format on + private: + template + static constexpr void _Make_heap_unchecked(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // make nontrivial [_First, _Last) into a heap, using _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + using _Diff = iter_difference_t<_It>; + const _Diff _Bottom = _Last - _First; + for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen + // reheap top half, bottom to top + --_Hole; + iter_value_t<_It> _Val = _RANGES iter_move(_First + _Hole); + _RANGES _Pop_heap_hole_by_index(_First, _Hole, _Bottom, _STD move(_Val), _Pred, _Proj); + } + } + }; + + inline constexpr _Make_heap_fn make_heap{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATES is_heap AND is_heap_until template _CONSTEXPR20 _RanIt _Is_heap_until_unchecked(_RanIt _First, _RanIt _Last, _Pr _Pred) { @@ -4150,13 +4431,98 @@ _NODISCARD bool is_heap(_ExPo&& _Exec, _RanIt _First, _RanIt _Last) noexcept /* // test if range is a heap ordered by operator< return _STD is_heap(_STD forward<_ExPo>(_Exec), _First, _Last, less{}); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::is_heap + template + _NODISCARD constexpr _It _Is_heap_until_unchecked( + _It _First, const iter_difference_t<_It> _Size, _Pr _Pred, _Pj _Proj) { + // find extent of counted range that is a heap ordered with respect to _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It, _Pj>>); + + if (_Size == 0) { + return _First; + } + + using _Diff = iter_difference_t<_It>; + _Diff _Off = 1; + for (; _Off < _Size; ++_Off) { + const auto _Mid1 = _First + static_cast<_Diff>((_Off - 1) >> 1); // shift for codegen + auto _Mid2 = _First + _Off; + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid1), _STD invoke(_Proj, *_Mid2))) { + return _Mid2; + } + } + + return _First + _Off; + } + + class _Is_heap_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_strict_weak_order> _Pr = ranges::less> + _NODISCARD constexpr bool operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + const auto _Size = _RANGES distance(_UFirst, _ULast); + const auto _UResult = + _RANGES _Is_heap_until_unchecked(_STD move(_UFirst), _Size, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _UResult == _ULast; + } + + template , _Pj>> _Pr = ranges::less> + _NODISCARD constexpr bool operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + const auto _Size = _RANGES distance(_Range); + const auto _UResult = + _RANGES _Is_heap_until_unchecked(_Ubegin(_Range), _Size, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _UResult == _Uend(_Range); + } + }; + + inline constexpr _Is_heap_fn is_heap{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::is_heap_until + class _Is_heap_until_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_strict_weak_order> _Pr = ranges::less> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _Size = _RANGES distance(_UFirst, _Get_unwrapped(_STD move(_Last))); + auto _UResult = + _RANGES _Is_heap_until_unchecked(_STD move(_UFirst), _Size, _Pass_fn(_Pred), _Pass_fn(_Proj)); + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + template , _Pj>> _Pr = ranges::less> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + const auto _Size = _RANGES distance(_Range); + auto _UResult = _RANGES _Is_heap_until_unchecked(_Ubegin(_Range), _Size, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_UResult)); + } + }; + + inline constexpr _Is_heap_until_fn is_heap_until{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE sort_heap template _CONSTEXPR20 void _Sort_heap_unchecked(_RanIt _First, _RanIt _Last, _Pr _Pred) { // order heap by repeatedly popping, using _Pred - for (; 2 <= _Last - _First; --_Last) { + for (; _Last - _First >= 2; --_Last) { _Pop_heap_unchecked(_First, _Last, _Pred); } } @@ -4182,6 +4548,51 @@ _CONSTEXPR20 void sort_heap(_RanIt _First, _RanIt _Last) { // order heap by repe #ifdef __cpp_lib_concepts namespace ranges { + // VARIABLE ranges::sort_heap + class _Sort_heap_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + _Sort_heap_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + template + requires sortable, _Pr, _Pj> + constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + if constexpr (common_range<_Rng>) { + _Sort_heap_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _RANGES end(_Range); + } else { + auto _ULast = _Get_final_iterator_unwrapped(_Range); + _Sort_heap_unchecked(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_ULast)); + } + } + // clang-format on + private: + template + static constexpr void _Sort_heap_unchecked(const _It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // order heap by repeatedly popping, using _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + for (; _Last - _First >= 2; --_Last) { + _RANGES _Pop_heap_unchecked(_First, _Last, _Pred, _Proj); + } + } + }; + + inline constexpr _Sort_heap_fn sort_heap{_Not_quite_object::_Construct_tag{}}; + // VARIABLE ranges::lower_bound template _NODISCARD constexpr _It _Lower_bound_unchecked( diff --git a/stl/inc/array b/stl/inc/array index f10f39837b1..d51bc8f54b9 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -193,8 +193,7 @@ private: } if (_Off < 0) { -#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned - _STL_VERIFY(_Idx >= -static_cast(_Off), "cannot seek array iterator before begin"); + _STL_VERIFY(_Idx >= size_t{0} - static_cast(_Off), "cannot seek array iterator before begin"); } if (_Off > 0) { @@ -204,7 +203,7 @@ private: _CONSTEXPR17 _Array_const_iterator& operator+=(const ptrdiff_t _Off) noexcept { _Verify_offset(_Off); - _Idx += _Off; + _Idx += static_cast(_Off); return *this; } diff --git a/stl/inc/iterator b/stl/inc/iterator index f4ba3bcce7e..fee3511a908 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -582,8 +582,8 @@ public: } if (_Off < 0) { -#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned - _STL_VERIFY(_Myindex >= -static_cast(_Off), "cannot seek checked_array_iterator before begin"); + _STL_VERIFY( + _Myindex >= size_t{0} - static_cast(_Off), "cannot seek checked_array_iterator before begin"); } if (_Off > 0) { @@ -619,9 +619,8 @@ public: } if (_Off < 0) { -#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned - _STL_VERIFY( - _Mysize - _Myindex >= -static_cast(_Off), "cannot seek checked_array_iterator after end"); + _STL_VERIFY(_Mysize - _Myindex >= size_t{0} - static_cast(_Off), + "cannot seek checked_array_iterator after end"); } _Myindex -= _Off; @@ -681,9 +680,8 @@ public: constexpr void _Verify_offset(const difference_type _Off) const noexcept { if (_Off < 0) { -#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned - _STL_VERIFY( - _Myindex >= -static_cast(_Off), "cannot seek checked_array_iterator iterator before begin"); + _STL_VERIFY(_Myindex >= size_t{0} - static_cast(_Off), + "cannot seek checked_array_iterator iterator before begin"); } if (_Off > 0) { diff --git a/stl/inc/xstring b/stl/inc/xstring index 73b64e8fb8a..3f4deea83ae 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -1033,8 +1033,8 @@ public: } if (_Off < 0) { -#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned - _STL_VERIFY(_Myoff >= -static_cast(_Off), "cannot seek string_view iterator before begin"); + _STL_VERIFY( + _Myoff >= size_t{0} - static_cast(_Off), "cannot seek string_view iterator before begin"); } if (_Off > 0) { @@ -1073,8 +1073,8 @@ public: } if (_Off < 0) { -#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned - _STL_VERIFY(_Mysize - _Myoff >= -static_cast(_Off), "cannot seek string_view iterator after end"); + _STL_VERIFY(_Mysize - _Myoff >= size_t{0} - static_cast(_Off), + "cannot seek string_view iterator after end"); } _Myoff -= _Off; @@ -1903,7 +1903,6 @@ public: const auto _Rawptr = _Unfancy(_Ptr); if (_Off < 0) { -#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned _STL_VERIFY(_Contptr - _Rawptr <= _Off, "cannot seek string iterator before begin"); } diff --git a/tests/std/test.lst b/tests/std/test.lst index 413f56a4325..c78a27ee8fb 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -252,6 +252,7 @@ tests\P0896R4_ranges_alg_find_if tests\P0896R4_ranges_alg_find_if_not tests\P0896R4_ranges_alg_for_each tests\P0896R4_ranges_alg_for_each_n +tests\P0896R4_ranges_alg_heap tests\P0896R4_ranges_alg_is_permutation tests\P0896R4_ranges_alg_is_sorted tests\P0896R4_ranges_alg_minmax diff --git a/tests/std/tests/P0896R4_ranges_alg_heap/env.lst b/tests/std/tests/P0896R4_ranges_alg_heap/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_heap/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_heap/test.cpp b/tests/std/tests/P0896R4_ranges_alg_heap/test.cpp new file mode 100644 index 00000000000..ac24c5645f2 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_heap/test.cpp @@ -0,0 +1,219 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Covers ranges::push_heap, ranges::pop_heap, ranges::make_heap, ranges::is_heap, ranges::is_heap_until, and +// ranges::sort_heap + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +#define ASSERT(...) assert((__VA_ARGS__)) + +// Validate dangling story +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), int*>); +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), int*>); +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), int*>); +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), int*>); +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), int*>); + +using P = pair; + +struct empty_ranges { + template + static constexpr void call() { + // Validate empty ranges (only make_heap and sort_heap accept empty ranges) + const Range range{}; + + ASSERT(ranges::make_heap(range, ranges::less{}, get_first) == ranges::end(range)); + ASSERT(ranges::make_heap(ranges::begin(range), ranges::end(range), ranges::less{}, get_first) + == ranges::end(range)); + + ASSERT(ranges::sort_heap(range, ranges::less{}, get_first) == ranges::end(range)); + ASSERT(ranges::sort_heap(ranges::begin(range), ranges::end(range), ranges::less{}, get_first) + == ranges::end(range)); + } +}; + +struct is_heap_test { + template + static constexpr void call() { + // Validate is_heap and is_heap_until + using ranges::is_heap, ranges::is_heap_until, ranges::less, ranges::swap; + + array buff = { + P{1668617627, 0}, + P{1429106719, 1}, + P{-47163201, 2}, + P{-441494788, 3}, + P{-1200257975, 4}, + P{-1459960308, 5}, + P{-912489821, 6}, + P{-2095681771, 7}, + P{-1298559576, 8}, + P{-1260655766, 9}, + }; + const Range wrapped{buff}; + + ASSERT(is_heap(wrapped, less{}, get_first)); + ASSERT(is_heap(wrapped.begin(), wrapped.end(), less{}, get_first)); + + ASSERT(is_heap_until(wrapped, less{}, get_first) == wrapped.end()); + ASSERT(is_heap_until(wrapped.begin(), wrapped.end(), less{}, get_first) == wrapped.end()); + + swap(buff[0], buff[1]); + + ASSERT(!is_heap(wrapped, less{}, get_first)); + ASSERT(!is_heap(wrapped.begin(), wrapped.end(), less{}, get_first)); + + ASSERT(is_heap_until(wrapped, less{}, get_first) == wrapped.begin() + 1); + ASSERT(is_heap_until(wrapped.begin(), wrapped.end(), less{}, get_first) == wrapped.begin() + 1); + } +}; + +struct make_and_sort_heap_test { + static constexpr array initial_values = { + P{-1200257975, 0}, + P{-1260655766, 1}, + P{-1298559576, 2}, + P{-1459960308, 3}, + P{-2095681771, 4}, + P{-441494788, 5}, + P{-47163201, 6}, + P{-912489821, 7}, + P{1429106719, 8}, + P{1668617627, 9}, + }; + + template + static constexpr void call() { + // Validate make_heap and sort_heap + using ranges::make_heap, ranges::sort_heap, ranges::is_heap, ranges::is_sorted, ranges::less, ranges::swap; + + { + auto buff = initial_values; + const Range wrapped{buff}; + + ASSERT(!is_heap(wrapped, less{}, get_first)); + + make_heap(wrapped, less{}, get_first); + ASSERT(is_heap(wrapped, less{}, get_first)); + + sort_heap(wrapped, less{}, get_first); + ASSERT(is_sorted(wrapped, less{}, get_first)); + } + + { + auto buff = initial_values; + const Range wrapped{buff}; + + ASSERT(!is_heap(wrapped.begin(), wrapped.end(), less{}, get_first)); + + make_heap(wrapped.begin(), wrapped.end(), less{}, get_first); + ASSERT(is_heap(wrapped.begin(), wrapped.end(), less{}, get_first)); + + sort_heap(wrapped.begin(), wrapped.end(), less{}, get_first); + ASSERT(is_sorted(wrapped.begin(), wrapped.end(), less{}, get_first)); + } + } +}; + +struct push_and_pop_heap_test { + static constexpr array initial_values = { + P{1668617627, 0}, + P{1429106719, 1}, + P{-47163201, 2}, + P{-441494788, 3}, + P{-1200257975, 4}, + P{-1459960308, 5}, + P{-912489821, 6}, + P{-2095681771, 7}, + P{-1298559576, 8}, + P{-1260655766, 9}, + }; + STATIC_ASSERT(ranges::is_heap(initial_values, ranges::less{}, get_first)); + + static constexpr array expectedPushed = { + P{1668617627, 0}, + P{1429106719, 1}, + P{-47163201, 2}, + P{-1260655766, 9}, + P{-441494788, 3}, + P{-1459960308, 5}, + P{-912489821, 6}, + P{-2095681771, 7}, + P{-1298559576, 8}, + P{-1200257975, 4}, + }; + STATIC_ASSERT(ranges::is_heap(expectedPushed, ranges::less{}, get_first)); + + static constexpr array expectedPopped = { + P{1429106719, 1}, + P{-441494788, 3}, + P{-47163201, 2}, + P{-1260655766, 9}, + P{-1200257975, 4}, + P{-1459960308, 5}, + P{-912489821, 6}, + P{-2095681771, 7}, + P{-1298559576, 8}, + P{1668617627, 0}, + }; + STATIC_ASSERT(ranges::is_heap(expectedPopped.begin(), expectedPopped.end() - 1, ranges::less{}, get_first)); + + template + static constexpr void call() { + // Validate push_heap and pop_heap + using ranges::push_heap, ranges::pop_heap, ranges::equal, ranges::is_heap, ranges::less; + + { + auto buff = initial_values; + const Range wrapped{buff}; + + pop_heap(wrapped, less{}, get_first); + ASSERT(equal(wrapped, expectedPopped)); + + push_heap(wrapped, less{}, get_first); + ASSERT(equal(wrapped, expectedPushed)); + } + + { + auto buff = initial_values; + const Range wrapped{buff}; + + pop_heap(wrapped.begin(), wrapped.end(), less{}, get_first); + ASSERT(is_heap(expectedPopped.begin(), expectedPopped.end() - 1, less{}, get_first)); + ASSERT(equal(wrapped.begin(), wrapped.end(), expectedPopped.begin(), expectedPopped.end())); + + push_heap(wrapped.begin(), wrapped.end(), less{}, get_first); + ASSERT(is_heap(expectedPushed, less{}, get_first)); + ASSERT(equal(wrapped.begin(), wrapped.end(), expectedPushed.begin(), expectedPushed.end())); + } + } +}; + +int main() { + STATIC_ASSERT((test_random(), true)); + test_random(); + + STATIC_ASSERT((test_random(), true)); + test_random(); + + STATIC_ASSERT((test_random(), true)); + test_random(); + + STATIC_ASSERT((test_random(), true)); + test_random(); +}