コード例 #1
0
        public static Entry *Find <T>(UnsafeOrderedCollection *collection, T key) where T : unmanaged, IComparable <T>
        {
            int entryIndex = collection->Root;

            while (entryIndex != 0)
            {
                var entry    = GetEntry(collection, entryIndex);
                var entryKey = GetKey <T>(collection, entryIndex);
                var compare  = key.CompareTo(entryKey);
                if (compare < 0)
                {
                    entryIndex = entry->Left;
                }
                else if (compare > 0)
                {
                    entryIndex = entry->Right;
                }
                else
                {
                    return(entry);
                }
            }

            return(null);
        }
コード例 #2
0
        public static void VisitNodes <T>(UnsafeOrderedCollection *collection, Action <T?, T, int, bool> callback) where T : unmanaged
        {
            Stack <(int, int, int, bool)> entries = new Stack <(int, int, int, bool)>();

            entries.Push((0, collection->Root, 0, false));

            while (entries.Count > 0)
            {
                var(p, n, d, s) = entries.Pop();

                if (n > 0)
                {
                    var ne = GetEntry(collection, n);

                    entries.Push((n, ne->Left, d + 1, true));
                    entries.Push((n, ne->Right, d + 1, false));

                    if (p > 0)
                    {
                        callback(GetKey <T>(collection, p), GetKey <T>(collection, n), d, s);
                    }
                    else
                    {
                        callback(null, GetKey <T>(collection, n), d, s);
                    }
                }
            }
        }
コード例 #3
0
        public static T GetKey <T>(UnsafeOrderedCollection *collection, int index) where T : unmanaged
        {
            Assert.Check(index > 0);
            var entry = GetEntry(collection, index);

            return(*(T *)((byte *)entry + collection->KeyOffset));
        }
コード例 #4
0
        static string PrintEntry <T>(UnsafeOrderedCollection *collection, int index, Func <T, string> print) where T : unmanaged
        {
            var entry = GetEntry(collection, index);

            if (entry == null)
            {
                return("*");
            }

            if (entry->Left == 0 && entry->Right == 0)
            {
                return(GetKey <T>(collection, index).ToString());
            }

            var key   = print(GetKey <T>(collection, index));
            var bal   = PrintBalance(entry);
            var left  = PrintEntry <T>(collection, entry->Left, print);
            var right = PrintEntry <T>(collection, entry->Right, print);

            if (index == collection->Root)
            {
                return($"{key}{bal}={left}|{right}");
            }
            else
            {
                return($"[{key}{bal}={left}|{right}]");
            }
        }
コード例 #5
0
            public Iterator(UnsafeOrderedCollection *collection)
            {
                Collection = collection;
                Current    = null;

                _depth = 0;
                _index = Collection->Root;
            }
コード例 #6
0
 public static Entry *GetEntry(UnsafeOrderedCollection *collection, int index)
 {
     if (index <= 0)
     {
         return(null);
     }
     return(collection->Entries.Element <Entry>(index - 1));
 }
コード例 #7
0
        internal static void Free(UnsafeOrderedCollection *collection)
        {
            // free the buffer
            UnsafeBuffer.Free(&collection->Entries);

            // free collection header itself
            Native.Free(collection);
        }
コード例 #8
0
        public static Entry *GetEntry(UnsafeOrderedCollection *collection, int index)
        {
            if (index <= 0)
            {
                return(null);
            }

            return((Entry *)UnsafeBuffer.Element(collection->Entries.Ptr, index - 1, collection->Entries.Stride));
        }
コード例 #9
0
        public static void Clear(UnsafeOrderedCollection *collection)
        {
            collection->Root      = 0;
            collection->UsedCount = 0;

            collection->FreeHead  = 0;
            collection->FreeCount = 0;

            collection->Entries.Clear();
        }
コード例 #10
0
        static int Height(UnsafeOrderedCollection *collection, int index)
        {
            if (index == 0)
            {
                return(0);
            }

            var entry = GetEntry(collection, index);

            return(1 + Math.Max(Height(collection, entry->Left), Height(collection, entry->Right)));
        }
コード例 #11
0
        static void FreeEntry(UnsafeOrderedCollection *collection, int entryIndex)
        {
            var entry = GetEntry(collection, entryIndex);

            entry->Right   = 0;
            entry->Balance = 0;
            entry->Left    = collection->FreeHead;

            collection->FreeHead  = entryIndex;
            collection->FreeCount = collection->FreeCount + 1;
        }
