use std::cmp::Ordering;
const RADIX_THRESHOLD: usize = 256;
#[allow(clippy::uninit_vec, unsafe_code)]
pub fn radix_sort_coordinate_adaptive<T: Clone>(
entries: &mut [(u64, T)],
max_tid: u32,
max_pos: u64,
) {
let n = entries.len();
if n < RADIX_THRESHOLD {
insertion_sort_by_key(entries, |(k, _)| *k);
return;
}
let pos_bytes = bytes_needed_u64(max_pos);
let tid_bytes = bytes_needed_u32(max_tid);
let total_bytes = pos_bytes + tid_bytes;
if total_bytes == 0 {
return; }
let mut aux: Vec<(u64, T)> = Vec::with_capacity(n);
unsafe {
aux.set_len(n);
}
let mut src = entries as *mut [(u64, T)];
let mut dst = aux.as_mut_slice() as *mut [(u64, T)];
for byte_idx in 0..total_bytes {
let src_slice = unsafe { &*src };
let dst_slice = unsafe { &mut *dst };
let mut counts = [0usize; 256];
for (key, _) in src_slice {
let byte = ((key >> (byte_idx * 8)) & 0xFF) as usize;
counts[byte] += 1;
}
let mut total = 0;
for count in &mut counts {
let c = *count;
*count = total;
total += c;
}
for item in src_slice {
let byte = ((item.0 >> (byte_idx * 8)) & 0xFF) as usize;
let dest_idx = counts[byte];
counts[byte] += 1;
dst_slice[dest_idx] = item.clone();
}
std::mem::swap(&mut src, &mut dst);
}
if total_bytes % 2 == 1 {
let src_slice = unsafe { &*src };
entries.clone_from_slice(src_slice);
}
}
#[inline]
pub(crate) fn bytes_needed_u64(val: u64) -> usize {
if val == 0 {
return 0;
}
((64 - val.leading_zeros()) as usize).div_ceil(8)
}
#[inline]
fn bytes_needed_u32(val: u32) -> usize {
if val == 0 {
return 0;
}
((32 - val.leading_zeros()) as usize).div_ceil(8)
}
#[inline]
#[allow(unsafe_code)]
pub fn heap_sift_down<T, F>(heap: &mut [T], mut i: usize, n: usize, lt: &F)
where
F: Fn(&T, &T) -> bool,
{
let tmp = unsafe { std::ptr::read(&raw const heap[i]) };
loop {
let left = 2 * i + 1;
if left >= n {
break;
}
let right = left + 1;
let mut child = left;
if right < n && lt(&heap[left], &heap[right]) {
child = right;
}
if !lt(&tmp, &heap[child]) {
break;
}
unsafe {
std::ptr::copy_nonoverlapping(&raw const heap[child], &raw mut heap[i], 1);
}
i = child;
}
unsafe {
std::ptr::write(&raw mut heap[i], tmp);
}
}
#[inline]
pub fn heap_make<T, F>(heap: &mut [T], lt: &F)
where
F: Fn(&T, &T) -> bool,
{
let n = heap.len();
if n <= 1 {
return;
}
for i in (0..n / 2).rev() {
heap_sift_down(heap, i, n, lt);
}
}
#[inline]
pub fn heap_pop<T, F>(heap: &mut [T], n: usize, lt: &F) -> usize
where
F: Fn(&T, &T) -> bool,
{
if n == 0 {
return 0;
}
if n == 1 {
return 0;
}
heap.swap(0, n - 1);
let new_n = n - 1;
if new_n > 0 {
heap_sift_down(heap, 0, new_n, lt);
}
new_n
}
#[inline]
pub fn heap_replace_top<T, F>(heap: &mut [T], new_value: T, n: usize, lt: &F)
where
F: Fn(&T, &T) -> bool,
{
heap[0] = new_value;
heap_sift_down(heap, 0, n, lt);
}
#[inline]
pub fn insertion_sort_by_key<T, K: Ord, F: Fn(&T) -> K>(arr: &mut [T], key_fn: F) {
for i in 1..arr.len() {
let key = key_fn(&arr[i]);
let insert_pos = arr[..i].partition_point(|x| key_fn(x) <= key);
if insert_pos < i {
arr[insert_pos..=i].rotate_right(1);
}
}
}
#[inline]
pub fn binary_insertion_sort<T, F>(arr: &mut [T], compare: F)
where
F: Fn(&T, &T) -> Ordering,
{
for i in 1..arr.len() {
let mut lo = 0;
let mut hi = i;
while lo < hi {
let mid = lo + (hi - lo) / 2;
if compare(&arr[mid], &arr[i]) == Ordering::Greater {
hi = mid;
} else {
lo = mid + 1;
}
}
if lo < i {
arr[lo..=i].rotate_right(1);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
#[repr(transparent)]
struct PackedCoordinateKey(u64);
impl PackedCoordinateKey {
#[allow(clippy::cast_sign_loss)]
fn new(tid: i32, pos: i32, reverse: bool) -> Self {
let tid_bits = if tid < 0 { 0xFFFF_u64 } else { (tid as u64) & 0xFFFF };
let pos_bits = if pos < 0 { 0xFFFF_FFFF_u64 } else { (pos as u64) & 0xFFFF_FFFF };
let reverse_bit = u64::from(reverse);
Self((tid_bits << 48) | (pos_bits << 16) | (reverse_bit << 15))
}
}
impl PartialOrd for PackedCoordinateKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for PackedCoordinateKey {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
#[allow(clippy::cast_sign_loss)]
fn pack_coordinate_for_radix(tid: i32, pos: i32, reverse: bool, nref: u32) -> u64 {
let tid_val = if tid < 0 { nref } else { tid as u32 };
let pos_val = (((pos + 1) as u64) << 1) | u64::from(reverse);
pos_val | (u64::from(tid_val) << 40)
}
#[test]
fn test_packed_coordinate_key() {
let k1 = PackedCoordinateKey::new(0, 100, false);
let k2 = PackedCoordinateKey::new(0, 200, false);
let k3 = PackedCoordinateKey::new(1, 100, false);
let k4 = PackedCoordinateKey::new(0, 100, true);
assert!(k1 < k2); assert!(k1 < k3); assert!(k1 < k4); }
#[test]
fn test_packed_coordinate_key_unmapped() {
let mapped = PackedCoordinateKey::new(0, 100, false);
let unmapped = PackedCoordinateKey::new(-1, -1, false);
assert!(mapped < unmapped); }
#[test]
fn test_insertion_sort_by_key_packed() {
let mut entries: Vec<(u64, i32)> = vec![(5, 50), (3, 30), (8, 80), (1, 10), (4, 40)];
let mut expected = entries.clone();
expected.sort_by_key(|(k, _)| *k);
insertion_sort_by_key(&mut entries, |(k, _)| *k);
assert_eq!(entries, expected);
}
#[test]
fn test_radix_sort_coordinate_adaptive_large() {
let mut entries: Vec<(u64, usize)> = (0..1000).rev().map(|i| (i as u64, i)).collect();
let mut expected = entries.clone();
expected.sort_by_key(|(k, _)| *k);
radix_sort_coordinate_adaptive(&mut entries, 100, 1000);
assert_eq!(entries, expected);
}
#[test]
fn test_insertion_sort() {
let mut arr = vec![5, 3, 8, 1, 4, 2, 7, 6];
binary_insertion_sort(&mut arr, std::cmp::Ord::cmp);
assert_eq!(arr, vec![1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn test_insertion_sort_by_key() {
let mut arr: Vec<(i32, &str)> = vec![(5, "five"), (3, "three"), (8, "eight"), (1, "one")];
insertion_sort_by_key(&mut arr, |(k, _)| *k);
assert_eq!(arr[0].0, 1);
assert_eq!(arr[1].0, 3);
assert_eq!(arr[2].0, 5);
assert_eq!(arr[3].0, 8);
}
#[test]
fn test_heap_operations() {
let lt = |a: &i32, b: &i32| *a > *b;
let mut heap = vec![5, 3, 8, 1, 4, 2, 7, 6];
heap_make(&mut heap, <);
let mut sorted = Vec::new();
let mut n = heap.len();
while n > 0 {
sorted.push(heap[0]);
n = heap_pop(&mut heap, n, <);
}
assert_eq!(sorted, vec![1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn test_pack_coordinate_for_radix() {
let nref = 100u32;
let k1 = pack_coordinate_for_radix(0, 100, false, nref);
let k2 = pack_coordinate_for_radix(0, 200, false, nref);
let k3 = pack_coordinate_for_radix(1, 100, false, nref);
let k4 = pack_coordinate_for_radix(0, 100, true, nref);
assert!(k1 < k2); assert!(k1 < k3); assert!(k1 < k4);
let unmapped = pack_coordinate_for_radix(-1, -1, false, nref);
assert!(k1 < unmapped);
assert!(k3 < unmapped);
}
#[test]
fn test_bytes_needed() {
assert_eq!(bytes_needed_u64(0), 0);
assert_eq!(bytes_needed_u64(255), 1);
assert_eq!(bytes_needed_u64(256), 2);
assert_eq!(bytes_needed_u64(65535), 2);
assert_eq!(bytes_needed_u64(65536), 3);
assert_eq!(bytes_needed_u64(u64::MAX), 8);
assert_eq!(bytes_needed_u32(0), 0);
assert_eq!(bytes_needed_u32(255), 1);
assert_eq!(bytes_needed_u32(256), 2);
assert_eq!(bytes_needed_u32(u32::MAX), 4);
}
#[test]
fn test_radix_sort_adaptive() {
let mut entries: Vec<(u64, usize)> = vec![
(pack_coordinate_for_radix(1, 500, false, 100), 0),
(pack_coordinate_for_radix(0, 100, true, 100), 1),
(pack_coordinate_for_radix(0, 100, false, 100), 2),
(pack_coordinate_for_radix(2, 0, false, 100), 3),
(pack_coordinate_for_radix(-1, -1, false, 100), 4), ];
radix_sort_coordinate_adaptive(&mut entries, 100, 1002);
assert_eq!(entries[0].1, 2); assert_eq!(entries[1].1, 1); assert_eq!(entries[2].1, 0); assert_eq!(entries[3].1, 3); assert_eq!(entries[4].1, 4); }
}