using System; using System.Collections; using System.Collections.Generic; namespace Nanomesh { public class LinkedHashSet : IReadOnlyCollection where T : IComparable { private readonly Dictionary> elements; private LinkedHashNode first, last; /// /// Initializes a new instance of the class. /// public LinkedHashSet() { elements = new Dictionary>(); } /// /// Initializes a new instance of the class. /// /// public LinkedHashSet(IEnumerable initialValues) : this() { UnionWith(initialValues); } public LinkedHashNode First => first; public LinkedHashNode Last => last; #region Implementation of IEnumerable /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// 1 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// /// 2 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region Implementation of ICollection /// /// Gets the number of elements contained in the . /// /// /// The number of elements contained in the . /// public int Count => elements.Count; /// /// Removes all items from the . /// /// The is read-only. public void Clear() { elements.Clear(); first = null; last = null; } /// /// Determines whether the contains a specific value. /// /// /// true if is found in the ; otherwise, false. /// /// The object to locate in the . public bool Contains(T item) { return elements.ContainsKey(item); } /// /// Copies the elements of the to an , starting at a particular index. /// /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing.The zero-based index in at which copying begins. is null. is less than 0. is multidimensional.-or-The number of elements in the source is greater than the available space from to the end of the destination .-or-Type cannot be cast automatically to the type of the destination . public void CopyTo(T[] array, int arrayIndex) { int index = arrayIndex; foreach (T item in this) { array[index++] = item; } } /// /// Removes the first occurrence of a specific object from the . /// /// /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// /// The object to remove from the .The is read-only. public bool Remove(T item) { if (elements.TryGetValue(item, out LinkedHashNode node)) { elements.Remove(item); Unlink(node); return true; } return false; } #endregion #region Implementation of ISet /// /// Modifies the current set so that it contains all elements that are present in either the current set or the specified collection. /// /// The collection to compare to the current set. is null. public void UnionWith(IEnumerable other) { foreach (T item in other) { Add(item); } } /// /// Modifies the current set so that it contains only elements that are also in a specified collection. /// /// The collection to compare to the current set. is null. public void IntersectWith(IEnumerable other) { ISet otherSet = AsSet(other); LinkedHashNode current = first; while (current != null) { if (!otherSet.Contains(current.Value)) { elements.Remove(current.Value); Unlink(current); } current = current.Next; } } /// /// Removes all elements in the specified collection from the current set. /// /// The collection of items to remove from the set. is null. public void ExceptWith(IEnumerable other) { foreach (T item in other) { Remove(item); } } /// /// Modifies the current set so that it contains only elements that are present either in the current set or in the specified collection, but not both. /// /// The collection to compare to the current set. is null. public void SymmetricExceptWith(IEnumerable other) { foreach (T item in other) { if (elements.TryGetValue(item, out LinkedHashNode node)) { elements.Remove(item); Unlink(node); } else { Add(item); } } } /// /// Determines whether the current set is a superset of a specified collection. /// /// /// true if the current set is a superset of ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool IsSupersetOf(IEnumerable other) { int numberOfOthers = CountOthers(other, out int numberOfOthersPresent); // All others must be present. return numberOfOthersPresent == numberOfOthers; } /// /// Determines whether the current set is a correct superset of a specified collection. /// /// /// true if the object is a correct superset of ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool IsProperSupersetOf(IEnumerable other) { int numberOfOthers = CountOthers(other, out int numberOfOthersPresent); // All others must be present, plus we need to have at least one additional item. return numberOfOthersPresent == numberOfOthers && numberOfOthers < Count; } /// /// Determines whether the current set and the specified collection contain the same elements. /// /// /// true if the current set is equal to ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool SetEquals(IEnumerable other) { int numberOfOthers = CountOthers(other, out int numberOfOthersPresent); return numberOfOthers == Count && numberOfOthersPresent == Count; } /// /// Adds an element to the current set and returns a value to indicate if the element was successfully added. /// /// /// true if the element is added to the set; false if the element is already in the set. /// /// The element to add to the set. public bool Add(T item) { if (elements.ContainsKey(item)) { return false; } LinkedHashNode node = new LinkedHashNode(item) { Previous = last }; if (first == null) { first = node; } if (last != null) { last.Next = node; } last = node; elements.Add(item, node); return true; } public bool AddAfter(T item, LinkedHashNode itemInPlace) { if (elements.ContainsKey(item)) { return false; } LinkedHashNode node = new LinkedHashNode(item) { Previous = itemInPlace }; if (itemInPlace.Next != null) { node.Next = itemInPlace.Next; itemInPlace.Next.Previous = node; } else { last = node; } itemInPlace.Next = node; elements.Add(item, node); return true; } public bool PushAfter(T item, LinkedHashNode itemInPlace) { if (elements.ContainsKey(item)) { return false; } LinkedHashNode node = Last; Unlink(node); elements.Remove(node.Value); node.Value = item; node.Next = null; node.Previous = itemInPlace; if (itemInPlace.Next != null) { node.Next = itemInPlace.Next; itemInPlace.Next.Previous = node; } else { last = node; } itemInPlace.Next = node; elements.Add(item, node); return true; } public bool AddBefore(T item, LinkedHashNode itemInPlace) { if (elements.ContainsKey(item)) { return false; } LinkedHashNode node = new LinkedHashNode(item) { Next = itemInPlace }; if (itemInPlace.Previous != null) { node.Previous = itemInPlace.Previous; itemInPlace.Previous.Next = node; } else { first = node; } itemInPlace.Previous = node; elements.Add(item, node); return true; } public bool PushBefore(T item, LinkedHashNode itemInPlace) { if (elements.ContainsKey(item)) { return false; } LinkedHashNode node = Last; Unlink(node); elements.Remove(node.Value); node.Value = item; node.Previous = null; node.Next = itemInPlace; if (itemInPlace.Previous != null) { node.Previous = itemInPlace.Previous; itemInPlace.Previous.Next = node; } else { first = node; } itemInPlace.Previous = node; elements.Add(item, node); return true; } #endregion /// /// Returns an enumerator that iterates through a collection. /// /// /// An struct that can be used to iterate through the collection. /// public Enumerator GetEnumerator() { return new Enumerator(this); } /// /// Count the elements in the given collection and determine both the total /// count and how many of the elements that are present in the current set. /// private int CountOthers(IEnumerable items, out int numberOfOthersPresent) { numberOfOthersPresent = 0; int numberOfOthers = 0; foreach (T item in items) { numberOfOthers++; if (Contains(item)) { numberOfOthersPresent++; } } return numberOfOthers; } /// /// Cast the given collection to an ISet<T> if possible. If not, /// return a new set containing the items. /// private static ISet AsSet(IEnumerable items) { return items as ISet ?? new HashSet(items); } /// /// Unlink a node from the linked list by updating the node pointers in /// its preceeding and subsequent node. Also update the _first and _last /// pointers if necessary. /// private void Unlink(LinkedHashNode node) { if (node.Previous != null) { node.Previous.Next = node.Next; } if (node.Next != null) { node.Next.Previous = node.Previous; } if (ReferenceEquals(node, first)) { first = node.Next; } if (ReferenceEquals(node, last)) { last = node.Previous; } } public class LinkedHashNode { public TElement Value; public LinkedHashNode Next; public LinkedHashNode Previous; public LinkedHashNode(TElement value) { Value = value; } public override string ToString() { return Value.ToString(); } } public struct Enumerator : IEnumerator { private LinkedHashNode _node; private T _current; internal Enumerator(LinkedHashSet set) { _current = default(T); _node = set.first; } /// public bool MoveNext() { if (_node == null) { return false; } _current = _node.Value; _node = _node.Next; return true; } /// public T Current => _current; /// object IEnumerator.Current => Current; /// void IEnumerator.Reset() { throw new NotSupportedException(); } /// public void Dispose() { } } public void AddMin(T item) { LinkedHashNode current = Last; while (current != null && item.CompareTo(current.Value) < 0) { current = current.Previous; } if (current == Last) { return; } if (current == null) { AddBefore(item, First); } else { AddAfter(item, current); } } public void PushMin(T item) { LinkedHashNode current = Last; while (current != null && item.CompareTo(current.Value) < 0) { current = current.Previous; } if (current == Last) { return; } if (current == null) { PushBefore(item, First); } else { PushAfter(item, current); } } } }