コード例 #12
0
        public static bool Remove <T>(UnsafeOrderedCollection *collection, T key) where T : unmanaged, IComparable <T>
        {
            int preCount = GetCount(collection);

            if (preCount == 0)
            {
                return(false);
            }

            collection->Root = DeletePerform <T>(collection, key);

            return(preCount != GetCount(collection));
        }
コード例 #13
0
        public static void Insert <T>(UnsafeOrderedCollection *collection, T key) where T : unmanaged, IComparable <T>
        {
            if (collection->FreeCount == 0 && collection->UsedCount == collection->Entries.Length)
            {
                if (collection->Entries.Dynamic == 1)
                {
                    Expand(collection);
                }
                else
                {
                    throw new InvalidOperationException(COLLECTION_FULL);
                }
            }

            collection->Root = InsertPerform <T>(collection, key, false);
        }
コード例 #14
0
        public static Entry *Insert <T>(UnsafeOrderedCollection *collection, T key, bool update = false)
            where T : unmanaged, IComparable <T>
        {
            if (collection->FreeCount == 0 && collection->UsedCount == collection->Entries.Length)
            {
                if (collection->Entries.Dynamic == 1)
                {
                    Expand(collection);
                }
                else
                {
                    throw new InvalidOperationException(ThrowHelper.InvalidOperation_CollectionFull);
                }
            }

            return(InsertPerform <T>(collection, key, update));
        }
コード例 #15
0
        static int RotateLeftRight(UnsafeOrderedCollection *collection, int nodeIndex)
        {
            var node = GetEntry(collection, nodeIndex);

            // left
            var leftIndex = node->Left;
            var left      = GetEntry(collection, leftIndex);

            // left right
            var leftRightIndex = left->Right;
            var leftRight      = GetEntry(collection, leftRightIndex);

            // left right left
            var leftRightLeftIndex = leftRight->Left;

            // left right right
            var leftRightRightIndex = leftRight->Right;

            node->Left  = leftRightRightIndex;
            left->Right = leftRightLeftIndex;

            leftRight->Left  = leftIndex;
            leftRight->Right = nodeIndex;

            if (leftRight->Balance == -1)
            {
                node->Balance = 0;
                left->Balance = 1;
            }
            else if (leftRight->Balance == 0)
            {
                node->Balance = 0;
                left->Balance = 0;
            }
            else
            {
                node->Balance = -1;
                left->Balance = 0;
            }

            // balance is ALWAYS zero after a left/right rotation
            leftRight->Balance = 0;

            return(leftRightIndex);
        }
コード例 #16
0
        static int RotateRightLeft(UnsafeOrderedCollection *collection, int entryIndex)
        {
            var node = GetEntry(collection, entryIndex);

            // right
            var rightIndex = node->Right;
            var right      = GetEntry(collection, rightIndex);

            // right left
            var rightLeftIndex = right->Left;
            var rightLeft      = GetEntry(collection, rightLeftIndex);

            // right left left
            var rightLeftLeftIndex = rightLeft->Left;

            // right left right
            var rightLeftRightIndex = rightLeft->Right;

            node->Right = rightLeftLeftIndex;
            right->Left = rightLeftRightIndex;

            rightLeft->Right = rightIndex;
            rightLeft->Left  = entryIndex;

            if (rightLeft->Balance == 1)
            {
                node->Balance  = 0;
                right->Balance = -1;
            }
            else if (rightLeft->Balance == 0)
            {
                node->Balance  = 0;
                right->Balance = 0;
            }
            else
            {
                node->Balance  = 1;
                right->Balance = 0;
            }

            // balance is ALWAYS zero after a right/left rotation
            rightLeft->Balance = 0;

            return(rightLeftIndex);
        }
コード例 #17
0
        static void Expand(UnsafeOrderedCollection *collection)
        {
            Assert.Check(collection->Entries.Dynamic == 1);
            Assert.Check(collection->FreeCount == 0);
            Assert.Check(collection->FreeHead == 0);

            var capacity   = collection->Entries.Length * 2;
            var newEntries = default(UnsafeBuffer);

            UnsafeBuffer.InitDynamic(&newEntries, capacity, collection->Entries.Stride);
            UnsafeBuffer.Copy(collection->Entries, 0, newEntries, 0, collection->Entries.Length);

            // free old memory
            UnsafeBuffer.Free(&collection->Entries);

            // new storage
            collection->Entries = newEntries;
        }
