#include <stdlib.h>
#include "MemoryManager.h"
#include "spnbox.h"
#include "../pnheaders/matrix.h"
#include "matrixmath.h"


 typedef struct InitTransformedNet_r
{
	int RowsAddedC;
	int* CColumns;
	
	int RowsAddedF;
	int* FColumns;
} InitTransformedNet_r;

/*This function does some basic checking to make sure parameters are as they 
should be. It returns the number of constraints or -1 in case any parameters are
invalid.*/
static int CheckParams(matrix* Dm, matrix* Dp, matrix* L, int* b, int* m0, matrix* F, matrix* C);

/*This function checks to see if the constraint row specified by Row is 
inadmissible. It returns a logic 1 if the constraint is inadmissible and a 0 
otherwise.*/
static int Inadmissible(int Row, matrix* D, matrix* L, matrix* F, matrix* C, int TucCount, int* Tuc, int TuoCount, int* Tuo);

/*InitTransformedNet counts how many rows and columns will need to be added to 
the i/o matrices of the Petri net to account for the new constraints being 
added, alloces the space, and pre-copies the existing i/o matrices into the 
appropriate places in the new ones.*/
static InitTransformedNet_r InitTransformedNet(int Row, MemoryManager* memory, matrix* Dm, matrix* Dp, matrix* OldDm, matrix* OldDp,  matrix* F, matrix* C, int TucCount, int* Tuc);

/*TransformC2L transforms the C constraints into marking constraints which it 
adds to the current marking constraint row. It adds new places to the Petri net 
for the new constraints to refer to, and establishes a few transition/place 
links.*/
static void TransformC2L(int Row, matrix* C, matrix* LRow, matrix* Dp, int FirstDRow);

/*TransformF2L transforms the F constraints into marking constraints which it 
adds to the current marking constraint row. It adds new places to the Petri net 
for the new constraints to refer to, and establishes a few transition/place 
links.*/
static void TransformF2L(int Row, matrix* F, matrix* LRow, matrix* Dm, matrix* Dp, int TucCount, int* Tuc, int StartRow, int StartCol);

/*FindInSet returns the index of the integer find in the array pointed to by 
set. If find is not found, -1 is returned.*/
static int FindInSet(int find, int* set, int SetSize);

/*CollapseNet takes the results of enforcing (and transformation, if it was 
done) the current constraint row and collapses it back into the Petri net that 
will be returned. It also copies transformed constraints to the structure to be 
returned.*/
static void CollapseNet(int ConstraintRow, linenf_r *Enforced, InitTransformedNet_r *Transform, linenf_r *Final, int Places, int Transitions);

