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

/*Subroutine declarations. See subroutine implementation for descriptions.*/
static void AddPlaces(pn2eacpn_r* result, matrix* Dm, int Transition, int* Qflag, int QLength, int* ipl, int ipCount, int* dpl, int dpCount);

static int GetQPlace(matrix* U, int Pair, int* Qflag, int* mask, pn2eacpn_r* result);

static matrix GetU(int* cardbt, pn2eacpn_r* result, int Transition, int **TD, int *TDCount);

static int InitParts(MemoryManager* mem, pn2eacpn_r* result, int** cardbt, int** cardbtx, int** Qflag, matrix* Dm, matrix* Dp, matrix* MX, matrix* L0, int** TD, int *TDCount);

static int CheckParams(MemoryManager* mem, matrix* Dm, matrix* Dp, int** Mask, matrix* MX, matrix* L0, matrix* L, int **ipl, int *ipCount, int **dpl, int *dpCount, int*** TD, int** TDCount);

static void FinalizeResult(pn2eacpn_r* result, matrix* L, matrix* Dm, int* ipl, int ipCount);

static int ProcessPlace(int Transition, int Place, matrix* Dm, matrix* Dp, int** TD, int* TDCount);

/*The function itself. pn2acpn transforms a Petri net to an asymmetric choice
Petri net*/
pn2eacpn_r pn2eacpn(matrix* Dm, matrix* Dp, int* Mask, matrix* MX, matrix* L0, matrix* L, int *ipl, int ipCount, int* dpl, int dpCount, int** TD, int* TDCount)
{
	int Transition, Transitions, Places, i, j, x;
	int *cardbt, *cardbtx, *Qflag;
	MemoryManager mem;
	matrix U;
	pn2eacpn_r result;
	
	/*Initialize the value to be returned to an error value.*/
	memset(&result, 0, sizeof(pn2eacpn_r));
	
	/*Initialize the memory manager.*/
	mem = CreateMemoryManager(10, 5, 0, 0);
	
	if (! CheckParams(&mem, Dm, Dp, &Mask, MX, L0, L, &ipl, &ipCount, &dpl, &dpCount, &TD, &TDCount))
	{
		return result;
	}
	Places = NumberOfRows(*Dm);
	Transitions = NumberOfColumns(*Dm);
	
	/*Initialize parts of the return value and the flag matrices cardbt and 
	cardbtx. This function returns the index of the first transition which should 
	be processed.*/
	Transition = InitParts(&mem, &result, &cardbt, &cardbtx, &Qflag, Dm, Dp, MX, L0, TD, TDCount);
	
	/*Loop until there are no more transitions to be processed. This will be when
	Transition has been set to -1 by either of the functions responsible for 
	finding the next transition to be processed.*/
	while (Transition >= 0)
	{
		
		cardbtx[Transition] = 0;
		/*Get U, a 2xn matrix each column of which contains a pair of place
		indices for which a particular relationship holds.*/
		U = GetU(cardbt, &result, Transition, TD, TDCount);
				
		/*Iterate through each of the place pairs. From each pair we will develop
		the index of a single place, for which we will set the Q-flag and with
		which we will make an alteration to the cardbtx flag array.*/
		/*The Q-flag array defaults to all cleared.*/
		memset(Qflag, 0, sizeof(int) * Places);		
		for (i = 0; i < NumberOfColumns(U); i++)
		{
			/*Get the index of some particular place algorithmically.*/
			if ((x = GetQPlace(&U, i, Qflag, Mask, &result)) >= 0)
			{
				/*Set the corresponding q flag.*/
				Qflag[x] = 1;
				/*We OR cardbtx with cardbt AND the place from Dm*/
				for (j = 0; j < NumberOfColumns(*Dm); j++)
				{
					cardbtx[j] = cardbtx[j] || (cardbt[j] && GetMatrixEl(Dm, x, j));
				}				
			}
		}
		/*Free the memory allocated for the U vector now that we don't need it
		anymore.*/
		DeallocateMatrix(&U);
		/*Fill in the new places and transitions we reserved memory for above. Fill 
		in the unraisable transition arrays for the new transitions.*/
		AddPlaces(&result, Dm, Transition, Qflag, Places, ipl, ipCount, dpl, dpCount);
		/*Clear the current element of cardbtx in case it was reset some how.*/
		cardbtx[Transition] = 0;
		/*Find the next transition to be processed. This will be the first one with
		a set cardbtx flag. The next transition number defaults to -1 to indicate
		that there are no more transitions to be processed.*/
		Transition = -1;
		for (i = 0; i < Transitions; i++)
		{
			if (cardbtx[i])
			{
				Transition = i;
				break;
			}
		}		
	}
	/*Finalize the result structure; fill in the last few values.*/
	FinalizeResult(&result, L, Dm, ipl, ipCount);
	FreeMemory(&mem);
	return result;
}

/*******************************************************************************
FinalizeResult takes the result structure and fills in values that are not
computed during the main processing loop: the incidence matrix Df, the new 
marking constraint matrix Lf, and the new independent place list iplf.
*/
void FinalizeResult(pn2eacpn_r* result, matrix* L, matrix* Dm, int* ipl, int ipCount)
{
	int places, oldplaces, i, place, newplaces;
	/*Fill the incidence matrix.*/
	result->Df = SubtractMatrix(&result->Dpf, &result->Dmf, (matrix*) 2);
	
	oldplaces = NumberOfRows(*Dm);
	places = NumberOfRows(result->Df);
	newplaces = places - oldplaces;
	
	/*The new L matrix should be the same as the old with columns of zeros added
	for all the new places.*/
	if (L)
	{
		AllocateMatrixType(2, &result->LF, NumberOfRows(*L), NumberOfColumns(*L) + newplaces);
		CopyBlock(NumberOfRows(*L), NumberOfColumns(*L), L, 0, 0, &result->LF, 0, 0);
	}
	
	/*The new independent place list should contain all the places that were 
	added and each of the old places.*/
	result->iplfCount = ipCount + (places - oldplaces);
	result->iplf = tcalloc(result->iplfCount, sizeof(int));
	for (i = 0; i < ipCount; i++)
	{
		result->iplf[i] = ipl[i];
	}
	place = oldplaces;
	for (; i < result->iplfCount; i++)
	{
		result->iplf[i] = place++;
	}	
	
}

