001/* A class that represents model time. 002 003 Copyright (c) 2004-2018 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.actor.util; 029 030import java.math.BigInteger; 031 032import ptolemy.actor.Director; 033import ptolemy.kernel.util.IllegalActionException; 034import ptolemy.kernel.util.InternalErrorException; 035import ptolemy.math.ExtendedMath; 036import ptolemy.util.DoubleUtilities; 037 038/////////////////////////////////////////////////////////////////// 039//// Time 040 041/** 042 * An object of the Time class represents time in a model. An instance of Time 043 * has a value that is immutable. It has no limit on the magnitude, and 044 * it does not lose resolution as the magnitude increases. There are 045 * two time constants: NEGATIVE_INFINITY and POSITIVE_INFINITY. 046 * <p> 047 * The time value is quantized to the time resolution specified by the 048 * <i>timeResolution</i> parameter of the associated director. The reason for 049 * this is that without quantization, it is extremely difficult to compare two 050 * time values with digit-to-digit accuracy because of the unpredictable 051 * numerical errors introduced during computation. In practice, two time values 052 * can only be distinguished if their difference can be detected by some 053 * measuring instrument, which always has a smallest unit for measurement. This 054 * smallest unit measurement gives the physical meaning of the time resolution 055 * used for quantization. There are three exceptions that do not really need a 056 * notion of time resolution, ZERO, POSITIVE_INFINITY, and NEGATIVE_INFINITY. 057 * These are provided as static fields, and normally these should be the only 058 * instances of Time constructed without a director. 059 * <p> 060 * This implementation of Time does not lose resolution as the magnitude of 061 * the time increases (unlike floating point numbers). This is because 062 * Time is represented internally as a multiple of the resolution, and 063 * the multiple is not constrained to any limited magnitude. 064 * <p> 065 * The time value can be retrieved in three ways, the {@link #toString()}method 066 * and the {@link #getDoubleValue()}method and the {@link #getLongValue()} 067 * method. The first method returns a string representation while the second 068 * method returns a double value. There are some limitations on both methods. 069 * For the toString() method, we cannot directly do numerical operations on 070 * strings. For the getDoubleValue() method, we can not guarantee that the 071 * returned double value preserves the time resolution because of the limited 072 * digits for double representation. We recommend to operate on time objects 073 * directly instead of the time values of time objects. getLongValue() returns 074 * an integer multiple of the director resolution. 075 * <p> 076 * Two operations, add and subtract, can be performed on a time object, where 077 * the argument can be a double or a time object. If the argument is not a time 078 * object, the argument is quantized before the operations are performed. These 079 * operations return a new time object with a quantized result. 080 * <p> 081 * The time value of a time object can be infinite. The add and subtract 082 * operations on infinite time values follow rules similar to the IEEE Standard 083 * 754 for Floating Point Numbers. In particular, adding two positive or negative 084 * infinities yield a positive or negative infinity; adding a positive infinity to 085 * a negative infinity, however, triggers an ArithmeticException; the negation 086 * of a positive or negative infinity is a negative or positive infinity, 087 * respectively. 088 * <p> 089 * This class implements the Comparable interface, where two time objects can be 090 * compared in the following way. If any of the two time objects contains an 091 * infinite time value, the rules are: a negative infinity is equal to a 092 * negative infinity and less than anything else; a positive infinity is equal 093 * to a positive infinity and bigger than anything else. If none of the time 094 * objects has an infinite time value, the time values of two time objects are 095 * compared. If the time values are the same, the two time objects are treated 096 * equal, or they represent the same model time. Otherwise, the time object 097 * containing a bigger time value is regarded to happen after the time object 098 * with a smaller time value. 099 * <p> 100 * All time objects share the same time resolution, which is provided by the 101 * top-level director. In some domains, such as CT and DE, users can change the 102 * time resolution by configuring the <i>timeResolution </i> parameter. The 103 * default value for this parameter "1E-10", which has value 10 <sup>-10 </sup>. 104 * To preserve the consistency of time values, timeResolution cannot be changed 105 * when a model is running (attempting to do so will trigger an exception). 106 * 107 * @author Haiyang Zheng, Edward A. Lee, Elaine Cheong 108 * @version $Id$ 109 * @since Ptolemy II 4.1 110 * @Pt.ProposedRating Yellow (hyzheng) 111 * @Pt.AcceptedRating Red (hyzheng) 112 */ 113public class Time implements Comparable { 114 /** Construct a Time object with zero as the time value. This object 115 * is associated with the given director, which provides the necessary 116 * information for quantization. 117 * @param director The director with which this time object is associated. 118 * This must not be null, or subsequent uses of the class will fail. 119 */ 120 public Time(Director director) { 121 _director = director; 122 _timeValue = BigInteger.ZERO; 123 } 124 125 /** Construct a Time object with the specified double value as its 126 * time value. The specified director provides the resolution that 127 * is used to quantize the double value so that the value of the 128 * resulting Time object is a multiple of the precision. 129 * @param director The director with which this time object is associated. 130 * @param timeValue A double value as the specified time value. 131 * @exception ArithmeticException If the argument is NaN. 132 * @exception IllegalActionException If the given double time value does 133 * not match the time resolution. 134 */ 135 public Time(Director director, double timeValue) 136 throws IllegalActionException { 137 _director = director; 138 139 if (Double.isNaN(timeValue)) { 140 throw new ArithmeticException("Time value can not be NaN."); 141 } 142 143 if (Double.isInfinite(timeValue)) { 144 _timeValue = null; 145 146 if (timeValue < 0) { 147 _isNegativeInfinite = true; 148 } else { 149 _isPositiveInfinite = true; 150 } 151 } else { 152 _timeValue = _doubleToMultiple(timeValue); 153 } 154 } 155 156 /** Construct a Time object with the specified long value as its time value. 157 * @param director The director with which this time object is associated. 158 * @param timeValue A long value as the specified time value, as a multiple 159 * of the resolution. 160 */ 161 public Time(Director director, long timeValue) { 162 _director = director; 163 _timeValue = BigInteger.valueOf(timeValue); 164 } 165 166 /////////////////////////////////////////////////////////////////// 167 //// private constructor //// 168 169 /** Construct a Time object with the specified BigInteger value as its 170 * time value (as a multiple of the precision). The time object 171 * is associated with the given director, 172 * which provides the necessary information for quantization. 173 * This constructor is private and can only be accessed by the methods 174 * defined inside this class. 175 * @param director The director with which this time object is associated. 176 * @param timeValue The multiple of the precision that is the time value. 177 */ 178 private Time(Director director, BigInteger timeValue) { 179 _director = director; 180 _timeValue = timeValue; 181 } 182 183 /** Construct a Time object with value that is one of _POSITIVE_INFINITY 184 * or _NEGATIVE_INFINITY. 185 * @param value One of _POSITIVE_INFINITY or _NEGATIVE_INFINITY. 186 */ 187 private Time(int value) { 188 if (value == _POSITIVE_INFINITY) { 189 _timeValue = null; 190 _isPositiveInfinite = true; 191 } else if (value == _NEGATIVE_INFINITY) { 192 _timeValue = null; 193 _isNegativeInfinite = true; 194 } else { 195 _timeValue = BigInteger.ZERO; 196 } 197 } 198 199 /////////////////////////////////////////////////////////////////// 200 //// static fields //// 201 202 // Indicator to create negative infinite value. 203 private static int _ZERO = 0; 204 205 // Indicator to create positve infinite value. 206 private static int _POSITIVE_INFINITY = 1; 207 208 // Indicator to create negative infinite value. 209 private static int _NEGATIVE_INFINITY = 2; 210 211 /////////////////////////////////////////////////////////////////// 212 //// public variables //// 213 // NOTE: For the following constants, the director argument is null 214 // because these constants are invariant to any time resolution. 215 216 /** A static and final time constant holding a negative infinity. */ 217 public static final Time NEGATIVE_INFINITY = new Time(_NEGATIVE_INFINITY); 218 219 /** A static and final time constant holding a positive infinity. */ 220 public static final Time POSITIVE_INFINITY = new Time(_POSITIVE_INFINITY); 221 222 /** A static and final time constant holding a zero. */ 223 public static final Time ZERO = new Time(_ZERO); 224 225 /////////////////////////////////////////////////////////////////// 226 //// public methods //// 227 228 /** Return a new time object whose time value is increased by the 229 * given double value. The specified double value is quantized 230 * to a multiple of the precision before it is added. 231 * @param timeValue The amount of the time increment. 232 * @return A new time object with the incremented time value. 233 * @exception ArithmeticException If the result is not a valid 234 * number (the argument is NaN or the sum would be), or the given time 235 * value does not match the time resolution. 236 */ 237 public Time add(double timeValue) { 238 // NOTE: a double time value can be either positive infinite, 239 // negative infinite, or a NaN. 240 if (Double.isNaN(timeValue)) { 241 throw new ArithmeticException("Time: Time value can not be NaN."); 242 } 243 244 if (Double.isInfinite(timeValue)) { 245 if (timeValue < 0) { 246 // time value is a negative infinity 247 if (_isPositiveInfinite) { 248 throw new ArithmeticException( 249 "Time: Adding a positive infinity to a negative " 250 + "infinity results in an invalid time."); 251 } else { 252 return NEGATIVE_INFINITY; 253 } 254 } else { 255 // time value is a positive infinity 256 if (_isNegativeInfinite) { 257 throw new ArithmeticException( 258 "Time: Adding a negative infinity to a positive " 259 + "infinity results in an invalid time."); 260 } else { 261 return POSITIVE_INFINITY; 262 } 263 } 264 } else if (isInfinite()) { 265 return this; 266 } else { 267 BigInteger quantizedValue; 268 269 try { 270 quantizedValue = _doubleToMultiple(timeValue); 271 } catch (IllegalActionException e) { 272 throw new ArithmeticException("Cannot guarantee the specified " 273 + "time resolution " + _timeResolution() 274 + " for the time value " + timeValue + ".\nTry " 275 + "choosing a greater time resolution " 276 + "by configuring the timeResolution parameter of the " 277 + "director.\n" 278 + "Check the stack trace to see which actor or " 279 + "parameter caused this exception."); 280 } 281 282 return new Time(_director, _timeValue.add(quantizedValue)); 283 } 284 } 285 286 /** Return a new time object whose time value is the sum of that of 287 * this time object and of the specified time object. The two time 288 * objects are expected to have directors with the same time resolution. 289 * If they do not, then the returned result is a new Time object 290 * representing the sum of the double values of the two Time objects. 291 * This would not be as accurate. 292 * @param time The time object contains the amount of time increment. 293 * @return A new time object with the quantized and incremented time value. 294 * @exception ArithmeticException If the result is not a valid number 295 * (it is the sum of positive and negative infinity). 296 */ 297 public Time add(Time time) { 298 // Note: a time value of a time object can be either positive infinite 299 // or negative infinite. 300 if (time._isNegativeInfinite) { 301 // the time object has a negative infinity time value 302 if (_isPositiveInfinite) { 303 throw new ArithmeticException( 304 "Time: Adding a positive infinity to a negative " 305 + "infinity yields an invalid time."); 306 } else { 307 return NEGATIVE_INFINITY; 308 } 309 } else if (time._isPositiveInfinite) { 310 // the time object has a positive infinity time value 311 if (_isNegativeInfinite) { 312 throw new ArithmeticException( 313 "Adding a negative infinity to a positive " 314 + "infinity yields an invalid time."); 315 } else { 316 return POSITIVE_INFINITY; 317 } 318 } else if (isInfinite()) { 319 return this; 320 } 321 322 // If I don't reference a director, then use the other guy's director. 323 Director director = _director; 324 if (director == null) { 325 director = time._director; 326 } 327 // Ensure the resolutions are the same. 328 try { 329 double resolution = _timeResolution(); 330 331 if (resolution != time._timeResolution()) { 332 double thisValue = getDoubleValue(); 333 double thatValue = time.getDoubleValue(); 334 return new Time(director, thisValue + thatValue); 335 } 336 } catch (IllegalActionException e) { 337 // If the time resolution values are malformed this 338 // should have been caught before this. 339 throw new InternalErrorException(e); 340 } 341 342 return new Time(director, _timeValue.add(time._timeValue)); 343 } 344 345 /** Add the specified double to this time without checking whether the 346 * specified double is too large to ensure the time resolution of the 347 * director. 348 * <p> 349 * That is, two calls to this method could yield the same result even if 350 * the two arguments differ by more than the time resolution. 351 * The {@link #add(double)} method, in contrast, will throw an exception 352 * if two such calls could yield the same result. 353 * <p> 354 * This method should only be used if the time resolution is not 355 * really needed for this addition. 356 * For example, when finding the stop time of long simulation, where 357 * `timeValue` is a large number, it would be reasonable to use this 358 * method if the stop time does not need to be so precise. 359 * 360 * @param timeValue The value to add. 361 * @return The sum of this Time object and the value to add. 362 */ 363 public Time addUnchecked(final double timeValue) { 364 365 // NOTE: a double time value can be either positive infinite, 366 // negative infinite, or a NaN. 367 if (Double.isNaN(timeValue)) { 368 throw new ArithmeticException("Time: Time value cannot be NaN."); 369 } 370 371 if (Double.isInfinite(timeValue)) { 372 if (timeValue < 0) { 373 // time value is a negative infinity 374 if (_isPositiveInfinite) { 375 throw new ArithmeticException( 376 "Time: Adding a positive infinity to a negative " 377 + "infinity results in an invalid time."); 378 } else { 379 return NEGATIVE_INFINITY; 380 } 381 } else { 382 // time value is a positive infinity 383 if (_isNegativeInfinite) { 384 throw new ArithmeticException( 385 "Time: Adding a negative infinity to a positive " 386 + "infinity results in an invalid time."); 387 } else { 388 return POSITIVE_INFINITY; 389 } 390 } 391 } 392 393 if (isInfinite()) { 394 return this; 395 } 396 397 // Note the code above should substantively match that in 398 // method #add(double). 399 // The code below should substatively match that in #_doubleToMultiple(), 400 // except that it does not throw an exception based on the 401 // `_timeResolution()`. 402 403 final double precision = _timeResolution(); 404 final long multiple = Math.round(timeValue / precision); 405 final BigInteger quantizedValue = BigInteger.valueOf(multiple); 406 return new Time(_director, _timeValue.add(quantizedValue)); 407 } 408 409 /** Return -1, 0, or 1 if this time object is less than, equal to, or 410 * greater than the given argument. Note that a ClassCastException 411 * will be thrown if the argument is not an instance of Time. 412 * This object expects the directors associated with this and the 413 * specified Time objects to have the same time resolution. If this 414 * is not the case, then it compares the double representations of 415 * those time values, which is not as accurate. 416 * @param time A time object to compare to. 417 * @return The integer -1, 0, or 1 if this is less than, equal to, or 418 * greater than the argument. 419 */ 420 @Override 421 public int compareTo(Object time) { 422 // NOTE: a time object may contain infinite time values. 423 Time castTime = (Time) time; 424 425 // If at least one of the time objects has an infinite time value, 426 if (castTime.isInfinite() || isInfinite()) { 427 if (castTime._isNegativeInfinite) { 428 // the castTime object is a negative infinity. 429 if (_isNegativeInfinite) { 430 return 0; 431 } else { 432 return 1; 433 } 434 } else if (castTime._isPositiveInfinite) { 435 // the castTime object is a positive infinity. 436 if (_isPositiveInfinite) { 437 return 0; 438 } else { 439 return -1; 440 } 441 } else { 442 // the castTime object is not infinite, this object must 443 // be infinite. 444 if (_isNegativeInfinite) { 445 return -1; 446 } else { 447 return 1; 448 } 449 } 450 } 451 452 double resolution = _timeResolution(); 453 454 if (resolution == castTime._timeResolution()) { 455 return _timeValue.compareTo(castTime._timeValue); 456 } else { 457 double thisValue = getDoubleValue(); 458 double thatValue = castTime.getDoubleValue(); 459 460 if (thisValue < thatValue) { 461 return -1; 462 } else if (thisValue > thatValue) { 463 return 1; 464 } else { 465 return 0; 466 } 467 } 468 } 469 470 /** Return true if this time object has the same time value as 471 * that of the given time object. 472 * @param time The time object that this time object is compared to. 473 * @return True if the two time objects have the same time value. 474 */ 475 @Override 476 public boolean equals(Object time) { 477 if (time instanceof Time) { 478 return this.compareTo(time) == 0; 479 } 480 return false; 481 } 482 483 /** Return the double representation of the time value of this 484 * time object. Note that the returned result is not necessarily 485 * as accurate as the internal representation. In particular, if 486 * the internal representation is too large, then then the 487 * returned result may be infinite. In addition, if the 488 * magnitude of the returned number is large relative to the time 489 * resolution of the associated director, then the result may be 490 * inaccurate by more than the time resolution. 491 * @return The double representation of the time value. 492 */ 493 public double getDoubleValue() { 494 if (_isPositiveInfinite) { 495 return Double.POSITIVE_INFINITY; 496 } else if (_isNegativeInfinite) { 497 return Double.NEGATIVE_INFINITY; 498 } else { 499 // NOTE: Using doubleValue() here hugely increases the 500 // execution time... Could instead use longValue(), but the 501 // result would not necessarily be accurate. 502 //return _timeValue.doubleValue() * _timeResolution(); 503 return DoubleUtilities.bigToDouble(_timeValue) * _timeResolution(); 504 } 505 } 506 507 /** Return the long representation of the time value of this time 508 * object. The long representation is a multiple of the 509 * resolution of the associated director. Note that a Time value 510 * of positive infinity will return Long.MAX_VALUE and a Time 511 * value of negative infinity will return Long.MIN_VALUE. 512 * @return The long representation of the time value. 513 */ 514 public long getLongValue() { 515 if (_isPositiveInfinite) { 516 return Long.MAX_VALUE; 517 } else if (_isNegativeInfinite) { 518 return Long.MIN_VALUE; 519 } else { 520 return _timeValue.longValue(); 521 } 522 } 523 524 /** Return the hash code for the time object. If two time objects contains 525 * the same quantized time value, they have the same hash code. 526 * Note that the quantization is performed on the time value before 527 * calculating the hash code. 528 * @return The hash code for the time object. 529 */ 530 @Override 531 public int hashCode() { 532 if (_isNegativeInfinite) { 533 return Integer.MIN_VALUE; 534 } else if (_isPositiveInfinite) { 535 return Integer.MAX_VALUE; 536 } else { 537 return _timeValue.hashCode(); 538 } 539 } 540 541 // FIXME: profiling shows that the following six methods are called 542 // enormous times and performance can be greatly improved if no infinities 543 // are supported. 544 545 /** Return true if the current time value is infinite. 546 * @return true if the current time value is infinite. 547 */ 548 public final boolean isInfinite() { 549 return _isNegativeInfinite || _isPositiveInfinite; 550 } 551 552 /** Return true if the current time value is a negative value 553 * (including negative infinity). 554 * @return true if the current time value is a negative value 555 * (including negative infinity). 556 */ 557 public final boolean isNegative() { 558 if (_timeValue != null) { 559 return _timeValue.signum() == -1; 560 } 561 return _isNegativeInfinite; 562 } 563 564 /** Return true if the current time value is a negative infinity. 565 * @return true if the current time value is a negative infinity. 566 */ 567 public final boolean isNegativeInfinite() { 568 return _isNegativeInfinite; 569 } 570 571 /** Return true if the current time value is a positive value 572 * (including positive infinity). 573 * @return true if the current time value is a positive value 574 * (including positive infinity). 575 */ 576 public final boolean isPositive() { 577 if (_timeValue != null) { 578 return _timeValue.signum() == 1; 579 } 580 return _isPositiveInfinite; 581 } 582 583 /** Return true if the current time value is a positive infinity. 584 * @return true if the current time value is a positive infinity. 585 */ 586 public final boolean isPositiveInfinite() { 587 return _isPositiveInfinite; 588 } 589 590 /** Return true if the current time value is zero. 591 * @return true if the current time value is a zero. 592 */ 593 public final boolean isZero() { 594 if (_timeValue != null) { 595 return _timeValue.signum() == 0; 596 } 597 return false; 598 } 599 600 /** Return the maximum value of time whose representation as a double 601 * is always accurate to the specified time resolution. In other words, 602 * if you ask this instance of Time for its value as a double, if the 603 * returned double is larger than the number returned by this method, 604 * then the double representation is not necessarily accurate to the 605 * specified resolution. 606 * @return The maximum value of time above which the double representation 607 * may not be accurate to the specified resolution. 608 */ 609 public double maximumAccurateValueAsDouble() { 610 // NOTE: when the time value is too big a multiple of the 611 // resolution, the double representation 612 // fails to deliver adequate precision. 613 // Here is an example: if time resolution is 1E-12, 614 // any double that is bigger than 8191.999999999999 cannot 615 // distinguish itself from other bigger values (even 616 // slightly bigger with the difference as small as the time 617 // resolution). Therefore, 8191.999999999999 is the LUB of the 618 // set of double values have the specified time resolution. 619 // NOTE: The strategy to find the LUB for a given time 620 // resolution r: find the smallest N such that time resolution 621 // r >= 2^(-1*N); get M = 52 - N, which is the multiplication 622 // we can apply on the significand without loss of time 623 // resolution; the LUB is (1 + 1 - 1.0/2^(-52)) * 2^M. 624 // For example: with the above example time resolution 1e-12, 625 // we get N = 40, M = 12. Then we get the LUB as 626 // 8191.999999999999. For time resolution as 1e-10, the lub is 627 // 524287.99999999994. 628 // NOTE: according to the IEEE754 floating point standard, 629 // the formula to calculate a decimal value from a binary 630 // representation is 631 // (-1)^(sign)x(1+significand)x2^(exponent-127) for 632 // signal precision and 633 // (-1)^(sign)x(1+significand)x2^(exponent-1023) 634 // for double presision. 635 int minimumNumberOfBits = (int) Math 636 .floor(-1 * ExtendedMath.log2(_timeResolution())) + 1; 637 int maximumGain = 52 - minimumNumberOfBits; 638 639 return ExtendedMath.DOUBLE_PRECISION_SIGNIFICAND_ONLY 640 * Math.pow(2.0, maximumGain); 641 } 642 643 /** Return a new Time object whose value equals the argument, 644 * which is interpreted in milliseconds. 645 * @param director The director with which this time object is associated. 646 * @param milliseconds The time in ms. 647 * @return 648 */ 649 public static Time milliseconds(Director director, long milliseconds) { 650 // Handle the default case efficiently and exactly. 651 double resolution = director.getTimeResolution(); 652 if (resolution == 10E-10) { 653 return new Time(director, BigInteger.valueOf(milliseconds) 654 .multiply(BigInteger.valueOf(10000000))); 655 } 656 // Resolution in ms. 657 double resolutionMs = resolution * 1000; 658 long multiple = Math.round(milliseconds / resolutionMs); 659 return new Time(director, multiple); 660 } 661 662 /** Return a new Time object whose time value is decreased by the 663 * given double value. Quantization is performed on both the 664 * timeValue argument and the result. 665 * @param timeValue The amount of time decrement. 666 * @return A new time object with time value decremented. 667 */ 668 public Time subtract(double timeValue) { 669 return add(-1 * timeValue); 670 } 671 672 /** Return a new time object whose time value is decreased by the 673 * time value of the specified time object. This method assumes that the two 674 * time values have directors with the same time resolution. If 675 * this is not the case, then the result is a new Time object whose 676 * value is constructed from the difference between the double values 677 * for this and the specified Time objects, using the time resolution 678 * of the director of this one. 679 * @param time The time object contains the amount of time decrement. 680 * @return A new time object with time value decremented. 681 */ 682 public Time subtract(Time time) { 683 if (time.isNegativeInfinite()) { 684 return add(POSITIVE_INFINITY); 685 } else if (time.isPositiveInfinite()) { 686 return add(NEGATIVE_INFINITY); 687 } else { 688 return add(new Time(time._director, time._timeValue.negate())); 689 } 690 } 691 692 /** Subtract the specified time from this time and return the result as 693 * a double. 694 * <p> 695 * This is equivalent to calling {@link #subtract(Time)} and then invoking 696 * getDoubleValue(), but it is more efficient, in that it avoids 697 * unnecessary construction of Time objects. 698 * @param time The time to subtract. 699 * @return This Time minus the specified time, as a double. 700 */ 701 public double subtractToDouble(final Time time) { 702 703 // Note: a time value of a Time object can be either positive infinite 704 // or negative infinite. 705 if (time._isNegativeInfinite) { 706 if (_isNegativeInfinite) { 707 throw new ArithmeticException( 708 "Subtracting negative infinity from negative infinity yields an invalid time."); 709 } 710 return (Double.POSITIVE_INFINITY); 711 } 712 713 if (time._isPositiveInfinite) { 714 if (_isPositiveInfinite) { 715 throw new ArithmeticException( 716 "Subtracting positive infinity from positive infinity yields an invalid time."); 717 } 718 return (Double.NEGATIVE_INFINITY); 719 } 720 721 if (_isPositiveInfinite) { 722 return (Double.POSITIVE_INFINITY); 723 } 724 725 if (_isNegativeInfinite) { 726 return (Double.NEGATIVE_INFINITY); 727 } 728 729 // Handle case of different resolutions. 730 final double resolution = _timeResolution(); 731 if (resolution != time._timeResolution()) { 732 final double thisValue = getDoubleValue(); 733 final double thatValue = time.getDoubleValue(); 734 return (thisValue - thatValue); 735 } 736 737 final BigInteger difference = _timeValue.subtract(time._timeValue); 738 return (DoubleUtilities.bigToDouble(difference) * resolution); 739 } 740 741 /** Return the string representation of this time object. 742 * This is actually an approximation generated by first converting to a double. 743 * Note that the string representation of infinities can not be 744 * used to construct the time objects containing infinite time values. 745 * @return A String representation of this time object. 746 */ 747 @Override 748 public String toString() { 749 if (_isPositiveInfinite) { 750 return "Infinity"; 751 } else if (_isNegativeInfinite) { 752 return "-Infinity"; 753 } else { 754 return "" + getDoubleValue(); 755 756 // NOTE: Could use BigDecimal to get full resolution, as follows, 757 // but the resulution is absurd. 758 759 /* 760 BigDecimal resolution = new BigDecimal(_director.getTimeResolution()); 761 BigDecimal scale = new BigDecimal(_timeValue); 762 return resolution.multiply(scale).toString(); 763 */ 764 } 765 } 766 767 /////////////////////////////////////////////////////////////////// 768 //// protected methods //// 769 770 /** Return the time resolution for this Time object, which in this 771 * class is the time resolution of the director given in the 772 * constructor. If no director was given, then return a default 773 * time resolution of 10E-10. 774 * This is a protected method to allow subclasses to use their 775 * own time resolution. 776 * @return The time resolution of the director. 777 */ 778 protected double _timeResolution() { 779 if (_director != null) { 780 return _director.getTimeResolution(); 781 } 782 return 10E-10; 783 } 784 785 /////////////////////////////////////////////////////////////////// 786 //// private methods //// 787 788 /** Given a double, return the BigInteger that represents its 789 * quantized value. The BigInteger is the rounded result of dividing 790 * the double by the time resolution. 791 * @param value The value as a double. 792 * @return A BigInteger that specifies this double value as a multiple 793 * of the resolution given by the associated director. 794 * @exception IllegalActionException If the given double time value does 795 * not match the time resolution. 796 */ 797 private BigInteger _doubleToMultiple(double value) 798 throws IllegalActionException { 799 // NOTE: when the value is too big a multiple of the resolution, 800 // the division fails to deliver adequate precision. If this happens, 801 // an illegal action exception will be thrown indicating the double 802 // value does not match the specified time resolution. 803 // Here is an example: if time resolution is 1E-12, 804 // any double that is bigger than 8191.999999999999 cannot distinguish 805 // itself from any other bigger values (even slightly bigger with the 806 // difference as small as the time resolution). Therefore, 807 // 8191.999999999999 is the LUB of the set of double values have the 808 // specified time resolution. 809 // NOTE: The strategy to find the LUB for a given time resolution r: 810 // find the smallest N such that time resolution r >= 2^(-1*N); 811 // get M = 52 - N, which is the multiplication we can apply on the 812 // significand without loss of time resolution; 813 // the LUB is (1 + 1 - 1.0/2^(-52)) * 2^M. 814 // For example: with the above example time resolution 1e-12, we get 815 // N = 40, M = 12. Then we get the LUB as 8191.999999999999. 816 // NOTE: according to the IEEE754 floating point standard, 817 // the formula to calculate a decimal value from a binary 818 // representation is (-1)^(sign)x(1+significand)x2^(exponent-127) for 819 // signal precision and (-1)^(sign)x(1+significand)x2^(exponent-1023) 820 // for double precision. 821 double precision = _timeResolution(); 822 long multiple = Math.round(value / precision); 823 824 if (Math.abs(multiple * precision - value) > precision) { 825 throw new IllegalActionException("The given time value " + value 826 + " is too large to be converted precisely to an " 827 + "instance of Time with the specified time resolution of " 828 + precision 829 + ". The maximum value that can always be precisely converted is " 830 + maximumAccurateValueAsDouble() 831 + ". A number close to your value that can be converted is " 832 + multiple * precision); 833 } 834 835 return BigInteger.valueOf(multiple); 836 } 837 838 /////////////////////////////////////////////////////////////////// 839 //// private variables //// 840 841 /** The director that this time object is associated with. */ 842 private Director _director; 843 844 /** A boolean variable is true if the time value is a positive infinity. 845 * By default, it is false. 846 */ 847 private boolean _isPositiveInfinite = false; 848 849 /** A boolean variable is true if the time value is a negative infinity. 850 * By default, it is false. 851 */ 852 private boolean _isNegativeInfinite = false; 853 854 /** The time value, as a multiple of the resolution. */ 855 private BigInteger _timeValue = null; 856}