From e5a4e3fa62f4db975c6359bb3813da832678314d Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Fri, 27 Jun 2014 18:04:42 +0400 Subject: [PATCH] Adds java.math support --- .../java/lang/TArithmeticException.java | 32 + .../teavm/classlib/java/math/TBigDecimal.java | 3004 +++++++++++++++++ .../teavm/classlib/java/math/TBigInteger.java | 1539 +++++++++ .../teavm/classlib/java/math/TBitLevel.java | 353 ++ .../teavm/classlib/java/math/TConversion.java | 458 +++ .../teavm/classlib/java/math/TDivision.java | 952 ++++++ .../teavm/classlib/java/math/TElementary.java | 432 +++ .../teavm/classlib/java/math/TLogical.java | 808 +++++ .../classlib/java/math/TMathContext.java | 290 ++ .../classlib/java/math/TMultiplication.java | 510 +++ .../teavm/classlib/java/math/TPrimality.java | 279 ++ .../classlib/java/math/TRoundingMode.java | 123 + .../java/math/BigDecimalArithmeticTest.java | 1712 ++++++++++ .../java/math/BigDecimalCompareTest.java | 549 +++ .../java/math/BigDecimalConstructorsTest.java | 740 ++++ .../java/math/BigDecimalConvertTest.java | 619 ++++ .../math/BigDecimalScaleOperationsTest.java | 374 ++ .../classlib/java/math/BigIntegerAddTest.java | 519 +++ .../classlib/java/math/BigIntegerAndTest.java | 454 +++ .../java/math/BigIntegerCompareTest.java | 567 ++++ .../java/math/BigIntegerConstructorsTest.java | 819 +++++ .../java/math/BigIntegerConvertTest.java | 851 +++++ .../java/math/BigIntegerDivideTest.java | 703 ++++ .../java/math/BigIntegerHashCodeTest.java | 83 + .../java/math/BigIntegerModPowTest.java | 368 ++ .../java/math/BigIntegerMultiplyTest.java | 408 +++ .../classlib/java/math/BigIntegerNotTest.java | 201 ++ .../java/math/BigIntegerOperateBitsTest.java | 1465 ++++++++ .../classlib/java/math/BigIntegerOrTest.java | 440 +++ .../java/math/BigIntegerSubtractTest.java | 573 ++++ .../java/math/BigIntegerToStringTest.java | 163 + .../classlib/java/math/BigIntegerXorTest.java | 297 ++ .../plugin/ResourceProgramTransformer.java | 2 +- 33 files changed, 20686 insertions(+), 1 deletion(-) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArithmeticException.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigDecimal.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigInteger.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBitLevel.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TConversion.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TDivision.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TElementary.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TLogical.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMathContext.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TMultiplication.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TPrimality.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/math/TRoundingMode.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArithmeticException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArithmeticException.java new file mode 100644 index 000000000..7bbe9451a --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArithmeticException.java @@ -0,0 +1,32 @@ +/* + * 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.lang; + +/** + * + * @author Alexey Andreev + */ +public class TArithmeticException extends TRuntimeException { + private static final long serialVersionUID = 8084592456171302650L; + + public TArithmeticException() { + super(); + } + + public TArithmeticException(TString message) { + super(message); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigDecimal.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigDecimal.java new file mode 100644 index 000000000..78486f548 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigDecimal.java @@ -0,0 +1,3004 @@ +/* + * 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; + + +/** + * This class represents immutable arbitrary precision decimal numbers. Each + * {@code BigDecimal} instance is represented with a unscaled arbitrary + * precision mantissa (the unscaled value) and a scale. The value of the {@code + * BigDecimal} is {@code unscaledValue} 10^(-{@code scale}). + */ +public class TBigDecimal extends Number implements Comparable, Serializable { + + /** + * The constant zero as a {@code BigDecimal}. + */ + public static final TBigDecimal ZERO = new TBigDecimal(0, 0); + + /** + * The constant one as a {@code BigDecimal}. + */ + public static final TBigDecimal ONE = new TBigDecimal(1, 0); + + /** + * The constant ten as a {@code BigDecimal}. + */ + public static final TBigDecimal TEN = new TBigDecimal(10, 0); + + /** + * Rounding mode where positive values are rounded towards positive infinity + * and negative values towards negative infinity. + * + * @see TRoundingMode#UP + */ + public static final int ROUND_UP = 0; + + /** + * Rounding mode where the values are rounded towards zero. + * + * @see TRoundingMode#DOWN + */ + public static final int ROUND_DOWN = 1; + + /** + * Rounding mode to round towards positive infinity. For positive values + * this rounding mode behaves as {@link #ROUND_UP}, for negative values as + * {@link #ROUND_DOWN}. + * + * @see TRoundingMode#CEILING + */ + public static final int ROUND_CEILING = 2; + + /** + * Rounding mode to round towards negative infinity. For positive values + * this rounding mode behaves as {@link #ROUND_DOWN}, for negative values as + * {@link #ROUND_UP}. + * + * @see TRoundingMode#FLOOR + */ + public static final int ROUND_FLOOR = 3; + + /** + * Rounding mode where values are rounded towards the nearest neighbor. + * Ties are broken by rounding up. + * + * @see TRoundingMode#HALF_UP + */ + public static final int ROUND_HALF_UP = 4; + + /** + * Rounding mode where values are rounded towards the nearest neighbor. + * Ties are broken by rounding down. + * + * @see TRoundingMode#HALF_DOWN + */ + public static final int ROUND_HALF_DOWN = 5; + + /** + * Rounding mode where values are rounded towards the nearest neighbor. + * Ties are broken by rounding to the even neighbor. + * + * @see TRoundingMode#HALF_EVEN + */ + public static final int ROUND_HALF_EVEN = 6; + + /** + * Rounding mode where the rounding operations throws an {@code + * ArithmeticException} for the case that rounding is necessary, i.e. for + * the case that the value cannot be represented exactly. + * + * @see TRoundingMode#UNNECESSARY + */ + public static final int ROUND_UNNECESSARY = 7; + + /** This is the serialVersionUID used by the sun implementation. */ + private static final long serialVersionUID = 6108874887143696463L; + + /** The double closer to Log10(2). */ + private static final double LOG10_2 = 0.3010299956639812; + + /** The String representation is cached. */ + private transient String toStringImage = null; + + /** Cache for the hash code. */ + private transient int hashCode = 0; + + /** + * An array with powers of five that fit in the type long + * (5^0,5^1,...,5^27). + */ + private static final TBigInteger FIVE_POW[]; + + /** + * An array with powers of ten that fit in the type long + * (10^0,10^1,...,10^18). + */ + private static final TBigInteger TEN_POW[]; + + /** + * An array with powers of ten that fit in the type long + * (10^0,10^1,...,10^18). + */ + private static final long[] LONG_TEN_POW = new long[] + { 1L, + 10L, + 100L, + 1000L, + 10000L, + 100000L, + 1000000L, + 10000000L, + 100000000L, + 1000000000L, + 10000000000L, + 100000000000L, + 1000000000000L, + 10000000000000L, + 100000000000000L, + 1000000000000000L, + 10000000000000000L, + 100000000000000000L, + 1000000000000000000L, }; + + + private static final long[] LONG_FIVE_POW = new long[] + { 1L, + 5L, + 25L, + 125L, + 625L, + 3125L, + 15625L, + 78125L, + 390625L, + 1953125L, + 9765625L, + 48828125L, + 244140625L, + 1220703125L, + 6103515625L, + 30517578125L, + 152587890625L, + 762939453125L, + 3814697265625L, + 19073486328125L, + 95367431640625L, + 476837158203125L, + 2384185791015625L, + 11920928955078125L, + 59604644775390625L, + 298023223876953125L, + 1490116119384765625L, + 7450580596923828125L, }; + + private static final int[] LONG_FIVE_POW_BIT_LENGTH = new int[LONG_FIVE_POW.length]; + private static final int[] LONG_TEN_POW_BIT_LENGTH = new int[LONG_TEN_POW.length]; + + private static final int BI_SCALED_BY_ZERO_LENGTH = 11; + + /** + * An array with the first BigInteger scaled by zero. + * ([0,0],[1,0],...,[10,0]). + */ + private static final TBigDecimal BI_SCALED_BY_ZERO[] = new TBigDecimal[BI_SCALED_BY_ZERO_LENGTH]; + + /** + * An array with the zero number scaled by the first positive scales. + * (0*10^0, 0*10^1, ..., 0*10^10). + */ + private static final TBigDecimal ZERO_SCALED_BY[] = new TBigDecimal[11]; + + /** An array filled with characters '0'. */ + private static final char[] CH_ZEROS = new char[100]; + + static { + // To fill all static arrays. + int i = 0; + + for (; i < ZERO_SCALED_BY.length; i++) { + BI_SCALED_BY_ZERO[i] = new TBigDecimal(i, 0); + ZERO_SCALED_BY[i] = new TBigDecimal(0, i); + CH_ZEROS[i] = '0'; + } + + for (; i < CH_ZEROS.length; i++) { + CH_ZEROS[i] = '0'; + } + for(int j=0; jprecision(). 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) { + // math.02= + 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("Infinity 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()) || (this.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 ((this.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."); //$NON-NLS-1$ + 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..10efa33d2 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBigInteger.java @@ -0,0 +1,1539 @@ +/* + * 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<> 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..0c4592a47 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TBitLevel.java @@ -0,0 +1,353 @@ +/* + * 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..cfe352c75 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TConversion.java @@ -0,0 +1,458 @@ +/* + * 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"; //$NON-NLS-1$ + } + 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"; //$NON-NLS-1$ + case 1: + return "0.0"; //$NON-NLS-1$ + case 2: + return "0.00"; //$NON-NLS-1$ + case 3: + return "0.000"; //$NON-NLS-1$ + case 4: + return "0.0000"; //$NON-NLS-1$ + case 5: + return "0.00000"; //$NON-NLS-1$ + case 6: + return "0.000000"; //$NON-NLS-1$ + default: + StringBuilder result1 = new StringBuilder(); + if (scale < 0) { + result1.append("0E+"); //$NON-NLS-1$ + } else { + result1.append("0E"); //$NON-NLS-1$ + } + 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"; //$NON-NLS-1$ + case 1: return "0.0"; //$NON-NLS-1$ + case 2: return "0.00"; //$NON-NLS-1$ + case 3: return "0.000"; //$NON-NLS-1$ + case 4: return "0.0000"; //$NON-NLS-1$ + case 5: return "0.00000"; //$NON-NLS-1$ + case 6: return "0.000000"; //$NON-NLS-1$ + default: + StringBuilder result1 = new StringBuilder(); + if (scale < 0) { + result1.append("0E+"); //$NON-NLS-1$ + } else { + result1.append("0E"); //$NON-NLS-1$ + } + result1.append( (scale == Integer.MIN_VALUE) ? "2147483648" : Integer.toString(-scale)); //$NON-NLS-1$ + 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=a3a2a1a0
B=b2b1b1
b0*a3b0*a2b0*a1b0*a0
b1*a3b1*a2b1*a1b1*a0
+b2*a3b2*a2b2*a1b2*a0
____________________________________
A*B=R=r5r4r3r2r1r0
+ * + *
+ * + * @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..be4538161 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/math/TPrimality.java @@ -0,0 +1,279 @@ +/* + * 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/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java new file mode 100644 index 000000000..e83ee39a3 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java @@ -0,0 +1,1712 @@ +/* + * 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 static org.junit.Assert.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.math.RoundingMode; +import org.junit.Test; + +/** + * Class: java.math.BigDecimal + * Methods: add, subtract, multiply, divide + */ +public class BigDecimalArithmeticTest { + /** + * Add two numbers of equal positive scales + */ + @Test + public void testAddEqualScalePosPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 10; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "123121247898748373566323807282924555312937.1991359555"; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.add(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Add two numbers of equal positive scales using MathContext + */ + @Test + public void testAddMathContextEqualScalePosPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 10; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "1.2313E+41"; + int cScale = -37; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(5, RoundingMode.UP); + BigDecimal result = aNumber.add(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Add two numbers of equal negative scales + */ + @Test + public void testAddEqualScaleNegNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -10; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "1.231212478987483735663238072829245553129371991359555E+61"; + int cScale = -10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.add(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Add two numbers of equal negative scales using MathContext + */ + @Test + public void testAddMathContextEqualScaleNegNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -10; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "1.2312E+61"; + int cScale = -57; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(5, RoundingMode.FLOOR); + BigDecimal result = aNumber.add(bNumber, mc); + assertEquals("incorrect value ", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Add two numbers of different scales; the first is positive + */ + @Test + public void testAddDiffScalePosNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "7472334294161400358170962860775454459810457634.781384756794987"; + int cScale = 15; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.add(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Add two numbers of different scales using MathContext; the first is positive + */ + @Test + public void testAddMathContextDiffScalePosNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "7.47233429416141E+45"; + int cScale = -31; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(15, RoundingMode.CEILING); + BigDecimal result = aNumber.add(bNumber, mc); + assertEquals("incorrect value", c, c.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Add two numbers of different scales; the first is negative + */ + @Test + public void testAddDiffScaleNegPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "1231212478987482988429808779810457634781459480137916301878791834798.7234564568"; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.add(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Add two zeroes of different scales; the first is negative + */ + @Test + public void testAddDiffScaleZeroZero() { + String a = "0"; + int aScale = -15; + String b = "0"; + int bScale = 10; + String c = "0E-10"; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.add(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Subtract two numbers of equal positive scales + */ + @Test + public void testSubtractEqualScalePosPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 10; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "123121247898748224119637948679166971643339.7522230419"; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.subtract(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Subtract two numbers of equal positive scales using MathContext + */ + @Test + public void testSubtractMathContextEqualScalePosPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 10; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "1.23121247898749E+41"; + int cScale = -27; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(15, RoundingMode.CEILING); + BigDecimal result = aNumber.subtract(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Subtract two numbers of equal negative scales + */ + @Test + public void testSubtractEqualScaleNegNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -10; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "1.231212478987482241196379486791669716433397522230419E+61"; + int cScale = -10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.subtract(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Subtract two numbers of different scales; the first is positive + */ + @Test + public void testSubtractDiffScalePosNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "-7472334291698975400195996883915836900189542365.218615243205013"; + int cScale = 15; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.subtract(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Subtract two numbers of different scales using MathContext; + * the first is positive + */ + @Test + public void testSubtractMathContextDiffScalePosNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "-7.4723342916989754E+45"; + int cScale = -29; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(17, RoundingMode.DOWN); + BigDecimal result = aNumber.subtract(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Subtract two numbers of different scales; the first is negative + */ + @Test + public void testSubtractDiffScaleNegPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "1231212478987482988429808779810457634781310033452057698121208165201.2765435432"; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.subtract(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Subtract two numbers of different scales using MathContext; + * the first is negative + */ + @Test + public void testSubtractMathContextDiffScaleNegPos() { + String a = "986798656676789766678767876078779810457634781384756794987"; + int aScale = -15; + String b = "747233429293018787918347987234564568"; + int bScale = 40; + String c = "9.867986566767897666787678760787798104576347813847567949870000000000000E+71"; + int cScale = -2; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(70, RoundingMode.HALF_DOWN); + BigDecimal result = aNumber.subtract(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Multiply two numbers of positive scales + */ + @Test + public void testMultiplyScalePosPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "92000312286217574978643009574114545567010139156902666284589309.1880727173060570190220616"; + int cScale = 25; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.multiply(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Multiply two numbers of positive scales using MathContext + */ + @Test + public void testMultiplyMathContextScalePosPos() { + String a = "97665696756578755423325476545428779810457634781384756794987"; + int aScale = -25; + String b = "87656965586786097685674786576598865"; + int bScale = 10; + String c = "8.561078619600910561431314228543672720908E+108"; + int cScale = -69; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(40, RoundingMode.HALF_DOWN); + BigDecimal result = aNumber.multiply(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Multiply two numbers of negative scales + */ + @Test + public void testMultiplyEqualScaleNegNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -15; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "9.20003122862175749786430095741145455670101391569026662845893091880727173060570190220616E+111"; + int cScale = -25; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.multiply(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Multiply two numbers of different scales + */ + @Test + public void testMultiplyDiffScalePosNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 10; + String b = "747233429293018787918347987234564568"; + int bScale = -10; + String c = "920003122862175749786430095741145455670101391569026662845893091880727173060570190220616"; + int cScale = 0; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.multiply(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Multiply two numbers of different scales using MathContext + */ + @Test + public void testMultiplyMathContextDiffScalePosNeg() { + String a = "987667796597975765768768767866756808779810457634781384756794987"; + int aScale = 100; + String b = "747233429293018787918347987234564568"; + int bScale = -70; + String c = "7.3801839465418518653942222612429081498248509257207477E+68"; + int cScale = -16; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(53, RoundingMode.HALF_UP); + BigDecimal result = aNumber.multiply(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Multiply two numbers of different scales + */ + @Test + public void testMultiplyDiffScaleNegPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "9.20003122862175749786430095741145455670101391569026662845893091880727173060570190220616E+91"; + int cScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.multiply(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Multiply two numbers of different scales using MathContext + */ + @Test + public void testMultiplyMathContextDiffScaleNegPos() { + String a = "488757458676796558668876576576579097029810457634781384756794987"; + int aScale = -63; + String b = "747233429293018787918347987234564568"; + int bScale = 63; + String c = "3.6521591193960361339707130098174381429788164316E+98"; + int cScale = -52; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + MathContext mc = new MathContext(47, RoundingMode.HALF_UP); + BigDecimal result = aNumber.multiply(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * pow(int) + */ + @Test + public void testPow() { + String a = "123121247898748298842980"; + int aScale = 10; + int exp = 10; + String c = "8004424019039195734129783677098845174704975003788210729597" + + "4875206425711159855030832837132149513512555214958035390490" + + "798520842025826.594316163502809818340013610490541783276343" + + "6514490899700151256484355936102754469438371850240000000000"; + int cScale = 100; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.pow(exp); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * pow(0) + */ + @Test + public void testPow0() { + String a = "123121247898748298842980"; + int aScale = 10; + int exp = 0; + String c = "1"; + int cScale = 0; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.pow(exp); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * ZERO.pow(0) + */ + @Test + public void testZeroPow0() { + String c = "1"; + int cScale = 0; + BigDecimal result = BigDecimal.ZERO.pow(0); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * pow(int, MathContext) + */ + @Test + public void testPowMathContext() { + String a = "123121247898748298842980"; + int aScale = 10; + int exp = 10; + String c = "8.0044E+130"; + int cScale = -126; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + MathContext mc = new MathContext(5, RoundingMode.HALF_UP); + BigDecimal result = aNumber.pow(exp, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", cScale, result.scale()); + } + + /** + * Divide by zero + */ + @Test + public void testDivideByZero() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = BigDecimal.valueOf(0L); + try { + aNumber.divide(bNumber); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Division by zero", e.getMessage()); + } + } + + /** + * Divide with ROUND_UNNECESSARY + */ + @Test + public void testDivideExceptionRM() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + try { + aNumber.divide(bNumber, BigDecimal.ROUND_UNNECESSARY); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Rounding necessary", e.getMessage()); + } + } + + /** + * Divide with invalid rounding mode + */ + @Test + public void testDivideExceptionInvalidRM() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + try { + aNumber.divide(bNumber, 100); + fail("IllegalArgumentException has not been caught"); + } catch (IllegalArgumentException e) { + assertEquals("Improper exception message", "Invalid rounding mode", e.getMessage()); + } + } + + /** + * Divide: local variable exponent is less than zero + */ + @Test + public void testDivideExpLessZero() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "1.64770E+10"; + int resScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_CEILING); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: local variable exponent is equal to zero + */ + @Test + public void testDivideExpEqualsZero() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -15; + String b = "747233429293018787918347987234564568"; + int bScale = 10; + String c = "1.64769459009933764189139568605273529E+40"; + int resScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_CEILING); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: local variable exponent is greater than zero + */ + @Test + public void testDivideExpGreaterZero() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -15; + String b = "747233429293018787918347987234564568"; + int bScale = 20; + String c = "1.647694590099337641891395686052735285121058381E+50"; + int resScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_CEILING); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: remainder is zero + */ + @Test + public void testDivideRemainderIsZero() { + String a = "8311389578904553209874735431110"; + int aScale = -15; + String b = "237468273682987234567849583746"; + int bScale = 20; + String c = "3.5000000000000000000000000000000E+36"; + int resScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_CEILING); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_UP, result is negative + */ + @Test + public void testDivideRoundUpNeg() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "-1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_UP); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_UP, result is positive + */ + @Test + public void testDivideRoundUpPos() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_UP); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_DOWN, result is negative + */ + @Test + public void testDivideRoundDownNeg() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "-1.24390557635720517122423359799283E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_DOWN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_DOWN, result is positive + */ + @Test + public void testDivideRoundDownPos() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "1.24390557635720517122423359799283E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_DOWN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_FLOOR, result is positive + */ + @Test + public void testDivideRoundFloorPos() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "1.24390557635720517122423359799283E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_FLOOR); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_FLOOR, result is negative + */ + @Test + public void testDivideRoundFloorNeg() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "-1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_FLOOR); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_CEILING, result is positive + */ + @Test + public void testDivideRoundCeilingPos() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_CEILING); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_CEILING, result is negative + */ + @Test + public void testDivideRoundCeilingNeg() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "-1.24390557635720517122423359799283E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_CEILING); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_UP, result is positive; distance = -1 + */ + @Test + public void testDivideRoundHalfUpPos() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_UP); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_UP, result is negative; distance = -1 + */ + @Test + public void testDivideRoundHalfUpNeg() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "-1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_UP); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_UP, result is positive; distance = 1 + */ + @Test + public void testDivideRoundHalfUpPos1() { + String a = "92948782094488478231212478987482988798104576347813847567949855464535634534563456"; + int aScale = -24; + String b = "74723342238476237823754692930187879183479"; + int bScale = 13; + String c = "1.2439055763572051712242335979928354832010167729111113605E+76"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_UP); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_UP, result is negative; distance = 1 + */ + @Test + public void testDivideRoundHalfUpNeg1() { + String a = "-92948782094488478231212478987482988798104576347813847567949855464535634534563456"; + int aScale = -24; + String b = "74723342238476237823754692930187879183479"; + int bScale = 13; + String c = "-1.2439055763572051712242335979928354832010167729111113605E+76"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_UP); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_UP, result is negative; equidistant + */ + @Test + public void testDivideRoundHalfUpNeg2() { + String a = "-37361671119238118911893939591735"; + int aScale = 10; + String b = "74723342238476237823787879183470"; + int bScale = 15; + String c = "-1E+5"; + int resScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_UP); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_DOWN, result is positive; distance = -1 + */ + @Test + public void testDivideRoundHalfDownPos() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_DOWN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_DOWN, result is negative; distance = -1 + */ + @Test + public void testDivideRoundHalfDownNeg() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "-1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_DOWN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_DOWN, result is positive; distance = 1 + */ + @Test + public void testDivideRoundHalfDownPos1() { + String a = "92948782094488478231212478987482988798104576347813847567949855464535634534563456"; + int aScale = -24; + String b = "74723342238476237823754692930187879183479"; + int bScale = 13; + String c = "1.2439055763572051712242335979928354832010167729111113605E+76"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_DOWN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_DOWN, result is negative; distance = 1 + */ + @Test + public void testDivideRoundHalfDownNeg1() { + String a = "-92948782094488478231212478987482988798104576347813847567949855464535634534563456"; + int aScale = -24; + String b = "74723342238476237823754692930187879183479"; + int bScale = 13; + String c = "-1.2439055763572051712242335979928354832010167729111113605E+76"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_DOWN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_UP, result is negative; equidistant + */ + @Test + public void testDivideRoundHalfDownNeg2() { + String a = "-37361671119238118911893939591735"; + int aScale = 10; + String b = "74723342238476237823787879183470"; + int bScale = 15; + String c = "0E+5"; + int resScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_DOWN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_EVEN, result is positive; distance = -1 + */ + @Test + public void testDivideRoundHalfEvenPos() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_EVEN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_EVEN, result is negative; distance = -1 + */ + @Test + public void testDivideRoundHalfEvenNeg() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + String c = "-1.24390557635720517122423359799284E+53"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_EVEN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_EVEN, result is positive; distance = 1 + */ + @Test + public void testDivideRoundHalfEvenPos1() { + String a = "92948782094488478231212478987482988798104576347813847567949855464535634534563456"; + int aScale = -24; + String b = "74723342238476237823754692930187879183479"; + int bScale = 13; + String c = "1.2439055763572051712242335979928354832010167729111113605E+76"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_EVEN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_EVEN, result is negative; distance = 1 + */ + @Test + public void testDivideRoundHalfEvenNeg1() { + String a = "-92948782094488478231212478987482988798104576347813847567949855464535634534563456"; + int aScale = -24; + String b = "74723342238476237823754692930187879183479"; + int bScale = 13; + String c = "-1.2439055763572051712242335979928354832010167729111113605E+76"; + int resScale = -21; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_EVEN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide: rounding mode is ROUND_HALF_EVEN, result is negative; equidistant + */ + @Test + public void testDivideRoundHalfEvenNeg2() { + String a = "-37361671119238118911893939591735"; + int aScale = 10; + String b = "74723342238476237823787879183470"; + int bScale = 15; + String c = "0E+5"; + int resScale = -5; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, resScale, BigDecimal.ROUND_HALF_EVEN); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide to BigDecimal + */ + @Test + public void testDivideBigDecimal1() { + String a = "-37361671119238118911893939591735"; + int aScale = 10; + String b = "74723342238476237823787879183470"; + int bScale = 15; + String c = "-5E+4"; + int resScale = -4; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Divide to BigDecimal + */ + @Test + public void testDivideBigDecimal2() { + String a = "-37361671119238118911893939591735"; + int aScale = 10; + String b = "74723342238476237823787879183470"; + int bScale = -15; + String c = "-5E-26"; + int resScale = 26; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divide(BigDecimal, scale, RoundingMode) + */ + @Test + public void testDivideBigDecimalScaleRoundingModeUP() { + String a = "-37361671119238118911893939591735"; + int aScale = 10; + String b = "74723342238476237823787879183470"; + int bScale = -15; + int newScale = 31; + RoundingMode rm = RoundingMode.UP; + String c = "-5.00000E-26"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, newScale, rm); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", newScale, result.scale()); + } + + /** + * divide(BigDecimal, scale, RoundingMode) + */ + @Test + public void testDivideBigDecimalScaleRoundingModeDOWN() { + String a = "-37361671119238118911893939591735"; + int aScale = 10; + String b = "74723342238476237823787879183470"; + int bScale = 15; + int newScale = 31; + RoundingMode rm = RoundingMode.DOWN; + String c = "-50000.0000000000000000000000000000000"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, newScale, rm); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", newScale, result.scale()); + } + + /** + * divide(BigDecimal, scale, RoundingMode) + */ + @Test + public void testDivideBigDecimalScaleRoundingModeCEILING() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 100; + String b = "74723342238476237823787879183470"; + int bScale = 15; + int newScale = 45; + RoundingMode rm = RoundingMode.CEILING; + String c = "1E-45"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, newScale, rm); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", newScale, result.scale()); + } + + /** + * divide(BigDecimal, scale, RoundingMode) + */ + @Test + public void testDivideBigDecimalScaleRoundingModeFLOOR() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 100; + String b = "74723342238476237823787879183470"; + int bScale = 15; + int newScale = 45; + RoundingMode rm = RoundingMode.FLOOR; + String c = "0E-45"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, newScale, rm); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", newScale, result.scale()); + } + + /** + * divide(BigDecimal, scale, RoundingMode) + */ + @Test + public void testDivideBigDecimalScaleRoundingModeHALF_UP() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = -51; + String b = "74723342238476237823787879183470"; + int bScale = 45; + int newScale = 3; + RoundingMode rm = RoundingMode.HALF_UP; + String c = "50000260373164286401361913262100972218038099522752460421" + + "05959924024355721031761947728703598332749334086415670525" + + "3761096961.670"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, newScale, rm); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", newScale, result.scale()); + } + + /** + * divide(BigDecimal, scale, RoundingMode) + */ + @Test + public void testDivideBigDecimalScaleRoundingModeHALF_DOWN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 5; + String b = "74723342238476237823787879183470"; + int bScale = 15; + int newScale = 7; + RoundingMode rm = RoundingMode.HALF_DOWN; + String c = "500002603731642864013619132621009722.1803810"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, newScale, rm); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", newScale, result.scale()); + } + + /** + * divide(BigDecimal, scale, RoundingMode) + */ + @Test + public void testDivideBigDecimalScaleRoundingModeHALF_EVEN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 5; + String b = "74723342238476237823787879183470"; + int bScale = 15; + int newScale = 7; + RoundingMode rm = RoundingMode.HALF_EVEN; + String c = "500002603731642864013619132621009722.1803810"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, newScale, rm); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", newScale, result.scale()); + } + + /** + * divide(BigDecimal, MathContext) + */ + @Test + public void testDivideBigDecimalScaleMathContextUP() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 15; + String b = "748766876876723342238476237823787879183470"; + int bScale = 10; + int precision = 21; + RoundingMode rm = RoundingMode.UP; + MathContext mc = new MathContext(precision, rm); + String c = "49897861180.2562512996"; + int resScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divide(BigDecimal, MathContext) + */ + @Test + public void testDivideBigDecimalScaleMathContextDOWN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 15; + String b = "748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 21; + RoundingMode rm = RoundingMode.DOWN; + MathContext mc = new MathContext(precision, rm); + String c = "4.98978611802562512995E+70"; + int resScale = -50; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divide(BigDecimal, MathContext) + */ + @Test + public void testDivideBigDecimalScaleMathContextCEILING() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 15; + String b = "748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 21; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String c = "4.98978611802562512996E+70"; + int resScale = -50; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divide(BigDecimal, MathContext) + */ + @Test + public void testDivideBigDecimalScaleMathContextFLOOR() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 15; + String b = "748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 21; + RoundingMode rm = RoundingMode.FLOOR; + MathContext mc = new MathContext(precision, rm); + String c = "4.98978611802562512995E+70"; + int resScale = -50; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divide(BigDecimal, MathContext) + */ + @Test + public void testDivideBigDecimalScaleMathContextHALF_UP() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 21; + RoundingMode rm = RoundingMode.HALF_UP; + MathContext mc = new MathContext(precision, rm); + String c = "2.77923185514690367475E+26"; + int resScale = -6; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divide(BigDecimal, MathContext) + */ + @Test + public void testDivideBigDecimalScaleMathContextHALF_DOWN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 21; + RoundingMode rm = RoundingMode.HALF_DOWN; + MathContext mc = new MathContext(precision, rm); + String c = "2.77923185514690367475E+26"; + int resScale = -6; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divide(BigDecimal, MathContext) + */ + @Test + public void testDivideBigDecimalScaleMathContextHALF_EVEN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 21; + RoundingMode rm = RoundingMode.HALF_EVEN; + MathContext mc = new MathContext(precision, rm); + String c = "2.77923185514690367475E+26"; + int resScale = -6; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divide(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + + /** + * BigDecimal.divide with a scale that's too large + * + * Regression test for HARMONY-6271 + */ + @Test + public void testDivideLargeScale() { + BigDecimal arg1 = new BigDecimal("320.0E+2147483647"); + BigDecimal arg2 = new BigDecimal("6E-2147483647"); + try { + arg1.divide(arg2, Integer.MAX_VALUE, java.math.RoundingMode.CEILING); + fail("Expected ArithmeticException when dividing with a scale that's too large"); + } catch (ArithmeticException e) { + // expected behaviour + } + } + + /** + * divideToIntegralValue(BigDecimal) + */ + @Test + public void testDivideToIntegralValue() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + String c = "277923185514690367474770683"; + int resScale = 0; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divideToIntegralValue(bNumber); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divideToIntegralValue(BigDecimal, MathContext) + */ + @Test + public void testDivideToIntegralValueMathContextUP() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 32; + RoundingMode rm = RoundingMode.UP; + MathContext mc = new MathContext(precision, rm); + String c = "277923185514690367474770683"; + int resScale = 0; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divideToIntegralValue(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divideToIntegralValue(BigDecimal, MathContext) + */ + @Test + public void testDivideToIntegralValueMathContextDOWN() { + String a = "3736186567876876578956958769675785435673453453653543654354365435675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 75; + RoundingMode rm = RoundingMode.DOWN; + MathContext mc = new MathContext(precision, rm); + String c = "2.7792318551469036747477068339450205874992634417590178670822889E+62"; + int resScale = -1; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.divideToIntegralValue(bNumber, mc); + assertEquals("incorrect value", c, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * divideAndRemainder(BigDecimal) + */ + @Test + public void testDivideAndRemainder1() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + String res = "277923185514690367474770683"; + int resScale = 0; + String rem = "1.3032693871288309587558885943391070087960319452465789990E-15"; + int remScale = 70; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result[] = aNumber.divideAndRemainder(bNumber); + assertEquals("incorrect quotient value", res, result[0].toString()); + assertEquals("incorrect quotient scale", resScale, result[0].scale()); + assertEquals("incorrect remainder value", rem, result[1].toString()); + assertEquals("incorrect remainder scale", remScale, result[1].scale()); + } + + /** + * divideAndRemainder(BigDecimal) + */ + @Test + public void testDivideAndRemainder2() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = -45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + String res = "2779231855146903674747706830969461168692256919247547952" + + "2608549363170374005512836303475980101168105698072946555" + + "6862849"; + int resScale = 0; + String rem = "3.4935796954060524114470681810486417234751682675102093970E-15"; + int remScale = 70; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result[] = aNumber.divideAndRemainder(bNumber); + assertEquals("incorrect quotient value", res, result[0].toString()); + assertEquals("incorrect quotient scale", resScale, result[0].scale()); + assertEquals("incorrect remainder value", rem, result[1].toString()); + assertEquals("incorrect remainder scale", remScale, result[1].scale()); + } + + /** + * divideAndRemainder(BigDecimal, MathContext) + */ + @Test + public void testDivideAndRemainderMathContextUP() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 70; + int precision = 75; + RoundingMode rm = RoundingMode.UP; + MathContext mc = new MathContext(precision, rm); + String res = "277923185514690367474770683"; + int resScale = 0; + String rem = "1.3032693871288309587558885943391070087960319452465789990E-15"; + int remScale = 70; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result[] = aNumber.divideAndRemainder(bNumber, mc); + assertEquals("incorrect quotient value", res, result[0].toString()); + assertEquals("incorrect quotient scale", resScale, result[0].scale()); + assertEquals("incorrect remainder value", rem, result[1].toString()); + assertEquals("incorrect remainder scale", remScale, result[1].scale()); + } + + /** + * divideAndRemainder(BigDecimal, MathContext) + */ + @Test + public void testDivideAndRemainderMathContextDOWN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 20; + int precision = 15; + RoundingMode rm = RoundingMode.DOWN; + MathContext mc = new MathContext(precision, rm); + String res = "0E-25"; + int resScale = 25; + String rem = "3736186567876.876578956958765675671119238118911893939591735"; + int remScale = 45; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result[] = aNumber.divideAndRemainder(bNumber, mc); + assertEquals("incorrect quotient value", res, result[0].toString()); + assertEquals("incorrect quotient scale", resScale, result[0].scale()); + assertEquals("incorrect remainder value", rem, result[1].toString()); + assertEquals("incorrect remainder scale", remScale, result[1].scale()); + } + + /** + * remainder(BigDecimal) + */ + @Test + public void testRemainder1() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 10; + String res = "3736186567876.876578956958765675671119238118911893939591735"; + int resScale = 45; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.remainder(bNumber); + assertEquals("incorrect quotient value", res, result.toString()); + assertEquals("incorrect quotient scale", resScale, result.scale()); + } + + /** + * remainder(BigDecimal) + */ + @Test + public void testRemainder2() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = -45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 10; + String res = "1149310942946292909508821656680979993738625937.2065885780"; + int resScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.remainder(bNumber); + assertEquals("incorrect quotient value", res, result.toString()); + assertEquals("incorrect quotient scale", resScale, result.scale()); + } + + /** + * remainder(BigDecimal, MathContext) + */ + @Test + public void testRemainderMathContextHALF_UP() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 10; + int precision = 15; + RoundingMode rm = RoundingMode.HALF_UP; + MathContext mc = new MathContext(precision, rm); + String res = "3736186567876.876578956958765675671119238118911893939591735"; + int resScale = 45; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.remainder(bNumber, mc); + assertEquals("incorrect quotient value", res, result.toString()); + assertEquals("incorrect quotient scale", resScale, result.scale()); + } + + /** + * remainder(BigDecimal, MathContext) + */ + @Test + public void testRemainderMathContextHALF_DOWN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = -45; + String b = "134432345432345748766876876723342238476237823787879183470"; + int bScale = 10; + int precision = 75; + RoundingMode rm = RoundingMode.HALF_DOWN; + MathContext mc = new MathContext(precision, rm); + String res = "1149310942946292909508821656680979993738625937.2065885780"; + int resScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal result = aNumber.remainder(bNumber, mc); + assertEquals("incorrect quotient value", res, result.toString()); + assertEquals("incorrect quotient scale", resScale, result.scale()); + } + + /** + * round(BigDecimal, MathContext) + */ + @Test + public void testRoundMathContextHALF_DOWN() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = -45; + int precision = 75; + RoundingMode rm = RoundingMode.HALF_DOWN; + MathContext mc = new MathContext(precision, rm); + String res = "3.736186567876876578956958765675671119238118911893939591735E+102"; + int resScale = -45; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.round(mc); + assertEquals("incorrect quotient value", res, result.toString()); + assertEquals("incorrect quotient scale", resScale, result.scale()); + } + + /** + * round(BigDecimal, MathContext) + */ + @Test + public void testRoundMathContextHALF_UP() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + int precision = 15; + RoundingMode rm = RoundingMode.HALF_UP; + MathContext mc = new MathContext(precision, rm); + String res = "3736186567876.88"; + int resScale = 2; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.round(mc); + assertEquals("incorrect quotient value", res, result.toString()); + assertEquals("incorrect quotient scale", resScale, result.scale()); + } + + /** + * round(BigDecimal, MathContext) when precision = 0 + */ + @Test + public void testRoundMathContextPrecision0() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + int precision = 0; + RoundingMode rm = RoundingMode.HALF_UP; + MathContext mc = new MathContext(precision, rm); + String res = "3736186567876.876578956958765675671119238118911893939591735"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.round(mc); + assertEquals("incorrect quotient value", res, result.toString()); + assertEquals("incorrect quotient scale", aScale, result.scale()); + } + + + /** + * ulp() of a positive BigDecimal + */ + @Test + public void testUlpPos() { + String a = "3736186567876876578956958765675671119238118911893939591735"; + int aScale = -45; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.ulp(); + String res = "1E+45"; + int resScale = -45; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * ulp() of a negative BigDecimal + */ + @Test + public void testUlpNeg() { + String a = "-3736186567876876578956958765675671119238118911893939591735"; + int aScale = 45; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.ulp(); + String res = "1E-45"; + int resScale = 45; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * ulp() of a negative BigDecimal + */ + @Test + public void testUlpZero() { + String a = "0"; + int aScale = 2; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.ulp(); + String res = "0.01"; + int resScale = 2; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java new file mode 100644 index 000000000..5fb96c0e3 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java @@ -0,0 +1,549 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.math.RoundingMode; +import org.junit.Test; + +/** + * Class: java.math.BigDecimal + * Methods: abs, compareTo, equals, hashCode, + * max, min, negate, signum + */ +public class BigDecimalCompareTest { + /** + * Abs() of a negative BigDecimal + */ + @Test + public void testAbsNeg() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + String result = "123809648392384754573567356745735635678902957849027687.87678287"; + assertEquals("incorrect value", result, aNumber.abs().toString()); + } + + /** + * Abs() of a positive BigDecimal + */ + @Test + public void testAbsPos() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + String result = "123809648392384754573567356745735635678902957849027687.87678287"; + assertEquals("incorrect value", result, aNumber.abs().toString()); + } + + /** + * Abs(MathContext) of a negative BigDecimal + */ + @Test + public void testAbsMathContextNeg() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + int precision = 15; + RoundingMode rm = RoundingMode.HALF_DOWN; + MathContext mc = new MathContext(precision, rm); + String result = "1.23809648392385E+53"; + int resScale = -39; + BigDecimal res = aNumber.abs(mc); + assertEquals("incorrect value", result, res.toString()); + assertEquals("incorrect scale", resScale, res.scale()); + } + + /** + * Abs(MathContext) of a positive BigDecimal + */ + @Test + public void testAbsMathContextPos() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + int precision = 41; + RoundingMode rm = RoundingMode.HALF_EVEN; + MathContext mc = new MathContext(precision, rm); + String result = "1.2380964839238475457356735674573563567890E+53"; + int resScale = -13; + BigDecimal res = aNumber.abs(mc); + assertEquals("incorrect value", result, res.toString()); + assertEquals("incorrect scale", resScale, res.scale()); + } + + /** + * Compare to a number of an equal scale + */ + @Test + public void testCompareEqualScale1() { + String a = "12380964839238475457356735674573563567890295784902768787678287"; + int aScale = 18; + String b = "4573563567890295784902768787678287"; + int bScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + int result = 1; + assertEquals("incorrect result", result, aNumber.compareTo(bNumber)); + } + + /** + * Compare to a number of an equal scale + */ + @Test + public void testCompareEqualScale2() { + String a = "12380964839238475457356735674573563567890295784902768787678287"; + int aScale = 18; + String b = "4573563923487289357829759278282992758247567890295784902768787678287"; + int bScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + int result = -1; + assertEquals("incorrect result", result, aNumber.compareTo(bNumber)); + } + + /** + * Compare to a number of an greater scale + */ + @Test + public void testCompareGreaterScale1() { + String a = "12380964839238475457356735674573563567890295784902768787678287"; + int aScale = 28; + String b = "4573563567890295784902768787678287"; + int bScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + int result = 1; + assertEquals("incorrect result", result, aNumber.compareTo(bNumber)); + } + + /** + * Compare to a number of an greater scale + */ + @Test + public void testCompareGreaterScale2() { + String a = "12380964839238475457356735674573563567890295784902768787678287"; + int aScale = 48; + String b = "4573563567890295784902768787678287"; + int bScale = 2; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + int result = -1; + assertEquals("incorrect result", result, aNumber.compareTo(bNumber)); + } + + /** + * Compare to a number of an less scale + */ + @Test + public void testCompareLessScale1() { + String a = "12380964839238475457356735674573563567890295784902768787678287"; + int aScale = 18; + String b = "4573563567890295784902768787678287"; + int bScale = 28; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + int result = 1; + assertEquals("incorrect result", result, aNumber.compareTo(bNumber)); + } + + /** + * Compare to a number of an less scale + */ + @Test + public void testCompareLessScale2() { + String a = "12380964839238475457356735674573"; + int aScale = 36; + String b = "45735635948573894578349572001798379183767890295784902768787678287"; + int bScale = 48; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + int result = -1; + assertEquals("incorrect result", result, aNumber.compareTo(bNumber)); + } + + /** + * Equals() for unequal BigDecimals + */ + @Test + public void testEqualsUnequal1() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "7472334223847623782375469293018787918347987234564568"; + int bScale = 13; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + assertFalse(aNumber.equals(bNumber)); + } + + /** + * Equals() for unequal BigDecimals + */ + @Test + public void testEqualsUnequal2() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "92948782094488478231212478987482988429808779810457634781384756794987"; + int bScale = 13; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + assertFalse(aNumber.equals(bNumber)); + } + + /** + * Equals() for unequal BigDecimals + */ + @Test + public void testEqualsUnequal3() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "92948782094488478231212478987482988429808779810457634781384756794987"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertFalse(aNumber.equals(b)); + } + + /** + * equals() for equal BigDecimals + */ + @Test + public void testEqualsEqual() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "92948782094488478231212478987482988429808779810457634781384756794987"; + int bScale = -24; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + assertEquals(aNumber, bNumber); + } + + /** + * equals() for equal BigDecimals + */ + @Test + public void testEqualsNull() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertFalse(aNumber.equals(null)); + } + + /** + * hashCode() for equal BigDecimals + */ + @Test + public void testHashCodeEqual() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = -24; + String b = "92948782094488478231212478987482988429808779810457634781384756794987"; + int bScale = -24; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + assertEquals("incorrect value", aNumber.hashCode(), bNumber.hashCode()); + } + + /** + * hashCode() for unequal BigDecimals + */ + @Test + public void testHashCodeUnequal() { + String a = "8478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String b = "92948782094488478231212478987482988429808779810457634781384756794987"; + int bScale = -24; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + assertTrue("incorrect value", aNumber.hashCode() != bNumber.hashCode()); + } + + /** + * max() for equal BigDecimals + */ + @Test + public void testMaxEqual() { + String a = "8478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String b = "8478231212478987482988429808779810457634781384756794987"; + int bScale = 41; + String c = "8478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.max(bNumber)); + } + + /** + * max() for unequal BigDecimals + */ + @Test + public void testMaxUnequal1() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 24; + String b = "92948782094488478231212478987482988429808779810457634781384756794987"; + int bScale = 41; + String c = "92948782094488478231212478987482988429808779810457634781384756794987"; + int cScale = 24; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.max(bNumber)); + } + + /** + * max() for unequal BigDecimals + */ + @Test + public void testMaxUnequal2() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String b = "94488478231212478987482988429808779810457634781384756794987"; + int bScale = 41; + String c = "92948782094488478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.max(bNumber)); + } + + /** + * min() for equal BigDecimals + */ + @Test + public void testMinEqual() { + String a = "8478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String b = "8478231212478987482988429808779810457634781384756794987"; + int bScale = 41; + String c = "8478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.min(bNumber)); + } + + /** + * min() for unequal BigDecimals + */ + @Test + public void testMinUnequal1() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 24; + String b = "92948782094488478231212478987482988429808779810457634781384756794987"; + int bScale = 41; + String c = "92948782094488478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.min(bNumber)); + } + + /** + * min() for unequal BigDecimals + */ + @Test + public void testMinUnequal2() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String b = "94488478231212478987482988429808779810457634781384756794987"; + int bScale = 41; + String c = "94488478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = new BigDecimal(new BigInteger(b), bScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.min(bNumber)); + } + + /** + * plus() for a positive BigDecimal + */ + @Test + public void testPlusPositive() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String c = "92948782094488478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.plus()); + } + + /** + * plus(MathContext) for a positive BigDecimal + */ + @Test + public void testPlusMathContextPositive() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + int precision = 37; + RoundingMode rm = RoundingMode.FLOOR; + MathContext mc = new MathContext(precision, rm); + String c = "929487820944884782312124789.8748298842"; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal res = aNumber.plus(mc); + assertEquals("incorrect value", c, res.toString()); + assertEquals("incorrect scale", cScale, res.scale()); + } + + /** + * plus() for a negative BigDecimal + */ + @Test + public void testPlusNegative() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String c = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.plus()); + } + + /** + * plus(MathContext) for a negative BigDecimal + */ + @Test + public void testPlusMathContextNegative() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 49; + int precision = 46; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String c = "-9294878209448847823.121247898748298842980877981"; + int cScale = 27; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal res = aNumber.plus(mc); + assertEquals("incorrect value", c, res.toString()); + assertEquals("incorrect scale", cScale, res.scale()); + } + + /** + * negate() for a positive BigDecimal + */ + @Test + public void testNegatePositive() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String c = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.negate()); + } + + /** + * negate(MathContext) for a positive BigDecimal + */ + @Test + public void testNegateMathContextPositive() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + int precision = 37; + RoundingMode rm = RoundingMode.FLOOR; + MathContext mc = new MathContext(precision, rm); + String c = "-929487820944884782312124789.874829884"; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal res = aNumber.negate(mc); + String resString = res.toString(); + assertEquals("incorrect value", c, resString.substring(0, resString.length() - 1)); + assertEquals("incorrect scale", cScale, res.scale()); + } + + /** + * negate() for a negative BigDecimal + */ + @Test + public void testNegateNegative() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + String c = "92948782094488478231212478987482988429808779810457634781384756794987"; + int cScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal cNumber = new BigDecimal(new BigInteger(c), cScale); + assertEquals("incorrect value", cNumber, aNumber.negate()); + } + + /** + * negate(MathContext) for a negative BigDecimal + */ + @Test + public void testNegateMathContextNegative() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 49; + int precision = 46; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String c = "9294878209448847823.12124789874829884298087798"; + int cScale = 27; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal res = aNumber.negate(mc); + String resString = res.toString(); + assertEquals("incorrect value", c, resString.substring(0, resString.length() - 1)); + assertEquals("incorrect scale", cScale, res.scale()); + } + + /** + * signum() for a positive BigDecimal + */ + @Test + public void testSignumPositive() { + String a = "92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertEquals("incorrect value", 1, aNumber.signum()); + } + + /** + * signum() for a negative BigDecimal + */ + @Test + public void testSignumNegative() { + String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; + int aScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertEquals("incorrect value", -1, aNumber.signum()); + } + + /** + * signum() for zero + */ + @Test + public void testSignumZero() { + String a = "0"; + int aScale = 41; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertEquals("incorrect value", 0, aNumber.signum()); + } + + /* + * Regression test for HARMONY-6406 + */ + @Test + public void testApproxPrecision() { + BigDecimal testInstance = BigDecimal.TEN.multiply(new BigDecimal("0.1")); + int result = testInstance.compareTo(new BigDecimal("1.00")); + assertEquals(0, result); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java new file mode 100644 index 000000000..e85f30f4e --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java @@ -0,0 +1,740 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.math.RoundingMode; +import org.junit.Test; + +/** + * Class: java.math.BigDecimal + * Methods: constructors and fields + */ +public class BigDecimalConstructorsTest { + /** + * check ONE + */ + @Test + public void testFieldONE() { + String oneS = "1"; + double oneD = 1.0; + assertEquals("incorrect string value", oneS, BigDecimal.ONE.toString()); + assertEquals("incorrect double value", oneD, BigDecimal.ONE.doubleValue(), 0); + } + + /** + * check TEN + */ + @Test + public void testFieldTEN() { + String oneS = "10"; + double oneD = 10.0; + assertEquals("incorrect string value", oneS, BigDecimal.TEN.toString()); + assertEquals("incorrect double value", oneD, BigDecimal.TEN.doubleValue(), 0); + } + + /** + * check ZERO + */ + @Test + public void testFieldZERO() { + String oneS = "0"; + double oneD = 0.0; + assertEquals("incorrect string value", oneS, BigDecimal.ZERO.toString()); + assertEquals("incorrect double value", oneD, BigDecimal.ZERO.doubleValue(), 0); + } + + /** + * new BigDecimal(BigInteger value) + */ + @Test + public void testConstrBI() { + String a = "1231212478987482988429808779810457634781384756794987"; + BigInteger bA = new BigInteger(a); + BigDecimal aNumber = new BigDecimal(bA); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", 0, aNumber.scale()); + + try { + new BigDecimal((BigInteger) null); + fail("No NullPointerException"); + } catch (NullPointerException e) { + //expected + } + } + + /** + * new BigDecimal(BigInteger value, int scale) + */ + @Test + public void testConstrBIScale() { + String a = "1231212478987482988429808779810457634781384756794987"; + BigInteger bA = new BigInteger(a); + int aScale = 10; + BigDecimal aNumber = new BigDecimal(bA, aScale); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(BigInteger value, MathContext) + */ + @Test + public void testConstrBigIntegerMathContext() { + String a = "1231212478987482988429808779810457634781384756794987"; + BigInteger bA = new BigInteger(a); + int precision = 46; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String res = "1231212478987482988429808779810457634781384757"; + int resScale = -6; + BigDecimal result = new BigDecimal(bA, mc); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * new BigDecimal(BigInteger value, int scale, MathContext) + */ + @Test + public void testConstrBigIntegerScaleMathContext() { + String a = "1231212478987482988429808779810457634781384756794987"; + BigInteger bA = new BigInteger(a); + int aScale = 10; + int precision = 46; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String res = "1231212478987482988429808779810457634781384757"; + int resScale = 4; + BigDecimal result = new BigDecimal(bA, aScale, mc); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * new BigDecimal(char[] value); + */ + @Test + public void testConstrChar() { + char value[] = {'-', '1', '2', '3', '8', '0', '.', '4', '7', '3', '8', 'E', '-', '4', '2', '3'}; + BigDecimal result = new BigDecimal(value); + String res = "-1.23804738E-419"; + int resScale = 427; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + + try { + // Regression for HARMONY-783 + new BigDecimal(new char[] {}); + fail("NumberFormatException has not been thrown"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(char[] value, int offset, int len); + */ + @Test + public void testConstrCharIntInt() { + char value[] = {'-', '1', '2', '3', '8', '0', '.', '4', '7', '3', '8', 'E', '-', '4', '2', '3'}; + int offset = 3; + int len = 12; + BigDecimal result = new BigDecimal(value, offset, len); + String res = "3.804738E-40"; + int resScale = 46; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + + try { + // Regression for HARMONY-783 + new BigDecimal(new char[] {}, 0, 0); + fail("NumberFormatException has not been thrown"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(char[] value, int offset, int len, MathContext mc); + */ + @Test + public void testConstrCharIntIntMathContext() { + char value[] = {'-', '1', '2', '3', '8', '0', '.', '4', '7', '3', '8', 'E', '-', '4', '2', '3'}; + int offset = 3; + int len = 12; + int precision = 4; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + BigDecimal result = new BigDecimal(value, offset, len, mc); + String res = "3.805E-40"; + int resScale = 43; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + + try { + // Regression for HARMONY-783 + new BigDecimal(new char[] {}, 0, 0, MathContext.DECIMAL32); + fail("NumberFormatException has not been thrown"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(char[] value, int offset, int len, MathContext mc); + */ + @Test + public void testConstrCharIntIntMathContextException1() { + char value[] = {'-', '1', '2', '3', '8', '0', '.', '4', '7', '3', '8', 'E', '-', '4', '2', '3'}; + int offset = 3; + int len = 120; + int precision = 4; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + try { + new BigDecimal(value, offset, len, mc); + fail("NumberFormatException has not been thrown"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(char[] value, int offset, int len, MathContext mc); + */ + @Test + public void testConstrCharIntIntMathContextException2() { + char value[] = {'-', '1', '2', '3', '8', '0', ',', '4', '7', '3', '8', 'E', '-', '4', '2', '3'}; + int offset = 3; + int len = 120; + int precision = 4; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + try { + new BigDecimal(value, offset, len, mc); + fail("NumberFormatException has not been thrown"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(char[] value, MathContext mc); + */ + @Test + public void testConstrCharMathContext() { + try { + // Regression for HARMONY-783 + new BigDecimal(new char[] {}, MathContext.DECIMAL32); + fail("NumberFormatException has not been thrown"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(double value) when value is NaN + */ + @Test + public void testConstrDoubleNaN() { + double a = Double.NaN; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message", "Infinite or NaN", e + .getMessage()); + } + } + + /** + * new BigDecimal(double value) when value is positive infinity + */ + @Test + public void testConstrDoublePosInfinity() { + double a = Double.POSITIVE_INFINITY; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message", "Infinite or NaN", + e.getMessage()); + } + } + + /** + * new BigDecimal(double value) when value is positive infinity + */ + @Test + public void testConstrDoubleNegInfinity() { + double a = Double.NEGATIVE_INFINITY; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message", "Infinite or NaN", + e.getMessage()); + } + } + + /** + * new BigDecimal(double value) + */ + @Test + public void testConstrDouble() { + double a = 732546982374982347892379283571094797.287346782359284756; + int aScale = 0; + BigInteger bA = new BigInteger("732546982374982285073458350476230656"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(double, MathContext) + */ + @Test + public void testConstrDoubleMathContext() { + double a = 732546982374982347892379283571094797.287346782359284756; + int precision = 21; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String res = "732546982374982285074"; + int resScale = -15; + BigDecimal result = new BigDecimal(a, mc); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * new BigDecimal(0.1) + */ + @Test + public void testConstrDouble01() { + double a = 1.E-1; + int aScale = 55; + BigInteger bA = new BigInteger("1000000000000000055511151231257827021181583404541015625"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(0.555) + */ + @Test + public void testConstrDouble02() { + double a = 0.555; + int aScale = 53; + BigInteger bA = new BigInteger("55500000000000004884981308350688777863979339599609375"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(-0.1) + */ + @Test + public void testConstrDoubleMinus01() { + double a = -1.E-1; + int aScale = 55; + BigInteger bA = new BigInteger("-1000000000000000055511151231257827021181583404541015625"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(int value) + */ + @Test + public void testConstrInt() { + int a = 732546982; + String res = "732546982"; + int resScale = 0; + BigDecimal result = new BigDecimal(a); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * new BigDecimal(int, MathContext) + */ + @Test + public void testConstrIntMathContext() { + int a = 732546982; + int precision = 21; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String res = "732546982"; + int resScale = 0; + BigDecimal result = new BigDecimal(a, mc); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * new BigDecimal(long value) + */ + @Test + public void testConstrLong() { + long a = 4576578677732546982L; + String res = "4576578677732546982"; + int resScale = 0; + BigDecimal result = new BigDecimal(a); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * new BigDecimal(long, MathContext) + */ + @Test + public void testConstrLongMathContext() { + long a = 4576578677732546982L; + int precision = 5; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String res = "45766"; + int resScale = -14; + BigDecimal result = new BigDecimal(a, mc); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * new BigDecimal(double value) when value is denormalized + */ + @Test + public void testConstrDoubleDenormalized() { + double a = 2.274341322658976E-309; + int aScale = 1073; + BigInteger bA = new BigInteger("227434132265897633950269241702666687639731047124115603942986140264569528085692462493371029187342478828091760934014851133733918639492582043963243759464684978401240614084312038547315281016804838374623558434472007664427140169018817050565150914041833284370702366055678057809362286455237716100382057360123091641959140448783514464639706721250400288267372238950016114583259228262046633530468551311769574111763316146065958042194569102063373243372766692713192728878701004405568459288708477607744497502929764155046100964958011009313090462293046650352146796805866786767887226278836423536035611825593567576424943331337401071583562754098901412372708947790843318760718495117047155597276492717187936854356663665005157041552436478744491526494952982062613955349661409854888916015625"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value) + * when value is not a valid representation of BigDecimal. + */ + @Test + public void testConstrStringException() { + String a = "-238768.787678287a+10"; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) {} + } + + /** + * new BigDecimal(String value) when exponent is empty. + */ + @Test + public void testConstrStringExceptionEmptyExponent1() { + String a = "-238768.787678287e"; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(String value) when exponent is empty. + */ + @Test + public void testConstrStringExceptionEmptyExponent2() { + String a = "-238768.787678287e-"; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(String value) when exponent is greater than + * Integer.MAX_VALUE. + */ + @Test + public void testConstrStringExceptionExponentGreaterIntegerMax() { + String a = "-238768.787678287e214748364767876"; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(String value) when exponent is less than + * Integer.MIN_VALUE. + */ + @Test + public void testConstrStringExceptionExponentLessIntegerMin() { + String a = "-238768.787678287e-214748364767876"; + try { + new BigDecimal(a); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + } + } + + /** + * new BigDecimal(String value) + * when exponent is Integer.MAX_VALUE. + */ + @Test + public void testConstrStringExponentIntegerMax() { + String a = "-238768.787678287e2147483647"; + int aScale = -2147483638; + BigInteger bA = new BigInteger("-238768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value) + * when exponent is Integer.MIN_VALUE. + */ + @Test + public void testConstrStringExponentIntegerMin() { + String a = ".238768e-2147483648"; + try { + new BigDecimal(a); + fail("NumberFormatException expected"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message","Scale out of range.", + e.getMessage()); + } + } + + /** + * new BigDecimal(String value); value does not contain exponent + */ + @Test + public void testConstrStringWithoutExpPos1() { + String a = "732546982374982347892379283571094797.287346782359284756"; + int aScale = 18; + BigInteger bA = new BigInteger("732546982374982347892379283571094797287346782359284756"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value does not contain exponent + */ + @Test + public void testConstrStringWithoutExpPos2() { + String a = "+732546982374982347892379283571094797.287346782359284756"; + int aScale = 18; + BigInteger bA = new BigInteger("732546982374982347892379283571094797287346782359284756"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value does not contain exponent + */ + @Test + public void testConstrStringWithoutExpNeg() { + String a = "-732546982374982347892379283571094797.287346782359284756"; + int aScale = 18; + BigInteger bA = new BigInteger("-732546982374982347892379283571094797287346782359284756"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value does not contain exponent + * and decimal point + */ + @Test + public void testConstrStringWithoutExpWithoutPoint() { + String a = "-732546982374982347892379283571094797287346782359284756"; + int aScale = 0; + BigInteger bA = new BigInteger("-732546982374982347892379283571094797287346782359284756"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value contains exponent + * and does not contain decimal point + */ + @Test + public void testConstrStringWithExponentWithoutPoint1() { + String a = "-238768787678287e214"; + int aScale = -214; + BigInteger bA = new BigInteger("-238768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value contains exponent + * and does not contain decimal point + */ + @Test + public void testConstrStringWithExponentWithoutPoint2() { + String a = "-238768787678287e-214"; + int aScale = 214; + BigInteger bA = new BigInteger("-238768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value contains exponent + * and does not contain decimal point + */ + @Test + public void testConstrStringWithExponentWithoutPoint3() { + String a = "238768787678287e-214"; + int aScale = 214; + BigInteger bA = new BigInteger("238768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value contains exponent + * and does not contain decimal point + */ + @Test + public void testConstrStringWithExponentWithoutPoint4() { + String a = "238768787678287e+214"; + int aScale = -214; + BigInteger bA = new BigInteger("238768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); value contains exponent + * and does not contain decimal point + */ + @Test + public void testConstrStringWithExponentWithoutPoint5() { + String a = "238768787678287E214"; + int aScale = -214; + BigInteger bA = new BigInteger("238768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); + * value contains both exponent and decimal point + */ + @Test + public void testConstrStringWithExponentWithPoint1() { + String a = "23985439837984782435652424523876878.7678287e+214"; + int aScale = -207; + BigInteger bA = new BigInteger("239854398379847824356524245238768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); + * value contains both exponent and decimal point + */ + @Test + public void testConstrStringWithExponentWithPoint2() { + String a = "238096483923847545735673567457356356789029578490276878.7678287e-214"; + int aScale = 221; + BigInteger bA = new BigInteger("2380964839238475457356735674573563567890295784902768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); + * value contains both exponent and decimal point + */ + @Test + public void testConstrStringWithExponentWithPoint3() { + String a = "2380964839238475457356735674573563567890.295784902768787678287E+21"; + int aScale = 0; + BigInteger bA = new BigInteger("2380964839238475457356735674573563567890295784902768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); + * value contains both exponent and decimal point + */ + @Test + public void testConstrStringWithExponentWithPoint4() { + String a = "23809648392384754573567356745735635678.90295784902768787678287E+21"; + int aScale = 2; + BigInteger bA = new BigInteger("2380964839238475457356735674573563567890295784902768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value); + * value contains both exponent and decimal point + */ + @Test + public void testConstrStringWithExponentWithPoint5() { + String a = "238096483923847545735673567457356356789029.5784902768787678287E+21"; + int aScale = -2; + BigInteger bA = new BigInteger("2380964839238475457356735674573563567890295784902768787678287"); + BigDecimal aNumber = new BigDecimal(a); + assertEquals("incorrect value", bA, aNumber.unscaledValue()); + assertEquals("incorrect scale", aScale, aNumber.scale()); + } + + /** + * new BigDecimal(String value, MathContext) + */ + @Test + public void testConstrStringMathContext() { + String a = "-238768787678287e214"; + int precision = 5; + RoundingMode rm = RoundingMode.CEILING; + MathContext mc = new MathContext(precision, rm); + String res = "-23876"; + int resScale = -224; + BigDecimal result = new BigDecimal(a, mc); + assertEquals("incorrect value", res, result.unscaledValue().toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java new file mode 100644 index 000000000..8eb5a8f5f --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java @@ -0,0 +1,619 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigDecimal + * Methods: doubleValue, floatValue, intValue, longValue, + * valueOf, toString, toBigInteger + */ +public class BigDecimalConvertTest { + /** + * Double value of a negative BigDecimal + */ + @Test + public void testDoubleValueNeg() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + double result = -1.2380964839238476E53; + assertEquals("incorrect value", result, aNumber.doubleValue(), 0); + } + + /** + * Double value of a positive BigDecimal + */ + @Test + public void testDoubleValuePos() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + double result = 1.2380964839238476E53; + assertEquals("incorrect value", result, aNumber.doubleValue(), 0); + } + + /** + * Double value of a large positive BigDecimal + */ + @Test + public void testDoubleValuePosInfinity() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+400"; + BigDecimal aNumber = new BigDecimal(a); + double result = Double.POSITIVE_INFINITY; + assertEquals("incorrect value", result, aNumber.doubleValue(), 0); + } + + /** + * Double value of a large negative BigDecimal + */ + @Test + public void testDoubleValueNegInfinity() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+400"; + BigDecimal aNumber = new BigDecimal(a); + double result = Double.NEGATIVE_INFINITY; + assertEquals("incorrect value", result, aNumber.doubleValue(), 0); + } + + /** + * Double value of a small negative BigDecimal + */ + @Test + public void testDoubleValueMinusZero() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E-400"; + BigDecimal aNumber = new BigDecimal(a); + long minusZero = -9223372036854775808L; + double result = aNumber.doubleValue(); + assertTrue("incorrect value", Double.doubleToLongBits(result) == minusZero); + } + + /** + * Double value of a small positive BigDecimal + */ + @Test + public void testDoubleValuePlusZero() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E-400"; + BigDecimal aNumber = new BigDecimal(a); + long zero = 0; + double result = aNumber.doubleValue(); + assertTrue("incorrect value", Double.doubleToLongBits(result) == zero); + } + + /** + * Float value of a negative BigDecimal + */ + @Test + public void testFloatValueNeg() { + String a = "-1238096483923847.6356789029578E+21"; + BigDecimal aNumber = new BigDecimal(a); + float result = -1.2380965E36F; + assertTrue("incorrect value", aNumber.floatValue() == result); + } + + /** + * Float value of a positive BigDecimal + */ + @Test + public void testFloatValuePos() { + String a = "1238096483923847.6356789029578E+21"; + BigDecimal aNumber = new BigDecimal(a); + float result = 1.2380965E36F; + assertTrue("incorrect value", aNumber.floatValue() == result); + } + + /** + * Float value of a large positive BigDecimal + */ + @Test + public void testFloatValuePosInfinity() { + String a = "123809648373567356745735.6356789787678287E+200"; + BigDecimal aNumber = new BigDecimal(a); + float result = Float.POSITIVE_INFINITY; + assertTrue("incorrect value", aNumber.floatValue() == result); + } + + /** + * Float value of a large negative BigDecimal + */ + @Test + public void testFloatValueNegInfinity() { + String a = "-123809648392384755735.63567887678287E+200"; + BigDecimal aNumber = new BigDecimal(a); + float result = Float.NEGATIVE_INFINITY; + assertTrue("incorrect value", aNumber.floatValue() == result); + } + + /** + * Float value of a small negative BigDecimal + */ + @Test + public void testFloatValueMinusZero() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E-400"; + BigDecimal aNumber = new BigDecimal(a); + int minusZero = -2147483648; + float result = aNumber.floatValue(); + assertTrue("incorrect value", Float.floatToIntBits(result) == minusZero); + } + + /** + * Float value of a small positive BigDecimal + */ + @Test + public void testFloatValuePlusZero() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E-400"; + BigDecimal aNumber = new BigDecimal(a); + int zero = 0; + float result = aNumber.floatValue(); + assertTrue("incorrect value", Float.floatToIntBits(result) == zero); + } + + /** + * Integer value of a negative BigDecimal + */ + @Test + public void testIntValueNeg() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + int result = 218520473; + assertTrue("incorrect value", aNumber.intValue() == result); + } + + /** + * Integer value of a positive BigDecimal + */ + @Test + public void testIntValuePos() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + int result = -218520473; + assertTrue("incorrect value", aNumber.intValue() == result); + } + + /** + * Long value of a negative BigDecimal + */ + @Test + public void testLongValueNeg() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + long result = -1246043477766677607L; + assertTrue("incorrect value", aNumber.longValue() == result); + } + + /** + * Long value of a positive BigDecimal + */ + @Test + public void testLongValuePos() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigDecimal aNumber = new BigDecimal(a); + long result = 1246043477766677607L; + assertTrue("incorrect value", aNumber.longValue() == result); + } + + /** + * scaleByPowerOfTen(int n) + */ + @Test + public void testScaleByPowerOfTen1() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 13; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.scaleByPowerOfTen(10); + String res = "1231212478987482988429808779810457634781384756794.987"; + int resScale = 3; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * scaleByPowerOfTen(int n) + */ + @Test + public void testScaleByPowerOfTen2() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -13; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.scaleByPowerOfTen(10); + String res = "1.231212478987482988429808779810457634781384756794987E+74"; + int resScale = -23; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Convert a positive BigDecimal to BigInteger + */ + @Test + public void testToBigIntegerPos1() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigInteger bNumber = new BigInteger("123809648392384754573567356745735635678902957849027687"); + BigDecimal aNumber = new BigDecimal(a); + BigInteger result = aNumber.toBigInteger(); + assertTrue("incorrect value", result.equals(bNumber)); + } + + /** + * Convert a positive BigDecimal to BigInteger + */ + @Test + public void testToBigIntegerPos2() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+15"; + BigInteger bNumber = new BigInteger("123809648392384754573567356745735635678902957849"); + BigDecimal aNumber = new BigDecimal(a); + BigInteger result = aNumber.toBigInteger(); + assertTrue("incorrect value", result.equals(bNumber)); + } + + /** + * Convert a positive BigDecimal to BigInteger + */ + @Test + public void testToBigIntegerPos3() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+45"; + BigInteger bNumber = new BigInteger("123809648392384754573567356745735635678902957849027687876782870000000000000000"); + BigDecimal aNumber = new BigDecimal(a); + BigInteger result = aNumber.toBigInteger(); + assertTrue("incorrect value", result.equals(bNumber)); + } + + /** + * Convert a negative BigDecimal to BigInteger + */ + @Test + public void testToBigIntegerNeg1() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+21"; + BigInteger bNumber = new BigInteger("-123809648392384754573567356745735635678902957849027687"); + BigDecimal aNumber = new BigDecimal(a); + BigInteger result = aNumber.toBigInteger(); + assertTrue("incorrect value", result.equals(bNumber)); + } + + /** + * Convert a negative BigDecimal to BigInteger + */ + @Test + public void testToBigIntegerNeg2() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+15"; + BigInteger bNumber = new BigInteger("-123809648392384754573567356745735635678902957849"); + BigDecimal aNumber = new BigDecimal(a); + BigInteger result = aNumber.toBigInteger(); + assertTrue("incorrect value", result.equals(bNumber)); + } + + /** + * Convert a negative BigDecimal to BigInteger + */ + @Test + public void testToBigIntegerNeg3() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+45"; + BigInteger bNumber = new BigInteger("-123809648392384754573567356745735635678902957849027687876782870000000000000000"); + BigDecimal aNumber = new BigDecimal(a); + BigInteger result = aNumber.toBigInteger(); + assertTrue("incorrect value", result.equals(bNumber)); + } + + /** + * Convert a small BigDecimal to BigInteger + */ + @Test + public void testToBigIntegerZero() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E-500"; + BigInteger bNumber = new BigInteger("0"); + BigDecimal aNumber = new BigDecimal(a); + BigInteger result = aNumber.toBigInteger(); + assertTrue("incorrect value", result.equals(bNumber)); + } + + /** + * toBigIntegerExact() + */ + @Test + public void testToBigIntegerExact1() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E+45"; + BigDecimal aNumber = new BigDecimal(a); + String res = "-123809648392384754573567356745735635678902957849027687876782870000000000000000"; + BigInteger result = aNumber.toBigIntegerExact(); + assertEquals("incorrect value", res, result.toString()); + } + + /** + * toBigIntegerExact() + */ + @Test + public void testToBigIntegerExactException() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E-10"; + BigDecimal aNumber = new BigDecimal(a); + try { + aNumber.toBigIntegerExact(); + fail("java.lang.ArithmeticException has not been thrown"); + } catch (java.lang.ArithmeticException e) { + return; + } + } + + /** + * Convert a positive BigDecimal to an engineering string representation + */ + @Test + public void testToEngineeringStringPos() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E-501"; + BigDecimal aNumber = new BigDecimal(a); + String result = "123.80964839238475457356735674573563567890295784902768787678287E-471"; + assertEquals("incorrect value", result, aNumber.toEngineeringString()); + } + + /** + * Convert a negative BigDecimal to an engineering string representation + */ + @Test + public void testToEngineeringStringNeg() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E-501"; + BigDecimal aNumber = new BigDecimal(a); + String result = "-123.80964839238475457356735674573563567890295784902768787678287E-471"; + assertEquals("incorrect value", result, aNumber.toEngineeringString()); + } + + /** + * Convert a negative BigDecimal to an engineering string representation + */ + @Test + public void testToEngineeringStringZeroPosExponent() { + String a = "0.0E+16"; + BigDecimal aNumber = new BigDecimal(a); + String result = "0E+15"; + assertEquals("incorrect value", result, aNumber.toEngineeringString()); + } + + /** + * Convert a negative BigDecimal to an engineering string representation + */ + @Test + public void testToEngineeringStringZeroNegExponent() { + String a = "0.0E-16"; + BigDecimal aNumber = new BigDecimal(a); + String result = "0.00E-15"; + assertEquals("incorrect value", result, aNumber.toEngineeringString()); + } + + /** + * Convert a negative BigDecimal with a negative exponent to a plain string + * representation; scale == 0. + */ + @Test + public void testToPlainStringNegNegExp() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E-100"; + BigDecimal aNumber = new BigDecimal(a); + String result = "-0.000000000000000000000000000000000000000000000000000000000000000000012380964839238475457356735674573563567890295784902768787678287"; + assertTrue("incorrect value", aNumber.toPlainString().equals(result)); + } + + /** + * Convert a negative BigDecimal with a positive exponent + * to a plain string representation; + * scale == 0. + */ + @Test + public void testToPlainStringNegPosExp() { + String a = "-123809648392384754573567356745735.63567890295784902768787678287E100"; + BigDecimal aNumber = new BigDecimal(a); + String result = "-1238096483923847545735673567457356356789029578490276878767828700000000000000000000000000000000000000000000000000000000000000000000000"; + assertTrue("incorrect value", aNumber.toPlainString().equals(result)); + } + + /** + * Convert a positive BigDecimal with a negative exponent + * to a plain string representation; + * scale == 0. + */ + @Test + public void testToPlainStringPosNegExp() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E-100"; + BigDecimal aNumber = new BigDecimal(a); + String result = "0.000000000000000000000000000000000000000000000000000000000000000000012380964839238475457356735674573563567890295784902768787678287"; + assertTrue("incorrect value", aNumber.toPlainString().equals(result)); + } + + /** + * Convert a negative BigDecimal with a negative exponent + * to a plain string representation; + * scale == 0. + */ + @Test + public void testToPlainStringPosPosExp() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E+100"; + BigDecimal aNumber = new BigDecimal(a); + String result = "1238096483923847545735673567457356356789029578490276878767828700000000000000000000000000000000000000000000000000000000000000000000000"; + assertTrue("incorrect value", aNumber.toPlainString().equals(result)); + } + + /** + * Convert a BigDecimal to a string representation; + * scale == 0. + */ + @Test + public void testToStringZeroScale() { + String a = "-123809648392384754573567356745735635678902957849027687876782870"; + BigDecimal aNumber = new BigDecimal(new BigInteger(a)); + String result = "-123809648392384754573567356745735635678902957849027687876782870"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Convert a positive BigDecimal to a string representation + */ + @Test + public void testToStringPos() { + String a = "123809648392384754573567356745735.63567890295784902768787678287E-500"; + BigDecimal aNumber = new BigDecimal(a); + String result = "1.2380964839238475457356735674573563567890295784902768787678287E-468"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Convert a negative BigDecimal to a string representation + */ + @Test + public void testToStringNeg() { + String a = "-123.4564563673567380964839238475457356735674573563567890295784902768787678287E-5"; + BigDecimal aNumber = new BigDecimal(a); + String result = "-0.001234564563673567380964839238475457356735674573563567890295784902768787678287"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Create a BigDecimal from a positive long value; scale == 0 + */ + @Test + public void testValueOfPosZeroScale() { + long a = 98374823947823578L; + BigDecimal aNumber = BigDecimal.valueOf(a); + String result = "98374823947823578"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Create a BigDecimal from a negative long value; scale is 0 + */ + @Test + public void testValueOfNegZeroScale() { + long a = -98374823947823578L; + BigDecimal aNumber = BigDecimal.valueOf(a); + String result = "-98374823947823578"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Create a BigDecimal from a negative long value; scale is positive + */ + @Test + public void testValueOfNegScalePos() { + long a = -98374823947823578L; + int scale = 12; + BigDecimal aNumber = BigDecimal.valueOf(a, scale); + String result = "-98374.823947823578"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Create a BigDecimal from a negative long value; scale is negative + */ + @Test + public void testValueOfNegScaleNeg() { + long a = -98374823947823578L; + int scale = -12; + BigDecimal aNumber = BigDecimal.valueOf(a, scale); + String result = "-9.8374823947823578E+28"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Create a BigDecimal from a negative long value; scale is positive + */ + @Test + public void testValueOfPosScalePos() { + long a = 98374823947823578L; + int scale = 12; + BigDecimal aNumber = BigDecimal.valueOf(a, scale); + String result = "98374.823947823578"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Create a BigDecimal from a negative long value; scale is negative + */ + @Test + public void testValueOfPosScaleNeg() { + long a = 98374823947823578L; + int scale = -12; + BigDecimal aNumber = BigDecimal.valueOf(a, scale); + String result = "9.8374823947823578E+28"; + assertTrue("incorrect value", aNumber.toString().equals(result)); + } + + /** + * Create a BigDecimal from a negative double value + */ + @Test + public void testValueOfDoubleNeg() { + double a = -65678765876567576.98788767; + BigDecimal result = BigDecimal.valueOf(a); + String res = "-65678765876567576"; + int resScale = 0; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Create a BigDecimal from a positive double value + */ + @Test + public void testValueOfDoublePos1() { + double a = 65678765876567576.98788767; + BigDecimal result = BigDecimal.valueOf(a); + String res = "65678765876567576"; + int resScale = 0; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Create a BigDecimal from a positive double value + */ + @Test + public void testValueOfDoublePos2() { + double a = 12321237576.98788767; + BigDecimal result = BigDecimal.valueOf(a); + String res = "12321237576.987888"; + int resScale = 6; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Create a BigDecimal from a positive double value + */ + @Test + public void testValueOfDoublePos3() { + double a = 12321237576.9878838; + BigDecimal result = BigDecimal.valueOf(a); + String res = "12321237576.987885"; + int resScale = 6; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * valueOf(Double.NaN) + */ + @Test + public void testValueOfDoubleNaN() { + double a = Double.NaN; + try { + BigDecimal.valueOf(a); + fail("NumberFormatException has not been thrown for Double.NaN"); + } catch (NumberFormatException e) { + return; + } + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java new file mode 100644 index 000000000..fac6fa45a --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java @@ -0,0 +1,374 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.*; +import org.junit.Test; + +/** + * Class: java.math.BigDecimal + * Methods: movePointLeft, movePointRight, scale, setScale, unscaledValue * + */ +public class BigDecimalScaleOperationsTest { + /** + * Check the default scale + */ + @Test + public void testScaleDefault() { + String a = "1231212478987482988429808779810457634781384756794987"; + int cScale = 0; + BigDecimal aNumber = new BigDecimal(new BigInteger(a)); + assertTrue("incorrect scale", aNumber.scale() == cScale); + } + + /** + * Check a negative scale + */ + @Test + public void testScaleNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = -10; + int cScale = -10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertTrue("incorrect scale", aNumber.scale() == cScale); + } + + /** + * Check a positive scale + */ + @Test + public void testScalePos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 10; + int cScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertTrue("incorrect scale", aNumber.scale() == cScale); + } + + /** + * Check the zero scale + */ + @Test + public void testScaleZero() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 0; + int cScale = 0; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + assertTrue("incorrect scale", aNumber.scale() == cScale); + } + + /** + * Check the unscaled value + */ + @Test + public void testUnscaledValue() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 100; + BigInteger bNumber = new BigInteger(a); + BigDecimal aNumber = new BigDecimal(bNumber, aScale); + assertTrue("incorrect unscaled value", aNumber.unscaledValue().equals(bNumber)); + } + + /** + * Set a greater new scale + */ + @Test + public void testSetScaleGreater() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 18; + int newScale = 28; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertEquals("incorrect value", 0, bNumber.compareTo(aNumber)); + } + + /** + * Set a less new scale; this.scale == 8; newScale == 5. + */ + @Test + public void testSetScaleLess() { + String a = "2.345726458768760000E+10"; + int newScale = 5; + BigDecimal aNumber = new BigDecimal(a); + BigDecimal bNumber = aNumber.setScale(newScale); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertEquals("incorrect value", 0, bNumber.compareTo(aNumber)); + } + + /** + * Verify an exception when setting a new scale + */ + @Test + public void testSetScaleException() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + try { + aNumber.setScale(newScale); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Rounding necessary", e.getMessage()); + } + } + + /** + * Set the same new scale + */ + @Test + public void testSetScaleSame() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 18; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.equals(aNumber)); + } + + /** + * Set a new scale + */ + @Test + public void testSetScaleRoundUp() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478139"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_UP); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * Set a new scale + */ + @Test + public void testSetScaleRoundDown() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478138"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_DOWN); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * Set a new scale + */ + @Test + public void testSetScaleRoundCeiling() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478139"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_CEILING); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * Set a new scale + */ + @Test + public void testSetScaleRoundFloor() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478138"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_FLOOR); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * Set a new scale + */ + @Test + public void testSetScaleRoundHalfUp() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478138"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_HALF_UP); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * Set a new scale + */ + @Test + public void testSetScaleRoundHalfDown() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478138"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_HALF_DOWN); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * Set a new scale + */ + @Test + public void testSetScaleRoundHalfEven() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478138"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_HALF_EVEN); + assertTrue("incorrect scale", bNumber.scale() == newScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * SetScale(int, RoundingMode) + */ + @Test + public void testSetScaleIntRoundingMode() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 28; + int newScale = 18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal result = aNumber.setScale(newScale, RoundingMode.HALF_EVEN); + String res = "123121247898748298842980.877981045763478138"; + int resScale = 18; + assertEquals("incorrect value", res, result.toString()); + assertEquals("incorrect scale", resScale, result.scale()); + } + + /** + * Move the decimal point to the left; the shift value is positive + */ + @Test + public void testMovePointLeftPos() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 28; + int shift = 18; + int resScale = 46; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.movePointLeft(shift); + assertTrue("incorrect scale", bNumber.scale() == resScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a)); + } + + /** + * Move the decimal point to the left; the shift value is positive + */ + @Test + public void testMovePointLeftNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 28; + int shift = -18; + int resScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.movePointLeft(shift); + assertTrue("incorrect scale", bNumber.scale() == resScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a)); + } + + /** + * Move the decimal point to the right; the shift value is positive + */ + @Test + public void testMovePointRightPosGreater() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 28; + int shift = 18; + int resScale = 10; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.movePointRight(shift); + assertTrue("incorrect scale", bNumber.scale() == resScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a)); + } + + /** + * Move the decimal point to the right; the shift value is positive + */ + @Test + public void testMovePointRightPosLess() { + String a = "1231212478987482988429808779810457634781384756794987"; + String b = "123121247898748298842980877981045763478138475679498700"; + int aScale = 28; + int shift = 30; + int resScale = 0; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.movePointRight(shift); + assertTrue("incorrect scale", bNumber.scale() == resScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b)); + } + + /** + * Move the decimal point to the right; the shift value is positive + */ + @Test + public void testMovePointRightNeg() { + String a = "1231212478987482988429808779810457634781384756794987"; + int aScale = 28; + int shift = -18; + int resScale = 46; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + BigDecimal bNumber = aNumber.movePointRight(shift); + assertTrue("incorrect scale", bNumber.scale() == resScale); + assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a)); + } + + /** + * Move the decimal point to the right when the scale overflows + */ + @Test + public void testMovePointRightException() { + String a = "12312124789874829887348723648726347429808779810457634781384756794987"; + int aScale = Integer.MAX_VALUE; //2147483647 + int shift = -18; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + try { + aNumber.movePointRight(shift); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Underflow", e.getMessage()); + } + } + + /** + * precision() + */ + @Test + public void testPrecision() { + String a = "12312124789874829887348723648726347429808779810457634781384756794987"; + int aScale = 14; + BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale); + int prec = aNumber.precision(); + assertEquals(68, prec); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java new file mode 100644 index 000000000..fe864d27d --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java @@ -0,0 +1,519 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Method: add + */ +public class BigIntegerAddTest { + /** + * Add two positive numbers of the same length + */ + @Test + public void testCase1() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {11, 22, 33, 44, 55, 66, 77, 11, 22, 33}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two negative numbers of the same length + */ + @Test + public void testCase2() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-12, -23, -34, -45, -56, -67, -78, -12, -23, -33}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Add two numbers of the same length. + * The first one is positive and the second is negative. + * The first one is greater in absolute value. + */ + @Test + public void testCase3() { + byte aBytes[] = {3, 4, 5, 6, 7, 8, 9}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte rBytes[] = {2, 2, 2, 2, 2, 2, 2}; + int aSign = 1; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two numbers of the same length. + * The first one is negative and the second is positive. + * The first one is greater in absolute value. + */ + @Test + public void testCase4() { + byte aBytes[] = {3, 4, 5, 6, 7, 8, 9}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte rBytes[] = {-3, -3, -3, -3, -3, -3, -2}; + int aSign = -1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Add two numbers of the same length. + * The first is positive and the second is negative. + * The first is less in absolute value. + */ + @Test + public void testCase5() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {3, 4, 5, 6, 7, 8, 9}; + byte rBytes[] = {-3, -3, -3, -3, -3, -3, -2}; + int aSign = 1; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Add two numbers of the same length. + * The first one is negative and the second is positive. + * The first one is less in absolute value. + */ + @Test + public void testCase6() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {3, 4, 5, 6, 7, 8, 9}; + byte rBytes[] = {2, 2, 2, 2, 2, 2, 2}; + int aSign = -1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two positive numbers of different length. + * The first is longer. + */ + @Test + public void testCase7() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {1, 2, 3, 4, 15, 26, 37, 41, 52, 63, 74, 15, 26, 37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two positive numbers of different length. + * The second is longer. + */ + @Test + public void testCase8() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte rBytes[] = {1, 2, 3, 4, 15, 26, 37, 41, 52, 63, 74, 15, 26, 37}; + BigInteger aNumber = new BigInteger(aBytes); + BigInteger bNumber = new BigInteger(bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two negative numbers of different length. + * The first is longer. + */ + @Test + public void testCase9() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-2, -3, -4, -5, -16, -27, -38, -42, -53, -64, -75, -16, -27, -37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Add two negative numbers of different length. + * The second is longer. + */ + @Test + public void testCase10() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-2, -3, -4, -5, -16, -27, -38, -42, -53, -64, -75, -16, -27, -37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Add two numbers of different length and sign. + * The first is positive. + * The first is longer. + */ + @Test + public void testCase11() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {1, 2, 3, 3, -6, -15, -24, -40, -49, -58, -67, -6, -15, -23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two numbers of different length and sign. + * The first is positive. + * The second is longer. + */ + @Test + public void testCase12() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Add two numbers of different length and sign. + * The first is negative. + * The first is longer. + */ + @Test + public void testCase13() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Add two numbers of different length and sign. + * The first is negative. + * The second is longer. + */ + @Test + public void testCase14() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {1, 2, 3, 3, -6, -15, -24, -40, -49, -58, -67, -6, -15, -23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two equal numbers of different signs + */ + @Test + public void testCase15() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte rBytes[] = {0}; + int aSign = -1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Add zero to a number + */ + @Test + public void testCase16() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {0}; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add a number to zero + */ + @Test + public void testCase17() { + byte aBytes[] = {0}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add zero to zero + */ + @Test + public void testCase18() { + byte aBytes[] = {0}; + byte bBytes[] = {0}; + byte rBytes[] = {0}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Add ZERO to a number + */ + @Test + public void testCase19() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add a number to zero + */ + @Test + public void testCase20() { + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int bSign = 1; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add ZERO to ZERO + */ + @Test + public void testCase21() { + byte rBytes[] = {0}; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = BigInteger.ZERO; + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Add ONE to ONE + */ + @Test + public void testCase22() { + byte rBytes[] = {2}; + BigInteger aNumber = BigInteger.ONE; + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Add two numbers so that carry is 1 + */ + @Test + public void testCase23() { + byte aBytes[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + byte bBytes[] = {-1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {1, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.add(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java new file mode 100644 index 000000000..e09ef1973 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java @@ -0,0 +1,454 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Method: and + */ +public class BigIntegerAndTest { + /** + * And for zero and a positive number + */ + @Test + public void testZeroPos() { + byte aBytes[] = {0}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 0; + int bSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * And for zero and a negative number + */ + @Test + public void testZeroNeg() { + byte aBytes[] = {0}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 0; + int bSign = -1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * And for a positive number and zero + */ + @Test + public void testPosZero() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {0}; + int aSign = 1; + int bSign = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * And for a negative number and zero + */ + @Test + public void testNegPos() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {0}; + int aSign = -1; + int bSign = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * And for zero and zero + */ + @Test + public void testZeroZero() { + byte aBytes[] = {0}; + byte bBytes[] = {0}; + int aSign = 0; + int bSign = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * And for zero and one + */ + @Test + public void testZeroOne() { + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.and(bNumber); + assertTrue(result.equals(BigInteger.ZERO)); + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * And for one and one + */ + @Test + public void testOneOne() { + BigInteger aNumber = BigInteger.ONE; + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.and(bNumber); + assertTrue(result.equals(BigInteger.ONE)); + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for two positive numbers of the same length + */ + @Test + public void testPosPosSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -128, 56, 100, 4, 4, 17, 37, 16, 1, 64, 1, 10, 3}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for two positive numbers; the first is longer + */ + @Test + public void testPosPosFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -2, -76, 88, 44, 1, 2, 17, 35, 16, 9, 2, 5, 6, 21}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for two positive numbers; the first is shorter + */ + @Test + public void testPosPosFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -2, -76, 88, 44, 1, 2, 17, 35, 16, 9, 2, 5, 6, 21}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for two negative numbers of the same length + */ + @Test + public void testNegNegSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-1, 1, 2, 3, 3, 0, 65, -96, -48, -124, -60, 12, -40, -31, 97}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * And for two negative numbers; the first is longer + */ + @Test + public void testNegNegFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-1, 127, -10, -57, -101, 1, 2, 2, 2, -96, -16, 8, -40, -59, 68, -88, -88, 16, 73}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * And for two negative numbers; the first is shorter + */ + @Test + public void testNegNegFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-1, 127, -10, -57, -101, 1, 2, 2, 2, -96, -16, 8, -40, -59, 68, -88, -88, 16, 73}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * And for two numbers of different signs and the same length + */ + @Test + public void testPosNegSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {0, -6, -80, 72, 8, 75, 2, -79, 34, 16, -119}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for two numbers of different signs and the same length + */ + @Test + public void testNegPosSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {0, -2, 125, -60, -104, 1, 10, 6, 2, 32, 56, 2, 4, 4, 21}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for a negative and a positive numbers; the first is longer + */ + @Test + public void testNegPosFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {73, -92, -48, 4, 12, 6, 4, 32, 48, 64, 0, 8, 3}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for a negative and a positive numbers; the first is shorter + */ + @Test + public void testNegPosFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {0, -128, 9, 56, 100, 0, 0, 1, 1, 90, 1, -32, 0, 10, -126, 21, 82, -31, -95}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for a positive and a negative numbers; the first is longer + */ + @Test + public void testPosNegFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {0, -128, 9, 56, 100, 0, 0, 1, 1, 90, 1, -32, 0, 10, -126, 21, 82, -31, -95}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * And for a positive and a negative numbers; the first is shorter + */ + @Test + public void testPosNegFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {73, -92, -48, 4, 12, 6, 4, 32, 48, 64, 0, 8, 3}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Test for a special case + */ + @Test + public void testSpecialCase1() { + byte aBytes[] = {-1, -1, -1, -1}; + byte bBytes[] = {5, -4, -3, -2}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-1, 0, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Test for a special case + */ + @Test + public void testSpecialCase2() { + byte aBytes[] = {-51}; + byte bBytes[] = {-52, -51, -50, -49, -48}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {0, -52, -51, -50, -49, 16}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.and(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java new file mode 100644 index 000000000..ccd228a5d --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java @@ -0,0 +1,567 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Methods: abs, compareTo, equals, max, min, negate, signum + */ +public class BigIntegerCompareTest { + /** + * abs() for a positive number + */ + @Test + public void testAbsPositive() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.abs(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * abs() for a negative number + */ + @Test + public void testAbsNegative() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int aSign = -1; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.abs(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * compareTo(BigInteger a). + * Compare two positive numbers. + * The first is greater. + */ + @Test + public void testCompareToPosPos1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare two positive numbers. + * The first is less. + */ + @Test + public void testCompareToPosPos2() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(-1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare two equal positive numbers. + */ + @Test + public void testCompareToEqualPos() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(0, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare two negative numbers. + * The first is greater in absolute value. + */ + @Test + public void testCompareToNegNeg1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(-1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare two negative numbers. + * The first is less in absolute value. + */ + @Test + public void testCompareNegNeg2() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = -1; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare two equal negative numbers. + */ + @Test + public void testCompareToEqualNeg() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = -1; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(0, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare two numbers of different signs. + * The first is positive. + */ + @Test + public void testCompareToDiffSigns1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare two numbers of different signs. + * The first is negative. + */ + @Test + public void testCompareToDiffSigns2() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(-1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare a positive number to ZERO. + */ + @Test + public void testCompareToPosZero() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + assertEquals(1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare ZERO to a positive number. + */ + @Test + public void testCompareToZeroPos() { + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int bSign = 1; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(-1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare a negative number to ZERO. + */ + @Test + public void testCompareToNegZero() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + assertEquals(-1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare ZERO to a negative number. + */ + @Test + public void testCompareToZeroNeg() { + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int bSign = -1; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = new BigInteger(bSign, bBytes); + assertEquals(1, aNumber.compareTo(bNumber)); + } + + /** + * compareTo(BigInteger a). + * Compare ZERO to ZERO. + */ + @Test + public void testCompareToZeroZero() { + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = BigInteger.ZERO; + assertEquals(0, aNumber.compareTo(bNumber)); + } + + /** + * equals(Object obj). + * obj is not a BigInteger + */ + @Test + public void testEqualsObject() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + Object obj = new Object(); + assertFalse(aNumber.equals(obj)); + } + + /** + * equals(null). + */ + @Test + public void testEqualsNull() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertFalse(aNumber.equals(null)); + } + + /** + * equals(Object obj). + * obj is a BigInteger. + * numbers are equal. + */ + @Test + public void testEqualsBigIntegerTrue() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + Object bNumber = new BigInteger(bSign, bBytes); + assertTrue(aNumber.equals(bNumber)); + } + + /** + * equals(Object obj). + * obj is a BigInteger. + * numbers are not equal. + */ + @Test + public void testEqualsBigIntegerFalse() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + Object bNumber = new BigInteger(bSign, bBytes); + assertFalse(aNumber.equals(bNumber)); + } + + /** + * max(BigInteger val). + * the first is greater. + */ + @Test + public void testMaxGreater() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.max(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertTrue("incorrect sign", result.signum() == 1); + } + + /** + * max(BigInteger val). + * the first is less. + */ + @Test + public void testMaxLess() { + byte aBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.max(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertTrue("incorrect sign", result.signum() == 1); + } + + /** + * max(BigInteger val). + * numbers are equal. + */ + @Test + public void testMaxEqual() { + byte aBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.max(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * max(BigInteger val). + * max of negative and ZERO. + */ + @Test + public void testMaxNegZero() { + byte aBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = -1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + BigInteger result = aNumber.max(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertTrue("incorrect sign", result.signum() == 0); + } + + /** + * min(BigInteger val). + * the first is greater. + */ + @Test + public void testMinGreater() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.min(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * min(BigInteger val). + * the first is less. + */ + @Test + public void testMinLess() { + byte aBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.min(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * min(BigInteger val). + * numbers are equal. + */ + @Test + public void testMinEqual() { + byte aBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + byte bBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.min(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertTrue("incorrect sign", result.signum() == 1); + } + + /** + * max(BigInteger val). + * min of positive and ZERO. + */ + @Test + public void testMinPosZero() { + byte aBytes[] = {45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + BigInteger result = aNumber.min(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertTrue("incorrect sign", result.signum() == 0); + } + + /** + * negate() a positive number. + */ + @Test + public void testNegatePositive() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + byte rBytes[] = {-13, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -27, -4, -91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.negate(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertTrue("incorrect sign", result.signum() == -1); + } + + /** + * negate() a negative number. + */ + @Test + public void testNegateNegative() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = -1; + byte rBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.negate(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertTrue("incorrect sign", result.signum() == 1); + } + + /** + * negate() ZERO. + */ + @Test + public void testNegateZero() { + byte rBytes[] = {0}; + BigInteger aNumber = BigInteger.ZERO; + BigInteger result = aNumber.negate(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * signum() of a positive number. + */ + @Test + public void testSignumPositive() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * signum() of a negative number. + */ + @Test + public void testSignumNegative() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * signum() of ZERO. + */ + @Test + public void testSignumZero() { + BigInteger aNumber = BigInteger.ZERO; + assertEquals("incorrect sign", 0, aNumber.signum()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java new file mode 100644 index 000000000..f6e7145a9 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java @@ -0,0 +1,819 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import java.util.Random; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Constructors: BigInteger(byte[] a), BigInteger(int sign, byte[] a), + * BigInteger(String val, int radix) + */ +public class BigIntegerConstructorsTest { + /** + * Create a number from an array of bytes. + * Verify an exception thrown if an array is zero bytes long + */ + @Test + public void testConstructorBytesException() { + byte aBytes[] = {}; + try { + new BigInteger(aBytes); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message", "Zero length BigInteger", e.getMessage()); + } + } + + /** + * Create a positive number from an array of bytes. + * The number fits in an array of integers. + */ + @Test + public void testConstructorBytesPositive1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte rBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from an array of bytes. + * The number fits in an integer. + */ + @Test + public void testConstructorBytesPositive2() { + byte aBytes[] = {12, 56, 100}; + byte rBytes[] = {12, 56, 100}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from an array of bytes. + * The number of bytes is 4. + */ + @Test + public void testConstructorBytesPositive3() { + byte aBytes[] = {127, 56, 100, -1}; + byte rBytes[] = {127, 56, 100, -1}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from an array of bytes. + * The number of bytes is multiple of 4. + */ + @Test + public void testConstructorBytesPositive() { + byte aBytes[] = {127, 56, 100, -1, 14, 75, -24, -100}; + byte rBytes[] = {127, 56, 100, -1, 14, 75, -24, -100}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a negative number from an array of bytes. + * The number fits in an array of integers. + */ + @Test + public void testConstructorBytesNegative1() { + byte aBytes[] = {-12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + byte rBytes[] = {-12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from an array of bytes. + * The number fits in an integer. + */ + @Test + public void testConstructorBytesNegative2() { + byte aBytes[] = {-12, 56, 100}; + byte rBytes[] = {-12, 56, 100}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from an array of bytes. + * The number of bytes is 4. + */ + @Test + public void testConstructorBytesNegative3() { + byte aBytes[] = {-128, -12, 56, 100}; + byte rBytes[] = {-128, -12, 56, 100}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from an array of bytes. + * The number of bytes is multiple of 4. + */ + @Test + public void testConstructorBytesNegative4() { + byte aBytes[] = {-128, -12, 56, 100, -13, 56, 93, -78}; + byte rBytes[] = {-128, -12, 56, 100, -13, 56, 93, -78}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a zero number from an array of zero bytes. + */ + @Test + public void testConstructorBytesZero() { + byte aBytes[] = {0, 0, 0, -0, +0, 0, -0}; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a number from a sign and an array of bytes. + * Verify an exception thrown if a sign has improper value. + */ + @Test + public void testConstructorSignBytesException1() { + byte aBytes[] = {123, 45, -3, -76}; + int aSign = 3; + try { + new BigInteger(aSign, aBytes); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message", "Invalid signum value", e.getMessage()); + } + } + + /** + * Create a number from a sign and an array of bytes. + * Verify an exception thrown if the array contains non-zero bytes while the sign is 0. + */ + @Test + public void testConstructorSignBytesException2() { + byte aBytes[] = {123, 45, -3, -76}; + int aSign = 0; + try { + new BigInteger(aSign, aBytes); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message", "signum-magnitude mismatch", e.getMessage()); + } + } + + /** + * Create a positive number from a sign and an array of bytes. + * The number fits in an array of integers. + * The most significant byte is positive. + */ + @Test + public void testConstructorSignBytesPositive1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15}; + int aSign = 1; + byte rBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a sign and an array of bytes. + * The number fits in an array of integers. + * The most significant byte is negative. + */ + @Test + public void testConstructorSignBytesPositive2() { + byte aBytes[] = {-12, 56, 100, -2, -76, 89, 45, 91, 3, -15}; + int aSign = 1; + byte rBytes[] = {0, -12, 56, 100, -2, -76, 89, 45, 91, 3, -15}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a sign and an array of bytes. + * The number fits in an integer. + */ + @Test + public void testConstructorSignBytesPositive3() { + byte aBytes[] = {-12, 56, 100}; + int aSign = 1; + byte rBytes[] = {0, -12, 56, 100}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a sign and an array of bytes. + * The number of bytes is 4. + * The most significant byte is positive. + */ + @Test + public void testConstructorSignBytesPositive4() { + byte aBytes[] = {127, 56, 100, -2}; + int aSign = 1; + byte rBytes[] = {127, 56, 100, -2}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a sign and an array of bytes. + * The number of bytes is 4. + * The most significant byte is negative. + */ + @Test + public void testConstructorSignBytesPositive5() { + byte aBytes[] = {-127, 56, 100, -2}; + int aSign = 1; + byte rBytes[] = {0, -127, 56, 100, -2}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a sign and an array of bytes. + * The number of bytes is multiple of 4. + * The most significant byte is positive. + */ + @Test + public void testConstructorSignBytesPositive6() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 23, -101}; + int aSign = 1; + byte rBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 23, -101}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a sign and an array of bytes. + * The number of bytes is multiple of 4. + * The most significant byte is negative. + */ + @Test + public void testConstructorSignBytesPositive7() { + byte aBytes[] = {-12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 23, -101}; + int aSign = 1; + byte rBytes[] = {0, -12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 23, -101}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a negative number from a sign and an array of bytes. + * The number fits in an array of integers. + * The most significant byte is positive. + */ + @Test + public void testConstructorSignBytesNegative1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15}; + int aSign = -1; + byte rBytes[] = {-13, -57, -101, 1, 75, -90, -46, -92, -4, 15}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from a sign and an array of bytes. + * The number fits in an array of integers. + * The most significant byte is negative. + */ + @Test + public void testConstructorSignBytesNegative2() { + byte aBytes[] = {-12, 56, 100, -2, -76, 89, 45, 91, 3, -15}; + int aSign = -1; + byte rBytes[] = {-1, 11, -57, -101, 1, 75, -90, -46, -92, -4, 15}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from a sign and an array of bytes. + * The number fits in an integer. + */ + @Test + public void testConstructorSignBytesNegative3() { + byte aBytes[] = {-12, 56, 100}; + int aSign = -1; + byte rBytes[] = {-1, 11, -57, -100}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from a sign and an array of bytes. + * The number of bytes is 4. + * The most significant byte is positive. + */ + @Test + public void testConstructorSignBytesNegative4() { + byte aBytes[] = {127, 56, 100, -2}; + int aSign = -1; + byte rBytes[] = {-128, -57, -101, 2}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from a sign and an array of bytes. + * The number of bytes is 4. + * The most significant byte is negative. + */ + @Test + public void testConstructorSignBytesNegative5() { + byte aBytes[] = {-127, 56, 100, -2}; + int aSign = -1; + byte rBytes[] = {-1, 126, -57, -101, 2}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from a sign and an array of bytes. + * The number of bytes is multiple of 4. + * The most significant byte is positive. + */ + @Test + public void testConstructorSignBytesNegative6() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 23, -101}; + int aSign = -1; + byte rBytes[] = {-13, -57, -101, 1, 75, -90, -46, -92, -4, 14, -24, 101}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a negative number from a sign and an array of bytes. + * The number of bytes is multiple of 4. + * The most significant byte is negative. + */ + @Test + public void testConstructorSignBytesNegative7() { + byte aBytes[] = {-12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 23, -101}; + int aSign = -1; + byte rBytes[] = {-1, 11, -57, -101, 1, 75, -90, -46, -92, -4, 14, -24, 101}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a zero number from a sign and an array of zero bytes. + * The sign is -1. + */ + @Test + public void testConstructorSignBytesZero1() { + byte aBytes[] = {-0, 0, +0, 0, 0, 00, 000}; + int aSign = -1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a zero number from a sign and an array of zero bytes. + * The sign is 0. + */ + @Test + public void testConstructorSignBytesZero2() { + byte aBytes[] = {-0, 0, +0, 0, 0, 00, 000}; + int aSign = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a zero number from a sign and an array of zero bytes. + * The sign is 1. + */ + @Test + public void testConstructorSignBytesZero3() { + byte aBytes[] = {-0, 0, +0, 0, 0, 00, 000}; + int aSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a zero number from a sign and an array of zero length. + * The sign is -1. + */ + @Test + public void testConstructorSignBytesZeroNull1() { + byte aBytes[] = {}; + int aSign = -1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a zero number from a sign and an array of zero length. + * The sign is 0. + */ + @Test + public void testConstructorSignBytesZeroNull2() { + byte aBytes[] = {}; + int aSign = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a zero number from a sign and an array of zero length. + * The sign is 1. + */ + @Test + public void testConstructorSignBytesZeroNull3() { + byte aBytes[] = {}; + int aSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a number from a string value and radix. + * Verify an exception thrown if a radix is out of range + */ + @Test + public void testConstructorStringException1() { + String value = "9234853876401"; + int radix = 45; + try { + new BigInteger(value, radix); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + assertEquals("Improper exception message", "Radix out of range", e.getMessage()); + } + } + + /** + * Create a number from a string value and radix. + * Verify an exception thrown if the string starts with a space. + */ + @Test + public void testConstructorStringException2() { + String value = " 9234853876401"; + int radix = 10; + try { + new BigInteger(value, radix); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + } + } + + /** + * Create a number from a string value and radix. + * Verify an exception thrown if the string contains improper characters. + */ + @Test + public void testConstructorStringException3() { + String value = "92348$*#78987"; + int radix = 34; + try { + new BigInteger(value, radix); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + } + } + + /** + * Create a number from a string value and radix. + * Verify an exception thrown if some digits are greater than radix. + */ + @Test + public void testConstructorStringException4() { + String value = "98zv765hdsaiy"; + int radix = 20; + try { + new BigInteger(value, radix); + fail("NumberFormatException has not been caught"); + } catch (NumberFormatException e) { + } + } + + /** + * Create a positive number from a string value and radix 2. + */ + @Test + public void testConstructorStringRadix2() { + String value = "10101010101010101"; + int radix = 2; + byte rBytes[] = {1, 85, 85}; + BigInteger aNumber = new BigInteger(value, radix); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a string value and radix 8. + */ + @Test + public void testConstructorStringRadix8() { + String value = "76356237071623450"; + int radix = 8; + byte rBytes[] = {7, -50, -28, -8, -25, 39, 40}; + BigInteger aNumber = new BigInteger(value, radix); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a string value and radix 10. + */ + @Test + public void testConstructorStringRadix10() { + String value = "987328901348934898"; + int radix = 10; + byte rBytes[] = {13, -77, -78, 103, -103, 97, 68, -14}; + BigInteger aNumber = new BigInteger(value, radix); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a string value and radix 16. + */ + @Test + public void testConstructorStringRadix16() { + String value = "fe2340a8b5ce790"; + int radix = 16; + byte rBytes[] = {15, -30, 52, 10, -117, 92, -25, -112}; + BigInteger aNumber = new BigInteger(value, radix); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a positive number from a string value and radix 36. + */ + @Test + public void testConstructorStringRadix36() { + String value = "skdjgocvhdjfkl20jndjkf347ejg457"; + int radix = 36; + byte rBytes[] = {0, -12, -116, 112, -105, 12, -36, 66, 108, 66, -20, -37, -15, 108, -7, 52, -99, -109, -8, -45, -5}; + BigInteger aNumber = new BigInteger(value, radix); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * Create a negative number from a string value and radix 10. + */ + @Test + public void testConstructorStringRadix10Negative() { + String value = "-234871376037"; + int radix = 36; + byte rBytes[] = {-4, 48, 71, 62, -76, 93, -105, 13}; + BigInteger aNumber = new BigInteger(value, radix); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * Create a zero number from a string value and radix 36. + */ + @Test + public void testConstructorStringRadix10Zero() { + String value = "-00000000000000"; + int radix = 10; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(value, radix); + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } + + /** + * Create a random number of 75 bits length. + */ + @Test + public void testConstructorRandom() { + int bitLen = 75; + Random rnd = new Random(); + BigInteger aNumber = new BigInteger(bitLen, rnd); + assertTrue("incorrect bitLength", aNumber.bitLength() <= bitLen); + } + + /** + * Create a prime number of 25 bits length. + */ + @Test + public void testConstructorPrime() { + int bitLen = 25; + Random rnd = new Random(); + BigInteger aNumber = new BigInteger(bitLen, 80, rnd); + assertTrue("incorrect bitLength", aNumber.bitLength() == bitLen); + } + + /** + * Create a prime number of 2 bits length. + */ + @Test + public void testConstructorPrime2() { + int bitLen = 2; + Random rnd = new Random(); + BigInteger aNumber = new BigInteger(bitLen, 80, rnd); + assertTrue("incorrect bitLength", aNumber.bitLength() == bitLen); + int num = aNumber.intValue(); + assertTrue("incorrect value", num == 2 || num == 3); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java new file mode 100644 index 000000000..e97915651 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java @@ -0,0 +1,851 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Methods: intValue, longValue, toByteArray(), valueOf(long val), + * floatValue(), doubleValue() + */ +public class BigIntegerConvertTest { + /** + * Return the double value of ZERO. + */ + @Test + public void testDoubleValueZero() { + String a = "0"; + double result = 0.0; + double aNumber = new BigInteger(a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * The number's length is less than 64 bits. + */ + @Test + public void testDoubleValuePositive1() { + String a = "27467238945"; + double result = 2.7467238945E10; + double aNumber = new BigInteger(a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * The number's bit length is inside [63, 1024]. + */ + @Test + public void testDoubleValuePositive2() { + String a = "2746723894572364578265426346273456972"; + double result = 2.7467238945723645E36; + double aNumber = new BigInteger(a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a negative number to a double value. + * The number's bit length is less than 64 bits. + */ + @Test + public void testDoubleValueNegative1() { + String a = "-27467238945"; + double result = -2.7467238945E10; + double aNumber = new BigInteger(a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a negative number to a double value. + * The number's bit length is inside [63, 1024]. + */ + @Test + public void testDoubleValueNegative2() { + String a = "-2746723894572364578265426346273456972"; + double result = -2.7467238945723645E36; + double aNumber = new BigInteger(a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * Rounding is needed. + * The rounding bit is 1 and the next bit to the left is 1. + */ + @Test + public void testDoubleValuePosRounded1() { + byte[] a = {-128, 1, 2, 3, 4, 5, 60, 23, 1, -3, -5}; + int aSign = 1; + double result = 1.54747264387948E26; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * Rounding is needed. + * The rounding bit is 1 and the next bit to the left is 0 + * but some of dropped bits are 1s. + */ + @Test + public void testDoubleValuePosRounded2() { + byte[] a = {-128, 1, 2, 3, 4, 5, 36, 23, 1, -3, -5}; + int aSign = 1; + double result = 1.547472643879479E26; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == result); + } + /** + * Convert a positive number to a double value. + * Rounding is NOT needed. + */ + @Test + public void testDoubleValuePosNotRounded() { + byte[] a = {-128, 1, 2, 3, 4, 5, -128, 23, 1, -3, -5}; + int aSign = 1; + double result = 1.5474726438794828E26; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * Rounding is needed. + */ + @Test + public void testDoubleValueNegRounded1() { + byte[] a = {-128, 1, 2, 3, 4, 5, 60, 23, 1, -3, -5}; + int aSign = -1; + double result = -1.54747264387948E26; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * Rounding is needed. + * The rounding bit is 1 and the next bit to the left is 0 + * but some of dropped bits are 1s. + */ + @Test + public void testDoubleValueNegRounded2() { + byte[] a = {-128, 1, 2, 3, 4, 5, 36, 23, 1, -3, -5}; + int aSign = -1; + double result = -1.547472643879479E26; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * Rounding is NOT needed. + */ + @Test + public void testDoubleValueNegNotRounded() { + byte[] a = {-128, 1, 2, 3, 4, 5, -128, 23, 1, -3, -5}; + int aSign = -1; + double result = -1.5474726438794828E26; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * The exponent is 1023 and the mantissa is all 1s. + * The rounding bit is 0. + * The result is Double.MAX_VALUE. + */ + @Test + public void testDoubleValuePosMaxValue() { + byte[] a = {0, -1, -1, -1, -1, -1, -1, -8, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + int aSign = 1; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == Double.MAX_VALUE); + } + + /** + * Convert a negative number to a double value. + * The exponent is 1023 and the mantissa is all 1s. + * The result is -Double.MAX_VALUE. + */ + @Test + public void testDoubleValueNegMaxValue() { + byte[] a = {0, -1, -1, -1, -1, -1, -1, -8, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + int aSign = -1; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == -Double.MAX_VALUE); + } + + /** + * Convert a positive number to a double value. + * The exponent is 1023 and the mantissa is all 1s. + * The rounding bit is 1. + * The result is Double.POSITIVE_INFINITY. + */ + @Test + public void testDoubleValuePositiveInfinity1() { + byte[] a = {-1, -1, -1, -1, -1, -1, -1, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int aSign = 1; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == Double.POSITIVE_INFINITY); + } + + /** + * Convert a positive number to a double value. + * The number's bit length is greater than 1024. + */ + @Test + public void testDoubleValuePositiveInfinity2() { + String a = "2746723894572364578265426346273456972283746872364768676747462342342342342342342342323423423423423423426767456345745293762384756238475634563456845634568934568347586346578648576478568456457634875673845678456786587345873645767456834756745763457863485768475678465783456702897830296720476846578634576384567845678346573465786457863"; + double aNumber = new BigInteger(a).doubleValue(); + assertTrue(aNumber == Double.POSITIVE_INFINITY); + } + + /** + * Convert a negative number to a double value. + * The number's bit length is greater than 1024. + */ + @Test + public void testDoubleValueNegativeInfinity1() { + String a = "-2746723894572364578265426346273456972283746872364768676747462342342342342342342342323423423423423423426767456345745293762384756238475634563456845634568934568347586346578648576478568456457634875673845678456786587345873645767456834756745763457863485768475678465783456702897830296720476846578634576384567845678346573465786457863"; + double aNumber = new BigInteger(a).doubleValue(); + assertTrue(aNumber == Double.NEGATIVE_INFINITY); + } + + /** + * Convert a negative number to a double value. + * The exponent is 1023 and the mantissa is all 0s. + * The rounding bit is 0. + * The result is Double.NEGATIVE_INFINITY. + */ + @Test + public void testDoubleValueNegativeInfinity2() { + byte[] a = {-1, -1, -1, -1, -1, -1, -1, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int aSign = -1; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == Double.NEGATIVE_INFINITY); + } + + /** + * Convert a positive number to a double value. + * The exponent is 1023 and the mantissa is all 0s + * but the 54th bit (implicit) is 1. + */ + @Test + public void testDoubleValuePosMantissaIsZero() { + byte[] a = {-128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int aSign = 1; + double result = 8.98846567431158E307; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * The exponent is 1023 and the mantissa is all 0s + * but the 54th bit (implicit) is 1. + */ + @Test + public void testDoubleValueNegMantissaIsZero() { + byte[] a = {-128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int aSign = -1; + double aNumber = new BigInteger(aSign, a).doubleValue(); + assertTrue(aNumber == -8.98846567431158E307); + } + + /** + * Return the float value of ZERO. + */ + @Test + public void testFloatValueZero() { + String a = "0"; + float result = 0.0f; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * The number's length is less than 32 bits. + */ + @Test + public void testFloatValuePositive1() { + String a = "27467238"; + float result = 2.7467238E7f; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * The number's bit length is inside [32, 127]. + */ + @Test + public void testFloatValuePositive2() { + String a = "27467238945723645782"; + float result = 2.7467239E19f; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a negative number to a float value. + * The number's bit length is less than 32 bits. + */ + @Test + public void testFloatValueNegative1() { + String a = "-27467238"; + float result = -2.7467238E7f; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a negative number to a doufloatble value. + * The number's bit length is inside [63, 1024]. + */ + @Test + public void testFloatValueNegative2() { + String a = "-27467238945723645782"; + float result = -2.7467239E19f; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * Rounding is needed. + * The rounding bit is 1 and the next bit to the left is 1. + */ + @Test + public void testFloatValuePosRounded1() { + byte[] a = {-128, 1, -1, -4, 4, 5, 60, 23, 1, -3, -5}; + int aSign = 1; + float result = 1.5475195E26f; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * Rounding is needed. + * The rounding bit is 1 and the next bit to the left is 0 + * but some of dropped bits are 1s. + */ + @Test + public void testFloatValuePosRounded2() { + byte[] a = {-128, 1, 2, -128, 4, 5, 60, 23, 1, -3, -5}; + int aSign = 1; + float result = 1.5474728E26f; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == result); + } + /** + * Convert a positive number to a float value. + * Rounding is NOT needed. + */ + @Test + public void testFloatValuePosNotRounded() { + byte[] a = {-128, 1, 2, 3, 4, 5, 60, 23, 1, -3, -5}; + int aSign = 1; + float result = 1.5474726E26f; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * Rounding is needed. + */ + @Test + public void testFloatValueNegRounded1() { + byte[] a = {-128, 1, -1, -4, 4, 5, 60, 23, 1, -3, -5}; + int aSign = -1; + float result = -1.5475195E26f; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * Rounding is needed. + * The rounding bit is 1 and the next bit to the left is 0 + * but some of dropped bits are 1s. + */ + @Test + public void testFloatValueNegRounded2() { + byte[] a = {-128, 1, 2, -128, 4, 5, 60, 23, 1, -3, -5}; + int aSign = -1; + float result = -1.5474728E26f; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * Rounding is NOT needed. + */ + @Test + public void testFloatValueNegNotRounded() { + byte[] a = {-128, 1, 2, 3, 4, 5, 60, 23, 1, -3, -5}; + int aSign = -1; + float result = -1.5474726E26f; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a float value. + * The exponent is 1023 and the mantissa is all 1s. + * The rounding bit is 0. + * The result is Float.MAX_VALUE. + */ + @Test + public void testFloatValuePosMaxValue() { + byte[] a = {0, -1, -1, -1, 0, -1, -1, -8, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = 1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == Float.MAX_VALUE); + } + + /** + * Convert a negative number to a float value. + * The exponent is 1023 and the mantissa is all 1s. + * The rounding bit is 0. + * The result is -Float.MAX_VALUE. + */ + @Test + public void testFloatValueNegMaxValue() { + byte[] a = {0, -1, -1, -1, 0, -1, -1, -8, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = -1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == -Float.MAX_VALUE); + } + + /** + * Convert a positive number to a float value. + * The exponent is 1023 and the mantissa is all 1s. + * The rounding bit is 1. + * The result is Float.POSITIVE_INFINITY. + */ + @Test + public void testFloatValuePositiveInfinity1() { + byte[] a = {0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = 1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == Float.POSITIVE_INFINITY); + } + + /** + * Convert a positive number to a float value. + * The number's bit length is greater than 127. + */ + @Test + public void testFloatValuePositiveInfinity2() { + String a = "2746723894572364578265426346273456972283746872364768676747462342342342342342342342323423423423423423426767456345745293762384756238475634563456845634568934568347586346578648576478568456457634875673845678456786587345873645767456834756745763457863485768475678465783456702897830296720476846578634576384567845678346573465786457863"; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == Float.POSITIVE_INFINITY); + } + + /** + * Convert a negative number to a float value. + * The number's bit length is greater than 127. + */ + @Test + public void testFloatValueNegativeInfinity1() { + String a = "-2746723894572364578265426346273456972283746872364768676747462342342342342342342342323423423423423423426767456345745293762384756238475634563456845634568934568347586346578648576478568456457634875673845678456786587345873645767456834756745763457863485768475678465783456702897830296720476846578634576384567845678346573465786457863"; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == Float.NEGATIVE_INFINITY); + } + + /** + * Convert a negative number to a float value. + * The exponent is 1023 and the mantissa is all 0s. + * The rounding bit is 0. + * The result is Float.NEGATIVE_INFINITY. + */ + @Test + public void testFloatValueNegativeInfinity2() { + byte[] a = {0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = -1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == Float.NEGATIVE_INFINITY); + } + + /** + * Convert a positive number to a float value. + * The exponent is 1023 and the mantissa is all 0s + * but the 54th bit (implicit) is 1. + */ + @Test + public void testFloatValuePosMantissaIsZero() { + byte[] a = {-128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = 1; + float result = 1.7014118E38f; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive number to a double value. + * The exponent is 1023 and the mantissa is all 0s + * but the 54th bit (implicit) is 1. + */ + @Test + public void testFloatValueNegMantissaIsZero() { + byte[] a = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = -1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertTrue(aNumber == Float.NEGATIVE_INFINITY); + } + + /** + * Convert a negative number to a float value. + * The number's bit length is less than 32 bits. + */ + @Test + public void testFloatValueBug2482() { + String a = "2147483649"; + float result = 2.14748365E9f; + float aNumber = new BigInteger(a).floatValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a positive BigInteger to an integer value. + * The low digit is positive + */ + @Test + public void testIntValuePositive1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3}; + int resInt = 1496144643; + int aNumber = new BigInteger(aBytes).intValue(); + assertTrue(aNumber == resInt); + } + + /** + * Convert a positive BigInteger to an integer value. + * The low digit is positive + */ + @Test + public void testIntValuePositive2() { + byte aBytes[] = {12, 56, 100}; + int resInt = 800868; + int aNumber = new BigInteger(aBytes).intValue(); + assertTrue(aNumber == resInt); + } + + /** + * Convert a positive BigInteger to an integer value. + * The low digit is negative. + */ + @Test + public void testIntValuePositive3() { + byte aBytes[] = {56, 13, 78, -12, -5, 56, 100}; + int sign = 1; + int resInt = -184862620; + int aNumber = new BigInteger(sign, aBytes).intValue(); + assertTrue(aNumber == resInt); + } + + /** + * Convert a negative BigInteger to an integer value. + * The low digit is negative. + */ + @Test + public void testIntValueNegative1() { + byte aBytes[] = {12, 56, 100, -2, -76, -128, 45, 91, 3}; + int sign = -1; + int resInt = 2144511229; + int aNumber = new BigInteger(sign, aBytes).intValue(); + assertTrue(aNumber == resInt); + } + + /** + * Convert a negative BigInteger to an integer value. + * The low digit is negative. + */ + @Test + public void testIntValueNegative2() { + byte aBytes[] = {-12, 56, 100}; + int result = -771996; + int aNumber = new BigInteger(aBytes).intValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a negative BigInteger to an integer value. + * The low digit is positive. + */ + @Test + public void testIntValueNegative3() { + byte aBytes[] = {12, 56, 100, -2, -76, 127, 45, 91, 3}; + int sign = -1; + int resInt = -2133678851; + int aNumber = new BigInteger(sign, aBytes).intValue(); + assertTrue(aNumber == resInt); + } + + /** + * Convert a BigInteger to a positive long value + * The BigInteger is longer than int. + */ + @Test + public void testLongValuePositive1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, 120, -34, -12, 45, 98}; + long result = 3268209772258930018L; + long aNumber = new BigInteger(aBytes).longValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a number to a positive long value + * The number fits in a long. + */ + @Test + public void testLongValuePositive2() { + byte aBytes[] = {12, 56, 100, 18, -105, 34, -18, 45}; + long result = 880563758158769709L; + long aNumber = new BigInteger(aBytes).longValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a number to a negative long value + * The BigInteger is longer than int. + */ + @Test + public void testLongValueNegative1() { + byte aBytes[] = {12, -1, 100, -2, -76, -128, 45, 91, 3}; + long result = -43630045168837885L; + long aNumber = new BigInteger(aBytes).longValue(); + assertTrue(aNumber == result); + } + + /** + * Convert a number to a negative long value + * The number fits in a long. + */ + @Test + public void testLongValueNegative2() { + byte aBytes[] = {-12, 56, 100, 45, -101, 45, 98}; + long result = -3315696807498398L; + long aNumber = new BigInteger(aBytes).longValue(); + assertTrue(aNumber == result); + } + + /** + * valueOf (long val): convert Integer.MAX_VALUE to a BigInteger. + */ + @Test + public void testValueOfIntegerMax() { + long longVal = Integer.MAX_VALUE; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {127, -1, -1, -1}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * valueOf (long val): convert Integer.MIN_VALUE to a BigInteger. + */ + @Test + public void testValueOfIntegerMin() { + long longVal = Integer.MIN_VALUE; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {-128, 0, 0, 0}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * valueOf (long val): convert Long.MAX_VALUE to a BigInteger. + */ + @Test + public void testValueOfLongMax() { + long longVal = Long.MAX_VALUE; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {127, -1, -1, -1, -1, -1, -1, -1}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * valueOf (long val): convert Long.MIN_VALUE to a BigInteger. + */ + @Test + public void testValueOfLongMin() { + long longVal = Long.MIN_VALUE; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {-128, 0, 0, 0, 0, 0, 0, 0}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * valueOf (long val): convert a positive long value to a BigInteger. + */ + @Test + public void testValueOfLongPositive1() { + long longVal = 268209772258930018L; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {3, -72, -33, 93, -24, -56, 45, 98}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * valueOf (long val): convert a positive long value to a BigInteger. + * The long value fits in integer. + */ + @Test + public void testValueOfLongPositive2() { + long longVal = 58930018L; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {3, -125, 51, 98}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, aNumber.signum()); + } + + /** + * valueOf (long val): convert a negative long value to a BigInteger. + */ + @Test + public void testValueOfLongNegative1() { + long longVal = -268209772258930018L; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {-4, 71, 32, -94, 23, 55, -46, -98}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + + /** + * valueOf (long val): convert a negative long value to a BigInteger. + * The long value fits in integer. + */ + @Test + public void testValueOfLongNegative2() { + long longVal = -58930018L; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {-4, 124, -52, -98}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, aNumber.signum()); + } + /** + * valueOf (long val): convert a zero long value to a BigInteger. + */ + @Test + public void testValueOfLongZero() { + long longVal = 0L; + BigInteger aNumber = BigInteger.valueOf(longVal); + byte rBytes[] = {0}; + byte resBytes[] = new byte[rBytes.length]; + resBytes = aNumber.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, aNumber.signum()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java new file mode 100644 index 000000000..8ec471be7 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java @@ -0,0 +1,703 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Methods: divide, remainder, mod, and divideAndRemainder + */ +public class BigIntegerDivideTest { + /** + * Divide by zero + */ + @Test + public void testCase1() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {0}; + int aSign = 1; + int bSign = 0; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + try { + aNumber.divide(bNumber); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "BigInteger divide by zero", e.getMessage()); + } + } + + /** + * Divide by ZERO + */ + @Test + public void testCase2() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + try { + aNumber.divide(bNumber); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "BigInteger divide by zero", e.getMessage()); + } + } + + /** + * Divide two equal positive numbers + */ + @Test + public void testCase3() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127}; + byte bBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Divide two equal in absolute value numbers of different signs. + */ + @Test + public void testCase4() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127}; + byte bBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Divide two numbers of different length and different signs. + * The second is longer. + */ + @Test + public void testCase5() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127}; + byte bBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 1, 2, 3, 4, 5}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Divide two positive numbers of the same length. + * The second is greater. + */ + @Test + public void testCase6() { + byte aBytes[] = {1, 100, 56, 7, 98, -1, 39, -128, 127}; + byte bBytes[] = {15, 100, 56, 7, 98, -1, 39, -128, 127}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Divide two positive numbers. + */ + @Test + public void testCase7() { + byte aBytes[] = {1, 100, 56, 7, 98, -1, 39, -128, 127, 5, 6, 7, 8, 9}; + byte bBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {23, 115, 11, 78, 35, -11}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Divide a positive number by a negative one. + */ + @Test + public void testCase8() { + byte aBytes[] = {1, 100, 56, 7, 98, -1, 39, -128, 127, 5, 6, 7, 8, 9}; + byte bBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-24, -116, -12, -79, -36, 11}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Divide a negative number by a positive one. + */ + @Test + public void testCase9() { + byte aBytes[] = {1, 100, 56, 7, 98, -1, 39, -128, 127, 5, 6, 7, 8, 9}; + byte bBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-24, -116, -12, -79, -36, 11}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Divide two negative numbers. + */ + @Test + public void testCase10() { + byte aBytes[] = {1, 100, 56, 7, 98, -1, 39, -128, 127, 5, 6, 7, 8, 9}; + byte bBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {23, 115, 11, 78, 35, -11}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Divide zero by a negative number. + */ + @Test + public void testCase11() { + byte aBytes[] = {0}; + byte bBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + int aSign = 0; + int bSign = -1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Divide ZERO by a negative number. + */ + @Test + public void testCase12() { + byte bBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + int bSign = -1; + byte rBytes[] = {0}; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Divide a positive number by ONE. + */ + @Test + public void testCase13() { + byte aBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + int aSign = 1; + byte rBytes[] = {15, 48, -29, 7, 98, -1, 39, -128}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Divide ONE by ONE. + */ + @Test + public void testCase14() { + byte rBytes[] = {1}; + BigInteger aNumber = BigInteger.ONE; + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Verifies the case when borrow != 0 in the private divide method. + */ + @Test + public void testDivisionKnuth1() { + byte aBytes[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {-3, -3, -3, -3}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -5, -12, -33, -96, -36, -105, -56, 92, 15, 48, -109}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Verifies the case when the divisor is already normalized. + */ + @Test + public void testDivisionKnuthIsNormalized() { + byte aBytes[] = {-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5}; + byte bBytes[] = {-1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {0, -9, -8, -7, -6, -5, -4, -3}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Verifies the case when the first digits of the dividend + * and divisor equal. + */ + @Test + public void testDivisionKnuthFirstDigitsEqual() { + byte aBytes[] = {2, -3, -4, -5, -1, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5}; + byte bBytes[] = {2, -3, -4, -5, -1, -1, -1, -1}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {0, -1, -1, -1, -1, -2, -88, -60, 41}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Divide the number of one digit by the number of one digit + */ + @Test + public void testDivisionKnuthOneDigitByOneDigit() { + byte aBytes[] = {113, -83, 123, -5}; + byte bBytes[] = {2, -3, -4, -5}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Divide the number of multi digits by the number of one digit + */ + @Test + public void testDivisionKnuthMultiDigitsByOneDigit() { + byte aBytes[] = {113, -83, 123, -5, 18, -34, 67, 39, -29}; + byte bBytes[] = {2, -3, -4, -5}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-38, 2, 7, 30, 109, -43}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.divide(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Remainder of division by zero + */ + @Test + public void testCase15() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {0}; + int aSign = 1; + int bSign = 0; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + try { + aNumber.remainder(bNumber); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "BigInteger divide by zero", e.getMessage()); + } + } + + /** + * Remainder of division of equal numbers + */ + @Test + public void testCase16() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127}; + byte bBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Remainder of division of two positive numbers + */ + @Test + public void testCase17() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 75}; + byte bBytes[] = {27, -15, 65, 39, 100}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {12, -21, 73, 56, 27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Remainder of division of two negative numbers + */ + @Test + public void testCase18() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 75}; + byte bBytes[] = {27, -15, 65, 39, 100}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-13, 20, -74, -57, -27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Remainder of division of two numbers of different signs. + * The first is positive. + */ + @Test + public void testCase19() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 75}; + byte bBytes[] = {27, -15, 65, 39, 100}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {12, -21, 73, 56, 27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Remainder of division of two numbers of different signs. + * The first is negative. + */ + @Test + public void testCase20() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 75}; + byte bBytes[] = {27, -15, 65, 39, 100}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-13, 20, -74, -57, -27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Tests the step D6 from the Knuth algorithm + */ + @Test + public void testRemainderKnuth1() { + byte aBytes[] = {-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1}; + byte bBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7, 7, 18, -89}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Divide the number of one digit by the number of one digit + */ + @Test + public void testRemainderKnuthOneDigitByOneDigit() { + byte aBytes[] = {113, -83, 123, -5}; + byte bBytes[] = {2, -3, -4, -50}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {2, -9, -14, 53}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Divide the number of multi digits by the number of one digit + */ + @Test + public void testRemainderKnuthMultiDigitsByOneDigit() { + byte aBytes[] = {113, -83, 123, -5, 18, -34, 67, 39, -29}; + byte bBytes[] = {2, -3, -4, -50}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {2, -37, -60, 59}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.remainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * divideAndRemainder of two numbers of different signs. + * The first is negative. + */ + @Test + public void testCase21() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 75}; + byte bBytes[] = {27, -15, 65, 39, 100}; + int aSign = -1; + int bSign = 1; + byte rBytes[][] = { + {-5, 94, -115, -74, -85, 84}, + {-13, 20, -74, -57, -27} + }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result[] = aNumber.divideAndRemainder(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result[0].toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + if (resBytes[i] != rBytes[0][i]) { + fail("Incorrect quotation"); + } + } + assertEquals(-1, result[0].signum()); + resBytes = result[1].toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + if (resBytes[i] != rBytes[1][i]) { + fail("Incorrect remainder"); + } + assertEquals(-1, result[1].signum()); + } + } + + /** + * mod when modulus is negative + */ + @Test + public void testCase22() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {1, 30, 40, 56, -1, 45}; + int aSign = 1; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + try { + aNumber.mod(bNumber); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "BigInteger: modulus not positive", e.getMessage()); + } + } + + /** + * mod when a divisor is positive + */ + @Test + public void testCase23() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 75}; + byte bBytes[] = {27, -15, 65, 39, 100}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {12, -21, 73, 56, 27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.mod(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * mod when a divisor is negative + */ + @Test + public void testCase24() { + byte aBytes[] = {-127, 100, 56, 7, 98, -1, 39, -128, 127, 75}; + byte bBytes[] = {27, -15, 65, 39, 100}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {15, 5, -9, -17, 73}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.mod(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java new file mode 100644 index 000000000..e7d63542e --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java @@ -0,0 +1,83 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Method: hashCode() + */ +public class BigIntegerHashCodeTest { + /** + * Test hash codes for the same object + */ + @Test + public void testSameObject() { + String value1 = "12378246728727834290276457386374882976782849"; + String value2 = "-5634562095872038262928728727834290276457386374882976782849"; + BigInteger aNumber1 = new BigInteger(value1); + BigInteger aNumber2 = new BigInteger(value2); + int code1 = aNumber1.hashCode(); + aNumber1.add(aNumber2).shiftLeft(125); + aNumber1.subtract(aNumber2).shiftRight(125); + aNumber1.multiply(aNumber2).toByteArray(); + aNumber1.divide(aNumber2).bitLength(); + aNumber1.gcd(aNumber2).pow(7); + int code2 = aNumber1.hashCode(); + assertTrue("hash codes for the same object differ", code1 == code2); + } + + /** + * Test hash codes for equal objects. + */ + @Test + public void testEqualObjects() { + String value1 = "12378246728727834290276457386374882976782849"; + String value2 = "12378246728727834290276457386374882976782849"; + BigInteger aNumber1 = new BigInteger(value1); + BigInteger aNumber2 = new BigInteger(value2); + int code1 = aNumber1.hashCode(); + int code2 = aNumber2.hashCode(); + if (aNumber1.equals(aNumber2)) { + assertTrue("hash codes for equal objects are unequal", code1 == code2); + } + } + + /** + * Test hash codes for unequal objects. + * The codes are unequal. + */ + @Test + public void testUnequalObjectsUnequal() { + String value1 = "12378246728727834290276457386374882976782849"; + String value2 = "-5634562095872038262928728727834290276457386374882976782849"; + BigInteger aNumber1 = new BigInteger(value1); + BigInteger aNumber2 = new BigInteger(value2); + int code1 = aNumber1.hashCode(); + int code2 = aNumber2.hashCode(); + if (!aNumber1.equals(aNumber2)) { + assertTrue("hash codes for unequal objects are equal", code1 != code2); + } + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java new file mode 100644 index 000000000..caf43ceef --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java @@ -0,0 +1,368 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger Methods: modPow, modInverse, and gcd + */ +public class BigIntegerModPowTest { + /** + * modPow: non-positive modulus + */ + @Test + public void testModPowException() { + byte aBytes[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte eBytes[] = { 1, 2, 3, 4, 5 }; + byte mBytes[] = { 1, 2, 3 }; + int aSign = 1; + int eSign = 1; + int mSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger exp = new BigInteger(eSign, eBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + try { + aNumber.modPow(exp, modulus); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "BigInteger: modulus not positive", e.getMessage()); + } + + try { + BigInteger.ZERO.modPow(new BigInteger("-1"), new BigInteger("10")); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + // expected + } + } + + /** + * modPow: positive exponent + */ + @Test + public void testModPowPosExp() { + byte aBytes[] = { -127, 100, 56, 7, 98, -1, 39, -128, 127, 75, 48, -7 }; + byte eBytes[] = { 27, -15, 65, 39 }; + byte mBytes[] = { -128, 2, 3, 4, 5 }; + int aSign = 1; + int eSign = 1; + int mSign = 1; + byte rBytes[] = { 113, 100, -84, -28, -85 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger exp = new BigInteger(eSign, eBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + BigInteger result = aNumber.modPow(exp, modulus); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * modPow: negative exponent + */ + @Test + public void testModPowNegExp() { + byte aBytes[] = { -127, 100, 56, 7, 98, -1, 39, -128, 127, 75, 48, -7 }; + byte eBytes[] = { 27, -15, 65, 39 }; + byte mBytes[] = { -128, 2, 3, 4, 5 }; + int aSign = 1; + int eSign = -1; + int mSign = 1; + byte rBytes[] = { 12, 118, 46, 86, 92 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger exp = new BigInteger(eSign, eBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + BigInteger result = aNumber.modPow(exp, modulus); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + @Test + public void testModPowZeroExp() { + BigInteger exp = new BigInteger("0"); + BigInteger[] base = new BigInteger[] { new BigInteger("-1"), new BigInteger("0"), new BigInteger("1") }; + BigInteger[] mod = new BigInteger[] { new BigInteger("2"), new BigInteger("10"), new BigInteger("2147483648") }; + + for (int i = 0; i < base.length; ++i) { + for (int j = 0; j < mod.length; ++j) { + assertEquals(base[i] + " modePow(" + exp + ", " + mod[j] + ") should be " + BigInteger.ONE, + BigInteger.ONE, base[i].modPow(exp, mod[j])); + } + } + + mod = new BigInteger[] { new BigInteger("1") }; + for (int i = 0; i < base.length; ++i) { + for (int j = 0; j < mod.length; ++j) { + assertEquals(base[i] + " modePow(" + exp + ", " + mod[j] + ") should be " + BigInteger.ZERO, + BigInteger.ZERO, base[i].modPow(exp, mod[j])); + } + } + } + + /** + * modInverse: non-positive modulus + */ + @Test + public void testmodInverseException() { + byte aBytes[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte mBytes[] = { 1, 2, 3 }; + int aSign = 1; + int mSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + try { + aNumber.modInverse(modulus); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "BigInteger: modulus not positive", e.getMessage()); + } + } + + /** + * modInverse: non-invertible number + */ + @Test + public void testmodInverseNonInvertible() { + byte aBytes[] = { -15, 24, 123, 56, -11, -112, -34, -98, 8, 10, 12, 14, 25, 125, -15, 28, -127 }; + byte mBytes[] = { -12, 1, 0, 0, 0, 23, 44, 55, 66 }; + int aSign = 1; + int mSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + try { + aNumber.modInverse(modulus); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "BigInteger not invertible.", e.getMessage()); + } + } + + /** + * modInverse: positive number + */ + @Test + public void testmodInversePos1() { + byte aBytes[] = { 24, 123, 56, -11, -112, -34, -98, 8, 10, 12, 14, 25, 125, -15, 28, -127 }; + byte mBytes[] = { 122, 45, 36, 100, 122, 45 }; + int aSign = 1; + int mSign = 1; + byte rBytes[] = { 47, 3, 96, 62, 87, 19 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + BigInteger result = aNumber.modInverse(modulus); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * modInverse: positive number (another case: a < 0) + */ + @Test + public void testmodInversePos2() { + byte aBytes[] = { 15, 24, 123, 56, -11, -112, -34, -98, 8, 10, 12, 14, 25, 125, -15, 28, -127 }; + byte mBytes[] = { 2, 122, 45, 36, 100 }; + int aSign = 1; + int mSign = 1; + byte rBytes[] = { 1, -93, 40, 127, 73 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + BigInteger result = aNumber.modInverse(modulus); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * modInverse: negative number + */ + @Test + public void testmodInverseNeg1() { + byte aBytes[] = { 15, 24, 123, 56, -11, -112, -34, -98, 8, 10, 12, 14, 25, 125, -15, 28, -127 }; + byte mBytes[] = { 2, 122, 45, 36, 100 }; + int aSign = -1; + int mSign = 1; + byte rBytes[] = { 0, -41, 4, -91, 27 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger modulus = new BigInteger(mSign, mBytes); + BigInteger result = aNumber.modInverse(modulus); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * modInverse: negative number (another case: x < 0) + */ + @Test + public void testmodInverseNeg2() { + byte aBytes[] = { -15, 24, 123, 57, -15, 24, 123, 57, -15, 24, 123, 57 }; + byte mBytes[] = { 122, 2, 4, 122, 2, 4 }; + byte rBytes[] = { 85, 47, 127, 4, -128, 45 }; + BigInteger aNumber = new BigInteger(aBytes); + BigInteger modulus = new BigInteger(mBytes); + BigInteger result = aNumber.modInverse(modulus); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * gcd: the second number is zero + */ + @Test + public void testGcdSecondZero() { + byte aBytes[] = { 15, 24, 123, 57, -15, 24, 123, 57, -15, 24, 123, 57 }; + byte bBytes[] = { 0 }; + int aSign = 1; + int bSign = 1; + byte rBytes[] = { 15, 24, 123, 57, -15, 24, 123, 57, -15, 24, 123, 57 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.gcd(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * gcd: the first number is zero + */ + @Test + public void testGcdFirstZero() { + byte aBytes[] = { 0 }; + byte bBytes[] = { 15, 24, 123, 57, -15, 24, 123, 57, -15, 24, 123, 57 }; + int aSign = 1; + int bSign = 1; + byte rBytes[] = { 15, 24, 123, 57, -15, 24, 123, 57, -15, 24, 123, 57 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.gcd(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * gcd: the first number is ZERO + */ + @Test + public void testGcdFirstZERO() { + byte bBytes[] = { 15, 24, 123, 57, -15, 24, 123, 57, -15, 24, 123, 57 }; + int bSign = 1; + byte rBytes[] = { 15, 24, 123, 57, -15, 24, 123, 57, -15, 24, 123, 57 }; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.gcd(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * gcd: both numbers are zeros + */ + @Test + public void testGcdBothZeros() { + byte rBytes[] = { 0 }; + BigInteger aNumber = new BigInteger("0"); + BigInteger bNumber = BigInteger.valueOf(0L); + BigInteger result = aNumber.gcd(bNumber); + byte resBytes[] = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * gcd: the first number is longer + */ + @Test + public void testGcdFirstLonger() { + byte aBytes[] = { -15, 24, 123, 56, -11, -112, -34, -98, 8, 10, 12, 14, 25, 125, -15, 28, -127 }; + byte bBytes[] = { -12, 1, 0, 0, 0, 23, 44, 55, 66 }; + int aSign = 1; + int bSign = 1; + byte rBytes[] = { 7 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.gcd(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * gcd: the second number is longer + */ + @Test + public void testGcdSecondLonger() { + byte aBytes[] = { -12, 1, 0, 0, 0, 23, 44, 55, 66 }; + byte bBytes[] = { -15, 24, 123, 56, -11, -112, -34, -98, 8, 10, 12, 14, 25, 125, -15, 28, -127 }; + int aSign = 1; + int bSign = 1; + byte rBytes[] = { 7 }; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.gcd(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for (int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java new file mode 100644 index 000000000..bf2065a8a --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java @@ -0,0 +1,408 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Method: multiply + */ +public class BigIntegerMultiplyTest { + /** + * Multiply two negative numbers of the same length + */ + @Test + public void testCase1() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {10, 40, 100, -55, 96, 51, 76, 40, -45, 85, 105, 4, 28, -86, -117, -52, 100, 120, 90}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Multiply two numbers of the same length and different signs. + * The first is negative. + */ + @Test + public void testCase2() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-11, -41, -101, 54, -97, -52, -77, -41, 44, -86, -106, -5, -29, 85, 116, 51, -101, -121, -90}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Multiply two positive numbers of different length. + * The first is longer. + */ + @Test + public void testCase3() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {10, 40, 100, -55, 96, 51, 76, 40, -45, 85, 115, 44, -127, + 115, -21, -62, -15, 85, 64, -87, -2, -36, -36, -106}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Multiply two positive numbers of different length. + * The second is longer. + */ + @Test + public void testCase4() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {10, 40, 100, -55, 96, 51, 76, 40, -45, 85, 115, 44, -127, + 115, -21, -62, -15, 85, 64, -87, -2, -36, -36, -106}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Multiply two numbers of different length and different signs. + * The first is positive. + * The first is longer. + */ + @Test + public void testCase5() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-11, -41, -101, 54, -97, -52, -77, -41, 44, -86, -116, -45, 126, + -116, 20, 61, 14, -86, -65, 86, 1, 35, 35, 106}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Multiply two numbers of different length and different signs. + * The first is positive. + * The second is longer. + */ + @Test + public void testCase6() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-11, -41, -101, 54, -97, -52, -77, -41, 44, -86, -116, -45, 126, + -116, 20, 61, 14, -86, -65, 86, 1, 35, 35, 106}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Multiply a number by zero. + */ + @Test + public void testCase7() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + byte bBytes[] = {0}; + int aSign = 1; + int bSign = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Multiply a number by ZERO. + */ + @Test + public void testCase8() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + int aSign = 1; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Multiply a positive number by ONE. + */ + @Test + public void testCase9() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + int aSign = 1; + byte rBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Multiply a negative number by ONE. + */ + @Test + public void testCase10() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 4, 5}; + int aSign = -1; + byte rBytes[] = {-2, -3, -4, -5, -6, -7, -8, -2, -3, -4, -2, -3, -4, -5, -5}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Multiply two numbers of 4 bytes length. + */ + @Test + public void testIntbyInt1() { + byte aBytes[] = {10, 20, 30, 40}; + byte bBytes[] = {1, 2, 3, 4}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-11, -41, -101, 55, 5, 15, 96}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Multiply two numbers of 4 bytes length. + */ + @Test + public void testIntbyInt2() { + byte aBytes[] = {-1, -1, -1, -1}; + byte bBytes[] = {-1, -1, -1, -1}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -1, -1, -1, -2, 0, 0, 0, 1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.multiply(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Negative exponent. + */ + @Test + public void testPowException() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + int exp = -5; + BigInteger aNumber = new BigInteger(aSign, aBytes); + try { + aNumber.pow(exp); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Negative exponent", e.getMessage()); + } + } + + /** + * Exponentiation of a negative number to an odd exponent. + */ + @Test + public void testPowNegativeNumToOddExp() { + byte aBytes[] = {50, -26, 90, 69, 120, 32, 63, -103, -14, 35}; + int aSign = -1; + int exp = 5; + byte rBytes[] = {-21, -94, -42, -15, -127, 113, -50, -88, 115, -35, 3, + 59, -92, 111, -75, 103, -42, 41, 34, -114, 99, -32, 105, -59, 127, + 45, 108, 74, -93, 105, 33, 12, -5, -20, 17, -21, -119, -127, -115, + 27, -122, 26, -67, 109, -125, 16, 91, -70, 109}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.pow(exp); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Exponentiation of a negative number to an even exponent. + */ + @Test + public void testPowNegativeNumToEvenExp() { + byte aBytes[] = {50, -26, 90, 69, 120, 32, 63, -103, -14, 35}; + int aSign = -1; + int exp = 4; + byte rBytes[] = {102, 107, -122, -43, -52, -20, -27, 25, -9, 88, -13, + 75, 78, 81, -33, -77, 39, 27, -37, 106, 121, -73, 108, -47, -101, + 80, -25, 71, 13, 94, -7, -33, 1, -17, -65, -70, -61, -3, -47}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.pow(exp); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Exponentiation of a negative number to zero exponent. + */ + @Test + public void testPowNegativeNumToZeroExp() { + byte aBytes[] = {50, -26, 90, 69, 120, 32, 63, -103, -14, 35}; + int aSign = -1; + int exp = 0; + byte rBytes[] = {1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.pow(exp); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Exponentiation of a positive number. + */ + @Test + public void testPowPositiveNum() { + byte aBytes[] = {50, -26, 90, 69, 120, 32, 63, -103, -14, 35}; + int aSign = 1; + int exp = 5; + byte rBytes[] = {20, 93, 41, 14, 126, -114, 49, 87, -116, 34, -4, -60, + 91, -112, 74, -104, 41, -42, -35, 113, -100, 31, -106, 58, -128, + -46, -109, -75, 92, -106, -34, -13, 4, 19, -18, 20, 118, 126, 114, + -28, 121, -27, 66, -110, 124, -17, -92, 69, -109}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.pow(exp); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Exponentiation of a negative number to zero exponent. + */ + @Test + public void testPowPositiveNumToZeroExp() { + byte aBytes[] = {50, -26, 90, 69, 120, 32, 63, -103, -14, 35}; + int aSign = 1; + int exp = 0; + byte rBytes[] = {1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.pow(exp); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java new file mode 100644 index 000000000..f92e2ca8e --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java @@ -0,0 +1,201 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Methods: and, andNot + */ +public class BigIntegerNotTest { + /** + * andNot for two positive numbers; the first is longer + */ + @Test + public void testAndNotPosPosFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -128, 9, 56, 100, 0, 0, 1, 1, 90, 1, -32, 0, 10, -126, 21, 82, -31, -96}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.andNot(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * andNot for two positive numbers; the first is shorter + */ + @Test + public void testAndNotPosPosFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {73, -92, -48, 4, 12, 6, 4, 32, 48, 64, 0, 8, 2}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.andNot(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * andNot for two negative numbers; the first is longer + */ + @Test + public void testAndNotNegNegFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {73, -92, -48, 4, 12, 6, 4, 32, 48, 64, 0, 8, 2}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.andNot(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * andNot for a negative and a positive numbers; the first is longer + */ + @Test + public void testNegPosFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-1, 127, -10, -57, -101, 1, 2, 2, 2, -96, -16, 8, -40, -59, 68, -88, -88, 16, 72}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.andNot(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Not for ZERO + */ + @Test + public void testNotZero() { + byte rBytes[] = {-1}; + BigInteger aNumber = BigInteger.ZERO; + BigInteger result = aNumber.not(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Not for ONE + */ + @Test + public void testNotOne() { + byte rBytes[] = {-2}; + BigInteger aNumber = BigInteger.ONE; + BigInteger result = aNumber.not(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Not for a positive number + */ + @Test + public void testNotPos() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + int aSign = 1; + byte rBytes[] = {-1, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -27, 116}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.not(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Not for a negative number + */ + @Test + public void testNotNeg() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + int aSign = -1; + byte rBytes[] = {0, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -118}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.not(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Not for a negative number + */ + @Test + public void testNotSpecialCase() { + byte aBytes[] = {-1, -1, -1, -1}; + int aSign = 1; + byte rBytes[] = {-1, 0, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.not(); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } +} \ No newline at end of file diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java new file mode 100644 index 000000000..d09af25bf --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java @@ -0,0 +1,1465 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Methods: bitLength, shiftLeft, shiftRight, + * clearBit, flipBit, setBit, testBit + */ +public class BigIntegerOperateBitsTest { + /** + * bitCount() of zero. + */ + @Test + public void testBitCountZero() { + BigInteger aNumber = new BigInteger("0"); + assertEquals(0, aNumber.bitCount()); + } + + /** + * bitCount() of a negative number. + */ + @Test + public void testBitCountNeg() { + BigInteger aNumber = new BigInteger("-12378634756382937873487638746283767238657872368748726875"); + assertEquals(87, aNumber.bitCount()); + } + + /** + * bitCount() of a negative number. + */ + @Test + public void testBitCountPos() { + BigInteger aNumber = new BigInteger("12378634756343564757582937873487638746283767238657872368748726875"); + assertEquals(107, aNumber.bitCount()); + } + + /** + * bitLength() of zero. + */ + @Test + public void testBitLengthZero() { + BigInteger aNumber = new BigInteger("0"); + assertEquals(0, aNumber.bitLength()); + } + + /** + * bitLength() of a positive number. + */ + @Test + public void testBitLengthPositive1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals(108, aNumber.bitLength()); + } + + /** + * bitLength() of a positive number with the leftmost bit set + */ + @Test + public void testBitLengthPositive2() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals(96, aNumber.bitLength()); + } + + /** + * bitLength() of a positive number which is a power of 2 + */ + @Test + public void testBitLengthPositive3() { + byte aBytes[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals(81, aNumber.bitLength()); + } + + /** + * bitLength() of a negative number. + */ + @Test + public void testBitLengthNegative1() { + byte aBytes[] = {12, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, 3, 91}; + int aSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals(108, aNumber.bitLength()); + } + + /** + * bitLength() of a negative number with the leftmost bit set + */ + @Test + public void testBitLengthNegative2() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals(96, aNumber.bitLength()); + } + + /** + * bitLength() of a negative number which is a power of 2 + */ + @Test + public void testBitLengthNegative3() { + byte aBytes[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertEquals(80, aNumber.bitLength()); + } + + /** + * clearBit(int n) of a negative n + */ + @Test + public void testClearBitException() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = -7; + BigInteger aNumber = new BigInteger(aSign, aBytes); + try { + aNumber.clearBit(number); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Negative bit address", e.getMessage()); + } + } + + /** + * clearBit(int n) outside zero + */ + @Test + public void testClearBitZero() { + byte aBytes[] = {0}; + int aSign = 0; + int number = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * clearBit(int n) outside zero + */ + @Test + public void testClearBitZeroOutside1() { + byte aBytes[] = {0}; + int aSign = 0; + int number = 95; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * clearBit(int n) inside a negative number + */ + @Test + public void testClearBitNegativeInside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 15; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, 92, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * clearBit(int n) inside a negative number + */ + @Test + public void testClearBitNegativeInside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 44; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -62, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * clearBit(2) in the negative number with all ones in bit representation + */ + @Test + public void testClearBitNegativeInside3() { + String as = "-18446744073709551615"; + int number = 2; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.clearBit(number); + assertEquals(as, result.toString()); + } + + /** + * clearBit(0) in the negative number of length 1 + * with all ones in bit representation. + * the resulting number's length is 2. + */ + @Test + public void testClearBitNegativeInside4() { + String as = "-4294967295"; + String res = "-4294967296"; + int number = 0; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.clearBit(number); + assertEquals(res, result.toString()); + } + + /** + * clearBit(0) in the negative number of length 2 + * with all ones in bit representation. + * the resulting number's length is 3. + */ + @Test + public void testClearBitNegativeInside5() { + String as = "-18446744073709551615"; + String res = "-18446744073709551616"; + int number = 0; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.clearBit(number); + assertEquals(res, result.toString()); + } + + /** + * clearBit(int n) outside a negative number + */ + @Test + public void testClearBitNegativeOutside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 150; + byte rBytes[] = {-65, -1, -1, -1, -1, -1, -2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * clearBit(int n) outside a negative number + */ + @Test + public void testClearBitNegativeOutside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 165; + byte rBytes[] = {-33, -1, -1, -1, -1, -1, -1, -1, -2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * clearBit(int n) inside a positive number + */ + @Test + public void testClearBitPositiveInside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 20; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -31, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * clearBit(int n) inside a positive number + */ + @Test + public void testClearBitPositiveInside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 17; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * clearBit(int n) inside a positive number + */ + @Test + public void testClearBitPositiveInside3() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 45; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 13, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * clearBit(int n) inside a positive number + */ + @Test + public void testClearBitPositiveInside4 () { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 50; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * clearBit(int n) inside a positive number + */ + @Test + public void testClearBitPositiveInside5 () { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 63; + byte rBytes[] = {1, -128, 56, 100, -2, 52, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * clearBit(int n) outside a positive number + */ + @Test + public void testClearBitPositiveOutside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 150; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * clearBit(int n) outside a positive number + */ + @Test + public void testClearBitPositiveOutside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 191; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * clearBit(int n) the leftmost bit in a negative number + */ + @Test + public void testClearBitTopNegative() { + byte aBytes[] = {1, -128, 56, 100, -15, 35, 26}; + int aSign = -1; + int number = 63; + byte rBytes[] = {-1, 127, -2, 127, -57, -101, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.clearBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * flipBit(int n) of a negative n + */ + @Test + public void testFlipBitException() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = -7; + BigInteger aNumber = new BigInteger(aSign, aBytes); + try { + aNumber.flipBit(number); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Negative bit address", e.getMessage()); + } + } + + /** + * flipBit(int n) zero + */ + @Test + public void testFlipBitZero() { + byte aBytes[] = {0}; + int aSign = 0; + int number = 0; + byte rBytes[] = {1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * flipBit(int n) outside zero + */ + @Test + public void testFlipBitZeroOutside1() { + byte aBytes[] = {0}; + int aSign = 0; + int number = 62; + byte rBytes[] = {64, 0, 0, 0, 0, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue("incorrect value", resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * flipBit(int n) outside zero + */ + @Test + public void testFlipBitZeroOutside2() { + byte aBytes[] = {0}; + int aSign = 0; + int number = 63; + byte rBytes[] = {0, -128, 0, 0, 0, 0, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue("incorrect value", resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * flipBit(int n) the leftmost bit in a negative number + */ + @Test + public void testFlipBitLeftmostNegative() { + byte aBytes[] = {1, -128, 56, 100, -15, 35, 26}; + int aSign = -1; + int number = 48; + byte rBytes[] = {-1, 127, -57, -101, 14, -36, -26, 49}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * flipBit(int n) the leftmost bit in a positive number + */ + @Test + public void testFlipBitLeftmostPositive() { + byte aBytes[] = {1, -128, 56, 100, -15, 35, 26}; + int aSign = 1; + int number = 48; + byte rBytes[] = {0, -128, 56, 100, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * flipBit(int n) inside a negative number + */ + @Test + public void testFlipBitNegativeInside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 15; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, 92, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * flipBit(int n) inside a negative number + */ + @Test + public void testFlipBitNegativeInside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 45; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -14, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * flipBit(int n) inside a negative number with all ones in bit representation + */ + @Test + public void testFlipBitNegativeInside3() { + String as = "-18446744073709551615"; + String res = "-18446744073709551611"; + int number = 2; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.flipBit(number); + assertEquals(res, result.toString()); + } + + /** + * flipBit(0) in the negative number of length 1 + * with all ones in bit representation. + * the resulting number's length is 2. + */ + @Test + public void testFlipBitNegativeInside4() { + String as = "-4294967295"; + String res = "-4294967296"; + int number = 0; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.flipBit(number); + assertEquals(res, result.toString()); + } + + /** + * flipBit(0) in the negative number of length 2 + * with all ones in bit representation. + * the resulting number's length is 3. + */ + @Test + public void testFlipBitNegativeInside5() { + String as = "-18446744073709551615"; + String res = "-18446744073709551616"; + int number = 0; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.flipBit(number); + assertEquals(res, result.toString()); + } + + /** + * flipBit(int n) outside a negative number + */ + @Test + public void testFlipBitNegativeOutside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 150; + byte rBytes[] = {-65, -1, -1, -1, -1, -1, -2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * flipBit(int n) outside a negative number + */ + @Test + public void testFlipBitNegativeOutside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 191; + byte rBytes[] = {-1, 127, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * flipBit(int n) inside a positive number + */ + @Test + public void testFlipBitPositiveInside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 15; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, -93, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * flipBit(int n) inside a positive number + */ + @Test + public void testFlipBitPositiveInside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 45; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 13, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * flipBit(int n) outside a positive number + */ + @Test + public void testFlipBitPositiveOutside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 150; + byte rBytes[] = {64, 0, 0, 0, 0, 0, 1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * flipBit(int n) outside a positive number + */ + @Test + public void testFlipBitPositiveOutside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 191; + byte rBytes[] = {0, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.flipBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) of a negative n + */ + @Test + public void testSetBitException() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = -7; + BigInteger aNumber = new BigInteger(aSign, aBytes); + try { + aNumber.setBit(number); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Negative bit address", e.getMessage()); + } + } + + /** + * setBit(int n) outside zero + */ + @Test + public void testSetBitZero() { + byte aBytes[] = {0}; + int aSign = 0; + int number = 0; + byte rBytes[] = {1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) outside zero + */ + @Test + public void testSetBitZeroOutside1() { + byte aBytes[] = {0}; + int aSign = 0; + int number = 95; + byte rBytes[] = {0, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) inside a positive number + */ + @Test + public void testSetBitPositiveInside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 20; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) inside a positive number + */ + @Test + public void testSetBitPositiveInside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 17; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -13, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) inside a positive number + */ + @Test + public void testSetBitPositiveInside3() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 45; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) inside a positive number + */ + @Test + public void testSetBitPositiveInside4 () { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 50; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 93, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) outside a positive number + */ + @Test + public void testSetBitPositiveOutside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 150; + byte rBytes[] = {64, 0, 0, 0, 0, 0, 1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) outside a positive number + */ + @Test + public void testSetBitPositiveOutside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 223; + byte rBytes[] = {0, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) the leftmost bit in a positive number + */ + @Test + public void testSetBitTopPositive() { + byte aBytes[] = {1, -128, 56, 100, -15, 35, 26}; + int aSign = 1; + int number = 63; + byte rBytes[] = {0, -128, 1, -128, 56, 100, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * setBit(int n) the leftmost bit in a negative number + */ + @Test + public void testSetBitLeftmostNegative() { + byte aBytes[] = {1, -128, 56, 100, -15, 35, 26}; + int aSign = -1; + int number = 48; + byte rBytes[] = {-1, 127, -57, -101, 14, -36, -26, 49}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * setBit(int n) inside a negative number + */ + @Test + public void testSetBitNegativeInside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 15; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * setBit(int n) inside a negative number + */ + @Test + public void testSetBitNegativeInside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 44; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * setBit(int n) inside a negative number with all ones in bit representation + */ + @Test + public void testSetBitNegativeInside3() { + String as = "-18446744073709551615"; + String res = "-18446744073709551611"; + int number = 2; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.setBit(number); + assertEquals(res, result.toString()); + } + + /** + * setBit(0) in the negative number of length 1 + * with all ones in bit representation. + * the resulting number's length is 2. + */ + @Test + public void testSetBitNegativeInside4() { + String as = "-4294967295"; + int number = 0; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.setBit(number); + assertEquals(as, result.toString()); + } + + /** + * setBit(0) in the negative number of length 2 + * with all ones in bit representation. + * the resulting number's length is 3. + */ + @Test + public void testSetBitNegativeInside5() { + String as = "-18446744073709551615"; + int number = 0; + BigInteger aNumber = new BigInteger(as); + BigInteger result = aNumber.setBit(number); + assertEquals(as, result.toString()); + } + + /** + * setBit(int n) outside a negative number + */ + @Test + public void testSetBitNegativeOutside1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 150; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * setBit(int n) outside a negative number + */ + @Test + public void testSetBitNegativeOutside2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 191; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, -36, -26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.setBit(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * setBit: check the case when the number of bit to be set can be + * represented as n * 32 + 31, where n is an arbitrary integer. + * Here 191 = 5 * 32 + 31 + */ + @Test + public void testSetBitBug1331() { + BigInteger result = BigInteger.valueOf(0L).setBit(191); + assertEquals("incorrect value", "3138550867693340381917894711603833208051177722232017256448", result.toString()); + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftLeft(int n), n = 0 + */ + @Test + public void testShiftLeft1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 0; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftLeft(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftLeft(int n), n < 0 + */ + @Test + public void testShiftLeft2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = -27; + byte rBytes[] = {48, 7, 12, -97, -42, -117, 37, -85, 96}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftLeft(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftLeft(int n) a positive number, n > 0 + */ + @Test + public void testShiftLeft3() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 27; + byte rBytes[] = {12, 1, -61, 39, -11, -94, -55, 106, -40, 31, -119, 24, -48, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftLeft(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftLeft(int n) a positive number, n > 0 + */ + @Test + public void testShiftLeft4() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 45; + byte rBytes[] = {48, 7, 12, -97, -42, -117, 37, -85, 96, 126, 36, 99, 64, 0, 0, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftLeft(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftLeft(int n) a negative number, n > 0 + */ + @Test + public void testShiftLeft5() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 45; + byte rBytes[] = {-49, -8, -13, 96, 41, 116, -38, 84, -97, -127, -37, -100, -64, 0, 0, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftLeft(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * shiftRight(int n), n = 0 + */ + @Test + public void testShiftRight1() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 0; + byte rBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftRight(int n), n < 0 + */ + @Test + public void testShiftRight2() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = -27; + byte rBytes[] = {12, 1, -61, 39, -11, -94, -55, 106, -40, 31, -119, 24, -48, 0, 0, 0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftRight(int n), 0 < n < 32 + */ + @Test + public void testShiftRight3() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 27; + byte rBytes[] = {48, 7, 12, -97, -42, -117, 37, -85, 96}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftRight(int n), n > 32 + */ + @Test + public void testShiftRight4() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 45; + byte rBytes[] = {12, 1, -61, 39, -11, -94, -55}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * shiftRight(int n), n is greater than bitLength() + */ + @Test + public void testShiftRight5() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 300; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * shiftRight a negative number; + * shift distance is multiple of 32; + * shifted bits are NOT zeroes. + */ + @Test + public void testShiftRightNegNonZeroesMul32() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 1, 0, 0, 0, 0, 0, 0, 0}; + int aSign = -1; + int number = 64; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -92}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * shiftRight a negative number; + * shift distance is NOT multiple of 32; + * shifted bits are NOT zeroes. + */ + @Test + public void testShiftRightNegNonZeroes() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = -1; + int number = 68; + byte rBytes[] = {-25, -4, 121, -80, 20, -70, 109, 42}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * shiftRight a negative number; + * shift distance is NOT multiple of 32; + * shifted bits are zeroes. + */ + @Test + public void testShiftRightNegZeroes() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = -1; + int number = 68; + byte rBytes[] = {-25, -4, 121, -80, 20, -70, 109, 48}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * shiftRight a negative number; + * shift distance is multiple of 32; + * shifted bits are zeroes. + */ + @Test + public void testShiftRightNegZeroesMul32() { + byte aBytes[] = {1, -128, 56, 100, -2, -76, 89, 45, 91, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = -1; + int number = 64; + byte rBytes[] = {-2, 127, -57, -101, 1, 75, -90, -46, -91}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger result = aNumber.shiftRight(number); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * testBit(int n) of a negative n + */ + @Test + public void testTestBitException() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = -7; + BigInteger aNumber = new BigInteger(aSign, aBytes); + try { + aNumber.testBit(number); + fail("ArithmeticException has not been caught"); + } catch (ArithmeticException e) { + assertEquals("Improper exception message", "Negative bit address", e.getMessage()); + } + } + + /** + * testBit(int n) of a positive number + */ + @Test + public void testTestBitPositive1() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 7; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertTrue(!aNumber.testBit(number)); + } + + /** + * testBit(int n) of a positive number + */ + @Test + public void testTestBitPositive2() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 45; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertTrue(aNumber.testBit(number)); + } + + /** + * testBit(int n) of a positive number, n > bitLength() + */ + @Test + public void testTestBitPositive3() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = 1; + int number = 300; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertTrue(!aNumber.testBit(number)); + } + + /** + * testBit(int n) of a negative number + */ + @Test + public void testTestBitNegative1() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 7; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertTrue(aNumber.testBit(number)); + } + + /** + * testBit(int n) of a positive n + */ + @Test + public void testTestBitNegative2() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 45; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertTrue(!aNumber.testBit(number)); + } + + /** + * testBit(int n) of a positive n, n > bitLength() + */ + @Test + public void testTestBitNegative3() { + byte aBytes[] = {-1, -128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26}; + int aSign = -1; + int number = 300; + BigInteger aNumber = new BigInteger(aSign, aBytes); + assertTrue(aNumber.testBit(number)); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.java new file mode 100644 index 000000000..41ddc5d44 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Method: or + */ +public class BigIntegerOrTest { + /** + * Or for zero and a positive number + */ + @Test + public void testZeroPos() { + byte aBytes[] = {0}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 0; + int bSign = 1; + byte rBytes[] = {0, -2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Or for zero and a negative number + */ + @Test + public void testZeroNeg() { + byte aBytes[] = {0}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 0; + int bSign = -1; + byte rBytes[] = {-1, 1, 2, 3, 3, -6, -15, -24, -40, -49, -58, -67, -6, -15, -23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for a positive number and zero + */ + @Test + public void testPosZero() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {0}; + int aSign = 1; + int bSign = 0; + byte rBytes[] = {0, -2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Or for a negative number and zero + */ + @Test + public void testNegPos() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {0}; + int aSign = -1; + int bSign = 0; + byte rBytes[] = {-1, 1, 2, 3, 3, -6, -15, -24, -40, -49, -58, -67, -6, -15, -23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for zero and zero + */ + @Test + public void testZeroZero() { + byte aBytes[] = {0}; + byte bBytes[] = {0}; + int aSign = 0; + int bSign = 0; + byte rBytes[] = {0}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 0, result.signum()); + } + + /** + * Or for zero and one + */ + @Test + public void testZeroOne() { + byte aBytes[] = {0}; + byte bBytes[] = {1}; + int aSign = 0; + int bSign = 1; + byte rBytes[] = {1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Or for one and one + */ + @Test + public void testOneOne() { + byte aBytes[] = {1}; + byte bBytes[] = {1}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {1}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Or for two positive numbers of the same length + */ + @Test + public void testPosPosSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -2, -3, -4, -4, -1, -66, 95, 47, 123, 59, -13, 39, 30, -97}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Or for two positive numbers; the first is longer + */ + @Test + public void testPosPosFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -128, 9, 56, 100, -2, -3, -3, -3, 95, 15, -9, 39, 58, -69, 87, 87, -17, -73}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Or for two positive numbers; the first is shorter + */ + @Test + public void testPosPosFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {0, -128, 9, 56, 100, -2, -3, -3, -3, 95, 15, -9, 39, 58, -69, 87, 87, -17, -73}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", 1, result.signum()); + } + + /** + * Or for two negative numbers of the same length + */ + @Test + public void testNegNegSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-1, 127, -57, -101, -5, -5, -18, -38, -17, -2, -65, -2, -11, -3}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for two negative numbers; the first is longer + */ + @Test + public void testNegNegFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-1, 1, 75, -89, -45, -2, -3, -18, -36, -17, -10, -3, -6, -7, -21}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for two negative numbers; the first is shorter + */ + @Test + public void testNegNegFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-1, 1, 75, -89, -45, -2, -3, -18, -36, -17, -10, -3, -6, -7, -21}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for two numbers of different signs and the same length + */ + @Test + public void testPosNegSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-1, 1, -126, 59, 103, -2, -11, -7, -3, -33, -57, -3, -5, -5, -21}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for two numbers of different signs and the same length + */ + @Test + public void testNegPosSameLength() { + byte aBytes[] = {-128, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-1, 5, 79, -73, -9, -76, -3, 78, -35, -17, 119}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for a negative and a positive numbers; the first is longer + */ + @Test + public void testNegPosFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-1, 127, -10, -57, -101, -1, -1, -2, -2, -91, -2, 31, -1, -11, 125, -22, -83, 30, 95}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for two negative numbers; the first is shorter + */ + @Test + public void testNegPosFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-74, 91, 47, -5, -13, -7, -5, -33, -49, -65, -1, -9, -3}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for a positive and a negative numbers; the first is longer + */ + @Test + public void testPosNegFirstLonger() { + byte aBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + byte bBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-74, 91, 47, -5, -13, -7, -5, -33, -49, -65, -1, -9, -3}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + /** + * Or for a positive and a negative number; the first is shorter + */ + @Test + public void testPosNegFirstShorter() { + byte aBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + byte bBytes[] = {-128, 9, 56, 100, -2, -76, 89, 45, 91, 3, -15, 35, 26, -117, 23, 87, -25, -75}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {-1, 127, -10, -57, -101, -1, -1, -2, -2, -91, -2, 31, -1, -11, 125, -22, -83, 30, 95}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.or(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals("incorrect sign", -1, result.signum()); + } + + @Test + public void testRegression() { + // Regression test for HARMONY-1996 + BigInteger x = new BigInteger("-1023"); + BigInteger r1 = x.and((BigInteger.ZERO.not()).shiftLeft(32)); + BigInteger r3 = x.and((BigInteger.ZERO.not().shiftLeft(32) ).not()); + BigInteger result = r1.or(r3); + assertEquals(x, result); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java new file mode 100644 index 000000000..69c46374a --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java @@ -0,0 +1,573 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Method: subtract + */ +public class BigIntegerSubtractTest { + /** + * Subtract two positive numbers of the same length. + * The first is greater. + */ + @Test + public void testCase1() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {9, 18, 27, 36, 45, 54, 63, 9, 18, 27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two positive numbers of the same length. + * The second is greater. + */ + @Test + public void testCase2() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {-10, -19, -28, -37, -46, -55, -64, -10, -19, -27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } + + /** + * Subtract two numbers of the same length and different signs. + * The first is positive. + * The first is greater in absolute value. + */ + @Test + public void testCase3() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {11, 22, 33, 44, 55, 66, 77, 11, 22, 33}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two numbers of the same length and different signs. + * The first is positive. + * The second is greater in absolute value. + */ + @Test + public void testCase4() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {11, 22, 33, 44, 55, 66, 77, 11, 22, 33}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two negative numbers of the same length. + * The first is greater in absolute value. + */ + @Test + public void testCase5() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-10, -19, -28, -37, -46, -55, -64, -10, -19, -27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } + + /** + * Subtract two negative numbers of the same length. + * The second is greater in absolute value. + */ + @Test + public void testCase6() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {9, 18, 27, 36, 45, 54, 63, 9, 18, 27}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two numbers of the same length and different signs. + * The first is negative. + * The first is greater in absolute value. + */ + @Test + public void testCase7() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-12, -23, -34, -45, -56, -67, -78, -12, -23, -33}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } + + /** + * Subtract two numbers of the same length and different signs. + * The first is negative. + * The second is greater in absolute value. + */ + @Test + public void testCase8() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-12, -23, -34, -45, -56, -67, -78, -12, -23, -33}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } + + /** + * Subtract two positive numbers of different length. + * The first is longer. + */ + @Test + public void testCase9() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {1, 2, 3, 3, -6, -15, -24, -40, -49, -58, -67, -6, -15, -23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two positive numbers of different length. + * The second is longer. + */ + @Test + public void testCase10() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } + + /** + * Subtract two numbers of different length and different signs. + * The first is positive. + * The first is greater in absolute value. + */ + @Test + public void testCase11() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {1, 2, 3, 4, 15, 26, 37, 41, 52, 63, 74, 15, 26, 37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two numbers of the same length and different signs. + * The first is positive. + * The second is greater in absolute value. + */ + @Test + public void testCase12() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + int aSign = 1; + int bSign = -1; + byte rBytes[] = {1, 2, 3, 4, 15, 26, 37, 41, 52, 63, 74, 15, 26, 37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two numbers of different length and different signs. + * The first is negative. + * The first is longer. + */ + @Test + public void testCase13() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-2, -3, -4, -5, -16, -27, -38, -42, -53, -64, -75, -16, -27, -37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } + + /** + * Subtract two numbers of the same length and different signs. + * The first is negative. + * The second is longer. + */ + @Test + public void testCase14() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = 1; + byte rBytes[] = {-2, -3, -4, -5, -16, -27, -38, -42, -53, -64, -75, -16, -27, -37}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } + + /** + * Subtract two negative numbers of different length. + * The first is longer. + */ + @Test + public void testCase15() { + byte aBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + byte bBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {-2, -3, -4, -4, 5, 14, 23, 39, 48, 57, 66, 5, 14, 23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); +} + + /** + * Subtract two negative numbers of different length. + * The second is longer. + */ + @Test + public void testCase16() { + byte aBytes[] = {10, 20, 30, 40, 50, 60, 70, 10, 20, 30}; + byte bBytes[] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7}; + int aSign = -1; + int bSign = -1; + byte rBytes[] = {1, 2, 3, 3, -6, -15, -24, -40, -49, -58, -67, -6, -15, -23}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract two positive equal in absolute value numbers. + */ + @Test + public void testCase17() { + byte aBytes[] = {-120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + byte bBytes[] = {-120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + byte rBytes[] = {0}; + int aSign = 1; + int bSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(0, result.signum()); + } + + /** + * Subtract zero from a number. + * The number is positive. + */ + @Test + public void testCase18() { + byte aBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + byte bBytes[] = {0}; + byte rBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + int aSign = 1; + int bSign = 0; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract a number from zero. + * The number is negative. + */ + @Test + public void testCase19() { + byte aBytes[] = {0}; + byte bBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + byte rBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + int aSign = 0; + int bSign = -1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract zero from zero. + */ + @Test + public void testCase20() { + byte aBytes[] = {0}; + byte bBytes[] = {0}; + byte rBytes[] = {0}; + int aSign = 0; + int bSign = 0; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(0, result.signum()); + } + + /** + * Subtract ZERO from a number. + * The number is positive. + */ + @Test + public void testCase21() { + byte aBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + byte rBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + int aSign = 1; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = BigInteger.ZERO; + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract a number from ZERO. + * The number is negative. + */ + @Test + public void testCase22() { + byte bBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + byte rBytes[] = {120, 34, 78, -23, -111, 45, 127, 23, 45, -3}; + int bSign = -1; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(1, result.signum()); + } + + /** + * Subtract ZERO from ZERO. + */ + @Test + public void testCase23() { + byte rBytes[] = {0}; + BigInteger aNumber = BigInteger.ZERO; + BigInteger bNumber = BigInteger.ZERO; + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(0, result.signum()); + } + + /** + * Subtract ONE from ONE. + */ + @Test + public void testCase24() { + byte rBytes[] = {0}; + BigInteger aNumber = BigInteger.ONE; + BigInteger bNumber = BigInteger.ONE; + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(0, result.signum()); + } + + /** + * Subtract two numbers so that borrow is 1. + */ + @Test + public void testCase25() { + byte aBytes[] = {-1, -1, -1, -1, -1, -1, -1, -1}; + byte bBytes[] = {-128, -128, -128, -128, -128, -128, -128, -128, -128}; + int aSign = 1; + int bSign = 1; + byte rBytes[] = {-128, 127, 127, 127, 127, 127, 127, 127, 127}; + BigInteger aNumber = new BigInteger(aSign, aBytes); + BigInteger bNumber = new BigInteger(bSign, bBytes); + BigInteger result = aNumber.subtract(bNumber); + byte resBytes[] = new byte[rBytes.length]; + resBytes = result.toByteArray(); + for(int i = 0; i < resBytes.length; i++) { + assertTrue(resBytes[i] == rBytes[i]); + } + assertEquals(-1, result.signum()); + } +} + diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java new file mode 100644 index 000000000..1f8fd7181 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java @@ -0,0 +1,163 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import java.math.BigInteger; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Class: java.math.BigInteger + * Method: toString(int radix) + */ +public class BigIntegerToStringTest { + /** + * If 36 < radix < 2 it should be set to 10 + */ + @Test + public void testRadixOutOfRange() { + String value = "442429234853876401"; + int radix = 10; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(45); + assertTrue(result.equals(value)); + } + + /** + * test negative number of radix 2 + */ + @Test + public void testRadix2Neg() { + String value = "-101001100010010001001010101110000101010110001010010101010101010101010101010101010101010101010010101"; + int radix = 2; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test positive number of radix 2 + */ + @Test + public void testRadix2Pos() { + String value = "101000011111000000110101010101010101010001001010101010101010010101010101010000100010010"; + int radix = 2; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test negative number of radix 10 + */ + @Test + public void testRadix10Neg() { + String value = "-2489756308572364789878394872984"; + int radix = 16; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test positive number of radix 10 + */ + @Test + public void testRadix10Pos() { + String value = "2387627892347567398736473476"; + int radix = 16; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test negative number of radix 16 + */ + @Test + public void testRadix16Neg() { + String value = "-287628a883451b800865c67e8d7ff20"; + int radix = 16; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test positive number of radix 16 + */ + @Test + public void testRadix16Pos() { + String value = "287628a883451b800865c67e8d7ff20"; + int radix = 16; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test negative number of radix 24 + */ + @Test + public void testRadix24Neg() { + String value = "-287628a88gmn3451b8ijk00865c67e8d7ff20"; + int radix = 24; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test positive number of radix 24 + */ + @Test + public void testRadix24Pos() { + String value = "287628a883451bg80ijhk0865c67e8d7ff20"; + int radix = 24; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test negative number of radix 24 + */ + @Test + public void testRadix36Neg() { + String value = "-uhguweut98iu4h3478tq3985pq98yeiuth33485yq4aiuhalai485yiaehasdkr8tywi5uhslei8"; + int radix = 36; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } + + /** + * test positive number of radix 24 + */ + @Test + public void testRadix36Pos() { + String value = "23895lt45y6vhgliuwhgi45y845htsuerhsi4586ysuerhtsio5y68peruhgsil4568ypeorihtse48y6"; + int radix = 36; + BigInteger aNumber = new BigInteger(value, radix); + String result = aNumber.toString(radix); + assertTrue(result.equals(value)); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java new file mode 100644 index 000000000..ac7c61dcf --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java @@ -0,0 +1,297 @@ +/* + * 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. + */ +/** + * @author Elena Semukhina + */ + +package org.teavm.classlib.java.math; + +import static org.junit.Assert.*; +import java.math.BigInteger; +import org.junit.Test; + +/** + * Class: java.math.BigInteger + * Method: xor + */ +public class BigIntegerXorTest { + /** + * Xor for zero and a positive number + */ + @Test + public void testZeroPos() { + String numA = "0"; + String numB = "27384627835298756289327365"; + String res = "27384627835298756289327365"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for zero and a negative number + */ + @Test + public void testZeroNeg() { + String numA = "0"; + String numB = "-27384627835298756289327365"; + String res = "-27384627835298756289327365"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for a positive number and zero + */ + @Test + public void testPosZero() { + String numA = "27384627835298756289327365"; + String numB = "0"; + String res = "27384627835298756289327365"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for a negative number and zero + */ + @Test + public void testNegPos() { + String numA = "-27384627835298756289327365"; + String numB = "0"; + String res = "-27384627835298756289327365"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for zero and zero + */ + @Test + public void testZeroZero() { + String numA = "0"; + String numB = "0"; + String res = "0"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for zero and one + */ + @Test + public void testZeroOne() { + String numA = "0"; + String numB = "1"; + String res = "1"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for one and one + */ + @Test + public void testOneOne() { + String numA = "1"; + String numB = "1"; + String res = "0"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two positive numbers of the same length + */ + @Test + public void testPosPosSameLength() { + String numA = "283746278342837476784564875684767"; + String numB = "293478573489347658763745839457637"; + String res = "71412358434940908477702819237626"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two positive numbers; the first is longer + */ + @Test + public void testPosPosFirstLonger() { + String numA = "2837462783428374767845648748973847593874837948575684767"; + String numB = "293478573489347658763745839457637"; + String res = "2837462783428374767845615168483972194300564226167553530"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two positive numbers; the first is shorter + */ + @Test + public void testPosPosFirstShorter() { + String numA = "293478573489347658763745839457637"; + String numB = "2837462783428374767845648748973847593874837948575684767"; + String res = "2837462783428374767845615168483972194300564226167553530"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two negative numbers of the same length + */ + @Test + public void testNegNegSameLength() { + String numA = "-283746278342837476784564875684767"; + String numB = "-293478573489347658763745839457637"; + String res = "71412358434940908477702819237626"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two negative numbers; the first is longer + */ + @Test + public void testNegNegFirstLonger() { + String numA = "-2837462783428374767845648748973847593874837948575684767"; + String numB = "-293478573489347658763745839457637"; + String res = "2837462783428374767845615168483972194300564226167553530"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two negative numbers; the first is shorter + */ + @Test + public void testNegNegFirstShorter() { + String numA = "293478573489347658763745839457637"; + String numB = "2837462783428374767845648748973847593874837948575684767"; + String res = "2837462783428374767845615168483972194300564226167553530"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two numbers of different signs and the same length + */ + @Test + public void testPosNegSameLength() { + String numA = "283746278342837476784564875684767"; + String numB = "-293478573489347658763745839457637"; + String res = "-71412358434940908477702819237628"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two numbers of different signs and the same length + */ + @Test + public void testNegPosSameLength() { + String numA = "-283746278342837476784564875684767"; + String numB = "293478573489347658763745839457637"; + String res = "-71412358434940908477702819237628"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for a negative and a positive numbers; the first is longer + */ + @Test + public void testNegPosFirstLonger() { + String numA = "-2837462783428374767845648748973847593874837948575684767"; + String numB = "293478573489347658763745839457637"; + String res = "-2837462783428374767845615168483972194300564226167553532"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for two negative numbers; the first is shorter + */ + @Test + public void testNegPosFirstShorter() { + String numA = "-293478573489347658763745839457637"; + String numB = "2837462783428374767845648748973847593874837948575684767"; + String res = "-2837462783428374767845615168483972194300564226167553532"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for a positive and a negative numbers; the first is longer + */ + @Test + public void testPosNegFirstLonger() { + String numA = "2837462783428374767845648748973847593874837948575684767"; + String numB = "-293478573489347658763745839457637"; + String res = "-2837462783428374767845615168483972194300564226167553532"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } + + /** + * Xor for a positive and a negative number; the first is shorter + */ + @Test + public void testPosNegFirstShorter() { + String numA = "293478573489347658763745839457637"; + String numB = "-2837462783428374767845648748973847593874837948575684767"; + String res = "-2837462783428374767845615168483972194300564226167553532"; + BigInteger aNumber = new BigInteger(numA); + BigInteger bNumber = new BigInteger(numB); + BigInteger result = aNumber.xor(bNumber); + assertTrue(res.equals(result.toString())); + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java index 0b80ebbe6..6d71fb628 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -62,7 +62,7 @@ class ResourceProgramTransformer { return Arrays.asList(accessInsn); } ClassReader iface = innerSource.get(method.getClassName()); - if (!isSubclass(iface, Resource.class.getName())) { + if (iface == null || !isSubclass(iface, Resource.class.getName())) { return null; } if (method.getName().startsWith("get")) {