various undirected graph methods
parent
0b66236d78
commit
15baed3cfe
|
@ -33,6 +33,9 @@ namespace MoonTools.Core.Graph
|
||||||
public IEnumerable<TNode> Nodes => nodes;
|
public IEnumerable<TNode> Nodes => nodes;
|
||||||
public IEnumerable<(TNode, TNode)> Edges => edges;
|
public IEnumerable<(TNode, TNode)> Edges => edges;
|
||||||
|
|
||||||
|
public int Order => nodes.Count;
|
||||||
|
public int Size => edges.Count;
|
||||||
|
|
||||||
public void AddNode(TNode node)
|
public void AddNode(TNode node)
|
||||||
{
|
{
|
||||||
if (!Exists(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)>();
|
readonly List<(TNode, TNode)> edgesToRemove = new List<(TNode, TNode)>();
|
||||||
|
|
||||||
public void RemoveNode(TNode node)
|
public void RemoveNode(TNode node)
|
||||||
|
@ -96,7 +105,7 @@ namespace MoonTools.Core.Graph
|
||||||
neighbors.Remove(node);
|
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);
|
CheckNodes(v, u);
|
||||||
|
|
||||||
|
@ -105,7 +114,7 @@ namespace MoonTools.Core.Graph
|
||||||
edgesToEdgeData.Add((v, u), edgeData);
|
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)
|
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"); }
|
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);
|
CheckEdge(v, u);
|
||||||
neighbors[v].Remove(u);
|
neighbors[v].Remove(u);
|
||||||
|
edges.Remove((v, u));
|
||||||
edgesToEdgeData.Remove((v, u));
|
edgesToEdgeData.Remove((v, u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,40 +149,167 @@ namespace MoonTools.Core.Graph
|
||||||
return neighbors[node];
|
return neighbors[node];
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly HashSet<TNode> discovered = new HashSet<TNode>();
|
readonly Stack<TNode> dfsStack = new Stack<TNode>();
|
||||||
readonly Dictionary<TNode, uint> dfsOutput = new Dictionary<TNode, uint>();
|
readonly HashSet<TNode> dfsDiscovered = new HashSet<TNode>();
|
||||||
|
|
||||||
public IEnumerable<(TNode, uint)> NodeDFS()
|
public IEnumerable<TNode> PreorderNodeDFS()
|
||||||
{
|
{
|
||||||
discovered.Clear();
|
dfsStack.Clear();
|
||||||
dfsOutput.Clear();
|
dfsDiscovered.Clear();
|
||||||
uint time = 0;
|
|
||||||
|
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
|
void dfsHelper(TNode v) // refactor this to remove closure
|
||||||
{
|
{
|
||||||
discovered.Add(v);
|
dfsDiscovered.Add(v);
|
||||||
|
|
||||||
foreach (var neighbor in Neighbors(v))
|
foreach (var neighbor in Neighbors(v))
|
||||||
{
|
{
|
||||||
if (!discovered.Contains(neighbor))
|
if (!dfsDiscovered.Contains(neighbor))
|
||||||
{
|
{
|
||||||
dfsHelper(neighbor);
|
dfsHelper(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time++;
|
postorderOutput.Add(v);
|
||||||
dfsOutput[v] = time;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var node in Nodes)
|
foreach (var node in Nodes)
|
||||||
{
|
{
|
||||||
if (!discovered.Contains(node))
|
if (!dfsDiscovered.Contains(node))
|
||||||
{
|
{
|
||||||
dfsHelper(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()
|
public bool Cyclic()
|
||||||
|
@ -182,7 +319,7 @@ namespace MoonTools.Core.Graph
|
||||||
|
|
||||||
public IEnumerable<TNode> TopologicalSort()
|
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>();
|
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));
|
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]
|
[Test]
|
||||||
public void RemoveEdge()
|
public void RemoveEdge()
|
||||||
{
|
{
|
||||||
|
@ -106,15 +147,57 @@ namespace Tests
|
||||||
myGraph.AddEdges(
|
myGraph.AddEdges(
|
||||||
('a', 'b', dummyEdgeData),
|
('a', 'b', dummyEdgeData),
|
||||||
('a', 'c', 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));
|
var indexA = result.IndexOf('a');
|
||||||
result.Should().Contain(('b', 2));
|
var indexB = result.IndexOf('b');
|
||||||
result.Should().Contain(('c', 3));
|
var indexC = result.IndexOf('c');
|
||||||
result.Should().Contain(('a', 4));
|
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]
|
[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