Home | History | Annotate | Download | only in math
      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 {
     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 
     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 
     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 
     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 
     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 
     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).
    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 
    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 
    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}).
    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 
    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 
    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 
    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 
    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).
    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 
    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 
    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}).
    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 
    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 
    248   public void testSqrtZeroAlwaysZero() {
    249     for (RoundingMode mode : ALL_ROUNDING_MODES) {
    250       assertEquals(ZERO, BigIntegerMath.sqrt(ZERO, mode));
    251     }
    252   }
    253 
    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 
    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 
    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