There are two procedures which can be called in Filter Factory by the user:
Filter Factory uses Donald E. Knuth's subtractive random number generator algorithm ("ran3"), which has been published at page 283 of "The Art of Computer Programming, volume 2: Seminumerical Algorithms", Addison-Wesley, Reading, MA, second edition, 1981. (PDF Page 307)
rnd(a,b) will output the following data using default seed rst(0):
00000000 0A 23 6A 73 9E 6F 78 5B 86 58 25 91 40 75 7D A4 00000010 2B 31 CD BE 5C A7 E3 7E 30 0C 4F B5 11 D2 24 20 00000020 B1 01 4D 83 4B AA CE 9E 82 96 E9 36 5C A7 5F 01 00000030 B6 A3 1C 32 AB 66 10 EA 72 69 26 1B 24 CE 8D E8 00000040 D6 8F A8 0A 19 D6 45 2A 7B 2A A2 2A FC 7D 6E 46 ... 00010000 66 E7 61 34 8F 5C 63 5D 35 2A C9 56 E2 D7 B2 21 00010010 32 06 34 7B 30 A9 79 0E 37 29 09 E8 12 C5 FA B6 00010020 B5 BA 28 C9 66 BC 1C 84 56 21 1F 2D 9A 39 68 3C 00010030 BB 5F 04 62 96 DA 4D B0 32 A7 0C C6 F6 A7 41 B1 00010040 D4 A8 37 B5 3D 79 B9 F6 4B D5 77 CE 13 9F C1 87 ...
uint16_t RND_INDEX_COUNTER[2]; // [PTR DS:[1C006004]]+0094h uint32_t RND_LOOKUP[56]; // [PTR DS:[1C006004]]+0098h uint32_t RND_SEED = 0; // [PTR DS:[1C006004]]+4578h uint32_t RND_SEED_SAVE = 0; // [PTR DS:[1C006004]]+457Ch uint32_t rnd(uint32_t a, uint32_t b) { RND_INDEX_COUNTER[0] = RND_INDEX_COUNTER[0] % 55 + 1; RND_INDEX_COUNTER[1] = RND_INDEX_COUNTER[1] % 55 + 1; uint32_t result = RND_LOOKUP[RND_INDEX_COUNTER[0]] - RND_LOOKUP[RND_INDEX_COUNTER[1]]; RND_LOOKUP[RND_INDEX_COUNTER[0]] = result; // Scale result to interval [a..b] int32_t range = b - a; if (range < 0) return 0; // this check was not existing in 3.00a return a + (result % (range + 1)); } void fill_rnd_lookup(uint32_t param_seed) { unsigned int iVar2; unsigned int iVar3; unsigned int iVar5; unsigned int iVar6; // 161803398 = 1.61803398 * 10^8 ~ phi * 10^8 iVar5 = 161803398 - (param_seed & 0x7fff); RND_LOOKUP[55] = iVar5; iVar6 = 1; for (iVar2 = 21; iVar2 <= 1134; iVar2 += 21) { iVar3 = iVar6; RND_LOOKUP[iVar2 % 55] = iVar3; iVar6 = iVar5 - iVar3; iVar5 = iVar3; } for (iVar2 =0; iVar2 <4; iVar2++) { unsigned int cnt = 1; do { RND_LOOKUP[cnt++] = RND_LOOKUP[cnt] - RND_LOOKUP[((cnt + 30) % 55) + 1]; } while (cnt <= 55); } RND_SEED_SAVE = param_seed; return; } uint32_t rst(uint32_t seed) { RND_SEED = seed; return 0; } void initialize_variables() { RND_INDEX_COUNTER[0] = 0; RND_INDEX_COUNTER[1] = 31; fill_rnd_lookup(0); } int main() { initialize_variables(); // Optional: Set individual seed rst(1); if (RND_SEED != RND_SEED_SAVE) { fill_rnd_lookup(RND_SEED); } // Print 10 random numbers for (int i=0; i<10; i++) { printf("%d\n", rnd(0, 255)); } }
In C++:
uint16_t RND_INDEX_COUNTER[2]; // [PTR DS:[1C006004]]+0094h uint32_t RND_LOOKUP[56]; // [PTR DS:[1C006004]]+0098h uint32_t RND_SEED = 0; // [PTR DS:[1C006004]]+4578h uint32_t RND_SEED_SAVE = 0; // [PTR DS:[1C006004]]+457Ch
Procedure is stored in the OPER resource.
Assembly (Filter Factory 3.0.4 for Photoshop/Windows):
8D 9F 94 00 00 00 | lea ebx,[edi+$00000094 (RND_INDEX_COUNTER)] | |
66 8B 43 02 | mov ax,[ebx+$02 (2)] | ax = RND_INDEX_COUNTER[1]; |
66 40 | inc ax | ax++; |
66 3D 38 00 | cmp ax,$0038 (56) | . |
75 04 | jnz +$04 (@@1) | if (ax != 56) goto @@1; |
66 B8 01 00 | mov ax,$0001 (1) | ax = 1; |
@@1: | @@1: | |
66 89 43 02 | mov [ebx+$02 (2)],ax | RND_INDEX_COUNTER[1] = ax; |
66 8B 03 | mov ax,[ebx] | ax = RND_INDEX_COUNTER[0]; |
66 40 | inc ax | ax++; |
66 3D 38 00 | cmp ax,$0038 (56) | . |
75 04 | jnz +$04 (@@2) | if (ax != 56) goto @@2; |
66 B8 01 00 | mov ax,$0001 (1) | ax = 1; |
@@2: | @@2: | |
66 89 03 | mov [ebx],ax | RND_INDEX_COUNTER[0] = ax; |
0F B7 03 | movzx eax,[ebx] | eax = RND_INDEX_COUNTER[0]; |
8D 4C 83 04 | lea ecx,[ebx+eax*4+$04 (4)] | T = eax; |
8B 01 | mov eax,[ecx] | eax = RND_LOOKUP[T]; |
0F B6 53 02 | movzx edx,[ebx+$02 (2)] | edx = RND_INDEX_COUNTER[1]; |
2B 44 93 04 | sub eax,[ebx+edx*4+$04 (4)] | eax -= RND_LOOKUP[edx]; // eax = RND_LOOKUP[RND_INDEX_COUNTER[0]] - RND_LOOKUP[RND_INDEX_COUNTER[1]] |
89 01 | mov [ecx],eax | RND_LOOKUP[T] = eax; |
5E | pop esi (param_b) | . |
59 | pop ecx (param_a) | . |
2B F1 | sub esi,ecx | esi = param_b - param_a; |
79 04 | jns +$04 (@@3) | if (esi >= 0) goto @@3; |
33 C0 | xor eax | eax = 0 (Error: param_a > param_b . Return 0) |
EB 08 | jmp +$08 (@@4) | goto @@4; |
@@3: | @@3: | |
46 | inc esi | esi++; |
2B D2 | sub edx,edx | edx = 0; |
F7 F6 | div esi | eax /= esi; edx = eax / esi |
8D 04 11 | lea eax,[ecx+edx] | eax = ecx + edx; |
@@4: | @@4: | |
50 | push eax | return eax; // return param_a + (RND_LOOKUP[T] % (param_b - param_a + 1)); |
In C++:
uint32_t rnd(uint32_t a, uint32_t b) { RND_INDEX_COUNTER[0] = RND_INDEX_COUNTER[0] % 55 + 1; RND_INDEX_COUNTER[1] = RND_INDEX_COUNTER[1] % 55 + 1; uint32_t result = RND_LOOKUP[RND_INDEX_COUNTER[0]] - RND_LOOKUP[RND_INDEX_COUNTER[1]]; RND_LOOKUP[RND_INDEX_COUNTER[0]] = result; // Scale result to interval [a..b] int32_t range = b - a; if (range < 0) return 0; // this check was not existing in 3.00a return a + (result % (range + 1)); }
Procedure is stored in the OPER resource.
Writes parameter to EDI[$4578] (RND_SEED) and returns 0.
Assembly (Filter Factory 3.0.4 for Photoshop/Windows):
58 | pop eax (param_i) | . |
89 87 78 45 00 00 | mov [edi+$00004578 (RND_SEED)],eax | RND_SEED = param_i; |
2B C0 | sub eax,eax | param_i = 0; |
50 | push eax | return param_i; |
In C++:
uint32_t rst(uint32_t seed) { RND_SEED = seed; return 0; }
Before a pixel is processed (?), the RND_LOOKUP table will be initialized, if the user has chosen a new individual seed.
Assembly (Filter Factory 3.0.4 for Photoshop/Windows):
..... .text:1C003661 loc_1C003661: .text:1C003661 mov eax, ds:dword_1C006004 .text:1C003666 mov ecx, [eax+4578h] ; RND_SEED .text:1C00366C mov edx, [eax+457Ch] ; RND_SEED_SAVE .text:1C003672 cmp edx, ecx .text:1C003674 jz short loc_1C00369D .text:1C003676 mov edx, eax .text:1C003678 mov ebx, [eax+44h] ; xmin .text:1C00367B cmp [edx+40h], ebx ; x .text:1C00367E jnz short loc_1C00369D .text:1C003680 mov edx, eax .text:1C003682 mov ebx, [eax+4Ch] ; y .text:1C003685 cmp [edx+50h], ebx ; ymin .text:1C003688 jnz short loc_1C00369D .text:1C00368A mov edx, eax .text:1C00368C mov ebx, [eax+5Ch] ; zmin .text:1C00368F cmp [edx+58h], ebx ; z .text:1C003692 jnz short loc_1C00369D .text:1C003694 push ecx .text:1C003695 call fill_rnd_lookup ; fill if x==y==z==0 & RND_SEED!=RND_SEED_SAVE .text:1C00369A add esp, 4 .text:1C00369D .text:1C00369D loc_1C00369D: .text:1C00369D .....
In C++:
unsigned int process_pixel() { // ... if ((x==xmin) && (y==ymin) && (z==zmin) && (RND_SEED != RND_SEED_SAVE)) { fill_rnd_lookup(RND_SEED); } // ... }
fill_rnd_lookup() will be called at two locations:
Assembly (Filter Factory 3.0.4 for Photoshop/Windows):
1C0039B0: 53 push ebx 1C0039B1: 56 push esi 1C0039B2: 57 push edi 1C0039B3: 55 push ebp 1C0039B4: 8B 35 04 60 00 1C mov esi,dword ptr ds:[1C006004h] 1C0039BA: BF 86 EC A4 09 mov edi,9A4EC86h edi = 161803398 (= 1.61803398 * 10^8 ≈ φ * 10^8) 1C0039BF: 81 C6 98 00 00 00 add esi,98h esi = RND_LOOKUP 1C0039C5: 8B 44 24 14 mov eax,dword ptr [esp+14h] . 1C0039C9: 25 FF 7F 00 00 and eax,7FFFh eax = arg_seed & 0x7FFF 1C0039CE: BB 01 00 00 00 mov ebx,1 ebx = 1 1C0039D3: 2B F8 sub edi,eax edi = 161803398 - (arg_seed & 0x7FFF) 1C0039D5: B9 15 00 00 00 mov ecx,15h ecx = 21 1C0039DA: 89 BE DC 00 00 00 mov dword ptr [esi+000000DCh],edi RND_LOOKUP[55] = edi @@1: 1C0039E0: BD 37 00 00 00 mov ebp,37h ebp = 55 1C0039E5: 8B C1 mov eax,ecx eax = ecx 1C0039E7: 99 cdq 1C0039E8: F7 FD idiv ebp eax = eax / ebp edx = eax % ebp 1C0039EA: 8B C3 mov eax,ebx eax = ebx 1C0039EC: 2B FB sub edi,ebx edi -= ebx 1C0039EE: 89 04 96 mov dword ptr [esi+edx*4],eax RND_LOOKUP[edx] = eax 1C0039F1: 8B DF mov ebx,edi ebx = edi 1C0039F3: 8B F8 mov edi,eax edi = eax 1C0039F5: 83 C1 15 add ecx,15h ecx += 21 1C0039F8: 81 F9 6E 04 00 00 cmp ecx,46Eh 1C0039FE: 7E E0 jle 1C0039E0 if (ecx <= 1134) jmp @@1 1C003A00: BF 04 00 00 00 mov edi,4 edi = 4 @@2: 1C003A05: BB 01 00 00 00 mov ebx,1 ebx = 1 1C003A0A: 8D 9B 00 00 00 00 lea ebx,[ebx+00000000h] (nop) @@3: 1C003A10: 8D 43 1E lea eax,[ebx+1Eh] eax = ebx + 30 1C003A13: B9 37 00 00 00 mov ecx,37h ecx = 55 1C003A18: 99 cdq 1C003A19: F7 F9 idiv ecx eax = eax / ecx edx = eax % ecx 1C003A1B: 8B 0C 9E mov ecx,dword ptr [esi+ebx*4] ecx = RND_LOOKUP[ebx] 1C003A1E: 43 inc ebx ebx++ 1C003A1F: 8B 44 96 04 mov eax,dword ptr [esi+edx*4+4] eax = RND_LOOKUP[edx+1] 1C003A23: 2B C8 sub ecx,eax ecx -= eax 1C003A25: 83 FB 37 cmp ebx,37h ebx == 55 ? 1C003A28: 89 4C 9E FC mov dword ptr [esi+ebx*4-4],ecx RND_LOOKUP[ebx-1] = ecx 1C003A2C: 7E E2 jle 1C003A10 if (ebx<=55) jmp @@3 1C003A2E: 4F dec edi edi-- 1C003A2F: 90 nop 1C003A30: 75 D3 jne 1C003A05 if (ebx!=55) jmp @@2 1C003A32: 8B 0D 04 60 00 1C mov ecx,dword ptr ds:[1C006004h] 1C003A38: 8B 44 24 14 mov eax,dword ptr [esp+14h] eax = arg_seed 1C003A3C: 89 81 7C 45 00 00 mov dword ptr [ecx+0000457Ch],eax RND_SEED_SAVE = eax 1C003A42: 5D pop ebp 1C003A43: 5F pop edi 1C003A44: 5E pop esi 1C003A45: 5B pop ebx 1C003A46: C3 ret
In C++:
void fill_rnd_lookup(uint32_t param_seed) { unsigned int iVar2; unsigned int iVar3; unsigned int iVar5; unsigned int iVar6; // 161803398 = 1.61803398 * 10^8 ~ phi * 10^8 iVar5 = 161803398 - (param_seed & 0x7fff); RND_LOOKUP[55] = iVar5; iVar6 = 1; for (iVar2 = 21; iVar2 <= 1134; iVar2 += 21) { iVar3 = iVar6; RND_LOOKUP[iVar2 % 55] = iVar3; iVar6 = iVar5 - iVar3; iVar5 = iVar3; } for (iVar2 =0; iVar2 <4; iVar2++) { unsigned int cnt = 1; do { RND_LOOKUP[cnt++] = RND_LOOKUP[cnt] - RND_LOOKUP[((cnt + 30) % 55) + 1]; } while (cnt <= 55); } RND_SEED_SAVE = param_seed; return; }
This procedure initializes (among other things) the counters RND_INDEX_COUNTER[0] and RND_INDEX_COUNTER[1].
Assembly (Filter Factory 3.0.4 for Photoshop/Windows):
1C003350: 53 push ebx 1C003351: 2B D2 sub edx,edx @@1: 1C003353: A1 00 60 00 1C mov eax,[1C006000] 1C003358: 8B 0D 04 60 00 1C mov ecx,dword ptr ds:[1C006004h] 1C00335E: 8B 5C 10 08 mov ebx,dword ptr [eax+edx+8] 1C003362: 83 C2 04 add edx,4 1C003365: 89 5C 11 60 mov dword ptr [ecx+edx+60h],ebx 1C003369: 83 FA 20 cmp edx,20h 1C00336C: 7C E5 jl 1C003353(@@1) 1C00336E: A1 04 60 00 1C mov eax,[1C006004] 1C003373: 05 78 01 00 00 add eax,178h map[0] 1C003378: 50 push eax 1C003379: A1 04 60 00 1C mov eax,[1C006004] 1C00337E: 8B 48 64 mov ecx,dword ptr [eax+64h] ctl[0] 1C003381: 8B 50 68 mov edx,dword ptr [eax+68h] ctl[1] 1C003384: 51 push ecx 1C003385: 52 push edx 1C003386: E8 C5 04 00 00 call 1C003850 1C00338B: 83 C4 0C add esp,0Ch 1C00338E: A1 04 60 00 1C mov eax,[1C006004] 1C003393: 05 78 02 00 00 add eax,278h map[1] 1C003398: 50 push eax 1C003399: A1 04 60 00 1C mov eax,[1C006004] 1C00339E: 8B 48 6C mov ecx,dword ptr [eax+6Ch] ctl[2] 1C0033A1: 8B 50 70 mov edx,dword ptr [eax+70h] ctl[3] 1C0033A4: 51 push ecx 1C0033A5: 52 push edx 1C0033A6: E8 A5 04 00 00 call 1C003850 1C0033AB: 83 C4 0C add esp,0Ch 1C0033AE: A1 04 60 00 1C mov eax,[1C006004] 1C0033B3: 05 78 03 00 00 add eax,378h map[2] 1C0033B8: 50 push eax 1C0033B9: A1 04 60 00 1C mov eax,[1C006004] 1C0033BE: 8B 48 74 mov ecx,dword ptr [eax+74h] ctl[4] 1C0033C1: 8B 50 78 mov edx,dword ptr [eax+78h] ctl[5] 1C0033C4: 51 push ecx 1C0033C5: 52 push edx 1C0033C6: E8 85 04 00 00 call 1C003850 1C0033CB: 83 C4 0C add esp,0Ch 1C0033CE: A1 04 60 00 1C mov eax,[1C006004] 1C0033D3: 05 78 04 00 00 add eax,478h map[3] 1C0033D8: 50 push eax 1C0033D9: A1 04 60 00 1C mov eax,[1C006004] 1C0033DE: 8B 48 7C mov ecx,dword ptr [eax+7Ch] ctl[6] 1C0033E1: 8B 90 80 00 00 00 mov edx,dword ptr [eax+80h] ctl[7] 1C0033E7: 51 push ecx 1C0033E8: 52 push edx 1C0033E9: E8 62 04 00 00 call 1C003850 1C0033EE: 83 C4 0C add esp,0Ch 1C0033F1: 8B 0D 04 60 00 1C mov ecx,dword ptr ds:[1C006004h] 1C0033F7: 66 C7 81 94 00 00 mov word ptr [ecx+00000094h],0 RND_INDEX_COUNTER[0] = 0 00 00 00 1C003400: 8B 0D 04 60 00 1C mov ecx,dword ptr ds:[1C006004h] 1C003406: 6A 00 push 0 param_seed = 0; 1C003408: 66 C7 81 96 00 00 mov word ptr [ecx+00000096h],1Fh RND_INDEX_COUNTER[1] = 31 00 1F 00 1C003411: E8 9A 05 00 00 call 1C0039B0 call fill_rnd_lookup(param_seed) 1C003416: 83 C4 04 add esp,4 1C003419: A1 04 60 00 1C mov eax,[1C006004] 1C00341E: 05 78 35 00 00 add eax,3578h cell[0..255] 1C003423: 50 push eax 1C003424: E8 37 07 00 00 call 1C003B60 1C003429: 83 C4 04 add esp,4 1C00342C: 5B pop ebx 1C00342D: C3 ret
In C++:
void initialize_variables() { // ... RND_INDEX_COUNTER[0] = 0; RND_INDEX_COUNTER[1] = 31; fill_rnd_lookup(0); // ... }
rand()&0xFF
using srand(0)
, 512 MiB dataseed = seed * 214013 + 2531011
Entropy = 8.000000 bits per byte. Optimum compression would reduce the size of this 536870912 byte file by 0 percent. Chi square distribution for 536870912 samples is 0.00, and randomly would exceed this value more than than 99.99 percent of the times. Arithmetic mean value of data bytes is 127.5000 (127.5 = random). Monte Carlo value for Pi is 3.141621005 (error 0.00 percent). Serial correlation coefficient is 0.000011 (totally uncorrelated = 0.0).
rand()&0xFF
using srand(0)
, 512 MiB dataseed = seed * 1103515245 + 12345
Entropy = 8.000000 bits per byte. Optimum compression would reduce the size of this 536870912 byte file by 0 percent. Chi square distribution for 536870912 samples is 0.00, and randomly would exceed this value more than than 99.99 percent of the times. Arithmetic mean value of data bytes is 127.5000 (127.5 = random). Monte Carlo value for Pi is 3.141647872 (error 0.00 percent). Serial correlation coefficient is -0.000001 (totally uncorrelated = 0.0).
rnd(0,255)
using rst(0)
, 512 MiB dataEntropy = 8.000000 bits per byte. Optimum compression would reduce the size of this 536870912 byte file by 0 percent. Chi square distribution for 536870912 samples is 245.05, and randomly would exceed this value 66.16 percent of the times. Arithmetic mean value of data bytes is 127.4980 (127.5 = random). Monte Carlo value for Pi is 3.141413402 (error 0.01 percent). Serial correlation coefficient is 0.000125 (totally uncorrelated = 0.0).