package org.nevec.rjm;

import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Random;
import java.util.Scanner;
import java.util.Vector;

import it.cavallium.warppi.util.Error;

/**
 * A one-parameter polynomial with rational coefficients.
 * Alternatively to be interpreted as a sequence which has the polynomial as an
 * (approximate)
 * generating function.
 *
 * @since 2006-06-25
 * @author Richard J. Mathar
 */
class RatPoly {
	/**
	 * The list of all coefficients, ascending exponents. Starting with a0, then
	 * a1, representing
	 * a value a0+a1*x+a2*x^2+a3*x^3+...
	 */
	protected Vector<Rational> a;

	/**
	 * Default ctor.
	 * Initializes the zero-valued polynomial x=0.
	 */
	public RatPoly() {
		a = new Vector<>();
	} /* ctor */

	/**
	 * Constructor with an explicit list of coefficients.
	 *
	 * @param L
	 *            the coefficients a0, a1, a2, a3,.., A deep copy of the these
	 *            is created.
	 */
	public RatPoly(final Vector<Rational> L) {
		a = new Vector<>();
		for (int i = 0; i < L.size(); i++) {
			a.add(L.elementAt(i).clone());
		}
		simplify();
	} /* ctor */

	/**
	 * Constructor with a comma-separated list as the list of coefficients.
	 *
	 * @param L
	 *            the string of the form a0,a1,a2,a3 with the coefficients
	 */
	public RatPoly(final String L) throws NumberFormatException {
		a = new Vector<>();
		final Scanner sc = new Scanner(L);
		sc.useDelimiter(",");
		while (sc.hasNext()) {
			final String tok = sc.next();
			a.add(new Rational(tok));
		}
		simplify();
		sc.close();
	} /* ctor */

	/**
	 * Constructor from a hypergeometric series.
	 *
	 * @param A
	 *            the list of values in the numerator of AFB
	 * @param B
	 *            the list of values in the denominator of AFB
	 * @param nmax
	 *            the order of the truncated polynomial representation
	 * @throws Error
	 * @since 2008-11-13
	 */
	public RatPoly(final Vector<BigInteger> A, final Vector<BigInteger> B, final int nmax) throws Error {
		/*
		 * To allow common initialization with the signature below,
		 * the main body is assembled in a separate function.
		 */
		init(A, B, nmax);
	}

	/**
	 * Constructor from a hypergeometric series.
	 *
	 * @param A
	 *            the list of values in the numerator of AFB.
	 *            At least one of these values must be a negative integer, which
	 *            implicitly determines
	 *            the order of the new polynomial.
	 * @param B
	 *            the list of values in the denominator of AFB
	 * @throws Error
	 * @since 2009-08-05
	 */
	public RatPoly(final Vector<BigInteger> A, final Vector<BigInteger> B) throws Error {
		BigInteger Nmax = BigInteger.ONE.negate();
		for (int j = 0; j < A.size(); j++) {
			if (A.elementAt(j).compareTo(BigInteger.ZERO) <= 0) {
				if (Nmax.compareTo(BigInteger.ZERO) < 0) {
					Nmax = A.elementAt(j).negate();
				} else {
					Nmax = Nmax.min(A.elementAt(j).negate());
				}
			}
		}
		if (Nmax.compareTo(BigInteger.ZERO) < 0) {
			throw new ArithmeticException("Infinite Number of Terms in Series " + Nmax.toString());
		}

		final int nmax = Nmax.intValue() - 1;
		init(A, B, nmax);
	} /* ctor */

	/**
	 * Constructor from a hypergeometric series.
	 *
	 * @param A
	 *            the list of values in the numerator of AFB
	 * @param B
	 *            the list of values in the denominator of AFB
	 * @param nmax
	 *            the order of the truncated polynomial representation
	 * @throws Error
	 * @since 2008-11-13
	 */
	protected void init(final Vector<BigInteger> A, final Vector<BigInteger> B, final int nmax) throws Error {
		a = new Vector<>();
		final Factorial f = new Factorial();
		for (int n = 0; n <= nmax; n++) {
			Rational c = new Rational(1, 1);
			for (int j = 0; j < A.size(); j++) {
				final Rational aEl = new Rational(A.elementAt(j));
				c = c.multiply(aEl.Pochhammer(n));
			}
			for (int j = 0; j < B.size(); j++) {
				final Rational bEl = new Rational(B.elementAt(j));
				c = c.divide(bEl.Pochhammer(n));
			}
			c = c.divide(f.at(n));
			a.add(c);
		}
		simplify();
	} /* init */

