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,
and for that we need to know if n2 will fit in iter_difference_t<Iter1>. If
n2 <=
n1 that is trivially true.


> 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
> >
>
>

Reply via email to