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}