From e68741c0ff9bc5ff9400786b560962f5b2ad682f Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Thu, 31 Jan 2013 18:59:33 +0400
Subject: [PATCH] libbitset: bitset_index (splited off from bitmap-index)

---
 .gitignore                    |   1 +
 include/lib/bitset/index.h    | 218 +++++++++++++++++++++++
 src/lib/bitset/CMakeLists.txt |   1 +
 src/lib/bitset/index.c        | 323 ++++++++++++++++++++++++++++++++++
 test/unit/CMakeLists.txt      |   2 +
 test/unit/bitset_index.c      | 287 ++++++++++++++++++++++++++++++
 test/unit/bitset_index.result |  22 +++
 test/unit/bitset_index.test   |   1 +
 8 files changed, 855 insertions(+)
 create mode 100644 include/lib/bitset/index.h
 create mode 100644 src/lib/bitset/index.c
 create mode 100644 test/unit/bitset_index.c
 create mode 100644 test/unit/bitset_index.result
 create mode 100644 test/unit/bitset_index.test

diff --git a/.gitignore b/.gitignore
index b532180bdc..c03c192d05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,7 @@ test/unit/rlist
 test/unit/bit_test
 test/unit/bitset_basic_test
 test/unit/bitset_iterator_test
+test/unit/bitset_index_test
 Makefile
 CMakeFiles
 CMakeCache.txt
