pub use crate::hash::{ribbon_hash, start};
const K_COEFF_AND_RESULT_FACTOR: u64 = 0xc28f_8282_2b65_0bed; const K_COEFF_XOR64: u64 = 0xc367_844a_6e52_731d; const K_SEED_MIX_MASK: u64 = 0xf0f0_f0f0_f0f0_f0f0;
const K_SEED_MIX_SHIFT: u32 = 4;
const K_TO_RAW_SEED_FACTOR: u64 = 0xc782_19a2_3eea_dd03;
#[inline]
pub fn coeff_row_128(h: u64) -> u128 {
let a = h.wrapping_mul(K_COEFF_AND_RESULT_FACTOR);
let c = ((a as u128) << 64) ^ ((a ^ K_COEFF_XOR64) as u128);
c | 1
}
#[inline]
pub fn result_row(h: u64) -> u32 {
let a = h.wrapping_mul(K_COEFF_AND_RESULT_FACTOR);
a.swap_bytes() as u32 }
#[inline]
pub fn ordinal_to_raw_seed(ordinal: u32) -> u32 {
let mut tmp = (ordinal as u64).wrapping_mul(K_TO_RAW_SEED_FACTOR);
tmp ^= (tmp & K_SEED_MIX_MASK) >> K_SEED_MIX_SHIFT;
tmp as u32
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
fn load() -> serde_json::Value {
let text = std::fs::read_to_string(
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/vectors/std_w128_r7.json"),
)
.expect("reference vectors must be present");
serde_json::from_str(&text).expect("reference vectors must be valid JSON")
}
fn num(value: &serde_json::Value, name: &str) -> u64 {
value[name]
.as_u64()
.unwrap_or_else(|| panic!("missing or non-u64 reference field {name}"))
}
#[test]
fn w128_hash_coeff_result_seed_match_reference() {
let j = load();
let num_starts = num(&j, "num_starts_seed0");
let vectors = j["hash_vectors"]
.as_array()
.expect("hash_vectors must be an array");
for obj in vectors {
let key = num(obj, "key");
let h = ribbon_hash(key, 0);
assert_eq!(h, num(obj, "hash"), "hash mismatch key {key}");
assert_eq!(
start(h, num_starts),
num(obj, "start"),
"start mismatch key {key}"
);
let cr = coeff_row_128(h);
assert_eq!(
(cr >> 64) as u64,
num(obj, "coeff_hi"),
"coeff_hi mismatch key {key}"
);
assert_eq!(
cr as u64,
num(obj, "coeff_lo"),
"coeff_lo mismatch key {key}"
);
assert_eq!(
result_row(h) as u64,
num(obj, "result"),
"result mismatch key {key}"
);
}
assert!(vectors.len() >= 1000, "expected >=1000 vectors");
}
#[test]
fn seed_mapping_is_injective_and_zero_fixed() {
assert_eq!(ordinal_to_raw_seed(0), 0);
let seeds: std::collections::HashSet<u32> = (0..64).map(ordinal_to_raw_seed).collect();
assert_eq!(
seeds.len(),
64,
"ordinal->raw seed must be injective over 0..64"
);
}
}