On Mon, 19 May 2025, Tomasz Kaminski wrote: > > > On Mon, May 19, 2025 at 6:47 AM Patrick Palka <ppa...@redhat.com> wrote: > I would appreciate a short explanation on the approach being put here, > in the message. Like passing -1 as means of saying, size not know. > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > From the non-stylistic changes, I have noticed that we need some explicit > conversion between different types. > I think we should add test that would check with size being __max_diff_t, > maybe we should add such a range to > testutils_iterators. > Rest of the of comments are mostly stylistic. > > > -- >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, > > I think I would make this function private. The user should not look into > wrappers, > but it does need to be public. > I have slight preference for ordering arguments in following manner: > first1, last1, n1, > first2, last2, n2,
Done. > + _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) > > Very subjective, but I would other ifs, to not nest: > if (__first2 == __last) > return true; > else if (n1 == -1 || n2 == -2) > mismatch; > else if (n1 < n2) > return false; > else if constexpr (random_access_iterator<_Iter1>) > equal(first, first + n2, ....) > else > equal with counted. Nice, I like that too. > > + { > + if (__n1 < __n2) > + return false; > + if constexpr (random_access_iterator<_Iter1>) > + return ranges::equal(__first1, __first1 + __n2, > > I think you need to cast __n2 to iter_difference_t<Iter1> here explicitly, > it must fit because it is smaller or equal to n1, but still there may not be > a candidate. Done, thanks for auditing this integer-class type stuff. > + std::move(__first2), __last2, > + std::move(__pred), > + std::move(__proj1), > std::move(__proj2)); > + else > + return > ranges::equal(counted_iterator(std::move(__first1), __n2), > > Similar here, cast to iter_difference_t<Iter1>. Done. > + 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, > > Similar comments about the private, and other of arguments as for above., Done. > + _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); > > I think you need to cast n2 to iter_difference_t<_Iter1> here explicitly. Done. > + 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); > > Could you add a comment, that ranges::advance(__it, -__n2, __first) cannot be > used here, > because __n2 may not fit into iter_difference<Iter1>? Good point, I think that's true in general, but if the size of the haystack is known (i.e. __n1 != -1) then we already checked that __n1 < __n2 earlier, so I think we can use ranges::advance (with a cast) in that case. If __n1 == -1 then we can't use ranges::advance also because we need to detect and return false if the first (haystack) range is smaller than the second (needle) range, which IIUC we can't do with advance. This made me realize that for a sized non-common bidirectional haystack, we should instead use the sized haystack code path, since it's fewer operations overall due to ranges::next(__first, __last1) in the bidirectional code path having to traverse the entire range. So I changed if constexpr (random_access_iterator<_Iter1> || !bidirectional_iterator<_Iter1>) to if constexpr (random_access_iterator<_Iter1> || !(bidirectional_iterator<_Iter1> && same_as<_Iter1, _Sent1>)) > + 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; > > Could you make this if first in the _S_impl, and similarly for starts_with, > I think it will make it a lot easier to reason about it. Done. > + > + _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); > > Again we need to cast __n2 - __m to iter_difference_t<Iter1>. Done. I also combined this line and the next into one using ranges::next: __first1 = ranges::next(__prev_first1, iter_difference_t<_Iter1>(__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. > > Could you expand a comment here, adding something like: > If _first_1 is input_iterot, it must be sized, so this was handled by _n != 1 > case Done. > + __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()) ); > +} > > Could you add the usual test with a second range using __max_diff_t as > difference type. What would be the best way to test the integer-class type situation for all the various code paths (non-forward, non-bidirectional, non-random-access etc)? > + > +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 > > >