/***************************  hashing-bool-ops.c  *****************************

  Purpose:	Hashing-based implementation of boolean operations.

  Provenance:	Written and tested by S. Wartik, April 1991.

  Notes:	None.

**/

#include	<stdio.h>
#include	"hash.h"

static boolean error_occurred;	/* TRUE iff an error occurred on the	*/
				/* last call to a boolean operator.	*/

#define hash(s,e) (abs((*((s)->hashing_function))(e)) % (s)->Number_Of_Buckets)

extern char	*malloc();

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

  	Create(lower, upper, s)

  Returns:	void

  Purpose:	Create a hashing-based implementation of a set, using
  		application-provided specifications of the details
		of hashing (function, comparison routine, and number of
		buckets).

  Plan:		Set the values of "s" to the parameters, and allocate
  		global storage for the buckets.

  Notes:	This routine must be called before any of the other
  		routines (save error_occurred()) will work.

**/

void Create(Number_Of_Buckets, Hashing_Function, Comparator, s)
    int     Number_Of_Buckets;
    int     (*Hashing_Function)();
    boolean (*Comparator)();
    set     *s;
{
    register unsigned int    Bucket_Array_Size;
    register int    	     i;

    if ( Number_Of_Buckets <= 0 ) {
        error_occurred = TRUE;
        return;
    }
    s->Number_Of_Buckets = Number_Of_Buckets;
    s->hashing_function = Hashing_Function;
    s->comparator = Comparator;
    Bucket_Array_Size = sizeof(bucket_element) * Number_Of_Buckets;
    s->buckets = (bucket_element **)malloc(Bucket_Array_Size);
    if ( error_occurred = (s->buckets == NULL) )
        return;
    for ( i = 0; i < Number_Of_Buckets; i++ )
        s->buckets[i] = (bucket_element *)NULL;

}

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

  	Clear(s)

  Returns:	void

  Purpose:	Indicate that set s is empty.

  Plan:		Set all buckets of s to NULL.

  Notes:	The Create() function does not automatically clear
  		a set; this routine must be called to do so.

**/

void Clear(s)
        set     *s;
{
    register int            i;
    register bucket_element *b, *next_b;

    for ( i = 0; i < s->Number_Of_Buckets; i++ )
        if ( s->buckets[i] != NULL ) {
            b = s->buckets[i];
            while ( b != NULL ) {
                next_b = b->next_datum;
                free( (char *)b );
                b = next_b;
            }
            s->buckets[i] = NULL;
        }
    error_occurred = FALSE;
}


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

  	Insert(s, e)

  Returns:	void

  Purpose:	Insert element e into set s.

  Plan:		Hash the element to its bucket, and insert it into
  		the linked list for that bucket.

  Notes:	e may already be in s; if so, it is not added.

**/

void Insert(s, e)
    set             *s;
    elementType     e;
{
    register bucket_element *b, *New_Element;
    register int            bucket;

    error_occurred = FALSE;

    bucket = hash(s,e);
    for ( b = s->buckets[bucket]; b != NULL ; b = b->next_datum )
        if ( (*(s->comparator))(b->datum, e) )
            return;

    New_Element = (bucket_element *)malloc(sizeof (bucket_element));
    if ( New_Element == NULL ) {
        error_occurred = TRUE;
        return;
    }
    New_Element->datum = e;
    New_Element->next_datum = s->buckets[bucket];
    s->buckets[bucket] = New_Element;
}

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

  	Delete(s, e)

  Returns:	void

  Purpose:	Delete element e from set s.

  Plan:		Find the bucket in which e should reside, search for
  		it in the bucket's list, and, if it's there, remove
		it from the list.

  Notes:	e doesn't have to be in s.

**/


void Delete(s, e)
        set             *s;
        elementType     e;
{
    register bucket_element *b, *previous;
    register int            bucket;

    error_occurred = FALSE;

    bucket = hash(s, e);
    if ( (b = s->buckets[bucket]) == NULL )
        return;
    if ( (*(s->comparator))(b->datum, e) )
        s->buckets[bucket] = b->next_datum;
    else {
        while ( b->next_datum != NULL ) {
            if ( (*(s->comparator))(b->datum, e) )
                break;
            previous = b;
            b = b->next_datum;
        }
        if ( b == NULL )
            return;
        previous->next_datum = b->next_datum;
    }
    free( (char *)b );
}

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

  	Empty(s)

  Returns:	boolean

  Purpose:	Determine whether a set is empty: return TRUE iff it is.

  Plan:		Any non-NULL bucket indicates the set is not empty; test
  		for that.

  Notes:	None.

**/


boolean Empty(s)
    set     *s;
{
    register int i;

    error_occurred = FALSE;
    for ( i = 0; i < s->Number_Of_Buckets; i++ )
        if ( s->buckets[i] != NULL )
            return FALSE;
    return TRUE;
}

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

  	Member(s, e)

  Returns:	boolean

  Purpose:	Determine if e is a member of s: return TRUE iff it is.

  Plan:		Look for e in the list for bucket hash(s, e).

  Notes:	None.

**/