diff --git a/include/lib/bitset/index.h b/include/lib/bitset/index.h
new file mode 100644
index 0000000000..37c7bd13d5
--- /dev/null
+++ b/include/lib/bitset/index.h
@@ -0,0 +1,218 @@
+#ifndef TARANTOOL_LIB_BITSET_INDEX_H_INCLUDED
+#define TARANTOOL_LIB_BITSET_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.
+ */
+
+/**
+ * @file
+ * @brief BitsetIndex - a bit index based on @link bitset @endlink.
+ * @see bitset.h
+ */
+
+#include <lib/bitset/bitset.h>
+#include <lib/bitset/iterator.h>
+
+/**
+ * @brief BitsetIndex
+ */
+struct bitset_index {
+	/** @cond false **/
+	struct bitset **bitsets;
+	size_t capacity;
+	void *(*realloc)(void *ptr, size_t size);
+	/** @endcond **/
+};
+
+/**
+ * @brief Construct \a index
+ * @param index bitset index
+ * @param realloc memory allocator to use
+ * @retval 0 on success
+ * @retval -1 on memory error
+ */
+int
+bitset_index_create(struct bitset_index *index,
+		    void *(*realloc)(void *ptr, size_t size));
+
+/**
+ * @brief Destruct \a index
+ * @param index bitset index
+ */
+void
+bitset_index_destroy(struct bitset_index *index);
+
+/**
+ * @brief Insert (\a key, \a value) pair into \a index.
+ * Only one pair with same value can exist in the index.
+ * If pair with same \a value is exist, it will be updated quietly.
+ * @param index object
+ * @param key key
+ * @param key_size size of the key
+ * @param value value
+ * @retval 0 on success
+ * @retval -1 on memory error
+ */
+int
+bitset_index_insert(struct bitset_index *index, const void *key, size_t key_size,
+		    size_t value);
+
+/**
+ * @brief Remove a pair with \a value (*, \a value) from \a index.
+ * @param index bitset index
+ * @param value value
+ */
+void
+bitset_index_remove_value(struct bitset_index *index, size_t value);
+
+/**
+ * @brief Initialize \a expr to iterate over a bitset index.
+ * The \a expr can be then passed to @link bitset_index_init_iterator @endlink.
+ *
+ * 'All' algorithm. Matches all pairs in a index.
+ *
+ * @param expr bitset expression
+ * @retval 0 on success
+ * @retval -1 on memory error
+ * @see @link bitset_index_init_iterator @endlink
+ * @see expr.h
+ */
+int
+bitset_index_expr_all(struct bitset_expr *expr);
+
+/**
+ * @brief Initialize \a expr to iterate over a bitset index.
+ * The \a expr can be then passed to @link bitset_index_init_iterator @endlink.
+ *
+ * 'Equals' algorithm. Matches all pairs where \a key exactly equals to
+ * pair.key (\a key == pair.key).
+ *
+ * @param expr bitset expression
+ * @param key key
+ * @param key_size of \a key (in char, as sizeof returns)
+ * @retval 0 on success
+ * @retval -1 on memory error
+ * @see @link bitset_index_init_iterator @endlink
+ * @see expr.h
+ */
+int
+bitset_index_expr_equals(struct bitset_expr *expr, const void *key,
+			 size_t key_size);
+
+/**
+ * @brief Initialize \a expr to iterate over a bitset index.
+ * The \a expr can be then passed to @link bitset_index_init_iterator @endlink.
+ *
+ * 'All-Bits-Set' algorithm. Matches all pairs where all bits from \a key
+ * is set in pair.key ((\a key & pair.key) == \a key).
+ *
+ * @param expr bitset expression
+ * @retval 0 on success
+ * @retval -1 on memory error
+ * @see @link bitset_index_init_iterator @endlink
+ * @see expr.h
+ */
+int
+bitset_index_expr_all_set(struct bitset_expr *expr, const void *key,
+			  size_t key_size);
+
+/**
+ * @brief Initialize \a expr to iterate over a bitset index.
+ * The \a expr can be then passed to @link bitset_index_init_iterator @endlink.
+ *
+ * 'Any-Bits-Set' algorithm. Matches all pairs where at least one bit from
+ * \a key is set in pair.key ((\a key & pair.key) != 0).
+ *
+ * @param expr bitset expression
+ * @retval 0 on success
+ * @retval -1 on memory error
+ * @see @link bitset_index_init_iterator @endlink
+ * @see expr.h
+ */
+int
+bitset_index_expr_any_set(struct bitset_expr *expr, const void *key,
+			  size_t key_size);
+
+/**
+ * @brief Initialize \a expr to iterate over a bitset index.
+ * The \a expr can be then passed to @link bitset_index_init_iterator @endlink.
+ *
+ * 'All-Bits-Not-Set' algorithm. Matches all pairs in the \a index, where all
+ * bits from the \a key is not set in pair.key ((\a key & pair.key) == 0).
+ *
+ * @param expr bitset expression
+ * @retval 0 on success
+ * @retval -1 on memory error
+ * @see @link bitset_index_init_iterator @endlink
+ * @see expr.h
+ */
+int
+bitset_index_expr_all_not_set(struct bitset_expr *expr, const void *key,
+			      size_t key_size);
+
+/**
+ * @brief Initialize \a it using \a expr and bitsets used in \a index.
+ *
+ * @param index bitset index
+ * @param it bitset iterator
+ * @param expr bitset expression
+ * @retval 0 on success
+ * @retval 1 on memory error
+ */
+int
+bitset_index_init_iterator(struct bitset_index *index,
+			   struct bitset_iterator *it,
+			   struct bitset_expr *expr);
+
+/**
+ * @brief Checks if a (*, \a value) pair exists in \a index
+ * @param index bitset index
+ * @param value
+ * @retval true if \a index contains pair with the \a value
+ * @retval false otherwise
+ */
+bool
+bitset_index_contains_value(struct bitset_index *index, size_t value);
+
+/**
+ * @brief Return a number of pairs in \a index.
+ * @param index bitset index
+ * @return number of pairs in \a index
+ */
+inline size_t
+bitset_index_size(struct bitset_index *index)
+{
+	return bitset_cardinality(index->bitsets[0]);
+}
+
+#if defined(DEBUG)
+void
+bitset_index_dump(struct bitset_index *index, int verbose, FILE *stream);
+#endif /* defined(DEBUG) */
+
+#endif /* TARANTOOL_LIB_BITSET_INDEX_H_INCLUDED */
diff --git a/src/lib/bitset/CMakeLists.txt b/src/lib/bitset/CMakeLists.txt
index 0b1316d002..1339a02efa 100644
--- a/src/lib/bitset/CMakeLists.txt
+++ b/src/lib/bitset/CMakeLists.txt
@@ -3,6 +3,7 @@ set(lib_sources
     page.c
     expr.c
     iterator.c
+    index.c
 )
 
 set_source_files_compile_flags(${lib_sources})
