629 lines
15 KiB
C++
629 lines
15 KiB
C++
/*
|
|
* 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 <stdlib.h>
|
|
|
|
#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 */
|
|
}
|