internal bool Insert(string keyword, long dataByteOffset, BPlusTreeNode parentNode)
        {
            //i <- n[x]
            //if leaf[x]
            //     then while i >= 1 and k < keyi[x]
            //            do keyi+1[x] <- keyi[x]
            //               i <- i - 1
            //          keyi+1[x] <- k
            //          n[x] <- n[x] + 1
            //          Disk-Write(x)
            //     else while i >= and k < keyi[x]
            //            do i <- i - 1
            //          i <- i + 1
            //          Disk-Read(ci[x])
            //          if n[ci[x]] = 2t - 1
            //               then B-Tree-Split-Child(x, i, ci[x])
            //                    if k > keyi[x]
            //                       then i <- i + 1
            //          B-Tree-Insert-Nonfull(ci[x], k)

            int i = KeywordCount - 1;

            if (IsLeaf)
            {
                while (i > -1 && String.Compare(keyword, Keywords[i], false) < 0)
                {
                    Keywords[i + 1]            = Keywords[i];
                    ChildrenByteOffsets[i + 1] = ChildrenByteOffsets[i];
                    i--;
                }
                Keywords[i + 1]            = keyword;
                ChildrenByteOffsets[i + 1] = dataByteOffset;
                KeywordCount++;
                return(Write(parentNode));
            }
            else
            {
                while (i > -1 && String.Compare(keyword, Keywords[i], false) < 0)
                {
                    i--;
                }
                i++;
                BPlusTreeNode child = BPlusTreeNode.Read(_tree, ChildrenByteOffsets[i], true);

                _tree.Log(("Inspecting node @ " + child.FileOffset + " -> ").PadRight(50) + child.ToString());
                if (!child.IsFull)
                {
                    return(child.Insert(keyword, dataByteOffset, this));
                }
                else
                {
                    // must first split the child
                    BPlusTreeNode newChild = SplitChild(i, child, parentNode);
                    if (String.Compare(keyword, newChild.Keywords[0], false) < 0)
                    {
                        // new keyword belongs somewhere below the original child
                        return(child.Insert(keyword, dataByteOffset, this));
                    }
                    else
                    {
                        // new keyword belongs somewhere below the new child
                        return(newChild.Insert(keyword, dataByteOffset, this));
                    }
                }
            }
        }
        // here's the vernacular:
        // The _keywords array represents all the keywords in this node.
        // The _byteRanges array represents all the file offsets in this node for either (a) a keyword or (b) the data itself.
        //   a. If it is located in an index node (i.e. non-Leaf), the byte range represents the start/end offsets in the index file (used for traversing to other nodes)
        //   b. If it is located in a Leaf node, the byte range represents the start/end offsets in the data file (for looking up associated hit(s))


        // Think ByteRange = pointer to next node or data, Keyword = item we're searching by
        // So we always have 1 more pointer (or byterange, BR) than search item (or keyword, K):
        //
        // |-------- Node ---------|
        // |      go       pi      |
        // |  /   |    |   |    \  |
        // | BR1  K1  BR2  K2  BR3 |
        // |-----------------------|


        /// <summary>
        /// Creates a new root node for the tree and makes the original root the first child of the new root.
        /// </summary>
        /// <param name="tree"></param>
        /// <returns></returns>
        internal static BPlusTreeNode ReplaceRoot(BPlusTree tree, BPlusTreeNode originalRoot, string keyword, long dataByteOffset)
        {
            // NOTE: This is a very special case.
            // replacing the root involves creating 2 new nodes:
            //  1. the new root node
            //  2. splitting the existing root node into 2 nodes, one of which is a new one

            BPlusTreeNode newRoot = null;

            // create a new root node, mark it as not a leaf, put it in exact same spot as original root
            newRoot = new BPlusTreeNode {
                IsLeaf                 = false,
                _tree                  = tree,
                Keywords               = new string[tree.Fanout],
                ChildrenByteOffsets    = new long[tree.Fanout + 1],
                KeywordCount           = 0,
                FileOffset             = -1,
                LeftSiblingFileOffset  = -1,
                RightSiblingFileOffset = -1,
            };

            tree.Log("------------ BEGIN Replacing root ------------");
            tree.Log("BEGIN Replacing root. original node -> ".PadRight(50) + originalRoot.ToString());
            tree.Log("BEGIN Replacing root. new node -> ".PadRight(50) + newRoot.ToString());


            // add our new root to file, update the root node pointer
            BPlusAbandonedNode abandoned = tree.GetNextAvailableNodeLocation(originalRoot);

            newRoot.FileOffset = abandoned.FileOffset;
            newRoot.Write(null);
            tree.WriteRootNodeOffset(newRoot.FileOffset);


            // trickle-down insert the new keyword/data
            if (!String.IsNullOrEmpty(keyword))
            {
                // split the original
                BPlusTreeNode newChild = newRoot.SplitChild(0, originalRoot, null);

                tree.Log("CONTINUE Replacing root. new child node -> ".PadRight(50) + originalRoot.ToString());


                if (newRoot.Insert(keyword, dataByteOffset, null))
                {
                    // the new root was relocated during insertion (outgrew its allotted space)
                    // the insert should take care of it

                    // re-read it in just in case
                    newRoot = BPlusTreeNode.Read(tree, newRoot.FileOffset, true);
                }
            }
            else
            {
                newRoot = BPlusTreeNode.Read(tree, newRoot.FileOffset, true);
            }


            tree.Log("END Replacing root. original node -> ".PadRight(50) + originalRoot.ToString());
            //			_tree.Log("END Replacing root. new child node -> ".PadRight(50) + newChild.ToString());
            tree.Log("END Replacing root. new root -> ".PadRight(50) + newRoot.ToString());
            tree.Log("------------ END Replacing root ------------");

            return(newRoot);
        }