import java.io.Serializable;

/*
 * File: CdbTuple.java
 * Author: Dillon Sadofsky (adapted from the materials at cse.unl.edu/~scot)
 *
 * Description:	This class represents a tuple in the constraint database.
 *	It stores several private members (an ADT ABMatrix and an array of
 *	strings) that contain the names of the variables involved in the tuple
 *	and the matrix representation of the addition constraints involving
 *	those variables.
 *		This class is meant to be used by other programs and does not have
 *	any type of main method.  For usage guidlines, see the included test.java
 *	and documentation.txt.
 */

public class CdbTuple implements Serializable
{
	/**
	 * for serialization
	 */
	private static final long serialVersionUID = -8537171694674323049L;

	/**
	 * Represents an addition bound matrix
	 */
	private ABMatrix abm;

	/**
	 * names is a list of variable names associated with the rows and columns
	 */
	private String[] names;

	/**
	 * The do nothing constructor, you will probably want to write a do
	 * something constructor.
	 */
	public CdbTuple()
	{
		abm = null;
		names = null;
	}

	/*
	 * This is a copy constructor
	 */
	public CdbTuple(ABMatrix mat, String[] names )
	{
		abm = mat;
		this.names = names;
	}

	//Accessors/ mutators

	/*
	 * Mutator to alter the variable strings stored in the class
	 * input: int nIndex	->	this is the index of the variable to alter/add
	 *		String strNew	->	the new name of the variable
	 * 
	 *	When this function is called, the variable name will replace an existing
	 *		entry at the specified location, or resize the array to make room.
	 */
	public void SetVariable(int nIndex, String strNew)
	{
		// if they are aiming too high, resize the array
		if (names == null || nIndex >= names.length)
		{
			// Make it one larger
			ResizeArray(nIndex + 1);
		}

		// Store the value in the new location
		names[nIndex] = strNew;
	}

	/*
	 * This function is similar to SetVariable, but it will never replace an
	 * existing value, only grow the array and add it to the end.
	 */
	public void AddVariable(String strNew)
	{
		// If it doesn't exist yet, create it, otherwise, grow it by 1
		if (names == null)
			ResizeArray(1);
		else
			ResizeArray(names.length + 1);

		// Insert data in new last location
		names[names.length - 1] = strNew;
	}

	/*
	 * This function is just here for ease of being able to resize/grow the
	 * names array.  The input is the new size for the String array, measured
	 * in number of items.
	 */
	private void ResizeArray(int nSize)
	{
		String[] newNames = new String[nSize];

		// if the old array existed, then copy the data
		if (names != null)
		{
			for (int i = 0; i < names.length; ++i)
				newNames[i] = names[i];
		}

		// Move names over to the new value, let the trash collector deal with the old one
		names = newNames;
	}

	/*
	 * This is a trace function good for debugging.  It prints out the variables involved
	 * in the matrix, then tells the matrix itself to trace.
	 */
	public void Trace()
	{
		// Print the variables first
		System.out.println("Variables: ");

		// Iterate through and print them, comma seperated
		for (int i = 0; names != null && i < names.length; ++i)
		{
			// Seperate with commas
			if (i != 0)
				System.out.print(", ");

			System.out.print(names[i]);
		}

		// Also print the matrix
		System.out.println("\nHere is the matrix:");

		// Don't attempt to call functions on a non-existant object
		if (abm != null)
			abm.Trace();
	}

	/*
	 * This function will take an initial size for the constraint matrix and initialize it.
	 */
	public void InitializeMatrix(int nSize) throws Exception
	{
		abm = new ABMatrix(nSize);
	}

	/*
	 * This one is like above, but also passes the initial matrix data to the constructor.
	 */
	public void InitializeMatrix(int nSize, double[][] aryData) throws Exception
	{
		abm = new ABMatrix(nSize, aryData);
	}

	// An accessor that gets the array index of a particular string
	public int GetStringIndex(String strVariable)
	{
		// If there is no array, we cannot find it
		if (names == null)
			return -1;

		// Scan the array for the location of this string.  Eventually, this should probably utilize a map or hash
		for (int i = names.length - 1; i >= 0; --i)
		{
			if (names[i].equalsIgnoreCase(strVariable))
				return i;
		}

		// We didn't find it, so return invalid
		return -1;
	}

	/*
	 * Function:	public void AddConstraint(int nX, int nY, double nWeight)
	 * Description:
	 * 		This function will allow the user to add a constraint to the matrix representation
	 * of the tuple.  It deals with all issues of subsumption.
	 */
	public void AddConstraint(int nX, int nY, double nWeight)
	{
		double nCur = abm.GetItem(nX, nY);

		// If the current value is -infinity, then the new value cannot be smaller
		if (nCur == Double.NEGATIVE_INFINITY)
			abm.SetItem(nX, nY, nWeight);
		else // We only need to keep the strongest constraint between two vertices, so keep the maximum
			abm.SetItem(nX, nY, Math.max(nWeight, nCur));
	}

	/**
	 * Function: satisfiable()
	 * Input:		none
	 * Output:		boolean - whether or not the current ABMatrix is satisfiable
	 *
	 * 	In order to see if the matrix is satisfiable, we need to compute the transitive
	 * closure of the matrix representation of the constraints.  The data is currently
	 * stored in abm.  In class we learned that if a matrix of constraints is satisfiable,
	 * there will exist no positive-weight cycles.  Since we always keep the stronger of
	 * two constraints, the main diagonal will store all maximum weight cycles.  If any
	 * of these values is greater than 0, then the constraint tuple is not satisfiable.
	 */
	public boolean satisfiable()
	{
		// Let's not change the matrix right now, just get a version with transitive closure computed
		ABMatrix abmNew = abm.GetTransitiveClosure();

		// If this matrix was satisfiable, the main diagonal would be values <= 0
		for (int i = abmNew.GetDimension() - 1; i >= 0; --i)
		{
			// I always iterate backwards if possible so that we only have to call the function once
			if (abmNew.GetItem(i, i) > 0)
				return false;
		}

		// We didn't find anything that ruined it, so it is satisfiable
		return true;
	}
}
