

public interface CyAttributes {
	// new method for CyAttributes
	void setEquationAttribute(final String id, final String attributeName, final String eqn) throws IllegalArgumentException;

	// All other get methods will be the same.  
}


/**
 * The implementation of an equation attribute that is defined by a
 * string, which in turn specifies various Functions that get evaluated
 * to produce the result of the Equation. 
 */
public interface Equation {
	byte getType();

	/**
	 * @param id The node/edge ID used to identify the row in CyAttributes.
	 * @returns either the result of a successful equation evaluation or null if an error occurred.
	 */
	Object eval(final String id);
}


public interface BooleanEquation extends Equation {
	@Override Boolean eval(final String id);
}


public interface StringEquation extends Equation {
	@Override String eval(final String id);
}


public interface DoubleEquation extends Equation {
	@Override Double eval(final String id);
}


/**
 * This is the low level, builtin function that performs a single 
 * operation on input values.
 */
public interface Function {
	/**
	 * Used to parse the function string.
	 * @returns the name by which you must call the function when used in an attribute equation.
	 */
	String getName();

	/**
	 * Used to parse the function parameters.
	 * @returns the list of argument types for this function, like MultiHashMapDefinition.TYPE_FLOATING_POINT etc.
	 */
	byte[] getParameterTypes();

	/**
	 * Used to define the function's return type. 
	 * @returns one of the MultiHashMapDefinition.* constants, e.g. TYPE_FLOATING_POINT.
	 */
	byte getReturnType();

	/**
	 * Used to provide help for users.
	 * @returns a description of how to use this function for a casual user.
	 */
	String getHelpDescription();

	/** Used to invoke this function.
	 * @param args the function arguments which must correspond in type and number to what getParameterTypes() returns.
	 * @returns the result of the function evaluation.  The actual type of the returned object will be what getReturnType() returns.
	 * @throws ArithmeticException thrown if a numeric error, e.g. a division by zero occurred.
	 * @thrown IllegalArgumentException thrown for any error that is not a numeric error, for example if a function only accepts positive
	 * numbers and a negative number was passed in.
	 */
	Object evaluateFunction(Object args...) throws IllegalArgumentException, ArithmeticException;
}


public interface EquationFactory {
	/** Cashes equations and returns one of a number of descendents of class Equation.
	 *  @param eqn  The string representation of an attribute equation.
	 *  @note May return the same object when given an identical equation string.
	 */
	Equation createEquation(final String eqn) throws IllegalArgumentException;

	/** Installs a new function as a built-in for the attribute equations.
	 *  @throws IllegalArgumentException Thrown if a function with same name has already been registered.
	 */
	void registerFunction(final Function f) throws IllegalArgumentException;
}


/**
 * Sample implementation for CyAttributes.
 */
public void setEquationAttribute(final String id, final String attributeName, final String eqnString) {
	// Sanity checks:
        if (id == null)
		throw new IllegalArgumentException("id is null");
        if (attributeName == null)
		throw new IllegalArgumentException("attributeName is null");
        if (eqnString == null)
		throw new IllegalArgumentException("eqnString is null");

	final Equation eqn = EquationFactory.createEquation(eqnString);

        final byte type = mmapDef.getAttributeValueType(attributeName);
        if (type < 0) {
		mmapDef.defineAttribute(attributeName, eqn.getType(), null);
        } else {
		if ( type != eqn.getType() ) 
			throw new IllegalArgumentException("definition for attributeName '" + attributeName
							   + "' already exists and it is not of type " + eqn.getType());
		final byte[] dimTypes = mmapDef.getAttributeKeyspaceDimensionTypes(attributeName);

		if (dimTypes.length != 0)
			throw new IllegalArgumentException("definition for attributeName '" + attributeName
							   + "' already exists and it is not of TYPE_INTEGER");
        }

        mmap.setAttributeValue(id, attributeName, eqn, null);
}


/**
 * Sample implementation of CyAttributes.getDoubleAttribute. This shows
 * how Equations are handled internally and how they get evaluated.
 */
public Double getDoubleAttribute(final String id, final String attrName) {
        final byte type = mmapDef.getAttributeValueType(attributeName);
        if (type < 0) {
		return null;
        }

        if (type == MultiHashMapDefinition.TYPE_FLOATING_POINT) {
        	return (Double) mmap.getAttributeValue(id, attributeName, null);
        } else if (type == MultiHashMapDefinition.TYPE_FLOATING_POINT_FUNCTION) {
        	DoubleFunction df = (DoubleFunction) mmap.getAttributeValue(id, attributeName, null);
		return df.eval(id);
	} else {
		throw new ClassCastException("definition for attributeName '" + attributeName + "' is not of TYPE_FLOATING");
	}
}

