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.NONZERO_BIGINTEGER_CANDIDATES; 23 import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES; 24 import static java.math.BigInteger.ONE; 25 import static java.math.BigInteger.TEN; 26 import static java.math.BigInteger.ZERO; 27 import static java.math.RoundingMode.CEILING; 28 import static java.math.RoundingMode.DOWN; 29 import static java.math.RoundingMode.FLOOR; 30 import static java.math.RoundingMode.HALF_DOWN; 31 import static java.math.RoundingMode.HALF_EVEN; 32 import static java.math.RoundingMode.HALF_UP; 33 import static java.math.RoundingMode.UNNECESSARY; 34 import static java.math.RoundingMode.UP; 35 import static java.util.Arrays.asList; 36 37 import com.google.common.annotations.GwtCompatible; 38 import com.google.common.annotations.GwtIncompatible; 39 import com.google.common.testing.NullPointerTester; 40 41 import junit.framework.TestCase; 42 43 import java.math.BigDecimal; 44 import java.math.BigInteger; 45 import java.math.RoundingMode; 46 47 /** 48 * Tests for BigIntegerMath. 49 * 50 * @author Louis Wasserman 51 */ 52 @GwtCompatible(emulated = true) 53 public class BigIntegerMathTest extends TestCase { 54 @GwtIncompatible("TODO") testConstantSqrt2PrecomputedBits()55 public void testConstantSqrt2PrecomputedBits() { 56 assertEquals(BigIntegerMath.sqrt( 57 BigInteger.ZERO.setBit(2 * BigIntegerMath.SQRT2_PRECOMPUTE_THRESHOLD + 1), FLOOR), 58 BigIntegerMath.SQRT2_PRECOMPUTED_BITS); 59 } 60 testIsPowerOfTwo()61 public void testIsPowerOfTwo() { 62 for (BigInteger x : ALL_BIGINTEGER_CANDIDATES) { 63 // Checks for a single bit set. 64 boolean expected = x.signum() > 0 & x.and(x.subtract(ONE)).equals(ZERO); 65 assertEquals(expected, BigIntegerMath.isPowerOfTwo(x)); 66 } 67 } 68 testLog2ZeroAlwaysThrows()69 public void testLog2ZeroAlwaysThrows() { 70 for (RoundingMode mode : ALL_ROUNDING_MODES) { 71 try { 72 BigIntegerMath.log2(ZERO, mode); 73 fail("Expected IllegalArgumentException"); 74 } catch (IllegalArgumentException expected) {} 75 } 76 } 77 testLog2NegativeAlwaysThrows()78 public void testLog2NegativeAlwaysThrows() { 79 for (RoundingMode mode : ALL_ROUNDING_MODES) { 80 try { 81 BigIntegerMath.log2(BigInteger.valueOf(-1), mode); 82 fail("Expected IllegalArgumentException"); 83 } catch (IllegalArgumentException expected) {} 84 } 85 } 86 testLog2Floor()87 public void testLog2Floor() { 88 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 89 for (RoundingMode mode : asList(FLOOR, DOWN)) { 90 int result = BigIntegerMath.log2(x, mode); 91 assertTrue(ZERO.setBit(result).compareTo(x) <= 0); 92 assertTrue(ZERO.setBit(result + 1).compareTo(x) > 0); 93 } 94 } 95 } 96 testLog2Ceiling()97 public void testLog2Ceiling() { 98 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 99 for (RoundingMode mode : asList(CEILING, UP)) { 100 int result = BigIntegerMath.log2(x, mode); 101 assertTrue(ZERO.setBit(result).compareTo(x) >= 0); 102 assertTrue(result == 0 || ZERO.setBit(result - 1).compareTo(x) < 0); 103 } 104 } 105 } 106 107 // Relies on the correctness of isPowerOfTwo(BigInteger). testLog2Exact()108 public void testLog2Exact() { 109 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 110 // We only expect an exception if x was not a power of 2. 111 boolean isPowerOf2 = BigIntegerMath.isPowerOfTwo(x); 112 try { 113 assertEquals(x, ZERO.setBit(BigIntegerMath.log2(x, UNNECESSARY))); 114 assertTrue(isPowerOf2); 115 } catch (ArithmeticException e) { 116 assertFalse(isPowerOf2); 117 } 118 } 119 } 120 testLog2HalfUp()121 public void testLog2HalfUp() { 122 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 123 int result = BigIntegerMath.log2(x, HALF_UP); 124 BigInteger x2 = x.pow(2); 125 // x^2 < 2^(2 * result + 1), or else we would have rounded up 126 assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) > 0); 127 // x^2 >= 2^(2 * result - 1), or else we would have rounded down 128 assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) <= 0); 129 } 130 } 131 testLog2HalfDown()132 public void testLog2HalfDown() { 133 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 134 int result = BigIntegerMath.log2(x, HALF_DOWN); 135 BigInteger x2 = x.pow(2); 136 // x^2 <= 2^(2 * result + 1), or else we would have rounded up 137 assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) >= 0); 138 // x^2 > 2^(2 * result - 1), or else we would have rounded down 139 assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) < 0); 140 } 141 } 142 143 // Relies on the correctness of log2(BigInteger, {HALF_UP,HALF_DOWN}). testLog2HalfEven()144 public void testLog2HalfEven() { 145 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 146 int halfEven = BigIntegerMath.log2(x, HALF_EVEN); 147 // Now figure out what rounding mode we should behave like (it depends if FLOOR was 148 // odd/even). 149 boolean floorWasEven = (BigIntegerMath.log2(x, FLOOR) & 1) == 0; 150 assertEquals(BigIntegerMath.log2(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); 151 } 152 } 153 154 @GwtIncompatible("TODO") testLog10ZeroAlwaysThrows()155 public void testLog10ZeroAlwaysThrows() { 156 for (RoundingMode mode : ALL_ROUNDING_MODES) { 157 try { 158 BigIntegerMath.log10(ZERO, mode); 159 fail("Expected IllegalArgumentException"); 160 } catch (IllegalArgumentException expected) {} 161 } 162 } 163 164 @GwtIncompatible("TODO") testLog10NegativeAlwaysThrows()165 public void testLog10NegativeAlwaysThrows() { 166 for (RoundingMode mode : ALL_ROUNDING_MODES) { 167 try { 168 BigIntegerMath.log10(BigInteger.valueOf(-1), mode); 169 fail("Expected IllegalArgumentException"); 170 } catch (IllegalArgumentException expected) {} 171 } 172 } 173 174 @GwtIncompatible("TODO") testLog10Floor()175 public void testLog10Floor() { 176 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 177 for (RoundingMode mode : asList(FLOOR, DOWN)) { 178 int result = BigIntegerMath.log10(x, mode); 179 assertTrue(TEN.pow(result).compareTo(x) <= 0); 180 assertTrue(TEN.pow(result + 1).compareTo(x) > 0); 181 } 182 } 183 } 184 185 @GwtIncompatible("TODO") testLog10Ceiling()186 public void testLog10Ceiling() { 187 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 188 for (RoundingMode mode : asList(CEILING, UP)) { 189 int result = BigIntegerMath.log10(x, mode); 190 assertTrue(TEN.pow(result).compareTo(x) >= 0); 191 assertTrue(result == 0 || TEN.pow(result - 1).compareTo(x) < 0); 192 } 193 } 194 } 195 196 // Relies on the correctness of log10(BigInteger, FLOOR). 197 @GwtIncompatible("TODO") testLog10Exact()198 public void testLog10Exact() { 199 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 200 int logFloor = BigIntegerMath.log10(x, FLOOR); 201 boolean expectSuccess = TEN.pow(logFloor).equals(x); 202 try { 203 assertEquals(logFloor, BigIntegerMath.log10(x, UNNECESSARY)); 204 assertTrue(expectSuccess); 205 } catch (ArithmeticException e) { 206 assertFalse(expectSuccess); 207 } 208 } 209 } 210 211 @GwtIncompatible("TODO") testLog10HalfUp()212 public void testLog10HalfUp() { 213 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 214 int result = BigIntegerMath.log10(x, HALF_UP); 215 BigInteger x2 = x.pow(2); 216 // x^2 < 10^(2 * result + 1), or else we would have rounded up 217 assertTrue(TEN.pow(2 * result + 1).compareTo(x2) > 0); 218 // x^2 >= 10^(2 * result - 1), or else we would have rounded down 219 assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) <= 0); 220 } 221 } 222 223 @GwtIncompatible("TODO") testLog10HalfDown()224 public void testLog10HalfDown() { 225 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 226 int result = BigIntegerMath.log10(x, HALF_DOWN); 227 BigInteger x2 = x.pow(2); 228 // x^2 <= 10^(2 * result + 1), or else we would have rounded up 229 assertTrue(TEN.pow(2 * result + 1).compareTo(x2) >= 0); 230 // x^2 > 10^(2 * result - 1), or else we would have rounded down 231 assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) < 0); 232 } 233 } 234 235 // Relies on the correctness of log10(BigInteger, {HALF_UP,HALF_DOWN}). 236 @GwtIncompatible("TODO") testLog10HalfEven()237 public void testLog10HalfEven() { 238 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 239 int halfEven = BigIntegerMath.log10(x, HALF_EVEN); 240 // Now figure out what rounding mode we should behave like (it depends if FLOOR was 241 // odd/even). 242 boolean floorWasEven = (BigIntegerMath.log10(x, FLOOR) & 1) == 0; 243 assertEquals(BigIntegerMath.log10(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); 244 } 245 } 246 247 @GwtIncompatible("TODO") testLog10TrivialOnPowerOf10()248 public void testLog10TrivialOnPowerOf10() { 249 BigInteger x = BigInteger.TEN.pow(100); 250 for (RoundingMode mode : ALL_ROUNDING_MODES) { 251 assertEquals(100, BigIntegerMath.log10(x, mode)); 252 } 253 } 254 255 @GwtIncompatible("TODO") testSqrtZeroAlwaysZero()256 public void testSqrtZeroAlwaysZero() { 257 for (RoundingMode mode : ALL_ROUNDING_MODES) { 258 assertEquals(ZERO, BigIntegerMath.sqrt(ZERO, mode)); 259 } 260 } 261 262 @GwtIncompatible("TODO") testSqrtNegativeAlwaysThrows()263 public void testSqrtNegativeAlwaysThrows() { 264 for (RoundingMode mode : ALL_ROUNDING_MODES) { 265 try { 266 BigIntegerMath.sqrt(BigInteger.valueOf(-1), mode); 267 fail("Expected IllegalArgumentException"); 268 } catch (IllegalArgumentException expected) {} 269 } 270 } 271 272 @GwtIncompatible("TODO") testSqrtFloor()273 public void testSqrtFloor() { 274 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 275 for (RoundingMode mode : asList(FLOOR, DOWN)) { 276 BigInteger result = BigIntegerMath.sqrt(x, mode); 277 assertTrue(result.compareTo(ZERO) > 0); 278 assertTrue(result.pow(2).compareTo(x) <= 0); 279 assertTrue(result.add(ONE).pow(2).compareTo(x) > 0); 280 } 281 } 282 } 283 284 @GwtIncompatible("TODO") testSqrtCeiling()285 public void testSqrtCeiling() { 286 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 287 for (RoundingMode mode : asList(CEILING, UP)) { 288 BigInteger result = BigIntegerMath.sqrt(x, mode); 289 assertTrue(result.compareTo(ZERO) > 0); 290 assertTrue(result.pow(2).compareTo(x) >= 0); 291 assertTrue(result.signum() == 0 || result.subtract(ONE).pow(2).compareTo(x) < 0); 292 } 293 } 294 } 295 296 // Relies on the correctness of sqrt(BigInteger, FLOOR). 297 @GwtIncompatible("TODO") 298 public void testSqrtExact() { 299 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 300 BigInteger floor = BigIntegerMath.sqrt(x, FLOOR); 301 // We only expect an exception if x was not a perfect square. 302 boolean isPerfectSquare = floor.pow(2).equals(x); 303 try { 304 assertEquals(floor, BigIntegerMath.sqrt(x, UNNECESSARY)); 305 assertTrue(isPerfectSquare); 306 } catch (ArithmeticException e) { 307 assertFalse(isPerfectSquare); 308 } 309 } 310 } 311 312 @GwtIncompatible("TODO") 313 public void testSqrtHalfUp() { 314 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 315 BigInteger result = BigIntegerMath.sqrt(x, HALF_UP); 316 BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); 317 BigInteger x4 = x.shiftLeft(2); 318 // sqrt(x) < result + 0.5, so 4 * x < (result + 0.5)^2 * 4 319 // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 320 assertTrue(x4.compareTo(plusHalfSquared) < 0); 321 BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); 322 // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 323 // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 324 assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) >= 0); 325 } 326 } 327 328 @GwtIncompatible("TODO") 329 public void testSqrtHalfDown() { 330 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 331 BigInteger result = BigIntegerMath.sqrt(x, HALF_DOWN); 332 BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); 333 BigInteger x4 = x.shiftLeft(2); 334 // sqrt(x) <= result + 0.5, so 4 * x <= (result + 0.5)^2 * 4 335 // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 336 assertTrue(x4.compareTo(plusHalfSquared) <= 0); 337 BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); 338 // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 339 // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 340 assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) > 0); 341 } 342 } 343 344 // Relies on the correctness of sqrt(BigInteger, {HALF_UP,HALF_DOWN}). 345 @GwtIncompatible("TODO") 346 public void testSqrtHalfEven() { 347 for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { 348 BigInteger halfEven = BigIntegerMath.sqrt(x, HALF_EVEN); 349 // Now figure out what rounding mode we should behave like (it depends if FLOOR was 350 // odd/even). 351 boolean floorWasOdd = BigIntegerMath.sqrt(x, FLOOR).testBit(0); 352 assertEquals(BigIntegerMath.sqrt(x, floorWasOdd ? HALF_UP : HALF_DOWN), halfEven); 353 } 354 } 355 356 @GwtIncompatible("TODO") 357 public void testDivNonZero() { 358 for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { 359 for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { 360 for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { 361 BigInteger expected = 362 new BigDecimal(p).divide(new BigDecimal(q), 0, mode).toBigIntegerExact(); 363 assertEquals(expected, BigIntegerMath.divide(p, q, mode)); 364 } 365 } 366 } 367 } 368 369 @GwtIncompatible("TODO") 370 public void testDivNonZeroExact() { 371 for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { 372 for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { 373 boolean dividesEvenly = p.remainder(q).equals(ZERO); 374 375 try { 376 assertEquals(p, BigIntegerMath.divide(p, q, UNNECESSARY).multiply(q)); 377 assertTrue(dividesEvenly); 378 } catch (ArithmeticException e) { 379 assertFalse(dividesEvenly); 380 } 381 } 382 } 383 } 384 385 @GwtIncompatible("TODO") 386 public void testZeroDivIsAlwaysZero() { 387 for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { 388 for (RoundingMode mode : ALL_ROUNDING_MODES) { 389 assertEquals(ZERO, BigIntegerMath.divide(ZERO, q, mode)); 390 } 391 } 392 } 393 394 @GwtIncompatible("TODO") 395 public void testDivByZeroAlwaysFails() { 396 for (BigInteger p : ALL_BIGINTEGER_CANDIDATES) { 397 for (RoundingMode mode : ALL_ROUNDING_MODES) { 398 try { 399 BigIntegerMath.divide(p, ZERO, mode); 400 fail("Expected ArithmeticException"); 401 } catch (ArithmeticException expected) {} 402 } 403 } 404 } 405 406 public void testFactorial() { 407 BigInteger expected = BigInteger.ONE; 408 for (int i = 1; i <= 200; i++) { 409 expected = expected.multiply(BigInteger.valueOf(i)); 410 assertEquals(expected, BigIntegerMath.factorial(i)); 411 } 412 } 413 414 public void testFactorial0() { 415 assertEquals(BigInteger.ONE, BigIntegerMath.factorial(0)); 416 } 417 418 public void testFactorialNegative() { 419 try { 420 BigIntegerMath.factorial(-1); 421 fail("Expected IllegalArgumentException"); 422 } catch (IllegalArgumentException expected) {} 423 } 424 425 public void testBinomialSmall() { 426 runBinomialTest(0, 30); 427 } 428 429 @GwtIncompatible("too slow") 430 public void testBinomialLarge() { 431 runBinomialTest(31, 100); 432 } 433 434 // Depends on the correctness of BigIntegerMath.factorial 435 private static void runBinomialTest(int firstN, int lastN) { 436 for (int n = firstN; n <= lastN; n++) { 437 for (int k = 0; k <= n; k++) { 438 BigInteger expected = BigIntegerMath 439 .factorial(n) 440 .divide(BigIntegerMath.factorial(k)) 441 .divide(BigIntegerMath.factorial(n - k)); 442 assertEquals(expected, BigIntegerMath.binomial(n, k)); 443 } 444 } 445 } 446 447 public void testBinomialOutside() { 448 for (int n = 0; n <= 50; n++) { 449 try { 450 BigIntegerMath.binomial(n, -1); 451 fail("Expected IllegalArgumentException"); 452 } catch (IllegalArgumentException expected) {} 453 try { 454 BigIntegerMath.binomial(n, n + 1); 455 fail("Expected IllegalArgumentException"); 456 } catch (IllegalArgumentException expected) {} 457 } 458 } 459 460 @GwtIncompatible("NullPointerTester") 461 public void testNullPointers() { 462 NullPointerTester tester = new NullPointerTester(); 463 tester.setDefault(BigInteger.class, ONE); 464 tester.setDefault(int.class, 1); 465 tester.setDefault(long.class, 1L); 466 tester.testAllPublicStaticMethods(BigIntegerMath.class); 467 } 468 } 469