use super::super::bvh::MortonEntry;
use super::sort_by_code;
fn entries(codes: &[u64]) -> Vec<MortonEntry> {
codes
.iter()
.enumerate()
.map(|(source, code)| MortonEntry {
code: *code,
source: u32::try_from(source).expect("fixture index fits u32"),
})
.collect()
}
fn reference(codes: &[u64]) -> Vec<(u64, u32)> {
let mut expected: Vec<(u64, u32)> = codes
.iter()
.enumerate()
.map(|(source, code)| {
(
*code,
u32::try_from(source).expect("fixture index fits u32"),
)
})
.collect();
expected.sort_unstable();
expected
}
fn check(codes: &[u64], min_len: usize) {
let mut values = entries(codes);
let mut scratch = Vec::new();
sort_by_code(&mut values, &mut scratch, min_len);
let actual: Vec<(u64, u32)> = values.iter().map(|e| (e.code, e.source)).collect();
assert_eq!(
actual,
reference(codes),
"min_len {min_len}, {} keys",
codes.len()
);
}
fn spread(len: usize, distinct: u64) -> Vec<u64> {
let mut codes = Vec::with_capacity(len);
let mut state = 0x9e37_79b9_7f4a_7c15u64;
for _ in 0..len {
state ^= state >> 30;
state = state.wrapping_mul(0xbf58_476d_1ce4_e5b9);
state ^= state >> 27;
codes.push((state % distinct) & 0x7fff_ffff_ffff_ffff);
}
codes
}
#[test]
fn sorting_agrees_with_the_comparison_sort_on_both_paths() {
for len in [0usize, 1, 2, 3, 17, 255, 256, 257, 4096] {
let codes = spread(len, 1_000_000);
check(&codes, 0);
check(&codes, usize::MAX);
}
}
#[test]
fn equal_keys_keep_their_input_order_so_the_source_never_needs_carrying() {
let codes = spread(1000, 10);
check(&codes, 0);
check(&codes, usize::MAX);
}
#[test]
fn a_column_of_one_repeated_key_is_left_in_source_order() {
let codes = vec![42u64; 500];
check(&codes, 0);
check(&codes, usize::MAX);
}
#[test]
fn keys_that_differ_only_in_the_top_byte_partition_correctly() {
let codes: Vec<u64> = (0..512u64).map(|index| (index % 256) << 48).collect();
check(&codes, 0);
check(&codes, usize::MAX);
}
#[test]
fn keys_that_differ_only_in_the_lowest_byte_still_sort() {
let codes: Vec<u64> = (0..512u64).map(|index| index % 256).collect();
check(&codes, 0);
check(&codes, usize::MAX);
}
#[test]
fn the_parallel_and_serial_paths_produce_the_same_permutation() {
let codes = spread(20_000, 5_000);
let mut parallel = entries(&codes);
let mut serial = entries(&codes);
let mut scratch = Vec::new();
sort_by_code(&mut parallel, &mut scratch, 0);
sort_by_code(&mut serial, &mut scratch, usize::MAX);
let parallel: Vec<(u64, u32)> = parallel.iter().map(|e| (e.code, e.source)).collect();
let serial: Vec<(u64, u32)> = serial.iter().map(|e| (e.code, e.source)).collect();
assert_eq!(parallel, serial);
}
#[test]
fn the_full_63_bit_range_sorts_without_losing_high_bits() {
let codes = vec![
0x7fff_ffff_ffff_ffff,
0x0000_0000_0000_0001,
0x4000_0000_0000_0000,
0x0000_0000_ffff_ffff,
0x0000_ffff_0000_0000,
0,
];
check(&codes, 0);
check(&codes, usize::MAX);
}