001/* A token that contains a Complex.
002
003 Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.data;
029
030import ptolemy.data.expr.ASTPtRootNode;
031import ptolemy.data.expr.ParseTreeEvaluator;
032import ptolemy.data.expr.PtParser;
033import ptolemy.data.type.BaseType;
034import ptolemy.data.type.Type;
035import ptolemy.data.type.TypeLattice;
036import ptolemy.data.unit.UnitUtilities;
037import ptolemy.graph.CPO;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.math.Complex;
040
041///////////////////////////////////////////////////////////////////
042//// ComplexToken
043
044/**
045 A token that contains a Complex number represented by a 64-bit
046 double-precision floating point real and imaginary parts.
047
048 @see ptolemy.data.Token
049 @see ptolemy.math.Complex
050 @author Yuhong Xiong, Neil Smyth, Christopher Hylands, Steve Neuendorffer
051 @version $Id$
052 @since Ptolemy II 0.2
053 @Pt.ProposedRating Green (neuendor)
054 @Pt.AcceptedRating Green (cxh)
055 */
056public class ComplexToken extends ScalarToken {
057    /** Construct a ComplexToken with Complex 0.0+0.0i.
058     */
059    public ComplexToken() {
060        _value = Complex.ZERO;
061    }
062
063    /** Construct a ComplexToken with the specified value.
064     *  @param value The specified value.
065     */
066    public ComplexToken(Complex value) {
067        _value = value;
068    }
069
070    /** Construct a ComplexToken from the specified string.
071     *  @param init The initialization string, of the format
072     *  <code><i>real</i>+<i>imaginary</i>i</code>, for example
073     *  <code>1.0+2.0i</code>.
074     *  @exception IllegalActionException If the string does not represent
075     *   a parsable complex number.
076     */
077    public ComplexToken(String init) throws IllegalActionException {
078        PtParser parser = new PtParser();
079        ASTPtRootNode tree = parser.generateParseTree(init);
080        Token token = new ParseTreeEvaluator().evaluateParseTree(tree);
081
082        if (token instanceof ComplexToken) {
083            _value = ((ComplexToken) token).complexValue();
084        } else {
085            throw new IllegalActionException("A ComplexToken cannot be"
086                    + " created from the expression '" + init + "'");
087        }
088    }
089
090    ///////////////////////////////////////////////////////////////////
091    ////                         public methods                    ////
092
093    /** Return the value of this token as a Complex.
094     *  @return The value of this token as a Complex
095     */
096    @Override
097    public Complex complexValue() {
098        // Complex is immutable, so we can just return the value.
099        return _value;
100    }
101
102    /** Convert the specified token into an instance of ComplexToken.
103     *  This method does lossless conversion.  The units of the
104     *  returned token will be the same as the units of the given
105     *  token.
106     *  If the argument is already an instance of ComplexToken,
107     *  it is returned without any change. Otherwise, if the argument
108     *  is below ComplexToken in the type hierarchy, it is converted to
109     *  an instance of ComplexToken or one of the subclasses of
110     *  ComplexToken and returned. If none of the above conditions are
111     *  met, an exception is thrown.
112     *  @param token The token to be converted to a ComplexToken.
113     *  @return A ComplexToken.
114     *  @exception IllegalActionException If the conversion
115     *   cannot be carried out.
116     */
117    public static ComplexToken convert(Token token)
118            throws IllegalActionException {
119        if (token instanceof ComplexToken) {
120            return (ComplexToken) token;
121        }
122        if (token.isNil()) {
123            return ComplexToken.NIL;
124        }
125
126        int compare = TypeLattice.compare(BaseType.COMPLEX, token);
127
128        if (compare == CPO.LOWER || compare == CPO.INCOMPARABLE) {
129            throw new IllegalActionException(
130                    notSupportedIncomparableConversionMessage(token,
131                            "complex"));
132        }
133
134        compare = TypeLattice.compare(BaseType.DOUBLE, token);
135
136        if (compare == CPO.SAME || compare == CPO.HIGHER) {
137            DoubleToken doubleToken = DoubleToken.convert(token);
138            ComplexToken result = new ComplexToken(doubleToken.complexValue());
139            if (doubleToken._unitCategoryExponents != null && !UnitUtilities
140                    .isUnitless(doubleToken._unitCategoryExponents)) {
141                result._unitCategoryExponents = doubleToken
142                        ._copyOfCategoryExponents();
143            }
144            return result;
145        }
146
147        // The argument is below ComplexToken in the type hierarchy,
148        // but I don't recognize it.
149        throw new IllegalActionException(
150                notSupportedConversionMessage(token, "complex"));
151    }
152
153    /** Return true if the argument's class is IntToken and it has the
154     *  same values as this token.
155     *  @param object An instance of Object.
156     *  @return True if the argument is a ComplexToken with the
157     *  same value.
158     */
159    @Override
160    public boolean equals(Object object) {
161        if (object == null) {
162            return false;
163        }
164        // This test rules out subclasses.
165        if (object.getClass() != getClass()) {
166            return false;
167        }
168
169        if (((ComplexToken) object).complexValue().equals(_value)) {
170            return true;
171        }
172
173        return false;
174    }
175
176    /** Return true if the token is nil, (aka null or missing).
177     *  Nil or missing tokens occur when a data source is sparsely populated.
178     *  @return True if the token is the {@link #NIL} token.
179     */
180    @Override
181    public boolean isNil() {
182        // We use a method here so that we can easily change how
183        // we determine if a token is nil without modify lots of classes.
184        // Can't use equals() here, or we'll go into an infinite loop.
185        return this == ComplexToken.NIL;
186    }
187
188    /** Return the type of this token.
189     *  @return BaseType.COMPLEX
190     */
191    @Override
192    public Type getType() {
193        return BaseType.COMPLEX;
194    }
195
196    /** Return a hash code value for this token. This method returns the
197     *  integer portion of the magnitude of the contained complex number.
198     *  @return A hash code value for this token.
199     */
200    @Override
201    public int hashCode() {
202        return (int) _value.magnitude();
203    }
204
205    /** Returns a new ComplexToken with value 1.0.
206     *  @return A new ComplexToken with value 1.0.
207     */
208    @Override
209    public Token one() {
210        return new ComplexToken(new Complex(1.0));
211    }
212
213    /** Return the value of this token as a string that can be parsed
214     *  by the expression language to recover a token with the same value.
215     *  @return A String formed using java.lang.Complex.toString().
216     */
217    @Override
218    public String toString() {
219        String unitString = "";
220
221        if (!_isUnitless()) {
222            unitString = " * " + unitsString();
223        }
224
225        if (isNil()) {
226            // FIXME: what about units?
227            return super.toString();
228        }
229
230        return _value.toString() + unitString;
231    }
232
233    /** Returns a new ComplexToken with value Complex.ZERO.
234     *  @return A new ComplexToken with value Complex.ZERO.
235     */
236    @Override
237    public Token zero() {
238        return new ComplexToken(Complex.ZERO);
239    }
240
241    ///////////////////////////////////////////////////////////////////
242    ////                         public variables                  ////
243
244    /** A token that represents a missing value.
245     *  Null or missing tokens are common in analytical systems
246     *  like R and SAS where they are used to handle sparsely populated data
247     *  sources.  In database parlance, missing tokens are sometimes called
248     *  null tokens.  Since null is a Java keyword, we use the term "nil".
249     *  The toString() method on a nil token returns the string "nil".
250     */
251    public static final ComplexToken NIL = new ComplexToken();
252
253    ///////////////////////////////////////////////////////////////////
254    ////                         protected methods                 ////
255
256    /** Return a ScalarToken containing the absolute value of the
257     *  value of this token. If this token contains a non-negative
258     *  number, it is returned directly; otherwise, a new token is is
259     *  return.  Note that it is explicitly allowable to return this
260     *  token, since the units are the same.
261     *  @return A DoubleToken.
262     */
263    @Override
264    protected ScalarToken _absolute() {
265        DoubleToken result = new DoubleToken(_value.magnitude());
266        return result;
267    }
268
269    /** Return a new token whose value is the value of the
270     *  argument Token added to the value of this Token.  It is assumed
271     *  that the type of the argument is an ComplexToken.
272     *  @param rightArgument The token to add to this token.
273     *  @return A new ComplexToken containing the result.
274     */
275    @Override
276    protected ScalarToken _add(ScalarToken rightArgument) {
277        Complex result = _value
278                .add(((ComplexToken) rightArgument).complexValue());
279        return new ComplexToken(result);
280    }
281
282    /** Throw an exception because bitwise AND is not supported.
283     *  @param rightArgument The ComplexToken to bitwise AND with this one.
284     *  @exception IllegalActionException Always thrown.
285     *  @return An exception.
286     */
287    @Override
288    protected ScalarToken _bitwiseAnd(ScalarToken rightArgument)
289            throws IllegalActionException {
290        throw new IllegalActionException(
291                notSupportedMessage("bitwiseAnd", this, rightArgument));
292    }
293
294    /** Throw an exception because bitwise NOT is not supported.
295     *  @exception IllegalActionException Always thrown.
296     *  @return An exception.
297     */
298    @Override
299    protected ScalarToken _bitwiseNot() throws IllegalActionException {
300        throw new IllegalActionException(
301                notSupportedMessage("bitwiseNot", this, this));
302    }
303
304    /** Throw an exception because bitwise OR is not supported.
305     *  @param rightArgument The ComplexToken to bitwise OR with this one.
306     *  @exception IllegalActionException Always thrown.
307     *  @return An exception.
308     */
309    @Override
310    protected ScalarToken _bitwiseOr(ScalarToken rightArgument)
311            throws IllegalActionException {
312        throw new IllegalActionException(
313                notSupportedMessage("bitwiseOr", this, rightArgument));
314    }
315
316    /** Throw an exception because bitwise XOR is not supported.
317     *  @param rightArgument The ComplexToken to bitwise XOR with this one.
318     *  @exception IllegalActionException Always thrown.
319     *  @return An exception.
320     */
321    @Override
322    protected ScalarToken _bitwiseXor(ScalarToken rightArgument)
323            throws IllegalActionException {
324        throw new IllegalActionException(
325                notSupportedMessage("bitwiseXor", this, rightArgument));
326    }
327
328    /** Return a new token whose value is the value of this token
329     *  divided by the value of the argument token. It is assumed that
330     *  the type of the argument is an ComplexToken
331     *  @param rightArgument The token to divide this token by.
332     *  @return A new ComplexToken containing the result.
333     *  @exception IllegalActionException Not thrown by this base class.
334     */
335    @Override
336    protected ScalarToken _divide(ScalarToken rightArgument)
337            throws IllegalActionException {
338        Complex result = _value
339                .divide(((ComplexToken) rightArgument).complexValue());
340        return new ComplexToken(result);
341    }
342
343    /** Test that the value of this rightArgument is close to the
344     *  first argument, where "close" means that the distance between
345     *  their values is less than or equal to the second argument. It
346     *  is assumed that the type of the argument is ComplexToken.
347     *
348     *  @param rightArgument The rightArgument to compare to this
349     *  rightArgument.
350     *  @param epsilon The value that we use to determine whether two
351     *  tokens are close.
352     *  @return A true-valued rightArgument if the first argument is
353     *  close in value to this rightArgument.
354     */
355    @Override
356    protected BooleanToken _isCloseTo(ScalarToken rightArgument,
357            double epsilon) {
358        return BooleanToken.getInstance(complexValue().isCloseTo(
359                ((ComplexToken) rightArgument).complexValue(), epsilon));
360    }
361
362    /** Throw an exception because complex values cannot be compared.
363     *  @param rightArgument The token to compare to this token.
364     *  @exception IllegalActionException Always thrown.
365     *  @return An exception.
366     */
367    @Override
368    protected BooleanToken _isLessThan(ScalarToken rightArgument)
369            throws IllegalActionException {
370        throw new IllegalActionException(
371                notSupportedMessage("isLessThan", this, rightArgument)
372                        + " because complex numbers cannot be compared.");
373    }
374
375    /** Throw an exception because the modulo operation does not
376     *  make sense for complex values.
377     *  @param rightArgument The token to modulo this token by.
378     *  @exception IllegalActionException Always thrown.
379     *  @return An exception.
380     */
381    @Override
382    protected ScalarToken _modulo(ScalarToken rightArgument)
383            throws IllegalActionException {
384        throw new IllegalActionException(
385                notSupportedMessage("modulo", this, rightArgument));
386    }
387
388    /** Return a new token whose value is the value of this token
389     *  multiplied by the value of the argument token.  It is assumed that
390     *  the type of the argument is an ComplexToken.
391     *  @param rightArgument The token to multiply this token by.
392     *  @return A new ComplexToken containing the result.
393     */
394    @Override
395    protected ScalarToken _multiply(ScalarToken rightArgument) {
396        Complex result = _value
397                .multiply(((ComplexToken) rightArgument).complexValue());
398        return new ComplexToken(result);
399    }
400
401    /** Return a new token whose value is the value of the argument token
402     *  subtracted from the value of this token.  It is assumed that
403     *  the type of the argument is an ComplexToken.
404     *  @param rightArgument The token to subtract from this token.
405     *  @return A new ComplexToken containing the result.
406     */
407    @Override
408    protected ScalarToken _subtract(ScalarToken rightArgument) {
409        Complex result = _value
410                .subtract(((ComplexToken) rightArgument).complexValue());
411        return new ComplexToken(result);
412    }
413
414    ///////////////////////////////////////////////////////////////////
415    ////                         private variables                 ////
416    private Complex _value = null;
417}