diff --git a/ICD.Common.Utils.Tests/RecursionUtilsTest.cs b/ICD.Common.Utils.Tests/RecursionUtilsTest.cs index 95bbed2..f64c5f2 100644 --- a/ICD.Common.Utils.Tests/RecursionUtilsTest.cs +++ b/ICD.Common.Utils.Tests/RecursionUtilsTest.cs @@ -26,6 +26,52 @@ namespace ICD.Common.Utils.Tests } } + private static IEnumerable WideGraph(int node) + { + switch (node) + { + case 1: + yield return 2; + yield return 3; + yield return 4; + yield return 5; + yield return 6; + break; + case 2: + yield return 21; + yield return 22; + break; + case 3: + yield return 1; + yield return 31; + yield return 32; + break; + case 4: + yield return 41; + yield return 42; + break; + case 5: + yield return 51; + yield return 52; + break; + case 6: + yield return 61; + yield return 62; + break; + case 21: + yield return 62; + break; + case 41: + yield return 43; + break; + case 42: + yield return 43; + break; + default: + yield break; + } + } + [Test] public void BreadthFirstSearchTest() { @@ -73,5 +119,61 @@ namespace ICD.Common.Utils.Tests Assert.AreEqual(1, path.Length); Assert.AreEqual(1, path[0]); } + + [Test] + public void BreadthFirstSearchManyDestinationsTest() + { + Assert.Throws(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 1 }, null)); + Assert.Throws(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, null, Graph)); + Assert.IsEmpty(RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 5 }, Graph)); + + Dictionary> paths = RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 21, 22, 31, 43, 62 }, WideGraph); + + //Make sure all paths were found + Assert.IsTrue(paths.Keys.Contains(21)); + Assert.IsTrue(paths.Keys.Contains(22)); + Assert.IsTrue(paths.Keys.Contains(31)); + Assert.IsTrue(paths.Keys.Contains(43)); + Assert.IsTrue(paths.Keys.Contains(62)); + + //Make sure the shortest paths are taken + Assert.AreEqual(3, paths[21].Count()); + Assert.AreEqual(3, paths[22].Count()); + Assert.AreEqual(3, paths[31].Count()); // infinite loop exists between 1 and 3 + Assert.AreEqual(4, paths[43].Count()); // 43 has two parents of equal distance, 41 and 42 + Assert.AreEqual(3, paths[62].Count()); // two paths exist, one is 3, one is 4 + + // make sure that destinations which were not asked for were not returned + Assert.IsFalse(paths.Keys.Contains(1)); + Assert.IsFalse(paths.Keys.Contains(2)); + Assert.IsFalse(paths.Keys.Contains(3)); + Assert.IsFalse(paths.Keys.Contains(32)); + + //Verify path for destination 21 + Assert.AreEqual(1, paths[21].ToArray()[0]); + Assert.AreEqual(2, paths[21].ToArray()[1]); + Assert.AreEqual(21, paths[21].ToArray()[2]); + + //Verify path for destination 22 + Assert.AreEqual(1, paths[22].ToArray()[0]); + Assert.AreEqual(2, paths[22].ToArray()[1]); + Assert.AreEqual(22, paths[22].ToArray()[2]); + + //Verify path for destination 31 + Assert.AreEqual(1, paths[31].ToArray()[0]); + Assert.AreEqual(3, paths[31].ToArray()[1]); + Assert.AreEqual(31, paths[31].ToArray()[2]); + + //Verify path for destination 43 + Assert.AreEqual(1, paths[43].ToArray()[0]); + Assert.AreEqual(4, paths[43].ToArray()[1]); + Assert.AreEqual(41, paths[43].ToArray()[2]); // when multiple parents exist with valid paths back, the first one should be consistently selected + Assert.AreEqual(43, paths[43].ToArray()[3]); + + //Verify path for destination 62 + Assert.AreEqual(1, paths[62].ToArray()[0]); + Assert.AreEqual(6, paths[62].ToArray()[1]); + Assert.AreEqual(62, paths[62].ToArray()[2]); + } } } diff --git a/ICD.Common.Utils/RecursionUtils.cs b/ICD.Common.Utils/RecursionUtils.cs index 1cbc435..957ef70 100644 --- a/ICD.Common.Utils/RecursionUtils.cs +++ b/ICD.Common.Utils/RecursionUtils.cs @@ -92,7 +92,7 @@ namespace ICD.Common.Utils // Found a path to the destination if (comparer.Equals(node, destination)) - return GetPath(destination, nodeParents).Reverse(); + return GetPath(destination, root, nodeParents, comparer).Reverse(); } } @@ -166,7 +166,7 @@ namespace ICD.Common.Utils destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination))) { destinationsProcessed.Add(destination); - pathsToReturn.Add(destination, GetPath(destination, nodeParents).Reverse()); + pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse().ToArray()); } foreach (T destination in destinationsProcessed) @@ -187,15 +187,20 @@ namespace ICD.Common.Utils /// /// /// + /// /// /// /// - private static IEnumerable GetPath(T start, IDictionary nodeParents) + private static IEnumerable GetPath(T start, T end, IDictionary nodeParents, IEqualityComparer comparer) { IcdHashSet visited = new IcdHashSet(); while (true) { yield return start; + + if (comparer.Equals(start, end)) + break; + visited.Add(start); T next;