	/**
	 * Create a copy of this.
	 *
	 * @since 2008-11-07
	 */
	@Override
	@SuppressWarnings("unchecked")
	public RatPoly clone() {
		final RatPoly clo = new RatPoly();
		clo.a = (Vector<Rational>) a.clone();
		return clo;
	} /* clone */

	/**
	 * Retrieve a polynomial coefficient.
	 *
	 * @param n
	 *            the zero-based index of the coefficient. n=0 for the constant
	 *            term.
	 * @return the polynomial coefficient in front of x^n.
	 */
	public Rational at(final int n) {
		if (n < a.size()) {
			return a.elementAt(n);
		} else {
			return new Rational(0, 1);
		}
	} /* at */

	/**
	 * Horner scheme to find the function value at the argument x
	 *
	 * @param x
	 *            The argument of the polynomial
	 * @param mc
	 *            The context determining the precision of the value returned.
	 * @since 2008-10-26
	 */
	public BigComplex valueOf(final BigComplex x, final MathContext mc) {
		/* result is initialized to zero */
		BigComplex f = new BigComplex();
		for (int i = degree(); i >= 0; i--) {
			f = f.multiply(x, mc).add(a.elementAt(i).BigDecimalValue(mc));
		}
		return f;
	} /* valueOf */

	/**
	 * Horner scheme to find the function value at the argument x
	 *
	 * @param x
	 *            The argument of the polynomial
	 * @since 2008-11-13
	 */
	public Rational valueOf(final Rational x) {
		/* result is initialized to zero */
		Rational f = new Rational(0, 1);
		for (int i = degree(); i >= 0; i--) {
			f = f.multiply(x).add(a.elementAt(i));
		}
		return f;
	} /* valueOf */

	/**
	 * Horner scheme to find the function value at the argument x
	 *
	 * @param x
	 *            The argument of the polynomial
	 * @since 2008-11-13
	 */
	public Rational valueOf(final int x) {
		return valueOf(new Rational(x, 1));
	} /* valueOf */

	/**
	 * Horner scheme to evaluate the function at the argument x
	 *
	 * @param x
	 *            The argument of the polynomial
	 * @since 2010-08-27
	 */
	public Rational valueOf(final BigInteger x) {
		return valueOf(new Rational(x));
	} /* valueOf */

	/*
	 * Set a polynomial coefficient.
	 *
	 * @param n the zero-based index of the coefficient. n=0 for the constant
	 * term.
	 * If the polynomial has not yet the degree to need this coefficient,
	 * the intermediate coefficients are implicitly set to zero.
	 *
	 * @param value the new value of the coefficient.
	 */
	public void set(final int n, final Rational value) {
		if (n < a.size()) {
			a.set(n, value);
		} else {
			/*
			 * fill intermediate powers with coefficients of zero
			 */
			while (a.size() < n) {
				a.add(new Rational(0, 1));
			}
			a.add(value);
		}
	} /* set */

	/**
	 * Set a polynomial coefficient.
	 *
	 * @param n
	 *            the zero-based index of the coefficient. n=0 for the constant
	 *            term.
	 *            If the polynomial has not yet the degree to need this
	 *            coefficient,
	 *            the intermediate coefficients are implicitly set to zero.
	 * @param value
	 *            the new value of the coefficient.
	 */
	public void set(final int n, final BigInteger value) {
		final Rational val2 = new Rational(value, BigInteger.ONE);
		set(n, val2);
	} /* set */

	/**
	 * Set a polynomial coefficient.
	 *
	 * @param n
	 *            the zero-based index of the coefficient. n=0 for the constant
	 *            term.
	 *            If the polynomial has not yet the degree to need this
	 *            coefficient,
	 *            the intermediate coefficients are implicitly set to zero.
	 * @param value
	 *            the new value of the coefficient.
	 */
	public void set(final int n, final int value) {
		final Rational val2 = new Rational(value, 1);
		set(n, val2);
	} /* set */

	/*
	 * Set to the taylor series of exp(x) up to degree nmax.
	 *
	 * @param nmax the maximum polynomial degree
	 */
	public void setExp(final int nmax) {
		a.clear();
		final Factorial factorial = new Factorial();
		for (int n = 0; n <= nmax; n++) {
			set(n, new Rational(BigInteger.ONE, factorial.at(n)));
		}
	} /* setExp */

