001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math.analysis.polynomials;
018
019 import java.io.Serializable;
020 import java.util.Arrays;
021
022 import org.apache.commons.math.exception.util.LocalizedFormats;
023 import org.apache.commons.math.exception.NoDataException;
024 import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
025 import org.apache.commons.math.analysis.UnivariateRealFunction;
026 import org.apache.commons.math.util.FastMath;
027
028 /**
029 * Immutable representation of a real polynomial function with real coefficients.
030 * <p>
031 * <a href="http://mathworld.wolfram.com/HornersMethod.html">Horner's Method</a>
032 * is used to evaluate the function.</p>
033 *
034 * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 d??c. 2010) $
035 */
036 public class PolynomialFunction implements DifferentiableUnivariateRealFunction, Serializable {
037
038 /**
039 * Serialization identifier
040 */
041 private static final long serialVersionUID = -7726511984200295583L;
042
043 /**
044 * The coefficients of the polynomial, ordered by degree -- i.e.,
045 * coefficients[0] is the constant term and coefficients[n] is the
046 * coefficient of x^n where n is the degree of the polynomial.
047 */
048 private final double coefficients[];
049
050 /**
051 * Construct a polynomial with the given coefficients. The first element
052 * of the coefficients array is the constant term. Higher degree
053 * coefficients follow in sequence. The degree of the resulting polynomial
054 * is the index of the last non-null element of the array, or 0 if all elements
055 * are null.
056 * <p>
057 * The constructor makes a copy of the input array and assigns the copy to
058 * the coefficients property.</p>
059 *
060 * @param c polynomial coefficients
061 * @throws NullPointerException if c is null
062 * @throws NoDataException if c is empty
063 */
064 public PolynomialFunction(double c[]) {
065 super();
066 int n = c.length;
067 if (n == 0) {
068 throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
069 }
070 while ((n > 1) && (c[n - 1] == 0)) {
071 --n;
072 }
073 this.coefficients = new double[n];
074 System.arraycopy(c, 0, this.coefficients, 0, n);
075 }
076
077 /**
078 * Compute the value of the function for the given argument.
079 * <p>
080 * The value returned is <br>
081 * <code>coefficients[n] * x^n + ... + coefficients[1] * x + coefficients[0]</code>
082 * </p>
083 *
084 * @param x the argument for which the function value should be computed
085 * @return the value of the polynomial at the given point
086 * @see UnivariateRealFunction#value(double)
087 */
088 public double value(double x) {
089 return evaluate(coefficients, x);
090 }
091
092
093 /**
094 * Returns the degree of the polynomial
095 *
096 * @return the degree of the polynomial
097 */
098 public int degree() {
099 return coefficients.length - 1;
100 }
101
102 /**
103 * Returns a copy of the coefficients array.
104 * <p>
105 * Changes made to the returned copy will not affect the coefficients of
106 * the polynomial.</p>
107 *
108 * @return a fresh copy of the coefficients array
109 */
110 public double[] getCoefficients() {
111 return coefficients.clone();
112 }
113
114 /**
115 * Uses Horner's Method to evaluate the polynomial with the given coefficients at
116 * the argument.
117 *
118 * @param coefficients the coefficients of the polynomial to evaluate
119 * @param argument the input value
120 * @return the value of the polynomial
121 * @throws NoDataException if coefficients is empty
122 * @throws NullPointerException if coefficients is null
123 */
124 protected static double evaluate(double[] coefficients, double argument) {
125 int n = coefficients.length;
126 if (n == 0) {
127 throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
128 }
129 double result = coefficients[n - 1];
130 for (int j = n -2; j >=0; j--) {
131 result = argument * result + coefficients[j];
132 }
133 return result;
134 }
135
136 /**
137 * Add a polynomial to the instance.
138 * @param p polynomial to add
139 * @return a new polynomial which is the sum of the instance and p
140 */
141 public PolynomialFunction add(final PolynomialFunction p) {
142
143 // identify the lowest degree polynomial
144 final int lowLength = FastMath.min(coefficients.length, p.coefficients.length);
145 final int highLength = FastMath.max(coefficients.length, p.coefficients.length);
146
147 // build the coefficients array
148 double[] newCoefficients = new double[highLength];
149 for (int i = 0; i < lowLength; ++i) {
150 newCoefficients[i] = coefficients[i] + p.coefficients[i];
151 }
152 System.arraycopy((coefficients.length < p.coefficients.length) ?
153 p.coefficients : coefficients,
154 lowLength,
155 newCoefficients, lowLength,
156 highLength - lowLength);
157
158 return new PolynomialFunction(newCoefficients);
159
160 }
161
162 /**
163 * Subtract a polynomial from the instance.
164 * @param p polynomial to subtract
165 * @return a new polynomial which is the difference the instance minus p
166 */
167 public PolynomialFunction subtract(final PolynomialFunction p) {
168
169 // identify the lowest degree polynomial
170 int lowLength = FastMath.min(coefficients.length, p.coefficients.length);
171 int highLength = FastMath.max(coefficients.length, p.coefficients.length);
172
173 // build the coefficients array
174 double[] newCoefficients = new double[highLength];
175 for (int i = 0; i < lowLength; ++i) {
176 newCoefficients[i] = coefficients[i] - p.coefficients[i];
177 }
178 if (coefficients.length < p.coefficients.length) {
179 for (int i = lowLength; i < highLength; ++i) {
180 newCoefficients[i] = -p.coefficients[i];
181 }
182 } else {
183 System.arraycopy(coefficients, lowLength, newCoefficients, lowLength,
184 highLength - lowLength);
185 }
186
187 return new PolynomialFunction(newCoefficients);
188
189 }
190
191 /**
192 * Negate the instance.
193 * @return a new polynomial
194 */
195 public PolynomialFunction negate() {
196 double[] newCoefficients = new double[coefficients.length];
197 for (int i = 0; i < coefficients.length; ++i) {
198 newCoefficients[i] = -coefficients[i];
199 }
200 return new PolynomialFunction(newCoefficients);
201 }
202
203 /**
204 * Multiply the instance by a polynomial.
205 * @param p polynomial to multiply by
206 * @return a new polynomial
207 */
208 public PolynomialFunction multiply(final PolynomialFunction p) {
209
210 double[] newCoefficients = new double[coefficients.length + p.coefficients.length - 1];
211
212 for (int i = 0; i < newCoefficients.length; ++i) {
213 newCoefficients[i] = 0.0;
214 for (int j = FastMath.max(0, i + 1 - p.coefficients.length);
215 j < FastMath.min(coefficients.length, i + 1);
216 ++j) {
217 newCoefficients[i] += coefficients[j] * p.coefficients[i-j];
218 }
219 }
220
221 return new PolynomialFunction(newCoefficients);
222
223 }
224
225 /**
226 * Returns the coefficients of the derivative of the polynomial with the given coefficients.
227 *
228 * @param coefficients the coefficients of the polynomial to differentiate
229 * @return the coefficients of the derivative or null if coefficients has length 1.
230 * @throws NoDataException if coefficients is empty
231 * @throws NullPointerException if coefficients is null
232 */
233 protected static double[] differentiate(double[] coefficients) {
234 int n = coefficients.length;
235 if (n == 0) {
236 throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
237 }
238 if (n == 1) {
239 return new double[]{0};
240 }
241 double[] result = new double[n - 1];
242 for (int i = n - 1; i > 0; i--) {
243 result[i - 1] = i * coefficients[i];
244 }
245 return result;
246 }
247
248 /**
249 * Returns the derivative as a PolynomialRealFunction
250 *
251 * @return the derivative polynomial
252 */
253 public PolynomialFunction polynomialDerivative() {
254 return new PolynomialFunction(differentiate(coefficients));
255 }
256
257 /**
258 * Returns the derivative as a UnivariateRealFunction
259 *
260 * @return the derivative function
261 */
262 public UnivariateRealFunction derivative() {
263 return polynomialDerivative();
264 }
265
266 /** Returns a string representation of the polynomial.
267
268 * <p>The representation is user oriented. Terms are displayed lowest
269 * degrees first. The multiplications signs, coefficients equals to
270 * one and null terms are not displayed (except if the polynomial is 0,
271 * in which case the 0 constant term is displayed). Addition of terms
272 * with negative coefficients are replaced by subtraction of terms
273 * with positive coefficients except for the first displayed term
274 * (i.e. we display <code>-3</code> for a constant negative polynomial,
275 * but <code>1 - 3 x + x^2</code> if the negative coefficient is not
276 * the first one displayed).</p>
277
278 * @return a string representation of the polynomial
279
280 */
281 @Override
282 public String toString() {
283
284 StringBuilder s = new StringBuilder();
285 if (coefficients[0] == 0.0) {
286 if (coefficients.length == 1) {
287 return "0";
288 }
289 } else {
290 s.append(Double.toString(coefficients[0]));
291 }
292
293 for (int i = 1; i < coefficients.length; ++i) {
294
295 if (coefficients[i] != 0) {
296
297 if (s.length() > 0) {
298 if (coefficients[i] < 0) {
299 s.append(" - ");
300 } else {
301 s.append(" + ");
302 }
303 } else {
304 if (coefficients[i] < 0) {
305 s.append("-");
306 }
307 }
308
309 double absAi = FastMath.abs(coefficients[i]);
310 if ((absAi - 1) != 0) {
311 s.append(Double.toString(absAi));
312 s.append(' ');
313 }
314
315 s.append("x");
316 if (i > 1) {
317 s.append('^');
318 s.append(Integer.toString(i));
319 }
320 }
321
322 }
323
324 return s.toString();
325
326 }
327
328 /** {@inheritDoc} */
329 @Override
330 public int hashCode() {
331 final int prime = 31;
332 int result = 1;
333 result = prime * result + Arrays.hashCode(coefficients);
334 return result;
335 }
336
337 /** {@inheritDoc} */
338 @Override
339 public boolean equals(Object obj) {
340 if (this == obj)
341 return true;
342 if (!(obj instanceof PolynomialFunction))
343 return false;
344 PolynomialFunction other = (PolynomialFunction) obj;
345 if (!Arrays.equals(coefficients, other.coefficients))
346 return false;
347 return true;
348 }
349
350 }