diff options
| author | luwenpeng <[email protected]> | 2024-03-21 10:06:11 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2024-03-21 10:06:11 +0800 |
| commit | 36b9a8282ac00808e5ae60328ee80bd91348beed (patch) | |
| tree | e3a922bd3dd6e230c8327d8bb69ea9ca2c1d67ac /deps/interval_tree | |
| parent | 1cdbb7c2a4b1317e7dfef7c6a18e5ee892f09de6 (diff) | |
Add interval tree
Diffstat (limited to 'deps/interval_tree')
| -rw-r--r-- | deps/interval_tree/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | deps/interval_tree/interval.cpp | 71 | ||||
| -rw-r--r-- | deps/interval_tree/interval.h | 59 | ||||
| -rw-r--r-- | deps/interval_tree/interval_list.cpp | 277 | ||||
| -rw-r--r-- | deps/interval_tree/interval_list.h | 57 | ||||
| -rw-r--r-- | deps/interval_tree/itree.cpp | 629 | ||||
| -rw-r--r-- | deps/interval_tree/itree.h | 77 | ||||
| -rw-r--r-- | deps/interval_tree/test/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | deps/interval_tree/test/gtest_interval_tree.cpp | 336 |
9 files changed, 1515 insertions, 0 deletions
diff --git a/deps/interval_tree/CMakeLists.txt b/deps/interval_tree/CMakeLists.txt new file mode 100644 index 0000000..7a6de00 --- /dev/null +++ b/deps/interval_tree/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(interval_tree STATIC interval.cpp interval_list.cpp itree.cpp) +target_include_directories(interval_tree PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + +add_subdirectory(test)
\ No newline at end of file diff --git a/deps/interval_tree/interval.cpp b/deps/interval_tree/interval.cpp new file mode 100644 index 0000000..bdcebaa --- /dev/null +++ b/deps/interval_tree/interval.cpp @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#include "interval.h" + +#include <stdlib.h> + +interval_t *interval_new(uint64_t low, uint64_t high, void *data, dup_f dup, rel_f rel) +{ + interval_t *ri = (interval_t *)malloc(sizeof *ri); + if (ri == NULL) + { + return NULL; + } + + ri->low = low; + ri->high = high; + ri->dup = dup; + ri->rel = rel; + ri->data = ri->dup(data); + + return ri; +} + +interval_t *interval_copy(const interval_t *i) +{ + return interval_new(i->low, i->high, i->data, i->dup, i->rel); +} + +void interval_delete(interval_t *i) +{ + if (i != NULL) + { + if (i->data) + { + i->rel(i->data); + } + free(i); + } +} + +int interval_overlap(const interval_t *i1, const interval_t *i2) +{ + return i1->low <= i2->high && i2->low <= i1->high; +} + +int interval_equal(const interval_t *i1, const interval_t *i2) +{ + return i1->low == i2->low && i1->high == i2->high; +} diff --git a/deps/interval_tree/interval.h b/deps/interval_tree/interval.h new file mode 100644 index 0000000..4504c7c --- /dev/null +++ b/deps/interval_tree/interval.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef _INTERVAL_H_ +#define _INTERVAL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stddef.h> +#include <stdint.h> + +/* User-defined item handling */ +typedef void *(*dup_f)(void *p); +typedef void (*rel_f)(void *p); + +typedef struct interval +{ + uint64_t low, high; /* Interval boundaries, inclusive */ + void *data; /* User-defined content */ + dup_f dup; /* Clone an interval data item */ + rel_f rel; /* Destroy an interval data item */ +} interval_t; + +/* Interval functions */ +interval_t *interval_new(uint64_t low, uint64_t high, void *data, dup_f dup, rel_f rel); +interval_t *interval_copy(const interval_t *i); +void interval_delete(interval_t *i); +int interval_overlap(const interval_t *i1, const interval_t *i2); +int interval_equal(const interval_t *i1, const interval_t *i2); + +#ifdef __cplusplus +} +#endif + +#endif /* _INTERVAL_H_ */ diff --git a/deps/interval_tree/interval_list.cpp b/deps/interval_tree/interval_list.cpp new file mode 100644 index 0000000..6700564 --- /dev/null +++ b/deps/interval_tree/interval_list.cpp @@ -0,0 +1,277 @@ +/* + * 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. + */ + +#include "interval_list.h" + +#include <stdlib.h> +#include <stdio.h> + +#ifndef HEIGHT_LIMIT +#define HEIGHT_LIMIT 64 +#endif + +/* Note: doesn't own the interval, comes from the tree + which is responsible for deallocating the interval. */ +typedef struct ilistnode +{ + const interval_t *interval; + struct ilistnode *next; +} ilistnode_t; + +struct ilist +{ + ilistnode_t *head, *tail; /* Dummy nodes */ + size_t size; +}; + +struct ilisttrav +{ + ilist_t *list; /* Paired list */ + ilistnode_t *it; /* Current node */ +}; + +static ilistnode_t *ilistnode_new(const interval_t *i, ilistnode_t *next) +{ + ilistnode_t *node = (ilistnode_t *)malloc(sizeof *node); + if (node != NULL) + { + node->interval = i; /* NOTE: doesn't own the interval, pointer aliasing */ + node->next = next; + } + + return node; +} + +/* + Destroy a single node, assuming it's been unlinked. + Returns the next node specified by the link. +*/ +ilistnode_t *ilistnode_delete(ilistnode_t *node) +{ + ilistnode_t *next = NULL; + if (node != NULL) + { + /* Save a reference to the next node + since we're about to destroy this one */ + next = node->next; + free(node); + } + + return next; +} + +ilist_t *ilist_new() +{ + ilist_t *list = (ilist_t *)malloc(sizeof *list); + if (list != NULL) + { + ilistnode_t *tail = ilistnode_new(NULL, NULL); + if (tail == NULL) + { + free(list); + list = NULL; + } + else + { + list->tail = tail; + list->head = ilistnode_new(NULL, list->tail); + + if (list->head == NULL) + { + free(list); + list = NULL; + } + list->size = 0; + } + } + + return list; +} + +void ilist_delete(ilist_t *list) +{ + if (list == NULL) + { + return; + } + + ilistnode_t *it = list->head; + while (it != list->tail) + { + it = ilistnode_delete(it); + } + + ilistnode_delete(it); /* Delete tail */ + free(list); +} + +size_t ilist_size(ilist_t *list) +{ + return list->size; +} + +static ilistnode_t *insert_before(ilist_t *, ilistnode_t *, interval_t *); +static ilistnode_t *insert_after(ilist_t *list, ilistnode_t *pos, interval_t *i) +{ + ilistnode_t *node = NULL; + if (list != NULL && pos != NULL) + { + if (pos != list->tail) + { + node = ilistnode_new(i, pos->next); + if (node != NULL) + { + pos->next = node; + ++list->size; + } + } + else + { + node = insert_before(list, pos, i); + } + } + + return node; +} + +static ilistnode_t *insert_before(ilist_t *list, ilistnode_t *pos, interval_t *i) +{ + ilistnode_t *node = NULL; + if (list != NULL && pos != NULL) + { + if (pos != list->head) + { + /* Find the previous node */ + ilistnode_t *it = list->head; + while (it->next != pos) + { + it = it->next; + } + + /* Create a new node and set the new link */ + node = ilistnode_new(i, it->next); + if (node != NULL) + { + it->next = node; + ++list->size; + } + } + else + { + node = insert_after(list, pos, i); + } + } + + return node; +} + +int ilist_append(ilist_t *list, interval_t *i) +{ + + ilistnode_t *node = insert_before(list, list->tail, i); + if (node != NULL) + { + return 1; + } + + return 0; +} + +ilisttrav_t *ilisttrav_new(ilist_t *list) +{ + if (list == NULL) + { + return NULL; + } + + ilisttrav_t *trav = (ilisttrav_t *)malloc(sizeof(ilisttrav_t)); + if (trav != NULL) + { + trav->list = list; + } + + return trav; +} + +void ilisttrav_delete(ilisttrav_t *trav) +{ + free(trav); +} + +const interval_t *ilisttrav_first(ilisttrav_t *trav) +{ + if (trav->list == NULL) + { + return NULL; + } + + trav->it = trav->list->head->next; + + return trav->it == NULL || trav->it == trav->list->tail ? NULL : trav->it->interval; +} + +const interval_t *ilisttrav_last(ilisttrav_t *trav) +{ + if (trav->list == NULL) + { + return NULL; + } + + trav->it = trav->list->head; + while (trav->it->next != trav->list->tail) + { + trav->it = trav->it->next; + } + + return trav->it == trav->list->head || trav->it == NULL ? NULL : trav->it->interval; +} + +const interval_t *ilisttrav_next(ilisttrav_t *trav) +{ + if (trav == NULL) + { + return NULL; + } + + trav->it = trav->it->next; + return trav->it == NULL || trav->it == trav->list->tail ? NULL : trav->it->interval; +} + +/* Very inefficient, need a doubly linked list to properly support this */ +const interval_t *ilisttrav_prev(ilisttrav_t *trav) +{ + if (trav == NULL) + { + return NULL; + } + + ilistnode_t *it = trav->list->head; + while (it->next != trav->it) + { + it = it->next; + } + + trav->it = it; + return trav->it == trav->list->head || trav->it == NULL ? NULL : trav->it->interval; +} diff --git a/deps/interval_tree/interval_list.h b/deps/interval_tree/interval_list.h new file mode 100644 index 0000000..7333419 --- /dev/null +++ b/deps/interval_tree/interval_list.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef _INTERVAL_LIST_H_ +#define _INTERVAL_LIST_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "interval.h" + +/* Declarations for an interval list, opaque types */ +typedef struct ilist ilist_t; +typedef struct ilisttrav ilisttrav_t; + +/* Interval list functions */ +ilist_t *ilist_new(); +void ilist_delete(ilist_t *list); +size_t ilist_size(ilist_t *list); +int ilist_append(ilist_t *list, interval_t *i); + +/* Interval list traversal functions */ +ilisttrav_t *ilisttrav_new(ilist_t *list); +void ilisttrav_delete(ilisttrav_t *trav); +const interval_t *ilisttrav_first(ilisttrav_t *trav); +const interval_t *ilisttrav_last(ilisttrav_t *trav); +const interval_t *ilisttrav_next(ilisttrav_t *trav); +const interval_t *ilisttrav_prev(ilisttrav_t *trav); + +#ifdef __cplusplus +} +#endif + +#endif /* _INTERVAL_LIST_H_ */ diff --git a/deps/interval_tree/itree.cpp b/deps/interval_tree/itree.cpp new file mode 100644 index 0000000..5bb68ef --- /dev/null +++ b/deps/interval_tree/itree.cpp @@ -0,0 +1,629 @@ +/* + * 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)) + else if (it->interval->low >= interval->low && it->interval->high <= interval->high) + { + 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 */ +} diff --git a/deps/interval_tree/itree.h b/deps/interval_tree/itree.h new file mode 100644 index 0000000..505e272 --- /dev/null +++ b/deps/interval_tree/itree.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#ifndef _INTERVAL_TREE_H_ +#define _INTERVAL_TREE_H_ + +/* + 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 + +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "interval.h" +#include "interval_list.h" + +/* Opaque types */ +typedef struct itree itree_t; +typedef struct itreetrav itreetrav_t; + +/* User-defined interval data item handling */ +typedef void *(*dup_f)(void *p); +typedef void (*rel_f)(void *p); + +/* Interval tree functions */ +itree_t *itree_new(dup_f dup, rel_f rel); +void itree_delete(itree_t *tree); +// Find the first interval containing the specified interval +interval_t *itree_find(itree_t *tree, interval_t *interval); +ilist_t *itree_findall(itree_t *tree, interval_t *interval); +int itree_insert(itree_t *tree, interval_t *interval); +// Delete the first interval contained by the specified interval +int itree_remove(itree_t *tree, interval_t *interval); +size_t itree_size(itree_t *tree); + +/* Tree traversal functions */ +itreetrav_t *itreetnew(void); +void itreetdelete(itreetrav_t *trav); +interval_t *itreetfirst(itreetrav_t *trav, itree_t *tree); +interval_t *itreetlast(itreetrav_t *trav, itree_t *tree); +interval_t *itreetnext(itreetrav_t *trav); +interval_t *itreetprev(itreetrav_t *trav); + +#ifdef __cplusplus +} +#endif + +#endif /* _INTERVAL_TREE_H_ */ diff --git a/deps/interval_tree/test/CMakeLists.txt b/deps/interval_tree/test/CMakeLists.txt new file mode 100644 index 0000000..c56eea6 --- /dev/null +++ b/deps/interval_tree/test/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(gtest_interval_tree gtest_interval_tree.cpp) +target_link_libraries(gtest_interval_tree interval_tree gtest) + +include(GoogleTest) +gtest_discover_tests(gtest_interval_tree)
\ No newline at end of file diff --git a/deps/interval_tree/test/gtest_interval_tree.cpp b/deps/interval_tree/test/gtest_interval_tree.cpp new file mode 100644 index 0000000..417c275 --- /dev/null +++ b/deps/interval_tree/test/gtest_interval_tree.cpp @@ -0,0 +1,336 @@ +#include <gtest/gtest.h> + +#include "itree.h" + +void *my_dup(void *p) +{ + return p ? strdup((const char *)p) : NULL; +} + +void my_rel(void *p) +{ + if (p) + { + free(p); + } +} + +// find overlap +#if 1 +TEST(INTERVAL_TREE, FIND) +{ + itree_t *tree; + interval_t *result; + interval_t query; + interval_t segment; + + // new + tree = itree_new(my_dup, my_rel); + EXPECT_TRUE(tree != nullptr); + EXPECT_TRUE(itree_size(tree) == 0); + + // insert + segment = { + .low = 5, + .high = 9, + .data = (void *)"Hello", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 1); + + // find + query = { + .low = 5, + .high = 5, + }; + result = itree_find(tree, &query); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "Hello"); + + // find + query = { + .low = 6, + .high = 8, + }; + result = itree_find(tree, &query); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "Hello"); + + // find + query = { + .low = 5, + .high = 9, + }; + result = itree_find(tree, &query); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "Hello"); + + // find + query = { + .low = 9, + .high = 9, + }; + result = itree_find(tree, &query); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "Hello"); + + // find + query = { + .low = 1, + .high = 14, + }; + result = itree_find(tree, &query); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "Hello"); + + // not found + query = { + .low = 1, + .high = 4, + }; + result = itree_find(tree, &query); + EXPECT_TRUE(result == nullptr); + + // not found + query = { + .low = 10, + .high = 14, + }; + result = itree_find(tree, &query); + + itree_delete(tree); +} +#endif + +#if 1 +TEST(INTERVAL_TREE, DELETE1) +{ + itree_t *tree; + interval_t query; + interval_t segment; + + // new + tree = itree_new(my_dup, my_rel); + EXPECT_TRUE(tree != nullptr); + EXPECT_TRUE(itree_size(tree) == 0); + + // insert + segment = { + .low = 5, + .high = 9, + .data = (void *)"Hello", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 1); + + // delete + query = { + .low = 5, + .high = 5, + }; + EXPECT_TRUE(itree_remove(tree, &query) == 0); + EXPECT_TRUE(itree_size(tree) == 1); + + // delete + query = { + .low = 9, + .high = 9, + }; + EXPECT_TRUE(itree_remove(tree, &query) == 0); + EXPECT_TRUE(itree_size(tree) == 1); + + // delete + query = { + .low = 5, + .high = 9, + }; + EXPECT_TRUE(itree_remove(tree, &query) == 1); + EXPECT_TRUE(itree_size(tree) == 0); + + itree_delete(tree); +} +#endif + +#if 1 +TEST(INTERVAL_TREE, DELETE2) +{ + itree_t *tree; + interval_t query; + interval_t segment; + + // new + tree = itree_new(my_dup, my_rel); + EXPECT_TRUE(tree != nullptr); + EXPECT_TRUE(itree_size(tree) == 0); + + // insert + segment = { + .low = 5, + .high = 9, + .data = (void *)"Hello", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 1); + + // insert + segment = { + .low = 15, + .high = 19, + .data = (void *)"World", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 2); + + // delete + query = { + .low = 1, + .high = 20, + }; + EXPECT_TRUE(itree_remove(tree, &query) == 1); + EXPECT_TRUE(itree_size(tree) == 1); + + // delete + query = { + .low = 1, + .high = 20, + }; + EXPECT_TRUE(itree_remove(tree, &query) == 1); + EXPECT_TRUE(itree_size(tree) == 0); + + itree_delete(tree); +} +#endif + +#if 1 +TEST(INTERVAL_TREE, REPEAT1) +{ + itree_t *tree; + interval_t segment; + interval_t query; + + // new + tree = itree_new(my_dup, my_rel); + EXPECT_TRUE(tree != nullptr); + EXPECT_TRUE(itree_size(tree) == 0); + + // insert + segment = { + .low = 5, + .high = 9, + .data = (void *)"Hello", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 1); + + // insert + segment = { + .low = 5, + .high = 9, + .data = (void *)"World", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 2); + + // remove + query = { + .low = 5, + .high = 9, + }; + EXPECT_TRUE(itree_remove(tree, &query) == 1); + EXPECT_TRUE(itree_size(tree) == 1); + EXPECT_TRUE(itree_remove(tree, &query) == 1); + EXPECT_TRUE(itree_size(tree) == 0); + + itree_delete(tree); +} +#endif + +#if 1 // repeat, return the fist one +TEST(INTERVAL_TREE, REPEAT2) +{ + itree_t *tree; + ilist_t *list; + ilisttrav_t *trav; + interval_t *result; + interval_t query; + interval_t segment; + + // new + tree = itree_new(my_dup, my_rel); + EXPECT_TRUE(tree != nullptr); + EXPECT_TRUE(itree_size(tree) == 0); + + // insert + segment = { + .low = 5, + .high = 9, + .data = (void *)"Hello", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 1); + + // insert + segment = { + .low = 5, + .high = 9, + .data = (void *)"World", + }; + EXPECT_TRUE(itree_insert(tree, &segment) == 1); + EXPECT_TRUE(itree_size(tree) == 2); + + // find + query = { + .low = 5, + .high = 9, + }; + result = itree_find(tree, &query); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "Hello"); + + // find all, free the repeat + query = { + .low = 5, + .high = 9, + }; + list = itree_findall(tree, &query); + EXPECT_TRUE(list != nullptr); + EXPECT_TRUE(ilist_size(list) == 2); + trav = ilisttrav_new(list); + EXPECT_TRUE(trav != nullptr); + + result = (interval_t *)ilisttrav_first(trav); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "Hello"); + + result = (interval_t *)ilisttrav_next(trav); + EXPECT_TRUE(result != nullptr); + EXPECT_TRUE(result->low == 5); + EXPECT_TRUE(result->high == 9); + EXPECT_STREQ((const char *)result->data, "World"); + + ilisttrav_delete(trav); + ilist_delete(list); + + itree_delete(tree); +} +#endif + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} |
