1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-16 02:50:26 -08:00
emacs/mps/code/tree.c
Richard Brooksby 9d7b1ff29e Improving comments in response to code review suggestions <https://info.ravenbrook.com/mail/2014/03/11/14-47-33/0/>.
Copied from Perforce
 Change: 184725
 ServerID: perforce.ravenbrook.com
2014-03-11 15:46:51 +00:00

533 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* tree.c: BINARY TREE IMPLEMENTATION
*
* $Id$
* Copyright (C) 2014 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. TODO: Design documentation for this
* requirement, meanwhile see job003651 and job003640.
*/
#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,
TreeCompare compare, TreeKeyMethod 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, TreeCompare compare, TreeKeyMethod key)
{
AVERT(Tree, tree);
return TreeDebugCountBetween(tree, compare, key, NULL, NULL);
}
#if 0 /* This code is not currently in use in the MPS */
/* 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, TreeCompare compare)
{
Tree node, parent;
Compare cmp = CompareEQUAL;
AVERT(Tree, root);
AVER(treeReturn != NULL);
AVER(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;
}
/* 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, TreeCompare compare)
{
Tree parent;
Compare cmp;
AVER(treeReturn != NULL);
AVER(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;
}
/* 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.
*
* <http://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. 197200.
*/
Bool TreeTraverseMorris(Tree tree, TreeVisitor visit,
void *closureP, Size closureS)
{
Tree node;
Bool visiting = TRUE;
AVERT(Tree, tree);
AVER(FUNCHECK(visit));
/* closureP, closureS arbitrary */
node = tree;
while (node != TreeEMPTY) {
if (node->left == TreeEMPTY) {
if (visiting)
visiting = visit(node, closureP, closureS);
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, closureP, closureS);
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,
TreeCompare compare,
TreeKeyMethod key,
TreeVisitor visit, void *closureP, Size closureS)
{
Tree parent, node;
AVERT(Tree, tree);
AVER(FUNCHECK(visit));
/* closureP, closureS 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, closureP, closureS))
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, closureP, closureS))
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 right 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;
}
#if 0 /* This code is currently not in use in the MPS */
/* 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);
}
}
#endif /* not currently in use in the MPS */
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* 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.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* 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, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND 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.
*/