diff --git a/Tets_Python/Main.py b/Tets_Python/Main.py new file mode 100644 index 0000000..7cda937 --- /dev/null +++ b/Tets_Python/Main.py @@ -0,0 +1,58 @@ +from TotOnline import TotOnline +from StartUpTest import StartUPTest + + +class Main: + + def __init__(self): + print("Main class instantiated") + + +if __name__ == "__main__": + filename = "resources/SmallTest.txt" + with open(filename, "rb") as f: + binary_data = f.read().strip() + + filename = "resources/data2.txt" + with open(filename, "rb") as f: + binary_data2 = f.read().strip() + + # Call the StarUpTest + print("StartUp:") + result = StartUPTest.monobit_test(binary_data2) + print("p_value:", result[0]) + print("test passed (p_value >= 0.01):", result[1]) + + result = StartUPTest.test5(binary_data2) + print("Z_tau:", result[0]) + print("test passed (Z_tau > 2326 and Z_tau < 2674):", result[1]) + +# Call the TotalFailure-Test + + print("Total Failure:") + result = TotOnline.total_failure(binary_data, pattern_length=10) + print("p_value:", result[0]) + print("test passed (p_value >= 0.01):", result[1]) + +# Call the Online Test + print("Monobit:") + result = TotOnline.monobit_test(binary_data) + print("p_value:", result[0]) + print("test passed (p_value >= 0.01):", result[1]) + + print("Block Frequency:") + result = TotOnline.block_frequency(binary_data) + print("p_value:", result[0]) + print("test passed (p_value >= 0.01):", result[1]) + + print("Run:") + result = TotOnline.run_test(binary_data) + print("p_value:", result[0]) + print("test passed (p_value >= 0.01):", result[1]) + + print("Longest Run:") + result = TotOnline.longest_one_block_test(binary_data) + print("p_value:", result[0]) + print("test passed (p_value >= 0.01):", result[1]) + + diff --git a/Tets_Python/README.md b/Tets_Python/README.md new file mode 100644 index 0000000..49f56a4 --- /dev/null +++ b/Tets_Python/README.md @@ -0,0 +1,2 @@ +Die Main Klasse fällt nacher weg und wird vollständig im restlichen Code zur Erstellung und Verarbeitung der Zufallszahlen integriert. +Aktuell werden die Daten für die Tests aus zwei verschiedenen Dateien eingelesen, da der Startup Test eine deutlich größere Menge an Zahlen benötigt (hier mindestens 20.000 Bits der Total Failure/Online Test nur mindestens 128 Bits) \ No newline at end of file diff --git a/Tets_Python/StartUpTest.py b/Tets_Python/StartUpTest.py new file mode 100644 index 0000000..e1b3958 --- /dev/null +++ b/Tets_Python/StartUpTest.py @@ -0,0 +1,88 @@ +from math import fabs as fabs +from math import sqrt as sqrt +from scipy.special import erfc as erfc + + +class StartUPTest: + + @staticmethod + def monobit_test(binary_data: str): + + length_of_bit_string = len(binary_data) + + # Variable for S(n) + count = 0 + # Iterate each bit in the string and compute for S(n) + for bit in binary_data: + if bit == 48: + # If bit is 0, then -1 from the S(n) + count -= 1 + elif bit == 49: + # If bit is 1, then +1 to the S(n) + count += 1 + + # Compute the test statistic + sObs = count / sqrt(length_of_bit_string) + + # Compute p-Value + p_value = erfc(fabs(sObs) / sqrt(2)) + + # return a p_value and randomness result + return (p_value, (p_value >= 0.01)) + + @staticmethod + def test5(binary_data: str): + + ShiftFeld = [0] * 5000 + MaxKorrFeld = [0] * 5000 + + # Fill BitFeldB with data + + for tau in range(1, 5001): + Z_tau = 0 + for i in range(5000): + Z_tau += binary_data[i] ^ binary_data[i + tau] + ShiftFeld[tau - 1] = Z_tau + + #Debugging + #for i in range(5000): + # print(ShiftFeld[i], end=' ') + + # Find the index of the maximum deviation from 2500 + max_deviation = 0 + for tau in range(5000): + deviation = abs(ShiftFeld[tau] - 2500) + if deviation > max_deviation: + max_deviation = deviation + + # Find all indices with the maximum deviation + j = 0 + for tau in range(5000): + deviation = abs(ShiftFeld[tau] - 2500) + if deviation == max_deviation: + MaxKorrFeld[j] = tau + j += 1 + + print("Maximale Z_tau-Abweichung von 2500:", max_deviation) + print("Aufgetreten für Shifts:") + for k in range(j): + print("Shift:", MaxKorrFeld[k] + 1) + + tau = MaxKorrFeld[0] + Z_tau = 0 + for i in range(10000, 15000): + Z_tau += StartUPTest.charToInt(i, binary_data) ^ StartUPTest.charToInt(i + tau + 1, binary_data) + tau += 1 + + ok = Z_tau > 2326 and Z_tau < 2674 + return (Z_tau, ok) + + @staticmethod + def charToInt(index, binary_data: str): + value = 0 + if binary_data[index] == 49: + value = 1 + else: + value = 0 + return value + diff --git a/Tets_Python/TotOnline.py b/Tets_Python/TotOnline.py new file mode 100644 index 0000000..a09e398 --- /dev/null +++ b/Tets_Python/TotOnline.py @@ -0,0 +1,227 @@ +from math import log as log +from numpy import zeros as zeros +from math import fabs as fabs +from math import floor as floor +from math import sqrt as sqrt +from scipy.special import erfc as erfc +from scipy.special import gammaincc as gammaincc + + +class TotOnline: + @staticmethod + def total_failure(binary_data: str, pattern_length=10): + + length_of_binary_data = len(binary_data) + + # Augment the n-bit sequence to create n overlapping m-bit sequences by appending m-1 bits + # from the beginning of the sequence to the end of the sequence. + binary_data += binary_data[:pattern_length + 1:] + + # Get max length one patterns for m, m-1, m-2 + max_pattern = '' + for i in range(pattern_length + 2): + max_pattern += '1' + + # Keep track of each pattern's frequency (how often it appears) + vobs_01 = zeros(int(max_pattern[0:pattern_length:], 2) + 1) + vobs_02 = zeros(int(max_pattern[0:pattern_length + 1:], 2) + 1) + + for i in range(length_of_binary_data): + # Work out what pattern is observed + vobs_01[int(binary_data[i:i + pattern_length:], 2)] += 1 + vobs_02[int(binary_data[i:i + pattern_length + 1:], 2)] += 1 + + # Calculate the test statistics and p values + vobs = [vobs_01, vobs_02] + + sums = zeros(2) + for i in range(2): + for j in range(len(vobs[i])): + if vobs[i][j] > 0: + sums[i] += vobs[i][j] * log(vobs[i][j] / length_of_binary_data) + sums /= length_of_binary_data + ape = sums[0] - sums[1] + + xObs = 2.0 * length_of_binary_data * (log(2) - ape) + + p_value = gammaincc(pow(2, pattern_length - 1), xObs / 2.0) + + return (p_value, (p_value >= 0.01)) + + @staticmethod + def monobit_test(binary_data: str): + + length_of_bit_string = len(binary_data) + + # Variable for S(n) + count = 0 + # Iterate each bit in the string and compute for S(n) + for bit in binary_data: + if bit == 48: + # If bit is 0, then -1 from the S(n) + count -= 1 + elif bit == 49: + # If bit is 1, then +1 to the S(n) + count += 1 + + # Compute the test statistic + sObs = count / sqrt(length_of_bit_string) + + # Compute p-Value + p_value = erfc(fabs(sObs) / sqrt(2)) + + # return a p_value and randomness result + return (p_value, (p_value >= 0.01)) + + @staticmethod + def block_frequency(binary_data: str, block_size=128): + + length_of_bit_string = len(binary_data) + + if length_of_bit_string < block_size: + block_size = length_of_bit_string + + # Compute the number of blocks based on the input given. Discard the remainder + number_of_blocks = floor(length_of_bit_string / block_size) + + if number_of_blocks == 1: + # For block size M=1, this test degenerates to test 1, the Frequency (Monobit) test. + return TotOnline.monobit_test(binary_data[0:block_size]) + + # Initialized variables + block_start = 0 + block_end = block_size + proportion_sum = 0.0 + + # Create a for loop to process each block + for counter in range(number_of_blocks): + # Partition the input sequence and get the data for block + block_data = binary_data[block_start:block_end] + + # Determine the proportion 蟺i of ones in each M-bit + one_count = 0 + for bit in block_data: + if bit == 49: + one_count += 1 + # compute π + pi = one_count / block_size + + # Compute Σ(πi -½)^2. + proportion_sum += pow(pi - 0.5, 2.0) + + # Next Block + block_start += block_size + block_end += block_size + + # Compute 4M Σ(πi -½)^2. + result = 4.0 * block_size * proportion_sum + + # Compute P-Value + p_value = gammaincc(number_of_blocks / 2, result / 2) + + return (p_value, (p_value >= 0.01)) + + @staticmethod + def run_test(binary_data: str): + + one_count = 0 + vObs = 0 + length_of_binary_data = len(binary_data) + + # Predefined tau = 2 / sqrt(n) + tau = 2 / sqrt(length_of_binary_data) + + # Step 1 - Compute the pre-test proportion πof ones in the input sequence: π = Σjεj / n + one_count = binary_data.count(49) + + pi = one_count / length_of_binary_data + + # Step 2 - If it can be shown that absolute value of (π - 0.5) is greater than or equal to tau + # then the run test need not be performed. + if abs(pi - 0.5) >= tau: + return (0.0000) + else: + # Step 3 - Compute vObs + for item in range(1, length_of_binary_data): + if binary_data[item] != binary_data[item - 1]: + vObs += 1 + vObs += 1 + + # Step 4 - Compute p_value = erfc((|vObs − 2nπ * (1−π)|)/(2 * sqrt(2n) * π * (1−π))) + p_value = erfc(abs(vObs - (2 * (length_of_binary_data) * pi * (1 - pi))) / (2 * sqrt(2 * length_of_binary_data) * pi * (1 - pi))) + + return (p_value, (p_value > 0.01)) + + @staticmethod + def longest_one_block_test(binary_data: str): + + length_of_binary_data = len(binary_data) + # print('Length of binary string: ', length_of_binary_data) + + # Initialized k, m. n, pi and v_values + if length_of_binary_data < 128: + # Not enough data to run this test + return (0.00000, 'Error: Not enough data to run this test') + elif length_of_binary_data < 6272: + k = 3 + m = 8 + v_values = [1, 2, 3, 4] + pi_values = [0.2148, 0.3672, 0.2305, 0.1875] + elif length_of_binary_data < 750000: + k = 5 + m = 128 + v_values = [4, 5, 6, 7, 8, 9] + pi_values = [0.1174, 0.2430, 0.2493, 0.1752, 0.1027, 0.1124] + else: + # If length_of_bit_string > 750000 + k = 6 + m = 10000 + v_values = [10, 11, 12, 13, 14, 15, 16] + pi_values = [0.0882, 0.2092, 0.2483, 0.1933, 0.1208, 0.0675, 0.0727] + + number_of_blocks = floor(length_of_binary_data / m) + block_start = 0 + block_end = m + xObs = 0 + # This will intialized an array with a number of 0 you specified. + frequencies = zeros(k + 1) + + # print('Number of Blocks: ', number_of_blocks) + + for count in range(number_of_blocks): + block_data = binary_data[block_start:block_end] + max_run_count = 0 + run_count = 0 + + # This will count the number of ones in the block + for bit in block_data: + if bit == 49: + run_count += 1 + max_run_count = max(max_run_count, run_count) + else: + max_run_count = max(max_run_count, run_count) + run_count = 0 + + max(max_run_count, run_count) + + #print('Block Data: ', block_data, '. Run Count: ', max_run_count) + if max_run_count < v_values[0]: + frequencies[0] += 1 + for j in range(k): + if max_run_count == v_values[j]: + frequencies[j] += 1 + if max_run_count > v_values[k - 1]: + frequencies[k] += 1 + + block_start += m + block_end += m + + # print("Frequencies: ", frequencies) + # Compute xObs + for count in range(len(frequencies)): + xObs += pow((frequencies[count] - (number_of_blocks * pi_values[count])), 2.0) / ( + number_of_blocks * pi_values[count]) + + p_value = gammaincc(float(k / 2), float(xObs / 2)) + + return (p_value, (p_value > 0.01)) \ No newline at end of file