using Flee.CalcEngine.PublicTypes; namespace Flee.CalcEngine.InternalTypes { /// /// Keeps track of our dependencies /// /// internal class DependencyManager { /// /// Map of a node and the nodes that depend on it /// private readonly Dictionary> _myDependentsMap; private readonly IEqualityComparer _myEqualityComparer; /// /// Map of a node and the number of nodes that point to it /// private readonly Dictionary _myPrecedentsMap; public DependencyManager(IEqualityComparer comparer) { _myEqualityComparer = comparer; _myDependentsMap = new Dictionary>(_myEqualityComparer); _myPrecedentsMap = new Dictionary(_myEqualityComparer); } private IDictionary CreateInnerDictionary() { return new Dictionary(_myEqualityComparer); } private IDictionary GetInnerDictionary(T tail) { Dictionary value = null; if (_myDependentsMap.TryGetValue(tail, out value) == true) { return value; } else { return null; } } // Create a dependency list with only the dependents of the given tails public DependencyManager CloneDependents(T[] tails) { IDictionary seenNodes = this.CreateInnerDictionary(); DependencyManager copy = new DependencyManager(_myEqualityComparer); foreach (T tail in tails) { this.CloneDependentsInternal(tail, copy, seenNodes); } return copy; } private void CloneDependentsInternal(T tail, DependencyManager target, IDictionary seenNodes) { if (seenNodes.ContainsKey(tail) == true) { // We've already added this node so just return return; } else { // Haven't seen this node yet; mark it as visited seenNodes.Add(tail, null); target.AddTail(tail); } IDictionary innerDict = this.GetInnerDictionary(tail); // Do the recursive add foreach (T head in innerDict.Keys) { target.AddDepedency(tail, head); this.CloneDependentsInternal(head, target, seenNodes); } } public T[] GetTails() { T[] arr = new T[_myDependentsMap.Keys.Count]; _myDependentsMap.Keys.CopyTo(arr, 0); return arr; } public void Clear() { _myDependentsMap.Clear(); _myPrecedentsMap.Clear(); } public void ReplaceDependency(T old, T replaceWith) { Dictionary value = _myDependentsMap[old]; _myDependentsMap.Remove(old); _myDependentsMap.Add(replaceWith, value); foreach (Dictionary innerDict in _myDependentsMap.Values) { if (innerDict.ContainsKey(old) == true) { innerDict.Remove(old); innerDict.Add(replaceWith, null); } } } public void AddTail(T tail) { if (_myDependentsMap.ContainsKey(tail) == false) { _myDependentsMap.Add(tail, (Dictionary)this.CreateInnerDictionary()); } } public void AddDepedency(T tail, T head) { IDictionary innerDict = this.GetInnerDictionary(tail); if (innerDict.ContainsKey(head) == false) { innerDict.Add(head, head); this.AddPrecedent(head); } } public void RemoveDependency(T tail, T head) { IDictionary innerDict = this.GetInnerDictionary(tail); this.RemoveHead(head, innerDict); } private void RemoveHead(T head, IDictionary dict) { if (dict.Remove(head) == true) { this.RemovePrecedent(head); } } public void Remove(T[] tails) { foreach (Dictionary innerDict in _myDependentsMap.Values) { foreach (T tail in tails) { this.RemoveHead(tail, innerDict); } } foreach (T tail in tails) { _myDependentsMap.Remove(tail); } } public void GetDirectDependents(T tail, List dest) { Dictionary innerDict = (Dictionary)this.GetInnerDictionary(tail); dest.AddRange(innerDict.Keys); } public T[] GetDependents(T tail) { Dictionary dependents = (Dictionary)this.CreateInnerDictionary(); this.GetDependentsRecursive(tail, dependents); T[] arr = new T[dependents.Count]; dependents.Keys.CopyTo(arr, 0); return arr; } private void GetDependentsRecursive(T tail, Dictionary dependents) { dependents[tail] = null; Dictionary directDependents = (Dictionary)this.GetInnerDictionary(tail); foreach (T pair in directDependents.Keys) { this.GetDependentsRecursive(pair, dependents); } } public void GetDirectPrecedents(T head, IList dest) { foreach (T tail in _myDependentsMap.Keys) { Dictionary innerDict = (Dictionary)this.GetInnerDictionary(tail); if (innerDict.ContainsKey(head) == true) { dest.Add(tail); } } } private void AddPrecedent(T head) { int count = 0; _myPrecedentsMap.TryGetValue(head, out count); _myPrecedentsMap[head] = count + 1; } private void RemovePrecedent(T head) { int count = _myPrecedentsMap[head] - 1; if (count == 0) { _myPrecedentsMap.Remove(head); } else { _myPrecedentsMap[head] = count; } } public bool HasPrecedents(T head) { return _myPrecedentsMap.ContainsKey(head); } public bool HasDependents(T tail) { Dictionary innerDict = (Dictionary)this.GetInnerDictionary(tail); return innerDict.Count > 0; } private string FormatValues(ICollection values) { string[] strings = new string[values.Count]; T[] keys = new T[values.Count]; values.CopyTo(keys, 0); for (int i = 0; i <= keys.Length - 1; i++) { strings[i] = keys[i].ToString(); } if (strings.Length == 0) { return ""; } else { return string.Join(",", strings); } } /// /// Add all nodes that don't have any incoming edges into a queue /// /// /// public Queue GetSources(T[] rootTails) { Queue q = new Queue(); foreach (T rootTail in rootTails) { if (this.HasPrecedents(rootTail) == false) { q.Enqueue(rootTail); } } return q; } public IList TopologicalSort(Queue sources) { IList output = new List(); List directDependents = new List(); while (sources.Count > 0) { T n = sources.Dequeue(); output.Add(n); directDependents.Clear(); this.GetDirectDependents(n, directDependents); foreach (T m in directDependents) { this.RemoveDependency(n, m); if (this.HasPrecedents(m) == false) { sources.Enqueue(m); } } } if (output.Count != this.Count) { throw new CircularReferenceException(); } return output; } #if DEBUG public string Precedents { get { List list = new List(); foreach (KeyValuePair pair in _myPrecedentsMap) { list.Add(pair.ToString()); } return string.Join(System.Environment.NewLine, list.ToArray()); } } #endif public string DependencyGraph { get { string[] lines = new string[_myDependentsMap.Count]; int index = 0; foreach (KeyValuePair> pair in _myDependentsMap) { T key = pair.Key; string s = this.FormatValues(pair.Value.Keys); lines[index] = $"{key} -> {s}"; index += 1; } return string.Join(System.Environment.NewLine, lines); } } public int Count => _myDependentsMap.Count; } }