/* hashtable.h -- (C) Geoffrey Reynolds, April 2006.

   Hash table implementation of the baby steps giant steps lookup table.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
*/

#ifndef _HASHTABLE_H
#define _HASHTABLE_H

#include <stdint.h>
#include "config.h"
#include "memset_fast32.h"

/*
  The interface:
    init_hashtable(M) :- Allocate resources for a maximum of M elements.
    clear_hashtable() :- Delete all elements. Call before first use.
    insert32/64(j,bj) :- Insert an element (j,bj), bj is a 32/64 bit value.
    lookup32/64(bj)   :- Return j if (j,bj) is in the hashtable, bj is 32/64
                         bits. Return HASH_NOT_FOUND otherwise.
    fini_hashtable()  :- Free all resources ready for a new call to init.

  The following assumptions are valid for the current bsgs implementation:
    1. Every element (j,bj) is such that both j and bj are unique,
       0 <= j < M, and 0 <= bj < p for some 32/64 bit prime p.
    2. All insertions will be completed before the first lookup is made.
*/


/* Set SHORT_HASHTABLE to limit hashtable elements to 0..(2^15-2), which
   allows the following information to fit in a short (uint16_t):
   bit 15 clear: single element in bits 0-14.
   bit 15 set: first element in bits 0-14 and more in the overflow list.
*/
#define SHORT_HASHTABLE 1

#define HASH_NOT_FOUND UINT32_MAX

#if SHORT_HASHTABLE
#define HASH_T uint16_t
#define HASH_BITS 16
#define HASH_DESC "short"
#define HASH_MINIMUM_SIZE 16
#else
#define HASH_T uint32_t
#define HASH_BITS 32
#define HASH_DESC "long"
#define HASH_MINIMUM_SIZE 8
#endif

#define HASH_MASK1 (1U<<(HASH_BITS-1))
#define HASH_MASK2 ((1U<<(HASH_BITS-1))-1)
#define HASH_MAX_ELTS HASH_MASK2

extern HASH_T *htable;
extern HASH_T *olist;
extern uint32_t hsize;
extern uint32_t hsize_minus1;
extern uint64_t *BJ64;
extern HASH_T empty_slot;

void init_hashtable(uint32_t M);
void fini_hashtable(void);

#if SHORT_HASHTABLE
static inline void clear_hashtable(void)
{
  if (sizeof(uint_fast32_t) == 8)
  {
    uint64_t pattern = (uint64_t)empty_slot << 16 | empty_slot;
    memset_fast32((uint_fast32_t *)htable, pattern << 32 | pattern, hsize/4);
  }
  else
    memset_fast32((uint_fast32_t *)htable,
                  (uint_fast32_t)empty_slot << 16 | empty_slot, hsize/2);
}
#else
static inline void clear_hashtable(void)
{
  if (sizeof(uint_fast32_t) == 8)
    memset_fast32((uint_fast32_t *)htable,
                  (uint64_t)empty_slot << 32 | empty_slot, hsize/2);
  else
    memset_fast32((uint_fast32_t *)htable,empty_slot,hsize);
}
#endif

static inline void insert64(uint32_t j, uint64_t bj)
{
  uint32_t slot;

  BJ64[j] = bj;
  slot = bj & hsize_minus1;
  if (htable[slot] == empty_slot)
    htable[slot] = j;
  else
  {
    olist[j] = htable[slot] ^ HASH_MASK1;
    htable[slot] = j | HASH_MASK1;
  }
}

static inline uint32_t lookup64(uint64_t bj)
{
  uint32_t slot;
  HASH_T elt;

  slot = bj & hsize_minus1;
  elt = htable[slot];
  if (BJ64[elt & HASH_MASK2] == bj)
    return elt & HASH_MASK2;
  if ((elt & HASH_MASK1) == 0)
    return HASH_NOT_FOUND;

  elt &= HASH_MASK2;
  do
  {
    if (BJ64[olist[elt] & HASH_MASK2] == bj)
      return olist[elt] & HASH_MASK2;
    elt = olist[elt];
  } while ((elt & HASH_MASK1) == 0);

  return HASH_NOT_FOUND;
}

#endif /* _HASHTABLE_H */
