/* PSPP - a program for statistical analysis. Copyright (C) 2007, 2009, 2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Augmented binary tree (ABT) data structure. */ /* These library routines have no external dependencies other than the standard C library. If you add routines in this file, please add a corresponding test to abt-test.c. This test program should achieve 100% coverage of lines and branches in this code, as reported by "gcov -b". */ #ifdef HAVE_CONFIG_H #include #endif #include "libpspp/abt.h" #include #include "libpspp/cast.h" #include "libpspp/assertion.h" static struct abt_node **down_link (struct abt *, struct abt_node *); static struct abt_node *skew (struct abt *, struct abt_node *); static struct abt_node *split (struct abt *, struct abt_node *); /* Initializes ABT as an empty ABT that uses COMPARE and REAUGMENT functions, passing in AUX as auxiliary data. The comparison function is optional. If it is null, this indicates that the tree is being used for its augmentations only. ABT functions that compare nodes may not be used with trees that lack comparison functions; contrariwise, other functions that could disrupt the ordering of a tree may not be used if a comparison function is specified. Refer to individual function descriptions for details. */ void abt_init (struct abt *abt, abt_compare_func *compare, abt_reaugment_func *reaugment, const void *aux) { assert (reaugment != NULL); abt->root = NULL; abt->compare = compare; abt->reaugment = reaugment; abt->aux = aux; } /* Inserts the given NODE into ABT. Returns a null pointer if successful. Returns the existing node already in ABT equal to NODE, on failure. This function may be used only if ABT has a comparison function. */ struct abt_node * abt_insert (struct abt *abt, struct abt_node *node) { node->down[0] = NULL; node->down[1] = NULL; node->level = 1; if (abt->root == NULL) { abt->root = node; node->up = NULL; abt_reaugmented (abt, node); } else { struct abt_node *p = abt->root; for (;;) { int cmp, dir; cmp = abt->compare (node, p, abt->aux); if (cmp == 0) return p; dir = cmp > 0; if (p->down[dir] == NULL) { p->down[dir] = node; node->up = p; abt_reaugmented (abt, node); break; } p = p->down[dir]; } } while ((node = node->up) != NULL) { node = skew (abt, node); node = split (abt, node); } return NULL; } /* Inserts NODE before or after node P in ABT, depending on the value of AFTER. If P is null, then the node is inserted as the first node in the tree, if AFTER is true, or the last node, if AFTER is false. */ static inline void insert_relative (struct abt *abt, const struct abt_node *p, bool after, struct abt_node *node) { node->down[0] = NULL; node->down[1] = NULL; node->level = 1; if (abt->root == NULL) { assert (p == NULL); abt->root = node; node->up = NULL; abt_reaugmented (abt, node); } else { int dir = after; if (p == NULL) { p = abt->root; dir = !after; } while (p->down[dir] != NULL) { p = p->down[dir]; dir = !after; } CONST_CAST (struct abt_node *, p)->down[dir] = node; node->up = CONST_CAST (struct abt_node *, p); abt_reaugmented (abt, node); } while ((node = node->up) != NULL) { node = skew (abt, node); node = split (abt, node); } } /* Inserts NODE after node P in ABT. If P is null, then the node is inserted as the first node in the tree. This function may be used only if ABT lacks a comparison function. */ void abt_insert_after (struct abt *abt, const struct abt_node *p, struct abt_node *node) { assert (abt->compare == NULL); insert_relative (abt, p, true, node); } /* Inserts NODE before node P in ABT. If P is null, then the node is inserted as the last node in the tree. This function may be used only if ABT lacks a comparison function. */ void abt_insert_before (struct abt *abt, const struct abt_node *p, struct abt_node *node) { assert (abt->compare == NULL); insert_relative (abt, p, false, node); } /* Deletes P from ABT. */ void abt_delete (struct abt *abt, struct abt_node *p) { struct abt_node **q = down_link (abt, p); struct abt_node *r = p->down[1]; if (r == NULL) { *q = NULL; p = p->up; } else if (r->down[0] == NULL) { r->down[0] = p->down[0]; *q = r; r->up = p->up; if (r->down[0] != NULL) r->down[0]->up = r; r->level = p->level; p = r; } else { struct abt_node *s = r->down[0]; while (s->down[0] != NULL) s = s->down[0]; r = s->up; r->down[0] = s->down[1]; s->down[0] = p->down[0]; s->down[1] = p->down[1]; *q = s; s->down[0]->up = s; s->down[1]->up = s; s->up = p->up; s->level = p->level; if (r->down[0] != NULL) r->down[0]->up = r; p = r; } abt_reaugmented (abt, p); for (; p != NULL; p = p->up) if ((p->down[0] != NULL ? p->down[0]->level : 0) < p->level - 1 || (p->down[1] != NULL ? p->down[1]->level : 0) < p->level - 1) { p->level--; if (p->down[1] != NULL && p->down[1]->level > p->level) p->down[1]->level = p->level; p = skew (abt, p); skew (abt, p->down[1]); if (p->down[1]->down[1] != NULL) skew (abt, p->down[1]->down[1]); p = split (abt, p); split (abt, p->down[1]); } } /* Returns the node with minimum value in ABT, or a null pointer if ABT is empty. */ struct abt_node * abt_first (const struct abt *abt) { struct abt_node *p = abt->root; if (p != NULL) while (p->down[0] != NULL) p = p->down[0]; return p; } /* Returns the node with maximum value in ABT, or a null pointer if ABT is empty. */ struct abt_node * abt_last (const struct abt *abt) { struct abt_node *p = abt->root; if (p != NULL) while (p->down[1] != NULL) p = p->down[1]; return p; } /* Searches ABT for a node equal to TARGET. Returns the node if found, or a null pointer otherwise. This function may be used only if ABT has a comparison function. */ struct abt_node * abt_find (const struct abt *abt, const struct abt_node *target) { const struct abt_node *p; int cmp; for (p = abt->root; p != NULL; p = p->down[cmp > 0]) { cmp = abt->compare (target, p, abt->aux); if (cmp == 0) return CONST_CAST (struct abt_node *, p); } return NULL; } /* Returns the node in ABT following P in in-order. If P is null, returns the minimum node in ABT. Returns a null pointer if P is the maximum node in ABT or if P is null and ABT is empty. */ struct abt_node * abt_next (const struct abt *abt, const struct abt_node *p) { if (p == NULL) return abt_first (abt); else if (p->down[1] == NULL) { struct abt_node *q; for (q = p->up; ; p = q, q = q->up) if (q == NULL || p == q->down[0]) return q; } else { p = p->down[1]; while (p->down[0] != NULL) p = p->down[0]; return CONST_CAST (struct abt_node *, p); } } /* Returns the node in ABT preceding P in in-order. If P is null, returns the maximum node in ABT. Returns a null pointer if P is the minimum node in ABT or if P is null and ABT is empty. */ struct abt_node * abt_prev (const struct abt *abt, const struct abt_node *p) { if (p == NULL) return abt_last (abt); else if (p->down[0] == NULL) { struct abt_node *q; for (q = p->up; ; p = q, q = q->up) if (q == NULL || p == q->down[1]) return q; } else { p = p->down[0]; while (p->down[1] != NULL) p = p->down[1]; return CONST_CAST (struct abt_node *, p); } } /* Calls ABT's reaugmentation function to compensate for augmentation data in P having been modified. Use abt_changed, instead, if the key data in P has changed. It is not safe to update more than one node's augmentation data, then to call this function for each node. Instead, update a single node's data, call this function, update another node's data, and so on. Alternatively, remove all affected nodes from the tree, update their values, then re-insert all of them. */ void abt_reaugmented (const struct abt *abt, struct abt_node *p) { for (; p != NULL; p = p->up) abt->reaugment (p, abt->aux); } /* Moves P around in ABT to compensate for its key having changed. Returns a null pointer if successful. If P's new value is equal to that of some other node in ABT, returns the other node after removing P from ABT. This function is an optimization only if it is likely that P can actually retain its relative position in ABT, e.g. its key has only been adjusted slightly. Otherwise, it is more efficient to simply remove P from ABT, change its key, and re-insert P. It is not safe to update more than one node's key, then to call this function for each node. Instead, update a single node's key, call this function, update another node's key, and so on. Alternatively, remove all affected nodes from the tree, update their keys, then re-insert all of them. This function may be used only if ABT has a comparison function. If it doesn't, then you probably just want abt_reaugmented. */ struct abt_node * abt_changed (struct abt *abt, struct abt_node *p) { struct abt_node *prev = abt_prev (abt, p); struct abt_node *next = abt_next (abt, p); if ((prev != NULL && abt->compare (prev, p, abt->aux) >= 0) || (next != NULL && abt->compare (p, next, abt->aux) >= 0)) { abt_delete (abt, p); return abt_insert (abt, p); } else { abt_reaugmented (abt, p); return NULL; } } /* ABT nodes may be moved around in memory as necessary, e.g. as the result of an realloc operation on a block that contains a node. Once this is done, call this function passing node P that was moved and its ABT before attempting any other operation on ABT. It is not safe to move more than one node, then to call this function for each node. Instead, move a single node, call this function, move another node, and so on. Alternatively, remove all affected nodes from the tree, move them, then re-insert all of them. This function may be used only if ABT has a comparison function. */ void abt_moved (struct abt *abt, struct abt_node *p) { if (p->up != NULL) { int d = p->up->down[0] == NULL || abt->compare (p, p->up, abt->aux) > 0; p->up->down[d] = p; } else abt->root = p; if (p->down[0] != NULL) p->down[0]->up = p; if (p->down[1] != NULL) p->down[1]->up = p; } /* Returns the address of the pointer that points down to P within ABT. */ static struct abt_node ** down_link (struct abt *abt, struct abt_node *p) { return (p->up != NULL ? &p->up->down[p->up->down[0] != p] : &abt->root); } /* Remove a left "horizontal link" at A, if present. Returns the node that occupies the position previously occupied by A. */ static struct abt_node * skew (struct abt *abt, struct abt_node *a) { struct abt_node *b = a->down[0]; if (b != NULL && b->level == a->level) { /* Rotate right. */ a->down[0] = b->down[1]; b->down[1] = a; *down_link (abt, a) = b; if (a->down[0] != NULL) a->down[0]->up = a; b->up = a->up; a->up = b; abt->reaugment (a, abt->aux); abt->reaugment (b, abt->aux); return b; } else return a; } /* Removes a pair of consecutive right "horizontal links" at A, if present. Returns the node that occupies the position previously occupied by A. */ static struct abt_node * split (struct abt *abt, struct abt_node *a) { struct abt_node *b = a->down[1]; if (b != NULL && b->down[1] != NULL && b->down[1]->level == a->level) { /* Rotate left. */ a->down[1] = b->down[0]; b->down[0] = a; *down_link (abt, a) = b; if (a->down[1] != NULL) a->down[1]->up = a; b->up = a->up; a->up = b; b->level++; abt->reaugment (a, abt->aux); abt->reaugment (b, abt->aux); return b; } else return a; }