1029 lines
32 KiB
Java
1029 lines
32 KiB
Java
/*
|
|
* 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.EXPONENTS;
|
|
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.NONZERO_LONG_CANDIDATES;
|
|
import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES;
|
|
import static com.google.common.math.MathTesting.POSITIVE_LONG_CANDIDATES;
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static com.google.common.truth.Truth.assertWithMessage;
|
|
import static java.math.BigInteger.valueOf;
|
|
import static java.math.RoundingMode.FLOOR;
|
|
import static java.math.RoundingMode.UNNECESSARY;
|
|
|
|
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.EnumSet;
|
|
import java.util.Random;
|
|
import junit.framework.TestCase;
|
|
|
|
/**
|
|
* Tests for LongMath.
|
|
*
|
|
* @author Louis Wasserman
|
|
*/
|
|
@GwtCompatible(emulated = true)
|
|
public class LongMathTest extends TestCase {
|
|
@SuppressWarnings("ConstantOverflow")
|
|
public void testMaxSignedPowerOfTwo() {
|
|
assertTrue(LongMath.isPowerOfTwo(LongMath.MAX_SIGNED_POWER_OF_TWO));
|
|
assertFalse(LongMath.isPowerOfTwo(LongMath.MAX_SIGNED_POWER_OF_TWO * 2));
|
|
}
|
|
|
|
public void testCeilingPowerOfTwo() {
|
|
for (long x : POSITIVE_LONG_CANDIDATES) {
|
|
BigInteger expectedResult = BigIntegerMath.ceilingPowerOfTwo(BigInteger.valueOf(x));
|
|
if (fitsInLong(expectedResult)) {
|
|
assertEquals(expectedResult.longValue(), LongMath.ceilingPowerOfTwo(x));
|
|
} else {
|
|
try {
|
|
LongMath.ceilingPowerOfTwo(x);
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void testFloorPowerOfTwo() {
|
|
for (long x : POSITIVE_LONG_CANDIDATES) {
|
|
BigInteger expectedResult = BigIntegerMath.floorPowerOfTwo(BigInteger.valueOf(x));
|
|
assertEquals(expectedResult.longValue(), LongMath.floorPowerOfTwo(x));
|
|
}
|
|
}
|
|
|
|
public void testCeilingPowerOfTwoNegative() {
|
|
for (long x : NEGATIVE_LONG_CANDIDATES) {
|
|
try {
|
|
LongMath.ceilingPowerOfTwo(x);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
|
|
public void testFloorPowerOfTwoNegative() {
|
|
for (long x : NEGATIVE_LONG_CANDIDATES) {
|
|
try {
|
|
LongMath.floorPowerOfTwo(x);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
|
|
public void testCeilingPowerOfTwoZero() {
|
|
try {
|
|
LongMath.ceilingPowerOfTwo(0L);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
|
|
public void testFloorPowerOfTwoZero() {
|
|
try {
|
|
LongMath.floorPowerOfTwo(0L);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testConstantMaxPowerOfSqrt2Unsigned() {
|
|
assertEquals(
|
|
/*expected=*/ BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Long.SIZE - 1), FLOOR)
|
|
.longValue(),
|
|
/*actual=*/ LongMath.MAX_POWER_OF_SQRT2_UNSIGNED);
|
|
}
|
|
|
|
@GwtIncompatible // BigIntegerMath // TODO(cpovirk): GWT-enable BigIntegerMath
|
|
public void testMaxLog10ForLeadingZeros() {
|
|
for (int i = 0; i < Long.SIZE; i++) {
|
|
assertEquals(
|
|
BigIntegerMath.log10(BigInteger.ONE.shiftLeft(Long.SIZE - i), FLOOR),
|
|
LongMath.maxLog10ForLeadingZeros[i]);
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testConstantsPowersOf10() {
|
|
for (int i = 0; i < LongMath.powersOf10.length; i++) {
|
|
assertEquals(LongMath.checkedPow(10, i), LongMath.powersOf10[i]);
|
|
}
|
|
try {
|
|
LongMath.checkedPow(10, LongMath.powersOf10.length);
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testConstantsHalfPowersOf10() {
|
|
for (int i = 0; i < LongMath.halfPowersOf10.length; i++) {
|
|
assertEquals(
|
|
BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * i + 1), FLOOR),
|
|
BigInteger.valueOf(LongMath.halfPowersOf10[i]));
|
|
}
|
|
BigInteger nextBigger =
|
|
BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * LongMath.halfPowersOf10.length + 1), FLOOR);
|
|
assertTrue(nextBigger.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0);
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testConstantsSqrtMaxLong() {
|
|
assertEquals(
|
|
/*expected=*/ LongMath.sqrt(Long.MAX_VALUE, FLOOR),
|
|
/*actual=*/ LongMath.FLOOR_SQRT_MAX_LONG);
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testConstantsFactorials() {
|
|
long expected = 1;
|
|
for (int i = 0; i < LongMath.factorials.length; i++, expected *= i) {
|
|
assertEquals(expected, LongMath.factorials[i]);
|
|
}
|
|
try {
|
|
LongMath.checkedMultiply(
|
|
LongMath.factorials[LongMath.factorials.length - 1], LongMath.factorials.length);
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expect) {
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testConstantsBiggestBinomials() {
|
|
for (int k = 0; k < LongMath.biggestBinomials.length; k++) {
|
|
assertTrue(fitsInLong(BigIntegerMath.binomial(LongMath.biggestBinomials[k], k)));
|
|
assertTrue(
|
|
LongMath.biggestBinomials[k] == Integer.MAX_VALUE
|
|
|| !fitsInLong(BigIntegerMath.binomial(LongMath.biggestBinomials[k] + 1, k)));
|
|
// In the first case, any long is valid; in the second, we want to test that the next-bigger
|
|
// long overflows.
|
|
}
|
|
int k = LongMath.biggestBinomials.length;
|
|
assertFalse(fitsInLong(BigIntegerMath.binomial(2 * k, k)));
|
|
// 2 * k is the smallest value for which we don't replace k with (n-k).
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testConstantsBiggestSimpleBinomials() {
|
|
for (int k = 0; k < LongMath.biggestSimpleBinomials.length; k++) {
|
|
assertTrue(LongMath.biggestSimpleBinomials[k] <= LongMath.biggestBinomials[k]);
|
|
long unused = simpleBinomial(LongMath.biggestSimpleBinomials[k], k); // mustn't throw
|
|
if (LongMath.biggestSimpleBinomials[k] < Integer.MAX_VALUE) {
|
|
// unless all n are fair game with this k
|
|
try {
|
|
simpleBinomial(LongMath.biggestSimpleBinomials[k] + 1, k);
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
}
|
|
try {
|
|
int k = LongMath.biggestSimpleBinomials.length;
|
|
simpleBinomial(2 * k, k);
|
|
// 2 * k is the smallest value for which we don't replace k with (n-k).
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
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
|
|
@GwtIncompatible // TODO
|
|
private long simpleBinomial(int n, int k) {
|
|
long accum = 1;
|
|
for (int i = 0; i < k; i++) {
|
|
accum = LongMath.checkedMultiply(accum, n - i);
|
|
accum /= i + 1;
|
|
}
|
|
return accum;
|
|
}
|
|
|
|
@GwtIncompatible // java.math.BigInteger
|
|
public void testIsPowerOfTwo() {
|
|
for (long x : ALL_LONG_CANDIDATES) {
|
|
// Checks for a single bit set.
|
|
BigInteger bigX = BigInteger.valueOf(x);
|
|
boolean expected = (bigX.signum() > 0) && (bigX.bitCount() == 1);
|
|
assertEquals(expected, LongMath.isPowerOfTwo(x));
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testLog10ZeroAlwaysThrows() {
|
|
for (RoundingMode mode : ALL_ROUNDING_MODES) {
|
|
try {
|
|
LongMath.log10(0L, mode);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testLog10NegativeAlwaysThrows() {
|
|
for (long x : NEGATIVE_LONG_CANDIDATES) {
|
|
for (RoundingMode mode : ALL_ROUNDING_MODES) {
|
|
try {
|
|
LongMath.log10(x, mode);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY.
|
|
@GwtIncompatible // TODO
|
|
public void testLog10MatchesBigInteger() {
|
|
for (long x : POSITIVE_LONG_CANDIDATES) {
|
|
for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
|
|
assertEquals(BigIntegerMath.log10(valueOf(x), mode), LongMath.log10(x, mode));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Relies on the correctness of log10(long, FLOOR) and of pow(long, int).
|
|
@GwtIncompatible // TODO
|
|
public void testLog10Exact() {
|
|
for (long x : POSITIVE_LONG_CANDIDATES) {
|
|
int floor = LongMath.log10(x, FLOOR);
|
|
boolean expectedSuccess = LongMath.pow(10, floor) == x;
|
|
try {
|
|
assertEquals(floor, LongMath.log10(x, UNNECESSARY));
|
|
assertTrue(expectedSuccess);
|
|
} catch (ArithmeticException e) {
|
|
if (expectedSuccess) {
|
|
failFormat("expected log10(%s, UNNECESSARY) = %s; got ArithmeticException", x, floor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testLog10TrivialOnPowerOf10() {
|
|
long x = 1000000000000L;
|
|
for (RoundingMode mode : ALL_ROUNDING_MODES) {
|
|
assertEquals(12, LongMath.log10(x, mode));
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testSqrtNegativeAlwaysThrows() {
|
|
for (long x : NEGATIVE_LONG_CANDIDATES) {
|
|
for (RoundingMode mode : ALL_ROUNDING_MODES) {
|
|
try {
|
|
LongMath.sqrt(x, mode);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY.
|
|
@GwtIncompatible // TODO
|
|
public void testSqrtMatchesBigInteger() {
|
|
for (long x : POSITIVE_LONG_CANDIDATES) {
|
|
for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
|
|
// Promote the long value (rather than using longValue() on the expected value) to avoid
|
|
// any risk of truncation which could lead to a false positive.
|
|
assertEquals(BigIntegerMath.sqrt(valueOf(x), mode), valueOf(LongMath.sqrt(x, mode)));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Relies on the correctness of sqrt(long, FLOOR). */
|
|
@GwtIncompatible // TODO
|
|
public void testSqrtExactMatchesFloorOrThrows() {
|
|
for (long x : POSITIVE_LONG_CANDIDATES) {
|
|
long sqrtFloor = LongMath.sqrt(x, FLOOR);
|
|
// We only expect an exception if x was not a perfect square.
|
|
boolean isPerfectSquare = (sqrtFloor * sqrtFloor == x);
|
|
try {
|
|
assertEquals(sqrtFloor, LongMath.sqrt(x, UNNECESSARY));
|
|
assertTrue(isPerfectSquare);
|
|
} catch (ArithmeticException e) {
|
|
assertFalse(isPerfectSquare);
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testPow() {
|
|
for (long i : ALL_LONG_CANDIDATES) {
|
|
for (int exp : EXPONENTS) {
|
|
assertEquals(LongMath.pow(i, exp), valueOf(i).pow(exp).longValue());
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
@AndroidIncompatible // TODO(cpovirk): File BigDecimal.divide() rounding bug.
|
|
public void testDivNonZero() {
|
|
for (long p : NONZERO_LONG_CANDIDATES) {
|
|
for (long q : NONZERO_LONG_CANDIDATES) {
|
|
for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
|
|
long expected =
|
|
new BigDecimal(valueOf(p)).divide(new BigDecimal(valueOf(q)), 0, mode).longValue();
|
|
long actual = LongMath.divide(p, q, mode);
|
|
if (expected != actual) {
|
|
failFormat("expected divide(%s, %s, %s) = %s; got %s", p, q, mode, expected, actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
@AndroidIncompatible // Bug in older versions of Android we test against, since fixed.
|
|
public void testDivNonZeroExact() {
|
|
for (long p : NONZERO_LONG_CANDIDATES) {
|
|
for (long q : NONZERO_LONG_CANDIDATES) {
|
|
boolean expectedSuccess = (p % q) == 0L;
|
|
|
|
try {
|
|
assertEquals(p, LongMath.divide(p, q, UNNECESSARY) * q);
|
|
assertTrue(expectedSuccess);
|
|
} catch (ArithmeticException e) {
|
|
if (expectedSuccess) {
|
|
failFormat(
|
|
"expected divide(%s, %s, UNNECESSARY) to succeed; got ArithmeticException", p, q);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testZeroDivIsAlwaysZero() {
|
|
for (long q : NONZERO_LONG_CANDIDATES) {
|
|
for (RoundingMode mode : ALL_ROUNDING_MODES) {
|
|
assertEquals(0L, LongMath.divide(0L, q, mode));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testDivByZeroAlwaysFails() {
|
|
for (long p : ALL_LONG_CANDIDATES) {
|
|
for (RoundingMode mode : ALL_ROUNDING_MODES) {
|
|
try {
|
|
LongMath.divide(p, 0L, mode);
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testIntMod() {
|
|
for (long x : ALL_LONG_CANDIDATES) {
|
|
for (int m : POSITIVE_INTEGER_CANDIDATES) {
|
|
assertEquals(valueOf(x).mod(valueOf(m)).intValue(), LongMath.mod(x, m));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testIntModNegativeModulusFails() {
|
|
for (long x : ALL_LONG_CANDIDATES) {
|
|
for (int m : NEGATIVE_INTEGER_CANDIDATES) {
|
|
try {
|
|
LongMath.mod(x, m);
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testIntModZeroModulusFails() {
|
|
for (long x : ALL_LONG_CANDIDATES) {
|
|
try {
|
|
LongMath.mod(x, 0);
|
|
fail("Expected AE");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
@GwtIncompatible // TODO
|
|
public void testMod() {
|
|
for (long x : ALL_LONG_CANDIDATES) {
|
|
for (long m : POSITIVE_LONG_CANDIDATES) {
|
|
assertEquals(valueOf(x).mod(valueOf(m)).longValue(), LongMath.mod(x, m));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testModNegativeModulusFails() {
|
|
for (long x : ALL_LONG_CANDIDATES) {
|
|
for (long m : NEGATIVE_LONG_CANDIDATES) {
|
|
try {
|
|
LongMath.mod(x, m);
|
|
fail("Expected ArithmeticException");
|
|
} catch (ArithmeticException expected) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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)));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testGCDZero() {
|
|
for (long a : POSITIVE_LONG_CANDIDATES) {
|
|
assertEquals(a, LongMath.gcd(a, 0));
|
|
assertEquals(a, LongMath.gcd(0, a));
|
|
}
|
|
assertEquals(0, LongMath.gcd(0, 0));
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testGCDNegativePositiveThrows() {
|
|
for (long a : NEGATIVE_LONG_CANDIDATES) {
|
|
try {
|
|
LongMath.gcd(a, 3);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
try {
|
|
LongMath.gcd(3, a);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testGCDNegativeZeroThrows() {
|
|
for (long a : NEGATIVE_LONG_CANDIDATES) {
|
|
try {
|
|
LongMath.gcd(a, 0);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
try {
|
|
LongMath.gcd(0, a);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
@GwtIncompatible // TODO
|
|
public void testCheckedAdd() {
|
|
for (long a : ALL_LONG_CANDIDATES) {
|
|
for (long b : ALL_LONG_CANDIDATES) {
|
|
BigInteger expectedResult = valueOf(a).add(valueOf(b));
|
|
boolean expectedSuccess = fitsInLong(expectedResult);
|
|
try {
|
|
assertEquals(a + b, LongMath.checkedAdd(a, b));
|
|
assertTrue(expectedSuccess);
|
|
} catch (ArithmeticException e) {
|
|
if (expectedSuccess) {
|
|
failFormat(
|
|
"expected checkedAdd(%s, %s) = %s; got ArithmeticException", a, b, expectedResult);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
@AndroidIncompatible // slow
|
|
public void testCheckedSubtract() {
|
|
for (long a : ALL_LONG_CANDIDATES) {
|
|
for (long b : ALL_LONG_CANDIDATES) {
|
|
BigInteger expectedResult = valueOf(a).subtract(valueOf(b));
|
|
boolean expectedSuccess = fitsInLong(expectedResult);
|
|
try {
|
|
assertEquals(a - b, LongMath.checkedSubtract(a, b));
|
|
assertTrue(expectedSuccess);
|
|
} catch (ArithmeticException e) {
|
|
if (expectedSuccess) {
|
|
failFormat(
|
|
"expected checkedSubtract(%s, %s) = %s; got ArithmeticException",
|
|
a, b, expectedResult);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
public void testCheckedMultiply() {
|
|
boolean isAndroid = TestPlatform.isAndroid();
|
|
for (long a : ALL_LONG_CANDIDATES) {
|
|
for (long b : ALL_LONG_CANDIDATES) {
|
|
if (isAndroid && a == -4294967296L && b == 2147483648L) {
|
|
/*
|
|
* Bug in older versions of Android we test against, since fixed: -9223372036854775808L /
|
|
* -4294967296L = -9223372036854775808L!
|
|
*
|
|
* To be clear, this bug affects not the test's computation of the expected result but the
|
|
* _actual prod code_. But it probably affects only unusual cases.
|
|
*/
|
|
continue;
|
|
}
|
|
BigInteger expectedResult = valueOf(a).multiply(valueOf(b));
|
|
boolean expectedSuccess = fitsInLong(expectedResult);
|
|
try {
|
|
assertEquals(a * b, LongMath.checkedMultiply(a, b));
|
|
assertTrue(expectedSuccess);
|
|
} catch (ArithmeticException e) {
|
|
if (expectedSuccess) {
|
|
failFormat(
|
|
"expected checkedMultiply(%s, %s) = %s; got ArithmeticException",
|
|
a, b, expectedResult);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testCheckedPow() {
|
|
for (long b : ALL_LONG_CANDIDATES) {
|
|
for (int exp : EXPONENTS) {
|
|
BigInteger expectedResult = valueOf(b).pow(exp);
|
|
boolean expectedSuccess = fitsInLong(expectedResult);
|
|
try {
|
|
assertEquals(expectedResult.longValue(), LongMath.checkedPow(b, exp));
|
|
assertTrue(expectedSuccess);
|
|
} catch (ArithmeticException e) {
|
|
if (expectedSuccess) {
|
|
failFormat(
|
|
"expected checkedPow(%s, %s) = %s; got ArithmeticException",
|
|
b, exp, expectedResult);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
@GwtIncompatible // TODO
|
|
public void testSaturatedAdd() {
|
|
for (long a : ALL_LONG_CANDIDATES) {
|
|
for (long b : ALL_LONG_CANDIDATES) {
|
|
assertOperationEquals(
|
|
a, b, "s+", saturatedCast(valueOf(a).add(valueOf(b))), LongMath.saturatedAdd(a, b));
|
|
}
|
|
}
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
@GwtIncompatible // TODO
|
|
public void testSaturatedSubtract() {
|
|
for (long a : ALL_LONG_CANDIDATES) {
|
|
for (long b : ALL_LONG_CANDIDATES) {
|
|
assertOperationEquals(
|
|
a,
|
|
b,
|
|
"s-",
|
|
saturatedCast(valueOf(a).subtract(valueOf(b))),
|
|
LongMath.saturatedSubtract(a, b));
|
|
}
|
|
}
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
@GwtIncompatible // TODO
|
|
public void testSaturatedMultiply() {
|
|
for (long a : ALL_LONG_CANDIDATES) {
|
|
for (long b : ALL_LONG_CANDIDATES) {
|
|
assertOperationEquals(
|
|
a,
|
|
b,
|
|
"s*",
|
|
saturatedCast(valueOf(a).multiply(valueOf(b))),
|
|
LongMath.saturatedMultiply(a, b));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testSaturatedPow() {
|
|
for (long a : ALL_LONG_CANDIDATES) {
|
|
for (int b : EXPONENTS) {
|
|
assertOperationEquals(
|
|
a, b, "s^", saturatedCast(valueOf(a).pow(b)), LongMath.saturatedPow(a, b));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void assertOperationEquals(long a, long b, String op, long expected, long actual) {
|
|
if (expected != actual) {
|
|
fail("Expected for " + a + " " + op + " " + b + " = " + expected + ", but got " + actual);
|
|
}
|
|
}
|
|
|
|
// Depends on the correctness of BigIntegerMath.factorial.
|
|
@GwtIncompatible // TODO
|
|
public void testFactorial() {
|
|
for (int n = 0; n <= 50; n++) {
|
|
BigInteger expectedBig = BigIntegerMath.factorial(n);
|
|
long expectedLong = fitsInLong(expectedBig) ? expectedBig.longValue() : Long.MAX_VALUE;
|
|
assertEquals(expectedLong, LongMath.factorial(n));
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // TODO
|
|
public void testFactorialNegative() {
|
|
for (int n : NEGATIVE_INTEGER_CANDIDATES) {
|
|
try {
|
|
LongMath.factorial(n);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@GwtIncompatible // Slow
|
|
public void testBinomial_exhaustiveNotOverflowing() {
|
|
// Tests all of the inputs to LongMath.binomial that won't cause it to overflow, that weren't
|
|
// tested in the previous method, for k >= 3.
|
|
for (int k = 3; k < LongMath.biggestBinomials.length; k++) {
|
|
for (int n = 70; n <= LongMath.biggestBinomials[k]; n++) {
|
|
assertEquals(BigIntegerMath.binomial(n, k).longValue(), 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) {
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@GwtIncompatible // far too slow
|
|
public void testSqrtOfPerfectSquareAsDoubleIsPerfect() {
|
|
// This takes just over a minute on my machine.
|
|
for (long n = 0; n <= LongMath.FLOOR_SQRT_MAX_LONG; n++) {
|
|
long actual = (long) Math.sqrt(n * n);
|
|
assertTrue(actual == n);
|
|
}
|
|
}
|
|
|
|
public void testSqrtOfLongIsAtMostFloorSqrtMaxLong() {
|
|
long sqrtMaxLong = (long) Math.sqrt(Long.MAX_VALUE);
|
|
assertTrue(sqrtMaxLong <= LongMath.FLOOR_SQRT_MAX_LONG);
|
|
}
|
|
|
|
@AndroidIncompatible // slow
|
|
@GwtIncompatible // java.math.BigInteger
|
|
public void testMean() {
|
|
// Odd-sized ranges have an obvious mean
|
|
assertMean(2, 1, 3);
|
|
|
|
assertMean(-2, -3, -1);
|
|
assertMean(0, -1, 1);
|
|
assertMean(1, -1, 3);
|
|
assertMean((1L << 62) - 1, -1, Long.MAX_VALUE);
|
|
|
|
// Even-sized ranges should prefer the lower mean
|
|
assertMean(2, 1, 4);
|
|
assertMean(-3, -4, -1);
|
|
assertMean(0, -1, 2);
|
|
assertMean(0, Long.MIN_VALUE + 2, Long.MAX_VALUE);
|
|
assertMean(0, 0, 1);
|
|
assertMean(-1, -1, 0);
|
|
assertMean(-1, Long.MIN_VALUE, Long.MAX_VALUE);
|
|
|
|
// x == y == mean
|
|
assertMean(1, 1, 1);
|
|
assertMean(0, 0, 0);
|
|
assertMean(-1, -1, -1);
|
|
assertMean(Long.MIN_VALUE, Long.MIN_VALUE, Long.MIN_VALUE);
|
|
assertMean(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE);
|
|
|
|
// Exhaustive checks
|
|
for (long x : ALL_LONG_CANDIDATES) {
|
|
for (long y : ALL_LONG_CANDIDATES) {
|
|
assertMean(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** 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;
|
|
}
|
|
|
|
private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
|
|
private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
|
|
|
|
private static long saturatedCast(BigInteger big) {
|
|
if (big.compareTo(MAX_LONG) > 0) {
|
|
return Long.MAX_VALUE;
|
|
}
|
|
if (big.compareTo(MIN_LONG) < 0) {
|
|
return Long.MIN_VALUE;
|
|
}
|
|
return big.longValue();
|
|
}
|
|
|
|
@GwtIncompatible // NullPointerTester
|
|
public void testNullPointers() {
|
|
NullPointerTester tester = new NullPointerTester();
|
|
tester.setDefault(int.class, 1);
|
|
tester.setDefault(long.class, 1L);
|
|
tester.testAllPublicStaticMethods(LongMath.class);
|
|
}
|
|
|
|
@GwtIncompatible // isPrime is GWT-incompatible
|
|
public void testIsPrimeSmall() {
|
|
// Check the first 1000 integers
|
|
for (int i = 2; i < 1000; i++) {
|
|
assertEquals(BigInteger.valueOf(i).isProbablePrime(100), LongMath.isPrime(i));
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // isPrime is GWT-incompatible
|
|
public void testIsPrimeManyConstants() {
|
|
// Test the thorough test inputs, which also includes special constants in the Miller-Rabin
|
|
// tests.
|
|
for (long l : POSITIVE_LONG_CANDIDATES) {
|
|
assertEquals(BigInteger.valueOf(l).isProbablePrime(100), LongMath.isPrime(l));
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // isPrime is GWT-incompatible
|
|
public void testIsPrimeOnUniformRandom() {
|
|
Random rand = new Random(1);
|
|
for (int bits = 10; bits < 63; bits++) {
|
|
for (int i = 0; i < 2000; i++) {
|
|
// A random long between 0 and Long.MAX_VALUE, inclusive.
|
|
long l = rand.nextLong() & ((1L << bits) - 1);
|
|
assertEquals(BigInteger.valueOf(l).isProbablePrime(100), LongMath.isPrime(l));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // isPrime is GWT-incompatible
|
|
public void testIsPrimeOnRandomPrimes() {
|
|
Random rand = new Random(1);
|
|
for (int bits = 10; bits < 63; bits++) {
|
|
for (int i = 0; i < 100; i++) {
|
|
long p = BigInteger.probablePrime(bits, rand).longValue();
|
|
assertTrue(LongMath.isPrime(p));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // isPrime is GWT-incompatible
|
|
public void testIsPrimeOnRandomComposites() {
|
|
Random rand = new Random(1);
|
|
for (int bits = 5; bits < 32; bits++) {
|
|
for (int i = 0; i < 100; i++) {
|
|
long p = BigInteger.probablePrime(bits, rand).longValue();
|
|
long q = BigInteger.probablePrime(bits, rand).longValue();
|
|
assertFalse(LongMath.isPrime(p * q));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible // isPrime is GWT-incompatible
|
|
public void testIsPrimeThrowsOnNegative() {
|
|
try {
|
|
LongMath.isPrime(-1);
|
|
fail("Expected IllegalArgumentException");
|
|
} catch (IllegalArgumentException expected) {
|
|
}
|
|
}
|
|
|
|
private static final long[] roundToDoubleTestCandidates = {
|
|
0,
|
|
16,
|
|
1L << 53,
|
|
(1L << 53) + 1,
|
|
(1L << 53) + 2,
|
|
(1L << 53) + 3,
|
|
(1L << 53) + 4,
|
|
1L << 54,
|
|
(1L << 54) + 1,
|
|
(1L << 54) + 2,
|
|
(1L << 54) + 3,
|
|
(1L << 54) + 4,
|
|
0x7ffffffffffffe00L, // halfway between 2^63 and next-lower double
|
|
0x7ffffffffffffe01L, // above + 1
|
|
0x7ffffffffffffdffL, // above - 1
|
|
Long.MAX_VALUE - (1L << 11) + 1,
|
|
Long.MAX_VALUE - 2,
|
|
Long.MAX_VALUE - 1,
|
|
Long.MAX_VALUE,
|
|
-16,
|
|
-1L << 53,
|
|
-(1L << 53) - 1,
|
|
-(1L << 53) - 2,
|
|
-(1L << 53) - 3,
|
|
-(1L << 53) - 4,
|
|
-1L << 54,
|
|
-(1L << 54) - 1,
|
|
-(1L << 54) - 2,
|
|
-(1L << 54) - 3,
|
|
-(1L << 54) - 4,
|
|
Long.MIN_VALUE + 2,
|
|
Long.MIN_VALUE + 1,
|
|
Long.MIN_VALUE
|
|
};
|
|
|
|
@GwtIncompatible
|
|
public void testRoundToDoubleAgainstBigInteger() {
|
|
for (RoundingMode roundingMode : EnumSet.complementOf(EnumSet.of(UNNECESSARY))) {
|
|
for (long candidate : roundToDoubleTestCandidates) {
|
|
assertThat(LongMath.roundToDouble(candidate, roundingMode))
|
|
.isEqualTo(BigIntegerMath.roundToDouble(BigInteger.valueOf(candidate), roundingMode));
|
|
}
|
|
}
|
|
}
|
|
|
|
@GwtIncompatible
|
|
public void testRoundToDoubleAgainstBigIntegerUnnecessary() {
|
|
for (long candidate : roundToDoubleTestCandidates) {
|
|
Double expectedDouble = null;
|
|
try {
|
|
expectedDouble = BigIntegerMath.roundToDouble(BigInteger.valueOf(candidate), UNNECESSARY);
|
|
} catch (ArithmeticException expected) {
|
|
// do nothing
|
|
}
|
|
|
|
if (expectedDouble != null) {
|
|
assertThat(LongMath.roundToDouble(candidate, UNNECESSARY)).isEqualTo(expectedDouble);
|
|
} else {
|
|
try {
|
|
LongMath.roundToDouble(candidate, UNNECESSARY);
|
|
fail("Expected ArithmeticException on roundToDouble(" + candidate + ", UNNECESSARY)");
|
|
} catch (ArithmeticException expected) {
|
|
// success
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void failFormat(String template, Object... args) {
|
|
assertWithMessage(template, args).fail();
|
|
}
|
|
}
|