Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id DADB2200B8B for ; Tue, 4 Oct 2016 22:48:45 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id D93CF160ACC; Tue, 4 Oct 2016 20:48:45 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id B05DB160AC7 for ; Tue, 4 Oct 2016 22:48:43 +0200 (CEST) Received: (qmail 96402 invoked by uid 500); 4 Oct 2016 20:48:42 -0000 Mailing-List: contact commits-help@sis.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sis-dev@sis.apache.org Delivered-To: mailing list commits@sis.apache.org Received: (qmail 96393 invoked by uid 99); 4 Oct 2016 20:48:42 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 04 Oct 2016 20:48:42 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 5CEB2C0E0C for ; Tue, 4 Oct 2016 20:48:42 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -1.199 X-Spam-Level: X-Spam-Status: No, score=-1.199 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-2.999] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id u8xYmlsD8dsY for ; Tue, 4 Oct 2016 20:48:34 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTP id 2DC525FE69 for ; Tue, 4 Oct 2016 20:48:34 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id E3602E0069 for ; Tue, 4 Oct 2016 20:48:32 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 5D3AF3A0248 for ; Tue, 4 Oct 2016 20:48:32 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1763329 - in /sis/branches/JDK8: core/sis-utility/src/main/java/org/apache/sis/internal/system/ core/sis-utility/src/main/java/org/apache/sis/math/ core/sis-utility/src/test/java/org/apache/sis/math/ storage/sis-netcdf/src/main/java/org/ap... Date: Tue, 04 Oct 2016 20:48:31 -0000 To: commits@sis.apache.org From: desruisseaux@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20161004204832.5D3AF3A0248@svn01-us-west.apache.org> archived-at: Tue, 04 Oct 2016 20:48:46 -0000 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 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 { /** For cross-version compatibility. */ @@ -282,7 +349,8 @@ abstract class ArrayVector { /** For cross-version compatibility. */ @@ -330,10 +398,40 @@ abstract class ArrayVector= 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 { /** For cross-version compatibility. */ @@ -382,10 +480,43 @@ abstract class ArrayVector= 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 { /** For cross-version compatibility. */ @@ -435,10 +566,15 @@ abstract class ArrayVector { /** For cross-version compatibility. */ @@ -489,6 +625,10 @@ abstract class ArrayVector 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 { + /** + * 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 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 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 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 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 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 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 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 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 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 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. * - *

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].

+ *

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.

* * @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 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 getElementType(); /** + * Returns {@code true} if this vector contains only integer values. + * This method may iterate over all values for performing this verification. + * + * @return {@code true} if this vector contains only integer values. + */ + public boolean isInteger() { + if (!Numbers.isInteger(getElementType())) { + for (int i=size(); --i >= 0;) { + final double v = doubleValue(i); + if (v != Math.floor(v)) { + return false; + } + } + } + return true; + } + + /** * Returns {@code true} if integer values shall be interpreted as unsigned values. * This method may return {@code true} for data stored in {@code byte[]}, {@code short[]}, {@code int[]} * or {@code long[]} arrays, but never for data stored in {@code float[]} and {@code double[]} arrays. @@ -384,6 +417,126 @@ public abstract class Vector extends Abs public abstract Number set(int index, Number value); /** + * Returns {@code a-b} as a signed value, throwing an exception if the result overflows a {@code long}. + * The given values will be interpreted as unsigned values if this vector {@linkplain #isUnsigned() is unsigned}. + * + * @param a the first value, unsigned if {@link #isUnsigned()} return {@code true}. + * @param b the value to subtract from {@code a}, unsigned if {@link #isUnsigned()} return {@code true}. + * @return the difference, always signed. + * @throws ArithmeticException if the difference is too large. + * + * @see Math#subtractExact(long, long) + */ + final long subtract(final long a, final long b) { + final long inc = a - b; + // The sign of the difference shall be the same than the sign of Long.compare(…). + if ((((isUnsigned() ? Long.compareUnsigned(a, b) : Long.compare(a, b)) ^ inc) & Long.MIN_VALUE) != 0) { + throw new ArithmeticException(); + } + return inc; + } + + /** + * Returns the increment between all consecutive values if this increment is constant, or {@code null} otherwise. + * If the returned value is non-null, then the following condition shall hold for all values of i in + * the [0 … {@link #size()} - 1] range: + * + *
{@linkplain Math#abs(double) abs}({@linkplain #doubleValue(int) doubleValue}(i) + * - (doubleValue(0) + increment*i)) <= tolerance
+ * + * 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, … n-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. + *
    + *
  • Vector is backed by an {@code int[]} array while values could be stored as {@code short} values.
  • + *
  • Vector contains increasing or decreasing values with a constant delta between consecutive values.
  • + *
+ * + * 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). + * + *
When to use
+ * 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; in-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