fix(simulation): address PR #122 review — determinism, correctness, labeling
- HashMap → BTreeMap throughout econ-sim for deterministic iteration (D-010) - Fix cost_factor: multiplicative gate×zone instead of additive (trade.rs) - Extract derive_seed to shared prng.rs, consolidate FNV-1a implementation - Rename run_shock_test → run_no_explosion_check (not D-179 Test 3) - Deduplicate cross-zone FX rate collection in Test 4 - Replace ORDER BY RANDOM() with deterministic ordering + ChaCha8Rng - Make commodity coverage failure a hard error consistent with D-175 - Fix gap-fill off-by-one (4 corps → 3 when coverage = 0) - Correct test report: EconEvent exists, location_type is body/station All four D-179 stability tests still pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
//! 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::<f64>(); // avoid ln(0)
|
||||
let u2: f64 = rng.random::<f64>();
|
||||
(-2.0 * u1.ln()).sqrt() * (2.0 * PI * u2).cos()
|
||||
}
|
||||
Reference in New Issue
Block a user