diff --git a/src/box/alter.cc b/src/box/alter.cc index 3a609dfcdb5462066dc91dca58aaf4344c8094a0..031f75fa23e9f4406a2e2f4f8f1ab49cef76e6f8 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -219,6 +219,8 @@ key_def_new_from_tuple(struct tuple *tuple) key_def_set_part(key_def, i, fieldno, field_type); } } + if (type == RTREE && key_def->opts.dimension == 0) + key_def->opts.dimension = 2; key_def_check(key_def); scoped_guard.is_active = false; return key_def; diff --git a/src/box/errcode.h b/src/box/errcode.h index 142e931a2902a5beff20eb864fb53a7f6e8f56ac..2f87bf72958288554b863f9530cab7c1b23330ef 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -152,6 +152,7 @@ struct errcode_record { /* 98 */_(ER_UNSUPPORTED_ROLE_PRIV, 2, "Unsupported role privilege '%s'") \ /* 99 */_(ER_LOAD_FUNCTION, 2, "Failed to dynamically load function '%s': %s") \ /*100 */_(ER_FUNCTION_LANGUAGE, 2, "Unsupported language '%s' specified for function '%s'") \ + /*101 */_(ER_RTREE_RECT_ERROR, 2, "RTree: %s must be an array with %u (point) or %u (rectangle/box) numeric coordinates") \ /* * !IMPORTANT! Please follow instructions at start of the file diff --git a/src/box/index.cc b/src/box/index.cc index fbe5afcd69420ba3241352975aa93da9bbd151e7..147329a26aaf2376b37c03d14c3a164d60a4dd86 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -69,41 +69,42 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key, /* Fall through. */ } - if (key_def->type == RTREE) { - if (part_count != 1 && part_count != 2 && part_count != 4) - tnt_raise(ClientError, ER_KEY_PART_COUNT, 4, part_count); - if (part_count == 1) { + if (key_def->type == RTREE) { + unsigned d = key_def->opts.dimension; + if (part_count != 1 && part_count != d && part_count != d * 2) + tnt_raise(ClientError, ER_KEY_PART_COUNT, + d * 2, part_count); + if (part_count == 1) { enum mp_type mp_type = mp_typeof(*key); key_mp_type_validate(ARRAY, mp_type, ER_KEY_PART_TYPE, 0); - uint32_t arr_size = mp_decode_array(&key); - if (arr_size != 2 && arr_size != 4) - tnt_raise(ClientError, ER_UNSUPPORTED, - "R-Tree key", "Key should contain 2 (point) " - "or 4 (rectangle) numeric coordinates"); - for (uint32_t part = 0; part < arr_size; part++) { + uint32_t array_size = mp_decode_array(&key); + if (array_size != d && array_size != d * 2) + tnt_raise(ClientError, ER_RTREE_RECT_ERROR, + "Key", d, d * 2); + for (uint32_t part = 0; part < array_size; part++) { enum mp_type mp_type = mp_typeof(*key); mp_next(&key); key_mp_type_validate(NUMBER, mp_type, ER_KEY_PART_TYPE, 0); } - } else { + } else { for (uint32_t part = 0; part < part_count; part++) { enum mp_type mp_type = mp_typeof(*key); mp_next(&key); key_mp_type_validate(NUMBER, mp_type, ER_KEY_PART_TYPE, part); } - } - } else { - if (part_count > key_def->part_count) - tnt_raise(ClientError, ER_KEY_PART_COUNT, - key_def->part_count, part_count); - - /* Partial keys are allowed only for TREE index type. */ - if (key_def->type != TREE && part_count < key_def->part_count) { - tnt_raise(ClientError, ER_EXACT_MATCH, - key_def->part_count, part_count); - } - key_validate_parts(key_def, key, part_count); - } + } + } else { + if (part_count > key_def->part_count) + tnt_raise(ClientError, ER_KEY_PART_COUNT, + key_def->part_count, part_count); + + /* Partial keys are allowed only for TREE index type. */ + if (key_def->type != TREE && part_count < key_def->part_count) { + tnt_raise(ClientError, ER_EXACT_MATCH, + key_def->part_count, part_count); + } + key_validate_parts(key_def, key, part_count); + } } void diff --git a/src/box/memtx_rtree.cc b/src/box/memtx_rtree.cc index d0eec301d3866f05c59ecd4b41119ab1d451e1b0..e854b877a1ee6a157985fa0fd0d005d7f47a63fc 100644 --- a/src/box/memtx_rtree.cc +++ b/src/box/memtx_rtree.cc @@ -36,60 +36,51 @@ /* {{{ Utilities. *************************************************/ -inline void extract_rectangle(struct rtree_rect *rect, - const struct tuple *tuple, struct key_def *kd) +inline int +mp_decode_rect(struct rtree_rect *rect, unsigned dimension, + const char *mp, unsigned count) { - assert(kd->part_count == 1); - const char *elems = tuple_field(tuple, kd->parts[0].fieldno); - uint32_t size = mp_decode_array(&elems); - switch (size) { - case 1: // array - { - const char* elems = tuple_field(tuple, kd->parts[0].fieldno); - uint32_t size = mp_decode_array(&elems); - switch (size) { - case 2: // point - 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: - 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", "Field should be array with " - "size 2 (point) or 4 (rectangle)"); - + if (count == dimension) { /* point */ + for (unsigned i = 0; i < dimension; i++) { + coord_t c = mp_decode_num(&mp, i); + rect->coords[i * 2] = c; + rect->coords[i * 2 + 1] = c; } - break; + } else if (count == dimension * 2) { /* box */ + for (unsigned i = 0; i < dimension; i++) { + coord_t c = mp_decode_num(&mp, i); + rect->coords[i * 2] = c; + } + for (unsigned i = 0; i < dimension; i++) { + coord_t c = mp_decode_num(&mp, i + dimension); + rect->coords[i * 2 + 1] = c; + } + } else { + return -1; } - case 2: // point - 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: - 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, dimension); + return 0; +} +inline int +mp_decode_rect(struct rtree_rect *rect, unsigned dimension, + const char *mp) +{ + uint32_t size = mp_decode_array(&mp); + return mp_decode_rect(rect, dimension, mp, size); +} + +inline void +extract_rectangle(struct rtree_rect *rect, const struct tuple *tuple, + struct key_def *key_def) +{ + assert(key_def->part_count == 1); + const char *elems = tuple_field(tuple, key_def->parts[0].fieldno); + unsigned dimension = key_def->opts.dimension; + if (mp_decode_rect(rect, dimension, elems)) { + tnt_raise(ClientError, ER_RTREE_RECT_ERROR, + "Field", dimension, dimension * 2); } - rtree_rect_normalize(rect); } /* {{{ MemtxRTree Iterators ****************************************/ @@ -103,7 +94,7 @@ index_rtree_iterator_free(struct iterator *i) { struct index_rtree_iterator *itr = (struct index_rtree_iterator *)i; rtree_iterator_destroy(&itr->impl); - delete itr; + delete itr; } static struct tuple * @@ -124,102 +115,74 @@ MemtxRTree::~MemtxRTree() index_rtree_iterator_free(m_position); m_position = NULL; } - rtree_destroy(&tree); + rtree_destroy(&m_tree); } MemtxRTree::MemtxRTree(struct key_def *key_def) - : Index(key_def) + : Index(key_def) { assert(key_def->part_count == 1); assert(key_def->parts[0].type = ARRAY); assert(key_def->opts.is_unique == false); + m_dimension = key_def->opts.dimension; + if (m_dimension < 1 || m_dimension > RTREE_MAX_DIMENSION) { + char message[64]; + snprintf(message, 64, "dimension (%u) must belong to range " + "[%u, %u]", m_dimension, 1, RTREE_MAX_DIMENSION); + tnt_raise(ClientError, ER_UNSUPPORTED, + "RTREE index", message); + } + memtx_index_arena_init(); - rtree_init(&tree, MEMTX_EXTENT_SIZE, + rtree_init(&m_tree, m_dimension, MEMTX_EXTENT_SIZE, memtx_index_extent_alloc, memtx_index_extent_free); } size_t MemtxRTree::size() const { - return rtree_number_of_records(&tree); + return rtree_number_of_records(&m_tree); } size_t MemtxRTree::bsize() const { - return rtree_used_size(&tree); + return rtree_used_size(&m_tree); } struct tuple * MemtxRTree::findByKey(const char *key, uint32_t part_count) const { + struct rtree_iterator iterator; + rtree_iterator_init(&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: - 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: - 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: - assert(false); - } - break; - } - case 2: - 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: - 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: + if (mp_decode_rect(&rect, m_dimension, key, part_count)) assert(false); - } - struct tuple *result = NULL; - if (rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) + + struct tuple *result = NULL; + if (rtree_search(&m_tree, &rect, SOP_OVERLAPS, &iterator)) result = (struct tuple *)rtree_iterator_next(&iterator); - rtree_iterator_destroy(&iterator); - return result; + rtree_iterator_destroy(&iterator); + return result; } struct tuple * MemtxRTree::replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode) + enum dup_replace_mode) { - struct rtree_rect rect; - if (new_tuple) { - extract_rectangle(&rect, new_tuple, key_def); - rtree_insert(&tree, &rect, new_tuple); - } + struct rtree_rect rect; + if (new_tuple) { + extract_rectangle(&rect, new_tuple, key_def); + rtree_insert(&m_tree, &rect, new_tuple); + } if (old_tuple) { - extract_rectangle(&rect, old_tuple, key_def); - if (!rtree_remove(&tree, &rect, old_tuple)) - old_tuple = NULL; - } - return old_tuple; + extract_rectangle(&rect, old_tuple, key_def); + if (!rtree_remove(&m_tree, &rect, old_tuple)) + old_tuple = NULL; + } + return old_tuple; } struct iterator * @@ -240,98 +203,59 @@ MemtxRTree::allocIterator() const void MemtxRTree::initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, uint32_t part_count) const + const char *key, uint32_t part_count) const { - struct rtree_rect rect; - index_rtree_iterator *it = (index_rtree_iterator *)iterator; - switch (part_count) { - case 0: - if (type != ITER_ALL) { - tnt_raise(ClientError, ER_UNSUPPORTED, - "R-Tree index", "It is possible to omit key only for ITER_ALL"); - } - break; - case 1: - { - uint32_t size = mp_decode_array(&key); - switch (size) { - case 2: - 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: - 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: + index_rtree_iterator *it = (index_rtree_iterator *)iterator; + + struct rtree_rect rect; + if (part_count == 0) { + if (type != ITER_ALL) { tnt_raise(ClientError, ER_UNSUPPORTED, - "R-Tree index", "Key should be array of 2 (point) " - "or 4 (rectangle) numeric coordinates"); + "R-Tree index", + "It is possible to omit " + "key only for ITER_ALL"); } - break; + } else if (mp_decode_rect(&rect, m_dimension, key, part_count)) { + tnt_raise(ClientError, ER_RTREE_RECT_ERROR, + "Key", m_dimension, m_dimension * 2); } - case 2: - 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); + + spatial_search_op op; + switch (type) { + case ITER_ALL: + op = SOP_ALL; + break; + case ITER_EQ: + op = SOP_EQUALS; + break; + case ITER_GT: + op = SOP_STRICT_CONTAINS; + break; + case ITER_GE: + op = SOP_CONTAINS; break; - case 4: - 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); + case ITER_LT: + op = SOP_STRICT_BELONGS; + break; + case ITER_LE: + op = SOP_BELONGS; + break; + case ITER_OVERLAPS: + op = SOP_OVERLAPS; + break; + case ITER_NEIGHBOR: + op = SOP_NEIGHBOR; break; default: tnt_raise(ClientError, ER_UNSUPPORTED, - "R-Tree index", "Key contain 2 (point) " - "or 4 (rectangle) numeric coordinates"); + "RTREE index", "Unsupported search operation for RTREE"); } - spatial_search_op op; - switch (type) { - case ITER_ALL: - op = SOP_ALL; - break; - case ITER_EQ: - op = SOP_EQUALS; - break; - case ITER_GT: - op = SOP_STRICT_CONTAINS; - break; - case ITER_GE: - op = SOP_CONTAINS; - break; - case ITER_LT: - op = SOP_STRICT_BELONGS; - break; - case ITER_LE: - op = SOP_BELONGS; - break; - case ITER_OVERLAPS: - op = SOP_OVERLAPS; - break; - case ITER_NEIGHBOR: - op = SOP_NEIGHBOR; - break; - default: - tnt_raise(ClientError, ER_UNSUPPORTED, - "R-Tree index", "Unsupported search operation for R-Tree"); - } - rtree_search(&tree, &rect, op, &it->impl); + rtree_search(&m_tree, &rect, op, &it->impl); } void MemtxRTree::beginBuild() { - rtree_purge(&tree); + rtree_purge(&m_tree); } - diff --git a/src/box/memtx_rtree.h b/src/box/memtx_rtree.h index f4aa0ca614827efc0f2b9f4104d1a1397db1a62d..ef2b91b59f75fb365fd4ff06449ea4cb75f39739 100644 --- a/src/box/memtx_rtree.h +++ b/src/box/memtx_rtree.h @@ -53,7 +53,8 @@ class MemtxRTree: public Index const char *key, uint32_t part_count) const; protected: - struct rtree tree; + unsigned m_dimension; + struct rtree m_tree; }; #endif /* TARANTOOL_BOX_MEMTX_RTREE_H_INCLUDED */ diff --git a/src/lib/salad/rtree.c b/src/lib/salad/rtree.c index 70a43d410c1eeff74ae6bed723f595f3a50eb3d0..11da353dc651894ef12a6111e909604cb6a4cc18 100644 --- a/src/lib/salad/rtree.c +++ b/src/lib/salad/rtree.c @@ -29,49 +29,42 @@ #include "rtree.h" #include <string.h> #include <assert.h> +#include <limits.h> +#include <sys/types.h> /*------------------------------------------------------------------------- */ /* R-tree internal structures definition */ /*------------------------------------------------------------------------- */ +enum { + /* rtree will try to determine optimal page size */ + RTREE_OPTIMAL_BRANCHES_IN_PAGE = 18, + /* actual number of branches could be up to double of the previous + * constant */ + RTREE_MAXIMUM_BRANCHES_IN_PAGE = RTREE_OPTIMAL_BRANCHES_IN_PAGE * 2 +}; struct rtree_page_branch { - struct rtree_rect rect; union { struct rtree_page *page; record_t record; } data; + struct rtree_rect rect; }; 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 + RTREE_BRANCH_DATA_SIZE = sizeof(((struct rtree_page_branch *)0)->data) }; 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; - sq_coord_t distance; -}; - -enum { - RTREE_NEIGHBORS_IN_PAGE = (RTREE_PAGE_SIZE - sizeof(void*)) / - sizeof(struct rtree_neighbor) + char data[]; }; struct rtree_neighbor_page { struct rtree_neighbor_page* next; - struct rtree_neighbor buf[RTREE_NEIGHBORS_IN_PAGE]; + struct rtree_neighbor buf[]; }; struct rtree_reinsert_list { @@ -79,46 +72,77 @@ struct rtree_reinsert_list { int level; }; +static int +neighbor_cmp(struct rtree_neighbor *a, struct rtree_neighbor *b) +{ + return a->distance < b->distance ? -1 : + a->distance > b->distance ? 1 : + a->level < b->level ? -1 : + a->level > b->level ? 1 : + a < b ? -1 : a > b ? 1 : 0; + return 0; +} + +rb_gen(, rtnt_, rtnt_t, struct rtree_neighbor, link, neighbor_cmp); + /*------------------------------------------------------------------------- */ /* R-tree rectangle methods */ /*------------------------------------------------------------------------- */ void -rtree_rect_normalize(struct rtree_rect *rect) +rtree_rect_normalize(struct rtree_rect *rect, unsigned dimension) { - for (int i = RTREE_DIMENSION; --i >= 0; ) { - if (rect->lower_point.coords[i] <= rect->upper_point.coords[i]) + for (int i = dimension; --i >= 0; ) { + coord_t *coords = &rect->coords[2 * i]; + if (coords[0] <= coords[1]) 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; + coord_t tmp = coords[0]; + coords[0] = coords[1]; + coords[1] = tmp; } } +static void +rtree_rect_copy(struct rtree_rect *to, const struct rtree_rect *from, + unsigned dimension) +{ + for (int i = dimension * 2; --i >= 0; ) + to->coords[i] = from->coords[i]; +} + 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; + rect->coords[0] = left; + rect->coords[1] = right; + rect->coords[2] = bottom; + rect->coords[3] = top; +} + +void +rtree_set2dp(struct rtree_rect *rect, coord_t x, coord_t y) +{ + rect->coords[0] = x; + rect->coords[1] = x; + rect->coords[2] = y; + rect->coords[3] = y; } static sq_coord_t -rtree_rect_point_distance2(const struct rtree_rect *rect, - const struct rtree_point *point) +rtree_rect_neigh_distance2(const struct rtree_rect *rect, + const struct rtree_rect *neigh_rect, + unsigned dimension) { sq_coord_t result = 0; - for (int i = RTREE_DIMENSION; --i >= 0; ) { - if (point->coords[i] < rect->lower_point.coords[i]) { - sq_coord_t diff = (sq_coord_t)(point->coords[i] - - rect->lower_point.coords[i]); + for (int i = dimension; --i >= 0; ) { + const coord_t *coords = &rect->coords[2 * i]; + coord_t neigh_coord = neigh_rect->coords[2 * i]; + if (neigh_coord < coords[0]) { + sq_coord_t diff = (sq_coord_t)(neigh_coord - coords[0]); result += diff * diff; - } else if (point->coords[i] > rect->upper_point.coords[i]) { - sq_coord_t diff = (sq_coord_t)(point->coords[i] - - rect->upper_point.coords[i]); + } else if (neigh_coord > coords[1]) { + sq_coord_t diff = (sq_coord_t)(neigh_coord - coords[1]); result += diff * diff; } } @@ -126,24 +150,38 @@ rtree_rect_point_distance2(const struct rtree_rect *rect, } static area_t -rtree_rect_area(const struct rtree_rect *rect) +rtree_rect_area(const struct rtree_rect *rect, unsigned dimension) { area_t area = 1; - for (int i = RTREE_DIMENSION; --i >= 0; ) { - area *= rect->upper_point.coords[i] - - rect->lower_point.coords[i]; + for (int i = dimension; --i >= 0; ) { + const coord_t *coords = &rect->coords[2 * i]; + area *= coords[1] - coords[0]; } return area; } +static coord_t +rtree_rect_half_margin(const struct rtree_rect *rect, unsigned dimension) +{ + coord_t hm = 0; + for (int i = dimension; --i >= 0; ) { + const coord_t *coords = &rect->coords[2 * i]; + hm += coords[1] - coords[0]; + } + return hm; +} + static void -rtree_rect_add(struct rtree_rect *to, const struct rtree_rect *item) +rtree_rect_add(struct rtree_rect *to, const struct rtree_rect *item, + unsigned dimension) { - 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]; + for (int i = dimension; --i >= 0; ) { + coord_t *to_coords = &to->coords[2 * i]; + const coord_t *item_coords = &item->coords[2 * i]; + if (to_coords[0] > item_coords[0]) + to_coords[0] = item_coords[0]; + if (to_coords[1] < item_coords[1]) + to_coords[1] = item_coords[1]; } } @@ -159,86 +197,117 @@ rtree_max(coord_t a, coord_t b) return a > b ? a : b; } -static struct rtree_rect +static void rtree_rect_cover(const struct rtree_rect *item1, - const struct rtree_rect *item2) + const struct rtree_rect *item2, + struct rtree_rect *result, + unsigned dimension) { - 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]); + for (int i = dimension; --i >= 0; ) { + const coord_t *i1_coords = &item1->coords[2 * i]; + const coord_t *i2_coords = &item2->coords[2 * i]; + coord_t *r_coords = &result->coords[2 * i]; + r_coords[0] = rtree_min(i1_coords[0], i2_coords[0]); + r_coords[1] = rtree_max(i1_coords[1], i2_coords[1]); + } +} + +static void +rtree_rect_intersection(const struct rtree_rect *item1, + const struct rtree_rect *item2, + struct rtree_rect *result, + unsigned dimension) +{ + for (int i = dimension; --i >= 0; ) { + const coord_t *i1_coords = &item1->coords[2 * i]; + const coord_t *i2_coords = &item2->coords[2 * i]; + coord_t *r_coords = &result->coords[2 * i]; + if (i1_coords[0] > i2_coords[1] || i1_coords[1] < i2_coords[0]) + r_coords[0] = r_coords[1] = 0; + else { + r_coords[0] = rtree_max(i1_coords[0], i2_coords[0]); + r_coords[1] = rtree_min(i1_coords[1], i2_coords[1]); + } } - return res; } static bool rtree_rect_intersects_rect(const struct rtree_rect *rt1, - const struct rtree_rect *rt2) + const struct rtree_rect *rt2, + unsigned dimension) { - 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]) + for (int i = dimension; --i >= 0; ) { + const coord_t *coords1 = &rt1->coords[2 * i]; + const coord_t *coords2 = &rt2->coords[2 * i]; + if (coords1[0] > coords2[1] || coords1[1] < coords2[0]) return false; + } return true; } static bool rtree_rect_in_rect(const struct rtree_rect *rt1, - const struct rtree_rect *rt2) + const struct rtree_rect *rt2, + unsigned dimension) { - 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]) + for (int i = dimension; --i >= 0; ) { + const coord_t *coords1 = &rt1->coords[2 * i]; + const coord_t *coords2 = &rt2->coords[2 * i]; + if (coords1[0] < coords2[0] || coords1[1] > coords2[1]) return false; + } return true; } static bool rtree_rect_strict_in_rect(const struct rtree_rect *rt1, - const struct rtree_rect *rt2) + const struct rtree_rect *rt2, + unsigned dimension) { - 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]) + for (int i = dimension; --i >= 0; ) { + const coord_t *coords1 = &rt1->coords[2 * i]; + const coord_t *coords2 = &rt2->coords[2 * i]; + if (coords1[0] <= coords2[0] || coords1[1] >= coords2[1]) return false; + } return true; } static bool rtree_rect_holds_rect(const struct rtree_rect *rt1, - const struct rtree_rect *rt2) + const struct rtree_rect *rt2, + unsigned dimension) { - return rtree_rect_in_rect(rt2, rt1); + return rtree_rect_in_rect(rt2, rt1, dimension); } static bool rtree_rect_strict_holds_rect(const struct rtree_rect *rt1, - const struct rtree_rect *rt2) + const struct rtree_rect *rt2, + unsigned dimension) { - return rtree_rect_strict_in_rect(rt2, rt1); + return rtree_rect_strict_in_rect(rt2, rt1, dimension); } static bool rtree_rect_equal_to_rect(const struct rtree_rect *rt1, - const struct rtree_rect *rt2) + const struct rtree_rect *rt2, + unsigned dimension) { - 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]) + for (int i = dimension * 2; --i >= 0; ) + if (rt1->coords[i] != rt2->coords[i]) return false; return true; } static bool rtree_always_true(const struct rtree_rect *rt1, - const struct rtree_rect *rt2) + const struct rtree_rect *rt2, + unsigned dimension) { (void)rt1; (void)rt2; + (void)dimension; return true; } @@ -268,199 +337,234 @@ rtree_free_page(struct rtree *tree, struct rtree_page *page) tree->free_pages = (void *)page; } +static struct rtree_page_branch * +rtree_get_branch(const struct rtree *tree, const struct rtree_page *page, + unsigned ind) +{ + return (struct rtree_page_branch *) + (page->data + ind * tree->page_branch_size); +} + static void -set_next_reinsert_page(struct rtree_page *page, struct rtree_page *next_page) +rtree_branch_copy(struct rtree_page_branch *to, + const struct rtree_page_branch *from, unsigned dimension) +{ + to->data = from->data; + rtree_rect_copy(&to->rect, &from->rect, dimension); +} + + +static void +set_next_reinsert_page(const struct rtree *tree, 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_branch *b = rtree_get_branch(tree, page, + tree->page_max_fill - 1); + b->data.page = next_page; } struct rtree_page * -get_next_reinsert_page(const struct rtree_page *page) +get_next_reinsert_page(const struct rtree *tree, const struct rtree_page *page) { - return page->b[RTREE_MAX_FILL - 1].data.page; + struct rtree_page_branch *b = rtree_get_branch(tree, page, + tree->page_max_fill - 1); + return b->data.page; } /* Calculate cover of all rectangles at page */ -static struct rtree_rect -rtree_page_cover(const struct rtree_page *page) +static void +rtree_page_cover(const struct rtree *tree, const struct rtree_page *page, + struct rtree_rect *res) { - 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; + rtree_rect_copy(res, &rtree_get_branch(tree, page, 0)->rect, + tree->dimension); + for (int i = 1; i < page->n; i++) { + rtree_rect_add(res, &rtree_get_branch(tree, page, i)->rect, + tree->dimension); + } } /* Create root page by first inserting record */ static void -rtree_page_init_with_record(struct rtree_page *page, +rtree_page_init_with_record(const struct rtree *tree, struct rtree_page *page, struct rtree_rect *rect, record_t obj) { + struct rtree_page_branch *b = rtree_get_branch(tree, page, 0); page->n = 1; - page->b[0].rect = *rect; - page->b[0].data.record = obj; -} - -/* Create root page by branch */ -static void -rtree_page_init_with_branch(struct rtree_page *page, - const struct rtree_page_branch *br) -{ - page->n = 1; - page->b[0] = *br; + rtree_rect_copy(&b->rect, rect, tree->dimension); + b->data.record = obj; } /* Create new root page (root splitting) */ static void -rtree_page_init_with_pages(struct rtree_page *page, - struct rtree_page *page1, - struct rtree_page *page2) +rtree_page_init_with_pages(const struct rtree *tree, 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; + struct rtree_page_branch *b = rtree_get_branch(tree, page, 0); + rtree_page_cover(tree, page1, &b->rect); + b->data.page = page1; + b = rtree_get_branch(tree, page, 1); + rtree_page_cover(tree, page2, &b->rect); + b->data.page = page2; } static struct rtree_page * rtree_split_page(struct rtree *tree, struct rtree_page *page, const struct rtree_page_branch *br) { - area_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 = 0; - bool worst_waste_set = false; - - 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 (!worst_waste_set) { - worst_waste_set = true; - worst_waste = waste; - seed[0] = i; - seed[1] = j; - }else if (waste > worst_waste) { - worst_waste = waste; - seed[0] = i; - seed[1] = j; - } - } - bp = page->b + i; + assert(page->n == tree-> page_max_fill); + const struct rtree_rect *rects[RTREE_MAXIMUM_BRANCHES_IN_PAGE + 1]; + unsigned ids[RTREE_MAXIMUM_BRANCHES_IN_PAGE + 1]; + rects[0] = &br->rect; + ids[0] = 0; + for (unsigned i = 0; i < page->n; i++) { + struct rtree_page_branch *b = rtree_get_branch(tree, page, i); + rects[i + 1] = &b->rect; + ids[i + 1] = i + 1; } - assert(seed[0] >= 0); - - char taken[RTREE_MAX_FILL]; - memset(taken, 0, sizeof(taken)); - struct rtree_rect group_rect[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_with_branch(p, br); - } else { - group_rect[0] = page->b[seed[0] - 1].rect; - rtree_page_init_with_branch(p, &page->b[seed[0] - 1]); - page->b[seed[0] - 1] = *br; - } - area_t group_area[2] = {rect_area[seed[0]], rect_area[seed[1]]}; - int group_card[2] = {1, 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; - area_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); - area_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; + const unsigned n = page->n + 1; + const unsigned k_max = n - 2 * tree->page_min_fill; + unsigned d = tree->dimension; + unsigned best_axis = 0; + coord_t best_s = 0; + for (unsigned a = 0; a < d; a++) { + for (unsigned i = 0; i < n - 1; i++) { + unsigned min_i = i; + coord_t min_l = rects[ids[i]]->coords[2 * a]; + coord_t min_r = rects[ids[i]]->coords[2 * a + 1]; + for (unsigned j = i + 1; j < n; j++) { + coord_t l = rects[ids[j]]->coords[2 * a]; + coord_t r = rects[ids[j]]->coords[2 * a + 1]; + if (l < min_l || (l == min_l && r < min_r)) { + min_i = j; + min_l = l; + min_r = r; } } + unsigned tmp = ids[i]; + ids[i] = ids[min_i]; + ids[min_i] = tmp; } - 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]; + struct rtree_rect test_rect; + coord_t dir_hm[RTREE_MAXIMUM_BRANCHES_IN_PAGE + 1]; + coord_t rev_hm[RTREE_MAXIMUM_BRANCHES_IN_PAGE + 1]; + dir_hm[0] = 0; + rtree_rect_copy(&test_rect, rects[ids[0]], d); + dir_hm[1] = rtree_rect_half_margin(&test_rect, d); + for (unsigned i = 1; i < n - tree->page_min_fill; i++) { + rtree_rect_add(&test_rect, rects[ids[i]], d); + dir_hm[i + 1] = rtree_rect_half_margin(&test_rect, d); + } + rev_hm[0] = 0; + rtree_rect_copy(&test_rect, rects[ids[n - 1]], d); + rev_hm[1] = rtree_rect_half_margin(&test_rect, d); + for (unsigned i = 1; i < n - tree->page_min_fill; i++) { + rtree_rect_add(&test_rect, rects[ids[n - i - 1]], d); + rev_hm[i + 1] = rtree_rect_half_margin(&test_rect, d); + } + coord_t s = 0; + for (unsigned k = 0; k < k_max; k++) { + unsigned k1 = tree->page_min_fill + k; + unsigned k2 = n - k1; + s += dir_hm[k1] + rev_hm[k2]; + } + if (a == 0 || s < best_s) { + best_axis = a; + best_s = s; + } + } + unsigned a = best_axis; + for (unsigned i = 0; i < n - 1; i++) { + unsigned min_i = i; + coord_t min_l = rects[ids[i]]->coords[2 * a]; + coord_t min_r = rects[ids[i]]->coords[2 * a + 1]; + for (unsigned j = i + 1; j < n; j++) { + coord_t l = rects[ids[j]]->coords[2 * a]; + coord_t r = rects[ids[j]]->coords[2 * a + 1]; + if (l < min_l || (l == min_l && r < min_r)) { + min_i = j; + min_l = l; + min_r = r; } } + unsigned tmp = ids[i]; + ids[i] = ids[min_i]; + ids[min_i] = tmp; + } + area_t min_overlap = 0; + area_t min_area = 0; + unsigned min_k = 0; + for (unsigned k = 0; k < k_max; k++) { + unsigned k1 = tree->page_min_fill + k; + /* unsigned k2 = n - k1; */ + struct rtree_rect rt1, rt2, over_rt; + rtree_rect_copy(&rt1, rects[ids[0]], d); + for (int i = 1; i < k1; i++) { + rtree_rect_add(&rt1, rects[ids[i]], d); + } + rtree_rect_copy(&rt2, rects[ids[k1]], d); + for (int i = k1 + 1; i < n; i++) { + rtree_rect_add(&rt2, rects[ids[i]], d); + } + rtree_rect_intersection(&rt1, &rt2, &over_rt, d); + area_t overlap = rtree_rect_area(&over_rt, d); + area_t area = rtree_rect_area(&rt1, d) + + rtree_rect_area(&rt2, d); + if (k == 0 || overlap < min_overlap || + (overlap == min_overlap && area < min_area)) { + min_k = k; + min_overlap = overlap; + min_area = area; + } + } + unsigned k = min_k; + unsigned k1 = tree->page_min_fill + k; + unsigned k2 = n - k1; + struct rtree_page *new_page = rtree_alloc_page(tree); + tree->n_pages++; + char taken[RTREE_MAXIMUM_BRANCHES_IN_PAGE]; + memset(taken, 0, sizeof(taken)); + for (unsigned i = 0; i < k1; i++) { + struct rtree_page_branch *new_b = + rtree_get_branch(tree, new_page, i); + const struct rtree_page_branch *from_b = br; + if (ids[i]) { + from_b = rtree_get_branch(tree, page, ids[i] - 1); + taken[ids[i] - 1] = 1; + } + rtree_branch_copy(new_b, from_b, d); + } + unsigned moved = 0; + for (unsigned i = 0, j = 0; j < page->n; j++) { + if (taken[j] == 0) { + struct rtree_page_branch *to, *from; + to = rtree_get_branch(tree, page, i++); + from = rtree_get_branch(tree, page, j); + rtree_branch_copy(to, from, tree->dimension); + moved++; + } } - 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]; + assert(moved == k2 || moved + 1 == k2); + if (moved + 1 == k2) { + struct rtree_page_branch *to; + to = rtree_get_branch(tree, page, moved); + rtree_branch_copy(to, br, tree->dimension); } - return p; + new_page->n = k1; + page->n = k2; + return new_page; } 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; + if (page->n < tree->page_max_fill) { + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, page->n++); + rtree_branch_copy(b, br, tree->dimension); return NULL; } else { return rtree_split_page(tree, page, br); @@ -468,11 +572,15 @@ rtree_page_add_branch(struct rtree *tree, struct rtree_page *page, } static void -rtree_page_remove_branch(struct rtree_page *page, int i) +rtree_page_remove_branch(struct rtree *tree, 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)); + page->n--; + for (int j = i; j < page->n; j++) { + struct rtree_page_branch *to, *from; + to = rtree_get_branch(tree, page, j); + from = rtree_get_branch(tree, page, j + 1); + rtree_branch_copy(to, from, tree->dimension); + } } static struct rtree_page * @@ -481,14 +589,20 @@ rtree_page_insert(struct rtree *tree, struct rtree_page *page, { struct rtree_page_branch br; if (--level != 0) { - /* not a leaf page */ + /* not a leaf page, minize area increase */ int mini = -1; - area_t min_incr, best_area; + area_t min_incr = 0, best_area = 0; for (int i = 0; i < page->n; i++) { - area_t r_area = rtree_rect_area(&page->b[i].rect); - struct rtree_rect cover = - rtree_rect_cover(&page->b[i].rect, rect); - area_t incr = rtree_rect_area(&cover) - r_area; + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, i); + area_t r_area = rtree_rect_area(&b->rect, + tree->dimension); + struct rtree_rect cover; + rtree_rect_cover(&b->rect, rect, + &cover, tree->dimension); + area_t incr = rtree_rect_area(&cover, + tree->dimension); + incr -= r_area; assert(incr >= 0); if (i == 0) { best_area = r_area; @@ -498,29 +612,32 @@ rtree_page_insert(struct rtree *tree, struct rtree_page *page, best_area = r_area; min_incr = incr; mini = i; - } else if (incr == min_incr && r_area < best_area) { + } else if (incr == min_incr && + r_area < best_area) { best_area = r_area; mini = i; } } assert(mini >= 0); - struct rtree_page *p = page->b[mini].data.page; + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, mini); + struct rtree_page *p = b->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); + rtree_rect_add(&b->rect, rect, tree->dimension); return NULL; } else { /* child was split */ - page->b[mini].rect = rtree_page_cover(p); + rtree_page_cover(tree, p, &b->rect); br.data.page = q; - br.rect = rtree_page_cover(q); + rtree_page_cover(tree, q, &br.rect); return rtree_page_add_branch(tree, page, &br); } } else { br.data.record = obj; - br.rect = *rect; + rtree_rect_copy(&br.rect, rect, tree->dimension); return rtree_page_add_branch(tree, page, &br); } } @@ -530,30 +647,35 @@ 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) { + unsigned d = tree->dimension; if (--level != 0) { - for (int i = 0; i < page->n; i++) { - if (!rtree_rect_intersects_rect(&page->b[i].rect, rect)) + for (unsigned i = 0; i < page->n; i++) { + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, i); + if (!rtree_rect_intersects_rect(&b->rect, rect, d)) continue; - struct rtree_page *next_page = page->b[i].data.page; + struct rtree_page *next_page = b->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); + if (next_page->n >= tree->page_min_fill) { + rtree_page_cover(tree, next_page, &b->rect); } else { /* not enough entries in child */ - set_next_reinsert_page(next_page, rlist->chain); + set_next_reinsert_page(tree, next_page, + rlist->chain); rlist->chain = next_page; rlist->level = level - 1; - rtree_page_remove_branch(page, i); + rtree_page_remove_branch(tree, 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); + for (unsigned i = 0; i < page->n; i++) { + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, i); + if (b->data.page == obj) { + rtree_page_remove_branch(tree, page, i); return true; } } @@ -565,8 +687,11 @@ 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); + for (int i = 0; i < page->n; i++) { + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, i); + rtree_page_purge(tree, b->data.page, level); + } } rtree_free_page(tree, page); } @@ -576,11 +701,15 @@ rtree_page_purge(struct rtree *tree, struct rtree_page *page, int level) /*------------------------------------------------------------------------- */ static bool -rtree_iterator_goto_first(struct rtree_iterator *itr, int sp, struct rtree_page* pg) +rtree_iterator_goto_first(struct rtree_iterator *itr, int sp, + struct rtree_page* pg) { + unsigned d = itr->tree->dimension; 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)) { + struct rtree_page_branch *b; + b = rtree_get_branch(itr->tree, pg, i); + if (itr->leaf_cmp(&itr->rect, &b->rect, d)) { itr->stack[sp].page = pg; itr->stack[sp].pos = i; return true; @@ -588,9 +717,11 @@ rtree_iterator_goto_first(struct rtree_iterator *itr, int sp, struct rtree_page* } } else { for (int i = 0, n = pg->n; i < n; i++) { - if (itr->intr_cmp(&itr->rect, &pg->b[i].rect) + struct rtree_page_branch *b; + b = rtree_get_branch(itr->tree, pg, i); + if (itr->intr_cmp(&itr->rect, &b->rect, d) && rtree_iterator_goto_first(itr, sp + 1, - pg->b[i].data.page)) + b->data.page)) { itr->stack[sp].page = pg; itr->stack[sp].pos = i; @@ -605,19 +736,24 @@ rtree_iterator_goto_first(struct rtree_iterator *itr, int sp, struct rtree_page* static bool rtree_iterator_goto_next(struct rtree_iterator *itr, int sp) { + unsigned d = itr->tree->dimension; 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)) { + struct rtree_page_branch *b; + b = rtree_get_branch(itr->tree, pg, i); + if (itr->leaf_cmp(&itr->rect, &b->rect, d)) { 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) + struct rtree_page_branch *b; + b = rtree_get_branch(itr->tree, pg, i); + if (itr->intr_cmp(&itr->rect, &b->rect, d) && rtree_iterator_goto_first(itr, sp + 1, - pg->b[i].data.page)) + b->data.page)) { itr->stack[sp].page = pg; itr->stack[sp].pos = i; @@ -638,26 +774,30 @@ rtree_iterator_destroy(struct rtree_iterator *itr) (struct rtree_page *) curr); } itr->page_list = NULL; - itr->page_pos = RTREE_NEIGHBORS_IN_PAGE; + itr->page_pos = INT_MAX; +} + +struct rtree_neighbor * +rtree_iterator_reset_cb(rtnt_t *t, struct rtree_neighbor *n, void *d) +{ + (void) t; + struct rtree_iterator *itr = (struct rtree_iterator *)d; + n->next = itr->neigh_free_list; + itr->neigh_free_list = n; + return 0; } 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; - } + rtnt_iter(&itr->neigh_tree, 0, rtree_iterator_reset_cb, (void *)itr); + rtnt_new(&itr->neigh_tree); } static struct rtree_neighbor * rtree_iterator_allocate_neighbour(struct rtree_iterator *itr) { - if (itr->page_pos >= RTREE_NEIGHBORS_IN_PAGE) { + if (itr->page_pos >= itr->tree->neighbours_in_page) { struct rtree_neighbor_page *new_page = (struct rtree_neighbor_page *) rtree_alloc_page((struct rtree*)itr->tree); @@ -680,7 +820,6 @@ rtree_iterator_new_neighbor(struct rtree_iterator *itr, n->child = child; n->distance = distance; n->level = level; - n->next = NULL; return n; } @@ -696,29 +835,34 @@ void rtree_iterator_init(struct rtree_iterator *itr) { itr->tree = 0; - itr->neigh_list = NULL; + rtnt_new(&itr->neigh_tree); itr->neigh_free_list = NULL; itr->page_list = NULL; - itr->page_pos = RTREE_NEIGHBORS_IN_PAGE; + itr->page_pos = INT_MAX; } static void -rtree_iterator_insert_neighbor(struct rtree_iterator *itr, - struct rtree_neighbor *node) -{ - struct rtree_neighbor *prev = NULL, *next = itr->neigh_list; - sq_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; +rtree_iterator_process_neigh(struct rtree_iterator *itr, + struct rtree_neighbor *neighbor) +{ + unsigned d = itr->tree->dimension; + void *child = neighbor->child; + struct rtree_page *pg = (struct rtree_page *)child; + int level = neighbor->level; + rtree_iterator_free_neighbor(itr, neighbor); + for (int i = 0, n = pg->n; i < n; i++) { + struct rtree_page_branch *b; + b = rtree_get_branch(itr->tree, pg, i); + coord_t distance = + rtree_rect_neigh_distance2(&b->rect, &itr->rect, d); + struct rtree_neighbor *neigh = + rtree_iterator_new_neighbor(itr, b->data.page, + distance, level - 1); + rtnt_insert(&itr->neigh_tree, neigh); + } } + record_t rtree_iterator_next(struct rtree_iterator *itr) { @@ -742,32 +886,27 @@ rtree_iterator_next(struct rtree_iterator *itr) * page and insert them in sorted list */ while (true) { - struct rtree_neighbor *neighbor = itr->neigh_list; + struct rtree_neighbor *neighbor = + rtnt_first(&itr->neigh_tree); 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) + rtnt_remove(&itr->neigh_tree, neighbor); + if (neighbor->level == 0) { + void *child = neighbor->child; + rtree_iterator_free_neighbor(itr, neighbor); 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); + } else { + rtree_iterator_process_neigh(itr, neighbor); } } } 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; + if (!itr->eof && rtree_iterator_goto_next(itr, sp)) { + struct rtree_page_branch *b; + b = rtree_get_branch(itr->tree, + itr->stack[sp].page, itr->stack[sp].pos); + return b->data.record; + } itr->eof = true; return NULL; } @@ -776,8 +915,8 @@ rtree_iterator_next(struct rtree_iterator *itr) /* R-tree methods */ /*------------------------------------------------------------------------- */ -void -rtree_init(struct rtree *tree, uint32_t extent_size, +int +rtree_init(struct rtree *tree, unsigned dimension, uint32_t extent_size, rtree_extent_alloc_t extent_alloc, rtree_extent_free_t extent_free) { tree->n_records = 0; @@ -785,9 +924,27 @@ rtree_init(struct rtree *tree, uint32_t extent_size, tree->root = NULL; tree->version = 0; tree->n_pages = 0; - matras_create(&tree->mtab, extent_size, RTREE_PAGE_SIZE, - extent_alloc, extent_free); tree->free_pages = 0; + + tree->dimension = dimension; + tree->page_branch_size = + (RTREE_BRANCH_DATA_SIZE + dimension * 2 * sizeof(coord_t)); + tree->page_size = RTREE_OPTIMAL_BRANCHES_IN_PAGE * + tree->page_branch_size + sizeof(int); + /* round up to closest power of 2 */ + int lz = __builtin_clz(tree->page_size - 1); + tree->page_size = 1u << (sizeof(int) * CHAR_BIT - lz); + assert(tree->page_size - sizeof(int) >= + tree->page_branch_size * RTREE_OPTIMAL_BRANCHES_IN_PAGE); + tree->page_max_fill = (tree->page_size - sizeof(int)) / + tree->page_branch_size; + tree->page_min_fill = tree->page_max_fill * 2 / 5; + tree->neighbours_in_page = (tree->page_size - sizeof(void *)) + / sizeof(struct rtree_neighbor); + + matras_create(&tree->mtab, extent_size, tree->page_size, + extent_alloc, extent_free); + return 0; } void @@ -802,7 +959,7 @@ 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_with_record(tree->root, rect, obj); + rtree_page_init_with_record(tree, tree->root, rect, obj); tree->height = 1; tree->n_pages++; } else { @@ -811,7 +968,8 @@ rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj) if (p != NULL) { /* root splitted */ struct rtree_page *new_root = rtree_alloc_page(tree); - rtree_page_init_with_pages(new_root, tree->root, p); + rtree_page_init_with_pages(tree, new_root, + tree->root, p); tree->root = new_root; tree->height++; tree->n_pages++; @@ -821,7 +979,6 @@ rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj) tree->n_records++; } - bool rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj) { @@ -835,16 +992,17 @@ rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj) int level = rlist.level; while (pg != NULL) { for (int i = 0, n = pg->n; i < n; i++) { + struct rtree_page_branch *b; + b = rtree_get_branch(tree, pg, i); struct rtree_page *p = rtree_page_insert(tree, tree->root, - &pg->b[i].rect, - pg->b[i].data.record, + &b->rect, b->data.record, tree->height - level); if (p != NULL) { /* root splitted */ struct rtree_page *new_root = rtree_alloc_page(tree); - rtree_page_init_with_pages(new_root, + rtree_page_init_with_pages(tree, new_root, tree->root, p); tree->root = new_root; tree->height++; @@ -852,13 +1010,15 @@ rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj) } } level--; - struct rtree_page *next = get_next_reinsert_page(pg); + struct rtree_page *next = get_next_reinsert_page(tree, 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; + struct rtree_page_branch *b; + b = rtree_get_branch(tree, tree->root, 0); + struct rtree_page *new_root = b->data.page; rtree_free_page(tree, tree->root); tree->root = new_root; tree->height--; @@ -877,7 +1037,7 @@ rtree_search(const struct rtree *tree, const struct rtree_rect *rect, assert(itr->tree == 0 || itr->tree == tree); itr->tree = tree; itr->version = tree->version; - itr->rect = *rect; + rtree_rect_copy(&itr->rect, rect, tree->dimension); itr->op = op; assert(tree->height <= RTREE_MAX_HEIGHT); switch (op) { @@ -907,17 +1067,18 @@ rtree_search(const struct rtree *tree, const struct rtree_rect *rect, break; case SOP_NEIGHBOR: if (tree->root) { - struct rtree_rect cover = rtree_page_cover(tree->root); + struct rtree_rect cover; + rtree_page_cover(tree, tree->root, &cover); sq_coord_t distance = - rtree_rect_point_distance2(&cover, - &rect->lower_point); - itr->neigh_list = + rtree_rect_neigh_distance2(&cover, rect, + tree->dimension); + struct rtree_neighbor *n = rtree_iterator_new_neighbor(itr, tree->root, distance, tree->height); + rtnt_insert(&itr->neigh_tree, n); return true; } else { - itr->neigh_list = NULL; return false; } } @@ -947,7 +1108,7 @@ rtree_purge(struct rtree *tree) size_t rtree_used_size(const struct rtree *tree) { - return tree->n_pages * RTREE_PAGE_SIZE; + return tree->n_pages * tree->page_size; } unsigned @@ -956,32 +1117,32 @@ rtree_number_of_records(const struct rtree *tree) { } #if 0 +#include <stdio.h> void -rtree_debug_print_page(const struct rtree_page *page, unsigned level, unsigned path) +rtree_debug_print_page(const struct rtree *tree, 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("%d:\n", path); + unsigned d = tree->dimension; + for (int i = 0; i < page->n; i++) { + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, i); + double v = 1; + for (unsigned j = 0; j < d; j++) { + double d1 = b->rect.coords[j * 2]; + double d2 = b->rect.coords[j * 2 + 1]; + v *= (d2 - d1) / 100; + printf("[%04.1lf-%04.1lf:%04.1lf]", d2, d1, d2 - d1); } - printf("\n"); - for (int i = 0; i < page->n; i++) - rtree_debug_print_page(page->b[i].data.page, level, path * 100 + i); - } else { + printf("%d\n", (int)(v * 100)); + } + if (--level > 1) { 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); + struct rtree_page_branch *b; + b = rtree_get_branch(tree, page, i); + rtree_debug_print_page(tree, b->data.page, level, + path * 100 + i + 1); } - printf("\n"); } } @@ -989,7 +1150,7 @@ void rtree_debug_print(const struct rtree *tree) { if (tree->root) - rtree_debug_print_page(tree->root, tree->height, 0); + rtree_debug_print_page(tree, tree->root, tree->height, 1); } #endif diff --git a/src/lib/salad/rtree.h b/src/lib/salad/rtree.h index 4d1f4c2e2864810d17ee7a6f99adb82ff0e86f4c..a1f5ba50257d1faf81cba79690bad4d386730055 100644 --- a/src/lib/salad/rtree.h +++ b/src/lib/salad/rtree.h @@ -32,6 +32,9 @@ #include <stdbool.h> #include "small/matras.h" +#define RB_COMPACT 1 +#include "third_party/rb.h" + /** * In-memory Guttman's R-tree */ @@ -49,17 +52,21 @@ typedef double area_t; extern "C" { #endif /* defined(__cplusplus) */ +struct rtree_neighbor { + rb_node(struct rtree_neighbor) link; + struct rtree_neighbor *next; + void *child; + int level; + sq_coord_t distance; +}; + +typedef rb_tree(struct rtree_neighbor) rtnt_t; + enum { - /** Number of dimensions of R-tree geometry */ - RTREE_DIMENSION = 2, /** Maximal possible R-tree height */ RTREE_MAX_HEIGHT = 16, - /** - * R-Tree uses linear search for elements on a page, - * so a larger page size can hurt performance. - * must be power of 2 - */ - RTREE_PAGE_SIZE = 1024 + /** Maximal possible R-tree height */ + RTREE_MAX_DIMENSION = 20 }; /** @@ -93,31 +100,36 @@ enum spatial_search_op typedef void *(*rtree_extent_alloc_t)(); typedef void (*rtree_extent_free_t)(void *); -/* A point in RTREE_DIMENSION space */ -struct rtree_point -{ - /* coordinates of the point */ - coord_t coords[RTREE_DIMENSION]; -}; - /* A box in RTREE_DIMENSION space */ struct rtree_rect { - /* vertex with minimal coordinates */ - struct rtree_point lower_point; - /* vertex with maximal coordinates (diagonal to lower_point) */ - struct rtree_point upper_point; + /* coords: { low X, upper X, low Y, upper Y, etc } */ + coord_t coords[RTREE_MAX_DIMENSION * 2]; }; /* Type of function, comparing two rectangles */ typedef bool (*rtree_comparator_t)(const struct rtree_rect *rt1, - const struct rtree_rect *rt2); + const struct rtree_rect *rt2, + unsigned dimension); /* Main rtree struct */ struct rtree { /* Root node (page) */ struct rtree_page *root; + /* R-tree dimension */ + unsigned dimension; + /* Minimal number of branches in tree page */ + unsigned page_min_fill; + /* Maximal number of branches in tree page */ + unsigned page_max_fill; + /* Page size in bytes */ + unsigned page_size; + /* Page branch size in bytes */ + unsigned page_branch_size; + /* For iterator usage, pages are splitted into structs neighbours + * Here is number of neighbours fit into one page */ + unsigned neighbours_in_page; /* Number of records in entire tree */ unsigned n_records; /* Height of a tree */ @@ -146,13 +158,13 @@ struct rtree_iterator /* A verion of a tree when the iterator was created */ int version; - /* Special single-linked list of closest neqighbors + /* Special rb tree of closest neqighbors * Used only for iteration with op = SOP_NEIGHBOR * For allocating list entries, page allocator of tree is used. * Allocated page is much bigger than list entry and thus * provides several list entries. */ - struct rtree_neighbor *neigh_list; + rtnt_t neigh_tree; /* List of unused (deleted) list entries */ struct rtree_neighbor *neigh_free_list; /* List of tree pages, allocated for list entries */ @@ -184,7 +196,7 @@ struct rtree_iterator * @param rect - pointer to a rectangle */ void -rtree_rect_normalize(struct rtree_rect *rect); +rtree_rect_normalize(struct rtree_rect *rect, unsigned dimension); /** * @brief Set up 2D rectangle by 4 coordinates @@ -195,15 +207,24 @@ void rtree_set2d(struct rtree_rect *rect, coord_t left, coord_t bottom, coord_t right, coord_t top); +/** + * @brief Set up 2D rectangle by 2 coordinates (set to point) + * @param rect - pointer to a rectangle + * @params x, y - corresponding coordinates + */ +void +rtree_set2dp(struct rtree_rect *rect, coord_t x, coord_t y); + /** * @brief Initialize a tree * @param tree - pointer to a tree * @param extent_size - size of extents allocated by extent_alloc (see next) * @param extent_alloc - extent allocation function * @param extent_free - extent deallocation function + * @return 0 on success, -1 on error */ -void -rtree_init(struct rtree *tree, uint32_t extent_size, +int +rtree_init(struct rtree *tree, unsigned dimension, uint32_t extent_size, rtree_extent_alloc_t extent_alloc, rtree_extent_free_t extent_free); /** diff --git a/test/box/misc.result b/test/box/misc.result index 8ff8f3bc9e42856884a49fbed898b9249ae78b28..7a3030305d43d8e7f76d51a604d3a76ff21ebba1 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -257,6 +257,7 @@ t; - 'box.error.DROP_USER : 44' - 'box.error.CROSS_ENGINE_TRANSACTION : 81' - 'box.error.injection : table: <address> + - 'box.error.RTREE_RECT_ERROR : 101' - 'box.error.FUNCTION_LANGUAGE : 100' - 'box.error.MODIFY_INDEX : 14' - 'box.error.TUPLE_FOUND : 3' diff --git a/test/box/rtree_array.result b/test/box/rtree_array.result index 659b7905c4f24973b287a4e8647e384866bf92a9..18925aa6f7c409b470225e2468e322ab6651d540 100644 --- a/test/box/rtree_array.result +++ b/test/box/rtree_array.result @@ -76,16 +76,191 @@ s.index.spatial:select({10.0,10.0}, {iterator = 'EQ'}) -- select neighbors of point (5,5) s.index.spatial:select({5.0,5.0}, {iterator = 'NEIGHBOR'}) --- -- - [6, [10, 10]] - - [4, [10, 0]] +- - [1, [0, 0]] - [2, [0, 10]] - - [1, [0, 0]] - - [8, [50, 10]] - - [7, [10, 50]] - - [5, [50, 0]] + - [4, [10, 0]] + - [6, [10, 10]] - [3, [0, 50]] + - [5, [50, 0]] + - [7, [10, 50]] + - [8, [50, 10]] - [9, [50, 50]] ... s:drop() --- ... +s = box.schema.space.create('spatial') +--- +... +_ = s:create_index('primary') +--- +... +spatial = s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}, dimension = 8}) +--- +... +spatial.type +--- +- RTREE +... +s:insert{ 1,{0, 0, 0, 0, 0, 0, 0, 0}} +--- +- [1, [0, 0, 0, 0, 0, 0, 0, 0]] +... +s:insert{ 2,{10, 0, 0, 0, 0, 0, 0, 0}} +--- +- [2, [10, 0, 0, 0, 0, 0, 0, 0]] +... +s:insert{ 3,{0, 10, 0, 0, 0, 0, 0, 0}} +--- +- [3, [0, 10, 0, 0, 0, 0, 0, 0]] +... +s:insert{ 4,{0, 0, 10, 0, 0, 0, 0, 0}} +--- +- [4, [0, 0, 10, 0, 0, 0, 0, 0]] +... +s:insert{ 5,{0, 0, 0, 10, 0, 0, 0, 0}} +--- +- [5, [0, 0, 0, 10, 0, 0, 0, 0]] +... +s:insert{ 6,{0, 0, 0, 0, 10, 0, 0, 0}} +--- +- [6, [0, 0, 0, 0, 10, 0, 0, 0]] +... +s:insert{ 7,{0, 0, 0, 0, 0, 10, 0, 0}} +--- +- [7, [0, 0, 0, 0, 0, 10, 0, 0]] +... +s:insert{ 8,{0, 0, 0, 0, 0, 0, 10, 0}} +--- +- [8, [0, 0, 0, 0, 0, 0, 10, 0]] +... +s:insert{ 9,{0, 0, 0, 0, 0, 0, 0, 10}} +--- +- [9, [0, 0, 0, 0, 0, 0, 0, 10]] +... +s:insert{10,{50, 0, 0, 0, 0, 0, 0, 0}} +--- +- [10, [50, 0, 0, 0, 0, 0, 0, 0]] +... +s:insert{11,{0, 50, 0, 0, 0, 0, 0, 0}} +--- +- [11, [0, 50, 0, 0, 0, 0, 0, 0]] +... +s:insert{12,{0, 0, 50, 0, 0, 0, 0, 0}} +--- +- [12, [0, 0, 50, 0, 0, 0, 0, 0]] +... +s:insert{13,{0, 0, 0, 50, 0, 0, 0, 0}} +--- +- [13, [0, 0, 0, 50, 0, 0, 0, 0]] +... +s:insert{14,{0, 0, 0, 0, 50, 0, 0, 0}} +--- +- [14, [0, 0, 0, 0, 50, 0, 0, 0]] +... +s:insert{15,{0, 0, 0, 0, 0, 50, 0, 0}} +--- +- [15, [0, 0, 0, 0, 0, 50, 0, 0]] +... +s:insert{16,{0, 0, 0, 0, 0, 0, 50, 0}} +--- +- [16, [0, 0, 0, 0, 0, 0, 50, 0]] +... +s:insert{17,{0, 0, 0, 0, 0, 0, 0, 50}} +--- +- [17, [0, 0, 0, 0, 0, 0, 0, 50]] +... +s:insert{18,{10, 10, 10, 10, 10, 10, 10, 10}} +--- +- [18, [10, 10, 10, 10, 10, 10, 10, 10]] +... +s:insert{19,{10, 50, 10, 50, 10, 50, 10, 50}} +--- +- [19, [10, 50, 10, 50, 10, 50, 10, 50]] +... +s:insert{20,{0, 10, 50, 0, 10, 50, 0, 10}} +--- +- [20, [0, 10, 50, 0, 10, 50, 0, 10]] +... +p0 = {0, 0, 0, 0, 0, 0, 0, 0} +--- +... +p5 = {5, 5, 5, 5, 5, 5, 5, 5} +--- +... +p10 = {10, 10, 10, 10, 10, 10, 10, 10 } +--- +... +rt0_10 = {0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10 } +--- +... +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +--- +- - [1, [0, 0, 0, 0, 0, 0, 0, 0]] + - [2, [10, 0, 0, 0, 0, 0, 0, 0]] + - [3, [0, 10, 0, 0, 0, 0, 0, 0]] + - [4, [0, 0, 10, 0, 0, 0, 0, 0]] + - [5, [0, 0, 0, 10, 0, 0, 0, 0]] + - [6, [0, 0, 0, 0, 10, 0, 0, 0]] + - [7, [0, 0, 0, 0, 0, 10, 0, 0]] + - [8, [0, 0, 0, 0, 0, 0, 10, 0]] + - [9, [0, 0, 0, 0, 0, 0, 0, 10]] + - [10, [50, 0, 0, 0, 0, 0, 0, 0]] + - [11, [0, 50, 0, 0, 0, 0, 0, 0]] + - [12, [0, 0, 50, 0, 0, 0, 0, 0]] + - [13, [0, 0, 0, 50, 0, 0, 0, 0]] + - [14, [0, 0, 0, 0, 50, 0, 0, 0]] + - [15, [0, 0, 0, 0, 0, 50, 0, 0]] + - [16, [0, 0, 0, 0, 0, 0, 50, 0]] + - [17, [0, 0, 0, 0, 0, 0, 0, 50]] + - [18, [10, 10, 10, 10, 10, 10, 10, 10]] + - [19, [10, 50, 10, 50, 10, 50, 10, 50]] + - [20, [0, 10, 50, 0, 10, 50, 0, 10]] +... +-- select records belonging to rectangle (0,0,..10,10,..) +s.index.spatial:select(rt0_10, {iterator = 'LE'}) +--- +- - [1, [0, 0, 0, 0, 0, 0, 0, 0]] + - [2, [10, 0, 0, 0, 0, 0, 0, 0]] + - [3, [0, 10, 0, 0, 0, 0, 0, 0]] + - [4, [0, 0, 10, 0, 0, 0, 0, 0]] + - [5, [0, 0, 0, 10, 0, 0, 0, 0]] + - [6, [0, 0, 0, 0, 10, 0, 0, 0]] + - [7, [0, 0, 0, 0, 0, 10, 0, 0]] + - [8, [0, 0, 0, 0, 0, 0, 10, 0]] + - [9, [0, 0, 0, 0, 0, 0, 0, 10]] + - [18, [10, 10, 10, 10, 10, 10, 10, 10]] +... +-- select records with coordinates (10,10) +s.index.spatial:select(p10, {iterator = 'EQ'}) +--- +- - [18, [10, 10, 10, 10, 10, 10, 10, 10]] +... +-- select neighbors of point (5,5) +s.index.spatial:select(p5, {iterator = 'NEIGHBOR'}) +--- +- - [1, [0, 0, 0, 0, 0, 0, 0, 0]] + - [2, [10, 0, 0, 0, 0, 0, 0, 0]] + - [3, [0, 10, 0, 0, 0, 0, 0, 0]] + - [4, [0, 0, 10, 0, 0, 0, 0, 0]] + - [5, [0, 0, 0, 10, 0, 0, 0, 0]] + - [6, [0, 0, 0, 0, 10, 0, 0, 0]] + - [7, [0, 0, 0, 0, 0, 10, 0, 0]] + - [8, [0, 0, 0, 0, 0, 0, 10, 0]] + - [9, [0, 0, 0, 0, 0, 0, 0, 10]] + - [18, [10, 10, 10, 10, 10, 10, 10, 10]] + - [10, [50, 0, 0, 0, 0, 0, 0, 0]] + - [11, [0, 50, 0, 0, 0, 0, 0, 0]] + - [12, [0, 0, 50, 0, 0, 0, 0, 0]] + - [13, [0, 0, 0, 50, 0, 0, 0, 0]] + - [14, [0, 0, 0, 0, 50, 0, 0, 0]] + - [15, [0, 0, 0, 0, 0, 50, 0, 0]] + - [16, [0, 0, 0, 0, 0, 0, 50, 0]] + - [17, [0, 0, 0, 0, 0, 0, 0, 50]] + - [20, [0, 10, 50, 0, 10, 50, 0, 10]] + - [19, [10, 50, 10, 50, 10, 50, 10, 50]] +... +s:drop() +--- +... diff --git a/test/box/rtree_array.test.lua b/test/box/rtree_array.test.lua index 19897b1d3acf92f3a4f5f664e741253e477b42d5..f6a0785baf17f4db5518ba6411940a83464fe9f0 100644 --- a/test/box/rtree_array.test.lua +++ b/test/box/rtree_array.test.lua @@ -24,3 +24,46 @@ s.index.spatial:select({10.0,10.0}, {iterator = 'EQ'}) s.index.spatial:select({5.0,5.0}, {iterator = 'NEIGHBOR'}) s:drop() + +s = box.schema.space.create('spatial') +_ = s:create_index('primary') +spatial = s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}, dimension = 8}) + +spatial.type + +s:insert{ 1,{0, 0, 0, 0, 0, 0, 0, 0}} +s:insert{ 2,{10, 0, 0, 0, 0, 0, 0, 0}} +s:insert{ 3,{0, 10, 0, 0, 0, 0, 0, 0}} +s:insert{ 4,{0, 0, 10, 0, 0, 0, 0, 0}} +s:insert{ 5,{0, 0, 0, 10, 0, 0, 0, 0}} +s:insert{ 6,{0, 0, 0, 0, 10, 0, 0, 0}} +s:insert{ 7,{0, 0, 0, 0, 0, 10, 0, 0}} +s:insert{ 8,{0, 0, 0, 0, 0, 0, 10, 0}} +s:insert{ 9,{0, 0, 0, 0, 0, 0, 0, 10}} +s:insert{10,{50, 0, 0, 0, 0, 0, 0, 0}} +s:insert{11,{0, 50, 0, 0, 0, 0, 0, 0}} +s:insert{12,{0, 0, 50, 0, 0, 0, 0, 0}} +s:insert{13,{0, 0, 0, 50, 0, 0, 0, 0}} +s:insert{14,{0, 0, 0, 0, 50, 0, 0, 0}} +s:insert{15,{0, 0, 0, 0, 0, 50, 0, 0}} +s:insert{16,{0, 0, 0, 0, 0, 0, 50, 0}} +s:insert{17,{0, 0, 0, 0, 0, 0, 0, 50}} +s:insert{18,{10, 10, 10, 10, 10, 10, 10, 10}} +s:insert{19,{10, 50, 10, 50, 10, 50, 10, 50}} +s:insert{20,{0, 10, 50, 0, 10, 50, 0, 10}} + +p0 = {0, 0, 0, 0, 0, 0, 0, 0} +p5 = {5, 5, 5, 5, 5, 5, 5, 5} +p10 = {10, 10, 10, 10, 10, 10, 10, 10 } +rt0_10 = {0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10 } + +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +-- select records belonging to rectangle (0,0,..10,10,..) +s.index.spatial:select(rt0_10, {iterator = 'LE'}) +-- select records with coordinates (10,10) +s.index.spatial:select(p10, {iterator = 'EQ'}) +-- select neighbors of point (5,5) +s.index.spatial:select(p5, {iterator = 'NEIGHBOR'}) + +s:drop() diff --git a/test/box/rtree_misc.result b/test/box/rtree_misc.result index f356dac885bf27a91fdeb57adac9595d4749d7d8..9b5183d5fd4bbe726ed64d9a48c79aefcc88f114 100644 --- a/test/box/rtree_misc.result +++ b/test/box/rtree_misc.result @@ -93,8 +93,8 @@ s:insert{1, 2, nil, 3} ... s:insert{1, 2, {}} --- -- error: R-Tree index does not support Key should contain 2 (point) or 4 (rectangle) - coordinates +- error: 'RTree: Field must be an array with 2 (point) or 4 (rectangle/box) numeric + coordinates' ... s:insert{1, 2, {"3", "4", "5", "6"}} --- @@ -118,13 +118,13 @@ s:insert{1, 2, {3, 4, 5, "6"}} ... s:insert{1, 2, {3}} --- -- error: R-Tree index does not support Field should be array with size 2 (point) or - 4 (rectangle) +- error: 'RTree: Field must be an array with 2 (point) or 4 (rectangle/box) numeric + coordinates' ... s:insert{1, 2, {3, 4, 5}} --- -- error: R-Tree index does not support Key should contain 2 (point) or 4 (rectangle) - coordinates +- error: 'RTree: Field must be an array with 2 (point) or 4 (rectangle/box) numeric + coordinates' ... -- inserting good value s:insert{1, 2, {3, 4, 5, 6}} diff --git a/test/box/rtree_point.result b/test/box/rtree_point.result index 69c8214de1b0b27f1eddd5c0a5bc0971b6e2be6d..6591342f32b765f51db1534a7f9fd59d885f6921 100644 --- a/test/box/rtree_point.result +++ b/test/box/rtree_point.result @@ -72,14 +72,14 @@ s.index.spatial:select({10,10}, {iterator = 'EQ'}) -- select neighbors of point (5,5) s.index.spatial:select({5,5}, {iterator = 'NEIGHBOR'}) --- -- - [6, [10, 10]] - - [4, [10, 0]] +- - [1, [0, 0]] - [2, [0, 10]] - - [1, [0, 0]] - - [8, [50, 10]] - - [7, [10, 50]] - - [5, [50, 0]] + - [4, [10, 0]] + - [6, [10, 10]] - [3, [0, 50]] + - [5, [50, 0]] + - [7, [10, 50]] + - [8, [50, 10]] - [9, [50, 50]] ... s:drop() diff --git a/test/box/rtree_point_r2.result b/test/box/rtree_point_r2.result index 238d0d9cd00b2eb4c1e69acf23dc6be251289be7..ce89fac9fd57038a15fcb26fd2f5b0e404ae2638 100644 --- a/test/box/rtree_point_r2.result +++ b/test/box/rtree_point_r2.result @@ -72,14 +72,14 @@ s.index.spatial:select({10.0,10.0}, {iterator = 'EQ'}) -- select neighbors of point (5,5) s.index.spatial:select({5.0,5.0}, {iterator = 'NEIGHBOR'}) --- -- - [6, [10, 10]] - - [4, [10, 0]] +- - [1, [0, 0]] - [2, [0, 10]] - - [1, [0, 0]] - - [8, [50, 10]] - - [7, [10, 50]] - - [5, [50, 0]] + - [4, [10, 0]] + - [6, [10, 10]] - [3, [0, 50]] + - [5, [50, 0]] + - [7, [10, 50]] + - [8, [50, 10]] - [9, [50, 50]] ... s:drop() diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 6136e80c216479bc05f4ef55915e072352ebb306..b09b15f25365dcd1b77ce5c60cd9431db511de99 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -43,14 +43,16 @@ add_executable(slab_arena.test slab_arena.c) target_link_libraries(slab_arena.test small) add_executable(arena_mt.test arena_mt.c) target_link_libraries(arena_mt.test small pthread) -add_executable(bps_tree.test bps_tree.cc ${CMAKE_SOURCE_DIR}/third_party/qsort_arg.c) -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}/src/lib/salad/rtree.c) -target_link_libraries(rtree.test small) -add_executable(rtree_itr.test rtree_itr.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtree.c) -target_link_libraries(rtree_itr.test small) +add_executable(bps_tree.test bps_tree.cc) +target_link_libraries(bps_tree.test small misc) +add_executable(bps_tree_itr.test bps_tree_itr.cc) +target_link_libraries(bps_tree_itr.test small misc) +add_executable(rtree.test rtree.cc) +target_link_libraries(rtree.test salad small) +add_executable(rtree_itr.test rtree_itr.cc) +target_link_libraries(rtree_itr.test salad small) +add_executable(rtree_multidim.test rtree_multidim.cc) +target_link_libraries(rtree_multidim.test salad small) add_executable(matras.test matras.cc) target_link_libraries(matras.test small) add_executable(light.test light.cc) diff --git a/test/unit/rtree.cc b/test/unit/rtree.cc index e8836deb9d5c81722f0e45d55e4c8de1d7373ccf..3469fe2d1ec705936d0e73e5e9bb324fa9d04a95 100644 --- a/test/unit/rtree.cc +++ b/test/unit/rtree.cc @@ -9,7 +9,7 @@ static int page_count = 0; -const uint32_t extent_size = RTREE_PAGE_SIZE * 8; +const uint32_t extent_size = 1024 * 8; static void * extent_alloc() @@ -36,16 +36,13 @@ simple_check() header(); struct rtree tree; - rtree_init(&tree, extent_size, extent_alloc, extent_free); + rtree_init(&tree, 2, extent_size, extent_alloc, extent_free); printf("Insert 1..X, remove 1..X\n"); for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { fail("element already in tree (1)", "true"); @@ -58,10 +55,7 @@ simple_check() for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (!rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { fail("element in tree (1)", "false"); @@ -87,10 +81,7 @@ simple_check() for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { fail("element already in tree (2)", "true"); @@ -103,10 +94,7 @@ simple_check() for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (!rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) { fail("element in tree (2)", "false"); @@ -133,10 +121,7 @@ simple_check() for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { fail("element already in tree (3)", "true"); @@ -149,10 +134,7 @@ simple_check() for (size_t i = 1; i <= rounds; i++) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (!rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { fail("element in tree (3)", "false"); @@ -179,10 +161,7 @@ simple_check() for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { fail("element already in tree (4)", "true"); @@ -195,10 +174,7 @@ simple_check() for (size_t i = rounds; i != 0; i--) { record_t rec = (record_t)i; - 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; + rtree_set2d(&rect, i, i, i + 0.5, i + 0.5); if (!rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { fail("element in tree (4)", "false"); @@ -247,15 +223,12 @@ neighbor_test() static struct rtree_rect basis; for (size_t i = 0; i < test_count; i++) { - 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; + rtree_set2d(&arr[i], i, i, i + 1, i + 1); } for (size_t i = 0; i <= test_count; i++) { struct rtree tree; - rtree_init(&tree, extent_size, extent_alloc, extent_free); + rtree_init(&tree, 2, extent_size, extent_alloc, extent_free); rtree_test_build(&tree, arr, i); diff --git a/test/unit/rtree_itr.cc b/test/unit/rtree_itr.cc index 08bc959aec8ec7efc0c194ba0aeadea47b822ce2..088edd657e1ca3e929247251656a931037ce263d 100644 --- a/test/unit/rtree_itr.cc +++ b/test/unit/rtree_itr.cc @@ -8,7 +8,7 @@ static int extent_count = 0; -const uint32_t extent_size = RTREE_PAGE_SIZE * 8; +const uint32_t extent_size = 1024 * 8; static void * extent_alloc() @@ -30,7 +30,7 @@ itr_check() header(); struct rtree tree; - rtree_init(&tree, extent_size, extent_alloc, extent_free); + rtree_init(&tree, 2, extent_size, extent_alloc, extent_free); /* Filling tree */ const size_t count1 = 10000; @@ -161,7 +161,7 @@ itr_check() 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); + rtree_rect_normalize(&rect, 2); if (!rtree_search(&tree, &rect, SOP_STRICT_CONTAINS, &iterator) && j != 0 && j != count2 - 1) { fail("Integrity check failed (11)", "false"); } @@ -211,7 +211,7 @@ itr_invalidate_check() del_cnt = test_size - del_pos; } struct rtree tree; - rtree_init(&tree, extent_size, extent_alloc, extent_free); + rtree_init(&tree, 2, extent_size, extent_alloc, extent_free); struct rtree_iterator iterators[test_size]; for (size_t i = 0; i < test_size; i++) rtree_iterator_init(iterators + i); @@ -255,7 +255,7 @@ itr_invalidate_check() size_t ins_cnt = rand() % max_insert_count + 1; struct rtree tree; - rtree_init(&tree, extent_size, extent_alloc, extent_free); + rtree_init(&tree, 2, extent_size, extent_alloc, extent_free); struct rtree_iterator iterators[test_size]; for (size_t i = 0; i < test_size; i++) rtree_iterator_init(iterators + i); diff --git a/test/unit/rtree_itr.result b/test/unit/rtree_itr.result index 4a3762cf11520ff94c45e7e94d41795a43ae52aa..14196b6b38923ea23b06006fb47dea36a9768501 100644 --- a/test/unit/rtree_itr.result +++ b/test/unit/rtree_itr.result @@ -1,6 +1,6 @@ *** itr_check *** Test tree size: 50000 ---> 0x5 0x4 0x3 0x2 0x1 0xa 0x9 +--> 0x1 0x2 0x3 0x4 0x5 0x6 0x7 <-- 0xc34c 0xc34d 0xc34e 0xc34f 0xc350 0xc34b 0xc34a *** itr_check: done *** *** itr_invalidate_check *** diff --git a/test/unit/rtree_multidim.cc b/test/unit/rtree_multidim.cc new file mode 100644 index 0000000000000000000000000000000000000000..4d67179e47d47437180bf4fb6e1770bf27ffaaa2 --- /dev/null +++ b/test/unit/rtree_multidim.cc @@ -0,0 +1,436 @@ +#include <algorithm> + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <stdbool.h> +#include <inttypes.h> +#include <time.h> + +#include "unit.h" +#include "salad/rtree.h" +#include "../../src/lib/salad/rtree.h" + +#include <vector> +#include <set> +using namespace std; + +const uint32_t extent_size = 1024 * 16; + +const coord_t SPACE_LIMIT = 100; +const coord_t BOX_LIMIT = 10; +const unsigned BOX_POINT_CHANCE_PERCENT = 5; +const unsigned NEIGH_COUNT = 5; +const unsigned AVERAGE_COUNT = 500; +const unsigned TEST_ROUNDS = 1000; + +static int page_count = 0; + +static void * +extent_alloc() +{ + page_count++; + return malloc(extent_size); +} + +static void +extent_free(void *page) +{ + page_count--; + free(page); +} + +struct CCoordPair { + coord_t a, b; +}; + +coord_t +rand(coord_t lim) +{ + return rand() % 1024 * lim / 1024; +} + +template<unsigned DIMENSION> +struct CBox { + CCoordPair pairs[DIMENSION]; + void RandomPoint() + { + for (unsigned i = 0; i < DIMENSION; i++) { + pairs[i].b = pairs[i].a = rand(SPACE_LIMIT); + } + } + void Randomize() + { + coord_t widths[DIMENSION] = {0}; + if (rand() % 100 >= BOX_POINT_CHANCE_PERCENT) + for (unsigned i = 0; i < DIMENSION; i++) + widths[i] = rand(BOX_LIMIT); + for (unsigned i = 0; i < DIMENSION; i++) { + pairs[i].a = rand(SPACE_LIMIT - widths[i]); + pairs[i].b = pairs[i].a + widths[i]; + } + } + void RandomizeBig() + { + coord_t widths[DIMENSION] = {0}; + if (DIMENSION == 1) + for (unsigned i = 0; i < DIMENSION; i++) + widths[i] = rand(SPACE_LIMIT / 4); + else if (DIMENSION == 2) + for (unsigned i = 0; i < DIMENSION; i++) + widths[i] = rand(SPACE_LIMIT / 3); + else if (DIMENSION == 2) + for (unsigned i = 0; i < DIMENSION; i++) + widths[i] = rand(SPACE_LIMIT / 2); + else + for (unsigned i = 0; i < DIMENSION; i++) + widths[i] = rand(SPACE_LIMIT); + + for (unsigned i = 0; i < DIMENSION; i++) { + pairs[i].a = rand(SPACE_LIMIT - widths[i]); + pairs[i].b = pairs[i].a + widths[i]; + } + } + void FillRTreeRect(struct rtree_rect *rt) + { + for (unsigned i = 0; i < DIMENSION; i++) { + rt->coords[2 * i] = pairs[i].a; + rt->coords[2 * i + 1] = pairs[i].b; + } + } + bool operator== (const struct rtree_rect *rt) const + { + for (unsigned i = 0; i < DIMENSION; i++) { + if (rt->coords[2 * i] != pairs[i].a || + rt->coords[2 * i + 1] != pairs[i].b) + return false; + } + return true; + } + bool In(const CBox<DIMENSION> &another) const + { + for (unsigned i = 0; i < DIMENSION; i++) { + if (pairs[i].a < another.pairs[i].a || + pairs[i].b > another.pairs[i].b) + return false; + } + return true; + } + bool InStrictly(const CBox<DIMENSION> &another) const + { + for (unsigned i = 0; i < DIMENSION; i++) { + if (pairs[i].a <= another.pairs[i].a || + pairs[i].b >= another.pairs[i].b) + return false; + } + return true; + } + coord_t Distance2(const CBox<DIMENSION> &point) const + { + coord_t res = 0; + for (unsigned i = 0; i < DIMENSION; i++) { + if (point.pairs[i].a < pairs[i].a) { + coord_t d = pairs[i].a - point.pairs[i].a; + res += d * d; + } else if (point.pairs[i].a > pairs[i].b) { + coord_t d = point.pairs[i].a - pairs[i].b; + res += d * d; + } + } + return res; + } +}; + +template<unsigned DIMENSION> +struct CBoxSetEntry { + CBox<DIMENSION> box; + size_t id; + size_t next; + bool used; + bool operator<(const CBoxSetEntry<DIMENSION> &a) const + { + return id < a.id; + } +}; + +template<unsigned DIMENSION> +struct CBoxSet { + vector<CBoxSetEntry<DIMENSION> > entries; + size_t boxCount; + size_t free; + CBoxSet() : boxCount(0), free(SIZE_MAX) {} + size_t getNewID() + { + size_t res; + if (free != SIZE_MAX) { + res = free; + free = entries[free].next; + } else { + res = entries.size(); + entries.resize(res + 1); + } + return res; + } + size_t AddBox(const CBox<DIMENSION> &box) + { + size_t id = getNewID(); + entries[id].box = box; + entries[id].id = id; + entries[id].next = SIZE_MAX; + entries[id].used = true; + boxCount++; + return id; + } + size_t RandUsedID() const + { + assert(boxCount); + size_t res = rand() % entries.size(); + while (!entries[res].used) + if (++res >= entries.size()) + res = 0; + return res; + } + void DeleteBox(size_t id) + { + entries[id].used = false; + entries[id].next = free; + free = id; + boxCount--; + } + void SelectIn(const CBox<DIMENSION> &box, + vector<CBoxSetEntry<DIMENSION> > &result) const + { + result.clear(); + for (size_t i = 0; i < entries.size(); i++) + if (entries[i].used && entries[i].box.In(box)) + result.push_back(entries[i]); + } + void SelectInStrictly(const CBox<DIMENSION> &box, + vector<CBoxSetEntry<DIMENSION> > &result) const + { + result.clear(); + for (size_t i = 0; i < entries.size(); i++) + if (entries[i].used && entries[i].box.InStrictly(box)) + result.push_back(entries[i]); + } + void SelectNeigh(const CBox<DIMENSION> &point, + vector<CBoxSetEntry<DIMENSION> > &result) const; +}; + +template<unsigned DIMENSION> +struct CEntryByDistance { + const CBox<DIMENSION> &point; + CEntryByDistance(const CBox<DIMENSION> &point_) : point(point_) {} + bool operator()(const CBoxSetEntry<DIMENSION> &a, + const CBoxSetEntry<DIMENSION> &b) const + { + coord_t da = a.box.Distance2(point); + coord_t db = b.box.Distance2(point); + return da < db ? true : da > db ? false : a.id < b.id; + } +}; + +template<unsigned DIMENSION> +void CBoxSet<DIMENSION>::SelectNeigh(const CBox<DIMENSION> &point, + vector<CBoxSetEntry<DIMENSION> > &result) const +{ + result.clear(); + CEntryByDistance<DIMENSION> comp(point); + set<CBoxSetEntry<DIMENSION>, CEntryByDistance<DIMENSION> > set(comp); + size_t i = 0; + for (; i < entries.size() && set.size() < NEIGH_COUNT; i++) { + if (!entries[i].used) + continue; + set.insert(entries[i]); + } + coord_t max_d = set.rbegin()->box.Distance2(point); + for (; i < entries.size(); i++) { + if (!entries[i].used) + continue; + coord_t d = entries[i].box.Distance2(point); + if (d < max_d) { + auto itr = set.end(); + --itr; + set.erase(itr); + set.insert(entries[i]); + max_d = set.rbegin()->box.Distance2(point); + } + } + for (auto itr : set) + result.push_back(itr); +} + +template<unsigned DIMENSION> +static void +test_select_neigh(const CBoxSet<DIMENSION> &set, const struct rtree *tree) +{ + CBox<DIMENSION> box; + box.RandomizeBig(); + vector<CBoxSetEntry<DIMENSION> > res1; + set.SelectNeigh(box, res1); + + struct rtree_rect rt; + box.FillRTreeRect(&rt); + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + vector<CBoxSetEntry<DIMENSION> > res2; + if (rtree_search(tree, &rt, SOP_NEIGHBOR, &iterator)) { + void *record; + while((record = rtree_iterator_next(&iterator))) { + CBoxSetEntry<DIMENSION> entry; + entry.id = ((unsigned)(uintptr_t)record) - 1; + entry.box = set.entries[entry.id].box; + res2.push_back(entry); + if (res2.size() == NEIGH_COUNT) + break; + } + } + if (res1.size() != res2.size()) { + printf("%s result size differ %d %d\n", __func__, + (int)res1.size(), (int)res2.size()); + } else { + for (size_t i = 0; i < res1.size(); i++) + if (res1[i].id != res2[i].id && + res1[i].box.Distance2(box) != + res2[i].box.Distance2(box)) + printf("%s result differ!\n", __func__); + } + rtree_iterator_destroy(&iterator); + +} + +template<unsigned DIMENSION> +static void +test_select_in(const CBoxSet<DIMENSION> &set, const struct rtree *tree) +{ + CBox<DIMENSION> box; + box.RandomizeBig(); + vector<CBoxSetEntry<DIMENSION> > res1; + set.SelectIn(box, res1); + + struct rtree_rect rt; + box.FillRTreeRect(&rt); + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + vector<CBoxSetEntry<DIMENSION> > res2; + if (rtree_search(tree, &rt, SOP_BELONGS, &iterator)) { + void *record; + while((record = rtree_iterator_next(&iterator))) { + CBoxSetEntry<DIMENSION> entry; + entry.id = ((unsigned)(uintptr_t)record) - 1; + entry.box = set.entries[entry.id].box; + res2.push_back(entry); + } + } + sort(res1.begin(), res1.end()); + sort(res2.begin(), res2.end()); + if (res1.size() != res2.size()) { + printf("%s result size differ %d %d\n", __func__, + (int)res1.size(), (int)res2.size()); + } else { + for (size_t i = 0; i < res1.size(); i++) + if (res1[i].id != res2[i].id) + printf("%s result differ!\n", __func__); + } + rtree_iterator_destroy(&iterator); + +} + +template<unsigned DIMENSION> +static void +test_select_strict_in(const CBoxSet<DIMENSION> &set, const struct rtree *tree) +{ + CBox<DIMENSION> box; + box.RandomizeBig(); + vector<CBoxSetEntry<DIMENSION> > res1; + set.SelectInStrictly(box, res1); + + struct rtree_rect rt; + box.FillRTreeRect(&rt); + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + vector<CBoxSetEntry<DIMENSION> > res2; + if (rtree_search(tree, &rt, SOP_STRICT_BELONGS, &iterator)) { + void *record; + while((record = rtree_iterator_next(&iterator))) { + CBoxSetEntry<DIMENSION> entry; + entry.id = ((unsigned)(uintptr_t)record) - 1; + entry.box = set.entries[entry.id].box; + res2.push_back(entry); + } + } + sort(res1.begin(), res1.end()); + sort(res2.begin(), res2.end()); + if (res1.size() != res2.size()) { + printf("%s result size differ %d %d\n", __func__, + (int)res1.size(), (int)res2.size()); + } else { + for (size_t i = 0; i < res1.size(); i++) + if (res1[i].id != res2[i].id) + printf("%s result differ!\n", __func__); + } + rtree_iterator_destroy(&iterator); + +} + +template<unsigned DIMENSION> +static void +rand_test() +{ + header(); + + CBoxSet<DIMENSION> set; + + struct rtree tree; + rtree_init(&tree, DIMENSION, extent_size, extent_alloc, extent_free); + + printf("\tDIMENSION: %u, page size: %u, max fill: %u\n", + DIMENSION, tree.page_size, tree.page_max_fill); + + for (unsigned i = 0; i < TEST_ROUNDS; i++) { + bool insert; + if (set.boxCount == 0) { + insert = true; + } else if (set.boxCount == AVERAGE_COUNT) { + insert = false; + } else { + insert = rand() % (AVERAGE_COUNT * 2) > set.boxCount; + } + if (insert) { + CBox<DIMENSION> box; + box.Randomize(); + size_t id = set.AddBox(box); + struct rtree_rect rt; + box.FillRTreeRect(&rt); + rtree_insert(&tree, &rt, (void *)(id + 1)); + } else { + size_t id = set.RandUsedID(); + struct rtree_rect rt; + set.entries[id].box.FillRTreeRect(&rt); + rtree_remove(&tree, &rt, (void *)(id + 1)); + set.DeleteBox(id); + } + assert(set.boxCount == tree.n_records); + test_select_neigh<DIMENSION>(set, &tree); + test_select_in<DIMENSION>(set, &tree); + test_select_strict_in<DIMENSION>(set, &tree); + } + + rtree_destroy(&tree); + + footer(); +} + +int +main(void) +{ + srand(time(0)); + rand_test<1>(); + rand_test<2>(); + rand_test<3>(); + rand_test<8>(); + rand_test<16>(); + if (page_count != 0) { + fail("memory leak!", "true"); + } +} diff --git a/test/unit/rtree_multidim.result b/test/unit/rtree_multidim.result new file mode 100644 index 0000000000000000000000000000000000000000..f22a472274a738ddce04a533796e569c84fced5a --- /dev/null +++ b/test/unit/rtree_multidim.result @@ -0,0 +1,16 @@ + *** rand_test *** + DIMENSION: 1, page size: 512, max fill: 21 + *** rand_test: done *** + *** rand_test *** + DIMENSION: 2, page size: 1024, max fill: 25 + *** rand_test: done *** + *** rand_test *** + DIMENSION: 3, page size: 1024, max fill: 18 + *** rand_test: done *** + *** rand_test *** + DIMENSION: 8, page size: 4096, max fill: 30 + *** rand_test: done *** + *** rand_test *** + DIMENSION: 16, page size: 8192, max fill: 31 + *** rand_test: done *** + \ No newline at end of file diff --git a/test/wal_off/rtree_benchmark.result b/test/wal_off/rtree_benchmark.result index c4b4c4071fa0c8d972039698252638f3a1d6c946..16c486e4dc3815ce3c291e45b639c2ee613b2588 100644 --- a/test/wal_off/rtree_benchmark.result +++ b/test/wal_off/rtree_benchmark.result @@ -1,25 +1,32 @@ -s = box.schema.space.create('rtreebench') +n_records = 10000 --- ... -_ = s:create_index('primary') +n_iterations = 10000 --- ... -_ = s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) +n_neighbors = 10 --- ... -n_records = 20000 +file = io.open("rtree_benchmark.res", "w") --- ... -n_iterations = 10000 +s = box.schema.space.create('rtreebench') --- ... -n_neighbors = 10 +_ = s:create_index('primary') --- ... -file = io.open("rtree_benchmark.res", "w") +_ = s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) +--- +... +file:write(" *** 2D *** \n") +--- +- true +... +rect_width = 180 / math.pow(n_records, 1 / 2) --- ... -start = os.clock() +start = os.time() --- ... --# setopt delimiter ';' @@ -28,47 +35,47 @@ for i = 1, n_records do end; --- ... -file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.clock() - start)); +file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.time() - start)); --- - true ... -start = os.clock(); +start = os.time(); --- ... n = 0; --- ... for i = 1, n_iterations do - x = 180*math.random() - y = 180*math.random() - for k,v in s.index.spatial:pairs({x,y,x+1,y+1}, {iterator = 'LE'}) do + x = (180 - rect_width) * math.random() + y = (180 - rect_width) * math.random() + for k,v in s.index.spatial:pairs({x,y,x+rect_width,y+rect_width}, {iterator = 'LE'}) do n = n + 1 end end; --- ... -file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.clock() - start)); +file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.time() - start)); --- - true ... -start = os.clock(); +start = os.time(); --- ... n = 0 for i = 1, n_iterations do - x = 180*math.random() - y = 180*math.random() + x = 180 * math.random() + y = 180 * math.random() for k,v in pairs(s.index.spatial:select({x,y }, {limit = n_neighbors, iterator = 'NEIGHBOR'})) do n = n + 1 end end; --- ... -file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.clock() - start)); +file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.time() - start)); --- - true ... -start = os.clock(); +start = os.time(); --- ... for i = 1, n_records do @@ -76,14 +83,104 @@ for i = 1, n_records do end; --- ... -file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.clock() - start)); +file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.time() - start)); --- - true ... -file:close(); +s:drop(); +--- +... +dimension = 8; +--- +... +s = box.schema.space.create('rtreebench'); +--- +... +_ = s:create_index('primary'); +--- +... +_ = s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}, dimension = dimension}); +--- +... +file:write(" *** 8D *** \n") +rect_width = 180 / math.pow(n_records, 1 / dimension) + +start = os.time(); +--- +... +for i = 1, n_records do + local record = {} + for j = 1, dimension do + table.insert(record, 180*math.random()) + end + s:insert{i,record} +end; +--- +... +file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.time() - start)); +--- +- true +... +start = os.time(); +--- +... +n = 0; +--- +... +for i = 1, n_iterations do + local rect = {} + for j = 1, dimension do + table.insert(rect, (180 - rect_width) * math.random()) + end + for j = 1, dimension do + table.insert(rect, rect[j] + rect_width) + end + for k,v in s.index.spatial:pairs(rect, {iterator = 'LE'}) do + n = n + 1 + end +end; +--- +... +file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.time() - start)); +--- +- true +... +start = os.time(); +--- +... +n = 0 +for i = 1, 0 do + local rect = {} + for j = 1, dimension do + table.insert(rect, 180*math.random()) + end + for k,v in pairs(s.index.spatial:select(rect, {limit = n_neighbors, iterator = 'NEIGHBOR'})) do + n = n + 1 + end +end; +--- +... +file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.time() - start)); +--- +- true +... +start = os.time(); +--- +... +for i = 1, n_records do + s:delete{i} +end; +--- +... +file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.time() - start)); --- - true ... s:drop(); --- ... +file:close(); +--- +- true +... +--# setopt delimiter '' diff --git a/test/wal_off/rtree_benchmark.test.lua b/test/wal_off/rtree_benchmark.test.lua index 37dec0fda2a2b0771a2c278d1eea19acb1810418..fa072eeb7b49d1cf25041b17f544aab3f58a7fe6 100644 --- a/test/wal_off/rtree_benchmark.test.lua +++ b/test/wal_off/rtree_benchmark.test.lua @@ -1,49 +1,115 @@ +n_records = 10000 +n_iterations = 10000 +n_neighbors = 10 + +file = io.open("rtree_benchmark.res", "w") + s = box.schema.space.create('rtreebench') _ = s:create_index('primary') _ = s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) -n_records = 20000 -n_iterations = 10000 -n_neighbors = 10 +file:write(" *** 2D *** \n") +rect_width = 180 / math.pow(n_records, 1 / 2) -file = io.open("rtree_benchmark.res", "w") -start = os.clock() +start = os.time() --# setopt delimiter ';' for i = 1, n_records do s:insert{i,{180*math.random(),180*math.random()}} end; -file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.clock() - start)); +file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.time() - start)); -start = os.clock(); +start = os.time(); n = 0; for i = 1, n_iterations do - x = 180*math.random() - y = 180*math.random() - for k,v in s.index.spatial:pairs({x,y,x+1,y+1}, {iterator = 'LE'}) do + x = (180 - rect_width) * math.random() + y = (180 - rect_width) * math.random() + for k,v in s.index.spatial:pairs({x,y,x+rect_width,y+rect_width}, {iterator = 'LE'}) do n = n + 1 end end; -file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.clock() - start)); +file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.time() - start)); -start = os.clock(); +start = os.time(); n = 0 for i = 1, n_iterations do - x = 180*math.random() - y = 180*math.random() + x = 180 * math.random() + y = 180 * math.random() for k,v in pairs(s.index.spatial:select({x,y }, {limit = n_neighbors, iterator = 'NEIGHBOR'})) do n = n + 1 end end; -file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.clock() - start)); +file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.time() - start)); -start = os.clock(); +start = os.time(); for i = 1, n_records do s:delete{i} end; -file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.clock() - start)); +file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.time() - start)); + +s:drop(); + +dimension = 8; + +s = box.schema.space.create('rtreebench'); +_ = s:create_index('primary'); +_ = s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}, dimension = dimension}); + +file:write(" *** 8D *** \n") +rect_width = 180 / math.pow(n_records, 1 / dimension) + +start = os.time(); + +for i = 1, n_records do + local record = {} + for j = 1, dimension do + table.insert(record, 180*math.random()) + end + s:insert{i,record} +end; + +file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.time() - start)); + +start = os.time(); +n = 0; +for i = 1, n_iterations do + local rect = {} + for j = 1, dimension do + table.insert(rect, (180 - rect_width) * math.random()) + end + for j = 1, dimension do + table.insert(rect, rect[j] + rect_width) + end + for k,v in s.index.spatial:pairs(rect, {iterator = 'LE'}) do + n = n + 1 + end +end; +file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.time() - start)); + +start = os.time(); +n = 0 +for i = 1, 0 do + local rect = {} + for j = 1, dimension do + table.insert(rect, 180*math.random()) + end + for k,v in pairs(s.index.spatial:select(rect, {limit = n_neighbors, iterator = 'NEIGHBOR'})) do + n = n + 1 + end +end; +file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.time() - start)); + +start = os.time(); +for i = 1, n_records do + s:delete{i} +end; +file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.time() - start)); -file:close(); s:drop(); +file:close(); + +--# setopt delimiter '' + +