/****************************  support.c  **********************************

   Purpose:	Provide some useful support routines:

   		    --	Storage allocators that exit on error (since this
		    	isn't a subroutine library, there's no need for
			fancy error-handling).

		    --	A routine to write the MPHF to a file.

		    --	A routine to verify the correctness of a MPHF.

   Provenance:	Written and tested by Q. Chen and E. Fox, March 1991.
   		Edited and tested by S. Wartik, April 1991.

   Notes:	None.
**/

#include <stdio.h>

#include "types.h"

#ifdef __STDC__

extern char	*malloc( unsigned int size );
extern char	*realloc ( char *area, unsigned int size );

extern void	exit();

#else

extern char	*malloc(),
		*realloc();

extern void	exit();

#endif

/*************************************************************************

	owncalloc( n, size )

  Return:	char * -- Pointer to a chunk of memory.

  Purpose:	Allocate a chunk of memory of 'n' elements each of size 'size'.
		Return the pointer to the chunk.  Abort if no space is available.
**/

char *owncalloc( n, size )
    int		n,      /* in: number of elements.   */
		size;   /* in: size of each element. */
{
    char	*temp;

    if ( (temp = malloc( (unsigned int)(n*size) )) == 0 ) {
	fputs("Panic: cannot allocate memory.\n", stderr);
	exit(1);
    }
    return(temp);
}

/*************************************************************************

	ownrealloc( n, size )

  Return:	char * -- Pointer to a chunk of memory.

  Purpose:	Re-allocate a chunk of memory pointed to by area -- make it
  		new_size bytes.  Abort if no space is available.
**/

char *ownrealloc( area, new_size )
    char	*area;		/* in: area to re-allocate. */
    int		new_size;	/* in: new size.	    */
{
    char	*temp;

    if ( (temp = realloc( area, (unsigned)new_size )) == 0 ) {
	fputs("Panic: cannot reallocate memory.\n", stderr);
	exit(1);
    }
    return(temp);
}

/*************************************************************************

	write_gfun( arcs, vertices, tbl_seed, spec_file )

  Return:	void
  
  Purpose:	Write the MPHF specification to a file
**/

void
write_gfun( arcs, vertices, tbl_seed, spec_file )
    arcsType	    *arcs;      /* in: the arcs.                     		 */
    verticesType    *vertices;  /* in: the vertices.                 		 */
    int		    tbl_seed;   /* in: seed used to set up random number tables. */
    char	    *spec_file; /* in: name of the specification file.           */
{
    int		i;		/* Iterator through vertices.			*/
    FILE	*fp;		/* Handle for specification file.		*/

    if ( (fp = fopen(spec_file, "w")) == NULL ) {
	   fprintf(stderr, "Can't create hashing specification file \"%s\".\n",
			   spec_file);
	   exit(1);
    }

    fprintf(fp, "%d\n%d\n%d\n",
	       arcs->no_arcs, vertices->no_vertices, tbl_seed);
    for ( i = 0; i < vertices->no_vertices; i++ )
	fprintf(fp, "%d\n", vertices->vertexArray[i].g);

    fclose(fp);
}

/*************************************************************************

	verify_mphf( arcs, vertices )

  Return:	int -- NORM if MPHF is correct, ABNORM if not.

  Purpose:	Verify the computed MPHF is indeed minimal and perfect
**/

int verify_mphf( arcs, vertices )
    arcsType	 *arcs;		       	/* in: the arcs.     */
    verticesType *vertices;		/* in: the vertices. */
{
    int  	i,
		status = NORM,
		hash;        		/* Hash value of a key. */
    char	*disk;       		/* Hash table.          */

    disk = owncalloc( arcs->no_arcs, sizeof(char) );

    for( i = 0; i < arcs->no_arcs; disk[i++] = EMPTY );

    for ( i = 0; i < arcs->no_arcs; i++ ) {
	hash = abs( arcs->arcArray[i].h0 +
		    vertices->vertexArray[arcs->arcArray[i].h12[0]].g +
		    vertices->vertexArray[arcs->arcArray[i].h12[1]].g
		  ) % arcs->no_arcs ;

	if ( hash < 0 ) {
	    fprintf(stderr, "Panic: negative hash value.\n");
	    status = ABNORM;
	    break;
	}

	if ( disk[hash] == FULL ) {
	    fprintf(stderr, "Panic: hash entry collided at");
	    fprintf(stderr, " position %d by the %dth word!\n", hash, i);
	    status = ABNORM;
	    break;
	}
	else
	    disk[hash] =  FULL;
    }

    free( (char *)disk );

    return(status);
}