linenf_r linenf(matrix* Dm, matrix* Dp, matrix* L, int* b, int* m0, matrix* F, matrix* C, int TucCount, int* Tuc, int TuoCount, int* Tuo)
{
	/*Variables*/
	int i, j, k, Constraints, Places, Transitions, NewPlaces, NewTransitions, NewTucCount, NewCRows; 
	int ldp, ldm;
	int NotSolved = 0, Impossible = 0;
	int *NewTuc;
	matrix D, NewD, NewDm, NewDp, NewL, NewF, NewC;
	int* Newm0;	
	MemoryManager Memory, RowMemory;
	linenf_r Ret;
	linenf_r EnfResult;
	ilpadm_r AdmResult;
	InitTransformedNet_r Transform;
	
	
	/*Initialize the return structure to a suitable error value.*/
	memset(&Ret, 0, sizeof(linenf_r));
	Ret.how = HOW_ERROR;
	
	/*Check the parameters. -1 means a parameter is invalid - any other value is a 
	constraint count.*/
	if ((Constraints = CheckParams(Dm, Dp, L, b, m0, F, C)) == -1)
	{
		return Ret;
	}
	
	/*Initialize the result messages*/
	Ret.how = HOW_OK;
	Ret.dhow = tcalloc(Constraints, sizeof(char*));
	for (i = 0; i < Constraints; i++)
	{
		Ret.dhow[i] = HOW_OK;
	}
	
	Places = NumberOfRows(*Dm);
	Transitions = NumberOfColumns(*Dm);
	
	/*If no constraints are provided there is no enforcement to be done.
	Copy the plant net as the supervised net and the initial marking as given.*/
	if (! Constraints)
	{
		AllocateMatrixType(2, &Ret.Dfm, Places, Transitions);
		CopyMatrix(Dm, &Ret.Dfm);
		AllocateMatrixType(2, &Ret.Dfp, Places, Transitions);
		CopyMatrix(Dp, &Ret.Dfp);
		if (m0)
		{
			Ret.ms0 = tcalloc(Places, sizeof(int));
			for (i = 0; i < Places; i++)
			{
				Ret.ms0[i] = m0[i];
			}
		}
		return Ret;
	}
	
	if (! Tuc) TucCount = 0;
	if (! Tuo) TuoCount = 0;
	
	/*Setup the memory manager. Set aside space for all the matrices and 
	individual address we expect to need and then let address block sizes default 
	to their minimum.*/
	Memory = CreateMemoryManager(3, 3, 0, 0);

	/*Setup default values for L, C, and F.*/
	if (! L)
	{
		L = mmalloc(&Memory, sizeof(matrix));
		MAllocateMatrixType(&Memory, 2, L, Constraints, Places);
	}
	
	if (! C)
	{
		C = mmalloc(&Memory, sizeof(matrix));
		MAllocateMatrixType(&Memory, 2, C, Constraints, Transitions);
	}
	
	if (! F)
	{
		F = mmalloc(&Memory, sizeof(matrix));
		MAllocateMatrixType(&Memory, 2, F, Constraints, Transitions);
	}		

	/*We'll need the incidence matrix later. Get it.*/
	D = SubtractMatrix(Dp, Dm, (matrix*) 2);
	ManageMatrix(&Memory, &D);

	/*Allocate the result memory to hold transformed constraints and a new pair of 
	Petri net i/o matrices. There will be a new place (row) added for each constraint.*/
	AllocateMatrixType(2, &Ret.Dfm, Places + Constraints, Transitions);
	AllocateMatrixType(2, &Ret.Dfp, Places + Constraints, Transitions);
	Ret.ms0 = tcalloc(Places + Constraints, sizeof(int));
	if (m0)
	{
		for (i = 0; i < Places; i++)
		{
			Ret.ms0[i] = m0[i];
		}
	}
	/*There will be as many transformed constraints as there were original 
	constraints.*/
	AllocateMatrixType(2, &Ret.Lf, Constraints, Places);
	AllocateMatrixType(2, &Ret.Cf, Constraints, Transitions);
	if (b)
	{
		Ret.bf = tcalloc(Constraints, sizeof(int));
	}
	/*The result petri net will include the original net.*/
	CopyBlock(Places, Transitions, Dm, 0, 0, &Ret.Dfm, 0, 0);
	CopyBlock(Places, Transitions, Dp, 0, 0, &Ret.Dfp, 0, 0);
		
	if (TuoCount || TucCount)
	{
		/*If there are either uncontrollable or unobservable transitions present, 
		we must transform any unnacceptable constraints and then enforce them, and 
		directly enforce any acceptable constraints.*/
		
		/*Begin the constraint loop.*/
		for (i = 0; i < Constraints; i++)
		{
			/*Setup the in-loop memory. This will be used to free memory allocated 
			during the loop.*/
			RowMemory = CreateMemoryManager(5, 5, 0, 0);
			
			/*Check inadmissibility of constraint.*/
			if (Inadmissible(i, &D, L, F, C, TucCount, Tuc, TuoCount, Tuo))
			{
				/*Allocate space for and initialize the transformed i/o matrices we will 
				fill.*/
				Transform = InitTransformedNet(i, &RowMemory, &NewDm, &NewDp, Dm, Dp, F, C, TucCount, Tuc);
				
				/*Allocate space for the new marking constraint row and copy the current 
				marking constraint row into it. Use a type-one matrix since we are just 
				dealing with a vector and that is fastest with a type 1.*/
				MAllocateMatrixType(&RowMemory, 1, &NewL, 1, NumberOfRows(NewDm));
				CopyBlock(1, Places, L, i, 0, &NewL, 0, 0);				
				
				/*Transform the C constraints.*/
				TransformC2L(i, C, &NewL, &NewDp, NumberOfRows(*Dm));
				
				/*Transform the F constraints*/
				TransformF2L(i, F, &NewL, &NewDm, &NewDp, TucCount, Tuc, NumberOfRows(*Dm) + Transform.RowsAddedC, NumberOfColumns(*Dm));
				
				/*Build the initial marking for the marking constraint transformation. 
				It should be all zeros except that the original places should have the 
				original initial marking, if provided.*/
				Newm0 = mcalloc(&RowMemory, NumberOfRows(NewDp), sizeof(int));
				if (m0)
				{
					for (j = 0; j < NumberOfRows(*Dp); j++)
					{
						Newm0[j] = m0[j];
					}
				}
				
				/*Build a new uncontrollable transition list. It should include all 
				original uncontrollable transitions plus each of the new transitions 
				that have been added.*/
				NewTucCount = TucCount + Transform.RowsAddedF;
				if (NewTucCount)
				{
					NewTuc = mcalloc(&RowMemory, NewTucCount, sizeof(int));
				}
				for (j = 0; j < TucCount; j++)
				{
					NewTuc[j] = Tuc[j];
				}
				for (j = 0; j < Transform.RowsAddedF; j++)
				{
					NewTuc[TucCount + j] = NumberOfColumns(*Dm) + j;
				}
				/*Get the incidence matrix that will be passed to ilpadm.*/
				NewD = SubtractMatrix(&NewDp, &NewDm, (matrix*) 2);
				ManageMatrix(&RowMemory, &NewD);
				
				/*Transform the marking constraints to admissible marking constraints.
				Use mroadm first. If it fails to solve, try the slower but more
				thorough ilpadm.*/
				AdmResult = (ilpadm_r) mroadm(&NewL, b ? b + i : 0, &NewD, NewTuc, NewTucCount, Tuo, TuoCount, Newm0);
				if (strcmp(AdmResult.dhow[0], HOW_OK))
				{
					DeallocateIlpadm(&AdmResult);
					AdmResult = ilpadm(&NewL, b ? b + i : 0, &NewD, NewTucCount, NewTuc, TuoCount, Tuo, Newm0);
				}
				
				Ret.dhow[i] = AdmResult.dhow[0];
				
				/*If the transformation was not OK, zero the return values.*/
				if (strcmp(AdmResult.dhow[0], HOW_OK))
				{
					if (b) 
					{
						AdmResult.ba[0] = 0;
					}
					for (j = 0; j < NumberOfColumns(AdmResult.La); j++)
					{
						SetMatrixEl(&AdmResult.La, 0, j, 0);
					}
					/*If the problem was not solved or was impossible, set the appropriate 
					overall status flag.*/
					if (! strcmp(AdmResult.how, HOW_NOTSOLVED))
					{
						NotSolved |= 1;
					}
					if (! strcmp(AdmResult.how, HOW_IMPOSSIBLE))
					{
						Impossible |= 1;
					}
				}
				
				/*Now, use recursion to enforce these acceptable constraints. We'll need 
				a new version of the F constraint: a row vector corresponding to the 
				current constraint row from F, with all elements corresponding to 
				uncontrollable transitions zeroed out, and zeroes for all the elements
				corresponding to new transitions.*/
				MAllocateMatrixType(&RowMemory, 1, &NewF, 1, NumberOfColumns(NewD));
				for (j = 0; j < NumberOfColumns(*F); j++)
				{
					if (FindInSet(j, Tuc, TucCount) == -1)
					{
						SetMatrixEl(&NewF, 0, j, GetMatrixEl(F, i, j));
					}
				}
				
				/*Enforce the constraints.*/
				EnfResult = linenf(&NewDm, &NewDp, &AdmResult.La, b ? AdmResult.ba : 0, Newm0, &NewF, 0, 0, 0, 0, 0);
					
				/*Collapse the enforced net back to applying to the original problem and 
				store the results in the return structure.*/
				CollapseNet(i, &EnfResult, &Transform, &Ret, Places, Transitions);
				DeallocateIlpadm(&AdmResult);
				DeallocateLinenf(&EnfResult);
			}
			else
			{
				/*If we get here it means the current constraint is admissible. Extract 
				current constraint rows and	enforce them individually.*/
				MAllocateMatrixType(&RowMemory, 1, &NewL, 1, Places);
				MAllocateMatrixType(&RowMemory, 1, &NewF, 1, Transitions);
				MAllocateMatrixType(&RowMemory, 1, &NewC, 1, Transitions);
				CopyBlock(1, Places, L, i, 0, &NewL, 0, 0);
				CopyBlock(1, Transitions, F, i, 0, &NewF, 0, 0);
				CopyBlock(1, Transitions, C, i, 0, &NewC, 0, 0);
				/*While we're at it, copy the untransformed constraints to the 
				appropriate place in the final transformed constraint matrices.*/
				CopyBlock(1, Places, &NewL, 0, 0, &Ret.Lf, i, 0);
				CopyBlock(1, Transitions, &NewC, 0, 0, &Ret.Cf, i, 0);
				
				/*Enforce the constraints.*/
				EnfResult = linenf(Dm, Dp, &NewL, b ? b + i : 0, m0, &NewF, &NewC, 0, 0, 0, 0);
				
				/*Copy the enforced constraints into the appropriate places.*/
				CollapseNet(i, &EnfResult, 0, &Ret, Places, Transitions);
				DeallocateLinenf(&EnfResult);
			}
			//Free memory
			FreeMemory(&RowMemory);
		}
		FreeMemory(&Memory);
		//Status message
		if (NotSolved)
		{
			Ret.how = HOW_NOTSOLVED;
		}
		if (Impossible)
		{
			Ret.how = HOW_IMPOSSIBLE;
		}
		//Return
		return Ret;
	}
	else
	{
		/*If we get here, we are guaranteed that there are no inadmissible 
		constraints. Enforce constraints algorithmically.*/
		
		for (i = 0; i < Constraints; i++)
		{
			for (j = 0; j < Transitions; j++)
			{
				ldm = MultiplyVector(L, &D, i, j, 0) + GetMatrixEl(C, i, j);
				if (ldm < 0)
				{
					ldp = -ldm;
					ldm = 0;
				}
				else
				{
					ldp = 0;
				}
				k = GetMatrixEl(F, i, j) - ldm;
				SetMatrixEl(&Ret.Dfp, Places + i, j, ldp + (k > 0 ? k : 0));
				k = GetMatrixEl(F, i, j);
				SetMatrixEl(&Ret.Dfm, Places + i, j, k > ldm ? k : ldm);
			}
			if (b)
			{				
				if (m0)
				{
					/*If the initial marking was provided, generate the initial marking 
					for the newly-added places as well.*/
					Ret.ms0[Places + i] = b[i] - MultiplyVector(L, (matrix*) m0, i, MULTIPLYVECTOR_ARRAY, 0);
				}
				/*While we're in the loop, copy the constraint vector unchanged if it 
				was given.*/
				Ret.bf[i] = b[i];
			}
		}
		/*Copy the unchanged constraint matrices.*/
		CopyMatrix(L, &Ret.Lf);
		CopyMatrix(C, &Ret.Cf);
		//Free memory
		FreeMemory(&Memory);
		return Ret;
	}
}



