From e829c618889a8b4bb95b8cdb0af328c1aedd2e3e Mon Sep 17 00:00:00 2001
From: Alexandr <a.lyapunov@corp.mail.ru>
Date: Tue, 18 Nov 2014 16:26:40 +0300
Subject: [PATCH] converted rtree to plain c, moved it to salad

---
 cmake/BuildMisc.cmake             |   1 -
 src/box/rtree_index.cc            | 166 ++++--
 src/box/rtree_index.h             |   4 +-
 src/lib/salad/CMakeLists.txt      |   2 +-
 src/lib/salad/bps_tree.h          |   3 +-
 src/lib/salad/rtree.c             | 939 ++++++++++++++++++++++++++++++
 src/lib/salad/rtree.h             | 146 +++++
 test/box/rtree_benchmark.result   |   2 +-
 test/box/rtree_benchmark.test.lua |   2 +-
 test/box/rtree_rect.result        |   4 +-
 test/box/rtree_rect.test.lua      |   4 +-
 test/unit/CMakeLists.txt          |   4 +-
 test/unit/rtree.cc                | 164 +++---
 test/unit/rtree_itr.cc            | 172 +++---
 third_party/rtree.cc              | 616 --------------------
 third_party/rtree.h               | 240 --------
 16 files changed, 1401 insertions(+), 1068 deletions(-)
 create mode 100644 src/lib/salad/rtree.c
 create mode 100644 src/lib/salad/rtree.h
 delete mode 100644 third_party/rtree.cc
 delete mode 100644 third_party/rtree.h

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