001/*
002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
003 * Copyright (C) 2006 - JScience (http://jscience.org/)
004 * All rights reserved.
005 * 
006 * Permission to use, copy, modify, and distribute this software is
007 * freely granted, provided that this notice is preserved.
008 */
009package org.jscience.mathematics.function;
010
011import java.io.Serializable;
012import org.jscience.mathematics.structure.Ring;
013
014import javolution.context.ArrayFactory;
015import javolution.lang.MathLib;
016import javolution.lang.Realtime;
017import javolution.lang.ValueType;
018import javolution.text.Text;
019import javolution.text.TextBuilder;
020
021/**
022 * This class represents the term of a {@link Polynomial polynomial} 
023 * such as <code>x·y²</code>. 
024 * 
025 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
026 * @version 3.0, February 13, 2006
027 */
028public final class Term implements Serializable, Comparable<Term>, ValueType,
029        Realtime {
030
031    /**
032     * Holds the multiplicative identity.
033     */
034    public static Term ONE = new Term(0);
035
036    /**
037     * Holds the term's factory.
038     */
039    private static final ArrayFactory<Term> FACTORY = new ArrayFactory<Term>() {
040
041        @Override
042        protected Term create(int size) {
043            return new Term(size);
044        }
045    };
046
047    /**
048     * Holds the variables (ordered).
049     */
050    private final Variable<?>[] _variables;
051
052    /**
053     * Holds the corresponding powers (positive and different from zero).
054     */
055    private final int[] _powers;
056
057    /**
058     * Holds the number of variables.
059     */
060    private int _size;
061
062    /**
063     * Creates a new term of specified capacity.
064     * 
065     * @param capacity the maxium number of variables.
066     */
067    private Term(int capacity) {
068        _variables = new Variable[capacity];
069        _powers = new int[capacity];
070    }
071
072    /**
073     * Return the term corresponding to the specified variable raised to
074     * the specified power.
075     * 
076     * @param v the variable.
077     * @param n the power. 
078     * @return the term for <code>v<sup>n</sup></code>
079     * @throws IllegalArgumentException if <code>n &lt; 0</code> 
080     */
081    public static Term valueOf(Variable<?> v, int n) {
082        if (n == 0)
083            return ONE;
084        if (n < 0)
085            throw new IllegalArgumentException("n: " + n
086                    + " negative values are not allowed");
087        Term term = FACTORY.array(1);
088        term._variables[0] = v;
089        term._powers[0] = n;
090        term._size = 1;
091        return term;
092    }
093
094    /**
095     * Returns the number of variables for this term.
096     * 
097     * @return the number of variables.
098     */
099    public int size() {
100        return _size;
101    }
102
103    /**
104     * Returns the variable at the specified index (variables are 
105     * lexically ordered).
106     * 
107     * @param index the variable index.
108     * @return this term variables at specified position.
109     * @throws IndexOutOfBoundsException if  
110     *         <code>(index < 0) || (index >= size())</code>
111     */
112    public Variable<?> getVariable(int index) {
113        if (index > _size)
114            throw new IllegalArgumentException();
115        return _variables[index];
116    }
117
118    /**
119     * Returns the power of the variable at the specified position.
120     * 
121     * @param index the variable index.
122     * @return the power of the variable at the specified index.
123     * @throws IndexOutOfBoundsException if  
124     *         <code>(index < 0) || (index >= size())</code>
125     */
126    public int getPower(int index) {
127        if (index > _size)
128            throw new IllegalArgumentException();
129        return _powers[index];
130    }
131
132    /**
133     * Returns the power of the specified variable.
134     * 
135     * @param v the variable for which the power is returned.
136     * @return the power of the corresponding variable or <code>0</code> if 
137     *         this term does not hold the specified variable.
138     */
139    public int getPower(Variable<?> v) {
140        for (int i = 0; i < _size; i++) {
141            if (_variables[i] == v)
142                return _powers[i];
143        }
144        return 0;
145    }
146
147    /**
148     * Return the product of this term with the one specified. 
149     * 
150     * @param that the term multiplier.
151     * @return <code>this · that</code>
152     * @throws IllegalArgumentException if the specified term holds a 
153     *         variable having the same symbol as one of the variable of
154     *         this term; but both variables are distinct.
155     */
156    public Term times(Term that) {
157        final int thisSize = this.size();
158        final int thatSize = that.size();
159        Term result = FACTORY.array(thisSize + thatSize);
160        result._size = 0;
161        for (int i = 0, j = 0;;) {
162            Variable<?> left = (i < thisSize) ? this._variables[i] : null;
163            Variable<?> right = (j < thatSize) ? that._variables[j] : null;
164            if (left == null) {
165                if (right == null)
166                    return result;
167                result._powers[result._size] = that._powers[j++];
168                result._variables[result._size++] = right;
169                continue;
170            }
171            if (right == null) {
172                result._powers[result._size] = this._powers[i++];
173                result._variables[result._size++] = left;
174                continue;
175            }
176            if (right == left) {
177                result._powers[result._size] = this._powers[i++]
178                        + that._powers[j++];
179                result._variables[result._size++] = right;
180                continue;
181            }
182            final int cmp = left.getSymbol().compareTo(right.getSymbol());
183            if (cmp < 0) {
184                result._powers[result._size] = this._powers[i++];
185                result._variables[result._size++] = left;
186            } else if (cmp > 0) {
187                result._powers[result._size] = that._powers[j++];
188                result._variables[result._size++] = right;
189            } else {
190                throw new IllegalArgumentException(
191                        "Found distinct variables with same symbol: "
192                                + left.getSymbol());
193            }
194        }
195    }
196
197    /**
198     * Return the division of this term with the one specified. 
199     * 
200     * @param that the term divisor.
201     * @return <code>this / that</code>
202     * @throws UnsupportedOperationException if this division would 
203     *         result in negative power.
204     * @throws IllegalArgumentException if the specified term holds a 
205     *         variable having the same symbol as one of the variable of
206     *         this term; but both variables are distinct.
207     */
208    public Term divide(Term that) {
209        final int thisSize = this._size;
210        final int thatSize = that._size;
211        Term result = FACTORY.array(MathLib.max(thisSize, thatSize));
212        result._size = 0;
213        for (int i = 0, j = 0;;) {
214            Variable<?> left = (i < thisSize) ? this._variables[i] : null;
215            Variable<?> right = (j < thatSize) ? that._variables[j] : null;
216            if (left == null) {
217                if (right == null)
218                    return result;
219                throw new UnsupportedOperationException(this + "/" + that
220                        + " would result in a negative power");
221            }
222            if (right == null) {
223                result._powers[result._size] = this._powers[i++];
224                result._variables[result._size++] = left;
225                continue;
226            }
227            if (right == left) {
228                final int power = this._powers[i++] - that._powers[j++];
229                if (power < 0)
230                    throw new UnsupportedOperationException(this + "/" + that
231                            + " would result in a negative power");
232                if (power > 0) {
233                    result._powers[result._size] = power;
234                    result._variables[result._size++] = right;
235                }
236                continue;
237            }
238            final int cmp = left.getSymbol().compareTo(right.getSymbol());
239            if (cmp < 0) {
240                result._powers[result._size] = this._powers[i++];
241                result._variables[result._size++] = left;
242            } else if (cmp > 0) {
243                throw new UnsupportedOperationException(this + "/" + that
244                        + " would result in a negative power");
245            } else {
246                throw new IllegalArgumentException(
247                        "Found distinct variables with same symbol: "
248                                + left.getSymbol());
249            }
250        }
251    }
252
253    /**
254     * Indicates if this term is equal to the object specified.
255     *
256     * @param  obj the object to compare for equality.
257     * @return <code>true</code> if this term and the specified object are
258     *         considered equal; <code>false</code> otherwise.
259     */
260    public boolean equals(Object obj) {
261        if (this == obj)
262            return true;
263        if (!(obj instanceof Term))
264            return false;
265        Term that = (Term) obj;
266        if (this._size != that._size)
267            return false;
268        for (int i = 0; i < _size; i++) {
269            if ((!this._variables[i].equals(that._variables[i]))
270                    || (this._powers[i] != that._powers[i]))
271                return false;
272        }
273        return true;
274    }
275
276    /**
277     * Returns a hash code for this term.
278     *
279     * @return a hash code value for this object.
280     */
281    public final int hashCode() {
282        int h = 0;
283        for (int i = 0; i < _size; i++) {
284            h += _variables[i].hashCode() * _powers[i];
285        }
286        return h;
287    }
288
289    /**
290     * Returns the text representation of this term as a 
291     * <code>java.lang.String</code>.
292     * 
293     * @return <code>toText().toString()</code>
294     */
295    public final String toString() {
296        return toText().toString();
297    }
298
299    /**
300     * Returns the text representation of this term.
301     */
302    public Text toText() {
303        TextBuilder tb = TextBuilder.newInstance();
304        for (int i = 0; i < _size; i++) {
305            tb.append(_variables[i].getSymbol());
306            int power = _powers[i];
307            switch (power) {
308            case 1:
309                break;
310            case 2:
311                tb.append('²');
312                break;
313            case 3:
314                tb.append('³');
315                break;
316            default:
317                tb.append(power);
318            }
319        }
320        return tb.toText();
321    }
322
323    /**
324     * Returns an entierely new copy of this term 
325     * {@link javolution.context.AllocatorContext allocated} 
326     * by the calling thread (possibly on the stack).
327     *     
328     * @return an identical and independant copy of this term.
329     */
330    public Term copy() {
331        Term term = FACTORY.array(_size);
332        term._size = _size;
333        for (int i = 0; i < _size; i++) {
334            term._powers[i] = _powers[i];
335            term._variables[i] = _variables[i];
336        }
337        return term;
338    }
339
340    /**
341     * Compares this term with the one specified for order.
342     * 
343     * @param that the term to be compared to.
344     * @return a negative integer, zero, or a positive integer as this term
345     *         is less than, equal to, or greater than the specified term.
346     */
347    public int compareTo(Term that) {
348        int n = Math.min(this._size, that._size);
349        for (int i = 0; i < n; i++) {
350            int cmp = this._variables[i].getSymbol().compareTo(
351                    that._variables[i].getSymbol());
352            if (cmp != 0)
353                return cmp;
354            cmp = that._powers[i] - this._powers[i];
355            if (cmp != 0)
356                return cmp;
357        }
358        return that._size - this._size;
359    }
360
361    /**
362     * Evaluates this term by replacing its {@link Variable
363     * variables} by their current (context-local) values.
364     *
365     * @return the evaluation of this term or <code>null</code> if ONE.
366     * @throws FunctionException if any of this term's variable is not set.
367     */
368    @SuppressWarnings("unchecked")
369    Ring evaluate() {
370        Ring result = null;
371        for (int i = 0; i < _size; i++) {
372            Ring pow2 = (Ring) _variables[i].get();
373            if (pow2 == null)
374                throw new FunctionException("Variable: " + _variables[i]
375                        + " is not set");
376            int n = _powers[i];
377            while (n >= 1) { // Iteration.
378                if ((n & 1) == 1) {
379                    result = (result == null) ? pow2 : (Ring) result
380                            .times(pow2);
381                }
382                pow2 = (Ring) pow2.times(pow2);
383                n >>>= 1;
384            }
385        }
386        return result;
387    }
388
389    private static final long serialVersionUID = 1L;
390}