{"id":142,"date":"2020-08-02T10:31:12","date_gmt":"2020-08-02T10:31:12","guid":{"rendered":"https:\/\/michaeljohnsteiner.com\/?p=142"},"modified":"2020-10-04T11:43:31","modified_gmt":"2020-10-04T11:43:31","slug":"miniset-cs","status":"publish","type":"post","link":"https:\/\/michaeljohnsteiner.com\/index.php\/2020\/08\/02\/miniset-cs\/","title":{"rendered":"MiniSet.cs"},"content":{"rendered":"\n<p>A Small Segmented Set 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;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\n\/\/\/ &lt;summary>\n\/\/\/ Date: 10\/03\/20 **MJS**\n\/\/\/ Customer set using indexed hash buckets.\n\/\/\/ Possible uses less memory then standard hashset, with very similar search performance.\n\/\/\/ &lt;\/summary>\n\/\/\/ &lt;typeparam name=\"T\">&lt;\/typeparam>\n[DebuggerDisplay(\"Count = {\" + nameof(Count) + \"}\")]\n[Serializable]\npublic class MiniSet&lt;T> : IEnumerable&lt;T>\n{\n    private  Bucket[]             _buckets;\n    private  SizeHelper&lt;T>        _newSize = new SizeHelper&lt;T>();\n    internal IEqualityComparer&lt;T> Comparer;\n    public   int                  Count;\n    public   int                  ReSizes;\n    public MiniSet(int size) : this(size, EqualityComparer&lt;T>.Default)\n    {\n    }\n    public MiniSet(int size, IEqualityComparer&lt;T> comparer)\n    {\n        if (comparer == null)\n            comparer = EqualityComparer&lt;T>.Default;\n        Comparer = comparer;\n        _buckets = new Bucket[size];\n        Count    = 0;\n        ReSizes  = 0;\n    }\n\n    public MiniSet(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        Count    = coll.Count;\n        _buckets = new Bucket[Count];\n        UnionWith(coll);\n    }\n    public int                     ElementCount         => GetElementCount();\n    public int                     NumberOfEmptyBuckets => GetNumberOfEmptyBuckets();\n    public (int mDepth, int index) MaximumBucketDepth   => GetMaximumBucketDepth();\n    public float                   LoadRatio            => GetLoadRatio();\n    IEnumerator IEnumerable.GetEnumerator()\n    {\n        return GetEnumerator();\n    }\n    public void Clear()\n    {\n        _buckets.Clear();\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 Add(T item)\n    {\n        EnsureSize();\n        var hashCode = Comparer.GetHashCode(item) &amp; int.MaxValue;\n        if (FindEntry(item, hashCode).APos != -1)\n            return false;\n        var pos = hashCode % _buckets.Length;\n        if (_buckets[pos] == null)\n            _buckets[pos] = new Bucket();\n        _buckets[pos].Add(item);\n        Count++;\n        return true;\n    }\n\n    public int AddRange(IEnumerable&lt;T> items)\n    {\n        return items.Sum(i => !Add(i) ? 0 : 1);\n    }\n    public T[] ToArray()\n    {\n        var newArray = new T[Count];\n        using (var en = GetEnumerator())\n        {\n            var ptr = 0;\n            while (en.MoveNext())\n            {\n                var value = en.Current;\n                if (value == null)\n                    break;\n                newArray[ptr++] = value;\n            }\n            return newArray;\n        }\n    }\n    private (int APos, int BPos) FindEntry(T item, int hashCode)\n    {\n        if (Count == 0)\n            return (-1, -1);\n        var aPos = hashCode % _buckets.Length;\n        var bPos = 0;\n        if (_buckets[aPos] == null)\n            _buckets[aPos] = new Bucket();\n        foreach (var i in _buckets[aPos].Values)\n        {\n            if (Comparer.Equals(i, item))\n                return (aPos, bPos);\n            bPos++;\n        }\n        return (-1, -1);\n    }\n    private void EnsureSize()\n    {\n        if (Count >= _buckets.Length)\n        {\n            ReSizes++;\n            var cArray = ToArray();\n            _buckets = new Bucket[_newSize.GetNewSize(_buckets.Length)];\n            foreach (var i in cArray)\n            {\n                var hashCode = Comparer.GetHashCode(i) &amp; int.MaxValue;\n                var pos      = hashCode % _buckets.Length;\n                if (_buckets[pos] == null)\n                    _buckets[pos] = new Bucket();\n                _buckets[pos].Add(i);\n            }\n        }\n    }\n    public bool Contains(T item)\n    {\n        var hashCode = Comparer.GetHashCode(item) &amp; int.MaxValue;\n        return FindEntry(item, hashCode).APos != -1;\n    }\n    public IEnumerator&lt;T> GetEnumerator()\n    {\n        for (var i = 0; i &lt; _buckets.Length; i++)\n            if (_buckets[i] != null)\n                for (var j = 0; j &lt; _buckets[i].Count; ++j)\n                    yield return _buckets[i].Values[j];\n    }\n    public int GetElementCount()\n    {\n        var count = 0;\n        for (var i = 0; i &lt; _buckets.Length; i++)\n            if (_buckets[i] != null)\n            {\n                var c = _buckets[i].Count;\n                count += c;\n            }\n        return count;\n    }\n    public int GetNumberOfEmptyBuckets()\n    {\n        var count = 0;\n        for (var i = 0; i &lt; _buckets.Length; i++)\n            if (_buckets[i] == null)\n                count++;\n        return count;\n    }\n    public int GetNumberOfFilledBuckets()\n    {\n        var count = 0;\n        for (var i = 0; i &lt; _buckets.Length; i++)\n            if (_buckets[i] != null)\n                count++;\n        return count;\n    }\n    public (int mDepth, int index) GetMaximumBucketDepth()\n    {\n        var max = 0;\n        var j   = 0;\n        for (var i = 0; i &lt; _buckets.Length; i++)\n            if (_buckets[i] != null)\n            {\n                var count = _buckets[i].Count;\n                if (count > max)\n                {\n                    max = count;\n                    j   = i;\n                }\n            }\n        return (max, j);\n    }\n    public KeyValuePair&lt;int, int>[] BucketDepthList => GetBucketDepthList();\n    public KeyValuePair&lt;int, int>[] GetBucketDepthList()\n    {\n        var bdic = new Dictionary&lt;int, int>();\n        for (var i = 0; i &lt; _buckets.Length; i++)\n            if (_buckets[i] != null)\n            {\n                var count = _buckets[i].Count;\n                if (!bdic.ContainsKey(count))\n                {\n                    bdic.Add(count, 0);\n                    bdic[count]++;\n                }\n                else\n                {\n                    bdic[count]++;\n                }\n            }\n        return bdic.OrderByDescending(x=>x.Value).ToArray();\n    }\n    public float GetLoadRatio()\n    {\n        var x = Count;\n        var y = _buckets.Length;\n        var r = x \/ (float) y;\n        return r;\n    }\n    internal class Bucket\n    {\n        public int Count;\n        public T[] Values;\n        public Bucket()\n        {\n            Values = new T[11];\n            Count  = 0;\n        }\n        public void Add(T item)\n        {\n            if (Count >= Values.Length)\n            {\n                var ta = new T[Values.Length+3];\n                Array.Copy(Values, 0, ta, 0, Count);\n                Values = ta;\n            }\n            Values[Count++] = item;\n        }\n    }\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>A Small Segmented Set 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":[5,68,69],"_links":{"self":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/142"}],"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=142"}],"version-history":[{"count":5,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/142\/revisions"}],"predecessor-version":[{"id":235,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/142\/revisions\/235"}],"wp:attachment":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/media?parent=142"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/categories?post=142"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/tags?post=142"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}