Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ISB_LAB_2/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SIZE_BLOCK = 8
NUMBER_OF_BLOCKS = 16
PI_VALUES = [0.2148, 0.3672, 0.2305, 0.1875]
102 changes: 102 additions & 0 deletions ISB_LAB_2/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import json
from typing import Dict, Any, Tuple
from tester import test_sequence


def read_sequence(filename: str) -> str:
"""
Чтение бинарной последовательности из файла.

Args:
filename: Путь к файлу с последовательностью

Returns:
Бинарная последовательность в виде строки
"""
try:
with open(filename, 'r', encoding='utf-8') as f:
sequence = f.readline().strip()
return sequence
except FileNotFoundError:
raise FileNotFoundError(f"Файл {filename} не найден")
except Exception as e:
raise Exception(f"Ошибка при чтении файла {filename}: {e}")


def write_to_file(random_gen: str, sequence: str, file_path: str, results: Tuple[float, float, float]) -> None:
"""
Запись результатов тестов в файл.

Args:
random_gen: название генератора случайных значений
sequence: тестируемая бинарная последовательность
file_path: путь к файлу, куда запишем результаты тестов
results: кортеж с результатами тестов (p1, p2, p3)
"""
try:
with open(f'{file_path}', 'a', encoding='utf-8') as file:
file.write(f"\nРезультаты тестов для {random_gen}:\n")
file.write(f"Последовательность: {sequence}\n")
file.write(
f"Частотный тест p-значение: {results[0]:.6f} {'(Passed)' if results[0] >= 0.01 else '(Failed)'}\n")
file.write(
f"Тест на серии p-значение: {results[1]:.6f} {'(Passed)' if results[1] >= 0.01 else '(Failed)'}\n")
file.write(
f"Тест на самую длинную серию p-значение: {results[2]:.6f} {'(Passed)' if results[2] >= 0.01 else '(Failed)'}\n")
file.write("-" * 60 + "\n\n")
except FileNotFoundError:
raise FileNotFoundError(f"Файл {file_path} отсутствует")
except Exception as ex:
raise Exception(f"Ошибка при записи в файл: {ex}")


def main() -> int:
"""
Главная функция программы.
Считываются данные конфигурационного файла и запускается тестирование.

Returns:
0 в случае успешного выполнения, 1 при возникновении ошибок.
"""
# Чтение paths.json
try:
with open("paths.json", 'r') as config_file:
config: Dict[str, Any] = json.load(config_file)
except FileNotFoundError:
print("Ошибка: Не удалось открыть файл paths.json!")
return 1
except json.JSONDecodeError as e:
print(f"Ошибка: Не удалось разобрать paths.json: {e}")
return 1

# Получение путей к файлам
try:
cpp_input_file: str = config["input_file_cpp"]
java_input_file: str = config["input_file_java"]
results_file: str = config["results"]
except KeyError as e:
print(f"Ошибка: В paths.json отсутствует ключ '{e}'!")
return 1

# Тестирование последовательностей и запись результатов
try:
# Чтение и тестирование C++ последовательности
cpp_sequence = read_sequence(cpp_input_file)
cpp_results = test_sequence(cpp_input_file)
write_to_file("C++ Генератор", cpp_sequence, results_file, cpp_results)

# Чтение и тестирование Java последовательности
java_sequence = read_sequence(java_input_file)
java_results = test_sequence(java_input_file)
write_to_file("Java Генератор", java_sequence, results_file, java_results)

except Exception as e:
print(f"Ошибка во время тестирования: {e}")
return 1

return 0


if __name__ == "__main__":
exit(main())

1 change: 1 addition & 0 deletions ISB_LAB_2/output/cpp_sequence.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
01011001001001100010100000110010000111101011110100000101110001010101101011001000001010001111001011111101111110100001000010001011
1 change: 1 addition & 0 deletions ISB_LAB_2/output/java_sequence.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
01100011000000010010111011001100100010101100001000010000001110100101010100101010001110101011101100010100011100111101101100011011
5 changes: 5 additions & 0 deletions ISB_LAB_2/paths.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"input_file_cpp": "output/cpp_sequence.txt",
"input_file_java": "output/java_sequence.txt",
"output_dir": "output/"
}
48 changes: 48 additions & 0 deletions ISB_LAB_2/src/generate_cpp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

