tool/TeamCity.Docker/Generic/GraphExtensions.cs (120 lines of code) (raw):

namespace TeamCity.Docker.Generic { using System; using System.Collections.Generic; using System.Linq; using IoC; internal static class GraphExtensions { public static IEnumerable<ILink<TNode, TLink>>[,] ToAdjacencyMatrix<TNode, TLink>([NotNull] this IGraph<TNode, TLink> graph) { var indices = graph.Nodes.Select((node, index) => new { node, index }).ToDictionary(i => i.node, i => i.index); return ToAdjacencyMatrix(graph, node => indices[node]); } public static IEnumerable<ILink<TNode, TLink>>[,] ToAdjacencyMatrix<TNode, TLink>([NotNull] this IGraph<TNode, TLink> graph, Func<INode<TNode>, int> indexSelector) { if (graph == null) throw new ArgumentNullException(nameof(graph)); var matrix = new ICollection<ILink<TNode, TLink>>[graph.NodesCount, graph.NodesCount]; for (var i = 0; i < graph.NodesCount; i++) { for (var j = 0; j <= i; j++) { matrix[i, j] = new List<ILink<TNode, TLink>>(); } } for (var i = 0; i < graph.NodesCount; i++) { for (var j = i + 1; j < graph.NodesCount; j++) { matrix[i, j] = matrix[j, i]; } } foreach (var link in graph.Links) { var i = indexSelector(link.From); var j = indexSelector(link.To); matrix[i, j].Add(link); } var result = new IEnumerable<ILink<TNode, TLink>>[graph.NodesCount, graph.NodesCount]; for (var i = 0; i < graph.NodesCount; i++) { for (var j = 0; j < graph.NodesCount; j++) { result[i, j] = matrix[i, j]; } } return result; } public static IEnumerable<INode<TNode>> FindMinimumCutByStoerWagner<TNode, TLink>([NotNull] this IGraph<TNode, TLink> graph, [NotNull] Func<IEnumerable<ILink<TNode, TLink>>, int> weightSelector, out int bestCost) { if (graph == null) throw new ArgumentNullException(nameof(graph)); if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector)); var nodes = graph.Nodes.Select((node, index) => new {node, index}).ToList(); var indices = nodes.ToDictionary(i => i.node, i => i.index); var reverseIndices = nodes.ToDictionary(i => i.index, i => i.node); var nodesCount = graph.NodesCount; var matrix = graph.ToAdjacencyMatrix(node => indices[node]); var weightMatrix = new int[nodesCount, nodesCount]; for (var i = 0; i < nodesCount; i++) { for (var j = 0; j < nodesCount; j++) { weightMatrix[i, j] = weightSelector(matrix[i, j]); } } bestCost = int.MaxValue; var bestСut = new List<int>(); var activeNodes = new List<int>[nodesCount]; for (var i = 0; i < nodesCount; ++i) { activeNodes[i] = new List<int>(1) {i}; } var weights = new int[nodesCount]; var exist = new bool[nodesCount]; var inActive = new bool[nodesCount]; Array.Fill(exist, true); for (var ph = 0; ph < nodesCount - 1; ph++) { Array.Fill(inActive, false); Array.Fill(weights, 0); var previous = 0; for (var it = 0; it < nodesCount - ph; ++it) { var selected = -1; for (var i = 0; i < nodesCount; ++i) { if (exist[i] && !inActive[i] && (selected == -1 || weights[i] > weights[selected])) { selected = i; } } if (it == nodesCount - ph - 1) { if (weights[selected] < bestCost) { bestCost = weights[selected]; bestСut = activeNodes[selected]; } activeNodes[previous].AddRange(activeNodes[selected]); for (var i = 0; i < nodesCount; ++i) { weightMatrix[i, previous] += weightMatrix[selected, i]; weightMatrix[previous, i] = weightMatrix[i, previous]; } exist[selected] = false; } else { inActive[selected] = true; for (var i = 0; i < nodesCount; ++i) { weights[i] += weightMatrix[selected, i]; } previous = selected; } } } return bestСut.Select(i => reverseIndices[i]); } } }