aboutsummaryrefslogtreecommitdiff
path: root/shared/common/qtree.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/common/qtree.c')
-rw-r--r--shared/common/qtree.c780
1 files changed, 780 insertions, 0 deletions
diff --git a/shared/common/qtree.c b/shared/common/qtree.c
new file mode 100644
index 0000000..368e38c
--- /dev/null
+++ b/shared/common/qtree.c
@@ -0,0 +1,780 @@
+/* Copyright (c) 2004 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_pd.h"
+#include "qtree.h"
+
+/* Since there is no sentinel node, the deletion routine has to have
+ a few extra checks. LATER rethink. */
+
+/* LATER freelist */
+
+typedef t_qnode *(*t_qtree_inserthook)(t_qnode *);
+
+#ifdef QTREE_DEBUG
+/* returns black-height or 0 if failed */
+static int qnode_verify(t_qnode *np)
+{
+ if (np)
+ {
+ int bhl, bhr;
+ if (((bhl = qnode_verify(np->n_left)) == 0) ||
+ ((bhr = qnode_verify(np->n_right)) == 0))
+ return (0);
+ if (bhl != bhr)
+ {
+ /* failure: two paths rooted in the same node
+ contain different number of black nodes */
+ bug("qnode_verify: not balanced");
+ return (0);
+ }
+ if (np->n_black)
+ return (bhl + 1);
+ else
+ {
+ if ((np->n_left && !np->n_left->n_black) ||
+ (np->n_right && !np->n_right->n_black))
+ {
+ bug("qnode_verify: adjacent red nodes");
+ return (0);
+ }
+ return (bhl);
+ }
+ }
+ else return (1);
+}
+
+/* returns black-height or 0 if failed */
+static int qtree_verify(t_qtree *tree)
+{
+ return (qnode_verify(tree->t_root));
+}
+
+static int qnode_checkmulti(t_qnode *np1, t_qnode *np2)
+{
+ if (np1 && np2 && np1->n_key == np2->n_key)
+ {
+ if (np1 == np2)
+ bug("qnode_checkmulti");
+ else
+ return (1);
+ }
+ return (0);
+}
+
+static void qnode_post(t_qtree *tree, t_qnode *np,
+ t_qnode_vshowhook hook, char *message)
+{
+ startpost("%g ", np->n_key);
+ if (tree->t_valuetype == QTREETYPE_FLOAT)
+ startpost("%g ", QNODE_GETFLOAT(np));
+ else if (tree->t_valuetype == QTREETYPE_SYMBOL)
+ startpost("%s ", QNODE_GETSYMBOL(np)->s_name);
+ else if (tree->t_valuetype == QTREETYPE_ATOM)
+ {
+ t_atom *ap = QNODE_GETATOMPTR(np);
+ if (ap->a_type == A_FLOAT)
+ startpost("%g ", ap->a_w.w_float);
+ else if (ap->a_type == A_SYMBOL)
+ startpost("%s ", ap->a_w.w_symbol->s_name);
+ }
+ else if (hook)
+ {
+ char buf[MAXPDSTRING];
+ (*hook)(np, buf, MAXPDSTRING);
+ startpost("%s ", buf);
+ }
+ else startpost("0x%08x ", (int)QNODE_GETSYMBOL(np));
+ startpost("%s ", (np->n_black ? "black" : "red"));
+
+ if (qnode_checkmulti(np, np->n_parent) ||
+ qnode_checkmulti(np, np->n_left) ||
+ qnode_checkmulti(np, np->n_right) ||
+ qnode_checkmulti(np->n_parent, np->n_left) ||
+ qnode_checkmulti(np->n_parent, np->n_right) ||
+ qnode_checkmulti(np->n_left, np->n_right))
+ startpost("multi ");
+
+ if (np->n_parent)
+ startpost("(%g -> ", np->n_parent->n_key);
+ else
+ startpost("(nul -> ");
+ if (np->n_left)
+ startpost("%g, ", np->n_left->n_key);
+ else
+ startpost("nul, ");
+ if (np->n_right)
+ startpost("%g)", np->n_right->n_key);
+ else
+ startpost("nul)");
+ if (message)
+ post(": %s", message);
+ else
+ endpost();
+}
+
+/* Assert a standard stackless traversal producing the same sequence,
+ as the auxiliary list. */
+static int qtree_checktraversal(t_qtree *tree)
+{
+ t_qnode *treewalk = tree->t_root;
+ t_qnode *listwalk = tree->t_first;
+ int count = 0;
+ while (treewalk)
+ {
+ t_qnode *prev = treewalk->n_left;
+ if (prev)
+ {
+ while (prev->n_right && prev->n_right != treewalk)
+ prev = prev->n_right;
+ if (prev->n_right)
+ {
+ prev->n_right = 0;
+ count++;
+ if (treewalk == listwalk)
+ listwalk = listwalk->n_next;
+ else
+ {
+ bug("qtree_checktraversal 1");
+ qnode_post(tree, treewalk, 0, "treewalk");
+ if (listwalk)
+ qnode_post(tree, listwalk, 0, "listwalk");
+ else
+ post("empty listwalk pointer");
+ listwalk = treewalk;
+ }
+ treewalk = treewalk->n_right;
+ }
+ else
+ {
+ prev->n_right = treewalk;
+ treewalk = treewalk->n_left;
+ }
+ }
+ else
+ {
+ count++;
+ if (treewalk == listwalk)
+ listwalk = listwalk->n_next;
+ else
+ {
+ bug("qtree_checktraversal 2");
+ qnode_post(tree, treewalk, 0, "treewalk");
+ if (listwalk)
+ qnode_post(tree, listwalk, 0, "listwalk");
+ else
+ post("empty listwalk pointer");
+ listwalk = treewalk;
+ }
+ treewalk = treewalk->n_right;
+ }
+ }
+ return (count);
+}
+
+static int qnode_height(t_qnode *np)
+{
+ if (np)
+ {
+ int lh = qnode_height(np->n_left);
+ int rh = qnode_height(np->n_right);
+ return (lh > rh ? lh + 1 : rh + 1);
+ }
+ else return (0);
+}
+
+void qtree_debug(t_qtree *tree, int level, t_qnode_vshowhook hook)
+{
+ t_qnode *np;
+ int count;
+ post("------------------------");
+ count = qtree_checktraversal(tree);
+ if (level)
+ {
+ for (np = tree->t_first; np; np = np->n_next)
+ qnode_post(tree, np, hook, 0);
+ if (level > 1)
+ {
+ post("************");
+ for (np = tree->t_last; np; np = np->n_prev)
+ startpost("%g ", np->n_key);
+ endpost();
+ }
+ }
+ if (tree->t_root)
+ {
+ t_qnode *first = tree->t_root, *last = tree->t_root;
+ while (first->n_left && first->n_left != tree->t_root)
+ first = first->n_left;
+ while (last->n_right && last->n_right != tree->t_root)
+ last = last->n_right;
+ post("count %d, height %d, root %g",
+ count, qnode_height(tree->t_root), tree->t_root->n_key);
+ post("first %g, root->left* %g, last %g, root->right* %g",
+ (tree->t_first ? tree->t_first->n_key : 0), first->n_key,
+ (tree->t_last ? tree->t_last->n_key : 0), last->n_key);
+ }
+ else post("empty");
+ post("...verified (black-height is %d)", qtree_verify(tree));
+ post("------------------------");
+}
+#endif
+
+/* assuming that target node (np->n_right) exists */
+static void qtree_lrotate(t_qtree *tree, t_qnode *np)
+{
+ t_qnode *target = np->n_right;
+ if (np->n_right = target->n_left)
+ np->n_right->n_parent = np;
+ if (!(target->n_parent = np->n_parent))
+ tree->t_root = target;
+ else if (np == np->n_parent->n_left)
+ np->n_parent->n_left = target;
+ else
+ np->n_parent->n_right = target;
+ target->n_left = np;
+ np->n_parent = target;
+}
+
+/* assuming that target node (np->n_left) exists */
+static void qtree_rrotate(t_qtree *tree, t_qnode *np)
+{
+ t_qnode *target = np->n_left;
+ if (np->n_left = target->n_right)
+ np->n_left->n_parent = np;
+ if (!(target->n_parent = np->n_parent))
+ tree->t_root = target;
+ else if (np == np->n_parent->n_left)
+ np->n_parent->n_left = target;
+ else
+ np->n_parent->n_right = target;
+ target->n_right = np;
+ np->n_parent = target;
+}
+
+static t_qnode *qtree_preinserthook(t_qnode *np)
+{
+ while (np->n_prev && np->n_prev->n_key == np->n_key)
+ np = np->n_prev;
+ if (np->n_left)
+ {
+ np = np->n_prev;
+ if (np->n_right)
+ {
+ /* LATER revisit */
+ bug("qtree_preinserthook");
+ return (0); /* do nothing */
+ }
+ }
+ return (np);
+}
+
+static t_qnode *qtree_postinserthook(t_qnode *np)
+{
+ while (np->n_next && np->n_next->n_key == np->n_key)
+ np = np->n_next;
+ if (np->n_right)
+ {
+ np = np->n_next;
+ if (np->n_left)
+ {
+ /* LATER revisit */
+ bug("qtree_postinserthook");
+ return (0); /* do nothing */
+ }
+ }
+ return (np);
+}
+
+/* Returns a newly inserted or already existing node (or 0 if allocation
+ failed). A caller is responsible for assigning a value. If hook is
+ supplied, it is called iff key is found. In case of key being found
+ (which means foundp returns 1), a new node is inserted, unless hook is
+ either empty, or returns null. Hook's nonempty return is the parent
+ for the new node. It is expected to have no more than one child. */
+static t_qnode *qtree_doinsert(t_qtree *tree, double key,
+ t_qtree_inserthook hook, int *foundp)
+{
+ t_qnode *np, *parent, *result;
+ int leftchild;
+ *foundp = 0;
+ if (!(np = tree->t_root))
+ {
+ if (!(np = getbytes(tree->t_nodesize)))
+ return (0);
+ np->n_key = key;
+ np->n_black = 1;
+ tree->t_root = tree->t_first = tree->t_last = np;
+ return (np);
+ }
+
+ do
+ {
+ if (np->n_key == key)
+ {
+ *foundp = 1;
+ if (hook && (parent = (*hook)(np)))
+ {
+ if (parent->n_left && parent->n_right)
+ {
+ bug("qtree_insert, callback return 1");
+ parent = parent->n_next;
+ }
+ if (leftchild = (key < parent->n_key))
+ {
+ if (parent->n_left)
+ {
+ bug("qtree_insert, callback return 2");
+ leftchild = 0;
+ }
+ }
+ else if (parent->n_right)
+ leftchild = 1;
+ goto addit;
+ }
+ else return (np); /* a caller may then keep or replace the value */
+ }
+ else parent = np;
+ }
+ while (np = (key < np->n_key ? np->n_left : np->n_right));
+ leftchild = (key < parent->n_key);
+addit:
+ /* parent has no more than one child, new node becomes
+ parent's immediate successor or predecessor */
+ if (!(np = getbytes(tree->t_nodesize)))
+ return (0);
+ np->n_key = key;
+ np->n_parent = parent;
+ if (leftchild)
+ {
+ parent->n_left = np;
+ /* update the auxiliary linked list structure */
+ np->n_next = parent;
+ if (np->n_prev = parent->n_prev)
+ np->n_prev->n_next = np;
+ else
+ tree->t_first = np;
+ parent->n_prev = np;
+ }
+ else
+ {
+ parent->n_right = np;
+ /* update the auxiliary linked list structure */
+ np->n_prev = parent;
+ if (np->n_next = parent->n_next)
+ np->n_next->n_prev = np;
+ else
+ tree->t_last = np;
+ parent->n_next = np;
+ }
+ result = np;
+
+ /* balance the tree -- LATER clean this if possible... */
+ np->n_black = 0;
+ while (np != tree->t_root && !np->n_parent->n_black)
+ {
+ t_qnode *uncle;
+ /* np->n_parent->n_parent exists (we always paint root node in black) */
+ if (np->n_parent == np->n_parent->n_parent->n_left)
+ {
+ uncle = np->n_parent->n_parent->n_right;
+ if (!uncle /* (sentinel not used) */
+ || uncle->n_black)
+ {
+ if (np == np->n_parent->n_right)
+ {
+ np = np->n_parent;
+ qtree_lrotate(tree, np);
+ }
+ np->n_parent->n_black = 1;
+ np->n_parent->n_parent->n_black = 0;
+ qtree_rrotate(tree, np->n_parent->n_parent);
+ }
+ else
+ {
+ np->n_parent->n_black = 1;
+ uncle->n_black = 1;
+ np = np->n_parent->n_parent;
+ np->n_black = 0;
+ }
+ }
+ else
+ {
+ uncle = np->n_parent->n_parent->n_left;
+ if (!uncle /* (sentinel not used) */
+ || uncle->n_black)
+ {
+ if (np == np->n_parent->n_left)
+ {
+ np = np->n_parent;
+ qtree_rrotate(tree, np);
+ }
+ np->n_parent->n_black = 1;
+ np->n_parent->n_parent->n_black = 0;
+ qtree_lrotate(tree, np->n_parent->n_parent);
+ }
+ else
+ {
+ np->n_parent->n_black = 1;
+ uncle->n_black = 1;
+ np = np->n_parent->n_parent;
+ np->n_black = 0;
+ }
+ }
+ }
+ tree->t_root->n_black = 1;
+ return (result);
+}
+
+/* assuming that requested node exists */
+void qtree_delete(t_qtree *tree, t_qnode *gone)
+{
+ t_qnode *parent; /* parent of gone, after relinking */
+ t_qnode *child; /* gone's only child (or null), after relinking */
+ /* gone has to be the parent of no more than one child */
+ if (gone->n_left && gone->n_right)
+ {
+ /* Successor is the new parent of gone's children, and a new child
+ of gone's parent (if any). Successor always exists in this context,
+ and it has no left child. The simplistic scheme is to replace
+ gone's fields with successor's fields, and delete the successor.
+ We cannot do so, however, because successor may be pointed at... */
+ t_qnode *successor = gone->n_next;
+ child = successor->n_right;
+ successor->n_left = gone->n_left;
+ successor->n_left->n_parent = successor;
+ if (successor == gone->n_right)
+ parent = successor;
+ else
+ {
+ /* successor's parent always exists in this context,
+ successor is the left child of its parent */
+ parent = successor->n_parent;
+ parent->n_left = child;
+ if (child) /* (sentinel not used) */
+ child->n_parent = parent;
+ successor->n_right = gone->n_right;
+ successor->n_right->n_parent = successor;
+ }
+ if (gone->n_parent)
+ {
+ int swp;
+ if (gone == gone->n_parent->n_left)
+ gone->n_parent->n_left = successor;
+ else
+ gone->n_parent->n_right = successor;
+ successor->n_parent = gone->n_parent;
+ swp = gone->n_black;
+ gone->n_black = successor->n_black;
+ successor->n_black = swp;
+ }
+ else
+ {
+ tree->t_root = successor;
+ successor->n_parent = 0;
+ gone->n_black = successor->n_black;
+ successor->n_black = 1; /* LATER rethink */
+ }
+
+ /* update the auxiliary linked list structure */
+ if (successor->n_prev = gone->n_prev)
+ gone->n_prev->n_next = successor;
+ else
+ tree->t_first = successor;
+ }
+ else
+ {
+ /* update the auxiliary linked list structure */
+ if (gone->n_prev)
+ gone->n_prev->n_next = gone->n_next;
+ else
+ tree->t_first = gone->n_next;
+ if (gone->n_next)
+ gone->n_next->n_prev = gone->n_prev;
+ else
+ tree->t_last = gone->n_prev;
+
+ /* connect gone's child with gone's parent */
+ if (gone->n_left)
+ child = gone->n_left;
+ else
+ child = gone->n_right;
+ if (parent = gone->n_parent)
+ {
+ if (child) /* (sentinel not used) */
+ child->n_parent = parent;
+ if (gone == parent->n_left)
+ parent->n_left = child;
+ else
+ parent->n_right = child;
+ }
+ else
+ {
+ if (tree->t_root = child)
+ {
+ child->n_parent = 0;
+ child->n_black = 1; /* LATER rethink */
+ }
+ goto done;
+ }
+ }
+
+ if (gone->n_black)
+ {
+ /* balance the tree -- LATER clean this if possible... */
+ /* on entry: tree is not empty, parent always exists, child
+ not necessarily... */
+ while (child != tree->t_root &&
+ (!child || /* (sentinel not used) */
+ child->n_black))
+ {
+ t_qnode *other; /* another child of the same parent */
+ if (child == parent->n_left)
+ {
+ other = parent->n_right;
+ if (other && /* (sentinel not used) */
+ !other->n_black)
+ {
+ other->n_black = 1;
+ parent->n_black = 0;
+ qtree_lrotate(tree, parent);
+ other = parent->n_right;
+ }
+ if (!other || /* (sentinel not used) */
+ (!other->n_left || other->n_left->n_black) &&
+ (!other->n_right || other->n_right->n_black))
+ {
+ if (other) /* (sentinel not used) */
+ other->n_black = 0;
+ child = parent;
+ parent = parent->n_parent;
+ }
+ else
+ {
+ if (!other || /* (sentinel not used) */
+ !other->n_right || other->n_right->n_black)
+ {
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_left) other->n_left->n_black = 1;
+ other->n_black = 0;
+ qtree_rrotate(tree, other);
+ other = parent->n_right;
+ }
+ }
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_right) other->n_right->n_black = 1;
+ other->n_black = parent->n_black;
+ }
+ parent->n_black = 1;
+ qtree_lrotate(tree, parent);
+ tree->t_root->n_black = 1; /* LATER rethink */
+ goto done;
+ }
+ }
+ else /* right child */
+ {
+ other = parent->n_left;
+ if (other && /* (sentinel not used) */
+ !other->n_black)
+ {
+ other->n_black = 1;
+ parent->n_black = 0;
+ qtree_rrotate(tree, parent);
+ other = parent->n_left;
+ }
+ if (!other || /* (sentinel not used) */
+ (!other->n_left || other->n_left->n_black) &&
+ (!other->n_right || other->n_right->n_black))
+ {
+ if (other) /* (sentinel not used) */
+ other->n_black = 0;
+ child = parent;
+ parent = parent->n_parent;
+ }
+ else
+ {
+ if (!other || /* (sentinel not used) */
+ !other->n_left || other->n_left->n_black)
+ {
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_right) other->n_right->n_black = 1;
+ other->n_black = 0;
+ qtree_lrotate(tree, other);
+ other = parent->n_left;
+ }
+ }
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_left) other->n_left->n_black = 1;
+ other->n_black = parent->n_black;
+ }
+ parent->n_black = 1;
+ qtree_rrotate(tree, parent);
+ tree->t_root->n_black = 1; /* LATER rethink */
+ goto done;
+ }
+ }
+ }
+ if (child) /* (sentinel not used) */
+ child->n_black = 1;
+ }
+done:
+ freebytes(gone, tree->t_nodesize);
+#ifdef QTREE_DEBUG
+ qtree_verify(tree);
+#endif
+}
+
+t_qnode *qtree_search(t_qtree *tree, double key)
+{
+ t_qnode *np = tree->t_root;
+ while (np && np->n_key != key)
+ np = (key < np->n_key ? np->n_left : np->n_right);
+ return (np);
+}
+
+t_qnode *qtree_closest(t_qtree *tree, double key, int geqflag)
+{
+ t_qnode *np, *parent;
+ if (!(np = tree->t_root))
+ return (0);
+ do
+ if (np->n_key == key)
+ return (np);
+ else
+ parent = np;
+ while (np = (key < np->n_key ? np->n_left : np->n_right));
+ if (geqflag)
+ return (key > parent->n_key ? parent->n_next : parent);
+ else
+ return (key < parent->n_key ? parent->n_prev : parent);
+}
+
+t_qnode *qtree_insert(t_qtree *tree, double key, int *foundp)
+{
+ return (qtree_doinsert(tree, key, 0, foundp));
+}
+
+t_qnode *qtree_multiinsert(t_qtree *tree, double key, int fifoflag)
+{
+ int found;
+ return (qtree_doinsert(tree, key, (fifoflag ?
+ qtree_postinserthook :
+ qtree_preinserthook), &found));
+}
+
+t_qnode *qtree_insertfloat(t_qtree *tree, double key, t_float f,
+ int replaceflag)
+{
+ int found;
+ t_qnode *np = qtree_doinsert(tree, key, 0, &found);
+ if (np && (!found || replaceflag))
+ {
+ if (tree->t_valuetype == QTREETYPE_FLOAT)
+ {
+ t_qnode_float *npf = (t_qnode_float *)np;
+ npf->nf_value = f;
+ }
+ else if (tree->t_valuetype == QTREETYPE_ATOM)
+ {
+ t_qnode_atom *npa = (t_qnode_atom *)np;
+ t_atom *ap = &npa->na_value;
+ SETFLOAT(ap, f);
+ }
+ else bug("qtree_insertfloat");
+ }
+ return (np);
+}
+
+t_qnode *qtree_insertsymbol(t_qtree *tree, double key, t_symbol *s,
+ int replaceflag)
+{
+ int found;
+ t_qnode *np = qtree_doinsert(tree, key, 0, &found);
+ if (np && (!found || replaceflag))
+ {
+ if (tree->t_valuetype == QTREETYPE_SYMBOL)
+ {
+ t_qnode_symbol *nps = (t_qnode_symbol *)np;
+ nps->ns_value = s;
+ }
+ else if (tree->t_valuetype == QTREETYPE_ATOM)
+ {
+ t_qnode_atom *npa = (t_qnode_atom *)np;
+ t_atom *ap = &npa->na_value;
+ SETSYMBOL(ap, s);
+ }
+ else bug("qtree_insertsymbol");
+ }
+ return (np);
+}
+
+t_qnode *qtree_insertatom(t_qtree *tree, double key, t_atom *ap,
+ int replaceflag)
+{
+ int found;
+ t_qnode *np = qtree_doinsert(tree, key, 0, &found);
+ if (np && (!found || replaceflag))
+ {
+ if (tree->t_valuetype == QTREETYPE_ATOM)
+ {
+ t_qnode_atom *npa = (t_qnode_atom *)np;
+ npa->na_value = *ap;
+ }
+ else bug("qtree_insertatom");
+ }
+ return (np);
+}
+
+/* LATER preallocate 'freecount' nodes */
+static void qtree_doinit(t_qtree *tree, t_qtreetype vtype,
+ size_t nodesize, int freecount)
+{
+ tree->t_root = tree->t_first = tree->t_last = 0;
+ tree->t_valuetype = vtype;
+ tree->t_nodesize = nodesize;
+}
+
+void qtree_inittyped(t_qtree *tree, t_qtreetype vtype, int freecount)
+{
+ size_t nsize;
+ switch (vtype)
+ {
+ case QTREETYPE_FLOAT:
+ nsize = sizeof(t_qnode_float);
+ break;
+ case QTREETYPE_SYMBOL:
+ nsize = sizeof(t_qnode_symbol);
+ break;
+ case QTREETYPE_ATOM:
+ nsize = sizeof(t_qnode_atom);
+ break;
+ default:
+ bug("qtree_inittyped");
+ vtype = QTREETYPE_ILLEGAL;
+ nsize = sizeof(t_qnode);
+ }
+ qtree_doinit(tree, vtype, nsize, freecount);
+}
+
+void qtree_initcustom(t_qtree *tree, size_t nodesize, int freecount)
+{
+ qtree_doinit(tree, QTREETYPE_CUSTOM, nodesize, freecount);
+}
+
+/* LATER keep and/or preallocate 'freecount' nodes (if negative, keep all) */
+void qtree_clear(t_qtree *tree, int freecount)
+{
+ t_qnode *np, *next = tree->t_first;
+ while (next)
+ {
+ np = next;
+ next = next->n_next;
+ freebytes(np, tree->t_nodesize);
+ }
+ qtree_doinit(tree, tree->t_valuetype, tree->t_nodesize, 0);
+}