/**
* @brief Извлекает значение поля input_file_cpp из JSON-файла настроек
*
* Функция выполняет поиск значения по ключу "input_file_cpp" в файле settings.json.
* Обрабатывает базовые ошибки чтения файла и форматирования JSON.
*
* @param filename Путь к файлу settings.json
* @return string Значение поля input_file_cpp или пустая строка в случае ошибки
*/
string getInputFileCpp(const string& filename) {
ifstream file(filename);
if (!file.is_open()) {
cerr << "Error: Could not open " << filename << "!" << endl;
return "";
}

string line;
while (getline(file, line)) {
// Удаляем пробелы и табы для упрощения поиска
string cleaned_line;
for (char c : line) {
if (c != ' ' && c != '\t') {
cleaned_line += c;
}
}

// Ищем ключ input_file_cpp
size_t pos = cleaned_line.find("\"input_file_cpp\":\"");
if (pos != string::npos) {
size_t start = pos + 16; // Длина "\"input_file_cpp\":\""
size_t end = cleaned_line.find("\"", start);
if (end != string::npos) {
file.close();
return cleaned_line.substr(start, end - start);
}
}
}

file.close();
cerr << "Error: 'input_file_cpp' not found or has invalid format in " << filename << "!" << endl;
return "";
}
34 changes: 34 additions & 0 deletions ISB_LAB_2/src/generate_java.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Извлекает значение поля input_file_java из JSON-файла настроек
*
* Функция выполняет поиск значения по ключу "input_file_java" в файле settings.json.
* Обрабатывает исключения ввода-вывода и случаи отсутствия требуемого поля.
* В случае ошибки выводит сообщение в stderr и возвращает пустую строку.
*
* @param filename путь к файлу settings.json
* @return значение поля input_file_java или пустая строка в случае ошибки
*/
public static String getInputFileJava(String filename) {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
// Ищем строку содержащую input_file_java
Comment on lines +11 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не исправлено

В java есть JavaDoc
https://docs.oracle.com/en/java/javase/23/docs/specs/man/javadoc.html

if (line.contains("input_file_java")) {
// Извлекаем значение между кавычками
String[] parts = line.split("\"");
for (int i = 0; i < parts.length - 1; i++) {
if (parts[i].contains("input_file_java")) {
return parts[i + 2]; // Значение после ключа
}
}
}
}

System.err.println("Error: 'input_file_java' not found in " + filename + "!");
return "";

} catch (IOException e) {
System.err.println("Error: Could not read " + filename + "! " + e.getMessage());
return "";
}
}
22 changes: 22 additions & 0 deletions ISB_LAB_2/tester.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from tests import frequency_test, runs_test, longest_run_test


def test_sequence(filename: str) -> tuple[float, float, float]:
"""
Проводит серию тестов для двоичной последовательности.

Args:
filename: Путь к файлу, содержащему тестируемую двоичную последовательность.
Return:
tuple(float, float, float): Кортеж, состоящий из результатов тестов
"""

with open(filename, 'r') as f:
sequence = f.readline().strip()

# Выполнение тестов
p1 = frequency_test(sequence)
p2 = runs_test(sequence)
p3 = longest_run_test(sequence)

return p1, p2, p3
98 changes: 98 additions & 0 deletions ISB_LAB_2/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import math
from scipy import special
from constants import SIZE_BLOCK, PI_VALUES, NUMBER_OF_BLOCKS

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. файл с константами не является исполняемым
  2. этого модуля в данном пул-реквесте вообще нет
    как у вас компилится код?
  3. вынести константы в файл с настройками

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не исправлено


def frequency_test(sequence: str) -> float:
"""
Проводит частотный тест для двоичной последовательности.
Args:
sequence: Строка из двоичных цифр ('0' и '1') для тестирования.
Return:
p-значение теста.
"""
n = len(sequence)
sum_bits = 0

# Подсчет суммы: +1 для '1', -1 для '0'
sum_bits = sum( 1 if bit == '1' else -1 for bit in sequence )

# Вычисление статистики
s_obs = abs(sum_bits) / math.sqrt(n)
p_value = math.erfc(s_obs / math.sqrt(2.0))

return p_value

def runs_test(sequence: str) -> float:
"""
Проводит тест на количество серий в двоичной последовательности.
Args:
sequence: Строка из двоичных цифр ('0' и '1') для тестирования.
Return:
0.0, если последовательность слишком смещена для проведения теста.
p-значение теста.
"""
n = len(sequence)
ones = sequence.count('1')

# Проверка условия: доля единиц близка к 0.5
pi = ones / n
if abs(pi - 0.5) >= 2.0 / math.sqrt(n):
print(f"Тест на серии: Доля единиц ({pi:.6f}) слишком отклоняется. Тест не пройден.")
return 0.0

runs = 0
for i in range(1, n):
if sequence[i] != sequence[i-1]:
runs += 1

# Вычисление статистики
numerator = abs(runs - 2 * n * ones * (1 - ones))
denominator = 2 * (math.sqrt(2 * n)) * ones * (1 - ones)

# Защита от деления на ноль
if denominator == 0:
return 0.0

p_value = math.erfc(numerator / denominator)
return p_value


def longest_run_test(sequence: str) -> float:
"""
Проводит тест на самую длинную серию единиц в двоичной последовательности.
Args:
sequence: Строка из двоичных цифр ('0' и '1') для тестирования.
Return:
p-значение теста.
"""

length = len(sequence)

if length < SIZE_BLOCK * NUMBER_OF_BLOCKS:
raise ValueError("Sequence is too short for the test")

v = [0, 0, 0, 0]

for i in range(0, SIZE_BLOCK * NUMBER_OF_BLOCKS, SIZE_BLOCK):
block = sequence[i:i + SIZE_BLOCK]
max_length = current = 0

for bit in block:
current = current + 1 if bit == '1' else 0
max_length = max(max_length, current)

match max_length:
case max_length if max_length <= 1:
v[0] += 1
case 2:
v[1] += 1
case 3:
v[2] += 1
case max_length if max_length >= 4:
v[3] += 1

xi_square = sum(((v[i] - NUMBER_OF_BLOCKS * PI_VALUES[i]) ** 2) / (NUMBER_OF_BLOCKS * PI_VALUES[i]) for i in range(4))

p_value = special.gammaincc(3/2, xi_square/2)

return p_value