You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2016/10/10 15:24:06 UTC

svn commit: r1764119 [5/11] - in /sis/trunk: ./ core/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-feature/src/main/java/org/apache/sis/feature/builder/ core/sis-feature/src/main/java/org/apache/sis/internal/feature/ core/sis-metadat...

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java?rev=1764119&r1=1764118&r2=1764119&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] Mon Oct 10 15:24:03 2016
@@ -21,9 +21,11 @@ import org.apache.sis.util.Numbers;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.CheckedContainer;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.measure.NumberRange;
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk8.JDK8;
+import org.apache.sis.internal.jdk8.IntSupplier;
 
 
 /**
@@ -53,47 +55,131 @@ abstract class ArrayVector<E extends Num
      * @throws IllegalArgumentException if the type of the given object is not recognized by the method.
      */
     static Vector newInstance(final Object array, final boolean isUnsigned) throws IllegalArgumentException {
+        final Vector vec;
         if (array instanceof double[]) {
-            return new ArrayVector.Double((double[]) array);
+            vec = new Doubles((double[]) array);
+        } else if (array instanceof float[]) {
+            vec = new Floats((float[]) array);
+        } else if (array instanceof long[]) {
+            vec = isUnsigned ? new UnsignedLongs((long[]) array)
+                             : new         Longs((long[]) array);
+        } else if (array instanceof int[]) {
+            vec = isUnsigned ? new UnsignedIntegers((int[]) array)
+                             : new         Integers((int[]) array);
+        } else if (array instanceof short[]) {
+            vec = isUnsigned ? new UnsignedShorts((short[]) array)
+                             : new         Shorts((short[]) array);
+        } else if (array instanceof byte[]) {
+            vec = isUnsigned ? new UnsignedBytes((byte[]) array)
+                             : new         Bytes((byte[]) array);
+        } else if (array instanceof Number[]) {
+            vec = new Raw((Number[]) array);
+        } else if (array instanceof String[]) {
+            vec = new ASCII((String[]) array);
+        } else {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentClass_2, "array", array.getClass()));
         }
-        if (array instanceof float[]) {
-            return new ArrayVector.Float((float[]) array);
-        }
-        if (array instanceof long[]) {
-            if (isUnsigned) {
-                return new ArrayVector.UnsignedLong((long[]) array);
-            } else {
-                return new ArrayVector.Long((long[]) array);
-            }
-        }
-        if (array instanceof int[]) {
-            if (isUnsigned) {
-                return new ArrayVector.UnsignedInteger((int[]) array);
-            } else {
-                return new ArrayVector.Integer((int[]) array);
-            }
-        }
-        if (array instanceof short[]) {
-            if (isUnsigned) {
-                return new ArrayVector.UnsignedShort((short[]) array);
-            } else {
-                return new ArrayVector.Short((short[]) array);
-            }
-        }
-        if (array instanceof byte[]) {
-            if (isUnsigned) {
-                return new ArrayVector.UnsignedByte((byte[]) array);
-            } else {
-                return new ArrayVector.Byte((byte[]) array);
+        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.
+     */
+    @Override
+    public final Vector compress(final double tolerance) {
+        final Vector vec = super.compress(tolerance);
+        if (vec == this && !isEmpty()) {
+            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    = JDK8.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, JDK8.toIntExact(delta));
+                    }
+                }
+            } else if (!Float.class.equals(getElementType())) {
+                /*
+                 * For floating point types, verify if values are equivalent to 'float' values.
+                 * There is two different ways to pad extra fraction digits in 'double' values:
+                 * with zero fraction digits in base 2 representation (the standard Java cast),
+                 * or with zero fraction digits in base 10 representation.
+                 */
+                final int length = size();
+                int i = 0;
+                double v;
+                do if (i >= length) {
+                    return new Floats(toFloatArray());
+                } while (!(Math.abs((v = doubleValue(i++)) - (float) v) > tolerance));    // Use '!' for accepting NaN.
+                /*
+                 * Same try than above loop, but now using base 10 representation.
+                 * This is a more costly computation.
+                 */
+                i = 0;
+                do if (i >= length) {
+                    return new Decimal(toFloatArray());
+                } while (!(Math.abs((v = doubleValue(i++)) - DecimalFunctions.floatToDouble((float) v)) > tolerance));
             }
         }
-        if (array instanceof Number[]) {
-            return new ArrayVector.Raw((Number[]) array);
-        }
-        if (array instanceof String[]) {
-            return new ArrayVector.ASCII((String[]) array);
+        return vec;
+    }
+
+    /**
+     * Returns a copy of current data as a floating point array.
+     */
+    float[] toFloatArray() {
+        final float[] copy = new float[size()];
+        for (int i=0; i<copy.length; i++) {
+            copy[i] = (float) doubleValue(i);
         }
-        throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalParameterType_2, "array", array.getClass()));
+        return copy;
     }
 
     /**
@@ -127,7 +213,7 @@ abstract class ArrayVector<E extends Num
     /**
      * A vector backed by an array of type {@code double[]}.
      */
