/*
 * Copyright (c) 2009-2012, Vsevolod Stakhov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * 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 ''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 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 "config.h"
#include "bloom.h"

/* 4 bits are used for counting (implementing delete operation) */
#define SIZE_BIT 4

/* These macroes are for 4 bits for counting element */
#define INCBIT(a, n, acc) do {																\
	acc = a[n * SIZE_BIT / CHAR_BIT] & (0xF << (n % (CHAR_BIT / SIZE_BIT) * SIZE_BIT));		\
	acc ++;																					\
	acc &= 0xF;																				\
																							\
	a[n * SIZE_BIT / CHAR_BIT] &= (0xF << (4 - (n % (CHAR_BIT/SIZE_BIT) * SIZE_BIT)));		\
	a[n * SIZE_BIT / CHAR_BIT] |= (acc << (n % (CHAR_BIT/SIZE_BIT) * SIZE_BIT));			\
} while (0);

#define DECBIT(a, n, acc) do {																\
	acc = a[n * SIZE_BIT / CHAR_BIT] & (0xF << (n % (CHAR_BIT / SIZE_BIT) * SIZE_BIT));		\
	acc --;																					\
	acc &= 0xF;																				\
																							\
	a[n * SIZE_BIT / CHAR_BIT] &= (0xF << (4 - (n % (CHAR_BIT/SIZE_BIT) * SIZE_BIT)));		\
	a[n * SIZE_BIT / CHAR_BIT] |= (acc << (n % (CHAR_BIT/SIZE_BIT) * SIZE_BIT));			\
} while (0);

#define GETBIT(a, n) (a[n * SIZE_BIT / CHAR_BIT] & (0xF << (n % (CHAR_BIT/SIZE_BIT) * SIZE_BIT)))

/* Common hash functions */
guint
bloom_sax_hash (const gchar *key)
{
	guint                           h = 0;

	while (*key)
		h ^= (h << 5) + (h >> 2) + (gchar)*key++;

	return h;
}

guint
bloom_sdbm_hash (const gchar *key)
{
	guint                           h = 0;

	while (*key)
		h = (gchar)*key++ + (h << 6) + (h << 16) - h;

	return h;
}

guint
bloom_fnv_hash (const gchar *key)
{
	guint                           h = 0;

	while (*key) {
		h ^= (gchar)*key++;
		h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
	}

	return h;
}

guint
bloom_rs_hash (const gchar *key)
{
	guint                           b = 378551;
	guint                           a = 63689;
	guint                           hash = 0;

	while (*key) {
		hash = hash * a + (gchar)*key++;
		a = a * b;
	}

	return hash;
}

guint
bloom_js_hash (const gchar *key)
{
	guint                           hash = 1315423911;

	while (*key) {
		hash ^= ((hash << 5) + (gchar)*key++ + (hash >> 2));
	}

	return hash;
}


guint
bloom_elf_hash (const gchar *key)
{
	guint                           hash = 0;
	guint                           x = 0;

	while (*key) {
		hash = (hash << 4) + (gchar)*key++;
		if ((x = hash & 0xF0000000L) != 0) {
			hash ^= (x >> 24);
		}
		hash &= ~x;
	}

	return hash;
}


guint
bloom_bkdr_hash (const gchar *key)
{
	guint                           seed = 131;	/* 31 131 1313 13131 131313 etc.. */
	guint                           hash = 0;

	while (*key) {
		hash = (hash * seed) + (gchar)*key++;
	}

	return hash;
}


guint
bloom_ap_hash (const gchar *key)
{
	guint                           hash = 0xAAAAAAAA;
	guint                           i = 0;

	while (*key) {
		hash ^= ((i & 1) == 0) ? ((hash << 7) ^ ((gchar)*key) * (hash >> 3)) : (~((hash << 11) + (((gchar)*key) ^ (hash >> 5))));
		key++;
	}

	return hash;
}

bloom_filter_t                 *
bloom_create (size_t size, size_t nfuncs, ...)
{
	bloom_filter_t                 *bloom;
	va_list                         l;
	gsize                           n;

	if (!(bloom = g_malloc (sizeof (bloom_filter_t)))) {
		return NULL;
	}
	if (!(bloom->a = g_new0 (gchar,  (size + CHAR_BIT - 1) / CHAR_BIT * SIZE_BIT))) {
		g_free (bloom);
		return NULL;
	}
	if (!(bloom->funcs = (hashfunc_t *) g_malloc (nfuncs * sizeof (hashfunc_t)))) {
		g_free (bloom->a);
		g_free (bloom);
		return NULL;
	}

	va_start (l, nfuncs);
	for (n = 0; n < nfuncs; ++n) {
		bloom->funcs[n] = va_arg (l, hashfunc_t);
	}
	va_end (l);

	bloom->nfuncs = nfuncs;
	bloom->asize = size;

	return bloom;
}

void
bloom_destroy (bloom_filter_t * bloom)
{
	g_free (bloom->a);
	g_free (bloom->funcs);
	g_free (bloom);
}

gboolean
bloom_add (bloom_filter_t * bloom, const gchar *s)
{
	size_t                          n;
	u_char                          t;
	guint                           v;

	for (n = 0; n < bloom->nfuncs; ++n) {
		v = bloom->funcs[n] (s) % bloom->asize;
		INCBIT (bloom->a, v, t);
	}

	return TRUE;
}

gboolean
bloom_del (bloom_filter_t * bloom, const gchar *s)
{
	size_t                          n;
	u_char                          t;
	guint                           v;

	for (n = 0; n < bloom->nfuncs; ++n) {
		v = bloom->funcs[n] (s) % bloom->asize;
		DECBIT (bloom->a, v, t);
	}

	return TRUE;

}

gboolean
bloom_check (bloom_filter_t * bloom, const gchar *s)
{
	size_t                          n;
	guint                           v;

	for (n = 0; n < bloom->nfuncs; ++n) {
		v = bloom->funcs[n] (s) % bloom->asize;
		if (!(GETBIT (bloom->a, v)))
			return FALSE;
	}

	return TRUE;
}