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_LONG_CANDIDATES;
     21 import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES;
     22 import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES;
     23 import static com.google.common.math.MathTesting.EXPONENTS;
     24 import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES;
     25 import static com.google.common.math.MathTesting.NEGATIVE_LONG_CANDIDATES;
     26 import static com.google.common.math.MathTesting.NONZERO_LONG_CANDIDATES;
     27 import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES;
     28 import static com.google.common.math.MathTesting.POSITIVE_LONG_CANDIDATES;
     29 import static java.math.BigInteger.valueOf;
     30 import static java.math.RoundingMode.FLOOR;
     31 import static java.math.RoundingMode.UNNECESSARY;
     32 
     33 import com.google.common.testing.NullPointerTester;
     34 
     35 import junit.framework.TestCase;
     36 
     37 import java.math.BigDecimal;
     38 import java.math.BigInteger;
     39 import java.math.RoundingMode;
     40 
     41 /**
     42  * Tests for LongMath.
     43  *
     44  * @author Louis Wasserman
     45  */
     46 public class LongMathTest extends TestCase {
     47   public void testConstantMaxPowerOfSqrt2Unsigned() {
     48     assertEquals(BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Long.SIZE - 1), FLOOR).longValue(),
     49         LongMath.MAX_POWER_OF_SQRT2_UNSIGNED);
     50   }
     51 
     52   public void testConstantsPowersOf10() {
     53     for (int i = 0; i < LongMath.POWERS_OF_10.length; i++) {
     54       assertEquals(LongMath.checkedPow(10, i), LongMath.POWERS_OF_10[i]);
     55     }
     56     try {
     57       LongMath.checkedPow(10, LongMath.POWERS_OF_10.length);
     58       fail("Expected ArithmeticException");
     59     } catch (ArithmeticException expected) {}
     60   }
     61 
     62   public void testConstantsHalfPowersOf10() {
     63     for (int i = 0; i < LongMath.HALF_POWERS_OF_10.length; i++) {
     64       assertEquals(BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * i + 1), FLOOR),
     65           BigInteger.valueOf(LongMath.HALF_POWERS_OF_10[i]));
     66     }
     67     BigInteger nextBigger =
     68         BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * LongMath.HALF_POWERS_OF_10.length + 1), FLOOR);
     69     assertTrue(nextBigger.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0);
     70   }
     71 
     72   public void testConstantsSqrtMaxLong() {
     73     assertEquals(LongMath.sqrt(Long.MAX_VALUE, FLOOR), LongMath.FLOOR_SQRT_MAX_LONG);
     74   }
     75 
     76   public void testConstantsFactorials() {
     77     long expected = 1;
     78     for (int i = 0; i < LongMath.FACTORIALS.length; i++, expected *= i) {
     79       assertEquals(expected, LongMath.FACTORIALS[i]);
     80     }
     81     try {
     82       LongMath.checkedMultiply(
     83           LongMath.FACTORIALS[LongMath.FACTORIALS.length - 1], LongMath.FACTORIALS.length);
     84       fail("Expected ArithmeticException");
     85     } catch (ArithmeticException expect) {}
     86   }
     87 
     88   public void testConstantsBiggestBinomials() {
     89     for (int k = 0; k < LongMath.BIGGEST_BINOMIALS.length; k++) {
     90       assertTrue(fitsInLong(BigIntegerMath.binomial(LongMath.BIGGEST_BINOMIALS[k], k)));
     91       assertTrue(LongMath.BIGGEST_BINOMIALS[k] == Integer.MAX_VALUE
     92           || !fitsInLong(BigIntegerMath.binomial(LongMath.BIGGEST_BINOMIALS[k] + 1, k)));
     93       // In the first case, any long is valid; in the second, we want to test that the next-bigger
     94       // long overflows.
     95     }
     96     int k = LongMath.BIGGEST_BINOMIALS.length;
     97     assertFalse(fitsInLong(BigIntegerMath.binomial(2 * k, k)));
     98     // 2 * k is the smallest value for which we don't replace k with (n-k).
     99   }
    100 
    101   public void testConstantsBiggestSimpleBinomials() {
    102     for (int k = 0; k < LongMath.BIGGEST_SIMPLE_BINOMIALS.length; k++) {
    103       assertTrue(LongMath.BIGGEST_SIMPLE_BINOMIALS[k] <= LongMath.BIGGEST_BINOMIALS[k]);
    104       simpleBinomial(LongMath.BIGGEST_SIMPLE_BINOMIALS[k], k); // mustn't throw
    105       if (LongMath.BIGGEST_SIMPLE_BINOMIALS[k] < Integer.MAX_VALUE) {
    106         // unless all n are fair game with this k
    107         try {
    108           simpleBinomial(LongMath.BIGGEST_SIMPLE_BINOMIALS[k] + 1, k);
    109           fail("Expected ArithmeticException");
    110         } catch (ArithmeticException expected) {}
    111       }
    112     }
    113     try {
    114       int k = LongMath.BIGGEST_SIMPLE_BINOMIALS.length;
    115       simpleBinomial(2 * k, k);
    116       // 2 * k is the smallest value for which we don't replace k with (n-k).
    117       fail("Expected ArithmeticException");
    118     } catch (ArithmeticException expected) {}
    119   }
    120 
    121   // Throws an ArithmeticException if "the simple implementation" of binomial coefficients overflows
    122   private long simpleBinomial(int n, int k) {
    123     long accum = 1;
    124     for (int i = 0; i < k; i++) {
    125       accum = LongMath.checkedMultiply(accum, n - i);
    126       accum /= i + 1;
    127     }
    128     return accum;
    129   }
    130 
    131   public void testIsPowerOfTwo() {
    132     for (long x : ALL_LONG_CANDIDATES) {
    133       // Checks for a single bit set.
    134       boolean expected = x > 0 & (x & (x - 1)) == 0L;
    135       assertEquals(expected, LongMath.isPowerOfTwo(x));
    136     }
    137   }
    138 
    139   public void testLog2ZeroAlwaysThrows() {
    140     for (RoundingMode mode : ALL_ROUNDING_MODES) {
    141       try {
    142         LongMath.log2(0L, mode);
    143         fail("Expected IllegalArgumentException");
    144       } catch (IllegalArgumentException expected) {}
    145     }
    146   }
    147 
    148   public void testLog2NegativeAlwaysThrows() {
    149     for (long x : NEGATIVE_LONG_CANDIDATES) {
    150       for (RoundingMode mode : ALL_ROUNDING_MODES) {
    151         try {
    152           LongMath.log2(x, mode);
    153           fail("Expected IllegalArgumentException");
    154         } catch (IllegalArgumentException expected) {}
    155       }
    156     }
    157   }
    158 
    159   /* Relies on the correctness of BigIntegerMath.log2 for all modes except UNNECESSARY. */
    160   public void testLog2MatchesBigInteger() {
    161     for (long x : POSITIVE_LONG_CANDIDATES) {
    162       for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
    163         // The BigInteger implementation is tested separately, use it as the reference.
    164         assertEquals(BigIntegerMath.log2(valueOf(x), mode), LongMath.log2(x, mode));
    165       }
    166     }
    167   }
    168 
    169   /* Relies on the correctness of isPowerOfTwo(long). */
    170   public void testLog2Exact() {
    171     for (long x : POSITIVE_LONG_CANDIDATES) {
    172       // We only expect an exception if x was not a power of 2.
    173       boolean isPowerOf2 = LongMath.isPowerOfTwo(x);
    174       try {
    175         assertEquals(x, 1L << LongMath.log2(x, UNNECESSARY));
    176         assertTrue(isPowerOf2);
    177       } catch (ArithmeticException e) {
    178         assertFalse(isPowerOf2);
    179       }
    180     }
    181   }
    182 
    183   public void testLog10ZeroAlwaysThrows() {
    184     for (RoundingMode mode : ALL_ROUNDING_MODES) {
    185       try {
    186         LongMath.log10(0L, mode);
    187         fail("Expected IllegalArgumentException");
    188       } catch (IllegalArgumentException expected) {}
    189     }
    190   }
    191 
    192   public void testLog10NegativeAlwaysThrows() {
    193     for (long x : NEGATIVE_LONG_CANDIDATES) {
    194       for (RoundingMode mode : ALL_ROUNDING_MODES) {
    195         try {
    196           LongMath.log10(x, mode);
    197           fail("Expected IllegalArgumentException");
    198         } catch (IllegalArgumentException expected) {}
    199       }
    200     }
    201   }
    202 
    203   // Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY.
    204   public void testLog10MatchesBigInteger() {
    205     for (long x : POSITIVE_LONG_CANDIDATES) {
    206       for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
    207         assertEquals(BigIntegerMath.log10(valueOf(x), mode), LongMath.log10(x, mode));
    208       }
    209     }
    210   }
    211 
    212   // Relies on the correctness of log10(long, FLOOR) and of pow(long, int).
    213   public void testLog10Exact() {
    214     for (long x : POSITIVE_LONG_CANDIDATES) {
    215       int floor = LongMath.log10(x, FLOOR);
    216       boolean expectSuccess = LongMath.pow(10, floor) == x;
    217       try {
    218         assertEquals(floor, LongMath.log10(x, UNNECESSARY));
    219         assertTrue(expectSuccess);
    220       } catch (ArithmeticException e) {
    221         assertFalse(expectSuccess);
    222       }
    223     }
    224   }
    225 
    226   public void testLog10TrivialOnPowerOf10() {
    227     long x = 1000000000000L;
    228     for (RoundingMode mode : ALL_ROUNDING_MODES) {
    229       assertEquals(12, LongMath.log10(x, mode));
    230     }
    231   }
    232 
    233   public void testSqrtNegativeAlwaysThrows() {
    234     for (long x : NEGATIVE_LONG_CANDIDATES) {
    235       for (RoundingMode mode : ALL_ROUNDING_MODES) {
    236         try {
    237           LongMath.sqrt(x, mode);
    238           fail("Expected IllegalArgumentException");
    239         } catch (IllegalArgumentException expected) {}
    240       }
    241     }
    242   }
    243 
    244   // Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY.
    245   public void testSqrtMatchesBigInteger() {
    246     for (long x : POSITIVE_LONG_CANDIDATES) {
    247       for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
    248         // Promote the long value (rather than using longValue() on the expected value) to avoid
    249         // any risk of truncation which could lead to a false positive.
    250         assertEquals(BigIntegerMath.sqrt(valueOf(x), mode), valueOf(LongMath.sqrt(x, mode)));
    251       }
    252     }
    253   }
    254 
    255   /* Relies on the correctness of sqrt(long, FLOOR). */
    256   public void testSqrtExactMatchesFloorOrThrows() {
    257     for (long x : POSITIVE_LONG_CANDIDATES) {
    258       long logFloor = LongMath.sqrt(x, FLOOR);
    259       // We only expect an exception if x was not a perfect square.
    260       boolean isPerfectSquare = (logFloor * logFloor == x);
    261       try {
    262         assertEquals(logFloor, LongMath.sqrt(x, UNNECESSARY));
    263         assertTrue(isPerfectSquare);
    264       } catch (ArithmeticException e) {
    265         assertFalse(isPerfectSquare);
    266       }
    267     }
    268   }
    269 
    270   public void testPow() {
    271     for (long i : ALL_LONG_CANDIDATES) {
    272       for (int exp : EXPONENTS) {
    273         assertEquals(LongMath.pow(i, exp), valueOf(i)
    274             .pow(exp)
    275             .longValue());
    276       }
    277     }
    278   }
    279 
    280   public void testDivNonZero() {
    281     for (long p : NONZERO_LONG_CANDIDATES) {
    282       for (long q : NONZERO_LONG_CANDIDATES) {
    283         for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
    284           long expected =
    285               new BigDecimal(valueOf(p)).divide(new BigDecimal(valueOf(q)), 0, mode).longValue();
    286           assertEquals(expected, LongMath.divide(p, q, mode));
    287         }
    288       }
    289     }
    290   }
    291 
    292   public void testDivNonZeroExact() {
    293     for (long p : NONZERO_LONG_CANDIDATES) {
    294       for (long q : NONZERO_LONG_CANDIDATES) {
    295         boolean dividesEvenly = (p % q) == 0L;
    296 
    297         try {
    298           assertEquals(p, LongMath.divide(p, q, UNNECESSARY) * q);
    299           assertTrue(dividesEvenly);
    300         } catch (ArithmeticException e) {
    301           assertFalse(dividesEvenly);
    302         }
    303       }
    304     }
    305   }
    306 
    307   public void testZeroDivIsAlwaysZero() {
    308     for (long q : NONZERO_LONG_CANDIDATES) {
    309       for (RoundingMode mode : ALL_ROUNDING_MODES) {
    310         assertEquals(0L, LongMath.divide(0L, q, mode));
    311       }
    312     }
    313   }
    314 
    315   public void testDivByZeroAlwaysFails() {
    316     for (long p : ALL_LONG_CANDIDATES) {
    317       for (RoundingMode mode : ALL_ROUNDING_MODES) {
    318         try {
    319           LongMath.divide(p, 0L, mode);
    320           fail("Expected ArithmeticException");
    321         } catch (ArithmeticException expected) {}
    322       }
    323     }
    324   }
    325 
    326   public void testIntMod() {
    327     for (long x : ALL_LONG_CANDIDATES) {
    328       for (int m : POSITIVE_INTEGER_CANDIDATES) {
    329         assertEquals(valueOf(x)
    330             .mod(valueOf(m))
    331             .intValue(), LongMath.mod(x, m));
    332       }
    333     }
    334   }
    335 
    336   public void testIntModNegativeModulusFails() {
    337     for (long x : ALL_LONG_CANDIDATES) {
    338       for (int m : NEGATIVE_INTEGER_CANDIDATES) {
    339         try {
    340           LongMath.mod(x, m);
    341           fail("Expected ArithmeticException");
    342         } catch (ArithmeticException expected) {}
    343       }
    344     }
    345   }
    346 
    347   public void testIntModZeroModulusFails() {
    348     for (long x : ALL_LONG_CANDIDATES) {
    349       try {
    350         LongMath.mod(x, 0);
    351         fail("Expected AE");
    352       } catch (ArithmeticException expected) {}
    353     }
    354   }
    355 
    356   public void testMod() {
    357     for (long x : ALL_LONG_CANDIDATES) {
    358       for (long m : POSITIVE_LONG_CANDIDATES) {
    359         assertEquals(valueOf(x)
    360             .mod(valueOf(m))
    361             .longValue(), LongMath.mod(x, m));
    362       }
    363     }
    364   }
    365 
    366   public void testModNegativeModulusFails() {
    367     for (long x : ALL_LONG_CANDIDATES) {
    368       for (long m : NEGATIVE_LONG_CANDIDATES) {
    369         try {
    370           LongMath.mod(x, m);
    371           fail("Expected ArithmeticException");
    372         } catch (ArithmeticException expected) {}
    373       }
    374     }
    375   }
    376 
    377   public void testGCD() {
    378     for (long a : POSITIVE_LONG_CANDIDATES) {
    379       for (long b : POSITIVE_LONG_CANDIDATES) {
    380         assertEquals(valueOf(a).gcd(valueOf(b)), valueOf(LongMath.gcd(a, b)));
    381       }
    382     }
    383   }
    384 
    385   public void testGCDZero() {
    386     for (long a : POSITIVE_LONG_CANDIDATES) {
    387       assertEquals(a, LongMath.gcd(a, 0));
    388       assertEquals(a, LongMath.gcd(0, a));
    389     }
    390     assertEquals(0, LongMath.gcd(0, 0));
    391   }
    392 
    393   public void testGCDNegativePositiveThrows() {
    394     for (long a : NEGATIVE_LONG_CANDIDATES) {
    395       try {
    396         LongMath.gcd(a, 3);
    397         fail("Expected IllegalArgumentException");
    398       } catch (IllegalArgumentException expected) {}
    399       try {
    400         LongMath.gcd(3, a);
    401         fail("Expected IllegalArgumentException");
    402       } catch (IllegalArgumentException expected) {}
    403     }
    404   }
    405 
    406   public void testGCDNegativeZeroThrows() {
    407     for (long a : NEGATIVE_LONG_CANDIDATES) {
    408       try {
    409         LongMath.gcd(a, 0);
    410         fail("Expected IllegalArgumentException");
    411       } catch (IllegalArgumentException expected) {}
    412       try {
    413         LongMath.gcd(0, a);
    414         fail("Expected IllegalArgumentException");
    415       } catch (IllegalArgumentException expected) {}
    416     }
    417   }
    418 
    419   public void testCheckedAdd() {
    420     for (long a : ALL_INTEGER_CANDIDATES) {
    421       for (long b : ALL_INTEGER_CANDIDATES) {
    422         BigInteger expectedResult = valueOf(a).add(valueOf(b));
    423         boolean expectedSuccess = fitsInLong(expectedResult);
    424         try {
    425           assertEquals(a + b, LongMath.checkedAdd(a, b));
    426           assertTrue(expectedSuccess);
    427         } catch (ArithmeticException e) {
    428           assertFalse(expectedSuccess);
    429         }
    430       }
    431     }
    432   }
    433 
    434   public void testCheckedSubtract() {
    435     for (long a : ALL_INTEGER_CANDIDATES) {
    436       for (long b : ALL_INTEGER_CANDIDATES) {
    437         BigInteger expectedResult = valueOf(a).subtract(valueOf(b));
    438         boolean expectedSuccess = fitsInLong(expectedResult);
    439         try {
    440           assertEquals(a - b, LongMath.checkedSubtract(a, b));
    441           assertTrue(expectedSuccess);
    442         } catch (ArithmeticException e) {
    443           assertFalse(expectedSuccess);
    444         }
    445       }
    446     }
    447   }
    448 
    449   public void testCheckedMultiply() {
    450     for (long a : ALL_INTEGER_CANDIDATES) {
    451       for (long b : ALL_INTEGER_CANDIDATES) {
    452         BigInteger expectedResult = valueOf(a).multiply(valueOf(b));
    453         boolean expectedSuccess = fitsInLong(expectedResult);
    454         try {
    455           assertEquals(a * b, LongMath.checkedMultiply(a, b));
    456           assertTrue(expectedSuccess);
    457         } catch (ArithmeticException e) {
    458           assertFalse(expectedSuccess);
    459         }
    460       }
    461     }
    462   }
    463 
    464   public void testCheckedPow() {
    465     for (long b : ALL_INTEGER_CANDIDATES) {
    466       for (int exp : EXPONENTS) {
    467         BigInteger expectedResult = valueOf(b).pow(exp);
    468         boolean expectedSuccess = fitsInLong(expectedResult);
    469         try {
    470           assertEquals(expectedResult.longValue(), LongMath.checkedPow(b, exp));
    471           assertTrue(expectedSuccess);
    472         } catch (ArithmeticException e) {
    473           assertFalse(expectedSuccess);
    474         }
    475       }
    476     }
    477   }
    478 
    479   // Depends on the correctness of BigIntegerMath.factorial.
    480   public void testFactorial() {
    481     for (int n = 0; n <= 50; n++) {
    482       BigInteger expectedBig = BigIntegerMath.factorial(n);
    483       long expectedLong = fitsInLong(expectedBig) ? expectedBig.longValue() : Long.MAX_VALUE;
    484       assertEquals(expectedLong, LongMath.factorial(n));
    485     }
    486   }
    487 
    488   public void testFactorialNegative() {
    489     for (int n : NEGATIVE_INTEGER_CANDIDATES) {
    490       try {
    491         LongMath.factorial(n);
    492         fail("Expected IllegalArgumentException");
    493       } catch (IllegalArgumentException expected) {}
    494     }
    495   }
    496 
    497   // Depends on the correctness of BigIntegerMath.binomial.
    498   public void testBinomial() {
    499     for (int n = 0; n <= 70; n++) {
    500       for (int k = 0; k <= n; k++) {
    501         BigInteger expectedBig = BigIntegerMath.binomial(n, k);
    502         long expectedLong = fitsInLong(expectedBig) ? expectedBig.longValue() : Long.MAX_VALUE;
    503         assertEquals(expectedLong, LongMath.binomial(n, k));
    504       }
    505     }
    506   }
    507 
    508   public void testBinomialOutside() {
    509     for (int n = 0; n <= 50; n++) {
    510       try {
    511         LongMath.binomial(n, -1);
    512         fail("Expected IllegalArgumentException");
    513       } catch (IllegalArgumentException expected) {}
    514       try {
    515         LongMath.binomial(n, n + 1);
    516         fail("Expected IllegalArgumentException");
    517       } catch (IllegalArgumentException expected) {}
    518     }
    519   }
    520 
    521   public void testBinomialNegative() {
    522     for (int n : NEGATIVE_INTEGER_CANDIDATES) {
    523       try {
    524         LongMath.binomial(n, 0);
    525         fail("Expected IllegalArgumentException");
    526       } catch (IllegalArgumentException expected) {}
    527     }
    528   }
    529 
    530   private boolean fitsInLong(BigInteger big) {
    531     return big.bitLength() <= 63;
    532   }
    533 
    534   public void testNullPointers() throws Exception {
    535     NullPointerTester tester = new NullPointerTester();
    536     tester.setDefault(RoundingMode.class, FLOOR);
    537     tester.setDefault(int.class, 1);
    538     tester.setDefault(long.class, 1L);
    539     tester.testAllPublicStaticMethods(LongMath.class);
    540   }
    541 }
    542