initial commit
commit
ad8f97e674
|
@ -0,0 +1,222 @@
|
||||||
|
# The following command works for downloading when using Git for Windows:
|
||||||
|
# curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore
|
||||||
|
#
|
||||||
|
# Download this file using PowerShell v3 under Windows with the following comand:
|
||||||
|
# Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore
|
||||||
|
#
|
||||||
|
# or wget:
|
||||||
|
# wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
x64/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
# build folder is nowadays used for build scripts and should not be ignored
|
||||||
|
#build/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.log
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# OS generated files #
|
||||||
|
.DS_Store*
|
||||||
|
Icon?
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
*.ncrunch*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.Publish.xml
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
*.Cache
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
modulesbin/
|
||||||
|
tempbin/
|
||||||
|
|
||||||
|
# EPiServer Site file (VPP)
|
||||||
|
AppData/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file to a newer
|
||||||
|
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# vim
|
||||||
|
*.txt~
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Temp files when opening LibreOffice on ubuntu
|
||||||
|
.~lock.*
|
||||||
|
|
||||||
|
# svn
|
||||||
|
.svn
|
||||||
|
|
||||||
|
# CVS - Source Control
|
||||||
|
**/CVS/
|
||||||
|
|
||||||
|
# Remainings from resolving conflicts in Source Control
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
**/App_Data/*.mdf
|
||||||
|
**/App_Data/*.ldf
|
||||||
|
**/App_Data/*.sdf
|
||||||
|
|
||||||
|
|
||||||
|
#LightSwitch generated files
|
||||||
|
GeneratedArtifacts/
|
||||||
|
_Pvt_Extensions/
|
||||||
|
ModelManifest.xml
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Windows detritus
|
||||||
|
# =========================
|
||||||
|
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Mac desktop service store files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# SASS Compiler cache
|
||||||
|
.sass-cache
|
||||||
|
|
||||||
|
# Visual Studio 2014 CTP
|
||||||
|
**/*.sln.ide
|
||||||
|
|
||||||
|
# Visual Studio temp something
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# dotnet stuff
|
||||||
|
project.lock.json
|
||||||
|
|
||||||
|
# VS 2015+
|
||||||
|
*.vc.vc.opendb
|
||||||
|
*.vc.db
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Output folder used by Webpack or other FE stuff
|
||||||
|
**/node_modules/*
|
||||||
|
**/wwwroot/*
|
||||||
|
|
||||||
|
# SpecFlow specific
|
||||||
|
*.feature.cs
|
||||||
|
*.feature.xlsx.*
|
||||||
|
*.Specs_*.html
|
||||||
|
|
||||||
|
#####
|
||||||
|
# End of core ignore list, below put you custom 'per project' settings (patterns or path)
|
||||||
|
#####
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
|
// Use hover for the description of the existing attributes
|
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": ".NET Core Launch (console)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
|
"program": "${workspaceFolder}/test/bin/Debug/netcoreapp3.0/test.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/test",
|
||||||
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
"console": "internalConsole",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach",
|
||||||
|
"processId": "${command:pickProcess}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/test/test.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "publish",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"publish",
|
||||||
|
"${workspaceFolder}/test/test.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "watch",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"watch",
|
||||||
|
"run",
|
||||||
|
"${workspaceFolder}/test/test.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,419 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace MoonTools.Core.Graph
|
||||||
|
{
|
||||||
|
public enum SearchSymbol
|
||||||
|
{
|
||||||
|
start,
|
||||||
|
finish
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DirectedGraph<T>
|
||||||
|
{
|
||||||
|
private class SimpleCycleComparer : IEqualityComparer<IEnumerable<T>>
|
||||||
|
{
|
||||||
|
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
|
||||||
|
{
|
||||||
|
return x.SequenceEqual(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(IEnumerable<T> obj)
|
||||||
|
{
|
||||||
|
return obj.Aggregate(0, (current, next) => current.GetHashCode() ^ next.GetHashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<T> _vertices = new List<T>();
|
||||||
|
protected Dictionary<T, HashSet<T>> _neighbors = new Dictionary<T, HashSet<T>>();
|
||||||
|
|
||||||
|
public IEnumerable<T> Vertices { get { return _vertices; } }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GRAPH STRUCTURE METHODS
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void AddVertex(T vertex)
|
||||||
|
{
|
||||||
|
if (!VertexExists(vertex))
|
||||||
|
{
|
||||||
|
_vertices.Add(vertex);
|
||||||
|
_neighbors.Add(vertex, new HashSet<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddVertices(params T[] vertices)
|
||||||
|
{
|
||||||
|
foreach (var vertex in vertices)
|
||||||
|
{
|
||||||
|
AddVertex(vertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool VertexExists(T vertex)
|
||||||
|
{
|
||||||
|
return Vertices.Contains(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveVertex(T vertex)
|
||||||
|
{
|
||||||
|
var edgesToRemove = new List<Tuple<T, T>>();
|
||||||
|
|
||||||
|
if (VertexExists(vertex))
|
||||||
|
{
|
||||||
|
foreach (var entry in _neighbors)
|
||||||
|
{
|
||||||
|
if (entry.Value.Contains(vertex))
|
||||||
|
{
|
||||||
|
edgesToRemove.Add(Tuple.Create(entry.Key, vertex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var edge in edgesToRemove)
|
||||||
|
{
|
||||||
|
RemoveEdge(edge.Item1, edge.Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
_vertices.Remove(vertex);
|
||||||
|
_neighbors.Remove(vertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEdge(T v, T u)
|
||||||
|
{
|
||||||
|
if (VertexExists(v) && VertexExists(u))
|
||||||
|
{
|
||||||
|
_neighbors[v].Add(u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEdges(params Tuple<T, T>[] edges)
|
||||||
|
{
|
||||||
|
foreach (var edge in edges)
|
||||||
|
{
|
||||||
|
AddEdge(edge.Item1, edge.Item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveEdge(T v, T u)
|
||||||
|
{
|
||||||
|
_neighbors[v].Remove(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> Neighbors(T vertex)
|
||||||
|
{
|
||||||
|
if (VertexExists(vertex))
|
||||||
|
{
|
||||||
|
return _neighbors[vertex];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GRAPH ANALYSIS METHODS
|
||||||
|
*/
|
||||||
|
|
||||||
|
public Dictionary<T, Dictionary<SearchSymbol, uint>> NodeDFS()
|
||||||
|
{
|
||||||
|
var discovered = new HashSet<T>();
|
||||||
|
uint time = 0;
|
||||||
|
var output = new Dictionary<T, Dictionary<SearchSymbol, uint>>();
|
||||||
|
|
||||||
|
foreach (var vertex in Vertices)
|
||||||
|
{
|
||||||
|
output.Add(vertex, new Dictionary<SearchSymbol, uint>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void dfsHelper(T v)
|
||||||
|
{
|
||||||
|
discovered.Add(v);
|
||||||
|
time++;
|
||||||
|
output[v].Add(SearchSymbol.start, time);
|
||||||
|
|
||||||
|
foreach (var neighbor in Neighbors(v))
|
||||||
|
{
|
||||||
|
if (!discovered.Contains(neighbor))
|
||||||
|
{
|
||||||
|
dfsHelper(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time++;
|
||||||
|
output[v].Add(SearchSymbol.finish, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var vertex in Vertices)
|
||||||
|
{
|
||||||
|
if (!discovered.Contains(vertex))
|
||||||
|
{
|
||||||
|
dfsHelper(vertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Cyclic()
|
||||||
|
{
|
||||||
|
return StronglyConnectedComponents().Any((scc) => scc.Count() > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> TopologicalSort()
|
||||||
|
{
|
||||||
|
var dfs = NodeDFS();
|
||||||
|
var priority = new SortedList<uint, T>();
|
||||||
|
foreach (var entry in dfs)
|
||||||
|
{
|
||||||
|
priority.Add(entry.Value[SearchSymbol.finish], entry.Key);
|
||||||
|
}
|
||||||
|
return priority.Values.Reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IEnumerable<T>> StronglyConnectedComponents()
|
||||||
|
{
|
||||||
|
var preorder = new Dictionary<T, uint>();
|
||||||
|
var lowlink = new Dictionary<T, uint>();
|
||||||
|
var sccFound = new Dictionary<T, bool>();
|
||||||
|
var sccQueue = new Stack<T>();
|
||||||
|
|
||||||
|
var result = new List<List<T>>();
|
||||||
|
|
||||||
|
uint preorderCounter = 0;
|
||||||
|
|
||||||
|
foreach (var source in Vertices)
|
||||||
|
{
|
||||||
|
if (!sccFound.ContainsKey(source))
|
||||||
|
{
|
||||||
|
var queue = new Stack<T>();
|
||||||
|
queue.Push(source);
|
||||||
|
|
||||||
|
while (queue.Count > 0)
|
||||||
|
{
|
||||||
|
var v = queue.Peek();
|
||||||
|
if (!preorder.ContainsKey(v))
|
||||||
|
{
|
||||||
|
preorderCounter++;
|
||||||
|
preorder[v] = preorderCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
var done = true;
|
||||||
|
var vNeighbors = Neighbors(v);
|
||||||
|
foreach (var w in vNeighbors)
|
||||||
|
{
|
||||||
|
if (!preorder.ContainsKey(w))
|
||||||
|
{
|
||||||
|
queue.Push(w);
|
||||||
|
done = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done)
|
||||||
|
{
|
||||||
|
lowlink[v] = preorder[v];
|
||||||
|
foreach (var w in vNeighbors)
|
||||||
|
{
|
||||||
|
if (!sccFound.ContainsKey(w))
|
||||||
|
{
|
||||||
|
if (preorder[w] > preorder[v])
|
||||||
|
{
|
||||||
|
lowlink[v] = Math.Min(lowlink[v], lowlink[w]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lowlink[v] = Math.Min(lowlink[v], preorder[w]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queue.Pop();
|
||||||
|
if (lowlink[v] == preorder[v])
|
||||||
|
{
|
||||||
|
sccFound[v] = true;
|
||||||
|
var scc = new List<T>
|
||||||
|
{
|
||||||
|
v
|
||||||
|
};
|
||||||
|
while (sccQueue.Count > 0 && preorder[sccQueue.Peek()] > preorder[v])
|
||||||
|
{
|
||||||
|
var k = sccQueue.Pop();
|
||||||
|
sccFound[k] = true;
|
||||||
|
scc.Add(k);
|
||||||
|
}
|
||||||
|
result.Add(scc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sccQueue.Push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IEnumerable<T>> SimpleCycles()
|
||||||
|
{
|
||||||
|
void unblock(T thisnode, HashSet<T> blocked, Dictionary<T, HashSet<T>> B)
|
||||||
|
{
|
||||||
|
var stack = new Stack<T>();
|
||||||
|
stack.Push(thisnode);
|
||||||
|
|
||||||
|
while (stack.Count > 0)
|
||||||
|
{
|
||||||
|
var node = stack.Pop();
|
||||||
|
if (blocked.Contains(thisnode))
|
||||||
|
{
|
||||||
|
blocked.Remove(thisnode);
|
||||||
|
if (B.ContainsKey(node))
|
||||||
|
{
|
||||||
|
foreach (var n in B[node])
|
||||||
|
{
|
||||||
|
if (!stack.Contains(n))
|
||||||
|
{
|
||||||
|
stack.Push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
B[node].Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<List<T>> result = new List<List<T>>();
|
||||||
|
var subGraph = Clone();
|
||||||
|
|
||||||
|
var sccs = new Stack<IEnumerable<T>>();
|
||||||
|
foreach (var scc in StronglyConnectedComponents())
|
||||||
|
{
|
||||||
|
sccs.Push(scc);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sccs.Count > 0)
|
||||||
|
{
|
||||||
|
var scc = new Stack<T>(sccs.Pop());
|
||||||
|
var startNode = scc.Pop();
|
||||||
|
var path = new Stack<T>();
|
||||||
|
path.Push(startNode);
|
||||||
|
var blocked = new HashSet<T>
|
||||||
|
{
|
||||||
|
startNode
|
||||||
|
};
|
||||||
|
var closed = new HashSet<T>();
|
||||||
|
var B = new Dictionary<T, HashSet<T>>();
|
||||||
|
var stack = new Stack<Tuple<T, Stack<T>>>();
|
||||||
|
stack.Push(Tuple.Create(startNode, new Stack<T>(subGraph.Neighbors(startNode))));
|
||||||
|
|
||||||
|
while (stack.Count > 0)
|
||||||
|
{
|
||||||
|
var entry = stack.Peek();
|
||||||
|
var thisnode = entry.Item1;
|
||||||
|
var neighbors = entry.Item2;
|
||||||
|
|
||||||
|
if (neighbors.Count > 0)
|
||||||
|
{
|
||||||
|
var nextNode = neighbors.Pop();
|
||||||
|
|
||||||
|
if (nextNode.Equals(startNode))
|
||||||
|
{
|
||||||
|
var resultPath = new List<T>();
|
||||||
|
foreach (var v in path)
|
||||||
|
{
|
||||||
|
resultPath.Add(v);
|
||||||
|
}
|
||||||
|
result.Add(resultPath);
|
||||||
|
foreach (var v in path)
|
||||||
|
{
|
||||||
|
closed.Add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!blocked.Contains(nextNode))
|
||||||
|
{
|
||||||
|
path.Push(nextNode);
|
||||||
|
stack.Push(Tuple.Create(nextNode, new Stack<T>(subGraph.Neighbors(nextNode))));
|
||||||
|
closed.Remove(nextNode);
|
||||||
|
blocked.Add(nextNode);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neighbors.Count == 0)
|
||||||
|
{
|
||||||
|
if (closed.Contains(thisnode))
|
||||||
|
{
|
||||||
|
unblock(thisnode, blocked, B);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var neighbor in subGraph.Neighbors(thisnode))
|
||||||
|
{
|
||||||
|
if (!B.ContainsKey(neighbor))
|
||||||
|
{
|
||||||
|
B[neighbor] = new HashSet<T>();
|
||||||
|
}
|
||||||
|
B[neighbor].Add(thisnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.Pop();
|
||||||
|
path.Pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph.RemoveVertex(startNode);
|
||||||
|
var H = subGraph.SubGraph(scc.ToArray());
|
||||||
|
var HSccs = H.StronglyConnectedComponents();
|
||||||
|
foreach (var HScc in HSccs)
|
||||||
|
{
|
||||||
|
sccs.Push(HScc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Distinct(new SimpleCycleComparer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectedGraph<T> Clone()
|
||||||
|
{
|
||||||
|
var clone = new DirectedGraph<T>();
|
||||||
|
clone.AddVertices(Vertices.ToArray());
|
||||||
|
|
||||||
|
foreach (var v in Vertices)
|
||||||
|
{
|
||||||
|
foreach (var n in Neighbors(v))
|
||||||
|
{
|
||||||
|
clone.AddEdge(v, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectedGraph<T> SubGraph(params T[] subVertices)
|
||||||
|
{
|
||||||
|
var subGraph = new DirectedGraph<T>();
|
||||||
|
subGraph.AddVertices(subVertices.ToArray());
|
||||||
|
|
||||||
|
foreach (var v in Vertices)
|
||||||
|
{
|
||||||
|
if (Vertices.Contains(v))
|
||||||
|
{
|
||||||
|
var neighbors = Neighbors(v);
|
||||||
|
foreach (var u in neighbors)
|
||||||
|
{
|
||||||
|
if (subVertices.Contains(u))
|
||||||
|
{
|
||||||
|
subGraph.AddEdge(v, u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return subGraph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace MoonTools.Core.Graph
|
||||||
|
{
|
||||||
|
public class UndirectedGraph<T>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.26124.0
|
||||||
|
MinimumVisualStudioVersion = 15.0.26124.0
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonTools.Core.Graph", "Graph\MoonTools.Core.Graph.csproj", "{424ACD00-5613-4DBF-8D79-6509D7841D8A}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{424ACD00-5613-4DBF-8D79-6509D7841D8A}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{423B5FFF-5B19-4D1C-ACF7-B5908E3E50EA}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,373 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using FluentAssertions;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using MoonTools.Core.Graph;
|
||||||
|
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
public class DirectedGraphTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void AddVertex()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertex(4);
|
||||||
|
|
||||||
|
Assert.That(myGraph.Vertices, Does.Contain(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddVertices()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(4, 20, 69);
|
||||||
|
|
||||||
|
Assert.IsTrue(myGraph.VertexExists(4));
|
||||||
|
Assert.IsTrue(myGraph.VertexExists(20));
|
||||||
|
Assert.IsTrue(myGraph.VertexExists(69));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddEdge()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(5, 6);
|
||||||
|
myGraph.AddEdge(5, 6);
|
||||||
|
|
||||||
|
Assert.That(myGraph.Neighbors(5), Does.Contain(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddEdges()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(2, 4),
|
||||||
|
Tuple.Create(3, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.That(myGraph.Neighbors(1), Does.Contain(2));
|
||||||
|
Assert.That(myGraph.Neighbors(2), Does.Contain(3));
|
||||||
|
Assert.That(myGraph.Neighbors(2), Does.Contain(4));
|
||||||
|
Assert.That(myGraph.Neighbors(3), Does.Contain(4));
|
||||||
|
Assert.That(myGraph.Neighbors(1), Does.Not.Contain(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RemoveEdge()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(2, 4),
|
||||||
|
Tuple.Create(3, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.RemoveEdge(2, 3);
|
||||||
|
|
||||||
|
Assert.That(myGraph.Neighbors(2), Does.Not.Contain(3));
|
||||||
|
Assert.That(myGraph.Neighbors(2), Does.Contain(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RemoveVertex()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(2, 4),
|
||||||
|
Tuple.Create(3, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.RemoveVertex(2);
|
||||||
|
|
||||||
|
myGraph.Vertices.Should().NotContain(2);
|
||||||
|
myGraph.Neighbors(1).Should().NotContain(2);
|
||||||
|
myGraph.Neighbors(3).Should().Contain(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void NodeDFS()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<char>();
|
||||||
|
myGraph.AddVertices('a', 'b', 'c', 'd');
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create('a', 'b'),
|
||||||
|
Tuple.Create('a', 'c'),
|
||||||
|
Tuple.Create('b', 'd')
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = myGraph.NodeDFS();
|
||||||
|
|
||||||
|
Assert.That(result['a'][SearchSymbol.start], Is.EqualTo(1));
|
||||||
|
Assert.That(result['a'][SearchSymbol.finish], Is.EqualTo(8));
|
||||||
|
|
||||||
|
Assert.That(result['b'][SearchSymbol.start], Is.EqualTo(2));
|
||||||
|
Assert.That(result['b'][SearchSymbol.finish], Is.EqualTo(5));
|
||||||
|
|
||||||
|
Assert.That(result['c'][SearchSymbol.start], Is.EqualTo(6));
|
||||||
|
Assert.That(result['c'][SearchSymbol.finish], Is.EqualTo(7));
|
||||||
|
|
||||||
|
Assert.That(result['d'][SearchSymbol.start], Is.EqualTo(3));
|
||||||
|
Assert.That(result['d'][SearchSymbol.finish], Is.EqualTo(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TopologicalSortSimple()
|
||||||
|
{
|
||||||
|
var simpleGraph = new DirectedGraph<char>();
|
||||||
|
simpleGraph.AddVertices('a', 'b', 'c', 'd');
|
||||||
|
simpleGraph.AddEdges(
|
||||||
|
Tuple.Create('a', 'b'),
|
||||||
|
Tuple.Create('a', 'c'),
|
||||||
|
Tuple.Create('b', 'a'),
|
||||||
|
Tuple.Create('b', 'd')
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.That(simpleGraph.TopologicalSort(), Is.EqualTo(new char[] { 'a', 'c', 'b', 'd' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TopologicalSortComplex()
|
||||||
|
{
|
||||||
|
var complexGraph = new DirectedGraph<char>();
|
||||||
|
complexGraph.AddVertices('a', 'b', 'c', 'd', 'e', 'f', 'g', 't', 'm');
|
||||||
|
complexGraph.AddEdges(
|
||||||
|
Tuple.Create('a', 'b'),
|
||||||
|
Tuple.Create('a', 'c'),
|
||||||
|
Tuple.Create('a', 'd'),
|
||||||
|
Tuple.Create('b', 'f'),
|
||||||
|
Tuple.Create('b', 'g'),
|
||||||
|
Tuple.Create('c', 'g'),
|
||||||
|
Tuple.Create('e', 't'),
|
||||||
|
Tuple.Create('t', 'm')
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.That(
|
||||||
|
complexGraph.TopologicalSort(),
|
||||||
|
Is.EqualTo(new char[] { 'e', 't', 'm', 'a', 'd', 'c', 'b', 'g', 'f' })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StronglyConnectedComponentsSimple()
|
||||||
|
{
|
||||||
|
var simpleGraph = new DirectedGraph<int>();
|
||||||
|
simpleGraph.AddVertices(1, 2, 3);
|
||||||
|
simpleGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(3, 2),
|
||||||
|
Tuple.Create(2, 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = simpleGraph.StronglyConnectedComponents();
|
||||||
|
var scc = new int[] { 1, 2, 3 };
|
||||||
|
|
||||||
|
result.Should().ContainEquivalentOf(scc);
|
||||||
|
Assert.That(result.Count, Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StronglyConnectedComponentsMedium()
|
||||||
|
{
|
||||||
|
var mediumGraph = new DirectedGraph<int>();
|
||||||
|
mediumGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
mediumGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(1, 3),
|
||||||
|
Tuple.Create(1, 4),
|
||||||
|
Tuple.Create(4, 2),
|
||||||
|
Tuple.Create(3, 4),
|
||||||
|
Tuple.Create(2, 3)
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = mediumGraph.StronglyConnectedComponents();
|
||||||
|
var sccA = new int[] { 2, 3, 4 };
|
||||||
|
var sccB = new int[] { 1 };
|
||||||
|
|
||||||
|
result.Should().ContainEquivalentOf(sccA);
|
||||||
|
result.Should().ContainEquivalentOf(sccB);
|
||||||
|
Assert.That(result.Count, Is.EqualTo(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StronglyConnectedComponentsComplex()
|
||||||
|
{
|
||||||
|
var complexGraph = new DirectedGraph<int>();
|
||||||
|
complexGraph.AddVertices(1, 2, 3, 4, 5, 6, 7, 8);
|
||||||
|
complexGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(2, 8),
|
||||||
|
Tuple.Create(3, 4),
|
||||||
|
Tuple.Create(3, 7),
|
||||||
|
Tuple.Create(4, 5),
|
||||||
|
Tuple.Create(5, 3),
|
||||||
|
Tuple.Create(5, 6),
|
||||||
|
Tuple.Create(7, 4),
|
||||||
|
Tuple.Create(7, 6),
|
||||||
|
Tuple.Create(8, 1),
|
||||||
|
Tuple.Create(8, 7)
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = complexGraph.StronglyConnectedComponents();
|
||||||
|
var sccA = new int[] { 3, 4, 5, 7 };
|
||||||
|
var sccB = new int[] { 1, 2, 8 };
|
||||||
|
var sccC = new int[] { 6 };
|
||||||
|
|
||||||
|
result.Should().ContainEquivalentOf(sccA);
|
||||||
|
result.Should().ContainEquivalentOf(sccB);
|
||||||
|
result.Should().ContainEquivalentOf(sccC);
|
||||||
|
Assert.That(result.Count, Is.EqualTo(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Clone()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 1),
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(2, 1),
|
||||||
|
Tuple.Create(3, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
var clone = myGraph.Clone();
|
||||||
|
Assert.That(clone, Is.Not.EqualTo(myGraph));
|
||||||
|
clone.Vertices.Should().BeEquivalentTo(1, 2, 3, 4);
|
||||||
|
clone.Neighbors(1).Should().BeEquivalentTo(1, 2);
|
||||||
|
clone.Neighbors(2).Should().BeEquivalentTo(3, 1);
|
||||||
|
clone.Neighbors(3).Should().BeEquivalentTo(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SubGraph()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 1),
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(2, 1),
|
||||||
|
Tuple.Create(3, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
var subGraph = myGraph.SubGraph(1, 2, 3);
|
||||||
|
subGraph.Vertices.Should().BeEquivalentTo(1, 2, 3);
|
||||||
|
subGraph.Neighbors(1).Should().BeEquivalentTo(1, 2);
|
||||||
|
subGraph.Neighbors(2).Should().BeEquivalentTo(1, 3);
|
||||||
|
subGraph.Neighbors(3).Should().NotContain(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SimpleCyclesSimple()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(0, 1, 2);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(0, 0),
|
||||||
|
Tuple.Create(0, 1),
|
||||||
|
Tuple.Create(0, 2),
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 0),
|
||||||
|
Tuple.Create(2, 1),
|
||||||
|
Tuple.Create(2, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = myGraph.SimpleCycles();
|
||||||
|
|
||||||
|
var cycleA = new int[] { 0 };
|
||||||
|
var cycleB = new int[] { 0, 1, 2 };
|
||||||
|
var cycleC = new int[] { 0, 2 };
|
||||||
|
var cycleD = new int[] { 1, 2 };
|
||||||
|
var cycleE = new int[] { 2 };
|
||||||
|
|
||||||
|
result.Should().ContainEquivalentOf(cycleA);
|
||||||
|
result.Should().ContainEquivalentOf(cycleB);
|
||||||
|
result.Should().ContainEquivalentOf(cycleC);
|
||||||
|
result.Should().ContainEquivalentOf(cycleD);
|
||||||
|
result.Should().ContainEquivalentOf(cycleE);
|
||||||
|
result.Should().HaveCount(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SimpleCyclesComplex()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(0, 1),
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(3, 0),
|
||||||
|
Tuple.Create(0, 3),
|
||||||
|
Tuple.Create(3, 4),
|
||||||
|
Tuple.Create(4, 5),
|
||||||
|
Tuple.Create(5, 0),
|
||||||
|
Tuple.Create(0, 1),
|
||||||
|
Tuple.Create(1, 6),
|
||||||
|
Tuple.Create(6, 7),
|
||||||
|
Tuple.Create(7, 8),
|
||||||
|
Tuple.Create(8, 0),
|
||||||
|
Tuple.Create(8, 9)
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = myGraph.SimpleCycles();
|
||||||
|
var cycleA = new int[] { 0, 3 };
|
||||||
|
var cycleB = new int[] { 0, 1, 2, 3, 4, 5 };
|
||||||
|
var cycleC = new int[] { 0, 1, 2, 3 };
|
||||||
|
var cycleD = new int[] { 0, 3, 4, 5 };
|
||||||
|
var cycleE = new int[] { 0, 1, 6, 7, 8 };
|
||||||
|
|
||||||
|
result.Should().ContainEquivalentOf(cycleA);
|
||||||
|
result.Should().ContainEquivalentOf(cycleB);
|
||||||
|
result.Should().ContainEquivalentOf(cycleC);
|
||||||
|
result.Should().ContainEquivalentOf(cycleD);
|
||||||
|
result.Should().ContainEquivalentOf(cycleE);
|
||||||
|
result.Should().HaveCount(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Cyclic()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(3, 1),
|
||||||
|
Tuple.Create(3, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.That(myGraph.Cyclic(), Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Acyclic()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedGraph<int>();
|
||||||
|
myGraph.AddVertices(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
Tuple.Create(1, 2),
|
||||||
|
Tuple.Create(2, 3),
|
||||||
|
Tuple.Create(3, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.That(myGraph.Cyclic(), Is.False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="nunit" Version="3.12.0"/>
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0"/>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0"/>
|
||||||
|
<PackageReference Include="FluentAssertions" Version="5.9.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Graph\MoonTools.Core.Graph.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
Loading…
Reference in New Issue