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

class Program
{
    static void Main(string[] args)
    {
        // Размеры матриц
        int[] sizes = { 100, 300, 500, 700, 1000 };

        // Максимальное количество потоков
        int maxThreads = 20;

        foreach (var size in sizes)
        {
            Console.WriteLine($"\nРазмер матрицы: {size}x{size}");

            // Генерация двух матриц
            var matrixA = GenerateMatrix(size);
            var matrixB = GenerateMatrix(size);

            // Последовательное умножение
            var stopwatch = Stopwatch.StartNew();
            var resultSequential = MultiplyMatricesSequential(matrixA, matrixB);
            stopwatch.Stop();
            long timeSequential = stopwatch.ElapsedMilliseconds;
            Console.WriteLine($"Последовательное умножение: {timeSequential} мс");

            // Параллельное умножение с разным количеством потоков
            Console.WriteLine("Параллельное умножение (время выполнения для каждого количества потоков):");
            for (int threads = 1; threads <= maxThreads; threads++)
            {
                stopwatch.Restart();
                var resultParallel = MultiplyMatricesParallel(matrixA, matrixB, threads);
                stopwatch.Stop();
                long timeParallel = stopwatch.ElapsedMilliseconds;
                Console.WriteLine($"Потоков: {threads} — {timeParallel} мс");
            }
        }
    }

    // Генерация квадратной матрицы размером size x size
    static double[,] GenerateMatrix(int size)
    {
        var random = new Random();
        var matrix = new double[size, size];
        for (int i = 0; i < size; i++)
            for (int j = 0; j < size; j++)
                matrix[i, j] = random.NextDouble();
        return matrix;
    }

    // Последовательное умножение матриц
    static double[,] MultiplyMatricesSequential(double[,] matrixA, double[,] matrixB)
    {
        int size = matrixA.GetLength(0);
        var result = new double[size, size];

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

    // Параллельное умножение матриц
    static double[,] MultiplyMatricesParallel(double[,] matrixA, double[,] matrixB, int threadCount)
    {
        int size = matrixA.GetLength(0);
        var result = new double[size, size];

        Parallel.For(0, threadCount, threadIndex =>
        {
            int rowsPerThread = size / threadCount;
            int startRow = threadIndex * rowsPerThread;
            int endRow = (threadIndex == threadCount - 1) ? size : startRow + rowsPerThread;

            for (int i = startRow; i < endRow; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    for (int k = 0; k < size; k++)
                    {
                        result[i, j] += matrixA[i, k] * matrixB[k, j];
                    }
                }
            }
        });

        return result;
    }
}