isaac.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. unit ISAAC;
  2. {Bob Jenkins' public domain random number generator ISAAC}
  3. interface
  4. {$i std.inc}
  5. (*************************************************************************
  6. DESCRIPTION : Bob Jenkins' public domain random number generator ISAAC
  7. (Indirection, Shift, Accumulate, Add, and Count)
  8. Period at least 2^40, average 2^8295
  9. REQUIREMENTS : TP5-7, D1-D7/D9-D10/D12, FPC, VP, WDOSX
  10. EXTERNAL DATA : ---
  11. MEMORY USAGE : ---
  12. DISPLAY MODE : ---
  13. REFERENCES : http://burtleburtle.net/bob/rand/isaacafa.html
  14. (ISAAC: a fast cryptographic random number generator)
  15. Version Date Author Modification
  16. ------- -------- ------- ------------------------------------------
  17. 0.10 23.07.05 W.Ehrhardt Initial BP7 port of rand.c with RANDSIZ=256
  18. 0.11 23.07.05 we Some tweaking in isaac_generate
  19. 0.12 23.07.05 we non crypt option (RANDSIZ=16), much slower
  20. 0.13 23.07.05 we use RANDSIZ=256 only, procedure Mix
  21. 0.14 23.07.05 we use mix array m, use inc where possible
  22. 0.15 23.07.05 we BASM16 in isaac_generate
  23. 0.16 24.07.05 we routines for word, long, double etc.
  24. 0.17 01.09.05 we byte typecast in init0
  25. 0.18 05.11.08 we isaac_dword function
  26. 0.19 02.12.08 we BTypes/Ptr2Inc
  27. 0.20 06.01.09 we Uses BTypes moved to implementation
  28. 0.21 14.06.12 we Fix bug in _read for trailing max 3 bytes
  29. **************************************************************************)
  30. (*-------------------------------------------------------------------------
  31. (C) Copyright 2005-2012 Wolfgang Ehrhardt
  32. This software is provided 'as-is', without any express or implied warranty.
  33. In no event will the authors be held liable for any damages arising from
  34. the use of this software.
  35. Permission is granted to anyone to use this software for any purpose,
  36. including commercial applications, and to alter it and redistribute it
  37. freely, subject to the following restrictions:
  38. 1. The origin of this software must not be misrepresented; you must not
  39. claim that you wrote the original software. If you use this software in
  40. a product, an acknowledgment in the product documentation would be
  41. appreciated but is not required.
  42. 2. Altered source versions must be plainly marked as such, and must not be
  43. misrepresented as being the original software.
  44. 3. This notice may not be removed or altered from any source distribution.
  45. ----------------------------------------------------------------------------*)
  46. {$ifdef BIT16}
  47. {$N+}
  48. {$endif}
  49. type
  50. isaac_ctx = record {context of random number generator}
  51. randmem: array[0..255] of longint; {the internal state}
  52. randrsl: array[0..255] of longint; {the results given to the user}
  53. randa : longint; {accumulator}
  54. randb : longint; {the last result}
  55. randc : longint; {counter, guarantees cycle >= 2^40 }
  56. nextres: longint; {the next result }
  57. randidx: word; {the index in randrsl[] }
  58. end;
  59. procedure isaac_init (var ctx: isaac_ctx; seed: longint);
  60. {-Init context from randrsl[0]=seed, randrsl[i]=0 otherwise}
  61. procedure isaac_init0(var ctx: isaac_ctx);
  62. {-Init context from randseed}
  63. {$ifdef CONST}
  64. procedure isaac_inita(var ctx: isaac_ctx; const key: array of longint; klen: integer);
  65. {-Init all context variables with separate seeds, klen: number of seeds}
  66. {$else}
  67. procedure isaac_inita(var ctx: isaac_ctx; var KArr; klen: integer);
  68. {-Init all context variables with separate seeds, klen: number of seeds}
  69. {$endif}
  70. procedure isaac_next(var ctx: isaac_ctx);
  71. {-Next step of PRNG}
  72. procedure isaac_read(var ctx: isaac_ctx; dest: pointer; len: longint);
  73. {-Read len bytes from the PRNG to dest}
  74. function isaac_long(var ctx: isaac_ctx): longint;
  75. {-Next random positive longint}
  76. function isaac_dword(var ctx: isaac_ctx): {$ifdef HAS_CARD32}cardinal{$else}longint{$endif};
  77. {-Next 32 bit random dword (cardinal or longint)}
  78. function isaac_word(var ctx: isaac_ctx): word;
  79. {-Next random word}
  80. function isaac_double(var ctx: isaac_ctx): double;
  81. {-Next random double [0..1) with 32 bit precision}
  82. function isaac_double53(var ctx: isaac_ctx): double;
  83. {-Next random double in [0..1) with full double 53 bit precision}
  84. function isaac_selftest: boolean;
  85. {-Simple self-test of ISAAC PRNG}
  86. {$ifdef testing}
  87. {interfaced for cycle testing without overhead, do not use for normal use}
  88. procedure isaac_generate(var ctx: isaac_ctx);
  89. {-generate next 256 result values, ie refill randrsl}
  90. {$endif}
  91. implementation
  92. uses
  93. BTypes;
  94. {---------------------------------------------------------------------------}
  95. procedure isaac_generate(var ctx: isaac_ctx);
  96. {-generate next 256 result values, ie refill randrsl}
  97. var
  98. x,y: longint;
  99. xi : integer absolute x; {better performance for BIT16}
  100. i : integer;
  101. {$ifdef BASM16}
  102. pra: pointer; {pointer to cxt.randa for faster BASM16 access}
  103. {$endif}
  104. begin
  105. {$ifdef BASM16}
  106. pra := @ctx.randa;
  107. {$endif}
  108. with ctx do begin
  109. inc(randc);
  110. inc(randb, randc);
  111. for i:=0 to 255 do begin
  112. {$ifdef BASM16}
  113. case i and 3 of
  114. 0: asm
  115. les di,[pra]
  116. db $66; mov ax,es:[di]
  117. db $66; shl ax,13
  118. db $66; xor es:[di],ax
  119. end;
  120. 1: asm
  121. les di,[pra]
  122. db $66; mov ax,es:[di]
  123. db $66; shr ax,6
  124. db $66; xor es:[di],ax
  125. end;
  126. 2: asm
  127. les di,[pra]
  128. db $66; mov ax,es:[di]
  129. db $66; shl ax,2
  130. db $66; xor es:[di],ax
  131. end;
  132. 3: asm
  133. {shr 16 is special, use word [pra+2]}
  134. les di,[pra]
  135. mov ax, es:[di+2]
  136. xor es:[di],ax
  137. end;
  138. end;
  139. {$else}
  140. case i and 3 of
  141. 0: randa := randa xor (randa shl 13);
  142. 1: randa := randa xor (randa shr 6);
  143. 2: randa := randa xor (randa shl 2);
  144. 3: randa := randa xor (randa shr 16);
  145. end;
  146. {$endif}
  147. x := randmem[i];
  148. inc(randa,randmem[(i+128) and 255]);
  149. y := randmem[(xi shr 2) and 255] + randa + randb;
  150. randmem[i] := y;
  151. randb := randmem[(y shr 10) and 255] + x;
  152. randrsl[i] := randb;
  153. end;
  154. {reset result index}
  155. randidx:=0;
  156. end;
  157. end;
  158. {---------------------------------------------------------------------------}
  159. procedure internal_init(var ctx: isaac_ctx; flag: boolean);
  160. {-Init state, use randrsl if flag=true}
  161. var
  162. i,j: integer;
  163. m: array[0..7] of longint;
  164. procedure Mix;
  165. {-mix the array}
  166. begin
  167. m[0] := m[0] xor (m[1] shl 11); inc(m[3], m[0]); inc(m[1], m[2]);
  168. m[1] := m[1] xor (m[2] shr 2); inc(m[4], m[1]); inc(m[2], m[3]);
  169. m[2] := m[2] xor (m[3] shl 8); inc(m[5], m[2]); inc(m[3], m[4]);
  170. m[3] := m[3] xor (m[4] shr 16); inc(m[6], m[3]); inc(m[4], m[5]);
  171. m[4] := m[4] xor (m[5] shl 10); inc(m[7], m[4]); inc(m[5], m[6]);
  172. m[5] := m[5] xor (m[6] shr 4); inc(m[0], m[5]); inc(m[6], m[7]);
  173. m[6] := m[6] xor (m[7] shl 8); inc(m[1], m[6]); inc(m[7], m[0]);
  174. m[7] := m[7] xor (m[0] shr 9); inc(m[2], m[7]); inc(m[0], m[1]);
  175. end;
  176. begin
  177. with ctx do begin
  178. randa := 0;
  179. randb := 0;
  180. randc := 0;
  181. for i:=0 to 7 do m[i] := longint($9e3779b9); {the golden ratio}
  182. for i:=0 to 3 do Mix;
  183. i := 0;
  184. while i<256 do begin
  185. {fill in randmem[] with messy stuff}
  186. if flag then begin
  187. {use all the information in the seed}
  188. for j:=0 to 7 do inc(m[j], randrsl[i+j]);
  189. end;
  190. Mix;
  191. move(m, randmem[i], sizeof(m));
  192. inc(i,8);
  193. end;
  194. if flag then begin
  195. {do a second pass to make all of the seed affect all of randmem}
  196. i := 0;
  197. while i<256 do begin
  198. for j:=0 to 7 do inc(m[j], randmem[i+j]);
  199. Mix;
  200. move(m, randmem[i], sizeof(m));
  201. inc(i,8);
  202. end;
  203. end;
  204. {generate first set of results}
  205. isaac_generate(ctx);
  206. {prepare to use the first set of results }
  207. randidx := 0;
  208. end;
  209. end;
  210. {---------------------------------------------------------------------------}
  211. procedure isaac_init(var ctx: isaac_ctx; seed: longint);
  212. {-Init context from randrsl[0]=seed, randrsl[i]=0 otherwise}
  213. begin
  214. with ctx do begin
  215. fillchar(randrsl, sizeof(randrsl),0);
  216. randrsl[0] := seed;
  217. end;
  218. internal_init(ctx, true);
  219. end;
  220. {---------------------------------------------------------------------------}
  221. procedure isaac_init0(var ctx: isaac_ctx);
  222. {-Init context from randseed and randrsl[i]:=random}
  223. var
  224. i,j: integer;
  225. tl: longint;
  226. ta: packed array[0..3] of byte absolute tl;
  227. begin
  228. with ctx do begin
  229. for i:=0 to 255 do begin
  230. for j:=0 to 3 do ta[j] := byte(random(256));
  231. randrsl[i] := tl;
  232. end;
  233. end;
  234. internal_init(ctx, true);
  235. end;
  236. {---------------------------------------------------------------------------}
  237. {$ifdef CONST}
  238. procedure isaac_inita(var ctx: isaac_ctx; const key: array of longint; klen: integer);
  239. {-Init all context variables with separate seeds, klen: number of seeds}
  240. {$else}
  241. procedure isaac_inita(var ctx: isaac_ctx; var KArr; klen: integer);
  242. {-Init all context variables with separate seeds, klen: number of seeds}
  243. var
  244. key: packed array[0..255] of longint absolute KArr; {T5-6 do not have open arrrays}
  245. {$endif}
  246. var
  247. i: integer;
  248. begin
  249. {$ifdef CONST}
  250. if klen>high(key)+1 then klen := high(key)+1;
  251. {$endif}
  252. with ctx do begin
  253. for i:=0 to 255 do begin
  254. if i<klen then randrsl[i]:=key[i] else randrsl[i]:=0;
  255. end;
  256. end;
  257. internal_init(ctx, true);
  258. end;
  259. {---------------------------------------------------------------------------}
  260. procedure isaac_next(var ctx: isaac_ctx);
  261. {-Next step of PRNG}
  262. begin
  263. with ctx do begin
  264. if randidx>255 then isaac_generate(ctx);
  265. nextres := randrsl[randidx];
  266. inc(randidx);
  267. end;
  268. end;
  269. {---------------------------------------------------------------------------}
  270. procedure isaac_read(var ctx: isaac_ctx; dest: pointer; len: longint);
  271. {-Read len bytes from the PRNG to dest}
  272. type
  273. plong = ^longint;
  274. begin
  275. {not optimized}
  276. while len>3 do begin
  277. isaac_next(ctx);
  278. plong(dest)^ := ctx.nextres;
  279. inc(Ptr2Inc(dest),4);
  280. dec(len, 4);
  281. end;
  282. if len>0 then begin
  283. isaac_next(ctx);
  284. move(ctx.nextres, dest^, len and 3);
  285. end;
  286. end;
  287. {---------------------------------------------------------------------------}
  288. function isaac_long(var ctx: isaac_ctx): longint;
  289. {-Next random positive longint}
  290. begin
  291. isaac_next(ctx);
  292. isaac_long := ctx.nextres shr 1;
  293. end;
  294. {---------------------------------------------------------------------------}
  295. function isaac_dword(var ctx: isaac_ctx): {$ifdef HAS_CARD32}cardinal{$else}longint{$endif};
  296. {-Next 32 bit random dword (cardinal or longint)}
  297. begin
  298. isaac_next(ctx);
  299. {$ifdef HAS_CARD32}
  300. isaac_dword := cardinal(ctx.nextres);
  301. {$else}
  302. isaac_dword := ctx.nextres;
  303. {$endif}
  304. end;
  305. {---------------------------------------------------------------------------}
  306. function isaac_word(var ctx: isaac_ctx): word;
  307. {-Next random word}
  308. type
  309. TwoWords = packed record
  310. L,H: word
  311. end;
  312. begin
  313. isaac_next(ctx);
  314. isaac_word := TwoWords(ctx.nextres).H;
  315. end;
  316. {---------------------------------------------------------------------------}
  317. function isaac_double(var ctx: isaac_ctx): double;
  318. {-Next random double [0..1) with 32 bit precision}
  319. begin
  320. isaac_next(ctx);
  321. isaac_double := (ctx.nextres + 2147483648.0) / 4294967296.0;
  322. end;
  323. {---------------------------------------------------------------------------}
  324. function isaac_double53(var ctx: isaac_ctx): double;
  325. {-Next random double in [0..1) with full double 53 bit precision}
  326. var
  327. hb,lb: longint;
  328. begin
  329. isaac_next(ctx);
  330. hb := ctx.nextres shr 5;
  331. isaac_next(ctx);
  332. lb := ctx.nextres shr 6;
  333. isaac_double53 := (hb*67108864.0+lb)/9007199254740992.0;
  334. end;
  335. {---------------------------------------------------------------------------}
  336. function isaac_selftest: boolean;
  337. {-Simple self-test of ISAAC PRNG}
  338. var
  339. ctx: isaac_ctx;
  340. begin
  341. fillchar(ctx, sizeof(ctx),0);
  342. internal_init(ctx, true);
  343. isaac_generate(ctx);
  344. {check first and last longint of randvec.txt}
  345. if ctx.randrsl[0]<>longint($f650e4c8) then begin
  346. isaac_selftest := false;
  347. exit;
  348. end;
  349. isaac_generate(ctx);
  350. isaac_selftest := ctx.randrsl[255] = longint($4bb5af29);
  351. end;
  352. end.