#include "spnbox.h"
#include "matrixmath.h"
#include "MemoryManager.h"

/*CheckParam checks most of the parameters passed to isadm to make sure they are 
valid and returns the number of constraints or zero for bad parameters. It 
displays all necessary error messages if bad parameters are encountered.*/
static int CheckParam(matrix* Dm,  matrix* Dp, matrix* L, matrix* F, matrix* C);

/*StructuralAnalysis does the actual analysis to determine constraint 
admissibility. It returns v as an array of integers.*/
static int* StructuralAnalysis(matrix* D, matrix* L, matrix* F, matrix* C, int TucCount, int* Tuc, int TuoCount, int* Tuo);

int* isadm(matrix* Dm, matrix* Dp, matrix* L, int* b, int* m0, matrix* F, matrix* C, int TucCount, int* Tuc, int TuoCount, int* Tuo)
{
	int Places, Transitions, Constraints;
	int* v;
	MemoryManager memory;
	matrix D;
	int i;
	/*Check parameter validity:
	Required: Dm, Dp, one of (L, F, C).
	All dimensions must match.*/
	if (! Tuc) TucCount = 0;
	if (! Tuo) TuoCount = 0;
	if (! (Constraints = CheckParam(Dm, Dp, L, F, C)))
	{
		merror(0, "ISADM: There must be at least one constraint row");
		return 0;
	}
	//Create a memory manager for use with dynamic memory tracking.
	memory = CreateMemoryManager(5, 10, 0, 0);
	
	//Build the incidence matrix.
	D = SubtractMatrix(Dp, Dm, (matrix*) 2);
	ManageMatrix(&memory, &D);
	
	//Get the Place/Transition counts
	Places = NumberOfRows(*Dm);
	Transitions = NumberOfColumns(*Dm);

	/*L, F, and C should default to zero matrices of the appropriate size.*/
	if (! L)
	{
		L = mmalloc(&memory, sizeof(matrix));
		MAllocateMatrixType(&memory, 2, L, Constraints, Places);
	}
	if (! F)
	{
		F = mmalloc(&memory, sizeof(matrix));
		MAllocateMatrixType(&memory, 2, F, Constraints, Transitions);
	}
	if (! C)
	{
		C = mmalloc(&memory, sizeof(matrix));
		MAllocateMatrixType(&memory, 2, C, Constraints, Transitions);
	}
	
	/*Do the structural analysis.*/
	v = StructuralAnalysis(&D, L, F, C, TucCount, Tuc, TuoCount, Tuo);
	/*Free memory and return.*/
	FreeMemory(&memory);
	return v;
}

/*******************************************************************************
StructuralAnalysis performs an algorithmic examination of the given set of
constraints to determine which ones are admissible, returning an integer flag
array of the same format as that returned by isadm itself.
StructuralAnalysis is the workhorse portion of the isadm function. If isadm is
ever extended to employ also tests based on a reachability graph, those tests
will be implemented as another function like StructuralAnalysis and isadm will
make the decision as to which one to call.*/
int* StructuralAnalysis(matrix* D, matrix* L, matrix* F, matrix* C, int TucCount, int* Tuc, int TuoCount, int* Tuo)
{
	int i, j, k, Constraints, Transition;
	int ldm, ldp;
	int* v;
	
	/*Get the number of constraints*/
	Constraints = NumberOfRows(*L);
	
	/*Allocate space for the return values.*/
	v = tcalloc(Constraints, sizeof(int));
	
	/*Iterate through all constraints.*/ 
	for (i = 0; i < Constraints; i++)
	{		
		
		/*v defaults to 1 (admissible)*/
		v[i] = 1;
		/*Iterate through all UC/UT transitions.*/
		for (j = 0; j < TucCount + TucCount; j++)
		{
			Transition = j < TucCount ? Tuc[j] : Tuo[j - TucCount];
			/*Get ldm and ldp, quantities related to how much the firing of the 
			transition will change current constraint satisfaction. ldm contains the 
			amount by which constraint satisfaction decreases when transitions fire.
			ldp contains the amount by which constraint satisfaction increases when 
			transitions fire.*/
			ldm = MultiplyVector(L, D, i, Transition, 0) + GetMatrixEl(C, i, Transition);
			if (ldm < 0)
			{
				ldp = -ldm;
				ldm = 0;
			}
			else
			{
				ldp = 0;
			}
			k = GetMatrixEl(F, i, Transition);
			if (k - ldm > 0) ldp += (k - ldm);
			if (k > ldm) ldm = k;
			
			/*For transitions which are uncontrollable, an inadmissible constraint is 
			one which becomes less satisfied if the transition fires (in other words, 
			one for which ldm is nonzero)*/
			if (j < TucCount)
			{
				if (ldm)
				{
					v[i] = 0;
					break;
				}
			}
			/*For transitions which are unobservable, an inadmissible constraint is 
			one for which the net satisfaction changes at all if the transition fires 
			(in other words, one for which ldm != ldp*/
			else
			{
				if (ldm != ldp)
				{
					v[i] = 0;
					break;
				}
			}
		}
	}
	return v;
}

/*******************************************************************************
CheckParams makes sure that necessary parameters have been provided and does
some basic validity checking.*/
int CheckParam(matrix* Dm,  matrix* Dp, matrix* L, matrix* F, matrix* C)
{
	int Constraints;
	if (! (Dm && Dp))
	{
		merror(0, "ISADM: Dm and Dp were not provided");
		return 0;
	}
	if (! (L || F || C)) 
	{
		merror (0, "ISADM: L, F, and C were all left empty. At least one must be provided.");
		return 0;
	}
	if (NumberOfRows(*Dm) != NumberOfRows(*Dp) || NumberOfColumns(*Dm) != NumberOfColumns(*Dp))
	{
		merror(0, "ISADM: Dimensions of Dm and Dp do not agree");
		return 0;
	}
	Constraints = L ? NumberOfRows(*L) : F ? NumberOfRows(*F) : NumberOfRows(*C);
	if (L)
	{		
		if (NumberOfColumns(*L) != NumberOfRows(*Dm))
		{
			merror(0, "ISADM: Dimensions of L and the incidence matrix do not agree.");
			return 0;
		}
	}
	if (F)
	{
		if (NumberOfRows(*F) != Constraints || NumberOfColumns(*F) != NumberOfColumns(*Dm))
		{
			merror(0, "ISADM: Dimensions of F and the constraint/incidence matrices do not agree");
			return 0;
		}
	}
	if (C)
	{
		if (NumberOfRows(*C) != Constraints || NumberOfColumns(*C) != NumberOfColumns(*Dm))
		{
			merror(0, "ISADM: Dimensions of C and the constraint/incidence matrices do not agree");
			return 0;
		}
	}
	return Constraints;
}
