public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4
@ 2020-02-04  2:07 Patrick Palka
  2020-02-04  2:08 ` [PATCH 3/3] libstdc++: Implement C++20 range adaptors Patrick Palka
                   ` (2 more replies)
  0 siblings, 3 replies; 27+ messages in thread
From: Patrick Palka @ 2020-02-04  2:07 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jwakely, Patrick Palka

These changes are needed for some of the tests in the constrained algorithm
patch, because they use move_iterator with an uncopyable output_iterator.  The
other changes described in the paper are already applied, it seems.

libstdc++-v3/ChangeLog:

	* include/bits/stl_iterator.h (move_iterator::move_iterator): Move the
	iterator when initializing _M_current.
	(move_iterator::base): Split into two overloads differing in
	ref-qualifiers as in P1207R4.
---
 libstdc++-v3/include/bits/stl_iterator.h | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 784d200d22f..1a288a5c785 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -1166,7 +1166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       explicit _GLIBCXX17_CONSTEXPR
       move_iterator(iterator_type __i)
-      : _M_current(__i) { }
+      : _M_current(std::move(__i)) { }
 
       template<typename _Iter>
 	_GLIBCXX17_CONSTEXPR
@@ -1174,9 +1174,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_current(__i.base()) { }
 
       _GLIBCXX17_CONSTEXPR iterator_type
-      base() const
+      base() const &
+#if __cplusplus > 201703L && __cpp_lib_concepts
+	requires copy_constructible<iterator_type>
+#endif
       { return _M_current; }
 
+      _GLIBCXX17_CONSTEXPR iterator_type
+      base() &&
+      { return std::move(_M_current); }
+
       _GLIBCXX17_CONSTEXPR reference
       operator*() const
       { return static_cast<reference>(*_M_current); }
-- 
2.25.0.114.g5b0ca878e0

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-04  2:07 [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Patrick Palka
@ 2020-02-04  2:08 ` Patrick Palka
  2020-02-06 17:25   ` Jonathan Wakely
  2020-02-17 22:07   ` Stephan Bergmann
  2020-02-04  2:09 ` [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms Patrick Palka
  2020-02-04 10:41 ` [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Jonathan Wakely
  2 siblings, 2 replies; 27+ messages in thread
From: Patrick Palka @ 2020-02-04  2:08 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jwakely, Patrick Palka

This patch implements [range.adaptors].  It also includes the changes from P3280
and P3278 and P3323, without which many standard examples won't work.

The implementation is mostly dictated by the spec and there was not much room
for implementation discretion.  The most interesting part that was not specified
by the spec is the design of the range adaptors and range adaptor closures,
which I tried to design in a way that minimizes boilerplate and statefulness (so
that e.g. the composition of two stateless closures is stateless).

What is left unimplemented is caching of calls to begin() in filter_view,
drop_view and reverse_view, which is required to guarantee that begin() has
amortized constant time complexity.  I can implement this in a subsequent patch.

"Interesting" parts of the patch are marked with XXX comments.

libstdc++-v3/ChangeLog:

	Implement C++20 range adaptors
	* include/std/ranges: Include <functional>.
	(subrange::_S_store_size): Mark as const instead of constexpr to
	avoid what seems to be a bug in GCC.
	(__detail::__box): Give it defaulted copy and move constructors.
	(views::_Single::operator()): Mark constexpr.
	(views::_Iota::operator()): Mark constexpr.
	(__detail::Empty): Define.
	(views::_RangeAdaptor, views::_RangeAdaptorClosure, ref_view, all_view,
	views::all, filter_view, views::filter, transform_view,
	views::transform, take_view, views::take, take_while_view,
	views::take_while, drop_view, views::drop, join_view, views::join,
	__detail::require_constant, __detail::tiny_range, split_view,
	views::split, views::_Counted, views::counted, common_view,
	views::common, reverse_view, views::reverse,
	views::__detail::__is_reversible_subrange,
	views::__detail::__is_reverse_view, reverse_view, views::reverse,
	__detail::__has_tuple_element, elements_view, views::elements,
	views::keys, views::values): Define.
	* testsuite/std/ranges/adaptors/all.cc: New test.
	* testsuite/std/ranges/adaptors/common.cc: Likewise.
	* testsuite/std/ranges/adaptors/counted.cc: Likewise.
	* testsuite/std/ranges/adaptors/drop.cc: Likewise.
	* testsuite/std/ranges/adaptors/drop_while.cc: Likewise.
	* testsuite/std/ranges/adaptors/elements.cc: Likewise.
	* testsuite/std/ranges/adaptors/filter.cc: Likewise.
	* testsuite/std/ranges/adaptors/join.cc: Likewise.
	* testsuite/std/ranges/adaptors/reverse.cc: Likewise.
	* testsuite/std/ranges/adaptors/split.cc: Likewise.
	* testsuite/std/ranges/adaptors/take.cc: Likewise.
	* testsuite/std/ranges/adaptors/take_while.cc: Likewise.
	* testsuite/std/ranges/adaptors/transform.cc: Likewise.
---
 libstdc++-v3/include/std/ranges               | 2415 ++++++++++++++++-
 .../testsuite/std/ranges/adaptors/all.cc      |  124 +
 .../testsuite/std/ranges/adaptors/common.cc   |   68 +
 .../testsuite/std/ranges/adaptors/counted.cc  |   64 +
 .../testsuite/std/ranges/adaptors/drop.cc     |  107 +
 .../std/ranges/adaptors/drop_while.cc         |   63 +
 .../testsuite/std/ranges/adaptors/elements.cc |   52 +
 .../testsuite/std/ranges/adaptors/filter.cc   |   97 +
 .../testsuite/std/ranges/adaptors/join.cc     |  112 +
 .../testsuite/std/ranges/adaptors/reverse.cc  |   86 +
 .../testsuite/std/ranges/adaptors/split.cc    |   82 +
 .../testsuite/std/ranges/adaptors/take.cc     |   95 +
 .../std/ranges/adaptors/take_while.cc         |   62 +
 .../std/ranges/adaptors/transform.cc          |   86 +
 14 files changed, 3509 insertions(+), 4 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index ea558c76c9d..947e223f7f9 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -39,6 +39,7 @@
 #if __cpp_lib_concepts
 
 #include <compare>
+#include <functional> // std::ref
 #include <initializer_list>
 #include <iterator>
 #include <limits>
@@ -255,7 +256,8 @@ namespace ranges
     class subrange : public view_interface<subrange<_It, _Sent, _Kind>>
     {
     private:
-      static constexpr bool _S_store_size
+      // XXX: gcc complains when using constexpr here
+      static const bool _S_store_size
 	= _Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _It>;
 
       _It _M_begin = _It();
@@ -507,6 +509,9 @@ namespace ranges
 	: std::optional<_Tp>{std::in_place}
 	{ }
 
+	__box(const __box&) = default;
+	__box(__box&&) = default;
+
 	using std::optional<_Tp>::operator=;
 
 	__box&
@@ -922,7 +927,7 @@ namespace views
   struct _Single
   {
     template<typename _Tp>
-      auto
+      constexpr auto
       operator()(_Tp&& __e) const
       { return single_view{std::forward<_Tp>(__e)}; }
   };
@@ -932,19 +937,2421 @@ namespace views
   struct _Iota
   {
     template<typename _Tp>
-      auto
+      constexpr auto
       operator()(_Tp&& __e) const
       { return iota_view{std::forward<_Tp>(__e)}; }
 
     template<typename _Tp, typename _Up>
-      auto
+      constexpr auto
       operator()(_Tp&& __e, _Up&& __f) const
       { return iota_view{std::forward<_Tp>(__e), std::forward<_Tp>(__f)}; }
   };
 
   inline constexpr _Iota iota{};
+} // namespace views
+
+namespace __detail
+{
+  struct _Empty { };
+} // namespace __detail
+
+namespace views
+{
+  template<typename _Callable>
+    struct _RangeAdaptorClosure;
+
+  template<typename _Callable>
+    struct _RangeAdaptor
+    {
+    protected:
+      [[no_unique_address]]
+	conditional_t<!is_default_constructible_v<_Callable>,
+		      _Callable, __detail::_Empty> _M_callable;
+
+    public:
+      constexpr
+      _RangeAdaptor(const _Callable& = {})
+	requires is_default_constructible_v<_Callable>
+      { }
+
+      constexpr
+      _RangeAdaptor(_Callable __callable)
+	requires (!is_default_constructible_v<_Callable>)
+	: _M_callable(std::move(__callable))
+      { }
+
+      template<typename... _Args>
+	requires (sizeof...(_Args) >= 1)
+	constexpr auto
+	operator()(_Args&&... __args) const
+	{
+	  if constexpr (is_invocable_v<_Callable, _Args...>)
+	    {
+	      static_assert(sizeof...(_Args) != 1,
+			    "a _RangeAdaptor that accepts only one argument "
+			    "should be defined as a _RangeAdaptorClosure");
+	      return _Callable{}(std::forward<_Args>(__args)...);
+	    }
+	  else
+	    {
+	      auto __closure = [__args...] <typename _Range> (_Range&& __r) {
+		return _Callable{}(std::forward<_Range>(__r), __args...);
+	      };
+	      using _ClosureType = decltype(__closure);
+	      return _RangeAdaptorClosure<_ClosureType>(std::move(__closure));
+	    }
+	}
+    };
+
+  template<typename _Callable>
+    struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
+    {
+      using _RangeAdaptor<_Callable>::_RangeAdaptor;
+
+      template<viewable_range _Range>
+	requires requires { declval<_Callable>()(declval<_Range>()); }
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  if constexpr (is_default_constructible_v<_Callable>)
+	    return _Callable{}(std::forward<_Range>(__r));
+	  else
+	    return this->_M_callable(std::forward<_Range>(__r));
+	}
+
+      template<viewable_range _Range>
+	requires requires { declval<_Callable>()(declval<_Range>()); }
+	friend constexpr auto
+	operator|(_Range&& __r, const _RangeAdaptorClosure& __o)
+	{ return __o(std::forward<_Range>(__r)); }
+
+      template<typename _Tp>
+	friend constexpr auto
+	operator|(const _RangeAdaptorClosure<_Tp>& __x,
+		  const _RangeAdaptorClosure& __y)
+	{
+	  if constexpr (is_default_constructible_v<_Tp>
+			&& is_default_constructible_v<_Callable>)
+	    {
+	      auto __closure = [] <typename _Up> (_Up&& __e) {
+		return std::forward<_Up>(__e) | decltype(__x){} | decltype(__y){};
+	      };
+	      return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	    }
+	  else if constexpr (is_default_constructible_v<_Tp>
+			     && !is_default_constructible_v<_Callable>)
+	    {
+	      auto __closure = [__y] <typename _Up> (_Up&& __e) {
+		return std::forward<_Up>(__e) | decltype(__x){} | __y;
+	      };
+	      return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	    }
+	  else if constexpr (!is_default_constructible_v<_Tp>
+			     && is_default_constructible_v<_Callable>)
+	    {
+	      auto __closure = [__x] <typename _Up> (_Up&& __e) {
+		return std::forward<_Up>(__e) | __x | decltype(__y){};
+	      };
+	      return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	    }
+	  else
+	    {
+	      auto __closure = [__x, __y] <typename _Up> (_Up&& __e) {
+		return std::forward<_Up>(__e) | __x | __y;
+	      };
+	      return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	    }
+	}
+    };
 
+  template<typename _Callable>
+    _RangeAdaptorClosure(_Callable) -> _RangeAdaptorClosure<_Callable>;
 } // namespace views
+
+  template<range _Range> requires is_object_v<_Range>
+    class ref_view : public view_interface<ref_view<_Range>>
+    {
+    private:
+      _Range* _M_r = nullptr;
+
+      static void _S_fun(_Range&); // not defined
+      static void _S_fun(_Range&&) = delete;
+
+    public:
+      constexpr
+      ref_view() noexcept = default;
+
+      template<__detail::__not_same_as<ref_view> _Tp>
+	requires convertible_to<_Tp, _Range&>
+	  && requires { _S_fun(declval<_Tp>()); }
+	constexpr
+	ref_view(_Tp&& __t)
+	  : _M_r(addressof(static_cast<_Range&>(std::forward<_Tp>(__t))))
+	{ }
+
+      constexpr _Range&
+      base() const
+      { return *_M_r; }
+
+      constexpr iterator_t<_Range>
+      begin() const
+      { return ranges::begin(*_M_r); }
+
+      constexpr sentinel_t<_Range>
+      end() const
+      { return ranges::end(*_M_r); }
+
+      constexpr bool
+      empty() const requires requires { ranges::empty(*_M_r); }
+      { return ranges::empty(*_M_r); }
+
+      constexpr auto
+      size() const requires sized_range<_Range>
+      { return ranges::size(*_M_r); }
+
+      constexpr auto
+      data() const requires contiguous_range<_Range>
+      { return ranges::data(*_M_r); }
+    };
+
+  template<typename _Range>
+    ref_view(_Range&) -> ref_view<_Range>;
+
+  template<typename _Tp>
+    inline constexpr bool enable_safe_range<ref_view<_Tp>> = true;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptorClosure all
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	if constexpr (view<decay_t<_Range>>)
+	  return std::forward<_Range>(__r);
+	else if constexpr (requires { ref_view{std::forward<_Range>(__r)}; })
+	  return ref_view{std::forward<_Range>(__r)};
+	else
+	  return subrange{std::forward<_Range>(__r)};
+      };
+  } // namespace views
+
+  template<viewable_range _Range>
+    using all_view = decltype(views::all(declval<_Range>()));
+
+  // XXX: the following algos are copied verbatim from ranges_algo.h to avoid a
+  // circular dependency with that header.
+  namespace __detail
+  {
+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	     typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr _Iter
+      find_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+      {
+	while (__first != __last
+	    && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  ++__first;
+	return __first;
+      }
+
+    template<input_range _Range, typename _Proj = identity,
+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+	       _Pred>
+      constexpr safe_iterator_t<_Range>
+      find_if(_Range&& __r, _Pred __pred, _Proj __proj = {})
+      {
+	return __detail::find_if(ranges::begin(__r), ranges::end(__r),
+				 std::move(__pred), std::move(__proj));
+      }
+
+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	     typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr _Iter
+      find_if_not(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+      {
+	while (__first != __last
+	    && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  ++__first;
+	return __first;
+      }
+
+    template<input_range _Range, typename _Proj = identity,
+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+	       _Pred>
+      constexpr safe_iterator_t<_Range>
+      find_if_not(_Range&& __r, _Pred __pred, _Proj __proj = {})
+      {
+	return __detail::find_if_not(ranges::begin(__r), ranges::end(__r),
+				     std::move(__pred), std::move(__proj));
+      }
+
+    template<typename _Tp, typename _Proj = identity,
+	     indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	       _Comp = ranges::less>
+      constexpr const _Tp&
+      min(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {})
+      {
+	if (std::__invoke(std::move(__comp),
+			  std::__invoke(__proj, __b),
+			  std::__invoke(__proj, __a)))
+	  return __b;
+	else
+	  return __a;
+      }
+
+    template<typename _Iter1, typename _Iter2>
+      struct mismatch_result
+      {
+	[[no_unique_address]] _Iter1 in1;
+	[[no_unique_address]] _Iter2 in2;
+
+	template<typename _IIter1, typename _IIter2>
+	  requires convertible_to<const _Iter1&, _IIter1>
+	    && convertible_to<const _Iter2&, _IIter2>
+	  operator mismatch_result<_IIter1, _IIter2>() const &
+	  {
+	    return {in1, in2};
+	  }
+
+	template<typename _IIter1, typename _IIter2>
+	  requires convertible_to<_Iter1, _IIter1>
+	    && convertible_to<_Iter2, _IIter2>
+	  operator mismatch_result<_IIter1, _IIter2>() &&
+	  {
+	    return {std::move(in1), std::move(in2)};
+	  }
+      };
+
+    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 mismatch_result<_Iter1, _Iter2>
+      mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	       _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+      {
+	while (__first1 != __last1 && __first2 != __last2
+	       && (bool)std::__invoke(__pred,
+				      std::__invoke(__proj1, *__first1),
+				      std::__invoke(__proj2, *__first2)))
+	{
+	  ++__first1;
+	  ++__first2;
+	}
+	return { std::move(__first1), std::move(__first2) };
+      }
+  } // namespace __detail
+
+  template<input_range _Vp,
+	   indirect_unary_predicate<iterator_t<_Vp>> _Pred>
+    requires view<_Vp> && is_object_v<_Pred>
+    class filter_view : public view_interface<filter_view<_Vp, _Pred>>
+    {
+    private:
+      struct _Sentinel;
+
+      struct _Iterator
+      {
+      private:
+	static constexpr auto
+	_S_iter_concept()
+	{
+	  if constexpr (bidirectional_range<_Vp>)
+	    return bidirectional_iterator_tag{};
+	  else if constexpr (forward_range<_Vp>)
+	    return forward_iterator_tag{};
+	  else
+	    return input_iterator_tag{};
+	}
+
+	static constexpr auto
+	_S_iter_cat()
+	{
+	  using _Cat = iterator_traits<iterator_t<_Vp>>::iterator_category;
+	  if constexpr (derived_from<_Cat, bidirectional_iterator_tag>)
+	    return bidirectional_iterator_tag{};
+	  else if constexpr (derived_from<_Cat, forward_iterator_tag>)
+	    return forward_iterator_tag{};
+	  else
+	    return _Cat{};
+	}
+
+	friend filter_view;
+
+	iterator_t<_Vp> _M_current = iterator_t<_Vp>();
+	filter_view* _M_parent = nullptr;
+
+      public:
+	using iterator_concept = decltype(_S_iter_concept());
+	using iterator_category = decltype(_S_iter_cat());
+	using value_type = range_value_t<_Vp>;
+	using difference_type = range_difference_t<_Vp>;
+
+	_Iterator() = default;
+
+	constexpr
+	_Iterator(filter_view& __parent, iterator_t<_Vp> __current)
+	  : _M_current(std::move(__current)),
+	    _M_parent(addressof(__parent))
+	{ }
+
+	constexpr iterator_t<_Vp>
+	base() const &
+	  requires copyable<iterator_t<_Vp>>
+	{ return _M_current; }
+
+	constexpr iterator_t<_Vp>
+	base() &&
+	{ return std::move(_M_current); }
+
+	constexpr range_reference_t<_Vp>
+	operator*() const
+	{ return *_M_current; }
+
+	constexpr iterator_t<_Vp>
+	operator->() const
+	  requires __detail::__has_arrow<iterator_t<_Vp>>
+	    && copyable<iterator_t<_Vp>>
+	{ return _M_current; }
+
+	constexpr _Iterator&
+	operator++()
+	{
+	  _M_current = __detail::find_if(std::move(++_M_current),
+					 ranges::end(_M_parent->_M_base),
+					 std::ref(*_M_parent->_M_pred));
+	  return *this;
+	}
+
+	constexpr void
+	operator++(int)
+	{ ++*this; }
+
+	constexpr _Iterator
+	operator++(int) requires forward_range<_Vp>
+	{
+	  auto __tmp = *this;
+	  ++*this;
+	  return __tmp;
+	}
+
+	constexpr _Iterator&
+	operator--() requires bidirectional_range<_Vp>
+	{
+	  do
+	    --_M_current;
+	  while (!invoke(*_M_parent->_M_pred, *_M_current));
+	  return *this;
+	}
+
+	constexpr _Iterator
+	operator--(int) requires bidirectional_range<_Vp>
+	{
+	  auto __tmp = *this;
+	  --*this;
+	  return __tmp;
+	}
+
+	friend constexpr bool
+	operator==(const _Iterator& __x, const _Iterator& __y)
+	  requires equality_comparable<iterator_t<_Vp>>
+	{ return __x._M_current == __y._M_current; }
+
+	friend constexpr range_rvalue_reference_t<_Vp>
+	iter_move(const _Iterator& __i)
+	  noexcept(noexcept(ranges::iter_move(__i._M_current)))
+	{ return ranges::iter_move(__i._M_current); }
+
+	friend constexpr void
+	iter_swap(const _Iterator& __x, const _Iterator& __y)
+	  noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current)))
+	  requires indirectly_swappable<iterator_t<_Vp>>
+	{ ranges::iter_swap(__x._M_current, __y._M_current); }
+      };
+
+      struct _Sentinel
+      {
+      private:
+	sentinel_t<_Vp> _M_end = sentinel_t<_Vp>();
+
+	constexpr bool
+	__equal(const _Iterator& __i) const
+	{ return __i._M_current == _M_end; }
+
+      public:
+	_Sentinel() = default;
+
+	constexpr explicit
+	_Sentinel(filter_view& __parent)
+	  : _M_end(ranges::end(__parent._M_base))
+	{ }
+
+	constexpr sentinel_t<_Vp>
+	base() const
+	{ return _M_end; }
+
+	friend constexpr bool
+	operator==(const _Iterator& __x, const _Sentinel& __y)
+	{ return __y.__equal(__x); }
+      };
+
+      _Vp _M_base = _Vp();
+      __detail::__box<_Pred> _M_pred;
+
+    public:
+      filter_view() = default;
+
+      constexpr
+      filter_view(_Vp __base, _Pred __pred)
+	: _M_base(std::move(__base)), _M_pred(std::move(__pred))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range>
+	requires viewable_range<_Range>
+	  && constructible_from<_Vp, all_view<_Range>>
+	constexpr
+	filter_view(_Range&& __r, _Pred __pred)
+	  : _M_base(views::all(std::forward<_Range>(__r))),
+	    _M_pred(std::move(__pred))
+	{ }
+      */
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr _Iterator
+      begin()
+      {
+	// XXX: we need to cache the result here as per [range.filter.view]
+	__glibcxx_assert(_M_pred.has_value());
+	return {*this, __detail::find_if(_M_base, std::ref(*_M_pred))};
+      }
+
+      constexpr auto
+      end()
+      {
+	if constexpr (common_range<_Vp>)
+	  return _Iterator{*this, ranges::end(_M_base)};
+	else
+	  return _Sentinel{*this};
+      }
+    };
+
+  template<typename _Range, typename _Pred>
+    filter_view(_Range&&, _Pred) -> filter_view<all_view<_Range>, _Pred>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptor filter
+      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
+      {
+	return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+      };
+  } // namespace views
+
+  template<input_range _Vp, copy_constructible _Fp>
+    requires view<_Vp> && is_object_v<_Fp>
+      && regular_invocable<_Fp&, range_reference_t<_Vp>>
+    class transform_view : public view_interface<transform_view<_Vp, _Fp>>
+    {
+    private:
+      template<bool _Const>
+	struct _Sentinel;
+
+      template<bool _Const>
+	struct _Iterator
+	{
+	private:
+	  using _Parent
+	    = conditional_t<_Const, const transform_view, transform_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  static constexpr auto
+	  _S_iter_concept()
+	  {
+	    if constexpr (random_access_range<_Vp>)
+	      return random_access_iterator_tag{};
+	    else if constexpr (bidirectional_range<_Vp>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (forward_range<_Vp>)
+	      return forward_iterator_tag{};
+	    else
+	      return input_iterator_tag{};
+	  }
+
+	  static constexpr auto
+	  _S_iter_cat()
+	  {
+	    using _Cat = iterator_traits<iterator_t<_Vp>>::iterator_category;
+	    if constexpr (derived_from<_Cat, contiguous_iterator_tag>)
+	      return random_access_iterator_tag{};
+	    else
+	      return _Cat{};
+	  }
+
+	  static constexpr decltype(auto)
+	  __iter_move(const _Iterator& __i = {})
+	    noexcept(noexcept(invoke(*__i._M_parent->_M_fun, *__i._M_current)))
+	  {
+	    if constexpr (is_lvalue_reference_v<decltype(*__i)>)
+	      return std::move(*__i);
+	    else
+	      return *__i;
+	  }
+
+	  iterator_t<_Base> _M_current = iterator_t<_Base>();
+	  _Parent* _M_parent = nullptr;
+
+	public:
+	  using iterator_concept = decltype(_S_iter_concept());
+	  using iterator_category = decltype(_S_iter_cat());
+	  using value_type
+	    = remove_cvref_t<invoke_result_t<_Fp&, range_reference_t<_Base>>>;
+	  using difference_type = range_difference_t<_Base>;
+
+	  _Iterator() = default;
+
+	  constexpr
+	  _Iterator(_Parent& __parent, iterator_t<_Base> __current)
+	    : _M_current(std::move(__current)), _M_parent(addressof(__parent))
+	  { }
+
+	  constexpr
+	  _Iterator(_Iterator<!_Const> __i)
+	    requires _Const
+	      && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+	    : _M_current(std::move(__i._M_current)), _M_parent(__i._M_parent)
+	  { }
+
+	  constexpr iterator_t<_Base>
+	  base() const &
+	    requires copyable<iterator_t<_Base>>
+	  { return _M_current; }
+
+	  constexpr iterator_t<_Base>
+	  base() &&
+	  { return std::move(_M_current); }
+
+	  constexpr decltype(auto)
+	  operator*() const
+	  { return invoke(*_M_parent->_M_fun, *_M_current); }
+
+	  constexpr _Iterator&
+	  operator++()
+	  {
+	    ++_M_current;
+	    return *this;
+	  }
+
+	  constexpr void
+	  operator++(int)
+	  { ++_M_current; }
+
+	  constexpr _Iterator
+	  operator++(int) requires forward_range<_Base>
+	  {
+	    auto __tmp = *this;
+	    ++*this;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator--() requires bidirectional_range<_Base>
+	  {
+	    --_M_current;
+	    return *this;
+	  }
+
+	  constexpr _Iterator
+	  operator--(int) requires bidirectional_range<_Base>
+	  {
+	    auto __tmp = *this;
+	    --*this;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator+=(difference_type __n) requires random_access_range<_Base>
+	  {
+	    _M_current += __n;
+	    return *this;
+	  }
+
+	  constexpr _Iterator&
+	  operator-=(difference_type __n) requires random_access_range<_Base>
+	  {
+	    _M_current -= __n;
+	    return *this;
+	  }
+
+	  constexpr decltype(auto)
+	  operator[](difference_type __n) const
+	    requires random_access_range<_Base>
+	  { return invoke(*_M_parent->_M_fun, _M_current[__n]); }
+
+	  friend constexpr bool
+	  operator==(const _Iterator& __x, const _Iterator& __y)
+	    requires equality_comparable<iterator_t<_Base>>
+	  { return __x._M_current == __y._M_current; }
+
+	  friend constexpr bool
+	  operator<(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __x._M_current < __y._M_current; }
+
+	  friend constexpr bool
+	  operator>(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __y < __x; }
+
+	  friend constexpr bool
+	  operator<=(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return !(__y < __x); }
+
+	  friend constexpr bool
+	  operator>=(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return !(__x < __y); }
+
+#ifdef __cpp_lib_threeway_comparison
+	  friend constexpr compare_three_way_result_t<iterator_t<_Base>>
+	  operator<=>(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	      && three_way_comparable<iterator_t<_Base>>
+	  { return __x._M_current <=> __y._M_current; }
+#endif
+
+	  friend constexpr _Iterator
+	  operator+(_Iterator __i, difference_type __n)
+	    requires random_access_range<_Base>
+	  { return {*__i._M_parent, __i._M_current + __n}; }
+
+	  friend constexpr _Iterator
+	  operator+(difference_type __n, _Iterator __i)
+	    requires random_access_range<_Base>
+	  { return {*__i._M_parent, __i._M_current + __n}; }
+
+	  friend constexpr _Iterator
+	  operator-(_Iterator __i, difference_type __n)
+	    requires random_access_range<_Base>
+	  { return {*__i._M_parent, __i._M_current - __n}; }
+
+	  friend constexpr difference_type
+	  operator-(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __x._M_current - __y._M_current; }
+
+	  friend constexpr decltype(auto)
+	  iter_move(const _Iterator& __i) noexcept(noexcept(__iter_move()))
+	  { return __iter_move(__i); }
+
+	  friend constexpr void
+	  iter_swap(const _Iterator& __x, const _Iterator& __y)
+	    noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current)))
+	    requires indirectly_swappable<iterator_t<_Base>>
+	  { return ranges::iter_swap(__x._M_current, __y._M_current); }
+
+	  friend _Sentinel<_Const>;
+	};
+
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Parent
+	    = conditional_t<_Const, const transform_view, transform_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr range_difference_t<_Base>
+	  __distance_from(const _Iterator<_Const>& __i) const
+	  { return _M_end - __i._M_current; }
+
+	  constexpr bool
+	  __equal(const _Iterator<_Const>& __i) const
+	  { return __i._M_current == _M_end; }
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(sentinel_t<_Base> __end)
+	    : _M_end(__end)
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __i)
+	    requires _Const
+	      && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(std::move(__i._M_end))
+	  { }
+
+	  constexpr sentinel_t<_Base>
+	  base() const
+	  { return _M_end; }
+
+	  friend constexpr bool
+	  operator==(const _Iterator<_Const>& __x, const _Sentinel& __y)
+	  { return __y.__equal(__x); }
+
+	  friend constexpr range_difference_t<_Base>
+	  operator-(const _Iterator<_Const>& __x, const _Sentinel& __y)
+	    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	  { return -__y.__distance_from(__x); }
+
+	  friend constexpr range_difference_t<_Base>
+	  operator-(const _Sentinel& __y, const _Iterator<_Const>& __x)
+	    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	  { return __y.__distance_from(__x); }
+	};
+
+      _Vp _M_base = _Vp();
+      __detail::__box<_Fp> _M_fun;
+
+    public:
+      transform_view() = default;
+
+      constexpr
+      transform_view(_Vp __base, _Fp __fun)
+	: _M_base(std::move(__base)), _M_fun(std::move(__fun))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range>
+	requires viewable_range<_Range>
+	  && constructible_from<_Vp, all_view<_Range>>
+	constexpr
+	transform_view(_Range&& __r, _Fp __fun)
+	  : _M_base(views::all(std::forward<_Range>(__r)))
+	{
+	}
+	*/
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base ; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr _Iterator<false>
+      begin()
+      { return _Iterator<false>{*this, ranges::begin(_M_base)}; }
+
+      constexpr _Iterator<true>
+      begin() const
+	requires range<const _Vp>
+	  && regular_invocable<const _Fp&, range_reference_t<const _Vp>>
+      { return _Iterator<true>{*this, ranges::begin(_M_base)}; }
+
+      constexpr _Sentinel<false>
+      end()
+      { return _Sentinel<false>{ranges::end(_M_base)}; }
+
+      constexpr _Iterator<false>
+      end() requires common_range<_Vp>
+      { return _Iterator<false>{*this, ranges::end(_M_base)}; }
+
+      constexpr _Sentinel<true>
+      end() const
+	requires range<const _Vp>
+	  && regular_invocable<const _Fp&, range_reference_t<const _Vp>>
+      { return _Sentinel<true>{ranges::end(_M_base)}; }
+
+      constexpr _Iterator<true>
+      end() const
+	requires common_range<const _Vp>
+	  && regular_invocable<const _Fp&, range_reference_t<const _Vp>>
+      { return _Iterator<true>{*this, ranges::end(_M_base)}; }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      { return ranges::size(_M_base); }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      { return ranges::size(_M_base); }
+    };
+
+  template<typename _Range, typename _Fp>
+    transform_view(_Range&&, _Fp) -> transform_view<all_view<_Range>, _Fp>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptor transform
+      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
+      {
+	return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
+      };
+  } // namespace views
+
+  template<view _Vp>
+    class take_view : public view_interface<take_view<_Vp>>
+    {
+    private:
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+	  using _CI = counted_iterator<iterator_t<_Base>>;
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(sentinel_t<_Base> __end)
+	    : _M_end(__end)
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __s)
+	    requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(std::move(__s._M_end))
+	  { }
+
+	  constexpr sentinel_t<_Base>
+	  base() const
+	  { return _M_end; }
+
+	  friend constexpr bool operator==(const _CI& __y, const _Sentinel& __x)
+	  { return __y.count() == 0 || __y.base() == __x._M_end; }
+	};
+
+      _Vp _M_base = _Vp();
+      range_difference_t<_Vp> _M_count = 0;
+
+    public:
+      take_view() = default;
+
+      constexpr
+      take_view(_Vp base, range_difference_t<_Vp> __count)
+	: _M_base(std::move(base)), _M_count(std::move(__count))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<viewable_range _Range>
+	requires constructible_from<_Vp, all_view<_Range>>
+      constexpr
+      take_view(_Range&& __r, range_difference_t<_Vp> __count)
+	: _M_base(views::all(std::forward<_Range>(__r))), _M_count(__count)
+      { }
+      */
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin() requires (!__detail::__simple_view<_Vp>)
+      {
+	if constexpr (sized_range<_Vp>) {
+	  if constexpr (random_access_range<_Vp>)
+	    return ranges::begin(_M_base);
+	  else
+	    return counted_iterator{ranges::begin(_M_base), size()};
+	} else
+	  return counted_iterator{ranges::begin(_M_base), _M_count};
+      }
+
+      constexpr auto
+      begin() const requires range<const _Vp>
+      {
+	if constexpr (sized_range<const _Vp>) {
+	  if constexpr (random_access_range<const _Vp>)
+	    return ranges::begin(_M_base);
+	  else
+	    return counted_iterator{ranges::begin(_M_base), size()};
+	} else
+	  return counted_iterator{ranges::begin(_M_base), _M_count};
+      }
+
+      constexpr auto
+      end() requires (!__detail::__simple_view<_Vp>)
+      {
+	if constexpr (sized_range<_Vp>) {
+	  if constexpr (random_access_range<_Vp>)
+	    return ranges::begin(_M_base) + size();
+	  else
+	    return default_sentinel;
+	} else
+	  return _Sentinel<false>{ranges::end(_M_base)};
+      }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      {
+	if constexpr (sized_range<const _Vp>) {
+	  if constexpr (random_access_range<const _Vp>)
+	    return ranges::begin(_M_base) + size();
+	  else
+	    return default_sentinel;
+	} else
+	  return _Sentinel<true>{ranges::end(_M_base)};
+      }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      {
+	auto __n = ranges::size(_M_base);
+	return __detail::min(__n, static_cast<decltype(__n)>(_M_count));
+      }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      {
+	auto __n = ranges::size(_M_base);
+	return __detail::min(__n, static_cast<decltype(__n)>(_M_count));
+      }
+    };
+
+  template<range _Range>
+    take_view(_Range&&, range_difference_t<_Range>)
+      -> take_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptor take
+      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
+      {
+	return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+      };
+  } // namespace views
+
+  template<view _Vp, typename _Pred>
+    requires input_range<_Vp> && is_object_v<_Pred>
+      && indirect_unary_predicate<const _Pred, iterator_t<_Vp>>
+    class take_while_view : public view_interface<take_while_view<_Vp, _Pred>>
+    {
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+	  const _Pred* _M_pred = nullptr;
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+	    : _M_end(__end), _M_pred(__pred)
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __s)
+	    requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(__s._M_end), _M_pred(__s._M_pred)
+	  { }
+
+	  constexpr sentinel_t<_Base>
+	  base() const { return _M_end; }
+
+	  friend constexpr bool
+	  operator==(const iterator_t<_Base>& __x, const _Sentinel& __y)
+	  { return __y._M_end == __x || !invoke(*__y._M_pred, *__x); }
+	};
+
+      _Vp _M_base;
+      __detail::__box<_Pred> _M_pred;
+
+    public:
+      take_while_view() = default;
+
+      constexpr
+      take_while_view(_Vp base, _Pred __pred)
+	: _M_base(std::move(base)), _M_pred(std::move(__pred))
+      {
+      }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr const _Pred&
+      pred() const
+      { return *_M_pred; }
+
+      constexpr auto
+      begin() requires (!__detail::__simple_view<_Vp>)
+      { return ranges::begin(_M_base); }
+
+      constexpr auto
+      begin() const requires range<const _Vp>
+      { return ranges::begin(_M_base); }
+
+      constexpr auto
+      end() requires (!__detail::__simple_view<_Vp>)
+      { return _Sentinel<false>(ranges::end(_M_base),
+				addressof(*_M_pred)); }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      { return _Sentinel<true>(ranges::end(_M_base),
+			       addressof(*_M_pred)); }
+    };
+
+  template<typename _Range, typename _Pred>
+    take_while_view(_Range&&, _Pred)
+      -> take_while_view<all_view<_Range>, _Pred>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptor take_while
+      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
+      {
+	return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+      };
+  } // namespace views
+
+  template<view _Vp>
+    class drop_view : public view_interface<drop_view<_Vp>>
+    {
+    private:
+      _Vp _M_base;
+      range_difference_t<_Vp> _M_count;
+
+    public:
+      drop_view() = default;
+
+      constexpr
+      drop_view(_Vp __base, range_difference_t<_Vp> __count)
+	: _M_base(std::move(__base)), _M_count(__count)
+      { __glibcxx_assert(__count >= 0); }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin() requires (!(__detail::__simple_view<_Vp>
+			  && random_access_range<_Vp>))
+      {
+	// XXX: we need to cache the result here as per [range.drop.view]
+	return ranges::next(ranges::begin(_M_base), _M_count,
+			    ranges::end(_M_base));
+      }
+
+      constexpr auto
+      begin() const requires random_access_range<const _Vp>
+      {
+	return ranges::next(ranges::begin(_M_base), _M_count,
+			    ranges::end(_M_base));
+      }
+
+      constexpr auto
+      end() requires (!__detail::__simple_view<_Vp>)
+      { return ranges::end(_M_base); }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      { return ranges::end(_M_base); }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      {
+	const auto __s = ranges::size(_M_base);
+	const auto __c = static_cast<decltype(__s)>(_M_count);
+	return __s < __c ? 0 : __s - __c;
+      }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      {
+	const auto __s = ranges::size(_M_base);
+	const auto __c = static_cast<decltype(__s)>(_M_count);
+	return __s < __c ? 0 : __s - __c;
+      }
+    };
+
+  template<typename _Range>
+    drop_view(_Range&&, range_difference_t<_Range>)
+      -> drop_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptor drop
+      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
+      {
+	return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+      };
+  } // namespace views
+
+  template<view _Vp, typename _Pred>
+    requires input_range<_Vp> && is_object_v<_Pred>
+      && indirect_unary_predicate<const _Pred, iterator_t<_Vp>>
+    class drop_while_view : public view_interface<drop_while_view<_Vp, _Pred>>
+    {
+    private:
+      _Vp _M_base;
+      __detail::__box<_Pred> _M_pred;
+
+    public:
+      drop_while_view() = default;
+
+      constexpr
+      drop_while_view(_Vp __base, _Pred __pred)
+	: _M_base(std::move(__base)), _M_pred(std::move(__pred))
+      { }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr const _Pred&
+      pred() const
+      { return *_M_pred; }
+
+      constexpr auto
+      begin()
+      {
+	// XXX: we need to cache the result here as per [range.drop.while.view]
+	return __detail::find_if_not(_M_base, cref(*_M_pred));
+      }
+
+      constexpr auto
+      end()
+      { return ranges::end(_M_base); }
+    };
+
+  template<typename _Range, typename _Pred>
+    drop_while_view(_Range&&, _Pred)
+      -> drop_while_view<all_view<_Range>, _Pred>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptor drop_while
+      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
+      {
+	return drop_while_view{std::forward<_Range>(__r),
+			       std::forward<_Pred>(__p)};
+      };
+  } // namespace views
+
+  template<input_range _Vp>
+    requires view<_Vp> && input_range<range_reference_t<_Vp>>
+      && (is_reference_v<range_reference_t<_Vp>>
+	  || view<range_value_t<_Vp>>)
+    class join_view : public view_interface<join_view<_Vp>>
+    {
+    private:
+      using _InnerRange = range_reference_t<_Vp>;
+
+      template<bool _Const>
+	struct _Sentinel;
+
+      template<bool _Const>
+	struct _Iterator
+	{
+	private:
+	  using _Parent = conditional_t<_Const, const join_view, join_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  static constexpr bool _S_ref_is_glvalue
+	    = is_reference_v<range_reference_t<_Base>>;
+
+	  constexpr void
+	  _M_satisfy()
+	  {
+	    auto __update_inner = [this] (range_reference_t<_Base> __x) -> auto&
+	    {
+	      if constexpr (_S_ref_is_glvalue)
+		return __x;
+	      else
+		return (_M_parent->_M_inner = views::all(std::move(__x)));
+	    };
+
+	    for (; _M_outer != ranges::end(_M_parent->_M_base); ++_M_outer)
+	      {
+		auto& inner = __update_inner(*_M_outer);
+		_M_inner = ranges::begin(inner);
+		if (_M_inner != ranges::end(inner))
+		  return;
+	      }
+
+	    if constexpr (_S_ref_is_glvalue)
+	      _M_inner = iterator_t<range_reference_t<_Base>>();
+	  }
+
+	  static constexpr auto
+	  _S_iter_concept()
+	  {
+	    if constexpr (_S_ref_is_glvalue
+			  && bidirectional_range<_Base>
+			  && bidirectional_range<range_reference_t<_Base>>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (_S_ref_is_glvalue
+			       && forward_range<_Base>
+			       && forward_range<range_reference_t<_Base>>)
+	      return forward_iterator_tag{};
+	    else
+	      return input_iterator_tag{};
+	  }
+
+	  static constexpr auto
+	  _S_iter_cat()
+	  {
+	    using _OuterCat
+	      = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    using _InnerCat
+	      = iterator_traits<iterator_t<range_reference_t<_Base>>>
+		 ::iterator_category;
+	    if constexpr (_S_ref_is_glvalue
+			  && derived_from<_OuterCat, bidirectional_iterator_tag>
+			  && derived_from<_InnerCat, bidirectional_iterator_tag>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (_S_ref_is_glvalue
+			       && derived_from<_OuterCat, forward_iterator_tag>
+			       && derived_from<_InnerCat, forward_iterator_tag>)
+	      return forward_iterator_tag{};
+	    else if constexpr (derived_from<_OuterCat, input_iterator_tag>
+			       && derived_from<_InnerCat, input_iterator_tag>)
+	      return input_iterator_tag{};
+	    else
+	      return output_iterator_tag{};
+	  }
+
+	  iterator_t<_Base> _M_outer = iterator_t<_Base>();
+	  iterator_t<range_reference_t<_Base>> _M_inner
+	    = iterator_t<range_reference_t<_Base>>();
+	  _Parent* _M_parent = nullptr;
+
+	public:
+	  using iterator_concept = decltype(_S_iter_concept());
+	  using iterator_category = decltype(_S_iter_cat());
+	  using value_type = range_value_t<range_reference_t<_Base>>;
+	  using difference_type
+	    = common_type_t<range_difference_t<_Base>,
+			    range_difference_t<range_reference_t<_Base>>>;
+
+	  _Iterator() = default;
+
+	  // XXX: had to change the type of __outer from iterator_t<_Vp> to
+	  // iterator_t<_Base> here, a possible defect in the spec?
+	  constexpr
+	  _Iterator(_Parent& __parent, iterator_t<_Base> __outer)
+	    : _M_outer(std::move(__outer)), _M_parent(addressof(__parent))
+	  { _M_satisfy(); }
+
+	  constexpr
+	  _Iterator(_Iterator<!_Const> __i)
+	    requires _Const
+	      && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+	      && convertible_to<iterator_t<_InnerRange>,
+				iterator_t<range_reference_t<_Base>>>
+	    : _M_outer(std::move(__i._M_outer)), _M_inner(__i._M_inner),
+	      _M_parent(__i._M_parent)
+	  { }
+
+	  constexpr decltype(auto)
+	  operator*() const
+	  { return *_M_inner; }
+
+	  constexpr iterator_t<_Base>
+	  operator->() const
+	    requires __detail::__has_arrow<iterator_t<_Base>>
+	      && copyable<iterator_t<_Base>>
+	  { return _M_inner; }
+
+	  constexpr _Iterator&
+	  operator++()
+	  {
+	    auto&& __inner_range = [this] () -> decltype(auto) {
+	      if constexpr (_S_ref_is_glvalue)
+		return *_M_outer;
+	      else
+		return _M_parent->_M_inner;
+	    }();
+	    if (++_M_inner == ranges::end(__inner_range))
+	      {
+		++_M_outer;
+		_M_satisfy();
+	      }
+	    return *this;
+	  }
+
+	  constexpr void
+	  operator++(int)
+	  { ++*this; }
+
+	  constexpr _Iterator
+	  operator++(int)
+	    requires _S_ref_is_glvalue && forward_range<_Base>
+	      && forward_range<range_reference_t<_Base>>
+	  {
+	    auto __tmp = *this;
+	    ++*this;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator--()
+	    requires _S_ref_is_glvalue && bidirectional_range<_Base>
+	      && bidirectional_range<range_reference_t<_Base>>
+	  {
+	    if (_M_outer == ranges::end(_M_parent->_M_base))
+	      _M_inner = ranges::end(*--_M_outer);
+	    while (_M_inner == ranges::begin(*_M_outer))
+	      _M_inner = ranges::end(*--_M_outer);
+	    --_M_inner;
+	    return *this;
+	  }
+
+	  constexpr _Iterator
+	  operator--(int)
+	    requires _S_ref_is_glvalue && bidirectional_range<_Base>
+	      && bidirectional_range<range_reference_t<_Base>>
+	  {
+	    auto __tmp = *this;
+	    --*this;
+	    return __tmp;
+	  }
+
+	  friend constexpr bool
+	  operator==(const _Iterator& __x, const _Iterator& __y)
+	    requires _S_ref_is_glvalue
+	      && equality_comparable<iterator_t<_Base>>
+	      && equality_comparable<iterator_t<range_reference_t<_Base>>>
+	  {
+	    return (__x._M_outer == __y._M_outer
+		    && __x._M_inner == __y._M_inner);
+	  }
+
+	  friend constexpr decltype(auto)
+	  iter_move(const _Iterator& __i)
+	  noexcept(noexcept(ranges::iter_move(__i._M_inner)))
+	  { return ranges::iter_move(__i._M_inner); }
+
+	  friend constexpr void
+	  iter_swap(const _Iterator& __x, const _Iterator& __y)
+	    noexcept(noexcept(ranges::iter_swap(__x._M_inner, __y._M_inner)))
+	  { return ranges::iter_swap(__x._M_inner, __y._M_inner); }
+
+	  friend _Sentinel<_Const>;
+	};
+
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Parent = conditional_t<_Const, const join_view, join_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr bool
+	  __equal(const _Iterator<_Const>& __i) const
+	  { return __i._M_outer == _M_end; }
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(_Parent& __parent)
+	    : _M_end(ranges::end(__parent._M_base))
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __s)
+	    requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(std::move(__s._M_end))
+	  { }
+
+	  friend constexpr bool
+	  operator==(const _Iterator<_Const>& __x, const _Sentinel& __y)
+	  { return __y.__equal(__x); }
+	};
+
+      _Vp _M_base = _Vp();
+
+      // XXX: _M_inner is "present only when !is_reference_v<_InnerRange>"
+      // Applied P3278 and made this field mutable.
+      [[no_unique_address]] mutable
+	conditional_t<!is_reference_v<_InnerRange>,
+		      all_view<_InnerRange>, __detail::_Empty> _M_inner;
+
+    public:
+      join_view() = default;
+
+      constexpr explicit
+      join_view(_Vp __base)
+	: _M_base(std::move(__base))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range>
+	requires viewable_range<_Range>
+	  && constructible_from<_Vp, all_view<_Range>>
+      constexpr explicit
+      join_view(_Range&& __r)
+	: _M_base(views::all(std::forward<_Range>(__r)))
+      { }
+      */
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin()
+      {
+	return _Iterator<__detail::__simple_view<_Vp>>{*this,
+						       ranges::begin(_M_base)};
+      }
+
+      constexpr auto
+      begin() const
+	requires input_range<const _Vp>
+	  && is_reference_v<range_reference_t<const _Vp>>
+      {
+	return _Iterator<true>{*this, ranges::begin(_M_base)};
+      }
+
+      constexpr auto
+      end()
+      {
+	if constexpr (forward_range<_Vp> && is_reference_v<_InnerRange>
+		      && forward_range<_InnerRange>
+		      && common_range<_Vp> && common_range<_InnerRange>)
+	  return _Iterator<__detail::__simple_view<_Vp>>{*this,
+							 ranges::end(_M_base)};
+	else
+	  return _Sentinel<__detail::__simple_view<_Vp>>{*this};
+      }
+
+      constexpr auto
+      end() const
+	requires input_range<const _Vp>
+	  && is_reference_v<range_reference_t<const _Vp>>
+      {
+	if constexpr (forward_range<const _Vp>
+		      && is_reference_v<range_reference_t<const _Vp>>
+		      && forward_range<range_reference_t<const _Vp>>
+		      && common_range<const _Vp>
+		      && common_range<range_reference_t<const _Vp>>)
+	  return _Iterator<true>{*this, ranges::end(_M_base)};
+	else
+	  return _Sentinel<true>{*this};
+      }
+    };
+
+  template<typename _Range>
+    explicit join_view(_Range&&) -> join_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptorClosure join
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	return join_view{std::forward<_Range>(__r)};
+      };
+  } // namespace views
+
+  namespace __detail
+  {
+    template<auto>
+      struct __require_constant;
+
+    template<typename _Range>
+      concept __tiny_range = sized_range<_Range>
+	&& requires
+	   { typename __require_constant<remove_reference_t<_Range>::size()>; }
+	&& (remove_reference_t<_Range>::size() <= 1);
+  }
+
+  template<input_range _Vp, forward_range _Pattern>
+    requires view<_Vp> && view<_Pattern>
+      && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
+			       ranges::equal_to>
+      && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>)
+    class split_view : public view_interface<split_view<_Vp, _Pattern>>
+    {
+    private:
+      template<bool _Const>
+	struct _InnerIter;
+
+      template<bool _Const>
+	struct _OuterIter
+	{
+	private:
+	  using _Parent = conditional_t<_Const, const split_view, split_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr bool
+	  __at_end() const
+	  { return _M_current == ranges::end(_M_parent->_M_base); }
+
+	  // XXX: [24.7.11.3.1]
+	  //  Many of the following specifications refer to the notional member
+	  //  current of outer-iterator.  current is equivalent to current_­ if
+	  //  V models forward_range, and parent_->current_­ otherwise.
+	  constexpr auto&
+	  __current()
+	  {
+	    if constexpr (forward_range<_Vp>)
+	      return _M_current;
+	    else
+	      return _M_parent->_M_current;
+	  }
+
+	  constexpr auto&
+	  __current() const
+	  {
+	    if constexpr (forward_range<_Vp>)
+	      return _M_current;
+	    else
+	      return _M_parent->_M_current;
+	  }
+
+	  _Parent* _M_parent = nullptr;
+
+	  // XXX: _M_current is present only if "V models forward_range"
+	  [[no_unique_address]]
+	    conditional_t<forward_range<_Vp>,
+			  iterator_t<_Base>, __detail::_Empty> _M_current;
+
+	public:
+	  using iterator_concept = conditional_t<forward_range<_Base>,
+						 forward_iterator_tag,
+						 input_iterator_tag>;
+	  using iterator_category = input_iterator_tag;
+	  using difference_type = range_difference_t<_Base>;
+
+	  struct value_type : view_interface<value_type>
+	  {
+	  private:
+	    _OuterIter _M_i = _OuterIter();
+
+	  public:
+	    value_type() = default;
+
+	    constexpr explicit
+	    value_type(_OuterIter __i)
+	      : _M_i(std::move(__i))
+	    { }
+
+	    constexpr _InnerIter<_Const>
+	    begin() const
+	      requires copyable<_OuterIter>
+	    { return _InnerIter<_Const>{_M_i}; }
+
+	    constexpr _InnerIter<_Const>
+	    begin()
+	      requires (!copyable<_OuterIter>)
+	    { return _InnerIter<_Const>{std::move(_M_i)}; }
+
+	    constexpr default_sentinel_t
+	    end() const
+	    { return default_sentinel; }
+	  };
+
+	  _OuterIter() = default;
+
+	  constexpr explicit
+	  _OuterIter(_Parent& __parent) requires (!forward_range<_Base>)
+	    : _M_parent(address(__parent))
+	  { }
+
+	  constexpr
+	  _OuterIter(_Parent& __parent, iterator_t<_Base> __current)
+	    requires forward_range<_Base>
+	    : _M_parent(addressof(__parent)), _M_current(std::move(__current))
+	  { }
+
+	  constexpr
+	  _OuterIter(_OuterIter<!_Const> __i)
+	    requires _Const
+	      && convertible_to<iterator_t<_Vp>, iterator_t<const _Vp>>
+	    : _M_parent(__i._M_parent), _M_current(std::move(__i._M_current))
+	  { }
+
+	  constexpr value_type
+	  operator*() const
+	  { return value_type{*this}; }
+
+	  constexpr _OuterIter&
+	  operator++()
+	  {
+	    const auto __end = ranges::end(_M_parent->_M_base);
+	    if (_M_current == __end)
+	      return *this;
+	    const auto __pbegin = ranges::begin(_M_parent->_M_pattern);
+	    const auto __pend = ranges::end(_M_parent->_M_pattern);
+	    if (__pbegin == __pend)
+	      ++_M_current;
+	    else
+	      do
+		{
+		  auto [__b, __p]
+		    = __detail::mismatch(std::move(_M_current), __end,
+					 __pbegin, __pend);
+		  _M_current = std::move(__b);
+		  if (__p == __pend)
+		    break;
+		} while (++_M_current != __end);
+	    return *this;
+	  }
+
+	  constexpr decltype(auto)
+	  operator++(int)
+	  {
+	    if constexpr (forward_range<_Base>)
+	      {
+		auto __tmp = *this;
+		++*this;
+		return __tmp;
+	      }
+	    else
+	      ++*this;
+	  }
+
+	  friend constexpr bool
+	  operator==(const _OuterIter& __x, const _OuterIter& __y)
+	    requires forward_range<_Base>
+	  { return __x._M_current == __y._M_current; }
+
+	  friend constexpr bool
+	  operator==(const _OuterIter& __x, default_sentinel_t)
+	  { return __x.__at_end(); };
+
+	  friend _InnerIter<_Const>;
+	};
+
+      template<bool _Const>
+	struct _InnerIter
+	{
+	private:
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr bool
+	  __at_end() const
+	  {
+	    // XXX: the spec says
+	    // auto [__pcur, __pend] = subrange{_M_i._M_parent->_M_pattern};
+	    // but a subrange cannot be decomposed with a structured binding
+	    auto& __pattern = _M_i._M_parent->_M_pattern;
+	    auto __pcur = ranges::begin(__pattern);
+	    auto __pend = ranges::end(__pattern);
+
+	    auto __end = ranges::end(_M_i._M_parent->_M_base);
+	    if constexpr (__detail::__tiny_range<_Pattern>)
+	      {
+		const auto& __cur = _M_i.__current();
+		if (__cur == __end)
+		  return true;
+		if (__pcur == __pend)
+		  return _M_incremented;
+		return *__cur == *__pcur;
+	      }
+	    else
+	      {
+		auto __cur = _M_i.__current();
+		if (__cur == __end)
+		  return true;
+		if (__pcur == __pend)
+		  return _M_incremented;
+		do
+		  {
+		    if (*__cur != *__pcur)
+		      return false;
+		    if (++__pcur == __pend)
+		      return true;
+		  } while (++__cur != __end);
+		return false;
+	      }
+	  }
+
+	  static constexpr auto
+	  _S_iter_cat()
+	  {
+	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    if constexpr (derived_from<_Cat, forward_iterator_tag>)
+	      return forward_iterator_tag{};
+	    else
+	      return _Cat{};
+	  }
+
+	  static constexpr decltype(auto)
+	  __iter_move(const _InnerIter& __i = {})
+	  noexcept(noexcept(ranges::iter_move(__i._M_i.__current())))
+	  { return ranges::iter_move(__i._M_i.__current()); }
+
+	  static constexpr void
+	  __iter_swap(const _InnerIter& __x = {}, const _InnerIter& __y = {})
+	    noexcept(noexcept(ranges::iter_swap(__x._M_i.__current(),
+						__y._M_i.__current())))
+	  { ranges::iter_swap(__x._M_i.__current(), __y._M_i.__current()); }
+
+	  _OuterIter<_Const> _M_i = _OuterIter<_Const>();
+	  bool _M_incremented = false;
+
+	public:
+	  using iterator_concept = typename _OuterIter<_Const>::iterator_concept;
+	  using iterator_category = decltype(_S_iter_cat());
+	  using value_type = range_value_t<_Base>;
+	  using difference_type = range_difference_t<_Base>;
+
+	  _InnerIter() = default;
+
+	  constexpr explicit
+	  _InnerIter(_OuterIter<_Const> __i)
+	    : _M_i(std::move(__i))
+	  { }
+
+	  constexpr decltype(auto)
+	  operator*() const
+	  { return *_M_i._M_current; }
+
+	  constexpr _InnerIter&
+	  operator++()
+	  {
+	    _M_incremented = true;
+	    if constexpr (!forward_range<_Base>)
+	      if constexpr (_Pattern::size() == 0)
+		return *this;
+	    ++_M_i.__current();
+	    return *this;
+	  }
+
+	  constexpr decltype(auto)
+	  operator++(int)
+	  {
+	    if constexpr (forward_range<_Vp>)
+	      {
+		auto __tmp = *this;
+		++*this;
+		return __tmp;
+	      }
+	    else
+	      ++*this;
+	  }
+
+	  friend constexpr bool
+	  operator==(const _InnerIter& __x, const _InnerIter& __y)
+	    requires forward_range<_Base>
+	  { return __x._M_i.__current() == __y._M_i.__current(); }
+
+	  friend constexpr bool
+	  operator==(const _InnerIter& __x, default_sentinel_t)
+	  { return __x.__at_end(); }
+
+	  friend constexpr decltype(auto)
+	  iter_move(const _InnerIter& __i) noexcept(noexcept(__iter_move()))
+	  { return __iter_move(__i); }
+
+	  friend constexpr void
+	  iter_swap(const _InnerIter& __x, const _InnerIter& __y)
+	    noexcept(noexcept(__iter_swap()))
+	    requires indirectly_swappable<iterator_t<_Base>>
+	  { __iter_swap(__x, __y); }
+	};
+
+      _Vp _M_base = _Vp();
+      _Pattern _M_pattern = _Pattern();
+
+      // XXX: _M_current is "present only if !forward_range<V>"
+      [[no_unique_address]]
+	conditional_t<!forward_range<_Vp>,
+		      iterator_t<_Vp>, __detail::_Empty> _M_current;
+
+
+    public:
+      split_view() = default;
+
+      constexpr
+      split_view(_Vp __base, _Pattern __pattern)
+	: _M_base(std::move(__base)), _M_pattern(std::move(__pattern))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range, forward_range _Pred>
+	requires constructible_from<_Vp, all_view<_Range>>
+	  && constructible_from<_Pattern, all_view<_Pred>>
+	constexpr
+	split_view(_Range&& __r, _Pred&& __p)
+	  : _M_base(views::all(std::forward<_Range>(__r))),
+	    _M_pattern(views::all(std::forward<_Pred>(__p)))
+      { }
+      */
+
+      template<input_range _Range>
+	requires constructible_from<_Vp, all_view<_Range>>
+	  && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
+	constexpr
+	split_view(_Range&& __r, range_value_t<_Range> __e)
+	  : _M_base(views::all(std::forward<_Range>(__r))),
+	    _M_pattern(std::move(__e))
+	{ }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin()
+      {
+	if constexpr (forward_range<_Vp>)
+	  return _OuterIter<__detail::__simple_view<_Vp>>{*this,
+							ranges::begin(_M_base)};
+	else
+	  {
+	    _M_current = ranges::begin(_M_base);
+	    return _OuterIter<false>{*this};
+	  }
+      }
+
+      constexpr auto
+      begin() const requires forward_range<_Vp> && forward_range<const _Vp>
+      {
+	return _OuterIter<true>{*this, ranges::begin(_M_base)};
+      }
+
+      constexpr auto
+      end() requires forward_range<_Vp> && common_range<_Vp>
+      {
+	return _OuterIter<__detail::__simple_view<_Vp>>{*this, ranges::end(_M_base)};
+      }
+
+      constexpr auto
+      end() const
+      {
+	if constexpr (forward_range<_Vp>
+		      && forward_range<const _Vp>
+		      && common_range<const _Vp>)
+	  return _OuterIter<true>{*this, ranges::end(_M_base)};
+	else
+	  return default_sentinel;
+      }
+    };
+
+  template<typename _Range, typename _Pred>
+    split_view(_Range&&, _Pred&&)
+      -> split_view<all_view<_Range>, all_view<_Pred>>;
+
+  template<input_range _Range>
+    split_view(_Range&&, range_value_t<_Range>)
+      -> split_view<all_view<_Range>, single_view<range_value_t<_Range>>>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptor split
+      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
+      {
+	return split_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
+      };
+  } // namespace views
+
+  namespace views
+  {
+    struct _Counted
+    {
+      template<input_or_output_iterator _Iter>
+      constexpr auto
+      operator()(_Iter __i, iter_difference_t<_Iter> __n) const
+      {
+	if constexpr (random_access_iterator<_Iter>)
+	  return subrange{__i, __i + __n};
+	else
+	  return subrange{counted_iterator{__i, __n}, default_sentinel};
+      }
+    };
+
+    inline constexpr _Counted counted{};
+  } // namespace views
+
+  template<view _Vp>
+    requires (!common_range<_Vp>) && copyable<iterator_t<_Vp>>
+    class common_view : public view_interface<common_view<_Vp>>
+    {
+    private:
+      _Vp _M_base = _Vp();
+
+    public:
+      common_view() = default;
+
+      constexpr explicit
+      common_view(_Vp __r)
+	: _M_base(std::move(__r))
+      { }
+
+      /* XXX: P3280 doesn't remove this constructor, but I think it should?
+      template<viewable_range _Range>
+	requires (!common_range<_Range>) && constructible_from<_Vp, all_view<_Range>>
+	constexpr explicit
+	common_view(_Range&& __r)
+	  : _M_base(views::all(std::forward<_Range>(__r)))
+	{ }
+	*/
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin()
+      {
+	if constexpr (random_access_range<_Vp> && sized_range<_Vp>)
+	  return ranges::begin(_M_base);
+	else
+	  return common_iterator<iterator_t<_Vp>, sentinel_t<_Vp>>
+		  (ranges::begin(_M_base));
+      }
+
+      constexpr auto
+      begin() const requires range<const _Vp>
+      {
+	if constexpr (random_access_range<const _Vp> && sized_range<const _Vp>)
+	  return ranges::begin(_M_base);
+	else
+	  return common_iterator<iterator_t<const _Vp>, sentinel_t<const _Vp>>
+		  (ranges::begin(_M_base));
+      }
+
+      constexpr auto
+      end()
+      {
+	if constexpr (random_access_range<_Vp> && sized_range<_Vp>)
+	  return ranges::begin(_M_base) + ranges::size(_M_base);
+	else
+	  return common_iterator<iterator_t<_Vp>, sentinel_t<_Vp>>
+		  (ranges::end(_M_base));
+      }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      {
+	if constexpr (random_access_range<const _Vp> && sized_range<const _Vp>)
+	  return ranges::begin(_M_base) + ranges::size(_M_base);
+	else
+	  return common_iterator<iterator_t<const _Vp>, sentinel_t<const _Vp>>
+		  (ranges::end(_M_base));
+      }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      { return ranges::size(_M_base); }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      { return ranges::size(_M_base); }
+    };
+
+  template<typename _Range>
+    common_view(_Range&&) -> common_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr _RangeAdaptorClosure common
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	if constexpr (common_range<_Range>
+		      && requires { views::all(std::forward<_Range>(__r)); })
+	  return views::all(std::forward<_Range>(__r));
+	else
+	  return common_view{std::forward<_Range>(__r)};
+      };
+
+  } // namespace views
+
+  template<view _Vp>
+    requires bidirectional_range<_Vp>
+    class reverse_view : public view_interface<reverse_view<_Vp>>
+    {
+    private:
+      _Vp _M_base = _Vp();
+
+    public:
+      reverse_view() = default;
+
+      constexpr explicit
+      reverse_view(_Vp __r)
+	: _M_base(std::move(__r))
+	{ }
+
+      /* XXX: P3280 removes this constructor
+      template<viewable_range _Range>
+	requires bidirectional_range<_Range> && constructible_from<_Vp, all_view<_Range>>
+	constexpr explicit
+	reverse_view(_Range&& __r)
+	  : _M_base(views::all(std::forward<_Range>(__r)))
+	{ }
+	*/
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr reverse_iterator<iterator_t<_Vp>>
+      begin()
+      {
+	// XXX: we need to cache the result here as per [range.reverse.view]
+	return make_reverse_iterator(ranges::next(ranges::begin(_M_base),
+						  ranges::end(_M_base)));
+      }
+
+      constexpr auto
+      begin() requires common_range<_Vp>
+      { return make_reverse_iterator(ranges::end(_M_base)); }
+
+      constexpr auto
+      begin() const requires common_range<const _Vp>
+      { return make_reverse_iterator(ranges::end(_M_base)); }
+
+      constexpr reverse_iterator<iterator_t<_Vp>>
+      end()
+      { return make_reverse_iterator(ranges::begin(_M_base)); }
+
+      constexpr auto
+      end() const requires common_range<const _Vp>
+      { return make_reverse_iterator(ranges::begin(_M_base)); }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      { return ranges::size(_M_base); }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      { return ranges::size(_M_base); }
+    };
+
+  template<typename _Range>
+    reverse_view(_Range&&) -> reverse_view<all_view<_Range>>;
+
+  namespace views
+  {
+    namespace __detail
+    {
+      template<typename>
+	inline constexpr bool __is_reversible_subrange = false;
+
+      template<typename _Iter, subrange_kind _Kind>
+	inline constexpr bool
+	  __is_reversible_subrange<subrange<reverse_iterator<_Iter>,
+					    reverse_iterator<_Iter>,
+					    _Kind>> = true;
+
+      template<typename>
+	inline constexpr bool __is_reverse_view = false;
+
+      template<typename _Vp>
+	inline constexpr bool __is_reverse_view<reverse_view<_Vp>> = true;
+    }
+
+    inline constexpr _RangeAdaptorClosure reverse
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	using _Tp = remove_cvref_t<_Range>;
+	if constexpr (__detail::__is_reverse_view<_Tp>)
+	  return std::forward<_Range>(__r).base();
+	else if constexpr (__detail::__is_reversible_subrange<_Tp>)
+	  {
+	    using _Iter = decltype(ranges::begin(__r).base());
+	    if constexpr (sized_range<_Tp>)
+	      return subrange<_Iter, _Iter, subrange_kind::sized>
+		      (__r.end().base(), __r.begin().base(), __r.size());
+	    else
+	      return subrange<_Iter, _Iter, subrange_kind::unsized>
+		      (__r.end().base(), __r.begin().base());
+	  }
+	else
+	  return reverse_view{std::forward<_Range>(__r)};
+      };
+  } // namespace views
+
+  namespace __detail
+  {
+    template<typename _Tp, size_t _Nm>
+    concept __has_tuple_element = requires(_Tp __t)
+      {
+	typename tuple_size<_Tp>::type;
+	requires _Nm < tuple_size_v<_Tp>;
+	typename tuple_element_t<_Nm, _Tp>;
+	// XXX: we applied P3323 here
+	{ get<_Nm>(__t) } -> convertible_to<const tuple_element_t<_Nm, _Tp>&>;
+      };
+  }
+
+  template<input_range _Vp, size_t _Nm>
+    requires view<_Vp>
+      && __detail::__has_tuple_element<range_value_t<_Vp>, _Nm>
+      && __detail::__has_tuple_element<remove_reference_t<range_reference_t<_Vp>>,
+				       _Nm>
+  class elements_view : public view_interface<elements_view<_Vp, _Nm>>
+  {
+  public:
+    elements_view() = default;
+
+    constexpr explicit
+    elements_view(_Vp base)
+      : _M_base(std::move(base))
+    { }
+
+    constexpr _Vp
+    base() const& requires copy_constructible<_Vp>
+    { return _M_base; }
+
+    constexpr _Vp
+    base() &&
+    { return std::move(_M_base); }
+
+    constexpr auto
+    begin() requires (!__detail::__simple_view<_Vp>)
+    { return _Iterator<false>(ranges::begin(_M_base)); }
+
+    constexpr auto
+    begin() const requires __detail::__simple_view<_Vp>
+    { return _Iterator<true>(ranges::begin(_M_base)); }
+
+    constexpr auto
+    end() requires (!__detail::__simple_view<_Vp>)
+    { return ranges::end(_M_base); }
+
+    constexpr auto
+    end() const requires __detail::__simple_view<_Vp>
+    { return ranges::end(_M_base); }
+
+    constexpr auto
+    size() requires sized_range<_Vp>
+    { return ranges::size(_M_base); }
+
+    constexpr auto
+    size() const requires sized_range<const _Vp>
+    { return ranges::size(_M_base); }
+
+  private:
+    template<bool _Const>
+      struct _Iterator
+      {
+	using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	iterator_t<_Base> _M_current;
+
+	friend _Iterator<!_Const>;
+
+      public:
+	using iterator_category
+	  = typename iterator_traits<iterator_t<_Base>>::iterator_category;
+	using value_type
+	  = remove_cvref_t<tuple_element_t<_Nm, range_value_t<_Base>>>;
+	using difference_type = range_difference_t<_Base>;
+
+	_Iterator() = default;
+
+	constexpr explicit
+	_Iterator(iterator_t<_Base> current)
+	  : _M_current(std::move(current))
+	{ }
+
+	constexpr
+	_Iterator(_Iterator<!_Const> i)
+	  requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+	  : _M_current(std::move(i._M_current))
+	{ }
+
+	constexpr iterator_t<_Base>
+	base() const&
+	  requires copyable<iterator_t<_Base>>
+	{ return _M_current; }
+
+	constexpr iterator_t<_Base>
+	base() &&
+	{ return std::move(_M_current); }
+
+	constexpr decltype(auto)
+	  operator*() const
+	{ return get<_Nm>(*_M_current); }
+
+	constexpr _Iterator&
+	operator++()
+	{
+	  ++_M_current;
+	  return *this;
+	}
+
+	constexpr void
+	operator++(int) requires (!forward_range<_Base>)
+	{ ++_M_current; }
+
+	constexpr _Iterator
+	operator++(int) requires forward_range<_Base>
+	{
+	  auto __tmp = *this;
+	  ++_M_current;
+	  return __tmp;
+	}
+
+	constexpr _Iterator&
+	operator--() requires bidirectional_range<_Base>
+	{
+	  --_M_current;
+	  return *this;
+	}
+
+	constexpr _Iterator
+	operator--(int) requires bidirectional_range<_Base>
+	{
+	  auto __tmp = *this;
+	  --_M_current;
+	  return __tmp;
+	}
+
+	constexpr _Iterator&
+	operator+=(difference_type __n)
+	  requires random_access_range<_Base>
+	{
+	  _M_current += __n;
+	  return *this;
+	}
+
+	constexpr _Iterator&
+	operator-=(difference_type __n)
+	  requires random_access_range<_Base>
+	{
+	  _M_current -= __n;
+	  return *this;
+	}
+
+	constexpr decltype(auto)
+	operator[](difference_type __n) const
+	  requires random_access_range<_Base>
+	{ return get<_Nm>(*(_M_current + __n)); }
+
+	friend constexpr bool
+	operator==(const _Iterator& __x, const _Iterator& __y)
+	  requires equality_comparable<iterator_t<_Base>>
+	{ return __x._M_current == __y._M_current; }
+
+	friend constexpr bool
+	operator==(const _Iterator& __x, const sentinel_t<_Base>& __y)
+	{ return __x._M_current == __y; }
+
+	friend constexpr bool
+	operator<(const _Iterator& __x, const _Iterator& __y)
+	  requires random_access_range<_Base>
+	{ return __x._M_current < __y._M_current; }
+
+	friend constexpr bool
+	operator>(const _Iterator& __x, const _Iterator& __y)
+	  requires random_access_range<_Base>
+	{ return __y._M_current < __x._M_current; }
+
+	friend constexpr bool
+	operator<=(const _Iterator& __x, const _Iterator& __y)
+	  requires random_access_range<_Base>
+	{ return !(__y._M_current > __x._M_current); }
+
+	friend constexpr bool
+	operator>=(const _Iterator& __x, const _Iterator& __y)
+	  requires random_access_range<_Base>
+	{ return !(__x._M_current > __y._M_current); }
+
+#ifdef __cpp_lib_threeway_comparison
+	friend constexpr compare_three_way_result_t<iterator_t<_Base>>
+	operator<=>(const _Iterator& __x, const _Iterator& __y)
+	  requires random_access_range<_Base>
+	    && three_way_comparable<iterator_t<_Base>>
+	{ return __x._M_current <=> __y._M_current; }
+#endif
+
+	friend constexpr _Iterator
+	operator+(const _Iterator& __x, difference_type __y)
+	  requires random_access_range<_Base>
+	{ return _Iterator{__x} += __y; }
+
+	friend constexpr _Iterator
+	operator+(difference_type __x, const _Iterator& __y)
+	  requires random_access_range<_Base>
+	{ return __y + __x; }
+
+	friend constexpr _Iterator
+	operator-(const _Iterator& __x, difference_type __y)
+	  requires random_access_range<_Base>
+	{ return _Iterator{__x} -= __y; }
+
+	friend constexpr difference_type
+	operator-(const _Iterator& __x, const _Iterator& __y)
+	  requires random_access_range<_Base>
+	{ return __x._M_current - __y._M_current; }
+
+	friend constexpr difference_type
+	operator-(const _Iterator<_Const>& __x, const sentinel_t<_Base>& __y)
+	  requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	{ return __x._M_current - __y; }
+
+	friend constexpr difference_type
+	operator-(const sentinel_t<_Base>& __x, const _Iterator<_Const>& __y)
+	  requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	{ return -(__y - __x); }
+      };
+
+    _Vp _M_base = _Vp();
+  };
+
+  template<typename _Range>
+    using keys_view = elements_view<all_view<_Range>, 0>;
+
+  template<typename _Range>
+    using values_view = elements_view<all_view<_Range>, 1>;
+
+  namespace views
+  {
+    template<size_t _Nm>
+    inline constexpr _RangeAdaptorClosure elements
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	return elements_view<all_view<_Range>, _Nm>{std::forward<_Range>(__r)};
+      };
+
+    inline constexpr _RangeAdaptorClosure keys = elements<0>;
+    inline constexpr _RangeAdaptorClosure values = elements<1>;
+  } // namespace views
+
 } // namespace ranges
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
new file mode 100644
index 00000000000..7815475ed24
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
@@ -0,0 +1,124 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto v = views::all(x);
+
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+
+  VERIFY( ranges::size(v) == 5 );
+  VERIFY( ranges::size(x | views::all) == 5 );
+  VERIFY( ranges::size(v | views::all | views::all) == 5 );
+  VERIFY( ranges::size(v | (views::all | views::all)) == 5 );
+
+  ranges::reverse(v);
+  VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) );
+}
+
+void
+test02()
+{
+  int x[5] = { 0 };
+  int k = 0;
+  for (auto&& i : ranges::ref_view{x})
+    i += ++k;
+  VERIFY( ranges::equal(x, (int[]){1,2,3,4,5}) );
+}
+
+constexpr bool
+test03()
+{
+  std::array ints{0,1,2,3,4,5};
+  auto even = [] (int i) { return i%2==0; };
+  auto odd = [] (int i) { return i%2==1; };
+  auto square = [] (int i) { return i*i; };
+  int count = 0;
+  for (auto v : (ints
+		 | (views::all
+		    | (views::filter(even)
+		    | (views::filter(odd) | views::all)))
+		 | views::transform(square)))
+    count++;
+  return count == 0;
+}
+
+constexpr bool
+test04()
+{
+  auto even = [] (int i) { return i%2==0; };
+  auto odd = [] (int i) { return i%2==1; };
+  auto square = [] (int i) { return i*i; };
+  auto increment = [] (int i) { return i+1; };
+  auto small = [] (int i) { return i<30; };
+  auto non_negative = [] (int i) { return i>=0; };
+  auto negative = [] (int i) { return i<0; };
+  int count = 0;
+  return ranges::equal(views::iota(-5)
+		       | views::drop_while(negative)
+		       | views::take_while(non_negative)
+		       | views::transform(increment)
+		       | views::filter(odd)
+		       | views::take(3)
+		       | views::all
+		       | views::transform(square),
+		       views::iota(-5)
+		       | views::drop_while(negative)
+		       | views::drop(1)
+		       | views::filter(odd)
+		       | views::transform(square)
+		       | views::take_while(small)
+		       | views::take_while(small));
+}
+
+static_assert(std::is_empty_v<decltype(views::common
+				       | views::join
+				       | views::all
+				       | views::common
+				       | views::keys
+				       | views::reverse)>);
+static_assert(sizeof(decltype(views::take(5) | views::drop(5)))
+	      == sizeof(decltype(views::take(5)
+				 | views::join
+				 | views::common
+				 | views::all
+				 | views::keys
+				 | views::drop(5)
+				 | views::reverse)));
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+  static_assert(test04());
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
new file mode 100644
index 00000000000..d1600544605
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,1,3};
+  auto v = x | views::common;
+  VERIFY( std::count(v.begin(), v.end(), 1) == 2);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+  static_assert(std::same_as<decltype(v), decltype(views::common(v))>);
+
+  auto v2 = v | (views::common | views::common);
+  VERIFY( std::count(v2.begin(), v2.end(), 1) == 2);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,1,3};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto v = ranges::common_view(rx);
+  VERIFY( std::count(v.begin(), v.end(), 1) == 2);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::forward_range<decltype(v)>);
+  static_assert(std::same_as<decltype(v), decltype(views::common(v))>);
+
+  auto v2 = v | (views::common | views::common);
+  VERIFY( std::count(v2.begin(), v2.end(), 1) == 2);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc
new file mode 100644
index 00000000000..c81a8a89741
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc
@@ -0,0 +1,64 @@
+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {0,1,2,3,4,5,0,1,2,3,4,5};
+  auto v = views::counted(x, 5);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test02()
+{
+  int x[] = {0,1,2,3,4,5,0,1,2,3,4,5};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto v = views::counted(rx.begin(), 5);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(!ranges::bidirectional_range<R>);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
new file mode 100644
index 00000000000..93fbafcf5a3
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
@@ -0,0 +1,107 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto v = x | views::drop(3);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::random_access_range<R>);
+  VERIFY( ranges::equal(v, (int[]){4,5}) );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  auto t = views::drop(3) | views::reverse;
+  auto v = x | t;
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::random_access_range<R>);
+  VERIFY( ranges::equal(v, (int[]){5,4}) );
+}
+
+void
+test03()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = rx | views::drop(3);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+  VERIFY( ranges::equal(v, (int[]){4,5}) );
+}
+
+
+void
+test04()
+{
+  auto v = views::iota(0) | views::drop(10);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  VERIFY( ranges::equal(v | views::take(3), (int[]){10,11,12}) );
+}
+
+void
+test05()
+{
+  int x[] = {1,2,3};
+  auto r = ranges::subrange(x, x+1);
+  auto v = views::drop(r, 2);
+  VERIFY( ranges::begin(v) == x+1 );
+  VERIFY( ranges::size(v) == 0 );
+}
+
+void
+test06()
+{
+  int x[] = {1,2,3};
+  VERIFY( ranges::empty(x | views::drop(10)) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
new file mode 100644
index 00000000000..be47551563d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
@@ -0,0 +1,63 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  auto p = [] (int i) { return i != 16; };
+  auto v = views::iota(10) | views::drop_while(p);
+  VERIFY( ranges::equal(v | views::take(5), (int[]){16,17,18,19,20}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = rx | views::drop_while([] (int i) { return i<4; });
+  VERIFY( ranges::equal(v, (int[]){4,5}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
+
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
new file mode 100644
index 00000000000..0c6ee4bf58d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <tuple>
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  std::tuple<int, int> x[] = {{1,2},{3,4},{5,6}};
+  auto v0 = x | views::elements<0>;
+  VERIFY( ranges::equal(v0, (int[]){1,3,5}) );
+  VERIFY( ranges::equal(v0, x | views::keys) );
+  VERIFY( ranges::size(v0) == 3 );
+
+  using R0 = decltype(v0);
+  static_assert(ranges::random_access_range<R0>);
+  static_assert(ranges::sized_range<R0>);
+
+  auto v1 = x | views::reverse | views::elements<1> | views::reverse;
+  VERIFY( ranges::equal(v1, (int[]){2,4,6}) );
+  VERIFY( ranges::equal(v1, x | views::values) );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
new file mode 100644
index 00000000000..83d52967a0f
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5,6};
+  auto is_odd = [] (int i) { return i%2==1; };
+  auto v = x | views::filter(is_odd);
+  using R = decltype(v);
+  static_assert(std::same_as<int&, decltype(*v.begin())>);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::input_range<R>);
+  static_assert(ranges::common_range<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+  static_assert(!ranges::random_access_range<R>);
+  static_assert(ranges::range<ranges::all_view<R>>);
+  VERIFY( ranges::equal(v, (int[]){1,3,5}) );
+  VERIFY( ranges::equal(v | views::reverse, (int[]){5,3,1}) );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5,6};
+  auto f = [flag=false] (int) mutable { return flag = !flag; };
+  auto v = views::filter(f)(x);
+  using R = decltype(v);
+  static_assert(std::same_as<int&, decltype(*v.begin())>);
+  static_assert(ranges::range<R>);
+  static_assert(std::copyable<R>);
+  static_assert(!ranges::view<const R>);
+  VERIFY( ranges::equal(v, (int[]){1,3,5}) );
+}
+
+struct X
+{
+  int i, j;
+};
+
+void
+test03()
+{
+  X x[] = {{1,3}, {2,5}, {3,7}, {4,9}};
+  test_range<X, bidirectional_iterator_wrapper> rx(x);
+  auto v = rx | views::filter([] (auto&& p) { return p.i%2==0; });
+  int sum = 0;
+  for (auto i = v.begin(); i != v.end(); ++i)
+    sum += i->j;
+  VERIFY( sum == 14 );
+}
+
+void
+test04()
+{
+  auto yes = [] (int) { return true; };
+  VERIFY( ranges::equal(views::iota(0) | views::filter(yes) | views::take(1),
+			(int[]){0}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
new file mode 100644
index 00000000000..d3e652da009
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -0,0 +1,112 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  using namespace std::literals;
+  std::string_view cs[] = {"the", "quick", "brown", "fox"};
+  auto v = cs | views::join;
+  VERIFY( ranges::equal(v, "thequickbrownfox"sv) );
+  using R = decltype(v);
+  static_assert(ranges::bidirectional_range<R>);
+  static_assert(ranges::bidirectional_range<const R>);
+  static_assert(ranges::common_range<R>);
+  static_assert(ranges::common_range<const R>);
+}
+
+void
+test02()
+{
+  auto v = (views::iota(0,4)
+	    | views::transform([] (int i) { return views::iota(0,i); })
+	    | views::join);
+  VERIFY( ranges::equal(v, (int[]){0,0,1,0,1,2}) );
+  using R = decltype(v);
+  static_assert(ranges::input_range<R>);
+  static_assert(!ranges::range<const R>);
+  static_assert(!ranges::forward_range<R>);
+  static_assert(!ranges::common_range<const R>);
+}
+
+void
+test03()
+{
+  auto v = (views::iota(0,4)
+	    | views::transform([] (int i) { return views::iota(0,i); })
+	    | views::filter([] (auto) { return true; })
+	    | views::join);
+  VERIFY( ranges::equal(v, (int[]){0,0,1,0,1,2}) );
+  using R = decltype(v);
+  static_assert(ranges::input_range<R>);
+  static_assert(!ranges::range<const R>);
+  static_assert(!ranges::forward_range<R>);
+  static_assert(!ranges::common_range<const R>);
+}
+
+void
+test04()
+{
+  auto v = (views::iota(0,4)
+	    | views::transform([] (int i) { return views::iota(0,i); }));
+  auto v2 = ranges::ref_view{v};
+  VERIFY( ranges::equal(v2 | views::join, (int[]){0,0,1,0,1,2}) );
+  using R = decltype(v2);
+  static_assert(ranges::random_access_range<R>);
+  static_assert(ranges::range<const R>);
+  static_assert(ranges::common_range<const R>);
+  static_assert(ranges::random_access_range<ranges::range_reference_t<R>>);
+  static_assert(!std::is_reference_v<ranges::range_reference_t<R>>);
+}
+
+void
+test05()
+{
+  using namespace std::literals;
+  std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"};
+  auto v = x | views::join | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
new file mode 100644
index 00000000000..b3f2e7eef3b
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
@@ -0,0 +1,86 @@
+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto v = x | views::reverse;
+  VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) );
+  VERIFY( ranges::equal(v | views::reverse, x) );
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::sized_range<decltype(v)>);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = views::reverse(rx);
+  VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) );
+  VERIFY( ranges::equal(v | views::reverse, rx) );
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(!ranges::sized_range<decltype(v)>);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(!ranges::random_access_range<decltype(v)>);
+  static_assert(ranges::bidirectional_range<decltype(v)>);
+}
+
+void
+test03()
+{
+  int x[] = {1,7,3,6,5,2,4,8};
+  auto is_even = [] (int i) { return i%2==0; };
+  int sum = 0;
+  for (auto i : x | views::reverse | views::filter(is_even))
+    sum += i;
+  VERIFY( sum == 20 );
+}
+
+void
+test04()
+{
+  int x[] = {1,2,3,4,5};
+  VERIFY( ranges::equal(x | views::reverse | (views::reverse | views::reverse),
+			(int[]){5,4,3,2,1}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
new file mode 100644
index 00000000000..129a8249f21
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
@@ -0,0 +1,82 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+using namespace std::literals;
+
+void
+test01()
+{
+  auto x = "the  quick  brown  fox"sv;
+  auto p = std::string{"  "};
+  auto v = x | views::split(p);
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test02()
+{
+  auto x = "the quick brown fox"sv;
+  auto v = x | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test03()
+{
+  char x[] = "the quick brown fox";
+  test_range<char, forward_iterator_wrapper> rx(x);
+  auto v = rx | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
new file mode 100644
index 00000000000..32ea9fc5107
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
@@ -0,0 +1,95 @@
+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  auto v = views::iota(0) | views::take(5);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(!ranges::sized_range<decltype(v)>);
+  static_assert(!ranges::common_range<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+  static_assert(!ranges::contiguous_range<decltype(v)>);
+  static_assert(ranges::range<const decltype(v)>);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+}
+
+void
+test02()
+{
+  auto v = views::take(views::iota(0, 20), 5);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::sized_range<decltype(v)>);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+  static_assert(!ranges::contiguous_range<decltype(v)>);
+  static_assert(ranges::range<const decltype(v)>);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+}
+
+void
+test03()
+{
+  int x[] = {0,1,2,3,4,5};
+  auto is_odd = [] (int i) { return i%2 == 1; };
+  auto v = x | views::filter(is_odd) | views::take(3);
+  ranges::begin(v);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::forward_range<R>);
+  static_assert(!ranges::random_access_range<R>);
+  static_assert(!ranges::range<const R>);
+  VERIFY( ranges::equal(v, (int[]){1,3,5}) );
+}
+
+void
+test04()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = ranges::take_view{rx, 3};
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+  VERIFY( ranges::equal(v | views::take(5), (int[]){1,2,3}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
new file mode 100644
index 00000000000..b261ffd1aae
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -0,0 +1,62 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  auto p = [] (int i) { return i != 16; };
+  auto v = views::iota(10) | views::take_while(p);
+  VERIFY( ranges::equal(v, (int[]){10,11,12,13,14,15}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto v = rx | views::take_while([] (int i) { return i<4; });
+  VERIFY( ranges::equal(v, (int[]){1,2,3}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::forward_range<R>);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
new file mode 100644
index 00000000000..ad51fffb43d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -0,0 +1,86 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto is_odd = [] (int i) { return i%2==1; };
+  auto v = x | views::transform(is_odd);
+  VERIFY( ranges::equal(v, (int[]){1,0,1,0,1}) );
+  using R = decltype(v);
+  static_assert(std::same_as<bool, decltype(*ranges::begin(v))>);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+struct X
+{
+  int i,j;
+};
+
+void
+test02()
+{
+  X x[] = {{1,2},{3,4},{5,6},{7,8},{9,10}};
+  test_range<X, random_access_iterator_wrapper> rx(x);
+  auto v = rx | views::transform(&X::i);
+  VERIFY( ranges::size(v) == 5 );
+  VERIFY( ranges::distance(v.begin(), v.end()) == 5 );
+  VERIFY( ranges::equal(v, (int[]){1,3,5,7,9}) );
+  VERIFY( ranges::equal(v | views::reverse, (int[]){9,7,5,3,1}) );
+  using R = decltype(v);
+  static_assert(std::same_as<int&, decltype(*ranges::begin(v))>);
+  static_assert(std::same_as<int, std::iter_value_t<ranges::iterator_t<R>>>);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test03()
+{
+  auto id = [] (int i) { return i; };
+  auto v = views::iota(0) | (views::filter(id)
+			     | views::transform(id)
+			     | views::take(5));
+  VERIFY( ranges::equal(v, (int[]){1,2,3,4,5}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
-- 
2.25.0.114.g5b0ca878e0

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-04  2:07 [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Patrick Palka
  2020-02-04  2:08 ` [PATCH 3/3] libstdc++: Implement C++20 range adaptors Patrick Palka
@ 2020-02-04  2:09 ` Patrick Palka
  2020-02-05 18:39   ` François Dumont
                     ` (3 more replies)
  2020-02-04 10:41 ` [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Jonathan Wakely
  2 siblings, 4 replies; 27+ messages in thread
From: Patrick Palka @ 2020-02-04  2:09 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jwakely, Patrick Palka

This patch implements the C++20 ranges overloads for the algorithms in
[algorithms].  Most of the algorithms were reimplemented, with each of their
implementations very closely following the existing implementation in
bits/stl_algo.h and bits/stl_algobase.h.  The reason for reimplementing most of
the algorithms instead of forwarding to their STL-style overload is because
forwarding cannot be conformantly and efficiently performed for algorithms that
operate on non-random-access iterators.  But algorithms that operate on random
access iterators can safely and efficiently be forwarded to the STL-style
implementation, and this patch does so for push_heap, pop_heap, make_heap,
sort_heap, sort, stable_sort, nth_element, inplace_merge and stable_partition.

What's missing from this patch is debug-iterator and container specializations
that are present for some of the STL-style algorithms that need to be ported
over to the ranges algos.  I marked them missing at TODO comments.  There are
also some other minor outstanding TODOs.

The code that could use the most thorough review is ranges::__copy_or_move,
ranges::__copy_or_move_backward, ranges::__equal and
ranges::__lexicographical_compare.  In the tests, I tried to test the interface
of each new overload, as well as the correctness of the new implementation.

libstdc++-v3/ChangeLog:

	Implement C++20 constrained algorithms
	* include/Makefile.am: Add new header.
	* include/Makefile.in: Regenerate.
	* include/std/algorithm: Include <bits/ranges_algo.h>.
	* include/bits/ranges_algo.h: New file.
	* testsuite/25_algorithms/adjacent_find/constrained.cc: New file.
	* testsuite/25_algorithms/all_of/constrained.cc: New file.
	* testsuite/25_algorithms/any_of/constrained.cc: New file.
	* testsuite/25_algorithms/binary_search/constrained.cc: New file.
	* testsuite/25_algorithms/copy/constrained.cc: New file.
	* testsuite/25_algorithms/copy_backward/constrained.cc: New file.
	* testsuite/25_algorithms/copy_if/constrained.cc: New file.
	* testsuite/25_algorithms/copy_n/constrained.cc: New file.
	* testsuite/25_algorithms/count/constrained.cc: New file.
	* testsuite/25_algorithms/count_if/constrained.cc: New file.
	* testsuite/25_algorithms/equal/constrained.cc: New file.
	* testsuite/25_algorithms/equal_range/constrained.cc: New file.
	* testsuite/25_algorithms/fill/constrained.cc: New file.
	* testsuite/25_algorithms/fill_n/constrained.cc: New file.
	* testsuite/25_algorithms/find/constrained.cc: New file.
	* testsuite/25_algorithms/find_end/constrained.cc: New file.
	* testsuite/25_algorithms/find_first_of/constrained.cc: New file.
	* testsuite/25_algorithms/find_if/constrained.cc: New file.
	* testsuite/25_algorithms/find_if_not/constrained.cc: New file.
	* testsuite/25_algorithms/for_each/constrained.cc: New file.
	* testsuite/25_algorithms/generate/constrained.cc: New file.
	* testsuite/25_algorithms/generate_n/constrained.cc: New file.
	* testsuite/25_algorithms/heap/constrained.cc: New file.
	* testsuite/25_algorithms/includes/constrained.cc: New file.
	* testsuite/25_algorithms/inplace_merge/constrained.cc: New file.
	* testsuite/25_algorithms/is_partitioned/constrained.cc: New file.
	* testsuite/25_algorithms/is_permutation/constrained.cc: New file.
	* testsuite/25_algorithms/is_sorted/constrained.cc: New file.
	* testsuite/25_algorithms/is_sorted_until/constrained.cc: New file.
	* testsuite/25_algorithms/lexicographical_compare/constrained.cc: New file.
	* testsuite/25_algorithms/lower_bound/constrained.cc: New file.
	* testsuite/25_algorithms/max/constrained.cc: New file.
	* testsuite/25_algorithms/max_element/constrained.cc: New file.
	* testsuite/25_algorithms/merge/constrained.cc: New file.
	* testsuite/25_algorithms/min/constrained.cc: New file.
	* testsuite/25_algorithms/min_element/constrained.cc: New file.
	* testsuite/25_algorithms/minmax/constrained.cc: New file.
	* testsuite/25_algorithms/minmax_element/constrained.cc: New file.
	* testsuite/25_algorithms/mismatch/constrained.cc: New file.
	* testsuite/25_algorithms/move/constrained.cc: New file.
	* testsuite/25_algorithms/move_backward/constrained.cc: New file.
	* testsuite/25_algorithms/next_permutation/constrained.cc: New file.
	* testsuite/25_algorithms/none_of/constrained.cc: New file.
	* testsuite/25_algorithms/nth_element/constrained.cc: New file.
	* testsuite/25_algorithms/partial_sort/constrained.cc: New file.
	* testsuite/25_algorithms/partial_sort_copy/constrained.cc: New file.
	* testsuite/25_algorithms/partition/constrained.cc: New file.
	* testsuite/25_algorithms/partition_copy/constrained.cc: New file.
	* testsuite/25_algorithms/partition_point/constrained.cc: New file.
	* testsuite/25_algorithms/prev_permutation/constrained.cc: New file.
	* testsuite/25_algorithms/remove/constrained.cc: New file.
	* testsuite/25_algorithms/remove_copy/constrained.cc: New file.
	* testsuite/25_algorithms/remove_copy_if/constrained.cc: New file.
	* testsuite/25_algorithms/remove_if/constrained.cc: New file.
	* testsuite/25_algorithms/replace/constrained.cc: New file.
	* testsuite/25_algorithms/replace_copy/constrained.cc: New file.
	* testsuite/25_algorithms/replace_copy_if/constrained.cc: New file.
	* testsuite/25_algorithms/replace_if/constrained.cc: New file.
	* testsuite/25_algorithms/reverse/constrained.cc: New file.
	* testsuite/25_algorithms/reverse_copy/constrained.cc: New file.
	* testsuite/25_algorithms/rotate/constrained.cc: New file.
	* testsuite/25_algorithms/rotate_copy/constrained.cc: New file.
	* testsuite/25_algorithms/search/constrained.cc: New file.
	* testsuite/25_algorithms/search_n/constrained.cc: New file.
	* testsuite/25_algorithms/set_difference/constrained.cc: New file.
	* testsuite/25_algorithms/set_intersection/constrained.cc: New file.
	* testsuite/25_algorithms/set_symmetric_difference/constrained.cc: New file.
	* testsuite/25_algorithms/set_union/constrained.cc: New file.
	* testsuite/25_algorithms/shuffle/constrained.cc: New file.
	* testsuite/25_algorithms/sort/constrained.cc: New file.
	* testsuite/25_algorithms/stable_partition/constrained.cc: New file.
	* testsuite/25_algorithms/stable_sort/constrained.cc: New file.
	* testsuite/25_algorithms/swap_ranges/constrained.cc: New file.
	* testsuite/25_algorithms/transform/constrained.cc: New file.
	* testsuite/25_algorithms/unique/constrained.cc: New file.
	* testsuite/25_algorithms/unique_copy/constrained.cc: New file.
	* testsuite/25_algorithms/upper_bound/constrained.cc: New file.
---
 libstdc++-v3/include/Makefile.am              |    1 +
 libstdc++-v3/include/Makefile.in              |    1 +
 libstdc++-v3/include/bits/ranges_algo.h       | 3640 +++++++++++++++++
 libstdc++-v3/include/std/algorithm            |    3 +
 .../adjacent_find/constrained.cc              |   68 +
 .../25_algorithms/all_of/constrained.cc       |   90 +
 .../25_algorithms/any_of/constrained.cc       |   88 +
 .../binary_search/constrained.cc              |   61 +
 .../25_algorithms/copy/constrained.cc         |  225 +
 .../copy_backward/constrained.cc              |  193 +
 .../25_algorithms/copy_if/constrained.cc      |   77 +
 .../25_algorithms/copy_n/constrained.cc       |   72 +
 .../25_algorithms/count/constrained.cc        |   75 +
 .../25_algorithms/count_if/constrained.cc     |   73 +
 .../25_algorithms/equal/constrained.cc        |   96 +
 .../25_algorithms/equal_range/constrained.cc  |   69 +
 .../25_algorithms/fill/constrained.cc         |   92 +
 .../25_algorithms/fill_n/constrained.cc       |   98 +
 .../25_algorithms/find/constrained.cc         |   75 +
 .../25_algorithms/find_end/constrained.cc     |   98 +
 .../find_first_of/constrained.cc              |   83 +
 .../25_algorithms/find_if/constrained.cc      |   77 +
 .../25_algorithms/find_if_not/constrained.cc  |   77 +
 .../25_algorithms/for_each/constrained.cc     |   83 +
 .../25_algorithms/generate/constrained.cc     |   77 +
 .../25_algorithms/generate_n/constrained.cc   |   84 +
 .../25_algorithms/heap/constrained.cc         |  107 +
 .../25_algorithms/includes/constrained.cc     |   74 +
 .../inplace_merge/constrained.cc              |   69 +
 .../is_partitioned/constrained.cc             |   58 +
 .../is_permutation/constrained.cc             |   85 +
 .../25_algorithms/is_sorted/constrained.cc    |   67 +
 .../is_sorted_until/constrained.cc            |   72 +
 .../lexicographical_compare/constrained.cc    |  164 +
 .../25_algorithms/lower_bound/constrained.cc  |   66 +
 .../25_algorithms/max/constrained.cc          |   82 +
 .../25_algorithms/max_element/constrained.cc  |   60 +
 .../25_algorithms/merge/constrained.cc        |   75 +
 .../25_algorithms/min/constrained.cc          |   82 +
 .../25_algorithms/min_element/constrained.cc  |   60 +
 .../25_algorithms/minmax/constrained.cc       |   98 +
 .../minmax_element/constrained.cc             |   68 +
 .../25_algorithms/mismatch/constrained.cc     |   76 +
 .../25_algorithms/move/constrained.cc         |  203 +
 .../move_backward/constrained.cc              |  170 +
 .../next_permutation/constrained.cc           |   83 +
 .../25_algorithms/none_of/constrained.cc      |   88 +
 .../25_algorithms/nth_element/constrained.cc  |   76 +
 .../25_algorithms/partial_sort/constrained.cc |   84 +
 .../partial_sort_copy/constrained.cc          |   97 +
 .../25_algorithms/partition/constrained.cc    |   71 +
 .../partition_copy/constrained.cc             |   81 +
 .../partition_point/constrained.cc            |   67 +
 .../prev_permutation/constrained.cc           |   84 +
 .../25_algorithms/remove/constrained.cc       |   97 +
 .../25_algorithms/remove_copy/constrained.cc  |  109 +
 .../remove_copy_if/constrained.cc             |  113 +
 .../25_algorithms/remove_if/constrained.cc    |   97 +
 .../25_algorithms/replace/constrained.cc      |  104 +
 .../25_algorithms/replace_copy/constrained.cc |  109 +
 .../replace_copy_if/constrained.cc            |  118 +
 .../25_algorithms/replace_if/constrained.cc   |  109 +
 .../25_algorithms/reverse/constrained.cc      |   77 +
 .../25_algorithms/reverse_copy/constrained.cc |   74 +
 .../25_algorithms/rotate/constrained.cc       |   97 +
 .../25_algorithms/rotate_copy/constrained.cc  |   93 +
 .../25_algorithms/search/constrained.cc       |   88 +
 .../25_algorithms/search_n/constrained.cc     |   80 +
 .../set_difference/constrained.cc             |   87 +
 .../set_intersection/constrained.cc           |   88 +
 .../set_symmetric_difference/constrained.cc   |  123 +
 .../25_algorithms/set_union/constrained.cc    |   91 +
 .../25_algorithms/shuffle/constrained.cc      |   70 +
 .../25_algorithms/sort/constrained.cc         |   81 +
 .../stable_partition/constrained.cc           |   76 +
 .../25_algorithms/stable_sort/constrained.cc  |   70 +
 .../25_algorithms/swap_ranges/constrained.cc  |  124 +
 .../25_algorithms/transform/constrained.cc    |  148 +
 .../25_algorithms/unique/constrained.cc       |  143 +
 .../25_algorithms/unique_copy/constrained.cc  |  113 +
 .../25_algorithms/upper_bound/constrained.cc  |   66 +
 81 files changed, 10788 insertions(+)
 create mode 100644 libstdc++-v3/include/bits/ranges_algo.h
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/adjacent_find/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/all_of/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/any_of/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/binary_search/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/copy_backward/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/copy_if/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/copy_n/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/count/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/count_if/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/equal_range/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/fill/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/fill_n/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_end/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_first_of/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_if/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_if_not/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/for_each/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/generate/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/generate_n/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/includes/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/is_partitioned/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/is_sorted/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/is_sorted_until/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/lower_bound/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/max/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/max_element/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/merge/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/min/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/min_element/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/mismatch/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/move/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/move_backward/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/next_permutation/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/none_of/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/partition/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/partition_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/partition_point/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/prev_permutation/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/remove/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/remove_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/remove_copy_if/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/remove_if/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/replace/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/replace_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/replace_copy_if/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/replace_if/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/reverse/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/reverse_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/rotate/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/rotate_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/search/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/search_n/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/set_difference/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/set_intersection/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/set_symmetric_difference/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/set_union/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/swap_ranges/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/transform/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/unique/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/unique_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/upper_bound/constrained.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 89835759069..1d342cecbcc 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -157,6 +157,7 @@ bits_headers = \
 	${bits_srcdir}/random.tcc \
 	${bits_srcdir}/range_access.h \
 	${bits_srcdir}/range_cmp.h \
+	${bits_srcdir}/ranges_algo.h \
 	${bits_srcdir}/refwrap.h \
 	${bits_srcdir}/regex.h \
 	${bits_srcdir}/regex.tcc \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 123d24bb1c6..c735d67a5d3 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -502,6 +502,7 @@ bits_headers = \
 	${bits_srcdir}/random.tcc \
 	${bits_srcdir}/range_access.h \
 	${bits_srcdir}/range_cmp.h \
+	${bits_srcdir}/ranges_algo.h \
 	${bits_srcdir}/refwrap.h \
 	${bits_srcdir}/regex.h \
 	${bits_srcdir}/regex.tcc \
diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
new file mode 100644
index 00000000000..2e177ce7f7a
--- /dev/null
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -0,0 +1,3640 @@
+// Core algorithmic facilities -*- C++ -*-
+
+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file bits/ranges_algo.h
+ *  This is an internal header file, included by other library headers.
+ *  Do not attempt to use it directly. @headername{algorithm}
+ */
+
+#ifndef _RANGES_ALGO_H
+#define _RANGES_ALGO_H 1
+
+#if __cplusplus > 201703L
+
+#include <compare>
+#include <cmath>
+#include <iterator>
+// #include <bits/range_concepts.h>
+#include <ranges>
+#include <bits/invoke.h>
+#include <bits/cpp_type_traits.h> // __is_byte
+#include <bits/random.h> // concept uniform_random_bit_generator
+
+#if __cpp_lib_concepts
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+namespace ranges
+{
+  namespace __detail
+  {
+    template<typename _Tp>
+    constexpr inline bool __is_normal_iterator = false;
+
+    template<typename _Iterator, typename _Container>
+    constexpr inline bool
+      __is_normal_iterator<__gnu_cxx::__normal_iterator<_Iterator, _Container>>
+      = true;
+
+    template<typename _Tp>
+    constexpr inline bool __is_reverse_iterator = false;
+
+    template<typename _Iterator>
+    constexpr inline bool
+      __is_reverse_iterator<reverse_iterator<_Iterator>> = true;
+
+    template<typename _Tp>
+    constexpr inline bool __is_move_iterator = false;
+
+    template<typename _Iterator>
+    constexpr inline bool
+      __is_move_iterator<move_iterator<_Iterator>> = true;
+
+    template<typename _Comp, typename _Proj>
+    constexpr auto
+    __make_comp_proj(_Comp& __comp, _Proj& __proj)
+    {
+      return [&] (auto&& __lhs, auto&& __rhs) -> bool {
+	using _TL = decltype(__lhs);
+	using _TR = decltype(__rhs);
+	return std::__invoke(__comp,
+			     std::__invoke(__proj, std::forward<_TL>(__lhs)),
+			     std::__invoke(__proj, std::forward<_TR>(__rhs)));
+      };
+    }
+
+    template<typename _Pred, typename _Proj>
+    constexpr auto
+    __make_pred_proj(_Pred& __pred, _Proj& __proj)
+    {
+      return [&] <typename _Tp> (_Tp&& __arg) -> bool {
+	return std::__invoke(__pred,
+			     std::__invoke(__proj, std::forward<_Tp>(__arg)));
+      };
+    }
+  }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr bool
+    all_of(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (!(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  return false;
+      return true;
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    constexpr bool
+    all_of(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::all_of(ranges::begin(__r), ranges::end(__r),
+			    std::move(__pred), std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr bool
+    any_of(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  return true;
+      return false;
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    constexpr bool
+    any_of(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::any_of(ranges::begin(__r), ranges::end(__r),
+			    std::move(__pred), std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr bool
+    none_of(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  return false;
+      return true;
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    constexpr bool
+    none_of(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::none_of(ranges::begin(__r), ranges::end(__r),
+			    std::move(__pred), std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Fp>
+    struct for_each_result
+    {
+      [[no_unique_address]] _Iter in;
+      [[no_unique_address]] _Fp fun;
+
+      template<typename _Iter2, typename _F2p>
+	requires convertible_to<const _Iter&, _Iter2>
+	  && convertible_to<const _Fp&, _F2p>
+	operator for_each_result<_Iter2, _F2p>() const &
+	{ return {in, fun}; }
+
+      template<typename _Iter2, typename _F2p>
+	requires convertible_to<_Iter, _Iter2> && convertible_to<_Fp, _F2p>
+	operator for_each_result<_Iter2, _F2p>() &&
+	{ return {std::move(in), std::move(fun)}; }
+    };
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirectly_unary_invocable<projected<_Iter, _Proj>> _Fun>
+    constexpr for_each_result<_Iter, _Fun>
+    for_each(_Iter __first, _Sent __last, _Fun __f, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	std::__invoke(__f, std::__invoke(__proj, *__first));
+      return { std::move(__first), std::move(__f) };
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirectly_unary_invocable<projected<iterator_t<_Range>, _Proj>>
+	     _Fun>
+    constexpr for_each_result<safe_iterator_t<_Range>, _Fun>
+    for_each(_Range&& __r, _Fun __f, _Proj __proj = {})
+    {
+      return ranges::for_each(ranges::begin(__r), ranges::end(__r),
+			      std::move(__f), std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp,
+	   typename _Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to,
+				       projected<_Iter, _Proj>, const _Tp*>
+    constexpr _Iter
+    find(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {})
+    {
+      while (__first != __last
+	  && !(std::__invoke(__proj, *__first) == __value))
+	++__first;
+      return __first;
+    }
+
+  template<input_range _Range, typename _Tp, typename _Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to,
+				       projected<iterator_t<_Range>, _Proj>,
+				       const _Tp*>
+    constexpr safe_iterator_t<_Range>
+    find(_Range&& __r, const _Tp& __value, _Proj __proj = {})
+    {
+      return ranges::find(ranges::begin(__r), ranges::end(__r), __value,
+			  std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr _Iter
+    find_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      while (__first != __last
+	  && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	++__first;
+      return __first;
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+	     _Pred>
+    constexpr safe_iterator_t<_Range>
+    find_if(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::find_if(ranges::begin(__r), ranges::end(__r),
+			     std::move(__pred), std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr _Iter
+    find_if_not(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      while (__first != __last
+	  && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	++__first;
+      return __first;
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+	     _Pred>
+    constexpr safe_iterator_t<_Range>
+    find_if_not(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::find_if_not(ranges::begin(__r), ranges::end(__r),
+				 std::move(__pred), std::move(__proj));
+    }
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   forward_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 _Iter1
+    find_first_of(_Iter1 __first1, _Sent1 __last1,
+		  _Iter2 __first2, _Sent2 __last2,
+		  _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      for (; __first1 != __last1; ++__first1)
+	for (auto __iter = __first2; __iter != __last2; ++__iter)
+	  if (std::__invoke(__pred,
+			    std::__invoke(__proj1, *__first1),
+			    std::__invoke(__proj2, *__iter)))
+	    return __first1;
+      return __first1;
+    }
+
+  template<input_range _Range1, forward_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 safe_iterator_t<_Range1>
+    find_first_of(_Range1&& __r1, _Range2&& __r2,
+		  _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::find_first_of(ranges::begin(__r1), ranges::end(__r1),
+				   ranges::begin(__r2), ranges::end(__r2),
+				   std::move(__pred),
+				   std::move(__proj1), std::move(__proj2));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, typename _Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to,
+				       projected<_Iter, _Proj>,
+				       const _Tp*>
+    constexpr iter_difference_t<_Iter>
+    count(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {})
+    {
+      iter_difference_t<_Iter> __n = 0;
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__proj, *__first) == __value)
+	  ++__n;
+      return __n;
+    }
+
+  template<input_range _Range, typename _Tp, typename _Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to,
+				       projected<iterator_t<_Range>, _Proj>,
+				       const _Tp*>
+    constexpr range_difference_t<_Range>
+    count(_Range&& __r, const _Tp& __value, _Proj __proj = {})
+    {
+      return ranges::count(ranges::begin(__r), ranges::end(__r),
+			   __value, std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr iter_difference_t<_Iter>
+    count_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      iter_difference_t<_Iter> __n = 0;
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  ++__n;
+      return __n;
+    }
+
+  template<input_range _Range,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    constexpr range_difference_t<_Range>
+    count_if(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::count_if(ranges::begin(__r), ranges::end(__r),
+			      std::move(__pred), std::move(__proj));
+    }
+
+  template<typename _Iter1, typename _Iter2>
+    struct mismatch_result
+    {
+      [[no_unique_address]] _Iter1 in1;
+      [[no_unique_address]] _Iter2 in2;
+
+      template<typename _IIter1, typename _IIter2>
+	requires convertible_to<const _Iter1&, _IIter1>
+	  && convertible_to<const _Iter2&, _IIter2>
+	operator mismatch_result<_IIter1, _IIter2>() const &
+	{ return {in1, in2}; }
+
+      template<typename _IIter1, typename _IIter2>
+	requires convertible_to<_Iter1, _IIter1>
+	  && convertible_to<_Iter2, _IIter2>
+	operator mismatch_result<_IIter1, _IIter2>() &&
+	{ return {std::move(in1), std::move(in2)}; }
+    };
+
+  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 mismatch_result<_Iter1, _Iter2>
+    mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	     _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      while (__first1 != __last1 && __first2 != __last2
+	     && (bool)std::__invoke(__pred,
+				    std::__invoke(__proj1, *__first1),
+				    std::__invoke(__proj2, *__first2)))
+      {
+	++__first1;
+	++__first2;
+      }
+      return { std::move(__first1), std::move(__first2) };
+    }
+
+  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 mismatch_result<iterator_t<_Range1>, iterator_t<_Range2>>
+    mismatch(_Range1&& __r1, _Range2&& __r2,
+	     _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::mismatch(ranges::begin(__r1), ranges::end(__r1),
+			      ranges::begin(__r2), ranges::end(__r2),
+			      std::move(__pred),
+			      std::move(__proj1), std::move(__proj2));
+    }
+
+  template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   forward_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 subrange<_Iter1>
+    search(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	   _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      if (__first1 == __last1 || __first2 == __last2)
+	return {__first1, __first1};
+
+      for (;;)
+	{
+	  for (;;)
+	    {
+	      if (__first1 == __last1)
+		return {__first1, __first1};
+	      if (std::__invoke(__pred,
+				std::__invoke(__proj1, *__first1),
+				std::__invoke(__proj2, *__first2)))
+		break;
+	      ++__first1;
+	    }
+	  auto __cur1 = __first1;
+	  auto __cur2 = __first2;
+	  for (;;)
+	    {
+	      if (++__cur2 == __last2)
+		return {__first1, ++__cur1};
+	      if (++__cur1 == __last1)
+		return {__cur1, __cur1};
+	      if (!(bool)std::__invoke(__pred,
+				       std::__invoke(__proj1, *__cur1),
+				       std::__invoke(__proj2, *__cur2)))
+		{
+		  ++__first1;
+		  break;
+		}
+	    }
+	}
+    }
+
+  template<forward_range _Range1, forward_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 safe_subrange_t<_Range1>
+    search(_Range1&& __r1, _Range2&& __r2,
+	   _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::search(ranges::begin(__r1), ranges::end(__r1),
+			    ranges::begin(__r2), ranges::end(__r2),
+			    std::move(__pred),
+			    std::move(__proj1), std::move(__proj2));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp,
+	   typename _Pred = ranges::equal_to, typename _Proj = identity>
+    requires indirectly_comparable<_Iter, const _Tp*, _Pred, _Proj>
+    constexpr subrange<_Iter>
+    search_n(_Iter __first, _Sent __last, iter_difference_t<_Iter> __count,
+	     const _Tp& __value, _Pred __pred = {}, _Proj __proj = {})
+    {
+      if (__count <= 0)
+	return {__first, __first};
+
+      auto __value_comp = [&] <typename _Rp> (_Rp&& __arg) {
+	  return std::__invoke(__pred, std::forward<_Rp>(__arg), __value);
+      };
+      if (__count == 1)
+	{
+	  __first = ranges::find_if(std::move(__first), __last,
+				    std::move(__value_comp), std::move(__proj));
+	  if (__first == __last)
+	    return {__first, __first};
+	  else
+	    {
+	      auto __end = __first;
+	      return {__first, ++__end};
+	    }
+	}
+
+      if constexpr (sized_sentinel_for<_Sent, _Iter>)
+	{
+	  auto __tail_size = __last - __first;
+	  auto __remainder = __count;
+
+	  while (__remainder <= __tail_size)
+	    {
+	      __first += __remainder;
+	      __tail_size -= __remainder;
+	      auto __backtrack = __first;
+	      while (__value_comp(std::__invoke(__proj, *--__backtrack)))
+		{
+		  if (--__remainder == 0)
+		    return {__first - __count, __first};
+		}
+	    }
+	  auto __i = __first + __tail_size;
+	  return {__i, __i};
+	}
+      else
+	{
+	  __first = ranges::find_if(__first, __last, __value_comp, __proj);
+	  while (__first != __last)
+	    {
+	      auto __n = __count;
+	      auto __i = __first;
+	      ++__i;
+	      while (__i != __last && __n != 1
+		     && __value_comp(std::__invoke(__proj, *__i)))
+		{
+		  ++__i;
+		  --__n;
+		}
+	      if (__n == 1)
+		return {__first, __i};
+	      if (__i == __last)
+		return {__i, __i};
+	      __first = ranges::find_if(++__i, __last, __value_comp, __proj);
+	    }
+	  return {__first, __first};
+	}
+    }
+
+  template<forward_range _Range, typename _Tp,
+	   typename _Pred = ranges::equal_to, typename _Proj = identity>
+    requires indirectly_comparable<iterator_t<_Range>, const _Tp*, _Pred, _Proj>
+    constexpr safe_subrange_t<_Range>
+    search_n(_Range&& __r, range_difference_t<_Range> __count,
+	     const _Tp& __value, _Pred __pred = {}, _Proj __proj = {})
+    {
+      return ranges::search_n(ranges::begin(__r), ranges::end(__r),
+			      std::move(__count), __value,
+			      std::move(__pred), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   forward_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 subrange<_Iter1>
+    __find_end(_Iter1 __first1, _Sent1 __last1,
+	       _Iter2 __first2, _Sent2 __last2,
+	       _Pred __pred, _Proj1 __proj1, _Proj2 __proj2)
+    {
+      auto __i = ranges::next(__first1, __last1);
+      if (__first2 == __last2)
+	return {__i, __i};
+
+      auto __result_begin = __i;
+      auto __result_end = __i;
+      for (;;)
+	{
+	  auto __new_range = ranges::search(__first1, __last1,
+					    __first2, __last2,
+					    __pred, __proj1, __proj2);
+	  auto __new_result_begin = ranges::begin(__new_range);
+	  auto __new_result_end = ranges::end(__new_range);
+	  if (__new_result_begin == __last1)
+	    return {__result_begin, __result_end};
+	  else
+	    {
+	      __result_begin = __new_result_begin;
+	      __result_end = __new_result_end;
+	      __first1 = __result_begin;
+	      ++__first1;
+	    }
+	}
+    }
+
+  template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   forward_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 subrange<_Iter1>
+    find_end(_Iter1 __first1, _Sent1 __last1,
+	     _Iter2 __first2, _Sent2 __last2,
+	     _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      if constexpr (bidirectional_iterator<_Iter1>
+		    && bidirectional_iterator<_Iter2>)
+	{
+	  auto __i1 = ranges::next(__first1, __last1);
+	  auto __i2 = ranges::next(__first2, __last2);
+	  auto __rresult
+	    = ranges::search(reverse_iterator<_Iter1>{__i1},
+			     reverse_iterator<_Iter1>{__first1},
+			     reverse_iterator<_Iter2>{__i2},
+			     reverse_iterator<_Iter2>{__first2},
+			     std::move(__pred),
+			     std::move(__proj1), std::move(__proj2));
+	  auto __result_first = ranges::end(__rresult).base();
+	  auto __result_last = ranges::begin(__rresult).base();
+	  if (__result_last == __first1)
+	    return {__i1, __i1};
+	  else
+	    return {__result_first, __result_last};
+	}
+      else
+	return ranges::__find_end(__first1, __last1, __first2, __last2,
+				  std::move(__pred),
+				  std::move(__proj1), std::move(__proj2));
+    }
+
+  template<forward_range _Range1, forward_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 safe_subrange_t<_Range1>
+    find_end(_Range1&& __r1, _Range2&& __r2,
+	     _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::find_end(ranges::begin(__r1), ranges::end(__r1),
+			      ranges::begin(__r2), ranges::end(__r2),
+			      std::move(__pred),
+			      std::move(__proj1), std::move(__proj2));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_binary_predicate<projected<_Iter, _Proj>,
+				     projected<_Iter, _Proj>> _Pred
+	     = ranges::equal_to>
+    constexpr _Iter
+    adjacent_find(_Iter __first, _Sent __last,
+		  _Pred __pred = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return __first;
+      auto __next = __first;
+      for (; ++__next != __last; __first = __next)
+	{
+	  if (std::__invoke(__pred,
+			    std::__invoke(__proj, *__first),
+			    std::__invoke(__proj, *__next)))
+	    return __first;
+	}
+      return __next;
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_binary_predicate<
+	     projected<iterator_t<_Range>, _Proj>,
+	     projected<iterator_t<_Range>, _Proj>> _Pred = ranges::equal_to>
+    constexpr safe_iterator_t<_Range>
+    adjacent_find(_Range&& __r, _Pred __pred = {}, _Proj __proj = {})
+    {
+      return ranges::adjacent_find(ranges::begin(__r), ranges::end(__r),
+				   std::move(__pred), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   forward_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   typename _Proj1 = identity, typename _Proj2 = identity,
+	   indirect_equivalence_relation<projected<_Iter1, _Proj1>,
+					 projected<_Iter2, _Proj2>> _Pred
+	     = ranges::equal_to>
+    constexpr bool
+    is_permutation(_Iter1 __first1, _Sent1 __last1,
+		   _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
+		   _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      constexpr bool __sized_iters
+	= (sized_sentinel_for<_Sent1, _Iter1>
+	   && sized_sentinel_for<_Sent2, _Iter2>);
+      if constexpr (__sized_iters)
+	{
+	  auto __d1 = ranges::distance(__first1, __last1);
+	  auto __d2 = ranges::distance(__first2, __last2);
+	  if (__d1 != __d2)
+	    return false;
+	}
+
+      // Efficiently compare identical prefixes:  O(N) if sequences
+      // have the same elements in the same order.
+      for (; __first1 != __last1 && __first2 != __last2;
+	   ++__first1, (void)++__first2)
+	if (!(bool)std::__invoke(__pred,
+				 std::__invoke(__proj1, *__first1),
+				 std::__invoke(__proj2, *__first2)))
+	    break;
+
+      if constexpr (__sized_iters)
+	{
+	  if (__first1 == __last1)
+	    return true;
+	}
+      else
+	{
+	  auto __d1 = ranges::distance(__first1, __last1);
+	  auto __d2 = ranges::distance(__first2, __last2);
+	  if (__d1 == 0 && __d2 == 0)
+	    return true;
+	  if (__d1 != __d2)
+	    return false;
+	}
+
+      for (auto __scan = __first1; __scan != __last1; ++__scan)
+	{
+	  auto __proj_scan = std::__invoke(__proj1, *__scan);
+	  auto __comp_scan = [&] <typename _Tp> (_Tp&& __arg) {
+	    return std::__invoke(__pred, __proj_scan,
+				 std::forward<_Tp>(__arg));
+	  };
+	  if (__scan != ranges::find_if(__first1, __scan,
+					__comp_scan, __proj1))
+	    continue; // We've seen this one before.
+
+	  auto __matches = ranges::count_if(__first2, __last2,
+					    __comp_scan, __proj2);
+	  if (__matches == 0
+	      || ranges::count_if(__scan, __last1,
+				  __comp_scan, __proj1) != __matches)
+	    return false;
+	}
+      return true;
+    }
+
+  template<forward_range _Range1, forward_range _Range2,
+	   typename _Proj1 = identity, typename _Proj2 = identity,
+	   indirect_equivalence_relation<
+	     projected<iterator_t<_Range1>, _Proj1>,
+	     projected<iterator_t<_Range2>, _Proj2>> _Pred = ranges::equal_to>
+    constexpr bool
+    is_permutation(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
+		   _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::is_permutation(ranges::begin(__r1), ranges::end(__r1),
+				    ranges::begin(__r2), ranges::end(__r2),
+				    std::move(__pred),
+				    std::move(__proj1), std::move(__proj2));
+    }
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   typename _Pred, typename _Proj1, typename _Proj2>
+    requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
+    constexpr bool
+    __equal(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	    _Pred __pred, _Proj1 __proj1, _Proj2 __proj2)
+    {
+      // TODO: implement more specializations to at least have parity with
+      // std::equal.
+      constexpr bool __sized_iters
+	= (sized_sentinel_for<_Sent1, _Iter1>
+	   && sized_sentinel_for<_Sent2, _Iter2>);
+      if constexpr (__sized_iters)
+	{
+	  auto __d1 = ranges::distance(__first1, __last1);
+	  auto __d2 = ranges::distance(__first2, __last2);
+	  if (__d1 != __d2)
+	    return false;
+
+	  using _ValueType1 = iter_value_t<_Iter1>;
+	  using _ValueType2 = iter_value_t<_Iter2>;
+	  constexpr bool __use_memcmp
+	    = ((is_integral_v<_ValueType1> || is_pointer_v<_ValueType1>)
+	       && is_same_v<_ValueType1, _ValueType2>
+	       && is_pointer_v<_Iter1>
+	       && is_pointer_v<_Iter2>
+	       && is_same_v<_Pred, ranges::equal_to>
+	       && is_same_v<_Proj1, identity>
+	       && is_same_v<_Proj2, identity>);
+	  if constexpr (__use_memcmp)
+	    {
+	      if (const size_t __len = (__last1 - __first1))
+		return !std::__memcmp(__first1, __first2, __len);
+	      return true;
+	    }
+	  else
+	    {
+	      for (; __first1 != __last1; ++__first1, (void)++__first2)
+		if (!(bool)std::__invoke(__pred,
+					 std::__invoke(__proj1, *__first1),
+					 std::__invoke(__proj2, *__first2)))
+		  return false;
+	      return true;
+	    }
+	}
+      else
+	{
+	  for (; __first1 != __last1 && __first2 != __last2;
+	       ++__first1, (void)++__first2)
+	    if (!(bool)std::__invoke(__pred,
+				     std::__invoke(__proj1, *__first1),
+				     std::__invoke(__proj2, *__first2)))
+	      return false;
+	  return __first1 == __last1 && __first2 == __last2;
+	}
+    }
+
+  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
+    equal(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	  _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::__equal(std::__niter_base(std::move(__first1)),
+			     std::__niter_base(std::move(__last1)),
+			     std::__niter_base(std::move(__first2)),
+			     std::__niter_base(std::move(__last2)),
+			     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
+    equal(_Range1&& __r1, _Range2&& __r2,
+	  _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::equal(ranges::begin(__r1), ranges::end(__r1),
+			   ranges::begin(__r2), ranges::end(__r2),
+			   std::move(__pred),
+			   std::move(__proj1), std::move(__proj2));
+    }
+
+  template<typename _Iter, typename _Out>
+    struct copy_result
+    {
+      [[no_unique_address]] _Iter in;
+      [[no_unique_address]] _Out out;
+
+      template<typename _Iter2, typename _Out2>
+	requires convertible_to<const _Iter&, _Iter2>
+	  && convertible_to<const _Out&, _Out2>
+	operator copy_result<_Iter2, _Out2>() const &
+	{ return {in, out}; }
+
+      template<typename _Iter2, typename _Out2>
+	requires convertible_to<_Iter, _Iter2>
+	  && convertible_to<_Out, _Out2>
+	operator copy_result<_Iter2, _Out2>() &&
+	{ return {std::move(in), std::move(out)}; }
+    };
+
+  template<typename _Iter, typename _Out>
+    using move_result = copy_result<_Iter, _Out>;
+
+  template<typename _Iter1, typename _Iter2>
+    using move_backward_result = copy_result<_Iter1, _Iter2>;
+
+  template<typename _Iter1, typename _Iter2>
+    using copy_backward_result = copy_result<_Iter1, _Iter2>;
+
+  template<bool _IsMove,
+	   bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   bidirectional_iterator _Out>
+    requires (_IsMove
+	      ? indirectly_movable<_Iter, _Out>
+	      : indirectly_copyable<_Iter, _Out>)
+    constexpr conditional_t<_IsMove,
+			    move_backward_result<_Iter, _Out>,
+			    copy_backward_result<_Iter, _Out>>
+    __copy_or_move_backward(_Iter __first, _Sent __last, _Out __result);
+
+  template<bool _IsMove,
+	   input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out>
+    requires (_IsMove
+	      ? indirectly_movable<_Iter, _Out>
+	      : indirectly_copyable<_Iter, _Out>)
+    constexpr conditional_t<_IsMove,
+			    move_result<_Iter, _Out>,
+			    copy_result<_Iter, _Out>>
+    __copy_or_move(_Iter __first, _Sent __last, _Out __result)
+    {
+      // TODO: implement more specializations to be at least on par with
+      // std::copy/std::move.
+      constexpr bool __normal_iterator_p
+	= (__detail::__is_normal_iterator<_Iter>
+	   || __detail::__is_normal_iterator<_Out>);
+      constexpr bool __reverse_p
+	= (__detail::__is_reverse_iterator<_Iter>
+	   && __detail::__is_reverse_iterator<_Out>);
+      constexpr bool __move_iterator_p = __detail::__is_move_iterator<_Iter>;
+      if constexpr (__move_iterator_p)
+	{
+	  auto [__in, __out]
+	    = ranges::__copy_or_move<true>(std::move(__first).base(),
+					   std::move(__last).base(),
+					   std::move(__result));
+	  return {move_iterator{std::move(__in)}, std::move(__out)};
+	}
+      else if constexpr (__reverse_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move_backward<_IsMove>(__last.base(),
+						       __first.base(),
+						       __result.base());
+	  return {reverse_iterator{std::move(__in)},
+		  reverse_iterator{std::move(__out)}};
+	}
+      else if constexpr (__normal_iterator_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move<_IsMove>(std::__niter_base(__first),
+					      std::__niter_base(__last),
+					      std::__niter_base(__result));
+	  return {std::__niter_wrap(__first, std::move(__in)),
+		  std::__niter_wrap(__result, std::move(__out))};
+	}
+      else if constexpr (sized_sentinel_for<_Sent, _Iter>)
+	{
+	  using _ValueTypeI = iter_value_t<_Iter>;
+	  using _ValueTypeO = iter_value_t<_Out>;
+	  constexpr bool __use_memmove
+	    = (is_trivially_copyable_v<_ValueTypeI>
+	       && is_same_v<_ValueTypeI, _ValueTypeO>
+	       && is_pointer_v<_Iter>
+	       && is_pointer_v<_Out>);
+
+	  if constexpr (__use_memmove)
+	    {
+	      static_assert(_IsMove
+			    ? is_move_assignable_v<_ValueTypeI>
+			    : is_copy_assignable_v<_ValueTypeI>);
+	      auto __num = __last - __first;
+	      if (__num)
+		std::__memmove<_IsMove>(__result, __first, __num);
+	      return {__first + __num, __result + __num};
+	    }
+	  else
+	    {
+	      for (auto __n = __last - __first; __n > 0; --__n)
+		{
+		  if constexpr (_IsMove)
+		    *__result = std::move(*__first);
+		  else
+		    *__result = *__first;
+		  ++__first;
+		  ++__result;
+		}
+	      return {std::move(__first), std::move(__result)};
+	    }
+	}
+      else
+	{
+	  while (__first != __last)
+	    {
+	      if constexpr (_IsMove)
+		*__result = std::move(*__first);
+	      else
+		*__result = *__first;
+	      ++__first;
+	      ++__result;
+	    }
+	  return {std::move(__first), std::move(__result)};
+	}
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out>
+    requires indirectly_copyable<_Iter, _Out>
+    constexpr copy_result<_Iter, _Out>
+    copy(_Iter __first, _Sent __last, _Out __result)
+    {
+      return ranges::__copy_or_move<false>(std::move(__first),
+					   std::move(__last),
+					   std::move(__result));
+    }
+
+  template<input_range _Range, weakly_incrementable _Out>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+    constexpr copy_result<safe_iterator_t<_Range>, _Out>
+    copy(_Range&& __r, _Out __result)
+    {
+      return ranges::copy(ranges::begin(__r), ranges::end(__r),
+			  std::move(__result));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out>
+    requires indirectly_movable<_Iter, _Out>
+    constexpr move_result<_Iter, _Out>
+    move(_Iter __first, _Sent __last, _Out __result)
+    {
+      return ranges::__copy_or_move<true>(std::move(__first),
+					  std::move(__last),
+					  std::move(__result));
+    }
+
+  template<input_range _Range, weakly_incrementable _Out>
+    requires indirectly_movable<iterator_t<_Range>, _Out>
+    constexpr move_result<safe_iterator_t<_Range>, _Out>
+    move(_Range&& __r, _Out __result)
+    {
+      return ranges::move(ranges::begin(__r), ranges::end(__r),
+			  std::move(__result));
+    }
+
+  template<bool _IsMove,
+	   bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   bidirectional_iterator _Out>
+    requires (_IsMove
+	      ? indirectly_movable<_Iter, _Out>
+	      : indirectly_copyable<_Iter, _Out>)
+    constexpr conditional_t<_IsMove,
+			    move_backward_result<_Iter, _Out>,
+			    copy_backward_result<_Iter, _Out>>
+    __copy_or_move_backward(_Iter __first, _Sent __last, _Out __result)
+    {
+      // TODO: implement more specializations to be at least on par with
+      // std::copy_backward/std::move_backward.
+      constexpr bool __normal_iterator_p
+	= (__detail::__is_normal_iterator<_Iter>
+	   || __detail::__is_normal_iterator<_Out>);
+      constexpr bool __reverse_p
+	= (__detail::__is_reverse_iterator<_Iter>
+	   && __detail::__is_reverse_iterator<_Out>);
+      if constexpr (__reverse_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move<_IsMove>(__last.base(),
+					      __first.base(),
+					      __result.base());
+	  return {reverse_iterator{std::move(__in)},
+		  reverse_iterator{std::move(__out)}};
+	}
+      else if constexpr (__normal_iterator_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move_backward<_IsMove>
+	      (std::__niter_base(__first),
+	       std::__niter_base(__last),
+	       std::__niter_base(__result));
+	  return {std::__niter_wrap(__first, std::move(__in)),
+		  std::__niter_wrap(__result, std::move(__out))};
+	}
+      else if constexpr (sized_sentinel_for<_Sent, _Iter>)
+	{
+	  using _ValueTypeI = iter_value_t<_Iter>;
+	  using _ValueTypeO = iter_value_t<_Out>;
+	  constexpr bool __use_memmove
+	    = (is_trivially_copyable_v<_ValueTypeI>
+	       && is_same_v<_ValueTypeI, _ValueTypeO>
+	       && is_pointer_v<_Iter>
+	       && is_pointer_v<_Out>);
+	  if constexpr (__use_memmove)
+	    {
+	      static_assert(_IsMove
+			    ? is_move_assignable_v<_ValueTypeI>
+			    : is_copy_assignable_v<_ValueTypeI>);
+	      auto __num = __last - __first;
+	      if (__num)
+		std::__memmove<_IsMove>(__result - __num, __first, __num);
+	      return {__first + __num, __result - __num};
+	    }
+	  else
+	    {
+	      auto __lasti = ranges::next(__first, __last);
+	      auto __tail = __lasti;
+
+	      for (auto __n = __last - __first; __n > 0; --__n)
+		{
+		  --__tail;
+		  --__result;
+		  if constexpr (_IsMove)
+		    *__result = std::move(*__tail);
+		  else
+		    *__result = *__tail;
+		}
+	      return {std::move(__lasti), std::move(__result)};
+	    }
+	}
+      else
+	{
+	  auto __lasti = ranges::next(__first, __last);
+	  auto __tail = __lasti;
+
+	  while (__first != __tail)
+	    {
+	      --__tail;
+	      --__result;
+	      if constexpr (_IsMove)
+		*__result = std::move(*__tail);
+	      else
+		*__result = *__tail;
+	    }
+	  return {std::move(__lasti), std::move(__result)};
+	}
+    }
+
+  template<bidirectional_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   bidirectional_iterator _Iter2>
+    requires indirectly_copyable<_Iter1, _Iter2>
+    constexpr copy_backward_result<_Iter1, _Iter2>
+    copy_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result)
+    {
+      return ranges::__copy_or_move_backward<false>(std::move(__first),
+						    std::move(__last),
+						    std::move(__result));
+    }
+
+  template<bidirectional_range _Range, bidirectional_iterator _Iter>
+    requires indirectly_copyable<iterator_t<_Range>, _Iter>
+    constexpr copy_backward_result<safe_iterator_t<_Range>, _Iter>
+    copy_backward(_Range&& __r, _Iter __result)
+    {
+      return ranges::copy_backward(ranges::begin(__r), ranges::end(__r),
+				   std::move(__result));
+    }
+
+  template<bidirectional_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   bidirectional_iterator _Iter2>
+    requires indirectly_movable<_Iter1, _Iter2>
+    constexpr move_backward_result<_Iter1, _Iter2>
+    move_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result)
+    {
+      return ranges::__copy_or_move_backward<true>(std::move(__first),
+						   std::move(__last),
+						   std::move(__result));
+    }
+
+  template<bidirectional_range _Range, bidirectional_iterator _Iter>
+    requires indirectly_movable<iterator_t<_Range>, _Iter>
+    constexpr move_backward_result<safe_iterator_t<_Range>, _Iter>
+    move_backward(_Range&& __r, _Iter __result)
+    {
+      return ranges::move_backward(ranges::begin(__r), ranges::end(__r),
+				   std::move(__result));
+    }
+
+  template<typename _Iter, typename _Out>
+    using copy_n_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, weakly_incrementable _Out>
+    requires indirectly_copyable<_Iter, _Out>
+    constexpr copy_n_result<_Iter, _Out>
+    copy_n(_Iter __first, iter_difference_t<_Iter> __n, _Out __result)
+    {
+      if constexpr (random_access_iterator<_Iter>)
+	return ranges::copy(__first, __first + __n, std::move(__result));
+      else
+	{
+	  for (; __n > 0; --__n, (void)++__result, (void)++__first)
+	    *__result = *__first;
+	  return {std::move(__first), std::move(__result)};
+	}
+    }
+
+  template<typename _Iter, typename _Out>
+    using copy_if_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out, typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    requires indirectly_copyable<_Iter, _Out>
+    constexpr copy_if_result<_Iter, _Out>
+    copy_if(_Iter __first, _Sent __last, _Out __result,
+	    _Pred __pred, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  {
+	    *__result = *__first;
+	    ++__result;
+	  }
+      return {std::move(__first), std::move(__result)};
+    }
+
+  template<input_range _Range, weakly_incrementable _Out,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+    constexpr copy_if_result<safe_iterator_t<_Range>, _Out>
+    copy_if(_Range&& __r, _Out __result, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::copy_if(ranges::begin(__r), ranges::end(__r),
+			     std::move(__result),
+			     std::move(__pred), std::move(__proj));
+    }
+
+  template<typename _Iter1, typename _Iter2>
+    using swap_ranges_result = mismatch_result<_Iter1, _Iter2>;
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2>
+    requires indirectly_swappable<_Iter1, _Iter2>
+    constexpr swap_ranges_result<_Iter1, _Iter2>
+    swap_ranges(_Iter1 __first1, _Sent1 __last1,
+		_Iter2 __first2, _Sent2 __last2)
+    {
+      for (; __first1 != __last1 && __first2 != __last2;
+	   ++__first1, (void)++__first2)
+	ranges::iter_swap(__first1, __first2);
+      return {std::move(__first1), std::move(__first2)};
+    }
+
+  template<input_range _Range1, input_range _Range2>
+    requires indirectly_swappable<iterator_t<_Range1>, iterator_t<_Range2>>
+    constexpr swap_ranges_result<safe_iterator_t<_Range1>,
+				 safe_iterator_t<_Range2>>
+    swap_ranges(_Range1&& __r1, _Range2&& __r2)
+    {
+      return ranges::swap_ranges(ranges::begin(__r1), ranges::end(__r1),
+				 ranges::begin(__r2), ranges::end(__r2));
+    }
+
+  template<typename _Iter, typename _Out>
+    using unary_transform_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out,
+	   copy_constructible _Fp, typename _Proj = identity>
+    requires writable<_Out, indirect_result_t<_Fp&, projected<_Iter, _Proj>>>
+    constexpr unary_transform_result<_Iter, _Out>
+    transform(_Iter __first1, _Sent __last1, _Out __result,
+	      _Fp __op, _Proj __proj = {})
+    {
+      for (; __first1 != __last1; ++__first1, (void)++__result)
+	*__result = std::__invoke(__op, std::__invoke(__proj, *__first1));
+      return {std::move(__first1), std::move(__result)};
+    }
+
+  template<input_range _Range, weakly_incrementable _Out,
+	   copy_constructible _Fp, typename _Proj = identity>
+    requires writable<_Out,
+		      indirect_result_t<_Fp&, projected<iterator_t<_Range>,
+						      _Proj>>>
+    constexpr unary_transform_result<safe_iterator_t<_Range>, _Out>
+    transform(_Range&& __r, _Out __result, _Fp __op, _Proj __proj = {})
+    {
+      return ranges::transform(ranges::begin(__r), ranges::end(__r),
+			       std::move(__result),
+			       std::move(__op), std::move(__proj));
+    }
+
+  template<typename _Iter1, typename _Iter2, typename _Out>
+    struct binary_transform_result
+    {
+      [[no_unique_address]] _Iter1 in1;
+      [[no_unique_address]] _Iter2 in2;
+      [[no_unique_address]] _Out  out;
+
+      template<typename _IIter1, typename _IIter2, typename _OOut>
+	requires convertible_to<const _Iter1&, _IIter1> &&
+	  && convertible_to<const _Iter2&, _IIter2>
+	  && convertible_to<const _Out&, _OOut>
+	operator binary_transform_result<_IIter1, _IIter2, _OOut>() const &
+	{ return {in1, in2, out}; }
+
+      template<typename _IIter1, typename _IIter2, typename _OOut>
+	requires convertible_to<_Iter1, _IIter1>
+	  && convertible_to<_Iter2, _IIter2>
+	  && convertible_to<_Out, _OOut>
+	operator binary_transform_result<_IIter1, _IIter2, _OOut>() &&
+	{ return {std::move(in1), std::move(in2), std::move(out)}; }
+    };
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   weakly_incrementable _Out, copy_constructible _Fp,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires writable<_Out, indirect_result_t<_Fp&, projected<_Iter1, _Proj1>,
+					   projected<_Iter2, _Proj2>>>
+    constexpr binary_transform_result<_Iter1, _Iter2, _Out>
+    transform(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	      _Out __result, _Fp __binary_op,
+	      _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      for (; __first1 != __last1 && __first2 != __last2;
+	   ++__first1, (void)++__first2, ++__result)
+	*__result = std::__invoke(__binary_op,
+				  std::__invoke(__proj1, *__first1),
+				  std::__invoke(__proj2, *__first2));
+      return {std::move(__first1), std::move(__first2), std::move(__result)};
+    }
+
+  template<input_range _Range1, input_range _Range2,
+	   weakly_incrementable _Out, copy_constructible _Fp,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires writable<_Out, indirect_result_t<_Fp&,
+					      projected<iterator_t<_Range1>,
+							_Proj1>,
+					      projected<iterator_t<_Range2>,
+							_Proj2>>>
+    constexpr binary_transform_result<safe_iterator_t<_Range1>,
+				      safe_iterator_t<_Range2>, _Out>
+    transform(_Range1&& __r1, _Range2&& __r2, _Out __result,
+	      _Fp __binary_op, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::transform(ranges::begin(__r1), ranges::end(__r1),
+			       ranges::begin(__r2), ranges::end(__r2),
+			       std::move(__result), std::move(__binary_op),
+			       std::move(__proj1), std::move(__proj2));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp1, typename _Tp2, typename _Proj = identity>
+    requires writable<_Iter, const _Tp2&> &&
+	     indirect_binary_predicate<ranges::equal_to,
+				       projected<_Iter, _Proj>, const _Tp1*>
+    constexpr _Iter
+    replace(_Iter __first, _Sent __last,
+	    const _Tp1& __old_value, const _Tp2& __new_value,
+	    _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__proj, *__first) == __old_value)
+	  *__first = __new_value;
+      return __first;
+    }
+
+  template<input_range _Range,
+	   typename _Tp1, typename _Tp2, typename _Proj = identity>
+    requires writable<iterator_t<_Range>, const _Tp2&> &&
+	     indirect_binary_predicate<ranges::equal_to,
+				       projected<iterator_t<_Range>, _Proj>,
+						 const _Tp1*>
+    constexpr safe_iterator_t<_Range>
+    replace(_Range&& __r,
+	    const _Tp1& __old_value, const _Tp2& __new_value,
+	    _Proj __proj = {})
+    {
+      return ranges::replace(ranges::begin(__r), ranges::end(__r),
+			     __old_value, __new_value, std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    requires writable<_Iter, const _Tp&>
+    constexpr _Iter
+    replace_if(_Iter __first, _Sent __last,
+	       _Pred __pred, const _Tp& __new_value, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  *__first = __new_value;
+      return std::move(__first);
+    }
+
+  template<input_range _Range, typename _Tp, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires writable<iterator_t<_Range>, const _Tp&>
+    constexpr safe_iterator_t<_Range>
+    replace_if(_Range&& __r,
+	       _Pred __pred, const _Tp& __new_value, _Proj __proj = {})
+    {
+      return ranges::replace_if(ranges::begin(__r), ranges::end(__r),
+				std::move(__pred), __new_value,
+				std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Out>
+    using replace_copy_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp1, typename _Tp2, output_iterator<const _Tp2&> _Out,
+	   typename _Proj = identity>
+    requires indirectly_copyable<_Iter, _Out>
+      && indirect_binary_predicate<ranges::equal_to,
+				   projected<_Iter, _Proj>, const _Tp1*>
+    constexpr replace_copy_result<_Iter, _Out>
+    replace_copy(_Iter __first, _Sent __last, _Out __result,
+		 const _Tp1& __old_value, const _Tp2& __new_value,
+		 _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first, (void)++__result)
+	if (std::__invoke(__proj, *__first) == __old_value)
+	  *__result = __new_value;
+	else
+	  *__result = *__first;
+      return {std::move(__first), std::move(__result)};
+    }
+
+  template<input_range _Range, typename _Tp1, typename _Tp2,
+	   output_iterator<const _Tp2&> _Out, typename _Proj = identity>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+      && indirect_binary_predicate<ranges::equal_to,
+				   projected<iterator_t<_Range>, _Proj>,
+				   const _Tp1*>
+    constexpr replace_copy_result<safe_iterator_t<_Range>, _Out>
+    replace_copy(_Range&& __r, _Out __result,
+		 const _Tp1& __old_value, const _Tp2& __new_value,
+		 _Proj __proj = {})
+    {
+      return ranges::replace_copy(ranges::begin(__r), ranges::end(__r),
+				  std::move(__result), __old_value,
+				  __new_value, std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Out>
+    using replace_copy_if_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, output_iterator<const _Tp&> _Out,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    requires indirectly_copyable<_Iter, _Out>
+    constexpr replace_copy_if_result<_Iter, _Out>
+    replace_copy_if(_Iter __first, _Sent __last, _Out __result,
+		    _Pred __pred, const _Tp& __new_value, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first, (void)++__result)
+	if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  *__result = __new_value;
+	else
+	  *__result = *__first;
+      return {std::move(__first), std::move(__result)};
+    }
+
+  template<input_range _Range,
+	   typename _Tp, output_iterator<const _Tp&> _Out,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+    constexpr replace_copy_if_result<safe_iterator_t<_Range>, _Out>
+    replace_copy_if(_Range&& __r, _Out __result,
+		    _Pred __pred, const _Tp& __new_value, _Proj __proj = {})
+    {
+      return ranges::replace_copy_if(ranges::begin(__r), ranges::end(__r),
+				     std::move(__result), std::move(__pred),
+				     __new_value, std::move(__proj));
+    }
+
+  template<typename _Tp, output_iterator<const _Tp&> _Out>
+    constexpr _Out
+    fill_n(_Out __first, iter_difference_t<_Out> __n, const _Tp& __value)
+    {
+      // TODO: implement more specializations to be at least on par with
+      // std::fill_n
+      if (__n <= 0)
+	return __first;
+
+      // TODO: is __is_byte the best condition?
+      if constexpr (is_pointer_v<_Out> && __is_byte<_Tp>::__value)
+	{
+	  __builtin_memset(__first, static_cast<unsigned char>(__value), __n);
+	  return __first + __n;
+	}
+      else if constexpr (is_scalar_v<_Tp>)
+	{
+	  const auto __tmp = __value;
+	  for (; __n > 0; --__n, (void)++__first)
+	    *__first = __tmp;
+	  return __first;
+	}
+      else
+	{
+	  for (; __n > 0; --__n, (void)++__first)
+	    *__first = __value;
+	  return __first;
+	}
+    }
+
+  template<typename _Tp,
+	   output_iterator<const _Tp&> _Out, sentinel_for<_Out> _Sent>
+    constexpr _Out
+    fill(_Out __first, _Sent __last, const _Tp& __value)
+    {
+      // TODO: implement more specializations to be at least on par with
+      // std::fill
+      if constexpr (sized_sentinel_for<_Sent, _Out>)
+	{
+	  const auto __len = __last - __first;
+	  return ranges::fill_n(__first, __len, __value);
+	}
+      else if constexpr (is_scalar_v<_Tp>)
+	{
+	  const auto __tmp = __value;
+	  for (; __first != __last; ++__first)
+	    *__first = __tmp;
+	  return __first;
+	}
+      else
+	{
+	  for (; __first != __last; ++__first)
+	    *__first = __value;
+	  return __first;
+	}
+    }
+
+  template<typename _Tp, output_range<const _Tp&> _Range>
+    constexpr safe_iterator_t<_Range>
+    fill(_Range&& __r, const _Tp& __value)
+    {
+      return ranges::fill(ranges::begin(__r), ranges::end(__r), __value);
+    }
+
+  template<input_or_output_iterator _Out, copy_constructible _Fp>
+    requires invocable<_Fp&> && writable<_Out, invoke_result_t<_Fp&>>
+    constexpr _Out
+    generate_n(_Out __first, iter_difference_t<_Out> __n, _Fp __gen)
+    {
+      for (; __n > 0; --__n, (void)++__first)
+	*__first = std::__invoke(__gen);
+      return __first;
+    }
+
+  template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent,
+	   copy_constructible _Fp>
+    requires invocable<_Fp&> && writable<_Out, invoke_result_t<_Fp&>>
+    constexpr _Out
+    generate(_Out __first, _Sent __last, _Fp __gen)
+    {
+      for (; __first != __last; ++__first)
+	*__first = std::__invoke(__gen);
+      return __first;
+    }
+
+  template<typename _Range, copy_constructible _Fp>
+    requires invocable<_Fp&> && output_range<_Range, invoke_result_t<_Fp&>>
+    constexpr safe_iterator_t<_Range>
+    generate(_Range&& __r, _Fp __gen)
+    {
+      return ranges::generate(ranges::begin(__r), ranges::end(__r),
+			      std::move(__gen));
+    }
+
+  template<permutable _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr subrange<_Iter>
+    remove_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      __first = ranges::find_if(__first, __last, __pred, __proj);
+      if (__first == __last)
+	return {__first, __first};
+
+      auto __result = __first;
+      ++__first;
+      for (; __first != __last; ++__first)
+	if (!(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  {
+	    *__result = std::move(*__first);
+	    ++__result;
+	  }
+
+      return {__result, __first};
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires permutable<iterator_t<_Range>>
+    constexpr safe_subrange_t<_Range>
+    remove_if(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::remove_if(ranges::begin(__r), ranges::end(__r),
+			       std::move(__pred), std::move(__proj));
+    }
+
+  template<permutable _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, typename _Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to,
+				       projected<_Iter, _Proj>,
+				       const _Tp*>
+    constexpr subrange<_Iter>
+    remove(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {})
+    {
+      auto __pred = [&] (auto&& __arg) {
+	return std::forward<decltype(__arg)>(__arg) == __value;
+      };
+      return ranges::remove_if(__first, __last,
+			       std::move(__pred), std::move(__proj));
+    }
+
+  template<forward_range _Range, typename _Tp, typename _Proj = identity>
+    requires permutable<iterator_t<_Range>> &&
+	     indirect_binary_predicate<ranges::equal_to,
+				       projected<iterator_t<_Range>, _Proj>,
+				       const _Tp*>
+    constexpr safe_subrange_t<_Range>
+    remove(_Range&& __r, const _Tp& __value, _Proj __proj = {})
+    {
+      return ranges::remove(ranges::begin(__r), ranges::end(__r),
+			    __value, std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Out>
+    using remove_copy_if_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out, typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    requires indirectly_copyable<_Iter, _Out>
+    constexpr remove_copy_if_result<_Iter, _Out>
+    remove_copy_if(_Iter __first, _Sent __last, _Out __result,
+		   _Pred __pred, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (!(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  {
+	    *__result = *__first;
+	    ++__result;
+	  }
+      return {std::move(__first), std::move(__result)};
+    }
+
+  template<input_range _Range, weakly_incrementable _Out,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+    constexpr remove_copy_if_result<safe_iterator_t<_Range>, _Out>
+    remove_copy_if(_Range&& __r, _Out __result,
+		   _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::remove_copy_if(ranges::begin(__r), ranges::end(__r),
+				    std::move(__result),
+				    std::move(__pred), std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Out>
+    using remove_copy_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out, typename _Tp, typename _Proj = identity>
+    requires indirectly_copyable<_Iter, _Out>
+      && indirect_binary_predicate<ranges::equal_to,
+				   projected<_Iter, _Proj>,
+				   const _Tp*>
+    constexpr remove_copy_result<_Iter, _Out>
+    remove_copy(_Iter __first, _Sent __last, _Out __result,
+		const _Tp& __value, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (!(std::__invoke(__proj, *__first) == __value))
+	  {
+	    *__result = *__first;
+	    ++__result;
+	  }
+      return {std::move(__first), std::move(__result)};
+    }
+
+  template<input_range _Range, weakly_incrementable _Out,
+	   typename _Tp, typename _Proj = identity>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+      && indirect_binary_predicate<ranges::equal_to,
+				   projected<iterator_t<_Range>, _Proj>,
+				   const _Tp*>
+    constexpr remove_copy_result<safe_iterator_t<_Range>, _Out>
+    remove_copy(_Range&& __r, _Out __result,
+		const _Tp& __value, _Proj __proj = {})
+    {
+      return ranges::remove_copy(ranges::begin(__r), ranges::end(__r),
+				 std::move(__result), __value,
+				 std::move(__proj));
+
+    }
+
+  template<permutable _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_equivalence_relation<
+	     projected<_Iter, _Proj>> _Comp = ranges::equal_to>
+    constexpr subrange<_Iter>
+    unique(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      __first = ranges::adjacent_find(__first, __last, __comp, __proj);
+      if (__first == __last)
+	return {__first, __first};
+
+      auto __dest = __first;
+      ++__first;
+      while (++__first != __last)
+	if (!(bool)std::__invoke(__comp,
+				 std::__invoke(__proj, *__dest),
+				 std::__invoke(__proj, *__first)))
+	  *++__dest = std::move(*__first);
+      return {++__dest, __first};
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_equivalence_relation<
+	     projected<iterator_t<_Range>, _Proj>> _Comp = ranges::equal_to>
+    requires permutable<iterator_t<_Range>>
+    constexpr safe_subrange_t<_Range>
+    unique(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::unique(ranges::begin(__r), ranges::end(__r),
+			    std::move(__comp), std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Out>
+    using unique_copy_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out, typename _Proj = identity,
+	   indirect_equivalence_relation<
+	     projected<_Iter, _Proj>> _Comp = ranges::equal_to>
+    requires indirectly_copyable<_Iter, _Out>
+      && (forward_iterator<_Iter>
+	  || (input_iterator<_Out>
+	      && same_as<iter_value_t<_Iter>, iter_value_t<_Out>>)
+	  || indirectly_copyable_storable<_Iter, _Out>)
+    constexpr unique_copy_result<_Iter, _Out>
+    unique_copy(_Iter __first, _Sent __last, _Out __result,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return {std::move(__first), std::move(__result)};
+
+      // TODO: perform a closer comparison with reference implementations
+      if constexpr (forward_iterator<_Iter>)
+	{
+	  auto __next = __first;
+	  *__result = *__next;
+	  while (++__next != __last)
+	    if (!(bool)std::__invoke(__comp,
+				     std::__invoke(__proj, *__first),
+				     std::__invoke(__proj, *__next)))
+	      {
+		__first = __next;
+		*++__result = *__first;
+	      }
+	  return {__next, std::move(++__result)};
+	}
+      else if constexpr (input_iterator<_Out>
+			 && same_as<iter_value_t<_Iter>, iter_value_t<_Out>>)
+	{
+	  *__result = *__first;
+	  while (++__first != __last)
+	    if (!(bool)std::__invoke(__comp,
+				     std::__invoke(__proj, *__result),
+				     std::__invoke(__proj, *__first)))
+		*++__result = *__first;
+	  return {std::move(__first), std::move(++__result)};
+	}
+      else // indirectly_copyable_storable<_Iter, _Out>
+	{
+	  auto __value = *__first;
+	  *__result = __value;
+	  while (++__first != __last)
+	    {
+	      if (!(bool)std::__invoke(__comp,
+				       std::__invoke(__proj, *__first),
+				       std::__invoke(__proj, __value)))
+		{
+		  __value = *__first;
+		  *++__result = __value;
+		}
+	    }
+	  return {std::move(__first), std::move(++__result)};
+	}
+    }
+
+  template<input_range _Range,
+	   weakly_incrementable _Out, typename _Proj = identity,
+	   indirect_equivalence_relation<
+	     projected<iterator_t<_Range>, _Proj>> _Comp = ranges::equal_to>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+      && (forward_iterator<iterator_t<_Range>>
+	  || (input_iterator<_Out>
+	      && same_as<range_value_t<_Range>, iter_value_t<_Out>>)
+	  || indirectly_copyable_storable<iterator_t<_Range>, _Out>)
+    constexpr unique_copy_result<safe_iterator_t<_Range>, _Out>
+    unique_copy(_Range&& __r, _Out __result,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::unique_copy(ranges::begin(__r), ranges::end(__r),
+				 std::move(__result),
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent>
+    requires permutable<_Iter>
+    constexpr _Iter
+    reverse(_Iter __first, _Sent __last)
+    {
+      auto __i = ranges::next(__first, __last);
+      auto __tail = __i;
+
+      if constexpr (random_access_iterator<_Iter>)
+	{
+	  if (__first != __last)
+	    {
+	      --__tail;
+	      while (__first < __tail)
+		{
+		  ranges::iter_swap(__first, __tail);
+		  ++__first;
+		  --__tail;
+		}
+	    }
+	  return __i;
+	}
+      else
+	{
+	  for (;;)
+	    if (__first == __tail || __first == --__tail)
+	      break;
+	    else
+	      {
+		ranges::iter_swap(__first, __tail);
+		++__first;
+	      }
+	  return __i;
+	}
+    }
+
+  template<bidirectional_range _Range>
+    requires permutable<iterator_t<_Range>>
+    constexpr safe_iterator_t<_Range>
+    reverse(_Range&& __r)
+    {
+      return ranges::reverse(ranges::begin(__r), ranges::end(__r));
+    }
+
+  template<typename _Iter, typename _Out>
+    using reverse_copy_result = copy_result<_Iter, _Out>;
+
+  template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out>
+    requires indirectly_copyable<_Iter, _Out>
+    constexpr reverse_copy_result<_Iter, _Out>
+    reverse_copy(_Iter __first, _Sent __last, _Out __result)
+    {
+      auto __i = ranges::next(__first, __last);
+      auto __tail = __i;
+      while (__first != __tail)
+	{
+	  --__tail;
+	  *__result = *__tail;
+	  ++__result;
+	}
+      return {__i, __result};
+    }
+
+  template<bidirectional_range _Range, weakly_incrementable _Out>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+    constexpr reverse_copy_result<safe_iterator_t<_Range>, _Out>
+    reverse_copy(_Range&& __r, _Out __result)
+    {
+      return ranges::reverse_copy(ranges::begin(__r), ranges::end(__r),
+				  std::move(__result));
+    }
+
+  template<permutable _Iter, sentinel_for<_Iter> _Sent>
+    constexpr subrange<_Iter>
+    rotate(_Iter __first, _Iter __middle, _Sent __last)
+    {
+      auto __lasti = ranges::next(__first, __last);
+      if (__first == __middle)
+	return {__lasti, __lasti};
+      if (__last == __middle)
+	return {std::move(__first), std::move(__lasti)};
+
+      if constexpr (random_access_iterator<_Iter>)
+	{
+	  auto __n = __lasti - __first;
+	  auto __k = __middle - __first;
+
+	  if (__k == __n - __k)
+	    {
+	      ranges::swap_ranges(__first, __middle, __middle, __middle + __k);
+	      return {std::move(__middle), std::move(__lasti)};
+	    }
+
+	  auto __p = __first;
+	  auto __ret = __first + (__lasti - __middle);
+
+	  for (;;)
+	    {
+	      if (__k < __n - __k)
+		{
+		  // TODO: is_pod is deprecated, but this condition is
+		  // consistent with the STL implementation.
+		  if constexpr (__is_pod(iter_value_t<_Iter>))
+		    if (__k == 1)
+		      {
+			auto __t = std::move(*__p);
+			ranges::move(__p + 1, __p + __n, __p);
+			*(__p + __n - 1) = std::move(__t);
+			return {std::move(__ret), std::move(__lasti)};
+		      }
+		  auto __q = __p + __k;
+		  for (decltype(__n) __i = 0; __i < __n - __k; ++ __i)
+		    {
+		      ranges::iter_swap(__p, __q);
+		      ++__p;
+		      ++__q;
+		    }
+		  __n %= __k;
+		  if (__n == 0)
+		    return {std::move(__ret), std::move(__lasti)};
+		  ranges::swap(__n, __k);
+		  __k = __n - __k;
+		}
+	      else
+		{
+		  __k = __n - __k;
+		  // TODO: is_pod is deprecated, but this condition is
+		  // consistent with the STL implementation.
+		  if constexpr (__is_pod(iter_value_t<_Iter>))
+		    if (__k == 1)
+		      {
+			auto __t = std::move(*(__p + __n - 1));
+			ranges::move_backward(__p, __p + __n - 1, __p + __n);
+			*__p = std::move(__t);
+			return {std::move(__ret), std::move(__lasti)};
+		      }
+		  auto __q = __p + __n;
+		  __p = __q - __k;
+		  for (decltype(__n) __i = 0; __i < __n - __k; ++ __i)
+		    {
+		      --__p;
+		      --__q;
+		      ranges::iter_swap(__p, __q);
+		    }
+		  __n %= __k;
+		  if (__n == 0)
+		    return {std::move(__ret), std::move(__lasti)};
+		  std::swap(__n, __k);
+		}
+	    }
+	}
+      else if constexpr (bidirectional_iterator<_Iter>)
+	{
+	  auto __tail = __lasti;
+
+	  ranges::reverse(__first, __middle);
+	  ranges::reverse(__middle, __tail);
+
+	  while (__first != __middle && __middle != __tail)
+	    {
+	      ranges::iter_swap(__first, --__tail);
+	      ++__first;
+	    }
+
+	  if (__first == __middle)
+	    {
+	      ranges::reverse(__middle, __tail);
+	      return {std::move(__tail), std::move(__lasti)};
+	    }
+	  else
+	    {
+	      ranges::reverse(__first, __middle);
+	      return {std::move(__first), std::move(__lasti)};
+	    }
+	}
+      else
+	{
+	  auto __first2 = __middle;
+	  do
+	    {
+	      ranges::iter_swap(__first, __first2);
+	      ++__first;
+	      ++__first2;
+	      if (__first == __middle)
+		__middle = __first2;
+	    } while (__first2 != __last);
+
+	  auto __ret = __first;
+
+	  __first2 = __middle;
+
+	  while (__first2 != __last)
+	    {
+	      ranges::iter_swap(__first, __first2);
+	      ++__first;
+	      ++__first2;
+	      if (__first == __middle)
+		__middle = __first2;
+	      else if (__first2 == __last)
+		__first2 = __middle;
+	    }
+	  return {std::move(__ret), std::move(__lasti)};
+	}
+    }
+
+  template<forward_range _Range>
+    requires permutable<iterator_t<_Range>>
+    constexpr safe_subrange_t<_Range>
+    rotate(_Range&& __r, iterator_t<_Range> __middle)
+    {
+      return ranges::rotate(ranges::begin(__r),
+			    std::move(__middle),
+			    ranges::end(__r));
+    }
+
+  template<typename _Iter, typename _Out>
+    using rotate_copy_result = copy_result<_Iter, _Out>;
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out>
+    requires indirectly_copyable<_Iter, _Out>
+    constexpr rotate_copy_result<_Iter, _Out>
+    rotate_copy(_Iter __first, _Iter __middle, _Sent __last, _Out __result)
+    {
+      auto __copy1 = ranges::copy(__middle,
+				  std::move(__last),
+				  std::move(__result));
+      auto __copy2 = ranges::copy(std::move(__first),
+				  std::move(__middle),
+				  std::move(__copy1.out));
+      return { std::move(__copy1.in), std::move(__copy2.out) };
+    }
+
+  template<forward_range _Range, weakly_incrementable _Out>
+    requires indirectly_copyable<iterator_t<_Range>, _Out>
+    constexpr rotate_copy_result<safe_iterator_t<_Range>, _Out>
+    rotate_copy(_Range&& __r, iterator_t<_Range> __middle, _Out __result)
+    {
+      return ranges::rotate_copy(ranges::begin(__r),
+				 std::move(__middle),
+				 ranges::end(__r),
+				 std::move(__result));
+    }
+
+#ifdef _GLIBCXX_USE_C99_STDINT_TR1
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Gen>
+    requires permutable<_Iter>
+      && uniform_random_bit_generator<remove_reference_t<_Gen>>
+    _Iter
+    shuffle(_Iter __first, _Sent __last, _Gen&& __g)
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::shuffle(std::move(__first), __lasti, std::forward<_Gen>(__g));
+      return __lasti;
+    }
+
+  template<random_access_range _Range, typename _Gen>
+    requires permutable<iterator_t<_Range>>
+      && uniform_random_bit_generator<remove_reference_t<_Gen>>
+    safe_iterator_t<_Range>
+    shuffle(_Range&& __r, _Gen&& __g)
+    {
+      return ranges::shuffle(ranges::begin(__r), ranges::end(__r),
+			     std::forward<_Gen>(__g));
+    }
+#endif
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr _Iter
+    push_heap(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::push_heap(__first, __lasti,
+		     __detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr safe_iterator_t<_Range>
+    push_heap(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::push_heap(ranges::begin(__r), ranges::end(__r),
+			       std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr _Iter
+    pop_heap(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::pop_heap(__first, __lasti,
+		    __detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr safe_iterator_t<_Range>
+    pop_heap(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::pop_heap(ranges::begin(__r), ranges::end(__r),
+			       std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr _Iter
+    make_heap(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::make_heap(__first, __lasti,
+		     __detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr safe_iterator_t<_Range>
+    make_heap(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::make_heap(ranges::begin(__r), ranges::end(__r),
+			       std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr _Iter
+    sort_heap(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::sort_heap(__first, __lasti,
+		     __detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr safe_iterator_t<_Range>
+    sort_heap(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::sort_heap(ranges::begin(__r), ranges::end(__r),
+			       std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Iter
+    is_heap_until(_Iter __first, _Sent __last,
+		  _Comp __comp = {}, _Proj __proj = {})
+    {
+      iter_difference_t<_Iter> __n = ranges::distance(__first, __last);
+      iter_difference_t<_Iter> __parent = 0, __child = 1;
+      for (; __child < __n; ++__child)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj, *(__first + __parent)),
+			  std::__invoke(__proj, *(__first + __child))))
+	  return __first + __child;
+	else if ((__child & 1) == 0)
+	  ++__parent;
+
+      return __first + __n;
+    }
+
+  template<random_access_range _Range,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr safe_iterator_t<_Range>
+    is_heap_until(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::is_heap_until(ranges::begin(__r), ranges::end(__r),
+				   std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr bool
+    is_heap(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return (__last
+	      == ranges::is_heap_until(__first, __last,
+				       std::move(__comp),
+				       std::move(__proj)));
+    }
+
+  template<random_access_range _Range,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr bool
+    is_heap(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::is_heap(ranges::begin(__r), ranges::end(__r),
+			     std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr _Iter
+    sort(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::sort(std::move(__first), __lasti,
+		__detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr safe_iterator_t<_Range>
+    sort(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::sort(ranges::begin(__r), ranges::end(__r),
+			  std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    _Iter
+    stable_sort(_Iter __first, _Sent __last,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::stable_sort(std::move(__first), __lasti,
+		       __detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    safe_iterator_t<_Range>
+    stable_sort(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::stable_sort(ranges::begin(__r), ranges::end(__r),
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr _Iter
+    partial_sort(_Iter __first, _Iter __middle, _Sent __last,
+		 _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __middle)
+	return ranges::next(__first, __last);
+
+      ranges::make_heap(__first, __middle, __comp, __proj);
+      auto __i = __middle;
+      for (; __i != __last; ++__i)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj, *__i),
+			  std::__invoke(__proj, *__first)))
+	  {
+	    ranges::pop_heap(__first, __middle, __comp, __proj);
+	    ranges::iter_swap(__middle-1, __i);
+	    ranges::push_heap(__first, __middle, __comp, __proj);
+	  }
+      ranges::sort_heap(__first, __middle, __comp, __proj);
+
+      return __i;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr safe_iterator_t<_Range>
+    partial_sort(_Range&& __r, iterator_t<_Range> __middle,
+		 _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::partial_sort(ranges::begin(__r),
+				  std::move(__middle),
+				  ranges::end(__r),
+				  std::move(__comp), std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Out>
+    using partial_sort_copy_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   random_access_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires indirectly_copyable<_Iter1, _Iter2>
+      && sortable<_Iter2, _Comp, _Proj2>
+      && indirect_strict_weak_order<_Comp,
+				    projected<_Iter1, _Proj1>,
+				    projected<_Iter2, _Proj2>>
+    constexpr partial_sort_copy_result<_Iter1, _Iter2>
+    partial_sort_copy(_Iter1 __first, _Sent1 __last,
+		      _Iter2 __result_first, _Sent2 __result_last,
+		      _Comp __comp = {},
+		      _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      if (__result_first == __result_last)
+	{
+	  // TODO: Eliminating the variable __lasti triggers an ICE.
+	  auto __lasti = ranges::next(std::move(__first),
+				      std::move(__last));
+	  return {std::move(__lasti), std::move(__result_first)};
+	}
+
+      auto __result_real_last = __result_first;
+      while (__first != __last && __result_real_last != __result_last)
+	{
+	  *__result_real_last = *__first;
+	  ++__result_real_last;
+	  ++__first;
+	}
+
+      ranges::make_heap(__result_first, __result_real_last, __comp, __proj2);
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj1, *__first),
+			  std::__invoke(__proj2, *__result_first)))
+	  {
+	    ranges::pop_heap(__result_first, __result_real_last,
+			     __comp, __proj2);
+	    *(__result_real_last-1) = *__first;
+	    ranges::push_heap(__result_first, __result_real_last,
+			      __comp, __proj2);
+	  }
+      ranges::sort_heap(__result_first, __result_real_last, __comp, __proj2);
+
+      return {std::move(__first), std::move(__result_real_last)};
+    }
+
+  template<input_range _Range1, random_access_range _Range2,
+	   typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires indirectly_copyable<iterator_t<_Range1>, iterator_t<_Range2>>
+      && sortable<iterator_t<_Range2>, _Comp, _Proj2>
+      && indirect_strict_weak_order<_Comp,
+				    projected<iterator_t<_Range1>, _Proj1>,
+				    projected<iterator_t<_Range2>, _Proj2>>
+    constexpr partial_sort_copy_result<safe_iterator_t<_Range1>,
+				       safe_iterator_t<_Range2>>
+    partial_sort_copy(_Range1&& __r, _Range2&& __out, _Comp __comp = {},
+		      _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::partial_sort_copy(ranges::begin(__r), ranges::end(__r),
+				       ranges::begin(__out), ranges::end(__out),
+				       std::move(__comp),
+				       std::move(__proj1), std::move(__proj2));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Iter
+    is_sorted_until(_Iter __first, _Sent __last,
+		    _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return __first;
+
+      auto __next = __first;
+      for (++__next; __next != __last; __first = __next, (void)++__next)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj, *__next),
+			  std::__invoke(__proj, *__first)))
+	  return __next;
+      return __next;
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr safe_iterator_t<_Range>
+    is_sorted_until(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::is_sorted_until(ranges::begin(__r), ranges::end(__r),
+				     std::move(__comp), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr bool
+    is_sorted(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return true;
+
+      auto __next = __first;
+      for (++__next; __next != __last; __first = __next, (void)++__next)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj, *__next),
+			  std::__invoke(__proj, *__first)))
+	  return false;
+      return true;
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr bool
+    is_sorted(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::is_sorted(ranges::begin(__r), ranges::end(__r),
+			       std::move(__comp), std::move(__proj));
+    }
+
+  template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr _Iter
+    nth_element(_Iter __first, _Iter __nth, _Sent __last,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::nth_element(std::move(__first), std::move(__nth), __lasti,
+		       __detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<random_access_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr safe_iterator_t<_Range>
+    nth_element(_Range&& __r, iterator_t<_Range> __nth,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::nth_element(ranges::begin(__r), std::move(__nth),
+				 ranges::end(__r),
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*, projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Iter
+    lower_bound(_Iter __first, _Sent __last,
+		const _Tp& __value, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __len = ranges::distance(__first, __last);
+
+      while (__len > 0)
+	{
+	  auto __half = __len / 2;
+	  auto __middle = __first;
+	  ranges::advance(__middle, __half);
+	  if (std::__invoke(__comp, std::__invoke(__proj, *__middle), __value))
+	    {
+	      __first = __middle;
+	      ++__first;
+	      __len = __len - __half - 1;
+	    }
+	  else
+	    __len = __half;
+	}
+      return __first;
+    }
+
+  template<forward_range _Range, typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*,
+				      projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr safe_iterator_t<_Range>
+    lower_bound(_Range&& __r,
+		const _Tp& __value, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::lower_bound(ranges::begin(__r), ranges::end(__r),
+				 __value,
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*, projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Iter
+    upper_bound(_Iter __first, _Sent __last,
+		const _Tp& __value, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __len = ranges::distance(__first, __last);
+
+      while (__len > 0)
+	{
+	  auto __half = __len / 2;
+	  auto __middle = __first;
+	  ranges::advance(__middle, __half);
+	  if (std::__invoke(__comp, __value, std::__invoke(__proj, *__middle)))
+	    __len = __half;
+	  else
+	    {
+	      __first = __middle;
+	      ++__first;
+	      __len = __len - __half - 1;
+	    }
+	}
+      return __first;
+    }
+
+  template<forward_range _Range, typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*,
+				      projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr safe_iterator_t<_Range>
+    upper_bound(_Range&& __r,
+		const _Tp& __value, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::upper_bound(ranges::begin(__r), ranges::end(__r),
+				 __value,
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*, projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr subrange<_Iter>
+    equal_range(_Iter __first, _Sent __last,
+		const _Tp& __value, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __len = ranges::distance(__first, __last);
+
+      while (__len > 0)
+	{
+	  auto __half = __len / 2;
+	  auto __middle = __first;
+	  ranges::advance(__middle, __half);
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, *__middle),
+			    __value))
+	    {
+	      __first = __middle;
+	      ++__first;
+	      __len = __len - __half - 1;
+	    }
+	  else if (std::__invoke(__comp,
+				 __value,
+				 std::__invoke(__proj, *__middle)))
+	    __len = __half;
+	  else
+	    {
+	      auto __left
+		= ranges::lower_bound(__first, __middle,
+				      __value, __comp, __proj);
+	      ranges::advance(__first, __len);
+	      auto __right
+		= ranges::upper_bound(++__middle, __first,
+				      __value, __comp, __proj);
+	      return {__left, __right};
+	    }
+	}
+      return {__first, __first};
+    }
+
+  template<forward_range _Range,
+	   typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*,
+				      projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr safe_subrange_t<_Range>
+    equal_range(_Range&& __r, const _Tp& __value,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::equal_range(ranges::begin(__r), ranges::end(__r),
+				 __value,
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*, projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr bool
+    binary_search(_Iter __first, _Sent __last,
+		  const _Tp& __value, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __i = ranges::lower_bound(__first, __last, __value, __comp, __proj);
+      if (__i == __last)
+	return false;
+      return !(bool)std::__invoke(__comp, __value, std::__invoke(__proj, *__i));
+    }
+
+  template<forward_range _Range,
+	   typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<const _Tp*,
+				      projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr bool
+    binary_search(_Range&& __r, const _Tp& __value, _Comp __comp = {},
+		  _Proj __proj = {})
+    {
+      return ranges::binary_search(ranges::begin(__r), ranges::end(__r),
+				   __value,
+				   std::move(__comp), std::move(__proj));
+    }
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr bool
+    is_partitioned(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      __first = ranges::find_if_not(std::move(__first), __last, __pred, __proj);
+      if (__first == __last)
+	return true;
+      ++__first;
+      return ranges::none_of(std::move(__first), std::move(__last),
+			     std::move(__pred), std::move(__proj));
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    constexpr bool
+    is_partitioned(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::is_partitioned(ranges::begin(__r), ranges::end(__r),
+				    std::move(__pred), std::move(__proj));
+    }
+
+  template<permutable _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr subrange<_Iter>
+    partition(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      if constexpr (bidirectional_iterator<_Iter>)
+	{
+	  auto __lasti = ranges::next(__first, __last);
+	  auto __tail = __lasti;
+	  for (;;)
+	    {
+	      for (;;)
+		if (__first == __tail)
+		  return {std::move(__first), std::move(__lasti)};
+		else if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+		  ++__first;
+		else
+		  break;
+	      --__tail;
+	      for (;;)
+		if (__first == __tail)
+		  return {std::move(__first), std::move(__lasti)};
+		else if (!(bool)std::__invoke(__pred,
+					      std::__invoke(__proj, *__tail)))
+		  --__tail;
+		else
+		  break;
+	      ranges::iter_swap(__first, __tail);
+	      ++__first;
+	    }
+	}
+      else
+	{
+	  if (__first == __last)
+	    return {std::move(__first), std::move(__first)};
+
+	  while (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	    if (++__first == __last)
+	      return {std::move(__first), std::move(__first)};
+
+	  auto __next = __first;
+	  while (++__next != __last)
+	    if (std::__invoke(__pred, std::__invoke(__proj, *__next)))
+	      {
+		ranges::iter_swap(__first, __next);
+		++__first;
+	      }
+
+	  return {std::move(__first), std::move(__next)};
+	}
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires permutable<iterator_t<_Range>>
+    constexpr safe_subrange_t<_Range>
+    partition(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::partition(ranges::begin(__r), ranges::end(__r),
+			       std::move(__pred), std::move(__proj));
+    }
+
+  template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    requires permutable<_Iter>
+    subrange<_Iter>
+    stable_partition(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      auto __middle
+	= std::stable_partition(std::move(__first), __lasti,
+				__detail::__make_pred_proj(__pred, __proj));
+      return {std::move(__middle), std::move(__lasti)};
+    }
+
+  template<bidirectional_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires permutable<iterator_t<_Range>>
+    safe_subrange_t<_Range>
+    stable_partition(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::stable_partition(ranges::begin(__r), ranges::end(__r),
+				      std::move(__pred), std::move(__proj));
+    }
+
+  template<typename _Iter, typename _Out1, typename _O2>
+    struct partition_copy_result
+    {
+      [[no_unique_address]] _Iter  in;
+      [[no_unique_address]] _Out1 out1;
+      [[no_unique_address]] _O2 out2;
+
+      template<typename _IIter, typename _OOut1, typename _OOut2>
+	requires convertible_to<const _Iter&, _IIter>
+	  && convertible_to<const _Out1&, _OOut1>
+	  && convertible_to<const _O2&, _OOut2>
+	operator partition_copy_result<_IIter, _OOut1, _OOut2>() const &
+	{ return {in, out1, out2}; }
+
+      template<typename _IIter, typename _OOut1, typename _OOut2>
+	requires convertible_to<_Iter, _IIter>
+	  && convertible_to<_Out1, _OOut1>
+	  && convertible_to<_O2, _OOut2>
+	operator partition_copy_result<_IIter, _OOut1, _OOut2>() &&
+	{ return {std::move(in), std::move(out1), std::move(out2)}; }
+    };
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   weakly_incrementable _Out1, weakly_incrementable _O2,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    requires indirectly_copyable<_Iter, _Out1>
+      && indirectly_copyable<_Iter, _O2>
+    constexpr partition_copy_result<_Iter, _Out1, _O2>
+    partition_copy(_Iter __first, _Sent __last,
+		   _Out1 __out_true, _O2 __out_false,
+		   _Pred __pred, _Proj __proj = {})
+    {
+      for (; __first != __last; ++__first)
+	if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  {
+	    *__out_true = *__first;
+	    ++__out_true;
+	  }
+	else
+	  {
+	    *__out_false = *__first;
+	    ++__out_false;
+	  }
+
+      return {std::move(__first), std::move(__out_true), std::move(__out_false)};
+    }
+
+  template<input_range _Range, weakly_incrementable _Out1,
+	   weakly_incrementable _O2,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires indirectly_copyable<iterator_t<_Range>, _Out1>
+      && indirectly_copyable<iterator_t<_Range>, _O2>
+    constexpr partition_copy_result<safe_iterator_t<_Range>, _Out1, _O2>
+    partition_copy(_Range&& __r, _Out1 out_true, _O2 out_false,
+		   _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::partition_copy(ranges::begin(__r), ranges::end(__r),
+				    std::move(out_true), std::move(out_false),
+				    std::move(__pred), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    constexpr _Iter
+    partition_point(_Iter __first, _Sent __last,
+		    _Pred __pred, _Proj __proj = {})
+    {
+      auto __len = ranges::distance(__first, __last);
+
+      while (__len > 0)
+	{
+	  auto __half = __len / 2;
+	  auto __middle = __first;
+	  ranges::advance(__middle, __half);
+	  if (std::__invoke(__pred, std::__invoke(__proj, *__middle)))
+	    {
+	      __first = __middle;
+	      ++__first;
+	      __len = __len - __half - 1;
+	    }
+	  else
+	    __len = __half;
+	}
+      return __first;
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    constexpr safe_iterator_t<_Range>
+    partition_point(_Range&& __r, _Pred __pred, _Proj __proj = {})
+    {
+      return ranges::partition_point(ranges::begin(__r), ranges::end(__r),
+				     std::move(__pred), std::move(__proj));
+    }
+
+  template<typename _Iter1, typename _Iter2, typename _Out>
+    using merge_result = binary_transform_result<_Iter1, _Iter2, _Out>;
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   weakly_incrementable _Out, typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<_Iter1, _Iter2, _Out, _Comp, _Proj1, _Proj2>
+    constexpr merge_result<_Iter1, _Iter2, _Out>
+    merge(_Iter1 __first1, _Sent1 __last1,
+	  _Iter2 __first2, _Sent2 __last2, _Out __result,
+	  _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      while (__first1 != __last1 && __first2 != __last2)
+	{
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj2, *__first2),
+			    std::__invoke(__proj1, *__first1)))
+	    {
+	      *__result = *__first2;
+	      ++__first2;
+	    }
+	  else
+	    {
+	      *__result = *__first1;
+	      ++__first1;
+	    }
+	  ++__result;
+	}
+      auto __copy1 = ranges::copy(std::move(__first1), std::move(__last1),
+				  std::move(__result));
+      auto __copy2 = ranges::copy(std::move(__first2), std::move(__last2),
+				  std::move(__copy1.out));
+      return { std::move(__copy1.in), std::move(__copy2.in),
+	       std::move(__copy2.out) };
+    }
+
+  template<input_range _Range1, input_range _Range2, weakly_incrementable _Out,
+	   typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<iterator_t<_Range1>, iterator_t<_Range2>, _Out,
+		       _Comp, _Proj1, _Proj2>
+    constexpr merge_result<safe_iterator_t<_Range1>,
+			   safe_iterator_t<_Range2>,
+			   _Out>
+    merge(_Range1&& __r1, _Range2&& __r2, _Out __result,
+	  _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::merge(ranges::begin(__r1), ranges::end(__r1),
+			   ranges::begin(__r2), ranges::end(__r2),
+			   std::move(__result), std::move(__comp),
+			   std::move(__proj1), std::move(__proj2));
+    }
+
+  template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less,
+	   typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    _Iter
+    inplace_merge(_Iter __first, _Iter __middle, _Sent __last,
+		  _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __lasti = ranges::next(__first, __last);
+      std::inplace_merge(std::move(__first), std::move(__middle), __lasti,
+			 __detail::__make_comp_proj(__comp, __proj));
+      return __lasti;
+    }
+
+  template<bidirectional_range _Range,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    safe_iterator_t<_Range>
+    inplace_merge(_Range&& __r, iterator_t<_Range> __middle,
+		  _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::inplace_merge(ranges::begin(__r), std::move(__middle),
+				   ranges::end(__r),
+				   std::move(__comp), std::move(__proj));
+    }
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   typename _Proj1 = identity, typename _Proj2 = identity,
+	   indirect_strict_weak_order<projected<_Iter1, _Proj1>,
+				      projected<_Iter2, _Proj2>>
+	     _Comp = ranges::less>
+    constexpr bool
+    includes(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	     _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      while (__first1 != __last1 && __first2 != __last2)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj2, *__first2),
+			  std::__invoke(__proj1, *__first1)))
+	  return false;
+	else if (std::__invoke(__comp,
+			       std::__invoke(__proj1, *__first1),
+			       std::__invoke(__proj2, *__first2)))
+	  ++__first1;
+	else
+	  {
+	    ++__first1;
+	    ++__first2;
+	  }
+
+      return __first2 == __last2;
+    }
+
+  template<input_range _Range1, input_range _Range2, typename _Proj1 = identity,
+	   typename _Proj2 = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range1>, _Proj1>,
+				      projected<iterator_t<_Range2>, _Proj2>>
+	     _Comp = ranges::less>
+    constexpr bool
+    includes(_Range1&& __r1, _Range2&& __r2, _Comp __comp = {},
+	     _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::includes(ranges::begin(__r1), ranges::end(__r1),
+			      ranges::begin(__r2), ranges::end(__r2),
+			      std::move(__comp),
+			      std::move(__proj1), std::move(__proj2));
+    }
+
+  template<typename _Iter1, typename _Iter2, typename _Out>
+    using set_union_result = binary_transform_result<_Iter1, _Iter2, _Out>;
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   weakly_incrementable _Out, typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<_Iter1, _Iter2, _Out, _Comp, _Proj1, _Proj2>
+    constexpr set_union_result<_Iter1, _Iter2, _Out>
+    set_union(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	      _Out __result, _Comp __comp = {},
+	      _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      while (__first1 != __last1 && __first2 != __last2)
+	{
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj1, *__first1),
+			    std::__invoke(__proj2, *__first2)))
+	    {
+	      *__result = *__first1;
+	      ++__first1;
+	    }
+	  else if (std::__invoke(__comp,
+				 std::__invoke(__proj2, *__first2),
+				 std::__invoke(__proj1, *__first1)))
+	    {
+	      *__result = *__first2;
+	      ++__first2;
+	    }
+	  else
+	    {
+	      *__result = *__first1;
+	      ++__first1;
+	      ++__first2;
+	    }
+	  ++__result;
+	}
+      auto __copy1 = ranges::copy(std::move(__first1), std::move(__last1),
+				  std::move(__result));
+      auto __copy2 = ranges::copy(std::move(__first2), std::move(__last2),
+				  std::move(__copy1.out));
+      return {std::move(__copy1.in), std::move(__copy2.in),
+	      std::move(__copy2.out)};
+    }
+
+  template<input_range _Range1, input_range _Range2, weakly_incrementable _Out,
+	   typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<iterator_t<_Range1>, iterator_t<_Range2>, _Out,
+		       _Comp, _Proj1, _Proj2>
+    constexpr set_union_result<safe_iterator_t<_Range1>,
+			       safe_iterator_t<_Range2>, _Out>
+    set_union(_Range1&& __r1, _Range2&& __r2, _Out __result, _Comp __comp = {},
+	      _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::set_union(ranges::begin(__r1), ranges::end(__r1),
+			       ranges::begin(__r2), ranges::end(__r2),
+			       std::move(__result), std::move(__comp),
+			       std::move(__proj1), std::move(__proj2));
+    }
+
+  template<typename _Iter1, typename _Iter2, typename _Out>
+    using set_intersection_result = binary_transform_result<_Iter1, _Iter2, _Out>;
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   weakly_incrementable _Out, typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<_Iter1, _Iter2, _Out, _Comp, _Proj1, _Proj2>
+    constexpr set_intersection_result<_Iter1, _Iter2, _Out>
+    set_intersection(_Iter1 __first1, _Sent1 __last1,
+		     _Iter2 __first2, _Sent2 __last2, _Out __result,
+		     _Comp __comp = {},
+		     _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      while (__first1 != __last1 && __first2 != __last2)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj1, *__first1),
+			  std::__invoke(__proj2, *__first2)))
+	  ++__first1;
+	else if (std::__invoke(__comp,
+			       std::__invoke(__proj2, *__first2),
+			       std::__invoke(__proj1, *__first1)))
+	  ++__first2;
+	else
+	  {
+	    *__result = *__first1;
+	    ++__first1;
+	    ++__first2;
+	    ++__result;
+	  }
+      // TODO: Eliminating these variables triggers an ICE.
+      auto __last1i = ranges::next(std::move(__first1), std::move(__last1));
+      auto __last2i = ranges::next(std::move(__first2), std::move(__last2));
+      return {std::move(__last1i), std::move(__last2i), std::move(__result)};
+    }
+
+  template<input_range _Range1, input_range _Range2, weakly_incrementable _Out,
+	   typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<iterator_t<_Range1>, iterator_t<_Range2>, _Out,
+		       _Comp, _Proj1, _Proj2>
+    constexpr set_intersection_result<safe_iterator_t<_Range1>,
+				      safe_iterator_t<_Range2>, _Out>
+    set_intersection(_Range1&& __r1, _Range2&& __r2, _Out __result,
+		     _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::set_intersection(ranges::begin(__r1), ranges::end(__r1),
+				      ranges::begin(__r2), ranges::end(__r2),
+				      std::move(__result), std::move(__comp),
+				      std::move(__proj1), std::move(__proj2));
+    }
+
+  template<typename _Iter, typename _Out>
+    using set_difference_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   weakly_incrementable _Out, typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<_Iter1, _Iter2, _Out, _Comp, _Proj1, _Proj2>
+    constexpr set_difference_result<_Iter1, _Out>
+    set_difference(_Iter1 __first1, _Sent1 __last1,
+		   _Iter2 __first2, _Sent2 __last2, _Out __result,
+		   _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      while (__first1 != __last1 && __first2 != __last2)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj1, *__first1),
+			  std::__invoke(__proj2, *__first2)))
+	  {
+	    *__result = *__first1;
+	    ++__first1;
+	    ++__result;
+	  }
+	else if (std::__invoke(__comp,
+			       std::__invoke(__proj2, *__first2),
+			       std::__invoke(__proj1, *__first1)))
+	  ++__first2;
+	else
+	  {
+	    ++__first1;
+	    ++__first2;
+	  }
+      return ranges::copy(std::move(__first1), std::move(__last1),
+			  std::move(__result));
+    }
+
+  template<input_range _Range1, input_range _Range2, weakly_incrementable _Out,
+	   typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<iterator_t<_Range1>, iterator_t<_Range2>, _Out,
+		       _Comp, _Proj1, _Proj2>
+    constexpr set_difference_result<safe_iterator_t<_Range1>, _Out>
+    set_difference(_Range1&& __r1, _Range2&& __r2, _Out __result,
+		   _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return ranges::set_difference(ranges::begin(__r1), ranges::end(__r1),
+				    ranges::begin(__r2), ranges::end(__r2),
+				    std::move(__result), std::move(__comp),
+				    std::move(__proj1), std::move(__proj2));
+    }
+
+  template<typename _Iter1, typename _Iter2, typename _Out>
+    using set_symmetric_difference_result
+      = binary_transform_result<_Iter1, _Iter2, _Out>;
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   weakly_incrementable _Out, typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<_Iter1, _Iter2, _Out, _Comp, _Proj1, _Proj2>
+    constexpr set_symmetric_difference_result<_Iter1, _Iter2, _Out>
+    set_symmetric_difference(_Iter1 __first1, _Sent1 __last1,
+			     _Iter2 __first2, _Sent2 __last2,
+			     _Out __result, _Comp __comp = {},
+			     _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      while (__first1 != __last1 && __first2 != __last2)
+	if (std::__invoke(__comp,
+			  std::__invoke(__proj1, *__first1),
+			  std::__invoke(__proj2, *__first2)))
+	  {
+	    *__result = *__first1;
+	    ++__first1;
+	    ++__result;
+	  }
+	else if (std::__invoke(__comp,
+			       std::__invoke(__proj2, *__first2),
+			       std::__invoke(__proj1, *__first1)))
+	  {
+	    *__result = *__first2;
+	    ++__first2;
+	    ++__result;
+	  }
+	else
+	  {
+	    ++__first1;
+	    ++__first2;
+	  }
+      auto __copy1 = ranges::copy(std::move(__first1), std::move(__last1),
+				  std::move(__result));
+      auto __copy2 = ranges::copy(std::move(__first2), std::move(__last2),
+				  std::move(__copy1.out));
+      return {std::move(__copy1.in), std::move(__copy2.in),
+	      std::move(__copy2.out)};
+    }
+
+  template<input_range _Range1, input_range _Range2, weakly_incrementable _Out,
+	   typename _Comp = ranges::less,
+	   typename _Proj1 = identity, typename _Proj2 = identity>
+    requires mergeable<iterator_t<_Range1>, iterator_t<_Range2>, _Out,
+		       _Comp, _Proj1, _Proj2>
+    constexpr set_symmetric_difference_result<safe_iterator_t<_Range1>,
+					      safe_iterator_t<_Range2>,
+					      _Out>
+    set_symmetric_difference(_Range1&& __r1, _Range2&& __r2, _Out __result,
+			     _Comp __comp = {},
+			     _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return (ranges::set_symmetric_difference
+	      (ranges::begin(__r1), ranges::end(__r1),
+	       ranges::begin(__r2), ranges::end(__r2),
+	       std::move(__result), std::move(__comp),
+	       std::move(__proj1), std::move(__proj2)));
+    }
+
+  template<typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	     _Comp = ranges::less>
+    constexpr const _Tp&
+    min(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (std::__invoke(std::move(__comp),
+			std::__invoke(__proj, __b),
+			std::__invoke(__proj, __a)))
+	return __b;
+      else
+	return __a;
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    requires indirectly_copyable_storable<iterator_t<_Range>,
+					  range_value_t<_Range>*>
+    constexpr range_value_t<_Range>
+    min(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __first = ranges::begin(__r);
+      auto __last = ranges::end(__r);
+      __glibcxx_assert(__first != __last);
+      auto __result = *__first;
+      while (++__first != __last)
+	{
+	  auto __tmp = *__first;
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, __tmp),
+			    std::__invoke(__proj, __result)))
+	    __result = std::move(__tmp);
+	}
+      return __result;
+    }
+
+  template<copyable _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Tp
+    min(initializer_list<_Tp> __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::min(ranges::subrange(__r),
+			 std::move(__comp), std::move(__proj));
+    }
+
+  template<typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	     _Comp = ranges::less>
+    constexpr const _Tp&
+    max(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (std::__invoke(std::move(__comp),
+			std::__invoke(__proj, __a),
+			std::__invoke(__proj, __b)))
+	return __b;
+      else
+	return __a;
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    requires indirectly_copyable_storable<iterator_t<_Range>,
+					  range_value_t<_Range>*>
+    constexpr range_value_t<_Range>
+    max(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __first = ranges::begin(__r);
+      auto __last = ranges::end(__r);
+      __glibcxx_assert(__first != __last);
+      auto __result = *__first;
+      while (++__first != __last)
+	{
+	  auto __tmp = *__first;
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, __result),
+			    std::__invoke(__proj, __tmp)))
+	    __result = std::move(__tmp);
+	}
+      return __result;
+    }
+
+  template<copyable _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Tp
+    max(initializer_list<_Tp> __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::max(ranges::subrange(__r),
+			 std::move(__comp), std::move(__proj));
+    }
+
+  template<typename _Tp>
+    struct minmax_result
+    {
+      [[no_unique_address]] _Tp min;
+      [[no_unique_address]] _Tp max;
+
+      template<typename _Tp2>
+	requires convertible_to<const _Tp&, _Tp2>
+	operator minmax_result<_Tp2>() const &
+	{ return {min, max}; }
+
+      template<typename _Tp2>
+	requires convertible_to<_Tp, _Tp2>
+	operator minmax_result<_Tp2>() &&
+	{ return {std::move(min), std::move(max)}; }
+    };
+
+  template<typename _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	     _Comp = ranges::less>
+    constexpr minmax_result<const _Tp&>
+    minmax(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (std::__invoke(std::move(__comp),
+			std::__invoke(__proj, __b),
+			std::__invoke(__proj, __a)))
+	return {__b, __a};
+      else
+	return {__a, __b};
+    }
+
+  template<input_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    requires indirectly_copyable_storable<iterator_t<_Range>,
+    range_value_t<_Range>*>
+    constexpr minmax_result<range_value_t<_Range>>
+    minmax(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      auto __first = ranges::begin(__r);
+      auto __last = ranges::end(__r);
+      __glibcxx_assert(__first != __last);
+      minmax_result<range_value_t<_Range>> __result = {*__first, *__first};
+      while (++__first != __last)
+	{
+	  auto __tmp = *__first;
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, __tmp),
+			    std::__invoke(__proj, __result.min)))
+	    __result.min = std::move(__tmp);
+	  if (!(bool)std::__invoke(__comp,
+				   std::__invoke(__proj, __tmp),
+				   std::__invoke(__proj, __result.max)))
+	    __result.max = std::move(__tmp);
+	}
+      return __result;
+    }
+
+  template<copyable _Tp, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	     _Comp = ranges::less>
+    constexpr minmax_result<_Tp>
+    minmax(initializer_list<_Tp> __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::minmax(ranges::subrange(__r),
+			    std::move(__comp), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Iter
+    min_element(_Iter __first, _Sent __last,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return __first;
+
+      auto __i = __first;
+      while (++__i != __last)
+	{
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, *__i),
+			    std::__invoke(__proj, *__first)))
+	    __first = __i;
+	}
+      return __first;
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr safe_iterator_t<_Range>
+    min_element(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::min_element(ranges::begin(__r), ranges::end(__r),
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr _Iter
+    max_element(_Iter __first, _Sent __last,
+		_Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return __first;
+
+      auto __i = __first;
+      while (++__i != __last)
+	{
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, *__first),
+			    std::__invoke(__proj, *__i)))
+	    __first = __i;
+	}
+      return __first;
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr safe_iterator_t<_Range>
+    max_element(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::max_element(ranges::begin(__r), ranges::end(__r),
+				 std::move(__comp), std::move(__proj));
+    }
+
+  template<typename _Iter>
+    using minmax_element_result = minmax_result<_Iter>;
+
+  template<forward_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Proj = identity,
+	   indirect_strict_weak_order<projected<_Iter, _Proj>>
+	     _Comp = ranges::less>
+    constexpr minmax_element_result<_Iter>
+    minmax_element(_Iter __first, _Sent __last,
+		   _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return {__first, __first};
+
+      minmax_element_result<_Iter> __result = {__first, __first};
+      auto __i = __first;
+      while (++__i != __last)
+	{
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, *__i),
+			    std::__invoke(__proj, *__result.min)))
+	    __result.min = __i;
+	  if (!(bool)std::__invoke(__comp,
+				   std::__invoke(__proj, *__i),
+				   std::__invoke(__proj, *__result.max)))
+	    __result.max = __i;
+	}
+      return __result;
+    }
+
+  template<forward_range _Range, typename _Proj = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+	     _Comp = ranges::less>
+    constexpr minmax_element_result<safe_iterator_t<_Range>>
+    minmax_element(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::minmax_element(ranges::begin(__r), ranges::end(__r),
+				    std::move(__comp), std::move(__proj));
+    }
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   typename _Proj1, typename _Proj2,
+	   indirect_strict_weak_order<projected<_Iter1, _Proj1>,
+				      projected<_Iter2, _Proj2>> _Comp>
+    constexpr bool
+    __lexicographical_compare(_Iter1 __first1, _Sent1 __last1,
+			      _Iter2 __first2, _Sent2 __last2,
+			      _Comp __comp, _Proj1 __proj1, _Proj2 __proj2)
+    {
+      constexpr bool __sized_iters
+	= (sized_sentinel_for<_Sent1, _Iter1>
+	   && sized_sentinel_for<_Sent2, _Iter2>);
+      if constexpr (__sized_iters)
+	{
+	  auto __d1 = ranges::distance(__first1, __last1);
+	  auto __d2 = ranges::distance(__first2, __last2);
+
+	  using _ValueType1 = iter_value_t<_Iter1>;
+	  using _ValueType2 = iter_value_t<_Iter2>;
+	  constexpr bool __use_memcmp
+	    = ((is_integral_v<_ValueType1> || is_pointer_v<_ValueType1>)
+	       && is_same_v<_ValueType1, _ValueType2>
+	       && is_pointer_v<_Iter1>
+	       && is_pointer_v<_Iter2>
+	       && (is_same_v<_Comp, ranges::less>
+		   || is_same_v<_Comp, ranges::greater>)
+	       && is_same_v<_Proj1, identity>
+	       && is_same_v<_Proj2, identity>);
+	  if constexpr (__use_memcmp)
+	    {
+	      if (const auto __len = std::min(__d1, __d2))
+		{
+		  const auto __c = std::__memcmp(__first1, __first2, __len);
+		  if constexpr (is_same_v<_Comp, ranges::less>)
+		    {
+		      if (__c < 0)
+			return true;
+		      if (__c > 0)
+			return false;
+		    }
+		  else if constexpr (is_same_v<_Comp, ranges::greater>)
+		    {
+		      if (__c > 0)
+			return true;
+		      if (__c < 0)
+			return false;
+		    }
+		  else
+		    __builtin_unreachable();
+		}
+	      return (__last1 - __first1 < __last2 - __first2);
+	    }
+	}
+
+      for (; __first1 != __last1 && __first2 != __last2;
+	   ++__first1, (void) ++__first2)
+	{
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj1, *__first1),
+			    std::__invoke(__proj2, *__first2)))
+	    return true;
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj2, *__first2),
+			    std::__invoke(__proj1, *__first1)))
+	    return false;
+	}
+      return __first1 == __last1 && __first2 != __last2;
+    }
+
+  template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	   typename _Proj1 = identity, typename _Proj2 = identity,
+	   indirect_strict_weak_order<projected<_Iter1, _Proj1>,
+				      projected<_Iter2, _Proj2>>
+	     _Comp = ranges::less>
+    constexpr bool
+    lexicographical_compare(_Iter1 __first1, _Sent1 __last1,
+			    _Iter2 __first2, _Sent2 __last2,
+			    _Comp __comp = {},
+			    _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return (ranges::__lexicographical_compare
+	      (std::__niter_base(std::move(__first1)),
+	       std::__niter_base(std::move(__last1)),
+	       std::__niter_base(std::move(__first2)),
+	       std::__niter_base(std::move(__last2)),
+	       std::move(__comp),
+	       std::move(__proj1), std::move(__proj2)));
+    }
+
+  template<input_range _Range1, input_range _Range2, typename _Proj1 = identity,
+	   typename _Proj2 = identity,
+	   indirect_strict_weak_order<projected<iterator_t<_Range1>, _Proj1>,
+				      projected<iterator_t<_Range2>, _Proj2>>
+	     _Comp = ranges::less>
+    constexpr bool
+    lexicographical_compare(_Range1&& __r1, _Range2&& __r2, _Comp __comp = {},
+			    _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+    {
+      return (ranges::lexicographical_compare
+	      (ranges::begin(__r1), ranges::end(__r1),
+	       ranges::begin(__r2), ranges::end(__r2),
+	       std::move(__comp),
+	       std::move(__proj1), std::move(__proj2)));
+    }
+
+  template<typename _Iter>
+    struct next_permutation_result
+    {
+      bool found;
+      _Iter in;
+    };
+
+  template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr next_permutation_result<_Iter>
+    next_permutation(_Iter __first, _Sent __last,
+		     _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return {false, std::move(__first)};
+
+      auto __i = __first;
+      ++__i;
+      if (__i == __last)
+	return {false, std::move(__i)};
+
+      auto __lasti = ranges::next(__first, __last);
+      __i = __lasti;
+      --__i;
+
+      for (;;)
+	{
+	  auto __ii = __i;
+	  --__i;
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, *__i),
+			    std::__invoke(__proj, *__ii)))
+	    {
+	      auto __j = __lasti;
+	      while (!(bool)std::__invoke(__comp,
+					  std::__invoke(__proj, *__i),
+					  std::__invoke(__proj, *--__j)))
+		;
+	      ranges::iter_swap(__i, __j);
+	      ranges::reverse(__ii, __last);
+	      return {true, std::move(__lasti)};
+	    }
+	  if (__i == __first)
+	    {
+	      ranges::reverse(__first, __last);
+	      return {false, std::move(__lasti)};
+	    }
+	}
+    }
+
+  template<bidirectional_range _Range, typename _Comp = ranges::less,
+	   typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr next_permutation_result<safe_iterator_t<_Range>>
+    next_permutation(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::next_permutation(ranges::begin(__r), ranges::end(__r),
+				      std::move(__comp), std::move(__proj));
+    }
+
+  template<typename _Iter>
+    using prev_permutation_result = next_permutation_result<_Iter>;
+
+  template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   typename _Comp = ranges::less, typename _Proj = identity>
+    requires sortable<_Iter, _Comp, _Proj>
+    constexpr prev_permutation_result<_Iter>
+    prev_permutation(_Iter __first, _Sent __last,
+		     _Comp __comp = {}, _Proj __proj = {})
+    {
+      if (__first == __last)
+	return {false, std::move(__first)};
+
+      auto __i = __first;
+      ++__i;
+      if (__i == __last)
+	return {false, std::move(__i)};
+
+      auto __lasti = ranges::next(__first, __last);
+      __i = __lasti;
+      --__i;
+
+      for (;;)
+	{
+	  auto __ii = __i;
+	  --__i;
+	  if (std::__invoke(__comp,
+			    std::__invoke(__proj, *__ii),
+			    std::__invoke(__proj, *__i)))
+	    {
+	      auto __j = __lasti;
+	      while (!(bool)std::__invoke(__comp,
+					  std::__invoke(__proj, *--__j),
+					  std::__invoke(__proj, *__i)))
+		;
+	      ranges::iter_swap(__i, __j);
+	      ranges::reverse(__ii, __last);
+	      return {true, std::move(__lasti)};
+	    }
+	  if (__i == __first)
+	    {
+	      ranges::reverse(__first, __last);
+	      return {false, std::move(__lasti)};
+	    }
+	}
+    }
+
+  template<bidirectional_range _Range, typename _Comp = ranges::less,
+	   typename _Proj = identity>
+    requires sortable<iterator_t<_Range>, _Comp, _Proj>
+    constexpr prev_permutation_result<safe_iterator_t<_Range>>
+    prev_permutation(_Range&& __r, _Comp __comp = {}, _Proj __proj = {})
+    {
+      return ranges::prev_permutation(ranges::begin(__r), ranges::end(__r),
+				      std::move(__comp), std::move(__proj));
+    }
+
+} // namespace ranges
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // concepts
+#endif // C++20
+#endif // _RANGES_ALGO_H
diff --git a/libstdc++-v3/include/std/algorithm b/libstdc++-v3/include/std/algorithm
index e3d34024b73..4b956b89403 100644
--- a/libstdc++-v3/include/std/algorithm
+++ b/libstdc++-v3/include/std/algorithm
@@ -60,6 +60,9 @@
 #include <utility> // UK-300.
 #include <bits/stl_algobase.h>
 #include <bits/stl_algo.h>
+#if __cplusplus > 201703L
+# include <bits/ranges_algo.h>
+#endif
 
 #if __cplusplus > 201402L
 // Parallel STL algorithms
diff --git a/libstdc++-v3/testsuite/25_algorithms/adjacent_find/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/adjacent_find/constrained.cc
new file mode 100644
index 00000000000..d56ac5a19db
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/adjacent_find/constrained.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  int y[] = { 2, 7, 8, 8, 9 };
+
+  VERIFY( ranges::adjacent_find(x, x+6, {}, &X::i) == x+0 );
+  VERIFY( ranges::adjacent_find(x+1, x+6, {}, &X::i) == x+6 );
+  VERIFY( ranges::adjacent_find(y) == y+2 );
+  VERIFY( ranges::adjacent_find(y, y+4) == y+2 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  VERIFY( ranges::adjacent_find(c, {}, &X::i) == ranges::begin(c) );
+
+  test_range<int, forward_iterator_wrapper> r(y);
+  auto res = ranges::adjacent_find(r);
+  VERIFY( *res == 8 && *++res == 8 );
+}
+
+void
+test02()
+{
+  static constexpr X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  static constexpr X y[] = { {2}, {6}, {8}, {10}, {11} };
+  static_assert(ranges::adjacent_find(x, {}, &X::i) == x+0);
+  static_assert(ranges::adjacent_find(y, {}, &X::i) == y+5);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
+
diff --git a/libstdc++-v3/testsuite/25_algorithms/all_of/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/all_of/constrained.cc
new file mode 100644
index 00000000000..a472dd0750a
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/all_of/constrained.cc
@@ -0,0 +1,90 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+struct XLess
+{
+  int val;
+  bool operator()(X& x) const { return x.i < val; }
+};
+
+struct ILess
+{
+  int val;
+  bool operator()(int& i) const { return i < val; }
+};
+
+template<typename T>
+struct NotZero
+{
+  bool operator()(T& t) const { return t != 0; }
+};
+
+void
+test01()
+{
+  X x[] = { {2}, {4}, {6}, {8}, {10}, {11} };
+
+  VERIFY( ranges::all_of(x, x+5, XLess{11}) );
+  VERIFY( ranges::all_of(x, x+5, ILess{11}, &X::i) );
+  VERIFY( !ranges::all_of(x, x+6, ILess{11}, &X::i) );
+  VERIFY( !ranges::all_of(x, XLess{11}) );
+  VERIFY( ranges::all_of(x, XLess{12}) );
+  VERIFY( ranges::all_of(x, ILess{12}, &X::i) );
+  VERIFY( !ranges::all_of(x, ILess{11}, &X::i) );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  VERIFY( ranges::all_of(c, NotZero<int>{}, &X::i) );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  VERIFY( ranges::all_of(r, NotZero<int>{}, &X::i) );
+
+  r.bounds.first = x;
+  VERIFY( ranges::all_of(r, NotZero<X* const>{}, [](X& x) { return &x; }) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6} };
+  static_assert(ranges::all_of(y, [](int j) { return j%2 == 0; }, &Y::j));
+  static_assert(ranges::all_of(y, [](const Y& y) { return y.j == y.i * 2; }));
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/any_of/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/any_of/constrained.cc
new file mode 100644
index 00000000000..b234692d2c7
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/any_of/constrained.cc
@@ -0,0 +1,88 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+struct XLess
+{
+  int val;
+  bool operator()(X& x) const { return x.i < val; }
+};
+
+struct ILess
+{
+  int val;
+  bool operator()(int& i) const { return i < val; }
+};
+
+template<typename T>
+struct NotZero
+{
+  bool operator()(T& t) const { return t != 0; }
+};
+
+void
+test01()
+{
+  X x[] = { {2}, {4}, {6}, {8}, {10}, {11} };
+
+  VERIFY( ranges::any_of(x, x+6, XLess{3}) );
+  VERIFY( ranges::any_of(x, x+6, ILess{3}, &X::i) );
+  VERIFY( !ranges::any_of(x+1, x+6, XLess{3}) );
+  VERIFY( !ranges::any_of(x+1, x+6, ILess{3}, &X::i) );
+  VERIFY( ranges::any_of(x, XLess{5}) );
+  VERIFY( ranges::any_of(x, ILess{5}, &X::i) );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  VERIFY( ranges::any_of(c, NotZero<int>{}, &X::i) );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  VERIFY( ranges::any_of(r, NotZero<int>{}, &X::i) );
+  VERIFY( ranges::any_of(r, NotZero<X* const>{}, [](X& x) { return &x; }) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6} };
+  static_assert(ranges::any_of(y, [](int i) { return i%2 == 0; }, &Y::i));
+  static_assert(ranges::any_of(y, [](const Y& y) { return y.i + y.j == 3; }));
+  static_assert(!ranges::any_of(y, [](const Y& y) { return y.i == y.j; }));
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/binary_search/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/binary_search/constrained.cc
new file mode 100644
index 00000000000..42aaa8ef2f7
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/binary_search/constrained.cc
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  float x[] = {1, 2, 3, 4, 5, 5, 6, 7};
+  test_container<float, forward_iterator_wrapper> cx(x);
+  for (int i = 0; i < 7; i++)
+    {
+      VERIFY( ranges::binary_search(cx, i, {}, [] (int a) { return a-1; }) );
+      VERIFY( !ranges::binary_search(cx.begin(), cx.end(), i+0.5) );
+    }
+  VERIFY( !ranges::binary_search(cx, 0) );
+
+  ranges::reverse(x);
+  test_range<float, forward_iterator_wrapper> rx(x);
+  VERIFY( ranges::binary_search(rx, 5, ranges::greater{}) );
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1, 2, 3};
+  return (ranges::binary_search(x, 3)
+	  && !ranges::binary_search(x, x, 3));
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc
new file mode 100644
index 00000000000..85f7d649608
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc
@@ -0,0 +1,225 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+    {
+      int x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      int y[7] = { 0 };
+      int z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      auto [in, out] = ranges::copy(x, y);
+      VERIFY( ranges::equal(x, y) && in == x+7 && out == y+7 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      char y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_container<int, forward_iterator_wrapper> cx(x);
+      test_container<char, forward_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::copy(cx, ranges::begin(cy));
+      VERIFY( ranges::equal(x, x+3, y, y+3) && in.ptr == x+3 && out.ptr == y+3 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      char x[3] = { 1, 2, 3 };
+      int y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_range<char, input_iterator_wrapper> rx(x);
+      test_range<int, output_iterator_wrapper> ry(y);
+      auto [in, out] = ranges::copy(rx, ranges::begin(ry));
+      VERIFY( ranges::equal(x, x+3, y, y+3) && in.ptr == x+3 && out.ptr == y+3 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+}
+
+struct X
+{
+  int i;
+  constexpr X (int a) : i(a) { }
+};
+
+void
+test02()
+{
+  int x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  int z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::copy(x, y);
+  VERIFY( ranges::equal(x, x+5, y, y+5, {}, {}, &X::i) );
+  VERIFY( in == x+5 );
+  VERIFY( out == y+5 );
+  VERIFY( y[5].i == 2 );
+  VERIFY( ranges::equal(x, z) );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  int x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  int z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::copy(x, y);
+  ok &= ranges::equal(x, x+5, y, y+5, {}, {}, &X::i);
+  ok &= (in == x+5);
+  ok &= (out == y+5);
+  ok &= (y[5].i == 2);
+  ok &= ranges::equal(x, z);
+  return ok;
+}
+
+struct Y
+{
+  int i;
+  int moved = 0;
+
+  constexpr Y(int a) : i(a) { }
+
+  constexpr Y(const Y&) = delete;
+  constexpr Y& operator=(const Y&) = delete;
+
+  constexpr Y(Y&& other)
+  {
+    *this = std::move(other);
+  }
+
+  constexpr Y&
+  operator=(Y&& other)
+  {
+    other.moved++;
+    i = other.i;
+    return *this;
+  }
+
+  friend constexpr bool
+  operator==(const Y& a, const Y& b)
+  { return a.i == b.i; }
+};
+
+void
+test04()
+{
+  Y x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  Y y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+  Y z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  test_range<Y, input_iterator_wrapper> rx(x);
+  auto [in, out] = ranges::copy(std::move_iterator{ranges::begin(rx)},
+				std::move_sentinel{ranges::end(rx)},
+				ranges::begin(y));
+  VERIFY( ranges::equal(x, y) && std::move(in).base().ptr == x+7 && out == y+7 );
+  VERIFY( ranges::equal(x, z) );
+  for (const auto& v : x)
+    VERIFY( v.moved == 1 );
+  for (const auto& v : y)
+    VERIFY( v.moved == 0 );
+}
+
+constexpr bool
+test05()
+{
+  bool ok = true;
+  Y x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  Y y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+  Y z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  auto [in, out] = ranges::copy(std::move_iterator{ranges::begin(x)},
+				std::move_sentinel{ranges::end(x)},
+				ranges::begin(y));
+  ok &= ranges::equal(x, y);
+  ok &= in.base() == x+7;
+  ok &= out == y+7;
+  ok &= ranges::equal(x, z);
+  for (const auto& v : x)
+    ok &= v.moved == 1;
+  for (const auto& v : y)
+    ok &= v.moved == 0;
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+  test04();
+  static_assert(test05());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_backward/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/copy_backward/constrained.cc
new file mode 100644
index 00000000000..900f78aaa73
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_backward/constrained.cc
@@ -0,0 +1,193 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+    {
+      int x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      int y[7] = { 0 };
+      int z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+      VERIFY( ranges::equal(x, y) && in == x+7 && out == y);
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      char y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_container<int, bidirectional_iterator_wrapper> cx(x);
+      test_container<char, bidirectional_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::copy_backward(cx, ranges::end(cy));
+      VERIFY( ranges::equal(x, x+3, y+1, y+4) && in.ptr == x+3 && out.ptr == y+1 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  int x[] = { {2}, {2}, {6}, {8}, {10} };
+  int y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  const int z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+  ok &= ranges::equal(x, x+5, y+1, y+6);
+  ok &= (in == x+5);
+  ok &= (out == y+1);
+  ok &= (y[0] == 2);
+  ok &= ranges::equal(x, z);
+  return ok;
+}
+
+/*  move_iterators are always input_iterators and therefore do not model
+ *  bidirectional_iterator, so I think the following tests are rightly invalid.
+
+struct Y
+{
+  int i;
+  int moved = 0;
+
+  constexpr Y(int a) : i(a) { }
+
+  constexpr Y(const Y&) = delete;
+  constexpr Y& operator=(const Y&) = delete;
+
+  constexpr Y(Y&& other)
+  {
+    *this = std::move(other);
+  }
+
+  constexpr Y&
+  operator=(Y&& other)
+  {
+    other.moved++;
+    i = other.i;
+    return *this;
+  }
+
+  friend constexpr bool
+  operator==(const Y& a, const Y& b)
+  { return a.i == b.i; }
+};
+
+void
+test02()
+{
+  Y x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  Y y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+  Y z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  test_range<Y, bidirectional_iterator_wrapper> rx(x);
+  auto [in, out] = ranges::copy_backward(std::move_iterator{ranges::begin(rx)},
+					 std::move_sentinel{ranges::end(rx)},
+					 ranges::end(y));
+  VERIFY( ranges::equal(x, y) && std::move(in).base().ptr == x+7 && out == y );
+  VERIFY( ranges::equal(x, z) );
+  for (const auto& v : x)
+    VERIFY( v.moved == 1 );
+  for (const auto& v : y)
+    VERIFY( v.moved == 0 );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  Y x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  Y y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+  Y z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  auto [in, out] = ranges::copy_backward(std::move_iterator{ranges::begin(x)},
+					 std::move_sentinel{ranges::end(x)},
+					 ranges::end(y));
+  ok &= ranges::equal(x, y);
+  ok &= in.base() == x+7;
+  ok &= out == y;
+  ok &= ranges::equal(x, z);
+  for (const auto& v : x)
+    ok &= v.moved == 1;
+  for (const auto& v : y)
+    ok &= v.moved == 0;
+  return ok;
+}
+*/
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_if/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/copy_if/constrained.cc
new file mode 100644
index 00000000000..8a92d227f16
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_if/constrained.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5,6,7};
+
+    {
+      const int y[] = {2,4,6};
+      int w[7];
+      test_range<int, input_iterator_wrapper> rx(x);
+      test_range<int, output_iterator_wrapper> rw(w);
+      auto [in,out] = ranges::copy_if(rx, rw.begin(),
+				      [] (int a) { return (a%2)==0; });
+      VERIFY( in == rx.end() && out.ptr == w+3 );
+      VERIFY( ranges::equal(w, w+3, y, y+3) );
+    }
+
+    {
+      const int y[] = {1,3,5,7};
+      int w[7];
+      test_range<int, input_iterator_wrapper> rx(x);
+      test_range<int, output_iterator_wrapper> rw(w);
+      auto [in,out] = ranges::copy_if(rx, rw.begin(),
+				      [] (int a) { return (a%2)==0; },
+				      [] (int a) { return a+1; });
+      VERIFY( in == rx.end() && out.ptr == w+4 );
+      VERIFY( ranges::equal(w, w+4, y, y+4) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1,2,3};
+  const int y[] = {1,3};
+  int w[3];
+  auto [in,out] = ranges::copy_if(x, w, [] (int a) { return (a%2)==1; });
+  return ranges::equal(w, out, y, y+2);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_n/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/copy_n/constrained.cc
new file mode 100644
index 00000000000..78a4539826a
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_n/constrained.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+template<template<typename> typename in_wrapper,
+	 template<typename> typename out_wrapper>
+void
+test01()
+{
+  int x[] = {1,2,3,4,5,6,7};
+  for (int i = -1; i <= 7; i++)
+    {
+      test_range<int, in_wrapper> rx(x);
+      int w[7];
+      test_range<int, out_wrapper> rw(w);
+      ranges::copy_n(rx.begin(), i, rw.begin());
+      if (i >= 0)
+	VERIFY( ranges::equal(x, x+i, w, w+i) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1,2,3};
+  int y[2];
+  auto [in,out] = ranges::copy_n(x, 2, y);
+  return (in == x+2
+	  && out == y+2
+	  && ranges::equal(x, x+2, y, y+2));
+}
+
+int
+main()
+{
+  test01<input_iterator_wrapper,
+	 output_iterator_wrapper>();
+  test01<random_access_iterator_wrapper,
+	 output_iterator_wrapper>();
+  test01<random_access_iterator_wrapper,
+	 random_access_iterator_wrapper>();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/count/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/count/constrained.cc
new file mode 100644
index 00000000000..2a9bb27de5e
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/count/constrained.cc
@@ -0,0 +1,75 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10}, {11}, {2} };
+  auto res = ranges::count(x, x+7, 2, &X::i);
+  VERIFY( res == 3 );
+  res = ranges::count(x, x+7, 8, &X::i);
+  VERIFY( res == 1 );
+  res = ranges::count(x, x+7, 9, &X::i);
+  VERIFY( res == 0 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  res = ranges::count(c, 6, &X::i);
+  VERIFY( res == 1 );
+  res = ranges::count(c, 9, &X::i);
+  VERIFY( res == 0 );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  res = ranges::count(r, 2, &X::i);
+  VERIFY( res == 3 );
+
+  r.bounds.first = x;
+  res = ranges::count(r, 9, &X::i);
+  VERIFY( res == 0 );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6}, {1,6} };
+  static_assert(ranges::count(y, 6, &Y::j) == 2);
+  static_assert(ranges::count(y, 5, &Y::j) == 0);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/count_if/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/count_if/constrained.cc
new file mode 100644
index 00000000000..79cdae31826
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/count_if/constrained.cc
@@ -0,0 +1,73 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10}, {11}, {2} };
+  auto res = ranges::count_if(x, x+7, [] (int i) { return i % 2 == 0; }, &X::i);
+  VERIFY( res == 6 );
+  res = ranges::count_if(x, x+7, [] (int i) { return i % 2 == 1; }, &X::i);
+  VERIFY( res == 1 );
+  res = ranges::count_if(x, x+7, [] (int i) { return i < 0; }, &X::i);
+  VERIFY( res == 0 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  res = ranges::count_if(c, [] (int i) { return i == 2; }, &X::i);
+  VERIFY( res == 3 );
+  res = ranges::count_if(c, [] (int i) { return i < 0; }, &X::i);
+  VERIFY( res == 0 );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  res = ranges::count_if(c, [] (int i) { return i == 2; }, &X::i);
+  VERIFY( res == 3 );
+  res = ranges::count_if(c, [] (int i) { return i < 0; }, &X::i);
+  VERIFY( res == 0 );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6}, {1,6} };
+  static_assert(ranges::count_if(y, [] (int i) { return i < 5; }, &Y::j) == 2);
+  static_assert(ranges::count_if(y, [] (int i) { return i != 4; }, &Y::j) == 3);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc
new file mode 100644
index 00000000000..231bd8cfeaa
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc
@@ -0,0 +1,96 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  int x[] = { {2}, {2}, {6}, {8}, {10}, {11}, {11} };
+  int y[] = { {2}, {2}, {6}, {8}, {10}, {11}, {11} };
+  X   z[] = { {2}, {6}, {8}, {10}, {2}, {2} };
+  int w[] = { {1}, {1}, {1}, {1}, {1} };
+
+  VERIFY( ranges::equal(w, w+4, w+1, w+5) );
+  VERIFY( ranges::equal(w, w+5, w, w+5, ranges::greater{},
+			[] (int a) { return a+1; }) );
+
+  test_container<int, forward_iterator_wrapper> cx(x), cy(y);
+  test_container<X, forward_iterator_wrapper> cz(z);
+  VERIFY( ranges::equal(cx, cy) );
+  VERIFY( !ranges::equal(cx, cy, {}, [] (int a) { return a+1; }) );
+  VERIFY( !ranges::equal(cx, cz, {}, {}, &X::i) );
+
+  test_range<int, input_iterator_wrapper> rx(x), ry(y);
+  test_range<X, input_iterator_wrapper> rz(z);
+  VERIFY( ranges::equal(rx, ry) );
+
+  rx.bounds.first = x;
+  ry.bounds.first = y;
+  VERIFY( !ranges::equal(rx, ry, {}, {}, [] (int a) { return a+1; }) );
+
+  rx.bounds.first = x;
+  rz.bounds.first = z;
+  VERIFY( !ranges::equal(rx, rz, {}, {}, &X::i) );
+}
+
+void
+test02()
+{
+  static constexpr X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  static constexpr X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  static constexpr int z[] = { {2}, {6}, {8}, {10}, {2}, {2} };
+  static constexpr int w[] = { {2}, {6}, {8}, {10}, {2}, {2} };
+
+  static_assert(ranges::equal(z, w));
+  static_assert(!ranges::equal(z, z+5, w+1, w+6));
+  static_assert(!ranges::equal(z, z, {}, {}, [] (int a) { return a+1; }));
+  static_assert(!ranges::equal(x, y, {}, &X::i, &X::i));
+}
+
+void
+test03()
+{
+  std::vector<int> x = { {2}, {2}, {6}, {8}, {10}, {11} };
+  std::vector<int> y = { {2}, {2}, {6}, {8}, {10}, {11} };
+  std::vector<int> z = { {2}, {2}, {6}, {8}, {10}, {12} };
+  VERIFY( ranges::equal(x, y) );
+  VERIFY( !ranges::equal(x, z) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/equal_range/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/equal_range/constrained.cc
new file mode 100644
index 00000000000..4ddf4590c21
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/equal_range/constrained.cc
@@ -0,0 +1,69 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1, 2, 3, 4, 5, 5, 6, 7};
+  for (unsigned i = 0; i < 5; i++)
+    for (unsigned j = 6; j < 8; j++)
+      {
+	test_container<int, forward_iterator_wrapper> cx(x);
+	auto range = ranges::equal_range(std::next(cx.begin(), i),
+					 std::next(cx.begin(), j),
+					 4, {}, [] (int a) { return a-1; });
+	VERIFY( range.begin().ptr == x+4 && range.end().ptr == x+6 );
+      }
+
+  ranges::reverse(x);
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto range = ranges::equal_range(rx, 5, ranges::greater{},
+				   [] (int a) { return a+1; });
+  VERIFY( range.begin().ptr == x+4 && range.end().ptr == x+5 );
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  auto range1 = ranges::equal_range(x, 6);
+  auto range2 = ranges::equal_range(x, x, 6);
+  auto range3 = ranges::equal_range(x, 1);
+  return (range1.begin() == x+5 && range1.end() == x+5
+	  && range2.begin() == x && range2.end() == x
+	  && range3.begin() == x && range3.end() == x+1);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/fill/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/fill/constrained.cc
new file mode 100644
index 00000000000..4813b8302ce
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/fill/constrained.cc
@@ -0,0 +1,92 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <list>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+};
+
+void
+test01()
+{
+  const int c[6] = { 17, 17, 17, 17, 17, 17 };
+    {
+      X x[6];
+      VERIFY( ranges::fill(x, X{17}) == x+6 );
+      VERIFY( ranges::equal(x, c, {}, &X::i) );
+    }
+
+    {
+      char x[6];
+      VERIFY( ranges::fill(x, 17) == x+6 );
+      VERIFY( ranges::equal(x, c) );
+    }
+
+    {
+      X x[6];
+      test_container<X, forward_iterator_wrapper> cx(x);
+      VERIFY( ranges::fill(cx, X{17}) == cx.end() );
+      VERIFY( ranges::equal(cx, c, {}, &X::i) );
+    }
+
+    {
+      int x[6];
+      test_range<int, output_iterator_wrapper> rx(x);
+      VERIFY( ranges::fill(rx, 17) == rx.end() );
+      VERIFY( ranges::equal(x, c) );
+    }
+
+    {
+      std::list<int> list(6);
+      ranges::fill(list, 17);
+      VERIFY( ranges::equal(list, c) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  int x[5];
+  ranges::fill(x, 17);
+  for (auto v : x)
+    ok &= v == 17;
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/fill_n/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/fill_n/constrained.cc
new file mode 100644
index 00000000000..e9ce8e8fb0a
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/fill_n/constrained.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <list>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+};
+
+void
+test01()
+{
+  const int c[6] = { 17, 17, 17, 4, 5, 6 };
+    {
+      X x[6] = { {1}, {2}, {3}, {4}, {5}, {6} };
+      VERIFY( ranges::fill_n(x, 3, X{17}) == x+3 );
+      VERIFY( ranges::equal(x, c, {}, &X::i) );
+    }
+
+    {
+      char x[6];
+      VERIFY( ranges::fill_n(x, 3, 17) == x+3 );
+      VERIFY( ranges::equal(x, x+3, c, c+3) );
+    }
+
+    {
+      X x[6] = { 1, 2, 3, 4, 5, 6 };
+      test_container<X, forward_iterator_wrapper> cx(x);
+      VERIFY( ranges::fill_n(cx.begin(), 3, X{17})->i == 4 );
+      VERIFY( ranges::equal(cx, c, {}, &X::i) );
+    }
+
+    {
+      int x[6] = { 1, 2, 3, 4, 5, 6 };;
+      test_range<int, output_iterator_wrapper> rx(x);
+      ranges::fill_n(ranges::begin(rx), 3, 17);
+      VERIFY( ranges::equal(x, c) );
+    }
+
+    {
+      std::list<int> list({1, 2, 3, 4, 5, 6});
+      ranges::fill_n(list.begin(), 3, 17);
+      VERIFY( ranges::equal(list, c) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  int x[6] = { 1, 2, 3, 4, 5, 6 };
+  const int y[6] = { 1, 2, 3, 4, 5, 6 };
+  const int z[6] = { 17, 17, 17, 4, 5, 6 };
+
+  ranges::fill_n(x, 0, 17);
+  ranges::fill_n(x, -1, 17);
+  ok &= ranges::equal(x, y);
+
+  ranges::fill_n(x, 3, 17);
+  ok &= ranges::equal(x, z);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/find/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/find/constrained.cc
new file mode 100644
index 00000000000..6f6b9547e67
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find/constrained.cc
@@ -0,0 +1,75 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  auto res = ranges::find(x, x+6, 8, &X::i);
+  VERIFY( res == x+3 );
+  res = ranges::find(x, x+6, 2, &X::i);
+  VERIFY( res == x+0 );
+  res = ranges::find(x, x+6, 9, &X::i);
+  VERIFY( res == x+6 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  auto res2 = ranges::find(c, 8, &X::i);
+  VERIFY( res2 != ranges::end(c) && res2->i == 8 );
+  res2 = ranges::find(c, 9, &X::i);
+  VERIFY( res2 == ranges::end(c) );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  auto res3 = ranges::find(r, 8, &X::i);
+  VERIFY( res3 != ranges::end(r) && res3->i == 8 );
+
+  r.bounds.first = x;
+  res3 = ranges::find(r, 9, &X::i);
+  VERIFY( res3 == ranges::end(r) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6} };
+  static_assert(ranges::find(y, 4, &Y::j) == y+1);
+  static_assert(ranges::find(y, 5, &Y::j) == y+3);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_end/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/find_end/constrained.cc
new file mode 100644
index 00000000000..b51e4a734fb
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_end/constrained.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {10}, {11}, {2}, {6}, {8}, {10}, {11} };
+  X y[] = { {10}, {11} };
+  {
+
+    test_container<X, forward_iterator_wrapper> c(x);
+    auto res = ranges::find_end(c, y, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res)->i == 10 && std::get<1>(res) == ranges::end(c) );
+    res = ranges::find_end(c, c, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res) == ranges::begin(c)
+	    && std::get<1>(res) == ranges::end(c) );
+  }
+
+  {
+    test_range<X, forward_iterator_wrapper> r(x);
+    auto res = ranges::find_end(r, y, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res)->i == 10 && std::get<1>(res) == ranges::end(r) );
+    res = ranges::find_end(r, r, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res) == ranges::begin(r)
+	    && std::get<1>(res) == ranges::end(r) );
+  }
+
+  {
+    test_range<X, bidirectional_iterator_wrapper> r(x);
+    auto res = ranges::find_end(r, y, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res)->i == 10 && std::get<1>(res) == ranges::end(r) );
+    res = ranges::find_end(r, r, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res) == ranges::begin(r)
+	    && std::get<1>(res) == ranges::end(r) );
+  }
+}
+
+void
+test02()
+{
+  static constexpr X x[] = { {2}, {2}, {6}, {8}, {10}, {6}, {8}, {11} };
+  static constexpr X y[] = { {6}, {8} };
+  static constexpr int z[] = { 2, 8 };
+  static constexpr int w[] = { 2 };
+
+  static_assert(std::get<0>(ranges::find_end(x, y, {}, &X::i, &X::i)) == x+5);
+  static_assert(std::get<1>(ranges::find_end(x, y, {}, &X::i, &X::i)) == x+7);
+
+  static_assert(std::get<0>(ranges::find_end(x, z, {}, &X::i)) == x+8);
+  static_assert(std::get<1>(ranges::find_end(x, z, {}, &X::i)) == x+8);
+
+  static_assert(std::get<0>(ranges::find_end(x, w, {}, &X::i)) == x+1);
+  static_assert(std::get<1>(ranges::find_end(x, w, {}, &X::i)) == x+2);
+
+  static_assert(std::get<0>(ranges::find_end(x, x+6, w, w, {}, &X::i)) == x+6);
+  static_assert(std::get<1>(ranges::find_end(x, x+6, w, w, {}, &X::i)) == x+6);
+
+  static_assert(std::get<0>(ranges::find_end(x, x, w, w+1, {}, &X::i)) == x+0);
+  static_assert(std::get<1>(ranges::find_end(x, x, w, w+1, {}, &X::i)) == x+0);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_first_of/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/find_first_of/constrained.cc
new file mode 100644
index 00000000000..81a15761fa0
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_first_of/constrained.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  int y[] = { 2, 7, 8, 9 };
+  X w[] = { {2}, {7}, {8}, {9} };
+
+  auto res = ranges::find_first_of(x, x+6, y+1, y+4, {}, &X::i);
+  VERIFY( res == x+3 );
+  res = ranges::find_first_of(x, x+6, w, w+4, {}, &X::i, &X::i);
+  VERIFY( res == x+0 );
+  res = ranges::find_first_of(x, x+6, y+3, y+4, {}, &X::i);
+  VERIFY( res == x+6 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  test_container<int, forward_iterator_wrapper> d1(y+1, y+4);
+  auto res2 = ranges::find_first_of(c, d1, {}, &X::i);
+  VERIFY( res2 != ranges::end(c) && res2->i == 8 );
+
+  test_container<X, forward_iterator_wrapper> d2(w+3, w+4);
+  res2 = ranges::find_first_of(c, d2, {}, &X::i, &X::i);
+  VERIFY( res2 == ranges::end(c) );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  test_range<int, forward_iterator_wrapper> s1(y+1, y+4);
+  auto res3 = ranges::find_first_of(r, s1, {}, &X::i);
+  VERIFY( res3 != ranges::end(r) && res3->i == 8 );
+
+  test_range<X, forward_iterator_wrapper> s2(w+3, w+4);
+  r.bounds.first = x;
+  res3 = ranges::find_first_of(r, s2, {}, &X::i, &X::i);
+  VERIFY( res3 == ranges::end(r) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6} };
+  static_assert(ranges::find_first_of(y, y, {}, &Y::j, &Y::i) == y);
+  static_assert(ranges::find_first_of(y, y, {}, &Y::i, &Y::j) == y+1);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_if/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/find_if/constrained.cc
new file mode 100644
index 00000000000..299bdd0fceb
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_if/constrained.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  auto res = ranges::find_if(x, x+6, [] (X& v) { return v.i == 8; });
+  VERIFY( res == x+3 );
+  res = ranges::find_if(x, x+6, [] (X& v) { return v.i % 2 == 0; });
+  VERIFY( res == x+0 );
+  res = ranges::find_if(x, x+6, [] (X& v) { return v.i == 9; });
+  VERIFY( res == x+6 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  auto res2 = ranges::find_if(c, [] (int i) { return i > 7; }, &X::i);
+  VERIFY( res2 != ranges::end(c) && res2->i == 8 );
+  res2 = ranges::find_if(c, [] (int i) { return i > 11; }, &X::i);
+  VERIFY( res2 == ranges::end(c) );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  auto res3 = ranges::find_if(r, [] (int i) { return i > 10; }, &X::i);
+  VERIFY( res3 != ranges::end(r) && res3->i == 11 );
+
+  r.bounds.first = x;
+  res3 = ranges::find_if(r, [] (int i) { return i == 9; }, &X::i);
+  VERIFY( res3 == ranges::end(r) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6} };
+  static_assert(ranges::find_if(y, [] (int i) { return i > 3; }, &Y::j)
+		== y+1);
+  static_assert(ranges::find_if(y, [] (int i) { return i == 5; }, &Y::j)
+		== y+3);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_if_not/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/find_if_not/constrained.cc
new file mode 100644
index 00000000000..838434aa7e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_if_not/constrained.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  auto res = ranges::find_if_not(x, x+6, [] (X& v) { return v.i != 8; });
+  VERIFY( res == x+3 );
+  res = ranges::find_if_not(x, x+6, [] (X& v) { return v.i % 2 == 1; });
+  VERIFY( res == x+0 );
+  res = ranges::find_if_not(x, x+6, [] (X& v) { return v.i != 9; });
+  VERIFY( res == x+6 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  auto res2 = ranges::find_if_not(c, [] (int i) { return i <= 7; }, &X::i);
+  VERIFY( res2 != ranges::end(c) && res2->i == 8 );
+  res2 = ranges::find_if_not(c, [] (int i) { return i <= 11; }, &X::i);
+  VERIFY( res2 == ranges::end(c) );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  auto res3 = ranges::find_if_not(r, [] (int i) { return i <= 10; }, &X::i);
+  VERIFY( res3 != ranges::end(r) && res3->i == 11 );
+
+  r.bounds.first = x;
+  res3 = ranges::find_if_not(r, [] (int i) { return i != 9; }, &X::i);
+  VERIFY( res3 == ranges::end(r) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6} };
+  static_assert(ranges::find_if_not(y, [] (int i) { return i <= 3; }, &Y::j)
+		== y+1);
+  static_assert(ranges::find_if_not(y, [] (int i) { return i != 5; }, &Y::j)
+		== y+3);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/for_each/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/for_each/constrained.cc
new file mode 100644
index 00000000000..142ad2e57da
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/for_each/constrained.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+static int a;
+
+void
+f(int& i)
+{
+  a += i;
+}
+
+void
+test01()
+{
+  X x[] = { {2}, {4}, {6}, {8}, {10}, {11} };
+
+  auto res = ranges::for_each(x, x+6, f, &X::i);
+  VERIFY( res.in == x+6 );
+  VERIFY( res.fun == &f );
+  VERIFY( a == 41 );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  int p = 0;
+  ranges::for_each(c, [&p](int i) { ++p; }, &X::i);
+  VERIFY( p == 6 );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  int q = 0;
+  ranges::for_each(r, [&q](X&) { ++q; });
+  VERIFY( q == 6 );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  auto f = []
+  {
+    Y y[] = { {1,2}, {2,4}, {3,6} };
+    int a = 0;
+    ranges::for_each(y, [&a](int i) { a += i; }, &Y::i);
+    return a;
+  };
+  static_assert(f() == 6);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/generate/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/generate/constrained.cc
new file mode 100644
index 00000000000..71bcbaa616d
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/generate/constrained.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  const int c[6] = { 1, 2, 3, 4, 5, 6 };
+
+    {
+      int x[6];
+      int a = 1;
+      VERIFY( ranges::generate(x, [&] { return a++; }) == x+6 );
+      VERIFY( ranges::equal(x, c) );
+    }
+
+    {
+      int x[6];
+      int a = 1;
+      test_container<int, forward_iterator_wrapper> cx(x);
+      VERIFY( ranges::generate(cx, [&] { return a++; }) == cx.end() );
+      VERIFY( ranges::equal(cx, c) );
+    }
+
+    {
+      int x[6];
+      int a = 1;
+      test_range<int, output_iterator_wrapper> rx(x);
+      VERIFY( ranges::generate(rx, [&] { return a++; }) == rx.end() );
+      VERIFY( ranges::equal(x, c) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  const int c[6] = { 1, 2, 3, 4, 5, 6 };
+  int x[6];
+  int a = 1;
+  ranges::generate(x, [&] { return a++; });
+  return ranges::equal(x, c);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/generate_n/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/generate_n/constrained.cc
new file mode 100644
index 00000000000..ff894addecc
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/generate_n/constrained.cc
@@ -0,0 +1,84 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  const int c[6] = { 1, 2, 3, 4, 5, 6 };
+
+    {
+      int x[6] = { 7, 8, 9, 4, 5, 6 };
+      int a = 1;
+      VERIFY( ranges::generate_n(x, 3, [&] { return a++; }) == x+3 );
+      VERIFY( ranges::equal(x, c) );
+    }
+
+    {
+      int x[6] = { 7, 8, 9, 4, 5, 6 };
+      int a = 1;
+      test_container<int, forward_iterator_wrapper> cx(x);
+      VERIFY( *ranges::generate_n(cx.begin(), 3, [&] { return a++; })
+	       == 4 );
+      VERIFY( ranges::equal(cx, c) );
+    }
+
+    {
+      int x[6] = { 7, 8, 9, 4, 5, 6 };
+      int a = 1;
+      test_range<int, output_iterator_wrapper> rx(x);
+      ranges::generate_n(ranges::begin(rx), 3, [&] { return a++; });
+      VERIFY( ranges::equal(x, c) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  int c[6] = { 1, 2, 3, 4, 5, 6 };
+  int x[6];
+  int a = 1;
+  ranges::generate_n(x, 6, [&] { return a++; });
+  ok &= ranges::equal(x, c);
+  ranges::generate_n(c, 0, [] { return -1; });
+  ok &= ranges::equal(x, c);
+  ranges::generate_n(c, -2, [] { return -1; });
+  ok &= ranges::equal(x, c);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc
new file mode 100644
index 00000000000..eacf2906371
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc
@@ -0,0 +1,107 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+template<template<typename, template<typename> typename> typename container>
+void
+test01()
+{
+  int x[50];
+
+  auto pred = std::greater{};
+  auto proj = [] (int a) { return -a; };
+  for (int i = 0; i < 50; i++)
+    {
+      std::iota(x, x+50, 1);
+      container<int, random_access_iterator_wrapper> rx(x);
+
+      std::ranlux48_base g(i);
+      ranges::shuffle(rx, g);
+
+      auto iter = ranges::make_heap(rx, pred, proj);
+      VERIFY( iter == rx.end() );
+      VERIFY( ranges::is_heap(rx, pred, proj) );
+      VERIFY( ranges::is_heap_until(rx, pred, proj) == rx.end() );
+
+      iter = ranges::pop_heap(rx, pred, proj);
+      VERIFY( iter == rx.end() );
+      VERIFY( *(iter-1) == 50 );
+      VERIFY( ranges::is_heap_until(rx, pred, proj) == iter-1 );
+
+      iter = ranges::pop_heap(rx.begin(), iter-1, pred, proj);
+      VERIFY( iter+1 == rx.end() );
+      VERIFY( *(iter-1) == 49 );
+      VERIFY( ranges::is_heap_until(rx, pred, proj) == iter-1 );
+
+      *(iter-1) = i;
+      iter = ranges::push_heap(rx.begin(), iter, pred, proj);
+      VERIFY( iter+1 == rx.end() );
+      VERIFY( ranges::is_heap_until(rx, pred, proj) == iter );
+
+      *iter = 2*i;
+      iter = ranges::push_heap(rx.begin(), rx.end(), pred, proj);
+      VERIFY( iter == rx.end() );
+      VERIFY( ranges::is_heap_until(rx, pred, proj) == iter );
+
+      *(rx.begin()+1) *= -1;
+      VERIFY( !ranges::is_heap(rx, pred, proj) );
+      *(rx.begin()+1) *= -1;
+      VERIFY( ranges::is_heap(rx, pred, proj) );
+
+      iter = ranges::sort_heap(rx, pred, proj);
+      VERIFY( iter == rx.end() );
+      VERIFY( ranges::is_sorted(rx, pred, proj) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  int x[] = {1,2,3,4,5};
+  ranges::make_heap(x);
+  ranges::pop_heap(x);
+  x[4] = 7;
+  ranges::push_heap(x);
+  ok &= ranges::is_heap(x);
+  ok &= ranges::is_heap_until(x) == x+5;
+  ranges::sort_heap(x);
+  ok &= ranges::equal(x, (int[]){1,2,3,4,7});
+  return ok;
+}
+
+int
+main()
+{
+  test01<test_range>();
+  test01<test_container>();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/includes/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/includes/constrained.cc
new file mode 100644
index 00000000000..f959a1d60a4
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/includes/constrained.cc
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5,6,7};
+  int y[] = {2,4,6};
+  test_range<int, input_iterator_wrapper> rx(x), ry(y);
+
+  VERIFY( ranges::includes(rx, ry) );
+
+  rx.bounds.first = x;
+  ry.bounds.first = y;
+  VERIFY( ranges::includes(rx, ry,
+			   ranges::greater{},
+			   std::negate<>{},
+			   std::negate<>{}) );
+
+  test_container<int, forward_iterator_wrapper> cx(x), cy(y);
+  VERIFY( ranges::includes(cx.begin(), cx.end(),
+			   cy.begin(), cy.end(),
+			   {},
+			   [] (int a) { return a+1; },
+			   [] (int a) { return a+2; }) );
+
+  VERIFY( ranges::includes(x, x+1, y, y) );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  ok &= ranges::includes((int[]){1,2,3},
+			 (int[]){1});
+  ok &= !ranges::includes((int[]){1,2,3},
+			  (int[]){1,2,3,4});
+  return true;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc
new file mode 100644
index 00000000000..856056865d9
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc
@@ -0,0 +1,69 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  for (int i = 0; i <= 5; i++)
+    for (int j = 0; j <= 5; j++)
+      {
+	std::vector<int> v(x, x+i);
+	v.insert(v.end(), x, x+j);
+	ranges::sort(v);
+
+	test_range<int, bidirectional_iterator_wrapper> rz(&v[0], &v[0]+i+j);
+	auto result = ranges::inplace_merge(rz, next(ranges::begin(rz), i));
+	VERIFY( result == rz.end() );
+
+	VERIFY( ranges::is_sorted(rz) );
+      }
+}
+
+void
+test02()
+{
+  struct X { int i, j; };
+  X x[] = { {1, 1}, {3, 4}, {5, 5}, {2, 2}, {2, 3} };
+  auto comp = ranges::greater{};
+  auto proj = [] (X a) { return -a.i; };
+  ranges::inplace_merge(x, x+3, x+5, comp, proj);
+  VERIFY( ranges::is_sorted(x, {}, &X::i) );
+  VERIFY( ranges::is_sorted(x, {}, &X::j) );
+}
+
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/is_partitioned/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/is_partitioned/constrained.cc
new file mode 100644
index 00000000000..8035667b38d
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/is_partitioned/constrained.cc
@@ -0,0 +1,58 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {2,4,6,1,3,5};
+  test_container<int, forward_iterator_wrapper> cx(x);
+  VERIFY( ranges::is_partitioned(cx, [] (int a) { return a%2==0; }) );
+
+  test_range<int, input_iterator_wrapper> rx(x);
+  VERIFY( ranges::is_partitioned(rx,
+				 [] (int a) { return a%2==1; },
+				 [] (int a) { return a+1; }) );
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1,2,3,4,5,6,1};
+  return (ranges::is_partitioned(x, x+6, [] (int a) { return a<=2; })
+	  && !ranges::is_partitioned(x, x+7, [] (int a) { return a<=2; }));
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc
new file mode 100644
index 00000000000..c5393becc8d
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  int x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  int y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  int z[] = { {2}, {6}, {8}, {10}, {2}, {2} };
+
+  VERIFY( ranges::is_permutation(x, x+6, y, y+6) );
+  VERIFY( !ranges::is_permutation(x, x+6, y, y+5) );
+
+  test_container<int, forward_iterator_wrapper> cx(x), cy(y), cz(z);
+  test_range<int, forward_iterator_wrapper> rx(x), ry(y), rz(z);
+  VERIFY( ranges::is_permutation(cx, ry) );
+  VERIFY( !ranges::is_permutation(rx, cz) );
+  VERIFY( ranges::is_permutation(rx, cy) );
+  VERIFY( !ranges::is_permutation(cx, rz) );
+}
+
+void
+test02()
+{
+  static constexpr X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  static constexpr X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  static constexpr int z[] = { {2}, {6}, {8}, {10}, {2}, {2} };
+  static_assert(ranges::is_permutation(x, y, {}, &X::i, &X::i));
+  static_assert(!ranges::is_permutation(x, z, {}, &X::i));
+  static_assert(!ranges::is_permutation(z, y, {}, {}, &X::i));
+}
+
+void
+test03()
+{
+  int x[] = { 1, 2, 3, 4 };
+  int y[] = { 1, 2, 3, 3 };
+  test_container<int, bidirectional_iterator_wrapper> cx(x);
+  do
+    do
+      {
+	VERIFY( ranges::is_permutation(cx, x) );
+	VERIFY( !ranges::is_permutation(y, cx) );
+      } while (std::next_permutation(y, y+4));
+  while (std::next_permutation(std::begin(cx), std::end(cx)));
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/is_sorted/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/is_sorted/constrained.cc
new file mode 100644
index 00000000000..af00afe48e8
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/is_sorted/constrained.cc
@@ -0,0 +1,67 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {3,4,5,1};
+  test_container<int, forward_iterator_wrapper> cx(x);
+  VERIFY( ranges::is_sorted(cx.begin(), ranges::next(cx.begin(), 3)) );
+  VERIFY( !ranges::is_sorted(cx) );
+  VERIFY( !ranges::is_sorted(cx, ranges::greater{}) );
+  VERIFY( ranges::is_sorted(cx, {}, [] (int a) { return 0; }) );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  VERIFY( ranges::is_sorted(rx) );
+  VERIFY( !ranges::is_sorted(ranges::begin(rx),
+			     next(ranges::begin(rx), 2),
+			     ranges::greater{}) );
+}
+
+constexpr bool
+test03()
+{
+  int x[] = { 1,2 };
+  return (ranges::is_sorted(x)
+	  && ranges::is_sorted(x, x) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/is_sorted_until/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/is_sorted_until/constrained.cc
new file mode 100644
index 00000000000..a81aa49ee94
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/is_sorted_until/constrained.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {3,4,5,1};
+  test_container<int, forward_iterator_wrapper> cx(x);
+  VERIFY( ranges::is_sorted_until(cx.begin(),
+				  ranges::next(cx.begin(), 3))
+	  == ranges::next(cx.begin(), 3) );
+  VERIFY( ranges::is_sorted_until(cx) == ranges::next(cx.begin(), 3) );
+  VERIFY( ranges::is_sorted_until(cx, ranges::greater{})
+	  == ranges::next(cx.begin(), 1) );
+  VERIFY( ranges::is_sorted_until(cx, {}, [] (int a) { return 0; })
+	  == cx.end() );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  VERIFY( ranges::is_sorted_until(rx) == ranges::end(rx) );
+  VERIFY( ranges::is_sorted_until(ranges::begin(rx),
+				  next(ranges::begin(rx), 2),
+				  ranges::greater{})
+	  == next(ranges::begin(rx), 1) );
+}
+
+constexpr bool
+test03()
+{
+  int x[] = { 1,2 };
+  return (ranges::is_sorted_until(x) == x+2
+	  && ranges::is_sorted_until(x, x) == x );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/constrained.cc
new file mode 100644
index 00000000000..b82c872bbbb
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/constrained.cc
@@ -0,0 +1,164 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  char y[] = {1, 2, 3, 5};
+  long z[] = {1, 2, 3, 4, 5, 6};
+
+    {
+      test_range<int, input_iterator_wrapper> rx(x);
+      test_range<char, input_iterator_wrapper> ry(y);
+      test_range<long, input_iterator_wrapper> rz(z);
+
+      VERIFY( ranges::lexicographical_compare(rx, ry) );
+      rx.bounds.first = x;
+      ry.bounds.first = y;
+      VERIFY( !ranges::lexicographical_compare(ry, rx) );
+    }
+
+  test_range<int, forward_iterator_wrapper> rx(x);
+  test_range<char, forward_iterator_wrapper> ry(y);
+  test_range<long, forward_iterator_wrapper> rz(z);
+
+  VERIFY( ranges::lexicographical_compare(rx, rz) );
+  VERIFY( !ranges::lexicographical_compare(rz, rx) );
+
+  VERIFY( !ranges::lexicographical_compare(rx, rx) );
+  VERIFY( ranges::lexicographical_compare(rx, rx, {}, std::negate<>{}) );
+  VERIFY( ranges::lexicographical_compare(rx, rx, std::greater{},
+					  {}, std::negate<>{}) );
+
+  VERIFY( !ranges::lexicographical_compare(rx, ry, {},
+					   std::negate<>{},
+					   std::negate<>{}) );
+  VERIFY( ranges::lexicographical_compare(ry, rx, {},
+					  std::negate<>{},
+					  std::negate<>{}) );
+
+  VERIFY( ranges::lexicographical_compare(rx, rz, ranges::greater{}) );
+  VERIFY( !ranges::lexicographical_compare(rz, rx, ranges::greater{}) );
+
+  VERIFY( ranges::lexicographical_compare(rx, ry, ranges::greater{},
+					  std::negate<>{},
+					  std::negate<>{}) );
+  VERIFY( !ranges::lexicographical_compare(ry, rx, ranges::greater{},
+					   std::negate<>{},
+					   std::negate<>{}) );
+}
+
+void
+test02()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  int y[] = {1, 2, 3, 5};
+  int z[] = {1, 2, 3, 4, 5, 6};
+
+  VERIFY( ranges::lexicographical_compare(x, y) );
+  VERIFY( !ranges::lexicographical_compare(y, x) );
+
+  VERIFY( ranges::lexicographical_compare(x, z) );
+  VERIFY( !ranges::lexicographical_compare(z, x) );
+
+  VERIFY( !ranges::lexicographical_compare(x, x) );
+
+  VERIFY( !ranges::lexicographical_compare(x, y, {},
+					   std::negate<>{},
+					   std::negate<>{}) );
+  VERIFY( ranges::lexicographical_compare(y, x, {},
+					  std::negate<>{},
+					  std::negate<>{}) );
+
+  VERIFY( ranges::lexicographical_compare(x, z, ranges::greater{}) );
+  VERIFY( !ranges::lexicographical_compare(z, x, ranges::greater{}) );
+
+  VERIFY( ranges::lexicographical_compare(x, y, ranges::greater{},
+					  std::negate<>{},
+					  std::negate<>{}) );
+  VERIFY( !ranges::lexicographical_compare(y, x, ranges::greater{},
+					   std::negate<>{},
+					   std::negate<>{}) );
+}
+
+void
+test03()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  int y[] = {1, 2, 5, 3};
+  int z[] = {1, 2, 3, 5};
+
+  do
+    {
+      VERIFY( ranges::lexicographical_compare(x, y) );
+      VERIFY( !ranges::lexicographical_compare(x, y, ranges::greater{}) );
+      VERIFY( !ranges::lexicographical_compare(y, x) );
+      VERIFY( ranges::lexicographical_compare(y, x, ranges::greater{}) );
+
+      test_container<int, forward_iterator_wrapper> cy(y);
+      VERIFY( ranges::lexicographical_compare(x, cy) );
+      VERIFY( !ranges::lexicographical_compare(x, cy, ranges::greater{}) );
+      VERIFY( !ranges::lexicographical_compare(cy, x) );
+      VERIFY( ranges::lexicographical_compare(cy, x, ranges::greater{}) );
+
+      test_container<int, forward_iterator_wrapper> cz(z);
+      VERIFY( ranges::lexicographical_compare(cz.begin(), cz.end(),
+					      cy.begin(), cy.end()) );
+      VERIFY( !ranges::lexicographical_compare(cy.begin(), cy.end(),
+					       cz.begin(), cz.end()) );
+
+      std::vector<int> vx(x, x+5), vy(y, y+5);
+      VERIFY( ranges::lexicographical_compare(vx, vy) );
+      VERIFY( !ranges::lexicographical_compare(vx, vy, ranges::greater{}) );
+      VERIFY( !ranges::lexicographical_compare(vy, vx) );
+      VERIFY( ranges::lexicographical_compare(vy, vx, ranges::greater{}) );
+    } while (ranges::next_permutation(y).found);
+}
+
+constexpr bool
+test04()
+{
+  int x[] = {1};
+  int y[] = {1};
+  return (ranges::lexicographical_compare((int[]){1,2,3,5},
+					  (int[]){1,2,4})
+	  && !ranges::lexicographical_compare(x, x, y, y));
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  static_assert(test04());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/lower_bound/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/lower_bound/constrained.cc
new file mode 100644
index 00000000000..df93f41e823
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/lower_bound/constrained.cc
@@ -0,0 +1,66 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1, 2, 3, 4, 5, 5, 6, 7};
+  for (unsigned i = 0; i < 5; i++)
+    for (unsigned j = 5; j < 8; j++)
+      {
+	test_container<int, forward_iterator_wrapper> cx(x);
+	auto result = ranges::lower_bound(std::next(cx.begin(), i),
+					  std::next(cx.begin(), j),
+					  4, {}, [] (int a) { return a-1; });
+	VERIFY( result.ptr == x+4 );
+      }
+
+  ranges::reverse(x);
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto result = ranges::lower_bound(rx, 5, ranges::greater{},
+				    [] (int a) { return a+1; });
+  VERIFY( result.ptr == x+4 );
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  return (ranges::lower_bound(x, 6) == x+5
+	  && ranges::lower_bound(x, x, 6) == x
+	  && ranges::lower_bound(x, 1) == x);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc
new file mode 100644
index 00000000000..3fcdb3a366c
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc
@@ -0,0 +1,82 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i, j;
+};
+
+void
+test01()
+{
+  VERIFY( ranges::max(1, 2) == 2);
+  VERIFY( ranges::max(2, 1) == 2);
+  VERIFY( ranges::max(1, 2, ranges::greater{}) == 1);
+  VERIFY( ranges::max(1, 2, ranges::greater{}, std::negate<>{}) == 2);
+  VERIFY( ranges::max(1, 2, {}, std::negate<>{}) == 1);
+  VERIFY( ranges::max(X{1,2}, X{1,3}, {}, &X::i).j == 2 );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4};
+  do
+    {
+      test_range<int, input_iterator_wrapper> cx(x);
+      VERIFY( ranges::max(cx) == 4 );
+      cx.bounds.first = x;
+      VERIFY( ranges::max(cx, ranges::greater{}) == 1 );
+      cx.bounds.first = x;
+      VERIFY( ranges::max(cx, {}, std::negate<>{}) == 1);
+      cx.bounds.first = x;
+      VERIFY( ranges::max(cx, ranges::greater{}, std::negate<>{}) == 4 );
+    } while (ranges::next_permutation(x).found);
+
+  constexpr X y[] = {{0,5},{1,2},{1,3}};
+  static_assert(ranges::max(y, {}, &X::i).j == 2);
+}
+
+void
+test03()
+{
+  VERIFY( ranges::max({2,3,1,4}) == 4 );
+  VERIFY( ranges::max({2,3,1,4}, ranges::greater{}) == 1 );
+  VERIFY( ranges::max({2,3,1,4}, {}, std::negate<>{}) == 1 );
+  VERIFY( ranges::max({2,3,1,4}, ranges::greater{}, std::negate<>{}) == 4 );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/max_element/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/max_element/constrained.cc
new file mode 100644
index 00000000000..427faedbc44
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/max_element/constrained.cc
@@ -0,0 +1,60 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i, j;
+};
+
+void
+test01()
+{
+  int x[] = {1,2,3,4};
+  do
+    {
+      test_range<int, forward_iterator_wrapper> cx(x);
+      VERIFY( *ranges::max_element(cx) == 4 );
+      VERIFY( *ranges::max_element(cx, ranges::greater{}) == 1 );
+      VERIFY( *ranges::max_element(cx, {}, std::negate<>{}) == 1);
+      VERIFY( *ranges::max_element(cx, ranges::greater{}, std::negate<>{}) == 4 );
+    } while (ranges::next_permutation(x).found);
+
+  test_container<int, forward_iterator_wrapper> cx(x);
+  VERIFY( ranges::max_element(cx.begin(), cx.begin()) == cx.begin() );
+
+  constexpr X y[] = {{0,5},{1,2},{1,3}};
+  static_assert(ranges::max_element(y, y+3, {}, &X::i)->j == 2);
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/merge/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/merge/constrained.cc
new file mode 100644
index 00000000000..3f3a0f79f9b
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/merge/constrained.cc
@@ -0,0 +1,75 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  for (int i = 0; i <= 5; i++)
+    for (int j = 0; j <= 5; j++)
+      {
+	int z[10];
+	test_range<int, input_iterator_wrapper> rx(x, x+i), ry(x, x+j);
+	test_range<int, output_iterator_wrapper> rz(z, z+i+j);
+	auto [in1,in2,out] = ranges::merge(rx, ry, rz.begin());
+	VERIFY( in1 == rx.end() );
+	VERIFY( in2 == ry.end() );
+	VERIFY( out == rz.end() );
+
+	std::vector<int> v(x, x+i);
+	v.insert(v.end(), x, x+j);
+	ranges::sort(v);
+
+	VERIFY( ranges::equal(v.begin(), v.end(), z, z+i+j) );
+      }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {-1,-3,-5};
+  int y[] = {2,4,6};
+  int z[6];
+  ranges::merge(x, x+3, y, y+3, z,
+		ranges::greater{}, {}, [] (int a) { return -a; });
+
+  const int w[6] = {-1, 2, -3, 4, -5, 6};
+  return ranges::equal(w, z);
+}
+
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc
new file mode 100644
index 00000000000..c3a83b975c9
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc
@@ -0,0 +1,82 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i, j;
+};
+
+void
+test01()
+{
+  VERIFY( ranges::min(1, 2) == 1);
+  VERIFY( ranges::min(2, 1) == 1);
+  VERIFY( ranges::min(1, 2, ranges::greater{}) == 2);
+  VERIFY( ranges::min(1, 2, ranges::greater{}, std::negate<>{}) == 1);
+  VERIFY( ranges::min(1, 2, {}, std::negate<>{}) == 2);
+  VERIFY( ranges::min(X{1,2}, X{1,3}, {}, &X::i).j == 2 );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4};
+  do
+    {
+      test_range<int, input_iterator_wrapper> cx(x);
+      VERIFY( ranges::min(cx) == 1 );
+      cx.bounds.first = x;
+      VERIFY( ranges::min(cx, ranges::greater{}) == 4 );
+      cx.bounds.first = x;
+      VERIFY( ranges::min(cx, {}, std::negate<>{}) == 4);
+      cx.bounds.first = x;
+      VERIFY( ranges::min(cx, ranges::greater{}, std::negate<>{}) == 1 );
+    } while (ranges::next_permutation(x).found);
+
+  constexpr X y[] = {{5,0},{1,2},{1,3}};
+  static_assert(ranges::min(y, {}, &X::i).j == 2);
+}
+
+void
+test03()
+{
+  VERIFY( ranges::min({2,3,1,4}) == 1 );
+  VERIFY( ranges::min({2,3,1,4}, ranges::greater{}) == 4 );
+  VERIFY( ranges::min({2,3,1,4}, {}, std::negate<>{}) == 4 );
+  VERIFY( ranges::min({2,3,1,4}, ranges::greater{}, std::negate<>{}) == 1 );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/min_element/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/min_element/constrained.cc
new file mode 100644
index 00000000000..518060522a3
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/min_element/constrained.cc
@@ -0,0 +1,60 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i, j;
+};
+
+void
+test01()
+{
+  int x[] = {1,2,3,4};
+  do
+    {
+      test_range<int, forward_iterator_wrapper> cx(x);
+      VERIFY( *ranges::min_element(cx) == 1 );
+      VERIFY( *ranges::min_element(cx, ranges::greater{}) == 4 );
+      VERIFY( *ranges::min_element(cx, {}, std::negate<>{}) == 4);
+      VERIFY( *ranges::min_element(cx, ranges::greater{}, std::negate<>{}) == 1 );
+    } while (ranges::next_permutation(x).found);
+
+  test_container<int, forward_iterator_wrapper> cx(x);
+  VERIFY( ranges::min_element(cx.begin(), cx.begin()) == cx.begin() );
+
+  constexpr X y[] = {{5,0},{1,2},{1,3}};
+  static_assert(ranges::min_element(y, y+3, {}, &X::i)->j == 2);
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc
new file mode 100644
index 00000000000..aa9364ab04c
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+template<typename T1, typename T2>
+constexpr bool
+operator==(const ranges::minmax_result<T1>& lhs,
+	   const ranges::minmax_result<T2>& rhs)
+{
+  return (lhs.min == rhs.min
+	  && rhs.max == rhs.max);
+}
+
+
+struct X
+{
+  int i, j;
+};
+
+using res_t = ranges::minmax_result<int>;
+
+void
+test01()
+{
+  VERIFY( ranges::minmax(1, 2) == res_t(1,2) );
+  VERIFY( ranges::minmax(2, 1) == res_t(1,2) );
+  VERIFY( ranges::minmax(1, 2, ranges::greater{}) == res_t(2,1) );
+  VERIFY( ranges::minmax(1, 2, ranges::greater{}, std::negate<>{}) == res_t(1,2) );
+  VERIFY( ranges::minmax(1, 2, {}, std::negate<>{}) == res_t(2,1) );
+  VERIFY( ranges::minmax(X{1,2}, X{1,3}, {}, &X::i).min.j == 2 );
+  VERIFY( ranges::minmax(X{1,2}, X{1,3}, {}, &X::i).max.j == 3 );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4};
+  do
+    {
+      test_range<int, input_iterator_wrapper> cx(x);
+      VERIFY( ranges::minmax(cx) == res_t(1,4) );
+      cx.bounds.first = x;
+      VERIFY( ranges::minmax(cx, ranges::greater{}) == res_t(4,1) );
+      cx.bounds.first = x;
+      VERIFY( ranges::minmax(cx, {}, std::negate<>{}) == res_t(4,1));
+      cx.bounds.first = x;
+      VERIFY( ranges::minmax(cx, ranges::greater{}, std::negate<>{})
+	      == res_t(1,4) );
+    } while (ranges::next_permutation(x).found);
+
+  constexpr X y[] = {{1,5},{1,2},{1,3}};
+  static_assert(ranges::minmax(y, {}, &X::i).min.j == 5);
+  static_assert(ranges::minmax(y, {}, &X::i).max.j == 3);
+}
+
+void
+test03()
+{
+  VERIFY( ranges::minmax({2,3,1,4}) == res_t(1,4) );
+  VERIFY( ranges::minmax({2,3,1,4}, ranges::greater{}) == res_t(4,1) );
+  VERIFY( ranges::minmax({2,3,1,4}, {}, std::negate<>{}) == res_t(4,1) );
+  VERIFY( ranges::minmax({2,3,1,4}, ranges::greater{}, std::negate<>{})
+	  == res_t(1,4) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc
new file mode 100644
index 00000000000..40019c43326
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i, j;
+};
+
+void
+test01()
+{
+  int x[] = {1,2,3,4};
+  do
+    {
+      test_range<int, forward_iterator_wrapper> cx(x);
+      VERIFY( *ranges::minmax_element(cx).min == 1 );
+      VERIFY( *ranges::minmax_element(cx).max == 4 );
+      VERIFY( *ranges::minmax_element(cx, ranges::greater{}).min == 4 );
+      VERIFY( *ranges::minmax_element(cx, ranges::greater{}).max == 1 );
+      VERIFY( *ranges::minmax_element(cx, {}, std::negate<>{}).min == 4);
+      VERIFY( *ranges::minmax_element(cx, {}, std::negate<>{}).max == 1);
+      VERIFY( *ranges::minmax_element(cx, ranges::greater{}, std::negate<>{}).min
+	      == 1 );
+      VERIFY( *ranges::minmax_element(cx, ranges::greater{}, std::negate<>{}).max
+	      == 4 );
+    } while (ranges::next_permutation(x).found);
+
+  test_container<int, forward_iterator_wrapper> cx(x);
+  VERIFY( ranges::minmax_element(cx.begin(), cx.begin()).min == cx.begin() );
+  VERIFY( ranges::minmax_element(cx.begin(), cx.begin()).max == cx.begin() );
+
+  constexpr X y[] = {{1,5},{1,2},{1,3}};
+  static_assert(ranges::minmax_element(y, y+3, {}, &X::i).min->j == 5);
+  static_assert(ranges::minmax_element(y, y+3, {}, &X::i).max->j == 3);
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/mismatch/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/mismatch/constrained.cc
new file mode 100644
index 00000000000..37e79a2d62f
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/mismatch/constrained.cc
@@ -0,0 +1,76 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X xa[] = { {1}, {2}, {3}, {4}, {5}, {6} };
+  X xb[] = { {1}, {2}, {3}, {3}, {5}, {6} };
+  auto res = ranges::mismatch(xa, xa+6, xb, xb+6, {}, &X::i, &X::i);
+  VERIFY( res.in1 == xa+3 && res.in2 == xb+3 );
+
+  test_container<X, forward_iterator_wrapper> ca(xa);
+  test_container<X, forward_iterator_wrapper> cb(xb);
+  auto res2 = ranges::mismatch(ca, cb, {}, &X::i, &X::i);
+  VERIFY( res2.in1->i == 4 && res2.in2->i == 3 );
+  res2 = ranges::mismatch(ca, ca, {}, &X::i, &X::i);
+  VERIFY( res2.in1 == ranges::end(ca) && res2.in2 == ranges::end(ca) );
+
+  test_range<X, input_iterator_wrapper> ra(xa);
+  test_range<X, input_iterator_wrapper> rb(xb);
+  auto res3 = ranges::mismatch(ra, rb, {}, &X::i, &X::i);
+  VERIFY( res3.in1->i == 4 && res3.in2->i == 3 );
+
+  test_range<X, input_iterator_wrapper> ra2(xa);
+  ra.bounds.first = xa;
+  res3 = ranges::mismatch(ra, ra2, {}, &X::i, &X::i);
+  VERIFY( res3.in1 == ranges::end(ra) && res3.in2 == ranges::end(ra2) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y ya[] = { {1,2}, {2,4}, {3,6}, {1,6} };
+  static constexpr Y yb[] = { {2,1}, {4,2}, {4,2}, {7,1} };
+  static_assert(ranges::mismatch(ya, yb, {}, &Y::i, &Y::j).in1 == ya+2);
+  static_assert(ranges::mismatch(ya, yb, {}, &Y::i, &Y::j).in2 == yb+2);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/move/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/move/constrained.cc
new file mode 100644
index 00000000000..d205b3572a1
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/move/constrained.cc
@@ -0,0 +1,203 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+  int moved = 0;
+
+  constexpr X() : i(0) { }
+  constexpr X(int a) : i(a) { }
+
+  constexpr X(const X&) = delete;
+  constexpr X& operator=(const X&) = delete;
+
+  constexpr X(X&& other)
+  {
+    *this = std::move(other);
+  }
+
+  constexpr X&
+  operator=(X&& other)
+  {
+    other.moved++;
+    i = other.i;
+    return *this;
+  }
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  { return a.i == b.i; }
+};
+
+void
+test01()
+{
+    {
+      X x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      X y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+      X z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      auto [in, out] = ranges::move(x, y);
+      VERIFY( ranges::equal(x, y) && in == x+7 && out == y+7 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      char y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_container<int, forward_iterator_wrapper> cx(x);
+      test_container<char, forward_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::move(cx, cy.begin());
+      VERIFY( ranges::equal(x, x+3, y, y+3) && in.ptr == x+3 && out.ptr == y+3 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      char x[3] = { 1, 2, 3 };
+      int y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_range<char, input_iterator_wrapper> cx(x);
+      test_range<int, output_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::move(cx, ranges::begin(cy));
+      VERIFY( ranges::equal(x, x+3, y, y+3) && in.ptr == x+3 && out.ptr == y+3 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x= {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+}
+
+void
+test02()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  X z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::move(x, y);
+  VERIFY( ranges::equal(x, x+5, y, y+5) );
+  VERIFY( in == x+5 );
+  VERIFY( out == y+5 );
+  VERIFY( y[5].i == 2 );
+  VERIFY( ranges::equal(x, z) );
+  VERIFY( ranges::count(x, 1, &X::moved) == 5 );
+  VERIFY( ranges::count(y, 0, &X::moved) == 6 );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  X x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  X z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::move(x, y);
+  ok &= ranges::equal(x, x+5, y, y+5);
+  ok &= (in == x+5);
+  ok &= (out == y+5);
+  ok &= (y[5].i == 2);
+  ok &= ranges::equal(x, z);
+  ok &= ranges::count(x, 1, &X::moved) == 5;
+  ok &= ranges::count(y, 0, &X::moved) == 6;
+  return ok;
+}
+
+void
+test04()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  X z[] = { {2}, {2}, {6}, {8}, {10} };
+  test_range<X, input_iterator_wrapper> rx(x);
+  auto [in, out] = ranges::move(std::move_iterator{ranges::begin(rx)},
+				std::move_sentinel{ranges::end(rx)},
+				ranges::begin(y));
+  VERIFY( ranges::equal(x, x+5, y, y+5) );
+  VERIFY( std::move(in).base().ptr == x+5 );
+  VERIFY( out == y+5 );
+  VERIFY( y[5].i == 2 );
+  VERIFY( ranges::equal(x, z) );
+  VERIFY( ranges::count(x, 1, &X::moved) == 5 );
+  VERIFY( ranges::count(y, 0, &X::moved) == 6 );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/move_backward/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/move_backward/constrained.cc
new file mode 100644
index 00000000000..3c4aa5dc70d
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/move_backward/constrained.cc
@@ -0,0 +1,170 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+  int moved = 0;
+
+  constexpr X() : i(0) { }
+  constexpr X(int a) : i(a) { }
+
+  constexpr X(const X&) = delete;
+  constexpr X& operator=(const X&) = delete;
+
+  constexpr X(X&& other)
+  {
+    *this = std::move(other);
+  }
+
+  constexpr X&
+  operator=(X&& other)
+  {
+    other.moved++;
+    i = other.i;
+    return *this;
+  }
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  { return a.i == b.i; }
+};
+
+void
+test01()
+{
+    {
+      X x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      X y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+      X z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      auto [in, out] = ranges::move_backward(x, y+7);
+      VERIFY( ranges::equal(x, y) && in == x+7 && out == y );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      char y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_container<int, bidirectional_iterator_wrapper> cx(x);
+      test_container<char, bidirectional_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::move_backward(cx, cy.end());
+      VERIFY( ranges::equal(x, x+3, y+1, y+4) && in.ptr == x+3 && out.ptr == y+1 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x= {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+}
+
+void
+test02()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  const X z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::move_backward(x, ranges::end(y));
+  VERIFY( ranges::equal(x, x+5, y+1, y+6) );
+  VERIFY( in == x+5 );
+  VERIFY( out == y+1 );
+  VERIFY( y[0].i == 2 );
+  VERIFY( ranges::equal(x, z) );
+  VERIFY( ranges::count(x, 1, &X::moved) == 5 );
+  VERIFY( ranges::count(y, 0, &X::moved) == 6 );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  X x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  const X z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::move_backward(x, ranges::end(y));
+  ok &= ranges::equal(x, x+5, y+1, y+6);
+  ok &= (in == x+5);
+  ok &= (out == y+1);
+  ok &= (y[0].i == 2);
+  ok &= ranges::equal(x, z);
+  ok &= ranges::count(x, 1, &X::moved) == 5;
+  ok &= ranges::count(y, 0, &X::moved) == 6;
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/next_permutation/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/next_permutation/constrained.cc
new file mode 100644
index 00000000000..e69b551a56b
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/next_permutation/constrained.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  int y[] = {1, 2, 3, 4, 5};
+
+  for (int i = 0; i <= 5; i++)
+    {
+      test_container<int, bidirectional_iterator_wrapper> cx(x, x+i);
+      test_container<int, bidirectional_iterator_wrapper> cy(y, y+i);
+      for (int j = 0; ; j++)
+	{
+	  auto found1 = std::next_permutation(cx.begin(), cx.end());
+	  auto [found2,last] = ranges::next_permutation(cy.begin(), cy.end());
+	  VERIFY( found1 == found2 );
+	  VERIFY( ranges::equal(cx, cy) );
+	  if (!found2)
+	    break;
+	}
+    }
+}
+
+void
+test02()
+{
+  int x[] = {5, 4, 3, 2, 1};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto [found,last] = ranges::next_permutation(rx, ranges::greater{});
+  VERIFY( found && last == rx.end() );
+  VERIFY( last == rx.end() );
+  VERIFY( ranges::equal(rx, (int[]){5,4,3,1,2}) );
+  ranges::next_permutation(rx, {}, [] (int a) { return -a; });
+  VERIFY( ranges::equal(rx, (int[]){5,4,2,3,1}) );
+
+  VERIFY( !ranges::next_permutation(x, x).found );
+  VERIFY( !ranges::next_permutation(x, x+1).found );
+}
+
+constexpr bool
+test03()
+{
+  int x[] = {1,2,3};
+  ranges::next_permutation(x);
+  return ranges::equal(x, (int[]){1,3,2});
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/none_of/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/none_of/constrained.cc
new file mode 100644
index 00000000000..55617a97a7b
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/none_of/constrained.cc
@@ -0,0 +1,88 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+struct XLess
+{
+  int val;
+  bool operator()(X& x) const { return x.i < val; }
+};
+
+struct ILess
+{
+  int val;
+  bool operator()(int& i) const { return i < val; }
+};
+
+template<typename T>
+struct NotZero
+{
+  bool operator()(T& t) const { return t != 0; }
+};
+
+void
+test01()
+{
+  X x[] = { {2}, {4}, {6}, {8}, {10}, {11} };
+
+  VERIFY( !ranges::none_of(x, x+6, XLess{3}) );
+  VERIFY( !ranges::none_of(x, x+6, ILess{3}, &X::i) );
+  VERIFY( ranges::none_of(x+1, x+6, XLess{3}) );
+  VERIFY( ranges::none_of(x+1, x+6, ILess{3}, &X::i) );
+  VERIFY( !ranges::none_of(x, XLess{5}) );
+  VERIFY( !ranges::none_of(x, ILess{5}, &X::i) );
+
+  test_container<X, forward_iterator_wrapper> c(x);
+  VERIFY( !ranges::none_of(c, NotZero<int>{}, &X::i) );
+
+  test_range<X, input_iterator_wrapper> r(x);
+  VERIFY( !ranges::none_of(r, NotZero<int>{}, &X::i) );
+  VERIFY( !ranges::none_of(r, NotZero<X* const>{}, [](X& x) { return &x; }) );
+}
+
+struct Y { int i; int j; };
+
+void
+test02()
+{
+  static constexpr Y y[] = { {1,2}, {2,4}, {3,6} };
+  static_assert(!ranges::none_of(y, [](int i) { return i%2 == 0; }, &Y::i));
+  static_assert(!ranges::none_of(y, [](const Y& y) { return y.i + y.j == 3; }));
+  static_assert(ranges::none_of(y, [](const Y& y) { return y.i == y.j; }));
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc
new file mode 100644
index 00000000000..34f3013b199
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc
@@ -0,0 +1,76 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[50];
+  std::iota(x, x+50, 0);
+
+  auto pred = std::greater{};
+  auto proj = [] (int a) { return -a; };
+  for (int i = 0; i < 50; i++)
+    {
+      test_range<int, random_access_iterator_wrapper> rx(x);
+      std::ranlux48_base g(i);
+      ranges::shuffle(rx, g);
+
+      auto result = ranges::nth_element(rx, rx.begin()+i, pred, proj);
+      VERIFY( result == rx.end() );
+      VERIFY( x[i] == i );
+      for (int j = 0; j < i; j++)
+	for (int k = i; k < 50; k++)
+	  VERIFY( !pred(proj(x[k]), proj(x[j])) );
+
+      result = ranges::nth_element(rx, rx.begin()+i, pred);
+      VERIFY( result == rx.end() );
+      VERIFY( x[i] == 49-i );
+      for (int j = 0; j < i; j++)
+	for (int k = i; k < 50; k++)
+	  VERIFY( !pred(x[k], x[j]) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {5,2,1,3,4};
+  ranges::nth_element(x, x+3);
+  return x[3] == 4;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc
new file mode 100644
index 00000000000..430e3c0b61b
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc
@@ -0,0 +1,84 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  for (unsigned size = 0; size < 50; ++size)
+    {
+      std::vector<int> vref(size);
+      std::iota(vref.begin(), vref.end(), 0);
+      std::vector<int> v1(vref), v2(vref);
+      test_container<int, random_access_iterator_wrapper> c
+	= {&v1[0], &v1[0] + size};
+      test_range<int, random_access_iterator_wrapper> r
+	= {&v2[0], &v2[0] + size};
+
+      std::ranlux48_base g1(size), g2(size + 1);
+      ranges::shuffle(c, g1);
+      ranges::shuffle(ranges::begin(r), ranges::end(r), g2);
+
+      for (unsigned middle = 0; middle < std::min(size, 10U); ++middle)
+	{
+	  auto res1 = ranges::partial_sort(c.begin(), c.begin()+middle, c.end(),
+					   {}, std::negate<>{});
+	  VERIFY( res1 == c.end() );
+
+	  auto res2 = ranges::partial_sort(r,
+					   ranges::begin(r)+middle,
+					   ranges::greater{});
+	  VERIFY( res2 == ranges::end(r) );
+
+	  VERIFY( ranges::equal(c.begin(), c.begin()+middle,
+				r.begin(), r.begin()+middle) );
+	  VERIFY( ranges::equal(c.begin(), c.begin()+middle,
+				vref.rbegin(), vref.rbegin()+middle) );
+	}
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = { 5,4,1,3,2 };
+  const int y[] = { 1,2,3 };
+  ranges::partial_sort(x, x+3, x+5);
+  return ranges::equal(x, x+3, y, y+3);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc
new file mode 100644
index 00000000000..6b586f885dd
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  for (unsigned size = 0; size < 50; ++size)
+    {
+      std::vector<int> vref(size);
+      std::iota(vref.begin(), vref.end(), 0);
+      std::vector<int> v1(vref), v2(vref);
+
+      std::ranlux48_base g1(size), g2(size + 1);
+      ranges::shuffle(v1, g1);
+      ranges::shuffle(v2, g2);
+
+      for (unsigned middle = 0; middle < 10; ++middle)
+	{
+	  test_container<int, forward_iterator_wrapper> c
+	    = {&v1[0], &v1[0] + size};
+	  test_range<int, input_iterator_wrapper> r
+	    = {&v2[0], &v2[0] + size};
+
+	  std::vector<int> o1(middle), o2(middle);
+	  test_range<int, random_access_iterator_wrapper> w1
+	    = {&o1[0], &o1[0]+middle};
+	  test_range<int, random_access_iterator_wrapper> w2
+	    = {&o2[0], &o2[0]+middle};
+
+	  auto [in1, out1] = ranges::partial_sort_copy(c.begin(), c.end(),
+						       w1.begin(), w1.end(),
+						       {},
+						       std::negate<>{},
+						       std::negate<>{});
+	  VERIFY( in1 == c.end() );
+	  VERIFY( out1 == w1.begin() + std::min(size, middle) );
+
+	  auto [in2,out2] = ranges::partial_sort_copy(r, w2, ranges::greater{});
+	  VERIFY( in2 == ranges::end(r) );
+	  VERIFY( out2 == w2.begin() + std::min(size, middle) );
+
+	  VERIFY( ranges::equal(w1.begin(), out1, w2.begin(), out2) );
+	  VERIFY( ranges::equal(w1.begin(), out1,
+				vref.rbegin(),
+				vref.rbegin()+(out1-w1.begin())) );
+	}
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = { 5,4,1,3,2 };
+  int w[3];
+  const int y[] = { 1,2,3 };
+  ranges::partial_sort_copy(x, x+5, w, w+3);
+  return ranges::equal(w, y);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/partition/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/partition/constrained.cc
new file mode 100644
index 00000000000..4e5fa5e32cd
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/partition/constrained.cc
@@ -0,0 +1,71 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+template<template<typename> typename wrapper>
+void
+test01()
+{
+    {
+      int x[] = {1,2,3,4,5,6,7,8,9,10};
+      test_container<int, wrapper> cx(x);
+      auto range = ranges::partition(cx, [] (int a) { return a%2==0; });
+      VERIFY( range.begin().ptr == x+5 );
+      VERIFY( range.end().ptr == x+10 );
+      VERIFY( ranges::is_partitioned(cx, [] (int a) { return a%2==0; }) );
+    }
+
+    {
+      int x[] = {1,2,3,4,5,6,7,8};
+      test_range<int, wrapper> rx(x);
+      auto range = ranges::partition(rx,
+				     [] (int a) { return a%2==0; },
+				     [] (int a) { return a+1; });
+      VERIFY( range.begin().ptr == x+4 );
+      VERIFY( range.end().ptr == x+8 );
+      VERIFY( ranges::is_partitioned(rx, [] (int a) { return a%2==1; }) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1,2,3,4,5,6,7,8,9,10};
+  auto range = ranges::partition(x, x+9, [] (int a) { return a < 100; });
+  return (range.begin() == x+9 && range.end() == x+9);
+}
+
+int
+main()
+{
+  test01<forward_iterator_wrapper>();
+  test01<bidirectional_iterator_wrapper>();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/partition_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/partition_copy/constrained.cc
new file mode 100644
index 00000000000..8ed6e24d451
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/partition_copy/constrained.cc
@@ -0,0 +1,81 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+    {
+      int x[] = {1,2,3,4,5,6,7,8,9,10,11};
+      int y[5], z[6];
+      test_container<int, forward_iterator_wrapper> cx(x);
+      test_container<int, forward_iterator_wrapper> cy(y), cz(z);
+      auto pred = [] (int a) { return a%2==0; };
+      auto [in,out_true,out_false]
+	= ranges::partition_copy(cx, cy.begin(), cz.begin(), pred);
+      VERIFY( in.ptr == x+11 );
+      VERIFY( out_true.ptr == y+5 );
+      VERIFY( out_false.ptr == z+6 );
+      VERIFY( ranges::all_of(cy, pred) );
+      VERIFY( ranges::none_of(cz, pred) );
+    }
+
+    {
+      int x[] = {1,2,3,4,5,6,7,8,9,10,11};
+      int y[6], z[5];
+      test_range<int, input_iterator_wrapper> cx(x);
+      test_range<int, output_iterator_wrapper> cy(y), cz(z);
+      auto pred = [] (int a) { return a%2==0; };
+      auto proj = [] (int a) { return a+1; };
+      auto [in,out_true,out_false]
+	= ranges::partition_copy(cx, cy.begin(), cz.begin(), pred, proj);
+      VERIFY( in.ptr == x+11 );
+      VERIFY( out_true.ptr == y+6 );
+      VERIFY( out_false.ptr == z+5 );
+      VERIFY( ranges::none_of(y, pred) );
+      VERIFY( ranges::all_of(z, pred) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1,2,3,4,5,6,7,8,9,10};
+  auto range = ranges::partition(x, x+9, [] (int a) { return a < 100; });
+  return (range.begin() == x+9 && range.end() == x+9);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/partition_point/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/partition_point/constrained.cc
new file mode 100644
index 00000000000..2a430f23438
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/partition_point/constrained.cc
@@ -0,0 +1,67 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  for (int k = 1; k <= 7; k++)
+    {
+      int x[] = {1,2,3,4,5,6,7};
+      test_container<int, forward_iterator_wrapper> cx(x);
+      auto pred = [&] (int a) { return a <= k; };
+      auto middle = ranges::partition_point(cx, pred);
+      VERIFY( middle.ptr == x+k );
+    }
+
+  for (int k = 1; k <= 8; k++)
+    {
+      int x[] = {1,2,3,4,5,6,7,8};
+      test_range<int, forward_iterator_wrapper> rx(x);
+      auto pred = [&] (int a) { return a > -k; };
+      auto proj = [] (int a) { return -a; };
+      auto middle = ranges::partition_point(rx, pred, proj);
+      VERIFY( middle.ptr == x+k-1 );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  return (ranges::partition_point(x, x+5, [] (int a) { return a < 6; }) == x+5
+	  && ranges::partition_point(x, x+5, [] (int a) { return a < 0; }) == x);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/prev_permutation/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/prev_permutation/constrained.cc
new file mode 100644
index 00000000000..25bbad9be0c
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/prev_permutation/constrained.cc
@@ -0,0 +1,84 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {5, 4, 3, 2, 1};
+  int y[] = {5, 4, 3, 2, 1};
+
+  for (int i = 0; i <= 5; i++)
+    {
+      test_container<int, bidirectional_iterator_wrapper> cx(x, x+i);
+      test_container<int, bidirectional_iterator_wrapper> cy(y, y+i);
+      for (int j = 0; ; j++)
+	{
+	  auto found1 = std::prev_permutation(cx.begin(), cx.end());
+	  auto [found2,last] = ranges::prev_permutation(cy.begin(), cy.end());
+	  VERIFY( found1 == found2 );
+	  VERIFY( ranges::equal(cx, cy) );
+	  if (!found2)
+	    break;
+	}
+    }
+}
+
+void
+test02()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto [found,last] = ranges::prev_permutation(rx, ranges::greater{});
+  VERIFY( found && last == rx.end() );
+  VERIFY( last == rx.end() );
+  VERIFY( ranges::equal(rx, (int[]){1,2,3,5,4}) );
+  ranges::prev_permutation(rx, {}, [] (int a) { return -a; });
+  VERIFY( ranges::equal(rx, (int[]){1,2,4,3,5}) );
+
+  VERIFY( !ranges::prev_permutation(x, x).found );
+  VERIFY( !ranges::prev_permutation(x, x+1).found );
+}
+
+constexpr bool
+test03()
+{
+  int x[] = {3,2,1};
+  ranges::prev_permutation(x);
+  return ranges::equal(x, (int[]){3,1,2});
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
+
diff --git a/libstdc++-v3/testsuite/25_algorithms/remove/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/remove/constrained.cc
new file mode 100644
index 00000000000..39a002f3cb6
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/remove/constrained.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+};
+
+void
+test01()
+{
+  int x[5] = { 1, 2, 3, 4, 5 };
+  const int y[4] = { 1, 2, 4, 5 };
+  auto res = ranges::remove(x, 3);
+  VERIFY( res.begin() == x+4 && res.end() == x+5 );
+  VERIFY( ranges::equal(x, x+4, y, y+4) );
+}
+
+void
+test02()
+{
+  int x[1];
+  test_container<int, forward_iterator_wrapper> c(x, x);
+  auto res = ranges::remove(c, 1);
+  VERIFY( res.begin().ptr == x && res.end().ptr == x );
+}
+
+void
+test03()
+{
+  int x[1] = {1};
+  test_container<int, forward_iterator_wrapper> c(x);
+  auto res = ranges::remove(c, 0);
+  VERIFY( res.begin().ptr == x+1 && res.end().ptr == x+1 );
+  res = ranges::remove(c, 1);
+  VERIFY( res.begin().ptr == x && res.end().ptr == x+1 );
+}
+
+void
+test04()
+{
+  X x[8] = { {0}, {1}, {0}, {1}, {0}, {0}, {1}, {1} };
+  const int y[4] = { 0, 0, 0, 0 };
+  test_container<X, forward_iterator_wrapper> c(x);
+  auto res = ranges::remove(c, 1, &X::i);
+  VERIFY( res.begin().ptr == x+4 && res.end().ptr == x+8 );
+  VERIFY( ranges::equal(x, x+4, y, y+4, {}, &X::i) );
+}
+
+constexpr bool
+test05()
+{
+  int x[6] = { 3, 2, 3, 3, 5, 3 };
+  const int y[2] = { 2, 5 };
+  auto res = ranges::remove(x, 3);
+  return ranges::equal(x, res.begin(), y, y+2);
+}
+
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  static_assert(test05());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/remove_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/remove_copy/constrained.cc
new file mode 100644
index 00000000000..0cf65a727b8
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/remove_copy/constrained.cc
@@ -0,0 +1,109 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  {
+    return a.i == b.i;
+  }
+};
+
+void
+test01()
+{
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[4];
+      X z[4] = { {2}, {2}, {6}, {10} };
+      auto [in,out] = ranges::remove_copy(x, x+5, y, 8, &X::i);
+      VERIFY( in == x+5 && out == y+4 );
+      VERIFY( ranges::equal(y, z) );
+    }
+
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[5];
+      X z[5] = { {2}, {2}, {6}, {8}, {10} };
+      auto [in,out] = ranges::remove_copy(x, x+5, y, 11, &X::i);
+      VERIFY( in == x+5 && out == y+5 );
+      VERIFY( ranges::equal(x, x+5, y, y+5) && ranges::equal(y, z) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {2} };
+      X y[3];
+      X z[3] = { {6}, {8}, {10} };
+      test_container<X, forward_iterator_wrapper> cx(x), cy(y), cz(z);
+      auto [in, out] = ranges::remove_copy(cx, cy.begin(), 2, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(cy, cz) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[4];
+      const X z[4] = { {6}, {8}, {10}, {11} };
+      test_range<X, input_iterator_wrapper> cx(x);
+      test_range<X, output_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::remove_copy(cx, cy.begin(), 2, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(y, z) );
+    }
+}
+
+struct Y { int i; int j; };
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  Y x[3] = { {3,2}, {2,4}, {3,6} };
+  Y y[1];
+  Y z[1] = { {2,4} };
+  auto [in, out] = ranges::remove_copy(x, y, 3, &Y::i);
+  ok &= in == x+3;
+  ok &= out == y+1;
+  ok &= ranges::equal(y, z, {}, &Y::i, &Y::i);
+  ok &= ranges::equal(y, z, {}, &Y::j, &Y::j);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/remove_copy_if/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/remove_copy_if/constrained.cc
new file mode 100644
index 00000000000..b7c239ff397
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/remove_copy_if/constrained.cc
@@ -0,0 +1,113 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  {
+    return a.i == b.i;
+  }
+};
+
+void
+test01()
+{
+  auto is_negative_p = [] (int a) { return a < 0; };
+  auto is_two_p = [] (int a) { return a == 2; };
+
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {2}, {11} };
+      X y[2];
+      X z[2] = { {6}, {8} };
+      auto [in, out] = ranges::remove_copy_if(x, x+5, y, is_two_p, &X::i);
+      VERIFY( in == x+5 && out == y+2 );
+      VERIFY( ranges::equal(y, z) );
+    }
+
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[5];
+      X z[5] = { {2}, {2}, {6}, {8}, {10} };
+      auto [in, out] = ranges::remove_copy_if(x, x+5, y, is_negative_p, &X::i);
+      VERIFY( in == x+5 && out == y+5 );
+      VERIFY( ranges::equal(x, x+5, y, y+5) && ranges::equal(y, z) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[4];
+      X z[4] = { {6}, {8}, {10}, {11} };
+      test_container<X, forward_iterator_wrapper> cx(x), cy(y), cz(z);
+      auto [in, out] = ranges::remove_copy_if(cx, cy.begin(), is_two_p, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(cy, cz) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[4];
+      const X z[4] = { {6}, {8}, {10}, {11} };
+      test_range<X, input_iterator_wrapper> cx(x);
+      test_range<X, output_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::remove_copy_if(cx, cy.begin(), is_two_p, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(y, z) );
+    }
+}
+
+struct Y { int i; int j; };
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  Y x[3] = { {3,2}, {2,4}, {3,6} };
+  Y y[1];
+  Y z[1] = { {2,4} };
+  auto [in, out]
+    = ranges::remove_copy_if(x, y, [] (int a) { return a%2 == 1; }, &Y::i);
+  ok &= in == x+3;
+  ok &= out == y+1;
+  ok &= ranges::equal(y, z, {}, &Y::i, &Y::i);
+  ok &= ranges::equal(y, z, {}, &Y::j, &Y::j);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/remove_if/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/remove_if/constrained.cc
new file mode 100644
index 00000000000..1abc231ab93
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/remove_if/constrained.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+};
+
+void
+test01()
+{
+  int x[5] = { 1, 2, 3, 4, 5 };
+  const int y[4] = { 1, 2, 4, 5 };
+  auto res = ranges::remove_if(x, [] (int a) { return a == 3; });
+  VERIFY( res.begin() == x+4 && res.end() == x+5 );
+  VERIFY( ranges::equal(x, x+4, y, y+4) );
+}
+
+void
+test02()
+{
+  int x[1];
+  test_container<int, forward_iterator_wrapper> c(x, x);
+  auto res = ranges::remove_if(c, [] (int a) { return a == 1; });
+  VERIFY( res.begin().ptr == x && res.end().ptr == x );
+}
+
+void
+test03()
+{
+  int x[1] = {1};
+  test_container<int, forward_iterator_wrapper> c(x);
+  auto res = ranges::remove_if(c, [] (int a) { return a == 0; });
+  VERIFY( res.begin().ptr == x+1 && res.end().ptr == x+1 );
+  res = ranges::remove_if(c, [] (int a) { return a == 1; });
+  VERIFY( res.begin().ptr == x && res.end().ptr == x+1 );
+}
+
+void
+test04()
+{
+  X x[8] = { {0}, {1}, {0}, {1}, {0}, {0}, {1}, {1} };
+  const int y[4] = { 0, 0, 0, 0 };
+  test_range<X, forward_iterator_wrapper> c(x);
+  auto res = ranges::remove_if(c, [] (int a) { return a == 1; }, &X::i);
+  VERIFY( res.begin().ptr == x+4 && res.end().ptr == x+8 );
+  VERIFY( ranges::equal(x, x+4, y, y+4, {}, &X::i) );
+}
+
+constexpr bool
+test05()
+{
+  int x[6] = { 3, 2, 3, 3, 5, 3 };
+  const int y[2] = { 2, 5 };
+  auto res = ranges::remove_if(x, [] (int a) { return a == 3; });
+  return ranges::equal(x, res.begin(), y, y+2);
+}
+
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  static_assert(test05());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/replace/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/replace/constrained.cc
new file mode 100644
index 00000000000..3546872b72f
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/replace/constrained.cc
@@ -0,0 +1,104 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  {
+    return a.i == b.i;
+  }
+};
+
+void
+test01()
+{
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6] = { {2}, {2}, {6}, {9}, {10}, {11} };
+      auto res = ranges::replace(x, x+5, 8, X{9}, &X::i);
+      VERIFY( res == x+5 );
+      VERIFY( ranges::equal(x, y) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      auto res = ranges::replace(x, x+5, 7, X{9}, &X::i);
+      VERIFY( res == x+5 );
+      VERIFY( ranges::equal(x, y) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6] = { {7}, {7}, {6}, {8}, {10}, {11} };
+      test_container<X, forward_iterator_wrapper> cx(x), cy(y);
+      auto res = ranges::replace(cx, 2, X{7}, &X::i);
+      VERIFY( res == cx.end() );
+      VERIFY( ranges::equal(cx, cy) );
+    }
+
+    {
+      int x[6] = { {2}, {2}, {6}, {8}, {10}, {2} };
+      int y[6] = { {7}, {7}, {6}, {8}, {10}, {7} };
+      test_range<int, input_iterator_wrapper> rx(x), ry(y);
+      auto res = ranges::replace(rx, 2, 7);
+      VERIFY( res == rx.end() );
+
+      rx.bounds.first = x;
+      ry.bounds.first = y;
+      VERIFY( ranges::equal(rx, ry) );
+    }
+}
+
+struct Y { int i; int j; };
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  Y x[] = { {3,2}, {2,4}, {3,6} };
+  Y y[] = { {4,5}, {2,4}, {4,5} };
+  auto res = ranges::replace(x, 3, Y{4,5}, &Y::i);
+  ok &= res == x+3;
+  ok &= ranges::equal(x, y, {}, &Y::i, &Y::i);
+  ok &= ranges::equal(x, y, {}, &Y::j, &Y::j);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/replace_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/replace_copy/constrained.cc
new file mode 100644
index 00000000000..12e76a4cfdf
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/replace_copy/constrained.cc
@@ -0,0 +1,109 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  {
+    return a.i == b.i;
+  }
+};
+
+void
+test01()
+{
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[5];
+      X z[5] = { {2}, {2}, {6}, {9}, {10} };
+      auto [in,out] = ranges::replace_copy(x, x+5, y, 8, X{9}, &X::i);
+      VERIFY( in == x+5 && out == y+5 );
+      VERIFY( ranges::equal(y, z) );
+    }
+
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[5];
+      X z[5] = { {2}, {2}, {6}, {8}, {10} };
+      auto [in,out] = ranges::replace_copy(x, x+5, y, 7, X{9}, &X::i);
+      VERIFY( in == x+5 && out == y+5 );
+      VERIFY( ranges::equal(x, x+5, y, y+5) && ranges::equal(y, z) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6];
+      X z[6] = { {7}, {7}, {6}, {8}, {10}, {11} };
+      test_container<X, forward_iterator_wrapper> cx(x), cy(y), cz(z);
+      auto [in, out] = ranges::replace_copy(cx, cy.begin(), 2, X{7}, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(cy, cz) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6];
+      const X z[6] = { {7}, {7}, {6}, {8}, {10}, {11} };
+      test_range<X, input_iterator_wrapper> cx(x);
+      test_range<X, output_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::replace_copy(cx, cy.begin(), 2, X{7}, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(y, z) );
+    }
+}
+
+struct Y { int i; int j; };
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  Y x[] = { {3,2}, {2,4}, {3,6} };
+  Y y[] = { {4,5}, {2,4}, {4,5} };
+  Y z[] = { {4,5}, {2,4}, {4,5} };
+  auto [in, out] = ranges::replace_copy(x, y, 3, Y{4,5}, &Y::i);
+  ok &= in == x+3;
+  ok &= out == y+3;
+  ok &= ranges::equal(y, z, {}, &Y::i, &Y::i);
+  ok &= ranges::equal(y, z, {}, &Y::j, &Y::j);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/replace_copy_if/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/replace_copy_if/constrained.cc
new file mode 100644
index 00000000000..9186a0a3d6c
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/replace_copy_if/constrained.cc
@@ -0,0 +1,118 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  {
+    return a.i == b.i;
+  }
+};
+
+void
+test01()
+{
+  auto is_negative_p = [] (int a) { return a < 0; };
+  auto is_two_p = [] (int a) { return a == 2; };
+
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[5];
+      X z[5] = { {9}, {9}, {6}, {8}, {10} };
+      auto [in, out] = ranges::replace_copy_if(x, x+5, y,
+					       is_two_p, X{9}, &X::i);
+      VERIFY( in == x+5 && out == y+5 );
+      VERIFY( ranges::equal(y, z) );
+    }
+
+    {
+      const X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[5];
+      X z[5] = { {2}, {2}, {6}, {8}, {10} };
+      auto [in, out] = ranges::replace_copy_if(x, x+5, y,
+					       is_negative_p, X{9}, &X::i);
+      VERIFY( in == x+5 && out == y+5 );
+      VERIFY( ranges::equal(x, x+5, y, y+5) && ranges::equal(y, z) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6];
+      X z[6] = { {7}, {7}, {6}, {8}, {10}, {11} };
+      test_container<X, forward_iterator_wrapper> cx(x), cy(y), cz(z);
+      auto [in, out] = ranges::replace_copy_if(cx, cy.begin(),
+					       is_two_p, X{7}, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(cy, cz) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6];
+      const X z[6] = { {7}, {7}, {6}, {8}, {10}, {11} };
+      test_range<X, input_iterator_wrapper> cx(x);
+      test_range<X, output_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::replace_copy_if(cx, cy.begin(),
+					       is_two_p, X{7}, &X::i);
+      VERIFY( in == cx.end() && out == cy.end() );
+      VERIFY( ranges::equal(y, z) );
+    }
+}
+
+struct Y { int i; int j; };
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  Y x[] = { {3,2}, {2,4}, {3,6} };
+  Y y[] = { {4,5}, {2,4}, {4,5} };
+  Y z[] = { {4,5}, {2,4}, {4,5} };
+  auto [in, out]
+    = ranges::replace_copy_if(x, y,
+			      [] (int a) { return a%2 == 1; }, Y{4,5}, &Y::i);
+  ok &= in == x+3;
+  ok &= out == y+3;
+  ok &= ranges::equal(y, z, {}, &Y::i, &Y::i);
+  ok &= ranges::equal(y, z, {}, &Y::j, &Y::j);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/replace_if/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/replace_if/constrained.cc
new file mode 100644
index 00000000000..8ebcc411997
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/replace_if/constrained.cc
@@ -0,0 +1,109 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  {
+    return a.i == b.i;
+  }
+};
+
+void
+test01()
+{
+  auto is_even_p = [] (int a) { return a%2 == 0; };
+  auto is_negative_p = [] (int a) { return a < 0; };
+  auto is_two_p = [] (int a) { return a == 2; };
+    {
+      X x[6] = { {1}, {2}, {6}, {8}, {10}, {11} };
+      X y[6] = { {1}, {9}, {9}, {9}, {9}, {11} };
+      auto res = ranges::replace_if(x, x+5, is_even_p, X{9}, &X::i);
+      VERIFY( res == x+5 );
+      VERIFY( ranges::equal(x, y) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      auto res = ranges::replace_if(x, x+5, is_negative_p, X{9}, &X::i);
+      VERIFY( res == x+5 );
+      VERIFY( ranges::equal(x, y) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {10}, {11} };
+      X y[6] = { {7}, {7}, {6}, {8}, {10}, {11} };
+      test_container<X, forward_iterator_wrapper> cx(x), cy(y);
+      auto res = ranges::replace_if(cx, is_two_p, X{7}, &X::i);
+      VERIFY( res == cx.end() );
+      VERIFY( ranges::equal(cx, cy) );
+    }
+
+    {
+      int x[6] = { {2}, {2}, {6}, {8}, {10}, {2} };
+      int y[6] = { {7}, {7}, {6}, {8}, {10}, {7} };
+      test_range<int, input_iterator_wrapper> rx(x), ry(y);
+      auto res = ranges::replace_if(rx, is_two_p, 7);
+      VERIFY( res == rx.end() );
+
+      rx.bounds.first = x;
+      ry.bounds.first = y;
+      VERIFY( ranges::equal(rx, ry) );
+    }
+}
+
+struct Y { int i; int j; };
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  Y x[] = { {3,2}, {2,4}, {3,6} };
+  Y y[] = { {4,5}, {2,4}, {4,5} };
+  auto res = ranges::replace_if(x, [] (int a) { return a%2 == 1; },
+				Y{4,5}, &Y::i);
+  ok &= res == x+3;
+  ok &= ranges::equal(x, y, {}, &Y::i, &Y::i);
+  ok &= ranges::equal(x, y, {}, &Y::j, &Y::j);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
+
diff --git a/libstdc++-v3/testsuite/25_algorithms/reverse/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/reverse/constrained.cc
new file mode 100644
index 00000000000..58cec27b5a0
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/reverse/constrained.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::test_container;
+using __gnu_test::bidirectional_iterator_wrapper;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+template<template<typename> typename wrapper>
+void
+test01()
+{
+  int x[] = { 1, 2, 3, 4 };
+  test_container<int, wrapper> cx(x);
+  const int y[] = { 4, 3, 2, 1 };
+
+  auto res = ranges::reverse(cx);
+  VERIFY( res == ranges::end(cx) );
+  VERIFY( ranges::equal(cx, y) );
+}
+
+template<template<typename> typename wrapper>
+void
+test02()
+{
+  int x[] = { 1, 2, 3, 4, 5 };
+  test_range<int, wrapper> rx(x);
+  const int y[] = { 5, 4, 3, 2, 1 };
+
+  auto res = ranges::reverse(rx);
+  VERIFY( res == ranges::end(rx) );
+  VERIFY( ranges::equal(rx, y) );
+}
+
+constexpr bool
+test03()
+{
+  int x[] = { 1, 2, 3 };
+  const int y[] = { 2, 1, 3 };
+  ranges::reverse(x, x+2);
+  return ranges::equal(x, y);
+}
+
+int
+main()
+{
+  test01<bidirectional_iterator_wrapper>();
+  test02<bidirectional_iterator_wrapper>();
+
+  test01<random_access_iterator_wrapper>();
+  test02<random_access_iterator_wrapper>();
+
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/reverse_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/reverse_copy/constrained.cc
new file mode 100644
index 00000000000..1ee40bedb39
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/reverse_copy/constrained.cc
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::test_container;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = { 1, 2, 3, 4 };
+  int w[4];
+  test_container<int, bidirectional_iterator_wrapper> cx(x), cw(w);
+  const int y[] = { 4, 3, 2, 1 };
+
+  auto [in,out] = ranges::reverse_copy(cx, cw.begin());
+  VERIFY( in == ranges::end(cx) && out == cw.end() );
+  VERIFY( ranges::equal(cw, y) );
+}
+
+void
+test02()
+{
+  int x[] = { 1, 2, 3, 4, 5 };
+  int w[5];
+  test_range<int, bidirectional_iterator_wrapper> rx(x), rw(w);
+  const int y[] = { 5, 4, 3, 2, 1 };
+
+  auto [in,out] = ranges::reverse_copy(rx, ranges::begin(rw));
+  VERIFY( in == ranges::end(rx) && out == ranges::end(rw) );
+  VERIFY( ranges::equal(rw, y) );
+}
+
+constexpr bool
+test03()
+{
+  const int x[] = { 1, 2, 3 };
+  int w[2];
+  const int y[] = { 2, 1 };
+  ranges::reverse_copy(x, x+2, w);
+  return ranges::equal(w, y);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/rotate/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/rotate/constrained.cc
new file mode 100644
index 00000000000..34095e743ad
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/rotate/constrained.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::bidirectional_iterator_wrapper;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+  X (int a) : i(a) { }
+
+  friend bool
+  operator==(const X& lhs, const X& rhs)
+  {
+    return lhs.i == rhs.i;
+  }
+};
+
+static_assert(!std::is_trivial_v<X>);
+
+template<template<typename, template<typename> typename> typename container,
+	 template<typename> typename wrapper,
+	 typename T = int>
+void
+test01()
+{
+  for (int a = 0; a <= 7; a++)
+    {
+      T x[] = {1, 2, 3, 4, 5, 6, 7};
+      container<T, wrapper> rx(x);
+      auto i = ranges::begin(rx);
+      std::advance(i, a);
+      auto res = ranges::rotate(rx, i);
+      if (a == 0)
+	VERIFY( ranges::begin(res) == ranges::end(rx) );
+      else
+	VERIFY( ranges::begin(res)
+		 == std::next(ranges::begin(rx),
+			      ranges::distance(i, ranges::end(rx))) );
+      VERIFY( ranges::end(res) == ranges::end(rx) );
+      for (int k = 0; k < 7; k++)
+	VERIFY( x[k] == (k+a)%7 + 1 );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1, 2, 3, 4};
+  const int y[] = { 2, 3, 1, 4 };
+  ranges::rotate(x, x+1, x+3);
+  return ranges::equal(x, y);
+}
+
+int
+main()
+{
+  test01<test_container, forward_iterator_wrapper>();
+  test01<test_range, forward_iterator_wrapper>();
+
+  test01<test_container, bidirectional_iterator_wrapper>();
+  test01<test_range, bidirectional_iterator_wrapper>();
+
+  test01<test_container, random_access_iterator_wrapper>();
+  test01<test_range, random_access_iterator_wrapper>();
+
+  test01<test_container, random_access_iterator_wrapper, X>();
+  test01<test_range, random_access_iterator_wrapper, X>();
+
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/rotate_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/rotate_copy/constrained.cc
new file mode 100644
index 00000000000..f036377817b
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/rotate_copy/constrained.cc
@@ -0,0 +1,93 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+  X () : i(0) { }
+  X (int a) : i(a) { }
+
+  friend bool
+  operator==(const X& lhs, const X& rhs)
+  {
+    return lhs.i == rhs.i;
+  }
+};
+
+static_assert(!std::is_trivial_v<X>);
+
+template<template<typename, template<typename> typename> typename container,
+	 template<typename> typename wrapper,
+	 typename T = int>
+void
+test01()
+{
+  for (int a = 0; a <= 7; a++)
+    {
+      T x[] = {1, 2, 3, 4, 5, 6, 7};
+      T w[7];
+      container<T, wrapper> rx(x), rw(w);
+      auto i = ranges::begin(rx);
+      std::advance(i, a);
+      auto [in,out] = ranges::rotate_copy(rx, i, ranges::begin(rw));
+      VERIFY( in == ranges::end(rx) );
+      VERIFY( out == ranges::end(rw) );
+      for (int k = 0; k < 7; k++)
+	VERIFY( w[k] == (k+a)%7 + 1 );
+    }
+}
+
+constexpr bool
+test02()
+{
+  const int x[] = {1, 2, 3, 4};
+  int w[3];
+  const int y[] = { 2, 3, 1};
+  auto [in,out] = ranges::rotate_copy(x, x+1, x+3, w);
+  return (in == x+3
+	  && out == w+3
+	  && ranges::equal(w, y));
+}
+
+int
+main()
+{
+  test01<test_container, forward_iterator_wrapper>();
+  test01<test_range, forward_iterator_wrapper>();
+
+  test01<test_container, random_access_iterator_wrapper>();
+  test01<test_range, random_access_iterator_wrapper>();
+
+  test01<test_container, random_access_iterator_wrapper, X>();
+  test01<test_range, random_access_iterator_wrapper, X>();
+
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/search/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/search/constrained.cc
new file mode 100644
index 00000000000..314d9d2acf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/search/constrained.cc
@@ -0,0 +1,88 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  X x[] = { {2}, {6}, {8}, {10}, {11} };
+  X y[] = { {10}, {11} };
+  {
+
+    test_container<X, forward_iterator_wrapper> c(x);
+    auto res = ranges::search(c, y, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res)->i == 10 && std::get<1>(res) == ranges::end(c) );
+    res = ranges::search(c, c, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res) == ranges::begin(c)
+	    && std::get<1>(res) == ranges::end(c) );
+  }
+
+  {
+    test_range<X, forward_iterator_wrapper> r(x);
+    auto res = ranges::search(r, y, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res)->i == 10 && std::get<1>(res) == ranges::end(r) );
+    res = ranges::search(r, r, {}, &X::i, &X::i);
+    VERIFY( std::get<0>(res) == ranges::begin(r)
+	    && std::get<1>(res) == ranges::end(r) );
+  }
+}
+
+void
+test02()
+{
+  static constexpr X x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  static constexpr X y[] = { {6}, {8} };
+  static constexpr int z[] = { 2, 8 };
+  static constexpr int w[] = { 2 };
+
+  static_assert(std::get<0>(ranges::search(x, y, {}, &X::i, &X::i)) == x+2);
+  static_assert(std::get<1>(ranges::search(x, y, {}, &X::i, &X::i)) == x+4);
+
+  static_assert(std::get<0>(ranges::search(x, z, {}, &X::i)) == x+6);
+  static_assert(std::get<1>(ranges::search(x, z, {}, &X::i)) == x+6);
+
+  static_assert(std::get<0>(ranges::search(x, w, {}, &X::i)) == x+0);
+  static_assert(std::get<1>(ranges::search(x, w, {}, &X::i)) == x+1);
+
+  static_assert(std::get<0>(ranges::search(x, x+6, w, w, {}, &X::i)) == x+0);
+  static_assert(std::get<1>(ranges::search(x, x+6, w, w, {}, &X::i)) == x+0);
+
+  static_assert(std::get<0>(ranges::search(x, x, w, w+1, {}, &X::i)) == x+0);
+  static_assert(std::get<1>(ranges::search(x, x, w, w+1, {}, &X::i)) == x+0);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/search_n/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/search_n/constrained.cc
new file mode 100644
index 00000000000..c1ac6da4bf8
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/search_n/constrained.cc
@@ -0,0 +1,80 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+  int x[] = { {2}, {2}, {6}, {8}, {10}, {11} };
+  auto res = ranges::search_n(x+0, x+6, 2, 2);
+  VERIFY( res.begin() == x+0 && res.end() == x+2 );
+
+  int z[] = { {1}, {2}, {2}, {4}, {5}, {6} };
+  res = ranges::search_n(z, 3, 3, std::greater<int>());
+  VERIFY( res.begin() == z+3 && res.end() == z+6 );
+
+  test_container<int, forward_iterator_wrapper> cx(x);
+  auto res2 = ranges::search_n(cx, 2, 2);
+  VERIFY( res2.begin() == cx.begin() && *res2.end() == 6 );
+
+  int y[] = { {2}, {2}, {8}, {2}, {2}, {2}, {5} };
+  test_range<int, forward_iterator_wrapper> ry(y);
+  auto res3 = ranges::search_n(ry, 3, 2);
+  VERIFY( *res3.begin() == 2 && *res3.end() == 5 );
+
+  auto res4 = ranges::search_n(ry, 1, 8);
+  VERIFY( res4.begin().ptr == y+2 && res4.end().ptr == y+3 );
+}
+
+void
+test02()
+{
+  static constexpr X x[] = { {2}, {2}, {6}, {8}, {10}, {2} };
+  static constexpr X y[] = { {2}, {6}, {8}, {8}, {8}, {2} };
+  static constexpr int z[] = { {2}, {6}, {8}, {10}, {2}, {2} };
+
+  static_assert(ranges::search_n(z, 0, 5).end() == z+0);
+  static_assert(ranges::search_n(z, 1, 5).begin() == z+6);
+  static_assert(ranges::search_n(x, 2, 3, {}, &X::i).begin() == x+6);
+  static_assert(ranges::search_n(x, 2, 2, {}, &X::i).end() == x+2);
+  static_assert(ranges::search_n(y, 3, 8, {}, &X::i).begin() == y+2);
+  static_assert(ranges::search_n(y, 3, 8, {}, &X::i).end() == y+5);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
+
diff --git a/libstdc++-v3/testsuite/25_algorithms/set_difference/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/set_difference/constrained.cc
new file mode 100644
index 00000000000..3f46a9b523e
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/set_difference/constrained.cc
@@ -0,0 +1,87 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {4,2,1,1,0};
+  int y[] = {3,2,1};
+  int z[3];
+  test_range<int, input_iterator_wrapper> rx(x), ry(y);
+  test_range<int, output_iterator_wrapper> rz(z);
+  auto [in,out]
+    = ranges::set_difference(rx, ry, rz.begin(), ranges::greater{});
+  VERIFY( in.ptr == x+5 );
+  VERIFY( out.ptr == z+3 );
+  VERIFY( ranges::equal(z, (int[]){4,1,0}) );
+}
+
+void
+test02()
+{
+  int x[] = {3,2,1,1,0};
+  int y[] = {3,2,2,1,0};
+  int z[1];
+  test_container<int, forward_iterator_wrapper> rx(x), ry(y);
+  test_container<int, forward_iterator_wrapper> rz(z);
+  auto [in,out]
+    = ranges::set_difference(rx.begin(), rx.end(),
+			     ry.begin(), ry.end(),
+			     rz.begin(),
+			     {},
+			     std::negate<>{},
+			     std::negate<>{});
+  VERIFY( in.ptr == x+5 );
+  VERIFY( out.ptr == z+1 );
+  VERIFY( ranges::equal(z, (int[]){1}) );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  int x[1] = {0};
+  int y[1] = {1};
+  int z[1];
+  ok &= ranges::set_difference(x, x, y, y+1, z).out == z;
+  ok &= ranges::set_difference(x, x+1, y, y, z).out == z+1;
+  ok &= z[0] == 0;
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/set_intersection/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/set_intersection/constrained.cc
new file mode 100644
index 00000000000..0db3e411871
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/set_intersection/constrained.cc
@@ -0,0 +1,88 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {4,2,1,1,0};
+  int y[] = {3,2,1};
+  int z[2];
+  test_range<int, input_iterator_wrapper> rx(x), ry(y);
+  test_range<int, output_iterator_wrapper> rz(z);
+  auto [in1,in2,out]
+    = ranges::set_intersection(rx, ry, rz.begin(), ranges::greater{});
+  VERIFY( in1.ptr == x+5 );
+  VERIFY( in2.ptr == y+3 );
+  VERIFY( out.ptr == z+2 );
+  VERIFY( ranges::equal(z, (int[]){2,1}) );
+}
+
+void
+test02()
+{
+  int x[] = {3,2,1,1,0};
+  int y[] = {3,2,1,0};
+  int z[4];
+  test_container<int, forward_iterator_wrapper> rx(x), ry(y);
+  test_container<int, forward_iterator_wrapper> rz(z);
+  auto [in1,in2,out]
+    = ranges::set_intersection(rx.begin(), rx.end(),
+			       ry.begin(), ry.end(),
+			       rz.begin(),
+			       {},
+			       std::negate<>{},
+			       std::negate<>{});
+  VERIFY( in1.ptr == x+5 );
+  VERIFY( in2.ptr == y+4 );
+  VERIFY( out.ptr == z+4 );
+  VERIFY( ranges::equal(z, (int[]){3,2,1,0}) );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  int x[1] = {0};
+  int y[1] = {1};
+  int z[1];
+  ok &= ranges::set_intersection(x, x, y, y+1, z).out == z;
+  ok &= ranges::set_intersection(x, x+1, y, y, z).out == z;
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/set_symmetric_difference/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/set_symmetric_difference/constrained.cc
new file mode 100644
index 00000000000..b138aee3adb
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/set_symmetric_difference/constrained.cc
@@ -0,0 +1,123 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {4,2,1,1,0};
+  int y[] = {3,2,2,1};
+  int z[5];
+  test_range<int, input_iterator_wrapper> rx(x), ry(y);
+  test_range<int, output_iterator_wrapper> rz(z);
+  auto [in1,in2,out]
+    = ranges::set_symmetric_difference(rx, ry, rz.begin(), ranges::greater{});
+  VERIFY( in1.ptr == x+5 );
+  VERIFY( in2.ptr == y+4 );
+  VERIFY( out.ptr == z+5 );
+  VERIFY( ranges::equal(z, (int[]){4,3,2,1,0}) );
+}
+
+void
+test02()
+{
+  int x[] = {3,2,1,1,0};
+  int y[] = {3,2,1,0};
+  int z[1];
+  test_container<int, forward_iterator_wrapper> rx(x), ry(y);
+  test_container<int, forward_iterator_wrapper> rz(z);
+  auto [in1,in2,out]
+    = ranges::set_symmetric_difference(rx.begin(), rx.end(),
+				       ry.begin(), ry.end(),
+				       rz.begin(),
+				       {},
+				       std::negate<>{},
+				       std::negate<>{});
+  VERIFY( in1.ptr == x+5 );
+  VERIFY( in2.ptr == y+4 );
+  VERIFY( out.ptr == z+1 );
+  VERIFY( ranges::equal(z, (int[]){1}) );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  int x[1] = {0};
+  int y[1] = {1};
+  int z[1];
+  ok &= ranges::set_symmetric_difference(x, x, y, y+1, z).out == z+1;
+  ok &= z[0] == 1;
+  ok &= ranges::set_symmetric_difference(x, x+1, y, y, z).out == z+1;
+  ok &= z[0] == 0;
+  return ok;
+}
+
+void
+test04()
+{
+  int x[15] = {5,5,5,5,5,4,4,4,4,3,3,3,2,2,1};
+  int y[15] = {5,5,5,5,5,4,4,4,4,3,3,3,2,2,1};
+  for (int k = 0; k < 100; k++)
+    {
+      std::ranlux48_base g(k);
+      ranges::shuffle(x, g);
+      ranges::shuffle(y, g);
+      ranges::sort(x, x+10);
+      ranges::sort(y, y+10);
+
+      int z[15];
+      auto z_out = ranges::set_symmetric_difference(x, x+10, y, y+10, z).out;
+
+      int w1[15];
+      auto w1_out = ranges::set_difference(x, x+10, y, y+10, w1).out;
+
+      int w2[15];
+      auto w2_out = ranges::set_difference(y, y+10, x, x+10, w2).out;
+
+      int w3[15];
+      auto w3_out = ranges::set_union(w1, w1_out, w2, w2_out, w3).out;
+
+      VERIFY( ranges::equal(z, z_out, w3, w3_out) );
+    }
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/set_union/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/set_union/constrained.cc
new file mode 100644
index 00000000000..19bcd7aab9f
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/set_union/constrained.cc
@@ -0,0 +1,91 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {4,2,1,1,0};
+  int y[] = {3,2,1};
+  int z[6];
+  test_range<int, input_iterator_wrapper> rx(x), ry(y);
+  test_range<int, output_iterator_wrapper> rz(z);
+  auto [in1,in2,out]
+    = ranges::set_union(rx, ry, rz.begin(),
+			ranges::greater{});
+  VERIFY( in1.ptr == x+5 );
+  VERIFY( in2.ptr == y+3 );
+  VERIFY( out.ptr == z+6 );
+  VERIFY( ranges::equal(z, (int[]){4,3,2,1,1,0}) );
+}
+
+void
+test02()
+{
+  int x[] = {4,2,1,1,0};
+  int y[] = {3,2,1,1,0};
+  int z[6];
+  test_container<int, forward_iterator_wrapper> rx(x), ry(y);
+  test_container<int, forward_iterator_wrapper> rz(z);
+  auto [in1,in2,out]
+    = ranges::set_union(rx.begin(), rx.end(),
+			ry.begin(), ry.end(),
+			rz.begin(),
+			{},
+			std::negate<>{},
+			std::negate<>{});
+  VERIFY( in1.ptr == x+5 );
+  VERIFY( in2.ptr == y+5 );
+  VERIFY( out.ptr == z+6 );
+  VERIFY( ranges::equal(z, (int[]){4,3,2,1,1,0}) );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  int x[1] = {0};
+  int y[1] = {1};
+  int z[1];
+  ranges::set_union(x, x, y, y+1, z);
+  ok &= z[0] == 1;
+  ranges::set_union(x, x+1, y, y, z);
+  ok &= z[0] == 0;
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc
new file mode 100644
index 00000000000..b96343aecce
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc
@@ -0,0 +1,70 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+// This test is adapted from 25_algorithms/shuffle/1.cc.
+
+void
+test01()
+{
+  for (unsigned size = 0; size < 50; ++size)
+    {
+      std::vector<int> vref(size);
+      std::iota(vref.begin(), vref.end(), 0);
+      std::vector<int> v1(vref), v2(vref);
+      test_container<int, random_access_iterator_wrapper> c
+	= {&v1[0], &v1[0] + size};
+      test_range<int, random_access_iterator_wrapper> r
+	= {&v2[0], &v2[0] + size};
+
+      std::ranlux48_base g1(size), g2(size + 1);
+      VERIFY( ranges::shuffle(c, g1) == c.end() );
+      VERIFY( ranges::shuffle(ranges::begin(r), ranges::end(r), g2)
+	      == ranges::end(r) );
+
+      if (size >= 10)
+	{
+	  VERIFY( !ranges::equal(c, vref) );
+	  VERIFY( !ranges::equal(r, vref) );
+	  VERIFY( !ranges::equal(c, r) );
+	}
+
+      VERIFY( ranges::is_permutation(c, vref) );
+      VERIFY( ranges::is_permutation(r, vref) );
+    }
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc
new file mode 100644
index 00000000000..d8227773ccb
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc
@@ -0,0 +1,81 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  for (unsigned size = 0; size < 50; ++size)
+    {
+      std::vector<int> vref(size);
+      std::iota(vref.begin(), vref.end(), 0);
+      std::vector<int> v1(vref), v2(vref);
+      test_container<int, random_access_iterator_wrapper> c
+	= {&v1[0], &v1[0] + size};
+      test_range<int, random_access_iterator_wrapper> r
+	= {&v2[0], &v2[0] + size};
+
+      std::ranlux48_base g1(size), g2(size + 1);
+      ranges::shuffle(c, g1);
+      ranges::shuffle(ranges::begin(r), ranges::end(r), g2);
+
+      VERIFY( ranges::sort(c) == c.end() );
+      VERIFY( ranges::sort(r) == ranges::end(r) );
+
+      VERIFY( ranges::equal(c, vref) );
+      VERIFY( ranges::equal(r, vref) );
+    }
+}
+
+struct X
+{
+  int i;
+  constexpr X(int a) : i(a) { }
+};
+
+constexpr bool
+test02()
+{
+  X x[] = {3,4,2,1,5};
+  const X y[] = {4,3,2,1,5};
+
+  auto res = ranges::sort(x, x+4, ranges::greater{}, &X::i);
+  return (res == x+4
+	  && ranges::equal(x, y, {}, &X::i, &X::i));
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc
new file mode 100644
index 00000000000..761e3dd32d4
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc
@@ -0,0 +1,76 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+    {
+      int x[] = {1,2,3,4,5,6,7,8,9,10};
+      test_container<int, bidirectional_iterator_wrapper> cx(x);
+      auto pred = [] (int a) { return a%2==0; };
+      auto range = ranges::stable_partition(cx, pred);
+      VERIFY( ranges::all_of(cx.begin(), range.begin(), pred) );
+      VERIFY( ranges::none_of(range, pred) );
+    }
+
+    {
+      int x[] = {1,2,3,4,5,6,7,8,9,10,11};
+      test_range<int, bidirectional_iterator_wrapper> cx(x);
+      auto pred = [] (int a) { return a%2==0; };
+      auto range = ranges::stable_partition(cx, pred);
+      VERIFY( ranges::all_of(cx.begin(), range.begin(), pred) );
+      VERIFY( ranges::none_of(range, pred) );
+    }
+}
+
+void
+test02()
+{
+  for (int k = 1; k <= 10; k++)
+    {
+      int x[] = {1,2,3,4,5,6,7,8,9,10};
+      auto pred = [&] (int a) { return a >= k; };
+      auto proj = [] (int a) { return a-1; };
+      auto range = ranges::stable_partition(x, x+10, pred, proj);
+      VERIFY( ranges::all_of(x, range.begin(), pred, proj) );
+      VERIFY( ranges::none_of(range, pred, proj) );
+
+      int y[] = {0,1,2,3,4,5,6,7,8,9};
+      ranges::rotate(y, y+k);
+      VERIFY( ranges::equal(x, y, {}, proj) );
+    }
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc
new file mode 100644
index 00000000000..23a8c03b519
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc
@@ -0,0 +1,70 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-cstdint "" }
+
+#include <algorithm>
+#include <random>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+// This test doesn't verify the stability property of ranges::stable_sort,
+// because at the moment it's just defined to be a wrapper over
+// std::stable_sort.
+
+void
+test01()
+{
+  for (unsigned size = 0; size < 50; ++size)
+    {
+      std::vector<int> vref(size);
+      std::iota(vref.begin(), vref.end(), 0);
+      std::vector<int> v1(vref), v2(vref);
+      test_container<int, random_access_iterator_wrapper> c
+	= {&v1[0], &v1[0] + size};
+      test_range<int, random_access_iterator_wrapper> r
+	= {&v2[0], &v2[0] + size};
+
+      std::ranlux48_base g1(size), g2(size + 1);
+      ranges::shuffle(c, g1);
+      ranges::shuffle(ranges::begin(r), ranges::end(r), g2);
+
+      auto res1 = ranges::stable_sort(c.begin(), c.end(), {}, std::negate<>{});
+      VERIFY( res1 == c.end() );
+
+      auto res2 = ranges::stable_sort(r, ranges::greater{});
+      VERIFY( res2 == ranges::end(r) );
+
+      VERIFY( ranges::equal(c, r) );
+      VERIFY( ranges::equal(c.begin(), c.end(), vref.rbegin(), vref.rend()) );
+    }
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/swap_ranges/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/swap_ranges/constrained.cc
new file mode 100644
index 00000000000..338b6a435f9
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/swap_ranges/constrained.cc
@@ -0,0 +1,124 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+  int moved = 0;
+
+  constexpr X(int a) : i(a) { }
+
+  constexpr X(const X&) = delete;
+  constexpr X& operator=(const X&) = delete;
+
+  constexpr X(X&& other)
+  {
+    *this = std::move(other);
+  }
+
+  constexpr X&
+  operator=(X&& other)
+  {
+    other.moved++;
+    i = other.i;
+    return *this;
+  }
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  { return a.i == b.i; }
+};
+
+void
+test01()
+{
+    {
+      X x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      X y[7] = { 2, 4, 3, 5, 8, 9, 1 };
+      X z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      X w[7] = { 2, 4, 3, 5, 8, 9, 1 };
+      auto [x_iter, y_iter] = ranges::swap_ranges(x, y);
+      VERIFY( ranges::equal(y, z) && x_iter == x+7 && y_iter == y+7 );
+      VERIFY( ranges::equal(x, w) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      int y[4] = { 2, 4, 6, 0 };
+      int z[3] = { 1, 2, 3 };
+      int w[3] = { 2, 4, 6 };
+      test_container<int, forward_iterator_wrapper> cx(x);
+      test_container<int, forward_iterator_wrapper> cy(y);
+      auto [x_iter, y_iter] = ranges::swap_ranges(cx, cy);
+      VERIFY( ranges::equal(y, y+3, z, z+3) );
+      VERIFY( x_iter.ptr == x+3 && y_iter.ptr == y+3 );
+      VERIFY( y[3] == 0 );
+      VERIFY( ranges::equal(x, w) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      int y[4] = { 2, 4, 6, 0 };
+      int z[3] = { 1, 2, 3 };
+      int w[3] = { 2, 4, 6 };
+      test_range<int, input_iterator_wrapper> cx(x);
+      test_range<int, input_iterator_wrapper> cy(y);
+      auto [y_iter, x_iter] = ranges::swap_ranges(cy, cx);
+      VERIFY( ranges::equal(y, y+3, z, z+3) );
+      VERIFY( x_iter.ptr == x+3 && y_iter.ptr == y+3 );
+      VERIFY( y[3] == 0 );
+      VERIFY( ranges::equal(x, w) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  X x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  X y[7] = { 2, 4, 3, 5, 8, 9, 1 };
+  X z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  X w[7] = { 2, 4, 3, 5, 8, 9, 1 };
+  auto [x_iter, y_iter] = ranges::swap_ranges(x, y);
+  ok &= ranges::equal(y, z) && x_iter == x+7 && y_iter == y+7;
+  ok &= ranges::equal(x, w);
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
+
+
diff --git a/libstdc++-v3/testsuite/25_algorithms/transform/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/transform/constrained.cc
new file mode 100644
index 00000000000..da8b7f2be07
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/transform/constrained.cc
@@ -0,0 +1,148 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X { int i; };
+
+void
+test01()
+{
+    {
+      int x[6] = { {2}, {4}, {6}, {8}, {10}, {11} };
+      int y[6] = { {3}, {5}, {7}, {9}, {11}, {12} };
+      auto [in, out] = ranges::transform(x, x, [] (int a) { return a+1; });
+      VERIFY( in == x+6 && out == x+6 );
+      VERIFY( ranges::equal(x, y) );
+    }
+
+    {
+      X x[6] = { {2}, {4}, {6}, {8}, {10}, {11} };
+      int y[7] = { {3}, {5}, {7}, {9}, {11}, {12}, {0} };
+      int z[7] = { {0}, {0}, {0}, {0}, {0}, {0}, {0} };
+      test_container<X, forward_iterator_wrapper> cx(x);
+      test_container<int, forward_iterator_wrapper> cy(y), cz(z);
+      auto [in, out]
+	= ranges::transform(cx, cz.begin(), [] (int a) { return a+1; }, &X::i);
+      VERIFY( ranges::equal(cy, cz) );
+      VERIFY( in == cx.end() && ++out == cz.end() );
+    }
+
+    {
+      X x[6] = { {2}, {4}, {6}, {8}, {10}, {11} };
+      X y[7] = { {3}, {5}, {7}, {9}, {11}, {12}, {0} };
+      int z[7] = { {0}, {0}, {0}, {0}, {0}, {0}, {0} };
+      test_range<X, input_iterator_wrapper> rx(x), ry(y);
+      test_range<int, output_iterator_wrapper> rz(z);
+      auto [in, out]
+	= ranges::transform(rx, rz.begin(), [] (int a) { return a+1; }, &X::i);
+      VERIFY( ranges::equal(ry, z, {}, &X::i) );
+      VERIFY( in == rx.end() && out.ptr == z+6 );
+    }
+}
+
+struct Y { int i; int j; };
+
+constexpr bool
+test02()
+{
+  int x[] = { 1, 2, 3 };
+  Y y[] = { {1,2}, {2,4}, {3,6} };
+  ranges::transform(y, y+3, x, [] (int a) { return -a; }, &Y::i);
+  return x[0] == -1 && x[1] == -2 && x[2] == -3;
+}
+
+void
+test03()
+{
+    {
+      int x[6] = { {2}, {4}, {6}, {8}, {10}, {11} };
+      const int y[6] = { {3}, {5}, {7}, {9}, {11}, {12} };
+      int z[6] = { {5}, {9}, {13}, {17}, {21}, {23} };
+      auto [in1, in2, out] = ranges::transform(x, y, x, std::plus<>{});
+      VERIFY( in1 == x+6 && in2 == y+6 && out == x+6 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      int x[6] = { {2}, {4}, {6}, {8}, {10}, {11} };
+      const int y[6] = { {3}, {5}, {7}, {9}, {11}, {12} };
+      int z[6] = { {5}, {9}, {13}, {17}, {21}, {23} };
+      auto [in1, in2, out] = ranges::transform(y, x, x, std::plus<>{});
+      VERIFY( in1 == y+6 && in2 == x+6 && out == x+6 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      X x[6] = { {2}, {4}, {6}, {8}, {10}, {11} };
+      int y[7] = { {3}, {5}, {7}, {9}, {11}, {12}, {0} };
+      int z[6] = { {5}, {9}, {13}, {17}, {21}, {23} };
+      int w[6];
+      test_container<X, forward_iterator_wrapper> cx(x);
+      test_container<int, forward_iterator_wrapper> cy(y), cz(z), cw(w);
+      auto [in1, in2, out]
+	= ranges::transform(cx, cy, cw.begin(), std::plus<>{}, &X::i);
+      VERIFY( in1 == cx.end() && ++in2 == cy.end() && out == cw.end() );
+      VERIFY( ranges::equal(cw, cz) );
+    }
+
+    {
+      X x[6] = { {2}, {4}, {6}, {8}, {10}, {11} };
+      int y[7] = { {3}, {5}, {7}, {9}, {11}, {12}, {0} };
+      int z[6] = { {5}, {9}, {13}, {17}, {21}, {23} };
+      int w[6];
+      test_range<X, input_iterator_wrapper> rx(x);
+      test_range<int, input_iterator_wrapper> ry(y), rz(z);
+      test_range<int, output_iterator_wrapper> rw(w);
+      auto [in1, in2, out]
+	= ranges::transform(rx, ry, rw.begin(), std::plus<>{}, &X::i);
+      VERIFY( in1 == rx.end() && ++in2 == ry.end() && out.ptr == w+6 );
+      VERIFY( ranges::equal(w, rz) );
+    }
+}
+
+constexpr bool
+test04()
+{
+  int x[3];
+  const Y y[3] = { {1,2}, {2,4}, {3,6} };
+  ranges::transform(y, y+3, y, y+3, x, std::plus<>{}, &Y::i, &Y::j);
+  return x[0] == 3 && x[1] == 6 && x[2] == 9;
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+  test03();
+  static_assert(test04());
+}
+
diff --git a/libstdc++-v3/testsuite/25_algorithms/unique/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/unique/constrained.cc
new file mode 100644
index 00000000000..e863a601ddd
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/unique/constrained.cc
@@ -0,0 +1,143 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <list>
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+};
+
+void
+test01()
+{
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {2}, {11} };
+      const int y[5] = { {2}, {6}, {8}, {2}, {11} };
+      test_container<X, forward_iterator_wrapper> cx(x);
+      auto res = ranges::unique(cx, {}, &X::i);
+      VERIFY( res.end() == cx.end() );
+      VERIFY( ranges::equal(cx.begin(), res.begin(), y, y+5, {}, &X::i) );
+    }
+
+    {
+      X x[6] = { {2}, {2}, {6}, {8}, {2}, {11} };
+      const int y[5] = { {2}, {6}, {8}, {2}, {11} };
+      test_range<X, forward_iterator_wrapper> rx(x);
+      auto res = ranges::unique(rx, {}, &X::i);
+      VERIFY( res.end() == rx.end() );
+      VERIFY( ranges::equal(rx.begin(), res.begin(), y, y+5, {}, &X::i) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  int x[2] = {2, 2};
+  const int y[1] = {2};
+  auto res = ranges::unique(x);
+  return ranges::equal(x, res.begin(), y, y+1, ranges::equal_to{});
+}
+
+/* The following is adapted from 25_algorithms/unique/2.cc.  */
+
+namespace two_dot_cc
+{
+  const int T1[] = {1, 4, 4, 6, 1, 2, 2, 3, 1, 6, 6, 6, 5, 7, 5, 4, 4};
+  const int T2[] = {1, 1, 1, 2, 2, 1, 1, 7, 6, 6, 7, 8, 8, 8, 8, 9, 9};
+  const int N = sizeof(T1) / sizeof(int);
+
+  const int A1[] = {1, 4, 6, 1, 2, 3, 1, 6, 5, 7, 5, 4};
+  const int A2[] = {1, 4, 4, 6, 6, 6, 6, 7};
+  const int A3[] = {1, 1, 1};
+
+  const int B1[] = {1, 2, 1, 7, 6, 7, 8, 9};
+  const int B2[] = {1, 1, 1, 2, 2, 7, 7, 8, 8, 8, 8, 9, 9};
+  const int B3[] = {9, 9, 8, 8, 8, 8, 7, 6, 6, 1, 1, 1, 1, 1};
+
+  void test01()
+  {
+    using namespace std;
+
+    list<int>::iterator pos;
+
+    list<int> coll(T1, T1 + N);
+    pos = ranges::unique(coll.begin(), coll.end()).begin();
+    VERIFY( equal(coll.begin(), pos, A1) );
+
+    list<int> coll2(T2, T2 + N);
+    pos = ranges::unique(coll2.begin(), coll2.end()).begin();
+    VERIFY( equal(coll2.begin(), pos, B1) );
+  }
+
+  void test02()
+  {
+    using namespace std;
+
+    list<int>::iterator pos;
+
+    list<int> coll(T1, T1 + N);
+    pos = ranges::unique(coll.begin(), coll.end(), greater<int>()).begin();
+    VERIFY( equal(coll.begin(), pos, A2) );
+
+    list<int> coll2(T2, T2 + N);
+    pos = ranges::unique(coll2.begin(), coll2.end(), greater<int>()).begin();
+    VERIFY( equal(coll2.begin(), pos, B2) );
+  }
+
+  void test03()
+  {
+    using namespace std;
+
+    list<int>::iterator pos;
+
+    list<int> coll(T1, T1 + N);
+    pos = ranges::unique(coll.begin(), coll.end(), less<int>()).begin();
+    VERIFY( equal(coll.begin(), pos, A3) );
+
+    list<int> coll2(T2, T2 + N);
+    reverse(coll2.begin(), coll2.end());
+    pos = ranges::unique(coll2.begin(), coll2.end(), less<int>()).begin();
+    VERIFY( equal(coll2.begin(), pos, B3) );
+  }
+} // namespace two_dot_cc
+
+int main()
+{
+  test01();
+  static_assert(test02());
+
+  two_dot_cc::test01();
+  two_dot_cc::test02();
+  two_dot_cc::test03();
+
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/unique_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/unique_copy/constrained.cc
new file mode 100644
index 00000000000..bf16cdc4ee9
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/unique_copy/constrained.cc
@@ -0,0 +1,113 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
+using __gnu_test::output_iterator_wrapper;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+template<template<typename> typename source_wrapper,
+	 template<typename> typename dest_wrapper>
+void
+test01()
+{
+  int x[6] = {0, 0, 0, 1, 1, 1};
+  int y[2];
+  const int z[2] = {0, 1};
+
+  test_range<int, source_wrapper> rx(x);
+  test_range<int, dest_wrapper> ry(y);
+  auto [in,out] = ranges::unique_copy(rx, ry.begin());
+  VERIFY( in == ranges::end(rx) && out == ranges::end(ry) );
+  VERIFY( ranges::equal(y, z) );
+}
+
+template<template<typename> typename source_wrapper,
+	 template<typename> typename dest_wrapper>
+void
+test02()
+{
+  int x[6] = {0, 0, 0, 1, 1, 1};
+  int y[2] = {0, 0};
+  const int z[2] = {0, 0};
+
+  test_range<int, source_wrapper> rx(x, x);
+  test_range<int, dest_wrapper> ry(y, y);
+  auto [in, out] = ranges::unique_copy(rx.begin(), rx.end(), ry.begin());
+  VERIFY( in.ptr == x && out.ptr == y );
+  VERIFY( ranges::equal(y, z) );
+}
+
+template<template<typename> typename source_wrapper,
+	 template<typename> typename dest_wrapper>
+void
+test03()
+{
+  struct X { int i; };
+  X x[6] = { {1}, {2}, {2}, {4}, {4}, {6} };
+  X y[4] = { {1}, {2}, {4}, {6} };
+  const X z[4] = { {1}, {2}, {4}, {6} };
+
+  test_range<X, source_wrapper> rx(x);
+  test_range<X, dest_wrapper> ry(y);
+  auto [in, out]
+    = ranges::unique_copy(rx, ry.begin(), ranges::equal_to{}, &X::i);
+  VERIFY( in == ranges::end(rx) && out == ranges::end(ry) );
+  VERIFY( ranges::equal(y, z, {}, &X::i, &X::i) );
+}
+
+constexpr bool
+test04()
+{
+  struct X { int i; };
+  X x[7] = { {1}, {2}, {2}, {2}, {4}, {4}, {6} };
+  X y[4] = { {1}, {2}, {4}, {6} };
+  const X z[4] = { {1}, {2}, {4}, {6} };
+
+  auto [in, out]
+    = ranges::unique_copy(x, x+7, y, ranges::equal_to{}, &X::i);
+  return (in == ranges::end(x)
+	  && out == ranges::end(y)
+	  && ranges::equal(y, z, {}, &X::i, &X::i));
+}
+
+int
+main()
+{
+  test01<input_iterator_wrapper, output_iterator_wrapper>();
+  test01<input_iterator_wrapper, forward_iterator_wrapper>();
+  test01<forward_iterator_wrapper, output_iterator_wrapper>();
+
+  test02<input_iterator_wrapper, output_iterator_wrapper>();
+  test02<input_iterator_wrapper, forward_iterator_wrapper>();
+  test02<forward_iterator_wrapper, output_iterator_wrapper>();
+
+  test03<input_iterator_wrapper, output_iterator_wrapper>();
+  test03<input_iterator_wrapper, forward_iterator_wrapper>();
+  test03<forward_iterator_wrapper, output_iterator_wrapper>();
+
+  static_assert(test04());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/upper_bound/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/upper_bound/constrained.cc
new file mode 100644
index 00000000000..5182431f68f
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/upper_bound/constrained.cc
@@ -0,0 +1,66 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1, 2, 3, 4, 5, 5, 6, 7};
+  for (unsigned i = 0; i < 5; i++)
+    for (unsigned j = 6; j < 8; j++)
+      {
+	test_container<int, forward_iterator_wrapper> cx(x);
+	auto result = ranges::upper_bound(std::next(cx.begin(), i),
+					  std::next(cx.begin(), j),
+					  4, {}, [] (int a) { return a-1; });
+	VERIFY( result.ptr == x+6 );
+      }
+
+  ranges::reverse(x);
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto result = ranges::upper_bound(rx, 5, ranges::greater{},
+				    [] (int a) { return a+1; });
+  VERIFY( result.ptr == x+5 );
+}
+
+constexpr bool
+test02()
+{
+  int x[] = {1, 2, 3, 4, 5};
+  return (ranges::upper_bound(x, 6) == x+5
+	  && ranges::upper_bound(x, x, 6) == x
+	  && ranges::upper_bound(x, 1) == x+1);
+}
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
-- 
2.25.0.114.g5b0ca878e0

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4
  2020-02-04  2:07 [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Patrick Palka
  2020-02-04  2:08 ` [PATCH 3/3] libstdc++: Implement C++20 range adaptors Patrick Palka
  2020-02-04  2:09 ` [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms Patrick Palka
@ 2020-02-04 10:41 ` Jonathan Wakely
  2020-02-04 21:23   ` Patrick Palka
  2 siblings, 1 reply; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-04 10:41 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On 03/02/20 21:07 -0500, Patrick Palka wrote:
>These changes are needed for some of the tests in the constrained algorithm
>patch, because they use move_iterator with an uncopyable output_iterator.  The
>other changes described in the paper are already applied, it seems.
>
>libstdc++-v3/ChangeLog:
>
>	* include/bits/stl_iterator.h (move_iterator::move_iterator): Move the
>	iterator when initializing _M_current.
>	(move_iterator::base): Split into two overloads differing in
>	ref-qualifiers as in P1207R4.
>---
> libstdc++-v3/include/bits/stl_iterator.h | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
>diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
>index 784d200d22f..1a288a5c785 100644
>--- a/libstdc++-v3/include/bits/stl_iterator.h
>+++ b/libstdc++-v3/include/bits/stl_iterator.h
>@@ -1166,7 +1166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       explicit _GLIBCXX17_CONSTEXPR
>       move_iterator(iterator_type __i)
>-      : _M_current(__i) { }
>+      : _M_current(std::move(__i)) { }
>
>       template<typename _Iter>
> 	_GLIBCXX17_CONSTEXPR
>@@ -1174,9 +1174,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	: _M_current(__i.base()) { }
>
>       _GLIBCXX17_CONSTEXPR iterator_type
>-      base() const
>+      base() const &
>+#if __cplusplus > 201703L && __cpp_lib_concepts
>+	requires copy_constructible<iterator_type>
>+#endif
>       { return _M_current; }
>
>+      _GLIBCXX17_CONSTEXPR iterator_type
>+      base() &&
>+      { return std::move(_M_current); }
>+

I think this change has to be restricted to C++20 and later, otherwise
a move_iterator in C++17 can change state so that its _M_current
becomes moved-from, which should not be possible before C++20.

So something like:

#if __cplusplus <= 201703L
       _GLIBCXX17_CONSTEXPR iterator_type
       base() const
       { return _M_current; }
#else
       constexpr iterator_type
       base() const &
#if __cpp_lib_concepts
	requires copy_constructible<iterator_type>
#endif
       { return _M_current; }

       constexpr iterator_type
       base() &&
       { return std::move(_M_current); }
#endif

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4
  2020-02-04 10:41 ` [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Jonathan Wakely
@ 2020-02-04 21:23   ` Patrick Palka
  2020-02-04 21:49     ` Jonathan Wakely
  0 siblings, 1 reply; 27+ messages in thread
From: Patrick Palka @ 2020-02-04 21:23 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

On Tue, 4 Feb 2020, Jonathan Wakely wrote:

> On 03/02/20 21:07 -0500, Patrick Palka wrote:
> > These changes are needed for some of the tests in the constrained algorithm
> > patch, because they use move_iterator with an uncopyable output_iterator.
> > The
> > other changes described in the paper are already applied, it seems.
> > 
> > libstdc++-v3/ChangeLog:
> > 
> > 	* include/bits/stl_iterator.h (move_iterator::move_iterator): Move the
> > 	iterator when initializing _M_current.
> > 	(move_iterator::base): Split into two overloads differing in
> > 	ref-qualifiers as in P1207R4.
> > ---
> > libstdc++-v3/include/bits/stl_iterator.h | 11 +++++++++--
> > 1 file changed, 9 insertions(+), 2 deletions(-)
> > 
> > diff --git a/libstdc++-v3/include/bits/stl_iterator.h
> > b/libstdc++-v3/include/bits/stl_iterator.h
> > index 784d200d22f..1a288a5c785 100644
> > --- a/libstdc++-v3/include/bits/stl_iterator.h
> > +++ b/libstdc++-v3/include/bits/stl_iterator.h
> > @@ -1166,7 +1166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > 
> >       explicit _GLIBCXX17_CONSTEXPR
> >       move_iterator(iterator_type __i)
> > -      : _M_current(__i) { }
> > +      : _M_current(std::move(__i)) { }
> > 
> >       template<typename _Iter>
> > 	_GLIBCXX17_CONSTEXPR
> > @@ -1174,9 +1174,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > 	: _M_current(__i.base()) { }
> > 
> >       _GLIBCXX17_CONSTEXPR iterator_type
> > -      base() const
> > +      base() const &
> > +#if __cplusplus > 201703L && __cpp_lib_concepts
> > +	requires copy_constructible<iterator_type>
> > +#endif
> >       { return _M_current; }
> > 
> > +      _GLIBCXX17_CONSTEXPR iterator_type
> > +      base() &&
> > +      { return std::move(_M_current); }
> > +
> 
> I think this change has to be restricted to C++20 and later, otherwise
> a move_iterator in C++17 can change state so that its _M_current
> becomes moved-from, which should not be possible before C++20.
> 
> So something like:
> 
> #if __cplusplus <= 201703L
>       _GLIBCXX17_CONSTEXPR iterator_type
>       base() const
>       { return _M_current; }
> #else
>       constexpr iterator_type
>       base() const &
> #if __cpp_lib_concepts
> 	requires copy_constructible<iterator_type>
> #endif
>       { return _M_current; }
> 
>       constexpr iterator_type
>       base() &&
>       { return std::move(_M_current); }
> #endif
> 
> 

Thanks for the review, here is the updated patch.

libstdc++-v3/ChangeLog:

	* include/bits/stl_iterator.h (move_iterator::move_iterator): Move __i
	when initializing _M_current.
	(move_iterator::base): Split into two overloads differing in
	ref-qualifiers as in P1207R4 for C++20.
---
 libstdc++-v3/include/bits/stl_iterator.h | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 784d200d22f..c200f7a9d14 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -1166,16 +1166,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       explicit _GLIBCXX17_CONSTEXPR
       move_iterator(iterator_type __i)
-      : _M_current(__i) { }
+      : _M_current(std::move(__i)) { }
 
       template<typename _Iter>
 	_GLIBCXX17_CONSTEXPR
 	move_iterator(const move_iterator<_Iter>& __i)
 	: _M_current(__i.base()) { }
 
+#if __cplusplus <= 201703L
       _GLIBCXX17_CONSTEXPR iterator_type
       base() const
       { return _M_current; }
+#else
+      constexpr iterator_type
+      base() const &
+#if __cpp_lib_concepts
+	requires copy_constructible<iterator_type>
+#endif
+      { return _M_current; }
+
+      constexpr iterator_type
+      base() &&
+      { return std::move(_M_current); }
+#endif
 
       _GLIBCXX17_CONSTEXPR reference
       operator*() const
-- 
2.25.0.114.g5b0ca878e0

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4
  2020-02-04 21:23   ` Patrick Palka
@ 2020-02-04 21:49     ` Jonathan Wakely
  0 siblings, 0 replies; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-04 21:49 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On 04/02/20 16:23 -0500, Patrick Palka wrote:
>On Tue, 4 Feb 2020, Jonathan Wakely wrote:
>
>> On 03/02/20 21:07 -0500, Patrick Palka wrote:
>> > These changes are needed for some of the tests in the constrained algorithm
>> > patch, because they use move_iterator with an uncopyable output_iterator.
>> > The
>> > other changes described in the paper are already applied, it seems.
>> >
>> > libstdc++-v3/ChangeLog:
>> >
>> > 	* include/bits/stl_iterator.h (move_iterator::move_iterator): Move the
>> > 	iterator when initializing _M_current.
>> > 	(move_iterator::base): Split into two overloads differing in
>> > 	ref-qualifiers as in P1207R4.
>> > ---
>> > libstdc++-v3/include/bits/stl_iterator.h | 11 +++++++++--
>> > 1 file changed, 9 insertions(+), 2 deletions(-)
>> >
>> > diff --git a/libstdc++-v3/include/bits/stl_iterator.h
>> > b/libstdc++-v3/include/bits/stl_iterator.h
>> > index 784d200d22f..1a288a5c785 100644
>> > --- a/libstdc++-v3/include/bits/stl_iterator.h
>> > +++ b/libstdc++-v3/include/bits/stl_iterator.h
>> > @@ -1166,7 +1166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >
>> >       explicit _GLIBCXX17_CONSTEXPR
>> >       move_iterator(iterator_type __i)
>> > -      : _M_current(__i) { }
>> > +      : _M_current(std::move(__i)) { }
>> >
>> >       template<typename _Iter>
>> > 	_GLIBCXX17_CONSTEXPR
>> > @@ -1174,9 +1174,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > 	: _M_current(__i.base()) { }
>> >
>> >       _GLIBCXX17_CONSTEXPR iterator_type
>> > -      base() const
>> > +      base() const &
>> > +#if __cplusplus > 201703L && __cpp_lib_concepts
>> > +	requires copy_constructible<iterator_type>
>> > +#endif
>> >       { return _M_current; }
>> >
>> > +      _GLIBCXX17_CONSTEXPR iterator_type
>> > +      base() &&
>> > +      { return std::move(_M_current); }
>> > +
>>
>> I think this change has to be restricted to C++20 and later, otherwise
>> a move_iterator in C++17 can change state so that its _M_current
>> becomes moved-from, which should not be possible before C++20.
>>
>> So something like:
>>
>> #if __cplusplus <= 201703L
>>       _GLIBCXX17_CONSTEXPR iterator_type
>>       base() const
>>       { return _M_current; }
>> #else
>>       constexpr iterator_type
>>       base() const &
>> #if __cpp_lib_concepts
>> 	requires copy_constructible<iterator_type>
>> #endif
>>       { return _M_current; }
>>
>>       constexpr iterator_type
>>       base() &&
>>       { return std::move(_M_current); }
>> #endif
>>
>>
>
>Thanks for the review, here is the updated patch.

Thanks, this is OK for master (it's safe enough to change now, and it's
needed for some C++20-only changes that will also be safe to do now as
they don't touch exiting code).


>libstdc++-v3/ChangeLog:
>
>	* include/bits/stl_iterator.h (move_iterator::move_iterator): Move __i
>	when initializing _M_current.
>	(move_iterator::base): Split into two overloads differing in
>	ref-qualifiers as in P1207R4 for C++20.
>---
> libstdc++-v3/include/bits/stl_iterator.h | 15 ++++++++++++++-
> 1 file changed, 14 insertions(+), 1 deletion(-)
>
>diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
>index 784d200d22f..c200f7a9d14 100644
>--- a/libstdc++-v3/include/bits/stl_iterator.h
>+++ b/libstdc++-v3/include/bits/stl_iterator.h
>@@ -1166,16 +1166,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       explicit _GLIBCXX17_CONSTEXPR
>       move_iterator(iterator_type __i)
>-      : _M_current(__i) { }
>+      : _M_current(std::move(__i)) { }
>
>       template<typename _Iter>
> 	_GLIBCXX17_CONSTEXPR
> 	move_iterator(const move_iterator<_Iter>& __i)
> 	: _M_current(__i.base()) { }
>
>+#if __cplusplus <= 201703L
>       _GLIBCXX17_CONSTEXPR iterator_type
>       base() const
>       { return _M_current; }
>+#else
>+      constexpr iterator_type
>+      base() const &
>+#if __cpp_lib_concepts
>+	requires copy_constructible<iterator_type>
>+#endif
>+      { return _M_current; }
>+
>+      constexpr iterator_type
>+      base() &&
>+      { return std::move(_M_current); }
>+#endif
>
>       _GLIBCXX17_CONSTEXPR reference
>       operator*() const
>-- 
>2.25.0.114.g5b0ca878e0
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-04  2:09 ` [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms Patrick Palka
@ 2020-02-05 18:39   ` François Dumont
  2020-02-05 19:25     ` Patrick Palka
  2020-02-06 12:36     ` Jonathan Wakely
  2020-02-06 16:25   ` Jonathan Wakely
                     ` (2 subsequent siblings)
  3 siblings, 2 replies; 27+ messages in thread
From: François Dumont @ 2020-02-05 18:39 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches; +Cc: libstdc++, jwakely

Hi

     Is it me or the patch isn't an attachment ? It is far more 
convenient to provide something easy to extract and apply locally.

On 2/4/20 3:07 AM, Patrick Palka wrote:
> This patch implements the C++20 ranges overloads for the algorithms in
> [algorithms].  Most of the algorithms were reimplemented, with each of their
> implementations very closely following the existing implementation in
> bits/stl_algo.h and bits/stl_algobase.h.  The reason for reimplementing most of
> the algorithms instead of forwarding to their STL-style overload is because
> forwarding cannot be conformantly and efficiently performed for algorithms that
> operate on non-random-access iterators.  But algorithms that operate on random
> access iterators can safely and efficiently be forwarded to the STL-style
> implementation, and this patch does so for push_heap, pop_heap, make_heap,
> sort_heap, sort, stable_sort, nth_element, inplace_merge and stable_partition.
>
> What's missing from this patch is debug-iterator

Always the 5th wheel of the car like we say in French :-)

I'll be looking at this point once I manage to apply the patch.

>   and container specializations
> that are present for some of the STL-style algorithms that need to be ported
> over to the ranges algos.  I marked them missing at TODO comments.  There are
> also some other minor outstanding TODOs.
>
> The code that could use the most thorough review is ranges::__copy_or_move,
> ranges::__copy_or_move_backward, ranges::__equal and
> ranges::__lexicographical_compare.  In the tests, I tried to test the interface
> of each new overload, as well as the correctness of the new implementation.
>
> diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> new file mode 100644
> index 00000000000..2e177ce7f7a
> --- /dev/null
> +++ b/libstdc++-v3/include/bits/ranges_algo.h
> @@ -0,0 +1,3640 @@
> +// Core algorithmic facilities -*- C++ -*-
> +
> +// Copyright (C) 2019-2020 Free Software Foundation, Inc.

Copyright for new files is wrong, should be only 2020. I know it is 
painful to maintain that when you work on patch on several years.

>
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +/** @file bits/ranges_algo.h
> + *  This is an internal header file, included by other library headers.
> + *  Do not attempt to use it directly. @headername{algorithm}
> + */
> +
> +#ifndef _RANGES_ALGO_H
> +#define _RANGES_ALGO_H 1
> +
> +#if __cplusplus > 201703L
> +
> +#include <compare>
> +#include <cmath>
> +#include <iterator>
> +// #include <bits/range_concepts.h>
> +#include <ranges>
> +#include <bits/invoke.h>
> +#include <bits/cpp_type_traits.h> // __is_byte
> +#include <bits/random.h> // concept uniform_random_bit_generator
> +
> +#if __cpp_lib_concepts
> +namespace std _GLIBCXX_VISIBILITY(default)
> +{
> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> +namespace ranges
> +{
> +  namespace __detail
> +  {
> +    template<typename _Tp>
> +    constexpr inline bool __is_normal_iterator = false;
> +
> +    template<typename _Iterator, typename _Container>
> +    constexpr inline bool
> +      __is_normal_iterator<__gnu_cxx::__normal_iterator<_Iterator, _Container>>
> +      = true;
> +
> +    template<typename _Tp>
> +    constexpr inline bool __is_reverse_iterator = false;
> +
> +    template<typename _Iterator>
> +    constexpr inline bool
> +      __is_reverse_iterator<reverse_iterator<_Iterator>> = true;
> +
> +    template<typename _Tp>
> +    constexpr inline bool __is_move_iterator = false;
> +
> +    template<typename _Iterator>
> +    constexpr inline bool
> +      __is_move_iterator<move_iterator<_Iterator>> = true;

Did you consider the __is_move_iterator in stl_iterator.h ?

At least this version will also detect a move_iterator in different 
situation. I haven't check yet what you are doing with that but it might 
be an easy way to get better debug iterators integration for instance.

François

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-05 18:39   ` François Dumont
@ 2020-02-05 19:25     ` Patrick Palka
  2020-02-06 10:39       ` Jonathan Wakely
  2020-02-06 12:36     ` Jonathan Wakely
  1 sibling, 1 reply; 27+ messages in thread
From: Patrick Palka @ 2020-02-05 19:25 UTC (permalink / raw)
  To: François Dumont; +Cc: Patrick Palka, gcc-patches, libstdc++, jwakely

[-- Attachment #1: Type: text/plain, Size: 6513 bytes --]

[resending with attachment now compressed]

On Wed, 5 Feb 2020, François Dumont wrote:

> Hi
> 
>     Is it me or the patch isn't an attachment ? It is far more convenient to
> provide something easy to extract and apply locally.

Good point, I've attached the patch as an attachment and I'll make sure
to provide big patches as an attachment in the future.  I should also
have noted that the patches are also available in my user branch
libstdcxx-constrained-algo-adaptors which you can fetch with

    git fetch origin refs/users/ppalka/heads/libstdcxx-constrained-algos-adaptors

> 
> On 2/4/20 3:07 AM, Patrick Palka wrote:
> > This patch implements the C++20 ranges overloads for the algorithms in
> > [algorithms].  Most of the algorithms were reimplemented, with each of their
> > implementations very closely following the existing implementation in
> > bits/stl_algo.h and bits/stl_algobase.h.  The reason for reimplementing most
> > of
> > the algorithms instead of forwarding to their STL-style overload is because
> > forwarding cannot be conformantly and efficiently performed for algorithms
> > that
> > operate on non-random-access iterators.  But algorithms that operate on
> > random
> > access iterators can safely and efficiently be forwarded to the STL-style
> > implementation, and this patch does so for push_heap, pop_heap, make_heap,
> > sort_heap, sort, stable_sort, nth_element, inplace_merge and
> > stable_partition.
> > 
> > What's missing from this patch is debug-iterator
> 
> Always the 5th wheel of the car like we say in French :-)
> 
> I'll be looking at this point once I manage to apply the patch.

That would be much appreciated! :)

> 
> >   and container specializations
> > that are present for some of the STL-style algorithms that need to be ported
> > over to the ranges algos.  I marked them missing at TODO comments.  There
> > are
> > also some other minor outstanding TODOs.
> > 
> > The code that could use the most thorough review is ranges::__copy_or_move,
> > ranges::__copy_or_move_backward, ranges::__equal and
> > ranges::__lexicographical_compare.  In the tests, I tried to test the
> > interface
> > of each new overload, as well as the correctness of the new implementation.
> > 
> > diff --git a/libstdc++-v3/include/bits/ranges_algo.h
> > b/libstdc++-v3/include/bits/ranges_algo.h
> > new file mode 100644
> > index 00000000000..2e177ce7f7a
> > --- /dev/null
> > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > @@ -0,0 +1,3640 @@
> > +// Core algorithmic facilities -*- C++ -*-
> > +
> > +// Copyright (C) 2019-2020 Free Software Foundation, Inc.
> 
> Copyright for new files is wrong, should be only 2020. I know it is painful to
> maintain that when you work on patch on several years.

Thanks, fixed!

> 
> > 
> > +//
> > +// This file is part of the GNU ISO C++ Library.  This library is free
> > +// software; you can redistribute it and/or modify it under the
> > +// terms of the GNU General Public License as published by the
> > +// Free Software Foundation; either version 3, or (at your option)
> > +// any later version.
> > +
> > +// This library is distributed in the hope that it will be useful,
> > +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +// GNU General Public License for more details.
> > +
> > +// Under Section 7 of GPL version 3, you are granted additional
> > +// permissions described in the GCC Runtime Library Exception, version
> > +// 3.1, as published by the Free Software Foundation.
> > +
> > +// You should have received a copy of the GNU General Public License and
> > +// a copy of the GCC Runtime Library Exception along with this program;
> > +// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> > +// <http://www.gnu.org/licenses/>.
> > +
> > +/** @file bits/ranges_algo.h
> > + *  This is an internal header file, included by other library headers.
> > + *  Do not attempt to use it directly. @headername{algorithm}
> > + */
> > +
> > +#ifndef _RANGES_ALGO_H
> > +#define _RANGES_ALGO_H 1
> > +
> > +#if __cplusplus > 201703L
> > +
> > +#include <compare>
> > +#include <cmath>
> > +#include <iterator>
> > +// #include <bits/range_concepts.h>
> > +#include <ranges>
> > +#include <bits/invoke.h>
> > +#include <bits/cpp_type_traits.h> // __is_byte
> > +#include <bits/random.h> // concept uniform_random_bit_generator
> > +
> > +#if __cpp_lib_concepts
> > +namespace std _GLIBCXX_VISIBILITY(default)
> > +{
> > +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> > +namespace ranges
> > +{
> > +  namespace __detail
> > +  {
> > +    template<typename _Tp>
> > +    constexpr inline bool __is_normal_iterator = false;
> > +
> > +    template<typename _Iterator, typename _Container>
> > +    constexpr inline bool
> > +      __is_normal_iterator<__gnu_cxx::__normal_iterator<_Iterator,
> > _Container>>
> > +      = true;
> > +
> > +    template<typename _Tp>
> > +    constexpr inline bool __is_reverse_iterator = false;
> > +
> > +    template<typename _Iterator>
> > +    constexpr inline bool
> > +      __is_reverse_iterator<reverse_iterator<_Iterator>> = true;
> > +
> > +    template<typename _Tp>
> > +    constexpr inline bool __is_move_iterator = false;
> > +
> > +    template<typename _Iterator>
> > +    constexpr inline bool
> > +      __is_move_iterator<move_iterator<_Iterator>> = true;
> 
> Did you consider the __is_move_iterator in stl_iterator.h ?
> 
> At least this version will also detect a move_iterator in different situation.
> I haven't check yet what you are doing with that but it might be an easy way
> to get better debug iterators integration for instance.

The __is_move_iterator in stl_iterator.h is "deep" in that it looks
through other iterator adaptors and returns true if the iterator is in
some level wrapped by a move_iterator.  It turned out that for the
implementation of __copy_or_move, we need just a "shallow" version of
__is_move_iterator which returns true iff the outermost iterator adaptor
is a move_iterator.

Also IIRC, the way __miter_base() is currently defined assumes that the
underlying iterator is copyable which is not necessarily true anymore
for non-forward iterators.  So I would have to also fix __miter_base()
which might be risky to do at this stage.

> 
> François
> 
> 

[-- Attachment #2: Type: application/gzip, Size: 35865 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-05 19:25     ` Patrick Palka
@ 2020-02-06 10:39       ` Jonathan Wakely
  0 siblings, 0 replies; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-06 10:39 UTC (permalink / raw)
  To: Patrick Palka; +Cc: François Dumont, gcc-patches, libstdc++

On 05/02/20 14:24 -0500, Patrick Palka wrote:
>Also IIRC, the way __miter_base() is currently defined assumes that the
>underlying iterator is copyable which is not necessarily true anymore
>for non-forward iterators.  So I would have to also fix __miter_base()
>which might be risky to do at this stage.

Agreed. The current patch only affects C++20, which makes it much less
risky.

I was thinking about how range algos interact with debug mode, and I
think we might want to take the opportunity to do things a bit
differently.

Just like if-constexpr allows algos to use different implementations
without tag-dispatching, we might be able to simplify how we deal with
debug iterators.

For example, instead of spliting every algo into foo and __foo parts
and making foo do the debug checks, then unwrap the debug iterators
and call __foo, we could just unwrap them and recursively call the
same function again:

template<typename _It>
   constexpr It
   foo(It __first, __last)
   {
     if constexpr (__is_debug_iter<_It>)
       {
         // do debug checks ...
         // and the work on unwrapped iterators:
         return std::__niter_wrap(foo(std::__niter_base(__first),
                                      std::__niter_base(__last)));
       }

       // ...
   }

It's OK to use the functions that assume the iterators are copyable
here, because we know that our debug iterators are copyable.

We should also consider when we even need debug checks for the algos
taking a range. In many cases, calling foo(vec) doesn't need to check
if the iterators are valid, because we know that ranges::begin(vec)
and ranges::end(vec) will call vec.begin() and vec.end() which are
valid. That won't always be true, because somebody could create an
invalid range by trying hard enough, but I think in many cases we can
assume that a range doesn't contain invalid iterators. However, since
they just forward to the overload taking a pair of iterators, we will
get the debug checks there anyway. But I don't think the overloads
taking a range should do any debug checks explicitly.

We can add debug assertions to subrange, and to range adaptors like
take_view and drop_view to prevent the creation of invalid ranges in
the first place, so that we can assume they're valid after that.

I'll talk to the Microsoft library team about this topic when I see
them next week. I assume they've already been thinking about it and
will probably have some useful input.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-05 18:39   ` François Dumont
  2020-02-05 19:25     ` Patrick Palka
@ 2020-02-06 12:36     ` Jonathan Wakely
  1 sibling, 0 replies; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-06 12:36 UTC (permalink / raw)
  To: François Dumont; +Cc: Patrick Palka, gcc-patches, libstdc++

On 05/02/20 19:39 +0100, François Dumont wrote:
>Hi
>
>    Is it me or the patch isn't an attachment ? It is far more 
>convenient to provide something easy to extract and apply locally.
>
>On 2/4/20 3:07 AM, Patrick Palka wrote:
>>This patch implements the C++20 ranges overloads for the algorithms in
>>[algorithms].  Most of the algorithms were reimplemented, with each of their
>>implementations very closely following the existing implementation in
>>bits/stl_algo.h and bits/stl_algobase.h.  The reason for reimplementing most of
>>the algorithms instead of forwarding to their STL-style overload is because
>>forwarding cannot be conformantly and efficiently performed for algorithms that
>>operate on non-random-access iterators.  But algorithms that operate on random
>>access iterators can safely and efficiently be forwarded to the STL-style
>>implementation, and this patch does so for push_heap, pop_heap, make_heap,
>>sort_heap, sort, stable_sort, nth_element, inplace_merge and stable_partition.
>>
>>What's missing from this patch is debug-iterator
>
>Always the 5th wheel of the car like we say in French :-)
>
>I'll be looking at this point once I manage to apply the patch.
>
>>  and container specializations
>>that are present for some of the STL-style algorithms that need to be ported
>>over to the ranges algos.  I marked them missing at TODO comments.  There are
>>also some other minor outstanding TODOs.
>>
>>The code that could use the most thorough review is ranges::__copy_or_move,
>>ranges::__copy_or_move_backward, ranges::__equal and
>>ranges::__lexicographical_compare.  In the tests, I tried to test the interface
>>of each new overload, as well as the correctness of the new implementation.
>>
>>diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
>>new file mode 100644
>>index 00000000000..2e177ce7f7a
>>--- /dev/null
>>+++ b/libstdc++-v3/include/bits/ranges_algo.h
>>@@ -0,0 +1,3640 @@
>>+// Core algorithmic facilities -*- C++ -*-
>>+
>>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
>
>Copyright for new files is wrong, should be only 2020. I know it is 
>painful to maintain that when you work on patch on several years.

I assume Patrick kept the 2019 date because his patch started from a
file I sent him with a few of the algos, and that was dated 2019.

I can't remember what the actual rule is for new files that contain
old code. The copyright on some of the new file *is* from 2019, even
if it wasn't added to the GCC repo yet.

New files containing new code should definitely only have the new date
(usually when I point out wrong dates in patch review it's because
somebody's just copied the comment header from an old testcase and so
the old dates are wrong).

I only wrote that code in December 2019 though, so it doesn't make a
lot of difference either way, 2020 is fine.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-04  2:09 ` [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms Patrick Palka
  2020-02-05 18:39   ` François Dumont
@ 2020-02-06 16:25   ` Jonathan Wakely
  2020-02-07  1:13     ` Patrick Palka
  2020-02-07 13:51   ` Jonathan Wakely
  2020-02-13 18:07   ` François Dumont
  3 siblings, 1 reply; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-06 16:25 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On 03/02/20 21:07 -0500, Patrick Palka wrote:
>+#ifndef _RANGES_ALGO_H
>+#define _RANGES_ALGO_H 1
>+
>+#if __cplusplus > 201703L
>+
>+#include <compare>
>+#include <cmath>
>+#include <iterator>
>+// #include <bits/range_concepts.h>

This line could be removed, or leave it as a reminder to me to
refactor <ranges> so that the small utility pieces are in a small
utility header (like <bits/ranges_concepts.h> that can be included
instead of the whole of <ranges>.

>+#include <ranges>
>+#include <bits/invoke.h>
>+#include <bits/cpp_type_traits.h> // __is_byte
>+#include <bits/random.h> // concept uniform_random_bit_generator

I wonder if we want to move that concept to <bits/uniform_rand_dist.h>
instead, which already exists to allow <algorithm> to avoid including
the whole of <random>. If we do that, it would make sense to rename
<bits/uniform_rand_dist.h> to <bits/random_fwd.h> or something like
that.

>+
>+#if __cpp_lib_concepts
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+namespace ranges
>+{
>+  namespace __detail
>+  {
>+    template<typename _Tp>
>+    constexpr inline bool __is_normal_iterator = false;

All these templates in the __detail namespace should be indented by
two spaces after the template-head i.e.

     template<typename _Tp>
       constexpr inline bool __is_normal_iterator = false;

(That indentation scheme has been in the libstdc++ style guide for
longer than I've been contributing to the project, but it doesn't seem
very popular with new contributors, and it wastes a level of
indentation for templates, which means most of the library. Maybe we
should revisit that convention.)


>+  template<typename _Iter, typename _Out>
>+    using unary_transform_result = copy_result<_Iter, _Out>;
>+
>+  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
>+	   weakly_incrementable _Out,
>+	   copy_constructible _Fp, typename _Proj = identity>
>+    requires writable<_Out, indirect_result_t<_Fp&, projected<_Iter, _Proj>>>

I have a pending patch to implement P1878R1, which renames writable
(and a few other concepts). I'll wait until your patch is in, and
change these places using it.

>+    partial_sort_copy(_Iter1 __first, _Sent1 __last,
>+		      _Iter2 __result_first, _Sent2 __result_last,
>+		      _Comp __comp = {},
>+		      _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
>+    {
>+      if (__result_first == __result_last)
>+	{
>+	  // TODO: Eliminating the variable __lasti triggers an ICE.
>+	  auto __lasti = ranges::next(std::move(__first),
>+				      std::move(__last));
>+	  return {std::move(__lasti), std::move(__result_first)};

Please try to reduce that and report it to bugzilla at some point,
thanks.

>+++ b/libstdc++-v3/testsuite/25_algorithms/all_of/constrained.cc
>@@ -0,0 +1,90 @@
>+// Copyright (C) 2019 Free Software Foundation, Inc.

This should be 2020. That's the only change necessary though, please
adjust that and commit to master. Great work, thank you!

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-04  2:08 ` [PATCH 3/3] libstdc++: Implement C++20 range adaptors Patrick Palka
@ 2020-02-06 17:25   ` Jonathan Wakely
  2020-02-06 23:53     ` Patrick Palka
  2020-02-17 22:07   ` Stephan Bergmann
  1 sibling, 1 reply; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-06 17:25 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On 03/02/20 21:07 -0500, Patrick Palka wrote:
>This patch implements [range.adaptors].  It also includes the changes from P3280
>and P3278 and P3323, without which many standard examples won't work.
>
>The implementation is mostly dictated by the spec and there was not much room
>for implementation discretion.  The most interesting part that was not specified
>by the spec is the design of the range adaptors and range adaptor closures,
>which I tried to design in a way that minimizes boilerplate and statefulness (so
>that e.g. the composition of two stateless closures is stateless).
>
>What is left unimplemented is caching of calls to begin() in filter_view,
>drop_view and reverse_view, which is required to guarantee that begin() has
>amortized constant time complexity.  I can implement this in a subsequent patch.
>
>"Interesting" parts of the patch are marked with XXX comments.



>--- a/libstdc++-v3/include/std/ranges
>+++ b/libstdc++-v3/include/std/ranges
>@@ -39,6 +39,7 @@
> #if __cpp_lib_concepts
>
> #include <compare>
>+#include <functional> // std::ref

Please use <bits/refwrap.h> instead. <functional> is huge.

> #include <initializer_list>
> #include <iterator>
> #include <limits>
>+
>+namespace __detail
>+{
>+  struct _Empty { };
>+} // namespace __detail
>+
>+namespace views
>+{
>+  template<typename _Callable>
>+    struct _RangeAdaptorClosure;
>+
>+  template<typename _Callable>
>+    struct _RangeAdaptor
>+    {
>+    protected:
>+      [[no_unique_address]]
>+	conditional_t<!is_default_constructible_v<_Callable>,
>+		      _Callable, __detail::_Empty> _M_callable;
>+
>+    public:
>+      constexpr
>+      _RangeAdaptor(const _Callable& = {})
>+	requires is_default_constructible_v<_Callable>
>+      { }
>+
>+      constexpr
>+      _RangeAdaptor(_Callable __callable)

As mentioned on IRC, the non-explicit constructors here make me
nervous. I'd either like them to be explicit, or for these typesto be
in their own namespace so that there is never a reason to attempt
implicit conversions to them just because some function related to
them is found in an associated namespace.

>+	requires (!is_default_constructible_v<_Callable>)
>+	: _M_callable(std::move(__callable))
>+      { }
>+




>+      template<__detail::__not_same_as<ref_view> _Tp>
>+	requires convertible_to<_Tp, _Range&>
>+	  && requires { _S_fun(declval<_Tp>()); }
>+	constexpr
>+	ref_view(_Tp&& __t)
>+	  : _M_r(addressof(static_cast<_Range&>(std::forward<_Tp>(__t))))

This should be std-qualified to avoid ADL, and should use the internal
std::__addressof function (just to avoid the extra call from
std::addressof).

>+  // XXX: the following algos are copied verbatim from ranges_algo.h to avoid a
>+  // circular dependency with that header.

Ugh, that's unfortunate, but OK.

I guess we could put the body of the functions in new, unconstrained
functions, and then have the ones in <bits/ranges_algo.h> and these
call those, to reuse the implementations. But they're so small and
simple it's probably not worth it.

>+  namespace __detail
>+  {
>+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
>+	     typename _Proj = identity,
>+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
>+      constexpr _Iter
>+      find_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
>+      {
>+	while (__first != __last
>+	    && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
>+	  ++__first;
>+	return __first;
>+      }
>+
>+    template<input_range _Range, typename _Proj = identity,
>+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
>+	       _Pred>
>+      constexpr safe_iterator_t<_Range>
>+      find_if(_Range&& __r, _Pred __pred, _Proj __proj = {})
>+      {
>+	return __detail::find_if(ranges::begin(__r), ranges::end(__r),
>+				 std::move(__pred), std::move(__proj));
>+      }

It looks like maybe we don't need this overload.

>+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
>+	     typename _Proj = identity,
>+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
>+      constexpr _Iter
>+      find_if_not(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
>+      {
>+	while (__first != __last
>+	    && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
>+	  ++__first;
>+	return __first;
>+      }
>+
>+    template<input_range _Range, typename _Proj = identity,
>+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
>+	       _Pred>
>+      constexpr safe_iterator_t<_Range>
>+      find_if_not(_Range&& __r, _Pred __pred, _Proj __proj = {})
>+      {
>+	return __detail::find_if_not(ranges::begin(__r), ranges::end(__r),
>+				     std::move(__pred), std::move(__proj));
>+      }

Nor this one.

>+    template<typename _Tp, typename _Proj = identity,
>+	     indirect_strict_weak_order<projected<const _Tp*, _Proj>>
>+	       _Comp = ranges::less>
>+      constexpr const _Tp&
>+      min(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {})
>+      {
>+	if (std::__invoke(std::move(__comp),
>+			  std::__invoke(__proj, __b),
>+			  std::__invoke(__proj, __a)))
>+	  return __b;
>+	else
>+	  return __a;
>+      }
>+
>+    template<typename _Iter1, typename _Iter2>
>+      struct mismatch_result

It seems like these result types could definitely be shared (by
putting them in a new <bits/xxx> header that both include). Duplicated
function templates isn't great, but not too bad. Duplicated class
templates means additional RTTI and debuginfo gets generated. We
should definitely revisit this.

If/when we add a new <bits/ranges_concepts.h> header these kind of
thigns could be moved in there.

>+      {
>+	[[no_unique_address]] _Iter1 in1;
>+	[[no_unique_address]] _Iter2 in2;
>+
>+	template<typename _IIter1, typename _IIter2>
>+	  requires convertible_to<const _Iter1&, _IIter1>
>+	    && convertible_to<const _Iter2&, _IIter2>
>+	  operator mismatch_result<_IIter1, _IIter2>() const &
>+	  {
>+	    return {in1, in2};
>+	  }
>+
>+	template<typename _IIter1, typename _IIter2>
>+	  requires convertible_to<_Iter1, _IIter1>
>+	    && convertible_to<_Iter2, _IIter2>
>+	  operator mismatch_result<_IIter1, _IIter2>() &&
>+	  {
>+	    return {std::move(in1), std::move(in2)};
>+	  }

Do we need these conversion operators here? They don't seem to be
needed.

>+	constexpr
>+	_Iterator(filter_view& __parent, iterator_t<_Vp> __current)
>+	  : _M_current(std::move(__current)),
>+	    _M_parent(addressof(__parent))

std::__addressof.

>+	{ }
>+
>+	constexpr iterator_t<_Vp>
>+	base() const &
>+	  requires copyable<iterator_t<_Vp>>
>+	{ return _M_current; }
>+
>+	constexpr iterator_t<_Vp>
>+	base() &&
>+	{ return std::move(_M_current); }
>+
>+	constexpr range_reference_t<_Vp>
>+	operator*() const
>+	{ return *_M_current; }
>+
>+	constexpr iterator_t<_Vp>
>+	operator->() const
>+	  requires __detail::__has_arrow<iterator_t<_Vp>>
>+	    && copyable<iterator_t<_Vp>>
>+	{ return _M_current; }
>+
>+	constexpr _Iterator&
>+	operator++()
>+	{
>+	  _M_current = __detail::find_if(std::move(++_M_current),
>+					 ranges::end(_M_parent->_M_base),
>+					 std::ref(*_M_parent->_M_pred));
>+	  return *this;
>+	}
>+
>+	constexpr void
>+	operator++(int)
>+	{ ++*this; }
>+
>+	constexpr _Iterator
>+	operator++(int) requires forward_range<_Vp>
>+	{
>+	  auto __tmp = *this;
>+	  ++*this;
>+	  return __tmp;
>+	}
>+
>+	constexpr _Iterator&
>+	operator--() requires bidirectional_range<_Vp>
>+	{
>+	  do
>+	    --_M_current;
>+	  while (!invoke(*_M_parent->_M_pred, *_M_current));

This should be std::__invoke.

>+      constexpr _Iterator
>+      begin()
>+      {
>+	// XXX: we need to cache the result here as per [range.filter.view]
>+	__glibcxx_assert(_M_pred.has_value());
>+	return {*this, __detail::find_if(_M_base, std::ref(*_M_pred))};

If this used:

   __detail::find_if(ranges:begin(_M_base),
                     ranges::end(_M_base), std::ref(*_M_pred))

We wouldn't need the find_if(_Range&&, ...) overload.

>+      }



>+	  static constexpr decltype(auto)
>+	  __iter_move(const _Iterator& __i = {})
>+	    noexcept(noexcept(invoke(*__i._M_parent->_M_fun, *__i._M_current)))

std::__invoke

>+	  {
>+	    if constexpr (is_lvalue_reference_v<decltype(*__i)>)
>+	      return std::move(*__i);
>+	    else
>+	      return *__i;
>+	  }


>+	  constexpr decltype(auto)
>+	  operator*() const
>+	  { return invoke(*_M_parent->_M_fun, *_M_current); }

std::__invoke.

>+	  constexpr decltype(auto)
>+	  operator[](difference_type __n) const
>+	    requires random_access_range<_Base>
>+	  { return invoke(*_M_parent->_M_fun, _M_current[__n]); }

std::__invoke

>+
>+	  friend constexpr bool
>+	  operator==(const _Iterator& __x, const _Iterator& __y)
>+	    requires equality_comparable<iterator_t<_Base>>
>+	  { return __x._M_current == __y._M_current; }
>+
>+	  friend constexpr bool
>+	  operator<(const _Iterator& __x, const _Iterator& __y)
>+	    requires random_access_range<_Base>
>+	  { return __x._M_current < __y._M_current; }
>+
>+	  friend constexpr bool
>+	  operator>(const _Iterator& __x, const _Iterator& __y)
>+	    requires random_access_range<_Base>
>+	  { return __y < __x; }
>+
>+	  friend constexpr bool
>+	  operator<=(const _Iterator& __x, const _Iterator& __y)
>+	    requires random_access_range<_Base>
>+	  { return !(__y < __x); }
>+
>+	  friend constexpr bool
>+	  operator>=(const _Iterator& __x, const _Iterator& __y)
>+	    requires random_access_range<_Base>
>+	  { return !(__x < __y); }
>+
>+#ifdef __cpp_lib_threeway_comparison

This macro is mispelled, should be three_way with an underscore.

>+	  friend constexpr compare_three_way_result_t<iterator_t<_Base>>

Hmm, I think this return type will be ill-formed and make the whole
class ill-formed if iterator_t<_Base> doesn't satisfy
three_way_comparable. That seems like another library issue.

We should probably just return auto for now, right?

>+	  operator<=>(const _Iterator& __x, const _Iterator& __y)
>+	    requires random_access_range<_Base>
>+	      && three_way_comparable<iterator_t<_Base>>
>+	  { return __x._M_current <=> __y._M_current; }
>+#endif


>+	  friend constexpr bool
>+	  operator==(const iterator_t<_Base>& __x, const _Sentinel& __y)
>+	  { return __y._M_end == __x || !invoke(*__y._M_pred, *__x); }

std::__invoke


>+      {
>+	return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
>+      };
>+  } // namespace views
>+
>+  template<view _Vp>
>+    class drop_view : public view_interface<drop_view<_Vp>>
>+    {
>+    private:
>+      _Vp _M_base;
>+      range_difference_t<_Vp> _M_count;
>+
>+    public:
>+      drop_view() = default;
>+
>+      constexpr
>+      drop_view(_Vp __base, range_difference_t<_Vp> __count)
>+	: _M_base(std::move(__base)), _M_count(__count)
>+      { __glibcxx_assert(__count >= 0); }

Thanks for putting these debug assertions in.

>+      constexpr auto
>+      begin()
>+      {
>+	// XXX: we need to cache the result here as per [range.drop.while.view]
>+	return __detail::find_if_not(_M_base, cref(*_M_pred));

Again, this could use ranges::begin(_M_base) and ranges::end(_M_base)
to avoid one of the duplicated find_if_not overloads.


>+
>+    inline constexpr _RangeAdaptorClosure reverse
>+      = [] <viewable_range _Range> (_Range&& __r)
>+      {
>+	using _Tp = remove_cvref_t<_Range>;
>+	if constexpr (__detail::__is_reverse_view<_Tp>)
>+	  return std::forward<_Range>(__r).base();
>+	else if constexpr (__detail::__is_reversible_subrange<_Tp>)
>+	  {
>+	    using _Iter = decltype(ranges::begin(__r).base());
>+	    if constexpr (sized_range<_Tp>)
>+	      return subrange<_Iter, _Iter, subrange_kind::sized>
>+		      (__r.end().base(), __r.begin().base(), __r.size());
>+	    else
>+	      return subrange<_Iter, _Iter, subrange_kind::unsized>
>+		      (__r.end().base(), __r.begin().base());
>+	  }
>+	else
>+	  return reverse_view{std::forward<_Range>(__r)};
>+      };
>+  } // namespace views
>+
>+  namespace __detail
>+  {
>+    template<typename _Tp, size_t _Nm>
>+    concept __has_tuple_element = requires(_Tp __t)
>+      {
>+	typename tuple_size<_Tp>::type;
>+	requires _Nm < tuple_size_v<_Tp>;
>+	typename tuple_element_t<_Nm, _Tp>;
>+	// XXX: we applied P3323 here
>+	{ get<_Nm>(__t) } -> convertible_to<const tuple_element_t<_Nm, _Tp>&>;

std::get here.

>+	constexpr decltype(auto)
>+	  operator*() const
>+	{ return get<_Nm>(*_M_current); }

and std::get here.

>+	constexpr decltype(auto)
>+	operator[](difference_type __n) const
>+	  requires random_access_range<_Base>
>+	{ return get<_Nm>(*(_M_current + __n)); }

and std::get here.

>+#ifdef __cpp_lib_threeway_comparison

Misspelled macro again.

>+	friend constexpr compare_three_way_result_t<iterator_t<_Base>>

Same issue with the return type.

>+	operator<=>(const _Iterator& __x, const _Iterator& __y)
>+	  requires random_access_range<_Base>
>+	    && three_way_comparable<iterator_t<_Base>>
>+	{ return __x._M_current <=> __y._M_current; }
>+#endif
>+


>new file mode 100644
>index 00000000000..d1600544605
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
>@@ -0,0 +1,68 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.

Does this actually use any old code from 2019 or should it just be
2020? (Same question for some other new tests).

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-06 17:25   ` Jonathan Wakely
@ 2020-02-06 23:53     ` Patrick Palka
  2020-02-07 11:48       ` Jonathan Wakely
  0 siblings, 1 reply; 27+ messages in thread
From: Patrick Palka @ 2020-02-06 23:53 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 127431 bytes --]

On Thu, 6 Feb 2020, Jonathan Wakely wrote:

> On 03/02/20 21:07 -0500, Patrick Palka wrote:
> > This patch implements [range.adaptors].  It also includes the changes from
> > P3280
> > and P3278 and P3323, without which many standard examples won't work.
> > 
> > The implementation is mostly dictated by the spec and there was not much
> > room
> > for implementation discretion.  The most interesting part that was not
> > specified
> > by the spec is the design of the range adaptors and range adaptor closures,
> > which I tried to design in a way that minimizes boilerplate and statefulness
> > (so
> > that e.g. the composition of two stateless closures is stateless).
> > 
> > What is left unimplemented is caching of calls to begin() in filter_view,
> > drop_view and reverse_view, which is required to guarantee that begin() has
> > amortized constant time complexity.  I can implement this in a subsequent
> > patch.
> > 
> > "Interesting" parts of the patch are marked with XXX comments.
> 
> 
> 
> > --- a/libstdc++-v3/include/std/ranges
> > +++ b/libstdc++-v3/include/std/ranges
> > @@ -39,6 +39,7 @@
> > #if __cpp_lib_concepts
> > 
> > #include <compare>
> > +#include <functional> // std::ref
> 
> Please use <bits/refwrap.h> instead. <functional> is huge.

Fixed.

> 
> > #include <initializer_list>
> > #include <iterator>
> > #include <limits>
> > +
> > +namespace __detail
> > +{
> > +  struct _Empty { };
> > +} // namespace __detail
> > +
> > +namespace views
> > +{
> > +  template<typename _Callable>
> > +    struct _RangeAdaptorClosure;
> > +
> > +  template<typename _Callable>
> > +    struct _RangeAdaptor
> > +    {
> > +    protected:
> > +      [[no_unique_address]]
> > +	conditional_t<!is_default_constructible_v<_Callable>,
> > +		      _Callable, __detail::_Empty> _M_callable;
> > +
> > +    public:
> > +      constexpr
> > +      _RangeAdaptor(const _Callable& = {})
> > +	requires is_default_constructible_v<_Callable>
> > +      { }
> > +
> > +      constexpr
> > +      _RangeAdaptor(_Callable __callable)
> 
> As mentioned on IRC, the non-explicit constructors here make me
> nervous. I'd either like them to be explicit, or for these typesto be
> in their own namespace so that there is never a reason to attempt
> implicit conversions to them just because some function related to
> them is found in an associated namespace.

Fixed by quarantining these classes into the namespace __adaptor.

> 
> > +	requires (!is_default_constructible_v<_Callable>)
> > +	: _M_callable(std::move(__callable))
> > +      { }
> > +
> 
> 
> 
> 
> > +      template<__detail::__not_same_as<ref_view> _Tp>
> > +	requires convertible_to<_Tp, _Range&>
> > +	  && requires { _S_fun(declval<_Tp>()); }
> > +	constexpr
> > +	ref_view(_Tp&& __t)
> > +	  : _M_r(addressof(static_cast<_Range&>(std::forward<_Tp>(__t))))
> 
> This should be std-qualified to avoid ADL, and should use the internal
> std::__addressof function (just to avoid the extra call from
> std::addressof).

Fixed, for good measure I replaced every call to addressof with
std::__addressof.

> 
> > +  // XXX: the following algos are copied verbatim from ranges_algo.h to
> > avoid a
> > +  // circular dependency with that header.
> 
> Ugh, that's unfortunate, but OK.
> 
> I guess we could put the body of the functions in new, unconstrained
> functions, and then have the ones in <bits/ranges_algo.h> and these
> call those, to reuse the implementations. But they're so small and
> simple it's probably not worth it.

Yeah, I suppose not given their simplicity.

> 
> > +  namespace __detail
> > +  {
> > +    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
> > +	     typename _Proj = identity,
> > +	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
> > +      constexpr _Iter
> > +      find_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
> > +      {
> > +	while (__first != __last
> > +	    && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
> > +	  ++__first;
> > +	return __first;
> > +      }
> > +
> > +    template<input_range _Range, typename _Proj = identity,
> > +	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
> > +	       _Pred>
> > +      constexpr safe_iterator_t<_Range>
> > +      find_if(_Range&& __r, _Pred __pred, _Proj __proj = {})
> > +      {
> > +	return __detail::find_if(ranges::begin(__r), ranges::end(__r),
> > +				 std::move(__pred), std::move(__proj));
> > +      }
> 
> It looks like maybe we don't need this overload.

Fixed by removing this overload.

> 
> > +    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
> > +	     typename _Proj = identity,
> > +	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
> > +      constexpr _Iter
> > +      find_if_not(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj =
> > {})
> > +      {
> > +	while (__first != __last
> > +	    && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
> > +	  ++__first;
> > +	return __first;
> > +      }
> > +
> > +    template<input_range _Range, typename _Proj = identity,
> > +	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
> > +	       _Pred>
> > +      constexpr safe_iterator_t<_Range>
> > +      find_if_not(_Range&& __r, _Pred __pred, _Proj __proj = {})
> > +      {
> > +	return __detail::find_if_not(ranges::begin(__r), ranges::end(__r),
> > +				     std::move(__pred), std::move(__proj));
> > +      }
> 
> Nor this one.

Fixed by removing this overload too.

> 
> > +    template<typename _Tp, typename _Proj = identity,
> > +	     indirect_strict_weak_order<projected<const _Tp*, _Proj>>
> > +	       _Comp = ranges::less>
> > +      constexpr const _Tp&
> > +      min(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj =
> > {})
> > +      {
> > +	if (std::__invoke(std::move(__comp),
> > +			  std::__invoke(__proj, __b),
> > +			  std::__invoke(__proj, __a)))
> > +	  return __b;
> > +	else
> > +	  return __a;
> > +      }
> > +
> > +    template<typename _Iter1, typename _Iter2>
> > +      struct mismatch_result
> 
> It seems like these result types could definitely be shared (by
> putting them in a new <bits/xxx> header that both include). Duplicated
> function templates isn't great, but not too bad. Duplicated class
> templates means additional RTTI and debuginfo gets generated. We
> should definitely revisit this.
> 
> If/when we add a new <bits/ranges_concepts.h> header these kind of
> thigns could be moved in there.

In the new version of the patch this local copy of mismatch_result and
made mismatch() return a pair<_Iter1, _Iter2> instead.

> 
> > +      {
> > +	[[no_unique_address]] _Iter1 in1;
> > +	[[no_unique_address]] _Iter2 in2;
> > +
> > +	template<typename _IIter1, typename _IIter2>
> > +	  requires convertible_to<const _Iter1&, _IIter1>
> > +	    && convertible_to<const _Iter2&, _IIter2>
> > +	  operator mismatch_result<_IIter1, _IIter2>() const &
> > +	  {
> > +	    return {in1, in2};
> > +	  }
> > +
> > +	template<typename _IIter1, typename _IIter2>
> > +	  requires convertible_to<_Iter1, _IIter1>
> > +	    && convertible_to<_Iter2, _IIter2>
> > +	  operator mismatch_result<_IIter1, _IIter2>() &&
> > +	  {
> > +	    return {std::move(in1), std::move(in2)};
> > +	  }
> 
> Do we need these conversion operators here? They don't seem to be
> needed.

Fixed by removing mismatch_result altogether.

> 
> > +	constexpr
> > +	_Iterator(filter_view& __parent, iterator_t<_Vp> __current)
> > +	  : _M_current(std::move(__current)),
> > +	    _M_parent(addressof(__parent))
> 
> std::__addressof.
> 
> > +	{ }
> > +
> > +	constexpr iterator_t<_Vp>
> > +	base() const &
> > +	  requires copyable<iterator_t<_Vp>>
> > +	{ return _M_current; }
> > +
> > +	constexpr iterator_t<_Vp>
> > +	base() &&
> > +	{ return std::move(_M_current); }
> > +
> > +	constexpr range_reference_t<_Vp>
> > +	operator*() const
> > +	{ return *_M_current; }
> > +
> > +	constexpr iterator_t<_Vp>
> > +	operator->() const
> > +	  requires __detail::__has_arrow<iterator_t<_Vp>>
> > +	    && copyable<iterator_t<_Vp>>
> > +	{ return _M_current; }
> > +
> > +	constexpr _Iterator&
> > +	operator++()
> > +	{
> > +	  _M_current = __detail::find_if(std::move(++_M_current),
> > +					 ranges::end(_M_parent->_M_base),
> > +					 std::ref(*_M_parent->_M_pred));
> > +	  return *this;
> > +	}
> > +
> > +	constexpr void
> > +	operator++(int)
> > +	{ ++*this; }
> > +
> > +	constexpr _Iterator
> > +	operator++(int) requires forward_range<_Vp>
> > +	{
> > +	  auto __tmp = *this;
> > +	  ++*this;
> > +	  return __tmp;
> > +	}
> > +
> > +	constexpr _Iterator&
> > +	operator--() requires bidirectional_range<_Vp>
> > +	{
> > +	  do
> > +	    --_M_current;
> > +	  while (!invoke(*_M_parent->_M_pred, *_M_current));
> 
> This should be std::__invoke.

Fixed here and everywhere else.

> 
> > +      constexpr _Iterator
> > +      begin()
> > +      {
> > +	// XXX: we need to cache the result here as per [range.filter.view]
> > +	__glibcxx_assert(_M_pred.has_value());
> > +	return {*this, __detail::find_if(_M_base, std::ref(*_M_pred))};
> 
> If this used:
> 
>   __detail::find_if(ranges:begin(_M_base),
>                     ranges::end(_M_base), std::ref(*_M_pred))
> 
> We wouldn't need the find_if(_Range&&, ...) overload.

Fixed.

> 
> > +      }
> 
> 
> 
> > +	  static constexpr decltype(auto)
> > +	  __iter_move(const _Iterator& __i = {})
> > +	    noexcept(noexcept(invoke(*__i._M_parent->_M_fun,
> > *__i._M_current)))
> 
> std::__invoke
> 
> > +	  {
> > +	    if constexpr (is_lvalue_reference_v<decltype(*__i)>)
> > +	      return std::move(*__i);
> > +	    else
> > +	      return *__i;
> > +	  }
> 
> 
> > +	  constexpr decltype(auto)
> > +	  operator*() const
> > +	  { return invoke(*_M_parent->_M_fun, *_M_current); }
> 
> std::__invoke.
> 
> > +	  constexpr decltype(auto)
> > +	  operator[](difference_type __n) const
> > +	    requires random_access_range<_Base>
> > +	  { return invoke(*_M_parent->_M_fun, _M_current[__n]); }
> 
> std::__invoke
> 
> > +
> > +	  friend constexpr bool
> > +	  operator==(const _Iterator& __x, const _Iterator& __y)
> > +	    requires equality_comparable<iterator_t<_Base>>
> > +	  { return __x._M_current == __y._M_current; }
> > +
> > +	  friend constexpr bool
> > +	  operator<(const _Iterator& __x, const _Iterator& __y)
> > +	    requires random_access_range<_Base>
> > +	  { return __x._M_current < __y._M_current; }
> > +
> > +	  friend constexpr bool
> > +	  operator>(const _Iterator& __x, const _Iterator& __y)
> > +	    requires random_access_range<_Base>
> > +	  { return __y < __x; }
> > +
> > +	  friend constexpr bool
> > +	  operator<=(const _Iterator& __x, const _Iterator& __y)
> > +	    requires random_access_range<_Base>
> > +	  { return !(__y < __x); }
> > +
> > +	  friend constexpr bool
> > +	  operator>=(const _Iterator& __x, const _Iterator& __y)
> > +	    requires random_access_range<_Base>
> > +	  { return !(__x < __y); }
> > +
> > +#ifdef __cpp_lib_threeway_comparison
> 
> This macro is mispelled, should be three_way with an underscore.

Oops!  It looks like it's also mispelled in the definition of iota_view
earlier in this file.


> 
> > +	  friend constexpr compare_three_way_result_t<iterator_t<_Base>>
> 
> Hmm, I think this return type will be ill-formed and make the whole
> class ill-formed if iterator_t<_Base> doesn't satisfy
> three_way_comparable. That seems like another library issue.
> 
> We should probably just return auto for now, right?

That makes sense, fixed.

> 
> > +	  operator<=>(const _Iterator& __x, const _Iterator& __y)
> > +	    requires random_access_range<_Base>
> > +	      && three_way_comparable<iterator_t<_Base>>
> > +	  { return __x._M_current <=> __y._M_current; }
> > +#endif
> 
> 
> > +	  friend constexpr bool
> > +	  operator==(const iterator_t<_Base>& __x, const _Sentinel& __y)
> > +	  { return __y._M_end == __x || !invoke(*__y._M_pred, *__x); }
> 
> std::__invoke
> 
> 
> > +      {
> > +	return take_while_view{std::forward<_Range>(__r),
> > std::forward<_Pred>(__p)};
> > +      };
> > +  } // namespace views
> > +
> > +  template<view _Vp>
> > +    class drop_view : public view_interface<drop_view<_Vp>>
> > +    {
> > +    private:
> > +      _Vp _M_base;
> > +      range_difference_t<_Vp> _M_count;
> > +
> > +    public:
> > +      drop_view() = default;
> > +
> > +      constexpr
> > +      drop_view(_Vp __base, range_difference_t<_Vp> __count)
> > +	: _M_base(std::move(__base)), _M_count(__count)
> > +      { __glibcxx_assert(__count >= 0); }
> 
> Thanks for putting these debug assertions in.

I've tried to add an assertion it whenever the spec explicitly listed a
precondition, there weren't very many of them here.

> 
> > +      constexpr auto
> > +      begin()
> > +      {
> > +	// XXX: we need to cache the result here as per
> > [range.drop.while.view]
> > +	return __detail::find_if_not(_M_base, cref(*_M_pred));
> 
> Again, this could use ranges::begin(_M_base) and ranges::end(_M_base)
> to avoid one of the duplicated find_if_not overloads.

Fixed.

> 
> 
> > +
> > +    inline constexpr _RangeAdaptorClosure reverse
> > +      = [] <viewable_range _Range> (_Range&& __r)
> > +      {
> > +	using _Tp = remove_cvref_t<_Range>;
> > +	if constexpr (__detail::__is_reverse_view<_Tp>)
> > +	  return std::forward<_Range>(__r).base();
> > +	else if constexpr (__detail::__is_reversible_subrange<_Tp>)
> > +	  {
> > +	    using _Iter = decltype(ranges::begin(__r).base());
> > +	    if constexpr (sized_range<_Tp>)
> > +	      return subrange<_Iter, _Iter, subrange_kind::sized>
> > +		      (__r.end().base(), __r.begin().base(), __r.size());
> > +	    else
> > +	      return subrange<_Iter, _Iter, subrange_kind::unsized>
> > +		      (__r.end().base(), __r.begin().base());
> > +	  }
> > +	else
> > +	  return reverse_view{std::forward<_Range>(__r)};
> > +      };
> > +  } // namespace views
> > +
> > +  namespace __detail
> > +  {
> > +    template<typename _Tp, size_t _Nm>
> > +    concept __has_tuple_element = requires(_Tp __t)
> > +      {
> > +	typename tuple_size<_Tp>::type;
> > +	requires _Nm < tuple_size_v<_Tp>;
> > +	typename tuple_element_t<_Nm, _Tp>;
> > +	// XXX: we applied P3323 here
> > +	{ get<_Nm>(__t) } -> convertible_to<const tuple_element_t<_Nm, _Tp>&>;
> 
> std::get here.

Fixed here and everywhere else.

> 
> > +	constexpr decltype(auto)
> > +	  operator*() const
> > +	{ return get<_Nm>(*_M_current); }
> 
> and std::get here.
> 
> > +	constexpr decltype(auto)
> > +	operator[](difference_type __n) const
> > +	  requires random_access_range<_Base>
> > +	{ return get<_Nm>(*(_M_current + __n)); }
> 
> and std::get here.
> 
> > +#ifdef __cpp_lib_threeway_comparison
> 
> Misspelled macro again.

Fixed.

> 
> > +	friend constexpr compare_three_way_result_t<iterator_t<_Base>>
> 
> Same issue with the return type.

Fixed.

> 
> > +	operator<=>(const _Iterator& __x, const _Iterator& __y)
> > +	  requires random_access_range<_Base>
> > +	    && three_way_comparable<iterator_t<_Base>>
> > +	{ return __x._M_current <=> __y._M_current; }
> > +#endif
> > +
> 
> 
> > new file mode 100644
> > index 00000000000..d1600544605
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
> > @@ -0,0 +1,68 @@
> > +// Copyright (C) 2019-2020 Free Software Foundation, Inc.
> 
> Does this actually use any old code from 2019 or should it just be
> 2020? (Same question for some other new tests).

Fixed here and in the other tests.

Here's an updated patch that fixes all of the above and adds a missing
piece of the ranges header that was present at the end of its synopsis
in the spec.  It also makes some minor formatting fixes and fixes some
accidental deviations from the spec.  Incremental changes can be seen at
https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;a=shortlog;h=refs/users/ppalka/heads/libstdcxx-constrained-algos-adaptors

-- >8 --

libstdc++-v3/ChangeLog:

	Implement C++20 range adaptors
	* include/std/ranges: Include <functional>.
	(subrange::_S_store_size): Mark as const instead of constexpr to
	avoid what seems to be a bug in GCC.
	(__detail::__box): Give it defaulted copy and move constructors.
	(ranges::views::_Single::operator()): Mark constexpr.
	(ranges::views::_Iota::operator()): Mark constexpr.
	(__detail::Empty): Define.
	(ranges::views::__closure::_RangeAdaptor,
	ranges::views::__closure::_RangeAdaptorClosure, ref_view, all_view,
	ranges::views::all, ranges::__detail::find_if,
	ranges::__detail::find_if_not, ranges::__detail::mismatch,
	ranges::detail::min, filter_view, ranges::views::filter, transform_view,
	ranges::views::transform, take_view, ranges::views::take,
	take_while_view, ranges::views::take_while, drop_view,
	ranges::views::drop, join_view, ranges::views::join,
	__detail::require_constant, __detail::tiny_range, split_view,
	ranges::views::split, ranges::views::_Counted, ranges::views::counted,
	common_view, ranges::views::common, reverse_view,
	ranges::views::reverse,
	ranges::views::__detail::__is_reversible_subrange,
	ranges::views::__detail::__is_reverse_view, reverse_view,
	ranges::views::reverse, __detail::__has_tuple_element, elements_view,
	ranges::views::elements, ranges::views::keys, ranges::views::values):
	Define.
	(views): Alias for ranges::views.
	(tuple_size<ranges::subrange<>>, tuple_element<0, ranges::subrange,
	tuple_element<1, ranges::subrange): New partial specializations.
	* testsuite/std/ranges/adaptors/all.cc: New test.
	* testsuite/std/ranges/adaptors/common.cc: Likewise.
	* testsuite/std/ranges/adaptors/counted.cc: Likewise.
	* testsuite/std/ranges/adaptors/drop.cc: Likewise.
	* testsuite/std/ranges/adaptors/drop_while.cc: Likewise.
	* testsuite/std/ranges/adaptors/elements.cc: Likewise.
	* testsuite/std/ranges/adaptors/filter.cc: Likewise.
	* testsuite/std/ranges/adaptors/join.cc: Likewise.
	* testsuite/std/ranges/adaptors/reverse.cc: Likewise.
	* testsuite/std/ranges/adaptors/split.cc: Likewise.
	* testsuite/std/ranges/adaptors/take.cc: Likewise.
	* testsuite/std/ranges/adaptors/take_while.cc: Likewise.
	* testsuite/std/ranges/adaptors/transform.cc: Likewise.
---
 libstdc++-v3/include/std/ranges                    | 2405 +++++++++++++++++++-
 libstdc++-v3/testsuite/std/ranges/adaptors/all.cc  |  122 +
 .../testsuite/std/ranges/adaptors/common.cc        |   68 +
 .../testsuite/std/ranges/adaptors/counted.cc       |   64 +
 libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc |  107 +
 .../testsuite/std/ranges/adaptors/drop_while.cc    |   63 +
 .../testsuite/std/ranges/adaptors/elements.cc      |   52 +
 .../testsuite/std/ranges/adaptors/filter.cc        |   97 +
 libstdc++-v3/testsuite/std/ranges/adaptors/join.cc |  112 +
 .../testsuite/std/ranges/adaptors/reverse.cc       |   86 +
 .../testsuite/std/ranges/adaptors/split.cc         |   82 +
 libstdc++-v3/testsuite/std/ranges/adaptors/take.cc |   95 +
 .../testsuite/std/ranges/adaptors/take_while.cc    |   62 +
 .../testsuite/std/ranges/adaptors/transform.cc     |   86 +
 14 files changed, 3496 insertions(+), 5 deletions(-)

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 860f7283be5..8a8fefb6f19 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -38,11 +38,13 @@
 
 #if __cpp_lib_concepts
 
+#include <bits/refwrap.h>
 #include <compare>
 #include <initializer_list>
 #include <iterator>
 #include <limits>
 #include <optional>
+#include <tuple>
 
 /**
  * @defgroup ranges Ranges
@@ -255,7 +257,8 @@ namespace ranges
     class subrange : public view_interface<subrange<_It, _Sent, _Kind>>
     {
     private:
-      static constexpr bool _S_store_size
+      // XXX: gcc complains when using constexpr here
+      static const bool _S_store_size
 	= _Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _It>;
 
       _It _M_begin = _It();
@@ -507,6 +510,9 @@ namespace ranges
 	: std::optional<_Tp>{std::in_place}
 	{ }
 
+	__box(const __box&) = default;
+	__box(__box&&) = default;
+
 	using std::optional<_Tp>::operator=;
 
 	__box&
@@ -922,7 +928,7 @@ namespace views
   struct _Single
   {
     template<typename _Tp>
-      auto
+      constexpr auto
       operator()(_Tp&& __e) const
       { return single_view{std::forward<_Tp>(__e)}; }
   };
@@ -932,20 +938,2409 @@ namespace views
   struct _Iota
   {
     template<typename _Tp>
-      auto
+      constexpr auto
       operator()(_Tp&& __e) const
       { return iota_view{std::forward<_Tp>(__e)}; }
 
     template<typename _Tp, typename _Up>
-      auto
+      constexpr auto
       operator()(_Tp&& __e, _Up&& __f) const
       { return iota_view{std::forward<_Tp>(__e), std::forward<_Tp>(__f)}; }
   };
 
   inline constexpr _Iota iota{};
+} // namespace views
+
+namespace __detail
+{
+  struct _Empty { };
+} // namespace __detail
+
+namespace views
+{
+  namespace __adaptor
+  {
+    template<typename _Callable>
+      struct _RangeAdaptorClosure;
+
+    template<typename _Callable>
+      struct _RangeAdaptor
+      {
+      protected:
+	[[no_unique_address]]
+	  conditional_t<!is_default_constructible_v<_Callable>,
+			_Callable, __detail::_Empty> _M_callable;
+
+      public:
+	constexpr
+	_RangeAdaptor(const _Callable& = {})
+	  requires is_default_constructible_v<_Callable>
+	{ }
+
+	constexpr
+	_RangeAdaptor(_Callable __callable)
+	  requires (!is_default_constructible_v<_Callable>)
+	  : _M_callable(std::move(__callable))
+	{ }
+
+	template<typename... _Args>
+	  requires (sizeof...(_Args) >= 1)
+	  constexpr auto
+	  operator()(_Args&&... __args) const
+	  {
+	    if constexpr (is_invocable_v<_Callable, _Args...>)
+	      {
+		static_assert(sizeof...(_Args) != 1,
+			      "a _RangeAdaptor that accepts only one argument "
+			      "should be defined as a _RangeAdaptorClosure");
+		return _Callable{}(std::forward<_Args>(__args)...);
+	      }
+	    else
+	      {
+		auto __closure = [__args...] <typename _Range> (_Range&& __r) {
+		  return _Callable{}(std::forward<_Range>(__r), __args...);
+		};
+		using _ClosureType = decltype(__closure);
+		return _RangeAdaptorClosure<_ClosureType>(std::move(__closure));
+	      }
+	  }
+      };
+
+    template<typename _Callable>
+      struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
+      {
+	using _RangeAdaptor<_Callable>::_RangeAdaptor;
+
+	template<viewable_range _Range>
+	  requires requires { declval<_Callable>()(declval<_Range>()); }
+	  constexpr auto
+	  operator()(_Range&& __r) const
+	  {
+	    if constexpr (is_default_constructible_v<_Callable>)
+	      return _Callable{}(std::forward<_Range>(__r));
+	    else
+	      return this->_M_callable(std::forward<_Range>(__r));
+	  }
+
+	template<viewable_range _Range>
+	  requires requires { declval<_Callable>()(declval<_Range>()); }
+	  friend constexpr auto
+	  operator|(_Range&& __r, const _RangeAdaptorClosure& __o)
+	  { return __o(std::forward<_Range>(__r)); }
 
+	template<typename _Tp>
+	  friend constexpr auto
+	  operator|(const _RangeAdaptorClosure<_Tp>& __x,
+		    const _RangeAdaptorClosure& __y)
+	  {
+	    if constexpr (is_default_constructible_v<_Tp>
+			  && is_default_constructible_v<_Callable>)
+	      {
+		auto __closure = [] <typename _Up> (_Up&& __e) {
+		  return std::forward<_Up>(__e) | decltype(__x){} | decltype(__y){};
+		};
+		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	      }
+	    else if constexpr (is_default_constructible_v<_Tp>
+			       && !is_default_constructible_v<_Callable>)
+	      {
+		auto __closure = [__y] <typename _Up> (_Up&& __e) {
+		  return std::forward<_Up>(__e) | decltype(__x){} | __y;
+		};
+		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	      }
+	    else if constexpr (!is_default_constructible_v<_Tp>
+			       && is_default_constructible_v<_Callable>)
+	      {
+		auto __closure = [__x] <typename _Up> (_Up&& __e) {
+		  return std::forward<_Up>(__e) | __x | decltype(__y){};
+		};
+		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	      }
+	    else
+	      {
+		auto __closure = [__x, __y] <typename _Up> (_Up&& __e) {
+		  return std::forward<_Up>(__e) | __x | __y;
+		};
+		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
+	      }
+	  }
+      };
+
+    template<typename _Callable>
+      _RangeAdaptorClosure(_Callable) -> _RangeAdaptorClosure<_Callable>;
+  } // namespace __adaptor
 } // namespace views
-} // namespace ranges
+
+  template<range _Range> requires is_object_v<_Range>
+    class ref_view : public view_interface<ref_view<_Range>>
+    {
+    private:
+      _Range* _M_r = nullptr;
+
+      static void _S_fun(_Range&); // not defined
+      static void _S_fun(_Range&&) = delete;
+
+    public:
+      constexpr
+      ref_view() noexcept = default;
+
+      template<__detail::__not_same_as<ref_view> _Tp>
+	requires convertible_to<_Tp, _Range&>
+	  && requires { _S_fun(declval<_Tp>()); }
+	constexpr
+	ref_view(_Tp&& __t)
+	  : _M_r(std::__addressof(static_cast<_Range&>(std::forward<_Tp>(__t))))
+	{ }
+
+      constexpr _Range&
+      base() const
+      { return *_M_r; }
+
+      constexpr iterator_t<_Range>
+      begin() const
+      { return ranges::begin(*_M_r); }
+
+      constexpr sentinel_t<_Range>
+      end() const
+      { return ranges::end(*_M_r); }
+
+      constexpr bool
+      empty() const requires requires { ranges::empty(*_M_r); }
+      { return ranges::empty(*_M_r); }
+
+      constexpr auto
+      size() const requires sized_range<_Range>
+      { return ranges::size(*_M_r); }
+
+      constexpr auto
+      data() const requires contiguous_range<_Range>
+      { return ranges::data(*_M_r); }
+    };
+
+  template<typename _Range>
+    ref_view(_Range&) -> ref_view<_Range>;
+
+  template<typename _Tp>
+    inline constexpr bool enable_safe_range<ref_view<_Tp>> = true;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptorClosure all
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	if constexpr (view<decay_t<_Range>>)
+	  return std::forward<_Range>(__r);
+	else if constexpr (requires { ref_view{std::forward<_Range>(__r)}; })
+	  return ref_view{std::forward<_Range>(__r)};
+	else
+	  return subrange{std::forward<_Range>(__r)};
+      };
+  } // namespace views
+
+  template<viewable_range _Range>
+    using all_view = decltype(views::all(declval<_Range>()));
+
+  // XXX: the following algos are copied from ranges_algo.h to avoid a circular
+  // dependency with that header.
+  namespace __detail
+  {
+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	     typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr _Iter
+      find_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+      {
+	while (__first != __last
+	    && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  ++__first;
+	return __first;
+      }
+
+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	     typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr _Iter
+      find_if_not(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {})
+      {
+	while (__first != __last
+	    && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  ++__first;
+	return __first;
+      }
+
+    template<typename _Tp, typename _Proj = identity,
+	     indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+	       _Comp = ranges::less>
+      constexpr const _Tp&
+      min(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {})
+      {
+	if (std::__invoke(std::move(__comp),
+			  std::__invoke(__proj, __b),
+			  std::__invoke(__proj, __a)))
+	  return __b;
+	else
+	  return __a;
+      }
+
+    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 pair<_Iter1, _Iter2>
+      mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+	       _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
+      {
+	while (__first1 != __last1 && __first2 != __last2
+	       && (bool)std::__invoke(__pred,
+				      std::__invoke(__proj1, *__first1),
+				      std::__invoke(__proj2, *__first2)))
+	{
+	  ++__first1;
+	  ++__first2;
+	}
+	return { std::move(__first1), std::move(__first2) };
+      }
+  } // namespace __detail
+
+  template<input_range _Vp,
+	   indirect_unary_predicate<iterator_t<_Vp>> _Pred>
+    requires view<_Vp> && is_object_v<_Pred>
+    class filter_view : public view_interface<filter_view<_Vp, _Pred>>
+    {
+    private:
+      struct _Sentinel;
+
+      struct _Iterator
+      {
+      private:
+	static constexpr auto
+	_S_iter_concept()
+	{
+	  if constexpr (bidirectional_range<_Vp>)
+	    return bidirectional_iterator_tag{};
+	  else if constexpr (forward_range<_Vp>)
+	    return forward_iterator_tag{};
+	  else
+	    return input_iterator_tag{};
+	}
+
+	static constexpr auto
+	_S_iter_cat()
+	{
+	  using _Cat = iterator_traits<iterator_t<_Vp>>::iterator_category;
+	  if constexpr (derived_from<_Cat, bidirectional_iterator_tag>)
+	    return bidirectional_iterator_tag{};
+	  else if constexpr (derived_from<_Cat, forward_iterator_tag>)
+	    return forward_iterator_tag{};
+	  else
+	    return _Cat{};
+	}
+
+	friend filter_view;
+
+	iterator_t<_Vp> _M_current = iterator_t<_Vp>();
+	filter_view* _M_parent = nullptr;
+
+      public:
+	using iterator_concept = decltype(_S_iter_concept());
+	using iterator_category = decltype(_S_iter_cat());
+	using value_type = range_value_t<_Vp>;
+	using difference_type = range_difference_t<_Vp>;
+
+	_Iterator() = default;
+
+	constexpr
+	_Iterator(filter_view& __parent, iterator_t<_Vp> __current)
+	  : _M_current(std::move(__current)),
+	    _M_parent(std::__addressof(__parent))
+	{ }
+
+	constexpr iterator_t<_Vp>
+	base() const &
+	  requires copyable<iterator_t<_Vp>>
+	{ return _M_current; }
+
+	constexpr iterator_t<_Vp>
+	base() &&
+	{ return std::move(_M_current); }
+
+	constexpr range_reference_t<_Vp>
+	operator*() const
+	{ return *_M_current; }
+
+	constexpr iterator_t<_Vp>
+	operator->() const
+	  requires __detail::__has_arrow<iterator_t<_Vp>>
+	    && copyable<iterator_t<_Vp>>
+	{ return _M_current; }
+
+	constexpr _Iterator&
+	operator++()
+	{
+	  _M_current = __detail::find_if(std::move(++_M_current),
+					 ranges::end(_M_parent->_M_base),
+					 std::ref(*_M_parent->_M_pred));
+	  return *this;
+	}
+
+	constexpr void
+	operator++(int)
+	{ ++*this; }
+
+	constexpr _Iterator
+	operator++(int) requires forward_range<_Vp>
+	{
+	  auto __tmp = *this;
+	  ++*this;
+	  return __tmp;
+	}
+
+	constexpr _Iterator&
+	operator--() requires bidirectional_range<_Vp>
+	{
+	  do
+	    --_M_current;
+	  while (!std::__invoke(*_M_parent->_M_pred, *_M_current));
+	  return *this;
+	}
+
+	constexpr _Iterator
+	operator--(int) requires bidirectional_range<_Vp>
+	{
+	  auto __tmp = *this;
+	  --*this;
+	  return __tmp;
+	}
+
+	friend constexpr bool
+	operator==(const _Iterator& __x, const _Iterator& __y)
+	  requires equality_comparable<iterator_t<_Vp>>
+	{ return __x._M_current == __y._M_current; }
+
+	friend constexpr range_rvalue_reference_t<_Vp>
+	iter_move(const _Iterator& __i)
+	  noexcept(noexcept(ranges::iter_move(__i._M_current)))
+	{ return ranges::iter_move(__i._M_current); }
+
+	friend constexpr void
+	iter_swap(const _Iterator& __x, const _Iterator& __y)
+	  noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current)))
+	  requires indirectly_swappable<iterator_t<_Vp>>
+	{ ranges::iter_swap(__x._M_current, __y._M_current); }
+      };
+
+      struct _Sentinel
+      {
+      private:
+	sentinel_t<_Vp> _M_end = sentinel_t<_Vp>();
+
+	constexpr bool
+	__equal(const _Iterator& __i) const
+	{ return __i._M_current == _M_end; }
+
+      public:
+	_Sentinel() = default;
+
+	constexpr explicit
+	_Sentinel(filter_view& __parent)
+	  : _M_end(ranges::end(__parent._M_base))
+	{ }
+
+	constexpr sentinel_t<_Vp>
+	base() const
+	{ return _M_end; }
+
+	friend constexpr bool
+	operator==(const _Iterator& __x, const _Sentinel& __y)
+	{ return __y.__equal(__x); }
+      };
+
+      _Vp _M_base = _Vp();
+      __detail::__box<_Pred> _M_pred;
+
+    public:
+      filter_view() = default;
+
+      constexpr
+      filter_view(_Vp __base, _Pred __pred)
+	: _M_base(std::move(__base)), _M_pred(std::move(__pred))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range>
+	requires viewable_range<_Range>
+	  && constructible_from<_Vp, all_view<_Range>>
+	constexpr
+	filter_view(_Range&& __r, _Pred __pred)
+	  : _M_base(views::all(std::forward<_Range>(__r))),
+	    _M_pred(std::move(__pred))
+	{ }
+      */
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr _Iterator
+      begin()
+      {
+	// XXX: we need to cache the result here as per [range.filter.view]
+	__glibcxx_assert(_M_pred.has_value());
+	return {*this, __detail::find_if(ranges::begin(_M_base),
+					 ranges::end(_M_base),
+					 std::ref(*_M_pred))};
+      }
+
+      constexpr auto
+      end()
+      {
+	if constexpr (common_range<_Vp>)
+	  return _Iterator{*this, ranges::end(_M_base)};
+	else
+	  return _Sentinel{*this};
+      }
+    };
+
+  template<typename _Range, typename _Pred>
+    filter_view(_Range&&, _Pred) -> filter_view<all_view<_Range>, _Pred>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptor filter
+      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
+      {
+	return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+      };
+  } // namespace views
+
+  template<input_range _Vp, copy_constructible _Fp>
+    requires view<_Vp> && is_object_v<_Fp>
+      && regular_invocable<_Fp&, range_reference_t<_Vp>>
+    class transform_view : public view_interface<transform_view<_Vp, _Fp>>
+    {
+    private:
+      template<bool _Const>
+	struct _Sentinel;
+
+      template<bool _Const>
+	struct _Iterator
+	{
+	private:
+	  using _Parent
+	    = conditional_t<_Const, const transform_view, transform_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  static constexpr auto
+	  _S_iter_concept()
+	  {
+	    if constexpr (random_access_range<_Vp>)
+	      return random_access_iterator_tag{};
+	    else if constexpr (bidirectional_range<_Vp>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (forward_range<_Vp>)
+	      return forward_iterator_tag{};
+	    else
+	      return input_iterator_tag{};
+	  }
+
+	  static constexpr auto
+	  _S_iter_cat()
+	  {
+	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    if constexpr (derived_from<_Cat, contiguous_iterator_tag>)
+	      return random_access_iterator_tag{};
+	    else
+	      return _Cat{};
+	  }
+
+	  static constexpr decltype(auto)
+	  __iter_move(const _Iterator& __i = {})
+	    noexcept(noexcept(std::__invoke(*__i._M_parent->_M_fun,
+					    *__i._M_current)))
+	  {
+	    if constexpr (is_lvalue_reference_v<decltype(*__i)>)
+	      return std::move(*__i);
+	    else
+	      return *__i;
+	  }
+
+	  iterator_t<_Base> _M_current = iterator_t<_Base>();
+	  _Parent* _M_parent = nullptr;
+
+	public:
+	  using iterator_concept = decltype(_S_iter_concept());
+	  using iterator_category = decltype(_S_iter_cat());
+	  using value_type
+	    = remove_cvref_t<invoke_result_t<_Fp&, range_reference_t<_Base>>>;
+	  using difference_type = range_difference_t<_Base>;
+
+	  _Iterator() = default;
+
+	  constexpr
+	  _Iterator(_Parent& __parent, iterator_t<_Base> __current)
+	    : _M_current(std::move(__current)),
+	      _M_parent(std::__addressof(__parent))
+	  { }
+
+	  constexpr
+	  _Iterator(_Iterator<!_Const> __i)
+	    requires _Const
+	      && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+	    : _M_current(std::move(__i._M_current)), _M_parent(__i._M_parent)
+	  { }
+
+	  constexpr iterator_t<_Base>
+	  base() const &
+	    requires copyable<iterator_t<_Base>>
+	  { return _M_current; }
+
+	  constexpr iterator_t<_Base>
+	  base() &&
+	  { return std::move(_M_current); }
+
+	  constexpr decltype(auto)
+	  operator*() const
+	  { return std::__invoke(*_M_parent->_M_fun, *_M_current); }
+
+	  constexpr _Iterator&
+	  operator++()
+	  {
+	    ++_M_current;
+	    return *this;
+	  }
+
+	  constexpr void
+	  operator++(int)
+	  { ++_M_current; }
+
+	  constexpr _Iterator
+	  operator++(int) requires forward_range<_Base>
+	  {
+	    auto __tmp = *this;
+	    ++*this;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator--() requires bidirectional_range<_Base>
+	  {
+	    --_M_current;
+	    return *this;
+	  }
+
+	  constexpr _Iterator
+	  operator--(int) requires bidirectional_range<_Base>
+	  {
+	    auto __tmp = *this;
+	    --*this;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator+=(difference_type __n) requires random_access_range<_Base>
+	  {
+	    _M_current += __n;
+	    return *this;
+	  }
+
+	  constexpr _Iterator&
+	  operator-=(difference_type __n) requires random_access_range<_Base>
+	  {
+	    _M_current -= __n;
+	    return *this;
+	  }
+
+	  constexpr decltype(auto)
+	  operator[](difference_type __n) const
+	    requires random_access_range<_Base>
+	  { return std::__invoke(*_M_parent->_M_fun, _M_current[__n]); }
+
+	  friend constexpr bool
+	  operator==(const _Iterator& __x, const _Iterator& __y)
+	    requires equality_comparable<iterator_t<_Base>>
+	  { return __x._M_current == __y._M_current; }
+
+	  friend constexpr bool
+	  operator<(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __x._M_current < __y._M_current; }
+
+	  friend constexpr bool
+	  operator>(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __y < __x; }
+
+	  friend constexpr bool
+	  operator<=(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return !(__y < __x); }
+
+	  friend constexpr bool
+	  operator>=(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return !(__x < __y); }
+
+#ifdef __cpp_lib_three_way_comparison
+	  friend constexpr auto
+	  operator<=>(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	      && three_way_comparable<iterator_t<_Base>>
+	  { return __x._M_current <=> __y._M_current; }
+#endif
+
+	  friend constexpr _Iterator
+	  operator+(_Iterator __i, difference_type __n)
+	    requires random_access_range<_Base>
+	  { return {*__i._M_parent, __i._M_current + __n}; }
+
+	  friend constexpr _Iterator
+	  operator+(difference_type __n, _Iterator __i)
+	    requires random_access_range<_Base>
+	  { return {*__i._M_parent, __i._M_current + __n}; }
+
+	  friend constexpr _Iterator
+	  operator-(_Iterator __i, difference_type __n)
+	    requires random_access_range<_Base>
+	  { return {*__i._M_parent, __i._M_current - __n}; }
+
+	  friend constexpr difference_type
+	  operator-(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __x._M_current - __y._M_current; }
+
+	  friend constexpr decltype(auto)
+	  iter_move(const _Iterator& __i) noexcept(noexcept(__iter_move()))
+	  { return __iter_move(__i); }
+
+	  friend constexpr void
+	  iter_swap(const _Iterator& __x, const _Iterator& __y)
+	    noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current)))
+	    requires indirectly_swappable<iterator_t<_Base>>
+	  { return ranges::iter_swap(__x._M_current, __y._M_current); }
+
+	  friend _Sentinel<_Const>;
+	};
+
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Parent
+	    = conditional_t<_Const, const transform_view, transform_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr range_difference_t<_Base>
+	  __distance_from(const _Iterator<_Const>& __i) const
+	  { return _M_end - __i._M_current; }
+
+	  constexpr bool
+	  __equal(const _Iterator<_Const>& __i) const
+	  { return __i._M_current == _M_end; }
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(sentinel_t<_Base> __end)
+	    : _M_end(__end)
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __i)
+	    requires _Const
+	      && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(std::move(__i._M_end))
+	  { }
+
+	  constexpr sentinel_t<_Base>
+	  base() const
+	  { return _M_end; }
+
+	  friend constexpr bool
+	  operator==(const _Iterator<_Const>& __x, const _Sentinel& __y)
+	  { return __y.__equal(__x); }
+
+	  friend constexpr range_difference_t<_Base>
+	  operator-(const _Iterator<_Const>& __x, const _Sentinel& __y)
+	    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	  { return -__y.__distance_from(__x); }
+
+	  friend constexpr range_difference_t<_Base>
+	  operator-(const _Sentinel& __y, const _Iterator<_Const>& __x)
+	    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	  { return __y.__distance_from(__x); }
+	};
+
+      _Vp _M_base = _Vp();
+      __detail::__box<_Fp> _M_fun;
+
+    public:
+      transform_view() = default;
+
+      constexpr
+      transform_view(_Vp __base, _Fp __fun)
+	: _M_base(std::move(__base)), _M_fun(std::move(__fun))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range>
+	requires viewable_range<_Range>
+	  && constructible_from<_Vp, all_view<_Range>>
+	constexpr
+	transform_view(_Range&& __r, _Fp __fun)
+	  : _M_base(views::all(std::forward<_Range>(__r)))
+	{
+	}
+	*/
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base ; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr _Iterator<false>
+      begin()
+      { return _Iterator<false>{*this, ranges::begin(_M_base)}; }
+
+      constexpr _Iterator<true>
+      begin() const
+	requires range<const _Vp>
+	  && regular_invocable<const _Fp&, range_reference_t<const _Vp>>
+      { return _Iterator<true>{*this, ranges::begin(_M_base)}; }
+
+      constexpr _Sentinel<false>
+      end()
+      { return _Sentinel<false>{ranges::end(_M_base)}; }
+
+      constexpr _Iterator<false>
+      end() requires common_range<_Vp>
+      { return _Iterator<false>{*this, ranges::end(_M_base)}; }
+
+      constexpr _Sentinel<true>
+      end() const
+	requires range<const _Vp>
+	  && regular_invocable<const _Fp&, range_reference_t<const _Vp>>
+      { return _Sentinel<true>{ranges::end(_M_base)}; }
+
+      constexpr _Iterator<true>
+      end() const
+	requires common_range<const _Vp>
+	  && regular_invocable<const _Fp&, range_reference_t<const _Vp>>
+      { return _Iterator<true>{*this, ranges::end(_M_base)}; }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      { return ranges::size(_M_base); }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      { return ranges::size(_M_base); }
+    };
+
+  template<typename _Range, typename _Fp>
+    transform_view(_Range&&, _Fp) -> transform_view<all_view<_Range>, _Fp>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptor transform
+      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
+      {
+	return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
+      };
+  } // namespace views
+
+  template<view _Vp>
+    class take_view : public view_interface<take_view<_Vp>>
+    {
+    private:
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+	  using _CI = counted_iterator<iterator_t<_Base>>;
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(sentinel_t<_Base> __end)
+	    : _M_end(__end)
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __s)
+	    requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(std::move(__s._M_end))
+	  { }
+
+	  constexpr sentinel_t<_Base>
+	  base() const
+	  { return _M_end; }
+
+	  friend constexpr bool operator==(const _CI& __y, const _Sentinel& __x)
+	  { return __y.count() == 0 || __y.base() == __x._M_end; }
+	};
+
+      _Vp _M_base = _Vp();
+      range_difference_t<_Vp> _M_count = 0;
+
+    public:
+      take_view() = default;
+
+      constexpr
+      take_view(_Vp base, range_difference_t<_Vp> __count)
+	: _M_base(std::move(base)), _M_count(std::move(__count))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<viewable_range _Range>
+	requires constructible_from<_Vp, all_view<_Range>>
+      constexpr
+      take_view(_Range&& __r, range_difference_t<_Vp> __count)
+	: _M_base(views::all(std::forward<_Range>(__r))), _M_count(__count)
+      { }
+      */
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin() requires (!__detail::__simple_view<_Vp>)
+      {
+	if constexpr (sized_range<_Vp>)
+	  {
+	    if constexpr (random_access_range<_Vp>)
+	      return ranges::begin(_M_base);
+	    else
+	      return counted_iterator{ranges::begin(_M_base), size()};
+	  }
+	else
+	  return counted_iterator{ranges::begin(_M_base), _M_count};
+      }
+
+      constexpr auto
+      begin() const requires range<const _Vp>
+      {
+	if constexpr (sized_range<const _Vp>)
+	  {
+	    if constexpr (random_access_range<const _Vp>)
+	      return ranges::begin(_M_base);
+	    else
+	      return counted_iterator{ranges::begin(_M_base), size()};
+	  }
+	else
+	  return counted_iterator{ranges::begin(_M_base), _M_count};
+      }
+
+      constexpr auto
+      end() requires (!__detail::__simple_view<_Vp>)
+      {
+	if constexpr (sized_range<_Vp>)
+	  {
+	    if constexpr (random_access_range<_Vp>)
+	      return ranges::begin(_M_base) + size();
+	    else
+	      return default_sentinel;
+	  }
+	else
+	  return _Sentinel<false>{ranges::end(_M_base)};
+      }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      {
+	if constexpr (sized_range<const _Vp>)
+	  {
+	    if constexpr (random_access_range<const _Vp>)
+	      return ranges::begin(_M_base) + size();
+	    else
+	      return default_sentinel;
+	  }
+	else
+	  return _Sentinel<true>{ranges::end(_M_base)};
+      }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      {
+	auto __n = ranges::size(_M_base);
+	return __detail::min(__n, static_cast<decltype(__n)>(_M_count));
+      }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      {
+	auto __n = ranges::size(_M_base);
+	return __detail::min(__n, static_cast<decltype(__n)>(_M_count));
+      }
+    };
+
+  template<range _Range>
+    take_view(_Range&&, range_difference_t<_Range>)
+      -> take_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptor take
+      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
+      {
+	return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+      };
+  } // namespace views
+
+  template<view _Vp, typename _Pred>
+    requires input_range<_Vp> && is_object_v<_Pred>
+      && indirect_unary_predicate<const _Pred, iterator_t<_Vp>>
+    class take_while_view : public view_interface<take_while_view<_Vp, _Pred>>
+    {
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+	  const _Pred* _M_pred = nullptr;
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+	    : _M_end(__end), _M_pred(__pred)
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __s)
+	    requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(__s._M_end), _M_pred(__s._M_pred)
+	  { }
+
+	  constexpr sentinel_t<_Base>
+	  base() const { return _M_end; }
+
+	  friend constexpr bool
+	  operator==(const iterator_t<_Base>& __x, const _Sentinel& __y)
+	  { return __y._M_end == __x || !std::__invoke(*__y._M_pred, *__x); }
+	};
+
+      _Vp _M_base;
+      __detail::__box<_Pred> _M_pred;
+
+    public:
+      take_while_view() = default;
+
+      constexpr
+      take_while_view(_Vp base, _Pred __pred)
+	: _M_base(std::move(base)), _M_pred(std::move(__pred))
+      {
+      }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr const _Pred&
+      pred() const
+      { return *_M_pred; }
+
+      constexpr auto
+      begin() requires (!__detail::__simple_view<_Vp>)
+      { return ranges::begin(_M_base); }
+
+      constexpr auto
+      begin() const requires range<const _Vp>
+      { return ranges::begin(_M_base); }
+
+      constexpr auto
+      end() requires (!__detail::__simple_view<_Vp>)
+      { return _Sentinel<false>(ranges::end(_M_base),
+				std::__addressof(*_M_pred)); }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      { return _Sentinel<true>(ranges::end(_M_base),
+			       std::__addressof(*_M_pred)); }
+    };
+
+  template<typename _Range, typename _Pred>
+    take_while_view(_Range&&, _Pred)
+      -> take_while_view<all_view<_Range>, _Pred>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptor take_while
+      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
+      {
+	return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+      };
+  } // namespace views
+
+  template<view _Vp>
+    class drop_view : public view_interface<drop_view<_Vp>>
+    {
+    private:
+      _Vp _M_base;
+      range_difference_t<_Vp> _M_count;
+
+    public:
+      drop_view() = default;
+
+      constexpr
+      drop_view(_Vp __base, range_difference_t<_Vp> __count)
+	: _M_base(std::move(__base)), _M_count(__count)
+      { __glibcxx_assert(__count >= 0); }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin() requires (!(__detail::__simple_view<_Vp>
+			  && random_access_range<_Vp>))
+      {
+	// XXX: we need to cache the result here as per [range.drop.view]
+	return ranges::next(ranges::begin(_M_base), _M_count,
+			    ranges::end(_M_base));
+      }
+
+      constexpr auto
+      begin() const requires random_access_range<const _Vp>
+      {
+	return ranges::next(ranges::begin(_M_base), _M_count,
+			    ranges::end(_M_base));
+      }
+
+      constexpr auto
+      end() requires (!__detail::__simple_view<_Vp>)
+      { return ranges::end(_M_base); }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      { return ranges::end(_M_base); }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      {
+	const auto __s = ranges::size(_M_base);
+	const auto __c = static_cast<decltype(__s)>(_M_count);
+	return __s < __c ? 0 : __s - __c;
+      }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      {
+	const auto __s = ranges::size(_M_base);
+	const auto __c = static_cast<decltype(__s)>(_M_count);
+	return __s < __c ? 0 : __s - __c;
+      }
+    };
+
+  template<typename _Range>
+    drop_view(_Range&&, range_difference_t<_Range>)
+      -> drop_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptor drop
+      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
+      {
+	return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+      };
+  } // namespace views
+
+  template<view _Vp, typename _Pred>
+    requires input_range<_Vp> && is_object_v<_Pred>
+      && indirect_unary_predicate<const _Pred, iterator_t<_Vp>>
+    class drop_while_view : public view_interface<drop_while_view<_Vp, _Pred>>
+    {
+    private:
+      _Vp _M_base;
+      __detail::__box<_Pred> _M_pred;
+
+    public:
+      drop_while_view() = default;
+
+      constexpr
+      drop_while_view(_Vp __base, _Pred __pred)
+	: _M_base(std::move(__base)), _M_pred(std::move(__pred))
+      { }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr const _Pred&
+      pred() const
+      { return *_M_pred; }
+
+      constexpr auto
+      begin()
+      {
+	// XXX: we need to cache the result here as per [range.drop.while.view]
+	return __detail::find_if_not(ranges::begin(_M_base),
+				     ranges::end(_M_base),
+				     std::cref(*_M_pred));
+      }
+
+      constexpr auto
+      end()
+      { return ranges::end(_M_base); }
+    };
+
+  template<typename _Range, typename _Pred>
+    drop_while_view(_Range&&, _Pred)
+      -> drop_while_view<all_view<_Range>, _Pred>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptor drop_while
+      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
+      {
+	return drop_while_view{std::forward<_Range>(__r),
+			       std::forward<_Pred>(__p)};
+      };
+  } // namespace views
+
+  template<input_range _Vp>
+    requires view<_Vp> && input_range<range_reference_t<_Vp>>
+      && (is_reference_v<range_reference_t<_Vp>>
+	  || view<range_value_t<_Vp>>)
+    class join_view : public view_interface<join_view<_Vp>>
+    {
+    private:
+      using _InnerRange = range_reference_t<_Vp>;
+
+      template<bool _Const>
+	struct _Sentinel;
+
+      template<bool _Const>
+	struct _Iterator
+	{
+	private:
+	  using _Parent = conditional_t<_Const, const join_view, join_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  static constexpr bool _S_ref_is_glvalue
+	    = is_reference_v<range_reference_t<_Base>>;
+
+	  constexpr void
+	  _M_satisfy()
+	  {
+	    auto __update_inner = [this] (range_reference_t<_Base> __x) -> auto&
+	    {
+	      if constexpr (_S_ref_is_glvalue)
+		return __x;
+	      else
+		return (_M_parent->_M_inner = views::all(std::move(__x)));
+	    };
+
+	    for (; _M_outer != ranges::end(_M_parent->_M_base); ++_M_outer)
+	      {
+		auto& inner = __update_inner(*_M_outer);
+		_M_inner = ranges::begin(inner);
+		if (_M_inner != ranges::end(inner))
+		  return;
+	      }
+
+	    if constexpr (_S_ref_is_glvalue)
+	      _M_inner = iterator_t<range_reference_t<_Base>>();
+	  }
+
+	  static constexpr auto
+	  _S_iter_concept()
+	  {
+	    if constexpr (_S_ref_is_glvalue
+			  && bidirectional_range<_Base>
+			  && bidirectional_range<range_reference_t<_Base>>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (_S_ref_is_glvalue
+			       && forward_range<_Base>
+			       && forward_range<range_reference_t<_Base>>)
+	      return forward_iterator_tag{};
+	    else
+	      return input_iterator_tag{};
+	  }
+
+	  static constexpr auto
+	  _S_iter_cat()
+	  {
+	    using _OuterCat
+	      = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    using _InnerCat
+	      = iterator_traits<iterator_t<range_reference_t<_Base>>>
+		 ::iterator_category;
+	    if constexpr (_S_ref_is_glvalue
+			  && derived_from<_OuterCat, bidirectional_iterator_tag>
+			  && derived_from<_InnerCat, bidirectional_iterator_tag>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (_S_ref_is_glvalue
+			       && derived_from<_OuterCat, forward_iterator_tag>
+			       && derived_from<_InnerCat, forward_iterator_tag>)
+	      return forward_iterator_tag{};
+	    else if constexpr (derived_from<_OuterCat, input_iterator_tag>
+			       && derived_from<_InnerCat, input_iterator_tag>)
+	      return input_iterator_tag{};
+	    else
+	      return output_iterator_tag{};
+	  }
+
+	  iterator_t<_Base> _M_outer = iterator_t<_Base>();
+	  iterator_t<range_reference_t<_Base>> _M_inner
+	    = iterator_t<range_reference_t<_Base>>();
+	  _Parent* _M_parent = nullptr;
+
+	public:
+	  using iterator_concept = decltype(_S_iter_concept());
+	  using iterator_category = decltype(_S_iter_cat());
+	  using value_type = range_value_t<range_reference_t<_Base>>;
+	  using difference_type
+	    = common_type_t<range_difference_t<_Base>,
+			    range_difference_t<range_reference_t<_Base>>>;
+
+	  _Iterator() = default;
+
+	  // XXX: had to change the type of __outer from iterator_t<_Vp> to
+	  // iterator_t<_Base> here, a possible defect in the spec?
+	  constexpr
+	  _Iterator(_Parent& __parent, iterator_t<_Base> __outer)
+	    : _M_outer(std::move(__outer)),
+	      _M_parent(std::__addressof(__parent))
+	  { _M_satisfy(); }
+
+	  constexpr
+	  _Iterator(_Iterator<!_Const> __i)
+	    requires _Const
+	      && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+	      && convertible_to<iterator_t<_InnerRange>,
+				iterator_t<range_reference_t<_Base>>>
+	    : _M_outer(std::move(__i._M_outer)), _M_inner(__i._M_inner),
+	      _M_parent(__i._M_parent)
+	  { }
+
+	  constexpr decltype(auto)
+	  operator*() const
+	  { return *_M_inner; }
+
+	  constexpr iterator_t<_Base>
+	  operator->() const
+	    requires __detail::__has_arrow<iterator_t<_Base>>
+	      && copyable<iterator_t<_Base>>
+	  { return _M_inner; }
+
+	  constexpr _Iterator&
+	  operator++()
+	  {
+	    auto&& __inner_range = [this] () -> decltype(auto) {
+	      if constexpr (_S_ref_is_glvalue)
+		return *_M_outer;
+	      else
+		return _M_parent->_M_inner;
+	    }();
+	    if (++_M_inner == ranges::end(__inner_range))
+	      {
+		++_M_outer;
+		_M_satisfy();
+	      }
+	    return *this;
+	  }
+
+	  constexpr void
+	  operator++(int)
+	  { ++*this; }
+
+	  constexpr _Iterator
+	  operator++(int)
+	    requires _S_ref_is_glvalue && forward_range<_Base>
+	      && forward_range<range_reference_t<_Base>>
+	  {
+	    auto __tmp = *this;
+	    ++*this;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator--()
+	    requires _S_ref_is_glvalue && bidirectional_range<_Base>
+	      && bidirectional_range<range_reference_t<_Base>>
+	  {
+	    if (_M_outer == ranges::end(_M_parent->_M_base))
+	      _M_inner = ranges::end(*--_M_outer);
+	    while (_M_inner == ranges::begin(*_M_outer))
+	      _M_inner = ranges::end(*--_M_outer);
+	    --_M_inner;
+	    return *this;
+	  }
+
+	  constexpr _Iterator
+	  operator--(int)
+	    requires _S_ref_is_glvalue && bidirectional_range<_Base>
+	      && bidirectional_range<range_reference_t<_Base>>
+	  {
+	    auto __tmp = *this;
+	    --*this;
+	    return __tmp;
+	  }
+
+	  friend constexpr bool
+	  operator==(const _Iterator& __x, const _Iterator& __y)
+	    requires _S_ref_is_glvalue
+	      && equality_comparable<iterator_t<_Base>>
+	      && equality_comparable<iterator_t<range_reference_t<_Base>>>
+	  {
+	    return (__x._M_outer == __y._M_outer
+		    && __x._M_inner == __y._M_inner);
+	  }
+
+	  friend constexpr decltype(auto)
+	  iter_move(const _Iterator& __i)
+	  noexcept(noexcept(ranges::iter_move(__i._M_inner)))
+	  { return ranges::iter_move(__i._M_inner); }
+
+	  friend constexpr void
+	  iter_swap(const _Iterator& __x, const _Iterator& __y)
+	    noexcept(noexcept(ranges::iter_swap(__x._M_inner, __y._M_inner)))
+	  { return ranges::iter_swap(__x._M_inner, __y._M_inner); }
+
+	  friend _Sentinel<_Const>;
+	};
+
+      template<bool _Const>
+	struct _Sentinel
+	{
+	private:
+	  using _Parent = conditional_t<_Const, const join_view, join_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr bool
+	  __equal(const _Iterator<_Const>& __i) const
+	  { return __i._M_outer == _M_end; }
+
+	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
+
+	public:
+	  _Sentinel() = default;
+
+	  constexpr explicit
+	  _Sentinel(_Parent& __parent)
+	    : _M_end(ranges::end(__parent._M_base))
+	  { }
+
+	  constexpr
+	  _Sentinel(_Sentinel<!_Const> __s)
+	    requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+	    : _M_end(std::move(__s._M_end))
+	  { }
+
+	  friend constexpr bool
+	  operator==(const _Iterator<_Const>& __x, const _Sentinel& __y)
+	  { return __y.__equal(__x); }
+	};
+
+      _Vp _M_base = _Vp();
+
+      // XXX: _M_inner is "present only when !is_reference_v<_InnerRange>"
+      // Applied P3278 and made this field mutable.
+      [[no_unique_address]] mutable
+	conditional_t<!is_reference_v<_InnerRange>,
+		      all_view<_InnerRange>, __detail::_Empty> _M_inner;
+
+    public:
+      join_view() = default;
+
+      constexpr explicit
+      join_view(_Vp __base)
+	: _M_base(std::move(__base))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range>
+	requires viewable_range<_Range>
+	  && constructible_from<_Vp, all_view<_Range>>
+      constexpr explicit
+      join_view(_Range&& __r)
+	: _M_base(views::all(std::forward<_Range>(__r)))
+      { }
+      */
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin()
+      {
+	return _Iterator<__detail::__simple_view<_Vp>>{*this,
+						       ranges::begin(_M_base)};
+      }
+
+      constexpr auto
+      begin() const
+	requires input_range<const _Vp>
+	  && is_reference_v<range_reference_t<const _Vp>>
+      {
+	return _Iterator<true>{*this, ranges::begin(_M_base)};
+      }
+
+      constexpr auto
+      end()
+      {
+	if constexpr (forward_range<_Vp> && is_reference_v<_InnerRange>
+		      && forward_range<_InnerRange>
+		      && common_range<_Vp> && common_range<_InnerRange>)
+	  return _Iterator<__detail::__simple_view<_Vp>>{*this,
+							 ranges::end(_M_base)};
+	else
+	  return _Sentinel<__detail::__simple_view<_Vp>>{*this};
+      }
+
+      constexpr auto
+      end() const
+	requires input_range<const _Vp>
+	  && is_reference_v<range_reference_t<const _Vp>>
+      {
+	if constexpr (forward_range<const _Vp>
+		      && is_reference_v<range_reference_t<const _Vp>>
+		      && forward_range<range_reference_t<const _Vp>>
+		      && common_range<const _Vp>
+		      && common_range<range_reference_t<const _Vp>>)
+	  return _Iterator<true>{*this, ranges::end(_M_base)};
+	else
+	  return _Sentinel<true>{*this};
+      }
+    };
+
+  template<typename _Range>
+    explicit join_view(_Range&&) -> join_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptorClosure join
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	return join_view{std::forward<_Range>(__r)};
+      };
+  } // namespace views
+
+  namespace __detail
+  {
+    template<auto>
+      struct __require_constant;
+
+    template<typename _Range>
+      concept __tiny_range = sized_range<_Range>
+	&& requires
+	   { typename __require_constant<remove_reference_t<_Range>::size()>; }
+	&& (remove_reference_t<_Range>::size() <= 1);
+  }
+
+  template<input_range _Vp, forward_range _Pattern>
+    requires view<_Vp> && view<_Pattern>
+      && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
+			       ranges::equal_to>
+      && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>)
+    class split_view : public view_interface<split_view<_Vp, _Pattern>>
+    {
+    private:
+      template<bool _Const>
+	struct _InnerIter;
+
+      template<bool _Const>
+	struct _OuterIter
+	{
+	private:
+	  using _Parent = conditional_t<_Const, const split_view, split_view>;
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr bool
+	  __at_end() const
+	  { return _M_current == ranges::end(_M_parent->_M_base); }
+
+	  // XXX: [24.7.11.3.1]
+	  //  Many of the following specifications refer to the notional member
+	  //  current of outer-iterator.  current is equivalent to current_­ if
+	  //  V models forward_range, and parent_->current_­ otherwise.
+	  constexpr auto&
+	  __current()
+	  {
+	    if constexpr (forward_range<_Vp>)
+	      return _M_current;
+	    else
+	      return _M_parent->_M_current;
+	  }
+
+	  constexpr auto&
+	  __current() const
+	  {
+	    if constexpr (forward_range<_Vp>)
+	      return _M_current;
+	    else
+	      return _M_parent->_M_current;
+	  }
+
+	  _Parent* _M_parent = nullptr;
+
+	  // XXX: _M_current is present only if "V models forward_range"
+	  [[no_unique_address]]
+	    conditional_t<forward_range<_Vp>,
+			  iterator_t<_Base>, __detail::_Empty> _M_current;
+
+	public:
+	  using iterator_concept = conditional_t<forward_range<_Base>,
+						 forward_iterator_tag,
+						 input_iterator_tag>;
+	  using iterator_category = input_iterator_tag;
+	  using difference_type = range_difference_t<_Base>;
+
+	  struct value_type : view_interface<value_type>
+	  {
+	  private:
+	    _OuterIter _M_i = _OuterIter();
+
+	  public:
+	    value_type() = default;
+
+	    constexpr explicit
+	    value_type(_OuterIter __i)
+	      : _M_i(std::move(__i))
+	    { }
+
+	    constexpr _InnerIter<_Const>
+	    begin() const
+	      requires copyable<_OuterIter>
+	    { return _InnerIter<_Const>{_M_i}; }
+
+	    constexpr _InnerIter<_Const>
+	    begin()
+	      requires (!copyable<_OuterIter>)
+	    { return _InnerIter<_Const>{std::move(_M_i)}; }
+
+	    constexpr default_sentinel_t
+	    end() const
+	    { return default_sentinel; }
+	  };
+
+	  _OuterIter() = default;
+
+	  constexpr explicit
+	  _OuterIter(_Parent& __parent) requires (!forward_range<_Base>)
+	    : _M_parent(address(__parent))
+	  { }
+
+	  constexpr
+	  _OuterIter(_Parent& __parent, iterator_t<_Base> __current)
+	    requires forward_range<_Base>
+	    : _M_parent(std::__addressof(__parent)),
+	      _M_current(std::move(__current))
+	  { }
+
+	  constexpr
+	  _OuterIter(_OuterIter<!_Const> __i)
+	    requires _Const
+	      && convertible_to<iterator_t<_Vp>, iterator_t<const _Vp>>
+	    : _M_parent(__i._M_parent), _M_current(std::move(__i._M_current))
+	  { }
+
+	  constexpr value_type
+	  operator*() const
+	  { return value_type{*this}; }
+
+	  constexpr _OuterIter&
+	  operator++()
+	  {
+	    const auto __end = ranges::end(_M_parent->_M_base);
+	    if (_M_current == __end)
+	      return *this;
+	    const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern};
+	    if (__pbegin == __pend)
+	      ++_M_current;
+	    else
+	      do
+		{
+		  auto [__b, __p]
+		    = __detail::mismatch(std::move(_M_current), __end,
+					 __pbegin, __pend);
+		  _M_current = std::move(__b);
+		  if (__p == __pend)
+		    break;
+		} while (++_M_current != __end);
+	    return *this;
+	  }
+
+	  constexpr decltype(auto)
+	  operator++(int)
+	  {
+	    if constexpr (forward_range<_Base>)
+	      {
+		auto __tmp = *this;
+		++*this;
+		return __tmp;
+	      }
+	    else
+	      ++*this;
+	  }
+
+	  friend constexpr bool
+	  operator==(const _OuterIter& __x, const _OuterIter& __y)
+	    requires forward_range<_Base>
+	  { return __x._M_current == __y._M_current; }
+
+	  friend constexpr bool
+	  operator==(const _OuterIter& __x, default_sentinel_t)
+	  { return __x.__at_end(); };
+
+	  friend _InnerIter<_Const>;
+	};
+
+      template<bool _Const>
+	struct _InnerIter
+	{
+	private:
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  constexpr bool
+	  __at_end() const
+	  {
+	    auto [__pcur, __pend] = subrange{_M_i._M_parent->_M_pattern};
+	    auto __end = ranges::end(_M_i._M_parent->_M_base);
+	    if constexpr (__detail::__tiny_range<_Pattern>)
+	      {
+		const auto& __cur = _M_i.__current();
+		if (__cur == __end)
+		  return true;
+		if (__pcur == __pend)
+		  return _M_incremented;
+		return *__cur == *__pcur;
+	      }
+	    else
+	      {
+		auto __cur = _M_i.__current();
+		if (__cur == __end)
+		  return true;
+		if (__pcur == __pend)
+		  return _M_incremented;
+		do
+		  {
+		    if (*__cur != *__pcur)
+		      return false;
+		    if (++__pcur == __pend)
+		      return true;
+		  } while (++__cur != __end);
+		return false;
+	      }
+	  }
+
+	  static constexpr auto
+	  _S_iter_cat()
+	  {
+	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    if constexpr (derived_from<_Cat, forward_iterator_tag>)
+	      return forward_iterator_tag{};
+	    else
+	      return _Cat{};
+	  }
+
+	  static constexpr decltype(auto)
+	  __iter_move(const _InnerIter& __i = {})
+	  noexcept(noexcept(ranges::iter_move(__i._M_i.__current())))
+	  { return ranges::iter_move(__i._M_i.__current()); }
+
+	  static constexpr void
+	  __iter_swap(const _InnerIter& __x = {}, const _InnerIter& __y = {})
+	    noexcept(noexcept(ranges::iter_swap(__x._M_i.__current(),
+						__y._M_i.__current())))
+	  { ranges::iter_swap(__x._M_i.__current(), __y._M_i.__current()); }
+
+	  _OuterIter<_Const> _M_i = _OuterIter<_Const>();
+	  bool _M_incremented = false;
+
+	public:
+	  using iterator_concept = typename _OuterIter<_Const>::iterator_concept;
+	  using iterator_category = decltype(_S_iter_cat());
+	  using value_type = range_value_t<_Base>;
+	  using difference_type = range_difference_t<_Base>;
+
+	  _InnerIter() = default;
+
+	  constexpr explicit
+	  _InnerIter(_OuterIter<_Const> __i)
+	    : _M_i(std::move(__i))
+	  { }
+
+	  constexpr decltype(auto)
+	  operator*() const
+	  { return *_M_i._M_current; }
+
+	  constexpr _InnerIter&
+	  operator++()
+	  {
+	    _M_incremented = true;
+	    if constexpr (!forward_range<_Base>)
+	      if constexpr (_Pattern::size() == 0)
+		return *this;
+	    ++_M_i.__current();
+	    return *this;
+	  }
+
+	  constexpr decltype(auto)
+	  operator++(int)
+	  {
+	    if constexpr (forward_range<_Vp>)
+	      {
+		auto __tmp = *this;
+		++*this;
+		return __tmp;
+	      }
+	    else
+	      ++*this;
+	  }
+
+	  friend constexpr bool
+	  operator==(const _InnerIter& __x, const _InnerIter& __y)
+	    requires forward_range<_Base>
+	  { return __x._M_i.__current() == __y._M_i.__current(); }
+
+	  friend constexpr bool
+	  operator==(const _InnerIter& __x, default_sentinel_t)
+	  { return __x.__at_end(); }
+
+	  friend constexpr decltype(auto)
+	  iter_move(const _InnerIter& __i) noexcept(noexcept(__iter_move()))
+	  { return __iter_move(__i); }
+
+	  friend constexpr void
+	  iter_swap(const _InnerIter& __x, const _InnerIter& __y)
+	    noexcept(noexcept(__iter_swap()))
+	    requires indirectly_swappable<iterator_t<_Base>>
+	  { __iter_swap(__x, __y); }
+	};
+
+      _Vp _M_base = _Vp();
+      _Pattern _M_pattern = _Pattern();
+
+      // XXX: _M_current is "present only if !forward_range<V>"
+      [[no_unique_address]]
+	conditional_t<!forward_range<_Vp>,
+		      iterator_t<_Vp>, __detail::_Empty> _M_current;
+
+
+    public:
+      split_view() = default;
+
+      constexpr
+      split_view(_Vp __base, _Pattern __pattern)
+	: _M_base(std::move(__base)), _M_pattern(std::move(__pattern))
+      { }
+
+      /* XXX: P3280 removes this constructor
+      template<input_range _Range, forward_range _Pred>
+	requires constructible_from<_Vp, all_view<_Range>>
+	  && constructible_from<_Pattern, all_view<_Pred>>
+	constexpr
+	split_view(_Range&& __r, _Pred&& __p)
+	  : _M_base(views::all(std::forward<_Range>(__r))),
+	    _M_pattern(views::all(std::forward<_Pred>(__p)))
+      { }
+      */
+
+      template<input_range _Range>
+	requires constructible_from<_Vp, all_view<_Range>>
+	  && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
+	constexpr
+	split_view(_Range&& __r, range_value_t<_Range> __e)
+	  : _M_base(views::all(std::forward<_Range>(__r))),
+	    _M_pattern(std::move(__e))
+	{ }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin()
+      {
+	if constexpr (forward_range<_Vp>)
+	  return _OuterIter<__detail::__simple_view<_Vp>>{*this,
+							ranges::begin(_M_base)};
+	else
+	  {
+	    _M_current = ranges::begin(_M_base);
+	    return _OuterIter<false>{*this};
+	  }
+      }
+
+      constexpr auto
+      begin() const requires forward_range<_Vp> && forward_range<const _Vp>
+      {
+	return _OuterIter<true>{*this, ranges::begin(_M_base)};
+      }
+
+      constexpr auto
+      end() requires forward_range<_Vp> && common_range<_Vp>
+      {
+	return _OuterIter<__detail::__simple_view<_Vp>>{*this, ranges::end(_M_base)};
+      }
+
+      constexpr auto
+      end() const
+      {
+	if constexpr (forward_range<_Vp>
+		      && forward_range<const _Vp>
+		      && common_range<const _Vp>)
+	  return _OuterIter<true>{*this, ranges::end(_M_base)};
+	else
+	  return default_sentinel;
+      }
+    };
+
+  template<typename _Range, typename _Pred>
+    split_view(_Range&&, _Pred&&)
+      -> split_view<all_view<_Range>, all_view<_Pred>>;
+
+  template<input_range _Range>
+    split_view(_Range&&, range_value_t<_Range>)
+      -> split_view<all_view<_Range>, single_view<range_value_t<_Range>>>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptor split
+      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
+      {
+	return split_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
+      };
+  } // namespace views
+
+  namespace views
+  {
+    struct _Counted
+    {
+      template<input_or_output_iterator _Iter>
+      constexpr auto
+      operator()(_Iter __i, iter_difference_t<_Iter> __n) const
+      {
+	if constexpr (random_access_iterator<_Iter>)
+	  return subrange{__i, __i + __n};
+	else
+	  return subrange{counted_iterator{std::move(__i), __n},
+			  default_sentinel};
+      }
+    };
+
+    inline constexpr _Counted counted{};
+  } // namespace views
+
+  template<view _Vp>
+    requires (!common_range<_Vp>) && copyable<iterator_t<_Vp>>
+    class common_view : public view_interface<common_view<_Vp>>
+    {
+    private:
+      _Vp _M_base = _Vp();
+
+    public:
+      common_view() = default;
+
+      constexpr explicit
+      common_view(_Vp __r)
+	: _M_base(std::move(__r))
+      { }
+
+      /* XXX: P3280 doesn't remove this constructor, but I think it should?
+      template<viewable_range _Range>
+	requires (!common_range<_Range>) && constructible_from<_Vp, all_view<_Range>>
+	constexpr explicit
+	common_view(_Range&& __r)
+	  : _M_base(views::all(std::forward<_Range>(__r)))
+	{ }
+	*/
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin()
+      {
+	if constexpr (random_access_range<_Vp> && sized_range<_Vp>)
+	  return ranges::begin(_M_base);
+	else
+	  return common_iterator<iterator_t<_Vp>, sentinel_t<_Vp>>
+		  (ranges::begin(_M_base));
+      }
+
+      constexpr auto
+      begin() const requires range<const _Vp>
+      {
+	if constexpr (random_access_range<const _Vp> && sized_range<const _Vp>)
+	  return ranges::begin(_M_base);
+	else
+	  return common_iterator<iterator_t<const _Vp>, sentinel_t<const _Vp>>
+		  (ranges::begin(_M_base));
+      }
+
+      constexpr auto
+      end()
+      {
+	if constexpr (random_access_range<_Vp> && sized_range<_Vp>)
+	  return ranges::begin(_M_base) + ranges::size(_M_base);
+	else
+	  return common_iterator<iterator_t<_Vp>, sentinel_t<_Vp>>
+		  (ranges::end(_M_base));
+      }
+
+      constexpr auto
+      end() const requires range<const _Vp>
+      {
+	if constexpr (random_access_range<const _Vp> && sized_range<const _Vp>)
+	  return ranges::begin(_M_base) + ranges::size(_M_base);
+	else
+	  return common_iterator<iterator_t<const _Vp>, sentinel_t<const _Vp>>
+		  (ranges::end(_M_base));
+      }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      { return ranges::size(_M_base); }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      { return ranges::size(_M_base); }
+    };
+
+  template<typename _Range>
+    common_view(_Range&&) -> common_view<all_view<_Range>>;
+
+  namespace views
+  {
+    inline constexpr __adaptor::_RangeAdaptorClosure common
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	if constexpr (common_range<_Range>
+		      && requires { views::all(std::forward<_Range>(__r)); })
+	  return views::all(std::forward<_Range>(__r));
+	else
+	  return common_view{std::forward<_Range>(__r)};
+      };
+
+  } // namespace views
+
+  template<view _Vp>
+    requires bidirectional_range<_Vp>
+    class reverse_view : public view_interface<reverse_view<_Vp>>
+    {
+    private:
+      _Vp _M_base = _Vp();
+
+    public:
+      reverse_view() = default;
+
+      constexpr explicit
+      reverse_view(_Vp __r)
+	: _M_base(std::move(__r))
+	{ }
+
+      /* XXX: P3280 removes this constructor
+      template<viewable_range _Range>
+	requires bidirectional_range<_Range> && constructible_from<_Vp, all_view<_Range>>
+	constexpr explicit
+	reverse_view(_Range&& __r)
+	  : _M_base(views::all(std::forward<_Range>(__r)))
+	{ }
+	*/
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr reverse_iterator<iterator_t<_Vp>>
+      begin()
+      {
+	// XXX: we need to cache the result here as per [range.reverse.view]
+	return make_reverse_iterator(ranges::next(ranges::begin(_M_base),
+						  ranges::end(_M_base)));
+      }
+
+      constexpr auto
+      begin() requires common_range<_Vp>
+      { return make_reverse_iterator(ranges::end(_M_base)); }
+
+      constexpr auto
+      begin() const requires common_range<const _Vp>
+      { return make_reverse_iterator(ranges::end(_M_base)); }
+
+      constexpr reverse_iterator<iterator_t<_Vp>>
+      end()
+      { return make_reverse_iterator(ranges::begin(_M_base)); }
+
+      constexpr auto
+      end() const requires common_range<const _Vp>
+      { return make_reverse_iterator(ranges::begin(_M_base)); }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      { return ranges::size(_M_base); }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      { return ranges::size(_M_base); }
+    };
+
+  template<typename _Range>
+    reverse_view(_Range&&) -> reverse_view<all_view<_Range>>;
+
+  namespace views
+  {
+    namespace __detail
+    {
+      template<typename>
+	inline constexpr bool __is_reversible_subrange = false;
+
+      template<typename _Iter, subrange_kind _Kind>
+	inline constexpr bool
+	  __is_reversible_subrange<subrange<reverse_iterator<_Iter>,
+					    reverse_iterator<_Iter>,
+					    _Kind>> = true;
+
+      template<typename>
+	inline constexpr bool __is_reverse_view = false;
+
+      template<typename _Vp>
+	inline constexpr bool __is_reverse_view<reverse_view<_Vp>> = true;
+    }
+
+    inline constexpr __adaptor::_RangeAdaptorClosure reverse
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	using _Tp = remove_cvref_t<_Range>;
+	if constexpr (__detail::__is_reverse_view<_Tp>)
+	  return std::forward<_Range>(__r).base();
+	else if constexpr (__detail::__is_reversible_subrange<_Tp>)
+	  {
+	    using _Iter = decltype(ranges::begin(__r).base());
+	    if constexpr (sized_range<_Tp>)
+	      return subrange<_Iter, _Iter, subrange_kind::sized>
+		      (__r.end().base(), __r.begin().base(), __r.size());
+	    else
+	      return subrange<_Iter, _Iter, subrange_kind::unsized>
+		      (__r.end().base(), __r.begin().base());
+	  }
+	else
+	  return reverse_view{std::forward<_Range>(__r)};
+      };
+  } // namespace views
+
+  namespace __detail
+  {
+    template<typename _Tp, size_t _Nm>
+    concept __has_tuple_element = requires(_Tp __t)
+      {
+	typename tuple_size<_Tp>::type;
+	requires _Nm < tuple_size_v<_Tp>;
+	typename tuple_element_t<_Nm, _Tp>;
+	// XXX: we applied P3323 here
+	{ std::get<_Nm>(__t) }
+	  -> convertible_to<const tuple_element_t<_Nm, _Tp>&>;
+      };
+  }
+
+  template<input_range _Vp, size_t _Nm>
+    requires view<_Vp>
+      && __detail::__has_tuple_element<range_value_t<_Vp>, _Nm>
+      && __detail::__has_tuple_element<remove_reference_t<range_reference_t<_Vp>>,
+				       _Nm>
+    class elements_view : public view_interface<elements_view<_Vp, _Nm>>
+    {
+    public:
+      elements_view() = default;
+
+      constexpr explicit
+      elements_view(_Vp base)
+	: _M_base(std::move(base))
+      { }
+
+      constexpr _Vp
+      base() const& requires copy_constructible<_Vp>
+      { return _M_base; }
+
+      constexpr _Vp
+      base() &&
+      { return std::move(_M_base); }
+
+      constexpr auto
+      begin() requires (!__detail::__simple_view<_Vp>)
+      { return _Iterator<false>(ranges::begin(_M_base)); }
+
+      constexpr auto
+      begin() const requires __detail::__simple_view<_Vp>
+      { return _Iterator<true>(ranges::begin(_M_base)); }
+
+      constexpr auto
+      end() requires (!__detail::__simple_view<_Vp>)
+      { return ranges::end(_M_base); }
+
+      constexpr auto
+      end() const requires __detail::__simple_view<_Vp>
+      { return ranges::end(_M_base); }
+
+      constexpr auto
+      size() requires sized_range<_Vp>
+      { return ranges::size(_M_base); }
+
+      constexpr auto
+      size() const requires sized_range<const _Vp>
+      { return ranges::size(_M_base); }
+
+    private:
+      template<bool _Const>
+	struct _Iterator
+	{
+	  using _Base = conditional_t<_Const, const _Vp, _Vp>;
+
+	  iterator_t<_Base> _M_current;
+
+	  friend _Iterator<!_Const>;
+
+	public:
+	  using iterator_category
+	    = typename iterator_traits<iterator_t<_Base>>::iterator_category;
+	  using value_type
+	    = remove_cvref_t<tuple_element_t<_Nm, range_value_t<_Base>>>;
+	  using difference_type = range_difference_t<_Base>;
+
+	  _Iterator() = default;
+
+	  constexpr explicit
+	  _Iterator(iterator_t<_Base> current)
+	    : _M_current(std::move(current))
+	  { }
+
+	  constexpr
+	  _Iterator(_Iterator<!_Const> i)
+	    requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+	    : _M_current(std::move(i._M_current))
+	  { }
+
+	  constexpr iterator_t<_Base>
+	  base() const&
+	    requires copyable<iterator_t<_Base>>
+	  { return _M_current; }
+
+	  constexpr iterator_t<_Base>
+	  base() &&
+	  { return std::move(_M_current); }
+
+	  constexpr decltype(auto)
+	    operator*() const
+	  { return std::get<_Nm>(*_M_current); }
+
+	  constexpr _Iterator&
+	  operator++()
+	  {
+	    ++_M_current;
+	    return *this;
+	  }
+
+	  constexpr void
+	  operator++(int) requires (!forward_range<_Base>)
+	  { ++_M_current; }
+
+	  constexpr _Iterator
+	  operator++(int) requires forward_range<_Base>
+	  {
+	    auto __tmp = *this;
+	    ++_M_current;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator--() requires bidirectional_range<_Base>
+	  {
+	    --_M_current;
+	    return *this;
+	  }
+
+	  constexpr _Iterator
+	  operator--(int) requires bidirectional_range<_Base>
+	  {
+	    auto __tmp = *this;
+	    --_M_current;
+	    return __tmp;
+	  }
+
+	  constexpr _Iterator&
+	  operator+=(difference_type __n)
+	    requires random_access_range<_Base>
+	  {
+	    _M_current += __n;
+	    return *this;
+	  }
+
+	  constexpr _Iterator&
+	  operator-=(difference_type __n)
+	    requires random_access_range<_Base>
+	  {
+	    _M_current -= __n;
+	    return *this;
+	  }
+
+	  constexpr decltype(auto)
+	  operator[](difference_type __n) const
+	    requires random_access_range<_Base>
+	  { return std::get<_Nm>(*(_M_current + __n)); }
+
+	  friend constexpr bool
+	  operator==(const _Iterator& __x, const _Iterator& __y)
+	    requires equality_comparable<iterator_t<_Base>>
+	  { return __x._M_current == __y._M_current; }
+
+	  friend constexpr bool
+	  operator==(const _Iterator& __x, const sentinel_t<_Base>& __y)
+	  { return __x._M_current == __y; }
+
+	  friend constexpr bool
+	  operator<(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __x._M_current < __y._M_current; }
+
+	  friend constexpr bool
+	  operator>(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __y._M_current < __x._M_current; }
+
+	  friend constexpr bool
+	  operator<=(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return !(__y._M_current > __x._M_current); }
+
+	  friend constexpr bool
+	  operator>=(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return !(__x._M_current > __y._M_current); }
+
+#ifdef __cpp_lib_three_way_comparison
+	  friend constexpr auto
+	  operator<=>(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	      && three_way_comparable<iterator_t<_Base>>
+	  { return __x._M_current <=> __y._M_current; }
+#endif
+
+	  friend constexpr _Iterator
+	  operator+(const _Iterator& __x, difference_type __y)
+	    requires random_access_range<_Base>
+	  { return _Iterator{__x} += __y; }
+
+	  friend constexpr _Iterator
+	  operator+(difference_type __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __y + __x; }
+
+	  friend constexpr _Iterator
+	  operator-(const _Iterator& __x, difference_type __y)
+	    requires random_access_range<_Base>
+	  { return _Iterator{__x} -= __y; }
+
+	  friend constexpr difference_type
+	  operator-(const _Iterator& __x, const _Iterator& __y)
+	    requires random_access_range<_Base>
+	  { return __x._M_current - __y._M_current; }
+
+	  friend constexpr difference_type
+	  operator-(const _Iterator<_Const>& __x, const sentinel_t<_Base>& __y)
+	    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	  { return __x._M_current - __y; }
+
+	  friend constexpr difference_type
+	  operator-(const sentinel_t<_Base>& __x, const _Iterator<_Const>& __y)
+	    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+	  { return -(__y - __x); }
+	};
+
+      _Vp _M_base = _Vp();
+    };
+
+  template<typename _Range>
+    using keys_view = elements_view<all_view<_Range>, 0>;
+
+  template<typename _Range>
+    using values_view = elements_view<all_view<_Range>, 1>;
+
+  namespace views
+  {
+    template<size_t _Nm>
+    inline constexpr __adaptor::_RangeAdaptorClosure elements
+      = [] <viewable_range _Range> (_Range&& __r)
+      {
+	return elements_view<all_view<_Range>, _Nm>{std::forward<_Range>(__r)};
+      };
+
+    inline constexpr __adaptor::_RangeAdaptorClosure keys = elements<0>;
+    inline constexpr __adaptor::_RangeAdaptorClosure values = elements<1>;
+  } // namespace views
+
+} // namespace ranges
+
+  namespace views = ranges::views;
+
+  template<typename _Iter, typename _Sent, ranges::subrange_kind _Kind>
+    struct tuple_size<ranges::subrange<_Iter, _Sent, _Kind>>
+    : integral_constant<size_t, 2>
+    { };
+
+  template<typename _Iter, typename _Sent, ranges::subrange_kind _Kind>
+    struct tuple_element<0, ranges::subrange<_Iter, _Sent, _Kind>>
+    { using type = _Iter; };
+
+  template<typename _Iter, typename _Sent, ranges::subrange_kind _Kind>
+    struct tuple_element<1, ranges::subrange<_Iter, _Sent, _Kind>>
+    { using type = _Sent; };
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 #endif // library concepts
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
new file mode 100644
index 00000000000..577ce7460e6
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
@@ -0,0 +1,122 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto v = views::all(x);
+
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+
+  VERIFY( ranges::size(v) == 5 );
+  VERIFY( ranges::size(x | views::all) == 5 );
+  VERIFY( ranges::size(v | views::all | views::all) == 5 );
+  VERIFY( ranges::size(v | (views::all | views::all)) == 5 );
+
+  ranges::reverse(v);
+  VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) );
+}
+
+void
+test02()
+{
+  int x[5] = { 0 };
+  int k = 0;
+  for (auto&& i : ranges::ref_view{x})
+    i += ++k;
+  VERIFY( ranges::equal(x, (int[]){1,2,3,4,5}) );
+}
+
+constexpr bool
+test03()
+{
+  std::array ints{0,1,2,3,4,5};
+  auto even = [] (int i) { return i%2==0; };
+  auto odd = [] (int i) { return i%2==1; };
+  auto square = [] (int i) { return i*i; };
+  int sum = 0;
+  for (auto v : (ints
+		 | (views::all
+		    | (views::filter(even)
+		    | (views::filter(odd) | views::all)))
+		 | views::transform(square)))
+    sum += v;
+  return sum == 0;
+}
+
+constexpr bool
+test04()
+{
+  auto odd = [] (int i) { return i%2==1; };
+  auto square = [] (int i) { return i*i; };
+  auto increment = [] (int i) { return i+1; };
+  auto small = [] (int i) { return i<30; };
+  auto non_negative = [] (int i) { return i>=0; };
+  auto negative = [] (int i) { return i<0; };
+  return ranges::equal(views::iota(-5)
+		       | views::drop_while(negative)
+		       | views::take_while(non_negative)
+		       | views::transform(increment)
+		       | views::filter(odd)
+		       | views::take(3)
+		       | views::all
+		       | views::transform(square),
+		       views::iota(-5)
+		       | views::drop_while(negative)
+		       | views::drop(1)
+		       | views::filter(odd)
+		       | views::transform(square)
+		       | views::take_while(small)
+		       | views::take_while(small));
+}
+
+static_assert(std::is_empty_v<decltype(views::common
+				       | views::join
+				       | views::all
+				       | views::common
+				       | views::keys
+				       | views::reverse)>);
+static_assert(sizeof(decltype(views::take(5) | views::drop(5)))
+	      == sizeof(decltype(views::take(5)
+				 | views::join
+				 | views::common
+				 | views::all
+				 | views::keys
+				 | views::drop(5)
+				 | views::reverse)));
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+  static_assert(test04());
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
new file mode 100644
index 00000000000..b73796835be
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,1,3};
+  auto v = x | views::common;
+  VERIFY( std::count(v.begin(), v.end(), 1) == 2);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+  static_assert(std::same_as<decltype(v), decltype(views::common(v))>);
+
+  auto v2 = v | (views::common | views::common);
+  VERIFY( std::count(v2.begin(), v2.end(), 1) == 2);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,1,3};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto v = ranges::common_view(rx);
+  VERIFY( std::count(v.begin(), v.end(), 1) == 2);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::forward_range<decltype(v)>);
+  static_assert(std::same_as<decltype(v), decltype(views::common(v))>);
+
+  auto v2 = v | (views::common | views::common);
+  VERIFY( std::count(v2.begin(), v2.end(), 1) == 2);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc
new file mode 100644
index 00000000000..e81f9062d39
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc
@@ -0,0 +1,64 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {0,1,2,3,4,5,0,1,2,3,4,5};
+  auto v = views::counted(x, 5);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test02()
+{
+  int x[] = {0,1,2,3,4,5,0,1,2,3,4,5};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto v = views::counted(rx.begin(), 5);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(!ranges::bidirectional_range<R>);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
new file mode 100644
index 00000000000..93fbafcf5a3
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
@@ -0,0 +1,107 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto v = x | views::drop(3);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::random_access_range<R>);
+  VERIFY( ranges::equal(v, (int[]){4,5}) );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  auto t = views::drop(3) | views::reverse;
+  auto v = x | t;
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::random_access_range<R>);
+  VERIFY( ranges::equal(v, (int[]){5,4}) );
+}
+
+void
+test03()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = rx | views::drop(3);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+  VERIFY( ranges::equal(v, (int[]){4,5}) );
+}
+
+
+void
+test04()
+{
+  auto v = views::iota(0) | views::drop(10);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  VERIFY( ranges::equal(v | views::take(3), (int[]){10,11,12}) );
+}
+
+void
+test05()
+{
+  int x[] = {1,2,3};
+  auto r = ranges::subrange(x, x+1);
+  auto v = views::drop(r, 2);
+  VERIFY( ranges::begin(v) == x+1 );
+  VERIFY( ranges::size(v) == 0 );
+}
+
+void
+test06()
+{
+  int x[] = {1,2,3};
+  VERIFY( ranges::empty(x | views::drop(10)) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
new file mode 100644
index 00000000000..be47551563d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
@@ -0,0 +1,63 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  auto p = [] (int i) { return i != 16; };
+  auto v = views::iota(10) | views::drop_while(p);
+  VERIFY( ranges::equal(v | views::take(5), (int[]){16,17,18,19,20}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = rx | views::drop_while([] (int i) { return i<4; });
+  VERIFY( ranges::equal(v, (int[]){4,5}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
+
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
new file mode 100644
index 00000000000..d846c4cf33e
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <tuple>
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  std::tuple<int, int> x[] = {{1,2},{3,4},{5,6}};
+  auto v0 = x | views::elements<0>;
+  VERIFY( ranges::equal(v0, (int[]){1,3,5}) );
+  VERIFY( ranges::equal(v0, x | views::keys) );
+  VERIFY( ranges::size(v0) == 3 );
+
+  using R0 = decltype(v0);
+  static_assert(ranges::random_access_range<R0>);
+  static_assert(ranges::sized_range<R0>);
+
+  auto v1 = x | views::reverse | views::elements<1> | views::reverse;
+  VERIFY( ranges::equal(v1, (int[]){2,4,6}) );
+  VERIFY( ranges::equal(v1, x | views::values) );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
new file mode 100644
index 00000000000..83d52967a0f
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5,6};
+  auto is_odd = [] (int i) { return i%2==1; };
+  auto v = x | views::filter(is_odd);
+  using R = decltype(v);
+  static_assert(std::same_as<int&, decltype(*v.begin())>);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::input_range<R>);
+  static_assert(ranges::common_range<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+  static_assert(!ranges::random_access_range<R>);
+  static_assert(ranges::range<ranges::all_view<R>>);
+  VERIFY( ranges::equal(v, (int[]){1,3,5}) );
+  VERIFY( ranges::equal(v | views::reverse, (int[]){5,3,1}) );
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5,6};
+  auto f = [flag=false] (int) mutable { return flag = !flag; };
+  auto v = views::filter(f)(x);
+  using R = decltype(v);
+  static_assert(std::same_as<int&, decltype(*v.begin())>);
+  static_assert(ranges::range<R>);
+  static_assert(std::copyable<R>);
+  static_assert(!ranges::view<const R>);
+  VERIFY( ranges::equal(v, (int[]){1,3,5}) );
+}
+
+struct X
+{
+  int i, j;
+};
+
+void
+test03()
+{
+  X x[] = {{1,3}, {2,5}, {3,7}, {4,9}};
+  test_range<X, bidirectional_iterator_wrapper> rx(x);
+  auto v = rx | views::filter([] (auto&& p) { return p.i%2==0; });
+  int sum = 0;
+  for (auto i = v.begin(); i != v.end(); ++i)
+    sum += i->j;
+  VERIFY( sum == 14 );
+}
+
+void
+test04()
+{
+  auto yes = [] (int) { return true; };
+  VERIFY( ranges::equal(views::iota(0) | views::filter(yes) | views::take(1),
+			(int[]){0}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
new file mode 100644
index 00000000000..d3e652da009
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -0,0 +1,112 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  using namespace std::literals;
+  std::string_view cs[] = {"the", "quick", "brown", "fox"};
+  auto v = cs | views::join;
+  VERIFY( ranges::equal(v, "thequickbrownfox"sv) );
+  using R = decltype(v);
+  static_assert(ranges::bidirectional_range<R>);
+  static_assert(ranges::bidirectional_range<const R>);
+  static_assert(ranges::common_range<R>);
+  static_assert(ranges::common_range<const R>);
+}
+
+void
+test02()
+{
+  auto v = (views::iota(0,4)
+	    | views::transform([] (int i) { return views::iota(0,i); })
+	    | views::join);
+  VERIFY( ranges::equal(v, (int[]){0,0,1,0,1,2}) );
+  using R = decltype(v);
+  static_assert(ranges::input_range<R>);
+  static_assert(!ranges::range<const R>);
+  static_assert(!ranges::forward_range<R>);
+  static_assert(!ranges::common_range<const R>);
+}
+
+void
+test03()
+{
+  auto v = (views::iota(0,4)
+	    | views::transform([] (int i) { return views::iota(0,i); })
+	    | views::filter([] (auto) { return true; })
+	    | views::join);
+  VERIFY( ranges::equal(v, (int[]){0,0,1,0,1,2}) );
+  using R = decltype(v);
+  static_assert(ranges::input_range<R>);
+  static_assert(!ranges::range<const R>);
+  static_assert(!ranges::forward_range<R>);
+  static_assert(!ranges::common_range<const R>);
+}
+
+void
+test04()
+{
+  auto v = (views::iota(0,4)
+	    | views::transform([] (int i) { return views::iota(0,i); }));
+  auto v2 = ranges::ref_view{v};
+  VERIFY( ranges::equal(v2 | views::join, (int[]){0,0,1,0,1,2}) );
+  using R = decltype(v2);
+  static_assert(ranges::random_access_range<R>);
+  static_assert(ranges::range<const R>);
+  static_assert(ranges::common_range<const R>);
+  static_assert(ranges::random_access_range<ranges::range_reference_t<R>>);
+  static_assert(!std::is_reference_v<ranges::range_reference_t<R>>);
+}
+
+void
+test05()
+{
+  using namespace std::literals;
+  std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"};
+  auto v = x | views::join | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
new file mode 100644
index 00000000000..0c6aceabbed
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
@@ -0,0 +1,86 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto v = x | views::reverse;
+  VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) );
+  VERIFY( ranges::equal(v | views::reverse, x) );
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::sized_range<decltype(v)>);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = views::reverse(rx);
+  VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) );
+  VERIFY( ranges::equal(v | views::reverse, rx) );
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(!ranges::sized_range<decltype(v)>);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(!ranges::random_access_range<decltype(v)>);
+  static_assert(ranges::bidirectional_range<decltype(v)>);
+}
+
+void
+test03()
+{
+  int x[] = {1,7,3,6,5,2,4,8};
+  auto is_even = [] (int i) { return i%2==0; };
+  int sum = 0;
+  for (auto i : x | views::reverse | views::filter(is_even))
+    sum += i;
+  VERIFY( sum == 20 );
+}
+
+void
+test04()
+{
+  int x[] = {1,2,3,4,5};
+  VERIFY( ranges::equal(x | views::reverse | (views::reverse | views::reverse),
+			(int[]){5,4,3,2,1}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
new file mode 100644
index 00000000000..129a8249f21
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
@@ -0,0 +1,82 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+using namespace std::literals;
+
+void
+test01()
+{
+  auto x = "the  quick  brown  fox"sv;
+  auto p = std::string{"  "};
+  auto v = x | views::split(p);
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test02()
+{
+  auto x = "the quick brown fox"sv;
+  auto v = x | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test03()
+{
+  char x[] = "the quick brown fox";
+  test_range<char, forward_iterator_wrapper> rx(x);
+  auto v = rx | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
new file mode 100644
index 00000000000..e2d2edbe0a8
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
@@ -0,0 +1,95 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = ranges::views;
+
+void
+test01()
+{
+  auto v = views::iota(0) | views::take(5);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(!ranges::sized_range<decltype(v)>);
+  static_assert(!ranges::common_range<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+  static_assert(!ranges::contiguous_range<decltype(v)>);
+  static_assert(ranges::range<const decltype(v)>);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+}
+
+void
+test02()
+{
+  auto v = views::take(views::iota(0, 20), 5);
+  static_assert(ranges::view<decltype(v)>);
+  static_assert(ranges::sized_range<decltype(v)>);
+  static_assert(ranges::common_range<decltype(v)>);
+  static_assert(ranges::random_access_range<decltype(v)>);
+  static_assert(!ranges::contiguous_range<decltype(v)>);
+  static_assert(ranges::range<const decltype(v)>);
+  VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) );
+}
+
+void
+test03()
+{
+  int x[] = {0,1,2,3,4,5};
+  auto is_odd = [] (int i) { return i%2 == 1; };
+  auto v = x | views::filter(is_odd) | views::take(3);
+  ranges::begin(v);
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::forward_range<R>);
+  static_assert(!ranges::random_access_range<R>);
+  static_assert(!ranges::range<const R>);
+  VERIFY( ranges::equal(v, (int[]){1,3,5}) );
+}
+
+void
+test04()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, bidirectional_iterator_wrapper> rx(x);
+  auto v = ranges::take_view{rx, 3};
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::sized_range<R>);
+  static_assert(ranges::bidirectional_range<R>);
+  VERIFY( ranges::equal(v | views::take(5), (int[]){1,2,3}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
new file mode 100644
index 00000000000..b261ffd1aae
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -0,0 +1,62 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  auto p = [] (int i) { return i != 16; };
+  auto v = views::iota(10) | views::take_while(p);
+  VERIFY( ranges::equal(v, (int[]){10,11,12,13,14,15}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test02()
+{
+  int x[] = {1,2,3,4,5};
+  test_range<int, forward_iterator_wrapper> rx(x);
+  auto v = rx | views::take_while([] (int i) { return i<4; });
+  VERIFY( ranges::equal(v, (int[]){1,2,3}) );
+  using R = decltype(v);
+  static_assert(ranges::view<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::forward_range<R>);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
new file mode 100644
index 00000000000..ad51fffb43d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -0,0 +1,86 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::random_access_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  auto is_odd = [] (int i) { return i%2==1; };
+  auto v = x | views::transform(is_odd);
+  VERIFY( ranges::equal(v, (int[]){1,0,1,0,1}) );
+  using R = decltype(v);
+  static_assert(std::same_as<bool, decltype(*ranges::begin(v))>);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+struct X
+{
+  int i,j;
+};
+
+void
+test02()
+{
+  X x[] = {{1,2},{3,4},{5,6},{7,8},{9,10}};
+  test_range<X, random_access_iterator_wrapper> rx(x);
+  auto v = rx | views::transform(&X::i);
+  VERIFY( ranges::size(v) == 5 );
+  VERIFY( ranges::distance(v.begin(), v.end()) == 5 );
+  VERIFY( ranges::equal(v, (int[]){1,3,5,7,9}) );
+  VERIFY( ranges::equal(v | views::reverse, (int[]){9,7,5,3,1}) );
+  using R = decltype(v);
+  static_assert(std::same_as<int&, decltype(*ranges::begin(v))>);
+  static_assert(std::same_as<int, std::iter_value_t<ranges::iterator_t<R>>>);
+  static_assert(ranges::view<R>);
+  static_assert(ranges::sized_range<R>);
+  static_assert(!ranges::common_range<R>);
+  static_assert(ranges::random_access_range<R>);
+}
+
+void
+test03()
+{
+  auto id = [] (int i) { return i; };
+  auto v = views::iota(0) | (views::filter(id)
+			     | views::transform(id)
+			     | views::take(5));
+  VERIFY( ranges::equal(v, (int[]){1,2,3,4,5}) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-06 16:25   ` Jonathan Wakely
@ 2020-02-07  1:13     ` Patrick Palka
  0 siblings, 0 replies; 27+ messages in thread
From: Patrick Palka @ 2020-02-07  1:13 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

On Thu, 6 Feb 2020, Jonathan Wakely wrote:

> On 03/02/20 21:07 -0500, Patrick Palka wrote:
> > +#ifndef _RANGES_ALGO_H
> > +#define _RANGES_ALGO_H 1
> > +
> > +#if __cplusplus > 201703L
> > +
> > +#include <compare>
> > +#include <cmath>
> > +#include <iterator>
> > +// #include <bits/range_concepts.h>
> 
> This line could be removed, or leave it as a reminder to me to
> refactor <ranges> so that the small utility pieces are in a small
> utility header (like <bits/ranges_concepts.h> that can be included
> instead of the whole of <ranges>.

I guess I'll leave it in then.

> 
> > +#include <ranges>
> > +#include <bits/invoke.h>
> > +#include <bits/cpp_type_traits.h> // __is_byte
> > +#include <bits/random.h> // concept uniform_random_bit_generator
> 
> I wonder if we want to move that concept to <bits/uniform_rand_dist.h>
> instead, which already exists to allow <algorithm> to avoid including
> the whole of <random>. If we do that, it would make sense to rename
> <bits/uniform_rand_dist.h> to <bits/random_fwd.h> or something like
> that.

That makes sense -- I can try to do that in a followup patch.

> 
> > +
> > +#if __cpp_lib_concepts
> > +namespace std _GLIBCXX_VISIBILITY(default)
> > +{
> > +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> > +namespace ranges
> > +{
> > +  namespace __detail
> > +  {
> > +    template<typename _Tp>
> > +    constexpr inline bool __is_normal_iterator = false;
> 
> All these templates in the __detail namespace should be indented by
> two spaces after the template-head i.e.
> 
>     template<typename _Tp>
>       constexpr inline bool __is_normal_iterator = false;
> 
> (That indentation scheme has been in the libstdc++ style guide for
> longer than I've been contributing to the project, but it doesn't seem
> very popular with new contributors, and it wastes a level of
> indentation for templates, which means most of the library. Maybe we
> should revisit that convention.)

Fixed

> 
> 
> > +  template<typename _Iter, typename _Out>
> > +    using unary_transform_result = copy_result<_Iter, _Out>;
> > +
> > +  template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
> > +	   weakly_incrementable _Out,
> > +	   copy_constructible _Fp, typename _Proj = identity>
> > +    requires writable<_Out, indirect_result_t<_Fp&, projected<_Iter,
> > _Proj>>>
> 
> I have a pending patch to implement P1878R1, which renames writable
> (and a few other concepts). I'll wait until your patch is in, and
> change these places using it.

Sounds good.

> 
> > +    partial_sort_copy(_Iter1 __first, _Sent1 __last,
> > +		      _Iter2 __result_first, _Sent2 __result_last,
> > +		      _Comp __comp = {},
> > +		      _Proj1 __proj1 = {}, _Proj2 __proj2 = {})
> > +    {
> > +      if (__result_first == __result_last)
> > +	{
> > +	  // TODO: Eliminating the variable __lasti triggers an ICE.
> > +	  auto __lasti = ranges::next(std::move(__first),
> > +				      std::move(__last));
> > +	  return {std::move(__lasti), std::move(__result_first)};
> 
> Please try to reduce that and report it to bugzilla at some point,
> thanks.

Will do!  Interestingly, it was an ICE in the middle-end.  I wasn't able
to reproduce it anymore, but I'll try more carefully tomorrow.

> 
> > +++ b/libstdc++-v3/testsuite/25_algorithms/all_of/constrained.cc
> > @@ -0,0 +1,90 @@
> > +// Copyright (C) 2019 Free Software Foundation, Inc.
> 
> This should be 2020. That's the only change necessary though, please
> adjust that and commit to master. Great work, thank you!

Fixed.  Thank you for the review!  Patch committed, hopefully without
any fallout.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-06 23:53     ` Patrick Palka
@ 2020-02-07 11:48       ` Jonathan Wakely
  2020-02-07 14:28         ` Patrick Palka
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-07 11:48 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 593 bytes --]

On 06/02/20 18:53 -0500, Patrick Palka wrote:
>On Thu, 6 Feb 2020, Jonathan Wakely wrote:
>> > +#ifdef __cpp_lib_threeway_comparison
>>
>> This macro is mispelled, should be three_way with an underscore.
>
>Oops!  It looks like it's also mispelled in the definition of iota_view
>earlier in this file.

Oops, yes, my fault then. Fixed with this patch, tested
powerpc64le-linux and committed to master. I've reported the defect
with the return types to the LWG chair.

Your incremental changes look good, please squash them into the base
patch and push that to master. Thanks.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 2936 bytes --]

commit 5713834e4b99e4c4c99eef15698a497f091b7dc4
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Feb 7 11:31:12 2020 +0000

    libstdc++: Enable three-way comparison for iota_view iterators
    
    The declaration of operator<=> was disabled due to a typo in the macro.
    The declaration was also ill-formed when three_way_comparable<_Winc> is
    not satisfied, which is a defect in the C++20 draft.
    
            * include/std/ranges (iota_view::_Iterator): Fix typo in name of
            __cpp_lib_three_way_comparison macro and use deduced return type for
            operator<=>.
            * testsuite/std/ranges/iota/iterator.cc: New test.

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 860f7283be5..dc277a74fb6 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -776,8 +776,8 @@ namespace ranges
 	  requires totally_ordered<_Winc>
 	{ return !(__x < __y); }
 
-#ifdef __cpp_lib_threeway_comparison
-	friend constexpr compare_three_way_result_t<_Winc>
+#ifdef __cpp_lib_three_way_comparison
+	friend constexpr auto
 	operator<=>(const _Iterator& __x, const _Iterator& __y)
 	  requires totally_ordered<_Winc> && three_way_comparable<_Winc>
 	{ return __x._M_value <=> __y._M_value; }
diff --git a/libstdc++-v3/testsuite/std/ranges/iota/iterator.cc b/libstdc++-v3/testsuite/std/ranges/iota/iterator.cc
new file mode 100644
index 00000000000..4d471431eae
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/iota/iterator.cc
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <ranges>
+
+auto i = std::ranges::iota_view<int>{}.begin();
+static_assert( std::three_way_comparable<decltype(i)> );
+
+struct Inc {
+  Inc& operator++();
+  Inc operator++(int);
+  friend long operator-(Inc, Inc);
+};
+static_assert( ! std::three_way_comparable<Inc> );
+
+// Instantiating iterator type must be valid despite !three_way_comparable<Inc>
+auto j = std::ranges::iota_view<Inc>{}.begin();
+static_assert( ! std::three_way_comparable<decltype(j)> );

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-04  2:09 ` [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms Patrick Palka
  2020-02-05 18:39   ` François Dumont
  2020-02-06 16:25   ` Jonathan Wakely
@ 2020-02-07 13:51   ` Jonathan Wakely
  2020-02-13 18:07   ` François Dumont
  3 siblings, 0 replies; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-07 13:51 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On 03/02/20 21:07 -0500, Patrick Palka wrote:
>+  template<typename _Iter1, typename _Iter2, typename _Out>
>+    struct binary_transform_result
>+    {
>+      [[no_unique_address]] _Iter1 in1;
>+      [[no_unique_address]] _Iter2 in2;
>+      [[no_unique_address]] _Out  out;
>+
>+      template<typename _IIter1, typename _IIter2, typename _OOut>
>+	requires convertible_to<const _Iter1&, _IIter1> &&
>+	  && convertible_to<const _Iter2&, _IIter2>

WHAT IS HAPPENING HERE?!

Notice we have requires A && && B

I'm fixing it, but that needs following up to see if there's a
compiler bug!


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-07 11:48       ` Jonathan Wakely
@ 2020-02-07 14:28         ` Patrick Palka
  0 siblings, 0 replies; 27+ messages in thread
From: Patrick Palka @ 2020-02-07 14:28 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

On Fri, 7 Feb 2020, Jonathan Wakely wrote:

> On 06/02/20 18:53 -0500, Patrick Palka wrote:
> > On Thu, 6 Feb 2020, Jonathan Wakely wrote:
> > > > +#ifdef __cpp_lib_threeway_comparison
> > > 
> > > This macro is mispelled, should be three_way with an underscore.
> > 
> > Oops!  It looks like it's also mispelled in the definition of iota_view
> > earlier in this file.
> 
> Oops, yes, my fault then. Fixed with this patch, tested
> powerpc64le-linux and committed to master. I've reported the defect
> with the return types to the LWG chair.
> 
> Your incremental changes look good, please squash them into the base
> patch and push that to master. Thanks.

Thanks for the review.  I just committed the squashed version after a
successful regtest.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-04  2:09 ` [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms Patrick Palka
                     ` (2 preceding siblings ...)
  2020-02-07 13:51   ` Jonathan Wakely
@ 2020-02-13 18:07   ` François Dumont
  2020-02-13 19:00     ` Jonathan Wakely
  3 siblings, 1 reply; 27+ messages in thread
From: François Dumont @ 2020-02-13 18:07 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches; +Cc: libstdc++, jwakely

On 2/4/20 3:07 AM, Patrick Palka wrote:
> This patch implements the C++20 ranges overloads for the algorithms in
> [algorithms].  Most of the algorithms were reimplemented, with each of their
> implementations very closely following the existing implementation in
> bits/stl_algo.h and bits/stl_algobase.h.  The reason for reimplementing most of
> the algorithms instead of forwarding to their STL-style overload is because
> forwarding cannot be conformantly and efficiently performed for algorithms that
> operate on non-random-access iterators.
Why ? Do you have a clear counter-example ?

Maybe at the time you wrote this code those algos were not constexpr 
qualified, but they are now.

>    But algorithms that operate on random
> access iterators can safely and efficiently be forwarded to the STL-style
> implementation, and this patch does so for push_heap, pop_heap, make_heap,
> sort_heap, sort, stable_sort, nth_element, inplace_merge and stable_partition.
IMHO we should try as much as possible to forward to algos in 
stl_algobase.h.

Those are highly customized and will be even more in the future when 
some patches I have on my side will be integrated (I hope).

If you do so you won't have to care much about _GLIBCXX_DEBUG iterators.

>
> What's missing from this patch is debug-iterator and container specializations
> that are present for some of the STL-style algorithms that need to be ported
> over to the ranges algos.  I marked them missing at TODO comments.  There are
> also some other minor outstanding TODOs.
>
> The code that could use the most thorough review is ranges::__copy_or_move,
> ranges::__copy_or_move_backward, ranges::__equal and
> ranges::__lexicographical_compare.  In the tests, I tried to test the interface
> of each new overload, as well as the correctness of the new implementation.
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-13 18:07   ` François Dumont
@ 2020-02-13 19:00     ` Jonathan Wakely
  2020-02-14  6:00       ` François Dumont
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-13 19:00 UTC (permalink / raw)
  To: François Dumont; +Cc: Patrick Palka, gcc-patches, libstdc++

On 13/02/20 19:07 +0100, François Dumont wrote:
>On 2/4/20 3:07 AM, Patrick Palka wrote:
>>This patch implements the C++20 ranges overloads for the algorithms in
>>[algorithms].  Most of the algorithms were reimplemented, with each of their
>>implementations very closely following the existing implementation in
>>bits/stl_algo.h and bits/stl_algobase.h.  The reason for reimplementing most of
>>the algorithms instead of forwarding to their STL-style overload is because
>>forwarding cannot be conformantly and efficiently performed for algorithms that
>>operate on non-random-access iterators.
>Why ? Do you have a clear counter-example ?
>
>Maybe at the time you wrote this code those algos were not constexpr 
>qualified, but they are now.

It has nothing to do with constexpr.

If you call a ranges algo with an iterator and a sentinel that is a
different type to the iterator, you can't call the old STL algo unless
you can efficiently get an end iterator that refers to the same
position as the sentinel. For random access iterators that is:

auto last2 = first + (last - first);

But for non-random access iterators finding the distance between first
and last is not possible in O(1), and incrementing first by that
distance is also not possible in O(1).

>>   But algorithms that operate on random
>>access iterators can safely and efficiently be forwarded to the STL-style
>>implementation, and this patch does so for push_heap, pop_heap, make_heap,
>>sort_heap, sort, stable_sort, nth_element, inplace_merge and stable_partition.
>IMHO we should try as much as possible to forward to algos in 
>stl_algobase.h.

That would not be conforming in many cases.

The old code assumes iterators can be copied. It assumes they have an
iterator_category. It assumes they have operator->(). Most
importantly, it assumes the begin and end iterators have the same
type.

The old algorithms do tag dispatching, which adds function call
overhead at run-time and overload resolution overhead at compile-time.

The new constrained algos can be implemented much more cleanly using
if-constexpr and the new iterator concepts.

There are very good reasons to reimplement the new ranges algos.

>Those are highly customized and will be even more in the future when 
>some patches I have on my side will be integrated (I hope).
>
>If you do so you won't have to care much about _GLIBCXX_DEBUG iterators.
>
>>
>>What's missing from this patch is debug-iterator and container specializations
>>that are present for some of the STL-style algorithms that need to be ported
>>over to the ranges algos.  I marked them missing at TODO comments.  There are
>>also some other minor outstanding TODOs.
>>
>>The code that could use the most thorough review is ranges::__copy_or_move,
>>ranges::__copy_or_move_backward, ranges::__equal and
>>ranges::__lexicographical_compare.  In the tests, I tried to test the interface
>>of each new overload, as well as the correctness of the new implementation.
>>
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms
  2020-02-13 19:00     ` Jonathan Wakely
@ 2020-02-14  6:00       ` François Dumont
  0 siblings, 0 replies; 27+ messages in thread
From: François Dumont @ 2020-02-14  6:00 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

Thanks for those additional information.

I still think that the same way we rely on STL algos for 
push_heap/pop_heap/... we should do it for copy/move/... from 
stl_algobase.h for RAI.

In fact range algos that are already trying to call C functions should 
just try to call the STL counterparts which will then forward to C 
functions.

On 2/13/20 8:00 PM, Jonathan Wakely wrote:
> On 13/02/20 19:07 +0100, François Dumont wrote:
>> On 2/4/20 3:07 AM, Patrick Palka wrote:
>>> This patch implements the C++20 ranges overloads for the algorithms in
>>> [algorithms].  Most of the algorithms were reimplemented, with each 
>>> of their
>>> implementations very closely following the existing implementation in
>>> bits/stl_algo.h and bits/stl_algobase.h.  The reason for 
>>> reimplementing most of
>>> the algorithms instead of forwarding to their STL-style overload is 
>>> because
>>> forwarding cannot be conformantly and efficiently performed for 
>>> algorithms that
>>> operate on non-random-access iterators.
>> Why ? Do you have a clear counter-example ?
>>
>> Maybe at the time you wrote this code those algos were not constexpr 
>> qualified, but they are now.
>
> It has nothing to do with constexpr.
>
> If you call a ranges algo with an iterator and a sentinel that is a
> different type to the iterator, you can't call the old STL algo unless
> you can efficiently get an end iterator that refers to the same
> position as the sentinel. For random access iterators that is:
>
> auto last2 = first + (last - first);
>
> But for non-random access iterators finding the distance between first
> and last is not possible in O(1), and incrementing first by that
> distance is also not possible in O(1).
>
>>>   But algorithms that operate on random
>>> access iterators can safely and efficiently be forwarded to the 
>>> STL-style
>>> implementation, and this patch does so for push_heap, pop_heap, 
>>> make_heap,
>>> sort_heap, sort, stable_sort, nth_element, inplace_merge and 
>>> stable_partition.
>> IMHO we should try as much as possible to forward to algos in 
>> stl_algobase.h.
>
> That would not be conforming in many cases.
>
> The old code assumes iterators can be copied. It assumes they have an
> iterator_category. It assumes they have operator->(). Most
> importantly, it assumes the begin and end iterators have the same
> type.
>
> The old algorithms do tag dispatching, which adds function call
> overhead at run-time and overload resolution overhead at compile-time.
>
> The new constrained algos can be implemented much more cleanly using
> if-constexpr and the new iterator concepts.
>
> There are very good reasons to reimplement the new ranges algos.
>
>> Those are highly customized and will be even more in the future when 
>> some patches I have on my side will be integrated (I hope).
>>
>> If you do so you won't have to care much about _GLIBCXX_DEBUG iterators.
>>
>>>
>>> What's missing from this patch is debug-iterator and container 
>>> specializations
>>> that are present for some of the STL-style algorithms that need to 
>>> be ported
>>> over to the ranges algos.  I marked them missing at TODO comments.  
>>> There are
>>> also some other minor outstanding TODOs.
>>>
>>> The code that could use the most thorough review is 
>>> ranges::__copy_or_move,
>>> ranges::__copy_or_move_backward, ranges::__equal and
>>> ranges::__lexicographical_compare.  In the tests, I tried to test 
>>> the interface
>>> of each new overload, as well as the correctness of the new 
>>> implementation.
>>>
>>
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-04  2:08 ` [PATCH 3/3] libstdc++: Implement C++20 range adaptors Patrick Palka
  2020-02-06 17:25   ` Jonathan Wakely
@ 2020-02-17 22:07   ` Stephan Bergmann
  2020-02-18  1:20     ` Patrick Palka
  2020-02-18 10:14     ` Jonathan Wakely
  1 sibling, 2 replies; 27+ messages in thread
From: Stephan Bergmann @ 2020-02-17 22:07 UTC (permalink / raw)
  To: libstdc++; +Cc: Patrick Palka, gcc-patches, jwakely

On 04/02/2020 03:07, Patrick Palka wrote:
> This patch implements [range.adaptors].  It also includes the changes from P3280
> and P3278 and P3323, without which many standard examples won't work.

I see that with this 
<https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;a=commit;h=cba9ef069e58eac00f30489d3ef21390caee6e45> 
"libstdc++: Implement C++20 range adaptors", compiling <ranges> with 
recent Clang trunk (which appears to mostly implement C++20 concepts 
now) in -std=c++2a mode fails as below (besides also failing due to some 
"missing" typenames, where Clang apparently doesn't yet implement 
P0634R3).  And I'm not sure which of Clang vs. GCC is right here.

The failure is

> gcc/trunk/inst/include/c++/10.0.1/ranges:1512:47: error: ambiguous deduction for template arguments of '_RangeAdaptor'
>     inline constexpr __adaptor::_RangeAdaptor filter
>                                               ^
> gcc/trunk/inst/include/c++/10.0.1/ranges:1073:2: note: candidate function [with _Callable = std::ranges::views::(lambda at gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
>         _RangeAdaptor(const _Callable& = {})
>         ^
> gcc/trunk/inst/include/c++/10.0.1/ranges:1078:2: note: candidate function [with _Callable = std::ranges::views::(lambda at gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
>         _RangeAdaptor(_Callable __callable)
>         ^

and a stripped-down reproducer is

> template<typename T> struct S {
>   S(T const &) requires true;
>   S(T) requires false;
> };
> S s = 0;

(Clang accepts this when the last line is replaced with

> S<int> s = 0;

and thus no class template argument deduction needs to be done.)

I think what is relevant here is [over.match.class.deduct]/1 in the 
current spec, which specifies a helper set of hypothetical function 
templates based on a class' constructors for class template argument 
deduction.  It details the function templates' template parameters, 
function parameters, and return types, but does not mention 
requires-clauses.  From my superficial understanding of concepts and 
class template argument deduction it would thus look like the 
constructors' requires-clauses should indeed not be taken into account here?

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-17 22:07   ` Stephan Bergmann
@ 2020-02-18  1:20     ` Patrick Palka
  2020-02-18  7:31       ` Stephan Bergmann
  2020-02-18 11:56       ` Jonathan Wakely
  2020-02-18 10:14     ` Jonathan Wakely
  1 sibling, 2 replies; 27+ messages in thread
From: Patrick Palka @ 2020-02-18  1:20 UTC (permalink / raw)
  To: Stephan Bergmann
  Cc: libstdc++, Patrick Palka, gcc-patches, jwakely, jason, polacek

Hi Stephan,

On Mon, 17 Feb 2020, Stephan Bergmann wrote:

> On 04/02/2020 03:07, Patrick Palka wrote:
> > This patch implements [range.adaptors].  It also includes the changes from
> > P3280
> > and P3278 and P3323, without which many standard examples won't work.
> 
> I see that with this
> <https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;a=commit;h=cba9ef069e58eac00f30489d3ef21390caee6e45>
> "libstdc++: Implement C++20 range adaptors", compiling <ranges> with recent
> Clang trunk (which appears to mostly implement C++20 concepts now) in
> -std=c++2a mode fails as below (besides also failing due to some "missing"
> typenames, where Clang apparently doesn't yet implement P0634R3).  And I'm not
> sure which of Clang vs. GCC is right here.
> 
> The failure is
> 
> > gcc/trunk/inst/include/c++/10.0.1/ranges:1512:47: error: ambiguous deduction
> > for template arguments of '_RangeAdaptor'
> >     inline constexpr __adaptor::_RangeAdaptor filter
> >                                               ^
> > gcc/trunk/inst/include/c++/10.0.1/ranges:1073:2: note: candidate function
> > [with _Callable = std::ranges::views::(lambda at
> > gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
> >         _RangeAdaptor(const _Callable& = {})
> >         ^
> > gcc/trunk/inst/include/c++/10.0.1/ranges:1078:2: note: candidate function
> > [with _Callable = std::ranges::views::(lambda at
> > gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
> >         _RangeAdaptor(_Callable __callable)
> >         ^
> 
> and a stripped-down reproducer is
> 
> > template<typename T> struct S {
> >   S(T const &) requires true;
> >   S(T) requires false;
> > };
> > S s = 0;
> 
> (Clang accepts this when the last line is replaced with
> 
> > S<int> s = 0;
> 
> and thus no class template argument deduction needs to be done.)
> 
> I think what is relevant here is [over.match.class.deduct]/1 in the current
> spec, which specifies a helper set of hypothetical function templates based on
> a class' constructors for class template argument deduction.  It details the
> function templates' template parameters, function parameters, and return
> types, but does not mention requires-clauses.  From my superficial
> understanding of concepts and class template argument deduction it would thus
> look like the constructors' requires-clauses should indeed not be taken into
> account here?

Thanks for letting me know about this issue.  That would be my
interpretation of the spec, too.  Maybe someone else could shed light on
this question?

The following patch simplifies _RangeAdaptor's constructors to no longer
need to be constrained and should resolve the deduction ambiguity error
reported by Clang?  Unfortunately the patch triggers an ICE in GCC,
which I'll look into tomorrow.

-- >8 --

Subject: [PATCH] Simplify constructors of _RangeAdaptor

---
 libstdc++-v3/include/std/ranges | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 6759f9b4b46..a04387a75c4 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1064,19 +1064,13 @@ namespace views
       struct _RangeAdaptor
       {
       protected:
-	[[no_unique_address]]
-	  conditional_t<!is_default_constructible_v<_Callable>,
-			_Callable, __detail::_Empty> _M_callable;
+	[[no_unique_address]] _Callable _M_callable;
 
       public:
-	constexpr
-	_RangeAdaptor(const _Callable& = {})
-	  requires is_default_constructible_v<_Callable>
-	{ }
+	_RangeAdaptor() = default;
 
 	constexpr
 	_RangeAdaptor(_Callable __callable)
-	  requires (!is_default_constructible_v<_Callable>)
 	  : _M_callable(std::move(__callable))
 	{ }
 
-- 
2.25.1.291.ge68e29171c

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-18  1:20     ` Patrick Palka
@ 2020-02-18  7:31       ` Stephan Bergmann
  2020-02-18 11:56       ` Jonathan Wakely
  1 sibling, 0 replies; 27+ messages in thread
From: Stephan Bergmann @ 2020-02-18  7:31 UTC (permalink / raw)
  To: Patrick Palka; +Cc: libstdc++, gcc-patches, jwakely, jason, polacek

On 18/02/2020 02:20, Patrick Palka wrote:
> On Mon, 17 Feb 2020, Stephan Bergmann wrote:
>> On 04/02/2020 03:07, Patrick Palka wrote:
>>> This patch implements [range.adaptors].  It also includes the changes from
>>> P3280
>>> and P3278 and P3323, without which many standard examples won't work.
>>
>> I see that with this
>> <https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;a=commit;h=cba9ef069e58eac00f30489d3ef21390caee6e45>
>> "libstdc++: Implement C++20 range adaptors", compiling <ranges> with recent
>> Clang trunk (which appears to mostly implement C++20 concepts now) in
>> -std=c++2a mode fails as below (besides also failing due to some "missing"
>> typenames, where Clang apparently doesn't yet implement P0634R3).  And I'm not
>> sure which of Clang vs. GCC is right here.
>>
>> The failure is
>>
>>> gcc/trunk/inst/include/c++/10.0.1/ranges:1512:47: error: ambiguous deduction
>>> for template arguments of '_RangeAdaptor'
>>>      inline constexpr __adaptor::_RangeAdaptor filter
>>>                                                ^
>>> gcc/trunk/inst/include/c++/10.0.1/ranges:1073:2: note: candidate function
>>> [with _Callable = std::ranges::views::(lambda at
>>> gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
>>>          _RangeAdaptor(const _Callable& = {})
>>>          ^
>>> gcc/trunk/inst/include/c++/10.0.1/ranges:1078:2: note: candidate function
>>> [with _Callable = std::ranges::views::(lambda at
>>> gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
>>>          _RangeAdaptor(_Callable __callable)
>>>          ^
>>
>> and a stripped-down reproducer is
>>
>>> template<typename T> struct S {
>>>    S(T const &) requires true;
>>>    S(T) requires false;
>>> };
>>> S s = 0;
>>
>> (Clang accepts this when the last line is replaced with
>>
>>> S<int> s = 0;
>>
>> and thus no class template argument deduction needs to be done.)
>>
>> I think what is relevant here is [over.match.class.deduct]/1 in the current
>> spec, which specifies a helper set of hypothetical function templates based on
>> a class' constructors for class template argument deduction.  It details the
>> function templates' template parameters, function parameters, and return
>> types, but does not mention requires-clauses.  From my superficial
>> understanding of concepts and class template argument deduction it would thus
>> look like the constructors' requires-clauses should indeed not be taken into
>> account here?
> 
> Thanks for letting me know about this issue.  That would be my
> interpretation of the spec, too.  Maybe someone else could shed light on
> this question?
> 
> The following patch simplifies _RangeAdaptor's constructors to no longer
> need to be constrained and should resolve the deduction ambiguity error
> reported by Clang?  Unfortunately the patch triggers an ICE in GCC,
> which I'll look into tomorrow.

Thanks, I can confirm that patch works fine with Clang.

Btw, the "missing" typenames for Clang I mentioned above would be fixed 
with the following patch.  I don't know whether you would want to do 
that, as technically the current C++20-only code is fine, but here it is 
anyway:

> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index 970e904bddd..c487dd612c2 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -1326,7 +1326,7 @@ namespace views
>  	static constexpr auto
>  	_S_iter_cat()
>  	{
> -	  using _Cat = iterator_traits<iterator_t<_Vp>>::iterator_category;
> +	  using _Cat = typename iterator_traits<iterator_t<_Vp>>::iterator_category;
>  	  if constexpr (derived_from<_Cat, bidirectional_iterator_tag>)
>  	    return bidirectional_iterator_tag{};
>  	  else if constexpr (derived_from<_Cat, forward_iterator_tag>)
> @@ -1549,7 +1549,7 @@ namespace views
>  	  static constexpr auto
>  	  _S_iter_cat()
>  	  {
> -	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
> +	    using _Cat = typename iterator_traits<iterator_t<_Base>>::iterator_category;
>  	    if constexpr (derived_from<_Cat, contiguous_iterator_tag>)
>  	      return random_access_iterator_tag{};
>  	    else
> @@ -2294,9 +2294,9 @@ namespace views
>  	  _S_iter_cat()
>  	  {
>  	    using _OuterCat
> -	      = iterator_traits<iterator_t<_Base>>::iterator_category;
> +	      = typename iterator_traits<iterator_t<_Base>>::iterator_category;
>  	    using _InnerCat
> -	      = iterator_traits<iterator_t<range_reference_t<_Base>>>
> +	      = typename iterator_traits<iterator_t<range_reference_t<_Base>>>
>  		 ::iterator_category;
>  	    if constexpr (_S_ref_is_glvalue
>  			  && derived_from<_OuterCat, bidirectional_iterator_tag>
> @@ -2765,7 +2765,7 @@ namespace views
>  	  static constexpr auto
>  	  _S_iter_cat()
>  	  {
> -	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
> +	    using _Cat = typename iterator_traits<iterator_t<_Base>>::iterator_category;
>  	    if constexpr (derived_from<_Cat, forward_iterator_tag>)
>  	      return forward_iterator_tag{};
>  	    else

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-17 22:07   ` Stephan Bergmann
  2020-02-18  1:20     ` Patrick Palka
@ 2020-02-18 10:14     ` Jonathan Wakely
  1 sibling, 0 replies; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-18 10:14 UTC (permalink / raw)
  To: Stephan Bergmann; +Cc: libstdc++, Patrick Palka, gcc-patches

On 17/02/20 23:07 +0100, Stephan Bergmann wrote:
>On 04/02/2020 03:07, Patrick Palka wrote:
>>This patch implements [range.adaptors].  It also includes the changes from P3280
>>and P3278 and P3323, without which many standard examples won't work.
>
>I see that with this <https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;a=commit;h=cba9ef069e58eac00f30489d3ef21390caee6e45> 
>"libstdc++: Implement C++20 range adaptors", compiling <ranges> with 
>recent Clang trunk (which appears to mostly implement C++20 concepts 
>now)

[snip]

Thanks, I've been meaning to find out how well Clang trunk deals with
our code using concepts.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-18  1:20     ` Patrick Palka
  2020-02-18  7:31       ` Stephan Bergmann
@ 2020-02-18 11:56       ` Jonathan Wakely
  2020-02-18 20:11         ` Stephan Bergmann
  1 sibling, 1 reply; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-18 11:56 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Stephan Bergmann, libstdc++, gcc-patches, jason, polacek

On 17/02/20 20:20 -0500, Patrick Palka wrote:
>Hi Stephan,
>
>On Mon, 17 Feb 2020, Stephan Bergmann wrote:
>
>> On 04/02/2020 03:07, Patrick Palka wrote:
>> > This patch implements [range.adaptors].  It also includes the changes from
>> > P3280
>> > and P3278 and P3323, without which many standard examples won't work.
>>
>> I see that with this
>> <https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;a=commit;h=cba9ef069e58eac00f30489d3ef21390caee6e45>
>> "libstdc++: Implement C++20 range adaptors", compiling <ranges> with recent
>> Clang trunk (which appears to mostly implement C++20 concepts now) in
>> -std=c++2a mode fails as below (besides also failing due to some "missing"
>> typenames, where Clang apparently doesn't yet implement P0634R3).  And I'm not
>> sure which of Clang vs. GCC is right here.
>>
>> The failure is
>>
>> > gcc/trunk/inst/include/c++/10.0.1/ranges:1512:47: error: ambiguous deduction
>> > for template arguments of '_RangeAdaptor'
>> >     inline constexpr __adaptor::_RangeAdaptor filter
>> >                                               ^
>> > gcc/trunk/inst/include/c++/10.0.1/ranges:1073:2: note: candidate function
>> > [with _Callable = std::ranges::views::(lambda at
>> > gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
>> >         _RangeAdaptor(const _Callable& = {})
>> >         ^
>> > gcc/trunk/inst/include/c++/10.0.1/ranges:1078:2: note: candidate function
>> > [with _Callable = std::ranges::views::(lambda at
>> > gcc/trunk/inst/include/c++/10.0.1/ranges:1513:9)]
>> >         _RangeAdaptor(_Callable __callable)
>> >         ^
>>
>> and a stripped-down reproducer is
>>
>> > template<typename T> struct S {
>> >   S(T const &) requires true;
>> >   S(T) requires false;
>> > };
>> > S s = 0;
>>
>> (Clang accepts this when the last line is replaced with
>>
>> > S<int> s = 0;
>>
>> and thus no class template argument deduction needs to be done.)
>>
>> I think what is relevant here is [over.match.class.deduct]/1 in the current
>> spec, which specifies a helper set of hypothetical function templates based on
>> a class' constructors for class template argument deduction.  It details the
>> function templates' template parameters, function parameters, and return
>> types, but does not mention requires-clauses.  From my superficial
>> understanding of concepts and class template argument deduction it would thus
>> look like the constructors' requires-clauses should indeed not be taken into
>> account here?
>
>Thanks for letting me know about this issue.  That would be my
>interpretation of the spec, too.  Maybe someone else could shed light on
>this question?
>
>The following patch simplifies _RangeAdaptor's constructors to no longer
>need to be constrained and should resolve the deduction ambiguity error
>reported by Clang?  Unfortunately the patch triggers an ICE in GCC,
>which I'll look into tomorrow.

Does this solve the problem, and work with both compilers?

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index b9ac528fdff..481ba75ee5e 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1102,6 +1102,9 @@ namespace views
           }
        };

+    template<typename _Callable>
+      _RangeAdaptor(_Callable) -> _RangeAdaptor<_Callable>;
+
      template<typename _Callable>
        struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
        {


With this deduction guide CTAD should work, without having to consider
the constraints on the constructors.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-18 11:56       ` Jonathan Wakely
@ 2020-02-18 20:11         ` Stephan Bergmann
  2020-02-18 23:41           ` Jonathan Wakely
  0 siblings, 1 reply; 27+ messages in thread
From: Stephan Bergmann @ 2020-02-18 20:11 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, libstdc++, gcc-patches, jason, polacek

On 18/02/2020 12:56, Jonathan Wakely wrote:
> Does this solve the problem, and work with both compilers?
> 
> diff --git a/libstdc++-v3/include/std/ranges 
> b/libstdc++-v3/include/std/ranges
> index b9ac528fdff..481ba75ee5e 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -1102,6 +1102,9 @@ namespace views
>            }
>         };
> 
> +    template<typename _Callable>
> +      _RangeAdaptor(_Callable) -> _RangeAdaptor<_Callable>;
> +
>       template<typename _Callable>
>         struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
>         {
> 
> 
> With this deduction guide CTAD should work, without having to consider
> the constraints on the constructors.

Yes, that indeed compiles with both GCC and Clang.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] libstdc++: Implement C++20 range adaptors
  2020-02-18 20:11         ` Stephan Bergmann
@ 2020-02-18 23:41           ` Jonathan Wakely
  0 siblings, 0 replies; 27+ messages in thread
From: Jonathan Wakely @ 2020-02-18 23:41 UTC (permalink / raw)
  To: Stephan Bergmann; +Cc: Patrick Palka, libstdc++, gcc-patches, jason, polacek

[-- Attachment #1: Type: text/plain, Size: 1309 bytes --]

On 18/02/20 21:11 +0100, Stephan Bergmann wrote:
>On 18/02/2020 12:56, Jonathan Wakely wrote:
>>Does this solve the problem, and work with both compilers?
>>
>>diff --git a/libstdc++-v3/include/std/ranges 
>>b/libstdc++-v3/include/std/ranges
>>index b9ac528fdff..481ba75ee5e 100644
>>--- a/libstdc++-v3/include/std/ranges
>>+++ b/libstdc++-v3/include/std/ranges
>>@@ -1102,6 +1102,9 @@ namespace views
>>           }
>>        };
>>
>>+    template<typename _Callable>
>>+      _RangeAdaptor(_Callable) -> _RangeAdaptor<_Callable>;
>>+
>>      template<typename _Callable>
>>        struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
>>        {
>>
>>
>>With this deduction guide CTAD should work, without having to consider
>>the constraints on the constructors.
>
>Yes, that indeed compiles with both GCC and Clang.

Fixed with this patch, which also introduces some new typedefs to
avoid having to evaluate the iterator_t alias template again and
again.

Tested powerpc64le-linux, committed to master.

There's still something not quite right, because this compiles fine
with GCC but not with Clang:

#include <ranges>
int main()
{
   int a[1] = { 2 };
   auto f = a | std::views::filter([](auto&&){return true;});
   return *std::ranges::begin(f);
}


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 10168 bytes --]

commit a5b213dda50aca90637979f13da2eb377eff9930
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Feb 18 23:22:25 2020 +0000

    libstdc++: Fix compilation of <ranges> with Clang (PR 93818)
    
            PR libstdc++/93818
            * include/std/ranges (_RangeAdaptor): Add deduction guide.
            (filter_view::_Iterator): Add alias _Vp_iter and use in place of
            iterator_t<_Vp>.
            (filter_view::_Iterator::_S_iter_cat()): Add 'typename'.
            (transform_view::_Iterator): Add alias _Base_iter and use in place of
            iterator_t<_Base>.
            (transform_view::_Iterator::_S_iter_cat()): Add 'typename'.
            (join_view::_Iterator): Add _Outer_iter and _Inner_iter aliases.
            (join_view::_Iterator::_S_iter_cat()): Add 'typename'.
            (split_view::_InnerIter::_S_iter_cat()): Likewise.

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index b9ac528fdff..e6bdac315c0 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1102,6 +1102,9 @@ namespace views
 	  }
       };
 
+    template<typename _Callable>
+      _RangeAdaptor(_Callable) -> _RangeAdaptor<_Callable>;
+
     template<typename _Callable>
       struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
       {
@@ -1325,7 +1328,7 @@ namespace views
 	static constexpr auto
 	_S_iter_cat()
 	{
-	  using _Cat = iterator_traits<iterator_t<_Vp>>::iterator_category;
+	  using _Cat = typename iterator_traits<_Vp_iter>::iterator_category;
 	  if constexpr (derived_from<_Cat, bidirectional_iterator_tag>)
 	    return bidirectional_iterator_tag{};
 	  else if constexpr (derived_from<_Cat, forward_iterator_tag>)
@@ -1336,7 +1339,9 @@ namespace views
 
 	friend filter_view;
 
-	iterator_t<_Vp> _M_current = iterator_t<_Vp>();
+	using _Vp_iter = iterator_t<_Vp>;
+
+	_Vp_iter _M_current = _Vp_iter();
 	filter_view* _M_parent = nullptr;
 
       public:
@@ -1348,17 +1353,17 @@ namespace views
 	_Iterator() = default;
 
 	constexpr
-	_Iterator(filter_view& __parent, iterator_t<_Vp> __current)
+	_Iterator(filter_view& __parent, _Vp_iter __current)
 	  : _M_current(std::move(__current)),
 	    _M_parent(std::__addressof(__parent))
 	{ }
 
-	constexpr iterator_t<_Vp>
+	constexpr _Vp_iter
 	base() const &
-	  requires copyable<iterator_t<_Vp>>
+	  requires copyable<_Vp_iter>
 	{ return _M_current; }
 
-	constexpr iterator_t<_Vp>
+	constexpr _Vp_iter
 	base() &&
 	{ return std::move(_M_current); }
 
@@ -1366,10 +1371,10 @@ namespace views
 	operator*() const
 	{ return *_M_current; }
 
-	constexpr iterator_t<_Vp>
+	constexpr _Vp_iter
 	operator->() const
-	  requires __detail::__has_arrow<iterator_t<_Vp>>
-	    && copyable<iterator_t<_Vp>>
+	  requires __detail::__has_arrow<_Vp_iter>
+	    && copyable<_Vp_iter>
 	{ return _M_current; }
 
 	constexpr _Iterator&
@@ -1412,7 +1417,7 @@ namespace views
 
 	friend constexpr bool
 	operator==(const _Iterator& __x, const _Iterator& __y)
-	  requires equality_comparable<iterator_t<_Vp>>
+	  requires equality_comparable<_Vp_iter>
 	{ return __x._M_current == __y._M_current; }
 
 	friend constexpr range_rvalue_reference_t<_Vp>
@@ -1423,7 +1428,7 @@ namespace views
 	friend constexpr void
 	iter_swap(const _Iterator& __x, const _Iterator& __y)
 	  noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current)))
-	  requires indirectly_swappable<iterator_t<_Vp>>
+	  requires indirectly_swappable<_Vp_iter>
 	{ ranges::iter_swap(__x._M_current, __y._M_current); }
       };
 
@@ -1548,7 +1553,8 @@ namespace views
 	  static constexpr auto
 	  _S_iter_cat()
 	  {
-	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    using _Cat
+              = typename iterator_traits<_Base_iter>::iterator_category;
 	    if constexpr (derived_from<_Cat, contiguous_iterator_tag>)
 	      return random_access_iterator_tag{};
 	    else
@@ -1566,7 +1572,9 @@ namespace views
 	      return *__i;
 	  }
 
-	  iterator_t<_Base> _M_current = iterator_t<_Base>();
+	  using _Base_iter = iterator_t<_Base>;
+
+	  _Base_iter _M_current = _Base_iter();
 	  _Parent* _M_parent = nullptr;
 
 	public:
@@ -1579,7 +1587,7 @@ namespace views
 	  _Iterator() = default;
 
 	  constexpr
-	  _Iterator(_Parent& __parent, iterator_t<_Base> __current)
+	  _Iterator(_Parent& __parent, _Base_iter __current)
 	    : _M_current(std::move(__current)),
 	      _M_parent(std::__addressof(__parent))
 	  { }
@@ -1587,16 +1595,16 @@ namespace views
 	  constexpr
 	  _Iterator(_Iterator<!_Const> __i)
 	    requires _Const
-	      && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+	      && convertible_to<iterator_t<_Vp>, _Base_iter>
 	    : _M_current(std::move(__i._M_current)), _M_parent(__i._M_parent)
 	  { }
 
-	  constexpr iterator_t<_Base>
+	  constexpr _Base_iter
 	  base() const &
-	    requires copyable<iterator_t<_Base>>
+	    requires copyable<_Base_iter>
 	  { return _M_current; }
 
-	  constexpr iterator_t<_Base>
+	  constexpr _Base_iter
 	  base() &&
 	  { return std::move(_M_current); }
 
@@ -1659,7 +1667,7 @@ namespace views
 
 	  friend constexpr bool
 	  operator==(const _Iterator& __x, const _Iterator& __y)
-	    requires equality_comparable<iterator_t<_Base>>
+	    requires equality_comparable<_Base_iter>
 	  { return __x._M_current == __y._M_current; }
 
 	  friend constexpr bool
@@ -1686,7 +1694,7 @@ namespace views
 	  friend constexpr auto
 	  operator<=>(const _Iterator& __x, const _Iterator& __y)
 	    requires random_access_range<_Base>
-	      && three_way_comparable<iterator_t<_Base>>
+	      && three_way_comparable<_Base_iter>
 	  { return __x._M_current <=> __y._M_current; }
 #endif
 
@@ -1717,7 +1725,7 @@ namespace views
 	  friend constexpr void
 	  iter_swap(const _Iterator& __x, const _Iterator& __y)
 	    noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current)))
-	    requires indirectly_swappable<iterator_t<_Base>>
+	    requires indirectly_swappable<_Base_iter>
 	  { return ranges::iter_swap(__x._M_current, __y._M_current); }
 
 	  friend _Sentinel<_Const>;
@@ -2271,7 +2279,7 @@ namespace views
 	      }
 
 	    if constexpr (_S_ref_is_glvalue)
-	      _M_inner = iterator_t<range_reference_t<_Base>>();
+	      _M_inner = _Inner_iter();
 	  }
 
 	  static constexpr auto
@@ -2293,10 +2301,9 @@ namespace views
 	  _S_iter_cat()
 	  {
 	    using _OuterCat
-	      = iterator_traits<iterator_t<_Base>>::iterator_category;
+	      = typename iterator_traits<_Outer_iter>::iterator_category;
 	    using _InnerCat
-	      = iterator_traits<iterator_t<range_reference_t<_Base>>>
-		 ::iterator_category;
+	      = typename iterator_traits<_Inner_iter>::iterator_category;
 	    if constexpr (_S_ref_is_glvalue
 			  && derived_from<_OuterCat, bidirectional_iterator_tag>
 			  && derived_from<_InnerCat, bidirectional_iterator_tag>)
@@ -2312,9 +2319,11 @@ namespace views
 	      return output_iterator_tag{};
 	  }
 
-	  iterator_t<_Base> _M_outer = iterator_t<_Base>();
-	  iterator_t<range_reference_t<_Base>> _M_inner
-	    = iterator_t<range_reference_t<_Base>>();
+	  using _Outer_iter = iterator_t<_Base>;
+	  using _Inner_iter = iterator_t<range_reference_t<_Base>>;
+
+	  _Outer_iter _M_outer = _Outer_iter();
+	  _Inner_iter _M_inner = _Inner_iter();
 	  _Parent* _M_parent = nullptr;
 
 	public:
@@ -2330,7 +2339,7 @@ namespace views
 	  // XXX: had to change the type of __outer from iterator_t<_Vp> to
 	  // iterator_t<_Base> here, a possible defect in the spec?
 	  constexpr
-	  _Iterator(_Parent& __parent, iterator_t<_Base> __outer)
+	  _Iterator(_Parent& __parent, _Outer_iter __outer)
 	    : _M_outer(std::move(__outer)),
 	      _M_parent(std::__addressof(__parent))
 	  { _M_satisfy(); }
@@ -2338,9 +2347,8 @@ namespace views
 	  constexpr
 	  _Iterator(_Iterator<!_Const> __i)
 	    requires _Const
-	      && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
-	      && convertible_to<iterator_t<_InnerRange>,
-				iterator_t<range_reference_t<_Base>>>
+	      && convertible_to<iterator_t<_Vp>, _Outer_iter>
+	      && convertible_to<iterator_t<_InnerRange>, _Inner_iter>
 	    : _M_outer(std::move(__i._M_outer)), _M_inner(__i._M_inner),
 	      _M_parent(__i._M_parent)
 	  { }
@@ -2349,10 +2357,10 @@ namespace views
 	  operator*() const
 	  { return *_M_inner; }
 
-	  constexpr iterator_t<_Base>
+	  constexpr _Outer_iter
 	  operator->() const
-	    requires __detail::__has_arrow<iterator_t<_Base>>
-	      && copyable<iterator_t<_Base>>
+	    requires __detail::__has_arrow<_Outer_iter>
+	      && copyable<_Outer_iter>
 	  { return _M_inner; }
 
 	  constexpr _Iterator&
@@ -2412,8 +2420,8 @@ namespace views
 	  friend constexpr bool
 	  operator==(const _Iterator& __x, const _Iterator& __y)
 	    requires _S_ref_is_glvalue
-	      && equality_comparable<iterator_t<_Base>>
-	      && equality_comparable<iterator_t<range_reference_t<_Base>>>
+	      && equality_comparable<_Outer_iter>
+	      && equality_comparable<_Inner_iter>
 	  {
 	    return (__x._M_outer == __y._M_outer
 		    && __x._M_inner == __y._M_inner);
@@ -2764,7 +2772,8 @@ namespace views
 	  static constexpr auto
 	  _S_iter_cat()
 	  {
-	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    using _Cat
+              = typename iterator_traits<iterator_t<_Base>>::iterator_category;
 	    if constexpr (derived_from<_Cat, forward_iterator_tag>)
 	      return forward_iterator_tag{};
 	    else
@@ -2786,7 +2795,8 @@ namespace views
 	  bool _M_incremented = false;
 
 	public:
-	  using iterator_concept = typename _OuterIter<_Const>::iterator_concept;
+	  using iterator_concept
+	    = typename _OuterIter<_Const>::iterator_concept;
 	  using iterator_category = decltype(_S_iter_cat());
 	  using value_type = range_value_t<_Base>;
 	  using difference_type = range_difference_t<_Base>;

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2020-02-18 23:41 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-04  2:07 [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Patrick Palka
2020-02-04  2:08 ` [PATCH 3/3] libstdc++: Implement C++20 range adaptors Patrick Palka
2020-02-06 17:25   ` Jonathan Wakely
2020-02-06 23:53     ` Patrick Palka
2020-02-07 11:48       ` Jonathan Wakely
2020-02-07 14:28         ` Patrick Palka
2020-02-17 22:07   ` Stephan Bergmann
2020-02-18  1:20     ` Patrick Palka
2020-02-18  7:31       ` Stephan Bergmann
2020-02-18 11:56       ` Jonathan Wakely
2020-02-18 20:11         ` Stephan Bergmann
2020-02-18 23:41           ` Jonathan Wakely
2020-02-18 10:14     ` Jonathan Wakely
2020-02-04  2:09 ` [PATCH 2/3] libstdc++: Implement C++20 constrained algorithms Patrick Palka
2020-02-05 18:39   ` François Dumont
2020-02-05 19:25     ` Patrick Palka
2020-02-06 10:39       ` Jonathan Wakely
2020-02-06 12:36     ` Jonathan Wakely
2020-02-06 16:25   ` Jonathan Wakely
2020-02-07  1:13     ` Patrick Palka
2020-02-07 13:51   ` Jonathan Wakely
2020-02-13 18:07   ` François Dumont
2020-02-13 19:00     ` Jonathan Wakely
2020-02-14  6:00       ` François Dumont
2020-02-04 10:41 ` [PATCH 1/3] libstdc++: Apply the move_iterator changes described in P1207R4 Jonathan Wakely
2020-02-04 21:23   ` Patrick Palka
2020-02-04 21:49     ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).