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/04 20:48:31 UTC
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...
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(\u2026).
+ 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 \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>)) <= 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.
@@ -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, \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.
+ * 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));
}
}
}