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