/******************************************************/
/*          Utility Functions and Subroutines         */
/******************************************************/

void CollapseNet(int ConstraintRow, linenf_r *Enforced, InitTransformedNet_r *Transform, linenf_r *Final, int Places, int Transitions)
{	
	int DpF, DmF, Index, Dp, Dm, j;
	for (j = 0; j < Transitions; j++)
	{
		/*If the current column is one that caused a new column to be added via 
		F-transform, get the element in the	current row of the transformed petri net 
		corresponding to the added column.*/
		if (Transform && (Index = FindInSet(j, Transform->FColumns, Transform->RowsAddedF)) != -1)
		{
			DmF = GetMatrixEl(&Enforced->Dfm, NumberOfRows(Enforced->Dfm) - 1, Transitions + Index);
			DpF = GetMatrixEl(&Enforced->Dfp, NumberOfRows(Enforced->Dfp) - 1, Transitions + Index);
		}
		else
		{
			DmF = 0;
			DpF = 0;
		}
		Dm = GetMatrixEl(&Enforced->Dfm, NumberOfRows(Enforced->Dfm) - 1, j);
		Dp = GetMatrixEl(&Enforced->Dfp, NumberOfRows(Enforced->Dfp) - 1, j);
		
		SetMatrixEl(&Final->Dfm, Places + ConstraintRow, j, Dm + DmF);
		SetMatrixEl(&Final->Dfp, Places + ConstraintRow, j, Dp + DpF);
	}
	/*Record the transformed C constraints from the transformed marking 
	constraints that were returned by ilpadm and have been passed unchanged 
	through linenf and into the Enforced structure.*/
	if (Transform)
	{
		for (j = 0; j < Transform->RowsAddedC; j++)
		{
			SetMatrixEl(&Final->Cf, ConstraintRow, Transform->CColumns[j], GetMatrixEl(&Enforced->Lf, 0, Places + j));		
		}
	}
	/*Record the transformed marking constraints corresponding to the original 
	places.*/
	CopyBlock(1, Places, &Enforced->Lf, 0, 0, &Final->Lf, ConstraintRow, 0);
	
	/*Copy the transformed constraint vector value.*/
	if (Enforced->bf)
	{
		Final->bf[ConstraintRow] = Enforced->bf[0];
	}		
	/*Copy the initial marking value.*/
	Final->ms0[Places + ConstraintRow] = Enforced->ms0[NumberOfRows(Enforced->Dfm) - 1];
}

