From 00aaa17bb6b25e8bdfe2605639b6d7aab9ceb16e Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Fri, 16 Mar 2018 14:30:21 -0400 Subject: [PATCH] Adding GetClique and GetCliques recursion methods --- ICD.Common.Utils.Tests/RecursionUtilsTest.cs | 55 ++++++++++++++ ICD.Common.Utils/RecursionUtils.cs | 79 ++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/ICD.Common.Utils.Tests/RecursionUtilsTest.cs b/ICD.Common.Utils.Tests/RecursionUtilsTest.cs index f64c5f2..8a32c50 100644 --- a/ICD.Common.Utils.Tests/RecursionUtilsTest.cs +++ b/ICD.Common.Utils.Tests/RecursionUtilsTest.cs @@ -72,6 +72,61 @@ namespace ICD.Common.Utils.Tests } } + private static readonly Dictionary> s_CliqueGraph = new Dictionary> + { + {1, new[] {2, 3}}, + {2, new[] {1, 4}}, + {3, new[] {1}}, + {4, new[] {2}}, + {5, new[] {6}}, + {6, new[] {5}} + }; + + [Test] + public void GetCliquesTest() + { + int[][] cliques = + RecursionUtils.GetCliques(s_CliqueGraph.Keys, i => s_CliqueGraph[i]) + .Select(c => c.ToArray()) + .ToArray(); + + Assert.AreEqual(2, cliques.Length); + + int[] clique1 = cliques.FirstOrDefault(c => c.Contains(1)); + int[] clique2 = cliques.FirstOrDefault(c => c != clique1); + + Assert.NotNull(clique1); + Assert.NotNull(clique2); + + Assert.AreEqual(4, clique1.Length); + Assert.IsTrue(clique1.Contains(1)); + Assert.IsTrue(clique1.Contains(2)); + Assert.IsTrue(clique1.Contains(3)); + Assert.IsTrue(clique1.Contains(4)); + + Assert.AreEqual(2, clique2.Length); + Assert.IsTrue(clique2.Contains(5)); + Assert.IsTrue(clique2.Contains(6)); + } + + [Test] + public void GetCliqueTest() + { + int[] clique = RecursionUtils.GetClique(s_CliqueGraph, 1).ToArray(); + + Assert.AreEqual(4, clique.Length); + Assert.IsTrue(clique.Contains(1)); + Assert.IsTrue(clique.Contains(2)); + Assert.IsTrue(clique.Contains(3)); + Assert.IsTrue(clique.Contains(4)); + + clique = RecursionUtils.GetClique(s_CliqueGraph, 5).ToArray(); + + Assert.AreEqual(2, clique.Length); + Assert.IsTrue(clique.Contains(5)); + Assert.IsTrue(clique.Contains(6)); + } + [Test] public void BreadthFirstSearchTest() { diff --git a/ICD.Common.Utils/RecursionUtils.cs b/ICD.Common.Utils/RecursionUtils.cs index 7708a17..fe2a4fd 100644 --- a/ICD.Common.Utils/RecursionUtils.cs +++ b/ICD.Common.Utils/RecursionUtils.cs @@ -3,11 +3,90 @@ 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 a sequence of the cliques in the graph. + /// + /// + /// + /// + /// + public static IEnumerable> GetCliques(IEnumerable nodes, Func> getAdjacent) + { + if (nodes == null) + throw new ArgumentNullException("nodes"); + + if (getAdjacent == null) + throw new ArgumentNullException("getAdjacent"); + + Dictionary> map = nodes.ToDictionary(n => n, getAdjacent); + IcdHashSet visited = new IcdHashSet(); + + return map.Keys + .Where(n => !visited.Contains(n)) + .Select(node => GetClique(map, visited, node)); + } + + /// + /// Gets the clique containing the given node. + /// + /// + /// + /// + /// + public static IEnumerable GetClique(IDictionary> map, T node) + { + if (map == null) + throw new ArgumentNullException("map"); + +// ReSharper disable once CompareNonConstrainedGenericWithNull + if (node == null) + throw new ArgumentNullException("node"); + + return GetClique(map, new IcdHashSet(), node); + } + + /// + /// Gets the clique containing the node. + /// + /// + /// + /// + /// + /// + private static IEnumerable GetClique(IDictionary> map, IcdHashSet visited, T node) + { + if (map == null) + throw new ArgumentNullException("map"); + + if (visited == null) + throw new ArgumentNullException("visited"); + +// ReSharper disable once CompareNonConstrainedGenericWithNull + if (node == null) + throw new ArgumentNullException("node"); + + if (visited.Contains(node)) + yield break; + + if (!map.ContainsKey(node)) + yield break; + + visited.Add(node); + + yield return node; + + IEnumerable adjacent = map.GetDefault(node, Enumerable.Empty()); + + foreach (T item in adjacent.SelectMany(a => GetClique(map, visited, a))) + yield return item; + } + /// /// Returns all of the nodes in the tree via breadth-first search. ///