use crate::marshal::{register_typed_fn_0, register_typed_fn_1, register_typed_fn_2};
use crate::module_exports::ModuleExports;
use crate::typed_module_exports::{ConcreteReturn, ConcreteType, TypedReturn};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use std::cell::RefCell;
thread_local! {
static RNG: RefCell<ChaCha8Rng> = RefCell::new(ChaCha8Rng::from_entropy());
}
pub fn with_rng<F, R>(f: F) -> R
where
F: FnOnce(&mut ChaCha8Rng) -> R,
{
RNG.with(|rng| f(&mut *rng.borrow_mut()))
}
pub fn create_random_intrinsics_module() -> ModuleExports {
let mut module = ModuleExports::new("std::core::intrinsics::random");
module.description =
"Random number generation intrinsics (ChaCha8 thread-local PRNG)".to_string();
register_typed_fn_0::<_>(
&mut module,
"__intrinsic_random",
"Generate random f64 in [0, 1)",
ConcreteType::Number,
|_ctx| {
let value = with_rng(|rng| rng.r#gen::<f64>());
Ok(TypedReturn::Concrete(ConcreteReturn::F64(value)))
},
);
register_typed_fn_2::<_, f64, f64>(
&mut module,
"__intrinsic_random_int",
"Generate random integer in [lo, hi] (inclusive); returns as number",
[("lo", "number"), ("hi", "number")],
ConcreteType::Number,
|lo, hi, _ctx| {
let lo = lo as i64;
let hi = hi as i64;
if lo > hi {
return Err(format!(
"__intrinsic_random_int: lo ({}) must be <= hi ({})",
lo, hi
));
}
let value = with_rng(|rng| rng.gen_range(lo..=hi));
Ok(TypedReturn::Concrete(ConcreteReturn::F64(value as f64)))
},
);
register_typed_fn_1::<_, f64>(
&mut module,
"__intrinsic_random_seed",
"Seed the thread-local RNG for reproducibility",
"seed",
"number",
ConcreteType::Unit,
|seed, _ctx| {
let seed = seed as u64;
with_rng(|rng| {
*rng = ChaCha8Rng::seed_from_u64(seed);
});
Ok(TypedReturn::Concrete(ConcreteReturn::Unit))
},
);
register_typed_fn_2::<_, f64, f64>(
&mut module,
"__intrinsic_random_normal",
"Generate random number from a normal distribution (Box-Muller)",
[("mean", "number"), ("std", "number")],
ConcreteType::Number,
|mean, std, _ctx| {
if std < 0.0 {
return Err("__intrinsic_random_normal: std must be non-negative".to_string());
}
let value = with_rng(|rng| {
let u1: f64 = rng.r#gen();
let u2: f64 = rng.r#gen();
let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
mean + std * z
});
Ok(TypedReturn::Concrete(ConcreteReturn::F64(value)))
},
);
register_typed_fn_1::<_, i64>(
&mut module,
"__intrinsic_random_array",
"Generate array of n random numbers in [0, 1)",
"n",
"int",
ConcreteType::ArrayNumber,
|n, _ctx| {
if n < 0 {
return Err("__intrinsic_random_array: n must be non-negative".to_string());
}
let n = n as usize;
let values: Vec<f64> = with_rng(|rng| (0..n).map(|_| rng.r#gen::<f64>()).collect());
Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(values)))
},
);
module
}