use exaloglog::{ExaLogLog, ExaLogLogFast};
fn splitmix64(x: i64) -> u64 {
let mut x = x as u64;
x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
x ^ (x >> 31)
}
fn rust_state_d24(p: u32, n: u32) -> Vec<u8> {
let mut s = ExaLogLogFast::new_dense(p);
for i in 0..n as i64 {
s.add_hash(splitmix64(i));
}
let snap = s.snapshot();
let mut out = Vec::with_capacity(snap.len() * 4);
for r in &snap {
out.extend_from_slice(&r.to_le_bytes());
}
out
}
fn rust_state_d20(p: u32, n: u32) -> Vec<u8> {
let mut s = ExaLogLog::new_dense(p);
for i in 0..n as i64 {
s.add_hash(splitmix64(i));
}
let bytes = s.to_bytes();
bytes[8..].to_vec() }
fn load_fixture(d: u32, p: u32, n: u32) -> Vec<u8> {
let path = format!("tests/fixtures/d{d}_p{p}_n{n}.hex");
let s = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("missing fixture {path}: {e}"));
let s = s.trim();
(0..s.len() / 2)
.map(|i| u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).unwrap())
.collect()
}
fn check(d: u32, p: u32, n: u32) {
let expected = load_fixture(d, p, n);
let actual = if d == 24 {
rust_state_d24(p, n)
} else {
rust_state_d20(p, n)
};
assert_eq!(
actual, expected,
"Java/Rust state diverged at t=2, d={d}, p={p}, n={n}"
);
}
#[test]
fn java_parity_d24_p4() {
check(24, 4, 100);
check(24, 4, 1000);
check(24, 4, 10000);
}
#[test]
fn java_parity_d24_p8() {
check(24, 8, 100);
check(24, 8, 1000);
check(24, 8, 10000);
}
#[test]
fn java_parity_d24_p12() {
check(24, 12, 100);
check(24, 12, 1000);
check(24, 12, 10000);
}
#[test]
fn java_parity_d20_p4() {
check(20, 4, 100);
check(20, 4, 1000);
check(20, 4, 10000);
}
#[test]
fn java_parity_d20_p8() {
check(20, 8, 100);
check(20, 8, 1000);
check(20, 8, 10000);
}
#[test]
fn java_parity_d20_p12() {
check(20, 12, 100);
check(20, 12, 1000);
check(20, 12, 10000);
}