1// © 2017 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3package com.ibm.icu.impl.number;
4
5import java.math.BigDecimal;
6import java.math.BigInteger;
7
8public final class DecimalQuantity_ByteArrayBCD extends DecimalQuantity_AbstractBCD {
9
10  /**
11   * The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map
12   * to one digit. For example, the number "12345" in BCD is "0x12345".
13   *
14   * <p>Whenever bcd changes internally, {@link #compact()} must be called, except in special cases
15   * like setting the digit to zero.
16   */
17  private byte[] bcd = new byte[100];
18
19  @Override
20  public int maxRepresentableDigits() {
21    return Integer.MAX_VALUE;
22  }
23
24  public DecimalQuantity_ByteArrayBCD(long input) {
25    setToLong(input);
26  }
27
28  public DecimalQuantity_ByteArrayBCD(int input) {
29    setToInt(input);
30  }
31
32  public DecimalQuantity_ByteArrayBCD(double input) {
33    setToDouble(input);
34  }
35
36  public DecimalQuantity_ByteArrayBCD(BigInteger input) {
37    setToBigInteger(input);
38  }
39
40  public DecimalQuantity_ByteArrayBCD(BigDecimal input) {
41    setToBigDecimal(input);
42  }
43
44  public DecimalQuantity_ByteArrayBCD(DecimalQuantity_ByteArrayBCD other) {
45    copyFrom(other);
46  }
47
48  @Override
49  public DecimalQuantity createCopy() {
50    return new DecimalQuantity_ByteArrayBCD(this);
51  }
52
53  @Override
54  protected byte getDigitPos(int position) {
55    if (position < 0 || position > precision) return 0;
56    return bcd[position];
57  }
58
59  @Override
60  protected void setDigitPos(int position, byte value) {
61    assert position >= 0;
62    ensureCapacity(position + 1);
63    bcd[position] = value;
64  }
65
66  @Override
67  protected void shiftLeft(int numDigits) {
68    ensureCapacity(precision + numDigits);
69    int i = precision + numDigits - 1;
70    for (; i >= numDigits; i--) {
71      bcd[i] = bcd[i - numDigits];
72    }
73    for (; i >= 0; i--) {
74      bcd[i] = 0;
75    }
76    scale -= numDigits;
77    precision += numDigits;
78  }
79
80  @Override
81  protected void shiftRight(int numDigits) {
82    int i = 0;
83    for (; i < precision - numDigits; i++) {
84      bcd[i] = bcd[i + numDigits];
85    }
86    for (; i < precision; i++) {
87      bcd[i] = 0;
88    }
89    scale += numDigits;
90    precision -= numDigits;
91  }
92
93  @Override
94  protected void setBcdToZero() {
95    for (int i = 0; i < precision; i++) {
96      bcd[i] = (byte) 0;
97    }
98    scale = 0;
99    precision = 0;
100    isApproximate = false;
101    origDouble = 0;
102    origDelta = 0;
103  }
104
105  @Override
106  protected void readIntToBcd(int n) {
107    assert n != 0;
108    int i = 0;
109    for (; n != 0L; n /= 10L, i++) {
110      bcd[i] = (byte) (n % 10);
111    }
112    scale = 0;
113    precision = i;
114  }
115
116  private static final byte[] LONG_MIN_VALUE =
117      new byte[] {8, 0, 8, 5, 7, 7, 4, 5, 8, 6, 3, 0, 2, 7, 3, 3, 2, 2, 9};
118
119  @Override
120  protected void readLongToBcd(long n) {
121    assert n != 0;
122    if (n == Long.MIN_VALUE) {
123      // Can't consume via the normal path.
124      System.arraycopy(LONG_MIN_VALUE, 0, bcd, 0, LONG_MIN_VALUE.length);
125      scale = 0;
126      precision = LONG_MIN_VALUE.length;
127      return;
128    }
129    int i = 0;
130    for (; n != 0L; n /= 10L, i++) {
131      bcd[i] = (byte) (n % 10);
132    }
133    scale = 0;
134    precision = i;
135  }
136
137  @Override
138  protected void readBigIntegerToBcd(BigInteger n) {
139    assert n.signum() != 0;
140    int i = 0;
141    for (; n.signum() != 0; i++) {
142      BigInteger[] temp = n.divideAndRemainder(BigInteger.TEN);
143      ensureCapacity(i + 1);
144      bcd[i] = temp[1].byteValue();
145      n = temp[0];
146    }
147    scale = 0;
148    precision = i;
149  }
150
151  @Override
152  protected BigDecimal bcdToBigDecimal() {
153    // Converting to a string here is faster than doing BigInteger/BigDecimal arithmetic.
154    return new BigDecimal(toDumbString());
155  }
156
157  private String toDumbString() {
158    StringBuilder sb = new StringBuilder();
159    if (isNegative()) sb.append('-');
160    if (precision == 0) {
161      sb.append('0');
162      return sb.toString();
163    }
164    for (int i = precision - 1; i >= 0; i--) {
165      sb.append(getDigitPos(i));
166    }
167    if (scale != 0) {
168      sb.append('E');
169      sb.append(scale);
170    }
171    return sb.toString();
172  }
173
174  @Override
175  protected void compact() {
176    // Special handling for 0
177    boolean isZero = true;
178    for (int i = 0; i < precision; i++) {
179      if (bcd[i] != 0) {
180        isZero = false;
181        break;
182      }
183    }
184    if (isZero) {
185      scale = 0;
186      precision = 0;
187      return;
188    }
189
190    // Compact the number (remove trailing zeros)
191    int delta = 0;
192    for (; bcd[delta] == 0; delta++) ;
193    shiftRight(delta);
194
195    // Compute precision
196    int leading = precision - 1;
197    for (; leading >= 0 && bcd[leading] == 0; leading--) ;
198    precision = leading + 1;
199  }
200
201  private void ensureCapacity(int capacity) {
202    if (bcd.length >= capacity) return;
203    byte[] bcd1 = new byte[capacity * 2];
204    System.arraycopy(bcd, 0, bcd1, 0, bcd.length);
205    bcd = bcd1;
206  }
207
208  @Override
209  protected void copyBcdFrom(DecimalQuantity _other) {
210    DecimalQuantity_ByteArrayBCD other = (DecimalQuantity_ByteArrayBCD) _other;
211    System.arraycopy(other.bcd, 0, bcd, 0, bcd.length);
212  }
213
214  @Override
215  public String toString() {
216    StringBuilder sb = new StringBuilder();
217    for (int i = 30; i >= 0; i--) {
218      sb.append(bcd[i]);
219    }
220    return String.format(
221        "<DecimalQuantity3 %s:%d:%d:%s %s%s%d>",
222        (lOptPos > 1000 ? "max" : String.valueOf(lOptPos)),
223        lReqPos,
224        rReqPos,
225        (rOptPos < -1000 ? "min" : String.valueOf(rOptPos)),
226        sb,
227        "E",
228        scale);
229  }
230}
231