import random
import time
import copy
from multiprocessing import Pool
import concurrent.futures
from copy import deepcopy


class Matrix:
    def __init__(self) -> None:
        self.matrix_100 = [[0] * 100 for _ in range(100)]
        self.matrix_300 = [[0] * 300 for _ in range(300)]
        self.matrix_500 = [[0] * 500 for _ in range(500)]

    def str_matrix(self, type_list: str):
        _str = ""

        current_matrix = getattr(self, type_list)

        for i in range(len(current_matrix)):
            _str += "[ "

            for j in range(len(current_matrix[0])):
                _str += str(current_matrix[i][j]) + " "

            _str += " ]\n"

        return _str


def init_matrix(matrix: Matrix, size: int):
    for i in range(size):
        for j in range(size):
            matrix.__dict__[f"matrix_{size}"][i][j] = random.randint(0, 5)


def parallel_det(matrix, num_threads=1):
    n = len(matrix)
    m = deepcopy(matrix)
    det_value = 1

    for i in range(n):
        if m[i][i] == 0:
            for j in range(i + 1, n):
                if m[j][i] != 0:
                    m[i], m[j] = m[j], m[i]
                    det_value *= -1
                    break
            else:
                return 0

        with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:
            futures = [executor.submit(process_row, i, j, m, n) for j in range(i + 1, n)]
            concurrent.futures.wait(futures)

        det_value *= m[i][i]
        m = [list(row) for row in m]  # Обновляем строки матрицы

    return det_value


def process_row(i, j, m, n):
    factor = m[j][i] / m[i][i]
    for k in range(i, n):
        m[j][k] -= factor * m[i][k]
    return m[j]


def det(matrix):
    n = len(matrix)
    m = [row[:] for row in matrix]
    det_value = 1

    for i in range(n):
        if m[i][i] == 0:
            for j in range(i + 1, n):
                if m[j][i] != 0:
                    m[i], m[j] = m[j], m[i]
                    det_value *= -1
                    break
            else:
                return 0

        for j in range(i + 1, n):
            factor = m[j][i] / m[i][i]
            for k in range(i, n):
                m[j][k] -= factor * m[i][k]

        det_value *= m[i][i]

    return det_value


def benchmark():
    matrix = Matrix()
    init_matrix(matrix, 100)
    init_matrix(matrix, 300)
    init_matrix(matrix, 500)

    sizes = [100, 300, 500]
    for size in sizes:
        current_matrix = getattr(matrix, f'matrix_{size}')

        start_time = time.time()
        seq_result = det(current_matrix)
        seq_time = time.time() - start_time
        print(f"Последовательный детерминант {size}x{size}: {seq_result}, Время: {seq_time:.6f}с")


        start_time = time.time()
        par_result = parallel_det(current_matrix, num_threads=4)  # Измените число потоков по необходимости
        par_time = time.time() - start_time
        print(f"Параллельный детерминант {size}x{size}: {par_result}, Время: {par_time:.6f}с")


if __name__ == "__main__":
    benchmark()