NTMonomial.java

/*******************************************************************************
 * Copyright (c) 2013 Stephen F. Siegel, University of Delaware.
 * 
 * This file is part of SARL.
 * 
 * SARL is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * SARL is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with SARL. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package edu.udel.cis.vsl.sarl.ideal.common;

import java.util.Set;

import edu.udel.cis.vsl.sarl.IF.expr.NumericExpression;
import edu.udel.cis.vsl.sarl.IF.number.IntegerNumber;
import edu.udel.cis.vsl.sarl.IF.number.NumberFactory;
import edu.udel.cis.vsl.sarl.IF.object.SymbolicObject;
import edu.udel.cis.vsl.sarl.expr.common.HomogeneousExpression;
import edu.udel.cis.vsl.sarl.ideal.IF.Constant;
import edu.udel.cis.vsl.sarl.ideal.IF.IdealFactory;
import edu.udel.cis.vsl.sarl.ideal.IF.Monic;
import edu.udel.cis.vsl.sarl.ideal.IF.Monomial;
import edu.udel.cis.vsl.sarl.ideal.IF.Polynomial;
import edu.udel.cis.vsl.sarl.ideal.IF.Primitive;
import edu.udel.cis.vsl.sarl.ideal.IF.RationalExpression;
import edu.udel.cis.vsl.sarl.object.IF.ObjectFactory;

/**
 * A non-trivial {@link Monomial} is the product of a {@link Constant} and a
 * {@link Monic}. The constant must not be 0 or 1 and the monic must not be
 * empty.
 * 
 * @author siegel
 */
