Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
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));
        }
        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)
        }
Ejemplo n.º 4
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)
        }