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