/*FindInSet returns the index of the integer find in the array pointed to by 
set. If find is not found, -1 is returned.*/
int FindInSet(int find, int* set, int SetSize)
{
	int i;
	for (i = 0; i < SetSize; i++)
	{
		if (find == set[i])
		{
			return i;
		}
	}
	return -1;
}

/*TransformF2L transforms the F constraints into marking constraints which it 
adds to the current marking constraint row. It adds new places to the Petri net 
for the new constraints to refer to, and establishes a few transition/place 
links.*/
void TransformF2L(int Row, matrix* F, matrix* LRow, matrix* Dm, matrix* Dp, int TucCount, int* Tuc, int StartRow, int StartCol)
{
	int i, k = 0, x;
	/*We add a new place and a new transition for each entry in the appropriate 
	row of F that is greater than 0 and which corresponds to an uncontrollable 
	transition. The output matrix gets an identity the size of the number of new 
	places and transitions being added placed in its lower-left corner.
	The input matrix gets all columns corresponding to the F-columns being worked 
	with copied into the newly added columns, has the original columns zeroed, and 
	then has a single 1 put	in each of the new rows, where the 1 goes into the 
	column corresponding to the column of the F matrix that caused it to be 
	placed.*/
		
	for (i = 0; i < TucCount; i++)
	{
		if (GetMatrixEl(F, Row, Tuc[i]) > 0)
		{
			/*The new marking constraint was determined in Matlab per this expression:
			x = [lx, F(j,Tf).*(F(j,Tf)>0)+lx*Dxm(:,Tf)]
			The el-by-el product F(j, Tf).*(F(j, Tf) > 0) simply returns all entries 
			of F such that they are greater than zero in a single row vector. The 
			product lx*Dxm(:,Tf) returns a row vector with a column for each such 
			entry in F. The content of each element is the vector product of the lx 
			vector and a column from Dm corresponding to the entry in F. Therefore, on 
			an element-by-element basis, each new element of the marking constraint is 
			the sum of the contents of an appropriate element in F and the vector 
			product of the original marking constraint and the corresponding column of 
			Dm. In the original Matlab program, rows and columns for F constraints are 
			not added to Dm and Dp until after this line, but since the rows and 
			columns have already been added as zeros we can use the MultiplyVector 
			function directly.*/					
			SetMatrixEl(LRow, 0, StartRow + k, GetMatrixEl(F, Row, Tuc[i]) + MultiplyVector(LRow, Dm, 0, Tuc[i], 0));
			
			/*Fill in the appropriate elements in Dm.*/
			SetMatrixEl(Dm, StartRow + k, StartCol + k, 1);
			
			/*Perform the column copy in Dp: column corresponding to current F-column 
			to new column being	added	for current F-column.*/
			CopyBlock(NumberOfRows(*Dp), 1, Dp, 0, Tuc[i], Dp, 0, StartCol + k);
			/*Zero the Dp column corresponding to the current F column, except we want 
			to put a single 1	in the row that is the new row being added for the 
			current F column.*/
			for (x = 0; x < NumberOfRows(*Dp); x++)
			{
				if (x == StartRow + k)
				{
					SetMatrixEl(Dp, x, Tuc[i], 1);
				}
				else
				{
					SetMatrixEl(Dp, x, Tuc[i], 0);
				}
			}
			k++;
		}
	}
}

