On Tue, May 20, 2025 at 3:07 PM Patrick Palka <ppa...@redhat.com> wrote:

> On Tue, 20 May 2025, Tomasz Kaminski wrote:
>
> >
> >
> > On Mon, May 19, 2025 at 6:06 PM Patrick Palka <ppa...@redhat.com> wrote:
> >       On Mon, 19 May 2025, Patrick Palka wrote:
> >
> >       > Changes in v3:
> >       >   * Use the forward_range code path for a (non-sized)
> bidirectional
> >       >     haystack, since it's slightly fewer increments/decrements
> >       >     overall.
> >       >   * Fix wrong iter_difference_t cast in starts_with.
> >       >
> >       > Changes in v2:
> >       >   Addressed Tomasz's review comments, namely:
> >       >   * Added explicit iter_difference_t casts
> >       >   * Made _S_impl member private
> >       >   * Optimized sized bidirectional case of ends_with
> >       >   * Rearranged control flow of starts_with::_S_impl
> >       >
> >       > Still left to do:
> >       >   * Add tests for integer-class types
> >       >   * Still working on a better commit description ;)
> >       >
> >       > -- >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       | 236
> ++++++++++++++++++
> >       >  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, 516 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..54646ae62f7e 100644
> >       > --- a/libstdc++-v3/include/bits/ranges_algo.h
> >       > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> >       > @@ -438,6 +438,242 @@ 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, __n1,
> >       > +                    std::move(__first2), __last2, __n2,
> >       > +                    std::move(__pred),
> >       > +                    std::move(__proj1), std::move(__proj2));
> >       > +      }
> >       > +
> >       > +    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),
> __n1,
> >       > +                    ranges::begin(__r2), ranges::end(__r2),
> __n2,
> >       > +                    std::move(__pred),
> >       > +                    std::move(__proj1), std::move(__proj2));
> >       > +      }
> >       > +
> >       > +  private:
> >       > +    template<typename _Iter1, typename _Sent1, typename _Iter2,
> typename _Sent2,
> >       > +          typename _Pred,
> >       > +          typename _Proj1, typename _Proj2>
> >       > +      static constexpr bool
> >       > +      _S_impl(_Iter1 __first1, _Sent1 __last1,
> iter_difference_t<_Iter1> __n1,
> >       > +           _Iter2 __first2, _Sent2 __last2,
> iter_difference_t<_Iter2> __n2,
> >       > +           _Pred __pred,
> >       > +           _Proj1 __proj1, _Proj2 __proj2)
> >       > +      {
> >       > +     if (__first2 == __last2) [[unlikely]]
> >       > +       return true;
> >       > +     else if (__n1 == -1 || __n2 == -1)
> >       > +       return ranges::mismatch(std::move(__first1), __last1,
> >       > +                               std::move(__first2), __last2,
> >       > +                               std::move(__pred),
> >       > +                               std::move(__proj1),
> std::move(__proj2)).in2 == __last2;
> >       > +     else if (__n1 < __n2)
> >       > +       return false;
> >       > +     else if constexpr (random_access_iterator<_Iter1>)
> >       > +       return ranges::equal(__first1, __first1 +
> iter_difference_t<_Iter1>(__n2),
> >       > +                            std::move(__first2), __last2,
> >       > +                            std::move(__pred),
> >       > +                            std::move(__proj1),
> std::move(__proj2));
> >       > +     else
> >       > +       return
> ranges::equal(counted_iterator(std::move(__first1),
> >       > +
>  iter_difference_t<_Iter1>(__n2)),
> >       > +                            default_sentinel,
> >       > +                            std::move(__first2), __last2,
> >       > +                            std::move(__pred),
> >       > +                            std::move(__proj1),
> std::move(__proj2));
> >       > +      }
> >       > +  };
> >       > +
> >       > +  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, __n1,
> >       > +                    std::move(__first2), __last2, __n2,
> >       > +                    std::move(__pred),
> >       > +                    std::move(__proj1), std::move(__proj2));
> >       > +      }
> >       > +
> >       > +    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),
> __n1,
> >       > +                    ranges::begin(__r2), ranges::end(__r2),
> __n2,
> >       > +                    std::move(__pred),
> >       > +                    std::move(__proj1), std::move(__proj2));
> >       > +      }
> >       > +
> >       > +  private:
> >       > +    template<typename _Iter1, typename _Sent1,
> >       > +          typename _Iter2, typename _Sent2,
> >       > +          typename _Pred,
> >       > +          typename _Proj1, typename _Proj2>
> >       > +      static constexpr bool
> >       > +      _S_impl(_Iter1 __first1, _Sent1 __last1,
> iter_difference_t<_Iter1> __n1,
> >       > +           _Iter2 __first2, _Sent2 __last2,
> iter_difference_t<_Iter2> __n2,
> >       > +           _Pred __pred,
> >       > +           _Proj1 __proj1, _Proj2 __proj2)
> >       > +      {
> >       > +     if (__first2 == __last2) [[unlikely]]
> >       > +       return true;
> >       > +
> >       > +     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>
> >       > +                       || !same_as<_Iter1, _Sent1>)
> >
> > I begin to wonder what is the benefit of not skipping common
> bidirectional ranges here?
> > Code below for random access ranges will call operator- (which is O(1))
> > and equal. The bidirectional case will decrease the range n2 times.
> > Should we just check random_access_range?
>
> Hmm, but common bidirectional ranges do get skipped in this both-sized
> code path?  The if constexpr condition as written should be true for
> non-common bidirectional ranges, false for common bidirectional ranges.
> Common bidirectional ranges currently should always go through the
> bidirectional_iterator && same_as code path.
>
I think I caused misunderstanding, I do not see reason to exclude
bidirectional
common ranges here, the random_access_path seem to do more efficient,
as it's call equal with same arguments, but does only subtraction which is
O(1)
instead of n2 decrements.

>
> >
> > Did you mean to optimize bidirectional common not sized ranges, by
> handling them without computing distance:
> > i.e. having something like this in beginning (just after first2 ==
> last2).
> > if constexpr(bidirectional_iterator<_Iter1> && same_as<_Iter1, _Sent1>
> >                    && bidirectional_iterator<_Iter2> && same_as<_Iter2,
> _Sent2>)
> >    if (n1 != -1 || n2 != -1)
> >     return  std::mismatch(make_reverse_iterator(__last1),
> make_reverse_iterator(__first1),
> >                             make_reverse_iterator(__last2),
> make_reverse_iterator(__first2)).in2.base() == __first2;
>
> I didn't consider that but that sounds very worthwhile, I'll try
> implementing it.  I think it should be preferred over the both-sized
> code path even.
>
In that case use starts_with::_S_impl on a reversed iterator. This will
optimize the sized cases.

>
> > }
> >       > +           {
> >       > +             ranges::advance(__first1, __n1 -
> iter_difference_t<_Iter1>(__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> &&
> same_as<_Iter1, _Sent1>)
> >       > +       {
> >       > +         _Iter1 __it1 = ranges::next(__first1, __last1);
> >
> >       We can just initialize __it1 to __last1 now that this code path
> requires
> >       common-ness.
> >
> >       > +         if (__n1 != -1)
> >       > +           ranges::advance(__it1,
> -iter_difference_t<_Iter1>(__n2), __first1);
> >
> >       We don't need to use the three-parameter versions of advance here
> since
> >       we know __n1 >= __n2.  Consider these two things fixed
> >
> >       > +         else
> >       > +           {
> >       > +             // We can't use ranges::advance if the haystack
> size is
> >       > +             // unknown, since we need to detect and return
> false if
> >       > +             // it's smaller than the needle.
> >       > +             iter_difference_t<_Iter2> __m = __n2;
> >       > +             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>)
> >       > +       {
> >       > +         _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;
> >       > +                 __first1 = ranges::next(__prev_first1,
> >       > +
>  iter_difference_t<_Iter1>(__n2 - __m));
> >       > +                 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
> >       > +       // If __first1 is non-forward, it must be sized, so it
> was handled
> >       > +       // by the __n1 != -1 case earlier.
> >       > +       __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