-
Notifications
You must be signed in to change notification settings - Fork 1
/
isaac_unroll.php
146 lines (123 loc) · 5.37 KB
/
isaac_unroll.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?php
/*
------------------------------------------------------------------------------
ISAAC random number generator by Bob Jenkins. PHP port by Ilmari Karonen.
Based on the randport.c and readable.c reference C implementations by Bob
Jenkins, with some inspiration taken from the Perl port by John L. Allen.
This version of the code has an unrolled inner loop for a modest speed-up
(around 10% to 20% according to my benchmarks). Of course, it's still
incredibly slow compared to the C implementation.
This code is released into the public domain. Do whatever you want with it.
The latest version of this code can be found at https://github.com/vyznev/ISAAC-PHP.
HISTORY:
2013-01-20: Initial version.
2017-09-21: Moved to GitHub, comments tweaked.
------------------------------------------------------------------------------
*/
class ISAAC {
private $m, $a, $b, $c; // internal state
public $r; // current chunk of results
public function isaac()
{
$c = ++$this->c; // c gets incremented once per 256 results
$b = $this->b += $c; // then combined with b
$a = $this->a;
$m =& $this->m;
$r = array();
for ($i = 0; $i < 256; ++$i) {
// unroll 0:
$x = $m[$i];
$a ^= ($a << 13);
$a += $m[$i ^ 128];
$m[$i] = $y = 0xffffffff & ($m[($x >> 2) & 255] + $a + $b);
$r[$i] = $b = 0xffffffff & ($m[($y >> 10) & 255] + $x);
// unroll 1:
$x = $m[++$i];
$a ^= ($a >> 6) & 0x03ffffff;
$a += $m[$i ^ 128];
$m[$i] = $y = 0xffffffff & ($m[($x >> 2) & 255] + $a + $b);
$r[$i] = $b = 0xffffffff & ($m[($y >> 10) & 255] + $x);
// unroll 2:
$x = $m[++$i];
$a ^= ($a << 2);
$a += $m[$i ^ 128];
$m[$i] = $y = 0xffffffff & ($m[($x >> 2) & 255] + $a + $b);
$r[$i] = $b = 0xffffffff & ($m[($y >> 10) & 255] + $x);
// unroll 3:
$x = $m[++$i];
$a ^= ($a >> 16) & 0x0000ffff;
$a += $m[$i ^ 128];
$m[$i] = $y = 0xffffffff & ($m[($x >> 2) & 255] + $a + $b);
$r[$i] = $b = 0xffffffff & ($m[($y >> 10) & 255] + $x);
// avoid weird overflow behavior on 64-bit PHP
$a &= 0xffffffff;
}
$this->a = $a;
$this->b = $b;
$this->c = $c;
$this->r = $r;
}
public function rand()
{
if (empty($this->r)) $this->isaac();
return array_pop($this->r);
}
private static function mix( &$a, &$b, &$c, &$d, &$e, &$f, &$g, &$h )
{
$a ^= ($b << 11); $d += $a; $b += $c;
$b ^= ($c >> 2) & 0x3fffffff; $e += $b; $c += $d;
$c ^= ($d << 8); $f += $c; $d += $e;
$d ^= ($e >> 16) & 0x0000ffff; $g += $d; $e += $f;
$e ^= ($f << 10); $h += $e; $f += $g;
$f ^= ($g >> 4) & 0x0fffffff; $a += $f; $g += $h;
$g ^= ($h << 8); $b += $g; $h += $a;
$h ^= ($a >> 9) & 0x007fffff; $c += $h; $a += $b;
// 64-bit PHP does something weird on integer overflow; avoid it
$a &= 0xffffffff; $b &= 0xffffffff; $c &= 0xffffffff; $d &= 0xffffffff;
$e &= 0xffffffff; $f &= 0xffffffff; $g &= 0xffffffff; $h &= 0xffffffff;
}
public function __construct ( $seed = null )
{
$this->a = $this->b = $this->c = 0;
$this->m = array_fill(0, 256, 0);
$m =& $this->m;
$a = $b = $c = $d = $e = $f = $g = $h = 0x9e3779b9; // golden ratio
for ($i = 0; $i < 4; ++$i) {
ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h); // scramble it
}
if ( isset($seed) ) {
if ( is_string($seed) ) {
// emulate casting char* to int* on a little-endian CPU
$seed = array_values(unpack("V256", pack("a1024", $seed)));
}
// initialize using the contents of $seed as the seed
for ($i = 0; $i < 256; $i += 8) {
$a += $seed[$i ]; $b += $seed[$i+1];
$c += $seed[$i+2]; $d += $seed[$i+3];
$e += $seed[$i+4]; $f += $seed[$i+5];
$g += $seed[$i+6]; $h += $seed[$i+7];
ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h);
$m[$i ] = $a; $m[$i+1] = $b; $m[$i+2] = $c; $m[$i+3] = $d;
$m[$i+4] = $e; $m[$i+5] = $f; $m[$i+6] = $g; $m[$i+7] = $h;
}
// do a second pass to make all of the seed affect all of $m
for ($i = 0; $i < 256; $i += 8) {
$a += $m[$i ]; $b += $m[$i+1]; $c += $m[$i+2]; $d += $m[$i+3];
$e += $m[$i+4]; $f += $m[$i+5]; $g += $m[$i+6]; $h += $m[$i+7];
ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h);
$m[$i ] = $a; $m[$i+1] = $b; $m[$i+2] = $c; $m[$i+3] = $d;
$m[$i+4] = $e; $m[$i+5] = $f; $m[$i+6] = $g; $m[$i+7] = $h;
}
}
else {
// fill in $m with messy stuff (does anyone really use this?)
for ($i = 0; $i < 256; $i += 8) {
ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h);
$m[$i ] = $a; $m[$i+1] = $b; $m[$i+2] = $c; $m[$i+3] = $d;
$m[$i+4] = $e; $m[$i+5] = $f; $m[$i+6] = $g; $m[$i+7] = $h;
}
}
// fill in the first set of results
$this->isaac();
}
}