/*******************************************************************************
AddPlaces is responsible for filling in the values in the transitions and 
places that are added during each iteration of the main processing loop. It 
fills in values in the Petri net matrices, in the initial marking constraint 
matrix L0f, in the independent-to-dependent marking transformation matrix MXf, 
and in the unraisable transition arrays for the newly-created transitions.
It requires the result vector, the original output matrix, the transition
currently being processed by the main processing loop, the vector of Q flags,
the length of the vector of Q flags, and the lists of dependent and independent
places.
*/
void AddPlaces(pn2eacpn_r* result, matrix* Dm, int Transition, int* Qflag, int Qlength, int* ipl, int ipCount, int* dpl, int dpCount)
{
	int Rows, Cols, i, j, x, ToAdd = 0;
	matrix Dmf, Dpf, MX, L0;
	int **TD;
	int *TDCount;
	
	/*We will be adding a place and transition for each set Qflag. Count them.*/
	for (i = 0; i < Qlength; i++)
	{
		if (Qflag[i]) ToAdd++;
	}	
	
	/*We will be adding new columns to MX and L0. Reseve space and copy
	existing columns.*/
	if (NumberOfColumns(result->MXF))	
	{	
		AllocateMatrixType(2, &MX, NumberOfRows(result->MXF), NumberOfColumns(result->MXF) + ToAdd);
		CopyBlock(NumberOfRows(result->MXF), NumberOfColumns(result->MXF), &result->MXF, 0, 0, &MX, 0, 0);
	}
	else
	{
		/*Zero MX.*/
		AllocateMatrix(&MX, 0, 0);
	}
	if (NumberOfColumns(result->L0F))
	{
		AllocateMatrixType(2, &L0, NumberOfRows(result->L0F), NumberOfColumns(result->L0F) + ToAdd);
		CopyBlock(NumberOfRows(result->L0F), NumberOfColumns(result->L0F), &result->L0F, 0, 0, &L0, 0, 0);
	}
	else
	{
		AllocateMatrix(&L0, 0, 0);
	}

	/*We add rows and columns to the Petri net matrices. Store them temporarily 
	in the local Dmf and Dpf matrices. Allocate space and copy the existing 
	matrices into the new space.*/	
	Rows = NumberOfRows(result->Dmf);
	Cols = NumberOfColumns(result->Dmf);
	AllocateMatrixType(2, &Dmf, Rows + ToAdd, Cols + ToAdd);
	AllocateMatrixType(2, &Dpf, Rows + ToAdd, Cols + ToAdd);
	CopyBlock(Rows, Cols, &result->Dmf, 0, 0, &Dmf, 0, 0);
	CopyBlock(Rows, Cols, &result->Dpf, 0, 0, &Dpf, 0, 0);
	
	/*We will be adding a new unraisable transition array for each transition
	being added. Allocate space for the addresses of the new arrays and copy
	the addresses of the existing arrays, which will be reused.*/
	TD = tcalloc(Cols + ToAdd, sizeof(int*));
	TDCount = tcalloc(Cols + ToAdd, sizeof(int));
	for (i = 0; i < Cols; i++)
	{
		TDCount[i] = result->TDCount[i];
		TD[i] = result->TD[i];
	}
	/*The new unraisable transition array for each transition consists of the
	unraisable transition array from the transition that got it added (the
	current transition) plus the new transition itself. Fill in the new
	unraisable transition arrays.*/
	for (i = Cols; i < Cols + ToAdd; i++)
	{
		TDCount[i] = result->TDCount[Transition] + 1;
		TD[i] = tcalloc(TDCount[i], sizeof(int));
		for (j = 0; j < result->TDCount[Transition]; j++)
		{
			TD[i][j] = result->TD[Transition][j];
		}
		TD[i][result->TDCount[Transition]] = i;
	}
	/*Free the old memory used by the structure and replace it with the new.*/
	free(result->TD);
	free(result->TDCount);
	result->TD = TD;
	result->TDCount = TDCount;
		
	/*Iterate through the places which have been Q-flagged. At each one add a new
	place and transition. Use j to keep track of which place or transition is
	being added.*/	
	j = 0;
	for (i = 0; i < Qlength; i++)
	{
		if (! Qflag[i]) continue;
		/*Zero the output arc at the place and transition of interest*/
		SetMatrixEl(&Dmf, i, Transition, 0);		
		/*The place gets an output arc added at the new transition*/
		SetMatrixEl(&Dmf, i, Cols + j, 1);
		/*The new place gets an input arc added at the new transition*/
		SetMatrixEl(&Dpf, Rows + j, Cols + j, 1);
		/*The new place gets an output arc added to the current transition, with 
		the same weight as the original output arc at the place and transition of
		interest.*/
		SetMatrixEl(&Dmf, Rows + j, Transition, GetMatrixEl(Dm, i, Transition));
		/*Modify MX and L0 if they exist.*/
		if (NumberOfColumns(MX) || NumberOfColumns(L0))
		{
			/*Find out if the current place is independent..*/		
			for (x = 0; x < ipCount; x++)
			{
				if (ipl[x] == i)
				{				
					break;
				}
			}
			/*If it is, we want to copy to the new columns of MX and L0 the values 
			in their respective existing columns corresponding to the independent 
			place just found. (if they exist)*/
			if (x < ipCount)
			{					
				if (NumberOfColumns(MX))
				{
					CopyBlock(NumberOfRows(MX), 1, &MX, 0, x, &MX, 0, NumberOfColumns(MX) - ToAdd + j);
				}
				if (NumberOfColumns(L0))
				{
					CopyBlock(NumberOfRows(L0), 1, &L0, 0, x, &L0, 0, NumberOfColumns(L0) - ToAdd + j);
				}
			}
			/*If not, we fill the new column of MX with a -1 in the row corresponding
			to the dependent place indexed to by i. The new column of L0 remains zeroed.*/
			else
			{
				/*Get the index of the place.*/
				for (x = 0; x < dpCount; x++)
				{
					if (dpl[x] == i) break;
				}
				if (x < dpCount && NumberOfRows(MX))
				{
					SetMatrixEl(&MX, x, NumberOfColumns(MX) - ToAdd + j, -1);
				}			
			}
		}		
		/*Increment the added-place counter.*/
		j++;
	}
	/*Now deallocate the old matrices and replace them with the new.*/
	DeallocateMatrix(&result->Dmf);
	result->Dmf = Dmf;
	DeallocateMatrix(&result->Dpf);
	result->Dpf = Dpf;
	if (NumberOfColumns(MX))
	{
		DeallocateMatrix(&result->MXF);
		result->MXF = MX;
	}
	if (NumberOfColumns(L0))
	{
		DeallocateMatrix(&result->L0F);
		result->L0F = L0;
	}
}

