internal BPlusTreeNode(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent)
 {
     Tree     = tree;
     Keywords = new List <TKey>();
     Parent   = parent;
     IsLeaf   = false;
 }
        public BPlusTreeIndexNode <TKey, TValue> PromoteToIndexNode()
        {
            BPlusTreeIndexNode <TKey, TValue> indexNode = null;

            try {
                BPlusTree <TKey, TValue> .LogInfo("Begin promoting leaf node to index node: " + this.ToString());

                indexNode          = new BPlusTreeIndexNode <TKey, TValue>(this.Tree, this.Parent, null);
                indexNode.Location = this.Location;
                indexNode.SlotSize = this.SlotSize;
            } finally {
                BPlusTree <TKey, TValue> .LogInfo("End promoting leaf node to index node: " + this.ToString());
            }
            return(indexNode);
        }
        protected internal BPlusTreeIndexNode(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, BinaryReader rdr)
            : base(tree, parent)
        {
            IsLeaf         = false;
            ChildLocations = new List <long>();

            if (rdr != null)
            {
                // index nodes store only children locations (which are always longs)
                short childCount = rdr.ReadInt16();
                for (int i = 0; i < childCount; i++)
                {
                    ChildLocations.Add(rdr.ReadInt64());
                }
            }
        }
        private static BPlusTreeNode <TKey, TValue> processRead(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, BinaryReader rdr, object[] args)
        {
            // stream has been positioned appropriately.
            // just read from it.

            BPlusTreeNode <TKey, TValue> node = null;

            tree.Lock(delegate() {
                long startLocation = rdr.BaseStream.Position;
                int slotSize       = rdr.ReadInt32();
                bool leaf          = rdr.ReadBoolean();
                short keywordCount = rdr.ReadInt16();

                if (leaf)
                {
                    node = new BPlusTreeLeafNode <TKey, TValue>(tree, parent, rdr);
                }
                else
                {
                    node = new BPlusTreeIndexNode <TKey, TValue>(tree, parent, rdr);
                }
                node.Location = startLocation;

                // now we must read in all the keywords (they're always at the end of the stream)
                for (short i = 0; i < keywordCount; i++)
                {
                    TKey keyManager = new TKey();
                    keyManager.Read(rdr);
                    node.Keywords.Add(keyManager);
                }

                node.SlotSize = slotSize;

                // orient the stream pointer to the end of the node (which may be past the current data in it)
                rdr.BaseStream.Position = startLocation + node.SlotSize;
            });

            return(node);
        }
        protected internal BPlusTreeLeafNode(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, BinaryReader rdr)
            : base(tree, parent)
        {
            IsLeaf = true;
//			LeftSiblingLocation = -1;
            RightSiblingLocation = -1;
            Values = new List <TValue>();

            // leaves store only sibling locations and values (keywords are handled by the base, since they're in every kind of node)
            if (rdr != null)
            {
//				LeftSiblingLocation = rdr.ReadInt64();
                RightSiblingLocation = rdr.ReadInt64();
                short valueCount = rdr.ReadInt16();

                for (int i = 0; i < valueCount; i++)
                {
                    TValue val = new TValue();
                    val.Read(rdr);
                    Values.Add(val);
                }
            }
        }
        internal BPlusTreeNode <TKey, TValue> SplitChild(BPlusTreeNode <TKey, TValue> originalChild, out int keywordIndex)
        {
            // Split the current node into two nodes:
            //   a. Left node contains all pointers/keywords to the left of the median
            //   b. Right node contains all pointers/keywords to the right of the median
            //   c. Parent node gets median keyword pushed into it
            //
            // Disk-wise: the originalChild is always written back to its original position (since we're splitting it, it's guaranteed to get smaller)
            //            the newChild is placed into either an available abandoned node or tacked on the end of the file.
            //            the parent node is written back to its original position IF IT FITS.  Since we're coyping a keyword
            //               up into the parent node, it may cause it to outgrow its allocated size in the file.  If this occurs,
            //               the call to this.Write() implicitly calls relocate().  relocate() will then either write the parent to
            //               an abandoned node or tack it onto the end of the file.



            // first, determine the offset of the current originalchild in this (the parent)
            keywordIndex = 0;

            try {
                BPlusTree <TKey, TValue> .LogInfo("Begin splitting node=" + this.ToString() + ", child=" + originalChild.ToString());

                if (ChildLocations.Count > 0)
                {
                    // TODO: troubleshoot this -- after several thousand splits, originalChild.Location is no longer found in the ChildLocations array!!!
                    //if (LastChildLocation == originalChild.Location) {
                    //    // rightmost child, on a full node.
                    //} else {
                    while (keywordIndex < ChildLocations.Count && ChildLocations[keywordIndex] != originalChild.Location)
                    {
                        keywordIndex++;
                    }
                    //}
                }

                // assign the median keyword to the parent's Keywords list (inserts just before the current one)
                //if (keywordIndex > this.Keywords.Count) {
                //    // rightmost keyword, add to the end of the list
                //    this.Keywords.Add(originalChild.MedianKeyword);
                //} else {
                // not rightmost keyword, insert it at the proper position
                this.Keywords.Insert(keywordIndex, originalChild.MedianKeyword);
                //}

                if (originalChild.IsLeaf)
                {
                    // since this is a leaf, we will copy up the median keyword to the parent (as opposed to push up for index nodes)

                    // create a leaf node
                    BPlusTreeLeafNode <TKey, TValue> origNode = (BPlusTreeLeafNode <TKey, TValue>)originalChild;
                    BPlusTreeLeafNode <TKey, TValue> newNode  = new BPlusTreeLeafNode <TKey, TValue>(this.Tree, (BPlusTreeIndexNode <TKey, TValue>) this, null);

                    // copy right half of keywords + values from originalChild to newChild
                    newNode.Keywords.AddRange(origNode.Keywords.Skip(this.Tree.Median - 1));
                    newNode.Values.AddRange(origNode.Values.Skip(this.Tree.Median - 1));

                    // remove right half of keywords + values from originalChild
                    origNode.Keywords = origNode.Keywords.Take(this.Tree.Median - 1).ToList();
                    origNode.Values   = origNode.Values.Take(this.Tree.Median - 1).ToList();

                    var nextKeywordIndex = keywordIndex + 1;
                    Tree.Lock(delegate() {
                        // write out nodes so sibling locations can be set properly

                        // note: orig node should never relocate on write since we're making it smaller (moving 1/2 of its keywords to a new node)
                        if (origNode.Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("orig leaf node relocated during split child: " + origNode.ToString());
                        }
                        if (newNode.Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("new leaf node relocated during split child: " + newNode.ToString());
                        }

                        // adjust sibling locations (we always add the new child to the right of the original one)
                        newNode.RightSiblingLocation = origNode.RightSiblingLocation;
                        //				newNode.LeftSiblingLocation = origNode.Location;

                        origNode.RightSiblingLocation = newNode.Location;

                        ChildLocations.Insert(nextKeywordIndex, newNode.Location);

                        // write them out again so we save sibling locations
                        if (origNode.Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("orig leaf node relocated on 2nd write during split child: " + origNode.ToString());
                        }
                        if (newNode.Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("new leaf node relocated on 2nd write during split child: " + newNode.ToString());
                        }

                        // and write out the parent since it changed as well
                        if (Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("parent of leaf node relocated during split child: " + this.ToString());
                        }
                    });

                    return(newNode);
                }
                else
                {
                    // since this is an index node, we push up the keyword to the parent (as opposed to copy up for leaves)
                    // this means the median keyword will be removed from the original child but not moved to the new child

                    // create an index node
                    BPlusTreeIndexNode <TKey, TValue> origNode = (BPlusTreeIndexNode <TKey, TValue>)originalChild;
                    BPlusTreeIndexNode <TKey, TValue> newNode  = new BPlusTreeIndexNode <TKey, TValue>(this.Tree, (BPlusTreeIndexNode <TKey, TValue>) this, null);

                    // copy right half of keywords + childlocations from originalChild to newChild
                    // (since this is an index node, skip the median keyword
                    newNode.Keywords.AddRange(origNode.Keywords.Skip(this.Tree.Median));
                    newNode.ChildLocations.AddRange(origNode.ChildLocations.Skip(this.Tree.Median));

                    // remove right half of keywords + childlocations from originalChild
                    // (note we stop one shy of the median keyword -- this is because the median keyword needs to be "pushed up"
                    //  on an index node split.  However, we don't drop the corresponding ChildLocation)
                    origNode.Keywords       = origNode.Keywords.Take(this.Tree.Median - 1).ToList();
                    origNode.ChildLocations = origNode.ChildLocations.Take(this.Tree.Median).ToList();

                    var ki = keywordIndex;
                    Tree.Lock(delegate() {
                        if (origNode.Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("orig index node relocated during split child: " + origNode.ToString());
                        }
                        if (newNode.Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("new index node relocated during split child: " + newNode.ToString());
                        }

                        // original node may have relocated on write...
                        ChildLocations[ki] = origNode.Location;

                        ChildLocations.Insert(ki + 1, newNode.Location);

                        // and write out the parent since it changed as well
                        if (Write())
                        {
                            BPlusTree <TKey, TValue> .LogInfo("parent of index node relocated during split child: " + this.ToString());
                        }
                    });

                    return(newNode);
                }
            } finally {
                BPlusTree <TKey, TValue> .LogInfo("End splitting node=" + this.ToString() + ", child=" + originalChild.ToString());
            }
        }
        protected BPlusTreeNode <TKey, TValue> GetRightSibling(bool mustHaveSameParent)
        {
            if (this.IsLeaf)
            {
                // use the rightsibling pointer
                BPlusTreeLeafNode <TKey, TValue> leaf = this as BPlusTreeLeafNode <TKey, TValue>;
                return(BPlusTreeNode <TKey, TValue> .Read(this.Tree, this.Parent, leaf.RightSiblingLocation));
            }
            else
            {
                // look in parent for our smallest keyword, grab childlocation to the left of it
                int parentChildCount;
                int childLocationIndex = GetIndexInParent(out parentChildCount);

                if (childLocationIndex == -1)
                {
                    // never found the index, or no parent (aka root). No sibling to return.
                    return(null);
                }
                else
                {
                    if (childLocationIndex < parentChildCount - 1)
                    {
                        // we know the right sibling is attached to the same parent.
                        BPlusTreeNode <TKey, TValue> node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, this.Parent, Parent.ChildLocations[childLocationIndex + 1]);

                        return(node);
                    }
                    else
                    {
                        // we are the rightmost child, as we're at the location that's 1 past the fanout size
                        if (mustHaveSameParent)
                        {
                            // caller wants the sibling only if it's from the same parent, and it's not.
                            return(null);
                        }
                        else
                        {
                            // we have to find the common ancestor between this and its right sibling.
                            // spin up until we find the first node whose child index is <= Fanoutsize
                            // then drill down the leftmost path until we're at the same depth as we started at
                            int i = 0;
                            int nodeChildLocationIndex        = -1;
                            BPlusTreeNode <TKey, TValue> node = this;
                            while (node.Parent != null)
                            {
                                i++;

                                // keep bubbling up until we're at a node whose parent has at least one child to our right.
                                nodeChildLocationIndex = node.GetIndexInParent(out parentChildCount);
                                if (nodeChildLocationIndex < parentChildCount - 1)
                                {
                                    // this node is not the rightmost child of its parent.
                                    // we're done bubbling (node contains the ancestor node, nodeChildLocationIndex can tell us the subtree to search down)
                                    break;
                                }
                                else
                                {
                                    node = node.Parent;
                                }
                            }

                            if (nodeChildLocationIndex == parentChildCount - 1)
                            {
                                // the only common ancestor has our node being the rightmost node in the tree (at this depth).
                                return(null);
                            }


                            // ok, node contains the ancestor somehwere up the tree that has the node we're looking for as its
                            // leftmost descendent at the same depth.
                            // So we just keep drilling down the leftmost path until we're at the same depth.
                            node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, node as BPlusTreeIndexNode <TKey, TValue>, nodeChildLocationIndex + 1);

                            while (i > 0)
                            {
                                BPlusTreeIndexNode <TKey, TValue> indexNode = node as BPlusTreeIndexNode <TKey, TValue>;
                                node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, indexNode, indexNode.ChildLocations[0]);

                                i--;
                            }

                            // node now contains the right sibling!
                            return(node);
                        }
                    }
                }



                //// bounce up to the parent
                //if (Parent != null) {
                //    TKey ourKey = FirstKeyword;
                //    for (int i=0; i < Parent.Keywords.Count; i++) {
                //        TKey parentKeyword = Parent.Keywords[i];
                //        if (parentKeyword.CompareTo(ourKey, KeywordMatchMode.ExactMatch, false) == 0) {
                //            // follow right child
                //            BPlusTreeNode<TKey, TValue> node = BPlusTreeNode<TKey, TValue>.Read(this.Tree, this.Parent, Parent.ChildLocations[i+1]);
                //            return node;
                //        }
                //    }
                //}
                //return null;
            }
        }
        protected BPlusTreeNode <TKey, TValue> GetLeftSibling(bool mustHaveSameParent)
        {
            // look in parent for our smallest keyword, grab childlocation to the left of it

            int parentChildCount;
            int childLocationIndex = GetIndexInParent(out parentChildCount);

            if (childLocationIndex == -1)
            {
                // never found the index, or no parent (aka root). No sibling to return.
                return(null);
            }
            else
            {
                if (childLocationIndex == 0)
                {
                    if (mustHaveSameParent)
                    {
                        // caller wants the sibling only if it's from the same parent, and it's not.
                        return(null);
                    }
                    else
                    {
                        // we have to find the parent of our left sibling.
                        // spin up until we find the first node whose child index is > 0
                        // then drill down the rightmost path until we're at the same depth as we started at
                        int i = 0;
                        int nodeChildLocationIndex        = -1;
                        BPlusTreeNode <TKey, TValue> node = this;
                        while (node.Parent != null)
                        {
                            i++;

                            // keep bubbling up until we're at a node whose parent has at least one child to our left.
                            nodeChildLocationIndex = node.GetIndexInParent(out parentChildCount);
                            if (nodeChildLocationIndex > 0)
                            {
                                // this node is not the leftmost child of its parent.
                                // we're done bubbling (node contains the ancestor node, nodeChildLocationIndex can tell us the subtree to search down)
                                break;
                            }
                            else
                            {
                                node = node.Parent;
                            }
                        }

                        if (nodeChildLocationIndex == 0)
                        {
                            // the only common ancestor has our node being the leftmost node in the tree (at this depth).
                            return(null);
                        }

                        // ok, node contains the ancestor somehwere up the tree that has the node we're looking for as its
                        // rightmost descendent at the same depth.
                        // So we just keep drilling down until we're at the same depth.
                        node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, node as BPlusTreeIndexNode <TKey, TValue>, nodeChildLocationIndex - 1);

                        while (i > 0)
                        {
                            BPlusTreeIndexNode <TKey, TValue> indexNode = node as BPlusTreeIndexNode <TKey, TValue>;
                            node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, indexNode, indexNode.LastChildLocation);

                            i--;
                        }

                        // node now contains the left sibling!
                        return(node);
                    }
                }
                else
                {
                    // we know the left sibling is attached to the same parent.
                    BPlusTreeNode <TKey, TValue> node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, this.Parent, Parent.ChildLocations[childLocationIndex - 1]);

                    return(node);
                }
            }
        }
        /// <summary>
        /// Factory method for reading data from the tree's stream and returning either a BPlusTreeIndexNode or BPlusTreeLeafNode object
        /// </summary>
        /// <param name="tree"></param>
        /// <param name="location"></param>
        /// <returns></returns>
        internal static BPlusTreeNode <TKey, TValue> Read(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, long location)
        {
            if (location < 1)
            {
                return(null);
            }

            BPlusTreeNode <TKey, TValue> node = null;

            if (!tree.NodeCache.TryGetValue(location, out node))
            {
                node = tree.ReadNodeFromDisk(parent, processRead, location, null);
            }
            return(node);
        }