dotnet35_rand_rs/lib.rs
1//! 一个模拟 .NET 3.5 的 Random 类的库
2//!
3//! A library for simulating the .NET 3.5 Random class
4//!
5//! 用法 Usage:
6//! ```rust
7//! use dotnet35_rand_rs::DotNet35Random;
8//!
9//! let mut rand = DotNet35Random::new(0);
10//! println!("{}", rand.next());
11//!
12//! ```
13//!
14//! by shenjackyuanjie
15
16use std::default::Default;
17use std::time::{SystemTime, UNIX_EPOCH};
18
19/// .NET 3.5 的 Random 类的常量
20/// Constants of .NET 3.5 Random class
21///
22/// "万一你真需要修改常量呢?"
23///
24/// "What if you really need to modify the constants?"
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub struct DotNet35Const {
27 pub mz: i32,
28 pub mbig: i32,
29 pub mseed: i32,
30}
31
32impl DotNet35Const {
33 /// 默认的种子
34 pub const DEFAULT_MSEED: i32 = 161803398;
35
36 /// "万一你真需要修改常量呢?"
37 ///
38 /// "What if you really need to modify the constants?"
39 pub const fn new(mz: i32, mbig: i32, mseed: i32) -> Self {
40 Self { mz, mbig, mseed }
41 }
42
43 pub const fn new_default() -> Self {
44 Self {
45 mz: 0,
46 mbig: i32::MAX,
47 mseed: Self::DEFAULT_MSEED,
48 }
49 }
50}
51
52impl Default for DotNet35Const {
53 /// 默认值
54 ///
55 /// default value
56 fn default() -> Self {
57 Self::new_default()
58 }
59}
60
61/// 用于模拟 .NET 3.5 的 Random 类
62/// A struct for simulating the .NET 3.5 Random class
63///
64/// 所有的方法都是公开的, 以防万一 (点名批评某个模拟Java random的库)
65///
66/// All methods are public, just in case you need it
67///
68/// 用法 Usage:
69/// ```rust
70/// use dotnet35_rand_rs::DotNet35Random;
71///
72/// let mut rand = DotNet35Random::new(0);
73/// println!("{}", rand.next());
74/// ```
75///
76/// 参考源码 Reference source code:
77/// 感谢 @DoubleUTH 提供的微软源码的链接
78/// Thanks to @DoubleUTH for providing the link to the Microsoft source code
79///
80/// 请注意, 这里的源码进行了二次格式化, 看着好看一些, 其余没有修改
81/// Notice: The source code here has been formatted twice to make it look better, and the rest has not been modified
82/// https://github.com/microsoft/referencesource/blob/51cf7850defa8a17d815b4700b67116e3fa283c2/mscorlib/system/random.cs
83/// ```csharp
84/// // ==++==
85/// //
86/// // Copyright (c) Microsoft Corporation. All rights reserved.
87/// //
88/// // ==--==
89/// /*============================================================
90/// **
91/// ** Class: Random
92/// **
93/// **
94/// ** Purpose: A random number generator.
95/// **
96/// **
97/// ===========================================================*/
98/// namespace System
99/// {
100/// using System;
101/// using System.Runtime;
102/// using System.Runtime.CompilerServices;
103/// using System.Globalization;
104/// using System.Diagnostics.Contracts;
105/// [System.Runtime.InteropServices.ComVisible(true)]
106/// [Serializable]
107/// public class Random
108/// {
109/// //
110/// // Private Constants
111/// //
112/// private const int MBIG = Int32.MaxValue;
113/// private const int MSEED = 161803398;
114/// private const int MZ = 0;
115/// //
116/// // Member Variables
117/// //
118/// private int inext;
119/// private int inextp;
120/// private int[] SeedArray = new int[56];
121/// //
122/// // Constructors
123/// //
124///
125/// public Random()
126/// : this(Environment.TickCount)
127/// {
128/// }
129/// public Random(int Seed)
130/// {
131/// int ii;
132/// int mj, mk;
133///
134/// //Initialize our Seed array.
135/// //This algorithm comes from Numerical Recipes in C (2nd Ed.)
136/// int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
137/// mj = MSEED - subtraction;
138/// SeedArray[55] = mj;
139/// mk = 1;
140/// for (int i = 1; i < 55; i++)
141/// { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
142/// ii = (21 * i) % 55;
143/// SeedArray[ii] = mk;
144/// mk = mj - mk;
145/// if (mk < 0) mk += MBIG;
146/// mj = SeedArray[ii];
147/// }
148/// for (int k = 1; k < 5; k++)
149/// {
150/// for (int i = 1; i < 56; i++)
151/// {
152/// SeedArray[i] -= SeedArray[1 + (i + 30) % 55];
153/// if (SeedArray[i] < 0) SeedArray[i] += MBIG;
154/// }
155/// }
156/// inext = 0;
157/// inextp = 21;
158/// Seed = 1;
159/// }
160/// //
161/// // Package Private Methods
162/// //
163/// /*====================================Sample====================================
164/// **Action: Return a new random number [0..1) and reSeed the Seed array.
165/// **Returns: A double [0..1)
166/// **Arguments: None
167/// **Exceptions: None
168/// ==============================================================================*/
169/// protected virtual double Sample()
170/// {
171/// //Including this division at the end gives us significantly improved
172/// //random number distribution.
173/// return (InternalSample() * (1.0 / MBIG));
174/// }
175/// private int InternalSample()
176/// {
177/// int retVal;
178/// int locINext = inext;
179/// int locINextp = inextp;
180///
181/// if (++locINext >= 56) locINext = 1;
182/// if (++locINextp >= 56) locINextp = 1;
183///
184/// retVal = SeedArray[locINext] - SeedArray[locINextp];
185///
186/// if (retVal == MBIG) retVal--;
187/// if (retVal < 0) retVal += MBIG;
188///
189/// SeedArray[locINext] = retVal;
190///
191/// inext = locINext;
192/// inextp = locINextp;
193///
194/// return retVal;
195/// }
196/// //
197/// // Public Instance Methods
198/// //
199/// /*=====================================Next=====================================
200/// **Returns: An int [0..Int32.MaxValue)
201/// **Arguments: None
202/// **Exceptions: None.
203/// ==============================================================================*/
204/// public virtual int Next()
205/// {
206/// return InternalSample();
207/// }
208/// private double GetSampleForLargeRange()
209/// {
210/// // The distribution of double value returned by Sample
211/// // is not distributed well enough for a large range.
212/// // If we use Sample for a range [Int32.MinValue..Int32.MaxValue)
213/// // We will end up getting even numbers only.
214///
215/// int result = InternalSample();
216/// // Note we can't use addition here. The distribution will be bad if we do that.
217/// bool negative = (InternalSample() % 2 == 0) ? true : false; // decide the sign based on second sample
218/// if (negative)
219/// {
220/// result = -result;
221/// }
222/// double d = result;
223/// d += (Int32.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
224/// d /= 2 * (uint)Int32.MaxValue - 1;
225/// return d;
226/// }
227/// /*=====================================Next=====================================
228/// **Returns: An int [minvalue..maxvalue)
229/// **Arguments: minValue -- the least legal value for the Random number.
230/// ** maxValue -- One greater than the greatest legal return value.
231/// **Exceptions: None.
232/// ==============================================================================*/
233/// public virtual int Next(int minValue, int maxValue)
234/// {
235/// if (minValue > maxValue)
236/// {
237/// throw new ArgumentOutOfRangeException("minValue", Environment.GetResourceString("Argument_MinMaxValue", "minValue", "maxValue"));
238/// }
239/// Contract.EndContractBlock();
240///
241/// long range = (long)maxValue - minValue;
242/// if (range <= (long)Int32.MaxValue)
243/// {
244/// return ((int)(Sample() * range) + minValue);
245/// }
246/// else
247/// {
248/// return (int)((long)(GetSampleForLargeRange() * range) + minValue);
249/// }
250/// }
251/// /*=====================================Next=====================================
252/// **Returns: An int [0..maxValue)
253/// **Arguments: maxValue -- One more than the greatest legal return value.
254/// **Exceptions: None.
255/// ==============================================================================*/
256/// public virtual int Next(int maxValue)
257/// {
258/// if (maxValue < 0)
259/// {
260/// throw new ArgumentOutOfRangeException("maxValue", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", "maxValue"));
261/// }
262/// Contract.EndContractBlock();
263/// return (int)(Sample() * maxValue);
264/// }
265/// /*=====================================Next=====================================
266/// **Returns: A double [0..1)
267/// **Arguments: None
268/// **Exceptions: None
269/// ==============================================================================*/
270/// public virtual double NextDouble()
271/// {
272/// return Sample();
273/// }
274/// /*==================================NextBytes===================================
275/// **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled.
276/// **Returns:Void
277/// **Arugments: buffer -- the array to be filled.
278/// **Exceptions: None
279/// ==============================================================================*/
280/// public virtual void NextBytes(byte[] buffer)
281/// {
282/// if (buffer == null) throw new ArgumentNullException("buffer");
283/// Contract.EndContractBlock();
284/// for (int i = 0; i < buffer.Length; i++)
285/// {
286/// buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1));
287/// }
288/// }
289/// }
290/// }
291/// ```
292#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
293pub struct DotNet35Random {
294 /// private int inext;
295 pub inext: usize,
296 /// private int inextp;
297 pub inextp: usize,
298 /// private int[] SeedArray = new int[56];
299 pub seed_array: [i32; 56],
300 /// consts
301 pub consts: DotNet35Const,
302 /// seed
303 pub seed: i32,
304}
305
306impl DotNet35Random {
307 /// 默认的构造函数
308 ///
309 /// default constructor
310 pub fn new(seed: i32) -> Self {
311 let consts = DotNet35Const::default();
312 DotNet35Random::new_with_const(seed, consts)
313 }
314
315 /// `public DotNet35Random() : this(Environment.TickCount)`
316 ///
317 /// 用当前时间作为种子的构造函数
318 /// Constructor with current time as seed
319 pub fn new_now() -> Self {
320 let consts = DotNet35Const::default();
321 // Environment.TickCount
322
323 let start = SystemTime::now();
324 let since_the_epoch = start
325 .duration_since(UNIX_EPOCH)
326 .unwrap_or(std::time::Duration::new(0, 0));
327 let tick_count = since_the_epoch.as_millis() as i32;
328 DotNet35Random::new_with_const(tick_count, consts)
329 }
330
331 /// "万一你真需要修改常量呢?"
332 ///
333 /// "What if you really need to modify the constants?"
334 pub fn new_with_const(seed: i32, consts: DotNet35Const) -> Self {
335 let mut seed_array = [0; 56];
336 let subtraction = if seed == i32::MIN {
337 i32::MAX
338 } else {
339 seed.abs()
340 };
341 let mut mj: i32 = consts.mseed - subtraction;
342 seed_array[55] = mj;
343 let mut mk = 1;
344
345 for i in 1..55 {
346 let ii = 21 * i % 55;
347 seed_array[ii] = mk;
348
349 // overflow warning! use wrapping_sub
350 mk = mj.wrapping_sub(mk);
351 if mk < 0 {
352 // 可能导致算数溢出, 使用 wrapping_add
353 mk = mk.wrapping_add(consts.mbig);
354 }
355 mj = seed_array[ii];
356 }
357 for _ in 1..5 {
358 for i in 1..56 {
359 // 可能导致算数溢出, 使用 wrapping_sub 和 wrapping_add
360 seed_array[i] = seed_array[i].wrapping_sub(seed_array[1 + (i + 30) % 55]);
361 if seed_array[i] < 0 {
362 seed_array[i] = seed_array[i].wrapping_add(consts.mbig);
363 }
364 }
365 }
366 Self {
367 inext: 0,
368 inextp: 31,
369 seed_array,
370 consts,
371 seed,
372 }
373 }
374
375 /// `private double GetSampleForLargeRange()`
376 ///
377 /// 原始的 GetSampleForLargeRange 方法
378 /// original GetSampleForLargeRange method
379 ///
380 /// 源码注释翻译:
381 /// Sample 返回的 double 值的分布对于大范围来说不够好
382 /// 如果我们使用 Sample 来生成 [Int32.MinValue..Int32.MaxValue) 的范围
383 /// 我们将只会得到偶数
384 ///
385 /// Source code comment:
386 /// The distribution of double value returned by Sample
387 /// is not distributed well enough for a large range.
388 /// if we use Sample for a range [Int32.MinValue..Int32.MaxValue)
389 /// we will end up getting even numbers only.
390 pub fn get_sample_for_large_range(&mut self) -> f64 {
391 let result = self.internal_sample();
392 // 注意, 我们不能使用加法. 如果我们这么做, 分布将会很糟糕
393 let negative = self.internal_sample() % 2 == 0;
394 // 根据第二个样本决定符号
395 let result = if negative { -result } else { result };
396 let mut d = result as f64;
397 d += (i32::MAX - 1) as f64;
398 // 获取一个范围在 [0 .. 2 * Int32MaxValue - 1) 的数字
399 // 我专门去查了一下 C# 的运算符优先级来写的这一行代码
400 // I specially checked the operator priority of C# to write this line of code
401 d /= ((i32::MAX as u32 * 2) - 1) as f64;
402 d
403 }
404
405 /// `protected virtual double Sample()`
406 ///
407 /// 原始的 Sample 方法
408 /// original Sample method
409 pub fn sample(&mut self) -> f64 {
410 self.internal_sample() as f64 * (1.0 / self.consts.mbig as f64)
411 }
412
413 /// `private int InternalSample()`
414 ///
415 /// 原始的 InternalSample 方法
416 /// original InternalSample method
417 pub fn internal_sample(&mut self) -> i32 {
418 self.inext += 1;
419 if self.inext >= 56 {
420 self.inext = 1;
421 }
422 self.inextp += 1;
423 if self.inextp >= 56 {
424 self.inextp = 1;
425 }
426 let mut ret_val = self.seed_array[self.inext] - self.seed_array[self.inextp];
427 if ret_val == self.consts.mbig {
428 ret_val -= 1;
429 }
430 if ret_val < 0 {
431 ret_val += i32::MAX;
432 }
433 self.seed_array[self.inext] = ret_val;
434 ret_val
435 }
436
437 /// `public virtual int Next()`
438 ///
439 /// 默认的 Next 方法
440 /// default Next method
441 #[allow(clippy::should_implement_trait)]
442 pub fn next(&mut self) -> i32 {
443 self.internal_sample()
444 }
445
446 /// `public virtual int Next(int maxValue)`
447 ///
448 /// 限制最大值的 Next 方法
449 /// Next method with max value
450 pub fn next_with_max(&mut self, max_value: i32) -> Option<i32> {
451 if max_value < self.consts.mz {
452 None
453 } else {
454 Some((self.sample() * max_value as f64) as i32)
455 }
456 }
457
458 /// `public virtual int Next(int minValue, int maxValue)`
459 ///
460 /// 限制最大值和最小值的 Next 方法
461 /// Next method with max value and min value
462 pub fn next_in_range(&mut self, min_value: i32, max_value: i32) -> Option<i32> {
463 if min_value > max_value {
464 return None;
465 }
466 let range = max_value as i64 - min_value as i64;
467 if range <= i32::MAX as i64 {
468 Some((self.sample() * range as f64) as i32 + min_value)
469 } else {
470 Some((self.get_sample_for_large_range() * range as f64) as i32 + min_value)
471 }
472 }
473
474 /// `public virtual void NextBytes(byte[] buffer)`
475 ///
476 /// 返回随机字节的 NextBytes 方法
477 /// NextBytes method with random bytes
478 pub fn next_bytes(&mut self, buffer: &mut [u8]) {
479 (0..buffer.len()).for_each(|i| {
480 buffer[i] = (self.internal_sample() % u8::MAX as i32) as u8;
481 });
482 }
483
484 /// `public virtual double NextDouble()`
485 ///
486 /// 返回随机浮点数的 NextDouble 方法
487 /// NextDouble method with random double
488 pub fn next_double(&mut self) -> f64 {
489 self.sample()
490 }
491
492 /// 更 rusty 一些的 next_f64 方法
493 /// more rusty next_f64 method
494 pub fn next_f64(&mut self) -> f64 {
495 self.sample()
496 }
497
498 /// 更 rusty 一些的 next_i32 方法
499 /// more rusty next_i32 method
500 pub fn next_i32(&mut self) -> i32 {
501 self.internal_sample()
502 }
503
504 /// 更 rusty 一些的 next_u32 方法
505 /// more rusty next_u32 method
506 pub fn next_u32(&mut self) -> u32 {
507 self.internal_sample() as u32
508 }
509
510 pub fn next_u8_vec(&mut self, len: usize) -> Vec<u8> {
511 let mut vec = Vec::with_capacity(len);
512 for _ in 0..len {
513 vec.push((self.internal_sample() % u8::MAX as i32) as u8);
514 }
515 vec
516 }
517}
518
519#[test]
520fn create() {
521 let mut rand = DotNet35Random::new(0);
522 assert_eq!(rand.next(), 1976681210);
523 assert_eq!(rand.next(), 551155468);
524}
525
526#[test]
527fn verify() {
528 let mut rand = DotNet35Random::new(1919810);
529 assert_eq!(rand.next(), 429045588);
530 assert_eq!(rand.next(), 1732108734);
531 assert_eq!(rand.next(), 970222201);
532 assert_eq!(rand.next(), 369077841);
533 assert_eq!(rand.next_double(), 0.11859490215712921);
534 assert_eq!(rand.next(), 298877408);
535}
536
537#[test]
538fn create_now() {
539 let mut rand = DotNet35Random::new_now();
540 for _ in 0..100 {
541 assert!(rand.next_with_max(1000).unwrap() < 1000);
542 let next = rand.next_in_range(1000, 2000).unwrap();
543 assert!((1000..2000).contains(&next));
544 }
545}