-    private static final class Double extends ArrayVector<java.lang.Double> {
+    private static final class Doubles extends ArrayVector<Double> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = -2900375382498345812L;
 
@@ -135,13 +221,13 @@ abstract class ArrayVector<E extends Num
         private final double[] array;
 
         /** Creates a new vector for the given array. */
-        Double(final double[] array) {
+        Doubles(final double[] array) {
             this.array = array;
         }
 
         /** Returns the type of elements in the backing array. */
-        @Override public Class<java.lang.Double> getElementType() {
-            return java.lang.Double.class;
+        @Override public Class<Double> getElementType() {
+            return Double.class;
         }
 
         /** Returns the length of the backing array. */
@@ -151,12 +237,12 @@ abstract class ArrayVector<E extends Num
 
         /** Returns {@code true} if the value at the given index is {@code NaN}. */
         @Override public boolean isNaN(final int index) {
-            return java.lang.Double.isNaN(array[index]);
+            return Double.isNaN(array[index]);
         }
 
         /** Returns the string representation at the given index. */
         @Override public String stringValue(final int index) {
-            return java.lang.Double.toString(array[index]);
+            return Double.toString(array[index]);
         }
 
         /** Returns the value at the given index. */
@@ -185,12 +271,32 @@ abstract class ArrayVector<E extends Num
             modCount++;
             return old;
         }
+
+        /** Returns a copy of current data as a floating point array. */
+        @Override float[] toFloatArray() {
+            return Numerics.copyAsFloats(array);
+        }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<Double> range(final IntSupplier indices, int n) {
+            double min = Double.POSITIVE_INFINITY;
+            double max = Double.NEGATIVE_INFINITY;
+            while (--n >= 0) {
+                final double value = array[(indices != null) ? indices.getAsInt() : n];
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            return NumberRange.create(min, true, max, true);
+        }
     }
 
     /**
-     * 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 Float extends ArrayVector<java.lang.Float> {
+    private static class Floats extends ArrayVector<Float> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 5395284704294981455L;
 
@@ -198,13 +304,13 @@ abstract class ArrayVector<E extends Num
         private final float[] array;
 
         /** Creates a new vector for the given array. */
-        Float(final float[] array) {
+        Floats(final float[] array) {
             this.array = array;
         }
 
         /** Returns the type of elements in the backing array. */
-        @Override public final Class<java.lang.Float> getElementType() {
-            return java.lang.Float.class;
+        @Override public final Class<Float> getElementType() {
+            return Float.class;
         }
 
         /** Returns the length of the backing array. */
@@ -214,18 +320,18 @@ abstract class ArrayVector<E extends Num
 
         /** Returns {@code true} if the value at the given index is {@code NaN}. */
         @Override public final boolean isNaN(final int index) {
-            return java.lang.Float.isNaN(array[index]);
+            return Float.isNaN(array[index]);
         }
 
         /** Returns the string representation at the given index. */
         @Override public final String stringValue(final int index) {
-            return java.lang.Float.toString(array[index]);
+            return Float.toString(array[index]);
         }
 
         /** Returns the value at the given index. */
         @Override public       double doubleValue(int index) {return array[index];}
         @Override public final float   floatValue(int index) {return array[index];}
-        @Override public final Number         get(int index) {return array[index];}
+        @Override public       Number         get(int index) {return array[index];}
 
         /** Sets the value at the given index. */
         @Override public final Number set(final int index, final Number value) {
@@ -234,6 +340,27 @@ abstract class ArrayVector<E extends Num
             modCount++;
             return old;
         }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override final NumberRange<?> range(final IntSupplier indices, int n) {
+            float min = Float.POSITIVE_INFINITY;
+            float max = Float.NEGATIVE_INFINITY;
+            while (--n >= 0) {
+                final float value = array[(indices != null) ? indices.getAsInt() : n];
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            return createRange(min, max);
+        }
+
+        /**
+         * Creates a range from the given minimum and maximum values, inclusive.
+         * The default implementation creates a range of {@code float}, but this method is
+         * overridden by {@code ArrayVector.Decimal} which create a range of {@code double}.
+         */
+        NumberRange<?> createRange(final float min, final float max) {
+            return NumberRange.create(min, true, max, true);
+        }
     }
 
     /**
@@ -241,7 +368,7 @@ abstract class ArrayVector<E extends Num
      * the errors when represented in base 10. This implementation should be used only when there is good reasons to
      * believe that the {@code float} data where encoded in base 10 in the first place (for example in an ASCII file).
      */
-    static final class Decimal extends Float {
+    static final class Decimal extends Floats {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 6085386820455858377L;
 
@@ -254,12 +381,24 @@ abstract class ArrayVector<E extends Num
         @Override public double doubleValue(final int index) {
             return DecimalFunctions.floatToDouble(super.floatValue(index));
         }
+
+        /** Returns the value at the given index. */
+        @Override public Number get(final int index) {
+            return doubleValue(index);
+        }
+
+        /** Creates a range from the given minimum and maximum values. */
+        @Override NumberRange<?> createRange(final float min, final float max) {
+            return NumberRange.create(DecimalFunctions.floatToDouble(min), true,
+                                      DecimalFunctions.floatToDouble(max), true);
+        }
     }
 
     /**
-     * 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 Long extends ArrayVector<java.lang.Long> {
+    private static class Longs extends ArrayVector<Long> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 338413429037224587L;
 
@@ -267,18 +406,18 @@ abstract class ArrayVector<E extends Num
         private final long[] array;
 
         /** Creates a new vector for the given array. */
-        Long(final long[] array) {
+        Longs(final long[] array) {
             this.array = array;
         }
 
         /** Returns the type of elements in the backing array. */
-        @Override public final Class<java.lang.Long> getElementType() {
-            return java.lang.Long.class;
+        @Override public final Class<Long> getElementType() {
+            return Long.class;
         }
 
         /** Returns the string representation at the given index. */
         @Override public String stringValue(final int index) {
-            return java.lang.Long.toString(array[index]);
+            return Long.toString(array[index]);
         }
 
         @Override public final int     size()          {return array.length;}
@@ -293,12 +432,54 @@ abstract class ArrayVector<E extends Num
             modCount++;
             return old;
         }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<?> range(final IntSupplier indices, int n) {
+            long min = Long.MAX_VALUE;
+            long max = Long.MIN_VALUE;
+            while (--n >= 0) {
+                final long value = array[(indices != null) ? indices.getAsInt() : n];
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            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 Integer extends ArrayVector<java.lang.Integer> {
+    private static class Integers extends ArrayVector<Integer> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = -1292641147544275801L;
 
@@ -306,18 +487,18 @@ abstract class ArrayVector<E extends Num
         private final int[] array;
 
         /** Creates a new vector for the given array. */
-        Integer(final int[] array) {
+        Integers(final int[] array) {
             this.array = array;
         }
 
         /** Returns the type of elements in the backing array. */
-        @Override public final Class<java.lang.Integer> getElementType() {
-            return java.lang.Integer.class;
+        @Override public final Class<Integer> getElementType() {
+            return Integer.class;
         }
 
         /** Returns the string representation at the given index. */
         @Override public String stringValue(final int index) {
-            return java.lang.Integer.toString(array[index]);
+            return Integer.toString(array[index]);
         }
 
         @Override public final int     size()          {return array.length;}
@@ -333,12 +514,57 @@ abstract class ArrayVector<E extends Num
             modCount++;
             return old;
         }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<?> range(final IntSupplier indices, int n) {
+            int min = Integer.MAX_VALUE;
+            int max = Integer.MIN_VALUE;
+            while (--n >= 0) {
+                final int value = array[(indices != null) ? indices.getAsInt() : n];
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            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 Short extends ArrayVector<java.lang.Short> {
+    private static class Shorts extends ArrayVector<Short> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = -126825963332296000L;
 
@@ -346,18 +572,18 @@ abstract class ArrayVector<E extends Num
         private final short[] array;
 
         /** Creates a new vector for the given array. */
-        Short(final short[] array) {
+        Shorts(final short[] array) {
             this.array = array;
         }
 
         /** Returns the type of elements in the backing array. */
-        @Override public final Class<java.lang.Short> getElementType() {
-            return java.lang.Short.class;
+        @Override public final Class<Short> getElementType() {
+            return Short.class;
         }
 
         /** Returns the string representation at the given index. */
         @Override public String stringValue(final int index) {
-            return java.lang.Short.toString(array[index]);
+            return Short.toString(array[index]);
         }
 
         @Override public final int     size()          {return array.length;}
@@ -374,12 +600,29 @@ abstract class ArrayVector<E extends Num
             modCount++;
             return old;
         }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<?> range(final IntSupplier indices, int n) {
+            short min = Short.MAX_VALUE;
+            short max = Short.MIN_VALUE;
+            while (--n >= 0) {
+                final short value = array[(indices != null) ? indices.getAsInt() : n];
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            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 Byte extends ArrayVector<java.lang.Byte> {
+    private static class Bytes extends ArrayVector<Byte> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 7933568876180528548L;
 
@@ -387,18 +630,18 @@ abstract class ArrayVector<E extends Num
         private final byte[] array;
 
         /** Creates a new vector for the given array. */
-        Byte(final byte[] array) {
+        Bytes(final byte[] array) {
             this.array = array;
         }
 
         /** Returns the type of elements in the backing array. */
-        @Override public final Class<java.lang.Byte> getElementType() {
-            return java.lang.Byte.class;
+        @Override public final Class<Byte> getElementType() {
+            return Byte.class;
         }
 
         /** Returns the string representation at the given index. */
         @Override public String stringValue(final int index) {
-            return java.lang.Byte.toString(array[index]);
+            return Byte.toString(array[index]);
         }
 
         @Override public final int     size()          {return array.length;}
@@ -416,17 +659,33 @@ abstract class ArrayVector<E extends Num
             modCount++;
             return old;
         }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<?> range(final IntSupplier indices, int n) {
+            byte min = Byte.MAX_VALUE;
+            byte max = Byte.MIN_VALUE;
+            while (--n >= 0) {
+                final byte value = array[(indices != null) ? indices.getAsInt() : n];
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            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.
     }
 
     /**
      * A vector backed by an array of type {@code long[]} to be interpreted as unsigned values.
      */
-    private static final class UnsignedLong extends Long {
+    private static final class UnsignedLongs extends Longs {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 712968674526282882L;
 
         /** Creates a new vector for the given array. */
-        UnsignedLong(final long[] array) {
+        UnsignedLongs(final long[] array) {
             super(array);
         }
 
@@ -454,17 +713,29 @@ abstract class ArrayVector<E extends Num
         @Override public String stringValue(final int index) {
             return JDK8.toUnsignedString(super.longValue(index));
         }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<Double> 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);
+        }
     }
 
     /**
      * A vector backed by an array of type {@code int[]} to be interpreted as unsigned values.
      */
-    private static final class UnsignedInteger extends Integer {
+    private static final class UnsignedIntegers extends Integers {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 8420585724189054050L;
 
         /** Creates a new vector for the given array. */
-        UnsignedInteger(final int[] array) {
+        UnsignedIntegers(final int[] array) {
             super(array);
         }
 
@@ -472,7 +743,7 @@ abstract class ArrayVector<E extends Num
         @Override public boolean isUnsigned()          {return true;}
         @Override public double doubleValue(int index) {return longValue(index);}
         @Override public float   floatValue(int index) {return longValue(index);}
-        @Override public long     longValue(int index) {return JDK8.toUnsignedLong(super.intValue(index));}
+        @Override public long     longValue(int index) {return super.intValue(index) & 0xFFFFFFFFL;}
         @Override public int       intValue(int index) {
             final int value = super.intValue(index);
             if (value >= 0) return value;
@@ -483,17 +754,29 @@ abstract class ArrayVector<E extends Num
         @Override public String stringValue(final int index) {
             return JDK8.toUnsignedString(super.intValue(index));
         }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<?> range(final IntSupplier indices, int n) {
+            long min = Long.MAX_VALUE;
+            long max = Long.MIN_VALUE;
+            while (--n >= 0) {
+                final long value = longValue((indices != null) ? indices.getAsInt() : n);
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            return NumberRange.create(min, true, max, true);
+        }
     }
 
     /**
      * A vector backed by an array of type {@code short[]} to be interpreted as unsigned values.
      */
-    private static final class UnsignedShort extends Short {
+    private static final class UnsignedShorts extends Shorts {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 8219060080494444776L;
 
         /** Creates a new vector for the given array. */
-        UnsignedShort(final short[] array) {
+        UnsignedShorts(final short[] array) {
             super(array);
         }
 
@@ -501,8 +784,8 @@ abstract class ArrayVector<E extends Num
         @Override public boolean isUnsigned()          {return true;}
         @Override public double doubleValue(int index) {return intValue(index);}
         @Override public float   floatValue(int index) {return intValue(index);}
-        @Override public long     longValue(int index) {return JDK8.toUnsignedLong(super.shortValue(index));}
-        @Override public int       intValue(int index) {return JDK8.toUnsignedInt (super.shortValue(index));}
+        @Override public long     longValue(int index) {return super.shortValue(index) & 0xFFFFL;}
+        @Override public int       intValue(int index) {return super.shortValue(index) & 0xFFFF;}
         @Override public short   shortValue(int index) {
             final short value = super.shortValue(index);
             if (value >= 0) return value;
@@ -511,19 +794,31 @@ abstract class ArrayVector<E extends Num
 
         /** Returns the string representation at the given index. */
         @Override public String stringValue(final int index) {
-            return java.lang.Integer.toString(intValue(index));
+            return Integer.toString(intValue(index));
+        }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<?> range(final IntSupplier indices, int n) {
+            int min = Integer.MAX_VALUE;
+            int max = Integer.MIN_VALUE;
+            while (--n >= 0) {
+                final int value = intValue((indices != null) ? indices.getAsInt() : n);
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            return NumberRange.create(min, true, max, true);
         }
     }
 
     /**
      * A vector backed by an array of type {@code byte[]} to be interpreted as unsigned values.
      */
-    private static final class UnsignedByte extends Byte {
+    private static final class UnsignedBytes extends Bytes {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = -2150064612523948331L;
 
         /** Creates a new vector for the given array. */
-        UnsignedByte(final byte[] array) {
+        UnsignedBytes(final byte[] array) {
             super(array);
         }
 
@@ -542,7 +837,19 @@ abstract class ArrayVector<E extends Num
 
         /** Returns the string representation at the given index. */
         @Override public String stringValue(final int index) {
-            return java.lang.Integer.toString(intValue(index));
+            return Integer.toString(intValue(index));
+        }
+
+        /** Finds the minimum and maximum values in the array or in a subset of the array. */
+        @Override NumberRange<?> range(final IntSupplier indices, int n) {
+            short min = Short.MAX_VALUE;
+            short max = Short.MIN_VALUE;
+            while (--n >= 0) {
+                final short value = shortValue((indices != null) ? indices.getAsInt() : n);
+                if (value < min) min = value;
+                if (value > max) max = value;
+            }
+            return NumberRange.create(min, true, max, true);
         }
     }
 
@@ -550,7 +857,7 @@ abstract class ArrayVector<E extends Num
      * A vector backed by an array of type {@code String[]}.
      * This is not recommended, but happen for example in GDAL extensions for GeoTIFF files.
      */
-    private static final class ASCII extends ArrayVector<java.lang.Double> {
+    private static final class ASCII extends ArrayVector<Double> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 2801615620517491573L;
 
@@ -563,8 +870,8 @@ abstract class ArrayVector<E extends Num
         }
 
         /** Returns the type of elements in the backing array. */
-        @Override public final Class<java.lang.Double> getElementType() {
-            return java.lang.Double.class;
+        @Override public final Class<Double> getElementType() {
+            return Double.class;
         }
 
         /** Returns {@code true} if the element at the given index is null or NaN. */
@@ -584,15 +891,15 @@ abstract class ArrayVector<E extends Num
 
         @Override public int           size()          {return array.length;}
         @Override public String stringValue(int index) {return array[index];}
-        @Override public double doubleValue(int index) {return java.lang.Double .parseDouble(array[index]);}
-        @Override public float   floatValue(int index) {return java.lang.Float  .parseFloat (array[index]);}
-        @Override public long     longValue(int index) {return java.lang.Long   .parseLong  (array[index]);}
-        @Override public int       intValue(int index) {return java.lang.Integer.parseInt   (array[index]);}
-        @Override public short   shortValue(int index) {return java.lang.Short  .parseShort (array[index]);}
-        @Override public byte     byteValue(int index) {return java.lang.Byte   .parseByte  (array[index]);}
+        @Override public double doubleValue(int index) {return Double .parseDouble(array[index]);}
+        @Override public float   floatValue(int index) {return Float  .parseFloat (array[index]);}
+        @Override public long     longValue(int index) {return Long   .parseLong  (array[index]);}
+        @Override public int       intValue(int index) {return Integer.parseInt   (array[index]);}
+        @Override public short   shortValue(int index) {return Short  .parseShort (array[index]);}
+        @Override public byte     byteValue(int index) {return Byte   .parseByte  (array[index]);}
         @Override public Number         get(int index) {
             final String value = array[index];
-            return (value != null) ? java.lang.Double.parseDouble(value) : null;
+            return (value != null) ? Double.parseDouble(value) : null;
         }
 
         /** Stores the given value in this vector and returns the previous value. */
@@ -629,8 +936,8 @@ abstract class ArrayVector<E extends Num
         @Override public boolean isNaN(final int index) {
             Number value = array[index];
             if (value == null) return true;
-            if (value instanceof java.lang.Float)  return ((java.lang.Float)  value).isNaN();
-            if (value instanceof java.lang.Double) return ((java.lang.Double) value).isNaN();
+            if (value instanceof Float)  return ((Float)  value).isNaN();
+            if (value instanceof Double) return ((Double) value).isNaN();
             return false;
         }
 

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java?rev=1764119&r1=1764118&r2=1764119&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ConcatenatedVector.java [UTF-8] Mon Oct 10 15:24:03 2016
@@ -18,6 +18,8 @@ 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;
 
 
 /**
@@ -65,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
@@ -235,6 +245,35 @@ 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.
+     */
+    @Override
+    public NumberRange<?> range() {
+        return first.range().unionAny(second.range());
+    }
+
+    /**
      * Delegates to the backing vectors if possible.
      */
     @Override

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java?rev=1764119&r1=1764118&r2=1764119&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java [UTF-8] Mon Oct 10 15:24:03 2016
@@ -17,85 +17,47 @@
 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.
-     */
-    private final double increment;
-
-    /**
      * The length of this vector.
      */
-    private final int length;
+    final int length;
 
     /**
-     * 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.
+     * Creates a sequence of numbers of the given length.
      */
-    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;
     }
 
@@ -103,64 +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;
+        }
+
+        /** 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);
+        }
     }
 
+
     /**
-     * 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/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java?rev=1764119&r1=1764118&r2=1764119&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] Mon Oct 10 15:24:03 2016
@@ -20,7 +20,15 @@ import java.io.Serializable;
 import java.util.Arrays;
 import java.util.AbstractList;
 import java.util.RandomAccess;
+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 org.apache.sis.internal.jdk8.IntSupplier;
+import org.apache.sis.internal.jdk8.JDK8;
 
 import static org.apache.sis.util.ArgumentChecks.ensureValidIndex;
 
@@ -80,6 +88,8 @@ import static org.apache.sis.util.Argume
  * @since   0.8
  * @version 0.8
  * @module
+ *
+ * @see org.apache.sis.util.collection.IntegerList
  */
 public abstract class Vector extends AbstractList<Number> implements RandomAccess {
     /**
@@ -119,7 +129,7 @@ public abstract class Vector extends Abs
         if (array instanceof Vector) {
             return (Vector) array;
         }
-        throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalParameterType_2, "array", array.getClass()));
+        throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentClass_2, "array", array.getClass()));
     }
 
     /**
@@ -150,17 +160,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);
+        }
     }
 
     /**
@@ -186,6 +206,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.
@@ -355,8 +393,13 @@ public abstract class Vector extends Abs
 
     /**
      * Returns the number at the given index, or {@code null} if none.
-     * If non-null, the object returned by this method will be an instance
-     * of the class returned by {@link #getElementType()} or a sub-class.
+     * The object returned by this method is usually an instance of the class returned by {@link #getElementType()},
+     * but may also be an instance of a wider type if this is necessary for representing the values.
+     *
+     * <div class="note"><b>Example:</b>
+     * if {@link #getElementType()} returns {@code Byte.class} but {@link #isUnsigned()} returns {@code true},
+     * then this method may return instances of {@link Short} since that type is the smallest Java primitive
+     * type capable to hold byte values in the [128 \u2026 255] range.</div>
      *
      * @param  index  the index in the [0 \u2026 {@linkplain #size() size}-1] range.
      * @return the value at the given index (may be {@code null}).
@@ -382,6 +425,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(\u2026).
+        if ((((isUnsigned() ? JDK8.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 \u2026 {@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, \u2026 <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.
@@ -429,26 +592,27 @@ 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);
     }
 
     /**
-     * A view over an other vector in a range of index.
+     * A view over another vector in a range of index.
      */
     private final class SubSampling extends Vector implements Serializable {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 7641036842053528486L;
 
         /** Index of the first element in the enclosing vector. */
-        private final int first;
+        final int first;
 
         /** The index increment. May be negative but not zero. */
-        private final int step;
+        final int step;
 
         /** The length of this vector. */
-        private final int length;
+        final int length;
 
         /** Creates a new view over the given range. */
         protected SubSampling(final int first, final int step, final int length) {
@@ -464,7 +628,7 @@ public abstract class Vector extends Abs
         }
 
         /** Returns the index where to look for the value in the enclosing vector. */
-        private int toBacking(final int index) {
+        final int toBacking(final int index) {
             ensureValidIndex(length, index);
             return index*step + first;
         }
@@ -500,6 +664,7 @@ public abstract class Vector extends Abs
         @Override public String  stringValue(int index)     {return Vector.this.stringValue(toBacking(index));}
         @Override public Number  get        (int index)     {return Vector.this.get        (toBacking(index));}
 
+        /** Delegates to the enclosing vector. */
         @Override public Number set(final int index, final Number v) {
             final Number old = Vector.this.set(toBacking(index), v);
             modCount++;
@@ -523,6 +688,29 @@ public abstract class Vector extends Abs
             }
             return super.createConcatenate(toAppend);
         }
+
+        /** Delegates to the enclosing vector */
+        @Override NumberRange<?> range(final IntSupplier indices, final int n) {
+            if (indices != null) {
+                return Vector.this.range(new IntSupplier() {
+                    @Override public int getAsInt() {
+                        return toBacking(indices.getAsInt());
+                    }
+                }, n);
+            }
+            IntSupplier supplier = null;
+            if (first != 0 || step != 1) {
+                supplier = new IntSupplier() {
+                    private int index = first;
+                    @Override public int getAsInt() {
+                        final int i = index;
+                        index += step;
+                        return i;
+                    }
+                };
+            }
+            return Vector.this.range(supplier, n);
+        }
     }
 
     /**
@@ -649,6 +837,7 @@ public abstract class Vector extends Abs
         @Override public String   stringValue(int i)    {return Vector.this.stringValue(indices[i]);}
         @Override public Number   get        (int i)    {return Vector.this.get        (indices[i]);}
 
+        /** Delegates to the enclosing vector. */
         @Override public Number set(final int i, final Number v) {
             final Number old = Vector.this.set(indices[i], v);
             modCount++;
@@ -678,6 +867,24 @@ public abstract class Vector extends Abs
             }
             return super.createConcatenate(toAppend);
         }
+
+        /** Delegates to the enclosing vector. */
+        @Override NumberRange<?> range(final IntSupplier supplier, final int n) {
+            if (supplier != null) {
+                return Vector.this.range(new IntSupplier() {
+                    @Override public int getAsInt() {
+                        return indices[supplier.getAsInt()];
+                    }
+                }, n);
+            } else {
+                return Vector.this.range(new IntSupplier() {
+                    private int index;
+                    @Override public int getAsInt() {
+                        return indices[index++];
+                    }
+                }, n);
+            }
+        }
     }
 
     /**
@@ -741,6 +948,55 @@ public abstract class Vector extends Abs
     }
 
     /**
+     * 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:
+     *
+     * <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 {@code this}.
+     */
+    @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);
+    }
+
+    /**
+     * Logs a warning about an exception that can be safely ignored.
+     */
+    final void warning(final String method, final RuntimeException e) {
+        Logging.recoverableException(Logging.getLogger(Loggers.MATH), Vector.class, method, e);
+    }
+
+    /**
      * Returns a string representation of this vector.
      *
      * @return a string representation of this vector.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java?rev=1764119&r1=1764118&r2=1764119&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java [UTF-8] Mon Oct 10 15:24:03 2016
@@ -74,12 +74,12 @@ public class MeasurementRange<E extends
      * Constructs a range of {@code float} values.
      * This method may return a shared instance, at implementation choice.
      *
-     * @param  minValue       The minimal value, or {@link Float#NEGATIVE_INFINITY} if none.
+     * @param  minValue       the minimal value, or {@link Float#NEGATIVE_INFINITY} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
-     * @param  maxValue       The maximal value, or {@link Float#POSITIVE_INFINITY} if none.
+     * @param  maxValue       the maximal value, or {@link Float#POSITIVE_INFINITY} if none.
      * @param  isMaxIncluded  {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
-     * @param  unit           The unit of measurement, or {@code null} if unknown.
-     * @return The new range of numeric values for the given endpoints and unit of measurement.
+     * @param  unit           the unit of measurement, or {@code null} if unknown.
+     * @return the new range of numeric values for the given endpoints and unit of measurement.
      */
     public static MeasurementRange<Float> create(float minValue, boolean isMinIncluded,
                                                  float maxValue, boolean isMaxIncluded, Unit<?> unit)
@@ -93,12 +93,12 @@ public class MeasurementRange<E extends
      * Constructs a range of {@code double} values.
      * This method may return a shared instance, at implementation choice.
      *
-     * @param  minValue       The minimal value, or {@link Double#NEGATIVE_INFINITY} if none.
+     * @param  minValue       the minimal value, or {@link Double#NEGATIVE_INFINITY} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
-     * @param  maxValue       The maximal value, or {@link Double#POSITIVE_INFINITY} if none.
+     * @param  maxValue       the maximal value, or {@link Double#POSITIVE_INFINITY} if none.
      * @param  isMaxIncluded  {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
-     * @param  unit           The unit of measurement, or {@code null} if unknown.
-     * @return The new range of numeric values for the given endpoints and unit of measurement.
+     * @param  unit           the unit of measurement, or {@code null} if unknown.
+     * @return the new range of numeric values for the given endpoints and unit of measurement.
      */
     public static MeasurementRange<Double> create(double minValue, boolean isMinIncluded,
                                                   double maxValue, boolean isMaxIncluded, Unit<?> unit)
@@ -113,9 +113,9 @@ public class MeasurementRange<E extends
      * The {@code minValue} is often zero for creating a range of strictly positive values.
      * This method may return a shared instance, at implementation choice.
      *
-     * @param  minValue The minimal value (exclusive), or {@link Double#NEGATIVE_INFINITY} if none.
-     * @param  unit The unit of measurement, or {@code null} if unknown.
-     * @return The new range of numeric values greater than the given value.
+     * @param  minValue  the minimal value (exclusive), or {@link Double#NEGATIVE_INFINITY} if none.
+     * @param  unit      the unit of measurement, or {@code null} if unknown.
+     * @return the new range of numeric values greater than the given value.
      *
      * @since 0.6
      */
@@ -131,12 +131,12 @@ public class MeasurementRange<E extends
      *
      * <p>This method may return a shared instance, at implementation choice.</p>
      *
-     * @param  minValue       The minimal value, or {@code null} if none.
+     * @param  minValue       the minimal value, or {@code null} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
-     * @param  maxValue       The maximal value, or {@code null} if none.
+     * @param  maxValue       the maximal value, or {@code null} if none.
      * @param  isMaxIncluded  {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
-     * @param  unit           The unit of measurement, or {@code null} if unknown.
-     * @return The new range, or {@code null} if both {@code minValue} and {@code maxValue} are {@code null}.
+     * @param  unit           the unit of measurement, or {@code null} if unknown.
+     * @return the new range, or {@code null} if both {@code minValue} and {@code maxValue} are {@code null}.
      *
      * @see NumberRange#createBestFit(Number, boolean, Number, boolean)
      */
@@ -158,8 +158,8 @@ public class MeasurementRange<E extends
      * Constructs a range with the same values than the specified range and the given unit.
      * This is a copy constructor, with the addition of a unit of measurement.
      *
-     * @param range The range to copy. The elements must be {@link Number} instances.
-     * @param unit  The unit of measurement, or {@code null} if unknown.
+     * @param  range  the range to copy. The elements must be {@link Number} instances.
+     * @param  unit   the unit of measurement, or {@code null} if unknown.
      */
     public MeasurementRange(final Range<E> range, final Unit<?> unit) {
         super(range);
@@ -173,12 +173,11 @@ public class MeasurementRange<E extends
      * the given type is not wide enough, then the values are truncated in the same way
      * than the Java language casts primitive types.
      *
-     * @param  type  The element type, restricted to one of {@link Byte}, {@link Short},
-     *               {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
-     * @param  range The range of values.
-     * @param  unit  The unit of measurement, or {@code null} if unknown.
-     * @throws IllegalArgumentException If the given type is not one of the primitive
-     *         wrappers for numeric types.
+     * @param  type   the element type, restricted to one of {@link Byte}, {@link Short},
+     *                {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
+     * @param  range  the range of values.
+     * @param  unit   the unit of measurement, or {@code null} if unknown.
+     * @throws IllegalArgumentException if the given type is not one of the primitive wrappers for numeric types.
      */
     public MeasurementRange(final Class<E> type, final ValueRange range, final Unit<?> unit) throws IllegalArgumentException {
         super(type, range);
@@ -188,12 +187,12 @@ public class MeasurementRange<E extends
     /**
      * Constructs a range of {@link Number} objects.
      *
-     * @param type          The element type, usually one of {@link Float} or {@link Double}.
-     * @param minValue      The minimal value, or {@code null} if none.
-     * @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
-     * @param maxValue      The maximal value, or {@code null} if none.
-     * @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
-     * @param unit          The unit of measurement, or {@code null} if unknown.
+     * @param type           the element type, usually one of {@link Float} or {@link Double}.
+     * @param minValue       the minimal value, or {@code null} if none.
+     * @param isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
+     * @param maxValue       the maximal value, or {@code null} if none.
+     * @param isMaxIncluded  {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
+     * @param unit           the unit of measurement, or {@code null} if unknown.
      */
     public MeasurementRange(final Class<E> type,
                             final E minValue, final boolean isMinIncluded,
@@ -205,13 +204,12 @@ public class MeasurementRange<E extends
     }
 
     /**
-     * Constructs a range with the same values than the specified range,
-     * casted to the specified type.
+     * Constructs a range with the same values than the specified range, casted to the specified type.
      *
-     * @param type  The element type, usually one of {@link Byte}, {@link Short},
-     *              {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
-     * @param range The range to copy. The elements must be {@link Number} instances.
-     * @param unit  The unit of measurement, or {@code null} if unknown.
+     * @param  type   the element type, usually one of {@link Byte}, {@link Short},
+     *                {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
+     * @param  range  the range to copy. The elements must be {@link Number} instances.
+     * @param  unit   the unit of measurement, or {@code null} if unknown.
      */
     private MeasurementRange(final Class<E> type, final Range<? extends Number> range, final Unit<?> unit) {
         super(type, range);
@@ -231,7 +229,7 @@ public class MeasurementRange<E extends
     /**
      * Returns the unit of measurement, or {@code null} if unknown.
      *
-     * @return The unit of measurement, or {@code null}.
+     * @return the unit of measurement, or {@code null}.
      */
     @Override
     public Unit<?> unit() {
@@ -243,10 +241,9 @@ public class MeasurementRange<E extends
      * then the specified target unit are simply assigned to the returned range with no
      * other changes.
      *
-     * @param  targetUnit the target unit, or {@code null} for keeping the unit unchanged.
-     * @return The converted range, or {@code this} if no conversion is needed.
-     * @throws ConversionException if the target unit are not compatible with
-     *         this {@linkplain #unit() range unit}.
+     * @param  targetUnit  the target unit, or {@code null} for keeping the unit unchanged.
+     * @return the converted range, or {@code this} if no conversion is needed.
+     * @throws ConversionException if the target unit are not compatible with this {@linkplain #unit() range unit}.
      */
     public MeasurementRange<E> convertTo(final Unit<?> targetUnit) throws ConversionException {
         return convertAndCast(elementType, targetUnit);
@@ -271,10 +268,9 @@ public class MeasurementRange<E extends
      * If the given range is an instance of {@code MeasurementRange}, converts that
      * range to the unit of this range. Otherwise returns the given range unchanged.
      *
-     * @param  range The range to convert.
-     * @return The converted range.
-     * @throws IllegalArgumentException if the given target unit is not compatible with
-     *         the unit of this range.
+     * @param  range  the range to convert.
+     * @return the converted range.
+     * @throws IllegalArgumentException if the given target unit is not compatible with the unit of this range.
      */
     private <N extends E> Range<N> convert(final Range<N> range) throws IllegalArgumentException {
         if (range instanceof MeasurementRange<?>) try {
@@ -291,9 +287,9 @@ public class MeasurementRange<E extends
      * measurement, then this method convert the {@code range} unit to the same unit than this
      * instance.
      *
-     * @param type The class to cast to. Must be one of {@link Byte}, {@link Short},
-     *             {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
-     * @return The casted range, or {@code range} if no cast is needed.
+     * @param  type  the class to cast to. Must be one of {@link Byte}, {@link Short},
+     *               {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
+     * @return the casted range, or {@code range} if no cast is needed.
      */
     @Override
     <N extends Number & Comparable<? super N>>
@@ -314,12 +310,11 @@ public class MeasurementRange<E extends
      * This method is invoked on the {@code other} instance in expressions like
      * {@code this.operation(other)}.
      *
-     * @param  type The class to cast to. Must be one of {@link Byte}, {@link Short},
-     *             {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
+     * @param  type  the class to cast to. Must be one of {@link Byte}, {@link Short},
+     *               {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
      * @param  targetUnit the target unit, or {@code null} for no change.
-     * @return The casted range, or {@code this}.
-     * @throws ConversionException if the given target unit is not compatible with
-     *         the unit of this range.
+     * @return the casted range, or {@code this}.
+     * @throws ConversionException if the given target unit is not compatible with the unit of this range.
      */
     @SuppressWarnings("unchecked")
     private <N extends Number & Comparable<? super N>> MeasurementRange<N>
@@ -369,7 +364,7 @@ public class MeasurementRange<E extends
      * the operation.
      *
      * @return {@inheritDoc}
-     * @throws IllegalArgumentException is the given range is an instance of
+     * @throws IllegalArgumentException if the given range is an instance of
      *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override
@@ -384,7 +379,7 @@ public class MeasurementRange<E extends
      * the operation.
      *
      * @return {@inheritDoc}
-     * @throws IllegalArgumentException is the given range is an instance of
+     * @throws IllegalArgumentException if the given range is an instance of
      *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override
@@ -399,7 +394,7 @@ public class MeasurementRange<E extends
      * the operation.
      *
      * @return {@inheritDoc}
-     * @throws IllegalArgumentException is the given range is an instance of
+     * @throws IllegalArgumentException if the given range is an instance of
      *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override
@@ -414,7 +409,7 @@ public class MeasurementRange<E extends
      * the operation.
      *
      * @return {@inheritDoc}
-     * @throws IllegalArgumentException is the given range is an instance of
+     * @throws IllegalArgumentException if the given range is an instance of
      *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override
@@ -429,7 +424,7 @@ public class MeasurementRange<E extends
      * the operation.
      *
      * @return {@inheritDoc}
-     * @throws IllegalArgumentException is the given range is an instance of
+     * @throws IllegalArgumentException if the given range is an instance of
      *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override