001: package JSci.maths.polynomials;
002:
003: import JSci.GlobalSettings;
004: import JSci.maths.MathDouble;
005: import JSci.maths.analysis.RealFunction;
006: import JSci.maths.fields.*;
007: import JSci.maths.fields.Field;
008: import JSci.maths.groups.AbelianGroup;
009:
010: /** A Polynomial as a <code>Ring.Member</code> over a <i>real</i> <code>Field</code>
011: * @author b.dietrich
012: * @author Mark Hale
013: */
014: public class RealPolynomial extends RealFunction implements Polynomial {
015: /** Coefficients in ascending degree order */
016: private double[] _coeff;
017:
018: /** Creates a new instance of RealPolynomial */
019: public RealPolynomial(double[] coeff) {
020: if (coeff == null) {
021: throw new NullPointerException(
022: "Coefficients cannot be null");
023: }
024: _coeff = normalise(coeff);
025: }
026:
027: /**
028: * Normalises the coefficient array.
029: * Trims off any leading (high degree) zero terms.
030: */
031: private static double[] normalise(double[] c) {
032: int i = c.length - 1;
033: while (i >= 0 && Math.abs(c[i]) <= GlobalSettings.ZERO_TOL)
034: i--;
035: if (i < 0) {
036: return new double[] { 0.0 };
037: } else if (i < c.length - 1) {
038: double[] arr = new double[i + 1];
039: System.arraycopy(c, 0, arr, 0, arr.length);
040: return arr;
041: } else {
042: return c;
043: }
044: }
045:
046: /**
047: * Creates a new RealPolynomial object.
048: *
049: * @param f
050: */
051: public RealPolynomial(Field.Member[] f) {
052: if (f == null) {
053: throw new NullPointerException(
054: "Coefficients cannot be null");
055: }
056: _coeff = normalise(toDoubleArray(f));
057: }
058:
059: private static double[] toDoubleArray(Field.Member[] f) {
060: double[] arr = new double[f.length];
061: for (int i = 0; i < arr.length; i++) {
062: if (f[i] instanceof MathDouble)
063: arr[i] = ((MathDouble) f[i]).value();
064: else
065: throw new IllegalArgumentException(
066: "Different fields. Argument was "
067: + f[i].getClass());
068: }
069: return arr;
070: }
071:
072: /** Get the coefficient of degree k, i.e. <I>a_k</I> if
073: * <I>P(x)</I> := sum_{k=0}^n <I>a_k x^k</I>
074: * @param k degree
075: * @return coefficient as described above
076: */
077: public Field.Member getCoefficient(int k) {
078: return new MathDouble(getCoefficientAsDouble(k));
079: }
080:
081: /** Get the coefficient of degree k, i.e. <I>a_k</I> if
082: * <I>P(x)</I> := sum_{k=0}^n <I>a_k x^k</I> as a real number
083: * @param k degree
084: * @return coefficient as described above
085: */
086: public double getCoefficientAsDouble(int k) {
087: if (k >= _coeff.length) {
088: return 0.0;
089: } else {
090: return _coeff[k];
091: }
092: }
093:
094: /** Get the coefficients as an array
095: * @return the coefficients as an array
096: */
097: public Field.Member[] getCoefficients() {
098: return RealPolynomialRing
099: .toMathDouble(getCoefficientsAsDoubles());
100: }
101:
102: /** Get the coefficients as an array of doubles
103: * @return the coefficients as an array
104: */
105: public double[] getCoefficientsAsDoubles() {
106: return _coeff;
107: }
108:
109: /**
110: * Evaluates this polynomial.
111: */
112: public double map(double x) {
113: return PolynomialMath.evalPolynomial(this , x);
114: }
115:
116: /** The degree
117: * @return the degree
118: */
119: public int degree() {
120: return _coeff.length - 1;
121: }
122:
123: public Object getSet() {
124: return RealPolynomialRing.getInstance();
125: }
126:
127: /**
128: * Returns true if this polynomial is equal to zero.
129: * All coefficients are tested for |a_k| < GlobalSettings.ZERO_TOL.
130: * @return true if all coefficients < GlobalSettings.ZERO_TOL
131: */
132: public boolean isZero() {
133: for (int k = 0; k < _coeff.length; k++) {
134: if (Math.abs(_coeff[k]) > GlobalSettings.ZERO_TOL) {
135: return false;
136: }
137: }
138:
139: return true;
140: }
141:
142: /**
143: * Returns true if this polynomial is equal to one.
144: * It is tested, whether |a_0 - 1| <= GlobalSettings.ZERO_TOL and the remaining
145: * coefficients are |a_k| < GlobalSettings.ZERO_TOL.
146: * @return true if this is equal to one.
147: */
148: public boolean isOne() {
149: if (Math.abs(_coeff[0] - 1.0) > GlobalSettings.ZERO_TOL)
150: return false;
151:
152: for (int k = 1; k < _coeff.length; k++) {
153: if (Math.abs(_coeff[k]) > GlobalSettings.ZERO_TOL) {
154: return false;
155: }
156: }
157:
158: return true;
159: }
160:
161: /** The group composition law. Returns a new polynom with grade = max( this.grade, g.grade)
162: * @param g a group member
163: *
164: */
165: public RealFunction add(RealFunction g) {
166: if (g instanceof RealPolynomial) {
167: RealPolynomial p = (RealPolynomial) g;
168: int maxgrade = PolynomialMath.maxDegree(this , p);
169: double[] c = new double[maxgrade + 1];
170: for (int k = 0; k < c.length; k++) {
171: c[k] = getCoefficientAsDouble(k)
172: + p.getCoefficientAsDouble(k);
173: }
174: return new RealPolynomial(c);
175: } else {
176: return super .add(g);
177: }
178: }
179:
180: /**
181: * Differentiate the real polynomial. Only useful iff the polynomial is built over
182: * a Banach space and an appropriate multiplication law is provided.
183: *
184: * @return a new polynomial with degree = max(this.degree-1 , 0)
185: */
186: public RealFunction differentiate() {
187: if (degree() == 0) {
188: return (RealPolynomial) RealPolynomialRing.getInstance()
189: .zero();
190: } else {
191: double[] dn = new double[degree()];
192: for (int k = 0; k < dn.length; k++) {
193: dn[k] = getCoefficientAsDouble(k + 1) * (k + 1);
194: }
195:
196: return new RealPolynomial(dn);
197: }
198: }
199:
200: /** return a new real Polynomial with coefficients divided by <I>f</I>
201: * @param f divisor
202: * @return new Polynomial with coefficients /= <I>f</I>
203: */
204: public Polynomial scalarDivide(Field.Member f) {
205: if (f instanceof Number) {
206: double a = ((Number) f).doubleValue();
207:
208: return scalarDivide(a);
209: } else {
210: throw new IllegalArgumentException(
211: "Member class not recognised by this method.");
212: }
213: }
214:
215: /** return a new real Polynomial with coefficients divided by <I>a</I>
216: * @param a divisor
217: * @return new Polynomial with coefficients /= <I>a</I>
218: */
219: public RealPolynomial scalarDivide(double a) {
220: double[] c = new double[_coeff.length];
221: for (int k = 0; k < c.length; k++) {
222: c[k] = _coeff[k] / a;
223: }
224:
225: return new RealPolynomial(c);
226: }
227:
228: /**
229: * Returns true if this polynomial is equal to another.
230: * @param o the other polynomial
231: *
232: * @return true if so
233: */
234: public boolean equals(Object o) {
235: if (o == this ) {
236: return true;
237: } else if (o instanceof RealPolynomial) {
238: RealPolynomial p = (RealPolynomial) o;
239:
240: return ((RealPolynomial) this .subtract(p)).isZero();
241: }
242:
243: return false;
244: }
245:
246: /**
247: * Some kind of hashcode... (Since I have an equals)
248: * @return a hashcode
249: */
250: public int hashCode() {
251: int res = 0;
252: for (int k = 0; k < _coeff.length; k++) {
253: res += (int) (_coeff[k] * 10.0);
254: }
255:
256: return res;
257: }
258:
259: /**
260: * "inverse" operation for differentiate
261: * @return the integrated polynomial
262: */
263: public RealPolynomial integrate() {
264: double[] dn = new double[_coeff.length + 1];
265: for (int k = 1; k < dn.length; k++) {
266: dn[k] = getCoefficientAsDouble(k - 1) / k;
267: }
268:
269: return new RealPolynomial(dn);
270: }
271:
272: /**
273: * Returns the multiplication of this polynomial by a scalar
274: * @param f
275: */
276: public Polynomial scalarMultiply(Field.Member f) {
277: if (f instanceof Number) {
278: double a = ((Number) f).doubleValue();
279: return scalarMultiply(a);
280: } else {
281: throw new IllegalArgumentException(
282: "Member class not recognised by this method.");
283: }
284: }
285:
286: /**
287: * Returns the multiplication of this polynomial by a scalar
288: * @param a factor
289: * @return new Polynomial with coefficients *= <I>a</I>
290: */
291: public RealPolynomial scalarMultiply(double a) {
292: double[] c = new double[_coeff.length];
293: for (int k = 0; k < c.length; k++) {
294: c[k] = _coeff[k] * a;
295: }
296:
297: return new RealPolynomial(c);
298: }
299:
300: /**
301: * The multiplication law. Multiplies this Polynomial with another
302: * @param r a polynomial
303: * @return a new Polynomial with grade = max( this.grade, r.grade) + min( this.grade, r.grade)
304: */
305: public RealFunction multiply(RealFunction r) {
306: if (r instanceof RealPolynomial) {
307: RealPolynomial p = (RealPolynomial) r;
308: int maxgrade = PolynomialMath.maxDegree(this , p);
309: int mingrade = PolynomialMath.minDegree(this , p);
310: int destgrade = maxgrade + mingrade;
311: double[] n = new double[destgrade + 1];
312: for (int k = 0; k < _coeff.length; k++) {
313: for (int j = 0; j < p._coeff.length; j++) {
314: n[k + j] += (_coeff[k] * p._coeff[j]);
315: }
316: }
317:
318: return new RealPolynomial(n);
319: } else {
320: throw new IllegalArgumentException(
321: "Member class not recognised by this method.");
322: }
323: }
324:
325: /** Returns the inverse member. (That is mult(-1))
326: * @return inverse
327: */
328: public AbelianGroup.Member negate() {
329: double[] c = new double[_coeff.length];
330: for (int k = 0; k < c.length; k++) {
331: c[k] = -_coeff[k];
332: }
333:
334: return new RealPolynomial(c);
335: }
336:
337: /** The group composition law with inverse.
338: * @param g a group member
339: *
340: */
341: public RealFunction subtract(RealFunction g) {
342: if (g instanceof RealPolynomial) {
343: RealPolynomial p = (RealPolynomial) g;
344: int maxgrade = PolynomialMath.maxDegree(this , p);
345: double[] c = new double[maxgrade + 1];
346: for (int k = 0; k < c.length; k++) {
347: c[k] = getCoefficientAsDouble(k)
348: - p.getCoefficientAsDouble(k);
349: }
350: return new RealPolynomial(c);
351: } else {
352: return super .subtract(g);
353: }
354: }
355:
356: /**
357: * String representation <I>P(x) = a_k x^k +...</I>
358: * @return String
359: */
360: public String toString() {
361: StringBuffer sb = new StringBuffer("P(x) = ");
362: if (_coeff[degree()] < 0.0) {
363: sb.append("-");
364: } else {
365: sb.append(" ");
366: }
367: for (int k = degree(); k > 0; k--) {
368: sb.append(Math.abs(_coeff[k])).append("x^").append(k)
369: .append((_coeff[k - 1] >= 0.0) ? " + " : " - ");
370: }
371: sb.append(Math.abs(_coeff[0]));
372:
373: return sb.toString();
374: }
375: }
|