Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 74a1dee

Browse files
Added multi-threaded RNG sample generation and results analysis
Results are provided and are very promising!
1 parent b9461d3 commit 74a1dee

6 files changed

Lines changed: 211 additions & 0 deletions

File tree

.idea/dictionaries/project.xml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/rng_tests/Results.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
NOTE: Manually created file to store results of RNG tests. You can run your own test.
2+
3+
PS > python .\rng_test.py
4+
----------------------------------------
5+
Starting RNG tests...
6+
----------------------------------------
7+
8+
Read 10000000 bytes from rng_output.bin
9+
10+
----------------------------------------
11+
Shannon Entropy:
12+
7.9995 bits per byte
13+
----------------------------------------
14+
Frequency test:
15+
Total bits: 80000000
16+
Zeros: 40082720
17+
Ones: 40024692
18+
----------------------------------------
19+
Runs test:
20+
Runs: 39989904
21+
Avg run length: 2.00
22+
Max run length: 21
23+
Min run length: 1
24+
----------------------------------------
25+
Autocorrelation test (lag=1):
26+
Autocorrelation: 0.5001 (expected ~0.5)
27+
----------------------------------------
28+
Autocorrelation test (lag=8):
29+
Autocorrelation: 0.4997 (expected ~0.5)
30+
----------------------------------------
31+
Bit position frequency test:
32+
Bit position 0: set bit frequency = 0.5007 (expected ~0.5)
33+
Bit position 1: set bit frequency = 0.4987 (expected ~0.5)
34+
Bit position 2: set bit frequency = 0.5008 (expected ~0.5)
35+
Bit position 3: set bit frequency = 0.5004 (expected ~0.5)
36+
Bit position 4: set bit frequency = 0.4994 (expected ~0.5)
37+
Bit position 5: set bit frequency = 0.5018 (expected ~0.5)
38+
Bit position 6: set bit frequency = 0.5000 (expected ~0.5)
39+
Bit position 7: set bit frequency = 0.5007 (expected ~0.5)
40+
----------------------------------------

