use rand::{seq::SliceRandom, thread_rng};
use rayon::prelude::*;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
pub struct HashSort;
impl HashSort {
pub fn hash_sort<T: Clone>(lines: &mut [T], get_key: impl Fn(&T) -> &[u8] + Sync) {
if lines.len() < 2 {
return;
}
let groups = Self::hash_group_lines(lines, &get_key);
let shuffled_indices = Self::create_shuffled_indices(&groups);
Self::reorder_by_indices(lines, &shuffled_indices);
}
fn hash_group_lines<T>(lines: &[T], get_key: impl Fn(&T) -> &[u8]) -> Vec<Vec<usize>> {
let mut hash_to_indices: HashMap<u64, Vec<usize>> = HashMap::new();
for (idx, line) in lines.iter().enumerate() {
let key = get_key(line);
let hash = Self::fast_hash(key);
hash_to_indices.entry(hash).or_default().push(idx);
}
hash_to_indices.into_values().collect()
}
#[inline]
fn fast_hash(data: &[u8]) -> u64 {
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
hasher.finish()
}
fn create_shuffled_indices(groups: &[Vec<usize>]) -> Vec<usize> {
let mut rng = thread_rng();
let mut result = Vec::with_capacity(groups.iter().map(|g| g.len()).sum());
let mut group_order: Vec<usize> = (0..groups.len()).collect();
group_order.shuffle(&mut rng);
for &group_idx in &group_order {
result.extend_from_slice(&groups[group_idx]);
}
result
}
fn reorder_by_indices<T: Clone>(lines: &mut [T], indices: &[usize]) {
let original = lines.to_vec();
for (new_pos, &old_pos) in indices.iter().enumerate() {
lines[new_pos] = original[old_pos].clone();
}
}
pub fn parallel_hash_sort<T: Clone + Send + Sync>(
lines: &mut [T],
get_key: impl Fn(&T) -> &[u8] + Sync,
) {
if lines.len() < 100_000 {
Self::hash_sort(lines, get_key);
return;
}
let groups = Self::parallel_hash_group(lines, &get_key);
let shuffled_indices = Self::create_shuffled_indices(&groups);
Self::reorder_by_indices(lines, &shuffled_indices);
}
fn parallel_hash_group<T: Send + Sync>(
lines: &[T],
get_key: &(impl Fn(&T) -> &[u8] + Sync),
) -> Vec<Vec<usize>> {
let _chunk_size = lines.len() / rayon::current_num_threads();
let hashes: Vec<(usize, u64)> = lines
.par_iter()
.enumerate()
.map(|(idx, line)| {
let key = get_key(line);
(idx, Self::fast_hash(key))
})
.collect();
let mut hash_to_indices: HashMap<u64, Vec<usize>> = HashMap::new();
for (idx, hash) in hashes {
hash_to_indices.entry(hash).or_default().push(idx);
}
hash_to_indices.into_values().collect()
}
pub fn streaming_random_sort<R, W>(
_reader: R,
_writer: W,
_memory_limit_mb: usize,
) -> std::io::Result<()>
where
R: std::io::BufRead,
W: std::io::Write,
{
todo!("Streaming implementation")
}
}
#[cfg(target_arch = "x86_64")]
pub mod simd_hash {
use std::arch::x86_64::*;
#[target_feature(enable = "avx2")]
pub unsafe fn simd_hash_avx2(data: &[u8]) -> u64 {
let mut hash = 0u64;
let mut i = 0;
while i + 32 <= data.len() {
let chunk = _mm256_loadu_si256(data.as_ptr().add(i) as *const __m256i);
let mixed = _mm256_xor_si256(chunk, _mm256_set1_epi64x(0x9E3779B97F4A7C15u64 as i64));
let vals: [u64; 4] = std::mem::transmute(mixed);
hash ^= vals[0].wrapping_mul(vals[1]) ^ vals[2].wrapping_mul(vals[3]);
i += 32;
}
while i < data.len() {
hash = hash.wrapping_mul(0x9E3779B97F4A7C15u64) ^ (data[i] as u64);
i += 1;
}
hash ^= hash >> 33;
hash = hash.wrapping_mul(0xC2B2AE3D27D4EB4Fu64);
hash ^= hash >> 29;
hash
}
}
pub struct ZeroAllocHashSort;
impl ZeroAllocHashSort {
pub fn sort_indices_only(count: usize, get_hash: impl Fn(usize) -> u64) -> Vec<usize> {
let mut groups: HashMap<u64, Vec<usize>> = HashMap::new();
for i in 0..count {
let hash = get_hash(i);
groups.entry(hash).or_default().push(i);
}
let mut rng = thread_rng();
let mut group_vec: Vec<_> = groups.into_values().collect();
group_vec.shuffle(&mut rng);
let mut result = Vec::with_capacity(count);
for group in group_vec {
result.extend(group);
}
result
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_ultra_random_sort() {
let data = ["apple", "banana", "apple", "cherry", "banana"];
let mut groups: std::collections::HashMap<&str, Vec<usize>> =
std::collections::HashMap::new();
for (i, item) in data.iter().enumerate() {
groups.entry(*item).or_default().push(i);
}
let mut i = 0;
while i < data.len() {
let current = data[i];
let mut j = i + 1;
while j < data.len() && data[j] == current {
j += 1;
}
for item in &data[i..j] {
assert_eq!(*item, current);
}
i = j;
}
}
#[test]
fn test_performance() {
let mut data: Vec<String> = Vec::new();
for i in 0..100_000 {
data.push(format!("item_{}", i % 100));
}
let start = std::time::Instant::now();
let mut groups: std::collections::HashMap<&[u8], Vec<usize>> =
std::collections::HashMap::new();
for (i, item) in data.iter().enumerate() {
groups.entry(item.as_bytes()).or_default().push(i);
}
let duration = start.elapsed();
println!("Ultra random sort took: {duration:?}");
assert!(duration.as_millis() < 100); }
}