import numpy as np
from concurrent.futures import ThreadPoolExecutor
import time
import argparse

# Вычисляет детерминант матрицы с использованием метода Гаусса
def determinant_sequential(matrix):
    n = matrix.shape[0]
    mat = matrix.copy().astype(float)
    
    for i in range(n):
        if mat[i, i] == 0:
            for j in range(i + 1, n):
                if mat[j, i] != 0:
                    mat[[i, j]] = mat[[j, i]]  # Меняем строки
                    break
        
        for j in range(i + 1, n):
            factor = mat[j, i] / mat[i, i]
            mat[j] -= factor * mat[i]
    
    det = np.prod(np.diag(mat))
    return det

# Функция для вычисления минора
def calculate_minor(matrix, i, j):
    minor = np.delete(matrix, i, axis=0)
    minor = np.delete(minor, j, axis=1)
    return np.linalg.det(minor)

# Параллельное вычисление
def determinant_parallel(matrix, num_threads):

    if num_threads == 1:
        return determinant_sequential(matrix)
    
    n = len(matrix)
    results = []
    
    with ThreadPoolExecutor(max_workers=num_threads) as executor:
        futures = [
            executor.submit(lambda x: ((-1) ** x) * matrix[0, x] * calculate_minor(matrix, 0, x), i)
            for i in range(n)
        ]
        for future in futures:
            results.append(future.result())
    
    return sum(results)

def benchmark(matrix_sizes, num_threads_list):
    for size in matrix_sizes:
        matrix = np.random.rand(size, size)
        print(f"\nРазмер матрицы: {size}x{size}")
        
        # Бенчмарк для последовательного алгоритма
        start_time = time.time()
        det_seq = determinant_sequential(matrix)
        sequential_time = time.time() - start_time
        print(f"Последовательный алгоритм занял: {sequential_time:.4f} секунд")

        # Бенчмарк для параллельного алгоритма
        for num_threads in num_threads_list:
            start_time = time.time()
            det_par = determinant_parallel(matrix, num_threads)
            parallel_time = time.time() - start_time
            print(f"Параллельный алгоритм с {num_threads} потоками занял: {parallel_time:.4f} секунд")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Запуск бенчмарков для последовательного и параллельного вычисления детерминанта.")
    parser.add_argument(
        "--threads",
        type=int,
        nargs="+",
        required=True,
        help="Список количества потоков для параллельного алгоритма (например, --threads 1 2 4 8)"
    )
    args = parser.parse_args()
    
    # Задание размеров матриц для тестов
    matrix_sizes = [100, 300, 500]
    num_threads_list = args.threads  # Получаем список потоков из аргументов командной строки

    # Запуск бенчмарка
    benchmark(matrix_sizes, num_threads_list)