	/**
	 * Set to the taylor series representing 0+x.
	 */
	public void setx() {
		a.clear();
		/* coefficient 0/1=0 */
		a.add(new Rational(0, 1));
		/* coefficient 1/1=1 */
		a.add(new Rational(1, 1));
	} /* setx */

	/**
	 * Count of coefficients. One more than the degree of the polynomial.
	 *
	 * @return the number of polynomial coefficients.
	 */
	public int size() {
		return a.size();
	} /* size */

	/**
	 * Polynomial degree.
	 *
	 * @return the polynomial degree.
	 */
	public int degree() {
		return a.size() - 1;
	} /* degree */

	/**
	 * Lower Polynomial degree.
	 *
	 * @return The smallest exponent n such that [x^n] of the polynomial is
	 *         nonzero.
	 *         If the polynmial is identical zero, the result is (still) 0.
	 * @since 2010-08-27
	 */
	public int ldegree() {
		for (int n = 0; n < a.size(); n++) {
			if (a.elementAt(n).compareTo(BigInteger.ZERO) != 0) {
				return n;
			}
		}
		return 0;
	} /* ldegree */

	/**
	 * Multiply by a constant factor.
	 *
	 * @param val
	 *            the factor
	 * @return the product of this with the factor.
	 *         All coefficients of this have been multiplied individually by the
	 *         factor.
	 */
	public RatPoly multiply(final Rational val) {
		final RatPoly resul = new RatPoly();
		if (val.compareTo(BigInteger.ZERO) != 0) {
			for (int n = 0; n < a.size(); n++) {
				resul.set(n, a.elementAt(n).multiply(val));
			}
		}
		return resul;
	} /* multiply */

	/**
	 * Multiply by a constant factor.
	 *
	 * @param val
	 *            the factor
	 * @return the product of this with the factor.
	 *         All coefficients of this have been multiplied individually by the
	 *         factor.
	 * @since 2010-08-27
	 */
	public RatPoly multiply(final BigInteger val) {
		final RatPoly resul = new RatPoly();
		if (val.compareTo(BigInteger.ZERO) != 0) {
			for (int n = 0; n < a.size(); n++) {
				resul.set(n, a.elementAt(n).multiply(val));
			}
		}
		return resul;
	} /* multiply */

	/**
	 * Multiply by another polynomial
	 *
	 * @param val
	 *            the other polynomial
	 * @return the product of this with the other polynomial
	 */
	public RatPoly multiply(final RatPoly val) {
		final RatPoly resul = new RatPoly();
		/*
		 * the degree of the result is the sum of the two degrees.
		 */
		final int nmax = degree() + val.degree();
		for (int n = 0; n <= nmax; n++) {
			Rational coef = new Rational(0, 1);
			for (int nleft = 0; nleft <= n; nleft++) {
				coef = coef.add(at(nleft).multiply(val.at(n - nleft)));
			}
			resul.set(n, coef);
		}
		resul.simplify();
		return resul;
	} /* multiply */

	/**
	 * Raise to a positive power.
	 *
	 * @param n
	 *            The non-negative exponent of the power
	 * @return The n-th power of this.
	 */
	public RatPoly pow(final int n) throws ArithmeticException {
		RatPoly resul = new RatPoly("1");
		if (n < 0) {
			throw new ArithmeticException("negative polynomial power " + n);
		} else {
			/*
			 * this ought probably be done with some binary representation
			 * of the power and a smaller number of multiplications.
			 */
			for (int i = 1; i <= n; i++) {
				resul = resul.multiply(this);
			}
			resul.simplify();
			return resul;
		}
	} /* pow */

