On Tue, May 20, 2025 at 3:17 PM Tomasz Kaminski <tkami...@redhat.com> wrote:
> > > On Tue, May 20, 2025 at 3:07 PM Patrick Palka <ppa...@redhat.com> wrote: > >> On Tue, 20 May 2025, Tomasz Kaminski wrote: >> >> > >> > >> > On Mon, May 19, 2025 at 6:06 PM Patrick Palka <ppa...@redhat.com> >> wrote: >> > On Mon, 19 May 2025, Patrick Palka wrote: >> > >> > > Changes in v3: >> > > * Use the forward_range code path for a (non-sized) >> bidirectional >> > > haystack, since it's slightly fewer increments/decrements >> > > overall. >> > > * Fix wrong iter_difference_t cast in starts_with. >> > > >> > > Changes in v2: >> > > Addressed Tomasz's review comments, namely: >> > > * Added explicit iter_difference_t casts >> > > * Made _S_impl member private >> > > * Optimized sized bidirectional case of ends_with >> > > * Rearranged control flow of starts_with::_S_impl >> > > >> > > Still left to do: >> > > * Add tests for integer-class types >> > > * Still working on a better commit description ;) >> > > >> > > -- >8 -- >> > > >> > > libstdc++-v3/ChangeLog: >> > > >> > > * include/bits/ranges_algo.h (__starts_with_fn, >> starts_with): >> > > Define. >> > > (__ends_with_fn, ends_with): Define. >> > > * include/bits/version.def (ranges_starts_ends_with): >> Define. >> > > * include/bits/version.h: Regenerate. >> > > * include/std/algorithm: Provide >> __cpp_lib_ranges_starts_ends_with. >> > > * src/c++23/std.cc.in (ranges::starts_with): Export. >> > > (ranges::ends_with): Export. >> > > * testsuite/25_algorithms/ends_with/1.cc: New test. >> > > * testsuite/25_algorithms/starts_with/1.cc: New test. >> > > --- >> > > libstdc++-v3/include/bits/ranges_algo.h | 236 >> ++++++++++++++++++ >> > > libstdc++-v3/include/bits/version.def | 8 + >> > > libstdc++-v3/include/bits/version.h | 10 + >> > > libstdc++-v3/include/std/algorithm | 1 + >> > > libstdc++-v3/src/c++23/std.cc.in | 4 + >> > > .../testsuite/25_algorithms/ends_with/1.cc | 129 ++++++++++ >> > > .../testsuite/25_algorithms/starts_with/1.cc | 128 ++++++++++ >> > > 7 files changed, 516 insertions(+) >> > > create mode 100644 >> libstdc++-v3/testsuite/25_algorithms/ends_with/1.cc >> > > create mode 100644 >> libstdc++-v3/testsuite/25_algorithms/starts_with/1.cc >> > > >> > > diff --git a/libstdc++-v3/include/bits/ranges_algo.h >> b/libstdc++-v3/include/bits/ranges_algo.h >> > > index f36e7dd59911..54646ae62f7e 100644 >> > > --- a/libstdc++-v3/include/bits/ranges_algo.h >> > > +++ b/libstdc++-v3/include/bits/ranges_algo.h >> > > @@ -438,6 +438,242 @@ namespace ranges >> > > >> > > inline constexpr __search_n_fn search_n{}; >> > > >> > > +#if __glibcxx_ranges_starts_ends_with // C++ >= 23 >> > > + struct __starts_with_fn >> > > + { >> > > + template<input_iterator _Iter1, sentinel_for<_Iter1> >> _Sent1, >> > > + input_iterator _Iter2, sentinel_for<_Iter2> _Sent2, >> > > + typename _Pred = ranges::equal_to, >> > > + typename _Proj1 = identity, typename _Proj2 = >> identity> >> > > + requires indirectly_comparable<_Iter1, _Iter2, _Pred, >> _Proj1, _Proj2> >> > > + constexpr bool >> > > + operator()(_Iter1 __first1, _Sent1 __last1, >> > > + _Iter2 __first2, _Sent2 __last2, _Pred __pred = >> {}, >> > > + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const >> > > + { >> > > + iter_difference_t<_Iter1> __n1 = -1; >> > > + iter_difference_t<_Iter2> __n2 = -1; >> > > + if constexpr (sized_sentinel_for<_Sent1, _Iter1>) >> > > + __n1 = __last1 - __first1; >> > > + if constexpr (sized_sentinel_for<_Sent2, _Iter2>) >> > > + __n2 = __last2 - __first2; >> > > + return _S_impl(std::move(__first1), __last1, __n1, >> > > + std::move(__first2), __last2, __n2, >> > > + std::move(__pred), >> > > + std::move(__proj1), std::move(__proj2)); >> > > + } >> > > + >> > > + template<input_range _Range1, input_range _Range2, >> > > + typename _Pred = ranges::equal_to, >> > > + typename _Proj1 = identity, typename _Proj2 = >> identity> >> > > + requires indirectly_comparable<iterator_t<_Range1>, >> iterator_t<_Range2>, >> > > + _Pred, _Proj1, _Proj2> >> > > + constexpr bool >> > > + operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred >> = {}, >> > > + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const >> > > + { >> > > + range_difference_t<_Range1> __n1 = -1; >> > > + range_difference_t<_Range1> __n2 = -1; >> > > + if constexpr (sized_range<_Range1>) >> > > + __n1 = ranges::size(__r1); >> > > + if constexpr (sized_range<_Range2>) >> > > + __n2 = ranges::size(__r2); >> > > + return _S_impl(ranges::begin(__r1), ranges::end(__r1), >> __n1, >> > > + ranges::begin(__r2), ranges::end(__r2), >> __n2, >> > > + std::move(__pred), >> > > + std::move(__proj1), std::move(__proj2)); >> > > + } >> > > + >> > > + private: >> > > + template<typename _Iter1, typename _Sent1, typename >> _Iter2, typename _Sent2, >> > > + typename _Pred, >> > > + typename _Proj1, typename _Proj2> >> > > + static constexpr bool >> > > + _S_impl(_Iter1 __first1, _Sent1 __last1, >> iter_difference_t<_Iter1> __n1, >> > > + _Iter2 __first2, _Sent2 __last2, >> iter_difference_t<_Iter2> __n2, >> > > + _Pred __pred, >> > > + _Proj1 __proj1, _Proj2 __proj2) >> > > + { >> > > + if (__first2 == __last2) [[unlikely]] >> > > + return true; >> > > + else if (__n1 == -1 || __n2 == -1) >> > > + return ranges::mismatch(std::move(__first1), __last1, >> > > + std::move(__first2), __last2, >> > > + std::move(__pred), >> > > + std::move(__proj1), >> std::move(__proj2)).in2 == __last2; >> > > + else if (__n1 < __n2) >> > > + return false; >> > > + else if constexpr (random_access_iterator<_Iter1>) >> > > + return ranges::equal(__first1, __first1 + >> iter_difference_t<_Iter1>(__n2), >> > > + std::move(__first2), __last2, >> > > + std::move(__pred), >> > > + std::move(__proj1), >> std::move(__proj2)); >> > > + else >> > > + return >> ranges::equal(counted_iterator(std::move(__first1), >> > > + >> iter_difference_t<_Iter1>(__n2)), >> > > + default_sentinel, >> > > + std::move(__first2), __last2, >> > > + std::move(__pred), >> > > + std::move(__proj1), >> std::move(__proj2)); >> > > + } >> > > + }; >> > > + >> > > + inline constexpr __starts_with_fn starts_with{}; >> > > + >> > > + struct __ends_with_fn >> > > + { >> > > + template<input_iterator _Iter1, sentinel_for<_Iter1> >> _Sent1, >> > > + input_iterator _Iter2, sentinel_for<_Iter2> _Sent2, >> > > + typename _Pred = ranges::equal_to, >> > > + typename _Proj1 = identity, typename _Proj2 = >> identity> >> > > + requires (forward_iterator<_Iter1> || >> sized_sentinel_for<_Sent1, _Iter1>) >> > > + && (forward_iterator<_Iter2> || >> sized_sentinel_for<_Sent2, _Iter2>) >> > > + && indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, >> _Proj2> >> > > + constexpr bool >> > > + operator()(_Iter1 __first1, _Sent1 __last1, >> > > + _Iter2 __first2, _Sent2 __last2, _Pred __pred = >> {}, >> > > + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const >> > > + { >> > > + iter_difference_t<_Iter1> __n1 = -1; >> > > + iter_difference_t<_Iter2> __n2 = -1; >> > > + if constexpr (sized_sentinel_for<_Sent1, _Iter1>) >> > > + __n1 = __last1 - __first1; >> > > + if constexpr (sized_sentinel_for<_Sent2, _Iter2>) >> > > + __n2 = __last2 - __first2; >> > > + return _S_impl(std::move(__first1), __last1, __n1, >> > > + std::move(__first2), __last2, __n2, >> > > + std::move(__pred), >> > > + std::move(__proj1), std::move(__proj2)); >> > > + } >> > > + >> > > + template<input_range _Range1, input_range _Range2, >> > > + typename _Pred = ranges::equal_to, >> > > + typename _Proj1 = identity, typename _Proj2 = >> identity> >> > > + requires (forward_range<_Range1> || sized_range<_Range1>) >> > > + && (forward_range<_Range2> || sized_range<_Range2>) >> > > + && indirectly_comparable<iterator_t<_Range1>, >> iterator_t<_Range2>, >> > > + _Pred, _Proj1, _Proj2> >> > > + constexpr bool >> > > + operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred >> = {}, >> > > + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const >> > > + { >> > > + range_difference_t<_Range1> __n1 = -1; >> > > + range_difference_t<_Range1> __n2 = -1; >> > > + if constexpr (sized_range<_Range1>) >> > > + __n1 = ranges::size(__r1); >> > > + if constexpr (sized_range<_Range2>) >> > > + __n2 = ranges::size(__r2); >> > > + return _S_impl(ranges::begin(__r1), ranges::end(__r1), >> __n1, >> > > + ranges::begin(__r2), ranges::end(__r2), >> __n2, >> > > + std::move(__pred), >> > > + std::move(__proj1), std::move(__proj2)); >> > > + } >> > > + >> > > + private: >> > > + template<typename _Iter1, typename _Sent1, >> > > + typename _Iter2, typename _Sent2, >> > > + typename _Pred, >> > > + typename _Proj1, typename _Proj2> >> > > + static constexpr bool >> > > + _S_impl(_Iter1 __first1, _Sent1 __last1, >> iter_difference_t<_Iter1> __n1, >> > > + _Iter2 __first2, _Sent2 __last2, >> iter_difference_t<_Iter2> __n2, >> > > + _Pred __pred, >> > > + _Proj1 __proj1, _Proj2 __proj2) >> > > + { >> > > + if (__first2 == __last2) [[unlikely]] >> > > + return true; >> > > + >> > > + if (__n2 == -1) >> > > + { >> > > + if constexpr (forward_iterator<_Iter2>) >> > > + __n2 = ranges::distance(__first2, __last2); >> > > + else >> > > + // Size is known and passed by the caller. >> > > + __builtin_unreachable(); >> > > + } >> > > + >> > > + if (__n1 != -1) >> > > + { >> > > + if (__n1 < __n2) >> > > + return false; >> > > + if constexpr (random_access_iterator<_Iter1> >> > > + || !bidirectional_iterator<_Iter1> >> > > + || !same_as<_Iter1, _Sent1>) >> > >> > I begin to wonder what is the benefit of not skipping common >> bidirectional ranges here? >> > Code below for random access ranges will call operator- (which is O(1)) >> > and equal. The bidirectional case will decrease the range n2 times. >> > Should we just check random_access_range? >> >> Hmm, but common bidirectional ranges do get skipped in this both-sized >> code path? The if constexpr condition as written should be true for >> non-common bidirectional ranges, false for common bidirectional ranges. >> Common bidirectional ranges currently should always go through the >> bidirectional_iterator && same_as code path. >> > I think I caused misunderstanding, I do not see reason to exclude > bidirectional > common ranges here, the random_access_path seem to do more efficient, > as it's call equal with same arguments, but does only subtraction which is > O(1) > instead of n2 decrements. > Sorry, I got myself totally confused for a moment by the checks. This only rejects bidirectional, common ranges, that are not random access, because them we can iterate backwards. But then iterating backwards is valuable only if n2 < (n1 - n2), and we could know that at this point. And n2 << n1 is fine, if we think of this algorithm as search, but I think it is closer to equal. >> > >> > Did you mean to optimize bidirectional common not sized ranges, by >> handling them without computing distance: >> > i.e. having something like this in beginning (just after first2 == >> last2). >> > if constexpr(bidirectional_iterator<_Iter1> && same_as<_Iter1, _Sent1> >> > && bidirectional_iterator<_Iter2> && same_as<_Iter2, >> _Sent2>) >> > if (n1 != -1 || n2 != -1) >> > return std::mismatch(make_reverse_iterator(__last1), >> make_reverse_iterator(__first1), >> > make_reverse_iterator(__last2), >> make_reverse_iterator(__first2)).in2.base() == __first2; >> >> I didn't consider that but that sounds very worthwhile, I'll try >> implementing it. I think it should be preferred over the both-sized >> code path even. >> > In that case use starts_with::_S_impl on a reversed iterator. This will > optimize the sized cases. > >> >> > } >> > > + { >> > > + ranges::advance(__first1, __n1 - >> iter_difference_t<_Iter1>(__n2)); >> > > + return ranges::equal(std::move(__first1), __last1, >> > > + std::move(__first2), __last2, >> > > + std::move(__pred), >> > > + std::move(__proj1), >> std::move(__proj2)); >> > > + } >> > > + } >> > > + >> > > + if constexpr (bidirectional_iterator<_Iter1> && >> same_as<_Iter1, _Sent1>) >> > > + { >> > > + _Iter1 __it1 = ranges::next(__first1, __last1); >> > >> > We can just initialize __it1 to __last1 now that this code path >> requires >> > common-ness. >> > >> > > + if (__n1 != -1) >> > > + ranges::advance(__it1, >> -iter_difference_t<_Iter1>(__n2), __first1); >> > >> > We don't need to use the three-parameter versions of advance here >> since >> > we know __n1 >= __n2. Consider these two things fixed >> > >> > > + else >> > > + { >> > > + // We can't use ranges::advance if the haystack >> size is >> > > + // unknown, since we need to detect and return >> false if >> > > + // it's smaller than the needle. >> > > + iter_difference_t<_Iter2> __m = __n2; >> > > + while (__m != 0 && __it1 != __first1) >> > > + { >> > > + --__m; >> > > + --__it1; >> > > + } >> > > + if (__m != 0) >> > > + return false; >> > > + } >> > > + return ranges::equal(__it1, __last1, >> > > + std::move(__first2), __last2, >> > > + std::move(__pred), >> > > + std::move(__proj1), >> std::move(__proj2)); >> > > + } >> > > + else if constexpr (forward_iterator<_Iter1>) >> > > + { >> > > + _Iter1 __prev_first1; >> > > + __n1 = 0; >> > > + while (true) >> > > + { >> > > + iter_difference_t<_Iter2> __m = __n2; >> > > + _Iter1 __it1 = __first1; >> > > + while (__m != 0 && __it1 != __last1) >> > > + { >> > > + ++__n1; >> > > + --__m; >> > > + ++__it1; >> > > + } >> > > + if (__m != 0) >> > > + { >> > > + // __glibcxx_assert(__it1 == __last1); >> > > + if (__n1 < __n2) >> > > + return false; >> > > + __first1 = ranges::next(__prev_first1, >> > > + >> iter_difference_t<_Iter1>(__n2 - __m)); >> > > + break; >> > > + } >> > > + __prev_first1 = __first1; >> > > + __first1 = __it1; >> > > + } >> > > + return ranges::equal(__first1, __last1, >> > > + std::move(__first2), __last2, >> > > + std::move(__pred), >> > > + std::move(__proj1), >> std::move(__proj2)); >> > > + } >> > > + else >> > > + // If __first1 is non-forward, it must be sized, so it >> was handled >> > > + // by the __n1 != -1 case earlier. >> > > + __builtin_unreachable(); >> > > + } >> > > + >> > > + }; >> > > + >> > > + inline constexpr __ends_with_fn ends_with{}; >> > > +#endif // __glibcxx_ranges_starts_ends_with >> > > + >> > > struct __find_end_fn >> > > { >> > > template<forward_iterator _Iter1, sentinel_for<_Iter1> >> _Sent1, >> > > diff --git a/libstdc++-v3/include/bits/version.def >> b/libstdc++-v3/include/bits/version.def >> > > index 6ca148f0488f..8db1967cc184 100644 >> > > --- a/libstdc++-v3/include/bits/version.def >> > > +++ b/libstdc++-v3/include/bits/version.def >> > > @@ -1660,6 +1660,14 @@ ftms = { >> > > }; >> > > }; >> > > >> > > +ftms = { >> > > + name = ranges_starts_ends_with; >> > > + values = { >> > > + v = 202106; >> > > + cxxmin = 23; >> > > + }; >> > > +}; >> > > + >> > > ftms = { >> > > name = constexpr_bitset; >> > > values = { >> > > diff --git a/libstdc++-v3/include/bits/version.h >> b/libstdc++-v3/include/bits/version.h >> > > index 48a090c14a3d..3749528633f2 100644 >> > > --- a/libstdc++-v3/include/bits/version.h >> > > +++ b/libstdc++-v3/include/bits/version.h >> > > @@ -1848,6 +1848,16 @@ >> > > #endif /* !defined(__cpp_lib_ranges_find_last) && >> defined(__glibcxx_want_ranges_find_last) */ >> > > #undef __glibcxx_want_ranges_find_last >> > > >> > > +#if !defined(__cpp_lib_ranges_starts_ends_with) >> > > +# if (__cplusplus >= 202100L) >> > > +# define __glibcxx_ranges_starts_ends_with 202106L >> > > +# if defined(__glibcxx_want_all) || >> defined(__glibcxx_want_ranges_starts_ends_with) >> > > +# define __cpp_lib_ranges_starts_ends_with 202106L >> > > +# endif >> > > +# endif >> > > +#endif /* !defined(__cpp_lib_ranges_starts_ends_with) && >> defined(__glibcxx_want_ranges_starts_ends_with) */ >> > > +#undef __glibcxx_want_ranges_starts_ends_with >> > > + >> > > #if !defined(__cpp_lib_constexpr_bitset) >> > > # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && >> (__cpp_constexpr_dynamic_alloc) >> > > # define __glibcxx_constexpr_bitset 202202L >> > > diff --git a/libstdc++-v3/include/std/algorithm >> b/libstdc++-v3/include/std/algorithm >> > > index 321a5e22d86b..1563cdf2b17c 100644 >> > > --- a/libstdc++-v3/include/std/algorithm >> > > +++ b/libstdc++-v3/include/std/algorithm >> > > @@ -74,6 +74,7 @@ >> > > #define __glibcxx_want_ranges_contains >> > > #define __glibcxx_want_ranges_find_last >> > > #define __glibcxx_want_ranges_fold >> > > +#define __glibcxx_want_ranges_starts_ends_with >> > > #define __glibcxx_want_robust_nonmodifying_seq_ops >> > > #define __glibcxx_want_sample >> > > #define __glibcxx_want_shift >> > > diff --git a/libstdc++-v3/src/c++23/std.cc.in >> b/libstdc++-v3/src/c++23/std.cc.in >> > > index 417c8a1a5626..2e4b3f75234f 100644 >> > > --- a/libstdc++-v3/src/c++23/std.cc.in >> > > +++ b/libstdc++-v3/src/c++23/std.cc.in >> > > @@ -506,6 +506,10 @@ export namespace std >> > > using ranges::find_last; >> > > using ranges::find_last_if; >> > > using ranges::find_last_if_not; >> > > +#endif >> > > +#if __cpp_lib_ranges_starts_ends_with >> > > + using ranges::starts_with; >> > > + using ranges::ends_with; >> > > #endif >> > > } >> > > } >> > > diff --git >> a/libstdc++-v3/testsuite/25_algorithms/ends_with/1.cc >> b/libstdc++-v3/testsuite/25_algorithms/ends_with/1.cc >> > > new file mode 100644 >> > > index 000000000000..e5714101aecc >> > > --- /dev/null >> > > +++ b/libstdc++-v3/testsuite/25_algorithms/ends_with/1.cc >> > > @@ -0,0 +1,129 @@ >> > > +// { dg-do run { target c++23 } } >> > > + >> > > +#include <algorithm> >> > > + >> > > +#include <testsuite_hooks.h> >> > > +#include <testsuite_iterators.h> >> > > + >> > > +namespace ranges = std::ranges; >> > > + >> > > +template<typename Range1, typename Range2> >> > > +void >> > > +test01() >> > > +{ >> > > + int n[] = {1,2,3,4,5,6,7,8,9,10}; >> > > + >> > > + Range1 haystack(n, n+10); >> > > + Range2 needle(n+7, n+10); >> > > + VERIFY( ranges::ends_with(haystack, needle) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( ranges::ends_with(haystack, needle) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( !ranges::ends_with(haystack, needle) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( ranges::ends_with(haystack, needle, >> > > + [](int n, int m) { return std::abs(n >> - m) <= 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( ranges::ends_with(haystack, needle, >> > > + ranges::equal_to{}, >> > > + [](int n) { return n - 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( ranges::ends_with(haystack, needle, >> > > + ranges::equal_to{}, >> > > + std::identity{}, >> > > + [](int n) { return n + 1; }) ); >> > > + >> > > + haystack = Range1(n, n+5); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( !ranges::ends_with(haystack, needle) ); >> > > +} >> > > + >> > > +template<typename Range1, typename Range2> >> > > +void >> > > +test02() >> > > +{ >> > > + int n[] = {1,2,3,4,5,6,7,8,9,10}; >> > > + >> > > + Range1 haystack(n, n+10); >> > > + Range2 needle(n+7, n+10); >> > > + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( !ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end(), >> > > + [](int n, int m) { return std::abs(n >> - m) <= 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end(), >> > > + ranges::equal_to{}, >> > > + [](int n) { return n - 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+6, n+9); >> > > + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end(), >> > > + ranges::equal_to{}, >> > > + std::identity{}, >> > > + [](int n) { return n + 1; }) ); >> > > + >> > > + haystack = Range1(n, n+5); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( !ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > + >> > > + haystack = Range1(n, n+5); >> > > + needle = Range2(n+10, n+10); >> > > + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > +} >> > > + >> > > +int >> > > +main() >> > > +{ >> > > + using namespace __gnu_test; >> > > + using forward = test_forward_range<int>; >> > > + using input_sized = test_input_sized_range<int>; >> > > + using input_sized_sent = test_sized_range_sized_sent<int, >> input_iterator_wrapper>; >> > > + using random_access = test_random_access_range<int>; >> > > + using random_access_sized = >> test_random_access_sized_range<int>; >> > > + using random_access_sized_sent = >> test_sized_range_sized_sent<int, random_access_iterator_wrapper>; >> > > + >> > > + test01<forward, forward>(); >> > > + test01<random_access, random_access>(); >> > > + test02<forward, forward>(); >> > > + test02<random_access, random_access>(); >> > > + >> > > + test01<input_sized, input_sized>(); >> > > + test01<random_access_sized, random_access_sized>(); >> > > + // test02<input_sized, input_sized>(); constraint violation >> > > + test02<random_access_sized, random_access_sized>(); >> > > + >> > > + test01<input_sized_sent, input_sized_sent>(); >> > > + test01<random_access_sized_sent, random_access_sized_sent>(); >> > > + test02<input_sized_sent, input_sized_sent>(); >> > > + test02<random_access_sized_sent, random_access_sized_sent>(); >> > > +} >> > > diff --git >> a/libstdc++-v3/testsuite/25_algorithms/starts_with/1.cc >> b/libstdc++-v3/testsuite/25_algorithms/starts_with/1.cc >> > > new file mode 100644 >> > > index 000000000000..805f31ea2b03 >> > > --- /dev/null >> > > +++ b/libstdc++-v3/testsuite/25_algorithms/starts_with/1.cc >> > > @@ -0,0 +1,128 @@ >> > > +// { dg-do run { target c++23 } } >> > > + >> > > +#include <algorithm> >> > > + >> > > +#include <testsuite_hooks.h> >> > > +#include <testsuite_iterators.h> >> > > + >> > > +namespace ranges = std::ranges; >> > > + >> > > +template<typename Range1, typename Range2> >> > > +void >> > > +test01() >> > > +{ >> > > + int n[] = {1,2,3,4,5,6,7,8,9,10}; >> > > + >> > > + Range1 haystack(n, n+10); >> > > + Range2 needle(n, n+3); >> > > + VERIFY( ranges::starts_with(haystack, needle) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( ranges::starts_with(haystack, needle) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( !ranges::starts_with(haystack, needle) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( ranges::starts_with(haystack, needle, >> > > + [](int n, int m) { return >> std::abs(n - m) <= 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( ranges::starts_with(haystack, needle, >> > > + ranges::equal_to{}, >> > > + [](int n) { return n + 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( ranges::starts_with(haystack, needle, >> > > + ranges::equal_to{}, >> > > + std::identity{}, >> > > + [](int n) { return n - 1; }) ); >> > > + >> > > + haystack = Range1(n, n+5); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( !ranges::starts_with(haystack, needle) ); >> > > + >> > > + haystack = Range1(n, n+5); >> > > + needle = Range2(n+10, n+10); >> > > + VERIFY( ranges::starts_with(haystack, needle) ); >> > > +} >> > > + >> > > +template<typename Range1, typename Range2> >> > > +void >> > > +test02() >> > > +{ >> > > + int n[] = {1,2,3,4,5,6,7,8,9,10}; >> > > + >> > > + Range1 haystack(n, n+10); >> > > + Range2 needle(n, n+3); >> > > + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( !ranges::starts_with(haystack.begin(), >> haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end(), >> > > + [](int n, int m) { return >> std::abs(n - m) <= 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end(), >> > > + ranges::equal_to{}, >> > > + [](int n) { return n + 1; }) ); >> > > + >> > > + haystack = Range1(n); >> > > + needle = Range2(n+1, n+4); >> > > + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), >> > > + needle.begin(), needle.end(), >> > > + ranges::equal_to{}, >> > > + std::identity{}, >> > > + [](int n) { return n - 1; }) ); >> > > + >> > > + haystack = Range1(n, n+5); >> > > + needle = Range2(n, n+10); >> > > + VERIFY( !ranges::starts_with(haystack.begin(), >> haystack.end(), >> > > + needle.begin(), needle.end()) ); >> > > +} >> > > + >> > > +int >> > > +main() >> > > +{ >> > > + using namespace __gnu_test; >> > > + using input = test_input_range<int>; >> > > + using input_sized = test_input_sized_range<int>; >> > > + using input_sized_sent = test_sized_range_sized_sent<int, >> input_iterator_wrapper>; >> > > + using random_access = test_random_access_range<int>; >> > > + using random_access_sized = >> test_random_access_sized_range<int>; >> > > + using random_access_sized_sent = >> test_sized_range_sized_sent<int, random_access_iterator_wrapper>; >> > > + >> > > + test01<input, input>(); >> > > + test01<random_access, random_access>(); >> > > + test02<input, input>(); >> > > + test02<random_access, random_access>(); >> > > + >> > > + test01<input_sized, input_sized>(); >> > > + test01<random_access_sized, random_access_sized>(); >> > > + test02<input_sized, input_sized>(); >> > > + test02<random_access_sized, random_access_sized>(); >> > > + >> > > + test01<input_sized_sent, input_sized_sent>(); >> > > + test01<random_access_sized_sent, random_access_sized_sent>(); >> > > + test02<input_sized_sent, input_sized_sent>(); >> > > + test02<random_access_sized_sent, random_access_sized_sent>(); >> > > +} >> > > -- >> > > 2.49.0.608.gcb96e1697a >> > > >> > > >> > >> > >> > > >