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>)) <= 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