use crate::authoring::primitives::rng::mix_seed;
pub const SWEEP_SALT: u64 = 0x005E_E900_5EED_0001;
pub const CONFIG_SALT: u64 = 0x00C0_4F16_5EED_0001;
pub const DESIGN_SALT: u64 = 0x00DE_5160_5EED_0001;
pub const SEARCH_SALT: u64 = 0x005E_A6C4_5EED_0001;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum SeedScheme {
#[default]
Common,
Independent,
}
impl SeedScheme {
pub fn as_str(self) -> &'static str {
match self {
Self::Common => "common",
Self::Independent => "independent",
}
}
pub fn formula(self) -> &'static str {
match self {
Self::Common => "mix_seed(mix_seed(root ^ SWEEP_SALT) + rep)",
Self::Independent => "mix_seed(mix_seed((mix_seed(root ^ CONFIG_SALT) + config_id) ^ SWEEP_SALT) + rep)",
}
}
pub fn seed(self, root: u64, config_id: u64, rep: u64) -> u64 {
match self {
Self::Common => run_seed(root, rep),
Self::Independent => independent_run_seed(root, config_id, rep),
}
}
}
pub fn run_seed(root: u64, rep: u64) -> u64 {
mix_seed(mix_seed(root ^ SWEEP_SALT).wrapping_add(rep))
}
pub fn independent_run_seed(root: u64, config_id: u64, rep: u64) -> u64 {
run_seed(mix_seed(root ^ CONFIG_SALT).wrapping_add(config_id), rep)
}
pub fn design_seed(root: u64, block: usize) -> u64 {
mix_seed(mix_seed(root ^ DESIGN_SALT).wrapping_add(block as u64))
}
pub fn search_seed(root: u64) -> u64 {
mix_seed(mix_seed(root ^ SEARCH_SALT))
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use super::{SWEEP_SALT, SeedScheme, design_seed, independent_run_seed, run_seed, search_seed};
#[test]
fn run_seeds_do_not_overlap_across_nearby_roots() {
let seeds: BTreeSet<u64> = (0..64)
.flat_map(|root| (0..64).map(move |rep| run_seed(root, rep)))
.collect();
assert_eq!(seeds.len(), 64 * 64);
}
#[test]
fn common_seeds_depend_on_the_replicate_only() {
let scheme = SeedScheme::Common;
assert_eq!(scheme.seed(7, 0, 3), scheme.seed(7, 5, 3));
assert_eq!(scheme.seed(7, 0, 3), run_seed(7, 3));
assert_ne!(scheme.seed(7, 0, 3), scheme.seed(7, 0, 4));
assert_ne!(scheme.seed(7, 0, 3), scheme.seed(8, 0, 3));
}
#[test]
fn independent_seeds_differ_by_config() {
let scheme = SeedScheme::Independent;
assert_ne!(scheme.seed(7, 0, 3), scheme.seed(7, 1, 3));
let seeds: BTreeSet<u64> = (0..64)
.flat_map(|config_id| (0..64).map(move |rep| independent_run_seed(7, config_id, rep)))
.collect();
assert_eq!(seeds.len(), 64 * 64);
let common: BTreeSet<u64> = (0..64).map(|rep| run_seed(7, rep)).collect();
assert!(seeds.is_disjoint(&common), "the two schemes do not share seeds");
}
#[test]
fn design_seeds_differ_by_block_and_from_run_seeds() {
let designs: BTreeSet<u64> = (0..64)
.flat_map(|root| (0..64).map(move |block| design_seed(root, block)))
.collect();
assert_eq!(designs.len(), 64 * 64);
let runs: BTreeSet<u64> = (0..64)
.flat_map(|root| (0..64).map(move |rep| run_seed(root, rep)))
.collect();
assert!(designs.is_disjoint(&runs), "a design draws apart from every run");
}
#[test]
fn search_seeds_differ_by_root_and_from_other_seeds() {
let searches: BTreeSet<u64> = (0..4096).map(search_seed).collect();
assert_eq!(searches.len(), 4096);
let others: BTreeSet<u64> = (0..64)
.flat_map(|root| (0..64).flat_map(move |index| [run_seed(root, index), design_seed(root, index as usize)]))
.collect();
assert!(
searches.is_disjoint(&others),
"a search draws apart from every run and design"
);
}
#[test]
fn no_run_seed_is_zero() {
for root in [0, 1, u64::MAX, SWEEP_SALT] {
for rep in 0..1024 {
assert_ne!(run_seed(root, rep), 0, "root {root}, replicate {rep}");
assert_ne!(independent_run_seed(root, rep, rep), 0, "root {root}, replicate {rep}");
}
}
}
}