public class NTMonomial extends HomogeneousExpression<SymbolicObject>
		implements Monomial {

	/**
	 * Cached value returned by {@link #expand(IdealFactory)}.
	 */
	private Monomial[] expansion = null;

	/**
	 * Cache value returned by {@link #termMap(IdealFactory)}.
	 */
	private Monomial[] termMap = null;

	/**
	 * Constructs new {@link NTMonomial} using given <code>constant</code> and
	 * <code>monic</code>. Preconditions (checked by assertions only):
	 * <ul>
	 * <li><code>constant</code> is not 0 or 1</li>
	 * <li><code>monic</code> is not empty (i.e., monic is not 1)</li>
	 * </ul>
	 * 
	 * @param constant
	 *            the constant in the new monomial
	 * @param monic
	 *            the monic in the new monomial
	 */
	protected NTMonomial(Constant constant, Monic monic) {
		super(SymbolicOperator.MULTIPLY, constant.type(),
				new SymbolicObject[] { constant, monic });
		assert !constant.isZero();
		assert !constant.isOne();
		assert !monic.isOne();
	}

	@Override
	public Monic monic(IdealFactory factory) {
		return (Monic) argument(1);
	}

	/**
	 * Returns the {@link Monic} component of this {@link Monomial}.
	 * 
	 * @return the {@link Monic} component of this
	 */
	public Monic monic() {
		return (Monic) argument(1);
	}

	@Override
	public Constant monomialConstant(IdealFactory factory) {
		return (Constant) argument(0);
	}

	/**
	 * Returns the {@link Constant} component of this {@link Monomial}.
	 * 
	 * @return the constant component of this
	 */
	public Constant monomialConstant() {
		return (Constant) argument(0);
	}

	@Override
	public Monomial numerator(IdealFactory factory) {
		return this;
	}

	@Override
	public Monomial denominator(IdealFactory factory) {
		return factory.one(type());
	}

	@Override
	public Monomial[] expand(IdealFactory factory) {
		if (expansion == null) {
			Monic monic = this.monic();

			if (monic.hasNontrivialExpansion(factory))
				expansion = factory.multiplyConstantTermMap(monomialConstant(),
						monic.expand(factory));
			else
				expansion = new Monomial[] { this };
			if (isCanonic())
				factory.objectFactory().canonize(expansion);
		}
		return expansion;
	}

	@Override
	public IntegerNumber monomialDegree(NumberFactory factory) {
		return monic().monomialDegree(factory);
	}

	@Override
	public Monomial[] termMap(IdealFactory factory) {
		if (termMap == null) {
			Monic monic = monic();

			if (monic instanceof Polynomial) {
				termMap = factory.multiplyConstantTermMap(
						(Constant) argument(0), monic.termMap(factory));
			} else {
				termMap = new Monomial[] { this };
			}
			if (isCanonic())
				factory.objectFactory().canonize(termMap);
		}
		return termMap;
	}

	@Override
	public IntegerNumber totalDegree(NumberFactory factory) {
		return monic().totalDegree(factory);
	}

	@Override
	public boolean hasNontrivialExpansion(IdealFactory factory) {
		return monic().hasNontrivialExpansion(factory);
	}

	@Override
	public void canonizeChildren(ObjectFactory of) {
		super.canonizeChildren(of);
		if (expansion != null)
			of.canonize(expansion);
		if (termMap != null)
			of.canonize(termMap);
	}

	@Override
	public int monomialOrder(IdealFactory factory) {
		return ((Monic) argument(1)).monomialOrder(factory);
	}

	@Override
	public Monomial[] lower(IdealFactory factory) {
		Monomial[] lowering;
		int order = monomialOrder(factory);
		Monic monic = this.monic();

		if (order == 0) {
			lowering = new Monomial[] { this };
		} else {
			lowering = factory.multiplyConstantTermMap(monomialConstant(),
					monic instanceof Primitive ? monic.termMap(factory)
							: monic.lower(factory));
		}
		if (isCanonic())
			factory.objectFactory().canonize(lowering);
		return lowering;
	}

	@Override
	public RationalExpression powerRational(IdealFactory factory,
			RationalExpression exponent) {
		/*
		 * Generally we don't know if monomial^n can be safely transformed to
		 * monomialConstant^n * monic^n. But for several specific cases, we know
		 * that's fine:
		 * 
		 * 1. this monomial is negative (which means the exponent is meaningful
		 * for negative numbers)
		 * 
		 * 2. both monomialConstant and monic are positive
		 * 
		 * 3. monomialConstant, monic or this monomial is zero
		 * 
		 * 4. "1/exponent" can be casted to integer without loss of precision
		 * and "1/exponent" is odd.
		 * 
		 * 5. "exponent" can be casted to integer without loss of precision.
		 */
		boolean separate = false;

		if (factory.isPositive(this).isFalse())
			separate = true;
		if (factory.isPositive(monomialConstant()).isTrue()
				&& factory.isPositive(monic()).isTrue())
			separate = true;
		if (this.isZero() || monomialConstant().isZero() || monic().isZero())
			separate = true;
		if (factory.floor(exponent).equals(factory.ceil(exponent)))
			separate = true;

		RationalExpression exponentReciprocal = factory
				.divide(factory.oneReal(), exponent);

		if (factory.floor(exponentReciprocal)
				.equals(factory.ceil(exponentReciprocal))) {
			NumericExpression safelyCastedExponentDenominator = factory.cast(
					exponentReciprocal, factory.typeFactory().integerType());

			if (factory.modulo(safelyCastedExponentDenominator,
					factory.intConstant(2)).isOne())
				separate = true;
		}
		if (separate)
			return factory.multiply(
					monomialConstant().powerRational(factory, exponent),
					monic().powerRational(factory, exponent));
		else
			return factory.expression(SymbolicOperator.POWER, type(), this,
					exponent);
	}

	@Override
	public Monomial powerInt(IdealFactory factory, IntegerNumber exponent) {
		return factory.monomial(monomialConstant().powerInt(factory, exponent),
				monic().powerInt(factory, exponent));
	}

	@Override
	public IntegerNumber maxDegreeOf(NumberFactory factory,
			Primitive primitive) {
		return monic().maxDegreeOf(factory, primitive);
	}

	@Override
	public Set<Primitive> getTruePrimitives() {
		return monic().getTruePrimitives();
	}

}