/* * Libitree: an interval tree library in C * * Copyright (C) 2018 Alessandro Vullo * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* Interval Tree library This is an adaptation of the AVL balanced tree C library created by Julienne Walker which can be found here: http://www.eternallyconfuzzled.com/Libraries.aspx */ #include "itree.h" #include #ifndef HEIGHT_LIMIT #define HEIGHT_LIMIT 64 /* Tallest allowable tree */ #endif typedef struct itreenode { int balance; /* Balance factor */ uint64_t max; /* Maximum high value in the subtree rooted at this node */ interval_t *interval; /* The interval the node represents */ struct itreenode *link[2]; /* Left (0) and right (1) links */ } itreenode_t; struct itree { itreenode_t *root; /* Top of the tree */ dup_f dup; /* Clone an interval data item (user-defined) */ rel_f rel; /* Destroy an interval data item (user-defined) */ size_t size; /* Number of items (user-defined) */ }; struct itreetrav { itree_t *tree; /* Paired tree */ itreenode_t *it; /* Current node */ itreenode_t *path[HEIGHT_LIMIT]; /* Traversal path */ size_t top; /* Top of stack */ }; /* Two way single rotation */ #define single(root, dir) \ do \ { \ itreenode_t *save = root->link[!dir]; \ root->link[!dir] = save->link[dir]; \ save->link[dir] = root; \ root = save; \ } while (0) /* Two way double rotation */ #define double(root, dir) \ do \ { \ itreenode_t *save = root->link[!dir]->link[dir]; \ root->link[!dir]->link[dir] = save->link[!dir]; \ save->link[!dir] = root->link[!dir]; \ root->link[!dir] = save; \ save = root->link[!dir]; \ root->link[!dir] = save->link[dir]; \ save->link[dir] = root; \ root = save; \ } while (0) /* Adjust balance before double rotation */ #define adjust_balance(root, dir, bal) \ do \ { \ itreenode_t *n = root->link[dir]; \ itreenode_t *nn = n->link[!dir]; \ if (nn->balance == 0) \ { \ root->balance = n->balance = 0; \ } \ else if (nn->balance == bal) \ { \ root->balance = -bal; \ n->balance = 0; \ } \ else \ { /* nn->balance == -bal */ \ root->balance = 0; \ n->balance = bal; \ } \ nn->balance = 0; \ } while (0) /* Rebalance after insertion */ #define insert_balance(root, dir) \ do \ { \ itreenode_t *n = root->link[dir]; \ int bal = dir == 0 ? -1 : +1; \ if (n->balance == bal) \ { \ root->balance = n->balance = 0; \ single(root, !dir); \ } \ else \ { /* n->balance == -bal */ \ adjust_balance(root, dir, bal); \ double(root, !dir); \ } \ } while (0) /* Rebalance after deletion */ #define remove_balance(root, dir, done) \ do \ { \ itreenode_t *n = root->link[!dir]; \ int bal = dir == 0 ? -1 : +1; \ if (n->balance == -bal) \ { \ root->balance = n->balance = 0; \ single(root, dir); \ } \ else if (n->balance == bal) \ { \ adjust_balance(root, !dir, -bal); \ double(root, dir); \ } \ else \ { /* n->balance == 0 */ \ root->balance = -bal; \ n->balance = bal; \ single(root, dir); \ done = 1; \ } \ } while (0) static itreenode_t *new_node(itree_t *tree, interval_t *i) { itreenode_t *rn = (itreenode_t *)malloc(sizeof *rn); if (rn == NULL) { return NULL; } rn->interval = (interval_t *)malloc(sizeof(interval_t)); if (rn->interval == NULL) { return NULL; } rn->interval->low = i->low; rn->interval->high = i->high; rn->interval->data = tree->dup(i->data); rn->balance = 0; rn->max = i->high; rn->link[0] = rn->link[1] = NULL; return rn; } itree_t *itree_new(dup_f dup, rel_f rel) { itree_t *rt = (itree_t *)malloc(sizeof *rt); if (rt == NULL) { return NULL; } rt->root = NULL; rt->dup = dup; rt->rel = rel; rt->size = 0; return rt; } void itree_delete(itree_t *tree) { itreenode_t *it = tree->root; itreenode_t *save; /* Destruction by rotation */ while (it != NULL) { if (it->link[0] == NULL) { /* Remove node */ save = it->link[1]; tree->rel(it->interval->data); free(it->interval); free(it); } else { /* Rotate right */ save = it->link[0]; it->link[0] = save->link[1]; save->link[1] = it; } it = save; } free(tree); } interval_t *itree_find(itree_t *tree, interval_t *interval) { itreenode_t *it = tree->root; while (it != NULL) { /* int cmp = tree->cmp ( it->data, data ); */ /* if ( cmp == 0 ) */ /* break; */ /* it = it->link[cmp < 0]; */ if (interval_overlap(it->interval, interval)) { break; } it = it->link[it->link[0] == NULL || it->link[0]->max < interval->low]; } return it == NULL ? NULL : it->interval; } void search(itreenode_t *node, interval_t *interval, ilist_t *results) { /* * If interval is to the right of the rightmost point of any interval * in this node and all its children, there won't be any matches */ if (node == NULL || interval->low > node->max) { return; } /* search the left subtree */ if (node->link[0] != NULL && node->link[0]->max >= interval->low) { search(node->link[0], interval, results); } /* search this node */ if (interval_overlap(node->interval, interval)) { ilist_append(results, node->interval); } /* * if interval is to the left of the start of this interval * it can't be in any child to the right */ if (interval->high < node->interval->low) { return; } /* search the right subtree */ search(node->link[1], interval, results); } ilist_t *itree_findall(itree_t *tree, interval_t *interval) { ilist_t *results = ilist_new(); if (results != NULL) { /* empty tree case */ if (tree->root == NULL) { return results; } search(tree->root, interval, results); } return results; } int itree_insert(itree_t *tree, interval_t *interval) { /* Empty tree case */ if (tree->root == NULL) { tree->root = new_node(tree, interval); if (tree->root == NULL) { return 0; } } else { itreenode_t head = {0}; /* Temporary tree root */ itreenode_t *s, *t; /* Place to rebalance and parent */ itreenode_t *p, *q; /* Iterator and save pointer to the newly inserted node */ int dir; /* Set up false root to ease maintenance */ t = &head; t->link[1] = tree->root; /* Search down the tree, saving rebalance points */ for (s = p = t->link[1];; p = q) { /* Duplicates admitted, placed in the right subtree */ dir = p->interval->low <= interval->low; /* tree->cmp ( p->data, data ) < 0; */ q = p->link[dir]; p->max = p->max < interval->high ? interval->high : p->max; /* Update ancestor's max if needed */ if (q == NULL) { break; } if (q->balance != 0) { t = p; s = q; } } p->link[dir] = q = new_node(tree, interval); if (q == NULL) { return 0; /* TODO: should rollback to previous ancestors' max values */ } /* Update balance factors */ for (p = s; p != q; p = p->link[dir]) { dir = p->interval->low <= interval->low; /* tree->cmp ( p->data, data ) < 0; */ p->balance += dir == 0 ? -1 : +1; } q = s; /* Save rebalance point for parent fix */ /* Rebalance if necessary */ if (abs(s->balance) > 1) { dir = s->interval->low <= interval->low; /* tree->cmp ( s->data, data ) < 0; */ insert_balance(s, dir); } /* Fix parent */ if (q == head.link[1]) { tree->root = s; } else { t->link[q == t->link[1]] = s; } } ++tree->size; return 1; } int itree_remove(itree_t *tree, interval_t *interval) { if (tree->root != NULL) { itreenode_t *it, *up[HEIGHT_LIMIT]; int upd[HEIGHT_LIMIT], top = 0, top_max; int done = 0; it = tree->root; /* Search down tree and save path */ for (;;) { if (it == NULL) { return 0; } else if (interval_equal(it->interval, interval)) { break; } /* Push direction and node onto stack */ upd[top] = it->interval->low <= interval->low; /* tree->cmp ( it->data, data ) < 0; */ up[top++] = it; it = it->link[upd[top - 1]]; } /* Remove the node */ if (it->link[0] == NULL || it->link[1] == NULL) { /* Which child is not null? */ int dir = it->link[0] == NULL; /* Fix parent */ if (top != 0) { up[top - 1]->link[upd[top - 1]] = it->link[dir]; } else { tree->root = it->link[dir]; } tree->rel(it->interval->data); free(it->interval); free(it); } else { /* Find the inorder successor */ itreenode_t *heir = it->link[1]; void *save; /* Save this path too */ upd[top] = 1; up[top++] = it; while (heir->link[0] != NULL) { upd[top] = 0; up[top++] = heir; heir = heir->link[0]; } /* Swap data */ save = it->interval; it->interval = heir->interval; heir->interval = (interval_t *)save; /* Unlink successor and fix parent */ up[top - 1]->link[up[top - 1] == it] = heir->link[1]; tree->rel(heir->interval->data); free(heir->interval); free(heir); } /* Update max: walk back up the search path and bubbles up to root */ top_max = top; while (--top_max >= 0) { itreenode_t *left = up[top_max]->link[0], *right = up[top_max]->link[1]; if (left != NULL && right != NULL) { uint64_t left_right_max = left->max < right->max ? right->max : left->max; up[top_max]->max = up[top_max]->interval->high < left_right_max ? left_right_max : up[top_max]->interval->high; } else if (left != NULL && right == NULL) { up[top_max]->max = up[top_max]->interval->high < left->max ? left->max : up[top_max]->interval->high; } else if (left == NULL && right != NULL) { up[top_max]->max = up[top_max]->interval->high < right->max ? right->max : up[top_max]->interval->high; } else { up[top_max]->max = up[top_max]->interval->high; } } /* Walk back up the search path */ while (--top >= 0 && !done) { /* Update balance factors */ up[top]->balance += upd[top] != 0 ? -1 : +1; /* Terminate or rebalance as necessary */ if (abs(up[top]->balance) == 1) { break; } else if (abs(up[top]->balance) > 1) { remove_balance(up[top], upd[top], done); /* Fix parent */ if (top != 0) { up[top - 1]->link[upd[top - 1]] = up[top]; } else { tree->root = up[0]; } } } --tree->size; } return 1; } size_t itree_size(itree_t *tree) { return tree->size; } itreetrav_t *itreetnew(void) { return (itreetrav_t *)malloc(sizeof(itreetrav_t)); } void itreetdelete(itreetrav_t *trav) { free(trav); } /* First step in traversal, handles min and max */ static interval_t *start(itreetrav_t *trav, itree_t *tree, int dir) { trav->tree = tree; trav->it = tree->root; trav->top = 0; /* Build a path to work with */ if (trav->it != NULL) { while (trav->it->link[dir] != NULL) { trav->path[trav->top++] = trav->it; trav->it = trav->it->link[dir]; } } #ifdef DEBUG if (trav->it) printf("[%.1f, %.1f] (%d) (%.1f)\n", trav->it->interval->low, trav->it->interval->high, *(int *)trav->it->interval->data, trav->it->max); #endif return trav->it == NULL ? NULL : trav->it->interval; } /* Subsequent traversal steps, handles ascending and descending */ static interval_t *move(itreetrav_t *trav, int dir) { if (trav->it->link[dir] != NULL) { /* Continue down this branch */ trav->path[trav->top++] = trav->it; trav->it = trav->it->link[dir]; while (trav->it->link[!dir] != NULL) { trav->path[trav->top++] = trav->it; trav->it = trav->it->link[!dir]; } } else { /* Move to the next branch */ itreenode_t *last; do { if (trav->top == 0) { trav->it = NULL; { break; } } last = trav->it; trav->it = trav->path[--trav->top]; } while (last == trav->it->link[dir]); } #ifdef DEBUG if (trav->it) printf("[%.1f, %.1f] (%d) (%.1f)\n", trav->it->interval->low, trav->it->interval->high, *(int *)trav->it->interval->data, trav->it->max); #endif return trav->it == NULL ? NULL : trav->it->interval; } interval_t *itreetfirst(itreetrav_t *trav, itree_t *tree) { return start(trav, tree, 0); /* Min value */ } interval_t *itreetlast(itreetrav_t *trav, itree_t *tree) { return start(trav, tree, 1); /* Max value */ } interval_t *itreetnext(itreetrav_t *trav) { return move(trav, 1); /* Toward larger items */ } interval_t *itreetprev(itreetrav_t *trav) { return move(trav, 0); /* Toward smaller items */ }