diff --git a/Cargo.lock b/Cargo.lock index e9bdd29606..0b740e8158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1245,15 +1245,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_distr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2" -dependencies = [ - "rand 0.7.3", -] - [[package]] name = "rand_hc" version = "0.1.0" @@ -1539,7 +1530,7 @@ dependencies = [ "paste", "pwd", "rand 0.7.3", - "rand_distr", + "rand_core 0.5.1", "regex", "result-like", "rustc_version_runtime", diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 1da5c9c2dc..fccb2dbcc5 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -30,8 +30,8 @@ num-traits = "0.2.8" num-integer = "0.1.41" num-rational = "0.2.2" num-iter = "0.1.39" -rand = { version = "0.7", features = ["small_rng"] } -rand_distr = "0.2" +rand = "0.7" +rand_core = "0.5" getrandom = "0.1" log = "0.4" rustpython-derive = {path = "../derive", version = "0.1.1"} diff --git a/vm/src/stdlib/random.rs b/vm/src/stdlib/random.rs index 19716746fe..46e58d45f4 100644 --- a/vm/src/stdlib/random.rs +++ b/vm/src/stdlib/random.rs @@ -3,22 +3,60 @@ use std::cell::RefCell; use num_bigint::{BigInt, Sign}; +use num_traits::Signed; +use rand::RngCore; -use rand::distributions::Distribution; -use rand::{RngCore, SeedableRng}; -use rand::rngs::SmallRng; -use rand_distr::Normal; - -use crate::function::OptionalArg; +use crate::function::OptionalOption; +use crate::obj::objint::PyIntRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyValue, PyResult}; +use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::VirtualMachine; -use crate::vm::VirtualMachine; +mod mersenne; + +#[derive(Debug)] +enum PyRng { + Std(rand::rngs::ThreadRng), + MT(mersenne::MT19937), +} + +impl Default for PyRng { + fn default() -> Self { + PyRng::Std(rand::thread_rng()) + } +} + +impl RngCore for PyRng { + fn next_u32(&mut self) -> u32 { + match self { + Self::Std(s) => s.next_u32(), + Self::MT(m) => m.next_u32(), + } + } + fn next_u64(&mut self) -> u64 { + match self { + Self::Std(s) => s.next_u64(), + Self::MT(m) => m.next_u64(), + } + } + fn fill_bytes(&mut self, dest: &mut [u8]) { + match self { + Self::Std(s) => s.fill_bytes(dest), + Self::MT(m) => m.fill_bytes(dest), + } + } + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + match self { + Self::Std(s) => s.try_fill_bytes(dest), + Self::MT(m) => m.try_fill_bytes(dest), + } + } +} #[pyclass(name = "Random")] #[derive(Debug)] struct PyRandom { - rng: RefCell + rng: RefCell, } impl PyValue for PyRandom { @@ -27,86 +65,74 @@ impl PyValue for PyRandom { } } -#[pyimpl] +#[pyimpl(flags(BASETYPE))] impl PyRandom { #[pyslot(new)] fn new(cls: PyClassRef, vm: &VirtualMachine) -> PyResult> { PyRandom { - rng: RefCell::new(SmallRng::from_entropy()) - }.into_ref_with_type(vm, cls) - } - - #[pymethod] - fn seed(&self, n: Option, vm: &VirtualMachine) -> PyResult { - let rng = match n { - None => SmallRng::from_entropy(), - Some(n) => { - let seed = n as u64; - SmallRng::seed_from_u64(seed) - } - }; - - *self.rng.borrow_mut() = rng; - - Ok(vm.ctx.none()) + rng: RefCell::new(PyRng::default()), + } + .into_ref_with_type(vm, cls) } #[pymethod] - fn getrandbits(&self, k: usize, vm: &VirtualMachine) -> PyResult { - let bytes = (k - 1) / 8 + 1; - let mut bytearray = vec![0u8; bytes]; - self.rng.borrow_mut().fill_bytes(&mut bytearray); + fn random(&self) -> f64 { + gen_res53(&mut *self.rng.borrow_mut()) + } - let bits = bytes % 8; - if bits > 0 { - bytearray[0] >>= 8 - bits; + #[pymethod] + fn seed(&self, n: OptionalOption) { + let new_rng = match n.flat_option() { + None => PyRng::default(), + Some(n) => { + let (_, mut key) = n.as_bigint().abs().to_u32_digits(); + if cfg!(target_endian = "big") { + key.reverse(); + } + PyRng::MT(mersenne::MT19937::new_with_slice_seed(&key)) + } + }; + + *self.rng.borrow_mut() = new_rng; + } + + #[pymethod] + fn getrandbits(&self, mut k: usize) -> BigInt { + let mut rng = self.rng.borrow_mut(); + + let mut gen_u32 = |k| rng.next_u32() >> (32 - k) as u32; + + if k <= 32 { + return gen_u32(k).into(); } - - println!("{:?}", k); - println!("{:?}", bytearray); - let result = BigInt::from_bytes_be(Sign::Plus, &bytearray); - Ok(vm.ctx.new_bigint(&result)) + let words = (k - 1) / 8 + 1; + let mut wordarray = vec![0u32; words]; + + let it = wordarray.iter_mut(); + #[cfg(target_endian = "big")] + let it = it.rev(); + for word in it { + *word = gen_u32(k); + k -= 32; + } + + BigInt::from_slice(Sign::NoSign, &wordarray) } } pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { let ctx = &vm.ctx; - let random_type = PyRandom::make_class(ctx); - py_module!(vm, "_random", { - "Random" => random_type, - "gauss" => ctx.new_function(random_normalvariate), // TODO: is this the same? - "normalvariate" => ctx.new_function(random_normalvariate), - "random" => ctx.new_function(random_random), - // "weibull", ctx.new_function(random_weibullvariate), + "Random" => PyRandom::make_class(ctx), }) } -fn random_normalvariate(mu: f64, sigma: f64, vm: &VirtualMachine) -> PyResult { - let normal = Normal::new(mu, sigma).map_err(|rand_err| { - vm.new_exception_msg( - vm.ctx.exceptions.arithmetic_error.clone(), - format!("invalid normal distribution: {:?}", rand_err), - ) - })?; - let value = normal.sample(&mut rand::thread_rng()); - Ok(value) +// taken from the translated mersenne twister +/* generates a random number on [0,1) with 53-bit resolution*/ +fn gen_res53(rng: &mut R) -> f64 { + let a = rng.next_u32() >> 5; + let b = rng.next_u32() >> 6; + (a as f64 * 67108864.0 + b as f64) * (1.0 / 9007199254740992.0) } - -fn random_random(_vm: &VirtualMachine) -> f64 { - rand::random() -} - -/* - * TODO: enable this function: -fn random_weibullvariate(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(alpha, Some(vm.ctx.float_type())), (beta, Some(vm.ctx.float_type()))]); - let alpha = objfloat::get_value(alpha); - let beta = objfloat::get_value(beta); - let weibull = Weibull::new(alpha, beta); - let value = weibull.sample(&mut rand::thread_rng()); - let py_value = vm.ctx.new_float(value); - Ok(py_value) -} -*/ +/* These real versions are due to Isaku Wada, 2002/01/09 added */ diff --git a/vm/src/stdlib/random/mersenne.rs b/vm/src/stdlib/random/mersenne.rs new file mode 100644 index 0000000000..d4732a6ff5 --- /dev/null +++ b/vm/src/stdlib/random/mersenne.rs @@ -0,0 +1,202 @@ +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ + +// this was translated from c; all rights go to copyright holders listed above +// https://gist.github.com/coolreader18/b56d510f1b0551d2954d74ad289f7d2e + +/* Period parameters */ +const N: usize = 624; +const M: usize = 397; +const MATRIX_A: u32 = 0x9908b0dfu32; /* constant vector a */ +const UPPER_MASK: u32 = 0x80000000u32; /* most significant w-r bits */ +const LOWER_MASK: u32 = 0x7fffffffu32; /* least significant r bits */ + +pub struct MT19937 { + mt: [u32; N], /* the array for the state vector */ + mti: usize, /* mti==N+1 means mt[N] is not initialized */ +} +impl Default for MT19937 { + fn default() -> Self { + MT19937 { + mt: [0; N], + mti: N + 1, + } + } +} +impl std::fmt::Debug for MT19937 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.pad("MT19937") + } +} + +impl MT19937 { + pub fn new_with_slice_seed(init_key: &[u32]) -> Self { + let mut state = Self::default(); + state.seed_slice(init_key); + state + } + + /* initializes self.mt[N] with a seed */ + fn seed(&mut self, s: u32) { + self.mt[0] = s & 0xffffffffu32; + self.mti = 1; + while self.mti < N { + self.mt[self.mti] = 1812433253u32 + .wrapping_mul(self.mt[self.mti - 1] ^ (self.mt[self.mti - 1] >> 30)) + + self.mti as u32; + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array self.mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + self.mt[self.mti] &= 0xffffffffu32; + /* for >32 bit machines */ + self.mti += 1; + } + } + + /* initialize by an array with array-length */ + /* init_key is the array for initializing keys */ + /* key_length is its length */ + /* slight change for C++, 2004/2/26 */ + pub fn seed_slice(&mut self, init_key: &[u32]) { + let mut i; + let mut j; + let mut k; + self.seed(19650218); + i = 1; + j = 0; + k = if N > init_key.len() { + N + } else { + init_key.len() + }; + while k != 0 { + self.mt[i] = (self.mt[i] + ^ ((self.mt[i - 1] ^ (self.mt[i - 1] >> 30)).wrapping_mul(1664525u32))) + + init_key[j] + + j as u32; /* non linear */ + self.mt[i] &= 0xffffffffu32; /* for WORDSIZE > 32 machines */ + i += 1; + j += 1; + if i >= N { + self.mt[0] = self.mt[N - 1]; + i = 1; + } + if j >= init_key.len() { + j = 0; + } + k -= 1; + } + k = N - 1; + while k != 0 { + self.mt[i] = (self.mt[i] + ^ ((self.mt[i - 1] ^ (self.mt[i - 1] >> 30)).wrapping_mul(1566083941u32))) + - i as u32; /* non linear */ + self.mt[i] &= 0xffffffffu32; /* for WORDSIZE > 32 machines */ + i += 1; + if i >= N { + self.mt[0] = self.mt[N - 1]; + i = 1; + } + k -= 1; + } + + self.mt[0] = 0x80000000u32; /* MSB is 1; assuring non-zero initial array */ + } + + /* generates a random number on [0,0xffffffff]-interval */ + fn gen_u32(&mut self) -> u32 { + let mut y: u32; + let mag01 = |x| if (x & 0x1) == 1 { MATRIX_A } else { 0 }; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if self.mti >= N { + /* generate N words at one time */ + + if self.mti == N + 1 + /* if seed() has not been called, */ + { + self.seed(5489u32); + } /* a default initial seed is used */ + + for kk in 0..N - M { + y = (self.mt[kk] & UPPER_MASK) | (self.mt[kk + 1] & LOWER_MASK); + self.mt[kk] = self.mt[kk + M] ^ (y >> 1) ^ mag01(y); + } + for kk in N - M..N - 1 { + y = (self.mt[kk] & UPPER_MASK) | (self.mt[kk + 1] & LOWER_MASK); + self.mt[kk] = self.mt[kk.wrapping_add(M.wrapping_sub(N))] ^ (y >> 1) ^ mag01(y); + } + y = (self.mt[N - 1] & UPPER_MASK) | (self.mt[0] & LOWER_MASK); + self.mt[N - 1] = self.mt[M - 1] ^ (y >> 1) ^ mag01(y); + + self.mti = 0; + } + + y = self.mt[self.mti]; + self.mti += 1; + + /* Tempering */ + y ^= y >> 11; + y ^= (y << 7) & 0x9d2c5680u32; + y ^= (y << 15) & 0xefc60000u32; + y ^= y >> 18; + + y + } +} + +impl rand::RngCore for MT19937 { + fn next_u32(&mut self) -> u32 { + self.gen_u32() + } + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_u32(self) + } + fn fill_bytes(&mut self, dest: &mut [u8]) { + rand_core::impls::fill_bytes_via_next(self, dest) + } + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + Ok(self.fill_bytes(dest)) + } +}