using System;
using System.Diagnostics;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class MatrixDeterminant
{
    public static double DeterminantSequential(double[][] matrix)
    {
        int size = matrix.Length;

        if (size == 1)
        {
            return matrix[0][0];
        }

        if (size == 2)
        {
            return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
        }

        double determinant = 0;

        for (int i = 0; i < size; i++)
        {
            double sign = (i % 2 == 0) ? 1 : -1;
            double cofactor = sign * matrix[0][i] * DeterminantSequential(GetSubMatrix(matrix, 0, i));
            determinant += cofactor;
        }

        return determinant;
    }

    public static double DeterminantParallel(double[][] matrix, int numThreads)
    {
        int size = matrix.Length;

        if (size == 1)
        {
            return matrix[0][0];
        }

        if (size == 2)
        {
            return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
        }

        double determinant = 0;

        Parallel.For(0, size, new ParallelOptions { MaxDegreeOfParallelism = numThreads }, i =>
        {
            double sign = (i % 2 == 0) ? 1 : -1;
            double cofactor = sign * matrix[0][i] * DeterminantSequential(GetSubMatrix(matrix, 0, i));
            determinant += cofactor;
        });

        return determinant;
    }


    [Benchmark]
    public void DeterminantSequential_2x2()
    {
        double[][] matrix = GenerateRandomMatrix(2, 2);
        DeterminantSequential(matrix);
    }

    [Benchmark]
    public void DeterminantParallel_2x2_4TH()
    {
        double[][] matrix = GenerateRandomMatrix(2, 2);
        DeterminantParallel(matrix, 4);
    }
    [Benchmark]
    public void DeterminantParallel_2x2_8TH()
    {
        double[][] matrix = GenerateRandomMatrix(2, 2);
        DeterminantParallel(matrix, 8);
    }
    [Benchmark]
    public void DeterminantParallel_2x2_16TH()
    {
        double[][] matrix = GenerateRandomMatrix(2, 2);
        DeterminantParallel(matrix, 16);
    }
    [Benchmark]
    public void DeterminantSequential_4x4()
    {
        double[][] matrix = GenerateRandomMatrix(4, 4);
        DeterminantSequential(matrix);
    }

    [Benchmark]
    public void DeterminantParallel_4x4_4TH()
    {
        double[][] matrix = GenerateRandomMatrix(4, 4);
        DeterminantParallel(matrix, 4);
    }
    [Benchmark]
    public void DeterminantParallel_4x4_8TH()
    {
        double[][] matrix = GenerateRandomMatrix(4, 4);
        DeterminantParallel(matrix, 8);
    }
    [Benchmark]
    public void DeterminantParallel_4x4_16TH()
    {
        double[][] matrix = GenerateRandomMatrix(4, 4);
        DeterminantParallel(matrix, 16);
    }
    [Benchmark]
    public void DeterminantSequential_8x8()
    {
        double[][] matrix = GenerateRandomMatrix(8, 8);
        DeterminantSequential(matrix);
    }

    [Benchmark]
    public void DeterminantParallel_8x8_4TH()
    {
        double[][] matrix = GenerateRandomMatrix(8, 8);
        DeterminantParallel(matrix, 4);
    }
    [Benchmark]
    public void DeterminantParallel_8x8_8TH()
    {
        double[][] matrix = GenerateRandomMatrix(8, 8);
        DeterminantParallel(matrix, 8);
    }
    [Benchmark]
    public void DeterminantParallel_8x8_16TH()
    {
        double[][] matrix = GenerateRandomMatrix(8, 8);
        DeterminantParallel(matrix, 16);
    }
    public static double[][] GenerateRandomMatrix(int rows, int cols)
    {
        double[][] matrix = new double[rows][];

        Random rand = new Random();

        for (int i = 0; i < rows; i++)
        {
            matrix[i] = new double[cols];
            for (int j = 0; j < cols; j++)
            {
                matrix[i][j] = rand.Next(1, 10);
            }
        }

        return matrix;
    }

    private static double[][] GetSubMatrix(double[][] matrix, int rowToRemove, int colToRemove)
    {
        int size = matrix.Length;
        double[][] subMatrix = new double[size - 1][];

        for (int i = 0, newRow = 0; i < size; i++)
        {
            if (i != rowToRemove)
            {
                subMatrix[newRow] = new double[size - 1];
                for (int j = 0, newCol = 0; j < size; j++)
                {
                    if (j != colToRemove)
                    {
                        subMatrix[newRow][newCol] = matrix[i][j];
                        newCol++;
                    }
                }
                newRow++;
            }
        }
        return subMatrix;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<MatrixDeterminant>();
        Console.ReadLine();
    }
}