/*******************************************************************************
GetQPlace examines the pair of place indices in the U matrix that is
currently being processed and selects and returns on of the indicies, to be used
to set a Q-flag and perform operations on the cardbtx flag vector.
It requires the U matrix, the pair number ot process, the vector of Q-flags,
the mask provided to pn2acpn, and the result structure for read access to Dmf
and Dpf.*/
int GetQPlace(matrix* U, int Pair, int* Qflag, int* mask, pn2eacpn_r* result)
{
	int U1, U2, i, arcCount1, arcCount2;
	U1 = GetMatrixEl(U, 0, Pair);
	U2 = GetMatrixEl(U, 1, Pair);
	
	/*If neither U1 nor U2 indexes a place that has a high mask bit, there is no
	valid Q index. Return -1.*/
	if (! (mask[U1] || mask[U2]))
	{
		return -1;
	}
	/*If only one of U1 or U2 index a place with a high mask bit, that place is
	the Q index.*/
	if (mask[U1] && (! mask[U2]))
	{
		return U1;
	}
	if ((! mask[U1]) && mask[U2])
	{
		return U2;
	}
	/*Otherwise, we do some other tests to eliminate one of the numbers.*/
	/*If the qflags for both of the U indices are still 0...*/
	if (! (Qflag[U1] || Qflag[U2]))
	{
		//If we are examining any but the last pair of place indexes:
		if (Pair < NumberOfColumns(*U) - 1)
		{
			/*If the first index in the next pair is the same as the first index in 
			this pair, return that index.*/
			if (U1 == GetMatrixEl(U, 0, Pair + 1))
			{
				return U1;
			}
			else
			{
				/*Otherwise, if there exist any pairs after the current one such that 
				one of their indicies is the same as the second index of the current 
				pair, return the second index of the current pair.*/
				for (i = Pair + 1; i < NumberOfColumns(*U); i++)
				{
					if (GetMatrixEl(U, 0, i) == U2 || GetMatrixEl(U, 1, i) == U2)
					{
						return U2;
					}
				}
			}
		}
		/*If we get here, then we count the number of nonzero output arcs in the 
		places specified by U1 and U2. We return the index of the place with the 
		most nonzero output arcs.*/
		arcCount1 = 0;
		arcCount2 = 0;
		for (i = 0; i < NumberOfColumns(result->Dmf); i++)
		{
			if (GetMatrixEl(&result->Dmf, U1, i)) arcCount1++;
			if (GetMatrixEl(&result->Dmf, U2, i)) arcCount2++;
		}
		if (arcCount1 >= arcCount2)
		{
			return U1;
		}
		else
		{
			return U2;
		}
	}
	//If we get here, no valid place number was found. Return -1.
	return -1;
}

