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
17package com.google.common.math;
18
19import static com.google.common.math.MathTesting.ALL_INTEGER_CANDIDATES;
20import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES;
21import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES;
22import static com.google.common.math.MathTesting.EXPONENTS;
23import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES;
24import static com.google.common.math.MathTesting.NONZERO_INTEGER_CANDIDATES;
25import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES;
26import static com.google.common.math.TestPlatform.intsCanGoOutOfRange;
27import static java.math.BigInteger.valueOf;
28import static java.math.RoundingMode.FLOOR;
29import static java.math.RoundingMode.UNNECESSARY;
30
31import com.google.common.annotations.GwtCompatible;
32import com.google.common.annotations.GwtIncompatible;
33import com.google.common.testing.NullPointerTester;
34
35import junit.framework.TestCase;
36
37import java.math.BigDecimal;
38import java.math.BigInteger;
39import java.math.RoundingMode;
40
41/**
42 * Tests for {@link IntMath}.
43 *
44 * @author Louis Wasserman
45 */
46@GwtCompatible(emulated = true)
47public class IntMathTest extends TestCase {
48  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
49  public void testConstantMaxPowerOfSqrt2Unsigned() {
50    assertEquals(
51        BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Integer.SIZE - 1), FLOOR).intValue(),
52        IntMath.MAX_POWER_OF_SQRT2_UNSIGNED);
53  }
54
55  @GwtIncompatible("pow()")
56  public void testConstantsPowersOf10() {
57    for (int i = 0; i < IntMath.powersOf10.length - 1; i++) {
58      assertEquals(IntMath.pow(10, i), IntMath.powersOf10[i]);
59    }
60  }
61
62  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
63  public void testMaxLog10ForLeadingZeros() {
64    for (int i = 0; i < Integer.SIZE; i++) {
65      assertEquals(
66          BigIntegerMath.log10(BigInteger.ONE.shiftLeft(Integer.SIZE - i), FLOOR),
67          IntMath.maxLog10ForLeadingZeros[i]);
68    }
69  }
70
71  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
72  public void testConstantsHalfPowersOf10() {
73    for (int i = 0; i < IntMath.halfPowersOf10.length; i++) {
74      assert IntMath.halfPowersOf10[i]
75          == Math.min(Integer.MAX_VALUE,
76              BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * i + 1), FLOOR).longValue());
77    }
78  }
79
80  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
81  public void testConstantsBiggestBinomials() {
82    for (int k = 0; k < IntMath.biggestBinomials.length; k++) {
83      assertTrue(fitsInInt(BigIntegerMath.binomial(IntMath.biggestBinomials[k], k)));
84      assertTrue(IntMath.biggestBinomials[k] == Integer.MAX_VALUE
85          || !fitsInInt(BigIntegerMath.binomial(IntMath.biggestBinomials[k] + 1, k)));
86      // In the first case, any int is valid; in the second, we want to test that the next-bigger
87      // int overflows.
88    }
89    assertFalse(
90        fitsInInt(BigIntegerMath.binomial(
91            2 * IntMath.biggestBinomials.length, IntMath.biggestBinomials.length)));
92  }
93
94  @GwtIncompatible("sqrt")
95  public void testPowersSqrtMaxInt() {
96    assertEquals(IntMath.sqrt(Integer.MAX_VALUE, FLOOR), IntMath.FLOOR_SQRT_MAX_INT);
97  }
98
99  public void testLessThanBranchFree() {
100    for (int x : ALL_INTEGER_CANDIDATES) {
101      for (int y : ALL_INTEGER_CANDIDATES) {
102        if (LongMath.fitsInInt((long) x - y)) {
103          int expected = (x < y) ? 1 : 0;
104          int actual = IntMath.lessThanBranchFree(x, y);
105          assertEquals(expected, actual);
106        }
107      }
108    }
109  }
110
111  @GwtIncompatible("java.math.BigInteger")
112  public void testIsPowerOfTwo() {
113    for (int x : ALL_INTEGER_CANDIDATES) {
114      // Checks for a single bit set.
115      BigInteger bigX = BigInteger.valueOf(x);
116      boolean expected = (bigX.signum() > 0) && (bigX.bitCount() == 1);
117      assertEquals(expected, IntMath.isPowerOfTwo(x));
118    }
119  }
120
121  public void testLog2ZeroAlwaysThrows() {
122    for (RoundingMode mode : ALL_ROUNDING_MODES) {
123      try {
124        IntMath.log2(0, mode);
125        fail("Expected IllegalArgumentException");
126      } catch (IllegalArgumentException expected) {}
127    }
128  }
129
130  public void testLog2NegativeAlwaysThrows() {
131    for (int x : NEGATIVE_INTEGER_CANDIDATES) {
132      for (RoundingMode mode : ALL_ROUNDING_MODES) {
133        try {
134          IntMath.log2(x, mode);
135          fail("Expected IllegalArgumentException");
136        } catch (IllegalArgumentException expected) {}
137      }
138    }
139  }
140
141  // Relies on the correctness of BigIntegrerMath.log2 for all modes except UNNECESSARY.
142  public void testLog2MatchesBigInteger() {
143    for (int x : POSITIVE_INTEGER_CANDIDATES) {
144      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
145        assertEquals(BigIntegerMath.log2(valueOf(x), mode), IntMath.log2(x, mode));
146      }
147    }
148  }
149
150  // Relies on the correctness of isPowerOfTwo(int).
151  public void testLog2Exact() {
152    for (int x : POSITIVE_INTEGER_CANDIDATES) {
153      // We only expect an exception if x was not a power of 2.
154      boolean isPowerOf2 = IntMath.isPowerOfTwo(x);
155      try {
156        assertEquals(x, 1 << IntMath.log2(x, UNNECESSARY));
157        assertTrue(isPowerOf2);
158      } catch (ArithmeticException e) {
159        assertFalse(isPowerOf2);
160      }
161    }
162  }
163
164  @GwtIncompatible("log10")
165  public void testLog10ZeroAlwaysThrows() {
166    for (RoundingMode mode : ALL_ROUNDING_MODES) {
167      try {
168        IntMath.log10(0, mode);
169        fail("Expected IllegalArgumentException");
170      } catch (IllegalArgumentException expected) {}
171    }
172  }
173
174  @GwtIncompatible("log10")
175  public void testLog10NegativeAlwaysThrows() {
176    for (int x : NEGATIVE_INTEGER_CANDIDATES) {
177      for (RoundingMode mode : ALL_ROUNDING_MODES) {
178        try {
179          IntMath.log10(x, mode);
180          fail("Expected IllegalArgumentException");
181        } catch (IllegalArgumentException expected) {}
182      }
183    }
184  }
185
186  // Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY.
187  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
188  public void testLog10MatchesBigInteger() {
189    for (int x : POSITIVE_INTEGER_CANDIDATES) {
190      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
191        // The BigInteger implementation is tested separately, use it as the reference.
192        assertEquals(BigIntegerMath.log10(valueOf(x), mode), IntMath.log10(x, mode));
193      }
194    }
195  }
196
197  // Relies on the correctness of log10(int, FLOOR) and of pow(int, int).
198  @GwtIncompatible("pow()")
199  public void testLog10Exact() {
200    for (int x : POSITIVE_INTEGER_CANDIDATES) {
201      int floor = IntMath.log10(x, FLOOR);
202      boolean expectSuccess = IntMath.pow(10, floor) == x;
203      try {
204        assertEquals(floor, IntMath.log10(x, UNNECESSARY));
205        assertTrue(expectSuccess);
206      } catch (ArithmeticException e) {
207        assertFalse(expectSuccess);
208      }
209    }
210  }
211
212  @GwtIncompatible("log10")
213  public void testLog10TrivialOnPowerOfTen() {
214    int x = 1000000;
215    for (RoundingMode mode : ALL_ROUNDING_MODES) {
216      assertEquals(6, IntMath.log10(x, mode));
217    }
218  }
219
220  // Simple test to cover sqrt(0) for all types and all modes.
221  @GwtIncompatible("sqrt")
222  public void testSqrtZeroAlwaysZero() {
223    for (RoundingMode mode : ALL_ROUNDING_MODES) {
224      assertEquals(0, IntMath.sqrt(0, mode));
225    }
226  }
227
228  @GwtIncompatible("sqrt")
229  public void testSqrtNegativeAlwaysThrows() {
230    for (int x : NEGATIVE_INTEGER_CANDIDATES) {
231      for (RoundingMode mode : RoundingMode.values()) {
232        try {
233          IntMath.sqrt(x, mode);
234          fail("Expected IllegalArgumentException");
235        } catch (IllegalArgumentException expected) {}
236      }
237    }
238  }
239
240  /* Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY. */
241  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
242  public void testSqrtMatchesBigInteger() {
243    for (int x : POSITIVE_INTEGER_CANDIDATES) {
244      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
245        // The BigInteger implementation is tested separately, use it as the reference.
246        // Promote the int value (rather than using intValue() on the expected value) to avoid
247        // any risk of truncation which could lead to a false positive.
248        assertEquals(BigIntegerMath.sqrt(valueOf(x), mode), valueOf(IntMath.sqrt(x, mode)));
249      }
250    }
251  }
252
253  /* Relies on the correctness of sqrt(int, FLOOR). */
254  @GwtIncompatible("sqrt")
255  public void testSqrtExactMatchesFloorOrThrows() {
256    for (int x : POSITIVE_INTEGER_CANDIDATES) {
257      int floor = IntMath.sqrt(x, FLOOR);
258      // We only expect an exception if x was not a perfect square.
259      boolean isPerfectSquare = (floor * floor == x);
260      try {
261        assertEquals(floor, IntMath.sqrt(x, UNNECESSARY));
262        assertTrue(isPerfectSquare);
263      } catch (ArithmeticException e) {
264        assertFalse(isPerfectSquare);
265      }
266    }
267  }
268
269  @GwtIncompatible("2147483646^2 expected=4")
270  public void testPow() {
271    for (int i : ALL_INTEGER_CANDIDATES) {
272      for (int pow : EXPONENTS) {
273        assertEquals(i + "^" + pow, BigInteger.valueOf(i).pow(pow).intValue(), IntMath.pow(i, pow));
274      }
275    }
276  }
277
278  public void testDivNonZero() {
279    for (int p : NONZERO_INTEGER_CANDIDATES) {
280      for (int q : NONZERO_INTEGER_CANDIDATES) {
281        for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
282          // Skip some tests that fail due to GWT's non-compliant int implementation.
283          // TODO(cpovirk): does this test fail for only some rounding modes or for all?
284          if (p == -2147483648 && q == -1 && intsCanGoOutOfRange()) {
285            continue;
286          }
287          int expected =
288              new BigDecimal(valueOf(p)).divide(new BigDecimal(valueOf(q)), 0, mode).intValue();
289          assertEquals(p + "/" + q, force32(expected), IntMath.divide(p, q, mode));
290        }
291      }
292    }
293  }
294
295  public void testDivNonZeroExact() {
296    for (int p : NONZERO_INTEGER_CANDIDATES) {
297      for (int q : NONZERO_INTEGER_CANDIDATES) {
298        // Skip some tests that fail due to GWT's non-compliant int implementation.
299        if (p == -2147483648 && q == -1 && intsCanGoOutOfRange()) {
300          continue;
301        }
302        boolean dividesEvenly = (p % q) == 0;
303        try {
304          assertEquals(p + "/" + q, p, IntMath.divide(p, q, UNNECESSARY) * q);
305          assertTrue(p + "/" + q + " not expected to divide evenly", dividesEvenly);
306        } catch (ArithmeticException e) {
307          assertFalse(p + "/" + q + " expected to divide evenly", dividesEvenly);
308        }
309      }
310    }
311  }
312
313  public void testZeroDivIsAlwaysZero() {
314    for (int q : NONZERO_INTEGER_CANDIDATES) {
315      for (RoundingMode mode : ALL_ROUNDING_MODES) {
316        assertEquals(0, IntMath.divide(0, q, mode));
317      }
318    }
319  }
320
321  public void testDivByZeroAlwaysFails() {
322    for (int p : ALL_INTEGER_CANDIDATES) {
323      for (RoundingMode mode : ALL_ROUNDING_MODES) {
324        try {
325          IntMath.divide(p, 0, mode);
326          fail("Expected ArithmeticException");
327        } catch (ArithmeticException expected) {}
328      }
329    }
330  }
331
332  public void testMod() {
333    for (int x : ALL_INTEGER_CANDIDATES) {
334      for (int m : POSITIVE_INTEGER_CANDIDATES) {
335        assertEquals(valueOf(x).mod(valueOf(m)).intValue(), IntMath.mod(x, m));
336      }
337    }
338  }
339
340  public void testModNegativeModulusFails() {
341    for (int x : POSITIVE_INTEGER_CANDIDATES) {
342      for (int m : NEGATIVE_INTEGER_CANDIDATES) {
343        try {
344          IntMath.mod(x, m);
345          fail("Expected ArithmeticException");
346        } catch (ArithmeticException expected) {}
347      }
348    }
349  }
350
351  public void testModZeroModulusFails() {
352    for (int x : ALL_INTEGER_CANDIDATES) {
353      try {
354        IntMath.mod(x, 0);
355        fail("Expected ArithmeticException");
356      } catch (ArithmeticException expected) {}
357    }
358  }
359
360  public void testGCD() {
361    for (int a : POSITIVE_INTEGER_CANDIDATES) {
362      for (int b : POSITIVE_INTEGER_CANDIDATES) {
363        assertEquals(valueOf(a).gcd(valueOf(b)), valueOf(IntMath.gcd(a, b)));
364      }
365    }
366  }
367
368  public void testGCDZero() {
369    for (int a : POSITIVE_INTEGER_CANDIDATES) {
370      assertEquals(a, IntMath.gcd(a, 0));
371      assertEquals(a, IntMath.gcd(0, a));
372    }
373    assertEquals(0, IntMath.gcd(0, 0));
374  }
375
376  public void testGCDNegativePositiveThrows() {
377    for (int a : NEGATIVE_INTEGER_CANDIDATES) {
378      try {
379        IntMath.gcd(a, 3);
380        fail("Expected IllegalArgumentException");
381      } catch (IllegalArgumentException expected) {}
382      try {
383        IntMath.gcd(3, a);
384        fail("Expected IllegalArgumentException");
385      } catch (IllegalArgumentException expected) {}
386    }
387  }
388
389  public void testGCDNegativeZeroThrows() {
390    for (int a : NEGATIVE_INTEGER_CANDIDATES) {
391      try {
392        IntMath.gcd(a, 0);
393        fail("Expected IllegalArgumentException");
394      } catch (IllegalArgumentException expected) {}
395      try {
396        IntMath.gcd(0, a);
397        fail("Expected IllegalArgumentException");
398      } catch (IllegalArgumentException expected) {}
399    }
400  }
401
402  public void testCheckedAdd() {
403    for (int a : ALL_INTEGER_CANDIDATES) {
404      for (int b : ALL_INTEGER_CANDIDATES) {
405        BigInteger expectedResult = valueOf(a).add(valueOf(b));
406        boolean expectedSuccess = fitsInInt(expectedResult);
407        try {
408          assertEquals(a + b, IntMath.checkedAdd(a, b));
409          assertTrue(expectedSuccess);
410        } catch (ArithmeticException e) {
411          assertFalse(expectedSuccess);
412        }
413      }
414    }
415  }
416
417  public void testCheckedSubtract() {
418    for (int a : ALL_INTEGER_CANDIDATES) {
419      for (int b : ALL_INTEGER_CANDIDATES) {
420        BigInteger expectedResult = valueOf(a).subtract(valueOf(b));
421        boolean expectedSuccess = fitsInInt(expectedResult);
422        try {
423          assertEquals(a - b, IntMath.checkedSubtract(a, b));
424          assertTrue(expectedSuccess);
425        } catch (ArithmeticException e) {
426          assertFalse(expectedSuccess);
427        }
428      }
429    }
430  }
431
432  public void testCheckedMultiply() {
433    for (int a : ALL_INTEGER_CANDIDATES) {
434      for (int b : ALL_INTEGER_CANDIDATES) {
435        BigInteger expectedResult = valueOf(a).multiply(valueOf(b));
436        boolean expectedSuccess = fitsInInt(expectedResult);
437        try {
438          assertEquals(a * b, IntMath.checkedMultiply(a, b));
439          assertTrue(expectedSuccess);
440        } catch (ArithmeticException e) {
441          assertFalse(expectedSuccess);
442        }
443      }
444    }
445  }
446
447  public void testCheckedPow() {
448    for (int b : ALL_INTEGER_CANDIDATES) {
449      for (int k : EXPONENTS) {
450        BigInteger expectedResult = valueOf(b).pow(k);
451        boolean expectedSuccess = fitsInInt(expectedResult);
452        try {
453          assertEquals(b + "^" + k, force32(expectedResult.intValue()), IntMath.checkedPow(b, k));
454          assertTrue(b + "^" + k + " should have succeeded", expectedSuccess);
455        } catch (ArithmeticException e) {
456          assertFalse(b + "^" + k + " should have failed", expectedSuccess);
457        }
458      }
459    }
460  }
461
462  // Depends on the correctness of BigIntegerMath.factorial.
463  public void testFactorial() {
464    for (int n = 0; n <= 50; n++) {
465      BigInteger expectedBig = BigIntegerMath.factorial(n);
466      int expectedInt = fitsInInt(expectedBig) ? expectedBig.intValue() : Integer.MAX_VALUE;
467      assertEquals(expectedInt, IntMath.factorial(n));
468    }
469  }
470
471  public void testFactorialNegative() {
472    for (int n : NEGATIVE_INTEGER_CANDIDATES) {
473      try {
474        IntMath.factorial(n);
475        fail("Expected IllegalArgumentException");
476      } catch (IllegalArgumentException expected) {}
477    }
478  }
479
480  // Depends on the correctness of BigIntegerMath.binomial.
481  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
482  public void testBinomial() {
483    for (int n = 0; n <= 50; n++) {
484      for (int k = 0; k <= n; k++) {
485        BigInteger expectedBig = BigIntegerMath.binomial(n, k);
486        int expectedInt = fitsInInt(expectedBig) ? expectedBig.intValue() : Integer.MAX_VALUE;
487        assertEquals(expectedInt, IntMath.binomial(n, k));
488      }
489    }
490  }
491
492  @GwtIncompatible("binomial")
493  public void testBinomialOutside() {
494    for (int n = 0; n <= 50; n++) {
495      try {
496        IntMath.binomial(n, -1);
497        fail("Expected IllegalArgumentException");
498      } catch (IllegalArgumentException expected) {}
499      try {
500        IntMath.binomial(n, n + 1);
501        fail("Expected IllegalArgumentException");
502      } catch (IllegalArgumentException expected) {}
503    }
504  }
505
506  @GwtIncompatible("binomial")
507  public void testBinomialNegative() {
508    for (int n : NEGATIVE_INTEGER_CANDIDATES) {
509      try {
510        IntMath.binomial(n, 0);
511        fail("Expected IllegalArgumentException");
512      } catch (IllegalArgumentException expected) {}
513    }
514  }
515
516  @GwtIncompatible("java.math.BigInteger")
517  public void testMean() {
518    // Odd-sized ranges have an obvious mean
519    assertMean(2, 1, 3);
520
521    assertMean(-2, -3, -1);
522    assertMean(0, -1, 1);
523    assertMean(1, -1, 3);
524    assertMean((1 << 30) - 1, -1, Integer.MAX_VALUE);
525
526    // Even-sized ranges should prefer the lower mean
527    assertMean(2, 1, 4);
528    assertMean(-3, -4, -1);
529    assertMean(0, -1, 2);
530    assertMean(0, Integer.MIN_VALUE + 2, Integer.MAX_VALUE);
531    assertMean(0, 0, 1);
532    assertMean(-1, -1, 0);
533    assertMean(-1, Integer.MIN_VALUE, Integer.MAX_VALUE);
534
535    // x == y == mean
536    assertMean(1, 1, 1);
537    assertMean(0, 0, 0);
538    assertMean(-1, -1, -1);
539    assertMean(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
540    assertMean(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
541
542    // Exhaustive checks
543    for (int x : ALL_INTEGER_CANDIDATES) {
544      for (int y : ALL_INTEGER_CANDIDATES) {
545        assertMean(x, y);
546      }
547    }
548  }
549
550  /**
551   * Helper method that asserts the arithmetic mean of x and y is equal
552   * to the expectedMean.
553   */
554  private static void assertMean(int expectedMean, int x, int y) {
555    assertEquals("The expectedMean should be the same as computeMeanSafely",
556        expectedMean, computeMeanSafely(x, y));
557    assertMean(x, y);
558  }
559
560  /**
561   * Helper method that asserts the arithmetic mean of x and y is equal
562   * to the result of computeMeanSafely.
563   */
564  private static void assertMean(int x, int y) {
565    int expectedMean = computeMeanSafely(x, y);
566    assertEquals(expectedMean, IntMath.mean(x, y));
567    assertEquals("The mean of x and y should equal the mean of y and x",
568        expectedMean, IntMath.mean(y, x));
569  }
570
571  /**
572   * Computes the mean in a way that is obvious and resilient to
573   * overflow by using BigInteger arithmetic.
574   */
575  private static int computeMeanSafely(int x, int y) {
576    BigInteger bigX = BigInteger.valueOf(x);
577    BigInteger bigY = BigInteger.valueOf(y);
578    BigDecimal bigMean = new BigDecimal(bigX.add(bigY))
579        .divide(BigDecimal.valueOf(2), BigDecimal.ROUND_FLOOR);
580    // parseInt blows up on overflow as opposed to intValue() which does not.
581    return Integer.parseInt(bigMean.toString());
582  }
583
584  private static boolean fitsInInt(BigInteger big) {
585    return big.bitLength() <= 31;
586  }
587
588  @GwtIncompatible("NullPointerTester")
589  public void testNullPointers() {
590    NullPointerTester tester = new NullPointerTester();
591    tester.setDefault(int.class, 1);
592    tester.testAllPublicStaticMethods(IntMath.class);
593  }
594
595  private static int force32(int value) {
596    // GWT doesn't consistently overflow values to make them 32-bit, so we need to force it.
597    return value & 0xffffffff;
598  }
599}
600