[libcxx-commits] [libcxxabi] [clang-tools-extra] [libunwind] [flang] [openmp] [llvm] [compiler-rt] [mlir] [libcxx] [clang] [libc] [polly] [lld] [lldb] [libc++][numeric] P0543R3: Saturation arithmetic (PR #77967)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jan 20 11:19:59 PST 2024


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/77967

>From 48c4463e8817c8ee0f00ffa7422e6fafbe838275 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 10 Jan 2024 13:46:19 +0200
Subject: [PATCH 01/38] [libc++][numeric] P0543R3: Saturation arithmetic

Implements: https://wg21.link/P0543R3
- https://eel.is/c++draft/numeric.sat

Additional notes:
- Division: https://eel.is/c++draft/expr.mul#4
- Arithmetic conversions: https://eel.is/c++draft/expr.arith.conv#1.5
- Clang builtins: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-functions
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/18.rst               |   7 +-
 libcxx/docs/Status/Cxx2cPapers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 .../include/__numeric/saturation_arithmetic.h | 135 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   1 +
 libcxx/include/numeric                        |  13 ++
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/numeric.inc                |  10 ++
 .../numeric.version.compile.pass.cpp          |  16 +--
 .../version.version.compile.pass.cpp          |  16 +--
 .../numeric.ops.sat/add_sat.pass.cpp          | 129 +++++++++++++++++
 .../numeric.ops.sat/add_sat.verify.cpp        |  39 +++++
 .../numeric.ops.sat/div_sat.assert.pass.cpp   |  53 +++++++
 .../numeric.ops.sat/div_sat.pass.cpp          | 108 ++++++++++++++
 .../numeric.ops.sat/div_sat.verify.cpp        |  39 +++++
 .../numeric.ops.sat/mul_sat.pass.cpp          | 119 +++++++++++++++
 .../numeric.ops.sat/mul_sat.verify.cpp        |  39 +++++
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 132 +++++++++++++++++
 .../numeric.ops.sat/saturate_cast.verify.cpp  |  44 ++++++
 .../numeric.ops.sat/sub_sat.pass.cpp          | 107 ++++++++++++++
 .../numeric.ops.sat/sub_sat.verify.cpp        |  39 +++++
 .../generate_feature_test_macro_components.py |   5 +-
 23 files changed, 1026 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__numeric/saturation_arithmetic.h
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 893a3b13ca06e02..9dd9c0c023bc8ad 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -432,7 +432,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_rcu``                                   *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_saturation_arithmetic``                 *unimplemented*
+    ``__cpp_lib_saturation_arithmetic``                 ``202311L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_smart_ptr_owner_equality``              *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 6de7d07e454d345..877e387d9280fd9 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -54,12 +54,13 @@ Implemented Papers
 - P2905R2 - Runtime format strings
 - P2918R2 - Runtime format strings II
 - P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26
-- P2870R3 - Remove basic_string::reserve()
+- P2870R3 - Remove ``basic_string::reserve()``
 - P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?)
-- P2821R5 - span.at()
-- P0521R0 - Proposed Resolution for CA 14 (shared_ptr use_count/unique)
+- P2821R5 - ``span.at()``
+- P0521R0 - Proposed Resolution for CA 14 (``shared_ptr`` ``use_count/unique``)
 - P1759R6 - Native handles and file streams
 - P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
+- P0543R3 - Saturation arithmetic
 
 
 Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 5701717f39766c6..b38b3028863feee 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -27,7 +27,7 @@
 "`P2714R1 <https://wg21.link/P2714R1>`__","LWG","Bind front and back to NTTP callables","Varna June 2023","","",""
 "`P2630R4 <https://wg21.link/P2630R4>`__","LWG","``submdspan``","Varna June 2023","","",""
 "","","","","","",""
-"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","","",""
+"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","|Complete|","18.0",""
 "`P2407R5 <https://wg21.link/P2407R5>`__","LWG","Freestanding Library: Partial Classes","Kona November 2023","","",""
 "`P2546R5 <https://wg21.link/P2546R5>`__","LWG","Debugging Support","Kona November 2023","","",""
 "`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","|Complete|","18.0","|format| |DR|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0fe3ab44d2466e9..c4d8a9f092de14d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -569,6 +569,7 @@ set(files
   __numeric/pstl_reduce.h
   __numeric/pstl_transform_reduce.h
   __numeric/reduce.h
+  __numeric/saturation_arithmetic.h
   __numeric/transform_exclusive_scan.h
   __numeric/transform_inclusive_scan.h
   __numeric/transform_reduce.h
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
new file mode 100644
index 000000000000000..61ae35f3ee02d50
--- /dev/null
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -0,0 +1,135 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
+#define _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
+
+#include <__concepts/arithmetic.h>
+#include <__config>
+#include <__type_traits/decay.h>
+#include <__utility/cmp.h>
+#include <limits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <typename _Tp>
+// concept __libcpp_standard_integer = __libcpp_unsigned_integer<_Tp> || __libcpp_signed_integer<_Tp>;
+// concept __libcpp_standard_integer =
+//     __libcpp_unsigned_integer<remove_cv<_Tp>> || __libcpp_signed_integer<remove_cv<_Tp>>;
+concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
+
+template <__libcpp_standard_integer _Tp>
+//   requires __libcpp_standard_integer<_Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+  // builtins: clang/docs/LanguageExtensions.rst
+  // builtins:
+  // https://github.com/llvm/llvm-project/blob/7b45c549670a8e8b6fe90f4382b0699dd20707d3/clang/docs/LanguageExtensions.rst#L3500
+  if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
+    return __sum;
+  // Handle overflow
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    return std::numeric_limits<_Tp>::max();
+  } else {
+    // Signed addition overflow
+    if (__x > 0)
+      // Overflows if (x > 0 && y > 0)
+      return std::numeric_limits<_Tp>::max();
+    else
+      // Overflows if  (x < 0 && y < 0)
+      return std::numeric_limits<_Tp>::min();
+  }
+}
+
+template <__libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+  if (_Tp __sub; !__builtin_sub_overflow(__x, __y, &__sub))
+    return __sub;
+  // Handle overflow
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    // Overflows if (x < y)
+    return std::numeric_limits<_Tp>::min();
+  } else {
+    // Signed subtration overflow
+    if (__x > 0)
+      // Overflows if (x > 0 && y < 0)
+      return std::numeric_limits<_Tp>::max();
+    else
+      // Overflows if (x < 0 && y > 0)
+      return std::numeric_limits<_Tp>::min();
+  }
+}
+
+template <__libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+  if (_Tp __mul; !__builtin_mul_overflow(__x, __y, &__mul))
+    return __mul;
+  // Handle overflow
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    return std::numeric_limits<_Tp>::max();
+  } else {
+    // Signed multiplication overflow
+    // if (__x > 0 && __y > 0)
+    //   // Overflows if (x > 0 && y > 0)
+    //   return std::numeric_limits<_Tp>::max();
+    // else if (__y > 0)
+    //   // Overflows if (x > 0 && y < 0)
+    //   return std::numeric_limits<_Tp>::max();
+    if (__x > 0) {
+      if (__y > 0)
+        // Overflows if (x > 0 && y > 0)
+        return std::numeric_limits<_Tp>::max();
+      // Overflows if (x > 0 && y < 0)
+      return std::numeric_limits<_Tp>::min();
+    }
+    if (__y > 0)
+      // Overflows if (x < 0 && y > 0)
+      return std::numeric_limits<_Tp>::min();
+    // Overflows if (x < 0 && y < 0)
+    return std::numeric_limits<_Tp>::max();
+  }
+}
+
+template <__libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+  _LIBCPP_ASSERT_UNCATEGORIZED(__y != 0, "Division by 0 is undefined");
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    return __x / __y;
+  } else {
+    // Handle signed division overflow
+    if (__x == std::numeric_limits<_Tp>::min() && __y == _Tp{-1})
+      return std::numeric_limits<_Tp>::max();
+    return __x / __y;
+  }
+}
+
+template <__libcpp_standard_integer _Rp, __libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+  // if (std::in_range<_Rp>(__x)) {
+  //   return _Rp{__x};
+  // }
+  // Handle overflow
+  if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
+    return std::numeric_limits<_Rp>::min();
+  if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
+    return std::numeric_limits<_Rp>::max();
+  // No overflow
+  return static_cast<_Rp>(__x);
+}
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d10670d4faaffc5..194a74a1e07b145 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1580,6 +1580,7 @@ module std_private_numeric_pstl_transform_reduce    [system] {
   export *
 }
 module std_private_numeric_reduce                   [system] { header "__numeric/reduce.h" }
+module std_private_numeric_saturation_arithmetic    [system] { header "__numeric/saturation_arithmetic.h" }
 module std_private_numeric_transform_exclusive_scan [system] { header "__numeric/transform_exclusive_scan.h" }
 module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
 module std_private_numeric_transform_reduce         [system] { header "__numeric/transform_reduce.h" }
diff --git a/libcxx/include/numeric b/libcxx/include/numeric
index d09d0a81fcc03a2..0fe7115f1c666e1 100644
--- a/libcxx/include/numeric
+++ b/libcxx/include/numeric
@@ -140,6 +140,18 @@ template<class T>
 template<class T>
     constexpr T* midpoint(T* a, T* b);        // C++20
 
+// [numeric.sat], saturation arithmetic
+template<class T>
+constexpr T add_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T>
+constexpr T sub_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T>
+constexpr T mul_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T>
+constexpr T div_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T, class U>
+constexpr T saturate_cast(U x) noexcept;                    // freestanding, Since C++26
+
 }  // std
 
 */
@@ -160,6 +172,7 @@ template<class T>
 #include <__numeric/pstl_reduce.h>
 #include <__numeric/pstl_transform_reduce.h>
 #include <__numeric/reduce.h>
+#include <__numeric/saturation_arithmetic.h>
 #include <__numeric/transform_exclusive_scan.h>
 #include <__numeric/transform_inclusive_scan.h>
 #include <__numeric/transform_reduce.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index c96647894dce630..b8d0c4e6f3c898c 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -504,7 +504,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_out_ptr                              202311L
 # define __cpp_lib_ratio                                202306L
 // # define __cpp_lib_rcu                                  202306L
-// # define __cpp_lib_saturation_arithmetic                202311L
+# define __cpp_lib_saturation_arithmetic                202311L
 // # define __cpp_lib_smart_ptr_owner_equality             202306L
 # define __cpp_lib_span_at                              202311L
 // # define __cpp_lib_span_initializer_list                202311L
diff --git a/libcxx/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc
index d2b7688d4e5f10a..0800319de820d9b 100644
--- a/libcxx/modules/std/numeric.inc
+++ b/libcxx/modules/std/numeric.inc
@@ -54,4 +54,14 @@ export namespace std {
 
   // [numeric.ops.midpoint], midpoint
   using std::midpoint;
+
+#if _LIBCPP_STD_VER >= 26
+  // [numeric.sat], saturation arithmetic
+  using std::add_sat;
+  using std::sub_sat;
+  using std::mul_sat;
+  using std::div_sat;
+  using std::saturate_cast;
+#endif
+
 } // namespace std
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
index b510eefc69a5d34..d132b7c7b9c4f53 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
@@ -263,17 +263,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should be defined in c++26"
-#   endif
-#   if __cpp_lib_saturation_arithmetic != 202311L
-#     error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_saturation_arithmetic
+#   error "__cpp_lib_saturation_arithmetic should be defined in c++26"
+# endif
+# if __cpp_lib_saturation_arithmetic != 202311L
+#   error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
 # endif
 
 #endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index d5a0839b30f8249..edb36da28bbe15a 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7167,17 +7167,11 @@
 #   error "__cpp_lib_sample should have the value 201603L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should be defined in c++26"
