[libcxx-commits] [libcxx] Libcxx nth element small ranges (PR #215698)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 12 06:04:34 PDT 2026


================
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// Exhaustively check std::nth_element on all permutations of small ranges.
+// This specifically covers the size-0..5 sorting-network specializations and the
+// size-6..7 selection-sort fallback.
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <functional>
+#include <numeric>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class Iter, class Compare>
+void check_postcondition(Iter first, Iter nth, Iter last, Compare comp) {
+  if (nth == last)
+    return;
+  for (Iter i = first; i != nth; ++i)
+    assert(!comp(*nth, *i));
+  for (Iter i = nth; i != last; ++i)
+    assert(!comp(*i, *nth));
+}
+
+template <class Iter, class Compare>
+void test_permutations(std::vector<int> values, Compare comp) {
+  std::size_t const size    = values.size();
+  std::vector<int> expected = values;
+  std::sort(expected.begin(), expected.end(), comp);
+
+  do {
+    for (std::size_t nth = 0; nth <= size; ++nth) {
+      std::vector<int> v = values;
+      std::nth_element(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size), comp);
+      if (nth != size)
+        assert(v[nth] == expected[nth]);
+      assert(std::is_permutation(v.begin(), v.end(), values.begin()));
+      check_postcondition(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size), comp);
+    }
+  } while (std::next_permutation(values.begin(), values.end()));
+}
+
+template <class Iter>
+void test_distinct() {
+  for (int size = 0; size <= 8; ++size) {
+    std::vector<int> values(static_cast<std::size_t>(size));
+    std::iota(values.begin(), values.end(), 0);
+    test_permutations<Iter>(values, std::less<int>());
+    test_permutations<Iter>(values, std::greater<int>());
+  }
+}
+
+// Exercise equal-element paths with every bit pattern of 0/1 for sizes 1..8.
+template <class Iter>
+void test_duplicates() {
+  for (int size = 1; size <= 8; ++size) {
+    for (int mask = 0; mask < (1 << size); ++mask) {
+      std::vector<int> values(static_cast<std::size_t>(size));
+      for (int i = 0; i < size; ++i)
+        values[static_cast<std::size_t>(i)] = (mask >> i) & 1;
+
+      std::vector<int> expected = values;
+      std::sort(expected.begin(), expected.end());
+      for (int nth = 0; nth < size; ++nth) {
+        std::vector<int> v = values;
+        std::nth_element(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size));
+        assert(v[static_cast<std::size_t>(nth)] == expected[static_cast<std::size_t>(nth)]);
+        check_postcondition(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size), std::less<int>());
+      }
+    }
+  }
+}
+
+#if TEST_STD_VER >= 20
+constexpr bool test_constexpr_small_ranges() {
+  // Sizes 3, 4 and 5 hit the specialized sorting-network dispatch.
+  {
+    int a[] = {2, 0, 1};
+    std::nth_element(a, a + 1, a + 3);
+    if (a[1] != 1)
+      return false;
+  }
+  {
+    int a[] = {3, 0, 2, 1};
+    std::nth_element(a, a + 2, a + 4);
+    if (a[2] != 2)
+      return false;
+  }
+  {
+    int a[] = {4, 2, 0, 3, 1};
+    std::nth_element(a, a + 2, a + 5);
+    if (a[2] != 2)
+      return false;
+  }
+  {
+    int a[] = {4, 2, 0, 3, 1};
+    std::nth_element(a, a + 2, a + 5, std::greater<>());
+    if (a[2] != 2)
+      return false;
+  }
+  return true;
+}
+static_assert(test_constexpr_small_ranges());
----------------
ldionne wrote:

The pattern we use for testing constexpr is:

```
TEST_CONSTEXPR_CXX20 bool test() {
  // put all test content here

  return true;
}

int main(int, char**) {
  test();
#if TEST_STD_VER > X
  static_assert(test());
#endif

  return 0;
}
```

Then, we have exactly the same testing code for compile-time and runtime. I understand that you might hit constexpr-step limit issues. If so, there are two ways to solve it:
1. raise the limits by adding something like
```
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=20000000
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=80000000
```
2. Lower limits in the test when `if (TEST_IS_CONSTANT_EVALUATED)`.

But in both cases, we generally strive to reuse the same code for testing compile-time and runtime.


https://github.com/llvm/llvm-project/pull/215698


More information about the libcxx-commits mailing list