sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1763329 - in /sis/branches/JDK8: core/sis-utility/src/main/java/org/apache/sis/internal/system/ core/sis-utility/src/main/java/org/apache/sis/math/ core/sis-utility/src/test/java/org/apache/sis/math/ storage/sis-netcdf/src/main/java/org/ap...
Date Tue, 04 Oct 2016 20:48:31 GMT
Author: desruisseaux
Date: Tue Oct  4 20:48:31 2016
New Revision: 1763329

URL: http://svn.apache.org/viewvc?rev=1763329&view=rev
Log:
Add Vector.isInteger(), Vector.increment(double) and Vector.compress(double) methods.
Those methods are used directly or indirectly by NetCDF FeaturesInfo implementation.

Added:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/PackedVector.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java
    sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java?rev=1763329&r1=1763328&r2=1763329&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -52,6 +52,11 @@ public final class Loggers extends Stati
     public static final String SYSTEM = "org.apache.sis.system";
 
     /**
+     * The logger for operations related to mathematical operations.
+     */
+    public static final String MATH = "org.apache.sis.math";
+
+    /**
      * The logger for operations related to JDBC operations.
      */
     public static final String SQL = "org.apache.sis.sql";

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java?rev=1763329&r1=1763328&r2=1763329&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -76,8 +76,72 @@ abstract class ArrayVector<E extends Num
         } else {
             throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentClass_2, "array", array.getClass()));
         }
-        if (vec.isEmpty()) {
-            return new SequenceVector(vec.getElementType());
+        return vec;
+    }
+
+    /**
+     * Returns a vector with the same data than this vector but encoded in a more compact way,
+     * or {@code this} if this method can not do better than current {@code Vector} instance.
+     */
+    public final Vector compress(final double tolerance) {
+        final Vector vec = super.compress(tolerance);
+        if (vec == this) {
+            if (isInteger()) {
+                /*
+                 * For integer values, verify if we can pack the data into a smaller type.
+                 * We will use a vector backed by IntegerList in order to use only the amount of bits needed,
+                 * unless that amount is exactly the number of bits of a primitive type (8, 16, 32 or 64) in
+                 * which case using one of the specialized class in this ArrayVector is more performant.
+                 */
+                final NumberRange<?> range = range();
+                if (range != null && !range.isEmpty() && range.getMinDouble() >= Long.MIN_VALUE
+                                                      && range.getMaxDouble() <= Long.MAX_VALUE)
+                {
+                    final long min      = range.getMinValue().longValue();
+                    final long max      = range.getMaxValue().longValue();
+                    final long delta    = Math.subtractExact(max, min);
+                    final int  bitCount = Long.SIZE - Long.numberOfLeadingZeros(delta);
+                    if (bitCount != Numbers.primitiveBitCount(getElementType())) {
+                        switch (bitCount) {
+                            case Byte.SIZE: {
+                                final boolean isSigned = (min >= Byte.MIN_VALUE && max <= Byte.MAX_VALUE);
+                                if (isSigned || (min >= 0 && max <= 0xFF)) {
+                                    final byte[] array = new byte[size()];
+                                    for (int i=0; i < array.length; i++) {
+                                        array[i] = (byte) intValue(i);
+                                    }
+                                    return isSigned ? new Bytes(array) : new UnsignedBytes(array);
+                                }
+                                break;
+                            }
+                            case Short.SIZE: {
+                                final boolean isSigned = (min >= Short.MIN_VALUE && max <= Short.MAX_VALUE);
+                                if (isSigned || (min >= 0 && max <= 0xFFFF)) {
+                                    final short[] array = new short[size()];
+                                    for (int i=0; i < array.length; i++) {
+                                        array[i] = (short) intValue(i);
+                                    }
+                                    return isSigned ? new Shorts(array) : new UnsignedShorts(array);
+                                }
+                                break;
+                            }
+                            case Integer.SIZE: {
+                                final boolean isSigned = (min >= Integer.MIN_VALUE && max <= Integer.MAX_VALUE);
+                                if (isSigned || (min >= 0 && max <= 0xFFFFFFFF)) {
+                                    final int[] array = new int[size()];
+                                    for (int i=0; i < array.length; i++) {
+                                        array[i] = (int) longValue(i);
+                                    }
+                                    return isSigned ? new Integers(array) : new UnsignedIntegers(array);
+                                }
+                                break;
+                            }
+                            // The Long.SIZE case should never happen because of the 'bitCount' check before the switch.
+                        }
+                        return new PackedVector(this, min, Math.toIntExact(delta));
+                    }
+                }
+            }
         }
         return vec;
     }
