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_BIGINTEGER_CANDIDATES; 20 import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES; 21 import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES; 22 import static com.google.common.math.MathTesting.NEGATIVE_BIGINTEGER_CANDIDATES; 23 import static com.google.common.math.MathTesting.NONZERO_BIGINTEGER_CANDIDATES; 24 import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES; 25 import static com.google.common.truth.Truth.assertThat; 26 import static com.google.common.truth.Truth.assertWithMessage; 27 import static java.math.BigInteger.ONE; 28 import static java.math.BigInteger.TEN; 29 import static java.math.BigInteger.ZERO; 30 import static java.math.RoundingMode.CEILING; 31 import static java.math.RoundingMode.DOWN; 32 import static java.math.RoundingMode.FLOOR; 33 import static java.math.RoundingMode.HALF_DOWN; 34 import static java.math.RoundingMode.HALF_EVEN; 35 import static java.math.RoundingMode.HALF_UP; 36 import static java.math.RoundingMode.UNNECESSARY; 37 import static java.math.RoundingMode.UP; 38 import static java.math.RoundingMode.values; 39 import static java.util.Arrays.asList; 40 41 import com.google.common.annotations.GwtCompatible; 42 import com.google.common.annotations.GwtIncompatible; 43 import com.google.common.annotations.J2ktIncompatible; 44 import com.google.common.testing.NullPointerTester; 45 import com.google.errorprone.annotations.CanIgnoreReturnValue; 46 import java.math.BigDecimal; 47 import java.math.BigInteger; 48 import java.math.RoundingMode; 49 import java.util.EnumMap; 50 import java.util.EnumSet; 51 import java.util.Map; 52 import junit.framework.TestCase; 53 54 /** 55 * Tests for BigIntegerMath. 56 * 57 * @author Louis Wasserman 58 */ 59 @ElementTypesAreNonnullByDefault 60 @GwtCompatible(emulated = true) 61 public class BigIntegerMathTest extends TestCase { testCeilingPowerOfTwo()62 public void testCeilingPowerOfTwo() { 63 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 64 BigInteger result = BigIntegerMath.ceilingPowerOfTwo(x); 65 assertTrue(BigIntegerMath.isPowerOfTwo(result)); 66 assertTrue(result.compareTo(x) >= 0); 67 assertTrue(result.compareTo(x.add(x)) < 0); 68 } 69 } 70 71 public void testFloorPowerOfTwo() { 72 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 73 BigInteger result = BigIntegerMath.floorPowerOfTwo(x); 74 assertTrue(BigIntegerMath.isPowerOfTwo(result)); 75 assertTrue(result.compareTo(x) <= 0); 76 assertTrue(result.add(result).compareTo(x) > 0); 77 } 78 } 79 80 public void testCeilingPowerOfTwoNegative() { 81 for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { 82 try { 83 BigIntegerMath.ceilingPowerOfTwo(x); 84 fail("Expected IllegalArgumentException"); 85 } catch (IllegalArgumentException expected) { 86 } 87 } 88 } 89 90 public void testFloorPowerOfTwoNegative() { 91 for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { 92 try { 93 BigIntegerMath.floorPowerOfTwo(x); 94 fail("Expected IllegalArgumentException"); 95 } catch (IllegalArgumentException expected) { 96 } 97 } 98 } 99 100 public void testCeilingPowerOfTwoZero() { 101 try { 102 BigIntegerMath.ceilingPowerOfTwo(BigInteger.ZERO); 103 fail("Expected IllegalArgumentException"); 104 } catch (IllegalArgumentException expected) { 105 } 106 } 107 108 public void testFloorPowerOfTwoZero() { 109 try { 110 BigIntegerMath.floorPowerOfTwo(BigInteger.ZERO); 111 fail("Expected IllegalArgumentException"); 112 } catch (IllegalArgumentException expected) { 113 } 114 } 115 116 @GwtIncompatible // TODO 117 public void testConstantSqrt2PrecomputedBits() { 118 assertEquals( 119 BigIntegerMath.sqrt( 120 BigInteger.ZERO.setBit(2 * BigIntegerMath.SQRT2_PRECOMPUTE_THRESHOLD + 1), FLOOR), 121 BigIntegerMath.SQRT2_PRECOMPUTED_BITS); 122 } 123 124 public void testIsPowerOfTwo() { 125 for (BigInteger x : ALL_BIGINTEGER_CANDIDATES) { 126 // Checks for a single bit set. 127 boolean expected = x.signum() > 0 & x.and(x.subtract(ONE)).equals(ZERO); 128 assertEquals(expected, BigIntegerMath.isPowerOfTwo(x)); 129 } 130 } 131 testLog2ZeroAlwaysThrows()132 public void testLog2ZeroAlwaysThrows() { 133 for (RoundingMode mode : ALL_ROUNDING_MODES) { 134 try { 135 BigIntegerMath.log2(ZERO, mode); 136 fail("Expected IllegalArgumentException"); 137 } catch (IllegalArgumentException expected) { 138 } 139 } 140 } 141 testLog2NegativeAlwaysThrows()142 public void testLog2NegativeAlwaysThrows() { 143 for (RoundingMode mode : ALL_ROUNDING_MODES) { 144 try { 145 BigIntegerMath.log2(BigInteger.valueOf(-1), mode); 146 fail("Expected IllegalArgumentException"); 147 } catch (IllegalArgumentException expected) { 148 } 149 } 150 } 151 testLog2Floor()152 public void testLog2Floor() { 153 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 154 for (RoundingMode mode : asList(FLOOR, DOWN)) { 155 int result = BigIntegerMath.log2(x, mode); 156 assertTrue(ZERO.setBit(result).compareTo(x) <= 0); 157 assertTrue(ZERO.setBit(result + 1).compareTo(x) > 0); 158 } 159 } 160 } 161 testLog2Ceiling()162 public void testLog2Ceiling() { 163 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 164 for (RoundingMode mode : asList(CEILING, UP)) { 165 int result = BigIntegerMath.log2(x, mode); 166 assertTrue(ZERO.setBit(result).compareTo(x) >= 0); 167 assertTrue(result == 0 || ZERO.setBit(result - 1).compareTo(x) < 0); 168 } 169 } 170 } 171 172 // Relies on the correctness of isPowerOfTwo(BigInteger). testLog2Exact()173 public void testLog2Exact() { 174 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 175 // We only expect an exception if x was not a power of 2. 176 boolean isPowerOf2 = BigIntegerMath.isPowerOfTwo(x); 177 try { 178 assertEquals(x, ZERO.setBit(BigIntegerMath.log2(x, UNNECESSARY))); 179 assertTrue(isPowerOf2); 180 } catch (ArithmeticException e) { 181 assertFalse(isPowerOf2); 182 } 183 } 184 } 185 testLog2HalfUp()186 public void testLog2HalfUp() { 187 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 188 int result = BigIntegerMath.log2(x, HALF_UP); 189 BigInteger x2 = x.pow(2); 190 // x^2 < 2^(2 * result + 1), or else we would have rounded up 191 assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) > 0); 192 // x^2 >= 2^(2 * result - 1), or else we would have rounded down 193 assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) <= 0); 194 } 195 } 196 testLog2HalfDown()197 public void testLog2HalfDown() { 198 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 199 int result = BigIntegerMath.log2(x, HALF_DOWN); 200 BigInteger x2 = x.pow(2); 201 // x^2 <= 2^(2 * result + 1), or else we would have rounded up 202 assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) >= 0); 203 // x^2 > 2^(2 * result - 1), or else we would have rounded down 204 assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) < 0); 205 } 206 } 207 208 // Relies on the correctness of log2(BigInteger, {HALF_UP,HALF_DOWN}). testLog2HalfEven()209 public void testLog2HalfEven() { 210 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 211 int halfEven = BigIntegerMath.log2(x, HALF_EVEN); 212 // Now figure out what rounding mode we should behave like (it depends if FLOOR was 213 // odd/even). 214 boolean floorWasEven = (BigIntegerMath.log2(x, FLOOR) & 1) == 0; 215 assertEquals(BigIntegerMath.log2(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); 216 } 217 } 218 219 @GwtIncompatible // TODO testLog10ZeroAlwaysThrows()220 public void testLog10ZeroAlwaysThrows() { 221 for (RoundingMode mode : ALL_ROUNDING_MODES) { 222 try { 223 BigIntegerMath.log10(ZERO, mode); 224 fail("Expected IllegalArgumentException"); 225 } catch (IllegalArgumentException expected) { 226 } 227 } 228 } 229 230 @GwtIncompatible // TODO testLog10NegativeAlwaysThrows()231 public void testLog10NegativeAlwaysThrows() { 232 for (RoundingMode mode : ALL_ROUNDING_MODES) { 233 try { 234 BigIntegerMath.log10(BigInteger.valueOf(-1), mode); 235 fail("Expected IllegalArgumentException"); 236 } catch (IllegalArgumentException expected) { 237 } 238 } 239 } 240 241 @GwtIncompatible // TODO testLog10Floor()242 public void testLog10Floor() { 243 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 244 for (RoundingMode mode : asList(FLOOR, DOWN)) { 245 int result = BigIntegerMath.log10(x, mode); 246 assertTrue(TEN.pow(result).compareTo(x) <= 0); 247 assertTrue(TEN.pow(result + 1).compareTo(x) > 0); 248 } 249 } 250 } 251 252 @GwtIncompatible // TODO testLog10Ceiling()253 public void testLog10Ceiling() { 254 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 255 for (RoundingMode mode : asList(CEILING, UP)) { 256 int result = BigIntegerMath.log10(x, mode); 257 assertTrue(TEN.pow(result).compareTo(x) >= 0); 258 assertTrue(result == 0 || TEN.pow(result - 1).compareTo(x) < 0); 259 } 260 } 261 } 262 263 // Relies on the correctness of log10(BigInteger, FLOOR). 264 @GwtIncompatible // TODO testLog10Exact()265 public void testLog10Exact() { 266 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 267 int logFloor = BigIntegerMath.log10(x, FLOOR); 268 boolean expectSuccess = TEN.pow(logFloor).equals(x); 269 try { 270 assertEquals(logFloor, BigIntegerMath.log10(x, UNNECESSARY)); 271 assertTrue(expectSuccess); 272 } catch (ArithmeticException e) { 273 assertFalse(expectSuccess); 274 } 275 } 276 } 277 278 @GwtIncompatible // TODO testLog10HalfUp()279 public void testLog10HalfUp() { 280 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 281 int result = BigIntegerMath.log10(x, HALF_UP); 282 BigInteger x2 = x.pow(2); 283 // x^2 < 10^(2 * result + 1), or else we would have rounded up 284 assertTrue(TEN.pow(2 * result + 1).compareTo(x2) > 0); 285 // x^2 >= 10^(2 * result - 1), or else we would have rounded down 286 assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) <= 0); 287 } 288 } 289 290 @GwtIncompatible // TODO testLog10HalfDown()291 public void testLog10HalfDown() { 292 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 293 int result = BigIntegerMath.log10(x, HALF_DOWN); 294 BigInteger x2 = x.pow(2); 295 // x^2 <= 10^(2 * result + 1), or else we would have rounded up 296 assertTrue(TEN.pow(2 * result + 1).compareTo(x2) >= 0); 297 // x^2 > 10^(2 * result - 1), or else we would have rounded down 298 assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) < 0); 299 } 300 } 301 302 // Relies on the correctness of log10(BigInteger, {HALF_UP,HALF_DOWN}). 303 @GwtIncompatible // TODO testLog10HalfEven()304 public void testLog10HalfEven() { 305 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 306 int halfEven = BigIntegerMath.log10(x, HALF_EVEN); 307 // Now figure out what rounding mode we should behave like (it depends if FLOOR was 308 // odd/even). 309 boolean floorWasEven = (BigIntegerMath.log10(x, FLOOR) & 1) == 0; 310 assertEquals(BigIntegerMath.log10(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); 311 } 312 } 313 314 @GwtIncompatible // TODO testLog10TrivialOnPowerOf10()315 public void testLog10TrivialOnPowerOf10() { 316 BigInteger x = BigInteger.TEN.pow(100); 317 for (RoundingMode mode : ALL_ROUNDING_MODES) { 318 assertEquals(100, BigIntegerMath.log10(x, mode)); 319 } 320 } 321 322 @GwtIncompatible // TODO testSqrtZeroAlwaysZero()323 public void testSqrtZeroAlwaysZero() { 324 for (RoundingMode mode : ALL_ROUNDING_MODES) { 325 assertEquals(ZERO, BigIntegerMath.sqrt(ZERO, mode)); 326 } 327 } 328 329 @GwtIncompatible // TODO testSqrtNegativeAlwaysThrows()330 public void testSqrtNegativeAlwaysThrows() { 331 for (RoundingMode mode : ALL_ROUNDING_MODES) { 332 try { 333 BigIntegerMath.sqrt(BigInteger.valueOf(-1), mode); 334 fail("Expected IllegalArgumentException"); 335 } catch (IllegalArgumentException expected) { 336 } 337 } 338 } 339 340 @GwtIncompatible // TODO testSqrtFloor()341 public void testSqrtFloor() { 342 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 343 for (RoundingMode mode : asList(FLOOR, DOWN)) { 344 BigInteger result = BigIntegerMath.sqrt(x, mode); 345 assertTrue(result.compareTo(ZERO) > 0); 346 assertTrue(result.pow(2).compareTo(x) <= 0); 347 assertTrue(result.add(ONE).pow(2).compareTo(x) > 0); 348 } 349 } 350 } 351 352 @GwtIncompatible // TODO testSqrtCeiling()353 public void testSqrtCeiling() { 354 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 355 for (RoundingMode mode : asList(CEILING, UP)) { 356 BigInteger result = BigIntegerMath.sqrt(x, mode); 357 assertTrue(result.compareTo(ZERO) > 0); 358 assertTrue(result.pow(2).compareTo(x) >= 0); 359 assertTrue(result.signum() == 0 || result.subtract(ONE).pow(2).compareTo(x) < 0); 360 } 361 } 362 } 363 364 // Relies on the correctness of sqrt(BigInteger, FLOOR). 365 @GwtIncompatible // TODO 366 public void testSqrtExact() { 367 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 368 BigInteger floor = BigIntegerMath.sqrt(x, FLOOR); 369 // We only expect an exception if x was not a perfect square. 370 boolean isPerfectSquare = floor.pow(2).equals(x); 371 try { 372 assertEquals(floor, BigIntegerMath.sqrt(x, UNNECESSARY)); 373 assertTrue(isPerfectSquare); 374 } catch (ArithmeticException e) { 375 assertFalse(isPerfectSquare); 376 } 377 } 378 } 379 380 @GwtIncompatible // TODO 381 public void testSqrtHalfUp() { 382 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 383 BigInteger result = BigIntegerMath.sqrt(x, HALF_UP); 384 BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); 385 BigInteger x4 = x.shiftLeft(2); 386 // sqrt(x) < result + 0.5, so 4 * x < (result + 0.5)^2 * 4 387 // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 388 assertTrue(x4.compareTo(plusHalfSquared) < 0); 389 BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); 390 // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 391 // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 392 assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) >= 0); 393 } 394 } 395 396 @GwtIncompatible // TODO 397 public void testSqrtHalfDown() { 398 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 399 BigInteger result = BigIntegerMath.sqrt(x, HALF_DOWN); 400 BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); 401 BigInteger x4 = x.shiftLeft(2); 402 // sqrt(x) <= result + 0.5, so 4 * x <= (result + 0.5)^2 * 4 403 // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 404 assertTrue(x4.compareTo(plusHalfSquared) <= 0); 405 BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); 406 // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 407 // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 408 assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) > 0); 409 } 410 } 411 412 // Relies on the correctness of sqrt(BigInteger, {HALF_UP,HALF_DOWN}). 413 @GwtIncompatible // TODO 414 public void testSqrtHalfEven() { 415 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 416 BigInteger halfEven = BigIntegerMath.sqrt(x, HALF_EVEN); 417 // Now figure out what rounding mode we should behave like (it depends if FLOOR was 418 // odd/even). 419 boolean floorWasOdd = BigIntegerMath.sqrt(x, FLOOR).testBit(0); 420 assertEquals(BigIntegerMath.sqrt(x, floorWasOdd ? HALF_UP : HALF_DOWN), halfEven); 421 } 422 } 423 424 @GwtIncompatible // TODO 425 @AndroidIncompatible // slow 426 public void testDivNonZero() { 427 for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { 428 for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { 429 for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { 430 BigInteger expected = 431 new BigDecimal(p).divide(new BigDecimal(q), 0, mode).toBigIntegerExact(); 432 assertEquals(expected, BigIntegerMath.divide(p, q, mode)); 433 } 434 } 435 } 436 } 437 438 private static final BigInteger BAD_FOR_ANDROID_P = new BigInteger("-9223372036854775808"); 439 private static final BigInteger BAD_FOR_ANDROID_Q = new BigInteger("-1"); 440 441 @GwtIncompatible // TODO 442 @AndroidIncompatible // slow 443 public void testDivNonZeroExact() { 444 String runtimeName = System.getProperty("java.runtime.name"); 445 boolean isAndroid = runtimeName != null && runtimeName.contains("Android"); 446 for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { 447 for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { 448 if (isAndroid && p.equals(BAD_FOR_ANDROID_P) && q.equals(BAD_FOR_ANDROID_Q)) { 449 // https://issuetracker.google.com/issues/37074172 450 continue; 451 } 452 453 boolean dividesEvenly = p.remainder(q).equals(ZERO); 454 455 try { 456 BigInteger quotient = BigIntegerMath.divide(p, q, UNNECESSARY); 457 BigInteger undone = quotient.multiply(q); 458 if (!p.equals(undone)) { 459 failFormat("expected %s.multiply(%s) = %s; got %s", quotient, q, p, undone); 460 } 461 assertTrue(dividesEvenly); 462 } catch (ArithmeticException e) { 463 assertFalse(dividesEvenly); 464 } 465 } 466 } 467 } 468 469 @GwtIncompatible // TODO 470 public void testZeroDivIsAlwaysZero() { 471 for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { 472 for (RoundingMode mode : ALL_ROUNDING_MODES) { 473 assertEquals(ZERO, BigIntegerMath.divide(ZERO, q, mode)); 474 } 475 } 476 } 477 478 @GwtIncompatible // TODO 479 public void testDivByZeroAlwaysFails() { 480 for (BigInteger p : ALL_BIGINTEGER_CANDIDATES) { 481 for (RoundingMode mode : ALL_ROUNDING_MODES) { 482 try { 483 BigIntegerMath.divide(p, ZERO, mode); 484 fail("Expected ArithmeticException"); 485 } catch (ArithmeticException expected) { 486 } 487 } 488 } 489 } 490 491 public void testFactorial() { 492 BigInteger expected = BigInteger.ONE; 493 for (int i = 1; i <= 200; i++) { 494 expected = expected.multiply(BigInteger.valueOf(i)); 495 assertEquals(expected, BigIntegerMath.factorial(i)); 496 } 497 } 498 499 public void testFactorial0() { 500 assertEquals(BigInteger.ONE, BigIntegerMath.factorial(0)); 501 } 502 503 public void testFactorialNegative() { 504 try { 505 BigIntegerMath.factorial(-1); 506 fail("Expected IllegalArgumentException"); 507 } catch (IllegalArgumentException expected) { 508 } 509 } 510 511 public void testBinomialSmall() { 512 runBinomialTest(0, 30); 513 } 514 515 @GwtIncompatible // too slow 516 public void testBinomialLarge() { 517 runBinomialTest(31, 100); 518 } 519 520 // Depends on the correctness of BigIntegerMath.factorial 521 private static void runBinomialTest(int firstN, int lastN) { 522 for (int n = firstN; n <= lastN; n++) { 523 for (int k = 0; k <= n; k++) { 524 BigInteger expected = 525 BigIntegerMath.factorial(n) 526 .divide(BigIntegerMath.factorial(k)) 527 .divide(BigIntegerMath.factorial(n - k)); 528 assertEquals(expected, BigIntegerMath.binomial(n, k)); 529 } 530 } 531 } 532 533 public void testBinomialOutside() { 534 for (int n = 0; n <= 50; n++) { 535 try { 536 BigIntegerMath.binomial(n, -1); 537 fail("Expected IllegalArgumentException"); 538 } catch (IllegalArgumentException expected) { 539 } 540 try { 541 BigIntegerMath.binomial(n, n + 1); 542 fail("Expected IllegalArgumentException"); 543 } catch (IllegalArgumentException expected) { 544 } 545 } 546 } 547 548 @J2ktIncompatible 549 @GwtIncompatible // EnumSet.complementOf 550 private static final class RoundToDoubleTester { 551 private final BigInteger input; 552 private final Map<RoundingMode, Double> expectedValues = new EnumMap<>(RoundingMode.class); 553 private boolean unnecessaryShouldThrow = false; 554 555 RoundToDoubleTester(BigInteger input) { 556 this.input = input; 557 } 558 559 @CanIgnoreReturnValue 560 RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) { 561 for (RoundingMode mode : modes) { 562 Double previous = expectedValues.put(mode, expectedValue); 563 if (previous != null) { 564 throw new AssertionError(); 565 } 566 } 567 return this; 568 } 569 570 @CanIgnoreReturnValue 571 public RoundToDoubleTester roundUnnecessaryShouldThrow() { 572 unnecessaryShouldThrow = true; 573 return this; 574 } 575 576 public void test() { 577 assertThat(expectedValues.keySet()) 578 .containsAtLeastElementsIn(EnumSet.complementOf(EnumSet.of(UNNECESSARY))); 579 for (Map.Entry<RoundingMode, Double> entry : expectedValues.entrySet()) { 580 RoundingMode mode = entry.getKey(); 581 Double expectation = entry.getValue(); 582 assertWithMessage("roundToDouble(" + input + ", " + mode + ")") 583 .that(BigIntegerMath.roundToDouble(input, mode)) 584 .isEqualTo(expectation); 585 } 586 587 if (!expectedValues.containsKey(UNNECESSARY)) { 588 assertWithMessage("Expected roundUnnecessaryShouldThrow call") 589 .that(unnecessaryShouldThrow) 590 .isTrue(); 591 try { 592 BigIntegerMath.roundToDouble(input, UNNECESSARY); 593 fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)"); 594 } catch (ArithmeticException expected) { 595 // expected 596 } 597 } 598 } 599 } 600 601 @J2ktIncompatible 602 @GwtIncompatible 603 public void testRoundToDouble_zero() { 604 new RoundToDoubleTester(BigInteger.ZERO).setExpectation(0.0, values()).test(); 605 } 606 607 @J2ktIncompatible 608 @GwtIncompatible 609 public void testRoundToDouble_smallPositive() { 610 new RoundToDoubleTester(BigInteger.valueOf(16)).setExpectation(16.0, values()).test(); 611 } 612 613 @J2ktIncompatible 614 @GwtIncompatible 615 public void testRoundToDouble_maxPreciselyRepresentable() { 616 new RoundToDoubleTester(BigInteger.valueOf(1L << 53)) 617 .setExpectation(Math.pow(2, 53), values()) 618 .test(); 619 } 620 621 @J2ktIncompatible 622 @GwtIncompatible 623 public void testRoundToDouble_maxPreciselyRepresentablePlusOne() { 624 double twoToThe53 = Math.pow(2, 53); 625 // the representable doubles are 2^53 and 2^53 + 2. 626 // 2^53+1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. 627 new RoundToDoubleTester(BigInteger.valueOf((1L << 53) + 1)) 628 .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN) 629 .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP) 630 .roundUnnecessaryShouldThrow() 631 .test(); 632 } 633 634 @J2ktIncompatible 635 @GwtIncompatible 636 public void testRoundToDouble_twoToThe54PlusOne() { 637 double twoToThe54 = Math.pow(2, 54); 638 // the representable doubles are 2^54 and 2^54 + 4 639 // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down. 640 new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 1)) 641 .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN) 642 .setExpectation(Math.nextUp(twoToThe54), CEILING, UP) 643 .roundUnnecessaryShouldThrow() 644 .test(); 645 } 646 647 @J2ktIncompatible 648 @GwtIncompatible 649 public void testRoundToDouble_twoToThe54PlusThree() { 650 double twoToThe54 = Math.pow(2, 54); 651 // the representable doubles are 2^54 and 2^54 + 4 652 // 2^54+3 is more than halfway between, so HALF_DOWN and HALF_UP will both go up. 653 new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 3)) 654 .setExpectation(twoToThe54, DOWN, FLOOR) 655 .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN) 656 .roundUnnecessaryShouldThrow() 657 .test(); 658 } 659 660 @J2ktIncompatible 661 @GwtIncompatible 662 public void testRoundToDouble_twoToThe54PlusFour() { 663 new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 4)) 664 .setExpectation(Math.pow(2, 54) + 4, values()) 665 .test(); 666 } 667 668 @J2ktIncompatible 669 @GwtIncompatible 670 public void testRoundToDouble_maxDouble() { 671 BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY); 672 new RoundToDoubleTester(maxDoubleAsBI).setExpectation(Double.MAX_VALUE, values()).test(); 673 } 674 675 @J2ktIncompatible 676 @GwtIncompatible 677 public void testRoundToDouble_maxDoublePlusOne() { 678 BigInteger maxDoubleAsBI = 679 DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY).add(BigInteger.ONE); 680 new RoundToDoubleTester(maxDoubleAsBI) 681 .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) 682 .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) 683 .roundUnnecessaryShouldThrow() 684 .test(); 685 } 686 687 @J2ktIncompatible 688 @GwtIncompatible 689 public void testRoundToDouble_wayTooBig() { 690 BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT); 691 new RoundToDoubleTester(bi) 692 .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) 693 .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) 694 .roundUnnecessaryShouldThrow() 695 .test(); 696 } 697 698 @J2ktIncompatible 699 @GwtIncompatible 700 public void testRoundToDouble_smallNegative() { 701 new RoundToDoubleTester(BigInteger.valueOf(-16)).setExpectation(-16.0, values()).test(); 702 } 703 704 @J2ktIncompatible 705 @GwtIncompatible 706 public void testRoundToDouble_minPreciselyRepresentable() { 707 new RoundToDoubleTester(BigInteger.valueOf(-1L << 53)) 708 .setExpectation(-Math.pow(2, 53), values()) 709 .test(); 710 } 711 712 @J2ktIncompatible 713 @GwtIncompatible 714 public void testRoundToDouble_minPreciselyRepresentableMinusOne() { 715 // the representable doubles are -2^53 and -2^53 - 2. 716 // -2^53-1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. 717 new RoundToDoubleTester(BigInteger.valueOf((-1L << 53) - 1)) 718 .setExpectation(-Math.pow(2, 53), DOWN, CEILING, HALF_DOWN, HALF_EVEN) 719 .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 53)), FLOOR, UP, HALF_UP) 720 .roundUnnecessaryShouldThrow() 721 .test(); 722 } 723 724 @J2ktIncompatible 725 @GwtIncompatible 726 public void testRoundToDouble_negativeTwoToThe54MinusOne() { 727 new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 1)) 728 .setExpectation(-Math.pow(2, 54), DOWN, CEILING, HALF_DOWN, HALF_UP, HALF_EVEN) 729 .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP) 730 .roundUnnecessaryShouldThrow() 731 .test(); 732 } 733 734 @J2ktIncompatible 735 @GwtIncompatible 736 public void testRoundToDouble_negativeTwoToThe54MinusThree() { 737 new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 3)) 738 .setExpectation(-Math.pow(2, 54), DOWN, CEILING) 739 .setExpectation( 740 DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP, HALF_DOWN, HALF_UP, HALF_EVEN) 741 .roundUnnecessaryShouldThrow() 742 .test(); 743 } 744 745 @J2ktIncompatible 746 @GwtIncompatible 747 public void testRoundToDouble_negativeTwoToThe54MinusFour() { 748 new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 4)) 749 .setExpectation(-Math.pow(2, 54) - 4, values()) 750 .test(); 751 } 752 753 @J2ktIncompatible 754 @GwtIncompatible 755 public void testRoundToDouble_minDouble() { 756 BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY); 757 new RoundToDoubleTester(minDoubleAsBI).setExpectation(-Double.MAX_VALUE, values()).test(); 758 } 759 760 @J2ktIncompatible 761 @GwtIncompatible 762 public void testRoundToDouble_minDoubleMinusOne() { 763 BigInteger minDoubleAsBI = 764 DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY).subtract(BigInteger.ONE); 765 new RoundToDoubleTester(minDoubleAsBI) 766 .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) 767 .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) 768 .roundUnnecessaryShouldThrow() 769 .test(); 770 } 771 772 @J2ktIncompatible 773 @GwtIncompatible 774 public void testRoundToDouble_negativeWayTooBig() { 775 BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT).negate(); 776 new RoundToDoubleTester(bi) 777 .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) 778 .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) 779 .roundUnnecessaryShouldThrow() 780 .test(); 781 } 782 783 @J2ktIncompatible 784 @GwtIncompatible // NullPointerTester 785 public void testNullPointers() { 786 NullPointerTester tester = new NullPointerTester(); 787 tester.setDefault(BigInteger.class, ONE); 788 tester.setDefault(int.class, 1); 789 tester.setDefault(long.class, 1L); 790 tester.testAllPublicStaticMethods(BigIntegerMath.class); 791 } 792 793 @GwtIncompatible // String.format 794 private static void failFormat(String template, Object... args) { 795 fail(String.format(template, args)); 796 } 797 } 798