001/* A sample implementation of the CQComparator that operates on instances
002 of Double.
003
004 Copyright (c) 1998-2014 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027
028 */
029package ptolemy.actor.util.test;
030
031import ptolemy.actor.util.CQComparator;
032import ptolemy.actor.util.CalendarQueue;
033
034///////////////////////////////////////////////////////////////////
035//// DoubleCQComparator
036
037/**
038 This class implements the CQComparator interface. It compares instances
039 of Double. Therefore, all arguments passed to its methods have to be of
040 type Double (or Double[] for the getBinWidth() method). If this is violated,
041 a ClassCastException will be thrown. This class is used to test the
042 CalendarQueue.
043
044 @author Lukito Muliadi
045 @version $Id$
046 @since Ptolemy II 0.2
047 @Pt.ProposedRating Green (eal)
048 @Pt.AcceptedRating Yellow (liuj)
049 @see CQComparator
050 @see CalendarQueue
051 */
052public class DoubleCQComparator implements CQComparator {
053    ///////////////////////////////////////////////////////////////////
054    ////                         public methods                    ////
055
056    /** Compare the two argument. Return a negative integer,
057     *  zero, or a positive integer as the first argument is less than,
058     *  equal to, or greater than the second.
059     *  Both arguments have to be instances of Double, otherwise a
060     *  ClassCastException will be thrown.
061     *  @param object1 The first Double.
062     *  @param object2 The second Double.
063     *  @return A negative integer, zero, or a positive integer if the first
064     *   argument is less than, equal to, or greater than the second.
065     *  @exception ClassCastException If either argument is not an instance
066     *   of Double.
067     */
068    @Override
069    public int compare(Object object1, Object object2) {
070        Double a = (Double) object1;
071        Double b = (Double) object2;
072
073        // FindBugs: http://findbugs.sourceforge.net/bugDescriptions.html#CO_COMPARETO_INCORRECT_FLOATING
074
075        // "This method compares double or float values using pattern
076        // like this: val1 > val2 ? 1 : val1 < val2 ? -1 : 0. This
077        // pattern works incorrectly for -0.0 and NaN values which may
078        // result in incorrect sorting result or broken collection (if
079        // compared values are used as keys). Consider using
080        // Double.compare or Float.compare static methods which handle
081        // all the special cases correctly."
082
083        return a.compareTo(b);
084
085        // if (a.doubleValue() < b.doubleValue()) {
086        //      return -1;
087        // } else if (a.doubleValue() > b.doubleValue()) {
088        //     return 1;
089        // } else {
090        //     return 0;
091        // }
092    }
093
094    /** Given an entry, a zero reference, and a bin width, return a
095     *  virtual bin number for the entry.  The virtual bin number is a
096     *  quantized double.  The calculation performed is:
097     *  <p>
098     *  <i>(entry - zeroReference) / binWidth</i>,
099     *  </p>
100     *  with the result cast to long.
101     *  If the arguments are not instances of Double, then a
102     *  ClassCastException will be thrown.
103     *  @param entry The entry.
104     *  @return The virtual bin number for the entry, according to the
105     *   zero reference and the bin width.
106     *  @exception ClassCastException If the arguments are not instances of
107     *   Double.
108     */
109    @Override
110    public long getVirtualBinNumber(Object entry) {
111        return (long) ((((Double) entry).doubleValue()
112                - _zeroReference.doubleValue()) / _binWidth.doubleValue());
113    }
114
115    /** Given an array of Double objects, find the appropriate bin
116     *  width. By 'appropriate', the bin width is chosen such that on average
117     *  the number of entry in all non-empty bins is equal to one.
118     *  If the argument is null, return the default bin width, which is 1.0
119     *  for this implementation.
120     *
121     *  @param entryArray An array of Double objects.
122     *  @exception ClassCastException If one of the array elements is not
123     *   an instance of Double.
124     */
125    @Override
126    public void setBinWidth(Object[] entryArray) {
127        if (entryArray == null) {
128            // Reset to default.
129            _binWidth = Double.valueOf(1.0);
130            return;
131        }
132
133        double[] diff = new double[entryArray.length - 1];
134
135        double average = 0;
136
137        for (int i = 1; i < entryArray.length; ++i) {
138            diff[i - 1] = ((Double) entryArray[i]).doubleValue()
139                    - ((Double) entryArray[i - 1]).doubleValue();
140            average = average + diff[i - 1];
141        }
142
143        average = average / diff.length;
144
145        double effAverage = 0;
146        int nEffSamples = 0;
147
148        for (int i = 1; i < entryArray.length; ++i) {
149            if (diff[i - 1] < 2 * average) {
150                nEffSamples++;
151                effAverage = effAverage + diff[i - 1];
152            }
153        }
154
155        // To avoid returning NaN or 0.0 for the width, if this is
156        // the result, leave the bin width unchanged.
157        if (effAverage == 0.0 || nEffSamples == 0) {
158            return;
159        }
160
161        effAverage = effAverage / nEffSamples;
162        _binWidth = Double.valueOf(3.0 * effAverage);
163    }
164
165    /** Set the zero reference, to be used in calculating the virtual
166     *  bin number.
167     *  @exception ClassCastException If the argument is not an instance
168     *   of Double.
169     */
170    @Override
171    public void setZeroReference(Object zeroReference) {
172        _zeroReference = (Double) zeroReference;
173    }
174
175    ///////////////////////////////////////////////////////////////////
176    ////                         private members                   ////
177    // The bin width.
178    private Double _binWidth = Double.valueOf(1.0);
179
180    // The zero reference.
181    private Double _zeroReference = Double.valueOf(0.0);
182}