import random
import time
import multiprocessing
import numpy as np

# Генерация матрицы
def generate_matrix(size):
    return [[random.randint(0, 10) for _ in range(size)] for _ in range(size)]

# Вычисление детерминанта матрицы (рекурсивно)
def determinant(matrix):
    size = len(matrix)
    if size == 2:
        return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
    
    det = 0
    for col in range(size):
        submatrix = [row[:col] + row[col+1:] for row in matrix[1:]]
        det += ((-1) ** col) * matrix[0][col] * determinant(submatrix)
    return det

# Вычисление детерминанта параллельно
def parallel_determinant(matrix, num_processes):
    size = len(matrix)
    if size <= 2:
        return determinant(matrix)
    
    # Разбиение задачи по строкам на несколько потоков
    chunk_size = size // num_processes
    chunks = []
    
    # Создание задач для потоков
    for i in range(num_processes):
        start_row = i * chunk_size
        end_row = (i + 1) * chunk_size if i < num_processes - 1 else size
        chunks.append((matrix[start_row:end_row], i))
    
    with multiprocessing.Pool(processes=num_processes) as pool:
        results = pool.starmap(calculate_determinant_chunk, [(matrix, chunk[0], chunk[1]) for chunk in chunks])
    
    det = sum(results)
    return det

# Вычисление детерминанта для части матрицы в одном процессе
def calculate_determinant_chunk(matrix, chunk, chunk_index):
    size = len(matrix)
    det = 0
    for row in chunk:
        for col in range(size):
            submatrix = [r[:col] + r[col+1:] for r in matrix[1:]]
            det += ((-1) ** (chunk_index + col)) * matrix[0][col] * determinant(submatrix)
    return det

# Замер времени для параллельного вычисления детерминанта
def benchmark(size, num_processes=1):
    matrix = generate_matrix(size)

    start_time = time.time()
    parallel_determinant(matrix, num_processes)
    par_time = time.time() - start_time

    return par_time

def main():
    # Размеры матриц
    matrix_sizes = [9, 10, 11]
    # Количество потоков
    num_processes_list = [1, 2, 4, 6, 8]
    # Таблица с бенчмарками
    print("-*" * 40)
    print(f"{'Количество потоков':<20}{'|9x9 (сек.)':<20}{'|10x10 (сек.)':<20}{'|11x11 (сек.)'}")
    print("-*" * 40)
    
    for num_processes in num_processes_list:
        row = f"{num_processes:<20}"
        
        for size in matrix_sizes:
            par_time = benchmark(size, num_processes)
            row += f"|{par_time:.4f}".ljust(20)
        print(row)
        print("-*" * 40)

if __name__ == "__main__":
    main()