#[derive(Clone, Copy, Debug)]
pub struct SeedLaw {
seed: u64,
}
fn splitmix64(mut z: u64) -> u64 {
z = z.wrapping_add(0x9E37_79B9_7F4A_7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn derive(seed: u64, domain: u64, a: u64, b: u64) -> u64 {
let mut z = splitmix64(seed ^ splitmix64(domain));
z = splitmix64(z ^ splitmix64(a));
z = splitmix64(z ^ splitmix64(b));
if z == 0 { 0x9E37_79B9_7F4A_7C15 } else { z }
}
impl SeedLaw {
pub fn new(seed: u64) -> Self {
Self { seed }
}
pub fn seed(&self) -> u64 {
self.seed
}
pub fn init(&self) -> u64 {
derive(self.seed, 0x11, 0, 0)
}
pub fn epoch_order(&self, epoch: u64) -> u64 {
derive(self.seed, 0x22, epoch, 0)
}
pub fn sample_noise(&self, epoch: u64, global_index: u64) -> u64 {
derive(self.seed, 0x33, epoch, global_index)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn derivations_are_nonzero_and_domain_separated() {
let law = SeedLaw::new(0);
assert_ne!(law.init(), 0);
assert_ne!(law.epoch_order(0), 0);
assert_ne!(law.sample_noise(0, 0), 0);
assert_ne!(law.init(), law.epoch_order(0));
assert_ne!(law.epoch_order(0), law.epoch_order(1));
assert_ne!(law.sample_noise(0, 1), law.sample_noise(1, 0));
}
#[test]
fn same_inputs_same_outputs() {
let a = SeedLaw::new(42);
let b = SeedLaw::new(42);
assert_eq!(a.init(), b.init());
assert_eq!(a.epoch_order(7), b.epoch_order(7));
assert_eq!(a.sample_noise(3, 999), b.sample_noise(3, 999));
}
}