From e829c618889a8b4bb95b8cdb0af328c1aedd2e3e Mon Sep 17 00:00:00 2001 From: Alexandr <a.lyapunov@corp.mail.ru> Date: Tue, 18 Nov 2014 16:26:40 +0300 Subject: [PATCH] converted rtree to plain c, moved it to salad --- cmake/BuildMisc.cmake | 1 - src/box/rtree_index.cc | 166 ++++-- src/box/rtree_index.h | 4 +- src/lib/salad/CMakeLists.txt | 2 +- src/lib/salad/bps_tree.h | 3 +- src/lib/salad/rtree.c | 939 ++++++++++++++++++++++++++++++ src/lib/salad/rtree.h | 146 +++++ test/box/rtree_benchmark.result | 2 +- test/box/rtree_benchmark.test.lua | 2 +- test/box/rtree_rect.result | 4 +- test/box/rtree_rect.test.lua | 4 +- test/unit/CMakeLists.txt | 4 +- test/unit/rtree.cc | 164 +++--- test/unit/rtree_itr.cc | 172 +++--- third_party/rtree.cc | 616 -------------------- third_party/rtree.h | 240 -------- 16 files changed, 1401 insertions(+), 1068 deletions(-) create mode 100644 src/lib/salad/rtree.c create mode 100644 src/lib/salad/rtree.h delete mode 100644 third_party/rtree.cc delete mode 100644 third_party/rtree.h diff --git a/cmake/BuildMisc.cmake b/cmake/BuildMisc.cmake index 881b92644b..1b12412d63 100644 --- a/cmake/BuildMisc.cmake +++ b/cmake/BuildMisc.cmake @@ -7,7 +7,6 @@ macro(libmisc_build) ${PROJECT_SOURCE_DIR}/third_party/proctitle.c ${PROJECT_SOURCE_DIR}/third_party/PMurHash.c ${PROJECT_SOURCE_DIR}/third_party/base64.c - ${PROJECT_SOURCE_DIR}/third_party/rtree.cc ) if (NOT HAVE_MEMMEM) diff --git a/src/box/rtree_index.cc b/src/box/rtree_index.cc index edfd38129c..f162ff22d3 100644 --- a/src/box/rtree_index.cc +++ b/src/box/rtree_index.cc @@ -41,9 +41,10 @@ static int rtree_page_pool_initialized = 0; /* {{{ Utilities. *************************************************/ -inline void extract_rectangle(rectangle_t& r, struct tuple const* tuple, struct key_def* kd) +inline void extract_rectangle(struct rtree_rect *rect, + const struct tuple *tuple, struct key_def *kd) { - const char* elems = tuple_field(tuple, kd->parts[0].fieldno); + const char *elems = tuple_field(tuple, kd->parts[0].fieldno); uint32_t size = mp_decode_array(&elems); assert (kd->part_count == 1); switch (size) { @@ -53,13 +54,18 @@ inline void extract_rectangle(rectangle_t& r, struct tuple const* tuple, struct uint32_t size = mp_decode_array(&elems); switch (size) { case 2: // point - r.boundary[0] = r.boundary[2] = mp_decode_num(&elems, 0); - r.boundary[1] = r.boundary[3] = mp_decode_num(&elems, 1); + rect->lower_point.coords[0] = + rect->upper_point.coords[0] = + mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = + rect->upper_point.coords[1] = + mp_decode_num(&elems, 1); break; case 4: - for (int i = 0; i < 4; i++) { - r.boundary[i] = mp_decode_num(&elems, i); - } + rect->lower_point.coords[0] = mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = mp_decode_num(&elems, 1); + rect->upper_point.coords[0] = mp_decode_num(&elems, 2); + rect->upper_point.coords[1] = mp_decode_num(&elems, 3); break; default: tnt_raise(ClientError, ER_UNSUPPORTED, @@ -70,37 +76,46 @@ inline void extract_rectangle(rectangle_t& r, struct tuple const* tuple, struct break; } case 2: // point - r.boundary[0] = r.boundary[2] = mp_decode_num(&elems, 0); - r.boundary[1] = r.boundary[3] = mp_decode_num(&elems, 1); + rect->lower_point.coords[0] = + rect->upper_point.coords[0] = + mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = + rect->upper_point.coords[1] = + mp_decode_num(&elems, 1); break; case 4: - for (int i = 0; i < 4; i++) { - r.boundary[i] = mp_decode_num(&elems, i); - } + rect->lower_point.coords[0] = mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = mp_decode_num(&elems, 1); + rect->upper_point.coords[0] = mp_decode_num(&elems, 2); + rect->upper_point.coords[1] = mp_decode_num(&elems, 3); break; default: tnt_raise(ClientError, ER_UNSUPPORTED, "R-Tree index", "Key should contain 2 (point) or 4 (rectangle) coordinates"); } + rtree_rect_normalize(rect); } /* {{{ TreeIndex Iterators ****************************************/ -struct rtree_iterator { +struct index_rtree_iterator { struct iterator base; - R_tree_iterator impl; + struct rtree_iterator impl; }; static void -rtree_iterator_free(struct iterator *i) +index_rtree_iterator_free(struct iterator *i) { - delete (rtree_iterator*)i; + struct index_rtree_iterator *itr = (struct index_rtree_iterator *)i; + rtree_iterator_destroy(&itr->impl); + delete itr; } static struct tuple * -rtree_iterator_next(struct iterator *i) +index_rtree_iterator_next(struct iterator *i) { - return (tuple*)((rtree_iterator*)i)->impl.next(); + struct index_rtree_iterator *itr = (struct index_rtree_iterator *)i; + return (struct tuple *)rtree_iterator_next(&itr->impl); } /* }}} */ @@ -123,19 +138,21 @@ RTreeIndex::~RTreeIndex() { // Iterator has to be destroye prior to tree if (m_position != NULL) { - m_position->free(m_position); + index_rtree_iterator_free(m_position); m_position = NULL; } + rtree_destroy(&tree); } RTreeIndex::RTreeIndex(struct key_def *key_def) - : Index(key_def), tree(rtree_page_alloc, rtree_page_free) + : Index(key_def) { if (rtree_page_pool_initialized == 0) { mempool_create(&rtree_page_pool, &cord()->slabc, RTREE_PAGE_SIZE); rtree_page_pool_initialized = 1; } + rtree_init(&tree, rtree_page_alloc, rtree_page_free); if (key_def->part_count != 1 || key_def->parts[0].type != BOX) { tnt_raise(ClientError, ER_UNSUPPORTED, "R-Tree index", "Key should have BOX type"); @@ -145,33 +162,39 @@ RTreeIndex::RTreeIndex(struct key_def *key_def) size_t RTreeIndex::size() const { - return tree.number_of_records(); + return rtree_number_of_records(&tree); } size_t RTreeIndex::memsize() const { - return tree.used_size(); + return rtree_used_size(&tree); } struct tuple * RTreeIndex::findByKey(const char *key, uint32_t part_count) const { - rectangle_t r; - R_tree_iterator iterator; + rtree_rect rect; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); switch (part_count) { case 1: { uint32_t size = mp_decode_array(&key); switch (size) { case 2: - r.boundary[0] = r.boundary[2] = mp_decode_num(&key, 0); - r.boundary[1] = r.boundary[3] = mp_decode_num(&key, 1); + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); break; case 4: - for (int i = 0; i < 4; i++) { - r.boundary[i] = mp_decode_num(&key, i); - } + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); break; default: tnt_raise(ClientError, ER_UNSUPPORTED, @@ -181,39 +204,44 @@ RTreeIndex::findByKey(const char *key, uint32_t part_count) const break; } case 2: - r.boundary[0] = r.boundary[2] = mp_decode_num(&key, 0); - r.boundary[1] = r.boundary[3] = mp_decode_num(&key, 1); + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); break; case 4: - for (int i = 0; i < 4; i++) { - r.boundary[i] = mp_decode_num(&key, i); - } + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); break; default: tnt_raise(ClientError, ER_UNSUPPORTED, "R-Tree key", "Key should contain 2 (point) " "or 4 (rectangle) numeric coordinates"); } - if (tree.search(r, SOP_OVERLAPS, iterator)) { - return (struct tuple*)iterator.next(); - } - return NULL; + struct tuple *result = NULL; + if (rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) + result = (struct tuple *)rtree_iterator_next(&iterator); + rtree_iterator_destroy(&iterator); + return result; } struct tuple * RTreeIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, enum dup_replace_mode) { - rectangle_t r; + struct rtree_rect rect; if (new_tuple) { - extract_rectangle(r, new_tuple, key_def); - tree.insert(r, new_tuple); + extract_rectangle(&rect, new_tuple, key_def); + rtree_insert(&tree, &rect, new_tuple); } if (old_tuple) { - extract_rectangle(r, old_tuple, key_def); - if (!tree.remove(r, old_tuple)) { + extract_rectangle(&rect, old_tuple, key_def); + if (!rtree_remove(&tree, &rect, old_tuple)) old_tuple = NULL; - } } return old_tuple; } @@ -221,14 +249,16 @@ RTreeIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, struct iterator * RTreeIndex::allocIterator() const { - rtree_iterator *it = (rtree_iterator *)new rtree_iterator(); + index_rtree_iterator *it = new index_rtree_iterator; + memset(it, 0, sizeof(*it)); + rtree_iterator_init(&it->impl); if (it == NULL) { tnt_raise(ClientError, ER_MEMORY_ISSUE, - sizeof(struct rtree_iterator), + sizeof(struct index_rtree_iterator), "RTreeIndex", "iterator"); } - it->base.next = rtree_iterator_next; - it->base.free = rtree_iterator_free; + it->base.next = index_rtree_iterator_next; + it->base.free = index_rtree_iterator_free; return &it->base; } @@ -236,8 +266,8 @@ void RTreeIndex::initIterator(struct iterator *iterator, enum iterator_type type, const char *key, uint32_t part_count) const { - rectangle_t r; - rtree_iterator *it = (rtree_iterator *)iterator; + struct rtree_rect rect; + index_rtree_iterator *it = (index_rtree_iterator *)iterator; switch (part_count) { case 0: if (type != ITER_ALL) { @@ -250,13 +280,18 @@ RTreeIndex::initIterator(struct iterator *iterator, enum iterator_type type, uint32_t size = mp_decode_array(&key); switch (size) { case 2: - r.boundary[0] = r.boundary[2] = mp_decode_num(&key, 0); - r.boundary[1] = r.boundary[3] = mp_decode_num(&key, 1); + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); break; case 4: - for (int i = 0; i < 4; i++) { - r.boundary[i] = mp_decode_num(&key, i); - } + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); break; default: tnt_raise(ClientError, ER_UNSUPPORTED, @@ -266,20 +301,25 @@ RTreeIndex::initIterator(struct iterator *iterator, enum iterator_type type, break; } case 2: - r.boundary[0] = r.boundary[2] = mp_decode_num(&key, 0); - r.boundary[1] = r.boundary[3] = mp_decode_num(&key, 1); + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); break; case 4: - for (int i = 0; i < 4; i++) { - r.boundary[i] = mp_decode_num(&key, i); - } + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); break; default: tnt_raise(ClientError, ER_UNSUPPORTED, "R-Tree index", "Key contain 2 (point) " "or 4 (rectangle) numeric coordinates"); } - Spatial_search_op op; + spatial_search_op op; switch (type) { case ITER_ALL: op = SOP_ALL; @@ -309,13 +349,13 @@ RTreeIndex::initIterator(struct iterator *iterator, enum iterator_type type, tnt_raise(ClientError, ER_UNSUPPORTED, "R-Tree index", "Unsupported search operation for R-Tree"); } - tree.search(r, op, it->impl); + rtree_search(&tree, &rect, op, &it->impl); } void RTreeIndex::beginBuild() { - tree.purge(); + rtree_purge(&tree); } diff --git a/src/box/rtree_index.h b/src/box/rtree_index.h index a40976450a..451e0d7777 100644 --- a/src/box/rtree_index.h +++ b/src/box/rtree_index.h @@ -31,7 +31,7 @@ #include "index.h" -#include <third_party/rtree.h> +#include <salad/rtree.h> class RTreeIndex: public Index { @@ -53,7 +53,7 @@ class RTreeIndex: public Index const char *key, uint32_t part_count) const; protected: - R_tree tree; + struct rtree tree; }; #endif /* TARANTOOL_BOX_RTREE_INDEX_H_INCLUDED */ diff --git a/src/lib/salad/CMakeLists.txt b/src/lib/salad/CMakeLists.txt index fe1be4c4bc..9db3d800e8 100644 --- a/src/lib/salad/CMakeLists.txt +++ b/src/lib/salad/CMakeLists.txt @@ -1,3 +1,3 @@ -set(lib_sources rope.c rlist.c) +set(lib_sources rope.c rlist.c rtree.c) set_source_files_compile_flags(${lib_sources}) add_library(salad ${lib_sources}) diff --git a/src/lib/salad/bps_tree.h b/src/lib/salad/bps_tree.h index 6be1b456e7..48f85b87b0 100644 --- a/src/lib/salad/bps_tree.h +++ b/src/lib/salad/bps_tree.h @@ -426,8 +426,7 @@ typedef uint32_t bps_tree_block_id_t; /** * struct bps_block forward declaration (Used in struct bps_tree) - */ -struct bps_block; + */struct bps_block; #ifdef BPS_TREE_DEBUG_BRANCH_VISIT #define BPS_TREE_BRANCH_TRACE(tree, type, branch_bit) \ diff --git a/src/lib/salad/rtree.c b/src/lib/salad/rtree.c new file mode 100644 index 0000000000..d0f388e6d4 --- /dev/null +++ b/src/lib/salad/rtree.c @@ -0,0 +1,939 @@ +#include "rtree.h" +#include <string.h> +#include <assert.h> + +/*------------------------------------------------------------------------- */ +/* R-tree internal structures definition */ +/*------------------------------------------------------------------------- */ + +struct rtree_page_branch { + struct rtree_rect rect; + union { + struct rtree_page *page; + record_t record; + } data; +}; + +enum { + /* maximal number of branches at page */ + RTREE_MAX_FILL = (RTREE_PAGE_SIZE - sizeof(int)) / + sizeof(struct rtree_page_branch), + /* minimal number of branches at non-root page */ + RTREE_MIN_FILL = RTREE_MAX_FILL / 2 +}; + +struct rtree_page { + /* number of branches at page */ + int n; + /* branches */ + struct rtree_page_branch b[RTREE_MAX_FILL]; +}; + +struct rtree_neighbor { + void *child; + struct rtree_neighbor *next; + int level; + coord_t distance; +}; + +enum { + RTREE_NEIGHBORS_IN_PAGE = (RTREE_PAGE_SIZE - sizeof(void*)) / + sizeof(struct rtree_neighbor) +}; + +struct rtree_neighbor_page { + struct rtree_neighbor_page* next; + struct rtree_neighbor buf[RTREE_NEIGHBORS_IN_PAGE]; +}; + +struct rtree_reinsert_list { + struct rtree_page *chain; + int level; +}; + +/*------------------------------------------------------------------------- */ +/* R-tree rectangle methods */ +/*------------------------------------------------------------------------- */ + + +void +rtree_rect_normalize(struct rtree_rect *rect) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) { + if (rect->lower_point.coords[i] <= rect->upper_point.coords[i]) + continue; + coord_t tmp = rect->lower_point.coords[i]; + rect->lower_point.coords[i] = rect->upper_point.coords[i]; + rect->upper_point.coords[i] = tmp; + } +} + +void +rtree_set2d(struct rtree_rect *rect, + coord_t left, coord_t bottom, coord_t right, coord_t top) +{ + assert(RTREE_DIMENSION == 2); + rect->lower_point.coords[0] = left; + rect->lower_point.coords[1] = bottom; + rect->upper_point.coords[0] = right; + rect->upper_point.coords[1] = top; +} + +static coord_t +rtree_rect_point_distance2(const struct rtree_rect *rect, + const struct rtree_point *point) +{ + coord_t result = 0; + for (int i = RTREE_DIMENSION; --i >= 0; ) { + if (point->coords[i] < rect->lower_point.coords[i]) { + coord_t diff = point->coords[i] - + rect->lower_point.coords[i]; + result += diff * diff; + } else if (point->coords[i] > rect->upper_point.coords[i]) { + coord_t diff = point->coords[i] - + rect->upper_point.coords[i]; + result += diff * diff; + } + } + return result; +} + +static coord_t +rtree_rect_area(const struct rtree_rect *rect) +{ + coord_t area = 1; + for (int i = RTREE_DIMENSION; --i >= 0; ) + area *= rect->upper_point.coords[i] - rect->lower_point.coords[i]; + return area; +} + +static void +rtree_rect_add(struct rtree_rect *to, const struct rtree_rect *item) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) { + if (to->lower_point.coords[i] > item->lower_point.coords[i]) + to->lower_point.coords[i] = item->lower_point.coords[i]; + if (to->upper_point.coords[i] < item->upper_point.coords[i]) + to->upper_point.coords[i] = item->upper_point.coords[i]; + } +} + +static coord_t +rtree_min(coord_t a, coord_t b) +{ + return a < b ? a : b; +} + +static coord_t +rtree_max(coord_t a, coord_t b) +{ + return a > b ? a : b; +} + +static struct rtree_rect +rtree_rect_cover(const struct rtree_rect *item1, const struct rtree_rect *item2) +{ + struct rtree_rect res; + for (int i = RTREE_DIMENSION; --i >= 0; ) { + res.lower_point.coords[i] = + rtree_min(item1->lower_point.coords[i], + item2->lower_point.coords[i]); + res.upper_point.coords[i] = + rtree_max(item1->upper_point.coords[i], + item2->upper_point.coords[i]); + } + return res; +} + +static bool +rtree_rect_intersects_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] > rt2->upper_point.coords[i] || + rt1->upper_point.coords[i] < rt2->lower_point.coords[i]) + return false; + return true; +} + +static bool +rtree_rect_in_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] < rt2->lower_point.coords[i] || + rt1->upper_point.coords[i] > rt2->upper_point.coords[i]) + return false; + return true; +} + +static bool +rtree_rect_strict_in_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] <= rt2->lower_point.coords[i] || + rt1->upper_point.coords[i] >= rt2->upper_point.coords[i]) + return false; + return true; +} + +static bool +rtree_rect_holds_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + return rtree_rect_in_rect(rt2, rt1); +} + +static bool +rtree_rect_strict_holds_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + return rtree_rect_strict_in_rect(rt2, rt1); +} + +static bool +rtree_rect_equal_to_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] != rt2->lower_point.coords[i] || + rt1->upper_point.coords[i] != rt2->upper_point.coords[i]) + return false; + return true; +} + +static bool +rtree_always_true(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + (void)rt1; + (void)rt2; + return true; +} + +/*------------------------------------------------------------------------- */ +/* R-tree page methods */ +/*------------------------------------------------------------------------- */ + +static struct rtree_page * +rtree_alloc_page(struct rtree *tree) +{ + return (struct rtree_page *)tree->page_alloc(); +} + +static void +rtree_free_page(struct rtree *tree, struct rtree_page *page) +{ + tree->page_free(page); +} + +static void +set_next_reinsert_page(struct rtree_page *page, struct rtree_page *next_page) +{ + /* The page must be MIN_FILLed, so last branch is unused */ + page->b[RTREE_MAX_FILL - 1].data.page = next_page; +} + +struct rtree_page * +get_next_reinsert_page(const struct rtree_page *page) +{ + return page->b[RTREE_MAX_FILL - 1].data.page; +} + +/* Calculate cover of all rectangles at page */ +static struct rtree_rect +rtree_page_cover(const struct rtree_page *page) +{ + struct rtree_rect res = page->b[0].rect; + for (int i = 1; i < page->n; i++) + rtree_rect_add(&res, &page->b[i].rect); + return res; +} + +/* Create root page by first inserting record */ +static void +rtree_page_init_record(struct rtree_page *page, + struct rtree_rect *rect, record_t obj) +{ + page->n = 1; + page->b[0].rect = *rect; + page->b[0].data.record = obj; +} + +/* Create root page by branch */ +static void +rtree_page_init_branch(struct rtree_page *page, + const struct rtree_page_branch *br) +{ + page->n = 1; + page->b[0] = *br; +} + +/* Create new root page (root splitting) */ +static void +rtree_page_init_page(struct rtree_page *page, + struct rtree_page *page1, + struct rtree_page *page2) +{ + page->n = 2; + page->b[0].rect = rtree_page_cover(page1); + page->b[0].data.page = page1; + page->b[1].rect = rtree_page_cover(page2); + page->b[1].data.page = page2; +} + +static struct rtree_page * +rtree_split_page(struct rtree *tree, struct rtree_page *page, + const struct rtree_page_branch *br) +{ + coord_t rect_area[RTREE_MAX_FILL + 1]; + rect_area[0] = rtree_rect_area(&br->rect); + for (int i = 0; i < RTREE_MAX_FILL; i++) + rect_area[i + 1] = rtree_rect_area(&page->b[i].rect); + + /* + * As the seeds for the two groups, find two rectangles which waste + * the most area if covered by a single rectangle. + */ + int seed[2] = {-1, -1}; + coord_t worst_waste = -RTREE_COORD_MAX; + + const struct rtree_page_branch *bp = br; + for (int i = 0; i < RTREE_MAX_FILL; i++) { + for (int j = i + 1; j <= RTREE_MAX_FILL; j++) { + struct rtree_rect cover = + rtree_rect_cover(&bp->rect, + &page->b[j - 1].rect); + coord_t waste = rtree_rect_area(&cover) - + rect_area[i] - rect_area[j]; + if (waste > worst_waste) { + worst_waste = waste; + seed[0] = i; + seed[1] = j; + } + } + bp = page->b + i; + } + assert(seed[0] >= 0); + + char taken[RTREE_MAX_FILL]; + memset(taken, 0, sizeof(taken)); + struct rtree_rect group_rect[2]; + coord_t group_area[2]; + int group_card[2]; + struct rtree_page *p = rtree_alloc_page(tree); + tree->n_pages++; + + taken[seed[1] - 1] = 2; + group_rect[1] = page->b[seed[1] - 1].rect; + + if (seed[0] == 0) { + group_rect[0] = br->rect; + rtree_page_init_branch(p, br); + } else { + group_rect[0] = page->b[seed[0] - 1].rect; + rtree_page_init_branch(p, &page->b[seed[0] - 1]); + page->b[seed[0] - 1] = *br; + } + group_card[0] = group_card[1] = 1; + group_area[0] = rect_area[seed[0]]; + group_area[1] = rect_area[seed[1]]; + + /* + * Split remaining rectangles between two groups. + * The one chosen is the one with the greatest difference in area + * expansion depending on which group - the rect most strongly + * attracted to one group and repelled from the other. + */ + while (group_card[0] + group_card[1] < RTREE_MAX_FILL + 1 + && group_card[0] < RTREE_MAX_FILL + 1 - RTREE_MIN_FILL + && group_card[1] < RTREE_MAX_FILL + 1 - RTREE_MIN_FILL) + { + int better_group = -1, chosen = -1; + coord_t biggest_diff = -1; + for (int i = 0; i < RTREE_MAX_FILL; i++) { + if (taken[i]) + continue; + struct rtree_rect cover0 = + rtree_rect_cover(&group_rect[0], + &page->b[i].rect); + struct rtree_rect cover1 = + rtree_rect_cover(&group_rect[1], + &page->b[i].rect); + coord_t diff = rtree_rect_area(&cover0) - group_area[0] + - (rtree_rect_area(&cover1) - group_area[1]); + if (diff > biggest_diff || -diff > biggest_diff) { + chosen = i; + if (diff < 0) { + better_group = 0; + biggest_diff = -diff; + } else { + better_group = 1; + biggest_diff = diff; + } + } + } + assert(chosen >= 0); + group_card[better_group]++; + rtree_rect_add(&group_rect[better_group], + &page->b[chosen].rect); + group_area[better_group] = + rtree_rect_area(&group_rect[better_group]); + taken[chosen] = better_group + 1; + if (better_group == 0) + p->b[group_card[0] - 1] = page->b[chosen]; + } + /* + * If one group gets too full, then remaining rectangle are + * split between two groups in such way to balance CARDs of two groups. + */ + if (group_card[0] + group_card[1] < RTREE_MAX_FILL + 1) { + for (int i = 0; i < RTREE_MAX_FILL; i++) { + if (taken[i]) + continue; + if (group_card[0] >= group_card[1]) { + taken[i] = 2; + group_card[1] += 1; + } else { + taken[i] = 1; + p->b[group_card[0]++] = page->b[i]; + } + } + } + p->n = group_card[0]; + page->n = group_card[1]; + for (int i = 0, j = 0; i < page->n; j++) { + if (taken[j] == 2) + page->b[i++] = page->b[j]; + } + return p; +} + +static struct rtree_page* +rtree_page_add_branch(struct rtree *tree, struct rtree_page *page, + const struct rtree_page_branch *br) +{ + if (page->n < RTREE_MAX_FILL) { + page->b[page->n++] = *br; + return NULL; + } else { + return rtree_split_page(tree, page, br); + } +} + +static void +rtree_page_remove_branch(struct rtree_page *page, int i) +{ + page->n -= 1; + memmove(page->b + i, page->b + i + 1, + (page->n - i) * sizeof(struct rtree_page_branch)); +} + +static struct rtree_page * +rtree_page_insert(struct rtree *tree, struct rtree_page *page, + const struct rtree_rect *rect, record_t obj, int level) +{ + struct rtree_page_branch br; + if (--level != 0) { + /* not a leaf page */ + int i, mini = 0; + coord_t min_incr = RTREE_COORD_MAX; + coord_t best_area = RTREE_COORD_MAX; + for (i = 0; i < page->n; i++) { + coord_t r_area = rtree_rect_area(&page->b[i].rect); + struct rtree_rect cover = + rtree_rect_cover(&page->b[i].rect, rect); + coord_t incr = rtree_rect_area(&cover) - r_area; + assert(incr >= 0); + if (incr < min_incr) { + best_area = r_area; + min_incr = incr; + mini = i; + } else if (incr == min_incr && r_area < best_area) { + best_area = r_area; + mini = i; + } + } + struct rtree_page *p = page->b[mini].data.page; + struct rtree_page *q = rtree_page_insert(tree, p, + rect, obj, level); + if (q == NULL) { + /* child was not split */ + rtree_rect_add(&page->b[mini].rect, rect); + return NULL; + } else { + /* child was split */ + page->b[mini].rect = rtree_page_cover(p); + br.data.page = q; + br.rect = rtree_page_cover(q); + return rtree_page_add_branch(tree, page, &br); + } + } else { + br.data.record = obj; + br.rect = *rect; + return rtree_page_add_branch(tree, page, &br); + } +} + +static bool +rtree_page_remove(struct rtree *tree, struct rtree_page *page, + const struct rtree_rect *rect, record_t obj, + int level, struct rtree_reinsert_list *rlist) +{ + if (--level != 0) { + for (int i = 0; i < page->n; i++) { + if (!rtree_rect_intersects_rect(&page->b[i].rect, rect)) + continue; + struct rtree_page *next_page = page->b[i].data.page; + if (!rtree_page_remove(tree, next_page, rect, + obj, level, rlist)) + continue; + if (next_page->n >= RTREE_MIN_FILL) { + page->b[i].rect = + rtree_page_cover(next_page); + } else { + /* not enough entries in child */ + set_next_reinsert_page(next_page, rlist->chain); + rlist->chain = next_page; + rlist->level = level - 1; + rtree_page_remove_branch(page, i); + } + return true; + } + } else { + for (int i = 0; i < page->n; i++) { + if (page->b[i].data.page == obj) { + rtree_page_remove_branch(page, i); + return true; + } + } + } + return false; +} + +static void +rtree_page_purge(struct rtree *tree, struct rtree_page *page, int level) +{ + if (--level != 0) { /* this is an internal node in the tree */ + for (int i = 0; i < page->n; i++) + rtree_page_purge(tree, page->b[i].data.page, level); + } + tree->page_free(page); +} + +/*------------------------------------------------------------------------- */ +/* R-tree iterator methods */ +/*------------------------------------------------------------------------- */ + +static bool +rtree_iterator_goto_first(struct rtree_iterator *itr, int sp, struct rtree_page* pg) +{ + if (sp + 1 == itr->tree->height) { + for (int i = 0, n = pg->n; i < n; i++) { + if (itr->leaf_cmp(&itr->rect, &pg->b[i].rect)) { + itr->stack[sp].page = pg; + itr->stack[sp].pos = i; + return true; + } + } + } else { + for (int i = 0, n = pg->n; i < n; i++) { + if (itr->intr_cmp(&itr->rect, &pg->b[i].rect) + && rtree_iterator_goto_first(itr, sp + 1, + pg->b[i].data.page)) + { + itr->stack[sp].page = pg; + itr->stack[sp].pos = i; + return true; + } + } + } + return false; +} + + +static bool +rtree_iterator_goto_next(struct rtree_iterator *itr, int sp) +{ + struct rtree_page *pg = itr->stack[sp].page; + if (sp + 1 == itr->tree->height) { + for (int i = itr->stack[sp].pos, n = pg->n; ++i < n;) { + if (itr->leaf_cmp(&itr->rect, &pg->b[i].rect)) { + itr->stack[sp].pos = i; + return true; + } + } + } else { + for (int i = itr->stack[sp].pos, n = pg->n; ++i < n;) { + if (itr->intr_cmp(&itr->rect, &pg->b[i].rect) + && rtree_iterator_goto_first(itr, sp + 1, + pg->b[i].data.page)) + { + itr->stack[sp].page = pg; + itr->stack[sp].pos = i; + return true; + } + } + } + return sp > 0 ? rtree_iterator_goto_next(itr, sp - 1) : false; +} + +void +rtree_iterator_destroy(struct rtree_iterator *itr) +{ + struct rtree_neighbor_page *curr, *next; + for (curr = itr->page_list; curr != NULL; curr = next) { + next = curr->next; + itr->tree->page_free(curr); + } + itr->page_list = NULL; + itr->page_pos = RTREE_NEIGHBORS_IN_PAGE; +} + +static void +rtree_iterator_reset(struct rtree_iterator *itr) +{ + if (itr->neigh_list != NULL) { + struct rtree_neighbor **npp = &itr->neigh_free_list; + while (*npp != NULL) { + npp = &(*npp)->next; + } + *npp = itr->neigh_list; + itr->neigh_list = NULL; + } +} + +static struct rtree_neighbor* +rtree_iterator_allocate_neighbour(struct rtree_iterator *itr) +{ + if (itr->page_pos >= RTREE_NEIGHBORS_IN_PAGE) { + struct rtree_neighbor_page *new_page = + (struct rtree_neighbor_page *)itr->tree->page_alloc(); + new_page->next = itr->page_list; + itr->page_list = new_page; + itr->page_pos = 0; + } + return itr->page_list->buf + itr->page_pos++; +} + +static struct rtree_neighbor * +rtree_iterator_new_neighbor(struct rtree_iterator *itr, + void* child, coord_t distance, int level) +{ + struct rtree_neighbor *n = itr->neigh_free_list; + if (n == NULL) + n = rtree_iterator_allocate_neighbour(itr); + else + itr->neigh_free_list = n->next; + n->child = child; + n->distance = distance; + n->level = level; + n->next = NULL; + return n; +} + +static void +rtree_iterator_free_neighbor(struct rtree_iterator *itr, + struct rtree_neighbor *n) +{ + n->next = itr->neigh_free_list; + itr->neigh_free_list = n; +} + +void +rtree_iterator_init(struct rtree_iterator *itr) +{ + itr->neigh_list = NULL; + itr->neigh_free_list = NULL; + itr->page_list = NULL; + itr->page_pos = RTREE_NEIGHBORS_IN_PAGE; +} + +static void +rtree_iterator_insert_neighbor(struct rtree_iterator *itr, + struct rtree_neighbor *node) +{ + struct rtree_neighbor *prev = NULL, *next = itr->neigh_list; + coord_t distance = node->distance; + while (next != NULL && next->distance < distance) { + prev = next; + next = prev->next; + } + node->next = next; + if (prev == NULL) + itr->neigh_list = node; + else + prev->next = node; +} + +record_t +rtree_iterator_next(struct rtree_iterator *itr) +{ + if (itr->version != itr->tree->version) { + /* Index was updated since cursor initialziation */ + return NULL; + } + if (itr->op == SOP_NEIGHBOR) { + /* To return element in order of increasing distance from + * specified point, we build sorted list of R-Tree items + * (ordered by distance from specified point) starting from + * root page. + * Algorithm is the following: + * + * insert root R-Tree page in the sorted list + * while sorted list is not empty: + * get top element from the sorted list + * if it is tree leaf (record) then return it as + * current element + * otherwise (R-Tree page) get siblings of this R-Tree + * page and insert them in sorted list + */ + while (true) { + struct rtree_neighbor *neighbor = itr->neigh_list; + if (neighbor == NULL) + return NULL; + void *child = neighbor->child; + int level = neighbor->level; + itr->neigh_list = neighbor->next; + rtree_iterator_free_neighbor(itr, neighbor); + if (level == 0) + return (record_t)child; + struct rtree_page *pg = (struct rtree_page *)child; + for (int i = 0, n = pg->n; i < n; i++) { + struct rtree_page *pg = + (struct rtree_page *)child; + coord_t distance = + rtree_rect_point_distance2(&pg->b[i].rect, + &itr->rect.lower_point); + struct rtree_neighbor *neigh = + rtree_iterator_new_neighbor(itr, pg->b[i].data.page, + distance, level - 1); + rtree_iterator_insert_neighbor(itr, neigh); + } + } + } + int sp = itr->tree->height - 1; + if (!itr->eof && rtree_iterator_goto_next(itr, sp)) + return itr->stack[sp].page->b[itr->stack[sp].pos].data.record; + itr->eof = true; + return NULL; +} + +/*------------------------------------------------------------------------- */ +/* R-tree methods */ +/*------------------------------------------------------------------------- */ + +void +rtree_init(struct rtree *tree, + rtree_page_alloc_t page_alloc, rtree_page_free_t page_free) +{ + tree->n_records = 0; + tree->height = 0; + tree->root = NULL; + tree->version = 0; + tree->n_pages = 0; + tree->page_alloc = page_alloc; + tree->page_free = page_free; +} + +void +rtree_destroy(struct rtree *tree) +{ + rtree_purge(tree); +} + +void +rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj) +{ + if (tree->root == NULL) { + tree->root = rtree_alloc_page(tree); + rtree_page_init_record(tree->root, rect, obj); + tree->height = 1; + tree->n_pages++; + } else { + struct rtree_page *p = + rtree_page_insert(tree, tree->root, rect, obj, tree->height); + if (p != NULL) { + /* root splitted */ + struct rtree_page *new_root = rtree_alloc_page(tree); + rtree_page_init_page(new_root, tree->root, p); + tree->root = new_root; + tree->height++; + tree->n_pages++; + } + } + tree->version++; + tree->n_records++; +} + + +bool +rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj) +{ + struct rtree_reinsert_list rlist; + rlist.chain = NULL; + if (tree->height == 0) + return false; + if (!rtree_page_remove(tree, tree->root, rect, obj, tree->height, &rlist)) + return false; + struct rtree_page *pg = rlist.chain; + int level = rlist.level; + while (pg != NULL) { + for (int i = 0, n = pg->n; i < n; i++) { + struct rtree_page *p = + rtree_page_insert(tree, tree->root, + &pg->b[i].rect, + pg->b[i].data.record, + tree->height - level); + if (p != NULL) { + /* root splitted */ + struct rtree_page *new_root + = rtree_alloc_page(tree); + rtree_page_init_page(new_root, tree->root, p); + tree->root = new_root; + tree->height++; + tree->n_pages++; + } + } + level--; + struct rtree_page *next = get_next_reinsert_page(pg); + rtree_free_page(tree, pg); + tree->n_pages--; + pg = next; + } + if (tree->root->n == 1 && tree->height > 1) { + struct rtree_page *new_root = tree->root->b[0].data.page; + rtree_free_page(tree, tree->root); + tree->root = new_root; + tree->height--; + tree->n_pages--; + } + tree->n_records--; + tree->version++; + return true; +} + +bool +rtree_search(const struct rtree *tree, const struct rtree_rect *rect, + enum spatial_search_op op, struct rtree_iterator *itr) +{ + rtree_iterator_reset(itr); + itr->tree = tree; + itr->version = tree->version; + itr->rect = *rect; + itr->op = op; + assert(tree->height <= RTREE_MAX_HEIGHT); + switch (op) { + case SOP_ALL: + itr->intr_cmp = itr->leaf_cmp = rtree_always_true; + break; + case SOP_EQUALS: + itr->intr_cmp = rtree_rect_in_rect; + itr->leaf_cmp = rtree_rect_equal_to_rect; + break; + case SOP_CONTAINS: + itr->intr_cmp = itr->leaf_cmp = rtree_rect_in_rect; + break; + case SOP_STRICT_CONTAINS: + itr->intr_cmp = itr->leaf_cmp = rtree_rect_strict_in_rect; + break; + case SOP_OVERLAPS: + itr->intr_cmp = itr->leaf_cmp = rtree_rect_intersects_rect; + break; + case SOP_BELONGS: + itr->intr_cmp = rtree_rect_intersects_rect; + itr->leaf_cmp = rtree_rect_holds_rect; + break; + case SOP_STRICT_BELONGS: + itr->intr_cmp = rtree_rect_intersects_rect; + itr->leaf_cmp = rtree_rect_strict_holds_rect; + break; + case SOP_NEIGHBOR: + if (tree->root) { + struct rtree_rect cover = rtree_page_cover(tree->root); + coord_t distance = + rtree_rect_point_distance2(&cover, + &rect->lower_point); + itr->neigh_list = + rtree_iterator_new_neighbor(itr, tree->root, + distance, + tree->height); + return true; + } else { + itr->neigh_list = NULL; + return false; + } + } + if (tree->root && rtree_iterator_goto_first(itr, 0, tree->root)) { + itr->stack[tree->height-1].pos -= 1; + /* will be incremented by goto_next */ + itr->eof = false; + return true; + } else { + itr->eof = true; + return false; + } +} + +void +rtree_purge(struct rtree *tree) +{ + if (tree->root != NULL) { + rtree_page_purge(tree, tree->root, tree->height); + tree->root = NULL; + tree->n_records = 0; + tree->n_pages = 0; + tree->height = 0; + } +} + +size_t +rtree_used_size(const struct rtree *tree) +{ + return tree->n_pages * RTREE_PAGE_SIZE; +} + +unsigned +rtree_number_of_records(const struct rtree *tree) { + return tree->n_records; +} + +#if 0 +void +rtree_debug_print_page(const struct rtree_page *page, unsigned level, unsigned path) +{ + printf("%d:", path); + if (--level) { + for (int i = 0; i < page->n; i++) { + printf(" ["); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.lower_point.coords[j]); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.upper_point.coords[j]); + printf("]"); + } + printf("\n"); + for (int i = 0; i < page->n; i++) + rtree_debug_print_page(page->b[i].data.page, level, path * 100 + i); + } else { + for (int i = 0; i < page->n; i++) { + printf(" ["); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.lower_point.coords[j]); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.upper_point.coords[j]); + printf(": %p]", (void *)page->b[i].data.record); + } + printf("\n"); + } +} + +void +rtree_debug_print(const struct rtree *tree) +{ + if (tree->root) + rtree_debug_print_page(tree->root, tree->height, 0); +} +#endif + diff --git a/src/lib/salad/rtree.h b/src/lib/salad/rtree.h new file mode 100644 index 0000000000..646e4b22c6 --- /dev/null +++ b/src/lib/salad/rtree.h @@ -0,0 +1,146 @@ +/* + * Guttman's R-Tree + * Copyright (C) 2014 Mail.RU + */ + +#ifndef TARANTOOL_RTREE_H_INCLUDED +#define TARANTOOL_RTREE_H_INCLUDED + +#include <stddef.h> +#include <stdbool.h> + +/* Type of payload data */ +typedef void *record_t; +/* Type of coordinate */ +typedef double coord_t; +/* Minimal and maximal coordinate */ +#include <float.h> +#define RTREE_COORD_MAX DBL_MAX +#define RTREE_COORD_MIN DBL_MIN + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +enum { + /* Number of dimensions of R-tree geometry */ + RTREE_DIMENSION = 2, + /* Maximal possible R-tree height */ + RTREE_MAX_HEIGHT = 16, + /* R-Tree use linear search within element on the page, + so larger page cause worse performance */ + RTREE_PAGE_SIZE = 1024 +}; + +enum spatial_search_op +{ + SOP_ALL, + SOP_EQUALS, + SOP_CONTAINS, + SOP_STRICT_CONTAINS, + SOP_OVERLAPS, + SOP_BELONGS, + SOP_STRICT_BELONGS, + SOP_NEIGHBOR +}; + +typedef void* (*rtree_page_alloc_t)(); +typedef void (*rtree_page_free_t)(void*); + +struct rtree_point +{ + coord_t coords[RTREE_DIMENSION]; +}; + +struct rtree_rect +{ + struct rtree_point lower_point, upper_point; +}; + +typedef bool (*rtree_comparator_t)(const struct rtree_rect *rt1, + const struct rtree_rect *rt2); + +struct rtree +{ + struct rtree_page *root; + unsigned n_records; + unsigned height; + unsigned version; + unsigned n_pages; + rtree_page_alloc_t page_alloc; + rtree_page_free_t page_free; +}; + +struct rtree_iterator +{ + const struct rtree *tree; + struct rtree_rect rect; + enum spatial_search_op op; + bool eof; + int version; + + struct rtree_neighbor *neigh_list; + struct rtree_neighbor *neigh_free_list; + struct rtree_neighbor_page *page_list; + int page_pos; + + rtree_comparator_t intr_cmp; + rtree_comparator_t leaf_cmp; + + struct { + struct rtree_page *page; + int pos; + } stack[RTREE_MAX_HEIGHT]; +}; + +void +rtree_rect_normalize(struct rtree_rect *rect); + +void +rtree_set2d(struct rtree_rect *rect, + coord_t left, coord_t bottom, coord_t right, coord_t top); + +void +rtree_init(struct rtree *tree, rtree_page_alloc_t page_alloc, rtree_page_free_t page_free); + +void +rtree_destroy(struct rtree *tree); + +void +rtree_purge(struct rtree *tree); + +bool +rtree_search(const struct rtree *tree, const struct rtree_rect *rect, + enum spatial_search_op op, struct rtree_iterator *itr); + +void +rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj); + +bool +rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj); + +size_t +rtree_used_size(const struct rtree *tree); + +unsigned +rtree_number_of_records(const struct rtree *tree); + +#if 0 +void +rtree_debug_print(const struct rtree *tree); +#endif + +void +rtree_iterator_init(struct rtree_iterator *itr); + +void +rtree_iterator_destroy(struct rtree_iterator *itr); + +record_t +rtree_iterator_next(struct rtree_iterator *itr); + +#if defined(__cplusplus) +} +#endif /* defined(__cplusplus) */ + +#endif /* #ifndef TARANTOOL_RTREE_H_INCLUDED */ diff --git a/test/box/rtree_benchmark.result b/test/box/rtree_benchmark.result index 2042d6d355..1658290d4f 100644 --- a/test/box/rtree_benchmark.result +++ b/test/box/rtree_benchmark.result @@ -23,7 +23,7 @@ s:create_index('spatial', { type = 'rtree', parts = {2, 'box'}}) name: spatial type: RTREE ... -n_records = 100000 +n_records = 20000 --- ... n_iterations = 10000 diff --git a/test/box/rtree_benchmark.test.lua b/test/box/rtree_benchmark.test.lua index 08aa631851..5f8457b982 100644 --- a/test/box/rtree_benchmark.test.lua +++ b/test/box/rtree_benchmark.test.lua @@ -2,7 +2,7 @@ s = box.schema.create_space('rtreebench') s:create_index('primary') s:create_index('spatial', { type = 'rtree', parts = {2, 'box'}}) -n_records = 100000 +n_records = 20000 n_iterations = 10000 n_neighbors = 10 diff --git a/test/box/rtree_rect.result b/test/box/rtree_rect.result index ca8e4dd34e..1206beeaeb 100644 --- a/test/box/rtree_rect.result +++ b/test/box/rtree_rect.result @@ -51,8 +51,8 @@ s.index.spatial:select({0,0,5,5}, {iterator = 'LT'}) --- - [] ... --- select records strict belonging to rectangle (4,4,10,10) -s.index.spatial:select({4,4,10,10}, {iterator = 'LT'}) +-- select records strict belonging to rectangle (4,4,11,11) +s.index.spatial:select({4,4,11,11}, {iterator = 'LT'}) --- - - [2, [5, 5, 10, 10]] ... diff --git a/test/box/rtree_rect.test.lua b/test/box/rtree_rect.test.lua index a22da55323..a71f0a418b 100644 --- a/test/box/rtree_rect.test.lua +++ b/test/box/rtree_rect.test.lua @@ -12,8 +12,8 @@ s.index.spatial:select({}, {iterator = 'ALL'}) s.index.spatial:select({0,0,5,5}, {iterator = 'LE'}) -- select records strict belonging to rectangle (0,0,5,5) s.index.spatial:select({0,0,5,5}, {iterator = 'LT'}) --- select records strict belonging to rectangle (4,4,10,10) -s.index.spatial:select({4,4,10,10}, {iterator = 'LT'}) +-- select records strict belonging to rectangle (4,4,11,11) +s.index.spatial:select({4,4,11,11}, {iterator = 'LT'}) -- select records containing point (5,5) s.index.spatial:select({5,5}, {iterator = 'GE'}) -- select records containing rectangle (1,1,2,2) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index a7bdd49104..3bd8a6d3f4 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -41,9 +41,9 @@ add_executable(bps_tree.test bps_tree.cc ${CMAKE_SOURCE_DIR}/third_party/qsort_a target_link_libraries(bps_tree.test small) add_executable(bps_tree_itr.test bps_tree_itr.cc ${CMAKE_SOURCE_DIR}/third_party/qsort_arg.c) target_link_libraries(bps_tree_itr.test small) -add_executable(rtree.test rtree.cc ${CMAKE_SOURCE_DIR}/third_party/rtree.cc) +add_executable(rtree.test rtree.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtree.c) target_link_libraries(rtree.test) -add_executable(rtree_itr.test rtree_itr.cc ${CMAKE_SOURCE_DIR}/third_party/rtree.cc) +add_executable(rtree_itr.test rtree_itr.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtree.c) target_link_libraries(rtree_itr.test) add_executable(matras.test matras.cc) target_link_libraries(matras.test small) diff --git a/test/unit/rtree.cc b/test/unit/rtree.cc index 4de3666939..9e4960c862 100644 --- a/test/unit/rtree.cc +++ b/test/unit/rtree.cc @@ -5,7 +5,7 @@ #include <inttypes.h> #include "unit.h" -#include "rtree.h" +#include "salad/rtree.h" static int page_count = 0; @@ -26,52 +26,58 @@ page_free(void *page) static void simple_check() { - rectangle_t r; - R_tree_iterator iterator; + struct rtree_rect rect; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); const size_t rounds = 2000; header(); - R_tree tree(page_alloc, page_free); + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); printf("Insert 1..X, remove 1..X\n"); for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (tree.search(r, SOP_EQUALS, iterator)) { + if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { fail("element already in tree (1)", "true"); } - tree.insert(r, rec); + rtree_insert(&tree, &rect, rec); } - if (tree.number_of_records() != rounds) { + if (rtree_number_of_records(&tree) != rounds) { fail("Tree count mismatch (1)", "true"); } for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (!tree.search(r, SOP_EQUALS, iterator)) { + if (!rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { fail("element in tree (1)", "false"); } - if (iterator.next() != rec) { + if (rtree_iterator_next(&iterator) != rec) { fail("right search result (1)", "true"); } - if (iterator.next()) { + if (rtree_iterator_next(&iterator)) { fail("single search result (1)", "true"); } - if (!tree.remove(r, rec)) { + if (!rtree_remove(&tree, &rect, rec)) { fail("delete element in tree (1)", "false"); } - if (tree.search(r, SOP_EQUALS, iterator)) { + if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { fail("element still in tree (1)", "true"); } } - if (tree.number_of_records() != 0) { + if (rtree_number_of_records(&tree) != 0) { fail("Tree count mismatch (1)", "true"); } @@ -79,40 +85,44 @@ simple_check() for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (tree.search(r, SOP_OVERLAPS, iterator)) { + if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { fail("element already in tree (2)", "true"); } - tree.insert(r, rec); + rtree_insert(&tree, &rect, rec); } - if (tree.number_of_records() != rounds) { + if (rtree_number_of_records(&tree) != rounds) { fail("Tree count mismatch (2)", "true"); } for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (!tree.search(r, SOP_OVERLAPS, iterator)) { + if (!rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) { fail("element in tree (2)", "false"); } - if (iterator.next() != rec) { + if (rtree_iterator_next(&iterator) != rec) { fail("right search result (2)", "true"); } - if (iterator.next()) { + if (rtree_iterator_next(&iterator)) { fail("single search result (2)", "true"); } - if (!tree.remove(r, rec)) { + if (!rtree_remove(&tree, &rect, rec)) { fail("delete element in tree (2)", "false"); } - if (tree.search(r, SOP_OVERLAPS, iterator)) { + if (rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) { fail("element still in tree (2)", "true"); } } - if (tree.number_of_records() != 0) { + if (rtree_number_of_records(&tree) != 0) { fail("Tree count mismatch (2)", "true"); } @@ -121,40 +131,44 @@ simple_check() for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (tree.search(r, SOP_BELONGS, iterator)) { + if (rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { fail("element already in tree (3)", "true"); } - tree.insert(r, rec); + rtree_insert(&tree, &rect, rec); } - if (tree.number_of_records() != rounds) { + if (rtree_number_of_records(&tree) != rounds) { fail("Tree count mismatch (3)", "true"); } for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (!tree.search(r, SOP_BELONGS, iterator)) { + if (!rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { fail("element in tree (3)", "false"); } - if (iterator.next() != rec) { + if (rtree_iterator_next(&iterator) != rec) { fail("right search result (3)", "true"); } - if (iterator.next()) { + if (rtree_iterator_next(&iterator)) { fail("single search result (3)", "true"); } - if (!tree.remove(r, rec)) { + if (!rtree_remove(&tree, &rect, rec)) { fail("delete element in tree (3)", "false"); } - if (tree.search(r, SOP_BELONGS, iterator)) { + if (rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { fail("element still in tree (3)", "true"); } } - if (tree.number_of_records() != 0) { + if (rtree_number_of_records(&tree) != 0) { fail("Tree count mismatch (3)", "true"); } @@ -163,54 +177,61 @@ simple_check() for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (tree.search(r, SOP_CONTAINS, iterator)) { + if (rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { fail("element already in tree (4)", "true"); } - tree.insert(r, rec); + rtree_insert(&tree, &rect, rec); } - if (tree.number_of_records() != rounds) { + if (rtree_number_of_records(&tree) != rounds) { fail("Tree count mismatch (4)", "true"); } for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - r.boundary[0] = r.boundary[1] = i; - r.boundary[2] = r.boundary[3] = i + 0.5; + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; - if (!tree.search(r, SOP_CONTAINS, iterator)) { + if (!rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { fail("element in tree (4)", "false"); } - if (iterator.next() != rec) { + if (rtree_iterator_next(&iterator) != rec) { fail("right search result (4)", "true"); } - if (iterator.next()) { + if (rtree_iterator_next(&iterator)) { fail("single search result (4)", "true"); } - if (!tree.remove(r, rec)) { + if (!rtree_remove(&tree, &rect, rec)) { fail("delete element in tree (4)", "false"); } - if (tree.search(r, SOP_CONTAINS, iterator)) { + if (rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { fail("element still in tree (4)", "true"); } } - if (tree.number_of_records() != 0) { + if (rtree_number_of_records(&tree) != 0) { fail("Tree count mismatch (4)", "true"); } - tree.purge(); + rtree_purge(&tree); + rtree_destroy(&tree); + + rtree_iterator_destroy(&iterator); footer(); } static void -rtree_test_build(R_tree& tree, rectangle_t* arr, int count) +rtree_test_build(struct rtree *tree, struct rtree_rect *arr, int count) { for (size_t i = 0; i < count; i++) { record_t rec = (record_t)(i + 1); - tree.insert(arr[i], rec); + rtree_insert(tree, &arr[i], rec); } } @@ -220,32 +241,39 @@ neighbor_test() header(); const int test_count = 1000; - R_tree_iterator iterator; - rectangle_t arr[test_count]; - static rectangle_t basis; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + struct rtree_rect arr[test_count]; + static struct rtree_rect basis; for (size_t i = 0; i < test_count; i++) { - arr[i].boundary[0] = arr[i].boundary[1] = i; - arr[i].boundary[2] = arr[i].boundary[3] = i+1; + arr[i].lower_point.coords[0] = i; + arr[i].lower_point.coords[1] = i; + arr[i].upper_point.coords[0] = i + 1; + arr[i].upper_point.coords[1] = i + 1; } for (size_t i = 0; i <= test_count; i++) { - R_tree tree(page_alloc, page_free); + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); - rtree_test_build(tree, arr, i); + rtree_test_build(&tree, arr, i); - if (!tree.search(basis, SOP_NEIGHBOR, iterator) && i != 0) { + if (!rtree_search(&tree, &basis, SOP_NEIGHBOR, &iterator) && i != 0) { fail("search is successful", "true"); } for (size_t j = 0; j < i; j++) { - record_t rec = iterator.next(); + record_t rec = rtree_iterator_next(&iterator); if (rec != record_t(j+1)) { fail("wrong search result", "true"); } } + rtree_destroy(&tree); } + rtree_iterator_destroy(&iterator); + footer(); } diff --git a/test/unit/rtree_itr.cc b/test/unit/rtree_itr.cc index 554777cafd..fae7c43110 100644 --- a/test/unit/rtree_itr.cc +++ b/test/unit/rtree_itr.cc @@ -4,7 +4,7 @@ #include <stdbool.h> #include "unit.h" -#include "rtree.h" +#include "salad/rtree.h" static int page_count = 0; @@ -27,44 +27,46 @@ itr_check() { header(); - R_tree tree(page_alloc, page_free); + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); /* Filling tree */ const size_t count1 = 10000; const size_t count2 = 5; - rectangle_t r; + struct rtree_rect rect; size_t count = 0; record_t rec; - R_tree_iterator iterator; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); for (size_t i = 0; i < count1; i++) { - r.boundary[0] = r.boundary[1] = i * 2 * count2; /* note that filled with even numbers */ + coord_t coord = i * 2 * count2; /* note that filled with even numbers */ for (size_t j = 0; j < count2; j++) { - r.boundary[2] = r.boundary[3] = r.boundary[0] + j; - tree.insert(r, record_t(++count)); + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + rtree_insert(&tree, &rect, record_t(++count)); } } - printf("Test tree size: %d\n", (int)tree.number_of_records()); + printf("Test tree size: %d\n", (int)rtree_number_of_records(&tree)); /* Test that tree filled ok */ for (size_t i = 0; i < count1; i++) { for (size_t j = 0; j < count2; j++) { - r.boundary[0] = r.boundary[1] = i * 2 * count2; - r.boundary[2] = r.boundary[3] = r.boundary[0] + j; - if (!tree.search(r, SOP_BELONGS, iterator)) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (!rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { fail("Integrity check failed (1)", "false"); } for (size_t k = 0; k <= j; k++) { - if (!iterator.next()) { + if (!rtree_iterator_next(&iterator)) { fail("Integrity check failed (2)", "false"); } } - if (iterator.next()) { + if (rtree_iterator_next(&iterator)) { fail("Integrity check failed (3)", "true"); } - r.boundary[0] = r.boundary[1] = (i * 2 + 1) * count2; - r.boundary[2] = r.boundary[3] = r.boundary[0] + j; - if (tree.search(r, SOP_BELONGS, iterator)) { + coord = (i * 2 + 1) * count2;; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { fail("Integrity check failed (4)", "true"); } } @@ -72,13 +74,13 @@ itr_check() /* Print 7 elems closest to coordinate basis */ { - static rectangle_t basis; + static struct rtree_rect basis; printf("--> "); - if (!tree.search(basis, SOP_NEIGHBOR, iterator)) { + if (!rtree_search(&tree, &basis, SOP_NEIGHBOR, &iterator)) { fail("Integrity check failed (5)", "false"); } for (int i = 0; i < 7; i++) { - rec = iterator.next(); + rec = rtree_iterator_next(&iterator); if (rec == 0) { fail("Integrity check failed (6)", "false"); } @@ -89,12 +91,13 @@ itr_check() /* Print 7 elems closest to the point [(count1-1)*count2*2, (count1-1)*count2*2] */ { printf("<-- "); - r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = (count1-1)*count2*2; - if (!tree.search(r, SOP_NEIGHBOR, iterator)) { + coord_t coord = (count1 - 1) * count2 * 2; + rtree_set2d(&rect, coord, coord, coord, coord); + if (!rtree_search(&tree, &rect, SOP_NEIGHBOR, &iterator)) { fail("Integrity check failed (5)", "false"); } for (int i = 0; i < 7; i++) { - rec = iterator.next(); + rec = rtree_iterator_next(&iterator); if (rec == 0) { fail("Integrity check failed (6)", "false"); } @@ -103,55 +106,84 @@ itr_check() printf("\n"); } - /* Test strict besize_ts */ + /* Test strict belongs */ for (size_t i = 0; i < count1; i++) { for (size_t j = 0; j < count2; j++) { - r.boundary[0] = r.boundary[1] = i * 2 * count2; - r.boundary[2] = r.boundary[3] = r.boundary[0] + j; - if (!tree.search(r, SOP_STRICT_BELONGS, iterator) && j != 0) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord - 0.1, coord - 0.1, coord + j, coord + j); + if (!rtree_search(&tree, &rect, SOP_STRICT_BELONGS, &iterator) && j != 0) { fail("Integrity check failed (7)", "false"); } for (size_t k = 0; k < j; k++) { - if (!iterator.next()) { + if (!rtree_iterator_next(&iterator)) { fail("Integrity check failed (8)", "false"); } } - if (iterator.next()) { + if (rtree_iterator_next(&iterator)) { fail("Integrity check failed (9)", "true"); } - r.boundary[0] = r.boundary[1] = (i * 2 + 1) * count2; - r.boundary[2] = r.boundary[3] = r.boundary[0] + j; - if (tree.search(r, SOP_STRICT_BELONGS, iterator)) { + coord = (i * 2 + 1) * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_STRICT_BELONGS, &iterator)) { fail("Integrity check failed (10)", "true"); } } } - /* Test strict contains */ + /* Test contains */ for (size_t i = 0; i < count1; i++) { for (size_t j = 0; j < count2; j++) { - r.boundary[0] = r.boundary[1] = i * 2 * count2; - r.boundary[2] = r.boundary[3] = r.boundary[0] + j; - if (!tree.search(r, SOP_STRICT_CONTAINS, iterator) && j != count2-1) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (!rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { fail("Integrity check failed (11)", "false"); } - for (size_t k = j; k < count2-1; k++) { - if (!iterator.next()) { + for (size_t k = j; k < count2; k++) { + if (!rtree_iterator_next(&iterator)) { fail("Integrity check failed (12)", "false"); } } - if (iterator.next()) { + if (rtree_iterator_next(&iterator)) { + fail("Integrity check failed (13)", "true"); + } + coord = (i * 2 + 1) * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { + fail("Integrity check failed (14)", "true"); + } + } + } + + /* Test strict contains */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord + 0.1, coord + 0.1, coord + j, coord + j); + rtree_rect_normalize(&rect); + if (!rtree_search(&tree, &rect, SOP_STRICT_CONTAINS, &iterator) && j != 0 && j != count2 - 1) { + fail("Integrity check failed (11)", "false"); + } + if (j) { + for (size_t k = j; k < count2 - 1; k++) { + if (!rtree_iterator_next(&iterator)) { + fail("Integrity check failed (12)", "false"); + } + } + } + if (rtree_iterator_next(&iterator)) { fail("Integrity check failed (13)", "true"); } - r.boundary[0] = r.boundary[1] = (i * 2 + 1) * count2; - r.boundary[2] = r.boundary[3] = r.boundary[0] + j; - if (tree.search(r, SOP_STRICT_CONTAINS, iterator)) { + coord = (i * 2 + 1) * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_STRICT_CONTAINS, &iterator)) { fail("Integrity check failed (14)", "true"); } } } - tree.purge(); + rtree_purge(&tree); + rtree_destroy(&tree); + rtree_iterator_destroy(&iterator); footer(); } @@ -165,10 +197,11 @@ itr_invalidate_check() const size_t max_delete_count = 100; const size_t max_insert_count = 200; const size_t attempt_count = 100; - struct R_tree_iterator iterators[test_size]; + struct rtree_iterator iterators[test_size]; + for (size_t i = 0; i < test_size; i++) + rtree_iterator_init(iterators + i); - R_tree tree(page_alloc, page_free); - rectangle_t r; + struct rtree_rect rect; /* invalidation during deletion */ srand(0); @@ -178,35 +211,36 @@ itr_invalidate_check() if (del_pos + del_cnt > test_size) { del_cnt = test_size - del_pos; } - R_tree tree(page_alloc, page_free); + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); for (size_t i = 0; i < test_size; i++) { - r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; - tree.insert(r, record_t(i+1)); + rtree_set2d(&rect, i, i, i, i); + rtree_insert(&tree, &rect, record_t(i+1)); } - r.boundary[0] = r.boundary[1] = 0; - r.boundary[2] = r.boundary[3] = test_size; - tree.search(r, SOP_BELONGS, iterators[0]); - if (!iterators[0].next()) { + rtree_set2d(&rect, 0, 0, test_size, test_size); + rtree_search(&tree, &rect, SOP_BELONGS, &iterators[0]); + if (!rtree_iterator_next(&iterators[0])) { fail("Integrity check failed (15)", "false"); } for (size_t i = 1; i < test_size; i++) { iterators[i] = iterators[i - 1]; - if (!iterators[i].next()) { + if (!rtree_iterator_next(&iterators[i])) { fail("Integrity check failed (16)", "false"); } } for (size_t i = del_pos; i < del_pos + del_cnt; i++) { - r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; - if (!tree.remove(r, record_t(i+1))) { + rtree_set2d(&rect, i, i, i, i); + if (!rtree_remove(&tree, &rect, record_t(i+1))) { fail("Integrity check failed (17)", "false"); } } for (size_t i = 0; i < test_size; i++) { - if (iterators[i].next()) { + if (rtree_iterator_next(&iterators[i])) { fail("Iterator was not invalidated (18)", "true"); } } + rtree_destroy(&tree); } /* invalidation during insertion */ @@ -215,35 +249,39 @@ itr_invalidate_check() size_t ins_pos = rand() % test_size; size_t ins_cnt = rand() % max_insert_count + 1; - R_tree tree(page_alloc, page_free); + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); for (size_t i = 0; i < test_size; i++) { - r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; - tree.insert(r, record_t(i+1)); + rtree_set2d(&rect, i, i, i, i); + rtree_insert(&tree, &rect, record_t(i+1)); } - r.boundary[0] = r.boundary[1] = 0; - r.boundary[2] = r.boundary[3] = test_size; - tree.search(r, SOP_BELONGS, iterators[0]); - if (!iterators[0].next()) { + rtree_set2d(&rect, 0, 0, test_size, test_size); + rtree_search(&tree, &rect, SOP_BELONGS, &iterators[0]); + if (!rtree_iterator_next(&iterators[0])) { fail("Integrity check failed (19)", "false"); } for (size_t i = 1; i < test_size; i++) { iterators[i] = iterators[i - 1]; - if (!iterators[0].next()) { + if (!rtree_iterator_next(&iterators[0])) { fail("Integrity check failed (20)", "false"); } } for (size_t i = ins_pos; i < ins_pos + ins_cnt; i++) { - r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; - tree.insert(r, record_t(test_size + i - ins_pos + 1)); + rtree_set2d(&rect, i, i, i, i); + rtree_insert(&tree, &rect, record_t(test_size + i - ins_pos + 1)); } for (size_t i = 0; i < test_size; i++) { - if (iterators[i].next()) { + if (rtree_iterator_next(&iterators[i])) { fail("Iterator was not invalidated (22)", "true"); } } + rtree_destroy(&tree); } + for (size_t i = 0; i < test_size; i++) + rtree_iterator_destroy(iterators + i); + footer(); } diff --git a/third_party/rtree.cc b/third_party/rtree.cc deleted file mode 100644 index 5b813f80c3..0000000000 --- a/third_party/rtree.cc +++ /dev/null @@ -1,616 +0,0 @@ -#include <new> -#include <string.h> -#include <assert.h> -#include "rtree.h" - - -class R_page { -public: - struct branch { - rectangle_t r; - R_page* p; - }; - - enum { - /* maximal number of branches at page */ - CARD = (RTREE_PAGE_SIZE-4)/sizeof(branch), - /* minimal number of branches at non-root page */ - MIN_FILL = CARD/2 - }; - - struct reinsert_list { - R_page* chain; - int level; - reinsert_list() { chain = NULL; } - }; - - R_page* insert(R_tree* tree, rectangle_t const& r, - record_t obj, int level); - - bool remove(R_tree* tree, rectangle_t const& r, record_t obj, - int level, reinsert_list& rlist); - - rectangle_t cover() const; - - R_page* split_page(R_tree* tree, branch const& br); - - R_page* add_branch(R_tree* tree, branch const& br) { - if (n < CARD) { - b[n++] = br; - return NULL; - } else { - return split_page(tree, br); - } - } - void remove_branch(int i); - - void purge(R_tree* tree, int level); - - R_page* next_reinsert_page() const { return (R_page*)b[CARD-1].p; } - - R_page(rectangle_t const& rect, record_t obj); - R_page(R_page* old_root, R_page* new_page); - - int n; /* number of branches at page */ - branch b[CARD]; -}; - -R_tree::R_tree(page_alloc_t pg_alloc, page_free_t pg_free) -{ - n_records = 0; - height = 0; - root = NULL; - update_count = 0; - n_pages = 0; - page_alloc = pg_alloc; - page_free = pg_free; -} - -R_tree::~R_tree() -{ - purge(); -} - -void R_tree::insert(rectangle_t const& r, record_t obj) -{ - if (root == NULL) { - root = new (page_alloc()) R_page(r, obj); - height = 1; - n_pages += 1; - } else { - R_page* p = root->insert(this, r, obj, height); - if (p != NULL) { - /* root splitted */ - root = new (page_alloc()) R_page(root, p); - height += 1; - n_pages += 1; - } - } - update_count += 1; - n_records += 1; -} - - -bool R_tree::remove(rectangle_t const& r, record_t obj) -{ - R_page::reinsert_list rlist; - if (height != 0 && root->remove(this, r, obj, height, rlist)) { - R_page* pg = rlist.chain; - int level = rlist.level; - while (pg != NULL) { - for (int i = 0, n = pg->n; i < n; i++) { - R_page* p = root->insert(this, - pg->b[i].r, - pg->b[i].p, - height-level); - if (p != NULL) { - /* root splitted */ - root = new (page_alloc()) R_page(root, p); - height += 1; - n_pages += 1; - } - } - level -= 1; - R_page* next = pg->next_reinsert_page(); - page_free(pg); - n_pages -= 1; - pg = next; - } - if (root->n == 1 && height > 1) { - R_page* new_root = root->b[0].p; - page_free(root); - root = new_root; - height -= 1; - n_pages -= 1; - } - n_records -= 1; - update_count += 1; - return true; - } - return false; -} - -bool R_tree_iterator::goto_first(int sp, R_page* pg) -{ - if (sp+1 == tree->height) { - for (int i = 0, n = pg->n; i < n; i++) { - if ((r.*leaf_cmp)(pg->b[i].r)) { - stack[sp].page = pg; - stack[sp].pos = i; - return true; - } - } - } else { - for (int i = 0, n = pg->n; i < n; i++) { - if ((r.*intr_cmp)(pg->b[i].r) - && goto_first(sp+1, pg->b[i].p)) - { - stack[sp].page = pg; - stack[sp].pos = i; - return true; - } - } - } - return false; -} - - -bool R_tree_iterator::goto_next(int sp) -{ - R_page* pg = stack[sp].page; - if (sp+1 == tree->height) { - for (int i = stack[sp].pos, n = pg->n; ++i < n;) { - if ((r.*leaf_cmp)(pg->b[i].r)) { - stack[sp].pos = i; - return true; - } - } - } else { - for (int i = stack[sp].pos, n = pg->n; ++i < n;) { - if ((r.*intr_cmp)(pg->b[i].r) - && goto_first(sp+1, pg->b[i].p)) - { - stack[sp].page = pg; - stack[sp].pos = i; - return true; - } - } - } - return sp > 0 ? goto_next(sp-1) : false; -} - -R_tree_iterator::R_tree_iterator() -{ - list = NULL; - free = NULL; - tree = NULL; - page_list = NULL; - page_pos = N_ELEMS; -} - -R_tree_iterator::~R_tree_iterator() -{ - Neighbor_page *curr, *next; - for (curr = page_list; curr != NULL; curr = next) { - next = curr->next; - tree->page_free(curr); - } - page_list = NULL; - page_pos = N_ELEMS; -} - -void R_tree_iterator::reset() -{ - if (list != NULL) { - Neighbor** npp = &free; - while (*npp != NULL) { - npp = &(*npp)->next; - } - *npp = list; - list = NULL; - } -} - - -R_tree_iterator::Neighbor* R_tree_iterator::allocate_neighbour() -{ - if (page_pos >= N_ELEMS) { - Neighbor_page* new_page = (Neighbor_page*)tree->page_alloc(); - new_page->next = page_list; - page_list = new_page; - page_pos = 0; - } - return &page_list->buf[page_pos++]; -} - -bool R_tree_iterator::init(R_tree const* tree, rectangle_t const& r, - Spatial_search_op op) -{ - reset(); - this->tree = (R_tree*)tree; - this->update_count = tree->update_count; - this->r = r; - this->op = op; - assert(tree->height <= MAX_HEIGHT); - switch (op) { - case SOP_ALL: - intr_cmp = leaf_cmp = &rectangle_t::operator_true; - break; - case SOP_EQUALS: - intr_cmp = &rectangle_t::operator <=; - leaf_cmp = &rectangle_t::operator ==; - break; - case SOP_CONTAINS: - intr_cmp = leaf_cmp = &rectangle_t::operator <=; - break; - case SOP_STRICT_CONTAINS: - intr_cmp = leaf_cmp = &rectangle_t::operator <; - break; - case SOP_OVERLAPS: - intr_cmp = leaf_cmp = &rectangle_t::operator &; - break; - case SOP_BELONGS: - intr_cmp = &rectangle_t::operator &; - leaf_cmp = &rectangle_t::operator >=; - break; - case SOP_STRICT_BELONGS: - intr_cmp = &rectangle_t::operator &; - leaf_cmp = &rectangle_t::operator >; - break; - case SOP_NEIGHBOR: - if (tree->root) { - list = new_neighbor(tree->root, - tree->root->cover().distance2(r.boundary), - tree->height); - return true; - } else { - list = NULL; - return false; - } - } - if (tree->root && goto_first(0, tree->root)) { - stack[tree->height-1].pos -= 1; - /* will be incremented by goto_next */ - eof = false; - return true; - } else { - eof = true; - return false; - } -} - -void R_tree_iterator::insert(Neighbor* node) -{ - Neighbor *prev = NULL, *next = list; - area_t distance = node->distance; - while (next != NULL && next->distance < distance) { - prev = next; - next = prev->next; - } - node->next = next; - if (prev == NULL) { - list = node; - } else { - prev->next = node; - } -} - -R_tree_iterator::Neighbor* R_tree_iterator::new_neighbor(void* child, - area_t distance, - int level) -{ - Neighbor* n = free; - if (n == NULL) { - n = allocate_neighbour(); - } else { - free = n->next; - } - n->child = child; - n->distance = distance; - n->level = level; - n->next = NULL; - return n; -} - -void R_tree_iterator::free_neighbor(Neighbor* n) -{ - n->next = free; - free = n; -} - -record_t R_tree_iterator::next() -{ - if (update_count != tree->update_count) { - /* Index was updated since cursor initialziation */ - return NULL; - } - if (op == SOP_NEIGHBOR) { - /* To return element in order of increasing distance from - * specified point, we build sorted list of R-Tree items - * (ordered by distance from specified point) starting from - * root page. - * Algorithm is the following: - * - * insert root R-Tree page in the sorted list - * while sorted list is not empty: - * get top element from the sorted list - * if it is tree leaf (record) then return it as - * current element - * otherwise (R-Tree page) get siblings of this R-Tree - * page and insert them in sorted list - */ - while (true) { - Neighbor* neighbor = list; - if (neighbor == NULL) { - return NULL; - } - R_page* pg = (R_page*)neighbor->child; - int level = neighbor->level; - list = neighbor->next; - free_neighbor(neighbor); - if (level == 0) { - return (record_t*)pg; - } - for (int i = 0, n = pg->n; i < n; i++) { - insert(new_neighbor(pg->b[i].p, - pg->b[i].r.distance2(r.boundary), - level-1)); - } - } - } - int sp = tree->height-1; - if (!eof && goto_next(sp)) { - return stack[sp].page->b[stack[sp].pos].p; - } - eof = true; - return NULL; -} - -bool R_tree::search(rectangle_t const& r, Spatial_search_op op, R_tree_iterator& iterator) const -{ - return iterator.init(this, r, op); -} - -void R_tree::purge() -{ - if (root != NULL) { - root->purge(this, height); - root = NULL; - n_records = 0; - n_pages = 0; - height = 0; - } -} - -/*------------------------------------------------------------------------- */ -/* R-tree page methods */ -/*------------------------------------------------------------------------- */ - - -/* Create root page */ -R_page::R_page(rectangle_t const& r, record_t obj) -{ - n = 1; - b[0].r = r; - b[0].p = (R_page*)obj; -} - -/* Create new root page (root splitting) */ -R_page::R_page(R_page* old_root, R_page* new_page) -{ - n = 2; - b[0].r = old_root->cover(); - b[0].p = old_root; - b[1].r = new_page->cover(); - b[1].p = new_page; -} - -/* Calculate cover of all rectangles at page */ -rectangle_t R_page::cover() const -{ - rectangle_t r = b[0].r; - for (int i = 1; i < n; i++) { - r += b[i].r; - } - return r; -} - -R_page* R_page::split_page(R_tree* tree, branch const& br) -{ - int i, j, seed[2] = {0,0}; - area_t rect_area[CARD+1], waste, worst_waste = AREA_MIN; - /* - * As the seeds for the two groups, find two rectangles which waste - * the most area if covered by a single rectangle. - */ - rect_area[0] = area(br.r); - for (i = 0; i < CARD; i++) { - rect_area[i+1] = area(b[i].r); - } - branch const* bp = &br; - for (i = 0; i < CARD; i++) { - for (j = i+1; j <= CARD; j++) { - waste = area(bp->r + b[j-1].r) - rect_area[i] - rect_area[j]; - if (waste > worst_waste) { - worst_waste = waste; - seed[0] = i; - seed[1] = j; - } - } - bp = &b[i]; - } - char taken[CARD]; - rectangle_t group[2]; - area_t group_area[2]; - int group_CARD[2]; - R_page* p; - - memset(taken, 0, sizeof taken); - taken[seed[1]-1] = 2; - group[1] = b[seed[1]-1].r; - - if (seed[0] == 0) { - group[0] = br.r; - p = new (tree->page_alloc()) R_page(br.r, br.p); - } else { - group[0] = b[seed[0]-1].r; - p = new (tree->page_alloc()) R_page(group[0], b[seed[0]-1].p); - b[seed[0]-1] = br; - } - tree->n_pages += 1; - group_CARD[0] = group_CARD[1] = 1; - group_area[0] = rect_area[seed[0]]; - group_area[1] = rect_area[seed[1]]; - /* - * Split remaining rectangles between two groups. - * The one chosen is the one with the greatest difference in area - * expansion depending on which group - the rect most strongly - * attracted to one group and repelled from the other. - */ - while (group_CARD[0] + group_CARD[1] < CARD + 1 - && group_CARD[0] < CARD + 1 - MIN_FILL - && group_CARD[1] < CARD + 1 - MIN_FILL) - { - int better_group = -1, chosen = -1; - area_t biggest_diff = -1; - for (i = 0; i < CARD; i++) { - if (!taken[i]) { - area_t diff = (area(group[0] + b[i].r) - group_area[0]) - - (area(group[1] + b[i].r) - group_area[1]); - if (diff > biggest_diff || -diff > biggest_diff) { - chosen = i; - if (diff < 0) { - better_group = 0; - biggest_diff = -diff; - } else { - better_group = 1; - biggest_diff = diff; - } - } - } - } - assert(chosen >= 0); - group_CARD[better_group] += 1; - group[better_group] += b[chosen].r; - group_area[better_group] = area(group[better_group]); - taken[chosen] = better_group+1; - if (better_group == 0) { - p->b[group_CARD[0]-1] = b[chosen]; - } - } - /* - * If one group gets too full, then remaining rectangle are - * split between two groups in such way to balance CARDs of two groups. - */ - if (group_CARD[0] + group_CARD[1] < CARD + 1) { - for (i = 0; i < CARD; i++) { - if (!taken[i]) { - if (group_CARD[0] >= group_CARD[1]) { - taken[i] = 2; - group_CARD[1] += 1; - } else { - taken[i] = 1; - p->b[group_CARD[0]++] = b[i]; - } - } - } - } - p->n = group_CARD[0]; - n = group_CARD[1]; - for (i = 0, j = 0; i < n; j++) { - if (taken[j] == 2) { - b[i++] = b[j]; - } - } - return p; -} - -void R_page::remove_branch(int i) -{ - n -= 1; - memmove(&b[i], &b[i+1], (n-i)*sizeof(branch)); -} - -R_page* R_page::insert(R_tree* tree, rectangle_t const& r, record_t obj, int level) -{ - branch br; - if (--level != 0) { - /* not leaf page */ - int i, mini = 0; - area_t min_incr = AREA_MAX; - area_t best_area = AREA_MAX; - for (i = 0; i < n; i++) { - area_t r_area = area(b[i].r); - area_t incr = area(b[i].r + r) - r_area; - if (incr < min_incr) { - best_area = r_area; - min_incr = incr; - mini = i; - } else if (incr == min_incr && r_area < best_area) { - best_area = r_area; - mini = i; - } - } - R_page* p = b[mini].p; - R_page* q = p->insert(tree, r, obj, level); - if (q == NULL) { - /* child was not split */ - b[mini].r += r; - return NULL; - } else { - /* child was split */ - b[mini].r = p->cover(); - br.p = q; - br.r = q->cover(); - return add_branch(tree, br); - } - } else { - br.p = (R_page*)obj; - br.r = r; - return add_branch(tree, br); - } -} - -bool R_page::remove(R_tree* tree, rectangle_t const& r, record_t rec, - int level, reinsert_list& rlist) -{ - if (--level != 0) { - for (int i = 0; i < n; i++) { - if (b[i].r & r) { - R_page* p = b[i].p; - if (p->remove(tree, r, rec, level, rlist)) { - if (p->n >= MIN_FILL) { - b[i].r = p->cover(); - } else { - /* not enough entries in child */ - p->b[CARD-1].p = rlist.chain; - rlist.chain = p; - rlist.level = level - 1; - remove_branch(i); - } - return true; - } - } - } - } else { - for (int i = 0; i < n; i++) { - if (b[i].p == rec) { - remove_branch(i); - return true; - } - } - } - return false; -} - -void R_page::purge(R_tree* tree, int level) -{ - if (--level != 0) { /* this is an internal node in the tree */ - for (int i = 0; i < n; i++) { - b[i].p->purge(tree, level); - } - } - tree->page_free(this); -} - - diff --git a/third_party/rtree.h b/third_party/rtree.h deleted file mode 100644 index e390a90db3..0000000000 --- a/third_party/rtree.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Guttman's R-Tree - * Copyright (C) 2014 Mail.RU - */ - -#ifndef __RTREE_H__ -#define __RTREE_H__ - -#include <stdlib.h> -#include <limits.h> -#include <math.h> -#include <float.h> - -#define MAX_HEIGHT 16 -#define DIMENSIONS 2 - -typedef double coord_t; -typedef double area_t; -typedef void* record_t; - -#define AREA_MAX DBL_MAX -#define AREA_MIN DBL_MIN - -enum { - RTREE_DIMENSION = 2, - RTREE_PAGE_SIZE = 1024 /* R-Tree use linear search within element on the page, - so larger page cause worse performance */ -}; - -class R_tree; -class R_page; -class R_tree_iterator; - -class rectangle_t -{ -public: - coord_t boundary[RTREE_DIMENSION*2]; - - // Squarer of distance - area_t distance2(coord_t const* point) const - { - area_t d = 0; - for (int i = 0; i < RTREE_DIMENSION; i++) { - if (point[i] < boundary[i]) { - d += (boundary[i] - point[i]) * (boundary[i] - point[i]); - } else if (point[i] > boundary[RTREE_DIMENSION + i]) { - d += (boundary[RTREE_DIMENSION + i] - point[i]) - * (boundary[RTREE_DIMENSION + i] - point[i]); - } - } - return d; - } - - - friend area_t area(rectangle_t const& r) { - area_t area = 1; - for (int i = RTREE_DIMENSION; - --i >= 0; - area *= r.boundary[i+RTREE_DIMENSION] - r.boundary[i]); - return area; - } - - void operator +=(rectangle_t const& r) { - int i = RTREE_DIMENSION; - while (--i >= 0) { - boundary[i] = (boundary[i] <= r.boundary[i]) - ? boundary[i] : r.boundary[i]; - boundary[i+RTREE_DIMENSION] = - (boundary[i+RTREE_DIMENSION] >= r.boundary[i+RTREE_DIMENSION]) - ? boundary[i+RTREE_DIMENSION] : r.boundary[i+RTREE_DIMENSION]; - } - } - rectangle_t operator + (rectangle_t const& r) const { - rectangle_t res; - int i = RTREE_DIMENSION; - while (--i >= 0) { - res.boundary[i] = (boundary[i] <= r.boundary[i]) - ? boundary[i] : r.boundary[i]; - res.boundary[i+RTREE_DIMENSION] = - (boundary[i+RTREE_DIMENSION] >= r.boundary[i+RTREE_DIMENSION]) - ? boundary[i+RTREE_DIMENSION] : r.boundary[i+RTREE_DIMENSION]; - } - return res; - } - bool operator& (rectangle_t const& r) const { - int i = RTREE_DIMENSION; - while (--i >= 0) { - if (boundary[i] > r.boundary[i+RTREE_DIMENSION] || - r.boundary[i] > boundary[i+RTREE_DIMENSION]) - { - return false; - } - } - return true; - } - bool operator <= (rectangle_t const& r) const { - int i = RTREE_DIMENSION; - while (--i >= 0) { - if (boundary[i] < r.boundary[i] || - boundary[i+RTREE_DIMENSION] > r.boundary[i+RTREE_DIMENSION]) - { - return false; - } - } - return true; - } - bool operator < (rectangle_t const& r) const { - return *this <= r && *this != r; - } - - bool operator >= (rectangle_t const& r) const { - return r <= *this; - } - bool operator > (rectangle_t const& r) const { - return r <= *this && *this != r; - } - - bool operator == (rectangle_t const& r) const { - int i = RTREE_DIMENSION*2; - while (--i >= 0) { - if (boundary[i] != r.boundary[i]) { - return false; - } - } - return true; - } - bool operator != (rectangle_t const& r) const { - return !(*this == r); - } - bool operator_true(rectangle_t const&) const { - return true; - } -}; - -enum Spatial_search_op -{ - SOP_ALL, - SOP_EQUALS, - SOP_CONTAINS, - SOP_STRICT_CONTAINS, - SOP_OVERLAPS, - SOP_BELONGS, - SOP_STRICT_BELONGS, - SOP_NEIGHBOR -}; - -class R_tree_iterator -{ - friend class R_tree; - struct { - R_page* page; - int pos; - } stack[MAX_HEIGHT]; - - struct Neighbor { - void* child; - Neighbor* next; - int level; - area_t distance; - }; - - enum { - N_ELEMS = (RTREE_PAGE_SIZE-sizeof(Neighbor*))/ sizeof(Neighbor) - }; - - struct Neighbor_page { - Neighbor_page* next; - Neighbor buf[N_ELEMS]; - }; - - Neighbor* allocate_neighbour(); - - - typedef bool (rectangle_t::*comparator_t)(rectangle_t const& r) const; - - rectangle_t r; - Spatial_search_op op; - R_tree* tree; - Neighbor* list; - Neighbor* free; - bool eof; - int update_count; - Neighbor_page* page_list; - int page_pos; - - comparator_t intr_cmp; - comparator_t leaf_cmp; - - bool goto_first(int sp, R_page* pg); - bool goto_next(int sp); - bool init(R_tree const* tree, rectangle_t const& r, Spatial_search_op op); - void insert(Neighbor* node); - - Neighbor* new_neighbor(void* child, area_t distance, int level); - void free_neighbor(Neighbor* n); -public: - void reset(); - record_t next(); - - R_tree_iterator(); - ~R_tree_iterator(); -}; - -class R_tree -{ - friend class R_tree_iterator; - friend class R_page; - - typedef void* (*page_alloc_t)(); - typedef void (*page_free_t)(void*); - -public: - size_t used_size() const { - return n_pages * RTREE_PAGE_SIZE; - } - - unsigned number_of_records() const { - return n_records; - } - bool search(rectangle_t const& r, Spatial_search_op op, R_tree_iterator& iterator) const; - void insert(rectangle_t const& r, record_t obj); - bool remove(rectangle_t const& r, record_t obj); - void purge(); - R_tree(page_alloc_t page_alloc, page_free_t page_free); - ~R_tree(); - -protected: - unsigned n_records; - unsigned height; - R_page* root; - int update_count; - int n_pages; - page_alloc_t page_alloc; - page_free_t page_free; -}; - -#endif - - - -- GitLab