various undirected graph methods
							parent
							
								
									0b66236d78
								
							
						
					
					
						commit
						15baed3cfe
					
				|  | @ -33,6 +33,9 @@ namespace MoonTools.Core.Graph | |||
|         public IEnumerable<TNode> Nodes => nodes; | ||||
|         public IEnumerable<(TNode, TNode)> Edges => edges; | ||||
| 
 | ||||
|         public int Order => nodes.Count; | ||||
|         public int Size => edges.Count; | ||||
| 
 | ||||
|         public void AddNode(TNode node) | ||||
|         { | ||||
|             if (!Exists(node)) | ||||
|  | @ -71,6 +74,12 @@ namespace MoonTools.Core.Graph | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public int Degree(TNode node) | ||||
|         { | ||||
|             CheckNodes(node); | ||||
|             return neighbors[node].Count; | ||||
|         } | ||||
| 
 | ||||
|         readonly List<(TNode, TNode)> edgesToRemove = new List<(TNode, TNode)>(); | ||||
| 
 | ||||
|         public void RemoveNode(TNode node) | ||||
|  | @ -96,7 +105,7 @@ namespace MoonTools.Core.Graph | |||
|             neighbors.Remove(node); | ||||
|         } | ||||
| 
 | ||||
|         public void AddEdge(TNode v, TNode u, TEdgeData edgeData) | ||||
|         public virtual void AddEdge(TNode v, TNode u, TEdgeData edgeData) | ||||
|         { | ||||
|             CheckNodes(v, u); | ||||
| 
 | ||||
|  | @ -105,7 +114,7 @@ namespace MoonTools.Core.Graph | |||
|             edgesToEdgeData.Add((v, u), edgeData); | ||||
|         } | ||||
| 
 | ||||
|         public void AddEdges(params (TNode, TNode, TEdgeData)[] edges) | ||||
|         public virtual void AddEdges(params (TNode, TNode, TEdgeData)[] edges) | ||||
|         { | ||||
|             foreach (var edge in edges) | ||||
|             { | ||||
|  | @ -119,10 +128,11 @@ namespace MoonTools.Core.Graph | |||
|             if (!Exists(v, u)) { throw new ArgumentException($"Edge between vertex {v} and vertex {u} does not exist in the graph"); } | ||||
|         } | ||||
| 
 | ||||
|         public void RemoveEdge(TNode v, TNode u) | ||||
|         public virtual void RemoveEdge(TNode v, TNode u) | ||||
|         { | ||||
|             CheckEdge(v, u); | ||||
|             neighbors[v].Remove(u); | ||||
|             edges.Remove((v, u)); | ||||
|             edgesToEdgeData.Remove((v, u)); | ||||
|         } | ||||
| 
 | ||||
|  | @ -139,40 +149,167 @@ namespace MoonTools.Core.Graph | |||
|             return neighbors[node]; | ||||
|         } | ||||
| 
 | ||||
|         readonly HashSet<TNode> discovered = new HashSet<TNode>(); | ||||
|         readonly Dictionary<TNode, uint> dfsOutput = new Dictionary<TNode, uint>(); | ||||
|         readonly Stack<TNode> dfsStack = new Stack<TNode>(); | ||||
|         readonly HashSet<TNode> dfsDiscovered = new HashSet<TNode>(); | ||||
| 
 | ||||
