Note: this implementation currently delegates to {@link Math#random} * @return a random number between 0.0 and 1.0 @@ -3634,6 +3655,319 @@ public class FastMath { return StrictMath.IEEEremainder(dividend, divisor); // TODO provide our own implementation } + /** Convert a long to interger, detecting overflows + * @param n number to convert to int + * @return integer with same valie as n if no overflows occur + * @exception MathArithmeticException if n cannot fit into an int + * @since 3.4 + */ + public static int toIntExact(final long n) throws MathArithmeticException { + if (n < Integer.MIN_VALUE || n > Integer.MAX_VALUE) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW); + } + return (int) n; + } + + /** Increment a number, detecting overflows. + * @param n number to increment + * @return n+1 if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static int incrementExact(final int n) throws MathArithmeticException { + + if (n == Integer.MAX_VALUE) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, n, 1); + } + + return n + 1; + + } + + /** Increment a number, detecting overflows. + * @param n number to increment + * @return n+1 if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static long incrementExact(final long n) throws MathArithmeticException { + + if (n == Long.MAX_VALUE) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, n, 1); + } + + return n + 1; + + } + + /** Decrement a number, detecting overflows. + * @param n number to decrement + * @return n-1 if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static int decrementExact(final int n) throws MathArithmeticException { + + if (n == Integer.MIN_VALUE) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, n, 1); + } + + return n - 1; + + } + + /** Decrement a number, detecting overflows. + * @param n number to decrement + * @return n-1 if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static long decrementExact(final long n) throws MathArithmeticException { + + if (n == Long.MIN_VALUE) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, n, 1); + } + + return n - 1; + + } + + /** Add two numbers, detecting overflows. + * @param a first number to add + * @param b second number to add + * @return a+b if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static int addExact(final int a, final int b) throws MathArithmeticException { + + // compute sum + final int sum = a + b; + + // check for overflow + if ((a ^ b) >= 0 && (sum ^ b) < 0) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, a, b); + } + + return sum; + + } + + /** Add two numbers, detecting overflows. + * @param a first number to add + * @param b second number to add + * @return a+b if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static long addExact(final long a, final long b) throws MathArithmeticException { + + // compute sum + final long sum = a + b; + + // check for overflow + if ((a ^ b) >= 0 && (sum ^ b) < 0) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, a, b); + } + + return sum; + + } + + /** Subtract two numbers, detecting overflows. + * @param a first number + * @param b second number to subtract from a + * @return a-b if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static int subtractExact(final int a, final int b) { + + // compute subtraction + final int sub = a - b; + + // check for overflow + if ((a ^ b) < 0 && (sub ^ b) >= 0) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, a, b); + } + + return sub; + + } + + /** Subtract two numbers, detecting overflows. + * @param a first number + * @param b second number to subtract from a + * @return a-b if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static long subtractExact(final long a, final long b) { + + // compute subtraction + final long sub = a - b; + + // check for overflow + if ((a ^ b) < 0 && (sub ^ b) >= 0) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, a, b); + } + + return sub; + + } + + /** Multiply two numbers, detecting overflows. + * @param a first number to multiply + * @param b second number to multiply + * @return a*b if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static int multiplyExact(final int a, final int b) { + if (((b > 0) && (a > Integer.MAX_VALUE / b || a < Integer.MIN_VALUE / b)) || + ((b < -1) && (a > Integer.MIN_VALUE / b || a < Integer.MAX_VALUE / b)) || + ((b == -1) && (a == Integer.MIN_VALUE))) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_MULTIPLICATION, a, b); + } + return a * b; + } + + /** Multiply two numbers, detecting overflows. + * @param a first number to multiply + * @param b second number to multiply + * @return a*b if no overflows occur + * @exception MathArithmeticException if an overflow occurs + * @since 3.4 + */ + public static long multiplyExact(final long a, final long b) { + if (((b > 0l) && (a > Long.MAX_VALUE / b || a < Long.MIN_VALUE / b)) || + ((b < -1l) && (a > Long.MIN_VALUE / b || a < Long.MAX_VALUE / b)) || + ((b == -1l) && (a == Long.MIN_VALUE))) { + throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_MULTIPLICATION, a, b); + } + return a * b; + } + + /** Finds q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0. + *
+ * This methods returns the same value as integer division when + * a and b are same signs, but returns a different value when + * they are opposite (i.e. q is negative). + *
+ * @param a dividend + * @param b divisor + * @return q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0 + * @exception MathArithmeticException if b == 0 + * @see #floorMod(int, int) + * @since 3.4 + */ + public static int floorDiv(final int a, final int b) throws MathArithmeticException { + + if (b == 0) { + throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR); + } + + final int m = a % b; + if ((a ^ b) >= 0 || m == 0) { + // a an b have same sign, or division is exact + return a / b; + } else { + // a and b have opposite signs and division is not exact + return (a / b) - 1; + } + + } + + /** Finds q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0. + *+ * This methods returns the same value as integer division when + * a and b are same signs, but returns a different value when + * they are opposite (i.e. q is negative). + *
+ * @param a dividend + * @param b divisor + * @return q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0 + * @exception MathArithmeticException if b == 0 + * @see #floorMod(long, long) + * @since 3.4 + */ + public static long floorDiv(final long a, final long b) throws MathArithmeticException { + + if (b == 0l) { + throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR); + } + + final long m = a % b; + if ((a ^ b) >= 0l || m == 0l) { + // a an b have same sign, or division is exact + return a / b; + } else { + // a and b have opposite signs and division is not exact + return (a / b) - 1l; + } + + } + + /** Finds r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0. + *+ * This methods returns the same value as integer modulo when + * a and b are same signs, but returns a different value when + * they are opposite (i.e. q is negative). + *
+ * @param a dividend + * @param b divisor + * @return r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0 + * @exception MathArithmeticException if b == 0 + * @see #floorDiv(int, int) + * @since 3.4 + */ + public static int floorMod(final int a, final int b) throws MathArithmeticException { + + if (b == 0) { + throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR); + } + + final int m = a % b; + if ((a ^ b) >= 0 || m == 0) { + // a an b have same sign, or division is exact + return m; + } else { + // a and b have opposite signs and division is not exact + return b + m; + } + + } + + /** Finds r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0. + *+ * This methods returns the same value as integer modulo when + * a and b are same signs, but returns a different value when + * they are opposite (i.e. q is negative). + *
+ * @param a dividend + * @param b divisor + * @return r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b > 0 + * @exception MathArithmeticException if b == 0 + * @see #floorDiv(long, long) + * @since 3.4 + */ + public static long floorMod(final long a, final long b) { + + if (b == 0l) { + throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR); + } + + final long m = a % b; + if ((a ^ b) >= 0l || m == 0l) { + // a an b have same sign, or division is exact + return m; + } else { + // a and b have opposite signs and division is not exact + return b + m; + } + + } + /** * Returns the first argument with the sign of the second argument. * A NaN {@code sign} argument is treated as positive. diff --git a/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties b/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties index 1a3c23949..599dc7f85 100644 --- a/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties +++ b/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties @@ -265,6 +265,7 @@ OVERFLOW = d\u00e9passement de capacit\u00e9 OVERFLOW_IN_FRACTION = d\u00e9passement de capacit\u00e9 pour la fraction {0}/{1}, son signe ne peut \u00eatre chang\u00e9 OVERFLOW_IN_ADDITION = d\u00e9passement de capacit\u00e9 pour l''addition : {0} + {1} OVERFLOW_IN_SUBTRACTION = d\u00e9passement de capacit\u00e9 pour la soustraction : {0} - {1} +OVERFLOW_IN_MULTIPLICATION = d\u00e9passement de capacit\u00e9 pour la multiplication : {0} * {1} PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD = acc\u00e8s impossible \u00e0 la m\u00e9thode {0} dans la mise en \u0153uvre du pourcentage {1} PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD = l''implantation de pourcentage {0} ne dispose pas de la m\u00e9thode {1} PERMUTATION_EXCEEDS_N = la taille de la permutation ({0}) d\u00e9passe le domaine de la permutation ({1}) diff --git a/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java b/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java index 24d5bbc8c..34344d451 100644 --- a/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java +++ b/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java @@ -23,14 +23,13 @@ import java.util.Locale; import java.util.ResourceBundle; import org.junit.Assert; - import org.junit.Test; public class LocalizedFormatsTest { @Test public void testMessageNumber() { - Assert.assertEquals(319, LocalizedFormats.values().length); + Assert.assertEquals(320, LocalizedFormats.values().length); } @Test diff --git a/src/test/java/org/apache/commons/math3/util/FastMathTest.java b/src/test/java/org/apache/commons/math3/util/FastMathTest.java index 8408b7847..f8607548c 100644 --- a/src/test/java/org/apache/commons/math3/util/FastMathTest.java +++ b/src/test/java/org/apache/commons/math3/util/FastMathTest.java @@ -19,13 +19,16 @@ package org.apache.commons.math3.util; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.math.BigInteger; import org.apache.commons.math3.TestUtils; import org.apache.commons.math3.dfp.Dfp; import org.apache.commons.math3.dfp.DfpField; import org.apache.commons.math3.dfp.DfpMath; +import org.apache.commons.math3.exception.MathArithmeticException; import org.apache.commons.math3.random.MersenneTwister; import org.apache.commons.math3.random.RandomGenerator; +import org.apache.commons.math3.random.Well1024a; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; @@ -958,13 +961,13 @@ public class FastMathTest { @Test public void testNextAfter() { // 0x402fffffffffffff 0x404123456789abcd -> 4030000000000000 - Assert.assertEquals(16.0, FastMath.nextAfter(15.999999999999998, 34.27555555555555), 0.0); + Assert.assertEquals(16.0, FastMath.nextUp(15.999999999999998), 0.0); // 0xc02fffffffffffff 0x404123456789abcd -> c02ffffffffffffe Assert.assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 34.27555555555555), 0.0); // 0x402fffffffffffff 0x400123456789abcd -> 402ffffffffffffe - Assert.assertEquals(15.999999999999996, FastMath.nextAfter(15.999999999999998, 2.142222222222222), 0.0); + Assert.assertEquals(15.999999999999996, FastMath.nextDown(15.999999999999998), 0.0); // 0xc02fffffffffffff 0x400123456789abcd -> c02ffffffffffffe Assert.assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 2.142222222222222), 0.0); @@ -1037,8 +1040,8 @@ public class FastMathTest { Assert.assertEquals(-Float.MAX_VALUE,FastMath.nextAfter(Float.NEGATIVE_INFINITY, 0F), 0F); Assert.assertEquals(Float.MAX_VALUE,FastMath.nextAfter(Float.POSITIVE_INFINITY, 0F), 0F); Assert.assertEquals(Float.NaN,FastMath.nextAfter(Float.NaN, 0F), 0F); - Assert.assertEquals(Float.POSITIVE_INFINITY,FastMath.nextAfter(Float.MAX_VALUE, Float.POSITIVE_INFINITY), 0F); - Assert.assertEquals(Float.NEGATIVE_INFINITY,FastMath.nextAfter(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY), 0F); + Assert.assertEquals(Float.POSITIVE_INFINITY,FastMath.nextUp(Float.MAX_VALUE), 0F); + Assert.assertEquals(Float.NEGATIVE_INFINITY,FastMath.nextDown(-Float.MAX_VALUE), 0F); Assert.assertEquals(Float.MIN_VALUE, FastMath.nextAfter(0F, 1F), 0F); Assert.assertEquals(-Float.MIN_VALUE, FastMath.nextAfter(0F, -1F), 0F); Assert.assertEquals(0F, FastMath.nextAfter(Float.MIN_VALUE, -1F), 0F); @@ -1182,4 +1185,422 @@ public class FastMathTest { } } + @Test + public void testIncrementExactInt() { + int[] specialValues = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, + Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), + -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), + }; + for (int a : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdSum = bdA.add(BigInteger.ONE); + if (bdSum.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || + bdSum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + try { + FastMath.incrementExact(a); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdSum, BigInteger.valueOf(FastMath.incrementExact(a))); + } + } + } + + @Test + public void testDecrementExactInt() { + int[] specialValues = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, + Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), + -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), + }; + for (int a : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdSub = bdA.subtract(BigInteger.ONE); + if (bdSub.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || + bdSub.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + try { + FastMath.decrementExact(a); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdSub, BigInteger.valueOf(FastMath.decrementExact(a))); + } + } + } + + @Test + public void testAddExactInt() { + int[] specialValues = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, + Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), + -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), + }; + for (int a : specialValues) { + for (int b : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdB = BigInteger.valueOf(b); + BigInteger bdSum = bdA.add(bdB); + if (bdSum.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || + bdSum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + try { + FastMath.addExact(a, b); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdSum, BigInteger.valueOf(FastMath.addExact(a, b))); + } + } + } + } + + @Test + public void testAddExactLong() { + long[] specialValues = new long[] { + Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, + Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Long.MIN_VALUE / 2), 0 - (Long.MIN_VALUE / 2), 1 - (Long.MIN_VALUE / 2), + -1 + (Long.MAX_VALUE / 2), 0 + (Long.MAX_VALUE / 2), 1 + (Long.MAX_VALUE / 2), + }; + for (long a : specialValues) { + for (long b : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdB = BigInteger.valueOf(b); + BigInteger bdSum = bdA.add(bdB); + if (bdSum.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || + bdSum.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { + try { + FastMath.addExact(a, b); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdSum, BigInteger.valueOf(FastMath.addExact(a, b))); + } + } + } + } + + @Test + public void testSubtractExactInt() { + int[] specialValues = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, + Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), + -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), + }; + for (int a : specialValues) { + for (int b : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdB = BigInteger.valueOf(b); + BigInteger bdSub = bdA.subtract(bdB); + if (bdSub.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || + bdSub.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + try { + FastMath.subtractExact(a, b); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdSub, BigInteger.valueOf(FastMath.subtractExact(a, b))); + } + } + } + } + + @Test + public void testSubtractExactLong() { + long[] specialValues = new long[] { + Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, + Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Long.MIN_VALUE / 2), 0 - (Long.MIN_VALUE / 2), 1 - (Long.MIN_VALUE / 2), + -1 + (Long.MAX_VALUE / 2), 0 + (Long.MAX_VALUE / 2), 1 + (Long.MAX_VALUE / 2), + }; + for (long a : specialValues) { + for (long b : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdB = BigInteger.valueOf(b); + BigInteger bdSub = bdA.subtract(bdB); + if (bdSub.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || + bdSub.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { + try { + FastMath.subtractExact(a, b); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdSub, BigInteger.valueOf(FastMath.subtractExact(a, b))); + } + } + } + } + + @Test + public void testMultiplyExactInt() { + int[] specialValues = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, + Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), + -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), + }; + for (int a : specialValues) { + for (int b : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdB = BigInteger.valueOf(b); + BigInteger bdMul = bdA.multiply(bdB); + if (bdMul.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || + bdMul.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + try { + FastMath.multiplyExact(a, b); + Assert.fail("an exception should have been thrown " + a + b); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdMul, BigInteger.valueOf(FastMath.multiplyExact(a, b))); + } + } + } + } + + @Test + public void testMultiplyExactLong() { + long[] specialValues = new long[] { + Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, + Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + -1 - (Long.MIN_VALUE / 2), 0 - (Long.MIN_VALUE / 2), 1 - (Long.MIN_VALUE / 2), + -1 + (Long.MAX_VALUE / 2), 0 + (Long.MAX_VALUE / 2), 1 + (Long.MAX_VALUE / 2), + }; + for (long a : specialValues) { + for (long b : specialValues) { + BigInteger bdA = BigInteger.valueOf(a); + BigInteger bdB = BigInteger.valueOf(b); + BigInteger bdMul = bdA.multiply(bdB); + if (bdMul.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || + bdMul.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { + try { + FastMath.multiplyExact(a, b); + Assert.fail("an exception should have been thrown " + a + b); + } catch (MathArithmeticException mae) { + // expected + } + } else { + Assert.assertEquals(bdMul, BigInteger.valueOf(FastMath.multiplyExact(a, b))); + } + } + } + } + + @Test(expected=MathArithmeticException.class) + public void testToIntExactTooLow() { + FastMath.toIntExact(-1l + Integer.MIN_VALUE); + } + + @Test(expected=MathArithmeticException.class) + public void testToIntExactTooHigh() { + FastMath.toIntExact(+1l + Integer.MAX_VALUE); + } + + @Test + public void testToIntExact() { + for (int n = -1000; n < 1000; ++n) { + Assert.assertEquals(n, FastMath.toIntExact(0l + n)); + } + Assert.assertEquals(Integer.MIN_VALUE, FastMath.toIntExact(0l + Integer.MIN_VALUE)); + Assert.assertEquals(Integer.MAX_VALUE, FastMath.toIntExact(0l + Integer.MAX_VALUE)); + } + + @Test + public void testFloorDivInt() { + Assert.assertEquals(+1, FastMath.floorDiv(+4, +3)); + Assert.assertEquals(-2, FastMath.floorDiv(-4, +3)); + Assert.assertEquals(-2, FastMath.floorDiv(+4, -3)); + Assert.assertEquals(+1, FastMath.floorDiv(-4, -3)); + try { + FastMath.floorDiv(1, 0); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + for (int a = -100; a <= 100; ++a) { + for (int b = -100; b <= 100; ++b) { + if (b != 0) { + Assert.assertEquals(poorManFloorDiv(a, b), FastMath.floorDiv(a, b)); + } + } + } + } + + @Test + public void testFloorModInt() { + Assert.assertEquals(+1, FastMath.floorMod(+4, +3)); + Assert.assertEquals(+2, FastMath.floorMod(-4, +3)); + Assert.assertEquals(-2, FastMath.floorMod(+4, -3)); + Assert.assertEquals(-1, FastMath.floorMod(-4, -3)); + try { + FastMath.floorMod(1, 0); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + for (int a = -100; a <= 100; ++a) { + for (int b = -100; b <= 100; ++b) { + if (b != 0) { + Assert.assertEquals(poorManFloorMod(a, b), FastMath.floorMod(a, b)); + } + } + } + } + + @Test + public void testFloorDivModInt() { + RandomGenerator generator = new Well1024a(0x7ccab45edeaab90al); + for (int i = 0; i < 10000; ++i) { + int a = generator.nextInt(); + int b = generator.nextInt(); + if (b == 0) { + try { + FastMath.floorDiv(a, b); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + int d = FastMath.floorDiv(a, b); + int m = FastMath.floorMod(a, b); + Assert.assertEquals(FastMath.toIntExact(poorManFloorDiv(a, b)), d); + Assert.assertEquals(FastMath.toIntExact(poorManFloorMod(a, b)), m); + Assert.assertEquals(a, d * b + m); + if (b < 0) { + Assert.assertTrue(m <= 0); + Assert.assertTrue(-m < -b); + } else { + Assert.assertTrue(m >= 0); + Assert.assertTrue(m < b); + } + } + } + } + + @Test + public void testFloorDivLong() { + Assert.assertEquals(+1l, FastMath.floorDiv(+4l, +3l)); + Assert.assertEquals(-2l, FastMath.floorDiv(-4l, +3l)); + Assert.assertEquals(-2l, FastMath.floorDiv(+4l, -3l)); + Assert.assertEquals(+1l, FastMath.floorDiv(-4l, -3l)); + try { + FastMath.floorDiv(1l, 0l); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + for (long a = -100l; a <= 100l; ++a) { + for (long b = -100l; b <= 100l; ++b) { + if (b != 0) { + Assert.assertEquals(poorManFloorDiv(a, b), FastMath.floorDiv(a, b)); + } + } + } + } + + @Test + public void testFloorModLong() { + Assert.assertEquals(+1l, FastMath.floorMod(+4l, +3l)); + Assert.assertEquals(+2l, FastMath.floorMod(-4l, +3l)); + Assert.assertEquals(-2l, FastMath.floorMod(+4l, -3l)); + Assert.assertEquals(-1l, FastMath.floorMod(-4l, -3l)); + try { + FastMath.floorMod(1l, 0l); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + for (long a = -100l; a <= 100l; ++a) { + for (long b = -100l; b <= 100l; ++b) { + if (b != 0) { + Assert.assertEquals(poorManFloorMod(a, b), FastMath.floorMod(a, b)); + } + } + } + } + + @Test + public void testFloorDivModLong() { + RandomGenerator generator = new Well1024a(0xb87b9bc14c96ccd5l); + for (int i = 0; i < 10000; ++i) { + long a = generator.nextLong(); + long b = generator.nextLong(); + if (b == 0) { + try { + FastMath.floorDiv(a, b); + Assert.fail("an exception should have been thrown"); + } catch (MathArithmeticException mae) { + // expected + } + } else { + long d = FastMath.floorDiv(a, b); + long m = FastMath.floorMod(a, b); + Assert.assertEquals(poorManFloorDiv(a, b), d); + Assert.assertEquals(poorManFloorMod(a, b), m); + Assert.assertEquals(a, d * b + m); + if (b < 0) { + Assert.assertTrue(m <= 0); + Assert.assertTrue(-m < -b); + } else { + Assert.assertTrue(m >= 0); + Assert.assertTrue(m < b); + } + } + } + } + + private long poorManFloorDiv(long a, long b) { + + // find q0, r0 such that a = q0 b + r0 + BigInteger q0 = BigInteger.valueOf(a / b); + BigInteger r0 = BigInteger.valueOf(a % b); + BigInteger fd = BigInteger.valueOf(Integer.MIN_VALUE); + BigInteger bigB = BigInteger.valueOf(b); + + for (int k = -2; k < 2; ++k) { + // find another pair q, r such that a = q b + r + BigInteger bigK = BigInteger.valueOf(k); + BigInteger q = q0.subtract(bigK); + BigInteger r = r0.add(bigK.multiply(bigB)); + if (r.abs().compareTo(bigB.abs()) < 0 && + (r.longValue() == 0l || ((r.longValue() ^ b) & 0x8000000000000000l) == 0)) { + if (fd.compareTo(q) < 0) { + fd = q; + } + } + } + + return fd.longValue(); + + } + + private long poorManFloorMod(long a, long b) { + return a - b * poorManFloorDiv(a, b); + } + }