From 3ddb4fd9ab1a51c5e4bbcd32ab3c36ec4d45f873 Mon Sep 17 00:00:00 2001 From: Vera Date: Fri, 12 Sep 2025 20:15:54 +0300 Subject: [PATCH 1/8] generate random sequence on cpp --- lab_2/random.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++ lab_2/sequence_cpp.txt | 1 + 2 files changed, 54 insertions(+) create mode 100644 lab_2/random.cpp create mode 100644 lab_2/sequence_cpp.txt diff --git a/lab_2/random.cpp b/lab_2/random.cpp new file mode 100644 index 000000000..3dd495121 --- /dev/null +++ b/lab_2/random.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +/** +* Generates a random binary sequence of 128 bit +* +* @return Generated binary sequence as a string +*/ +std::string generate_seq() { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution number(0, 1); + + std::string sequence; + + for (size_t i = 0; i < 128; ++i) { + sequence += std::to_string(number(gen)); + } + + return sequence; +} + +/** +* Saves a sequence to the specified file +* +* @param filename Path to the file to save +* @param sequence Sequence to save +*/ +void save_seq(const std::string& filename, const std::string& sequence) { + try { + std::ofstream out; + out.open(filename); + + if (!out.is_open()) { + throw std::runtime_error("Failed to open file " + filename); + } + + out << sequence << std::endl; + out.close(); + } + catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + } +} + +int main() { + std::string sequence = generate_seq(); + save_seq("sequence_cpp.txt", sequence); + + return 0; +} diff --git a/lab_2/sequence_cpp.txt b/lab_2/sequence_cpp.txt new file mode 100644 index 000000000..41152f73c --- /dev/null +++ b/lab_2/sequence_cpp.txt @@ -0,0 +1 @@ +10001111101110111111001001001100101000101110100101001111000001010011001000001011111000011110110110110001110101100100010001000001 From 7c08bcf5e737555d712e2f8e11135cebeb381d0e Mon Sep 17 00:00:00 2001 From: Vera Date: Fri, 12 Sep 2025 21:13:55 +0300 Subject: [PATCH 2/8] generate random sequence on java --- lab_2/RandomGenerator.java | 47 ++++++++++++++++++++++++++++++++++++++ lab_2/sequence_java.txt | 1 + 2 files changed, 48 insertions(+) create mode 100644 lab_2/RandomGenerator.java create mode 100644 lab_2/sequence_java.txt diff --git a/lab_2/RandomGenerator.java b/lab_2/RandomGenerator.java new file mode 100644 index 000000000..665615425 --- /dev/null +++ b/lab_2/RandomGenerator.java @@ -0,0 +1,47 @@ +import java.io.FileWriter; +import java.io.IOException; +import java.util.Random; + +public class RandomGenerator { + + /** + * Generates a random binary sequence of 128 bit + * + * @return Generated binary sequence as a string + */ + public static String generate_seq() { + Random rand = new Random(); + StringBuilder sequence = new StringBuilder(); + + for (int i = 0; i < 128; ++i) { + sequence.append(rand.nextInt(2)); + } + + return sequence.toString(); + } + + /** + * Saves a sequence to the specified file + * + * @param filename Path to the file to save + * @param sequence Sequence to save + */ + public static void save_seq(String filename, String sequence) { + try (FileWriter writer = new FileWriter(filename)) { + writer.write(sequence + "\n"); + } + catch (IOException e) { + System.err.println(e.getMessage()); + } + } + + /** + * Main method of the program + * + * @param args Command line arguments (not used) + */ + public static void main(String[] args) { + String sequence = generate_seq(); + save_seq("sequence_java.txt", sequence); + } +} diff --git a/lab_2/sequence_java.txt b/lab_2/sequence_java.txt new file mode 100644 index 000000000..40cf80069 --- /dev/null +++ b/lab_2/sequence_java.txt @@ -0,0 +1 @@ +00111101111000001011100110110011000110101101111010101100111100110001011000101011111111001111000110011010111010101010111111010011 From ea0ad9912d2b000c18e2e63e974a4f6d65065a9b Mon Sep 17 00:00:00 2001 From: Vera Date: Fri, 12 Sep 2025 22:53:43 +0300 Subject: [PATCH 3/8] NIST tests --- lab_2/tests.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 lab_2/tests.py diff --git a/lab_2/tests.py b/lab_2/tests.py new file mode 100644 index 000000000..26ef822b8 --- /dev/null +++ b/lab_2/tests.py @@ -0,0 +1,73 @@ +import math +from scipy.special import gammaincc + + +def frequency_bit_test(sequence: str) -> float: + """ + Выполняет частотный побитовый тест NIST. + :param sequence: бинарная последовательность + :return: P-значение теста + """ + s_n = (sequence.count('1') - sequence.count('0')) / math.sqrt(len(sequence)) + p_value = math.erfc(s_n / math.sqrt(2)) + + return p_value + + +def identical_consecutive_bits_test(sequence: str) -> float: + """ + Выполняет тест на одинаковые подряд идущие биты. + :param sequence: бинарная последовательность + :return: P-значение теста + """ + n = len(sequence) + zeta = sequence.count('1') / n + + if abs(zeta - 0.5) >= 2 /math.sqrt(n): + return 0.0 + + v_n = sum(1 if sequence[i] != sequence[i + 1] else 0 for i in range(n - 1)) + p_value = math.erfc(abs(v_n - 2 * n * zeta * (1 - zeta)) / (2 * math.sqrt(2 * n) * zeta * (1 - zeta))) + + return p_value + + +def longest_sequence_in_block_test(sequence: str, pi: list[float]) -> float: + """ + Выполняет тест на самую длинную последовательность единиц в блоке. + :param sequence: бинарная последовательность + :param pi: список вероятностных констант + :return: P-значение теста + """ + blocks = [] + for i in range(0, len(sequence), 8): + block = sequence[i:i + 8] + blocks.append(block) + + v = [0, 0, 0, 0] + + for block in blocks: + max_len = 0 + cur_len = 0 + + for bit in block: + if bit == '1': + cur_len += 1 + max_len = max(max_len, cur_len) + else: + cur_len = 0 + + match max_len: + case x if x <= 1: + v[0] += 1 + case 2: + v[1] += 1 + case 3: + v[2] += 1 + case _: + v[3] += 1 + + chi2 = sum(((v[i] - 16 * pi[i]) ** 2) / (16 * pi[i]) for i in range(4)) + p_value = gammaincc(3 / 2, chi2/ 2) + + return p_value From 936ee82fd74e09e1d13112d7037f4e5bdee34bbc Mon Sep 17 00:00:00 2001 From: Vera Date: Sat, 13 Sep 2025 00:28:26 +0300 Subject: [PATCH 4/8] main() --- lab_2/results.json | 14 +++++++++++++ lab_2/settings.json | 6 ++++++ lab_2/tests.py | 42 +++++++++++++++++++++++++++++++++++---- lab_2/utility_file.py | 46 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 lab_2/results.json create mode 100644 lab_2/settings.json create mode 100644 lab_2/utility_file.py diff --git a/lab_2/results.json b/lab_2/results.json new file mode 100644 index 000000000..aa70e500a --- /dev/null +++ b/lab_2/results.json @@ -0,0 +1,14 @@ +{ + "cpp": { + "sequence": "10001111101110111111001001001100101000101110100101001111000001010011001000001011111000011110110110110001110101100100010001000001\n", + "frequency_bit_test": 0.8602230946114309, + "identical_consecutive_bits_test": 0.7963048833467226, + "longest_sequence_in_block_test": 0.8331960038394477 + }, + "java": { + "sequence": "00111101111000001011100110110011000110101101111010101100111100110001011000101011111111001111000110011010111010101010111111010011\n", + "frequency_bit_test": 0.052746322030963806, + "identical_consecutive_bits_test": 0.5615703198002073, + "longest_sequence_in_block_test": 0.16663886776067788 + } +} \ No newline at end of file diff --git a/lab_2/settings.json b/lab_2/settings.json new file mode 100644 index 000000000..6afa03c4d --- /dev/null +++ b/lab_2/settings.json @@ -0,0 +1,6 @@ +{ + "CPP": "sequence_cpp.txt", + "JAVA": "sequence_java.txt", + "RESULTS": "results.json", + "PI": [0.2148, 0.3672, 0.2305, 0.1875] +} \ No newline at end of file diff --git a/lab_2/tests.py b/lab_2/tests.py index 26ef822b8..a9160afe9 100644 --- a/lab_2/tests.py +++ b/lab_2/tests.py @@ -1,5 +1,6 @@ import math from scipy.special import gammaincc +from utility_file import * def frequency_bit_test(sequence: str) -> float: @@ -8,7 +9,7 @@ def frequency_bit_test(sequence: str) -> float: :param sequence: бинарная последовательность :return: P-значение теста """ - s_n = (sequence.count('1') - sequence.count('0')) / math.sqrt(len(sequence)) + s_n = abs(sequence.count('1') - sequence.count('0')) / math.sqrt(len(sequence)) p_value = math.erfc(s_n / math.sqrt(2)) return p_value @@ -67,7 +68,40 @@ def longest_sequence_in_block_test(sequence: str, pi: list[float]) -> float: case _: v[3] += 1 - chi2 = sum(((v[i] - 16 * pi[i]) ** 2) / (16 * pi[i]) for i in range(4)) - p_value = gammaincc(3 / 2, chi2/ 2) + chi2 = sum(((v[i] - 16 * pi[i]) ** 2) / (16 * pi[i]) for i in range(4)) + p_value = gammaincc(3 / 2, chi2/ 2) - return p_value + return p_value + + +def analyze_sequence(sequence: str, pi: list[float]) -> dict: + """ + Выполняет тесты NIST для данной последовательности. + :param sequence: бинарная последовательность + :param pi: список вероятностных констант + :return: словарь с результатами тестов + """ + return { + "sequence": sequence, + "frequency_bit_test": frequency_bit_test(sequence), + "identical_consecutive_bits_test": identical_consecutive_bits_test(sequence), + "longest_sequence_in_block_test": longest_sequence_in_block_test(sequence, pi) + } + + +def main(): + source = load_json("settings.json") + cpp_seq = read_txt(source["CPP"]) + java_seq = read_txt(source["JAVA"]) + + results = { + "cpp": analyze_sequence(cpp_seq, source["PI"]), + "java": analyze_sequence(java_seq, source["PI"]) + } + + write_json(results, source["RESULTS"]) + print(f"Результаты сохранены в {source["RESULTS"]}") + + +if __name__ == "__main__": + main() diff --git a/lab_2/utility_file.py b/lab_2/utility_file.py new file mode 100644 index 000000000..3d365f6ce --- /dev/null +++ b/lab_2/utility_file.py @@ -0,0 +1,46 @@ +import json + + +def read_txt(filename: str) -> str: + """ + Читает содержимое текстового файла. + :param filename: путь к файлу + :return: строка с содержимым файла + """ + try: + with open(filename, "r", encoding="utf-8") as file: + return file.read() + except FileNotFoundError: + print(f"File {filename} not found") + except Exception as e: + print(f"An error occurred while reading the file {filename}: {e}") + + +def load_json(filename: str) -> dict: + """ + Загружает данные из json-файла. + :param filename: путь к файлу + :return: словарь с содержимым файла + """ + try: + with open(filename, "r", encoding="utf-8") as file: + return json.load(file) + except FileNotFoundError: + print(f"File {filename} not found") + except json.JSONDecodeError: + print(f"File {filename} isn't correct JSON") + except Exception as e: + print(f"An error occurred while reading the file {filename}: {e}") + + +def write_json(data: dict, filename: str) -> None: + """ + Записывает данные в json-файл. + :param data: словарь с данными для записи + :param filename: путь к файлу, в который будут записаны данные + """ + try: + with open(filename, "w", encoding="utf-8") as file: + json.dump(data, file, indent=4, ensure_ascii=False) + except Exception as e: + print(f"An error occurred while saving the file {filename}: {e}") From 9eb0d18341497a62115eb2006f3d6a74b8a58a99 Mon Sep 17 00:00:00 2001 From: Vera Date: Sat, 13 Sep 2025 02:09:47 +0300 Subject: [PATCH 5/8] exceptions --- lab_2/tests.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lab_2/tests.py b/lab_2/tests.py index a9160afe9..a5c210881 100644 --- a/lab_2/tests.py +++ b/lab_2/tests.py @@ -90,17 +90,20 @@ def analyze_sequence(sequence: str, pi: list[float]) -> dict: def main(): - source = load_json("settings.json") - cpp_seq = read_txt(source["CPP"]) - java_seq = read_txt(source["JAVA"]) - - results = { - "cpp": analyze_sequence(cpp_seq, source["PI"]), - "java": analyze_sequence(java_seq, source["PI"]) - } - - write_json(results, source["RESULTS"]) - print(f"Результаты сохранены в {source["RESULTS"]}") + try: + source = load_json("settings.json") + cpp_seq = read_txt(source["CPP"]) + java_seq = read_txt(source["JAVA"]) + + results = { + "cpp": analyze_sequence(cpp_seq, source["PI"]), + "java": analyze_sequence(java_seq, source["PI"]) + } + + write_json(results, source["RESULTS"]) + print(f"Результаты сохранены в {source["RESULTS"]}") + except Exception as e: + print(f"Error: {e}") if __name__ == "__main__": From 2f7bcb4ad5e73ad22a382fd44da447dfd453cc2f Mon Sep 17 00:00:00 2001 From: Vera Date: Mon, 15 Sep 2025 02:37:42 +0300 Subject: [PATCH 6/8] fixes --- lab_2/results.json | 16 ++++++++-------- lab_2/tests.py | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lab_2/results.json b/lab_2/results.json index aa70e500a..bc6b2fc46 100644 --- a/lab_2/results.json +++ b/lab_2/results.json @@ -1,14 +1,14 @@ { "cpp": { - "sequence": "10001111101110111111001001001100101000101110100101001111000001010011001000001011111000011110110110110001110101100100010001000001\n", - "frequency_bit_test": 0.8602230946114309, - "identical_consecutive_bits_test": 0.7963048833467226, - "longest_sequence_in_block_test": 0.8331960038394477 + "sequence": "10001111101110111111001001001100101000101110100101001111000001010011001000001011111000011110110110110001110101100100010001000001", + "frequency_bit_test": 0.8596837951986662, + "identical_consecutive_bits_test": 0.7256806886581656, + "longest_sequence_in_block_test": 0.9693053066778472 }, "java": { - "sequence": "00111101111000001011100110110011000110101101111010101100111100110001011000101011111111001111000110011010111010101010111111010011\n", - "frequency_bit_test": 0.052746322030963806, - "identical_consecutive_bits_test": 0.5615703198002073, - "longest_sequence_in_block_test": 0.16663886776067788 + "sequence": "00111101111000001011100110110011000110101101111010101100111100110001011000101011111111001111000110011010111010101010111111010011", + "frequency_bit_test": 0.05182992721790971, + "identical_consecutive_bits_test": 0.5985061525910559, + "longest_sequence_in_block_test": 0.07923339282255983 } } \ No newline at end of file diff --git a/lab_2/tests.py b/lab_2/tests.py index a5c210881..7f70d6777 100644 --- a/lab_2/tests.py +++ b/lab_2/tests.py @@ -92,8 +92,8 @@ def analyze_sequence(sequence: str, pi: list[float]) -> dict: def main(): try: source = load_json("settings.json") - cpp_seq = read_txt(source["CPP"]) - java_seq = read_txt(source["JAVA"]) + cpp_seq = read_txt(source["CPP"])[:128] + java_seq = read_txt(source["JAVA"])[:128] results = { "cpp": analyze_sequence(cpp_seq, source["PI"]), From 48227585bb30755b09350659d4aeed7c3c92cab0 Mon Sep 17 00:00:00 2001 From: Vera Date: Mon, 15 Sep 2025 03:02:58 +0300 Subject: [PATCH 7/8] fixes --- lab_2/tests.py | 4 ++-- lab_2/utility_file.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lab_2/tests.py b/lab_2/tests.py index 7f70d6777..a5c210881 100644 --- a/lab_2/tests.py +++ b/lab_2/tests.py @@ -92,8 +92,8 @@ def analyze_sequence(sequence: str, pi: list[float]) -> dict: def main(): try: source = load_json("settings.json") - cpp_seq = read_txt(source["CPP"])[:128] - java_seq = read_txt(source["JAVA"])[:128] + cpp_seq = read_txt(source["CPP"]) + java_seq = read_txt(source["JAVA"]) results = { "cpp": analyze_sequence(cpp_seq, source["PI"]), diff --git a/lab_2/utility_file.py b/lab_2/utility_file.py index 3d365f6ce..aa72c63a2 100644 --- a/lab_2/utility_file.py +++ b/lab_2/utility_file.py @@ -9,7 +9,7 @@ def read_txt(filename: str) -> str: """ try: with open(filename, "r", encoding="utf-8") as file: - return file.read() + return file.read().strip() except FileNotFoundError: print(f"File {filename} not found") except Exception as e: From 32e67cfb6c02de261813d46cbd8a4a500576d040 Mon Sep 17 00:00:00 2001 From: Vera Date: Mon, 22 Sep 2025 21:54:29 +0300 Subject: [PATCH 8/8] added exceptions & param block_size --- lab_2/tests.py | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/lab_2/tests.py b/lab_2/tests.py index a5c210881..205075b61 100644 --- a/lab_2/tests.py +++ b/lab_2/tests.py @@ -9,6 +9,12 @@ def frequency_bit_test(sequence: str) -> float: :param sequence: бинарная последовательность :return: P-значение теста """ + if not sequence: + raise ZeroDivisionError("Sequence is empty") + + if not all(bit in '01' for bit in sequence): + raise ValueError("Sequence must include only '0' and '1'") + s_n = abs(sequence.count('1') - sequence.count('0')) / math.sqrt(len(sequence)) p_value = math.erfc(s_n / math.sqrt(2)) @@ -21,6 +27,12 @@ def identical_consecutive_bits_test(sequence: str) -> float: :param sequence: бинарная последовательность :return: P-значение теста """ + if not sequence: + raise ZeroDivisionError("Sequence is empty") + + if not all(bit in '01' for bit in sequence): + raise ValueError("Sequence must include only '0' and '1'") + n = len(sequence) zeta = sequence.count('1') / n @@ -33,19 +45,35 @@ def identical_consecutive_bits_test(sequence: str) -> float: return p_value -def longest_sequence_in_block_test(sequence: str, pi: list[float]) -> float: +def longest_sequence_in_block_test(sequence: str, pi: list[float], block_size: int = 8) -> float: """ Выполняет тест на самую длинную последовательность единиц в блоке. :param sequence: бинарная последовательность :param pi: список вероятностных констант + :param block_size: размер блока (по умолчанию 8) :return: P-значение теста """ - blocks = [] - for i in range(0, len(sequence), 8): - block = sequence[i:i + 8] - blocks.append(block) + if block_size <= 0: + raise ValueError("Block size must be a positive integer number") + + if len(sequence) % block_size: + raise ValueError("Sequence length must be a multiple of the block size") + + if not pi: + raise ValueError("List of probabilities is empty") + + if not all(0 < p < 1 for p in pi): + raise ValueError("List of probabilities isn't correct") + + if not sequence: + raise ValueError("Sequence is empty") + + if not all(bit in '01' for bit in sequence): + raise ValueError("Sequence must include only '0' and '1'") - v = [0, 0, 0, 0] + blocks = [sequence[i:i + block_size] for i in range(0, len(sequence), block_size)] + num_blocks = len(sequence) // block_size + v = [0] * len(pi) for block in blocks: max_len = 0 @@ -68,8 +96,8 @@ def longest_sequence_in_block_test(sequence: str, pi: list[float]) -> float: case _: v[3] += 1 - chi2 = sum(((v[i] - 16 * pi[i]) ** 2) / (16 * pi[i]) for i in range(4)) - p_value = gammaincc(3 / 2, chi2/ 2) + chi2 = sum(((v[i] - num_blocks * pi[i]) ** 2) / (num_blocks * pi[i]) for i in range(len(pi))) + p_value = gammaincc((len(pi) - 1) / 2, chi2 / 2) return p_value