mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-08 07:20:28 -08:00
597 lines
14 KiB
C
597 lines
14 KiB
C
/* tree.c: BINARY TREE IMPLEMENTATION
|
|
*
|
|
* $Id$
|
|
* Copyright (C) 2014-2020 Ravenbrook Limited. See end of file for license.
|
|
*
|
|
* Simple binary trees with utilities, for use as building blocks.
|
|
* Keep it simple, like Rings (see ring.h).
|
|
*
|
|
* The performance requirements on tree implementation will depend on
|
|
* how each individual function is applied in the MPS.
|
|
*
|
|
* .note.stack: It's important that the MPS have a bounded stack size,
|
|
* and this is a problem for tree algorithms. Basically, we have to
|
|
* avoid recursion. <design/sp#.sol.depth.no-recursion>.
|
|
*/
|
|
|
|
#include "tree.h"
|
|
#include "mpm.h"
|
|
|
|
SRCID(tree, "$Id$");
|
|
|
|
|
|
Bool TreeCheck(Tree tree)
|
|
{
|
|
if (tree != TreeEMPTY) {
|
|
CHECKL(tree != NULL);
|
|
CHECKL(tree->left == TreeEMPTY || tree->left != NULL);
|
|
CHECKL(tree->right == TreeEMPTY || tree->right != NULL);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
Bool TreeCheckLeaf(Tree tree)
|
|
{
|
|
CHECKL(TreeCheck(tree));
|
|
CHECKL(tree != TreeEMPTY);
|
|
CHECKL(tree->left == TreeEMPTY);
|
|
CHECKL(tree->right == TreeEMPTY);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* TreeDebugCount -- count and check order of tree
|
|
*
|
|
* This function may be called from a debugger or temporarily inserted
|
|
* during development to check a tree's integrity. It may not be called
|
|
* from the production MPS because it uses indefinite stack depth.
|
|
* See .note.stack.
|
|
*/
|
|
|
|
static Count TreeDebugCountBetween(Tree node,
|
|
TreeCompareFunction compare,
|
|
TreeKeyFunction key,
|
|
TreeKey min, TreeKey max)
|
|
{
|
|
if (node == TreeEMPTY)
|
|
return 0;
|
|
AVERT(Tree, node);
|
|
AVER(min == NULL || compare(node, min) != CompareGREATER);
|
|
AVER(max == NULL || compare(node, max) != CompareLESS);
|
|
return TreeDebugCountBetween(TreeLeft(node), compare, key, min, key(node)) +
|
|
1 +
|
|
TreeDebugCountBetween(TreeRight(node), compare, key, key(node), max);
|
|
}
|
|
|
|
Count TreeDebugCount(Tree tree, TreeCompareFunction compare,
|
|
TreeKeyFunction key)
|
|
{
|
|
AVERT(Tree, tree);
|
|
return TreeDebugCountBetween(tree, compare, key, NULL, NULL);
|
|
}
|
|
|
|
|
|
/* TreeFind -- search for a node matching the key
|
|
*
|
|
* If a matching node is found, sets *treeReturn to that node and returns
|
|
* CompareEQUAL. Otherwise returns values useful for inserting a node with
|
|
* the key. If the tree is empty, returns CompareEQUAL and sets *treeReturn
|
|
* to NULL. Otherwise, sets *treeReturn to a potential parent for the new
|
|
* node and returns CompareLESS if the new node should be its left child,
|
|
* or CompareGREATER for its right.
|
|
*/
|
|
|
|
Compare TreeFind(Tree *treeReturn, Tree root, TreeKey key,
|
|
TreeCompareFunction compare)
|
|
{
|
|
Tree node, parent;
|
|
Compare cmp = CompareEQUAL;
|
|
|
|
AVERT_CRITICAL(Tree, root);
|
|
AVER_CRITICAL(treeReturn != NULL);
|
|
AVER_CRITICAL(FUNCHECK(compare));
|
|
/* key is arbitrary */
|
|
|
|
parent = NULL;
|
|
node = root;
|
|
while (node != TreeEMPTY) {
|
|
parent = node;
|
|
cmp = compare(node, key);
|
|
switch (cmp) {
|
|
case CompareLESS:
|
|
node = node->left;
|
|
break;
|
|
case CompareEQUAL:
|
|
*treeReturn = node;
|
|
return cmp;
|
|
case CompareGREATER:
|
|
node = node->right;
|
|
break;
|
|
default:
|
|
NOTREACHED;
|
|
*treeReturn = NULL;
|
|
return cmp;
|
|
}
|
|
}
|
|
|
|
*treeReturn = parent;
|
|
return cmp;
|
|
}
|
|
|
|
|
|
/* TreeFindNext -- search for node containing key, or next node
|
|
*
|
|
* If there is a node that is greater than key, set *treeReturn to that
|
|
* node and return TRUE.
|
|
*
|
|
* Otherwise, key is greater than all nodes in the tree, so leave
|
|
* *treeReturn unchanged and return FALSE.
|
|
*/
|
|
|
|
Bool TreeFindNext(Tree *treeReturn, Tree root, TreeKey key,
|
|
TreeCompareFunction compare)
|
|
{
|
|
Tree node, best = NULL;
|
|
Bool result = FALSE;
|
|
|
|
AVERT(Tree, root);
|
|
AVER(treeReturn != NULL);
|
|
AVER(FUNCHECK(compare));
|
|
/* key is arbitrary */
|
|
|
|
node = root;
|
|
while (node != TreeEMPTY) {
|
|
Compare cmp = compare(node, key);
|
|
switch (cmp) {
|
|
case CompareLESS:
|
|
best = node;
|
|
result = TRUE;
|
|
node = node->left;
|
|
break;
|
|
case CompareEQUAL:
|
|
case CompareGREATER:
|
|
node = node->right;
|
|
break;
|
|
default:
|
|
NOTREACHED;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*treeReturn = best;
|
|
return result;
|
|
}
|
|
|
|
|
|
/* TreeInsert -- insert a node into a tree
|
|
*
|
|
* If the key doesn't exist in the tree, inserts a node as a leaf of the
|
|
* tree, returning the resulting tree in *treeReturn, and returns TRUE.
|
|
* Otherwise, *treeReturn points to the existing matching node, the tree
|
|
* is not modified, and returns FALSE.
|
|
*/
|
|
|
|
Bool TreeInsert(Tree *treeReturn, Tree root, Tree node,
|
|
TreeKey key, TreeCompareFunction compare)
|
|
{
|
|
Tree parent;
|
|
Compare cmp;
|
|
|
|
AVER(treeReturn != NULL);
|
|
AVERT(Tree, root);
|
|
AVER(TreeCheckLeaf(node));
|
|
AVER(FUNCHECK(compare));
|
|
/* key is arbitrary */
|
|
|
|
cmp = TreeFind(&parent, root, key, compare);
|
|
switch (cmp) {
|
|
case CompareLESS:
|
|
parent->left = node;
|
|
break;
|
|
case CompareEQUAL:
|
|
if (parent != NULL) {
|
|
*treeReturn = parent;
|
|
return FALSE;
|
|
}
|
|
AVER(root == TreeEMPTY);
|
|
root = node;
|
|
break;
|
|
case CompareGREATER:
|
|
parent->right = node;
|
|
break;
|
|
default:
|
|
NOTREACHED;
|
|
*treeReturn = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
*treeReturn = root;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#if 0 /* This code is currently not in use in the MPS */
|
|
|
|
/* TreeTraverseMorris -- traverse tree inorder in constant space
|
|
*
|
|
* The tree may not be accessed or modified during the traversal, and
|
|
* the traversal must complete in order to repair the tree.
|
|
*
|
|
* The visitor should return FALSE to terminate the traversal early,
|
|
* in which case FALSE is returned.
|
|
*
|
|
* TreeTraverse is generally superior if comparisons are cheap, but
|
|
* TreeTraverseMorris does not require any comparison function.
|
|
*
|
|
* <https://en.wikipedia.org/wiki/Tree_traversal#Morris_in-order_traversal_using_threading>
|
|
*
|
|
* Joseph M. Morris (1979). "Traversing Binary Trees Simply and Cheaply".
|
|
* Information Processing Letters 9:5 pp. 197-200.
|
|
*/
|
|
|
|
Bool TreeTraverseMorris(Tree tree, TreeVisitor visit,
|
|
void *closure)
|
|
{
|
|
Tree node;
|
|
Bool visiting = TRUE;
|
|
|
|
AVERT(Tree, tree);
|
|
AVER(FUNCHECK(visit));
|
|
/* closure arbitrary */
|
|
|
|
node = tree;
|
|
while (node != TreeEMPTY) {
|
|
if (node->left == TreeEMPTY) {
|
|
if (visiting)
|
|
visiting = visit(node, closure);
|
|
node = node->right;
|
|
} else {
|
|
Tree pre = node->left;
|
|
for (;;) {
|
|
if (pre->right == TreeEMPTY) {
|
|
pre->right = node;
|
|
node = node->left;
|
|
break;
|
|
}
|
|
if (pre->right == node) {
|
|
pre->right = TreeEMPTY;
|
|
if (visiting)
|
|
visiting = visit(node, closure);
|
|
else if (node == tree)
|
|
return FALSE;
|
|
node = node->right;
|
|
break;
|
|
}
|
|
pre = pre->right;
|
|
}
|
|
}
|
|
}
|
|
|
|
return visiting;
|
|
}
|
|
|
|
#endif /* not currently in use */
|
|
|
|
|
|
/* TreeTraverse -- traverse tree in-order using pointer reversal
|
|
*
|
|
* The tree may not be accessed or modified during the traversal, and
|
|
* the traversal must complete in order to repair the tree.
|
|
*
|
|
* The visitor should return FALSE to terminate the traversal early,
|
|
* in which case FALSE is returned.
|
|
*
|
|
* TreeTraverseMorris is an alternative when no cheap comparison is available.
|
|
*/
|
|
|
|
static Tree stepDownLeft(Tree node, Tree *parentIO)
|
|
{
|
|
Tree parent = *parentIO;
|
|
Tree child = TreeLeft(node);
|
|
TreeSetLeft(node, parent);
|
|
*parentIO = node;
|
|
return child;
|
|
}
|
|
|
|
static Tree stepDownRight(Tree node, Tree *parentIO)
|
|
{
|
|
Tree parent = *parentIO;
|
|
Tree child = TreeRight(node);
|
|
TreeSetRight(node, parent);
|
|
*parentIO = node;
|
|
return child;
|
|
}
|
|
|
|
static Tree stepUpRight(Tree node, Tree *parentIO)
|
|
{
|
|
Tree parent = *parentIO;
|
|
Tree grandparent = TreeLeft(parent);
|
|
TreeSetLeft(parent, node);
|
|
*parentIO = grandparent;
|
|
return parent;
|
|
}
|
|
|
|
static Tree stepUpLeft(Tree node, Tree *parentIO)
|
|
{
|
|
Tree parent = *parentIO;
|
|
Tree grandparent = TreeRight(parent);
|
|
TreeSetRight(parent, node);
|
|
*parentIO = grandparent;
|
|
return parent;
|
|
}
|
|
|
|
Bool TreeTraverse(Tree tree,
|
|
TreeCompareFunction compare,
|
|
TreeKeyFunction key,
|
|
TreeVisitor visit, void *closure)
|
|
{
|
|
Tree parent, node;
|
|
|
|
AVERT(Tree, tree);
|
|
AVER(FUNCHECK(visit));
|
|
/* closure arbitrary */
|
|
|
|
parent = TreeEMPTY;
|
|
node = tree;
|
|
|
|
if (node == TreeEMPTY)
|
|
return TRUE;
|
|
|
|
down:
|
|
if (TreeHasLeft(node)) {
|
|
node = stepDownLeft(node, &parent);
|
|
AVER(compare(parent, key(node)) == CompareLESS);
|
|
goto down;
|
|
}
|
|
if (!visit(node, closure))
|
|
goto abort;
|
|
if (TreeHasRight(node)) {
|
|
node = stepDownRight(node, &parent);
|
|
AVER(compare(parent, key(node)) != CompareLESS);
|
|
goto down;
|
|
}
|
|
|
|
up:
|
|
if (parent == TreeEMPTY)
|
|
return TRUE;
|
|
if (compare(parent, key(node)) != CompareLESS) {
|
|
node = stepUpLeft(node, &parent);
|
|
goto up;
|
|
}
|
|
node = stepUpRight(node, &parent);
|
|
if (!visit(node, closure))
|
|
goto abort;
|
|
if (!TreeHasRight(node))
|
|
goto up;
|
|
node = stepDownRight(node, &parent);
|
|
goto down;
|
|
|
|
abort:
|
|
if (parent == TreeEMPTY)
|
|
return FALSE;
|
|
if (compare(parent, key(node)) != CompareLESS)
|
|
node = stepUpLeft(node, &parent);
|
|
else
|
|
node = stepUpRight(node, &parent);
|
|
goto abort;
|
|
}
|
|
|
|
|
|
/* TreeRotateLeft -- Rotate right child edge of node
|
|
*
|
|
* Rotates node, right child of node, and left child of right
|
|
* child of node, leftwards in the order stated. Preserves tree
|
|
* ordering.
|
|
*/
|
|
|
|
void TreeRotateLeft(Tree *treeIO)
|
|
{
|
|
Tree tree, right;
|
|
|
|
AVER(treeIO != NULL);
|
|
tree = *treeIO;
|
|
AVERT(Tree, tree);
|
|
right = TreeRight(tree);
|
|
AVERT(Tree, right);
|
|
|
|
TreeSetRight(tree, TreeLeft(right));
|
|
TreeSetLeft(right, tree);
|
|
|
|
*treeIO = right;
|
|
}
|
|
|
|
|
|
/* TreeRotateRight -- Rotate left child edge of node
|
|
*
|
|
* Rotates node, left child of node, and right child of left
|
|
* child of node, leftwards in the order stated. Preserves tree
|
|
* ordering.
|
|
*/
|
|
|
|
void TreeRotateRight(Tree *treeIO)
|
|
{
|
|
Tree tree, left;
|
|
|
|
AVER(treeIO != NULL);
|
|
tree = *treeIO;
|
|
AVERT(Tree, tree);
|
|
left = TreeLeft(tree);
|
|
AVERT(Tree, left);
|
|
|
|
TreeSetLeft(*treeIO, TreeRight(left));
|
|
TreeSetRight(left, *treeIO);
|
|
|
|
*treeIO = left;
|
|
}
|
|
|
|
|
|
/* TreeReverseLeftSpine -- reverse the pointers on the left spine
|
|
*
|
|
* Descends the left spine of a tree, updating each node's left child
|
|
* to point to its parent instead. The root's left child is set to
|
|
* TreeEMPTY. Returns the leftmost child, or TreeEMPTY if the tree
|
|
* was empty.
|
|
*/
|
|
|
|
Tree TreeReverseLeftSpine(Tree tree)
|
|
{
|
|
Tree node, parent;
|
|
|
|
AVERT(Tree, tree);
|
|
|
|
parent = TreeEMPTY;
|
|
node = tree;
|
|
while (node != TreeEMPTY) {
|
|
Tree child = TreeLeft(node);
|
|
TreeSetLeft(node, parent);
|
|
parent = node;
|
|
node = child;
|
|
}
|
|
|
|
return parent;
|
|
}
|
|
|
|
|
|
/* TreeReverseRightSpine -- reverse the pointers on the right spine
|
|
*
|
|
* Descends the right spine of a tree, updating each node's right child
|
|
* to point to its parent instead. The root's right child is set to
|
|
* TreeEMPTY. Returns the rightmost child or TreeEMPTY if the tree
|
|
* was empty.
|
|
*/
|
|
|
|
Tree TreeReverseRightSpine(Tree tree)
|
|
{
|
|
Tree node, parent;
|
|
|
|
AVERT(Tree, tree);
|
|
|
|
parent = TreeEMPTY;
|
|
node = tree;
|
|
while (node != TreeEMPTY) {
|
|
Tree child = TreeRight(node);
|
|
TreeSetRight(node, parent);
|
|
parent = node;
|
|
node = child;
|
|
}
|
|
|
|
return parent;
|
|
}
|
|
|
|
|
|
/* TreeToVine -- unbalance a tree into a single right spine */
|
|
|
|
Count TreeToVine(Tree *link)
|
|
{
|
|
Count count = 0;
|
|
|
|
AVER(link != NULL);
|
|
AVERT(Tree, *link);
|
|
|
|
while (*link != TreeEMPTY) {
|
|
while (TreeHasLeft(*link))
|
|
TreeRotateRight(link);
|
|
link = &((*link)->right);
|
|
++count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
/* TreeBalance -- rebalance a tree
|
|
*
|
|
* Linear time, constant space rebalance.
|
|
*
|
|
* Quentin F. Stout and Bette L. Warren,
|
|
* "Tree Rebalancing in Optimal Time and Space",
|
|
* Communications of the ACM, Vol. 29, No. 9 (September 1986), p. 902-908
|
|
*/
|
|
|
|
void TreeBalance(Tree *treeIO)
|
|
{
|
|
Count depth;
|
|
|
|
AVER(treeIO != NULL);
|
|
AVERT(Tree, *treeIO);
|
|
|
|
depth = TreeToVine(treeIO);
|
|
|
|
if (depth > 2) {
|
|
Count n = depth - 1;
|
|
do {
|
|
Count m = n / 2, i;
|
|
Tree *link = treeIO;
|
|
for (i = 0; i < m; ++i) {
|
|
TreeRotateLeft(link);
|
|
link = &((*link)->right);
|
|
}
|
|
n = n - m - 1;
|
|
} while (n > 1);
|
|
}
|
|
}
|
|
|
|
|
|
/* TreeTraverseAndDelete -- traverse a tree while deleting nodes
|
|
*
|
|
* The visitor function must return TRUE to delete the current node,
|
|
* or FALSE to keep it.
|
|
*
|
|
* <design/arena#.chunk.delete.tricky>.
|
|
*/
|
|
void TreeTraverseAndDelete(Tree *treeIO, TreeVisitor visitor,
|
|
void *closure)
|
|
{
|
|
Tree *treeref = treeIO;
|
|
|
|
AVER(treeIO != NULL);
|
|
AVERT(Tree, *treeIO);
|
|
AVER(FUNCHECK(visitor));
|
|
/* closure arbitrary */
|
|
|
|
TreeToVine(treeIO);
|
|
|
|
while (*treeref != TreeEMPTY) {
|
|
Tree tree = *treeref; /* Current node. */
|
|
Tree *nextref = &tree->right; /* Location of pointer to next node. */
|
|
Tree next = *nextref; /* Next node. */
|
|
if ((*visitor)(tree, closure)) {
|
|
/* Delete current node. */
|
|
*treeref = next;
|
|
} else {
|
|
/* Keep current node. */
|
|
treeref = nextref;
|
|
}
|
|
}
|
|
TreeBalance(treeIO);
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2014-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|