Define midpoint and lerp functions for C++20 (P0811R3)
authorJonathan Wakely <jwakely@redhat.com>
Tue, 5 Mar 2019 18:37:24 +0000 (18:37 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 5 Mar 2019 18:37:24 +0000 (18:37 +0000)
The implementation of midpoint used for integral types is due to Howard
Hinnant and avoids a branch for int and larger types (but not for chars
and shorts).

The midpoint and lerp functions for floating point types come straight
from the P0811R3 proposal, with no attempt at optimization.

* include/c_compatibility/math.h [C++20] (lerp): Add using
declaration.
* include/c_global/cmath [C++20] (__cpp_lib_interpolate): Define.
(__lerp): Define function template to implement lerp.
(lerp(float, float, float), lerp(double, double, double))
(lerp(long double, long double, long double)): Define for C++20.
* include/std/numeric [C++20] (__cpp_lib_interpolate): Define.
(midpoint(T, T), midpoint(T*, T*)): Define.
* include/std::version [C++20] (__cpp_lib_interpolate): Define.
* testsuite/26_numerics/lerp.cc: New test.
* testsuite/26_numerics/midpoint/floating.cc: New test.
* testsuite/26_numerics/midpoint/integral.cc: New test.
* testsuite/26_numerics/midpoint/pointer.cc: New test.

From-SVN: r269398

libstdc++-v3/ChangeLog
libstdc++-v3/include/c_compatibility/math.h
libstdc++-v3/include/c_global/cmath
libstdc++-v3/include/std/numeric
libstdc++-v3/include/std/version
libstdc++-v3/testsuite/26_numerics/lerp.cc [new file with mode: 0644]
libstdc++-v3/testsuite/26_numerics/midpoint/floating.cc [new file with mode: 0644]
libstdc++-v3/testsuite/26_numerics/midpoint/integral.cc [new file with mode: 0644]
libstdc++-v3/testsuite/26_numerics/midpoint/pointer.cc [new file with mode: 0644]

index 1d3c7774fd9c142950bac8f910216a29d0e7f54b..44bf6e72c6502ccfdccc5f47bca62ecfb8e82786 100644 (file)
@@ -1,3 +1,19 @@
+2019-03-05  Jonathan Wakely  <jwakely@redhat.com>
+
+       * include/c_compatibility/math.h [C++20] (lerp): Add using
+       declaration.
+       * include/c_global/cmath [C++20] (__cpp_lib_interpolate): Define.
+       (__lerp): Define function template to implement lerp.
+       (lerp(float, float, float), lerp(double, double, double))
+       (lerp(long double, long double, long double)): Define for C++20.
+       * include/std/numeric [C++20] (__cpp_lib_interpolate): Define.
+       (midpoint(T, T), midpoint(T*, T*)): Define.
+       * include/std::version [C++20] (__cpp_lib_interpolate): Define.
+       * testsuite/26_numerics/lerp.cc: New test.
+       * testsuite/26_numerics/midpoint/floating.cc: New test.
+       * testsuite/26_numerics/midpoint/integral.cc: New test.
+       * testsuite/26_numerics/midpoint/pointer.cc: New test.
+
 2019-03-04  Edward Smith-Rowland  <3dw4rd@verizon.net>
 
        PR libstdc++/88996 Implement P0439R0
index 4253f25fc917a679749c234577743cdcffb9aeea..d9fe94ca26e4112e787e0f81daee0ccf39a7d47a 100644 (file)
@@ -177,5 +177,9 @@ using std::sph_neumannl;
 using std::sph_neumann;
 #endif // _GLIBCXX_USE_STD_SPEC_FUNCS
 
+#if __cplusplus > 201703L
+using std::lerp;
+#endif // C++20
+
 #endif // _GLIBCXX_MATH_H
 #endif // __cplusplus
index 121511cc8fb9dd28086a4b3cf735f5bc599f5ec6..b843c18f1dac67fd70a2bfc2da1323a01e3483e4 100644 (file)
@@ -1885,6 +1885,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 #endif // C++17
 
+#if __cplusplus > 201703L
+  // linear interpolation
+# define __cpp_lib_interpolate 201902L
+
+  template<typename _Fp>
+    constexpr _Fp
+    __lerp(_Fp __a, _Fp __b, _Fp __t)
+    {
+      if (__a <= 0 && __b >= 0 || __a >= 0 && __b <= 0)
+       return __t * __b + (1 - __t) * __a;
+
+      if (__t == 1)
+       return __b;                        // exact
+
+      // Exact at __t=0, monotonic except near __t=1,
+      // bounded, determinate, and consistent:
+      const _Fp __x = __a + __t * (__b - __a);
+      return __t > 1 == __b > __a
+       ? (__b < __x ? __x : __b)
+       : (__b > __x ? __x : __b);  // monotonic near __t=1
+    }
+
+  constexpr float
+  lerp(float __a, float __b, float __t)
+  { return std::__lerp(__a, __b, __t); }
+
+  constexpr double
+  lerp(double __a, double __b, double __t)
+  { return std::__lerp(__a, __b, __t); }
+
+  constexpr long double
+  lerp(long double __a, long double __b, long double __t)
+  { return std::__lerp(__a, __b, __t); }
+#endif // C++20
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
index c673e482812d96f9cad6adec7b250c9a69131799..ce02ee369f3698f4918811a796fc8040dcaa5517 100644 (file)
@@ -119,7 +119,7 @@ namespace __detail
     }
 } // namespace __detail
 
