PolynomialFactory.java

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

import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import edu.udel.cis.vsl.tass.number.IF.Exponentiator;
import edu.udel.cis.vsl.tass.number.IF.IntegerNumberIF;
import edu.udel.cis.vsl.tass.number.IF.Multiplier;
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.type.SymbolicTypeIF;
import edu.udel.cis.vsl.tass.symbolic.concrete.ConcreteFactory;
import edu.udel.cis.vsl.tass.symbolic.expression.SymbolicExpression;
import edu.udel.cis.vsl.tass.symbolic.expression.SymbolicExpressionKey;
import edu.udel.cis.vsl.tass.symbolic.monic.MonicFactory;
import edu.udel.cis.vsl.tass.symbolic.monic.MonicMonomial;
import edu.udel.cis.vsl.tass.symbolic.monomial.Monomial;
import edu.udel.cis.vsl.tass.symbolic.monomial.MonomialFactory;
import edu.udel.cis.vsl.tass.symbolic.power.PowerExpressionFactory;
import edu.udel.cis.vsl.tass.symbolic.type.SymbolicTypeFactory;

public class PolynomialFactory implements Multiplier<Polynomial> {

	private Map<SymbolicExpressionKey<Polynomial>, Polynomial> map = new HashMap<SymbolicExpressionKey<Polynomial>, Polynomial>();

	private ConcreteFactory concreteFactory;

	private MonicFactory monicFactory;

	private MonomialFactory monomialFactory;

	private SymbolicTypeFactory typeFactory;

	private NumericConcreteExpressionIF oneIntExpression, oneRealExpression;

	private Exponentiator<Polynomial> intExponentiator, realExponentiator;

	private Polynomial zeroIntPolynomial, zeroRealPolynomial, oneIntPolynomial,
			oneRealPolynomial;

	public PolynomialFactory(MonomialFactory monomialFactory) {
		this.monomialFactory = monomialFactory;
		monicFactory = monomialFactory.monicFactory();
		concreteFactory = monomialFactory.concreteFactory();
		typeFactory = monomialFactory.typeFactory();
		oneIntExpression = concreteFactory.oneIntExpression();
		oneRealExpression = concreteFactory.oneRealExpression();
		zeroIntPolynomial = polynomial(typeFactory.integerType(),
				new Monomial[] {});
		zeroRealPolynomial = polynomial(typeFactory.realType(),
				new Monomial[] {});
		oneIntPolynomial = polynomial(typeFactory.integerType(),
				new Monomial[] { monomialFactory.oneIntMonomial() });
		oneRealPolynomial = polynomial(typeFactory.realType(),
				new Monomial[] { monomialFactory.oneRealMonomial() });
		intExponentiator = new Exponentiator<Polynomial>(this, oneIntPolynomial);
		realExponentiator = new Exponentiator<Polynomial>(this,
				oneRealPolynomial);
	}

	public PowerExpressionFactory powerExpressionFactory() {
		return monomialFactory.powerExpressionFactory();
	}

	public Polynomial polynomial(SymbolicTypeIF type, Monomial[] terms) {
		return SymbolicExpression.flyweight(map, new Polynomial(type, terms));
	}

	public ConcreteFactory concreteFactory() {
		return concreteFactory;
	}

	public MonicFactory monicFactory() {
		return monicFactory;
	}

	public MonomialFactory monomialFactory() {
		return monomialFactory;
	}

	public SymbolicTypeFactory typeFactory() {
		return typeFactory;
	}

	public Polynomial zeroIntPolynomial() {
		return zeroIntPolynomial;
	}

	public Polynomial zeroRealPolynomial() {
		return zeroRealPolynomial;
	}

	public Polynomial oneIntPolynomial() {
		return oneIntPolynomial;
	}

	public Polynomial oneRealPolynomial() {
		return oneRealPolynomial;
	}

	public Polynomial polynomial(NumericPrimitive primitive) {
		SymbolicTypeIF type = primitive.type();
		NumericConcreteExpressionIF one = (type.isInteger() ? oneIntExpression
				: oneRealExpression);
		MonicMonomial monic = monicFactory.monicMonomial(primitive);
		Monomial monomial = monomialFactory.monomial(one, monic);
		Polynomial polynomial = polynomial(type, new Monomial[] { monomial });

		return polynomial;
	}

	public Polynomial polynomial(Monomial monomial) {
		if (monomial.isZero()) {
			return (monomial.type().isInteger() ? zeroIntPolynomial
					: zeroRealPolynomial);
		}
		return polynomial(monomial.type(), new Monomial[] { monomial });
	}

	public Polynomial polynomial(NumericConcreteExpressionIF constant) {
		if (constant.isZero()) {
			return (constant.type().isInteger() ? zeroIntPolynomial
					: zeroRealPolynomial);
		}
		return polynomial(monomialFactory.monomial(constant));
	}

	public Polynomial add(Polynomial polynomial0, Polynomial polynomial1) {
		Monomial[] terms0, terms1;
		int numTerms0, numTerms1, index0 = 0, index1 = 0;
		Vector<Monomial> termVector = new Vector<Monomial>();
		Monomial[] terms;
		int numTerms;
		SymbolicTypeIF type;

		assert polynomial0 != null;
		assert polynomial1 != null;
		assert polynomial0.type().equals(polynomial1.type());
		type = polynomial0.type();
		if (!type.equals(polynomial1.type())) {
			throw new IllegalArgumentException("Incompatible types:\n"
					+ polynomial0.type() + "\n" + polynomial1.type());
		}
		terms0 = polynomial0.terms();
		terms1 = polynomial1.terms();
		numTerms0 = terms0.length;
		numTerms1 = terms1.length;
		while (index0 < numTerms0 && index1 < numTerms1) {
			Monomial term0 = terms0[index0];
			Monomial term1 = terms1[index1];
			MonicMonomial monic0 = term0.monicMonomial();
			MonicMonomial monic1 = term1.monicMonomial();
			int compare = monic0.compareTo(monic1);

			if (compare == 0) {
				NumericConcreteExpressionIF coefficient = concreteFactory.add(
						term0.coefficient(), term1.coefficient());

				if (!coefficient.isZero())
					termVector.add(monomialFactory
							.monomial(coefficient, monic0));
				index0++;
				index1++;
			} else if (compare > 0) {
				termVector.add(term1);
				index1++;
			} else {
				termVector.add(term0);
				index0++;
			}
		}
		while (index0 < numTerms0) {
			termVector.add(terms0[index0]);
			index0++;
		}
		while (index1 < numTerms1) {
			termVector.add(terms1[index1]);
			index1++;
		}
		numTerms = termVector.size();
		terms = new Monomial[numTerms];
		for (int i = 0; i < numTerms; i++)
			terms[i] = termVector.elementAt(i);
		return polynomial(type, terms);
	}

	private Polynomial multiply(Monomial monomial, Polynomial polynomial) {
		Monomial[] oldTerms = polynomial.terms();
		int numTerms = oldTerms.length;
		Monomial[] newTerms = new Monomial[numTerms];

		for (int i = 0; i < numTerms; i++)
			newTerms[i] = monomialFactory.multiply(monomial, oldTerms[i]);
		return polynomial(polynomial.type(), newTerms);
	}

	public Polynomial multiply(Polynomial p1, Polynomial p2) {
		SymbolicTypeIF type = p1.type();
		Polynomial result = (type.isInteger() ? zeroIntPolynomial
				: zeroRealPolynomial);
		Monomial[] terms = p1.terms();

		for (Monomial term : terms)
			result = add(result, multiply(term, p2));
		return result;
	}

	public Polynomial exp(Polynomial polynomial, IntegerNumberIF exponent) {
		return (polynomial.type().isInteger() ? intExponentiator.exp(
				polynomial, exponent) : realExponentiator.exp(polynomial,
				exponent));
	}

	public Polynomial negate(Polynomial polynomial) {
		Monomial[] oldTerms = polynomial.terms();
		int numTerms = oldTerms.length;
		Monomial[] newTerms = new Monomial[numTerms];

		for (int i = 0; i < numTerms; i++) {
			newTerms[i] = monomialFactory.negate(oldTerms[i]);
		}
		return polynomial(polynomial.type(), newTerms);
	}

	public Polynomial castToReal(Polynomial polynomial) {
		// cast all monomials to real and add them up.
		Polynomial result = zeroRealPolynomial;

		for (Monomial monomial : polynomial.terms()) {
			result = add(result, polynomial(monomialFactory
					.castToReal(monomial)));
		}
		return result;
	}

	/**
	 * Divides the polynomial by the constant and returns the resulting
	 * polynomial. If polynomial has real type, every coeffcient is divided by
	 * the constant. If polynomial has integer type: the operation is only
	 * defined if the constant divides each coefficient. Otherwise an exception
	 * is thrown.
	 */
	public Polynomial divide(Polynomial polynomial,
			NumericConcreteExpressionIF constant) {
		int numTerms = polynomial.numTerms();
		Monomial[] oldTerms = polynomial.terms();
		Monomial[] newTerms = new Monomial[numTerms];

		for (int i = 0; i < numTerms; i++) {
			Monomial oldTerm = oldTerms[i];
			NumericConcreteExpressionIF oldCoefficient = oldTerm.coefficient();
			NumericConcreteExpressionIF newCoefficient = concreteFactory
					.divide(oldCoefficient, constant);

			if (polynomial.type().isInteger()
					&& !concreteFactory.mod(oldCoefficient,
							concreteFactory.abs(constant)).isZero()) {
				throw new IllegalArgumentException("Term " + oldTerm
						+ " is not divisible by " + constant);
			}
			newTerms[i] = monomialFactory.monomial(newCoefficient, oldTerm
					.monicMonomial());
		}
		return polynomial(polynomial.type(), newTerms);
	}

}