-#   endif
-#   if __cpp_lib_saturation_arithmetic != 202311L
-#     error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_saturation_arithmetic
+#   error "__cpp_lib_saturation_arithmetic should be defined in c++26"
+# endif
+# if __cpp_lib_saturation_arithmetic != 202311L
+#   error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
 # endif
 
 # ifndef __cpp_lib_scoped_lock
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
new file mode 100644
index 000000000000000..2964ce11d8b6542
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T add_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+template <typename IntegerT>
+constexpr bool test_signed() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::div_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{3}, IntegerT{4});
+    assert(sum == IntegerT{7});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-3}, IntegerT{4});
+    assert(sum == IntegerT{1});
+  }
+
+  // Saturation - max - both arguments positive
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+    assert(sum == maxVal);
+  }
+
+  // Saturation - min - both arguments negative
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{-4});
+    assert(sum == minVal);
+  }
+
+  return true;
+}
+
+template <typename IntegerT>
+constexpr bool test_unsigned() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::div_sat(minVal, maxVal)));
+
+  // No Saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{3}, IntegerT{4});
+    assert(sum == IntegerT{7});
+  }
+
+  // Saturation - max only
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+    assert(sum == maxVal);
+  }
+
+  return true;
+}
+
+constexpr bool test() {
+  // signed
+  test_signed<signed char>();
+  test_signed<short int>();
+  test_signed<int>();
+  test_signed<long int>();
+  test_signed<long long int>();
+  // unsigned
+  test_unsigned<unsigned char>();
+  test_unsigned<unsigned short int>();
+  test_unsigned<unsigned int>();
+  test_unsigned<unsigned long int>();
+  test_unsigned<unsigned long long int>();
+
+  return true;
+}
+
+// ADDITIONAL_COMPILE_FLAGS: -Wno-constant-conversion
+
+constexpr void cppreference_test() {
+  {
+    constexpr int a = std::add_sat(3, 4); // no saturation occurs, T = int
+    static_assert(a == 7);
+
+    constexpr unsigned char b = std::add_sat<unsigned char>(UCHAR_MAX, 4); // saturated
+    static_assert(b == UCHAR_MAX);
+
+    constexpr unsigned char c = std::add_sat(UCHAR_MAX, 4); // not saturated, T = int
+                                                            // add_sat(int, int) returns int tmp == 259,
+                                                            // then assignment truncates 259 % 256 == 3
+    static_assert(c == 3);
+
+    //  unsigned char d = std::add_sat(252, c); // Error: inconsistent deductions for T
+
+    constexpr unsigned char e = std::add_sat<unsigned char>(251, a); // saturated
+    static_assert(e == UCHAR_MAX);
+    // 251 is of type T = unsigned char, `a` is converted to unsigned char value;
+    // might yield an int -> unsigned char conversion warning for `a`
+
+    constexpr signed char f = std::add_sat<signed char>(-123, -3); // not saturated
+    static_assert(f == -126);
+
+    constexpr signed char g = std::add_sat<signed char>(-123, -13); // saturated
+    static_assert(g == std::numeric_limits<signed char>::min());    // g == -128
+  }
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  cppreference_test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
new file mode 100644
index 000000000000000..b05b30abea6fdb4
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T add_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cstdint>
+#include <numeric>
+
+#include "test_macros.h"
+
+template <typename IntegerT>
+constexpr void test_constraint() {
+  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
+  // expected-error@*:* 0-3 {{no matching function for call to 'add_sat'}}
+  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  [[maybe_unused]] auto sum = std::add_sat(IntegerT{3}, IntegerT{4});
+}
+
+constexpr bool test() {
+  test_constraint<bool>();
+  test_constraint<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint<wchar_t>();
+#endif
+  test_constraint<std::char16_t>();
+  test_constraint<std::char32_t>();
+
+  return true;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
new file mode 100644
index 000000000000000..98616d2c1c427c1
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: availability-verbose_abort-missing
+
+// <numeric>
+
+// template<class T>
+// constexpr T div_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+#include "check_assertion.h"
+
+template <typename IntegerT>
+constexpr void test() {
+  TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{3}, IntegerT{0}), "Division by 0 is undefined");
+}
+
+constexpr bool test() {
+  // signed
+  test<signed char>();
+  test<short int>();
+  test<int>();
+  test<long int>();
+  test<long long int>();
+  // unsigned
+  test<unsigned char>();
+  test<unsigned short int>();
+  test<unsigned int>();
+  test<unsigned long int>();
+  test<unsigned long long int>();
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
new file mode 100644
index 000000000000000..0411002d7c199e8
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T div_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+template <typename IntegerT>
+constexpr bool test_signed() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::div_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) div = std::div_sat(IntegerT{3}, IntegerT{4});
+    assert(div == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) div = std::div_sat(maxVal, minVal);
+    assert(div == (maxVal / minVal));
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
+    assert(div == (minVal / maxVal));
+  }
+
+  // Saturation - max only
+  {
+    std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, IntegerT{-1});
+    assert(div == maxVal);
+  }
+
+  return true;
+}
+
+template <typename IntegerT>
+constexpr bool test_unsigned() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::div_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) div = std::div_sat(IntegerT{3}, IntegerT{4});
+    assert(div == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
+    assert(div == (minVal / maxVal));
+  }
+
+  // Unsigned integer devision never overflow
+
+  return true;
+}
+
+constexpr bool test() {
+  // signed
+  test_signed<signed char>();
+  test_signed<short int>();
+  test_signed<int>();
+  test_signed<long int>();
+  test_signed<long long int>();
+  // unsigned
+  test_unsigned<unsigned char>();
+  test_unsigned<unsigned short int>();
+  test_unsigned<unsigned int>();
+  test_unsigned<unsigned long int>();
+  test_unsigned<unsigned long long int>();
+
+  return true;
+}
+
+constexpr void cppreference_test() {
+  {
+  static_assert("" && (std::div_sat<int>(6, 3) == 2)           // not saturated
+                && (std::div_sat<int>(INT_MIN, -1) == INT_MAX) // saturated
+                && (std::div_sat<unsigned>(6, 3) == 2)         // not saturated
+  );
+  }
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  cppreference_test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
new file mode 100644
index 000000000000000..e8ab8ef0c148f49
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T div_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cstdint>
+#include <numeric>
+
+#include "test_macros.h"
+
+template <typename IntegerT>
+constexpr void test_constraint() {
+  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
+  // expected-error@*:* 0-3 {{no matching function for call to 'div_sat'}}
+  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  [[maybe_unused]] auto sum = std::div_sat(IntegerT{3}, IntegerT{4});
+}
+
+constexpr bool test() {
+  test_constraint<bool>();
+  test_constraint<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint<wchar_t>();
+#endif
+  test_constraint<std::char16_t>();
+  test_constraint<std::char32_t>();
+
+  return true;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
new file mode 100644
index 000000000000000..1192b6b4a61dc0c
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T mul_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+template <typename IntegerT>
+constexpr bool test_signed() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::mul_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
+    assert(sum == IntegerT{12});
+  }
+
+  // Saturation - max - both arguments positive
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{4});
+    assert(sum == maxVal);
+  }
+
+  // Saturation - max - both arguments negative
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, IntegerT{-4});
+    assert(sum == maxVal);
+  }
+
+  // Saturation - min - left positive, right negative
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{-4});
+    assert(sum == minVal);
+  }
+
+  // Saturation - min - left negative, right positive
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, IntegerT{4});
+    assert(sum == minVal);
+  }
+
+  return true;
+}
+
+template <typename IntegerT>
+constexpr bool test_unsigned() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::mul_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
+    assert(sum == IntegerT{12});
+  }
+
+  // Saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{4});
+    assert(sum == maxVal);
+  }
+
+  return true;
+}
+
+constexpr bool test() {
+  // signed
+  test_signed<signed char>();
+  test_signed<short int>();
+  test_signed<int>();
+  test_signed<long int>();
+  test_signed<long long int>();
+  // unsigned
+  test_unsigned<unsigned char>();
+  test_unsigned<unsigned short int>();
+  test_unsigned<unsigned int>();
+  test_unsigned<unsigned long int>();
+  test_unsigned<unsigned long long int>();
+
+  return true;
+}
+
+constexpr void cppreference_test() {
+  {
+    static_assert(
+        "" && (std::mul_sat<int>(2, 3) == 6)                     // not saturated
+        && (std::mul_sat<int>(INT_MAX / 2, 3) == INT_MAX)        // saturated
+        && (std::mul_sat<int>(-2, 3) == -6)                      // not saturated
+        && (std::mul_sat<int>(INT_MIN / -2, -3) == INT_MIN)      // saturated
+        && (std::mul_sat<unsigned>(2, 3) == 6)                   // not saturated
+        && (std::mul_sat<unsigned>(UINT_MAX / 2, 3) == UINT_MAX) // saturated
+    );
+  }
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  cppreference_test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
new file mode 100644
index 000000000000000..8113be2fd23e6be
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T mul_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cstdint>
+#include <numeric>
+
+#include "test_macros.h"
+
+template <typename IntegerT>
+constexpr void test_constraint() {
+  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
+  // expected-error@*:* 0-3 {{no matching function for call to 'mul_sat'}}
+  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  [[maybe_unused]] auto sum = std::mul_sat(IntegerT{3}, IntegerT{4});
+}
+
+constexpr bool test() {
+  test_constraint<bool>();
+  test_constraint<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint<wchar_t>();
+#endif
+  test_constraint<std::char16_t>();
+  test_constraint<std::char32_t>();
+
+  return true;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
new file mode 100644
index 000000000000000..889b1864f128e53
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class R, class T>
+//   constexpr R saturate_cast(T x) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+#include <print>
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_signed_notsaturated() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+  assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
+  assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
+
+  return true;
+}
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_signed_saturated() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+  assert(std::saturate_cast<IntegerResultT>(minVal) == std::numeric_limits<IntegerResultT>::min());
+  assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+
+  return true;
+}
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_unsigned_notsaturated() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+  assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
+  assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
+
+  return true;
+}
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_unsigned_saturated() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+  assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+  assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+
+  return true;
+}
+
+constexpr bool test() {
+  // signed
+  test_signed_notsaturated<long long int, signed char>();
+  test_signed_notsaturated<long long int, short int>();
+  test_signed_notsaturated<long long int, int>();
+  test_signed_notsaturated<long long int, long int>();
+  test_signed_notsaturated<long long int, long long int>();
+  test_signed_saturated<signed char, long long int>();
+  test_signed_saturated<short int, long long int>();
+  test_signed_saturated<int, long long int>();
+  test_signed_saturated<long int, long long int>();
+  test_signed_saturated<long long int, long long int>();
+  // unsigned
+  test_unsigned_notsaturated<unsigned long long int, unsigned char>();
+  test_unsigned_notsaturated<unsigned long long int, unsigned short int>();
+  test_unsigned_notsaturated<unsigned long long int, unsigned int>();
+  test_unsigned_notsaturated<unsigned long long int, unsigned long int>();
+  test_unsigned_notsaturated<unsigned long long int, unsigned long long int>();
+  test_unsigned_saturated<unsigned char, unsigned long long int>();
+  test_unsigned_saturated<unsigned short int, unsigned long long int>();
+  test_unsigned_saturated<unsigned int, unsigned long long int>();
+  test_unsigned_saturated<unsigned long int, unsigned long long int>();
+  test_unsigned_saturated<unsigned long long int, unsigned long long int>();
+
+  return true;
+}
+
+constexpr void cppreference_test() {
+  {
+    constexpr std::int16_t x1{696};
+
+    constexpr std::int8_t x2 = std::saturate_cast<std::int8_t>(x1);
+    static_assert(x2 == std::numeric_limits<std::int8_t>::max());
+
+    constexpr std::uint8_t x3 = std::saturate_cast<std::uint8_t>(x1);
+    static_assert(x3 == std::numeric_limits<std::uint8_t>::max());
+
+    constexpr std::int16_t y1{-696};
+
+    constexpr std::int8_t y2 = std::saturate_cast<std::int8_t>(y1);
+    static_assert(y2 == std::numeric_limits<std::int8_t>::min());
+
+    constexpr std::uint8_t y3 = std::saturate_cast<std::uint8_t>(y1);
+    static_assert(y3 == 0);
+  }
+}
+
+int main(int, char**) {
+  test();
+  // static_assert(test());
+  cppreference_test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
new file mode 100644
index 000000000000000..d8895225e7468ee
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class R, class T>
+//   constexpr R saturate_cast(T x) noexcept;                    // freestanding
+
+#include <cstdint>
+#include <numeric>
+
+#include "test_macros.h"
+
+template <typename ResultIntegerT, typename IntegerT>
+constexpr void test_constraint() {
+  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
+  // expected-error@*:* 0-6 {{no matching function for call to 'saturate_cast'}}
+  // expected-error@*:* 0-4 {{expected unqualified-id}}
+  [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{4});
+}
+
+constexpr bool test() {
+  test_constraint<bool, int>();
+  test_constraint<char, int>();
+  test_constraint<int, bool>();
+  test_constraint<int, char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint<wchar_t, int>();
+  test_constraint<int, wchar_t>();
+#endif
+  test_constraint<std::char16_t, int>();
+  test_constraint<std::char32_t, int>();
+  test_constraint<int, std::char16_t>();
+  test_constraint<int, std::char32_t>();
+
+  return true;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
new file mode 100644
index 000000000000000..a3f0d175ec350dd
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T sub_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+template <typename IntegerT>
+constexpr bool test_signed() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::sub_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{4});
+    assert(sum == IntegerT{-1});
+  }
+
+  // Saturation - min - left negative, right positive
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
+    assert(sum == minVal);
+  }
+
+  // Saturation - max - left postitive, right negative
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(maxVal, IntegerT{-4});
+    assert(sum == maxVal);
+  }
+
+  return true;
+}
+
+template <typename IntegerT>
+constexpr bool test_unsigned() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::sub_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{1});
+    assert(sum == IntegerT{2});
+  }
+
+  // Saturation - min only
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
+    assert(sum == minVal);
+  }
+
+  return true;
+}
+
+constexpr bool test() {
+  // signed
+  test_signed<signed char>();
+  test_signed<short int>();
+  test_signed<int>();
+  test_signed<long int>();
+  test_signed<long long int>();
+  // unsigned
+  test_unsigned<unsigned char>();
+  test_unsigned<unsigned short int>();
+  test_unsigned<unsigned int>();
+  test_unsigned<unsigned long int>();
+  test_unsigned<unsigned long long int>();
+
+  return true;
+}
+
+constexpr void cppreference_test() {
+  {
+    static_assert(
+        "" && (std::sub_sat<int>(INT_MIN + 4, 3) == INT_MIN + 1) // not saturated
+        && (std::sub_sat<int>(INT_MIN + 4, 5) == INT_MIN)        // saturated
+        && (std::sub_sat<int>(INT_MAX - 4, -3) == INT_MAX - 1)   // not saturated
+        && (std::sub_sat<int>(INT_MAX - 4, -5) == INT_MAX)       // saturated
+        && (std::sub_sat<unsigned>(4, 3) == 1)                   // not saturated
+        && (std::sub_sat<unsigned>(4, 5) == 0)                   // saturated
+    );
+  }
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  cppreference_test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
new file mode 100644
index 000000000000000..a8e85975ab1a1c1
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T sub_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cstdint>
+#include <numeric>
+
+#include "test_macros.h"
+
+template <typename IntegerT>
+constexpr void test_constraint() {
+  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
+  // expected-error@*:* 0-3 {{no matching function for call to 'sub_sat'}}
+  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  [[maybe_unused]] auto sum = std::sub_sat(IntegerT{3}, IntegerT{4});
+}
+
+constexpr bool test() {
+  test_constraint<bool>();
+  test_constraint<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint<wchar_t>();
+#endif
+  test_constraint<std::char16_t>();
+  ttest_constraintest<std::char32_t>();
+
+  return true;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 8ee92909dfa53c2..6e7e4c2bb478d7b 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1010,10 +1010,7 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_saturation_arithmetic",
             "values": {"c++26": 202311},  # P0543R3 Saturation arithmetic
-            "headers": [
-                "numeric"  # TODO verify this entry since the paper was underspecified.
-            ],
-            "unimplemented": True,
+            "headers": ["numeric"],
         },
         {
             "name": "__cpp_lib_scoped_lock",

>From 2c939ccf4da507e6eabcd370966eddb105815670 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 12 Jan 2024 21:19:39 +0200
Subject: [PATCH 02/38] Cleanup

---
 .../include/__numeric/saturation_arithmetic.h | 15 --------
 .../numeric.ops.sat/add_sat.pass.cpp          | 35 ++-----------------
 .../numeric.ops.sat/div_sat.pass.cpp          | 14 ++------
 .../numeric.ops.sat/mul_sat.pass.cpp          | 18 ++--------
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 29 ++-------------
 .../numeric.ops.sat/sub_sat.pass.cpp          | 18 ++--------
 6 files changed, 11 insertions(+), 118 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 61ae35f3ee02d50..ec265435eb6a64d 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -25,17 +25,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #if _LIBCPP_STD_VER >= 26
 
 template <typename _Tp>
-// concept __libcpp_standard_integer = __libcpp_unsigned_integer<_Tp> || __libcpp_signed_integer<_Tp>;
-// concept __libcpp_standard_integer =
-//     __libcpp_unsigned_integer<remove_cv<_Tp>> || __libcpp_signed_integer<remove_cv<_Tp>>;
 concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
 
 template <__libcpp_standard_integer _Tp>
 //   requires __libcpp_standard_integer<_Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
-  // builtins: clang/docs/LanguageExtensions.rst
-  // builtins:
-  // https://github.com/llvm/llvm-project/blob/7b45c549670a8e8b6fe90f4382b0699dd20707d3/clang/docs/LanguageExtensions.rst#L3500
   if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
     return __sum;
   // Handle overflow
@@ -80,12 +74,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
     return std::numeric_limits<_Tp>::max();
   } else {
     // Signed multiplication overflow
-    // if (__x > 0 && __y > 0)
-    //   // Overflows if (x > 0 && y > 0)
-    //   return std::numeric_limits<_Tp>::max();
-    // else if (__y > 0)
-    //   // Overflows if (x > 0 && y < 0)
-    //   return std::numeric_limits<_Tp>::max();
     if (__x > 0) {
       if (__y > 0)
         // Overflows if (x > 0 && y > 0)
@@ -116,9 +104,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
 
 template <__libcpp_standard_integer _Rp, __libcpp_standard_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
-  // if (std::in_range<_Rp>(__x)) {
-  //   return _Rp{__x};
-  // }
   // Handle overflow
   if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
     return std::numeric_limits<_Rp>::min();
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index 2964ce11d8b6542..ccd3594fd3a6524 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -74,13 +74,13 @@ constexpr bool test_unsigned() {
 }
 
 constexpr bool test() {
-  // signed
+  // Signed
   test_signed<signed char>();
   test_signed<short int>();
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-  // unsigned
+  // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
@@ -90,40 +90,9 @@ constexpr bool test() {
   return true;
 }
 
-// ADDITIONAL_COMPILE_FLAGS: -Wno-constant-conversion
-
-constexpr void cppreference_test() {
-  {
-    constexpr int a = std::add_sat(3, 4); // no saturation occurs, T = int
-    static_assert(a == 7);
-
-    constexpr unsigned char b = std::add_sat<unsigned char>(UCHAR_MAX, 4); // saturated
-    static_assert(b == UCHAR_MAX);
-
-    constexpr unsigned char c = std::add_sat(UCHAR_MAX, 4); // not saturated, T = int
-                                                            // add_sat(int, int) returns int tmp == 259,
-                                                            // then assignment truncates 259 % 256 == 3
-    static_assert(c == 3);
-
-    //  unsigned char d = std::add_sat(252, c); // Error: inconsistent deductions for T
-
-    constexpr unsigned char e = std::add_sat<unsigned char>(251, a); // saturated
-    static_assert(e == UCHAR_MAX);
-    // 251 is of type T = unsigned char, `a` is converted to unsigned char value;
-    // might yield an int -> unsigned char conversion warning for `a`
-
-    constexpr signed char f = std::add_sat<signed char>(-123, -3); // not saturated
-    static_assert(f == -126);
-
-    constexpr signed char g = std::add_sat<signed char>(-123, -13); // saturated
-    static_assert(g == std::numeric_limits<signed char>::min());    // g == -128
-  }
-}
-
 int main(int, char**) {
   test();
   static_assert(test());
-  cppreference_test();
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 0411002d7c199e8..d2891f77c564fa6 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -74,13 +74,13 @@ constexpr bool test_unsigned() {
 }
 
 constexpr bool test() {
-  // signed
+  // Signed
   test_signed<signed char>();
   test_signed<short int>();
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-  // unsigned
+  // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
@@ -90,19 +90,9 @@ constexpr bool test() {
   return true;
 }
 
-constexpr void cppreference_test() {
-  {
-  static_assert("" && (std::div_sat<int>(6, 3) == 2)           // not saturated
-                && (std::div_sat<int>(INT_MIN, -1) == INT_MAX) // saturated
-                && (std::div_sat<unsigned>(6, 3) == 2)         // not saturated
-  );
-  }
-}
-
 int main(int, char**) {
   test();
   static_assert(test());
-  cppreference_test();
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 1192b6b4a61dc0c..2fbecd2fa55ff60 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -81,13 +81,13 @@ constexpr bool test_unsigned() {
 }
 
 constexpr bool test() {
-  // signed
+  // Signed
   test_signed<signed char>();
   test_signed<short int>();
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-  // unsigned
+  // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
@@ -97,23 +97,9 @@ constexpr bool test() {
   return true;
 }
 
-constexpr void cppreference_test() {
-  {
-    static_assert(
-        "" && (std::mul_sat<int>(2, 3) == 6)                     // not saturated
-        && (std::mul_sat<int>(INT_MAX / 2, 3) == INT_MAX)        // saturated
-        && (std::mul_sat<int>(-2, 3) == -6)                      // not saturated
-        && (std::mul_sat<int>(INT_MIN / -2, -3) == INT_MIN)      // saturated
-        && (std::mul_sat<unsigned>(2, 3) == 6)                   // not saturated
-        && (std::mul_sat<unsigned>(UINT_MAX / 2, 3) == UINT_MAX) // saturated
-    );
-  }
-}
-
 int main(int, char**) {
   test();
   static_assert(test());
-  cppreference_test();
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 889b1864f128e53..7e75903c46b109c 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -18,8 +18,6 @@
 #include <limits>
 #include <numeric>
 
-#include <print>
-
 template <typename IntegerResultT, typename IntegerT>
 constexpr bool test_signed_notsaturated() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
@@ -77,7 +75,7 @@ constexpr bool test_unsigned_saturated() {
 }
 
 constexpr bool test() {
-  // signed
+  // Signed
   test_signed_notsaturated<long long int, signed char>();
   test_signed_notsaturated<long long int, short int>();
   test_signed_notsaturated<long long int, int>();
@@ -88,7 +86,7 @@ constexpr bool test() {
   test_signed_saturated<int, long long int>();
   test_signed_saturated<long int, long long int>();
   test_signed_saturated<long long int, long long int>();
-  // unsigned
+  // Unsigned
   test_unsigned_notsaturated<unsigned long long int, unsigned char>();
   test_unsigned_notsaturated<unsigned long long int, unsigned short int>();
   test_unsigned_notsaturated<unsigned long long int, unsigned int>();
@@ -103,30 +101,9 @@ constexpr bool test() {
   return true;
 }
 
-constexpr void cppreference_test() {
-  {
-    constexpr std::int16_t x1{696};
-
-    constexpr std::int8_t x2 = std::saturate_cast<std::int8_t>(x1);
-    static_assert(x2 == std::numeric_limits<std::int8_t>::max());
-
-    constexpr std::uint8_t x3 = std::saturate_cast<std::uint8_t>(x1);
-    static_assert(x3 == std::numeric_limits<std::uint8_t>::max());
-
-    constexpr std::int16_t y1{-696};
-
-    constexpr std::int8_t y2 = std::saturate_cast<std::int8_t>(y1);
-    static_assert(y2 == std::numeric_limits<std::int8_t>::min());
-
-    constexpr std::uint8_t y3 = std::saturate_cast<std::uint8_t>(y1);
-    static_assert(y3 == 0);
-  }
-}
-
 int main(int, char**) {
   test();
-  // static_assert(test());
-  cppreference_test();
+  static_assert(test());
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index a3f0d175ec350dd..be337dae275d91d 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -69,13 +69,13 @@ constexpr bool test_unsigned() {
 }
 
 constexpr bool test() {
-  // signed
+  // Signed
   test_signed<signed char>();
   test_signed<short int>();
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-  // unsigned
+  // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
@@ -85,23 +85,9 @@ constexpr bool test() {
   return true;
 }
 
-constexpr void cppreference_test() {
-  {
-    static_assert(
-        "" && (std::sub_sat<int>(INT_MIN + 4, 3) == INT_MIN + 1) // not saturated
-        && (std::sub_sat<int>(INT_MIN + 4, 5) == INT_MIN)        // saturated
-        && (std::sub_sat<int>(INT_MAX - 4, -3) == INT_MAX - 1)   // not saturated
-        && (std::sub_sat<int>(INT_MAX - 4, -5) == INT_MAX)       // saturated
-        && (std::sub_sat<unsigned>(4, 3) == 1)                   // not saturated
-        && (std::sub_sat<unsigned>(4, 5) == 0)                   // saturated
-    );
-  }
-}
-
 int main(int, char**) {
   test();
   static_assert(test());
-  cppreference_test();
 
   return 0;
 }

>From f5db95e38044caaf7164dd9bb73ba2250f0da9ae Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 12 Jan 2024 21:30:22 +0200
Subject: [PATCH 03/38] Fixed "code formatter" error

---
 libcxx/modules/std/numeric.inc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc
index 0800319de820d9b..3bc7b2316815841 100644
--- a/libcxx/modules/std/numeric.inc
+++ b/libcxx/modules/std/numeric.inc
@@ -58,10 +58,10 @@ export namespace std {
 #if _LIBCPP_STD_VER >= 26
   // [numeric.sat], saturation arithmetic
   using std::add_sat;
-  using std::sub_sat;
-  using std::mul_sat;
   using std::div_sat;
+  using std::mul_sat;
   using std::saturate_cast;
+  using std::sub_sat;
 #endif
 
 } // namespace std

>From db3e72e22de261712f924f62929a6d499d1827d6 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 12 Jan 2024 23:28:21 +0200
Subject: [PATCH 04/38] Cleanup

---
 libcxx/include/__numeric/saturation_arithmetic.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index ec265435eb6a64d..6d13ec5b3fd5bcd 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -28,7 +28,6 @@ template <typename _Tp>
 concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
 
 template <__libcpp_standard_integer _Tp>
-//   requires __libcpp_standard_integer<_Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
     return __sum;

>From 30a159394380814e603ee1720f7e62d911e284b8 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 13 Jan 2024 21:27:23 +0200
Subject: [PATCH 05/38] WIP: prepare to address comments

---
 libcxx/include/__concepts/arithmetic.h         |  4 ++++
 .../include/__numeric/saturation_arithmetic.h  | 15 +++++----------
 .../numeric.ops.sat/add_sat.verify.cpp         | 12 ++++++++----
 .../numeric.ops.sat/div_sat.verify.cpp         | 12 ++++++++----
 .../numeric.ops.sat/mul_sat.verify.cpp         | 12 ++++++++----
 .../numeric.ops.sat/saturate_cast.verify.cpp   | 18 +++++++++++++-----
 .../numeric.ops.sat/sub_sat.verify.cpp         |  9 ++++++---
 7 files changed, 52 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__concepts/arithmetic.h b/libcxx/include/__concepts/arithmetic.h
index f41e4c9f2747cc3..f7efd31cf7792dc 100644
--- a/libcxx/include/__concepts/arithmetic.h
+++ b/libcxx/include/__concepts/arithmetic.h
@@ -42,9 +42,13 @@ concept floating_point = is_floating_point_v<_Tp>;
 
 template <class _Tp>
 concept __libcpp_unsigned_integer = __libcpp_is_unsigned_integer<_Tp>::value;
+
 template <class _Tp>
 concept __libcpp_signed_integer = __libcpp_is_signed_integer<_Tp>::value;
 
+template <typename _Tp>
+concept __libcpp_integer = __libcpp_unsigned_integer<_Tp> || __libcpp_signed_integer<_Tp>;
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 6d13ec5b3fd5bcd..8069d2574a91b2a 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -12,7 +12,6 @@
 
 #include <__concepts/arithmetic.h>
 #include <__config>
-#include <__type_traits/decay.h>
 #include <__utility/cmp.h>
 #include <limits>
 
@@ -23,11 +22,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26
-
-template <typename _Tp>
-concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
-
-template <__libcpp_standard_integer _Tp>
+template <__libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
     return __sum;
@@ -45,7 +40,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   }
 }
 
-template <__libcpp_standard_integer _Tp>
+template <__libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   if (_Tp __sub; !__builtin_sub_overflow(__x, __y, &__sub))
     return __sub;
@@ -64,7 +59,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   }
 }
 
-template <__libcpp_standard_integer _Tp>
+template <__libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   if (_Tp __mul; !__builtin_mul_overflow(__x, __y, &__mul))
     return __mul;
@@ -88,7 +83,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   }
 }
 
-template <__libcpp_standard_integer _Tp>
+template <__libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   _LIBCPP_ASSERT_UNCATEGORIZED(__y != 0, "Division by 0 is undefined");
   if constexpr (__libcpp_unsigned_integer<_Tp>) {
@@ -101,7 +96,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   }
 }
 
-template <__libcpp_standard_integer _Rp, __libcpp_standard_integer _Tp>
+template <__libcpp_integer _Rp, __libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
   // Handle overflow
   if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
index b05b30abea6fdb4..2c40d28e50b25a5 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
@@ -21,8 +21,8 @@
 template <typename IntegerT>
 constexpr void test_constraint() {
   // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-3 {{no matching function for call to 'add_sat'}}
-  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  // expected-error@*:* 0-9 {{no matching function for call to 'add_sat'}}
+  // expected-error@*:* 0-3 {{expected unqualified-id}}
   [[maybe_unused]] auto sum = std::add_sat(IntegerT{3}, IntegerT{4});
 }
 
@@ -32,8 +32,12 @@ constexpr bool test() {
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test_constraint<wchar_t>();
 #endif
-  test_constraint<std::char16_t>();
-  test_constraint<std::char32_t>();
+  test_constraint<char8_t>();
+  test_constraint<char16_t>();
+  test_constraint<char32_t>();
+  test_constraint<float>();
+  test_constraint<double>();
+  test_constraint<long double>();
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
index e8ab8ef0c148f49..65bce891b866557 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
@@ -21,8 +21,8 @@
 template <typename IntegerT>
 constexpr void test_constraint() {
   // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-3 {{no matching function for call to 'div_sat'}}
-  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  // expected-error@*:* 0-9 {{no matching function for call to 'div_sat'}}
+  // expected-error@*:* 0-3 {{expected unqualified-id}}
   [[maybe_unused]] auto sum = std::div_sat(IntegerT{3}, IntegerT{4});
 }
 
@@ -32,8 +32,12 @@ constexpr bool test() {
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test_constraint<wchar_t>();
 #endif
-  test_constraint<std::char16_t>();
-  test_constraint<std::char32_t>();
+  test_constraint<char8_t>();
+  test_constraint<char16_t>();
+  test_constraint<char32_t>();
+  test_constraint<float>();
+  test_constraint<double>();
+  test_constraint<long double>();
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
index 8113be2fd23e6be..fbc3c53d8a6c1a4 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
@@ -21,8 +21,8 @@
 template <typename IntegerT>
 constexpr void test_constraint() {
   // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-3 {{no matching function for call to 'mul_sat'}}
-  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  // expected-error@*:* 0-9 {{no matching function for call to 'mul_sat'}}
+  // expected-error@*:* 0-3 {{expected unqualified-id}}
   [[maybe_unused]] auto sum = std::mul_sat(IntegerT{3}, IntegerT{4});
 }
 
@@ -32,8 +32,12 @@ constexpr bool test() {
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test_constraint<wchar_t>();
 #endif
-  test_constraint<std::char16_t>();
-  test_constraint<std::char32_t>();
+  test_constraint<char8_t>();
+  test_constraint<char16_t>();
+  test_constraint<char32_t>();
+  test_constraint<float>();
+  test_constraint<double>();
+  test_constraint<long double>();
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
index d8895225e7468ee..ed2aee5e90c249d 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
@@ -21,7 +21,7 @@
 template <typename ResultIntegerT, typename IntegerT>
 constexpr void test_constraint() {
   // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-6 {{no matching function for call to 'saturate_cast'}}
+  // expected-error@*:* 0-25 {{no matching function for call to 'saturate_cast'}}
   // expected-error@*:* 0-4 {{expected unqualified-id}}
   [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{4});
 }
@@ -35,10 +35,18 @@ constexpr bool test() {
   test_constraint<wchar_t, int>();
   test_constraint<int, wchar_t>();
 #endif
-  test_constraint<std::char16_t, int>();
-  test_constraint<std::char32_t, int>();
-  test_constraint<int, std::char16_t>();
-  test_constraint<int, std::char32_t>();
+  test_constraint<char8_t, int>();
+  test_constraint<char16_t, int>();
+  test_constraint<char32_t, int>();
+  test_constraint<float, int>();
+  test_constraint<double, int>();
+  test_constraint<long double, int>();
+  test_constraint<int, char8_t>();
+  test_constraint<int, char16_t>();
+  test_constraint<int, char32_t>();
+  test_constraint<int, float>();
+  test_constraint<int, double>();
+  test_constraint<int, long double>();
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
index a8e85975ab1a1c1..034889373d156f6 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
@@ -21,7 +21,7 @@
 template <typename IntegerT>
 constexpr void test_constraint() {
   // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-3 {{no matching function for call to 'sub_sat'}}
+  // expected-error@*:* 0-9 {{no matching function for call to 'sub_sat'}}
   // expected-error@*:* 0-2 {{expected unqualified-id}}
   [[maybe_unused]] auto sum = std::sub_sat(IntegerT{3}, IntegerT{4});
 }
@@ -32,8 +32,11 @@ constexpr bool test() {
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test_constraint<wchar_t>();
 #endif
-  test_constraint<std::char16_t>();
-  ttest_constraintest<std::char32_t>();
+  test_constraint<char16_t>();
+  test_constraint<char32_t>();
+  test_constraint<float>();
+  test_constraint<double>();
+  test_constraint<long double>();
 
   return true;
 }

>From 2793acbef4e277ee72b854efa77b615402bf72a7 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 13 Jan 2024 22:54:35 +0200
Subject: [PATCH 06/38] WIP: Cleaned up tests

---
 .../numeric.ops/numeric.ops.sat/add_sat.verify.cpp         | 6 ++----
 .../numeric.ops/numeric.ops.sat/div_sat.verify.cpp         | 6 ++----
 .../numeric.ops/numeric.ops.sat/mul_sat.verify.cpp         | 6 ++----
 .../numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp   | 6 ++----
 .../numeric.ops/numeric.ops.sat/sub_sat.verify.cpp         | 7 +++----
 5 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
index 2c40d28e50b25a5..7df73043c922bdd 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
@@ -20,10 +20,8 @@
 
 template <typename IntegerT>
 constexpr void test_constraint() {
-  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-9 {{no matching function for call to 'add_sat'}}
-  // expected-error@*:* 0-3 {{expected unqualified-id}}
-  [[maybe_unused]] auto sum = std::add_sat(IntegerT{3}, IntegerT{4});
+  // expected-error@*:* 8-9 {{no matching function for call to 'add_sat'}}
+  [[maybe_unused]] auto sum = std::add_sat(IntegerT{}, IntegerT{});
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
index 65bce891b866557..5db6efd4ce397ea 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
@@ -20,10 +20,8 @@
 
 template <typename IntegerT>
 constexpr void test_constraint() {
-  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-9 {{no matching function for call to 'div_sat'}}
-  // expected-error@*:* 0-3 {{expected unqualified-id}}
-  [[maybe_unused]] auto sum = std::div_sat(IntegerT{3}, IntegerT{4});
+  // expected-error@*:* 8-9 {{no matching function for call to 'div_sat'}}
+  [[maybe_unused]] auto sum = std::div_sat(IntegerT{}, IntegerT{});
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
index fbc3c53d8a6c1a4..fcc24c09b7b9b40 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
@@ -20,10 +20,8 @@
 
 template <typename IntegerT>
 constexpr void test_constraint() {
-  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-9 {{no matching function for call to 'mul_sat'}}
-  // expected-error@*:* 0-3 {{expected unqualified-id}}
-  [[maybe_unused]] auto sum = std::mul_sat(IntegerT{3}, IntegerT{4});
+  // expected-error@*:* 8-9 {{no matching function for call to 'mul_sat'}}
+  [[maybe_unused]] auto sum = std::mul_sat(IntegerT{}, IntegerT{});
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
index ed2aee5e90c249d..379628b5664841f 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
@@ -20,10 +20,8 @@
 
 template <typename ResultIntegerT, typename IntegerT>
 constexpr void test_constraint() {
-  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-25 {{no matching function for call to 'saturate_cast'}}
-  // expected-error@*:* 0-4 {{expected unqualified-id}}
-  [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{4});
+  // expected-error@*:* 16-18 {{no matching function for call to 'saturate_cast'}}
+  [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{});
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
index 034889373d156f6..1ca5ca329b6c9a6 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
@@ -20,10 +20,8 @@
 
 template <typename IntegerT>
 constexpr void test_constraint() {
-  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
-  // expected-error@*:* 0-9 {{no matching function for call to 'sub_sat'}}
-  // expected-error@*:* 0-2 {{expected unqualified-id}}
-  [[maybe_unused]] auto sum = std::sub_sat(IntegerT{3}, IntegerT{4});
+  // expected-error@*:* 8-9 {{no matching function for call to 'sub_sat'}}
+  [[maybe_unused]] auto sum = std::sub_sat(IntegerT{}, IntegerT{});
 }
 
 constexpr bool test() {
@@ -32,6 +30,7 @@ constexpr bool test() {
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test_constraint<wchar_t>();
 #endif
+  test_constraint<char8_t>();
   test_constraint<char16_t>();
   test_constraint<char32_t>();
   test_constraint<float>();

>From 540cfec4a90bf628d6767b255218117f0b107e08 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 13 Jan 2024 23:39:24 +0200
Subject: [PATCH 07/38] WIP: Test `constexpr`

---
 .../numeric.ops.sat/div_sat.assert.pass.cpp   | 50 +++++++++++++------
 1 file changed, 36 insertions(+), 14 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
index 98616d2c1c427c1..971ea6a0a614689 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
@@ -24,24 +24,46 @@
 
 #include "check_assertion.h"
 
+#define ASSERT_CONSTEXPR(Expr) static_assert(__builtin_constant_p(Expr))
+#define ASSERT_NOT_CONSTEXPR(Expr) static_assert(!__builtin_constant_p(Expr));
+
 template <typename IntegerT>
-constexpr void test() {
+void test_assertion() {
   TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{3}, IntegerT{0}), "Division by 0 is undefined");
 }
 
-constexpr bool test() {
-  // signed
-  test<signed char>();
-  test<short int>();
-  test<int>();
-  test<long int>();
-  test<long long int>();
-  // unsigned
-  test<unsigned char>();
-  test<unsigned short int>();
-  test<unsigned int>();
-  test<unsigned long int>();
-  test<unsigned long long int>();
+template <typename IntegerT>
+void test_constexpr() {
+  ASSERT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
+  ASSERT_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
+}
+
+bool test() {
+  // Signed
+  test_assertion<signed char>();
+  test_assertion<short int>();
+  test_assertion<int>();
+  test_assertion<long int>();
+  test_assertion<long long int>();
+  // Unsigned
+  test_assertion<unsigned char>();
+  test_assertion<unsigned short int>();
+  test_assertion<unsigned int>();
+  test_assertion<unsigned long int>();
+  test_assertion<unsigned long long int>();
+
+  // Signed
+  test_constexpr<signed char>();
+  test_constexpr<short int>();
+  test_constexpr<int>();
+  test_constexpr<long int>();
+  test_constexpr<long long int>();
+  // Unsigned
+  test_constexpr<unsigned char>();
+  test_constexpr<unsigned short int>();
+  test_constexpr<unsigned int>();
+  test_constexpr<unsigned long int>();
+  test_constexpr<unsigned long long int>();
 
   return true;
 }

>From 83fff64955a2ef33aff63ce55bd18bd436de49b2 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 00:36:53 +0200
Subject: [PATCH 08/38] WIP: Optimized `saturate_cast`

---
 .../include/__numeric/saturation_arithmetic.h | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 8069d2574a91b2a..9514413453892a0 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -22,6 +22,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26
+
 template <__libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
@@ -98,13 +99,17 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
 
 template <__libcpp_integer _Rp, __libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
-  // Handle overflow
-  if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
-    return std::numeric_limits<_Rp>::min();
-  if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
-    return std::numeric_limits<_Rp>::max();
-  // No overflow
-  return static_cast<_Rp>(__x);
+  // Saturation is impossible.
+  if constexpr (std::cmp_less_equal(std::numeric_limits<_Rp>::min(), std::numeric_limits<_Tp>::min()) &&
+                std::cmp_greater_equal(std::numeric_limits<_Rp>::max(), std::numeric_limits<_Tp>::max()))
+    return static_cast<_Rp>(__x);
+  else {
+    // Handle overflow
+    if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
+      return std::numeric_limits<_Rp>::min();
+    // x >= std::numeric_limits<_Rp>::max()
+    return std::numeric_limits<_Rp>::max(); 
+  }
 }
 
 #endif // _LIBCPP_STD_VER >= 26

>From 2cab21ac5e83287a90ed2e73d9ee079a846b4f3c Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 10:11:05 +0200
Subject: [PATCH 09/38] Renamed test

---
 .../numeric.ops.sat/div_sat.assert.pass.cpp   | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
index 971ea6a0a614689..124affc9a8b6cba 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
@@ -28,7 +28,7 @@
 #define ASSERT_NOT_CONSTEXPR(Expr) static_assert(!__builtin_constant_p(Expr));
 
 template <typename IntegerT>
-void test_assertion() {
+void test_runtime_assertion() {
   TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{3}, IntegerT{0}), "Division by 0 is undefined");
 }
 
@@ -40,17 +40,17 @@ void test_constexpr() {
 
 bool test() {
   // Signed
-  test_assertion<signed char>();
-  test_assertion<short int>();
-  test_assertion<int>();
-  test_assertion<long int>();
-  test_assertion<long long int>();
+  test_runtime_assertion<signed char>();
+  test_runtime_assertion<short int>();
+  test_runtime_assertion<int>();
+  test_runtime_assertion<long int>();
+  test_runtime_assertion<long long int>();
   // Unsigned
-  test_assertion<unsigned char>();
-  test_assertion<unsigned short int>();
-  test_assertion<unsigned int>();
-  test_assertion<unsigned long int>();
-  test_assertion<unsigned long long int>();
+  test_runtime_assertion<unsigned char>();
+  test_runtime_assertion<unsigned short int>();
+  test_runtime_assertion<unsigned int>();
+  test_runtime_assertion<unsigned long int>();
+  test_runtime_assertion<unsigned long long int>();
 
   // Signed
   test_constexpr<signed char>();

>From 68cbc9f8832a181c19ae1e65d82731d775452313 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 10:57:54 +0200
Subject: [PATCH 10/38] WIP: `add_sat` - edge case tests

---
 .../numeric.ops.sat/add_sat.pass.cpp          | 65 ++++++++++++++++---
 1 file changed, 57 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index ccd3594fd3a6524..085c3ce7dd8bb92 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -27,24 +27,55 @@ constexpr bool test_signed() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{3}, IntegerT{4});
-    assert(sum == IntegerT{7});
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
+    assert(sum == IntegerT{55});
   }
 
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-3}, IntegerT{4});
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-27}, IntegerT{28});
     assert(sum == IntegerT{1});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
+    assert(sum == minVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{0});
+    assert(sum == maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
+    assert(sum == minVal + IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{-1});
+    assert(sum == maxVal + IntegerT{-1});
+  }
+
   // Saturation - max - both arguments positive
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
+    assert(sum == maxVal);
+  }
+
+  {
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
     assert(sum == maxVal);
   }
 
   // Saturation - min - both arguments negative
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{-4});
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
     assert(sum == minVal);
   }
 
@@ -60,13 +91,31 @@ constexpr bool test_unsigned() {
 
   // No Saturation
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{3}, IntegerT{4});
-    assert(sum == IntegerT{7});
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
+    assert(sum == IntegerT{55});
+  }
+
+    {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
+    assert(sum == IntegerT{1});
   }
 
   // Saturation - max only
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
+    assert(sum == maxVal);
+  }
+
+  {
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
     assert(sum == maxVal);
   }
 

>From e284255f28fdee5c6d91a067b21a73872a5f8327 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 11:14:38 +0200
Subject: [PATCH 11/38] WIP: Added extended integers types to tests

---
 .../numeric.ops.sat/add_sat.pass.cpp          |  8 ++++++-
 .../numeric.ops.sat/div_sat.pass.cpp          |  6 +++++
 .../numeric.ops.sat/mul_sat.pass.cpp          |  6 +++++
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 24 +++++++++++++++++++
 .../numeric.ops.sat/sub_sat.pass.cpp          |  6 +++++
 5 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index 085c3ce7dd8bb92..9dbaca98327142b 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -95,7 +95,7 @@ constexpr bool test_unsigned() {
     assert(sum == IntegerT{55});
   }
 
-    {
+  {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
     assert(sum == IntegerT{0});
   }
@@ -129,12 +129,18 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_signed<__int128_t>();
+#endif
   // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_unsigned<__uint128_t>();
+#endif
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index d2891f77c564fa6..1b3a84c8a41229b 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -80,12 +80,18 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_signed<__int128_t>();
+#endif
   // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_unsigned<__uint128_t>();
+#endif
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 2fbecd2fa55ff60..2f68d4f6ed4d4e8 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -87,12 +87,18 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_signed<__int128_t>();
+#endif
   // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_unsigned<__uint128_t>();
+#endif
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 7e75903c46b109c..710d0ade4cb433d 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -86,6 +86,18 @@ constexpr bool test() {
   test_signed_saturated<int, long long int>();
   test_signed_saturated<long int, long long int>();
   test_signed_saturated<long long int, long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_signed_notsaturated<__int128_t, signed char>();
+  test_signed_notsaturated<__int128_t, short int>();
+  test_signed_notsaturated<__int128_t, int>();
+  test_signed_notsaturated<__int128_t, long int>();
+  test_signed_notsaturated<__int128_t, long long int>();
+  test_signed_saturated<signed char, __int128_t>();
+  test_signed_saturated<short int, __int128_t>();
+  test_signed_saturated<int, __int128_t>();
+  test_signed_saturated<long int, __int128_t>();
+  test_signed_saturated<long long int, __int128_t>();
+#endif
   // Unsigned
   test_unsigned_notsaturated<unsigned long long int, unsigned char>();
   test_unsigned_notsaturated<unsigned long long int, unsigned short int>();
@@ -97,6 +109,18 @@ constexpr bool test() {
   test_unsigned_saturated<unsigned int, unsigned long long int>();
   test_unsigned_saturated<unsigned long int, unsigned long long int>();
   test_unsigned_saturated<unsigned long long int, unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_unsigned_notsaturated<__uint128_t, unsigned char>();
+  test_unsigned_notsaturated<__uint128_t, unsigned short int>();
+  test_unsigned_notsaturated<__uint128_t, unsigned int>();
+  test_unsigned_notsaturated<__uint128_t, unsigned long int>();
+  test_unsigned_notsaturated<__uint128_t, unsigned long long int>();
+  test_unsigned_saturated<unsigned char, __uint128_t>();
+  test_unsigned_saturated<unsigned short int, __uint128_t>();
+  test_unsigned_saturated<unsigned int, __uint128_t>();
+  test_unsigned_saturated<unsigned long int, __uint128_t>();
+  test_unsigned_saturated<unsigned long long int, __uint128_t>();
+#endif
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index be337dae275d91d..1a35cd4fdd0609e 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -75,12 +75,18 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_signed<__int128_t>();
+#endif
   // Unsigned
   test_unsigned<unsigned char>();
   test_unsigned<unsigned short int>();
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_unsigned<__uint128_t>();
+#endif
 
   return true;
 }

>From fb439785b084b83cf8dadea048c6bc87bd7676f2 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 11:25:17 +0200
Subject: [PATCH 12/38] WIP: Added `check_constexpr.h`

---
 .../numeric.ops.sat/add_sat.pass.cpp          |  2 +-
 .../numeric.ops.sat/div_sat.assert.pass.cpp   | 32 ++++---------------
 .../numeric.ops.sat/div_sat.pass.cpp          | 28 +++++++++++++++-
 .../numeric.ops.sat/mul_sat.pass.cpp          |  2 +-
 .../numeric.ops.sat/saturate_cast.pass.cpp    |  2 +-
 .../numeric.ops.sat/sub_sat.pass.cpp          |  2 +-
 libcxx/test/support/check_constexpr.h         | 21 ++++++++++++
 7 files changed, 59 insertions(+), 30 deletions(-)
 create mode 100644 libcxx/test/support/check_constexpr.h

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index 9dbaca98327142b..d71599d87e02c73 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -146,7 +146,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  test();
+  assert(test());
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
index 124affc9a8b6cba..a67b714cf65fb25 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
@@ -18,24 +18,13 @@
 // constexpr T div_sat(T x, T y) noexcept;                     // freestanding
 
 #include <cassert>
-#include <concepts>
-#include <limits>
 #include <numeric>
 
 #include "check_assertion.h"
 
-#define ASSERT_CONSTEXPR(Expr) static_assert(__builtin_constant_p(Expr))
-#define ASSERT_NOT_CONSTEXPR(Expr) static_assert(!__builtin_constant_p(Expr));
-
 template <typename IntegerT>
 void test_runtime_assertion() {
-  TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{3}, IntegerT{0}), "Division by 0 is undefined");
-}
-
-template <typename IntegerT>
-void test_constexpr() {
-  ASSERT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
-  ASSERT_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
+  TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{27}, IntegerT{0}), "Division by 0 is undefined");
 }
 
 bool test() {
@@ -45,25 +34,18 @@ bool test() {
   test_runtime_assertion<int>();
   test_runtime_assertion<long int>();
   test_runtime_assertion<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_runtime_assertion<__int128_t>();
+#endif
   // Unsigned
   test_runtime_assertion<unsigned char>();
   test_runtime_assertion<unsigned short int>();
   test_runtime_assertion<unsigned int>();
   test_runtime_assertion<unsigned long int>();
   test_runtime_assertion<unsigned long long int>();
-
-  // Signed
-  test_constexpr<signed char>();
-  test_constexpr<short int>();
-  test_constexpr<int>();
-  test_constexpr<long int>();
-  test_constexpr<long long int>();
-  // Unsigned
-  test_constexpr<unsigned char>();
-  test_constexpr<unsigned short int>();
-  test_constexpr<unsigned int>();
-  test_constexpr<unsigned long int>();
-  test_constexpr<unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_runtime_assertion<__uint128_t>();
+#endif
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 1b3a84c8a41229b..506dd6e15ab6239 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -18,6 +18,8 @@
 #include <limits>
 #include <numeric>
 
+#include "check_constexpr.h"
+
 template <typename IntegerT>
 constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
@@ -73,6 +75,12 @@ constexpr bool test_unsigned() {
   return true;
 }
 
+template <typename IntegerT>
+void test_constexpr() {
+  TEST_EXPRESSION_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
+  TEST_EXPRESSION_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
+}
+
 constexpr bool test() {
   // Signed
   test_signed<signed char>();
@@ -96,9 +104,27 @@ constexpr bool test() {
   return true;
 }
 
+bool test_constexpr() {
+  // Signed
+  test_constexpr<signed char>();
+  test_constexpr<short int>();
+  test_constexpr<int>();
+  test_constexpr<long int>();
+  test_constexpr<long long int>();
+  // Unsigned
+  test_constexpr<unsigned char>();
+  test_constexpr<unsigned short int>();
+  test_constexpr<unsigned int>();
+  test_constexpr<unsigned long int>();
+  test_constexpr<unsigned long long int>();
+
+  return true;
+}
+
 int main(int, char**) {
-  test();
+  assert(test());
   static_assert(test());
+  assert(test_constexpr());
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 2f68d4f6ed4d4e8..1e502e1cbaff3b3 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -104,7 +104,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  test();
+  assert(test());
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 710d0ade4cb433d..f7c1cb4dfc09d2a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -126,7 +126,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  test();
+  assert(test());
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 1a35cd4fdd0609e..12c14860560a87f 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -92,7 +92,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  test();
+  assert(test());
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/support/check_constexpr.h b/libcxx/test/support/check_constexpr.h
new file mode 100644
index 000000000000000..54a6c6caf7c9454
--- /dev/null
+++ b/libcxx/test/support/check_constexpr.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_SUPPORT_CHECK_CONSTEXPR_H
+#define TEST_SUPPORT_CHECK_CONSTEXPR_H
+
+#include "test_macros.h"
+
+#if TEST_STD_VER < 11
+#  error "C++11 or greater is required to use this header"
+#endif
+
+#define TEST_EXPRESSION_CONSTEXPR(expr) static_assert(__builtin_constant_p(expr))
+#define TEST_EXPRESSION_NOT_CONSTEXPR(expr) static_assert(!__builtin_constant_p(expr));
+
+#endif // TEST_SUPPORT_CHECK_CONSTEXPR_H

>From 39837d65d9cf5e6c78ebaf3a00c3d36be5dd32dd Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 12:18:07 +0200
Subject: [PATCH 13/38] WIP: Fixed code formatter error

---
 libcxx/include/__numeric/saturation_arithmetic.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 9514413453892a0..75dd540a84e689b 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -108,7 +108,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
     if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
       return std::numeric_limits<_Rp>::min();
     // x >= std::numeric_limits<_Rp>::max()
-    return std::numeric_limits<_Rp>::max(); 
+    return std::numeric_limits<_Rp>::max();
   }
 }
 

>From 3fbec0109f54c8006da57d852e24d0fbe5814f68 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 12:50:25 +0200
Subject: [PATCH 14/38] WIP: Addtional tests `sat_sub`

---
 .../numeric.ops.sat/div_sat.pass.cpp          | 24 +++++-----
 .../numeric.ops.sat/mul_sat.pass.cpp          | 28 ++++++------
 .../numeric.ops.sat/sub_sat.pass.cpp          | 44 ++++++++++++++-----
 3 files changed, 60 insertions(+), 36 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 506dd6e15ab6239..c7992bc72e02998 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -29,24 +29,24 @@ constexpr bool test_signed() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) div = std::div_sat(IntegerT{3}, IntegerT{4});
-    assert(div == IntegerT{0});
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+    assert(quot == IntegerT{0});
   }
 
   {
-    std::same_as<IntegerT> decltype(auto) div = std::div_sat(maxVal, minVal);
-    assert(div == (maxVal / minVal));
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, minVal);
+    assert(quot == (maxVal / minVal));
   }
 
   {
-    std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
-    assert(div == (minVal / maxVal));
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
+    assert(quot == (minVal / maxVal));
   }
 
   // Saturation - max only
   {
-    std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, IntegerT{-1});
-    assert(div == maxVal);
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, IntegerT{-1});
+    assert(quot == maxVal);
   }
 
   return true;
