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.impl.number;
5
6import java.math.BigDecimal;
7import java.math.BigInteger;
8
9/**
10 * A DecimalQuantity with internal storage as a 64-bit BCD, with fallback to a byte array
11 * for numbers that don't fit into the standard BCD.
12 * @hide Only a subset of ICU is exposed in Android
13 */
14public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_AbstractBCD {
15
16  /**
17   * The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map
18   * to one digit. For example, the number "12345" in BCD is "0x12345".
19   *
20   * <p>Whenever bcd changes internally, {@link #compact()} must be called, except in special cases
21   * like setting the digit to zero.
22   */
23  private byte[] bcdBytes;
24
25  private long bcdLong = 0L;
26
27  private boolean usingBytes = false;
28
29  @Override
30  public int maxRepresentableDigits() {
31    return Integer.MAX_VALUE;
32  }
33
34  public DecimalQuantity_DualStorageBCD() {
35    setBcdToZero();
36    flags = 0;
37  }
38
39  public DecimalQuantity_DualStorageBCD(long input) {
40    setToLong(input);
41  }
42
43  public DecimalQuantity_DualStorageBCD(int input) {
44    setToInt(input);
45  }
46
47  public DecimalQuantity_DualStorageBCD(double input) {
48    setToDouble(input);
49  }
50
51  public DecimalQuantity_DualStorageBCD(BigInteger input) {
52    setToBigInteger(input);
53  }
54
55  public DecimalQuantity_DualStorageBCD(BigDecimal input) {
56    setToBigDecimal(input);
57  }
58
59  public DecimalQuantity_DualStorageBCD(DecimalQuantity_DualStorageBCD other) {
60    copyFrom(other);
61  }
62
63  public DecimalQuantity_DualStorageBCD(Number number) {
64    if (number instanceof Long) {
65      setToLong(number.longValue());
66    } else if (number instanceof Integer) {
67      setToInt(number.intValue());
68    } else if (number instanceof Double) {
69      setToDouble(number.doubleValue());
70    } else if (number instanceof BigInteger) {
71      setToBigInteger((BigInteger) number);
72    } else if (number instanceof BigDecimal) {
73      setToBigDecimal((BigDecimal) number);
74    } else if (number instanceof android.icu.math.BigDecimal) {
75      setToBigDecimal(((android.icu.math.BigDecimal) number).toBigDecimal());
76    } else {
77      throw new IllegalArgumentException(
78          "Number is of an unsupported type: " + number.getClass().getName());
79    }
80  }
81
82  @Override
83  public DecimalQuantity createCopy() {
84    return new DecimalQuantity_DualStorageBCD(this);
85  }
86
87  @Override
88  protected byte getDigitPos(int position) {
89    if (usingBytes) {
90      if (position < 0 || position > precision) return 0;
91      return bcdBytes[position];
92    } else {
93      if (position < 0 || position >= 16) return 0;
94      return (byte) ((bcdLong >>> (position * 4)) & 0xf);
95    }
96  }
97
98  @Override
99  protected void setDigitPos(int position, byte value) {
100    assert position >= 0;
101    if (usingBytes) {
102      ensureCapacity(position + 1);
103      bcdBytes[position] = value;
104    } else if (position >= 16) {
105      switchStorage();
106      ensureCapacity(position + 1);
107      bcdBytes[position] = value;
108    } else {
109      int shift = position * 4;
110      bcdLong = bcdLong & ~(0xfL << shift) | ((long) value << shift);
111    }
112  }
113
114  @Override
115  protected void shiftLeft(int numDigits) {
116    if (!usingBytes && precision + numDigits > 16) {
117      switchStorage();
118    }
119    if (usingBytes) {
120      ensureCapacity(precision + numDigits);
121      int i = precision + numDigits - 1;
122      for (; i >= numDigits; i--) {
123        bcdBytes[i] = bcdBytes[i - numDigits];
124      }
125      for (; i >= 0; i--) {
126        bcdBytes[i] = 0;
127      }
128    } else {
129      bcdLong <<= (numDigits * 4);
130    }
131    scale -= numDigits;
132    precision += numDigits;
133  }
134
135  @Override
136  protected void shiftRight(int numDigits) {
137    if (usingBytes) {
138      int i = 0;
139      for (; i < precision - numDigits; i++) {
140        bcdBytes[i] = bcdBytes[i + numDigits];
141      }
142      for (; i < precision; i++) {
143        bcdBytes[i] = 0;
144      }
145    } else {
146      bcdLong >>>= (numDigits * 4);
147    }
148    scale += numDigits;
149    precision -= numDigits;
150  }
151
152  @Override
153  protected void setBcdToZero() {
154    if (usingBytes) {
155        bcdBytes = null;
156        usingBytes = false;
157    }
158    bcdLong = 0L;
159    scale = 0;
160    precision = 0;
161    isApproximate = false;
162    origDouble = 0;
163    origDelta = 0;
164  }
165
166  @Override
167  protected void readIntToBcd(int n) {
168    assert n != 0;
169    // ints always fit inside the long implementation.
170    long result = 0L;
171    int i = 16;
172    for (; n != 0; n /= 10, i--) {
173      result = (result >>> 4) + (((long) n % 10) << 60);
174    }
175    assert !usingBytes;
176    bcdLong = result >>> (i * 4);
177    scale = 0;
178    precision = 16 - i;
179  }
180
181  @Override
182  protected void readLongToBcd(long n) {
183    assert n != 0;
184    if (n >= 10000000000000000L) {
185      ensureCapacity();
186      int i = 0;
187      for (; n != 0L; n /= 10L, i++) {
188        bcdBytes[i] = (byte) (n % 10);
189      }
190      assert usingBytes;
191      scale = 0;
192      precision = i;
193    } else {
194      long result = 0L;
195      int i = 16;
196      for (; n != 0L; n /= 10L, i--) {
197        result = (result >>> 4) + ((n % 10) << 60);
198      }
199      assert i >= 0;
200      assert !usingBytes;
201      bcdLong = result >>> (i * 4);
202      scale = 0;
203      precision = 16 - i;
204    }
205  }
206
207  @Override
208  protected void readBigIntegerToBcd(BigInteger n) {
209    assert n.signum() != 0;
210    ensureCapacity(); // allocate initial byte array
211    int i = 0;
212    for (; n.signum() != 0; i++) {
213      BigInteger[] temp = n.divideAndRemainder(BigInteger.TEN);
214      ensureCapacity(i + 1);
215      bcdBytes[i] = temp[1].byteValue();
216      n = temp[0];
217    }
218    scale = 0;
219    precision = i;
220  }
221
222  @Override
223  protected BigDecimal bcdToBigDecimal() {
224    if (usingBytes) {
225      // Converting to a string here is faster than doing BigInteger/BigDecimal arithmetic.
226      BigDecimal result = new BigDecimal(toNumberString());
227      if (isNegative()) {
228          result = result.negate();
229      }
230      return result;
231    } else {
232      long tempLong = 0L;
233      for (int shift = (precision - 1); shift >= 0; shift--) {
234        tempLong = tempLong * 10 + getDigitPos(shift);
235      }
236      BigDecimal result = BigDecimal.valueOf(tempLong);
237      result = result.scaleByPowerOfTen(scale);
238      if (isNegative()) result = result.negate();
239      return result;
240    }
241  }
242
243  @Override
244  protected void compact() {
245    if (usingBytes) {
246      int delta = 0;
247      for (; delta < precision && bcdBytes[delta] == 0; delta++) ;
248      if (delta == precision) {
249        // Number is zero
250        setBcdToZero();
251        return;
252      } else {
253        // Remove trailing zeros
254        shiftRight(delta);
255      }
256
257      // Compute precision
258      int leading = precision - 1;
259      for (; leading >= 0 && bcdBytes[leading] == 0; leading--) ;
260      precision = leading + 1;
261
262      // Switch storage mechanism if possible
263      if (precision <= 16) {
264        switchStorage();
265      }
266
267    } else {
268      if (bcdLong == 0L) {
269        // Number is zero
270        setBcdToZero();
271        return;
272      }
273
274      // Compact the number (remove trailing zeros)
275      int delta = Long.numberOfTrailingZeros(bcdLong) / 4;
276      bcdLong >>>= delta * 4;
277      scale += delta;
278
279      // Compute precision
280      precision = 16 - (Long.numberOfLeadingZeros(bcdLong) / 4);
281    }
282  }
283
284  /** Ensure that a byte array of at least 40 digits is allocated. */
285  private void ensureCapacity() {
286    ensureCapacity(40);
287  }
288
289  private void ensureCapacity(int capacity) {
290    if (capacity == 0) return;
291    int oldCapacity = usingBytes ? bcdBytes.length : 0;
292    if (!usingBytes) {
293      bcdBytes = new byte[capacity];
294    } else if (oldCapacity < capacity) {
295      byte[] bcd1 = new byte[capacity * 2];
296      System.arraycopy(bcdBytes, 0, bcd1, 0, oldCapacity);
297      bcdBytes = bcd1;
298    }
299    usingBytes = true;
300  }
301
302  /** Switches the internal storage mechanism between the 64-bit long and the byte array. */
303  private void switchStorage() {
304    if (usingBytes) {
305      // Change from bytes to long
306      bcdLong = 0L;
307      for (int i = precision - 1; i >= 0; i--) {
308        bcdLong <<= 4;
309        bcdLong |= bcdBytes[i];
310      }
311      bcdBytes = null;
312      usingBytes = false;
313    } else {
314      // Change from long to bytes
315      ensureCapacity();
316      for (int i = 0; i < precision; i++) {
317        bcdBytes[i] = (byte) (bcdLong & 0xf);
318        bcdLong >>>= 4;
319      }
320      assert usingBytes;
321    }
322  }
323
324  @Override
325  protected void copyBcdFrom(DecimalQuantity _other) {
326    DecimalQuantity_DualStorageBCD other = (DecimalQuantity_DualStorageBCD) _other;
327    setBcdToZero();
328    if (other.usingBytes) {
329      ensureCapacity(other.precision);
330      System.arraycopy(other.bcdBytes, 0, bcdBytes, 0, other.precision);
331    } else {
332      bcdLong = other.bcdLong;
333    }
334  }
335
336  /**
337   * Checks whether the bytes stored in this instance are all valid. For internal unit testing only.
338   *
339   * @return An error message if this instance is invalid, or null if this instance is healthy.
340   * @deprecated This API is for ICU internal use only.
341 * @hide draft / provisional / internal are hidden on Android
342   */
343  @Deprecated
344  public String checkHealth() {
345    if (usingBytes) {
346      if (bcdLong != 0) return "Value in bcdLong but we are in byte mode";
347      if (precision == 0) return "Zero precision but we are in byte mode";
348      if (precision > bcdBytes.length) return "Precision exceeds length of byte array";
349      if (getDigitPos(precision - 1) == 0) return "Most significant digit is zero in byte mode";
350      if (getDigitPos(0) == 0) return "Least significant digit is zero in long mode";
351      for (int i = 0; i < precision; i++) {
352        if (getDigitPos(i) >= 10) return "Digit exceeding 10 in byte array";
353        if (getDigitPos(i) < 0) return "Digit below 0 in byte array";
354      }
355      for (int i = precision; i < bcdBytes.length; i++) {
356        if (getDigitPos(i) != 0) return "Nonzero digits outside of range in byte array";
357      }
358    } else {
359      if (bcdBytes != null) {
360        for (int i = 0; i < bcdBytes.length; i++) {
361          if (bcdBytes[i] != 0) return "Nonzero digits in byte array but we are in long mode";
362        }
363      }
364      if (precision == 0 && bcdLong != 0) return "Value in bcdLong even though precision is zero";
365      if (precision > 16) return "Precision exceeds length of long";
366      if (precision != 0 && getDigitPos(precision - 1) == 0)
367        return "Most significant digit is zero in long mode";
368      if (precision != 0 && getDigitPos(0) == 0)
369        return "Least significant digit is zero in long mode";
370      for (int i = 0; i < precision; i++) {
371        if (getDigitPos(i) >= 10) return "Digit exceeding 10 in long";
372        if (getDigitPos(i) < 0) return "Digit below 0 in long (?!)";
373      }
374      for (int i = precision; i < 16; i++) {
375        if (getDigitPos(i) != 0) return "Nonzero digits outside of range in long";
376      }
377    }
378
379    return null;
380  }
381
382  /**
383   * Checks whether this {@link DecimalQuantity_DualStorageBCD} is using its internal byte array storage mechanism.
384   *
385   * @return true if an internal byte array is being used; false if a long is being used.
386   * @deprecated This API is ICU internal only.
387 * @hide draft / provisional / internal are hidden on Android
388   */
389  @Deprecated
390  public boolean isUsingBytes() {
391    return usingBytes;
392  }
393
394  @Override
395  public String toString() {
396    return String.format(
397        "<DecimalQuantity %s:%d:%d:%s %s %s>",
398        (lOptPos > 1000 ? "999" : String.valueOf(lOptPos)),
399        lReqPos,
400        rReqPos,
401        (rOptPos < -1000 ? "-999" : String.valueOf(rOptPos)),
402        (usingBytes ? "bytes" : "long"),
403        toNumberString());
404  }
405
406  public String toNumberString() {
407      StringBuilder sb = new StringBuilder();
408      if (usingBytes) {
409        for (int i = precision - 1; i >= 0; i--) {
410          sb.append(bcdBytes[i]);
411        }
412      } else {
413        sb.append(Long.toHexString(bcdLong));
414      }
415      sb.append("E");
416      sb.append(scale);
417      return sb.toString();
418  }
419}
420