//===========================================================================
// GENERATE_ENTROPY (Black RNG algorithm - see www.RomanBlack.com)
//===========================================================================
void __fastcall generate_entropy(void)
{
long i=0;
long e=0;
long pos=0;
long next_pos=0;
long engine_pos=0;
long engine_last_pos=0;
unsigned char mask=0;
unsigned char engine_value=0;
unsigned char engine_last_value=0;
long shuffle_count=0;
long loops=0;
long bar_count=0;
unsigned char c=0;
unsigned char bit=0;
unsigned char last_bit=0;
unsigned char astring[32]; // for reading Edit box!
signed long max_loops=0;
//--------------------------------------------------
// This generates 1Mb of entropy data from the
// 1Mbit seed data;
// starts with 1Mb seed already in seed_data[]
// copies into 8Mb in ent_data[] (this is the working "cache")
// performs the scrambling in ent_data[]
// ends with 8Mb in ent_data[] (1byte=1bit)
// Note! for indexing convenience the engine uses 1byte for each cache bit
//--------------------------------------------------
// clear progress bar
Form1->ProgressBar1->Position = 0;
//--------------------------------------------------
// first clean ent_data[]
for(i=0; i<8000000; i++) ent_data[i] = NULL;
//--------------------------------------------------
// copy 1Mb seed_data[] into 8Mb in ent_data[]
e=0;
for(i=0; i<1000000; i++)
{
// get the char
c = seed_data[i];
// if bit in c is set, then ent_data byte=1;
if( c & 0x80 ) ent_data[e+0] = 1;
if( c & 0x40 ) ent_data[e+1] = 1;
if( c & 0x20 ) ent_data[e+2] = 1;
if( c & 0x10 ) ent_data[e+3] = 1;
if( c & 0x08 ) ent_data[e+4] = 1;
if( c & 0x04 ) ent_data[e+5] = 1;
if( c & 0x02 ) ent_data[e+6] = 1;
if( c & 0x01 ) ent_data[e+7] = 1;
// move to next pos (next 8 bytes) in ent_data
e += 8;
}
//--------------------------------------------------
// next do the scrambling!!
//--------------------------------------------------
// Black RNG scramble procedure;
// 1. exchange the picked up bit at current pos
// 1b. shift that new bit into mask (ie keep record of last 8 bits)
// 2. toggle the following bit
// 3. using X value from engine, move forward X bits
// 4. inc engine one cylinder
// 5. check last 8 exchanged bytes, if == mask; (1 chance in 256)
// 5a. exchange engine value (shuffles engine)
// Repeat (until scrambled enough)
//--------------------------------------------------
#define ENG_SIZE 17 // number of cylinders
// load the engine with our 17 cylinder values (refine these later after testing?)
engine_last_value=9; // held value at the start, ready to exchange
engine[0] = 1; // these values work well enough
engine[1] = 2;
engine[2] = 3;
engine[3] = 1;
engine[4] = 2;
engine[5] = 3;
engine[6] = 4;
engine[7] = 5;
engine[8] = 7;
engine[9] = 1;
engine[10] = 2;
engine[11] = 3;
engine[12] = 6;
engine[13] = 4;
engine[14] = 1;
engine[15] = 3;
engine[16] = 11; // one larger prime value breaks up patterns
//--------------------------------------------------
// prepare to loop and scramble the data
loops = 0;
bit = 0;
last_bit = 0;
mask = 0;
shuffle_count = 0;
// allows user to change max number of loops on main form!
// must read the edit box and convert to a number...
long length = Form1->Edit1->GetTextLen(); // get length
Form1->Edit1->GetTextBuf(astring,(length+1)); // get text, add 1 for NULL
tstring[length] = NULL; // add the null
max_loops = atol(astring); // get as number
if(max_loops > 500)
{
max_loops = 500;
Form1->Edit1->Text = "500";
Form1->Edit1->Repaint();
}
if(max_loops < 1)
{
max_loops =1;
Form1->Edit1->Text = "1";
Form1->Edit1->Repaint();
}
// set up progress bar
Form1->ProgressBar1->Max = max_loops;
// loop and scramble the data
while(loops < max_loops)
{
// 1. exchange the picked up bit at current pos
bit = ent_data[pos]; // pick up new bit
ent_data[pos] = last_bit; // drop old bit
last_bit = bit; // save bit for next exchange
// 1b. shift that new bit into mask (ie keep last 8 bits)
mask = (mask << 1); // shift all bits left
mask = (mask & 0xFE); // safe! force clear bit0
if(bit != 0) mask += 0x01; // put in the new bit
// 2. toggle the following bit
next_pos = (pos + 1); // get pos of the next bit
if(next_pos == 8000000) next_pos=0; // handle cache wrap around
if(ent_data[next_pos] == 0) ent_data[next_pos]=1; // now toggle it
else ent_data[next_pos]=0;
// 3. using X value from engine cylinder, move forward X bits
pos += engine[engine_pos]; // move X bits forward
if(pos >= 8000000) // handle wrap
{
pos = (pos - 8000000); // loop pos
loops++; // count 1 more cache loop done!
// also handle progress bar updating here
// update progress bar every 5 loops
bar_count++;
if(bar_count >= 5)
{
Form1->ProgressBar1->Position = loops;
bar_count = 0;
}
}
// 4. inc engine one more cylinder, loop if needed
engine_pos++;
if(engine_pos >= ENG_SIZE) engine_pos=0;
// 5. check last 8 exchanged bytes; if == mask; (1 chance in 256) then;
// 5a. exchange engine value (shuffles engine)
if(mask == 0x2C) // b'0010 1100' randomish mask value
{
// shuffle (exchange) an engine cylinder value
engine_value = engine[engine_pos]; // save a copy of the current cylinder
engine[engine_pos] = engine_last_value; // held cyl -> current cyl
engine_last_value = engine_value; // current cyl -> held cyl
shuffle_count++; // count how many times engine cylinders have been shuffled
}
}
//--------------------------------------------------
// all done!
Form1->ProgressBar1->Position = Form1->ProgressBar1->Max;
sprintf(tstring,"Loops: %ld Engine shuffles: %ld",
loops,shuffle_count);
Form1->Label3->Caption = tstring; // display stats
}
//---------------------------------------------------------------------------
Entropy testing results from Black RNG - 25 Nov 2010
Black RNG engine stats;
seed = just a text file
cache = 8000000 bits
cylinders = 17+1held; 1,2,3,1,2,3,4,5,7,1,2,3,6,4,1,3,11 + 9
method to extract random data; just test the cache contents after X loops
Results generated by John Walker's test software ENT.exe 2008,
see;
http://www.fourmilab.ch/random/
Results of 6 tests of first 120000 bytes of cache contents using the same seed,
after the RNG has done X loops through the cache;
Loops Entropy; Compress CHI square CHI square Arithmetic Monte Serial
through (8 perfect) able distrib. percent Mean Carlo Pi Correlation
cache; percent (256 (>10 & <90 (127.5 (0 percent (0 perfect)
(0 perfect) perfect?) perfect) perfect) perfect)
10 7.998496 0 249.85 57.93 127.5368 0.03 -0.001880
12 7.998601 0 232.34 84.25 127.3019 0.45 -0.002216
30 7.998421 0 262.36 36.23 127.2908 0.06 0.003228
100 7.998554 0 240.86 72.86 127.6494 0.43 -0.003508
200 7.998371 0 270.20 24.52 127.5329 0.25 0.000122
500 7.998429 0 261.70 37.32 127.2676 0.04 -0.000545
My explanation of the test values, much is copied from John Walker's page;
Entropy; The information density of the contents of the file, expressed as a number of bits
per character. A value of 8 means the randomness is so dense (so few patterns) the data cannot
be compressed.
Compressable percent; Same as above.
Chi Square test; The chi-square test is the most commonly used test for the randomness of data,
and is extremely sensitive to errors in pseudorandom sequence generators. If the percentage
is greater than 99% or less than 1%, the sequence is almost certainly not random. If the
percentage is between 99% and 95% or between 1% and 5%, the sequence is suspect.
Percentages between 90% and 95% and 5% and 10% indicate the sequence is almost suspect.
From my limited understanding of statistics math this value will change greatly according
to the actual contents of the data being tested and there is no "perfect" value, but percentages
between 10 and 90 are considered officially statistically "random". (see examples below!)
The low-order 8 bits returned by the standard Unix rand() function, yields;
Chi square distribution for 500000 samples is 0.01, and randomly would exceed this value more
than 99.99 percent of the times.
While an improved generator [Park & Miller] reports;
Chi square distribution for 500000 samples is 212.53, and randomly would exceed this value
97.53 percent of the times.
Chi-square result of a genuine random sequence created by timing radioactive decay events;
Chi square distribution for 500000 samples is 249.51, and randomly would exceed this value
40.98 percent of the times.
Black RNG (after as few as 10 loops, matches performance of a radioactive decay RNG!);
Chi square distribution for 120000 samples is 249.85, and randomly would exceed this value
57.93 percent of the times.
Arithmetic Mean; This is simply the result of summing the all the bytes in the file and
dividing by the file length. If the data is close to random, this should be about 127.5.
If the mean departs from this value, the numbers generated are consistently high or low.
Monte Carlo Pi; Each successive sequence of six bytes is used as 24 bit X and Y co-ordinates
within a square. If the distance of the randomly-generated point is less than the radius of
a circle inscribed within the square, the six-byte sequence is considered a *hit*.
The percentage of hits can be used to calculate the value of Pi. For very large streams
(this approximation converges very slowly), the value will approach the correct value of
Pi if the sequence is close to random. A 500000 byte file created by radioactive decay yielded:
Monte Carlo value for Pi is 3.143580574 (error 0.06 percent).
Serial Correlation Coefficient;
This quantity measures the extent to which each byte in the file depends upon the previous byte.
For random sequences, this value (which can be positive or negative) will, of course,
be close to zero. A non-random byte stream such as a C program will yield a serial correlation
coefficient on the order of 0.5. Wildly predictable data such as uncompressed bitmaps will
exhibit serial correlation coefficients approaching 1.