@@ -61,13 +61,13 @@ constexpr bool test_unsigned() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) div = std::div_sat(IntegerT{3}, IntegerT{4});
-    assert(div == IntegerT{0});
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+    assert(quot == IntegerT{0});
   }
 
   {
-    std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
-    assert(div == (minVal / maxVal));
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
+    assert(quot == (minVal / maxVal));
   }
 
   // Unsigned integer devision never overflow
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 1e502e1cbaff3b3..1f4d0ab518bbea9 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -27,32 +27,32 @@ constexpr bool test_signed() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
-    assert(sum == IntegerT{12});
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{3}, IntegerT{4});
+    assert(prod == IntegerT{12});
   }
 
   // Saturation - max - both arguments positive
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{4});
-    assert(sum == maxVal);
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{4});
+    assert(prod == maxVal);
   }
 
   // Saturation - max - both arguments negative
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, IntegerT{-4});
-    assert(sum == maxVal);
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{-4});
+    assert(prod == maxVal);
   }
 
   // Saturation - min - left positive, right negative
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{-4});
-    assert(sum == minVal);
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{-4});
+    assert(prod == minVal);
   }
 
   // Saturation - min - left negative, right positive
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, IntegerT{4});
-    assert(sum == minVal);
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{4});
+    assert(prod == minVal);
   }
 
   return true;
@@ -67,14 +67,14 @@ constexpr bool test_unsigned() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
-    assert(sum == IntegerT{12});
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{3}, IntegerT{4});
+    assert(prod == IntegerT{12});
   }
 
   // Saturation
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{4});
-    assert(sum == maxVal);
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{4});
+    assert(prod == maxVal);
   }
 
   return true;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 12c14860560a87f..2c5fcf6ec0f6990 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -27,20 +27,36 @@ constexpr bool test_signed() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{4});
-    assert(sum == IntegerT{-1});
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(IntegerT{3}, IntegerT{4});
+    assert(diff == IntegerT{-1});
   }
 
   // Saturation - min - left negative, right positive
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
-    assert(sum == minVal);
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, IntegerT{4});
+    assert(diff == minVal);
+  }
+
+  {
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
+    assert(diff == minVal);
   }
 
   // Saturation - max - left postitive, right negative
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(maxVal, IntegerT{-4});
-    assert(sum == maxVal);
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(maxVal, IntegerT{-4});
+    assert(diff == maxVal);
+  }
+
+  {
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{27};
+
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
+    assert(diff == maxVal);
   }
 
   return true;
@@ -55,14 +71,22 @@ constexpr bool test_unsigned() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{1});
-    assert(sum == IntegerT{2});
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(IntegerT{3}, IntegerT{1});
+    assert(diff == IntegerT{2});
   }
 
   // Saturation - min only
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
-    assert(sum == minVal);
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, IntegerT{4});
+    assert(diff == minVal);
+  }
+
+  {
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
+    assert(diff == minVal);
   }
 
   return true;

>From a77d6035e1f984109afcd13c55d8c0cf7681cc61 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 14:10:44 +0200
Subject: [PATCH 15/38] WIP: `add_sat` - additional test cases

---
 .../numeric.ops.sat/add_sat.pass.cpp          | 66 +++++++++++++++++--
 1 file changed, 61 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index d71599d87e02c73..29534092b484a32 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -25,7 +25,54 @@ constexpr bool test_signed() {
 
   static_assert(noexcept(std::div_sat(minVal, maxVal)));
 
-  // No saturation
+  // No saturation - (-1, 0, 1)
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{1});
+    assert(sum == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{0});
+    assert(sum == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{-1});
+    assert(sum == IntegerT{-1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{0});
+    assert(sum == IntegerT{-1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{1});
+    assert(sum == IntegerT{2});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{-1});
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{1});
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{-1});
+    assert(sum == IntegerT{-2});
+  }
+
+  // No saturation - (any value)
+
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
     assert(sum == IntegerT{55});
@@ -36,19 +83,21 @@ constexpr bool test_signed() {
     assert(sum == IntegerT{1});
   }
 
+  // No saturation - (min, -1, 0, 1, max)
+
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
     assert(sum == minVal);
   }
 
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{0});
-    assert(sum == maxVal);
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
+    assert(sum == minVal + IntegerT{1});
   }
 
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
-    assert(sum == minVal + IntegerT{1});
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{0});
+    assert(sum == maxVal);
   }
 
   {
@@ -63,6 +112,7 @@ constexpr bool test_signed() {
   }
 
   {
+    // Large values
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
 
@@ -72,6 +122,7 @@ constexpr bool test_signed() {
 
   // Saturation - min - both arguments negative
   {
+    // Large values
     constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
     constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
 
@@ -79,6 +130,11 @@ constexpr bool test_signed() {
     assert(sum == minVal);
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
+    assert(sum == minVal);
+  }
+
   return true;
 }
 

>From faf11737be127c4b4a2b3f8a43c530dd38d6be80 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 16:30:41 +0200
Subject: [PATCH 16/38] Completed  SFINAE tests

---
 .../numeric.ops.sat/add_sat.compile.pass.cpp  |  75 +++++++++++
 .../numeric.ops.sat/add_sat.pass.cpp          |  21 ++-
 .../numeric.ops.sat/add_sat.verify.cpp        |  41 ------
 .../numeric.ops.sat/div_sat.compile.pass.cpp  | 124 ++++++++++++++++++
 .../numeric.ops.sat/div_sat.pass.cpp          |   8 --
 .../numeric.ops.sat/div_sat.verify.cpp        |  41 ------
 .../numeric.ops.sat/mul_sat.compile.pass.cpp  |  76 +++++++++++
 .../numeric.ops.sat/mul_sat.verify.cpp        |  41 ------
 .../saturate_cast.compile.pass.cpp            |  77 +++++++++++
 .../numeric.ops.sat/saturate_cast.verify.cpp  |  50 -------
 .../numeric.ops.sat/sub_sat.compile.pass.cpp  |  76 +++++++++++
 .../numeric.ops.sat/sub_sat.verify.cpp        |  41 ------
 libcxx/test/support/check_constexpr.h         |  21 ---
 13 files changed, 446 insertions(+), 246 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
 delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
 delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
 delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
 delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
 delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
 delete mode 100644 libcxx/test/support/check_constexpr.h

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
new file mode 100644
index 000000000000000..56d2cfd4a2585ef
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T add_sat(T x, T y) noexcept;                     // freestanding
+
+#include <numeric>
+#include <limits>
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+  { std::add_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+  static_assert(CanDo<T, T>);
+  static_assert(!CanDo<U, T>);
+  static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+  using I = int;
+  static_assert(!CanDo<T, T>);
+  static_assert(!CanDo<I, T>);
+  static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+  // Contraint success - Signed
+  using SI = long long int;
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<int, SI>();
+  test_constraint_success<long int, SI>();
+  test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__int128_t, SI>();
+#endif
+  // Contraint success - Unsigned
+  using UI = unsigned long long int;
+  test_constraint_success<unsigned char, UI>();
+  test_constraint_success<unsigned short int, UI>();
+  test_constraint_success<unsigned int, UI>();
+  test_constraint_success<unsigned long int, UI>();
+  test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__uint128_t, UI>();
+#endif
+
+  // Contraint failure
+  test_constraint_fail<bool>();
+  test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint_fail<wchar_t>();
+#endif
+  test_constraint_fail<char8_t>();
+  test_constraint_fail<char16_t>();
+  test_constraint_fail<char32_t>();
+  test_constraint_fail<float>();
+  test_constraint_fail<double>();
+  test_constraint_fail<long double>();
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index 29534092b484a32..1fdf647ff606e43 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -25,7 +25,7 @@ constexpr bool test_signed() {
 
   static_assert(noexcept(std::div_sat(minVal, maxVal)));
 
-  // No saturation - (-1, 0, 1)
+  // No saturation (-1, 0, 1)
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
     assert(sum == IntegerT{0});
@@ -71,7 +71,7 @@ constexpr bool test_signed() {
     assert(sum == IntegerT{-2});
   }
 
-  // No saturation - (any value)
+  // No saturation (any value)
 
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
@@ -83,7 +83,7 @@ constexpr bool test_signed() {
     assert(sum == IntegerT{1});
   }
 
-  // No saturation - (min, -1, 0, 1, max)
+  // No saturation (min, -1, 0, 1, max)
 
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
@@ -120,6 +120,11 @@ constexpr bool test_signed() {
     assert(sum == maxVal);
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, maxVal);
+    assert(sum == maxVal);
+  }
+
   // Saturation - min - both arguments negative
   {
     // Large values
@@ -161,6 +166,11 @@ constexpr bool test_unsigned() {
     assert(sum == IntegerT{1});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
+    assert(sum == minVal);
+  }
+
   // Saturation - max only
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
@@ -175,6 +185,11 @@ constexpr bool test_unsigned() {
     assert(sum == maxVal);
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, maxVal);
+    assert(sum == maxVal);
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
deleted file mode 100644
index 7df73043c922bdd..000000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-// <numeric>
-
-// template<class T>
-// constexpr T add_sat(T x, T y) noexcept;                     // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename IntegerT>
-constexpr void test_constraint() {
-  // expected-error@*:* 8-9 {{no matching function for call to 'add_sat'}}
-  [[maybe_unused]] auto sum = std::add_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
-  test_constraint<bool>();
-  test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test_constraint<wchar_t>();
-#endif
-  test_constraint<char8_t>();
-  test_constraint<char16_t>();
-  test_constraint<char32_t>();
-  test_constraint<float>();
-  test_constraint<double>();
-  test_constraint<long double>();
-
-  return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
new file mode 100644
index 000000000000000..a91c2d6b5d6fdcb
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T div_sat(T x, T y) noexcept;                     // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+#include <type_traits>
+
+// Constraints
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+  { std::div_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+  static_assert(CanDo<T, T>);
+  static_assert(!CanDo<U, T>);
+  static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+  using I = int;
+  static_assert(!CanDo<T, T>);
+  static_assert(!CanDo<I, T>);
+  static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+  // Contraint success - Signed
+  using SI = long long int;
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<int, SI>();
+  test_constraint_success<long int, SI>();
+  test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__int128_t, SI>();
+#endif
+  // Contraint success - Unsigned
+  using UI = unsigned long long int;
+  test_constraint_success<unsigned char, UI>();
+  test_constraint_success<unsigned short int, UI>();
+  test_constraint_success<unsigned int, UI>();
+  test_constraint_success<unsigned long int, UI>();
+  test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__uint128_t, UI>();
+#endif
+
+  // Contraint failure
+  test_constraint_fail<bool>();
+  test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint_fail<wchar_t>();
+#endif
+  test_constraint_fail<char8_t>();
+  test_constraint_fail<char16_t>();
+  test_constraint_fail<char32_t>();
+  test_constraint_fail<float>();
+  test_constraint_fail<double>();
+  test_constraint_fail<long double>();
+}
+
+// Check precondition: not constexpr when `div_sat` by zero
+
+template <auto N>
+using QuotT = std::integral_constant<decltype(N), std::div_sat(N, N)>;
+
+template <auto N>
+QuotT<N> div_by_zero();
+
+template <auto N>
+concept CanDivByZero = requires { div_by_zero<N>(); };
+
+static_assert(!CanDivByZero<static_cast<signed char>(0)>);
+static_assert(!CanDivByZero<static_cast<short int>(0)>);
+static_assert(!CanDivByZero<0>);
+static_assert(!CanDivByZero<0L>);
+static_assert(!CanDivByZero<0LL>);
+#ifndef _LIBCPP_HAS_NO_INT128
+static_assert(!CanDivByZero<static_cast<__int128_t>(0)>);
+#endif
+static_assert(!CanDivByZero<static_cast<unsigned char>(0)>);
+static_assert(!CanDivByZero<static_cast<unsigned short int>(0)>);
+static_assert(!CanDivByZero<0U>);
+static_assert(!CanDivByZero<0UL>);
+static_assert(!CanDivByZero<0ULL>);
+#ifndef _LIBCPP_HAS_NO_INT128
+static_assert(!CanDivByZero<static_cast<__uint128_t>(0)>);
+#endif
+
+// static_assert(CanDivByZero<static_cast<signed char>(0)>);
+// static_assert(CanDivByZero<static_cast<short int>(0)>);
+// static_assert(CanDivByZero<0>);
+// static_assert(CanDivByZero<0L>);
+// static_assert(CanDivByZero<0LL>);
+// #ifndef _LIBCPP_HAS_NO_INT128
+// static_assert(CanDivByZero<static_cast<__int128_t>(0)>);
+// #endif
+// static_assert(CanDivByZero<static_cast<unsigned char>(0)>);
+// static_assert(CanDivByZero<static_cast<unsigned short int>(0)>);
+// static_assert(CanDivByZero<0U>);
+// static_assert(CanDivByZero<0UL>);
+// static_assert(CanDivByZero<0ULL>);
+// #ifndef _LIBCPP_HAS_NO_INT128
+// static_assert(CanDivByZero<static_cast<__uint128_t>(0)>);
+// #endif
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index c7992bc72e02998..232f8b826520c9d 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -18,8 +18,6 @@
 #include <limits>
 #include <numeric>
 
-#include "check_constexpr.h"
-
 template <typename IntegerT>
 constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
@@ -75,12 +73,6 @@ constexpr bool test_unsigned() {
   return true;
 }
 
-template <typename IntegerT>
-void test_constexpr() {
-  TEST_EXPRESSION_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
-  TEST_EXPRESSION_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
-}
-
 constexpr bool test() {
   // Signed
   test_signed<signed char>();
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
deleted file mode 100644
index 5db6efd4ce397ea..000000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-// <numeric>
-
-// template<class T>
-// constexpr T div_sat(T x, T y) noexcept;                     // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename IntegerT>
-constexpr void test_constraint() {
-  // expected-error@*:* 8-9 {{no matching function for call to 'div_sat'}}
-  [[maybe_unused]] auto sum = std::div_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
-  test_constraint<bool>();
-  test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test_constraint<wchar_t>();
-#endif
-  test_constraint<char8_t>();
-  test_constraint<char16_t>();
-  test_constraint<char32_t>();
-  test_constraint<float>();
-  test_constraint<double>();
-  test_constraint<long double>();
-
-  return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
new file mode 100644
index 000000000000000..20105daf6b0719d
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T mul_sat(T x, T y) noexcept;                     // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+  { std::mul_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+  static_assert(CanDo<T, T>);
+  static_assert(!CanDo<U, T>);
+  static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+  using I = int;
+  static_assert(!CanDo<T, T>);
+  static_assert(!CanDo<I, T>);
+  static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+  // Contraint success - Signed
+  using SI = long long int;
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<int, SI>();
+  test_constraint_success<long int, SI>();
+  test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__int128_t, SI>();
+#endif
+  // Contraint success - Unsigned
+  using UI = unsigned long long int;
+  test_constraint_success<unsigned char, UI>();
+  test_constraint_success<unsigned short int, UI>();
+  test_constraint_success<unsigned int, UI>();
+  test_constraint_success<unsigned long int, UI>();
+  test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__uint128_t, UI>();
+#endif
+
+  // Contraint failure
+  test_constraint_fail<bool>();
+  test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint_fail<wchar_t>();
+#endif
+  test_constraint_fail<char8_t>();
+  test_constraint_fail<char16_t>();
+  test_constraint_fail<char32_t>();
+  test_constraint_fail<float>();
+  test_constraint_fail<double>();
+  test_constraint_fail<long double>();
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
deleted file mode 100644
index fcc24c09b7b9b40..000000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-// <numeric>
-
-// template<class T>
-// constexpr T mul_sat(T x, T y) noexcept;                     // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename IntegerT>
-constexpr void test_constraint() {
-  // expected-error@*:* 8-9 {{no matching function for call to 'mul_sat'}}
-  [[maybe_unused]] auto sum = std::mul_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
-  test_constraint<bool>();
-  test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test_constraint<wchar_t>();
-#endif
-  test_constraint<char8_t>();
-  test_constraint<char16_t>();
-  test_constraint<char32_t>();
-  test_constraint<float>();
-  test_constraint<double>();
-  test_constraint<long double>();
-
-  return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
new file mode 100644
index 000000000000000..a1f53e0fd2cac06
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class R, class T>
+//   constexpr R saturate_cast(T x) noexcept;                    // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+
+template <typename R, typename T>
+concept CanDo = requires(T x) {
+  { std::saturate_cast<R>(x) } -> std::same_as<R>;
+};
+
+template <typename R, typename T>
+constexpr void test_constraint_success() {
+  static_assert(CanDo<R, T>);
+  static_assert(CanDo<T, T>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+  using I = int;
+  using R = T;
+  static_assert(!CanDo<R, T>);
+  static_assert(!CanDo<T, R>);
+  static_assert(!CanDo<I, T>);
+  static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+  // Contraint success - Signed
+  using SI = long long int;
+  test_constraint_success<SI, signed char>();
+  test_constraint_success<SI, short int>();
+  test_constraint_success<SI, signed char>();
+  test_constraint_success<SI, short int>();
+  test_constraint_success<SI, int>();
+  test_constraint_success<SI, long int>();
+  test_constraint_success<int, long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__int128_t, SI>();
+#endif
+  // Contraint success - Unsigned
+  using UI = unsigned long long int;
+  test_constraint_success<UI, unsigned char>();
+  test_constraint_success<UI, unsigned short int>();
+  test_constraint_success<UI, unsigned int>();
+  test_constraint_success<UI, unsigned long int>();
+  test_constraint_success<unsigned int, unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<UI, __uint128_t>();
+#endif
+
+  // Contraint failure
+  test_constraint_fail<bool>();
+  test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint_fail<wchar_t>();
+#endif
+  test_constraint_fail<char8_t>();
+  test_constraint_fail<char16_t>();
+  test_constraint_fail<char32_t>();
+  test_constraint_fail<float>();
+  test_constraint_fail<double>();
+  test_constraint_fail<long double>();
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
deleted file mode 100644
index 379628b5664841f..000000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-// <numeric>
-
-// template<class R, class T>
-//   constexpr R saturate_cast(T x) noexcept;                    // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename ResultIntegerT, typename IntegerT>
-constexpr void test_constraint() {
-  // expected-error@*:* 16-18 {{no matching function for call to 'saturate_cast'}}
-  [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{});
-}
-
-constexpr bool test() {
-  test_constraint<bool, int>();
-  test_constraint<char, int>();
-  test_constraint<int, bool>();
-  test_constraint<int, char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test_constraint<wchar_t, int>();
-  test_constraint<int, wchar_t>();
-#endif
-  test_constraint<char8_t, int>();
-  test_constraint<char16_t, int>();
-  test_constraint<char32_t, int>();
-  test_constraint<float, int>();
-  test_constraint<double, int>();
-  test_constraint<long double, int>();
-  test_constraint<int, char8_t>();
-  test_constraint<int, char16_t>();
-  test_constraint<int, char32_t>();
-  test_constraint<int, float>();
-  test_constraint<int, double>();
-  test_constraint<int, long double>();
-
-  return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
new file mode 100644
index 000000000000000..cd27770d9a77dea
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T sub_sat(T x, T y) noexcept;                     // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+  { std::sub_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+  static_assert(CanDo<T, T>);
+  static_assert(!CanDo<U, T>);
+  static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+  using I = int;
+  static_assert(!CanDo<T, T>);
+  static_assert(!CanDo<I, T>);
+  static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+  // Contraint success - Signed
+  using SI = long long int;
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<signed char, SI>();
+  test_constraint_success<short int, SI>();
+  test_constraint_success<int, SI>();
+  test_constraint_success<long int, SI>();
+  test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__int128_t, SI>();
+#endif
+  // Contraint success - Unsigned
+  using UI = unsigned long long int;
+  test_constraint_success<unsigned char, UI>();
+  test_constraint_success<unsigned short int, UI>();
+  test_constraint_success<unsigned int, UI>();
+  test_constraint_success<unsigned long int, UI>();
+  test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+  test_constraint_success<__uint128_t, UI>();
+#endif
+
+  // Contraint failure
+  test_constraint_fail<bool>();
+  test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint_fail<wchar_t>();
+#endif
+  test_constraint_fail<char8_t>();
+  test_constraint_fail<char16_t>();
+  test_constraint_fail<char32_t>();
+  test_constraint_fail<float>();
+  test_constraint_fail<double>();
+  test_constraint_fail<long double>();
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
deleted file mode 100644
index 1ca5ca329b6c9a6..000000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-// <numeric>
-
-// template<class T>
-// constexpr T sub_sat(T x, T y) noexcept;                     // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename IntegerT>
-constexpr void test_constraint() {
-  // expected-error@*:* 8-9 {{no matching function for call to 'sub_sat'}}
-  [[maybe_unused]] auto sum = std::sub_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
-  test_constraint<bool>();
-  test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test_constraint<wchar_t>();
-#endif
-  test_constraint<char8_t>();
-  test_constraint<char16_t>();
-  test_constraint<char32_t>();
-  test_constraint<float>();
-  test_constraint<double>();
-  test_constraint<long double>();
-
-  return true;
-}
diff --git a/libcxx/test/support/check_constexpr.h b/libcxx/test/support/check_constexpr.h
deleted file mode 100644
index 54a6c6caf7c9454..000000000000000
--- a/libcxx/test/support/check_constexpr.h
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef TEST_SUPPORT_CHECK_CONSTEXPR_H
-#define TEST_SUPPORT_CHECK_CONSTEXPR_H
-
-#include "test_macros.h"
-
-#if TEST_STD_VER < 11
-#  error "C++11 or greater is required to use this header"
-#endif
-
-#define TEST_EXPRESSION_CONSTEXPR(expr) static_assert(__builtin_constant_p(expr))
-#define TEST_EXPRESSION_NOT_CONSTEXPR(expr) static_assert(!__builtin_constant_p(expr));
-
-#endif // TEST_SUPPORT_CHECK_CONSTEXPR_H

>From b66f03446b98c0656dd561219408b119423715ad Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 22:20:48 +0200
Subject: [PATCH 17/38] WIP: Temporary static tests

---
 .../include/__numeric/saturation_arithmetic.h |  7 +-
 .../numeric.ops.sat/add_sat.pass.cpp          | 71 ++++++++++++++++
 .../numeric.ops.sat/div_sat.compile.pass.cpp  | 19 +----
 .../numeric.ops.sat/div_sat.pass.cpp          | 42 ++++++++++
 .../numeric.ops.sat/mul_sat.pass.cpp          | 31 +++++++
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 27 ++++++
 .../numeric.ops.sat/sub_sat.pass.cpp          | 83 +++++++++++++++++++
 7 files changed, 260 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 75dd540a84e689b..17efdd4b18c3e04 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -107,8 +107,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
     // Handle overflow
     if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
       return std::numeric_limits<_Rp>::min();
-    // x >= std::numeric_limits<_Rp>::max()
-    return std::numeric_limits<_Rp>::max();
+    // // x >= std::numeric_limits<_Rp>::max()
+    // return std::numeric_limits<_Rp>::max();
+    if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
+      return std::numeric_limits<_Rp>::max();
+    return static_cast<_Rp>(__x);
   }
 }
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index 1fdf647ff606e43..dc0485b1c75f734 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -222,3 +222,74 @@ int main(int, char**) {
 
   return 0;
 }
+
+
+#include <numeric>
+#include <limits>
+
+template<typename T, typename U>
+concept can_add_sat
+  = requires(T t, U u) { { std::add_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_add_sat<int, int> );
+static_assert( not can_add_sat<int, short> );
+static_assert( not can_add_sat<unsigned, int> );
+static_assert( noexcept(std::add_sat(0, 0)) );
+
+using std::add_sat;
+
+// Signed type
+static_assert(add_sat(0, 0) == 0);
+static_assert(add_sat(1, 1) == 2);
+static_assert(add_sat(-1, -1) == -2);
+static_assert(add_sat(-1, 1) == 0);
+constexpr auto max = std::numeric_limits<int>::max();
+constexpr auto min = std::numeric_limits<int>::min();
+static_assert(add_sat(max, 1) == max);
+static_assert(add_sat(1, max) == max);
+static_assert(add_sat(max, max) == max);
+static_assert(add_sat(min, -1) == min);
+static_assert(add_sat(-1, min) == min);
+static_assert(add_sat(min, min) == min);
+static_assert(add_sat(max, min) == -1);
+static_assert(add_sat(min, max) == -1);
+
+// Wider signed type than the args
+static_assert(add_sat<long long>(max, max) == (long long)max * 2);
+static_assert(add_sat<long long>(min, min) == (long long)min * 2);
+
+// Signed type that undergoes integer promotion
+constexpr auto shrt_max = std::numeric_limits<short>::max();
+constexpr auto shrt_min = std::numeric_limits<short>::min();
+static_assert(add_sat<short>(0, 0) == 0);
+static_assert(add_sat<short>(1, 1) == 2);
+static_assert(add_sat<short>(shrt_max, shrt_max) == shrt_max);
+static_assert(add_sat<short>(shrt_max, 1) == shrt_max);
+static_assert(add_sat<short>(1, shrt_max) == shrt_max);
+static_assert(add_sat<short>(shrt_min, (short)-1) == shrt_min);
+static_assert(add_sat<short>((short)-1, shrt_min) == shrt_min);
+static_assert(add_sat<short>(shrt_min, (short)1) == -shrt_max);
+static_assert(add_sat<short>((short)1, shrt_min) == -shrt_max);
+
+// Unsigned type
+static_assert(add_sat(0u, 0u) == 0u);
+static_assert(add_sat(1u, 1u) == 2u);
+constexpr auto umax = std::numeric_limits<unsigned>::max();
+static_assert(add_sat(umax, 1u) == umax);
+static_assert(add_sat(1u, umax) == umax);
+static_assert(add_sat(umax, umax) == umax);
+static_assert(add_sat(0u, umax) == umax);
+static_assert(add_sat(umax, 0u) == umax);
+static_assert(add_sat(0u, 1u) == 1u);
+static_assert(add_sat(1u, 0u) == 1u);
+
+// Wider unsigned type than the args
+static_assert(add_sat<unsigned long long>(umax, umax) == (long long)umax * 2);
+
+// Unsigned type that undergoes integer promotion
+constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
+static_assert(add_sat<unsigned short>(0, 0) == 0);
+static_assert(add_sat<unsigned short>(1, 1) == 2);
+static_assert(add_sat<unsigned short>(ushrt_max, ushrt_max) == ushrt_max);
+static_assert(add_sat<unsigned short>(ushrt_max, 1) == ushrt_max);
+static_assert(add_sat<unsigned short>(1, ushrt_max) == ushrt_max);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
index a91c2d6b5d6fdcb..ffa30d9e3d5cfd6 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
@@ -78,7 +78,7 @@ constexpr void test() {
   test_constraint_fail<long double>();
 }
 
-// Check precondition: not constexpr when `div_sat` by zero
+//  A function call expression that violates the precondition in the Preconditions: element is not a core constant expression (7.7 [expr.const]).
 
 template <auto N>
 using QuotT = std::integral_constant<decltype(N), std::div_sat(N, N)>;
@@ -105,20 +105,3 @@ static_assert(!CanDivByZero<0ULL>);
 #ifndef _LIBCPP_HAS_NO_INT128
 static_assert(!CanDivByZero<static_cast<__uint128_t>(0)>);
 #endif
-
-// static_assert(CanDivByZero<static_cast<signed char>(0)>);
-// static_assert(CanDivByZero<static_cast<short int>(0)>);
-// static_assert(CanDivByZero<0>);
-// static_assert(CanDivByZero<0L>);
-// static_assert(CanDivByZero<0LL>);
-// #ifndef _LIBCPP_HAS_NO_INT128
-// static_assert(CanDivByZero<static_cast<__int128_t>(0)>);
-// #endif
-// static_assert(CanDivByZero<static_cast<unsigned char>(0)>);
-// static_assert(CanDivByZero<static_cast<unsigned short int>(0)>);
-// static_assert(CanDivByZero<0U>);
-// static_assert(CanDivByZero<0UL>);
-// static_assert(CanDivByZero<0ULL>);
-// #ifndef _LIBCPP_HAS_NO_INT128
-// static_assert(CanDivByZero<static_cast<__uint128_t>(0)>);
-// #endif
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 232f8b826520c9d..9e457f56e9d56cf 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -120,3 +120,45 @@ int main(int, char**) {
 
   return 0;
 }
+
+#include <numeric>
+#include <climits>
+
+template<typename T, typename U>
+concept can_div_sat
+  = requires(T t, U u) { { std::div_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_div_sat<int, int> );
+static_assert( not can_div_sat<int, short> );
+static_assert( not can_div_sat<unsigned, int> );
+static_assert( noexcept(std::div_sat(0, 1)) );
+
+using std::div_sat;
+
+static_assert(std::div_sat(0, 1) == 0);
+static_assert(std::div_sat(0, -1) == 0);
+static_assert(std::div_sat(1, -1) == -1);
+static_assert(std::div_sat(10, -2) == -5);
+static_assert(std::div_sat(-10, -2) == 5);
+static_assert(std::div_sat(INT_MAX, 1) == INT_MAX);
+static_assert(std::div_sat(INT_MIN, 1) == INT_MIN);
+static_assert(std::div_sat(INT_MIN + 1, -1) == INT_MAX);
+static_assert(std::div_sat(0u, 1u) == 0u);
+static_assert(std::div_sat(UINT_MAX, 1u) == UINT_MAX);
+static_assert(std::div_sat(INT_MIN, -1) == INT_MAX);
+static_assert(std::div_sat((short)SHRT_MIN, (short)-1) == SHRT_MAX);
+static_assert(std::div_sat(LONG_MIN, -1L) == LONG_MAX);
+static_assert(std::div_sat(LLONG_MIN, -1LL) == LLONG_MAX);
+
+template<auto N>
+std::integral_constant<decltype(N), std::div_sat(N, N-N)>
+div_sat_by_zero();
+
+template<auto N>
+concept can_div_sat_by_zero = requires { div_sat_by_zero<N>(); };
+
+static_assert( not can_div_sat_by_zero<0> );
+static_assert( not can_div_sat_by_zero<1> );
+static_assert( not can_div_sat_by_zero<1u> );
+static_assert( not can_div_sat_by_zero<-1L> );
+static_assert( not can_div_sat_by_zero<short(99)> );
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 1f4d0ab518bbea9..64721c6ef97e8ed 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -109,3 +109,34 @@ int main(int, char**) {
 
   return 0;
 }
+
+#include <numeric>
+#include <climits>
+
+template<typename T, typename U>
+concept can_mul_sat
+  = requires(T t, U u) { { std::mul_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_mul_sat<int, int> );
+static_assert( not can_mul_sat<int, short> );
+static_assert( not can_mul_sat<unsigned, int> );
+static_assert( noexcept(std::mul_sat(0, 0)) );
+
+using std::mul_sat;
+
+static_assert(mul_sat(1, 1) == 1);
+static_assert(mul_sat(10, 11) == 110);
+static_assert(mul_sat(INT_MAX / 2, 3) == INT_MAX);
+static_assert(mul_sat(INT_MAX / 2, -3) == INT_MIN);
+static_assert(mul_sat(INT_MAX / -2, 3) == INT_MIN);
+static_assert(mul_sat(INT_MIN / 2, -3) == INT_MAX);
+static_assert(mul_sat(INT_MIN, -1) == INT_MAX);
+static_assert(mul_sat(INT_MAX, -1) == INT_MIN + 1);
+static_assert(mul_sat(INT_MAX, INT_MAX) == INT_MAX);
+static_assert(mul_sat(INT_MAX, -INT_MAX) == INT_MIN);
+static_assert(mul_sat(UINT_MAX, UINT_MAX) == UINT_MAX);
+static_assert(mul_sat(UINT_MAX, 0u) == 0);
+static_assert(mul_sat(0u, UINT_MAX) == 0);
+static_assert(mul_sat((short)SHRT_MAX, (short)2) == SHRT_MAX);
+static_assert(mul_sat((short)SHRT_MAX, (short)SHRT_MIN) == SHRT_MIN);
+static_assert(mul_sat<long long>(SHRT_MAX, 2) == 2L * SHRT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index f7c1cb4dfc09d2a..bf6aa23f20279f5 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -125,9 +125,36 @@ constexpr bool test() {
   return true;
 }
 
+#include <print>
+
 int main(int, char**) {
   assert(test());
   static_assert(test());
 
+  // std::println(stderr, "-----> {}", std::saturate_cast<signed short>(999));
+  // std::println(stderr, "-----> {}", std::saturate_cast<unsigned short>(999));
+  // assert(false);
+
   return 0;
 }
+
+#include <numeric>
+#include <climits>
+
+#if CHAR_BIT == 8
+static_assert(std::saturate_cast<unsigned char>(999) == 255);
+static_assert(std::saturate_cast<signed char>(999) == 127);
+#endif
+static_assert(std::saturate_cast<unsigned short>(999) == 999);
+static_assert(std::saturate_cast<signed short>(999) == 999);
+static_assert(std::saturate_cast<short>(INT_MAX) == SHRT_MAX);
+static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
+static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
+static_assert(std::saturate_cast<unsigned short>(UINT_MAX) == USHRT_MAX);
+static_assert(std::saturate_cast<int>(UINT_MAX) == INT_MAX);
+static_assert(std::saturate_cast<int>(INT_MAX) == INT_MAX);
+static_assert(std::saturate_cast<unsigned>(-1) == 0);
+static_assert(std::saturate_cast<unsigned>(INT_MIN) == 0);
+static_assert(std::saturate_cast<unsigned>(UINT_MAX) == UINT_MAX);
+static_assert(std::saturate_cast<unsigned>(LLONG_MAX) == UINT_MAX);
+static_assert(std::saturate_cast<unsigned>(ULLONG_MAX) == UINT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 2c5fcf6ec0f6990..3b3499bde792f78 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -121,3 +121,86 @@ int main(int, char**) {
 
   return 0;
 }
+
+#include <numeric>
+#include <limits>
+
+template<typename T, typename U>
+concept can_sub_sat
+  = requires(T t, U u) { { std::sub_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_sub_sat<int, int> );
+static_assert( not can_sub_sat<int, short> );
+static_assert( not can_sub_sat<unsigned, int> );
+static_assert( noexcept(std::sub_sat(0, 0)) );
+
+using std::sub_sat;
+
+// Signed type
+static_assert(sub_sat(0, 0) == 0);
+static_assert(sub_sat(1, 1) == 0);
+static_assert(sub_sat(-1, -1) == 0);
+static_assert(sub_sat(-1, 1) == -2);
+constexpr auto max = std::numeric_limits<int>::max();
+constexpr auto min = std::numeric_limits<int>::min();
+static_assert(sub_sat(max, 1) == max - 1);
+static_assert(sub_sat(1, max) == 1 - max);
+static_assert(sub_sat(max, max) == 0);
+static_assert(sub_sat(min, 1) == min);
+static_assert(sub_sat(min, 123) == min);
+static_assert(sub_sat(0, max) == min + 1);
+static_assert(sub_sat(-1, max) == min);
+static_assert(sub_sat(-2, max) == min);
+static_assert(sub_sat(-2, min) == max - 1);
+static_assert(sub_sat(-1, min) == max);
+static_assert(sub_sat(0, min) == max);
+static_assert(sub_sat(1, min) == max);
+static_assert(sub_sat(min, -1) == min + 1);
+static_assert(sub_sat(min, min) == 0);
+static_assert(sub_sat(max, min) == max);
+static_assert(sub_sat(min, max) == min);
+
+// Wider signed type than the args
+static_assert(sub_sat<long long>(max, min) == (long long)max * 2 + 1);
+static_assert(sub_sat<long long>(min, max) == (long long)min * 2 + 1);
+
+// Signed type that undergoes integer promotion
+constexpr auto shrt_max = std::numeric_limits<short>::max();
+constexpr auto shrt_min = std::numeric_limits<short>::min();
+static_assert(sub_sat<short>(0, 0) == 0);
+static_assert(sub_sat<short>(1, 1) == 0);
+static_assert(sub_sat<short>(3, 1) == 2);
+static_assert(sub_sat<short>(shrt_max, shrt_max) == 0);
+static_assert(sub_sat<short>(shrt_max, 1) == shrt_max - 1);
+static_assert(sub_sat<short>(1, shrt_max) == shrt_min + 2);
+static_assert(sub_sat<short>(shrt_max, shrt_min) == shrt_max);
+static_assert(sub_sat<short>(0, shrt_min) == shrt_max);
+static_assert(sub_sat<short>(shrt_min, (short)1) == shrt_min);
+static_assert(sub_sat<short>(shrt_min, (short)-1) == shrt_min + 1);
+static_assert(sub_sat<short>((short)-1, shrt_min) == shrt_max);
+static_assert(sub_sat<short>((short)1, shrt_min) == shrt_max);
+
+// Unsigned type
+static_assert(sub_sat(0u, 0u) == 0u);
+static_assert(sub_sat(1u, 1u) == 0u);
+static_assert(sub_sat(-1u, -1u) == 0u);
+static_assert(sub_sat(-1u, 1u) == -2u);
+constexpr auto umax = std::numeric_limits<unsigned>::max();
+static_assert(sub_sat(0u, 1u) == 0u);
+static_assert(sub_sat(umax, umax) == 0u);
+static_assert(sub_sat(umax, 0u) == umax);
+static_assert(sub_sat(0u, umax) == 0u);
+static_assert(sub_sat(umax, 1u) == umax - 1u);
+static_assert(sub_sat(0u, 0u) == 0u);
+
+// Wider unsigned type than the args
+static_assert(sub_sat<unsigned long long>(0u, umax) == 0u);
+
+// Unsigned type that undergoes integer promotion
+constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
+static_assert(sub_sat<unsigned short>(0, 0) == 0);
+static_assert(sub_sat<unsigned short>(1, 1) == 0);
+static_assert(sub_sat<unsigned short>(3, 1) == 2);
+static_assert(sub_sat<unsigned short>(ushrt_max, ushrt_max) == 0);
+static_assert(sub_sat<unsigned short>(0, 1) == 0);
+static_assert(sub_sat<unsigned short>(1, ushrt_max) == 0);

>From 248531aa9c21bda39f5482a87e49d4f451050033 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 22:23:49 +0200
Subject: [PATCH 18/38] WIP: Cleanup

---
 .../include/__numeric/saturation_arithmetic.h |  3 +-
 .../numeric.ops.sat/add_sat.pass.cpp          | 71 ----------------
 .../numeric.ops.sat/div_sat.pass.cpp          | 42 ----------
 .../numeric.ops.sat/mul_sat.pass.cpp          | 31 -------
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 27 ------
 .../numeric.ops.sat/sub_sat.pass.cpp          | 83 -------------------
 6 files changed, 1 insertion(+), 256 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 17efdd4b18c3e04..53955ab0a6a613d 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -107,10 +107,9 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
     // Handle overflow
     if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
       return std::numeric_limits<_Rp>::min();
-    // // x >= std::numeric_limits<_Rp>::max()
-    // return std::numeric_limits<_Rp>::max();
     if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
       return std::numeric_limits<_Rp>::max();
+    // No overflow
     return static_cast<_Rp>(__x);
   }
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index dc0485b1c75f734..1fdf647ff606e43 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -222,74 +222,3 @@ int main(int, char**) {
 
   return 0;
 }
-
-
-#include <numeric>
-#include <limits>
-
-template<typename T, typename U>
-concept can_add_sat
-  = requires(T t, U u) { { std::add_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_add_sat<int, int> );
-static_assert( not can_add_sat<int, short> );
-static_assert( not can_add_sat<unsigned, int> );
-static_assert( noexcept(std::add_sat(0, 0)) );
-
-using std::add_sat;
-
-// Signed type
-static_assert(add_sat(0, 0) == 0);
-static_assert(add_sat(1, 1) == 2);
-static_assert(add_sat(-1, -1) == -2);
-static_assert(add_sat(-1, 1) == 0);
-constexpr auto max = std::numeric_limits<int>::max();
-constexpr auto min = std::numeric_limits<int>::min();
-static_assert(add_sat(max, 1) == max);
-static_assert(add_sat(1, max) == max);
-static_assert(add_sat(max, max) == max);
-static_assert(add_sat(min, -1) == min);
-static_assert(add_sat(-1, min) == min);
-static_assert(add_sat(min, min) == min);
-static_assert(add_sat(max, min) == -1);
-static_assert(add_sat(min, max) == -1);
-
-// Wider signed type than the args
-static_assert(add_sat<long long>(max, max) == (long long)max * 2);
-static_assert(add_sat<long long>(min, min) == (long long)min * 2);
-
-// Signed type that undergoes integer promotion
-constexpr auto shrt_max = std::numeric_limits<short>::max();
-constexpr auto shrt_min = std::numeric_limits<short>::min();
-static_assert(add_sat<short>(0, 0) == 0);
-static_assert(add_sat<short>(1, 1) == 2);
-static_assert(add_sat<short>(shrt_max, shrt_max) == shrt_max);
-static_assert(add_sat<short>(shrt_max, 1) == shrt_max);
-static_assert(add_sat<short>(1, shrt_max) == shrt_max);
-static_assert(add_sat<short>(shrt_min, (short)-1) == shrt_min);
-static_assert(add_sat<short>((short)-1, shrt_min) == shrt_min);
-static_assert(add_sat<short>(shrt_min, (short)1) == -shrt_max);
-static_assert(add_sat<short>((short)1, shrt_min) == -shrt_max);
-
-// Unsigned type
-static_assert(add_sat(0u, 0u) == 0u);
-static_assert(add_sat(1u, 1u) == 2u);
-constexpr auto umax = std::numeric_limits<unsigned>::max();
-static_assert(add_sat(umax, 1u) == umax);
-static_assert(add_sat(1u, umax) == umax);
-static_assert(add_sat(umax, umax) == umax);
-static_assert(add_sat(0u, umax) == umax);
-static_assert(add_sat(umax, 0u) == umax);
-static_assert(add_sat(0u, 1u) == 1u);
-static_assert(add_sat(1u, 0u) == 1u);
-
-// Wider unsigned type than the args
-static_assert(add_sat<unsigned long long>(umax, umax) == (long long)umax * 2);
-
-// Unsigned type that undergoes integer promotion
-constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
-static_assert(add_sat<unsigned short>(0, 0) == 0);
-static_assert(add_sat<unsigned short>(1, 1) == 2);
-static_assert(add_sat<unsigned short>(ushrt_max, ushrt_max) == ushrt_max);
-static_assert(add_sat<unsigned short>(ushrt_max, 1) == ushrt_max);
-static_assert(add_sat<unsigned short>(1, ushrt_max) == ushrt_max);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 9e457f56e9d56cf..232f8b826520c9d 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -120,45 +120,3 @@ int main(int, char**) {
 
   return 0;
 }
-
-#include <numeric>
-#include <climits>
-
-template<typename T, typename U>
-concept can_div_sat
-  = requires(T t, U u) { { std::div_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_div_sat<int, int> );
-static_assert( not can_div_sat<int, short> );
-static_assert( not can_div_sat<unsigned, int> );
-static_assert( noexcept(std::div_sat(0, 1)) );
-
-using std::div_sat;
-
-static_assert(std::div_sat(0, 1) == 0);
-static_assert(std::div_sat(0, -1) == 0);
-static_assert(std::div_sat(1, -1) == -1);
-static_assert(std::div_sat(10, -2) == -5);
-static_assert(std::div_sat(-10, -2) == 5);
-static_assert(std::div_sat(INT_MAX, 1) == INT_MAX);
-static_assert(std::div_sat(INT_MIN, 1) == INT_MIN);
-static_assert(std::div_sat(INT_MIN + 1, -1) == INT_MAX);
-static_assert(std::div_sat(0u, 1u) == 0u);
-static_assert(std::div_sat(UINT_MAX, 1u) == UINT_MAX);
-static_assert(std::div_sat(INT_MIN, -1) == INT_MAX);
-static_assert(std::div_sat((short)SHRT_MIN, (short)-1) == SHRT_MAX);
-static_assert(std::div_sat(LONG_MIN, -1L) == LONG_MAX);
-static_assert(std::div_sat(LLONG_MIN, -1LL) == LLONG_MAX);
-
-template<auto N>
-std::integral_constant<decltype(N), std::div_sat(N, N-N)>
-div_sat_by_zero();
-
-template<auto N>
-concept can_div_sat_by_zero = requires { div_sat_by_zero<N>(); };
-
-static_assert( not can_div_sat_by_zero<0> );
-static_assert( not can_div_sat_by_zero<1> );
-static_assert( not can_div_sat_by_zero<1u> );
-static_assert( not can_div_sat_by_zero<-1L> );
-static_assert( not can_div_sat_by_zero<short(99)> );
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 64721c6ef97e8ed..1f4d0ab518bbea9 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -109,34 +109,3 @@ int main(int, char**) {
 
   return 0;
 }
-
-#include <numeric>
-#include <climits>
-
-template<typename T, typename U>
-concept can_mul_sat
-  = requires(T t, U u) { { std::mul_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_mul_sat<int, int> );
-static_assert( not can_mul_sat<int, short> );
-static_assert( not can_mul_sat<unsigned, int> );
-static_assert( noexcept(std::mul_sat(0, 0)) );
-
-using std::mul_sat;
-
-static_assert(mul_sat(1, 1) == 1);
-static_assert(mul_sat(10, 11) == 110);
-static_assert(mul_sat(INT_MAX / 2, 3) == INT_MAX);
-static_assert(mul_sat(INT_MAX / 2, -3) == INT_MIN);
-static_assert(mul_sat(INT_MAX / -2, 3) == INT_MIN);
-static_assert(mul_sat(INT_MIN / 2, -3) == INT_MAX);
-static_assert(mul_sat(INT_MIN, -1) == INT_MAX);
-static_assert(mul_sat(INT_MAX, -1) == INT_MIN + 1);
-static_assert(mul_sat(INT_MAX, INT_MAX) == INT_MAX);
-static_assert(mul_sat(INT_MAX, -INT_MAX) == INT_MIN);
-static_assert(mul_sat(UINT_MAX, UINT_MAX) == UINT_MAX);
-static_assert(mul_sat(UINT_MAX, 0u) == 0);
-static_assert(mul_sat(0u, UINT_MAX) == 0);
-static_assert(mul_sat((short)SHRT_MAX, (short)2) == SHRT_MAX);
-static_assert(mul_sat((short)SHRT_MAX, (short)SHRT_MIN) == SHRT_MIN);
-static_assert(mul_sat<long long>(SHRT_MAX, 2) == 2L * SHRT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index bf6aa23f20279f5..f7c1cb4dfc09d2a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -125,36 +125,9 @@ constexpr bool test() {
   return true;
 }
 
-#include <print>
-
 int main(int, char**) {
   assert(test());
   static_assert(test());
 
-  // std::println(stderr, "-----> {}", std::saturate_cast<signed short>(999));
-  // std::println(stderr, "-----> {}", std::saturate_cast<unsigned short>(999));
-  // assert(false);
-
   return 0;
 }
-
-#include <numeric>
-#include <climits>
-
-#if CHAR_BIT == 8
-static_assert(std::saturate_cast<unsigned char>(999) == 255);
-static_assert(std::saturate_cast<signed char>(999) == 127);
-#endif
-static_assert(std::saturate_cast<unsigned short>(999) == 999);
-static_assert(std::saturate_cast<signed short>(999) == 999);
-static_assert(std::saturate_cast<short>(INT_MAX) == SHRT_MAX);
-static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
-static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
-static_assert(std::saturate_cast<unsigned short>(UINT_MAX) == USHRT_MAX);
-static_assert(std::saturate_cast<int>(UINT_MAX) == INT_MAX);
-static_assert(std::saturate_cast<int>(INT_MAX) == INT_MAX);
-static_assert(std::saturate_cast<unsigned>(-1) == 0);
-static_assert(std::saturate_cast<unsigned>(INT_MIN) == 0);
-static_assert(std::saturate_cast<unsigned>(UINT_MAX) == UINT_MAX);
-static_assert(std::saturate_cast<unsigned>(LLONG_MAX) == UINT_MAX);
-static_assert(std::saturate_cast<unsigned>(ULLONG_MAX) == UINT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 3b3499bde792f78..2c5fcf6ec0f6990 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -121,86 +121,3 @@ int main(int, char**) {
 
   return 0;
 }
-
-#include <numeric>
-#include <limits>
-
-template<typename T, typename U>
-concept can_sub_sat
-  = requires(T t, U u) { { std::sub_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_sub_sat<int, int> );
-static_assert( not can_sub_sat<int, short> );
-static_assert( not can_sub_sat<unsigned, int> );
-static_assert( noexcept(std::sub_sat(0, 0)) );
-
-using std::sub_sat;
-
-// Signed type
-static_assert(sub_sat(0, 0) == 0);
-static_assert(sub_sat(1, 1) == 0);
-static_assert(sub_sat(-1, -1) == 0);
-static_assert(sub_sat(-1, 1) == -2);
-constexpr auto max = std::numeric_limits<int>::max();
-constexpr auto min = std::numeric_limits<int>::min();
-static_assert(sub_sat(max, 1) == max - 1);
-static_assert(sub_sat(1, max) == 1 - max);
-static_assert(sub_sat(max, max) == 0);
-static_assert(sub_sat(min, 1) == min);
-static_assert(sub_sat(min, 123) == min);
-static_assert(sub_sat(0, max) == min + 1);
-static_assert(sub_sat(-1, max) == min);
-static_assert(sub_sat(-2, max) == min);
-static_assert(sub_sat(-2, min) == max - 1);
-static_assert(sub_sat(-1, min) == max);
-static_assert(sub_sat(0, min) == max);
-static_assert(sub_sat(1, min) == max);
-static_assert(sub_sat(min, -1) == min + 1);
-static_assert(sub_sat(min, min) == 0);
-static_assert(sub_sat(max, min) == max);
-static_assert(sub_sat(min, max) == min);
-
-// Wider signed type than the args
-static_assert(sub_sat<long long>(max, min) == (long long)max * 2 + 1);
-static_assert(sub_sat<long long>(min, max) == (long long)min * 2 + 1);
-
-// Signed type that undergoes integer promotion
-constexpr auto shrt_max = std::numeric_limits<short>::max();
-constexpr auto shrt_min = std::numeric_limits<short>::min();
-static_assert(sub_sat<short>(0, 0) == 0);
-static_assert(sub_sat<short>(1, 1) == 0);
-static_assert(sub_sat<short>(3, 1) == 2);
-static_assert(sub_sat<short>(shrt_max, shrt_max) == 0);
-static_assert(sub_sat<short>(shrt_max, 1) == shrt_max - 1);
-static_assert(sub_sat<short>(1, shrt_max) == shrt_min + 2);
-static_assert(sub_sat<short>(shrt_max, shrt_min) == shrt_max);
-static_assert(sub_sat<short>(0, shrt_min) == shrt_max);
-static_assert(sub_sat<short>(shrt_min, (short)1) == shrt_min);
-static_assert(sub_sat<short>(shrt_min, (short)-1) == shrt_min + 1);
-static_assert(sub_sat<short>((short)-1, shrt_min) == shrt_max);
-static_assert(sub_sat<short>((short)1, shrt_min) == shrt_max);
-
-// Unsigned type
-static_assert(sub_sat(0u, 0u) == 0u);
-static_assert(sub_sat(1u, 1u) == 0u);
-static_assert(sub_sat(-1u, -1u) == 0u);
-static_assert(sub_sat(-1u, 1u) == -2u);
-constexpr auto umax = std::numeric_limits<unsigned>::max();
-static_assert(sub_sat(0u, 1u) == 0u);
-static_assert(sub_sat(umax, umax) == 0u);
-static_assert(sub_sat(umax, 0u) == umax);
-static_assert(sub_sat(0u, umax) == 0u);
-static_assert(sub_sat(umax, 1u) == umax - 1u);
-static_assert(sub_sat(0u, 0u) == 0u);
-
-// Wider unsigned type than the args
-static_assert(sub_sat<unsigned long long>(0u, umax) == 0u);
-
-// Unsigned type that undergoes integer promotion
-constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
-static_assert(sub_sat<unsigned short>(0, 0) == 0);
-static_assert(sub_sat<unsigned short>(1, 1) == 0);
-static_assert(sub_sat<unsigned short>(3, 1) == 2);
-static_assert(sub_sat<unsigned short>(ushrt_max, ushrt_max) == 0);
-static_assert(sub_sat<unsigned short>(0, 1) == 0);
-static_assert(sub_sat<unsigned short>(1, ushrt_max) == 0);

>From 14fb0cfa423e933294a26dfd4efa61cd66e95dcf Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 11:50:28 +0200
Subject: [PATCH 19/38] Updated tests: `add_sat`

---
 .../include/__numeric/saturation_arithmetic.h | 23 ++++---
 .../numeric.ops.sat/add_sat.pass.cpp          | 60 ++++++++++++++++++-
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 34 +++++++++++
 3 files changed, 102 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 53955ab0a6a613d..ef8ad824055d46c 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -99,19 +99,16 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
 
 template <__libcpp_integer _Rp, __libcpp_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
-  // Saturation is impossible.
-  if constexpr (std::cmp_less_equal(std::numeric_limits<_Rp>::min(), std::numeric_limits<_Tp>::min()) &&
-                std::cmp_greater_equal(std::numeric_limits<_Rp>::max(), std::numeric_limits<_Tp>::max()))
-    return static_cast<_Rp>(__x);
-  else {
-    // Handle overflow
-    if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
-      return std::numeric_limits<_Rp>::min();
-    if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
-      return std::numeric_limits<_Rp>::max();
-    // No overflow
-    return static_cast<_Rp>(__x);
-  }
+  // Saturation is impossible edge case when min _Rp < min _Tp and max _Rp > _Tp is expected to be optimized out by the
+  // compiler.
+
+  // Handle overflow
+  if (std::cmp_less(__x, std::numeric_limits<_Rp>::min()))
+    return std::numeric_limits<_Rp>::min();
+  if (std::cmp_greater(__x, std::numeric_limits<_Rp>::max()))
+    return std::numeric_limits<_Rp>::max();
+  // No overflow
+  return static_cast<_Rp>(__x);
 }
 
 #endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index 1fdf647ff606e43..55c4e1ada9fb92b 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -105,6 +105,16 @@ constexpr bool test_signed() {
     assert(sum == maxVal + IntegerT{-1});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
+    assert(sum == IntegerT{-1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, minVal);
+    assert(sum == IntegerT{-1});
+  }
+
   // Saturation - max - both arguments positive
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
@@ -151,9 +161,25 @@ constexpr bool test_unsigned() {
   static_assert(noexcept(std::div_sat(minVal, maxVal)));
 
   // No Saturation
+
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
-    assert(sum == IntegerT{55});
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{1});
+    assert(sum == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{0});
+    assert(sum == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{1});
+    assert(sum == IntegerT{2});
   }
 
   {
@@ -166,12 +192,42 @@ constexpr bool test_unsigned() {
     assert(sum == IntegerT{1});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, minVal);
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, minVal);
+    assert(sum == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
+    assert(sum == IntegerT{55});
+  }
+
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
     assert(sum == minVal);
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
+    assert(sum == maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, minVal);
+    assert(sum == maxVal);
+  }
+
   // Saturation - max only
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{1});
+    assert(sum == maxVal);
+  }
+
   {
     std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
     assert(sum == maxVal);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index f7c1cb4dfc09d2a..a2b3b982a6eb9a8 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -17,6 +17,16 @@
 #include <concepts>
 #include <limits>
 #include <numeric>
+#include <utility>
+
+// Signed T1 < Singed T2 - overflow
+// Signed T1 == Signed T2 - no overflow
+// Signed T1 > Signed T2 - no overflow
+
+// Unsigned T1 < Unsigned T2 - overflow
+// Unsigned T1 == Unsigned T2 - no overflow
+// Unsigned T1 > Unsigned T2 - no overflow
+
 
 template <typename IntegerResultT, typename IntegerT>
 constexpr bool test_signed_notsaturated() {
@@ -26,6 +36,22 @@ constexpr bool test_signed_notsaturated() {
   static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
   static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
 
+  assert(std::saturate_cast<IntegerResultT>(IntegerT{-1}) == IntegerT{-1});
+  assert(std::saturate_cast<IntegerResultT>(IntegerT{0}) == IntegerT{0});
+  assert(std::saturate_cast<IntegerResultT>(IntegerT{1}) == IntegerT{1});
+
+  {
+    // Large values
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+    assert(std::saturate_cast<IntegerResultT>(x) == x);
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    assert(std::saturate_cast<IntegerResultT>(x) == x);
+  }
+
   assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
   assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
 
@@ -43,6 +69,14 @@ constexpr bool test_signed_saturated() {
   assert(std::saturate_cast<IntegerResultT>(minVal) == std::numeric_limits<IntegerResultT>::min());
   assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
 
+  if constexpr (std::cmp_less(std::numeric_limits<IntegerResultT>::min(), std::numeric_limits<IntegerT>::min())) {
+    assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
+  }
+
+  if constexpr (std::cmp_greater(std::numeric_limits<IntegerResultT>::max(), std::numeric_limits<IntegerT>::max())) {
+    assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
+  }
+
   return true;
 }
 

>From 179ebaa67ab2fa5dffdd510ffbc02532fedb1b67 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 12:40:52 +0200
Subject: [PATCH 20/38] Updated tests: `div_sat`

---
 .../numeric.ops.sat/add_sat.pass.cpp          |   1 +
 .../numeric.ops.sat/div_sat.pass.cpp          | 196 ++++++++++++++++--
 .../numeric.ops.sat/saturate_cast.pass.cpp    |   9 -
 3 files changed, 176 insertions(+), 30 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index 55c4e1ada9fb92b..a739061f2672f2a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -234,6 +234,7 @@ constexpr bool test_unsigned() {
   }
 
   {
+    // Large values
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 232f8b826520c9d..7a21bb3f3a60ee8 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -27,10 +27,125 @@ constexpr bool test_signed() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, IntegerT{-1});
+    assert(quot == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, IntegerT{1});
+    assert(quot == IntegerT{-1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, minVal);
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, maxVal);
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, IntegerT{-1});
+    assert(quot == IntegerT{-1} * maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{-1});
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{1});
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, minVal);
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, IntegerT{-1});
+    assert(quot == IntegerT{-1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, minVal);
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, maxVal);
     assert(quot == IntegerT{0});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, IntegerT{1});
+    assert(quot == minVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, IntegerT{1});
+    assert(quot == maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{27}, IntegerT{28});
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{28}, IntegerT{27});
+    assert(quot == IntegerT{1});
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+    assert(sum == IntegerT{-1});
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-27};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+    assert(sum == IntegerT{-1});
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-28};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-27};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+    assert(sum == IntegerT{1});
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+    assert(sum == IntegerT{0});
+  }
+
   {
     std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, minVal);
     assert(quot == (maxVal / minVal));
@@ -59,16 +174,64 @@ constexpr bool test_unsigned() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{1});
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, maxVal);
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, IntegerT{1});
+    assert(quot == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, maxVal);
     assert(quot == IntegerT{0});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{27}, IntegerT{28});
+    assert(quot == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{28}, IntegerT{27});
+    assert(quot == IntegerT{1});
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+    assert(sum == IntegerT{0});
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{27};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+    assert(sum == IntegerT{1});
+  }
+
   {
     std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
-    assert(quot == (minVal / maxVal));
+    assert(quot == IntegerT{0});
   }
 
-  // Unsigned integer devision never overflow
+  {
+    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, maxVal);
+    assert(quot == IntegerT{1});
+  }
+
+  // Unsigned integer devision never overflows
 
   return true;
 }
@@ -96,27 +259,18 @@ constexpr bool test() {
   return true;
 }
 
-bool test_constexpr() {
-  // Signed
-  test_constexpr<signed char>();
-  test_constexpr<short int>();
-  test_constexpr<int>();
-  test_constexpr<long int>();
-  test_constexpr<long long int>();
-  // Unsigned
-  test_constexpr<unsigned char>();
-  test_constexpr<unsigned short int>();
-  test_constexpr<unsigned int>();
-  test_constexpr<unsigned long int>();
-  test_constexpr<unsigned long long int>();
-
-  return true;
-}
+// #include <print>
 
 int main(int, char**) {
+  using IntegerT = int;
+  // constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+
+  // std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, minVal);
+
+  // std::println(stderr, "--------> {}", quot);
+  // assert(false);
   assert(test());
   static_assert(test());
-  assert(test_constexpr());
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index a2b3b982a6eb9a8..8c53e3130c7fd9d 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -19,15 +19,6 @@
 #include <numeric>
 #include <utility>
 
-// Signed T1 < Singed T2 - overflow
-// Signed T1 == Signed T2 - no overflow
-// Signed T1 > Signed T2 - no overflow
-
-// Unsigned T1 < Unsigned T2 - overflow
-// Unsigned T1 == Unsigned T2 - no overflow
-// Unsigned T1 > Unsigned T2 - no overflow
-
-
 template <typename IntegerResultT, typename IntegerT>
 constexpr bool test_signed_notsaturated() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();

>From 191970dadfc9a7dfecccd628adc1bd108c52818d Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 13:17:15 +0200
Subject: [PATCH 21/38] Cleanup

---
 .../numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp | 2 +-
 .../numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp | 1 -
 .../numeric.ops/numeric.ops.sat/div_sat.pass.cpp         | 9 ---------
 .../numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp | 1 -
 .../numeric.ops.sat/saturate_cast.compile.pass.cpp       | 1 -
 .../numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp | 1 -
 6 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
index 56d2cfd4a2585ef..f92fedba6f1ae94 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
@@ -13,8 +13,8 @@
 // template<class T>
 // constexpr T add_sat(T x, T y) noexcept;                     // freestanding
 
+#include <concepts>
 #include <numeric>
-#include <limits>
 
 template <typename T, typename U>
 concept CanDo = requires(T x, U y) {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
index ffa30d9e3d5cfd6..a9069d7bc71baf9 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
@@ -15,7 +15,6 @@
 
 #include <concepts>
 #include <numeric>
-#include <limits>
 #include <type_traits>
 
 // Constraints
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 7a21bb3f3a60ee8..ea95ff4341c3855 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -259,16 +259,7 @@ constexpr bool test() {
   return true;
 }
 
-// #include <print>
-
 int main(int, char**) {
-  using IntegerT = int;
-  // constexpr auto minVal = std::numeric_limits<IntegerT>::min();
-
-  // std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, minVal);
-
-  // std::println(stderr, "--------> {}", quot);
-  // assert(false);
   assert(test());
   static_assert(test());
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
index 20105daf6b0719d..7f2b49392dcb9e6 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
@@ -15,7 +15,6 @@
 
 #include <concepts>
 #include <numeric>
-#include <limits>
 
 template <typename T, typename U>
 concept CanDo = requires(T x, U y) {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
index a1f53e0fd2cac06..de26897eb4fcd7b 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
@@ -15,7 +15,6 @@
 
 #include <concepts>
 #include <numeric>
-#include <limits>
 
 template <typename R, typename T>
 concept CanDo = requires(T x) {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
index cd27770d9a77dea..ccff03088c64be2 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
@@ -15,7 +15,6 @@
 
 #include <concepts>
 #include <numeric>
-#include <limits>
 
 template <typename T, typename U>
 concept CanDo = requires(T x, U y) {

>From b24b4a685faa6b37a7de72422eec9138ca7881fd Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 14:28:54 +0200
Subject: [PATCH 22/38] Updated tests: `mul_sat`

---
 .../numeric.ops.sat/mul_sat.pass.cpp          | 173 +++++++++++++++++-
 .../numeric.ops.sat/saturate_cast.pass.cpp    |   1 -
 2 files changed, 164 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 1f4d0ab518bbea9..39482ff1dcfdffa 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -27,34 +27,125 @@ constexpr bool test_signed() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{3}, IntegerT{4});
-    assert(prod == IntegerT{12});
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{0});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{-1});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{1});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{-1}, IntegerT{0});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{0});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{1});
+    assert(prod == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{27}, IntegerT{2});
+    assert(prod == IntegerT{54});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{2}, IntegerT{28});
+    assert(prod == IntegerT{56});
   }
 
   // Saturation - max - both arguments positive
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{4});
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
+    assert(sum == maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{1});
+    assert(prod == maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, maxVal);
     assert(prod == maxVal);
   }
 
   // Saturation - max - both arguments negative
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{-4});
+    // Large values
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
+    assert(sum == minVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{-1});
     assert(prod == maxVal);
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, minVal);
+    assert(sum == maxVal);
+  }
+
   // Saturation - min - left positive, right negative
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{-4});
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::mulSat(x, y);
+    assert(sum == minVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{-1});
     assert(prod == minVal);
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, minVal);
+    assert(sum == minVal);
+  }
+
   // Saturation - min - left negative, right positive
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{4});
+    // Large values
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
+    assert(sum == minVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{1});
     assert(prod == minVal);
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, maxVal);
+    assert(sum == minVal);
+  }
+
   return true;
 }
 
@@ -67,13 +158,77 @@ constexpr bool test_unsigned() {
 
   // No saturation
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{3}, IntegerT{4});
-    assert(prod == IntegerT{12});
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{0});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{1});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{0});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{1});
+    assert(prod == IntegerT{1});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{28}, IntegerT{2});
+    assert(prod == IntegerT{56});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{0});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(Integer{0}, minVal);
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{0});
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, maxVal);
+    assert(prod == IntegerT{0});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, maxVal);
+    assert(prod == IntegerT{0});
   }
 
   // Saturation
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{4});
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{1});
+    assert(prod == maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, maxVal);
+    assert(prod == maxVal);
+  }
+
+  {
+    // Large values
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
+    assert(sum == maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, maxVal);
     assert(prod == maxVal);
   }
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 8c53e3130c7fd9d..9f0f37e5e95b7d5 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -17,7 +17,6 @@
 #include <concepts>
 #include <limits>
 #include <numeric>
-#include <utility>
 
 template <typename IntegerResultT, typename IntegerT>
 constexpr bool test_signed_notsaturated() {

>From e108b3f05ce540e09d08032bfdf6773beee723ef Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 17:31:34 +0200
Subject: [PATCH 23/38] Fix CI

---
 libcxx/include/libcxx.imp                     |  1 +
 .../numeric.ops.sat/mul_sat.pass.cpp          | 36 ++++++++++++++-----
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index 8616f9639f4d903..45fa4a9541917f9 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -559,6 +559,7 @@
   { include: [ "<__numeric/pstl_reduce.h>", "private", "<numeric>", "public" ] },
   { include: [ "<__numeric/pstl_transform_reduce.h>", "private", "<numeric>", "public" ] },
   { include: [ "<__numeric/reduce.h>", "private", "<numeric>", "public" ] },
+  { include: [ "<__numeric/saturation_arithmetic.h>", "private", "<numeric>", "public" ] },
   { include: [ "<__numeric/transform_exclusive_scan.h>", "private", "<numeric>", "public" ] },
   { include: [ "<__numeric/transform_inclusive_scan.h>", "private", "<numeric>", "public" ] },
   { include: [ "<__numeric/transform_reduce.h>", "private", "<numeric>", "public" ] },
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 39482ff1dcfdffa..c84c5c95d7c78d9 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -66,6 +66,26 @@ constexpr bool test_signed() {
     assert(prod == IntegerT{56});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{1});
+    assert(prod == minVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, minVal);
+    assert(prod == minVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{-1});
+    assert(prod == IntegerT{-1} * maxVal);
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{-1}, maxVal);
+    assert(prod == IntegerT{-1} * maxVal);
+  }
+
   // Saturation - max - both arguments positive
   {
     // Large values
@@ -93,7 +113,7 @@ constexpr bool test_signed() {
     constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{28};
 
     std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
-    assert(sum == minVal);
+    assert(sum == maxVal);
   }
 
   {
@@ -112,15 +132,10 @@ constexpr bool test_signed() {
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{28};
 
-    std::same_as<IntegerT> decltype(auto) sum = std::mulSat(x, y);
+    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
     assert(sum == minVal);
   }
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{-1});
-    assert(prod == minVal);
-  }
-
   {
     std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, minVal);
     assert(sum == minVal);
@@ -188,7 +203,7 @@ constexpr bool test_unsigned() {
   }
 
   {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(Integer{0}, minVal);
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, minVal);
     assert(prod == IntegerT{0});
   }
 
@@ -207,6 +222,11 @@ constexpr bool test_unsigned() {
     assert(prod == IntegerT{0});
   }
 
+  {
+    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, minVal);
+    assert(prod == IntegerT{0});
+  }
+
   // Saturation
   {
     std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{1});

>From 3b8f7127a119c93e4b3a6d19bf60af54273caacb Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 18:04:22 +0200
Subject: [PATCH 24/38] Fix CI

---
 .../numeric.ops.sat/saturate_cast.pass.cpp          | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 9f0f37e5e95b7d5..d3250bb515ce57d 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -17,6 +17,7 @@
 #include <concepts>
 #include <limits>
 #include <numeric>
+#include <utility>
 
 template <typename IntegerResultT, typename IntegerT>
 constexpr bool test_signed_notsaturated() {
@@ -59,13 +60,13 @@ constexpr bool test_signed_saturated() {
   assert(std::saturate_cast<IntegerResultT>(minVal) == std::numeric_limits<IntegerResultT>::min());
   assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
 
-  if constexpr (std::cmp_less(std::numeric_limits<IntegerResultT>::min(), std::numeric_limits<IntegerT>::min())) {
-    assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
-  }
+  // if constexpr (std::cmp_less(std::numeric_limits<IntegerResultT>::min(), std::numeric_limits<IntegerT>::min())) {
+  //   assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
+  // }
 
-  if constexpr (std::cmp_greater(std::numeric_limits<IntegerResultT>::max(), std::numeric_limits<IntegerT>::max())) {
-    assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
-  }
+  // if constexpr (std::cmp_greater(std::numeric_limits<IntegerResultT>::max(), std::numeric_limits<IntegerT>::max())) {
+  //   assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
+  // }
 
   return true;
 }

>From 2289b2474fecc014c3efe18c8e367f4145524378 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 19 Jan 2024 09:20:34 +0200
Subject: [PATCH 25/38] Move release notes entry to avoid merge conflict.

---
 libcxx/docs/ReleaseNotes/18.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 325fc0e8ad17eb4..239e66c5dc97c17 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -58,9 +58,9 @@ Implemented Papers
 - P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?)
 - P2821R5 - ``span.at()``
 - P0521R0 - Proposed Resolution for CA 14 (``shared_ptr`` ``use_count/unique``)
+- P0543R3 - Saturation arithmetic
 - P1759R6 - Native handles and file streams
 - P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
-- P0543R3 - Saturation arithmetic
 
 
 Improvements and New Features

>From ed4e571e9043963be7d5964b2e35225e90129d81 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 19 Jan 2024 09:48:32 +0200
Subject: [PATCH 26/38] Addressed some comments - `TEST_HAS_NO_INT128`

---
 .../numeric.ops.sat/add_sat.compile.pass.cpp           |  4 ++--
 .../numeric.ops/numeric.ops.sat/add_sat.pass.cpp       |  8 ++++----
 .../numeric.ops.sat/div_sat.assert.pass.cpp            |  6 +++---
 .../numeric.ops.sat/div_sat.compile.pass.cpp           | 10 +++++-----
 .../numeric.ops/numeric.ops.sat/div_sat.pass.cpp       |  8 ++++----
 .../numeric.ops.sat/mul_sat.compile.pass.cpp           |  4 ++--
 .../numeric.ops/numeric.ops.sat/mul_sat.pass.cpp       |  6 +++---
 .../numeric.ops.sat/saturate_cast.compile.pass.cpp     |  4 ++--
 .../numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp |  6 +++---
 .../numeric.ops.sat/sub_sat.compile.pass.cpp           |  6 +++---
 .../numeric.ops/numeric.ops.sat/sub_sat.pass.cpp       |  6 +++---
 11 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
index f92fedba6f1ae94..d5a69dfd82b27aa 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
@@ -46,7 +46,7 @@ constexpr void test() {
   test_constraint_success<int, SI>();
   test_constraint_success<long int, SI>();
   test_constraint_success<long long int, int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__int128_t, SI>();
 #endif
   // Contraint success - Unsigned
@@ -56,7 +56,7 @@ constexpr void test() {
   test_constraint_success<unsigned int, UI>();
   test_constraint_success<unsigned long int, UI>();
   test_constraint_success<unsigned long long int, unsigned int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__uint128_t, UI>();
 #endif
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index a739061f2672f2a..dd7d203c6b7f81e 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -23,7 +23,7 @@ constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
-  static_assert(noexcept(std::div_sat(minVal, maxVal)));
+  static_assert(noexcept(std::add_sat(minVal, maxVal)));
 
   // No saturation (-1, 0, 1)
   {
@@ -257,7 +257,7 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_signed<__int128_t>();
 #endif
   // Unsigned
@@ -266,7 +266,7 @@ constexpr bool test() {
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_unsigned<__uint128_t>();
 #endif
 
@@ -274,7 +274,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  assert(test());
+  test();
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
index a67b714cf65fb25..f1b0b6236c22834 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
@@ -34,7 +34,7 @@ bool test() {
   test_runtime_assertion<int>();
   test_runtime_assertion<long int>();
   test_runtime_assertion<long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_runtime_assertion<__int128_t>();
 #endif
   // Unsigned
@@ -43,7 +43,7 @@ bool test() {
   test_runtime_assertion<unsigned int>();
   test_runtime_assertion<unsigned long int>();
   test_runtime_assertion<unsigned long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_runtime_assertion<__uint128_t>();
 #endif
 
@@ -51,7 +51,7 @@ bool test() {
 }
 
 int main(int, char**) {
-  assert(test());
+  test();
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
index a9069d7bc71baf9..7437a0861dd165a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
@@ -49,7 +49,7 @@ constexpr void test() {
   test_constraint_success<int, SI>();
   test_constraint_success<long int, SI>();
   test_constraint_success<long long int, int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__int128_t, SI>();
 #endif
   // Contraint success - Unsigned
@@ -59,14 +59,14 @@ constexpr void test() {
   test_constraint_success<unsigned int, UI>();
   test_constraint_success<unsigned long int, UI>();
   test_constraint_success<unsigned long long int, unsigned int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__uint128_t, UI>();
 #endif
 
   // Contraint failure
   test_constraint_fail<bool>();
   test_constraint_fail<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+#ifndef TEST_HAS_NO_INT128
   test_constraint_fail<wchar_t>();
 #endif
   test_constraint_fail<char8_t>();
@@ -93,7 +93,7 @@ static_assert(!CanDivByZero<static_cast<short int>(0)>);
 static_assert(!CanDivByZero<0>);
 static_assert(!CanDivByZero<0L>);
 static_assert(!CanDivByZero<0LL>);
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
 static_assert(!CanDivByZero<static_cast<__int128_t>(0)>);
 #endif
 static_assert(!CanDivByZero<static_cast<unsigned char>(0)>);
@@ -101,6 +101,6 @@ static_assert(!CanDivByZero<static_cast<unsigned short int>(0)>);
 static_assert(!CanDivByZero<0U>);
 static_assert(!CanDivByZero<0UL>);
 static_assert(!CanDivByZero<0ULL>);
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
 static_assert(!CanDivByZero<static_cast<__uint128_t>(0)>);
 #endif
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index ea95ff4341c3855..744cf0b62dcc717 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -231,7 +231,7 @@ constexpr bool test_unsigned() {
     assert(quot == IntegerT{1});
   }
 
-  // Unsigned integer devision never overflows
+  // Unsigned integer division never overflows
 
   return true;
 }
@@ -243,7 +243,7 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_signed<__int128_t>();
 #endif
   // Unsigned
@@ -252,7 +252,7 @@ constexpr bool test() {
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_unsigned<__uint128_t>();
 #endif
 
@@ -260,7 +260,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  assert(test());
+  test();
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
index 7f2b49392dcb9e6..4f50e7b868d25b8 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
@@ -46,7 +46,7 @@ constexpr void test() {
   test_constraint_success<int, SI>();
   test_constraint_success<long int, SI>();
   test_constraint_success<long long int, int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__int128_t, SI>();
 #endif
   // Contraint success - Unsigned
@@ -56,7 +56,7 @@ constexpr void test() {
   test_constraint_success<unsigned int, UI>();
   test_constraint_success<unsigned long int, UI>();
   test_constraint_success<unsigned long long int, unsigned int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__uint128_t, UI>();
 #endif
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index c84c5c95d7c78d9..8d64f5a3fc5c933 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -262,7 +262,7 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_signed<__int128_t>();
 #endif
   // Unsigned
@@ -271,7 +271,7 @@ constexpr bool test() {
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_unsigned<__uint128_t>();
 #endif
 
@@ -279,7 +279,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  assert(test());
+  test();
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
index de26897eb4fcd7b..2084767ae1c1238 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
@@ -47,7 +47,7 @@ constexpr void test() {
   test_constraint_success<SI, int>();
   test_constraint_success<SI, long int>();
   test_constraint_success<int, long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__int128_t, SI>();
 #endif
   // Contraint success - Unsigned
@@ -57,7 +57,7 @@ constexpr void test() {
   test_constraint_success<UI, unsigned int>();
   test_constraint_success<UI, unsigned long int>();
   test_constraint_success<unsigned int, unsigned long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<UI, __uint128_t>();
 #endif
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index d3250bb515ce57d..3bfa90ec6a16267 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -111,7 +111,7 @@ constexpr bool test() {
   test_signed_saturated<int, long long int>();
   test_signed_saturated<long int, long long int>();
   test_signed_saturated<long long int, long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_signed_notsaturated<__int128_t, signed char>();
   test_signed_notsaturated<__int128_t, short int>();
   test_signed_notsaturated<__int128_t, int>();
@@ -134,7 +134,7 @@ constexpr bool test() {
   test_unsigned_saturated<unsigned int, unsigned long long int>();
   test_unsigned_saturated<unsigned long int, unsigned long long int>();
   test_unsigned_saturated<unsigned long long int, unsigned long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_unsigned_notsaturated<__uint128_t, unsigned char>();
   test_unsigned_notsaturated<__uint128_t, unsigned short int>();
   test_unsigned_notsaturated<__uint128_t, unsigned int>();
@@ -151,7 +151,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  assert(test());
+  test();
   static_assert(test());
 
   return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
index ccff03088c64be2..2a0640ae539fbb9 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
@@ -46,7 +46,7 @@ constexpr void test() {
   test_constraint_success<int, SI>();
   test_constraint_success<long int, SI>();
   test_constraint_success<long long int, int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__int128_t, SI>();
 #endif
   // Contraint success - Unsigned
@@ -56,14 +56,14 @@ constexpr void test() {
   test_constraint_success<unsigned int, UI>();
   test_constraint_success<unsigned long int, UI>();
   test_constraint_success<unsigned long long int, unsigned int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_constraint_success<__uint128_t, UI>();
 #endif
 
   // Contraint failure
   test_constraint_fail<bool>();
   test_constraint_fail<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+#ifndef TEST_HAS_NO_INT128
   test_constraint_fail<wchar_t>();
 #endif
   test_constraint_fail<char8_t>();
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 2c5fcf6ec0f6990..f422bb986c69cd5 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -99,7 +99,7 @@ constexpr bool test() {
   test_signed<int>();
   test_signed<long int>();
   test_signed<long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_signed<__int128_t>();
 #endif
   // Unsigned
@@ -108,7 +108,7 @@ constexpr bool test() {
   test_unsigned<unsigned int>();
   test_unsigned<unsigned long int>();
   test_unsigned<unsigned long long int>();
-#ifndef _LIBCPP_HAS_NO_INT128
+#ifndef TEST_HAS_NO_INT128
   test_unsigned<__uint128_t>();
 #endif
 
@@ -116,7 +116,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  assert(test());
+  assert();
   static_assert(test());
 
   return 0;

>From eb6b9083f9f0f55704b2933f8758533e5d0beb1e Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 19 Jan 2024 11:22:18 +0200
Subject: [PATCH 27/38] Updated `add_sat.pass`

---
 .../numeric.ops.sat/add_sat.pass.cpp          | 238 +++++-------------
 .../numeric.ops.sat/div_sat.pass.cpp          |   1 +
 .../numeric.ops.sat/mul_sat.pass.cpp          |   1 +
 .../numeric.ops.sat/sub_sat.pass.cpp          |   1 +
 4 files changed, 61 insertions(+), 180 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index dd7d203c6b7f81e..b0eaa9cfd840bfa 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -23,132 +23,62 @@ constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
   static_assert(noexcept(std::add_sat(minVal, maxVal)));
 
-  // No saturation (-1, 0, 1)
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
-    assert(sum == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{1});
-    assert(sum == IntegerT{1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{0});
-    assert(sum == IntegerT{1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{-1});
-    assert(sum == IntegerT{-1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{0});
-    assert(sum == IntegerT{-1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{1});
-    assert(sum == IntegerT{2});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{-1});
-    assert(sum == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{1});
-    assert(sum == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{-1});
-    assert(sum == IntegerT{-2});
-  }
-
-  // No saturation (any value)
+  // clang-format off
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
-    assert(sum == IntegerT{55});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-27}, IntegerT{28});
-    assert(sum == IntegerT{1});
-  }
+  // No saturation (-1, 0, 1)
 
-  // No saturation (min, -1, 0, 1, max)
+  assert(std::add_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{-2});
+  assert(std::add_sat(IntegerT{-1}, IntegerT{ 0}) == IntegerT{-1});
+  assert(std::add_sat(IntegerT{-1}, IntegerT{ 1}) == IntegerT{ 0});
+  assert(std::add_sat(IntegerT{ 0}, IntegerT{-1}) == IntegerT{-1});
+  assert(std::add_sat(IntegerT{ 0}, IntegerT{ 0}) == IntegerT{ 0});
+  assert(std::add_sat(IntegerT{ 0}, IntegerT{ 1}) == IntegerT{ 1});
+  assert(std::add_sat(IntegerT{ 1}, IntegerT{-1}) == IntegerT{ 0});
+  assert(std::add_sat(IntegerT{ 1}, IntegerT{ 0}) == IntegerT{ 1});
+  assert(std::add_sat(IntegerT{ 1}, IntegerT{ 1}) == IntegerT{ 2});
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
-    assert(sum == minVal);
-  }
+  // No saturation (Large values)
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
-    assert(sum == minVal + IntegerT{1});
-  }
+  assert(std::add_sat(IntegerT{-27}, IntegerT{28})== IntegerT{ 1});
+  assert(std::add_sat(IntegerT{ 27}, IntegerT{28})== IntegerT{55});
 
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{0});
-    assert(sum == maxVal);
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+    assert(std::add_sat(x, y) == maxVal);
   }
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{-1});
-    assert(sum == maxVal + IntegerT{-1});
-  }
+  // No saturation (min, max)
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
-    assert(sum == IntegerT{-1});
-  }
+  assert(std::add_sat(minVal, IntegerT{ 0}) == minVal);
+  assert(std::add_sat(minVal, IntegerT{ 1}) == minVal + IntegerT{ 1});
+  assert(std::add_sat(minVal,       maxVal) == IntegerT{-1});
+  assert(std::add_sat(maxVal,       minVal) == IntegerT{-1});
+  assert(std::add_sat(maxVal, IntegerT{ 0}) == maxVal);
+  assert(std::add_sat(maxVal, IntegerT{-1}) == maxVal + IntegerT{-1});
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, minVal);
-    assert(sum == IntegerT{-1});
-  }
+  // Saturation
 
-  // Saturation - max - both arguments positive
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
-    assert(sum == maxVal);
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
+    assert(std::add_sat(x, y) == minVal);
   }
-
   {
-    // Large values
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
-    assert(sum == maxVal);
+    assert(std::add_sat(x, y) == maxVal);
   }
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, maxVal);
-    assert(sum == maxVal);
-  }
+  assert(std::add_sat(minVal,       minVal) == minVal);
+  assert(std::add_sat(minVal, IntegerT{-1}) == minVal);
+  assert(std::add_sat(maxVal, IntegerT{ 1}) == maxVal);
+  assert(std::add_sat(maxVal,       maxVal) == maxVal);
 
-  // Saturation - min - both arguments negative
-  {
-    // Large values
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
-    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
-    assert(sum == minVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
-    assert(sum == minVal);
-  }
+  // clang-format on
 
   return true;
 }
@@ -158,94 +88,42 @@ constexpr bool test_unsigned() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
-  static_assert(noexcept(std::div_sat(minVal, maxVal)));
-
-  // No Saturation
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
-    assert(sum == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{1});
-    assert(sum == IntegerT{1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{0});
-    assert(sum == IntegerT{1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{1});
-    assert(sum == IntegerT{2});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
-    assert(sum == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
-    assert(sum == IntegerT{1});
-  }
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
+  static_assert(noexcept(std::add_sat(minVal, maxVal)));
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, minVal);
-    assert(sum == IntegerT{0});
-  }
+  // clang-format off
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, minVal);
-    assert(sum == IntegerT{1});
-  }
+  // No Saturation
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
-    assert(sum == IntegerT{55});
-  }
+  assert(std::add_sat(IntegerT{0}, IntegerT{0}) == IntegerT{0});
+  assert(std::add_sat(IntegerT{0}, IntegerT{1}) == IntegerT{1});
+  assert(std::add_sat(IntegerT{1}, IntegerT{0}) == IntegerT{1});
+  assert(std::add_sat(IntegerT{1}, IntegerT{1}) == IntegerT{2});
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
-    assert(sum == minVal);
-  }
+  assert(std::add_sat(IntegerT{27}, IntegerT{28}) == IntegerT{55});
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
-    assert(sum == maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, minVal);
-    assert(sum == maxVal);
-  }
+  assert(std::add_sat(     minVal,      minVal) == minVal);
+  assert(std::add_sat(     minVal, IntegerT{0}) == IntegerT{0});
+  assert(std::add_sat(     minVal, IntegerT{1}) == IntegerT{1});
+  assert(std::add_sat(     minVal,      maxVal) == maxVal);
+  assert(std::add_sat(IntegerT{0},      minVal) == IntegerT{0});
+  assert(std::add_sat(IntegerT{1},      minVal) == IntegerT{1});
+  assert(std::add_sat(     maxVal,      minVal) == maxVal);
+  assert(std::add_sat(     maxVal, IntegerT{0}) == maxVal);
 
   // Saturation - max only
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{1});
-    assert(sum == maxVal);
-  }
 
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
-    assert(sum == maxVal);
-  }
-
-  {
-    // Large values
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
-    assert(sum == maxVal);
+    assert(std::add_sat(     x,           y) == maxVal);
+    assert(std::add_sat(     x,      maxVal) == maxVal);
+    assert(std::add_sat(maxVal, IntegerT{1}) == maxVal);
+    assert(std::add_sat(maxVal,           y) == maxVal);
+    assert(std::add_sat(maxVal,      maxVal) == maxVal);
   }
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, maxVal);
-    assert(sum == maxVal);
-  }
+  // clang-format on
 
   return true;
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 744cf0b62dcc717..a82fcfdaf781eee 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -23,6 +23,7 @@ constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minMax, minMax);
   static_assert(noexcept(std::div_sat(minVal, maxVal)));
 
   // No saturation
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 8d64f5a3fc5c933..6489a53a8e5b83e 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -23,6 +23,7 @@ constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, maxVal);
   static_assert(noexcept(std::mul_sat(minVal, maxVal)));
 
   // No saturation
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index f422bb986c69cd5..41c13d38903948a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -23,6 +23,7 @@ constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, maxVal);
   static_assert(noexcept(std::sub_sat(minVal, maxVal)));
 
   // No saturation

>From 20de72c25dd1394c593a57c165d8cf03b84bc36e Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 19 Jan 2024 12:44:40 +0200
Subject: [PATCH 28/38] Updated tests `div_sat.pass`

---
 .../numeric.ops.sat/add_sat.pass.cpp          |   5 +-
 .../numeric.ops.sat/div_sat.pass.cpp          | 238 +++++-------------
 2 files changed, 69 insertions(+), 174 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
index b0eaa9cfd840bfa..b621255e51b4cc0 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -33,18 +33,21 @@ constexpr bool test_signed() {
   assert(std::add_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{-2});
   assert(std::add_sat(IntegerT{-1}, IntegerT{ 0}) == IntegerT{-1});
   assert(std::add_sat(IntegerT{-1}, IntegerT{ 1}) == IntegerT{ 0});
+  assert(std::add_sat(IntegerT{-1},       maxVal) == IntegerT{-1} + maxVal);
   assert(std::add_sat(IntegerT{ 0}, IntegerT{-1}) == IntegerT{-1});
   assert(std::add_sat(IntegerT{ 0}, IntegerT{ 0}) == IntegerT{ 0});
   assert(std::add_sat(IntegerT{ 0}, IntegerT{ 1}) == IntegerT{ 1});
+  assert(std::add_sat(IntegerT{ 0},       minVal) == minVal);
+  assert(std::add_sat(IntegerT{ 0},       maxVal) == maxVal);
   assert(std::add_sat(IntegerT{ 1}, IntegerT{-1}) == IntegerT{ 0});
   assert(std::add_sat(IntegerT{ 1}, IntegerT{ 0}) == IntegerT{ 1});
   assert(std::add_sat(IntegerT{ 1}, IntegerT{ 1}) == IntegerT{ 2});
+  assert(std::add_sat(IntegerT{ 1},       minVal) == IntegerT{ 1} + minVal);
 
   // No saturation (Large values)
 
   assert(std::add_sat(IntegerT{-27}, IntegerT{28})== IntegerT{ 1});
   assert(std::add_sat(IntegerT{ 27}, IntegerT{28})== IntegerT{55});
-
   {
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index a82fcfdaf781eee..44e436557a16cee 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -23,145 +23,57 @@ constexpr bool test_signed() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
-  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minMax, minMax);
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
   static_assert(noexcept(std::div_sat(minVal, maxVal)));
 
-  // No saturation
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, IntegerT{-1});
-    assert(quot == IntegerT{1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, IntegerT{1});
-    assert(quot == IntegerT{-1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, minVal);
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, maxVal);
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, IntegerT{-1});
-    assert(quot == IntegerT{-1} * maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{-1});
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{1});
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, minVal);
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, IntegerT{-1});
-    assert(quot == IntegerT{-1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, minVal);
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, maxVal);
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, IntegerT{1});
-    assert(quot == minVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, IntegerT{1});
-    assert(quot == maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{27}, IntegerT{28});
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{28}, IntegerT{27});
-    assert(quot == IntegerT{1});
-  }
+  // No saturation (-1, 0, 1)
 
-  {
-    // Large values
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
-    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+  assert(std::div_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{ 1});
+  assert(std::div_sat(IntegerT{-1}, IntegerT{ 1}) == IntegerT{-1});
+  assert(std::div_sat(IntegerT{-1},       minVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{-1},       maxVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 0}, IntegerT{-1}) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 0}, IntegerT{ 1}) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 0},       minVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 1}, IntegerT{-1}) == IntegerT{-1});
+  assert(std::div_sat(IntegerT{ 1},       minVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 1},       maxVal) == IntegerT{ 0});
 
-    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
-    assert(sum == IntegerT{-1});
-  }
+  // No saturation (Large values)
 
+  assert(std::div_sat(IntegerT{27}, IntegerT{28}) == IntegerT{0});
+  assert(std::div_sat(IntegerT{28}, IntegerT{27}) == IntegerT{1});
   {
-    // Large values
-    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
-    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-27};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
-    assert(sum == IntegerT{-1});
+    constexpr IntegerT lesserVal = minVal / IntegerT{2} + IntegerT{-28};
+    constexpr IntegerT biggerVal = minVal / IntegerT{2} + IntegerT{-27};
+    assert(std::div_sat(lesserVal, biggerVal) == IntegerT{1});
+    assert(std::div_sat(biggerVal, lesserVal) == IntegerT{0});
   }
-
   {
-    // Large values
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
-    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
-    assert(sum == IntegerT{0});
+    constexpr IntegerT lesserVal = minVal / IntegerT{2} + IntegerT{-27};
+    constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
+    assert(std::div_sat(lesserVal, biggerVal) == IntegerT{-1});
+    assert(std::div_sat(biggerVal, lesserVal) == IntegerT{-1});
   }
 
   {
-    // Large values
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-28};
-    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-27};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
-    assert(sum == IntegerT{1});
-  }
-
-  {
-    // Large values
-    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
-    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
-    assert(sum == IntegerT{0});
+    constexpr IntegerT lesserVal = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
+    assert(std::div_sat(lesserVal, biggerVal) == IntegerT{0});
+    assert(std::div_sat(biggerVal, lesserVal) == IntegerT{1});
   }
 
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, minVal);
-    assert(quot == (maxVal / minVal));
-  }
+  // No saturation (min, max)
 
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
-    assert(quot == (minVal / maxVal));
-  }
+  assert(std::div_sat(minVal, IntegerT{ 1}) == minVal);
+  assert(std::div_sat(minVal,       maxVal) == (minVal / maxVal));
+  assert(std::div_sat(maxVal,       minVal) == (maxVal / minVal));
+  assert(std::div_sat(maxVal, IntegerT{-1}) ==-maxVal);
+  assert(std::div_sat(maxVal, IntegerT{ 1}) == maxVal);
 
   // Saturation - max only
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, IntegerT{-1});
-    assert(quot == maxVal);
-  }
+
+  assert(std::div_sat(minVal, IntegerT{-1}) == maxVal);
 
   return true;
 }
@@ -171,69 +83,38 @@ constexpr bool test_unsigned() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
   static_assert(noexcept(std::div_sat(minVal, maxVal)));
 
-  // No saturation
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{1});
-    assert(quot == IntegerT{0});
-  }
+  // clang-format off
 
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, maxVal);
-    assert(quot == IntegerT{0});
-  }
+  // No saturation (0, 1)
 
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, IntegerT{1});
-    assert(quot == IntegerT{1});
-  }
+  assert(std::div_sat(IntegerT{0}, IntegerT{1}) == IntegerT{0});
+  assert(std::div_sat(IntegerT{0},      maxVal) == IntegerT{0});
+  assert(std::div_sat(IntegerT{1}, IntegerT{1}) == IntegerT{1});
+  assert(std::div_sat(IntegerT{1},      maxVal) == IntegerT{0});
 
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, maxVal);
-    assert(quot == IntegerT{0});
-  }
+  // No saturation (large values)
 
+  assert(std::div_sat(IntegerT{27}, IntegerT{28}) == IntegerT{0});
+  assert(std::div_sat(IntegerT{28}, IntegerT{27}) == IntegerT{1});
   {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{27}, IntegerT{28});
-    assert(quot == IntegerT{0});
+    constexpr IntegerT lesserVal = maxVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
+    assert(std::div_sat(lesserVal, biggerVal) == IntegerT{0});
+    assert(std::div_sat(biggerVal, lesserVal) == IntegerT{1});
   }
 
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{28}, IntegerT{27});
-    assert(quot == IntegerT{1});
-  }
+  // No saturation (min, max)
 
-  {
-    // Large values
-    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
-    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
-    assert(sum == IntegerT{0});
-  }
-
-  {
-    // Large values
-    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
-    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{27};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
-    assert(sum == IntegerT{1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
-    assert(quot == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, maxVal);
-    assert(quot == IntegerT{1});
-  }
+  assert(std::div_sat(minVal, maxVal) == IntegerT{0});
+  assert(std::div_sat(maxVal, maxVal) == IntegerT{1});
 
   // Unsigned integer division never overflows
 
+  // clang-format on
+
   return true;
 }
 
@@ -260,7 +141,18 @@ constexpr bool test() {
   return true;
 }
 
+#include <print>
+
 int main(int, char**) {
+  // using IntegerT               = int;
+  // constexpr auto minVal        = std::numeric_limits<IntegerT>::min();
+  // constexpr auto maxVal        = std::numeric_limits<IntegerT>::max();
+  // constexpr IntegerT lesserVal = minVal / IntegerT{2} + IntegerT{-27};
+  // constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
+  // std::println(stderr, "---------------- = {} |", lesserVal / biggerVal);
+  // std::println(stderr, "---------------- = {} |", biggerVal / lesserVal);
+  // std::println(stderr, "---------------- = {} |", minVal / -1);
+  // assert(false);
   test();
   static_assert(test());
 

>From 3dfb7a02421d09abb36f5f04c38df51785212216 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 19 Jan 2024 13:24:33 +0200
Subject: [PATCH 29/38] Updated test `mul_sat.pass`

---
 .../numeric.ops.sat/div_sat.pass.cpp          |  34 +--
 .../numeric.ops.sat/mul_sat.pass.cpp          | 236 +++++-------------
 2 files changed, 67 insertions(+), 203 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 44e436557a16cee..056bd6571727c75 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -28,16 +28,16 @@ constexpr bool test_signed() {
 
   // No saturation (-1, 0, 1)
 
-  assert(std::div_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{ 1});
-  assert(std::div_sat(IntegerT{-1}, IntegerT{ 1}) == IntegerT{-1});
-  assert(std::div_sat(IntegerT{-1},       minVal) == IntegerT{ 0});
-  assert(std::div_sat(IntegerT{-1},       maxVal) == IntegerT{ 0});
-  assert(std::div_sat(IntegerT{ 0}, IntegerT{-1}) == IntegerT{ 0});
-  assert(std::div_sat(IntegerT{ 0}, IntegerT{ 1}) == IntegerT{ 0});
-  assert(std::div_sat(IntegerT{ 0},       minVal) == IntegerT{ 0});
-  assert(std::div_sat(IntegerT{ 1}, IntegerT{-1}) == IntegerT{-1});
-  assert(std::div_sat(IntegerT{ 1},       minVal) == IntegerT{ 0});
-  assert(std::div_sat(IntegerT{ 1},       maxVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{1});
+  assert(std::div_sat(IntegerT{-1}, IntegerT{1}) == IntegerT{-1});
+  assert(std::div_sat(IntegerT{-1}, minVal) == IntegerT{0});
+  assert(std::div_sat(IntegerT{-1}, maxVal) == IntegerT{0});
+  assert(std::div_sat(IntegerT{0}, IntegerT{-1}) == IntegerT{0});
+  assert(std::div_sat(IntegerT{0}, IntegerT{1}) == IntegerT{0});
+  assert(std::div_sat(IntegerT{0}, minVal) == IntegerT{0});
+  assert(std::div_sat(IntegerT{1}, IntegerT{-1}) == IntegerT{-1});
+  assert(std::div_sat(IntegerT{1}, minVal) == IntegerT{0});
+  assert(std::div_sat(IntegerT{1}, maxVal) == IntegerT{0});
 
   // No saturation (Large values)
 
@@ -55,7 +55,6 @@ constexpr bool test_signed() {
     assert(std::div_sat(lesserVal, biggerVal) == IntegerT{-1});
     assert(std::div_sat(biggerVal, lesserVal) == IntegerT{-1});
   }
-
   {
     constexpr IntegerT lesserVal = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
@@ -68,7 +67,7 @@ constexpr bool test_signed() {
   assert(std::div_sat(minVal, IntegerT{ 1}) == minVal);
   assert(std::div_sat(minVal,       maxVal) == (minVal / maxVal));
   assert(std::div_sat(maxVal,       minVal) == (maxVal / minVal));
-  assert(std::div_sat(maxVal, IntegerT{-1}) ==-maxVal);
+  assert(std::div_sat(maxVal, IntegerT{-1}) == -maxVal);
   assert(std::div_sat(maxVal, IntegerT{ 1}) == maxVal);
 
   // Saturation - max only
@@ -141,18 +140,7 @@ constexpr bool test() {
   return true;
 }
 
-#include <print>
-
 int main(int, char**) {
-  // using IntegerT               = int;
-  // constexpr auto minVal        = std::numeric_limits<IntegerT>::min();
-  // constexpr auto maxVal        = std::numeric_limits<IntegerT>::max();
-  // constexpr IntegerT lesserVal = minVal / IntegerT{2} + IntegerT{-27};
-  // constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
-  // std::println(stderr, "---------------- = {} |", lesserVal / biggerVal);
-  // std::println(stderr, "---------------- = {} |", biggerVal / lesserVal);
-  // std::println(stderr, "---------------- = {} |", minVal / -1);
-  // assert(false);
   test();
   static_assert(test());
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 6489a53a8e5b83e..f30c46d96a3173c 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -26,141 +26,65 @@ constexpr bool test_signed() {
   [[maybe_unused]] std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, maxVal);
   static_assert(noexcept(std::mul_sat(minVal, maxVal)));
 
-  // No saturation
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{0});
-    assert(prod == IntegerT{0});
-  }
+  // clang-format off
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{-1});
-    assert(prod == IntegerT{0});
-  }
+  // No saturation (-1, 0, 1)
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{1});
-    assert(prod == IntegerT{0});
-  }
+  assert(std::mul_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{ 1});
+  assert(std::mul_sat(IntegerT{-1}, IntegerT{ 0}) == IntegerT{ 0});
+  assert(std::mul_sat(IntegerT{-1}, IntegerT{ 1}) == IntegerT{-1});
+  assert(std::mul_sat(IntegerT{-1},       maxVal) == -maxVal);
+  assert(std::mul_sat(IntegerT{ 0}, IntegerT{-1}) == IntegerT{ 0});
+  assert(std::mul_sat(IntegerT{ 0}, IntegerT{ 0}) == IntegerT{ 0});
+  assert(std::mul_sat(IntegerT{ 0}, IntegerT{ 1}) == IntegerT{ 0});
+  assert(std::mul_sat(IntegerT{ 1}, IntegerT{-1}) == IntegerT{-1});
+  assert(std::mul_sat(IntegerT{ 1}, IntegerT{ 0}) == IntegerT{ 0});
+  assert(std::mul_sat(IntegerT{ 1}, IntegerT{ 1}) == IntegerT{ 1});
+  assert(std::mul_sat(IntegerT{ 1},       minVal) == minVal);
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{-1}, IntegerT{0});
-    assert(prod == IntegerT{0});
-  }
+  // No saturation (large values)
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{0});
-    assert(prod == IntegerT{0});
-  }
+  assert(std::mul_sat(IntegerT{27}, IntegerT{ 2}) == IntegerT{54});
+  assert(std::mul_sat(IntegerT{ 2}, IntegerT{28}) == IntegerT{56});
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{1});
-    assert(prod == IntegerT{1});
-  }
+  // No saturation (min, max)
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{27}, IntegerT{2});
-    assert(prod == IntegerT{54});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{2}, IntegerT{28});
-    assert(prod == IntegerT{56});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{1});
-    assert(prod == minVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, minVal);
-    assert(prod == minVal);
-  }
+  assert(std::mul_sat(      minVal, IntegerT{ 1}) == minVal);
+  assert(std::mul_sat(      maxVal, IntegerT{-1}) == -maxVal);
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{-1});
-    assert(prod == IntegerT{-1} * maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{-1}, maxVal);
-    assert(prod == IntegerT{-1} * maxVal);
-  }
-
-  // Saturation - max - both arguments positive
-  {
-    // Large values
-    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
-    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
-    assert(sum == maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{1});
-    assert(prod == maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, maxVal);
-    assert(prod == maxVal);
-  }
+  // Saturation
 
-  // Saturation - max - both arguments negative
   {
-    // Large values
     constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
-    assert(sum == maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{-1});
-    assert(prod == maxVal);
+    assert(std::mul_sat(x, y) == maxVal);
   }
-
   {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, minVal);
-    assert(sum == maxVal);
+    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+    assert(std::mul_sat(x, y) == minVal);
   }
-
-  // Saturation - min - left positive, right negative
   {
-    // Large values
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
-    assert(sum == minVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, minVal);
-    assert(sum == minVal);
+    assert(std::mul_sat(x, y) == minVal);
   }
-
-  // Saturation - min - left negative, right positive
   {
-    // Large values
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
-    assert(sum == minVal);
+    assert(std::mul_sat(x, y) == maxVal);
   }
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{1});
-    assert(prod == minVal);
-  }
+  // e.g. signed char range -128 to 127
+  assert(std::mul_sat(minVal, IntegerT{-1}) == maxVal);
+  assert(std::mul_sat(minVal, IntegerT{ 1}) == minVal);
+  assert(std::mul_sat(minVal,       minVal) == maxVal);
+  assert(std::mul_sat(minVal,       maxVal) == minVal);
+  assert(std::mul_sat(maxVal, IntegerT{ 1}) == maxVal);
+  assert(std::mul_sat(maxVal,       maxVal) == maxVal);
+  assert(std::mul_sat(maxVal,       minVal) == minVal);
 
-  {
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, maxVal);
-    assert(sum == minVal);
-  }
+  // clang-format on
 
   return true;
 }
@@ -172,86 +96,38 @@ constexpr bool test_unsigned() {
 
   static_assert(noexcept(std::mul_sat(minVal, maxVal)));
 
-  // No saturation
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{0});
-    assert(prod == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, IntegerT{1});
-    assert(prod == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{0});
-    assert(prod == IntegerT{0});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, IntegerT{1});
-    assert(prod == IntegerT{1});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{28}, IntegerT{2});
-    assert(prod == IntegerT{56});
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{0});
-    assert(prod == IntegerT{0});
-  }
+  // No saturation (0, 1)
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, minVal);
-    assert(prod == IntegerT{0});
-  }
+  assert(std::mul_sat(IntegerT{0}, IntegerT{0}) == IntegerT{0});
+  assert(std::mul_sat(IntegerT{0}, IntegerT{1}) == IntegerT{0});
+  assert(std::mul_sat(IntegerT{0},      minVal) == IntegerT{0});
+  assert(std::mul_sat(IntegerT{0},      maxVal) == IntegerT{0});
+  assert(std::mul_sat(IntegerT{1}, IntegerT{0}) == IntegerT{0});
+  assert(std::mul_sat(IntegerT{1}, IntegerT{1}) == IntegerT{1});
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{0});
-    assert(prod == IntegerT{0});
-  }
+  // No saturation (large value)
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{0}, maxVal);
-    assert(prod == IntegerT{0});
-  }
+  assert(std::mul_sat(IntegerT{28}, IntegerT{2}) == IntegerT{56});
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, maxVal);
-    assert(prod == IntegerT{0});
-  }
+  // No saturation (min, max)
 
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, minVal);
-    assert(prod == IntegerT{0});
-  }
+  assert(std::mul_sat(minVal, IntegerT{0}) == IntegerT{0});
+  assert(std::mul_sat(minVal, IntegerT{1}) == IntegerT{0});
+  assert(std::mul_sat(minVal,      maxVal) == IntegerT{0});
+  assert(std::mul_sat(maxVal, IntegerT{0}) == IntegerT{0});
+  assert(std::mul_sat(maxVal, IntegerT{1}) == maxVal);
+  assert(std::mul_sat(maxVal,      minVal) == IntegerT{0});
 
   // Saturation
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{1});
-    assert(prod == maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{1}, maxVal);
-    assert(prod == maxVal);
-  }
 
+  assert(std::mul_sat(IntegerT{1}, maxVal) == maxVal);
+  assert(std::mul_sat(maxVal, IntegerT{1}) == maxVal);
   {
-    // Large values
     constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
     constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
-
-    std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(x, y);
-    assert(sum == maxVal);
-  }
-
-  {
-    std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, maxVal);
-    assert(prod == maxVal);
+    assert(std::mul_sat(x, y) == maxVal);
   }
+  assert(std::mul_sat(maxVal, maxVal) == maxVal);
 
   return true;
 }

>From a6435a94c53df382df73e1d95b0d29919adc1728 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 19 Jan 2024 15:55:33 +0200
Subject: [PATCH 30/38] Updated test `sub_sat.pass`

---
 .../include/__numeric/saturation_arithmetic.h |   6 +-
 .../saturate_cast.compile.pass.cpp            |   1 +
 .../numeric.ops.sat/sub_sat.pass.cpp          | 106 +++++++++++-------
 3 files changed, 70 insertions(+), 43 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index ef8ad824055d46c..b73c0ef0f89c48d 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -32,8 +32,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
     return std::numeric_limits<_Tp>::max();
   } else {
     // Signed addition overflow
-    if (__x > 0)
-      // Overflows if (x > 0 && y > 0)
+    if (__x >= 0)
+      // Overflows if (x >= 0 && y > 0)
       return std::numeric_limits<_Tp>::max();
     else
       // Overflows if  (x < 0 && y < 0)
@@ -51,7 +51,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
     return std::numeric_limits<_Tp>::min();
   } else {
     // Signed subtration overflow
-    if (__x > 0)
+    if (__x >= 0)
       // Overflows if (x > 0 && y < 0)
       return std::numeric_limits<_Tp>::max();
     else
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
index 2084767ae1c1238..77f25da99623722 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
@@ -25,6 +25,7 @@ template <typename R, typename T>
 constexpr void test_constraint_success() {
   static_assert(CanDo<R, T>);
   static_assert(CanDo<T, T>);
+  static_assert(CanDo<T, R>);
 }
 
 template <typename T>
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 41c13d38903948a..35f7af2f3af4771 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -26,40 +26,55 @@ constexpr bool test_signed() {
   [[maybe_unused]] std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, maxVal);
   static_assert(noexcept(std::sub_sat(minVal, maxVal)));
 
-  // No saturation
-  {
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(IntegerT{3}, IntegerT{4});
-    assert(diff == IntegerT{-1});
-  }
+  // clang-format off
 
-  // Saturation - min - left negative, right positive
-  {
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, IntegerT{4});
-    assert(diff == minVal);
-  }
+  assert(std::sub_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{ 0});
+  assert(std::sub_sat(IntegerT{-1}, IntegerT{ 0}) == IntegerT{-1});
+  assert(std::sub_sat(IntegerT{-1}, IntegerT{ 1}) == IntegerT{-2});
+  assert(std::sub_sat(IntegerT{-1},       minVal) == IntegerT{-1} - minVal);
+  assert(std::sub_sat(IntegerT{-1},       maxVal) == IntegerT{-1} - maxVal);
+  assert(std::sub_sat(IntegerT{ 0}, IntegerT{-1}) == IntegerT{ 1});
+  assert(std::sub_sat(IntegerT{ 0}, IntegerT{ 0}) == IntegerT{ 0});
+  assert(std::sub_sat(IntegerT{ 0}, IntegerT{ 1}) == IntegerT{-1});
+  assert(std::sub_sat(IntegerT{ 0},       maxVal) == -maxVal);
 
-  {
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
-    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+  // No saturation (large value)
+  
+  assert(std::sub_sat(IntegerT{ 27}, IntegerT{-28}) ==  55);
+  assert(std::sub_sat(IntegerT{ 27}, IntegerT{ 28}) ==  -1);
+  assert(std::sub_sat(IntegerT{-27}, IntegerT{ 28}) == -55);
+  assert(std::sub_sat(IntegerT{-27}, IntegerT{-28}) ==   1);
 
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
-    assert(diff == minVal);
-  }
+  // No saturation (min, max)
+
+  assert(std::sub_sat(minVal, IntegerT{-1}) == minVal - IntegerT{-1});
+  assert(std::sub_sat(minVal, IntegerT{ 0}) == minVal);
+  assert(std::sub_sat(minVal,       minVal) == IntegerT{0});
+  assert(std::sub_sat(maxVal, IntegerT{ 0}) == maxVal);
+  assert(std::sub_sat(maxVal,       maxVal) == IntegerT{0});
+
+  // Saturation
+
+  assert(std::sub_sat(IntegerT{ 0},       minVal) == maxVal);
 
-  // Saturation - max - left postitive, right negative
   {
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(maxVal, IntegerT{-4});
-    assert(diff == maxVal);
+    constexpr IntegerT lesserVal = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
+    assert(std::sub_sat(lesserVal, biggerVal) == minVal);
   }
-
   {
-    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
-    constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{27};
-
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
-    assert(diff == maxVal);
+    constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
+    constexpr IntegerT lesserVal = minVal / IntegerT{2} + IntegerT{27};
+    assert(std::sub_sat(biggerVal, lesserVal) == maxVal);
   }
 
+  assert(std::sub_sat(minVal, IntegerT{ 1}) == minVal);
+  assert(std::sub_sat(minVal,       maxVal) == minVal);
+  assert(std::sub_sat(maxVal, IntegerT{-1}) == maxVal);
+  assert(std::sub_sat(maxVal,       minVal) == maxVal);
+
+  // clang-format on
+
   return true;
 }
 
@@ -68,27 +83,38 @@ constexpr bool test_unsigned() {
   constexpr auto minVal = std::numeric_limits<IntegerT>::min();
   constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
 
+  [[maybe_unused]] std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, maxVal);
+  static_assert(noexcept(std::sub_sat(minVal, maxVal)));
   static_assert(noexcept(std::sub_sat(minVal, maxVal)));
 
-  // No saturation
-  {
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(IntegerT{3}, IntegerT{1});
-    assert(diff == IntegerT{2});
-  }
+  // clang-format off
+
+  // No saturation (0, 1)
+
+  assert(std::sub_sat(IntegerT{0}, IntegerT{0}) == IntegerT{0});
+  assert(std::sub_sat(IntegerT{1}, IntegerT{0}) == IntegerT{1});
+  assert(std::sub_sat(IntegerT{1}, IntegerT{1}) == IntegerT{0});
+
+  // No saturatn (min, max)
+
+  assert(std::sub_sat(minVal, IntegerT{0}) == minVal);
+  assert(std::sub_sat(minVal,      maxVal) == minVal);
+  assert(std::sub_sat(minVal,      maxVal) == minVal);
+
+  // Saturation
+
+  assert(std::sub_sat(IntegerT{0}, IntegerT{1}) == minVal);
+  assert(std::sub_sat(IntegerT{0},      maxVal) == minVal);
 
-  // Saturation - min only
   {
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, IntegerT{4});
-    assert(diff == minVal);
+    constexpr IntegerT lesserVal = minVal / IntegerT{2} + IntegerT{27};
+    constexpr IntegerT biggerVal = maxVal / IntegerT{2} + IntegerT{28};
+    assert(std::sub_sat(lesserVal, biggerVal) == minVal);
   }
 
-  {
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
-    constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+  assert(std::sub_sat(minVal, IntegerT{1}) == minVal);
 
-    std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
-    assert(diff == minVal);
-  }
+  // clang-format on
 
   return true;
 }
@@ -117,7 +143,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  assert();
+  test();
   static_assert(test());
 
   return 0;

>From 80483e7bab930844f2ca41b873e7bd118968a5f1 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 12:45:11 +0200
Subject: [PATCH 31/38] Updated test `saturate_cast.pass`

---
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 211 +++++++++---------
 1 file changed, 105 insertions(+), 106 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 3bfa90ec6a16267..d03df0005d4954a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -14,138 +14,137 @@
 //   constexpr R saturate_cast(T x) noexcept;                     // freestanding
 
 #include <cassert>
-#include <concepts>
 #include <limits>
 #include <numeric>
-#include <utility>
 
-template <typename IntegerResultT, typename IntegerT>
-constexpr bool test_signed_notsaturated() {
-  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
-  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+static_assert(noexcept(saturate_cast<signed char>(std::numeric_limits<signed int>::max())));
+static_assert(noexcept(saturate_cast<signed char>(std::numeric_limits<unsigned int>::max())));
+static_assert(noexcept(saturate_cast<unsigned char>(std::numeric_limits<signed int>::max())));
+static_assert(noexcept(saturate_cast<unsigned char>(std::numeric_limits<unsigned int>::max())));
 
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+constexpr bool test_signed() {
+  // clang-format on
 
-  assert(std::saturate_cast<IntegerResultT>(IntegerT{-1}) == IntegerT{-1});
-  assert(std::saturate_cast<IntegerResultT>(IntegerT{0}) == IntegerT{0});
-  assert(std::saturate_cast<IntegerResultT>(IntegerT{1}) == IntegerT{1});
+#ifndef TEST_HAS_NO_INT128
+  using BiggestSIntT = __int128_t;
+#else
+  using BiggestSIntT = long long int;
+#endif
 
-  {
-    // Large values
-    constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
-    assert(std::saturate_cast<IntegerResultT>(x) == x);
-  }
+  // signed char: -128 to 127
 
-  {
-    // Large values
-    constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
-    assert(std::saturate_cast<IntegerResultT>(x) == x);
-  }
+  assert(std::saturate_cast<signed char, BiggestSIntT>(-255) == -128);
+  assert(std::saturate_cast<signed char, BiggestSIntT>(-128) == -128);
+  assert(std::saturate_cast<signed char, BiggestSIntT>(0) == 0);
+  assert(std::saturate_cast<signed char, BiggestSIntT>(127) == 127);
+  assert(std::saturate_cast<signed char, BiggestSIntT>(255) == 127);
 
-  assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
-  assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
+  // short: -32,768 to 32,767
 
-  return true;
-}
+  assert(std::saturate_cast<short int, BiggestSIntT>(-255) == -128);
+  assert(std::saturate_cast<short int, BiggestSIntT>(-32'768) == -32'767);
+  assert(std::saturate_cast<short int, BiggestSIntT>(0) == 0);
+  assert(std::saturate_cast<short int, BiggestSIntT>(32'768) == 32'768);
+  assert(std::saturate_cast<short int, BiggestSIntT>(255) == 32'768);
 
-template <typename IntegerResultT, typename IntegerT>
-constexpr bool test_signed_saturated() {
-  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
-  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+  // int: -2,147,483,648 to 2,147,483,647
 
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+  assert(std::saturate_cast<int, BiggestSIntT>(-255) == -2'147'483'648);
+  assert(std::saturate_cast<int, BiggestSIntT>(-2'147'483'648) == -2'147'483'648);
+  assert(std::saturate_cast<int, BiggestSIntT>(0) == 0);
+  assert(std::saturate_cast<int, BiggestSIntT>(2'147'483'647) == 2'147'483'647);
+  assert(std::saturate_cast<int, BiggestSIntT>(255) == 2'147'483'647);
 
-  assert(std::saturate_cast<IntegerResultT>(minVal) == std::numeric_limits<IntegerResultT>::min());
-  assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+  // long: -2,147,483,648 to 2,147,483,647
 
-  // if constexpr (std::cmp_less(std::numeric_limits<IntegerResultT>::min(), std::numeric_limits<IntegerT>::min())) {
-  //   assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
-  // }
+  assert(std::saturate_cast<long int, BiggestSIntT>(-255) == -2'147'483'648);
+  assert(std::saturate_cast<long int, BiggestSIntT>(-2'147'483'648) == -2'147'483'648);
+  assert(std::saturate_cast<long int, BiggestSIntT>(0) == 0);
+  assert(std::saturate_cast<long int, BiggestSIntT>(2'147'483'647) == 2'147'483'647);
+  assert(std::saturate_cast<long int, BiggestSIntT>(255) == 2'147'483'647);
 
-  // if constexpr (std::cmp_greater(std::numeric_limits<IntegerResultT>::max(), std::numeric_limits<IntegerT>::max())) {
-  //   assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
-  // }
+  // long long: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 
-  return true;
+  assert(std::saturate_cast<long long int, BiggestSIntT>(-255) == -9'223'372'036'854'775'808);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(-9'223'372'036'854'775'808) == -9'223'372'036'854'775'808);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(0) == 0);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(9'223'372'036'854'775'807) == 9'223'372'036'854'775'807);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(255) == 9'223'372'036'854'775'807);
+
+#ifndef TEST_HAS_NO_INT128
+  constexpr auto int128min = std::numeric_limits<__int128_t>::min();
+  constexpr auto int128max = std::numeric_limits<__int128_t>::max();
+
+  assert(std::saturate_cast<long long int, BiggestSIntT>(int128min) == -9'223'372'036'854'775'808);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(-9'223'372'036'854'775'808) == -9'223'372'036'854'775'808);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(0) == 0);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(9'223'372'036'854'775'807) == 9'223'372'036'854'775'807);
+  assert(std::saturate_cast<long long int, BiggestSIntT>(int128max) == 9'223'372'036'854'775'807);
+
+  assert(std::saturate_cast<__int128_t, BiggestSIntT>(int128min) == int128min);
+  assert(std::saturate_cast<__int128_t, BiggestSIntT>(0) == 0);
+  assert(std::saturate_cast<__int128_t, BiggestSIntT>(int128max) == int128max);
+#endif
+  // clang-format off
 }
 
-template <typename IntegerResultT, typename IntegerT>
-constexpr bool test_unsigned_notsaturated() {
-  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
-  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+constexpr void test_unsigned()
+{
+  // clang-format off
 
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+#ifndef TEST_HAS_NO_INT128
+  using BiggestUIntT = __uint128_t;
+#else 
+  using BiggestUIntT = unsigned long long int;
+#endif
 
-  assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
-  assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
+  // unsigned char: 0 to 255
 
-  return true;
-}
+  assert(std::saturate_cast<unsigned char, BiggestUIntT>(0) == 0);
+  assert(std::saturate_cast<unsigned char, BiggestUIntT>(127) == 127);
+  assert(std::saturate_cast<unsigned char, BiggestUIntT>(255) == 127);
 
-template <typename IntegerResultT, typename IntegerT>
-constexpr bool test_unsigned_saturated() {
-  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
-  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+  // short: 0 to 65,535
 
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
-  static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+  assert(std::saturate_cast<unsigned short int, BiggestUIntT>(0) == 0);
+  assert(std::saturate_cast<unsigned short int, BiggestUIntT>(65'535) == 65'535);
+  assert(std::saturate_cast<unsigned short int, BiggestUIntT>(255) == 65'535);
 
-  assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
-  assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+  // unsigned int: 0 to 4,294,967,295
 
-  return true;
-}
+  assert(std::saturate_cast<unsigned int, BiggestUIntT>(0) == 0);
+  assert(std::saturate_cast<unsigned int, BiggestUIntT>(4'294'967'295) == 4'294'967'295);
+  assert(std::saturate_cast<unsigned int, BiggestUIntT>(255) == 4'294'967'295);
+
+  // unsigned long: 0 to 4,294,967,295
+
+  assert(std::saturate_cast<unsigned long int, BiggestUIntT>(0) == 0);
+  assert(std::saturate_cast<unsigned long int, BiggestUIntT>(4'294'967'295) == 4'294'967'295);
+  assert(std::saturate_cast<unsigned long int, BiggestUIntT>(255) == 4'294'967'295);
+
+  // unsigned long long: 0 to 18,446,744,073,709,551,615
+
+  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(0) == 0);
+  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(18'446'744'073'709'551'615) == 118'446'744'073'709'551'615);
+  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(255) == 18'446'744'073'709'551'615);
 
-constexpr bool test() {
-  // Signed
-  test_signed_notsaturated<long long int, signed char>();
-  test_signed_notsaturated<long long int, short int>();
-  test_signed_notsaturated<long long int, int>();
-  test_signed_notsaturated<long long int, long int>();
-  test_signed_notsaturated<long long int, long long int>();
-  test_signed_saturated<signed char, long long int>();
-  test_signed_saturated<short int, long long int>();
-  test_signed_saturated<int, long long int>();
-  test_signed_saturated<long int, long long int>();
-  test_signed_saturated<long long int, long long int>();
-#ifndef TEST_HAS_NO_INT128
-  test_signed_notsaturated<__int128_t, signed char>();
-  test_signed_notsaturated<__int128_t, short int>();
-  test_signed_notsaturated<__int128_t, int>();
-  test_signed_notsaturated<__int128_t, long int>();
-  test_signed_notsaturated<__int128_t, long long int>();
-  test_signed_saturated<signed char, __int128_t>();
-  test_signed_saturated<short int, __int128_t>();
-  test_signed_saturated<int, __int128_t>();
-  test_signed_saturated<long int, __int128_t>();
-  test_signed_saturated<long long int, __int128_t>();
-#endif
-  // Unsigned
-  test_unsigned_notsaturated<unsigned long long int, unsigned char>();
-  test_unsigned_notsaturated<unsigned long long int, unsigned short int>();
-  test_unsigned_notsaturated<unsigned long long int, unsigned int>();
-  test_unsigned_notsaturated<unsigned long long int, unsigned long int>();
-  test_unsigned_notsaturated<unsigned long long int, unsigned long long int>();
-  test_unsigned_saturated<unsigned char, unsigned long long int>();
-  test_unsigned_saturated<unsigned short int, unsigned long long int>();
-  test_unsigned_saturated<unsigned int, unsigned long long int>();
-  test_unsigned_saturated<unsigned long int, unsigned long long int>();
-  test_unsigned_saturated<unsigned long long int, unsigned long long int>();
 #ifndef TEST_HAS_NO_INT128
-  test_unsigned_notsaturated<__uint128_t, unsigned char>();
-  test_unsigned_notsaturated<__uint128_t, unsigned short int>();
-  test_unsigned_notsaturated<__uint128_t, unsigned int>();
-  test_unsigned_notsaturated<__uint128_t, unsigned long int>();
-  test_unsigned_notsaturated<__uint128_t, unsigned long long int>();
-  test_unsigned_saturated<unsigned char, __uint128_t>();
-  test_unsigned_saturated<unsigned short int, __uint128_t>();
-  test_unsigned_saturated<unsigned int, __uint128_t>();
-  test_unsigned_saturated<unsigned long int, __uint128_t>();
-  test_unsigned_saturated<unsigned long long int, __uint128_t>();
+  constexpr auto uint128min = std::numeric_limits<__uint128_t>::min();
+  constexpr auto uint128max = std::numeric_limits<__uint128_t>::max();
+
+  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(0) == 0);
+  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(18'446'744'073'709'551'615) == 18'446'744'073'709'551'615);
+  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(int128max) == 18'446'744'073'709'551'615);
+
+  assert(std::saturate_cast<__uint128_t, BiggestUIntT>(0) == 0);
+  assert(std::saturate_cast<__uint128_t, BiggestUIntT>(uint128max) == uint128max);
 #endif
+  // clang-format off
+}
+
+constexpr bool test() {
+  test_signed();
+  test_unsigned();
 
   return true;
 }
@@ -155,4 +154,4 @@ int main(int, char**) {
   static_assert(test());
 
   return 0;
-}
+}
\ No newline at end of file

>From 1c2316b1f01d4a515e9f6840d98393a53a014936 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 15:02:20 +0200
Subject: [PATCH 32/38] Updated test `saturate_cast.pass`

---
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 194 ++++++++++--------
 1 file changed, 106 insertions(+), 88 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index d03df0005d4954a..a9fc5669d157a96 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -17,141 +17,159 @@
 #include <limits>
 #include <numeric>
 
-static_assert(noexcept(saturate_cast<signed char>(std::numeric_limits<signed int>::max())));
-static_assert(noexcept(saturate_cast<signed char>(std::numeric_limits<unsigned int>::max())));
-static_assert(noexcept(saturate_cast<unsigned char>(std::numeric_limits<signed int>::max())));
-static_assert(noexcept(saturate_cast<unsigned char>(std::numeric_limits<unsigned int>::max())));
+static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<signed int>::max())));
+static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<unsigned int>::max())));
+static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<signed int>::max())));
+static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<unsigned int>::max())));
 
-constexpr bool test_signed() {
-  // clang-format on
+#include <print>
+
+// constexpr
+bool test() {
+  // clang-format off
 
 #ifndef TEST_HAS_NO_INT128
-  using BiggestSIntT = __int128_t;
+  using SIntT = __int128_t;
+  using UIntT = __uint128_t;
 #else
-  using BiggestSIntT = long long int;
+  using SIntT = long long int;
+  using UIntT = unsigned long long int;
 #endif
 
+  // Biggest numbers
+
+  constexpr auto sintMin = std::numeric_limits<SIntT>::min();
+  constexpr auto sintMax = std::numeric_limits<SIntT>::max();
+  constexpr auto sintZero = SIntT{0};
+
+  constexpr auto uintMin = std::numeric_limits<UIntT>::min();
+  constexpr auto uintMax = std::numeric_limits<UIntT>::max();
+  constexpr auto uintZero = UIntT{0};
+
+  // Limits
+
+  // constexpr auto std_scharMin = std::numeric_limits<signed char>::min();
+  // constexpr auto std_scharMax = std::numeric_limits<signed char>::max();
+
+  // constexpr auto std_ucharMin = std::numeric_limits<unsigned char>::min();
+  // constexpr auto std_ucharMax = std::numeric_limits<unsigned char>::max();
+
+  // constexpr auto std_ssintMin = std::numeric_limits<signed short int>::min();
+  // constexpr auto std_ssintMax = std::numeric_limits<unsigned short int>::max();
+
+  // constexpr auto std_usintMin = std::numeric_limits<signed short int>::min();
+  // constexpr auto std_usintMax = std::numeric_limits<unsigned short int>::max();
+
+  // constexpr auto std_sintMin = std::numeric_limits<signed int>::min();
+  // constexpr auto std_sintMax = std::numeric_limits<signed int>::max();
+
+  // constexpr auto std_uintMin = std::numeric_limits<unsigned int>::min();
+  // constexpr auto std_uintMax = std::numeric_limits<unsigned int>::max();
+
+  constexpr auto std_slMin = std::numeric_limits<signed long int>::min();
+  constexpr auto std_slMax = std::numeric_limits<signed long int>::max();
+
+  // constexpr auto std_ulMin = std::numeric_limits<unsigned long int>::min();
+  constexpr auto std_ulMax = std::numeric_limits<unsigned long int>::max();
+
+  constexpr auto std_sllMin = std::numeric_limits<signed long long int>::min();
+  constexpr auto std_sllMax = std::numeric_limits<signed long long int>::max();
+  
+  // constexpr auto std_ullMin = std::numeric_limits<unsigned long long int>::min();
+  constexpr auto std_ullMax = std::numeric_limits<unsigned long long int>::max();
+
   // signed char: -128 to 127
 
-  assert(std::saturate_cast<signed char, BiggestSIntT>(-255) == -128);
-  assert(std::saturate_cast<signed char, BiggestSIntT>(-128) == -128);
-  assert(std::saturate_cast<signed char, BiggestSIntT>(0) == 0);
-  assert(std::saturate_cast<signed char, BiggestSIntT>(127) == 127);
-  assert(std::saturate_cast<signed char, BiggestSIntT>(255) == 127);
+  assert(std::saturate_cast<signed char>(sintMin) == -128);
+  assert(std::saturate_cast<signed char>(-128) == -128);
+  assert(std::saturate_cast<signed char>(0) == 0);
+  assert(std::saturate_cast<signed char>(127) == 127);
+  assert(std::saturate_cast<signed char>(sintMax) == 127);
 
   // short: -32,768 to 32,767
 
-  assert(std::saturate_cast<short int, BiggestSIntT>(-255) == -128);
-  assert(std::saturate_cast<short int, BiggestSIntT>(-32'768) == -32'767);
-  assert(std::saturate_cast<short int, BiggestSIntT>(0) == 0);
-  assert(std::saturate_cast<short int, BiggestSIntT>(32'768) == 32'768);
-  assert(std::saturate_cast<short int, BiggestSIntT>(255) == 32'768);
+  // std::println(stderr, "results: {}", std::saturate_cast<short int>(sintMin));
+  assert(std::saturate_cast<short int>(sintMin) == -32'768);
+  assert(std::saturate_cast<short int>(-32'768) == -32'768);
+  assert(std::saturate_cast<short int>(0) == 0);
+  assert(std::saturate_cast<short int>(32'767) == 32'767);
+  assert(std::saturate_cast<short int>(sintMax) == 32'767);
 
   // int: -2,147,483,648 to 2,147,483,647
 
-  assert(std::saturate_cast<int, BiggestSIntT>(-255) == -2'147'483'648);
-  assert(std::saturate_cast<int, BiggestSIntT>(-2'147'483'648) == -2'147'483'648);
-  assert(std::saturate_cast<int, BiggestSIntT>(0) == 0);
-  assert(std::saturate_cast<int, BiggestSIntT>(2'147'483'647) == 2'147'483'647);
-  assert(std::saturate_cast<int, BiggestSIntT>(255) == 2'147'483'647);
+  assert(std::saturate_cast<int>(sintMin) == -2'147'483'648);
+  assert(std::saturate_cast<int>(-2'147'483'648) == -2'147'483'648);
+  assert(std::saturate_cast<int>(0) == 0);
+  assert(std::saturate_cast<int>(2'147'483'647) == 2'147'483'647);
+  assert(std::saturate_cast<int>(sintMax) == 2'147'483'647);
 
   // long: -2,147,483,648 to 2,147,483,647
 
-  assert(std::saturate_cast<long int, BiggestSIntT>(-255) == -2'147'483'648);
-  assert(std::saturate_cast<long int, BiggestSIntT>(-2'147'483'648) == -2'147'483'648);
-  assert(std::saturate_cast<long int, BiggestSIntT>(0) == 0);
-  assert(std::saturate_cast<long int, BiggestSIntT>(2'147'483'647) == 2'147'483'647);
-  assert(std::saturate_cast<long int, BiggestSIntT>(255) == 2'147'483'647);
+  assert(std::saturate_cast<long int>(sintMin) == std_slMin);
+  // assert(std::saturate_cast<long int>(-2'147'483'648L) == std_slMin);
+  assert(std::saturate_cast<long int>(0L) == 0L);
+  // assert(std::saturate_cast<long int>(2'147'483'647L) == std_slMax);
+  assert(std::saturate_cast<long int>(sintMax) == std_slMax);
 
   // long long: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 
-  assert(std::saturate_cast<long long int, BiggestSIntT>(-255) == -9'223'372'036'854'775'808);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(-9'223'372'036'854'775'808) == -9'223'372'036'854'775'808);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(0) == 0);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(9'223'372'036'854'775'807) == 9'223'372'036'854'775'807);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(255) == 9'223'372'036'854'775'807);
-
-#ifndef TEST_HAS_NO_INT128
-  constexpr auto int128min = std::numeric_limits<__int128_t>::min();
-  constexpr auto int128max = std::numeric_limits<__int128_t>::max();
-
-  assert(std::saturate_cast<long long int, BiggestSIntT>(int128min) == -9'223'372'036'854'775'808);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(-9'223'372'036'854'775'808) == -9'223'372'036'854'775'808);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(0) == 0);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(9'223'372'036'854'775'807) == 9'223'372'036'854'775'807);
-  assert(std::saturate_cast<long long int, BiggestSIntT>(int128max) == 9'223'372'036'854'775'807);
-
-  assert(std::saturate_cast<__int128_t, BiggestSIntT>(int128min) == int128min);
-  assert(std::saturate_cast<__int128_t, BiggestSIntT>(0) == 0);
-  assert(std::saturate_cast<__int128_t, BiggestSIntT>(int128max) == int128max);
-#endif
-  // clang-format off
-}
-
-constexpr void test_unsigned()
-{
-  // clang-format off
+  assert(std::saturate_cast<long long int>(sintMin) == std_sllMin);
+  assert(std::saturate_cast<long long int>(sintMin) == -std_sllMin);
+  // assert(std::saturate_cast<long long int>(-9'223'372'036'854'775'808LL) == std_sllMin);
+  assert(std::saturate_cast<long long int>(0LL) == 0LL);
+  assert(std::saturate_cast<long long int>(9'223'372'036'854'775'807LL) == std_sllMax);
+  assert(std::saturate_cast<long long int>(sintMax) == std_sllMax);
 
 #ifndef TEST_HAS_NO_INT128
-  using BiggestUIntT = __uint128_t;
-#else 
-  using BiggestUIntT = unsigned long long int;
+  assert(std::saturate_cast<__int128_t>(sintMin) == sintMin);
+  assert(std::saturate_cast<__int128_t>(sintZero) == sintZero);
+  assert(std::saturate_cast<__int128_t>(sintMax) == sintMax);
 #endif
 
   // unsigned char: 0 to 255
 
-  assert(std::saturate_cast<unsigned char, BiggestUIntT>(0) == 0);
-  assert(std::saturate_cast<unsigned char, BiggestUIntT>(127) == 127);
-  assert(std::saturate_cast<unsigned char, BiggestUIntT>(255) == 127);
+  assert(std::saturate_cast<unsigned char>(0) == 0);
+  assert(std::saturate_cast<unsigned char>(127) == 127);
+  assert(std::saturate_cast<unsigned char>(uintMax) == 255);
 
   // short: 0 to 65,535
 
-  assert(std::saturate_cast<unsigned short int, BiggestUIntT>(0) == 0);
-  assert(std::saturate_cast<unsigned short int, BiggestUIntT>(65'535) == 65'535);
-  assert(std::saturate_cast<unsigned short int, BiggestUIntT>(255) == 65'535);
+  assert(std::saturate_cast<unsigned short int>(0) == 0);
+  assert(std::saturate_cast<unsigned short int>(65'535) == 65'535);
+  assert(std::saturate_cast<unsigned short int>(uintMax) == 65'535);
 
   // unsigned int: 0 to 4,294,967,295
 
-  assert(std::saturate_cast<unsigned int, BiggestUIntT>(0) == 0);
-  assert(std::saturate_cast<unsigned int, BiggestUIntT>(4'294'967'295) == 4'294'967'295);
-  assert(std::saturate_cast<unsigned int, BiggestUIntT>(255) == 4'294'967'295);
+  assert(std::saturate_cast<unsigned int>(0U) == 0U);
+  assert(std::saturate_cast<unsigned int>(4'294'967'295U) == 4'294'967'295U);
+  assert(std::saturate_cast<unsigned int>(uintMax) == 4'294'967'295U);
 
   // unsigned long: 0 to 4,294,967,295
 
-  assert(std::saturate_cast<unsigned long int, BiggestUIntT>(0) == 0);
-  assert(std::saturate_cast<unsigned long int, BiggestUIntT>(4'294'967'295) == 4'294'967'295);
-  assert(std::saturate_cast<unsigned long int, BiggestUIntT>(255) == 4'294'967'295);
+  assert(std::saturate_cast<unsigned long int>(0UL) == 0UL);
+  // assert(std::saturate_cast<unsigned long int>(4'294'967'295UL) == std_ulMax);
+  assert(std::saturate_cast<unsigned long int>(uintMax) == std_ulMax);
 
   // unsigned long long: 0 to 18,446,744,073,709,551,615
 
-  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(0) == 0);
-  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(18'446'744'073'709'551'615) == 118'446'744'073'709'551'615);
-  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(255) == 18'446'744'073'709'551'615);
+  assert(std::saturate_cast<unsigned long long int>(0ULL) == 0ULL);
+  assert(std::saturate_cast<unsigned long long int>(18'446'744'073'709'551'615ULL) == std_ullMax);
+  assert(std::saturate_cast<unsigned long long int>(uintMax) == std_ullMax);
 
 #ifndef TEST_HAS_NO_INT128
-  constexpr auto uint128min = std::numeric_limits<__uint128_t>::min();
-  constexpr auto uint128max = std::numeric_limits<__uint128_t>::max();
-
-  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(0) == 0);
-  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(18'446'744'073'709'551'615) == 18'446'744'073'709'551'615);
-  assert(std::saturate_cast<unsigned long long int, BiggestUIntT>(int128max) == 18'446'744'073'709'551'615);
-
-  assert(std::saturate_cast<__uint128_t, BiggestUIntT>(0) == 0);
-  assert(std::saturate_cast<__uint128_t, BiggestUIntT>(uint128max) == uint128max);
+  assert(std::saturate_cast<__uint128_t>(uintMin) == uintMin);
+  assert(std::saturate_cast<__uint128_t>(uintZero) == uintZero);
+  assert(std::saturate_cast<__uint128_t>(uintMax) == uintMax);
 #endif
-  // clang-format off
-}
 
-constexpr bool test() {
-  test_signed();
-  test_unsigned();
+  // clang-format on
 
   return true;
 }
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
-}
\ No newline at end of file
+}

>From fdff56900ef0ed99c2b8c37d3ab83b4968a89cf9 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 18:52:18 +0200
Subject: [PATCH 33/38] Updated test: `saturate_cast.pass`

---
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 157 ++++++++++++------
 1 file changed, 105 insertions(+), 52 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index a9fc5669d157a96..dfe5917b4f04551 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -14,20 +14,25 @@
 //   constexpr R saturate_cast(T x) noexcept;                     // freestanding
 
 #include <cassert>
+#include <concepts>
 #include <limits>
 #include <numeric>
 
+// Larger to smaller
 static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<signed int>::max())));
 static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<unsigned int>::max())));
 static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<signed int>::max())));
 static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<unsigned int>::max())));
-
-#include <print>
-
-// constexpr
-bool test() {
-  // clang-format off
-
+// Smaller to larger
+static_assert(noexcept(std::saturate_cast<signed int>(std::numeric_limits<signed char>::max())));
+static_assert(noexcept(std::saturate_cast<signed int>(std::numeric_limits<unsigned char>::max())));
+static_assert(noexcept(std::saturate_cast<unsigned int>(std::numeric_limits<signed char>::max())));
+static_assert(noexcept(std::saturate_cast<unsigned int>(std::numeric_limits<unsigned char>::max())));
+// Same type
+static_assert(noexcept(std::saturate_cast<signed long int>(std::numeric_limits<signed long int>::max())));
+static_assert(noexcept(std::saturate_cast<unsigned long int>(std::numeric_limits<unsigned long int>::max())));
+
+constexpr bool test() {
 #ifndef TEST_HAS_NO_INT128
   using SIntT = __int128_t;
   using UIntT = __uint128_t;
@@ -36,29 +41,33 @@ bool test() {
   using UIntT = unsigned long long int;
 #endif
 
-  // Biggest numbers
+  // Constants: biggest numbers
 
-  constexpr auto sintMin = std::numeric_limits<SIntT>::min();
-  constexpr auto sintMax = std::numeric_limits<SIntT>::max();
+  constexpr auto sintMin  = std::numeric_limits<SIntT>::min();
   constexpr auto sintZero = SIntT{0};
+  constexpr auto sintMax  = std::numeric_limits<SIntT>::max();
 
-  constexpr auto uintMin = std::numeric_limits<UIntT>::min();
-  constexpr auto uintMax = std::numeric_limits<UIntT>::max();
+  constexpr auto uintMin  = std::numeric_limits<UIntT>::min();
   constexpr auto uintZero = UIntT{0};
+  constexpr auto uintMax  = std::numeric_limits<UIntT>::max();
 
-  // Limits
+  // Constants: numeric limits
 
-  // constexpr auto std_scharMin = std::numeric_limits<signed char>::min();
-  // constexpr auto std_scharMax = std::numeric_limits<signed char>::max();
+  constexpr auto std_scharMin  = std::numeric_limits<signed char>::min();
+  constexpr auto std_scharZero = static_cast<signed char>(0);
+  constexpr auto std_scharMax  = std::numeric_limits<signed char>::max();
 
-  // constexpr auto std_ucharMin = std::numeric_limits<unsigned char>::min();
-  // constexpr auto std_ucharMax = std::numeric_limits<unsigned char>::max();
+  constexpr auto std_ucharMin  = std::numeric_limits<unsigned char>::min();
+  constexpr auto std_ucharZero = static_cast<unsigned char>(0);
+  constexpr auto std_ucharMax  = std::numeric_limits<unsigned char>::max();
 
-  // constexpr auto std_ssintMin = std::numeric_limits<signed short int>::min();
-  // constexpr auto std_ssintMax = std::numeric_limits<unsigned short int>::max();
+  constexpr auto std_ssintMin  = std::numeric_limits<signed short int>::min();
+  constexpr auto std_ssintZero = static_cast<signed short int>(0);
+  constexpr auto std_ssintMax  = std::numeric_limits<signed short int>::max();
 
-  // constexpr auto std_usintMin = std::numeric_limits<signed short int>::min();
-  // constexpr auto std_usintMax = std::numeric_limits<unsigned short int>::max();
+  constexpr auto std_usintMin  = std::numeric_limits<unsigned short int>::min();
+  constexpr auto std_usintZero = static_cast<unsigned short int>(0);
+  constexpr auto std_usintMax  = std::numeric_limits<unsigned short int>::max();
 
   // constexpr auto std_sintMin = std::numeric_limits<signed int>::min();
   // constexpr auto std_sintMax = std::numeric_limits<signed int>::max();
@@ -74,28 +83,63 @@ bool test() {
 
   constexpr auto std_sllMin = std::numeric_limits<signed long long int>::min();
   constexpr auto std_sllMax = std::numeric_limits<signed long long int>::max();
-  
+
   // constexpr auto std_ullMin = std::numeric_limits<unsigned long long int>::min();
   constexpr auto std_ullMax = std::numeric_limits<unsigned long long int>::max();
 
-  // signed char: -128 to 127
+  // clang-format off
+  
+  // signed char
+
+  assert(std::saturate_cast<signed char>(std_scharMin)  == std_scharMin);
+  assert(std::saturate_cast<signed char>(std_scharZero) == std_scharZero);
+  assert(std::saturate_cast<signed char>(std_scharMax)  == std_scharMax);
 
-  assert(std::saturate_cast<signed char>(sintMin) == -128);
-  assert(std::saturate_cast<signed char>(-128) == -128);
-  assert(std::saturate_cast<signed char>(0) == 0);
-  assert(std::saturate_cast<signed char>(127) == 127);
-  assert(std::saturate_cast<signed char>(sintMax) == 127);
+  assert(std::saturate_cast<signed char>(std_ucharMin)  == std_scharZero);
+  assert(std::saturate_cast<signed char>(std_ucharZero) == std_scharZero);
+  assert(std::saturate_cast<signed char>(std_ucharMax)  == std_scharMax);
 
-  // short: -32,768 to 32,767
+  assert(std::saturate_cast<signed char>(sintMin)       == std_scharMin);  // saturated
+  assert(std::saturate_cast<signed char>(sintZero)      == std_scharZero);
+  assert(std::saturate_cast<signed char>(sintMax)       == std_scharMax);  // saturated
 
-  // std::println(stderr, "results: {}", std::saturate_cast<short int>(sintMin));
-  assert(std::saturate_cast<short int>(sintMin) == -32'768);
-  assert(std::saturate_cast<short int>(-32'768) == -32'768);
-  assert(std::saturate_cast<short int>(0) == 0);
-  assert(std::saturate_cast<short int>(32'767) == 32'767);
-  assert(std::saturate_cast<short int>(sintMax) == 32'767);
+  assert(std::saturate_cast<signed char>(uintMin)       == std_scharZero);
+  assert(std::saturate_cast<signed char>(uintZero)      == std_scharZero);
+  assert(std::saturate_cast<signed char>(uintMax)       == std_scharMax);  // saturated
 
-  // int: -2,147,483,648 to 2,147,483,647
+  // short
+
+  std::same_as<signed short int> decltype(auto) _ = std::saturate_cast<signed short int>(std_scharMin);
+  assert(std::saturate_cast<signed short int>(std_scharMin)  == static_cast<signed short int>(std_scharMin));
+  assert(std::saturate_cast<signed short int>(std_scharZero) == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(std_scharMax)  == static_cast<signed short int>(std_scharMax));
+
+  std::same_as<signed short int> decltype(auto) _ = std::saturate_cast<signed short int>(std_ucharMin);
+  assert(std::saturate_cast<signed short int>(std_ucharMin)  == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(std_ucharZero) == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(std_ucharMax)  == static_cast<signed short int>(std_ucharMax));
+
+  std::same_as<signed short int> decltype(auto) _ = std::saturate_cast<signed short int>(std_ssintMin);
+  assert(std::saturate_cast<signed short int>(std_ssintMin)  == std_ssintMin);
+  assert(std::saturate_cast<signed short int>(std_ssintZero) == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(std_ssintMax)  == std_ssintMax);
+
+  std::same_as<signed short int> decltype(auto) _ = std::saturate_cast<signed short int>(std_usintMin);
+  assert(std::saturate_cast<signed short int>(std_usintMin)  == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(std_usintZero) == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(std_usintMax)  == std_ssintMax);  // saturated
+
+  std::same_as<signed short int> decltype(auto) _ = std::saturate_cast<signed short int>(sintMin);
+  assert(std::saturate_cast<signed short int>(sintMin)       == std_ssintMin);  // saturated
+  assert(std::saturate_cast<signed short int>(sintZero)      == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(sintMax)       == std_ssintMax);  // saturated
+
+  std::same_as<signed short int> decltype(auto) _ = std::saturate_cast<signed short int>(uintMin);
+  assert(std::saturate_cast<signed short int>(uintMin)       == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(uintZero)      == std_ssintZero);
+  assert(std::saturate_cast<signed short int>(uintMax)       == std_ssintMax);  // saturated
+
+  // int
 
   assert(std::saturate_cast<int>(sintMin) == -2'147'483'648);
   assert(std::saturate_cast<int>(-2'147'483'648) == -2'147'483'648);
@@ -103,7 +147,7 @@ bool test() {
   assert(std::saturate_cast<int>(2'147'483'647) == 2'147'483'647);
   assert(std::saturate_cast<int>(sintMax) == 2'147'483'647);
 
-  // long: -2,147,483,648 to 2,147,483,647
+  // long
 
   assert(std::saturate_cast<long int>(sintMin) == std_slMin);
   // assert(std::saturate_cast<long int>(-2'147'483'648L) == std_slMin);
@@ -111,55 +155,64 @@ bool test() {
   // assert(std::saturate_cast<long int>(2'147'483'647L) == std_slMax);
   assert(std::saturate_cast<long int>(sintMax) == std_slMax);
 
-  // long long: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
+  // long long
 
   assert(std::saturate_cast<long long int>(sintMin) == std_sllMin);
-  assert(std::saturate_cast<long long int>(sintMin) == -std_sllMin);
+  assert(std::saturate_cast<long long int>(sintMin) == std_sllMin);
   // assert(std::saturate_cast<long long int>(-9'223'372'036'854'775'808LL) == std_sllMin);
   assert(std::saturate_cast<long long int>(0LL) == 0LL);
   assert(std::saturate_cast<long long int>(9'223'372'036'854'775'807LL) == std_sllMax);
   assert(std::saturate_cast<long long int>(sintMax) == std_sllMax);
 
 #ifndef TEST_HAS_NO_INT128
-  assert(std::saturate_cast<__int128_t>(sintMin) == sintMin);
+  assert(std::saturate_cast<__int128_t>(sintMin)  == sintMin);
   assert(std::saturate_cast<__int128_t>(sintZero) == sintZero);
-  assert(std::saturate_cast<__int128_t>(sintMax) == sintMax);
+  assert(std::saturate_cast<__int128_t>(sintMax)  == sintMax);
 #endif
 
-  // unsigned char: 0 to 255
+  // unsigned char
 
-  assert(std::saturate_cast<unsigned char>(0) == 0);
-  assert(std::saturate_cast<unsigned char>(127) == 127);
-  assert(std::saturate_cast<unsigned char>(uintMax) == 255);
+  assert(std::saturate_cast<unsigned char>(std_scharMin)  == std_ucharMin);
+  assert(std::saturate_cast<unsigned char>(std_scharZero) == std_ucharZero);
+  assert(std::saturate_cast<unsigned char>(std_scharMax)  == static_cast<unsigned char>(std_scharMax));
+  assert(std::saturate_cast<unsigned char>(std_ucharMin)  == std_ucharMin);
+  assert(std::saturate_cast<unsigned char>(std_ucharZero) == std_ucharZero);
+  assert(std::saturate_cast<unsigned char>(std_ucharMax)  == std_ucharMax);
+  assert(std::saturate_cast<unsigned char>(sintMin)       == std_ucharMin);  // saturated
+  assert(std::saturate_cast<unsigned char>(sintZero)      == std_ucharZero);
+  assert(std::saturate_cast<unsigned char>(sintMax)       == std_ucharMax);  // saturated
+  assert(std::saturate_cast<unsigned char>(uintMin)       == std_ucharMin);
+  assert(std::saturate_cast<unsigned char>(uintZero)      == std_ucharZero);
+  assert(std::saturate_cast<unsigned char>(uintMax)       == std_ucharMax);  // saturated
 
-  // short: 0 to 65,535
+  // short
 
   assert(std::saturate_cast<unsigned short int>(0) == 0);
   assert(std::saturate_cast<unsigned short int>(65'535) == 65'535);
   assert(std::saturate_cast<unsigned short int>(uintMax) == 65'535);
 
-  // unsigned int: 0 to 4,294,967,295
+  // unsigned int
 
   assert(std::saturate_cast<unsigned int>(0U) == 0U);
   assert(std::saturate_cast<unsigned int>(4'294'967'295U) == 4'294'967'295U);
   assert(std::saturate_cast<unsigned int>(uintMax) == 4'294'967'295U);
 
-  // unsigned long: 0 to 4,294,967,295
+  // unsigned long
 
   assert(std::saturate_cast<unsigned long int>(0UL) == 0UL);
   // assert(std::saturate_cast<unsigned long int>(4'294'967'295UL) == std_ulMax);
   assert(std::saturate_cast<unsigned long int>(uintMax) == std_ulMax);
 
-  // unsigned long long: 0 to 18,446,744,073,709,551,615
+  // unsigned long long
 
   assert(std::saturate_cast<unsigned long long int>(0ULL) == 0ULL);
   assert(std::saturate_cast<unsigned long long int>(18'446'744'073'709'551'615ULL) == std_ullMax);
   assert(std::saturate_cast<unsigned long long int>(uintMax) == std_ullMax);
 
 #ifndef TEST_HAS_NO_INT128
-  assert(std::saturate_cast<__uint128_t>(uintMin) == uintMin);
+  assert(std::saturate_cast<__uint128_t>(uintMin)  == uintMin);
   assert(std::saturate_cast<__uint128_t>(uintZero) == uintZero);
-  assert(std::saturate_cast<__uint128_t>(uintMax) == uintMax);
+  assert(std::saturate_cast<__uint128_t>(uintMax)  == uintMax);
 #endif
 
   // clang-format on
@@ -169,7 +222,7 @@ bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From 38f41574b55318bd3bc67c7a6da790acdcd171be Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 20:23:47 +0200
Subject: [PATCH 34/38] Updated test `saturation_cast.pass`

---
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 330 +++++++++++++++---
 1 file changed, 273 insertions(+), 57 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index dfe5917b4f04551..b9664b3d67a8bfa 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -18,11 +18,6 @@
 #include <limits>
 #include <numeric>
 
-// Larger to smaller
-static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<signed int>::max())));
-static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<unsigned int>::max())));
-static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<signed int>::max())));
-static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<unsigned int>::max())));
 // Smaller to larger
 static_assert(noexcept(std::saturate_cast<signed int>(std::numeric_limits<signed char>::max())));
 static_assert(noexcept(std::saturate_cast<signed int>(std::numeric_limits<unsigned char>::max())));
@@ -31,8 +26,15 @@ static_assert(noexcept(std::saturate_cast<unsigned int>(std::numeric_limits<unsi
 // Same type
 static_assert(noexcept(std::saturate_cast<signed long int>(std::numeric_limits<signed long int>::max())));
 static_assert(noexcept(std::saturate_cast<unsigned long int>(std::numeric_limits<unsigned long int>::max())));
+// Larger to smaller
+static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<signed int>::max())));
+static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<unsigned int>::max())));
+static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<signed int>::max())));
+static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<unsigned int>::max())));
 
 constexpr bool test() {
+  // clang-format off
+
 #ifndef TEST_HAS_NO_INT128
   using SIntT = __int128_t;
   using UIntT = __uint128_t;
@@ -54,40 +56,44 @@ constexpr bool test() {
   // Constants: numeric limits
 
   constexpr auto std_scharMin  = std::numeric_limits<signed char>::min();
-  constexpr auto std_scharZero = static_cast<signed char>(0);
+  constexpr auto std_scharZero =         static_cast<signed char>(0);
   constexpr auto std_scharMax  = std::numeric_limits<signed char>::max();
 
   constexpr auto std_ucharMin  = std::numeric_limits<unsigned char>::min();
-  constexpr auto std_ucharZero = static_cast<unsigned char>(0);
+  constexpr auto std_ucharZero =         static_cast<unsigned char>(0);
   constexpr auto std_ucharMax  = std::numeric_limits<unsigned char>::max();
 
   constexpr auto std_ssintMin  = std::numeric_limits<signed short int>::min();
-  constexpr auto std_ssintZero = static_cast<signed short int>(0);
+  constexpr auto std_ssintZero =  static_cast<signed short int>(0);
   constexpr auto std_ssintMax  = std::numeric_limits<signed short int>::max();
 
   constexpr auto std_usintMin  = std::numeric_limits<unsigned short int>::min();
-  constexpr auto std_usintZero = static_cast<unsigned short int>(0);
+  constexpr auto std_usintZero =         static_cast<unsigned short int>(0);
   constexpr auto std_usintMax  = std::numeric_limits<unsigned short int>::max();
 
-  // constexpr auto std_sintMin = std::numeric_limits<signed int>::min();
-  // constexpr auto std_sintMax = std::numeric_limits<signed int>::max();
-
-  // constexpr auto std_uintMin = std::numeric_limits<unsigned int>::min();
-  // constexpr auto std_uintMax = std::numeric_limits<unsigned int>::max();
+  constexpr auto std_sintMin  = std::numeric_limits<signed int>::min();
+  constexpr auto std_sintZero =         static_cast<signed int>(0);
+  constexpr auto std_sintMax  = std::numeric_limits<signed int>::max();
 
-  constexpr auto std_slMin = std::numeric_limits<signed long int>::min();
-  constexpr auto std_slMax = std::numeric_limits<signed long int>::max();
+  constexpr auto std_uintMin  = std::numeric_limits<unsigned int>::min();
+  constexpr auto std_uintZero =         static_cast<unsigned int>(0);
+  constexpr auto std_uintMax  = std::numeric_limits<unsigned int>::max();
 
-  // constexpr auto std_ulMin = std::numeric_limits<unsigned long int>::min();
-  constexpr auto std_ulMax = std::numeric_limits<unsigned long int>::max();
+  constexpr auto std_slMin    = std::numeric_limits<signed long int>::min();
+  constexpr auto std_slZero   =         static_cast<signed long int>(0);
+  constexpr auto std_slMax    = std::numeric_limits<signed long int>::max();
 
-  constexpr auto std_sllMin = std::numeric_limits<signed long long int>::min();
-  constexpr auto std_sllMax = std::numeric_limits<signed long long int>::max();
+  constexpr auto std_ulMin    = std::numeric_limits<unsigned long int>::min();
+  constexpr auto std_ulZero   =         static_cast<unsigned long int>(0);
+  constexpr auto std_ulMax    = std::numeric_limits<unsigned long int>::max();
 
-  // constexpr auto std_ullMin = std::numeric_limits<unsigned long long int>::min();
-  constexpr auto std_ullMax = std::numeric_limits<unsigned long long int>::max();
+  constexpr auto std_sllMin   = std::numeric_limits<signed long long int>::min();
+  constexpr auto std_sllZero  =         static_cast<signed long long int>(0);
+  constexpr auto std_sllMax   = std::numeric_limits<signed long long int>::max();
 
-  // clang-format off
+  constexpr auto std_ullMin   = std::numeric_limits<unsigned long long int>::min();
+  constexpr auto std_ullZero  =         static_cast<unsigned long long int>(0);
+  constexpr auto std_ullMax   = std::numeric_limits<unsigned long long int>::max();
   
   // signed char
 
@@ -141,78 +147,288 @@ constexpr bool test() {
 
   // int
 
-  assert(std::saturate_cast<int>(sintMin) == -2'147'483'648);
-  assert(std::saturate_cast<int>(-2'147'483'648) == -2'147'483'648);
-  assert(std::saturate_cast<int>(0) == 0);
-  assert(std::saturate_cast<int>(2'147'483'647) == 2'147'483'647);
-  assert(std::saturate_cast<int>(sintMax) == 2'147'483'647);
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_scharMin);
+  assert(std::saturate_cast<signed int>(std_scharMin)  == static_cast<signed int>(std_scharMin));
+  assert(std::saturate_cast<signed int>(std_scharZero) == std_ssintZero);
+  assert(std::saturate_cast<signed int>(std_scharMax)  == static_cast<signed int>(std_scharMax));
+
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_ucharMin);
+  assert(std::saturate_cast<signed int>(std_ucharMin)  == std_ssintZero);
+  assert(std::saturate_cast<signed int>(std_ucharZero) == std_ssintZero);
+  assert(std::saturate_cast<signed int>(std_ucharMax)  == static_cast<signed int>(std_ucharMax));
+
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_ssintMin);
+  assert(std::saturate_cast<signed int>(std_sintMin)   == std_sintMin);
+  assert(std::saturate_cast<signed int>(std_sintZero)  == std_sintZero);
+  assert(std::saturate_cast<signed int>(std_sintMax)   == std_sintMax);
+
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_usintMin);
+  assert(std::saturate_cast<signed int>(std_uintMin)   == std_sintZero);
+  assert(std::saturate_cast<signed int>(std_uintZero)  == std_sintZero);
+  assert(std::saturate_cast<signed int>(std_uintMax)   == std_sintMax);  // saturated
+
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(sintMin);
+  assert(std::saturate_cast<signed int>(sintMin)       == std_sintMin);  // saturated
+  assert(std::saturate_cast<signed int>(sintZero)      == std_sintZero);
+  assert(std::saturate_cast<signed int>(sintMax)       == std_sintMax);  // saturated
+
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(uintMin);
+  assert(std::saturate_cast<signed int>(uintMin)       == std_sintZero);
+  assert(std::saturate_cast<signed int>(uintZero)      == std_sintZero);
+  assert(std::saturate_cast<signed int>(uintMax)       == std_sintMax);  // saturated
 
   // long
 
-  assert(std::saturate_cast<long int>(sintMin) == std_slMin);
-  // assert(std::saturate_cast<long int>(-2'147'483'648L) == std_slMin);
-  assert(std::saturate_cast<long int>(0L) == 0L);
-  // assert(std::saturate_cast<long int>(2'147'483'647L) == std_slMax);
-  assert(std::saturate_cast<long int>(sintMax) == std_slMax);
+  std::same_as<signed long int> decltype(auto) _ = std::saturate_cast<signed long int>(std_scharMin);
+  assert(std::saturate_cast<signed long int>(std_scharMin)  == static_cast<signed long int>(std_scharMin));
+  assert(std::saturate_cast<signed long int>(std_scharZero) == std_slZero);
+  assert(std::saturate_cast<signed long int>(std_scharMax)  == static_cast<signed long int>(std_scharMax));
+
+  std::same_as<signed long int> decltype(auto) _ = std::saturate_cast<signed long int>(std_ucharMin);
+  assert(std::saturate_cast<signed long int>(std_ucharMin)  == std_slZero);
+  assert(std::saturate_cast<signed long int>(std_ucharZero) == std_slZero);
+  assert(std::saturate_cast<signed long int>(std_ucharMax)  == static_cast<signed long int>(std_ucharMax));
+
+  std::same_as<signed long int> decltype(auto) _ = std::saturate_cast<signed long int>(std_slMin);
+  assert(std::saturate_cast<signed long int>(std_slMin)     == std_slMin);
+  assert(std::saturate_cast<signed long int>(std_slZero)    == std_slZero);
+  assert(std::saturate_cast<signed long int>(std_slMax)     == std_slMax);
+
+  std::same_as<signed long int> decltype(auto) _ = std::saturate_cast<signed long int>(std_ulMin);
+  assert(std::saturate_cast<signed long int>(std_ulMin)     == std_slZero);
+  assert(std::saturate_cast<signed long int>(std_ulZero)    == std_slZero);
+  assert(std::saturate_cast<signed long int>(std_ulMax)     == std_slMax);  // saturated
+
+  std::same_as<signed long int> decltype(auto) _ = std::saturate_cast<signed long int>(sintMin);
+  assert(std::saturate_cast<signed long int>(sintMin)       == std_slMin);  // saturated
+  assert(std::saturate_cast<signed long int>(sintZero)      == std_slZero);
+  assert(std::saturate_cast<signed long int>(sintMax)       == std_slMax);  // saturated
+
+  std::same_as<signed long int> decltype(auto) _ = std::saturate_cast<signed long int>(uintMin);
+  assert(std::saturate_cast<signed long int>(uintMin)       == std_slZero);
+  assert(std::saturate_cast<signed long int>(uintZero)      == std_slZero);
+  assert(std::saturate_cast<signed long int>(uintMax)       == std_slMax);  // saturated
 
   // long long
 
-  assert(std::saturate_cast<long long int>(sintMin) == std_sllMin);
-  assert(std::saturate_cast<long long int>(sintMin) == std_sllMin);
-  // assert(std::saturate_cast<long long int>(-9'223'372'036'854'775'808LL) == std_sllMin);
-  assert(std::saturate_cast<long long int>(0LL) == 0LL);
-  assert(std::saturate_cast<long long int>(9'223'372'036'854'775'807LL) == std_sllMax);
-  assert(std::saturate_cast<long long int>(sintMax) == std_sllMax);
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_scharMin);
+  assert(std::saturate_cast<signed long long int>(std_scharMin)  == static_cast<signed long long int>(std_scharMin));
+  assert(std::saturate_cast<signed long long int>(std_scharZero) == std_slZero);
+  assert(std::saturate_cast<signed long long int>(std_scharMax)  == static_cast<signed long long int>(std_scharMax));
+
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_ucharMin);
+  assert(std::saturate_cast<signed long long int>(std_ucharMin)  == std_slZero);
+  assert(std::saturate_cast<signed long long int>(std_ucharZero) == std_slZero);
+  assert(std::saturate_cast<signed long long int>(std_ucharMax)  == static_cast<signed long long int>(std_ucharMax));
+
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_slMin);
+  assert(std::saturate_cast<signed long long int>(std_sllMin)     == std_sllMin);
+  assert(std::saturate_cast<signed long long int>(std_sllZero)    == std_sllZero);
+  assert(std::saturate_cast<signed long long int>(std_sllMax)     == std_sllMax);
+
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_ulMin);
+  assert(std::saturate_cast<signed long long int>(std_ullMin)     == std_sllZero);
+  assert(std::saturate_cast<signed long long int>(std_ullZero)    == std_sllZero);
+  assert(std::saturate_cast<signed long long int>(std_ullMax)     == std_sllMax);  // saturated
+
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(sintMin);
+  assert(std::saturate_cast<signed long long int>(sintMin)        == std_sllMin);  // (128-bit) saturated
+  assert(std::saturate_cast<signed long long int>(sintZero)       == std_sllZero);
+  assert(std::saturate_cast<signed long long int>(sintMax)        == std_sllMax);  // (128-bit) saturated
+
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(uintMin);
+  assert(std::saturate_cast<signed long long int>(uintMin)        == std_sllZero);
+  assert(std::saturate_cast<signed long long int>(uintZero)       == std_sllZero);
+  assert(std::saturate_cast<signed long long int>(uintMax)        == std_sllMax);  // (128-bit) saturated
 
 #ifndef TEST_HAS_NO_INT128
