Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
93.18% |
41 / 44 |
|
66.67% |
6 / 9 |
CRAP | |
0.00% |
0 / 1 |
Alea | |
93.18% |
41 / 44 |
|
66.67% |
6 / 9 |
14.06 | |
0.00% |
0 / 1 |
__construct | |
96.00% |
24 / 25 |
|
0.00% |
0 / 1 |
6 | |||
random | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
uint32 | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
fract53 | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
version | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
args | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
exportState | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
importState | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
createWithState | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Alea; |
5 | |
6 | /** |
7 | * Alea non-cryptographic pseudo-random number generator. |
8 | * |
9 | * Compatible with https://www.npmjs.com/package/alea |
10 | * |
11 | * From http://baagoe.com/en/RandomMusings/javascript/ |
12 | * |
13 | * Archived at: |
14 | * * https://web.archive.org/web/20120619002808/http://baagoe.org/en/wiki/Better_random_numbers_for_javascript |
15 | * * https://web.archive.org/web/20120502223108/http://baagoe.com/en/RandomMusings/javascript/ |
16 | */ |
17 | class Alea { |
18 | |
19 | private $s0; |
20 | private $s1; |
21 | private $s2; |
22 | private $c; |
23 | private $args; |
24 | |
25 | /** |
26 | * Create a new pseudo-random number generator. |
27 | * @param mixed ...$args Seeds for the PRNG. Can be anything which can be |
28 | * converted to a string with 'strval'. |
29 | */ |
30 | public function __construct( ...$args ) { |
31 | // Johannes Baagøe <baagoe@baagoe.com>, 2010 |
32 | $s0 = 0; |
33 | $s1 = 0; |
34 | $s2 = 0; |
35 | $c = 1; |
36 | |
37 | if ( count( $args ) == 0 ) { |
38 | $args = [ gettimeofday( true ) ]; |
39 | } |
40 | $mash = new Mash(); |
41 | $s0 = $mash->mash( ' ' ); |
42 | $s1 = $mash->mash( ' ' ); |
43 | $s2 = $mash->mash( ' ' ); |
44 | |
45 | foreach ( $args as $a ) { |
46 | $s0 -= $mash->mash( $a ); |
47 | if ( $s0 < 0 ) { |
48 | $s0 += 1; |
49 | } |
50 | $s1 -= $mash->mash( $a ); |
51 | if ( $s1 < 0 ) { |
52 | $s1 += 1; |
53 | } |
54 | $s2 -= $mash->mash( $a ); |
55 | if ( $s2 < 0 ) { |
56 | $s2 += 1; |
57 | } |
58 | } |
59 | |
60 | $this->s0 = $s0; |
61 | $this->s1 = $s1; |
62 | $this->s2 = $s2; |
63 | $this->c = $c; |
64 | $this->args = $args; |
65 | } |
66 | |
67 | /** |
68 | * Get a float with 32 bits of randomness. |
69 | * @return float |
70 | */ |
71 | public function random(): float { |
72 | $t = 2091639 * $this->s0 + $this->c * 2.3283064365386963e-10; // 2^-32 |
73 | $this->s0 = $this->s1; |
74 | $this->s1 = $this->s2; |
75 | $this->c = (int)$t; |
76 | $this->s2 = $t - ( $this->c ); |
77 | return $this->s2; |
78 | } |
79 | |
80 | /** |
81 | * Get a random 32-bit unsigned integer. |
82 | * @return int |
83 | */ |
84 | public function uint32(): int { |
85 | return intval( $this->random() * 0x100000000 ); // 2^32 |
86 | } |
87 | |
88 | /** |
89 | * Get a float with the full 53 bits of randomness. |
90 | * @return float |
91 | */ |
92 | public function fract53(): float { |
93 | return $this->random() + |
94 | // Using `| 0` to get a truncating conversion to int32 is |
95 | // unconventional, but perfectly legit. |
96 | // @phan-suppress-next-line PhanTypeInvalidBitwiseBinaryOperator,PhanTypeInvalidLeftOperandOfBitwiseOp |
97 | ( $this->random() * 0x200000 | 0 ) * 1.1102230246251565e-16; // 2^-53 |
98 | } |
99 | |
100 | public static function version(): string { |
101 | return 'Alea 1.0'; |
102 | } |
103 | |
104 | public function args(): array { |
105 | return $this->args; |
106 | } |
107 | |
108 | // coverslide's additions to sync state between two generators |
109 | |
110 | /** |
111 | * @return array The exported state of this PRNG. |
112 | */ |
113 | public function exportState(): array { |
114 | return [ $this->s0, $this->s1, $this->s2, $this->c ]; |
115 | } |
116 | |
117 | /** |
118 | * @param array $i The exported state of some other Alea PRNG. |
119 | */ |
120 | public function importState( array $i ): void { |
121 | $this->s0 = $i[ 0 ]; |
122 | $this->s1 = $i[ 1 ]; |
123 | $this->s2 = $i[ 2 ]; |
124 | $this->c = $i[ 3 ]; |
125 | } |
126 | |
127 | /** |
128 | * Create a new generator synced with some exported state. |
129 | * |
130 | * @param array $i The exported state of some other Alea PRNG. |
131 | * @return Alea a new Alea PRNG. |
132 | */ |
133 | public static function createWithState( array $i ): Alea { |
134 | $random = new Alea(); |
135 | $random->importState( $i ); |
136 | return $random; |
137 | } |
138 | } |