{$OVERFLOWCHECKS OFF} unit Isaac; // modified by Michael in der Wiesche for Delphi 2: // removed Create/reSeed (without a given seed) // merged Create/reSeed (with a given seed) // optimized moving seed into result array // removed the flag indicating use of seed // changed count type from integer to byte // removed redundant count:=0 instructions // changed local index var types to word // had to convert all Cardinals to Integers: // D2 doesn't allow cardinals > MAXLONGINT // checked it against c-implementation: // no impact on algorithm within D2 // October, 20th 2000 { This is ISAAC, a high-quality pseudo-random number generator. ISAAC is crypto-secure. ISAAC has no bias. ISAAC has a minimal garanteed period of 2^40. ISAAC average period is 2^8295. ISAAC algorithm is the property of Bob Jenkins. ISAAC is freely reusable. ISAAC can be used for encryption (mainly stream cipher). ISAAC has a 8192 bits seed (read: 8192 bits key for encryption). ISAAC Object-oriented Delphi implementation version 1.0.0 by Sébastien SAUVAGE http://sebsauvage.net The ISAAC algorihm is freely reusable. This implementation of ISAAC is freely reusable. Please let me know if you make interesting uses of this implementation. Please mention: ISAAC algorithm by Bob Jenkins (http://burtleburtle.net/bob/) Delphi ISAAC implementation by Sébastien SAUVAGE http://sebsauvage.net This implementation was tested under Delphi 4, but should work with no great changes in other versions of Delphi. This is a readable (not fast) implementation. Code is not optimized. To get exactly the same results as the C version, you will have to: - change FALSE to TRUE at line £££1 in code (look for £££1 in code). - use the following program: x : TIsaac; i : integer; x.InitZero; x.Isaac; // This is needed to get the same results as readable.c for i:=0 to 20 write(IntToHex(x.val,8)); Cause : the C implementation inits with a seed although the seed is zero. The C implementation makes an additional call to Isaac() before reading values (which is unneeded because Isaac() is called within Init()). For normal use, you should NOT call Isaac(). This will be done automatically when needed by val(). You also should leave the boolean value to FALSE at £££1. This will speedup initialisation at no cost for security. 2 do list: - faster implementation (unfold loops, etc.) - even faster implementation (x86 ASM ?) - create a sample program using this class - create a utility program that generate an x bytes random data file with a good seed (time/date/keyboard/mouse?). - documentation - methods for gathering random data (time/date/keyboard/mouse/other ?) - methods to seed with a String, with an integer. - TIsaacStream ? - Self-test method, with exception raising on problem. - TIssacCrypt ? (encryption with a 'password' used as the key.) I'm no programming God, so please let me know if you find any bug in this implementation. Any question regarding this implementation should be sent directly to Sebastien Sauvage, *not* to Bob Jenkins. Bob Jenkins is not responsible for this implementation. Any question regarding the ISAAC algorithm should be sent directly to Bob Jenkins, *not* Sebastien Sauvage. I'm not responsible for the design of ISAAC. Paper and C/C++/Modula-2/Java implementations available here: http://burtleburtle.net/bob/rand/isaac.html Quote from the author of the ISAAC algorithm: > 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. History: May, 16th 2000, version 1.0.0 - First implementation. Usage example: var i: integer; x: TIsaac; begin x.Create; for i:=1 to 20 do Memo1.Lines.Add(IntToHex(x.val, 8)); x.reSeed; // reseed exactly as in Create() for i:=1 to 20 do Memo1.Lines.Add(IntToHex(x.val, 8)); // get the same values end; } interface type TIsaac = class(TObject) private rsl : array[0..255] of integer; { the results given to the user } mem : array[0..255] of integer; { the internal state } aa : integer; { accumulator } bb : integer; { the last result } cc : integer; { counter, guarantees cycle is at least 2^^40 } count : byte; { count through the results in rsl[] } public constructor Seed(const s: array of integer); procedure Isaac; { generate new numbers } function Val : integer; { get 1 random value } end; implementation // Initialize the objet with a given seed. // You can safely use an array of integers. // The array can have any size. The first 256 values will be used. // If the array has less than 256 values, all the available values will be used, the rest will be zero. constructor TIsaac.Seed(const s: array of integer); var i,m : word; // 16 bit operations are still faster a,b,c,d,e,f,g,h : integer; begin m:=succ(High(s) and 255); // make sure m doesn't exceed 255; succ(m) is the maximum number of elements in s Move(s[0], rsl[0], m*SizeOf(integer)); // move m elements from s to rsl if (m<256) then FillChar(rsl[m], SizeOf(rsl) - (m*SizeOf(integer)), 0); // fill in remainder with zero aa := 0; bb := 0; cc := 0; // the golden ratio a := $9E3779B9; b:=a; c:=a; d:=a; e:=a; f:=a; g:=a; h:=a; // scramble it for i := 0 to 3 do begin // mix a,b,c,d,e,f,g and h a := a xor (b shl 11); d:=d+a; b:=b+c; b := b xor (c shr 2); e:=e+b; c:=c+d; c := c xor (d shl 8); f:=f+c; d:=d+e; d := d xor (e shr 16); g:=g+d; e:=e+f; e := e xor (f shl 10); h:=h+e; f:=f+g; f := f xor (g shr 4); a:=a+f; g:=g+h; g := g xor (h shl 8); b:=b+g; h:=h+a; h := h xor (a shr 9); c:=c+h; a:=a+b; end; // fill in mem[] with messy stuff i := 0; while (i<256) do begin // use all the information in the seed a:=a+rsl[i ]; b:=b+rsl[i+1]; c:=c+rsl[i+2]; d:=d+rsl[i+3]; e:=e+rsl[i+4]; f:=f+rsl[i+5]; g:=g+rsl[i+6]; h:=h+rsl[i+7]; // mix a,b,c,d,e,f,g and h a := a xor (b shl 11); d:=d+a; b:=b+c; b := b xor (c shr 2); e:=e+b; c:=c+d; c := c xor (d shl 8); f:=f+c; d:=d+e; d := d xor (e shr 16); g:=g+d; e:=e+f; e := e xor (f shl 10); h:=h+e; f:=f+g; f := f xor (g shr 4); a:=a+f; g:=g+h; g := g xor (h shl 8); b:=b+g; h:=h+a; h := h xor (a shr 9); c:=c+h; a:=a+b; mem[i ]:=a; mem[i+1]:=b; mem[i+2]:=c; mem[i+3]:=d; mem[i+4]:=e; mem[i+5]:=f; mem[i+6]:=g; mem[i+7]:=h; i:=i+8; end; // do a second pass to make all of the seed affect all of mem i := 0; while (i<256) do begin // use all the information in the seed a:=a+mem[i ]; b:=b+mem[i+1]; c:=c+mem[i+2]; d:=d+mem[i+3]; e:=e+mem[i+4]; f:=f+mem[i+5]; g:=g+mem[i+6]; h:=h+mem[i+7]; // mix a,b,c,d,e,f,g and h a:=a xor (b shl 11); d:=d+a; b:=b+c; b:=b xor (c shr 2); e:=e+b; c:=c+d; c:=c xor (d shl 8); f:=f+c; d:=d+e; d:=d xor (e shr 16); g:=g+d; e:=e+f; e:=e xor (f shl 10); h:=h+e; f:=f+g; f:=f xor (g shr 4); a:=a+f; g:=g+h; g:=g xor (h shl 8); b:=b+g; h:=h+a; h:=h xor (a shr 9); c:=c+h; a:=a+b; mem[i ]:=a; mem[i+1]:=b; mem[i+2]:=c; mem[i+3]:=d; mem[i+4]:=e; mem[i+5]:=f; mem[i+6]:=g; mem[i+7]:=h; i:=i+8; end; // fill in the first set of results Isaac; end; // Generate 256 results. This implementation is not optimized. // You do NOT need to call this method. It will be done automatically // when needed when you call val() to get a value. procedure TIsaac.Isaac; var i : word; // 16 bit operations are still faster x,y : integer; begin inc(cc); bb := bb + cc; for i := 0 to 255 do begin x := mem[i]; case (i and 3) of 0: aa := aa xor (aa shl 13); 1: aa := aa xor (aa shr 6); 2: aa := aa xor (aa shl 2); 3: aa := aa xor (aa shr 16); end; aa := aa + mem[(i+128) and 255]; y := mem[(x shr 2) and 255] + aa + bb; mem[i] := y; bb := mem[(y shr 10) and 255] + x; rsl[i] := bb; end; count := 0; end; // Call Val to get a random value (32 bits). function TIsaac.Val : integer; begin Result := rsl[count]; inc(count); if (count=0) then Isaac; // take advantage of byte overflow end; end.