|         public IEnumerable<(TNode, uint)> NodeDFS() | ||||
|         public IEnumerable<TNode> PreorderNodeDFS() | ||||
|         { | ||||
|             discovered.Clear(); | ||||
|             dfsOutput.Clear(); | ||||
|             uint time = 0; | ||||
|             dfsStack.Clear(); | ||||
|             dfsDiscovered.Clear(); | ||||
| 
 | ||||
|             foreach (var node in Nodes) | ||||
|             { | ||||
|                 if (!dfsDiscovered.Contains(node)) | ||||
|                 { | ||||
|                     dfsStack.Push(node); | ||||
|                     while (dfsStack.Count > 0) | ||||
|                     { | ||||
|                         var current = dfsStack.Pop(); | ||||
|                         if (!dfsDiscovered.Contains(current)) | ||||
|                         { | ||||
|                             dfsDiscovered.Add(current); | ||||
|                             yield return current; | ||||
|                             foreach (var neighbor in Neighbors(current)) | ||||
|                             { | ||||
|                                 dfsStack.Push(neighbor); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // public IEnumerable<TNode> PostorderNodeDFS() | ||||
|         // { | ||||
|         //     dfsStack.Clear(); | ||||
|         //     dfsDiscovered.Clear(); | ||||
| 
 | ||||
|         //     foreach (var node in Nodes) | ||||
|         //     { | ||||
|         //         if (!dfsDiscovered.Contains(node)) | ||||
|         //         { | ||||
|         //             dfsStack.Push(node); | ||||
|         //             while (dfsStack.Count > 0) | ||||
|         //             { | ||||
|         //                 var current = dfsStack.Pop(); | ||||
|         //                 if (!dfsDiscovered.Contains(current)) | ||||
|         //                 { | ||||
|         //                     dfsDiscovered.Add(current); | ||||
|         //                     foreach (var neighbor in Neighbors(current)) | ||||
|         //                     { | ||||
|         //                         dfsStack.Push(neighbor); | ||||
|         //                     } | ||||
|         //                     yield return current; | ||||
|         //                 } | ||||
|         //             } | ||||
|         //         } | ||||
|         //     } | ||||
|         // } | ||||
| 
 | ||||
|         List<TNode> postorderOutput = new List<TNode>(); | ||||
| 
 | ||||
|         public IEnumerable<TNode> PostorderNodeDFS() | ||||
|         { | ||||
|             dfsDiscovered.Clear(); | ||||
|             postorderOutput.Clear(); | ||||
| 
 | ||||
|             void dfsHelper(TNode v) // refactor this to remove closure | ||||
|             { | ||||
|                 discovered.Add(v); | ||||
|                 dfsDiscovered.Add(v); | ||||
| 
 | ||||
|                 foreach (var neighbor in Neighbors(v)) | ||||
|                 { | ||||
|                     if (!discovered.Contains(neighbor)) | ||||
|                     if (!dfsDiscovered.Contains(neighbor)) | ||||
|                     { | ||||
|                         dfsHelper(neighbor); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 time++; | ||||
|                 dfsOutput[v] = time; | ||||
|                 postorderOutput.Add(v); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var node in Nodes) | ||||
|             { | ||||
|                 if (!discovered.Contains(node)) | ||||
|                 if (!dfsDiscovered.Contains(node)) | ||||
|                 { | ||||
|                     dfsHelper(node); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return dfsOutput.Select(entry => (entry.Key, entry.Value)); | ||||
|             return postorderOutput; | ||||
|         } | ||||
| 
 | ||||
|         readonly Queue<TNode> bfsQueue = new Queue<TNode>(); | ||||
|         readonly HashSet<TNode> bfsDiscovered = new HashSet<TNode>(); | ||||
| 
 | ||||
|         public IEnumerable<TNode> NodeBFS() | ||||
|         { | ||||
|             bfsQueue.Clear(); | ||||
|             bfsDiscovered.Clear(); | ||||
| 
 | ||||
|             foreach (var node in Nodes) | ||||
|             { | ||||
|                 if (!bfsDiscovered.Contains(node)) | ||||
|                 { | ||||
|                     bfsQueue.Enqueue(node); | ||||
|                     while (bfsQueue.Count > 0) | ||||
|                     { | ||||
|                         var current = bfsQueue.Dequeue(); | ||||
|                         foreach (var neighbor in Neighbors(current)) | ||||
|                         { | ||||
|                             if (!bfsDiscovered.Contains(neighbor)) | ||||
|                             { | ||||
|                                 bfsDiscovered.Add(neighbor); | ||||
|                                 bfsQueue.Enqueue(neighbor); | ||||
|                                 yield return neighbor; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // hoo boy this is bad for the GC | ||||
|         public IEnumerable<TNode> LexicographicBFS() | ||||
|         { | ||||
|             var sets = new List<List<TNode>>(); | ||||
|             sets.Add(Nodes.ToList()); | ||||
| 
 | ||||
|             while (sets.Count > 0) | ||||
|             { | ||||
|                 var firstSet = sets[0]; | ||||
|                 var node = firstSet[0]; | ||||
|                 firstSet.RemoveAt(0); | ||||
|                 if (firstSet.Count == 0) { sets.RemoveAt(0); } | ||||
| 
 | ||||
|                 yield return node; | ||||
| 
 | ||||
|                 var replaced = new List<List<TNode>>(); | ||||
| 
 | ||||
|                 foreach (var neighbor in Neighbors(node)) | ||||
|                 { | ||||
|                     if (sets.Any(set => set.Contains(neighbor))) | ||||
|                     { | ||||
|                         var s = sets.Find(set => set.Contains(neighbor)); | ||||
|                         var sIndex = sets.IndexOf(s); | ||||
|                         List<TNode> t; | ||||
|                         if (replaced.Contains(s)) | ||||
|                         { | ||||
|                             t = sets[sIndex - 1]; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             t = new List<TNode>(); | ||||
|                             sets.Insert(sIndex, t); | ||||
|                             replaced.Add(s); | ||||
|                         } | ||||
| 
 | ||||
|                         s.Remove(neighbor); | ||||
|                         t.Add(neighbor); | ||||
|                         if (s.Count == 0) { sets.Remove(s); } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public bool Cyclic() | ||||
|  | @ -182,7 +319,7 @@ namespace MoonTools.Core.Graph | |||
| 
 | ||||
|         public IEnumerable<TNode> TopologicalSort() | ||||
|         { | ||||
|             return NodeDFS().OrderByDescending(entry => entry.Item2).Select(entry => entry.Item1); | ||||
|             return PostorderNodeDFS().Reverse(); | ||||
|         } | ||||
| 
 | ||||
|         readonly Dictionary<TNode, uint> preorder = new Dictionary<TNode, uint>(); | ||||
|  |  | |||
|  | @ -0,0 +1,100 @@ | |||
| using System.Linq; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.Core.Graph | ||||
| { | ||||
|     public class UndirectedGraph<TNode, TEdgeData> : DirectedGraph<TNode, TEdgeData> where TNode : System.IEquatable<TNode> | ||||
|     { | ||||
|         enum Color { White, Gray, Black } | ||||
| 
 | ||||
|         new public int Size => edges.Count / 2; | ||||
| 
 | ||||
|         public bool Complete => Size == (Order * (Order - 1) / 2); | ||||
| 
 | ||||
|         public bool Chordal | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 var lexicographicOrder = LexicographicBFS(); | ||||
|                 return lexicographicOrder | ||||
|                     .Select((node, index) => (node, index)) | ||||
|                     .All(pair => | ||||
|                     { | ||||
|                         var (node, index) = pair; | ||||
|                         var successors = lexicographicOrder.Skip(index - 1).Take(nodes.Count - index - 1); | ||||
|                         return Clique(Neighbors(node).Intersect(successors).Union(Enumerable.Repeat(node, 1))); | ||||
|                     }); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public bool Bipartite | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 var colors = new Dictionary<TNode, Color>(); | ||||
|                 var d = new Dictionary<TNode, int>(); | ||||
|                 var partition = new Dictionary<TNode, int>(); | ||||
| 
 | ||||
|                 foreach (var node in Nodes) | ||||
|                 { | ||||
|                     colors[node] = Color.White; | ||||
|                     d[node] = int.MaxValue; | ||||
|                     partition[node] = 0; | ||||
|                 } | ||||
| 
 | ||||
|                 var start = Nodes.First(); | ||||
|                 colors[start] = Color.Gray; | ||||
|                 partition[start] = 1; | ||||
|                 d[start] = 0; | ||||
| 
 | ||||
|                 var stack = new Stack<TNode>(); | ||||
|                 stack.Push(start); | ||||
| 
 | ||||
|                 while (stack.Count > 0) | ||||
|                 { | ||||
|                     var node = stack.Pop(); | ||||
|                     foreach (var neighbor in Neighbors(node)) | ||||
|                     { | ||||
|                         if (partition[neighbor] == partition[node]) { return false; } | ||||
|                         if (colors[neighbor] == Color.White) | ||||
|                         { | ||||
|                             colors[neighbor] = Color.Gray; | ||||
|                             d[neighbor] = d[node] + 1; | ||||
|                             partition[neighbor] = 3 - partition[node]; | ||||
|                             stack.Push(neighbor); | ||||
|                         } | ||||
|                     } | ||||
|                     stack.Pop(); | ||||
|                     colors[node] = Color.Black; | ||||
|                 } | ||||
| 
 | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public override void AddEdge(TNode v, TNode u, TEdgeData edgeData) | ||||
|         { | ||||
|             base.AddEdge(v, u, edgeData); | ||||
|             base.AddEdge(u, v, edgeData); | ||||
|         } | ||||
| 
 | ||||
|         public override void AddEdges(params (TNode, TNode, TEdgeData)[] edges) | ||||
|         { | ||||
|             foreach (var edge in edges) | ||||
|             { | ||||
|                 AddEdge(edge.Item1, edge.Item2, edge.Item3); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public override void RemoveEdge(TNode v, TNode u) | ||||
|         { | ||||
|             base.RemoveEdge(v, u); | ||||
|             base.RemoveEdge(u, v); | ||||
|         } | ||||
| 
 | ||||
|         public bool Clique(IEnumerable<TNode> nodeList) | ||||
|         { | ||||
|             return nodeList.All(node => nodeList.All(other => Neighbors(node).Contains(other) || node.Equals(other))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -61,6 +61,47 @@ namespace Tests | |||
|             Assert.That(myGraph.Neighbors(1), Does.Not.Contain(4)); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Order() | ||||
|         { | ||||
|             var myGraph = new DirectedGraph<int, EdgeData>(); | ||||
|             myGraph.AddNodes(1, 2, 3, 4); | ||||
| 
 | ||||
|             myGraph.Order.Should().Be(4); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Size() | ||||
|         { | ||||
|             var myGraph = new DirectedGraph<int, EdgeData>(); | ||||
|             myGraph.AddNodes(1, 2, 3, 4); | ||||
|             myGraph.AddEdges( | ||||
|                 (1, 2, dummyEdgeData), | ||||
|                 (2, 3, dummyEdgeData), | ||||
|                 (2, 4, dummyEdgeData), | ||||
|                 (3, 4, dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             myGraph.Size.Should().Be(4); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Degree() | ||||
|         { | ||||
|             var myGraph = new DirectedGraph<int, EdgeData>(); | ||||
|             myGraph.AddNodes(1, 2, 3, 4); | ||||
|             myGraph.AddEdges( | ||||
|                 (1, 2, dummyEdgeData), | ||||
|                 (2, 3, dummyEdgeData), | ||||
|                 (2, 4, dummyEdgeData), | ||||
|                 (3, 4, dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             myGraph.Degree(1).Should().Be(1); | ||||
|             myGraph.Degree(2).Should().Be(2); | ||||
|             myGraph.Degree(3).Should().Be(1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void RemoveEdge() | ||||
|         { | ||||
|  | @ -106,15 +147,57 @@ namespace Tests | |||
|             myGraph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData) | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('c', 'd', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             var result = myGraph.NodeDFS(); | ||||
|             var result = myGraph.PreorderNodeDFS().ToList(); | ||||
| 
 | ||||
|             result.Should().Contain(('d', 1)); | ||||
|             result.Should().Contain(('b', 2)); | ||||
|             result.Should().Contain(('c', 3)); | ||||
|             result.Should().Contain(('a', 4)); | ||||
|             var indexA = result.IndexOf('a'); | ||||
|             var indexB = result.IndexOf('b'); | ||||
|             var indexC = result.IndexOf('c'); | ||||
|             var indexD = result.IndexOf('d'); | ||||
| 
 | ||||
|             Assert.That(indexA < indexB && indexA < indexC); | ||||
|             Assert.That(indexB < indexD || indexC < indexD); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void NodeBFS() | ||||
|         { | ||||
|             var myGraph = new DirectedGraph<char, EdgeData>(); | ||||
|             myGraph.AddNodes('a', 'b', 'c', 'd', 'e'); | ||||
|             myGraph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('c', 'e', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             var result = myGraph.NodeBFS().ToList(); | ||||
| 
 | ||||
|             result.IndexOf('a').Should().BeLessThan(result.IndexOf('b')); | ||||
|             result.IndexOf('a').Should().BeLessThan(result.IndexOf('c')); | ||||
|             result.IndexOf('b').Should().BeLessThan(result.IndexOf('d')); | ||||
|             result.IndexOf('c').Should().BeLessThan(result.IndexOf('e')); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void LexicographicBFS() | ||||
|         { | ||||
|             var graph = new DirectedGraph<char, EdgeData>(); | ||||
|             graph.AddNodes('a', 'b', 'c', 'd', 'e'); | ||||
|             graph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('b', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('b', 'e', dummyEdgeData), | ||||
|                 ('c', 'd', dummyEdgeData), | ||||
|                 ('d', 'e', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             graph.LexicographicBFS().Should().ContainInOrder(new char[] { 'a', 'b', 'c', 'd', 'e' }); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|  | @ -0,0 +1,134 @@ | |||
| using NUnit.Framework; | ||||
| using FluentAssertions; | ||||
| 
 | ||||
| using MoonTools.Core.Graph; | ||||
| 
 | ||||
| namespace Tests | ||||
| { | ||||
|     public class UndirectedGraphTests | ||||
|     { | ||||
|         EdgeData dummyEdgeData; | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Size() | ||||
|         { | ||||
|             var graph = new UndirectedGraph<char, EdgeData>(); | ||||
|             graph.AddNodes('a', 'b', 'c', 'd', 'e'); | ||||
|             graph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('a', 'd', dummyEdgeData), | ||||
|                 ('b', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('c', 'd', dummyEdgeData), | ||||
|                 ('c', 'e', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             graph.Size.Should().Be(7); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Degree() | ||||
|         { | ||||
|             var graph = new UndirectedGraph<char, EdgeData>(); | ||||
|             graph.AddNodes('a', 'b', 'c', 'd', 'e'); | ||||
|             graph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('a', 'd', dummyEdgeData), | ||||
|                 ('b', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('c', 'd', dummyEdgeData), | ||||
|                 ('c', 'e', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             graph.Degree('a').Should().Be(3); | ||||
|             graph.Degree('b').Should().Be(3); | ||||
|             graph.Degree('c').Should().Be(4); | ||||
|             graph.Degree('d').Should().Be(3); | ||||
|             graph.Degree('e').Should().Be(1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Clique() | ||||
|         { | ||||
|             var graph = new UndirectedGraph<char, EdgeData>(); | ||||
|             graph.AddNodes('a', 'b', 'c', 'd', 'e'); | ||||
|             graph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('a', 'd', dummyEdgeData), | ||||
|                 ('b', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('c', 'd', dummyEdgeData), | ||||
|                 ('c', 'e', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             graph.Clique(new char[] { 'a', 'b', 'c', 'd' }).Should().BeTrue(); | ||||
|             graph.Clique(new char[] { 'a', 'b', 'c', 'd', 'e' }).Should().BeFalse(); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Chordal() | ||||
|         { | ||||
|             var graph = new UndirectedGraph<char, EdgeData>(); | ||||
|             graph.AddNodes('a', 'b', 'c', 'd', 'e'); | ||||
|             graph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('b', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('b', 'e', dummyEdgeData), | ||||
|                 ('c', 'd', dummyEdgeData), | ||||
|                 ('d', 'e', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             graph.Chordal.Should().BeTrue(); | ||||
| 
 | ||||
|             graph.AddNode('f'); | ||||
|             graph.AddEdge('a', 'f', dummyEdgeData); | ||||
| 
 | ||||
|             graph.Chordal.Should().BeFalse(); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Complete() | ||||
|         { | ||||
|             var graph = new UndirectedGraph<char, EdgeData>(); | ||||
|             graph.AddNodes('a', 'b', 'c', 'd'); | ||||
|             graph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('a', 'd', dummyEdgeData), | ||||
|                 ('b', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('c', 'd', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             graph.Complete.Should().BeTrue(); | ||||
| 
 | ||||
|             graph.RemoveEdge('b', 'c'); | ||||
| 
 | ||||
|             graph.Complete.Should().BeFalse(); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Bipartite() | ||||
|         { | ||||
|             var graph = new UndirectedGraph<char, EdgeData>(); | ||||
|             graph.AddNodes('a', 'b', 'c', 'd', 'e'); | ||||
|             graph.AddEdges( | ||||
|                 ('a', 'b', dummyEdgeData), | ||||
|                 ('a', 'c', dummyEdgeData), | ||||
|                 ('b', 'd', dummyEdgeData), | ||||
|                 ('c', 'e', dummyEdgeData) | ||||
|             ); | ||||
| 
 | ||||
|             graph.Bipartite.Should().BeTrue(); | ||||
| 
 | ||||
|             graph.AddEdge('a', 'e', dummyEdgeData); | ||||
| 
 | ||||
|             graph.Bipartite.Should().BeFalse(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue