This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 11eaff770e13b1c311fa033062ce3a91a8dd3ccf
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Aug 23 19:25:06 2022 +0200

    Add overflow-safe `GridExtent.getMedian(int)` method.
    Leverage existing methods (minor update).
---
 .../org/apache/sis/coverage/grid/GridExtent.java   | 33 ++++++++++++++++++++++
 .../apache/sis/coverage/grid/GridExtentTest.java   |  7 +++--
 .../org/apache/sis/internal/util/Numerics.java     |  2 ++
 .../java/org/apache/sis/math/MathFunctions.java    |  2 +-
 .../main/java/org/apache/sis/math/Statistics.java  | 17 +++++------
 5 files changed, 50 insertions(+), 11 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index af34fe7c5a..1fdc1aed7f 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -747,6 +747,39 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
         return coordinates[index + dimension];
     }
 
+    /**
+     * Returns the average of low and high coordinates, rounded toward 
positive infinity.
+     * This method is equivalent to computing any of the following,
+     * except that this method does not overflow even if the sum would 
overflow:
+     *
+     * <ul>
+     *   <li>(<var>low</var> + <var>high</var>) / 2 rounded toward positive 
infinity, or</li>
+     *   <li>(<var>low</var> + <var>high</var> + 1) / 2 rounded toward 
negative infinity.</li>
+     * </ul>
+     *
+     * The two above formulas are equivalent, so the result does not depend
+     * on whether the high coordinate should be inclusive or exclusive.
+     *
+     * @param  index  the dimension for which to obtain the coordinate value.
+     * @return the median coordinate value at the given dimension.
+     * @throws IndexOutOfBoundsException if the given index is negative or is 
equal or greater
+     *         than the {@linkplain #getDimension() grid dimension}.
+     *
+     * @since 1.3
+     */
+    public long getMedian(final int index) {
+        final int dimension = getDimension();
+        ArgumentChecks.ensureValidIndex(dimension, index);
+        final long low  = coordinates[index];
+        final long high = coordinates[index + dimension];
+        /*
+         * Use `>> 1` instead of `/2` because the two operations differ in 
their rounding mode for negative values.
+         * The former rounds toward negative infinity (which is intended here) 
while the latter rounds toward zero.
+         * If at least one value is odd, add +1 to the result.
+         */
+        return (low >> 1) + (high >> 1) + ((low | high) & 1);
+    }
+
     /**
      * Returns the number of integer grid coordinates along the specified 
dimension.
      * This is equal to {@code getHigh(dimension) - getLow(dimension) + 1}.
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
 
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
index 843d330b46..65cec4bcf4 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -34,6 +34,7 @@ import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.jdk9.JDK9;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -64,8 +65,10 @@ public final strictfp class GridExtentTest extends TestCase {
      * Verifies the low and high values in the specified dimension of the 
given extent
      */
     static void assertExtentEquals(final GridExtent extent, final int 
dimension, final int low, final int high) {
-        assertEquals("low",  low,  extent.getLow (dimension));
-        assertEquals("high", high, extent.getHigh(dimension));
+        assertEquals("low",    low,  extent.getLow (dimension));
+        assertEquals("high",   high, extent.getHigh(dimension));
+        assertEquals("size",   high - low + 1, extent.getSize(dimension));
+        assertEquals("median", Numerics.ceilDiv(high + low, 2), 
extent.getMedian(dimension));
     }
 
     /**
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
index 47be423a21..fce0e2b2fc 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
@@ -215,6 +215,8 @@ public final class Numerics extends Static {
      * @return x/y rounded toward positive infinity.
      *
      * @see Math#floorDiv(int, int)
+     *
+     * @todo Replace by {@link Math#ceilDiv(int, int)} in JDK18.
      */
     public static int ceilDiv(final int x, final int y) {
         int r = x / y;
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java 
b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
index 0cdee86f93..ac0b663322 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
@@ -775,7 +775,7 @@ public final class MathFunctions extends Static {
              */
             return Double.NaN;
         }
-        exp -= (16383 - 1023);     // Change from 15 bias to 11 bias.
+        exp -= (16383 - Double.MAX_EXPONENT);       // Change from 15 bias to 
11 bias.
         // Check cases where mantissa excess what double can support.
         if (exp < 0)    return Double.NEGATIVE_INFINITY;
         if (exp > 2046) return Double.POSITIVE_INFINITY;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java 
b/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
index b89e6b50b1..0b1efbbc7a 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
@@ -24,6 +24,7 @@ import org.opengis.util.InternationalString;
 import org.apache.sis.util.SimpleInternationalString;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.util.DoubleDouble;
 
 import static java.lang.Math.*;
@@ -612,7 +613,7 @@ public class Statistics implements DoubleConsumer, 
LongConsumer, Cloneable, Seri
                      31 * (doubleToLongBits(maximum) +
                      31 * (doubleToLongBits(sum) +
                      31 * (doubleToLongBits(squareSum)))));
-        return (int) code ^ (int) (code >>> 32) ^ count;
+        return Long.hashCode(code) ^ count;
     }
 
     /**
@@ -624,13 +625,13 @@ public class Statistics implements DoubleConsumer, 
LongConsumer, Cloneable, Seri
     @Override
     public boolean equals(final Object object) {
         if (object != null && getClass() == object.getClass()) {
-            final Statistics cast = (Statistics) object;
-            return count == cast.count && countNaN == cast.countNaN
-                    && doubleToLongBits(minimum)   == 
doubleToLongBits(cast.minimum)
-                    && doubleToLongBits(maximum)   == 
doubleToLongBits(cast.maximum)
-                    && doubleToLongBits(sum)       == 
doubleToLongBits(cast.sum)
-                    && doubleToLongBits(squareSum) == 
doubleToLongBits(cast.squareSum)
-                    && Objects.equals(name, cast.name);
+            final Statistics other = (Statistics) object;
+            return count == other.count && countNaN == other.countNaN
+                    && Numerics.equals(minimum,   other.minimum)
+                    && Numerics.equals(maximum,   other.maximum)
+                    && Numerics.equals(sum,       other.sum)
+                    && Numerics.equals(squareSum, other.squareSum)
+                    && Objects .equals(name,      other.name);
         }
         return false;
     }

Reply via email to