-  assert(std::saturate_cast<__int128_t>(sintMin)  == sintMin);
-  assert(std::saturate_cast<__int128_t>(sintZero) == sintZero);
-  assert(std::saturate_cast<__int128_t>(sintMax)  == sintMax);
+  std::same_as<__int128_t> decltype(auto) _ = std::saturate_cast<__int128_t>(std_scharMin);
+  assert(std::saturate_cast<__int128_t>(std_scharMin)  == static_cast<__int128_t>(std_scharMin));
+  assert(std::saturate_cast<__int128_t>(std_scharZero) == sintZero);
+  assert(std::saturate_cast<__int128_t>(std_scharMax)  == static_cast<__int128_t>(std_scharMax));
+
+  std::same_as<__int128_t> decltype(auto) _ = std::saturate_cast<__int128_t>(std_ucharMin);
+  assert(std::saturate_cast<__int128_t>(std_ucharMin)  == static_cast<__int128_t>(std_ucharMin));
+  assert(std::saturate_cast<__int128_t>(std_ucharZero) == sintZero);
+  assert(std::saturate_cast<__int128_t>(std_ucharMax)  == static_cast<__int128_t>(std_ucharMax));
+
+  assert(std::saturate_cast<__int128_t>(sintMin)       == sintMin);
+  assert(std::saturate_cast<__int128_t>(sintZero)      == sintZero);
+  assert(std::saturate_cast<__int128_t>(sintMax)       == sintMax);
+
+  assert(std::saturate_cast<__int128_t>(uintMin)       == sintZero);
+  assert(std::saturate_cast<__int128_t>(uintZero)      == sintZero);
+  assert(std::saturate_cast<__int128_t>(uintMax)       == sintMax);  // saturated
 #endif
 
   // unsigned char
 
+  std::same_as<unsigned char> decltype(auto) _ = std::saturate_cast<unsigned char>(std_scharMin);
   assert(std::saturate_cast<unsigned char>(std_scharMin)  == std_ucharMin);
   assert(std::saturate_cast<unsigned char>(std_scharZero) == std_ucharZero);
   assert(std::saturate_cast<unsigned char>(std_scharMax)  == static_cast<unsigned char>(std_scharMax));
+
+  std::same_as<unsigned char> decltype(auto) _ = std::saturate_cast<unsigned char>(std_ucharMin);
   assert(std::saturate_cast<unsigned char>(std_ucharMin)  == std_ucharMin);
   assert(std::saturate_cast<unsigned char>(std_ucharZero) == std_ucharZero);
   assert(std::saturate_cast<unsigned char>(std_ucharMax)  == std_ucharMax);
+
+  std::same_as<unsigned char> decltype(auto) _ = std::saturate_cast<unsigned char>(sintMin);
   assert(std::saturate_cast<unsigned char>(sintMin)       == std_ucharMin);  // saturated
   assert(std::saturate_cast<unsigned char>(sintZero)      == std_ucharZero);
   assert(std::saturate_cast<unsigned char>(sintMax)       == std_ucharMax);  // saturated
+
+  std::same_as<unsigned char> decltype(auto) _ = std::saturate_cast<unsigned char>(uintMin);
   assert(std::saturate_cast<unsigned char>(uintMin)       == std_ucharMin);
   assert(std::saturate_cast<unsigned char>(uintZero)      == std_ucharZero);
   assert(std::saturate_cast<unsigned char>(uintMax)       == std_ucharMax);  // saturated
 
   // short
 
-  assert(std::saturate_cast<unsigned short int>(0) == 0);
-  assert(std::saturate_cast<unsigned short int>(65'535) == 65'535);
-  assert(std::saturate_cast<unsigned short int>(uintMax) == 65'535);
+  std::same_as<unsigned short int> decltype(auto) _ = std::saturate_cast<unsigned short int>(std_scharMin);
+  assert(std::saturate_cast<unsigned short int>(std_scharMin)  == std_usintMin);
+  assert(std::saturate_cast<unsigned short int>(std_scharZero) == std_usintZero);
+  assert(std::saturate_cast<unsigned short int>(std_scharMax)  == static_cast<unsigned short int>(std_scharMax));
+
+  std::same_as<unsigned short int> decltype(auto) _ = std::saturate_cast<unsigned short int>(std_ucharMin);
+  assert(std::saturate_cast<unsigned short int>(std_ucharMin)  == std_usintMin);
+  assert(std::saturate_cast<unsigned short int>(std_ucharZero) == std_usintZero);
+  assert(std::saturate_cast<unsigned short int>(std_ucharMax)  == static_cast<unsigned short int>(std_ucharMax));
+
+  std::same_as<unsigned short int> decltype(auto) _ = std::saturate_cast<unsigned short int>(std_scharMin);
+  assert(std::saturate_cast<unsigned short int>(std_ssintMin)  == std_usintMin);
+  assert(std::saturate_cast<unsigned short int>(std_ssintZero) == std_usintZero);
+  assert(std::saturate_cast<unsigned short int>(std_ssintMax)  == static_cast<unsigned short int>(std_ssintMax));
+
+  std::same_as<unsigned short int> decltype(auto) _ = std::saturate_cast<unsigned short int>(std_ucharMin);
+  assert(std::saturate_cast<unsigned short int>(std_usintMin)  == std_usintMin);
+  assert(std::saturate_cast<unsigned short int>(std_usintZero) == std_usintZero);
+  assert(std::saturate_cast<unsigned short int>(std_usintMax)  == std_usintMax);
+
+  std::same_as<unsigned short int> decltype(auto) _ = std::saturate_cast<unsigned short int>(sintMin);
+  assert(std::saturate_cast<unsigned short int>(sintMin)       == std_usintMin);  // saturated
+  assert(std::saturate_cast<unsigned short int>(sintZero)      == std_usintZero);
+  assert(std::saturate_cast<unsigned short int>(sintMax)       == std_usintMax);  // saturated
+
+  std::same_as<unsigned short int> decltype(auto) _ = std::saturate_cast<unsigned short int>(uintMin);
+  assert(std::saturate_cast<unsigned short int>(uintMin)       == std_usintMin);
+  assert(std::saturate_cast<unsigned short int>(uintZero)      == std_usintZero);
+  assert(std::saturate_cast<unsigned short int>(uintMax)       == std_usintMax);  // saturated
 
   // unsigned int
 
-  assert(std::saturate_cast<unsigned int>(0U) == 0U);
-  assert(std::saturate_cast<unsigned int>(4'294'967'295U) == 4'294'967'295U);
-  assert(std::saturate_cast<unsigned int>(uintMax) == 4'294'967'295U);
+  std::same_as<unsigned int> decltype(auto) _ = std::saturate_cast<unsigned int>(std_scharMin);
+  assert(std::saturate_cast<unsigned int>(std_scharMin)  == std_usintMin);
+  assert(std::saturate_cast<unsigned int>(std_scharZero) == std_usintZero);
+  assert(std::saturate_cast<unsigned int>(std_scharMax)  == static_cast<unsigned int>(std_scharMax));
+
+  std::same_as<unsigned int> decltype(auto) _ = std::saturate_cast<unsigned int>(std_ucharMin);
+  assert(std::saturate_cast<unsigned int>(std_ucharMin)  == std_uintMin);
+  assert(std::saturate_cast<unsigned int>(std_ucharZero) == std_uintZero);
+  assert(std::saturate_cast<unsigned int>(std_ucharMax)  == static_cast<unsigned int>(std_ucharMax));
+
+  std::same_as<unsigned int> decltype(auto) _ = std::saturate_cast<unsigned int>(std_sintMin);
+  assert(std::saturate_cast<unsigned int>(std_sintMin)   == std_uintMin);
+  assert(std::saturate_cast<unsigned int>(std_sintZero)  == std_uintZero);
+  assert(std::saturate_cast<unsigned int>(std_sintMax)   == static_cast<unsigned int>(std_sintMax));
+
+  std::same_as<unsigned int> decltype(auto) _ = std::saturate_cast<unsigned int>(std_uintMin);
+  assert(std::saturate_cast<unsigned int>(std_uintMin)   == std_uintMin);
+  assert(std::saturate_cast<unsigned int>(std_uintZero)  == std_uintZero);
+  assert(std::saturate_cast<unsigned int>(std_uintMax)   == std_uintMax);
+
+  std::same_as<unsigned int> decltype(auto) _ = std::saturate_cast<unsigned int>(sintMin);
+  assert(std::saturate_cast<unsigned int>(sintMin)       == std_uintMin);  // saturated
+  assert(std::saturate_cast<unsigned int>(sintZero)      == std_uintZero);
+  assert(std::saturate_cast<unsigned int>(sintMax)       == std_uintMax);  // saturated
+
+  std::same_as<unsigned int> decltype(auto) _ = std::saturate_cast<unsigned int>(uintMin);
+  assert(std::saturate_cast<unsigned int>(uintMin)       == std_uintMin);
+  assert(std::saturate_cast<unsigned int>(uintZero)      == std_uintZero);
+  assert(std::saturate_cast<unsigned int>(uintMax)       == std_uintMax);  // saturated
 
   // unsigned long
 
-  assert(std::saturate_cast<unsigned long int>(0UL) == 0UL);
-  // assert(std::saturate_cast<unsigned long int>(4'294'967'295UL) == std_ulMax);
-  assert(std::saturate_cast<unsigned long int>(uintMax) == std_ulMax);
+  std::same_as<unsigned long int> decltype(auto) _ = std::saturate_cast<unsigned long int>(std_scharMin);
+  assert(std::saturate_cast<unsigned long int>(std_scharMin)  == std_ulMin);
+  assert(std::saturate_cast<unsigned long int>(std_scharZero) == std_ulZero);
+  assert(std::saturate_cast<unsigned long int>(std_scharMax)  == static_cast<unsigned long int>(std_scharMax));
+
+  std::same_as<unsigned long int> decltype(auto) _ = std::saturate_cast<unsigned long int>(std_ucharMin);
+  assert(std::saturate_cast<unsigned long int>(std_ucharMin)  == std_ulMin);
+  assert(std::saturate_cast<unsigned long int>(std_ucharZero) == std_ulZero);
+  assert(std::saturate_cast<unsigned long int>(std_ucharMax)  == static_cast<unsigned long int>(std_ucharMax));
+
+  std::same_as<unsigned long int> decltype(auto) _ = std::saturate_cast<unsigned long int>(std_slMin);
+  assert(std::saturate_cast<unsigned long int>(std_slMin)     == std_ulMin);
+  assert(std::saturate_cast<unsigned long int>(std_slZero)    == std_ulZero);
+  assert(std::saturate_cast<unsigned long int>(std_slMax)     == static_cast<unsigned long int>(std_slMax));
+
+  std::same_as<unsigned long int> decltype(auto) _ = std::saturate_cast<unsigned long int>(std_ulMin);
+  assert(std::saturate_cast<unsigned long int>(std_ulMin)     == std_ulMin);
+  assert(std::saturate_cast<unsigned long int>(std_ulZero)    == std_ulZero);
+  assert(std::saturate_cast<unsigned long int>(std_ulMax)     == std_ulMax);
+
+  std::same_as<unsigned long int> decltype(auto) _ = std::saturate_cast<unsigned long int>(sintMin);
+  assert(std::saturate_cast<unsigned long int>(sintMin)       == std_ulMin);  // saturated
+  assert(std::saturate_cast<unsigned long int>(sintZero)      == std_ulZero);
+  assert(std::saturate_cast<unsigned long int>(sintMax)       == std_ulMax);  // saturated
+
+  std::same_as<unsigned long int> decltype(auto) _ = std::saturate_cast<unsigned long int>(uintMin);
+  assert(std::saturate_cast<unsigned long int>(uintMin)       == std_ulMin);
+  assert(std::saturate_cast<unsigned long int>(uintZero)      == std_ulZero);
+  assert(std::saturate_cast<unsigned long int>(uintMax)       == std_ulMax);  // saturated
 
   // unsigned long long
 
-  assert(std::saturate_cast<unsigned long long int>(0ULL) == 0ULL);
-  assert(std::saturate_cast<unsigned long long int>(18'446'744'073'709'551'615ULL) == std_ullMax);
-  assert(std::saturate_cast<unsigned long long int>(uintMax) == std_ullMax);
+  std::same_as<unsigned long long int> decltype(auto) _ = std::saturate_cast<unsigned long long int>(std_scharMin);
+  assert(std::saturate_cast<unsigned long long int>(std_scharMin)  == std_ullMin);
+  assert(std::saturate_cast<unsigned long long int>(std_scharZero) == std_ullZero);
+  assert(std::saturate_cast<unsigned long long int>(std_scharMax)  == static_cast<unsigned long long int>(std_scharMax));
+
+  std::same_as<unsigned long long int> decltype(auto) _ = std::saturate_cast<unsigned long long int>(std_ucharMin);
+  assert(std::saturate_cast<unsigned long long int>(std_ucharMin)  == std_ullMin);
+  assert(std::saturate_cast<unsigned long long int>(std_ucharZero) == std_ullZero);
+  assert(std::saturate_cast<unsigned long long int>(std_ucharMax)  == static_cast<unsigned long long int>(std_ucharMax));
+
+  std::same_as<unsigned long long int> decltype(auto) _ = std::saturate_cast<unsigned long long int>(std_sllMin);
+  assert(std::saturate_cast<unsigned long long int>(std_sllMin)    == std_ullMin);
+  assert(std::saturate_cast<unsigned long long int>(std_sllZero)   == std_ullZero);
+  assert(std::saturate_cast<unsigned long long int>(std_sllMax)    == static_cast<unsigned long long int>(std_sllMax));
+
+  std::same_as<unsigned long long int> decltype(auto) _ = std::saturate_cast<unsigned long long int>(std_ullMin);
+  assert(std::saturate_cast<unsigned long long int>(std_ullMin)    == std_ullMin);
+  assert(std::saturate_cast<unsigned long long int>(std_ullZero)   == std_ullZero);
+  assert(std::saturate_cast<unsigned long long int>(std_ullMax)    == std_ullMax);
+
+  std::same_as<unsigned long long int> decltype(auto) _ = std::saturate_cast<unsigned long long int>(sintMin);
+  assert(std::saturate_cast<unsigned long long int>(sintMin)       == std_ullMin);  // (128-bit) saturated
+  assert(std::saturate_cast<unsigned long long int>(sintZero)      == std_ullZero);
+  assert(std::saturate_cast<unsigned long long int>(sintMax)       == std_ullMax);  // (128-bit) saturated
+
+  std::same_as<unsigned long long int> decltype(auto) _ = std::saturate_cast<unsigned long long int>(uintMin);
+  assert(std::saturate_cast<unsigned long long int>(uintMin)       == std_ullMin);
+  assert(std::saturate_cast<unsigned long long int>(uintZero)      == std_ullZero);
+  assert(std::saturate_cast<unsigned long long int>(uintMax)       == std_ullMax);  // (128-bit) saturated
 
 #ifndef TEST_HAS_NO_INT128
-  assert(std::saturate_cast<__uint128_t>(uintMin)  == uintMin);
-  assert(std::saturate_cast<__uint128_t>(uintZero) == uintZero);
-  assert(std::saturate_cast<__uint128_t>(uintMax)  == uintMax);
+  std::same_as<__uint128_t> decltype(auto) _ = std::saturate_cast<__uint128_t>(std_scharMin);
+  assert(std::saturate_cast<__uint128_t>(std_scharMin)  == uintMin);
+  assert(std::saturate_cast<__uint128_t>(std_scharZero) == uintZero);
+  assert(std::saturate_cast<__uint128_t>(std_scharMax)  == static_cast<__uint128_t>(std_scharMax));
+
+  std::same_as<__uint128_t> decltype(auto) _ = std::saturate_cast<__uint128_t>(std_ucharMin);
+  assert(std::saturate_cast<__uint128_t>(std_ucharMin)  == uintMin);
+  assert(std::saturate_cast<__uint128_t>(std_ucharZero) == uintZero);
+  assert(std::saturate_cast<__uint128_t>(std_ucharMax)  == static_cast<__uint128_t>(std_ucharMax));
+
+  assert(std::saturate_cast<__uint128_t>(sintMin)       == uintMin); // saturated
+  assert(std::saturate_cast<__uint128_t>(sintZero)      == uintZero);
+  assert(std::saturate_cast<__uint128_t>(sintMax)       == static_cast<__uint128_t>(sintMax));
+
+  assert(std::saturate_cast<__uint128_t>(uintMin)       == uintMin);
+  assert(std::saturate_cast<__uint128_t>(uintZero)      == uintZero);
+  assert(std::saturate_cast<__uint128_t>(uintMax)       == uintMax);
 #endif
 
   // clang-format on

>From 453e681e173f057572dc14e5d0be895d1d04487f Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 20:33:02 +0200
Subject: [PATCH 35/38] Minor tweak

---
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 36 +++++++++----------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index b9664b3d67a8bfa..4a6a30395c665fb 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -71,29 +71,29 @@ constexpr bool test() {
   constexpr auto std_usintZero =         static_cast<unsigned short int>(0);
   constexpr auto std_usintMax  = std::numeric_limits<unsigned short int>::max();
 
-  constexpr auto std_sintMin  = std::numeric_limits<signed int>::min();
-  constexpr auto std_sintZero =         static_cast<signed int>(0);
-  constexpr auto std_sintMax  = std::numeric_limits<signed int>::max();
+  constexpr auto std_sintMin   = std::numeric_limits<signed int>::min();
+  constexpr auto std_sintZero  =         static_cast<signed int>(0);
+  constexpr auto std_sintMax   = std::numeric_limits<signed int>::max();
 
-  constexpr auto std_uintMin  = std::numeric_limits<unsigned int>::min();
-  constexpr auto std_uintZero =         static_cast<unsigned int>(0);
-  constexpr auto std_uintMax  = std::numeric_limits<unsigned int>::max();
+  constexpr auto std_uintMin   = std::numeric_limits<unsigned int>::min();
+  constexpr auto std_uintZero  =         static_cast<unsigned int>(0);
+  constexpr auto std_uintMax   = std::numeric_limits<unsigned int>::max();
 
-  constexpr auto std_slMin    = std::numeric_limits<signed long int>::min();
-  constexpr auto std_slZero   =         static_cast<signed long int>(0);
-  constexpr auto std_slMax    = std::numeric_limits<signed long int>::max();
+  constexpr auto std_slMin     = std::numeric_limits<signed long int>::min();
+  constexpr auto std_slZero    =         static_cast<signed long int>(0);
+  constexpr auto std_slMax     = std::numeric_limits<signed long int>::max();
 
-  constexpr auto std_ulMin    = std::numeric_limits<unsigned long int>::min();
-  constexpr auto std_ulZero   =         static_cast<unsigned long int>(0);
-  constexpr auto std_ulMax    = std::numeric_limits<unsigned long int>::max();
+  constexpr auto std_ulMin     = std::numeric_limits<unsigned long int>::min();
+  constexpr auto std_ulZero    =         static_cast<unsigned long int>(0);
+  constexpr auto std_ulMax     = std::numeric_limits<unsigned long int>::max();
 
-  constexpr auto std_sllMin   = std::numeric_limits<signed long long int>::min();
-  constexpr auto std_sllZero  =         static_cast<signed long long int>(0);
-  constexpr auto std_sllMax   = std::numeric_limits<signed long long int>::max();
+  constexpr auto std_sllMin    = std::numeric_limits<signed long long int>::min();
+  constexpr auto std_sllZero   =         static_cast<signed long long int>(0);
+  constexpr auto std_sllMax    = std::numeric_limits<signed long long int>::max();
 
-  constexpr auto std_ullMin   = std::numeric_limits<unsigned long long int>::min();
-  constexpr auto std_ullZero  =         static_cast<unsigned long long int>(0);
-  constexpr auto std_ullMax   = std::numeric_limits<unsigned long long int>::max();
+  constexpr auto std_ullMin    = std::numeric_limits<unsigned long long int>::min();
+  constexpr auto std_ullZero   =         static_cast<unsigned long long int>(0);
+  constexpr auto std_ullMax    = std::numeric_limits<unsigned long long int>::max();
   
   // signed char
 

>From 8261422697816576d8ce052ea64f259d66c83681 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 20:40:00 +0200
Subject: [PATCH 36/38] More tweaks

---
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 20 +++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 4a6a30395c665fb..41ed1f8300bd454 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -149,20 +149,20 @@ constexpr bool test() {
 
   std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_scharMin);
   assert(std::saturate_cast<signed int>(std_scharMin)  == static_cast<signed int>(std_scharMin));
-  assert(std::saturate_cast<signed int>(std_scharZero) == std_ssintZero);
+  assert(std::saturate_cast<signed int>(std_scharZero) == std_sintZero);
   assert(std::saturate_cast<signed int>(std_scharMax)  == static_cast<signed int>(std_scharMax));
 
   std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_ucharMin);
-  assert(std::saturate_cast<signed int>(std_ucharMin)  == std_ssintZero);
-  assert(std::saturate_cast<signed int>(std_ucharZero) == std_ssintZero);
+  assert(std::saturate_cast<signed int>(std_ucharMin)  == std_sintZero);
+  assert(std::saturate_cast<signed int>(std_ucharZero) == std_sintZero);
   assert(std::saturate_cast<signed int>(std_ucharMax)  == static_cast<signed int>(std_ucharMax));
 
-  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_ssintMin);
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_sintMin);
   assert(std::saturate_cast<signed int>(std_sintMin)   == std_sintMin);
   assert(std::saturate_cast<signed int>(std_sintZero)  == std_sintZero);
   assert(std::saturate_cast<signed int>(std_sintMax)   == std_sintMax);
 
-  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_usintMin);
+  std::same_as<signed int> decltype(auto) _ = std::saturate_cast<signed int>(std_uintMin);
   assert(std::saturate_cast<signed int>(std_uintMin)   == std_sintZero);
   assert(std::saturate_cast<signed int>(std_uintZero)  == std_sintZero);
   assert(std::saturate_cast<signed int>(std_uintMax)   == std_sintMax);  // saturated
@@ -213,20 +213,20 @@ constexpr bool test() {
 
   std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_scharMin);
   assert(std::saturate_cast<signed long long int>(std_scharMin)  == static_cast<signed long long int>(std_scharMin));
-  assert(std::saturate_cast<signed long long int>(std_scharZero) == std_slZero);
+  assert(std::saturate_cast<signed long long int>(std_scharZero) == std_sllZero);
   assert(std::saturate_cast<signed long long int>(std_scharMax)  == static_cast<signed long long int>(std_scharMax));
 
   std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_ucharMin);
-  assert(std::saturate_cast<signed long long int>(std_ucharMin)  == std_slZero);
-  assert(std::saturate_cast<signed long long int>(std_ucharZero) == std_slZero);
+  assert(std::saturate_cast<signed long long int>(std_ucharMin)  == std_sllZero);
+  assert(std::saturate_cast<signed long long int>(std_ucharZero) == std_sllZero);
   assert(std::saturate_cast<signed long long int>(std_ucharMax)  == static_cast<signed long long int>(std_ucharMax));
 
-  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_slMin);
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_sllMin);
   assert(std::saturate_cast<signed long long int>(std_sllMin)     == std_sllMin);
   assert(std::saturate_cast<signed long long int>(std_sllZero)    == std_sllZero);
   assert(std::saturate_cast<signed long long int>(std_sllMax)     == std_sllMax);
 
-  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_ulMin);
+  std::same_as<signed long long int> decltype(auto) _ = std::saturate_cast<signed long long int>(std_ullMin);
   assert(std::saturate_cast<signed long long int>(std_ullMin)     == std_sllZero);
   assert(std::saturate_cast<signed long long int>(std_ullZero)    == std_sllZero);
   assert(std::saturate_cast<signed long long int>(std_ullMax)     == std_sllMax);  // saturated

>From e9309673a074cdb5f6796f8eb98589d74dfbaf5a Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 21:15:09 +0200
Subject: [PATCH 37/38] More tweaks

---
 .../numeric.ops.sat/div_sat.pass.cpp          | 24 ++++++++-------
 .../numeric.ops.sat/saturate_cast.pass.cpp    | 30 +++++++++++--------
 2 files changed, 32 insertions(+), 22 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
index 056bd6571727c75..9d0a26afd1e91b8 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -26,18 +26,20 @@ constexpr bool test_signed() {
   [[maybe_unused]] std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
   static_assert(noexcept(std::div_sat(minVal, maxVal)));
 
+  // clang-format off
+
   // No saturation (-1, 0, 1)
 
-  assert(std::div_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{1});
-  assert(std::div_sat(IntegerT{-1}, IntegerT{1}) == IntegerT{-1});
-  assert(std::div_sat(IntegerT{-1}, minVal) == IntegerT{0});
-  assert(std::div_sat(IntegerT{-1}, maxVal) == IntegerT{0});
-  assert(std::div_sat(IntegerT{0}, IntegerT{-1}) == IntegerT{0});
-  assert(std::div_sat(IntegerT{0}, IntegerT{1}) == IntegerT{0});
-  assert(std::div_sat(IntegerT{0}, minVal) == IntegerT{0});
-  assert(std::div_sat(IntegerT{1}, IntegerT{-1}) == IntegerT{-1});
-  assert(std::div_sat(IntegerT{1}, minVal) == IntegerT{0});
-  assert(std::div_sat(IntegerT{1}, maxVal) == IntegerT{0});
+  assert(std::div_sat(IntegerT{-1}, IntegerT{-1}) == IntegerT{ 1});
+  assert(std::div_sat(IntegerT{-1}, IntegerT{ 1}) == IntegerT{-1});
+  assert(std::div_sat(IntegerT{-1},       minVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{-1},       maxVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 0}, IntegerT{-1}) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 0}, IntegerT{ 1}) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 0},       minVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 1}, IntegerT{-1}) == IntegerT{-1});
+  assert(std::div_sat(IntegerT{ 1},       minVal) == IntegerT{ 0});
+  assert(std::div_sat(IntegerT{ 1},       maxVal) == IntegerT{ 0});
 
   // No saturation (Large values)
 
@@ -74,6 +76,8 @@ constexpr bool test_signed() {
 
   assert(std::div_sat(minVal, IntegerT{-1}) == maxVal);
 
+  // clang-format on
+
   return true;
 }
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 41ed1f8300bd454..837494deb937e63 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -32,6 +32,12 @@ static_assert(noexcept(std::saturate_cast<signed char>(std::numeric_limits<unsig
 static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<signed int>::max())));
 static_assert(noexcept(std::saturate_cast<unsigned char>(std::numeric_limits<unsigned int>::max())));
 
+template <typename IntegerT>
+constexpr auto zero()
+{
+  return IntegerT{0};
+}
+
 constexpr bool test() {
   // clang-format off
 
@@ -46,53 +52,53 @@ constexpr bool test() {
   // Constants: biggest numbers
 
   constexpr auto sintMin  = std::numeric_limits<SIntT>::min();
-  constexpr auto sintZero = SIntT{0};
+  constexpr auto sintZero =                zero<SIntT>();
   constexpr auto sintMax  = std::numeric_limits<SIntT>::max();
 
   constexpr auto uintMin  = std::numeric_limits<UIntT>::min();
-  constexpr auto uintZero = UIntT{0};
+  constexpr auto uintZero =                zero<UIntT>();
   constexpr auto uintMax  = std::numeric_limits<UIntT>::max();
 
   // Constants: numeric limits
 
   constexpr auto std_scharMin  = std::numeric_limits<signed char>::min();
-  constexpr auto std_scharZero =         static_cast<signed char>(0);
+  constexpr auto std_scharZero =                zero<signed char>();
   constexpr auto std_scharMax  = std::numeric_limits<signed char>::max();
 
   constexpr auto std_ucharMin  = std::numeric_limits<unsigned char>::min();
-  constexpr auto std_ucharZero =         static_cast<unsigned char>(0);
+  constexpr auto std_ucharZero =                zero<unsigned char>();
   constexpr auto std_ucharMax  = std::numeric_limits<unsigned char>::max();
 
   constexpr auto std_ssintMin  = std::numeric_limits<signed short int>::min();
-  constexpr auto std_ssintZero =  static_cast<signed short int>(0);
+  constexpr auto std_ssintZero =                zero<signed short int>();
   constexpr auto std_ssintMax  = std::numeric_limits<signed short int>::max();
 
   constexpr auto std_usintMin  = std::numeric_limits<unsigned short int>::min();
-  constexpr auto std_usintZero =         static_cast<unsigned short int>(0);
+  constexpr auto std_usintZero =                zero<unsigned short int>();
   constexpr auto std_usintMax  = std::numeric_limits<unsigned short int>::max();
 
   constexpr auto std_sintMin   = std::numeric_limits<signed int>::min();
-  constexpr auto std_sintZero  =         static_cast<signed int>(0);
+  constexpr auto std_sintZero  =                zero<signed int>();
   constexpr auto std_sintMax   = std::numeric_limits<signed int>::max();
 
   constexpr auto std_uintMin   = std::numeric_limits<unsigned int>::min();
-  constexpr auto std_uintZero  =         static_cast<unsigned int>(0);
+  constexpr auto std_uintZero  =                zero<unsigned int>();
   constexpr auto std_uintMax   = std::numeric_limits<unsigned int>::max();
 
   constexpr auto std_slMin     = std::numeric_limits<signed long int>::min();
-  constexpr auto std_slZero    =         static_cast<signed long int>(0);
+  constexpr auto std_slZero    =                zero<signed long int>();
   constexpr auto std_slMax     = std::numeric_limits<signed long int>::max();
 
   constexpr auto std_ulMin     = std::numeric_limits<unsigned long int>::min();
-  constexpr auto std_ulZero    =         static_cast<unsigned long int>(0);
+  constexpr auto std_ulZero    =                zero<unsigned long int>();
   constexpr auto std_ulMax     = std::numeric_limits<unsigned long int>::max();
 
   constexpr auto std_sllMin    = std::numeric_limits<signed long long int>::min();
-  constexpr auto std_sllZero   =         static_cast<signed long long int>(0);
+  constexpr auto std_sllZero   =                zero<signed long long int>();
   constexpr auto std_sllMax    = std::numeric_limits<signed long long int>::max();
 
   constexpr auto std_ullMin    = std::numeric_limits<unsigned long long int>::min();
-  constexpr auto std_ullZero   =         static_cast<unsigned long long int>(0);
+  constexpr auto std_ullZero   =                zero<unsigned long long int>();
   constexpr auto std_ullMax    = std::numeric_limits<unsigned long long int>::max();
   
   // signed char

>From d97311b5ff0de222c09b135e2e05f8cfd04fd4aa Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 20 Jan 2024 21:19:20 +0200
Subject: [PATCH 38/38] More tweaks

---
 .../std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index f30c46d96a3173c..60dd776aa50f0d3 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -96,6 +96,8 @@ constexpr bool test_unsigned() {
 
   static_assert(noexcept(std::mul_sat(minVal, maxVal)));
 
+  // clang-format off
+
   // No saturation (0, 1)
 
   assert(std::mul_sat(IntegerT{0}, IntegerT{0}) == IntegerT{0});
@@ -129,6 +131,8 @@ constexpr bool test_unsigned() {
   }
   assert(std::mul_sat(maxVal, maxVal) == maxVal);
 
+  // clang-format on
+
   return true;
 }
 



More information about the libcxx-commits mailing list