1use std::cmp::Ordering;
12
13#[derive(Clone, Copy, Eq, PartialEq, Debug)]
18#[repr(transparent)]
19pub struct PackedCoordinateKey(u64);
20
21impl PackedCoordinateKey {
22 #[inline]
26 #[must_use]
27 #[allow(clippy::cast_sign_loss)]
28 pub fn new(tid: i32, pos: i32, reverse: bool) -> Self {
29 let tid_bits = if tid < 0 { 0xFFFF_u64 } else { (tid as u64) & 0xFFFF };
31 let pos_bits = if pos < 0 { 0xFFFF_FFFF_u64 } else { (pos as u64) & 0xFFFF_FFFF };
32 let reverse_bit = u64::from(reverse);
33
34 Self((tid_bits << 48) | (pos_bits << 16) | (reverse_bit << 15))
36 }
37
38 #[inline]
40 #[must_use]
41 pub fn as_u64(self) -> u64 {
42 self.0
43 }
44}
45
46impl PartialOrd for PackedCoordinateKey {
47 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
48 Some(self.cmp(other))
49 }
50}
51
52impl Ord for PackedCoordinateKey {
53 fn cmp(&self, other: &Self) -> Ordering {
54 self.0.cmp(&other.0)
55 }
56}
57
58const RADIX_THRESHOLD: usize = 256;
60
61#[allow(clippy::uninit_vec, unsafe_code)]
76pub fn radix_sort_coordinate_adaptive<T: Clone>(
77 entries: &mut [(u64, T)],
78 max_tid: u32,
79 max_pos: u64,
80) {
81 let n = entries.len();
82 if n < RADIX_THRESHOLD {
83 insertion_sort_by_key(entries, |(k, _)| *k);
84 return;
85 }
86
87 let pos_bytes = bytes_needed_u64(max_pos);
89 let tid_bytes = bytes_needed_u32(max_tid);
90 let total_bytes = pos_bytes + tid_bytes;
91
92 if total_bytes == 0 {
93 return; }
95
96 let mut aux: Vec<(u64, T)> = Vec::with_capacity(n);
98 unsafe {
99 aux.set_len(n);
100 }
101
102 let mut src = entries as *mut [(u64, T)];
103 let mut dst = aux.as_mut_slice() as *mut [(u64, T)];
104
105 for byte_idx in 0..total_bytes {
107 let src_slice = unsafe { &*src };
108 let dst_slice = unsafe { &mut *dst };
109
110 let mut counts = [0usize; 256];
112 for (key, _) in src_slice {
113 let byte = ((key >> (byte_idx * 8)) & 0xFF) as usize;
114 counts[byte] += 1;
115 }
116
117 let mut total = 0;
119 for count in &mut counts {
120 let c = *count;
121 *count = total;
122 total += c;
123 }
124
125 for item in src_slice {
127 let byte = ((item.0 >> (byte_idx * 8)) & 0xFF) as usize;
128 let dest_idx = counts[byte];
129 counts[byte] += 1;
130 dst_slice[dest_idx] = item.clone();
131 }
132
133 std::mem::swap(&mut src, &mut dst);
135 }
136
137 if total_bytes % 2 == 1 {
139 let src_slice = unsafe { &*src };
140 entries.clone_from_slice(src_slice);
141 }
142}
143
144#[inline]
146fn bytes_needed_u64(val: u64) -> usize {
147 if val == 0 {
148 return 0;
149 }
150 ((64 - val.leading_zeros()) as usize).div_ceil(8)
151}
152
153#[inline]
155fn bytes_needed_u32(val: u32) -> usize {
156 if val == 0 {
157 return 0;
158 }
159 ((32 - val.leading_zeros()) as usize).div_ceil(8)
160}
161
162#[inline]
169#[must_use]
170#[allow(clippy::cast_sign_loss)]
171pub fn pack_coordinate_for_radix(tid: i32, pos: i32, reverse: bool, nref: u32) -> u64 {
172 let tid_val = if tid < 0 { nref } else { tid as u32 };
174
175 let pos_val = (((pos + 1) as u64) << 1) | u64::from(reverse);
178
179 pos_val | (u64::from(tid_val) << 40)
182}
183
184#[allow(clippy::uninit_vec, unsafe_code)]
189pub fn radix_sort_u64<T: Clone>(entries: &mut [(u64, T)]) {
190 if entries.len() < RADIX_THRESHOLD {
191 insertion_sort_by_key(entries, |(k, _)| *k);
193 return;
194 }
195
196 let n = entries.len();
197
198 let mut aux: Vec<(u64, T)> = Vec::with_capacity(n);
200 unsafe {
201 aux.set_len(n);
202 }
203
204 let mut src = entries as *mut [(u64, T)];
205 let mut dst = aux.as_mut_slice() as *mut [(u64, T)];
206
207 for pass in 0..8 {
209 let shift = pass * 8;
210
211 let src_slice = unsafe { &*src };
212 let dst_slice = unsafe { &mut *dst };
213
214 let mut counts = [0usize; 256];
216 for (key, _) in src_slice {
217 let byte = ((key >> shift) & 0xFF) as usize;
218 counts[byte] += 1;
219 }
220
221 let mut total = 0;
223 for count in &mut counts {
224 let c = *count;
225 *count = total;
226 total += c;
227 }
228
229 for item in src_slice {
231 let byte = ((item.0 >> shift) & 0xFF) as usize;
232 let dest_idx = counts[byte];
233 counts[byte] += 1;
234 dst_slice[dest_idx] = item.clone();
235 }
236
237 std::mem::swap(&mut src, &mut dst);
239 }
240
241 }
243
244#[allow(unsafe_code)]
246pub fn radix_sort_coordinate<T: Clone>(entries: &mut [(PackedCoordinateKey, T)]) {
247 if entries.len() < RADIX_THRESHOLD {
248 insertion_sort_by_key(entries, |(k, _)| k.0);
249 return;
250 }
251
252 let entries_u64: &mut [(u64, T)] =
256 unsafe { std::slice::from_raw_parts_mut(entries.as_mut_ptr().cast(), entries.len()) };
257
258 radix_sort_u64(entries_u64);
259}
260
261#[inline]
278#[allow(unsafe_code)]
279pub fn heap_sift_down<T, F>(heap: &mut [T], mut i: usize, n: usize, lt: &F)
280where
281 F: Fn(&T, &T) -> bool,
282{
283 let tmp = unsafe { std::ptr::read(&raw const heap[i]) };
284
285 loop {
286 let left = 2 * i + 1;
287 if left >= n {
288 break;
289 }
290
291 let right = left + 1;
293 let mut child = left;
294 if right < n && lt(&heap[left], &heap[right]) {
295 child = right;
296 }
297
298 if !lt(&tmp, &heap[child]) {
300 break;
301 }
302
303 unsafe {
305 std::ptr::copy_nonoverlapping(&raw const heap[child], &raw mut heap[i], 1);
306 }
307 i = child;
308 }
309
310 unsafe {
311 std::ptr::write(&raw mut heap[i], tmp);
312 }
313}
314
315#[inline]
317pub fn heap_make<T, F>(heap: &mut [T], lt: &F)
318where
319 F: Fn(&T, &T) -> bool,
320{
321 let n = heap.len();
322 if n <= 1 {
323 return;
324 }
325
326 for i in (0..n / 2).rev() {
328 heap_sift_down(heap, i, n, lt);
329 }
330}
331
332#[inline]
336pub fn heap_pop<T, F>(heap: &mut [T], n: usize, lt: &F) -> usize
337where
338 F: Fn(&T, &T) -> bool,
339{
340 if n == 0 {
341 return 0;
342 }
343 if n == 1 {
344 return 0;
345 }
346
347 heap.swap(0, n - 1);
349
350 let new_n = n - 1;
352 if new_n > 0 {
353 heap_sift_down(heap, 0, new_n, lt);
354 }
355
356 new_n
357}
358
359#[inline]
363pub fn heap_replace_top<T, F>(heap: &mut [T], new_value: T, n: usize, lt: &F)
364where
365 F: Fn(&T, &T) -> bool,
366{
367 heap[0] = new_value;
368 heap_sift_down(heap, 0, n, lt);
369}
370
371#[inline]
380pub fn insertion_sort_by_key<T, K: Ord, F: Fn(&T) -> K>(arr: &mut [T], key_fn: F) {
381 for i in 1..arr.len() {
382 let key = key_fn(&arr[i]);
384 let insert_pos = arr[..i].partition_point(|x| key_fn(x) <= key);
385
386 if insert_pos < i {
388 arr[insert_pos..=i].rotate_right(1);
389 }
390 }
391}
392
393#[inline]
395pub fn binary_insertion_sort<T, F>(arr: &mut [T], compare: F)
396where
397 F: Fn(&T, &T) -> Ordering,
398{
399 for i in 1..arr.len() {
400 let mut lo = 0;
402 let mut hi = i;
403
404 while lo < hi {
405 let mid = lo + (hi - lo) / 2;
406 if compare(&arr[mid], &arr[i]) == Ordering::Greater {
407 hi = mid;
408 } else {
409 lo = mid + 1;
410 }
411 }
412
413 if lo < i {
415 arr[lo..=i].rotate_right(1);
416 }
417 }
418}
419
420pub fn hybrid_sort<T: Send, F>(arr: &mut [T], compare: F, parallel: bool)
422where
423 F: Fn(&T, &T) -> Ordering + Sync,
424{
425 const INSERTION_THRESHOLD: usize = 32;
426
427 if arr.len() <= INSERTION_THRESHOLD {
428 binary_insertion_sort(arr, compare);
429 } else if parallel {
430 use rayon::prelude::*;
431 arr.par_sort_unstable_by(|a, b| compare(a, b));
432 } else {
433 arr.sort_unstable_by(|a, b| compare(a, b));
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 #[test]
442 fn test_packed_coordinate_key() {
443 let k1 = PackedCoordinateKey::new(0, 100, false);
444 let k2 = PackedCoordinateKey::new(0, 200, false);
445 let k3 = PackedCoordinateKey::new(1, 100, false);
446 let k4 = PackedCoordinateKey::new(0, 100, true);
447
448 assert!(k1 < k2); assert!(k1 < k3); assert!(k1 < k4); }
452
453 #[test]
454 fn test_packed_coordinate_key_unmapped() {
455 let mapped = PackedCoordinateKey::new(0, 100, false);
456 let unmapped = PackedCoordinateKey::new(-1, -1, false);
457
458 assert!(mapped < unmapped); }
460
461 #[test]
462 fn test_radix_sort_small() {
463 let mut entries: Vec<(u64, i32)> = vec![(5, 50), (3, 30), (8, 80), (1, 10), (4, 40)];
464
465 radix_sort_u64(&mut entries);
466
467 assert_eq!(entries[0], (1, 10));
468 assert_eq!(entries[1], (3, 30));
469 assert_eq!(entries[2], (4, 40));
470 assert_eq!(entries[3], (5, 50));
471 assert_eq!(entries[4], (8, 80));
472 }
473
474 #[test]
475 fn test_radix_sort_large() {
476 let mut entries: Vec<(u64, usize)> = (0..1000).rev().map(|i| (i as u64, i)).collect();
477
478 radix_sort_u64(&mut entries);
479
480 for (i, (key, _)) in entries.iter().enumerate() {
481 assert_eq!(*key, i as u64);
482 }
483 }
484
485 #[test]
486 fn test_insertion_sort() {
487 let mut arr = vec![5, 3, 8, 1, 4, 2, 7, 6];
488 binary_insertion_sort(&mut arr, std::cmp::Ord::cmp);
489 assert_eq!(arr, vec![1, 2, 3, 4, 5, 6, 7, 8]);
490 }
491
492 #[test]
493 fn test_insertion_sort_by_key() {
494 let mut arr: Vec<(i32, &str)> = vec![(5, "five"), (3, "three"), (8, "eight"), (1, "one")];
495 insertion_sort_by_key(&mut arr, |(k, _)| *k);
496 assert_eq!(arr[0].0, 1);
497 assert_eq!(arr[1].0, 3);
498 assert_eq!(arr[2].0, 5);
499 assert_eq!(arr[3].0, 8);
500 }
501
502 #[test]
503 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
504 fn test_hybrid_sort() {
505 let mut arr: Vec<i32> = (0..100).rev().collect();
506 hybrid_sort(&mut arr, std::cmp::Ord::cmp, false);
507 for (i, &v) in arr.iter().enumerate() {
508 assert_eq!(v, i as i32);
509 }
510 }
511
512 #[test]
513 fn test_heap_operations() {
514 let lt = |a: &i32, b: &i32| *a > *b;
516
517 let mut heap = vec![5, 3, 8, 1, 4, 2, 7, 6];
518 heap_make(&mut heap, <);
519
520 let mut sorted = Vec::new();
522 let mut n = heap.len();
523 while n > 0 {
524 sorted.push(heap[0]);
525 n = heap_pop(&mut heap, n, <);
526 }
527
528 assert_eq!(sorted, vec![1, 2, 3, 4, 5, 6, 7, 8]);
529 }
530
531 #[test]
532 fn test_pack_coordinate_for_radix() {
533 let nref = 100u32;
534
535 let k1 = pack_coordinate_for_radix(0, 100, false, nref);
537 let k2 = pack_coordinate_for_radix(0, 200, false, nref);
538 let k3 = pack_coordinate_for_radix(1, 100, false, nref);
539 let k4 = pack_coordinate_for_radix(0, 100, true, nref);
540
541 assert!(k1 < k2); assert!(k1 < k3); assert!(k1 < k4); let unmapped = pack_coordinate_for_radix(-1, -1, false, nref);
547 assert!(k1 < unmapped);
548 assert!(k3 < unmapped);
549 }
550
551 #[test]
552 fn test_bytes_needed() {
553 assert_eq!(bytes_needed_u64(0), 0);
554 assert_eq!(bytes_needed_u64(255), 1);
555 assert_eq!(bytes_needed_u64(256), 2);
556 assert_eq!(bytes_needed_u64(65535), 2);
557 assert_eq!(bytes_needed_u64(65536), 3);
558 assert_eq!(bytes_needed_u64(u64::MAX), 8);
559
560 assert_eq!(bytes_needed_u32(0), 0);
561 assert_eq!(bytes_needed_u32(255), 1);
562 assert_eq!(bytes_needed_u32(256), 2);
563 assert_eq!(bytes_needed_u32(u32::MAX), 4);
564 }
565
566 #[test]
567 fn test_radix_sort_adaptive() {
568 let mut entries: Vec<(u64, usize)> = vec![
569 (pack_coordinate_for_radix(1, 500, false, 100), 0),
570 (pack_coordinate_for_radix(0, 100, true, 100), 1),
571 (pack_coordinate_for_radix(0, 100, false, 100), 2),
572 (pack_coordinate_for_radix(2, 0, false, 100), 3),
573 (pack_coordinate_for_radix(-1, -1, false, 100), 4), ];
575
576 radix_sort_coordinate_adaptive(&mut entries, 100, 1002);
578
579 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); }
586}