#!/bin/perl -w =pod ---------------------------------------------------------------------------- -- readable.c: My random number generator, ISAAC. (c) Bob Jenkins, March 1996 You may use this code in any way you wish, and it is free. No warrantee. ---------------------------------------------------------------------------- -- This is a mostly minimal change porting of Bob Jenkins ISAAC PRNG into Perl. The C version used as a starting point was readable.c. The porting mainly involved just using "my" to declare variables instead of 'ub4' and 'word', and inserting $ and @ signs in front of variables. :-) The major "trick" needed to get it to work in perl was to ``use integer;'' so that the integer math would be C-ish. But even doing this, left shifts (the '>>' operator), still sign-extend their operands, which had to be counteracted by bitwise and-ing the results with appropriate constants, for instance, $b^=0x3fffffff & ($c>>2); All the original comments in the C code have been preserved. A minor difference in functionality from readable.c was to take the number of 256 byte blocks to produce from the first argument, defaulting to the 2 that readable.c hardcodes. This made it easier to do timing tests, the results of which showed that readable.c compiled with -O was roughly 20 times faster than this perl version, which, suspiciously or not, compares similarly to what Bob obtained with Java. Of course, the C code could be turned into a compiled perl XS module and retain all of its native speed, but that is an exercise for another day. John L. Allen - 10/10/00 ---------------------------------------------------------------------------- -- readable.c: My random number generator, ISAAC. (c) Bob Jenkins, March 1996 You may use this code in any way you wish, and it is free. No warrantee. ---------------------------------------------------------------------------- -- =cut use integer; # /* a ub4 is an unsigned 4-byte quantity */ # /* external results */ my (@randrsl, $randcnt); # /* internal state */ my (@mm); my ($aa, $bb, $cc) = (0, 0, 0); sub isaac() { my ($i, $x, $y); $cc++; # /* cc just gets incremented once per 256 results */ $bb += $cc; # /* then combined with bb */ for ($i=0; $i<256; $i++) { $x = $mm[$i]; $aa = $mm[($i+128)&255] + ($aa^($aa << 13)); $mm[$i] = $y = $mm[($x>>2)&255] + $aa + $bb; $randrsl[$i] = $bb = $mm[($y>>10)&255] + $x; $i++; $x = $mm[$i]; $aa = $mm[($i+128)&255] + ($aa^(0x03ffffff & ($aa >> 6))); $mm[$i] = $y = $mm[($x>>2)&255] + $aa + $bb; $randrsl[$i] = $bb = $mm[($y>>10)&255] + $x; $i++; $x = $mm[$i]; $aa = $mm[($i+128)&255] + ($aa^($aa << 2)); $mm[$i] = $y = $mm[($x>>2)&255] + $aa + $bb; $randrsl[$i] = $bb = $mm[($y>>10)&255] + $x; $i++; $x = $mm[$i]; $aa = $mm[($i+128)&255] + ($aa^(0x0000ffff & ($aa >> 16))); $mm[$i] = $y = $mm[($x>>2)&255] + $aa + $bb; $randrsl[$i] = $bb = $mm[($y>>10)&255] + $x; =pod /* Note that bits 2..9 are chosen from x but 10..17 are chosen from y. The only important thing here is that 2..9 and 10..17 don't overlap. 2..9 and 10..17 were then chosen for speed in the optimized version (rand.c) */ /* See http://burtleburtle.net/bob/rand/isaac.html for further explanations and analysis. */ =cut } } =pod /* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */ "mix" is "inlined" - see below Note: The above C comment seems to be out of place, having been just above the definition of the mix macro in readable.c. =cut sub randinit { my ($flag) = @_; my $i; my ($a, $b, $c, $d, $e, $f, $g, $h); $aa = $bb = $cc = 0; $a = $b = $c = $d = $e = $f = $g = $h = 0x9e3779b9; # /* the golden ratio */ for ($i=0; $i<4; ++$i) # /* scramble it */ { # "mix" $a^=$b<<11; $d+=$a; $b+=$c; $b^=0x3fffffff & ($c>>2); $e+=$b; $c+=$d; $c^=$d<<8; $f+=$c; $d+=$e; $d^=0x0000ffff & ($e>>16); $g+=$d; $e+=$f; $e^=$f<<10; $h+=$e; $f+=$g; $f^=0x0fffffff & ($g>>4); $a+=$f; $g+=$h; $g^=$h<<8; $b+=$g; $h+=$a; $h^=0x007fffff & ($a>>9); $c+=$h; $a+=$b; } for ($i=0; $i<256; $i+=8) # /* fill in mm[] with messy stuff */ { if ($flag) # /* use all the information in the seed */ { $a+=$randrsl[$i ]; $b+=$randrsl[$i+1]; $c+=$randrsl[$i+2]; $d+=$randrsl[$i+3]; $e+=$randrsl[$i+4]; $f+=$randrsl[$i+5]; $g+=$randrsl[$i+6]; $h+=$randrsl[$i+7]; } # "mix" $a^=$b<<11; $d+=$a; $b+=$c; $b^=0x3fffffff & ($c>>2); $e+=$b; $c+=$d; $c^=$d<<8; $f+=$c; $d+=$e; $d^=0x0000ffff & ($e>>16); $g+=$d; $e+=$f; $e^=$f<<10; $h+=$e; $f+=$g; $f^=0x0fffffff & ($g>>4); $a+=$f; $g+=$h; $g^=$h<<8; $b+=$g; $h+=$a; $h^=0x007fffff & ($a>>9); $c+=$h; $a+=$b; $mm[$i ]=$a; $mm[$i+1]=$b; $mm[$i+2]=$c; $mm[$i+3]=$d; $mm[$i+4]=$e; $mm[$i+5]=$f; $mm[$i+6]=$g; $mm[$i+7]=$h; } if ($flag) { # /* do a second pass to make all of the seed affect all of mm */ for ($i=0; $i<256; $i+=8) { $a+=$mm[$i ]; $b+=$mm[$i+1]; $c+=$mm[$i+2]; $d+=$mm[$i+3]; $e+=$mm[$i+4]; $f+=$mm[$i+5]; $g+=$mm[$i+6]; $h+=$mm[$i+7]; # "mix" $a^=$b<<11; $d+=$a; $b+=$c; $b^=0x3fffffff & ($c>>2); $e+=$b; $c+=$d; $c^=$d<<8; $f+=$c; $d+=$e; $d^=0x0000ffff & ($e>>16); $g+=$d; $e+=$f; $e^=$f<<10; $h+=$e; $f+=$g; $f^=0x0fffffff & ($g>>4); $a+=$f; $g+=$h; $g^=$h<<8; $b+=$g; $h+=$a; $h^=0x007fffff & ($a>>9); $c+=$h; $a+=$b; $mm[$i ]=$a; $mm[$i+1]=$b; $mm[$i+2]=$c; $mm[$i+3]=$d; $mm[$i+4]=$e; $mm[$i+5]=$f; $mm[$i+6]=$g; $mm[$i+7]=$h; } } isaac(); # /* fill in the first set of results */ $randcnt=256; # /* prepare to use the first set of results */ }