{"id":122,"date":"2020-07-30T08:35:28","date_gmt":"2020-07-30T08:35:28","guid":{"rendered":"https:\/\/michaeljohnsteiner.com\/?p=122"},"modified":"2020-07-30T08:35:28","modified_gmt":"2020-07-30T08:35:28","slug":"tinyset-cs","status":"publish","type":"post","link":"https:\/\/michaeljohnsteiner.com\/index.php\/2020\/07\/30\/tinyset-cs\/","title":{"rendered":"TinySet.cs"},"content":{"rendered":"\n<p>A Small HashSet Class<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\n[DebuggerTypeProxy(typeof(HashSetDebugView&lt;>))]\n[DebuggerDisplay(\"Count = {\" + nameof(Count) + \"}\")]\n[Serializable]\npublic class TinySet&lt;T>\n{\n    private  int[]                _hashBuckets;\n    private  SizeHelper&lt;T>        _newSize = new SizeHelper&lt;T>();\n    internal IEqualityComparer&lt;T> Comparer;\n    public   int                  Position;\n    internal Slot[]               Slots;\n    public TinySet() : this(101, EqualityComparer&lt;T>.Default)\n    {\n    }\n    public TinySet(int size) : this(size, EqualityComparer&lt;T>.Default)\n    {\n    }\n    public TinySet(IEqualityComparer&lt;T> comparer) : this(101, comparer)\n    {\n    }\n    public TinySet(int size, IEqualityComparer&lt;T> comparer)\n    {\n        if (comparer == null)\n            comparer = EqualityComparer&lt;T>.Default;\n        Comparer     = comparer;\n        _hashBuckets = new int[size];\n        Slots        = new Slot[size];\n        Count        = 0;\n        Position     = -1;\n    }\n    public TinySet(IEnumerable&lt;T> collection, IEqualityComparer&lt;T> comparer)\n    {\n        if (comparer == null)\n            comparer = EqualityComparer&lt;T>.Default;\n        Comparer = comparer;\n        if (!(collection is ICollection&lt;T> coll))\n            return;\n        var size = coll.Count;\n        _hashBuckets = new int[size];\n        Slots        = new Slot[size];\n        UnionWith(coll);\n    }\n    public int Count\n    {\n        get;\n        private set;\n    }\n    public T[] ToArray()\n    {\n        var newArray = new T[Count];\n        var copied   = 0;\n        for (var i = 0; i &lt; Count &amp;&amp; copied &lt; Count; i++)\n            if (Slots[i].HashCode > 0)\n                newArray[copied++] = Slots[i].Value;\n        return newArray;\n    }\n    public void Create(int size, IEqualityComparer&lt;T> comparer)\n    {\n        if (comparer == null)\n            comparer = EqualityComparer&lt;T>.Default;\n        Comparer     = comparer;\n        _hashBuckets = new int[size];\n        Slots        = new Slot[size];\n        Count        = 0;\n        Position     = -1;\n    }\n    public void Clear()\n    {\n        var size = Slots.Length;\n        _hashBuckets = new int[size];\n        Slots        = new Slot[size];\n        Count        = 0;\n        Position     = -1;\n    }\n    public bool Add(T item)\n    {\n        return Insert(item, true);\n    }\n    public bool Contains(T item)\n    {\n        return Insert(item, false);\n    }\n    internal bool Insert(T item, bool add)\n    {\n        var hashCode = Comparer.GetHashCode(item) &amp; int.MaxValue;\n        if (FindEntry(item, hashCode) != -1)\n            return true;\n        if (add)\n        {\n            if (Count >= Slots.Length)\n                SetSizeAndOrForceNewHashCodes(_newSize.GetNewSize(_hashBuckets.Length));\n            var hashPos = hashCode % _hashBuckets.Length;\n            Slots[Count].Next     = _hashBuckets[hashPos] - 1;\n            Slots[Count].Value    = item;\n            Slots[Count].HashCode = hashCode;\n            _hashBuckets[hashPos] = Count + 1;\n            Position              = Count;\n            ++Count;\n        }\n        return false;\n    }\n    public void ForceNewHashCodes()\n    {\n        SetSizeAndOrForceNewHashCodes();\n    }\n    private void SetSizeAndOrForceNewHashCodes(int Size = 0)\n    {\n        if (Count == 0) return;\n        var newSize        = Size == 0 ? Count : Size;\n        var newSlots       = new Slot[newSize];\n        var newHashBuckets = new int[newSize];\n        if (Slots != null)\n            Array.Copy(Slots, 0, newSlots, 0, Count);\n        for (var i = 0; i &lt; newSize; ++i)\n            if (newSlots[i].HashCode > 0 &amp;&amp; newSlots[i].Value != null)\n                newSlots[i].HashCode = Comparer.GetHashCode(newSlots[i].Value) &amp; int.MaxValue;\n        for (var i = 0; i &lt; newSize; ++i)\n        {\n            var pos = newSlots[i].HashCode % newSize;\n            newSlots[i].Next    = newHashBuckets[pos] - 1;\n            newHashBuckets[pos] = i                   + 1;\n        }\n        Slots        = newSlots;\n        _hashBuckets = newHashBuckets;\n    }\n    public void TrimExcess()\n    {\n        var newHashBuckets = new int[Count];\n        var newSlots       = new Slot[Count];\n        Array.Copy(Slots, newSlots, Count);\n        for (var i = 0; i &lt; Count; i++)\n        {\n            var pos = newSlots[i].HashCode % Count;\n            newSlots[i].Next    = newHashBuckets[pos] - 1;\n            newHashBuckets[pos] = i                   + 1;\n        }\n        _hashBuckets = newHashBuckets;\n        Slots        = newSlots;\n    }\n    public bool Remove(T item)\n    {\n        if (Count != 0)\n        {\n            var hashCode = Comparer.GetHashCode(item) &amp; int.MaxValue;\n            var iPos     = hashCode % _hashBuckets.Length;\n            var tPos     = -1;\n            for (var sPos = _hashBuckets[iPos] - 1; sPos >= 0; sPos = Slots[sPos].Next)\n            {\n                if (Slots[sPos].HashCode == hashCode &amp;&amp; Comparer.Equals(Slots[sPos].Value, item))\n                {\n                    if (tPos &lt; 0)\n                        _hashBuckets[iPos] = Slots[sPos].Next + 1;\n                    else\n                        Slots[tPos].Next = Slots[sPos].Next;\n                    Slots[sPos].HashCode = -1;\n                    Slots[sPos].Value    = default;\n                    Slots[sPos].Next     = 0;\n                    --Count;\n                    return true;\n                }\n                tPos = sPos;\n            }\n        }\n        return false;\n    }\n    private int FindEntry(T item, int hashCode)\n    {\n        for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = Slots[position].Next)\n            if (Slots[position].HashCode == hashCode &amp;&amp; Comparer.Equals(Slots[position].Value, item))\n            {\n                Position = position;\n                return position;\n            }\n        return -1;\n    }\n    public int FindEntry(T item)\n    {\n        return FindEntry(item, Comparer.GetHashCode(item) &amp; int.MaxValue);\n    }\n    public void ExceptWith(IEnumerable&lt;T> other)\n    {\n        if (other == null)\n            throw new Exception(\"The other set must not be null.\");\n        if (Count == 0)\n            return;\n        if (Equals(other, this))\n            Clear();\n        else\n            foreach (var obj in other)\n                Remove(obj);\n    }\n    public void UnionWith(IEnumerable&lt;T> other)\n    {\n        if (other == null)\n            throw new Exception(\"The other set must not be null.\");\n        foreach (var obj in other)\n            Add(obj);\n    }\n    public bool Overlaps(IEnumerable&lt;T> other)\n    {\n        if (other == null)\n            throw new Exception(\"The other set must not be null.\");\n        return Count != 0 &amp;&amp; other.Any(Contains);\n    }\n    public bool ContainsAllElements(IEnumerable&lt;T> other)\n    {\n        return other.All(Contains);\n    }\n    public int RemoveWhere(Predicate&lt;T> pred)\n    {\n        if (pred == null)\n            throw new Exception(\"The Predicate cannot be null.\");\n        var matches = 0;\n        for (var i = 0; i &lt; Count; ++i)\n            if (Slots[i].HashCode >= 0)\n            {\n                var obj = Slots[i].Value;\n                if (pred(obj) &amp;&amp; Remove(obj))\n                    ++matches;\n            }\n        return matches;\n    }\n    internal struct Slot\n    {\n        public int HashCode;\n        public int Next;\n        public T   Value;\n    }\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>A Small HashSet Class<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[58,57,56],"_links":{"self":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/122"}],"collection":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/comments?post=122"}],"version-history":[{"count":1,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/122\/revisions"}],"predecessor-version":[{"id":123,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/122\/revisions\/123"}],"wp:attachment":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/media?parent=122"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/categories?post=122"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/tags?post=122"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}