-#if __cplusplus > 201402L
+#if __cplusplus >= 201703L
 
 #define __cpp_lib_gcd_lcm 201606
 // These were used in drafts of SD-6:
@@ -156,6 +156,50 @@ namespace __detail
 
 #endif // C++17
 
+#if __cplusplus > 201703L
+  // midpoint
+# define __cpp_lib_interpolate 201902L
+
+template<typename _Tp>
+    constexpr
+    enable_if_t<__and_<is_arithmetic<_Tp>, is_same<remove_cv_t<_Tp>, _Tp>,
+                      __not_<is_same<_Tp, bool>>>::value,
+               _Tp>
+    midpoint(_Tp __a, _Tp __b) noexcept
+    {
+      if constexpr (is_integral_v<_Tp>)
+       {
+         using _Up = make_unsigned_t<_Tp>;
+
+         int __k = 1;
+         _Up __m = __a;
+         _Up __M = __b;
+         if (__a > __b)
+           {
+             __k = -1;
+             __m = __b;
+             __M = __a;
+           }
+         return __a + __k * _Tp(_Up(__M - __m) / 2);
+       }
+      else
+       {
+         return __builtin_isnormal(__a) && __builtin_isnormal(__b)
+           ? __a / 2 + __b / 2
+           : (__a + __b) / 2;
+       }
+    }
+
+  template<typename _Tp>
+    constexpr
+    enable_if_t<__and_<is_object<_Tp>, bool_constant<sizeof(_Tp) != 0>>::value,
+               _Tp*>
+    midpoint(_Tp* __a, _Tp* __b) noexcept
+    {
+      return __a > __b ? __b + (__a - __b) / 2 : __a + (__b - __a) / 2;
+    }
+#endif // C++20
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
index fa3d473759b1433691f71d853595041f1470dc06..785e8966dbfcd62c08e5ec445ef5dab4910765e0 100644 (file)
 # define __cpp_lib_destroying_delete 201806L
 #endif
 #define __cpp_lib_erase_if 201900L
+#define __cpp_lib_interpolate 201902L
 #ifdef _GLIBCXX_HAVE_BUILTIN_IS_CONSTANT_EVALUATED
 # define __cpp_lib_is_constant_evaluated 201811L
 #endif
