blob: 7faf5cd3b10e174682cbe82dad5a902390d94c66 [file]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::unchecked;
/// Perform radix sort on a single array
///
/// - array: the array to be sorted
/// - counts: the counters to be used for counting, must be initialized to 0.
/// will be filled with the number of elements in each bucket after sorting.
/// - key: a function to extract the key from the array element
pub fn radix_sort_by_key<T>(array: &mut [T], counts: &mut [usize], key: impl Fn(&T) -> usize) {
#[derive(Default, Clone, Copy)]
struct Part {
cur: usize,
end: usize,
}
let num_keys = counts.len();
let mut counts = unchecked!(counts);
let mut parts = unchecked!(vec![Part::default(); num_keys]);
// count
array.iter().for_each(|item| counts[key(item)] += 1);
// construct parts
let mut beg = 0;
for (idx, count) in counts.iter().enumerate() {
if *count > 0 {
parts[idx] = Part {
cur: beg,
end: beg + count,
};
beg += count;
}
}
// reorganize each partition
let mut inexhausted_part_indices = unchecked!(vec![0; num_keys]);
for i in 0..num_keys {
inexhausted_part_indices[i] = i;
}
while {
inexhausted_part_indices.retain(|&i| parts[i].cur < parts[i].end);
inexhausted_part_indices.len() > 1
} {
for &part_idx in inexhausted_part_indices.iter() {
let cur_part = &parts[part_idx];
let cur = cur_part.cur;
let end = cur_part.end;
for item_idx in cur..end {
let target_part_idx = key(&array[item_idx]);
let target_part = &mut parts[target_part_idx];
unsafe {
// safety: skip bound check
array.swap_unchecked(item_idx, target_part.cur);
}
target_part.cur += 1;
}
}
}
}
#[cfg(test)]
mod test {
use rand::Rng;
use super::*;
#[test]
fn fuzzytest_u16_small() {
for n in 0..1000 {
let mut array = vec![];
for _ in 0..n {
array.push(rand::rng().random::<u16>());
}
let mut array1 = array.clone();
radix_sort_by_key(&mut array1, &mut [0; 65536], |key| *key as usize);
let mut array2 = array.clone();
array2.sort_unstable();
assert_eq!(array1, array2);
}
}
#[test]
fn fuzzytest_u16_1m() {
let mut array = vec![];
for _ in 0..1000000 {
array.push(rand::rng().random::<u16>());
}
let mut array1 = array.clone();
radix_sort_by_key(&mut array1, &mut [0; 65536], |key| *key as usize);
let mut array2 = array.clone();
array2.sort_unstable();
assert_eq!(array1, array2);
}
}