/*******************************************************************************
GetU creates and returns a 2 x n matrix, each column of which is a pair of
place indices such that the places exhibit some special relationship. The U
matrix is specific to the transition currently being investigated. Its 
calculation requires the cardbt flag vector, the result structure for access
to the Petri net matrices, the index of the transition currently being processed,
and a count of the number of places in the original Petri net.*/
matrix GetU(int* cardbt, pn2eacpn_r* result, int Transition, int **TD, int *TDCount)
{
	int *Row0, *Row1, *PlaceFlag;
	int NextCol, ColCapacity, i, j, k, flag0, flag1, Transitions, Places, dmi, dmj;	
	int iCount, jCount;
	matrix U;
	
	Transitions = NumberOfColumns(result->Dmf);
	Places = NumberOfRows(result->Dmf);
	
	/*U will be a 2xn matrix, where each column contains a pair of place
	indices. The pairs are determined algorithmically by examining all places in
	the current transition that are nonzero.*/	
	
	/*It is hard to predict the number of pairs that will be found. Store pairs
	in an array that will be reallocated periodically if necessary. Allocated
	in blocks the size of the number of places.*/
	Row0 = tcalloc(Places, sizeof(int));
	Row1 = tcalloc(Places, sizeof(int));
	NextCol = 0;
	ColCapacity = Places;
	
	/*We will be processing only a subset of places. Find that subset now
	to reduce processing time. Use the ProcessPlace function.*/
	PlaceFlag = tcalloc(Places, sizeof(int));
	for (i = 0; i < Places; i++)
	{		
		PlaceFlag[i] = ProcessPlace(Transition, i, &result->Dmf, &result->Dpf, TD, TDCount);
	}
	
	/*Iterate through all but the last place in the transition of interest. 
	Process only those places for which ProcessPlace returns a nonzero value.*/	
	for (i = 0; i < Places - 1; i++)
	{
		if (! PlaceFlag[i]) continue;
		/*Iterate through all the remaining places for which ProcessTransition
		returns true.*/
		for (j = i + 1; j < Places; j++)
		{
			if (! PlaceFlag[j]) continue;
			/*The original Matlab algorithm set a flag according to this
			algorithm: (some variable	names changed to make it apparent in terms 
			of variables already used):
			prj = Dmf(i,:) ~= 0; % p_j\bullet 
			prk = Dmf(j,:) ~= 0; % p_k\bullet 
			flag = sum((~prj) & (prj | prk)) & sum((~prk) & (prj | prk));
			The same operation can be reduced to a pair of examinations, one
			to get the results of the operation logical of 
			sum((~prj) & (prj | prk)) and the	next to get the results of the 
			operation logical of sum((~prk) & (prj | prk))*/
			flag0 = 0;
			flag1 = 0;
			for (k = 0; k < Transitions; k++)
			{
				dmi = GetMatrixEl(&result->Dmf, i, k);
				dmj = GetMatrixEl(&result->Dmf, j, k);
				if ((! dmi) && dmj)
				{
					flag0 = 1;							
				}
				if ((! dmj) && dmi)
				{
					flag1 = 1;
				}						
			}
			/*If both flags are set, we add the current pair of places to the
			U-matrix.*/
			if (flag0 && flag1)
			{
				/*If there is space left in the temporary holding places, use it. If
				not, reallocate with more space.*/
				if (NextCol == ColCapacity)
				{
					Row0 = realloc(Row0, (ColCapacity += Places) * sizeof(int));
					Row1 = realloc(Row1, (ColCapacity += Places) * sizeof(int));
				}
				Row0[NextCol] = i;
				Row1[NextCol++] = j;						
			}
		}
	}
	/*Copy the contents of Row0 and Row1 into the U-matrix.*/
	AllocateMatrixType(2, &U, 2, NextCol);
	for (i = 0; i < NextCol; i++)
	{
		SetMatrixEl(&U, 0, i, Row0[i]);
		SetMatrixEl(&U, 1, i, Row1[i]);
	}
	free(Row0);
	free(Row1);
	free(PlaceFlag);
	
	return U;
}

/*******************************************************************************
ProcessPlace returns a nonzero value for a place that should be processed in the
current transition. Any place should be processed which is both a parent and a
grandparent of the current transition, where the grandparent status is via a 
transition that is raisable even if the current transition is dead.
*/
int ProcessPlace(int Transition, int Place, matrix* Dm, matrix* Dp, int** TD, int* TDCount)
{
	int i, j, k;
	
	/*Check to see if the place is a parent of the transition. If not, fail.*/
	if (! GetMatrixEl(Dm, Place, Transition)) return 0;
	
	/*Iterate through all child transitions of the place.*/
	for (i = 0; i < NumberOfColumns(*Dm); i++)
	{		
		/*If the current transition is not a child of the place, skip it.*/
		if (! GetMatrixEl(Dm, Place, i)) continue;

		/*If the current transition is unraisable skip it.*/		
		k = 0;
		for (j = 0; j < TDCount[Transition]; j++)
		{
			if (TD[Transition][j] == i)
			{
				k = 1;
				break;
			}
		}
		if (k) continue;
		
		/*Otherwise iterate through each the transition's child places.*/
		for (j = 0; j < NumberOfRows(*Dm); j++)
		{
			/*If the place is a child of the transition being examined in the loop
			and a parent of the transition of interest, then the current place 
			satisfies the requirements. Return true.*/
			if (GetMatrixEl(Dp, j, i) && GetMatrixEl(Dm, j, Transition)) return 1;
		}
	}
	return 0;
}

