diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c2b53b5..868d947 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,5 +25,5 @@ jobs:
run: For /R %%I in (*.sln) do dotnet build %%I
shell: cmd
- name: test
- run: For /R %%I in (*.sln) do dotnet build %%I
+ run: For /R %%I in (*.sln) do dotnet test %%I
shell: cmd
diff --git a/C#/forSpbu/MatrixMult.Benchmark/MatrixMult.Benchmark.csproj b/C#/forSpbu/MatrixMult.Benchmark/MatrixMult.Benchmark.csproj
new file mode 100644
index 0000000..78592f1
--- /dev/null
+++ b/C#/forSpbu/MatrixMult.Benchmark/MatrixMult.Benchmark.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/C#/forSpbu/MatrixMult.Benchmark/MatrixMultBenchmark.cs b/C#/forSpbu/MatrixMult.Benchmark/MatrixMultBenchmark.cs
new file mode 100644
index 0000000..3f5fb10
--- /dev/null
+++ b/C#/forSpbu/MatrixMult.Benchmark/MatrixMultBenchmark.cs
@@ -0,0 +1,41 @@
+using BenchmarkDotNet.Attributes;
+namespace MatrixMult.Benchmark;
+
+public class MatrixMultBenchmark
+{
+ [Params(12, 24, 48, 96, 192, 384, 768, 1536)]
+ public int Size { get; set; }
+ private Matrix _fst = new Matrix();
+ private Matrix _sec = new Matrix();
+
+ [GlobalSetup]
+ public void GenerateRandomMatrix()
+ {
+ var random = new Random();
+ var fstElements = new int[Size, Size];
+ var secElements = new int[Size, Size];
+ for (int i = 0; i < Size; i++)
+ {
+ for (int j = 0; j < Size; j++)
+ {
+ fstElements[i, j] = random.Next();
+ secElements[i, j] = random.Next();
+ }
+ }
+
+ _fst = new Matrix(fstElements);
+ _sec = new Matrix(secElements);
+ }
+
+ [Benchmark]
+ public void MultiThreaded()
+ {
+ MatrixMultiplier.MultiThreadedMultiply(_fst, _sec);
+ }
+
+ [Benchmark]
+ public void SingleThreaded()
+ {
+ MatrixMultiplier.SingleThreadedMultiply(_fst, _sec);
+ }
+}
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult.Benchmark/Program.cs b/C#/forSpbu/MatrixMult.Benchmark/Program.cs
new file mode 100644
index 0000000..04960b1
--- /dev/null
+++ b/C#/forSpbu/MatrixMult.Benchmark/Program.cs
@@ -0,0 +1,5 @@
+using MatrixMult.Benchmark;
+using BenchmarkDotNet.Running;
+
+var table = BenchmarkRunner.Run();
+Console.WriteLine(table);
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult.Tests/MatrixMult.Tests.csproj b/C#/forSpbu/MatrixMult.Tests/MatrixMult.Tests.csproj
new file mode 100644
index 0000000..7cdd389
--- /dev/null
+++ b/C#/forSpbu/MatrixMult.Tests/MatrixMult.Tests.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/C#/forSpbu/MatrixMult.Tests/MultTests.cs b/C#/forSpbu/MatrixMult.Tests/MultTests.cs
new file mode 100644
index 0000000..bab80a0
--- /dev/null
+++ b/C#/forSpbu/MatrixMult.Tests/MultTests.cs
@@ -0,0 +1,108 @@
+namespace MatrixMult.Tests;
+
+public static class MultTests
+{
+ private static readonly (Matrix, Matrix, Matrix?)[] Tests =
+ {
+ (
+ new Matrix(
+ new int[,]
+ {
+ { 1, 2 },
+ { 3, 4 }
+ }),
+ new Matrix(
+ new int[,]
+ {
+ { 2, 3 },
+ { 4, 5 }
+ }),
+ new Matrix(
+ new int[,]
+ {
+ { 10, 13 },
+ { 22, 29 }
+ })
+ ),
+ (
+ new Matrix(
+ new int[,]
+ {
+ { 1, 2, 3 },
+ { 4, 5, 6 }
+ }),
+ new Matrix(
+ new int[,]
+ {
+ { 1, 2 },
+ { 4, 5 }
+ }),
+ null
+ ),
+ (
+ new Matrix(
+ new int[,]
+ {
+ { 1, 2, 3 },
+ { 4, 5, 6 }
+ }),
+ new Matrix(
+ new int[,]
+ {
+ { 1, 2 },
+ { 3, 4 },
+ { 5, 6 }
+ }),
+ new Matrix(
+ new int[,]
+ {
+ { 22, 28 },
+ { 49, 64 }
+ })
+ )
+ };
+
+ private static IEnumerable TestCases()
+ {
+ foreach (var test in Tests)
+ {
+ yield return new TestCaseData(new Func(MatrixMultiplier.MultiThreadedMultiply), test);
+ yield return new TestCaseData(new Func(MatrixMultiplier.SingleThreadedMultiply), test);
+ }
+ }
+
+ private static bool AreMatricesEqual(Matrix fst, Matrix sec)
+ {
+ if (fst.Height != sec.Height || fst.Width != sec.Width)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < fst.Height; i++)
+ {
+ for (int j = 0; j < sec.Width; j++)
+ {
+ if (fst.GetElement(i, j) != sec.GetElement(i, j))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ [Test, TestCaseSource(nameof(TestCases))]
+ public static void MultiplyMatricesTest(Func matrixMultImpl, (Matrix, Matrix, Matrix?) test)
+ {
+ if (test.Item3 == null)
+ {
+ Assert.Throws(() => matrixMultImpl(test.Item1, test.Item2));
+ }
+ else
+ {
+ var multResult = matrixMultImpl(test.Item1, test.Item2);
+ Assert.That(AreMatricesEqual(multResult, test.Item3), Is.True);
+ }
+ }
+}
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult.Tests/Usings.cs b/C#/forSpbu/MatrixMult.Tests/Usings.cs
new file mode 100644
index 0000000..cefced4
--- /dev/null
+++ b/C#/forSpbu/MatrixMult.Tests/Usings.cs
@@ -0,0 +1 @@
+global using NUnit.Framework;
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult/Matrix.cs b/C#/forSpbu/MatrixMult/Matrix.cs
new file mode 100644
index 0000000..23b88c2
--- /dev/null
+++ b/C#/forSpbu/MatrixMult/Matrix.cs
@@ -0,0 +1,101 @@
+namespace MatrixMult;
+
+///
+/// Class with matrix container
+///
+public class Matrix
+{
+ ///
+ /// Matrix elements
+ ///
+ private readonly int[,] _elements;
+
+ ///
+ /// Number of matrix rows
+ ///
+ public int Height => this._elements.GetLength(0);
+
+ ///
+ /// Number of matrix columns
+ ///
+ public int Width => this._elements.GetLength(1);
+
+ ///
+ /// Constructs empty matrix
+ ///
+ public Matrix()
+ {
+ this._elements = new int[0, 0];
+ }
+
+ ///
+ /// Constructs matrix with given elements
+ ///
+ /// Matrix elements
+ public Matrix(int [,] newElements)
+ {
+ this._elements = newElements;
+ }
+
+ ///
+ /// Constructs matrix with given string lines
+ ///
+ /// Array of lines to build matrix from
+ /// If given array of lines is incorrect
+ public Matrix(string[] lines)
+ {
+ var splitLines = lines.Select(str => str.Split(' ')).ToArray();
+ var localHeight = splitLines.Length;
+ var localWidth = splitLines[0].Length;
+
+ this._elements = new int[localHeight, localWidth];
+ for (int i = 0; i < localHeight; i++)
+ {
+ if (splitLines[i].Length != localWidth)
+ {
+ throw new MatrixCreationException("Inconsistent lines length");
+ }
+
+ for (var j = 0; j < localWidth; j++)
+ {
+ if (!int.TryParse(splitLines[i][j], out var parseValue))
+ {
+ throw new MatrixCreationException("Incorrect matrix element");
+ }
+ this._elements[i, j] = parseValue;
+ }
+ }
+ }
+
+ ///
+ /// Returns element by index
+ ///
+ /// First index
+ /// Second index
+ /// Value of matrix element in given position
+ /// If some index is out of range
+ public int GetElement(int firstInd, int secondInd) =>
+ firstInd < 0 || firstInd >= this.Height
+ ? throw new ArgumentOutOfRangeException(nameof(firstInd))
+ : secondInd < 0 || secondInd >= this.Width
+ ? throw new ArgumentOutOfRangeException(nameof(secondInd))
+ : this._elements[firstInd, secondInd];
+
+ ///
+ /// Returns matrix as array of string
+ ///
+ /// String array matrix representation
+ public string[] GetStringRepresentation()
+ {
+ var lines = new string[this.Height];
+ for (int i = 0; i < this.Height; i++)
+ {
+ for (int j = 0; j < this.Width; j++)
+ {
+ lines[i] += _elements[i, j] + " ";
+ }
+ }
+
+ return lines;
+ }
+}
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult/MatrixCreationException.cs b/C#/forSpbu/MatrixMult/MatrixCreationException.cs
new file mode 100644
index 0000000..bcd56ac
--- /dev/null
+++ b/C#/forSpbu/MatrixMult/MatrixCreationException.cs
@@ -0,0 +1,15 @@
+namespace MatrixMult;
+
+///
+/// Class for matrix creation exceptions
+///
+public class MatrixCreationException : Exception
+{
+ public MatrixCreationException() : base()
+ {
+ }
+
+ public MatrixCreationException(string message) : base(message)
+ {
+ }
+}
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult/MatrixMult.csproj b/C#/forSpbu/MatrixMult/MatrixMult.csproj
new file mode 100644
index 0000000..2b14c81
--- /dev/null
+++ b/C#/forSpbu/MatrixMult/MatrixMult.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
diff --git a/C#/forSpbu/MatrixMult/MatrixMultiplier.cs b/C#/forSpbu/MatrixMult/MatrixMultiplier.cs
new file mode 100644
index 0000000..486acfa
--- /dev/null
+++ b/C#/forSpbu/MatrixMult/MatrixMultiplier.cs
@@ -0,0 +1,88 @@
+namespace MatrixMult;
+
+///
+/// Class for matrix multiplication
+///
+public static class MatrixMultiplier
+{
+ ///
+ /// Performs matrix multiplication in single threaded mode
+ ///
+ /// First matrix in multiplication
+ /// Second matrix in multiplication
+ /// Result matrix
+ /// If matrices are incompatible(width of first and height of second are not equal)
+ public static Matrix SingleThreadedMultiply(Matrix first, Matrix second)
+ {
+ if (first.Width != second.Height)
+ {
+ throw new MatrixMultiplierException("Incompatible matrices");
+ }
+
+ var newElements = new int[first.Height, second.Width];
+ for (var i = 0; i < first.Height; i++)
+ {
+ for (var j = 0; j < second.Width; j++)
+ {
+ for (var k = 0; k < second.Height; k++)
+ {
+ newElements[i, j] += first.GetElement(i, k) * second.GetElement(k, j);
+ }
+ }
+ }
+
+ return new Matrix(newElements);
+ }
+
+ ///
+ /// Performs matrix multiplication in multi threaded mode
+ ///
+ /// First matrix in multiplication
+ /// Second matrix in multiplication
+ /// Result matrix
+ /// If matrices are incompatible(width of first and height of second are not equal)
+ public static Matrix MultiThreadedMultiply(Matrix first, Matrix second)
+ {
+ if (first.Width != second.Height)
+ {
+ throw new MatrixMultiplierException("Incompatible matrices");
+ }
+
+ var threadsAmount = Math.Min(Environment.ProcessorCount, first.Height);
+ var rowsForThread = first.Height / threadsAmount;
+
+ var threads = new Thread[threadsAmount];
+ var newElements = new int[first.Height, second.Width];
+ for (int i = 0; i < threadsAmount; i++)
+ {
+ var startRow = i * rowsForThread;
+ var endRow = (i == threadsAmount - 1) ? first.Height : (i + 1) * rowsForThread;
+
+ threads[i] = new Thread(() =>
+ {
+ for (var row = startRow; row < endRow; row++)
+ {
+ for (int j = 0; j < second.Width; j++)
+ {
+ for (int k = 0; k < second.Height; k++)
+ {
+ newElements[row, j] += first.GetElement(row, k) * second.GetElement(k, j);
+ }
+ }
+ }
+ });
+ }
+
+ foreach (var thread in threads)
+ {
+ thread.Start();
+ }
+
+ foreach (var thread in threads)
+ {
+ thread.Join();
+ }
+
+ return new Matrix(newElements);
+ }
+}
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult/MatrixMultiplierException.cs b/C#/forSpbu/MatrixMult/MatrixMultiplierException.cs
new file mode 100644
index 0000000..01b5d2b
--- /dev/null
+++ b/C#/forSpbu/MatrixMult/MatrixMultiplierException.cs
@@ -0,0 +1,15 @@
+namespace MatrixMult;
+
+///
+/// Class for matrix multiplier exceptions
+///
+public class MatrixMultiplierException : Exception
+{
+ public MatrixMultiplierException() : base()
+ {
+ }
+
+ public MatrixMultiplierException(string message) : base(message)
+ {
+ }
+}
\ No newline at end of file
diff --git a/C#/forSpbu/MatrixMult/Program.cs b/C#/forSpbu/MatrixMult/Program.cs
new file mode 100644
index 0000000..ddf9141
--- /dev/null
+++ b/C#/forSpbu/MatrixMult/Program.cs
@@ -0,0 +1,58 @@
+using MatrixMult;
+
+if (args.Length != 3)
+{
+ Console.WriteLine("Wrong number of arguments(should be mode -s(single) or -m(multi) and two file paths)");
+ return;
+}
+
+var mode = args[0];
+var fstFilePath = args[1];
+var secFilePath = args[2];
+if (string.IsNullOrEmpty(fstFilePath) || string.IsNullOrEmpty(secFilePath))
+{
+ Console.WriteLine("Wrong(null or empty) paths");
+ return;
+}
+if (mode != "-m" && mode != "-s")
+{
+ Console.WriteLine("Incorrect mode - s(single) or m(multi)");
+ return;
+}
+
+try
+{
+ var fstMatrix = new Matrix(File.ReadAllLines(fstFilePath));
+ var secMatrix = new Matrix(File.ReadAllLines(secFilePath));
+
+ var result = new Matrix();
+ switch (mode)
+ {
+ case "-m":
+ {
+ result = MatrixMultiplier.MultiThreadedMultiply(fstMatrix, secMatrix);
+ break;
+ }
+ case "-s":
+ {
+ result = MatrixMultiplier.SingleThreadedMultiply(fstMatrix, secMatrix);
+ break;
+ }
+ }
+
+ var resultLines = result.GetStringRepresentation();
+ File.WriteAllLines("result.txt", resultLines);
+ Console.WriteLine("Multiplication successful, result matrix has been written to result.txt");
+}
+catch (IOException)
+{
+ Console.WriteLine("File not found");
+}
+catch (MatrixCreationException)
+{
+ Console.WriteLine("Matrix creation error(given incorrect matrices)");
+}
+catch (MatrixMultiplierException)
+{
+ Console.WriteLine("Multiplication error(given incompatible matrices)");
+}
diff --git a/C#/forSpbu/forSpbu.sln b/C#/forSpbu/forSpbu.sln
index 527f400..04b3136 100644
--- a/C#/forSpbu/forSpbu.sln
+++ b/C#/forSpbu/forSpbu.sln
@@ -10,6 +10,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03.03", "03.03", "{882A9B9C
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "10.03", "10.03", "{EA6FC7D9-BDFB-49CD-AC00-FC5DDC5274B0}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{C0783CE2-5615-41A8-B385-64A80FBD28AC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07.09", "07.09", "{39F19078-6F77-48A7-A107-91BE48897BC7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixMult", "MatrixMult\MatrixMult.csproj", "{2D51ED0F-2E78-41A9-B4D7-CFD36A9FCCEB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixMult.Tests", "MatrixMult.Tests\MatrixMult.Tests.csproj", "{CCF2E7C9-46CB-4D91-8277-761DD7A5CF28}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixMult.Benchmark", "MatrixMult.Benchmark\MatrixMult.Benchmark.csproj", "{85632487-7520-4FD5-9FCB-56E9C2C147E3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,12 +31,28 @@ Global
{E007586F-9760-4744-BB25-EDEFD6BA860C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E007586F-9760-4744-BB25-EDEFD6BA860C}.Release|Any CPU.Build.0 = Release|Any CPU
{A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D51ED0F-2E78-41A9-B4D7-CFD36A9FCCEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D51ED0F-2E78-41A9-B4D7-CFD36A9FCCEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D51ED0F-2E78-41A9-B4D7-CFD36A9FCCEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D51ED0F-2E78-41A9-B4D7-CFD36A9FCCEB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CCF2E7C9-46CB-4D91-8277-761DD7A5CF28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CCF2E7C9-46CB-4D91-8277-761DD7A5CF28}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CCF2E7C9-46CB-4D91-8277-761DD7A5CF28}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CCF2E7C9-46CB-4D91-8277-761DD7A5CF28}.Release|Any CPU.Build.0 = Release|Any CPU
+ {85632487-7520-4FD5-9FCB-56E9C2C147E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {85632487-7520-4FD5-9FCB-56E9C2C147E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {85632487-7520-4FD5-9FCB-56E9C2C147E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {85632487-7520-4FD5-9FCB-56E9C2C147E3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {E007586F-9760-4744-BB25-EDEFD6BA860C} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F}
+ {E007586F-9760-4744-BB25-EDEFD6BA860C} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F}
{A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F}
+ {39F19078-6F77-48A7-A107-91BE48897BC7} = {C0783CE2-5615-41A8-B385-64A80FBD28AC}
+ {2D51ED0F-2E78-41A9-B4D7-CFD36A9FCCEB} = {39F19078-6F77-48A7-A107-91BE48897BC7}
+ {CCF2E7C9-46CB-4D91-8277-761DD7A5CF28} = {39F19078-6F77-48A7-A107-91BE48897BC7}
+ {85632487-7520-4FD5-9FCB-56E9C2C147E3} = {39F19078-6F77-48A7-A107-91BE48897BC7}
EndGlobalSection
EndGlobal