/// <summary> /// This is a wrapper around <see cref="StronglyConnectedComponentFinder{T}.DetectCycle"/> that uses the same way to setup dependencies as <see cref="DetectCycles{T}"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">Required. The sequence of elements that need to be sorted.</param> /// <param name="dependencySelector">Required. A delegate that takes an items of <see cref="source"/> and returns a sequence of dependencies. /// <remarks>Can return null to indicate no dependency.</remarks></param> /// <param name="comparer">Not required. A n implementation of <see cref="IEqualityComparer{T}"/>, this is used to compare the values with their dependencies.</param> public static StronglyConnectedComponentList <T> ResolveStronglyConnectedComponents <T>( IEnumerable <T> source, Func <T, IEnumerable <T> > dependencySelector, IEqualityComparer <T> comparer = null) { var valueComparer = comparer ?? EqualityComparer <T> .Default; var vertexComparer = new VertexValueComparer <T>(valueComparer); var finder = new StronglyConnectedComponentFinder <T>(); var vertices = VertexBuilder.BuildVertices(source, dependencySelector, valueComparer); return(finder.DetectCycle(vertices, vertexComparer)); }
public void CycleWithDuplicates() { var testValues = new { A = TestValue.Create("a ", "e"), B = TestValue.Create(" B", " c "), A2 = TestValue.Create("A ", "C"), C = TestValue.Create("C", "A", "D"), D = TestValue.Create("d"), E = TestValue.Create("E"), }; var graph = new[] { testValues.A, testValues.B, testValues.C, testValues.D, testValues.E, testValues.A2, }; var ignoreCase = DerivedComparer.Create(StringComparer.OrdinalIgnoreCase, (string t) => t.Trim()); var equalityComparer = TestValue.CreateComparer(ignoreCase); var vertexValueComparer = new VertexValueComparer <TestValue <string> >(equalityComparer); var byValue = graph.ToLookup(t => t.Value, ignoreCase); var vertices = VertexBuilder.BuildVertices(graph, s => s.DependsOn.SelectMany(d => byValue[d]), equalityComparer).ToList(); Assert.AreEqual(6, vertices.Count, "every source element should yield a vertex"); Assert.AreEqual(5, vertices.Distinct().Count(), "'A' and 'a ' should yield the exact same vertex"); var finder = new StronglyConnectedComponentFinder <TestValue <string> >(); var simpleComponents = finder.DetectCycle(vertices, vertexValueComparer); var components = simpleComponents.ExtractCycles(equalityComparer); Assert.AreEqual(4, components.Count, "It should be only 4 components"); Assert.AreEqual(3, components.IndependentComponents().Count(), "B, D & E are acylic"); Assert.AreEqual(1, components.Cycles().Count(), "Only 1 cycle should be found"); Assert.AreEqual (2, components.Take(2) .SelectMany(t => t) .Intersect(new[] { testValues.D, testValues.E }) .Count(), "D & E should be the first components."); var component3 = components.Skip(2).First(); var component4 = components.Skip(3).First(); Assert.AreEqual(component3.Count, 2, "3rd component should be a cycle of 2"); Assert.AreEqual (component3.Intersect(new[] { testValues.A, testValues.C }).Count(), 2, "3rd component should be a cycle of A & C"); Assert.AreEqual(testValues.B, component4.Single()); // 4th one has to be B (uses A) }