diff --git a/include/avl_tree.h b/include/avl_tree.h
new file mode 100644
index 0000000000000000000000000000000000000000..268bf485a5a559080774adfbedf8e8498904dde5
--- /dev/null
+++ b/include/avl_tree.h
@@ -0,0 +1,972 @@
+/*
+ * Copyright (C) 2012 Mail.RU
+ * Copyright (C) 2010 Teodor Sigaev
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _AVLTREE_H_
+#define _AVLTREE_H_
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <third_party/qsort_arg.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+#ifndef AVLTREE_NODE_SELF
+/*
+ * user could suggest pointer's storage himself
+ */
+typedef u_int32_t avlnode_t;
+#define AVLNIL (0x7fffffff)
+#define AVLINDEXMASK (0x7fffffff)
+#define AVLFLAGMASK (0x80000000)
+#define AVLMAXDEPTH (48)
+
+typedef struct avltree_node_pointers {
+    u_int32_t    left;   /* sizeof(avlnode_t) >= sizeof(avltree_node_pointers.left) !!! */
+    u_int32_t    right;
+} avltree_node_pointers;
+
+#define GET_AVLNODE_LEFT(snp) ((snp)->left & AVLINDEXMASK)
+#define SET_AVLNODE_LEFT(snp, v) ((snp)->left = (((snp)->left & AVLFLAGMASK) | (v)))
+#define GET_AVLNODE_RIGHT(snp) ((snp)->right & AVLINDEXMASK)
+#define SET_AVLNODE_RIGHT(snp, v) ((snp)->right = (((snp)->right & AVLFLAGMASK) | (v)))
+#define GET_AVLNODE_BALANCE(snp) (((snp)->right >> 31) ? 1 : ((snp)->left >> 31) ? -1 : 0)
+#define SET_AVLNODE_BALANCE(snp, v) ((snp)->left = (((snp)->left & AVLINDEXMASK) | ((v) < 0 ? AVLFLAGMASK : 0))), ((snp)->right = (((snp)->right & AVLINDEXMASK) | ((v) > 0 ? AVLFLAGMASK : 0)))
+
+#endif /* AVLTREE_NODE_SELF */
+
+#define _GET_AVLNODE_LEFT(n) GET_AVLNODE_LEFT( t->lrpointers + (n) )
+#define _SET_AVLNODE_LEFT(n, v) SET_AVLNODE_LEFT( t->lrpointers + (n), (v) )
+#define _GET_AVLNODE_RIGHT(n) GET_AVLNODE_RIGHT( t->lrpointers + (n) )
+#define _SET_AVLNODE_RIGHT(n, v) SET_AVLNODE_RIGHT( t->lrpointers + (n), (v) )
+#define _GET_AVLNODE_BALANCE(n) GET_AVLNODE_BALANCE( t->lrpointers + (n) )
+#define _SET_AVLNODE_BALANCE(n, v) SET_AVLNODE_BALANCE( t->lrpointers + (n), (v) )
+
+#define AVLITHELEM(t, i) ( (char *) (t)->members + (t)->elemsize * (i) )
+#define AVLELEMIDX(t, e) ( ((e) - (t)->members) / (t)->elemsize )
+
+/*
+ * makes definition of tree with methods, name should
+ * be unique across all definitions.
+ *
+ * Methods:
+ *   void avltree_NAME_init(avltree_NAME *tree, size_t elemsize, void *array,
+ *                         avlnode_t array_len, avlnode_t array_size,
+ *                         int (*compare)(const void *key, const void *elem, void *arg),
+ *                         int (*elemcompare)(const void *e1, const void *e2, void *arg),
+ *                         void *arg)
+ *
+ *   void avltree_NAME_replace(avltree_NAME *tree, void *value, void **p_oldvalue)
+ *   void avltree_NAME_delete(avltree_NAME *tree, void *value)
+ *   void* avltree_NAME_find(avltree_NAME *tree, void *key)
+ *
+ *   avlnode_t avltree_NAME_walk(avltree_NAME *t, void* array, avlnode_t limit, avlnode_t offset)
+ *   void avltree_NAME_walk_cb(avltree_NAME *t, int (*cb)(void* cb_arg, void* elem), void *cb_arg)
+ *
+ *   avltree_NAME_iterator* avltree_NAME_iterator_init(avltree_NAME *t)
+ *   void avltree_NAME_iterator_init_set(avltree_NAME *t, avltree_NAME_iterator **iterator, void *start)
+ *   avltree_NAME_iterator* avltree_NAME_iterator_reverse_init(avltree_NAME *t)
+ *   void avltree_NAME_iterator_reverse_init_set(avltree_NAME *t, avltree_NAME_iterator **iterator, void *start)
+ *   void avltree_NAME_iterator_free(avltree_NAME_iterator *i)
+ *
+ *   void* avltree_NAME_iterator_next(avltree_NAME_iterator *i)
+ *   void* avltree_NAME_iterator_reverse_next(avltree_NAME_iterator *i)
+ */
+
+#define AVL_DEF(name, realloc)                                                            \
+typedef struct avl_##name {                                                               \
+    void                    *members;                                                     \
+    avltree_node_pointers    *lrpointers;                                                 \
+                                                                                          \
+    avlnode_t                nmember;                                                     \
+    avlnode_t                ntotal;                                                      \
+                                                                                          \
+    int                     (*compare)(const void *key, const void *elem, void *);        \
+    int                     (*elemcompare)(const void *e1, const void *e2, void *);       \
+    void*                   arg;                                                          \
+    size_t                  elemsize;                                                     \
+                                                                                          \
+    avlnode_t                root;                                                        \
+    avlnode_t                garbage_head;                                                \
+    avlnode_t                size;                                                        \
+    avlnode_t                max_size;                                                    \
+    avlnode_t                max_depth;                                                   \
+} avl_##name;                                                                             \
+                                                                                          \
+static avlnode_t                                                                          \
+avl_##name##_mktree(avl_##name *t, avlnode_t depth,                                       \
+		      avlnode_t start, avlnode_t end, int* height) {                      \
+    avlnode_t    half = ( (end + start) >> 1 ), tmp;                                      \
+    int lh = 0, rh = 0;                                                                   \
+                                                                                          \
+    if (depth > t->max_depth) t->max_depth = depth;                                       \
+                                                                                          \
+    if ( half == start ||                                                                 \
+            ( tmp = avl_##name##_mktree(t, depth+1, start, half, &lh)) == half )          \
+        _SET_AVLNODE_LEFT(half, AVLNIL);                                                  \
+    else                                                                                  \
+        _SET_AVLNODE_LEFT(half, tmp);                                                     \
+    if ( half+1 >= end ||                                                                 \
+            ( tmp = avl_##name##_mktree(t, depth+1, half+1, end, &rh)) == half )          \
+        _SET_AVLNODE_RIGHT(half, AVLNIL);                                                 \
+    else                                                                                  \
+        _SET_AVLNODE_RIGHT(half, tmp);                                                    \
+                                                                                          \
+    _SET_AVLNODE_BALANCE(half, rh - lh);                                                  \
+                                                                                          \
+	if(height) {                                                                      \
+		*height = (lh > rh ? lh : rh) + 1;                                        \
+	}                                                                                 \
+                                                                                          \
+    return half;                                                                          \
+}                                                                                         \
+                                                                                          \
+static inline int                                                                         \
+avl_##name##_height_of_subtree(avl_##name *t, avlnode_t node) {                           \
+    if (node == AVLNIL)                                                                   \
+	    return 0;                                                                     \
+	int l = avl_##name##_height_of_subtree(t, _GET_AVLNODE_LEFT(node));               \
+	int r = avl_##name##_height_of_subtree(t, _GET_AVLNODE_RIGHT(node));              \
+    return 1 + (l > r ? l : r);                                                           \
+}                                                                                         \
+                                                                                          \
+static inline int                                                                         \
+avl_##name##_check_subtree(avl_##name *t, avlnode_t node) {                               \
+    if (node == AVLNIL)                                                                   \
+	    return 0;                                                                     \
+	if(_GET_AVLNODE_LEFT(node) != AVLNIL) {                                           \
+		void* l = AVLITHELEM(t, _GET_AVLNODE_LEFT(node));                         \
+		void* c = AVLITHELEM(t, node);                                            \
+		if(t->elemcompare(l, c, t->arg) >= 0) {                                   \
+			return 1;                                                         \
+		}                                                                         \
+	}                                                                                 \
+	if(_GET_AVLNODE_RIGHT(node) != AVLNIL) {                                          \
+		void* r = AVLITHELEM(t, _GET_AVLNODE_RIGHT(node));                        \
+		void* c = AVLITHELEM(t, node);                                            \
+		if(t->elemcompare(c, r, t->arg) >= 0) {                                   \
+			return 2;                                                         \
+		}                                                                         \
+	}                                                                                 \
+	int lh = avl_##name##_height_of_subtree(t, _GET_AVLNODE_LEFT(node));              \
+	int rh = avl_##name##_height_of_subtree(t, _GET_AVLNODE_RIGHT(node));             \
+	if(rh - lh != _GET_AVLNODE_BALANCE(node)) {                                       \
+		return 4;                                                                 \
+	}                                                                                 \
+	avlnode_t l = avl_##name##_check_subtree(t, _GET_AVLNODE_LEFT(node));             \
+	avlnode_t r = avl_##name##_check_subtree(t, _GET_AVLNODE_RIGHT(node));            \
+    return l | r;                                                                         \
+}                                                                                         \
+                                                                                          \
+static inline int                                                                         \
+avl_##name##_init(avl_##name *t, size_t elemsize, void *m,                                \
+                     avlnode_t nm, avlnode_t nt,                                          \
+                     int (*compare)(const void *, const void *, void *),                  \
+                     int (*elemcompare)(const void *, const void *, void *),              \
+                     void *arg) {                                                         \
+    memset(t, 0, sizeof(*t));                                                             \
+    t->members = m;                                                                       \
+    t->max_size = t->size = t->nmember = nm;                                              \
+    t->ntotal = (nt==0) ? nm : nt;                                                        \
+    t->compare = compare != NULL ? compare : elemcompare;                                 \
+    t->elemcompare = elemcompare != NULL ? elemcompare : compare;                         \
+    t->arg = arg;                                                                         \
+    t->elemsize = elemsize;                                                               \
+    t->garbage_head = t->root = AVLNIL;                                                   \
+                                                                                          \
+    if (t->ntotal == 0 || t->members == NULL) { /* from scratch */                        \
+        if (t->ntotal == 0) {                                                             \
+            t->members = NULL;                                                            \
+            t->ntotal = 64;                                                               \
+        }                                                                                 \
+                                                                                          \
+        if (t->members == NULL)                                                           \
+            t->members = realloc(NULL, elemsize * t->ntotal);                             \
+    }                                                                                     \
+    t->lrpointers = (avltree_node_pointers *) realloc(NULL,                               \
+                                sizeof(avltree_node_pointers) * t->ntotal);               \
+                                                                                          \
+    if (t->nmember == 1) {                                                                \
+        t->root = 0;                                                                      \
+        _SET_AVLNODE_RIGHT(0, AVLNIL);                                                    \
+        _SET_AVLNODE_LEFT(0, AVLNIL);                                                     \
+    } else if (t->nmember > 1)    {                                                       \
+        qsort_arg(t->members, t->nmember, elemsize, t->elemcompare, t->arg);              \
+        /* create tree */                                                                 \
+        t->root = avl_##name##_mktree(t, 1, 0, t->nmember, 0);                            \
+        /*avl_##name##_check_subtree(t, t->root);*/                                       \
+    }                                                                                     \
+    if (t->members && t->lrpointers)                                                      \
+        return 0;                                                                         \
+    else if (t->members)                                                                  \
+        return t->ntotal * sizeof(avltree_node_pointers);                                 \
+    else if (t->lrpointers)                                                               \
+        return t->ntotal * elemsize;                                                      \
+    else                                                                                  \
+        return t->ntotal * (sizeof(avltree_node_pointers) + elemsize);                    \
+}                                                                                         \
+                                                                                          \
+static inline void                                                                        \
+avl_##name##_destroy(avl_##name *t) {                                                     \
+        if (t == NULL)    return;                                                         \
+    t->members = realloc(t->members, 0);                                                  \
+    t->lrpointers = (avltree_node_pointers *)realloc(t->lrpointers, 0);                   \
+}                                                                                         \
+                                                                                          \
+/** Nodes in the garbage list have a loop on their right link. */                         \
+static inline bool                                                                        \
+avl_##name##_node_is_deleted(const avl_##name *t, avlnode_t node) {                       \
+                                                                                          \
+    return _GET_AVLNODE_RIGHT(node) == node;                                              \
+}                                                                                         \
+                                                                                          \
+static inline void*                                                                       \
+avl_##name##_find(const avl_##name *t, void *k) {                                         \
+    avlnode_t    node = t->root;                                                          \
+    while(node != AVLNIL) {                                                               \
+    int r = t->compare(k, AVLITHELEM(t, node), t->arg);                                   \
+        if (r > 0) {                                                                      \
+            node = _GET_AVLNODE_RIGHT(node);                                              \
+        } else if (r < 0) {                                                               \
+            node = _GET_AVLNODE_LEFT(node);                                               \
+        } else {                                                                          \
+            return AVLITHELEM(t, node);                                                   \
+        }                                                                                 \
+    }                                                                                     \
+    return NULL;                                                                          \
+}                                                                                         \
+                                                                                          \
+static inline void*                                                                       \
+avl_##name##_first(const avl_##name *t) {                                                 \
+    avlnode_t    node = t->root;                                                          \
+    avlnode_t    first = AVLNIL;                                                          \
+    while (node != AVLNIL) {                                                              \
+            first = node;                                                                 \
+            node = _GET_AVLNODE_LEFT(node);                                               \
+    }                                                                                     \
+    if (first != AVLNIL)                                                                  \
+        return AVLITHELEM(t, first);                                                      \
+    return NULL;                                                                          \
+}                                                                                         \
+                                                                                          \
+static inline void*                                                                       \
+avl_##name##_last(const avl_##name *t) {                                                  \
+    avlnode_t    node = t->root;                                                          \
+    avlnode_t    last = AVLNIL;                                                           \
+    while (node != AVLNIL) {                                                              \
+            last = node;                                                                  \
+            node = _GET_AVLNODE_RIGHT(node);                                              \
+    }                                                                                     \
+    if (last != AVLNIL)                                                                   \
+        return AVLITHELEM(t, last);                                                       \
+    return NULL;                                                                          \
+}                                                                                         \
+                                                                                          \
+static inline void*                                                                       \
+avl_##name##_random(const avl_##name *t, avlnode_t rnd) {                                 \
+    for (avlnode_t i = 0; i < t->size; i++, rnd++) {                                      \
+        rnd %= t->nmember;                                                                \
+        if (!avl_##name##_node_is_deleted(t, rnd))                                        \
+            return AVLITHELEM(t, rnd);                                                    \
+                                                                                          \
+    }                                                                                     \
+                                                                                          \
+    return NULL;                                                                          \
+}                                                                                         \
+static inline avlnode_t                                                                   \
+avl_##name##_size_of_subtree(avl_##name *t, avlnode_t node) {                             \
+    if (node == AVLNIL)                                                                   \
+        return 0;                                                                         \
+    return 1 +                                                                            \
+        avl_##name##_size_of_subtree(t, _GET_AVLNODE_LEFT(node)) +                        \
+        avl_##name##_size_of_subtree(t, _GET_AVLNODE_RIGHT(node));                        \
+}                                                                                         \
+                                                                                          \
+static inline int                                                                         \
+avl_##name##_reserve_places(avl_##name *t, avlnode_t nreserve) {                          \
+	avlnode_t num_free = t->ntotal - t->size;                                         \
+	if (num_free >= nreserve)                                                         \
+		return 0;                                                                 \
+	avlnode_t new_ntotal = MAX(t->ntotal * 2, t->ntotal + nreserve - num_free);       \
+	void *new_members = realloc(t->members, new_ntotal * t->elemsize);                \
+	if (!new_members)                                                                 \
+		return new_ntotal * t->elemsize;                                          \
+	t->members = new_members;                                                         \
+	avltree_node_pointers *new_lrpointers = (avltree_node_pointers *)                 \
+	realloc(t->lrpointers, new_ntotal * sizeof(avltree_node_pointers));               \
+	if (!new_lrpointers)                                                              \
+		return new_ntotal * sizeof(avltree_node_pointers);                        \
+	t->lrpointers = new_lrpointers;                                                   \
+	t->ntotal = new_ntotal;                                                           \
+	return 0;                                                                         \
+}                                                                                         \
+                                                                                          \
+static inline avlnode_t                                                                   \
+avl_##name##_get_place(avl_##name *t) {                                                   \
+    avlnode_t    node;                                                                    \
+    if (t->garbage_head != AVLNIL) {                                                      \
+        node = t->garbage_head;                                                           \
+        t->garbage_head = _GET_AVLNODE_LEFT(t->garbage_head);                             \
+    } else {                                                                              \
+        if (t->nmember >= t->ntotal) {                                                    \
+            avlnode_t new_ntotal = t->ntotal * 2;                                         \
+            t->members = realloc(t->members, new_ntotal * t->elemsize);                   \
+            t->lrpointers = (avltree_node_pointers *) realloc(t->lrpointers,              \
+                            new_ntotal * sizeof(avltree_node_pointers));                  \
+            t->ntotal = new_ntotal;                                                       \
+        }                                                                                 \
+                                                                                          \
+        node = t->nmember;                                                                \
+        t->nmember++;                                                                     \
+    }                                                                                     \
+    _SET_AVLNODE_LEFT(node, AVLNIL);                                                      \
+    _SET_AVLNODE_RIGHT(node, AVLNIL);                                                     \
+    _SET_AVLNODE_BALANCE(node, 0);                                                        \
+    return node;                                                                          \
+}                                                                                         \
+                                                                                          \
+static inline bool                                                                        \
+avl_##name##_rotate_left(avl_##name *t, avlnode_t parent, avlnode_t *new_parent) {        \
+	avlnode_t node = _GET_AVLNODE_RIGHT(parent);                                      \
+	if(_GET_AVLNODE_BALANCE(node) > 0) {                                              \
+		_SET_AVLNODE_BALANCE(parent, 0);                                          \
+		_SET_AVLNODE_BALANCE(node, 0);                                            \
+		_SET_AVLNODE_RIGHT(parent, _GET_AVLNODE_LEFT(node));                      \
+		_SET_AVLNODE_LEFT(node, parent);                                          \
+		*new_parent = node;                                                       \
+		return true;                                                              \
+	} else if(_GET_AVLNODE_BALANCE(node) == 0) {                                      \
+		_SET_AVLNODE_BALANCE(parent, 1);                                          \
+		_SET_AVLNODE_BALANCE(node, -1);                                           \
+		_SET_AVLNODE_RIGHT(parent, _GET_AVLNODE_LEFT(node));                      \
+		_SET_AVLNODE_LEFT(node, parent);                                          \
+		*new_parent = node;                                                       \
+		return false;                                                             \
+	} else {                                                                          \
+		avlnode_t l = _GET_AVLNODE_LEFT(node);                                    \
+		avlnode_t ll = _GET_AVLNODE_LEFT(l);                                      \
+		avlnode_t lr = _GET_AVLNODE_RIGHT(l);                                     \
+		int l_balance = _GET_AVLNODE_BALANCE(l);                                  \
+		_SET_AVLNODE_BALANCE(l, 0);                                               \
+		_SET_AVLNODE_BALANCE(node, l_balance < 0 ? 1 : 0);                        \
+		_SET_AVLNODE_BALANCE(parent, l_balance > 0 ? -1 : 0);                     \
+		_SET_AVLNODE_RIGHT(parent, ll);                                           \
+		_SET_AVLNODE_LEFT(node, lr);                                              \
+		_SET_AVLNODE_LEFT(l, parent);                                             \
+		_SET_AVLNODE_RIGHT(l, node);                                              \
+		*new_parent = l;                                                          \
+		return true;                                                              \
+	}                                                                                 \
+}                                                                                         \
+                                                                                          \
+static inline bool                                                                        \
+avl_##name##_rotate_right(avl_##name *t, avlnode_t parent, avlnode_t *new_parent) {       \
+	avlnode_t node = _GET_AVLNODE_LEFT(parent);                                       \
+	if(_GET_AVLNODE_BALANCE(node) < 0) {                                              \
+		_SET_AVLNODE_BALANCE(parent, 0);                                          \
+		_SET_AVLNODE_BALANCE(node, 0);                                            \
+		_SET_AVLNODE_LEFT(parent, _GET_AVLNODE_RIGHT(node));                      \
+		_SET_AVLNODE_RIGHT(node, parent);                                         \
+		*new_parent = node;                                                       \
+		return true;                                                              \
+	} else if(_GET_AVLNODE_BALANCE(node) == 0) {                                      \
+		_SET_AVLNODE_BALANCE(parent, -1);                                         \
+		_SET_AVLNODE_BALANCE(node, 1);                                            \
+		_SET_AVLNODE_LEFT(parent, _GET_AVLNODE_RIGHT(node));                      \
+		_SET_AVLNODE_RIGHT(node, parent);                                         \
+		*new_parent = node;                                                       \
+		return false;                                                             \
+	} else {                                                                          \
+		avlnode_t r = _GET_AVLNODE_RIGHT(node);                                   \
+		avlnode_t rl = _GET_AVLNODE_LEFT(r);                                      \
+		avlnode_t rr = _GET_AVLNODE_RIGHT(r);                                     \
+		int r_balance = _GET_AVLNODE_BALANCE(r);                                  \
+		_SET_AVLNODE_BALANCE(r, 0);                                               \
+		_SET_AVLNODE_BALANCE(node, r_balance > 0 ? -1 : 0);                       \
+		_SET_AVLNODE_BALANCE(parent, r_balance < 0 ? 1 : 0);                      \
+		_SET_AVLNODE_LEFT(parent, rr);                                            \
+		_SET_AVLNODE_RIGHT(node, rl);                                             \
+		_SET_AVLNODE_RIGHT(r, parent);                                            \
+		_SET_AVLNODE_LEFT(r, node);                                               \
+		*new_parent = r;                                                          \
+		return true;                                                              \
+	}                                                                                 \
+}                                                                                         \
+                                                                                          \
+static inline int                                                                         \
+avl_##name##_replace(avl_##name *t, void *v, void **p_old) {                              \
+    avlnode_t    node, depth = 0;                                                         \
+    avlnode_t    path[ AVLMAXDEPTH ];                                                     \
+                                                                                          \
+    if (t->root == AVLNIL) {                                                              \
+        _SET_AVLNODE_LEFT(0, AVLNIL);                                                     \
+        _SET_AVLNODE_RIGHT(0, AVLNIL);                                                    \
+        _SET_AVLNODE_BALANCE(0, 0);                                                       \
+        memcpy(t->members, v, t->elemsize);                                               \
+        t->root = 0;                                                                      \
+        t->garbage_head = AVLNIL;                                                         \
+        t->nmember = 1;                                                                   \
+        t->size=1;                                                                        \
+        if (p_old)                                                                        \
+            *p_old = NULL;                                                                \
+        return 0;                                                                         \
+    } else {                                                                              \
+        avlnode_t    parent = t->root;                                                    \
+                                                                                          \
+        for(;;)    {                                                                      \
+            int r = t->elemcompare(v, AVLITHELEM(t, parent), t->arg);                     \
+            if (r==0) {                                                                   \
+                if (p_old)                                                                \
+                    memcpy(*p_old, AVLITHELEM(t, parent), t->elemsize);                   \
+                memcpy(AVLITHELEM(t, parent), v, t->elemsize);                            \
+                return 0;                                                                 \
+            }                                                                             \
+            path[depth] = parent;                                                         \
+            depth++;                                                                      \
+            if (r>0) {                                                                    \
+                if (_GET_AVLNODE_RIGHT(parent) == AVLNIL) {                               \
+                    int reserve_result = avl_##name##_reserve_places(t, 1);               \
+                    if (reserve_result)                                                   \
+                        return reserve_result;                                            \
+                    node = avl_##name##_get_place(t);                                     \
+                    memcpy(AVLITHELEM(t, node), v, t->elemsize);                          \
+                    _SET_AVLNODE_RIGHT(parent, node);                                     \
+                    break;                                                                \
+                } else {                                                                  \
+                    parent = _GET_AVLNODE_RIGHT(parent);                                  \
+                }                                                                         \
+            } else {                                                                      \
+                if (_GET_AVLNODE_LEFT(parent) == AVLNIL) {                                \
+                    int reserve_result = avl_##name##_reserve_places(t, 1);               \
+                    if (reserve_result)                                                   \
+                        return reserve_result;                                            \
+                    node = avl_##name##_get_place(t);                                     \
+                    memcpy(AVLITHELEM(t, node), v, t->elemsize);                          \
+                    _SET_AVLNODE_LEFT(parent, node);                                      \
+                    break;                                                                \
+                } else {                                                                  \
+                    parent = _GET_AVLNODE_LEFT(parent);                                   \
+                }                                                                         \
+            }                                                                             \
+        }                                                                                 \
+    }                                                                                     \
+    if (p_old)                                                                            \
+        *p_old = NULL;                                                                    \
+                                                                                          \
+    t->size++;                                                                            \
+    if ( t->size > t->max_size )                                                          \
+        t->max_size = t->size;                                                            \
+    if ( depth > t->max_depth )                                                           \
+        t->max_depth = depth;                                                             \
+                                                                                          \
+	path[depth] = node;                                                               \
+    while(depth > 0) {                                                                    \
+        avlnode_t node = path[depth];                                                     \
+        avlnode_t parent = path[depth - 1];                                               \
+        if(_GET_AVLNODE_RIGHT(parent) == node) {                                          \
+        	if(_GET_AVLNODE_BALANCE(parent) < 0) {                                    \
+        		_SET_AVLNODE_BALANCE(parent, 0);                                  \
+        		break;                                                            \
+        	} else if(_GET_AVLNODE_BALANCE(parent) == 0) {                            \
+        		_SET_AVLNODE_BALANCE(parent, 1);                                  \
+        	} else {                                                                  \
+        		bool should_break =                                               \
+        			avl_##name##_rotate_left(t, parent, path + depth - 1);    \
+        		if(depth > 1) {                                                   \
+        			if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) {          \
+        				_SET_AVLNODE_LEFT(path[depth-2], path[depth-1]);  \
+        			} else {                                                  \
+        				_SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \
+        			}                                                         \
+        		}                                                                 \
+        		if(should_break) {                                                \
+        			break;                                                    \
+        		}                                                                 \
+        	}                                                                         \
+        } else {                                                                          \
+        	if(_GET_AVLNODE_BALANCE(parent) > 0) {                                    \
+        		_SET_AVLNODE_BALANCE(parent, 0);                                  \
+        		break;                                                            \
+        	} else if(_GET_AVLNODE_BALANCE(parent) == 0) {                            \
+        		_SET_AVLNODE_BALANCE(parent, -1);                                 \
+        	} else {                                                                  \
+        		bool should_break =                                               \
+        			avl_##name##_rotate_right(t, parent, path + depth - 1);   \
+        		if(depth > 1) {                                                   \
+        			if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) {          \
+        				_SET_AVLNODE_LEFT(path[depth-2], path[depth-1]);  \
+        			} else {                                                  \
+        				_SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \
+        			}                                                         \
+        		}                                                                 \
+        		if(should_break) {                                                \
+        			break;                                                    \
+        		}                                                                 \
+        	}                                                                         \
+        }                                                                                 \
+        depth--;                                                                          \
+    }                                                                                     \
+    t->root = path[0];                                                                    \
+    /*avl_##name##_check_subtree(t, t->root);*/                                           \
+    return 0;                                                                             \
+}                                                                                         \
+                                                                                          \
+static inline void                                                                        \
+avl_##name##_delete(avl_##name *t, void *k) {                                             \
+    avlnode_t    node = t->root;                                                          \
+    avlnode_t    parent = AVLNIL;                                                         \
+    avlnode_t    depth = 0;                                                               \
+    avlnode_t    path[AVLMAXDEPTH];                                                       \
+    int            lr = 0;                                                                \
+    while(node != AVLNIL) {                                                               \
+        path[depth++] = node;                                                             \
+        int r = t->elemcompare(k, AVLITHELEM(t, node), t->arg);                           \
+        if (r > 0) {                                                                      \
+            parent = node;                                                                \
+            node = _GET_AVLNODE_RIGHT(node);                                              \
+            lr = +1;                                                                      \
+        } else if (r < 0) {                                                               \
+            parent = node;                                                                \
+            node = _GET_AVLNODE_LEFT(node);                                               \
+            lr = -1;                                                                      \
+        } else {/* found */                                                               \
+            if (_GET_AVLNODE_LEFT(node) == AVLNIL && _GET_AVLNODE_RIGHT(node) == AVLNIL) {\
+                path[depth-1] = AVLNIL;                                                   \
+                if ( parent == AVLNIL )                                                   \
+                    t->root = AVLNIL;                                                     \
+                else if (lr <0)                                                           \
+                    _SET_AVLNODE_LEFT(parent, AVLNIL);                                    \
+                else                                                                      \
+                    _SET_AVLNODE_RIGHT(parent, AVLNIL);                                   \
+            } else if (_GET_AVLNODE_LEFT(node) == AVLNIL) {                               \
+                avlnode_t    child = _GET_AVLNODE_RIGHT(node);                            \
+                path[depth-1] = child;                                                    \
+                if (parent == AVLNIL) t->root = child;                                    \
+                else if (lr <0) _SET_AVLNODE_LEFT(parent, child);                         \
+                else _SET_AVLNODE_RIGHT(parent, child);                                   \
+            } else if (_GET_AVLNODE_RIGHT(node) == AVLNIL) {                              \
+                avlnode_t    child = _GET_AVLNODE_LEFT(node);                             \
+                path[depth-1] = child;                                                    \
+                if (parent == AVLNIL) t->root = child;                                    \
+                else if (lr <0) _SET_AVLNODE_LEFT(parent, child);                         \
+                else _SET_AVLNODE_RIGHT(parent, child);                                   \
+            } else {                                                                      \
+                avlnode_t todel;                                                          \
+                if (_GET_AVLNODE_BALANCE(node) >= 0) {                                    \
+                    todel = _GET_AVLNODE_RIGHT(node);                                     \
+            	    path[depth++] = todel;                                                \
+                    parent = AVLNIL;                                                      \
+                    lr = 1;                                                               \
+                    for(;;) {                                                             \
+                        if ( _GET_AVLNODE_LEFT(todel) != AVLNIL ) {                       \
+                            parent = todel;                                               \
+                            todel = _GET_AVLNODE_LEFT(todel);                             \
+                    	    path[depth++] = todel;                                        \
+                            lr = -1;                                                      \
+                        } else                                                            \
+                            break;                                                        \
+                    }                                                                     \
+                    memcpy(AVLITHELEM(t, node), AVLITHELEM(t, todel), t->elemsize);       \
+                    if (parent != AVLNIL)                                                 \
+                        _SET_AVLNODE_LEFT(parent, _GET_AVLNODE_RIGHT(todel));             \
+                    else                                                                  \
+                        _SET_AVLNODE_RIGHT(node, _GET_AVLNODE_RIGHT(todel));              \
+                } else {                                                                  \
+                    todel = _GET_AVLNODE_LEFT(node);                                      \
+            	    path[depth++] = todel;                                                \
+                    parent = AVLNIL;                                                      \
+                    lr = -1;                                                              \
+                    for(;;) {                                                             \
+                        if ( _GET_AVLNODE_RIGHT(todel) != AVLNIL ) {                      \
+                            parent = todel;                                               \
+                            todel = _GET_AVLNODE_RIGHT(todel);                            \
+                    	    path[depth++] = todel;                                        \
+                            lr = 1;                                                       \
+                        } else                                                            \
+                            break;                                                        \
+                    }                                                                     \
+                    memcpy(AVLITHELEM(t, node), AVLITHELEM(t, todel), t->elemsize);       \
+                    if (parent != AVLNIL)                                                 \
+                        _SET_AVLNODE_RIGHT(parent, _GET_AVLNODE_LEFT(todel));             \
+                    else                                                                  \
+                        _SET_AVLNODE_LEFT(node, _GET_AVLNODE_LEFT(todel));                \
+                }                                                                         \
+                node = todel; /* node to delete */                                        \
+            }                                                                             \
+                                                                                          \
+            _SET_AVLNODE_LEFT(node, t->garbage_head);                                     \
+            /*                                                                            \
+             * Loop back on the right link indicates that the node                        \
+             * is in the garbage list.                                                    \
+             */                                                                           \
+            _SET_AVLNODE_RIGHT(node, node);                                               \
+            t->garbage_head = node;                                                       \
+                                                                                          \
+            break;                                                                        \
+        }                                                                                 \
+    }                                                                                     \
+                                                                                          \
+    if (node == AVLNIL) /* not found */                                                   \
+        return;                                                                           \
+                                                                                          \
+    t->size --;                                                                           \
+                                                                                          \
+    depth--;                                                                              \
+    while(depth > 0) {                                                                    \
+        avlnode_t node = path[depth];                                                     \
+        avlnode_t parent = path[depth - 1];                                               \
+        if(lr == 1 || (lr == 0 && _GET_AVLNODE_RIGHT(parent) == node)) {                  \
+        	if(_GET_AVLNODE_BALANCE(parent) == 0) {                                   \
+        		_SET_AVLNODE_BALANCE(parent, -1);                                 \
+        		break;                                                            \
+        	} else if(_GET_AVLNODE_BALANCE(parent) == 1) {                            \
+        		_SET_AVLNODE_BALANCE(parent, 0);                                  \
+        	} else {                                                                  \
+        		bool should_break =                                               \
+        			!avl_##name##_rotate_right(t, parent, path + depth - 1);  \
+        		if(depth > 1) {                                                   \
+        			if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) {          \
+        				_SET_AVLNODE_LEFT(path[depth-2], path[depth-1]);  \
+        			} else {                                                  \
+        				_SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \
+        			}                                                         \
+        		}                                                                 \
+        		if(should_break) {                                                \
+        			break;                                                    \
+        		}                                                                 \
+        	}                                                                         \
+        } else {                                                                          \
+        	if(_GET_AVLNODE_BALANCE(parent) == 0) {                                   \
+        		_SET_AVLNODE_BALANCE(parent, 1);                                  \
+        		break;                                                            \
+        	} else if(_GET_AVLNODE_BALANCE(parent) == -1) {                           \
+        		_SET_AVLNODE_BALANCE(parent, 0);                                  \
+        	} else {                                                                  \
+        		bool should_break =                                               \
+        			!avl_##name##_rotate_left(t, parent, path + depth - 1);   \
+        		if(depth > 1) {                                                   \
+        			if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) {          \
+        				_SET_AVLNODE_LEFT(path[depth-2], path[depth-1]);  \
+        			} else {                                                  \
+        				_SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \
+        			}                                                         \
+        		}                                                                 \
+        		if(should_break) {                                                \
+        			break;                                                    \
+        		}                                                                 \
+        	}                                                                         \
+        }                                                                                 \
+        lr = 0;                                                                           \
+        depth--;                                                                          \
+    }                                                                                     \
+    t->root = path[0];                                                                    \
+    /*avl_##name##_check_subtree(t, t->root);*/                                           \
+                                                                                          \
+}                                                                                         \
+                                                                                          \
+static inline avlnode_t                                                                   \
+avl_##name##_walk(avl_##name *t, void* array, avlnode_t limit, avlnode_t offset) {        \
+    int         level = 0;                                                                \
+    avlnode_t    count= 0,                                                                \
+                node,                                                                     \
+                stack[ t->max_depth + 1 ];                                                \
+                                                                                          \
+    if (t->root == AVLNIL) return 0;                                                      \
+    stack[0] = t->root;                                                                   \
+                                                                                          \
+    while( (node = _GET_AVLNODE_LEFT( stack[level] )) != AVLNIL ) {                       \
+        level++;                                                                          \
+        stack[level] = node;                                                              \
+    }                                                                                     \
+                                                                                          \
+    while( count < offset + limit && level >= 0 ) {                                       \
+                                                                                          \
+        if (count >= offset)                                                              \
+             memcpy((char *) array + (count-offset) * t->elemsize,                        \
+                    AVLITHELEM(t, stack[level]), t->elemsize);                            \
+        count++;                                                                          \
+                                                                                          \
+        node = _GET_AVLNODE_RIGHT( stack[level] );                                        \
+        level--;                                                                          \
+        while( node != AVLNIL ) {                                                         \
+            level++;                                                                      \
+            stack[level] = node;                                                          \
+            node = _GET_AVLNODE_LEFT( stack[level] );                                     \
+        }                                                                                 \
+    }                                                                                     \
+                                                                                          \
+    return (count > offset) ? count - offset : 0;                                         \
+}                                                                                         \
+                                                                                          \
+static inline void                                                                        \
+avl_##name##_walk_cb(avl_##name *t, int (*cb)(void*, void*), void *cb_arg ) {             \
+    int         level = 0;                                                                \
+    avlnode_t    node,                                                                    \
+                stack[ t->max_depth + 1 ];                                                \
+                                                                                          \
+    if (t->root == AVLNIL) return;                                                        \
+    stack[0] = t->root;                                                                   \
+                                                                                          \
+    while( (node = _GET_AVLNODE_LEFT( stack[level] )) != AVLNIL ) {                       \
+        level++;                                                                          \
+        stack[level] = node;                                                              \
+    }                                                                                     \
+                                                                                          \
+    while( level >= 0 ) {                                                                 \
+        if ( cb(cb_arg, AVLITHELEM(t, stack[level])) == 0 )                               \
+             return;                                                                      \
+                                                                                          \
+        node = _GET_AVLNODE_RIGHT( stack[level] );                                        \
+        level--;                                                                          \
+        while( node != AVLNIL ) {                                                         \
+            level++;                                                                      \
+            stack[level] = node;                                                          \
+            node = _GET_AVLNODE_LEFT( stack[level] );                                     \
+        }                                                                                 \
+    }                                                                                     \
+}                                                                                         \
+                                                                                          \
+typedef struct avl_##name##_iterator {                                                    \
+    const avl_##name        *t;                                                           \
+    int                  level;                                                           \
+    int                  max_depth;                                                       \
+    avlnode_t             stack[0];                                                       \
+} avl_##name##_iterator;                                                                  \
+                                                                                          \
+static inline avl_##name##_iterator *                                                     \
+avl_##name##_iterator_alloc(avl_##name *t) {                                              \
+    avl_##name##_iterator *i = (avl_##name##_iterator *)                                  \
+        realloc(NULL, sizeof(*i) + sizeof(avlnode_t) * (t->max_depth + 1));               \
+    if (i) {                                                                              \
+        i->t = t;                                                                         \
+        i->level = 0;                                                                     \
+        i->stack[0] = t->root;                                                            \
+    }                                                                                     \
+    return i;                                                                             \
+}                                                                                         \
+                                                                                          \
+static inline avl_##name##_iterator *                                                     \
+avl_##name##_iterator_init(avl_##name *t) {                                               \
+    avl_##name##_iterator *i;                                                             \
+    avlnode_t node;                                                                       \
+                                                                                          \
+    if (t->root == AVLNIL) return NULL;                                                   \
+    i = avl_##name##_iterator_alloc(t);                                                   \
+    if (!i)                                                                               \
+        return i;                                                                         \
+                                                                                          \
+    while( (node = _GET_AVLNODE_LEFT( i->stack[i->level] )) != AVLNIL ) {                 \
+        i->level++;                                                                       \
+        i->stack[i->level] = node;                                                        \
+    }                                                                                     \
+                                                                                          \
+    return i;                                                                             \
+}                                                                                         \
+                                                                                          \
+static inline int                                                                         \
+avl_##name##_iterator_init_set(const avl_##name *t, avl_##name##_iterator **i,            \
+                                  void *k) {                                              \
+    avlnode_t node;                                                                       \
+    int      lastLevelEq = -1, cmp;                                                       \
+                                                                                          \
+    if ((*i) == NULL || t->max_depth > (*i)->max_depth) {                                 \
+        avl_##name##_iterator *new_i;                                                     \
+        new_i = (avl_##name##_iterator *) realloc(*i, sizeof(**i) +                       \
+			               sizeof(avlnode_t) * (t->max_depth + 31));          \
+        if (!new_i)                                                                       \
+	    return sizeof(**i) + sizeof(avlnode_t) * (t->max_depth + 31);                 \
+        *i = new_i;                                                                       \
+    }                                                                                     \
+                                                                                          \
+    (*i)->t = t;                                                                          \
+    (*i)->level = -1;                                                                     \
+    if (t->root == AVLNIL) {                                                              \
+            (*i)->max_depth = 0; /* valgrind points out it's used in the check above ^.*/ \
+            return 0;                                                                     \
+    }                                                                                     \
+                                                                                          \
+    (*i)->max_depth = t->max_depth;                                                       \
+    (*i)->stack[0] = t->root;                                                             \
+                                                                                          \
+    node = t->root;                                                                       \
+    while(node != AVLNIL) {                                                               \
+        cmp = t->compare(k, AVLITHELEM(t, node), t->arg);                                 \
+                                                                                          \
+        (*i)->level++;                                                                    \
+        (*i)->stack[(*i)->level] = node;                                                  \
+                                                                                          \
+        if (cmp > 0) {                                                                    \
+            (*i)->level--; /* exclude current node from path, ie "mark as visited" */     \
+            node = _GET_AVLNODE_RIGHT(node);                                              \
+        } else if (cmp < 0) {                                                             \
+            node = _GET_AVLNODE_LEFT(node);                                               \
+        } else {                                                                          \
+            lastLevelEq = (*i)->level;                                                    \
+            node = _GET_AVLNODE_LEFT(node); /* one way iterator: from left to right */    \
+        }                                                                                 \
+    }                                                                                     \
+                                                                                          \
+    if (lastLevelEq >= 0)                                                                 \
+        (*i)->level = lastLevelEq;                                                        \
+    return 0;                                                                             \
+}                                                                                         \
+                                                                                          \
+static inline avl_##name##_iterator *                                                     \
+avl_##name##_iterator_reverse_init(avl_##name *t) {                                       \
+    avl_##name##_iterator *i;                                                             \
+    avlnode_t node;                                                                       \
+                                                                                          \
+    if (t->root == AVLNIL) return NULL;                                                   \
+    i = avl_##name##_iterator_alloc(t);                                                   \
+    if (!i)                                                                               \
+        return i;                                                                         \
+                                                                                          \
+    while( (node = _GET_AVLNODE_RIGHT( i->stack[i->level] )) != AVLNIL ) {                \
+        i->level++;                                                                       \
+        i->stack[i->level] = node;                                                        \
+    }                                                                                     \
+                                                                                          \
+    return i;                                                                             \
+}                                                                                         \
+                                                                                          \
+static inline int                                                                         \
+avl_##name##_iterator_reverse_init_set(const avl_##name *t,                               \
+                                          avl_##name##_iterator **i, void *k) {           \
+    avlnode_t node;                                                                       \
+    int      lastLevelEq = -1, cmp;                                                       \
+                                                                                          \
+    if ((*i) == NULL || t->max_depth > (*i)->max_depth) {                                 \
+        avl_##name##_iterator *new_i;                                                     \
+        new_i = (avl_##name##_iterator *) realloc(*i, sizeof(**i) +                       \
+				       sizeof(avlnode_t) * (t->max_depth + 31));          \
+        if (!new_i)                                                                       \
+	    return sizeof(**i) + sizeof(avlnode_t) * (t->max_depth + 31);                 \
+        *i = new_i;                                                                       \
+    }                                                                                     \
+                                                                                          \
+    (*i)->t = t;                                                                          \
+    (*i)->level = -1;                                                                     \
+    if (t->root == AVLNIL) {                                                              \
+            (*i)->max_depth = 0;                                                          \
+            return 0;                                                                     \
+    }                                                                                     \
+                                                                                          \
+    (*i)->max_depth = t->max_depth;                                                       \
+    (*i)->stack[0] = t->root;                                                             \
+                                                                                          \
+    node = t->root;                                                                       \
+    while(node != AVLNIL) {                                                               \
+        cmp = t->compare(k, AVLITHELEM(t, node), t->arg);                                 \
+                                                                                          \
+        (*i)->level++;                                                                    \
+        (*i)->stack[(*i)->level] = node;                                                  \
+                                                                                          \
+        if (cmp < 0) {                                                                    \
+            (*i)->level--;                                                                \
+            node = _GET_AVLNODE_LEFT(node);                                               \
+        } else if (cmp > 0) {                                                             \
+            node = _GET_AVLNODE_RIGHT(node);                                              \
+        } else {                                                                          \
+            lastLevelEq = (*i)->level;                                                    \
+            node = _GET_AVLNODE_RIGHT(node);                                              \
+        }                                                                                 \
+    }                                                                                     \
+                                                                                          \
+    if (lastLevelEq >= 0)                                                                 \
+        (*i)->level = lastLevelEq;                                                        \
+    return 0;                                                                             \
+}                                                                                         \
+                                                                                          \
+static inline void                                                                        \
+avl_##name##_iterator_free(avl_##name##_iterator *i) {                                    \
+    if (i == NULL)    return;                                                             \
+    i = (avl_##name##_iterator *)realloc(i, 0);                                           \
+}                                                                                         \
+                                                                                          \
+/**                                                                                       \
+ * Get the last node on the iterator stack, check                                         \
+ * if the node is not deleted.                                                            \
+ */                                                                                       \
+static inline avlnode_t                                                                   \
+avl_##name##_iterator_next_node(avl_##name##_iterator *i) {                               \
+                                                                                          \
+    while (i->level >= 0) {                                                               \
+        avlnode_t return_node = i->stack[i->level--];                                     \
+        if (! avl_##name##_node_is_deleted(i->t, return_node))                            \
+            return return_node;                                                           \
+    }                                                                                     \
+    return AVLNIL;                                                                        \
+}                                                                                         \
+                                                                                          \
+static inline void*                                                                       \
+avl_##name##_iterator_next(avl_##name##_iterator *i) {                                    \
+                                                                                          \
+    if (i == NULL)  return NULL;                                                          \
+                                                                                          \
+    const avl_##name *t = i->t;                                                           \
+    avlnode_t returnNode = avl_##name##_iterator_next_node(i);                            \
+                                                                                          \
+    if (returnNode == AVLNIL) return NULL;                                                \
+                                                                                          \
+    avlnode_t node = _GET_AVLNODE_RIGHT(returnNode);                                      \
+    while (node != AVLNIL) {                                                              \
+        i->level++;                                                                       \
+        i->stack[i->level] = node;                                                        \
+        node = _GET_AVLNODE_LEFT(i->stack[i->level]);                                     \
+    }                                                                                     \
+                                                                                          \
+    return AVLITHELEM(t, returnNode);                                                     \
+}                                                                                         \
+                                                                                          \
+static inline void*                                                                       \
+avl_##name##_iterator_reverse_next(avl_##name##_iterator *i) {                            \
+                                                                                          \
+    if (i == NULL)  return NULL;                                                          \
+                                                                                          \
+    const avl_##name *t = i->t;                                                           \
+    avlnode_t returnNode = avl_##name##_iterator_next_node(i);                            \
+                                                                                          \
+    if (returnNode == AVLNIL) return NULL;                                                \
+                                                                                          \
+    avlnode_t node = _GET_AVLNODE_LEFT(returnNode);                                       \
+    while (node != AVLNIL) {                                                              \
+        i->level++;                                                                       \
+        i->stack[i->level] = node;                                                        \
+        node = _GET_AVLNODE_RIGHT(i->stack[i->level]);                                    \
+    }                                                                                     \
+    return AVLITHELEM(t, returnNode);                                                     \
+}
+/*
+ * vim: ts=4 sts=4 et
+ */
+#endif
+
+#if defined(__cplusplus)
+}
+#endif /* defined(__cplusplus) */
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 3df5f3144828eb5c131872b6036b960112d847e3..839df7bbdb8a67d22a483b374410ed0b20939b62 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -23,6 +23,7 @@ tarantool_module("box"
     index.cc
     hash_index.cc
     tree_index.cc
+    avl_tree_index.cc
     bitset_index.cc
     space.cc
     port.cc
diff --git a/src/box/avl_tree_index.cc b/src/box/avl_tree_index.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f00965844884046436144663ab346a66633c7050
--- /dev/null
+++ b/src/box/avl_tree_index.cc
@@ -0,0 +1,494 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "avl_tree_index.h"
+#include "tuple.h"
+#include "space.h"
+#include "exception.h"
+#include "errinj.h"
+
+/* {{{ Utilities. *************************************************/
+
+struct avl_index_node {
+	struct tuple *tuple;
+};
+
+struct avl_index_key_data
+{
+	const char *key;
+	uint32_t part_count;
+};
+
+static inline struct tuple *
+avl_index_unfold(const void *node)
+{
+	if (node == NULL)
+		return NULL;
+
+	struct avl_index_node *node_x = (struct avl_index_node *) node;
+	assert (node_x->tuple != NULL);
+	return node_x->tuple;
+}
+
+static inline void
+avl_index_fold(void *node, struct tuple *tuple)
+{
+	assert (node != NULL);
+	assert (tuple != NULL);
+
+	struct avl_index_node *node_x = (struct avl_index_node *) node;
+	node_x->tuple = tuple;
+}
+
+static int
+avl_index_node_compare(const void *node_a, const void *node_b, void *arg)
+{
+	AvlTreeIndex *self = (AvlTreeIndex *) arg;
+	struct tuple *tuple_a = avl_index_unfold(node_a);
+	struct tuple *tuple_b = avl_index_unfold(node_b);
+
+	return tuple_compare(tuple_a, tuple_b, self->key_def);
+}
+
+static int
+avl_index_node_compare_dup(const void *node_a, const void *node_b, void *arg)
+{
+	AvlTreeIndex *self = (AvlTreeIndex *) arg;
+	struct tuple *tuple_a = avl_index_unfold(node_a);
+	struct tuple *tuple_b = avl_index_unfold(node_b);
+
+	return tuple_compare_dup(tuple_a, tuple_b, self->key_def);
+}
+
+static int
+avl_index_node_compare_with_key(const void *key, const void *node, void *arg)
+{
+	AvlTreeIndex *self = (AvlTreeIndex *) arg;
+	struct avl_index_key_data *key_data =
+			(struct avl_index_key_data *) key;
+	struct tuple *tuple = avl_index_unfold(node);
+
+	/* the result is inverted because arguments are swapped */
+	return -tuple_compare_with_key(tuple, key_data->key,
+				       key_data->part_count, self->key_def);
+}
+
+#ifndef NDEBUG
+void *
+realloc_avl_inject(void *ptr, size_t size)
+{
+	if (size)
+		ERROR_INJECT(ERRINJ_TREE_ALLOC, return 0);
+	return realloc(ptr, size);
+}
+#endif
+
+/* {{{ AvlTreeIndex Iterators ****************************************/
+
+struct tree_iterator {
+	struct iterator base;
+	const AvlTreeIndex *index;
+	struct avl_index_iterator *iter;
+	struct avl_index_key_data key_data;
+};
+
+static void
+tree_iterator_free(struct iterator *iterator);
+
+static inline struct tree_iterator *
+tree_iterator(struct iterator *it)
+{
+	assert(it->free == tree_iterator_free);
+	return (struct tree_iterator *) it;
+}
+
+static void
+tree_iterator_free(struct iterator *iterator)
+{
+	struct tree_iterator *it = tree_iterator(iterator);
+	if (it->iter)
+		avl_index_iterator_free(it->iter);
+	free(it);
+}
+
+static struct tuple *
+tree_iterator_ge(struct iterator *iterator)
+{
+	struct tree_iterator *it = tree_iterator(iterator);
+	void *node = avl_index_iterator_next(it->iter);
+	return avl_index_unfold(node);
+}
+
+static struct tuple *
+tree_iterator_le(struct iterator *iterator)
+{
+	struct tree_iterator *it = tree_iterator(iterator);
+	void *node = avl_index_iterator_reverse_next(it->iter);
+	return avl_index_unfold(node);
+}
+
+static struct tuple *
+tree_iterator_eq(struct iterator *iterator)
+{
+	struct tree_iterator *it = tree_iterator(iterator);
+
+	void *node = avl_index_iterator_next(it->iter);
+	if (node && it->index->tree.compare(&it->key_data, node,
+					    (void *) it->index) == 0)
+		return avl_index_unfold(node);
+
+	return NULL;
+}
+
+static struct tuple *
+tree_iterator_req(struct iterator *iterator)
+{
+	struct tree_iterator *it = tree_iterator(iterator);
+
+	void *node = avl_index_iterator_reverse_next(it->iter);
+	if (node != NULL
+	    && it->index->tree.compare(&it->key_data, node,
+				       (void *) it->index) == 0) {
+		return avl_index_unfold(node);
+	}
+
+	return NULL;
+}
+
+static struct tuple *
+tree_iterator_lt(struct iterator *iterator)
+{
+	struct tree_iterator *it = tree_iterator(iterator);
+
+	void *node ;
+	while ((node = avl_index_iterator_reverse_next(it->iter)) != NULL) {
+		if (it->index->tree.compare(&it->key_data, node,
+					    (void *) it->index) != 0) {
+			it->base.next = tree_iterator_le;
+			return avl_index_unfold(node);
+		}
+	}
+
+	return NULL;
+}
+
+static struct tuple *
+tree_iterator_gt(struct iterator *iterator)
+{
+	struct tree_iterator *it = tree_iterator(iterator);
+
+	void *node;
+	while ((node = avl_index_iterator_next(it->iter)) != NULL) {
+		if (it->index->tree.compare(&it->key_data, node,
+					    (void *) it->index) != 0) {
+			it->base.next = tree_iterator_ge;
+			return avl_index_unfold(node);
+		}
+	}
+
+	return NULL;
+}
+
+/* }}} */
+
+/* {{{ AvlTreeIndex  **********************************************************/
+
+AvlTreeIndex::AvlTreeIndex(struct key_def *key_def, struct space *space)
+	: Index(key_def, space)
+{
+	memset(&tree, 0, sizeof tree);
+}
+
+AvlTreeIndex::~AvlTreeIndex()
+{
+	avl_index_destroy(&tree);
+}
+
+size_t
+AvlTreeIndex::size() const
+{
+	return tree.size;
+}
+
+size_t
+AvlTreeIndex::memsize() const
+{
+        return tree.size * (8 + sizeof(struct avl_index_node));
+}
+
+struct tuple *
+AvlTreeIndex::min() const
+{
+	void *node = avl_index_first(&tree);
+	return avl_index_unfold(node);
+}
+
+struct tuple *
+AvlTreeIndex::max() const
+{
+	void *node = avl_index_last(&tree);
+	return avl_index_unfold(node);
+}
+
+struct tuple *
+AvlTreeIndex::random(uint32_t rnd) const
+{
+	void *node = avl_index_random(&tree, rnd);
+	return avl_index_unfold(node);
+}
+
+struct tuple *
+AvlTreeIndex::findByKey(const char *key, uint32_t part_count) const
+{
+	assert(key_def->is_unique && part_count == key_def->part_count);
+
+	struct avl_index_key_data key_data;
+	key_data.key = key;
+	key_data.part_count = part_count;
+	void *node = avl_index_find(&tree, &key_data);
+	return avl_index_unfold(node);
+}
+
+struct tuple *
+AvlTreeIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple,
+		      enum dup_replace_mode mode)
+{
+	struct avl_index_node new_node;
+	struct avl_index_node old_node;
+	uint32_t errcode;
+
+	if (new_tuple) {
+		struct avl_index_node *p_dup_node = &old_node;
+		avl_index_fold(&new_node, new_tuple);
+
+		/* Try to optimistically replace the new_tuple. */
+		int tree_res = avl_index_replace(&tree, &new_node,
+						 (void **) &p_dup_node);
+
+		if (tree_res) {
+			tnt_raise(ClientError, ER_MEMORY_ISSUE, tree_res,
+				  "AvlTreeIndex", "replace");
+		}
+
+		struct tuple *dup_tuple = avl_index_unfold(p_dup_node);
+		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
+
+		if (errcode) {
+			avl_index_delete(&tree, &new_node);
+			if (p_dup_node != NULL)
+				avl_index_replace(&tree, p_dup_node, NULL);
+			tnt_raise(ClientError, errcode, index_n(this));
+		}
+		if (dup_tuple)
+			return dup_tuple;
+	}
+	if (old_tuple) {
+		avl_index_fold(&old_node, old_tuple);
+		avl_index_delete(&tree, &old_node);
+	}
+	return old_tuple;
+}
+
+struct iterator *
+AvlTreeIndex::allocIterator() const
+{
+	struct tree_iterator *it = (struct tree_iterator *)
+			calloc(1, sizeof(*it));
+	if (it == NULL) {
+		tnt_raise(ClientError, ER_MEMORY_ISSUE,
+			  sizeof(struct tree_iterator),
+			  "AvlTreeIndex", "iterator");
+	}
+
+	it->index = this;
+	it->base.free = tree_iterator_free;
+	return (struct iterator *) it;
+}
+
+void
+AvlTreeIndex::initIterator(struct iterator *iterator, enum iterator_type type,
+			   const char *key, uint32_t part_count) const
+{
+	assert (key != NULL || part_count == 0);
+	struct tree_iterator *it = tree_iterator(iterator);
+
+	if (part_count == 0) {
+		/*
+		 * If no key is specified, downgrade equality
+		 * iterators to a full range.
+		 */
+		type = iterator_type_is_reverse(type) ? ITER_LE : ITER_GE;
+		key = NULL;
+	}
+	it->key_data.key = key;
+	it->key_data.part_count = part_count;
+
+	if (iterator_type_is_reverse(type)) {
+		int r = avl_index_iterator_reverse_init_set(&tree,
+				&it->iter, &it->key_data);
+		if (r)
+			tnt_raise(ClientError, ER_MEMORY_ISSUE,
+				  r, "AvlTreeIndex", "init iterator");
+	} else {
+		int r = avl_index_iterator_init_set(&tree,
+				&it->iter, &it->key_data);
+		if (r)
+			tnt_raise(ClientError, ER_MEMORY_ISSUE,
+				  r, "AvlTreeIndex", "init iterator");
+	}
+
+	switch (type) {
+	case ITER_EQ:
+		it->base.next = tree_iterator_eq;
+		break;
+	case ITER_REQ:
+		it->base.next = tree_iterator_req;
+		break;
+	case ITER_ALL:
+	case ITER_GE:
+		it->base.next = tree_iterator_ge;
+		break;
+	case ITER_GT:
+		it->base.next = tree_iterator_gt;
+		break;
+	case ITER_LE:
+		it->base.next = tree_iterator_le;
+		break;
+	case ITER_LT:
+		it->base.next = tree_iterator_lt;
+		break;
+	default:
+		tnt_raise(ClientError, ER_UNSUPPORTED,
+			  "Tree index", "requested iterator type");
+	}
+}
+
+void
+AvlTreeIndex::beginBuild()
+{
+	assert(index_is_primary(this));
+
+	tree.size = 0;
+	tree.max_size = 64;
+
+	size_t sz = tree.max_size * sizeof(struct avl_index_node);
+	tree.members = malloc(sz);
+	if (tree.members == NULL) {
+		panic("malloc(): failed to allocate %" PRI_SZ " bytes", sz);
+	}
+}
+
+void
+AvlTreeIndex::buildNext(struct tuple *tuple)
+{
+	if (tree.size == tree.max_size) {
+		tree.max_size = MAX(tree.max_size * 2, 64);
+
+		size_t sz = tree.max_size * sizeof(struct avl_index_node);
+		tree.members = realloc(tree.members, sz);
+		if (tree.members == NULL) {
+			panic("malloc(): failed to allocate %" PRI_SZ " bytes",
+			      sz);
+		}
+	}
+
+	struct avl_index_node *node = (struct avl_index_node *)
+			tree.members + tree.size;
+	avl_index_fold(node, tuple);
+	tree.size++;
+}
+
+void
+AvlTreeIndex::endBuild()
+{
+	assert(index_is_primary(this));
+
+	uint32_t n_tuples = tree.size;
+	uint32_t estimated_tuples = tree.max_size;
+	void *nodes = tree.members;
+
+	int tree_res =
+	avl_index_init(&tree, sizeof(struct avl_index_node),
+			  nodes, n_tuples, estimated_tuples,
+			  avl_index_node_compare_with_key,
+			  avl_index_node_compare,
+			  this);
+	if (tree_res) {
+		panic("tree_init: failed to allocate %d bytes", tree_res);
+	}
+}
+
+void
+AvlTreeIndex::build(Index *pk)
+{
+	uint32_t n_tuples = pk->size();
+	uint32_t estimated_tuples = n_tuples * 1.2;
+
+	void *nodes = NULL;
+	if (n_tuples) {
+		/*
+		 * Allocate a little extra to avoid
+		 * unnecessary realloc() when more data is
+		 * inserted.
+		*/
+		size_t sz = estimated_tuples * sizeof(struct avl_index_node);
+		nodes = malloc(sz);
+		if (nodes == NULL) {
+			panic("malloc(): failed to allocate %" PRI_SZ " bytes",
+			      sz);
+		}
+	}
+
+	struct iterator *it = pk->position();
+	pk->initIterator(it, ITER_ALL, NULL, 0);
+
+	struct tuple *tuple;
+
+	for (uint32_t i = 0; (tuple = it->next(it)) != NULL; ++i) {
+		struct avl_index_node *node = (struct avl_index_node *)
+				nodes + i;
+		avl_index_fold(node, tuple);
+	}
+
+	if (n_tuples) {
+		say_info("Sorting %" PRIu32 " keys in index %" PRIu32 "...", n_tuples,
+			 index_n(this));
+	}
+
+	/* If n_tuples == 0 then estimated_tuples = 0, elem == NULL, tree is empty */
+	int tree_res =
+	avl_index_init(&tree, sizeof(struct avl_index_node),
+			  nodes, n_tuples, estimated_tuples,
+			  avl_index_node_compare_with_key,
+			  key_def->is_unique ? avl_index_node_compare
+					     : avl_index_node_compare_dup,
+			  this);
+	if (tree_res) {
+		panic("tree_init: failed to allocate %d bytes", tree_res);
+	}
+}
diff --git a/src/box/avl_tree_index.h b/src/box/avl_tree_index.h
new file mode 100644
index 0000000000000000000000000000000000000000..c32d11174b45a0f548e9f8ae773b679f43f5d8d2
--- /dev/null
+++ b/src/box/avl_tree_index.h
@@ -0,0 +1,75 @@
+#ifndef TARANTOOL_BOX_AVL_TREE_INDEX_H_INCLUDED
+#define TARANTOOL_BOX_AVL_TREE_INDEX_H_INCLUDED
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "index.h"
+
+#include <avl_tree.h>
+
+/**
+ * Instantiate sptree definitions
+ */
+#ifdef NDEBUG
+AVL_DEF(index, realloc);
+#else
+void *
+realloc_avl_inject(void *ptr, size_t size);
+AVL_DEF(index, realloc_avl_inject);
+#endif
+
+class AvlTreeIndex: public Index {
+public:
+	AvlTreeIndex(struct key_def *key_def, struct space *space);
+	virtual ~AvlTreeIndex();
+
+	virtual void beginBuild();
+	virtual void buildNext(struct tuple *tuple);
+	virtual void endBuild();
+	virtual void build(Index *pk);
+	virtual size_t size() const;
+	virtual struct tuple *min() const;
+	virtual struct tuple *max() const;
+	virtual struct tuple *random(uint32_t rnd) const;
+	virtual struct tuple *findByKey(const char *key, uint32_t part_count) const;
+	virtual struct tuple *replace(struct tuple *old_tuple,
+				      struct tuple *new_tuple,
+				      enum dup_replace_mode mode);
+
+	virtual size_t memsize() const;
+	virtual struct iterator *allocIterator() const;
+	virtual void initIterator(struct iterator *iterator,
+				  enum iterator_type type,
+				  const char *key, uint32_t part_count) const;
+
+// protected:
+	avl_index tree;
+};
+
+#endif /* TARANTOOL_BOX_AVL_TREE_INDEX_H_INCLUDED */
diff --git a/src/box/index.cc b/src/box/index.cc
index 323e200cd9334cb9ad86f4be18a54abcde2f5153..af6518703bc5754987c8064903d1131e47677781 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -29,6 +29,7 @@
 #include "index.h"
 #include "hash_index.h"
 #include "tree_index.h"