boolean Member(s, e)
        set             *s;
        elementType     e;
{
    register bucket_element *b;
    register int            bucket;

    error_occurred = FALSE;

    bucket = hash(s, e);
    for ( b = s->buckets[bucket]; b != NULL ; b = b->next_datum )
        if ( (*(s->comparator))(b->datum, e) )
            return TRUE;
    return FALSE;
}

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

  	Unite(s1, s2, s3)

  Returns:	void

  Purpose:	Return in s3 the union of sets s1 and s2.

  Plan:		Copy s1 into s3, and then use Insert() to add
  		elements of s2 to s3.

  Notes:	None.

**/

void Unite(s1, s2, s3)
    set     *s1, *s2;
    set     *s3;
{
    register int            i;
    register bucket_element *b;

    Copy(s1, s3);
    if ( Error_Occurred() )
        return;

    for ( i = 0; i < s2->Number_Of_Buckets; i++ ) {
        if ( s2->buckets[i] == NULL )
            continue;
        for ( b = s2->buckets[i]; b != NULL; b = b->next_datum )
            if ( ! Member(s3, b->datum) ) {
                Insert(s3, b->datum);
                if ( Error_Occurred() )
                   return;
	    }
    }
    error_occurred = FALSE;
}

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

  	Intersect(s1, s2, s3)

  Returns:	void

  Purpose:	Return in s3 the intersection of s1 and s2.

  Plan:		Clear s3.  Then, for each bucket in s1, search
  		through its elements, testing if each element is
		in s2; if so, add that element to s3.

  Notes:	None.

**/

void Intersect(s1, s2, s3)
    set     *s1, *s2;
    set     *s3;
{
    register int            i;
    register bucket_element *b;

    Clear(s3);

    for ( i = 0; i < s1->Number_Of_Buckets; i++ ) {
        if ( s1->buckets[i] == NULL )
            continue;
        for ( b = s1->buckets[i]; b != NULL; b = b->next_datum )
            if ( Member(s2, b->datum) ) {
                Insert(s3, b->datum);
                if ( Error_Occurred() )
                    return;
            }
    }
    error_occurred = FALSE;
}

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

  	Subtract(s1, s2, s3)

  Returns:	void

  Purpose:	Return in s3 the difference between s1 and s2 (i.e., s1-s2).

  Plan:		Clear s3.  Then, for each element in s1, insert it in
  		s3 only if it's not in s2.

  Notes:	None.

**/

void Subtract(s1, s2, s3)
    set     *s1, *s2;
    set     *s3;
{
    register int            i;
    register bucket_element *b;

    Clear(s3);

    for ( i = 0; i < s1->Number_Of_Buckets; i++ ) {
        if ( s1->buckets[i] == NULL )
            continue;
        for ( b = s1->buckets[i]; b != NULL; b = b->next_datum )
            if ( ! Member(s2, b->datum) ) {
                Insert(s3, b->datum);
                if ( Error_Occurred() )
                    return;
            }
    }
    error_occurred = FALSE;
}

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

  	Copy(source, destination)

  Returns:	void

  Purpose:	Create a copy of a set.

  Plan:		Clear the destination set.  Then, for each element of
  		the source, create a copy by finding its hash address
		in the destination and inserting it there.

  Notes:	The source and destination sets don't have to have the
  		same number of buckets.

**/

void Copy(source, destination)
    set	*source, *destination;
{
    register int                i, h;
    register bucket_element     *e, *b;
    
    Clear(destination);
    for ( i = 0; i < source->Number_Of_Buckets; i++ ) {
        if ( source->buckets[i] == NULL )
            continue;
        for ( b = source->buckets[i]; b != NULL; b = b->next_datum ) {
            h = hash(destination, b->datum);
            e = (bucket_element *)malloc(sizeof (bucket_element));
            if ( e == NULL ) {
                error_occurred = TRUE;
                return;
            }
            e->datum = b->datum;
            e->next_datum = destination->buckets[h];
            destination->buckets[h] = e;
        }
    }
    error_occurred = FALSE;
}

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

  	Iterate(s, f)

  Returns:	void

  Purpose:	Perform some application-defined function f on each element
  		of set s.  The function f() should be of the form:

			boolean f(e)
			    elementType	e;

		The iteration will continue as long as more elements
		remain in the set and f() returns TRUE.

  Plan:		Scan the buckets in sequence; for each one that is not
  		NULL, invoke the iterator function supplied during
		Create() on each element in that bucket's list.

  Notes:	There is no guaranteed order in which the elements are
  		passed to f().

**/

void Iterate(s, f)
    set     *s;
    boolean (*f)();
{
    register int    		i;
    register bucket_element	*b;

    error_occurred = FALSE;

    for ( i = 0; i < s->Number_Of_Buckets; i++ )
	for ( b = s->buckets[i]; b != NULL; b = b->next_datum )
            if ( ! (*f)(b->datum) )
                return;
}

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

  	Error_Occurred()

  Returns:	boolean

  Purpose:	Indicate whether the last operation could not be
  		completed due to some error.

  Plan:		The routine's value is the value of the local variable
  		"error_occurred".

  Notes:	It is the responsibility of each routine to correctly
  		set "error_occurred".

**/

boolean Error_Occurred()
{
    return error_occurred;
}
