#include "../pnheaders/general.h"
#include "../pnheaders/matrix.h"
#include "spnbox.h"

static int CheckParams(matrix *Dm, matrix *Dp, void *PlaceSet, int MultiplePlaceSets);
static inline int PlaceSets(void* PlaceSet, int MultiplePlaceSets);
static inline int IsPlaceFlagged(int Place, int SetIndex, void* PlaceSet, int MultiplePlaceSets);

issiph_r issiph(matrix *Dm, matrix *Dp, void *PlaceSet, int MultiplePlaceSets)
{
	issiph_r result;
	int i, j, k, in, out;
	
	memset(&result, 0, sizeof(issiph_r));
	/*Check parameters and fail if they are bad.*/
	if (! CheckParams(Dm, Dp, PlaceSet, MultiplePlaceSets))
	{
		return result;
	}
	
	/*Allocate space and initialize the result set (no flags set by default). The
	overall flag is cleared by default.*/
	result.flag = tcalloc(PlaceSets(PlaceSet, MultiplePlaceSets), sizeof(int));
	
	/*Iterate through all the place sets to be checked.*/
	for (i = 0; i < PlaceSets(PlaceSet, MultiplePlaceSets); i++)
	{
		/*The flag for the place set should be set by default.*/
		result.flag[i] = 1;
		/*Iterate through each transition and examine it.*/
		for (j = 0; j < NumberOfColumns(*Dm); j++)
		{
			/*For each transition, find out whether there are
			(a) any input arcs coming from places in the current set.
			(b) any output arcs coming from places in the current set.*/
			in = 0;
			out = 0;
			for (k = 0; k < NumberOfRows(*Dm); k++)
			{
				if (IsPlaceFlagged(k, i, PlaceSet, MultiplePlaceSets))
				{
					if (GetMatrixEl(Dp, k, j)) in = 1;
					if (GetMatrixEl(Dm, k, j)) out = 1;
				}
			}
			/*If in is set and out is cleared, the current transition is such that
			it feeds at least one of the current set of places without being fed by
			any of them. If this is the case the current place set is not a siphon.
			Clear the flag and break out of the transition loop. Also, set the flx
			flag to indicate that there was at least one place set that was not a
			siphone.*/
			if (in && ! out)
			{
				result.flag[i] = 0;
				result.flx = 1;
				break;
			}
		}
	}
	return result;
}

/*******************************************************************************
IsPlaceFlagged returns nonzero if the given place is a member of (is flagged in)
the current place set. It automatically determines how to treat the PlaceSet
pointer.*/
inline int IsPlaceFlagged(int Place, int SetIndex, void* PlaceSet, int MultiplePlaceSets)
{
	if (MultiplePlaceSets)
	{
		return GetMatrixEl((matrix*) PlaceSet, Place, SetIndex);
	}
	else
	{
		return ((int*) PlaceSet)[Place];
	}
}

/*******************************************************************************
PlaceSets returns the number of place sets to be iterated through by examining
the MultiplePlaceSets flag and the PlaceSet pointer.*/
inline int PlaceSets(void* PlaceSet, int MultiplePlaceSets)
{
	/*If MultiplePlaceSets is set, then PlaceSet points to a matrix such that each
	column is a place set (a set of flags, one for each place). The number of sets
	is the number of columns.*/
	if (MultiplePlaceSets)
	{
		return NumberOfColumns(*((matrix*) PlaceSet));
	}
	else
	{
		return 1;
	}
}

/*******************************************************************************
Check the parameters. Dm and Dp must be provided and of the same size. PlaceSet
must not be a null pointer. If MultiplePlaceSets is nonzero, PlaceSet must point
to a matrix with the same number of rows as Dm/Dp.*/
int CheckParams(matrix *Dm, matrix *Dp, void *PlaceSet, int MultiplePlaceSets)
{
	if (! (Dm && Dp))
	{
		merror(0, "ISSIPH: The input or the output matrix pointer was null");
		return 0;
	}
	if (NumberOfRows(*Dm) != NumberOfRows(*Dp) || NumberOfColumns(*Dm) != NumberOfColumns(*Dp))
	{
		merror(0, "ISSIPH: The input and output matrices are not the same size");
		return 0;
	}
	if (! PlaceSet)
	{
		merror(0, "ISSIPH: The place set pointer was null");
		return 0;
	}
	if (MultiplePlaceSets)
	{
		if (NumberOfRows(*((matrix*) PlaceSet)) != NumberOfRows(*Dm))
		{
			merror(0, "ISSIPH: The place set matrix did not have the same number of rows as there were places");
			return 0;
		}
	}
	return 1;
}