	/**
	 * Raise to a rational power.
	 * The result is the taylor expansion of this, truncated at the first
	 * term that remains undetermined based on the current number of
	 * coefficients.
	 *
	 * @param r
	 *            the exponent of the power
	 * @return This^r .
	 * @throws Error
	 * @since 2009-05-18
	 */
	public RatPoly pow(final Rational r) throws ArithmeticException, Error {
		/*
		 * split (a0+a1*x+a2*x^2+...)^r = a0^r*(1+a1/a0*r+a2/a0*r^2+..)^r
		 */
		Rational f = at(0);
		f = f.pow(r);

		/*
		 * scale the polynomial by division through the expansion coefficient of
		 * the absolute term
		 */
		final RatPoly red = divide(a.elementAt(0));

		/*
		 * and remove the leading term (now equal to 1)
		 */
		red.set(0, 0);

		/*
		 * Binomial expansion of the rest. sum_{l=0..infinity}
		 * binomial(r,l)*red^l
		 */
		RatPoly resul = new RatPoly("1");

		final int d = degree();
		for (int l = 1; l <= d; l++) {
			final Rational b = Rational.binomial(r, l);
			resul = resul.add(red.pow(l).multiply(b));
		}
		return resul.multiply(f);
	} /* pow */

	/**
	 * Add another polynomial
	 *
	 * @param val
	 *            The other polynomial
	 * @return The sum of this and the other polynomial
	 * @since 2008-10-25
	 */
	public RatPoly add(final RatPoly val) {
		final RatPoly resul = new RatPoly();
		/*
		 * the degree of the result is the larger of the two degrees (before
		 * simplify() at least).
		 */
		final int nmax = degree() > val.degree() ? degree() : val.degree();
		for (int n = 0; n <= nmax; n++) {
			final Rational coef = at(n).add(val.at(n));
			resul.set(n, coef);
		}
		resul.simplify();
		return resul;
	} /* add */

	/**
	 * Subtract another polynomial
	 *
	 * @param val
	 *            The other polynomial
	 * @return The difference between this and the other polynomial
	 * @since 2008-10-25
	 */
	public RatPoly subtract(final RatPoly val) {
		final RatPoly resul = new RatPoly();
		/*
		 * the degree of the result is the larger of the two degrees (before
		 * simplify() at least).
		 */
		final int nmax = degree() > val.degree() ? degree() : val.degree();
		for (int n = 0; n <= nmax; n++) {
			final Rational coef = at(n).subtract(val.at(n));
			resul.set(n, coef);
		}
		resul.simplify();
		return resul;
	} /* subtract */

	/**
	 * Divide by a constant.
	 *
	 * @param val
	 *            the constant through which the coefficients will be divided.
	 * @return the Taylor expansion of this/val .
	 * @throws Error
	 * @since 2009-05-18
	 */
	public RatPoly divide(final Rational val) throws Error {
		if (val.compareTo(Rational.ZERO) != 0) {
			final RatPoly resul = new RatPoly();
			for (int n = 0; n < a.size(); n++) {
				resul.set(n, a.elementAt(n).divide(val));
			}
			return resul;
		} else {
			throw new ArithmeticException("Cannot divide " + toPString() + " through zero.");
		}
	} /* divide */

	/**
	 * Divide by another polynomial.
	 *
	 * @param val
	 *            the other polynomial
	 * @param nmax
	 *            the maximum degree of the Taylor expansion of the result.
	 * @return the Taylor expansion of this/val up to degree nmax.
	 * @throws Error
	 */
	public RatPoly divide(final RatPoly val, final int nmax) throws Error {
		final RatPoly num = this;
		final RatPoly denom = val;

		/*
		 * divide by a common smallest power/degree
		 */
		while (num.at(0).compareTo(BigInteger.ZERO) == 0 && denom.at(0).compareTo(BigInteger.ZERO) == 0) {
			num.a.remove(0);
			denom.a.remove(0);
			if (num.size() <= 1 || denom.size() <= 1) {
				break;
			}
		}

		final RatPoly resul = new RatPoly();
		/*
		 * todo: If the polynomial division is exact, we could leave
		 * the loop earlier, indeed
		 */
		for (int n = 0; n <= nmax; n++) {
			Rational coef = num.at(n);
			for (int nres = 0; nres < n; nres++) {
				coef = coef.subtract(resul.at(nres).multiply(denom.at(n - nres)));
			}
			coef = coef.divide(denom.at(0));
			resul.set(n, coef);
		}
		resul.simplify();
		return resul;
	} /* divide */