/*******************************************************************************
InitParts initializes intermediate variables cardbt and cardbtx, flag arrays,
Qflag, a flag array, and the Petri net matrices in the result structure.
*/
int InitParts(MemoryManager* mem, pn2eacpn_r* result, int** cardbt, int** cardbtx, int** Qflag, matrix* Dm, matrix* Dp, matrix* MX, matrix* L0, int** TD, int *TDCount)
{
	int Places, Transitions, i, j, res = -1;
	Places = NumberOfRows(*Dm);
	Transitions = NumberOfColumns(*Dp);
	
	/*The result Petri net matrices should default to the same as the passed
	matrices. The result MX and L0 should also default to the same as the passed
	MX and L0, if they were given.*/
	AllocateMatrixType(2, &result->Dmf, Places, Transitions);
	CopyMatrix(Dm, &result->Dmf);
	AllocateMatrixType(2, &result->Dpf, Places, Transitions);
	CopyMatrix(Dp, &result->Dpf);	
	if (MX)
	{
		AllocateMatrixType(2, &result->MXF, NumberOfRows(*MX), NumberOfColumns(*MX));
		CopyMatrix(MX, &result->MXF);
	}
	if (L0)
	{
		AllocateMatrixType(2, &result->L0F, NumberOfRows(*L0), NumberOfColumns(*L0));
		CopyMatrix(L0, &result->L0F);
	}
	
	/*The Q-flag array has an element for each place.*/
	*Qflag = mcalloc(mem, Places, sizeof(int));
	
	/*cardbt and cardbts are flag arrays with elements for each transition. They
	default to containing a count of the number of nonzero output arcs associated 
	with the appropriate transition.*/
	*cardbt = mcalloc(mem, Transitions, sizeof(int));
	*cardbtx = mcalloc(mem, Transitions, sizeof(int));
	/*The return value should be the index of the first transition which will be
	processed. This is the index of the first transition such that the associated
	value of cardbt is greater than one.*/
	for (i = 0; i < Transitions; i++)
	{
		for (j = 0; j < Places; j++)
		{
			if (GetMatrixEl(Dm, j, i))
			{
				(*cardbt)[i]++;
				(*cardbtx)[i]++;
			}
			if (res < 0 && (*cardbt)[i] > 1)
			{
				res = i;
			}
		}
	}
	
	/*The return TD and TDCount default to what they were. We will be adding
	new transitions, but we will not be modifying any unraisabilities of the
	original transitions, so the new transitions will get their own unraisability
	arrays.*/
	result->TD = tcalloc(Transitions, sizeof(int*));
	result->TDCount = tcalloc(Transitions, sizeof(int));
	for (i = 0; i < Transitions; i++)
	{
		result->TD[i] = tcalloc(TDCount[i], sizeof(int));
		result->TDCount[i] = TDCount[i];
		for (j = 0; j < TDCount[i]; j++)
		{
			result->TD[i][j] = TD[i][j];
		}
	}		
	
	return res;
}
	