コード例 #18
0
        static int CreateEntry <T>(UnsafeOrderedCollection *collection, T key) where T : unmanaged
        {
            Entry *entry;
            int    entryIndex;

            if (collection->FreeHead > 0)
            {
                Assert.Check(collection->FreeCount > 0);

                // grab from free head
                entry = GetEntry(collection, entryIndex = collection->FreeHead);

                // new free-head is entrys left pointer
                collection->FreeHead  = entry->Left;
                collection->FreeCount = collection->FreeCount - 1;

                // we have to clear left pointer
                entry->Left = 0;
            }
            else
            {
                // this has to hold, as Insert<T> checks and expands if needed
                Assert.Check(collection->UsedCount < collection->Entries.Length);

                // grab entry from UsedCount, we add increment before as entries are 1 indexed
                entry = GetEntry(collection, entryIndex = ++collection->UsedCount);
            }

            // when we get a new entry, it's left/right/balance all have to be zero
            Assert.Check(entry->Left == 0);
            Assert.Check(entry->Right == 0);
            Assert.Check(entry->Balance == 0);

            // write key to entry
            *(T *)((byte *)entry + collection->KeyOffset) = key;

            // return entry index
            return(entryIndex);
        }
コード例 #19
0
        static int RotateLeft(UnsafeOrderedCollection *collection, int entryIndex, int *balance)
        {
            var entry = GetEntry(collection, entryIndex);

            // grab right
            var rightIndex = entry->Right;
            var right      = GetEntry(collection, rightIndex);

            // new right of entry is right->left
            entry->Right = right->Left;

            //new left of right is the entry
            right->Left = entryIndex;

            // increase right balance by 1
            *balance = ++right->Balance;

            // balance of entry is inverse of old rights balance
            entry->Balance = -right->Balance;

            // return right index
            return(rightIndex);
        }
コード例 #20
0
        static int RotateRight(UnsafeOrderedCollection *collection, int entryIndex, int *balance)
        {
            var entry = GetEntry(collection, entryIndex);

            // grab left
            var leftIndex = entry->Left;
            var left      = GetEntry(collection, leftIndex);

            // new left of entry is left->right
            entry->Left = left->Right;

            // new right of left is the entry
            left->Right = entryIndex;

            // reduce left balance by 1
            *balance = --left->Balance;

            // new entry balance becomes inverse of it's previous lefts balance
            entry->Balance = -left->Balance;

            // return entries old left to take its place in the path
            return(leftIndex);
        }
コード例 #21
0
 public static string PrintTree <T>(UnsafeOrderedCollection *collection, Func <T, string> print) where T : unmanaged
 {
     return(PrintEntry <T>(collection, collection->Root, print));
 }
コード例 #22
0
        static void SetKey <T>(UnsafeOrderedCollection *collection, int index, T value) where T : unmanaged
        {
            var entry = GetEntry(collection, index);

            *(T *)((byte *)entry + collection->KeyOffset) = value;
        }
コード例 #23
0
 static void AssertTree(UnsafeOrderedCollection *c, string expected)
 {
     NUnit.Framework.Assert.AreEqual(expected, UnsafeOrderedCollection.PrintTree <int>(c, Print));
 }
