/* -------------------------------------------------------------------
        *  FUNCTION: Insert
        *
        *  Insert string s at index i.
        *
        *  Time Complexity: O(logN)
        *  ------------------------------------------------------------------- */
        public void Insert(string s, int i)
        {
            Rope S = new Rope(s);

            if (i >= TotalLength) // add s to end
            {
                Concatenate(S);
            }
            else if (i <= 0) // add s to beginning
            {
                S.Concatenate(this);
                root = S.root;
            }
            else // split rope
            {
                Rope R2 = Split(i);
                Concatenate(S);
                Concatenate(R2);
            }
        }
        /* -------------------------------------------------------------------
        *  FUNCTION: Split
        *
        *  Splits a rope into two at index i, returns second rope.
        *
        *  Time Complexity: O(logN)
        *  ------------------------------------------------------------------- */
        public Rope Split(int i)
        {
            if (i == 0) // no split, return copy of original rope
            {
                return(this);
            }

            Rope R2    = new Rope("");  // rope to be returned
            Rope Rtemp = new Rope("");  // temp - used for concatenation to R2
            Node p     = NodeAt(ref i); // find leaf node containing i
            Node cur;                   // used to traverse up tree

            if (p != null)
            {
                if (i > 0) // i is not at start of node
                {
                    // split p into two nodes
                    p.left = new Node()
                    {
                        value  = p.value.Substring(0, i),
                        parent = p,
                        length = p.value.Substring(0, i).Length
                    };
                    p.right = new Node()
                    {
                        value  = p.value.Substring(i),
                        parent = p,
                        length = p.value.Substring(i).Length
                    };
                    p.value  = null;
                    p.length = p.left.length;
                    p        = p.right; // set p to right child
                }

                // move up tree until you're at a right child or root
                while (p != root && p == p.parent.left)
                {
                    p = p.parent;
                }

                cur       = p.parent; // keep track of parent node
                p.parent  = null;     // remove link to parent
                R2.root   = p;        // set p to root of new rope
                cur.right = null;     // cut off node p

                // move up tree until you have a new right child
                while (cur != root && cur == cur.parent.right)
                {
                    cur = cur.parent;
                }
                cur = cur.parent;

                // continue cutting off right children
                while (cur != null && cur.right != null)
                {
                    cur.right.parent = null;
                    Rtemp.root       = cur.right;
                    R2.Concatenate(Rtemp);
                    cur.right = null;

                    // stop loop at right child of root or at root
                    if (cur != root.right && cur != root)
                    {
                        // find new right child
                        while (cur != root && cur == cur.parent.right)
                        {
                            cur = cur.parent;
                        }
                        if (cur == root)
                        {
                            cur = null;
                        }
                    }
                }
                ReBalance(); // Rebalance the tree starting from root
            }
            return(R2);
        }