#ifndef LLVM_LIBC_SRC_STDLIB_QUICK_SORT_H
#define LLVM_LIBC_SRC_STDLIB_QUICK_SORT_H
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/cstddef.h"
#include "src/__support/macros/config.h"
#include "src/stdlib/qsort_pivot.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
template <typename A, typename F>
LIBC_INLINE size_t partition_lomuto_branchless(const A &array,
const void *pivot,
const F &is_less) {
const size_t array_len = array.len();
size_t left = 0;
size_t right = 0;
while (right < array_len) {
const bool right_is_lt = is_less(array.get(right), pivot);
array.swap(left, right);
left += static_cast<size_t>(right_is_lt);
right += 1;
}
return left;
}
template <typename A, typename F>
LIBC_INLINE size_t partition_hoare_branchy(const A &array, const void *pivot,
const F &is_less) {
const size_t array_len = array.len();
size_t left = 0;
size_t right = array_len;
while (true) {
while (left < right && is_less(array.get(left), pivot))
++left;
while (true) {
--right;
if (left >= right || is_less(array.get(right), pivot)) {
break;
}
}
if (left >= right)
break;
array.swap(left, right);
++left;
}
return left;
}
template <typename A, typename F>
LIBC_INLINE size_t partition(const A &array, size_t pivot_index,
const F &is_less) {
if (pivot_index != 0) {
array.swap(0, pivot_index);
}
const A array_without_pivot = array.make_array(1, array.len() - 1);
const void *pivot = array.get(0);
size_t num_lt;
if constexpr (A::has_fixed_size()) {
num_lt = partition_lomuto_branchless(array_without_pivot, pivot, is_less);
} else {
num_lt = partition_hoare_branchy(array_without_pivot, pivot, is_less);
}
array.swap(0, num_lt);
return num_lt;
}
template <typename A, typename F>
LIBC_INLINE void quick_sort_impl(A &array, const void *ancestor_pivot,
size_t limit, const F &is_less) {
while (true) {
const size_t array_len = array.len();
if (array_len <= 1)
return;
if (limit == 0) {
heap_sort(array, is_less);
return;
}
limit -= 1;
const size_t pivot_index = choose_pivot(array, is_less);
if (ancestor_pivot) {
if (!is_less(ancestor_pivot, array.get(pivot_index))) {
const size_t num_lt =
partition(array, pivot_index,
[is_less](const void *a, const void *b) -> bool {
return !is_less(b, a);
});
array.reset_bounds(num_lt + 1, array.len() - (num_lt + 1));
ancestor_pivot = nullptr;
continue;
}
}
size_t split_index = partition(array, pivot_index, is_less);
if (array_len == 2)
return;
A left = array.make_array(0, split_index);
const void *pivot = array.get(split_index);
const size_t right_start = split_index + 1;
A right = array.make_array(right_start, array.len() - right_start);
quick_sort_impl(left, ancestor_pivot, limit, is_less);
array = right;
ancestor_pivot = pivot;
}
}
constexpr size_t ilog2(size_t n) {
return static_cast<size_t>(cpp::bit_width(n)) - 1;
}
template <typename A, typename F>
LIBC_INLINE void quick_sort(A &array, const F &is_less) {
const void *ancestor_pivot = nullptr;
const size_t limit = 2 * ilog2((array.len() | 1));
quick_sort_impl(array, ancestor_pivot, limit, is_less);
}
}
}
#endif