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_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 java.math.BigInteger.valueOf;
     27 import static java.math.RoundingMode.FLOOR;
     28 import static java.math.RoundingMode.UNNECESSARY;
     29 
     30 import com.google.common.annotations.GwtCompatible;
     31 import com.google.common.annotations.GwtIncompatible;
     32 import com.google.common.testing.NullPointerTester;
     33 
     34 import junit.framework.TestCase;
     35 
     36 import java.math.BigDecimal;
     37 import java.math.BigInteger;
     38 import java.math.RoundingMode;
     39 
     40 /**
     41  * Tests for {@link IntMath}.
     42  *
     43  * @author Louis Wasserman
     44  */
     45 @GwtCompatible(emulated = true)
     46 public 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