IntMathTest.java revision 1d580d0f6ee4f21eb309ba7b509d2c6d671c4044
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 java.math.BigInteger.valueOf;
27import static java.math.RoundingMode.FLOOR;
28import static java.math.RoundingMode.UNNECESSARY;
29
30import com.google.common.annotations.GwtCompatible;
31import com.google.common.annotations.GwtIncompatible;
32import com.google.common.testing.NullPointerTester;
33
34import junit.framework.TestCase;
35
36import java.math.BigDecimal;
37import java.math.BigInteger;
38import java.math.RoundingMode;
39
40/**
41 * Tests for {@link IntMath}.
42 *
43 * @author Louis Wasserman
44 */
45@GwtCompatible(emulated = true)
46public class IntMathTest extends TestCase {
47  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
48  public void testConstantMaxPowerOfSqrt2Unsigned() {
49    assertEquals(
50        BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Integer.SIZE - 1), FLOOR).intValue(),
51        IntMath.MAX_POWER_OF_SQRT2_UNSIGNED);
52  }
53
54  @GwtIncompatible("pow()")
55  public void testConstantsPowersOf10() {
56    for (int i = 0; i < IntMath.POWERS_OF_10.length; i++) {
57      assertEquals(IntMath.pow(10, i), IntMath.POWERS_OF_10[i]);
58    }
59  }
60
61  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
62  public void testConstantsHalfPowersOf10() {
63    for (int i = 0; i < IntMath.HALF_POWERS_OF_10.length; i++) {
64      assert IntMath.HALF_POWERS_OF_10[i]
65          == Math.min(Integer.MAX_VALUE,
66              BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * i + 1), FLOOR).longValue());
67    }
68  }
69
70  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
71  public void testConstantsBiggestBinomials(){
72    for (int k = 0; k < IntMath.BIGGEST_BINOMIALS.length; k++) {
73      assertTrue(fitsInInt(BigIntegerMath.binomial(IntMath.BIGGEST_BINOMIALS[k], k)));
74      assertTrue(IntMath.BIGGEST_BINOMIALS[k] == Integer.MAX_VALUE
75          || !fitsInInt(BigIntegerMath.binomial(IntMath.BIGGEST_BINOMIALS[k] + 1, k)));
76      // In the first case, any int is valid; in the second, we want to test that the next-bigger
77      // int overflows.
78    }
79    assertFalse(
80        fitsInInt(BigIntegerMath.binomial(
81            2 * IntMath.BIGGEST_BINOMIALS.length, IntMath.BIGGEST_BINOMIALS.length)));
82  }
83
84  @GwtIncompatible("sqrt")
85  public void testPowersSqrtMaxInt() {
86    assertEquals(IntMath.sqrt(Integer.MAX_VALUE, FLOOR), IntMath.FLOOR_SQRT_MAX_INT);
87  }
88
89  public void testIsPowerOfTwo() {
90    for (int x : ALL_INTEGER_CANDIDATES) {
91      // Checks for a single bit set.
92      boolean expected = x > 0 & (x & (x - 1)) == 0;
93      assertEquals(expected, IntMath.isPowerOfTwo(x));
94    }
95  }
96
97  @GwtIncompatible("log2")
98  public void testLog2ZeroAlwaysThrows() {
99    for (RoundingMode mode : ALL_ROUNDING_MODES) {
100      try {
101        IntMath.log2(0, mode);
102        fail("Expected IllegalArgumentException");
103      } catch (IllegalArgumentException expected) {}
104    }
105  }
106
107  @GwtIncompatible("log2")
108  public void testLog2NegativeAlwaysThrows() {
109    for (int x : NEGATIVE_INTEGER_CANDIDATES) {
110      for (RoundingMode mode : ALL_ROUNDING_MODES) {
111        try {
112          IntMath.log2(x, mode);
113          fail("Expected IllegalArgumentException");
114        } catch (IllegalArgumentException expected) {}
115      }
116    }
117  }
118
119  // Relies on the correctness of BigIntegrerMath.log2 for all modes except UNNECESSARY.
120  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
121  public void testLog2MatchesBigInteger() {
122    for (int x : POSITIVE_INTEGER_CANDIDATES) {
123      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
124        assertEquals(BigIntegerMath.log2(valueOf(x), mode), IntMath.log2(x, mode));
125      }
126    }
127  }
128
129  // Relies on the correctness of isPowerOfTwo(int).
130  @GwtIncompatible("log2")
131  public void testLog2Exact() {
132    for (int x : POSITIVE_INTEGER_CANDIDATES) {
133      // We only expect an exception if x was not a power of 2.
134      boolean isPowerOf2 = IntMath.isPowerOfTwo(x);
135      try {
136        assertEquals(x, 1 << IntMath.log2(x, UNNECESSARY));
137        assertTrue(isPowerOf2);
138      } catch (ArithmeticException e) {
139        assertFalse(isPowerOf2);
140      }
141    }
142  }
143
144  @GwtIncompatible("log10")
145  public void testLog10ZeroAlwaysThrows() {
146    for (RoundingMode mode : ALL_ROUNDING_MODES) {
147      try {
148        IntMath.log10(0, mode);
149        fail("Expected IllegalArgumentException");
150      } catch (IllegalArgumentException expected) {}
151    }
152  }
153
154  @GwtIncompatible("log10")
155  public void testLog10NegativeAlwaysThrows() {
156    for (int x : NEGATIVE_INTEGER_CANDIDATES) {
157      for (RoundingMode mode : ALL_ROUNDING_MODES) {
158        try {
159          IntMath.log10(x, mode);
160          fail("Expected IllegalArgumentException");
161        } catch (IllegalArgumentException expected) {}
162      }
163    }
164  }
165
166  // Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY.
167  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
168  public void testLog10MatchesBigInteger() {
169    for (int x : POSITIVE_INTEGER_CANDIDATES) {
170      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
171        // The BigInteger implementation is tested separately, use it as the reference.
172        assertEquals(BigIntegerMath.log10(valueOf(x), mode), IntMath.log10(x, mode));
173      }
174    }
175  }
176
177  // Relies on the correctness of log10(int, FLOOR) and of pow(int, int).
178  @GwtIncompatible("pow()")
179  public void testLog10Exact() {
180    for (int x : POSITIVE_INTEGER_CANDIDATES) {
181      int floor = IntMath.log10(x, FLOOR);
182      boolean expectSuccess = IntMath.pow(10, floor) == x;
183      try {
184        assertEquals(floor, IntMath.log10(x, UNNECESSARY));
185        assertTrue(expectSuccess);
186      } catch (ArithmeticException e) {
187        assertFalse(expectSuccess);
188      }
189    }
190  }
191
192  @GwtIncompatible("log10")
193  public void testLog10TrivialOnPowerOfTen() {
194    int x = 1000000;
195    for (RoundingMode mode : ALL_ROUNDING_MODES) {
196      assertEquals(6, IntMath.log10(x, mode));
197    }
198  }
199
200  // Simple test to cover sqrt(0) for all types and all modes.
201  @GwtIncompatible("sqrt")
202  public void testSqrtZeroAlwaysZero() {
203    for (RoundingMode mode : ALL_ROUNDING_MODES) {
204      assertEquals(0, IntMath.sqrt(0, mode));
205    }
206  }
207
208  @GwtIncompatible("sqrt")
209  public void testSqrtNegativeAlwaysThrows() {
210    for (int x : NEGATIVE_INTEGER_CANDIDATES) {
211      for (RoundingMode mode : RoundingMode.values()) {
212        try {
213          IntMath.sqrt(x, mode);
214          fail("Expected IllegalArgumentException");
215        } catch (IllegalArgumentException expected) {}
216      }
217    }
218  }
219
220  /* Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY. */
221  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
222  public void testSqrtMatchesBigInteger() {
223    for (int x : POSITIVE_INTEGER_CANDIDATES) {
224      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
225        // The BigInteger implementation is tested separately, use it as the reference.
226        // Promote the int value (rather than using intValue() on the expected value) to avoid
227        // any risk of truncation which could lead to a false positive.
228        assertEquals(BigIntegerMath.sqrt(valueOf(x), mode), valueOf(IntMath.sqrt(x, mode)));
229      }
230    }
231  }
232
233  /* Relies on the correctness of sqrt(int, FLOOR). */
234  @GwtIncompatible("sqrt")
235  public void testSqrtExactMatchesFloorOrThrows() {
236    for (int x : POSITIVE_INTEGER_CANDIDATES) {
237      int floor = IntMath.sqrt(x, FLOOR);
238      // We only expect an exception if x was not a perfect square.
239      boolean isPerfectSquare = (floor * floor == x);
240      try {
241        assertEquals(floor, IntMath.sqrt(x, UNNECESSARY));
242        assertTrue(isPerfectSquare);
243      } catch (ArithmeticException e) {
244        assertFalse(isPerfectSquare);
245      }
246    }
247  }
248
249  @GwtIncompatible("2147483646^2 expected=4")
250  public void testPow() {
251    for (int i : ALL_INTEGER_CANDIDATES) {
252      for (int pow : EXPONENTS) {
253        assertEquals(i + "^" + pow, BigInteger.valueOf(i).pow(pow).intValue(), IntMath.pow(i, pow));
254      }
255    }
256  }
257
258  @GwtIncompatible("-2147483648/1 expected=2147483648")
259  public void testDivNonZero() {
260    for (int p : NONZERO_INTEGER_CANDIDATES) {
261      for (int q : NONZERO_INTEGER_CANDIDATES) {
262        for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
263          int expected =
264              new BigDecimal(valueOf(p)).divide(new BigDecimal(valueOf(q)), 0, mode).intValue();
265          assertEquals(p + "/" + q, expected, IntMath.divide(p, q, mode));
266        }
267      }
268    }
269  }
270
271  @GwtIncompatible("-2147483648/-1 not expected to divide evenly")
272  public void testDivNonZeroExact() {
273    for (int p : NONZERO_INTEGER_CANDIDATES) {
274      for (int q : NONZERO_INTEGER_CANDIDATES) {
275        boolean dividesEvenly = (p % q) == 0;
276
277        try {
278          assertEquals(p + "/" + q, p, IntMath.divide(p, q, UNNECESSARY) * q);
279          assertTrue(p + "/" + q + " expected to divide evenly", dividesEvenly);
280        } catch (ArithmeticException e) {
281          assertFalse(p + "/" + q + " not expected to divide evenly", dividesEvenly);
282        }
283      }
284    }
285  }
286
287  @GwtIncompatible("pow()")
288  public void testZeroDivIsAlwaysZero() {
289    for (int q : NONZERO_INTEGER_CANDIDATES) {
290      for (RoundingMode mode : ALL_ROUNDING_MODES) {
291        assertEquals(0, IntMath.divide(0, q, mode));
292      }
293    }
294  }
295
296  @GwtIncompatible("pow()")
297  public void testDivByZeroAlwaysFails() {
298    for (int p : ALL_INTEGER_CANDIDATES) {
299      for (RoundingMode mode : ALL_ROUNDING_MODES) {
300        try {
301          IntMath.divide(p, 0, mode);
302          fail("Expected ArithmeticException");
303        } catch (ArithmeticException expected) {}
304      }
305    }
306  }
307
308  public void testMod() {
309    for (int x : ALL_INTEGER_CANDIDATES) {
310      for (int m : POSITIVE_INTEGER_CANDIDATES) {
311        assertEquals(valueOf(x).mod(valueOf(m)).intValue(), IntMath.mod(x, m));
312      }
313    }
314  }
315
316  public void testModNegativeModulusFails() {
317    for (int x : POSITIVE_INTEGER_CANDIDATES) {
318      for (int m : NEGATIVE_INTEGER_CANDIDATES) {
319        try {
320          IntMath.mod(x, m);
321          fail("Expected ArithmeticException");
322        } catch (ArithmeticException expected) {}
323      }
324    }
325  }
326
327  public void testModZeroModulusFails() {
328    for (int x : ALL_INTEGER_CANDIDATES) {
329      try {
330        IntMath.mod(x, 0);
331        fail("Expected ArithmeticException");
332      } catch (ArithmeticException expected) {}
333    }
334  }
335
336  public void testGCD() {
337    for (int a : POSITIVE_INTEGER_CANDIDATES) {
338      for (int b : POSITIVE_INTEGER_CANDIDATES) {
339        assertEquals(valueOf(a).gcd(valueOf(b)), valueOf(IntMath.gcd(a, b)));
340      }
341    }
342  }
343
344  public void testGCDZero() {
345    for (int a : POSITIVE_INTEGER_CANDIDATES) {
346      assertEquals(a, IntMath.gcd(a, 0));
347      assertEquals(a, IntMath.gcd(0, a));
348    }
349    assertEquals(0, IntMath.gcd(0, 0));
350  }
351
352  public void testGCDNegativePositiveThrows() {
353    for (int a : NEGATIVE_INTEGER_CANDIDATES) {
354      try {
355        IntMath.gcd(a, 3);
356        fail("Expected IllegalArgumentException");
357      } catch (IllegalArgumentException expected) {}
358      try {
359        IntMath.gcd(3, a);
360        fail("Expected IllegalArgumentException");
361      } catch (IllegalArgumentException expected) {}
362    }
363  }
364
365  public void testGCDNegativeZeroThrows() {
366    for (int a : NEGATIVE_INTEGER_CANDIDATES) {
367      try {
368        IntMath.gcd(a, 0);
369        fail("Expected IllegalArgumentException");
370      } catch (IllegalArgumentException expected) {}
371      try {
372        IntMath.gcd(0, a);
373        fail("Expected IllegalArgumentException");
374      } catch (IllegalArgumentException expected) {}
375    }
376  }
377
378  public void testCheckedAdd() {
379    for (int a : ALL_INTEGER_CANDIDATES) {
380      for (int b : ALL_INTEGER_CANDIDATES) {
381        BigInteger expectedResult = valueOf(a).add(valueOf(b));
382        boolean expectedSuccess = fitsInInt(expectedResult);
383        try {
384          assertEquals(a + b, IntMath.checkedAdd(a, b));
385          assertTrue(expectedSuccess);
386        } catch (ArithmeticException e) {
387          assertFalse(expectedSuccess);
388        }
389      }
390    }
391  }
392
393  public void testCheckedSubtract() {
394    for (int a : ALL_INTEGER_CANDIDATES) {
395      for (int b : ALL_INTEGER_CANDIDATES) {
396        BigInteger expectedResult = valueOf(a).subtract(valueOf(b));
397        boolean expectedSuccess = fitsInInt(expectedResult);
398        try {
399          assertEquals(a - b, IntMath.checkedSubtract(a, b));
400          assertTrue(expectedSuccess);
401        } catch (ArithmeticException e) {
402          assertFalse(expectedSuccess);
403        }
404      }
405    }
406  }
407
408  public void testCheckedMultiply() {
409    for (int a : ALL_INTEGER_CANDIDATES) {
410      for (int b : ALL_INTEGER_CANDIDATES) {
411        BigInteger expectedResult = valueOf(a).multiply(valueOf(b));
412        boolean expectedSuccess = fitsInInt(expectedResult);
413        try {
414          assertEquals(a * b, IntMath.checkedMultiply(a, b));
415          assertTrue(expectedSuccess);
416        } catch (ArithmeticException e) {
417          assertFalse(expectedSuccess);
418        }
419      }
420    }
421  }
422
423  @GwtIncompatible("-2147483648^1 expected=2147483648")
424  public void testCheckedPow() {
425    for (int b : ALL_INTEGER_CANDIDATES) {
426      for (int k : EXPONENTS) {
427        BigInteger expectedResult = valueOf(b).pow(k);
428        boolean expectedSuccess = fitsInInt(expectedResult);
429        try {
430          assertEquals(b + "^" + k, expectedResult.intValue(), IntMath.checkedPow(b, k));
431          assertTrue(b + "^" + k + " should have succeeded", expectedSuccess);
432        } catch (ArithmeticException e) {
433          assertFalse(b + "^" + k + " should have failed", expectedSuccess);
434        }
435      }
436    }
437  }
438
439  // Depends on the correctness of BigIntegerMath.factorial.
440  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
441  public void testFactorial() {
442    for (int n = 0; n <= 50; n++) {
443      BigInteger expectedBig = BigIntegerMath.factorial(n);
444      int expectedInt = fitsInInt(expectedBig) ? expectedBig.intValue() : Integer.MAX_VALUE;
445      assertEquals(expectedInt, IntMath.factorial(n));
446    }
447  }
448
449  @GwtIncompatible("factorial")
450  public void testFactorialNegative() {
451    for (int n : NEGATIVE_INTEGER_CANDIDATES) {
452      try {
453        IntMath.factorial(n);
454        fail("Expected IllegalArgumentException");
455      } catch (IllegalArgumentException expected) {}
456    }
457  }
458
459  // Depends on the correctness of BigIntegerMath.binomial.
460  @GwtIncompatible("BigIntegerMath") // TODO(cpovirk): GWT-enable BigIntegerMath
461  public void testBinomial() {
462    for (int n = 0; n <= 50; n++) {
463      for (int k = 0; k <= n; k++) {
464        BigInteger expectedBig = BigIntegerMath.binomial(n, k);
465        int expectedInt = fitsInInt(expectedBig) ? expectedBig.intValue() : Integer.MAX_VALUE;
466        assertEquals(expectedInt, IntMath.binomial(n, k));
467      }
468    }
469  }
470
471  @GwtIncompatible("binomial")
472  public void testBinomialOutside() {
473    for (int n = 0; n <= 50; n++) {
474      try {
475        IntMath.binomial(n, -1);
476        fail("Expected IllegalArgumentException");
477      } catch (IllegalArgumentException expected) {}
478      try {
479        IntMath.binomial(n, n + 1);
480        fail("Expected IllegalArgumentException");
481      } catch (IllegalArgumentException expected) {}
482    }
483  }
484
485  @GwtIncompatible("binomial")
486  public void testBinomialNegative() {
487    for (int n : NEGATIVE_INTEGER_CANDIDATES) {
488      try {
489        IntMath.binomial(n, 0);
490        fail("Expected IllegalArgumentException");
491      } catch (IllegalArgumentException expected) {}
492    }
493  }
494
495  private boolean fitsInInt(BigInteger big) {
496    return big.bitLength() <= 31;
497  }
498
499  @GwtIncompatible("NullPointerTester")
500  public void testNullPointers() throws Exception {
501    NullPointerTester tester = new NullPointerTester();
502    tester.setDefault(int.class, 1);
503    tester.setDefault(RoundingMode.class, FLOOR);
504    tester.testAllPublicStaticMethods(IntMath.class);
505  }
506}
507