コード例 #24
0
        static int InsertPerform <T>(UnsafeOrderedCollection *collection, T insertKey, bool update) where T : unmanaged, IComparable <T>
        {
            const bool L = true;
            const bool R = false;

            int * path = stackalloc int[MAX_DEPTH];
            bool *side = stackalloc bool[MAX_DEPTH];

            // find insertion slot
            int entryIndex = collection->Root;
            int pathSize   = 0;

            while (entryIndex != 0 && pathSize < (MAX_DEPTH - 1))
            {
                var entry    = GetEntry(collection, entryIndex);
                var entryKey = GetKey <T>(collection, entryIndex);

                var compare = insertKey.CompareTo(entryKey);
                if (compare < 0)
                {
                    path[pathSize] = entryIndex;
                    side[pathSize] = L;
                    entryIndex     = entry->Left;
                }
                else if (compare > 0)
                {
                    path[pathSize] = entryIndex;
                    side[pathSize] = R;
                    entryIndex     = entry->Right;
                }
                else
                {
                    if (update)
                    {
                        SetKey(collection, entryIndex, insertKey);
                    }

                    return(collection->Root);
                }

                ++pathSize;
            }

            // means we hit max depth
            if (entryIndex != 0)
            {
                throw new InvalidOperationException("MAX_DEPTH EXCEEDED");
            }

            // means we're at the root, so just insert and return it
            if (pathSize == 0)
            {
                return(CreateEntry <T>(collection, insertKey));
            }

            // now we've created the entry at entryIndex,
            // insert it into path to simplify the reblance loop
            path[pathSize++] = CreateEntry <T>(collection, insertKey);

            // we initialize i to pathSize-2 because we don't
            // have to re-balance or check the node we just
            // inserted, this also simplifies the
            var i = pathSize - 2;
            var b = 0;

            // go back up path
            for (; i >= 0; --i)
            {
                var entry = GetEntry(collection, path[i]);

                // patch up left/right entry from previous path step
                if (side[i])
                {
                    entry->Left     = path[i + 1];
                    entry->Balance += 1;
                }
                else
                {
                    entry->Right    = path[i + 1];
                    entry->Balance -= 1;
                }

                // if we hit a 0 balance, we *MUST* stop
                // or otherwise we'll adjust balances
                // incorrectly on the way back up
                if (entry->Balance == 0)
                {
                    break;
                }

                // case 1 : left heavy
                if (entry->Balance == 2)
                {
                    // case 1-1 : left subtree is also left heavy
                    var left = GetEntry(collection, entry->Left);
                    if (left->Balance == 1)
                    {
                        path[i] = RotateRight(collection, path[i], &b);
                    }

                    // case 1-2 : left subtree is right heavy
                    else
                    {
                        path[i] = RotateLeftRight(collection, path[i]);
                    }

                    break;
                }

                // case 2 : right heavy
                else if (entry->Balance == -2)
                {
                    // case 2-1 : right subtree is also right heayv
                    var right = GetEntry(collection, entry->Right);
                    if (right->Balance == -1)
                    {
                        path[i] = RotateLeft(collection, path[i], &b);
                    }

                    // case 2-2 : right subtree is left heavy
                    else
                    {
                        path[i] = RotateRightLeft(collection, path[i]);
                    }

                    break;
                }
            }

            // this is needed to patch up rotation result
            // to it's parent, since we break out of the loop
            if (--i >= 0)
            {
                var entry = GetEntry(collection, path[i]);
                if (side[i])
                {
                    entry->Left = path[i + 1];
                }
                else
                {
                    entry->Right = path[i + 1];
                }
            }

            // path 0 is our root
            return(path[0]);
        }
