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Выполнение алгоритма на умножение двух матриц размером 100x100:");
            RunMatrixMultiplicationBenchmark(100, threads);

            Console.WriteLine("\nВыполнение алгоритма на умножение двух матриц размером 300x300:");
            RunMatrixMultiplicationBenchmark(300, threads);

            Console.WriteLine("\nВыполнение алгоритма на умножение двух матриц размером 500x500:");
            RunMatrixMultiplicationBenchmark(500, threads);

            Console.ReadLine();
        }
        else
        {
            Console.WriteLine("Некорректный выбор.");
            Console.WriteLine("\nНажмите Enter, чтобы вернуться в главное меню...");
            Console.ReadLine();
        }
        Console.ReadLine();
    }
    static int[,] GenerateRandomMatrix(int size)
    {
        int[,] matrix = new int[size, size];
        Random random = new Random();
        for (int i = 0; i < size; i++)
        {
            for (int j = 0; j < size; j++)
            {
                matrix[i, j] = random.Next(1, 10);
            }
        }
        return matrix;
    }
    static int[,] MultiplyMatrices(int[,] matrixA, int[,] matrixB, int size)
    {
        int[,] result = new int[size, size];
        for (int i = 0; i < size; i++)
        {
            for (int j = 0; j < size; j++)
            {
                result[i, j] = 0;
                for (int k = 0; k < size; k++)
                {
                    result[i, j] += matrixA[i, k] * matrixB[k, j];
                }
            }
        }
        return result;
    }
    static int[,] ParallelMultiplyMatrices(int[,] matrixA, int[,] matrixB, int size, int threads)
    {
        int[,] result = new int[size, size];
        Parallel.For(0, size, new ParallelOptions { MaxDegreeOfParallelism = threads }, i =>
        {
            for (int j = 0; j < size; j++)
            {
                result[i, j] = 0;
                for (int k = 0; k < size; k++)
                {
                    result[i, j] += matrixA[i, k] * matrixB[k, j];
                }
            }
        });
        return result;
    }

    static void RunMatrixMultiplicationBenchmark(int size, int threads)
    {
        // Создание случайных матриц
        int[,] matrixA = GenerateRandomMatrix(size);
        int[,] matrixB = GenerateRandomMatrix(size);

        // Выполнение последовательного алгоритма
        double sequentialTime = MeasureExecutionTime(() =>
        {
            MultiplyMatricesSequential(matrixA, matrixB);
        });

        Console.WriteLine($"Время выполнения последовательного алгоритма: {sequentialTime} секунд");

        // Выполнение параллельного алгоритма
        double parallelTime = MeasureExecutionTime(() =>
        {
            MultiplyMatricesParallel(matrixA, matrixB, threads);
        });

        Console.WriteLine($"Время выполнения параллельного алгоритма ({threads} потоков): {parallelTime} секунд");
    }
    static double MeasureExecutionTime(Action action)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        action.Invoke();
        stopwatch.Stop();

        return stopwatch.Elapsed.TotalSeconds;
    }



    static void MultiplyMatricesSequential(int[,] matrixA, int[,] matrixB)
    {
        int rowsA = matrixA.GetLength(0);
        int colsA = matrixA.GetLength(1);
        int colsB = matrixB.GetLength(1);

        int[,] result = new int[rowsA, colsB];

        for (int i = 0; i < rowsA; i++)
        {
            for (int j = 0; j < colsB; j++)
            {
                for (int k = 0; k < colsA; k++)
                {
                    result[i, j] += matrixA[i, k] * matrixB[k, j];
                }
            }
        }

        // Возвращаем результат умножения матриц
        // (можно не возвращать, если результат не нужен в дальнейшем)
        //return result;
    }

    static void MultiplyMatricesParallel(int[,] matrixA, int[,] matrixB, int threads)
    {
        int rowsA = matrixA.GetLength(0);
        int colsA = matrixA.GetLength(1);
        int colsB = matrixB.GetLength(1);

        int[,] result = new int[rowsA, colsB];

        Parallel.For(0, rowsA, new ParallelOptions { MaxDegreeOfParallelism = threads }, i =>
        {
        for (int j = 0; j < colsB; j++)
        {
            for (int k = 0; k < colsA; k++)
                {
                    result[i, j] += matrixA[i, k] * matrixB[k, j];
                }
            }
        });

        // Возвращаем результат умножения матриц
        // (можно не возвращать, если результат не нужен в дальнейшем)
        //return result;
    }



    //static void BenchmarkMultiplyMatrices(int size, int threads)
    //{
    //    int[,] matrixA = GenerateRandomMatrix(size);
    //    int[,] matrixB = GenerateRandomMatrix(size);
    //    Console.WriteLine($"Выполняется умножение матриц {size}x{size}");
    //    Stopwatch stopwatch = Stopwatch.StartNew();
    //    MultiplyMatrices(matrixA, matrixB, size);
    //    stopwatch.Stop();
    //    Console.WriteLine($"Время выполнения: {stopwatch.ElapsedMilliseconds} миллисекунд");
    //}
    static int GetNumberOfThreads()
    {
        int threads = 1;
        Console.WriteLine("Введите количество потоков:");
        int.TryParse(Console.ReadLine(), out threads);
        return threads;
    }
    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;
    }
}