diff --git a/libstdc++-v3/testsuite/26_numerics/lerp.cc b/libstdc++-v3/testsuite/26_numerics/lerp.cc
new file mode 100644 (file)
index 0000000..fae7483
--- /dev/null
@@ -0,0 +1,124 @@
+// 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 <cmath>
+
+#ifndef __cpp_lib_interpolate
+# error "Feature-test macro for midpoint and lerp missing"
+#elif __cpp_lib_interpolate != 201902L
+# error "Feature-test macro for midpoint and lerp has wrong value"
+#endif
+
+#include <limits>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using lim = std::numeric_limits<double>;
+
+  VERIFY( std::lerp(0.0, 1.0, 0.0) == 0.0 );
+  VERIFY( std::lerp(-2.0, 10.0, 1.0) == 10.0 );
+  VERIFY( std::lerp(2.0, -10.0, 1.0) == -10.0 );
+  VERIFY( std::lerp(-8.0, 10.0, 0.5) == 1.0 );
+  VERIFY( std::lerp(-10.0, 10.0, 0.25) == -5.0 );
+  VERIFY( std::lerp(10.0, -10.0, 0.375) == 2.5 );
+
+  VERIFY( std::lerp(2.0, 2.0, 200.0) == 2.0 );
+  VERIFY( std::lerp(2.0, 4.0, 200.0) == 402.0 );
+  VERIFY( std::lerp(2.0, 4.0, -20.0) == -38.0 );
+
+  VERIFY( std::lerp(1.1, 30201.1, 0) == 1.1 );
+  VERIFY( std::lerp(1.1, 30201.1, 1) == 30201.1 );
+  VERIFY( std::lerp(1.1, -30201.1, 0) == 1.1 );
+  VERIFY( std::lerp(1.1, -30201.1, 1) == -30201.1 );
+
+  VERIFY( std::lerp(1.1, 1.1, lim::infinity()) == 1.1 );
+  VERIFY( std::isfinite(std::lerp(1.1, 1.1+lim::min(), lim::max())) );
+
+  VERIFY( std::lerp(lim::max(), lim::max(), 1) == lim::max() );
+  VERIFY( std::lerp(lim::max(), lim::max()/9e9, 0) == lim::max() );
+  VERIFY( std::lerp(lim::max()/9e9, lim::max(), 1) == lim::max() );
+}
+
+void
+test02()
+{
+  using lim = std::numeric_limits<float>;
+
+  VERIFY( std::lerp(0.0f, 1.0f, 0.0f) == 0.0f );
+  VERIFY( std::lerp(-2.0f, 10.0f, 1.0f) == 10.0f );
+  VERIFY( std::lerp(2.0f, -10.0f, 1.0f) == -10.0f );
+  VERIFY( std::lerp(-8.0f, 10.0f, 0.5f) == 1.0f );
+  VERIFY( std::lerp(-10.0f, 10.0f, 0.25f) == -5.0f );
+  VERIFY( std::lerp(10.0f, -10.0f, 0.375f) == 2.5f );
+
+  VERIFY( std::lerp(2.0f, 2.0f, 200.0f) == 2.0f );
+  VERIFY( std::lerp(2.0f, 4.0f, 200.0f) == 402.0f );
+  VERIFY( std::lerp(2.0f, 4.0f, -20.0f) == -38.0f );
+
+  VERIFY( std::lerp(1.1f, 30201.1f, 0) == 1.1f );
+  VERIFY( std::lerp(1.1f, 30201.1f, 1) == 30201.1f );
+  VERIFY( std::lerp(1.1f, -30201.1f, 0) == 1.1f );
+  VERIFY( std::lerp(1.1f, -30201.1f, 1) == -30201.1f );
+
+  VERIFY( std::lerp(1.1f, 1.1f, lim::infinity()) == 1.1f );
+  VERIFY( std::isfinite(std::lerp(1.1f, 1.1f+lim::min(), lim::max())) );
+
+  VERIFY( std::lerp(lim::max(), lim::max(), 1) == lim::max() );
+  VERIFY( std::lerp(lim::max(), lim::max()/9e9f, 0) == lim::max() );
+  VERIFY( std::lerp(lim::max()/9e9f, lim::max(), 1) == lim::max() );
+}
+
+void
+test03()
+{
+  using lim = std::numeric_limits<long double>;
+
+  VERIFY( std::lerp(0.0l, 1.0l, 0.0l) == 0.0l );
+  VERIFY( std::lerp(-2.0l, 10.0l, 1.0l) == 10.0l );
+  VERIFY( std::lerp(2.0l, -10.0l, 1.0l) == -10.0l );
+  VERIFY( std::lerp(-8.0l, 10.0l, 0.5l) == 1.0l );
+  VERIFY( std::lerp(-10.0l, 10.0l, 0.25l) == -5.0l );
+  VERIFY( std::lerp(10.0l, -10.0l, 0.375l) == 2.5l );
+
+  VERIFY( std::lerp(2.0l, 2.0l, 200.0l) == 2.0l );
+  VERIFY( std::lerp(2.0l, 4.0l, 200.0l) == 402.0l );
+  VERIFY( std::lerp(2.0l, 4.0l, -20.0l) == -38.0l );
+
+  VERIFY( std::lerp(1.1l, 30201.1l, 0) == 1.1l );
+  VERIFY( std::lerp(1.1l, 30201.1l, 1) == 30201.1l );
+  VERIFY( std::lerp(1.1l, -30201.1l, 0) == 1.1l );
+  VERIFY( std::lerp(1.1l, -30201.1l, 1) == -30201.1l );
+
+  VERIFY( std::lerp(1.1l, 1.1l, lim::infinity()) == 1.1l );
+  VERIFY( std::isfinite(std::lerp(1.1l, 1.1l+lim::min(), lim::max())) );
+
+  VERIFY( std::lerp(lim::max(), lim::max(), 1) == lim::max() );
+  VERIFY( std::lerp(lim::max(), lim::max()/9e9l, 0) == lim::max() );
+  VERIFY( std::lerp(lim::max()/9e9l, lim::max(), 1) == lim::max() );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/26_numerics/midpoint/floating.cc b/libstdc++-v3/testsuite/26_numerics/midpoint/floating.cc
new file mode 100644 (file)
index 0000000..9c6e411
--- /dev/null
@@ -0,0 +1,65 @@
+// 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 <numeric>
+#include <limits>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using lim = std::numeric_limits<double>;
+
+  VERIFY( std::midpoint(2.0, 4.0) == 3.0 );
+  VERIFY( std::midpoint(0.0, 0.4) == 0.2 );
+  VERIFY( std::midpoint(0.0, -0.0) == 0.0 );
+  VERIFY( std::midpoint(9e9, -9e9) == 0.0 );
+
+  VERIFY( std::midpoint(lim::max(), lim::max()) == lim::max() );
+}
+
+void
+test02()
+{
+  using lim = std::numeric_limits<float>;
+
+  VERIFY( std::midpoint(2.0f, 4.0f) == 3.0f );
+  VERIFY( std::midpoint(0.0f, 0.4f) == 0.2f );
+  VERIFY( std::midpoint(0.0f, -0.0f) == 0.0f );
+  VERIFY( std::midpoint(9e9f, -9e9f) == 0.0f );
+}
+
+void
+test03()
+{
+  using lim = std::numeric_limits<long double>;
+
+  VERIFY( std::midpoint(2.0l, 4.0l) == 3.0l );
+  VERIFY( std::midpoint(0.0l, 0.4l) == 0.2l );
+  VERIFY( std::midpoint(0.0l, -0.0l) == 0.0l );
+  VERIFY( std::midpoint(9e9l, -9e9l) == 0.0l );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/26_numerics/midpoint/integral.cc b/libstdc++-v3/testsuite/26_numerics/midpoint/integral.cc
new file mode 100644 (file)
index 0000000..4fe66df
--- /dev/null
@@ -0,0 +1,121 @@
+// 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 <numeric>
+
+#ifndef __cpp_lib_interpolate
+# error "Feature-test macro for midpoint and lerp missing"
+#elif __cpp_lib_interpolate != 201902L
+# error "Feature-test macro for midpoint and lerp has wrong value"
+#endif
+
+#include <climits>
+#include <testsuite_hooks.h>
+
+static_assert(std::is_same_v<decltype(std::midpoint(0, 1)), int>);
+static_assert(noexcept(std::midpoint(1, 2)));
+
+struct test_type { };
+template<typename T> decltype(std::midpoint<T>(T(), T())) try_midpoint(int);
+template<typename T> test_type try_midpoint(...);
+template<typename T> constexpr bool no_midpoint()
+{ return std::is_same_v<decltype(try_midpoint<T>()), test_type>; }
+
+static_assert(no_midpoint<bool>());
+static_assert(no_midpoint<const bool>());
+static_assert(no_midpoint<const int>());
+static_assert(no_midpoint<volatile int>());
+
+static_assert( std::midpoint(0, 0) == 0 );
+static_assert( std::midpoint(1, 1) == 1 );
+static_assert( std::midpoint(0, 1) == 0 );
+static_assert( std::midpoint(1, 0) == 1 );
+static_assert( std::midpoint(0, 2) == 1 );
+static_assert( std::midpoint(3, 2) == 3 );
+static_assert( std::midpoint(-5, 4) == -1 );
+static_assert( std::midpoint(5, -4) == 1 );
+static_assert( std::midpoint(-5, -4) == -5 );
+static_assert( std::midpoint(-4, -5) == -4 );
+static_assert( std::midpoint(INT_MIN, INT_MAX) == -1 );
+static_assert( std::midpoint(INT_MAX, INT_MIN) == 0 );
+static_assert( std::midpoint(INT_MAX, INT_MAX) == INT_MAX );
+static_assert( std::midpoint(INT_MAX, INT_MAX-1) == INT_MAX );
+static_assert( std::midpoint(INT_MAX-1, INT_MAX-1) == INT_MAX-1 );
+static_assert( std::midpoint(INT_MAX-1, INT_MAX) == INT_MAX-1 );
+static_assert( std::midpoint(INT_MAX, INT_MAX-2) == INT_MAX-1 );
+
+static_assert( std::midpoint(0u, 0u) == 0 );
+static_assert( std::midpoint(0u, 1u) == 0 );
+static_assert( std::midpoint(1u, 0u) == 1 );
+static_assert( std::midpoint(0u, 2u) == 1 );
+static_assert( std::midpoint(3u, 2u) == 3 );
+static_assert( std::midpoint(0u, UINT_MAX) == UINT_MAX/2 );
+static_assert( std::midpoint(UINT_MAX, 0u) == (UINT_MAX/2 + 1) );
+static_assert( std::midpoint(UINT_MAX, UINT_MAX) == UINT_MAX );
+static_assert( std::midpoint(UINT_MAX, UINT_MAX-1) == UINT_MAX );
+static_assert( std::midpoint(UINT_MAX-1, UINT_MAX-1) == UINT_MAX-1 );
+static_assert( std::midpoint(UINT_MAX-1, UINT_MAX) == UINT_MAX-1 );
+static_assert( std::midpoint(UINT_MAX, UINT_MAX-2) == UINT_MAX-1 );
+
+static_assert( std::midpoint<short>(0, 0) == 0 );
+static_assert( std::midpoint<short>(0, 1) == 0 );
+static_assert( std::midpoint<short>(1, 0) == 1 );
+static_assert( std::midpoint<short>(0, 2) == 1 );
+static_assert( std::midpoint<short>(3, 2) == 3 );
+static_assert( std::midpoint<short>(-5, 4) == -1 );
+static_assert( std::midpoint<short>(5, -4) == 1 );
+static_assert( std::midpoint<short>(-5, -4) == -5 );
+static_assert( std::midpoint<short>(-4, -5) == -4 );
+static_assert( std::midpoint<short>(SHRT_MIN, SHRT_MAX) == -1 );
+static_assert( std::midpoint<short>(SHRT_MAX, SHRT_MIN) == 0 );
+static_assert( std::midpoint<short>(SHRT_MAX, SHRT_MAX) == SHRT_MAX );
+static_assert( std::midpoint<short>(SHRT_MAX, SHRT_MAX-1) == SHRT_MAX );
+static_assert( std::midpoint<short>(SHRT_MAX-1, SHRT_MAX-1) == SHRT_MAX-1 );
+static_assert( std::midpoint<short>(SHRT_MAX-1, SHRT_MAX) == SHRT_MAX-1 );
+static_assert( std::midpoint<short>(SHRT_MAX, SHRT_MAX-2) == SHRT_MAX-1 );
+
+static_assert( std::midpoint<signed char>(0, 0) == 0 );
+static_assert( std::midpoint<signed char>(1, 1) == 1 );
+static_assert( std::midpoint<signed char>(0, 1) == 0 );
+static_assert( std::midpoint<signed char>(1, 0) == 1 );
+static_assert( std::midpoint<signed char>(0, 2) == 1 );
+static_assert( std::midpoint<signed char>(3, 2) == 3 );
+static_assert( std::midpoint<signed char>(-5, 4) == -1 );
+static_assert( std::midpoint<signed char>(5, -4) == 1 );
+static_assert( std::midpoint<signed char>(-5, -4) == -5 );
+static_assert( std::midpoint<signed char>(-4, -5) == -4 );
+static_assert( std::midpoint<signed char>(SCHAR_MIN, SCHAR_MAX) == -1 );
+static_assert( std::midpoint<signed char>(SCHAR_MAX, SCHAR_MIN) == 0 );
+static_assert( std::midpoint<signed char>(SCHAR_MAX, SCHAR_MAX) == SCHAR_MAX );
+static_assert( std::midpoint<signed char>(SCHAR_MAX, SCHAR_MAX-1) == SCHAR_MAX);
+
+void
+test01()
+{
+  // Test every possibility for signed char.
+  for (int a = SCHAR_MIN; a <= SCHAR_MAX; ++a)
+    for (int b = SCHAR_MIN; b <= SCHAR_MAX; ++b)
+      VERIFY( std::midpoint(a, b) == std::midpoint<int>(a, b) );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/26_numerics/midpoint/pointer.cc b/libstdc++-v3/testsuite/26_numerics/midpoint/pointer.cc
new file mode 100644 (file)
index 0000000..bd586d2
--- /dev/null
@@ -0,0 +1,62 @@
+// 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 compile { target c++2a } }
+
+#include <numeric>
+#include <climits>
+#include <testsuite_hooks.h>
+
+const int* p = nullptr;
+static_assert(std::is_same_v<decltype(std::midpoint(p, p)), decltype(p)>);
+// This is a GNU extension:
+static_assert(noexcept(std::midpoint(p, p)));
+
+struct test_type { };
+template<typename T> decltype(std::midpoint((T*)0, (T*)0)) try_midpoint(int);
+template<typename T> test_type try_midpoint(...);
+template<typename T> constexpr bool no_midpoint()
+{ return std::is_same_v<decltype(try_midpoint<T>()), test_type>; }
+
+static_assert(no_midpoint<void>());
+static_assert(no_midpoint<int()>());
+static_assert(no_midpoint<int&>());
+static_assert(no_midpoint<struct Incomplete>());
+
+constexpr int ca[3] = {};
+static_assert( std::midpoint(ca, ca+3) == ca+1 );
+
+void
+test01()
+{
+  int a[4];
+  VERIFY( std::midpoint(a, a) == a );
+  VERIFY( std::midpoint(a, a+1) == a );
+  VERIFY( std::midpoint(a, a+2) == a+1 );
+  VERIFY( std::midpoint(a, a+3) == a+1 );
+  VERIFY( std::midpoint(a, a+4) == a+2 );
+  VERIFY( std::midpoint(a+1, a) == a+1 );
+  VERIFY( std::midpoint(a+2, a) == a+1 );
+  VERIFY( std::midpoint(a+3, a) == a+2 );
+  VERIFY( std::midpoint(a+4, a) == a+2 );
+}
+
+int main()
+{
+  test01();
+}