/*************************** hashing-bool-ops.c ***************************** Purpose: Hashing-based implementation of boolean operations. Provenance: Written and tested by S. Wartik, April 1991. Notes: None. **/ #include #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; }