changeset 5415:95882472f2da

(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.
author Richard M. Stallman <rms@gnu.org>
date Sun, 02 Jan 1994 19:01:15 +0000
parents 39f0a30bb163
children 3733a396e16a
files src/intervals.c
diffstat 1 files changed, 140 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/src/intervals.c	Sun Jan 02 19:00:54 1994 +0000
+++ b/src/intervals.c	Sun Jan 02 19:01:15 1994 +0000
@@ -284,34 +284,35 @@
 {
   INTERVAL i;
   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.  */
   if (! ROOT_INTERVAL_P (interval))
     if (AM_LEFT_CHILD (interval))
-      interval->parent->left = interval->left;
+      interval->parent->left = B;
     else
-      interval->parent->right = interval->left;
-  interval->left->parent = interval->parent;
+      interval->parent->right = B;
+  B->parent = interval->parent;
 
-  /* B gets the same length as A, since it get A's position in the tree.  */
-  interval->left->total_length = interval->total_length;
+  /* Make B the parent of A */
+  i = B->right;
+  B->right = interval;
+  interval->parent = B;
 
-  /* B becomes the parent of A.  */
-  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;
   if (! NULL_INTERVAL_P (i))
     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;
 }
-
+
 /* Assuming that a right child exists, perform the following operation:
 
     A               B   
@@ -327,34 +328,121 @@
 {
   INTERVAL i;
   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 (AM_LEFT_CHILD (interval))
-      interval->parent->left = interval->right;
+      interval->parent->left = B;
     else
-      interval->parent->right = interval->right;
-  interval->right->parent = interval->parent;
-
-  /* B must have the same total length of A.  */
-  interval->right->total_length = interval->total_length;
+      interval->parent->right = B;
+  B->parent = interval->parent;
 
   /* Make B the parent of A */
-  i = interval->right->left;
-  interval->right->left = interval;
-  interval->parent = interval->right;
+  i = B->left;
+  B->left = interval;
+  interval->parent = B;
 
   /* Make A point to c */
   interval->right = i;
   if (! NULL_INTERVAL_P (i))
     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;
 }
 
+/* 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
    character position OFFSET (counting from 0), relative to INTERVAL.
    INTERVAL becomes the left-hand piece, and the right-hand piece
@@ -380,7 +468,7 @@
   new->position = position + offset;
   new->parent = interval;
 
-  if (LEAF_INTERVAL_P (interval) || NULL_RIGHT_CHILD (interval))
+  if (NULL_RIGHT_CHILD (interval))
     {
       interval->right = new;
       new->total_length = new_length;
@@ -392,8 +480,10 @@
   new->right = interval->right;
   interval->right->parent = 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;
 }
@@ -436,7 +526,10 @@
   new->left = interval->left;
   new->left->parent = 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;
 }
@@ -465,6 +558,8 @@
   if (relative_position > TOTAL_LENGTH (tree))
     abort ();			/* Paranoia */
 
+  tree = balance_possible_root_interval (tree);
+
   while (1)
     {
       if (relative_position < LEFT_TOTAL_LENGTH (tree))
@@ -694,8 +789,11 @@
       /* Even if we are positioned between intervals, we default
 	 to the left one if it exists.  We extend it now and split
 	 off a part later, if stickyness demands it.  */
-      for (temp = prev ? prev : i; ! NULL_INTERVAL_P (temp); temp = temp->parent)
-	temp->total_length += length;
+      for (temp = prev ? prev : i;! NULL_INTERVAL_P (temp); temp = temp->parent)
+	{
+	  temp->total_length += length;
+	  temp = balance_possible_root_interval (temp);
+	}
       
       /* If at least one interval has sticky properties,
 	 we check the stickyness property by property.  */
@@ -739,7 +837,10 @@
   else
     {
       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;
@@ -1279,6 +1380,8 @@
 				make_number (position + length),
 				Qnil, buf);
 	}
+      if (! NULL_INTERVAL_P (buffer->intervals))
+	buffer->intervals = balance_an_interval (buffer->intervals);
       return;
     }
 
@@ -1370,7 +1473,8 @@
       over = next_interval (over);
     }
 
-  buffer->intervals = balance_intervals (buffer->intervals);
+  if (! NULL_INTERVAL_P (buffer->intervals))
+    buffer->intervals = balance_an_interval (buffer->intervals);
   return;
 }
 
@@ -1762,70 +1866,6 @@
     }
 }
 
-/* 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
    TREE from START to START + LENGTH.  */
 
@@ -1866,7 +1906,7 @@
       got += prevlen;
     }
 
-  return balance_intervals (new);
+  return balance_an_interval (new);
 }
 
 /* Give STRING the properties of BUFFER from POSITION to LENGTH.  */