#include <string.h>
#include "termhash.h"
#include "hash.h"
#include "termhash.h"
#include "util.h"
#define UB(n) ((1<<n)-1)
#define CAP(n) (1<<n)
#define INITIAL_TABLE_SIZE 8
typedef struct hash_entry *hash_entry;
typedef struct term_bucket *term_bucket;
struct hash_entry
{
int length;
stamp *stamps;
gen_e e;
};
struct term_bucket
{
hash_entry entry;
struct term_bucket *next;
};
#define scan_term_bucket(b,var) for(var = b; var; var = var->next)
struct term_hash
{
term_bucket * term_buckets;
region rgn;
int ub;
int size;
int capacity;
int inserts;
};
static int hash(int ub, stamp stamps[], int len);
static void post_insert(term_hash tab) deletes;
static void rehash(term_hash tab) deletes;
static void reinsert(term_hash tab, term_bucket b);
static void insert(term_hash tab, gen_e e, stamp * stamps, int len);
static void insert_entry(term_hash tab, struct hash_entry *entry);
static gen_e walk(term_bucket b, stamp * stamps, int len);
static const int primes[] =
{ 83, 1789, 5189, 5449, 5659, 6703, 7517, 7699, 8287, 8807, 9067, 9587,
10627, 10939, 11239};
static const int initial_table_size = INITIAL_TABLE_SIZE;
term_hash make_term_hash(region rgn)
{
int ub, n;
int i;
region r;
term_hash tab = ralloc(rgn, struct term_hash);
r = newregion();
ub = UB(initial_table_size);
n = CAP(initial_table_size);
tab->term_buckets = rarrayalloc(r, n, term_bucket);
for (i = 0; i < n; i++)
{
tab->term_buckets[i] = NULL;
}
tab->rgn = r;
tab->ub = ub;
tab->size = initial_table_size;
tab->capacity = n;
tab->inserts = 0;
return tab;
}
void term_hash_delete(term_hash tab) deletes
{
deleteregion(tab->rgn);
}
gen_e term_hash_find(term_hash tab, stamp stamps[], int len)
{
int hash_val;
term_bucket b;
hash_val = hash(tab->ub, stamps, len);
b = tab->term_buckets[hash_val];
return walk(b, stamps, len);
}
static gen_e walk(term_bucket b, stamp stamps[], int len)
{
term_bucket cur;
scan_term_bucket(b,cur)
{
if (len == cur->entry->length
&& (memcmp(stamps, cur->entry->stamps, sizeof(int)*len) == 0) )
return cur->entry->e;
}
return NULL;
}
bool term_hash_insert(term_hash tab, gen_e e, stamp * stamps, int len) deletes
{
if (term_hash_find(tab, stamps, len) != NULL)
{
return TRUE;
}
insert(tab, e, stamps, len);
post_insert(tab);
return FALSE;
}
static void insert(term_hash tab, gen_e e, stamp stamps[], int len)
{
hash_entry entry;
stamp * stamp_cpy;
int i;
entry = ralloc(tab->rgn, struct hash_entry);
stamp_cpy = rarrayalloc(tab->rgn, len, stamp);
for (i = 0; i < len; i++)
{
stamp_cpy[i] = stamps[i];
}
entry->length = len;
entry->stamps = stamp_cpy;
entry->e = e;
insert_entry(tab, entry);
}
static void insert_entry(term_hash tab, hash_entry entry)
{
int hash_val;
term_bucket b, new_term_bucket;
hash_val = hash(tab->ub, entry->stamps, entry->length);
b = tab->term_buckets[hash_val];
new_term_bucket = ralloc(tab->rgn, struct term_bucket);
new_term_bucket->entry = entry;
new_term_bucket->next = b;
tab->term_buckets[hash_val] = new_term_bucket;
}
static void post_insert(term_hash tab) deletes
{
if (tab->capacity == ++tab->inserts)
{
rehash(tab);
}
}
static void rehash(term_hash tab) deletes
{
region old_rgn;
term_bucket * old_term_buckets;
int i;
int old_table_size = tab->capacity;
old_term_buckets = tab->term_buckets;
tab->capacity *= 2;
tab->ub = UB(++tab->size);
old_rgn = tab->rgn;
tab->rgn = newregion();
tab->term_buckets = rarrayalloc(tab->rgn, tab->capacity, term_bucket);
for (i = 0; i < old_table_size; i++)
{
if (old_term_buckets[i] != NULL && old_term_buckets[i]->entry != NULL)
reinsert(tab, old_term_buckets[i]);
}
deleteregion(old_rgn);
}
static void reinsert(term_hash tab, term_bucket b)
{
term_bucket cur;
scan_term_bucket(b,cur)
insert(tab, cur->entry->e, cur->entry->stamps, cur->entry->length);
}
static int hash(int ub, stamp stamps[], int len)
{
int i, n;
n = 0;
for (i = 0; i < len; i++)
{
n = (n + (primes[i % 15] * abs(stamps[i]))) & ub;
}
return n;
}