Polynomial.java

package edu.udel.cis.vsl.tass.symbolic.polynomial;

import java.util.Arrays;

import edu.udel.cis.vsl.tass.number.Numbers;
import edu.udel.cis.vsl.tass.number.IF.IntegerNumberIF;
import edu.udel.cis.vsl.tass.number.IF.NumberFactoryIF;
import edu.udel.cis.vsl.tass.number.IF.NumberIF;
import edu.udel.cis.vsl.tass.symbolic.NumericPrimitive;
import edu.udel.cis.vsl.tass.symbolic.IF.tree.NumericConcreteExpressionIF;
import edu.udel.cis.vsl.tass.symbolic.IF.tree.TreeExpressionIF;
import edu.udel.cis.vsl.tass.symbolic.IF.type.SymbolicTypeIF;
import edu.udel.cis.vsl.tass.symbolic.expression.SymbolicExpression;
import edu.udel.cis.vsl.tass.symbolic.monic.MonicMonomial;
import edu.udel.cis.vsl.tass.symbolic.monomial.Monomial;

/**
 * A Polynomial is an expression that is a polynomial in primitive expressions.
 * In other words, it is the sum of monomials in primitive expressions. The
 * Polynomial is unqiuely represented by an ordered set of Monomials.
 * 
 * The polynomial can be either real or integer type. If real, all monomials
 * have real type. If integer, all monomials have integer type.
 * 
 * Two polynomials are considered equal iff their term sets are equal.
 */
public class Polynomial extends SymbolicExpression implements TreeExpressionIF {

	private static NumberFactoryIF numberFactory = Numbers.REAL_FACTORY;

	private static IntegerNumberIF NEG_ONE = numberFactory.integer(-1);

	private Monomial[] terms;

	/**
	 * Construct a new polynomial of either integer or real type, representing
	 * the sum of the given list of terms. The terms should be ordered from
	 * least to greatest monic monomial (see the natural order in
	 * MonicMonomial). This is assumed and is not checked. Erroneous behavior
	 * may result if this pre-condition is not met.
	 */
	// this is a problem: a lot of defects arise if this condition is
	// not checked. So checking it now.
	Polynomial(SymbolicTypeIF numericType, Monomial[] terms) {
		super(numericType);
		assert terms != null;

		for (int i = 0; i < terms.length; i++) {
			Monomial monomial = terms[i];

			assert numericType.equals(monomial.type());
			if (i > 0)
				assert monomial.monicMonomial().compareTo(
						terms[i - 1].monicMonomial()) > 0;
		}
		this.terms = terms;
	}

	public Monomial[] terms() {
		return terms;
	}

	public int numTerms() {
		return terms.length;
	}

	public NumericPrimitive extractPrimitive() {
		if (numTerms() == 1 && degree().isOne()) {
			Monomial monomial = terms[0];
			NumericConcreteExpressionIF constant = monomial.coefficient();

			if (constant.isOne()) {
				MonicMonomial monic = monomial.monicMonomial();

				return monic.factor(0);
			}
		}
		return null;
	}

	protected int intrinsicHashCode() {
		return type().hashCode() + Arrays.hashCode(terms);
	}

	protected boolean intrinsicEquals(SymbolicExpression expression) {
		return expression instanceof Polynomial
				&& type().equals(expression.type())
				&& Arrays.equals(terms, ((Polynomial) expression).terms);
	}

	public String toString() {
		if (terms.length == 0)
			return "0";

		String result = "";

		for (int i = 0; i < terms.length; i++) {
			if (i > 0)
				result += " + ";
			result += terms[i];
		}
		return result;
	}

	public String atomString() {
		return "(" + toString() + ")";
	}

	public boolean isZero() {
		return terms.length == 0;
	}

	public boolean isOne() {
		return terms.length == 1 && terms[0].isOne();
	}

	public IntegerNumberIF degree() {
		/*
		 * since monomials are ordered, the degree of the lead monomial is the
		 * degree of the polynomial
		 */
		if (isZero())
			return NEG_ONE;
		return terms[0].degree();
	}

	public TreeExpressionIF argument(int index) {
		return terms[index];
	}

	public SymbolicKind kind() {
		return SymbolicKind.ADD;
	}

	public int numArguments() {
		return terms.length;
	}

	private NumberIF zero() {
		return (type().isInteger() ? numberFactory.zeroInteger()
				: numberFactory.zeroRational());
	}

	/**
	 * Returns the constant term as a NumberIF. This will always return a
	 * non-null value. I.e., will return 0 if there is no constant term.
	 */
	public NumberIF constantTerm() {
		if (terms.length == 0) {
			return zero();
		} else {
			Monomial lastMonomial = terms[terms.length - 1];

			if (lastMonomial.degree().signum() == 0) {
				return lastMonomial.coefficient().value();
			} else {
				return zero();
			}
		}
	}

}