On Mon, 19 May 2025 at 10:35, Tomasz Kaminski <tkami...@redhat.com> wrote: > > > > On Mon, May 19, 2025 at 11:29 AM Jonathan Wakely <jwak...@redhat.com> wrote: >> >> On Mon, 19 May 2025 at 05:46, Patrick Palka <ppa...@redhat.com> wrote: >> > >> > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? >> > >> > -- >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 | 232 ++++++++++++++++++ >> > 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, 512 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..c59a555f528a 100644 >> > --- a/libstdc++-v3/include/bits/ranges_algo.h >> > +++ b/libstdc++-v3/include/bits/ranges_algo.h >> > @@ -438,6 +438,238 @@ 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, >> > + std::move(__first2), __last2, >> > + std::move(__pred), >> > + std::move(__proj1), std::move(__proj2), >> > + __n1, __n2); >> > + } >> > + >> > + 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), >> > + ranges::begin(__r2), ranges::end(__r2), >> > + std::move(__pred), >> > + std::move(__proj1), std::move(__proj2), >> > + __n1, __n2); >> > + } >> > + >> > + template<typename _Iter1, typename _Sent1, typename _Iter2, typename >> > _Sent2, >> > + typename _Pred, >> > + typename _Proj1, typename _Proj2> >> > + static constexpr bool >> > + _S_impl(_Iter1 __first1, _Sent1 __last1, >> > + _Iter2 __first2, _Sent2 __last2, >> > + _Pred __pred, >> > + _Proj1 __proj1, _Proj2 __proj2, >> > + iter_difference_t<_Iter1> __n1, >> > + iter_difference_t<_Iter2> __n2) >> > + { >> > + if (__n1 != -1 && __n2 != -1) >> > + { >> > + if (__n1 < __n2) >> > + return false; >> >> For random access iterators we end up doing this comparison twice, >> because ranges::equal will also do if ((last1 - first1) < (last2 - >> first2). > > >> >> So in theory, we could check if both ranges are random access >> as the first thing we do, and then just call ranges::equal and let >> that check the sizes. > > We need to trim the first range to the size of the second range before > calling equal,
Ah yes, of course. > and for that we need to know if n2 will fit in iter_difference_t<Iter1>. If > n2 <= > n1 that is trivially true. So my non-comment wasn't even correct ;-) >> >> That would need to be the very first thing we do >> in both oeprator() overloads, and we'd still need all the same logic >> in _S_impl to handle the case where only one of the ranges is random >> access. So I don't think it is worth doing - with optimization enabled >> I hope that the redundant distance calculations and n1 < n2 >> comparisons will be subject to common subexpression elimination. >> >> Apart from that non-comment, it looks like Tomasz has done a much more >> thorough review than I have. >> >> >> > + if constexpr (random_access_iterator<_Iter1>) >> > + return ranges::equal(__first1, __first1 + __n2, >> > + std::move(__first2), __last2, >> > + std::move(__pred), >> > + std::move(__proj1), std::move(__proj2)); >> > + else >> > + return ranges::equal(counted_iterator(std::move(__first1), >> > __n2), >> > + default_sentinel, >> > + std::move(__first2), __last2, >> > + std::move(__pred), >> > + std::move(__proj1), std::move(__proj2)); >> > + } >> > + else >> > + return ranges::mismatch(std::move(__first1), __last1, >> > + std::move(__first2), __last2, >> > + std::move(__pred), >> > + std::move(__proj1), >> > std::move(__proj2)).in2 == __last2; >> > + } >> > + }; >> > + >> > + 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, >> > + std::move(__first2), __last2, >> > + std::move(__pred), >> > + std::move(__proj1), std::move(__proj2), >> > + __n1, __n2); >> > + } >> > + >> > + 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), >> > + ranges::begin(__r2), ranges::end(__r2), >> > + std::move(__pred), >> > + std::move(__proj1), std::move(__proj2), >> > + __n1, __n2); >> > + } >> > + >> > + template<typename _Iter1, typename _Sent1, >> > + typename _Iter2, typename _Sent2, >> > + typename _Pred, >> > + typename _Proj1, typename _Proj2> >> > + static constexpr bool >> > + _S_impl(_Iter1 __first1, _Sent1 __last1, >> > + _Iter2 __first2, _Sent2 __last2, >> > + _Pred __pred, >> > + _Proj1 __proj1, _Proj2 __proj2, >> > + iter_difference_t<_Iter1> __n1, >> > + iter_difference_t<_Iter2> __n2) >> > + { >> > + 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>) >> > + { >> > + ranges::advance(__first1, __n1 - __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>) >> > + { >> > + iter_difference_t<_Iter2> __m = __n2; >> > + _Iter1 __it1 = ranges::next(__first1, __last1); >> > + 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>) >> > + { >> > + if (__first2 == __last2) >> > + return true; >> > + >> > + _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; >> > + ranges::advance(__prev_first1, __n2 - __m); >> > + __first1 = __prev_first1; >> > + 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 >> > + // Should have been handled by the __n1 != -1 case. >> > + __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 >> > >>