@@ -186,7 +250,10 @@ abstract class ArrayVector<E extends Num
     }
 
     /**
-     * A vector backed by an array of type {@code float[]}.
+     * A vector backed by an array of type {@code float[]}. In this class, conversions to the {@code double} type
+     * use the standard Java cast operator  (i.e. the {@code double} value is padded with zero fraction digits in
+     * its base 2 representation). The {@code ArrayVector.Decimal} subclass overrides this behavior with a more
+     * costly cast that tries to preserve the representation in base 10.
      */
     private static class Floats extends ArrayVector<Float> {
         /** For cross-version compatibility. */
@@ -282,7 +349,8 @@ abstract class ArrayVector<E extends Num
     }
 
     /**
-     * A vector backed by an array of type {@code long[]}.
+     * A vector backed by an array of type {@code long[]}. This class handles signed values.
+     * The {@code ArrayVector.UnsignedLongs} subclass handle unsigned {@code long} values.
      */
     private static class Longs extends ArrayVector<Long> {
         /** For cross-version compatibility. */
@@ -330,10 +398,40 @@ abstract class ArrayVector<E extends Num
             }
             return NumberRange.create(min, true, max, true);
         }
+
+        /**
+         * Returns the increment between values if this increment is constant, or {@code null} otherwise.
+         * Addition or subtraction of unsigned integers are bitwise identical to the same operations on
+         * signed integers. Consequently we do not need to distinguish the two cases during the loop.
+         */
+        @Override public final Number increment(final double tolerance) {
+            if (!(tolerance >= 0 && tolerance < 1)) {                       // Use '!' for catching NaN.
+                return super.increment(tolerance);
+            }
+            int i = array.length;
+            if (i >= 2) {
+                long p;
+                final long inc;
+                try {
+                    inc = subtract(array[--i], p = array[--i]);
+                } catch (ArithmeticException e) {
+                    warning("increment", e);
+                    return null;
+                }
+                while (i != 0) {
+                    if (p - (p = array[--i]) != inc) {
+                        return null;
+                    }
+                }
+                return inc;
+            }
+            return null;
+        }
     }
 
     /**
-     * A vector backed by an array of type {@code int[]}.
+     * A vector backed by an array of type {@code int[]}. This class handles signed values.
+     * The {@code ArrayVector.UnsignedIntegers} subclass handle unsigned {@code long} values.
      */
     private static class Integers extends ArrayVector<Integer> {
         /** For cross-version compatibility. */
@@ -382,10 +480,43 @@ abstract class ArrayVector<E extends Num
             }
             return NumberRange.create(min, true, max, true);
         }
+
+        /**
+         * Returns the increment between values if this increment is constant, or {@code null} otherwise.
+         * Addition or subtraction of unsigned integers are bitwise identical to the same operations on
+         * signed integers. Consequently we do not need to distinguish the two cases during the loop.
+         */
+        @Override public final Number increment(final double tolerance) {
+            if (!(tolerance >= 0 && tolerance < 1)) {                       // Use '!' for catching NaN.
+                return super.increment(tolerance);
+            }
+            int i = array.length;
+            if (i >= 2) {
+                final long inc = longValue(--i) - longValue(--i);
+                final boolean isSigned = (inc >= Integer.MIN_VALUE && inc <= Integer.MAX_VALUE);
+                if (isSigned || isUnsigned()) {             // Check against overflow.
+                    final int asInt = (int) inc;
+                    int p = array[i];
+                    while (i != 0) {
+                        if (p - (p = array[--i]) != asInt) {
+                            return null;
+                        }
+                    }
+                    // Do not use the ?: operator below since it casts 'asInt' to Long, which is not wanted.
+                    if (isSigned) {
+                        return asInt;
+                    } else {
+                        return inc;
+                    }
+                }
+            }
+            return null;
+        }
     }
 
     /**
-     * A vector backed by an array of type {@code short[]}.
+     * A vector backed by an array of type {@code short[]}. This class handles signed values.
+     * The {@code ArrayVector.UnsignedShorts} subclass handle unsigned {@code long} values.
      */
     private static class Shorts extends ArrayVector<Short> {
         /** For cross-version compatibility. */
@@ -435,10 +566,15 @@ abstract class ArrayVector<E extends Num
             }
             return NumberRange.create(min, true, max, true);
         }
+
+        // Not worth to override 'increment(double)' because the array can not be long anyway
+        // (except if the increment is zero) and the implicit conversion of 'short' to 'int'
+        // performed by Java would make the implementation a little bit more tricky.
     }
 
     /**
-     * A vector backed by an array of type {@code byte[]}.
+     * A vector backed by an array of type {@code byte[]}. This class handles signed values.
+     * The {@code ArrayVector.UnsignedBytes} subclass handle unsigned {@code long} values.
      */
     private static class Bytes extends ArrayVector<Byte> {
         /** For cross-version compatibility. */
@@ -489,6 +625,10 @@ abstract class ArrayVector<E extends Num
             }
             return NumberRange.create(min, true, max, true);
         }
+
+        // Not worth to override 'increment(double)' because the array can not be long anyway
+        // (except if the increment is zero) and the implicit conversion of 'byte' to 'int'
+        // performed by Java would make the implementation a little bit more tricky.
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java?rev=1763329&r1=1763328&r2=1763329&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -19,6 +19,7 @@ package org.apache.sis.math;
 import java.io.Serializable;
 import org.apache.sis.util.Classes;
 import org.apache.sis.measure.NumberRange;
+import org.apache.sis.util.Numbers;
 
 
 /**
@@ -66,6 +67,14 @@ final class ConcatenatedVector extends V
     }
 
     /**
+     * Returns {@code true} if this vector contains only integer values.
+     */
+    @Override
+    public boolean isInteger() {
+        return first.isInteger() && second.isInteger();
+    }
+
+    /**
      * Returns {@code true} only if both vectors are unsigned.
      */
     @Override
