/***************************  driver.c  **************************************

  Purpose:	Test driver program for boolean operations code.

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

  Notes:	This module contains a main program and accompanying
  		subroutines that, when compiled, exercise every function
		of the boolean operations module.  The testing is purely
		in terms of the external interface that the boolean
		operations module presents.  That is, the effects of
		constructor functions are examined through the projector
		functions.  This makes the code (almost) implementation-
		independent.  For this reason, it may be used (with slight
		modifications, controlled through #ifdef's) to test both
		the bit-vector and the hashing implementations.
		
  		Usage for the bit-vector version is:

			driver lower-bound upper-bound

		where the bounds specify the legal domain of the set.  In
		the current implementation, abs(upper-bound - lower-bound)
		must be less than 256.
			
**/

#include	<stdio.h>

#	include	"hash.h"

#define N_SETS		3

#define MIN_ELEMENTS	5		/* Make the tests "interesting". */
					/* Require at least 5 elements.	 */
int	hash(key)			/* Define the hashing function   */
	elementType	key;		/* as the sum of the squares of  */
{					/* each bit's magnitude, base 3. */
	int	i;
	int	threePower;
	int	sum;

	threePower = 1;
	sum = 0;
	for ( i = 0; i < 8*sizeof(elementType); i++ ) {
		register int	bitValue;
		bitValue = (key & 01)*threePower;
		bitValue *= bitValue;
		sum += bitValue;
		threePower *= 3;
	}

	return sum;
}

boolean compare(v1, v2)
	elementType	v1, v2;
{
	return v1 == v2;
}


void	Exit_If_Error_On();
void	Exit_Indicating_Failed_Op();


void	Test_Set_Operators();		/* The main routine is implemented  */
void	Test_Union_Operators();		/* as a series of calls to several  */
void	Test_Intersection_Operators();	/* procedures, each of which tests  */
void	Test_Subtraction_Operators();	/* a particular aspect of the	    */
void	Test_Copy();			/* module.			    */
void	Test_Iterate();

boolean	Iterator();


main(argc, argv)
	int	argc;
	char	*argv[];
{
	set	s[N_SETS];
	int	lower, upper;
	int	number_of_buckets;
	int	i;

	if ( argc != 4 ) {
		printf("Usage: %s lower-bound upper-bound number-of-buckets\n", argv[0]);
		exit(1);
	}

	lower = atoi(argv[1]);
	upper = atoi(argv[2]);
	if ( upper < lower+MIN_ELEMENTS ) {
		printf("Please specify a set that can contain at least %d elements.\n",
		       MIN_ELEMENTS);
		exit(1);
	}
	if ( (number_of_buckets = atoi(argv[3])) <= MIN_ELEMENTS ) {
		printf("The 1st argument must be an integer >= %d.\n",
		       MIN_ELEMENTS);
		exit(1);
	}
	

	for ( i = 0; i < N_SETS; i++ ) {	/* Test creation. */
		Create(number_of_buckets, hash, compare, &s[i]);
		Exit_If_Error_On("Create");
	}
	fputs("Creation works.\n", stdout);

	for ( i = 0; i < 2; i++ ) {		/* Test clearing sets. */
		Clear(&s[i]);
		Exit_If_Error_On("Clear");
		if ( Empty(&s[i]) )
			Exit_If_Error_On("Empty");
		else	Exit_Indicating_Failed_Op("Empty");
	}
	fputs("Clearing works.\n", stdout);

	Test_Set_Operators(s, lower, upper);
	fputs("Basic operators works.\n", stdout);
	Test_Union_Operators(s, lower, upper);
	fputs("Union works.\n", stdout);
	Test_Intersection_Operators(s, lower, upper);
	fputs("Intersection works.\n", stdout);
	Test_Subtraction_Operators(s, lower, upper);
	fputs("Subtraction works.\n", stdout);
	Test_Copy(s, lower, upper);
	fputs("Copying works.\n", stdout);
	Test_Iterate(s, lower, upper);
	fputs("Iterating works.\n", stdout);

	fputs("\nThe implementation passes.\n", stdout);
	exit(0);
}

void Test_Set_Operators(s, lower, upper)
	set	s[];
	int	lower, upper;
{
	int	i;
	
	for ( i = lower; i <= upper; i++ ) {	/* Test insertion of all    */
		Insert(&s[0], i);		/* valid elements for a     */
		Exit_If_Error_On("Insert");	/* set that doesn't already */
		if ( Member(&s[0], i) )		/* contain them.	    */
			Exit_If_Error_On("Member");
		else	Exit_Indicating_Failed_Op("Member");
	}

	for ( i = lower; i <= upper; i++ ) {	/* Test insertion of all */
		Insert(&s[0], i);		/* valid elements for a  */
		Exit_If_Error_On("Insert");	/* set that already has  */
		if ( Member(&s[0], i) )		/* them.		 */
			Exit_If_Error_On("Member");
		else	Exit_Indicating_Failed_Op("Member");
	}

	for ( i = lower; i <= upper; i++ ) {	/* Test deletion of all  */
		Delete(&s[0], i);		/* valid elements when   */
		Exit_If_Error_On("Delete");	/* the set already has	 */
		if ( ! Member(&s[0], i) )	/* them.		 */
			Exit_If_Error_On("Delete");
		else	Exit_Indicating_Failed_Op("Member");
	}

	for ( i = lower; i <= upper; i++ ) {	/* Test deletion of all  */
		Delete(&s[0], i);		/* valid elements when   */
		Exit_If_Error_On("Delete");	/* the set doesn't have	 */
		if ( ! Member(&s[0], i) )	/* them.		 */
			Exit_If_Error_On("Delete");
		else	Exit_Indicating_Failed_Op("Member");
	}
}

void Test_Union_Operators(s, lower, upper)
	set	s[];
	int	lower, upper;
{
	int	i;

	Insert(&s[0], lower);			/* Test union of a set    */
	Exit_If_Error_On("Insert");		/* with an empty set.	  */
	Unite(&s[0], &s[1], &s[2]);		/*   s2 <- {lower}U{}	  */
	Exit_If_Error_On("Unite");
	if ( Member(&s[2], lower) )
		Exit_If_Error_On("Member");
	else	Exit_Indicating_Failed_Op("Member|Unite");
	for ( i = lower+1; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Unite");
		else	Exit_If_Error_On("Member");

	Insert(&s[1], lower+1);		    /* Test union of two non-    */
	Exit_If_Error_On("Insert");	    /* empty, non-intersecting   */
	Unite(&s[0], &s[1], &s[2]);	    /* sets.			 */
	Exit_If_Error_On("Unite");	    /*   s2 <- {lower}U{lower+1} */
	if ( Member(&s[2], lower) && Member(&s[2], lower+1) )
		Exit_If_Error_On("Member");
	else	Exit_Indicating_Failed_Op("Member|Unite");
	for ( i = lower+2; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Unite");
		else	Exit_If_Error_On("Member");

					/* Test union of two non-empty,   */
	Insert(&s[0], lower+1);		/* intersecting sets.		  */
	Exit_If_Error_On("Insert");	/*   s2 <- {lower,lower+1} U	  */
	Unite(&s[0], &s[1], &s[2]);	/*		{lower+1}	  */
	Exit_If_Error_On("Unite");
	if ( Member(&s[2], lower) && Member(&s[2], lower+1) )
		Exit_If_Error_On("Member");
	else	Exit_Indicating_Failed_Op("Member|Unite");
	for ( i = lower+2; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Unite");
		else	Exit_If_Error_On("Member");
}

void Test_Intersection_Operators(s, lower, upper)
	set	s[];
	int	lower, upper;
{
	int	i;

	Clear(&s[0]);
	Clear(&s[1]);

	Insert(&s[0], lower);			/* Test intersection of a */
	Exit_If_Error_On("Insert");		/* set with an empty set. */
	Intersect(&s[0], &s[1], &s[2]);		/*   s2 <- {lower}^{}	  */
	Exit_If_Error_On("Intersect");
	for ( i = lower; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Intersect");
		else	Exit_If_Error_On("Member");

	Insert(&s[0], lower);		    /* Test intersection of two    */
	Exit_If_Error_On("Insert");	    /* non-empty, non-intersecting */
	Insert(&s[1], lower+1);		    /* sets.		   	   */
	Exit_If_Error_On("Insert");	    /*   s2 <- {lower}^{lower+1}   */
	Intersect(&s[0], &s[1], &s[2]);
	Exit_If_Error_On("Intersect");
	for ( i = lower; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Intersect");
		else	Exit_If_Error_On("Member");

	Insert(&s[0], lower);		    /* Test intersection of two non- */
	Exit_If_Error_On("Insert");	    /* non-empty, intersecting sets. */
	Insert(&s[0], lower+1);		    /*   s2 <- {lower,lower+1}^	     */
	Exit_If_Error_On("Insert");	    /*		  {lower,lower+2}    */
	Clear(&s[1]);
	Insert(&s[1], lower);
	Exit_If_Error_On("Insert");
	Insert(&s[1], lower+2);
	Exit_If_Error_On("Insert");
	Intersect(&s[0], &s[1], &s[2]);
	Exit_If_Error_On("Intersect");
	if ( Member(&s[2], lower) )
		Exit_If_Error_On("Member");
	else	Exit_Indicating_Failed_Op("Member|Intersect");
	for ( i = lower+1; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Intersect");
		else	Exit_If_Error_On("Member");
}

void Test_Subtraction_Operators(s, lower, upper)
	set	s[];
	int	lower, upper;
{
	int	i;

	Clear(&s[0]);				/* Subtract one empty set   */
	Clear(&s[1]);				/* from another.	    */
	Subtract(&s[0], &s[1], &s[2]);
	Exit_If_Error_On("Subtract");
	for ( i = lower; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Subtract");
		else	Exit_If_Error_On("Member");
/*
/* Added by Chris Fox to increase branch coverage
*/
	Insert(&s[0], lower);		    /* Test subtraction of two non-  */
	Exit_If_Error_On("Insert");	    /* non-empty, intersecting sets. */
	Insert(&s[0], lower+1);		    /*   s2 <- {lower,lower+1}-	     */
	Exit_If_Error_On("Insert");	    /*		  {lower,lower+2}    */
	Clear(&s[1]);
	Insert(&s[1], lower);
	Exit_If_Error_On("Insert");
	Insert(&s[1], lower+2);
	Exit_If_Error_On("Insert");
	Subtract(&s[0], &s[1], &s[2]);
	Exit_If_Error_On("Intersect");
	if ( Member(&s[2], lower+1) )
		Exit_If_Error_On("Member");
	else	Exit_Indicating_Failed_Op("Member|Subtract");
	if ( Member(&s[2], lower) )
		Exit_Indicating_Failed_Op("Member|Subtract");
		else	Exit_If_Error_On("Member");
	for ( i = lower+2; i <= upper; i++ )
		if ( Member(&s[2], i) )
			Exit_Indicating_Failed_Op("Member|Intersect");
		else	Exit_If_Error_On("Member");

}

void Test_Copy(s, lower, upper)
	set	s[];
	int	lower, upper;
{
	int	i;
	
	Clear(&s[0]);				 /* Create a set with every */
	for ( i = lower; i <= upper; i += 3 ) {	 /* third possible value,   */
		Insert(&s[0], i);		 /* copy it, and make sure  */
		Exit_If_Error_On("Insert");	 /* both sets have the same */
	}					 /* members.		    */

	Copy(&s[0], &s[1]);

	for ( i = lower; i <= upper; i++ )
		if ( Member(&s[0], i) != Member(&s[1], i) )
			Exit_Indicating_Failed_Op("Member|Copy");
		else	Exit_If_Error_On("Member");
}

int	Count_Of_Elements_Iterated_Over;

boolean Iterator(e)
	int	e;
{
	if ( e % 2 != 0 )	/* passed a value that wasn't inserted. */
		Exit_Indicating_Failed_Op("Iterate");
	Count_Of_Elements_Iterated_Over++;
	return TRUE;
}

void Test_Iterate(s, lower, upper)
	set	s[];
	int	lower, upper;
{
	int	i;
	int	Count_Of_Elements_Added;

	Count_Of_Elements_Added = Count_Of_Elements_Iterated_Over = 0;

	Clear(&s[0]);
	for ( i = (lower/2)*2 + 2; i <= upper; i += 2 ) {
		Insert(&s[0], i);		/* Insert all even values */
		Exit_If_Error_On("Insert");	/* (except perhaps the	  */
		Count_Of_Elements_Added++;	/* first) into the set.   */
	}

	Iterate(&s[0], Iterator);
	Exit_If_Error_On("Iterate");
	if ( Count_Of_Elements_Added != Count_Of_Elements_Iterated_Over )
		Exit_Indicating_Failed_Op("Iterate");
}

void Exit_If_Error_On(operation)
	char	operation[];
{
	if ( ! Error_Occurred() )
		return;
	printf("%s operation caused an error.\n", operation);
	exit(1);
}


void Exit_Indicating_Failed_Op(operation)
	char	operation[];
{
	printf("%s operation failed to function correctly.\n", operation);
	exit(1);
}