/*******************************************************************************
CheckParams examines the parameters to make sure that required parameters are
present and dimensions and array counts are consistent. It also fills in default
values where appropriate.
*/
int CheckParams(MemoryManager* mem, matrix* Dm, matrix* Dp, int** Mask, matrix* MX, matrix* L0, matrix* L, int **ipl, int *ipCount, int **dpl, int *dpCount, int*** TD, int** TDCount)
{
	int Places, Transitions, i, j, k;
	nltrans_r URT;
	matrix D;
	/*Dm - output matrix. Required. Same size as Dp.
	Dp - input matrix. Required. Same size as Dm. */
	if (! (Dm && Dp))
	{
		merror(0, "PN2ACPN: One of the required parameters Dm or Dp was null");
		return 0;
	}
	Places = NumberOfRows(*Dm);
	Transitions = NumberOfColumns(*Dm);
	if (NumberOfRows(*Dp) != Places || NumberOfColumns(*Dm) != Transitions)
	{
		merror(0, "PN2ACPN: The sizes of the input and output matrices Dp and Dm do not agree");
		return 0;
	}
	
	/*Mask = array of int flags. Not required. Defaults to array of 1s, one for 
	each place (row of D).*/
	if (! *Mask)
	{
		(*Mask) = mcalloc(mem, Places, sizeof(int));
		for (i = 0; i < Places; i++)
		{
			(*Mask)[i] = 1;
		}
	}
	
	/*ipl, dpl: unpredictable-length int arrays, holding indices of independent 
	and dependent places. Not required. If either array pointer is null the count
	should be set to zero and vice-versa.
	Total number of ipl and dpl must be same as number of places if given. ipl 
	defaults to indexes of all places, dpl defaults to empty set.*/
	if (! *ipl) 
	{
		*ipCount = 0;
	}
	else if (! ipCount)
	{
		*ipl = 0;
	}
	if (! *dpl) 
	{
		*dpCount = 0;
	}
	else if (! (*dpCount))
	{
		*dpl = 0;
	}
	
	if (! (*ipCount))
	{
		*ipl = mcalloc(mem, Places, sizeof(int));
		for (i = 0; i < Places; i++)
		{
			(*ipl)[i] = i;
		}
		*ipCount = Places;
	}
	
	if (*ipCount + *dpCount != Places)
	{
		FreeMemory(mem);
		merror(0, "PN2ACPN: The independent and dependent place list lengths do not agree with the number of places");
		return 0;
	}

	/*L0 - matrix, initial marking constraint. Not required. If present must have
	the same number of columns as there are independent places.*/
	if (L0)
	{
		if (NumberOfColumns(*L0) != *ipCount)
		{
			FreeMemory(mem);
			merror(0, "PN2ACPN: The size of L0 and the independent place list do not agree");
			return 0;
		}
	}
	
	/*L - matrix, marking constraint. Not required. If present must have the same
	number of columns as there are independent places.*/
	if (L)
	{
		if (NumberOfColumns(*L) != *ipCount)
		{
			FreeMemory(mem);
			merror(0, "PN2ACPN: The size of L and the independent place list do not agree");
			return 0;
		}
	}
	
	/*MX - matrix, same meaning as in msplit: is the transformation matrix from 
	the independent marking to the dependent marking. Not required. If present, 
	must have the same number of rows as there are dependent places and the same
	number of columns as there are independent places (if those counts are given)*/	
	if (MX && ipCount && dpCount)
	{
		if (NumberOfRows(*MX) != *dpCount || NumberOfColumns(*MX) != *ipCount)
		{
			FreeMemory(mem);
			merror(0, "PN2ACPN: The size of the transformation matrix MX and the dependent and independent place lists do not agree");
			return 0;
		}
	}
	
	/*TD, TDCount - TD is an array of arrays. It has one element (array) for each
	transition. Each element is an index array that lists the indices of
	transitions that cannot be brought to life if the transition corresponding to
	the element is dead. TDCount is an array that gives the length of the arrays
	in each element of TD.
	If these have not been provided, compute them using nltrans:
	nltrans_r nltrans(matrix* D, int* UnraisableTransitions, int urtCount)*/
	if (! (*TDCount && *TD))
	{
		/*Get the incidence matrix - needed by nltrans.*/
		D = SubtractMatrix(Dp, Dm, (matrix*) 2);		
		
		/*Allocate space for the TD and TDCount arrays.*/
		*TD = mcalloc(mem, Transitions, sizeof(int*));
		*TDCount = mcalloc(mem, Transitions, sizeof(int));
		for (i = 0; i < Transitions; i++)
		{
			/*Get a list of transitions which are unraisable if the transition we are
			currently examining is dead.*/
			URT = nltrans(&D, &i, 1);
			/*Count the number of transitions that are flagged in the flag array
			returned by nltrans.*/			
			for (j = 0; j < URT.dtrCount; j++)
			{
				if (URT.dtr[j]) (*TDCount)[i]++;
			}
			/*Now put their indices in the appropriate element of TD.*/
			(*TD)[i] = mcalloc(mem, (*TDCount)[i], sizeof(int));
			k = 0;
			for (j = 0; j < URT.dtrCount; j++)
			{
				if (URT.dtr[j]) (*TD)[i][k++] = j;
			}
			free(URT.dtr);
		}
		
		DeallocateMatrix(&D);
	}			
	return 1;
}