example/rng_tests/generate_bin.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os
2+
import threading
3+
4+
from tqdm import tqdm
5+
6+
import sys
7+
8+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
9+
from pyCTools.hwrng import MaxRNG
10+
11+
# Initialize the MaxRNG instance
12+
rng = MaxRNG()
13+
rng.dll.maxrng_init()
14+
15+
16+
def worker(size, index, results):
17+
try:
18+
data = rng.maxrng_threadsafe(size)
19+
results[index] = data
20+
except RuntimeError as e:
21+
results[index] = e
22+
23+
24+
def save_random_samples(total_bytes=10_000_000, chunk_size=1024):
25+
bytes_generated = 0
26+
27+
with open("rng_output.bin", "wb") as f, tqdm(total=total_bytes, unit="B", unit_scale=True,
28+
desc="Saving random samples") as pbar:
29+
while bytes_generated < total_bytes:
30+
num_cores = os.cpu_count() or 1
31+
num_threads_ = min(num_cores, (total_bytes - bytes_generated + chunk_size - 1) // chunk_size)
32+
33+
results = [None] * num_threads_
34+
threads_ = []
35+
for i_ in range(num_threads_):
36+
size = min(chunk_size, total_bytes - bytes_generated - i_ * chunk_size)
37+
t_ = threading.Thread(target=worker, args=(size, i_, results))
38+
threads_.append(t_)
39+
t_.start()
40+
for t_ in threads_:
41+
t_.join()
42+
for i_, result_ in enumerate(results):
43+
if isinstance(result_, Exception):
44+
raise result_
45+
f.write(result_)
46+
bytes_generated += len(result_)
47+
pbar.update(len(result_))
48+
print(f"Saved {bytes_generated} bytes to rng_output.bin")
49+
50+
51+
save_random_samples()
52+
print("Random samples saved to rng_output.bin")
382 KB
Loading

example/rng_tests/rng_output.bin

9.54 MB
Binary file not shown.

example/rng_tests/rng_test.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import math
2+
3+
4+
def shannon_entropy(data: bytes):
5+
if not data:
6+
return None
7+
freq = {}
8+
for b in data:
9+
freq[b] = freq.get(b, 0) + 1
10+
entropy = 0.0
11+
length = len(data)
12+
for count in freq.values():
13+
p = count / length
14+
entropy -= p * math.log2(p)
15+
print(f"Shannon Entropy:")
16+
print(f" {entropy:.4f} bits per byte")
17+
return None
18+
19+
20+
def frequency_test(data: bytes):
21+
zeros = sum(bin(b).count("0") for b in data)
22+
ones = sum(bin(b).count("1") for b in data)
23+
print("Frequency test:")
24+
print(f" Total bits: {len(data) * 8}")
25+
print(f" Zeros: {zeros}")
26+
print(f" Ones: {ones}")
27+
28+
29+
def runs_test(data: bytes):
30+
bits = ''.join(f'{b:08b}' for b in data)
31+
runs = 1
32+
prev_bit = bits[0]
33+
34+
run_lengths = []
35+
current_run_length = 1
36+
37+
for bit in bits[1:]:
38+
if bit == prev_bit:
39+
current_run_length += 1
40+
else:
41+
run_lengths.append(current_run_length)
42+
current_run_length = 1
43+
runs += 1
44+
prev_bit = bit
45+
run_lengths.append(current_run_length)
46+
47+
avg_run_length = sum(run_lengths) / len(run_lengths)
48+
max_run_length = max(run_lengths)
49+
min_run_length = min(run_lengths)
50+
51+
print("Runs test:")
52+
print(f" Runs: {runs}")
53+
print(f" Avg run length: {avg_run_length:.2f}")
54+
print(f" Max run length: {max_run_length}")
55+
print(f" Min run length: {min_run_length}")
56+
57+
58+
def autocorrelation_test(data: bytes, lag=1):
59+
bits = ''.join(f'{b:08b}' for b in data)
60+
n = len(bits)
61+
matches = 0
62+
for i in range(n - lag):
63+
if bits[i] == bits[i + lag]:
64+
matches += 1
65+
autocorr = matches / (n - lag)
66+
print(f"Autocorrelation test (lag={lag}):")
67+
print(f" Autocorrelation: {autocorr:.4f} (expected ~0.5)")
68+
69+
70+
def bit_position_frequency(data: bytes):
71+
print("Bit position frequency test:")
72+
counts = [0] * 8 # count of set bits per position
73+
total_bytes = len(data)
74+
75+
for b in data:
76+
for i in range(8):
77+
if b & (1 << i):
78+
counts[i] += 1
79+
80+
for i, c in enumerate(counts):
81+
freq = c / total_bytes
82+
print(f" Bit position {i}: set bit frequency = {freq:.4f} (expected ~0.5)")
83+
84+
85+
def main():
86+
print("-" * 40)
87+
print("Starting RNG tests...")
88+
print("-" * 40 + "\n")
89+
filename = "rng_output.bin"
90+
with open(filename, "rb") as f:
91+
data = f.read()
92+
93+
print(f"Read {len(data)} bytes from {filename}\n")
94+
95+
print("-" * 40)
96+
shannon_entropy(data)
97+
print("-" * 40)
98+
frequency_test(data)
99+
print("-" * 40)
100+
runs_test(data)
101+
print("-" * 40)
102+
autocorrelation_test(data, lag=1)
103+
print("-" * 40)
104+
autocorrelation_test(data, lag=8)
105+
print("-" * 40)
106+
bit_position_frequency(data)
107+
print("-" * 40)
108+
print("\nAll tests completed.")
109+
110+
111+
if __name__ == "__main__":
112+
main()

0 commit comments

Comments
 (0)