using System;
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Выберите задание для выполнения:");
        //Console.WriteLine("1 - Нахождение детерминанта квадратной матрицы");
        Console.WriteLine("2 - Бенчмарки");
        string choice = Console.ReadLine();
        if (choice == "1")
        {
            //int threads = GetNumberOfThreads();   // Запрашиваем количество потоков у пользователя
            //int matrixSize = 10;                // Размер матрицы
            //int[,] matrixA = GenerateRandomMatrix(matrixSize);
            //int[,] matrixB = GenerateRandomMatrix(matrixSize);

            //Console.WriteLine("Выполняется обычное умножение матриц...");
            //Stopwatch stopwatch = Stopwatch.StartNew();
            //int[,] result1 = MultiplyMatrices(matrixA, matrixB, matrixSize);
            //stopwatch.Stop();
            //Console.WriteLine($"Результат (обычное умножение матриц): \n {GetMatrixString(result1)}");
            //Console.WriteLine($"Время выполнения: {stopwatch.Elapsed.TotalSeconds} секунд \n");

            //Console.WriteLine("Выполняется параллельное умножение матриц...");
            //stopwatch.Restart();
            //int[,] result2 = ParallelMultiplyMatrices(matrixA, matrixB, matrixSize, threads);
            //stopwatch.Stop();
            //Console.WriteLine($"Результат (параллельное умножение матриц): \n {GetMatrixString(result2)}");
            //Console.WriteLine($"Время выполнения: {stopwatch.Elapsed.TotalSeconds} секунд \n");
            //Console.WriteLine("\nНажмите Enter, чтобы вернуться в главное меню...");
            //Console.ReadLine();
        }
        else if (choice == "2")
        {
            int threads = GetNumberOfThreads();   // Запрашиваем количество потоков у пользователя
            Console.WriteLine("\nВыполнение алгоритма по нахождению детерминанта квадратной матрицы размером 10x10:");
            RunDeterminantBenchmark(10, threads);

            Console.ReadLine();
        }
        else
        {
            Console.WriteLine("Некорректный выбор.");
            Console.WriteLine("\nНажмите Enter, чтобы вернуться в главное меню...");
            Console.ReadLine();
        }
        Console.ReadLine();
    }

    static void RunDeterminantBenchmark(int size, int threads)
    {
        int[,] matrix = GenerateRandomMatrix(size);

        Console.WriteLine("Исходная матрица:");
        GetMatrixString(matrix);

        Stopwatch stopwatch = Stopwatch.StartNew();
        stopwatch.Restart();  
        double sequentialDeterminant = CalculateDeterminantSequential(matrix);
        stopwatch.Stop();
        Console.WriteLine($"Последовательное выполнение: детерминант = {sequentialDeterminant}, время выполнения = {stopwatch.Elapsed.TotalSeconds} секунд");


        stopwatch.Restart();
        double parallelDeterminant = CalculateDeterminantParallel(matrix, threads);
        stopwatch.Stop();
        Console.WriteLine($"Параллельное выполнение: детерминант = {parallelDeterminant}, время выполнения = {stopwatch.Elapsed.TotalSeconds} секунд");
        
    }

    static int[,] GenerateRandomMatrix(int size)
    {
        Random random = new Random();
        int[,] matrix = new int[size, size];

        for (int i = 0; i < size; i++)
        {
            for (int j = 0; j < size; j++)
            {
                matrix[i, j] = random.Next(10);
            }
        }

        return matrix;
    }

    static double CalculateDeterminantSequential(int[,] matrix)
    {
        int size = matrix.GetLength(0);
        double determinant = 0;

        if (size == 1)
        {
            determinant = matrix[0, 0];
        }
        else if (size == 2)
        {
            determinant = matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0];
        }
        else
        {
            for (int j = 0; j < size; j++)
            {
                determinant += matrix[0, j] * CalculateMinor(matrix, 0, j) * Math.Pow(-1, j);
            }
        }

        return determinant;
    }

    static double CalculateDeterminantParallel(int[,] matrix, int threads)
    {
        int size = matrix.GetLength(0);
        double determinant = 0;

        if (size == 1)
        {
            determinant = matrix[0, 0];
        }
        else if (size == 2)
        {
            determinant = matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0];
        }
        else
        {
            Parallel.For(0, size, new ParallelOptions { MaxDegreeOfParallelism = threads }, j =>
            {
                determinant += matrix[0, j] * CalculateMinor(matrix, 0, j) * Math.Pow(-1, j);
            });
        }

        return determinant;
    }

    static double CalculateMinor(int[,] matrix, int row, int col)
    {
        int size = matrix.GetLength(0);
        int[,] minor = new int[size - 1, size - 1];

        int minorRow = 0;
        for (int i = 0; i < size; i++)
        {
            if (i == row)
            {
                continue;
            }

            int minorCol = 0;
            for (int j = 0; j < size; j++)
            {
                if (j == col)
                {
                    continue;
                }

                minor[minorRow, minorCol] = matrix[i, j];
                minorCol++;
            }

            minorRow++;
        }

        return CalculateDeterminantSequential(minor);
    }

    static double MeasureExecutionTime(Action action)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        action();
        stopwatch.Stop();

        return stopwatch.Elapsed.TotalSeconds;
    }


    static int GetNumberOfThreads()
    {
        int threads = 1;
        Console.WriteLine("Введите количество потоков:");
        int.TryParse(Console.ReadLine(), out threads);
        return threads;
    }

    static void PrintMatrix(int[,] matrix)
    {
        int size = matrix.GetLength(0);
        for (int i = 0; i < size; i++)
        {
            for (int j = 0; j < size; j++)
            {
                Console.Write(matrix[i, j] + " ");
            }
            Console.WriteLine();
        }
    }

    static string GetMatrixString(int[,] matrix)
    {
        int rows = matrix.GetLength(0);
        int columns = matrix.GetLength(1);
        string matrixString = "";
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < columns; j++)
            {
                matrixString += matrix[i, j] + " ";
            }
            matrixString += "\n";
        }
        return matrixString;
    }
}