/*TransformC2L transforms the C constraints into marking constraints which it 
adds to the current marking constraint row. It adds new places to the Petri net 
for the new constraints to refer to, and establishes a few transition/place 
links.*/
void TransformC2L(int Row, matrix* C, matrix* LRow, matrix* Dp, int FirstDRow)
{
	int i, k = 0;
	/*The new Petri net entries consist of new place rows, each with a single 1 in 
	the input matrix, where there is one row for and each of the 1s corresponds to 
	a column of the current row of C that is nonzero. The new marking constaint 
	entries consist of a copy of all the nonzero entries in the appropriate row of 
	C.*/
	for (i = 0; i < NumberOfColumns(*C); i++)
	{
		if(GetMatrixEl(C, Row, i))
		{
			SetMatrixEl(LRow, 0, FirstDRow + k, GetMatrixEl(C, Row, i));
			SetMatrixEl(Dp, FirstDRow + (k++), i, 1);
		}
	}
}

/*InitTransformedNet counts how many rows and columns will need to be added to 
the i/o matrices of the Petri net to account for the new constraints being 
added, alloces the space, and pre-copies the existing i/o matrices into the 
appropriate places in the new ones.*/
InitTransformedNet_r InitTransformedNet(int Row, MemoryManager* memory, matrix* Dm, matrix* Dp, matrix* OldDm, matrix* OldDp,  matrix* F, matrix* C, int TucCount, int* Tuc)
{
	int i, j;
	int* CCols, FCols;
	InitTransformedNet_r result;
	result.RowsAddedC = 0;
	result.RowsAddedF = 0;
	/*Since we don't know yet how many columns need to be processed, allocate 
	enough space to hold the indices of all columns.*/
	result.CColumns = mcalloc(memory, NumberOfColumns(*OldDm), sizeof(int));
	result.FColumns = mcalloc(memory, NumberOfColumns(*OldDm), sizeof(int));
	
	/*We will be adding a transition for each nonzero element in of the 
	appropriate row in C and adding both a transition and a place for each >0 
	element in the appropriate row of F that corresponds to an uncontrollable 
	transition.*/
	for (i = 0; i < NumberOfColumns(*OldDm); i++)
	{
		if(GetMatrixEl(C, Row, i))
		{			
			result.CColumns[result.RowsAddedC++] = i;
		}
		if(GetMatrixEl(F, Row, i) > 0)
		{
			/*Check to see if this corresponds to an uncontrollable transition.*/
			if(FindInSet(i, Tuc, TucCount) >= 0)
			{
				result.FColumns[result.RowsAddedF++] = i;
			}
		}
	}
	/*We now have a count of the new places and transitions that we need. Allocate 
	space.*/
	MAllocateMatrixType(memory, 2, Dm, NumberOfRows(*OldDm) + result.RowsAddedC + result.RowsAddedF, NumberOfColumns(*OldDm) + result.RowsAddedF);
	MAllocateMatrixType(memory, 2, Dp, NumberOfRows(*OldDp) + result.RowsAddedC + result.RowsAddedF, NumberOfColumns(*OldDp) + result.RowsAddedF);	
	/*Copy the existing matrices into the new ones.*/
	CopyBlock(NumberOfRows(*OldDm), NumberOfColumns(*OldDm), OldDm, 0, 0, Dm, 0, 0);
	CopyBlock(NumberOfRows(*OldDp), NumberOfColumns(*OldDp), OldDp, 0, 0, Dp, 0, 0);
	return result;
}