	/**
	 * Divide by another polynomial.
	 *
	 * @param val
	 *            the other polynomial
	 * @return A vector with [0] containg the polynomial of degree which is the
	 *         difference of thisdegree and the degree of val. [1] the remainder
	 *         polynomial.
	 *         This = returnvalue[0] + returnvalue[1]/val .
	 * @throws Error
	 * @since 2012-03-01
	 */
	public RatPoly[] divideAndRemainder(final RatPoly val) throws Error {
		final RatPoly[] ret = new RatPoly[2];
		/*
		 * remove any high-order zeros
		 */
		final RatPoly valSimpl = val.clone();
		valSimpl.simplify();
		final RatPoly thisSimpl = clone();
		thisSimpl.simplify();

		/*
		 * catch the case with val equal to zero
		 */
		if (valSimpl.degree() == 0 && valSimpl.a.firstElement().compareTo(Rational.ZERO) == 0) {
			throw new ArithmeticException("Division through zero polynomial");
		}
		/*
		 * degree of this smaller than degree of val: remainder is this
		 */
		if (thisSimpl.degree() < valSimpl.degree()) {
			/*
			 * leading polynomial equals zero
			 */
			ret[0] = new RatPoly();
			ret[1] = thisSimpl;
		} else {
			/*
			 * long division. Highest degree by dividing the highest degree
			 * of this thru val.
			 */
			ret[0] = new RatPoly();
			ret[0].set(thisSimpl.degree() - valSimpl.degree(), thisSimpl.a.lastElement().divide(valSimpl.a.lastElement()));

			/*
			 * recurrences: build this - val*(1-termresult) and feed this
			 * into another round of division. Have intermediate
			 * ret[0]+ret[1]/val.
			 */
			ret[1] = thisSimpl.subtract(ret[0].multiply(valSimpl));

			/*
			 * any remainder left ?
			 */
			if (ret[1].degree() < valSimpl.degree()) {
				;
			} else {
				final RatPoly rem[] = ret[1].divideAndRemainder(val);
				ret[0] = ret[0].add(rem[0]);
				ret[1] = rem[1];
			}
		}
		return ret;
	} /* divideAndRemainder */

	/**
	 * Print as a comma-separated list of coefficients.
	 *
	 * @return The representation a0,a1,a2,a3,...
	 *         This is a sort of opposite of the ctor that takes a string as an
	 *         argument.
	 * @since 2008-10-25
	 */
	@Override
	public String toString() {
		String str = new String();
		for (int n = 0; n < a.size(); n++) {
			if (n == 0) {
				str += a.elementAt(n).toString();
			} else {
				str += "," + a.elementAt(n).toString();
			}
		}
		/*
		 * print at least a sole zero
		 */
		if (str.length() == 0) {
			str = "0";
		}
		return str;
	} /* toString */

	/**
	 * Print as a polyomial in x.
	 *
	 * @return To representation a0+a1*x+a2*x^2+...
	 *         This does not print the terms with coefficients equal to zero.
	 * @since 2008-10-26
	 */
	public String toPString() {
		String str = new String();
		for (int n = 0; n < a.size(); n++) {
			final BigInteger num = a.elementAt(n).a;
			if (num.compareTo(BigInteger.ZERO) != 0) {
				str += " ";
				if (num.compareTo(BigInteger.ZERO) > 0) {
					str += "+";
				}
				str += a.elementAt(n).toString();
				if (n > 0) {
					str += "*x";
					if (n > 1) {
						str += "^" + n;
					}
				}
			}
		}
		/*
		 * print at least a sole zero
		 */
		if (str.length() == 0) {
			str = "0";
		}
		return str;
	} /* toPString */

	/**
	 * Simplify the representation.
	 * Trailing values with zero coefficients (at high powers) are deleted.
	 * This modifies the polynomial on the stop (does not return another
	 * instance)
	 */
	private void simplify() {
		int n = a.size() - 1;
		if (n >= 0) {
			while (a.elementAt(n).compareTo(BigInteger.ZERO) == 0) {
				a.remove(n);
				if (--n < 0) {
					break;
				}
			}
		}
	} /* simplify */

	/**
	 * First derivative.
	 *
	 * @return The first derivative with respect to the indeterminate variable.
	 * @since 2008-10-26
	 */
	public RatPoly derive() {
		if (a.size() <= 1) {
			/*
			 * derivative of the constant is just zero
			 */
			return new RatPoly();
		} else {
			final RatPoly d = new RatPoly();
			for (int i = 1; i <= degree(); i++) {
				final Rational c = a.elementAt(i).multiply(i);
				d.set(i - 1, c);
			}
			return d;
		}
	} /* derive */