+#include "avl_tree_index.h"
 #include "bitset_index.h"
 #include "tuple.h"
 #include "say.h"
@@ -71,7 +72,8 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key,
 		 * - ITERA_ALL iterator type, all index types
 		 * - ITER_GE iterator in HASH index (legacy)
 		 */
-		if (key_def->type == TREE || type == ITER_ALL ||
+		if (key_def->type == TREE || key_def->type == AVLTREE ||
+		    type == ITER_ALL ||
 		    (key_def->type == HASH && type == ITER_GE))
 			return;
 		/* Fall through. */
@@ -82,7 +84,8 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key,
 			  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) {
+	if (key_def->type != TREE && key_def->type != AVLTREE &&
+	    part_count < key_def->part_count) {
 		tnt_raise(ClientError, ER_EXACT_MATCH,
 			  key_def->part_count, part_count);
 	}
@@ -144,6 +147,8 @@ Index::factory(enum index_type type, struct key_def *key_def, struct space *spac
 		return new HashIndex(key_def, space);
 	case TREE:
 		return new TreeIndex(key_def, space);
+	case AVLTREE:
+		return new AvlTreeIndex(key_def, space);
 	case BITSET:
 		return new BitsetIndex(key_def, space);
 	default:
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 8cc234082736c727c7afdc99b55e3fb07703162d..75ef8c1a93bfe07168378941511835d65403ce1d 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -47,9 +47,10 @@ field_type_maxlen(enum field_type type)
 }
 
 #define INDEX_TYPE(_)                                               \
-	_(HASH,    0)       /* HASH Index  */                       \
-	_(TREE,    1)       /* TREE Index  */                       \
-	_(BITSET,  2)       /* BITSET Index  */                     \
+	_(HASH,    0)       /* HASH Index     */                    \
+	_(TREE,    1)       /* TREE Index     */                    \
+	_(AVLTREE, 2)       /* AVL TREE Index */                    \
+	_(BITSET,  3)       /* BITSET Index   */                    \
 
 ENUM(index_type, INDEX_TYPE);
 extern const char *index_type_strs[];
diff --git a/src/box/space.cc b/src/box/space.cc
index bbf8a5a7bb756ebddb79ab92b749524d8082221c..08618d6dd79c056ed61e1a99ffd64314b36267dd 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -494,6 +494,9 @@ check_spaces(struct tarantool_cfg *conf)
 			case TREE:
 				/* extra check for tree index not needed */
 				break;
+			case AVLTREE:
+				/* extra check for tree index not needed */
+				break;
 			case BITSET:
 				/* check bitset index */
 				/* bitset index must has single-field key */
diff --git a/test/big/tarantool.cfg b/test/big/tarantool.cfg
index 7726457bbc6fb376677d69f36dcd30dbea636a26..64c3d7ec910babecffa34626729e2655066bd0b8 100644
--- a/test/big/tarantool.cfg
+++ b/test/big/tarantool.cfg
@@ -402,3 +402,44 @@ space[28].index[0].type = TREE
 space[28].index[0].unique = true
 space[28].index[0].key_field[0].fieldno = 0
 space[28].index[0].key_field[0].type = NUM
+
+# Tests of AVL tree
+space[32].enabled = 1
+space[32].index[0].type = "AVLTREE"
+space[32].index[0].unique = 1
+space[32].index[0].key_field[0].fieldno = 0
+space[32].index[0].key_field[0].type = "NUM"
+
+space[33].enabled = 1
+space[33].index[0].type = "AVLTREE"
+space[33].index[0].unique = 1
+space[33].index[0].key_field[0].fieldno = 0
+space[33].index[0].key_field[0].type = "STR"
+
+space[34].enabled = true
+space[34].index[0].type = "AVLTREE"
+space[34].index[0].unique = true
+space[34].index[0].key_field[0].fieldno = 0
+space[34].index[0].key_field[0].type = "STR"
+
+space[35].enabled = true
+space[35].index[0].type = "AVLTREE"
+space[35].index[0].unique = true
+space[35].index[0].key_field[0].fieldno = 0
+space[35].index[0].key_field[0].type = "NUM"
+
+space[35].index[1].type = "AVLTREE"
+space[35].index[1].unique = true
+space[35].index[1].key_field[0].fieldno = 1
+space[35].index[1].key_field[0].type = "NUM"
+
+space[35].index[2].type = "AVLTREE"
+space[35].index[2].unique = false
+space[35].index[2].key_field[0].fieldno = 2
+space[35].index[2].key_field[0].type = "NUM"
+
+space[35].index[3].type = "AVLTREE"
+space[35].index[3].unique = true
+space[35].index[3].key_field[0].fieldno = 3
+space[35].index[3].key_field[0].type = "NUM"
+
diff --git a/test/big/tree_pk_avl.result b/test/big/tree_pk_avl.result
new file mode 100644
index 0000000000000000000000000000000000000000..33e6c5c9325a813ba1119a5e2d4b8b80c65e7dc5
--- /dev/null
+++ b/test/big/tree_pk_avl.result
@@ -0,0 +1,283 @@
+insert into t32 values (1, 'tuple')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+insert into t32 values (2, 'tuple 2')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+insert into t32 values (3, 'tuple 3')
+Insert OK, 1 row affected
+select * from t32 where k0 = 1
+Found 1 tuple:
+[1, 'tuple']
+select * from t32 where k0 = 2
+Found 1 tuple:
+[2, 'tuple 2']
+select * from t32 where k0 = 3
+Found 1 tuple:
+[3, 'tuple 3']
+delete from t32 where k0 = 1
+Delete OK, 1 row affected
+delete from t32 where k0 = 2
+Delete OK, 1 row affected
+delete from t32 where k0 = 3
+Delete OK, 1 row affected
+insert into t32 VALUES('xxxxxxx')
+An error occurred: ER_KEY_FIELD_TYPE, 'Supplied key field type does not match index type: expected NUM'
+insert into t32 VALUES('')
+An error occurred: ER_KEY_FIELD_TYPE, 'Supplied key field type does not match index type: expected NUM'
+insert into t32 VALUES('12')
+An error occurred: ER_KEY_FIELD_TYPE, 'Supplied key field type does not match index type: expected NUM'
+insert into t33 values ('identifier', 'tuple')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+insert into t33 values ('second', 'tuple 2')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+call box.select_range('33', '0', '100', 'second')
+Found 1 tuple:
+['second', 'tuple 2']
+call box.select_range('33', '0', '100', 'identifier')
+Found 2 tuples:
+['identifier', 'tuple']
+['second', 'tuple 2']
+insert into t33 values ('third', 'tuple 3')
+Insert OK, 1 row affected
+select * from t33 where k0 = 'identifier'
+Found 1 tuple:
+['identifier', 'tuple']
+select * from t33 where k0 = 'second'
+Found 1 tuple:
+['second', 'tuple 2']
+select * from t33 where k0 = 'third'
+Found 1 tuple:
+['third', 'tuple 3']
+delete from t33 where k0 = 'identifier'
+Delete OK, 1 row affected
+delete from t33 where k0 = 'second'
+Delete OK, 1 row affected
+delete from t33 where k0 = 'third'
+Delete OK, 1 row affected
+insert into t32 values (1, 'tuple')
+Insert OK, 1 row affected
+insert into t33 values (1, 'tuple')
+Insert OK, 1 row affected
+insert into t33 values (2, 'tuple')
+Insert OK, 1 row affected
+lua  function box.crossjoin(space0, space1, limit)   space0 = tonumber(space0)   space1 = tonumber(space1)   limit = tonumber(limit)   local result = {}   for k0, v0 in box.space[space0]:pairs() do     for k1, v1 in box.space[space1]:pairs() do       if limit <= 0 then         return unpack(result)       end       newtuple = {v0:unpack()}       for _, v in v1:pairs() do table.insert(newtuple, v) end       table.insert(result, newtuple)       limit = limit - 1     end   end   return unpack(result) end
+---
+...
+call box.crossjoin('33', '33', '0')
+No match
+call box.crossjoin('33', '33', '5')
+Found 4 tuples:
+[1, 'tuple', 1, 'tuple']
+[1, 'tuple', 2, 'tuple']
+[2, 'tuple', 1, 'tuple']
+[2, 'tuple', 2, 'tuple']
+call box.crossjoin('33', '33', '10000')
+Found 4 tuples:
+[1, 'tuple', 1, 'tuple']
+[1, 'tuple', 2, 'tuple']
+[2, 'tuple', 1, 'tuple']
+[2, 'tuple', 2, 'tuple']
+call box.crossjoin('33', '32', '10000')
+Found 2 tuples:
+[1, 'tuple', 1, 'tuple']
+[2, 'tuple', 1, 'tuple']
+lua box.space[33]:truncate()
+---
+...
+insert into t32 values (200, 'select me!')
+Insert OK, 1 row affected
+select * from t32 where k0 = 200
+Found 1 tuple:
+[200, 'select me!']
+select * from t32 where k0 = 199
+No match
+select * from t32 where k0 = 201
+No match
+insert into t34 values ('abcd')
+Insert OK, 1 row affected
+insert into t34 values ('abcda')
+Insert OK, 1 row affected
+insert into t34 values ('abcda_')
+Insert OK, 1 row affected
+insert into t34 values ('abcdb')
+Insert OK, 1 row affected
+insert into t34 values ('abcdb_')
+Insert OK, 1 row affected
+insert into t34 values ('abcdb__')
+Insert OK, 1 row affected
+insert into t34 values ('abcdb___')
+Insert OK, 1 row affected
+insert into t34 values ('abcdc')
+Insert OK, 1 row affected
+insert into t34 values ('abcdc_')
+Insert OK, 1 row affected
+lua box.space[34].index[0]:select_range(3, 'abcdb')
+---
+ - 'abcdb': {}
+ - 'abcdb_': {}
+ - 'abcdb__': {}
+...
+lua box.space[34]:truncate()
+---
+...
+lua box.space[35]:truncate()
+---
+...
+insert into t35 values (0, 0, 0, 0)
+Insert OK, 1 row affected
+insert into t35 values (1, 1, 1, 1)
+Insert OK, 1 row affected
+insert into t35 values (2, 2, 2, 2)
+Insert OK, 1 row affected
+replace into t35 values (1, 1, 1, 1)
+Replace OK, 1 row affected
+replace into t35 values (1, 10, 10, 10)
+Replace OK, 1 row affected
+replace into t35 values (1, 1, 1, 1)
+Replace OK, 1 row affected
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+select * from t35 WHERE k0 = 1
+Found 1 tuple:
+[1, 1, 1, 1]
+select * from t35 WHERE k1 = 1
+Found 1 tuple:
+[1, 1, 1, 1]
+select * from t35 WHERE k2 = 1
+Found 1 tuple:
+[1, 1, 1, 1]
+select * from t35 WHERE k3 = 1
+Found 1 tuple:
+[1, 1, 1, 1]
+insert into t35 values (10, 10, 10, 10)
+Insert OK, 1 row affected
+delete from t35 WHERE k0 = 10
+Delete OK, 1 row affected
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+insert into t35 values (1, 10, 10, 10)
+An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 0'
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+select * from t35 WHERE k0 = 1
+Found 1 tuple:
+[1, 1, 1, 1]
+replace into t35 values (10, 10, 10, 10)
+An error occurred: ER_TUPLE_NOT_FOUND, 'Tuple doesn't exist in index 0'
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+insert into t35 values (10, 0, 10, 10)
+An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 1'
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+select * from t35 WHERE k1 = 0
+Found 1 tuple:
+[0, 0, 0, 0]
+replace into t35 values (2, 0, 10, 10)
+An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 1'
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+select * from t35 WHERE k1 = 0
+Found 1 tuple:
+[0, 0, 0, 0]
+insert into t35 values (10, 10, 10, 0)
+An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 3'
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+select * from t35 WHERE k3 = 0
+Found 1 tuple:
+[0, 0, 0, 0]
+replace into t35 values (2, 10, 10, 0)
+An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 3'
+select * from t35 WHERE k0 = 10
+No match
+select * from t35 WHERE k1 = 10
+No match
+select * from t35 WHERE k2 = 10
+No match
+select * from t35 WHERE k3 = 10
+No match
+select * from t35 WHERE k3 = 0
+Found 1 tuple:
+[0, 0, 0, 0]
+insert into t35 values (4, 4, 0, 4)
+Insert OK, 1 row affected
+insert into t35 values (5, 5, 0, 5)
+Insert OK, 1 row affected
+insert into t35 values (6, 6, 0, 6)
+Insert OK, 1 row affected
+replace into t35 values (5, 5, 0, 5)
+Replace OK, 1 row affected
+select * from t35 WHERE k2 = 0
+Found 4 tuples:
+[0, 0, 0, 0]
+[4, 4, 0, 4]
+[5, 5, 0, 5]
+[6, 6, 0, 6]
+delete from t35 WHERE k0 = 5
+Delete OK, 1 row affected
+select * from t35 WHERE k2 = 0
+Found 3 tuples:
+[0, 0, 0, 0]
+[4, 4, 0, 4]
+[6, 6, 0, 6]
+lua box.space[35]:truncate()
+---
+...
diff --git a/test/big/tree_pk_avl.test b/test/big/tree_pk_avl.test
new file mode 100644
index 0000000000000000000000000000000000000000..d37c8be9df971af3cbf269b23f38b6542daeabf0
--- /dev/null
+++ b/test/big/tree_pk_avl.test
@@ -0,0 +1,181 @@
+# encoding: tarantool
+#
+# integer keys
+exec sql "insert into t32 values (1, 'tuple')"
+exec admin "save snapshot"
+exec sql "insert into t32 values (2, 'tuple 2')"
+exec admin "save snapshot"
+
+exec sql "insert into t32 values (3, 'tuple 3')"
+exec sql "select * from t32 where k0 = 1"
+exec sql "select * from t32 where k0 = 2"
+exec sql "select * from t32 where k0 = 3"
+
+# Cleanup
+exec sql "delete from t32 where k0 = 1"
+exec sql "delete from t32 where k0 = 2"
+exec sql "delete from t32 where k0 = 3"
+
+# Test incorrect keys - supplied key field type does not match index type
+# https://bugs.launchpad.net/tarantool/+bug/1072624
+exec sql "insert into t32 VALUES('xxxxxxx')"
+exec sql "insert into t32 VALUES('')"
+exec sql "insert into t32 VALUES('12')"
+
+# string keys
+exec sql "insert into t33 values ('identifier', 'tuple')"
+exec admin "save snapshot"
+exec sql "insert into t33 values ('second', 'tuple 2')"
+exec admin "save snapshot"
+exec sql "call box.select_range('33', '0', '100', 'second')"
+exec sql "call box.select_range('33', '0', '100', 'identifier')"
+
+exec sql "insert into t33 values ('third', 'tuple 3')"
+exec sql "select * from t33 where k0 = 'identifier'"
+exec sql "select * from t33 where k0 = 'second'"
+exec sql "select * from t33 where k0 = 'third'"
+
+# Cleanup
+exec sql "delete from t33 where k0 = 'identifier'"
+exec sql "delete from t33 where k0 = 'second'"
+exec sql "delete from t33 where k0 = 'third'"
+lua = """
+function box.crossjoin(space0, space1, limit)
+  space0 = tonumber(space0)
+  space1 = tonumber(space1)
+  limit = tonumber(limit)
+  local result = {}
+  for k0, v0 in box.space[space0]:pairs() do
+    for k1, v1 in box.space[space1]:pairs() do
+      if limit <= 0 then
+        return unpack(result)
+      end
+      newtuple = {v0:unpack()}
+      for _, v in v1:pairs() do table.insert(newtuple, v) end
+      table.insert(result, newtuple)
+      limit = limit - 1
+    end
+  end
+  return unpack(result)
+end"""
+exec sql "insert into t32 values (1, 'tuple')"
+exec sql "insert into t33 values (1, 'tuple')"
+exec sql "insert into t33 values (2, 'tuple')"
+exec admin "lua " + lua.replace('\n', ' ')
+exec sql "call box.crossjoin('33', '33', '0')"
+exec sql "call box.crossjoin('33', '33', '5')"
+exec sql "call box.crossjoin('33', '33', '10000')"
+exec sql "call box.crossjoin('33', '32', '10000')"
+exec admin "lua box.space[33]:truncate()"
+
+# Bug #922520 - select missing keys
+exec sql "insert into t32 values (200, 'select me!')"
+exec sql "select * from t32 where k0 = 200"
+exec sql "select * from t32 where k0 = 199"
+exec sql "select * from t32 where k0 = 201"
+
+# Test partially specified keys in TREE indexes
+exec sql "insert into t34 values ('abcd')"
+exec sql "insert into t34 values ('abcda')"
+exec sql "insert into t34 values ('abcda_')"
+exec sql "insert into t34 values ('abcdb')"
+exec sql "insert into t34 values ('abcdb_')"
+exec sql "insert into t34 values ('abcdb__')"
+exec sql "insert into t34 values ('abcdb___')"
+exec sql "insert into t34 values ('abcdc')"
+exec sql "insert into t34 values ('abcdc_')"
+exec admin "lua box.space[34].index[0]:select_range(3, 'abcdb')"
+exec admin "lua box.space[34]:truncate()"
+
+#
+# tree::replace tests
+#
+
+exec admin "lua box.space[35]:truncate()"
+
+exec sql "insert into t35 values (0, 0, 0, 0)"
+exec sql "insert into t35 values (1, 1, 1, 1)"
+exec sql "insert into t35 values (2, 2, 2, 2)"
+
+# OK
+exec sql "replace into t35 values (1, 1, 1, 1)"
+exec sql "replace into t35 values (1, 10, 10, 10)"
+exec sql "replace into t35 values (1, 1, 1, 1)"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+exec sql "select * from t35 WHERE k0 = 1"
+exec sql "select * from t35 WHERE k1 = 1"
+exec sql "select * from t35 WHERE k2 = 1"
+exec sql "select * from t35 WHERE k3 = 1"
+
+# OK
+exec sql "insert into t35 values (10, 10, 10, 10)"
+exec sql "delete from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+
+
+# TupleFound (primary key)
+exec sql "insert into t35 values (1, 10, 10, 10)"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+exec sql "select * from t35 WHERE k0 = 1"
+
+# TupleNotFound (primary key)
+exec sql "replace into t35 values (10, 10, 10, 10)"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+
+# TupleFound (key #1)
+exec sql "insert into t35 values (10, 0, 10, 10)"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+exec sql "select * from t35 WHERE k1 = 0"
+
+# TupleFound (key #1)
+exec sql "replace into t35 values (2, 0, 10, 10)"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+exec sql "select * from t35 WHERE k1 = 0"
+
+# TupleFound (key #3)
+exec sql "insert into t35 values (10, 10, 10, 0)"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+exec sql "select * from t35 WHERE k3 = 0"
+
+# TupleFound (key #3)
+exec sql "replace into t35 values (2, 10, 10, 0)"
+exec sql "select * from t35 WHERE k0 = 10"
+exec sql "select * from t35 WHERE k1 = 10"
+exec sql "select * from t35 WHERE k2 = 10"
+exec sql "select * from t35 WHERE k3 = 10"
+exec sql "select * from t35 WHERE k3 = 0"
+
+sql.sort = True
+# Non-Uniq test (key #2)
+exec sql "insert into t35 values (4, 4, 0, 4)"
+exec sql "insert into t35 values (5, 5, 0, 5)"
+exec sql "insert into t35 values (6, 6, 0, 6)"
+exec sql "replace into t35 values (5, 5, 0, 5)"
+exec sql "select * from t35 WHERE k2 = 0"
+exec sql "delete from t35 WHERE k0 = 5"
+exec sql "select * from t35 WHERE k2 = 0"
+sql.sort = False
+
+exec admin "lua box.space[35]:truncate()"
+
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 87e0a68347d29abd3ba061d70cd131d6159a9ce4..f0c2c22d5d5413aafa8d146b7cdeff99e8e7de23 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -25,3 +25,6 @@ add_executable(mempool.test mempool.c)
 target_link_libraries(mempool.test small)
 add_executable(small_alloc.test small_alloc.c)
 target_link_libraries(small_alloc.test small)
+add_executable(avl_tree.test avl_tree.c)
+target_link_libraries(avl_tree.test m)
+target_link_libraries(avl_tree.test misc)
diff --git a/test/unit/avl_tree.c b/test/unit/avl_tree.c
new file mode 100644
index 0000000000000000000000000000000000000000..2cd864804c404c0a3513c942e70bdbb1307509fa
--- /dev/null
+++ b/test/unit/avl_tree.c
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "unit.h"
+#include "avl_tree.h"
+#include "../third_party/sptree.h"
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif //#ifndef MAX
+
+SPTREE_DEF(test, realloc);
+AVL_DEF(test, realloc);
+
+static int
+node_comp(const void *p1, const void *p2, void* unused)
+{
+	(void)unused;
+	return *((const int *)p1) - *((const int *)p2);
+}
+
+static void
+simple_check()
+{
+	header();
+	
+	avl_test t;
+	
+	int ini[] = {1, 10, 2, 9, 3, 8, 4, 7, 5, 6};
+	int *use = (int *)malloc(sizeof(ini));
+	memcpy(use, ini, sizeof(ini));
+	int nuse = (int)(sizeof(ini) / sizeof(ini[0]));
+	avl_test_init(&t, sizeof(int), use, nuse, 0, &node_comp, 0, 0);
+	
+	int add[] = {-1, 11, 0, 12};
+	for (size_t i = 0; i < sizeof(add) / sizeof(add[0]); i++)
+		avl_test_replace(&t, &add[i], 0);
+
+	int rem[] = {3, 5, 7};
+	for (size_t i = 0; i < sizeof(rem) / sizeof(rem[0]); i++)
+		avl_test_delete(&t, &rem[i]);
+
+	int *val;
+	
+	val = avl_test_first(&t);
+	printf("%d ", *val);
+	val = avl_test_last(&t);
+	printf("%d\n", *val);
+	
+	avl_test_iterator* itr = avl_test_iterator_init(&t);
+	while ((val = avl_test_iterator_next(itr)))
+		printf("%d ", *val);
+	printf("\n");
+	avl_test_iterator_free(itr);
+	
+	itr = avl_test_iterator_reverse_init(&t);
+	while ((val = avl_test_iterator_reverse_next(itr)))
+		printf("%d ", *val);
+	printf("\n");
+	avl_test_iterator_free(itr);
+	
+	avl_test_destroy(&t);
+
+	
+	footer();
+}
+
+static void
+compare_with_sptree_check()
+{
+	header();
+
+	sptree_test spt_test;
+	sptree_test_init(&spt_test, sizeof(int), 0, 0, 0, &node_comp, 0, 0);
+	avl_test avl_test;
+	avl_test_init(&avl_test, sizeof(int), 0, 0, 0, &node_comp, 0, 0);
+	
+	for (int i = 0; i < 64 * 1024; i++) {
+		int rnd = rand() % (16 * 1024);
+		int find_res1 = sptree_test_find(&spt_test, &rnd) ? 1 : 0;
+		int find_res2 = avl_test_find(&avl_test, &rnd) ? 1 : 0;
+		if (find_res1 ^ find_res2) {
+			fail("trees identity", "false");
+			continue;
+		}
+			
+		if (find_res1 == 0) {
+			sptree_test_replace(&spt_test, &rnd, NULL);
+			avl_test_replace(&avl_test, &rnd, NULL);
+			
+		} else {
+			sptree_test_delete(&spt_test, &rnd);
+			avl_test_delete(&avl_test, &rnd);
+		}
+	}
+	sptree_test_destroy(&spt_test);
+	avl_test_destroy(&avl_test);
+	
+	footer();
+}
+
+int
+main(void)
+{
+	simple_check();
+	compare_with_sptree_check();
+}
\ No newline at end of file
diff --git a/test/unit/avl_tree.result b/test/unit/avl_tree.result
new file mode 100644
index 0000000000000000000000000000000000000000..59f971cc7244fbea1d6163b01d7ddd2e49acd8a5
--- /dev/null
+++ b/test/unit/avl_tree.result
@@ -0,0 +1,8 @@
+	*** simple_check ***
+-1 12
+-1 0 1 2 4 6 8 9 10 11 12 
+12 11 10 9 8 6 4 2 1 0 -1 
+	*** simple_check: done ***
+ 	*** compare_with_sptree_check ***
+	*** compare_with_sptree_check: done ***
+ 
\ No newline at end of file