• 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.NONZERO_BIGINTEGER_CANDIDATES;
24 import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES;
25 import static com.google.common.truth.Truth.assertThat;
26 import static com.google.common.truth.Truth.assertWithMessage;
27 import static java.math.BigInteger.ONE;
28 import static java.math.BigInteger.TEN;
29 import static java.math.BigInteger.ZERO;
30 import static java.math.RoundingMode.CEILING;
31 import static java.math.RoundingMode.DOWN;
32 import static java.math.RoundingMode.FLOOR;
33 import static java.math.RoundingMode.HALF_DOWN;
34 import static java.math.RoundingMode.HALF_EVEN;
35 import static java.math.RoundingMode.HALF_UP;
36 import static java.math.RoundingMode.UNNECESSARY;
37 import static java.math.RoundingMode.UP;
38 import static java.math.RoundingMode.values;
39 import static java.util.Arrays.asList;
40 
41 import com.google.common.annotations.GwtCompatible;
42 import com.google.common.annotations.GwtIncompatible;
43 import com.google.common.annotations.J2ktIncompatible;
44 import com.google.common.testing.NullPointerTester;
45 import com.google.errorprone.annotations.CanIgnoreReturnValue;
46 import java.math.BigDecimal;
47 import java.math.BigInteger;
48 import java.math.RoundingMode;
49 import java.util.EnumMap;
50 import java.util.EnumSet;
51 import java.util.Map;
52 import junit.framework.TestCase;
53 
54 /**
55  * Tests for BigIntegerMath.
56  *
57  * @author Louis Wasserman
58  */
59 @ElementTypesAreNonnullByDefault
60 @GwtCompatible(emulated = true)
61 public class BigIntegerMathTest extends TestCase {
testCeilingPowerOfTwo()62   public void testCeilingPowerOfTwo() {
63     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
64       BigInteger result = BigIntegerMath.ceilingPowerOfTwo(x);
65       assertTrue(BigIntegerMath.isPowerOfTwo(result));
66       assertTrue(result.compareTo(x) >= 0);
67       assertTrue(result.compareTo(x.add(x)) < 0);
68     }
69   }
70 
71   public void testFloorPowerOfTwo() {
72     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
73       BigInteger result = BigIntegerMath.floorPowerOfTwo(x);
74       assertTrue(BigIntegerMath.isPowerOfTwo(result));
75       assertTrue(result.compareTo(x) <= 0);
76       assertTrue(result.add(result).compareTo(x) > 0);
77     }
78   }
79 
80   public void testCeilingPowerOfTwoNegative() {
81     for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) {
82       try {
83         BigIntegerMath.ceilingPowerOfTwo(x);
84         fail("Expected IllegalArgumentException");
85       } catch (IllegalArgumentException expected) {
86       }
87     }
88   }
89 
90   public void testFloorPowerOfTwoNegative() {
91     for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) {
92       try {
93         BigIntegerMath.floorPowerOfTwo(x);
94         fail("Expected IllegalArgumentException");
95       } catch (IllegalArgumentException expected) {
96       }
97     }
98   }
99 
100   public void testCeilingPowerOfTwoZero() {
101     try {
102       BigIntegerMath.ceilingPowerOfTwo(BigInteger.ZERO);
103       fail("Expected IllegalArgumentException");
104     } catch (IllegalArgumentException expected) {
105     }
106   }
107 
108   public void testFloorPowerOfTwoZero() {
109     try {
110       BigIntegerMath.floorPowerOfTwo(BigInteger.ZERO);
111       fail("Expected IllegalArgumentException");
112     } catch (IllegalArgumentException expected) {
113     }
114   }
115 
116   @J2ktIncompatible
117   @GwtIncompatible // TODO
118   public void testConstantSqrt2PrecomputedBits() {
119     assertEquals(
120         BigIntegerMath.sqrt(
121             BigInteger.ZERO.setBit(2 * BigIntegerMath.SQRT2_PRECOMPUTE_THRESHOLD + 1), FLOOR),
122         BigIntegerMath.SQRT2_PRECOMPUTED_BITS);
123   }
124 
125   public void testIsPowerOfTwo() {
126     for (BigInteger x : ALL_BIGINTEGER_CANDIDATES) {
127       // Checks for a single bit set.
128       boolean expected = x.signum() > 0 & x.and(x.subtract(ONE)).equals(ZERO);
129       assertEquals(expected, BigIntegerMath.isPowerOfTwo(x));
130     }
131   }
132 
testLog2ZeroAlwaysThrows()133   public void testLog2ZeroAlwaysThrows() {
134     for (RoundingMode mode : ALL_ROUNDING_MODES) {
135       try {
136         BigIntegerMath.log2(ZERO, mode);
137         fail("Expected IllegalArgumentException");
138       } catch (IllegalArgumentException expected) {
139       }
140     }
141   }
142 
testLog2NegativeAlwaysThrows()143   public void testLog2NegativeAlwaysThrows() {
144     for (RoundingMode mode : ALL_ROUNDING_MODES) {
145       try {
146         BigIntegerMath.log2(BigInteger.valueOf(-1), mode);
147         fail("Expected IllegalArgumentException");
148       } catch (IllegalArgumentException expected) {
149       }
150     }
151   }
152 
testLog2Floor()153   public void testLog2Floor() {
154     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
155       for (RoundingMode mode : asList(FLOOR, DOWN)) {
156         int result = BigIntegerMath.log2(x, mode);
157         assertTrue(ZERO.setBit(result).compareTo(x) <= 0);
158         assertTrue(ZERO.setBit(result + 1).compareTo(x) > 0);
159       }
160     }
161   }
162 
testLog2Ceiling()163   public void testLog2Ceiling() {
164     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
165       for (RoundingMode mode : asList(CEILING, UP)) {
166         int result = BigIntegerMath.log2(x, mode);
167         assertTrue(ZERO.setBit(result).compareTo(x) >= 0);
168         assertTrue(result == 0 || ZERO.setBit(result - 1).compareTo(x) < 0);
169       }
170     }
171   }
172 
173   // Relies on the correctness of isPowerOfTwo(BigInteger).
testLog2Exact()174   public void testLog2Exact() {
175     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
176       // We only expect an exception if x was not a power of 2.
177       boolean isPowerOf2 = BigIntegerMath.isPowerOfTwo(x);
178       try {
179         assertEquals(x, ZERO.setBit(BigIntegerMath.log2(x, UNNECESSARY)));
180         assertTrue(isPowerOf2);
181       } catch (ArithmeticException e) {
182         assertFalse(isPowerOf2);
183       }
184     }
185   }
186 
testLog2HalfUp()187   public void testLog2HalfUp() {
188     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
189       int result = BigIntegerMath.log2(x, HALF_UP);
190       BigInteger x2 = x.pow(2);
191       // x^2 < 2^(2 * result + 1), or else we would have rounded up
192       assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) > 0);
193       // x^2 >= 2^(2 * result - 1), or else we would have rounded down
194       assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) <= 0);
195     }
196   }
197 
testLog2HalfDown()198   public void testLog2HalfDown() {
199     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
200       int result = BigIntegerMath.log2(x, HALF_DOWN);
201       BigInteger x2 = x.pow(2);
202       // x^2 <= 2^(2 * result + 1), or else we would have rounded up
203       assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) >= 0);
204       // x^2 > 2^(2 * result - 1), or else we would have rounded down
205       assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) < 0);
206     }
207   }
208 
209   // Relies on the correctness of log2(BigInteger, {HALF_UP,HALF_DOWN}).
testLog2HalfEven()210   public void testLog2HalfEven() {
211     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
212       int halfEven = BigIntegerMath.log2(x, HALF_EVEN);
213       // Now figure out what rounding mode we should behave like (it depends if FLOOR was
214       // odd/even).
215       boolean floorWasEven = (BigIntegerMath.log2(x, FLOOR) & 1) == 0;
216       assertEquals(BigIntegerMath.log2(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven);
217     }
218   }
219 
220   @J2ktIncompatible
221   @GwtIncompatible // TODO
testLog10ZeroAlwaysThrows()222   public void testLog10ZeroAlwaysThrows() {
223     for (RoundingMode mode : ALL_ROUNDING_MODES) {
224       try {
225         BigIntegerMath.log10(ZERO, mode);
226         fail("Expected IllegalArgumentException");
227       } catch (IllegalArgumentException expected) {
228       }
229     }
230   }
231 
232   @J2ktIncompatible
233   @GwtIncompatible // TODO
testLog10NegativeAlwaysThrows()234   public void testLog10NegativeAlwaysThrows() {
235     for (RoundingMode mode : ALL_ROUNDING_MODES) {
236       try {
237         BigIntegerMath.log10(BigInteger.valueOf(-1), mode);
238         fail("Expected IllegalArgumentException");
239       } catch (IllegalArgumentException expected) {
240       }
241     }
242   }
243 
244   @J2ktIncompatible
245   @GwtIncompatible // TODO
testLog10Floor()246   public void testLog10Floor() {
247     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
248       for (RoundingMode mode : asList(FLOOR, DOWN)) {
249         int result = BigIntegerMath.log10(x, mode);
250         assertTrue(TEN.pow(result).compareTo(x) <= 0);
251         assertTrue(TEN.pow(result + 1).compareTo(x) > 0);
252       }
253     }
254   }
255 
256   @J2ktIncompatible
257   @GwtIncompatible // TODO
testLog10Ceiling()258   public void testLog10Ceiling() {
259     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
260       for (RoundingMode mode : asList(CEILING, UP)) {
261         int result = BigIntegerMath.log10(x, mode);
262         assertTrue(TEN.pow(result).compareTo(x) >= 0);
263         assertTrue(result == 0 || TEN.pow(result - 1).compareTo(x) < 0);
264       }
265     }
266   }
267 
268   // Relies on the correctness of log10(BigInteger, FLOOR).
269   @J2ktIncompatible
270   @GwtIncompatible // TODO
testLog10Exact()271   public void testLog10Exact() {
272     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
273       int logFloor = BigIntegerMath.log10(x, FLOOR);
274       boolean expectSuccess = TEN.pow(logFloor).equals(x);
275       try {
276         assertEquals(logFloor, BigIntegerMath.log10(x, UNNECESSARY));
277         assertTrue(expectSuccess);
278       } catch (ArithmeticException e) {
279         assertFalse(expectSuccess);
280       }
281     }
282   }
283 
284   @J2ktIncompatible
285   @GwtIncompatible // TODO
testLog10HalfUp()286   public void testLog10HalfUp() {
287     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
288       int result = BigIntegerMath.log10(x, HALF_UP);
289       BigInteger x2 = x.pow(2);
290       // x^2 < 10^(2 * result + 1), or else we would have rounded up
291       assertTrue(TEN.pow(2 * result + 1).compareTo(x2) > 0);
292       // x^2 >= 10^(2 * result - 1), or else we would have rounded down
293       assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) <= 0);
294     }
295   }
296 
297   @J2ktIncompatible
298   @GwtIncompatible // TODO
testLog10HalfDown()299   public void testLog10HalfDown() {
300     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
301       int result = BigIntegerMath.log10(x, HALF_DOWN);
302       BigInteger x2 = x.pow(2);
303       // x^2 <= 10^(2 * result + 1), or else we would have rounded up
304       assertTrue(TEN.pow(2 * result + 1).compareTo(x2) >= 0);
305       // x^2 > 10^(2 * result - 1), or else we would have rounded down
306       assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) < 0);
307     }
308   }
309 
310   // Relies on the correctness of log10(BigInteger, {HALF_UP,HALF_DOWN}).
311   @J2ktIncompatible
312   @GwtIncompatible // TODO
testLog10HalfEven()313   public void testLog10HalfEven() {
314     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
315       int halfEven = BigIntegerMath.log10(x, HALF_EVEN);
316       // Now figure out what rounding mode we should behave like (it depends if FLOOR was
317       // odd/even).
318       boolean floorWasEven = (BigIntegerMath.log10(x, FLOOR) & 1) == 0;
319       assertEquals(BigIntegerMath.log10(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven);
320     }
321   }
322 
323   @J2ktIncompatible
324   @GwtIncompatible // TODO
testLog10TrivialOnPowerOf10()325   public void testLog10TrivialOnPowerOf10() {
326     BigInteger x = BigInteger.TEN.pow(100);
327     for (RoundingMode mode : ALL_ROUNDING_MODES) {
328       assertEquals(100, BigIntegerMath.log10(x, mode));
329     }
330   }
331 
332   @J2ktIncompatible
333   @GwtIncompatible // TODO
testSqrtZeroAlwaysZero()334   public void testSqrtZeroAlwaysZero() {
335     for (RoundingMode mode : ALL_ROUNDING_MODES) {
336       assertEquals(ZERO, BigIntegerMath.sqrt(ZERO, mode));
337     }
338   }
339 
340   @J2ktIncompatible
341   @GwtIncompatible // TODO
testSqrtNegativeAlwaysThrows()342   public void testSqrtNegativeAlwaysThrows() {
343     for (RoundingMode mode : ALL_ROUNDING_MODES) {
344       try {
345         BigIntegerMath.sqrt(BigInteger.valueOf(-1), mode);
346         fail("Expected IllegalArgumentException");
347       } catch (IllegalArgumentException expected) {
348       }
349     }
350   }
351 
352   @J2ktIncompatible
353   @GwtIncompatible // TODO
testSqrtFloor()354   public void testSqrtFloor() {
355     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
356       for (RoundingMode mode : asList(FLOOR, DOWN)) {
357         BigInteger result = BigIntegerMath.sqrt(x, mode);
358         assertTrue(result.compareTo(ZERO) > 0);
359         assertTrue(result.pow(2).compareTo(x) <= 0);
360         assertTrue(result.add(ONE).pow(2).compareTo(x) > 0);
361       }
362     }
363   }
364 
365   @J2ktIncompatible
366   @GwtIncompatible // TODO
testSqrtCeiling()367   public void testSqrtCeiling() {
368     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
369       for (RoundingMode mode : asList(CEILING, UP)) {
370         BigInteger result = BigIntegerMath.sqrt(x, mode);
371         assertTrue(result.compareTo(ZERO) > 0);
372         assertTrue(result.pow(2).compareTo(x) >= 0);
373         assertTrue(result.signum() == 0 || result.subtract(ONE).pow(2).compareTo(x) < 0);
374       }
375     }
376   }
377 
378   // Relies on the correctness of sqrt(BigInteger, FLOOR).
379   @J2ktIncompatible
380   @GwtIncompatible // TODO
381   public void testSqrtExact() {
382     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
383       BigInteger floor = BigIntegerMath.sqrt(x, FLOOR);
384       // We only expect an exception if x was not a perfect square.
385       boolean isPerfectSquare = floor.pow(2).equals(x);
386       try {
387         assertEquals(floor, BigIntegerMath.sqrt(x, UNNECESSARY));
388         assertTrue(isPerfectSquare);
389       } catch (ArithmeticException e) {
390         assertFalse(isPerfectSquare);
391       }
392     }
393   }
394 
395   @J2ktIncompatible
396   @GwtIncompatible // TODO
397   public void testSqrtHalfUp() {
398     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
399       BigInteger result = BigIntegerMath.sqrt(x, HALF_UP);
400       BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE);
401       BigInteger x4 = x.shiftLeft(2);
402       // sqrt(x) < result + 0.5, so 4 * x < (result + 0.5)^2 * 4
403       // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1
404       assertTrue(x4.compareTo(plusHalfSquared) < 0);
405       BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE);
406       // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4
407       // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1
408       assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) >= 0);
409     }
410   }
411 
412   @J2ktIncompatible
413   @GwtIncompatible // TODO
414   public void testSqrtHalfDown() {
415     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
416       BigInteger result = BigIntegerMath.sqrt(x, HALF_DOWN);
417       BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE);
418       BigInteger x4 = x.shiftLeft(2);
419       // sqrt(x) <= result + 0.5, so 4 * x <= (result + 0.5)^2 * 4
420       // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1
421       assertTrue(x4.compareTo(plusHalfSquared) <= 0);
422       BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE);
423       // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4
424       // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1
425       assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) > 0);
426     }
427   }
428 
429   // Relies on the correctness of sqrt(BigInteger, {HALF_UP,HALF_DOWN}).
430   @J2ktIncompatible
431   @GwtIncompatible // TODO
432   public void testSqrtHalfEven() {
433     for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) {
434       BigInteger halfEven = BigIntegerMath.sqrt(x, HALF_EVEN);
435       // Now figure out what rounding mode we should behave like (it depends if FLOOR was
436       // odd/even).
437       boolean floorWasOdd = BigIntegerMath.sqrt(x, FLOOR).testBit(0);
438       assertEquals(BigIntegerMath.sqrt(x, floorWasOdd ? HALF_UP : HALF_DOWN), halfEven);
439     }
440   }
441 
442   @J2ktIncompatible
443   @GwtIncompatible // TODO
444   @AndroidIncompatible // slow
445   public void testDivNonZero() {
446     for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) {
447       for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) {
448         for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
449           BigInteger expected =
450               new BigDecimal(p).divide(new BigDecimal(q), 0, mode).toBigIntegerExact();
451           assertEquals(expected, BigIntegerMath.divide(p, q, mode));
452         }
453       }
454     }
455   }
456 
457   private static final BigInteger BAD_FOR_ANDROID_P = new BigInteger("-9223372036854775808");
458   private static final BigInteger BAD_FOR_ANDROID_Q = new BigInteger("-1");
459 
460   private static final BigInteger BAD_FOR_GINGERBREAD_P = new BigInteger("-9223372036854775808");
461   private static final BigInteger BAD_FOR_GINGERBREAD_Q = new BigInteger("-4294967296");
462 
463   @J2ktIncompatible
464   @GwtIncompatible // TODO
465   @AndroidIncompatible // slow
466   public void testDivNonZeroExact() {
467     boolean isAndroid = System.getProperty("java.runtime.name").contains("Android");
468     for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) {
469       for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) {
470         if (isAndroid && p.equals(BAD_FOR_ANDROID_P) && q.equals(BAD_FOR_ANDROID_Q)) {
471           // https://code.google.com/p/android/issues/detail?id=196555
472           continue;
473         }
474         if (isAndroid && p.equals(BAD_FOR_GINGERBREAD_P) && q.equals(BAD_FOR_GINGERBREAD_Q)) {
475           // Works fine under Marshmallow, so I haven't filed a bug.
476           continue;
477         }
478 
479         boolean dividesEvenly = p.remainder(q).equals(ZERO);
480 
481         try {
482           BigInteger quotient = BigIntegerMath.divide(p, q, UNNECESSARY);
483           BigInteger undone = quotient.multiply(q);
484           if (!p.equals(undone)) {
485             failFormat("expected %s.multiply(%s) = %s; got %s", quotient, q, p, undone);
486           }
487           assertTrue(dividesEvenly);
488         } catch (ArithmeticException e) {
489           assertFalse(dividesEvenly);
490         }
491       }
492     }
493   }
494 
495   @J2ktIncompatible
496   @GwtIncompatible // TODO
497   public void testZeroDivIsAlwaysZero() {
498     for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) {
499       for (RoundingMode mode : ALL_ROUNDING_MODES) {
500         assertEquals(ZERO, BigIntegerMath.divide(ZERO, q, mode));
501       }
502     }
503   }
504 
505   @J2ktIncompatible
506   @GwtIncompatible // TODO
507   public void testDivByZeroAlwaysFails() {
508     for (BigInteger p : ALL_BIGINTEGER_CANDIDATES) {
509       for (RoundingMode mode : ALL_ROUNDING_MODES) {
510         try {
511           BigIntegerMath.divide(p, ZERO, mode);
512           fail("Expected ArithmeticException");
513         } catch (ArithmeticException expected) {
514         }
515       }
516     }
517   }
518 
519   public void testFactorial() {
520     BigInteger expected = BigInteger.ONE;
521     for (int i = 1; i <= 200; i++) {
522       expected = expected.multiply(BigInteger.valueOf(i));
523       assertEquals(expected, BigIntegerMath.factorial(i));
524     }
525   }
526 
527   public void testFactorial0() {
528     assertEquals(BigInteger.ONE, BigIntegerMath.factorial(0));
529   }
530 
531   public void testFactorialNegative() {
532     try {
533       BigIntegerMath.factorial(-1);
534       fail("Expected IllegalArgumentException");
535     } catch (IllegalArgumentException expected) {
536     }
537   }
538 
539   public void testBinomialSmall() {
540     runBinomialTest(0, 30);
541   }
542 
543   @J2ktIncompatible
544   @GwtIncompatible // too slow
545   public void testBinomialLarge() {
546     runBinomialTest(31, 100);
547   }
548 
549   // Depends on the correctness of BigIntegerMath.factorial
550   private static void runBinomialTest(int firstN, int lastN) {
551     for (int n = firstN; n <= lastN; n++) {
552       for (int k = 0; k <= n; k++) {
553         BigInteger expected =
554             BigIntegerMath.factorial(n)
555                 .divide(BigIntegerMath.factorial(k))
556                 .divide(BigIntegerMath.factorial(n - k));
557         assertEquals(expected, BigIntegerMath.binomial(n, k));
558       }
559     }
560   }
561 
562   public void testBinomialOutside() {
563     for (int n = 0; n <= 50; n++) {
564       try {
565         BigIntegerMath.binomial(n, -1);
566         fail("Expected IllegalArgumentException");
567       } catch (IllegalArgumentException expected) {
568       }
569       try {
570         BigIntegerMath.binomial(n, n + 1);
571         fail("Expected IllegalArgumentException");
572       } catch (IllegalArgumentException expected) {
573       }
574     }
575   }
576 
577   @J2ktIncompatible
578   @GwtIncompatible
579   private static final class RoundToDoubleTester {
580     private final BigInteger input;
581     private final Map<RoundingMode, Double> expectedValues = new EnumMap<>(RoundingMode.class);
582     private boolean unnecessaryShouldThrow = false;
583 
584     RoundToDoubleTester(BigInteger input) {
585       this.input = input;
586     }
587 
588     @CanIgnoreReturnValue
589     RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) {
590       for (RoundingMode mode : modes) {
591         Double previous = expectedValues.put(mode, expectedValue);
592         if (previous != null) {
593           throw new AssertionError();
594         }
595       }
596       return this;
597     }
598 
599     @CanIgnoreReturnValue
600     public RoundToDoubleTester roundUnnecessaryShouldThrow() {
601       unnecessaryShouldThrow = true;
602       return this;
603     }
604 
605     public void test() {
606       assertThat(expectedValues.keySet())
607           .containsAtLeastElementsIn(EnumSet.complementOf(EnumSet.of(UNNECESSARY)));
608       for (Map.Entry<RoundingMode, Double> entry : expectedValues.entrySet()) {
609         RoundingMode mode = entry.getKey();
610         Double expectation = entry.getValue();
611         assertWithMessage("roundToDouble(" + input + ", " + mode + ")")
612             .that(BigIntegerMath.roundToDouble(input, mode))
613             .isEqualTo(expectation);
614       }
615 
616       if (!expectedValues.containsKey(UNNECESSARY)) {
617         assertWithMessage("Expected roundUnnecessaryShouldThrow call")
618             .that(unnecessaryShouldThrow)
619             .isTrue();
620         try {
621           BigIntegerMath.roundToDouble(input, UNNECESSARY);
622           fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)");
623         } catch (ArithmeticException expected) {
624           // expected
625         }
626       }
627     }
628   }
629 
630   @J2ktIncompatible
631   @GwtIncompatible
632   public void testRoundToDouble_Zero() {
633     new RoundToDoubleTester(BigInteger.ZERO).setExpectation(0.0, values()).test();
634   }
635 
636   @J2ktIncompatible
637   @GwtIncompatible
638   public void testRoundToDouble_smallPositive() {
639     new RoundToDoubleTester(BigInteger.valueOf(16)).setExpectation(16.0, values()).test();
640   }
641 
642   @J2ktIncompatible
643   @GwtIncompatible
644   public void testRoundToDouble_maxPreciselyRepresentable() {
645     new RoundToDoubleTester(BigInteger.valueOf(1L << 53))
646         .setExpectation(Math.pow(2, 53), values())
647         .test();
648   }
649 
650   @J2ktIncompatible
651   @GwtIncompatible
652   public void testRoundToDouble_maxPreciselyRepresentablePlusOne() {
653     double twoToThe53 = Math.pow(2, 53);
654     // the representable doubles are 2^53 and 2^53 + 2.
655     // 2^53+1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down.
656     new RoundToDoubleTester(BigInteger.valueOf((1L << 53) + 1))
657         .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN)
658         .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP)
659         .roundUnnecessaryShouldThrow()
660         .test();
661   }
662 
663   @J2ktIncompatible
664   @GwtIncompatible
665   public void testRoundToDouble_twoToThe54PlusOne() {
666     double twoToThe54 = Math.pow(2, 54);
667     // the representable doubles are 2^54 and 2^54 + 4
668     // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down.
669     new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 1))
670         .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN)
671         .setExpectation(Math.nextUp(twoToThe54), CEILING, UP)
672         .roundUnnecessaryShouldThrow()
673         .test();
674   }
675 
676   @J2ktIncompatible
677   @GwtIncompatible
678   public void testRoundToDouble_twoToThe54PlusThree() {
679     double twoToThe54 = Math.pow(2, 54);
680     // the representable doubles are 2^54 and 2^54 + 4
681     // 2^54+3 is more than halfway between, so HALF_DOWN and HALF_UP will both go up.
682     new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 3))
683         .setExpectation(twoToThe54, DOWN, FLOOR)
684         .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN)
685         .roundUnnecessaryShouldThrow()
686         .test();
687   }
688 
689   @J2ktIncompatible
690   @GwtIncompatible
691   public void testRoundToDouble_twoToThe54PlusFour() {
692     new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 4))
693         .setExpectation(Math.pow(2, 54) + 4, values())
694         .test();
695   }
696 
697   @J2ktIncompatible
698   @GwtIncompatible
699   public void testRoundToDouble_maxDouble() {
700     BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY);
701     new RoundToDoubleTester(maxDoubleAsBI).setExpectation(Double.MAX_VALUE, values()).test();
702   }
703 
704   @J2ktIncompatible
705   @GwtIncompatible
706   public void testRoundToDouble_maxDoublePlusOne() {
707     BigInteger maxDoubleAsBI =
708         DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY).add(BigInteger.ONE);
709     new RoundToDoubleTester(maxDoubleAsBI)
710         .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN)
711         .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING)
712         .roundUnnecessaryShouldThrow()
713         .test();
714   }
715 
716   @J2ktIncompatible
717   @GwtIncompatible
718   public void testRoundToDouble_wayTooBig() {
719     BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT);
720     new RoundToDoubleTester(bi)
721         .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN)
722         .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING)
723         .roundUnnecessaryShouldThrow()
724         .test();
725   }
726 
727   @J2ktIncompatible
728   @GwtIncompatible
729   public void testRoundToDouble_smallNegative() {
730     new RoundToDoubleTester(BigInteger.valueOf(-16)).setExpectation(-16.0, values()).test();
731   }
732 
733   @J2ktIncompatible
734   @GwtIncompatible
735   public void testRoundToDouble_minPreciselyRepresentable() {
736     new RoundToDoubleTester(BigInteger.valueOf(-1L << 53))
737         .setExpectation(-Math.pow(2, 53), values())
738         .test();
739   }
740 
741   @J2ktIncompatible
742   @GwtIncompatible
743   public void testRoundToDouble_minPreciselyRepresentableMinusOne() {
744     // the representable doubles are -2^53 and -2^53 - 2.
745     // -2^53-1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down.
746     new RoundToDoubleTester(BigInteger.valueOf((-1L << 53) - 1))
747         .setExpectation(-Math.pow(2, 53), DOWN, CEILING, HALF_DOWN, HALF_EVEN)
748         .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 53)), FLOOR, UP, HALF_UP)
749         .roundUnnecessaryShouldThrow()
750         .test();
751   }
752 
753   @J2ktIncompatible
754   @GwtIncompatible
755   public void testRoundToDouble_negativeTwoToThe54MinusOne() {
756     new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 1))
757         .setExpectation(-Math.pow(2, 54), DOWN, CEILING, HALF_DOWN, HALF_UP, HALF_EVEN)
758         .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP)
759         .roundUnnecessaryShouldThrow()
760         .test();
761   }
762 
763   @J2ktIncompatible
764   @GwtIncompatible
765   public void testRoundToDouble_negativeTwoToThe54MinusThree() {
766     new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 3))
767         .setExpectation(-Math.pow(2, 54), DOWN, CEILING)
768         .setExpectation(
769             DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP, HALF_DOWN, HALF_UP, HALF_EVEN)
770         .roundUnnecessaryShouldThrow()
771         .test();
772   }
773 
774   @J2ktIncompatible
775   @GwtIncompatible
776   public void testRoundToDouble_negativeTwoToThe54MinusFour() {
777     new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 4))
778         .setExpectation(-Math.pow(2, 54) - 4, values())
779         .test();
780   }
781 
782   @J2ktIncompatible
783   @GwtIncompatible
784   public void testRoundToDouble_minDouble() {
785     BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY);
786     new RoundToDoubleTester(minDoubleAsBI).setExpectation(-Double.MAX_VALUE, values()).test();
787   }
788 
789   @J2ktIncompatible
790   @GwtIncompatible
791   public void testRoundToDouble_minDoubleMinusOne() {
792     BigInteger minDoubleAsBI =
793         DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY).subtract(BigInteger.ONE);
794     new RoundToDoubleTester(minDoubleAsBI)
795         .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN)
796         .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR)
797         .roundUnnecessaryShouldThrow()
798         .test();
799   }
800 
801   @J2ktIncompatible
802   @GwtIncompatible
803   public void testRoundToDouble_negativeWayTooBig() {
804     BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT).negate();
805     new RoundToDoubleTester(bi)
806         .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN)
807         .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR)
808         .roundUnnecessaryShouldThrow()
809         .test();
810   }
811 
812   @J2ktIncompatible
813   @GwtIncompatible // NullPointerTester
814   public void testNullPointers() {
815     NullPointerTester tester = new NullPointerTester();
816     tester.setDefault(BigInteger.class, ONE);
817     tester.setDefault(int.class, 1);
818     tester.setDefault(long.class, 1L);
819     tester.testAllPublicStaticMethods(BigIntegerMath.class);
820   }
821 
822   @J2ktIncompatible
823   @GwtIncompatible // String.format
824   private static void failFormat(String template, Object... args) {
825     fail(String.format(template, args));
826   }
827 }
828