blob: e0ab4b53f2d15833cef4890308c20b6df1d86d46 [file] [log] [blame]
/*
* 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_LONG_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_INTEGER_CANDIDATES;
import static com.google.common.math.MathTesting.NEGATIVE_LONG_CANDIDATES;
import static com.google.common.math.MathTesting.POSITIVE_LONG_CANDIDATES;
import static java.math.BigInteger.valueOf;
import static java.math.RoundingMode.UNNECESSARY;
import com.google.common.annotations.GwtCompatible;
import junit.framework.TestCase;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
/**
* Tests for LongMath.
*
* @author Louis Wasserman
*/
@GwtCompatible(emulated = true)
public class LongMathTest extends TestCase {
public void testLessThanBranchFree() {
for (long x : ALL_LONG_CANDIDATES) {
for (long y : ALL_LONG_CANDIDATES) {
BigInteger difference = BigInteger.valueOf(x).subtract(BigInteger.valueOf(y));
if (fitsInLong(difference)) {
int expected = (x < y) ? 1 : 0;
int actual = LongMath.lessThanBranchFree(x, y);
assertEquals(expected, actual);
}
}
}
}
// Throws an ArithmeticException if "the simple implementation" of binomial coefficients overflows
public void testLog2ZeroAlwaysThrows() {
for (RoundingMode mode : ALL_ROUNDING_MODES) {
try {
LongMath.log2(0L, mode);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {}
}
}
public void testLog2NegativeAlwaysThrows() {
for (long x : NEGATIVE_LONG_CANDIDATES) {
for (RoundingMode mode : ALL_ROUNDING_MODES) {
try {
LongMath.log2(x, mode);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {}
}
}
}
/* Relies on the correctness of BigIntegerMath.log2 for all modes except UNNECESSARY. */
public void testLog2MatchesBigInteger() {
for (long x : POSITIVE_LONG_CANDIDATES) {
for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
// The BigInteger implementation is tested separately, use it as the reference.
assertEquals(BigIntegerMath.log2(valueOf(x), mode), LongMath.log2(x, mode));
}
}
}
/* Relies on the correctness of isPowerOfTwo(long). */
public void testLog2Exact() {
for (long x : POSITIVE_LONG_CANDIDATES) {
// We only expect an exception if x was not a power of 2.
boolean isPowerOf2 = LongMath.isPowerOfTwo(x);
try {
assertEquals(x, 1L << LongMath.log2(x, UNNECESSARY));
assertTrue(isPowerOf2);
} catch (ArithmeticException e) {
assertFalse(isPowerOf2);
}
}
}
// Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY.
// Relies on the correctness of log10(long, FLOOR) and of pow(long, int).
// Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY.
/* Relies on the correctness of sqrt(long, FLOOR). */
public void testGCDExhaustive() {
for (long a : POSITIVE_LONG_CANDIDATES) {
for (long b : POSITIVE_LONG_CANDIDATES) {
assertEquals(valueOf(a).gcd(valueOf(b)), valueOf(LongMath.gcd(a, b)));
}
}
}
// Depends on the correctness of BigIntegerMath.factorial.
// Depends on the correctness of BigIntegerMath.binomial.
public void testBinomial() {
for (int n = 0; n <= 70; n++) {
for (int k = 0; k <= n; k++) {
BigInteger expectedBig = BigIntegerMath.binomial(n, k);
long expectedLong = fitsInLong(expectedBig) ? expectedBig.longValue() : Long.MAX_VALUE;
assertEquals(expectedLong, LongMath.binomial(n, k));
}
}
}
public void testBinomialOutside() {
for (int n = 0; n <= 50; n++) {
try {
LongMath.binomial(n, -1);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {}
try {
LongMath.binomial(n, n + 1);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {}
}
}
public void testBinomialNegative() {
for (int n : NEGATIVE_INTEGER_CANDIDATES) {
try {
LongMath.binomial(n, 0);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {}
}
}
public void testSqrtOfLongIsAtMostFloorSqrtMaxLong() {
long sqrtMaxLong = (long) Math.sqrt(Long.MAX_VALUE);
assertTrue(sqrtMaxLong <= LongMath.FLOOR_SQRT_MAX_LONG);
}
/**
* Helper method that asserts the arithmetic mean of x and y is equal
* to the expectedMean.
*/
private static void assertMean(long expectedMean, long x, long y) {
assertEquals("The expectedMean should be the same as computeMeanSafely",
expectedMean, computeMeanSafely(x, y));
assertMean(x, y);
}
/**
* Helper method that asserts the arithmetic mean of x and y is equal
*to the result of computeMeanSafely.
*/
private static void assertMean(long x, long y) {
long expectedMean = computeMeanSafely(x, y);
assertEquals(expectedMean, LongMath.mean(x, y));
assertEquals("The mean of x and y should equal the mean of y and x",
expectedMean, LongMath.mean(y, x));
}
/**
* Computes the mean in a way that is obvious and resilient to
* overflow by using BigInteger arithmetic.
*/
private static long computeMeanSafely(long x, long y) {
BigInteger bigX = BigInteger.valueOf(x);
BigInteger bigY = BigInteger.valueOf(y);
BigDecimal bigMean = new BigDecimal(bigX.add(bigY))
.divide(BigDecimal.valueOf(2), BigDecimal.ROUND_FLOOR);
// parseInt blows up on overflow as opposed to intValue() which does not.
return Long.parseLong(bigMean.toString());
}
private static boolean fitsInLong(BigInteger big) {
return big.bitLength() <= 63;
}
}