Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.12% covered (success)
95.12%
39 / 41
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Alea
95.12% covered (success)
95.12%
39 / 41
77.78% covered (warning)
77.78%
7 / 9
14
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
6
 random
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 uint32
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fract53
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 version
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 args
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 exportState
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 importState
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 createWithState
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare( strict_types = 1 );
3
4namespace 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 */
17class Alea {
18
19    private float $s0;
20    private float $s1;
21    private float $s2;
22    private float $c;
23    private array $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        $c = 1;
33
34        if ( count( $args ) == 0 ) {
35            $args = [ gettimeofday( true ) ];
36        }
37        $mash = new Mash();
38        $s0 = $mash->mash( ' ' );
39        $s1 = $mash->mash( ' ' );
40        $s2 = $mash->mash( ' ' );
41
42        foreach ( $args as $a ) {
43            $s0 -= $mash->mash( $a );
44            if ( $s0 < 0 ) {
45                $s0 += 1;
46            }
47            $s1 -= $mash->mash( $a );
48            if ( $s1 < 0 ) {
49                $s1 += 1;
50            }
51            $s2 -= $mash->mash( $a );
52            if ( $s2 < 0 ) {
53                $s2 += 1;
54            }
55        }
56
57        $this->s0 = $s0;
58        $this->s1 = $s1;
59        $this->s2 = $s2;
60        $this->c = $c;
61        $this->args = $args;
62    }
63
64    /**
65     * Get a float with 32 bits of randomness.
66     * @return float
67     */
68    public function random(): float {
69        $t = 2091639 * $this->s0 + $this->c * 2.3283064365386963e-10; // 2^-32
70        $this->s0 = $this->s1;
71        $this->s1 = $this->s2;
72        $this->c = (int)$t;
73        $this->s2 = $t - ( $this->c );
74        return $this->s2;
75    }
76
77    /**
78     * Get a random 32-bit unsigned integer.
79     * @return int
80     */
81    public function uint32(): int {
82        return intval( $this->random() * 0x100000000 ); // 2^32
83    }
84
85    /**
86     * Get a float with the full 53 bits of randomness.
87     * @return float
88     */
89    public function fract53(): float {
90        return $this->random() +
91            // FIXME truncation gives php deprecation notice: Implicit conversion from float to int loses precision
92            // TODO Enable displayDetailsOnTestsThatTriggerDeprecations in phpunit.xml.dist when fixed
93            // Using `| 0` to get a truncating conversion to int32 is
94            // unconventional, but perfectly legit.
95            // @phan-suppress-next-line PhanTypeInvalidBitwiseBinaryOperator,PhanTypeInvalidLeftOperandOfBitwiseOp
96            ( $this->random() * 0x200000 | 0 ) * 1.1102230246251565e-16; // 2^-53
97    }
98
99    public static function version(): string {
100        return 'Alea 1.0';
101    }
102
103    public function args(): array {
104        return $this->args;
105    }
106
107    // coverslide's additions to sync state between two generators
108
109    /**
110     * @return array The exported state of this PRNG.
111     */
112    public function exportState(): array {
113        return [ $this->s0, $this->s1, $this->s2, $this->c ];
114    }
115
116    /**
117     * @param array $i The exported state of some other Alea PRNG.
118     */
119    public function importState( array $i ): void {
120        $this->s0 = $i[ 0 ];
121        $this->s1 = $i[ 1 ];
122        $this->s2 = $i[ 2 ];
123        $this->c = $i[ 3 ];
124    }
125
126    /**
127     * Create a new generator synced with some exported state.
128     *
129     * @param array $i The exported state of some other Alea PRNG.
130     * @return Alea a new Alea PRNG.
131     */
132    public static function createWithState( array $i ): Alea {
133        $random = new Alea();
134        $random->importState( $i );
135        return $random;
136    }
137}