@@ -236,6 +245,26 @@ final class ConcatenatedVector extends V
     }
 
     /**
+     * Returns the increment between all consecutive values if this increment is constant, or {@code null} otherwise.
+     */
+    @Override
+    public Number increment(final double tolerance) {
+        Number inc = first.increment(tolerance);
+        if (inc != null) {
+            Number check = second.increment(tolerance);
+            if (check != null) {
+                final Class<? extends Number> type = Numbers.widestClass(inc.getClass(), check.getClass());
+                inc   = Numbers.cast(inc,   type);
+                check = Numbers.cast(check, type);
+                if (inc.equals(check)) {
+                    return inc;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Computes the minimal and maximal values in this vector.
      * This is the union of the range of the two concatenated vectors.
      */

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/PackedVector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/PackedVector.java?rev=1763329&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/PackedVector.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/PackedVector.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.math;
+
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.collection.IntegerList;
+
+
+/**
+ * A vector of integer values backed by an {@link IntegerList}.
+ * This offers a compressed storage using only the minimal amount of bits per value.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+final class PackedVector extends ArrayVector<Long> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 2025113345434607526L;
+
+    /**
+     * The compressed list of integer values. This list can store values from 0 to {@code delta} inclusive.
+     */
+    private final IntegerList data;
+
+    /**
+     * The offset to add to the {@link #data} in order to get the values to return.
+     */
+    private final long offset;
+
+    /**
+     * Creates a new compressed vector initialized to a copy of the data provided by the given vector.
+     *
+     * @param  source  the vector to copy.
+     * @param  offset  the minimal value in the source vector.
+     * @param  delta   the maximal value in the source vector minus {@code offset}.
+     */
+    PackedVector(final Vector source, final long offset, final int delta) {
+        this.offset = offset;
+        final int length = source.size();
+        data = new IntegerList(length, delta, true);
+        for (int i=0; i<length; i++) {
+            data.setInt(i, Math.toIntExact(source.longValue(i) - offset));
+        }
+    }
+
+    /**
+     * Type of elements fixed to {@code Long} even if the actual storage used by this class is more compact.
+     * The reason for the {@code Long} type is that this class can return any value in the {@code Long} range,
+     * because of the {@link #offset}.
+     */
+    @Override
+    public Class<Long> getElementType() {
+        return Long.class;
+    }
+
+    /**
+     * Returns the number of elements in this vector.
+     */
+    @Override
+    public int size() {
+        return data.size();
+    }
+
+    /**
+     * Returns the value at the given index as a {@code double} primitive type.
+     */
+    @Override
+    public double doubleValue(final int index) {
+        return longValue(index);
+    }
+
+    /**
+     * Returns the value at the given index as a {@code float} primitive type.
+     */
+    @Override
+    public float floatValue(final int index) {
+        return longValue(index);
+    }
+
+    /**
+     * Returns the value at the given index as a {@code long} primitive type.
+     */
+    @Override
+    public long longValue(final int index) {
+        return data.getInt(index) + offset;
+    }
+
+    /**
+     * Returns the string representation of the value at the given index.
+     */
+    @Override
+    public String stringValue(final int index) {
+        return Long.toString(longValue(index));
+    }
+
+    /**
+     * Returns the value at the given index wrapped in a {@link Long} instance.
+     */
+    @Override
+    public Number get(final int index) {
+        return longValue(index);
+    }
+
+    /**
+     * Sets the value at the given index at returns the previous value.
+     */
+    @Override
+    public Number set(final int index, final Number value) {
+        verifyType(value.getClass(), Numbers.LONG);
+        final Number old = get(index);
+        data.setInt(index, Math.toIntExact(Math.subtractExact(value.longValue(), offset)));
+        return old;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/PackedVector.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/PackedVector.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java?rev=1763329&r1=1763328&r2=1763329&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -18,96 +18,46 @@ package org.apache.sis.math;
 
 import java.io.Serializable;
 import org.apache.sis.measure.NumberRange;
+import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 
-import static org.apache.sis.util.Numbers.*;
-import static org.apache.sis.util.ArgumentChecks.ensureValidIndex;
-
 
 /**
- * A vector which is a sequence of numbers.
+ * A vector which is a sequence of increasing or decreasing values.
+ * Values may be {@code long} or {@code double} types.
  *
  * @author  Martin Desruisseaux (MPO, Geomatys)
  * @since   0.8
  * @version 0.8
  * @module
  */
-final class SequenceVector extends Vector implements Serializable {
+abstract class SequenceVector extends Vector implements Serializable {
     /**
      * For cross-version compatibility.
      */
     private static final long serialVersionUID = 7980737287789566091L;
 
     /**
-     * The element type, or {@code null} if values are NaN.
-     */
-    private final Class<? extends Number> type;
-
-    /**
-     * The value at index 0.
-     */
-    private final double first;
-
-    /**
-     * The difference between the values at two adjacent indexes.
-     * May be positive, negative or zero.
-     */
-    private final double increment;
-
-    /**
      * The length of this vector.
      */
-    private final int length;
+    final int length;
 
     /**
-     * Creates an empty vector of the given type.
+     * Creates a sequence of numbers of the given length.
      */
-    SequenceVector(final Class<? extends Number> type) {
-        this.type = type;
-        first     = 0;
-        increment = 0;
-        length    = 0;
-    }
-
-    /**
-     * Creates a sequence of numbers in a given range of values using the given increment.
-     *
-     * @param  first      the first value, inclusive.
-     * @param  increment  the difference between the values at two adjacent indexes.
-     * @param  length     the length of the vector.
-     */
-    public SequenceVector(final double first, final double increment, final int length) {
+    SequenceVector(final int length) {
+        this.length = length;
         if (length < 0) {
             throw new IllegalArgumentException(Errors.format(
                     Errors.Keys.IllegalArgumentValue_2, "length", length));
         }
-        this.first     = first;
-        this.increment = increment;
-        this.length    = length;
-        if (Double.isNaN(first) || Double.isNaN(increment)) {
-            type = null;
-        } else {
-            Class<? extends Number> t = narrowestClass(first);
-            t = widestClass(t, narrowestClass(first + increment));
-            t = widestClass(t, narrowestClass(first + increment*(length-1)));
-            type = t;
-        }
-    }
-
-    /**
-     * Returns the type of elements.
-     */
-    @Override
-    public Class<? extends Number> getElementType() {
-        // Float is the smallest type capable to hold NaN.
-        return (type != null) ? type : Float.class;
     }
 
     /**
      * {@code SequenceVector} values are always interpreted as signed values.
      */
     @Override
-    public boolean isUnsigned() {
+    public final boolean isUnsigned() {
         return false;
     }
 
@@ -115,79 +65,215 @@ final class SequenceVector extends Vecto
      * Returns the vector size.
      */
     @Override
-    public int size() {
+    public final int size() {
         return length;
     }
 
     /**
-     * Returns {@code true} if this vector returns {@code NaN} values.
+     * Unsupported operation since this vector is not modifiable.
      */
     @Override
-    public boolean isNaN(final int index) {
-        return type == null;
+    public final Number set(final int index, final Number value) {
+        throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, "Vector"));
     }
 
     /**
-     * Computes the value at the given index.
+     * Returns {@code this} since Apache SIS can not create a more compact vector than this {@code SequenceVector}.
      */
     @Override
-    public double doubleValue(final int index) throws IndexOutOfBoundsException {
-        ensureValidIndex(length, index);
-        return first + increment*index;
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    public final Vector compress(final double tolerance) {
+        return this;
     }
 
-    /**
-     * Computes the value at the given index.
-     */
-    @Override
-    public float floatValue(final int index) throws IndexOutOfBoundsException {
-        return (float) doubleValue(index);
-    }
 
     /**
-     * Returns the string representation of the value at the given index.
-     */
-    @Override
-    public String stringValue(final int index) throws IndexOutOfBoundsException {
-        return String.valueOf(doubleValue(index));
-    }
+     * A vector which is a sequence of increasing or decreasing {@code double} values.
+     */
+    static final class Doubles extends SequenceVector {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = -5222432536264284005L;
+
+        /**
+         * The value at index 0.
+         */
+        private final double first;
+
+        /**
+         * The difference between the values at two adjacent indexes.
+         * May be positive, negative or zero.
+         */
+        private final double increment;
+
+        /**
+         * Creates a sequence of numbers in a given range of values using the given increment.
+         *
+         * @param  first      the first value, inclusive.
+         * @param  increment  the difference between the values at two adjacent indexes.
+         * @param  length     the length of the vector.
+         */
+        Doubles(final Number first, final Number increment, final int length) {
+            super(length);
+            this.first     = first.doubleValue();
+            this.increment = increment.doubleValue();
+        }
 
-    /**
-     * Computes the value at the given index.
-     */
-    @Override
-    public Number get(final int index) throws IndexOutOfBoundsException {
-        return doubleValue(index);
-    }
+        /** Creates a new sequence for a subrange of this vector. */
+        @Override Vector createSubSampling(final int offset, final int step, final int n) {
+            return new Doubles(doubleValue(offset), increment*step, n);
+        }
 
-    /**
-     * Unsupported operation since this vector is not modifiable.
-     */
-    @Override
-    public Number set(final int index, final Number value) {
-        throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, "Vector"));
-    }
+        /** Returns the type of elements. */
+        @Override public Class<Double> getElementType() {
+            return Double.class;
+        }
 
-    /**
-     * Computes the minimal and maximal values in this vector.
-     * This is easily computed from the first value and the increment.
-     */
-    @Override
-    public NumberRange<Double> range() {
-        double min = first;
-        double max = first + increment * (length - 1);
-        if (max < min) {
-            min = max;
-            max = first;
+        /** Returns {@code true} if this vector contains only integer values. */
+        @Override public boolean isInteger() {
+            return Math.floor(first) == first && Math.floor(increment) == increment;
+        }
+
+        /**
+         * Returns {@code true} if this vector returns {@code NaN} values.
+         */
+        @Override public boolean isNaN(final int index) {
+            return Double.isNaN(first) || Double.isNaN(increment);
+        }
+
+        /** Computes the value at the given index. */
+        @Override public double doubleValue(final int index) {
+            ArgumentChecks.ensureValidIndex(length, index);
+            return first + increment*index;
+        }
+
+        /** Computes the value at the given index. */
+        @Override public float floatValue(final int index) {
+            return (float) doubleValue(index);
+        }
+
+        /** Returns the string representation of the value at the given index. */
+        @Override public String stringValue(final int index) {
+            return String.valueOf(doubleValue(index));
+        }
+
+        /** Computes the value at the given index. */
+        @Override public Number get(final int index) {
+            return doubleValue(index);
+        }
+
+        /** Returns the increment between all consecutive values */
+        @Override public Number increment(final double tolerance) {
+            return increment;
+        }
+
+        /** Computes the minimal and maximal values in this vector. */
+        @Override public NumberRange<Double> range() {
+            double min = first;
+            double max = first + increment * (length - 1);
+            if (max < min) {
+                min = max;
+                max = first;
+            }
+            return NumberRange.create(min, true, max, true);
         }
-        return NumberRange.create(min, true, max, true);
     }
 
+
     /**
-     * Creates a new sequence.
+     * A vector which is a sequence of increasing or decreasing {@code long} values.
      */
-    @Override
-    Vector createSubSampling(final int first, final int step, final int length) {
-        return new SequenceVector(doubleValue(first), increment*step, length);
+    static final class Longs extends SequenceVector {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 8959308953555132379L;
+
+        /**
+         * The value at index 0.
+         */
+        private final long first;
+
+        /**
+         * The difference between the values at two adjacent indexes.
+         * May be positive, negative or zero.
+         */
+        private final long increment;
+
+        /**
+         * Creates a sequence of numbers in a given range of values using the given increment.
+         *
+         * @param  first      the first value, inclusive.
+         * @param  increment  the difference between the values at two adjacent indexes.
+         * @param  length     the length of the vector.
+         */
+        Longs(final Number first, final Number increment, final int length) {
+            super(length);
+            this.first     = first.longValue();
+            this.increment = increment.longValue();
+        }
+
+        /** Creates a new sequence for a subrange of this vector. */
+        @Override Vector createSubSampling(final int offset, final int step, final int n) {
+            return new Longs(longValue(offset), increment*step, n);
+        }
+
+        /** Returns the type of elements. */
+        @Override public Class<Long> getElementType() {
+            return Long.class;
+        }
+
+        /** Returns {@code true} since this vector contains only integer values. */
+        @Override public boolean isInteger() {
+            return true;
+        }
+
+        /** Returns {@code false} since this vector never return {@code NaN} values. */
+        @Override public boolean isNaN(final int index) {
+            return false;
+        }
+
+        /** Computes the value at the given index. */
+        @Override public double doubleValue(final int index) {
+            return longValue(index);
+        }
+
+        /** Computes the value at the given index. */
+        @Override public float floatValue(final int index) {
+            return longValue(index);
+        }
+
+        /** Computes the value at the given index. */
+        @Override public long longValue(final int index) {
+            ArgumentChecks.ensureValidIndex(length, index);
+            return first + increment*index;
+        }
+
+        /** Returns the string representation of the value at the given index. */
+        @Override public String stringValue(final int index) {
+            return String.valueOf(longValue(index));
+        }
+
+        /** Computes the value at the given index. */
+        @Override public Number get(final int index) {
+            return longValue(index);
+        }
+
+        /** Returns the increment between all consecutive values */
+        @Override public Number increment(final double tolerance) {
+            return increment;
+        }
+
+        /** Computes the minimal and maximal values in this vector. */
+        @Override public NumberRange<Long> range() {
+            long min = first;
+            long max = first + increment * (length - 1);
+            if (max < min) {
+                min = max;
+                max = first;
+            }
+            return NumberRange.create(min, true, max, true);
+        }
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java?rev=1763329&r1=1763328&r2=1763329&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -22,7 +22,12 @@ import java.util.AbstractList;
 import java.util.RandomAccess;
 import java.util.function.IntSupplier;
 import org.apache.sis.measure.NumberRange;
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.internal.util.Numerics;
 
 import static org.apache.sis.util.ArgumentChecks.ensureValidIndex;
 
@@ -152,17 +157,27 @@ public abstract class Vector extends Abs
      * Note that the value given by the {@code first} argument is equivalent to a "lowest" or "minimum" value
      * only if the given increment is positive.
      *
-     * <p>The {@linkplain #getElementType() element type} will be the smallest type that can be used
-     * for storing every values. For example it will be {@code Byte.class} for the range [100:1:120]
-     * but will be {@code Double.class} for the range [0:0.1:1].</p>
+     * <p>The {@linkplain #getElementType() element type} will be inferred from the type of the given
+     * {@code Number} instances. If will typically be {@code Integer.class} for the [100:1:120] range
+     * and {@code Double.class} for the [0:0.1:1] range.</p>
      *
      * @param  first      the first value, inclusive.
      * @param  increment  the difference between the values at two adjacent indexes.
      * @param  length     the length of the desired vector.
      * @return the given sequence as a vector.
      */
-    public static Vector createSequence(final double first, final double increment, final int length) {
-        return new SequenceVector(first, increment, length);
+    public static Vector createSequence(final Number first, final Number increment, final int length) {
+        Class<? extends Number> type;
+        type = Numbers.widestClass(first, increment);
+        type = Numbers.widestClass(type,
+               Numbers.narrowestClass(first.doubleValue() + increment.doubleValue() * (length-1)));
+        final int t = Numbers.getEnumConstant(type);
+        if (t >= Numbers.BYTE && t <= Numbers.LONG) {
+            // Use the long type if possible because not all long values can be represented as double.
+            return new SequenceVector.Longs(first, increment, length);
+        } else {
+            return new SequenceVector.Doubles(first, increment, length);
+        }
     }
 
     /**
@@ -188,6 +203,24 @@ public abstract class Vector extends Abs
     public abstract Class<? extends Number> getElementType();
 
     /**
+     * Returns {@code true} if this vector contains only integer values.
+     * This method may iterate over all values for performing this verification.
+     *
+     * @return {@code true} if this vector contains only integer values.
+     */
+    public boolean isInteger() {
+        if (!Numbers.isInteger(getElementType())) {
+            for (int i=size(); --i >= 0;) {
+                final double v = doubleValue(i);
+                if (v != Math.floor(v)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
      * Returns {@code true} if integer values shall be interpreted as unsigned values.
      * This method may return {@code true} for data stored in {@code byte[]}, {@code short[]}, {@code int[]}
      * or {@code long[]} arrays, but never for data stored in {@code float[]} and {@code double[]} arrays.
@@ -384,6 +417,126 @@ public abstract class Vector extends Abs
     public abstract Number set(int index, Number value);
 
     /**
+     * Returns {@code a-b} as a signed value, throwing an exception if the result overflows a {@code long}.
+     * The given values will be interpreted as unsigned values if this vector {@linkplain #isUnsigned() is unsigned}.
+     *
+     * @param  a  the first value, unsigned if {@link #isUnsigned()} return {@code true}.
+     * @param  b  the value to subtract from {@code a}, unsigned if {@link #isUnsigned()} return {@code true}.
+     * @return the difference, always signed.
+     * @throws ArithmeticException if the difference is too large.
+     *
+     * @see Math#subtractExact(long, long)
+     */
+    final long subtract(final long a, final long b) {
+        final long inc = a - b;
+        // The sign of the difference shall be the same than the sign of Long.compare(…).
+        if ((((isUnsigned() ? Long.compareUnsigned(a, b) : Long.compare(a, b)) ^ inc) & Long.MIN_VALUE) != 0) {
+            throw new ArithmeticException();
+        }
+        return inc;
+    }
+
+    /**
+     * Returns the increment between all consecutive values if this increment is constant, or {@code null} otherwise.
+     * If the returned value is non-null, then the following condition shall hold for all values of <var>i</var> in
+     * the [0 … {@link #size()} - 1] range:
+     *
+     * <blockquote><code>{@linkplain Math#abs(double) abs}({@linkplain #doubleValue(int) doubleValue}(<var>i</var>)
+     * - (doubleValue(0) + increment*<var>i</var>)) &lt;= tolerance</code></blockquote>
+     *
+     * The tolerance threshold can be zero if exact matches are desired.
+     * The return value (if non-null) is always a signed value,
+     * even if this vector {@linkplain #isUnsigned() is unsigned}.
+     *
+     * @param  tolerance  the tolerance threshold for verifying if the increment is constant.
+     * @return the increment as a signed value, or {@code null} if the increment is not constant.
+     */
+    @SuppressWarnings("fallthrough")
+    public Number increment(final double tolerance) {
+        ArgumentChecks.ensurePositive("tolerance", tolerance);
+        int i = size();
+        if (i >= 2) try {
+            final int type = Numbers.getEnumConstant(getElementType());
+            /*
+             * For integer types, verify if the increment is constant. We do not use the "first + inc*i"
+             * formula because some 'long' values can not be represented accurately as 'double' values.
+             * The result will be converted to the same type than the vector element type if possible,
+             * or the next wider type if the increment is an unsigned value too big for the element type.
+             */
+            if (type >= Numbers.BYTE && type <= Numbers.LONG && tolerance < 1) {
+                long p;
+                final long inc = subtract(longValue(--i), p = longValue(--i));
+                while (i != 0) {
+                    if (p - (p = longValue(--i)) != inc) {
+                        return null;
+                    }
+                }
+                switch (type) {
+                    case Numbers.BYTE:    if (inc >= Byte   .MIN_VALUE && inc <= Byte   .MAX_VALUE) return (byte)  inc;  // else fallthrough
+                    case Numbers.SHORT:   if (inc >= Short  .MIN_VALUE && inc <= Short  .MAX_VALUE) return (short) inc;  // else fallthrough
+                    case Numbers.INTEGER: if (inc >= Integer.MIN_VALUE && inc <= Integer.MAX_VALUE) return (int)   inc;  // else fallthrough
+                    default: return inc;
+                }
+            }
+            /*
+             * For floating point types, we must use the same formula than the one used by SequenceVector:
+             *
+             *     doubleValue(i)  =  first + increment*i
+             *
+             * The intend is that if tolerance = 0 and this method returns a non-null value, then replacing
+             * this vector by an instance of SequenceVector should produce exactely the same double values.
+             */
+            if (type >= Numbers.FLOAT && type <= Numbers.DOUBLE) {
+                final double first = doubleValue(0);
+                final double inc = (doubleValue(--i) - first) / i;
+                while (i >= 1) {
+                    if (!(Math.abs(first + inc*i - doubleValue(i--)) <= tolerance)) {       // Use '!' for catching NaN.
+                        return null;
+                    }
+                }
+                if (type == Numbers.FLOAT) {
+                    final float f = (float) inc;
+                    if (f == inc) return f;             // Use the java.lang.Float wrapper class if possible.
+                }
+                return inc;
+            }
+        } catch (ArithmeticException e) {
+            warning("increment", e);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the minimal and maximal values found in this vector.
+     *
+     * @return minimal and maximal values found in this vector.
+     */
+    public NumberRange<?> range() {
+        return range(null, size());
+    }
+
+    /**
+     * Computes the range of values at the indices provided by the given supplier.
+     * The default implementation iterates over all {@code double} values, but
+     * subclasses should override with a more efficient implementation if possible.
+     *
+     * @param  indices  supplier of indices of the values to examine for computing the range,
+     *                  or {@code null} for the 0, 1, 2, … <var>n</var>-1 sequence.
+     * @param  n        number of indices to get from the supplier.
+     * @return the range of all values at the given indices.
+     */
+    NumberRange<?> range(final IntSupplier indices, int n) {
+        double min = Double.POSITIVE_INFINITY;
+        double max = Double.NEGATIVE_INFINITY;
+        while (--n >= 0) {
+            final double value = doubleValue((indices != null) ? indices.getAsInt() : n);
+            if (value < min) min = value;
+            if (value > max) max = value;
+        }
+        return NumberRange.create(min, true, max, true);
+    }
+
+    /**
      * Returns a view which contain the values of this vector in the given index range.
      * The returned view will contain the values from index {@code lower} inclusive to
      * {@code upper} exclusive.
@@ -431,6 +584,7 @@ public abstract class Vector extends Abs
 
     /**
      * Implementation of {@link #subSampling(int,int,int)} to be overridden by subclasses.
+     * Argument validity must have been verified by the caller.
      */
     Vector createSubSampling(final int first, final int step, final int length) {
         return new SubSampling(first, step, length);
@@ -778,33 +932,52 @@ public abstract class Vector extends Abs
     }
 
     /**
-     * Returns the minimal and maximal values found in this vector.
+     * Returns a vector with the same data than this vector but encoded in a more compact way,
+     * or {@code this} if this method can not do better than current {@code Vector} instance.
+     * Examples:
      *
-     * @return minimal and maximal values found in this vector.
+     * <ul>
+     *   <li>Vector is backed by an {@code int[]} array while values could be stored as {@code short} values.</li>
+     *   <li>Vector contains increasing or decreasing values with a constant delta between consecutive values.</li>
+     * </ul>
+     *
+     * The returned vector may or may not be backed by the array given to the {@link #create(Object, boolean)} method.
+     * Since the returned array may be a copy of {@code this} array, caller should not retain reference to {@code this}
+     * or reference to the backing array after this method call (otherwise an unnecessary duplication of data may exist
+     * in memory).
+     *
+     * <div class="section">When to use</div>
+     * It is usually not worth to compress small arrays. Performance-critical arrays may not be compressed neither.
+     * This method is best suited for arrays that may potentially be large and for which the cost of reading that
+     * array is small compared to the calculation performed with the values.
+     *
+     * @param  tolerance  maximal difference allowed between original and compressed vectors (can be zero).
+     * @return a more compact vector with the same data than this vector, or {@cod this}.
      */
-    public NumberRange<?> range() {
-        return range(null, size());
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    public Vector compress(final double tolerance) {
+        final int length = size();
+        final Number inc = increment(tolerance);
+        if (inc != null) {
+            return createSequence(get(0), inc, length);
+        }
+        /*
+         * Verify if the vector contains only NaN values. This extra check is useful because 'increment()'
+         * returns null if the array contains NaN. Note that for array of integers, 'isNaN(int)' is very
+         * efficient and the loop will stop immediately after the first iteration.
+         */
+        for (int i=0; i<length; i++) {
+            if (!isNaN(i)) return this;
+        }
+        final Double NaN = Numerics.valueOf(Double.NaN);
+        return new SequenceVector.Doubles(NaN, NaN, length);
     }
 
     /**
-     * Computes the range of values at the indices provided by the given supplier.
-     * The default implementation iterates over all {@code double} values, but
-     * subclasses should override with a more efficient implementation if possible.
-     *
-     * @param  indices  supplier of indices of the values to examine for computing the range,
-     *                  or {@code null} for the 0, 1, 2, … <var>n</var>-1 sequence.
-     * @param  n        number of indices to get from the supplier.
-     * @return the range of all values at the given indices.
+     * Logs a warning about an exception that can be safely ignored.
      */
-    NumberRange<?> range(final IntSupplier indices, int n) {
-        double min = Double.POSITIVE_INFINITY;
-        double max = Double.NEGATIVE_INFINITY;
-        while (--n >= 0) {
-            final double value = doubleValue((indices != null) ? indices.getAsInt() : n);
-            if (value < min) min = value;
-            if (value > max) max = value;
-        }
-        return NumberRange.create(min, true, max, true);
+    final void warning(final String method, final RuntimeException e) {
+        Logging.recoverableException(Logging.getLogger(Loggers.MATH), Vector.class, method, e);
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java?rev=1763329&r1=1763328&r2=1763329&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -44,7 +44,7 @@ public final strictfp class VectorTest e
     @Test
     public void testSequenceOfBytes() {
         vector = Vector.createSequence(100, 2, 10);
-        assertEquals(Byte.class, vector.getElementType());
+        assertEquals(Long.class, vector.getElementType());
         assertEquals(10, vector.size());
         for (int i=0; i<vector.size(); i++) {
             assertEquals(100 + 2*i, vector.byteValue(i));
@@ -251,6 +251,38 @@ public final strictfp class VectorTest e
     }
 
     /**
+     * Tests {@link Vector#increment(double)}.
+     */
+    @Test
+    @DependsOnMethod({"testShortArray", "testFloatArray", "testDoubleArray"})
+    public void testIncrement() {
+        for (int type = 0; type <= 9; type++) {
+            final Vector vec;
+            switch (type) {
+                case  0: vec = Vector.create(new double[] {  5,      8,     11,         14,         17}, false); break;
+                case  1: vec = Vector.create(new float[]  { -5,     -2,      1,          4,          7}, false); break;
+                case  2: vec = Vector.create(new long[]   { -5,     -2,      1,          4,          7}, false); break;
+                case  3: vec = Vector.create(new long[]   {120,    123,    126,        129,        132}, true ); break;
+                case  4: vec = Vector.create(new int[]    { -5,     -2,      1,          4,          7}, false); break;
+                case  5: vec = Vector.create(new int[]    {120,    123,    126,        129,        132}, true ); break;
+                case  6: vec = Vector.create(new short[]  { -5,     -2,      1,          4,          7}, false); break;
+                case  7: vec = Vector.create(new short[]  {120,    123,    126,        129,        132}, true ); break;
+                case  8: vec = Vector.create(new byte[]   { -5,     -2,      1,          4,          7}, false); break;
+                case  9: vec = Vector.create(new byte[]   {120,    123,    126, (byte) 129, (byte) 132}, true ); break;
+                default: throw new AssertionError(type);
+            }
+            String message = vec.getElementType().getSimpleName();
+            if (vec.isUnsigned()) {
+                message = "Unsigned " + message;
+            }
+            final Number inc = vec.increment(0);
+            assertNotNull(message, inc);
+            assertEquals (message, 3, inc.doubleValue(), STRICT);
+            assertEquals (message, vec.getElementType(), inc.getClass());
+        }
+    }
+
+    /**
      * Tests {@link Vector#range()}. This test depends on most other tests defined in this {@code VectorTest} class
      * since it needs to test various combination of vectors and sub-vectors.
      */

Modified: sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java?rev=1763329&r1=1763328&r2=1763329&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java [UTF-8] Tue Oct  4 20:48:31 2016
@@ -24,7 +24,6 @@ import org.apache.sis.internal.netcdf.Da
 import org.apache.sis.internal.netcdf.DiscreteSampling;
 import org.apache.sis.internal.netcdf.Resources;
 import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.util.collection.IntegerList;
 import ucar.nc2.constants.CF;
 
 
@@ -41,7 +40,7 @@ final class FeaturesInfo extends Discret
     /**
      * The number of instances for each feature.
      */
-    private final IntegerList counts;
+    private final Vector counts;
 
     /**
      * The moving feature identifiers ("mfIdRef").
@@ -51,19 +50,11 @@ final class FeaturesInfo extends Discret
     /**
      * Creates a new discrete sampling parser for features identified by the given variable.
      *
-     * @param  cpf          the count of instances per feature.
+     * @param  counts       the count of instances per feature.
      * @param  identifiers  the feature identifiers.
      */
-    private FeaturesInfo(final Vector cpf, final VariableInfo identifiers) {
-        final int n = cpf.size();
-        int max = cpf.range().getMaxValue().intValue();
-        if (max <= 0) {
-            max = Integer.MAX_VALUE;
-        }
-        counts = new IntegerList(n, max);
-        for (int i=0; i<n; i++) {
-            counts.addInt(cpf.intValue(i));
-        }
+    private FeaturesInfo(final Vector counts, final VariableInfo identifiers) {
+        this.counts      = counts;
         this.identifiers = identifiers;
     }
 
@@ -159,7 +150,7 @@ search: for (final VariableInfo counts :
                     /*
                      * At this point, all information has been verified as valid.
                      */
-                    features.add(new FeaturesInfo(counts.read(), identifiers));
+                    features.add(new FeaturesInfo(counts.read().compress(0), identifiers));
                 }
             }
         }



Mime
View raw message