Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.45% covered (success)
95.45%
42 / 44
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Alea
95.45% covered (success)
95.45%
42 / 44
77.78% covered (warning)
77.78%
7 / 9
14
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
25 / 25
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 $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}