1 /* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.math; 18 19 import static com.google.common.math.MathTesting.ALL_INTEGER_CANDIDATES; 20 import static com.google.common.math.MathTesting.ALL_LONG_CANDIDATES; 21 import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES; 22 import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES; 23 import static com.google.common.math.MathTesting.EXPONENTS; 24 import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES; 25 import static com.google.common.math.MathTesting.NEGATIVE_LONG_CANDIDATES; 26 import static com.google.common.math.MathTesting.NONZERO_LONG_CANDIDATES; 27 import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES; 28 import static com.google.common.math.MathTesting.POSITIVE_LONG_CANDIDATES; 29 import static java.math.BigInteger.valueOf; 30 import static java.math.RoundingMode.FLOOR; 31 import static java.math.RoundingMode.UNNECESSARY; 32 33 import com.google.common.testing.NullPointerTester; 34 35 import junit.framework.TestCase; 36 37 import java.math.BigDecimal; 38 import java.math.BigInteger; 39 import java.math.RoundingMode; 40 41 /** 42 * Tests for LongMath. 43 * 44 * @author Louis Wasserman 45 */ 46 public class LongMathTest extends TestCase { testConstantMaxPowerOfSqrt2Unsigned()47 public void testConstantMaxPowerOfSqrt2Unsigned() { 48 assertEquals(BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Long.SIZE - 1), FLOOR).longValue(), 49 LongMath.MAX_POWER_OF_SQRT2_UNSIGNED); 50 } 51 testConstantsPowersOf10()52 public void testConstantsPowersOf10() { 53 for (int i = 0; i < LongMath.POWERS_OF_10.length; i++) { 54 assertEquals(LongMath.checkedPow(10, i), LongMath.POWERS_OF_10[i]); 55 } 56 try { 57 LongMath.checkedPow(10, LongMath.POWERS_OF_10.length); 58 fail("Expected ArithmeticException"); 59 } catch (ArithmeticException expected) {} 60 } 61 testConstantsHalfPowersOf10()62 public void testConstantsHalfPowersOf10() { 63 for (int i = 0; i < LongMath.HALF_POWERS_OF_10.length; i++) { 64 assertEquals(BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * i + 1), FLOOR), 65 BigInteger.valueOf(LongMath.HALF_POWERS_OF_10[i])); 66 } 67 BigInteger nextBigger = 68 BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * LongMath.HALF_POWERS_OF_10.length + 1), FLOOR); 69 assertTrue(nextBigger.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0); 70 } 71 testConstantsSqrtMaxLong()72 public void testConstantsSqrtMaxLong() { 73 assertEquals(LongMath.sqrt(Long.MAX_VALUE, FLOOR), LongMath.FLOOR_SQRT_MAX_LONG); 74 } 75 testConstantsFactorials()76 public void testConstantsFactorials() { 77 long expected = 1; 78 for (int i = 0; i < LongMath.FACTORIALS.length; i++, expected *= i) { 79 assertEquals(expected, LongMath.FACTORIALS[i]); 80 } 81 try { 82 LongMath.checkedMultiply( 83 LongMath.FACTORIALS[LongMath.FACTORIALS.length - 1], LongMath.FACTORIALS.length); 84 fail("Expected ArithmeticException"); 85 } catch (ArithmeticException expect) {} 86 } 87 testConstantsBiggestBinomials()88 public void testConstantsBiggestBinomials() { 89 for (int k = 0; k < LongMath.BIGGEST_BINOMIALS.length; k++) { 90 assertTrue(fitsInLong(BigIntegerMath.binomial(LongMath.BIGGEST_BINOMIALS[k], k))); 91 assertTrue(LongMath.BIGGEST_BINOMIALS[k] == Integer.MAX_VALUE 92 || !fitsInLong(BigIntegerMath.binomial(LongMath.BIGGEST_BINOMIALS[k] + 1, k))); 93 // In the first case, any long is valid; in the second, we want to test that the next-bigger 94 // long overflows. 95 } 96 int k = LongMath.BIGGEST_BINOMIALS.length; 97 assertFalse(fitsInLong(BigIntegerMath.binomial(2 * k, k))); 98 // 2 * k is the smallest value for which we don't replace k with (n-k). 99 } 100 testConstantsBiggestSimpleBinomials()101 public void testConstantsBiggestSimpleBinomials() { 102 for (int k = 0; k < LongMath.BIGGEST_SIMPLE_BINOMIALS.length; k++) { 103 assertTrue(LongMath.BIGGEST_SIMPLE_BINOMIALS[k] <= LongMath.BIGGEST_BINOMIALS[k]); 104 simpleBinomial(LongMath.BIGGEST_SIMPLE_BINOMIALS[k], k); // mustn't throw 105 if (LongMath.BIGGEST_SIMPLE_BINOMIALS[k] < Integer.MAX_VALUE) { 106 // unless all n are fair game with this k 107 try { 108 simpleBinomial(LongMath.BIGGEST_SIMPLE_BINOMIALS[k] + 1, k); 109 fail("Expected ArithmeticException"); 110 } catch (ArithmeticException expected) {} 111 } 112 } 113 try { 114 int k = LongMath.BIGGEST_SIMPLE_BINOMIALS.length; 115 simpleBinomial(2 * k, k); 116 // 2 * k is the smallest value for which we don't replace k with (n-k). 117 fail("Expected ArithmeticException"); 118 } catch (ArithmeticException expected) {} 119 } 120 121 // Throws an ArithmeticException if "the simple implementation" of binomial coefficients overflows simpleBinomial(int n, int k)122 private long simpleBinomial(int n, int k) { 123 long accum = 1; 124 for (int i = 0; i < k; i++) { 125 accum = LongMath.checkedMultiply(accum, n - i); 126 accum /= i + 1; 127 } 128 return accum; 129 } 130 testIsPowerOfTwo()131 public void testIsPowerOfTwo() { 132 for (long x : ALL_LONG_CANDIDATES) { 133 // Checks for a single bit set. 134 boolean expected = x > 0 & (x & (x - 1)) == 0L; 135 assertEquals(expected, LongMath.isPowerOfTwo(x)); 136 } 137 } 138 testLog2ZeroAlwaysThrows()139 public void testLog2ZeroAlwaysThrows() { 140 for (RoundingMode mode : ALL_ROUNDING_MODES) { 141 try { 142 LongMath.log2(0L, mode); 143 fail("Expected IllegalArgumentException"); 144 } catch (IllegalArgumentException expected) {} 145 } 146 } 147 testLog2NegativeAlwaysThrows()148 public void testLog2NegativeAlwaysThrows() { 149 for (long x : NEGATIVE_LONG_CANDIDATES) { 150 for (RoundingMode mode : ALL_ROUNDING_MODES) { 151 try { 152 LongMath.log2(x, mode); 153 fail("Expected IllegalArgumentException"); 154 } catch (IllegalArgumentException expected) {} 155 } 156 } 157 } 158 159 /* Relies on the correctness of BigIntegerMath.log2 for all modes except UNNECESSARY. */ testLog2MatchesBigInteger()160 public void testLog2MatchesBigInteger() { 161 for (long x : POSITIVE_LONG_CANDIDATES) { 162 for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { 163 // The BigInteger implementation is tested separately, use it as the reference. 164 assertEquals(BigIntegerMath.log2(valueOf(x), mode), LongMath.log2(x, mode)); 165 } 166 } 167 } 168 169 /* Relies on the correctness of isPowerOfTwo(long). */ testLog2Exact()170 public void testLog2Exact() { 171 for (long x : POSITIVE_LONG_CANDIDATES) { 172 // We only expect an exception if x was not a power of 2. 173 boolean isPowerOf2 = LongMath.isPowerOfTwo(x); 174 try { 175 assertEquals(x, 1L << LongMath.log2(x, UNNECESSARY)); 176 assertTrue(isPowerOf2); 177 } catch (ArithmeticException e) { 178 assertFalse(isPowerOf2); 179 } 180 } 181 } 182 testLog10ZeroAlwaysThrows()183 public void testLog10ZeroAlwaysThrows() { 184 for (RoundingMode mode : ALL_ROUNDING_MODES) { 185 try { 186 LongMath.log10(0L, mode); 187 fail("Expected IllegalArgumentException"); 188 } catch (IllegalArgumentException expected) {} 189 } 190 } 191 testLog10NegativeAlwaysThrows()192 public void testLog10NegativeAlwaysThrows() { 193 for (long x : NEGATIVE_LONG_CANDIDATES) { 194 for (RoundingMode mode : ALL_ROUNDING_MODES) { 195 try { 196 LongMath.log10(x, mode); 197 fail("Expected IllegalArgumentException"); 198 } catch (IllegalArgumentException expected) {} 199 } 200 } 201 } 202 203 // Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY. testLog10MatchesBigInteger()204 public void testLog10MatchesBigInteger() { 205 for (long x : POSITIVE_LONG_CANDIDATES) { 206 for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { 207 assertEquals(BigIntegerMath.log10(valueOf(x), mode), LongMath.log10(x, mode)); 208 } 209 } 210 } 211 212 // Relies on the correctness of log10(long, FLOOR) and of pow(long, int). testLog10Exact()213 public void testLog10Exact() { 214 for (long x : POSITIVE_LONG_CANDIDATES) { 215 int floor = LongMath.log10(x, FLOOR); 216 boolean expectSuccess = LongMath.pow(10, floor) == x; 217 try { 218 assertEquals(floor, LongMath.log10(x, UNNECESSARY)); 219 assertTrue(expectSuccess); 220 } catch (ArithmeticException e) { 221 assertFalse(expectSuccess); 222 } 223 } 224 } 225 testLog10TrivialOnPowerOf10()226 public void testLog10TrivialOnPowerOf10() { 227 long x = 1000000000000L; 228 for (RoundingMode mode : ALL_ROUNDING_MODES) { 229 assertEquals(12, LongMath.log10(x, mode)); 230 } 231 } 232 testSqrtNegativeAlwaysThrows()233 public void testSqrtNegativeAlwaysThrows() { 234 for (long x : NEGATIVE_LONG_CANDIDATES) { 235 for (RoundingMode mode : ALL_ROUNDING_MODES) { 236 try { 237 LongMath.sqrt(x, mode); 238 fail("Expected IllegalArgumentException"); 239 } catch (IllegalArgumentException expected) {} 240 } 241 } 242 } 243 244 // Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY. testSqrtMatchesBigInteger()245 public void testSqrtMatchesBigInteger() { 246 for (long x : POSITIVE_LONG_CANDIDATES) { 247 for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { 248 // Promote the long value (rather than using longValue() on the expected value) to avoid 249 // any risk of truncation which could lead to a false positive. 250 assertEquals(BigIntegerMath.sqrt(valueOf(x), mode), valueOf(LongMath.sqrt(x, mode))); 251 } 252 } 253 } 254 255 /* Relies on the correctness of sqrt(long, FLOOR). */ testSqrtExactMatchesFloorOrThrows()256 public void testSqrtExactMatchesFloorOrThrows() { 257 for (long x : POSITIVE_LONG_CANDIDATES) { 258 long logFloor = LongMath.sqrt(x, FLOOR); 259 // We only expect an exception if x was not a perfect square. 260 boolean isPerfectSquare = (logFloor * logFloor == x); 261 try { 262 assertEquals(logFloor, LongMath.sqrt(x, UNNECESSARY)); 263 assertTrue(isPerfectSquare); 264 } catch (ArithmeticException e) { 265 assertFalse(isPerfectSquare); 266 } 267 } 268 } 269 testPow()270 public void testPow() { 271 for (long i : ALL_LONG_CANDIDATES) { 272 for (int exp : EXPONENTS) { 273 assertEquals(LongMath.pow(i, exp), valueOf(i) 274 .pow(exp) 275 .longValue()); 276 } 277 } 278 } 279 testDivNonZero()280 public void testDivNonZero() { 281 for (long p : NONZERO_LONG_CANDIDATES) { 282 for (long q : NONZERO_LONG_CANDIDATES) { 283 for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { 284 long expected = 285 new BigDecimal(valueOf(p)).divide(new BigDecimal(valueOf(q)), 0, mode).longValue(); 286 assertEquals(expected, LongMath.divide(p, q, mode)); 287 } 288 } 289 } 290 } 291 testDivNonZeroExact()292 public void testDivNonZeroExact() { 293 for (long p : NONZERO_LONG_CANDIDATES) { 294 for (long q : NONZERO_LONG_CANDIDATES) { 295 boolean dividesEvenly = (p % q) == 0L; 296 297 try { 298 assertEquals(p, LongMath.divide(p, q, UNNECESSARY) * q); 299 assertTrue(dividesEvenly); 300 } catch (ArithmeticException e) { 301 assertFalse(dividesEvenly); 302 } 303 } 304 } 305 } 306 testZeroDivIsAlwaysZero()307 public void testZeroDivIsAlwaysZero() { 308 for (long q : NONZERO_LONG_CANDIDATES) { 309 for (RoundingMode mode : ALL_ROUNDING_MODES) { 310 assertEquals(0L, LongMath.divide(0L, q, mode)); 311 } 312 } 313 } 314 testDivByZeroAlwaysFails()315 public void testDivByZeroAlwaysFails() { 316 for (long p : ALL_LONG_CANDIDATES) { 317 for (RoundingMode mode : ALL_ROUNDING_MODES) { 318 try { 319 LongMath.divide(p, 0L, mode); 320 fail("Expected ArithmeticException"); 321 } catch (ArithmeticException expected) {} 322 } 323 } 324 } 325 testIntMod()326 public void testIntMod() { 327 for (long x : ALL_LONG_CANDIDATES) { 328 for (int m : POSITIVE_INTEGER_CANDIDATES) { 329 assertEquals(valueOf(x) 330 .mod(valueOf(m)) 331 .intValue(), LongMath.mod(x, m)); 332 } 333 } 334 } 335 testIntModNegativeModulusFails()336 public void testIntModNegativeModulusFails() { 337 for (long x : ALL_LONG_CANDIDATES) { 338 for (int m : NEGATIVE_INTEGER_CANDIDATES) { 339 try { 340 LongMath.mod(x, m); 341 fail("Expected ArithmeticException"); 342 } catch (ArithmeticException expected) {} 343 } 344 } 345 } 346 testIntModZeroModulusFails()347 public void testIntModZeroModulusFails() { 348 for (long x : ALL_LONG_CANDIDATES) { 349 try { 350 LongMath.mod(x, 0); 351 fail("Expected AE"); 352 } catch (ArithmeticException expected) {} 353 } 354 } 355 testMod()356 public void testMod() { 357 for (long x : ALL_LONG_CANDIDATES) { 358 for (long m : POSITIVE_LONG_CANDIDATES) { 359 assertEquals(valueOf(x) 360 .mod(valueOf(m)) 361 .longValue(), LongMath.mod(x, m)); 362 } 363 } 364 } 365 testModNegativeModulusFails()366 public void testModNegativeModulusFails() { 367 for (long x : ALL_LONG_CANDIDATES) { 368 for (long m : NEGATIVE_LONG_CANDIDATES) { 369 try { 370 LongMath.mod(x, m); 371 fail("Expected ArithmeticException"); 372 } catch (ArithmeticException expected) {} 373 } 374 } 375 } 376 testGCD()377 public void testGCD() { 378 for (long a : POSITIVE_LONG_CANDIDATES) { 379 for (long b : POSITIVE_LONG_CANDIDATES) { 380 assertEquals(valueOf(a).gcd(valueOf(b)), valueOf(LongMath.gcd(a, b))); 381 } 382 } 383 } 384 testGCDZero()385 public void testGCDZero() { 386 for (long a : POSITIVE_LONG_CANDIDATES) { 387 assertEquals(a, LongMath.gcd(a, 0)); 388 assertEquals(a, LongMath.gcd(0, a)); 389 } 390 assertEquals(0, LongMath.gcd(0, 0)); 391 } 392 testGCDNegativePositiveThrows()393 public void testGCDNegativePositiveThrows() { 394 for (long a : NEGATIVE_LONG_CANDIDATES) { 395 try { 396 LongMath.gcd(a, 3); 397 fail("Expected IllegalArgumentException"); 398 } catch (IllegalArgumentException expected) {} 399 try { 400 LongMath.gcd(3, a); 401 fail("Expected IllegalArgumentException"); 402 } catch (IllegalArgumentException expected) {} 403 } 404 } 405 testGCDNegativeZeroThrows()406 public void testGCDNegativeZeroThrows() { 407 for (long a : NEGATIVE_LONG_CANDIDATES) { 408 try { 409 LongMath.gcd(a, 0); 410 fail("Expected IllegalArgumentException"); 411 } catch (IllegalArgumentException expected) {} 412 try { 413 LongMath.gcd(0, a); 414 fail("Expected IllegalArgumentException"); 415 } catch (IllegalArgumentException expected) {} 416 } 417 } 418 testCheckedAdd()419 public void testCheckedAdd() { 420 for (long a : ALL_INTEGER_CANDIDATES) { 421 for (long b : ALL_INTEGER_CANDIDATES) { 422 BigInteger expectedResult = valueOf(a).add(valueOf(b)); 423 boolean expectedSuccess = fitsInLong(expectedResult); 424 try { 425 assertEquals(a + b, LongMath.checkedAdd(a, b)); 426 assertTrue(expectedSuccess); 427 } catch (ArithmeticException e) { 428 assertFalse(expectedSuccess); 429 } 430 } 431 } 432 } 433 testCheckedSubtract()434 public void testCheckedSubtract() { 435 for (long a : ALL_INTEGER_CANDIDATES) { 436 for (long b : ALL_INTEGER_CANDIDATES) { 437 BigInteger expectedResult = valueOf(a).subtract(valueOf(b)); 438 boolean expectedSuccess = fitsInLong(expectedResult); 439 try { 440 assertEquals(a - b, LongMath.checkedSubtract(a, b)); 441 assertTrue(expectedSuccess); 442 } catch (ArithmeticException e) { 443 assertFalse(expectedSuccess); 444 } 445 } 446 } 447 } 448 testCheckedMultiply()449 public void testCheckedMultiply() { 450 for (long a : ALL_INTEGER_CANDIDATES) { 451 for (long b : ALL_INTEGER_CANDIDATES) { 452 BigInteger expectedResult = valueOf(a).multiply(valueOf(b)); 453 boolean expectedSuccess = fitsInLong(expectedResult); 454 try { 455 assertEquals(a * b, LongMath.checkedMultiply(a, b)); 456 assertTrue(expectedSuccess); 457 } catch (ArithmeticException e) { 458 assertFalse(expectedSuccess); 459 } 460 } 461 } 462 } 463 testCheckedPow()464 public void testCheckedPow() { 465 for (long b : ALL_INTEGER_CANDIDATES) { 466 for (int exp : EXPONENTS) { 467 BigInteger expectedResult = valueOf(b).pow(exp); 468 boolean expectedSuccess = fitsInLong(expectedResult); 469 try { 470 assertEquals(expectedResult.longValue(), LongMath.checkedPow(b, exp)); 471 assertTrue(expectedSuccess); 472 } catch (ArithmeticException e) { 473 assertFalse(expectedSuccess); 474 } 475 } 476 } 477 } 478 479 // Depends on the correctness of BigIntegerMath.factorial. testFactorial()480 public void testFactorial() { 481 for (int n = 0; n <= 50; n++) { 482 BigInteger expectedBig = BigIntegerMath.factorial(n); 483 long expectedLong = fitsInLong(expectedBig) ? expectedBig.longValue() : Long.MAX_VALUE; 484 assertEquals(expectedLong, LongMath.factorial(n)); 485 } 486 } 487 testFactorialNegative()488 public void testFactorialNegative() { 489 for (int n : NEGATIVE_INTEGER_CANDIDATES) { 490 try { 491 LongMath.factorial(n); 492 fail("Expected IllegalArgumentException"); 493 } catch (IllegalArgumentException expected) {} 494 } 495 } 496 497 // Depends on the correctness of BigIntegerMath.binomial. testBinomial()498 public void testBinomial() { 499 for (int n = 0; n <= 70; n++) { 500 for (int k = 0; k <= n; k++) { 501 BigInteger expectedBig = BigIntegerMath.binomial(n, k); 502 long expectedLong = fitsInLong(expectedBig) ? expectedBig.longValue() : Long.MAX_VALUE; 503 assertEquals(expectedLong, LongMath.binomial(n, k)); 504 } 505 } 506 } 507 testBinomialOutside()508 public void testBinomialOutside() { 509 for (int n = 0; n <= 50; n++) { 510 try { 511 LongMath.binomial(n, -1); 512 fail("Expected IllegalArgumentException"); 513 } catch (IllegalArgumentException expected) {} 514 try { 515 LongMath.binomial(n, n + 1); 516 fail("Expected IllegalArgumentException"); 517 } catch (IllegalArgumentException expected) {} 518 } 519 } 520 testBinomialNegative()521 public void testBinomialNegative() { 522 for (int n : NEGATIVE_INTEGER_CANDIDATES) { 523 try { 524 LongMath.binomial(n, 0); 525 fail("Expected IllegalArgumentException"); 526 } catch (IllegalArgumentException expected) {} 527 } 528 } 529 fitsInLong(BigInteger big)530 private boolean fitsInLong(BigInteger big) { 531 return big.bitLength() <= 63; 532 } 533 testNullPointers()534 public void testNullPointers() throws Exception { 535 NullPointerTester tester = new NullPointerTester(); 536 tester.setDefault(RoundingMode.class, FLOOR); 537 tester.setDefault(int.class, 1); 538 tester.setDefault(long.class, 1L); 539 tester.testAllPublicStaticMethods(LongMath.class); 540 } 541 } 542