| /* |
| * Copyright (C) 2011 The Guava Authors |
| * |
| * 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 com.google.common.math; |
| |
| import static com.google.common.math.MathTesting.ALL_BIGINTEGER_CANDIDATES; |
| import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES; |
| import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES; |
| import static com.google.common.math.MathTesting.NEGATIVE_BIGINTEGER_CANDIDATES; |
| import static com.google.common.math.MathTesting.NONZERO_BIGINTEGER_CANDIDATES; |
| import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES; |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| import static java.math.BigInteger.ONE; |
| import static java.math.BigInteger.TEN; |
| import static java.math.BigInteger.ZERO; |
| import static java.math.RoundingMode.CEILING; |
| import static java.math.RoundingMode.DOWN; |
| import static java.math.RoundingMode.FLOOR; |
| import static java.math.RoundingMode.HALF_DOWN; |
| import static java.math.RoundingMode.HALF_EVEN; |
| import static java.math.RoundingMode.HALF_UP; |
| import static java.math.RoundingMode.UNNECESSARY; |
| import static java.math.RoundingMode.UP; |
| import static java.math.RoundingMode.values; |
| import static java.util.Arrays.asList; |
| |
| import com.google.common.annotations.GwtCompatible; |
| import com.google.common.annotations.GwtIncompatible; |
| import com.google.common.testing.NullPointerTester; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.math.RoundingMode; |
| import java.util.EnumMap; |
| import java.util.EnumSet; |
| import java.util.Map; |
| import junit.framework.TestCase; |
| |
| /** |
| * Tests for BigIntegerMath. |
| * |
| * @author Louis Wasserman |
| */ |
| @GwtCompatible(emulated = true) |
| public class BigIntegerMathTest extends TestCase { |
| public void testCeilingPowerOfTwo() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| BigInteger result = BigIntegerMath.ceilingPowerOfTwo(x); |
| assertTrue(BigIntegerMath.isPowerOfTwo(result)); |
| assertTrue(result.compareTo(x) >= 0); |
| assertTrue(result.compareTo(x.add(x)) < 0); |
| } |
| } |
| |
| public void testFloorPowerOfTwo() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| BigInteger result = BigIntegerMath.floorPowerOfTwo(x); |
| assertTrue(BigIntegerMath.isPowerOfTwo(result)); |
| assertTrue(result.compareTo(x) <= 0); |
| assertTrue(result.add(result).compareTo(x) > 0); |
| } |
| } |
| |
| public void testCeilingPowerOfTwoNegative() { |
| for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { |
| try { |
| BigIntegerMath.ceilingPowerOfTwo(x); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| public void testFloorPowerOfTwoNegative() { |
| for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { |
| try { |
| BigIntegerMath.floorPowerOfTwo(x); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| public void testCeilingPowerOfTwoZero() { |
| try { |
| BigIntegerMath.ceilingPowerOfTwo(BigInteger.ZERO); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public void testFloorPowerOfTwoZero() { |
| try { |
| BigIntegerMath.floorPowerOfTwo(BigInteger.ZERO); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testConstantSqrt2PrecomputedBits() { |
| assertEquals( |
| BigIntegerMath.sqrt( |
| BigInteger.ZERO.setBit(2 * BigIntegerMath.SQRT2_PRECOMPUTE_THRESHOLD + 1), FLOOR), |
| BigIntegerMath.SQRT2_PRECOMPUTED_BITS); |
| } |
| |
| public void testIsPowerOfTwo() { |
| for (BigInteger x : ALL_BIGINTEGER_CANDIDATES) { |
| // Checks for a single bit set. |
| boolean expected = x.signum() > 0 & x.and(x.subtract(ONE)).equals(ZERO); |
| assertEquals(expected, BigIntegerMath.isPowerOfTwo(x)); |
| } |
| } |
| |
| public void testLog2ZeroAlwaysThrows() { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| try { |
| BigIntegerMath.log2(ZERO, mode); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| public void testLog2NegativeAlwaysThrows() { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| try { |
| BigIntegerMath.log2(BigInteger.valueOf(-1), mode); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| public void testLog2Floor() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : asList(FLOOR, DOWN)) { |
| int result = BigIntegerMath.log2(x, mode); |
| assertTrue(ZERO.setBit(result).compareTo(x) <= 0); |
| assertTrue(ZERO.setBit(result + 1).compareTo(x) > 0); |
| } |
| } |
| } |
| |
| public void testLog2Ceiling() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : asList(CEILING, UP)) { |
| int result = BigIntegerMath.log2(x, mode); |
| assertTrue(ZERO.setBit(result).compareTo(x) >= 0); |
| assertTrue(result == 0 || ZERO.setBit(result - 1).compareTo(x) < 0); |
| } |
| } |
| } |
| |
| // Relies on the correctness of isPowerOfTwo(BigInteger). |
| public void testLog2Exact() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| // We only expect an exception if x was not a power of 2. |
| boolean isPowerOf2 = BigIntegerMath.isPowerOfTwo(x); |
| try { |
| assertEquals(x, ZERO.setBit(BigIntegerMath.log2(x, UNNECESSARY))); |
| assertTrue(isPowerOf2); |
| } catch (ArithmeticException e) { |
| assertFalse(isPowerOf2); |
| } |
| } |
| } |
| |
| public void testLog2HalfUp() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| int result = BigIntegerMath.log2(x, HALF_UP); |
| BigInteger x2 = x.pow(2); |
| // x^2 < 2^(2 * result + 1), or else we would have rounded up |
| assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) > 0); |
| // x^2 >= 2^(2 * result - 1), or else we would have rounded down |
| assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) <= 0); |
| } |
| } |
| |
| public void testLog2HalfDown() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| int result = BigIntegerMath.log2(x, HALF_DOWN); |
| BigInteger x2 = x.pow(2); |
| // x^2 <= 2^(2 * result + 1), or else we would have rounded up |
| assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) >= 0); |
| // x^2 > 2^(2 * result - 1), or else we would have rounded down |
| assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) < 0); |
| } |
| } |
| |
| // Relies on the correctness of log2(BigInteger, {HALF_UP,HALF_DOWN}). |
| public void testLog2HalfEven() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| int halfEven = BigIntegerMath.log2(x, HALF_EVEN); |
| // Now figure out what rounding mode we should behave like (it depends if FLOOR was |
| // odd/even). |
| boolean floorWasEven = (BigIntegerMath.log2(x, FLOOR) & 1) == 0; |
| assertEquals(BigIntegerMath.log2(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testLog10ZeroAlwaysThrows() { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| try { |
| BigIntegerMath.log10(ZERO, mode); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testLog10NegativeAlwaysThrows() { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| try { |
| BigIntegerMath.log10(BigInteger.valueOf(-1), mode); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testLog10Floor() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : asList(FLOOR, DOWN)) { |
| int result = BigIntegerMath.log10(x, mode); |
| assertTrue(TEN.pow(result).compareTo(x) <= 0); |
| assertTrue(TEN.pow(result + 1).compareTo(x) > 0); |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testLog10Ceiling() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : asList(CEILING, UP)) { |
| int result = BigIntegerMath.log10(x, mode); |
| assertTrue(TEN.pow(result).compareTo(x) >= 0); |
| assertTrue(result == 0 || TEN.pow(result - 1).compareTo(x) < 0); |
| } |
| } |
| } |
| |
| // Relies on the correctness of log10(BigInteger, FLOOR). |
| @GwtIncompatible // TODO |
| public void testLog10Exact() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| int logFloor = BigIntegerMath.log10(x, FLOOR); |
| boolean expectSuccess = TEN.pow(logFloor).equals(x); |
| try { |
| assertEquals(logFloor, BigIntegerMath.log10(x, UNNECESSARY)); |
| assertTrue(expectSuccess); |
| } catch (ArithmeticException e) { |
| assertFalse(expectSuccess); |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testLog10HalfUp() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| int result = BigIntegerMath.log10(x, HALF_UP); |
| BigInteger x2 = x.pow(2); |
| // x^2 < 10^(2 * result + 1), or else we would have rounded up |
| assertTrue(TEN.pow(2 * result + 1).compareTo(x2) > 0); |
| // x^2 >= 10^(2 * result - 1), or else we would have rounded down |
| assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) <= 0); |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testLog10HalfDown() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| int result = BigIntegerMath.log10(x, HALF_DOWN); |
| BigInteger x2 = x.pow(2); |
| // x^2 <= 10^(2 * result + 1), or else we would have rounded up |
| assertTrue(TEN.pow(2 * result + 1).compareTo(x2) >= 0); |
| // x^2 > 10^(2 * result - 1), or else we would have rounded down |
| assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) < 0); |
| } |
| } |
| |
| // Relies on the correctness of log10(BigInteger, {HALF_UP,HALF_DOWN}). |
| @GwtIncompatible // TODO |
| public void testLog10HalfEven() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| int halfEven = BigIntegerMath.log10(x, HALF_EVEN); |
| // Now figure out what rounding mode we should behave like (it depends if FLOOR was |
| // odd/even). |
| boolean floorWasEven = (BigIntegerMath.log10(x, FLOOR) & 1) == 0; |
| assertEquals(BigIntegerMath.log10(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testLog10TrivialOnPowerOf10() { |
| BigInteger x = BigInteger.TEN.pow(100); |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| assertEquals(100, BigIntegerMath.log10(x, mode)); |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testSqrtZeroAlwaysZero() { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| assertEquals(ZERO, BigIntegerMath.sqrt(ZERO, mode)); |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testSqrtNegativeAlwaysThrows() { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| try { |
| BigIntegerMath.sqrt(BigInteger.valueOf(-1), mode); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testSqrtFloor() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : asList(FLOOR, DOWN)) { |
| BigInteger result = BigIntegerMath.sqrt(x, mode); |
| assertTrue(result.compareTo(ZERO) > 0); |
| assertTrue(result.pow(2).compareTo(x) <= 0); |
| assertTrue(result.add(ONE).pow(2).compareTo(x) > 0); |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testSqrtCeiling() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : asList(CEILING, UP)) { |
| BigInteger result = BigIntegerMath.sqrt(x, mode); |
| assertTrue(result.compareTo(ZERO) > 0); |
| assertTrue(result.pow(2).compareTo(x) >= 0); |
| assertTrue(result.signum() == 0 || result.subtract(ONE).pow(2).compareTo(x) < 0); |
| } |
| } |
| } |
| |
| // Relies on the correctness of sqrt(BigInteger, FLOOR). |
| @GwtIncompatible // TODO |
| public void testSqrtExact() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| BigInteger floor = BigIntegerMath.sqrt(x, FLOOR); |
| // We only expect an exception if x was not a perfect square. |
| boolean isPerfectSquare = floor.pow(2).equals(x); |
| try { |
| assertEquals(floor, BigIntegerMath.sqrt(x, UNNECESSARY)); |
| assertTrue(isPerfectSquare); |
| } catch (ArithmeticException e) { |
| assertFalse(isPerfectSquare); |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testSqrtHalfUp() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| BigInteger result = BigIntegerMath.sqrt(x, HALF_UP); |
| BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); |
| BigInteger x4 = x.shiftLeft(2); |
| // sqrt(x) < result + 0.5, so 4 * x < (result + 0.5)^2 * 4 |
| // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 |
| assertTrue(x4.compareTo(plusHalfSquared) < 0); |
| BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); |
| // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 |
| // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 |
| assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) >= 0); |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testSqrtHalfDown() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| BigInteger result = BigIntegerMath.sqrt(x, HALF_DOWN); |
| BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); |
| BigInteger x4 = x.shiftLeft(2); |
| // sqrt(x) <= result + 0.5, so 4 * x <= (result + 0.5)^2 * 4 |
| // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 |
| assertTrue(x4.compareTo(plusHalfSquared) <= 0); |
| BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); |
| // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 |
| // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 |
| assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) > 0); |
| } |
| } |
| |
| // Relies on the correctness of sqrt(BigInteger, {HALF_UP,HALF_DOWN}). |
| @GwtIncompatible // TODO |
| public void testSqrtHalfEven() { |
| for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { |
| BigInteger halfEven = BigIntegerMath.sqrt(x, HALF_EVEN); |
| // Now figure out what rounding mode we should behave like (it depends if FLOOR was |
| // odd/even). |
| boolean floorWasOdd = BigIntegerMath.sqrt(x, FLOOR).testBit(0); |
| assertEquals(BigIntegerMath.sqrt(x, floorWasOdd ? HALF_UP : HALF_DOWN), halfEven); |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| @AndroidIncompatible // slow |
| public void testDivNonZero() { |
| for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { |
| for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { |
| BigInteger expected = |
| new BigDecimal(p).divide(new BigDecimal(q), 0, mode).toBigIntegerExact(); |
| assertEquals(expected, BigIntegerMath.divide(p, q, mode)); |
| } |
| } |
| } |
| } |
| |
| private static final BigInteger BAD_FOR_ANDROID_P = new BigInteger("-9223372036854775808"); |
| private static final BigInteger BAD_FOR_ANDROID_Q = new BigInteger("-1"); |
| |
| private static final BigInteger BAD_FOR_GINGERBREAD_P = new BigInteger("-9223372036854775808"); |
| private static final BigInteger BAD_FOR_GINGERBREAD_Q = new BigInteger("-4294967296"); |
| |
| @GwtIncompatible // TODO |
| @AndroidIncompatible // slow |
| public void testDivNonZeroExact() { |
| boolean isAndroid = System.getProperties().getProperty("java.runtime.name").contains("Android"); |
| for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { |
| for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { |
| if (isAndroid && p.equals(BAD_FOR_ANDROID_P) && q.equals(BAD_FOR_ANDROID_Q)) { |
| // https://code.google.com/p/android/issues/detail?id=196555 |
| continue; |
| } |
| if (isAndroid && p.equals(BAD_FOR_GINGERBREAD_P) && q.equals(BAD_FOR_GINGERBREAD_Q)) { |
| // Works fine under Marshmallow, so I haven't filed a bug. |
| continue; |
| } |
| |
| boolean dividesEvenly = p.remainder(q).equals(ZERO); |
| |
| try { |
| BigInteger quotient = BigIntegerMath.divide(p, q, UNNECESSARY); |
| BigInteger undone = quotient.multiply(q); |
| if (!p.equals(undone)) { |
| failFormat("expected %s.multiply(%s) = %s; got %s", quotient, q, p, undone); |
| } |
| assertTrue(dividesEvenly); |
| } catch (ArithmeticException e) { |
| assertFalse(dividesEvenly); |
| } |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testZeroDivIsAlwaysZero() { |
| for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| assertEquals(ZERO, BigIntegerMath.divide(ZERO, q, mode)); |
| } |
| } |
| } |
| |
| @GwtIncompatible // TODO |
| public void testDivByZeroAlwaysFails() { |
| for (BigInteger p : ALL_BIGINTEGER_CANDIDATES) { |
| for (RoundingMode mode : ALL_ROUNDING_MODES) { |
| try { |
| BigIntegerMath.divide(p, ZERO, mode); |
| fail("Expected ArithmeticException"); |
| } catch (ArithmeticException expected) { |
| } |
| } |
| } |
| } |
| |
| public void testFactorial() { |
| BigInteger expected = BigInteger.ONE; |
| for (int i = 1; i <= 200; i++) { |
| expected = expected.multiply(BigInteger.valueOf(i)); |
| assertEquals(expected, BigIntegerMath.factorial(i)); |
| } |
| } |
| |
| public void testFactorial0() { |
| assertEquals(BigInteger.ONE, BigIntegerMath.factorial(0)); |
| } |
| |
| public void testFactorialNegative() { |
| try { |
| BigIntegerMath.factorial(-1); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public void testBinomialSmall() { |
| runBinomialTest(0, 30); |
| } |
| |
| @GwtIncompatible // too slow |
| public void testBinomialLarge() { |
| runBinomialTest(31, 100); |
| } |
| |
| // Depends on the correctness of BigIntegerMath.factorial |
| private static void runBinomialTest(int firstN, int lastN) { |
| for (int n = firstN; n <= lastN; n++) { |
| for (int k = 0; k <= n; k++) { |
| BigInteger expected = |
| BigIntegerMath.factorial(n) |
| .divide(BigIntegerMath.factorial(k)) |
| .divide(BigIntegerMath.factorial(n - k)); |
| assertEquals(expected, BigIntegerMath.binomial(n, k)); |
| } |
| } |
| } |
| |
| public void testBinomialOutside() { |
| for (int n = 0; n <= 50; n++) { |
| try { |
| BigIntegerMath.binomial(n, -1); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| try { |
| BigIntegerMath.binomial(n, n + 1); |
| fail("Expected IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| } |
| |
| @GwtIncompatible |
| private static final class RoundToDoubleTester { |
| private final BigInteger input; |
| private final Map<RoundingMode, Double> expectedValues = new EnumMap<>(RoundingMode.class); |
| private boolean unnecessaryShouldThrow = false; |
| |
| RoundToDoubleTester(BigInteger input) { |
| this.input = input; |
| } |
| |
| RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) { |
| for (RoundingMode mode : modes) { |
| Double previous = expectedValues.put(mode, expectedValue); |
| if (previous != null) { |
| throw new AssertionError(); |
| } |
| } |
| return this; |
| } |
| |
| public RoundToDoubleTester roundUnnecessaryShouldThrow() { |
| unnecessaryShouldThrow = true; |
| return this; |
| } |
| |
| public void test() { |
| assertThat(expectedValues.keySet()) |
| .containsAtLeastElementsIn(EnumSet.complementOf(EnumSet.of(UNNECESSARY))); |
| for (Map.Entry<RoundingMode, Double> entry : expectedValues.entrySet()) { |
| RoundingMode mode = entry.getKey(); |
| Double expectation = entry.getValue(); |
| assertWithMessage("roundToDouble(" + input + ", " + mode + ")") |
| .that(BigIntegerMath.roundToDouble(input, mode)) |
| .isEqualTo(expectation); |
| } |
| |
| if (!expectedValues.containsKey(UNNECESSARY)) { |
| assertWithMessage("Expected roundUnnecessaryShouldThrow call") |
| .that(unnecessaryShouldThrow) |
| .isTrue(); |
| try { |
| BigIntegerMath.roundToDouble(input, UNNECESSARY); |
| fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)"); |
| } catch (ArithmeticException expected) { |
| // expected |
| } |
| } |
| } |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_Zero() { |
| new RoundToDoubleTester(BigInteger.ZERO).setExpectation(0.0, values()).test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_smallPositive() { |
| new RoundToDoubleTester(BigInteger.valueOf(16)).setExpectation(16.0, values()).test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_maxPreciselyRepresentable() { |
| new RoundToDoubleTester(BigInteger.valueOf(1L << 53)) |
| .setExpectation(Math.pow(2, 53), values()) |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_maxPreciselyRepresentablePlusOne() { |
| double twoToThe53 = Math.pow(2, 53); |
| // the representable doubles are 2^53 and 2^53 + 2. |
| // 2^53+1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. |
| new RoundToDoubleTester(BigInteger.valueOf((1L << 53) + 1)) |
| .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN) |
| .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_twoToThe54PlusOne() { |
| double twoToThe54 = Math.pow(2, 54); |
| // the representable doubles are 2^54 and 2^54 + 4 |
| // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down. |
| new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 1)) |
| .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN) |
| .setExpectation(Math.nextUp(twoToThe54), CEILING, UP) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_twoToThe54PlusThree() { |
| double twoToThe54 = Math.pow(2, 54); |
| // the representable doubles are 2^54 and 2^54 + 4 |
| // 2^54+3 is more than halfway between, so HALF_DOWN and HALF_UP will both go up. |
| new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 3)) |
| .setExpectation(twoToThe54, DOWN, FLOOR) |
| .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_twoToThe54PlusFour() { |
| new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 4)) |
| .setExpectation(Math.pow(2, 54) + 4, values()) |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_maxDouble() { |
| BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY); |
| new RoundToDoubleTester(maxDoubleAsBI).setExpectation(Double.MAX_VALUE, values()).test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_maxDoublePlusOne() { |
| BigInteger maxDoubleAsBI = |
| DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY).add(BigInteger.ONE); |
| new RoundToDoubleTester(maxDoubleAsBI) |
| .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) |
| .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_wayTooBig() { |
| BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT); |
| new RoundToDoubleTester(bi) |
| .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) |
| .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_smallNegative() { |
| new RoundToDoubleTester(BigInteger.valueOf(-16)).setExpectation(-16.0, values()).test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_minPreciselyRepresentable() { |
| new RoundToDoubleTester(BigInteger.valueOf(-1L << 53)) |
| .setExpectation(-Math.pow(2, 53), values()) |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_minPreciselyRepresentableMinusOne() { |
| // the representable doubles are -2^53 and -2^53 - 2. |
| // -2^53-1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. |
| new RoundToDoubleTester(BigInteger.valueOf((-1L << 53) - 1)) |
| .setExpectation(-Math.pow(2, 53), DOWN, CEILING, HALF_DOWN, HALF_EVEN) |
| .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 53)), FLOOR, UP, HALF_UP) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_negativeTwoToThe54MinusOne() { |
| new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 1)) |
| .setExpectation(-Math.pow(2, 54), DOWN, CEILING, HALF_DOWN, HALF_UP, HALF_EVEN) |
| .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_negativeTwoToThe54MinusThree() { |
| new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 3)) |
| .setExpectation(-Math.pow(2, 54), DOWN, CEILING) |
| .setExpectation( |
| DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP, HALF_DOWN, HALF_UP, HALF_EVEN) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_negativeTwoToThe54MinusFour() { |
| new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 4)) |
| .setExpectation(-Math.pow(2, 54) - 4, values()) |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_minDouble() { |
| BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY); |
| new RoundToDoubleTester(minDoubleAsBI).setExpectation(-Double.MAX_VALUE, values()).test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_minDoubleMinusOne() { |
| BigInteger minDoubleAsBI = |
| DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY).subtract(BigInteger.ONE); |
| new RoundToDoubleTester(minDoubleAsBI) |
| .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) |
| .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible |
| public void testRoundToDouble_negativeWayTooBig() { |
| BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT).negate(); |
| new RoundToDoubleTester(bi) |
| .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) |
| .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) |
| .roundUnnecessaryShouldThrow() |
| .test(); |
| } |
| |
| @GwtIncompatible // NullPointerTester |
| public void testNullPointers() { |
| NullPointerTester tester = new NullPointerTester(); |
| tester.setDefault(BigInteger.class, ONE); |
| tester.setDefault(int.class, 1); |
| tester.setDefault(long.class, 1L); |
| tester.testAllPublicStaticMethods(BigIntegerMath.class); |
| } |
| |
| @GwtIncompatible // String.format |
| private static void failFormat(String template, Object... args) { |
| fail(String.format(template, args)); |
| } |
| } |