using System;
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(T node, Func> getAdjacent)
{
if (node == null)
throw new ArgumentNullException("node");
if (getAdjacent == null)
throw new ArgumentNullException("getAdjacent");
return BreadthFirstSearch(node, getAdjacent);
}
///
/// 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");
return GetCliqueIterator(map, visited, node);
}
///
/// Gets the clique containing the node.
///
///
///
///
///
///
private static IEnumerable GetCliqueIterator(IDictionary> map, IcdHashSet visited, T node)
{
if (!visited.Add(node))
yield break;
IEnumerable adjacent;
if (!map.TryGetValue(node, out adjacent))
yield break;
yield return node;
foreach (T item in adjacent.SelectMany(a => GetClique(map, visited, a)))
yield return item;
}
///
/// Returns true if there is a path from the given root to the given destination node.
///
///
///
///
///
///
public static bool BreadthFirstSearch(T root, T destination, Func> getChildren)
{
if (getChildren == null)
throw new ArgumentNullException("getChildren");
return BreadthFirstSearch(root, destination, getChildren, EqualityComparer.Default);
}
///
/// Returns true if there is a path from the given root to the given destination node.
///
///
///
///
///
///
///
public static bool BreadthFirstSearch(T root, T destination, Func> getChildren, IEqualityComparer comparer)
{
if (getChildren == null)
throw new ArgumentNullException("getChildren");
if (comparer == null)
throw new ArgumentNullException("comparer");
return BreadthFirstSearchPath(root, destination, getChildren, comparer) != null;
}
///
/// 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");
return BreadthFirstSearchIterator(root, getChildren);
}
///
/// Returns all of the nodes in the graph via breadth-first search.
///
///
///
///
///
private static IEnumerable BreadthFirstSearchIterator(T root, Func> getChildren)
{
IcdHashSet visited = new IcdHashSet {root};
Queue process = new Queue();
process.Enqueue(root);
T current;
while (process.Dequeue(out current))
{
yield return current;
foreach (T child in getChildren(current).Where(c => !visited.Contains(c)))
{
visited.Add(child);
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");
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");
if (comparer == null)
throw new ArgumentNullException("comparer");
// Edge case - root and destination are the same
if (comparer.Equals(root, destination))
return new[] {root};
Queue queue = new Queue();
queue.Enqueue(root);
Dictionary nodeParents = new Dictionary(comparer);
T current;
while (queue.Dequeue(out current))
{
foreach (T node in getChildren(current))
{
if (nodeParents.ContainsKey(node))
continue;
queue.Enqueue(node);
nodeParents.Add(node, current);
// Found a path to the destination
if (comparer.Equals(node, destination))
return GetPath(destination, root, nodeParents, comparer).Reverse();
}
}
return null;
}
///
/// Returns the shortest path from root to each destination via breadth-first search.
///
///
///
///
///
///
public static IEnumerable>>
BreadthFirstSearchPathManyDestinations(T root, IEnumerable destinations, Func> getChildren)
{
if (destinations == null)
throw new ArgumentNullException("destinations");
if (getChildren == null)
throw new ArgumentNullException("getChildren");
return BreadthFirstSearchPathManyDestinations(root, destinations, getChildren, EqualityComparer.Default);
}
///
/// Returns the shortest path from root to each destination via breadth-first search.
///
///
///
///
///
///
///
public static IEnumerable>>
BreadthFirstSearchPathManyDestinations(T root, IEnumerable destinations, Func> getChildren,
IEqualityComparer comparer)
{
if (destinations == null)
throw new ArgumentNullException("destinations");
if (getChildren == null)
throw new ArgumentNullException("getChildren");
if (comparer == null)
throw new ArgumentNullException("comparer");
IcdHashSet destinationsToBeProcessed = new IcdHashSet(destinations);
// Edge case, root is the destination
foreach (T destination in
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)).ToArray())
{
destinationsToBeProcessed.Remove(destination);
yield return new KeyValuePair>(destination, new[] {root});
}
if (destinationsToBeProcessed.Count == 0)
yield break;
Queue queue = new Queue();
queue.Enqueue(root);
Dictionary nodeParents = new Dictionary(comparer);
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)).ToArray())
{
destinationsToBeProcessed.Remove(destination);
yield return
new KeyValuePair>(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
}
if (destinationsToBeProcessed.Count == 0)
yield break;
}
}
}
///
/// Walks through a map of nodes from the starting point.
///
///
///
///
///
///
///
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;
if (!nodeParents.TryGetValue(start, out next))
break;
if (visited.Contains(next))
break;
start = next;
}
}
}
}