internal static RopeNode <T> Concat(RopeNode <T> left, RopeNode <T> right) { if (left.length == 0) { return(right); } if (right.length == 0) { return(left); } if (left.length + right.length <= NodeSize) { left = left.CloneIfShared(); // left is guaranteed to be leaf node after cloning: // - it cannot be function node (due to clone) // - it cannot be concat node (too short) right.CopyTo(0, left.contents, left.length, right.length); left.length += right.length; return(left); } var concatNode = new RopeNode <T>(); concatNode.left = left; concatNode.right = right; concatNode.length = left.length + right.length; concatNode.Rebalance(); return(concatNode); }
/// <summary> /// Balances this node and recomputes the 'height' field. /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value. /// </summary> internal void Rebalance() { // Rebalance() shouldn't be called on shared nodes - it's only called after modifications! Debug.Assert(!isShared); // leaf nodes are always balanced (we don't use 'height' to detect leaf nodes here // because Balance is supposed to recompute the height). if (left == null) { return; } // ensure we didn't miss a MergeIfPossible step Debug.Assert(length > NodeSize); // We need to loop until it's balanced. Rotations might cause two small leaves to combine to a larger one, // which changes the height and might mean we need additional balancing steps. while (Math.Abs(Balance) > 1) { // AVL balancing // note: because we don't care about the identity of concat nodes, this works a little different than usual // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged if (Balance > 1) { if (right.Balance < 0) { right = right.CloneIfShared(); right.RotateRight(); } RotateLeft(); // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that. left.Rebalance(); } else if (Balance < -1) { if (left.Balance > 0) { left = left.CloneIfShared(); left.RotateLeft(); } RotateRight(); // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that. right.Rebalance(); } } Debug.Assert(Math.Abs(Balance) <= 1); height = (byte)(1 + Math.Max(left.height, right.height)); }