Repository: commons-math
Updated Branches:
  refs/heads/master cee04d164 -> a67f0a33e


Added all Java 8 StrictMath methods to FastMath.

This change allows FastMath to remain compatible with newer Java
versions, despite it stiil requires only Java 5 to compile and run.

JIRA: MATH-1156


Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/a67f0a33
Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/a67f0a33
Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/a67f0a33

Branch: refs/heads/master
Commit: a67f0a33e68abf1c985db28b19ce70f427fd48bd
Parents: cee04d1
Author: Luc Maisonobe <l...@apache.org>
Authored: Tue Oct 7 13:54:55 2014 +0200
Committer: Luc Maisonobe <l...@apache.org>
Committed: Tue Oct 7 13:54:55 2014 +0200

----------------------------------------------------------------------
 pom.xml                                         |   6 +-
 src/changes/changes.xml                         |   4 +
 .../math3/exception/util/LocalizedFormats.java  |   1 +
 .../org/apache/commons/math3/util/FastMath.java | 334 +++++++++++++++
 .../util/LocalizedFormats_fr.properties         |   1 +
 .../exception/util/LocalizedFormatsTest.java    |   3 +-
 .../apache/commons/math3/util/FastMathTest.java | 429 ++++++++++++++++++-
 7 files changed, 769 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-math/blob/a67f0a33/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 3ca4958..3a62e60 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,9 +38,9 @@
   </issueManagement>
 
   <scm>
-    
<connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/math/trunk</connection>
-    
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/math/trunk</developerConnection>
-    <url>http://svn.apache.org/viewvc/commons/proper/math/trunk</url>
+    
<connection>scm:git:http://git-wip-us.apache.org/repos/asf/commons-math.git</connection>
+    
<developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/commons-math.git</developerConnection>
+    <url>https://git-wip-us.apache.org/repos/asf?p=commons-math.git</url>
   </scm>
 
   <distributionManagement>

http://git-wip-us.apache.org/repos/asf/commons-math/blob/a67f0a33/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0c6520b..7ea96c9 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -73,6 +73,10 @@ Users are encouraged to upgrade to this version as this 
release not
   2. A few methods in the FastMath class are in fact slower that their
   counterpart in either Math or StrictMath (cf. MATH-740 and MATH-901).
 ">
+      <action dev="luc" type="add" issue="MATH-1156" >
+        Added all Java 8 StrictMath methods to FastMath, so FastMath remains 
compatible
+        with newer Java versions.
+      </action>
       <action dev="tn" type="add" issue="MATH-1139" due-to="Alexey Volkov">
         Added Gumbel, Laplace, Logistic and Nakagami distributions.
       </action>

http://git-wip-us.apache.org/repos/asf/commons-math/blob/a67f0a33/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java 
b/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
index 617a2cc..b775440 100644
--- 
a/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
+++ 
b/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
@@ -293,6 +293,7 @@ public enum LocalizedFormats implements Localizable {
     OVERFLOW_IN_FRACTION("overflow in fraction {0}/{1}, cannot negate"),
     OVERFLOW_IN_ADDITION("overflow in addition: {0} + {1}"),
     OVERFLOW_IN_SUBTRACTION("overflow in subtraction: {0} - {1}"),
+    OVERFLOW_IN_MULTIPLICATION("overflow in multiplication: {0} * {1}"),
     PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD("cannot access {0} method 
in percentile implementation {1}"),
     PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD("percentile implementation 
{0} does not support {1}"),
     PERMUTATION_EXCEEDS_N("permutation size ({0}) exceeds permuation domain 
({1})"), /* keep */

http://git-wip-us.apache.org/repos/asf/commons-math/blob/a67f0a33/src/main/java/org/apache/commons/math3/util/FastMath.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/util/FastMath.java 
b/src/main/java/org/apache/commons/math3/util/FastMath.java
index d16406a..9ed4c04 100644
--- a/src/main/java/org/apache/commons/math3/util/FastMath.java
+++ b/src/main/java/org/apache/commons/math3/util/FastMath.java
@@ -18,6 +18,9 @@ package org.apache.commons.math3.util;
 
 import java.io.PrintStream;
 
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
 /**
  * Faster, more accurate, portable alternative to {@link Math} and
  * {@link StrictMath} for large scale computation.
@@ -804,6 +807,24 @@ public class FastMath {
         return nextAfter(a, Float.POSITIVE_INFINITY);
     }
 
+    /** Compute next number towards negative infinity.
+     * @param a number to which neighbor should be computed
+     * @return neighbor of a towards negative infinity
+     * @since 3.4
+     */
+    public static double nextDown(final double a) {
+        return nextAfter(a, Double.NEGATIVE_INFINITY);
+    }
+
+    /** Compute next number towards negative infinity.
+     * @param a number to which neighbor should be computed
+     * @return neighbor of a towards negative infinity
+     * @since 3.4
+     */
+    public static float nextDown(final float a) {
+        return nextAfter(a, Float.NEGATIVE_INFINITY);
+    }
+
     /** Returns a pseudo-random number between 0.0 and 1.0.
      * <p><b>Note:</b> 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.
+     * <p>
+     * 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).
+     * </p>
+     * @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.
+     * <p>
+     * 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).
+     * </p>
+     * @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.
+     * <p>
+     * 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).
+     * </p>
+     * @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.
+     * <p>
+     * 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).
+     * </p>
+     * @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.

http://git-wip-us.apache.org/repos/asf/commons-math/blob/a67f0a33/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties
----------------------------------------------------------------------
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 1a3c239..599dc7f 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})

http://git-wip-us.apache.org/repos/asf/commons-math/blob/a67f0a33/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java
----------------------------------------------------------------------
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 24d5bbc..34344d4 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

http://git-wip-us.apache.org/repos/asf/commons-math/blob/a67f0a33/src/test/java/org/apache/commons/math3/util/FastMathTest.java
----------------------------------------------------------------------
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 8408b78..f860754 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);
+    }
+
 }

Reply via email to