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}