use std::collections::HashMap;
use crate::types::Value;
pub struct Context {
pub vars: HashMap<String, Value>,
pub now_serial: Option<f64>,
pub rng_cell: Option<(u64, u32, u32, u32)>,
}
impl Context {
pub fn new(vars: HashMap<String, Value>) -> Self {
let normalized = vars.into_iter()
.map(|(k, v)| (k.to_uppercase(), v))
.collect();
Self { vars: normalized, now_serial: None, rng_cell: None }
}
pub fn empty() -> Self {
Self { vars: HashMap::new(), now_serial: None, rng_cell: None }
}
pub fn get(&self, name: &str) -> Value {
self.vars
.get(&name.to_uppercase())
.cloned()
.unwrap_or(Value::Empty)
}
pub fn lookup(&self, name: &str) -> Option<Value> {
self.vars.get(&name.to_uppercase()).cloned()
}
pub fn set(&mut self, name: String, value: Value) -> Option<Value> {
self.vars.insert(name.to_uppercase(), value)
}
pub fn remove(&mut self, name: &str) {
self.vars.remove(&name.to_uppercase());
}
pub fn draw_rand(&self, draw_index: u32) -> f64 {
if let Some((seed, si, r, c)) = self.rng_cell {
let key = self.splitmix_chain(seed, si, r, c, draw_index);
(key >> 32) as f64 / (u32::MAX as f64 + 1.0)
} else {
let s = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(12345);
let key = Self::mix64(s ^ Self::mix64(draw_index as u64));
(key >> 32) as f64 / (u32::MAX as f64 + 1.0)
}
}
pub fn draw_rand_n(&self, count: usize) -> Vec<f64> {
(0..count as u32).map(|i| self.draw_rand(i)).collect()
}
fn splitmix_chain(&self, seed: u64, si: u32, r: u32, c: u32, draw: u32) -> u64 {
let mut h = seed;
for part in [si as u64, r as u64, c as u64, draw as u64] {
h = Self::mix64(h ^ Self::mix64(part));
}
h
}
fn mix64(mut x: u64) -> u64 {
x = x.wrapping_add(0x9e3779b97f4a7c15);
x = (x ^ (x >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)).wrapping_mul(0x94d049bb133111eb);
x ^ (x >> 31)
}
}
#[cfg(test)]
mod tests;