On Mon, May 19, 2025 at 4:02 PM Patrick Palka <ppa...@redhat.com> wrote:
> 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. > There is ranges::advance with sentinel (f, n, l) that I would use here. > > 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>)) > Indeed, I was thinking about non-common bidirectional ranges, and if they should use forward_range code path, but for sure here it makes sense. > > > + 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 > > > > > >