org.jscience.mathematics.function.Polynomial Maven / Gradle / Ivy
/*
* JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
* Copyright (C) 2006 - JScience (http://jscience.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package org.jscience.mathematics.function;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jscience.mathematics.structure.GroupAdditive;
import org.jscience.mathematics.structure.GroupMultiplicative;
import org.jscience.mathematics.structure.Ring;
import javolution.util.FastMap;
import javolution.util.FastTable;
import javolution.context.ObjectFactory;
import javolution.text.Text;
import javolution.text.TextBuilder;
/**
* This class represents a mathematical expression involving a sum of powers
* in one or more {@link Variable variables} multiplied by
* coefficients (such as x² + x·y + 3y²
).
*
* Polynomials are characterized by the type of variable they operate
* upon. For example:[code]
* Variable> varX = new Variable.Local>("x");
* Polynomial> x = Polynomial.valueOf(Amount.valueOf(1, SI.METER), varX);
* and
* Variable varX = new Variable.Local("x");
* Polynomial x = Polynomial.valueOf(Complex.ONE, varX);[/code]
* are two different polynomials, the first one operates on physical
* {@link org.jscience.physics.amount.Amount measures},
* whereas the second operates on
* {@link org.jscience.mathematics.number.Complex complex} numbers.
*
* Terms (others than {@link Term#ONE ONE}) having zero (additive identity)
* for coefficient are automatically removed.
*
* @author Jean-Marie Dautelle
* @version 3.1, April 1, 2006
*/
public class Polynomial> extends Function implements
Ring> {
/**
* Holds the terms to coefficients mapping
* (never empty, holds Term.ONE when constant)
*/
final FastMap _termToCoef = new FastMap();
/**
* Default constructor.
*/
Polynomial() {
}
/**
* Returns an univariate polynomial of degree one with the specified
* coefficient multiplier.
*
* @param coefficient the coefficient for the variable of degree 1.
* @param variable the variable for this polynomial.
* @return valueOf(coefficient, Term.valueOf(variable, 1))
*/
public static > Polynomial valueOf(R coefficient,
Variable variable) {
return valueOf(coefficient, Term.valueOf(variable, 1));
}
/**
* Returns a polynomial corresponding to the specified {@link Term term}
* with the specified coefficient multiplier.
*
* @param coefficient the coefficient multiplier.
* @param term the term multiplicand.
* @return coefficient * term
*/
public static > Polynomial valueOf(R coefficient,
Term term) {
if (term.equals(Term.ONE)) return Constant.valueOf(coefficient);
if (isZero(coefficient)) return Constant.valueOf(coefficient);
Polynomial p = Polynomial.newInstance();
p._termToCoef.put(term, coefficient);
return p;
}
private static boolean isZero(GroupAdditive coefficient) {
return coefficient.equals(coefficient.opposite());
}
/**
* Returns the terms of this polynomial.
*
* @return this polynomial's terms.
*/
public Set getTerms() {
return _termToCoef.unmodifiable().keySet();
}
/**
* Returns the coefficient for the specified term.
*
* @param term the term for which the coefficient is returned.
* @return the coefficient for the specified term or null
* if this polynomial does not contain the specified term.
*/
public final R getCoefficient(Term term) {
return _termToCoef.get(term);
}
/**
* Returns the order of this polynomial for the specified variable.
*
* @return the polynomial order relative to the specified variable.
*/
public int getOrder(Variable v) {
int order = 0;
for (Term term : _termToCoef.keySet()) {
int power = term.getPower(v);
if (power > order) {
order = power;
}
}
return order;
}
/**
* Returns the sum of this polynomial with a constant polynomial
* having the specified value (convenience method).
*
* @param constantValue the value of the constant polynomial to add.
* @return this + Constant.valueOf(constantValue)
*/
public Polynomial plus(R constantValue) {
return this.plus(Constant.valueOf(constantValue));
}
/**
* Returns the product of this polynomial with a constant polynomial
* having the specified value (convenience method).
*
* @param constantValue the value of the constant polynomial to multiply.
* @return this · Constant.valueOf(constantValue)
*/
public Polynomial times(R constantValue) {
return this.times(Constant.valueOf(constantValue));
}
/**
* Returns the sum of two polynomials.
*
* @param that the polynomial being added.
* @return this + that
*/
public Polynomial plus(Polynomial that) {
Polynomial result = Polynomial.newInstance();
R zero = null;
result._termToCoef.putAll(this._termToCoef);
result._termToCoef.putAll(that._termToCoef);
for (FastMap.Entry e = result._termToCoef.head(),
end = result._termToCoef.tail(); (e = e.getNext()) != end;) {
Term term = e.getKey();
R thisCoef = this._termToCoef.get(term);
R thatCoef = that._termToCoef.get(term);
if ((thisCoef != null) && (thatCoef != null)) {
R sum = thisCoef.plus(thatCoef);
if (isZero(sum)) { // Remove entry (be careful iterating)
FastMap.Entry prev = e.getPrevious();
result._termToCoef.remove(term);
e = prev; // Move back to previous entry.
zero = sum; // To be used if constant polynomial.
} else {
result._termToCoef.put(term, sum);
}
} // Else the current coefficient is correct.
}
if (result._termToCoef.size() == 0) return Constant.valueOf(zero);
return result;
}
/**
* Returns the opposite of this polynomial.
*
* @return - this
*/
public Polynomial opposite() {
Polynomial result = Polynomial.newInstance();
for (FastMap.Entry e = _termToCoef.head(),
end = _termToCoef.tail(); (e = e.getNext()) != end;) {
result._termToCoef.put(e.getKey(), e.getValue().opposite());
}
return result;
}
/**
* Returns the difference of two polynomials.
*
* @param that the polynomial being subtracted.
* @return this - that
*/
public Polynomial minus(Polynomial that) {
return this.plus(that.opposite());
}
/**
* Returns the product of two polynomials.
*
* @param that the polynomial multiplier.
* @return this · that
*/
public Polynomial times(Polynomial that) {
Polynomial result = Polynomial.newInstance();
R zero = null;
for (Map.Entry entry1 : this._termToCoef.entrySet()) {
Term t1 = entry1.getKey();
R c1 = entry1.getValue();
for (Map.Entry entry2 : that._termToCoef.entrySet()) {
Term t2 = entry2.getKey();
R c2 = entry2.getValue();
Term t = t1.times(t2);
R c = c1.times(c2);
R prev = result.getCoefficient(t);
R coef = (prev != null) ? prev.plus(c) : c;
if (isZero(coef)) {
zero = coef;
} else {
result._termToCoef.put(t, coef);
}
}
}
if (result._termToCoef.size() == 0) return Constant.valueOf(zero);
return result;
}
/**
* Returns the composition of this polynomial with the one specified.
*
* @param that the polynomial for which the return value is passed as
* argument to this function.
* @return the polynomial (this o that)
* @throws FunctionException if this function is not univariate.
*/
public Polynomial compose(Polynomial that) {
List> variables = getVariables();
if (getVariables().size() != 1)
throw new FunctionException("This polynomial is not monovariate");
Variable v = variables.get(0);
Polynomial result = null;
for (Map.Entry entry : this._termToCoef.entrySet()) {
Term term = entry.getKey();
Constant cst = Constant.valueOf(entry.getValue());
int power = term.getPower(v);
if (power > 0) {
Polynomial fn = that.pow(power);
result = (result != null) ? result.plus(cst.times(fn)) : cst
.times(fn);
} else { // power = 0
result = (result != null) ? result.plus(cst) : cst;
}
}
return result;
}
////////////////////////////////////////////////////////////////
// Overrides parent method potentially returning polynomials //
////////////////////////////////////////////////////////////////
@SuppressWarnings("unchecked")
@Override
public Function compose(Function that) {
return (Function) ((that instanceof Polynomial) ?
compose((Polynomial)that) : super.compose(that));
}
@Override
public Polynomial differentiate(Variable v) {
if (this.getOrder(v) > 0) {
Polynomial result = null;
for (Map.Entry entry : this._termToCoef.entrySet()) {
Term term = entry.getKey();
R coef = entry.getValue();
int power = term.getPower(v);
if (power > 0) {
R newCoef = multiply(coef, power);
Term newTerm = term.divide(Term.valueOf(v, 1));
Polynomial p = valueOf(newCoef, newTerm);
result = (result != null) ? result.plus(p) : p;
}
}
return result;
} else { // Returns zero.
R coef = _termToCoef.values().iterator().next();
return Constant.valueOf(coef.plus(coef.opposite()));
}
}
private static > R multiply(R o, int n) {
if (n <= 0)
throw new IllegalArgumentException("n: " + n
+ " zero or negative values not allowed");
R shift2 = o;
R result = null;
while (n >= 1) { // Iteration.
if ((n & 1) == 1) {
result = (result == null) ? shift2 : result.plus(shift2);
}
shift2 = shift2.plus(shift2);
n >>>= 1;
}
return result;
}
@SuppressWarnings("unchecked")
@Override
public Polynomial integrate(Variable v) {
Polynomial result = null;
for (Map.Entry entry : this._termToCoef.entrySet()) {
Term term = entry.getKey();
R coef = entry.getValue();
int power = term.getPower(v);
R newCoef = (R)((GroupMultiplicative)multiply((R)((GroupMultiplicative)coef).inverse(), power + 1)).inverse();
Term newTerm = term.times(Term.valueOf(v, 1));
Polynomial p = valueOf(newCoef, newTerm);
result = (result != null) ? result.plus(p) : p;
}
return result;
}
@SuppressWarnings("unchecked")
@Override
public Function plus(Function that) {
return (that instanceof Polynomial) ?
this.plus((Polynomial)that) : super.plus(that);
}
@SuppressWarnings("unchecked")
@Override
public Function minus(Function that) {
return (that instanceof Polynomial) ?
this.minus((Polynomial)that) : super.minus(that);
}
@SuppressWarnings("unchecked")
@Override
public Function times(Function that) {
return (that instanceof Polynomial) ?
this.times((Polynomial)that) : super.times(that);
}
@SuppressWarnings("unchecked")
@Override
public Polynomial pow(int n) {
return (Polynomial) super.pow(n);
}
@SuppressWarnings("unchecked")
@Override
public List> getVariables() {
// We multiply all terms togethers, the resulting product
// will hold all variabgles (powers are always positive).
Term product = _termToCoef.head().getNext().getKey();
for (FastMap.Entry e = _termToCoef.head().getNext(),
end = _termToCoef.tail(); (e = e.getNext()) != end;) {
product = product.times(e.getKey());
}
FastTable vars = FastTable.newInstance();
for (int i=0, n = product.size(); i < n; i++) {
vars.add(product.getVariable(i));
}
return vars;
}
@Override
@SuppressWarnings("unchecked")
public R evaluate() {
R sum = null;
for (Map.Entry entry : _termToCoef.entrySet()) {
Term term = entry.getKey();
R coef = entry.getValue();
R termValue = (R) term.evaluate();
R value = (termValue != null) ? coef.times(termValue) : coef;
sum = (sum == null) ? value : sum.plus(value);
}
return sum;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Polynomial))
return false;
Polynomial that = (Polynomial) obj;
return this._termToCoef.equals(that._termToCoef);
}
@Override
public int hashCode() {
return _termToCoef.hashCode();
}
@Override
public Text toText() {
FastTable terms = FastTable.newInstance();
terms.addAll(_termToCoef.keySet());
terms.sort();
TextBuilder tb = TextBuilder.newInstance();
for (int i=0, n = terms.size(); i < n; i++) {
if (i != 0) {
tb.append(" + ");
}
tb.append('[').append(_termToCoef.get(terms.get(i)));
tb.append(']').append(terms.get(i));
}
return tb.toText();
}
/**
* Returns a copy of this polynomial
* {@link javolution.context.AllocatorContext allocated}
* by the calling thread (possibly on the stack).
*
* @return an identical and independant copy of this polynomial.
*/
public Polynomial copy() {
Polynomial p = Polynomial.newInstance();
for (Map.Entry entry : _termToCoef.entrySet()) {
p._termToCoef.put(entry.getKey().copy(), entry.getValue());
}
return p;
}
@SuppressWarnings("unchecked")
private static > Polynomial newInstance() {
Polynomial p = FACTORY.object();
p._termToCoef.clear();
return p;
}
@SuppressWarnings("unchecked")
private static final ObjectFactory FACTORY = new ObjectFactory() {
protected Polynomial create() {
return new Polynomial();
}
};
private static final long serialVersionUID = 1L;
}