001/* The numeric rounding strategy classes.
002
003 Copyright (c) 2002-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.math;
029
030import java.math.BigDecimal;
031import java.math.BigInteger;
032import java.util.HashMap;
033import java.util.Iterator;
034import java.util.Map;
035
036///////////////////////////////////////////////////////////////////
037//// Rounding
038
039/**
040 The Rounding class provides a type safe enumeration of strategies for
041 handling loss of numeric resolution when rounding. Rounding is typically
042 resolved when quantization constraints are applied to a computed result
043 in order to satisfy the requirements of the type to which the result is to be
044 assigned.
045 <p>
046 Rounding is an abstract class for all rounding strategies.
047 The primary method of this class is {@link #round(BigDecimal) round}. This
048 method will round a BigDecimal value to the appropriate integer
049 and return a BigInteger object.</p>
050 <p>
051 {@link BigDecimal} objects are rounded by calling the
052 {@link BigDecimal#setScale(int)} method with the appropriate
053 rounding mode. Several static methods are provided in this class
054 to support this functionality for each of the supported rounding
055 modes. In addition, several static singleton rounding strategies
056 are provided by this class to implement one of the supported
057 routing modes.  Each of these rounding strategies are modeled
058 after the rounding strategies provided by {@link BigDecimal}
059 and include:</p>
060
061 <ul>
062
063 <li> <i><b>up</b></i> <br>
064 Rounding mode to round away from zero.
065 Always increments the digit prior to a non-zero discarded fraction.
066 Note that this rounding mode never decreases the magnitude of
067 the calculated value.
068 This rounding mode is supported by the static
069 {@link #roundUp(BigDecimal) roundUp} method and the Rounding
070 singletons {@link #GENERAL}, {@link #UNKNOWN} and {@link #UP}.</li>
071
072 <li> <i><b>down</b></i> <br>
073 Rounding mode to round towards zero.
074 Never increments the digit prior to a discarded fraction
075 (i.e., truncates). Note that this rounding mode never increases
076 the magnitude of the calculated value.
077 This rounding mode is supported by the static
078 {@link #roundDown(BigDecimal) roundDown} method and the Rounding
079 singleton {@link #DOWN}.</li>
080
081 <li> <i><b>floor</b></i> <br>
082 Rounding mode to round towards negative infinity.
083 If decimal is positive, behave as <b>round down</b>;
084 if decimal is negative, behave as <b>round up</b>.
085 This rounding mode is supported by the static
086 {@link #roundFloor(BigDecimal) roundFloor} method and the Rounding
087 singleton {@link #FLOOR}.</li>
088
089 <li> <i><b>ceiling</b></i> <br>
090 Rounding mode to round towards positive infinity.
091 If decimal is positive, behave as <b>round up</b>;
092 if decimal is negative, behave as <b>round down</b>.
093 This rounding mode is supported by the static
094 {@link #roundCeiling(BigDecimal) roundCeiling} method and the Rounding
095 singleton {@link #CEILING}.</li>
096
097 <li> <i><b>half up</b></i> <br>
098 Rounding mode to round towards "nearest neighbor" unless
099 both neighbors are equidistant, in which case round up.
100 Behaves as for <b>round up</b> if the discarded fraction is &ge; .5;
101 otherwise, behaves as for <b>round down</b>. Note that this is the
102 rounding mode that most of us were taught in grade school.
103 Rounding mode to round towards zero.
104 This rounding mode is supported by the static
105 {@link #roundHalfUp(BigDecimal) roundHalfUp} method and the Rounding
106 singleton {@link #HALF_UP}.</li>
107
108 <li> <i><b>half down</b></i> <br>
109 Rounding mode to round towards "nearest neighbor" unless
110 both neighbors are equidistant, in which case round down.
111 Behaves as for <b>round up</b> if the discarded fraction is &gt; .5;
112 otherwise, behaves as for <b>ROUND_DOWN</b>.
113 This rounding mode is supported by the static
114 {@link #roundHalfDown(BigDecimal) roundHalfDown} method and the Rounding
115 singleton {@link #HALF_DOWN}.</li>
116
117 <li> <i><b>half even</b></i> <br>
118 Rounding mode to round towards the "nearest neighbor" unless
119 both neighbors are equidistant, in which case, round towards
120 the even neighbor. Behaves as for <b>round half up</b>
121 if the digit to the left of the discarded fraction is odd;
122 behaves as for <b>round half down</b> if it's even.
123 Note that this is the rounding
124 mode that minimizes cumulative error when applied repeatedly
125 over a sequence of calculations.
126 This rounding mode is supported by the static
127 {@link #roundHalfEven(BigDecimal) roundHalfEven} method and the Rounding
128 singletons {@link #HALF_EVEN} and {@link #CONVERGENT}.</li>
129
130 <li> <i><b>half floor</b></i> <br>
131 Rounding mode to round towards "nearest neighbor" unless
132 both neighbors are equidistant, in which case round
133 "ceiling". Behaves as <b>round half down</b>
134 if the decimal is positive and as <b>round half up</b>
135 if the decimal is negative. Note that there is no half floor rounding
136 mode supported for BigDecimal values.
137 This rounding mode is supported by the static
138 {@link #roundHalfFloor(BigDecimal) roundHalfFloor} method and
139 the Rounding singleton {@link #HALF_FLOOR}.</li>
140
141 <li> <i><b>half ceiling</b></i> <br>
142 Rounding mode to round towards "nearest neighbor" unless
143 both neighbors are equidistant, in which case round
144 "ceiling". Behaves as <b>round half up</b> if the decimal
145 is positive and as <b>round half down</b>
146 if the decimal is negative.
147 Note that there is no half ceiling rounding mode
148 supported for BigDecimal values.
149 This rounding mode is supported by the static
150 {@link #roundHalfFloor(BigDecimal) roundHalfCeiling} method and
151 the Rounding singleton {@link #HALF_CEILING}.</li>
152
153 </ul>
154
155
156 <p>A specific strategy may be chosen dynamically by invoking forName() or
157 getName() with one of the above strategy names. Alternatively a strategy
158 may be selected by using one of the static singletons.</p>
159 <p>
160 The <i>truncate</i> and <i>nearest</i> strategies should be
161 preferred since they
162 correspond to capabilities available on many processors. Other
163 rounding strategies may require costly code on practical hardware.</p>
164 <p>
165 The active class functionality is provided by the quantize method which is
166 normally invoked from Quantization.quantize.</p>
167
168 @author Ed Willink, Contributor: Mike Wirthlin
169 @version $Id$
170 @since Ptolemy II 2.1
171 @Pt.ProposedRating Red (Ed.Willink)
172 @Pt.AcceptedRating Red
173 */
174public abstract class Rounding implements Cloneable {
175
176    ///////////////////////////////////////////////////////////////////
177    ////                         public methods                    ////
178
179    /** Return this, that is, return the reference to this object.
180     *  @return This Rounding.
181     */
182    @Override
183    public Object clone() {
184        return this;
185    }
186
187    /** Determine if the argument represents the same Rounding as this
188     *  object.
189     *  @param object Another object.
190     *  @return True if the argument represents the same Rounding as
191     *   this object; false otherwise.
192     */
193    @Override
194    public boolean equals(Object object) {
195        // since Rounding is a type safe enumeration, can use == to
196        // test equality.
197        return this == object;
198    }
199
200    /** Return an instance of this class with the specified name.
201     *  @param name Rounding mode name.
202     *  @return An instance of Rounding or null if the given name
203     *  does not match a valid rounding mode.
204     */
205    public static Rounding forName(String name) {
206        return (Rounding) _nameToRounding.get(name);
207    }
208
209    /** Return an instance of this class with the specified name,
210     *  or null if none exists.
211     *
212     *  @param name The name of the Rounding strategy to find.
213     *  @return An instance of Rounding.
214     *  @exception IllegalArgumentException If the string does not
215     *   match one of the known strategies.
216     */
217    public static Rounding getName(String name)
218            throws IllegalArgumentException {
219        Rounding rounding = (Rounding) _nameToRounding.get(name);
220
221        if (rounding != null) {
222            return rounding;
223        }
224
225        throw new IllegalArgumentException(
226                "Unknown rounding strategy \"" + name + "\".");
227    }
228
229    /** Return a hash code value for this object.    */
230    @Override
231    public int hashCode() {
232        return _name.hashCode();
233    }
234
235    /**
236     * Return an iterator for the names of all overflow types.
237     * @return An iterator for the names of all overflow types.
238     */
239    public static Iterator nameIterator() {
240        return _nameToRounding.keySet().iterator();
241    }
242
243    /**
244     * Round the BigDecimal value using the appropriate rounding
245     * strategy. The result is a BigInteger value rounded
246     * appropriately. Each class that extends Rounding will provide
247     * a mode specific round function.
248     *
249     * @param decimal value to be rounded
250     * @return The rounded BigInteger.
251     */
252    public abstract BigInteger round(BigDecimal decimal);
253
254    /** Rounding mode to round towards positive infinity.
255     * If decimal is positive, behave as {@link #roundUp};
256     * if decimal is negative, behave as {@link #roundDown}.
257     *
258     * @see BigDecimal#ROUND_CEILING
259     *
260     * @param decimal The BigDecimal value to round.
261     * @return Rounded BigDecimal value
262     * **/
263    public static BigDecimal roundCeiling(BigDecimal decimal) {
264        return decimal.setScale(0, BigDecimal.ROUND_CEILING);
265    }
266
267    /** Rounding mode to round towards zero.
268     * Never increments the digit prior to a discarded fraction
269     * (i.e., truncates). Note that this rounding mode never increases
270     * the magnitude of the calculated value.
271     *
272     * @see BigDecimal#ROUND_DOWN
273     *
274     * @param decimal The BigDecimal value to round.
275     * @return Rounded BigDecimal value
276     * **/
277    public static BigDecimal roundDown(BigDecimal decimal) {
278        return decimal.setScale(0, BigDecimal.ROUND_DOWN);
279    }
280
281    /** Rounding mode to round towards negative infinity.
282     * If decimal is positive, behave as {@link #roundDown};
283     * if decimal is negative, behave as {@link #roundUp}.
284     *
285     * @see BigDecimal#ROUND_FLOOR
286     *
287     * @param decimal The BigDecimal value to round.
288     * @return Rounded BigDecimal value
289     * **/
290    public static BigDecimal roundFloor(BigDecimal decimal) {
291        return decimal.setScale(0, BigDecimal.ROUND_FLOOR);
292    }
293
294    /** Rounding mode to round towards "nearest neighbor" unless
295     * both neighbors are equidistant, in which case round
296     * "ceiling".
297     * Behaves as HALF_ROUND_UP if the decimal is positive and
298     * as HALF_ROUND_DOWN if the decimal is negative.
299     * <p>
300     *
301     * Note that there is no half ceiling rounding mode
302     * supported for BigDecimal values. This method uses a
303     * combination of the {@link BigDecimal#ROUND_HALF_UP} and
304     * {@link BigDecimal#ROUND_HALF_DOWN} to perform this
305     * new rounding mode.
306     *
307     * @param decimal The BigDecimal value to round.
308     * @return Rounded BigDecimal value
309     * **/
310    public static BigDecimal roundHalfCeiling(BigDecimal decimal) {
311        if (decimal.signum() == -1) {
312            return roundHalfDown(decimal);
313        }
314        return roundHalfUp(decimal);
315    }
316
317    /** Rounding mode to round towards "nearest neighbor" unless
318     * both neighbors are equidistant, in which case round down.
319     * Behaves as for ROUND_UP if the discarded fraction is &gt; .5;
320     * otherwise, behaves as for ROUND_DOWN.
321     *
322     * @see BigDecimal#ROUND_HALF_UP
323     *
324     * @param decimal The BigDecimal value to round.
325     * @return Rounded BigDecimal value
326     * **/
327    public static BigDecimal roundHalfDown(BigDecimal decimal) {
328        return decimal.setScale(0, BigDecimal.ROUND_HALF_DOWN);
329    }
330
331    /** Rounding mode to round towards the "nearest neighbor" unless
332     * both neighbors are equidistant, in which case, round towards
333     * the even neighbor. Behaves as for ROUND_HALF_UP if the digit
334     * to the left of the discarded fraction is odd; behaves as for
335     * ROUND_HALF_DOWN if it's even. Note that this is the rounding
336     * mode that minimizes cumulative error when applied repeatedly
337     * over a sequence of calculations.
338     *
339     * @see BigDecimal#ROUND_HALF_EVEN
340     *
341     * @param decimal The BigDecimal value to round.
342     * @return Rounded BigDecimal value
343     * **/
344    public static BigDecimal roundHalfEven(BigDecimal decimal) {
345        return decimal.setScale(0, BigDecimal.ROUND_HALF_EVEN);
346    }
347
348    /** Rounding mode to round towards "nearest neighbor" unless
349     * both neighbors are equidistant, in which case round
350     * "ceiling".
351     * Behaves as HALF_ROUND_DOWN if the decimal is positive and
352     * as HALF_ROUND_UP if the decimal is negative.
353     * <p>
354     *
355     * Note that there is no half floor rounding mode
356     * supported for BigDecimal values. This method uses a
357     * combination of the {@link BigDecimal#ROUND_HALF_UP} and
358     * {@link BigDecimal#ROUND_HALF_DOWN} to perform this
359     * new rounding mode.
360     *
361     * @param decimal The BigDecimal value to round.
362     * @return Rounded BigDecimal value
363     * **/
364    public static BigDecimal roundHalfFloor(BigDecimal decimal) {
365        if (decimal.signum() == -1) {
366            return roundHalfUp(decimal);
367        }
368        return roundHalfDown(decimal);
369    }
370
371    /** Rounding mode to round towards "nearest neighbor" unless
372     * both neighbors are equidistant, in which case round up.
373     * Behaves as for ROUND_UP if the discarded fraction is &ge; .5;
374     * otherwise, behaves as for ROUND_DOWN. Note that this is the
375     * rounding mode that most of us were taught in grade school.
376     * Rounding mode to round towards zero.
377     *
378     * @see BigDecimal#ROUND_HALF_UP
379     *
380     * @param decimal The BigDecimal value to round.
381     * @return Rounded BigDecimal value
382     * **/
383    public static BigDecimal roundHalfUp(BigDecimal decimal) {
384        return decimal.setScale(0, BigDecimal.ROUND_HALF_UP);
385    }
386
387    /** Rounding mode to round away from zero.
388     * Always increments the digit prior to a non-zero discarded fraction.
389     * Note that this rounding mode never decreases the magnitude of
390     * the calculated value.
391     *
392     * @see BigDecimal#ROUND_UP
393     *
394     * @param decimal The BigDecimal value to round.
395     * @return Rounded BigDecimal value
396     * **/
397    public static BigDecimal roundUp(BigDecimal decimal) {
398        return decimal.setScale(0, BigDecimal.ROUND_UP);
399    }
400
401    /** Return the string representation of this rounding.
402     *  @return A String.
403     */
404    @Override
405    public String toString() {
406        return _name;
407    }
408
409    ///////////////////////////////////////////////////////////////////
410    ////                         public variables                  ////
411    // NOTE: It may seem strange that these inner classes are built this
412    // way instead of as anonymous classes...  This code was copied from
413    // ptolemy.data.type.BaseType where an explanation that was valid
414    // for that usage may be found.
415
416    /** Singleton implementing ceiling rounding strategy. */
417    public static final RoundCeiling CEILING = new RoundCeiling();
418
419    /** Singleton implementing floor rounding strategy. */
420    public static final RoundFloor FLOOR = new RoundFloor();
421
422    /** Singleton implementing truncate rounding strategy. */
423    public static final RoundFloor TRUNCATE = FLOOR;
424
425    /** Singleton implementing down rounding strategy. */
426    public static final RoundDown DOWN = new RoundDown();
427
428    /** Singleton implementing up rounding strategy. */
429    public static final RoundUp UP = new RoundUp();
430
431    /** Singleton implementing half down rounding strategy. */
432    public static final RoundHalfDown HALF_DOWN = new RoundHalfDown();
433
434    /** Singleton implementing half up rounding strategy. */
435    public static final RoundHalfUp HALF_UP = new RoundHalfUp();
436
437    /** Singleton implementing half even rounding strategy. */
438    public static final RoundHalfEven HALF_EVEN = new RoundHalfEven();
439
440    /** Singleton implementing convergent rounding strategy. */
441    public static final RoundHalfEven CONVERGENT = HALF_EVEN;
442
443    /** Singleton implementing half ceiling rounding strategy. */
444    public static final RoundHalfCeiling HALF_CEILING = new RoundHalfCeiling();
445
446    /** Singleton implementing nearest rounding strategy. */
447    public static final RoundHalfCeiling NEAREST = HALF_CEILING;
448
449    /** Singleton implementing half floor rounding strategy. */
450    public static final RoundHalfFloor HALF_FLOOR = new RoundHalfFloor();
451
452    // The following singleton classes are listed below (and out of
453    // alphabetic order) because they depend on the construction of
454    // other singleton objects before they can be defined.
455
456    /** Singleton implementing general rounding strategy. */
457    public static final Rounding GENERAL = UP;
458
459    /** Singleton implementing unknown rounding strategy. */
460    public static final Rounding UNKNOWN = UP;
461
462    /** Singleton implementing unnecessary rounding strategy. */
463    public static final Rounding UNNECESSARY = HALF_UP;
464
465    ///////////////////////////////////////////////////////////////////
466    ////                         public inner classes              ////
467
468    /** Rounding class implementing the round ceiling strategy. */
469    public static class RoundCeiling extends Rounding {
470        private RoundCeiling() {
471            super("ceiling");
472        }
473
474        @Override
475        public BigInteger round(BigDecimal dec) {
476            return roundCeiling(dec).toBigInteger();
477        }
478    }
479
480    /** Rounding class implementing the round down strategy. */
481    public static class RoundDown extends Rounding {
482        private RoundDown() {
483            super("down");
484        }
485
486        @Override
487        public BigInteger round(BigDecimal dec) {
488            return roundDown(dec).toBigInteger();
489        }
490    }
491
492    /** Rounding class implementing the round floor strategy. */
493    public static class RoundFloor extends Rounding {
494        private RoundFloor() {
495            super("floor");
496            _addRounding(this, "truncate");
497        }
498
499        @Override
500        public BigInteger round(BigDecimal dec) {
501            return roundFloor(dec).toBigInteger();
502        }
503    }
504
505    /** Rounding class implementing the round half ceiling strategy. */
506    public static class RoundHalfCeiling extends Rounding {
507        private RoundHalfCeiling() {
508            super("half_ceiling");
509            _addRounding(this, "nearest");
510            _addRounding(this, "round"); // For compatibility
511        }
512
513        @Override
514        public BigInteger round(BigDecimal dec) {
515            return roundHalfCeiling(dec).toBigInteger();
516        }
517    }
518
519    /** Rounding class implementing the round half down strategy. */
520    public static class RoundHalfDown extends Rounding {
521        private RoundHalfDown() {
522            super("half_down");
523        }
524
525        @Override
526        public BigInteger round(BigDecimal dec) {
527            return roundHalfDown(dec).toBigInteger();
528        }
529    }
530
531    /** Rounding class implementing the round half even strategy. */
532    public static class RoundHalfEven extends Rounding {
533        private RoundHalfEven() {
534            super("half_even");
535            _addRounding(this, "convergent");
536        }
537
538        @Override
539        public BigInteger round(BigDecimal dec) {
540            return roundHalfEven(dec).toBigInteger();
541        }
542    }
543
544    /** Rounding class implementing the round half floor strategy. */
545    public static class RoundHalfFloor extends Rounding {
546        private RoundHalfFloor() {
547            super("half_floor");
548        }
549
550        @Override
551        public BigInteger round(BigDecimal dec) {
552            return roundHalfFloor(dec).toBigInteger();
553        }
554    }
555
556    /** Rounding class implementing the round half up strategy. */
557    public static class RoundHalfUp extends Rounding {
558        private RoundHalfUp() {
559            super("half_up");
560            _addRounding(this, "unnecessary");
561        }
562
563        @Override
564        public BigInteger round(BigDecimal dec) {
565            return roundHalfUp(dec).toBigInteger();
566        }
567    }
568
569    /** Rounding class implementing the round up strategy. */
570    public static class RoundUp extends Rounding {
571        private RoundUp() {
572            super("up");
573            _addRounding(this, "general");
574            _addRounding(this, "unknown");
575        }
576
577        @Override
578        public BigInteger round(BigDecimal dec) {
579            return roundUp(dec).toBigInteger();
580        }
581    }
582
583    ///////////////////////////////////////////////////////////////////
584    ////                     protected constructor                 ////
585
586    /** Construct a Rounding object with the given String name.
587     *  This name is used for finding a Rounding object at a later
588     *  time (@see #forName(String)). This constructor
589     *  is protected to make a type safe enumeration.
590     *
591     * @param name The String name to give this Rounding strategy.
592     *
593     */
594    protected Rounding(String name) {
595        _name = name;
596        _addRounding(this, name);
597    }
598
599    ///////////////////////////////////////////////////////////////////
600    ////                         protected methods                 ////
601
602    ///////////////////////////////////////////////////////////////////
603    ////                    package private method                 ////
604
605    // Add entries in this class to index the given name to
606    // the given rounding type.
607    static void _addRounding(Rounding type, String name) {
608        // Because the private variables are below the public variables
609        // that call this initializer,
610        // it doesn't work to initialize this statically.
611        if (_nameToRounding == null) {
612            _nameToRounding = new HashMap();
613        }
614
615        _nameToRounding.put(name, type);
616    }
617
618    ///////////////////////////////////////////////////////////////////
619    ////                         private variables                 ////
620
621    /** The name of the rounding mode. */
622    private String _name;
623
624    /** A map from rounding type name to the rounding type for all rounding
625     *  types.
626     */
627    private static Map _nameToRounding;
628
629}