1+ use core:: sync:: atomic:: { AtomicUsize , Ordering } ;
2+ #[ cfg( feature = "std" ) ]
13use std:: {
24 cell:: Cell ,
35 collections:: hash_map:: DefaultHasher ,
46 hash:: Hasher ,
5- num:: Wrapping ,
6- sync:: atomic:: { AtomicUsize , Ordering } ,
77} ;
88
99// Based on [Fisher–Yates shuffle].
@@ -18,18 +18,15 @@ pub fn shuffle<T>(slice: &mut [T]) {
1818
1919/// Return a value from `0..n`.
2020fn gen_index ( n : usize ) -> usize {
21- ( random ( ) % n as u64 ) as usize
21+ random ( ) % n
2222}
2323
24- /// Pseudorandom number generator based on [xorshift* ].
24+ /// Pseudorandom number generator based on [xorshift].
2525///
26- /// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
27- fn random ( ) -> u64 {
28- thread_local ! {
29- static RNG : Cell <Wrapping <u64 >> = Cell :: new( Wrapping ( prng_seed( ) ) ) ;
30- }
31-
32- fn prng_seed ( ) -> u64 {
26+ /// [xorshift]: https://en.wikipedia.org/wiki/Xorshift
27+ fn random ( ) -> usize {
28+ #[ cfg( feature = "std" ) ]
29+ fn prng_seed ( ) -> usize {
3330 static COUNTER : AtomicUsize = AtomicUsize :: new ( 0 ) ;
3431
3532 // Any non-zero seed will do
@@ -39,16 +36,75 @@ fn random() -> u64 {
3936 hasher. write_usize ( COUNTER . fetch_add ( 1 , Ordering :: Relaxed ) ) ;
4037 seed = hasher. finish ( ) ;
4138 }
42- seed
39+ seed as usize
4340 }
4441
45- RNG . with ( |rng| {
46- let mut x = rng. get ( ) ;
47- debug_assert_ne ! ( x. 0 , 0 ) ;
42+ #[ cfg( not( feature = "std" ) ) ]
43+ const fn prng_seed ( ) -> usize {
44+ // A deterministic seed is used in absense of TLS
45+ 42
46+ }
47+
48+ /// [xorshift*] is used on 64bit platforms.
49+ ///
50+ /// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
51+ #[ cfg( target_pointer_width = "64" ) ]
52+ fn xorshift ( mut x : usize ) -> ( usize , usize ) {
53+ debug_assert_ne ! ( x, 0 ) ;
4854 x ^= x >> 12 ;
4955 x ^= x << 25 ;
5056 x ^= x >> 27 ;
51- rng. set ( x) ;
52- x. 0 . wrapping_mul ( 0x2545_f491_4f6c_dd1d )
53- } )
57+ (
58+ x,
59+ x. wrapping_mul ( 0x2545_f491_4f6c_dd1d ) ,
60+ )
61+ }
62+
63+ /// [xorshift32] is used on 32bit platforms.
64+ ///
65+ /// [xorshift32]: https://en.wikipedia.org/wiki/Xorshift
66+ #[ cfg( target_pointer_width = "32" ) ]
67+ fn xorshift ( mut x : usize ) -> ( usize , usize ) {
68+ debug_assert_ne ! ( x, 0 ) ;
69+ x ^= x << 13 ;
70+ x ^= x >> 17 ;
71+ x ^= x << 5 ;
72+ ( x, x)
73+ }
74+
75+ /// A non-standard xorshift variant is used on 16bit platforms.
76+ #[ cfg( target_pointer_width = "16" ) ]
77+ fn xorshift ( mut x : usize ) -> ( usize , usize ) {
78+ // Constants chosen from: http://b2d-f9r.blogspot.com/2010/08/16-bit-xorshift-rng.html
79+ debug_assert_ne ! ( x, 0 ) ;
80+ x ^= x << 4 ;
81+ x ^= x >> 3 ;
82+ x ^= x << 7 ;
83+ ( x, x)
84+ }
85+
86+ #[ cfg( feature = "std" ) ]
87+ fn rng ( ) -> usize {
88+ thread_local ! {
89+ static RNG : Cell <usize > = Cell :: new( prng_seed( ) ) ;
90+ }
91+
92+ RNG . with ( |rng| {
93+ let ( x, res) = xorshift ( rng. get ( ) ) ;
94+ rng. set ( x) ;
95+ res
96+ } )
97+ }
98+
99+ #[ cfg( not( feature = "std" ) ) ]
100+ fn rng ( ) -> usize {
101+ static RNG : AtomicUsize = AtomicUsize :: new ( prng_seed ( ) ) ;
102+
103+ // Preemption here can cause multiple threads to observe repeated state
104+ let ( x, res) = xorshift ( RNG . load ( Ordering :: Relaxed ) ) ;
105+ RNG . store ( x, Ordering :: Relaxed ) ;
106+ res
107+ }
108+
109+ rng ( )
54110}
0 commit comments