	/**
	 * Scale coefficients such that the coefficient in front of the maximum
	 * degree is unity.
	 *
	 * @return The scaled polynomial
	 * @throws Error
	 * @since 2008-10-26
	 */
	public RatPoly monic() throws Error {
		final RatPoly m = new RatPoly();
		final int d = degree();
		for (int i = 0; i <= d; i++) {
			final Rational c = a.elementAt(i).divide(a.elementAt(d));
			m.set(i, c);
		}
		return m;
	} /* monic */

	/**
	 * Mobius transform.
	 *
	 * @param maxdeg
	 *            the maximum polynomial degree of the result
	 * @return the sequence of coefficients is the Mobius transform of the
	 *         original sequence.
	 * @since 2008-12-02
	 */
	public RatPoly mobiusT(final int maxdeg) {
		/*
		 * Start with the polynomial 0
		 */
		final RatPoly r = new RatPoly();
		for (int i = 1; i <= maxdeg; i++) {
			Rational c = new Rational();
			for (int d = 1; d <= i && d < a.size(); d++) {
				if (i % d == 0) {
					final Ifactor m = new Ifactor(i / d);
					c = c.add(a.elementAt(d).multiply(m.moebius()));
				}
			}
			r.set(i, c);
		}
		r.simplify();
		return r;
	} /* mobiusT */

	/**
	 * Inverse Mobius transform.
	 *
	 * @param maxdeg
	 *            the maximum polynomial degree of the result
	 * @return the sequence of coefficients is the inverse Mobius transform of
	 *         the original sequence.
	 * @since 2008-12-02
	 */
	public RatPoly mobiusTInv(final int maxdeg) {
		/*
		 * Start with the polynomial 0
		 */
		final RatPoly r = new RatPoly();
		for (int i = 1; i <= maxdeg; i++) {
			Rational c = new Rational();
			for (int d = 1; d <= i && d < a.size(); d++) {
				if (i % d == 0) {
					c = c.add(a.elementAt(d));
				}
			}
			r.set(i, c);
		}
		r.simplify();
		return r;
	} /* mobiusTInv */

