public void Test01b() { string[][] successors = new string[][] { /* 0 */ new string[] { }, // 0 has no successors /* 1 */ new string[] { }, /* 2 */ new string[] { "3" }, /* 3 */ new string[] { "1" }, /* 4 */ new string[] { "0", "1" }, /* 5 */ new string[] { "0", "2" }, }; Func <string, IEnumerable <string> > succF = x => successors[int.Parse(x)]; var sorted = TopologicalSort.IterativeSort <string>(new[] { "4", "5" }, i => succF(i).ToImmutableArray()); AssertTopologicallySorted(sorted, succF, "Test01"); Assert.Equal(6, sorted.Length); AssertEx.Equal(new[] { "4", "5", "2", "3", "1", "0" }, sorted); }
public void Test01() { int[][] successors = new int[][] { /* 0 */ new int[] { }, // 0 has no successors /* 1 */ new int[] { }, /* 2 */ new int[] { 3 }, /* 3 */ new int[] { 1 }, /* 4 */ new int[] { 0, 1, 0, 1 }, // tolerate duplicate edges /* 5 */ new int[] { 0, 2 }, }; Func <int, IEnumerable <int> > succF = x => successors[x]; var sorted = TopologicalSort.IterativeSort <int>(new[] { 4, 5 }, i => succF(i).ToImmutableArray()); AssertTopologicallySorted(sorted, succF, "Test01"); Assert.Equal(6, sorted.Length); AssertEx.Equal(new[] { 4, 5, 2, 3, 1, 0 }, sorted); }
public void TestCycle() { int[][] successors = new int[][] { /* 0 */ new int[] { }, /* 1 */ new int[] { 2, 4 }, /* 2 */ new int[] { }, /* 3 */ new int[] { 2, 5 }, /* 4 */ new int[] { 2, 3 }, /* 5 */ new int[] { 2, 1 }, /* 6 */ new int[] { 2, 7 }, /* 7 */ new int[] { } }; // 1 -> 4 -> 3 -> 5 -> 1 Assert.Throws <ArgumentException>(() => { var sorted = TopologicalSort.IterativeSort <int>(new[] { 1 }, x => successors[x].ToImmutableArray()); }); }
public void Test02() { int[][] successors = new int[][] { /* 0 */ new int[] { }, /* 1 */ new int[] { 2, 4 }, /* 2 */ new int[] { }, /* 3 */ new int[] { 2, 5 }, /* 4 */ new int[] { 2, 3 }, /* 5 */ new int[] { 2, }, /* 6 */ new int[] { 2, 7 }, /* 7 */ new int[] { } }; Func <int, IEnumerable <int> > succF = x => successors[x]; var sorted = TopologicalSort.IterativeSort <int>(new[] { 1, 6 }, i => succF(i).ToImmutableArray()); AssertTopologicallySorted(sorted, succF, "Test02"); Assert.Equal(7, sorted.Length); AssertEx.Equal(new[] { 1, 4, 3, 5, 6, 7, 2 }, sorted); }
/// <summary> /// Build the decision dag, giving an error if some cases are subsumed and a warning if the switch expression is not exhaustive. /// </summary> /// <param name="node"></param> /// <param name="boundInputExpression"></param> /// <param name="switchArms"></param> /// <param name="decisionDag"></param> /// <param name="diagnostics"></param> /// <returns>true if there was a non-exhaustive warning reported</returns> private bool CheckSwitchExpressionExhaustive( SwitchExpressionSyntax node, BoundExpression boundInputExpression, ImmutableArray <BoundSwitchExpressionArm> switchArms, out BoundDecisionDag decisionDag, out LabelSymbol defaultLabel, DiagnosticBag diagnostics) { defaultLabel = new GeneratedLabelSymbol("default"); decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchExpression(this.Compilation, node, boundInputExpression, switchArms, defaultLabel, diagnostics); var reachableLabels = decisionDag.ReachableLabels; foreach (BoundSwitchExpressionArm arm in switchArms) { if (!reachableLabels.Contains(arm.Label)) { diagnostics.Add(ErrorCode.ERR_SwitchArmSubsumed, arm.Pattern.Syntax.Location); } } if (!reachableLabels.Contains(defaultLabel)) { // switch expression is exhaustive; no default label needed. defaultLabel = null; return(false); } // We only report exhaustive warnings when the default label is reachable through some series of // tests that do not include a test in which the value is known to be null. Handling paths with // nulls is the job of the nullable walker. foreach (var n in TopologicalSort.IterativeSort <BoundDecisionDagNode>(new[] { decisionDag.RootNode }, nonNullSuccessors)) { if (n is BoundLeafDecisionDagNode leaf && leaf.Label == defaultLabel) { diagnostics.Add(ErrorCode.WRN_SwitchExpressionNotExhaustive, node.SwitchKeyword.GetLocation()); return(true); } } return(false); ImmutableArray <BoundDecisionDagNode> nonNullSuccessors(BoundDecisionDagNode n) { switch (n) { case BoundTestDecisionDagNode p: switch (p.Test) { case BoundDagNonNullTest t: // checks that the input is not null return(ImmutableArray.Create(p.WhenTrue)); case BoundDagExplicitNullTest t: // checks that the input is null return(ImmutableArray.Create(p.WhenFalse)); default: return(BoundDecisionDag.Successors(n)); } default: return(BoundDecisionDag.Successors(n)); } } }
public void TestRandom(int seed) { int numberOfNodes = 100; Random random = new Random(seed); // First, we produce a list of integers representing a possible (reversed) // topological sort of the graph we will construct var possibleSort = Enumerable.Range(0, numberOfNodes).ToArray(); shuffle(possibleSort); // Then we produce a set of edges that is consistent with that possible topological sort int[][] successors = new int[numberOfNodes][]; for (int i = numberOfNodes - 1; i >= 0; i--) { successors[possibleSort[i]] = randomSubset((int)Math.Sqrt(i), i); } // Perform a topological sort and check it. Func <int, IEnumerable <int> > succF = x => successors[x]; var sorted = TopologicalSort.IterativeSort <int>(Enumerable.Range(0, numberOfNodes).ToArray(), i => succF(i).ToImmutableArray()); Assert.Equal(numberOfNodes, sorted.Length); AssertTopologicallySorted(sorted, succF, $"TestRandom(seed: {seed})"); // Now we modify the graph to add an edge from the last node to the first node, which // probably induces a cycle. Since the graph is random, it is possible that this does // not induce a cycle. However, by the construction of the graph it is almost certain // that a cycle is induced. Nevertheless, to avoid flakiness in the tests, we do not // test with actual random graphs, but with graphs based on pseudo-random sequences using // random seeds hardcoded into the tests. That way we are testing on the same graphs each // time. successors[possibleSort[0]] = successors[possibleSort[0]].Concat(new int[] { possibleSort[numberOfNodes - 1] }).ToArray(); Assert.Throws <ArgumentException>(() => { TopologicalSort.IterativeSort <int>(Enumerable.Range(0, numberOfNodes).ToArray(), i => succF(i).ToImmutableArray()); }); // where void shuffle(int[] data) { int length = data.Length; for (int t = 0; t < length - 1; t++) { int r = random.Next(t, length); if (t != r) { var tmp = data[t]; data[t] = data[r]; data[r] = tmp; } } } int[] randomSubset(int count, int limit) { // We don't worry about duplicate values. That's all part of the test, // as the topological sort should tolerate duplicate edges. var result = new int[count]; for (int i = 0; i < count; i++) { result[i] = possibleSort[random.Next(0, limit)]; } return(result); } }