from multiprocessing import Pool, cpu_count
import time
import numpy as np


def determinant(matrix):
    matrix = np.array(matrix, dtype=float)
    n = matrix.shape[0]

    for i in range(n):
        if matrix[i, i] == 0:
            for j in range(i + 1, n):
                if matrix[j, i] != 0:
                    matrix[[i, j]] = matrix[[j, i]]
                    break
        if matrix[i, i] == 0:
            return 0

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

    det = 1.0
    for i in range(n):
        det *= matrix[i, i]
    return det


def _determinant_parallel_chunk(args):
    matrix, start_row, end_row = args
    n = matrix.shape[0]
    for i in range(start_row, end_row):
        for j in range(i + 1, n):
            factor = matrix[j, i] / matrix[i, i]
            matrix[j] -= factor * matrix[i]
    return matrix


def determinant_parallel(matrix, num_processes=None):
    if num_processes is None:
        num_processes = cpu_count()

    matrix = np.array(matrix, dtype=float)
    n = matrix.shape[0]

    chunk_size = n // num_processes
    tasks = [
        (
            matrix.copy(),
            i * chunk_size,
            (i + 1) * chunk_size if i != num_processes - 1 else n,
        )
        for i in range(num_processes)
    ]

    with Pool(processes=num_processes) as pool:
        results = pool.map(_determinant_parallel_chunk, tasks)

    # В каждой части делаем лишь частичное вычитание
    # Теперь выполняем окончательное вычисление детерминанта
    det = 1.0
    for i in range(n):
        det *= results[0][i, i]
    return det


sizes = [100, 300, 500]
NUM_PROCESSES = 12

for size in sizes:
    A = np.random.rand(size, size)
    start_time = time.time()
    det_seq = determinant(A)
    seq_time = time.time() - start_time

    start_time = time.time()
    det_par = determinant_parallel(A, num_processes=NUM_PROCESSES)
    par_time = time.time() - start_time

    print(
        f"Size {size}x{size}: Sequential Time = {seq_time:.4f} s, Parallel Time = {par_time:.4f} s"
    )