From 02cae250a6aa1c68c6b0b3a8eaa4a2834734a5f6 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Wed, 20 Dec 2017 20:28:58 -0500 Subject: [PATCH 1/9] Optimize multiroute --- ICD.Common.Utils/RecursionUtils.cs | 276 ++++++++++++++++++----------- 1 file changed, 176 insertions(+), 100 deletions(-) diff --git a/ICD.Common.Utils/RecursionUtils.cs b/ICD.Common.Utils/RecursionUtils.cs index fd491f4..df9919d 100644 --- a/ICD.Common.Utils/RecursionUtils.cs +++ b/ICD.Common.Utils/RecursionUtils.cs @@ -3,127 +3,203 @@ using System.Collections.Generic; using System.Linq; using ICD.Common.Properties; using ICD.Common.Utils.Collections; +using ICD.Common.Utils.Extensions; namespace ICD.Common.Utils { - public static class RecursionUtils - { - /// - /// Returns all of the nodes in the tree via breadth-first search. - /// - /// - /// - /// - /// - public static IEnumerable BreadthFirstSearch(T root, Func> getChildren) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + public static class RecursionUtils + { + /// + /// Returns all of the nodes in the tree via breadth-first search. + /// + /// + /// + /// + /// + public static IEnumerable BreadthFirstSearch(T root, Func> getChildren) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - Queue process = new Queue(); - process.Enqueue(root); + Queue process = new Queue(); + process.Enqueue(root); - while (process.Count > 0) - { - T current = process.Dequeue(); - yield return current; + while (process.Count > 0) + { + T current = process.Dequeue(); + yield return current; - foreach (T child in getChildren(current)) - process.Enqueue(child); - } - } + foreach (T child in getChildren(current)) + process.Enqueue(child); + } + } - /// - /// Returns the shortest path from root to destination via breadth-first search. - /// - /// - /// - /// - /// - /// - [CanBeNull] - public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + /// + /// Returns the shortest path from root to destination via breadth-first search. + /// + /// + /// + /// + /// + /// + [CanBeNull] + public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - return BreadthFirstSearchPath(root, destination, getChildren, EqualityComparer.Default); - } + return BreadthFirstSearchPath(root, destination, getChildren, EqualityComparer.Default); + } - /// - /// Returns the shortest path from root to destination via breadth-first search. - /// - /// - /// - /// - /// - /// - /// - [CanBeNull] - public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren, - IEqualityComparer comparer) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + [NotNull] + public static Dictionary> BreadthFirstSearchManyDestinations(T root, Dictionary destinations, Func> getChildren) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - if (comparer == null) - throw new ArgumentNullException("comparer"); + return BreadthFirstSearchPathManyDestinations(root, destinations, getChildren, EqualityComparer.Default); + } - // Edge case - root and destination are the same - if (comparer.Equals(root, destination)) - return new[] {root}; + /// + /// Returns the shortest path from root to destination via breadth-first search. + /// + /// + /// + /// + /// + /// + /// + [CanBeNull] + public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren, + IEqualityComparer comparer) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - Queue queue = new Queue(); - queue.Enqueue(root); + if (comparer == null) + throw new ArgumentNullException("comparer"); - Dictionary nodeParents = new Dictionary(); + // Edge case - root and destination are the same + if (comparer.Equals(root, destination)) + return new[] { root }; - while (queue.Count > 0) - { - T current = queue.Dequeue(); + Queue queue = new Queue(); + queue.Enqueue(root); - foreach (T node in getChildren(current)) - { - if (nodeParents.ContainsKey(node)) - continue; + Dictionary nodeParents = new Dictionary(); - queue.Enqueue(node); - nodeParents.Add(node, current); + while (queue.Count > 0) + { + T current = queue.Dequeue(); - // Found a path to the destination - if (comparer.Equals(node, destination)) - return GetPath(destination, nodeParents).Reverse(); - } - } + foreach (T node in getChildren(current)) + { + if (nodeParents.ContainsKey(node)) + continue; - return null; - } + queue.Enqueue(node); + nodeParents.Add(node, current); - /// - /// Walks through a map of nodes from the starting point. - /// - /// - /// - /// - /// - private static IEnumerable GetPath(T start, IDictionary nodeParents) - { - IcdHashSet visited = new IcdHashSet(); + // Found a path to the destination + if (comparer.Equals(node, destination)) + return GetPath(destination, nodeParents).Reverse(); + } + } - while (true) - { - yield return start; - visited.Add(start); + return null; + } - T next; - if (!nodeParents.TryGetValue(start, out next)) - break; + [NotNull] + public static Dictionary> BreadthFirstSearchPathManyDestinations(T root, Dictionary destinations, + Func> getChildren, + IEqualityComparer comparer) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - if (visited.Contains(next)) - break; + if (comparer == null) + throw new ArgumentNullException("comparer"); - start = next; - } - } - } + Dictionary destinationsToBeProcessed = new Dictionary(destinations); + List destinationsProcessed = new List(); + Dictionary> pathsToReturn = new Dictionary>(); + + //Edge case, root is the destination + foreach (var destination in destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination.Value))) + { + destinationsProcessed.Add(destination.Value); + pathsToReturn.Add(destination.Key, new[] { root }); + } + + foreach (var destination in destinationsProcessed) + { + destinationsToBeProcessed.RemoveValue(destination); + } + destinationsProcessed.Clear(); + if (destinationsToBeProcessed.Count == 0) + { + return pathsToReturn; + } + + Queue queue = new Queue(); + queue.Enqueue(root); + + Dictionary nodeParents = new Dictionary(); + + while (queue.Count > 0) + { + T current = queue.Dequeue(); + + foreach (T node in getChildren(current).Where(node => !nodeParents.ContainsKey(node))) + { + queue.Enqueue(node); + nodeParents.Add(node, current); + + foreach (var destination in destinationsToBeProcessed.Where(destination => comparer.Equals(node, destination.Value))) + { + destinationsProcessed.Add(destination.Value); + pathsToReturn.Add(destination.Key, GetPath(destination.Value, nodeParents).Reverse()); + } + foreach (var destination in destinationsProcessed) + { + destinationsToBeProcessed.RemoveValue(destination); + } + destinationsProcessed.Clear(); + if (destinationsToBeProcessed.Count == 0) + { + return pathsToReturn; + } + } + } + + return pathsToReturn; + } + + /// + /// Walks through a map of nodes from the starting point. + /// + /// + /// + /// + /// + private static IEnumerable GetPath(T start, IDictionary nodeParents) + { + IcdHashSet visited = new IcdHashSet(); + + while (true) + { + yield return start; + visited.Add(start); + + T next; + if (!nodeParents.TryGetValue(start, out next)) + break; + + if (visited.Contains(next)) + break; + + start = next; + } + } + } } From d1b87cc3295828648ca82c1a2b8fc999c259505f Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 2 Jan 2018 14:20:52 -0500 Subject: [PATCH 2/9] Whitespace, tidying --- ICD.Common.Utils/RecursionUtils.cs | 337 +++++++++++++++-------------- 1 file changed, 175 insertions(+), 162 deletions(-) diff --git a/ICD.Common.Utils/RecursionUtils.cs b/ICD.Common.Utils/RecursionUtils.cs index df9919d..1e0f942 100644 --- a/ICD.Common.Utils/RecursionUtils.cs +++ b/ICD.Common.Utils/RecursionUtils.cs @@ -7,199 +7,212 @@ using ICD.Common.Utils.Extensions; namespace ICD.Common.Utils { - public static class RecursionUtils - { - /// - /// Returns all of the nodes in the tree via breadth-first search. - /// - /// - /// - /// - /// - public static IEnumerable BreadthFirstSearch(T root, Func> getChildren) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + public static class RecursionUtils + { + /// + /// Returns all of the nodes in the tree via breadth-first search. + /// + /// + /// + /// + /// + public static IEnumerable BreadthFirstSearch(T root, Func> getChildren) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - Queue process = new Queue(); - process.Enqueue(root); + Queue process = new Queue(); + process.Enqueue(root); - while (process.Count > 0) - { - T current = process.Dequeue(); - yield return current; + while (process.Count > 0) + { + T current = process.Dequeue(); + yield return current; - foreach (T child in getChildren(current)) - process.Enqueue(child); - } - } + foreach (T child in getChildren(current)) + process.Enqueue(child); + } + } - /// - /// Returns the shortest path from root to destination via breadth-first search. - /// - /// - /// - /// - /// - /// - [CanBeNull] - public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + /// + /// Returns the shortest path from root to destination via breadth-first search. + /// + /// + /// + /// + /// + /// + [CanBeNull] + public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - return BreadthFirstSearchPath(root, destination, getChildren, EqualityComparer.Default); - } + return BreadthFirstSearchPath(root, destination, getChildren, EqualityComparer.Default); + } - [NotNull] - public static Dictionary> BreadthFirstSearchManyDestinations(T root, Dictionary destinations, Func> getChildren) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + /// + /// Returns the shortest path from root to destination via breadth-first search. + /// + /// + /// + /// + /// + /// + /// + [CanBeNull] + public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren, + IEqualityComparer comparer) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - return BreadthFirstSearchPathManyDestinations(root, destinations, getChildren, EqualityComparer.Default); - } + if (comparer == null) + throw new ArgumentNullException("comparer"); - /// - /// Returns the shortest path from root to destination via breadth-first search. - /// - /// - /// - /// - /// - /// - /// - [CanBeNull] - public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren, - IEqualityComparer comparer) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + // Edge case - root and destination are the same + if (comparer.Equals(root, destination)) + return new[] {root}; - if (comparer == null) - throw new ArgumentNullException("comparer"); + Queue queue = new Queue(); + queue.Enqueue(root); - // Edge case - root and destination are the same - if (comparer.Equals(root, destination)) - return new[] { root }; + Dictionary nodeParents = new Dictionary(); - Queue queue = new Queue(); - queue.Enqueue(root); + while (queue.Count > 0) + { + T current = queue.Dequeue(); - Dictionary nodeParents = new Dictionary(); + foreach (T node in getChildren(current)) + { + if (nodeParents.ContainsKey(node)) + continue; - while (queue.Count > 0) - { - T current = queue.Dequeue(); + queue.Enqueue(node); + nodeParents.Add(node, current); - foreach (T node in getChildren(current)) - { - if (nodeParents.ContainsKey(node)) - continue; + // Found a path to the destination + if (comparer.Equals(node, destination)) + return GetPath(destination, nodeParents).Reverse(); + } + } - queue.Enqueue(node); - nodeParents.Add(node, current); + return null; + } - // Found a path to the destination - if (comparer.Equals(node, destination)) - return GetPath(destination, nodeParents).Reverse(); - } - } + [NotNull] + public static Dictionary> BreadthFirstSearchManyDestinations(T root, + Dictionary destinations, + Func> + getChildren) + { + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - return null; - } + return BreadthFirstSearchPathManyDestinations(root, destinations, getChildren, EqualityComparer.Default); + } - [NotNull] - public static Dictionary> BreadthFirstSearchPathManyDestinations(T root, Dictionary destinations, - Func> getChildren, - IEqualityComparer comparer) - { - if (getChildren == null) - throw new ArgumentNullException("getChildren"); + [NotNull] + public static Dictionary> BreadthFirstSearchPathManyDestinations(T root, + Dictionary + destinations, + Func> + getChildren, + IEqualityComparer + comparer) + { + if (destinations == null) + throw new ArgumentNullException("destinations"); - if (comparer == null) - throw new ArgumentNullException("comparer"); + if (getChildren == null) + throw new ArgumentNullException("getChildren"); - Dictionary destinationsToBeProcessed = new Dictionary(destinations); - List destinationsProcessed = new List(); - Dictionary> pathsToReturn = new Dictionary>(); + if (comparer == null) + throw new ArgumentNullException("comparer"); - //Edge case, root is the destination - foreach (var destination in destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination.Value))) - { - destinationsProcessed.Add(destination.Value); - pathsToReturn.Add(destination.Key, new[] { root }); - } + Dictionary destinationsToBeProcessed = new Dictionary(destinations); + List destinationsProcessed = new List(); + Dictionary> pathsToReturn = new Dictionary>(); - foreach (var destination in destinationsProcessed) - { - destinationsToBeProcessed.RemoveValue(destination); - } - destinationsProcessed.Clear(); - if (destinationsToBeProcessed.Count == 0) - { - return pathsToReturn; - } + // Edge case, root is the destination + foreach ( + KeyValuePair destination in + destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination.Value))) + { + destinationsProcessed.Add(destination.Value); + pathsToReturn.Add(destination.Key, new[] {root}); + } - Queue queue = new Queue(); - queue.Enqueue(root); + foreach (T destination in destinationsProcessed) + { + destinationsToBeProcessed.RemoveValue(destination); + } + destinationsProcessed.Clear(); + if (destinationsToBeProcessed.Count == 0) + { + return pathsToReturn; + } - Dictionary nodeParents = new Dictionary(); + Queue queue = new Queue(); + queue.Enqueue(root); - while (queue.Count > 0) - { - T current = queue.Dequeue(); + Dictionary nodeParents = new Dictionary(); - foreach (T node in getChildren(current).Where(node => !nodeParents.ContainsKey(node))) - { - queue.Enqueue(node); - nodeParents.Add(node, current); + while (queue.Count > 0) + { + T current = queue.Dequeue(); - foreach (var destination in destinationsToBeProcessed.Where(destination => comparer.Equals(node, destination.Value))) - { - destinationsProcessed.Add(destination.Value); - pathsToReturn.Add(destination.Key, GetPath(destination.Value, nodeParents).Reverse()); - } - foreach (var destination in destinationsProcessed) - { - destinationsToBeProcessed.RemoveValue(destination); - } - destinationsProcessed.Clear(); - if (destinationsToBeProcessed.Count == 0) - { - return pathsToReturn; - } - } - } + foreach (T node in getChildren(current).Where(node => !nodeParents.ContainsKey(node))) + { + queue.Enqueue(node); + nodeParents.Add(node, current); - return pathsToReturn; - } + foreach ( + KeyValuePair destination in + destinationsToBeProcessed.Where(destination => comparer.Equals(node, destination.Value))) + { + destinationsProcessed.Add(destination.Value); + pathsToReturn.Add(destination.Key, GetPath(destination.Value, nodeParents).Reverse()); + } - /// - /// Walks through a map of nodes from the starting point. - /// - /// - /// - /// - /// - private static IEnumerable GetPath(T start, IDictionary nodeParents) - { - IcdHashSet visited = new IcdHashSet(); + foreach (T destination in destinationsProcessed) + destinationsToBeProcessed.RemoveValue(destination); - while (true) - { - yield return start; - visited.Add(start); + destinationsProcessed.Clear(); - T next; - if (!nodeParents.TryGetValue(start, out next)) - break; + if (destinationsToBeProcessed.Count == 0) + return pathsToReturn; + } + } - if (visited.Contains(next)) - break; + return pathsToReturn; + } - start = next; - } - } - } + /// + /// Walks through a map of nodes from the starting point. + /// + /// + /// + /// + /// + private static IEnumerable GetPath(T start, IDictionary nodeParents) + { + IcdHashSet visited = new IcdHashSet(); + + while (true) + { + yield return start; + visited.Add(start); + + T next; + if (!nodeParents.TryGetValue(start, out next)) + break; + + if (visited.Contains(next)) + break; + + start = next; + } + } + } } From e7afd8cceae6c9515aede4ff4caaa2831b666c24 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 2 Jan 2018 14:21:51 -0500 Subject: [PATCH 3/9] Resolving closure warning --- ICD.Common.Utils/RecursionUtils.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ICD.Common.Utils/RecursionUtils.cs b/ICD.Common.Utils/RecursionUtils.cs index 1e0f942..9dcadca 100644 --- a/ICD.Common.Utils/RecursionUtils.cs +++ b/ICD.Common.Utils/RecursionUtils.cs @@ -144,14 +144,10 @@ namespace ICD.Common.Utils } foreach (T destination in destinationsProcessed) - { destinationsToBeProcessed.RemoveValue(destination); - } destinationsProcessed.Clear(); if (destinationsToBeProcessed.Count == 0) - { return pathsToReturn; - } Queue queue = new Queue(); queue.Enqueue(root); @@ -167,9 +163,10 @@ namespace ICD.Common.Utils queue.Enqueue(node); nodeParents.Add(node, current); + T closureNode = node; foreach ( KeyValuePair destination in - destinationsToBeProcessed.Where(destination => comparer.Equals(node, destination.Value))) + destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination.Value))) { destinationsProcessed.Add(destination.Value); pathsToReturn.Add(destination.Key, GetPath(destination.Value, nodeParents).Reverse()); From 1e2a86374434bfb2a0de1368a1469ce159a0bdbf Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 2 Jan 2018 14:59:25 -0500 Subject: [PATCH 4/9] Simplifying BreadthFirstSearchPathManyDestinations --- ICD.Common.Utils/RecursionUtils.cs | 51 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/ICD.Common.Utils/RecursionUtils.cs b/ICD.Common.Utils/RecursionUtils.cs index 9dcadca..1cbc435 100644 --- a/ICD.Common.Utils/RecursionUtils.cs +++ b/ICD.Common.Utils/RecursionUtils.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using ICD.Common.Properties; using ICD.Common.Utils.Collections; -using ICD.Common.Utils.Extensions; namespace ICD.Common.Utils { @@ -101,10 +100,10 @@ namespace ICD.Common.Utils } [NotNull] - public static Dictionary> BreadthFirstSearchManyDestinations(T root, - Dictionary destinations, - Func> - getChildren) + public static Dictionary> BreadthFirstSearchManyDestinations(T root, + IEnumerable destinations, + Func> + getChildren) { if (getChildren == null) throw new ArgumentNullException("getChildren"); @@ -113,13 +112,13 @@ namespace ICD.Common.Utils } [NotNull] - public static Dictionary> BreadthFirstSearchPathManyDestinations(T root, - Dictionary - destinations, - Func> - getChildren, - IEqualityComparer - comparer) + public static Dictionary> BreadthFirstSearchPathManyDestinations(T root, + IEnumerable + destinations, + Func> + getChildren, + IEqualityComparer + comparer) { if (destinations == null) throw new ArgumentNullException("destinations"); @@ -130,21 +129,20 @@ namespace ICD.Common.Utils if (comparer == null) throw new ArgumentNullException("comparer"); - Dictionary destinationsToBeProcessed = new Dictionary(destinations); - List destinationsProcessed = new List(); - Dictionary> pathsToReturn = new Dictionary>(); + IcdHashSet destinationsToBeProcessed = new IcdHashSet(destinations); + IcdHashSet destinationsProcessed = new IcdHashSet(); + Dictionary> pathsToReturn = new Dictionary>(); // Edge case, root is the destination - foreach ( - KeyValuePair destination in - destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination.Value))) + foreach (T destination in + destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination))) { - destinationsProcessed.Add(destination.Value); - pathsToReturn.Add(destination.Key, new[] {root}); + destinationsProcessed.Add(destination); + pathsToReturn.Add(destination, new[] {root}); } foreach (T destination in destinationsProcessed) - destinationsToBeProcessed.RemoveValue(destination); + destinationsToBeProcessed.Remove(destination); destinationsProcessed.Clear(); if (destinationsToBeProcessed.Count == 0) return pathsToReturn; @@ -164,16 +162,15 @@ namespace ICD.Common.Utils nodeParents.Add(node, current); T closureNode = node; - foreach ( - KeyValuePair destination in - destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination.Value))) + foreach (T destination in + destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination))) { - destinationsProcessed.Add(destination.Value); - pathsToReturn.Add(destination.Key, GetPath(destination.Value, nodeParents).Reverse()); + destinationsProcessed.Add(destination); + pathsToReturn.Add(destination, GetPath(destination, nodeParents).Reverse()); } foreach (T destination in destinationsProcessed) - destinationsToBeProcessed.RemoveValue(destination); + destinationsToBeProcessed.Remove(destination); destinationsProcessed.Clear(); From 3a2f3b7526e2c4a12fde6999d8d64b162fb4add4 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 2 Jan 2018 16:49:23 -0500 Subject: [PATCH 5/9] Resolving warnings --- ICD.Common.Utils.Tests/RecursionUtilsTest.cs | 9 +++++++-- ICD.Common.Utils/IcdConsole.cs | 1 - ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ICD.Common.Utils.Tests/RecursionUtilsTest.cs b/ICD.Common.Utils.Tests/RecursionUtilsTest.cs index e87e74c..95bbed2 100644 --- a/ICD.Common.Utils.Tests/RecursionUtilsTest.cs +++ b/ICD.Common.Utils.Tests/RecursionUtilsTest.cs @@ -8,7 +8,7 @@ namespace ICD.Common.Utils.Tests [TestFixture] public sealed class RecursionUtilsTest { - private IEnumerable Graph(int node) + private static IEnumerable Graph(int node) { switch (node) { @@ -29,6 +29,7 @@ namespace ICD.Common.Utils.Tests [Test] public void BreadthFirstSearchTest() { + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed Assert.Throws(() => RecursionUtils.BreadthFirstSearch(1, null).ToArray()); int[] nodes = RecursionUtils.BreadthFirstSearch(1, Graph).ToArray(); @@ -43,8 +44,11 @@ namespace ICD.Common.Utils.Tests [Test] public void BreadthFirstSearchPathTest() { - Assert.Throws(() => RecursionUtils.BreadthFirstSearchPath(1, 4, null).ToArray()); + Assert.Throws(() => RecursionUtils.BreadthFirstSearchPath(1, 4, null)); + Assert.IsNull(RecursionUtils.BreadthFirstSearchPath(1, 5, Graph)); + + // ReSharper disable once AssignNullToNotNullAttribute int[] path = RecursionUtils.BreadthFirstSearchPath(1, 4, Graph).ToArray(); Assert.AreEqual(3, path.Length); @@ -63,6 +67,7 @@ namespace ICD.Common.Utils.Tests [Test] public void BreadthFirstSearchPathSingleNodeTest() { + // ReSharper disable once AssignNullToNotNullAttribute int[] path = RecursionUtils.BreadthFirstSearchPath(1, 1, Graph).ToArray(); Assert.AreEqual(1, path.Length); diff --git a/ICD.Common.Utils/IcdConsole.cs b/ICD.Common.Utils/IcdConsole.cs index bcbf993..2745f32 100644 --- a/ICD.Common.Utils/IcdConsole.cs +++ b/ICD.Common.Utils/IcdConsole.cs @@ -87,7 +87,6 @@ namespace ICD.Common.Utils #if SIMPLSHARP return CrestronConsole.SendControlSystemCommand(command, ref result); #else - result = string.Empty; return false; #endif } diff --git a/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs b/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs index 9f3e44a..900b1ce 100644 --- a/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs +++ b/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs @@ -12,7 +12,7 @@ namespace ICD.Common.Utils.Json /// The to write to. /// The value. /// The calling serializer. - public override sealed void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public sealed override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) { @@ -42,7 +42,7 @@ namespace ICD.Common.Utils.Json /// /// The object value. /// - public override sealed object ReadJson(JsonReader reader, Type objectType, object existingValue, + public sealed override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) From 0a0cf3f72065abe6d14a8bd8903d426615239cd6 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Thu, 4 Jan 2018 16:06:54 -0500 Subject: [PATCH 6/9] add unit test for multiroute. fix long standing bugs with routing uncovered by said unit test. --- ICD.Common.Utils.Tests/RecursionUtilsTest.cs | 102 +++++++++++++++++++ ICD.Common.Utils/RecursionUtils.cs | 11 +- 2 files changed, 110 insertions(+), 3 deletions(-) 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; From 2f1f19c3ff4d95aca847cec865761ca80dce6c31 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Fri, 5 Jan 2018 09:56:21 -0500 Subject: [PATCH 7/9] remove redundant toarray --- ICD.Common.Utils/RecursionUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICD.Common.Utils/RecursionUtils.cs b/ICD.Common.Utils/RecursionUtils.cs index 957ef70..f7abb4e 100644 --- a/ICD.Common.Utils/RecursionUtils.cs +++ b/ICD.Common.Utils/RecursionUtils.cs @@ -166,7 +166,7 @@ namespace ICD.Common.Utils destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination))) { destinationsProcessed.Add(destination); - pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse().ToArray()); + pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse()); } foreach (T destination in destinationsProcessed) From 0b05a87653251621d2b728f579162950cac98637 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Fri, 5 Jan 2018 11:06:00 -0500 Subject: [PATCH 8/9] add tryparse for short, ushort, long, and ulong --- ICD.Common.Utils/StringUtils.cs | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/ICD.Common.Utils/StringUtils.cs b/ICD.Common.Utils/StringUtils.cs index 8d8c0c8..df05254 100644 --- a/ICD.Common.Utils/StringUtils.cs +++ b/ICD.Common.Utils/StringUtils.cs @@ -191,6 +191,54 @@ namespace ICD.Common.Utils return TryConvert(Convert.ToUInt32, value, out result); } + /// + /// Attempts to parse the string as a short integer. + /// + /// + /// + /// + [PublicAPI] + public static bool TryParse(string value, out short result) + { + return TryConvert(Convert.ToInt16, value, out result); + } + + /// + /// Attempts to parse the string as an unsigned short integer. + /// + /// + /// + /// + [PublicAPI] + public static bool TryParse(string value, out ushort result) + { + return TryConvert(Convert.ToUInt16, value, out result); + } + + /// + /// Attempts to parse the string as a long integer. + /// + /// + /// + /// + [PublicAPI] + public static bool TryParse(string value, out long result) + { + return TryConvert(Convert.ToInt64, value, out result); + } + + /// + /// Attempts to parse the string as an unsigned long integer. + /// + /// + /// + /// + [PublicAPI] + public static bool TryParse(string value, out ulong result) + { + return TryConvert(Convert.ToUInt64, value, out result); + } + /// /// Attempts to parse the string as a float. /// From 1766b80ae8bcc124dfc472c8575d57b47364fa5c Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Fri, 5 Jan 2018 11:22:56 -0500 Subject: [PATCH 9/9] add tests for tryparse methods --- ICD.Common.Utils.Tests/StringUtilsTest.cs | 72 +++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/ICD.Common.Utils.Tests/StringUtilsTest.cs b/ICD.Common.Utils.Tests/StringUtilsTest.cs index 0a13c41..c819051 100644 --- a/ICD.Common.Utils.Tests/StringUtilsTest.cs +++ b/ICD.Common.Utils.Tests/StringUtilsTest.cs @@ -91,5 +91,77 @@ namespace ICD.Common.Utils.Tests { Assert.AreEqual(StringUtils.IsNullOrWhitespace(value), expectedResult); } + + [Test, UsedImplicitly] + public void TryParseIntTest() + { + int testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("1", out testVal)); + Assert.AreEqual(1, testVal); + } + + [Test, UsedImplicitly] + public void TryParseUintTest() + { + uint testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("1", out testVal)); + Assert.AreEqual((uint)1, testVal); + } + + [Test, UsedImplicitly] + public void TryParseShortTest() + { + short testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("1", out testVal)); + Assert.AreEqual((short)1, testVal); + } + + [Test, UsedImplicitly] + public void TryParseUshortTest() + { + ushort testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("1", out testVal)); + Assert.AreEqual((ushort)1, testVal); + } + + [Test, UsedImplicitly] + public void TryParseLongTest() + { + long testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("1", out testVal)); + Assert.AreEqual((long)1, testVal); + } + + [Test, UsedImplicitly] + public void TryParseUlongTest() + { + ulong testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("1", out testVal)); + Assert.AreEqual((ulong)1, testVal); + } + + [Test, UsedImplicitly] + public void TryParseFloatTest() + { + float testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("1.1", out testVal)); + Assert.AreEqual((float)1.1, testVal); + } + + [Test, UsedImplicitly] + public void TryParseBoolTest() + { + bool testVal; + Assert.IsFalse(StringUtils.TryParse("fish", out testVal)); + Assert.IsTrue(StringUtils.TryParse("true", out testVal)); + Assert.AreEqual(true, testVal); + } } }