コード例 #25
0
        static int DeletePerform <T>(UnsafeOrderedCollection *collection, T deleteKey) where T : unmanaged, IComparable <T>
        {
            const int L = -1;
            const int R = +1;

            int *  path = stackalloc int[MAX_DEPTH];
            sbyte *side = stackalloc sbyte[MAX_DEPTH];

            int entryIndex = collection->Root;
            int pathSize   = 0;

            while (entryIndex != 0 && pathSize < (MAX_DEPTH - 1))
            {
                var entry    = GetEntry(collection, entryIndex);
                var entryKey = GetKey <T>(collection, entryIndex);

                var compare = deleteKey.CompareTo(entryKey);
                if (compare < 0)
                {
                    path[pathSize] = entryIndex;
                    side[pathSize] = L;
                    entryIndex     = entry->Left;
                    ++pathSize;
                }
                else if (compare > 0)
                {
                    path[pathSize] = entryIndex;
                    side[pathSize] = R;
                    entryIndex     = entry->Right;
                    ++pathSize;
                }
                else
                {
                    path[pathSize] = 0;
                    side[pathSize] = 0;
                    ++pathSize;
                    break;
                }
            }

            // could not find entry
            if (entryIndex == 0)
            {
                return(collection->Root);
            }

            // so... delete handling is annoying as hell, there's a
            // lot of optimizations that can be done here,
            // tried to get as many in as I could come up with

            int entryLeft;
            int entryRight;
            int entryBalance;
            int entryPathIndex = pathSize - 1;

            {
                var entry = GetEntry(collection, entryIndex);
                entryLeft    = entry->Left;
                entryRight   = entry->Right;
                entryBalance = entry->Balance;
                FreeEntry(collection, entryIndex);
            }

            // case 1 : no children
            if (entryLeft + entryRight == 0)
            {
                // we are at root with no children, special case
                if (pathSize == 1)
                {
                    return(0);
                }
            }

            // case 2 : only left child
            if (entryRight == 0)
            {
                path[entryPathIndex] = entryLeft;
            }

            // case 3 : only right child
            else if (entryLeft == 0)
            {
                path[entryPathIndex] = entryRight;
            }

            // case 4 : left and right child
            else
            {
                var successorParentFound = false;
                var successorIndex       = entryRight;
                var successor            = GetEntry(collection, successorIndex);

                // find in-order successor, which is
                // the left most entry of the right subtree
                while (successor->Left != 0)
                {
                    // keep going down the left of entrys right subtree
                    path[pathSize] = successorIndex;
                    side[pathSize] = L;
                    ++pathSize;

                    successorParentFound = true;
                    successorIndex       = successor->Left;
                    successor            = GetEntry(collection, successorIndex);
                }

                // replace entry with successor, we went down
                // right sub-tree to find it so side is R
                path[entryPathIndex] = successorIndex;
                side[entryPathIndex] = R;

                // successor gets balance and left tree from entry,
                // we need to explicitly assign the left tree
                // because it will not be done when go back
                // and patch up the tree while we balance it
                successor->Left    = entryLeft;
                successor->Balance = entryBalance;

                // successor was not the direct right entry,
                // so needed to go down left path of right
                // sub-tree, we need to insert successor's old right
                // to attach it to the parent of the successor
                if (successorParentFound)
                {
                    path[pathSize] = successor->Right;
                    side[pathSize] = 0;
                    ++pathSize;
                }
            }

            int i = pathSize - 1;
            int m = i;
            var b = 0;

            if (path[i] == 0)
            {
                --i;
            }

            for (; i >= 0; --i)
            {
                var e = GetEntry(collection, path[i]);

                if (i < m)
                {
                    switch (side[i])
                    {
                    case L:
                        e->Left = path[i + 1];
                        break;

                    case R:
                        e->Right = path[i + 1];
                        break;
                    }
                }

                e->Balance += side[i];

                // case 1: left heavy
                if (e->Balance == 2)
                {
                    // case 1-1 : left left, entry is left heavy and it's
                    // left subtree is neutral or left heavy
                    var left = GetEntry(collection, e->Left);
                    if (left->Balance >= 0)
                    {
                        path[i] = RotateRight(collection, path[i], &b);

                        if (b == -1)
                        {
                            break;
                        }
                    }
                    // case 1-2: left right, entry is left heavy, and it's
                    // left tree is right heavy
                    else
                    {
                        path[i] = RotateLeftRight(collection, path[i]);
                    }
                }

                // case 2: right heavy
                else if (e->Balance == -2)
                {
                    // case 2-1: right right, entry is right heavy and it's
                    // right subtree is neutral or right heavy
                    var right = GetEntry(collection, e->Right);
                    if (right->Balance <= 0)
                    {
                        path[i] = RotateLeft(collection, path[i], &b);

                        if (b == 1)
                        {
                            break;
                        }
                    }
                    // case 2-2: right left, entry is right heavy and it's
                    // right subtree is left heavy
                    else
                    {
                        path[i] = RotateRightLeft(collection, path[i]);
                    }
                }
                else if (e->Balance != 0)
                {
                    break;
                }
            }

            // patch up all paths all the way to root
            for (--i; i >= 0; --i)
            {
                var e = GetEntry(collection, path[i]);
                switch (side[i])
                {
                case L:
                    e->Left = path[i + 1];
                    break;

                case R:
                    e->Right = path[i + 1];
                    break;
                }
            }

            return(path[0]);
        }
コード例 #26
0
 public static int Count(UnsafeOrderedCollection *collection)
 {
     return(collection->UsedCount - collection->FreeCount);
 }
コード例 #27
0
 public static int Height(UnsafeOrderedCollection *collection)
 {
     return(Height(collection, collection->Root));
 }
コード例 #28
0
 public static void Remove <T>(UnsafeOrderedCollection *collection, T key) where T : unmanaged, IComparable <T>
 {
     collection->Root = DeletePerform <T>(collection, key);
 }