/*===========================================================================
  Copyright (C) 1988-2009 European Southern Observatory (ESO)
 
  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.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++
.TYPE		Module
.IDENTIFICATION hash.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Hash table
.ENVIRONMENT	Any
.COMMENTS	
\begin{TeX}
\end{TeX}
.VERSION   1.0	18-Apr-1988: Creation

 090831		last modif
-----------------------------------------------------------------------*/

#define	DEBUG		1	/* For debugging only		*/

#define	PM_LEVEL 	LEVEL_STR	/* Only string-like functions 	*/

#include <midas_def.h>
#define  PASCAL_DEF	0	/* Don't include pascalisation ... */
#include <stesodef.h>
#include <hash.h>


extern int pm_enter(), pm_ed_i(), pm_iexit(), pm_ed_tr2();
extern int mm_free();


#define FINISH	goto FIN

MID_STATIC int jndex;
MID_STATIC H_ITEM *previous;
MID_STATIC int factor = 2;		/* seems to be the best factor... */

/*==========================================================================*/
int h_factor(n)
/*+++
.PURPOSE Set factor for computation of index
.RETURNS n
.REMARKS To modify for better hashing function
---*/
	int	n;		/* IN: Factor	*/
{
  factor = n;
  if (factor < 1) 	factor = 1;
  
  return(n);
}

/*==========================================================================*/
static int h_index(ht, symbol, len)
/*+++
.PURPOSE Compute index from symbol
.RETURNS The index (-1 for error)
.REMARKS Simple addition of ASCII values.
---*/
	H_TABLE	*ht;		/* IN: Descriptor of Tex table	*/
	char	*symbol;	/* IN: Symbol 	*/
	int	len;		/* IN: length of symbol		*/
{
	register int i;
	register char *p;

  for (jndex = 0, p = symbol, i = len; --i >=0; p++)
	jndex = ((jndex * factor) + (*p & 077)) % ht->size;

#if 0
  for (jndex = 0, p = symbol, i = len; --i >=0; p++)
	jndex += (*p & 0177);
  jndex %= ht->size;
#endif

  return(jndex);
}

/*==========================================================================*/
H_TABLE *h_create(size)
/*+++
.PURPOSE Create a Hash table.
.RETURNS Address of create table (NULL if failed)
.REMARKS Size is adjusted to have no divisor smaller than 19.
---*/
	int		size;		/* IN: size (number of elements)*/
{ 	
	register H_TABLE *ht;
	register int l;

  ENTER("*h_create");

  TRACE_ED_I("Proposed size is: ", size);

	/* Compute a number which has no divisor by 2,3,5,7,11,13,17,19 */

  for ( l = (size & 1 ? size : size+1); ; l += 2)
  {	if ( (l% 3) == 0)	continue;
	if ( (l% 5) == 0)	continue;
	if ( (l% 7) == 0)	continue;
	if ( (l%11) == 0)	continue;
	if ( (l%13) == 0)	continue;
	if ( (l%17) == 0)	continue;
	if ( (l%19) == 0)	continue;
	break;
  }

  TRACE_ED_I("Appropriate size is: ",l);

  ht = (H_TABLE *)mm_alloc(sizeof(H_TABLE) + (l-1)*sizeof(H_ITEM *));
  if(ht)
  {	ht->size = l;
	ht->symbols    = 0;
	ht->collisions = 0;
	while (--l >= 0)	ht->start[l] = NULL_PTR(H_ITEM);
  }

  EXIT_PTR(H_TABLE, ht);
}  

/*==========================================================================*/
int h_clear(ht)
/*+++
.PURPOSE Clear a h-table
.RETURNS OK 
.REMARKS 
---*/
	H_TABLE	*ht;		/* IN: Descriptor of Hash table to clear */
{
	register H_ITEM *pi, *pin;
	register int i;

  ENTER("h_clear");

  for (i=0; i < ht->size; i++)
  {	for (pi = ht->start[i]; pi; pi = pin)
		pin = pi->next, MEM_FREE(pi);
	ht->start[i] = NULL_PTR(H_ITEM);
  }
  ht->collisions = 0;
  ht->symbols    = 0;

  EXIT(OK);
}

/*==========================================================================*/
H_ITEM *h_look(ht, symbol, len)
/*+++
.PURPOSE Look for a specified symbol in the table.
.RETURNS Address of found item, or NULL
.REMARKS Use hash-table
---*/
	H_TABLE	*ht;		/* IN: Descriptor of Hash table	*/
	char	*symbol;	/* IN: symbol to retrieve	*/
	int len;		/* IN: Length of symbol		*/
{
	register H_ITEM *pi;

  ENTER("*h_look");

#if DEBUG
  TRACE_ED_STR2("Looking for: ", symbol, len);
#endif

  pi = ht->start[h_index(ht, symbol, len)];		/* Position	*/
  for ( previous = NULL_PTR(H_ITEM); pi; previous = pi, pi = pi->next)
  {	if (pi->ls != len)	continue;
	if (oscomp(symbol, &(pi->strings[0]), len) == 0)
		break;
  }

  EXIT_PTR(H_ITEM, pi);
}

