mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-02-15 20:54:58 +00:00
Merge remote-tracking branch 'origin/RoutingOp' into dev
This commit is contained in:
@@ -8,7 +8,7 @@ namespace ICD.Common.Utils.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public sealed class RecursionUtilsTest
|
public sealed class RecursionUtilsTest
|
||||||
{
|
{
|
||||||
private IEnumerable<int> Graph(int node)
|
private static IEnumerable<int> Graph(int node)
|
||||||
{
|
{
|
||||||
switch (node)
|
switch (node)
|
||||||
{
|
{
|
||||||
@@ -26,9 +26,56 @@ namespace ICD.Common.Utils.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<int> 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]
|
[Test]
|
||||||
public void BreadthFirstSearchTest()
|
public void BreadthFirstSearchTest()
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once ReturnValueOfPureMethodIsNotUsed
|
||||||
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearch(1, null).ToArray());
|
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearch(1, null).ToArray());
|
||||||
|
|
||||||
int[] nodes = RecursionUtils.BreadthFirstSearch(1, Graph).ToArray();
|
int[] nodes = RecursionUtils.BreadthFirstSearch(1, Graph).ToArray();
|
||||||
@@ -43,8 +90,11 @@ namespace ICD.Common.Utils.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void BreadthFirstSearchPathTest()
|
public void BreadthFirstSearchPathTest()
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchPath(1, 4, null).ToArray());
|
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchPath(1, 4, null));
|
||||||
|
|
||||||
|
Assert.IsNull(RecursionUtils.BreadthFirstSearchPath(1, 5, Graph));
|
||||||
|
|
||||||
|
// ReSharper disable once AssignNullToNotNullAttribute
|
||||||
int[] path = RecursionUtils.BreadthFirstSearchPath(1, 4, Graph).ToArray();
|
int[] path = RecursionUtils.BreadthFirstSearchPath(1, 4, Graph).ToArray();
|
||||||
|
|
||||||
Assert.AreEqual(3, path.Length);
|
Assert.AreEqual(3, path.Length);
|
||||||
@@ -63,10 +113,67 @@ namespace ICD.Common.Utils.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void BreadthFirstSearchPathSingleNodeTest()
|
public void BreadthFirstSearchPathSingleNodeTest()
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once AssignNullToNotNullAttribute
|
||||||
int[] path = RecursionUtils.BreadthFirstSearchPath(1, 1, Graph).ToArray();
|
int[] path = RecursionUtils.BreadthFirstSearchPath(1, 1, Graph).ToArray();
|
||||||
|
|
||||||
Assert.AreEqual(1, path.Length);
|
Assert.AreEqual(1, path.Length);
|
||||||
Assert.AreEqual(1, path[0]);
|
Assert.AreEqual(1, path[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BreadthFirstSearchManyDestinationsTest()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 1 }, null));
|
||||||
|
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, null, Graph));
|
||||||
|
Assert.IsEmpty(RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 5 }, Graph));
|
||||||
|
|
||||||
|
Dictionary<int, IEnumerable<int>> 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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,5 +91,77 @@ namespace ICD.Common.Utils.Tests
|
|||||||
{
|
{
|
||||||
Assert.AreEqual(StringUtils.IsNullOrWhitespace(value), expectedResult);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ namespace ICD.Common.Utils
|
|||||||
#if SIMPLSHARP
|
#if SIMPLSHARP
|
||||||
return CrestronConsole.SendControlSystemCommand(command, ref result);
|
return CrestronConsole.SendControlSystemCommand(command, ref result);
|
||||||
#else
|
#else
|
||||||
result = string.Empty;
|
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace ICD.Common.Utils.Json
|
|||||||
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
|
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
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)
|
if (value == null)
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ namespace ICD.Common.Utils.Json
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The object value.
|
/// The object value.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public override sealed object ReadJson(JsonReader reader, Type objectType, object existingValue,
|
public sealed override object ReadJson(JsonReader reader, Type objectType, object existingValue,
|
||||||
JsonSerializer serializer)
|
JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.TokenType == JsonToken.Null)
|
if (reader.TokenType == JsonToken.Null)
|
||||||
|
|||||||
@@ -92,27 +92,115 @@ namespace ICD.Common.Utils
|
|||||||
|
|
||||||
// Found a path to the destination
|
// Found a path to the destination
|
||||||
if (comparer.Equals(node, destination))
|
if (comparer.Equals(node, destination))
|
||||||
return GetPath(destination, nodeParents).Reverse();
|
return GetPath(destination, root, nodeParents, comparer).Reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NotNull]
|
||||||
|
public static Dictionary<T, IEnumerable<T>> BreadthFirstSearchManyDestinations<T>(T root,
|
||||||
|
IEnumerable<T> destinations,
|
||||||
|
Func<T, IEnumerable<T>>
|
||||||
|
getChildren)
|
||||||
|
{
|
||||||
|
if (getChildren == null)
|
||||||
|
throw new ArgumentNullException("getChildren");
|
||||||
|
|
||||||
|
return BreadthFirstSearchPathManyDestinations(root, destinations, getChildren, EqualityComparer<T>.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NotNull]
|
||||||
|
public static Dictionary<T, IEnumerable<T>> BreadthFirstSearchPathManyDestinations<T>(T root,
|
||||||
|
IEnumerable<T>
|
||||||
|
destinations,
|
||||||
|
Func<T, IEnumerable<T>>
|
||||||
|
getChildren,
|
||||||
|
IEqualityComparer<T>
|
||||||
|
comparer)
|
||||||
|
{
|
||||||
|
if (destinations == null)
|
||||||
|
throw new ArgumentNullException("destinations");
|
||||||
|
|
||||||
|
if (getChildren == null)
|
||||||
|
throw new ArgumentNullException("getChildren");
|
||||||
|
|
||||||
|
if (comparer == null)
|
||||||
|
throw new ArgumentNullException("comparer");
|
||||||
|
|
||||||
|
IcdHashSet<T> destinationsToBeProcessed = new IcdHashSet<T>(destinations);
|
||||||
|
IcdHashSet<T> destinationsProcessed = new IcdHashSet<T>();
|
||||||
|
Dictionary<T, IEnumerable<T>> pathsToReturn = new Dictionary<T, IEnumerable<T>>();
|
||||||
|
|
||||||
|
// Edge case, root is the destination
|
||||||
|
foreach (T destination in
|
||||||
|
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)))
|
||||||
|
{
|
||||||
|
destinationsProcessed.Add(destination);
|
||||||
|
pathsToReturn.Add(destination, new[] {root});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (T destination in destinationsProcessed)
|
||||||
|
destinationsToBeProcessed.Remove(destination);
|
||||||
|
destinationsProcessed.Clear();
|
||||||
|
if (destinationsToBeProcessed.Count == 0)
|
||||||
|
return pathsToReturn;
|
||||||
|
|
||||||
|
Queue<T> queue = new Queue<T>();
|
||||||
|
queue.Enqueue(root);
|
||||||
|
|
||||||
|
Dictionary<T, T> nodeParents = new Dictionary<T, T>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
T closureNode = node;
|
||||||
|
foreach (T destination in
|
||||||
|
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)))
|
||||||
|
{
|
||||||
|
destinationsProcessed.Add(destination);
|
||||||
|
pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (T destination in destinationsProcessed)
|
||||||
|
destinationsToBeProcessed.Remove(destination);
|
||||||
|
|
||||||
|
destinationsProcessed.Clear();
|
||||||
|
|
||||||
|
if (destinationsToBeProcessed.Count == 0)
|
||||||
|
return pathsToReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathsToReturn;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Walks through a map of nodes from the starting point.
|
/// Walks through a map of nodes from the starting point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="start"></param>
|
/// <param name="start"></param>
|
||||||
|
/// /// <param name="end"></param>
|
||||||
/// <param name="nodeParents"></param>
|
/// <param name="nodeParents"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static IEnumerable<T> GetPath<T>(T start, IDictionary<T, T> nodeParents)
|
private static IEnumerable<T> GetPath<T>(T start, T end, IDictionary<T, T> nodeParents, IEqualityComparer<T> comparer)
|
||||||
{
|
{
|
||||||
IcdHashSet<T> visited = new IcdHashSet<T>();
|
IcdHashSet<T> visited = new IcdHashSet<T>();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
yield return start;
|
yield return start;
|
||||||
|
|
||||||
|
if (comparer.Equals(start, end))
|
||||||
|
break;
|
||||||
|
|
||||||
visited.Add(start);
|
visited.Add(start);
|
||||||
|
|
||||||
T next;
|
T next;
|
||||||
|
|||||||
@@ -191,6 +191,54 @@ namespace ICD.Common.Utils
|
|||||||
return TryConvert(Convert.ToUInt32, value, out result);
|
return TryConvert(Convert.ToUInt32, value, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to parse the string as a short integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public static bool TryParse(string value, out short result)
|
||||||
|
{
|
||||||
|
return TryConvert(Convert.ToInt16, value, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to parse the string as an unsigned short integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public static bool TryParse(string value, out ushort result)
|
||||||
|
{
|
||||||
|
return TryConvert(Convert.ToUInt16, value, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to parse the string as a long integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public static bool TryParse(string value, out long result)
|
||||||
|
{
|
||||||
|
return TryConvert(Convert.ToInt64, value, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to parse the string as an unsigned long integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public static bool TryParse(string value, out ulong result)
|
||||||
|
{
|
||||||
|
return TryConvert(Convert.ToUInt64, value, out result);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to parse the string as a float.
|
/// Attempts to parse the string as a float.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user