//! Shared PRNG helpers for deterministic seeding (D-176, D-174). //! //! Both seed.rs and currency.rs use the same FNV-1a mix + Box-Muller transform. //! Centralised here to guarantee identical derivation chains across modules. use std::f64::consts::PI; use rand::Rng; use rand_chacha::ChaCha8Rng; /// FNV-1a 64-bit offset basis. const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325; /// FNV-1a 64-bit prime. const FNV_PRIME: u64 = 0x100000001b3; /// Deterministic per-key seed: FNV-1a of `key` mixed with `run_seed`. /// /// Starting from `run_seed + FNV_OFFSET_BASIS` provides per-run variation /// while preserving the FNV avalanche properties across keys. pub fn derive_seed(run_seed: u64, key: &str) -> u64 { let mut h = run_seed.wrapping_add(FNV_OFFSET_BASIS); for byte in key.bytes() { h ^= byte as u64; h = h.wrapping_mul(FNV_PRIME); } h } /// Box-Muller transform: standard normal variate from a ChaCha8 stream. pub fn standard_normal(rng: &mut ChaCha8Rng) -> f64 { let u1: f64 = 1.0 - rng.random::(); // avoid ln(0) let u2: f64 = rng.random::(); (-2.0 * u1.ln()).sqrt() * (2.0 * PI * u2).cos() }