/*Inadmissible tests the constraint row specified by Row to determine if it is 
an admissible constraint, given the constraint and incidence matrices and the 
lists of uncontrollable and unobservable transitions. Inadmissible returns a 1 
if the constraint is inadmissible and 0 otherwise.*/
int Inadmissible(int Row, matrix* D, matrix* L, matrix* F, matrix* C, int TucCount, int* Tuc, int TuoCount, int* Tuo)
{
	int i;
	/*Check uncontrollabe transitions. Any constraint for which l*d + c > 0, for l 
	the appropriate row from L, c the appropriate element from C, and d the 
	appropriate column from D representing an uncontrollable transition is an 
	inadmissible constraint. Additionally, any uncontrollable transition for which 
	the appropriate column of f (f the appropriate constraint row of F) > 0 makes 
	that constraint inadmissible.*/
	for (i = 0; i < TucCount; i++)
	{
		if (MultiplyVector(L, D, Row, Tuc[i], 0) + GetMatrixEl(C, Row, Tuc[i]) > 0 || GetMatrixEl(F, Row, Tuc[i]) > 0)
		{
			/*The constraint is inadmissible. Return true.*/
			return 1;
		}
	}
	/*Check unobservable transitions. Any unobservable transition for which l*d + 
	c != 0, for same definitions as above, makes the constraint inadmissible.*/
	for (i = 0; i < TuoCount; i++)
	{
		if (MultiplyVector(L, D, Row, Tuo[i], 0) + GetMatrixEl(C, Row, Tuo[i]))
		{
			/*The constraint is inadmissible. Return true.*/
			return 1;
		}
	}
	/*If we get here, the constraint is admissible. Return false.*/
	return 0;	
}

