/* * 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.POSITIVE_BIGINTEGER_CANDIDATES; import static java.math.BigInteger.ONE; 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.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; import java.math.BigInteger; import java.math.RoundingMode; /** * Tests for BigIntegerMath. * * @author Louis Wasserman */ @GwtCompatible(emulated = true) public class BigIntegerMathTest extends TestCase { 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); } } // Relies on the correctness of log10(BigInteger, FLOOR). // Relies on the correctness of log10(BigInteger, {HALF_UP,HALF_DOWN}). // Relies on the correctness of sqrt(BigInteger, FLOOR). // Relies on the correctness of sqrt(BigInteger, {HALF_UP,HALF_DOWN}). 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); } // 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) {} } } }