using System;
using System.Threading.Tasks;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        Console.WriteLine("Задания: \n1. проверка умножения матриц;\n2. бэнчмарки;");
        Console.Write("Введите номер задания: ");
        int numTask = int.Parse(Console.ReadLine());
        if (numTask == 1)
        {
            int[][] matrix1 = GenerateMatrix(10);
            int[][] matrix2 = GenerateMatrix(10);
            
            Console.WriteLine();
            PrintMatrix(matrix1); // Вывод результата
            Console.WriteLine();
            PrintMatrix(matrix2); // Вывод результата

            // Вводим количество потоков вручную
            Console.WriteLine();
            Console.Write("Введите количество потоков: ");
            int numThreads = int.Parse(Console.ReadLine());
            Console.WriteLine();

            Stopwatch sw1 = new Stopwatch();
            sw1.Start();
            int[][] resultSequential = MultiplyMatrix(matrix1, matrix2); // Вызов обычного алгоритма
            sw1.Stop();

            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            int[][] resultParallel = MultiplyMatrixParallel(matrix1, matrix2, numThreads); // Вызов параллельного алгоритма
            sw2.Stop();

            Console.WriteLine("Выполнение обычного алгоритма");
            PrintMatrix(resultSequential); // Вывод результата
            TimeSpan elapsedTime1 = sw1.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime1.TotalSeconds:F7} с");

            Console.WriteLine("\nВыполнение параллельного алгоритма");
            PrintMatrix(resultParallel); // Вывод результата
            TimeSpan elapsedTime2 = sw2.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime2.TotalSeconds:F7} с");

            Console.ReadLine();
        }
        else if (numTask == 2)
        {
            int[][] matrix1 = GenerateMatrix(100);
            int[][] matrix2 = GenerateMatrix(100);

            Console.Write("Введите количество потоков: ");
            int numThreads = int.Parse(Console.ReadLine());
            Console.WriteLine();

            Console.WriteLine("Размер матрицы: 100x100");

            Console.WriteLine("\nПоследовательный алгоритм:");
            Stopwatch sw1 = new Stopwatch();
            sw1.Start();
            int[][] resultSequential = MultiplyMatrix(matrix1, matrix2);
            sw1.Stop();
            TimeSpan elapsedTime1 = sw1.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime1.TotalSeconds:F7} мс");

            Console.WriteLine("\nПараллельный алгоритм:");
            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            int[][] resultParallel = MultiplyMatrixParallel(matrix1, matrix2, numThreads);
            sw2.Stop();
            TimeSpan elapsedTime2 = sw2.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime2.TotalSeconds:F7} с");

            Console.WriteLine("\n------------------------------------------\n");

            matrix1 = GenerateMatrix(300);
            matrix2 = GenerateMatrix(300);

            Console.WriteLine("Размер матрицы: 300x300");

            Console.WriteLine("\nПоследовательный алгоритм:");
            sw1.Restart();
            resultSequential = MultiplyMatrix(matrix1, matrix2);
            sw1.Stop();
            elapsedTime1 = sw1.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime1.TotalSeconds:F7} мс");

            Console.WriteLine("\nПараллельный алгоритм:");
            sw2.Restart();
            resultParallel = MultiplyMatrixParallel(matrix1, matrix2, numThreads);
            sw2.Stop();
            elapsedTime2 = sw2.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime2.TotalSeconds:F7} с");

            Console.WriteLine("\n------------------------------------------\n");

            matrix1 = GenerateMatrix(500);
            matrix2 = GenerateMatrix(500);

            Console.WriteLine("Размер матрицы: 500x500");

            Console.WriteLine("\nПоследовательный алгоритм:");
            sw1.Restart();
            resultSequential = MultiplyMatrix(matrix1, matrix2);
            sw1.Stop();
            elapsedTime1 = sw1.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime1.TotalSeconds:F7} мс");

            Console.WriteLine("\nПараллельный алгоритм:");
            sw2.Restart();
            resultParallel = MultiplyMatrixParallel(matrix1, matrix2, numThreads);
            sw2.Stop();
            elapsedTime2 = sw2.Elapsed;
            Console.WriteLine($"Время выполнения: {elapsedTime2.TotalSeconds:F7} с");

            Console.ReadLine();

        }
    }

    static int[][] GenerateMatrix(int size)
    {
        Random random = new Random();
        int[][] matrix = new int[size][];
        for (int i = 0; i < size; i++)
        {
            matrix[i] = new int[size];
            for (int j = 0; j < size; j++)
            {
                matrix[i][j] = random.Next(1, 100);
            }
        }
        return matrix;
    }

    static int[][] MultiplyMatrix(int[][] matrix1, int[][] matrix2)
    {
        int rows = matrix1.Length;
        int columns = matrix2[0].Length;
        int[][] result = new int[rows][];
        for (int i = 0; i < rows; i++)
        {
            result[i] = new int[columns];
            for (int j = 0; j < columns; j++)
            {
                result[i][j] = 0;
                for (int k = 0; k < matrix1[i].Length; k++)
                {
                    result[i][j] += matrix1[i][k] * matrix2[k][j];
                }
            }
        }
        return result;
    }

    static int[][] MultiplyMatrixParallel(int[][] matrix1, int[][] matrix2, int numThreads)
    {
        int rows = matrix1.Length;
        int columns = matrix2[0].Length;
        int[][] result = new int[rows][];


        Parallel.For(0, rows, new ParallelOptions { MaxDegreeOfParallelism = numThreads }, i =>
        {
            result[i] = new int[columns];
            for (int j = 0; j < columns; j++)
            {
                result[i][j] = 0;
                Parallel.For(0, matrix1[i].Length, k =>
                {
                    result[i][j] += matrix1[i][k] * matrix2[k][j];
                });
            }
        });
        return result;
    }

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