// Copyright 2020 The XLS Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Port of "ACM random" number generator to DSLX, similar to // https://github.com/google/or-tools/blob/66b8d230798f9b8a3c98c26a997daf04974400b8/ortools/base/random.cc#L35 // // DO NOT use ACM random for any application where security -- // unpredictability of subsequent output and previous output -- is // needed. ACMRandom is in *NO* *WAY* a cryptographically secure // pseudorandom number generator, and using it where recipients of its // output may wish to guess earlier/later output values would be very // bad. import std; const M = u32:2147483647; struct State { seed: u32 } // Returns a pseudo-random number in the range [1, 2^31-2]. // // Note that this is one number short on both ends of the full range of // non-negative 32-bit integers, which range from 0 to 2^31-1. pub fn rng_next(s: State) -> (State, u32) { const A = u64:16807; let product = (s.seed as u64) * A; let new_seed = ((product >> u64:31) + (product & (M as u64))) as u32; let new_seed = if std::sgt(new_seed, M) { new_seed - M } else { new_seed }; (State { seed: new_seed }, new_seed) } // Returns a pseudo random number in the range [1, (2^31-2)^2]. // Note that this does not cover all non-negative values of int64, which range // from 0 to 2^63-1. The top two bits are ALWAYS ZERO. pub fn rng_next64(s: State) -> (State, u64) { let (s, next0) = rng_next(s); let (s, next1) = rng_next(s); let result: s64 = ((next0 as s64) - s64:1) * (((M as s64) - s64:1) as s64) + (next1 as s64); (s, result as u64) } // Returns a fixed seed for use in the random number generator. pub fn rng_deterministic_seed() -> u32 { u32:301 } // Create the state for a new random number generator using the given seed. fn rng_sanitize_seed(seed: u32) -> u32 { let seed = seed & u32:0x7fffffff; match seed { u32:0 | M => u32:1, _ => seed, } } pub fn rng_new(seed: u32) -> State { State { seed: rng_sanitize_seed(seed) } } #[test] fn rng_next_test() { let r = rng_new(rng_deterministic_seed()); const EXPECTED = u64[20]:[ 0x002698ad4b48ead0, 0x1bfb1e0316f2d5de, 0x173a623c9725b477, 0x0a447a02823ad868, 0x1df74948b3fbea7e, 0x1bc8b594bcf01a39, 0x07b767ca9520e99a, 0x05e28b4320bfd20e, 0x0105906a24823f57, 0x1a1e7d14a6d24384, 0x2a7326df322e084d, 0x120bc9cc3fac4ec7, 0x2c8f193a1b46a9c5, 0x2b9c95743bbe3f90, 0x0dcfc5b1d0398b46, 0x006ba47b3448bea3, 0x3fe4fbf9a522891b, 0x23e1a50ad6aebca3, 0x1b263d39ea62be44, 0x13581d282e643b0e, ]; for (i, r): (u8, State) in u8:0..u8:20 { let (new_r, got) = rng_next64(r); let want = EXPECTED[i]; assert_eq(want, got); new_r }(r); }