precision(). Note that some call to the private
+ * method inplaceRound()
could update this field.
+ *
+ * @see #precision()
+ * @see #inplaceRound(TMathContext)
+ */
+ private transient int precision = 0;
+
+ private TBigDecimal(long smallValue, int scale) {
+ this.smallValue = smallValue;
+ this.scale = scale;
+ this.bitLength = bitLength(smallValue);
+ }
+
+ private TBigDecimal(int smallValue, int scale) {
+ this.smallValue = smallValue;
+ this.scale = scale;
+ this.bitLength = bitLength(smallValue);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a string representation
+ * given as a character array.
+ *
+ * @param in
+ * array of characters containing the string representation of
+ * this {@code BigDecimal}.
+ * @param offset
+ * first index to be copied.
+ * @param len
+ * number of characters to be used.
+ * @throws NullPointerException
+ * if {@code in == null}.
+ * @throws NumberFormatException
+ * if {@code offset < 0} or {@code len <= 0} or {@code
+ * offset+len-1 < 0} or {@code offset+len-1 >= in.length}.
+ * @throws NumberFormatException
+ * if in does not contain a valid string representation of a big
+ * decimal.
+ */
+ public TBigDecimal(char[] in, int offset, int len) {
+ int begin = offset; // first index to be copied
+ int last = offset + (len - 1); // last index to be copied
+ String scaleString = null; // buffer for scale
+ StringBuilder unscaledBuffer; // buffer for unscaled value
+ long newScale; // the new scale
+
+ if (in == null) {
+ throw new NullPointerException();
+ }
+ if ((last >= in.length) || (offset < 0) || (len <= 0) || (last < 0)) {
+ throw new NumberFormatException();
+ }
+ unscaledBuffer = new StringBuilder(len);
+ int bufLength = 0;
+ // To skip a possible '+' symbol
+ if ((offset <= last) && (in[offset] == '+')) {
+ offset++;
+ begin++;
+ }
+ int counter = 0;
+ boolean wasNonZero = false;
+ // Accumulating all digits until a possible decimal point
+ for (; (offset <= last) && (in[offset] != '.')
+ && (in[offset] != 'e') && (in[offset] != 'E'); offset++) {
+ if (!wasNonZero) {
+ if (in[offset] == '0') {
+ counter++;
+ } else {
+ wasNonZero = true;
+ }
+ }
+
+ }
+ unscaledBuffer.append(in, begin, offset - begin);
+ bufLength += offset - begin;
+ // A decimal point was found
+ if ((offset <= last) && (in[offset] == '.')) {
+ offset++;
+ // Accumulating all digits until a possible exponent
+ begin = offset;
+ for (; (offset <= last) && (in[offset] != 'e')
+ && (in[offset] != 'E'); offset++) {
+ if (!wasNonZero) {
+ if (in[offset] == '0') {
+ counter++;
+ } else {
+ wasNonZero = true;
+ }
+ }
+ }
+ scale = offset - begin;
+ bufLength +=scale;
+ unscaledBuffer.append(in, begin, scale);
+ } else {
+ scale = 0;
+ }
+ // An exponent was found
+ if ((offset <= last) && ((in[offset] == 'e') || (in[offset] == 'E'))) {
+ offset++;
+ // Checking for a possible sign of scale
+ begin = offset;
+ if ((offset <= last) && (in[offset] == '+')) {
+ offset++;
+ if ((offset <= last) && (in[offset] != '-')) {
+ begin++;
+ }
+ }
+ // Accumulating all remaining digits
+ scaleString = String.valueOf(in, begin, last + 1 - begin);
+ // Checking if the scale is defined
+ newScale = (long)scale - Integer.parseInt(scaleString);
+ scale = (int)newScale;
+ if (newScale != scale) {
+ throw new NumberFormatException("Scale out of range.");
+ }
+ }
+ // Parsing the unscaled value
+ if (bufLength < 19) {
+ smallValue = Long.parseLong(unscaledBuffer.toString());
+ bitLength = bitLength(smallValue);
+ } else {
+ setUnscaledValue(new TBigInteger(unscaledBuffer.toString()));
+ }
+ precision = unscaledBuffer.length() - counter;
+ if (unscaledBuffer.charAt(0) == '-') {
+ precision --;
+ }
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a string representation
+ * given as a character array.
+ *
+ * @param in
+ * array of characters containing the string representation of
+ * this {@code BigDecimal}.
+ * @param offset
+ * first index to be copied.
+ * @param len
+ * number of characters to be used.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws NullPointerException
+ * if {@code in == null}.
+ * @throws NumberFormatException
+ * if {@code offset < 0} or {@code len <= 0} or {@code
+ * offset+len-1 < 0} or {@code offset+len-1 >= in.length}.
+ * @throws NumberFormatException
+ * if {@code in} does not contain a valid string representation
+ * of a big decimal.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ */
+ public TBigDecimal(char[] in, int offset, int len, TMathContext mc) {
+ this(in, offset, len);
+ inplaceRound(mc);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a string representation
+ * given as a character array.
+ *
+ * @param in
+ * array of characters containing the string representation of
+ * this {@code BigDecimal}.
+ * @throws NullPointerException
+ * if {@code in == null}.
+ * @throws NumberFormatException
+ * if {@code in} does not contain a valid string representation
+ * of a big decimal.
+ */
+ public TBigDecimal(char[] in) {
+ this(in, 0, in.length);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a string representation
+ * given as a character array. The result is rounded according to the
+ * specified math context.
+ *
+ * @param in
+ * array of characters containing the string representation of
+ * this {@code BigDecimal}.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws NullPointerException
+ * if {@code in == null}.
+ * @throws NumberFormatException
+ * if {@code in} does not contain a valid string representation
+ * of a big decimal.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ */
+ public TBigDecimal(char[] in, TMathContext mc) {
+ this(in, 0, in.length);
+ inplaceRound(mc);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a string
+ * representation.
+ *
+ * @param val
+ * string containing the string representation of this {@code
+ * BigDecimal}.
+ * @throws NumberFormatException
+ * if {@code val} does not contain a valid string representation
+ * of a big decimal.
+ */
+ public TBigDecimal(String val) {
+ this(val.toCharArray(), 0, val.length());
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a string
+ * representation. The result is rounded according to the specified math
+ * context.
+ *
+ * @param val
+ * string containing the string representation of this {@code
+ * BigDecimal}.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws NumberFormatException
+ * if {@code val} does not contain a valid string representation
+ * of a big decimal.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ */
+ public TBigDecimal(String val, TMathContext mc) {
+ this(val.toCharArray(), 0, val.length());
+ inplaceRound(mc);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the 64bit double
+ * {@code val}. The constructed big decimal is equivalent to the given
+ * double. For example, {@code new BigDecimal(0.1)} is equal to {@code
+ * 0.1000000000000000055511151231257827021181583404541015625}. This happens
+ * as {@code 0.1} cannot be represented exactly in binary.
+ *
+ * To generate a big decimal instance which is equivalent to {@code 0.1} use
+ * the {@code BigDecimal(String)} constructor.
+ *
+ * @param val
+ * double value to be converted to a {@code BigDecimal} instance.
+ * @throws NumberFormatException
+ * if {@code val} is infinity or not a number.
+ */
+ public TBigDecimal(double val) {
+ if (Double.isInfinite(val) || Double.isNaN(val)) {
+ throw new NumberFormatException("Infinite or NaN");
+ }
+ long bits = Double.doubleToLongBits(val); // IEEE-754
+ long mantisa;
+ int trailingZeros;
+ // Extracting the exponent, note that the bias is 1023
+ scale = 1075 - (int)((bits >> 52) & 0x7FFL);
+ // Extracting the 52 bits of the mantisa.
+ mantisa = scale == 1075 ? (bits & 0xFFFFFFFFFFFFFL) << 1 : (bits & 0xFFFFFFFFFFFFFL) | 0x10000000000000L;
+ if (mantisa == 0) {
+ scale = 0;
+ precision = 1;
+ }
+ // To simplify all factors '2' in the mantisa
+ if (scale > 0) {
+ trailingZeros = Math.min(scale, Long.numberOfTrailingZeros(mantisa));
+ mantisa >>>= trailingZeros;
+ scale -= trailingZeros;
+ }
+ // Calculating the new unscaled value and the new scale
+ if((bits >> 63) != 0) {
+ mantisa = -mantisa;
+ }
+ int mantisaBits = bitLength(mantisa);
+ if (scale < 0) {
+ bitLength = mantisaBits == 0 ? 0 : mantisaBits - scale;
+ if(bitLength < 64) {
+ smallValue = mantisa << (-scale);
+ } else {
+ intVal = TBigInteger.valueOf(mantisa).shiftLeft(-scale);
+ }
+ scale = 0;
+ } else if (scale > 0) {
+ // m * 2^e = (m * 5^(-e)) * 10^e
+ if(scale < LONG_FIVE_POW.length && mantisaBits + LONG_FIVE_POW_BIT_LENGTH[scale] < 64) {
+ smallValue = mantisa * LONG_FIVE_POW[scale];
+ bitLength = bitLength(smallValue);
+ } else {
+ setUnscaledValue(TMultiplication.multiplyByFivePow(TBigInteger.valueOf(mantisa), scale));
+ }
+ } else { // scale == 0
+ smallValue = mantisa;
+ bitLength = mantisaBits;
+ }
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the 64bit double
+ * {@code val}. The constructed big decimal is equivalent to the given
+ * double. For example, {@code new BigDecimal(0.1)} is equal to {@code
+ * 0.1000000000000000055511151231257827021181583404541015625}. This happens
+ * as {@code 0.1} cannot be represented exactly in binary.
+ *
+ * To generate a big decimal instance which is equivalent to {@code 0.1} use
+ * the {@code BigDecimal(String)} constructor.
+ *
+ * @param val
+ * double value to be converted to a {@code BigDecimal} instance.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws NumberFormatException
+ * if {@code val} is infinity or not a number.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ */
+ public TBigDecimal(double val, TMathContext mc) {
+ this(val);
+ inplaceRound(mc);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the given big integer
+ * {@code val}. The scale of the result is {@code 0}.
+ *
+ * @param val
+ * {@code BigInteger} value to be converted to a {@code
+ * BigDecimal} instance.
+ */
+ public TBigDecimal(TBigInteger val) {
+ this(val, 0);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the given big integer
+ * {@code val}. The scale of the result is {@code 0}.
+ *
+ * @param val
+ * {@code BigInteger} value to be converted to a {@code
+ * BigDecimal} instance.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ */
+ public TBigDecimal(TBigInteger val, TMathContext mc) {
+ this(val);
+ inplaceRound(mc);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a given unscaled value
+ * {@code unscaledVal} and a given scale. The value of this instance is
+ * {@code unscaledVal} 10^(-{@code scale}).
+ *
+ * @param unscaledVal
+ * {@code BigInteger} representing the unscaled value of this
+ * {@code BigDecimal} instance.
+ * @param scale
+ * scale of this {@code BigDecimal} instance.
+ * @throws NullPointerException
+ * if {@code unscaledVal == null}.
+ */
+ public TBigDecimal(TBigInteger unscaledVal, int scale) {
+ if (unscaledVal == null) {
+ throw new NullPointerException();
+ }
+ this.scale = scale;
+ setUnscaledValue(unscaledVal);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from a given unscaled value
+ * {@code unscaledVal} and a given scale. The value of this instance is
+ * {@code unscaledVal} 10^(-{@code scale}). The result is rounded according
+ * to the specified math context.
+ *
+ * @param unscaledVal
+ * {@code BigInteger} representing the unscaled value of this
+ * {@code BigDecimal} instance.
+ * @param scale
+ * scale of this {@code BigDecimal} instance.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ * @throws NullPointerException
+ * if {@code unscaledVal == null}.
+ */
+ public TBigDecimal(TBigInteger unscaledVal, int scale, TMathContext mc) {
+ this(unscaledVal, scale);
+ inplaceRound(mc);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the given int
+ * {@code val}. The scale of the result is 0.
+ *
+ * @param val
+ * int value to be converted to a {@code BigDecimal} instance.
+ */
+ public TBigDecimal(int val) {
+ this(val,0);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the given int {@code
+ * val}. The scale of the result is {@code 0}. The result is rounded
+ * according to the specified math context.
+ *
+ * @param val
+ * int value to be converted to a {@code BigDecimal} instance.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code c.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ */
+ public TBigDecimal(int val, TMathContext mc) {
+ this(val,0);
+ inplaceRound(mc);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the given long {@code
+ * val}. The scale of the result is {@code 0}.
+ *
+ * @param val
+ * long value to be converted to a {@code BigDecimal} instance.
+ */
+ public TBigDecimal(long val) {
+ this(val,0);
+ }
+
+ /**
+ * Constructs a new {@code BigDecimal} instance from the given long {@code
+ * val}. The scale of the result is {@code 0}. The result is rounded
+ * according to the specified math context.
+ *
+ * @param val
+ * long value to be converted to a {@code BigDecimal} instance.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and the new big decimal cannot be represented
+ * within the given precision without rounding.
+ */
+ public TBigDecimal(long val, TMathContext mc) {
+ this(val);
+ inplaceRound(mc);
+ }
+
+ /* Public Methods */
+
+ /**
+ * Returns a new {@code BigDecimal} instance whose value is equal to {@code
+ * unscaledVal} 10^(-{@code scale}). The scale of the result is {@code
+ * scale}, and its unscaled value is {@code unscaledVal}.
+ *
+ * @param unscaledVal
+ * unscaled value to be used to construct the new {@code
+ * BigDecimal}.
+ * @param scale
+ * scale to be used to construct the new {@code BigDecimal}.
+ * @return {@code BigDecimal} instance with the value {@code unscaledVal}*
+ * 10^(-{@code unscaledVal}).
+ */
+ public static TBigDecimal valueOf(long unscaledVal, int scale) {
+ if (scale == 0) {
+ return valueOf(unscaledVal);
+ }
+ if ((unscaledVal == 0) && (scale >= 0)
+ && (scale < ZERO_SCALED_BY.length)) {
+ return ZERO_SCALED_BY[scale];
+ }
+ return new TBigDecimal(unscaledVal, scale);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance whose value is equal to {@code
+ * unscaledVal}. The scale of the result is {@code 0}, and its unscaled
+ * value is {@code unscaledVal}.
+ *
+ * @param unscaledVal
+ * value to be converted to a {@code BigDecimal}.
+ * @return {@code BigDecimal} instance with the value {@code unscaledVal}.
+ */
+ public static TBigDecimal valueOf(long unscaledVal) {
+ if ((unscaledVal >= 0) && (unscaledVal < BI_SCALED_BY_ZERO_LENGTH)) {
+ return BI_SCALED_BY_ZERO[(int)unscaledVal];
+ }
+ return new TBigDecimal(unscaledVal,0);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance whose value is equal to {@code
+ * val}. The new decimal is constructed as if the {@code BigDecimal(String)}
+ * constructor is called with an argument which is equal to {@code
+ * Double.toString(val)}. For example, {@code valueOf("0.1")} is converted to
+ * (unscaled=1, scale=1), although the double {@code 0.1} cannot be
+ * represented exactly as a double value. In contrast to that, a new {@code
+ * BigDecimal(0.1)} instance has the value {@code
+ * 0.1000000000000000055511151231257827021181583404541015625} with an
+ * unscaled value {@code 1000000000000000055511151231257827021181583404541015625}
+ * and the scale {@code 55}.
+ *
+ * @param val
+ * double value to be converted to a {@code BigDecimal}.
+ * @return {@code BigDecimal} instance with the value {@code val}.
+ * @throws NumberFormatException
+ * if {@code val} is infinite or {@code val} is not a number
+ */
+ public static TBigDecimal valueOf(double val) {
+ if (Double.isInfinite(val) || Double.isNaN(val)) {
+ throw new NumberFormatException("Infinity or NaN");
+ }
+ return new TBigDecimal(Double.toString(val));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this + augend}.
+ * The scale of the result is the maximum of the scales of the two
+ * arguments.
+ *
+ * @param augend
+ * value to be added to {@code this}.
+ * @return {@code this + augend}.
+ * @throws NullPointerException
+ * if {@code augend == null}.
+ */
+ public TBigDecimal add(TBigDecimal augend) {
+ int diffScale = this.scale - augend.scale;
+ // Fast return when some operand is zero
+ if (this.isZero()) {
+ if (diffScale <= 0) {
+ return augend;
+ }
+ if (augend.isZero()) {
+ return this;
+ }
+ } else if (augend.isZero()) {
+ if (diffScale >= 0) {
+ return this;
+ }
+ }
+ // Let be: this = [u1,s1] and augend = [u2,s2]
+ if (diffScale == 0) {
+ // case s1 == s2: [u1 + u2 , s1]
+ if (Math.max(this.bitLength, augend.bitLength) + 1 < 64) {
+ return valueOf(this.smallValue + augend.smallValue, this.scale);
+ }
+ return new TBigDecimal(this.getUnscaledValue().add(augend.getUnscaledValue()), this.scale);
+ } else if (diffScale > 0) {
+ // case s1 > s2 : [(u1 + u2) * 10 ^ (s1 - s2) , s1]
+ return addAndMult10(this, augend, diffScale);
+ } else {// case s2 > s1 : [(u2 + u1) * 10 ^ (s2 - s1) , s2]
+ return addAndMult10(augend, this, -diffScale);
+ }
+ }
+
+ private static TBigDecimal addAndMult10(TBigDecimal thisValue,TBigDecimal augend, int diffScale) {
+ if(diffScale < LONG_TEN_POW.length &&
+ Math.max(thisValue.bitLength, augend.bitLength + LONG_TEN_POW_BIT_LENGTH[diffScale]) + 1 < 64) {
+ return valueOf(thisValue.smallValue + augend.smallValue * LONG_TEN_POW[diffScale], thisValue.scale);
+ }
+ return new TBigDecimal(thisValue.getUnscaledValue().add(
+ TMultiplication.multiplyByTenPow(augend.getUnscaledValue(),diffScale)), thisValue.scale);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this + augend}.
+ * The result is rounded according to the passed context {@code mc}.
+ *
+ * @param augend
+ * value to be added to {@code this}.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code this + augend}.
+ * @throws NullPointerException
+ * if {@code augend == null} or {@code mc == null}.
+ */
+ public TBigDecimal add(TBigDecimal augend, TMathContext mc) {
+ TBigDecimal larger; // operand with the largest unscaled value
+ TBigDecimal smaller; // operand with the smallest unscaled value
+ TBigInteger tempBI;
+ long diffScale = (long)this.scale - augend.scale;
+ int largerSignum;
+ // Some operand is zero or the precision is infinity
+ if ((augend.isZero()) || (this.isZero()) || (mc.getPrecision() == 0)) {
+ return add(augend).round(mc);
+ }
+ // Cases where there is room for optimizations
+ if (this.aproxPrecision() < diffScale - 1) {
+ larger = augend;
+ smaller = this;
+ } else if (augend.aproxPrecision() < -diffScale - 1) {
+ larger = this;
+ smaller = augend;
+ } else {// No optimization is done
+ return add(augend).round(mc);
+ }
+ if (mc.getPrecision() >= larger.aproxPrecision()) {
+ // No optimization is done
+ return add(augend).round(mc);
+ }
+ // Cases where it's unnecessary to add two numbers with very different scales
+ largerSignum = larger.signum();
+ if (largerSignum == smaller.signum()) {
+ tempBI = TMultiplication.multiplyByPositiveInt(larger.getUnscaledValue(), 10)
+ .add(TBigInteger.valueOf(largerSignum));
+ } else {
+ tempBI = larger.getUnscaledValue().subtract(TBigInteger.valueOf(largerSignum));
+ tempBI = TMultiplication.multiplyByPositiveInt(tempBI, 10).add(TBigInteger.valueOf(largerSignum * 9));
+ }
+ // Rounding the improved adding
+ larger = new TBigDecimal(tempBI, larger.scale + 1);
+ return larger.round(mc);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this - subtrahend}.
+ * The scale of the result is the maximum of the scales of the two arguments.
+ *
+ * @param subtrahend
+ * value to be subtracted from {@code this}.
+ * @return {@code this - subtrahend}.
+ * @throws NullPointerException
+ * if {@code subtrahend == null}.
+ */
+ public TBigDecimal subtract(TBigDecimal subtrahend) {
+ int diffScale = this.scale - subtrahend.scale;
+ // Fast return when some operand is zero
+ if (this.isZero()) {
+ if (diffScale <= 0) {
+ return subtrahend.negate();
+ }
+ if (subtrahend.isZero()) {
+ return this;
+ }
+ } else if (subtrahend.isZero()) {
+ if (diffScale >= 0) {
+ return this;
+ }
+ }
+ // Let be: this = [u1,s1] and subtrahend = [u2,s2] so:
+ if (diffScale == 0) {
+ // case s1 = s2 : [u1 - u2 , s1]
+ if (Math.max(this.bitLength, subtrahend.bitLength) + 1 < 64) {
+ return valueOf(this.smallValue - subtrahend.smallValue,this.scale);
+ }
+ return new TBigDecimal(this.getUnscaledValue().subtract(subtrahend.getUnscaledValue()), this.scale);
+ } else if (diffScale > 0) {
+ // case s1 > s2 : [ u1 - u2 * 10 ^ (s1 - s2) , s1 ]
+ if(diffScale < LONG_TEN_POW.length &&
+ Math.max(this.bitLength, subtrahend.bitLength + LONG_TEN_POW_BIT_LENGTH[diffScale]) + 1 < 64) {
+ return valueOf(this.smallValue - subtrahend.smallValue * LONG_TEN_POW[diffScale], this.scale);
+ }
+ return new TBigDecimal(this.getUnscaledValue().subtract(
+ TMultiplication.multiplyByTenPow(subtrahend.getUnscaledValue(),diffScale)), this.scale);
+ } else {// case s2 > s1 : [ u1 * 10 ^ (s2 - s1) - u2 , s2 ]
+ diffScale = -diffScale;
+ if(diffScale < LONG_TEN_POW.length &&
+ Math.max(this.bitLength + LONG_TEN_POW_BIT_LENGTH[diffScale], subtrahend.bitLength) + 1 < 64) {
+ return valueOf(this.smallValue * LONG_TEN_POW[diffScale] - subtrahend.smallValue,subtrahend.scale);
+ }
+ return new TBigDecimal(TMultiplication.multiplyByTenPow(this.getUnscaledValue(), diffScale)
+ .subtract(subtrahend.getUnscaledValue()), subtrahend.scale);
+ }
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this - subtrahend}.
+ * The result is rounded according to the passed context {@code mc}.
+ *
+ * @param subtrahend
+ * value to be subtracted from {@code this}.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code this - subtrahend}.
+ * @throws NullPointerException
+ * if {@code subtrahend == null} or {@code mc == null}.
+ */
+ public TBigDecimal subtract(TBigDecimal subtrahend, TMathContext mc) {
+ long diffScale = subtrahend.scale - (long)this.scale;
+ int thisSignum;
+ TBigDecimal leftOperand; // it will be only the left operand (this)
+ TBigInteger tempBI;
+ // Some operand is zero or the precision is infinity
+ if (subtrahend.isZero() || isZero() || mc.getPrecision() == 0) {
+ return subtract(subtrahend).round(mc);
+ }
+ // Now: this != 0 and subtrahend != 0
+ if (subtrahend.aproxPrecision() < diffScale - 1) {
+ // Cases where it is unnecessary to subtract two numbers with very different scales
+ if (mc.getPrecision() < this.aproxPrecision()) {
+ thisSignum = this.signum();
+ if (thisSignum != subtrahend.signum()) {
+ tempBI = TMultiplication.multiplyByPositiveInt(this.getUnscaledValue(), 10)
+ .add(TBigInteger.valueOf(thisSignum));
+ } else {
+ tempBI = this.getUnscaledValue().subtract(TBigInteger.valueOf(thisSignum));
+ tempBI = TMultiplication.multiplyByPositiveInt(tempBI, 10)
+ .add(TBigInteger.valueOf(thisSignum * 9));
+ }
+ // Rounding the improved subtracting
+ leftOperand = new TBigDecimal(tempBI, this.scale + 1);
+ return leftOperand.round(mc);
+ }
+ }
+ // No optimization is done
+ return subtract(subtrahend).round(mc);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this *
+ * multiplicand}. The scale of the result is the sum of the scales of the
+ * two arguments.
+ *
+ * @param multiplicand
+ * value to be multiplied with {@code this}.
+ * @return {@code this * multiplicand}.
+ * @throws NullPointerException
+ * if {@code multiplicand == null}.
+ */
+ public TBigDecimal multiply(TBigDecimal multiplicand) {
+ long newScale = (long)this.scale + multiplicand.scale;
+
+ if (isZero() || multiplicand.isZero()) {
+ return zeroScaledBy(newScale);
+ }
+ /* Let be: this = [u1,s1] and multiplicand = [u2,s2] so:
+ * this x multiplicand = [ s1 * s2 , s1 + s2 ] */
+ if(this.bitLength + multiplicand.bitLength < 64) {
+ return valueOf(this.smallValue*multiplicand.smallValue, toIntScale(newScale));
+ }
+ return new TBigDecimal(this.getUnscaledValue().multiply(
+ multiplicand.getUnscaledValue()), toIntScale(newScale));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this *
+ * multiplicand}. The result is rounded according to the passed context
+ * {@code mc}.
+ *
+ * @param multiplicand
+ * value to be multiplied with {@code this}.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code this * multiplicand}.
+ * @throws NullPointerException
+ * if {@code multiplicand == null} or {@code mc == null}.
+ */
+ public TBigDecimal multiply(TBigDecimal multiplicand, TMathContext mc) {
+ TBigDecimal result = multiply(multiplicand);
+
+ result.inplaceRound(mc);
+ return result;
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
+ * As scale of the result the parameter {@code scale} is used. If rounding
+ * is required to meet the specified scale, then the specified rounding mode
+ * {@code roundingMode} is applied.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param scale
+ * the scale of the result returned.
+ * @param roundingMode
+ * rounding mode to be used to round the result.
+ * @return {@code this / divisor} rounded according to the given rounding
+ * mode.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws IllegalArgumentException
+ * if {@code roundingMode} is not a valid rounding mode.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
+ * necessary according to the given scale.
+ */
+ public TBigDecimal divide(TBigDecimal divisor, int scale, int roundingMode) {
+ return divide(divisor, scale, TRoundingMode.valueOf(roundingMode));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
+ * As scale of the result the parameter {@code scale} is used. If rounding
+ * is required to meet the specified scale, then the specified rounding mode
+ * {@code roundingMode} is applied.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param scale
+ * the scale of the result returned.
+ * @param roundingMode
+ * rounding mode to be used to round the result.
+ * @return {@code this / divisor} rounded according to the given rounding
+ * mode.
+ * @throws NullPointerException
+ * if {@code divisor == null} or {@code roundingMode == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if {@code roundingMode == RoundingMode.UNNECESSAR}Y and
+ * rounding is necessary according to the given scale and given
+ * precision.
+ */
+ public TBigDecimal divide(TBigDecimal divisor, int scale, TRoundingMode roundingMode) {
+ // Let be: this = [u1,s1] and divisor = [u2,s2]
+ if (roundingMode == null) {
+ throw new NullPointerException();
+ }
+ if (divisor.isZero()) {
+ throw new ArithmeticException("Division by zero");
+ }
+
+ long diffScale = ((long)this.scale - divisor.scale) - scale;
+ if(this.bitLength < 64 && divisor.bitLength < 64 ) {
+ if(diffScale == 0) {
+ return dividePrimitiveLongs(this.smallValue, divisor.smallValue, scale, roundingMode);
+ } else if(diffScale > 0) {
+ if(diffScale < LONG_TEN_POW.length &&
+ divisor.bitLength + LONG_TEN_POW_BIT_LENGTH[(int)diffScale] < 64) {
+ return dividePrimitiveLongs(this.smallValue,
+ divisor.smallValue*LONG_TEN_POW[(int)diffScale],
+ scale,
+ roundingMode);
+ }
+ } else { // diffScale < 0
+ if(-diffScale < LONG_TEN_POW.length &&
+ this.bitLength + LONG_TEN_POW_BIT_LENGTH[(int)-diffScale] < 64) {
+ return dividePrimitiveLongs(this.smallValue*LONG_TEN_POW[(int)-diffScale],
+ divisor.smallValue, scale, roundingMode);
+ }
+ }
+ }
+ TBigInteger scaledDividend = this.getUnscaledValue();
+ TBigInteger scaledDivisor = divisor.getUnscaledValue(); // for scaling of 'u2'
+
+ if (diffScale > 0) {
+ // Multiply 'u2' by: 10^((s1 - s2) - scale)
+ scaledDivisor = TMultiplication.multiplyByTenPow(scaledDivisor, (int)diffScale);
+ } else if (diffScale < 0) {
+ // Multiply 'u1' by: 10^(scale - (s1 - s2))
+ scaledDividend = TMultiplication.multiplyByTenPow(scaledDividend, (int)-diffScale);
+ }
+ return divideBigIntegers(scaledDividend, scaledDivisor, scale, roundingMode);
+ }
+
+ private static TBigDecimal divideBigIntegers(TBigInteger scaledDividend, TBigInteger scaledDivisor, int scale,
+ TRoundingMode roundingMode) {
+ TBigInteger[] quotAndRem = scaledDividend.divideAndRemainder(scaledDivisor); // quotient and remainder
+ // If after division there is a remainder...
+ TBigInteger quotient = quotAndRem[0];
+ TBigInteger remainder = quotAndRem[1];
+ if (remainder.signum() == 0) {
+ return new TBigDecimal(quotient, scale);
+ }
+ int sign = scaledDividend.signum() * scaledDivisor.signum();
+ int compRem; // 'compare to remainder'
+ if(scaledDivisor.bitLength() < 63) { // 63 in order to avoid out of long after <<1
+ long rem = remainder.longValue();
+ long divisor = scaledDivisor.longValue();
+ compRem = longCompareTo(Math.abs(rem) << 1,Math.abs(divisor));
+ // To look if there is a carry
+ compRem = roundingBehavior(quotient.testBit(0) ? 1 : 0,
+ sign * (5 + compRem), roundingMode);
+
+ } else {
+ // Checking if: remainder * 2 >= scaledDivisor
+ compRem = remainder.abs().shiftLeftOneBit().compareTo(scaledDivisor.abs());
+ compRem = roundingBehavior(quotient.testBit(0) ? 1 : 0, sign * (5 + compRem), roundingMode);
+ }
+ if (compRem != 0) {
+ if(quotient.bitLength() < 63) {
+ return valueOf(quotient.longValue() + compRem,scale);
+ }
+ quotient = quotient.add(TBigInteger.valueOf(compRem));
+ return new TBigDecimal(quotient, scale);
+ }
+ // Constructing the result with the appropriate unscaled value
+ return new TBigDecimal(quotient, scale);
+ }
+
+ private static TBigDecimal dividePrimitiveLongs(long scaledDividend, long scaledDivisor, int scale,
+ TRoundingMode roundingMode) {
+ long quotient = scaledDividend / scaledDivisor;
+ long remainder = scaledDividend % scaledDivisor;
+ int sign = Long.signum( scaledDividend ) * Long.signum(scaledDivisor);
+ if (remainder != 0) {
+ // Checking if: remainder * 2 >= scaledDivisor
+ int compRem; // 'compare to remainder'
+ compRem = longCompareTo(Math.abs(remainder) << 1, Math.abs(scaledDivisor));
+ // To look if there is a carry
+ quotient += roundingBehavior(((int)quotient) & 1, sign * (5 + compRem), roundingMode);
+ }
+ // Constructing the result with the appropriate unscaled value
+ return valueOf(quotient, scale);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
+ * The scale of the result is the scale of {@code this}. If rounding is
+ * required to meet the specified scale, then the specified rounding mode
+ * {@code roundingMode} is applied.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param roundingMode
+ * rounding mode to be used to round the result.
+ * @return {@code this / divisor} rounded according to the given rounding
+ * mode.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws IllegalArgumentException
+ * if {@code roundingMode} is not a valid rounding mode.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
+ * necessary according to the scale of this.
+ */
+ public TBigDecimal divide(TBigDecimal divisor, int roundingMode) {
+ return divide(divisor, scale, TRoundingMode.valueOf(roundingMode));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
+ * The scale of the result is the scale of {@code this}. If rounding is
+ * required to meet the specified scale, then the specified rounding mode
+ * {@code roundingMode} is applied.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param roundingMode
+ * rounding mode to be used to round the result.
+ * @return {@code this / divisor} rounded according to the given rounding
+ * mode.
+ * @throws NullPointerException
+ * if {@code divisor == null} or {@code roundingMode == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if {@code roundingMode == RoundingMode.UNNECESSARY} and
+ * rounding is necessary according to the scale of this.
+ */
+ public TBigDecimal divide(TBigDecimal divisor, TRoundingMode roundingMode) {
+ return divide(divisor, scale, roundingMode);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
+ * The scale of the result is the difference of the scales of {@code this}
+ * and {@code divisor}. If the exact result requires more digits, then the
+ * scale is adjusted accordingly. For example, {@code 1/128 = 0.0078125}
+ * which has a scale of {@code 7} and precision {@code 5}.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @return {@code this / divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if the result cannot be represented exactly.
+ */
+ public TBigDecimal divide(TBigDecimal divisor) {
+ TBigInteger p = this.getUnscaledValue();
+ TBigInteger q = divisor.getUnscaledValue();
+ TBigInteger gcd; // greatest common divisor between 'p' and 'q'
+ TBigInteger quotAndRem[];
+ long diffScale = (long)scale - divisor.scale;
+ int newScale; // the new scale for final quotient
+ int k; // number of factors "2" in 'q'
+ int l = 0; // number of factors "5" in 'q'
+ int i = 1;
+ int lastPow = FIVE_POW.length - 1;
+
+ if (divisor.isZero()) {
+ throw new ArithmeticException("Division by zero");
+ }
+ if (p.signum() == 0) {
+ return zeroScaledBy(diffScale);
+ }
+ // To divide both by the GCD
+ gcd = p.gcd(q);
+ p = p.divide(gcd);
+ q = q.divide(gcd);
+ // To simplify all "2" factors of q, dividing by 2^k
+ k = q.getLowestSetBit();
+ q = q.shiftRight(k);
+ // To simplify all "5" factors of q, dividing by 5^l
+ do {
+ quotAndRem = q.divideAndRemainder(FIVE_POW[i]);
+ if (quotAndRem[1].signum() == 0) {
+ l += i;
+ if (i < lastPow) {
+ i++;
+ }
+ q = quotAndRem[0];
+ } else {
+ if (i == 1) {
+ break;
+ }
+ i = 1;
+ }
+ } while (true);
+ // If abs(q) != 1 then the quotient is periodic
+ if (!q.abs().equals(TBigInteger.ONE)) {
+ throw new ArithmeticException("Non-terminating decimal expansion; no exact representable decimal result.");
+ }
+ // The sign of the is fixed and the quotient will be saved in 'p'
+ if (q.signum() < 0) {
+ p = p.negate();
+ }
+ // Checking if the new scale is out of range
+ newScale = toIntScale(diffScale + Math.max(k, l));
+ // k >= 0 and l >= 0 implies that k - l is in the 32-bit range
+ i = k - l;
+
+ p = (i > 0) ? TMultiplication.multiplyByFivePow(p, i)
+ : p.shiftLeft(-i);
+ return new TBigDecimal(p, newScale);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
+ * The result is rounded according to the passed context {@code mc}. If the
+ * passed math context specifies precision {@code 0}, then this call is
+ * equivalent to {@code this.divide(divisor)}.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code this / divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null} or {@code mc == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if {@code mc.getRoundingMode() == UNNECESSARY} and rounding
+ * is necessary according {@code mc.getPrecision()}.
+ */
+ public TBigDecimal divide(TBigDecimal divisor, TMathContext mc) {
+ /* Calculating how many zeros must be append to 'dividend'
+ * to obtain a quotient with at least 'mc.precision()' digits */
+ long traillingZeros = mc.getPrecision() + 2L + divisor.aproxPrecision() - aproxPrecision();
+ long diffScale = (long)scale - divisor.scale;
+ long newScale = diffScale; // scale of the final quotient
+ int compRem; // to compare the remainder
+ int i = 1; // index
+ int lastPow = TEN_POW.length - 1; // last power of ten
+ TBigInteger integerQuot; // for temporal results
+ TBigInteger quotAndRem[] = {getUnscaledValue()};
+ // In special cases it reduces the problem to call the dual method
+ if ((mc.getPrecision() == 0) || (this.isZero())
+ || (divisor.isZero())) {
+ return this.divide(divisor);
+ }
+ if (traillingZeros > 0) {
+ // To append trailing zeros at end of dividend
+ quotAndRem[0] = getUnscaledValue().multiply( TMultiplication.powerOf10(traillingZeros) );
+ newScale += traillingZeros;
+ }
+ quotAndRem = quotAndRem[0].divideAndRemainder( divisor.getUnscaledValue() );
+ integerQuot = quotAndRem[0];
+ // Calculating the exact quotient with at least 'mc.precision()' digits
+ if (quotAndRem[1].signum() != 0) {
+ // Checking if: 2 * remainder >= divisor ?
+ compRem = quotAndRem[1].shiftLeftOneBit().compareTo( divisor.getUnscaledValue() );
+ // quot := quot * 10 + r; with 'r' in {-6,-5,-4, 0,+4,+5,+6}
+ integerQuot = integerQuot.multiply(TBigInteger.TEN)
+ .add(TBigInteger.valueOf(quotAndRem[0].signum() * (5 + compRem)));
+ newScale++;
+ } else {
+ // To strip trailing zeros until the preferred scale is reached
+ while (!integerQuot.testBit(0)) {
+ quotAndRem = integerQuot.divideAndRemainder(TEN_POW[i]);
+ if ((quotAndRem[1].signum() == 0)
+ && (newScale - i >= diffScale)) {
+ newScale -= i;
+ if (i < lastPow) {
+ i++;
+ }
+ integerQuot = quotAndRem[0];
+ } else {
+ if (i == 1) {
+ break;
+ }
+ i = 1;
+ }
+ }
+ }
+ // To perform rounding
+ return new TBigDecimal(integerQuot, toIntScale(newScale), mc);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is the integral part of
+ * {@code this / divisor}. The quotient is rounded down towards zero to the
+ * next integer. For example, {@code 0.5/0.2 = 2}.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @return integral part of {@code this / divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ */
+ public TBigDecimal divideToIntegralValue(TBigDecimal divisor) {
+ TBigInteger integralValue; // the integer of result
+ TBigInteger powerOfTen; // some power of ten
+ TBigInteger quotAndRem[] = {getUnscaledValue()};
+ long newScale = (long)this.scale - divisor.scale;
+ long tempScale = 0;
+ int i = 1;
+ int lastPow = TEN_POW.length - 1;
+
+ if (divisor.isZero()) {
+ throw new ArithmeticException("Division by zero");
+ }
+ if ((divisor.aproxPrecision() + newScale > this.aproxPrecision() + 1L)
+ || (this.isZero())) {
+ /* If the divisor's integer part is greater than this's integer part,
+ * the result must be zero with the appropriate scale */
+ integralValue = TBigInteger.ZERO;
+ } else if (newScale == 0) {
+ integralValue = getUnscaledValue().divide( divisor.getUnscaledValue() );
+ } else if (newScale > 0) {
+ powerOfTen = TMultiplication.powerOf10(newScale);
+ integralValue = getUnscaledValue().divide( divisor.getUnscaledValue().multiply(powerOfTen) );
+ integralValue = integralValue.multiply(powerOfTen);
+ } else {// (newScale < 0)
+ powerOfTen = TMultiplication.powerOf10(-newScale);
+ integralValue = getUnscaledValue().multiply(powerOfTen).divide( divisor.getUnscaledValue() );
+ // To strip trailing zeros approximating to the preferred scale
+ while (!integralValue.testBit(0)) {
+ quotAndRem = integralValue.divideAndRemainder(TEN_POW[i]);
+ if ((quotAndRem[1].signum() == 0)
+ && (tempScale - i >= newScale)) {
+ tempScale -= i;
+ if (i < lastPow) {
+ i++;
+ }
+ integralValue = quotAndRem[0];
+ } else {
+ if (i == 1) {
+ break;
+ }
+ i = 1;
+ }
+ }
+ newScale = tempScale;
+ }
+ return ((integralValue.signum() == 0)
+ ? zeroScaledBy(newScale)
+ : new TBigDecimal(integralValue, toIntScale(newScale)));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is the integral part of
+ * {@code this / divisor}. The quotient is rounded down towards zero to the
+ * next integer. The rounding mode passed with the parameter {@code mc} is
+ * not considered. But if the precision of {@code mc > 0} and the integral
+ * part requires more digits, then an {@code ArithmeticException} is thrown.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param mc
+ * math context which determines the maximal precision of the
+ * result.
+ * @return integral part of {@code this / divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null} or {@code mc == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if {@code mc.getPrecision() > 0} and the result requires more
+ * digits to be represented.
+ */
+ public TBigDecimal divideToIntegralValue(TBigDecimal divisor, TMathContext mc) {
+ int mcPrecision = mc.getPrecision();
+ int diffPrecision = this.precision() - divisor.precision();
+ int lastPow = TEN_POW.length - 1;
+ long diffScale = (long)this.scale - divisor.scale;
+ long newScale = diffScale;
+ long quotPrecision = diffPrecision - diffScale + 1;
+ TBigInteger quotAndRem[] = new TBigInteger[2];
+ // In special cases it call the dual method
+ if ((mcPrecision == 0) || (this.isZero()) || (divisor.isZero())) {
+ return this.divideToIntegralValue(divisor);
+ }
+ // Let be: this = [u1,s1] and divisor = [u2,s2]
+ if (quotPrecision <= 0) {
+ quotAndRem[0] = TBigInteger.ZERO;
+ } else if (diffScale == 0) {
+ // CASE s1 == s2: to calculate u1 / u2
+ quotAndRem[0] = this.getUnscaledValue().divide( divisor.getUnscaledValue() );
+ } else if (diffScale > 0) {
+ // CASE s1 >= s2: to calculate u1 / (u2 * 10^(s1-s2)
+ quotAndRem[0] = this.getUnscaledValue().divide(
+ divisor.getUnscaledValue().multiply(TMultiplication.powerOf10(diffScale)) );
+ // To chose 10^newScale to get a quotient with at least 'mc.precision()' digits
+ newScale = Math.min(diffScale, Math.max(mcPrecision - quotPrecision + 1, 0));
+ // To calculate: (u1 / (u2 * 10^(s1-s2)) * 10^newScale
+ quotAndRem[0] = quotAndRem[0].multiply(TMultiplication.powerOf10(newScale));
+ } else {// CASE s2 > s1:
+ /* To calculate the minimum power of ten, such that the quotient
+ * (u1 * 10^exp) / u2 has at least 'mc.precision()' digits. */
+ long exp = Math.min(-diffScale, Math.max((long)mcPrecision - diffPrecision, 0));
+ long compRemDiv;
+ // Let be: (u1 * 10^exp) / u2 = [q,r]
+ quotAndRem = this.getUnscaledValue().multiply(TMultiplication.powerOf10(exp)).
+ divideAndRemainder(divisor.getUnscaledValue());
+ newScale += exp; // To fix the scale
+ exp = -newScale; // The remaining power of ten
+ // If after division there is a remainder...
+ if ((quotAndRem[1].signum() != 0) && (exp > 0)) {
+ // Log10(r) + ((s2 - s1) - exp) > mc.precision ?
+ compRemDiv = (new TBigDecimal(quotAndRem[1])).precision()
+ + exp - divisor.precision();
+ if (compRemDiv == 0) {
+ // To calculate: (r * 10^exp2) / u2
+ quotAndRem[1] = quotAndRem[1].multiply(TMultiplication.powerOf10(exp)).
+ divide(divisor.getUnscaledValue());
+ compRemDiv = Math.abs(quotAndRem[1].signum());
+ }
+ if (compRemDiv > 0) {
+ // The quotient won't fit in 'mc.precision()' digits
+ throw new ArithmeticException("Division impossible");
+ }
+ }
+ }
+ // Fast return if the quotient is zero
+ if (quotAndRem[0].signum() == 0) {
+ return zeroScaledBy(diffScale);
+ }
+ TBigInteger strippedBI = quotAndRem[0];
+ TBigDecimal integralValue = new TBigDecimal(quotAndRem[0]);
+ long resultPrecision = integralValue.precision();
+ int i = 1;
+ // To strip trailing zeros until the specified precision is reached
+ while (!strippedBI.testBit(0)) {
+ quotAndRem = strippedBI.divideAndRemainder(TEN_POW[i]);
+ if ((quotAndRem[1].signum() == 0) &&
+ ((resultPrecision - i >= mcPrecision)
+ || (newScale - i >= diffScale)) ) {
+ resultPrecision -= i;
+ newScale -= i;
+ if (i < lastPow) {
+ i++;
+ }
+ strippedBI = quotAndRem[0];
+ } else {
+ if (i == 1) {
+ break;
+ }
+ i = 1;
+ }
+ }
+ // To check if the result fit in 'mc.precision()' digits
+ if (resultPrecision > mcPrecision) {
+ throw new ArithmeticException("Division impossible");
+ }
+ integralValue.scale = toIntScale(newScale);
+ integralValue.setUnscaledValue(strippedBI);
+ return integralValue;
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this % divisor}.
+ *
+ * The remainder is defined as {@code this -
+ * this.divideToIntegralValue(divisor) * divisor}.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @return {@code this % divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ */
+ public TBigDecimal remainder(TBigDecimal divisor) {
+ return divideAndRemainder(divisor)[1];
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this % divisor}.
+ *
+ * The remainder is defined as {@code this -
+ * this.divideToIntegralValue(divisor) * divisor}.
+ *
+ * The specified rounding mode {@code mc} is used for the division only.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param mc
+ * rounding mode and precision to be used.
+ * @return {@code this % divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @throws ArithmeticException
+ * if {@code mc.getPrecision() > 0} and the result of {@code
+ * this.divideToIntegralValue(divisor, mc)} requires more digits
+ * to be represented.
+ */
+ public TBigDecimal remainder(TBigDecimal divisor, TMathContext mc) {
+ return divideAndRemainder(divisor, mc)[1];
+ }
+
+ /**
+ * Returns a {@code BigDecimal} array which contains the integral part of
+ * {@code this / divisor} at index 0 and the remainder {@code this %
+ * divisor} at index 1. The quotient is rounded down towards zero to the
+ * next integer.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @return {@code [this.divideToIntegralValue(divisor),
+ * this.remainder(divisor)]}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @see #divideToIntegralValue
+ * @see #remainder
+ */
+ public TBigDecimal[] divideAndRemainder(TBigDecimal divisor) {
+ TBigDecimal quotAndRem[] = new TBigDecimal[2];
+
+ quotAndRem[0] = this.divideToIntegralValue(divisor);
+ quotAndRem[1] = this.subtract( quotAndRem[0].multiply(divisor) );
+ return quotAndRem;
+ }
+
+ /**
+ * Returns a {@code BigDecimal} array which contains the integral part of
+ * {@code this / divisor} at index 0 and the remainder {@code this %
+ * divisor} at index 1. The quotient is rounded down towards zero to the
+ * next integer. The rounding mode passed with the parameter {@code mc} is
+ * not considered. But if the precision of {@code mc > 0} and the integral
+ * part requires more digits, then an {@code ArithmeticException} is thrown.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @param mc
+ * math context which determines the maximal precision of the
+ * result.
+ * @return {@code [this.divideToIntegralValue(divisor),
+ * this.remainder(divisor)]}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @see #divideToIntegralValue
+ * @see #remainder
+ */
+ public TBigDecimal[] divideAndRemainder(TBigDecimal divisor, TMathContext mc) {
+ TBigDecimal quotAndRem[] = new TBigDecimal[2];
+
+ quotAndRem[0] = this.divideToIntegralValue(divisor, mc);
+ quotAndRem[1] = this.subtract( quotAndRem[0].multiply(divisor) );
+ return quotAndRem;
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this ^ n}. The
+ * scale of the result is {@code n} times the scales of {@code this}.
+ *
+ * {@code x.pow(0)} returns {@code 1}, even if {@code x == 0}.
+ *
+ * Implementation Note: The implementation is based on the ANSI standard
+ * X3.274-1996 algorithm.
+ *
+ * @param n
+ * exponent to which {@code this} is raised.
+ * @return {@code this ^ n}.
+ * @throws ArithmeticException
+ * if {@code n < 0} or {@code n > 999999999}.
+ */
+ public TBigDecimal pow(int n) {
+ if (n == 0) {
+ return ONE;
+ }
+ if ((n < 0) || (n > 999999999)) {
+ throw new ArithmeticException("Invalid Operation");
+ }
+ long newScale = scale * (long)n;
+ // Let be: this = [u,s] so: this^n = [u^n, s*n]
+ return ((isZero())
+ ? zeroScaledBy(newScale)
+ : new TBigDecimal(getUnscaledValue().pow(n), toIntScale(newScale)));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this ^ n}. The
+ * result is rounded according to the passed context {@code mc}.
+ *
+ * Implementation Note: The implementation is based on the ANSI standard
+ * X3.274-1996 algorithm.
+ *
+ * @param n
+ * exponent to which {@code this} is raised.
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code this ^ n}.
+ * @throws ArithmeticException
+ * if {@code n < 0} or {@code n > 999999999}.
+ */
+ public TBigDecimal pow(int n, TMathContext mc) {
+ // The ANSI standard X3.274-1996 algorithm
+ int m = Math.abs(n);
+ int mcPrecision = mc.getPrecision();
+ int elength = (int)Math.log10(m) + 1; // decimal digits in 'n'
+ int oneBitMask; // mask of bits
+ TBigDecimal accum; // the single accumulator
+ TMathContext newPrecision = mc; // MathContext by default
+
+ // In particular cases, it reduces the problem to call the other 'pow()'
+ if ((n == 0) || ((isZero()) && (n > 0))) {
+ return pow(n);
+ }
+ if ((m > 999999999) || ((mcPrecision == 0) && (n < 0))
+ || ((mcPrecision > 0) && (elength > mcPrecision))) {
+ throw new ArithmeticException("Invalid Operation");
+ }
+ if (mcPrecision > 0) {
+ newPrecision = new TMathContext( mcPrecision + elength + 1,
+ mc.getRoundingMode());
+ }
+ // The result is calculated as if 'n' were positive
+ accum = round(newPrecision);
+ oneBitMask = Integer.highestOneBit(m) >> 1;
+
+ while (oneBitMask > 0) {
+ accum = accum.multiply(accum, newPrecision);
+ if ((m & oneBitMask) == oneBitMask) {
+ accum = accum.multiply(this, newPrecision);
+ }
+ oneBitMask >>= 1;
+ }
+ // If 'n' is negative, the value is divided into 'ONE'
+ if (n < 0) {
+ accum = ONE.divide(accum, newPrecision);
+ }
+ // The final value is rounded to the destination precision
+ accum.inplaceRound(mc);
+ return accum;
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is the absolute value of
+ * {@code this}. The scale of the result is the same as the scale of this.
+ *
+ * @return {@code abs(this)}
+ */
+ public TBigDecimal abs() {
+ return ((signum() < 0) ? negate() : this);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is the absolute value of
+ * {@code this}. The result is rounded according to the passed context
+ * {@code mc}.
+ *
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code abs(this)}
+ */
+ public TBigDecimal abs(TMathContext mc) {
+ return round(mc).abs();
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is the {@code -this}. The
+ * scale of the result is the same as the scale of this.
+ *
+ * @return {@code -this}
+ */
+ public TBigDecimal negate() {
+ if(bitLength < 63 || (bitLength == 63 && smallValue!=Long.MIN_VALUE)) {
+ return valueOf(-smallValue,scale);
+ }
+ return new TBigDecimal(getUnscaledValue().negate(), scale);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is the {@code -this}. The
+ * result is rounded according to the passed context {@code mc}.
+ *
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code -this}
+ */
+ public TBigDecimal negate(TMathContext mc) {
+ return round(mc).negate();
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code +this}. The scale
+ * of the result is the same as the scale of this.
+ *
+ * @return {@code this}
+ */
+ public TBigDecimal plus() {
+ return this;
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code +this}. The result
+ * is rounded according to the passed context {@code mc}.
+ *
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code this}, rounded
+ */
+ public TBigDecimal plus(TMathContext mc) {
+ return round(mc);
+ }
+
+ /**
+ * Returns the sign of this {@code BigDecimal}.
+ *
+ * @return {@code -1} if {@code this < 0},
+ * {@code 0} if {@code this == 0},
+ * {@code 1} if {@code this > 0}. */
+ public int signum() {
+ if( bitLength < 64) {
+ return Long.signum( this.smallValue );
+ }
+ return getUnscaledValue().signum();
+ }
+
+ private boolean isZero() {
+ //Watch out: -1 has a bitLength=0
+ return bitLength == 0 && this.smallValue != -1;
+ }
+
+ /**
+ * Returns the scale of this {@code BigDecimal}. The scale is the number of
+ * digits behind the decimal point. The value of this {@code BigDecimal} is
+ * the unsignedValue * 10^(-scale). If the scale is negative, then this
+ * {@code BigDecimal} represents a big integer.
+ *
+ * @return the scale of this {@code BigDecimal}.
+ */
+ public int scale() {
+ return scale;
+ }
+
+ /**
+ * Returns the precision of this {@code BigDecimal}. The precision is the
+ * number of decimal digits used to represent this decimal. It is equivalent
+ * to the number of digits of the unscaled value. The precision of {@code 0}
+ * is {@code 1} (independent of the scale).
+ *
+ * @return the precision of this {@code BigDecimal}.
+ */
+ public int precision() {
+ // Checking if the precision already was calculated
+ if (precision > 0) {
+ return precision;
+ }
+ int bitLength = this.bitLength;
+ int decimalDigits = 1; // the precision to be calculated
+ double doubleUnsc = 1; // intVal in 'double'
+
+ if (bitLength < 1024) {
+ // To calculate the precision for small numbers
+ if (bitLength >= 64) {
+ doubleUnsc = getUnscaledValue().doubleValue();
+ } else if (bitLength >= 1) {
+ doubleUnsc = smallValue;
+ }
+ decimalDigits += Math.log10(Math.abs(doubleUnsc));
+ } else {// (bitLength >= 1024)
+ /* To calculate the precision for large numbers
+ * Note that: 2 ^(bitlength() - 1) <= intVal < 10 ^(precision()) */
+ decimalDigits += (bitLength - 1) * LOG10_2;
+ // If after division the number isn't zero, exists an aditional digit
+ if (getUnscaledValue().divide(TMultiplication.powerOf10(decimalDigits)).signum() != 0) {
+ decimalDigits++;
+ }
+ }
+ precision = decimalDigits;
+ return precision;
+ }
+
+ /**
+ * Returns the unscaled value (mantissa) of this {@code BigDecimal} instance
+ * as a {@code BigInteger}. The unscaled value can be computed as {@code
+ * this} 10^(scale).
+ *
+ * @return unscaled value (this * 10^(scale)).
+ */
+ public TBigInteger unscaledValue() {
+ return getUnscaledValue();
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this}, rounded
+ * according to the passed context {@code mc}.
+ *
+ * If {@code mc.precision = 0}, then no rounding is performed.
+ *
+ * If {@code mc.precision > 0} and {@code mc.roundingMode == UNNECESSARY},
+ * then an {@code ArithmeticException} is thrown if the result cannot be
+ * represented exactly within the given precision.
+ *
+ * @param mc
+ * rounding mode and precision for the result of this operation.
+ * @return {@code this} rounded according to the passed context.
+ * @throws ArithmeticException
+ * if {@code mc.precision > 0} and {@code mc.roundingMode ==
+ * UNNECESSARY} and this cannot be represented within the given
+ * precision.
+ */
+ public TBigDecimal round(TMathContext mc) {
+ TBigDecimal thisBD = new TBigDecimal(getUnscaledValue(), scale);
+
+ thisBD.inplaceRound(mc);
+ return thisBD;
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance with the specified scale.
+ *
+ * If the new scale is greater than the old scale, then additional zeros are
+ * added to the unscaled value. In this case no rounding is necessary.
+ *
+ * If the new scale is smaller than the old scale, then trailing digits are
+ * removed. If these trailing digits are not zero, then the remaining
+ * unscaled value has to be rounded. For this rounding operation the
+ * specified rounding mode is used.
+ *
+ * @param newScale
+ * scale of the result returned.
+ * @param roundingMode
+ * rounding mode to be used to round the result.
+ * @return a new {@code BigDecimal} instance with the specified scale.
+ * @throws NullPointerException
+ * if {@code roundingMode == null}.
+ * @throws ArithmeticException
+ * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
+ * necessary according to the given scale.
+ */
+ public TBigDecimal setScale(int newScale, TRoundingMode roundingMode) {
+ if (roundingMode == null) {
+ throw new NullPointerException();
+ }
+ long diffScale = newScale - (long)scale;
+ // Let be: 'this' = [u,s]
+ if(diffScale == 0) {
+ return this;
+ }
+ if(diffScale > 0) {
+ // return [u * 10^(s2 - s), newScale]
+ if(diffScale < LONG_TEN_POW.length &&
+ (this.bitLength + LONG_TEN_POW_BIT_LENGTH[(int)diffScale]) < 64 ) {
+ return valueOf(this.smallValue*LONG_TEN_POW[(int)diffScale],newScale);
+ }
+ return new TBigDecimal(TMultiplication.multiplyByTenPow(getUnscaledValue(),(int)diffScale), newScale);
+ }
+ // diffScale < 0
+ // return [u,s] / [1,newScale] with the appropriate scale and rounding
+ if(this.bitLength < 64 && -diffScale < LONG_TEN_POW.length) {
+ return dividePrimitiveLongs(this.smallValue, LONG_TEN_POW[(int)-diffScale], newScale,roundingMode);
+ }
+ return divideBigIntegers(this.getUnscaledValue(),TMultiplication.powerOf10(-diffScale),newScale,roundingMode);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance with the specified scale.
+ *
+ * If the new scale is greater than the old scale, then additional zeros are
+ * added to the unscaled value. In this case no rounding is necessary.
+ *
+ * If the new scale is smaller than the old scale, then trailing digits are
+ * removed. If these trailing digits are not zero, then the remaining
+ * unscaled value has to be rounded. For this rounding operation the
+ * specified rounding mode is used.
+ *
+ * @param newScale
+ * scale of the result returned.
+ * @param roundingMode
+ * rounding mode to be used to round the result.
+ * @return a new {@code BigDecimal} instance with the specified scale.
+ * @throws IllegalArgumentException
+ * if {@code roundingMode} is not a valid rounding mode.
+ * @throws ArithmeticException
+ * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
+ * necessary according to the given scale.
+ */
+ public TBigDecimal setScale(int newScale, int roundingMode) {
+ return setScale(newScale, TRoundingMode.valueOf(roundingMode));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance with the specified scale. If
+ * the new scale is greater than the old scale, then additional zeros are
+ * added to the unscaled value. If the new scale is smaller than the old
+ * scale, then trailing zeros are removed. If the trailing digits are not
+ * zeros then an ArithmeticException is thrown.
+ *
+ * If no exception is thrown, then the following equation holds: {@code
+ * x.setScale(s).compareTo(x) == 0}.
+ *
+ * @param newScale
+ * scale of the result returned.
+ * @return a new {@code BigDecimal} instance with the specified scale.
+ * @throws ArithmeticException
+ * if rounding would be necessary.
+ */
+ public TBigDecimal setScale(int newScale) {
+ return setScale(newScale, TRoundingMode.UNNECESSARY);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance where the decimal point has
+ * been moved {@code n} places to the left. If {@code n < 0} then the
+ * decimal point is moved {@code -n} places to the right.
+ *
+ * The result is obtained by changing its scale. If the scale of the result
+ * becomes negative, then its precision is increased such that the scale is
+ * zero.
+ *
+ * Note, that {@code movePointLeft(0)} returns a result which is
+ * mathematically equivalent, but which has {@code scale >= 0}.
+ *
+ * @param n
+ * number of placed the decimal point has to be moved.
+ * @return {@code this * 10^(-n}).
+ */
+ public TBigDecimal movePointLeft(int n) {
+ return movePoint(scale + (long)n);
+ }
+
+ private TBigDecimal movePoint(long newScale) {
+ if (isZero()) {
+ return zeroScaledBy(Math.max(newScale, 0));
+ }
+ /* When: 'n'== Integer.MIN_VALUE isn't possible to call to movePointRight(-n)
+ * since -Integer.MIN_VALUE == Integer.MIN_VALUE */
+ if(newScale >= 0) {
+ if(bitLength < 64) {
+ return valueOf(smallValue,toIntScale(newScale));
+ }
+ return new TBigDecimal(getUnscaledValue(), toIntScale(newScale));
+ }
+ if(-newScale < LONG_TEN_POW.length &&
+ bitLength + LONG_TEN_POW_BIT_LENGTH[(int)-newScale] < 64 ) {
+ return valueOf(smallValue*LONG_TEN_POW[(int)-newScale],0);
+ }
+ return new TBigDecimal(TMultiplication.multiplyByTenPow(getUnscaledValue(),(int)-newScale), 0);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance where the decimal point has
+ * been moved {@code n} places to the right. If {@code n < 0} then the
+ * decimal point is moved {@code -n} places to the left.
+ *
+ * The result is obtained by changing its scale. If the scale of the result
+ * becomes negative, then its precision is increased such that the scale is
+ * zero.
+ *
+ * Note, that {@code movePointRight(0)} returns a result which is
+ * mathematically equivalent, but which has scale >= 0.
+ *
+ * @param n
+ * number of placed the decimal point has to be moved.
+ * @return {@code this * 10^n}.
+ */
+ public TBigDecimal movePointRight(int n) {
+ return movePoint(scale - (long)n);
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} whose value is {@code this} 10^{@code n}.
+ * The scale of the result is {@code this.scale()} - {@code n}.
+ * The precision of the result is the precision of {@code this}.
+ *
+ * This method has the same effect as {@link #movePointRight}, except that
+ * the precision is not changed.
+ *
+ * @param n
+ * number of places the decimal point has to be moved.
+ * @return {@code this * 10^n}
+ */
+ public TBigDecimal scaleByPowerOfTen(int n) {
+ long newScale = scale - (long)n;
+ if(bitLength < 64) {
+ //Taking care when a 0 is to be scaled
+ if( smallValue==0 ){
+ return zeroScaledBy( newScale );
+ }
+ return valueOf(smallValue,toIntScale(newScale));
+ }
+ return new TBigDecimal(getUnscaledValue(), toIntScale(newScale));
+ }
+
+ /**
+ * Returns a new {@code BigDecimal} instance with the same value as {@code
+ * this} but with a unscaled value where the trailing zeros have been
+ * removed. If the unscaled value of {@code this} has n trailing zeros, then
+ * the scale and the precision of the result has been reduced by n.
+ *
+ * @return a new {@code BigDecimal} instance equivalent to this where the
+ * trailing zeros of the unscaled value have been removed.
+ */
+ public TBigDecimal stripTrailingZeros() {
+ int i = 1; // 1 <= i <= 18
+ int lastPow = TEN_POW.length - 1;
+ long newScale = scale;
+
+ if (isZero()) {
+ return new TBigDecimal("0");
+ }
+ TBigInteger strippedBI = getUnscaledValue();
+ TBigInteger[] quotAndRem;
+
+ // while the number is even...
+ while (!strippedBI.testBit(0)) {
+ // To divide by 10^i
+ quotAndRem = strippedBI.divideAndRemainder(TEN_POW[i]);
+ // To look the remainder
+ if (quotAndRem[1].signum() == 0) {
+ // To adjust the scale
+ newScale -= i;
+ if (i < lastPow) {
+ // To set to the next power
+ i++;
+ }
+ strippedBI = quotAndRem[0];
+ } else {
+ if (i == 1) {
+ // 'this' has no more trailing zeros
+ break;
+ }
+ // To set to the smallest power of ten
+ i = 1;
+ }
+ }
+ return new TBigDecimal(strippedBI, toIntScale(newScale));
+ }
+
+ /**
+ * Compares this {@code BigDecimal} with {@code val}. Returns one of the
+ * three values {@code 1}, {@code 0}, or {@code -1}. The method behaves as
+ * if {@code this.subtract(val)} is computed. If this difference is > 0 then
+ * 1 is returned, if the difference is < 0 then -1 is returned, and if the
+ * difference is 0 then 0 is returned. This means, that if two decimal
+ * instances are compared which are equal in value but differ in scale, then
+ * these two instances are considered as equal.
+ *
+ * @param val
+ * value to be compared with {@code this}.
+ * @return {@code 1} if {@code this > val}, {@code -1} if {@code this < val},
+ * {@code 0} if {@code this == val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ @Override
+ public int compareTo(TBigDecimal val) {
+ int thisSign = signum();
+ int valueSign = val.signum();
+
+ if( thisSign == valueSign) {
+ if(this.scale == val.scale && this.bitLength<64 && val.bitLength<64 ) {
+ return (smallValue < val.smallValue) ? -1 : (smallValue > val.smallValue) ? 1 : 0;
+ }
+ long diffScale = (long)this.scale - val.scale;
+ int diffPrecision = this.aproxPrecision() - val.aproxPrecision();
+ if (diffPrecision > diffScale + 1) {
+ return thisSign;
+ } else if (diffPrecision < diffScale - 1) {
+ return -thisSign;
+ } else {// thisSign == val.signum() and diffPrecision is aprox. diffScale
+ TBigInteger thisUnscaled = this.getUnscaledValue();
+ TBigInteger valUnscaled = val.getUnscaledValue();
+ // If any of both precision is bigger, append zeros to the shorter one
+ if (diffScale < 0) {
+ thisUnscaled = thisUnscaled.multiply(TMultiplication.powerOf10(-diffScale));
+ } else if (diffScale > 0) {
+ valUnscaled = valUnscaled.multiply(TMultiplication.powerOf10(diffScale));
+ }
+ return thisUnscaled.compareTo(valUnscaled);
+ }
+ } else if (thisSign < valueSign) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Returns {@code true} if {@code x} is a {@code BigDecimal} instance and if
+ * this instance is equal to this big decimal. Two big decimals are equal if
+ * their unscaled value and their scale is equal. For example, 1.0
+ * (10*10^(-1)) is not equal to 1.00 (100*10^(-2)). Similarly, zero
+ * instances are not equal if their scale differs.
+ *
+ * @param x
+ * object to be compared with {@code this}.
+ * @return true if {@code x} is a {@code BigDecimal} and {@code this == x}.
+ */
+ @Override
+ public boolean equals(Object x) {
+ if (this == x) {
+ return true;
+ }
+ if (x instanceof TBigDecimal) {
+ TBigDecimal x1 = (TBigDecimal) x;
+ return x1.scale == scale
+ && (bitLength < 64 ? (x1.smallValue == smallValue)
+ : intVal.equals(x1.intVal));
+
+
+ }
+ return false;
+ }
+
+ /**
+ * Returns the minimum of this {@code BigDecimal} and {@code val}.
+ *
+ * @param val
+ * value to be used to compute the minimum with this.
+ * @return {@code min(this, val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigDecimal min(TBigDecimal val) {
+ return ((compareTo(val) <= 0) ? this : val);
+ }
+
+ /**
+ * Returns the maximum of this {@code BigDecimal} and {@code val}.
+ *
+ * @param val
+ * value to be used to compute the maximum with this.
+ * @return {@code max(this, val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigDecimal max(TBigDecimal val) {
+ return ((compareTo(val) >= 0) ? this : val);
+ }
+
+ /**
+ * Returns a hash code for this {@code BigDecimal}.
+ *
+ * @return hash code for {@code this}.
+ */
+ @Override
+ public int hashCode() {
+ if (hashCode != 0) {
+ return hashCode;
+ }
+ if (bitLength < 64) {
+ hashCode = (int)(smallValue & 0xffffffff);
+ hashCode = 33 * hashCode + (int)((smallValue >> 32) & 0xffffffff);
+ hashCode = 17 * hashCode + scale;
+ return hashCode;
+ }
+ hashCode = 17 * intVal.hashCode() + scale;
+ return hashCode;
+ }
+
+ /**
+ * Returns a canonical string representation of this {@code BigDecimal}. If
+ * necessary, scientific notation is used. This representation always prints
+ * all significant digits of this value.
+ *
+ * If the scale is negative or if {@code scale - precision >= 6} then
+ * scientific notation is used.
+ *
+ * @return a string representation of {@code this} in scientific notation if
+ * necessary.
+ */
+ @Override
+ public String toString() {
+ if (toStringImage != null) {
+ return toStringImage;
+ }
+ if (bitLength < 32) {
+ toStringImage = TConversion.toDecimalScaledString(smallValue,scale);
+ return toStringImage;
+ }
+ String intString = getUnscaledValue().toString();
+ if (scale == 0) {
+ return intString;
+ }
+ int begin = (getUnscaledValue().signum() < 0) ? 2 : 1;
+ int end = intString.length();
+ long exponent = -(long)scale + end - begin;
+ StringBuilder result = new StringBuilder();
+
+ result.append(intString);
+ if ((scale > 0) && (exponent >= -6)) {
+ if (exponent >= 0) {
+ result.insert(end - scale, '.');
+ } else {
+ result.insert(begin - 1, "0.");
+ result.insert(begin + 1, CH_ZEROS, 0, -(int)exponent - 1);
+ }
+ } else {
+ if (end - begin >= 1) {
+ result.insert(begin, '.');
+ end++;
+ }
+ result.insert(end, 'E');
+ if (exponent > 0) {
+ result.insert(++end, '+');
+ }
+ result.insert(++end, Long.toString(exponent));
+ }
+ toStringImage = result.toString();
+ return toStringImage;
+ }
+
+ /**
+ * Returns a string representation of this {@code BigDecimal}. This
+ * representation always prints all significant digits of this value.
+ *
+ * If the scale is negative or if {@code scale - precision >= 6} then
+ * engineering notation is used. Engineering notation is similar to the
+ * scientific notation except that the exponent is made to be a multiple of
+ * 3 such that the integer part is >= 1 and < 1000.
+ *
+ * @return a string representation of {@code this} in engineering notation
+ * if necessary.
+ */
+ public String toEngineeringString() {
+ String intString = getUnscaledValue().toString();
+ if (scale == 0) {
+ return intString;
+ }
+ int begin = (getUnscaledValue().signum() < 0) ? 2 : 1;
+ int end = intString.length();
+ long exponent = -(long)scale + end - begin;
+ StringBuilder result = new StringBuilder(intString);
+
+ if ((scale > 0) && (exponent >= -6)) {
+ if (exponent >= 0) {
+ result.insert(end - scale, '.');
+ } else {
+ result.insert(begin - 1, "0."); //$NON-NLS-1$
+ result.insert(begin + 1, CH_ZEROS, 0, -(int)exponent - 1);
+ }
+ } else {
+ int delta = end - begin;
+ int rem = (int)(exponent % 3);
+
+ if (rem != 0) {
+ // adjust exponent so it is a multiple of three
+ if (getUnscaledValue().signum() == 0) {
+ // zero value
+ rem = (rem < 0) ? -rem : 3 - rem;
+ exponent += rem;
+ } else {
+ // nonzero value
+ rem = (rem < 0) ? rem + 3 : rem;
+ exponent -= rem;
+ begin += rem;
+ }
+ if (delta < 3) {
+ for (int i = rem - delta; i > 0; i--) {
+ result.insert(end++, '0');
+ }
+ }
+ }
+ if (end - begin >= 1) {
+ result.insert(begin, '.');
+ end++;
+ }
+ if (exponent != 0) {
+ result.insert(end, 'E');
+ if (exponent > 0) {
+ result.insert(++end, '+');
+ }
+ result.insert(++end, Long.toString(exponent));
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns a string representation of this {@code BigDecimal}. No scientific
+ * notation is used. This methods adds zeros where necessary.
+ *
+ * If this string representation is used to create a new instance, this
+ * instance is generally not identical to {@code this} as the precision
+ * changes.
+ *
+ * {@code x.equals(new BigDecimal(x.toPlainString())} usually returns
+ * {@code false}.
+ *
+ * {@code x.compareTo(new BigDecimal(x.toPlainString())} returns {@code 0}.
+ *
+ * @return a string representation of {@code this} without exponent part.
+ */
+ public String toPlainString() {
+ String intStr = getUnscaledValue().toString();
+ if ((scale == 0) || ((isZero()) && (scale < 0))) {
+ return intStr;
+ }
+ int begin = (signum() < 0) ? 1 : 0;
+ int delta = scale;
+ // We take space for all digits, plus a possible decimal point, plus 'scale'
+ StringBuilder result = new StringBuilder(intStr.length() + 1 + Math.abs(scale));
+
+ if (begin == 1) {
+ // If the number is negative, we insert a '-' character at front
+ result.append('-');
+ }
+ if (scale > 0) {
+ delta -= (intStr.length() - begin);
+ if (delta >= 0) {
+ result.append("0."); //$NON-NLS-1$
+ // To append zeros after the decimal point
+ for (; delta > CH_ZEROS.length; delta -= CH_ZEROS.length) {
+ result.append(CH_ZEROS);
+ }
+ result.append(CH_ZEROS, 0, delta);
+ result.append(intStr.substring(begin));
+ } else {
+ delta = begin - delta;
+ result.append(intStr.substring(begin, delta));
+ result.append('.');
+ result.append(intStr.substring(delta));
+ }
+ } else {// (scale <= 0)
+ result.append(intStr.substring(begin));
+ // To append trailing zeros
+ for (; delta < -CH_ZEROS.length; delta += CH_ZEROS.length) {
+ result.append(CH_ZEROS);
+ }
+ result.append(CH_ZEROS, 0, -delta);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a big integer instance. A fractional
+ * part is discarded.
+ *
+ * @return this {@code BigDecimal} as a big integer instance.
+ */
+ public TBigInteger toBigInteger() {
+ if ((scale == 0) || (isZero())) {
+ return getUnscaledValue();
+ } else if (scale < 0) {
+ return getUnscaledValue().multiply(TMultiplication.powerOf10(-(long)scale));
+ } else {// (scale > 0)
+ return getUnscaledValue().divide(TMultiplication.powerOf10(scale));
+ }
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a big integer instance if it has no
+ * fractional part. If this {@code BigDecimal} has a fractional part, i.e.
+ * if rounding would be necessary, an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code BigDecimal} as a big integer value.
+ * @throws ArithmeticException
+ * if rounding is necessary.
+ */
+ public TBigInteger toBigIntegerExact() {
+ if ((scale == 0) || (isZero())) {
+ return getUnscaledValue();
+ } else if (scale < 0) {
+ return getUnscaledValue().multiply(TMultiplication.powerOf10(-(long)scale));
+ } else {// (scale > 0)
+ TBigInteger[] integerAndFraction;
+ // An optimization before do a heavy division
+ if ((scale > aproxPrecision()) || (scale > getUnscaledValue().getLowestSetBit())) {
+ throw new ArithmeticException("Rounding necessary");
+ }
+ integerAndFraction = getUnscaledValue().divideAndRemainder(TMultiplication.powerOf10(scale));
+ if (integerAndFraction[1].signum() != 0) {
+ // It exists a non-zero fractional part
+ throw new ArithmeticException("Rounding necessary");
+ }
+ return integerAndFraction[0];
+ }
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as an long value. Any fractional part is
+ * discarded. If the integral part of {@code this} is too big to be
+ * represented as an long, then {@code this} % 2^64 is returned.
+ *
+ * @return this {@code BigDecimal} as a long value.
+ */
+ @Override
+ public long longValue() {
+ /* If scale <= -64 there are at least 64 trailing bits zero in 10^(-scale).
+ * If the scale is positive and very large the long value could be zero. */
+ return ((scale <= -64) || (scale > aproxPrecision())
+ ? 0L
+ : toBigInteger().longValue());
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a long value if it has no fractional
+ * part and if its value fits to the int range ([-2^{63}..2^{63}-1]). If
+ * these conditions are not met, an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code BigDecimal} as a long value.
+ * @throws ArithmeticException
+ * if rounding is necessary or the number doesn't fit in a long.
+ */
+ public long longValueExact() {
+ return valueExact(64);
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as an int value. Any fractional part is
+ * discarded. If the integral part of {@code this} is too big to be
+ * represented as an int, then {@code this} % 2^32 is returned.
+ *
+ * @return this {@code BigDecimal} as a int value.
+ */
+ @Override
+ public int intValue() {
+ /* If scale <= -32 there are at least 32 trailing bits zero in 10^(-scale).
+ * If the scale is positive and very large the long value could be zero. */
+ return ((scale <= -32) || (scale > aproxPrecision())
+ ? 0
+ : toBigInteger().intValue());
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a int value if it has no fractional
+ * part and if its value fits to the int range ([-2^{31}..2^{31}-1]). If
+ * these conditions are not met, an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code BigDecimal} as a int value.
+ * @throws ArithmeticException
+ * if rounding is necessary or the number doesn't fit in a int.
+ */
+ public int intValueExact() {
+ return (int)valueExact(32);
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a short value if it has no fractional
+ * part and if its value fits to the short range ([-2^{15}..2^{15}-1]). If
+ * these conditions are not met, an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code BigDecimal} as a short value.
+ * @throws ArithmeticException
+ * if rounding is necessary of the number doesn't fit in a
+ * short.
+ */
+ public short shortValueExact() {
+ return (short)valueExact(16);
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a byte value if it has no fractional
+ * part and if its value fits to the byte range ([-128..127]). If these
+ * conditions are not met, an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code BigDecimal} as a byte value.
+ * @throws ArithmeticException
+ * if rounding is necessary or the number doesn't fit in a byte.
+ */
+ public byte byteValueExact() {
+ return (byte)valueExact(8);
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a float value. If {@code this} is too
+ * big to be represented as an float, then {@code Float.POSITIVE_INFINITY}
+ * or {@code Float.NEGATIVE_INFINITY} is returned.
+ *
+ * Note, that if the unscaled value has more than 24 significant digits,
+ * then this decimal cannot be represented exactly in a float variable. In
+ * this case the result is rounded.
+ *
+ * For example, if the instance {@code x1 = new BigDecimal("0.1")} cannot be
+ * represented exactly as a float, and thus {@code x1.equals(new
+ * BigDecimal(x1.folatValue())} returns {@code false} for this case.
+ *
+ * Similarly, if the instance {@code new BigDecimal(16777217)} is converted
+ * to a float, the result is {@code 1.6777216E}7.
+ *
+ * @return this {@code BigDecimal} as a float value.
+ */
+ @Override
+ public float floatValue() {
+ /* A similar code like in doubleValue() could be repeated here,
+ * but this simple implementation is quite efficient. */
+ float floatResult = signum();
+ long powerOfTwo = this.bitLength - (long)(scale / LOG10_2);
+ if ((powerOfTwo < -149) || (floatResult == 0.0f)) {
+ // Cases which 'this' is very small
+ floatResult *= 0.0f;
+ } else if (powerOfTwo > 129) {
+ // Cases which 'this' is very large
+ floatResult *= Float.POSITIVE_INFINITY;
+ } else {
+ floatResult = (float)doubleValue();
+ }
+ return floatResult;
+ }
+
+ /**
+ * Returns this {@code BigDecimal} as a double value. If {@code this} is too
+ * big to be represented as an float, then {@code Double.POSITIVE_INFINITY}
+ * or {@code Double.NEGATIVE_INFINITY} is returned.
+ *
+ * Note, that if the unscaled value has more than 53 significant digits,
+ * then this decimal cannot be represented exactly in a double variable. In
+ * this case the result is rounded.
+ *
+ * For example, if the instance {@code x1 = new BigDecimal("0.1")} cannot be
+ * represented exactly as a double, and thus {@code x1.equals(new
+ * BigDecimal(x1.doubleValue())} returns {@code false} for this case.
+ *
+ * Similarly, if the instance {@code new BigDecimal(9007199254740993L)} is
+ * converted to a double, the result is {@code 9.007199254740992E15}.
+ *
+ *
+ * @return this {@code BigDecimal} as a double value.
+ */
+ @Override
+ public double doubleValue() {
+ int sign = signum();
+ int exponent = 1076; // bias + 53
+ int lowestSetBit;
+ int discardedSize;
+ long powerOfTwo = this.bitLength - (long)(scale / LOG10_2);
+ long bits; // IEEE-754 Standard
+ long tempBits; // for temporal calculations
+ TBigInteger mantisa;
+
+ if ((powerOfTwo < -1074) || (sign == 0)) {
+ // Cases which 'this' is very small
+ return (sign * 0.0d);
+ } else if (powerOfTwo > 1025) {
+ // Cases which 'this' is very large
+ return (sign * Double.POSITIVE_INFINITY);
+ }
+ mantisa = getUnscaledValue().abs();
+ // Let be: this = [u,s], with s > 0
+ if (scale <= 0) {
+ // mantisa = abs(u) * 10^s
+ mantisa = mantisa.multiply(TMultiplication.powerOf10(-scale));
+ } else {// (scale > 0)
+ TBigInteger quotAndRem[];
+ TBigInteger powerOfTen = TMultiplication.powerOf10(scale);
+ int k = 100 - (int)powerOfTwo;
+ int compRem;
+
+ if (k > 0) {
+ /* Computing (mantisa * 2^k) , where 'k' is a enough big
+ * power of '2' to can divide by 10^s */
+ mantisa = mantisa.shiftLeft(k);
+ exponent -= k;
+ }
+ // Computing (mantisa * 2^k) / 10^s
+ quotAndRem = mantisa.divideAndRemainder(powerOfTen);
+ // To check if the fractional part >= 0.5
+ compRem = quotAndRem[1].shiftLeftOneBit().compareTo(powerOfTen);
+ // To add two rounded bits at end of mantisa
+ mantisa = quotAndRem[0].shiftLeft(2).add(
+ TBigInteger.valueOf((compRem * (compRem + 3)) / 2 + 1));
+ exponent -= 2;
+ }
+ lowestSetBit = mantisa.getLowestSetBit();
+ discardedSize = mantisa.bitLength() - 54;
+ if (discardedSize > 0) {// (n > 54)
+ // mantisa = (abs(u) * 10^s) >> (n - 54)
+ bits = mantisa.shiftRight(discardedSize).longValue();
+ tempBits = bits;
+ // #bits = 54, to check if the discarded fraction produces a carry
+ if ((((bits & 1) == 1) && (lowestSetBit < discardedSize))
+ || ((bits & 3) == 3)) {
+ bits += 2;
+ }
+ } else {// (n <= 54)
+ // mantisa = (abs(u) * 10^s) << (54 - n)
+ bits = mantisa.longValue() << -discardedSize;
+ tempBits = bits;
+ // #bits = 54, to check if the discarded fraction produces a carry:
+ if ((bits & 3) == 3) {
+ bits += 2;
+ }
+ }
+ // Testing bit 54 to check if the carry creates a new binary digit
+ if ((bits & 0x40000000000000L) == 0) {
+ // To drop the last bit of mantisa (first discarded)
+ bits >>= 1;
+ // exponent = 2^(s-n+53+bias)
+ exponent += discardedSize;
+ } else {// #bits = 54
+ bits >>= 2;
+ exponent += discardedSize + 1;
+ }
+ // To test if the 53-bits number fits in 'double'
+ if (exponent > 2046) {// (exponent - bias > 1023)
+ return (sign * Double.POSITIVE_INFINITY);
+ } else if (exponent <= 0) {// (exponent - bias <= -1023)
+ // Denormalized numbers (having exponent == 0)
+ if (exponent < -53) {// exponent - bias < -1076
+ return (sign * 0.0d);
+ }
+ // -1076 <= exponent - bias <= -1023
+ // To discard '- exponent + 1' bits
+ bits = tempBits >> 1;
+ tempBits = bits & (-1L >>> (63 + exponent));
+ bits >>= (-exponent );
+ // To test if after discard bits, a new carry is generated
+ if (((bits & 3) == 3) || (((bits & 1) == 1) && (tempBits != 0)
+ && (lowestSetBit < discardedSize))) {
+ bits += 1;
+ }
+ exponent = 0;
+ bits >>= 1;
+ }
+ // Construct the 64 double bits: [sign(1), exponent(11), mantisa(52)]
+ bits = (sign & 0x8000000000000000L) | ((long)exponent << 52) | (bits & 0xFFFFFFFFFFFFFL);
+ return Double.longBitsToDouble(bits);
+ }
+
+ /**
+ * Returns the unit in the last place (ULP) of this {@code BigDecimal}
+ * instance. An ULP is the distance to the nearest big decimal with the same
+ * precision.
+ *
+ * The amount of a rounding error in the evaluation of a floating-point
+ * operation is often expressed in ULPs. An error of 1 ULP is often seen as
+ * a tolerable error.
+ *
+ * For class {@code BigDecimal}, the ULP of a number is simply 10^(-scale).
+ *
+ * For example, {@code new BigDecimal(0.1).ulp()} returns {@code 1E-55}.
+ *
+ * @return unit in the last place (ULP) of this {@code BigDecimal} instance.
+ */
+ public TBigDecimal ulp() {
+ return valueOf(1, scale);
+ }
+
+ /* Private Methods */
+
+ /**
+ * It does all rounding work of the public method
+ * {@code round(MathContext)}, performing an inplace rounding
+ * without creating a new object.
+ *
+ * @param mc
+ * the {@code MathContext} for perform the rounding.
+ * @see #round(TMathContext)
+ */
+ private void inplaceRound(TMathContext mc) {
+ int mcPrecision = mc.getPrecision();
+ if (aproxPrecision() - mcPrecision <= 0 || mcPrecision == 0) {
+ return;
+ }
+ int discardedPrecision = precision() - mcPrecision;
+ // If no rounding is necessary it returns immediately
+ if ((discardedPrecision <= 0)) {
+ return;
+ }
+ // When the number is small perform an efficient rounding
+ if (this.bitLength < 64) {
+ smallRound(mc, discardedPrecision);
+ return;
+ }
+ // Getting the integer part and the discarded fraction
+ TBigInteger sizeOfFraction = TMultiplication.powerOf10(discardedPrecision);
+ TBigInteger[] integerAndFraction = getUnscaledValue().divideAndRemainder(sizeOfFraction);
+ long newScale = (long)scale - discardedPrecision;
+ int compRem;
+ TBigDecimal tempBD;
+ // If the discarded fraction is non-zero, perform rounding
+ if (integerAndFraction[1].signum() != 0) {
+ // To check if the discarded fraction >= 0.5
+ compRem = (integerAndFraction[1].abs().shiftLeftOneBit().compareTo(sizeOfFraction));
+ // To look if there is a carry
+ compRem = roundingBehavior( integerAndFraction[0].testBit(0) ? 1 : 0,
+ integerAndFraction[1].signum() * (5 + compRem),
+ mc.getRoundingMode());
+ if (compRem != 0) {
+ integerAndFraction[0] = integerAndFraction[0].add(TBigInteger.valueOf(compRem));
+ }
+ tempBD = new TBigDecimal(integerAndFraction[0]);
+ // If after to add the increment the precision changed, we normalize the size
+ if (tempBD.precision() > mcPrecision) {
+ integerAndFraction[0] = integerAndFraction[0].divide(TBigInteger.TEN);
+ newScale--;
+ }
+ }
+ // To update all internal fields
+ scale = toIntScale(newScale);
+ precision = mcPrecision;
+ setUnscaledValue(integerAndFraction[0]);
+ }
+
+ private static int longCompareTo(long value1, long value2) {
+ return value1 > value2 ? 1 : (value1 < value2 ? -1 : 0);
+ }
+ /**
+ * This method implements an efficient rounding for numbers which unscaled
+ * value fits in the type {@code long}.
+ *
+ * @param mc
+ * the context to use
+ * @param discardedPrecision
+ * the number of decimal digits that are discarded
+ * @see #round(TMathContext)
+ */
+ private void smallRound(TMathContext mc, int discardedPrecision) {
+ long sizeOfFraction = LONG_TEN_POW[discardedPrecision];
+ long newScale = (long)scale - discardedPrecision;
+ long unscaledVal = smallValue;
+ // Getting the integer part and the discarded fraction
+ long integer = unscaledVal / sizeOfFraction;
+ long fraction = unscaledVal % sizeOfFraction;
+ int compRem;
+ // If the discarded fraction is non-zero perform rounding
+ if (fraction != 0) {
+ // To check if the discarded fraction >= 0.5
+ compRem = longCompareTo(Math.abs(fraction) << 1,sizeOfFraction);
+ // To look if there is a carry
+ integer += roundingBehavior( ((int)integer) & 1,
+ Long.signum(fraction) * (5 + compRem),
+ mc.getRoundingMode());
+ // If after to add the increment the precision changed, we normalize the size
+ if (Math.log10(Math.abs(integer)) >= mc.getPrecision()) {
+ integer /= 10;
+ newScale--;
+ }
+ }
+ // To update all internal fields
+ scale = toIntScale(newScale);
+ precision = mc.getPrecision();
+ smallValue = integer;
+ bitLength = bitLength(integer);
+ intVal = null;
+ }
+
+ /**
+ * Return an increment that can be -1,0 or 1, depending of
+ * {@code roundingMode}.
+ *
+ * @param parityBit
+ * can be 0 or 1, it's only used in the case
+ * {@code HALF_EVEN}
+ * @param fraction
+ * the mantisa to be analyzed
+ * @param roundingMode
+ * the type of rounding
+ * @return the carry propagated after rounding
+ */
+ private static int roundingBehavior(int parityBit, int fraction, TRoundingMode roundingMode) {
+ int increment = 0; // the carry after rounding
+
+ switch (roundingMode) {
+ case UNNECESSARY:
+ if (fraction != 0) {
+ throw new ArithmeticException("Rounding necessary");
+ }
+ break;
+ case UP:
+ increment = Integer.signum(fraction);
+ break;
+ case DOWN:
+ break;
+ case CEILING:
+ increment = Math.max(Integer.signum(fraction), 0);
+ break;
+ case FLOOR:
+ increment = Math.min(Integer.signum(fraction), 0);
+ break;
+ case HALF_UP:
+ if (Math.abs(fraction) >= 5) {
+ increment = Integer.signum(fraction);
+ }
+ break;
+ case HALF_DOWN:
+ if (Math.abs(fraction) > 5) {
+ increment = Integer.signum(fraction);
+ }
+ break;
+ case HALF_EVEN:
+ if (Math.abs(fraction) + parityBit > 5) {
+ increment = Integer.signum(fraction);
+ }
+ break;
+ }
+ return increment;
+ }
+
+ /**
+ * If {@code intVal} has a fractional part throws an exception,
+ * otherwise it counts the number of bits of value and checks if it's out of
+ * the range of the primitive type. If the number fits in the primitive type
+ * returns this number as {@code long}, otherwise throws an
+ * exception.
+ *
+ * @param bitLengthOfType
+ * number of bits of the type whose value will be calculated
+ * exactly
+ * @return the exact value of the integer part of {@code BigDecimal}
+ * when is possible
+ * @throws ArithmeticException when rounding is necessary or the
+ * number don't fit in the primitive type
+ */
+ private long valueExact(int bitLengthOfType) {
+ TBigInteger bigInteger = toBigIntegerExact();
+
+ if (bigInteger.bitLength() < bitLengthOfType) {
+ // It fits in the primitive type
+ return bigInteger.longValue();
+ }
+ throw new ArithmeticException("Rounding necessary");
+ }
+
+ /**
+ * If the precision already was calculated it returns that value, otherwise
+ * it calculates a very good approximation efficiently . Note that this
+ * value will be {@code precision()} or {@code precision()-1}
+ * in the worst case.
+ *
+ * @return an approximation of {@code precision()} value
+ */
+ private int aproxPrecision() {
+ return (precision > 0) ? precision
+ : ((int) ((this.bitLength - 1) * LOG10_2)) + 1;
+ }
+
+ /**
+ * It tests if a scale of type {@code long} fits in 32 bits. It
+ * returns the same scale being casted to {@code int} type when is
+ * possible, otherwise throws an exception.
+ *
+ * @param longScale
+ * a 64 bit scale
+ * @return a 32 bit scale when is possible
+ * @throws ArithmeticException when {@code scale} doesn't
+ * fit in {@code int} type
+ * @see #scale
+ */
+ private static int toIntScale(long longScale) {
+ if (longScale < Integer.MIN_VALUE) {
+ throw new ArithmeticException("Overflow");
+ } else if (longScale > Integer.MAX_VALUE) {
+ throw new ArithmeticException("Underflow");
+ } else {
+ return (int)longScale;
+ }
+ }
+
+ /**
+ * It returns the value 0 with the most approximated scale of type
+ * {@code int}. if {@code longScale > Integer.MAX_VALUE} the
+ * scale will be {@code Integer.MAX_VALUE}; if
+ * {@code longScale < Integer.MIN_VALUE} the scale will be
+ * {@code Integer.MIN_VALUE}; otherwise {@code longScale} is
+ * casted to the type {@code int}.
+ *
+ * @param longScale
+ * the scale to which the value 0 will be scaled.
+ * @return the value 0 scaled by the closer scale of type {@code int}.
+ * @see #scale
+ */
+ private static TBigDecimal zeroScaledBy(long longScale) {
+ if (longScale == (int) longScale) {
+ return valueOf(0,(int)longScale);
+ }
+ if (longScale >= 0) {
+ return new TBigDecimal( 0, Integer.MAX_VALUE);
+ }
+ return new TBigDecimal( 0, Integer.MIN_VALUE);
+ }
+
+ private TBigInteger getUnscaledValue() {
+ if(intVal == null) {
+ intVal = TBigInteger.valueOf(smallValue);
+ }
+ return intVal;
+ }
+
+ private void setUnscaledValue(TBigInteger unscaledValue) {
+ this.intVal = unscaledValue;
+ this.bitLength = unscaledValue.bitLength();
+ if(this.bitLength < 64) {
+ this.smallValue = unscaledValue.longValue();
+ }
+ }
+
+ private static int bitLength(long smallValue) {
+ if(smallValue < 0) {
+ smallValue = ~smallValue;
+ }
+ return 64 - Long.numberOfLeadingZeros(smallValue);
+ }
+
+ private static int bitLength(int smallValue) {
+ if(smallValue < 0) {
+ smallValue = ~smallValue;
+ }
+ return 32 - Integer.numberOfLeadingZeros(smallValue);
+ }
+}
+
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigInteger.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigInteger.java
new file mode 100644
index 000000000..c0121f4bb
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigInteger.java
@@ -0,0 +1,1512 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+import java.util.Random;
+import java.io.Serializable;
+
+/**
+ * This class represents immutable integer numbers of arbitrary length. Large
+ * numbers are typically used in security applications and therefore BigIntegers
+ * offer dedicated functionality like the generation of large prime numbers or
+ * the computation of modular inverse.
+ *
+ * Since the class was modeled to offer all the functionality as the
+ * {@link Integer} class does, it provides even methods that operate bitwise on
+ * a two's complement representation of large integers. Note however that the
+ * implementations favors an internal representation where magnitude and sign
+ * are treated separately. Hence such operations are inefficient and should be
+ * discouraged. In simple words: Do NOT implement any bit fields based on
+ * BigInteger.
+ */
+public class TBigInteger extends Number implements Comparable, Serializable {
+
+ /** This is the serialVersionUID used by the sun implementation. */
+ private static final long serialVersionUID = -8287574255936472291L;
+
+ /* Fields used for the internal representation. */
+
+ /**
+ * The magnitude of this big integer. This array holds unsigned little
+ * endian digits. For example: {@code 13} is represented as [ 13 ]
+ * {@code -13} is represented as [ 13 ] {@code 2^32 + 13} is represented as
+ * [ 13, 1 ] {@code 2^64 + 13} is represented as [ 13, 0, 1 ] {@code 2^31}
+ * is represented as [ Integer.MIN_VALUE ] The magnitude array may be longer
+ * than strictly necessary, which results in additional trailing zeros.
+ */
+ transient int digits[];
+
+ /**
+ * The length of this in measured in ints. Can be less than digits.length().
+ */
+ transient int numberLength;
+
+ /** The sign of this. */
+ transient int sign;
+
+ /**
+ * The {@code BigInteger} constant 0.
+ */
+ public static final TBigInteger ZERO = new TBigInteger(0, 0);
+
+ /**
+ * The {@code BigInteger} constant 1.
+ */
+ public static final TBigInteger ONE = new TBigInteger(1, 1);
+
+ /**
+ * The {@code BigInteger} constant 10.
+ */
+ public static final TBigInteger TEN = new TBigInteger(1, 10);
+
+ /** The {@code BigInteger} constant -1. */
+ static final TBigInteger MINUS_ONE = new TBigInteger(-1, 1);
+
+ /** The {@code BigInteger} constant 0 used for comparison. */
+ static final int EQUALS = 0;
+
+ /** The {@code BigInteger} constant 1 used for comparison. */
+ static final int GREATER = 1;
+
+ /** The {@code BigInteger} constant -1 used for comparison. */
+ static final int LESS = -1;
+
+ /** All the {@code BigInteger} numbers in the range [0,10] are cached. */
+ static final TBigInteger[] SMALL_VALUES = { ZERO, ONE, new TBigInteger(1, 2), new TBigInteger(1, 3),
+ new TBigInteger(1, 4), new TBigInteger(1, 5), new TBigInteger(1, 6), new TBigInteger(1, 7),
+ new TBigInteger(1, 8), new TBigInteger(1, 9), TEN };
+
+ static final TBigInteger[] TWO_POWS;
+
+ static {
+ TWO_POWS = new TBigInteger[32];
+ for (int i = 0; i < TWO_POWS.length; i++) {
+ TWO_POWS[i] = TBigInteger.valueOf(1L << i);
+ }
+ }
+
+ private transient int firstNonzeroDigit = -2;
+
+ /** Cache for the hash code. */
+ private transient int hashCode = 0;
+
+ /**
+ * Constructs a random non-negative {@code BigInteger} instance in the range
+ * [0, 2^(numBits)-1].
+ *
+ * @param numBits
+ * maximum length of the new {@code BigInteger} in bits.
+ * @param rnd
+ * is an optional random generator to be used.
+ * @throws IllegalArgumentException
+ * if {@code numBits} < 0.
+ */
+ public TBigInteger(int numBits, Random rnd) {
+ if (numBits < 0) {
+ throw new IllegalArgumentException("numBits must be non-negative");
+ }
+ if (numBits == 0) {
+ sign = 0;
+ numberLength = 1;
+ digits = new int[] { 0 };
+ } else {
+ sign = 1;
+ numberLength = (numBits + 31) >> 5;
+ digits = new int[numberLength];
+ for (int i = 0; i < numberLength; i++) {
+ digits[i] = rnd.nextInt();
+ }
+ // Using only the necessary bits
+ digits[numberLength - 1] >>>= (-numBits) & 31;
+ cutOffLeadingZeroes();
+ }
+ }
+
+ /**
+ * Constructs a random {@code BigInteger} instance in the range [0,
+ * 2^(bitLength)-1] which is probably prime. The probability that the
+ * returned {@code BigInteger} is prime is beyond (1-1/2^certainty).
+ *
+ * @param bitLength
+ * length of the new {@code BigInteger} in bits.
+ * @param certainty
+ * tolerated primality uncertainty.
+ * @param rnd
+ * is an optional random generator to be used.
+ * @throws ArithmeticException
+ * if {@code bitLength} < 2.
+ */
+ public TBigInteger(int bitLength, int certainty, Random rnd) {
+ if (bitLength < 2) {
+ throw new ArithmeticException("bitLength < 2");
+ }
+ TBigInteger me = TPrimality.consBigInteger(bitLength, certainty, rnd);
+ sign = me.sign;
+ numberLength = me.numberLength;
+ digits = me.digits;
+ }
+
+ /**
+ * Constructs a new {@code BigInteger} instance from the string
+ * representation. The string representation consists of an optional minus
+ * sign followed by a non-empty sequence of decimal digits.
+ *
+ * @param val
+ * string representation of the new {@code BigInteger}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ * @throws NumberFormatException
+ * if {@code val} is not a valid representation of a
+ * {@code BigInteger}.
+ */
+ public TBigInteger(String val) {
+ this(val, 10);
+ }
+
+ /**
+ * Constructs a new {@code BigInteger} instance from the string
+ * representation. The string representation consists of an optional minus
+ * sign followed by a non-empty sequence of digits in the specified radix.
+ * For the conversion the method {@code Character.digit(char, radix)} is
+ * used.
+ *
+ * @param val
+ * string representation of the new {@code BigInteger}.
+ * @param radix
+ * the base to be used for the conversion.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ * @throws NumberFormatException
+ * if {@code val} is not a valid representation of a
+ * {@code BigInteger} or if {@code radix < Character.MIN_RADIX}
+ * or {@code radix > Character.MAX_RADIX}.
+ */
+ public TBigInteger(String val, int radix) {
+ if (val == null) {
+ throw new NullPointerException();
+ }
+ if ((radix < Character.MIN_RADIX) || (radix > Character.MAX_RADIX)) {
+ throw new NumberFormatException("Radix out of range");
+ }
+ if (val.length() == 0) {
+ throw new NumberFormatException("Zero length BigInteger");
+ }
+ setFromString(this, val, radix);
+ }
+
+ /**
+ * Constructs a new {@code BigInteger} instance with the given sign and the
+ * given magnitude. The sign is given as an integer (-1 for negative, 0 for
+ * zero, 1 for positive). The magnitude is specified as a byte array. The
+ * most significant byte is the entry at index 0.
+ *
+ * @param signum
+ * sign of the new {@code BigInteger} (-1 for negative, 0 for
+ * zero, 1 for positive).
+ * @param magnitude
+ * magnitude of the new {@code BigInteger} with the most
+ * significant byte first.
+ * @throws NullPointerException
+ * if {@code magnitude == null}.
+ * @throws NumberFormatException
+ * if the sign is not one of -1, 0, 1 or if the sign is zero and
+ * the magnitude contains non-zero entries.
+ */
+ public TBigInteger(int signum, byte[] magnitude) {
+ if (magnitude == null) {
+ throw new NullPointerException();
+ }
+ if ((signum < -1) || (signum > 1)) {
+ throw new NumberFormatException("Invalid signum value");
+ }
+ if (signum == 0) {
+ for (byte element : magnitude) {
+ if (element != 0) {
+ throw new NumberFormatException("signum-magnitude mismatch");
+ }
+ }
+ }
+ if (magnitude.length == 0) {
+ sign = 0;
+ numberLength = 1;
+ digits = new int[] { 0 };
+ } else {
+ sign = signum;
+ putBytesPositiveToIntegers(magnitude);
+ cutOffLeadingZeroes();
+ }
+ }
+
+ /**
+ * Constructs a new {@code BigInteger} from the given two's complement
+ * representation. The most significant byte is the entry at index 0. The
+ * most significant bit of this entry determines the sign of the new
+ * {@code BigInteger} instance. The given array must not be empty.
+ *
+ * @param val
+ * two's complement representation of the new {@code BigInteger}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ * @throws NumberFormatException
+ * if the length of {@code val} is zero.
+ */
+ public TBigInteger(byte[] val) {
+ if (val.length == 0) {
+ throw new NumberFormatException("Zero length BigInteger");
+ }
+ if (val[0] < 0) {
+ sign = -1;
+ putBytesNegativeToIntegers(val);
+ } else {
+ sign = 1;
+ putBytesPositiveToIntegers(val);
+ }
+ cutOffLeadingZeroes();
+ }
+
+ /**
+ * Constructs a number which array is of size 1.
+ *
+ * @param sign
+ * the sign of the number
+ * @param value
+ * the only one digit of array
+ */
+ TBigInteger(int sign, int value) {
+ this.sign = sign;
+ numberLength = 1;
+ digits = new int[] { value };
+ }
+
+ /**
+ * Constructs a number without to create new space. This construct should be
+ * used only if the three fields of representation are known.
+ *
+ * @param sign
+ * the sign of the number
+ * @param numberLength
+ * the length of the internal array
+ * @param digits
+ * a reference of some array created before
+ */
+ TBigInteger(int sign, int numberLength, int[] digits) {
+ this.sign = sign;
+ this.numberLength = numberLength;
+ this.digits = digits;
+ }
+
+ /**
+ * Creates a new {@code BigInteger} whose value is equal to the specified
+ * {@code long}.
+ *
+ * @param sign
+ * the sign of the number
+ * @param val
+ * the value of the new {@code BigInteger}.
+ */
+ TBigInteger(int sign, long val) {
+ // PRE: (val >= 0) && (sign >= -1) && (sign <= 1)
+ this.sign = sign;
+ if ((val & 0xFFFFFFFF00000000L) == 0) {
+ // It fits in one 'int'
+ numberLength = 1;
+ digits = new int[] { (int)val };
+ } else {
+ numberLength = 2;
+ digits = new int[] { (int)val, (int)(val >> 32) };
+ }
+ }
+
+ /**
+ * Creates a new {@code BigInteger} with the given sign and magnitude. This
+ * constructor does not create a copy, so any changes to the reference will
+ * affect the new number.
+ *
+ * @param signum
+ * The sign of the number represented by {@code digits}
+ * @param digits
+ * The magnitude of the number
+ */
+ TBigInteger(int signum, int digits[]) {
+ if (digits.length == 0) {
+ sign = 0;
+ numberLength = 1;
+ this.digits = new int[] { 0 };
+ } else {
+ sign = signum;
+ numberLength = digits.length;
+ this.digits = digits;
+ cutOffLeadingZeroes();
+ }
+ }
+
+ public static TBigInteger valueOf(long val) {
+ if (val < 0) {
+ if (val != -1) {
+ return new TBigInteger(-1, -val);
+ }
+ return MINUS_ONE;
+ } else if (val <= 10) {
+ return SMALL_VALUES[(int)val];
+ } else {// (val > 10)
+ return new TBigInteger(1, val);
+ }
+ }
+
+ /**
+ * Returns the two's complement representation of this BigInteger in a byte
+ * array.
+ *
+ * @return two's complement representation of {@code this}.
+ */
+ public byte[] toByteArray() {
+ if (this.sign == 0) {
+ return new byte[] { 0 };
+ }
+ TBigInteger temp = this;
+ int bitLen = bitLength();
+ int iThis = getFirstNonzeroDigit();
+ int bytesLen = (bitLen >> 3) + 1;
+ /*
+ * Puts the little-endian int array representing the magnitude of this
+ * BigInteger into the big-endian byte array.
+ */
+ byte[] bytes = new byte[bytesLen];
+ int firstByteNumber = 0;
+ int highBytes;
+ int digitIndex = 0;
+ int bytesInInteger = 4;
+ int digit;
+ int hB;
+
+ if (bytesLen - (numberLength << 2) == 1) {
+ bytes[0] = (byte)((sign < 0) ? -1 : 0);
+ highBytes = 4;
+ firstByteNumber++;
+ } else {
+ hB = bytesLen & 3;
+ highBytes = (hB == 0) ? 4 : hB;
+ }
+
+ digitIndex = iThis;
+ bytesLen -= iThis << 2;
+
+ if (sign < 0) {
+ digit = -temp.digits[digitIndex];
+ digitIndex++;
+ if (digitIndex == numberLength) {
+ bytesInInteger = highBytes;
+ }
+ for (int i = 0; i < bytesInInteger; i++, digit >>= 8) {
+ bytes[--bytesLen] = (byte)digit;
+ }
+ while (bytesLen > firstByteNumber) {
+ digit = ~temp.digits[digitIndex];
+ digitIndex++;
+ if (digitIndex == numberLength) {
+ bytesInInteger = highBytes;
+ }
+ for (int i = 0; i < bytesInInteger; i++, digit >>= 8) {
+ bytes[--bytesLen] = (byte)digit;
+ }
+ }
+ } else {
+ while (bytesLen > firstByteNumber) {
+ digit = temp.digits[digitIndex];
+ digitIndex++;
+ if (digitIndex == numberLength) {
+ bytesInInteger = highBytes;
+ }
+ for (int i = 0; i < bytesInInteger; i++, digit >>= 8) {
+ bytes[--bytesLen] = (byte)digit;
+ }
+ }
+ }
+ return bytes;
+ }
+
+ /** @see TBigInteger#BigInteger(String, int) */
+ private static void setFromString(TBigInteger bi, String val, int radix) {
+ int sign;
+ int[] digits;
+ int numberLength;
+ int stringLength = val.length();
+ int startChar;
+ int endChar = stringLength;
+
+ if (val.charAt(0) == '-') {
+ sign = -1;
+ startChar = 1;
+ stringLength--;
+ } else {
+ sign = 1;
+ startChar = 0;
+ }
+ /*
+ * We use the following algorithm: split a string into portions of n
+ * characters and convert each portion to an integer according to the
+ * radix. Then convert an exp(radix, n) based number to binary using the
+ * multiplication method. See D. Knuth, The Art of Computer Programming,
+ * vol. 2.
+ */
+
+ int charsPerInt = TConversion.digitFitInInt[radix];
+ int bigRadixDigitsLength = stringLength / charsPerInt;
+ int topChars = stringLength % charsPerInt;
+
+ if (topChars != 0) {
+ bigRadixDigitsLength++;
+ }
+ digits = new int[bigRadixDigitsLength];
+ // Get the maximal power of radix that fits in int
+ int bigRadix = TConversion.bigRadices[radix - 2];
+ // Parse an input string and accumulate the BigInteger's magnitude
+ int digitIndex = 0; // index of digits array
+ int substrEnd = startChar + ((topChars == 0) ? charsPerInt : topChars);
+ int newDigit;
+
+ for (int substrStart = startChar; substrStart < endChar; substrStart = substrEnd, substrEnd = substrStart +
+ charsPerInt) {
+ int bigRadixDigit = Integer.parseInt(val.substring(substrStart, substrEnd), radix);
+ newDigit = TMultiplication.multiplyByInt(digits, digitIndex, bigRadix);
+ newDigit += TElementary.inplaceAdd(digits, digitIndex, bigRadixDigit);
+ digits[digitIndex++] = newDigit;
+ }
+ numberLength = digitIndex;
+ bi.sign = sign;
+ bi.numberLength = numberLength;
+ bi.digits = digits;
+ bi.cutOffLeadingZeroes();
+ }
+
+ /**
+ * Returns a (new) {@code BigInteger} whose value is the absolute value of
+ * {@code this}.
+ *
+ * @return {@code abs(this)}.
+ */
+ public TBigInteger abs() {
+ return ((sign < 0) ? new TBigInteger(1, numberLength, digits) : this);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is the {@code -this}.
+ *
+ * @return {@code -this}.
+ */
+ public TBigInteger negate() {
+ return ((sign == 0) ? this : new TBigInteger(-sign, numberLength, digits));
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this + val}.
+ *
+ * @param val
+ * value to be added to {@code this}.
+ * @return {@code this + val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger add(TBigInteger val) {
+ return TElementary.add(this, val);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this - val}.
+ *
+ * @param val
+ * value to be subtracted from {@code this}.
+ * @return {@code this - val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger subtract(TBigInteger val) {
+ return TElementary.subtract(this, val);
+ }
+
+ /**
+ * Returns the sign of this {@code BigInteger}.
+ *
+ * @return {@code -1} if {@code this < 0}, {@code 0} if {@code this == 0},
+ * {@code 1} if {@code this > 0}.
+ */
+ public int signum() {
+ return sign;
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this >> n}. For
+ * negative arguments, the result is also negative. The shift distance may
+ * be negative which means that {@code this} is shifted left.
+ *
+ * Implementation Note: Usage of this method on negative values is
+ * not recommended as the current implementation is not efficient.
+ *
+ * @param n
+ * shift distance
+ * @return {@code this >> n} if {@code n >= 0}; {@code this << (-n)}
+ * otherwise
+ */
+ public TBigInteger shiftRight(int n) {
+ if ((n == 0) || (sign == 0)) {
+ return this;
+ }
+ return ((n > 0) ? TBitLevel.shiftRight(this, n) : TBitLevel.shiftLeft(this, -n));
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this << n}. The
+ * result is equivalent to {@code this * 2^n} if n >= 0. The shift distance
+ * may be negative which means that {@code this} is shifted right. The
+ * result then corresponds to {@code floor(this / 2^(-n))}.
+ *
+ * Implementation Note: Usage of this method on negative values is
+ * not recommended as the current implementation is not efficient.
+ *
+ * @param n
+ * shift distance.
+ * @return {@code this << n} if {@code n >= 0}; {@code this >> (-n)}.
+ * otherwise
+ */
+ public TBigInteger shiftLeft(int n) {
+ if ((n == 0) || (sign == 0)) {
+ return this;
+ }
+ return ((n > 0) ? TBitLevel.shiftLeft(this, n) : TBitLevel.shiftRight(this, -n));
+ }
+
+ TBigInteger shiftLeftOneBit() {
+ return (sign == 0) ? this : TBitLevel.shiftLeftOneBit(this);
+ }
+
+ /**
+ * Returns the length of the value's two's complement representation without
+ * leading zeros for positive numbers / without leading ones for negative
+ * values.
+ *
+ * The two's complement representation of {@code this} will be at least
+ * {@code bitLength() + 1} bits long.
+ *
+ * The value will fit into an {@code int} if {@code bitLength() < 32} or
+ * into a {@code long} if {@code bitLength() < 64}.
+ *
+ * @return the length of the minimal two's complement representation for
+ * {@code this} without the sign bit.
+ */
+ public int bitLength() {
+ return TBitLevel.bitLength(this);
+ }
+
+ /**
+ * Tests whether the bit at position n in {@code this} is set. The result is
+ * equivalent to {@code this & (2^n) != 0}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param n
+ * position where the bit in {@code this} has to be inspected.
+ * @return {@code this & (2^n) != 0}.
+ * @throws ArithmeticException
+ * if {@code n < 0}.
+ */
+ public boolean testBit(int n) {
+ if (n == 0) {
+ return ((digits[0] & 1) != 0);
+ }
+ if (n < 0) {
+ throw new ArithmeticException("Negative bit address");
+ }
+ int intCount = n >> 5;
+ if (intCount >= numberLength) {
+ return (sign < 0);
+ }
+ int digit = digits[intCount];
+ n = (1 << (n & 31)); // int with 1 set to the needed position
+ if (sign < 0) {
+ int firstNonZeroDigit = getFirstNonzeroDigit();
+ if (intCount < firstNonZeroDigit) {
+ return false;
+ } else if (firstNonZeroDigit == intCount) {
+ digit = -digit;
+ } else {
+ digit = ~digit;
+ }
+ }
+ return ((digit & n) != 0);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} which has the same binary representation
+ * as {@code this} but with the bit at position n set. The result is
+ * equivalent to {@code this | 2^n}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param n
+ * position where the bit in {@code this} has to be set.
+ * @return {@code this | 2^n}.
+ * @throws ArithmeticException
+ * if {@code n < 0}.
+ */
+ public TBigInteger setBit(int n) {
+ if (!testBit(n)) {
+ return TBitLevel.flipBit(this, n);
+ }
+ return this;
+ }
+
+ /**
+ * Returns a new {@code BigInteger} which has the same binary representation
+ * as {@code this} but with the bit at position n cleared. The result is
+ * equivalent to {@code this & ~(2^n)}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param n
+ * position where the bit in {@code this} has to be cleared.
+ * @return {@code this & ~(2^n)}.
+ * @throws ArithmeticException
+ * if {@code n < 0}.
+ */
+ public TBigInteger clearBit(int n) {
+ if (testBit(n)) {
+ return TBitLevel.flipBit(this, n);
+ }
+ return this;
+ }
+
+ /**
+ * Returns a new {@code BigInteger} which has the same binary representation
+ * as {@code this} but with the bit at position n flipped. The result is
+ * equivalent to {@code this ^ 2^n}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param n
+ * position where the bit in {@code this} has to be flipped.
+ * @return {@code this ^ 2^n}.
+ * @throws ArithmeticException
+ * if {@code n < 0}.
+ */
+ public TBigInteger flipBit(int n) {
+ if (n < 0) {
+ throw new ArithmeticException("Negative bit address");
+ }
+ return TBitLevel.flipBit(this, n);
+ }
+
+ /**
+ * Returns the position of the lowest set bit in the two's complement
+ * representation of this {@code BigInteger}. If all bits are zero (this=0)
+ * then -1 is returned as result.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @return position of lowest bit if {@code this != 0}, {@code -1} otherwise
+ */
+ public int getLowestSetBit() {
+ if (sign == 0) {
+ return -1;
+ }
+ // (sign != 0) implies that exists some non zero digit
+ int i = getFirstNonzeroDigit();
+ return ((i << 5) + Integer.numberOfTrailingZeros(digits[i]));
+ }
+
+ /**
+ * Use {@code bitLength(0)} if you want to know the length of the binary
+ * value in bits.
+ *
+ * Returns the number of bits in the binary representation of {@code this}
+ * which differ from the sign bit. If {@code this} is positive the result is
+ * equivalent to the number of bits set in the binary representation of
+ * {@code this}. If {@code this} is negative the result is equivalent to the
+ * number of bits set in the binary representation of {@code -this-1}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @return number of bits in the binary representation of {@code this} which
+ * differ from the sign bit
+ */
+ public int bitCount() {
+ return TBitLevel.bitCount(this);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code ~this}. The result
+ * of this operation is {@code -this-1}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @return {@code ~this}.
+ */
+ public TBigInteger not() {
+ return TLogical.not(this);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this & val}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param val
+ * value to be and'ed with {@code this}.
+ * @return {@code this & val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger and(TBigInteger val) {
+ return TLogical.and(this, val);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this | val}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param val
+ * value to be or'ed with {@code this}.
+ * @return {@code this | val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger or(TBigInteger val) {
+ return TLogical.or(this, val);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this ^ val}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param val
+ * value to be xor'ed with {@code this}
+ * @return {@code this ^ val}
+ * @throws NullPointerException
+ * if {@code val == null}
+ */
+ public TBigInteger xor(TBigInteger val) {
+ return TLogical.xor(this, val);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this & ~val}.
+ * Evaluating {@code x.andNot(val)} returns the same result as
+ * {@code x.and(val.not())}.
+ *
+ * Implementation Note: Usage of this method is not recommended as
+ * the current implementation is not efficient.
+ *
+ * @param val
+ * value to be not'ed and then and'ed with {@code this}.
+ * @return {@code this & ~val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger andNot(TBigInteger val) {
+ return TLogical.andNot(this, val);
+ }
+
+ /**
+ * Returns this {@code BigInteger} as an int value. If {@code this} is too
+ * big to be represented as an int, then {@code this} % 2^32 is returned.
+ *
+ * @return this {@code BigInteger} as an int value.
+ */
+ @Override
+ public int intValue() {
+ return (sign * digits[0]);
+ }
+
+ /**
+ * Returns this {@code BigInteger} as an long value. If {@code this} is too
+ * big to be represented as an long, then {@code this} % 2^64 is returned.
+ *
+ * @return this {@code BigInteger} as a long value.
+ */
+ @Override
+ public long longValue() {
+ long value = (numberLength > 1) ? (((long)digits[1]) << 32) | (digits[0] & 0xFFFFFFFFL)
+ : (digits[0] & 0xFFFFFFFFL);
+ return (sign * value);
+ }
+
+ /**
+ * Returns this {@code BigInteger} as an float value. If {@code this} is too
+ * big to be represented as an float, then {@code Float.POSITIVE_INFINITY}
+ * or {@code Float.NEGATIVE_INFINITY} is returned. Note, that not all
+ * integers x in the range [-Float.MAX_VALUE, Float.MAX_VALUE] can be
+ * represented as a float. The float representation has a mantissa of length
+ * 24. For example, 2^24+1 = 16777217 is returned as float 16777216.0.
+ *
+ * @return this {@code BigInteger} as a float value.
+ */
+ @Override
+ public float floatValue() {
+ return (float)doubleValue();
+ }
+
+ /**
+ * Returns this {@code BigInteger} as an double value. If {@code this} is
+ * too big to be represented as an double, then
+ * {@code Double.POSITIVE_INFINITY} or {@code Double.NEGATIVE_INFINITY} is
+ * returned. Note, that not all integers x in the range [-Double.MAX_VALUE,
+ * Double.MAX_VALUE] can be represented as a double. The double
+ * representation has a mantissa of length 53. For example, 2^53+1 =
+ * 9007199254740993 is returned as double 9007199254740992.0.
+ *
+ * @return this {@code BigInteger} as a double value
+ */
+ @Override
+ public double doubleValue() {
+ return TConversion.bigInteger2Double(this);
+ }
+
+ /**
+ * Compares this {@code BigInteger} with {@code val}. Returns one of the
+ * three values 1, 0, or -1.
+ *
+ * @param val
+ * value to be compared with {@code this}.
+ * @return {@code 1} if {@code this > val}, {@code -1} if {@code this < val}
+ * , {@code 0} if {@code this == val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ @Override
+ public int compareTo(TBigInteger val) {
+ if (sign > val.sign) {
+ return GREATER;
+ }
+ if (sign < val.sign) {
+ return LESS;
+ }
+ if (numberLength > val.numberLength) {
+ return sign;
+ }
+ if (numberLength < val.numberLength) {
+ return -val.sign;
+ }
+ // Equal sign and equal numberLength
+ return (sign * TElementary.compareArrays(digits, val.digits, numberLength));
+ }
+
+ /**
+ * Returns the minimum of this {@code BigInteger} and {@code val}.
+ *
+ * @param val
+ * value to be used to compute the minimum with {@code this}.
+ * @return {@code min(this, val)}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger min(TBigInteger val) {
+ return ((this.compareTo(val) == LESS) ? this : val);
+ }
+
+ /**
+ * Returns the maximum of this {@code BigInteger} and {@code val}.
+ *
+ * @param val
+ * value to be used to compute the maximum with {@code this}
+ * @return {@code max(this, val)}
+ * @throws NullPointerException
+ * if {@code val == null}
+ */
+ public TBigInteger max(TBigInteger val) {
+ return ((this.compareTo(val) == GREATER) ? this : val);
+ }
+
+ /**
+ * Returns a hash code for this {@code BigInteger}.
+ *
+ * @return hash code for {@code this}.
+ */
+ @Override
+ public int hashCode() {
+ if (hashCode != 0) {
+ return hashCode;
+ }
+ for (int i = 0; i < digits.length; i++) {
+ hashCode = (hashCode * 33 + (digits[i] & 0xffffffff));
+ }
+ hashCode = hashCode * sign;
+ return hashCode;
+ }
+
+ /**
+ * Returns {@code true} if {@code x} is a BigInteger instance and if this
+ * instance is equal to this {@code BigInteger}.
+ *
+ * @param x
+ * object to be compared with {@code this}.
+ * @return true if {@code x} is a BigInteger and {@code this == x},
+ * {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object x) {
+ if (this == x) {
+ return true;
+ }
+ if (x instanceof TBigInteger) {
+ TBigInteger x1 = (TBigInteger)x;
+ return sign == x1.sign && numberLength == x1.numberLength && equalsArrays(x1.digits);
+ }
+ return false;
+ }
+
+ boolean equalsArrays(final int[] b) {
+ int i;
+ for (i = numberLength - 1; (i >= 0) && (digits[i] == b[i]); i--) {
+ // Empty
+ }
+ return i < 0;
+ }
+
+ /**
+ * Returns a string representation of this {@code BigInteger} in decimal
+ * form.
+ *
+ * @return a string representation of {@code this} in decimal form.
+ */
+ @Override
+ public String toString() {
+ return TConversion.toDecimalScaledString(this, 0);
+ }
+
+ /**
+ * Returns a string containing a string representation of this
+ * {@code BigInteger} with base radix. If
+ * {@code radix < Character.MIN_RADIX} or
+ * {@code radix > Character.MAX_RADIX} then a decimal representation is
+ * returned. The characters of the string representation are generated with
+ * method {@code Character.forDigit}.
+ *
+ * @param radix
+ * base to be used for the string representation.
+ * @return a string representation of this with radix 10.
+ */
+ public String toString(int radix) {
+ return TConversion.bigInteger2String(this, radix);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is greatest common divisor
+ * of {@code this} and {@code val}. If {@code this==0} and {@code val==0}
+ * then zero is returned, otherwise the result is positive.
+ *
+ * @param val
+ * value with which the greatest common divisor is computed.
+ * @return {@code gcd(this, val)}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger gcd(TBigInteger val) {
+ TBigInteger val1 = this.abs();
+ TBigInteger val2 = val.abs();
+ // To avoid a possible division by zero
+ if (val1.signum() == 0) {
+ return val2;
+ } else if (val2.signum() == 0) {
+ return val1;
+ }
+
+ // Optimization for small operands
+ // (op2.bitLength() < 64) and (op1.bitLength() < 64)
+ if (((val1.numberLength == 1) || ((val1.numberLength == 2) && (val1.digits[1] > 0))) &&
+ (val2.numberLength == 1 || (val2.numberLength == 2 && val2.digits[1] > 0))) {
+ return TBigInteger.valueOf(TDivision.gcdBinary(val1.longValue(), val2.longValue()));
+ }
+
+ return TDivision.gcdBinary(val1.copy(), val2.copy());
+
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this * val}.
+ *
+ * @param val
+ * value to be multiplied with {@code this}.
+ * @return {@code this * val}.
+ * @throws NullPointerException
+ * if {@code val == null}.
+ */
+ public TBigInteger multiply(TBigInteger val) {
+ // This let us to throw NullPointerException when val == null
+ if (val.sign == 0) {
+ return ZERO;
+ }
+ if (sign == 0) {
+ return ZERO;
+ }
+ return TMultiplication.multiply(this, val);
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this ^ exp}.
+ *
+ * @param exp
+ * exponent to which {@code this} is raised.
+ * @return {@code this ^ exp}.
+ * @throws ArithmeticException
+ * if {@code exp < 0}.
+ */
+ public TBigInteger pow(int exp) {
+ if (exp < 0) {
+ throw new ArithmeticException("Negative exponent");
+ }
+ if (exp == 0) {
+ return ONE;
+ } else if (exp == 1 || equals(ONE) || equals(ZERO)) {
+ return this;
+ }
+
+ // if even take out 2^x factor which we can
+ // calculate by shifting.
+ if (!testBit(0)) {
+ int x = 1;
+ while (!testBit(x)) {
+ x++;
+ }
+ return getPowerOfTwo(x * exp).multiply(this.shiftRight(x).pow(exp));
+ }
+ return TMultiplication.pow(this, exp);
+ }
+
+ /**
+ * Returns a {@code BigInteger} array which contains {@code this / divisor}
+ * at index 0 and {@code this % divisor} at index 1.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @return {@code [this / divisor, this % divisor]}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ * @see #divide
+ * @see #remainder
+ */
+ public TBigInteger[] divideAndRemainder(TBigInteger divisor) {
+ int divisorSign = divisor.sign;
+ if (divisorSign == 0) {
+ throw new ArithmeticException("BigInteger divide by zero");
+ }
+ int divisorLen = divisor.numberLength;
+ int[] divisorDigits = divisor.digits;
+ if (divisorLen == 1) {
+ return TDivision.divideAndRemainderByInteger(this, divisorDigits[0], divisorSign);
+ }
+ // res[0] is a quotient and res[1] is a remainder:
+ int[] thisDigits = digits;
+ int thisLen = numberLength;
+ int cmp = (thisLen != divisorLen) ? ((thisLen > divisorLen) ? 1 : -1) : TElementary.compareArrays(thisDigits,
+ divisorDigits, thisLen);
+ if (cmp < 0) {
+ return new TBigInteger[] { ZERO, this };
+ }
+ int thisSign = sign;
+ int quotientLength = thisLen - divisorLen + 1;
+ int remainderLength = divisorLen;
+ int quotientSign = ((thisSign == divisorSign) ? 1 : -1);
+ int quotientDigits[] = new int[quotientLength];
+ int remainderDigits[] = TDivision.divide(quotientDigits, quotientLength, thisDigits, thisLen, divisorDigits,
+ divisorLen);
+ TBigInteger result0 = new TBigInteger(quotientSign, quotientLength, quotientDigits);
+ TBigInteger result1 = new TBigInteger(thisSign, remainderLength, remainderDigits);
+ result0.cutOffLeadingZeroes();
+ result1.cutOffLeadingZeroes();
+ return new TBigInteger[] { result0, result1 };
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this / divisor}.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @return {@code this / divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ */
+ public TBigInteger divide(TBigInteger divisor) {
+ if (divisor.sign == 0) {
+ throw new ArithmeticException("BigInteger divide by zero");
+ }
+ int divisorSign = divisor.sign;
+ if (divisor.isOne()) {
+ return ((divisor.sign > 0) ? this : this.negate());
+ }
+ int thisSign = sign;
+ int thisLen = numberLength;
+ int divisorLen = divisor.numberLength;
+ if (thisLen + divisorLen == 2) {
+ long val = (digits[0] & 0xFFFFFFFFL) / (divisor.digits[0] & 0xFFFFFFFFL);
+ if (thisSign != divisorSign) {
+ val = -val;
+ }
+ return valueOf(val);
+ }
+ int cmp = ((thisLen != divisorLen) ? ((thisLen > divisorLen) ? 1 : -1) : TElementary.compareArrays(digits,
+ divisor.digits, thisLen));
+ if (cmp == EQUALS) {
+ return ((thisSign == divisorSign) ? ONE : MINUS_ONE);
+ }
+ if (cmp == LESS) {
+ return ZERO;
+ }
+ int resLength = thisLen - divisorLen + 1;
+ int resDigits[] = new int[resLength];
+ int resSign = ((thisSign == divisorSign) ? 1 : -1);
+ if (divisorLen == 1) {
+ TDivision.divideArrayByInt(resDigits, digits, thisLen, divisor.digits[0]);
+ } else {
+ TDivision.divide(resDigits, resLength, digits, thisLen, divisor.digits, divisorLen);
+ }
+ TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this % divisor}.
+ * Regarding signs this methods has the same behavior as the % operator on
+ * int's, i.e. the sign of the remainder is the same as the sign of this.
+ *
+ * @param divisor
+ * value by which {@code this} is divided.
+ * @return {@code this % divisor}.
+ * @throws NullPointerException
+ * if {@code divisor == null}.
+ * @throws ArithmeticException
+ * if {@code divisor == 0}.
+ */
+ public TBigInteger remainder(TBigInteger divisor) {
+ if (divisor.sign == 0) {
+ throw new ArithmeticException("BigInteger divide by zero");
+ }
+ int thisLen = numberLength;
+ int divisorLen = divisor.numberLength;
+ if (((thisLen != divisorLen) ? ((thisLen > divisorLen) ? 1 : -1) : TElementary.compareArrays(digits,
+ divisor.digits, thisLen)) == LESS) {
+ return this;
+ }
+ int resLength = divisorLen;
+ int resDigits[] = new int[resLength];
+ if (resLength == 1) {
+ resDigits[0] = TDivision.remainderArrayByInt(digits, thisLen, divisor.digits[0]);
+ } else {
+ int qLen = thisLen - divisorLen + 1;
+ resDigits = TDivision.divide(null, qLen, digits, thisLen, divisor.digits, divisorLen);
+ }
+ TBigInteger result = new TBigInteger(sign, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code 1/this mod m}. The
+ * modulus {@code m} must be positive. The result is guaranteed to be in the
+ * interval {@code [0, m)} (0 inclusive, m exclusive). If {@code this} is
+ * not relatively prime to m, then an exception is thrown.
+ *
+ * @param m
+ * the modulus.
+ * @return {@code 1/this mod m}.
+ * @throws NullPointerException
+ * if {@code m == null}
+ * @throws ArithmeticException
+ * if {@code m < 0 or} if {@code this} is not relatively prime
+ * to {@code m}
+ */
+ public TBigInteger modInverse(TBigInteger m) {
+ if (m.sign <= 0) {
+ throw new ArithmeticException("BigInteger: modulus not positive");
+ }
+ // If both are even, no inverse exists
+ if (!(testBit(0) || m.testBit(0))) {
+ throw new ArithmeticException("BigInteger not invertible.");
+ }
+ if (m.isOne()) {
+ return ZERO;
+ }
+
+ // From now on: (m > 1)
+ TBigInteger res = TDivision.modInverseMontgomery(abs().mod(m), m);
+ if (res.sign == 0) {
+ throw new ArithmeticException("BigInteger not invertible.");
+ }
+
+ res = ((sign < 0) ? m.subtract(res) : res);
+ return res;
+
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this^exponent mod
+ * m}. The modulus {@code m} must be positive. The result is guaranteed to
+ * be in the interval {@code [0, m)} (0 inclusive, m exclusive). If the
+ * exponent is negative, then {@code this.modInverse(m)^(-exponent) mod m)}
+ * is computed. The inverse of this only exists if {@code this} is
+ * relatively prime to m, otherwise an exception is thrown.
+ *
+ * @param exponent
+ * the exponent.
+ * @param m
+ * the modulus.
+ * @return {@code this^exponent mod val}.
+ * @throws NullPointerException
+ * if {@code m == null} or {@code exponent == null}.
+ * @throws ArithmeticException
+ * if {@code m < 0} or if {@code exponent<0} and this is not
+ * relatively prime to {@code m}.
+ */
+ public TBigInteger modPow(TBigInteger exponent, TBigInteger m) {
+ if (m.sign <= 0) {
+ throw new ArithmeticException("BigInteger: modulus not positive");
+ }
+ TBigInteger base = this;
+
+ if (m.isOne() | (exponent.sign > 0 & base.sign == 0)) {
+ return TBigInteger.ZERO;
+ }
+ if (exponent.sign == 0) {
+ return TBigInteger.ONE.mod(m);
+ }
+ if (exponent.sign < 0) {
+ base = modInverse(m);
+ exponent = exponent.negate();
+ }
+ // From now on: (m > 0) and (exponent >= 0)
+ TBigInteger res = (m.testBit(0)) ? TDivision.oddModPow(base.abs(), exponent, m) : TDivision.evenModPow(
+ base.abs(), exponent, m);
+ if ((base.sign < 0) && exponent.testBit(0)) {
+ // -b^e mod m == ((-1 mod m) * (b^e mod m)) mod m
+ res = m.subtract(TBigInteger.ONE).multiply(res).mod(m);
+ }
+ // else exponent is even, so base^exp is positive
+ return res;
+ }
+
+ /**
+ * Returns a new {@code BigInteger} whose value is {@code this mod m}. The
+ * modulus {@code m} must be positive. The result is guaranteed to be in the
+ * interval {@code [0, m)} (0 inclusive, m exclusive). The behavior of this
+ * function is not equivalent to the behavior of the % operator defined for
+ * the built-in {@code int}'s.
+ *
+ * @param m
+ * the modulus.
+ * @return {@code this mod m}.
+ * @throws NullPointerException
+ * if {@code m == null}.
+ * @throws ArithmeticException
+ * if {@code m < 0}.
+ */
+ public TBigInteger mod(TBigInteger m) {
+ if (m.sign <= 0) {
+ throw new ArithmeticException("BigInteger: modulus not positive");
+ }
+ TBigInteger rem = remainder(m);
+ return rem.sign < 0 ? rem.add(m) : rem;
+ }
+
+ /**
+ * Tests whether this {@code BigInteger} is probably prime. If {@code true}
+ * is returned, then this is prime with a probability beyond
+ * (1-1/2^certainty). If {@code false} is returned, then this is definitely
+ * composite. If the argument {@code certainty} <= 0, then this method
+ * returns true.
+ *
+ * @param certainty
+ * tolerated primality uncertainty.
+ * @return {@code true}, if {@code this} is probably prime, {@code false}
+ * otherwise.
+ */
+ public boolean isProbablePrime(int certainty) {
+ return TPrimality.isProbablePrime(abs(), certainty);
+ }
+
+ /**
+ * Returns the smallest integer x > {@code this} which is probably prime as
+ * a {@code BigInteger} instance. The probability that the returned
+ * {@code BigInteger} is prime is beyond (1-1/2^80).
+ *
+ * @return smallest integer > {@code this} which is robably prime.
+ * @throws ArithmeticException
+ * if {@code this < 0}.
+ */
+ public TBigInteger nextProbablePrime() {
+ if (sign < 0) {
+ throw new ArithmeticException("start < 0: " + this);
+ }
+ return TPrimality.nextProbablePrime(this);
+ }
+
+ /**
+ * Returns a random positive {@code BigInteger} instance in the range [0,
+ * 2^(bitLength)-1] which is probably prime. The probability that the
+ * returned {@code BigInteger} is prime is beyond (1-1/2^80).
+ *
+ * Implementation Note: Currently {@code rnd} is ignored.
+ *
+ * @param bitLength
+ * length of the new {@code BigInteger} in bits.
+ * @param rnd
+ * random generator used to generate the new {@code BigInteger}.
+ * @return probably prime random {@code BigInteger} instance.
+ * @throws IllegalArgumentException
+ * if {@code bitLength < 2}.
+ */
+ public static TBigInteger probablePrime(int bitLength, Random rnd) {
+ return new TBigInteger(bitLength, 100, rnd);
+ }
+
+ /* Private Methods */
+
+ /** Decreases {@code numberLength} if there are zero high elements. */
+ final void cutOffLeadingZeroes() {
+ while ((numberLength > 0) && (digits[--numberLength] == 0)) {
+ // Empty
+ }
+ if (digits[numberLength++] == 0) {
+ sign = 0;
+ }
+ }
+
+ /** Tests if {@code this.abs()} is equals to {@code ONE} */
+ boolean isOne() {
+ return ((numberLength == 1) && (digits[0] == 1));
+ }
+
+ /**
+ * Puts a big-endian byte array into a little-endian int array.
+ */
+ private void putBytesPositiveToIntegers(byte[] byteValues) {
+ int bytesLen = byteValues.length;
+ int highBytes = bytesLen & 3;
+ numberLength = (bytesLen >> 2) + ((highBytes == 0) ? 0 : 1);
+ digits = new int[numberLength];
+ int i = 0;
+ // Put bytes to the int array starting from the end of the byte array
+ while (bytesLen > highBytes) {
+ digits[i++] = (byteValues[--bytesLen] & 0xFF) | (byteValues[--bytesLen] & 0xFF) << 8 |
+ (byteValues[--bytesLen] & 0xFF) << 16 | (byteValues[--bytesLen] & 0xFF) << 24;
+ }
+ // Put the first bytes in the highest element of the int array
+ for (int j = 0; j < bytesLen; j++) {
+ digits[i] = (digits[i] << 8) | (byteValues[j] & 0xFF);
+ }
+ }
+
+ /**
+ * Puts a big-endian byte array into a little-endian applying two
+ * complement.
+ */
+ private void putBytesNegativeToIntegers(byte[] byteValues) {
+ int bytesLen = byteValues.length;
+ int highBytes = bytesLen & 3;
+ numberLength = (bytesLen >> 2) + ((highBytes == 0) ? 0 : 1);
+ digits = new int[numberLength];
+ int i = 0;
+ // Setting the sign
+ digits[numberLength - 1] = -1;
+ // Put bytes to the int array starting from the end of the byte array
+ while (bytesLen > highBytes) {
+ digits[i] = (byteValues[--bytesLen] & 0xFF) | (byteValues[--bytesLen] & 0xFF) << 8 |
+ (byteValues[--bytesLen] & 0xFF) << 16 | (byteValues[--bytesLen] & 0xFF) << 24;
+ if (digits[i] != 0) {
+ digits[i] = -digits[i];
+ firstNonzeroDigit = i;
+ i++;
+ while (bytesLen > highBytes) {
+ digits[i] = (byteValues[--bytesLen] & 0xFF) | (byteValues[--bytesLen] & 0xFF) << 8 |
+ (byteValues[--bytesLen] & 0xFF) << 16 | (byteValues[--bytesLen] & 0xFF) << 24;
+ digits[i] = ~digits[i];
+ i++;
+ }
+ break;
+ }
+ i++;
+ }
+ if (highBytes != 0) {
+ // Put the first bytes in the highest element of the int array
+ if (firstNonzeroDigit != -2) {
+ for (int j = 0; j < bytesLen; j++) {
+ digits[i] = (digits[i] << 8) | (byteValues[j] & 0xFF);
+ }
+ digits[i] = ~digits[i];
+ } else {
+ for (int j = 0; j < bytesLen; j++) {
+ digits[i] = (digits[i] << 8) | (byteValues[j] & 0xFF);
+ }
+ digits[i] = -digits[i];
+ }
+ }
+ }
+
+ int getFirstNonzeroDigit() {
+ if (firstNonzeroDigit == -2) {
+ int i;
+ if (this.sign == 0) {
+ i = -1;
+ } else {
+ for (i = 0; digits[i] == 0; i++) {
+ // Empty
+ }
+ }
+ firstNonzeroDigit = i;
+ }
+ return firstNonzeroDigit;
+ }
+
+ /*
+ * Returns a copy of the current instance to achieve immutability
+ */
+ TBigInteger copy() {
+ int[] copyDigits = new int[numberLength];
+ System.arraycopy(digits, 0, copyDigits, 0, numberLength);
+ return new TBigInteger(sign, numberLength, copyDigits);
+ }
+
+ void unCache() {
+ firstNonzeroDigit = -2;
+ }
+
+ static TBigInteger getPowerOfTwo(int exp) {
+ if (exp < TWO_POWS.length) {
+ return TWO_POWS[exp];
+ }
+ int intCount = exp >> 5;
+ int bitN = exp & 31;
+ int resDigits[] = new int[intCount + 1];
+ resDigits[intCount] = 1 << bitN;
+ return new TBigInteger(1, intCount + 1, resDigits);
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBitLevel.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBitLevel.java
new file mode 100644
index 000000000..653ba3409
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBitLevel.java
@@ -0,0 +1,358 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+/**
+ * Static library that provides all the bit level operations for
+ * {@link TBigInteger}. The operations are:
+ *
+ * - Left Shifting
+ * - Right Shifting
+ * - Bit clearing
+ * - Bit setting
+ * - Bit counting
+ * - Bit testing
+ * - Getting of the lowest bit set
+ *
+ * All operations are provided in immutable way, and some in both mutable and
+ * immutable.
+ */
+class TBitLevel {
+
+ /** Just to denote that this class can't be instantiated. */
+ private TBitLevel() {
+ }
+
+ /** @see TBigInteger#bitLength() */
+ static int bitLength(TBigInteger val) {
+ if (val.sign == 0) {
+ return 0;
+ }
+ int bLength = (val.numberLength << 5);
+ int highDigit = val.digits[val.numberLength - 1];
+
+ if (val.sign < 0) {
+ int i = val.getFirstNonzeroDigit();
+ // We reduce the problem to the positive case.
+ if (i == val.numberLength - 1) {
+ highDigit--;
+ }
+ }
+ // Subtracting all sign bits
+ bLength -= Integer.numberOfLeadingZeros(highDigit);
+ return bLength;
+ }
+
+ /** @see TBigInteger#bitCount() */
+ static int bitCount(TBigInteger val) {
+ int bCount = 0;
+
+ if (val.sign == 0) {
+ return 0;
+ }
+
+ int i = val.getFirstNonzeroDigit();
+ if (val.sign > 0) {
+ for (; i < val.numberLength; i++) {
+ bCount += Integer.bitCount(val.digits[i]);
+ }
+ } else {// (sign < 0)
+ // this digit absorbs the carry
+ bCount += Integer.bitCount(-val.digits[i]);
+ for (i++; i < val.numberLength; i++) {
+ bCount += Integer.bitCount(~val.digits[i]);
+ }
+ // We take the complement sum:
+ bCount = (val.numberLength << 5) - bCount;
+ }
+ return bCount;
+ }
+
+ /**
+ * Performs a fast bit testing for positive numbers. The bit to to be tested
+ * must be in the range {@code [0, val.bitLength()-1]}
+ */
+ static boolean testBit(TBigInteger val, int n) {
+ // PRE: 0 <= n < val.bitLength()
+ return ((val.digits[n >> 5] & (1 << (n & 31))) != 0);
+ }
+
+ /**
+ * Check if there are 1s in the lowest bits of this BigInteger
+ *
+ * @param numberOfBits
+ * the number of the lowest bits to check
+ * @return false if all bits are 0s, true otherwise
+ */
+ static boolean nonZeroDroppedBits(int numberOfBits, int digits[]) {
+ int intCount = numberOfBits >> 5;
+ int bitCount = numberOfBits & 31;
+ int i;
+
+ for (i = 0; (i < intCount) && (digits[i] == 0); i++) {
+ // do nothing
+ }
+ return ((i != intCount) || (digits[i] << (32 - bitCount) != 0));
+ }
+
+ /** @see TBigInteger#shiftLeft(int) */
+ static TBigInteger shiftLeft(TBigInteger source, int count) {
+ int intCount = count >> 5;
+ count &= 31; // %= 32
+ int resLength = source.numberLength + intCount + ((count == 0) ? 0 : 1);
+ int resDigits[] = new int[resLength];
+
+ shiftLeft(resDigits, source.digits, intCount, count);
+ TBigInteger result = new TBigInteger(source.sign, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /**
+ * Performs {@code val <<= count}.
+ */
+ // val should have enough place (and one digit more)
+ static void inplaceShiftLeft(TBigInteger val, int count) {
+ int intCount = count >> 5; // count of integers
+ val.numberLength += intCount +
+ (Integer.numberOfLeadingZeros(val.digits[val.numberLength - 1]) - (count & 31) >= 0 ? 0 : 1);
+ shiftLeft(val.digits, val.digits, intCount, count & 31);
+ val.cutOffLeadingZeroes();
+ val.unCache();
+ }
+
+ /**
+ * Abstractly shifts left an array of integers in little endian (i.e. shift
+ * it right). Total shift distance in bits is intCount * 32 + count
+ *
+ * @param result
+ * the destination array
+ * @param source
+ * the source array
+ * @param intCount
+ * the shift distance in integers
+ * @param count
+ * an additional shift distance in bits
+ */
+ static void shiftLeft(int result[], int source[], int intCount, int count) {
+ if (count == 0) {
+ System.arraycopy(source, 0, result, intCount, result.length - intCount);
+ } else {
+ int rightShiftCount = 32 - count;
+
+ result[result.length - 1] = 0;
+ for (int i = result.length - 1; i > intCount; i--) {
+ result[i] |= source[i - intCount - 1] >>> rightShiftCount;
+ result[i - 1] = source[i - intCount - 1] << count;
+ }
+ }
+
+ for (int i = 0; i < intCount; i++) {
+ result[i] = 0;
+ }
+ }
+
+ /**
+ * Shifts the source digits left one bit, creating a value whose magnitude
+ * is doubled.
+ *
+ * @param result
+ * an array of digits that will hold the computed result when
+ * this method returns. The size of this array is
+ * {@code srcLen + 1}, and the format is the same as
+ * {@link TBigInteger#digits}.
+ * @param source
+ * the array of digits to shift left, in the same format as
+ * {@link TBigInteger#digits}.
+ * @param srcLen
+ * the length of {@code source}; may be less than
+ * {@code source.length}
+ */
+ static void shiftLeftOneBit(int result[], int source[], int srcLen) {
+ int carry = 0;
+ for (int i = 0; i < srcLen; i++) {
+ int val = source[i];
+ result[i] = (val << 1) | carry;
+ carry = val >>> 31;
+ }
+ if (carry != 0) {
+ result[srcLen] = carry;
+ }
+ }
+
+ static TBigInteger shiftLeftOneBit(TBigInteger source) {
+ int srcLen = source.numberLength;
+ int resLen = srcLen + 1;
+ int resDigits[] = new int[resLen];
+ shiftLeftOneBit(resDigits, source.digits, srcLen);
+ TBigInteger result = new TBigInteger(source.sign, resLen, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @see TBigInteger#shiftRight(int) */
+ static TBigInteger shiftRight(TBigInteger source, int count) {
+ int intCount = count >> 5; // count of integers
+ count &= 31; // count of remaining bits
+ if (intCount >= source.numberLength) {
+ return ((source.sign < 0) ? TBigInteger.MINUS_ONE : TBigInteger.ZERO);
+ }
+ int i;
+ int resLength = source.numberLength - intCount;
+ int resDigits[] = new int[resLength + 1];
+
+ shiftRight(resDigits, resLength, source.digits, intCount, count);
+ if (source.sign < 0) {
+ // Checking if the dropped bits are zeros (the remainder equals to
+ // 0)
+ for (i = 0; (i < intCount) && (source.digits[i] == 0); i++) {
+ // do nothing
+ }
+ // If the remainder is not zero, add 1 to the result
+ if ((i < intCount) || ((count > 0) && ((source.digits[i] << (32 - count)) != 0))) {
+ for (i = 0; (i < resLength) && (resDigits[i] == -1); i++) {
+ resDigits[i] = 0;
+ }
+ if (i == resLength) {
+ resLength++;
+ }
+ resDigits[i]++;
+ }
+ }
+ TBigInteger result = new TBigInteger(source.sign, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /**
+ * Performs {@code val >>= count} where {@code val} is a positive number.
+ */
+ static void inplaceShiftRight(TBigInteger val, int count) {
+ int sign = val.signum();
+ if (count == 0 || val.signum() == 0)
+ return;
+ int intCount = count >> 5; // count of integers
+ val.numberLength -= intCount;
+ if (!shiftRight(val.digits, val.numberLength, val.digits, intCount, count & 31) && sign < 0) {
+ // remainder not zero: add one to the result
+ int i;
+ for (i = 0; (i < val.numberLength) && (val.digits[i] == -1); i++) {
+ val.digits[i] = 0;
+ }
+ if (i == val.numberLength) {
+ val.numberLength++;
+ }
+ val.digits[i]++;
+ }
+ val.cutOffLeadingZeroes();
+ val.unCache();
+ }
+
+ /**
+ * Shifts right an array of integers. Total shift distance in bits is
+ * intCount * 32 + count.
+ *
+ * @param result
+ * the destination array
+ * @param resultLen
+ * the destination array's length
+ * @param source
+ * the source array
+ * @param intCount
+ * the number of elements to be shifted
+ * @param count
+ * the number of bits to be shifted
+ * @return dropped bit's are all zero (i.e. remaider is zero)
+ */
+ static boolean shiftRight(int result[], int resultLen, int source[], int intCount, int count) {
+ int i;
+ boolean allZero = true;
+ for (i = 0; i < intCount; i++) {
+ allZero &= source[i] == 0;
+ }
+ if (count == 0) {
+ System.arraycopy(source, intCount, result, 0, resultLen);
+ i = resultLen;
+ } else {
+ int leftShiftCount = 32 - count;
+
+ allZero &= (source[i] << leftShiftCount) == 0;
+ for (i = 0; i < resultLen - 1; i++) {
+ result[i] = (source[i + intCount] >>> count) | (source[i + intCount + 1] << leftShiftCount);
+ }
+ result[i] = (source[i + intCount] >>> count);
+ i++;
+ }
+
+ return allZero;
+ }
+
+ /**
+ * Performs a flipBit on the BigInteger, returning a BigInteger with the the
+ * specified bit flipped.
+ *
+ * @param intCount
+ * : the index of the element of the digits array where the
+ * operation will be performed
+ * @param bitNumber
+ * : the bit's position in the intCount element
+ */
+ static TBigInteger flipBit(TBigInteger val, int n) {
+ int resSign = (val.sign == 0) ? 1 : val.sign;
+ int intCount = n >> 5;
+ int bitN = n & 31;
+ int resLength = Math.max(intCount + 1, val.numberLength) + 1;
+ int resDigits[] = new int[resLength];
+ int i;
+
+ int bitNumber = 1 << bitN;
+ System.arraycopy(val.digits, 0, resDigits, 0, val.numberLength);
+
+ if (val.sign < 0) {
+ if (intCount >= val.numberLength) {
+ resDigits[intCount] = bitNumber;
+ } else {
+ // val.sign<0 y intCount < val.numberLength
+ int firstNonZeroDigit = val.getFirstNonzeroDigit();
+ if (intCount > firstNonZeroDigit) {
+ resDigits[intCount] ^= bitNumber;
+ } else if (intCount < firstNonZeroDigit) {
+ resDigits[intCount] = -bitNumber;
+ for (i = intCount + 1; i < firstNonZeroDigit; i++) {
+ resDigits[i] = -1;
+ }
+ resDigits[i] = resDigits[i]--;
+ } else {
+ i = intCount;
+ resDigits[i] = -((-resDigits[intCount]) ^ bitNumber);
+ if (resDigits[i] == 0) {
+ for (i++; resDigits[i] == -1; i++) {
+ resDigits[i] = 0;
+ }
+ resDigits[i]++;
+ }
+ }
+ }
+ } else {// case where val is positive
+ resDigits[intCount] ^= bitNumber;
+ }
+ TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TConversion.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TConversion.java
new file mode 100644
index 000000000..2018b5e5d
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TConversion.java
@@ -0,0 +1,440 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+/**
+ * Static library that provides {@link TBigInteger} base conversion from/to any
+ * integer represented in an {@link java.lang.String} Object.
+ */
+class TConversion {
+
+ /** Just to denote that this class can't be instantiated */
+ private TConversion() {}
+
+ /**
+ * Holds the maximal exponent for each radix, so that radixdigitFitInInt[radix]
+ * fit in an {@code int} (32 bits).
+ */
+ static final int[] digitFitInInt = { -1, -1, 31, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,
+ 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5 };
+
+ /**
+ * bigRadices values are precomputed maximal powers of radices (integer
+ * numbers from 2 to 36) that fit into unsigned int (32 bits). bigRadices[0] =
+ * 2 ^ 31, bigRadices[8] = 10 ^ 9, etc.
+ */
+
+ static final int bigRadices[] = { -2147483648, 1162261467, 1073741824, 1220703125, 362797056, 1977326743,
+ 1073741824, 387420489, 1000000000, 214358881, 429981696, 815730721, 1475789056, 170859375, 268435456,
+ 410338673, 612220032, 893871739, 1280000000, 1801088541, 113379904, 148035889, 191102976, 244140625,
+ 308915776, 387420489, 481890304, 594823321, 729000000, 887503681, 1073741824, 1291467969, 1544804416,
+ 1838265625, 60466176 };
+
+
+ /** @see TBigInteger#toString(int) */
+ static String bigInteger2String(TBigInteger val, int radix) {
+ int sign = val.sign;
+ int numberLength = val.numberLength;
+ int digits[] = val.digits;
+
+ if (sign == 0) {
+ return "0";
+ }
+ if (numberLength == 1) {
+ int highDigit = digits[numberLength - 1];
+ long v = highDigit & 0xFFFFFFFFL;
+ if (sign < 0) {
+ v = -v;
+ }
+ return Long.toString(v, radix);
+ }
+ if (radix == 10 || radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+ return val.toString();
+ }
+ double bitsForRadixDigit;
+ bitsForRadixDigit = Math.log(radix) / Math.log(2);
+ int resLengthInChars = (int) (val.abs().bitLength() / bitsForRadixDigit + ((sign < 0) ? 1 : 0)) + 1;
+
+ char result[] = new char[resLengthInChars];
+ int currentChar = resLengthInChars;
+ int resDigit;
+ if (radix != 16) {
+ int temp[] = new int[numberLength];
+ System.arraycopy(digits, 0, temp, 0, numberLength);
+ int tempLen = numberLength;
+ int charsPerInt = digitFitInInt[radix];
+ int i;
+ // get the maximal power of radix that fits in int
+ int bigRadix = bigRadices[radix - 2];
+ while (true) {
+ // divide the array of digits by bigRadix and convert remainders
+ // to characters collecting them in the char array
+ resDigit = TDivision.divideArrayByInt(temp, temp, tempLen, bigRadix);
+ int previous = currentChar;
+ do {
+ result[--currentChar] = Character.forDigit(resDigit % radix, radix);
+ } while (((resDigit /= radix) != 0) && (currentChar != 0));
+ int delta = charsPerInt - previous + currentChar;
+ for (i = 0; i < delta && currentChar > 0; i++) {
+ result[--currentChar] = '0';
+ }
+ for (i = tempLen - 1; (i > 0) && (temp[i] == 0); i--) {
+ // do nothing
+ }
+ tempLen = i + 1;
+ if ((tempLen == 1) && (temp[0] == 0)) { // the quotient is 0
+ break;
+ }
+ }
+ } else {
+ // radix == 16
+ for (int i = 0; i < numberLength; i++) {
+ for (int j = 0; (j < 8) && (currentChar > 0); j++) {
+ resDigit = digits[i] >> (j << 2) & 0xf;
+ result[--currentChar] = Character.forDigit(resDigit, 16);
+ }
+ }
+ }
+ while (result[currentChar] == '0') {
+ currentChar++;
+ }
+ if (sign == -1) {
+ result[--currentChar] = '-';
+ }
+ return new String(result, currentChar, resLengthInChars - currentChar);
+ }
+
+ /**
+ * Builds the correspondent {@code String} representation of {@code val}
+ * being scaled by {@code scale}.
+ *
+ * @see TBigInteger#toString()
+ * @see TBigDecimal#toString()
+ */
+ static String toDecimalScaledString(TBigInteger val, int scale) {
+ int sign = val.sign;
+ int numberLength = val.numberLength;
+ int digits[] = val.digits;
+ int resLengthInChars;
+ int currentChar;
+ char result[];
+
+ if (sign == 0) {
+ switch (scale) {
+ case 0:
+ return "0";
+ case 1:
+ return "0.0";
+ case 2:
+ return "0.00";
+ case 3:
+ return "0.000";
+ case 4:
+ return "0.0000";
+ case 5:
+ return "0.00000";
+ case 6:
+ return "0.000000";
+ default:
+ StringBuilder result1 = new StringBuilder();
+ if (scale < 0) {
+ result1.append("0E+");
+ } else {
+ result1.append("0E");
+ }
+ result1.append(-scale);
+ return result1.toString();
+ }
+ }
+ // one 32-bit unsigned value may contains 10 decimal digits
+ resLengthInChars = numberLength * 10 + 1 + 7;
+ // Explanation why +1+7:
+ // +1 - one char for sign if needed.
+ // +7 - For "special case 2" (see below) we have 7 free chars for
+ // inserting necessary scaled digits.
+ result = new char[resLengthInChars + 1];
+ // allocated [resLengthInChars+1] characters.
+ // a free latest character may be used for "special case 1" (see
+ // below)
+ currentChar = resLengthInChars;
+ if (numberLength == 1) {
+ int highDigit = digits[0];
+ if (highDigit < 0) {
+ long v = highDigit & 0xFFFFFFFFL;
+ do {
+ long prev = v;
+ v /= 10;
+ result[--currentChar] = (char) (0x0030 + ((int) (prev - v * 10)));
+ } while (v != 0);
+ } else {
+ int v = highDigit;
+ do {
+ int prev = v;
+ v /= 10;
+ result[--currentChar] = (char) (0x0030 + (prev - v * 10));
+ } while (v != 0);
+ }
+ } else {
+ int temp[] = new int[numberLength];
+ int tempLen = numberLength;
+ System.arraycopy(digits, 0, temp, 0, tempLen);
+ BIG_LOOP: while (true) {
+ // divide the array of digits by bigRadix and convert
+ // remainders
+ // to characters collecting them in the char array
+ long result11 = 0;
+ for (int i1 = tempLen - 1; i1 >= 0; i1--) {
+ long temp1 = (result11 << 32) + (temp[i1] & 0xFFFFFFFFL);
+ long res = divideLongByBillion(temp1);
+ temp[i1] = (int) res;
+ result11 = (int) (res >> 32);
+ }
+ int resDigit = (int) result11;
+ int previous = currentChar;
+ do {
+ result[--currentChar] = (char) (0x0030 + (resDigit % 10));
+ } while ((resDigit /= 10) != 0 && currentChar != 0);
+ int delta = 9 - previous + currentChar;
+ for (int i = 0; (i < delta) && (currentChar > 0); i++) {
+ result[--currentChar] = '0';
+ }
+ int j = tempLen - 1;
+ for (; temp[j] == 0; j--) {
+ if (j == 0) { // means temp[0] == 0
+ break BIG_LOOP;
+ }
+ }
+ tempLen = j + 1;
+ }
+ while (result[currentChar] == '0') {
+ currentChar++;
+ }
+ }
+ boolean negNumber = (sign < 0);
+ int exponent = resLengthInChars - currentChar - scale - 1;
+ if (scale == 0) {
+ if (negNumber) {
+ result[--currentChar] = '-';
+ }
+ return new String(result, currentChar, resLengthInChars - currentChar);
+ }
+ if ((scale > 0) && (exponent >= -6)) {
+ if (exponent >= 0) {
+ // special case 1
+ int insertPoint = currentChar + exponent;
+ for (int j = resLengthInChars - 1; j >= insertPoint; j--) {
+ result[j + 1] = result[j];
+ }
+ result[++insertPoint] = '.';
+ if (negNumber) {
+ result[--currentChar] = '-';
+ }
+ return new String(result, currentChar, resLengthInChars - currentChar + 1);
+ }
+ // special case 2
+ for (int j = 2; j < -exponent + 1; j++) {
+ result[--currentChar] = '0';
+ }
+ result[--currentChar] = '.';
+ result[--currentChar] = '0';
+ if (negNumber) {
+ result[--currentChar] = '-';
+ }
+ return new String(result, currentChar, resLengthInChars - currentChar);
+ }
+ int startPoint = currentChar + 1;
+ int endPoint = resLengthInChars;
+ StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
+ if (negNumber) {
+ result1.append('-');
+ }
+ if (endPoint - startPoint >= 1) {
+ result1.append(result[currentChar]);
+ result1.append('.');
+ result1.append(result, currentChar + 1, resLengthInChars - currentChar - 1);
+ } else {
+ result1.append(result, currentChar, resLengthInChars - currentChar);
+ }
+ result1.append('E');
+ if (exponent > 0) {
+ result1.append('+');
+ }
+ result1.append(Integer.toString(exponent));
+ return result1.toString();
+ }
+
+ /* can process only 32-bit numbers */
+ static String toDecimalScaledString(long value, int scale) {
+ int resLengthInChars;
+ int currentChar;
+ char result[];
+ boolean negNumber = value < 0;
+ if(negNumber) {
+ value = -value;
+ }
+ if (value == 0) {
+ switch (scale) {
+ case 0: return "0";
+ case 1: return "0.0";
+ case 2: return "0.00";
+ case 3: return "0.000";
+ case 4: return "0.0000";
+ case 5: return "0.00000";
+ case 6: return "0.000000";
+ default:
+ StringBuilder result1 = new StringBuilder();
+ if (scale < 0) {
+ result1.append("0E+");
+ } else {
+ result1.append("0E");
+ }
+ result1.append((scale == Integer.MIN_VALUE) ? "2147483648" : Integer.toString(-scale));
+ return result1.toString();
+ }
+ }
+ // one 32-bit unsigned value may contains 10 decimal digits
+ resLengthInChars = 18;
+ // Explanation why +1+7:
+ // +1 - one char for sign if needed.
+ // +7 - For "special case 2" (see below) we have 7 free chars for
+ // inserting necessary scaled digits.
+ result = new char[resLengthInChars+1];
+ // Allocated [resLengthInChars+1] characters.
+ // a free latest character may be used for "special case 1" (see below)
+ currentChar = resLengthInChars;
+ long v = value;
+ do {
+ long prev = v;
+ v /= 10;
+ result[--currentChar] = (char) (0x0030 + (prev - v * 10));
+ } while (v != 0);
+
+ long exponent = (long)resLengthInChars - (long)currentChar - scale - 1L;
+ if (scale == 0) {
+ if (negNumber) {
+ result[--currentChar] = '-';
+ }
+ return new String(result, currentChar, resLengthInChars - currentChar);
+ }
+ if (scale > 0 && exponent >= -6) {
+ if (exponent >= 0) {
+ // special case 1
+ int insertPoint = currentChar + (int)exponent;
+ for(int j = resLengthInChars - 1; j >= insertPoint; j--) {
+ result[j + 1] = result[j];
+ }
+ result[++insertPoint] = '.';
+ if (negNumber) {
+ result[--currentChar] = '-';
+ }
+ return new String(result, currentChar, resLengthInChars - currentChar + 1);
+ }
+ // special case 2
+ for (int j = 2; j < -exponent + 1; j++) {
+ result[--currentChar] = '0';
+ }
+ result[--currentChar] = '.';
+ result[--currentChar] = '0';
+ if (negNumber) {
+ result[--currentChar] = '-';
+ }
+ return new String(result, currentChar, resLengthInChars - currentChar);
+ }
+ int startPoint = currentChar + 1;
+ int endPoint = resLengthInChars;
+ StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
+ if (negNumber) {
+ result1.append('-');
+ }
+ if (endPoint - startPoint >= 1) {
+ result1.append(result[currentChar]);
+ result1.append('.');
+ result1.append(result, currentChar+1, resLengthInChars - currentChar-1);
+ } else {
+ result1.append(result, currentChar, resLengthInChars - currentChar);
+ }
+ result1.append('E');
+ if (exponent > 0) {
+ result1.append('+');
+ }
+ result1.append(Long.toString(exponent));
+ return result1.toString();
+ }
+
+ static long divideLongByBillion(long a) {
+ long quot;
+ long rem;
+
+ if (a >= 0) {
+ long bLong = 1000000000L;
+ quot = (a / bLong);
+ rem = (a % bLong);
+ } else {
+ /*
+ * Make the dividend positive shifting it right by 1 bit then get
+ * the quotient an remainder and correct them properly
+ */
+ long aPos = a >>> 1;
+ long bPos = 1000000000L >>> 1;
+ quot = aPos / bPos;
+ rem = aPos % bPos;
+ // double the remainder and add 1 if 'a' is odd
+ rem = (rem << 1) + (a & 1);
+ }
+ return (rem << 32) | (quot & 0xFFFFFFFFL);
+ }
+
+ /** @see TBigInteger#doubleValue() */
+ static double bigInteger2Double(TBigInteger val) {
+ // val.bitLength() < 64
+ if ((val.numberLength < 2) || ((val.numberLength == 2) && (val.digits[1] > 0))) {
+ return val.longValue();
+ }
+ // val.bitLength() >= 33 * 32 > 1024
+ if (val.numberLength > 32) {
+ return val.sign > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+ }
+ int bitLen = val.abs().bitLength();
+ long exponent = bitLen - 1;
+ int delta = bitLen - 54;
+ // We need 54 top bits from this, the 53th bit is always 1 in lVal.
+ long lVal = val.abs().shiftRight(delta).longValue();
+ /*
+ * Take 53 bits from lVal to mantissa. The least significant bit is
+ * needed for rounding.
+ */
+ long mantissa = lVal & 0x1FFFFFFFFFFFFFL;
+ if (exponent == 1023) {
+ if (mantissa == 0X1FFFFFFFFFFFFFL) {
+ return val.sign > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+ }
+ if (mantissa == 0x1FFFFFFFFFFFFEL) {
+ return val.sign > 0 ? Double.MAX_VALUE : -Double.MAX_VALUE;
+ }
+ }
+ // Round the mantissa
+ if ((mantissa & 1) == 1 && (mantissa & 2) == 2 || TBitLevel.nonZeroDroppedBits(delta, val.digits)) {
+ mantissa += 2;
+ }
+ mantissa >>= 1; // drop the rounding bit
+ long resSign = (val.sign < 0) ? 0x8000000000000000L : 0;
+ exponent = ((1023 + exponent) << 52) & 0x7FF0000000000000L;
+ long result = resSign | exponent | mantissa;
+ return Double.longBitsToDouble(result);
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TDivision.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TDivision.java
new file mode 100644
index 000000000..240e28395
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TDivision.java
@@ -0,0 +1,952 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+/**
+ * Static library that provides all operations related with division and modular
+ * arithmetic to {@link TBigInteger}. Some methods are provided in both mutable
+ * and immutable way. There are several variants provided listed below:
+ *
+ *
+ * - Division
+ *
+ * - {@link TBigInteger} division and remainder by {@link TBigInteger}.
+ * - {@link TBigInteger} division and remainder by {@code int}.
+ * - gcd between {@link TBigInteger} numbers.
+ *
+ *
+ * - Modular arithmetic
+ *
+ * - Modular exponentiation between {@link TBigInteger} numbers.
+ * - Modular inverse of a {@link TBigInteger} numbers.
+ *
+ *
+ *
+ */
+class TDivision {
+
+ /**
+ * Divides the array 'a' by the array 'b' and gets the quotient and the
+ * remainder. Implements the Knuth's division algorithm. See D. Knuth, The
+ * Art of Computer Programming, vol. 2. Steps D1-D8 correspond the steps in
+ * the algorithm description.
+ *
+ * @param quot
+ * the quotient
+ * @param quotLength
+ * the quotient's length
+ * @param a
+ * the dividend
+ * @param aLength
+ * the dividend's length
+ * @param b
+ * the divisor
+ * @param bLength
+ * the divisor's length
+ * @return the remainder
+ */
+ static int[] divide(int quot[], int quotLength, int a[], int aLength, int b[], int bLength) {
+
+ int normA[] = new int[aLength + 1]; // the normalized dividend
+ // an extra byte is needed for correct shift
+ int normB[] = new int[bLength + 1]; // the normalized divisor;
+ int normBLength = bLength;
+ /*
+ * Step D1: normalize a and b and put the results to a1 and b1 the
+ * normalized divisor's first digit must be >= 2^31
+ */
+ int divisorShift = Integer.numberOfLeadingZeros(b[bLength - 1]);
+ if (divisorShift != 0) {
+ TBitLevel.shiftLeft(normB, b, 0, divisorShift);
+ TBitLevel.shiftLeft(normA, a, 0, divisorShift);
+ } else {
+ System.arraycopy(a, 0, normA, 0, aLength);
+ System.arraycopy(b, 0, normB, 0, bLength);
+ }
+ int firstDivisorDigit = normB[normBLength - 1];
+ // Step D2: set the quotient index
+ int i = quotLength - 1;
+ int j = aLength;
+
+ while (i >= 0) {
+ // Step D3: calculate a guess digit guessDigit
+ int guessDigit = 0;
+ if (normA[j] == firstDivisorDigit) {
+ // set guessDigit to the largest unsigned int value
+ guessDigit = -1;
+ } else {
+ long product = (((normA[j] & 0xffffffffL) << 32) + (normA[j - 1] & 0xffffffffL));
+ long res = TDivision.divideLongByInt(product, firstDivisorDigit);
+ guessDigit = (int) res; // the quotient of divideLongByInt
+ int rem = (int) (res >> 32); // the remainder of
+ // divideLongByInt
+ // decrease guessDigit by 1 while leftHand > rightHand
+ if (guessDigit != 0) {
+ long leftHand = 0;
+ long rightHand = 0;
+ boolean rOverflowed = false;
+ guessDigit++; // to have the proper value in the loop
+ // below
+ do {
+ guessDigit--;
+ if (rOverflowed) {
+ break;
+ }
+ // leftHand always fits in an unsigned long
+ leftHand = (guessDigit & 0xffffffffL) * (normB[normBLength - 2] & 0xffffffffL);
+ /*
+ * rightHand can overflow; in this case the loop
+ * condition will be true in the next step of the loop
+ */
+ rightHand = ((long) rem << 32) + (normA[j - 2] & 0xffffffffL);
+ long longR = (rem & 0xffffffffL) + (firstDivisorDigit & 0xffffffffL);
+ /*
+ * checks that longR does not fit in an unsigned int;
+ * this ensures that rightHand will overflow unsigned
+ * long in the next step
+ */
+ if (Integer.numberOfLeadingZeros((int) (longR >>> 32)) < 32) {
+ rOverflowed = true;
+ } else {
+ rem = (int) longR;
+ }
+ } while (((leftHand ^ 0x8000000000000000L) > (rightHand ^ 0x8000000000000000L)));
+ }
+ }
+ // Step D4: multiply normB by guessDigit and subtract the production
+ // from normA.
+ if (guessDigit != 0) {
+ int borrow = TDivision.multiplyAndSubtract(normA, j - normBLength, normB, normBLength, guessDigit);
+ // Step D5: check the borrow
+ if (borrow != 0) {
+ // Step D6: compensating addition
+ guessDigit--;
+ long carry = 0;
+ for (int k = 0; k < normBLength; k++) {
+ carry += (normA[j - normBLength + k] & 0xffffffffL) + (normB[k] & 0xffffffffL);
+ normA[j - normBLength + k] = (int) carry;
+ carry >>>= 32;
+ }
+ }
+ }
+ if (quot != null) {
+ quot[i] = guessDigit;
+ }
+ // Step D7
+ j--;
+ i--;
+ }
+ /*
+ * Step D8: we got the remainder in normA. Denormalize it id needed
+ */
+ if (divisorShift != 0) {
+ // reuse normB
+ TBitLevel.shiftRight(normB, normBLength, normA, 0, divisorShift);
+ return normB;
+ }
+ System.arraycopy(normA, 0, normB, 0, bLength);
+ return normA;
+ }
+
+ /**
+ * Divides an array by an integer value. Implements the Knuth's division
+ * algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
+ *
+ * @param dest
+ * the quotient
+ * @param src
+ * the dividend
+ * @param srcLength
+ * the length of the dividend
+ * @param divisor
+ * the divisor
+ * @return remainder
+ */
+ static int divideArrayByInt(int dest[], int src[], final int srcLength, final int divisor) {
+
+ long rem = 0;
+ long bLong = divisor & 0xffffffffL;
+
+ for (int i = srcLength - 1; i >= 0; i--) {
+ long temp = (rem << 32) | (src[i] & 0xffffffffL);
+ long quot;
+ if (temp >= 0) {
+ quot = (temp / bLong);
+ rem = (temp % bLong);
+ } else {
+ /*
+ * make the dividend positive shifting it right by 1 bit then
+ * get the quotient an remainder and correct them properly
+ */
+ long aPos = temp >>> 1;
+ long bPos = divisor >>> 1;
+ quot = aPos / bPos;
+ rem = aPos % bPos;
+ // double the remainder and add 1 if a is odd
+ rem = (rem << 1) + (temp & 1);
+ if ((divisor & 1) != 0) {
+ // the divisor is odd
+ if (quot <= rem) {
+ rem -= quot;
+ } else {
+ if (quot - rem <= bLong) {
+ rem += bLong - quot;
+ quot -= 1;
+ } else {
+ rem += (bLong << 1) - quot;
+ quot -= 2;
+ }
+ }
+ }
+ }
+ dest[i] = (int) (quot & 0xffffffffL);
+ }
+ return (int) rem;
+ }
+
+ /**
+ * Divides an array by an integer value. Implements the Knuth's division
+ * algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
+ *
+ * @param src
+ * the dividend
+ * @param srcLength
+ * the length of the dividend
+ * @param divisor
+ * the divisor
+ * @return remainder
+ */
+ static int remainderArrayByInt(int src[], final int srcLength, final int divisor) {
+
+ long result = 0;
+
+ for (int i = srcLength - 1; i >= 0; i--) {
+ long temp = (result << 32) + (src[i] & 0xffffffffL);
+ long res = divideLongByInt(temp, divisor);
+ result = (int) (res >> 32);
+ }
+ return (int) result;
+ }
+
+ /**
+ * Divides a BigInteger
by a signed int
and
+ * returns the remainder.
+ *
+ * @param dividend
+ * the BigInteger to be divided. Must be non-negative.
+ * @param divisor
+ * a signed int
+ * @return divide % divisor
+ */
+ static int remainder(TBigInteger dividend, int divisor) {
+ return remainderArrayByInt(dividend.digits, dividend.numberLength, divisor);
+ }
+
+ /**
+ * Divides an unsigned long a by an unsigned int b. It is supposed that the
+ * most significant bit of b is set to 1, i.e. b < 0
+ *
+ * @param a
+ * the dividend
+ * @param b
+ * the divisor
+ * @return the long value containing the unsigned integer remainder in the
+ * left half and the unsigned integer quotient in the right half
+ */
+ static long divideLongByInt(long a, int b) {
+ long quot;
+ long rem;
+ long bLong = b & 0xffffffffL;
+
+ if (a >= 0) {
+ quot = (a / bLong);
+ rem = (a % bLong);
+ } else {
+ /*
+ * Make the dividend positive shifting it right by 1 bit then get
+ * the quotient an remainder and correct them properly
+ */
+ long aPos = a >>> 1;
+ long bPos = b >>> 1;
+ quot = aPos / bPos;
+ rem = aPos % bPos;
+ // double the remainder and add 1 if a is odd
+ rem = (rem << 1) + (a & 1);
+ if ((b & 1) != 0) { // the divisor is odd
+ if (quot <= rem) {
+ rem -= quot;
+ } else {
+ if (quot - rem <= bLong) {
+ rem += bLong - quot;
+ quot -= 1;
+ } else {
+ rem += (bLong << 1) - quot;
+ quot -= 2;
+ }
+ }
+ }
+ }
+ return (rem << 32) | (quot & 0xffffffffL);
+ }
+
+ /**
+ * Computes the quotient and the remainder after a division by an
+ * {@code int} number.
+ *
+ * @return an array of the form {@code [quotient, remainder]}.
+ */
+ static TBigInteger[] divideAndRemainderByInteger(TBigInteger val, int divisor, int divisorSign) {
+ // res[0] is a quotient and res[1] is a remainder:
+ int[] valDigits = val.digits;
+ int valLen = val.numberLength;
+ int valSign = val.sign;
+ if (valLen == 1) {
+ long a = (valDigits[0] & 0xffffffffL);
+ long b = (divisor & 0xffffffffL);
+ long quo = a / b;
+ long rem = a % b;
+ if (valSign != divisorSign) {
+ quo = -quo;
+ }
+ if (valSign < 0) {
+ rem = -rem;
+ }
+ return new TBigInteger[] { TBigInteger.valueOf(quo), TBigInteger.valueOf(rem) };
+ }
+ int quotientLength = valLen;
+ int quotientSign = ((valSign == divisorSign) ? 1 : -1);
+ int quotientDigits[] = new int[quotientLength];
+ int remainderDigits[];
+ remainderDigits = new int[] { TDivision.divideArrayByInt(quotientDigits, valDigits, valLen, divisor) };
+ TBigInteger result0 = new TBigInteger(quotientSign, quotientLength, quotientDigits);
+ TBigInteger result1 = new TBigInteger(valSign, 1, remainderDigits);
+ result0.cutOffLeadingZeroes();
+ result1.cutOffLeadingZeroes();
+ return new TBigInteger[] { result0, result1 };
+ }
+
+ /**
+ * Multiplies an array by int and subtracts it from a subarray of another
+ * array.
+ *
+ * @param a
+ * the array to subtract from
+ * @param start
+ * the start element of the subarray of a
+ * @param b
+ * the array to be multiplied and subtracted
+ * @param bLen
+ * the length of b
+ * @param c
+ * the multiplier of b
+ * @return the carry element of subtraction
+ */
+ static int multiplyAndSubtract(int a[], int start, int b[], int bLen, int c) {
+ long carry0 = 0;
+ long carry1 = 0;
+
+ for (int i = 0; i < bLen; i++) {
+ carry0 = TMultiplication.unsignedMultAddAdd(b[i], c, (int) carry0, 0);
+ carry1 = (a[start + i] & 0xffffffffL) - (carry0 & 0xffffffffL) + carry1;
+ a[start + i] = (int) carry1;
+ carry1 >>= 32; // -1 or 0
+ carry0 >>>= 32;
+ }
+
+ carry1 = (a[start + bLen] & 0xffffffffL) - carry0 + carry1;
+ a[start + bLen] = (int) carry1;
+ return (int) (carry1 >> 32); // -1 or 0
+ }
+
+ /**
+ * @param m
+ * a positive modulus Return the greatest common divisor of op1
+ * and op2,
+ *
+ * @param op1
+ * must be greater than zero
+ * @param op2
+ * must be greater than zero
+ * @see TBigInteger#gcd(TBigInteger)
+ * @return {@code GCD(op1, op2)}
+ */
+ static TBigInteger gcdBinary(TBigInteger op1, TBigInteger op2) {
+ // PRE: (op1 > 0) and (op2 > 0)
+
+ /*
+ * Divide both number the maximal possible times by 2 without rounding
+ * gcd(2*a, 2*b) = 2 * gcd(a,b)
+ */
+ int lsb1 = op1.getLowestSetBit();
+ int lsb2 = op2.getLowestSetBit();
+ int pow2Count = Math.min(lsb1, lsb2);
+
+ TBitLevel.inplaceShiftRight(op1, lsb1);
+ TBitLevel.inplaceShiftRight(op2, lsb2);
+
+ TBigInteger swap;
+ // I want op2 > op1
+ if (op1.compareTo(op2) == TBigInteger.GREATER) {
+ swap = op1;
+ op1 = op2;
+ op2 = swap;
+ }
+
+ do { // INV: op2 >= op1 && both are odd unless op1 = 0
+
+ // Optimization for small operands
+ // (op2.bitLength() < 64) implies by INV (op1.bitLength() < 64)
+ if ((op2.numberLength == 1) || ((op2.numberLength == 2) && (op2.digits[1] > 0))) {
+ op2 = TBigInteger.valueOf(TDivision.gcdBinary(op1.longValue(), op2.longValue()));
+ break;
+ }
+
+ // Implements one step of the Euclidean algorithm
+ // To reduce one operand if it's much smaller than the other one
+ if (op2.numberLength > op1.numberLength * 1.2) {
+ op2 = op2.remainder(op1);
+ if (op2.signum() != 0) {
+ TBitLevel.inplaceShiftRight(op2, op2.getLowestSetBit());
+ }
+ } else {
+
+ // Use Knuth's algorithm of successive subtract and shifting
+ do {
+ TElementary.inplaceSubtract(op2, op1); // both are odd
+ TBitLevel.inplaceShiftRight(op2, op2.getLowestSetBit());
+ } while (op2.compareTo(op1) >= TBigInteger.EQUALS);
+ }
+ // now op1 >= op2
+ swap = op2;
+ op2 = op1;
+ op1 = swap;
+ } while (op1.sign != 0);
+ return op2.shiftLeft(pow2Count);
+ }
+
+ /**
+ * Performs the same as {@link #gcdBinary(TBigInteger, TBigInteger)}, but
+ * with numbers of 63 bits, represented in positives values of {@code long}
+ * type.
+ *
+ * @param op1
+ * a positive number
+ * @param op2
+ * a positive number
+ * @see #gcdBinary(TBigInteger, TBigInteger)
+ * @return GCD(op1, op2)
+ */
+ static long gcdBinary(long op1, long op2) {
+ // PRE: (op1 > 0) and (op2 > 0)
+ int lsb1 = Long.numberOfTrailingZeros(op1);
+ int lsb2 = Long.numberOfTrailingZeros(op2);
+ int pow2Count = Math.min(lsb1, lsb2);
+
+ if (lsb1 != 0) {
+ op1 >>>= lsb1;
+ }
+ if (lsb2 != 0) {
+ op2 >>>= lsb2;
+ }
+ do {
+ if (op1 >= op2) {
+ op1 -= op2;
+ op1 >>>= Long.numberOfTrailingZeros(op1);
+ } else {
+ op2 -= op1;
+ op2 >>>= Long.numberOfTrailingZeros(op2);
+ }
+ } while (op1 != 0);
+ return (op2 << pow2Count);
+ }
+
+ /**
+ * Calculates a.modInverse(p) Based on: Savas, E; Koc, C "The Montgomery
+ * Modular Inverse - Revised"
+ */
+ static TBigInteger modInverseMontgomery(TBigInteger a, TBigInteger p) {
+
+ if (a.sign == 0) {
+ // ZERO hasn't inverse
+ throw new ArithmeticException("BigInteger not invertible");
+ }
+
+ if (!p.testBit(0)) {
+ // montgomery inverse require even modulo
+ return modInverseHars(a, p);
+ }
+
+ int m = p.numberLength * 32;
+ // PRE: a \in [1, p - 1]
+ TBigInteger u, v, r, s;
+ u = p.copy(); // make copy to use inplace method
+ v = a.copy();
+ int max = Math.max(v.numberLength, u.numberLength);
+ r = new TBigInteger(1, 1, new int[max + 1]);
+ s = new TBigInteger(1, 1, new int[max + 1]);
+ s.digits[0] = 1;
+ // s == 1 && v == 0
+
+ int k = 0;
+
+ int lsbu = u.getLowestSetBit();
+ int lsbv = v.getLowestSetBit();
+ int toShift;
+
+ if (lsbu > lsbv) {
+ TBitLevel.inplaceShiftRight(u, lsbu);
+ TBitLevel.inplaceShiftRight(v, lsbv);
+ TBitLevel.inplaceShiftLeft(r, lsbv);
+ k += lsbu - lsbv;
+ } else {
+ TBitLevel.inplaceShiftRight(u, lsbu);
+ TBitLevel.inplaceShiftRight(v, lsbv);
+ TBitLevel.inplaceShiftLeft(s, lsbu);
+ k += lsbv - lsbu;
+ }
+
+ r.sign = 1;
+ while (v.signum() > 0) {
+ // INV v >= 0, u >= 0, v odd, u odd (except last iteration when v is
+ // even (0))
+
+ while (u.compareTo(v) > TBigInteger.EQUALS) {
+ TElementary.inplaceSubtract(u, v);
+ toShift = u.getLowestSetBit();
+ TBitLevel.inplaceShiftRight(u, toShift);
+ TElementary.inplaceAdd(r, s);
+ TBitLevel.inplaceShiftLeft(s, toShift);
+ k += toShift;
+ }
+
+ while (u.compareTo(v) <= TBigInteger.EQUALS) {
+ TElementary.inplaceSubtract(v, u);
+ if (v.signum() == 0)
+ break;
+ toShift = v.getLowestSetBit();
+ TBitLevel.inplaceShiftRight(v, toShift);
+ TElementary.inplaceAdd(s, r);
+ TBitLevel.inplaceShiftLeft(r, toShift);
+ k += toShift;
+ }
+ }
+ if (!u.isOne()) {
+ throw new ArithmeticException("BigInteger not invertible.");
+ }
+ if (r.compareTo(p) >= TBigInteger.EQUALS) {
+ TElementary.inplaceSubtract(r, p);
+ }
+
+ r = p.subtract(r);
+
+ // Have pair: ((BigInteger)r, (Integer)k) where r == a^(-1) * 2^k mod
+ // (module)
+ int n1 = calcN(p);
+ if (k > m) {
+ r = monPro(r, TBigInteger.ONE, p, n1);
+ k = k - m;
+ }
+
+ r = monPro(r, TBigInteger.getPowerOfTwo(m - k), p, n1);
+ return r;
+ }
+
+ /**
+ * Calculate the first digit of the inverse
+ */
+ private static int calcN(TBigInteger a) {
+ long m0 = a.digits[0] & 0xFFFFFFFFL;
+ long n2 = 1L; // this is a'[0]
+ long powerOfTwo = 2L;
+ do {
+ if (((m0 * n2) & powerOfTwo) != 0) {
+ n2 |= powerOfTwo;
+ }
+ powerOfTwo <<= 1;
+ } while (powerOfTwo < 0x100000000L);
+ n2 = -n2;
+ return (int) (n2 & 0xFFFFFFFFL);
+ }
+
+ static TBigInteger squareAndMultiply(TBigInteger x2, TBigInteger a2, TBigInteger exponent, TBigInteger modulus,
+ int n2) {
+ TBigInteger res = x2;
+ for (int i = exponent.bitLength() - 1; i >= 0; i--) {
+ res = monPro(res, res, modulus, n2);
+ if (TBitLevel.testBit(exponent, i)) {
+ res = monPro(res, a2, modulus, n2);
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Implements the "Shifting Euclidean modular inverse algorithm". "Laszlo
+ * Hars - Modular Inverse Algorithms Without Multiplications for
+ * Cryptographic Applications"
+ *
+ * @see TBigInteger#modInverse(TBigInteger)
+ * @param a
+ * a positive number
+ * @param m
+ * a positive modulus
+ */
+ static TBigInteger modInverseHars(TBigInteger a, TBigInteger m) {
+ // PRE: (a > 0) and (m > 0)
+ TBigInteger u, v, r, s, temp;
+ // u = MAX(a,m), v = MIN(a,m)
+ if (a.compareTo(m) == TBigInteger.LESS) {
+ u = m;
+ v = a;
+ r = TBigInteger.ZERO;
+ s = TBigInteger.ONE;
+ } else {
+ v = m;
+ u = a;
+ s = TBigInteger.ZERO;
+ r = TBigInteger.ONE;
+ }
+ int uLen = u.bitLength();
+ int vLen = v.bitLength();
+ int f = uLen - vLen;
+
+ while (vLen > 1) {
+ if (u.sign == v.sign) {
+ u = u.subtract(v.shiftLeft(f));
+ r = r.subtract(s.shiftLeft(f));
+ } else {
+ u = u.add(v.shiftLeft(f));
+ r = r.add(s.shiftLeft(f));
+ }
+ uLen = u.abs().bitLength();
+ vLen = v.abs().bitLength();
+ f = uLen - vLen;
+ if (f < 0) {
+ // SWAP(u,v)
+ temp = u;
+ u = v;
+ v = temp;
+ // SWAP(r,s)
+ temp = r;
+ r = s;
+ s = temp;
+
+ f = -f;
+ vLen = uLen;
+ }
+ }
+ if (v.sign == 0) {
+ return TBigInteger.ZERO;
+ }
+ if (v.sign < 0) {
+ s = s.negate();
+ }
+ if (s.compareTo(m) == TBigInteger.GREATER) {
+ return s.subtract(m);
+ }
+ if (s.sign < 0) {
+ return s.add(m);
+ }
+ return s; // a^(-1) mod m
+ }
+
+ /*
+ * Implements the Montgomery modular exponentiation based in The sliding
+ * windows algorithm and the MongomeryReduction.
+ *
+ * @ar.org.fitc.ref
+ * "A. Menezes,P. van Oorschot, S. Vanstone - Handbook of Applied Cryptography"
+ * ;
+ *
+ * @see #oddModPow(BigInteger, BigInteger, BigInteger)
+ */
+ static TBigInteger slidingWindow(TBigInteger x2, TBigInteger a2, TBigInteger exponent, TBigInteger modulus, int n2) {
+ // fill odd low pows of a2
+ TBigInteger pows[] = new TBigInteger[8];
+ TBigInteger res = x2;
+ int lowexp;
+ TBigInteger x3;
+ int acc3;
+ pows[0] = a2;
+
+ x3 = monPro(a2, a2, modulus, n2);
+ for (int i = 1; i <= 7; i++) {
+ pows[i] = monPro(pows[i - 1], x3, modulus, n2);
+ }
+
+ for (int i = exponent.bitLength() - 1; i >= 0; i--) {
+ if (TBitLevel.testBit(exponent, i)) {
+ lowexp = 1;
+ acc3 = i;
+
+ for (int j = Math.max(i - 3, 0); j <= i - 1; j++) {
+ if (TBitLevel.testBit(exponent, j)) {
+ if (j < acc3) {
+ acc3 = j;
+ lowexp = (lowexp << (i - j)) ^ 1;
+ } else {
+ lowexp = lowexp ^ (1 << (j - acc3));
+ }
+ }
+ }
+
+ for (int j = acc3; j <= i; j++) {
+ res = monPro(res, res, modulus, n2);
+ }
+ res = monPro(pows[(lowexp - 1) >> 1], res, modulus, n2);
+ i = acc3;
+ } else {
+ res = monPro(res, res, modulus, n2);
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Performs modular exponentiation using the Montgomery Reduction. It
+ * requires that all parameters be positive and the modulus be odd. >
+ *
+ * @see TBigInteger#modPow(TBigInteger, TBigInteger)
+ * @see #monPro(TBigInteger, TBigInteger, TBigInteger, int)
+ * @see #slidingWindow(TBigInteger, TBigInteger, TBigInteger, TBigInteger,
+ * int)
+ * @see #squareAndMultiply(TBigInteger, TBigInteger, TBigInteger,
+ * TBigInteger, int)
+ */
+ static TBigInteger oddModPow(TBigInteger base, TBigInteger exponent, TBigInteger modulus) {
+ // PRE: (base > 0), (exponent > 0), (modulus > 0) and (odd modulus)
+ int k = (modulus.numberLength << 5); // r = 2^k
+ // n-residue of base [base * r (mod modulus)]
+ TBigInteger a2 = base.shiftLeft(k).mod(modulus);
+ // n-residue of base [1 * r (mod modulus)]
+ TBigInteger x2 = TBigInteger.getPowerOfTwo(k).mod(modulus);
+ TBigInteger res;
+ // Compute (modulus[0]^(-1)) (mod 2^32) for odd modulus
+
+ int n2 = calcN(modulus);
+ if (modulus.numberLength == 1) {
+ res = squareAndMultiply(x2, a2, exponent, modulus, n2);
+ } else {
+ res = slidingWindow(x2, a2, exponent, modulus, n2);
+ }
+
+ return monPro(res, TBigInteger.ONE, modulus, n2);
+ }
+
+ /**
+ * Performs modular exponentiation using the Montgomery Reduction. It
+ * requires that all parameters be positive and the modulus be even. Based
+ * The square and multiply algorithm and the Montgomery Reduction C. K.
+ * Koc - Montgomery Reduction with Even Modulus. The square and multiply
+ * algorithm and the Montgomery Reduction.
+ *
+ * @ar.org.fitc.ref "C. K. Koc - Montgomery Reduction with Even Modulus"
+ * @see TBigInteger#modPow(TBigInteger, TBigInteger)
+ */
+ static TBigInteger evenModPow(TBigInteger base, TBigInteger exponent, TBigInteger modulus) {
+ // PRE: (base > 0), (exponent > 0), (modulus > 0) and (modulus even)
+ // STEP 1: Obtain the factorization 'modulus'= q * 2^j.
+ int j = modulus.getLowestSetBit();
+ TBigInteger q = modulus.shiftRight(j);
+
+ // STEP 2: Compute x1 := base^exponent (mod q).
+ TBigInteger x1 = oddModPow(base, exponent, q);
+
+ // STEP 3: Compute x2 := base^exponent (mod 2^j).
+ TBigInteger x2 = pow2ModPow(base, exponent, j);
+
+ // STEP 4: Compute q^(-1) (mod 2^j) and y := (x2-x1) * q^(-1) (mod 2^j)
+ TBigInteger qInv = modPow2Inverse(q, j);
+ TBigInteger y = (x2.subtract(x1)).multiply(qInv);
+ inplaceModPow2(y, j);
+ if (y.sign < 0) {
+ y = y.add(TBigInteger.getPowerOfTwo(j));
+ }
+ // STEP 5: Compute and return: x1 + q * y
+ return x1.add(q.multiply(y));
+ }
+
+ /**
+ * It requires that all parameters be positive.
+ *
+ * @return {@code baseexponent mod (2j)}.
+ * @see TBigInteger#modPow(TBigInteger, TBigInteger)
+ */
+ static TBigInteger pow2ModPow(TBigInteger base, TBigInteger exponent, int j) {
+ // PRE: (base > 0), (exponent > 0) and (j > 0)
+ TBigInteger res = TBigInteger.ONE;
+ TBigInteger e = exponent.copy();
+ TBigInteger baseMod2toN = base.copy();
+ TBigInteger res2;
+ /*
+ * If 'base' is odd then it's coprime with 2^j and phi(2^j) = 2^(j-1);
+ * so we can reduce reduce the exponent (mod 2^(j-1)).
+ */
+ if (base.testBit(0)) {
+ inplaceModPow2(e, j - 1);
+ }
+ inplaceModPow2(baseMod2toN, j);
+
+ for (int i = e.bitLength() - 1; i >= 0; i--) {
+ res2 = res.copy();
+ inplaceModPow2(res2, j);
+ res = res.multiply(res2);
+ if (TBitLevel.testBit(e, i)) {
+ res = res.multiply(baseMod2toN);
+ inplaceModPow2(res, j);
+ }
+ }
+ inplaceModPow2(res, j);
+ return res;
+ }
+
+ private static void monReduction(int[] res, TBigInteger modulus, int n2) {
+
+ /* res + m*modulus_digits */
+ int[] modulus_digits = modulus.digits;
+ int modulusLen = modulus.numberLength;
+ long outerCarry = 0;
+
+ for (int i = 0; i < modulusLen; i++) {
+ long innnerCarry = 0;
+ int m = (int) TMultiplication.unsignedMultAddAdd(res[i], n2, 0, 0);
+ for (int j = 0; j < modulusLen; j++) {
+ innnerCarry = TMultiplication.unsignedMultAddAdd(m, modulus_digits[j], res[i + j], (int) innnerCarry);
+ res[i + j] = (int) innnerCarry;
+ innnerCarry >>>= 32;
+ }
+
+ outerCarry += (res[i + modulusLen] & 0xFFFFFFFFL) + innnerCarry;
+ res[i + modulusLen] = (int) outerCarry;
+ outerCarry >>>= 32;
+ }
+
+ res[modulusLen << 1] = (int) outerCarry;
+
+ /* res / r */
+ for (int j = 0; j < modulusLen + 1; j++) {
+ res[j] = res[j + modulusLen];
+ }
+ }
+
+ /**
+ * Implements the Montgomery Product of two integers represented by
+ * {@code int} arrays. The arrays are supposed in little endian
+ * notation.
+ *
+ * @param a
+ * The first factor of the product.
+ * @param b
+ * The second factor of the product.
+ * @param modulus
+ * The modulus of the operations. Zmodulus.
+ * @param n2
+ * The digit modulus'[0].
+ * @ar.org.fitc.ref "C. K. Koc - Analyzing and Comparing Montgomery
+ * Multiplication Algorithms"
+ * @see #modPowOdd(TBigInteger, TBigInteger, TBigInteger)
+ */
+ static TBigInteger monPro(TBigInteger a, TBigInteger b, TBigInteger modulus, int n2) {
+ int modulusLen = modulus.numberLength;
+ int res[] = new int[(modulusLen << 1) + 1];
+ TMultiplication.multArraysPAP(a.digits, Math.min(modulusLen, a.numberLength), b.digits,
+ Math.min(modulusLen, b.numberLength), res);
+ monReduction(res, modulus, n2);
+ return finalSubtraction(res, modulus);
+
+ }
+
+ /**
+ * Performs the final reduction of the Montgomery algorithm.
+ *
+ * @see monPro(BigInteger, BigInteger, BigInteger, long)
+ * @see monSquare(BigInteger, BigInteger, long)
+ */
+ static TBigInteger finalSubtraction(int res[], TBigInteger modulus) {
+
+ // skipping leading zeros
+ int modulusLen = modulus.numberLength;
+ boolean doSub = res[modulusLen] != 0;
+ if (!doSub) {
+ int modulusDigits[] = modulus.digits;
+ doSub = true;
+ for (int i = modulusLen - 1; i >= 0; i--) {
+ if (res[i] != modulusDigits[i]) {
+ doSub = (res[i] != 0) && ((res[i] & 0xFFFFFFFFL) > (modulusDigits[i] & 0xFFFFFFFFL));
+ break;
+ }
+ }
+ }
+
+ TBigInteger result = new TBigInteger(1, modulusLen + 1, res);
+
+ // if (res >= modulusDigits) compute (res - modulusDigits)
+ if (doSub) {
+ TElementary.inplaceSubtract(result, modulus);
+ }
+
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /**
+ * @param x
+ * an odd positive number.
+ * @param n
+ * the exponent by which 2 is raised.
+ * @return {@code x-1 (mod 2n)}.
+ */
+ static TBigInteger modPow2Inverse(TBigInteger x, int n) {
+ // PRE: (x > 0), (x is odd), and (n > 0)
+ TBigInteger y = new TBigInteger(1, new int[1 << n]);
+ y.numberLength = 1;
+ y.digits[0] = 1;
+ y.sign = 1;
+
+ for (int i = 1; i < n; i++) {
+ if (TBitLevel.testBit(x.multiply(y), i)) {
+ // Adding 2^i to y (setting the i-th bit)
+ y.digits[i >> 5] |= (1 << (i & 31));
+ }
+ }
+ return y;
+ }
+
+ /**
+ * Performs {@code x = x mod (2n)}.
+ *
+ * @param x
+ * a positive number, it will store the result.
+ * @param n
+ * a positive exponent of {@code 2}.
+ */
+ static void inplaceModPow2(TBigInteger x, int n) {
+ // PRE: (x > 0) and (n >= 0)
+ int fd = n >> 5;
+ int leadingZeros;
+
+ if ((x.numberLength < fd) || (x.bitLength() <= n)) {
+ return;
+ }
+ leadingZeros = 32 - (n & 31);
+ x.numberLength = fd + 1;
+ x.digits[fd] &= (leadingZeros < 32) ? (-1 >>> leadingZeros) : 0;
+ x.cutOffLeadingZeroes();
+ }
+
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TElementary.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TElementary.java
new file mode 100644
index 000000000..544a8cde7
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TElementary.java
@@ -0,0 +1,432 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+/**
+ * Static library that provides the basic arithmetic mutable operations for
+ * {@link TBigInteger}. The operations provided are listed below.
+ *
+ * - Addition.
+ * - Subtraction.
+ * - Comparison.
+ *
+ * In addition to this, some Inplace (mutable) methods are
+ * provided.
+ */
+class TElementary {
+
+ /** Just to denote that this class can't be instantiated */
+ private TElementary() {
+ }
+
+ /**
+ * Compares two arrays. All elements are treated as unsigned integers. The
+ * magnitude is the bit chain of elements in big-endian order.
+ *
+ * @param a
+ * the first array
+ * @param b
+ * the second array
+ * @param size
+ * the size of arrays
+ * @return 1 if a > b, -1 if a < b, 0 if a == b
+ */
+ static int compareArrays(final int[] a, final int[] b, final int size) {
+ int i;
+ for (i = size - 1; (i >= 0) && (a[i] == b[i]); i--) {
+ // do nothing
+ }
+ return ((i < 0) ? TBigInteger.EQUALS : (a[i] & 0xFFFFFFFFL) < (b[i] & 0xFFFFFFFFL) ? TBigInteger.LESS
+ : TBigInteger.GREATER);
+ }
+
+ /** @see TBigInteger#add(TBigInteger) */
+ static TBigInteger add(TBigInteger op1, TBigInteger op2) {
+ int resDigits[];
+ int resSign;
+ int op1Sign = op1.sign;
+ int op2Sign = op2.sign;
+
+ if (op1Sign == 0) {
+ return op2;
+ }
+ if (op2Sign == 0) {
+ return op1;
+ }
+ int op1Len = op1.numberLength;
+ int op2Len = op2.numberLength;
+
+ if (op1Len + op2Len == 2) {
+ long a = (op1.digits[0] & 0xFFFFFFFFL);
+ long b = (op2.digits[0] & 0xFFFFFFFFL);
+ long res;
+ int valueLo;
+ int valueHi;
+
+ if (op1Sign == op2Sign) {
+ res = a + b;
+ valueLo = (int) res;
+ valueHi = (int) (res >>> 32);
+ return ((valueHi == 0) ? new TBigInteger(op1Sign, valueLo) : new TBigInteger(op1Sign, 2, new int[] {
+ valueLo, valueHi }));
+ }
+ return TBigInteger.valueOf((op1Sign < 0) ? (b - a) : (a - b));
+ } else if (op1Sign == op2Sign) {
+ resSign = op1Sign;
+ // an augend should not be shorter than addend
+ resDigits = (op1Len >= op2Len) ? add(op1.digits, op1Len, op2.digits, op2Len) : add(op2.digits, op2Len,
+ op1.digits, op1Len);
+ } else { // signs are different
+ int cmp = ((op1Len != op2Len) ? ((op1Len > op2Len) ? 1 : -1)
+ : compareArrays(op1.digits, op2.digits, op1Len));
+
+ if (cmp == TBigInteger.EQUALS) {
+ return TBigInteger.ZERO;
+ }
+ // a minuend should not be shorter than subtrahend
+ if (cmp == TBigInteger.GREATER) {
+ resSign = op1Sign;
+ resDigits = subtract(op1.digits, op1Len, op2.digits, op2Len);
+ } else {
+ resSign = op2Sign;
+ resDigits = subtract(op2.digits, op2Len, op1.digits, op1Len);
+ }
+ }
+ TBigInteger res = new TBigInteger(resSign, resDigits.length, resDigits);
+ res.cutOffLeadingZeroes();
+ return res;
+ }
+
+ /**
+ * Performs {@code res = a + b}.
+ */
+ private static void add(int res[], int a[], int aSize, int b[], int bSize) {
+ // PRE: a.length < max(aSize, bSize)
+
+ int i;
+ long carry = (a[0] & 0xFFFFFFFFL) + (b[0] & 0xFFFFFFFFL);
+
+ res[0] = (int) carry;
+ carry >>= 32;
+
+ if (aSize >= bSize) {
+ for (i = 1; i < bSize; i++) {
+ carry += (a[i] & 0xFFFFFFFFL) + (b[i] & 0xFFFFFFFFL);
+ res[i] = (int) carry;
+ carry >>= 32;
+ }
+ for (; i < aSize; i++) {
+ carry += a[i] & 0xFFFFFFFFL;
+ res[i] = (int) carry;
+ carry >>= 32;
+ }
+ } else {
+ for (i = 1; i < aSize; i++) {
+ carry += (a[i] & 0xFFFFFFFFL) + (b[i] & 0xFFFFFFFFL);
+ res[i] = (int) carry;
+ carry >>= 32;
+ }
+ for (; i < bSize; i++) {
+ carry += b[i] & 0xFFFFFFFFL;
+ res[i] = (int) carry;
+ carry >>= 32;
+ }
+ }
+ if (carry != 0) {
+ res[i] = (int) carry;
+ }
+ }
+
+ /** @see TBigInteger#subtract(TBigInteger) */
+ static TBigInteger subtract(TBigInteger op1, TBigInteger op2) {
+ int resSign;
+ int resDigits[];
+ int op1Sign = op1.sign;
+ int op2Sign = op2.sign;
+
+ if (op2Sign == 0) {
+ return op1;
+ }
+ if (op1Sign == 0) {
+ return op2.negate();
+ }
+ int op1Len = op1.numberLength;
+ int op2Len = op2.numberLength;
+ if (op1Len + op2Len == 2) {
+ long a = (op1.digits[0] & 0xFFFFFFFFL);
+ long b = (op2.digits[0] & 0xFFFFFFFFL);
+ if (op1Sign < 0) {
+ a = -a;
+ }
+ if (op2Sign < 0) {
+ b = -b;
+ }
+ return TBigInteger.valueOf(a - b);
+ }
+ int cmp = ((op1Len != op2Len) ? ((op1Len > op2Len) ? 1 : -1) : TElementary.compareArrays(op1.digits, op2.digits,
+ op1Len));
+
+ if (cmp == TBigInteger.LESS) {
+ resSign = -op2Sign;
+ resDigits = (op1Sign == op2Sign) ? subtract(op2.digits, op2Len, op1.digits, op1Len) : add(op2.digits,
+ op2Len, op1.digits, op1Len);
+ } else {
+ resSign = op1Sign;
+ if (op1Sign == op2Sign) {
+ if (cmp == TBigInteger.EQUALS) {
+ return TBigInteger.ZERO;
+ }
+ resDigits = subtract(op1.digits, op1Len, op2.digits, op2Len);
+ } else {
+ resDigits = add(op1.digits, op1Len, op2.digits, op2Len);
+ }
+ }
+ TBigInteger res = new TBigInteger(resSign, resDigits.length, resDigits);
+ res.cutOffLeadingZeroes();
+ return res;
+ }
+
+ /**
+ * Performs {@code res = a - b}. It is assumed the magnitude of a is not
+ * less than the magnitude of b.
+ */
+ private static void subtract(int res[], int a[], int aSize, int b[], int bSize) {
+ // PRE: a[] >= b[]
+ int i;
+ long borrow = 0;
+
+ for (i = 0; i < bSize; i++) {
+ borrow += (a[i] & 0xFFFFFFFFL) - (b[i] & 0xFFFFFFFFL);
+ res[i] = (int) borrow;
+ borrow >>= 32; // -1 or 0
+ }
+ for (; i < aSize; i++) {
+ borrow += a[i] & 0xFFFFFFFFL;
+ res[i] = (int) borrow;
+ borrow >>= 32; // -1 or 0
+ }
+ }
+
+ /**
+ * Addss the value represented by {@code b} to the value represented by
+ * {@code a}. It is assumed the magnitude of a is not less than the
+ * magnitude of b.
+ *
+ * @return {@code a + b}
+ */
+ private static int[] add(int a[], int aSize, int b[], int bSize) {
+ // PRE: a[] >= b[]
+ int res[] = new int[aSize + 1];
+ add(res, a, aSize, b, bSize);
+ return res;
+ }
+
+ /**
+ * Performs {@code op1 += op2}. {@code op1} must have enough place to store
+ * the result (i.e. {@code op1.bitLength() >= op2.bitLength()}). Both should
+ * be positive (i.e. {@code op1 >= op2}).
+ *
+ * @param op1
+ * the input minuend, and the output result.
+ * @param op2
+ * the addend
+ */
+ static void inplaceAdd(TBigInteger op1, TBigInteger op2) {
+ // PRE: op1 >= op2 > 0
+ add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ op1.numberLength = Math.min(Math.max(op1.numberLength, op2.numberLength) + 1, op1.digits.length);
+ op1.cutOffLeadingZeroes();
+ op1.unCache();
+ }
+
+ /**
+ * Adds an integer value to the array of integers remembering carry.
+ *
+ * @return a possible generated carry (0 or 1)
+ */
+ static int inplaceAdd(int a[], final int aSize, final int addend) {
+ long carry = addend & 0xFFFFFFFFL;
+
+ for (int i = 0; (carry != 0) && (i < aSize); i++) {
+ carry += a[i] & 0xFFFFFFFFL;
+ a[i] = (int) carry;
+ carry >>= 32;
+ }
+ return (int) carry;
+ }
+
+ /**
+ * Performs: {@code op1 += addend}. The number must to have place to hold a
+ * possible carry.
+ */
+ static void inplaceAdd(TBigInteger op1, final int addend) {
+ int carry = inplaceAdd(op1.digits, op1.numberLength, addend);
+ if (carry == 1) {
+ op1.digits[op1.numberLength] = 1;
+ op1.numberLength++;
+ }
+ op1.unCache();
+ }
+
+ /**
+ * Performs {@code op1 -= op2}. {@code op1} must have enough place to store
+ * the result (i.e. {@code op1.bitLength() >= op2.bitLength()}). Both should
+ * be positive (what implies that {@code op1 >= op2}).
+ *
+ * @param op1
+ * the input minuend, and the output result.
+ * @param op2
+ * the subtrahend
+ */
+ static void inplaceSubtract(TBigInteger op1, TBigInteger op2) {
+ // PRE: op1 >= op2 > 0
+ subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ op1.cutOffLeadingZeroes();
+ op1.unCache();
+ }
+
+ /**
+ * Performs {@code res = b - a}
+ */
+ private static void inverseSubtract(int res[], int a[], int aSize, int b[], int bSize) {
+ int i;
+ long borrow = 0;
+ if (aSize < bSize) {
+ for (i = 0; i < aSize; i++) {
+ borrow += (b[i] & 0xFFFFFFFFL) - (a[i] & 0xFFFFFFFFL);
+ res[i] = (int) borrow;
+ borrow >>= 32; // -1 or 0
+ }
+ for (; i < bSize; i++) {
+ borrow += b[i] & 0xFFFFFFFFL;
+ res[i] = (int) borrow;
+ borrow >>= 32; // -1 or 0
+ }
+ } else {
+ for (i = 0; i < bSize; i++) {
+ borrow += (b[i] & 0xFFFFFFFFL) - (a[i] & 0xFFFFFFFFL);
+ res[i] = (int) borrow;
+ borrow >>= 32; // -1 or 0
+ }
+ for (; i < aSize; i++) {
+ borrow -= a[i] & 0xFFFFFFFFL;
+ res[i] = (int) borrow;
+ borrow >>= 32; // -1 or 0
+ }
+ }
+
+ }
+
+ /**
+ * Subtracts the value represented by {@code b} from the value represented
+ * by {@code a}. It is assumed the magnitude of a is not less than the
+ * magnitude of b.
+ *
+ * @return {@code a - b}
+ */
+ private static int[] subtract(int a[], int aSize, int b[], int bSize) {
+ // PRE: a[] >= b[]
+ int res[] = new int[aSize];
+ subtract(res, a, aSize, b, bSize);
+ return res;
+ }
+
+ /**
+ * Same as
+ *
+ * @link #inplaceSubtract(BigInteger, BigInteger), but without the
+ * restriction of non-positive values
+ * @param op1
+ * should have enough space to save the result
+ * @param op2
+ */
+ static void completeInPlaceSubtract(TBigInteger op1, TBigInteger op2) {
+ int resultSign = op1.compareTo(op2);
+ if (op1.sign == 0) {
+ System.arraycopy(op2.digits, 0, op1.digits, 0, op2.numberLength);
+ op1.sign = -op2.sign;
+ } else if (op1.sign != op2.sign) {
+ add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ op1.sign = resultSign;
+ } else {
+ int sign = unsignedArraysCompare(op1.digits, op2.digits, op1.numberLength, op2.numberLength);
+ if (sign > 0) {
+ subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ // op1.sign remains equal
+ } else {
+ inverseSubtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ op1.sign = -op1.sign;
+ }
+ }
+ op1.numberLength = Math.max(op1.numberLength, op2.numberLength) + 1;
+ op1.cutOffLeadingZeroes();
+ op1.unCache();
+ }
+
+ /**
+ * Same as @link #inplaceAdd(BigInteger, BigInteger), but without the
+ * restriction of non-positive values
+ *
+ * @param op1
+ * any number
+ * @param op2
+ * any number
+ */
+ static void completeInPlaceAdd(TBigInteger op1, TBigInteger op2) {
+ if (op1.sign == 0)
+ System.arraycopy(op2.digits, 0, op1.digits, 0, op2.numberLength);
+ else if (op2.sign == 0)
+ return;
+ else if (op1.sign == op2.sign)
+ add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ else {
+ int sign = unsignedArraysCompare(op1.digits, op2.digits, op1.numberLength, op2.numberLength);
+ if (sign > 0)
+ subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ else {
+ inverseSubtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
+ op1.sign = -op1.sign;
+ }
+ }
+ op1.numberLength = Math.max(op1.numberLength, op2.numberLength) + 1;
+ op1.cutOffLeadingZeroes();
+ op1.unCache();
+ }
+
+ /**
+ * Compares two arrays, representing unsigned integer in little-endian
+ * order. Returns +1,0,-1 if a is - respective - greater, equal or lesser
+ * then b
+ */
+ private static int unsignedArraysCompare(int[] a, int[] b, int aSize, int bSize) {
+ if (aSize > bSize)
+ return 1;
+ else if (aSize < bSize)
+ return -1;
+
+ else {
+ int i;
+ for (i = aSize - 1; i >= 0 && a[i] == b[i]; i--) {
+ // do nothing
+ }
+ return i < 0 ? TBigInteger.EQUALS : ((a[i] & 0xFFFFFFFFL) < (b[i] & 0xFFFFFFFFL) ? TBigInteger.LESS
+ : TBigInteger.GREATER);
+ }
+ }
+
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TLogical.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TLogical.java
new file mode 100644
index 000000000..1096f2eb1
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TLogical.java
@@ -0,0 +1,808 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+/**
+ * The library implements some logical operations over {@code BigInteger}. The
+ * operations provided are listed below.
+ *
+ * - not
+ * - and
+ * - andNot
+ * - or
+ * - xor
+ *
+ */
+class TLogical {
+
+ /** Just to denote that this class can't be instantiated. */
+
+ private TLogical() {}
+
+
+ /** @see TBigInteger#not() */
+ static TBigInteger not(TBigInteger val) {
+ if (val.sign == 0) {
+ return TBigInteger.MINUS_ONE;
+ }
+ if (val.equals(TBigInteger.MINUS_ONE)) {
+ return TBigInteger.ZERO;
+ }
+ int resDigits[] = new int[val.numberLength + 1];
+ int i;
+
+ if (val.sign > 0) {
+ // ~val = -val + 1
+ if (val.digits[val.numberLength - 1] != -1) {
+ for (i = 0; val.digits[i] == -1; i++) {
+ // do nothing
+ }
+ } else {
+ for (i = 0; (i < val.numberLength) && (val.digits[i] == -1); i++) {
+ // do nothing
+ }
+ if (i == val.numberLength) {
+ resDigits[i] = 1;
+ return new TBigInteger(-val.sign, i + 1, resDigits);
+ }
+ }
+ // Here a carry 1 was generated
+ } else {// (val.sign < 0)
+ // ~val = -val - 1
+ for (i = 0; val.digits[i] == 0; i++) {
+ resDigits[i] = -1;
+ }
+ // Here a borrow -1 was generated
+ }
+ // Now, the carry/borrow can be absorbed
+ resDigits[i] = val.digits[i] + val.sign;
+ // Copying the remaining unchanged digit
+ for (i++; i < val.numberLength; i++) {
+ resDigits[i] = val.digits[i];
+ }
+ return new TBigInteger(-val.sign, i, resDigits);
+ }
+
+ /** @see TBigInteger#and(TBigInteger) */
+ static TBigInteger and(TBigInteger val, TBigInteger that) {
+ if (that.sign == 0 || val.sign == 0) {
+ return TBigInteger.ZERO;
+ }
+ if (that.equals(TBigInteger.MINUS_ONE)){
+ return val;
+ }
+ if (val.equals(TBigInteger.MINUS_ONE)) {
+ return that;
+ }
+
+ if (val.sign > 0) {
+ if (that.sign > 0) {
+ return andPositive(val, that);
+ } else {
+ return andDiffSigns(val, that);
+ }
+ } else {
+ if (that.sign > 0) {
+ return andDiffSigns(that, val);
+ } else if (val.numberLength > that.numberLength) {
+ return andNegative(val, that);
+ } else {
+ return andNegative(that, val);
+ }
+ }
+ }
+
+ /** @return sign = 1, magnitude = val.magnitude & that.magnitude*/
+ static TBigInteger andPositive(TBigInteger val, TBigInteger that) {
+ // PRE: both arguments are positive
+ int resLength = Math.min(val.numberLength, that.numberLength);
+ int i = Math.max(val.getFirstNonzeroDigit(), that.getFirstNonzeroDigit());
+
+ if (i >= resLength) {
+ return TBigInteger.ZERO;
+ }
+
+ int resDigits[] = new int[resLength];
+ for ( ; i < resLength; i++) {
+ resDigits[i] = val.digits[i] & that.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @return sign = positive.magnitude & magnitude = -negative.magnitude */
+ static TBigInteger andDiffSigns(TBigInteger positive, TBigInteger negative) {
+ // PRE: positive is positive and negative is negative
+ int iPos = positive.getFirstNonzeroDigit();
+ int iNeg = negative.getFirstNonzeroDigit();
+
+ // Look if the trailing zeros of the negative will "blank" all
+ // the positive digits
+ if (iNeg >= positive.numberLength) {
+ return TBigInteger.ZERO;
+ }
+ int resLength = positive.numberLength;
+ int resDigits[] = new int[resLength];
+
+ // Must start from max(iPos, iNeg)
+ int i = Math.max(iPos, iNeg);
+ if (i == iNeg) {
+ resDigits[i] = -negative.digits[i] & positive.digits[i];
+ i++;
+ }
+ int limit = Math.min(negative.numberLength, positive.numberLength);
+ for ( ; i < limit; i++) {
+ resDigits[i] = ~negative.digits[i] & positive.digits[i];
+ }
+ // if the negative was shorter must copy the remaining digits
+ // from positive
+ if (i >= negative.numberLength) {
+ for ( ; i < positive.numberLength; i++) {
+ resDigits[i] = positive.digits[i];
+ }
+ } // else positive ended and must "copy" virtual 0's, do nothing then
+
+ TBigInteger result = new TBigInteger(1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @return sign = -1, magnitude = -(-longer.magnitude & -shorter.magnitude)*/
+ static TBigInteger andNegative(TBigInteger longer, TBigInteger shorter) {
+ // PRE: longer and shorter are negative
+ // PRE: longer has at least as many digits as shorter
+ int iLonger = longer.getFirstNonzeroDigit();
+ int iShorter = shorter.getFirstNonzeroDigit();
+
+ // Does shorter matter?
+ if (iLonger >= shorter.numberLength) {
+ return longer;
+ }
+
+ int resLength;
+ int resDigits[];
+ int i = Math.max(iShorter, iLonger);
+ int digit;
+ if (iShorter > iLonger) {
+ digit = -shorter.digits[i] & ~longer.digits[i];
+ } else if (iShorter < iLonger) {
+ digit = ~shorter.digits[i] & -longer.digits[i];
+ } else {
+ digit = -shorter.digits[i] & -longer.digits[i];
+ }
+ if (digit == 0) {
+ for (i++; i < shorter.numberLength && (digit = ~(longer.digits[i] | shorter.digits[i])) == 0; i++) {
+ // do nothing
+ }
+ if (digit == 0) {
+ // shorter has only the remaining virtual sign bits
+ for ( ; i < longer.numberLength && (digit = ~longer.digits[i]) == 0; i++) {
+ // do nothing
+ }
+ if (digit == 0) {
+ resLength = longer.numberLength + 1;
+ resDigits = new int[resLength];
+ resDigits[resLength - 1] = 1;
+
+ TBigInteger result = new TBigInteger(-1, resLength, resDigits);
+ return result;
+ }
+ }
+ }
+ resLength = longer.numberLength;
+ resDigits = new int[resLength];
+ resDigits[i] = -digit;
+ for (i++; i < shorter.numberLength; i++){
+ // resDigits[i] = ~(~longer.digits[i] & ~shorter.digits[i];)
+ resDigits[i] = longer.digits[i] | shorter.digits[i];
+ }
+ // shorter has only the remaining virtual sign bits
+ for( ; i < longer.numberLength; i++){
+ resDigits[i] = longer.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(-1, resLength, resDigits);
+ return result;
+ }
+
+ /** @see TBigInteger#andNot(TBigInteger) */
+ static TBigInteger andNot(TBigInteger val, TBigInteger that) {
+ if (that.sign == 0 ) {
+ return val;
+ }
+ if (val.sign == 0) {
+ return TBigInteger.ZERO;
+ }
+ if (val.equals(TBigInteger.MINUS_ONE)) {
+ return that.not();
+ }
+ if (that.equals(TBigInteger.MINUS_ONE)){
+ return TBigInteger.ZERO;
+ }
+
+ //if val == that, return 0
+
+ if (val.sign > 0) {
+ if (that.sign > 0) {
+ return andNotPositive(val, that);
+ } else {
+ return andNotPositiveNegative(val, that);
+ }
+ } else {
+ if (that.sign > 0) {
+ return andNotNegativePositive(val, that);
+ } else {
+ return andNotNegative(val, that);
+ }
+ }
+ }
+
+ /** @return sign = 1, magnitude = val.magnitude & ~that.magnitude*/
+ static TBigInteger andNotPositive(TBigInteger val, TBigInteger that) {
+ // PRE: both arguments are positive
+ int resDigits[] = new int[val.numberLength];
+
+ int limit = Math.min(val.numberLength, that.numberLength);
+ int i;
+ for (i = val.getFirstNonzeroDigit(); i < limit; i++) {
+ resDigits[i] = val.digits[i] & ~that.digits[i];
+ }
+ for ( ; i < val.numberLength; i++) {
+ resDigits[i] = val.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(1, val.numberLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @return sign = 1, magnitude = positive.magnitude & ~(-negative.magnitude)*/
+ static TBigInteger andNotPositiveNegative(TBigInteger positive, TBigInteger negative) {
+ // PRE: positive > 0 && negative < 0
+ int iNeg = negative.getFirstNonzeroDigit();
+ int iPos = positive.getFirstNonzeroDigit();
+
+ if (iNeg >= positive.numberLength) {
+ return positive;
+ }
+
+ int resLength = Math.min(positive.numberLength, negative.numberLength);
+ int resDigits[] = new int[resLength];
+
+ // Always start from first non zero of positive
+ int i = iPos;
+ for ( ; i < iNeg; i++) {
+ // resDigits[i] = positive.digits[i] & -1 (~0)
+ resDigits[i] = positive.digits[i];
+ }
+ if (i == iNeg) {
+ resDigits[i] = positive.digits[i] & (negative.digits[i] - 1);
+ i++;
+ }
+ for ( ; i < resLength; i++) {
+ // resDigits[i] = positive.digits[i] & ~(~negative.digits[i]);
+ resDigits[i] = positive.digits[i] & negative.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @return sign = -1, magnitude = -(-negative.magnitude & ~positive.magnitude)*/
+ static TBigInteger andNotNegativePositive(TBigInteger negative, TBigInteger positive) {
+ // PRE: negative < 0 && positive > 0
+ int resLength;
+ int resDigits[];
+ int limit;
+ int digit;
+
+ int iNeg = negative.getFirstNonzeroDigit();
+ int iPos = positive.getFirstNonzeroDigit();
+
+ if (iNeg >= positive.numberLength) {
+ return negative;
+ }
+
+ resLength = Math.max(negative.numberLength, positive.numberLength);
+ int i = iNeg;
+ if (iPos > iNeg) {
+ resDigits = new int[resLength];
+ limit = Math.min(negative.numberLength, iPos);
+ for ( ; i < limit; i++) {
+ // 1st case: resDigits [i] = -(-negative.digits[i] & (~0))
+ // otherwise: resDigits[i] = ~(~negative.digits[i] & ~0) ;
+ resDigits[i] = negative.digits[i];
+ }
+ if (i == negative.numberLength) {
+ for (i = iPos; i < positive.numberLength; i++) {
+ // resDigits[i] = ~(~positive.digits[i] & -1);
+ resDigits[i] = positive.digits[i];
+ }
+ }
+ } else {
+ digit = -negative.digits[i] & ~positive.digits[i];
+ if (digit == 0) {
+ limit = Math.min(positive.numberLength, negative.numberLength);
+ for (i++; i < limit && (digit = ~(negative.digits[i] | positive.digits[i])) == 0; i++) {
+ // do nothing
+ }
+ if (digit == 0) {
+ // the shorter has only the remaining virtual sign bits
+ for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++) {
+ // do nothing
+ }
+ for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++) {
+ // do nothing
+ }
+ if (digit == 0) {
+ resLength++;
+ resDigits = new int[resLength];
+ resDigits[resLength - 1] = 1;
+
+ TBigInteger result = new TBigInteger(-1, resLength, resDigits);
+ return result;
+ }
+ }
+ }
+ resDigits = new int[resLength];
+ resDigits[i] = -digit;
+ i++;
+ }
+
+ limit = Math.min(positive.numberLength, negative.numberLength);
+ for ( ; i < limit; i++) {
+ //resDigits[i] = ~(~negative.digits[i] & ~positive.digits[i]);
+ resDigits[i] = negative.digits[i] | positive.digits[i];
+ }
+ // Actually one of the next two cycles will be executed
+ for ( ; i < negative.numberLength; i++) {
+ resDigits[i] = negative.digits[i];
+ }
+ for ( ; i < positive.numberLength; i++) {
+ resDigits[i] = positive.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(-1, resLength, resDigits);
+ return result;
+ }
+
+ /** @return sign = 1, magnitude = -val.magnitude & ~(-that.magnitude)*/
+ static TBigInteger andNotNegative(TBigInteger val, TBigInteger that) {
+ // PRE: val < 0 && that < 0
+ int iVal = val.getFirstNonzeroDigit();
+ int iThat = that.getFirstNonzeroDigit();
+
+ if (iVal >= that.numberLength) {
+ return TBigInteger.ZERO;
+ }
+
+ int resLength = that.numberLength;
+ int resDigits[] = new int[resLength];
+ int limit;
+ int i = iVal;
+ if (iVal < iThat) {
+ // resDigits[i] = -val.digits[i] & -1;
+ resDigits[i] = -val.digits[i];
+ limit = Math.min(val.numberLength, iThat);
+ for (i++; i < limit; i++) {
+ // resDigits[i] = ~val.digits[i] & -1;
+ resDigits[i] = ~val.digits[i];
+ }
+ if (i == val.numberLength) {
+ for ( ; i < iThat; i++) {
+ // resDigits[i] = -1 & -1;
+ resDigits[i] = -1;
+ }
+ // resDigits[i] = -1 & ~-that.digits[i];
+ resDigits[i] = that.digits[i] - 1;
+ } else {
+ // resDigits[i] = ~val.digits[i] & ~-that.digits[i];
+ resDigits[i] = ~val.digits[i] & (that.digits[i] - 1);
+ }
+ } else if (iThat < iVal ) {
+ // resDigits[i] = -val.digits[i] & ~~that.digits[i];
+ resDigits[i] = -val.digits[i] & that.digits[i];
+ } else {
+ // resDigits[i] = -val.digits[i] & ~-that.digits[i];
+ resDigits[i] = -val.digits[i] & (that.digits[i] - 1);
+ }
+
+ limit = Math.min(val.numberLength, that.numberLength);
+ for (i++; i < limit; i++) {
+ // resDigits[i] = ~val.digits[i] & ~~that.digits[i];
+ resDigits[i] = ~val.digits[i] & that.digits[i];
+ }
+ for ( ; i < that.numberLength; i++) {
+ // resDigits[i] = -1 & ~~that.digits[i];
+ resDigits[i] = that.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @see TBigInteger#or(TBigInteger) */
+ static TBigInteger or(TBigInteger val, TBigInteger that) {
+ if (that.equals(TBigInteger.MINUS_ONE) || val.equals(TBigInteger.MINUS_ONE)) {
+ return TBigInteger.MINUS_ONE;
+ }
+ if (that.sign == 0) {
+ return val;
+ }
+ if (val.sign == 0) {
+ return that;
+ }
+
+ if (val.sign > 0) {
+ if (that.sign > 0) {
+ if (val.numberLength > that.numberLength) {
+ return orPositive(val, that);
+ } else {
+ return orPositive(that, val);
+ }
+ } else {
+ return orDiffSigns(val, that);
+ }
+ } else {
+ if (that.sign > 0) {
+ return orDiffSigns(that, val);
+ } else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
+ return orNegative(that, val);
+ } else {
+ return orNegative(val, that);
+ }
+ }
+ }
+
+ /** @return sign = 1, magnitude = longer.magnitude | shorter.magnitude*/
+ static TBigInteger orPositive(TBigInteger longer, TBigInteger shorter) {
+ // PRE: longer and shorter are positive;
+ // PRE: longer has at least as many digits as shorter
+ int resLength = longer.numberLength;
+ int resDigits[] = new int[resLength];
+
+ int i = Math.min(longer.getFirstNonzeroDigit(), shorter.getFirstNonzeroDigit());
+ for (i = 0; i < shorter.numberLength; i++) {
+ resDigits[i] = longer.digits[i] | shorter.digits[i];
+ }
+ for ( ; i < resLength; i++) {
+ resDigits[i] = longer.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(1, resLength, resDigits);
+ return result;
+ }
+
+ /** @return sign = -1, magnitude = -(-val.magnitude | -that.magnitude) */
+ static TBigInteger orNegative(TBigInteger val, TBigInteger that){
+ // PRE: val and that are negative;
+ // PRE: val has at least as many trailing zeros digits as that
+ int iThat = that.getFirstNonzeroDigit();
+ int iVal = val.getFirstNonzeroDigit();
+ int i;
+
+ if (iVal >= that.numberLength) {
+ return that;
+ }else if (iThat >= val.numberLength) {
+ return val;
+ }
+
+ int resLength = Math.min(val.numberLength, that.numberLength);
+ int resDigits[] = new int[resLength];
+
+ //Looking for the first non-zero digit of the result
+ if (iThat == iVal) {
+ resDigits[iVal] = -(-val.digits[iVal] | -that.digits[iVal]);
+ i = iVal;
+ } else {
+ for (i = iThat; i < iVal; i++) {
+ resDigits[i] = that.digits[i];
+ }
+ resDigits[i] = that.digits[i] & (val.digits[i] - 1);
+ }
+
+ for (i++; i < resLength; i++) {
+ resDigits[i] = val.digits[i] & that.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(-1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @return sign = -1, magnitude = -(positive.magnitude | -negative.magnitude) */
+ static TBigInteger orDiffSigns(TBigInteger positive, TBigInteger negative){
+ // Jumping over the least significant zero bits
+ int iNeg = negative.getFirstNonzeroDigit();
+ int iPos = positive.getFirstNonzeroDigit();
+ int i;
+ int limit;
+
+ // Look if the trailing zeros of the positive will "copy" all
+ // the negative digits
+ if (iPos >= negative.numberLength) {
+ return negative;
+ }
+ int resLength = negative.numberLength;
+ int resDigits[] = new int[resLength];
+
+ if (iNeg < iPos ) {
+ // We know for sure that this will
+ // be the first non zero digit in the result
+ for (i = iNeg; i < iPos; i++) {
+ resDigits[i] = negative.digits[i];
+ }
+ } else if (iPos < iNeg) {
+ i = iPos;
+ resDigits[i] = -positive.digits[i];
+ limit = Math.min(positive.numberLength, iNeg);
+ for(i++; i < limit; i++ ) {
+ resDigits[i] = ~positive.digits[i];
+ }
+ if (i != positive.numberLength) {
+ resDigits[i] = ~(-negative.digits[i] | positive.digits[i]);
+ } else{
+ for (; i 0) {
+ if (that.sign > 0) {
+ if (val.numberLength > that.numberLength) {
+ return xorPositive(val, that);
+ } else {
+ return xorPositive(that, val);
+ }
+ } else {
+ return xorDiffSigns(val, that);
+ }
+ } else {
+ if (that.sign > 0) {
+ return xorDiffSigns(that, val);
+ } else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
+ return xorNegative(that, val);
+ } else {
+ return xorNegative(val, that);
+ }
+ }
+ }
+
+ /** @return sign = 0, magnitude = longer.magnitude | shorter.magnitude */
+ static TBigInteger xorPositive(TBigInteger longer, TBigInteger shorter) {
+ // PRE: longer and shorter are positive;
+ // PRE: longer has at least as many digits as shorter
+ int resLength = longer.numberLength;
+ int resDigits[] = new int[resLength];
+ int i = Math.min(longer.getFirstNonzeroDigit(), shorter.getFirstNonzeroDigit());
+ for ( ; i < shorter.numberLength; i++) {
+ resDigits[i] = longer.digits[i] ^ shorter.digits[i];
+ }
+ for( ; i < longer.numberLength; i++ ){
+ resDigits[i] = longer.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @return sign = 0, magnitude = -val.magnitude ^ -that.magnitude */
+ static TBigInteger xorNegative(TBigInteger val, TBigInteger that){
+ // PRE: val and that are negative
+ // PRE: val has at least as many trailing zero digits as that
+ int resLength = Math.max(val.numberLength, that.numberLength);
+ int resDigits[] = new int[resLength];
+ int iVal = val.getFirstNonzeroDigit();
+ int iThat = that.getFirstNonzeroDigit();
+ int i = iThat;
+ int limit;
+
+
+ if (iVal == iThat) {
+ resDigits[i] = -val.digits[i] ^ -that.digits[i];
+ } else {
+ resDigits[i] = -that.digits[i];
+ limit = Math.min(that.numberLength, iVal);
+ for (i++; i < limit; i++) {
+ resDigits[i] = ~that.digits[i];
+ }
+ // Remains digits in that?
+ if (i == that.numberLength) {
+ //Jumping over the remaining zero to the first non one
+ for ( ;i < iVal; i++) {
+ //resDigits[i] = 0 ^ -1;
+ resDigits[i] = -1;
+ }
+ //resDigits[i] = -val.digits[i] ^ -1;
+ resDigits[i] = val.digits[i] - 1;
+ } else {
+ resDigits[i] = -val.digits[i] ^ ~that.digits[i];
+ }
+ }
+
+ limit = Math.min(val.numberLength, that.numberLength);
+ //Perform ^ between that al val until that ends
+ for (i++; i < limit; i++) {
+ //resDigits[i] = ~val.digits[i] ^ ~that.digits[i];
+ resDigits[i] = val.digits[i] ^ that.digits[i];
+ }
+ //Perform ^ between val digits and -1 until val ends
+ for ( ; i < val.numberLength; i++) {
+ //resDigits[i] = ~val.digits[i] ^ -1 ;
+ resDigits[i] = val.digits[i] ;
+ }
+ for ( ; i < that.numberLength; i++) {
+ //resDigits[i] = -1 ^ ~that.digits[i] ;
+ resDigits[i] = that.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ /** @return sign = 1, magnitude = -(positive.magnitude ^ -negative.magnitude)*/
+ static TBigInteger xorDiffSigns(TBigInteger positive, TBigInteger negative){
+ int resLength = Math.max(negative.numberLength, positive.numberLength);
+ int resDigits[];
+ int iNeg = negative.getFirstNonzeroDigit();
+ int iPos = positive.getFirstNonzeroDigit();
+ int i;
+ int limit;
+
+ //The first
+ if (iNeg < iPos) {
+ resDigits = new int[resLength];
+ i = iNeg;
+ //resDigits[i] = -(-negative.digits[i]);
+ resDigits[i] = negative.digits[i];
+ limit = Math.min(negative.numberLength, iPos);
+ //Skip the positive digits while they are zeros
+ for (i++; i < limit; i++) {
+ //resDigits[i] = ~(~negative.digits[i]);
+ resDigits[i] = negative.digits[i];
+ }
+ //if the negative has no more elements, must fill the
+ //result with the remaining digits of the positive
+ if (i == negative.numberLength) {
+ for ( ; i < positive.numberLength; i++) {
+ //resDigits[i] = ~(positive.digits[i] ^ -1) -> ~(~positive.digits[i])
+ resDigits[i] = positive.digits[i];
+ }
+ }
+ } else if (iPos < iNeg) {
+ resDigits = new int[resLength];
+ i = iPos;
+ //Applying two complement to the first non-zero digit of the result
+ resDigits[i] = -positive.digits[i];
+ limit = Math.min(positive.numberLength, iNeg);
+ for (i++; i < limit; i++) {
+ //Continue applying two complement the result
+ resDigits[i] = ~positive.digits[i];
+ }
+ //When the first non-zero digit of the negative is reached, must apply
+ //two complement (arithmetic negation) to it, and then operate
+ if (i == iNeg) {
+ resDigits[i] = ~(positive.digits[i] ^ -negative.digits[i]);
+ i++;
+ } else {
+ //if the positive has no more elements must fill the remaining digits with
+ //the negative ones
+ for ( ; i < iNeg; i++) {
+ // resDigits[i] = ~(0 ^ 0)
+ resDigits[i] = -1;
+ }
+ for ( ; i < negative.numberLength; i++) {
+ //resDigits[i] = ~(~negative.digits[i] ^ 0)
+ resDigits[i] = negative.digits[i];
+ }
+ }
+ } else {
+ int digit;
+ //The first non-zero digit of the positive and negative are the same
+ i = iNeg;
+ digit = positive.digits[i] ^ -negative.digits[i];
+ if (digit == 0) {
+ limit = Math.min(positive.numberLength, negative.numberLength);
+ for (i++; i < limit && (digit = positive.digits[i] ^ ~negative.digits[i]) == 0; i++) {
+ // do nothing
+ }
+ if (digit == 0) {
+ // shorter has only the remaining virtual sign bits
+ for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++) {
+ // do nothing
+ }
+ for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++) {
+ // do nothing
+ }
+ if (digit == 0) {
+ resLength = resLength + 1;
+ resDigits = new int[resLength];
+ resDigits[resLength - 1] = 1;
+
+ TBigInteger result = new TBigInteger(-1, resLength, resDigits);
+ return result;
+ }
+ }
+ }
+ resDigits = new int[resLength];
+ resDigits[i] = -digit;
+ i++;
+ }
+
+ limit = Math.min(negative.numberLength, positive.numberLength);
+ for ( ; i < limit; i++) {
+ resDigits[i] = ~(~negative.digits[i] ^ positive.digits[i]);
+ }
+ for ( ; i < positive.numberLength; i++) {
+ // resDigits[i] = ~(positive.digits[i] ^ -1)
+ resDigits[i] = positive.digits[i];
+ }
+ for ( ; i < negative.numberLength; i++) {
+ // resDigits[i] = ~(0 ^ ~negative.digits[i])
+ resDigits[i] = negative.digits[i];
+ }
+
+ TBigInteger result = new TBigInteger(-1, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMathContext.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMathContext.java
new file mode 100644
index 000000000..5dec4fdd6
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMathContext.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+import java.io.Serializable;
+
+/**
+ * Immutable objects describing settings such as rounding mode and digit
+ * precision for the numerical operations provided by class {@link TBigDecimal}.
+ */
+public final class TMathContext implements Serializable {
+
+ /**
+ * A {@code MathContext} which corresponds to the IEEE 754r quadruple
+ * decimal precision format: 34 digit precision and
+ * {@link TRoundingMode#HALF_EVEN} rounding.
+ */
+ public static final TMathContext DECIMAL128 = new TMathContext(34,
+ TRoundingMode.HALF_EVEN);
+
+ /**
+ * A {@code MathContext} which corresponds to the IEEE 754r single decimal
+ * precision format: 7 digit precision and {@link TRoundingMode#HALF_EVEN}
+ * rounding.
+ */
+ public static final TMathContext DECIMAL32 = new TMathContext(7,
+ TRoundingMode.HALF_EVEN);
+
+ /**
+ * A {@code MathContext} which corresponds to the IEEE 754r double decimal
+ * precision format: 16 digit precision and {@link TRoundingMode#HALF_EVEN}
+ * rounding.
+ */
+ public static final TMathContext DECIMAL64 = new TMathContext(16,
+ TRoundingMode.HALF_EVEN);
+
+ /**
+ * A {@code MathContext} for unlimited precision with
+ * {@link TRoundingMode#HALF_UP} rounding.
+ */
+ public static final TMathContext UNLIMITED = new TMathContext(0,
+ TRoundingMode.HALF_UP);
+
+ /** This is the serialVersionUID used by the sun implementation */
+ private static final long serialVersionUID = 5579720004786848255L;
+
+ /**
+ * The number of digits to be used for an operation; results are rounded to
+ * this precision.
+ */
+ private int precision;
+
+ /**
+ * A {@code RoundingMode} object which specifies the algorithm to be used
+ * for rounding.
+ */
+ private TRoundingMode roundingMode;
+
+ /**
+ * An array of {@code char} containing: {@code
+ * 'p','r','e','c','i','s','i','o','n','='}. It's used to improve the
+ * methods related to {@code String} conversion.
+ *
+ * @see #MathContext(String)
+ * @see #toString()
+ */
+ private final static char[] chPrecision = { 'p', 'r', 'e', 'c', 'i', 's',
+ 'i', 'o', 'n', '=' };
+
+ /**
+ * An array of {@code char} containing: {@code
+ * 'r','o','u','n','d','i','n','g','M','o','d','e','='}. It's used to
+ * improve the methods related to {@code String} conversion.
+ *
+ * @see #MathContext(String)
+ * @see #toString()
+ */
+ private final static char[] chRoundingMode = { 'r', 'o', 'u', 'n', 'd',
+ 'i', 'n', 'g', 'M', 'o', 'd', 'e', '=' };
+
+ /**
+ * Constructs a new {@code MathContext} with the specified precision and
+ * with the rounding mode {@link TRoundingMode#HALF_UP HALF_UP}. If the
+ * precision passed is zero, then this implies that the computations have to
+ * be performed exact, the rounding mode in this case is irrelevant.
+ *
+ * @param precision
+ * the precision for the new {@code MathContext}.
+ * @throws IllegalArgumentException
+ * if {@code precision < 0}.
+ */
+ public TMathContext(int precision) {
+ this(precision, TRoundingMode.HALF_UP);
+ }
+
+ /**
+ * Constructs a new {@code MathContext} with the specified precision and
+ * with the specified rounding mode. If the precision passed is zero, then
+ * this implies that the computations have to be performed exact, the
+ * rounding mode in this case is irrelevant.
+ *
+ * @param precision
+ * the precision for the new {@code MathContext}.
+ * @param roundingMode
+ * the rounding mode for the new {@code MathContext}.
+ * @throws IllegalArgumentException
+ * if {@code precision < 0}.
+ * @throws NullPointerException
+ * if {@code roundingMode} is {@code null}.
+ */
+ public TMathContext(int precision, TRoundingMode roundingMode) {
+ if (precision < 0) {
+ throw new IllegalArgumentException("Digits < 0");
+ }
+ if (roundingMode == null) {
+ throw new NullPointerException("null RoundingMode");
+ }
+ this.precision = precision;
+ this.roundingMode = roundingMode;
+ }
+
+ /**
+ * Constructs a new {@code MathContext} from a string. The string has to
+ * specify the precision and the rounding mode to be used and has to follow
+ * the following syntax: "precision=<precision> roundingMode=<roundingMode>"
+ * This is the same form as the one returned by the {@link #toString}
+ * method.
+ *
+ * @param val
+ * a string describing the precision and rounding mode for the
+ * new {@code MathContext}.
+ * @throws IllegalArgumentException
+ * if the string is not in the correct format or if the
+ * precision specified is < 0.
+ */
+ public TMathContext(String val) {
+ char[] charVal = val.toCharArray();
+ int i; // Index of charVal
+ int j; // Index of chRoundingMode
+ int digit; // It will contain the digit parsed
+
+ if ((charVal.length < 27) || (charVal.length > 45)) {
+ throw new IllegalArgumentException("bad string format");
+ }
+ // Parsing "precision=" String
+ for (i = 0; (i < chPrecision.length) && (charVal[i] == chPrecision[i]); i++) {
+ // do nothing
+ }
+
+ if (i < chPrecision.length) {
+ throw new IllegalArgumentException("bad string format");
+ }
+ // Parsing the value for "precision="...
+ digit = Character.digit(charVal[i], 10);
+ if (digit == -1) {
+ throw new IllegalArgumentException("bad string format");
+ }
+ this.precision = this.precision * 10 + digit;
+ i++;
+
+ do {
+ digit = Character.digit(charVal[i], 10);
+ if (digit == -1) {
+ if (charVal[i] == ' ') {
+ // It parsed all the digits
+ i++;
+ break;
+ }
+ // It isn't a valid digit, and isn't a white space
+ throw new IllegalArgumentException("bad string format");
+ }
+ // Accumulating the value parsed
+ this.precision = this.precision * 10 + digit;
+ if (this.precision < 0) {
+ throw new IllegalArgumentException("bad string format");
+ }
+ i++;
+ } while (true);
+ // Parsing "roundingMode="
+ for (j = 0; (j < chRoundingMode.length) && (charVal[i] == chRoundingMode[j]); i++, j++) {
+ // do nothing
+ }
+
+ if (j < chRoundingMode.length) {
+ throw new IllegalArgumentException("bad string format");
+ }
+ // Parsing the value for "roundingMode"...
+ this.roundingMode = TRoundingMode.valueOf(String.valueOf(charVal, i, charVal.length - i));
+ }
+
+ /* Public Methods */
+
+ /**
+ * Returns the precision. The precision is the number of digits used for an
+ * operation. Results are rounded to this precision. The precision is
+ * guaranteed to be non negative. If the precision is zero, then the
+ * computations have to be performed exact, results are not rounded in this
+ * case.
+ *
+ * @return the precision.
+ */
+ public int getPrecision() {
+ return precision;
+ }
+
+ /**
+ * Returns the rounding mode. The rounding mode is the strategy to be used
+ * to round results.
+ *
+ * The rounding mode is one of
+ * {@link TRoundingMode#UP},
+ * {@link TRoundingMode#DOWN},
+ * {@link TRoundingMode#CEILING},
+ * {@link TRoundingMode#FLOOR},
+ * {@link TRoundingMode#HALF_UP},
+ * {@link TRoundingMode#HALF_DOWN},
+ * {@link TRoundingMode#HALF_EVEN}, or
+ * {@link TRoundingMode#UNNECESSARY}.
+ *
+ * @return the rounding mode.
+ */
+ public TRoundingMode getRoundingMode() {
+ return roundingMode;
+ }
+
+ /**
+ * Returns true if x is a {@code MathContext} with the same precision
+ * setting and the same rounding mode as this {@code MathContext} instance.
+ *
+ * @param x
+ * object to be compared.
+ * @return {@code true} if this {@code MathContext} instance is equal to the
+ * {@code x} argument; {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object x) {
+ return ((x instanceof TMathContext)
+ && (((TMathContext) x).getPrecision() == precision) && (((TMathContext) x)
+ .getRoundingMode() == roundingMode));
+ }
+
+ /**
+ * Returns the hash code for this {@code MathContext} instance.
+ *
+ * @return the hash code for this {@code MathContext}.
+ */
+ @Override
+ public int hashCode() {
+ // Make place for the necessary bits to represent 8 rounding modes
+ return ((precision << 3) | roundingMode.ordinal());
+ }
+
+ /**
+ * Returns the string representation for this {@code MathContext} instance.
+ * The string has the form
+ * {@code
+ * "precision=<precision> roundingMode=<roundingMode>"
+ * } where {@code <precision>} is an integer describing the number
+ * of digits used for operations and {@code <roundingMode>} is the
+ * string representation of the rounding mode.
+ *
+ * @return a string representation for this {@code MathContext} instance
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(45);
+
+ sb.append(chPrecision);
+ sb.append(precision);
+ sb.append(' ');
+ sb.append(chRoundingMode);
+ sb.append(roundingMode);
+ return sb.toString();
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMultiplication.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMultiplication.java
new file mode 100644
index 000000000..837175468
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMultiplication.java
@@ -0,0 +1,510 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+/**
+ * Static library that provides all multiplication of {@link TBigInteger} methods.
+ */
+class TMultiplication {
+
+ /** Just to denote that this class can't be instantiated. */
+ private TMultiplication() {}
+
+ /**
+ * Break point in digits (number of {@code int} elements)
+ * between Karatsuba and Pencil and Paper multiply.
+ */
+ static final int whenUseKaratsuba = 63; // an heuristic value
+
+ /**
+ * An array with powers of ten that fit in the type {@code int}.
+ * ({@code 10^0,10^1,...,10^9})
+ */
+ static final int tenPows[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
+ };
+
+ /**
+ * An array with powers of five that fit in the type {@code int}.
+ * ({@code 5^0,5^1,...,5^13})
+ */
+ static final int fivePows[] = {
+ 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625,
+ 1953125, 9765625, 48828125, 244140625, 1220703125
+ };
+
+ /**
+ * An array with the first powers of ten in {@code BigInteger} version.
+ * ({@code 10^0,10^1,...,10^31})
+ */
+ static final TBigInteger[] bigTenPows = new TBigInteger[32];
+
+ /**
+ * An array with the first powers of five in {@code BigInteger} version.
+ * ({@code 5^0,5^1,...,5^31})
+ */
+ static final TBigInteger bigFivePows[] = new TBigInteger[32];
+
+
+
+ static {
+ int i;
+ long fivePow = 1L;
+
+ for (i = 0; i <= 18; i++) {
+ bigFivePows[i] = TBigInteger.valueOf(fivePow);
+ bigTenPows[i] = TBigInteger.valueOf(fivePow << i);
+ fivePow *= 5;
+ }
+ for (; i < bigTenPows.length; i++) {
+ bigFivePows[i] = bigFivePows[i - 1].multiply(bigFivePows[1]);
+ bigTenPows[i] = bigTenPows[i - 1].multiply(TBigInteger.TEN);
+ }
+ }
+
+ /**
+ * Performs a multiplication of two BigInteger and hides the algorithm used.
+ * @see TBigInteger#multiply(TBigInteger)
+ */
+ static TBigInteger multiply(TBigInteger x, TBigInteger y) {
+ return karatsuba(x, y);
+ }
+
+ /**
+ * Performs the multiplication with the Karatsuba's algorithm.
+ * Karatsuba's algorithm:
+ *
+ * u = u1 * B + u0
+ * v = v1 * B + v0
+ *
+ *
+ * u*v = (u1 * v1) * B2 + ((u1 - u0) * (v0 - v1) + u1 * v1 +
+ * u0 * v0 ) * B + u0 * v0
+ *
+ * @param op1 first factor of the product
+ * @param op2 second factor of the product
+ * @return {@code op1 * op2}
+ * @see #multiply(TBigInteger, TBigInteger)
+ */
+ static TBigInteger karatsuba(TBigInteger op1, TBigInteger op2) {
+ TBigInteger temp;
+ if (op2.numberLength > op1.numberLength) {
+ temp = op1;
+ op1 = op2;
+ op2 = temp;
+ }
+ if (op2.numberLength < whenUseKaratsuba) {
+ return multiplyPAP(op1, op2);
+ }
+ /* Karatsuba: u = u1*B + u0
+ * v = v1*B + v0
+ * u*v = (u1*v1)*B^2 + ((u1-u0)*(v0-v1) + u1*v1 + u0*v0)*B + u0*v0
+ */
+ // ndiv2 = (op1.numberLength / 2) * 32
+ int ndiv2 = (op1.numberLength & 0xFFFFFFFE) << 4;
+ TBigInteger upperOp1 = op1.shiftRight(ndiv2);
+ TBigInteger upperOp2 = op2.shiftRight(ndiv2);
+ TBigInteger lowerOp1 = op1.subtract(upperOp1.shiftLeft(ndiv2));
+ TBigInteger lowerOp2 = op2.subtract(upperOp2.shiftLeft(ndiv2));
+
+ TBigInteger upper = karatsuba(upperOp1, upperOp2);
+ TBigInteger lower = karatsuba(lowerOp1, lowerOp2);
+ TBigInteger middle = karatsuba( upperOp1.subtract(lowerOp1),
+ lowerOp2.subtract(upperOp2));
+ middle = middle.add(upper).add(lower);
+ middle = middle.shiftLeft(ndiv2);
+ upper = upper.shiftLeft(ndiv2 << 1);
+
+ return upper.add(middle).add(lower);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ * Implements traditional scholar algorithm described by Knuth.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * A= |
+ * a3 |
+ * a2 |
+ * a1 |
+ * a0 |
+ * |
+ * |
+ *
+ *
+ *
+ * B= |
+ * |
+ * b2 |
+ * b1 |
+ * b1 |
+ * |
+ * |
+ *
+ *
+ *
+ * |
+ * |
+ * |
+ * b0*a3 |
+ * b0*a2 |
+ * b0*a1 |
+ * b0*a0 |
+ *
+ *
+ *
+ * |
+ * |
+ * b1*a3 |
+ * b1*a2 |
+ * b1*a1 |
+ * b1*a0 |
+ *
+ *
+ *
+ * + |
+ * b2*a3 |
+ * b2*a2 |
+ * b2*a1 |
+ * b2*a0 |
+ *
+ *
+ *
+ * |
+ *______ |
+ * ______ |
+ * ______ |
+ * ______ |
+ * ______ |
+ * ______ |
+ *
+ *
+ *
+ *
+ * A*B=R= |
+ * r5 |
+ * r4 |
+ * r3 |
+ * r2 |
+ * r1 |
+ * r0 |
+ * |
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param op1 first factor of the multiplication {@code op1 >= 0}
+ * @param op2 second factor of the multiplication {@code op2 >= 0}
+ * @return a {@code BigInteger} of value {@code op1 * op2}
+ */
+ static TBigInteger multiplyPAP(TBigInteger a, TBigInteger b) {
+ // PRE: a >= b
+ int aLen = a.numberLength;
+ int bLen = b.numberLength;
+ int resLength = aLen + bLen;
+ int resSign = (a.sign != b.sign) ? -1 : 1;
+ // A special case when both numbers don't exceed int
+ if (resLength == 2) {
+ long val = unsignedMultAddAdd(a.digits[0], b.digits[0], 0, 0);
+ int valueLo = (int)val;
+ int valueHi = (int)(val >>> 32);
+ return ((valueHi == 0)
+ ? new TBigInteger(resSign, valueLo)
+ : new TBigInteger(resSign, 2, new int[]{valueLo, valueHi}));
+ }
+ int[] aDigits = a.digits;
+ int[] bDigits = b.digits;
+ int resDigits[] = new int[resLength];
+ // Common case
+ multArraysPAP(aDigits, aLen, bDigits, bLen, resDigits);
+ TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ static void multArraysPAP(int[] aDigits, int aLen, int[] bDigits, int bLen, int[] resDigits) {
+ if(aLen == 0 || bLen == 0) return;
+
+ if(aLen == 1) {
+ resDigits[bLen] = multiplyByInt(resDigits, bDigits, bLen, aDigits[0]);
+ } else if(bLen == 1) {
+ resDigits[aLen] = multiplyByInt(resDigits, aDigits, aLen, bDigits[0]);
+ } else {
+ multPAP(aDigits, bDigits, resDigits, aLen, bLen);
+ }
+ }
+
+ static void multPAP(int a[], int b[], int t[], int aLen, int bLen) {
+ if(a == b && aLen == bLen) {
+ square(a, aLen, t);
+ return;
+ }
+
+ for(int i = 0; i < aLen; i++){
+ long carry = 0;
+ int aI = a[i];
+ for (int j = 0; j < bLen; j++){
+ carry = unsignedMultAddAdd(aI, b[j], t[i+j], (int)carry);
+ t[i+j] = (int) carry;
+ carry >>>= 32;
+ }
+ t[i+bLen] = (int) carry;
+ }
+ }
+
+ /**
+ * Multiplies an array of integers by an integer value
+ * and saves the result in {@code res}.
+ * @param a the array of integers
+ * @param aSize the number of elements of intArray to be multiplied
+ * @param factor the multiplier
+ * @return the top digit of production
+ */
+ private static int multiplyByInt(int res[], int a[], final int aSize, final int factor) {
+ long carry = 0;
+ for (int i = 0; i < aSize; i++) {
+ carry = unsignedMultAddAdd(a[i], factor, (int)carry, 0);
+ res[i] = (int)carry;
+ carry >>>= 32;
+ }
+ return (int)carry;
+ }
+
+
+ /**
+ * Multiplies an array of integers by an integer value.
+ * @param a the array of integers
+ * @param aSize the number of elements of intArray to be multiplied
+ * @param factor the multiplier
+ * @return the top digit of production
+ */
+ static int multiplyByInt(int a[], final int aSize, final int factor) {
+ return multiplyByInt(a, a, aSize, factor);
+ }
+
+ /**
+ * Multiplies a number by a positive integer.
+ * @param val an arbitrary {@code BigInteger}
+ * @param factor a positive {@code int} number
+ * @return {@code val * factor}
+ */
+ static TBigInteger multiplyByPositiveInt(TBigInteger val, int factor) {
+ int resSign = val.sign;
+ if (resSign == 0) {
+ return TBigInteger.ZERO;
+ }
+ int aNumberLength = val.numberLength;
+ int[] aDigits = val.digits;
+
+ if (aNumberLength == 1) {
+ long res = unsignedMultAddAdd(aDigits[0], factor, 0, 0);
+ int resLo = (int)res;
+ int resHi = (int)(res >>> 32);
+ return ((resHi == 0)
+ ? new TBigInteger(resSign, resLo)
+ : new TBigInteger(resSign, 2, new int[]{resLo, resHi}));
+ }
+ // Common case
+ int resLength = aNumberLength + 1;
+ int resDigits[] = new int[resLength];
+
+ resDigits[aNumberLength] = multiplyByInt(resDigits, aDigits, aNumberLength, factor);
+ TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
+ result.cutOffLeadingZeroes();
+ return result;
+ }
+
+ static TBigInteger pow(TBigInteger base, int exponent) {
+ // PRE: exp > 0
+ TBigInteger res = TBigInteger.ONE;
+ TBigInteger acc = base;
+
+ for (; exponent > 1; exponent >>= 1) {
+ if ((exponent & 1) != 0) {
+ // if odd, multiply one more time by acc
+ res = res.multiply(acc);
+ }
+ // acc = base^(2^i)
+ //a limit where karatsuba performs a faster square than the square algorithm
+ if ( acc.numberLength == 1 ){
+ acc = acc.multiply(acc); // square
+ }
+ else{
+ acc = new TBigInteger(1, square(acc.digits, acc.numberLength, new int [acc.numberLength<<1]));
+ }
+ }
+ // exponent == 1, multiply one more time
+ res = res.multiply(acc);
+ return res;
+ }
+
+ /**
+ * Performs a2
+ * @param a The number to square.
+ * @param aLen The length of the number to square.
+ */
+ static int[] square(int[] a, int aLen, int[] res) {
+ long carry;
+
+ for(int i = 0; i < aLen; i++){
+ carry = 0;
+ for (int j = i+1; j < aLen; j++){
+ carry = unsignedMultAddAdd(a[i], a[j], res[i+j], (int)carry);
+ res[i+j] = (int) carry;
+ carry >>>= 32;
+ }
+ res[i+aLen] = (int) carry;
+ }
+
+ TBitLevel.shiftLeftOneBit(res, res, aLen << 1);
+
+ carry = 0;
+ for(int i = 0, index = 0; i < aLen; i++, index++){
+ carry = unsignedMultAddAdd(a[i], a[i], res[index],(int)carry);
+ res[index] = (int) carry;
+ carry >>>= 32;
+ index++;
+ carry += res[index] & 0xFFFFFFFFL;
+ res[index] = (int)carry;
+ carry >>>= 32;
+ }
+ return res;
+ }
+
+ /**
+ * Multiplies a number by a power of ten.
+ * This method is used in {@code BigDecimal} class.
+ * @param val the number to be multiplied
+ * @param exp a positive {@code long} exponent
+ * @return {@code val * 10exp}
+ */
+ static TBigInteger multiplyByTenPow(TBigInteger val, long exp) {
+ // PRE: exp >= 0
+ return ((exp < tenPows.length)
+ ? multiplyByPositiveInt(val, tenPows[(int)exp])
+ : val.multiply(powerOf10(exp)));
+ }
+
+ /**
+ * It calculates a power of ten, which exponent could be out of 32-bit range.
+ * Note that internally this method will be used in the worst case with
+ * an exponent equals to: {@code Integer.MAX_VALUE - Integer.MIN_VALUE}.
+ * @param exp the exponent of power of ten, it must be positive.
+ * @return a {@code BigInteger} with value {@code 10exp}.
+ */
+ static TBigInteger powerOf10(long exp) {
+ // PRE: exp >= 0
+ int intExp = (int)exp;
+ // "SMALL POWERS"
+ if (exp < bigTenPows.length) {
+ // The largest power that fit in 'long' type
+ return bigTenPows[intExp];
+ } else if (exp <= 50) {
+ // To calculate: 10^exp
+ return TBigInteger.TEN.pow(intExp);
+ } else if (exp <= 1000) {
+ // To calculate: 5^exp * 2^exp
+ return bigFivePows[1].pow(intExp).shiftLeft(intExp);
+ }
+ // "LARGE POWERS"
+ /*
+ * To check if there is free memory to allocate a BigInteger of the
+ * estimated size, measured in bytes: 1 + [exp / log10(2)]
+ */
+ long byteArraySize = 1 + (long)(exp / 2.4082399653118496);
+
+ if (byteArraySize > 1000000) {
+ throw new ArithmeticException("power of ten too big");
+ }
+ if (exp <= Integer.MAX_VALUE) {
+ // To calculate: 5^exp * 2^exp
+ return bigFivePows[1].pow(intExp).shiftLeft(intExp);
+ }
+ /*
+ * "HUGE POWERS"
+ *
+ * This branch probably won't be executed since the power of ten is too
+ * big.
+ */
+ // To calculate: 5^exp
+ TBigInteger powerOfFive = bigFivePows[1].pow(Integer.MAX_VALUE);
+ TBigInteger res = powerOfFive;
+ long longExp = exp - Integer.MAX_VALUE;
+
+ intExp = (int)(exp % Integer.MAX_VALUE);
+ while (longExp > Integer.MAX_VALUE) {
+ res = res.multiply(powerOfFive);
+ longExp -= Integer.MAX_VALUE;
+ }
+ res = res.multiply(bigFivePows[1].pow(intExp));
+ // To calculate: 5^exp << exp
+ res = res.shiftLeft(Integer.MAX_VALUE);
+ longExp = exp - Integer.MAX_VALUE;
+ while (longExp > Integer.MAX_VALUE) {
+ res = res.shiftLeft(Integer.MAX_VALUE);
+ longExp -= Integer.MAX_VALUE;
+ }
+ res = res.shiftLeft(intExp);
+ return res;
+ }
+
+ /**
+ * Multiplies a number by a power of five.
+ * This method is used in {@code BigDecimal} class.
+ * @param val the number to be multiplied
+ * @param exp a positive {@code int} exponent
+ * @return {@code val * 5exp}
+ */
+ static TBigInteger multiplyByFivePow(TBigInteger val, int exp) {
+ // PRE: exp >= 0
+ if (exp < fivePows.length) {
+ return multiplyByPositiveInt(val, fivePows[exp]);
+ } else if (exp < bigFivePows.length) {
+ return val.multiply(bigFivePows[exp]);
+ } else {// Large powers of five
+ return val.multiply(bigFivePows[1].pow(exp));
+ }
+ }
+
+ /**
+ * Computes the value unsigned ((uint)a*(uint)b + (uint)c + (uint)d). This
+ * method could improve the readability and performance of the code.
+ *
+ * @param a
+ * parameter 1
+ * @param b
+ * parameter 2
+ * @param c
+ * parameter 3
+ * @param d
+ * parameter 4
+ * @return value of expression
+ */
+ static long unsignedMultAddAdd(int a, int b, int c, int d) {
+ return (a & 0xFFFFFFFFL) * (b & 0xFFFFFFFFL) + (c & 0xFFFFFFFFL) + (d & 0xFFFFFFFFL);
+ }
+
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TPrimality.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TPrimality.java
new file mode 100644
index 000000000..2f6bbd78a
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TPrimality.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Provides primality probabilistic methods.
+ */
+class TPrimality {
+
+ /** Just to denote that this class can't be instantiated. */
+ private TPrimality() {
+ }
+
+ /** All prime numbers with bit length lesser than 10 bits. */
+ private static final int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
+ 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
+ 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433,
+ 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
+ 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
+ 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
+ 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,
+ 1009, 1013, 1019, 1021 };
+
+ /** All {@code BigInteger} prime numbers with bit length lesser than 8 bits. */
+ private static final TBigInteger BIprimes[] = new TBigInteger[primes.length];
+
+ /**
+ * It encodes how many iterations of Miller-Rabin test are need to get an
+ * error bound not greater than {@code 2(-100)}. For example: for
+ * a {@code 1000}-bit number we need {@code 4} iterations, since
+ * {@code BITS[3] < 1000 <= BITS[4]}.
+ */
+ private static final int[] BITS = { 0, 0, 1854, 1233, 927, 747, 627, 543, 480, 431, 393, 361, 335, 314, 295, 279,
+ 265, 253, 242, 232, 223, 216, 181, 169, 158, 150, 145, 140, 136, 132, 127, 123, 119, 114, 110, 105, 101,
+ 96, 92, 87, 83, 78, 73, 69, 64, 59, 54, 49, 44, 38, 32, 26, 1 };
+
+ /**
+ * It encodes how many i-bit primes there are in the table for
+ * {@code i=2,...,10}. For example {@code offsetPrimes[6]} says that from
+ * index {@code 11} exists {@code 7} consecutive {@code 6}-bit prime numbers
+ * in the array.
+ */
+ private static final int[][] offsetPrimes = { null, null, { 0, 2 }, { 2, 2 }, { 4, 2 }, { 6, 5 }, { 11, 7 },
+ { 18, 13 }, { 31, 23 }, { 54, 43 }, { 97, 75 } };
+
+ static {// To initialize the dual table of BigInteger primes
+ for (int i = 0; i < primes.length; i++) {
+ BIprimes[i] = TBigInteger.valueOf(primes[i]);
+ }
+ }
+
+ /**
+ * It uses the sieve of Eratosthenes to discard several composite numbers in
+ * some appropriate range (at the moment {@code [this, this + 1024]}). After
+ * this process it applies the Miller-Rabin test to the numbers that were
+ * not discarded in the sieve.
+ *
+ * @see TBigInteger#nextProbablePrime()
+ * @see #millerRabin(TBigInteger, int)
+ */
+ static TBigInteger nextProbablePrime(TBigInteger n) {
+ // PRE: n >= 0
+ int i, j;
+ int certainty;
+ int gapSize = 1024; // for searching of the next probable prime number
+ int modules[] = new int[primes.length];
+ boolean isDivisible[] = new boolean[gapSize];
+ TBigInteger startPoint;
+ TBigInteger probPrime;
+ // If n < "last prime of table" searches next prime in the table
+ if ((n.numberLength == 1) && (n.digits[0] >= 0) && (n.digits[0] < primes[primes.length - 1])) {
+ for (i = 0; n.digits[0] >= primes[i]; i++) {
+ // do nothing
+ }
+ return BIprimes[i];
+ }
+ /*
+ * Creates a "N" enough big to hold the next probable prime Note that: N
+ * < "next prime" < 2*N
+ */
+ startPoint = new TBigInteger(1, n.numberLength, new int[n.numberLength + 1]);
+ System.arraycopy(n.digits, 0, startPoint.digits, 0, n.numberLength);
+ // To fix N to the "next odd number"
+ if (n.testBit(0)) {
+ TElementary.inplaceAdd(startPoint, 2);
+ } else {
+ startPoint.digits[0] |= 1;
+ }
+ // To set the improved certainly of Miller-Rabin
+ j = startPoint.bitLength();
+ for (certainty = 2; j < BITS[certainty]; certainty++) {
+ // do nothing
+ }
+ // To calculate modules: N mod p1, N mod p2, ... for first primes.
+ for (i = 0; i < primes.length; i++) {
+ modules[i] = TDivision.remainder(startPoint, primes[i]) - gapSize;
+ }
+ while (true) {
+ // At this point, all numbers in the gap are initialized as
+ // probably primes
+ Arrays.fill(isDivisible, false);
+ // To discard multiples of first primes
+ for (i = 0; i < primes.length; i++) {
+ modules[i] = (modules[i] + gapSize) % primes[i];
+ j = (modules[i] == 0) ? 0 : (primes[i] - modules[i]);
+ for (; j < gapSize; j += primes[i]) {
+ isDivisible[j] = true;
+ }
+ }
+ // To execute Miller-Rabin for non-divisible numbers by all first
+ // primes
+ for (j = 0; j < gapSize; j++) {
+ if (!isDivisible[j]) {
+ probPrime = startPoint.copy();
+ TElementary.inplaceAdd(probPrime, j);
+
+ if (millerRabin(probPrime, certainty)) {
+ return probPrime;
+ }
+ }
+ }
+ TElementary.inplaceAdd(startPoint, gapSize);
+ }
+ }
+
+ /**
+ * A random number is generated until a probable prime number is found.
+ *
+ * @see TBigInteger#BigInteger(int,int,Random)
+ * @see TBigInteger#probablePrime(int,Random)
+ * @see #isProbablePrime(TBigInteger, int)
+ */
+ static TBigInteger consBigInteger(int bitLength, int certainty, Random rnd) {
+ // PRE: bitLength >= 2;
+ // For small numbers get a random prime from the prime table
+ if (bitLength <= 10) {
+ int rp[] = offsetPrimes[bitLength];
+ return BIprimes[rp[0] + rnd.nextInt(rp[1])];
+ }
+ int shiftCount = (-bitLength) & 31;
+ int last = (bitLength + 31) >> 5;
+ TBigInteger n = new TBigInteger(1, last, new int[last]);
+
+ last--;
+ do {// To fill the array with random integers
+ for (int i = 0; i < n.numberLength; i++) {
+ n.digits[i] = rnd.nextInt();
+ }
+ // To fix to the correct bitLength
+ n.digits[last] |= 0x80000000;
+ n.digits[last] >>>= shiftCount;
+ // To create an odd number
+ n.digits[0] |= 1;
+ } while (!isProbablePrime(n, certainty));
+ return n;
+ }
+
+ /**
+ * @see TBigInteger#isProbablePrime(int)
+ * @see #millerRabin(TBigInteger, int)
+ * @ar.org.fitc.ref Optimizations: "A. Menezes - Handbook of applied
+ * Cryptography, Chapter 4".
+ */
+ static boolean isProbablePrime(TBigInteger n, int certainty) {
+ // PRE: n >= 0;
+ if ((certainty <= 0) || ((n.numberLength == 1) && (n.digits[0] == 2))) {
+ return true;
+ }
+ // To discard all even numbers
+ if (!n.testBit(0)) {
+ return false;
+ }
+ // To check if 'n' exists in the table (it fit in 10 bits)
+ if ((n.numberLength == 1) && ((n.digits[0] & 0XFFFFFC00) == 0)) {
+ return (Arrays.binarySearch(primes, n.digits[0]) >= 0);
+ }
+ // To check if 'n' is divisible by some prime of the table
+ for (int i = 1; i < primes.length; i++) {
+ if (TDivision.remainderArrayByInt(n.digits, n.numberLength, primes[i]) == 0) {
+ return false;
+ }
+ }
+ // To set the number of iterations necessary for Miller-Rabin test
+ int i;
+ int bitLength = n.bitLength();
+
+ for (i = 2; bitLength < BITS[i]; i++) {
+ // do nothing
+ }
+ certainty = Math.min(i, 1 + ((certainty - 1) >> 1));
+
+ return millerRabin(n, certainty);
+ }
+
+ /**
+ * The Miller-Rabin primality test.
+ *
+ * @param n
+ * the input number to be tested.
+ * @param t
+ * the number of trials.
+ * @return {@code false} if the number is definitely compose, otherwise
+ * {@code true} with probability {@code 1 - 4(-t)}.
+ * @ar.org.fitc.ref "D. Knuth, The Art of Computer Programming Vo.2, Section
+ * 4.5.4., Algorithm P"
+ */
+ private static boolean millerRabin(TBigInteger n, int t) {
+ // PRE: n >= 0, t >= 0
+ TBigInteger x; // x := UNIFORM{2...n-1}
+ TBigInteger y; // y := x^(q * 2^j) mod n
+ TBigInteger n_minus_1 = n.subtract(TBigInteger.ONE); // n-1
+ int bitLength = n_minus_1.bitLength(); // ~ log2(n-1)
+ // (q,k) such that: n-1 = q * 2^k and q is odd
+ int k = n_minus_1.getLowestSetBit();
+ TBigInteger q = n_minus_1.shiftRight(k);
+ Random rnd = new Random();
+
+ for (int i = 0; i < t; i++) {
+ // To generate a witness 'x', first it use the primes of table
+ if (i < primes.length) {
+ x = BIprimes[i];
+ } else {/*
+ * It generates random witness only if it's necesssary. Note
+ * that all methods would call Miller-Rabin with t <= 50 so
+ * this part is only to do more robust the algorithm
+ */
+ do {
+ x = new TBigInteger(bitLength, rnd);
+ } while ((x.compareTo(n) >= TBigInteger.EQUALS) || (x.sign == 0) || x.isOne());
+ }
+ y = x.modPow(q, n);
+ if (y.isOne() || y.equals(n_minus_1)) {
+ continue;
+ }
+ for (int j = 1; j < k; j++) {
+ if (y.equals(n_minus_1)) {
+ continue;
+ }
+ y = y.multiply(y).mod(n);
+ if (y.isOne()) {
+ return false;
+ }
+ }
+ if (!y.equals(n_minus_1)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TRoundingMode.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TRoundingMode.java
new file mode 100644
index 000000000..e6fe46451
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TRoundingMode.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.math;
+
+/**
+ * Specifies the rounding behavior for operations whose results cannot be
+ * represented exactly.
+ */
+public enum TRoundingMode {
+
+ /**
+ * Rounding mode where positive values are rounded towards positive infinity
+ * and negative values towards negative infinity.
+ *
+ * Rule: {@code x.round().abs() >= x.abs()}
+ */
+ UP(TBigDecimal.ROUND_UP),
+
+ /**
+ * Rounding mode where the values are rounded towards zero.
+ *
+ * Rule: {@code x.round().abs() <= x.abs()}
+ */
+ DOWN(TBigDecimal.ROUND_DOWN),
+
+ /**
+ * Rounding mode to round towards positive infinity. For positive values
+ * this rounding mode behaves as {@link #UP}, for negative values as
+ * {@link #DOWN}.
+ *
+ * Rule: {@code x.round() >= x}
+ */
+ CEILING(TBigDecimal.ROUND_CEILING),
+
+ /**
+ * Rounding mode to round towards negative infinity. For positive values
+ * this rounding mode behaves as {@link #DOWN}, for negative values as
+ * {@link #UP}.
+ *
+ * Rule: {@code x.round() <= x}
+ */
+ FLOOR(TBigDecimal.ROUND_FLOOR),
+
+ /**
+ * Rounding mode where values are rounded towards the nearest neighbor. Ties
+ * are broken by rounding up.
+ */
+ HALF_UP(TBigDecimal.ROUND_HALF_UP),
+
+ /**
+ * Rounding mode where values are rounded towards the nearest neighbor. Ties
+ * are broken by rounding down.
+ */
+ HALF_DOWN(TBigDecimal.ROUND_HALF_DOWN),
+
+ /**
+ * Rounding mode where values are rounded towards the nearest neighbor. Ties
+ * are broken by rounding to the even neighbor.
+ */
+ HALF_EVEN(TBigDecimal.ROUND_HALF_EVEN),
+
+ /**
+ * Rounding mode where the rounding operations throws an ArithmeticException
+ * for the case that rounding is necessary, i.e. for the case that the value
+ * cannot be represented exactly.
+ */
+ UNNECESSARY(TBigDecimal.ROUND_UNNECESSARY);
+
+ /** The old constant of BigDecimal
. */
+ @SuppressWarnings("unused")
+ private final int bigDecimalRM;
+
+ /** It sets the old constant. */
+ TRoundingMode(int rm) {
+ bigDecimalRM = rm;
+ }
+
+ /**
+ * Converts rounding mode constants from class {@code BigDecimal} into
+ * {@code RoundingMode} values.
+ *
+ * @param mode
+ * rounding mode constant as defined in class {@code BigDecimal}
+ * @return corresponding rounding mode object
+ */
+ public static TRoundingMode valueOf(int mode) {
+ switch (mode) {
+ case TBigDecimal.ROUND_CEILING:
+ return CEILING;
+ case TBigDecimal.ROUND_DOWN:
+ return DOWN;
+ case TBigDecimal.ROUND_FLOOR:
+ return FLOOR;
+ case TBigDecimal.ROUND_HALF_DOWN:
+ return HALF_DOWN;
+ case TBigDecimal.ROUND_HALF_EVEN:
+ return HALF_EVEN;
+ case TBigDecimal.ROUND_HALF_UP:
+ return HALF_UP;
+ case TBigDecimal.ROUND_UNNECESSARY:
+ return UNNECESSARY;
+ case TBigDecimal.ROUND_UP:
+ return UP;
+ default:
+ throw new IllegalArgumentException("Invalid rounding mode");
+ }
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAnnotation.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAnnotation.java
new file mode 100644
index 000000000..a7b28b68a
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAnnotation.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+public class TAnnotation {
+ private Object value;
+
+ public TAnnotation(Object attribute) {
+ value = attribute;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[value=" + value + ']';
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAttributedCharacterIterator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAttributedCharacterIterator.java
new file mode 100644
index 000000000..f3baf6960
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAttributedCharacterIterator.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.java.io.TSerializable;
+import org.teavm.classlib.java.util.TMap;
+import org.teavm.classlib.java.util.TSet;
+
+public interface TAttributedCharacterIterator extends TCharacterIterator {
+
+ public static class Attribute implements TSerializable {
+ public static final Attribute INPUT_METHOD_SEGMENT = new Attribute(
+ "input_method_segment");
+
+ public static final Attribute LANGUAGE = new Attribute("language");
+
+ public static final Attribute READING = new Attribute("reading");
+
+ private String name;
+
+ protected Attribute(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public final boolean equals(Object object) {
+ return this == object;
+ }
+
+ protected String getName() {
+ return name;
+ }
+
+ @Override
+ public final int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + '(' + getName() + ')';
+ }
+ }
+
+ /**
+ * Returns a set of attributes present in the {@code
+ * AttributedCharacterIterator}. An empty set is returned if no attributes
+ * were defined.
+ *
+ * @return a set of attribute keys; may be empty.
+ */
+ public TSet getAllAttributeKeys();
+
+ /**
+ * Returns the value stored in the attribute for the current character. If
+ * the attribute was not defined then {@code null} is returned.
+ *
+ * @param attribute the attribute for which the value should be returned.
+ * @return the value of the requested attribute for the current character or
+ * {@code null} if it was not defined.
+ */
+ public Object getAttribute(Attribute attribute);
+
+ /**
+ * Returns a map of all attributes of the current character. If no
+ * attributes were defined for the current character then an empty map is
+ * returned.
+ *
+ * @return a map of all attributes for the current character or an empty
+ * map.
+ */
+ public TMap getAttributes();
+
+ /**
+ * Returns the index of the last character in the run having the same
+ * attributes as the current character.
+ *
+ * @return the index of the last character of the current run.
+ */
+ public int getRunLimit();
+
+ /**
+ * Returns the index of the last character in the run that has the same
+ * attribute value for the given attribute as the current character.
+ *
+ * @param attribute
+ * the attribute which the run is based on.
+ * @return the index of the last character of the current run.
+ */
+ public int getRunLimit(Attribute attribute);
+
+ /**
+ * Returns the index of the last character in the run that has the same
+ * attribute values for the attributes in the set as the current character.
+ *
+ * @param attributes
+ * the set of attributes which the run is based on.
+ * @return the index of the last character of the current run.
+ */
+ public int getRunLimit(TSet extends Attribute> attributes);
+
+ /**
+ * Returns the index of the first character in the run that has the same
+ * attributes as the current character.
+ *
+ * @return the index of the last character of the current run.
+ */
+ public int getRunStart();
+
+ /**
+ * Returns the index of the first character in the run that has the same
+ * attribute value for the given attribute as the current character.
+ *
+ * @param attribute
+ * the attribute which the run is based on.
+ * @return the index of the last character of the current run.
+ */
+ public int getRunStart(Attribute attribute);
+
+ /**
+ * Returns the index of the first character in the run that has the same
+ * attribute values for the attributes in the set as the current character.
+ *
+ * @param attributes
+ * the set of attributes which the run is based on.
+ * @return the index of the last character of the current run.
+ */
+ public int getRunStart(TSet extends Attribute> attributes);
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAttributedString.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAttributedString.java
new file mode 100644
index 000000000..d59073f56
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TAttributedString.java
@@ -0,0 +1,630 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.java.text.TAttributedCharacterIterator.Attribute;
+import org.teavm.classlib.java.util.*;
+
+public class TAttributedString {
+
+ String text;
+
+ TMap> attributeMap;
+
+ static class Range {
+ int start;
+
+ int end;
+
+ Object value;
+
+ Range(int s, int e, Object v) {
+ start = s;
+ end = e;
+ value = v;
+ }
+ }
+
+ static class AttributedIterator implements TAttributedCharacterIterator {
+
+ private int begin, end, offset;
+
+ private TAttributedString attrString;
+
+ private THashSet attributesAllowed;
+
+ AttributedIterator(TAttributedString attrString) {
+ this.attrString = attrString;
+ begin = 0;
+ end = attrString.text.length();
+ offset = 0;
+ }
+
+ AttributedIterator(TAttributedString attrString, TAttributedCharacterIterator.Attribute[] attributes, int begin,
+ int end) {
+ if (begin < 0 || end > attrString.text.length() || begin > end) {
+ throw new IllegalArgumentException();
+ }
+ this.begin = begin;
+ this.end = end;
+ offset = begin;
+ this.attrString = attrString;
+ if (attributes != null) {
+ THashSet set = new THashSet<>((attributes.length * 4 / 3) + 1);
+ for (int i = attributes.length; --i >= 0;) {
+ set.add(attributes[i]);
+ }
+ attributesAllowed = set;
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object clone() {
+ try {
+ AttributedIterator clone = (AttributedIterator) super.clone();
+ if (attributesAllowed != null) {
+ clone.attributesAllowed = (THashSet) attributesAllowed.clone();
+ }
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public char current() {
+ if (offset == end) {
+ return DONE;
+ }
+ return attrString.text.charAt(offset);
+ }
+
+ @Override
+ public char first() {
+ if (begin == end) {
+ return DONE;
+ }
+ offset = begin;
+ return attrString.text.charAt(offset);
+ }
+
+ @Override
+ public int getBeginIndex() {
+ return begin;
+ }
+
+ @Override
+ public int getEndIndex() {
+ return end;
+ }
+
+ @Override
+ public int getIndex() {
+ return offset;
+ }
+
+ private boolean inRange(Range range) {
+ if (!(range.value instanceof TAnnotation)) {
+ return true;
+ }
+ return range.start >= begin && range.start < end && range.end > begin && range.end <= end;
+ }
+
+ private boolean inRange(TList ranges) {
+ TIterator it = ranges.iterator();
+ while (it.hasNext()) {
+ Range range = it.next();
+ if (range.start >= begin && range.start < end) {
+ return !(range.value instanceof TAnnotation) || (range.end > begin && range.end <= end);
+ } else if (range.end > begin && range.end <= end) {
+ return !(range.value instanceof TAnnotation) || (range.start >= begin && range.start < end);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public TSet getAllAttributeKeys() {
+ if (begin == 0 && end == attrString.text.length() && attributesAllowed == null) {
+ return attrString.attributeMap.keySet();
+ }
+
+ TSet result = new THashSet<>((attrString.attributeMap.size() * 4 / 3) + 1);
+ TIterator>> it = attrString.attributeMap.entrySet().iterator();
+ while (it.hasNext()) {
+ TMap.Entry> entry = it.next();
+ if (attributesAllowed == null || attributesAllowed.contains(entry.getKey())) {
+ TList ranges = entry.getValue();
+ if (inRange(ranges)) {
+ result.add(entry.getKey());
+ }
+ }
+ }
+ return result;
+ }
+
+ private Object currentValue(TList ranges) {
+ TIterator it = ranges.iterator();
+ while (it.hasNext()) {
+ Range range = it.next();
+ if (offset >= range.start && offset < range.end) {
+ return inRange(range) ? range.value : null;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Object getAttribute(TAttributedCharacterIterator.Attribute attribute) {
+ if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
+ return null;
+ }
+ TArrayList ranges = (TArrayList) attrString.attributeMap.get(attribute);
+ if (ranges == null) {
+ return null;
+ }
+ return currentValue(ranges);
+ }
+
+ @Override
+ public TMap getAttributes() {
+ TMap result = new THashMap<>((attrString.attributeMap.size() * 4 / 3) + 1);
+ TIterator>> it = attrString.attributeMap.entrySet().iterator();
+ while (it.hasNext()) {
+ TMap.Entry> entry = it.next();
+ if (attributesAllowed == null || attributesAllowed.contains(entry.getKey())) {
+ Object value = currentValue(entry.getValue());
+ if (value != null) {
+ result.put(entry.getKey(), value);
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public int getRunLimit() {
+ return getRunLimit(getAllAttributeKeys());
+ }
+
+ private int runLimit(TList ranges) {
+ int result = end;
+ TListIterator it = ranges.listIterator(ranges.size());
+ while (it.hasPrevious()) {
+ Range range = it.previous();
+ if (range.end <= begin) {
+ break;
+ }
+ if (offset >= range.start && offset < range.end) {
+ return inRange(range) ? range.end : result;
+ } else if (offset >= range.end) {
+ break;
+ }
+ result = range.start;
+ }
+ return result;
+ }
+
+ @Override
+ public int getRunLimit(TAttributedCharacterIterator.Attribute attribute) {
+ if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
+ return end;
+ }
+ TArrayList ranges = (TArrayList) attrString.attributeMap.get(attribute);
+ if (ranges == null) {
+ return end;
+ }
+ return runLimit(ranges);
+ }
+
+ @Override
+ public int getRunLimit(TSet extends Attribute> attributes) {
+ int limit = end;
+ TIterator extends Attribute> it = attributes.iterator();
+ while (it.hasNext()) {
+ TAttributedCharacterIterator.Attribute attribute = it.next();
+ int newLimit = getRunLimit(attribute);
+ if (newLimit < limit) {
+ limit = newLimit;
+ }
+ }
+ return limit;
+ }
+
+ @Override
+ public int getRunStart() {
+ return getRunStart(getAllAttributeKeys());
+ }
+
+ private int runStart(TList ranges) {
+ int result = begin;
+ TIterator it = ranges.iterator();
+ while (it.hasNext()) {
+ Range range = it.next();
+ if (range.start >= end) {
+ break;
+ }
+ if (offset >= range.start && offset < range.end) {
+ return inRange(range) ? range.start : result;
+ } else if (offset < range.start) {
+ break;
+ }
+ result = range.end;
+ }
+ return result;
+ }
+
+ @Override
+ public int getRunStart(TAttributedCharacterIterator.Attribute attribute) {
+ if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
+ return begin;
+ }
+ TArrayList ranges = (TArrayList) attrString.attributeMap.get(attribute);
+ if (ranges == null) {
+ return begin;
+ }
+ return runStart(ranges);
+ }
+
+ @Override
+ public int getRunStart(TSet extends Attribute> attributes) {
+ int start = begin;
+ TIterator extends Attribute> it = attributes.iterator();
+ while (it.hasNext()) {
+ TAttributedCharacterIterator.Attribute attribute = it.next();
+ int newStart = getRunStart(attribute);
+ if (newStart > start) {
+ start = newStart;
+ }
+ }
+ return start;
+ }
+
+ @Override
+ public char last() {
+ if (begin == end) {
+ return DONE;
+ }
+ offset = end - 1;
+ return attrString.text.charAt(offset);
+ }
+
+ @Override
+ public char next() {
+ if (offset >= (end - 1)) {
+ offset = end;
+ return DONE;
+ }
+ return attrString.text.charAt(++offset);
+ }
+
+ @Override
+ public char previous() {
+ if (offset == begin) {
+ return DONE;
+ }
+ return attrString.text.charAt(--offset);
+ }
+
+ @Override
+ public char setIndex(int location) {
+ if (location < begin || location > end) {
+ throw new IllegalArgumentException();
+ }
+ offset = location;
+ if (offset == end) {
+ return DONE;
+ }
+ return attrString.text.charAt(offset);
+ }
+ }
+
+ public TAttributedString(TAttributedCharacterIterator iterator) {
+ if (iterator.getBeginIndex() > iterator.getEndIndex()) {
+ throw new IllegalArgumentException("Invalid substring range");
+ }
+ StringBuilder buffer = new StringBuilder();
+ for (int i = iterator.getBeginIndex(); i < iterator.getEndIndex(); i++) {
+ buffer.append(iterator.current());
+ iterator.next();
+ }
+ text = buffer.toString();
+ TSet attributes = iterator.getAllAttributeKeys();
+ if (attributes == null) {
+ return;
+ }
+ attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
+
+ TIterator it = attributes.iterator();
+ while (it.hasNext()) {
+ TAttributedCharacterIterator.Attribute attribute = it.next();
+ iterator.setIndex(0);
+ while (iterator.current() != TCharacterIterator.DONE) {
+ int start = iterator.getRunStart(attribute);
+ int limit = iterator.getRunLimit(attribute);
+ Object value = iterator.getAttribute(attribute);
+ if (value != null) {
+ addAttribute(attribute, value, start, limit);
+ }
+ iterator.setIndex(limit);
+ }
+ }
+ }
+
+ private TAttributedString(TAttributedCharacterIterator iterator, int start, int end, TSet attributes) {
+ if (start < iterator.getBeginIndex() || end > iterator.getEndIndex() || start > end) {
+ throw new IllegalArgumentException();
+ }
+
+ if (attributes == null) {
+ return;
+ }
+
+ StringBuilder buffer = new StringBuilder();
+ iterator.setIndex(start);
+ while (iterator.getIndex() < end) {
+ buffer.append(iterator.current());
+ iterator.next();
+ }
+ text = buffer.toString();
+ attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
+
+ TIterator it = attributes.iterator();
+ while (it.hasNext()) {
+ TAttributedCharacterIterator.Attribute attribute = it.next();
+ iterator.setIndex(start);
+ while (iterator.getIndex() < end) {
+ Object value = iterator.getAttribute(attribute);
+ int runStart = iterator.getRunStart(attribute);
+ int limit = iterator.getRunLimit(attribute);
+ if ((value instanceof TAnnotation && runStart >= start && limit <= end) ||
+ (value != null && !(value instanceof TAnnotation))) {
+ addAttribute(attribute, value, (runStart < start ? start : runStart) - start, (limit > end ? end
+ : limit) - start);
+ }
+ iterator.setIndex(limit);
+ }
+ }
+ }
+
+ public TAttributedString(TAttributedCharacterIterator iterator, int start, int end) {
+ this(iterator, start, end, iterator.getAllAttributeKeys());
+ }
+
+ public TAttributedString(TAttributedCharacterIterator iterator, int start, int end,
+ TAttributedCharacterIterator.Attribute[] attributes) {
+ this(iterator, start, end, new THashSet<>(TArrays.asList(attributes)));
+ }
+
+ public TAttributedString(String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ text = value;
+ attributeMap = new THashMap<>(11);
+ }
+
+ public TAttributedString(String value, TMap extends TAttributedCharacterIterator.Attribute, ?> attributes) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ if (value.length() == 0 && !attributes.isEmpty()) {
+ throw new IllegalArgumentException("Cannot add attributes to empty string");
+ }
+ text = value;
+ attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
+ TIterator> it = attributes.entrySet().iterator();
+ while (it.hasNext()) {
+ TMap.Entry, ?> entry = (TMap.Entry, ?>) it.next();
+ TArrayList ranges = new TArrayList<>(1);
+ ranges.add(new Range(0, text.length(), entry.getValue()));
+ attributeMap.put((TAttributedCharacterIterator.Attribute) entry.getKey(), ranges);
+ }
+ }
+
+ /**
+ * Applies a given attribute to this string.
+ *
+ * @param attribute
+ * the attribute that will be applied to this string.
+ * @param value
+ * the value of the attribute that will be applied to this
+ * string.
+ * @throws IllegalArgumentException
+ * if the length of this attributed string is 0.
+ * @throws NullPointerException
+ * if {@code attribute} is {@code null}.
+ */
+ public void addAttribute(TAttributedCharacterIterator.Attribute attribute, Object value) {
+ if (null == attribute) {
+ throw new NullPointerException();
+ }
+ if (text.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ TList ranges = attributeMap.get(attribute);
+ if (ranges == null) {
+ ranges = new TArrayList<>(1);
+ attributeMap.put(attribute, ranges);
+ } else {
+ ranges.clear();
+ }
+ ranges.add(new Range(0, text.length(), value));
+ }
+
+ /**
+ * Applies a given attribute to the given range of this string.
+ *
+ * @param attribute
+ * the attribute that will be applied to this string.
+ * @param value
+ * the value of the attribute that will be applied to this
+ * string.
+ * @param start
+ * the start of the range where the attribute will be applied.
+ * @param end
+ * the end of the range where the attribute will be applied.
+ * @throws IllegalArgumentException
+ * if {@code start < 0}, {@code end} is greater than the length
+ * of this string, or if {@code start >= end}.
+ * @throws NullPointerException
+ * if {@code attribute} is {@code null}.
+ */
+ public void addAttribute(TAttributedCharacterIterator.Attribute attribute, Object value, int start, int end) {
+ if (null == attribute) {
+ throw new NullPointerException();
+ }
+ if (start < 0 || end > text.length() || start >= end) {
+ throw new IllegalArgumentException();
+ }
+
+ if (value == null) {
+ return;
+ }
+
+ TList ranges = attributeMap.get(attribute);
+ if (ranges == null) {
+ ranges = new TArrayList<>(1);
+ ranges.add(new Range(start, end, value));
+ attributeMap.put(attribute, ranges);
+ return;
+ }
+ TListIterator it = ranges.listIterator();
+ while (it.hasNext()) {
+ Range range = it.next();
+ if (end <= range.start) {
+ it.previous();
+ break;
+ } else if (start < range.end || (start == range.end && value.equals(range.value))) {
+ Range r1 = null, r3;
+ it.remove();
+ r1 = new Range(range.start, start, range.value);
+ r3 = new Range(end, range.end, range.value);
+
+ while (end > range.end && it.hasNext()) {
+ range = it.next();
+ if (end <= range.end) {
+ if (end > range.start || (end == range.start && value.equals(range.value))) {
+ it.remove();
+ r3 = new Range(end, range.end, range.value);
+ break;
+ }
+ } else {
+ it.remove();
+ }
+ }
+
+ if (value.equals(r1.value)) {
+ if (value.equals(r3.value)) {
+ it.add(new Range(r1.start < start ? r1.start : start, r3.end > end ? r3.end : end, r1.value));
+ } else {
+ it.add(new Range(r1.start < start ? r1.start : start, end, r1.value));
+ if (r3.start < r3.end) {
+ it.add(r3);
+ }
+ }
+ } else {
+ if (value.equals(r3.value)) {
+ if (r1.start < r1.end) {
+ it.add(r1);
+ }
+ it.add(new Range(start, r3.end > end ? r3.end : end, r3.value));
+ } else {
+ if (r1.start < r1.end) {
+ it.add(r1);
+ }
+ it.add(new Range(start, end, value));
+ if (r3.start < r3.end) {
+ it.add(r3);
+ }
+ }
+ }
+ return;
+ }
+ }
+ it.add(new Range(start, end, value));
+ }
+
+ /**
+ * Applies a given set of attributes to the given range of the string.
+ *
+ * @param attributes
+ * the set of attributes that will be applied to this string.
+ * @param start
+ * the start of the range where the attribute will be applied.
+ * @param end
+ * the end of the range where the attribute will be applied.
+ * @throws IllegalArgumentException
+ * if {@code start < 0}, {@code end} is greater than the length
+ * of this string, or if {@code start >= end}.
+ */
+ public void addAttributes(TMap extends TAttributedCharacterIterator.Attribute, ?> attributes, int start, int end) {
+ TIterator> it = attributes.entrySet().iterator();
+ while (it.hasNext()) {
+ TMap.Entry, ?> entry = (TMap.Entry, ?>) it.next();
+ addAttribute((TAttributedCharacterIterator.Attribute) entry.getKey(), entry.getValue(), start, end);
+ }
+ }
+
+ /**
+ * Returns an {@code AttributedCharacterIterator} that gives access to the
+ * complete content of this attributed string.
+ *
+ * @return the newly created {@code AttributedCharacterIterator}.
+ */
+ public TAttributedCharacterIterator getIterator() {
+ return new AttributedIterator(this);
+ }
+
+ /**
+ * Returns an {@code AttributedCharacterIterator} that gives access to the
+ * complete content of this attributed string. Only attributes contained in
+ * {@code attributes} are available from this iterator if they are defined
+ * for this text.
+ *
+ * @param attributes
+ * the array containing attributes that will be in the new
+ * iterator if they are defined for this text.
+ * @return the newly created {@code AttributedCharacterIterator}.
+ */
+ public TAttributedCharacterIterator getIterator(TAttributedCharacterIterator.Attribute[] attributes) {
+ return new AttributedIterator(this, attributes, 0, text.length());
+ }
+
+ /**
+ * Returns an {@code AttributedCharacterIterator} that gives access to the
+ * contents of this attributed string starting at index {@code start} up to
+ * index {@code end}. Only attributes contained in {@code attributes} are
+ * available from this iterator if they are defined for this text.
+ *
+ * @param attributes
+ * the array containing attributes that will be in the new
+ * iterator if they are defined for this text.
+ * @param start
+ * the start index of the iterator on the underlying text.
+ * @param end
+ * the end index of the iterator on the underlying text.
+ * @return the newly created {@code AttributedCharacterIterator}.
+ */
+ public TAttributedCharacterIterator getIterator(TAttributedCharacterIterator.Attribute[] attributes, int start,
+ int end) {
+ return new AttributedIterator(this, attributes, start, end);
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TCharacterIterator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TCharacterIterator.java
new file mode 100644
index 000000000..82491583e
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TCharacterIterator.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+public interface TCharacterIterator extends Cloneable {
+ public static final char DONE = '\uffff';
+
+ public Object clone();
+
+ public char current();
+
+ public char first();
+
+ public int getBeginIndex();
+
+ public int getEndIndex();
+
+ public int getIndex();
+
+ public char last();
+
+ public char next();
+
+ public char previous();
+
+ public char setIndex(int location);
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java
new file mode 100644
index 000000000..958fc7693
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.impl.unicode.CLDRHelper;
+import org.teavm.classlib.impl.unicode.DateFormatCollection;
+import org.teavm.classlib.java.util.*;
+
+public abstract class TDateFormat extends TFormat {
+ protected TCalendar calendar;
+ public final static int DEFAULT = 2;
+ public final static int FULL = 0;
+ public final static int LONG = 1;
+ public final static int MEDIUM = 2;
+ public final static int SHORT = 3;
+ public final static int ERA_FIELD = 0;
+ public final static int YEAR_FIELD = 1;
+ public final static int MONTH_FIELD = 2;
+ public final static int DATE_FIELD = 3;
+ public final static int HOUR_OF_DAY1_FIELD = 4;
+ public final static int HOUR_OF_DAY0_FIELD = 5;
+ public final static int MINUTE_FIELD = 6;
+ public final static int SECOND_FIELD = 7;
+ public final static int MILLISECOND_FIELD = 8;
+ public final static int DAY_OF_WEEK_FIELD = 9;
+ public final static int DAY_OF_YEAR_FIELD = 10;
+ public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
+ public final static int WEEK_OF_YEAR_FIELD = 12;
+ public final static int WEEK_OF_MONTH_FIELD = 13;
+ public final static int AM_PM_FIELD = 14;
+ public final static int HOUR1_FIELD = 15;
+ public final static int HOUR0_FIELD = 16;
+ public final static int TIMEZONE_FIELD = 17;
+
+ protected TDateFormat() {
+ }
+
+ @Override
+ public Object clone() {
+ TDateFormat clone = (TDateFormat) super.clone();
+ clone.calendar = (TCalendar) calendar.clone();
+ return clone;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (!(object instanceof TDateFormat)) {
+ return false;
+ }
+ TDateFormat dateFormat = (TDateFormat) object;
+ return calendar.getFirstDayOfWeek() == dateFormat.calendar.getFirstDayOfWeek() &&
+ calendar.getMinimalDaysInFirstWeek() == dateFormat.calendar.getMinimalDaysInFirstWeek() &&
+ calendar.isLenient() == dateFormat.calendar.isLenient();
+ }
+
+ @Override
+ public final StringBuffer format(Object object, StringBuffer buffer, TFieldPosition field) {
+ if (object instanceof TDate) {
+ return format((TDate) object, buffer, field);
+ }
+ if (object instanceof Number) {
+ return format(new TDate(((Number) object).longValue()), buffer, field);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ public final String format(TDate date) {
+ return format(date, new StringBuffer(), new TFieldPosition(0)).toString();
+ }
+
+ public abstract StringBuffer format(TDate date, StringBuffer buffer, TFieldPosition field);
+
+ public static TLocale[] getAvailableLocales() {
+ return TLocale.getAvailableLocales();
+ }
+
+ public TCalendar getCalendar() {
+ return calendar;
+ }
+
+ public static TDateFormat getDateInstance() {
+ return getDateInstance(DEFAULT);
+ }
+
+ public static TDateFormat getDateInstance(int style) {
+ return getDateInstance(style, TLocale.getDefault());
+ }
+
+ public static TDateFormat getDateInstance(int style, TLocale locale) {
+ return new TSimpleDateFormat(getDateFormatString(style, locale), locale);
+ }
+
+ private static String getDateFormatString(int style, TLocale locale) {
+ DateFormatCollection formats = CLDRHelper.resolveDateFormats(locale.getLanguage(), locale.getCountry());
+ switch (style) {
+ case SHORT:
+ return formats.getShortFormat();
+ case MEDIUM:
+ return formats.getMediumFormat();
+ case LONG:
+ return formats.getLongFormat();
+ case FULL:
+ return formats.getFullFormat();
+ default:
+ throw new IllegalArgumentException("Unknown style: " + style);
+ }
+ }
+
+ public static TDateFormat getDateTimeInstance() {
+ return getDateTimeInstance(DEFAULT, DEFAULT);
+ }
+
+ public static TDateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
+ return getDateTimeInstance(dateStyle, timeStyle, TLocale.getDefault());
+ }
+
+ public static TDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TLocale locale) {
+ String pattern = getDateTimeFormatString(Math.max(dateStyle, timeStyle), locale);
+ pattern = pattern.replace("{0}", getTimeFormatString(dateStyle, locale))
+ .replace("{1}", getDateFormatString(timeStyle, locale));
+ return new TSimpleDateFormat(pattern, locale);
+ }
+
+ public static String getDateTimeFormatString(int style, TLocale locale) {
+ DateFormatCollection formats = CLDRHelper.resolveDateTimeFormats(locale.getLanguage(), locale.getCountry());
+ switch (style) {
+ case SHORT:
+ return formats.getShortFormat();
+ case MEDIUM:
+ return formats.getMediumFormat();
+ case LONG:
+ return formats.getLongFormat();
+ case FULL:
+ return formats.getFullFormat();
+ default:
+ throw new IllegalArgumentException("Unknown style: " + style);
+ }
+ }
+
+ public final static TDateFormat getInstance() {
+ return getDateTimeInstance(SHORT, SHORT);
+ }
+
+ static String getStyleName(int style) {
+ String styleName;
+ switch (style) {
+ case SHORT:
+ styleName = "SHORT";
+ break;
+ case MEDIUM:
+ styleName = "MEDIUM";
+ break;
+ case LONG:
+ styleName = "LONG";
+ break;
+ case FULL:
+ styleName = "FULL";
+ break;
+ default:
+ styleName = "";
+ }
+ return styleName;
+ }
+
+ public final static TDateFormat getTimeInstance() {
+ return getTimeInstance(DEFAULT);
+ }
+
+ public static TDateFormat getTimeInstance(int style) {
+ return getTimeInstance(style, TLocale.getDefault());
+ }
+
+ public static TDateFormat getTimeInstance(int style, TLocale locale) {
+ return new TSimpleDateFormat(getTimeFormatString(style, locale), locale);
+ }
+
+ private static String getTimeFormatString(int style, TLocale locale) {
+ DateFormatCollection formats = CLDRHelper.resolveTimeFormats(locale.getLanguage(), locale.getCountry());
+ switch (style) {
+ case SHORT:
+ return formats.getShortFormat();
+ case MEDIUM:
+ return formats.getMediumFormat();
+ case LONG:
+ return formats.getLongFormat();
+ case FULL:
+ return formats.getFullFormat();
+ default:
+ throw new IllegalArgumentException("Unknown style: " + style);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return calendar.getFirstDayOfWeek() + calendar.getMinimalDaysInFirstWeek() +
+ (calendar.isLenient() ? 1231 : 1237);
+ }
+
+ public boolean isLenient() {
+ return calendar.isLenient();
+ }
+
+ public TDate parse(String string) throws TParseException {
+ TParsePosition position = new TParsePosition(0);
+ TDate date = parse(string, position);
+ if (position.getIndex() == 0) {
+ throw new TParseException("Unparseable date" + string, position.getErrorIndex());
+ }
+ return date;
+ }
+
+ public abstract TDate parse(String string, TParsePosition position);
+
+ @Override
+ public Object parseObject(String string, TParsePosition position) {
+ return parse(string, position);
+ }
+
+ public void setCalendar(TCalendar cal) {
+ calendar = cal;
+ }
+
+ public void setLenient(boolean value) {
+ calendar.setLenient(value);
+ }
+
+ public static class Field extends TFormat.Field {
+ private static THashMap table = new THashMap<>();
+ public final static Field ERA = new Field("era", TCalendar.ERA);
+ public final static Field YEAR = new Field("year", TCalendar.YEAR);
+ public final static Field MONTH = new Field("month", TCalendar.MONTH);
+ public final static Field HOUR_OF_DAY0 = new Field("hour of day", TCalendar.HOUR_OF_DAY);
+ public final static Field HOUR_OF_DAY1 = new Field("hour of day 1", -1);
+ public final static Field MINUTE = new Field("minute", TCalendar.MINUTE);
+ public final static Field SECOND = new Field("second", TCalendar.SECOND);
+ public final static Field MILLISECOND = new Field("millisecond", TCalendar.MILLISECOND);
+ public final static Field DAY_OF_WEEK = new Field("day of week", TCalendar.DAY_OF_WEEK);
+ public final static Field DAY_OF_MONTH = new Field("day of month", TCalendar.DAY_OF_MONTH);
+ public final static Field DAY_OF_YEAR = new Field("day of year", TCalendar.DAY_OF_YEAR);
+ public final static Field DAY_OF_WEEK_IN_MONTH = new Field("day of week in month",
+ TCalendar.DAY_OF_WEEK_IN_MONTH);
+ public final static Field WEEK_OF_YEAR = new Field("week of year", TCalendar.WEEK_OF_YEAR);
+ public final static Field WEEK_OF_MONTH = new Field("week of month", TCalendar.WEEK_OF_MONTH);
+ public final static Field AM_PM = new Field("am pm", TCalendar.AM_PM);
+ public final static Field HOUR0 = new Field("hour", TCalendar.HOUR);
+ public final static Field HOUR1 = new Field("hour 1", -1);
+ public final static Field TIME_ZONE = new Field("time zone", -1);
+ private int calendarField = -1;
+
+ protected Field(String fieldName, int calendarField) {
+ super(fieldName);
+ this.calendarField = calendarField;
+ if (calendarField != -1 && table.get(new Integer(calendarField)) == null) {
+ table.put(new Integer(calendarField), this);
+ }
+ }
+
+ public int getCalendarField() {
+ return calendarField;
+ }
+
+ public static Field ofCalendarField(int calendarField) {
+ if (calendarField < 0 || calendarField >= TCalendar.FIELD_COUNT) {
+ throw new IllegalArgumentException();
+ }
+
+ return table.get(new Integer(calendarField));
+ }
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java
new file mode 100644
index 000000000..b7cc8fdab
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.java.util.TCalendar;
+import org.teavm.classlib.java.util.TGregorianCalendar;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+abstract class TDateFormatElement {
+ public abstract void format(TCalendar date, StringBuffer buffer);
+
+ public abstract void parse(String text, TCalendar date, TParsePosition position);
+
+ static boolean matches(String text, int position, String pattern) {
+ if (pattern.length() + position > text.length()) {
+ return false;
+ }
+ for (int i = 0; i < pattern.length(); ++i) {
+ if (Character.toLowerCase(pattern.charAt(i)) != Character.toLowerCase(text.charAt(position++))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static int whichMatches(String text, TParsePosition position, String[] patterns) {
+ for (int i = 0; i < patterns.length; ++i) {
+ if (matches(text, position.getIndex(), patterns[i])) {
+ position.setIndex(position.getIndex() + patterns[i].length());
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public static class MonthText extends TDateFormatElement {
+ String[] months;
+ String[] shortMonths;
+ boolean abbreviated;
+
+ public MonthText(TDateFormatSymbols symbols, boolean abbreviated) {
+ months = symbols.getMonths();
+ shortMonths = symbols.getShortMonths();
+ this.abbreviated = abbreviated;
+ }
+
+ @Override
+ public void format(TCalendar date, StringBuffer buffer) {
+ int month = date.get(TCalendar.MONTH);
+ buffer.append(abbreviated ? shortMonths[month] : months[month]);
+ }
+
+ @Override
+ public void parse(String text, TCalendar date, TParsePosition position) {
+ int month = whichMatches(text, position, months);
+ if (month < 0) {
+ month = whichMatches(text, position, shortMonths);
+ }
+ if (month < 0) {
+ position.setErrorIndex(position.getIndex());
+ } else {
+ date.set(TCalendar.MONTH, month);
+ }
+ }
+ }
+
+ public static class WeekdayText extends TDateFormatElement {
+ String[] weeks;
+ String[] shortWeeks;
+ boolean abbreviated;
+
+ public WeekdayText(TDateFormatSymbols symbols, boolean abbreviated) {
+ weeks = symbols.getWeekdays();
+ shortWeeks = symbols.getShortWeekdays();
+ this.abbreviated = abbreviated;
+ }
+
+ @Override
+ public void format(TCalendar date, StringBuffer buffer) {
+ int weekday = date.get(TCalendar.DAY_OF_WEEK) - 1;
+ buffer.append(abbreviated ? shortWeeks[weekday] : weeks[weekday]);
+ }
+
+ @Override
+ public void parse(String text, TCalendar date, TParsePosition position) {
+ int weekday = whichMatches(text, position, weeks);
+ if (weekday < 0) {
+ weekday = whichMatches(text, position, shortWeeks);
+ }
+ if (weekday < 0) {
+ position.setErrorIndex(position.getIndex());
+ } else {
+ date.set(TCalendar.WEEK_OF_MONTH, weekday + 1);
+ }
+ }
+ }
+
+ public static class EraText extends TDateFormatElement {
+ String[] eras;
+
+ public EraText(TDateFormatSymbols symbols) {
+ eras = symbols.getEras();
+ }
+
+ @Override
+ public void format(TCalendar date, StringBuffer buffer) {
+ int era = date.get(TCalendar.ERA);
+ buffer.append(eras[era]);
+ }
+
+ @Override
+ public void parse(String text, TCalendar date, TParsePosition position) {
+ int era = whichMatches(text, position, eras);
+ if (era < 0) {
+ position.setErrorIndex(position.getIndex());
+ } else {
+ date.set(TCalendar.ERA, era);
+ }
+ }
+ }
+
+ public static class AmPmText extends TDateFormatElement {
+ String[] ampms;
+
+ public AmPmText(TDateFormatSymbols symbols) {
+ ampms = symbols.getAmPmStrings();
+ }
+
+ @Override
+ public void format(TCalendar date, StringBuffer buffer) {
+ int ampm = date.get(TCalendar.AM_PM);
+ buffer.append(ampms[ampm]);
+ }
+
+ @Override
+ public void parse(String text, TCalendar date, TParsePosition position) {
+ int ampm = whichMatches(text, position, ampms);
+ if (ampm < 0) {
+ position.setErrorIndex(position.getIndex());
+ } else {
+ date.set(TCalendar.AM_PM, ampm);
+ }
+ }
+ }
+
+ public static class Numeric extends TDateFormatElement {
+ private int field;
+ private int length;
+
+ public Numeric(int field, int length) {
+ this.field = field;
+ this.length = length;
+ }
+
+ @Override
+ public void format(TCalendar date, StringBuffer buffer) {
+ int number = processBeforeFormat(date.get(field));
+ String str = Integer.toString(number);
+ for (int i = str.length(); i < length; ++i) {
+ buffer.append('0');
+ }
+ buffer.append(str);
+ }
+
+ @Override
+ public void parse(String text, TCalendar date, TParsePosition position) {
+ int num = 0;
+ int i = 0;
+ int pos = position.getIndex();
+ while (pos < text.length()) {
+ char c = text.charAt(pos);
+ if (c >= '0' && c <= '9') {
+ num = num * 10 + (c - '0');
+ ++pos;
+ ++i;
+ } else {
+ break;
+ }
+ }
+ if (i < length) {
+ position.setErrorIndex(position.getIndex());
+ return;
+ }
+ position.setIndex(pos);
+ date.set(field, processAfterParse(num));
+ }
+
+ protected int processBeforeFormat(int num) {
+ return num;
+ }
+
+ protected int processAfterParse(int num) {
+ return num;
+ }
+ }
+
+ public static class NumericMonth extends Numeric {
+ public NumericMonth(int length) {
+ super(TCalendar.MONTH, length);
+ }
+
+ @Override
+ protected int processBeforeFormat(int num) {
+ return num + 1;
+ }
+
+ @Override
+ protected int processAfterParse(int num) {
+ return num - 1;
+ }
+ }
+
+ public static class NumericWeekday extends Numeric {
+ public NumericWeekday(int length) {
+ super(TCalendar.DAY_OF_WEEK, length);
+ }
+
+ @Override
+ protected int processBeforeFormat(int num) {
+ return num == 1 ? 7 : num - 1;
+ }
+
+ @Override
+ protected int processAfterParse(int num) {
+ return num == 7 ? 1 : num + 1;
+ }
+ }
+
+ public static class NumericHour extends Numeric {
+ private int limit;
+
+ public NumericHour(int field, int length, int limit) {
+ super(field, length);
+ this.limit = limit;
+ }
+
+ @Override
+ protected int processBeforeFormat(int num) {
+ return num == 0 ? limit : num;
+ }
+
+ @Override
+ protected int processAfterParse(int num) {
+ return num == limit ? 0 : num;
+ }
+ }
+
+ public static class Year extends TDateFormatElement {
+ private int field;
+
+ public Year(int field) {
+ this.field = field;
+ }
+
+ @Override
+ public void format(TCalendar date, StringBuffer buffer) {
+ int number = date.get(field);
+ if (number < 10) {
+ buffer.append(number);
+ } else {
+ buffer.append((char)((number % 100 / 10) + '0'));
+ buffer.append((char)((number % 10) + '0'));
+ }
+ }
+
+ @Override
+ public void parse(String text, TCalendar date, TParsePosition position) {
+ int num = 0;
+ int pos = position.getIndex();
+ char c = text.charAt(pos++);
+ if (c < '0' || c > '9') {
+ position.setErrorIndex(position.getErrorIndex());
+ return;
+ }
+ num = c - '0';
+ c = text.charAt(pos);
+ if (c >= '0' && c <= '9') {
+ num = num * 10 + (c - '0');
+ ++pos;
+ }
+ position.setIndex(pos);
+ TCalendar calendar = new TGregorianCalendar();
+ int currentYear = calendar.get(TCalendar.YEAR);
+ int currentShortYear = currentYear % 100;
+ int century = currentYear / 100;
+ if (currentShortYear > 80) {
+ if (num < currentShortYear - 80) {
+ century++;
+ }
+ } else {
+ if (num > currentShortYear + 20) {
+ --century;
+ }
+ }
+ date.set(field, num + century * 100);
+ }
+ }
+
+ public static class ConstantText extends TDateFormatElement {
+ private String textConstant;
+
+ public ConstantText(String textConstant) {
+ this.textConstant = textConstant;
+ }
+
+ @Override
+ public void format(TCalendar date, StringBuffer buffer) {
+ buffer.append(textConstant);
+ }
+
+ @Override
+ public void parse(String text, TCalendar date, TParsePosition position) {
+ if (matches(text, position.getIndex(), textConstant)) {
+ position.setIndex(position.getIndex() + textConstant.length());
+ } else {
+ position.setErrorIndex(position.getIndex());
+ }
+ }
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatSymbols.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatSymbols.java
new file mode 100644
index 000000000..3cba349cc
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatSymbols.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.text;
+
+import java.util.Arrays;
+import org.teavm.classlib.impl.unicode.CLDRHelper;
+import org.teavm.classlib.java.io.TSerializable;
+import org.teavm.classlib.java.lang.TCloneable;
+import org.teavm.classlib.java.util.TLocale;
+
+public class TDateFormatSymbols implements TSerializable, TCloneable {
+ private TLocale locale;
+ private String localPatternChars;
+ String[] ampms, eras, months, shortMonths, shortWeekdays, weekdays;
+ String[][] zoneStrings;
+
+
+ public TDateFormatSymbols() {
+ this(TLocale.getDefault());
+ }
+
+ public TDateFormatSymbols(TLocale locale) {
+ this.locale = locale;
+ }
+
+ @Override
+ public Object clone() {
+ TDateFormatSymbols symbols = new TDateFormatSymbols(locale);
+ if (ampms != null) {
+ symbols.ampms = Arrays.copyOf(ampms, ampms.length);
+ }
+ if (eras != null) {
+ symbols.eras = Arrays.copyOf(eras, eras.length);
+ }
+ if (months != null) {
+ symbols.months = Arrays.copyOf(months, months.length);
+ }
+ if (shortMonths != null) {
+ symbols.shortMonths = Arrays.copyOf(shortMonths, shortMonths.length);
+ }
+ if (shortWeekdays != null) {
+ symbols.shortWeekdays = Arrays.copyOf(shortWeekdays.clone(), shortWeekdays.length);
+ }
+ if (weekdays != null) {
+ symbols.weekdays = Arrays.copyOf(weekdays, weekdays.length);
+ }
+ if (zoneStrings != null) {
+ symbols.zoneStrings = new String[zoneStrings.length][];
+ for (int i = 0; i < zoneStrings.length; i++) {
+ symbols.zoneStrings[i] = Arrays.copyOf(zoneStrings[i], zoneStrings[i].length);
+ }
+ }
+ return symbols;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (!(object instanceof TDateFormatSymbols)) {
+ return false;
+ }
+
+ TDateFormatSymbols obj = (TDateFormatSymbols) object;
+ if (!locale.equals(obj.locale)) {
+ return false;
+ }
+ if (!localPatternChars.equals(obj.localPatternChars)) {
+ return false;
+ }
+ if (!Arrays.equals(ampms, obj.ampms)) {
+ return false;
+ }
+ if (!Arrays.equals(eras, obj.eras)) {
+ return false;
+ }
+ if (!Arrays.equals(months, obj.months)) {
+ return false;
+ }
+ if (!Arrays.equals(shortMonths, obj.shortMonths)) {
+ return false;
+ }
+ if (!Arrays.equals(shortWeekdays, obj.shortWeekdays)) {
+ return false;
+ }
+ if (!Arrays.equals(weekdays, obj.weekdays)) {
+ return false;
+ }
+ if (zoneStrings.length != obj.zoneStrings.length) {
+ return false;
+ }
+ for (String[] element : zoneStrings) {
+ if (element.length != element.length) {
+ return false;
+ }
+ for (int j = 0; j < element.length; j++) {
+ if (element[j] != element[j] && !(element[j].equals(element[j]))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public String[] getAmPmStrings() {
+ if (ampms == null) {
+ ampms = CLDRHelper.resolveAmPm(locale.getLanguage(), locale.getCountry());
+ }
+ return ampms.clone();
+ }
+
+ public String[] getEras() {
+ if (eras == null) {
+ eras = CLDRHelper.resolveEras(locale.getLanguage(), locale.getCountry());
+ }
+ return eras.clone();
+ }
+
+ public String getLocalPatternChars() {
+ if (localPatternChars == null) {
+ localPatternChars = "";
+ }
+ return localPatternChars;
+ }
+
+ public String[] getMonths() {
+ if (months == null) {
+ months = CLDRHelper.resolveMonths(locale.getLanguage(), locale.getCountry());
+ }
+ return months.clone();
+ }
+
+ public String[] getShortMonths() {
+ if (shortMonths == null) {
+ shortMonths = CLDRHelper.resolveShortMonths(locale.getLanguage(), locale.getCountry());
+ }
+ return shortMonths.clone();
+ }
+
+ public String[] getShortWeekdays() {
+ if (shortWeekdays == null) {
+ shortWeekdays = CLDRHelper.resolveShortWeekdays(locale.getLanguage(), locale.getCountry());
+ }
+ return shortWeekdays.clone();
+ }
+
+ public String[] getWeekdays() {
+ if (weekdays == null) {
+ weekdays = CLDRHelper.resolveWeekdays(locale.getLanguage(), locale.getCountry());
+ }
+ return weekdays.clone();
+ }
+
+ public String[][] getZoneStrings() {
+ if (zoneStrings == null) {
+ return new String[0][];
+ }
+ String[][] clone = new String[zoneStrings.length][];
+ for (int i = zoneStrings.length; --i >= 0;) {
+ clone[i] = zoneStrings[i].clone();
+ }
+ return clone;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode;
+ hashCode = localPatternChars.hashCode();
+ for (String element : ampms) {
+ hashCode += element.hashCode();
+ }
+ for (String element : eras) {
+ hashCode += element.hashCode();
+ }
+ for (String element : months) {
+ hashCode += element.hashCode();
+ }
+ for (String element : shortMonths) {
+ hashCode += element.hashCode();
+ }
+ for (String element : shortWeekdays) {
+ hashCode += element.hashCode();
+ }
+ for (String element : weekdays) {
+ hashCode += element.hashCode();
+ }
+ for (String[] element : zoneStrings) {
+ for (int j = 0; j < element.length; j++) {
+ if (element[j] != null) {
+ hashCode += element[j].hashCode();
+ }
+ }
+ }
+ return hashCode;
+ }
+
+ public void setAmPmStrings(String[] data) {
+ ampms = data.clone();
+ }
+
+ public void setEras(String[] data) {
+ eras = data.clone();
+ }
+
+ public void setLocalPatternChars(String data) {
+ if (data == null) {
+ throw new NullPointerException();
+ }
+ localPatternChars = data;
+ }
+
+ public void setMonths(String[] data) {
+ months = data.clone();
+ }
+
+ public void setShortMonths(String[] data) {
+ shortMonths = data.clone();
+ }
+
+ public void setShortWeekdays(String[] data) {
+ shortWeekdays = data.clone();
+ }
+
+ public void setWeekdays(String[] data) {
+ weekdays = data.clone();
+ }
+
+ public void setZoneStrings(String[][] data) {
+ zoneStrings = data.clone();
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDecimalFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDecimalFormat.java
new file mode 100644
index 000000000..50d16ad12
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDecimalFormat.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.text;
+
+import java.text.DecimalFormatSymbols;
+import org.teavm.classlib.impl.unicode.CLDRDecimalData;
+import org.teavm.classlib.impl.unicode.CLDRHelper;
+import org.teavm.classlib.java.util.TLocale;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class TDecimalFormat extends TNumberFormat {
+ private TDecimalFormatSymbols symbols;
+
+ public TDecimalFormat() {
+ this(CLDRHelper.resolveDecimalFormat(TLocale.getDefault().getLanguage(), TLocale.getDefault().getCountry()));
+ }
+
+ public TDecimalFormat(String pattern) {
+ this(pattern, new TDecimalFormatSymbols());
+ }
+
+ public TDecimalFormat(String pattern, TDecimalFormatSymbols value) {
+ symbols = (TDecimalFormatSymbols)value.clone();
+ TLocale locale = symbols.getLocale();
+ applyPattern(pattern);
+
+ CLDRDecimalData decimalData = CLDRHelper.resolveDecimalData(locale.getLanguage(), locale.getCountry());
+ super.setMaximumFractionDigits(decimalData.getMaximumFractionDigits());
+ super.setMaximumIntegerDigits(decimalData.getMaximumIntegerDigits());
+ super.setMinimumFractionDigits(decimalData.getMinimumFractionDigits());
+ super.setMinimumIntegerDigits(decimalData.getMinimumIntegerDigits());
+ }
+
+ public void applyPattern(@SuppressWarnings("unused") String pattern) {
+
+ }
+
+ public DecimalFormatSymbols getDecimalFormatSymbols() {
+ return (DecimalFormatSymbols)symbols.clone();
+ }
+
+ @Override
+ public StringBuffer format(long value, StringBuffer buffer, TFieldPosition field) {
+ return null;
+ }
+
+ @Override
+ public Number parse(String string, TParsePosition position) {
+ return null;
+ }
+
+ @Override
+ public StringBuffer format(double value, StringBuffer buffer, TFieldPosition field) {
+ return null;
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDecimalFormatSymbols.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDecimalFormatSymbols.java
new file mode 100644
index 000000000..56191625a
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDecimalFormatSymbols.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.impl.unicode.CLDRDecimalData;
+import org.teavm.classlib.impl.unicode.CLDRHelper;
+import org.teavm.classlib.java.util.TLocale;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class TDecimalFormatSymbols {
+ private TLocale locale;
+ private char zeroDigit;
+ private char groupingSeparator;
+ private char decimalSeparator;
+ private char perMill;
+ private char percent;
+ private char digit;
+ private char patternSeparator;
+ private String NaN;
+ private String infinity;
+ private char minusSign;
+ private char monetaryDecimalSeparator;
+ private String exponentSeparator;
+
+ public TDecimalFormatSymbols() {
+ this(TLocale.getDefault());
+ }
+
+ public TDecimalFormatSymbols(TLocale locale) {
+ this.locale = locale;
+ initData();
+ }
+
+ private void initData() {
+ CLDRDecimalData data = CLDRHelper.resolveDecimalData(locale.getLanguage(), locale.getCountry());
+ zeroDigit = '0';
+ groupingSeparator = (char)data.getGroupingSeparator();
+ decimalSeparator = (char)data.getDecimalSeparator();
+ perMill = (char)data.getPerMill();
+ percent = (char)data.getPercent();
+ digit = '#';
+ patternSeparator = ';';
+ NaN = data.getNaN();
+ infinity = data.getInfinity();
+ minusSign = (char)data.getMinusSign();
+ monetaryDecimalSeparator = (char)data.getMonetaryDecimalSeparator();
+ exponentSeparator = data.getExponentSeparator();
+ }
+
+ public static TLocale[] getAvailableLocales() {
+ return TLocale.getAvailableLocales();
+ }
+
+ public static final TDecimalFormatSymbols getInstance() {
+ return new TDecimalFormatSymbols();
+ }
+
+ public static final TDecimalFormatSymbols getInstance(TLocale locale) {
+ return new TDecimalFormatSymbols(locale);
+ }
+
+ public char getZeroDigit() {
+ return zeroDigit;
+ }
+
+ public void setZeroDigit(char zeroDigit) {
+ this.zeroDigit = zeroDigit;
+ }
+
+ public char getGroupingSeparator() {
+ return groupingSeparator;
+ }
+
+ public void setGroupingSeparator(char groupingSeparator) {
+ this.groupingSeparator = groupingSeparator;
+ }
+
+ public char getPerMill() {
+ return perMill;
+ }
+
+ public void setPerMill(char perMill) {
+ this.perMill = perMill;
+ }
+
+ public char getPercent() {
+ return percent;
+ }
+
+ public void setPercent(char percent) {
+ this.percent = percent;
+ }
+
+ public TLocale getLocale() {
+ return locale;
+ }
+
+ public char getDecimalSeparator() {
+ return decimalSeparator;
+ }
+
+ public void setDecimalSeparator(char decimalSeparator) {
+ this.decimalSeparator = decimalSeparator;
+ }
+
+ public char getDigit() {
+ return digit;
+ }
+
+ public void setDigit(char digit) {
+ this.digit = digit;
+ }
+
+ public char getPatternSeparator() {
+ return patternSeparator;
+ }
+
+ public void setPatternSeparator(char patternSeparator) {
+ this.patternSeparator = patternSeparator;
+ }
+
+ public String getNaN() {
+ return NaN;
+ }
+
+ public void setNaN(String naN) {
+ NaN = naN;
+ }
+
+ public String getInfinity() {
+ return infinity;
+ }
+
+ public void setInfinity(String infinity) {
+ this.infinity = infinity;
+ }
+
+ public char getMinusSign() {
+ return minusSign;
+ }
+
+ public void setMinusSign(char minusSign) {
+ this.minusSign = minusSign;
+ }
+
+ public char getMonetaryDecimalSeparator() {
+ return monetaryDecimalSeparator;
+ }
+
+ public void setMonetaryDecimalSeparator(char monetaryDecimalSeparator) {
+ this.monetaryDecimalSeparator = monetaryDecimalSeparator;
+ }
+
+ public String getExponentSeparator() {
+ return exponentSeparator;
+ }
+
+ public void setExponentSeparator(String exponentSeparator) {
+ this.exponentSeparator = exponentSeparator;
+ }
+
+ public void setLocale(TLocale locale) {
+ this.locale = locale;
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError("This exception should not been thrown", e);
+ }
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TFieldPosition.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TFieldPosition.java
new file mode 100644
index 000000000..72e06628e
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TFieldPosition.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+public class TFieldPosition {
+ private int myField, beginIndex, endIndex;
+ private TFormat.Field myAttribute;
+
+ public TFieldPosition(int field) {
+ myField = field;
+ }
+
+ public TFieldPosition(TFormat.Field attribute) {
+ myAttribute = attribute;
+ myField = -1;
+ }
+
+ public TFieldPosition(TFormat.Field attribute, int field) {
+ myAttribute = attribute;
+ myField = field;
+ }
+
+ void clear() {
+ beginIndex = endIndex = 0;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof TFieldPosition)) {
+ return false;
+ }
+ TFieldPosition pos = (TFieldPosition) object;
+ return myField == pos.myField && myAttribute == pos.myAttribute && beginIndex == pos.beginIndex &&
+ endIndex == pos.endIndex;
+ }
+
+ public int getBeginIndex() {
+ return beginIndex;
+ }
+
+ public int getEndIndex() {
+ return endIndex;
+ }
+
+ public int getField() {
+ return myField;
+ }
+
+ public TFormat.Field getFieldAttribute() {
+ return myAttribute;
+ }
+
+ @Override
+ public int hashCode() {
+ int attributeHash = (myAttribute == null) ? 0 : myAttribute.hashCode();
+ return attributeHash + myField * 10 + beginIndex * 100 + endIndex;
+ }
+
+ public void setBeginIndex(int index) {
+ beginIndex = index;
+ }
+
+ public void setEndIndex(int index) {
+ endIndex = index;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[attribute=" + myAttribute + ", field=" + myField + ", beginIndex=" +
+ beginIndex + ", endIndex=" + endIndex + "]";
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TFormat.java
new file mode 100644
index 000000000..6ece84c18
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TFormat.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.java.io.TSerializable;
+import org.teavm.classlib.java.lang.TCloneable;
+
+public abstract class TFormat implements TSerializable, TCloneable {
+ public TFormat() {
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+ String convertPattern(String template, String fromChars, String toChars, boolean check) {
+ if (!check && fromChars.equals(toChars)) {
+ return template;
+ }
+ boolean quote = false;
+ StringBuilder output = new StringBuilder();
+ int length = template.length();
+ for (int i = 0; i < length; i++) {
+ int index;
+ char next = template.charAt(i);
+ if (next == '\'') {
+ quote = !quote;
+ }
+ if (!quote && (index = fromChars.indexOf(next)) != -1) {
+ output.append(toChars.charAt(index));
+ } else if (check && !quote && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
+ throw new IllegalArgumentException("Invalid pattern char" + next + " in " + template);
+ } else {
+ output.append(next);
+ }
+ }
+ if (quote) {
+ throw new IllegalArgumentException("Unterminated quote");
+ }
+ return output.toString();
+ }
+
+ public final String format(Object object) {
+ return format(object, new StringBuffer(), new TFieldPosition(0)).toString();
+ }
+
+ public abstract StringBuffer format(Object object, StringBuffer buffer, TFieldPosition field);
+
+ public TAttributedCharacterIterator formatToCharacterIterator(Object object) {
+ return new TAttributedString(format(object)).getIterator();
+ }
+
+ public Object parseObject(String string) throws TParseException {
+ TParsePosition position = new TParsePosition(0);
+ Object result = parseObject(string, position);
+ if (position.getIndex() == 0) {
+ throw new TParseException("Format.parseObject(String) parse failure", position.getErrorIndex());
+ }
+ return result;
+ }
+
+ public abstract Object parseObject(String string, TParsePosition position);
+
+ static boolean upTo(String string, TParsePosition position, StringBuffer buffer, char stop) {
+ int index = position.getIndex(), length = string.length();
+ boolean lastQuote = false, quote = false;
+ while (index < length) {
+ char ch = string.charAt(index++);
+ if (ch == '\'') {
+ if (lastQuote) {
+ buffer.append('\'');
+ }
+ quote = !quote;
+ lastQuote = true;
+ } else if (ch == stop && !quote) {
+ position.setIndex(index);
+ return true;
+ } else {
+ lastQuote = false;
+ buffer.append(ch);
+ }
+ }
+ position.setIndex(index);
+ return false;
+ }
+
+ static boolean upToWithQuotes(String string, TParsePosition position, StringBuffer buffer, char stop, char start) {
+ int index = position.getIndex(), length = string.length(), count = 1;
+ boolean quote = false;
+ while (index < length) {
+ char ch = string.charAt(index++);
+ if (ch == '\'') {
+ quote = !quote;
+ }
+ if (!quote) {
+ if (ch == stop) {
+ count--;
+ }
+ if (count == 0) {
+ position.setIndex(index);
+ return true;
+ }
+ if (ch == start) {
+ count++;
+ }
+ }
+ buffer.append(ch);
+ }
+ throw new IllegalArgumentException("Unmatched braces in the pattern");
+ }
+
+ public static class Field extends TAttributedCharacterIterator.Attribute {
+ protected Field(String fieldName) {
+ super(fieldName);
+ }
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TNumberFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TNumberFormat.java
new file mode 100644
index 000000000..545185fe2
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TNumberFormat.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.impl.unicode.CLDRHelper;
+import org.teavm.classlib.java.util.TLocale;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public abstract class TNumberFormat extends TFormat {
+ public static final int INTEGER_FIELD = 0;
+ public static final int FRACTION_FIELD = 1;
+ private boolean groupingUsed = true, parseIntegerOnly = false;
+ private int maximumIntegerDigits = 40, minimumIntegerDigits = 1,
+ maximumFractionDigits = 3, minimumFractionDigits = 0;
+
+ public TNumberFormat() {
+ }
+
+ @Override
+ public Object clone() {
+ return super.clone();
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (!(object instanceof TNumberFormat)) {
+ return false;
+ }
+ TNumberFormat obj = (TNumberFormat) object;
+ return groupingUsed == obj.groupingUsed
+ && parseIntegerOnly == obj.parseIntegerOnly
+ && maximumFractionDigits == obj.maximumFractionDigits
+ && maximumIntegerDigits == obj.maximumIntegerDigits
+ && minimumFractionDigits == obj.minimumFractionDigits
+ && minimumIntegerDigits == obj.minimumIntegerDigits;
+ }
+
+ public final String format(double value) {
+ return format(value, new StringBuffer(), new TFieldPosition(0)).toString();
+ }
+
+ public abstract StringBuffer format(double value, StringBuffer buffer, TFieldPosition field);
+
+ public final String format(long value) {
+ return format(value, new StringBuffer(), new TFieldPosition(0)).toString();
+ }
+
+ public abstract StringBuffer format(long value, StringBuffer buffer, TFieldPosition field);
+
+ @Override
+ public StringBuffer format(Object object, StringBuffer buffer, TFieldPosition field) {
+ if (object instanceof Number) {
+ double dv = ((Number) object).doubleValue();
+ long lv = ((Number) object).longValue();
+ if (dv == lv) {
+ return format(lv, buffer, field);
+ }
+ return format(dv, buffer, field);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ public static TLocale[] getAvailableLocales() {
+ return TLocale.getAvailableLocales();
+ }
+
+ public final static TNumberFormat getIntegerInstance() {
+ return getIntegerInstance(TLocale.getDefault());
+ }
+
+ public static TNumberFormat getIntegerInstance(TLocale locale) {
+ String pattern = CLDRHelper.resolveNumberFormat(locale.getLanguage(), locale.getCountry());
+ TDecimalFormat format = new TDecimalFormat(pattern, new TDecimalFormatSymbols(locale));
+ format.setParseIntegerOnly(true);
+ return format;
+ }
+
+ public final static TNumberFormat getInstance() {
+ return getNumberInstance();
+ }
+
+ public static TNumberFormat getInstance(TLocale locale) {
+ return getNumberInstance(locale);
+ }
+
+ public int getMaximumFractionDigits() {
+ return maximumFractionDigits;
+ }
+
+ public int getMaximumIntegerDigits() {
+ return maximumIntegerDigits;
+ }
+
+ public int getMinimumFractionDigits() {
+ return minimumFractionDigits;
+ }
+
+ public int getMinimumIntegerDigits() {
+ return minimumIntegerDigits;
+ }
+
+ public final static TNumberFormat getNumberInstance() {
+ return getNumberInstance(TLocale.getDefault());
+ }
+
+ public static TNumberFormat getNumberInstance(TLocale locale) {
+ String pattern = CLDRHelper.resolveDecimalFormat(locale.getLanguage(), locale.getCountry());
+ return new TDecimalFormat(pattern, new TDecimalFormatSymbols(locale));
+ }
+
+ public final static TNumberFormat getPercentInstance() {
+ return getPercentInstance(TLocale.getDefault());
+ }
+
+ public static TNumberFormat getPercentInstance(TLocale locale) {
+ String pattern = CLDRHelper.resolvePercentFormat(locale.getLanguage(), locale.getCountry());
+ return new TDecimalFormat(pattern, new TDecimalFormatSymbols(locale));
+ }
+
+ @Override
+ public int hashCode() {
+ return (groupingUsed ? 1231 : 1237) + (parseIntegerOnly ? 1231 : 1237)
+ + maximumFractionDigits + maximumIntegerDigits
+ + minimumFractionDigits + minimumIntegerDigits;
+ }
+
+ public boolean isGroupingUsed() {
+ return groupingUsed;
+ }
+
+ public boolean isParseIntegerOnly() {
+ return parseIntegerOnly;
+ }
+
+ public Number parse(String string) throws TParseException {
+ TParsePosition pos = new TParsePosition(0);
+ Number number = parse(string, pos);
+ if (pos.getIndex() == 0) {
+ throw new TParseException("Unparseable number: " + string, pos.getErrorIndex());
+ }
+ return number;
+ }
+
+ public abstract Number parse(String string, TParsePosition position);
+
+ @Override
+ public final Object parseObject(String string, TParsePosition position) {
+ if (position == null) {
+ throw new NullPointerException("position is null");
+ }
+
+ try {
+ return parse(string, position);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public void setGroupingUsed(boolean value) {
+ groupingUsed = value;
+ }
+
+ public void setMaximumFractionDigits(int value) {
+ maximumFractionDigits = value < 0 ? 0 : value;
+ if (maximumFractionDigits < minimumFractionDigits) {
+ minimumFractionDigits = maximumFractionDigits;
+ }
+ }
+
+ public void setMaximumIntegerDigits(int value) {
+ maximumIntegerDigits = value < 0 ? 0 : value;
+ if (maximumIntegerDigits < minimumIntegerDigits) {
+ minimumIntegerDigits = maximumIntegerDigits;
+ }
+ }
+
+ public void setMinimumFractionDigits(int value) {
+ minimumFractionDigits = value < 0 ? 0 : value;
+ if (maximumFractionDigits < minimumFractionDigits) {
+ maximumFractionDigits = minimumFractionDigits;
+ }
+ }
+
+ public void setMinimumIntegerDigits(int value) {
+ minimumIntegerDigits = value < 0 ? 0 : value;
+ if (maximumIntegerDigits < minimumIntegerDigits) {
+ maximumIntegerDigits = minimumIntegerDigits;
+ }
+ }
+
+ public void setParseIntegerOnly(boolean value) {
+ parseIntegerOnly = value;
+ }
+
+ public static class Field extends TFormat.Field {
+ public static final Field SIGN = new Field("sign");
+ public static final Field INTEGER = new Field("integer");
+ public static final Field FRACTION = new Field("fraction");
+ public static final Field EXPONENT = new Field("exponent");
+ public static final Field EXPONENT_SIGN = new Field("exponent sign");
+ public static final Field EXPONENT_SYMBOL = new Field("exponent symbol");
+ public static final Field DECIMAL_SEPARATOR = new Field("decimal separator");
+ public static final Field GROUPING_SEPARATOR = new Field("grouping separator");
+ public static final Field PERCENT = new Field("percent");
+ public static final Field PERMILLE = new Field("per mille");
+ public static final Field CURRENCY = new Field("currency");
+
+ protected Field(String fieldName) {
+ super(fieldName);
+ }
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TParseException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TParseException.java
new file mode 100644
index 000000000..3d7478d34
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TParseException.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+/**
+ * Thrown when the string being parsed is not in the correct form.
+ */
+public class TParseException extends Exception {
+
+ private static final long serialVersionUID = 2703218443322787634L;
+
+ private int errorOffset;
+
+ /**
+ * Constructs a new instance of this class with its stack trace, detail
+ * message and the location of the error filled in.
+ *
+ * @param detailMessage
+ * the detail message for this exception.
+ * @param location
+ * the index at which the parse exception occurred.
+ */
+ public TParseException(String detailMessage, int location) {
+ super(detailMessage);
+ errorOffset = location;
+ }
+
+ /**
+ * Returns the index at which this parse exception occurred.
+ *
+ * @return the location of this exception in the parsed string.
+ */
+ public int getErrorOffset() {
+ return errorOffset;
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TParsePosition.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TParsePosition.java
new file mode 100644
index 000000000..e20c31e94
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TParsePosition.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.teavm.classlib.java.text;
+
+public class TParsePosition {
+
+ private int currentPosition, errorIndex = -1;
+
+ public TParsePosition(int index) {
+ currentPosition = index;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof TParsePosition)) {
+ return false;
+ }
+ TParsePosition pos = (TParsePosition) object;
+ return currentPosition == pos.currentPosition && errorIndex == pos.errorIndex;
+ }
+
+ public int getErrorIndex() {
+ return errorIndex;
+ }
+
+ public int getIndex() {
+ return currentPosition;
+ }
+
+ @Override
+ public int hashCode() {
+ return currentPosition + errorIndex;
+ }
+
+ public void setErrorIndex(int index) {
+ errorIndex = index;
+ }
+
+ public void setIndex(int index) {
+ currentPosition = index;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[index=" + currentPosition + ", errorIndex=" + errorIndex + "]";
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDateFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDateFormat.java
new file mode 100644
index 000000000..3775405b3
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDateFormat.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.text;
+
+import org.teavm.classlib.impl.unicode.CLDRHelper;
+import org.teavm.classlib.java.util.TCalendar;
+import org.teavm.classlib.java.util.TDate;
+import org.teavm.classlib.java.util.TGregorianCalendar;
+import org.teavm.classlib.java.util.TLocale;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class TSimpleDateFormat extends TDateFormat {
+ private TDateFormatSymbols dateFormatSymbols;
+ private TDateFormatElement[] elements;
+ private String pattern;
+ private TLocale locale;
+
+ public TSimpleDateFormat() {
+ this(getDefaultPattern());
+ }
+
+ private static String getDefaultPattern() {
+ TLocale locale = TLocale.getDefault();
+ return CLDRHelper.resolveDateFormats(locale.getLanguage(), locale.getCountry()).getMediumFormat();
+ }
+
+ public TSimpleDateFormat(String pattern) {
+ this(pattern, TLocale.getDefault());
+ }
+
+ public TSimpleDateFormat(String pattern, TLocale locale) {
+ this(pattern, new TDateFormatSymbols(locale));
+ this.locale = locale;
+ }
+
+ public TSimpleDateFormat(String pattern, TDateFormatSymbols dateFormatSymbols) {
+ this.dateFormatSymbols = (TDateFormatSymbols)dateFormatSymbols.clone();
+ locale = TLocale.getDefault();
+ applyPattern(pattern);
+ }
+
+ @Override
+ public StringBuffer format(TDate date, StringBuffer buffer, TFieldPosition field) {
+ TCalendar calendar = new TGregorianCalendar(locale);
+ calendar.setTime(date);
+ for (TDateFormatElement element : elements) {
+ element.format(calendar, buffer);
+ }
+ return buffer;
+ }
+
+ public void applyPattern(String pattern) {
+ this.pattern = pattern;
+ reparsePattern();
+ }
+
+ private void reparsePattern() {
+ TSimpleDatePatternParser parser = new TSimpleDatePatternParser(dateFormatSymbols);
+ parser.parsePattern(pattern);
+ elements = parser.getElements().toArray(new TDateFormatElement[0]);
+ }
+
+ @Override
+ public TDate parse(String string, TParsePosition position) {
+ TCalendar calendar = new TGregorianCalendar(locale);
+ calendar.clear();
+ for (TDateFormatElement element : elements) {
+ if (position.getIndex() > string.length()) {
+ position.setErrorIndex(position.getErrorIndex());
+ return null;
+ }
+ element.parse(string, calendar, position);
+ if (position.getErrorIndex() >= 0) {
+ return null;
+ }
+ }
+ return calendar.getTime();
+ }
+
+ @Override
+ public Object clone() {
+ TSimpleDateFormat copy = (TSimpleDateFormat)super.clone();
+ copy.dateFormatSymbols = (TDateFormatSymbols)dateFormatSymbols.clone();
+ copy.elements = elements.clone();
+ return copy;
+ }
+
+ public TDateFormatSymbols getDateFormatSymbols() {
+ return (TDateFormatSymbols)dateFormatSymbols.clone();
+ }
+
+ public void setDateFormatSymbols(TDateFormatSymbols newFormatSymbols) {
+ dateFormatSymbols = (TDateFormatSymbols)newFormatSymbols.clone();
+ reparsePattern();
+ }
+
+ public String toPattern() {
+ return pattern;
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDatePatternParser.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDatePatternParser.java
new file mode 100644
index 000000000..a0f27576f
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDatePatternParser.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.text;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.teavm.classlib.java.util.TCalendar;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class TSimpleDatePatternParser {
+ private TDateFormatSymbols symbols;
+ private List elements = new ArrayList<>();
+ private int index;
+ private String pattern;
+
+ public TSimpleDatePatternParser(TDateFormatSymbols symbols) {
+ this.symbols = symbols;
+ }
+
+ public List getElements() {
+ return elements;
+ }
+
+ public void parsePattern(String pattern) {
+ elements.clear();
+ this.pattern = pattern;
+ for (index = 0; index < pattern.length();) {
+ char c = pattern.charAt(index);
+ switch (c) {
+ case '\'': {
+ ++index;
+ parseQuoted();
+ break;
+ }
+ case 'G':
+ parseRepetitions();
+ elements.add(new TDateFormatElement.EraText(symbols));
+ break;
+ case 'y':
+ case 'Y': {
+ int rep = parseRepetitions();
+ if (rep == 2) {
+ elements.add(new TDateFormatElement.Year(TCalendar.YEAR));
+ } else {
+ elements.add(new TDateFormatElement.Numeric(TCalendar.YEAR, rep));
+ }
+ break;
+ }
+ case 'M':
+ case 'L': {
+ int rep = parseRepetitions();
+ if (rep <= 2) {
+ elements.add(new TDateFormatElement.NumericMonth(rep));
+ } else {
+ elements.add(new TDateFormatElement.MonthText(symbols, rep == 3));
+ }
+ break;
+ }
+ case 'w': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.WEEK_OF_YEAR, rep));
+ break;
+ }
+ case 'W': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.WEEK_OF_MONTH, rep));
+ break;
+ }
+ case 'D': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.DAY_OF_YEAR, rep));
+ break;
+ }
+ case 'd': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.DAY_OF_MONTH, rep));
+ break;
+ }
+ case 'F': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.DAY_OF_WEEK_IN_MONTH, rep));
+ break;
+ }
+ case 'E':
+ case 'c': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.WeekdayText(symbols, rep <= 3));
+ break;
+ }
+ case 'u': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.NumericWeekday(rep));
+ break;
+ }
+ case 'a': {
+ parseRepetitions();
+ elements.add(new TDateFormatElement.AmPmText(symbols));
+ break;
+ }
+ case 'H': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.HOUR_OF_DAY, rep));
+ break;
+ }
+ case 'k': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.NumericHour(TCalendar.HOUR_OF_DAY, rep, 24));
+ break;
+ }
+ case 'K': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.HOUR, rep));
+ break;
+ }
+ case 'h': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.NumericHour(TCalendar.HOUR, rep, 12));
+ break;
+ }
+ case 'm': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.MINUTE, rep));
+ break;
+ }
+ case 's': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.SECOND, rep));
+ break;
+ }
+ case 'S': {
+ int rep = parseRepetitions();
+ elements.add(new TDateFormatElement.Numeric(TCalendar.MILLISECOND, rep));
+ break;
+ }
+ default:
+ if (isControl(c)) {
+ parseRepetitions();
+ } else {
+ StringBuilder sb = new StringBuilder();
+ while (index < pattern.length() && !isControl(pattern.charAt(index))) {
+ sb.append(pattern.charAt(index++));
+ }
+ elements.add(new TDateFormatElement.ConstantText(sb.toString()));
+ }
+ break;
+ }
+ }
+ }
+
+ private boolean isControl(char c) {
+ return c == '\'' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
+ }
+
+ private void parseQuoted() {
+ StringBuilder sb = new StringBuilder();
+ while (index < pattern.length()) {
+ char c = pattern.charAt(index++);
+ if (c == '\'') {
+ if (index < pattern.length() && pattern.charAt(index) == '\'') {
+ sb.append('\'');
+ ++index;
+ } else {
+ break;
+ }
+ } else {
+ sb.append(c);
+ }
+ }
+ elements.add(new TDateFormatElement.ConstantText(sb.toString()));
+ }
+
+ private int parseRepetitions() {
+ int count = 1;
+ char orig = pattern.charAt(index++);
+ while (index < pattern.length() && pattern.charAt(index) == orig) {
+ ++index;
+ ++count;
+ }
+ return count;
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java
new file mode 100644
index 000000000..8ab1bd5e3
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.classlib.java.util;
+
+import java.io.IOException;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.dependency.DependencyAgent;
+import org.teavm.dependency.DependencyPlugin;
+import org.teavm.dependency.MethodDependency;
+import org.teavm.javascript.ni.Generator;
+import org.teavm.javascript.ni.GeneratorContext;
+import org.teavm.model.MethodReference;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class DateNativeGenerator implements Generator, DependencyPlugin {
+ @Override
+ public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
+ switch (methodRef.getName()) {
+ case "buildNumericTime":
+ generateBuildNumericTime(context, writer);
+ break;
+ case "parseNumericTime":
+ generateParseNumericTime(context, writer);
+ break;
+ case "buildNumericUTC":
+ generateBuildNumericUTC(context, writer);
+ break;
+ case "getFullYear":
+ case "getMonth":
+ case "getDate":
+ case "getDay":
+ case "getHours":
+ case "getMinutes":
+ case "getSeconds":
+ case "getTimezoneOffset":
+ generateGetMethod(context, writer, methodRef.getName());
+ break;
+ case "setFullYear":
+ case "setMonth":
+ case "setDate":
+ case "setHours":
+ case "setMinutes":
+ case "setSeconds":
+ generateSetMethod(context, writer, methodRef.getName());
+ break;
+ case "toString":
+ case "toGMTString":
+ generateToString(context, writer, methodRef.getName());
+ break;
+ case "toLocaleFormat":
+ generateToLocaleFormat(context, writer);
+ break;
+ }
+ }
+
+ @Override
+ public void methodAchieved(DependencyAgent agent, MethodDependency method) {
+ switch (method.getMethod().getName()) {
+ case "toString":
+ case "toLocaleFormat":
+ case "toGMTString":
+ method.getResult().propagate(agent.getType("java.lang.String"));
+ break;
+ }
+ }
+
+ private void generateBuildNumericTime(GeneratorContext context, SourceWriter writer) throws IOException {
+ writer.append("return new Date(").append(context.getParameterName(1));
+ for (int i = 2; i <= 6; ++i) {
+ writer.append(',').ws().append(context.getParameterName(i));
+ }
+ writer.append(").getTime();").softNewLine();
+ }
+
+ private void generateParseNumericTime(GeneratorContext context, SourceWriter writer) throws IOException {
+ writer.append("return Date.parse(").append(context.getParameterName(1)).append(");").softNewLine();
+ }
+
+ private void generateBuildNumericUTC(GeneratorContext context, SourceWriter writer) throws IOException {
+ writer.append("return Date.UTC(").append(context.getParameterName(1));
+ for (int i = 2; i <= 6; ++i) {
+ writer.append(',').ws().append(context.getParameterName(i));
+ }
+ writer.append(").getTime();").softNewLine();
+ }
+
+ private void generateGetMethod(GeneratorContext context, SourceWriter writer, String methodName)
+ throws IOException {
+ writer.append("return new Date(").append(context.getParameterName(1)).append(").").append(methodName)
+ .append("();").softNewLine();
+ }
+
+ private void generateSetMethod(GeneratorContext context, SourceWriter writer, String methodName)
+ throws IOException {
+ writer.append("var date = new Date(").append(context.getParameterName(1)).append(");").softNewLine();
+ writer.append("return date.").append(methodName).append("(").append(context.getParameterName(2)).append(");")
+ .softNewLine();
+ }
+
+ private void generateToString(GeneratorContext context, SourceWriter writer, String method) throws IOException {
+ writer.append("return $rt_str(new Date(").append(context.getParameterName(1)).append(").").append(method)
+ .append("());").softNewLine();
+ }
+
+ private void generateToLocaleFormat(GeneratorContext context, SourceWriter writer) throws IOException {
+ writer.append("return $rt_str(new Date(").append(context.getParameterName(1))
+ .append(").toLocaleFormat($rt_ustr(").append(context.getParameterName(2)).append(")));")
+ .softNewLine();
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java
index ce667c68a..d012d7c51 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java
@@ -15,10 +15,8 @@
*/
package org.teavm.classlib.java.util;
-import org.teavm.classlib.java.lang.TIllegalArgumentException;
-import org.teavm.classlib.java.lang.TIllegalStateException;
-import org.teavm.classlib.java.lang.TIndexOutOfBoundsException;
-import org.teavm.classlib.java.lang.TUnsupportedOperationException;
+import org.teavm.classlib.java.lang.*;
+import org.teavm.javascript.ni.Rename;
/**
*
@@ -56,9 +54,11 @@ public abstract class TAbstractList extends TAbstractCollection implements
throw new TIllegalStateException();
}
checkConcurrentModification();
- TAbstractList.this.remove(index - 1);
+ TAbstractList.this.remove(removeIndex);
modCount = TAbstractList.this.modCount;
- --index;
+ if (removeIndex < index) {
+ --index;
+ }
--size;
removeIndex = -1;
}
@@ -159,6 +159,36 @@ public abstract class TAbstractList extends TAbstractCollection implements
}
}
+
+ @Override
+ public int hashCode() {
+ int hashCode = 1;
+ for (TIterator extends E> iter = iterator(); iter.hasNext();) {
+ E elem = iter.next();
+ hashCode = 31 * hashCode + (elem != null ? elem.hashCode() : 0);
+ }
+ return hashCode;
+ }
+
+ @Override
+ @Rename("equals")
+ public boolean equals0(TObject other) {
+ if (!(other instanceof TList)) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ TList