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<_Range2> __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));
+ }
+
+ friend struct __ends_with_fn;
+ };
+
+ 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<_Range2> __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 constexpr (!random_access_iterator<_Iter1>
+ && bidirectional_iterator<_Iter1> && same_as<_Iter1, _Sent1>
+ && bidirectional_iterator<_Iter2> && same_as<_Iter2, _Sent2>)
+ return starts_with._S_impl(std::make_reverse_iterator(__last1),
+ std::make_reverse_iterator(__first1),
+ __n1,
+ std::make_reverse_iterator(__last2),
+ std::make_reverse_iterator(__first2),
+ __n2,
+ std::move(__pred),
+ std::move(__proj1), std::move(__proj2));
+
+ if (__first2 == __last2) [[unlikely]]
+ return true;
+
+ if constexpr (forward_iterator<_Iter2>)
+ if (__n2 == -1)
+ __n2 = ranges::distance(__first2, __last2);
+
+ // __glibcxx_assert(__n2 != -1);
+
+ if (__n1 != -1)
+ {
+ if (__n1 < __n2)
+ return false;
+ auto __shift = __n1 - iter_difference_t<_Iter1>(__n2);
+ if (random_access_iterator<_Iter1>
+ || !bidirectional_iterator<_Iter1>
+ || !same_as<_Iter1, _Sent1>
+ || __shift < __n2)
+ {
+ ranges::advance(__first1, __shift);
+ 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 = __last1;
+ if (__n1 != -1)
+ ranges::advance(__it1, -iter_difference_t<_Iter1>(__n2));
+ 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>)
+ {
+ // __glibcxx_assert(__n1 == -1);
+ _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 the haystack is non-forward then it must be sized, in which case
+ // we already returned via 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,
};
};
+ftms = {
+ name = ranges_starts_ends_with;
+ values = {
+ v = 202106;
+ cxxmin = 23;
+ };
+};
+
ftms = {
name = constexpr_bitset;
values = {
#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
#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
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
}
}
--- /dev/null
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <ranges>
+
+#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()) );
+}
+
+void
+test03()
+{
+ auto haystack = std::views::iota(0, 10);
+ auto needle = std::views::iota(5, 10);
+
+#if __SIZEOF_INT128__
+ auto haystack_ict = std::views::iota(__int128(0), __int128(10));
+ auto needle_ict = std::views::iota(__int128(5), __int128(10));
+#else
+ auto haystack_ict = std::views::iota(0ll, 10ll);
+ auto needle_ict = std::views::iota(5ll, 10ll);
+#endif
+
+ VERIFY( ranges::ends_with(haystack, needle_ict) );
+ VERIFY( ranges::ends_with(haystack.begin(), haystack.end(),
+ needle_ict.begin(), needle_ict.end()) );
+
+ VERIFY( ranges::ends_with(haystack_ict, needle) );
+ VERIFY( ranges::ends_with(haystack_ict.begin(), haystack_ict.end(),
+ needle.begin(), needle.end()) );
+
+ VERIFY( ranges::ends_with(haystack_ict, needle_ict) );
+ VERIFY( ranges::ends_with(haystack_ict.begin(), haystack_ict.end(),
+ needle_ict.begin(), needle_ict.end()) );
+}
+
+int
+main()
+{
+ using namespace __gnu_test;
+ using forward = test_forward_range<int>;
+ using bidirectional_common = bidirectional_container<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<bidirectional_common, bidirectional_common>();
+ test02<bidirectional_common, bidirectional_common>();
+ test01<bidirectional_common, forward>();
+ test02<bidirectional_common, forward>();
+
+ 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>();
+
+ test03();
+}
--- /dev/null
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <ranges>
+
+#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()) );
+}
+
+void
+test03()
+{
+ auto haystack = std::views::iota(0, 10);
+ auto needle = std::views::iota(0, 5);
+
+#if __SIZEOF_INT128__
+ auto haystack_ict = std::views::iota(__int128(0), __int128(10));
+ auto needle_ict = std::views::iota(__int128(0), __int128(5));
+#else
+ auto haystack_ict = std::views::iota(0ll, 10ll);
+ auto needle_ict = std::views::iota(0ll, 5ll);
+#endif
+
+ VERIFY( ranges::starts_with(haystack, needle_ict) );
+ VERIFY( ranges::starts_with(haystack.begin(), haystack.end(),
+ needle_ict.begin(), needle_ict.end()) );
+
+ VERIFY( ranges::starts_with(haystack_ict, needle) );
+ VERIFY( ranges::starts_with(haystack_ict.begin(), haystack_ict.end(),
+ needle.begin(), needle.end()) );
+
+ VERIFY( ranges::starts_with(haystack_ict, needle_ict) );
+ VERIFY( ranges::starts_with(haystack_ict.begin(), haystack_ict.end(),
+ needle_ict.begin(), needle_ict.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>();
+
+ test03();
+}