From ed945873d247e787a055b2715befdd371a230ef2 Mon Sep 17 00:00:00 2001 From: MinyazevR Date: Wed, 6 Apr 2022 22:03:09 +0300 Subject: [PATCH 1/5] Writing an algorithm for finding an acyclic graph with the maximum edge length and writing a test in the case of a disconnected graph --- Routers/Routers/GraphTest/GraphTest.cs | 21 ++ Routers/Routers/GraphTest/GraphTest.csproj | 21 ++ Routers/Routers/GraphTest/InputTest.txt | 3 + Routers/Routers/Routers.sln | 31 ++ Routers/Routers/Routers/Comparator.cs | 20 ++ Routers/Routers/Routers/GraphExceptions.cs | 9 + Routers/Routers/Routers/IGraph.cs | 38 +++ .../Routers/Properties/launchSettings.json | 7 + Routers/Routers/Routers/Routers.cs | 312 ++++++++++++++++++ Routers/Routers/Routers/Routers.csproj | 10 + Routers/Routers/Routers/Solution.cs | 31 ++ 11 files changed, 503 insertions(+) create mode 100644 Routers/Routers/GraphTest/GraphTest.cs create mode 100644 Routers/Routers/GraphTest/GraphTest.csproj create mode 100644 Routers/Routers/GraphTest/InputTest.txt create mode 100644 Routers/Routers/Routers.sln create mode 100644 Routers/Routers/Routers/Comparator.cs create mode 100644 Routers/Routers/Routers/GraphExceptions.cs create mode 100644 Routers/Routers/Routers/IGraph.cs create mode 100644 Routers/Routers/Routers/Properties/launchSettings.json create mode 100644 Routers/Routers/Routers/Routers.cs create mode 100644 Routers/Routers/Routers/Routers.csproj create mode 100644 Routers/Routers/Routers/Solution.cs diff --git a/Routers/Routers/GraphTest/GraphTest.cs b/Routers/Routers/GraphTest/GraphTest.cs new file mode 100644 index 0000000..d6b41c5 --- /dev/null +++ b/Routers/Routers/GraphTest/GraphTest.cs @@ -0,0 +1,21 @@ +namespace GraphTest; + +using NUnit.Framework; +using Routers; + +public class Tests +{ + IGraph? graph = null; + [SetUp] + public void Setup() + { + graph = new Graph(); + } + + [Test] + public void DisconnectedGraph() + { + graph?.BuildGraph("..//..//..//InputTest.txt"); + Assert.Throws(() => graph?.BuildNewGraph()); + } +} \ No newline at end of file diff --git a/Routers/Routers/GraphTest/GraphTest.csproj b/Routers/Routers/GraphTest/GraphTest.csproj new file mode 100644 index 0000000..97caa87 --- /dev/null +++ b/Routers/Routers/GraphTest/GraphTest.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + diff --git a/Routers/Routers/GraphTest/InputTest.txt b/Routers/Routers/GraphTest/InputTest.txt new file mode 100644 index 0000000..2170138 --- /dev/null +++ b/Routers/Routers/GraphTest/InputTest.txt @@ -0,0 +1,3 @@ +1 : 4 (3), 6 (5) +2 : 3 (8) +6 : 8 (12) diff --git a/Routers/Routers/Routers.sln b/Routers/Routers/Routers.sln new file mode 100644 index 0000000..07ac4aa --- /dev/null +++ b/Routers/Routers/Routers.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Routers", "Routers\Routers.csproj", "{78B74291-487A-4340-AE29-2114D18ED214}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphTest", "GraphTest\GraphTest.csproj", "{FC895312-F6AB-48CF-A16B-4B7E8CCB7A88}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {78B74291-487A-4340-AE29-2114D18ED214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78B74291-487A-4340-AE29-2114D18ED214}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78B74291-487A-4340-AE29-2114D18ED214}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78B74291-487A-4340-AE29-2114D18ED214}.Release|Any CPU.Build.0 = Release|Any CPU + {FC895312-F6AB-48CF-A16B-4B7E8CCB7A88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC895312-F6AB-48CF-A16B-4B7E8CCB7A88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC895312-F6AB-48CF-A16B-4B7E8CCB7A88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC895312-F6AB-48CF-A16B-4B7E8CCB7A88}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {31700D31-9087-4D39-BA64-4DC5A05C25BE} + EndGlobalSection +EndGlobal diff --git a/Routers/Routers/Routers/Comparator.cs b/Routers/Routers/Routers/Comparator.cs new file mode 100644 index 0000000..0e61ceb --- /dev/null +++ b/Routers/Routers/Routers/Comparator.cs @@ -0,0 +1,20 @@ +namespace Routers; + +using System.Collections.Generic; + +public class Comparator : IComparer +{ + int IComparer.Compare(int x, int y) + { + if (x > y) + { + return -1; + } + else if (x == y) + { + return 0; + } + return 1; + } +} + diff --git a/Routers/Routers/Routers/GraphExceptions.cs b/Routers/Routers/Routers/GraphExceptions.cs new file mode 100644 index 0000000..37bb7eb --- /dev/null +++ b/Routers/Routers/Routers/GraphExceptions.cs @@ -0,0 +1,9 @@ +namespace Routers; + +/// +/// A class for creating custom exceptions +/// +public class DisconnectedGraph : Exception +{ + public DisconnectedGraph(string? message) : base(message) { } +} diff --git a/Routers/Routers/Routers/IGraph.cs b/Routers/Routers/Routers/IGraph.cs new file mode 100644 index 0000000..be0515e --- /dev/null +++ b/Routers/Routers/Routers/IGraph.cs @@ -0,0 +1,38 @@ +namespace Routers; + +public interface IGraph +{ + /// + /// Function for adding a node + /// + /// Node value + /// Was there a value in the graph + public bool AddNode(int index); + + /// + /// Function for creating an edge between vertices + /// + /// Value of the first node + /// Value of the second node + /// Edge length + /// Was there an edge in the graph + public bool SetLength(int valueFirstNode, int valueSecondNode, int length); + + /// + /// Function for constructing a new graph without cycles + /// + /// Acyclic graph, with maximum sum of edges + public Graph BuildNewGraph(); + + /// + /// Function for graph constructionПуть + /// + /// The path to the file with the table + public void BuildGraph(string pathToFile); + + /// + /// Function for graph printing + /// + /// File path + public void PrintGraph(string pathToFile); +} \ No newline at end of file diff --git a/Routers/Routers/Routers/Properties/launchSettings.json b/Routers/Routers/Routers/Properties/launchSettings.json new file mode 100644 index 0000000..a64d0f4 --- /dev/null +++ b/Routers/Routers/Routers/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "Routers": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/Routers/Routers/Routers/Routers.cs b/Routers/Routers/Routers/Routers.cs new file mode 100644 index 0000000..c34aa3c --- /dev/null +++ b/Routers/Routers/Routers/Routers.cs @@ -0,0 +1,312 @@ +namespace Routers; + +using System; +using System.Text; +using System.Collections.Generic; +using System.Collections; + +/// +/// Class representing a graph +/// +public class Graph : IGraph +{ + /// + /// Class for storing graph vertices + /// + private class Node + { + // Useless dictionary needed to output a table to a file by condition + public Dictionary newDictionary = new(); + } + + /// List for storing vertices + List nodes = new List(); + + /// List for storing vertex values + /// nodes[0] corresponds to nodeValue[0] + List nodeValue = new List(); + + /// Dictionary of Adjacency + /// The key is a pair of vertices, the value is the length of the edge between them + Dictionary<(int, int), int> adjacencyDictionary = new(); + + /// + /// Function for adding a node + /// + /// Node value + /// Was there a value in the graph + public bool AddNode(int index) + { + if (!nodeValue.Contains(index)) + { + nodes.Add(new()); + nodeValue.Add(index); + return true; + } + return false; + } + + /// + /// Function for creating an edge between vertices + /// + /// Value of the first node + /// Value of the second node + /// Edge length + /// Was there an edge in the graph + public bool SetLength(int valueFirstNode, int valueSecondNode, int length) + { + if (!nodeValue.Contains(valueFirstNode) || !nodeValue.Contains(valueSecondNode) || valueSecondNode == (valueFirstNode)) + { + return false; + } + if (!adjacencyDictionary.ContainsKey((valueFirstNode, valueSecondNode))) + { + adjacencyDictionary.Add((valueFirstNode, valueSecondNode), length); + } + return true; + } + + /// + /// Function for constructing a new graph without cycles + /// + /// Acyclic graph, with maximum sum of edges + public Graph BuildNewGraph() + { + if (!IsTheGraphConnected()) + { + throw new DisconnectedGraph("The graph is not connected"); + } + + ///List of subgraphs, each of which initially contains a vertex from the original graph + List subgraphs = new List(); + for (int i = 0; i < nodeValue.Count; i++) + { + subgraphs.Add(new()); + subgraphs[i].AddNode(nodeValue[i]); + } + + ///Dictionary of the adjacency of the original graph, but with values sorted in descending order + adjacencyDictionary = adjacencyDictionary.OrderBy(x => x.Value, new Comparator()).ToDictionary(x => x.Key, x => x.Value); + + /// We will go by the values in the adjacency dictionary. + /// If two vertices are in different graphs, then we combine + /// them into one graph by combining all existing vertices and adding a new edge. + /// If two vertices already lie in the same graph, + /// then adding an edge with the same vertices will result in a cycle, so in this case we do nothing. + /// Eventually there will remain one acyclic graph with the maximum sum of edges + while (subgraphs.Count != 1) + { + // For each pair of vertices from the adjacency dictionary + foreach ((int, int) edgeVertices in adjacencyDictionary.Keys) + { + var (firstNode, secondNode) = edgeVertices; + var length = adjacencyDictionary[edgeVertices]; + if (NodeContainedInSameGraph(firstNode, secondNode, subgraphs)) + { + continue; + } + else + { + CombineTwoGraphs(firstNode, secondNode, length, subgraphs); + } + } + } + return subgraphs[0]; + } + + /// + /// function for checking the location of two vertices in one graph from a set of graphs + /// + /// Value of the first node + /// Value of the second node + /// + /// Are two vertices in the same graph + private bool NodeContainedInSameGraph(int firstNode, int secondNode, List subgraphs) + { + for (int i = 0; i < subgraphs.Count; i++) + { + if (subgraphs[i].nodeValue.Contains(firstNode) && subgraphs[i].nodeValue.Contains(secondNode)) + { + return true; + } + if (subgraphs[i].nodeValue.Contains(firstNode) && !subgraphs[i].nodeValue.Contains(secondNode)) + { + return false; + } + if (!subgraphs[i].nodeValue.Contains(firstNode) && subgraphs[i].nodeValue.Contains(secondNode)) + { + return false; + } + } + return false; + } + + /// + /// Function for combining two graphs + /// + /// Value of the first node + /// Value of the second node + /// Edge length + /// List of graphs + private void CombineTwoGraphs(int firstNode, int secondNode, int length, List subgraphs) + { + Graph? firstGraph = null; + Graph? secondGraph = null; + for (int i = 0; i < subgraphs.Count; i++) + { + if (firstGraph != null && secondGraph != null) + { + break; + } + if (subgraphs[i].nodeValue.Contains(firstNode)) + { + firstGraph = subgraphs[i]; + } + if (subgraphs[i].nodeValue.Contains(secondNode)) + { + secondGraph = subgraphs[i]; + } + } + if (firstGraph == null || secondGraph == null) + { + return; + } + for (int i = 0; i < firstGraph.nodeValue.Count; i++) + { + secondGraph?.AddNode(firstGraph.nodeValue[i]); + } + if (secondGraph != null && firstGraph != null) + { + secondGraph.adjacencyDictionary = + firstGraph.adjacencyDictionary.Concat(secondGraph.adjacencyDictionary).ToDictionary(x => x.Key, x => x.Value); + secondGraph.adjacencyDictionary.Add((firstNode, secondNode), length); + } + if (firstGraph == null) + { + return; + } + firstGraph.nodes.Clear(); + firstGraph.nodeValue.Clear(); + firstGraph.adjacencyDictionary.Clear(); + subgraphs.Remove(firstGraph); + } + + /// + /// useless function designed to create a new dictionary for easy output to a file + /// + private void ConvertToAnotherDictionary() + { + foreach ((int, int) h in adjacencyDictionary.Keys) + { + var (first, second) = h; + nodes[nodeValue.IndexOf(first)].newDictionary.Add(second, adjacencyDictionary[h]); + } + } + + private List NumberOfConnectedGraphElements(int nodeIndex, List isTerminal) + { + isTerminal.Add(nodeIndex); + var array = new List(); + foreach ((int, int) h in adjacencyDictionary.Keys) + { + var (first, second) = h; + if (first == nodeIndex) + { + array.Add(second); + } + } + foreach (int item in array) + { + if (!isTerminal.Contains(item)) + { + NumberOfConnectedGraphElements(item, isTerminal); + } + } + return isTerminal; + } + + /// + /// Function for checking the connectivity of a graph + /// + /// Is the graph connected + private bool IsTheGraphConnected() + => NumberOfConnectedGraphElements(1, new()).Count == nodes.Count; + + /// + /// Function for graph constructionПуть + /// + /// The path to the file with the table + public void BuildGraph(string pathToFile) + { + var stringToConvert = File.ReadAllLines(pathToFile); + for (int i = 0; i < stringToConvert.Length; i++) + { + var newStringArray = stringToConvert[i].Split(' '); + int firstNumber = 0; + if (Int32.TryParse(newStringArray[0], out firstNumber)) + { + AddNode(firstNumber); + } + int secondNumber = 0; + for (int j = 1; j < newStringArray.Length; j++) + { + if (newStringArray[j] == ":") + { + continue; + } + int t = 0; + if (Int32.TryParse(newStringArray[j], out t)) + { + AddNode(t); + secondNumber = t; + } + else + { + int k = 0; + string newNumber = ""; + while (newStringArray[j][k] != ')') + { + if (newStringArray[j][k] == '(' || newStringArray[j][k] == ',') + { + k++; + continue; + } + newNumber += newStringArray[j][k]; + k++; + } + int length = 0; + if (Int32.TryParse(newNumber, out length)) + { + SetLength(firstNumber, secondNumber, length); + } + } + } + } + } + + /// + /// Function for graph printing + /// + /// File path + public void PrintGraph(string pathToFile) + { + ConvertToAnotherDictionary(); + using (StreamWriter writer = new StreamWriter(pathToFile)) + for (int i = 0; i < nodeValue.Count; i++) + { + if (nodes[i].newDictionary.Keys.Count != 0) + { + writer.Write($"{nodeValue[i]} : "); + } + else + { + continue; + } + foreach (int key in nodes[i].newDictionary.Keys) + { + writer.Write($"{key} ({nodes[i].newDictionary[key]}), "); + } + writer.WriteLine(); + } + } +} diff --git a/Routers/Routers/Routers/Routers.csproj b/Routers/Routers/Routers/Routers.csproj new file mode 100644 index 0000000..74abf5c --- /dev/null +++ b/Routers/Routers/Routers/Routers.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/Routers/Routers/Routers/Solution.cs b/Routers/Routers/Routers/Solution.cs new file mode 100644 index 0000000..d7d2aee --- /dev/null +++ b/Routers/Routers/Routers/Solution.cs @@ -0,0 +1,31 @@ +namespace Routers; + +public class Solution +{ + static int Main(string[] args) + { + if (args.Length != 2) + { + Console.WriteLine("Invalid input arguments"); + return -1; + } + IGraph graph = new Graph(); + graph.BuildGraph(args[0]); + Graph? newGraph = null; + try + { + newGraph = graph.BuildNewGraph(); + } + catch (DisconnectedGraph exception) + { + Console.WriteLine($"Error: {exception.Message}"); + return -1; + } + if (newGraph == null) + { + return -1; + } + newGraph.PrintGraph(args[1]); + return 0; + } +} From 2fd9e28462e74903250d7780fadaeb7a30476119 Mon Sep 17 00:00:00 2001 From: MinyazevR Date: Wed, 6 Apr 2022 22:16:22 +0300 Subject: [PATCH 2/5] Deleting a local file --- Routers/Routers/Routers/Properties/launchSettings.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Routers/Routers/Routers/Properties/launchSettings.json diff --git a/Routers/Routers/Routers/Properties/launchSettings.json b/Routers/Routers/Routers/Properties/launchSettings.json deleted file mode 100644 index a64d0f4..0000000 --- a/Routers/Routers/Routers/Properties/launchSettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "profiles": { - "Routers": { - "commandName": "Project" - } - } -} \ No newline at end of file From f1733c2eb4ace2ea464a93f9231b4d09d1fa202f Mon Sep 17 00:00:00 2001 From: MinyazevR Date: Fri, 22 Apr 2022 14:54:19 +0300 Subject: [PATCH 3/5] Add tests --- Routers/Routers/GraphTest/GraphTest.cs | 99 +++++++++- ...tions.cs => DisconnectedGraphException.cs} | 2 +- Routers/Routers/Routers/IGraph.cs | 10 +- Routers/Routers/Routers/Routers.cs | 176 ++++++++---------- Routers/Routers/Routers/Solution.cs | 8 +- 5 files changed, 188 insertions(+), 107 deletions(-) rename Routers/Routers/Routers/{GraphExceptions.cs => DisconnectedGraphException.cs} (68%) diff --git a/Routers/Routers/GraphTest/GraphTest.cs b/Routers/Routers/GraphTest/GraphTest.cs index d6b41c5..83c48b4 100644 --- a/Routers/Routers/GraphTest/GraphTest.cs +++ b/Routers/Routers/GraphTest/GraphTest.cs @@ -5,17 +5,110 @@ namespace GraphTest; public class Tests { - IGraph? graph = null; + IGraph graph = new Graph(); + [SetUp] public void Setup() { graph = new Graph(); } + [Test] + public void ShouldExpectedFalseWhenIsTheGraphConnectedForDisconnectGraph() + { + graph.AddNode(2); + graph.AddNode(4); + graph.AddNode(6); + graph.AddNode(7); + graph.AddNode(1); + graph.AddNode(5); + graph.AddNode(9); + graph.AddNode(8); + + graph.SetLength(6, 9, 12); + graph.SetLength(2, 4, 3); + graph.SetLength(2, 6, 4); + graph.SetLength(2, 7, 8); + graph.SetLength(2, 5, 2); + graph.SetLength(7, 8, 13); + + Assert.False(graph.IsTheGraphConnected()); + } + + [Test] + public void ShouldExpectedTrueWhenIsTheGraphConnectedForGraphConsisting1Node() + { + graph.AddNode(2); + Assert.True(graph.IsTheGraphConnected()); + } + + [Test] + public void ShouldExpectedTrueWhenIsTheGraphConnectedForEmptyGraph() + { + Assert.True(graph.IsTheGraphConnected()); + } + + [Test] + public void ShouldThrowsDisconnectedGraphWhenBuildAcyclicGraphForDisconnectGraph() + { + graph.AddNode(2); + graph.AddNode(4); + graph.AddNode(6); + graph.AddNode(7); + graph.AddNode(1); + graph.AddNode(5); + graph.AddNode(9); + graph.AddNode(8); + + graph.SetLength(2, 4, 3); + graph.SetLength(2, 6, 4); + graph.SetLength(2, 7, 8); + graph.SetLength(2, 5, 2); + graph.SetLength(7, 8, 13); + + Assert.Throws(() => graph.BuildAcyclicGraph()); + } + + [Test] + public void ShouldExpectedTrueWhenIsTheGraphConnectedForConnectedGraph() + { + graph.AddNode(2); + graph.AddNode(4); + graph.AddNode(6); + graph.AddNode(7); + graph.AddNode(1); + graph.AddNode(5); + graph.AddNode(9); + graph.AddNode(8); + + graph.SetLength(1, 6, 12); + graph.SetLength(6, 9, 12); + graph.SetLength(2, 4, 3); + graph.SetLength(2, 6, 4); + graph.SetLength(2, 7, 8); + graph.SetLength(2, 5, 2); + graph.SetLength(7, 8, 13); + + Assert.True(graph.IsTheGraphConnected()); + } + + [Test] + public void ShouldExpectedFalseWhenAddExistingNode() + { + graph.AddNode(2); + Assert.False(graph.AddNode(2)); + } + + [Test] + public void ShouldExpectedFalseWhenSetLengthForNonExistingNode() + { + Assert.False(graph.SetLength(2, 6, 4)); + } + [Test] public void DisconnectedGraph() { - graph?.BuildGraph("..//..//..//InputTest.txt"); - Assert.Throws(() => graph?.BuildNewGraph()); + graph.BuildGraph("..//..//..//InputTest.txt"); + Assert.Throws(() => graph.BuildAcyclicGraph()); } } \ No newline at end of file diff --git a/Routers/Routers/Routers/GraphExceptions.cs b/Routers/Routers/Routers/DisconnectedGraphException.cs similarity index 68% rename from Routers/Routers/Routers/GraphExceptions.cs rename to Routers/Routers/Routers/DisconnectedGraphException.cs index 37bb7eb..fd7e0c5 100644 --- a/Routers/Routers/Routers/GraphExceptions.cs +++ b/Routers/Routers/Routers/DisconnectedGraphException.cs @@ -5,5 +5,5 @@ /// public class DisconnectedGraph : Exception { - public DisconnectedGraph(string? message) : base(message) { } + public DisconnectedGraph() : base() { } } diff --git a/Routers/Routers/Routers/IGraph.cs b/Routers/Routers/Routers/IGraph.cs index be0515e..885096c 100644 --- a/Routers/Routers/Routers/IGraph.cs +++ b/Routers/Routers/Routers/IGraph.cs @@ -22,10 +22,10 @@ public interface IGraph /// Function for constructing a new graph without cycles /// /// Acyclic graph, with maximum sum of edges - public Graph BuildNewGraph(); + public Graph BuildAcyclicGraph(); /// - /// Function for graph constructionПуть + /// Function for graph construction /// /// The path to the file with the table public void BuildGraph(string pathToFile); @@ -35,4 +35,10 @@ public interface IGraph /// /// File path public void PrintGraph(string pathToFile); + + /// + /// Function for checking the connectivity of a graph + /// + /// Is the graph connected + public bool IsTheGraphConnected(); } \ No newline at end of file diff --git a/Routers/Routers/Routers/Routers.cs b/Routers/Routers/Routers/Routers.cs index c34aa3c..8882f51 100644 --- a/Routers/Routers/Routers/Routers.cs +++ b/Routers/Routers/Routers/Routers.cs @@ -1,9 +1,7 @@ namespace Routers; using System; -using System.Text; using System.Collections.Generic; -using System.Collections; /// /// Class representing a graph @@ -19,16 +17,23 @@ private class Node public Dictionary newDictionary = new(); } + public Graph(){ } + + public Graph (int x) + { + AddNode(x); + } + /// List for storing vertices - List nodes = new List(); + private readonly List nodes = new(); /// List for storing vertex values /// nodes[0] corresponds to nodeValue[0] - List nodeValue = new List(); + private readonly List nodeValue = new(); /// Dictionary of Adjacency /// The key is a pair of vertices, the value is the length of the edge between them - Dictionary<(int, int), int> adjacencyDictionary = new(); + private Dictionary<(int, int), int> adjacencyDictionary = new(); /// /// Function for adding a node @@ -43,6 +48,7 @@ public bool AddNode(int index) nodeValue.Add(index); return true; } + return false; } @@ -55,14 +61,17 @@ public bool AddNode(int index) /// Was there an edge in the graph public bool SetLength(int valueFirstNode, int valueSecondNode, int length) { - if (!nodeValue.Contains(valueFirstNode) || !nodeValue.Contains(valueSecondNode) || valueSecondNode == (valueFirstNode)) + if (!nodeValue.Contains(valueFirstNode) || !nodeValue.Contains(valueSecondNode) || valueSecondNode == valueFirstNode) { return false; } - if (!adjacencyDictionary.ContainsKey((valueFirstNode, valueSecondNode))) + + if (adjacencyDictionary.ContainsKey((valueFirstNode, valueSecondNode))) { - adjacencyDictionary.Add((valueFirstNode, valueSecondNode), length); + adjacencyDictionary.Remove((valueFirstNode, valueSecondNode)); } + + adjacencyDictionary.Add((valueFirstNode, valueSecondNode), length); return true; } @@ -70,19 +79,23 @@ public bool SetLength(int valueFirstNode, int valueSecondNode, int length) /// Function for constructing a new graph without cycles /// /// Acyclic graph, with maximum sum of edges - public Graph BuildNewGraph() + public Graph BuildAcyclicGraph() { if (!IsTheGraphConnected()) { - throw new DisconnectedGraph("The graph is not connected"); + throw new DisconnectedGraph(); } - ///List of subgraphs, each of which initially contains a vertex from the original graph - List subgraphs = new List(); + // List of subgraphs, each of which initially contains a vertex from the original graph + List subgraphs = new(); + + // The keys are nodes, the value is the index of the graph with the list in which the node is located + Dictionary graphInWhichTheNodeIsLocated = new(); + for (int i = 0; i < nodeValue.Count; i++) { - subgraphs.Add(new()); - subgraphs[i].AddNode(nodeValue[i]); + subgraphs.Add(new(nodeValue[i])); + graphInWhichTheNodeIsLocated.Add(nodeValue[i], i); } ///Dictionary of the adjacency of the original graph, but with values sorted in descending order @@ -101,44 +114,19 @@ public Graph BuildNewGraph() { var (firstNode, secondNode) = edgeVertices; var length = adjacencyDictionary[edgeVertices]; - if (NodeContainedInSameGraph(firstNode, secondNode, subgraphs)) + if (graphInWhichTheNodeIsLocated[firstNode] == graphInWhichTheNodeIsLocated[secondNode]) { continue; } + else { - CombineTwoGraphs(firstNode, secondNode, length, subgraphs); + CombineTwoGraphs(firstNode, secondNode, length, subgraphs, graphInWhichTheNodeIsLocated); } } } - return subgraphs[0]; - } - /// - /// function for checking the location of two vertices in one graph from a set of graphs - /// - /// Value of the first node - /// Value of the second node - /// - /// Are two vertices in the same graph - private bool NodeContainedInSameGraph(int firstNode, int secondNode, List subgraphs) - { - for (int i = 0; i < subgraphs.Count; i++) - { - if (subgraphs[i].nodeValue.Contains(firstNode) && subgraphs[i].nodeValue.Contains(secondNode)) - { - return true; - } - if (subgraphs[i].nodeValue.Contains(firstNode) && !subgraphs[i].nodeValue.Contains(secondNode)) - { - return false; - } - if (!subgraphs[i].nodeValue.Contains(firstNode) && subgraphs[i].nodeValue.Contains(secondNode)) - { - return false; - } - } - return false; + return subgraphs[0]; } /// @@ -148,43 +136,20 @@ private bool NodeContainedInSameGraph(int firstNode, int secondNode, List /// Value of the second node /// Edge length /// List of graphs - private void CombineTwoGraphs(int firstNode, int secondNode, int length, List subgraphs) + private static void CombineTwoGraphs(int firstNode, int secondNode, int length, List subgraphs, Dictionary dictionary) { - Graph? firstGraph = null; - Graph? secondGraph = null; - for (int i = 0; i < subgraphs.Count; i++) - { - if (firstGraph != null && secondGraph != null) - { - break; - } - if (subgraphs[i].nodeValue.Contains(firstNode)) - { - firstGraph = subgraphs[i]; - } - if (subgraphs[i].nodeValue.Contains(secondNode)) - { - secondGraph = subgraphs[i]; - } - } - if (firstGraph == null || secondGraph == null) - { - return; - } + Graph firstGraph = subgraphs[dictionary[firstNode]]; + Graph secondGraph = subgraphs[dictionary[secondNode]]; + for (int i = 0; i < firstGraph.nodeValue.Count; i++) { - secondGraph?.AddNode(firstGraph.nodeValue[i]); - } - if (secondGraph != null && firstGraph != null) - { - secondGraph.adjacencyDictionary = - firstGraph.adjacencyDictionary.Concat(secondGraph.adjacencyDictionary).ToDictionary(x => x.Key, x => x.Value); - secondGraph.adjacencyDictionary.Add((firstNode, secondNode), length); - } - if (firstGraph == null) - { - return; + secondGraph.AddNode(firstGraph.nodeValue[i]); } + + secondGraph.adjacencyDictionary = + firstGraph.adjacencyDictionary.Concat(secondGraph.adjacencyDictionary).ToDictionary(x => x.Key, x => x.Value); + secondGraph.adjacencyDictionary.Add((firstNode, secondNode), length); + firstGraph.nodes.Clear(); firstGraph.nodeValue.Clear(); firstGraph.adjacencyDictionary.Clear(); @@ -214,7 +179,12 @@ private List NumberOfConnectedGraphElements(int nodeIndex, List isTerm { array.Add(second); } + else if (second == nodeIndex) + { + array.Add(first); + } } + foreach (int item in array) { if (!isTerminal.Contains(item)) @@ -222,6 +192,7 @@ private List NumberOfConnectedGraphElements(int nodeIndex, List isTerm NumberOfConnectedGraphElements(item, isTerminal); } } + return isTerminal; } @@ -229,8 +200,8 @@ private List NumberOfConnectedGraphElements(int nodeIndex, List isTerm /// Function for checking the connectivity of a graph /// /// Is the graph connected - private bool IsTheGraphConnected() - => NumberOfConnectedGraphElements(1, new()).Count == nodes.Count; + public bool IsTheGraphConnected() + => nodeValue.Count == 0 || NumberOfConnectedGraphElements(nodeValue[0], new()).Count == nodes.Count; /// /// Function for graph constructionПуть @@ -242,11 +213,11 @@ public void BuildGraph(string pathToFile) for (int i = 0; i < stringToConvert.Length; i++) { var newStringArray = stringToConvert[i].Split(' '); - int firstNumber = 0; - if (Int32.TryParse(newStringArray[0], out firstNumber)) + if (Int32.TryParse(newStringArray[0], out int firstNumber)) { AddNode(firstNumber); } + int secondNumber = 0; for (int j = 1; j < newStringArray.Length; j++) { @@ -254,12 +225,13 @@ public void BuildGraph(string pathToFile) { continue; } - int t = 0; - if (Int32.TryParse(newStringArray[j], out t)) + + if (Int32.TryParse(newStringArray[j], out int number)) { - AddNode(t); - secondNumber = t; + AddNode(number); + secondNumber = number; } + else { int k = 0; @@ -271,11 +243,12 @@ public void BuildGraph(string pathToFile) k++; continue; } + newNumber += newStringArray[j][k]; k++; } - int length = 0; - if (Int32.TryParse(newNumber, out length)) + + if (Int32.TryParse(newNumber, out int length)) { SetLength(firstNumber, secondNumber, length); } @@ -291,22 +264,27 @@ public void BuildGraph(string pathToFile) public void PrintGraph(string pathToFile) { ConvertToAnotherDictionary(); - using (StreamWriter writer = new StreamWriter(pathToFile)) - for (int i = 0; i < nodeValue.Count; i++) + using StreamWriter writer = new(pathToFile); { - if (nodes[i].newDictionary.Keys.Count != 0) + for (int i = 0; i < nodeValue.Count; i++) { - writer.Write($"{nodeValue[i]} : "); - } - else - { - continue; - } - foreach (int key in nodes[i].newDictionary.Keys) - { - writer.Write($"{key} ({nodes[i].newDictionary[key]}), "); + if (nodes[i].newDictionary.Keys.Count != 0) + { + writer.Write($"{nodeValue[i]} : "); + } + + else + { + continue; + } + + foreach (int key in nodes[i].newDictionary.Keys) + { + writer.Write($"{key} ({nodes[i].newDictionary[key]}), "); + } + + writer.WriteLine(); } - writer.WriteLine(); } } } diff --git a/Routers/Routers/Routers/Solution.cs b/Routers/Routers/Routers/Solution.cs index d7d2aee..0508cf3 100644 --- a/Routers/Routers/Routers/Solution.cs +++ b/Routers/Routers/Routers/Solution.cs @@ -9,22 +9,26 @@ static int Main(string[] args) Console.WriteLine("Invalid input arguments"); return -1; } + IGraph graph = new Graph(); graph.BuildGraph(args[0]); - Graph? newGraph = null; + Graph? newGraph; + try { - newGraph = graph.BuildNewGraph(); + newGraph = graph.BuildAcyclicGraph(); } catch (DisconnectedGraph exception) { Console.WriteLine($"Error: {exception.Message}"); return -1; } + if (newGraph == null) { return -1; } + newGraph.PrintGraph(args[1]); return 0; } From c0358256538d226b137c640d3c0299637dc2f16e Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 2 Jun 2022 00:42:09 +0300 Subject: [PATCH 4/5] rename yml --- .github/workflows/{dotnet.yml => Routers.yml} | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) rename .github/workflows/{dotnet.yml => Routers.yml} (56%) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/Routers.yml similarity index 56% rename from .github/workflows/dotnet.yml rename to .github/workflows/Routers.yml index a6a59da..01e21d9 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/Routers.yml @@ -1,9 +1,9 @@ name: Build -on: [push, pull_request] +on: [push] jobs: - build: + build-Windows: runs-on: windows-latest @@ -20,4 +20,19 @@ jobs: - name: Test run: $slnList = Get-ChildItem $foo.FullName -Recurse -Filter '*.sln'; foreach ($file in $slnList) {dotnet test $file.FullName} + build-Ubuntu: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.x' + - name: Restore + run: for f in $(find . -name "*.sln"); do dotnet restore $f; done + - name: Build + run: for f in $(find . -name "*.sln"); do dotnet build $f; done + - name: Test + run: for f in $(find . -name "*.sln"); do dotnet test $f; done From 6deceffbf85f1faa8fab1225c9e42262ffaac58d Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 2 Jun 2022 01:18:50 +0300 Subject: [PATCH 5/5] rename yml --- .github/workflows/{Routers.yml => dotnet.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{Routers.yml => dotnet.yml} (100%) diff --git a/.github/workflows/Routers.yml b/.github/workflows/dotnet.yml similarity index 100% rename from .github/workflows/Routers.yml rename to .github/workflows/dotnet.yml