int CheckParams(matrix* Dm, matrix* Dp, matrix* L, int* b, int* m0, matrix* F, matrix* C)
{
	int Constraints = 0;
	/*Check to make sure the minimum number of parameters have been passed.
	Dm and Dp are the only required parameters.*/
	if(! (Dm && Dp))
	{
		merror(0, "LINENF: One of the required parameters is a null pointer");
		return -1;
	}
	/*Check to make sure the Petri net matrices agree in size.*/
	if(NumberOfRows(*Dm) != NumberOfRows(*Dp) || NumberOfColumns(*Dm) != NumberOfColumns(*Dp))
	{
		merror(0, "LINENF: The Petri net input and output matrices do not agree in size");
		return -1;
	}
	/*Check to make sure that each constraint matrix is the proper size if it is 
	given.*/
	if (L)
	{
		Constraints = NumberOfRows(*L);
		if (NumberOfColumns(*L) != NumberOfRows(*Dm))
		{
			merror(0, "LINENF: The dimensions of the Petri net and the constraints do not agree");
			return -1;
		}
	}
	if (F)
	{
		if (Constraints)
		{
			if(NumberOfRows(*F) != Constraints)
			{
				merror(0, "LINENF: The dimensions of the constraints do not agree");
				return -1;
			}
		}
		else
		{
			Constraints = NumberOfRows(*F);
		}
		if (NumberOfColumns(*F) != NumberOfColumns(*Dm))
		{
			merror(0, "LINENF: The dimensions of the Petri net and the constraints do not agree");
			return -1;
		}
	}
	if (C)
	{
		if (Constraints)
		{
			if (NumberOfRows(*C) != Constraints)
			{
				merror(0, "LINENF: The dimensions of the constraints do not agree");
				return -1;
			}
		}
		else
		{
			Constraints = NumberOfRows(*C);
		}
		if (NumberOfColumns(*C) != NumberOfColumns(*Dm))
		{
			merror(0, "LINENF: The dimensions of the Petri net and the constraints do not agree");
			return -1;
		}
	}
	/*If m0 is given, b must be given*/
	if(m0 && ! b && Constraints)
	{
		merror(0, "LINENF: The constraint vector b must be given if an initial marking m0 is given");
		return -1;
	}
	return Constraints;		
}
