View Javadoc
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_LONG_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_INTEGER_CANDIDATES;
23  import static com.google.common.math.MathTesting.NEGATIVE_LONG_CANDIDATES;
24  import static com.google.common.math.MathTesting.POSITIVE_LONG_CANDIDATES;
25  import static java.math.BigInteger.valueOf;
26  import static java.math.RoundingMode.UNNECESSARY;
27  
28  import com.google.common.annotations.GwtCompatible;
29  
30  import junit.framework.TestCase;
31  
32  import java.math.BigDecimal;
33  import java.math.BigInteger;
34  import java.math.RoundingMode;
35  
36  /**
37   * Tests for LongMath.
38   *
39   * @author Louis Wasserman
40   */
41  @GwtCompatible(emulated = true)
42  public class LongMathTest extends TestCase {
43    
44    public void testLessThanBranchFree() {
45      for (long x : ALL_LONG_CANDIDATES) {
46        for (long y : ALL_LONG_CANDIDATES) {
47          BigInteger difference = BigInteger.valueOf(x).subtract(BigInteger.valueOf(y));
48          if (fitsInLong(difference)) {
49            int expected = (x < y) ? 1 : 0;
50            int actual = LongMath.lessThanBranchFree(x, y);
51            assertEquals(expected, actual);
52          }
53        }
54      }
55    }
56  
57    // Throws an ArithmeticException if "the simple implementation" of binomial coefficients overflows
58  
59    public void testLog2ZeroAlwaysThrows() {
60      for (RoundingMode mode : ALL_ROUNDING_MODES) {
61        try {
62          LongMath.log2(0L, mode);
63          fail("Expected IllegalArgumentException");
64        } catch (IllegalArgumentException expected) {}
65      }
66    }
67  
68    public void testLog2NegativeAlwaysThrows() {
69      for (long x : NEGATIVE_LONG_CANDIDATES) {
70        for (RoundingMode mode : ALL_ROUNDING_MODES) {
71          try {
72            LongMath.log2(x, mode);
73            fail("Expected IllegalArgumentException");
74          } catch (IllegalArgumentException expected) {}
75        }
76      }
77    }
78  
79    /* Relies on the correctness of BigIntegerMath.log2 for all modes except UNNECESSARY. */
80    public void testLog2MatchesBigInteger() {
81      for (long x : POSITIVE_LONG_CANDIDATES) {
82        for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
83          // The BigInteger implementation is tested separately, use it as the reference.
84          assertEquals(BigIntegerMath.log2(valueOf(x), mode), LongMath.log2(x, mode));
85        }
86      }
87    }
88  
89    /* Relies on the correctness of isPowerOfTwo(long). */
90    public void testLog2Exact() {
91      for (long x : POSITIVE_LONG_CANDIDATES) {
92        // We only expect an exception if x was not a power of 2.
93        boolean isPowerOf2 = LongMath.isPowerOfTwo(x);
94        try {
95          assertEquals(x, 1L << LongMath.log2(x, UNNECESSARY));
96          assertTrue(isPowerOf2);
97        } catch (ArithmeticException e) {
98          assertFalse(isPowerOf2);
99        }
100     }
101   }
102 
103   // Relies on the correctness of BigIntegerMath.log10 for all modes except UNNECESSARY.
104 
105   // Relies on the correctness of log10(long, FLOOR) and of pow(long, int).
106 
107   // Relies on the correctness of BigIntegerMath.sqrt for all modes except UNNECESSARY.
108 
109   /* Relies on the correctness of sqrt(long, FLOOR). */
110 
111   public void testGCDExhaustive() {
112     for (long a : POSITIVE_LONG_CANDIDATES) {
113       for (long b : POSITIVE_LONG_CANDIDATES) {
114         assertEquals(valueOf(a).gcd(valueOf(b)), valueOf(LongMath.gcd(a, b)));
115       }
116     }
117   }
118 
119   // Depends on the correctness of BigIntegerMath.factorial.
120 
121   // Depends on the correctness of BigIntegerMath.binomial.
122   public void testBinomial() {
123     for (int n = 0; n <= 70; n++) {
124       for (int k = 0; k <= n; k++) {
125         BigInteger expectedBig = BigIntegerMath.binomial(n, k);
126         long expectedLong = fitsInLong(expectedBig) ? expectedBig.longValue() : Long.MAX_VALUE;
127         assertEquals(expectedLong, LongMath.binomial(n, k));
128       }
129     }
130   }
131 
132   public void testBinomialOutside() {
133     for (int n = 0; n <= 50; n++) {
134       try {
135         LongMath.binomial(n, -1);
136         fail("Expected IllegalArgumentException");
137       } catch (IllegalArgumentException expected) {}
138       try {
139         LongMath.binomial(n, n + 1);
140         fail("Expected IllegalArgumentException");
141       } catch (IllegalArgumentException expected) {}
142     }
143   }
144 
145   public void testBinomialNegative() {
146     for (int n : NEGATIVE_INTEGER_CANDIDATES) {
147       try {
148         LongMath.binomial(n, 0);
149         fail("Expected IllegalArgumentException");
150       } catch (IllegalArgumentException expected) {}
151     }
152   }
153   
154   public void testSqrtOfLongIsAtMostFloorSqrtMaxLong() {
155     long sqrtMaxLong = (long) Math.sqrt(Long.MAX_VALUE);
156     assertTrue(sqrtMaxLong <= LongMath.FLOOR_SQRT_MAX_LONG);
157   }
158 
159   /**
160    * Helper method that asserts the arithmetic mean of x and y is equal
161    * to the expectedMean.
162    */
163   private static void assertMean(long expectedMean, long x, long y) {
164     assertEquals("The expectedMean should be the same as computeMeanSafely",
165         expectedMean, computeMeanSafely(x, y));
166     assertMean(x, y);
167   }
168 
169   /**
170    * Helper method that asserts the arithmetic mean of x and y is equal
171    *to the result of computeMeanSafely.
172    */
173   private static void assertMean(long x, long y) {
174     long expectedMean = computeMeanSafely(x, y);
175     assertEquals(expectedMean, LongMath.mean(x, y));
176     assertEquals("The mean of x and y should equal the mean of y and x",
177         expectedMean, LongMath.mean(y, x));
178   }
179 
180   /**
181    * Computes the mean in a way that is obvious and resilient to
182    * overflow by using BigInteger arithmetic.
183    */
184   private static long computeMeanSafely(long x, long y) {
185     BigInteger bigX = BigInteger.valueOf(x);
186     BigInteger bigY = BigInteger.valueOf(y);
187     BigDecimal bigMean = new BigDecimal(bigX.add(bigY))
188         .divide(BigDecimal.valueOf(2), BigDecimal.ROUND_FLOOR);
189     // parseInt blows up on overflow as opposed to intValue() which does not.
190     return Long.parseLong(bigMean.toString());
191   }
192 
193   private static boolean fitsInLong(BigInteger big) {
194     return big.bitLength() <= 63;
195   }
196 }
197