예제 #1
0
        /// <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));
        }
예제 #2
0
        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)
        }