• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.NEGATIVE_INTEGER_CANDIDATES;
24 import static com.google.common.math.MathTesting.NONZERO_BIGINTEGER_CANDIDATES;
25 import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES;
26 import static java.math.BigInteger.ONE;
27 import static java.math.BigInteger.TEN;
28 import static java.math.BigInteger.ZERO;
29 import static java.math.RoundingMode.CEILING;
30 import static java.math.RoundingMode.DOWN;
31 import static java.math.RoundingMode.FLOOR;
32 import static java.math.RoundingMode.HALF_DOWN;
33 import static java.math.RoundingMode.HALF_EVEN;
34 import static java.math.RoundingMode.HALF_UP;
35 import static java.math.RoundingMode.UNNECESSARY;
36 import static java.math.RoundingMode.UP;
37 import static java.util.Arrays.asList;
38 
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 public class BigIntegerMathTest extends TestCase {
testConstantSqrt2PrecomputedBits()53   public void testConstantSqrt2PrecomputedBits() {
54     assertEquals(BigIntegerMath.sqrt(
55         BigInteger.ZERO.setBit(2 * BigIntegerMath.SQRT2_PRECOMPUTE_THRESHOLD + 1), FLOOR),
56         BigIntegerMath.SQRT2_PRECOMPUTED_BITS);
57   }
58 
testIsPowerOfTwo()59   public void testIsPowerOfTwo() {
60     for (BigInteger x : ALL_BIGINTEGER_CANDIDATES) {
61       // Checks for a single bit set.
62       boolean expected = x.signum() > 0 & x.and(x.subtract(ONE)).equals(ZERO);
63       assertEquals(expected, BigIntegerMath.isPowerOfTwo(x));
64     }
65   }
66 
testLog2ZeroAlwaysThrows()67   public void testLog2ZeroAlwaysThrows() {
68     for (RoundingMode mode : ALL_ROUNDING_MODES) {
69       try {
70         BigIntegerMath.log2(ZERO, mode);
71         fail("Expected IllegalArgumentException");
72       } catch (IllegalArgumentException expected) {}
73     }
74   }
75 
testLog2NegativeAlwaysThrows()76   public void testLog2NegativeAlwaysThrows() {
77     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
78       for (RoundingMode mode : ALL_ROUNDING_MODES) {
79         try {
80           BigIntegerMath.log2(x.negate(), mode);
81           fail("Expected IllegalArgumentException");
82         } catch (IllegalArgumentException expected) {}
83       }
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 
testLog10ZeroAlwaysThrows()154   public void testLog10ZeroAlwaysThrows() {
155     for (RoundingMode mode : ALL_ROUNDING_MODES) {
156       try {
157         BigIntegerMath.log10(ZERO, mode);
158         fail("Expected IllegalArgumentException");
159       } catch (IllegalArgumentException expected) {}
160     }
161   }
162 
testLog10NegativeAlwaysThrows()163   public void testLog10NegativeAlwaysThrows() {
164     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
165       for (RoundingMode mode : ALL_ROUNDING_MODES) {
166         try {
167           BigIntegerMath.log10(x.negate(), mode);
168           fail("Expected IllegalArgumentException");
169         } catch (IllegalArgumentException expected) {}
170       }
171     }
172   }
173 
testLog10Floor()174   public void testLog10Floor() {
175     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
176       for (RoundingMode mode : asList(FLOOR, DOWN)) {
177         int result = BigIntegerMath.log10(x, mode);
178         assertTrue(TEN.pow(result).compareTo(x) <= 0);
179         assertTrue(TEN.pow(result + 1).compareTo(x) > 0);
180       }
181     }
182   }
183 
testLog10Ceiling()184   public void testLog10Ceiling() {
185     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
186       for (RoundingMode mode : asList(CEILING, UP)) {
187         int result = BigIntegerMath.log10(x, mode);
188         assertTrue(TEN.pow(result).compareTo(x) >= 0);
189         assertTrue(result == 0 || TEN.pow(result - 1).compareTo(x) < 0);
190       }
191     }
192   }
193 
194   // Relies on the correctness of log10(BigInteger, FLOOR).
testLog10Exact()195   public void testLog10Exact() {
196     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
197       int logFloor = BigIntegerMath.log10(x, FLOOR);
198       boolean expectSuccess = TEN.pow(logFloor).equals(x);
199       try {
200         assertEquals(logFloor, BigIntegerMath.log10(x, UNNECESSARY));
201         assertTrue(expectSuccess);
202       } catch (ArithmeticException e) {
203         assertFalse(expectSuccess);
204       }
205     }
206   }
207 
testLog10HalfUp()208   public void testLog10HalfUp() {
209     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
210       int result = BigIntegerMath.log10(x, HALF_UP);
211       BigInteger x2 = x.pow(2);
212       // x^2 < 10^(2 * result + 1), or else we would have rounded up
213       assertTrue(TEN.pow(2 * result + 1).compareTo(x2) > 0);
214       // x^2 >= 10^(2 * result - 1), or else we would have rounded down
215       assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) <= 0);
216     }
217   }
218 
testLog10HalfDown()219   public void testLog10HalfDown() {
220     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
221       int result = BigIntegerMath.log10(x, HALF_DOWN);
222       BigInteger x2 = x.pow(2);
223       // x^2 <= 10^(2 * result + 1), or else we would have rounded up
224       assertTrue(TEN.pow(2 * result + 1).compareTo(x2) >= 0);
225       // x^2 > 10^(2 * result - 1), or else we would have rounded down
226       assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) < 0);
227     }
228   }
229 
230   // Relies on the correctness of log10(BigInteger, {HALF_UP,HALF_DOWN}).
testLog10HalfEven()231   public void testLog10HalfEven() {
232     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
233       int halfEven = BigIntegerMath.log10(x, HALF_EVEN);
234       // Now figure out what rounding mode we should behave like (it depends if FLOOR was
235       // odd/even).
236       boolean floorWasEven = (BigIntegerMath.log10(x, FLOOR) & 1) == 0;
237       assertEquals(BigIntegerMath.log10(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven);
238     }
239   }
240 
testLog10TrivialOnPowerOf10()241   public void testLog10TrivialOnPowerOf10() {
242     BigInteger x = BigInteger.TEN.pow(100);
243     for (RoundingMode mode : ALL_ROUNDING_MODES) {
244       assertEquals(100, BigIntegerMath.log10(x, mode));
245     }
246   }
247 
testSqrtZeroAlwaysZero()248   public void testSqrtZeroAlwaysZero() {
249     for (RoundingMode mode : ALL_ROUNDING_MODES) {
250       assertEquals(ZERO, BigIntegerMath.sqrt(ZERO, mode));
251     }
252   }
253 
testSqrtNegativeAlwaysThrows()254   public void testSqrtNegativeAlwaysThrows() {
255     for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) {
256       for (RoundingMode mode : ALL_ROUNDING_MODES) {
257         try {
258           BigIntegerMath.sqrt(x, mode);
259           fail("Expected IllegalArgumentException");
260         } catch (IllegalArgumentException expected) {}
261       }
262     }
263   }
264 
testSqrtFloor()265   public void testSqrtFloor() {
266     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
267       for (RoundingMode mode : asList(FLOOR, DOWN)) {
268         BigInteger result = BigIntegerMath.sqrt(x, mode);
269         assertTrue(result.compareTo(ZERO) > 0);
270         assertTrue(result.pow(2).compareTo(x) <= 0);
271         assertTrue(result.add(ONE).pow(2).compareTo(x) > 0);
272       }
273     }
274   }
275 
testSqrtCeiling()276   public void testSqrtCeiling() {
277     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
278       for (RoundingMode mode : asList(CEILING, UP)) {
279         BigInteger result = BigIntegerMath.sqrt(x, mode);
280         assertTrue(result.compareTo(ZERO) > 0);
281         assertTrue(result.pow(2).compareTo(x) >= 0);
282         assertTrue(result.signum() == 0 || result.subtract(ONE).pow(2).compareTo(x) < 0);
283       }
284     }
285   }
286 
287   // Relies on the correctness of sqrt(BigInteger, FLOOR).
288   public void testSqrtExact() {
289     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
290       BigInteger floor = BigIntegerMath.sqrt(x, FLOOR);
291       // We only expect an exception if x was not a perfect square.
292       boolean isPerfectSquare = floor.pow(2).equals(x);
293       try {
294         assertEquals(floor, BigIntegerMath.sqrt(x, UNNECESSARY));
295         assertTrue(isPerfectSquare);
296       } catch (ArithmeticException e) {
297         assertFalse(isPerfectSquare);
298       }
299     }
300   }
301 
302   public void testSqrtHalfUp() {
303     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
304       BigInteger result = BigIntegerMath.sqrt(x, HALF_UP);
305       BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE);
306       BigInteger x4 = x.shiftLeft(2);
307       // sqrt(x) < result + 0.5, so 4 * x < (result + 0.5)^2 * 4
308       // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1
309       assertTrue(x4.compareTo(plusHalfSquared) < 0);
310       BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE);
311       // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4
312       // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1
313       assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) >= 0);
314     }
315   }
316 
317   public void testSqrtHalfDown() {
318     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
319       BigInteger result = BigIntegerMath.sqrt(x, HALF_DOWN);
320       BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE);
321       BigInteger x4 = x.shiftLeft(2);
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(x4.compareTo(plusHalfSquared) <= 0);
325       BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE);
326       // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4
327       // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1
328       assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) > 0);
329     }
330   }
331 
332   // Relies on the correctness of sqrt(BigInteger, {HALF_UP,HALF_DOWN}).
333   public void testSqrtHalfEven() {
334     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
335       BigInteger halfEven = BigIntegerMath.sqrt(x, HALF_EVEN);
336       // Now figure out what rounding mode we should behave like (it depends if FLOOR was
337       // odd/even).
338       boolean floorWasOdd = BigIntegerMath.sqrt(x, FLOOR).testBit(0);
339       assertEquals(BigIntegerMath.sqrt(x, floorWasOdd ? HALF_UP : HALF_DOWN), halfEven);
340     }
341   }
342 
343   public void testDivNonZero() {
344     for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) {
345       for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) {
346         for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
347           BigInteger expected =
348               new BigDecimal(p).divide(new BigDecimal(q), 0, mode).toBigIntegerExact();
349           assertEquals(expected, BigIntegerMath.divide(p, q, mode));
350         }
351       }
352     }
353   }
354 
355   public void testDivNonZeroExact() {
356     for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) {
357       for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) {
358         boolean dividesEvenly = p.remainder(q).equals(ZERO);
359 
360         try {
361           assertEquals(p, BigIntegerMath.divide(p, q, UNNECESSARY).multiply(q));
362           assertTrue(dividesEvenly);
363         } catch (ArithmeticException e) {
364           assertFalse(dividesEvenly);
365         }
366       }
367     }
368   }
369 
370   public void testZeroDivIsAlwaysZero() {
371     for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) {
372       for (RoundingMode mode : ALL_ROUNDING_MODES) {
373         assertEquals(ZERO, BigIntegerMath.divide(ZERO, q, mode));
374       }
375     }
376   }
377 
378   public void testDivByZeroAlwaysFails() {
379     for (BigInteger p : ALL_BIGINTEGER_CANDIDATES) {
380       for (RoundingMode mode : ALL_ROUNDING_MODES) {
381         try {
382           BigIntegerMath.divide(p, ZERO, mode);
383           fail("Expected ArithmeticException");
384         } catch (ArithmeticException expected) {}
385       }
386     }
387   }
388 
389   public void testFactorial() {
390     BigInteger expected = BigInteger.ONE;
391     for (int i = 1; i <= 300; i++) {
392       expected = expected.multiply(BigInteger.valueOf(i));
393       assertEquals(expected, BigIntegerMath.factorial(i));
394     }
395   }
396 
397   public void testFactorial0() {
398     assertEquals(BigInteger.ONE, BigIntegerMath.factorial(0));
399   }
400 
401   public void testFactorialNegative() {
402     for (int n : NEGATIVE_INTEGER_CANDIDATES) {
403       try {
404         BigIntegerMath.factorial(n);
405         fail("Expected IllegalArgumentException");
406       } catch (IllegalArgumentException expected) {}
407     }
408   }
409 
410   // Depends on the correctness of BigIntegerMath.factorial
411   public void testBinomial() {
412     for (int n = 0; n <= 50; n++) {
413       for (int k = 0; k <= n; k++) {
414         BigInteger expected = BigIntegerMath
415             .factorial(n)
416             .divide(BigIntegerMath.factorial(k))
417             .divide(BigIntegerMath.factorial(n - k));
418         assertEquals(expected, BigIntegerMath.binomial(n, k));
419       }
420     }
421   }
422 
423   public void testBinomialOutside() {
424     for (int n = 0; n <= 50; n++) {
425       try {
426         BigIntegerMath.binomial(n, -1);
427         fail("Expected IllegalArgumentException");
428       } catch (IllegalArgumentException expected) {}
429       try {
430         BigIntegerMath.binomial(n, n + 1);
431         fail("Expected IllegalArgumentException");
432       } catch (IllegalArgumentException expected) {}
433     }
434   }
435 
436   public void testNullPointers() throws Exception {
437     NullPointerTester tester = new NullPointerTester();
438     tester.setDefault(BigInteger.class, ONE);
439     tester.setDefault(RoundingMode.class, FLOOR);
440     tester.setDefault(int.class, 1);
441     tester.setDefault(long.class, 1L);
442     tester.testAllPublicStaticMethods(BigIntegerMath.class);
443   }
444 }
445