1/* GENERATED SOURCE. DO NOT MODIFY. */
2// © 2017 and later: Unicode, Inc. and others.
3// License & terms of use: http://www.unicode.org/copyright.html#License
4package android.icu.number;
5
6import java.math.BigDecimal;
7import java.math.MathContext;
8import java.math.RoundingMode;
9
10import android.icu.impl.number.DecimalQuantity;
11import android.icu.impl.number.MultiplierProducer;
12import android.icu.impl.number.RoundingUtils;
13import android.icu.util.Currency;
14import android.icu.util.Currency.CurrencyUsage;
15
16/**
17 * A class that defines the rounding strategy to be used when formatting numbers in NumberFormatter.
18 *
19 * <p>
20 * To create a Rounder, use one of the factory methods.
21 *
22 * @see NumberFormatter
23 * @hide Only a subset of ICU is exposed in Android
24 * @hide draft / provisional / internal are hidden on Android
25 */
26public abstract class Rounder implements Cloneable {
27
28    /* package-private final */ MathContext mathContext;
29
30    /* package-private */ Rounder() {
31        mathContext = RoundingUtils.mathContextUnlimited(RoundingUtils.DEFAULT_ROUNDING_MODE);
32    }
33
34    /**
35     * Show all available digits to full precision.
36     *
37     * <p>
38     * <strong>NOTE:</strong> When formatting a <em>double</em>, this method, along with {@link #minFraction} and
39     * {@link #minDigits}, will trigger complex algorithm similar to <em>Dragon4</em> to determine the low-order digits
40     * and the number of digits to display based on the value of the double. If the number of fraction places or
41     * significant digits can be bounded, consider using {@link #maxFraction} or {@link #maxDigits} instead to maximize
42     * performance. For more information, read the following blog post.
43     *
44     * <p>
45     * http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/
46     *
47     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
48     * @see NumberFormatter
49     * @hide draft / provisional / internal are hidden on Android
50     */
51    public static Rounder unlimited() {
52        return constructInfinite();
53    }
54
55    /**
56     * Show numbers rounded if necessary to the nearest integer.
57     *
58     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
59     * @see NumberFormatter
60     * @hide draft / provisional / internal are hidden on Android
61     */
62    public static FractionRounder integer() {
63        return constructFraction(0, 0);
64    }
65
66    /**
67     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator).
68     * Additionally, pad with zeros to ensure that this number of places are always shown.
69     *
70     * <p>
71     * Example output with minMaxFractionPlaces = 3:
72     *
73     * <p>
74     * 87,650.000<br>
75     * 8,765.000<br>
76     * 876.500<br>
77     * 87.650<br>
78     * 8.765<br>
79     * 0.876<br>
80     * 0.088<br>
81     * 0.009<br>
82     * 0.000 (zero)
83     *
84     * <p>
85     * This method is equivalent to {@link #minMaxFraction} with both arguments equal.
86     *
87     * @param minMaxFractionPlaces
88     *            The minimum and maximum number of numerals to display after the decimal separator (rounding if too
89     *            long or padding with zeros if too short).
90     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
91     * @see NumberFormatter
92     * @hide draft / provisional / internal are hidden on Android
93     */
94    public static FractionRounder fixedFraction(int minMaxFractionPlaces) {
95        if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
96            return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
97        } else {
98            throw new IllegalArgumentException(
99                    "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
100        }
101    }
102
103    /**
104     * Always show at least a certain number of fraction places after the decimal separator, padding with zeros if
105     * necessary. Do not perform rounding (display numbers to their full precision).
106     *
107     * <p>
108     * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in {@link #unlimited}.
109     *
110     * @param minFractionPlaces
111     *            The minimum number of numerals to display after the decimal separator (padding with zeros if
112     *            necessary).
113     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
114     * @see NumberFormatter
115     * @hide draft / provisional / internal are hidden on Android
116     */
117    public static FractionRounder minFraction(int minFractionPlaces) {
118        if (minFractionPlaces >= 0 && minFractionPlaces < RoundingUtils.MAX_INT_FRAC_SIG) {
119            return constructFraction(minFractionPlaces, -1);
120        } else {
121            throw new IllegalArgumentException(
122                    "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
123        }
124    }
125
126    /**
127     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator).
128     * Unlike the other fraction rounding strategies, this strategy does <em>not</em> pad zeros to the end of the
129     * number.
130     *
131     * @param maxFractionPlaces
132     *            The maximum number of numerals to display after the decimal mark (rounding if necessary).
133     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
134     * @see NumberFormatter
135     * @hide draft / provisional / internal are hidden on Android
136     */
137    public static FractionRounder maxFraction(int maxFractionPlaces) {
138        if (maxFractionPlaces >= 0 && maxFractionPlaces < RoundingUtils.MAX_INT_FRAC_SIG) {
139            return constructFraction(0, maxFractionPlaces);
140        } else {
141            throw new IllegalArgumentException(
142                    "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
143        }
144    }
145
146    /**
147     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator);
148     * in addition, always show at least a certain number of places after the decimal separator, padding with zeros if
149     * necessary.
150     *
151     * @param minFractionPlaces
152     *            The minimum number of numerals to display after the decimal separator (padding with zeros if
153     *            necessary).
154     * @param maxFractionPlaces
155     *            The maximum number of numerals to display after the decimal separator (rounding if necessary).
156     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
157     * @see NumberFormatter
158     * @hide draft / provisional / internal are hidden on Android
159     */
160    public static FractionRounder minMaxFraction(int minFractionPlaces, int maxFractionPlaces) {
161        if (minFractionPlaces >= 0 && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG
162                && minFractionPlaces <= maxFractionPlaces) {
163            return constructFraction(minFractionPlaces, maxFractionPlaces);
164        } else {
165            throw new IllegalArgumentException(
166                    "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
167        }
168    }
169
170    /**
171     * Show numbers rounded if necessary to a certain number of significant digits or significant figures. Additionally,
172     * pad with zeros to ensure that this number of significant digits/figures are always shown.
173     *
174     * <p>
175     * This method is equivalent to {@link #minMaxDigits} with both arguments equal.
176     *
177     * @param minMaxSignificantDigits
178     *            The minimum and maximum number of significant digits to display (rounding if too long or padding with
179     *            zeros if too short).
180     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
181     * @see NumberFormatter
182     * @hide draft / provisional / internal are hidden on Android
183     */
184    public static Rounder fixedDigits(int minMaxSignificantDigits) {
185        if (minMaxSignificantDigits > 0 && minMaxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
186            return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
187        } else {
188            throw new IllegalArgumentException(
189                    "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
190        }
191    }
192
193    /**
194     * Always show at least a certain number of significant digits/figures, padding with zeros if necessary. Do not
195     * perform rounding (display numbers to their full precision).
196     *
197     * <p>
198     * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in {@link #unlimited}.
199     *
200     * @param minSignificantDigits
201     *            The minimum number of significant digits to display (padding with zeros if too short).
202     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
203     * @see NumberFormatter
204     * @hide draft / provisional / internal are hidden on Android
205     */
206    public static Rounder minDigits(int minSignificantDigits) {
207        if (minSignificantDigits > 0 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
208            return constructSignificant(minSignificantDigits, -1);
209        } else {
210            throw new IllegalArgumentException(
211                    "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
212        }
213    }
214
215    /**
216     * Show numbers rounded if necessary to a certain number of significant digits/figures.
217     *
218     * @param maxSignificantDigits
219     *            The maximum number of significant digits to display (rounding if too long).
220     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
221     * @see NumberFormatter
222     * @hide draft / provisional / internal are hidden on Android
223     */
224    public static Rounder maxDigits(int maxSignificantDigits) {
225        if (maxSignificantDigits > 0 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
226            return constructSignificant(0, maxSignificantDigits);
227        } else {
228            throw new IllegalArgumentException(
229                    "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
230        }
231    }
232
233    /**
234     * Show numbers rounded if necessary to a certain number of significant digits/figures; in addition, always show at
235     * least a certain number of significant digits, padding with zeros if necessary.
236     *
237     * @param minSignificantDigits
238     *            The minimum number of significant digits to display (padding with zeros if necessary).
239     * @param maxSignificantDigits
240     *            The maximum number of significant digits to display (rounding if necessary).
241     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
242     * @see NumberFormatter
243     * @hide draft / provisional / internal are hidden on Android
244     */
245    public static Rounder minMaxDigits(int minSignificantDigits, int maxSignificantDigits) {
246        if (minSignificantDigits > 0 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG
247                && minSignificantDigits <= maxSignificantDigits) {
248            return constructSignificant(minSignificantDigits, maxSignificantDigits);
249        } else {
250            throw new IllegalArgumentException(
251                    "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
252        }
253    }
254
255    /**
256     * Show numbers rounded if necessary to the closest multiple of a certain rounding increment. For example, if the
257     * rounding increment is 0.5, then round 1.2 to 1 and round 1.3 to 1.5.
258     *
259     * <p>
260     * In order to ensure that numbers are padded to the appropriate number of fraction places, set the scale on the
261     * rounding increment BigDecimal. For example, to round to the nearest 0.5 and always display 2 numerals after the
262     * decimal separator (to display 1.2 as "1.00" and 1.3 as "1.50"), you can run:
263     *
264     * <pre>
265     * Rounder.increment(new BigDecimal("0.50"))
266     * </pre>
267     *
268     * <p>
269     * For more information on the scale of Java BigDecimal, see {@link java.math.BigDecimal#scale()}.
270     *
271     * @param roundingIncrement
272     *            The increment to which to round numbers.
273     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
274     * @see NumberFormatter
275     * @hide draft / provisional / internal are hidden on Android
276     */
277    public static Rounder increment(BigDecimal roundingIncrement) {
278        if (roundingIncrement != null && roundingIncrement.compareTo(BigDecimal.ZERO) > 0) {
279            return constructIncrement(roundingIncrement);
280        } else {
281            throw new IllegalArgumentException("Rounding increment must be positive and non-null");
282        }
283    }
284
285    /**
286     * Show numbers rounded and padded according to the rules for the currency unit. The most common rounding settings
287     * for currencies include <code>Rounder.fixedFraction(2)</code>, <code>Rounder.integer()</code>, and
288     * <code>Rounder.increment(0.05)</code> for cash transactions ("nickel rounding").
289     *
290     * <p>
291     * The exact rounding details will be resolved at runtime based on the currency unit specified in the
292     * NumberFormatter chain. To round according to the rules for one currency while displaying the symbol for another
293     * currency, the withCurrency() method can be called on the return value of this method.
294     *
295     * @param currencyUsage
296     *            Either STANDARD (for digital transactions) or CASH (for transactions where the rounding increment may
297     *            be limited by the available denominations of cash or coins).
298     * @return A CurrencyRounder for chaining or passing to the NumberFormatter rounding() setter.
299     * @see NumberFormatter
300     * @hide draft / provisional / internal are hidden on Android
301     */
302    public static CurrencyRounder currency(CurrencyUsage currencyUsage) {
303        if (currencyUsage != null) {
304            return constructCurrency(currencyUsage);
305        } else {
306            throw new IllegalArgumentException("CurrencyUsage must be non-null");
307        }
308    }
309
310    /**
311     * Sets the {@link java.math.RoundingMode} to use when picking the direction to round (up or down). Common values
312     * include HALF_EVEN, HALF_UP, and FLOOR. The default is HALF_EVEN.
313     *
314     * @param roundingMode
315     *            The RoundingMode to use.
316     * @return A Rounder for chaining.
317     * @see NumberFormatter
318     * @hide draft / provisional / internal are hidden on Android
319     */
320    public Rounder withMode(RoundingMode roundingMode) {
321        return withMode(RoundingUtils.mathContextUnlimited(roundingMode));
322    }
323
324    /**
325     * Sets a MathContext directly instead of RoundingMode.
326     *
327     * @deprecated This API is ICU internal only.
328     * @hide draft / provisional / internal are hidden on Android
329     */
330    @Deprecated
331    public Rounder withMode(MathContext mathContext) {
332        if (this.mathContext.equals(mathContext)) {
333            return this;
334        }
335        Rounder other = (Rounder) this.clone();
336        other.mathContext = mathContext;
337        return other;
338    }
339
340    /**
341     * @deprecated This API is ICU internal only.
342     * @hide draft / provisional / internal are hidden on Android
343     */
344    @Deprecated
345    @Override
346    public Object clone() {
347        try {
348            return super.clone();
349        } catch (CloneNotSupportedException e) {
350            // Should not happen since parent is Object
351            throw new AssertionError(e);
352        }
353    }
354
355    /**
356     * @deprecated ICU 60 This API is ICU internal only.
357     * @hide draft / provisional / internal are hidden on Android
358     */
359    @Deprecated
360    public abstract void apply(DecimalQuantity value);
361
362    //////////////////////////
363    // PACKAGE-PRIVATE APIS //
364    //////////////////////////
365
366    static final InfiniteRounderImpl NONE = new InfiniteRounderImpl();
367
368    static final FractionRounderImpl FIXED_FRAC_0 = new FractionRounderImpl(0, 0);
369    static final FractionRounderImpl FIXED_FRAC_2 = new FractionRounderImpl(2, 2);
370    static final FractionRounderImpl MAX_FRAC_6 = new FractionRounderImpl(0, 6);
371
372    static final SignificantRounderImpl FIXED_SIG_2 = new SignificantRounderImpl(2, 2);
373    static final SignificantRounderImpl FIXED_SIG_3 = new SignificantRounderImpl(3, 3);
374    static final SignificantRounderImpl RANGE_SIG_2_3 = new SignificantRounderImpl(2, 3);
375
376    static final FracSigRounderImpl COMPACT_STRATEGY = new FracSigRounderImpl(0, 0, 2, -1);
377
378    static final IncrementRounderImpl NICKEL = new IncrementRounderImpl(BigDecimal.valueOf(0.05));
379
380    static final CurrencyRounderImpl MONETARY_STANDARD = new CurrencyRounderImpl(CurrencyUsage.STANDARD);
381    static final CurrencyRounderImpl MONETARY_CASH = new CurrencyRounderImpl(CurrencyUsage.CASH);
382
383    static final PassThroughRounderImpl PASS_THROUGH = new PassThroughRounderImpl();
384
385    static Rounder constructInfinite() {
386        return NONE;
387    }
388
389    static FractionRounder constructFraction(int minFrac, int maxFrac) {
390        if (minFrac == 0 && maxFrac == 0) {
391            return FIXED_FRAC_0;
392        } else if (minFrac == 2 && maxFrac == 2) {
393            return FIXED_FRAC_2;
394        } else if (minFrac == 0 && maxFrac == 6) {
395            return MAX_FRAC_6;
396        } else {
397            return new FractionRounderImpl(minFrac, maxFrac);
398        }
399    }
400
401    /** Assumes that minSig <= maxSig. */
402    static Rounder constructSignificant(int minSig, int maxSig) {
403        if (minSig == 2 && maxSig == 2) {
404            return FIXED_SIG_2;
405        } else if (minSig == 3 && maxSig == 3) {
406            return FIXED_SIG_3;
407        } else if (minSig == 2 && maxSig == 3) {
408            return RANGE_SIG_2_3;
409        } else {
410            return new SignificantRounderImpl(minSig, maxSig);
411        }
412    }
413
414    static Rounder constructFractionSignificant(FractionRounder base_, int minSig, int maxSig) {
415        assert base_ instanceof FractionRounderImpl;
416        FractionRounderImpl base = (FractionRounderImpl) base_;
417        if (base.minFrac == 0 && base.maxFrac == 0 && minSig == 2 /* && maxSig == -1 */) {
418            return COMPACT_STRATEGY;
419        } else {
420            return new FracSigRounderImpl(base.minFrac, base.maxFrac, minSig, maxSig);
421        }
422    }
423
424    static Rounder constructIncrement(BigDecimal increment) {
425        // NOTE: .equals() is what we want, not .compareTo()
426        if (increment.equals(NICKEL.increment)) {
427            return NICKEL;
428        } else {
429            return new IncrementRounderImpl(increment);
430        }
431    }
432
433    static CurrencyRounder constructCurrency(CurrencyUsage usage) {
434        if (usage == CurrencyUsage.STANDARD) {
435            return MONETARY_STANDARD;
436        } else if (usage == CurrencyUsage.CASH) {
437            return MONETARY_CASH;
438        } else {
439            throw new AssertionError();
440        }
441    }
442
443    static Rounder constructFromCurrency(CurrencyRounder base_, Currency currency) {
444        assert base_ instanceof CurrencyRounderImpl;
445        CurrencyRounderImpl base = (CurrencyRounderImpl) base_;
446        double incrementDouble = currency.getRoundingIncrement(base.usage);
447        if (incrementDouble != 0.0) {
448            BigDecimal increment = BigDecimal.valueOf(incrementDouble);
449            return constructIncrement(increment);
450        } else {
451            int minMaxFrac = currency.getDefaultFractionDigits(base.usage);
452            return constructFraction(minMaxFrac, minMaxFrac);
453        }
454    }
455
456    static Rounder constructPassThrough() {
457        return PASS_THROUGH;
458    }
459
460    /**
461     * Returns a valid working Rounder. If the Rounder is a CurrencyRounder, applies the given currency. Otherwise,
462     * simply passes through the argument.
463     *
464     * @param currency
465     *            A currency object to use in case the input object needs it.
466     * @return A Rounder object ready for use.
467     */
468    Rounder withLocaleData(Currency currency) {
469        if (this instanceof CurrencyRounder) {
470            return ((CurrencyRounder) this).withCurrency(currency);
471        } else {
472            return this;
473        }
474    }
475
476    int chooseMultiplierAndApply(DecimalQuantity input, MultiplierProducer producer) {
477        // TODO: Make a better and more efficient implementation.
478        // TODO: Avoid the object creation here.
479        DecimalQuantity copy = input.createCopy();
480
481        assert !input.isZero();
482        int magnitude = input.getMagnitude();
483        int multiplier = producer.getMultiplier(magnitude);
484        input.adjustMagnitude(multiplier);
485        apply(input);
486
487        // If the number turned to zero when rounding, do not re-attempt the rounding.
488        if (!input.isZero() && input.getMagnitude() == magnitude + multiplier + 1) {
489            magnitude += 1;
490            input.copyFrom(copy);
491            multiplier = producer.getMultiplier(magnitude);
492            input.adjustMagnitude(multiplier);
493            assert input.getMagnitude() == magnitude + multiplier - 1;
494            apply(input);
495            assert input.getMagnitude() == magnitude + multiplier;
496        }
497
498        return multiplier;
499    }
500
501    ///////////////
502    // INTERNALS //
503    ///////////////
504
505    static class InfiniteRounderImpl extends Rounder {
506
507        public InfiniteRounderImpl() {
508        }
509
510        @Override
511        public void apply(DecimalQuantity value) {
512            value.roundToInfinity();
513            value.setFractionLength(0, Integer.MAX_VALUE);
514        }
515    }
516
517    static class FractionRounderImpl extends FractionRounder {
518        final int minFrac;
519        final int maxFrac;
520
521        public FractionRounderImpl(int minFrac, int maxFrac) {
522            this.minFrac = minFrac;
523            this.maxFrac = maxFrac;
524        }
525
526        @Override
527        public void apply(DecimalQuantity value) {
528            value.roundToMagnitude(getRoundingMagnitudeFraction(maxFrac), mathContext);
529            value.setFractionLength(Math.max(0, -getDisplayMagnitudeFraction(minFrac)), Integer.MAX_VALUE);
530        }
531    }
532
533    static class SignificantRounderImpl extends Rounder {
534        final int minSig;
535        final int maxSig;
536
537        public SignificantRounderImpl(int minSig, int maxSig) {
538            this.minSig = minSig;
539            this.maxSig = maxSig;
540        }
541
542        @Override
543        public void apply(DecimalQuantity value) {
544            value.roundToMagnitude(getRoundingMagnitudeSignificant(value, maxSig), mathContext);
545            value.setFractionLength(Math.max(0, -getDisplayMagnitudeSignificant(value, minSig)), Integer.MAX_VALUE);
546        }
547
548        /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
549        public void apply(DecimalQuantity quantity, int minInt) {
550            assert quantity.isZero();
551            quantity.setFractionLength(minSig - minInt, Integer.MAX_VALUE);
552        }
553    }
554
555    static class FracSigRounderImpl extends Rounder {
556        final int minFrac;
557        final int maxFrac;
558        final int minSig;
559        final int maxSig;
560
561        public FracSigRounderImpl(int minFrac, int maxFrac, int minSig, int maxSig) {
562            this.minFrac = minFrac;
563            this.maxFrac = maxFrac;
564            this.minSig = minSig;
565            this.maxSig = maxSig;
566        }
567
568        @Override
569        public void apply(DecimalQuantity value) {
570            int displayMag = getDisplayMagnitudeFraction(minFrac);
571            int roundingMag = getRoundingMagnitudeFraction(maxFrac);
572            if (minSig == -1) {
573                // Max Sig override
574                int candidate = getRoundingMagnitudeSignificant(value, maxSig);
575                roundingMag = Math.max(roundingMag, candidate);
576            } else {
577                // Min Sig override
578                int candidate = getDisplayMagnitudeSignificant(value, minSig);
579                roundingMag = Math.min(roundingMag, candidate);
580            }
581            value.roundToMagnitude(roundingMag, mathContext);
582            value.setFractionLength(Math.max(0, -displayMag), Integer.MAX_VALUE);
583        }
584    }
585
586    static class IncrementRounderImpl extends Rounder {
587        final BigDecimal increment;
588
589        public IncrementRounderImpl(BigDecimal increment) {
590            this.increment = increment;
591        }
592
593        @Override
594        public void apply(DecimalQuantity value) {
595            value.roundToIncrement(increment, mathContext);
596            value.setFractionLength(increment.scale(), increment.scale());
597        }
598    }
599
600    static class CurrencyRounderImpl extends CurrencyRounder {
601        final CurrencyUsage usage;
602
603        public CurrencyRounderImpl(CurrencyUsage usage) {
604            this.usage = usage;
605        }
606
607        @Override
608        public void apply(DecimalQuantity value) {
609            // Call .withCurrency() before .apply()!
610            throw new AssertionError();
611        }
612    }
613
614    static class PassThroughRounderImpl extends Rounder {
615
616        public PassThroughRounderImpl() {
617        }
618
619        @Override
620        public void apply(DecimalQuantity value) {
621            // TODO: Assert that value has already been rounded
622        }
623    }
624
625    private static int getRoundingMagnitudeFraction(int maxFrac) {
626        if (maxFrac == -1) {
627            return Integer.MIN_VALUE;
628        }
629        return -maxFrac;
630    }
631
632    private static int getRoundingMagnitudeSignificant(DecimalQuantity value, int maxSig) {
633        if (maxSig == -1) {
634            return Integer.MIN_VALUE;
635        }
636        int magnitude = value.isZero() ? 0 : value.getMagnitude();
637        return magnitude - maxSig + 1;
638    }
639
640    private static int getDisplayMagnitudeFraction(int minFrac) {
641        if (minFrac == 0) {
642            return Integer.MAX_VALUE;
643        }
644        return -minFrac;
645    }
646
647    private static int getDisplayMagnitudeSignificant(DecimalQuantity value, int minSig) {
648        int magnitude = value.isZero() ? 0 : value.getMagnitude();
649        return magnitude - minSig + 1;
650    }
651}
652