	/**
	 * Binomial transform.
	 *
	 * @param maxdeg
	 *            the maximum polynomial degree of the result
	 * @return the sequence of coefficients is the binomial transform of the
	 *         original sequence.
	 * @since 2008-10-26
	 */
	public RatPoly binomialT(final int maxdeg) {
		final RatPoly r = new RatPoly();
		for (int i = 0; i <= maxdeg; i++) {
			Rational c = new Rational(0, 1);
			for (int j = 0; j <= i && j < a.size(); j++) {
				c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j)));
			}
			r.set(i, c);
		}
		r.simplify();
		return r;
	} /* binomialT */

	/**
	 * Inverse Binomial transform.
	 *
	 * @param maxdeg
	 *            the maximum polynomial degree of the result
	 * @return the sequence of coefficients is the inverse binomial transform of
	 *         the original sequence.
	 * @since 2008-10-26
	 */
	public RatPoly binomialTInv(final int maxdeg) {
		final RatPoly r = new RatPoly();
		for (int i = 0; i <= maxdeg; i++) {
			Rational c = new Rational(0, 1);
			for (int j = 0; j <= i && j < a.size(); j++) {
				if ((j + i) % 2 != 0) {
					c = c.subtract(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j)));
				} else {
					c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j)));
				}
			}
			r.set(i, c);
		}
		r.simplify();
		return r;
	} /* binomialTInv */

	/**
	 * Truncate polynomial degree.
	 *
	 * @param newdeg
	 *            The maximum degree of the result.
	 * @return The polynomial with all coefficients beyond deg set to zero.
	 *         If newdeg =3, for example the polynomial returned has at most
	 *         degree 3.
	 *         If newdeg is larger than the degree of this, zeros (at the higher
	 *         orders of x)
	 *         are appended. That polynomial would have formal degree larger
	 *         than this.
	 * @since 2008-10-26
	 */
	public RatPoly trunc(final int newdeg) {
		final RatPoly t = new RatPoly();
		for (int i = 0; i <= newdeg; i++) {
			t.set(i, at(i));
		}
		t.simplify();
		return t;
	} /* trunc */

	/**
	 * Generate the roots of the polynomial in floating point arithmetic.
	 *
	 * @see <a href="http://en.wikipedia.org/wiki/Durand-Kerner_method">Durand
	 *      Kerner method</a>
	 * @param the
	 *            number of floating point digits
	 * @throws Error
	 * @since 2008-10-26
	 */
	public Vector<BigComplex> roots(final int digits) throws Error {
		final RatPoly mon = monic();

		final Random rand = new Random();
		final MathContext mc = new MathContext(digits + 3, RoundingMode.DOWN);

		Vector<BigComplex> res = new Vector<>();

		final int d = mon.degree();
		double randRad = 0.;
		for (int i = 0; i <= d; i++) {
			/* scale coefficient at maximum degree */
			final double absi = Math.abs(mon.at(i).doubleValue());
			if (absi > randRad) {
				randRad = absi;
			}
		}
		randRad += 1.0;

		/*
		 * initial values randomly in radius 1+randRad
		 */
		for (int i = 0; i < d; i++) {
			final double rad = randRad * rand.nextDouble();
			final double phi = 2.0 * 3.14159 * rand.nextDouble();
			res.add(i, new BigComplex(rad * Math.cos(phi), rad * Math.sin(phi)));
		}

		/*
		 * iterate until convr indicates that all values changed by less than
		 * the digits
		 * precision indicates.
		 */
		boolean convr = false;
		for (; !convr;)// ORIGINAL LINE: for(int itr =0 ; ! convr ; itr++)
		{
			convr = true;
			final Vector<BigComplex> resPlus = new Vector<>();
			for (int v = 0; v < d; v++) {
				/*
				 * evaluate f(x)/(x-root1)/(x-root2)/... (x-rootdegr), Newton
				 * method
				 */
				BigComplex thisx = res.elementAt(v);
				BigComplex nv = mon.valueOf(thisx, mc);
				for (int j = 0; j < d; j++) {
					if (j != v) {
						nv = nv.divide(thisx.subtract(res.elementAt(j)), mc);
					}
				}

				/* is this value converged ? */
				if (nv.abs(mc).doubleValue() > thisx.abs(mc).doubleValue() * Math.pow(10.0, -digits)) {
					convr = false;
				}

				thisx = thisx.subtract(nv);

				/* If unstable, start over */
				if (thisx.abs(MathContext.DECIMAL32).doubleValue() > randRad) {
					return roots(digits);
				}

				resPlus.add(thisx);
			}
			res = resPlus;

		}
		return res;
	} /* roots */

	/**
	 * Generate the integer roots of the polynomial.
	 *
	 * @return The vector of integer roots, with multiplicity.
	 *         The shows alternatingly first a root then its multiplicty, then
	 *         another root and multiplicty etc.
	 * @since 2008-10-26
	 */
	public Vector<BigInteger> iroots() {
		/* The vector of the roots */
		final Vector<BigInteger> res = new Vector<>();

		final int lowd = ldegree();
		if (lowd == 0 && a.elementAt(0).compareTo(BigInteger.ZERO) == 0) {
			/*
			 * Case of polynomial identical to zero:
			 * reported as a simple root of value 0.
			 */
			res.add(BigInteger.ZERO);
			res.add(BigInteger.ONE);
			return res;
		}

		/*
		 * multiply all coefs with the lcm() to get an integer polynomial
		 * start with denominator of first non-zero coefficient.
		 */
		BigInteger lcmDeno = a.elementAt(lowd).b;
		for (int i = lowd + 1; i < degree(); i++) {
			lcmDeno = BigIntegerMath.lcm(lcmDeno, a.elementAt(i).b);
		}

		/*
		 * and eventually get the integer polynomial by ignoring the
		 * denominators
		 */
		final Vector<BigInteger> ipo = new Vector<>();
		for (int i = 0; i < a.size(); i++) {
			final BigInteger d = a.elementAt(i).a.multiply(lcmDeno).divide(a.elementAt(i).b);
			ipo.add(d);
		}

		final BigIntegerPoly p = new BigIntegerPoly(ipo);
		/*
		 * collect the integer roots (multiple roots only once). Since we
		 * removed the zero already above, cand does not contain zeros.
		 */
		final Vector<BigInteger> cand = p.iroots();
		for (int i = 0; i < cand.size(); i++) {
			final BigInteger r = cand.elementAt(i);
			final int deg = p.rootDeg(r);
			res.add(r);
			res.add(new BigInteger("" + deg));
		}

		return res;
	} /* iroots */

} /* RatPoly */
