View Javadoc
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_INTEGER_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.EXPONENTS;
23  import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES;
24  import static com.google.common.math.MathTesting.NONZERO_INTEGER_CANDIDATES;
25  import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES;
26  import static com.google.common.math.TestPlatform.intsCanGoOutOfRange;
27  import static java.math.BigInteger.valueOf;
28  import static java.math.RoundingMode.UNNECESSARY;
29  
30  import com.google.common.annotations.GwtCompatible;
31  
32  import junit.framework.TestCase;
33  
34  import java.math.BigDecimal;
35  import java.math.BigInteger;
36  import java.math.RoundingMode;
37  
38  /**
39   * Tests for {@link IntMath}.
40   *
41   * @author Louis Wasserman
42   */
43  @GwtCompatible(emulated = true)
44  public class IntMathTest extends TestCase {
45    
46    public void testLessThanBranchFree() {
47      for (int x : ALL_INTEGER_CANDIDATES) {
48        for (int y : ALL_INTEGER_CANDIDATES) {
49          if (LongMath.fitsInInt((long) x - y)) {
50            int expected = (x < y) ? 1 : 0;
51            int actual = IntMath.lessThanBranchFree(x, y);
52            assertEquals(expected, actual);
53          }
54        }
55      }
56    }
57  
58    public void testLog2ZeroAlwaysThrows() {
59      for (RoundingMode mode : ALL_ROUNDING_MODES) {
60        try {
61          IntMath.log2(0, mode);
62          fail("Expected IllegalArgumentException");
63        } catch (IllegalArgumentException expected) {}
64      }
65    }
66  
67    public void testLog2NegativeAlwaysThrows() {
68      for (int x : NEGATIVE_INTEGER_CANDIDATES) {
69        for (RoundingMode mode : ALL_ROUNDING_MODES) {
70          try {
71            IntMath.log2(x, mode);
72            fail("Expected IllegalArgumentException");
73          } catch (IllegalArgumentException expected) {}
74        }
75      }
76    }
77  
78    // Relies on the correctness of BigIntegrerMath.log2 for all modes except UNNECESSARY.
79    public void testLog2MatchesBigInteger() {
80      for (int x : POSITIVE_INTEGER_CANDIDATES) {
81        for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
82          assertEquals(BigIntegerMath.log2(valueOf(x), mode), IntMath.log2(x, mode));
83        }
84      }
85    }
86  
87    // Relies on the correctness of isPowerOfTwo(int).
88    public void testLog2Exact() {
89      for (int x : POSITIVE_INTEGER_CANDIDATES) {
90        // We only expect an exception if x was not a power of 2.
91        boolean isPowerOf2 = IntMath.isPowerOfTwo(x);
92        try {
93          assertEquals(x, 1 << IntMath.log2(x, UNNECESSARY));
94          assertTrue(isPowerOf2);
95        } catch (ArithmeticException e) {
96          assertFalse(isPowerOf2);
97        }
98      }
99    }
100 
101   // Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY.
102 
103   // Relies on the correctness of log10(int, FLOOR) and of pow(int, int).
104 
105   // Simple test to cover sqrt(0) for all types and all modes.
106 
107   /* Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY. */
108 
109   /* Relies on the correctness of sqrt(int, FLOOR). */
110 
111   public void testDivNonZero() {
112     for (int p : NONZERO_INTEGER_CANDIDATES) {
113       for (int q : NONZERO_INTEGER_CANDIDATES) {
114         for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
115           // Skip some tests that fail due to GWT's non-compliant int implementation.
116           // TODO(cpovirk): does this test fail for only some rounding modes or for all?
117           if (p == -2147483648 && q == -1 && intsCanGoOutOfRange()) {
118             continue;
119           }
120           int expected =
121               new BigDecimal(valueOf(p)).divide(new BigDecimal(valueOf(q)), 0, mode).intValue();
122           assertEquals(p + "/" + q, force32(expected), IntMath.divide(p, q, mode));
123         }
124       }
125     }
126   }
127 
128   public void testDivNonZeroExact() {
129     for (int p : NONZERO_INTEGER_CANDIDATES) {
130       for (int q : NONZERO_INTEGER_CANDIDATES) {
131         // Skip some tests that fail due to GWT's non-compliant int implementation.
132         if (p == -2147483648 && q == -1 && intsCanGoOutOfRange()) {
133           continue;
134         }
135         boolean dividesEvenly = (p % q) == 0;
136         try {
137           assertEquals(p + "/" + q, p, IntMath.divide(p, q, UNNECESSARY) * q);
138           assertTrue(p + "/" + q + " not expected to divide evenly", dividesEvenly);
139         } catch (ArithmeticException e) {
140           assertFalse(p + "/" + q + " expected to divide evenly", dividesEvenly);
141         }
142       }
143     }
144   }
145 
146   public void testZeroDivIsAlwaysZero() {
147     for (int q : NONZERO_INTEGER_CANDIDATES) {
148       for (RoundingMode mode : ALL_ROUNDING_MODES) {
149         assertEquals(0, IntMath.divide(0, q, mode));
150       }
151     }
152   }
153 
154   public void testDivByZeroAlwaysFails() {
155     for (int p : ALL_INTEGER_CANDIDATES) {
156       for (RoundingMode mode : ALL_ROUNDING_MODES) {
157         try {
158           IntMath.divide(p, 0, mode);
159           fail("Expected ArithmeticException");
160         } catch (ArithmeticException expected) {}
161       }
162     }
163   }
164 
165   public void testMod() {
166     for (int x : ALL_INTEGER_CANDIDATES) {
167       for (int m : POSITIVE_INTEGER_CANDIDATES) {
168         assertEquals(valueOf(x).mod(valueOf(m)).intValue(), IntMath.mod(x, m));
169       }
170     }
171   }
172 
173   public void testModNegativeModulusFails() {
174     for (int x : POSITIVE_INTEGER_CANDIDATES) {
175       for (int m : NEGATIVE_INTEGER_CANDIDATES) {
176         try {
177           IntMath.mod(x, m);
178           fail("Expected ArithmeticException");
179         } catch (ArithmeticException expected) {}
180       }
181     }
182   }
183 
184   public void testModZeroModulusFails() {
185     for (int x : ALL_INTEGER_CANDIDATES) {
186       try {
187         IntMath.mod(x, 0);
188         fail("Expected ArithmeticException");
189       } catch (ArithmeticException expected) {}
190     }
191   }
192 
193   public void testGCD() {
194     for (int a : POSITIVE_INTEGER_CANDIDATES) {
195       for (int b : POSITIVE_INTEGER_CANDIDATES) {
196         assertEquals(valueOf(a).gcd(valueOf(b)), valueOf(IntMath.gcd(a, b)));
197       }
198     }
199   }
200 
201   public void testGCDZero() {
202     for (int a : POSITIVE_INTEGER_CANDIDATES) {
203       assertEquals(a, IntMath.gcd(a, 0));
204       assertEquals(a, IntMath.gcd(0, a));
205     }
206     assertEquals(0, IntMath.gcd(0, 0));
207   }
208 
209   public void testGCDNegativePositiveThrows() {
210     for (int a : NEGATIVE_INTEGER_CANDIDATES) {
211       try {
212         IntMath.gcd(a, 3);
213         fail("Expected IllegalArgumentException");
214       } catch (IllegalArgumentException expected) {}
215       try {
216         IntMath.gcd(3, a);
217         fail("Expected IllegalArgumentException");
218       } catch (IllegalArgumentException expected) {}
219     }
220   }
221 
222   public void testGCDNegativeZeroThrows() {
223     for (int a : NEGATIVE_INTEGER_CANDIDATES) {
224       try {
225         IntMath.gcd(a, 0);
226         fail("Expected IllegalArgumentException");
227       } catch (IllegalArgumentException expected) {}
228       try {
229         IntMath.gcd(0, a);
230         fail("Expected IllegalArgumentException");
231       } catch (IllegalArgumentException expected) {}
232     }
233   }
234 
235   public void testCheckedAdd() {
236     for (int a : ALL_INTEGER_CANDIDATES) {
237       for (int b : ALL_INTEGER_CANDIDATES) {
238         BigInteger expectedResult = valueOf(a).add(valueOf(b));
239         boolean expectedSuccess = fitsInInt(expectedResult);
240         try {
241           assertEquals(a + b, IntMath.checkedAdd(a, b));
242           assertTrue(expectedSuccess);
243         } catch (ArithmeticException e) {
244           assertFalse(expectedSuccess);
245         }
246       }
247     }
248   }
249 
250   public void testCheckedSubtract() {
251     for (int a : ALL_INTEGER_CANDIDATES) {
252       for (int b : ALL_INTEGER_CANDIDATES) {
253         BigInteger expectedResult = valueOf(a).subtract(valueOf(b));
254         boolean expectedSuccess = fitsInInt(expectedResult);
255         try {
256           assertEquals(a - b, IntMath.checkedSubtract(a, b));
257           assertTrue(expectedSuccess);
258         } catch (ArithmeticException e) {
259           assertFalse(expectedSuccess);
260         }
261       }
262     }
263   }
264 
265   public void testCheckedMultiply() {
266     for (int a : ALL_INTEGER_CANDIDATES) {
267       for (int b : ALL_INTEGER_CANDIDATES) {
268         BigInteger expectedResult = valueOf(a).multiply(valueOf(b));
269         boolean expectedSuccess = fitsInInt(expectedResult);
270         try {
271           assertEquals(a * b, IntMath.checkedMultiply(a, b));
272           assertTrue(expectedSuccess);
273         } catch (ArithmeticException e) {
274           assertFalse(expectedSuccess);
275         }
276       }
277     }
278   }
279 
280   public void testCheckedPow() {
281     for (int b : ALL_INTEGER_CANDIDATES) {
282       for (int k : EXPONENTS) {
283         BigInteger expectedResult = valueOf(b).pow(k);
284         boolean expectedSuccess = fitsInInt(expectedResult);
285         try {
286           assertEquals(b + "^" + k, force32(expectedResult.intValue()), IntMath.checkedPow(b, k));
287           assertTrue(b + "^" + k + " should have succeeded", expectedSuccess);
288         } catch (ArithmeticException e) {
289           assertFalse(b + "^" + k + " should have failed", expectedSuccess);
290         }
291       }
292     }
293   }
294 
295   // Depends on the correctness of BigIntegerMath.factorial.
296   public void testFactorial() {
297     for (int n = 0; n <= 50; n++) {
298       BigInteger expectedBig = BigIntegerMath.factorial(n);
299       int expectedInt = fitsInInt(expectedBig) ? expectedBig.intValue() : Integer.MAX_VALUE;
300       assertEquals(expectedInt, IntMath.factorial(n));
301     }
302   }
303 
304   public void testFactorialNegative() {
305     for (int n : NEGATIVE_INTEGER_CANDIDATES) {
306       try {
307         IntMath.factorial(n);
308         fail("Expected IllegalArgumentException");
309       } catch (IllegalArgumentException expected) {}
310     }
311   }
312 
313   // Depends on the correctness of BigIntegerMath.binomial.
314 
315   /**
316    * Helper method that asserts the arithmetic mean of x and y is equal
317    * to the expectedMean.
318    */
319   private static void assertMean(int expectedMean, int x, int y) {
320     assertEquals("The expectedMean should be the same as computeMeanSafely",
321         expectedMean, computeMeanSafely(x, y));
322     assertMean(x, y);
323   }
324 
325   /**
326    * Helper method that asserts the arithmetic mean of x and y is equal
327    * to the result of computeMeanSafely.
328    */
329   private static void assertMean(int x, int y) {
330     int expectedMean = computeMeanSafely(x, y);
331     assertEquals(expectedMean, IntMath.mean(x, y));
332     assertEquals("The mean of x and y should equal the mean of y and x",
333         expectedMean, IntMath.mean(y, x));
334   }
335 
336   /**
337    * Computes the mean in a way that is obvious and resilient to
338    * overflow by using BigInteger arithmetic.
339    */
340   private static int computeMeanSafely(int x, int y) {
341     BigInteger bigX = BigInteger.valueOf(x);
342     BigInteger bigY = BigInteger.valueOf(y);
343     BigDecimal bigMean = new BigDecimal(bigX.add(bigY))
344         .divide(BigDecimal.valueOf(2), BigDecimal.ROUND_FLOOR);
345     // parseInt blows up on overflow as opposed to intValue() which does not.
346     return Integer.parseInt(bigMean.toString());
347   }
348 
349   private static boolean fitsInInt(BigInteger big) {
350     return big.bitLength() <= 31;
351   }
352 
353   private static int force32(int value) {
354     // GWT doesn't consistently overflow values to make them 32-bit, so we need to force it.
355     return value & 0xffffffff;
356   }
357 }
358