Commit 4314dea4 authored by Richard M. Stallman's avatar Richard M. Stallman
Browse files

(rotate_right, rotate_left): Simplify

total_length calculation.  Minimize pointer dereferencing.
(balance_an_interval): Remove recursive rebalancing.
Rebalance precisely when imbalanced.  If a rotation is done,
rebalance only the node which may have become unbalanced.
Iterate	until the current node is balanced.
(balance_possible_root_interval): New function.
(balance_intervals): Move the interation into rebalance_an_interval.
(balance_intervals_internal): New subroutine of balance_intervals.
(split_interval_right, split_interval_left): Speed up by
not checking LEAF_INTERVAL_P.
(split_interval_right, split_interval_left, find_interval,
adjust_intervals_for_insertion, graft_intervals_into_buffer):
Add dynamic rebalancing anywhere a node may become unbalanced.
(graft_intervals_into_buffer, copy_intervals): No longer
any need to do a full rebalance as the tree stays balanced.
parent 7ac78c9a
...@@ -284,34 +284,35 @@ rotate_right (interval) ...@@ -284,34 +284,35 @@ rotate_right (interval)
{ {
INTERVAL i; INTERVAL i;
INTERVAL B = interval->left; INTERVAL B = interval->left;
int len = LENGTH (interval); int old_total = interval->total_length;
/* Deal with any Parent of A; make it point to B. */ /* Deal with any Parent of A; make it point to B. */
if (! ROOT_INTERVAL_P (interval)) if (! ROOT_INTERVAL_P (interval))
if (AM_LEFT_CHILD (interval)) if (AM_LEFT_CHILD (interval))
interval->parent->left = interval->left; interval->parent->left = B;
else else
interval->parent->right = interval->left; interval->parent->right = B;
interval->left->parent = interval->parent; B->parent = interval->parent;
/* B gets the same length as A, since it get A's position in the tree. */ /* Make B the parent of A */
interval->left->total_length = interval->total_length; i = B->right;
B->right = interval;
/* B becomes the parent of A. */ interval->parent = B;
i = interval->left->right;
interval->left->right = interval;
interval->parent = interval->left;
/* A gets c as left child. */ /* Make A point to c */
interval->left = i; interval->left = i;
if (! NULL_INTERVAL_P (i)) if (! NULL_INTERVAL_P (i))
i->parent = interval; i->parent = interval;
interval->total_length = (len + LEFT_TOTAL_LENGTH (interval)
+ RIGHT_TOTAL_LENGTH (interval)); /* A's total length is decreased by the length of B and it's left child. */
interval->total_length -= B->total_length - LEFT_TOTAL_LENGTH (interval);
/* B must have the same total length of A. */
B->total_length = old_total;
return B; return B;
} }
/* Assuming that a right child exists, perform the following operation: /* Assuming that a right child exists, perform the following operation:
A B A B
...@@ -327,34 +328,121 @@ rotate_left (interval) ...@@ -327,34 +328,121 @@ rotate_left (interval)
{ {
INTERVAL i; INTERVAL i;
INTERVAL B = interval->right; INTERVAL B = interval->right;
int len = LENGTH (interval); int old_total = interval->total_length;
/* Deal with the parent of A. */ /* Deal with any parent of A; make it point to B. */
if (! ROOT_INTERVAL_P (interval)) if (! ROOT_INTERVAL_P (interval))
if (AM_LEFT_CHILD (interval)) if (AM_LEFT_CHILD (interval))
interval->parent->left = interval->right; interval->parent->left = B;
else else
interval->parent->right = interval->right; interval->parent->right = B;
interval->right->parent = interval->parent; B->parent = interval->parent;
/* B must have the same total length of A. */
interval->right->total_length = interval->total_length;
/* Make B the parent of A */ /* Make B the parent of A */
i = interval->right->left; i = B->left;
interval->right->left = interval; B->left = interval;
interval->parent = interval->right; interval->parent = B;
/* Make A point to c */ /* Make A point to c */
interval->right = i; interval->right = i;
if (! NULL_INTERVAL_P (i)) if (! NULL_INTERVAL_P (i))
i->parent = interval; i->parent = interval;
interval->total_length = (len + LEFT_TOTAL_LENGTH (interval)
+ RIGHT_TOTAL_LENGTH (interval)); /* A's total length is decreased by the length of B and it's right child. */
interval->total_length -= B->total_length - RIGHT_TOTAL_LENGTH (interval);
/* B must have the same total length of A. */
B->total_length = old_total;
return B; return B;
} }
/* Balance an interval tree with the assumption that the subtrees
themselves are already balanced. */
static INTERVAL
balance_an_interval (i)
INTERVAL i;
{
register int old_diff, new_diff;
while (1)
{
old_diff = LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i);
if (old_diff > 0)
{
new_diff = i->total_length - i->left->total_length
+ RIGHT_TOTAL_LENGTH (i->left) - LEFT_TOTAL_LENGTH (i->left);
if (abs (new_diff) >= old_diff)
break;
i = rotate_right (i);
balance_an_interval (i->right);
}
else if (old_diff < 0)
{
new_diff = i->total_length - i->right->total_length
+ LEFT_TOTAL_LENGTH (i->right) - RIGHT_TOTAL_LENGTH (i->right);
if (abs (new_diff) >= -old_diff)
break;
i = rotate_left (i);
balance_an_interval (i->left);
}
else
break;
}
return i;
}
/* Balance INTERVAL, potentially stuffing it back into its parent
Lisp Object. */
static INLINE INTERVAL
balance_possible_root_interval (interval)
register INTERVAL interval;
{
Lisp_Object parent;
if (interval->parent == NULL_INTERVAL)
return interval;
parent = (Lisp_Object) (interval->parent);
interval = balance_an_interval (interval);
if (XTYPE (parent) == Lisp_Buffer)
XBUFFER (parent)->intervals = interval;
else if (XTYPE (parent) == Lisp_String)
XSTRING (parent)->intervals = interval;
return interval;
}
/* Balance the interval tree TREE. Balancing is by weight
(the amount of text). */
static INTERVAL
balance_intervals_internal (tree)
register INTERVAL tree;
{
/* Balance within each side. */
if (tree->left)
balance_intervals (tree->left);
if (tree->right)
balance_intervals (tree->right);
return balance_an_interval (tree);
}
/* Advertised interface to balance intervals. */
INTERVAL
balance_intervals (tree)
INTERVAL tree;
{
if (tree == NULL_INTERVAL)
return NULL_INTERVAL;
return balance_intervals_internal (tree);
}
/* Split INTERVAL into two pieces, starting the second piece at /* Split INTERVAL into two pieces, starting the second piece at
character position OFFSET (counting from 0), relative to INTERVAL. character position OFFSET (counting from 0), relative to INTERVAL.
INTERVAL becomes the left-hand piece, and the right-hand piece INTERVAL becomes the left-hand piece, and the right-hand piece
...@@ -380,7 +468,7 @@ split_interval_right (interval, offset) ...@@ -380,7 +468,7 @@ split_interval_right (interval, offset)
new->position = position + offset; new->position = position + offset;
new->parent = interval; new->parent = interval;
if (LEAF_INTERVAL_P (interval) || NULL_RIGHT_CHILD (interval)) if (NULL_RIGHT_CHILD (interval))
{ {
interval->right = new; interval->right = new;
new->total_length = new_length; new->total_length = new_length;
...@@ -392,9 +480,11 @@ split_interval_right (interval, offset) ...@@ -392,9 +480,11 @@ split_interval_right (interval, offset)
new->right = interval->right; new->right = interval->right;
interval->right->parent = new; interval->right->parent = new;
interval->right = new; interval->right = new;
new->total_length = new_length + new->right->total_length; new->total_length = new_length + new->right->total_length;
balance_an_interval (new);
balance_possible_root_interval (interval);
return new; return new;
} }
...@@ -436,7 +526,10 @@ split_interval_left (interval, offset) ...@@ -436,7 +526,10 @@ split_interval_left (interval, offset)
new->left = interval->left; new->left = interval->left;
new->left->parent = new; new->left->parent = new;
interval->left = new; interval->left = new;
new->total_length = new_length + LEFT_TOTAL_LENGTH (new); new->total_length = new_length + new->left->total_length;
balance_an_interval (new);
balance_possible_root_interval (interval);
return new; return new;
} }
...@@ -465,6 +558,8 @@ find_interval (tree, position) ...@@ -465,6 +558,8 @@ find_interval (tree, position)
if (relative_position > TOTAL_LENGTH (tree)) if (relative_position > TOTAL_LENGTH (tree))
abort (); /* Paranoia */ abort (); /* Paranoia */
tree = balance_possible_root_interval (tree);
while (1) while (1)
{ {
if (relative_position < LEFT_TOTAL_LENGTH (tree)) if (relative_position < LEFT_TOTAL_LENGTH (tree))
...@@ -694,8 +789,11 @@ adjust_intervals_for_insertion (tree, position, length) ...@@ -694,8 +789,11 @@ adjust_intervals_for_insertion (tree, position, length)
/* Even if we are positioned between intervals, we default /* Even if we are positioned between intervals, we default
to the left one if it exists. We extend it now and split to the left one if it exists. We extend it now and split
off a part later, if stickyness demands it. */ off a part later, if stickyness demands it. */
for (temp = prev ? prev : i; ! NULL_INTERVAL_P (temp); temp = temp->parent) for (temp = prev ? prev : i;! NULL_INTERVAL_P (temp); temp = temp->parent)
temp->total_length += length; {
temp->total_length += length;
temp = balance_possible_root_interval (temp);
}
/* If at least one interval has sticky properties, /* If at least one interval has sticky properties,
we check the stickyness property by property. */ we check the stickyness property by property. */
...@@ -739,7 +837,10 @@ adjust_intervals_for_insertion (tree, position, length) ...@@ -739,7 +837,10 @@ adjust_intervals_for_insertion (tree, position, length)
else else
{ {
for (temp = i; ! NULL_INTERVAL_P (temp); temp = temp->parent) for (temp = i; ! NULL_INTERVAL_P (temp); temp = temp->parent)
temp->total_length += length; {
temp->total_length += length;
temp = balance_possible_root_interval (temp);
}
} }
return tree; return tree;
...@@ -1279,6 +1380,8 @@ graft_intervals_into_buffer (source, position, length, buffer, inherit) ...@@ -1279,6 +1380,8 @@ graft_intervals_into_buffer (source, position, length, buffer, inherit)
make_number (position + length), make_number (position + length),
Qnil, buf); Qnil, buf);
} }
if (! NULL_INTERVAL_P (buffer->intervals))
buffer->intervals = balance_an_interval (buffer->intervals);
return; return;
} }
...@@ -1370,7 +1473,8 @@ graft_intervals_into_buffer (source, position, length, buffer, inherit) ...@@ -1370,7 +1473,8 @@ graft_intervals_into_buffer (source, position, length, buffer, inherit)
over = next_interval (over); over = next_interval (over);
} }
buffer->intervals = balance_intervals (buffer->intervals); if (! NULL_INTERVAL_P (buffer->intervals))
buffer->intervals = balance_an_interval (buffer->intervals);
return; return;
} }
...@@ -1762,70 +1866,6 @@ verify_interval_modification (buf, start, end) ...@@ -1762,70 +1866,6 @@ verify_interval_modification (buf, start, end)
} }
} }
/* Balance an interval node if the amount of text in its left and right
subtrees differs by more than the percentage specified by
`interval-balance-threshold'. */
static INTERVAL
balance_an_interval (i)
INTERVAL i;
{
register int total_children_size = (LEFT_TOTAL_LENGTH (i)
+ RIGHT_TOTAL_LENGTH (i));
register int threshold = (XFASTINT (interval_balance_threshold)
* (total_children_size / 100));
/* Balance within each side. */
balance_intervals (i->left);
balance_intervals (i->right);
if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
&& (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
{
i = rotate_right (i);
/* If that made it unbalanced the other way, take it back. */
if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
&& (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
return rotate_left (i);
return i;
}
if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
&& (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
{
i = rotate_left (i);
if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
&& (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
return rotate_right (i);
return i;
}
return i;
}
/* Balance the interval tree TREE. Balancing is by weight
(the amount of text). */
INTERVAL
balance_intervals (tree)
register INTERVAL tree;
{
register INTERVAL new_tree;
if (NULL_INTERVAL_P (tree))
return NULL_INTERVAL;
new_tree = tree;
do
{
tree = new_tree;
new_tree = balance_an_interval (new_tree);
}
while (new_tree != tree);
return new_tree;
}
/* Produce an interval tree reflecting the intervals in /* Produce an interval tree reflecting the intervals in
TREE from START to START + LENGTH. */ TREE from START to START + LENGTH. */
...@@ -1866,7 +1906,7 @@ copy_intervals (tree, start, length) ...@@ -1866,7 +1906,7 @@ copy_intervals (tree, start, length)
got += prevlen; got += prevlen;
} }
return balance_intervals (new); return balance_an_interval (new);
} }
/* Give STRING the properties of BUFFER from POSITION to LENGTH. */ /* Give STRING the properties of BUFFER from POSITION to LENGTH. */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment