/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
#ifndef NORCSID
static char rcsid[]= "$Header$";
#endif

#include "assert.h"
#include "param.h"
#include "set.h"
#include <stdio.h>

/*
 * This file implements the marriage thesis from Hall.
 * The thesis says that given a number, say N, of subsets from
 * a finite set, it is possible to create a set with cardinality N,
 * that contains one member for each of the subsets,
 * iff for each number, say M, of subsets from 2 to N the union of
 * each M-tuple sets has cardinality >= M.
 *
 * So what, you might say. As indeed I did.
 * But this is actually used here to check the possibility of each
 * code rule. If a code rule has a number of token_sets in the with
 * clause and a number of properties in the uses rule it must be
 * possible to do this from an empty fakestack. Hall helps.
 */

#define MAXHALL (TOKPATMAX+MAXALLREG)
short hallsets[MAXHALL][SETSIZE];
int nhallsets= -1;
int hallfreq[MAXHALL][2];

hallverbose() {
	register i;
	register max;
	
	fprintf(stderr,"Table of hall frequencies\n   #   pre   post\n");
	for (max=MAXHALL-1;hallfreq[max][0]==0 && hallfreq[max][1]==0;max--)
		;
	for (i=0;i<=max;i++)
		fprintf(stderr,"%3d%6d%6d\n",i,hallfreq[i][0],hallfreq[i][1]);
}

inithall() {

	assert(nhallsets == -1);
	nhallsets=0;
}

nexthall(sp) register short *sp; {
	register i;
	
	assert(nhallsets>=0);
	for(i=0;i<SETSIZE;i++)
		hallsets[nhallsets][i] = sp[i];
	nhallsets++;
}

card(sp) register short *sp; {
	register sum,i;
	
	sum=0;
	for(i=0;i<8*sizeof(short)*SETSIZE;i++)
		if (BIT(sp,i))
			sum++;
	return(sum);
}

checkhall() {

	assert(nhallsets>=0);
	if (!hall())
		error("Hall says: \"You can't have those registers\"");
}

hall() {
	register i,j,k;
	int ok;
	
	hallfreq[nhallsets][0]++;
	/*
	 * If a set has cardinality >= nhallsets it can never be the cause
	 * of the hall algorithm failing. So it can be thrown away.
	 * But then nhallsets is less, so this step can be re-applied.
	 */

	do {
		ok = 0;
		for(i=0;i<nhallsets;i++)
			if (card(hallsets[i])>=nhallsets) {
				for (j=i+1;j<nhallsets;j++)
					for(k=0;k<SETSIZE;k++)
						hallsets[j-1][k] =
							hallsets[j][k];
				nhallsets--;
				ok = 1;
				break;
			}
	} while (ok);
	
	/*
	 * Now all sets have cardinality < nhallsets
	 */
	
	hallfreq[nhallsets][1]++;
	ok=recurhall(nhallsets,hallsets);
	nhallsets = -1;
	return(ok);
}

recurhall(nhallsets,hallsets) short hallsets[][SETSIZE]; {
	short copysets[MAXHALL][SETSIZE];
	short setsum[SETSIZE];
	register i,j,k,ncopys;
	
	/*
	 * First check cardinality of union of all
	 */
	for(k=0;k<SETSIZE;k++)
		setsum[k]=0;
	for(i=0;i<nhallsets;i++)
		unite(hallsets[i],setsum);
	if (card(setsum)<nhallsets)
		return(0);
	/*
	 * Now check the hall property of everything but one set,
	 * for all sets
	 */
	for(i=0;i<nhallsets;i++) {
		ncopys=0;
		for(j=0;j<nhallsets;j++) if (j!=i) {
			for(k=0;k<SETSIZE;k++)
				copysets[ncopys][k] = hallsets[j][k];
			ncopys++;
		}
		assert(ncopys == nhallsets-1);
		if (!recurhall(ncopys,copysets))
			return(0);
	}
	return(1);
}

unite(sp,into) register short *sp,*into; {
	register i;
	
	for(i=0;i<SETSIZE;i++)
		into[i] |= sp[i];
}

/*
 * Limerick (rot13)
 *
 * N zngurzngvpvna anzrq Unyy
 * Unf n urknurqebavpny onyy,
 * 	Naq gur phor bs vgf jrvtug
 * 	Gvzrf uvf crpxre'f, cyhf rvtug
 * Vf uvf cubar ahzore -- tvir uvz n pnyy..
 */