diff --git a/src/lib/bitset/index.c b/src/lib/bitset/index.c
new file mode 100644
index 0000000000..e8a408c4c6
--- /dev/null
+++ b/src/lib/bitset/index.c
@@ -0,0 +1,323 @@
+/*
+ * 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 <lib/bitset/index.h>
+#include <lib/bitset/expr.h>
+#include <lib/bit/bit.h>
+
+#include <string.h>
+#include <assert.h>
+
+const size_t INDEX_DEFAULT_CAPACITY = 32;
+
+static int
+bitset_index_reserve(struct bitset_index *index, size_t size);
+
+int
+bitset_index_create(struct bitset_index *index,
+		    void *(*realloc)(void *ptr, size_t size))
+{
+	assert (index != NULL);
+	memset(index, 0, sizeof(*index));
+	index->realloc = realloc;
+	if (bitset_index_reserve(index, 1) != 0)
+		return -1;
+
+	return 0;
+}
+
+void
+bitset_index_destroy(struct bitset_index *index)
+{
+	assert (index != NULL);
+	assert (index->capacity > 0);
+
+	for (size_t b = 0; b < index->capacity; b++) {
+		if (index->bitsets[b] == NULL)
+			break;
+
+		bitset_destroy(index->bitsets[b]);
+		index->realloc(index->bitsets[b], 0);
+		index->bitsets[b] = NULL;
+	}
+	if (index->capacity > 0) {
+		index->realloc(index->bitsets, 0);
+	}
+
+	memset(index, 0, sizeof(*index));
+}
+
+static int
+bitset_index_reserve(struct bitset_index *index, size_t size)
+{
+	if (size <= index->capacity)
+		return 0;
+
+	size_t capacity = (index->capacity > 0)
+				? index->capacity
+				: INDEX_DEFAULT_CAPACITY;
+
+	while (capacity <= size) {
+		capacity *= 2;
+	}
+
+	struct bitset **bitsets = index->realloc(index->bitsets,
+					capacity * sizeof(*index->bitsets));
+	if (bitsets == NULL)
+		goto error_1;
+
+	memset(bitsets + index->capacity, 0,
+	       (capacity - index->capacity) * sizeof(*index->bitsets));
+
+	/* Save bitset ** but do not update index->capacity */
+	index->bitsets = bitsets;
+
+	for (size_t b = index->capacity; b < capacity; b++) {
+		index->bitsets[b] = index->realloc(NULL,
+					sizeof(*index->bitsets[b]));
+		if (index->bitsets[b] == NULL)
+			goto error_2;
+
+		bitset_create(index->bitsets[b], index->realloc);
+	}
+
+	index->capacity = capacity;
+
+	return 0;
+
+error_2:
+	for (size_t b = index->capacity; b < capacity; b++) {
+		if (index->bitsets[b] == NULL)
+			break;
+
+		bitset_destroy(index->bitsets[b]);
+		index->realloc(index->bitsets[b], 0);
+		index->bitsets[b] = NULL;
+	}
+error_1:
+	return -1;
+}
+
+int
+bitset_index_insert(struct bitset_index *index, const void *key,
+		    size_t key_size, size_t value)
+{
+	assert (index != NULL);
+	assert (key != NULL);
+	assert (index->capacity > 0);
+
+	const size_t size = 1 + key_size * CHAR_BIT;
+	if (bitset_index_reserve(index, size) != 0)
+		return -1;
+
+	struct bit_iterator bit_it;
+	bit_iterator_init(&bit_it, key, key_size, true);
+	size_t pos;
+	while ( (pos = bit_iterator_next(&bit_it)) != SIZE_MAX) {
+		size_t b = pos + 1;
+		if (bitset_set(index->bitsets[b], value) < 0)
+			goto rollback;
+	}
+
+	if (bitset_set(index->bitsets[0], value) < 0)
+		goto rollback;
+
+	return 0;
+
+	/* TODO: partial rollback is not work properly here */
+rollback:
+	/* Rollback changes */
+	bit_iterator_init(&bit_it, key, size, true);
+	while ( (pos = bit_iterator_next(&bit_it)) != SIZE_MAX) {
+		size_t b = pos + 1;
+		if (index->bitsets[b] == NULL)
+			continue;
+
+		bitset_clear(index->bitsets[b], value);
+	}
+
+	bitset_clear(index->bitsets[0], value);
+
+	return -1;
+}
+
+void
+bitset_index_remove_value(struct bitset_index *index, size_t value)
+{
+	assert(index != NULL);
+
+	if (index->capacity == 0)
+		return;
+
+	for (size_t b = 1; b < index->capacity; b++) {
+		if (index->bitsets[b] == NULL)
+			continue;
+
+		/* Ignore all errors here */
+		bitset_clear(index->bitsets[b], value);
+	}
+	bitset_clear(index->bitsets[0], value);
+}
+
+bool
+bitset_index_contains_value(struct bitset_index *index, size_t value)
+{
+	assert(index != NULL);
+
+	return bitset_test(index->bitsets[0], value);
+}
+
+int
+bitset_index_expr_all(struct bitset_expr *expr)
+{
+	(void) index;
+
+	bitset_expr_clear(expr);
+	if (bitset_expr_add_conj(expr) != 0)
+		return -1;
+
+	if (bitset_expr_add_param(expr, 0, false) != 0)
+		return -1;
+
+	return 0;
+}
+
+int
+bitset_index_expr_equals(struct bitset_expr *expr, const void *key,
+			 size_t key_size)
+{
+	bitset_expr_clear(expr);
+
+	if (bitset_expr_add_conj(expr) != 0)
+		return -1;
+
+	for (size_t pos = 0; pos < key_size * CHAR_BIT; pos++) {
+		size_t b = pos + 1;
+		bool bit_exist = bit_test(key, pos);
+		if (bitset_expr_add_param(expr, b, !bit_exist) != 0)
+			return -1;
+	}
+
+	if (bitset_expr_add_param(expr, 0, false) != 0) {
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+bitset_index_expr_all_set(struct bitset_expr *expr, const void *key,
+			  size_t key_size)
+{
+	bitset_expr_clear(expr);
+
+	if (bitset_expr_add_conj(expr) != 0)
+		return -1;
+
+	struct bit_iterator bit_it;
+	bit_iterator_init(&bit_it, key, key_size, true);
+	size_t pos;
+	while ( (pos = bit_iterator_next(&bit_it)) != SIZE_MAX ) {
+		size_t b = pos + 1;
+		if (bitset_expr_add_param(expr, b, false) != 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+int
+bitset_index_expr_any_set(struct bitset_expr *expr, const void *key,
+			  size_t key_size)
+{
+	bitset_expr_clear(expr);
+
+	struct bit_iterator bit_it;
+	bit_iterator_init(&bit_it, key, key_size, true);
+	size_t pos;
+	while ( (pos = bit_iterator_next(&bit_it)) != SIZE_MAX) {
+		size_t b = pos + 1;
+		if (bitset_expr_add_conj(expr) != 0)
+			return -1;
+		if (bitset_expr_add_param(expr, b, false) != 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+int
+bitset_index_expr_all_not_set(struct bitset_expr *expr, const void *key,
+			      size_t key_size) {
+	bitset_expr_clear(expr);
+
+	if (bitset_expr_add_conj(expr) != 0)
+		return -1;
+
+	if (bitset_expr_add_param(expr, 0, false) != 0)
+		return -1;
+
+	struct bit_iterator bit_it;
+	bit_iterator_init(&bit_it, key, key_size, true);
+	size_t pos;
+	while ( (pos = bit_iterator_next(&bit_it)) != SIZE_MAX) {
+		size_t b = pos + 1;
+		if (bitset_expr_add_param(expr, b, true) != 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+int
+bitset_index_init_iterator(struct bitset_index *index,
+			   struct bitset_iterator *it, struct bitset_expr *expr)
+{
+	assert (index != NULL);
+	assert (it != NULL);
+
+	/* Check that we have all required bitsets */
+	size_t max = 0;
+	for (size_t c = 0; c < expr->size; c++) {
+		for (size_t b = 0; b < expr->conjs[c].size; b++) {
+			if (expr->conjs[c].bitset_ids[b] > max) {
+				max = expr->conjs[c].bitset_ids[b];
+			}
+		}
+	}
+
+	/* Resize the index with empty bitsets */
+	if (bitset_index_reserve(index, max + 1) != 0)
+		return -1;
+
+	return bitset_iterator_init(it, expr, index->bitsets, index->capacity);
+}
+
+extern inline size_t
+bitset_index_size(struct bitset_index *index);
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 1fa92bbdb0..a2afb4259e 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -14,6 +14,8 @@ add_executable(bitset_basic_test bitset_basic.c)
 target_link_libraries(bitset_basic_test bitset)
 add_executable(bitset_iterator_test bitset_iterator.c)
 target_link_libraries(bitset_iterator_test bitset)
+add_executable(bitset_index_test bitset_index.c)
+target_link_libraries(bitset_index_test bitset)
 
 add_executable(objc_finally objc_finally.m)
 add_executable(objc_catchcxx objc_catchcxx.m)
diff --git a/test/unit/bitset_index.c b/test/unit/bitset_index.c
new file mode 100644
index 0000000000..d39fd22521
--- /dev/null
+++ b/test/unit/bitset_index.c
@@ -0,0 +1,287 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#include <lib/bitset/index.h>
+#include "unit.h"
+
+enum { NUMS_SIZE = 1 << 16 };
+
+static
+void test_resize(void)
+{
+	header();
+
+	struct bitset_index index;
+	fail_unless(bitset_index_create(&index, realloc) == 0);
+	struct bitset_iterator it;
+	bitset_iterator_create(&it, realloc);
+	struct bitset_expr expr;
+	bitset_expr_create(&expr, realloc);
+
+	size_t key = 23411111;
+	size_t value = 2321321;
+
+	bitset_index_insert(&index, &key, sizeof(key), value);
+
+	fail_unless(bitset_index_expr_equals(&expr, &key, sizeof(key)) == 0);
+
+	fail_unless(bitset_index_init_iterator(&index, &it, &expr) == 0);
+	fail_unless(bitset_iterator_next(&it) == value);
+	fail_unless(bitset_iterator_next(&it) == SIZE_MAX);
+
+	bitset_expr_destroy(&expr);
+	bitset_iterator_destroy(&it);
+	bitset_index_destroy(&index);
+
+	footer();
+}
+
+static
+void test_get_size(void)
+{
+	header();
+
+	struct bitset_index index;
+	fail_unless(bitset_index_create(&index, realloc) == 0);
+
+	const size_t SET_SIZE = 1 << 10;
+	size_t key = 656906;
+	for(size_t i = 0; i < SET_SIZE; i++) {
+		bitset_index_insert(&index, &key, sizeof(key), i);
+	}
+
+	fail_unless(bitset_index_size(&index) == SET_SIZE);
+
+	bitset_index_destroy(&index);
+
+	footer();
+}
+
+static
+void check_keys(struct bitset_index *index,
+		size_t *keys, size_t *values, size_t size)
+{
+	struct bitset_iterator it;
+	bitset_iterator_create(&it, realloc);
+	struct bitset_expr expr;
+	bitset_expr_create(&expr, realloc);
+
+	printf("Checking keys... ");
+	for (size_t i = 0; i < size; i++) {
+		/* ignore removed keys */
+		if (keys[i] == SIZE_MAX) {
+			continue;
+		}
+
+		fail_unless(bitset_index_expr_equals(&expr, &keys[i],
+						     sizeof(keys[i])) == 0);
+
+		fail_unless(bitset_index_init_iterator(index, &it, &expr) == 0);
+
+		size_t pos;
+
+		bool pair_found = false;
+		while ( (pos = bitset_iterator_next(&it)) != SIZE_MAX) {
+			if (pos == values[i]) {
+				pair_found = true;
+				break;
+			}
+		}
+		fail_unless(pair_found);
+	}
+	printf("ok\n");
+
+	bitset_iterator_destroy(&it);
+	bitset_expr_destroy(&expr);
+}
+
+static
+void test_insert_remove(void)
+{
+	header();
+
+	struct bitset_index index;
+	fail_unless(bitset_index_create(&index, realloc) == 0);
+
+	size_t NUMS_SIZE = 1 << 11;
+	size_t *keys = malloc(NUMS_SIZE * sizeof(size_t));
+	size_t *values = malloc(NUMS_SIZE * sizeof(size_t));
+
+	printf("Generating test set... ");
+	for(size_t i = 0; i < NUMS_SIZE; i++) {
+		keys[i] = rand();
+		values[i] = rand();
+	}
+	printf("ok\n");
+
+	printf("Inserting pairs... ");
+	for(size_t i = 0; i < NUMS_SIZE; i++) {
+		bitset_index_insert(&index, &keys[i], sizeof(keys[i]),
+				    values[i]);
+
+	}
+	printf("ok\n");
+
+	check_keys(&index, keys, values, NUMS_SIZE);
+
+	printf("Removing random pairs... ");
+	for(size_t i = 0; i < NUMS_SIZE; i++) {
+		if (rand() % 5 == 0) {
+			bitset_index_remove_value(&index, values[i]);
+			keys[i] = SIZE_MAX;
+		}
+	}
+	printf("ok\n");
+
+	check_keys(&index, keys, values, NUMS_SIZE);
+
+	bitset_index_destroy(&index);
+
+	free(keys);
+	free(values);
+
+	footer();
+}
+
+
+static
+void test_simple(int mode, size_t search_mask)
+{
+	fail_unless(mode >= 0 && mode < 3);
+
+	struct bitset_index index;
+	fail_unless(bitset_index_create(&index, realloc) == 0);
+	struct bitset_iterator it;
+	bitset_iterator_create(&it, realloc);
+	struct bitset_expr expr;
+	bitset_expr_create(&expr, realloc);
+
+	size_t check_count = 0;
+	for (size_t key = 0; key < NUMS_SIZE; key++) {
+		bitset_index_insert(&index, &key, sizeof(key), key);
+		if (mode == 0) {
+			check_count++;
+		} else if (mode == 1 && (key & search_mask) == search_mask) {
+			check_count++;
+		} else if (mode == 2 && (key & search_mask) != 0) {
+			check_count++;
+		}
+	}
+
+	if (mode == 0) {
+		fail_unless(bitset_index_expr_all(&expr) == 0);
+	} else if (mode == 1) {
+		fail_unless(bitset_index_expr_all_set(&expr,
+				&search_mask, sizeof(search_mask)) == 0);
+	} else if (mode == 2) {
+		fail_unless(bitset_index_expr_any_set(&expr,
+				&search_mask, sizeof(search_mask)) == 0);
+	}
+	fail_unless(bitset_index_init_iterator(&index, &it, &expr) == 0);
+
+	size_t found_count = 0;
+	for (size_t key = 0; key < NUMS_SIZE; key++) {
+		size_t r = bitset_iterator_next(&it);
+		if (mode == 0) {
+			fail_unless(key == r);
+			found_count++;
+		} else if (mode == 1 && (key & search_mask) == search_mask) {
+			found_count++;
+		} else if (mode == 2 && (key & search_mask) != 0){
+			found_count++;
+		}
+	}
+	fail_unless(bitset_iterator_next(&it) == SIZE_MAX);
+	fail_unless(found_count == check_count);
+
+	bitset_expr_destroy(&expr);
+	bitset_iterator_destroy(&it);
+	bitset_index_destroy(&index);
+}
+
+static void
+test_empty_simple(void)
+{
+	header();
+	test_simple(1, 0); /* empty result */
+	footer();
+}
+
+static void
+test_all_simple(void)
+{
+	header();
+	test_simple(0, 0);  /* all */
+	footer();
+}
+
+static void
+test_all_set_simple(void)
+{
+	header();
+	size_t search_mask = 66; /* 0b1000010 */
+	test_simple(1, search_mask);  /* all bits set */
+	footer();
+}
+
+static void
+test_any_set_simple(void)
+{
+	header();
+	size_t search_mask = 66; /* 0b1000010 */
+	test_simple(2, search_mask);  /* any bits set */
+	footer();
+}
+
+static
+void test_equals_simple(void)
+{
+	header();
+
+	struct bitset_index index;
+	fail_unless(bitset_index_create(&index, realloc) == 0);
+	struct bitset_iterator it;
+	bitset_iterator_create(&it, realloc);
+	struct bitset_expr expr;
+	bitset_expr_create(&expr, realloc);
+
+	size_t mask = ~((size_t ) 7);
+	for (size_t i = 0; i < NUMS_SIZE; i++) {
+		size_t key = i & mask;
+		size_t value = i;
+		bitset_index_insert(&index, &key, sizeof(key), value);
+	}
+
+	size_t key1 = (rand() % NUMS_SIZE) & mask;
+	fail_unless(bitset_index_expr_equals(&expr, &key1, sizeof(key1)) == 0);
+	fail_unless(bitset_index_init_iterator(&index, &it, &expr) == 0);
+
+	for (size_t i = key1; i <= (key1 + ~mask); i++) {
+		fail_unless(i == bitset_iterator_next(&it));
+	}
+	fail_unless(bitset_iterator_next(&it) == SIZE_MAX);
+
+	bitset_expr_destroy(&expr);
+	bitset_iterator_destroy(&it);
+	bitset_index_destroy(&index);
+
+	footer();
+}
+
+int main(void)
+{
+	setbuf(stdout, NULL);
+
+	test_get_size();
+	test_resize();
+	test_insert_remove();
+	test_empty_simple();
+	test_all_simple();
+	test_all_set_simple();
+	test_any_set_simple();
+	test_equals_simple();
+
+	return 0;
+}
diff --git a/test/unit/bitset_index.result b/test/unit/bitset_index.result
new file mode 100644
index 0000000000..35bdc6231b
--- /dev/null
+++ b/test/unit/bitset_index.result
@@ -0,0 +1,22 @@
+	*** test_get_size ***
+	*** test_get_size: done ***
+ 	*** test_resize ***
+	*** test_resize: done ***
+ 	*** test_insert_remove ***
+Generating test set... ok
+Inserting pairs... ok
+Checking keys... ok
+Removing random pairs... ok
+Checking keys... ok
+	*** test_insert_remove: done ***
+ 	*** test_empty_simple ***
+	*** test_empty_simple: done ***
+ 	*** test_all_simple ***
+	*** test_all_simple: done ***
+ 	*** test_all_set_simple ***
+	*** test_all_set_simple: done ***
+ 	*** test_any_set_simple ***
+	*** test_any_set_simple: done ***
+ 	*** test_equals_simple ***
+	*** test_equals_simple: done ***
+ 
\ No newline at end of file
diff --git a/test/unit/bitset_index.test b/test/unit/bitset_index.test
new file mode 100644
index 0000000000..a7d9a5ec93
--- /dev/null
+++ b/test/unit/bitset_index.test
@@ -0,0 +1 @@
+run_test("bitset_index_test")
-- 
GitLab