1fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert// © 2017 and later: Unicode, Inc. and others.
2fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
3fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertpackage com.ibm.icu.number;
4fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
5fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.impl.number.DecimalQuantity;
6fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.impl.number.MicroProps;
7fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.impl.number.MicroPropsGenerator;
8fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.impl.number.Modifier;
9fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.impl.number.MultiplierProducer;
10fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.impl.number.NumberStringBuilder;
11fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.impl.number.RoundingUtils;
12fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.number.NumberFormatter.SignDisplay;
13fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.number.Rounder.SignificantRounderImpl;
14fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.text.DecimalFormatSymbols;
15fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport com.ibm.icu.text.NumberFormat;
16fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
17fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert/**
18fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert * A class that defines the scientific notation style to be used when formatting numbers in NumberFormatter.
19fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert *
20fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert * <p>
21fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert * To create a ScientificNotation, use one of the factory methods in {@link Notation}.
22fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert *
23fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert * @draft ICU 60
24fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert * @provisional This API might change or be removed in a future release.
25fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert * @see NumberFormatter
26fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert */
27fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertpublic class ScientificNotation extends Notation implements Cloneable {
28fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
29fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    int engineeringInterval;
30fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    boolean requireMinInt;
31fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    int minExponentDigits;
32fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    SignDisplay exponentSignDisplay;
33fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
34fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    /* package-private */ ScientificNotation(int engineeringInterval, boolean requireMinInt, int minExponentDigits,
35fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            SignDisplay exponentSignDisplay) {
36fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        this.engineeringInterval = engineeringInterval;
37fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        this.requireMinInt = requireMinInt;
38fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        this.minExponentDigits = minExponentDigits;
39fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        this.exponentSignDisplay = exponentSignDisplay;
40fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    }
41fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
42fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    /**
43fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * Sets the minimum number of digits to show in the exponent of scientific notation, padding with zeros if
44fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * necessary. Useful for fixed-width display.
45fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     *
46fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * <p>
47fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * For example, with minExponentDigits=2, the number 123 will be printed as "1.23E02" in <em>en-US</em> instead of
48fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * the default "1.23E2".
49fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     *
50fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @param minExponentDigits
51fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     *            The minimum number of digits to show in the exponent.
52fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @return A ScientificNotation, for chaining.
53fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @draft ICU 60
54fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @provisional This API might change or be removed in a future release.
55fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @see NumberFormatter
56fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     */
57fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    public ScientificNotation withMinExponentDigits(int minExponentDigits) {
58fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        if (minExponentDigits >= 0 && minExponentDigits < RoundingUtils.MAX_INT_FRAC_SIG) {
59fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            ScientificNotation other = (ScientificNotation) this.clone();
60fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            other.minExponentDigits = minExponentDigits;
61fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return other;
62fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        } else {
63fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            throw new IllegalArgumentException(
64fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                    "Integer digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
65fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
66fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    }
67fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
68fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    /**
69fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * Sets whether to show the sign on positive and negative exponents in scientific notation. The default is AUTO,
70fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * showing the minus sign but not the plus sign.
71fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     *
72fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * <p>
73fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * For example, with exponentSignDisplay=ALWAYS, the number 123 will be printed as "1.23E+2" in <em>en-US</em>
74fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * instead of the default "1.23E2".
75fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     *
76fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @param exponentSignDisplay
77fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     *            The strategy for displaying the sign in the exponent.
78fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @return A ScientificNotation, for chaining.
79fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @draft ICU 60
80fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @provisional This API might change or be removed in a future release.
81fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @see NumberFormatter
82fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     */
83fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    public ScientificNotation withExponentSignDisplay(SignDisplay exponentSignDisplay) {
84fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        ScientificNotation other = (ScientificNotation) this.clone();
85fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        other.exponentSignDisplay = exponentSignDisplay;
86fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        return other;
87fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    }
88fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
89fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    /**
90fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @internal
91fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     * @deprecated This API is ICU internal only.
92fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert     */
93fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    @Deprecated
94fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    @Override
95fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    public Object clone() {
96fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        try {
97fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return super.clone();
98fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        } catch (CloneNotSupportedException e) {
99fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // Should not happen since parent is Object
100fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            throw new AssertionError(e);
101fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
102fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    }
103fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
104fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    /* package-private */ MicroPropsGenerator withLocaleData(DecimalFormatSymbols symbols, boolean build,
105fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            MicroPropsGenerator parent) {
106fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        return new ScientificHandler(this, symbols, build, parent);
107fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    }
108fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
109fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    // NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
110fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    //
111fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    // During formatting, we need to provide an object with state (the exponent) as the inner modifier.
112fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    //
113fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    // In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the
114fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    // ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier
115fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    // instances. This scheme reduces the number of object creations by 1 in both safe and unsafe.
116fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    //
117fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    // In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates
118fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    // the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe.
119fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
120fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    private static class ScientificHandler implements MicroPropsGenerator, MultiplierProducer, Modifier {
121fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
122fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        final ScientificNotation notation;
123fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        final DecimalFormatSymbols symbols;
124fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        final ScientificModifier[] precomputedMods;
125fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        final MicroPropsGenerator parent;
126fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        /* unsafe */ int exponent;
127fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
128fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        private ScientificHandler(ScientificNotation notation, DecimalFormatSymbols symbols, boolean safe,
129fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                MicroPropsGenerator parent) {
130fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            this.notation = notation;
131fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            this.symbols = symbols;
132fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            this.parent = parent;
133fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
134fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            if (safe) {
135fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                // Pre-build the modifiers for exponents -12 through 12
136fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                precomputedMods = new ScientificModifier[25];
137fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                for (int i = -12; i <= 12; i++) {
138fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                    precomputedMods[i + 12] = new ScientificModifier(i, this);
139fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                }
140fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            } else {
141fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                precomputedMods = null;
142fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            }
143fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
144fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
145fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
146fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public MicroProps processQuantity(DecimalQuantity quantity) {
147fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            MicroProps micros = parent.processQuantity(quantity);
148fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            assert micros.rounding != null;
149fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
150fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // Treat zero as if it had magnitude 0
151fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            int exponent;
152fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            if (quantity.isZero()) {
153fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                if (notation.requireMinInt && micros.rounding instanceof SignificantRounderImpl) {
154fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                    // Show "00.000E0" on pattern "00.000E0"
155fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                    ((SignificantRounderImpl) micros.rounding).apply(quantity, notation.engineeringInterval);
156fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                    exponent = 0;
157fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                } else {
158fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                    micros.rounding.apply(quantity);
159fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                    exponent = 0;
160fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                }
161fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            } else {
162fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                exponent = -micros.rounding.chooseMultiplierAndApply(quantity, this);
163fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            }
164fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
165fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // Add the Modifier for the scientific format.
166fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            if (precomputedMods != null && exponent >= -12 && exponent <= 12) {
167fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                // Safe code path A
168fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                micros.modInner = precomputedMods[exponent + 12];
169fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            } else if (precomputedMods != null) {
170fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                // Safe code path B
171fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                micros.modInner = new ScientificModifier(exponent, this);
172fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            } else {
173fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                // Unsafe code path: mutates the object and re-uses it as a Modifier!
174fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                this.exponent = exponent;
175fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                micros.modInner = this;
176fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            }
177fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
178fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // We already performed rounding. Do not perform it again.
179fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            micros.rounding = Rounder.constructPassThrough();
180fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
181fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return micros;
182fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
183fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
184fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
185fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public int getMultiplier(int magnitude) {
186fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            int interval = notation.engineeringInterval;
187fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            int digitsShown;
188fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            if (notation.requireMinInt) {
189fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                // For patterns like "000.00E0" and ".00E0"
190fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                digitsShown = interval;
191fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            } else if (interval <= 1) {
192fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                // For patterns like "0.00E0" and "@@@E0"
193fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                digitsShown = 1;
194fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            } else {
195fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                // For patterns like "##0.00"
196fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                digitsShown = ((magnitude % interval + interval) % interval) + 1;
197fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            }
198fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return digitsShown - magnitude - 1;
199fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
200fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
201fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
202fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public int getPrefixLength() {
203fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // TODO: Localized exponent separator location.
204fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return 0;
205fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
206fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
207fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
208fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public int getCodePointCount() {
209fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // This method is not used for strong modifiers.
210fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            throw new AssertionError();
211fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
212fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
213fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
214fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public boolean isStrong() {
215fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // Scientific is always strong
216fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return true;
217fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
218fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
219fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
220fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
221fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return doApply(exponent, output, rightIndex);
222fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
223fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
224fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        private int doApply(int exponent, NumberStringBuilder output, int rightIndex) {
225fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // FIXME: Localized exponent separator location.
226fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            int i = rightIndex;
227fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // Append the exponent separator and sign
228fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            i += output.insert(i, symbols.getExponentSeparator(), NumberFormat.Field.EXPONENT_SYMBOL);
229fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            if (exponent < 0 && notation.exponentSignDisplay != SignDisplay.NEVER) {
230fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                i += output.insert(i, symbols.getMinusSignString(), NumberFormat.Field.EXPONENT_SIGN);
231fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            } else if (exponent >= 0 && notation.exponentSignDisplay == SignDisplay.ALWAYS) {
232fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                i += output.insert(i, symbols.getPlusSignString(), NumberFormat.Field.EXPONENT_SIGN);
233fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            }
234fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // Append the exponent digits (using a simple inline algorithm)
235fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            int disp = Math.abs(exponent);
236fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            for (int j = 0; j < notation.minExponentDigits || disp > 0; j++, disp /= 10) {
237fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                int d = disp % 10;
238fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                String digitString = symbols.getDigitStringsLocal()[d];
239fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert                i += output.insert(i - j, digitString, NumberFormat.Field.EXPONENT);
240fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            }
241fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return i - rightIndex;
242fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
243fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    }
244fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
245fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    private static class ScientificModifier implements Modifier {
246fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        final int exponent;
247fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        final ScientificHandler handler;
248fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
249fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        ScientificModifier(int exponent, ScientificHandler handler) {
250fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            this.exponent = exponent;
251fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            this.handler = handler;
252fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
253fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
254fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
255fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
256fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return handler.doApply(exponent, output, rightIndex);
257fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
258fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
259fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
260fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public int getPrefixLength() {
261fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // TODO: Localized exponent separator location.
262fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return 0;
263fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
264fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
265fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
266fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public int getCodePointCount() {
267fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // This method is not used for strong modifiers.
268fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            throw new AssertionError();
269fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
270fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert
271fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        @Override
272fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        public boolean isStrong() {
273fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            // Scientific is always strong
274fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert            return true;
275fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert        }
276fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert    }
277fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert}