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

Reply via email to