import numpy as np
import time
import threading

def determinant_sequential(matrix):
  return np.linalg.det(matrix)

def determinant_parallel(matrix, num_threads):
  n = len(matrix)
  if n == 1:
        return matrix[0][0]
  if n == 2:
        return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]

  threads = []
  results = [0] * num_threads
  chunk_size = n // num_threads

  def worker(thread_id):
        start_index = thread_id * chunk_size
        end_index = min((thread_id + 1) * chunk_size, n)
        det_sum = 0
        for i in range(start_index, end_index):
              submatrix = np.delete(matrix, i, 0)
              submatrix = np.delete(submatrix, 0, 1)
              det_sum += (-1)**i * matrix[i][0] * determinant_sequential(submatrix)
        results[thread_id] = det_sum

  for i in range(num_threads):
        thread = threading.Thread(target=worker, args=(i,))
        threads.append(thread)
        thread.start()

  for thread in threads:
        thread.join()

  return sum(results)

sizes = [100, 300, 500]
num_threads = [1, 2, 4]

results = {}

for size in sizes:
      matrix = np.random.rand(size, size)
      results[size] = {}
      for n_threads in num_threads:
            start_time = time.time()
            if n_threads == 1:
                det = determinant_sequential(matrix)
            else:
                det = determinant_parallel(matrix, n_threads)
            end_time = time.time()
            results[size][n_threads] = end_time - start_time
            print(f"Размер матрицы: {size}x{size}, Потоков: {n_threads}, Время: {end_time - start_time:.4f} сек.")


print("\n## Результаты бенчмарка:")
print("| Размер матрицы | 1 поток (последовательно) | 2 потока | 4 потока |")
for size, timings in results.items():
    print(f"| {size}x{size} | {timings [1] :.4f} сек. | {timings.get(2, 'N/A'):.4f} сек. | {timings.get(4, 'N/A'):.4f} сек. |")