unsafe int ReifyStagingNode(int parent, int indexInParent, Node *stagingNodes, int stagingNodeIndex,
                                    ref QuickList <int> subtrees, ref QuickList <int> treeletInternalNodes, ref int nextInternalNodeIndexToUse, ref QuickList <int> spareNodes, out bool nodesInvalidated)
        {
            nodesInvalidated = false;
            int internalNodeIndex;

            if (nextInternalNodeIndexToUse < treeletInternalNodes.Count)
            {
                //There is an internal node that we can use.
                //Note that we remove from the end to guarantee that the treelet root does not change location.
                //The CollectSubtrees function guarantees that the treelet root is enqueued first.
                internalNodeIndex = treeletInternalNodes.Elements[nextInternalNodeIndexToUse++];
            }
            else if (!spareNodes.TryPop(out internalNodeIndex))
            {
                //There was no pre-existing internal node that we could use. Apparently, the tree gained some internal nodes.
                //Watch out, calls to AllocateNode potentially invalidate all extant node pointers.
                //Fortunately, there are no pointers to invalidate... here. Propagate it up.
                //Note that this is a locking operation to support multithreading. In practice, n-ary trees should hit this very rarely.
                //In binary trees, this will never get hit at all.
                bool addCausedInvalidation;
                internalNodeIndex = AllocateNodeLocking(out addCausedInvalidation);
                if (addCausedInvalidation)
                {
                    nodesInvalidated = true;
                }
            }

            //To make the staging node real, it requires an accurate parent pointer, index in parent, and child indices.
            //Copy the staging node into the real tree.
            //We take the staging node's child bounds, child indices, leaf counts, and child count.
            //The parent and index in parent are provided by the caller.
            var stagingNode  = stagingNodes + stagingNodeIndex;
            var internalNode = nodes + internalNodeIndex;

            *internalNode = *stagingNode;
            internalNode->RefineFlag    = 0; //The staging node could have contained arbitrary refine flag data.
            internalNode->Parent        = parent;
            internalNode->IndexInParent = indexInParent;


            ReifyChildren(internalNodeIndex, stagingNodes, ref subtrees, ref treeletInternalNodes, ref nextInternalNodeIndexToUse, ref spareNodes, out nodesInvalidated);
            return(internalNodeIndex);
        }
        public void RemoveUnusedInternalNodes(ref QuickList <int> spareNodes)
        {
            if (spareNodes.Count > 0)
            {
                lock (nodeLocker)
                {
                    //Locked to protect against multiple threads dumping at once, and against other thread simultaneously running AllocateNodeLocking.
                    //Both of these can occur during multithreaded refinements.
                    //Since this only executes once, the cost doesn't really matter. And on binary trees, it will never execute.

                    //There were some spare internal nodes left. Apparently, the tree was compressed a little bit.
                    //Remove them from the real tree.
                    //Remove highest to lowest to avoid any situation where removing could invalidate an index.
                    Array.Sort(spareNodes.Elements, 0, spareNodes.Count);
                    int nodeIndex;
                    while (spareNodes.TryPop(out nodeIndex))
                    {
                        RemoveNodeAt(nodeIndex);
                    }
                }
            }
        }
        unsafe int ReifyStagingNode(int parent, int indexInParent, Node* stagingNodes, int stagingNodeIndex,
            ref QuickList<int> subtrees, ref QuickList<int> treeletInternalNodes, ref int nextInternalNodeIndexToUse, ref QuickList<int> spareNodes, out bool nodesInvalidated)
        {
            nodesInvalidated = false;
            int internalNodeIndex;
            if (nextInternalNodeIndexToUse < treeletInternalNodes.Count)
            {
                //There is an internal node that we can use.
                //Note that we remove from the end to guarantee that the treelet root does not change location.
                //The CollectSubtrees function guarantees that the treelet root is enqueued first.
                internalNodeIndex = treeletInternalNodes.Elements[nextInternalNodeIndexToUse++];
            }
            else if (!spareNodes.TryPop(out internalNodeIndex))
            {
                //There was no pre-existing internal node that we could use. Apparently, the tree gained some internal nodes.
                //Watch out, calls to AllocateNode potentially invalidate all extant node pointers.
                //Fortunately, there are no pointers to invalidate... here. Propagate it up.
                //Note that this is a locking operation to support multithreading. In practice, n-ary trees should hit this very rarely.
                //In binary trees, this will never get hit at all.
                bool addCausedInvalidation;
                internalNodeIndex = AllocateNodeLocking(out addCausedInvalidation);
                if (addCausedInvalidation)
                    nodesInvalidated = true;
            }

            //To make the staging node real, it requires an accurate parent pointer, index in parent, and child indices.
            //Copy the staging node into the real tree.
            //We take the staging node's child bounds, child indices, leaf counts, and child count.
            //The parent and index in parent are provided by the caller.
            var stagingNode = stagingNodes + stagingNodeIndex;
            var internalNode = nodes + internalNodeIndex;
            *internalNode = *stagingNode;
            internalNode->RefineFlag = 0; //The staging node could have contained arbitrary refine flag data.
            internalNode->Parent = parent;
            internalNode->IndexInParent = indexInParent;

            ReifyChildren(internalNodeIndex, stagingNodes, ref subtrees, ref treeletInternalNodes, ref nextInternalNodeIndexToUse, ref spareNodes, out nodesInvalidated);
            return internalNodeIndex;
        }
        public void RemoveUnusedInternalNodes(ref QuickList<int> spareNodes)
        {
            if (spareNodes.Count > 0)
            {
                lock (nodeLocker)
                {
                    //Locked to protect against multiple threads dumping at once, and against other thread simultaneously running AllocateNodeLocking.
                    //Both of these can occur during multithreaded refinements.
                    //Since this only executes once, the cost doesn't really matter. And on binary trees, it will never execute.

                    //There were some spare internal nodes left. Apparently, the tree was compressed a little bit.
                    //Remove them from the real tree.
                    //Remove highest to lowest to avoid any situation where removing could invalidate an index.
                    Array.Sort(spareNodes.Elements, 0, spareNodes.Count);
                    int nodeIndex;
                    while (spareNodes.TryPop(out nodeIndex))
                    {
                        RemoveNodeAt(nodeIndex);
                    }
                }
            }
        }