/*==========================================================================*/
int h_remove(ht, symbol, len)
/*+++
.PURPOSE Remove an item from table.
.RETURNS OK / NOK (didn't exist)
.REMARKS 
---*/
	H_TABLE	*ht;		/* IN: Descriptor of Tex table	*/
	char	*symbol;	/* IN: Symbol to remove	*/
	int	len;		/* IN: Length of symbol	*/
{ 	
	register H_ITEM *pi;
	int st;
  
  ENTER("h_remove");

  st = NOK;
  if_not(pi = h_look(ht, symbol, len))	FINISH;
  
  if (previous)					/* Modify Links	*/
  	previous->next 	 = pi->next;
  else	ht->start[jndex] = pi->next;

  ht->symbols -= 1;
  if (ht->start[jndex])	 ht->collisions -= 1;	/* Update collisions	*/
  st = OK;

  MEM_FREE(pi);				/* Free Allocated Memory	*/

  FIN:
  EXIT(st);
}

/*==========================================================================*/
H_ITEM *h_add(ht, symbol, ls, eq, leq)
/*+++
.PURPOSE Insert the item (symboling, equivalence) in the table.
.RETURNS Address of allocated item
.REMARKS If symbol already exists, it's modified.
---*/
	H_TABLE	*ht;		/* IN: Descriptor of Tex table	*/
	char	*symbol;	/* IN: Symbol to insert	*/
	int	ls;		/* IN: Length of Symbol		*/
	char 	*eq;		/* IN: Equivalence string	*/
	int	leq;		/* IN: Length of Equivalence String	*/
{ 	
	register H_ITEM *pi;
	register char *p;

  ENTER("*h_add");

#if DEBUG
  TRACE_ED_STR2("Insert: ", symbol, ls);
  TRACE_ED_STR2("Equate: ", eq, leq);
#endif

	/* 1. Check if symbol already exists. 
		If not, create it.		*/

  pi = h_look(ht, symbol, ls);
  if(pi)
  {  	if (pi->leq == leq)	goto REPLACE;
	h_remove(ht, symbol, ls);
	pi = h_look(ht, symbol, ls);
  }

	/* 2. Allocate H_ITEM: strings are terminated with EOS	*/

  if_not(pi = (H_ITEM *) mm_alloc(leq + ls + sizeof(H_ITEM)))
  	FINISH;
  pi->next = NULL_PTR(H_ITEM);
  ht->symbols += 1;

  if (previous)
  	previous->next 	 = pi, ht->collisions += 1;
  else	ht->start[jndex] = pi;


	/* 3. Fill H_ITEM, and copy strings	*/
	
  REPLACE:
  pi->leq = leq;
  pi->ls  = ls;
  p = &(pi->strings[0]);
  p += oscopy(p, symbol, ls);
  *(p++) = EOS;
  p += oscopy(p, eq, leq);
  *(p++) = EOS;
  
  FIN:
  EXIT_PTR(H_ITEM, pi);
}

/*==========================================================================*/
char *h_get(ht, symbol, len)
/*+++
.PURPOSE Retrieve the string equated to a given symbol
.RETURNS Address of equivalence string, or NULL
.REMARKS 
---*/
	H_TABLE	*ht;		/* IN: Descriptor of Tex table	*/
	char	*symbol;	/* IN: symbol to retrieve	*/
	int 	len;		/* IN: Length of symbol		*/
{ 	
	register H_ITEM *pi;
	char *r;

  ENTER("*h_get");

#if DEBUG
  TRACE_ED_STR2("Looking for an equivalence to: ", symbol, len);
#endif

  pi = h_look(ht, symbol, len);
  if(pi)
  	r = &(pi->strings[1+pi->ls]);
  else	r = NULL_PTR(char);

#if DEBUG
  if(r)
  TRACE_ED_STR2("===================>", r, pi->leq);
#endif

  EXITp(r);
}

/*==========================================================================*/
int h_log(ht)
/*+++
.PURPOSE Log the statistics for a hash table.
.RETURNS Number of free symbols.
.REMARKS Not traced.
---*/
	H_TABLE	*ht;		/* IN: Descriptor of Tex table	*/
{ 	
	int	n, i;

  for (n = 0, i = ht->size; --i >= 0; )
	if_not(ht->start[i])	n++;	/* Free entries */

  LOG_ED_I("Size of h-table: ", ht->size);
  LOG_ED_I(" Unused entries: ", n);
  LOG_ED_I("  Total symbols: ", ht->symbols);
  LOG_ED_I("     Collisions: ", ht->collisions);

  return(n);
}

