private static Func <StronglyConnectedComponent <T>, DependencyCycle <T> > BuildGetDependencyCycleFromStronglyConnectedComponent <T>(IEqualityComparer <T> valueComparer, IEnumerable <StronglyConnectedComponent <T> > asList) { var vertexComparer = new VertexValueComparer <T>(valueComparer); var byVertex = (from c in asList from v in c select new { c, v }) .GroupBy(k => k.v, v => v.c, vertexComparer) .ToDictionary(k => k.Key, v => v.ToHashSet()); var cycleByComponent = new Dictionary <StronglyConnectedComponent <T>, DependencyCycle <T> >(); DependencyCycle <T> GetCycle(StronglyConnectedComponent <T> sc) { if (cycleByComponent.TryGetValue(sc, out var result)) { return(result); } var dependencies = sc.SelectMany(t => t.Dependencies).SelectMany(d => byVertex[d]); var sets = new HashSet <DependencyCycle <T> >(); DependencyCycle <T> cycle; cycleByComponent.Add(sc, cycle = new DependencyCycle <T>(sc.Select(t => t.Value).ToHashSet(valueComparer), sets)); sets.UnionWith(dependencies.Select(GetCycle).Where(t => t != cycle)); return(cycle); } return(GetCycle); }
/// <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) }
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) }