import tkinter as tk
from tkinter import ttk
import numpy as np
from multiprocessing import cpu_count, Pool
import time

class DeterminantCalculator:
    def __init__(self, root):
        self.root = root
        self.root.title("Determinant Calculator")

        self.matrix_size_label = ttk.Label(root, text="Matrix Size:")
        self.matrix_size_label.grid(row=0, column=0, padx=10, pady=10)

        self.matrix_size_entry = ttk.Entry(root)
        self.matrix_size_entry.grid(row=0, column=1, padx=10, pady=10)

        self.algorithm_label = ttk.Label(root, text="Algorithm:")
        self.algorithm_label.grid(row=1, column=0, padx=10, pady=10)

        self.algorithm_var = tk.StringVar()
        self.algorithm_var.set("Normal")
        self.algorithm_menu = ttk.Combobox(root, textvariable=self.algorithm_var, values=["Normal", "Parallel"])
        self.algorithm_menu.grid(row=1, column=1, padx=10, pady=10)

        self.num_threads_label = ttk.Label(root, text="Number of Threads:")
        self.num_threads_label.grid(row=2, column=0, padx=10, pady=10)

        self.num_threads_entry = ttk.Entry(root)
        self.num_threads_entry.insert(0, str(cpu_count()))
        self.num_threads_entry.grid(row=2, column=1, padx=10, pady=10)

        self.generate_and_calculate_button = ttk.Button(root, text="Generate Matrix and Calculate Determinant", command=self.generate_and_calculate_determinant)
        self.generate_and_calculate_button.grid(row=3, column=0, columnspan=2, pady=10)

        self.result_label = ttk.Label(root, text="Result:")
        self.result_label.grid(row=4, column=0, padx=10, pady=10)

        self.time_label = ttk.Label(root, text="Time (s):")
        self.time_label.grid(row=5, column=0, padx=10, pady=10)

        self.matrix_label = ttk.Label(root, text="")
        self.matrix_label.grid(row=6, column=0, columnspan=2, pady=10)

        self.det_result = None  # Глобальная переменная для хранения результата

    def generate_matrix(self, size):
        matrix = np.random.randint(1, 10, size=(size, size))
        return matrix

    def show_random_matrix(self, matrix):
        self.matrix_label.config(text=f"Random Matrix:\n{matrix}")

    def calculate_determinant_parallel(self, matrix, num_threads):
        size = len(matrix)

        if size == 1:
            return matrix[0, 0], 0

        result = RawArray('d', [0.0])  # Разделяемый массив для хранения результата
        lock_object = np.ones(1)  # Использование numpy для создания объекта блокировки

        def calculate_submatrix(i):
            nonlocal result
            sub_matrix = self.get_submatrix(matrix, i)
            sub_determinant = matrix[0, i] * self.calculate_determinant(sub_matrix)

            with lock_object.get_lock():
                result[0] += (-1) ** i * sub_determinant

        with Pool(processes=num_threads) as pool:
            start_time = time.time()
            pool.map(calculate_submatrix, range(size))
            end_time = time.time()

        return result[0], end_time - start_time

    # ваш существующий код

    def get_submatrix(self, matrix, column_index):
        size = len(matrix)
        sub_matrix = np.zeros((size - 1, size - 1))

        for i in range(1, size):
            for j in range(size):
                if j < column_index:
                    sub_matrix[i - 1, j] = matrix[i, j]
                elif j > column_index:
                    sub_matrix[i - 1, j - 1] = matrix[i, j]

        return sub_matrix

    def calculate_determinant_group(self, matrix, row, col):
        sign = (-1) ** (row + col)
        minor = np.delete(np.delete(np.array(matrix), row, axis=0), col, axis=1)
        det_minor = self.calculate_determinant(minor)
        return sign * matrix[row][col] * det_minor

    def calculate_determinant(self, matrix=None):
        if matrix is None:
            matrix_size = int(self.matrix_size_entry.get())
            matrix = self.generate_matrix(matrix_size)

        size = len(matrix)
        if size == 1:
            return matrix[0][0]
        elif size == 2:
            return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
        else:
            det = 0
            for col in range(size):
                sign = (-1) ** col
                minor = np.delete(np.array(matrix), 0, axis=0)
                minor = np.delete(minor, col, axis=1)
                det += sign * matrix[0][col] * self.calculate_determinant(minor)
            return det

    def generate_and_calculate_determinant(self):
        matrix_size = int(self.matrix_size_entry.get())
        algorithm = self.algorithm_var.get()
        num_threads = int(self.num_threads_entry.get())

        random_matrix = self.generate_matrix(matrix_size)
        self.show_random_matrix(random_matrix)

        if algorithm == "Parallel":
            det, time_taken = self.calculate_determinant_parallel(random_matrix, num_threads)
        else:
            start_time = time.time()
            det = self.calculate_determinant(random_matrix)
            time_taken = time.time() - start_time

        result_str = f"Determinant: {det}"
        self.result_label.config(text=result_str)

        time_str = f"Time (s): {time_taken:.6f}"
        self.time_label.config(text=time_str)

        # Используем глобальную переменную для отображения результата в методе generate_and_calculate_determinant
        self.det_result = det

if __name__ == "__main__":
    root = tk.Tk()
    app = DeterminantCalculator(root)
    root.mainloop()