diff --git a/lab_2/RandomGenerator.java b/lab_2/RandomGenerator.java new file mode 100644 index 00000000..66561542 --- /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/random.cpp b/lab_2/random.cpp new file mode 100644 index 00000000..3dd49512 --- /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/results.json b/lab_2/results.json new file mode 100644 index 00000000..bc6b2fc4 --- /dev/null +++ b/lab_2/results.json @@ -0,0 +1,14 @@ +{ + "cpp": { + "sequence": "10001111101110111111001001001100101000101110100101001111000001010011001000001011111000011110110110110001110101100100010001000001", + "frequency_bit_test": 0.8596837951986662, + "identical_consecutive_bits_test": 0.7256806886581656, + "longest_sequence_in_block_test": 0.9693053066778472 + }, + "java": { + "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/sequence_cpp.txt b/lab_2/sequence_cpp.txt new file mode 100644 index 00000000..41152f73 --- /dev/null +++ b/lab_2/sequence_cpp.txt @@ -0,0 +1 @@ +10001111101110111111001001001100101000101110100101001111000001010011001000001011111000011110110110110001110101100100010001000001 diff --git a/lab_2/sequence_java.txt b/lab_2/sequence_java.txt new file mode 100644 index 00000000..40cf8006 --- /dev/null +++ b/lab_2/sequence_java.txt @@ -0,0 +1 @@ +00111101111000001011100110110011000110101101111010101100111100110001011000101011111111001111000110011010111010101010111111010011 diff --git a/lab_2/settings.json b/lab_2/settings.json new file mode 100644 index 00000000..6afa03c4 --- /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 new file mode 100644 index 00000000..205075b6 --- /dev/null +++ b/lab_2/tests.py @@ -0,0 +1,138 @@ +import math +from scipy.special import gammaincc +from utility_file import * + + +def frequency_bit_test(sequence: str) -> float: + """ + Выполняет частотный побитовый тест NIST. + :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)) + + return p_value + + +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 + + 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], block_size: int = 8) -> float: + """ + Выполняет тест на самую длинную последовательность единиц в блоке. + :param sequence: бинарная последовательность + :param pi: список вероятностных констант + :param block_size: размер блока (по умолчанию 8) + :return: P-значение теста + """ + 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'") + + 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 + 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] - 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 + + +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(): + 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__": + main() diff --git a/lab_2/utility_file.py b/lab_2/utility_file.py new file mode 100644 index 00000000..aa72c63a --- /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().strip() + 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}")