squares_rnd/
lib.rs

1//!Simple and fast counter based non-crypto random generator.
2//!
3//!The algorithm is based on `Middle Square Weyl Sequence RNG`.
4//!See [paper](https://arxiv.org/abs/2004.06278v7) for details.
5//!
6//!**NOTE**: Not cryptographically secure.
7//!
8//!There are several note-worthy properties to the algorithm:
9//!
10//!- State is represented by counter, which is incremented to produce new value, hence making
11//!it easy to predict how state would change.
12//!- The code is short and simple, only taking minimum amount of operations to produce uniform output.
13//!- `key` must have close to equal number of zeroes and ones for optimal output.
14//!This crate provides single key for use, to have more download key file [gist](https://gist.githubusercontent.com/DoumanAsh/a57bc65434702d5d7fb88343c65f3145/raw/a9b45f7155c483f689318ee501222e72be0d66ec/keys)
15
16#![no_std]
17#![allow(clippy::style)]
18#![warn(missing_docs)]
19
20use core::sync::atomic::{AtomicU64, Ordering};
21
22///Default key to be used with algorithm
23pub const KEY: u64 = 0x5d8491e219f6537d;
24
25pub mod distr;
26
27#[inline]
28///Generates random `u32`
29///
30///- `counter` - Integer counter which acts as state. Should be increased to generate new
31///number.
32///- `key` - Integer which in general should be irregular bit pattern with approximately equal
33///number of zeros and ones. Generally should be constant, but can be changed when new range of
34///random numbers is required.
35pub const fn rand32(counter: u64, key: u64) -> u32 {
36    let mut x = counter.wrapping_mul(key);
37    let y = x;
38    let z = y.wrapping_add(key);
39
40    x = x.wrapping_mul(x).wrapping_add(y);
41    x = (x >> 32) | (x << 32);
42
43    x = x.wrapping_mul(x).wrapping_add(z);
44    x = (x >> 32) | (x << 32);
45
46    x = x.wrapping_mul(x).wrapping_add(y);
47    x = (x >> 32) | (x << 32);
48
49    (x.wrapping_mul(x).wrapping_add(z) >> 32) as u32
50}
51
52#[inline]
53///Generates random `u64`
54///
55///- `counter` - Integer counter which acts as state. Should be increased to generate new
56///number.
57///- `key` - Integer which in general should be irregular bit pattern with approximately equal
58///number of zeros and ones. Generally should be constant, but can be changed when new range of
59///random numbers is required.
60pub const fn rand64(counter: u64, key: u64) -> u64 {
61    let mut x = counter.wrapping_mul(key);
62    let y = x;
63    let z = y.wrapping_add(key);
64
65    x = x.wrapping_mul(x).wrapping_add(y);
66    x = (x >> 32) | (x << 32);
67
68    x = x.wrapping_mul(x).wrapping_add(z);
69    x = (x >> 32) | (x << 32);
70
71    x = x.wrapping_mul(x).wrapping_add(y);
72    x = (x >> 32) | (x << 32);
73
74    x = x.wrapping_mul(x).wrapping_add(z);
75    let t = x;
76    x = (x >> 32) | (x << 32);
77
78    t ^ (x.wrapping_mul(x).wrapping_add(y) >> 32)
79}
80
81
82///Full rand result
83pub struct RandRes<T> {
84    ///Counter, used to generate `value`
85    pub counter: u64,
86    ///Value generated with `counter.
87    pub value: T
88}
89
90#[derive(Debug)]
91///Stateful representation of algorithm.
92///
93///Increments counter on each generation using the same key.
94pub struct Rand {
95    counter: AtomicU64,
96    key: u64,
97}
98
99impl Rand {
100    #[inline(always)]
101    ///Creates new instance with provided key.
102    pub const fn new(key: u64) -> Self {
103        Self::with_counter(0, key)
104    }
105
106    #[inline]
107    ///Creates new instance with provided key and initial value of counter.
108    pub const fn with_counter(counter: u64, key: u64) -> Self {
109        Self {
110            counter: AtomicU64::new(counter),
111            key,
112        }
113    }
114
115    #[inline]
116    ///Sets new counter value, returning old one
117    pub fn set_counter(&self, counter: u64) -> u64 {
118        self.counter.swap(counter, Ordering::AcqRel)
119    }
120
121    #[inline]
122    ///Gets current value of counter
123    pub fn counter(&self) -> u64 {
124        self.counter.load(Ordering::Acquire)
125    }
126
127    #[inline]
128    ///Generates new `u32` together with corresponding counter value
129    pub fn next_full_u32(&self) -> RandRes<u32> {
130        let counter = self.counter.fetch_add(1, Ordering::AcqRel);
131        RandRes {
132            counter,
133            value: rand32(counter, self.key)
134        }
135    }
136
137    #[inline]
138    ///Generates new `u32`
139    pub fn next_u32(&self) -> u32 {
140        rand32(self.counter.fetch_add(1, Ordering::AcqRel), self.key)
141    }
142
143    #[inline]
144    ///Generates new `u32` in range `0..to`
145    pub fn next_u32_up(&self, to: u32) -> u32 {
146        #[inline(always)]
147        fn mul_high_u32(a: u32, b: u32) -> u32 {
148            (((a as u64) * (b as u64)) >> 32) as u32
149        }
150
151        //https://lemire.me/blog/2016/06/30/fast-random-shuffling/
152        let mut result = self.next_u32();
153        let mut hi = mul_high_u32(result, to);
154        let mut lo = result.wrapping_mul(to);
155
156        if lo < to {
157            while lo < (to.wrapping_neg() % to) {
158                result = self.next_u32();
159                hi = mul_high_u32(result, to);
160                lo = result.wrapping_mul(to);
161            }
162        }
163
164        hi
165    }
166
167    #[inline]
168    ///Generates new `u64` together with corresponding counter value
169    pub fn next_full_u64(&self) -> RandRes<u64> {
170        let counter = self.counter.fetch_add(1, Ordering::AcqRel);
171        RandRes {
172            counter,
173            value: rand64(counter, self.key)
174        }
175    }
176
177    #[inline]
178    ///Generates new `u64`
179    pub fn next_u64(&self) -> u64 {
180        rand64(self.counter.fetch_add(1, Ordering::AcqRel), self.key)
181    }
182
183    #[inline]
184    ///Generates new `u64` in range `0..to`
185    pub fn next_u64_up(&self, to: u64) -> u64 {
186        #[inline(always)]
187        fn mul_high_u64(a: u64, b: u64) -> u64 {
188            (((a as u128) * (b as u128)) >> 64) as u64
189        }
190
191        //https://lemire.me/blog/2016/06/30/fast-random-shuffling/
192        let mut result = self.next_u64();
193        let mut hi = mul_high_u64(result, to);
194        let mut lo = result.wrapping_mul(to);
195
196        if lo < to {
197            while lo < (to.wrapping_neg() % to) {
198                result = self.next_u64();
199                hi = mul_high_u64(result, to);
200                lo = result.wrapping_mul(to);
201            }
202        }
203
204        hi
205    }
206}
207
208impl Default for Rand {
209    #[inline(always)]
210    fn default() -> Self {
211        Self::new(KEY)
212    }
213}