xref: /llvm-project/libc/src/stdlib/qsort_pivot.h (revision f0247081faac6b4c0cbaa1540fc9c10756e5a42e)
1a738d81cSLukas Bergdoll //===-- Implementation header for qsort utilities ---------------*- C++ -*-===//
2a738d81cSLukas Bergdoll //
3a738d81cSLukas Bergdoll // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a738d81cSLukas Bergdoll // See https://llvm.org/LICENSE.txt for license information.
5a738d81cSLukas Bergdoll // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a738d81cSLukas Bergdoll //
7a738d81cSLukas Bergdoll //===----------------------------------------------------------------------===//
8a738d81cSLukas Bergdoll 
9a738d81cSLukas Bergdoll #ifndef LLVM_LIBC_SRC_STDLIB_QSORT_PIVOT_H
10a738d81cSLukas Bergdoll #define LLVM_LIBC_SRC_STDLIB_QSORT_PIVOT_H
11a738d81cSLukas Bergdoll 
12*f0247081SJoelWee #include <stddef.h>  // For size_t
13a738d81cSLukas Bergdoll 
14a738d81cSLukas Bergdoll namespace LIBC_NAMESPACE_DECL {
15a738d81cSLukas Bergdoll namespace internal {
16a738d81cSLukas Bergdoll 
17a738d81cSLukas Bergdoll // Recursively select a pseudomedian if above this threshold.
18a738d81cSLukas Bergdoll constexpr size_t PSEUDO_MEDIAN_REC_THRESHOLD = 64;
19a738d81cSLukas Bergdoll 
20a738d81cSLukas Bergdoll // Selects a pivot from `array`. Algorithm taken from glidesort by Orson Peters.
21a738d81cSLukas Bergdoll //
22a738d81cSLukas Bergdoll // This chooses a pivot by sampling an adaptive amount of points, approximating
23a738d81cSLukas Bergdoll // the quality of a median of sqrt(n) elements.
24a738d81cSLukas Bergdoll template <typename A, typename F>
25a738d81cSLukas Bergdoll size_t choose_pivot(const A &array, const F &is_less) {
26a738d81cSLukas Bergdoll   const size_t len = array.len();
27a738d81cSLukas Bergdoll 
28a738d81cSLukas Bergdoll   if (len < 8) {
29a738d81cSLukas Bergdoll     return 0;
30a738d81cSLukas Bergdoll   }
31a738d81cSLukas Bergdoll 
32a738d81cSLukas Bergdoll   const size_t len_div_8 = len / 8;
33a738d81cSLukas Bergdoll 
34a738d81cSLukas Bergdoll   const size_t a = 0;             // [0, floor(n/8))
35a738d81cSLukas Bergdoll   const size_t b = len_div_8 * 4; // [4*floor(n/8), 5*floor(n/8))
36a738d81cSLukas Bergdoll   const size_t c = len_div_8 * 7; // [7*floor(n/8), 8*floor(n/8))
37a738d81cSLukas Bergdoll 
38a738d81cSLukas Bergdoll   if (len < PSEUDO_MEDIAN_REC_THRESHOLD)
39a738d81cSLukas Bergdoll     return median3(array, a, b, c, is_less);
40a738d81cSLukas Bergdoll   else
41a738d81cSLukas Bergdoll     return median3_rec(array, a, b, c, len_div_8, is_less);
42a738d81cSLukas Bergdoll }
43a738d81cSLukas Bergdoll 
44a738d81cSLukas Bergdoll // Calculates an approximate median of 3 elements from sections a, b, c, or
45a738d81cSLukas Bergdoll // recursively from an approximation of each, if they're large enough. By
46a738d81cSLukas Bergdoll // dividing the size of each section by 8 when recursing we have logarithmic
47a738d81cSLukas Bergdoll // recursion depth and overall sample from f(n) = 3*f(n/8) -> f(n) =
48a738d81cSLukas Bergdoll // O(n^(log(3)/log(8))) ~= O(n^0.528) elements.
49a738d81cSLukas Bergdoll template <typename A, typename F>
50a738d81cSLukas Bergdoll size_t median3_rec(const A &array, size_t a, size_t b, size_t c, size_t n,
51a738d81cSLukas Bergdoll                    const F &is_less) {
52a738d81cSLukas Bergdoll   if (n * 8 >= PSEUDO_MEDIAN_REC_THRESHOLD) {
53a738d81cSLukas Bergdoll     const size_t n8 = n / 8;
54a738d81cSLukas Bergdoll     a = median3_rec(array, a, a + (n8 * 4), a + (n8 * 7), n8, is_less);
55a738d81cSLukas Bergdoll     b = median3_rec(array, b, b + (n8 * 4), b + (n8 * 7), n8, is_less);
56a738d81cSLukas Bergdoll     c = median3_rec(array, c, c + (n8 * 4), c + (n8 * 7), n8, is_less);
57a738d81cSLukas Bergdoll   }
58a738d81cSLukas Bergdoll   return median3(array, a, b, c, is_less);
59a738d81cSLukas Bergdoll }
60a738d81cSLukas Bergdoll 
61a738d81cSLukas Bergdoll /// Calculates the median of 3 elements.
62a738d81cSLukas Bergdoll template <typename A, typename F>
63a738d81cSLukas Bergdoll size_t median3(const A &array, size_t a, size_t b, size_t c, const F &is_less) {
64a738d81cSLukas Bergdoll   const void *a_ptr = array.get(a);
65a738d81cSLukas Bergdoll   const void *b_ptr = array.get(b);
66a738d81cSLukas Bergdoll   const void *c_ptr = array.get(c);
67a738d81cSLukas Bergdoll 
68a738d81cSLukas Bergdoll   const bool x = is_less(a_ptr, b_ptr);
69a738d81cSLukas Bergdoll   const bool y = is_less(a_ptr, c_ptr);
70a738d81cSLukas Bergdoll   if (x == y) {
71a738d81cSLukas Bergdoll     // If x=y=0 then b, c <= a. In this case we want to return max(b, c).
72a738d81cSLukas Bergdoll     // If x=y=1 then a < b, c. In this case we want to return min(b, c).
73a738d81cSLukas Bergdoll     // By toggling the outcome of b < c using XOR x we get this behavior.
74a738d81cSLukas Bergdoll     const bool z = is_less(b_ptr, c_ptr);
75a738d81cSLukas Bergdoll     return z ^ x ? c : b;
76a738d81cSLukas Bergdoll   } else {
77a738d81cSLukas Bergdoll     // Either c <= a < b or b <= a < c, thus a is our median.
78a738d81cSLukas Bergdoll     return a;
79a738d81cSLukas Bergdoll   }
80a738d81cSLukas Bergdoll }
81a738d81cSLukas Bergdoll 
82a738d81cSLukas Bergdoll } // namespace internal
83a738d81cSLukas Bergdoll } // namespace LIBC_NAMESPACE_DECL
84a738d81cSLukas Bergdoll 
85a738d81cSLukas Bergdoll #endif // LLVM_LIBC_SRC_STDLIB_QSORT_PIVOT_H
86