use core::mem::{self, MaybeUninit};
use core::{ptr, slice};
use super::counting::{
Count, STACK_BYTES, Stack, bump, counters, exclusive_scan, in_order, key_slots, min_max,
places4, rewrite, scatter,
};
use super::key::IntegerKey;
use super::key::sealed::Bits;
use super::tim::Buffer;
pub fn radix<T: IntegerKey>(arr: &mut [T]) {
let len = arr.len();
if len < 2 {
return;
}
if in_order(arr, |x, y| y < x) {
return;
}
if in_order(arr, |x, y| y > x) {
arr.reverse();
return;
}
let first = arr[0].bits();
let varying = arr.iter().fold(<T::Bits as Bits>::ZERO, |acc, x| {
acc.or(x.bits().xor(first))
});
let (plan, lanes) = Plan::choose(varying, len);
if plan.digits == 1 && plan.low == 0 {
let (min, max) = min_max(arr);
let k = max.offset(min) + 1;
if len <= u32::MAX as usize {
rewrite::<T, u32>(arr, min, k);
} else {
rewrite::<T, usize>(arr, min, k);
}
} else if lanes == LANES {
sort_values_in_lanes(arr, &plan);
} else if len <= u16::MAX as usize {
sort_values::<T, u16, 1>(arr, &plan);
} else if len <= u32::MAX as usize {
sort_values::<T, u32, 1>(arr, &plan);
} else {
sort_values::<T, usize, 1>(arr, &plan);
}
}
#[inline(never)]
fn sort_values_in_lanes<T: IntegerKey>(arr: &mut [T], plan: &Plan) {
const {
assert!(
mem::size_of::<T>() == mem::size_of::<T::Bits>()
&& mem::align_of::<T>() == mem::align_of::<T::Bits>()
)
};
let flip = unsafe { mem::transmute_copy::<T, T::Bits>(&arr[0]) }.xor(arr[0].bits());
let bits = unsafe { slice::from_raw_parts_mut(arr.as_mut_ptr().cast::<T::Bits>(), arr.len()) };
let flipped = flip != <T::Bits as Bits>::ZERO;
if flipped {
for b in bits.iter_mut() {
*b = b.xor(flip);
}
}
sort_values::<T::Bits, u16, LANES>(bits, plan);
if flipped {
for b in bits.iter_mut() {
*b = b.xor(flip);
}
}
}
pub fn radix_by_key<T, K, F>(arr: &mut [T], mut key: F)
where
K: IntegerKey,
F: FnMut(&T) -> K,
{
let len = arr.len();
if len < 2 || mem::size_of::<T>() == 0 {
return;
}
let mut stack = Stack([MaybeUninit::uninit(); STACK_BYTES]);
let mut heap = Vec::new();
let slots = key_slots::<K::Bits>(len, &mut stack, &mut heap);
let first = key(&arr[0]).bits();
slots[0].write(first);
let mut varying = <K::Bits as Bits>::ZERO;
let mut prev = first;
let (mut descent, mut no_descent) = (false, false);
for (slot, x) in slots[1..].iter_mut().zip(&arr[1..]) {
let x = key(x).bits();
slot.write(x);
varying = varying.or(x.xor(first));
descent |= x < prev;
no_descent |= x >= prev;
prev = x;
}
if !descent {
return;
}
if !no_descent {
arr.reverse();
return;
}
let (plan, lanes) = Plan::choose(varying, len);
if plan.digits == 1 {
let keys = unsafe { slice::from_raw_parts_mut(slots.as_mut_ptr().cast::<K::Bits>(), len) };
let (mut min, mut max) = (plan.mask(), 0);
for k in keys.iter_mut() {
let d = k.digit(plan.low, plan.mask());
min = min.min(d);
max = max.max(d);
*k = Bits::of(d);
}
let (min, k) = (<K::Bits as Bits>::of(min), max - min + 1);
scatter(arr, keys, min, k);
} else if len <= u16::MAX as usize {
if lanes == LANES {
sort_by_keys::<T, K::Bits, u16, LANES>(arr, slots, &plan);
} else {
sort_by_keys::<T, K::Bits, u16, 1>(arr, slots, &plan);
}
} else if len <= u32::MAX as usize {
sort_by_keys::<T, K::Bits, u32, 1>(arr, slots, &plan);
} else {
sort_by_keys::<T, K::Bits, usize, 1>(arr, slots, &plan);
}
}
struct Plan {
low: u32,
width: u32,
digits: usize,
}
const MAX_WIDTH: u32 = 12;
const MIN_WIDTH: u32 = 8;
const MIN_WIDTH_LEN: usize = 64;
const SMALLEST_WIDTH: u32 = 4;
const LANES: usize = 8;
const LANE_WIDTH: u32 = 8;
const LANE_LEN_PER_DIGIT: usize = 80;
const LANES_MIN_LEN: usize = 16;
const LANE_GAIN: usize = 144;
const NARROW_WIDTH: u32 = 6;
const NARROW_WIDTH_LEN: usize = 24;
const NARROW_LEN_PER_DIGIT: usize = 48;
impl Plan {
fn new<B: Bits>(varying: B, max: u32) -> Self {
let low = varying.trailing_zeros();
let span = B::BITS - varying.leading_zeros() - low;
let digits = span.div_ceil(max);
Self {
low,
width: span.div_ceil(digits),
digits: digits as usize,
}
}
fn choose<B: Bits>(varying: B, len: usize) -> (Self, usize) {
let mut max = len.ilog2().clamp(SMALLEST_WIDTH, MAX_WIDTH);
if len >= MIN_WIDTH_LEN {
max = max.max(MIN_WIDTH);
}
let plan = Self::new(varying, max);
if plan.digits == 1 || len < LANES_MIN_LEN {
return (plan, 1);
}
let lanes = Self::new(varying, LANE_WIDTH);
if lanes.digits > LANES || len >= LANE_LEN_PER_DIGIT * lanes.digits {
return (plan, 1);
}
let narrow = Self::new(varying, NARROW_WIDTH);
let saved = narrow.digits - lanes.digits;
if len * saved * saved >= LANE_GAIN {
(lanes, LANES)
} else if len >= NARROW_WIDTH_LEN && len < NARROW_LEN_PER_DIGIT * plan.digits {
(narrow, 1)
} else {
(plan, 1)
}
}
fn radix(&self) -> usize {
1 << self.width
}
fn mask(&self) -> usize {
self.radix() - 1
}
fn shift(&self, d: usize) -> u32 {
self.low + d as u32 * self.width
}
}
#[inline(always)]
fn slot<const S: usize>(plan: &Plan, d: usize, v: usize) -> usize {
if S == 1 {
d * plan.radix() + v
} else {
v * S + d
}
}
fn counts_len<const S: usize>(plan: &Plan) -> usize {
if S == 1 {
plan.digits * plan.radix()
} else {
plan.radix() * S
}
}
fn histogram<T: IntegerKey, C: Count, const S: usize>(v: &[T], plan: &Plan, counts: &mut [C]) {
debug_assert!(S == 1 || plan.digits <= S);
match plan.digits {
1 => count_digits::<T, C, S>(v, plan, 1, counts),
2 => count_digits::<T, C, S>(v, plan, 2, counts),
3 => count_digits::<T, C, S>(v, plan, 3, counts),
4 => count_digits::<T, C, S>(v, plan, 4, counts),
5 => count_digits::<T, C, S>(v, plan, 5, counts),
6 => count_digits::<T, C, S>(v, plan, 6, counts),
7 => count_digits::<T, C, S>(v, plan, 7, counts),
8 => count_digits::<T, C, S>(v, plan, 8, counts),
13 => count_digits::<T, C, S>(v, plan, 13, counts),
16 => count_digits::<T, C, S>(v, plan, 16, counts),
digits => count_digits::<T, C, S>(v, plan, digits, counts),
}
}
#[inline(always)]
fn count_digits<T: IntegerKey, C: Count, const S: usize>(
v: &[T],
plan: &Plan,
digits: usize,
counts: &mut [C],
) {
debug_assert!(counts.len() == counts_len::<S>(plan));
let c = counts.as_mut_ptr();
let mask = plan.mask();
let quarter = v.len() / 4;
let (a, rest) = v.split_at(quarter);
let (b, rest) = rest.split_at(quarter);
let (d, rest) = rest.split_at(quarter);
let (e, rest) = rest.split_at(quarter);
for (((a, b), d), e) in a.iter().zip(b).zip(d).zip(e) {
for x in [a, b, d, e] {
unsafe { count_key::<T, C, S>(x, plan, digits, mask, c) };
}
}
for x in rest {
unsafe { count_key::<T, C, S>(x, plan, digits, mask, c) };
}
}
#[inline(always)]
unsafe fn count_key<T: IntegerKey, C: Count, const S: usize>(
x: &T,
plan: &Plan,
digits: usize,
mask: usize,
c: *mut C,
) {
let bits = x.bits();
for d in 0..digits {
unsafe { bump(c.add(slot::<S>(plan, d, bits.digit(plan.shift(d), mask)))) };
}
}
fn places<C: Count>(counts: &mut [C], len: usize, any: usize) -> bool {
if counts[any].get() == len {
return true;
}
table_places(counts);
false
}
fn table_places<C: Count>(counts: &mut [C]) {
if counts.len() <= 16 {
let mut sum = 0;
for c in counts.iter_mut() {
let count = c.get();
*c = C::ZERO.add(sum);
sum += count;
}
} else {
exclusive_scan(counts);
}
}
fn shared_lanes<B: Bits, C: Count, const S: usize>(
counts: &[C],
plan: &Plan,
len: usize,
first: B,
) -> u64 {
let mask = plan.mask();
(0..plan.digits).fold(0, |shared, d| {
let count = counts[slot::<S>(plan, d, first.digit(plan.shift(d), mask))].get();
shared | (u64::from(count == len) << d)
})
}
fn lane_places<C: Count, const S: usize>(counts: &mut [C], len: usize) {
let rows = counts.as_chunks_mut::<S>().0;
let (low, high) = rows.split_at_mut(rows.len() / 2);
debug_assert!(low.len() == high.len());
let (mut up, mut down) = ([C::ZERO; S], [C::ZERO.add(len); S]);
for (a, b) in low.iter_mut().zip(high.iter_mut().rev()) {
let (x, y) = (*a, *b);
*a = up;
for l in 0..S {
up[l] = up[l].add(x[l].get());
down[l] = C::ZERO.add(down[l].get() - y[l].get());
}
*b = down;
}
}
#[inline(always)]
fn is_shared<C: Count, const S: usize>(
counts: &[C],
plan: &Plan,
d: usize,
shared: u64,
len: usize,
any: usize,
) -> bool {
if S == 1 {
counts[slot::<S>(plan, d, any)].get() == len
} else {
shared & (1 << d) != 0
}
}
#[inline(always)]
fn pass_places<C: Count, const S: usize>(
counts: &mut [C],
plan: &Plan,
d: usize,
shared: u64,
len: usize,
any: usize,
) -> Option<*mut C> {
if is_shared::<C, S>(counts, plan, d, shared, len, any) {
return None;
}
if S == 1 {
let radix = plan.radix();
let table = &mut counts[d * radix..][..radix];
table_places(table);
return Some(table.as_mut_ptr());
}
Some(unsafe { counts.as_mut_ptr().add(d) })
}
fn sort_values<T: IntegerKey, C: Count, const S: usize>(arr: &mut [T], plan: &Plan) {
let len = arr.len();
let mut stack = Stack([MaybeUninit::uninit(); STACK_BYTES]);
let mut heap = Vec::new();
let counts = counters::<C>(counts_len::<S>(plan), &mut stack, &mut heap);
histogram::<T, C, S>(arr, plan, counts);
let first = arr[0].bits();
let mut shared = 0;
if S > 1 {
shared = shared_lanes::<T::Bits, C, S>(counts, plan, len, first);
lane_places::<C, S>(counts, len);
}
let mut buf = Buffer::<T>::new(len);
let mut src = arr.as_mut_ptr();
let mut dst = buf.get();
let mask = plan.mask();
if S == 1 {
for (d, counts) in counts.chunks_exact_mut(plan.radix()).enumerate() {
let shift = plan.shift(d);
if places(counts, len, first.digit(shift, mask)) {
continue;
}
unsafe { pass_values::<T, C, 1>(src, dst, len, counts.as_mut_ptr(), shift, mask) };
mem::swap(&mut src, &mut dst);
}
} else {
for d in 0..plan.digits {
if shared & (1 << d) != 0 {
continue;
}
unsafe {
let c = counts.as_mut_ptr().add(d);
pass_values::<T, C, S>(src, dst, len, c, plan.shift(d), mask);
}
mem::swap(&mut src, &mut dst);
}
}
if src != arr.as_mut_ptr() {
unsafe { ptr::copy_nonoverlapping(src, arr.as_mut_ptr(), len) };
}
}
#[inline(always)]
unsafe fn pass_values<T: IntegerKey, C: Count, const S: usize>(
src: *const T,
dst: *mut T,
len: usize,
c: *mut C,
shift: u32,
mask: usize,
) {
let mut i = 0;
while i + 4 <= len {
unsafe {
let x = [0, 1, 2, 3].map(|j| *src.add(i + j));
let b = x.map(|x| x.bits().digit(shift, mask) * S);
let p = places4(b, b.map(|b| (*c.add(b)).get()));
for j in 0..4 {
*c.add(b[j]) = C::ZERO.add(p[j] + 1);
}
for j in 0..4 {
dst.add(p[j]).write(x[j]);
}
}
i += 4;
}
for i in i..len {
unsafe {
let x = *src.add(i);
let pos = bump(c.add(x.bits().digit(shift, mask) * S));
dst.add(pos).write(x);
}
}
}
const GATHER_MIN_PASS_BYTES: usize = 96;
const GATHER_MAX_BYTES: usize = 16 << 20;
const GATHER_MIN_LEN: usize = MIN_WIDTH_LEN;
fn sort_by_keys<T, B: Bits + IntegerKey, C: Count, const S: usize>(
arr: &mut [T],
keys: &mut [MaybeUninit<B>],
plan: &Plan,
) {
let len = arr.len();
let keys = unsafe { slice::from_raw_parts_mut(keys.as_mut_ptr().cast::<B>(), len) };
let mut stack = Stack([MaybeUninit::uninit(); STACK_BYTES]);
let mut heap = Vec::new();
let counts = counters::<C>(counts_len::<S>(plan), &mut stack, &mut heap);
histogram::<B, C, S>(keys, plan, counts);
let mut shared = 0;
if S > 1 {
shared = shared_lanes::<B, C, S>(counts, plan, len, keys[0]);
lane_places::<C, S>(counts, len);
}
let size = mem::size_of::<T>();
if plan.digits * size >= GATHER_MIN_PASS_BYTES
&& len >= GATHER_MIN_LEN
&& len.saturating_mul(size) <= GATHER_MAX_BYTES
{
gather::<T, B, C, S>(arr, keys, plan, counts, shared);
} else {
move_with_keys::<T, B, C, S>(arr, keys, plan, counts, shared);
}
}
fn move_with_keys<T, B: Bits + IntegerKey, C: Count, const S: usize>(
arr: &mut [T],
keys: &mut [B],
plan: &Plan,
counts: &mut [C],
shared: u64,
) {
let len = arr.len();
let mut buf = Buffer::<T>::new(len);
let mut stack = Stack([MaybeUninit::uninit(); STACK_BYTES]);
let mut heap = Vec::new();
let key_buf = key_slots::<B>(len, &mut stack, &mut heap);
let (mut src, mut dst) = (arr.as_mut_ptr(), buf.get());
let (mut ks, mut kd) = (keys.as_mut_ptr(), key_buf.as_mut_ptr().cast::<B>());
let mask = plan.mask();
let first = keys[0];
let last = (0..plan.digits)
.rev()
.find(|&d| {
!is_shared::<C, S>(
counts,
plan,
d,
shared,
len,
first.digit(plan.shift(d), mask),
)
})
.unwrap_or(0);
for d in 0..plan.digits {
let any = first.digit(plan.shift(d), mask);
let Some(c) = pass_places::<C, S>(counts, plan, d, shared, len, any) else {
continue;
};
let shift = plan.shift(d);
let move_keys = d != last;
let mut i = 0;
while i + 4 <= len {
unsafe {
let k = [0, 1, 2, 3].map(|j| *ks.add(i + j));
let b = k.map(|k| k.digit(shift, mask) * S);
let p = places4(b, b.map(|b| (*c.add(b)).get()));
for j in 0..4 {
*c.add(b[j]) = C::ZERO.add(p[j] + 1);
}
for j in 0..4 {
if move_keys {
kd.add(p[j]).write(k[j]);
}
ptr::copy_nonoverlapping(src.add(i + j), dst.add(p[j]), 1);
}
}
i += 4;
}
for i in i..len {
unsafe {
let k = *ks.add(i);
let pos = bump(c.add(k.digit(shift, mask) * S));
if move_keys {
kd.add(pos).write(k);
}
ptr::copy_nonoverlapping(src.add(i), dst.add(pos), 1);
}
}
mem::swap(&mut src, &mut dst);
mem::swap(&mut ks, &mut kd);
}
if src != arr.as_mut_ptr() {
unsafe { ptr::copy_nonoverlapping(src, arr.as_mut_ptr(), len) };
}
}
fn gather<T, B: Bits + IntegerKey, C: Count, const S: usize>(
arr: &mut [T],
keys: &[B],
plan: &Plan,
counts: &mut [C],
shared: u64,
) {
let len = arr.len();
let mut tagged: Vec<(B, u32)> = keys.iter().zip(0..).map(|(&k, i)| (k, i)).collect();
let mut other: Vec<(B, u32)> = Vec::with_capacity(len);
let (mut src, mut dst) = (tagged.as_mut_ptr(), other.as_mut_ptr());
let mask = plan.mask();
let first = keys[0];
for d in 0..plan.digits {
let any = first.digit(plan.shift(d), mask);
let Some(c) = pass_places::<C, S>(counts, plan, d, shared, len, any) else {
continue;
};
let shift = plan.shift(d);
let mut i = 0;
while i + 4 <= len {
unsafe {
let e = [0, 1, 2, 3].map(|j| *src.add(i + j));
let b = e.map(|e| e.0.digit(shift, mask) * S);
let p = places4(b, b.map(|b| (*c.add(b)).get()));
for j in 0..4 {
*c.add(b[j]) = C::ZERO.add(p[j] + 1);
}
for j in 0..4 {
dst.add(p[j]).write(e[j]);
}
}
i += 4;
}
for i in i..len {
unsafe {
let e = *src.add(i);
let pos = bump(c.add(e.0.digit(shift, mask) * S));
dst.add(pos).write(e);
}
}
mem::swap(&mut src, &mut dst);
}
let mut buf = Buffer::<T>::new(len);
let out = buf.get();
let base = arr.as_ptr();
for i in 0..len {
unsafe { ptr::copy_nonoverlapping(base.add((*src.add(i)).1 as usize), out.add(i), 1) };
}
unsafe { ptr::copy_nonoverlapping(out, arr.as_mut_ptr(), len) };
}